git subrepo clone --branch=sono6good https://github.com/essej/JUCE.git deps/juce
subrepo: subdir: "deps/juce" merged: "b13f9084e" upstream: origin: "https://github.com/essej/JUCE.git" branch: "sono6good" commit: "b13f9084e" git-subrepo: version: "0.4.3" origin: "https://github.com/ingydotnet/git-subrepo.git" commit: "2f68596"
This commit is contained in:
494
deps/juce/examples/Utilities/OSCDemo.h
vendored
Normal file
494
deps/juce/examples/Utilities/OSCDemo.h
vendored
Normal file
@ -0,0 +1,494 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE examples.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
|
||||
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
|
||||
PURPOSE, ARE DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
The block below describes the properties of this PIP. A PIP is a short snippet
|
||||
of code that can be read by the Projucer and used to generate a JUCE project.
|
||||
|
||||
BEGIN_JUCE_PIP_METADATA
|
||||
|
||||
name: OSCDemo
|
||||
version: 1.0.0
|
||||
vendor: JUCE
|
||||
website: http://juce.com
|
||||
description: Application using the OSC protocol.
|
||||
|
||||
dependencies: juce_core, juce_data_structures, juce_events, juce_graphics,
|
||||
juce_gui_basics, juce_osc
|
||||
exporters: xcode_mac, vs2019, linux_make
|
||||
|
||||
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
|
||||
|
||||
type: Component
|
||||
mainClass: OSCDemo
|
||||
|
||||
useLocalCopy: 1
|
||||
|
||||
END_JUCE_PIP_METADATA
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class OSCLogListBox : public ListBox,
|
||||
private ListBoxModel,
|
||||
private AsyncUpdater
|
||||
{
|
||||
public:
|
||||
OSCLogListBox()
|
||||
{
|
||||
setModel (this);
|
||||
}
|
||||
|
||||
~OSCLogListBox() override = default;
|
||||
|
||||
//==============================================================================
|
||||
int getNumRows() override
|
||||
{
|
||||
return oscLogList.size();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void paintListBoxItem (int row, Graphics& g, int width, int height, bool rowIsSelected) override
|
||||
{
|
||||
ignoreUnused (rowIsSelected);
|
||||
|
||||
if (isPositiveAndBelow (row, oscLogList.size()))
|
||||
{
|
||||
g.setColour (Colours::white);
|
||||
|
||||
g.drawText (oscLogList[row],
|
||||
Rectangle<int> (width, height).reduced (4, 0),
|
||||
Justification::centredLeft, true);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void addOSCMessage (const OSCMessage& message, int level = 0)
|
||||
{
|
||||
oscLogList.add (getIndentationString (level)
|
||||
+ "- osc message, address = '"
|
||||
+ message.getAddressPattern().toString()
|
||||
+ "', "
|
||||
+ String (message.size())
|
||||
+ " argument(s)");
|
||||
|
||||
if (! message.isEmpty())
|
||||
{
|
||||
for (auto& arg : message)
|
||||
addOSCMessageArgument (arg, level + 1);
|
||||
}
|
||||
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void addOSCBundle (const OSCBundle& bundle, int level = 0)
|
||||
{
|
||||
OSCTimeTag timeTag = bundle.getTimeTag();
|
||||
|
||||
oscLogList.add (getIndentationString (level)
|
||||
+ "- osc bundle, time tag = "
|
||||
+ timeTag.toTime().toString (true, true, true, true));
|
||||
|
||||
for (auto& element : bundle)
|
||||
{
|
||||
if (element.isMessage())
|
||||
addOSCMessage (element.getMessage(), level + 1);
|
||||
else if (element.isBundle())
|
||||
addOSCBundle (element.getBundle(), level + 1);
|
||||
}
|
||||
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void addOSCMessageArgument (const OSCArgument& arg, int level)
|
||||
{
|
||||
String typeAsString;
|
||||
String valueAsString;
|
||||
|
||||
if (arg.isFloat32())
|
||||
{
|
||||
typeAsString = "float32";
|
||||
valueAsString = String (arg.getFloat32());
|
||||
}
|
||||
else if (arg.isInt32())
|
||||
{
|
||||
typeAsString = "int32";
|
||||
valueAsString = String (arg.getInt32());
|
||||
}
|
||||
else if (arg.isString())
|
||||
{
|
||||
typeAsString = "string";
|
||||
valueAsString = arg.getString();
|
||||
}
|
||||
else if (arg.isBlob())
|
||||
{
|
||||
typeAsString = "blob";
|
||||
auto& blob = arg.getBlob();
|
||||
valueAsString = String::fromUTF8 ((const char*) blob.getData(), (int) blob.getSize());
|
||||
}
|
||||
else
|
||||
{
|
||||
typeAsString = "(unknown)";
|
||||
}
|
||||
|
||||
oscLogList.add (getIndentationString (level + 1) + "- " + typeAsString.paddedRight(' ', 12) + valueAsString);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void addInvalidOSCPacket (const char* /* data */, int dataSize)
|
||||
{
|
||||
oscLogList.add ("- (" + String(dataSize) + "bytes with invalid format)");
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void clear()
|
||||
{
|
||||
oscLogList.clear();
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void handleAsyncUpdate() override
|
||||
{
|
||||
updateContent();
|
||||
scrollToEnsureRowIsOnscreen (oscLogList.size() - 1);
|
||||
repaint();
|
||||
}
|
||||
|
||||
private:
|
||||
static String getIndentationString (int level)
|
||||
{
|
||||
return String().paddedRight (' ', 2 * level);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
StringArray oscLogList;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSCLogListBox)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class OSCSenderDemo : public Component
|
||||
{
|
||||
public:
|
||||
OSCSenderDemo()
|
||||
{
|
||||
addAndMakeVisible (senderLabel);
|
||||
senderLabel.attachToComponent (&rotaryKnob, false);
|
||||
|
||||
rotaryKnob.setRange (0.0, 1.0);
|
||||
rotaryKnob.setSliderStyle (Slider::RotaryVerticalDrag);
|
||||
rotaryKnob.setTextBoxStyle (Slider::TextBoxBelow, true, 150, 25);
|
||||
rotaryKnob.setBounds (50, 50, 180, 180);
|
||||
addAndMakeVisible (rotaryKnob);
|
||||
rotaryKnob.onValueChange = [this]
|
||||
{
|
||||
// create and send an OSC message with an address and a float value:
|
||||
if (! sender1.send ("/juce/rotaryknob", (float) rotaryKnob.getValue()))
|
||||
showConnectionErrorMessage ("Error: could not send OSC message.");
|
||||
if (! sender2.send ("/juce/rotaryknob", (float) rotaryKnob.getValue()))
|
||||
showConnectionErrorMessage ("Error: could not send OSC message.");
|
||||
};
|
||||
|
||||
// specify here where to send OSC messages to: host URL and UDP port number
|
||||
if (! sender1.connect ("127.0.0.1", 9001))
|
||||
showConnectionErrorMessage ("Error: could not connect to UDP port 9001.");
|
||||
if (! sender2.connect ("127.0.0.1", 9002))
|
||||
showConnectionErrorMessage ("Error: could not connect to UDP port 9002.");
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void showConnectionErrorMessage (const String& messageText)
|
||||
{
|
||||
AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
|
||||
"Connection error",
|
||||
messageText,
|
||||
"OK");
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Slider rotaryKnob;
|
||||
OSCSender sender1, sender2;
|
||||
Label senderLabel { {}, "Sender" };
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSCSenderDemo)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class OSCReceiverDemo : public Component,
|
||||
private OSCReceiver,
|
||||
private OSCReceiver::ListenerWithOSCAddress<OSCReceiver::MessageLoopCallback>
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
OSCReceiverDemo()
|
||||
{
|
||||
addAndMakeVisible (receiverLabel);
|
||||
receiverLabel.attachToComponent (&rotaryKnob, false);
|
||||
|
||||
rotaryKnob.setRange (0.0, 1.0);
|
||||
rotaryKnob.setSliderStyle (Slider::RotaryVerticalDrag);
|
||||
rotaryKnob.setTextBoxStyle (Slider::TextBoxBelow, true, 150, 25);
|
||||
rotaryKnob.setBounds (50, 50, 180, 180);
|
||||
rotaryKnob.setInterceptsMouseClicks (false, false);
|
||||
addAndMakeVisible (rotaryKnob);
|
||||
|
||||
// specify here on which UDP port number to receive incoming OSC messages
|
||||
if (! connect (9001))
|
||||
showConnectionErrorMessage ("Error: could not connect to UDP port 9001.");
|
||||
|
||||
// tell the component to listen for OSC messages matching this address:
|
||||
addListener (this, "/juce/rotaryknob");
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void oscMessageReceived (const OSCMessage& message) override
|
||||
{
|
||||
if (message.size() == 1 && message[0].isFloat32())
|
||||
rotaryKnob.setValue (jlimit (0.0f, 10.0f, message[0].getFloat32()));
|
||||
}
|
||||
|
||||
void showConnectionErrorMessage (const String& messageText)
|
||||
{
|
||||
AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
|
||||
"Connection error",
|
||||
messageText,
|
||||
"OK");
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Slider rotaryKnob;
|
||||
Label receiverLabel { {}, "Receiver" };
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSCReceiverDemo)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class OSCMonitorDemo : public Component,
|
||||
private OSCReceiver::Listener<OSCReceiver::MessageLoopCallback>
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
OSCMonitorDemo()
|
||||
{
|
||||
portNumberLabel.setBounds (10, 18, 130, 25);
|
||||
addAndMakeVisible (portNumberLabel);
|
||||
|
||||
portNumberField.setEditable (true, true, true);
|
||||
portNumberField.setBounds (140, 18, 50, 25);
|
||||
addAndMakeVisible (portNumberField);
|
||||
|
||||
connectButton.setBounds (210, 18, 100, 25);
|
||||
addAndMakeVisible (connectButton);
|
||||
connectButton.onClick = [this] { connectButtonClicked(); };
|
||||
|
||||
clearButton.setBounds (320, 18, 60, 25);
|
||||
addAndMakeVisible (clearButton);
|
||||
clearButton.onClick = [this] { clearButtonClicked(); };
|
||||
|
||||
connectionStatusLabel.setBounds (450, 18, 240, 25);
|
||||
updateConnectionStatusLabel();
|
||||
addAndMakeVisible (connectionStatusLabel);
|
||||
|
||||
oscLogListBox.setBounds (0, 60, 700, 340);
|
||||
addAndMakeVisible (oscLogListBox);
|
||||
|
||||
oscReceiver.addListener (this);
|
||||
oscReceiver.registerFormatErrorHandler ([this] (const char* data, int dataSize)
|
||||
{
|
||||
oscLogListBox.addInvalidOSCPacket (data, dataSize);
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Label portNumberLabel { {}, "UDP Port Number: " };
|
||||
Label portNumberField { {}, "9002" };
|
||||
TextButton connectButton { "Connect" };
|
||||
TextButton clearButton { "Clear" };
|
||||
Label connectionStatusLabel;
|
||||
|
||||
OSCLogListBox oscLogListBox;
|
||||
OSCReceiver oscReceiver;
|
||||
|
||||
int currentPortNumber = -1;
|
||||
|
||||
//==============================================================================
|
||||
void connectButtonClicked()
|
||||
{
|
||||
if (! isConnected())
|
||||
connect();
|
||||
else
|
||||
disconnect();
|
||||
|
||||
updateConnectionStatusLabel();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void clearButtonClicked()
|
||||
{
|
||||
oscLogListBox.clear();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void oscMessageReceived (const OSCMessage& message) override
|
||||
{
|
||||
oscLogListBox.addOSCMessage (message);
|
||||
}
|
||||
|
||||
void oscBundleReceived (const OSCBundle& bundle) override
|
||||
{
|
||||
oscLogListBox.addOSCBundle (bundle);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void connect()
|
||||
{
|
||||
auto portToConnect = portNumberField.getText().getIntValue();
|
||||
|
||||
if (! isValidOscPort (portToConnect))
|
||||
{
|
||||
handleInvalidPortNumberEntered();
|
||||
return;
|
||||
}
|
||||
|
||||
if (oscReceiver.connect (portToConnect))
|
||||
{
|
||||
currentPortNumber = portToConnect;
|
||||
connectButton.setButtonText ("Disconnect");
|
||||
}
|
||||
else
|
||||
{
|
||||
handleConnectError (portToConnect);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void disconnect()
|
||||
{
|
||||
if (oscReceiver.disconnect())
|
||||
{
|
||||
currentPortNumber = -1;
|
||||
connectButton.setButtonText ("Connect");
|
||||
}
|
||||
else
|
||||
{
|
||||
handleDisconnectError();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void handleConnectError (int failedPort)
|
||||
{
|
||||
AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
|
||||
"OSC Connection error",
|
||||
"Error: could not connect to port " + String (failedPort),
|
||||
"OK");
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void handleDisconnectError()
|
||||
{
|
||||
AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
|
||||
"Unknown error",
|
||||
"An unknown error occurred while trying to disconnect from UDP port.",
|
||||
"OK");
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void handleInvalidPortNumberEntered()
|
||||
{
|
||||
AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
|
||||
"Invalid port number",
|
||||
"Error: you have entered an invalid UDP port number.",
|
||||
"OK");
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool isConnected() const
|
||||
{
|
||||
return currentPortNumber != -1;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool isValidOscPort (int port) const
|
||||
{
|
||||
return port > 0 && port < 65536;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void updateConnectionStatusLabel()
|
||||
{
|
||||
String text = "Status: ";
|
||||
|
||||
if (isConnected())
|
||||
text += "Connected to UDP port " + String (currentPortNumber);
|
||||
else
|
||||
text += "Disconnected";
|
||||
|
||||
auto textColour = isConnected() ? Colours::green : Colours::red;
|
||||
|
||||
connectionStatusLabel.setText (text, dontSendNotification);
|
||||
connectionStatusLabel.setFont (Font (15.00f, Font::bold));
|
||||
connectionStatusLabel.setColour (Label::textColourId, textColour);
|
||||
connectionStatusLabel.setJustificationType (Justification::centredRight);
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSCMonitorDemo)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class OSCDemo : public Component
|
||||
{
|
||||
public:
|
||||
OSCDemo()
|
||||
{
|
||||
addAndMakeVisible (monitor);
|
||||
addAndMakeVisible (receiver);
|
||||
addAndMakeVisible (sender);
|
||||
|
||||
setSize (700, 400);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto bounds = getLocalBounds();
|
||||
|
||||
auto lowerBounds = bounds.removeFromBottom (getHeight() / 2);
|
||||
auto halfBounds = bounds.removeFromRight (getWidth() / 2);
|
||||
|
||||
sender .setBounds (bounds);
|
||||
receiver.setBounds (halfBounds);
|
||||
monitor .setBounds (lowerBounds.removeFromTop (getHeight() / 2));
|
||||
}
|
||||
|
||||
private:
|
||||
OSCMonitorDemo monitor;
|
||||
OSCReceiverDemo receiver;
|
||||
OSCSenderDemo sender;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSCDemo)
|
||||
};
|
Reference in New Issue
Block a user