summaryrefslogtreecommitdiffstats
path: root/src/libs/engine
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/engine')
-rw-r--r--src/libs/engine/AlsaMidiDriver.cpp373
-rw-r--r--src/libs/engine/AlsaMidiDriver.h127
-rw-r--r--src/libs/engine/Array.h109
-rw-r--r--src/libs/engine/AudioDriver.h50
-rw-r--r--src/libs/engine/AudioInputNode.cpp49
-rw-r--r--src/libs/engine/AudioInputNode.h45
-rw-r--r--src/libs/engine/AudioOutputNode.cpp50
-rw-r--r--src/libs/engine/AudioOutputNode.h44
-rw-r--r--src/libs/engine/BridgeNode.cpp159
-rw-r--r--src/libs/engine/BridgeNode.h90
-rw-r--r--src/libs/engine/Buffer.cpp297
-rw-r--r--src/libs/engine/Buffer.h91
-rw-r--r--src/libs/engine/ClientBroadcaster.cpp331
-rw-r--r--src/libs/engine/ClientBroadcaster.h101
-rw-r--r--src/libs/engine/Connection.cpp45
-rw-r--r--src/libs/engine/Connection.h66
-rw-r--r--src/libs/engine/ConnectionBase.cpp96
-rw-r--r--src/libs/engine/ConnectionBase.h105
-rw-r--r--src/libs/engine/ControlInputNode.cpp49
-rw-r--r--src/libs/engine/ControlInputNode.h44
-rw-r--r--src/libs/engine/ControlOutputNode.cpp48
-rw-r--r--src/libs/engine/ControlOutputNode.h45
-rw-r--r--src/libs/engine/Controller.h82
-rw-r--r--src/libs/engine/DSSIPlugin.cpp340
-rw-r--r--src/libs/engine/DSSIPlugin.cpp.orig207
-rw-r--r--src/libs/engine/DSSIPlugin.h109
-rw-r--r--src/libs/engine/DSSIPlugin.h.orig84
-rw-r--r--src/libs/engine/Driver.h112
-rw-r--r--src/libs/engine/Event.cpp48
-rw-r--r--src/libs/engine/Event.h71
-rw-r--r--src/libs/engine/EventSource.h52
-rw-r--r--src/libs/engine/InputPort.cpp352
-rw-r--r--src/libs/engine/InputPort.h88
-rw-r--r--src/libs/engine/InternalNode.h70
-rw-r--r--src/libs/engine/JackAudioDriver.cpp373
-rw-r--r--src/libs/engine/JackAudioDriver.h176
-rw-r--r--src/libs/engine/JackMidiDriver.cpp217
-rw-r--r--src/libs/engine/JackMidiDriver.h123
-rw-r--r--src/libs/engine/LADSPAPlugin.cpp274
-rw-r--r--src/libs/engine/LADSPAPlugin.h69
-rw-r--r--src/libs/engine/LV2Plugin.cpp275
-rw-r--r--src/libs/engine/LV2Plugin.h76
-rw-r--r--src/libs/engine/LashDriver.cpp159
-rw-r--r--src/libs/engine/LashDriver.h57
-rw-r--r--src/libs/engine/List.h416
-rw-r--r--src/libs/engine/Maid.cpp46
-rw-r--r--src/libs/engine/Maid.h65
-rw-r--r--src/libs/engine/MaidObject.h38
-rw-r--r--src/libs/engine/Makefile.am259
-rw-r--r--src/libs/engine/MidiControlNode.cpp133
-rw-r--r--src/libs/engine/MidiControlNode.h72
-rw-r--r--src/libs/engine/MidiDriver.h78
-rw-r--r--src/libs/engine/MidiInputNode.cpp49
-rw-r--r--src/libs/engine/MidiInputNode.h43
-rw-r--r--src/libs/engine/MidiMessage.h51
-rw-r--r--src/libs/engine/MidiNoteNode.cpp304
-rw-r--r--src/libs/engine/MidiNoteNode.h87
-rw-r--r--src/libs/engine/MidiOutputNode.cpp49
-rw-r--r--src/libs/engine/MidiOutputNode.h43
-rw-r--r--src/libs/engine/MidiTriggerNode.cpp124
-rw-r--r--src/libs/engine/MidiTriggerNode.h64
-rw-r--r--src/libs/engine/Node.h120
-rw-r--r--src/libs/engine/NodeBase.cpp171
-rw-r--r--src/libs/engine/NodeBase.h105
-rw-r--r--src/libs/engine/NodeFactory.cpp707
-rw-r--r--src/libs/engine/NodeFactory.h93
-rw-r--r--src/libs/engine/OSCClient.cpp503
-rw-r--r--src/libs/engine/OSCClient.h131
-rw-r--r--src/libs/engine/OSCReceiver.cpp926
-rw-r--r--src/libs/engine/OSCReceiver.h124
-rw-r--r--src/libs/engine/OSCResponder.cpp88
-rw-r--r--src/libs/engine/OSCResponder.h61
-rw-r--r--src/libs/engine/ObjectSender.cpp207
-rw-r--r--src/libs/engine/ObjectSender.h55
-rw-r--r--src/libs/engine/ObjectStore.cpp109
-rw-r--r--src/libs/engine/ObjectStore.h61
-rw-r--r--src/libs/engine/Om.cpp36
-rw-r--r--src/libs/engine/Om.h42
-rw-r--r--src/libs/engine/OmApp.cpp231
-rw-r--r--src/libs/engine/OmApp.h99
-rw-r--r--src/libs/engine/OmInProcess.cpp74
-rw-r--r--src/libs/engine/OmObject.h115
-rw-r--r--src/libs/engine/OutputPort.cpp51
-rw-r--r--src/libs/engine/OutputPort.h64
-rw-r--r--src/libs/engine/Patch.cpp356
-rw-r--r--src/libs/engine/Patch.h136
-rw-r--r--src/libs/engine/Plugin.h149
-rw-r--r--src/libs/engine/PluginLibrary.h100
-rw-r--r--src/libs/engine/Port.cpp56
-rw-r--r--src/libs/engine/Port.h80
-rw-r--r--src/libs/engine/PortBase.cpp133
-rw-r--r--src/libs/engine/PortBase.h87
-rw-r--r--src/libs/engine/PortInfo.h153
-rw-r--r--src/libs/engine/PostProcessor.cpp122
-rw-r--r--src/libs/engine/PostProcessor.h78
-rw-r--r--src/libs/engine/QueuedEngineInterface.cpp299
-rw-r--r--src/libs/engine/QueuedEngineInterface.h145
-rw-r--r--src/libs/engine/QueuedEvent.h86
-rw-r--r--src/libs/engine/QueuedEventSource.cpp201
-rw-r--r--src/libs/engine/QueuedEventSource.h83
-rw-r--r--src/libs/engine/Responder.h63
-rw-r--r--src/libs/engine/TransportNode.cpp155
-rw-r--r--src/libs/engine/TransportNode.h48
-rw-r--r--src/libs/engine/Tree.h155
-rw-r--r--src/libs/engine/TreeImplementation.h410
-rw-r--r--src/libs/engine/cmdline.c150
-rw-r--r--src/libs/engine/cmdline.ggo7
-rw-r--r--src/libs/engine/cmdline.h45
-rw-r--r--src/libs/engine/events.h62
-rw-r--r--src/libs/engine/events/ActivateEvent.cpp52
-rw-r--r--src/libs/engine/events/ActivateEvent.h41
-rw-r--r--src/libs/engine/events/AddNodeEvent.cpp128
-rw-r--r--src/libs/engine/events/AddNodeEvent.h63
-rw-r--r--src/libs/engine/events/AllNotesOffEvent.cpp67
-rw-r--r--src/libs/engine/events/AllNotesOffEvent.h50
-rw-r--r--src/libs/engine/events/ClearPatchEvent.cpp114
-rw-r--r--src/libs/engine/events/ClearPatchEvent.h54
-rw-r--r--src/libs/engine/events/ConnectionEvent.cpp240
-rw-r--r--src/libs/engine/events/ConnectionEvent.h107
-rw-r--r--src/libs/engine/events/CreatePatchEvent.cpp150
-rw-r--r--src/libs/engine/events/CreatePatchEvent.h64
-rw-r--r--src/libs/engine/events/DSSIConfigureEvent.cpp73
-rw-r--r--src/libs/engine/events/DSSIConfigureEvent.h49
-rw-r--r--src/libs/engine/events/DSSIControlEvent.cpp68
-rw-r--r--src/libs/engine/events/DSSIControlEvent.h51
-rw-r--r--src/libs/engine/events/DSSIProgramEvent.cpp77
-rw-r--r--src/libs/engine/events/DSSIProgramEvent.h49
-rw-r--r--src/libs/engine/events/DSSIUpdateEvent.cpp80
-rw-r--r--src/libs/engine/events/DSSIUpdateEvent.h54
-rw-r--r--src/libs/engine/events/DeactivateEvent.cpp54
-rw-r--r--src/libs/engine/events/DeactivateEvent.h42
-rw-r--r--src/libs/engine/events/DestroyEvent.cpp168
-rw-r--r--src/libs/engine/events/DestroyEvent.h68
-rw-r--r--src/libs/engine/events/DisablePatchEvent.cpp70
-rw-r--r--src/libs/engine/events/DisablePatchEvent.h52
-rw-r--r--src/libs/engine/events/DisconnectNodeEvent.cpp140
-rw-r--r--src/libs/engine/events/DisconnectNodeEvent.h68
-rw-r--r--src/libs/engine/events/DisconnectPortEvent.cpp145
-rw-r--r--src/libs/engine/events/DisconnectPortEvent.h70
-rw-r--r--src/libs/engine/events/DisconnectionEvent.cpp295
-rw-r--r--src/libs/engine/events/DisconnectionEvent.h106
-rw-r--r--src/libs/engine/events/EnablePatchEvent.cpp82
-rw-r--r--src/libs/engine/events/EnablePatchEvent.h56
-rw-r--r--src/libs/engine/events/LashRestoreDoneEvent.h54
-rw-r--r--src/libs/engine/events/LoadPluginsEvent.cpp44
-rw-r--r--src/libs/engine/events/LoadPluginsEvent.h40
-rw-r--r--src/libs/engine/events/Makefile.am67
-rw-r--r--src/libs/engine/events/MidiLearnEvent.cpp88
-rw-r--r--src/libs/engine/events/MidiLearnEvent.h84
-rw-r--r--src/libs/engine/events/NoteOffEvent.cpp78
-rw-r--r--src/libs/engine/events/NoteOffEvent.h52
-rw-r--r--src/libs/engine/events/NoteOnEvent.cpp89
-rw-r--r--src/libs/engine/events/NoteOnEvent.h54
-rw-r--r--src/libs/engine/events/PingQueuedEvent.h44
-rw-r--r--src/libs/engine/events/RegisterClientEvent.cpp53
-rw-r--r--src/libs/engine/events/RegisterClientEvent.h53
-rw-r--r--src/libs/engine/events/RenameEvent.cpp123
-rw-r--r--src/libs/engine/events/RenameEvent.h66
-rw-r--r--src/libs/engine/events/RequestAllObjectsEvent.cpp55
-rw-r--r--src/libs/engine/events/RequestAllObjectsEvent.h50
-rw-r--r--src/libs/engine/events/RequestMetadataEvent.cpp80
-rw-r--r--src/libs/engine/events/RequestMetadataEvent.h56
-rw-r--r--src/libs/engine/events/RequestPluginsEvent.cpp55
-rw-r--r--src/libs/engine/events/RequestPluginsEvent.h51
-rw-r--r--src/libs/engine/events/RequestPortValueEvent.cpp81
-rw-r--r--src/libs/engine/events/RequestPortValueEvent.h56
-rw-r--r--src/libs/engine/events/SetMetadataEvent.cpp79
-rw-r--r--src/libs/engine/events/SetMetadataEvent.h53
-rw-r--r--src/libs/engine/events/SetPortValueEvent.cpp104
-rw-r--r--src/libs/engine/events/SetPortValueEvent.h56
-rw-r--r--src/libs/engine/events/SetPortValueQueuedEvent.cpp116
-rw-r--r--src/libs/engine/events/SetPortValueQueuedEvent.h57
-rw-r--r--src/libs/engine/events/UnregisterClientEvent.cpp45
-rw-r--r--src/libs/engine/events/UnregisterClientEvent.h53
-rw-r--r--src/libs/engine/instantiations.cpp49
-rw-r--r--src/libs/engine/main.cpp153
-rw-r--r--src/libs/engine/midi.h135
-rw-r--r--src/libs/engine/tests/Makefile.am27
-rw-r--r--src/libs/engine/tests/list_test.cpp93
-rw-r--r--src/libs/engine/tests/node_tree_test.cpp94
-rw-r--r--src/libs/engine/tests/old_node_tree_test.cpp72
-rw-r--r--src/libs/engine/tests/queue_test.cpp47
-rw-r--r--src/libs/engine/tuning.h39
-rw-r--r--src/libs/engine/util.h73
184 files changed, 21146 insertions, 0 deletions
diff --git a/src/libs/engine/AlsaMidiDriver.cpp b/src/libs/engine/AlsaMidiDriver.cpp
new file mode 100644
index 00000000..decd2471
--- /dev/null
+++ b/src/libs/engine/AlsaMidiDriver.cpp
@@ -0,0 +1,373 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "AlsaMidiDriver.h"
+#include <iostream>
+#include <cstdlib>
+#include <pthread.h>
+#include "Om.h"
+#include "OmApp.h"
+#include "util/types.h"
+#include "OmApp.h"
+#include "Maid.h"
+#include "AudioDriver.h"
+#include "PortInfo.h"
+#include "MidiMessage.h"
+#include "PortBase.h"
+#ifdef HAVE_LASH
+#include "LashDriver.h"
+#endif
+using std::cout; using std::cerr; using std::endl;
+
+namespace Om {
+
+
+//// AlsaMidiPort ////
+
+AlsaMidiPort::AlsaMidiPort(AlsaMidiDriver* driver, PortBase<MidiMessage>* port)
+: DriverPort(),
+ ListNode<AlsaMidiPort*>(this),
+ m_driver(driver),
+ m_patch_port(port),
+ m_port_id(0),
+ m_midi_pool(new unsigned char*[port->buffer_size()]),
+ m_events(1024)
+{
+ assert(port->parent() != NULL);
+ assert(port->poly() == 1);
+
+ if (port->port_info()->is_input()) {
+ if ((m_port_id = snd_seq_create_simple_port(driver->seq_handle(), port->path().c_str(),
+ SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE,
+ SND_SEQ_PORT_TYPE_APPLICATION)) < 0)
+ {
+ cerr << "[AlsaMidiPort] Error creating sequencer port." << endl;
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ if ((m_port_id = snd_seq_create_simple_port(driver->seq_handle(), port->path().c_str(),
+ SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ,
+ SND_SEQ_PORT_TYPE_APPLICATION)) < 0)
+ {
+ cerr << "[AlsaMidiPort] Error creating sequencer port." << endl;
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ /* Allocate event pool. This pool is used when preparing a block from the queue
+ * of Alsa events. The buffer member of the MidiMessage's in the patch port's
+ * buffer will be set directly to an element in this pool, then next cycle they
+ * will be overwritten (eliminating the need for any allocation/freeing). */
+ for (size_t i=0; i < port->buffer_size(); ++i)
+ m_midi_pool[i] = new unsigned char[MAX_MIDI_EVENT_SIZE];
+
+ port->buffer(0)->clear();
+ port->fixed_buffers(true);
+}
+
+
+AlsaMidiPort::~AlsaMidiPort()
+{
+ snd_seq_delete_simple_port(m_driver->seq_handle(), m_port_id);
+
+ // Free event pool
+ for (size_t i=0; i < m_patch_port->buffer_size(); ++i)
+ delete[] m_midi_pool[i];
+
+ delete[] m_midi_pool;
+}
+
+
+void
+AlsaMidiPort::add_to_driver()
+{
+ m_driver->add_port(this);
+}
+
+
+void
+AlsaMidiPort::remove_from_driver()
+{
+ m_driver->remove_port(this);
+}
+
+
+void
+AlsaMidiPort::set_name(const string& name)
+{
+ snd_seq_port_info_t* info = NULL;
+ snd_seq_port_info_malloc(&info);
+ snd_seq_get_port_info(m_driver->seq_handle(), m_port_id, info);
+ snd_seq_port_info_set_name(info, name.c_str());
+ snd_seq_set_port_info(m_driver->seq_handle(), m_port_id, info);
+ snd_seq_port_info_free(info);
+}
+
+
+void
+AlsaMidiPort::event(snd_seq_event_t* const ev)
+{
+ // Abuse the tick field to hold the timestamp
+ ev->time.tick = om->audio_driver()->time_stamp();
+
+ // Fix noteons with velocity 0 (required for DSSI spec)
+ if (ev->type == SND_SEQ_EVENT_NOTEON && ev->data.note.velocity == 0)
+ ev->type = SND_SEQ_EVENT_NOTEOFF;
+
+ m_events.push(*ev);
+}
+
+
+/** Generates a flat array of MIDI events for patching.
+ *
+ * Prepares all events that occurred during the time interval passed
+ * (which ideally are the events from the previous cycle with an exact
+ * 1 cycle delay) and creates a flat port buffer for this cycle.
+ */
+void
+AlsaMidiPort::prepare_block(const samplecount block_start, const samplecount block_end)
+{
+ assert(block_end >= block_start);
+
+ snd_seq_event_t* ev = NULL;
+ MidiMessage* message = NULL;
+ size_t num_events = 0;
+ size_t event_size = 0; // decoded length of Alsa event in bytes
+ int timestamp = 0;
+
+ while (!m_events.is_empty() && m_events.front().time.tick < block_end) {
+ assert(num_events < m_patch_port->buffer_size());
+ ev = &m_events.front();
+ message = &m_patch_port->buffer(0)->data()[num_events];
+
+ timestamp = ev->time.tick - block_start;
+ if (timestamp < 0) {
+ // FIXME: remove this (obviously not realtime safe)
+ cerr << "[AlsaMidiPort] Missed event by " << -timestamp << " samples!" << endl;
+ timestamp = 0;
+ }
+ assert(timestamp < (int)(block_end - block_start));
+
+ // Reset decoder so we don't get running status
+ snd_midi_event_reset_decode(m_driver->event_coder());
+
+ // FIXME: is this realtime safe?
+ if ((event_size = snd_midi_event_decode(m_driver->event_coder(),
+ m_midi_pool[num_events], MAX_MIDI_EVENT_SIZE, ev)) > 0) {
+ message->size = event_size;
+ message->time = timestamp;
+ message->buffer = m_midi_pool[num_events];
+ ++num_events;
+ } else {
+ cerr << "[AlsaMidiPort] Unable to decode MIDI event" << endl;
+ }
+
+ m_events.pop();
+ }
+
+ m_patch_port->buffer(0)->filled_size(num_events);
+ m_patch_port->tied_port()->buffer(0)->filled_size(num_events);
+}
+
+
+
+//// AlsaMidiDriver ////
+
+
+bool AlsaMidiDriver::m_midi_thread_exit_flag = true;
+
+
+AlsaMidiDriver::AlsaMidiDriver()
+: m_seq_handle(NULL),
+ m_event_coder(NULL),
+ m_is_activated(false)
+{
+ if (snd_seq_open(&m_seq_handle, "hw", SND_SEQ_OPEN_INPUT, 0) < 0) {
+ cerr << "[AlsaMidiDriver] Error opening ALSA sequencer." << endl;
+ exit(EXIT_FAILURE);
+ } else {
+ cout << "[AlsaMidiDriver] Successfully opened ALSA sequencer." << endl;
+ }
+
+ if (snd_midi_event_new(3, &m_event_coder)) {
+ cerr << "[AlsaMidiDriver] Failed to initialize ALSA MIDI event coder!";
+ exit(EXIT_FAILURE);
+ } else {
+ snd_midi_event_reset_encode(m_event_coder);
+ snd_midi_event_reset_decode(m_event_coder);
+ }
+
+ snd_seq_set_client_name(m_seq_handle, "Om");
+}
+
+
+AlsaMidiDriver::~AlsaMidiDriver()
+{
+ deactivate();
+ snd_midi_event_free(m_event_coder);
+ snd_seq_close(m_seq_handle);
+}
+
+
+/** Launch and start the MIDI thread.
+ */
+void
+AlsaMidiDriver::activate()
+{
+ // Just exit if already running
+ if (m_midi_thread_exit_flag == false)
+ return;
+
+ bool success = false;
+ m_midi_thread_exit_flag = false;
+
+ //if (om->audio_driver()->is_realtime()) {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+
+ if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO)) {
+ cerr << "[AlsaMidiDriver] Unable to set realtime scheduling for MIDI thread." << endl;
+ }
+
+ sched_param param;
+ param.sched_priority = 10;
+
+ pthread_attr_setstacksize(&attr, 1500000);
+
+ if (pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED)
+ || pthread_attr_setschedparam(&attr, &param))
+ cout << "[AlsaMidiDriver] Unable to set SCHED_FIFO priority "
+ << param.sched_priority << endl;
+
+ if (!pthread_create(&m_process_thread, &attr, process_midi_in, this)) {
+ cout << "[AlsaMidiDriver] Started realtime MIDI thread (SCHED_FIFO, priority "
+ << param.sched_priority << ")" << endl;
+ success = true;
+ } else {
+ cerr << "[AlsaMidiDriver] Unable to start realtime MIDI thread." << endl;
+ }
+ pthread_attr_destroy(&attr);
+ //}
+
+ if (!success) {
+ // FIXME: check for success
+ pthread_create(&m_process_thread, NULL, process_midi_in, this);
+ cout << "[AlsaMidiDriver] Started non-realtime MIDI thread." << endl;
+ }
+
+#ifdef HAVE_LASH
+ lash_driver->set_alsa_client_id(snd_seq_client_id(m_seq_handle));
+#endif
+
+ m_is_activated = true;
+}
+
+
+/** Terminate the MIDI thread.
+ */
+void
+AlsaMidiDriver::deactivate()
+{
+ if (m_is_activated) {
+ m_midi_thread_exit_flag = true;
+ pthread_cancel(m_process_thread);
+ pthread_join(m_process_thread, NULL);
+ m_is_activated = false;
+ }
+}
+
+
+/** Build flat arrays of events for DSSI plugins for each Port.
+ */
+void
+AlsaMidiDriver::prepare_block(const samplecount block_start, const samplecount block_end)
+{
+ for (List<AlsaMidiPort*>::iterator i = m_in_ports.begin(); i != m_in_ports.end(); ++i)
+ (*i)->prepare_block(block_start, block_end);
+}
+
+
+/** Add an Alsa MIDI port.
+ *
+ * Realtime safe, this is to be called at the beginning of a process cycle to
+ * insert (and actually begin using) a new port.
+ *
+ * See create_port() and remove_port().
+ */
+void
+AlsaMidiDriver::add_port(AlsaMidiPort* port)
+{
+ if (port->patch_port()->port_info()->is_input())
+ m_in_ports.push_back(port);
+ else
+ m_out_ports.push_back(port);
+}
+
+
+/** Remove an Alsa MIDI port.
+ *
+ * Realtime safe. This is to be called at the beginning of a process cycle to
+ * remove the port from the lists read by the audio thread, so the port
+ * will no longer be used and can be removed afterwards.
+ *
+ * It is the callers responsibility to delete the returned port.
+ */
+AlsaMidiPort*
+AlsaMidiDriver::remove_port(AlsaMidiPort* port)
+{
+ if (port->patch_port()->port_info()->is_input()) {
+ for (List<AlsaMidiPort*>::iterator i = m_in_ports.begin(); i != m_in_ports.end(); ++i)
+ if ((*i) == (AlsaMidiPort*)port)
+ return m_in_ports.remove(i)->elem();
+ } else {
+ for (List<AlsaMidiPort*>::iterator i = m_out_ports.begin(); i != m_out_ports.end(); ++i)
+ if ((*i) == port)
+ return m_out_ports.remove(i)->elem();
+ }
+
+ cerr << "[AlsaMidiDriver::remove_input] WARNING: Failed to find Jack port to remove!" << endl;
+ return NULL;
+}
+
+
+/** MIDI thread.
+ */
+void*
+AlsaMidiDriver::process_midi_in(void* alsa_driver)
+{
+ AlsaMidiDriver* ad = (AlsaMidiDriver*)alsa_driver;
+
+ snd_seq_event_t* ev;
+
+ int npfd = snd_seq_poll_descriptors_count(ad->m_seq_handle, POLLIN);
+ struct pollfd pfd;
+ snd_seq_poll_descriptors(ad->m_seq_handle, &pfd, npfd, POLLIN);
+
+ while ( ! m_midi_thread_exit_flag)
+ if (poll(&pfd, npfd, 100000) > 0)
+ while (snd_seq_event_input(ad->m_seq_handle, &ev) > 0)
+ for (List<AlsaMidiPort*>::iterator i = ad->m_in_ports.begin(); i != ad->m_in_ports.end(); ++i)
+ if ((*i)->port_id() == ev->dest.port)
+ (*i)->event(ev);
+
+ cout << "[AlsaMidiDriver] Exiting MIDI thread." << endl;
+
+ return NULL;
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/AlsaMidiDriver.h b/src/libs/engine/AlsaMidiDriver.h
new file mode 100644
index 00000000..5acbbfbf
--- /dev/null
+++ b/src/libs/engine/AlsaMidiDriver.h
@@ -0,0 +1,127 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef ALSAMIDIDRIVER_H
+#define ALSAMIDIDRIVER_H
+
+#include <alsa/asoundlib.h>
+#include "List.h"
+#include "util/Queue.h"
+#include "MidiDriver.h"
+
+namespace Om {
+
+class Node;
+class SetPortValueEvent;
+class AlsaMidiDriver;
+template <typename T> class PortBase;
+
+static const int MAX_MIDI_EVENT_SIZE = 3;
+
+
+/** Representation of an ALSA MIDI port.
+ *
+ * \ingroup engine
+ */
+class AlsaMidiPort : public DriverPort, public ListNode<AlsaMidiPort*>
+{
+public:
+ AlsaMidiPort(AlsaMidiDriver* driver, PortBase<MidiMessage>* port);
+ virtual ~AlsaMidiPort();
+
+ void event(snd_seq_event_t* const ev);
+
+ void prepare_block(const samplecount block_start, const samplecount block_end);
+
+ void add_to_driver();
+ void remove_from_driver();
+ void set_name(const string& name);
+
+ int port_id() const { return m_port_id; }
+ PortBase<MidiMessage>* patch_port() const { return m_patch_port; }
+
+private:
+ // Prevent copies (undefined)
+ AlsaMidiPort(const AlsaMidiPort&);
+ AlsaMidiPort& operator=(const AlsaMidiPort&);
+
+ AlsaMidiDriver* m_driver;
+ PortBase<MidiMessage>* m_patch_port;
+ int m_port_id;
+ unsigned char** m_midi_pool; ///< Pool of raw MIDI events for MidiMessage::buffer
+ Queue<snd_seq_event_t> m_events;
+};
+
+
+/** Alsa MIDI driver.
+ *
+ * This driver reads Alsa MIDI events and dispatches them to the appropriate
+ * AlsaMidiPort for processing.
+ *
+ * \ingroup engine
+ */
+class AlsaMidiDriver : public MidiDriver
+{
+public:
+ AlsaMidiDriver();
+ ~AlsaMidiDriver();
+
+ void activate();
+ void deactivate();
+
+ bool is_activated() const { return m_is_activated; }
+
+ void prepare_block(const samplecount block_start, const samplecount block_end);
+
+ DriverPort* create_port(PortBase<MidiMessage>* patch_port)
+ { return new AlsaMidiPort(this, patch_port); }
+
+ snd_seq_t* seq_handle() const { return m_seq_handle; }
+ snd_midi_event_t* event_coder() const { return m_event_coder; }
+
+private:
+
+ // Prevent copies (undefined)
+ AlsaMidiDriver(const AlsaMidiDriver&);
+ AlsaMidiDriver& operator=(const AlsaMidiDriver&);
+
+ List<AlsaMidiPort*> m_in_ports;
+ List<AlsaMidiPort*> m_out_ports;
+
+ friend class AlsaMidiPort;
+
+ // Functions for AlsaMidiPort
+ void add_port(AlsaMidiPort* port);
+ AlsaMidiPort* remove_port(AlsaMidiPort* port);
+
+ void add_output(ListNode<AlsaMidiPort*>* port);
+ ListNode<AlsaMidiPort*>* remove_output(AlsaMidiPort* port);
+
+ // MIDI thread
+ static void* process_midi_in(void* me);
+
+ snd_seq_t* m_seq_handle;
+ snd_midi_event_t* m_event_coder;
+ pthread_t m_process_thread;
+ bool m_is_activated;
+ static bool m_midi_thread_exit_flag;
+};
+
+
+} // namespace Om
+
+
+#endif // ALSAMIDIDRIVER_H
diff --git a/src/libs/engine/Array.h b/src/libs/engine/Array.h
new file mode 100644
index 00000000..6b56ecc5
--- /dev/null
+++ b/src/libs/engine/Array.h
@@ -0,0 +1,109 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef ARRAY_H
+#define ARRAY_H
+
+#include "MaidObject.h"
+#include <cassert>
+#include <cstdlib>
+#include "util/types.h"
+
+
+/** An array.
+ *
+ * Has a stack-like push_back() too, for find_process_order...
+ */
+template <class T>
+class Array : public MaidObject
+{
+public:
+ Array(size_t size = 0) : m_size(size), m_top(0), m_elems(NULL) {
+ if (size > 0)
+ m_elems = new T[size];
+ }
+
+ Array(size_t size, T initial_value) : m_size(size), m_top(0), m_elems(NULL) {
+ if (size > 0) {
+ m_elems = new T[size];
+ for (size_t i=0; i < size; ++i)
+ m_elems[i] = initial_value;
+ }
+ }
+
+ Array(size_t size, const Array<T>* contents) : m_size(size), m_top(size+1) {
+ m_elems = new T[size];
+ if (size <= contents->size())
+ memcpy(m_elems, contents->m_elems, size * sizeof(T));
+ else
+ memcpy(m_elems, contents->m_elems, contents->size() * sizeof(T));
+ }
+
+ ~Array() {
+ free();
+ }
+
+ void alloc(size_t num_elems) {
+ assert(num_elems > 0);
+
+ delete[] m_elems;
+ m_size = num_elems;
+ m_top = 0;
+
+ m_elems = new T[num_elems];
+ }
+
+ void alloc(size_t num_elems, T initial_value) {
+ assert(num_elems > 0);
+
+ delete[] m_elems;
+ m_size = num_elems;
+ m_top = 0;
+
+ m_elems = new T[num_elems];
+ for (size_t i=0; i < m_size; ++i)
+ m_elems[i] = initial_value;
+ }
+
+ void free() {
+ delete[] m_elems;
+ m_size = 0;
+ m_top = 0;
+ }
+
+ void push_back(T n) {
+ assert(m_top < m_size);
+ m_elems[m_top++] = n;
+ }
+
+ inline size_t size() const { return m_size; }
+
+ inline T& operator[](size_t i) const { assert(i < m_size); return m_elems[i]; }
+
+ inline T& at(size_t i) const { assert(i < m_size); return m_elems[i]; }
+
+private:
+ // Disallow copies (undefined)
+ Array(const Array& copy);
+ Array& operator=(const Array& copy);
+
+ size_t m_size;
+ size_t m_top; // points to empty element above "top" element
+ T* m_elems;
+};
+
+
+#endif // ARRAY_H
diff --git a/src/libs/engine/AudioDriver.h b/src/libs/engine/AudioDriver.h
new file mode 100644
index 00000000..056aeab4
--- /dev/null
+++ b/src/libs/engine/AudioDriver.h
@@ -0,0 +1,50 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef AUDIODRIVER_H
+#define AUDIODRIVER_H
+
+#include "Driver.h"
+#include "util/types.h"
+#include "List.h"
+
+namespace Om {
+
+class Patch;
+class AudioDriver;
+template <typename T> class PortBase;
+
+
+/** Audio driver abstract base class.
+ *
+ * \ingroup engine
+ */
+class AudioDriver : public Driver<sample>
+{
+public:
+
+ virtual void set_root_patch(Patch* patch) = 0;
+ virtual Patch* root_patch() = 0;
+
+ virtual samplecount buffer_size() const = 0;
+ virtual samplecount sample_rate() const = 0;
+ virtual samplecount time_stamp() const = 0;
+};
+
+
+} // namespace Om
+
+#endif // AUDIODRIVER_H
diff --git a/src/libs/engine/AudioInputNode.cpp b/src/libs/engine/AudioInputNode.cpp
new file mode 100644
index 00000000..8a3594cb
--- /dev/null
+++ b/src/libs/engine/AudioInputNode.cpp
@@ -0,0 +1,49 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "AudioInputNode.h"
+#include "InputPort.h"
+#include "OutputPort.h"
+#include "Plugin.h"
+#include "PortInfo.h"
+#include "Patch.h"
+
+namespace Om {
+
+
+AudioInputNode::AudioInputNode(const string& path, size_t poly, Patch* parent, samplerate srate, size_t buffer_size)
+: BridgeNode<sample>(path, poly, parent, srate, buffer_size)
+{
+ OutputPort<sample>* internal_port = new OutputPort<sample>(this, "in", 0, m_poly,
+ new PortInfo("in", AUDIO, OUTPUT), m_buffer_size);
+ InputPort<sample>* external_port = new InputPort<sample>(parent, m_name, 0, m_poly,
+ new PortInfo(m_name, AUDIO, INPUT), m_buffer_size);
+ external_port->tie(internal_port);
+ m_external_port = external_port;
+ internal_port->set_value(0, 0);
+
+ m_num_ports = 1;
+ m_ports.alloc(m_num_ports);
+ m_ports.at(0) = internal_port;
+
+ m_plugin.type(Plugin::Internal);
+ m_plugin.plug_label("audio_input");
+ m_plugin.name("Om Patch Audio Input Node");
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/AudioInputNode.h b/src/libs/engine/AudioInputNode.h
new file mode 100644
index 00000000..894ec082
--- /dev/null
+++ b/src/libs/engine/AudioInputNode.h
@@ -0,0 +1,45 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef AUDIOINPUTNODE_H
+#define AUDIOINPUTNODE_H
+
+#include <string>
+#include "util/types.h"
+#include "BridgeNode.h"
+
+using std::string;
+
+namespace Om {
+
+class Patch;
+template <typename T> class InputPort;
+
+
+/** Audio input BridgeNode.
+ *
+ * \ingroup engine
+ */
+class AudioInputNode : public BridgeNode<sample>
+{
+public:
+ AudioInputNode(const string& path, size_t poly, Patch* parent, samplerate srate, size_t buffer_size);
+};
+
+
+} // namespace Om
+
+#endif // AUDIOINPUTNODE_H
diff --git a/src/libs/engine/AudioOutputNode.cpp b/src/libs/engine/AudioOutputNode.cpp
new file mode 100644
index 00000000..d9a925f8
--- /dev/null
+++ b/src/libs/engine/AudioOutputNode.cpp
@@ -0,0 +1,50 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "AudioOutputNode.h"
+#include "InputPort.h"
+#include "OutputPort.h"
+#include "Plugin.h"
+#include "Patch.h"
+#include "PortInfo.h"
+#include <cassert>
+
+namespace Om {
+
+
+AudioOutputNode::AudioOutputNode(const string& path, size_t poly, Patch* parent, samplerate srate, size_t buffer_size)
+: BridgeNode<sample>(path, poly, parent, srate, buffer_size)
+{
+ OutputPort<sample>* external_port = new OutputPort<sample>(parent, m_name, 0, m_poly,
+ new PortInfo(m_name, AUDIO, OUTPUT), m_buffer_size);
+ InputPort<sample>* internal_port = new InputPort<sample>(this, "out", 0, m_poly,
+ new PortInfo("out", AUDIO, INPUT), m_buffer_size);
+ internal_port->tie(external_port);
+ m_external_port = external_port;
+ internal_port->set_value(0, 0);
+
+ m_num_ports = 1;
+ m_ports.alloc(m_num_ports);
+ m_ports.at(0) = internal_port;
+
+ m_plugin.type(Plugin::Internal);
+ m_plugin.plug_label("audio_output");
+ m_plugin.name("Om Patch Audio Output Node");
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/AudioOutputNode.h b/src/libs/engine/AudioOutputNode.h
new file mode 100644
index 00000000..6399cfc6
--- /dev/null
+++ b/src/libs/engine/AudioOutputNode.h
@@ -0,0 +1,44 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef AUDIOOUTPUTNODE_H
+#define AUDIOOUTPUTNODE_H
+
+#include <string>
+#include "util/types.h"
+#include "BridgeNode.h"
+
+using std::string;
+
+namespace Om {
+
+template <typename T> class OutputPort;
+
+
+/** Audio output BridgeNode.
+ *
+ * \ingroup engine
+ */
+class AudioOutputNode : public BridgeNode<sample>
+{
+public:
+ AudioOutputNode(const string& path, size_t poly, Patch* parent, samplerate srate, size_t buffer_size);
+};
+
+
+} // namespace Om
+
+#endif // AUDIOOUTPUTNODE_H
diff --git a/src/libs/engine/BridgeNode.cpp b/src/libs/engine/BridgeNode.cpp
new file mode 100644
index 00000000..776bcc92
--- /dev/null
+++ b/src/libs/engine/BridgeNode.cpp
@@ -0,0 +1,159 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "BridgeNode.h"
+//#include "ClientBroadcaster.h"
+#include "Plugin.h"
+#include "Patch.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "Maid.h"
+#include "Driver.h"
+#include "PortInfo.h"
+#include <cassert>
+
+namespace Om {
+
+template <typename T>
+BridgeNode<T>::BridgeNode(const string& path, size_t poly, Patch* parent, samplerate srate, size_t buffer_size)
+: InternalNode(path,
+ (parent->parent_patch() == NULL || poly != parent->parent_patch()->poly()) ? 1 : poly,
+ //poly,
+ parent, srate, buffer_size),
+ m_driver_port(NULL),
+ m_listnode(NULL),
+ m_external_port(NULL)
+{
+ //cerr << "Creating bridge node " << path << " - polyphony: " << m_poly << endl;
+ m_listnode = new ListNode<InternalNode*>(this);
+}
+template
+BridgeNode<sample>::BridgeNode(const string& path, size_t poly, Patch* parent, samplerate srate, size_t buffer_size);
+template
+BridgeNode<MidiMessage>::BridgeNode(const string& path, size_t poly, Patch* parent, samplerate srate, size_t buffer_size);
+
+
+template <typename T>
+BridgeNode<T>::~BridgeNode()
+{
+ delete m_driver_port;
+}
+template BridgeNode<sample>::~BridgeNode();
+template BridgeNode<MidiMessage>::~BridgeNode();
+
+
+template <typename T>
+void
+BridgeNode<T>::activate()
+{
+ assert(om->template driver<T>() != NULL);
+ assert(m_external_port != NULL); // Derived classes must create this
+ assert(parent_patch() != NULL);
+
+ if (parent_patch()->parent() == NULL && om != NULL)
+ m_driver_port = om->template driver<T>()->create_port(m_external_port);
+
+ InternalNode::activate();
+}
+
+
+template <typename T>
+void
+BridgeNode<T>::deactivate()
+{
+ if (m_is_added)
+ remove_from_patch();
+
+ InternalNode::deactivate();
+
+ if (m_driver_port != NULL) {
+ delete m_driver_port;
+ m_driver_port = NULL;
+ }
+}
+
+
+template <typename T>
+void
+BridgeNode<T>::add_to_patch()
+{
+ assert(parent_patch() != NULL);
+
+ parent_patch()->add_bridge_node(m_listnode);
+
+ InternalNode::add_to_patch();
+
+ // Activate driver port now in the audio thread (not before when created, to avoid race issues)
+ if (m_driver_port != NULL)
+ m_driver_port->add_to_driver();
+}
+
+
+template <typename T>
+void
+BridgeNode<T>::remove_from_patch()
+{
+ assert(parent_patch() != NULL);
+
+ if (m_is_added) {
+ if (m_driver_port != NULL)
+ m_driver_port->remove_from_driver();
+ ListNode<InternalNode*>* ln = NULL;
+ ln = parent_patch()->remove_bridge_node(this);
+
+ om->maid()->push(ln);
+ m_listnode = NULL;
+
+ }
+ InternalNode::remove_from_patch();
+}
+
+
+template <typename T>
+void
+BridgeNode<T>::set_path(const Path& new_path)
+{
+ InternalNode::set_path(new_path);
+
+ m_external_port->set_path(new_path);
+
+ if (m_driver_port != NULL)
+ m_driver_port->set_name(path().c_str());
+}
+
+
+#if 0
+template <typename T>
+void
+BridgeNode<T>::send_creation_messages(ClientInterface* client) const
+{
+ InternalNode::send_creation_messages(client);
+ om->client_broadcaster()->send_new_port_to(client, m_external_port);
+
+ // Send metadata
+ for (map<string, string>::const_iterator i = metadata().begin(); i != metadata().end(); ++i)
+ om->client_broadcaster()->send_metadata_update_to(client, path(), (*i).first, (*i).second);
+
+ // Send control value (if necessary)
+ //if (m_external_port->port_info()->is_control())
+ // om->client_broadcaster()->send_control_change_to(client, path(),
+ // ((PortBase<sample>*)m_external_port)->buffer(0)->value_at(0));
+}
+#endif
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/BridgeNode.h b/src/libs/engine/BridgeNode.h
new file mode 100644
index 00000000..99fad912
--- /dev/null
+++ b/src/libs/engine/BridgeNode.h
@@ -0,0 +1,90 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef BRIDGENODE_H
+#define BRIDGENODE_H
+
+#include <string>
+#include "InternalNode.h"
+#include "PortBase.h"
+using std::string;
+
+template <typename T> class ListNode;
+
+namespace Om {
+
+class DriverPort;
+namespace Shared {
+ class ClientInterface;
+} using Shared::ClientInterface;
+
+
+/** A Node to represent input/output Ports on a Patch.
+ *
+ * This node acts as both a Node and a Port (it is the only Node type that
+ * returns non-null for as_port()). The node is a normal Node in a Patch,
+ * the Port is a Port on that Patch (port->parent == node->parent).
+ *
+ * This class handles all DriverPort functionality as well (if this Node
+ * is on a top level Patch).
+ *
+ * Both input and output nodes are handled in this class.
+ *
+ * This node will force itself to monophonic (regardless of the poly parameter
+ * passed to constructor) if the parent of the patch it's representing a port
+ * on has a different polyphony than the patch (since connecting mismatched
+ * polyphonies is impossible).
+ *
+ * \ingroup engine
+ */
+template <typename T>
+class BridgeNode : public InternalNode
+{
+public:
+ virtual ~BridgeNode();
+
+ Port* as_port() { return m_external_port; }
+
+ void activate();
+ void deactivate();
+ void add_to_patch();
+ void remove_from_patch();
+ //void send_creation_messages(ClientInterface* client) const;
+
+ void set_path(const Path& new_path);
+
+protected:
+ // Disallow copies (undefined)
+ BridgeNode(const BridgeNode&);
+ BridgeNode& operator=(const BridgeNode&);
+
+ BridgeNode(const string& path, size_t poly, Patch* parent, samplerate srate, size_t buffer_size);
+
+ /** Driver port, used if this is on a top level Patch */
+ DriverPort* m_driver_port;
+
+ ListNode<InternalNode*>* m_listnode;
+
+ PortBase<T>* m_external_port;
+};
+
+
+template class BridgeNode<sample>;
+template class BridgeNode<MidiMessage>;
+
+} // namespace Om
+
+#endif // BRIDGENODE_H
diff --git a/src/libs/engine/Buffer.cpp b/src/libs/engine/Buffer.cpp
new file mode 100644
index 00000000..963b14a5
--- /dev/null
+++ b/src/libs/engine/Buffer.cpp
@@ -0,0 +1,297 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "Buffer.h"
+#include <iostream>
+#include <cassert>
+#include <stdlib.h>
+#include "MidiMessage.h"
+using std::cerr; using std::endl;
+
+/* TODO: Be sure these functions are vectorized by GCC when it's vectorizer
+ * stops sucking. Probably a good idea to inline them as well */
+
+namespace Om {
+
+
+template <typename T>
+Buffer<T>::Buffer(size_t size)
+: m_size(size),
+ m_filled_size(0),
+ m_is_joined(false),
+ m_state(OK),
+ m_set_value(0),
+ m_data(NULL),
+ m_local_data(NULL)
+{
+ assert(m_size > 0);
+ allocate();
+}
+template Buffer<sample>::Buffer(size_t size);
+template Buffer<MidiMessage>::Buffer(size_t size);
+
+
+/** Allocate and use a locally managed buffer (data).
+ */
+template<typename T>
+void
+Buffer<T>::allocate()
+{
+ assert(!m_is_joined);
+ assert(m_data == NULL);
+ assert(m_local_data == NULL);
+ assert(m_size > 0);
+
+ const int ret = posix_memalign((void**)&m_local_data, 16, m_size * sizeof(T));
+ if (ret != 0) {
+ cerr << "[Buffer] Failed to allocate buffer. Aborting." << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ assert(ret == 0);
+ assert(m_local_data != NULL);
+ m_data = m_local_data;
+
+ set(0, 0, m_size-1);
+}
+template void Buffer<sample>::allocate();
+template void Buffer<MidiMessage>::allocate();
+
+
+/** Free locally allocated buffer.
+ */
+template<typename T>
+void
+Buffer<T>::deallocate()
+{
+ assert(!m_is_joined);
+ free(m_local_data);
+ if (m_data == m_local_data)
+ m_data = NULL;
+ m_local_data = NULL;
+}
+template void Buffer<sample>::deallocate();
+template void Buffer<MidiMessage>::deallocate();
+
+
+/** Empty (ie zero) the buffer.
+ */
+template<typename T>
+void
+Buffer<T>::clear()
+{
+ set(0, 0, m_size-1);
+ m_state = OK;
+ m_filled_size = 0;
+}
+template void Buffer<sample>::clear();
+template void Buffer<MidiMessage>::clear();
+
+
+/** Set value of buffer to @a val after @a start_sample.
+ *
+ * The Buffer will handle setting the intial portion of the buffer to the
+ * value on the next cycle automatically (if @a start_sample is > 0), as
+ * long as pre_process() is called every cycle.
+ */
+template <typename T>
+void
+Buffer<T>::set(T val, size_t start_sample)
+{
+ assert(start_sample < m_size);
+
+ set(val, start_sample, m_size-1);
+
+ if (start_sample > 0)
+ m_state = HALF_SET_CYCLE_1;
+
+ m_set_value = val;
+}
+template void Buffer<sample>::set(sample val, size_t start_sample);
+template void Buffer<MidiMessage>::set(MidiMessage val, size_t start_sample);
+
+
+/** Set a block of buffer to @a val.
+ *
+ * @a start_sample and @a end_sample define the inclusive range to be set.
+ */
+template <typename T>
+void
+Buffer<T>::set(T val, size_t start_sample, size_t end_sample)
+{
+ assert(start_sample >= 0);
+ assert(end_sample >= start_sample);
+ assert(end_sample < m_size);
+ assert(m_data != NULL);
+
+ for (size_t i=start_sample; i <= end_sample; ++i)
+ m_data[i] = val;
+}
+template void Buffer<sample>::set(sample val, size_t start_sample, size_t end_sample);
+template void Buffer<MidiMessage>::set(MidiMessage val, size_t start_sample, size_t end_sample);
+
+
+/** Scale a block of buffer by @a val.
+ *
+ * @a start_sample and @a end_sample define the inclusive range to be set.
+ */
+template <typename T>
+void
+Buffer<T>::scale(T val, size_t start_sample, size_t end_sample)
+{
+ assert(start_sample >= 0);
+ assert(end_sample >= start_sample);
+ assert(end_sample < m_size);
+ assert(m_data != NULL);
+
+ for (size_t i=start_sample; i <= end_sample; ++i)
+ m_data[i] *= val;
+}
+template void Buffer<sample>::scale(sample val, size_t start_sample, size_t end_sample);
+
+
+/** Copy a block of @a src into buffer.
+ *
+ * @a start_sample and @a end_sample define the inclusive range to be set.
+ * This function only copies the same range in one buffer to another.
+ */
+template <typename T>
+void
+Buffer<T>::copy(const Buffer<T>* src, size_t start_sample, size_t end_sample)
+{
+ assert(start_sample >= 0);
+ assert(end_sample >= start_sample);
+ assert(end_sample < m_size);
+ assert(src != NULL);
+ assert(src->data() != NULL);
+ assert(m_data != NULL);
+
+ const T* const src_data = src->data();
+
+ for (size_t i=start_sample; i <= end_sample; ++i)
+ m_data[i] = src_data[i];
+}
+template void Buffer<sample>::copy(const Buffer<sample>* const src, size_t start_sample, size_t end_sample);
+template void Buffer<MidiMessage>::copy(const Buffer<MidiMessage>* const src, size_t start_sample, size_t end_sample);
+
+
+/** Accumulate a block of @a src into @a dst.
+ *
+ * @a start_sample and @a end_sample define the inclusive range to be accumulated.
+ * This function only adds the same range in one buffer to another.
+ */
+template <typename T>
+void
+Buffer<T>::accumulate(const Buffer<T>* const src, size_t start_sample, size_t end_sample)
+{
+ assert(start_sample >= 0);
+ assert(end_sample >= start_sample);
+ assert(end_sample < m_size);
+ assert(src != NULL);
+ assert(src->data() != NULL);
+ assert(m_data != NULL);
+
+ const T* const src_data = src->data();
+
+ for (size_t i=start_sample; i <= end_sample; ++i)
+ m_data[i] += src_data[i];
+
+}
+template void Buffer<sample>::accumulate(const Buffer<sample>* const src, size_t start_sample, size_t end_sample);
+
+
+/** Use another buffer's data instead of the local one.
+ *
+ * This buffer will essentially be identical to @a buf after this call.
+ */
+template<typename T>
+void
+Buffer<T>::join(Buffer<T>* buf)
+{
+ assert(buf->size() == m_size);
+
+ m_data = buf->m_data;
+ m_filled_size = buf->filled_size();
+ m_is_joined = true;
+
+ assert(m_filled_size <= m_size);
+}
+template void Buffer<sample>::join(Buffer<sample>* buf);
+template void Buffer<MidiMessage>::join(Buffer<MidiMessage>* buf);
+
+
+template<typename T>
+void
+Buffer<T>::unjoin()
+{
+ m_is_joined = false;
+ m_data = m_local_data;
+}
+template void Buffer<sample>::unjoin();
+template void Buffer<MidiMessage>::unjoin();
+
+
+template<>
+void
+Buffer<sample>::prepare(samplecount nframes)
+{
+ // FIXME: nframes parameter doesn't actually work,
+ // writing starts from 0 every time
+ assert(m_size == 1 || nframes == m_size);
+
+ switch (m_state) {
+ case HALF_SET_CYCLE_1:
+ m_state = HALF_SET_CYCLE_2;
+ break;
+ case HALF_SET_CYCLE_2:
+ set(m_set_value, 0, m_size-1);
+ m_state = OK;
+ break;
+ default:
+ break;
+ }
+}
+
+
+////// DriverBuffer ////////
+
+template <typename T>
+DriverBuffer<T>::DriverBuffer(size_t size)
+: Buffer<T>(size)
+{
+ Buffer<T>::deallocate(); // FIXME: allocate then immediately deallocate, dirty
+ Buffer<T>::m_data = NULL;
+}
+template DriverBuffer<sample>::DriverBuffer(size_t size);
+template DriverBuffer<MidiMessage>::DriverBuffer(size_t size);
+
+
+/** Set the buffer (data) used.
+ *
+ * This is only to be used by Drivers (to provide zero-copy processing).
+ */
+template<typename T>
+void
+DriverBuffer<T>::set_data(T* data)
+{
+ assert(!m_is_joined);
+ m_data = data;
+}
+template void DriverBuffer<sample>::set_data(sample* data);
+template void DriverBuffer<MidiMessage>::set_data(MidiMessage* data);
+
+
+} // namespace Om
diff --git a/src/libs/engine/Buffer.h b/src/libs/engine/Buffer.h
new file mode 100644
index 00000000..13d62727
--- /dev/null
+++ b/src/libs/engine/Buffer.h
@@ -0,0 +1,91 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef BUFFER_H
+#define BUFFER_H
+
+#include <cstddef>
+#include <cassert>
+#include "util/types.h"
+
+namespace Om {
+
+
+template <typename T>
+class Buffer
+{
+public:
+ Buffer(size_t size);
+
+ void clear();
+ void set(T val, size_t start_sample);
+ void set(T val, size_t start_sample, size_t end_sample);
+ void scale(T val, size_t start_sample, size_t end_sample);
+ void copy(const Buffer<T>* src, size_t start_sample, size_t end_sample);
+ void accumulate(const Buffer<T>* src, size_t start_sample, size_t end_sample);
+
+ void join(Buffer* buf);
+ void unjoin();
+
+ inline T& value_at(size_t offset) { assert(offset < m_size); return m_data[offset]; }
+
+ void prepare(samplecount nframes);
+
+ void filled_size(size_t size) { m_filled_size = size; }
+ size_t filled_size() const { return m_filled_size; }
+ bool is_joined() const { return m_is_joined; }
+ size_t size() const { return m_size; }
+ T* data() const { return m_data; }
+
+protected:
+ enum BufferState { OK, HALF_SET_CYCLE_1, HALF_SET_CYCLE_2 };
+
+ void allocate();
+ void deallocate();
+
+ size_t m_size; ///< Allocated buffer size
+ size_t m_filled_size; ///< Usable buffer size (for MIDI ports etc)
+ bool m_is_joined; ///< Whether or not @ref m_data is shares with another Buffer
+ BufferState m_state; ///< State of buffer for setting values next cycle
+ T m_set_value; ///< Value set by @ref set (may need to be set next cycle)
+
+ T* m_data; ///< Buffer to be returned by data() (not equal to m_local_data if joined)
+ T* m_local_data; ///< Locally allocated buffer
+};
+
+
+/** Less robust Buffer for Driver's use.
+ *
+ * Does not allocate an array by default, and allows direct setting of
+ * data pointer for zero-copy processing.
+ */
+template <typename T>
+class DriverBuffer : public Buffer<T>
+{
+public:
+ DriverBuffer(size_t size);
+
+ void set_data(T* data);
+
+private:
+ using Buffer<T>::m_data;
+ using Buffer<T>::m_is_joined;
+};
+
+
+} // namespace Om
+
+#endif // BUFFER_H
diff --git a/src/libs/engine/ClientBroadcaster.cpp b/src/libs/engine/ClientBroadcaster.cpp
new file mode 100644
index 00000000..f8823233
--- /dev/null
+++ b/src/libs/engine/ClientBroadcaster.cpp
@@ -0,0 +1,331 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ClientBroadcaster.h"
+#include <cassert>
+#include <iostream>
+#include <unistd.h>
+#include "Om.h"
+#include "OmApp.h"
+#include "ObjectStore.h"
+#include "NodeFactory.h"
+#include "util.h"
+#include "Patch.h"
+#include "Node.h"
+#include "PortInfo.h"
+#include "Plugin.h"
+#include "PortBase.h"
+#include "Connection.h"
+#include "AudioDriver.h"
+#include "ObjectSender.h"
+#include "interface/ClientKey.h"
+#include "interface/ClientInterface.h"
+#include "OSCClient.h"
+using std::cout; using std::cerr; using std::endl;
+using Om::Shared::ClientInterface;
+
+namespace Om {
+
+
+/** Register a client to receive messages over the notification band.
+ */
+void
+ClientBroadcaster::register_client(const ClientKey key, CountedPtr<ClientInterface> client)
+{
+ assert(key.type() == ClientKey::OSC_URL);
+ assert(key.uri() != "");
+
+ bool found = false;
+ for (ClientList::iterator i = _clients.begin(); i != _clients.end(); ++i)
+ if ((*i).first == key)
+ found = true;
+
+ if (!found) {
+ _clients.push_back(pair<ClientKey, CountedPtr<ClientInterface> >(key, client));
+ cout << "[ClientBroadcaster] Registered client " << key.uri()
+ << " (" << _clients.size() << " clients)" << endl;
+ } else {
+ cout << "[ClientBroadcaster] Client already registered." << endl;
+ }
+}
+
+
+/** Remove a client from the list of registered clients.
+ *
+ * The removed client is returned (not deleted). It is the caller's
+ * responsibility to delete the returned pointer, if it's not NULL.
+ *
+ * @return true if client was found and removed (and refcount decremented).
+ */
+bool
+ClientBroadcaster::unregister_client(const ClientKey& key)
+{
+ cerr << "FIXME: unregister broken\n";
+ return false;
+
+#if 0
+ if (responder == NULL)
+ return NULL;
+
+ // FIXME: remove filthy cast
+ const string url = lo_address_get_url(((OSCResponder*)responder)->source());
+ ClientInterface* r = NULL;
+
+ for (ClientList::iterator i = _clients.begin(); i != _clients.end(); ++i) {
+ if ((*i).second->url() == url) {
+ r = *i;
+ _clients.erase(i);
+ break;
+ }
+ }
+
+ if (r != NULL)
+ cout << "[OSC] Unregistered client " << r->url() << " (" << _clients.size() << " clients)" << endl;
+ else
+ cerr << "[OSC] ERROR: Unable to find client to unregister!" << endl;
+
+ return r;
+#endif
+}
+
+
+
+/** Looks up the client with the given @a source address (which is used as the
+ * unique identifier for registered clients).
+ *
+ * (A responder is passed to remove the dependency on liblo addresses in request
+ * events, in anticipation of libom and multiple ways of responding to clients).
+ */
+CountedPtr<ClientInterface>
+ClientBroadcaster::client(const ClientKey& key)
+{
+ for (ClientList::iterator i = _clients.begin(); i != _clients.end(); ++i)
+ if ((*i).first == key)
+ return (*i).second;
+
+ cerr << "[ClientBroadcaster] Failed to find client." << endl;
+
+ return NULL;
+}
+
+
+void
+ClientBroadcaster::send_error(const string& msg)
+{
+ for (ClientList::const_iterator i = _clients.begin(); i != _clients.end(); ++i)
+ (*i).second->error(msg);
+}
+
+
+
+void
+ClientBroadcaster::send_plugins_to(ClientInterface* client)
+{
+ om->node_factory()->lock_plugin_list();
+
+ const list<Plugin*>& plugs = om->node_factory()->plugins();
+ const Plugin* plugin;
+
+ lo_timetag tt;
+ lo_timetag_now(&tt);
+ lo_bundle b = lo_bundle_new(tt);
+ lo_message m = lo_message_new();
+ list<lo_message> msgs;
+
+ lo_message_add_int32(m, plugs.size());
+ lo_bundle_add_message(b, "/om/num_plugins", m);
+ msgs.push_back(m);
+
+ for (list<Plugin*>::const_iterator j = plugs.begin(); j != plugs.end(); ++j) {
+ plugin = (*j);
+ m = lo_message_new();
+
+ lo_message_add_string(m, plugin->type_string());
+ lo_message_add_string(m, plugin->uri().c_str());
+ lo_message_add_string(m, plugin->name().c_str());
+ lo_bundle_add_message(b, "/om/plugin", m);
+ msgs.push_back(m);
+ if (lo_bundle_length(b) > 1024) {
+ // FIXME FIXME FIXME dirty, dirty cast
+ lo_send_bundle(((OSCClient*)client)->address(), b);
+ lo_bundle_free(b);
+ b = lo_bundle_new(tt);
+ }
+ }
+
+ if (lo_bundle_length(b) > 0) {
+ // FIXME FIXME FIXME dirty, dirty cast
+ lo_send_bundle(((OSCClient*)client)->address(), b);
+ lo_bundle_free(b);
+ } else {
+ lo_bundle_free(b);
+ }
+ for (list<lo_bundle>::const_iterator i = msgs.begin(); i != msgs.end(); ++i)
+ lo_message_free(*i);
+
+ om->node_factory()->unlock_plugin_list();
+}
+
+
+void
+ClientBroadcaster::send_node(const Node* node)
+{
+ for (ClientList::const_iterator i = _clients.begin(); i != _clients.end(); ++i)
+ ObjectSender::send_node((*i).second.get(), node);
+}
+
+
+void
+ClientBroadcaster::send_port(const Port* port)
+{
+ for (ClientList::const_iterator i = _clients.begin(); i != _clients.end(); ++i)
+ ObjectSender::send_port((*i).second.get(), port);
+}
+
+
+void
+ClientBroadcaster::send_destroyed(const string& path)
+{
+ assert(path != "/");
+ for (ClientList::const_iterator i = _clients.begin(); i != _clients.end(); ++i)
+ (*i).second->object_destroyed(path);
+}
+
+void
+ClientBroadcaster::send_patch_cleared(const string& patch_path)
+{
+ for (ClientList::const_iterator i = _clients.begin(); i != _clients.end(); ++i)
+ (*i).second->patch_cleared(patch_path);
+}
+
+void
+ClientBroadcaster::send_connection(const Connection* const c)
+{
+ for (ClientList::const_iterator i = _clients.begin(); i != _clients.end(); ++i)
+ (*i).second->connection(c->src_port()->path(), c->dst_port()->path());
+}
+
+
+void
+ClientBroadcaster::send_disconnection(const string& src_port_path, const string& dst_port_path)
+{
+ for (ClientList::const_iterator i = _clients.begin(); i != _clients.end(); ++i)
+ (*i).second->disconnection(src_port_path, dst_port_path);
+}
+
+
+void
+ClientBroadcaster::send_patch_enable(const string& patch_path)
+{
+ for (ClientList::const_iterator i = _clients.begin(); i != _clients.end(); ++i)
+ (*i).second->patch_enabled(patch_path);
+}
+
+
+void
+ClientBroadcaster::send_patch_disable(const string& patch_path)
+{
+ for (ClientList::const_iterator i = _clients.begin(); i != _clients.end(); ++i)
+ (*i).second->patch_disabled(patch_path);
+}
+
+
+/** Send notification of a metadata update.
+ *
+ * Like control changes, does not send update to client that set the metadata, if applicable.
+ */
+void
+ClientBroadcaster::send_metadata_update(const string& node_path, const string& key, const string& value)
+{
+ for (ClientList::const_iterator i = _clients.begin(); i != _clients.end(); ++i)
+ (*i).second->metadata_update(node_path, key, value);
+}
+
+
+/** Send notification of a control change.
+ *
+ * If responder is specified, the notification will not be send to the address of
+ * that responder (to avoid sending redundant information back to clients and
+ * forcing clients to ignore things to avoid feedback loops etc).
+ */
+void
+ClientBroadcaster::send_control_change(const string& port_path, float value)
+{
+ for (ClientList::const_iterator i = _clients.begin(); i != _clients.end(); ++i)
+ (*i).second->control_change(port_path, value);
+}
+
+
+void
+ClientBroadcaster::send_program_add(const string& node_path, int bank, int program, const string& name)
+{
+ for (ClientList::const_iterator i = _clients.begin(); i != _clients.end(); ++i)
+ (*i).second->program_add(node_path, bank, program, name);
+}
+
+
+void
+ClientBroadcaster::send_program_remove(const string& node_path, int bank, int program)
+{
+ for (ClientList::const_iterator i = _clients.begin(); i != _clients.end(); ++i)
+ (*i).second->program_remove(node_path, bank, program);
+}
+
+
+/** Send a patch.
+ *
+ * Sends all objects underneath Patch - contained Nodes, etc.
+ */
+void
+ClientBroadcaster::send_patch(const Patch* const p)
+{
+ for (ClientList::const_iterator i = _clients.begin(); i != _clients.end(); ++i)
+ ObjectSender::send_patch((*i).second.get(), p);
+}
+
+
+/** Sends notification of an OmObject's renaming
+ */
+void
+ClientBroadcaster::send_rename(const string& old_path, const string& new_path)
+{
+ for (ClientList::const_iterator i = _clients.begin(); i != _clients.end(); ++i)
+ (*i).second->object_renamed(old_path, new_path);
+}
+
+
+/** Sends all OmObjects known to the engine.
+ */
+void
+ClientBroadcaster::send_all_objects()
+{
+ cerr << "FIXME: send_all" << endl;
+
+ //for (ClientList::const_iterator i = _clients.begin(); i != _clients.end(); ++i)
+ // (*i).second->send_all_objects();
+}
+
+/*
+void
+ClientBroadcaster::send_node_creation_messages(const Node* const node)
+{
+ // This is pretty stupid :/ in and out and back again!
+ for (ClientList::const_iterator i = _clients.begin(); i != _clients.end(); ++i)
+ node->send_creation_messages((*i).second);
+}
+*/
+
+} // namespace Om
diff --git a/src/libs/engine/ClientBroadcaster.h b/src/libs/engine/ClientBroadcaster.h
new file mode 100644
index 00000000..b0a60f57
--- /dev/null
+++ b/src/libs/engine/ClientBroadcaster.h
@@ -0,0 +1,101 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef CLIENTBROADCASTER_H
+#define CLIENTBROADCASTER_H
+
+#include <string>
+#include <iostream>
+#include <utility>
+#include <list>
+#include <lo/lo.h>
+#include <pthread.h>
+#include "util/types.h"
+#include "interface/ClientInterface.h"
+#include "util/CountedPtr.h"
+
+using std::list; using std::string; using std::pair;
+
+namespace Om {
+
+class Node;
+class Port;
+class PortInfo;
+class Plugin;
+class Patch;
+class Connection;
+class Responder;
+namespace Shared { class ClientKey; }
+using Shared::ClientKey;
+using Shared::ClientInterface;
+
+/** Broadcaster for all clients.
+ *
+ * This sends messages to all client simultaneously through the opaque
+ * ClientInterface. The clients may be OSC driver, in process, theoretically
+ * anything that implements ClientInterface.
+ *
+ * This also serves as the database of all registered clients.
+ *
+ * \ingroup engine
+ */
+class ClientBroadcaster
+{
+public:
+ void register_client(const ClientKey key, CountedPtr<ClientInterface> client);
+ bool unregister_client(const ClientKey& key);
+
+ CountedPtr<ClientInterface> client(const ClientKey& key);
+
+ // Notification band:
+
+ //void send_client_registration(const string& url, int client_id);
+
+ // Error that isn't the direct result of a request
+ void send_error(const string& msg);
+
+ void send_plugins_to(ClientInterface* client);
+
+ //void send_node_creation_messages(const Node* const node);
+
+ void send_patch(const Patch* const p);
+ void send_node(const Node* const node);
+ void send_port(const Port* port);
+ void send_destroyed(const string& path);
+ void send_patch_cleared(const string& patch_path);
+ void send_connection(const Connection* const connection);
+ void send_disconnection(const string& src_port_path, const string& dst_port_path);
+ void send_rename(const string& old_path, const string& new_path);
+ void send_all_objects();
+ void send_patch_enable(const string& patch_path);
+ void send_patch_disable(const string& patch_path);
+ void send_metadata_update(const string& node_path, const string& key, const string& value);
+ void send_control_change(const string& port_path, float value);
+ void send_program_add(const string& node_path, int bank, int program, const string& name);
+ void send_program_remove(const string& node_path, int bank, int program);
+
+private:
+ typedef list<pair<ClientKey, CountedPtr<ClientInterface> > > ClientList;
+ //list<pair<ClientKey, ClientInterface* const> > _clients;
+ ClientList _clients;
+};
+
+
+
+} // namespace Om
+
+#endif // CLIENTBROADCASTER_H
+
diff --git a/src/libs/engine/Connection.cpp b/src/libs/engine/Connection.cpp
new file mode 100644
index 00000000..d1592009
--- /dev/null
+++ b/src/libs/engine/Connection.cpp
@@ -0,0 +1,45 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "Connection.h"
+#include "util.h"
+#include "Node.h"
+#include "Om.h"
+#include "Port.h"
+
+namespace Om {
+
+
+/** Constructor for a connection from a node's output port.
+ *
+ * This handles both polyphonic and monophonic nodes, transparently to the
+ * user (InputPort).
+ */
+Connection::Connection(Port* const src_port, Port* const dst_port)
+: m_src_port(src_port),
+ m_dst_port(dst_port),
+ m_is_poly_to_mono( (src_port->parent_node()->poly() > dst_port->parent_node()->poly()) ),
+ m_pending_disconnection(false)
+{
+ assert(src_port != NULL);
+ assert(dst_port != NULL);
+
+ assert((src_port->parent_node()->poly() == dst_port->parent_node()->poly())
+ || (src_port->parent_node()->poly() == 1 || dst_port->parent_node()->poly() == 1));
+}
+
+} // namespace Om
+
diff --git a/src/libs/engine/Connection.h b/src/libs/engine/Connection.h
new file mode 100644
index 00000000..769c2047
--- /dev/null
+++ b/src/libs/engine/Connection.h
@@ -0,0 +1,66 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef CONNECTION_H
+#define CONNECTION_H
+
+#include <cstdlib>
+#include "MaidObject.h"
+#include "util/types.h"
+
+namespace Om {
+
+class Port;
+
+
+/** Represents a single inbound connection for an InputPort.
+ *
+ * This can be a group of ports (ie coming from a polyphonic Node) or
+ * a single Port. This class exists basically as an abstraction of mixing
+ * down polyphonic inputs, so InputPort can just deal with mixing down
+ * multiple connections (oblivious to the polyphonic situation of the
+ * connection itself).
+ *
+ * \ingroup engine
+ */
+class Connection : public MaidObject
+{
+public:
+ virtual ~Connection() {}
+
+ Port* src_port() const { return m_src_port; }
+ Port* dst_port() const { return m_dst_port; }
+
+ /** Used by some (recursive) events to prevent double disconnections */
+ bool pending_disconnection() { return m_pending_disconnection; }
+ void pending_disconnection(bool b) { m_pending_disconnection = b; }
+
+protected:
+ // Disallow copies (undefined)
+ Connection(const Connection&);
+
+ Connection(Port* const src_port, Port* const dst_port);
+
+ Port* const m_src_port;
+ Port* const m_dst_port;
+ bool m_is_poly_to_mono;
+ bool m_pending_disconnection;
+};
+
+
+} // namespace Om
+
+#endif // CONNECTION_H
diff --git a/src/libs/engine/ConnectionBase.cpp b/src/libs/engine/ConnectionBase.cpp
new file mode 100644
index 00000000..c8936818
--- /dev/null
+++ b/src/libs/engine/ConnectionBase.cpp
@@ -0,0 +1,96 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ConnectionBase.h"
+#include "util.h"
+#include "InputPort.h"
+#include "OutputPort.h"
+#include "Node.h"
+#include "Om.h"
+#include "Port.h"
+
+namespace Om {
+
+
+/** Constructor for a connection from a node's output port.
+ *
+ * This handles both polyphonic and monophonic nodes, transparently to the
+ * user (InputPort).
+ */
+template <typename T>
+ConnectionBase<T>::ConnectionBase(OutputPort<T>* const src_port, InputPort<T>* const dst_port)
+: Connection(src_port, dst_port),
+ m_local_buffer(NULL),
+ m_is_poly_to_mono( (src_port->parent_node()->poly() > dst_port->parent_node()->poly()) ),
+ m_buffer_size(src_port->buffer_size()),
+ m_pending_disconnection(false)
+{
+ assert((src_port->parent_node()->poly() == dst_port->parent_node()->poly())
+ || (src_port->parent_node()->poly() == 1 || dst_port->parent_node()->poly() == 1));
+
+ if (m_is_poly_to_mono) // Poly -> Mono connection, need a buffer to mix in to
+ m_local_buffer = new Buffer<T>(m_buffer_size);
+}
+template ConnectionBase<sample>::ConnectionBase(OutputPort<sample>* const src_port, InputPort<sample>* const dst_port);
+template ConnectionBase<MidiMessage>::ConnectionBase(OutputPort<MidiMessage>* const src_port, InputPort<MidiMessage>* const dst_port);
+
+
+template <typename T>
+ConnectionBase<T>::~ConnectionBase()
+{
+ delete m_local_buffer;
+}
+template ConnectionBase<sample>::~ConnectionBase();
+template ConnectionBase<MidiMessage>::~ConnectionBase();
+
+
+template <typename sample>
+void
+ConnectionBase<sample>::prepare_buffers()
+{
+ /* Thought: A poly output port can be connected to multiple mono input
+ * ports, which means this mix down would have to happen many times.
+ * Adding a method to OutputPort that mixes down all it's outputs into
+ * a buffer (if it hasn't been done already this cycle) and returns that
+ * would avoid having to mix multiple times. Probably not a very common
+ * case, but it would be faster anyway. */
+
+ if (m_is_poly_to_mono) {
+ m_local_buffer->copy(src_port()->buffer(0), 0, m_buffer_size-1);
+
+ // Mix all the source's voices down into local buffer starting at the second
+ // voice (buffer is already set to first voice above)
+ for (size_t j=1; j < src_port()->poly(); ++j)
+ m_local_buffer->accumulate(src_port()->buffer(j), 0, m_buffer_size-1);
+
+ // Scale the buffer down.
+ if (src_port()->poly() > 1)
+ m_local_buffer->scale(1.0f/(float)src_port()->poly(), 0, m_buffer_size-1);
+ }
+}
+template void ConnectionBase<sample>::prepare_buffers();
+
+
+// FIXME: MIDI mixing not implemented
+template <>
+void
+ConnectionBase<MidiMessage>::prepare_buffers()
+{
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/ConnectionBase.h b/src/libs/engine/ConnectionBase.h
new file mode 100644
index 00000000..72acae2e
--- /dev/null
+++ b/src/libs/engine/ConnectionBase.h
@@ -0,0 +1,105 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef CONNECTIONBASE_H
+#define CONNECTIONBASE_H
+
+#include "util/types.h"
+#include "OutputPort.h"
+#include "Connection.h"
+
+namespace Om {
+
+class MidiMessage;
+class Port;
+template <typename T> class InputPort;
+
+
+/** A Connection with a type.
+ *
+ * \ingroup engine
+ */
+template <typename T>
+class ConnectionBase : public Connection
+{
+public:
+ ConnectionBase(OutputPort<T>* const src_port, InputPort<T>* const dst_port);
+ virtual ~ConnectionBase();
+
+ void prepare_buffers();
+
+ inline OutputPort<T>* src_port() const { return (OutputPort<T>*)m_src_port; }
+ inline InputPort<T>* dst_port() const { return (InputPort<T>*)m_dst_port; }
+
+ /** Used by some (recursive) events to prevent double disconnections */
+ bool pending_disconnection() { return m_pending_disconnection; }
+ void pending_disconnection(bool b) { m_pending_disconnection = b; }
+
+ /** Get the buffer for a particular voice.
+ * A ConnectionBase is smart - it knows the destination port respondering the
+ * buffer, and will return accordingly (ie the same buffer for every voice
+ * in a mono->poly connection).
+ */
+ inline Buffer<T>* buffer(size_t voice) const;
+
+private:
+ // Disallow copies (undefined)
+ ConnectionBase(const ConnectionBase& copy);
+ ConnectionBase& operator=(const ConnectionBase&);
+
+ Buffer<T>* m_local_buffer; ///< Only used for poly->mono connections
+ bool m_is_poly_to_mono;
+ size_t m_buffer_size;
+ bool m_pending_disconnection;
+};
+
+
+template <>
+inline Buffer<sample>*
+ConnectionBase<sample>::buffer(size_t voice) const
+{
+ PortBase<sample>* const src_port = (PortBase<sample>*)m_src_port;
+
+ if (m_is_poly_to_mono) {
+ return m_local_buffer;
+ } else {
+ if (src_port->poly() == 1)
+ return src_port->buffer(0);
+ else
+ return src_port->buffer(voice);
+ }
+}
+
+
+template <>
+inline Buffer<MidiMessage>*
+ConnectionBase<MidiMessage>::buffer(size_t voice) const
+{
+ // No such thing as polyphonic MIDI ports
+ assert(m_src_port->poly() == 1);
+ assert(m_dst_port->poly() == 1);
+
+ PortBase<MidiMessage>* const src_port = (PortBase<MidiMessage>*)m_src_port;
+ return src_port->buffer(0);
+}
+
+
+template class ConnectionBase<sample>;
+template class ConnectionBase<MidiMessage>;
+
+} // namespace Om
+
+#endif // CONNECTIONBASE_H
diff --git a/src/libs/engine/ControlInputNode.cpp b/src/libs/engine/ControlInputNode.cpp
new file mode 100644
index 00000000..d4d83558
--- /dev/null
+++ b/src/libs/engine/ControlInputNode.cpp
@@ -0,0 +1,49 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ControlInputNode.h"
+#include "util/types.h"
+#include "Patch.h"
+#include "OutputPort.h"
+#include "InputPort.h"
+#include "Plugin.h"
+#include "PortInfo.h"
+
+namespace Om {
+
+
+ControlInputNode::ControlInputNode(const string& path, size_t poly, Patch* parent, samplerate srate, size_t buffer_size)
+: BridgeNode<sample>(path, poly, parent, srate, buffer_size)
+{
+ OutputPort<sample>* internal_port = new OutputPort<sample>(this, "in", 0, m_poly,
+ new PortInfo("in", CONTROL, OUTPUT), 1);
+ InputPort<sample>* external_port = new InputPort<sample>(parent, m_name, 0, m_poly,
+ new PortInfo(m_name, CONTROL, INPUT), 1);
+ external_port->tie(internal_port);
+ m_external_port = external_port;
+
+ m_num_ports = 1;
+ m_ports.alloc(m_num_ports);
+ m_ports.at(0) = internal_port;
+
+ m_plugin.type(Plugin::Internal);
+ m_plugin.plug_label("control_input");
+ m_plugin.name("Om Patch Control Input Node");
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/ControlInputNode.h b/src/libs/engine/ControlInputNode.h
new file mode 100644
index 00000000..0be82af0
--- /dev/null
+++ b/src/libs/engine/ControlInputNode.h
@@ -0,0 +1,44 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef CONTROLINPUTNODE_H
+#define CONTROLINPUTNODE_H
+
+#include <string>
+#include "util/types.h"
+#include "BridgeNode.h"
+
+using std::string;
+
+namespace Om {
+
+template <typename T> class InputPort;
+
+
+/** Control input BridgeNode.
+ *
+ * \ingroup engine
+ */
+class ControlInputNode : public BridgeNode<sample>
+{
+public:
+ ControlInputNode(const string& path, size_t poly, Patch* parent, samplerate srate, size_t buffer_size);
+};
+
+
+} // namespace Om
+
+#endif // CONTROLINPUTNODE_H
diff --git a/src/libs/engine/ControlOutputNode.cpp b/src/libs/engine/ControlOutputNode.cpp
new file mode 100644
index 00000000..7e1f3e2a
--- /dev/null
+++ b/src/libs/engine/ControlOutputNode.cpp
@@ -0,0 +1,48 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ControlOutputNode.h"
+#include "InputPort.h"
+#include "OutputPort.h"
+#include "PortInfo.h"
+#include "Plugin.h"
+#include "Patch.h"
+
+namespace Om {
+
+
+ControlOutputNode::ControlOutputNode(const string& path, size_t poly, Patch* parent, samplerate srate, size_t buffer_size)
+: BridgeNode<sample>(path, poly, parent, srate, buffer_size)
+{
+ OutputPort<sample>* external_port = new OutputPort<sample>(parent, m_name, 0, m_poly,
+ new PortInfo(m_name, CONTROL, OUTPUT), 1);
+ InputPort<sample>* internal_port = new InputPort<sample>(this, "out", 0, m_poly,
+ new PortInfo("out", CONTROL, INPUT), 1);
+ internal_port->tie(external_port);
+ m_external_port = external_port;
+
+ m_num_ports = 1;
+ m_ports.alloc(m_num_ports);
+ m_ports.at(0) = internal_port;
+
+ m_plugin.type(Plugin::Internal);
+ m_plugin.plug_label("control_output");
+ m_plugin.name("Om Patch Control Output Node");
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/ControlOutputNode.h b/src/libs/engine/ControlOutputNode.h
new file mode 100644
index 00000000..64fd9ce6
--- /dev/null
+++ b/src/libs/engine/ControlOutputNode.h
@@ -0,0 +1,45 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef CONTROLOUTPUTNODE_H
+#define CONTROLOUTPUTNODE_H
+
+#include <string>
+#include "util/types.h"
+#include "BridgeNode.h"
+
+using std::string;
+
+namespace Om {
+
+class Port;
+template <typename T> class OutputPort;
+
+
+/** Control output BridgeNode.
+ *
+ * \ingroup engine
+ */
+class ControlOutputNode : public BridgeNode<sample>
+{
+public:
+ ControlOutputNode(const string& path, size_t poly, Patch* parent, samplerate srate, size_t buffer_size);
+};
+
+
+} // namespace Om
+
+#endif // CONTROLOUTPUTNODE_H
diff --git a/src/libs/engine/Controller.h b/src/libs/engine/Controller.h
new file mode 100644
index 00000000..facfb4c2
--- /dev/null
+++ b/src/libs/engine/Controller.h
@@ -0,0 +1,82 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef CONTROLLER_H
+#define CONTROLLER_H
+
+namespace Om {
+
+
+/** Public interface to Om engine shared library.
+ *
+ * This is an interface to all the audio-processing related functionality
+ * only. OSC communication, LASH session management, etc, are not part of
+ * this library.
+ */
+class Controller {
+public:
+ void init();
+ void quit();
+
+ void load_plugins();
+
+ void activate_engine();
+ void deactivate_engine();
+
+ void create_patch(const char* path, unsigned int polyphony);
+ void clear_patch(const char* path);
+ void enable_patch(const char* path);
+ void disable_patch(const char* path);
+ void rename(const char* old_path, const char* new_path);
+
+ void create_node(const char* path,
+ const char* type,
+ const char* library_name,
+ const char* library_label,
+ unsigned int polyphony);
+
+ void destroy(const char* path);
+
+ void connect(const char* src_port_path, const char* dst_port_path);
+ void disconnect(const char* src_port_path, const char* dst_port_path);
+ void disconnect_all(const char* path);
+
+ void set_port_value(const char* path, float value);
+ void set_port_value_voice(const char* path, unsigned int voice);
+ void set_port_value_slow(const char* path, float value);
+
+ void note_on(const char* node_path,
+ unsigned char note_num,
+ unsigned char velocity);
+
+ void note_off(const char* node_path,
+ unsigned char note_num);
+
+ void all_notes_off(const char* node_path);
+ void midi_learn(const char* node_path);
+
+ void get_metadata();
+ void set_metadata();
+ void responder_plugins();
+ void responder_all_objects();
+ void responder_port_value();
+};
+
+
+} // namespace Om
+
+#endif // CONTROLLER_H
+
diff --git a/src/libs/engine/DSSIPlugin.cpp b/src/libs/engine/DSSIPlugin.cpp
new file mode 100644
index 00000000..e8525b7b
--- /dev/null
+++ b/src/libs/engine/DSSIPlugin.cpp
@@ -0,0 +1,340 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "DSSIPlugin.h"
+#include <map>
+#include <set>
+#include "Om.h"
+#include "OmApp.h"
+#include "ClientBroadcaster.h"
+#include "interface/ClientInterface.h"
+#include "InputPort.h"
+#include "PortInfo.h"
+
+using namespace std;
+
+namespace Om {
+
+
+DSSIPlugin::DSSIPlugin(const string& name, size_t poly, Patch* parent, DSSI_Descriptor* descriptor, samplerate srate, size_t buffer_size)
+: LADSPAPlugin(name, 1, parent, descriptor->LADSPA_Plugin, srate, buffer_size),
+ m_dssi_descriptor(descriptor),
+ m_ui_addr(NULL),
+ m_bank(-1),
+ m_program(-1),
+ m_midi_in_port(NULL),
+ m_alsa_events(new snd_seq_event_t[m_buffer_size]),
+ m_alsa_encoder(NULL)
+{
+ if (has_midi_input())
+ m_num_ports = descriptor->LADSPA_Plugin->PortCount + 1;
+
+ snd_midi_event_new(3, &m_alsa_encoder);
+}
+
+
+DSSIPlugin::~DSSIPlugin()
+{
+ if (m_ui_addr != NULL)
+ lo_address_free(m_ui_addr);
+
+ snd_midi_event_free(m_alsa_encoder);
+ delete [] m_alsa_events;
+}
+
+
+/** This needs to be overridden here because LADSPAPlugin::instantiate()
+ * allocates the port array, and we want to add the MIDI input port to that
+ * array.
+ */
+bool
+DSSIPlugin::instantiate()
+{
+ if (!LADSPAPlugin::instantiate())
+ return false;
+
+ if (has_midi_input()) {
+ assert(m_num_ports == m_descriptor->PortCount + 1);
+ assert(m_ports.size() == m_descriptor->PortCount + 1);
+
+ m_midi_in_port = new InputPort<MidiMessage>(this, "MIDI In", m_num_ports-1, 1,
+ new PortInfo("MIDI In", MIDI, INPUT), m_buffer_size);
+ m_ports.at(m_num_ports-1) = m_midi_in_port;
+ }
+
+ return true;
+}
+
+
+void
+DSSIPlugin::activate()
+{
+ LADSPAPlugin::activate();
+
+ update_programs(false);
+ set_default_program();
+
+ snd_midi_event_reset_encode(m_alsa_encoder);
+}
+
+
+void
+DSSIPlugin::set_ui_url(const string& url)
+{
+ if (m_ui_addr != NULL)
+ lo_address_free(m_ui_addr);
+
+ m_ui_url = url;
+ m_ui_addr = lo_address_new_from_url(url.c_str());
+ char* base_path = lo_url_get_path(url.c_str());
+ m_ui_base_path = base_path;
+ free(base_path);
+ cerr << "Set UI base path to " << m_ui_base_path << endl;
+}
+
+
+void
+DSSIPlugin::set_control(size_t port_num, sample val)
+{
+ assert(port_num < m_descriptor->PortCount);
+ ((PortBase<sample>*)m_ports.at(port_num))->set_value(val, 0);
+}
+
+
+void
+DSSIPlugin::configure(const string& key, const string& val)
+{
+ m_dssi_descriptor->configure(m_instances[0], key.c_str(), val.c_str());
+ m_configures[key] = val;
+ update_programs(true);
+}
+
+
+void
+DSSIPlugin::program(int bank, int program)
+{
+ if (m_dssi_descriptor->select_program)
+ m_dssi_descriptor->select_program(m_instances[0], bank, program);
+
+ m_bank = bank;
+ m_program = program;
+}
+
+
+void
+DSSIPlugin::convert_events()
+{
+ assert(has_midi_input());
+ assert(m_midi_in_port != NULL);
+
+ Buffer<MidiMessage>& buffer = *m_midi_in_port->buffer(0);
+ m_encoded_events = 0;
+
+ for (size_t i = 0; i < buffer.filled_size(); ++i) {
+ snd_midi_event_encode(m_alsa_encoder, buffer.value_at(i).buffer,
+ buffer.value_at(i).size,
+ &m_alsa_events[m_encoded_events]);
+ m_alsa_events[m_encoded_events].time.tick = buffer.value_at(i).time;
+ if (m_alsa_events[m_encoded_events].type != SND_SEQ_EVENT_NONE)
+ ++m_encoded_events;
+ }
+}
+
+
+bool
+DSSIPlugin::has_midi_input() const
+{
+ return (m_dssi_descriptor->run_synth || m_dssi_descriptor->run_multiple_synths);
+}
+
+
+void
+DSSIPlugin::run(size_t nframes)
+{
+ NodeBase::run(nframes);
+
+ if (m_dssi_descriptor->run_synth) {
+ convert_events();
+ m_dssi_descriptor->run_synth(m_instances[0], nframes,
+ m_alsa_events, m_encoded_events);
+ } else if (m_dssi_descriptor->run_multiple_synths) {
+ convert_events();
+ // I hate this stupid function
+ snd_seq_event_t* events[1] = { m_alsa_events };
+ long unsigned events_sizes[1] = { m_encoded_events };
+ m_dssi_descriptor->run_multiple_synths(1, m_instances, nframes,
+ events, events_sizes);
+ } else {
+ LADSPAPlugin::run(nframes);
+ }
+}
+
+
+void
+DSSIPlugin::send_control(int port_num, float value)
+{
+ string path = m_ui_base_path + "/control";
+ lo_send(m_ui_addr, path.c_str(), "if", port_num, value);
+}
+
+
+void
+DSSIPlugin::send_program(int bank, int value)
+{
+ string path = m_ui_base_path + "/program";
+ lo_send(m_ui_addr, path.c_str(), "ii", bank, value);
+}
+
+
+void
+DSSIPlugin::send_configure(const string& key, const string& val)
+{
+ string path = m_ui_base_path + "/configure";
+ lo_send(m_ui_addr, path.c_str(), "ss", key.c_str(), val.c_str());
+}
+
+
+void
+DSSIPlugin::send_show()
+{
+ string path = m_ui_base_path + "/show";
+ lo_send(m_ui_addr, path.c_str(), NULL);
+}
+
+
+void
+DSSIPlugin::send_hide()
+{
+ string path = m_ui_base_path + "/hide";
+ lo_send(m_ui_addr, path.c_str(), NULL);
+}
+
+
+void
+DSSIPlugin::send_quit()
+{
+ string path = m_ui_base_path + "/quit";
+ lo_send(m_ui_addr, path.c_str(), NULL);
+}
+
+
+void
+DSSIPlugin::send_update()
+{
+ // send "configure"s
+ for (map<string, string>::iterator i = m_configures.begin(); i != m_configures.end(); ++i)
+ send_configure((*i).first, (*i).second);
+
+ // send "program"
+ send_program(m_bank, m_program);
+
+ // send "control"s
+ for (size_t i=0; i < m_ports.size(); ++i)
+ if (m_ports[i]->port_info()->is_control())
+ send_control(m_ports[i]->num(), ((PortBase<sample>*)m_ports[i])->buffer(0)->value_at(0));
+
+ // send "show" FIXME: not to spec
+ send_show();
+}
+
+
+bool
+DSSIPlugin::update_programs(bool send_events)
+{
+ // remember all old banks and programs
+ set<pair<int, int> > to_be_deleted;
+ map<int, Bank>::const_iterator iter;
+ Bank::const_iterator iter2;
+ for (iter = m_banks.begin(); iter != m_banks.end(); ++iter) {
+ for (iter2 = iter->second.begin(); iter2 != iter->second.end(); ++iter2) {
+ to_be_deleted.insert(make_pair(iter->first, iter2->first));
+ }
+ }
+
+ // iterate over all programs
+ if (m_dssi_descriptor->get_program) {
+ for (int i = 0; true; ++i) {
+ const DSSI_Program_Descriptor* descriptor =
+ m_dssi_descriptor->get_program(m_instances[0], i);
+ if (!descriptor)
+ break;
+
+ iter = m_banks.find(descriptor->Bank);
+ if (iter == m_banks.end() ||
+ iter->second.find(descriptor->Program) == iter->second.end() ||
+ iter->second.find(descriptor->Program)->second != descriptor->Name) {
+ m_banks[descriptor->Bank][descriptor->Program] = descriptor->Name;
+ if (send_events) {
+ om->client_broadcaster()->send_program_add(path(), descriptor->Bank,
+ descriptor->Program,
+ descriptor->Name);
+ }
+ to_be_deleted.erase(make_pair(descriptor->Bank, descriptor->Program));
+ }
+ }
+ }
+
+ // remove programs that has disappeared from the plugin
+ set<pair<int, int> >::const_iterator set_iter;
+ for (set_iter = to_be_deleted.begin();
+ set_iter != to_be_deleted.end(); ++set_iter) {
+ m_banks[set_iter->first].erase(set_iter->second);
+ if (send_events)
+ om->client_broadcaster()->send_program_remove(path(), set_iter->first, set_iter->second);
+ if (m_banks[set_iter->first].size() == 0)
+ m_banks.erase(set_iter->first);
+ }
+
+ return true;
+}
+
+
+void
+DSSIPlugin::set_default_program()
+{
+ map<int, Bank>::const_iterator iter = m_banks.begin();
+ if (iter != m_banks.end()) {
+ Bank::const_iterator iter2 = iter->second.begin();
+ if (iter2 != iter->second.end())
+ program(iter->first, iter2->first);
+ }
+}
+
+
+const map<int, DSSIPlugin::Bank>&
+DSSIPlugin::get_programs() const
+{
+ return m_banks;
+}
+
+
+/*
+void
+DSSIPlugin::send_creation_messages(ClientInterface* client) const
+{
+ LADSPAPlugin::send_creation_messages(client);
+
+ for (map<int, Bank>::const_iterator i = get_programs().begin();
+ i != get_programs().end(); ++i) {
+
+ for (Bank::const_iterator j = i->second.begin(); j != i->second.end(); ++j)
+ client->program_add(path(),
+ i->first, j->first, j->second);
+ }
+}
+*/
+
+} // namespace Om
diff --git a/src/libs/engine/DSSIPlugin.cpp.orig b/src/libs/engine/DSSIPlugin.cpp.orig
new file mode 100644
index 00000000..7c8060f0
--- /dev/null
+++ b/src/libs/engine/DSSIPlugin.cpp.orig
@@ -0,0 +1,207 @@
+/* This file is part of Om. Copyright (C) 2005 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "DSSIPlugin.h"
+#include <map>
+#include "Om.h"
+#include "OmApp.h"
+#include "config.h"
+#include "Patch.h"
+#include "PortBase.h"
+#include "InputPort.h"
+#include "PortInfo.h"
+
+using std::map;
+
+namespace Om {
+
+
+DSSIPlugin::DSSIPlugin(const string& name, uint poly, Patch* parent, DSSI_Descriptor* descriptor, samplerate srate, size_t buffer_size)
+: LADSPAPlugin(name, 1, parent, descriptor->LADSPA_Plugin, srate, buffer_size),
+ m_ui_addr(NULL),
+ m_bank(-1),
+ m_program(-1),
+ m_dssi_descriptor(descriptor)
+{
+ m_num_ports = descriptor->LADSPA_Plugin->PortCount + 1;
+ m_ports.alloc(m_num_ports);
+
+ m_midi_in_port = new InputPort<MidiMessage>(this, "MIDI In", m_num_ports-1, 1,
+ new PortInfo("MIDI In", MIDI, INPUT), m_buffer_size);
+
+ m_ports.at(m_num_ports-1) = m_midi_in_port;
+}
+
+
+DSSIPlugin::~DSSIPlugin()
+{
+ if (m_ui_addr != NULL)
+ lo_address_free(m_ui_addr);
+}
+
+
+void
+DSSIPlugin::activate()
+{
+ LADSPAPlugin::activate();
+}
+
+
+void
+DSSIPlugin::set_ui_url(const string& url)
+{
+ if (m_ui_addr != NULL)
+ lo_address_free(m_ui_addr);
+
+ m_ui_url = url;
+ m_ui_addr = lo_address_new_from_url(url.c_str());
+ char* base_path = lo_url_get_path(url.c_str());
+ m_ui_base_path = base_path;
+ free(base_path);
+ cerr << "Set UI base path to " << m_ui_base_path << endl;
+}
+
+
+void
+DSSIPlugin::set_control(uint port_num, sample val)
+{
+ assert(port_num < m_descriptor->PortCount);
+ ((PortBase<sample>*)m_ports.at(port_num))->set_value(0, val);
+}
+
+
+void
+DSSIPlugin::configure(const string& key, const string& val)
+{
+ m_dssi_descriptor->configure(m_instances[0], key.c_str(), val.c_str());
+ m_configures[key] = val;
+}
+
+
+void
+DSSIPlugin::program(int bank, int program)
+{
+ if (m_dssi_descriptor->select_program)
+ m_dssi_descriptor->select_program(m_instances[0], bank, program);
+
+ m_bank = bank;
+ m_program = program;
+}
+
+
+void
+DSSIPlugin::run(size_t nframes)
+{
+ NodeBase::run(nframes);
+
+ /*if (m_dssi_descriptor->run_synth) {
+ m_dssi_descriptor->run_synth(m_instances[0], nframes,
+ parent_patch()->dssi_events_array(), parent_patch()->dssi_events_size());
+ } else if (m_dssi_descriptor->run_multiple_synths) { // I hate this stupid function
+ snd_seq_event_t* events[1] = { parent_patch()->dssi_events_array() };
+ long unsigned events_sizes[1] = { parent_patch()->dssi_events_size() };
+ m_dssi_descriptor->run_multiple_synths(1, m_instances, nframes,
+ events, events_sizes);
+ } else {
+ LADSPAPlugin::run(nframes);
+ }
+ */
+
+ if (m_dssi_descriptor->run_synth) {
+ m_dssi_descriptor->run_synth(m_instances[0], nframes,
+ (snd_seq_event_t*)m_midi_in_port->buffer(0), m_midi_in_port->get_valid_buffer_size());
+ /*} else if (m_dssi_descriptor->run_multiple_synths) { // I hate this stupid function
+ snd_seq_event_t* events[1] = { parent_patch()->dssi_events_array() };
+ long unsigned events_sizes[1] = { parent_patch()->dssi_events_size() };
+ m_dssi_descriptor->run_multiple_synths(1, m_instances, nframes,
+ events, events_sizes);*/
+ } else {
+ LADSPAPlugin::run(nframes);
+ }
+
+
+}
+
+
+void
+DSSIPlugin::send_control(int port_num, float value)
+{
+ string path = m_ui_base_path + "/control";
+ lo_send(m_ui_addr, path.c_str(), "if", port_num, value);
+}
+
+
+void
+DSSIPlugin::send_program(int bank, int value)
+{
+ string path = m_ui_base_path + "/program";
+ lo_send(m_ui_addr, path.c_str(), "ii", bank, value);
+}
+
+
+void
+DSSIPlugin::send_configure(const string& key, const string& val)
+{
+ string path = m_ui_base_path + "/configure";
+ lo_send(m_ui_addr, path.c_str(), "ss", key.c_str(), val.c_str());
+}
+
+
+void
+DSSIPlugin::send_show()
+{
+ string path = m_ui_base_path + "/show";
+ lo_send(m_ui_addr, path.c_str(), NULL);
+}
+
+
+void
+DSSIPlugin::send_hide()
+{
+ string path = m_ui_base_path + "/hide";
+ lo_send(m_ui_addr, path.c_str(), NULL);
+}
+
+
+void
+DSSIPlugin::send_quit()
+{
+ string path = m_ui_base_path + "/quit";
+ lo_send(m_ui_addr, path.c_str(), NULL);
+}
+
+
+void
+DSSIPlugin::send_update()
+{
+ // send "configure"s
+ for (map<string, string>::iterator i = m_configures.begin(); i != m_configures.end(); ++i)
+ send_configure((*i).first, (*i).second);
+
+ // send "program"
+ send_program(m_bank, m_program);
+
+ // send "control"s
+ for (size_t i=0; i < m_ports.size(); ++i)
+ if (m_ports[i]->port_info()->is_control())
+ send_control(m_ports[i]->num(), ((PortBase<sample>*)m_ports[i])->get_value(0, 0));
+
+ // send "show" FIXME: not to spec
+ send_show();
+}
+
+
+} // namespace Om
diff --git a/src/libs/engine/DSSIPlugin.h b/src/libs/engine/DSSIPlugin.h
new file mode 100644
index 00000000..d546a8fe
--- /dev/null
+++ b/src/libs/engine/DSSIPlugin.h
@@ -0,0 +1,109 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef DSSIPLUGIN_H
+#define DSSIPLUGIN_H
+
+#include <asoundlib.h>
+#include <dssi.h>
+#include <lo/lo.h>
+#include "LADSPAPlugin.h"
+
+namespace Om {
+
+class MidiMessage;
+template <typename T> class InputPort;
+namespace Shared {
+ class ClientInterface;
+} using Shared::ClientInterface;
+
+
+/** An instance of a DSSI plugin.
+ */
+class DSSIPlugin : public LADSPAPlugin
+{
+public:
+
+ typedef map<int, string> Bank;
+
+ DSSIPlugin(const string& name, size_t poly, Patch* parent, DSSI_Descriptor* descriptor, samplerate srate, size_t buffer_size);
+ ~DSSIPlugin();
+
+ bool instantiate();
+
+ void activate();
+
+ void set_ui_url(const string& url);
+ void send_update();
+
+ void set_control(size_t port_num, sample val);
+ void configure(const string& key, const string& val);
+ void program(int bank, int program);
+
+ void run(size_t nframes);
+
+ bool update_programs(bool send_events);
+ void set_default_program();
+ const map<int, Bank>& get_programs() const;
+
+ //void send_creation_messages(ClientInterface* client) const;
+
+ const Plugin* plugin() const { return m_plugin; }
+ void plugin(const Plugin* const pi) { m_plugin = pi; }
+
+private:
+ // Prevent copies (undefined)
+ DSSIPlugin(const DSSIPlugin& copy);
+ DSSIPlugin& operator=(const DSSIPlugin& copy);
+
+ bool has_midi_input() const;
+
+ // DSSI GUI messages
+ void send_control(int port_num, float value);
+ void send_program(int bank, int value);
+ void send_configure(const string& key, const string& val);
+ void send_show();
+ void send_hide();
+ void send_quit();
+
+ // Conversion to ALSA MIDI events
+ void convert_events();
+
+
+ DSSI_Descriptor* m_dssi_descriptor;
+
+ string m_ui_url;
+ string m_ui_base_path;
+ lo_address m_ui_addr;
+
+ // Current values
+ int m_bank;
+ int m_program;
+ map<string, string> m_configures;
+ map<int, Bank> m_banks;
+
+ InputPort<MidiMessage>* m_midi_in_port;
+ snd_seq_event_t* m_alsa_events;
+ unsigned long m_encoded_events;
+ snd_midi_event_t* m_alsa_encoder;
+};
+
+
+} // namespace Om
+
+
+#endif // DSSIPLUGIN_H
+
diff --git a/src/libs/engine/DSSIPlugin.h.orig b/src/libs/engine/DSSIPlugin.h.orig
new file mode 100644
index 00000000..816c65a7
--- /dev/null
+++ b/src/libs/engine/DSSIPlugin.h.orig
@@ -0,0 +1,84 @@
+/* This file is part of Om. Copyright (C) 2005 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef DSSIPLUGIN_H
+#define DSSIPLUGIN_H
+
+#include <dssi.h>
+#include <lo/lo.h>
+#include "LADSPAPlugin.h"
+
+namespace Om {
+
+class MidiMessage;
+template <typename T> class InputPort;
+
+
+/** An instance of a DSSI plugin.
+ */
+class DSSIPlugin : public LADSPAPlugin
+{
+public:
+ DSSIPlugin(const string& name, uint poly, Patch* parent, DSSI_Descriptor* descriptor, samplerate srate, size_t buffer_size);
+ ~DSSIPlugin();
+
+ void activate();
+
+ void set_ui_url(const string& url);
+ void send_update();
+
+ void set_control(uint port_num, sample val);
+ void configure(const string& key, const string& val);
+ void program(int bank, int program);
+
+ void run(size_t nframes);
+
+ const Plugin* const plugin() const { return m_plugin; }
+ void plugin(const Plugin* const pi) { m_plugin = pi; }
+
+private:
+ // Prevent copies
+ DSSIPlugin(const DSSIPlugin& copy) : LADSPAPlugin(copy) { exit(EXIT_FAILURE); }
+ DSSIPlugin& operator=(const DSSIPlugin& copy) { exit(EXIT_FAILURE); }
+
+ // DSSI GUI messages
+ void send_control(int port_num, float value);
+ void send_program(int bank, int value);
+ void send_configure(const string& key, const string& val);
+ void send_show();
+ void send_hide();
+ void send_quit();
+
+ string m_ui_url;
+ string m_ui_base_path;
+ lo_address m_ui_addr;
+
+ // Current values
+ int m_bank;
+ int m_program;
+ map<string, string> m_configures;
+
+ DSSI_Descriptor* m_dssi_descriptor;
+
+ InputPort<MidiMessage>* m_midi_in_port;
+};
+
+
+} // namespace Om
+
+
+#endif // DSSIPLUGIN_H
+
diff --git a/src/libs/engine/Driver.h b/src/libs/engine/Driver.h
new file mode 100644
index 00000000..be882d8d
--- /dev/null
+++ b/src/libs/engine/Driver.h
@@ -0,0 +1,112 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef DRIVER_H
+#define DRIVER_H
+
+#include <string>
+using std::string;
+
+namespace Om {
+
+template <typename T> class PortBase;
+
+
+/** Representation of a system (outside Om, ie hardware) audio port.
+ *
+ * This is the class through which the rest of the engine manages everything
+ * related to driver ports. Derived classes are expected to have a pointer to
+ * their driver (to be able to perform the operation necessary).
+ *
+ * \ingroup engine
+ */
+class DriverPort {
+public:
+ virtual ~DriverPort() {}
+
+ /** Add this port to driver at the beginning of a process cycle (realtime safe) */
+ virtual void add_to_driver() = 0;
+
+ /** Remove this port at the beginning of a process cycle (realtime safe) */
+ virtual void remove_from_driver() = 0;
+
+ /** Set the name of the system port */
+ virtual void set_name(const string& name) = 0;
+
+protected:
+ DriverPort() {}
+};
+
+
+/** Driver abstract base class.
+ *
+ * A Driver is, from the perspective of OmObjects (nodes, patches, ports) an
+ * interface for managing system ports. An implementation of Driver basically
+ * needs to manage DriverPorts, and handle writing/reading data to/from them.
+ *
+ * The template parameter T is the type of data this driver manages (ie the
+ * data type of the bridge ports it will handle).
+ *
+ * \ingroup engine
+ */
+template <typename T>
+class Driver
+{
+public:
+ virtual ~Driver() {}
+
+ virtual void activate() = 0;
+ virtual void deactivate() = 0;
+
+ virtual bool is_activated() const = 0;
+
+ /** Create a port ready to be inserted with add_input (non realtime).
+ *
+ * May return NULL if the Driver can not drive the port for some reason.
+ */
+ virtual DriverPort* create_port(PortBase<T>* patch_port) = 0;
+};
+
+
+#if 0
+/** Dummy audio driver.
+ *
+ * Not abstract, all functions are dummies. One of these will be allocated and
+ * "used" if no working AUDIO driver is loaded. (Doing it this way as opposed to
+ * just making Driver have dummy functions makes sure any existing Driver
+ * derived class actually implements the required functions).
+ *
+ * \ingroup engine
+ */
+class DummyDriver : public Driver
+{
+public:
+ ~DummyDriver() {}
+
+ void activate() {}
+ void deactivate() {}
+
+ void enable() {}
+ void disable() {}
+
+ DriverPort* create_port(PortBase<sample>* patch_port) { return NULL; }
+};
+#endif
+
+
+} // namespace Om
+
+#endif // DRIVER_H
diff --git a/src/libs/engine/Event.cpp b/src/libs/engine/Event.cpp
new file mode 100644
index 00000000..7d590e76
--- /dev/null
+++ b/src/libs/engine/Event.cpp
@@ -0,0 +1,48 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "Event.h"
+#include "Responder.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "AudioDriver.h"
+
+namespace Om {
+
+// It'd be nice if this was inlined..
+Event::Event(CountedPtr<Responder> responder)
+: m_responder(responder),
+ m_executed(false)
+{
+ m_time_stamp = om->audio_driver()->time_stamp();
+}
+
+
+/** Construct an event with no responder.
+ *
+ * For internal events only, attempting to access the responder will
+ * cause a NULL pointer dereference.
+ */
+Event::Event()
+: m_responder(NULL),
+ m_executed(false)
+{
+ m_time_stamp = om->audio_driver()->time_stamp();
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/Event.h b/src/libs/engine/Event.h
new file mode 100644
index 00000000..a6d06f83
--- /dev/null
+++ b/src/libs/engine/Event.h
@@ -0,0 +1,71 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef EVENT_H
+#define EVENT_H
+
+#include <cassert>
+#include "util/CountedPtr.h"
+#include "util/types.h"
+#include "MaidObject.h"
+#include "Responder.h"
+
+namespace Om {
+
+
+
+/** Base class for all events (both realtime and QueuedEvent).
+ *
+ * This is for time-critical events like note ons. There is no non-realtime
+ * pre-execute method as in QueuedEvent's, any lookups etc need to be done in the
+ * realtime execute() method.
+ *
+ * QueuedEvent extends this class with a pre_process() method for any work that needs
+ * to be done before processing in the realtime audio thread.
+ *
+ * \ingroup engine
+ */
+class Event : public MaidObject
+{
+public:
+ virtual ~Event() {}
+
+ /** Execute event, MUST be realtime safe */
+ virtual void execute(samplecount offset) { assert(!m_executed); m_executed = true; }
+
+ /** Perform any actions after execution (ie send OSC response to client).
+ * No realtime requirement. */
+ virtual void post_process() {}
+
+ inline samplecount time_stamp() { return m_time_stamp; }
+
+protected:
+ Event(CountedPtr<Responder> responder);
+ Event();
+
+ // Prevent copies
+ Event(const Event&);
+ Event& operator=(const Event&);
+
+ CountedPtr<Responder> m_responder;
+ samplecount m_time_stamp;
+ bool m_executed;
+};
+
+
+} // namespace Om
+
+#endif // EVENT_H
diff --git a/src/libs/engine/EventSource.h b/src/libs/engine/EventSource.h
new file mode 100644
index 00000000..b038f427
--- /dev/null
+++ b/src/libs/engine/EventSource.h
@@ -0,0 +1,52 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef EVENTSOURCE_H
+#define EVENTSOURCE_H
+
+namespace Om {
+
+class Event;
+class QueuedEvent;
+
+
+/** Source for events to run in the audio thread.
+ *
+ * The AudioDriver gets events from an EventSource in the process callback
+ * (realtime audio thread) and executes them, then they are sent to the
+ * PostProcessor and finalised (post-processing thread).
+ */
+class EventSource
+{
+public:
+
+ virtual ~EventSource() {}
+
+ virtual Event* pop_earliest_event_before(const samplecount time) = 0;
+
+ virtual void start() = 0;
+
+ virtual void stop() = 0;
+
+protected:
+ EventSource() {}
+};
+
+
+} // namespace Om
+
+#endif // EVENTSOURCE_H
+
diff --git a/src/libs/engine/InputPort.cpp b/src/libs/engine/InputPort.cpp
new file mode 100644
index 00000000..3837da1b
--- /dev/null
+++ b/src/libs/engine/InputPort.cpp
@@ -0,0 +1,352 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "InputPort.h"
+#include <iostream>
+#include <cstdlib>
+#include <cassert>
+#include "ConnectionBase.h"
+#include "OutputPort.h"
+#include "PortInfo.h"
+#include "Node.h"
+#include "Om.h"
+#include "util.h"
+
+using std::cerr; using std::cout; using std::endl;
+
+
+namespace Om {
+
+
+template <typename T>
+InputPort<T>::InputPort(Node* node, const string& name, size_t index, size_t poly, PortInfo* port_info, size_t buffer_size)
+: PortBase<T>(node, name, index, poly, port_info, buffer_size)
+{
+ assert(port_info->is_input() && !port_info->is_output());
+}
+template InputPort<sample>::InputPort(Node* node, const string& name, size_t index, size_t poly, PortInfo* port_info, size_t buffer_size);
+template InputPort<MidiMessage>::InputPort(Node* node, const string& name, size_t index, size_t poly, PortInfo* port_info, size_t buffer_size);
+
+
+/** Add a connection. Realtime safe.
+ *
+ * The buffer of this port will be set directly to the connection's buffer
+ * if there is only one connection, since no mixing needs to take place.
+ */
+template<typename T>
+void
+InputPort<T>::add_connection(ListNode<ConnectionBase<T>*>* const c)
+{
+ m_connections.push_back(c);
+
+ bool modify_buffers = !m_fixed_buffers;
+ if (modify_buffers && m_is_tied)
+ modify_buffers = !m_tied_port->fixed_buffers();
+
+ if (modify_buffers) {
+ if (m_connections.size() == 1) {
+ // Use buffer directly to avoid copying
+ for (size_t i=0; i < m_poly; ++i) {
+ m_buffers.at(i)->join(c->elem()->buffer(i));
+ if (m_is_tied)
+ m_tied_port->buffer(i)->join(m_buffers.at(i));
+ assert(m_buffers.at(i)->data() == c->elem()->buffer(i)->data());
+ }
+ } else if (m_connections.size() == 2) {
+ // Used to directly use single connection buffer, now there's two
+ // so have to use local ones again and mix down
+ for (size_t i=0; i < m_poly; ++i) {
+ m_buffers.at(i)->unjoin();
+ if (m_is_tied)
+ m_tied_port->buffer(i)->join(m_buffers.at(i));
+ }
+ }
+ update_buffers();
+ }
+
+ assert( ! m_is_tied || m_tied_port != NULL);
+ assert( ! m_is_tied || m_buffers.at(0)->data() == m_tied_port->buffer(0)->data());
+}
+template void InputPort<sample>::add_connection(ListNode<ConnectionBase<sample>*>* const c);
+template void InputPort<MidiMessage>::add_connection(ListNode<ConnectionBase<MidiMessage>*>* const c);
+
+
+/** Remove a connection. Realtime safe.
+ */
+template <typename T>
+ListNode<ConnectionBase<T>*>*
+InputPort<T>::remove_connection(const OutputPort<T>* const src_port)
+{
+ bool modify_buffers = !m_fixed_buffers;
+ if (modify_buffers && m_is_tied)
+ modify_buffers = !m_tied_port->fixed_buffers();
+
+ typedef typename List<ConnectionBase<T>*>::iterator ConnectionBaseListIterator;
+ bool found = false;
+ ListNode<ConnectionBase<T>*>* connection = NULL;
+ for (ConnectionBaseListIterator i = m_connections.begin(); i != m_connections.end(); ++i) {
+ if ((*i)->src_port()->path() == src_port->path()) {
+ connection = m_connections.remove(i);
+ found = true;
+ }
+ }
+
+ if ( ! found) {
+ cerr << "WARNING: [InputPort<T>::remove_connection] Connection not found !" << endl;
+ exit(EXIT_FAILURE);
+ } else {
+ if (m_connections.size() == 0) {
+ for (size_t i=0; i < m_poly; ++i) {
+ // Use a local buffer
+ if (modify_buffers && m_buffers.at(i)->is_joined())
+ m_buffers.at(i)->unjoin();
+ m_buffers.at(i)->clear(); // Write silence
+ if (m_is_tied)
+ m_tied_port->buffer(i)->join(m_buffers.at(i));
+ }
+ } else if (modify_buffers && m_connections.size() == 1) {
+ // Share a buffer
+ for (size_t i=0; i < m_poly; ++i) {
+ m_buffers.at(i)->join((*m_connections.begin())->buffer(i));
+ if (m_is_tied)
+ m_tied_port->buffer(i)->join(m_buffers.at(i));
+ }
+ }
+ }
+
+ if (modify_buffers)
+ update_buffers();
+
+ assert( ! m_is_tied || m_tied_port != NULL);
+ assert( ! m_is_tied || m_buffers.at(0)->data() == m_tied_port->buffer(0)->data());
+
+ return connection;
+}
+template ListNode<ConnectionBase<sample>*>*
+InputPort<sample>::remove_connection(const OutputPort<sample>* const src_port);
+template ListNode<ConnectionBase<MidiMessage>*>*
+InputPort<MidiMessage>::remove_connection(const OutputPort<MidiMessage>* const src_port);
+
+
+/** Update any changed buffers with the plugin this is a port on.
+ *
+ * This calls ie the LADSPA connect_port function when buffers have been changed
+ * due to a connection or disconnection.
+ */
+template <typename T>
+void
+InputPort<T>::update_buffers()
+{
+ for (size_t i=0; i < m_poly; ++i)
+ InputPort<T>::parent_node()->set_port_buffer(i, m_index, m_buffers.at(i)->data());
+}
+template void InputPort<sample>::update_buffers();
+template void InputPort<MidiMessage>::update_buffers();
+
+
+/** Returns whether this port is connected to the passed port.
+ */
+template <typename T>
+bool
+InputPort<T>::is_connected_to(const OutputPort<T>* const port) const
+{
+ typedef typename List<ConnectionBase<T>*>::const_iterator ConnectionBaseListIterator;
+ for (ConnectionBaseListIterator i = m_connections.begin(); i != m_connections.end(); ++i)
+ if ((*i)->src_port() == port)
+ return true;
+
+ return false;
+}
+template bool InputPort<sample>::is_connected_to(const OutputPort<sample>* const port) const;
+template bool InputPort<MidiMessage>::is_connected_to(const OutputPort<MidiMessage>* const port) const;
+
+
+/** "Ties" this port to an OutputPort, so they share the same buffer.
+ *
+ * This is used by OutputNode and InputNode to provide two different ports
+ * (internal and external) that share a buffer.
+ */
+template <typename T>
+void
+InputPort<T>::tie(OutputPort<T>* const port)
+{
+ assert((Port*)port != (Port*)this);
+ assert(port->poly() == this->poly());
+ assert(!m_is_tied);
+ assert(m_tied_port == NULL);
+
+ if (Port::parent_node() != NULL) {
+ assert(m_poly == port->poly());
+
+ for (size_t i=0; i < m_poly; ++i)
+ port->buffer(i)->join(m_buffers.at(i));
+ }
+ m_is_tied = true;
+ m_tied_port = port;
+ port->set_tied_port(this);
+
+ assert(m_buffers.at(0)->data() == port->buffer(0)->data());
+
+ //cerr << "*** Tied " << this->path() << " <-> " << port->path() << endl;
+}
+template void InputPort<sample>::tie(OutputPort<sample>* const port);
+template void InputPort<MidiMessage>::tie(OutputPort<MidiMessage>* const port);
+
+
+/** Prepare buffer for access, mixing if necessary. Realtime safe.
+ * FIXME: nframes parameter not used,
+ */
+template<>
+void
+InputPort<sample>::prepare_buffers(size_t nframes)
+{
+ assert(!m_is_tied || m_tied_port != NULL);
+
+ typedef List<ConnectionBase<sample>*>::iterator ConnectionBaseListIterator;
+ bool do_mixdown = true;
+
+ if (m_connections.size() == 0) return;
+
+ for (ConnectionBaseListIterator c = m_connections.begin(); c != m_connections.end(); ++c)
+ (*c)->prepare_buffers();
+
+ // If only one connection, buffer is (maybe) used directly (no copying)
+ if (m_connections.size() == 1) {
+ // Buffer changed since connection
+ if (m_buffers.at(0)->data() != (*m_connections.begin())->buffer(0)->data()) {
+ if (m_fixed_buffers || (m_is_tied && m_tied_port->fixed_buffers())) {
+ // can't change buffer, must copy
+ do_mixdown = true;
+ } else {
+ // zero-copy
+ assert(m_buffers.at(0)->is_joined());
+ m_buffers.at(0)->join((*m_connections.begin())->buffer(0));
+ do_mixdown = false;
+ }
+ } else {
+ do_mixdown = false;
+ }
+ update_buffers();
+ }
+
+ if (!do_mixdown) {
+ assert(m_buffers.at(0)->data() == (*m_connections.begin())->buffer(0)->data());
+ return;
+ }
+
+ assert(!m_is_tied || m_tied_port != NULL);
+ assert(!m_is_tied || m_buffers.at(0)->data() == m_tied_port->buffer(0)->data());
+
+ for (size_t voice=0; voice < m_poly; ++voice) {
+ m_buffers.at(voice)->copy((*m_connections.begin())->buffer(voice), 0, m_buffer_size-1);
+
+ if (m_connections.size() > 1) {
+ // Copy first connection
+ ConnectionBaseListIterator c = m_connections.begin();
+
+ // Add all other connections
+ for (++c; c != m_connections.end(); ++c)
+ m_buffers.at(voice)->accumulate((*c)->buffer(voice), 0, m_buffer_size-1);
+ }
+ }
+}
+
+
+/** Prepare buffer for access, realtime safe.
+ *
+ * MIDI mixing not yet implemented.
+ */
+template <>
+void
+InputPort<MidiMessage>::prepare_buffers(size_t nframes)
+{
+ assert(!m_is_tied || m_tied_port != NULL);
+
+ const size_t num_ins = m_connections.size();
+ bool do_mixdown = true;
+
+ assert(num_ins == 0 || num_ins == 1);
+
+ typedef List<ConnectionBase<MidiMessage>*>::iterator ConnectionBaseListIterator;
+ assert(m_poly == 1);
+
+ for (ConnectionBaseListIterator c = m_connections.begin(); c != m_connections.end(); ++c)
+ (*c)->prepare_buffers();
+
+
+ // If only one connection, buffer is used directly (no copying)
+ if (num_ins == 1) {
+ // Buffer changed since connection
+ if (m_buffers.at(0) != (*m_connections.begin())->buffer(0)) {
+ if (m_fixed_buffers || (m_is_tied && m_tied_port->fixed_buffers())) {
+ // can't change buffer, must copy
+ do_mixdown = true;
+ } else {
+ // zero-copy
+ assert(m_buffers.at(0)->is_joined());
+ m_buffers.at(0)->join((*m_connections.begin())->buffer(0));
+ if (m_is_tied)
+ m_tied_port->buffer(0)->join(m_buffers.at(0));
+ do_mixdown = false;
+ }
+ update_buffers();
+ } else {
+ do_mixdown = false;
+ }
+ assert(!m_is_tied || m_tied_port != NULL);
+ assert(!m_is_tied || m_buffers.at(0)->data() == m_tied_port->buffer(0)->data());
+ assert(!m_is_tied || m_buffers.at(0)->filled_size() == m_tied_port->buffer(0)->filled_size());
+ assert(do_mixdown || m_buffers.at(0)->filled_size() ==
+ (*m_connections.begin())->src_port()->buffer(0)->filled_size());
+ }
+
+ // Get valid buffer size from inbound connections, unless a port on a top-level
+ // patch (which will be fed by the MidiDriver)
+ if (m_parent->parent() != NULL) {
+ if (num_ins == 1) {
+ m_buffers.at(0)->filled_size(
+ (*m_connections.begin())->src_port()->buffer(0)->filled_size());
+
+ if (m_is_tied)
+ m_tied_port->buffer(0)->filled_size(m_buffers.at(0)->filled_size());
+
+ assert(m_buffers.at(0)->filled_size() ==
+ (*m_connections.begin())->src_port()->buffer(0)->filled_size());
+ } else {
+ // Mixing not implemented
+ m_buffers.at(0)->clear();
+ }
+ }
+
+ assert(!m_is_tied || m_buffers.at(0)->data() == m_tied_port->buffer(0)->data());
+
+ if (!do_mixdown || m_buffers.at(0)->filled_size() == 0 || num_ins == 0)
+ return;
+
+ //cerr << path() << " - Copying MIDI buffer" << endl;
+
+ // Be sure buffers are the same as tied port's, if joined
+ assert(!m_is_tied || m_tied_port != NULL);
+ assert(!m_is_tied || m_buffers.at(0)->data() == m_tied_port->buffer(0)->data());
+
+ if (num_ins > 0)
+ for (size_t i=0; i < m_buffers.at(0)->filled_size(); ++i)
+ m_buffers.at(0)[i] = (*m_connections.begin())->buffer(0)[i];
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/InputPort.h b/src/libs/engine/InputPort.h
new file mode 100644
index 00000000..0e5d9d68
--- /dev/null
+++ b/src/libs/engine/InputPort.h
@@ -0,0 +1,88 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef INPUTPORT_H
+#define INPUTPORT_H
+
+#include <string>
+#include <cstdlib>
+#include <cassert>
+#include "PortBase.h"
+#include "List.h"
+#include "MidiMessage.h"
+using std::string;
+
+namespace Om {
+
+template <typename T> class ConnectionBase;
+template <typename T> class OutputPort;
+class Node;
+
+
+/** An input port on a Node or Patch.
+ *
+ * All ports have a Buffer, but the actual contents (data) of that buffer may be
+ * set directly to the incoming connection's buffer if there's only one inbound
+ * connection, to eliminate the need to copy/mix.
+ *
+ * If a port has multiple connections, they will be mixed down into the local
+ * buffer and it will be used.
+ *
+ * \ingroup engine
+ */
+template <typename T>
+class InputPort : public PortBase<T>
+{
+public:
+ InputPort(Node* node, const string& name, size_t index, size_t poly, PortInfo* port_info, size_t buffer_size);
+ virtual ~InputPort() {}
+
+ void add_connection(ListNode<ConnectionBase<T>*>* const c);
+ ListNode<ConnectionBase<T>*>* remove_connection(const OutputPort<T>* const src_port);
+
+ void prepare_buffers(size_t nframes);
+
+ void tie(OutputPort<T>* const port);
+
+ bool is_connected() const { return (m_connections.size() > 0); }
+ bool is_connected_to(const OutputPort<T>* const port) const;
+
+private:
+ // Prevent copies (Undefined)
+ InputPort<T>(const InputPort<T>& copy);
+ InputPort<T>& operator=(const InputPort<T>&);
+
+ void update_buffers();
+
+ List<ConnectionBase<T>*> m_connections;
+
+ // This is just stupid...
+ using PortBase<T>::m_is_tied;
+ using PortBase<T>::m_tied_port;
+ using PortBase<T>::m_buffers;
+ using PortBase<T>::m_poly;
+ using PortBase<T>::m_index;
+ using PortBase<T>::m_buffer_size;
+ using PortBase<T>::m_fixed_buffers;
+};
+
+
+template class InputPort<sample>;
+template class InputPort<MidiMessage>;
+
+} // namespace Om
+
+#endif // INPUTPORT_H
diff --git a/src/libs/engine/InternalNode.h b/src/libs/engine/InternalNode.h
new file mode 100644
index 00000000..fea2d3ad
--- /dev/null
+++ b/src/libs/engine/InternalNode.h
@@ -0,0 +1,70 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef INTERNALNODE_H
+#define INTERNALNODE_H
+
+#include <cstdlib>
+#include "NodeBase.h"
+#include "Plugin.h"
+
+namespace Om {
+
+class Patch;
+
+
+/** Base class for Internal (builtin) nodes
+ *
+ * \ingroup engine
+ */
+class InternalNode : public NodeBase
+{
+public:
+ InternalNode(const string& path, size_t poly, Patch* parent, samplerate srate, size_t buffer_size)
+ : NodeBase(path, poly, parent, srate, buffer_size),
+ m_is_added(false)
+ {
+ m_plugin.lib_path("/Om");
+ }
+
+ virtual ~InternalNode() {}
+
+ virtual void deactivate() { if (m_is_added) remove_from_patch(); NodeBase::deactivate(); }
+
+ virtual void run(size_t nframes) { NodeBase::run(nframes); }
+
+ virtual void add_to_patch() { assert(!m_is_added); m_is_added = true; }
+ virtual void remove_from_patch() { assert(m_is_added); m_is_added = false; }
+
+ //virtual void send_creation_messages(ClientInterface* client) const
+ //{ NodeBase::send_creation_messages(client); }
+
+ virtual const Plugin* plugin() const { return &m_plugin; }
+ virtual void plugin(const Plugin* const) { exit(EXIT_FAILURE); }
+
+protected:
+ // Disallow copies (undefined)
+ InternalNode(const InternalNode&);
+ InternalNode& operator=(const InternalNode&);
+
+ Plugin m_plugin;
+ bool m_is_added;
+};
+
+
+} // namespace Om
+
+#endif // INTERNALNODE_H
diff --git a/src/libs/engine/JackAudioDriver.cpp b/src/libs/engine/JackAudioDriver.cpp
new file mode 100644
index 00000000..0f5e3b1a
--- /dev/null
+++ b/src/libs/engine/JackAudioDriver.cpp
@@ -0,0 +1,373 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "JackAudioDriver.h"
+#include "config.h"
+#include "tuning.h"
+#include <iostream>
+#include <cstdlib>
+#include "Om.h"
+#include "OmApp.h"
+#include "util.h"
+#include "Event.h"
+#include "QueuedEvent.h"
+#include "EventSource.h"
+#include "PostProcessor.h"
+#include "util/Queue.h"
+#include "Node.h"
+#include "Patch.h"
+#include "Port.h"
+#include "PortInfo.h"
+#include "MidiDriver.h"
+#include "List.h"
+#include "PortBase.h"
+#ifdef HAVE_LASH
+#include "LashDriver.h"
+#endif
+
+using std::cout; using std::cerr; using std::endl;
+
+
+namespace Om {
+
+
+//// JackAudioPort ////
+
+JackAudioPort::JackAudioPort(JackAudioDriver* driver, PortBase<sample>* patch_port)
+: DriverPort(),
+ ListNode<JackAudioPort*>(this),
+ m_driver(driver),
+ m_jack_port(NULL),
+ m_jack_buffer(NULL),
+ m_patch_port(patch_port)
+{
+ assert(patch_port->tied_port() != NULL);
+ assert(patch_port->poly() == 1);
+
+ m_jack_port = jack_port_register(m_driver->jack_client(),
+ patch_port->path().c_str(), JACK_DEFAULT_AUDIO_TYPE,
+ (patch_port->port_info()->is_input()) ? JackPortIsInput : JackPortIsOutput,
+ 0);
+
+ m_jack_buffer = new DriverBuffer<jack_sample_t>(driver->buffer_size());
+
+ patch_port->fixed_buffers(true);
+}
+
+
+JackAudioPort::~JackAudioPort()
+{
+ jack_port_unregister(m_driver->jack_client(), m_jack_port);
+}
+
+
+void
+JackAudioPort::add_to_driver()
+{
+ m_driver->add_port(this);
+}
+
+
+void
+JackAudioPort::remove_from_driver()
+{
+ m_driver->remove_port(this);
+}
+
+void
+JackAudioPort::prepare_buffer(jack_nframes_t nframes)
+{
+ // Technically this doesn't need to be done every time for output ports
+ m_jack_buffer->set_data((jack_default_audio_sample_t*)
+ jack_port_get_buffer(m_jack_port, nframes));
+
+ assert(m_patch_port->tied_port() != NULL);
+
+ // FIXME: fixed_buffers switch on/off thing can be removed once this shit
+ // gets figured out and assertions can go away
+ m_patch_port->fixed_buffers(false);
+ m_patch_port->buffer(0)->join(m_jack_buffer);
+ m_patch_port->tied_port()->buffer(0)->join(m_jack_buffer);
+
+ m_patch_port->fixed_buffers(true);
+
+ assert(m_patch_port->buffer(0)->data() == m_patch_port->tied_port()->buffer(0)->data());
+ assert(m_patch_port->buffer(0)->data() == m_jack_buffer->data());
+}
+
+
+//// JackAudioDriver ////
+
+JackAudioDriver::JackAudioDriver()
+: m_client(NULL),
+ m_buffer_size(0),
+ m_sample_rate(0),
+ m_is_activated(false),
+ m_local_client(true),
+ m_root_patch(NULL),
+ m_start_of_current_cycle(0),
+ m_start_of_last_cycle(0)
+{
+ m_client = jack_client_new("Om");
+ if (m_client == NULL) {
+ cerr << "[JackAudioDriver] Unable to connect to Jack. Exiting." << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ jack_on_shutdown(m_client, shutdown_cb, this);
+
+ m_buffer_size = jack_get_buffer_size(m_client);
+ m_sample_rate = jack_get_sample_rate(m_client);
+
+ jack_set_sample_rate_callback(m_client, sample_rate_cb, this);
+ jack_set_buffer_size_callback(m_client, buffer_size_cb, this);
+}
+
+JackAudioDriver::JackAudioDriver(jack_client_t* jack_client)
+: m_client(jack_client),
+ m_buffer_size(jack_get_buffer_size(jack_client)),
+ m_sample_rate(jack_get_sample_rate(jack_client)),
+ m_is_activated(false),
+ m_local_client(false),
+ m_start_of_current_cycle(0),
+ m_start_of_last_cycle(0)
+{
+ jack_on_shutdown(m_client, shutdown_cb, this);
+
+ jack_set_sample_rate_callback(m_client, sample_rate_cb, this);
+ jack_set_buffer_size_callback(m_client, buffer_size_cb, this);
+}
+
+
+JackAudioDriver::~JackAudioDriver()
+{
+ deactivate();
+
+ if (m_local_client)
+ jack_client_close(m_client);
+}
+
+
+void
+JackAudioDriver::activate()
+{
+ if (m_is_activated) {
+ cerr << "[JackAudioDriver] Jack driver already activated." << endl;
+ return;
+ }
+
+ jack_set_process_callback(m_client, process_cb, this);
+
+ m_is_activated = true;
+
+ if (jack_activate(m_client)) {
+ cerr << "[JackAudioDriver] Could not activate Jack client, aborting." << endl;
+ exit(EXIT_FAILURE);
+ } else {
+ cout << "[JackAudioDriver] Activated Jack client." << endl;
+#ifdef HAVE_LASH
+ lash_driver->set_jack_client_name("Om"); // FIXME: unique name
+#endif
+ }
+}
+
+
+void
+JackAudioDriver::deactivate()
+{
+ if (m_is_activated) {
+ // FIXME
+ reinterpret_cast<EventSource*>(om->osc_receiver())->stop();
+
+ jack_deactivate(m_client);
+ m_is_activated = false;
+
+ for (List<JackAudioPort*>::iterator i = m_ports.begin(); i != m_ports.end(); ++i)
+ jack_port_unregister(m_client, (*i)->jack_port());
+
+ m_ports.clear();
+
+ cout << "[JackAudioDriver] Deactivated Jack client." << endl;
+
+ om->post_processor()->stop();
+ }
+}
+
+
+/** Add a Jack port.
+ *
+ * Realtime safe, this is to be called at the beginning of a process cycle to
+ * insert (and actually begin using) a new port.
+ *
+ * See create_port() and remove_port().
+ */
+void
+JackAudioDriver::add_port(JackAudioPort* port)
+{
+ m_ports.push_back(port);
+}
+
+
+/** Remove a Jack port.
+ *
+ * Realtime safe. This is to be called at the beginning of a process cycle to
+ * remove the port from the lists read by the audio thread, so the port
+ * will no longer be used and can be removed afterwards.
+ *
+ * It is the callers responsibility to delete the returned port.
+ */
+JackAudioPort*
+JackAudioDriver::remove_port(JackAudioPort* port)
+{
+ for (List<JackAudioPort*>::iterator i = m_ports.begin(); i != m_ports.end(); ++i)
+ if ((*i) == port)
+ return m_ports.remove(i)->elem();
+
+ cerr << "[JackAudioDriver::remove_port] WARNING: Failed to find Jack port to remove!" << endl;
+ return NULL;
+}
+
+
+DriverPort*
+JackAudioDriver::create_port(PortBase<sample>* patch_port)
+{
+ if (patch_port->buffer_size() == m_buffer_size)
+ return new JackAudioPort(this, patch_port);
+ else
+ return NULL;
+}
+
+
+/** Process all the pending events for this cycle.
+ *
+ * Called from the realtime thread once every process cycle.
+ */
+void
+JackAudioDriver::process_events(jack_nframes_t block_start, jack_nframes_t block_end)
+{
+ Event* ev = NULL;
+
+ /* Limit the maximum number of queued events to process per cycle. This
+ * makes the process callback truly realtime-safe by preventing being
+ * choked by events coming in faster than they can be processed.
+ * FIXME: run the math on this and figure out a good value */
+ const int MAX_SLOW_EVENTS = m_buffer_size / 100;
+
+ int num_events_processed = 0;
+ int offset = 0;
+
+ // Process the "slow" events first, because it's possible some of the
+ // RT events depend on them
+
+ // FIXME
+ while ((ev = reinterpret_cast<EventSource*>(om->osc_receiver())
+ ->pop_earliest_event_before(block_end)) != NULL) {
+ ev->execute(0); // QueuedEvents are not sample accurate
+ om->post_processor()->push(ev);
+ if (++num_events_processed > MAX_SLOW_EVENTS)
+ break;
+ }
+
+ while (!om->event_queue()->is_empty()
+ && om->event_queue()->front()->time_stamp() < block_end) {
+ ev = om->event_queue()->pop();
+ offset = ev->time_stamp() - block_start;
+ if (offset < 0) offset = 0; // this can happen if we miss a cycle
+ ev->execute(offset);
+ om->post_processor()->push(ev);
+ ++num_events_processed;
+ }
+
+ if (num_events_processed > 0)
+ om->post_processor()->signal();
+}
+
+
+
+/**** Jack Callbacks ****/
+
+
+
+/** Jack process callback, drives entire audio thread.
+ *
+ * \callgraph
+ */
+int
+JackAudioDriver::m_process_cb(jack_nframes_t nframes)
+{
+ // FIXME: support nframes != buffer_size, even though that never damn well happens
+ assert(nframes == m_buffer_size);
+
+ // Note that jack can elect to not call this function for a cycle, if things aren't
+ // keeping up
+ m_start_of_current_cycle = jack_last_frame_time(m_client);
+ m_start_of_last_cycle = m_start_of_current_cycle - nframes;
+
+ assert(m_start_of_current_cycle - m_start_of_last_cycle == nframes);
+
+ m_transport_state = jack_transport_query(m_client, &m_position);
+
+ process_events(m_start_of_last_cycle, m_start_of_current_cycle);
+ om->midi_driver()->prepare_block(m_start_of_last_cycle, m_start_of_current_cycle);
+
+ // Set buffers of patch ports to Jack port buffers (zero-copy processing)
+ for (List<JackAudioPort*>::iterator i = m_ports.begin(); i != m_ports.end(); ++i)
+ (*i)->prepare_buffer(nframes);
+
+ // Run root patch
+ assert(m_root_patch != NULL);
+ m_root_patch->run(nframes);
+
+ return 0;
+}
+
+
+void
+JackAudioDriver::m_shutdown_cb()
+{
+ cout << "[JackAudioDriver] Jack shutdown. Exiting." << endl;
+ om->quit();
+}
+
+
+int
+JackAudioDriver::m_sample_rate_cb(jack_nframes_t nframes)
+{
+ if (m_is_activated) {
+ cerr << "[JackAudioDriver] Om does not support changing sample rate on the fly (yet). Aborting." << endl;
+ exit(EXIT_FAILURE);
+ } else {
+ m_sample_rate = nframes;
+ }
+ return 0;
+}
+
+
+int
+JackAudioDriver::m_buffer_size_cb(jack_nframes_t nframes)
+{
+ if (m_is_activated) {
+ cerr << "[JackAudioDriver] Om does not support chanding buffer size on the fly (yet). Aborting." << endl;
+ exit(EXIT_FAILURE);
+ } else {
+ m_buffer_size = nframes;
+ }
+ return 0;
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/JackAudioDriver.h b/src/libs/engine/JackAudioDriver.h
new file mode 100644
index 00000000..c55d392f
--- /dev/null
+++ b/src/libs/engine/JackAudioDriver.h
@@ -0,0 +1,176 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef JACKAUDIODRIVER_H
+#define JACKAUDIODRIVER_H
+
+#include <jack/jack.h>
+#include <jack/transport.h>
+#include "List.h"
+#include "AudioDriver.h"
+#include "Buffer.h"
+
+namespace Om {
+
+class Patch;
+class Port;
+template <typename T> class PortBase;
+class JackAudioDriver;
+typedef jack_default_audio_sample_t jack_sample_t;
+
+
+/** Used internally by JackAudioDriver to represent a Jack port.
+ *
+ * A Jack port always has a one-to-one association with a Patch port.
+ */
+class JackAudioPort : public DriverPort, public ListNode<JackAudioPort*>
+{
+public:
+ JackAudioPort(JackAudioDriver* driver, PortBase<sample>* patch_port);
+ ~JackAudioPort();
+
+ void add_to_driver();
+ void remove_from_driver();
+ void set_name(const string& name) { jack_port_set_name(m_jack_port, name.c_str()); };
+
+ void prepare_buffer(jack_nframes_t nframes);
+
+ jack_port_t* jack_port() const { return m_jack_port; }
+ DriverBuffer<sample>* buffer() const { return m_jack_buffer; }
+ void jack_buffer(jack_sample_t* s) { m_jack_buffer->set_data(s); }
+ PortBase<sample>* patch_port() const { return m_patch_port; }
+
+private:
+ // Prevent copies (undefined)
+ JackAudioPort(const JackAudioPort&);
+ JackAudioPort& operator=(const JackAudioPort&);
+
+ JackAudioDriver* m_driver;
+ jack_port_t* m_jack_port;
+ DriverBuffer<sample>* m_jack_buffer;
+ PortBase<sample>* m_patch_port;
+};
+
+
+
+/** The Jack AudioDriver.
+ *
+ * The process callback here drives the entire audio thread by "pulling"
+ * events from queues, processing them, running the patches, and passing
+ * events along to the PostProcessor.
+ *
+ * \ingroup engine
+ */
+class JackAudioDriver : public AudioDriver
+{
+public:
+ JackAudioDriver();
+ JackAudioDriver(jack_client_t *jack_client);
+ ~JackAudioDriver();
+
+ void activate();
+ void deactivate();
+ void enable();
+ void disable();
+
+ void process_events(jack_nframes_t block_start, jack_nframes_t block_end);
+
+ DriverPort* create_port(PortBase<sample>* patch_port);
+
+ Patch* root_patch() { return m_root_patch; }
+ void set_root_patch(Patch* patch) { m_root_patch = patch; }
+
+ /** Transport state for this frame.
+ * Intended to only be called from the audio thread. */
+ inline const jack_position_t* position() { return &m_position; }
+ inline const jack_transport_state_t transport_state() { return m_transport_state; }
+
+ bool is_realtime() { return jack_is_realtime(m_client); }
+
+ jack_client_t* jack_client() const { return m_client; }
+ samplecount buffer_size() const { return m_buffer_size; }
+ samplecount sample_rate() const { return m_sample_rate; }
+ bool is_activated() const { return m_is_activated; }
+
+ samplecount time_stamp() const { return jack_frame_time(m_client); }
+
+private:
+ // Prevent copies (undefined)
+ JackAudioDriver(const JackAudioDriver&);
+ JackAudioDriver& operator=(const JackAudioDriver&);
+
+ friend class JackAudioPort;
+
+ // Functions for JackAudioPort
+ void add_port(JackAudioPort* port);
+ JackAudioPort* remove_port(JackAudioPort* port);
+
+ // These are the static versions of the callbacks, they call
+ // the non-static ones below
+ inline static int process_cb(jack_nframes_t nframes, void* const jack_driver);
+ inline static void shutdown_cb(void* const jack_driver);
+ inline static int buffer_size_cb(jack_nframes_t nframes, void* const jack_driver);
+ inline static int sample_rate_cb(jack_nframes_t nframes, void* const jack_driver);
+
+ // Non static callbacks
+ int m_process_cb(jack_nframes_t nframes);
+ void m_shutdown_cb();
+ int m_buffer_size_cb(jack_nframes_t nframes);
+ int m_sample_rate_cb(jack_nframes_t nframes);
+
+ jack_client_t* m_client;
+ jack_nframes_t m_buffer_size;
+ jack_nframes_t m_sample_rate;
+ bool m_is_activated;
+ bool m_local_client; ///< Whether m_client should be closed on destruction
+ jack_position_t m_position;
+ jack_transport_state_t m_transport_state;
+
+ List<JackAudioPort*> m_ports;
+
+ Patch* m_root_patch;
+
+ jack_nframes_t m_start_of_current_cycle;
+ jack_nframes_t m_start_of_last_cycle;
+};
+
+
+inline int JackAudioDriver::process_cb(jack_nframes_t nframes, void* jack_driver)
+{
+ return ((JackAudioDriver*)jack_driver)->m_process_cb(nframes);
+}
+
+inline void JackAudioDriver::shutdown_cb(void* jack_driver)
+{
+ return ((JackAudioDriver*)jack_driver)->m_shutdown_cb();
+}
+
+
+inline int JackAudioDriver::buffer_size_cb(jack_nframes_t nframes, void* jack_driver)
+{
+ return ((JackAudioDriver*)jack_driver)->m_buffer_size_cb(nframes);
+}
+
+
+inline int JackAudioDriver::sample_rate_cb(jack_nframes_t nframes, void* jack_driver)
+{
+ return ((JackAudioDriver*)jack_driver)->m_sample_rate_cb(nframes);
+}
+
+
+} // namespace Om
+
+#endif // JACKAUDIODRIVER_H
diff --git a/src/libs/engine/JackMidiDriver.cpp b/src/libs/engine/JackMidiDriver.cpp
new file mode 100644
index 00000000..40054b8a
--- /dev/null
+++ b/src/libs/engine/JackMidiDriver.cpp
@@ -0,0 +1,217 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "JackMidiDriver.h"
+#include <iostream>
+#include <cstdlib>
+#include <pthread.h>
+#include "Om.h"
+#include "OmApp.h"
+#include "util/types.h"
+#include "midi.h"
+#include "OmApp.h"
+#include "Maid.h"
+#include "AudioDriver.h"
+#include "MidiInputNode.h"
+#include "PortInfo.h"
+#include "MidiMessage.h"
+#include "PortBase.h"
+#ifdef HAVE_LASH
+#include "LashDriver.h"
+#endif
+using std::cout; using std::cerr; using std::endl;
+
+namespace Om {
+
+
+//// JackMidiPort ////
+
+JackMidiPort::JackMidiPort(JackMidiDriver* driver, PortBase<MidiMessage>* patch_port)
+: DriverPort(),
+ ListNode<JackMidiPort*>(this),
+ m_driver(driver),
+ m_jack_port(NULL),
+ m_patch_port(patch_port)
+{
+ assert(patch_port->poly() == 1);
+
+ m_jack_port = jack_port_register(m_driver->jack_client(),
+ patch_port->path().c_str(), JACK_DEFAULT_MIDI_TYPE,
+ (patch_port->port_info()->is_input()) ? JackPortIsInput : JackPortIsOutput,
+ 0);
+
+ patch_port->buffer(0)->clear();
+ patch_port->fixed_buffers(true);
+}
+
+
+JackMidiPort::~JackMidiPort()
+{
+ jack_port_unregister(m_driver->jack_client(), m_jack_port);
+}
+
+
+void
+JackMidiPort::add_to_driver()
+{
+ m_driver->add_port(this);
+}
+
+
+void
+JackMidiPort::remove_from_driver()
+{
+ m_driver->remove_port(this);
+}
+
+
+/** Prepare events for a block.
+ *
+ * This is basically trivial (as opposed to AlsaMidiPort) since Jack MIDI
+ * data is in-band with the audio thread.
+ *
+ * Prepares all events that occurred during the time interval passed
+ * (which ideally are the events from the previous cycle with an exact
+ * 1 cycle delay).
+ */
+void
+JackMidiPort::prepare_block(const samplecount block_start, const samplecount block_end)
+{
+ assert(block_end >= block_start);
+
+ const samplecount nframes = block_end - block_start;
+ void* jack_buffer = jack_port_get_buffer(m_jack_port, nframes);
+ const jack_nframes_t event_count = jack_midi_port_get_info(jack_buffer, nframes)->event_count;
+
+ assert(event_count < m_patch_port->buffer_size());
+
+ // Copy events from Jack port buffer into patch port buffer
+ for (jack_nframes_t i=0; i < event_count; ++i) {
+ jack_midi_event_t* ev = (jack_midi_event_t*)&m_patch_port->buffer(0)->value_at(i);
+ jack_midi_event_get(ev, jack_buffer, i, nframes);
+
+ // Convert note ons with velocity 0 to proper note offs
+ if (ev->buffer[0] == MIDI_CMD_NOTE_ON && ev->buffer[2] == 0)
+ ev->buffer[0] = MIDI_CMD_NOTE_OFF;
+
+ // MidiMessage and jack_midi_event_t* are the same thing :/
+ MidiMessage* const message = &m_patch_port->buffer(0)->data()[i];
+ message->time = ev->time;
+ message->size = ev->size;
+ message->buffer = ev->buffer;
+ }
+
+ //cerr << "Jack MIDI got " << event_count << " events." << endl;
+
+ m_patch_port->buffer(0)->filled_size(event_count);
+ m_patch_port->tied_port()->buffer(0)->filled_size(event_count);
+}
+
+
+
+//// JackMidiDriver ////
+
+
+bool JackMidiDriver::m_midi_thread_exit_flag = true;
+
+
+JackMidiDriver::JackMidiDriver(jack_client_t* client)
+: m_client(client),
+ m_is_activated(false),
+ m_is_enabled(false)
+{
+}
+
+
+JackMidiDriver::~JackMidiDriver()
+{
+ deactivate();
+}
+
+
+/** Launch and start the MIDI thread.
+ */
+void
+JackMidiDriver::activate()
+{
+ m_is_activated = true;
+}
+
+
+/** Terminate the MIDI thread.
+ */
+void
+JackMidiDriver::deactivate()
+{
+ m_is_activated = false;
+}
+
+
+/** Build flat arrays of events for DSSI plugins for each Port.
+ */
+void
+JackMidiDriver::prepare_block(const samplecount block_start, const samplecount block_end)
+{
+ for (List<JackMidiPort*>::iterator i = m_in_ports.begin(); i != m_in_ports.end(); ++i)
+ (*i)->prepare_block(block_start, block_end);
+}
+
+
+/** Add an Jack MIDI port.
+ *
+ * Realtime safe, this is to be called at the beginning of a process cycle to
+ * insert (and actually begin using) a new port.
+ *
+ * See create_port() and remove_port().
+ */
+void
+JackMidiDriver::add_port(JackMidiPort* port)
+{
+ if (port->patch_port()->port_info()->is_input())
+ m_in_ports.push_back(port);
+ else
+ m_out_ports.push_back(port);
+}
+
+
+/** Remove an Jack MIDI port.
+ *
+ * Realtime safe. This is to be called at the beginning of a process cycle to
+ * remove the port from the lists read by the audio thread, so the port
+ * will no longer be used and can be removed afterwards.
+ *
+ * It is the callers responsibility to delete the returned port.
+ */
+JackMidiPort*
+JackMidiDriver::remove_port(JackMidiPort* port)
+{
+ if (port->patch_port()->port_info()->is_input()) {
+ for (List<JackMidiPort*>::iterator i = m_in_ports.begin(); i != m_in_ports.end(); ++i)
+ if ((*i) == (JackMidiPort*)port)
+ return m_in_ports.remove(i)->elem();
+ } else {
+ for (List<JackMidiPort*>::iterator i = m_out_ports.begin(); i != m_out_ports.end(); ++i)
+ if ((*i) == port)
+ return m_out_ports.remove(i)->elem();
+ }
+
+ cerr << "[JackMidiDriver::remove_input] WARNING: Failed to find Jack port to remove!" << endl;
+ return NULL;
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/JackMidiDriver.h b/src/libs/engine/JackMidiDriver.h
new file mode 100644
index 00000000..950d382f
--- /dev/null
+++ b/src/libs/engine/JackMidiDriver.h
@@ -0,0 +1,123 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef JACKMIDIDRIVER_H
+#define JACKMIDIDRIVER_H
+
+#include <jack/jack.h>
+#include <jack/midiport.h>
+#include "config.h"
+#include "List.h"
+#include "util/Queue.h"
+#include "MidiDriver.h"
+
+namespace Om {
+
+class Node;
+class SetPortValueEvent;
+class JackMidiDriver;
+template <typename T> class PortBase;
+
+
+/** Representation of an JACK MIDI port.
+ *
+ * \ingroup engine
+ */
+class JackMidiPort : public DriverPort, public ListNode<JackMidiPort*>
+{
+public:
+ JackMidiPort(JackMidiDriver* driver, PortBase<MidiMessage>* port);
+ virtual ~JackMidiPort();
+
+ void prepare_block(const samplecount block_start, const samplecount block_end);
+
+ void add_to_driver();
+ void remove_from_driver();
+ void set_name(const string& name) { jack_port_set_name(m_jack_port, name.c_str()); };
+
+ PortBase<MidiMessage>* patch_port() const { return m_patch_port; }
+
+private:
+ // Prevent copies (undefined)
+ JackMidiPort(const JackMidiPort&);
+ JackMidiPort& operator=(const JackMidiPort&);
+
+ JackMidiDriver* m_driver;
+ jack_port_t* m_jack_port;
+ PortBase<MidiMessage>* m_patch_port;
+};
+
+
+/** Jack MIDI driver.
+ *
+ * This driver reads Jack MIDI events and dispatches them to the appropriate
+ * JackMidiPort for processing.
+ *
+ * \ingroup engine
+ */
+class JackMidiDriver : public MidiDriver
+{
+public:
+ JackMidiDriver(jack_client_t* client);
+ ~JackMidiDriver();
+
+ void activate();
+ void deactivate();
+ void enable() { m_is_enabled = true; }
+ void disable() { m_is_enabled = false; }
+
+ bool is_activated() const { return m_is_activated; }
+ bool is_enabled() const { return m_is_enabled; }
+
+ void prepare_block(const samplecount block_start, const samplecount block_end);
+
+ JackMidiPort* create_port(PortBase<MidiMessage>* patch_port)
+ { return new JackMidiPort(this, patch_port); }
+
+ jack_client_t* jack_client() { return m_client; }
+
+private:
+ // Prevent copies (undefined)
+ JackMidiDriver(const JackMidiDriver&);
+ JackMidiDriver& operator=(const JackMidiDriver&);
+
+ List<JackMidiPort*> m_in_ports;
+ List<JackMidiPort*> m_out_ports;
+
+ friend class JackMidiPort;
+
+ // Functions for JackMidiPort
+ void add_port(JackMidiPort* port);
+ JackMidiPort* remove_port(JackMidiPort* port);
+
+ void add_output(ListNode<JackMidiPort*>* port);
+ ListNode<JackMidiPort*>* remove_output(JackMidiPort* port);
+
+ // MIDI thread
+ static void* process_midi_in(void* me);
+
+ jack_client_t* m_client;
+ pthread_t m_process_thread;
+ bool m_is_activated;
+ bool m_is_enabled;
+ static bool m_midi_thread_exit_flag;
+};
+
+
+} // namespace Om
+
+
+#endif // JACKMIDIDRIVER_H
diff --git a/src/libs/engine/LADSPAPlugin.cpp b/src/libs/engine/LADSPAPlugin.cpp
new file mode 100644
index 00000000..ddeb0be4
--- /dev/null
+++ b/src/libs/engine/LADSPAPlugin.cpp
@@ -0,0 +1,274 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "LADSPAPlugin.h"
+#include <iostream>
+#include <cassert>
+#include "float.h"
+#include <stdint.h>
+#include <cmath>
+#include "InputPort.h"
+#include "OutputPort.h"
+#include "PortInfo.h"
+#include "Plugin.h"
+
+namespace Om {
+
+
+/** Partially construct a LADSPAPlugin.
+ *
+ * Object is not usable until instantiate() is called with success.
+ * (It _will_ crash!)
+ */
+LADSPAPlugin::LADSPAPlugin(const string& path, size_t poly, Patch* parent, const LADSPA_Descriptor* descriptor, samplerate srate, size_t buffer_size)
+: NodeBase(path, poly, parent, srate, buffer_size),
+ m_descriptor(descriptor),
+ m_instances(NULL)
+{
+ assert(m_descriptor != NULL);
+
+ // Note that this may be changed by an overriding DSSIPlugin
+ // ie do not assume m_ports is all LADSPA plugin ports
+ m_num_ports = m_descriptor->PortCount;
+}
+
+
+/** Instantiate self from LADSPA plugin descriptor.
+ *
+ * Implemented as a seperate function (rather than in the constructor) to
+ * allow graceful error-catching of broken plugins.
+ *
+ * Returns whether or not plugin was successfully instantiated. If return
+ * value is false, this object may not be used.
+ */
+bool
+LADSPAPlugin::instantiate()
+{
+ m_ports.alloc(m_num_ports);
+
+ m_instances = new LADSPA_Handle[m_poly];
+
+ size_t port_buffer_size = 0;
+
+ for (size_t i=0; i < m_poly; ++i) {
+ m_instances[i] = m_descriptor->instantiate(m_descriptor, m_srate);
+ if (m_instances[i] == NULL) {
+ cerr << "Failed to instantiate plugin!" << endl;
+ return false;
+ }
+ }
+
+ string port_name;
+ string port_path;
+
+ Port* port = NULL;
+
+ for (size_t j=0; j < m_descriptor->PortCount; ++j) {
+ port_name = m_descriptor->PortNames[j];
+ string::size_type slash_index;
+
+ // Name mangling, to guarantee port names are unique
+ for (size_t k=0; k < m_descriptor->PortCount; ++k) {
+ assert(m_descriptor->PortNames[k] != NULL);
+ if (k != j && port_name == m_descriptor->PortNames[k]) { // clash
+ if (LADSPA_IS_PORT_CONTROL(m_descriptor->PortDescriptors[j]))
+ port_name += " (CR)";
+ else
+ port_name += " (AR)";
+ }
+ // Replace all slashes with "-" (so they don't screw up paths)
+ while ((slash_index = port_name.find("/")) != string::npos)
+ port_name[slash_index] = '-';
+ }
+
+ port_path = path() + "/" + port_name;
+
+ if (LADSPA_IS_PORT_CONTROL(m_descriptor->PortDescriptors[j]))
+ port_buffer_size = 1;
+ else if (LADSPA_IS_PORT_AUDIO(m_descriptor->PortDescriptors[j]))
+ port_buffer_size = m_buffer_size;
+
+ assert (LADSPA_IS_PORT_INPUT(m_descriptor->PortDescriptors[j])
+ || LADSPA_IS_PORT_OUTPUT(m_descriptor->PortDescriptors[j]));
+
+ if (LADSPA_IS_PORT_INPUT(m_descriptor->PortDescriptors[j])) {
+ port = new InputPort<sample>(this, port_name, j, m_poly,
+ new PortInfo(port_path, m_descriptor->PortDescriptors[j],
+ m_descriptor->PortRangeHints[j].HintDescriptor), port_buffer_size);
+ m_ports.at(j) = port;
+ } else if (LADSPA_IS_PORT_OUTPUT(m_descriptor->PortDescriptors[j])) {
+ port = new OutputPort<sample>(this, port_name, j, m_poly,
+ new PortInfo(port_path, m_descriptor->PortDescriptors[j],
+ m_descriptor->PortRangeHints[j].HintDescriptor), port_buffer_size);
+ m_ports.at(j) = port;
+ }
+
+ assert(m_ports.at(j) != NULL);
+
+ PortInfo* pi = port->port_info();
+ get_port_vals(j, pi);
+
+ // Set default control val
+ if (pi->is_control())
+ ((PortBase<sample>*)port)->set_value(pi->default_val(), 0);
+ else
+ ((PortBase<sample>*)port)->set_value(0.0f, 0);
+ }
+
+ return true;
+}
+
+
+LADSPAPlugin::~LADSPAPlugin()
+{
+ for (size_t i=0; i < m_poly; ++i)
+ m_descriptor->cleanup(m_instances[i]);
+
+ delete[] m_instances;
+}
+
+
+void
+LADSPAPlugin::activate()
+{
+ NodeBase::activate();
+
+ PortBase<sample>* port = NULL;
+
+ for (size_t i=0; i < m_poly; ++i) {
+ for (unsigned long j=0; j < m_descriptor->PortCount; ++j) {
+ port = static_cast<PortBase<sample>*>(m_ports.at(j));
+ set_port_buffer(i, j, ((PortBase<sample>*)m_ports.at(j))->buffer(i)->data());
+ if (port->port_info()->is_control())
+ port->set_value(port->port_info()->default_val(), 0);
+ else if (port->port_info()->is_audio())
+ port->set_value(0.0f, 0);
+ }
+ if (m_descriptor->activate != NULL)
+ m_descriptor->activate(m_instances[i]);
+ }
+}
+
+
+void
+LADSPAPlugin::deactivate()
+{
+ NodeBase::deactivate();
+
+ for (size_t i=0; i < m_poly; ++i)
+ if (m_descriptor->deactivate != NULL)
+ m_descriptor->deactivate(m_instances[i]);
+}
+
+
+void
+LADSPAPlugin::run(size_t nframes)
+{
+ NodeBase::run(nframes); // mixes down input ports
+ for (size_t i=0; i < m_poly; ++i)
+ m_descriptor->run(m_instances[i], nframes);
+}
+
+
+void
+LADSPAPlugin::set_port_buffer(size_t voice, size_t port_num, void* buf)
+{
+ assert(voice < m_poly);
+
+ // Could be a MIDI port after this
+ if (port_num < m_descriptor->PortCount) {
+ m_descriptor->connect_port(m_instances[voice], port_num, (sample*)buf);
+ }
+}
+
+
+// Based on code stolen from jack-rack
+void
+LADSPAPlugin::get_port_vals(ulong port_index, PortInfo* info)
+{
+ LADSPA_Data upper = 0.0f;
+ LADSPA_Data lower = 0.0f;
+ LADSPA_Data normal = 0.0f;
+ LADSPA_PortRangeHintDescriptor hint_descriptor = m_descriptor->PortRangeHints[port_index].HintDescriptor;
+
+ /* set upper and lower, possibly adjusted to the sample rate */
+ if (LADSPA_IS_HINT_SAMPLE_RATE(hint_descriptor)) {
+ upper = m_descriptor->PortRangeHints[port_index].UpperBound * m_srate;
+ lower = m_descriptor->PortRangeHints[port_index].LowerBound * m_srate;
+ } else {
+ upper = m_descriptor->PortRangeHints[port_index].UpperBound;
+ lower = m_descriptor->PortRangeHints[port_index].LowerBound;
+ }
+
+ if (LADSPA_IS_HINT_LOGARITHMIC(hint_descriptor)) {
+ /* FLT_EPSILON is defined as the different between 1.0 and the minimum
+ * float greater than 1.0. So, if lower is < FLT_EPSILON, it will be 1.0
+ * and the logarithmic control will have a base of 1 and thus not change
+ */
+ if (lower < FLT_EPSILON) lower = FLT_EPSILON;
+ }
+
+
+ if (LADSPA_IS_HINT_HAS_DEFAULT(hint_descriptor)) {
+
+ if (LADSPA_IS_HINT_DEFAULT_MINIMUM(hint_descriptor)) {
+ normal = lower;
+ } else if (LADSPA_IS_HINT_DEFAULT_LOW(hint_descriptor)) {
+ if (LADSPA_IS_HINT_LOGARITHMIC(hint_descriptor)) {
+ normal = exp(log(lower) * 0.75 + log(upper) * 0.25);
+ } else {
+ normal = lower * 0.75 + upper * 0.25;
+ }
+ } else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(hint_descriptor)) {
+ if (LADSPA_IS_HINT_LOGARITHMIC(hint_descriptor)) {
+ normal = exp(log(lower) * 0.5 + log(upper) * 0.5);
+ } else {
+ normal = lower * 0.5 + upper * 0.5;
+ }
+ } else if (LADSPA_IS_HINT_DEFAULT_HIGH(hint_descriptor)) {
+ if (LADSPA_IS_HINT_LOGARITHMIC(hint_descriptor)) {
+ normal = exp(log(lower) * 0.25 + log(upper) * 0.75);
+ } else {
+ normal = lower * 0.25 + upper * 0.75;
+ }
+ } else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(hint_descriptor)) {
+ normal = upper;
+ } else if (LADSPA_IS_HINT_DEFAULT_0(hint_descriptor)) {
+ normal = 0.0;
+ } else if (LADSPA_IS_HINT_DEFAULT_1(hint_descriptor)) {
+ normal = 1.0;
+ } else if (LADSPA_IS_HINT_DEFAULT_100(hint_descriptor)) {
+ normal = 100.0;
+ } else if (LADSPA_IS_HINT_DEFAULT_440(hint_descriptor)) {
+ normal = 440.0;
+ }
+ } else { // No default hint
+ if (LADSPA_IS_HINT_BOUNDED_BELOW(hint_descriptor)) {
+ normal = lower;
+ } else if (LADSPA_IS_HINT_BOUNDED_ABOVE(hint_descriptor)) {
+ normal = upper;
+ }
+ }
+
+ info->min_val(lower);
+ info->default_val(normal);
+ info->max_val(upper);
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/LADSPAPlugin.h b/src/libs/engine/LADSPAPlugin.h
new file mode 100644
index 00000000..dc1ba5e1
--- /dev/null
+++ b/src/libs/engine/LADSPAPlugin.h
@@ -0,0 +1,69 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LADSPAPLUGIN_H
+#define LADSPAPLUGIN_H
+
+#include <string>
+#include <ladspa.h>
+#include "util/types.h"
+#include "NodeBase.h"
+#include "Plugin.h"
+
+namespace Om {
+
+class PortInfo;
+
+
+/** An instance of a LADSPA plugin.
+ *
+ * \ingroup engine
+ */
+class LADSPAPlugin : public NodeBase
+{
+public:
+ LADSPAPlugin(const string& name, size_t poly, Patch* parent, const LADSPA_Descriptor* descriptor, samplerate srate, size_t buffer_size);
+ virtual ~LADSPAPlugin();
+
+ virtual bool instantiate();
+
+ void activate();
+ void deactivate();
+
+ void run(size_t nframes);
+
+ void set_port_buffer(size_t voice, size_t port_num, void* buf);
+
+ const Plugin* plugin() const { return m_plugin; }
+ void plugin(const Plugin* const pi) { m_plugin = pi; }
+
+protected:
+ // Prevent copies (undefined)
+ LADSPAPlugin(const LADSPAPlugin& copy);
+ LADSPAPlugin& operator=(const LADSPAPlugin&);
+
+ void get_port_vals(ulong port_index, PortInfo* info);
+
+ const LADSPA_Descriptor* m_descriptor;
+ LADSPA_Handle* m_instances;
+
+ const Plugin* m_plugin;
+};
+
+
+} // namespace Om
+
+#endif // LADSPAPLUGIN_H
diff --git a/src/libs/engine/LV2Plugin.cpp b/src/libs/engine/LV2Plugin.cpp
new file mode 100644
index 00000000..1c74bca9
--- /dev/null
+++ b/src/libs/engine/LV2Plugin.cpp
@@ -0,0 +1,275 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "LV2Plugin.h"
+#include <iostream>
+#include <cassert>
+#include "float.h"
+#include <stdint.h>
+#include <cmath>
+#include "InputPort.h"
+#include "OutputPort.h"
+#include "PortInfo.h"
+#include "Plugin.h"
+
+namespace Om {
+
+
+/** Partially construct a LV2Plugin.
+ *
+ * Object is not usable until instantiate() is called with success.
+ * (It _will_ crash!)
+ */
+LV2Plugin::LV2Plugin(const string& name,
+ size_t poly,
+ Patch* parent,
+ const SLV2Plugin* plugin,
+ samplerate srate,
+ size_t buffer_size)
+: NodeBase(name, poly, parent, srate, buffer_size),
+ m_lv2_plugin(plugin),
+ m_instances(NULL)
+{
+ assert(m_lv2_plugin);
+
+ // Note that this may be changed by an overriding DSSIPlugin
+ // ie do not assume m_ports is all LV2 plugin ports
+ m_num_ports = slv2_plugin_get_num_ports(m_lv2_plugin);
+}
+
+
+/** Instantiate self from LV2 plugin descriptor.
+ *
+ * Implemented as a seperate function (rather than in the constructor) to
+ * allow graceful error-catching of broken plugins.
+ *
+ * Returns whether or not plugin was successfully instantiated. If return
+ * value is false, this object may not be used.
+ */
+bool
+LV2Plugin::instantiate()
+{
+ m_ports.alloc(m_num_ports);
+
+ m_instances = new SLV2Instance*[m_poly];
+
+ size_t port_buffer_size = 0;
+
+ for (size_t i=0; i < m_poly; ++i) {
+ m_instances[i] = slv2_plugin_instantiate(m_lv2_plugin, m_srate, NULL);
+ if (m_instances[i] == NULL) {
+ cerr << "Failed to instantiate plugin!" << endl;
+ return false;
+ }
+ }
+
+ string port_name;
+ string port_path;
+
+ Port* port = NULL;
+
+ for (size_t j=0; j < m_num_ports; ++j) {
+ // LV2 shortnames are guaranteed to be unique
+ port_name = (char*)slv2_port_get_symbol(m_lv2_plugin, j);
+
+ string::size_type slash_index;
+
+ // Replace all slashes with "-" (so they don't screw up paths)
+ while ((slash_index = port_name.find("/")) != string::npos)
+ port_name[slash_index] = '-';
+
+ port_path = path() + "/" + port_name;
+
+ // Assumes there is only the 4 classes
+
+ SLV2PortClass port_class = slv2_port_get_class(m_lv2_plugin, j);
+ const bool is_control = (port_class == SLV2_CONTROL_RATE_INPUT
+ || port_class == SLV2_CONTROL_RATE_OUTPUT);
+ const bool is_input = (port_class == SLV2_CONTROL_RATE_INPUT
+ || port_class == SLV2_AUDIO_RATE_INPUT);
+
+ if (is_control)
+ port_buffer_size = 1;
+ else
+ port_buffer_size = m_buffer_size;
+
+ PortType type = is_control ? CONTROL : AUDIO;
+ PortDirection direction = is_input ? INPUT : OUTPUT;
+
+ if (is_input) {
+ port = new InputPort<sample>(this, port_name, j, m_poly,
+ new PortInfo(port_path, type, direction), port_buffer_size);
+ m_ports.at(j) = port;
+ } else /* output */ {
+ port = new OutputPort<sample>(this, port_name, j, m_poly,
+ new PortInfo(port_path, type, direction), port_buffer_size);
+ m_ports.at(j) = port;
+ }
+
+ assert(m_ports.at(j) != NULL);
+
+ PortInfo* pi = port->port_info();
+ get_port_vals(j, pi);
+
+ // Set default control val
+ if (pi->is_control())
+ ((PortBase<sample>*)port)->set_value(pi->default_val(), 0);
+ else
+ ((PortBase<sample>*)port)->set_value(0.0f, 0);
+ }
+ return true;
+}
+
+
+LV2Plugin::~LV2Plugin()
+{
+ for (size_t i=0; i < m_poly; ++i)
+ slv2_instance_free(m_instances[i]);
+
+ delete[] m_instances;
+}
+
+
+void
+LV2Plugin::activate()
+{
+ NodeBase::activate();
+
+ PortBase<sample>* port = NULL;
+
+ for (size_t i=0; i < m_poly; ++i) {
+ for (unsigned long j=0; j < m_num_ports; ++j) {
+ port = static_cast<PortBase<sample>*>(m_ports.at(j));
+ set_port_buffer(i, j, ((PortBase<sample>*)m_ports.at(j))->buffer(i)->data());
+ if (port->port_info()->is_control())
+ port->set_value(port->port_info()->default_val(), 0);
+ else if (port->port_info()->is_audio())
+ port->set_value(0.0f, 0);
+ }
+ slv2_instance_activate(m_instances[i]);
+ }
+}
+
+
+void
+LV2Plugin::deactivate()
+{
+ NodeBase::deactivate();
+
+ for (size_t i=0; i < m_poly; ++i)
+ slv2_instance_deactivate(m_instances[i]);
+}
+
+
+void
+LV2Plugin::run(size_t nframes)
+{
+ NodeBase::run(nframes); // mixes down input ports
+ for (size_t i=0; i < m_poly; ++i)
+ slv2_instance_run(m_instances[i], nframes);
+}
+
+
+void
+LV2Plugin::set_port_buffer(size_t voice, size_t port_num, void* buf)
+{
+ assert(voice < m_poly);
+
+ // Could be a MIDI port after this
+ if (port_num < m_num_ports) {
+ slv2_instance_connect_port(m_instances[voice], port_num, buf);
+ }
+}
+
+
+// Based on code stolen from jack-rack
+void
+LV2Plugin::get_port_vals(ulong port_index, PortInfo* info)
+{
+#if 0
+ LV2_Data upper = 0.0f;
+ LV2_Data lower = 0.0f;
+ LV2_Data normal = 0.0f;
+ LV2_PortRangeHintDescriptor hint_descriptor = m_descriptor->PortRangeHints[port_index].HintDescriptor;
+
+ /* set upper and lower, possibly adjusted to the sample rate */
+ if (LV2_IS_HINT_SAMPLE_RATE(hint_descriptor)) {
+ upper = m_descriptor->PortRangeHints[port_index].UpperBound * m_srate;
+ lower = m_descriptor->PortRangeHints[port_index].LowerBound * m_srate;
+ } else {
+ upper = m_descriptor->PortRangeHints[port_index].UpperBound;
+ lower = m_descriptor->PortRangeHints[port_index].LowerBound;
+ }
+
+ if (LV2_IS_HINT_LOGARITHMIC(hint_descriptor)) {
+ /* FLT_EPSILON is defined as the different between 1.0 and the minimum
+ * float greater than 1.0. So, if lower is < FLT_EPSILON, it will be 1.0
+ * and the logarithmic control will have a base of 1 and thus not change
+ */
+ if (lower < FLT_EPSILON) lower = FLT_EPSILON;
+ }
+
+
+ if (LV2_IS_HINT_HAS_DEFAULT(hint_descriptor)) {
+
+ if (LV2_IS_HINT_DEFAULT_MINIMUM(hint_descriptor)) {
+ normal = lower;
+ } else if (LV2_IS_HINT_DEFAULT_LOW(hint_descriptor)) {
+ if (LV2_IS_HINT_LOGARITHMIC(hint_descriptor)) {
+ normal = exp(log(lower) * 0.75 + log(upper) * 0.25);
+ } else {
+ normal = lower * 0.75 + upper * 0.25;
+ }
+ } else if (LV2_IS_HINT_DEFAULT_MIDDLE(hint_descriptor)) {
+ if (LV2_IS_HINT_LOGARITHMIC(hint_descriptor)) {
+ normal = exp(log(lower) * 0.5 + log(upper) * 0.5);
+ } else {
+ normal = lower * 0.5 + upper * 0.5;
+ }
+ } else if (LV2_IS_HINT_DEFAULT_HIGH(hint_descriptor)) {
+ if (LV2_IS_HINT_LOGARITHMIC(hint_descriptor)) {
+ normal = exp(log(lower) * 0.25 + log(upper) * 0.75);
+ } else {
+ normal = lower * 0.25 + upper * 0.75;
+ }
+ } else if (LV2_IS_HINT_DEFAULT_MAXIMUM(hint_descriptor)) {
+ normal = upper;
+ } else if (LV2_IS_HINT_DEFAULT_0(hint_descriptor)) {
+ normal = 0.0;
+ } else if (LV2_IS_HINT_DEFAULT_1(hint_descriptor)) {
+ normal = 1.0;
+ } else if (LV2_IS_HINT_DEFAULT_100(hint_descriptor)) {
+ normal = 100.0;
+ } else if (LV2_IS_HINT_DEFAULT_440(hint_descriptor)) {
+ normal = 440.0;
+ }
+ } else { // No default hint
+ if (LV2_IS_HINT_BOUNDED_BELOW(hint_descriptor)) {
+ normal = lower;
+ } else if (LV2_IS_HINT_BOUNDED_ABOVE(hint_descriptor)) {
+ normal = upper;
+ }
+ }
+
+ info->min_val(lower);
+ info->default_val(normal);
+ info->max_val(upper);
+#endif
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/LV2Plugin.h b/src/libs/engine/LV2Plugin.h
new file mode 100644
index 00000000..c3d3038a
--- /dev/null
+++ b/src/libs/engine/LV2Plugin.h
@@ -0,0 +1,76 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LV2PLUGIN_H
+#define LV2PLUGIN_H
+
+#include <string>
+#include <slv2/slv2.h>
+#include "util/types.h"
+#include "NodeBase.h"
+#include "Plugin.h"
+
+namespace Om {
+
+class PortInfo;
+
+
+/** An instance of a LV2 plugin.
+ *
+ * \ingroup engine
+ */
+class LV2Plugin : public NodeBase
+{
+public:
+ LV2Plugin(const string& name,
+ size_t poly,
+ Patch* parent,
+ const SLV2Plugin* plugin,
+ samplerate srate,
+ size_t buffer_size);
+
+ virtual ~LV2Plugin();
+
+ virtual bool instantiate();
+
+ void activate();
+ void deactivate();
+
+ void run(size_t nframes);
+
+ void set_port_buffer(size_t voice, size_t port_num, void* buf);
+
+ const Plugin* plugin() const { return m_om_plugin; }
+ void plugin(const Plugin* const p) { m_om_plugin = p; }
+
+protected:
+ // Prevent copies (undefined)
+ LV2Plugin(const LV2Plugin& copy);
+ LV2Plugin& operator=(const LV2Plugin&);
+
+ void get_port_vals(ulong port_index, PortInfo* info);
+
+ const SLV2Plugin* m_lv2_plugin;
+ SLV2Instance** m_instances;
+
+ const Plugin* m_om_plugin;
+};
+
+
+} // namespace Om
+
+#endif // LV2PLUGIN_H
+
diff --git a/src/libs/engine/LashDriver.cpp b/src/libs/engine/LashDriver.cpp
new file mode 100644
index 00000000..f42f87f2
--- /dev/null
+++ b/src/libs/engine/LashDriver.cpp
@@ -0,0 +1,159 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "LashDriver.h"
+#include "config.h"
+#include <iostream>
+#include <string>
+#include <cassert>
+#include "OmApp.h"
+
+using std::cerr; using std::cout; using std::endl;
+using std::string;
+
+namespace Om {
+
+
+LashDriver::LashDriver(OmApp* app, lash_args_t* args)
+: m_app(app),
+ m_client(NULL),
+ m_alsa_client_id(0) // FIXME: appropriate sentinel?
+{
+ m_client = lash_init(args, PACKAGE_NAME,
+ /*LASH_Config_Data_Set|LASH_Config_File*/0, LASH_PROTOCOL(2, 0));
+ if (m_client == NULL) {
+ cerr << "[LashDriver] Failed to connect to LASH. Session management will not function." << endl;
+ } else {
+ cout << "[LashDriver] Lash initialised" << endl;
+ lash_event_t* event = lash_event_new_with_type(LASH_Client_Name);
+ lash_event_set_string(event, "Om");
+ lash_send_event(m_client, event);
+ }
+}
+
+
+/** Set the Jack client name to be sent to LASH.
+ * The name isn't actually send until restore_finished() is called.
+ */
+void
+LashDriver::set_jack_client_name(const char* name)
+{
+ m_jack_client_name = name;
+ lash_jack_client_name(m_client, m_jack_client_name.c_str());
+}
+
+
+/** Set the Alsa client ID to be sent to LASH.
+ * The name isn't actually send until restore_finished() is called.
+ */
+void
+LashDriver::set_alsa_client_id(int id)
+{
+ m_alsa_client_id = id;
+ lash_alsa_client_id(m_client, m_alsa_client_id);
+}
+
+
+/** Notify LASH of our port names so it can restore connections.
+ * The Alsa client ID and Jack client name MUST be set before calling
+ * this function.
+ */
+void
+LashDriver::restore_finished()
+{
+ assert(m_jack_client_name != "");
+ assert(m_alsa_client_id != 0);
+
+ cerr << "LASH RESTORE FINISHED " << m_jack_client_name << " - " << m_alsa_client_id << endl;
+
+ lash_jack_client_name(m_client, m_jack_client_name.c_str());
+ lash_alsa_client_id(m_client, m_alsa_client_id);
+}
+
+
+void
+LashDriver::process_events()
+{
+ assert(m_client != NULL);
+
+ lash_event_t* ev = NULL;
+ lash_config_t* conf = NULL;
+
+ // Process events
+ while ((ev = lash_get_event(m_client)) != NULL) {
+ handle_event(ev);
+ lash_event_destroy(ev);
+ }
+
+ // Process configs
+ while ((conf = lash_get_config(m_client)) != NULL) {
+ handle_config(conf);
+ lash_config_destroy(conf);
+ }
+}
+
+
+void
+LashDriver::handle_event(lash_event_t* ev)
+{
+ LASH_Event_Type type = lash_event_get_type(ev);
+ const char* c_str = lash_event_get_string(ev);
+ string str = (c_str == NULL) ? "" : c_str;
+
+ //cout << "[LashDriver] LASH Event. Type = " << type << ", string = " << str << "**********" << endl;
+
+ /*if (type == LASH_Save_File) {
+ //cout << "[LashDriver] LASH Save File - " << str << endl;
+ m_app->store_window_location();
+ m_app->state_manager()->save(str.append("/locations"));
+ lash_send_event(m_client, lash_event_new_with_type(LASH_Save_File));
+ } else if (type == LASH_Restore_File) {
+ //cout << "[LashDriver] LASH Restore File - " << str << endl;
+ m_app->state_manager()->load(str.append("/locations"));
+ m_app->update_state();
+ lash_send_event(m_client, lash_event_new_with_type(LASH_Restore_File));
+ } else if (type == LASH_Save_Data_Set) {
+ //cout << "[LashDriver] LASH Save Data Set - " << endl;
+
+ // Tell LASH we're done
+ lash_send_event(m_client, lash_event_new_with_type(LASH_Save_Data_Set));
+ } else*/
+ if (type == LASH_Quit) {
+ //stop_thread();
+ m_client = NULL;
+ m_app->quit();
+ } else {
+ cerr << "[LashDriver] WARNING: Unhandled lash event, type " << static_cast<int>(type) << endl;
+ }
+}
+
+
+void
+LashDriver::handle_config(lash_config_t* conf)
+{
+ const char* key = NULL;
+ const void* val = NULL;
+ size_t val_size = 0;
+
+ //cout << "[LashDriver] LASH Config. Key = " << key << endl;
+
+ key = lash_config_get_key(conf);
+ val = lash_config_get_value(conf);
+ val_size = lash_config_get_value_size(conf);
+}
+
+
+} // namespace Om
diff --git a/src/libs/engine/LashDriver.h b/src/libs/engine/LashDriver.h
new file mode 100644
index 00000000..dedf1c6b
--- /dev/null
+++ b/src/libs/engine/LashDriver.h
@@ -0,0 +1,57 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LASHDRIVER_H
+#define LASHDRIVER_H
+
+#include <lash/lash.h>
+#include <pthread.h>
+#include <string>
+using std::string;
+
+namespace Om {
+
+class OmApp;
+
+
+/** Handles all support for LASH session management.
+ */
+class LashDriver
+{
+public:
+ LashDriver(OmApp* app, lash_args_t* args);
+
+ bool enabled() { return (m_client != NULL && lash_enabled(m_client)); }
+ void process_events();
+ void set_jack_client_name(const char* name);
+ void set_alsa_client_id(int id);
+ void restore_finished();
+
+private:
+ OmApp* m_app;
+ lash_client_t* m_client;
+
+ int m_alsa_client_id;
+ string m_jack_client_name;
+
+ void handle_event(lash_event_t* conf);
+ void handle_config(lash_config_t* conf);
+};
+
+
+} // namespace Om
+
+#endif // LASHDRIVER_H
diff --git a/src/libs/engine/List.h b/src/libs/engine/List.h
new file mode 100644
index 00000000..cc51bc5d
--- /dev/null
+++ b/src/libs/engine/List.h
@@ -0,0 +1,416 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIST_H
+#define LIST_H
+
+#include <cassert>
+#include "util/types.h"
+#include "MaidObject.h"
+
+
+/** A node in a List.
+ *
+ * This class is (unusually) exposed to the user to allow list operations
+ * to be realtime safe (ie so events can allocate list nodes in other threads
+ * and then insert them in the realtime thread.
+ */
+template <typename T>
+class ListNode : public MaidObject
+{
+public:
+ ListNode(T elem) : m_elem(elem), m_next(NULL), m_prev(NULL) {}
+ virtual ~ListNode() {}
+
+ ListNode* next() const { return m_next; }
+ void next(ListNode* ln) { m_next = ln; }
+ ListNode* prev() const { return m_prev; }
+ void prev(ListNode* ln) { m_prev = ln; }
+ T& elem() { return m_elem;}
+ const T& elem() const { return m_elem; }
+
+private:
+ // Prevent copies (undefined)
+ ListNode(const ListNode& copy);
+ ListNode& operator=(const ListNode& copy);
+
+ T m_elem;
+ ListNode* m_next;
+ ListNode* m_prev;
+};
+
+
+
+/** A realtime safe, (partially) thread safe doubly linked list.
+ *
+ * Elements can be added safely while another thread is reading the list. See
+ * documentation for specific functions for realtime/thread safeness.
+ */
+template <typename T>
+class List : public MaidObject
+{
+public:
+ List() : m_head(NULL), m_tail(NULL), m_size(0), m_end_iter(this), m_const_end_iter(this)
+ {
+ m_end_iter.m_listnode = NULL;
+ m_end_iter.m_next = NULL;
+ m_const_end_iter.m_listnode = NULL;
+ m_const_end_iter.m_next = NULL;
+ }
+ ~List();
+
+ void push_back(ListNode<T>* elem);
+ ListNode<T>* remove(const T elem);
+
+ void clear();
+ size_t size() const { return m_size; }
+
+ class iterator;
+
+ /** Realtime safe const iterator for a List. */
+ class const_iterator
+ {
+ public:
+ const_iterator(const List<T>* const list);
+ const_iterator(const iterator& i)
+ : m_list(i.m_list), m_listnode(i.m_listnode), m_next(i.m_next) {}
+
+ inline const T& operator*();
+ inline const_iterator& operator++();
+ inline bool operator!=(const const_iterator& iter) const;
+ inline bool operator!=(const iterator& iter) const;
+
+ friend class List<T>;
+
+ private:
+ const List<T>* const m_list;
+ const ListNode<T>* m_listnode;
+ const ListNode<T>* m_next; // use this instead of m_listnode->next() to allow deleting
+ };
+
+
+ /** Realtime safe iterator for a List. */
+ class iterator
+ {
+ public:
+ iterator(List<T>* const list);
+
+ inline T& operator*();
+ inline iterator& operator++();
+ inline bool operator!=(const iterator& iter) const;
+ inline bool operator!=(const const_iterator& iter) const;
+
+ friend class List<T>;
+ friend class List<T>::const_iterator;
+
+ private:
+ const List<T>* m_list;
+ ListNode<T>* m_listnode;
+ ListNode<T>* m_next; // use this instead of m_listnode->next() to allow deleting
+ };
+
+
+ ListNode<T>* remove(const iterator iter);
+
+ iterator begin();
+ const iterator end() const;
+
+ const_iterator begin() const;
+ //const_iterator end() const;
+
+private:
+ // Prevent copies (undefined)
+ List(const List& copy);
+ List& operator=(const List& copy);
+
+ ListNode<T>* m_head;
+ ListNode<T>* m_tail;
+ size_t m_size;
+ iterator m_end_iter;
+ const_iterator m_const_end_iter;
+};
+
+
+
+
+template <typename T>
+List<T>::~List<T>()
+{
+ clear();
+}
+
+
+/** Clear the list, deleting all ListNodes contained (but NOT their contents!)
+ *
+ * Not realtime safe.
+ */
+template <typename T>
+void
+List<T>::clear()
+{
+ if (m_head == NULL) return;
+
+ ListNode<T>* node = m_head;
+ ListNode<T>* next = NULL;
+
+ while (node != NULL) {
+ next = node->next();
+ delete node;
+ node = next;
+ }
+ m_tail = m_head = NULL;
+ m_size = 0;
+}
+
+
+/** Add an element to the list.
+ *
+ * This method can be called while another thread is reading the list.
+ * Realtime safe.
+ */
+template <typename T>
+void
+List<T>::push_back(ListNode<T>* const ln)
+{
+ assert(ln != NULL);
+
+ ln->next(NULL);
+ // FIXME: atomicity? relevant?
+ if (m_head == NULL) {
+ ln->prev(NULL);
+ m_head = m_tail = ln;
+ } else {
+ ln->prev(m_tail);
+ m_tail->next(ln);
+ m_tail = ln;
+ }
+ ++m_size;
+}
+
+
+/** Remove an element from the list.
+ *
+ * This function is realtime safe - it is the caller's responsibility to
+ * delete the returned ListNode, or there will be a leak.
+ */
+template <typename T>
+ListNode<T>*
+List<T>::remove(const T elem)
+{
+ // FIXME: atomicity?
+ ListNode<T>* n = m_head;
+ while (n != NULL) {
+ if (n->elem() == elem)
+ break;
+ n = n->next();
+ }
+ if (n != NULL) {
+ if (n == m_head) m_head = m_head->next();
+ if (n == m_tail) m_tail = m_tail->prev();
+ if (n->prev() != NULL)
+ n->prev()->next(n->next());
+ if (n->next() != NULL)
+ n->next()->prev(n->prev());
+ --m_size;
+ if (m_size == 0) m_head = m_tail = NULL; // FIXME: Shouldn't be necessary
+ return n;
+ }
+ return NULL;
+}
+
+
+/** Remove an element from the list using an iterator.
+ *
+ * This function is realtime safe - it is the caller's responsibility to
+ * delete the returned ListNode, or there will be a leak.
+ */
+template <typename T>
+ListNode<T>*
+List<T>::remove(const iterator iter)
+{
+ ListNode<T>* n = iter.m_listnode;
+ if (n != NULL) {
+ if (n == m_head) m_head = m_head->next();
+ if (n == m_tail) m_tail = m_tail->prev();
+ if (n->prev() != NULL)
+ n->prev()->next(n->next());
+ if (n->next() != NULL)
+ n->next()->prev(n->prev());
+ --m_size;
+ if (m_size == 0) m_head = m_tail = NULL; // FIXME: Shouldn't be necessary
+ return n;
+ }
+ return NULL;
+}
+
+
+//// Iterator stuff ////
+
+template <typename T>
+List<T>::iterator::iterator(List<T>* list)
+: m_list(list),
+ m_listnode(NULL),
+ m_next(NULL)
+{
+}
+
+
+template <typename T>
+T&
+List<T>::iterator::operator*()
+{
+ assert(m_listnode != NULL);
+ return m_listnode->elem();
+}
+
+
+template <typename T>
+inline typename List<T>::iterator&
+List<T>::iterator::operator++()
+{
+ assert(m_listnode != NULL);
+ m_listnode = m_next;
+ if (m_next != NULL)
+ m_next = m_next->next();
+ else
+ m_next = NULL;
+
+ return *this;
+}
+
+
+template <typename T>
+inline bool
+List<T>::iterator::operator!=(const iterator& iter) const
+{
+ return (m_listnode != iter.m_listnode);
+}
+
+
+template <typename T>
+inline bool
+List<T>::iterator::operator!=(const const_iterator& iter) const
+{
+ return (m_listnode != iter.m_listnode);
+}
+
+
+template <typename T>
+inline typename List<T>::iterator
+List<T>::begin()
+{
+ typename List<T>::iterator iter(this);
+ iter.m_listnode = m_head;
+ if (m_head != NULL)
+ iter.m_next = m_head->next();
+ else
+ iter.m_next = NULL;
+ return iter;
+}
+
+
+template <typename T>
+inline const typename List<T>::iterator
+List<T>::end() const
+{
+ /*typename List<T>::iterator iter(this);
+ iter.m_listnode = NULL;
+ iter.m_next = NULL;
+ return iter;*/
+ return m_end_iter;
+}
+
+
+
+/// const_iterator stuff ///
+
+
+template <typename T>
+List<T>::const_iterator::const_iterator(const List<T>* const list)
+: m_list(list),
+ m_listnode(NULL),
+ m_next(NULL)
+{
+}
+
+
+template <typename T>
+const T&
+List<T>::const_iterator::operator*()
+{
+ assert(m_listnode != NULL);
+ return m_listnode->elem();
+}
+
+
+template <typename T>
+inline typename List<T>::const_iterator&
+List<T>::const_iterator::operator++()
+{
+ assert(m_listnode != NULL);
+ m_listnode = m_next;
+ if (m_next != NULL)
+ m_next = m_next->next();
+ else
+ m_next = NULL;
+
+ return *this;
+}
+
+
+template <typename T>
+inline bool
+List<T>::const_iterator::operator!=(const const_iterator& iter) const
+{
+ return (m_listnode != iter.m_listnode);
+}
+
+
+template <typename T>
+inline bool
+List<T>::const_iterator::operator!=(const iterator& iter) const
+{
+ return (m_listnode != iter.m_listnode);
+}
+
+
+template <typename T>
+inline typename List<T>::const_iterator
+List<T>::begin() const
+{
+ typename List<T>::const_iterator iter(this);
+ iter.m_listnode = m_head;
+ if (m_head != NULL)
+ iter.m_next = m_head->next();
+ else
+ iter.m_next = NULL;
+ return iter;
+}
+
+#if 0
+template <typename T>
+inline typename List<T>::const_iterator
+List<T>::end() const
+{
+ /*typename List<T>::const_iterator iter(this);
+ iter.m_listnode = NULL;
+ iter.m_next = NULL;
+ return iter;*/
+ return m_const_end_iter;
+}
+#endif
+
+#endif // LIST_H
diff --git a/src/libs/engine/Maid.cpp b/src/libs/engine/Maid.cpp
new file mode 100644
index 00000000..3bebdedb
--- /dev/null
+++ b/src/libs/engine/Maid.cpp
@@ -0,0 +1,46 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "Maid.h"
+#include "MaidObject.h"
+
+
+Maid::Maid(size_t size)
+: m_objects(size)
+{
+}
+
+
+Maid::~Maid()
+{
+ cleanup();
+}
+
+
+/** Free all the objects in the queue (passed by push()).
+ */
+void
+Maid::cleanup()
+{
+ MaidObject* obj = NULL;
+
+ while (!m_objects.is_empty()) {
+ obj = m_objects.pop();
+ delete obj;
+ }
+}
+
+
diff --git a/src/libs/engine/Maid.h b/src/libs/engine/Maid.h
new file mode 100644
index 00000000..75412186
--- /dev/null
+++ b/src/libs/engine/Maid.h
@@ -0,0 +1,65 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef MAID_H
+#define MAID_H
+
+#include "MaidObject.h"
+#include "util/Queue.h"
+
+
+/** Explicitly driven garbage collector.
+ *
+ * This is how realtime parts of the code can schedule the deletion of
+ * objects - push() is realtime safe.
+ *
+ * cleanup() is meant to be called periodically to free memory, often
+ * enough to prevent the queue from overdflowing. This is done by the
+ * main thread, in OmApp.
+ *
+ * \ingroup engine
+ */
+class Maid
+{
+public:
+ Maid(size_t size);
+ ~Maid();
+
+ inline void push(MaidObject* obj);
+
+ void cleanup();
+
+private:
+ // Prevent copies
+ Maid(const Maid&);
+ Maid& operator=(const Maid&);
+
+ Queue<MaidObject*> m_objects;
+};
+
+
+/** Push an event to be deleted. Realtime safe.
+ */
+inline void
+Maid::push(MaidObject* obj)
+{
+ if (obj != NULL)
+ m_objects.push(obj);
+}
+
+
+#endif // MAID_H
+
diff --git a/src/libs/engine/MaidObject.h b/src/libs/engine/MaidObject.h
new file mode 100644
index 00000000..d0060520
--- /dev/null
+++ b/src/libs/engine/MaidObject.h
@@ -0,0 +1,38 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef MAIDOBJECT_H
+#define MAIDOBJECT_H
+
+
+/** An object that can be passed to the maid for deletion.
+ *
+ * \ingroup engine
+ */
+class MaidObject
+{
+public:
+ MaidObject() {}
+ virtual ~MaidObject() {}
+
+private:
+ // Prevent copies
+ MaidObject(const MaidObject&);
+ MaidObject& operator=(const MaidObject&);
+};
+
+
+#endif // MAIDOBJECT_H
diff --git a/src/libs/engine/Makefile.am b/src/libs/engine/Makefile.am
new file mode 100644
index 00000000..ae0b3e53
--- /dev/null
+++ b/src/libs/engine/Makefile.am
@@ -0,0 +1,259 @@
+SUBDIRS = tests
+DIST_SUBDIRS = events
+
+AM_CXXFLAGS = @JACK_CFLAGS@ @LOSC_CFLAGS@ @ALSA_CFLAGS@ @LASH_CFLAGS@ @SLV2_CFLAGS@ -I$(top_srcdir)/src/common -I$(top_srcdir)/src/engine/events -fno-exceptions -fno-rtti
+
+MAINTAINERCLEANFILES = Makefile.in
+
+common_SOURCES = \
+ util.h \
+ tuning.h \
+ Node.h \
+ NodeBase.h \
+ NodeBase.cpp \
+ InternalNode.h \
+ Patch.h \
+ Patch.cpp \
+ NodeFactory.h \
+ NodeFactory.cpp \
+ Om.h \
+ Om.cpp \
+ OmApp.h \
+ OmApp.cpp \
+ JackAudioDriver.h \
+ JackAudioDriver.cpp \
+ OSCReceiver.h \
+ OSCReceiver.cpp \
+ Responder.h \
+ OSCResponder.h \
+ OSCResponder.cpp \
+ ClientKey.h \
+ ClientBroadcaster.h \
+ ClientBroadcaster.cpp \
+ ObjectSender.h \
+ ObjectSender.cpp \
+ OSCClient.h \
+ OSCClient.cpp \
+ Buffer.h \
+ Buffer.cpp \
+ Port.h \
+ Port.cpp \
+ PortBase.h \
+ PortBase.cpp \
+ InputPort.h \
+ InputPort.cpp \
+ OutputPort.h \
+ OutputPort.cpp \
+ MidiMessage.h \
+ MidiNoteNode.h \
+ MidiNoteNode.cpp \
+ MidiTriggerNode.h \
+ MidiTriggerNode.cpp \
+ MidiControlNode.h \
+ MidiControlNode.cpp \
+ BridgeNode.h \
+ BridgeNode.cpp \
+ ControlInputNode.h \
+ ControlInputNode.cpp \
+ ControlOutputNode.h \
+ ControlOutputNode.cpp \
+ AudioInputNode.h \
+ AudioInputNode.cpp \
+ AudioOutputNode.h \
+ AudioOutputNode.cpp \
+ MidiInputNode.h \
+ MidiInputNode.cpp \
+ MidiOutputNode.h \
+ MidiOutputNode.cpp \
+ Event.h \
+ Event.cpp \
+ QueuedEvent.h \
+ EventSource.h \
+ QueuedEventSource.h \
+ QueuedEventSource.cpp \
+ QueuedEngineInterface.h \
+ QueuedEngineInterface.cpp \
+ OmObject.h \
+ Maid.h \
+ Maid.cpp \
+ MaidObject.h \
+ Tree.h \
+ ClientRecord.h \
+ PortInfo.h \
+ PluginLibrary.h \
+ Plugin.h \
+ Array.h \
+ List.h \
+ PostProcessor.h \
+ PostProcessor.cpp \
+ Connection.h \
+ Connection.cpp \
+ ConnectionBase.h \
+ ConnectionBase.cpp \
+ ObjectStore.h \
+ ObjectStore.cpp \
+ TransportNode.h \
+ TransportNode.cpp \
+ Driver.h \
+ AudioDriver.h \
+ MidiDriver.h \
+ midi.h \
+ ../common/util/Semaphore.h \
+ ../common/util/types.h \
+ ../common/util/Path.h \
+ ../common/util/Queue.h \
+ ../common/interface/ClientInterface.h \
+ ../common/interface/EngineInterface.h \
+ instantiations.cpp
+
+# Events
+common_SOURCES += \
+ events/RegisterClientEvent.h \
+ events/RegisterClientEvent.cpp \
+ events/UnregisterClientEvent.h \
+ events/UnregisterClientEvent.cpp \
+ events/PingQueuedEvent.h \
+ events/ActivateEvent.h \
+ events/ActivateEvent.cpp \
+ events/DeactivateEvent.h \
+ events/DeactivateEvent.cpp \
+ events/SetPortValueEvent.h \
+ events/SetPortValueEvent.cpp \
+ events/SetPortValueQueuedEvent.h \
+ events/SetPortValueQueuedEvent.cpp \
+ events/NoteOnEvent.h \
+ events/NoteOnEvent.cpp \
+ events/NoteOffEvent.h \
+ events/NoteOffEvent.cpp \
+ events/AllNotesOffEvent.h \
+ events/AllNotesOffEvent.cpp \
+ events/ConnectionEvent.h \
+ events/ConnectionEvent.cpp \
+ events/DisconnectionEvent.h \
+ events/DisconnectionEvent.cpp \
+ events/DisconnectNodeEvent.h \
+ events/DisconnectNodeEvent.cpp \
+ events/DisconnectPortEvent.h \
+ events/DisconnectPortEvent.cpp \
+ events/DestroyEvent.h \
+ events/DestroyEvent.cpp \
+ events/AddNodeEvent.h \
+ events/AddNodeEvent.cpp \
+ events/SetMetadataEvent.h \
+ events/SetMetadataEvent.cpp \
+ events/RequestMetadataEvent.h \
+ events/RequestMetadataEvent.cpp \
+ events/RequestPortValueEvent.h \
+ events/RequestPortValueEvent.cpp \
+ events/RequestAllObjectsEvent.h \
+ events/RequestAllObjectsEvent.cpp \
+ events/RequestPluginsEvent.h \
+ events/RequestPluginsEvent.cpp \
+ events/CreatePatchEvent.h \
+ events/CreatePatchEvent.cpp \
+ events/LoadPluginsEvent.h \
+ events/LoadPluginsEvent.cpp \
+ events/EnablePatchEvent.h \
+ events/EnablePatchEvent.cpp \
+ events/DisablePatchEvent.h \
+ events/DisablePatchEvent.cpp \
+ events/ClearPatchEvent.h \
+ events/ClearPatchEvent.cpp \
+ events/RenameEvent.h \
+ events/RenameEvent.cpp \
+ events/MidiLearnEvent.h \
+ events/MidiLearnEvent.cpp
+
+if WITH_JACK_MIDI
+common_SOURCES += \
+ JackMidiDriver.h \
+ JackMidiDriver.cpp
+endif
+
+if WITH_ALSA_MIDI
+common_SOURCES += \
+ AlsaMidiDriver.h \
+ AlsaMidiDriver.cpp
+endif
+
+if WITH_LADSPA
+common_SOURCES += \
+ LADSPAPlugin.h \
+ LADSPAPlugin.cpp
+endif
+
+if WITH_DSSI
+common_SOURCES += \
+ DSSIPlugin.h \
+ DSSIPlugin.cpp \
+ events/DSSIConfigureEvent.cpp \
+ events/DSSIConfigureEvent.h \
+ events/DSSIControlEvent.cpp \
+ events/DSSIControlEvent.h \
+ events/DSSIProgramEvent.cpp \
+ events/DSSIProgramEvent.h \
+ events/DSSIUpdateEvent.cpp \
+ events/DSSIUpdateEvent.h
+endif
+
+if WITH_LV2
+common_SOURCES += \
+ LV2Plugin.h \
+ LV2Plugin.cpp
+endif
+
+if WITH_LASH
+common_SOURCES += \
+ LashDriver.h \
+ LashDriver.cpp \
+ LashRestoreDoneEvent.h
+endif
+
+
+#
+# Non-installed library to link stand-alone and in-process build against
+#
+
+noinst_LTLIBRARIES = libom.la
+libom_la_SOURCES = $(common_SOURCES)
+
+
+
+#
+# Stand-alone engine
+#
+if BUILD_ENGINE
+
+bin_PROGRAMS = om
+om_DEPENDENCIES = libom.la
+om_LDADD = @JACK_LIBS@ @LOSC_LIBS@ @ALSA_LIBS@ @LASH_LIBS@ @SLV2_LIBS@ -lrt libom.la
+
+om_SOURCES = \
+ main.cpp \
+ cmdline.h \
+ cmdline.c
+
+endif # BUILD_ENGINE
+
+
+
+#
+# Jack internal client
+#
+if BUILD_IN_PROCESS_ENGINE
+
+
+# FIXME: broken
+
+
+# FIXME: Figure out how to get this properly
+omdir = $(prefix)/lib/jack
+
+om_la_CFLAGS = -fPIC
+om_LTLIBRARIES = om.la
+om_la_LDFLAGS = -module -avoid-version @JACK_LIBS@ @LOSC_LIBS@ @ALSA_LIBS@ @LASH_LIBS@ @SLV2_LIBS@
+om_la_SOURCES = OmInProcess.cpp
+
+endif # BUILD_IN_PROCESS_ENGINE
+
+
diff --git a/src/libs/engine/MidiControlNode.cpp b/src/libs/engine/MidiControlNode.cpp
new file mode 100644
index 00000000..2116c828
--- /dev/null
+++ b/src/libs/engine/MidiControlNode.cpp
@@ -0,0 +1,133 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "MidiControlNode.h"
+#include <math.h>
+#include "Om.h"
+#include "OmApp.h"
+#include "PostProcessor.h"
+#include "MidiLearnEvent.h"
+#include "InputPort.h"
+#include "OutputPort.h"
+#include "PortInfo.h"
+#include "Plugin.h"
+#include "util.h"
+#include "midi.h"
+
+
+namespace Om {
+
+
+MidiControlNode::MidiControlNode(const string& path, size_t poly, Patch* parent, samplerate srate, size_t buffer_size)
+: InternalNode(path, 1, parent, srate, buffer_size),
+ m_learning(false)
+{
+ m_num_ports = 7;
+ m_ports.alloc(m_num_ports);
+
+ m_midi_in_port = new InputPort<MidiMessage>(this, "MIDI In", 0, 1,
+ new PortInfo("MIDI In", MIDI, INPUT), m_buffer_size);
+ m_ports.at(0) = m_midi_in_port;
+
+ m_param_port = new InputPort<sample>(this, "Controller Number", 1, 1,
+ new PortInfo("Controller Number", CONTROL, INPUT, INTEGER, 60, 0, 127), 1);
+ m_ports.at(1) = m_param_port;
+ m_log_port = new InputPort<sample>(this, "Logarithmic", 2, 1,
+ new PortInfo("Logarithmic", CONTROL, INPUT, TOGGLE, 0, 0, 1), 1);
+ m_ports.at(2) = m_log_port;
+
+ m_min_port = new InputPort<sample>(this, "Min", 3, 1,
+ new PortInfo("Min", CONTROL, INPUT, NONE, 0, 0, 65535), 1);
+ m_ports.at(3) = m_min_port;
+
+ m_max_port = new InputPort<sample>(this, "Max", 4, 1,
+ new PortInfo("Max", CONTROL, INPUT, NONE, 1, 0, 65535), 1);
+ m_ports.at(4) = m_max_port;
+
+ m_audio_port = new OutputPort<sample>(this, "Out (AR)", 5, 1,
+ new PortInfo("Out (AR)", AUDIO, OUTPUT, 0, 0, 1), m_buffer_size);
+ m_ports.at(5) = m_audio_port;
+
+ m_control_port = new OutputPort<sample>(this, "Out (CR)", 6, 1,
+ new PortInfo("Out (CR)", CONTROL, OUTPUT, 0, 0, 1), 1);
+ m_ports.at(6) = m_control_port;
+
+ m_plugin.type(Plugin::Internal);
+ m_plugin.plug_label("midi_control_in");
+ m_plugin.name("Om Control Node (MIDI)");
+}
+
+
+void
+MidiControlNode::run(size_t nframes)
+{
+ InternalNode::run(nframes);
+
+ MidiMessage ev;
+
+ for (size_t i=0; i < m_midi_in_port->buffer(0)->filled_size(); ++i) {
+ ev = m_midi_in_port->buffer(0)->value_at(i);
+
+ if ((ev.buffer[0] & 0xF0) == MIDI_CMD_CONTROL)
+ control(ev.buffer[1], ev.buffer[2], ev.time);
+ }
+}
+
+
+void
+MidiControlNode::control(uchar control_num, uchar val, samplecount offset)
+{
+ assert(offset < m_buffer_size);
+
+ sample scaled_value;
+
+ const sample nval = (val / 127.0f); // normalized [0, 1]
+
+ if (m_learning) {
+ assert(m_learn_event != NULL);
+ m_param_port->set_value(control_num, offset);
+ assert(m_param_port->buffer(0)->value_at(0) == control_num);
+ m_learn_event->set_value(control_num);
+ m_learn_event->execute(offset);
+ om->post_processor()->push(m_learn_event);
+ om->post_processor()->signal();
+ m_learning = false;
+ m_learn_event = NULL;
+ }
+
+ if (m_log_port->buffer(0)->value_at(0) > 0.0f) {
+ // haaaaack, stupid negatives and logarithms
+ sample log_offset = 0;
+ if (m_min_port->buffer(0)->value_at(0) < 0)
+ log_offset = fabs(m_min_port->buffer(0)->value_at(0));
+ const sample min = log(m_min_port->buffer(0)->value_at(0)+1+log_offset);
+ const sample max = log(m_max_port->buffer(0)->value_at(0)+1+log_offset);
+ scaled_value = expf(nval * (max - min) + min) - 1 - log_offset;
+ } else {
+ const sample min = m_min_port->buffer(0)->value_at(0);
+ const sample max = m_max_port->buffer(0)->value_at(0);
+ scaled_value = ((nval) * (max - min)) + min;
+ }
+
+ if (control_num == m_param_port->buffer(0)->value_at(0)) {
+ m_control_port->set_value(scaled_value, offset);
+ m_audio_port->set_value(scaled_value, offset);
+ }
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/MidiControlNode.h b/src/libs/engine/MidiControlNode.h
new file mode 100644
index 00000000..b40e7631
--- /dev/null
+++ b/src/libs/engine/MidiControlNode.h
@@ -0,0 +1,72 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef MIDICONTROLNODE_H
+#define MIDICONTROLNODE_H
+
+#include <string>
+#include "NodeBase.h"
+#include "InternalNode.h"
+using std::string;
+
+namespace Om {
+
+class MidiLearnResponseEvent;
+class MidiMessage;
+template <typename T> class InputPort;
+template <typename T> class OutputPort;
+
+
+/** MIDI control input node.
+ *
+ * Creating one of these nodes is how a user makes "MIDI Bindings". Note that
+ * this node will always be monophonic, the poly parameter is ignored.
+ *
+ * \ingroup engine
+ */
+class MidiControlNode : public InternalNode
+{
+public:
+ MidiControlNode(const string& path, size_t poly, Patch* parent, samplerate srate, size_t buffer_size);
+
+ void run(size_t nframes);
+
+ void control(uchar control_num, uchar val, samplecount offset);
+
+ void learn(MidiLearnResponseEvent* ev) { m_learning = true; m_learn_event = ev; }
+
+private:
+ // Disallow copies (undefined)
+ MidiControlNode(const MidiControlNode& copy);
+ MidiControlNode& operator=(const MidiControlNode&);
+
+ bool m_learning;
+
+ InputPort<MidiMessage>* m_midi_in_port;
+ InputPort<sample>* m_param_port;
+ InputPort<sample>* m_log_port;
+ InputPort<sample>* m_min_port;
+ InputPort<sample>* m_max_port;
+ OutputPort<sample>* m_control_port;
+ OutputPort<sample>* m_audio_port;
+
+ MidiLearnResponseEvent* m_learn_event;
+};
+
+
+} // namespace Om
+
+#endif // MIDICONTROLNODE_H
diff --git a/src/libs/engine/MidiDriver.h b/src/libs/engine/MidiDriver.h
new file mode 100644
index 00000000..b8fd71ca
--- /dev/null
+++ b/src/libs/engine/MidiDriver.h
@@ -0,0 +1,78 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef MIDIDRIVER_H
+#define MIDIDRIVER_H
+
+#include "Driver.h"
+#include <iostream>
+using std::cout; using std::endl;
+
+namespace Om {
+
+class MidiMessage;
+
+
+/** Midi driver abstract base class.
+ *
+ * \ingroup engine
+ */
+class MidiDriver : public Driver<MidiMessage>
+{
+public:
+ /** Prepare events (however neccessary) for the specified block (realtime safe) */
+ virtual void prepare_block(const samplecount block_start, const samplecount block_end) = 0;
+};
+
+
+
+/** Dummy MIDIDriver.
+ *
+ * Not abstract, all functions are dummies. One of these will be allocated and
+ * "used" if no working MIDI driver is loaded. (Doing it this way as opposed to
+ * just making MidiDriver have dummy functions makes sure any existing MidiDriver
+ * derived class actually implements the required functions).
+ *
+ * \ingroup engine
+ */
+class DummyMidiDriver : public MidiDriver
+{
+public:
+ DummyMidiDriver() {
+ cout << "[DummyMidiDriver] Started Dummy MIDI driver." << endl;
+ }
+
+ ~DummyMidiDriver() {}
+
+ void activate() {}
+ void deactivate() {}
+
+ bool is_activated() const { return false; }
+ bool is_enabled() const { return false; }
+
+ void enable() {}
+ void disable() {}
+
+ DriverPort* create_port(PortBase<MidiMessage>* patch_port) { return NULL; }
+
+ void prepare_block(const samplecount block_start, const samplecount block_end) {}
+};
+
+
+
+} // namespace Om
+
+#endif // MIDIDRIVER_H
diff --git a/src/libs/engine/MidiInputNode.cpp b/src/libs/engine/MidiInputNode.cpp
new file mode 100644
index 00000000..25310807
--- /dev/null
+++ b/src/libs/engine/MidiInputNode.cpp
@@ -0,0 +1,49 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "MidiInputNode.h"
+#include "InputPort.h"
+#include "OutputPort.h"
+#include "Plugin.h"
+#include "PortInfo.h"
+#include "Patch.h"
+#include "MidiMessage.h"
+
+namespace Om {
+
+
+MidiInputNode::MidiInputNode(const string& path, size_t poly, Patch* parent, samplerate srate, size_t buffer_size)
+: BridgeNode<MidiMessage>(path, 1, parent, srate, buffer_size)
+{
+ OutputPort<MidiMessage>* internal_port = new OutputPort<MidiMessage>(this, "in", 0, m_poly,
+ new PortInfo("in", MIDI, OUTPUT), m_buffer_size);
+ InputPort<MidiMessage>* external_port = new InputPort<MidiMessage>(parent, m_name, 0, m_poly,
+ new PortInfo(m_name, MIDI, INPUT), m_buffer_size);
+ external_port->tie(internal_port);
+ m_external_port = external_port;
+
+ m_num_ports = 1;
+ m_ports.alloc(m_num_ports);
+ m_ports.at(0) = internal_port;
+
+ m_plugin.type(Plugin::Internal);
+ m_plugin.plug_label("midi_input");
+ m_plugin.name("Om Patch MIDI Input Node");
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/MidiInputNode.h b/src/libs/engine/MidiInputNode.h
new file mode 100644
index 00000000..0986a267
--- /dev/null
+++ b/src/libs/engine/MidiInputNode.h
@@ -0,0 +1,43 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef MIDIINPUTNODE_H
+#define MIDIINPUTNODE_H
+
+#include <string>
+#include "BridgeNode.h"
+
+using std::string;
+
+namespace Om {
+
+class MidiMessage;
+
+
+/** MIDI input BridgeNode.
+ *
+ * \ingroup engine
+ */
+class MidiInputNode : public BridgeNode<MidiMessage>
+{
+public:
+ MidiInputNode(const string& path, size_t poly, Patch* parent, samplerate srate, size_t buffer_size);
+};
+
+
+} // namespace Om
+
+#endif // MIDIINPUTNODE_H
diff --git a/src/libs/engine/MidiMessage.h b/src/libs/engine/MidiMessage.h
new file mode 100644
index 00000000..8dcb3e67
--- /dev/null
+++ b/src/libs/engine/MidiMessage.h
@@ -0,0 +1,51 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef MIDIMESSAGE_H
+#define MIDIMESSAGE_H
+
+namespace Om {
+
+
+/** Midi Message (the type a MIDI port connects to).
+ *
+ * For the time being (ie while it's still possible) this is binary
+ * compatible with jack_default_midi_event_t. Don't mess that up without
+ * dealing with all the repercussions (in the MidiDriver's).
+ *
+ * Note that with the current implementation one of these is NOT valid
+ * across process cycles (since the buffer is just a pointer to the bufferr
+ * given to us by Jack itself. In other words, if one of these needs to be
+ * stored, it needs to be deep copied. Less copying anyway.. :/
+ */
+struct MidiMessage
+{
+public:
+ MidiMessage() : time(0), size(0), buffer(NULL) {}
+
+ // Really just to allow setting to zero for generic algos
+ MidiMessage(const int& i) : time(0), size(0), buffer(NULL) {}
+
+ samplecount time;
+ size_t size;
+ unsigned char* buffer;
+};
+
+
+} // namespace Om
+
+
+#endif // MIDIMESSAGE_H
diff --git a/src/libs/engine/MidiNoteNode.cpp b/src/libs/engine/MidiNoteNode.cpp
new file mode 100644
index 00000000..9dc32f4a
--- /dev/null
+++ b/src/libs/engine/MidiNoteNode.cpp
@@ -0,0 +1,304 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "MidiNoteNode.h"
+#include <cmath>
+#include <iostream>
+#include "MidiMessage.h"
+#include "InputPort.h"
+#include "OutputPort.h"
+#include "PortInfo.h"
+#include "Plugin.h"
+#include "Array.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "AudioDriver.h"
+#include "util.h"
+#include "midi.h"
+
+using std::cerr; using std::cout; using std::endl;
+
+
+namespace Om {
+
+
+MidiNoteNode::MidiNoteNode(const string& path, size_t poly, Patch* parent, samplerate srate, size_t buffer_size)
+: InternalNode(path, poly, parent, srate, buffer_size),
+ m_voices(new Voice[poly]),
+ m_sustain(false)
+{
+ m_num_ports = 5;
+ m_ports.alloc(m_num_ports);
+
+ m_midi_in_port = new InputPort<MidiMessage>(this, "MIDI In", 0, 1,
+ new PortInfo("MIDI In", MIDI, INPUT), m_buffer_size);
+ m_ports.at(0) = m_midi_in_port;
+
+ m_freq_port = new OutputPort<sample>(this, "Frequency", 1, poly,
+ new PortInfo("Frequency", AUDIO, OUTPUT, 440, 0, 99999), m_buffer_size);
+ m_ports.at(1) = m_freq_port;
+
+ m_vel_port = new OutputPort<sample>(this, "Velocity", 2, poly,
+ new PortInfo("Velocity", AUDIO, OUTPUT, 0, 0, 1), m_buffer_size);
+ m_ports.at(2) = m_vel_port;
+
+ m_gate_port = new OutputPort<sample>(this, "Gate", 3, poly,
+ new PortInfo("Gate", AUDIO, OUTPUT, 0, 0, 1), m_buffer_size);
+ m_ports.at(3) = m_gate_port;
+
+ m_trig_port = new OutputPort<sample>(this, "Trigger", 4, poly,
+ new PortInfo("Trigger", AUDIO, OUTPUT, 0, 0, 1), m_buffer_size);
+ m_ports.at(4) = m_trig_port;
+
+ m_plugin.type(Plugin::Internal);
+ m_plugin.plug_label("note_in");
+ m_plugin.name("Om Note Node (MIDI, OSC)");
+}
+
+
+MidiNoteNode::~MidiNoteNode()
+{
+ delete[] m_voices;
+}
+
+
+void
+MidiNoteNode::run(size_t nframes)
+{
+ InternalNode::run(nframes);
+
+ MidiMessage ev;
+
+ for (size_t i=0; i < m_midi_in_port->buffer(0)->filled_size(); ++i) {
+ ev = m_midi_in_port->buffer(0)->value_at(i);
+
+ switch (ev.buffer[0] & 0xF0) {
+ case MIDI_CMD_NOTE_ON:
+ if (ev.buffer[2] == 0)
+ note_off(ev.buffer[1], ev.time);
+ else
+ note_on(ev.buffer[1], ev.buffer[2], ev.time);
+ break;
+ case MIDI_CMD_NOTE_OFF:
+ note_off(ev.buffer[1], ev.time);
+ break;
+ case MIDI_CMD_CONTROL:
+ switch (ev.buffer[1]) {
+ case MIDI_CTL_ALL_NOTES_OFF:
+ case MIDI_CTL_ALL_SOUNDS_OFF:
+ all_notes_off(ev.time);
+ break;
+ case MIDI_CTL_SUSTAIN:
+ if (ev.buffer[2] > 63)
+ sustain_on();
+ else
+ sustain_off(ev.time);
+ break;
+ case MIDI_CMD_BENDER:
+
+ break;
+ }
+ default:
+ break;
+ }
+ }
+}
+
+
+void
+MidiNoteNode::note_on(uchar note_num, uchar velocity, samplecount offset)
+{
+ const jack_nframes_t time_stamp = om->audio_driver()->time_stamp();
+
+ assert(offset < m_buffer_size);
+ assert(note_num <= 127);
+
+ Key* key = &m_keys[note_num];
+ Voice* voice = NULL;
+ size_t voice_num = 0;
+
+ // Look for free voices
+ for (size_t i=0; i < m_poly; ++i) {
+ if (m_voices[i].state == Voice::Voice::FREE) {
+ voice = &m_voices[i];
+ voice_num = i;
+ break;
+ }
+ }
+
+ // If we didn't find a free one, steal the oldest
+ if (voice == NULL) {
+ voice_num = 0;
+ voice = &m_voices[0];
+ jack_nframes_t oldest_time = m_voices[0].time;
+ for (size_t i=1; i < m_poly; ++i) {
+ if (m_voices[i].time < oldest_time) {
+ voice = &m_voices[i];
+ voice_num = i;
+ oldest_time = voice->time;
+ }
+ }
+ }
+ assert(voice != NULL);
+ assert(voice == &m_voices[voice_num]);
+
+ //cerr << "[MidiNoteNode] Note on. Key " << (int)note_num << ", Voice " << voice_num << endl;
+
+ // Update stolen key, if applicable
+ if (voice->state == Voice::Voice::ACTIVE) {
+ assert(m_keys[voice->note].voice == voice_num);
+ assert(m_keys[voice->note].state == Key::ON_ASSIGNED);
+ m_keys[voice->note].state = Key::Key::ON_UNASSIGNED;
+ //cerr << "[MidiNoteNode] Stole voice " << voice_num << endl;
+ }
+
+ // Store key information for later reallocation on note off
+ key->state = Key::Key::ON_ASSIGNED;
+ key->voice = voice_num;
+ key->time = time_stamp;
+
+ // Trigger voice
+ voice->state = Voice::Voice::ACTIVE;
+ voice->note = note_num;
+ voice->time = time_stamp;
+
+ assert(m_keys[voice->note].state == Key::Key::ON_ASSIGNED);
+ assert(m_keys[voice->note].voice == voice_num);
+
+ // one-sample jitter hack to avoid having to deal with trigger sample "next time"
+ if (offset == (samplecount)(m_buffer_size-1))
+ --offset;
+
+ m_freq_port->buffer(voice_num)->set(note_to_freq(note_num), offset);
+ m_vel_port->buffer(voice_num)->set(velocity/127.0, offset);
+ m_gate_port->buffer(voice_num)->set(1.0f, offset);
+
+ // trigger (one sample)
+ m_trig_port->buffer(voice_num)->set(1.0f, offset, offset);
+ m_trig_port->buffer(voice_num)->set(0.0f, offset+1);
+
+ assert(key->state == Key::Key::ON_ASSIGNED);
+ assert(voice->state == Voice::Voice::ACTIVE);
+ assert(key->voice == voice_num);
+ assert(m_voices[key->voice].note == note_num);
+}
+
+
+void
+MidiNoteNode::note_off(uchar note_num, samplecount offset)
+{
+ assert(offset < m_buffer_size);
+
+ Key* key = &m_keys[note_num];
+
+ if (key->state == Key::ON_ASSIGNED) {
+ // Assigned key, turn off voice and key
+ assert(m_voices[key->voice].state == Voice::ACTIVE);
+ assert(m_voices[key->voice].note == note_num);
+ key->state = Key::OFF;
+
+ if ( ! m_sustain)
+ free_voice(key->voice, offset);
+ else
+ m_voices[key->voice].state = Voice::HOLDING;
+ }
+
+ key->state = Key::OFF;
+}
+
+
+void
+MidiNoteNode::free_voice(size_t voice, samplecount offset)
+{
+ // Find a key to reassign to the freed voice (the newest, if there is one)
+ Key* replace_key = NULL;
+ uchar replace_key_num = 0;
+
+ for (uchar i = 0; i <= 127; ++i) {
+ if (m_keys[i].state == Key::ON_UNASSIGNED) {
+ if (replace_key == NULL || m_keys[i].time > replace_key->time) {
+ replace_key = &m_keys[i];
+ replace_key_num = i;
+ }
+ }
+ }
+
+ if (replace_key != NULL) { // Found a key to assign to freed voice
+ assert(&m_keys[replace_key_num] == replace_key);
+ assert(replace_key->state == Key::ON_UNASSIGNED);
+
+ // Change the freq but leave the gate high and don't retrigger
+ m_freq_port->buffer(voice)->set(note_to_freq(replace_key_num), offset);
+
+ replace_key->state = Key::ON_ASSIGNED;
+ replace_key->voice = voice;
+ m_keys[m_voices[voice].note].state = Key::ON_UNASSIGNED;
+ m_voices[voice].note = replace_key_num;
+ m_voices[voice].state = Voice::ACTIVE;
+ } else {
+ // No new note for voice, deactivate (set gate low)
+ //cerr << "[MidiNoteNode] Note off. Key " << (int)note_num << ", Voice " << voice << " Killed" << endl;
+ m_gate_port->buffer(voice)->set(0.0f, offset);
+ m_voices[voice].state = Voice::FREE;
+ }
+}
+
+
+void
+MidiNoteNode::all_notes_off(samplecount offset)
+{
+ //cerr << "Note off starting at sample " << offset << endl;
+ assert(offset < m_buffer_size);
+
+ // FIXME: set all keys to Key::OFF?
+
+ for (size_t i=0; i < m_poly; ++i) {
+ m_gate_port->buffer(i)->set(0.0f, offset);
+ m_voices[i].state = Voice::FREE;
+ }
+}
+
+
+float
+MidiNoteNode::note_to_freq(int num)
+{
+ static const float A4 = 440.0f;
+ if (num >= 0 && num <= 119)
+ return A4 * powf(2.0f, (float)(num - 57.0f) / 12.0f);
+ return 1.0f; // Some LADSPA plugins don't like freq=0
+}
+
+
+void
+MidiNoteNode::sustain_on()
+{
+ m_sustain = true;
+}
+
+
+void
+MidiNoteNode::sustain_off(samplecount offset)
+{
+ m_sustain = false;
+
+ for (size_t i=0; i < m_poly; ++i)
+ if (m_voices[i].state == Voice::HOLDING)
+ free_voice(i, offset);
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/MidiNoteNode.h b/src/libs/engine/MidiNoteNode.h
new file mode 100644
index 00000000..bf302144
--- /dev/null
+++ b/src/libs/engine/MidiNoteNode.h
@@ -0,0 +1,87 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef MIDINOTENODE_H
+#define MIDINOTENODE_H
+
+#include <string>
+#include "InternalNode.h"
+#include "util/types.h"
+
+using std::string;
+
+namespace Om {
+
+class MidiMessage;
+template <typename T> class InputPort;
+template <typename T> class OutputPort;
+
+
+/** MIDI note input node.
+ *
+ * For pitched instruments like keyboard, etc.
+ *
+ * \ingroup engine
+ */
+class MidiNoteNode : public InternalNode
+{
+public:
+ MidiNoteNode(const string& path, size_t poly, Patch* parent, samplerate srate, size_t buffer_size);
+ ~MidiNoteNode();
+
+ void run(size_t nframes);
+
+ void note_on(uchar note_num, uchar velocity, samplecount offset);
+ void note_off(uchar note_num, samplecount offset);
+ void all_notes_off(samplecount offset);
+
+ void sustain_on();
+ void sustain_off(samplecount offset);
+
+private:
+
+ /** Key, one for each key on the keyboard */
+ struct Key {
+ enum State { OFF, ON_ASSIGNED, ON_UNASSIGNED };
+ Key() : state(OFF), voice(0), time(0) {}
+ State state; size_t voice; samplecount time;
+ };
+
+ /** Voice, one of these always exists for each voice */
+ struct Voice {
+ enum State { FREE, ACTIVE, HOLDING };
+ Voice() : state(FREE), note(0) {}
+ State state; uchar note; samplecount time;
+ };
+
+ float note_to_freq(int num);
+ void free_voice(size_t voice, samplecount offset);
+
+ Voice* m_voices;
+ Key m_keys[128];
+ bool m_sustain; ///< Whether or not hold pedal is depressed
+
+ InputPort<MidiMessage>* m_midi_in_port;
+ OutputPort<sample>* m_freq_port;
+ OutputPort<sample>* m_vel_port;
+ OutputPort<sample>* m_gate_port;
+ OutputPort<sample>* m_trig_port;
+};
+
+
+} // namespace Om
+
+#endif // MIDINOTENODE_H
diff --git a/src/libs/engine/MidiOutputNode.cpp b/src/libs/engine/MidiOutputNode.cpp
new file mode 100644
index 00000000..33023cc6
--- /dev/null
+++ b/src/libs/engine/MidiOutputNode.cpp
@@ -0,0 +1,49 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "MidiOutputNode.h"
+#include "InputPort.h"
+#include "OutputPort.h"
+#include "Plugin.h"
+#include "PortInfo.h"
+#include "Patch.h"
+#include "MidiMessage.h"
+
+namespace Om {
+
+
+MidiOutputNode::MidiOutputNode(const string& path, size_t poly, Patch* parent, samplerate srate, size_t buffer_size)
+: BridgeNode<MidiMessage>(path, 1, parent, srate, buffer_size)
+{
+ OutputPort<MidiMessage>* external_port = new OutputPort<MidiMessage>(parent, m_name, 0, m_poly,
+ new PortInfo(m_name, MIDI, OUTPUT), m_buffer_size);
+ InputPort<MidiMessage>* internal_port = new InputPort<MidiMessage>(this, "out", 0, m_poly,
+ new PortInfo("out", MIDI, INPUT), m_buffer_size);
+ internal_port->tie(external_port);
+ m_external_port = external_port;
+
+ m_num_ports = 1;
+ m_ports.alloc(m_num_ports);
+ m_ports.at(0) = internal_port;
+
+ m_plugin.type(Plugin::Internal);
+ m_plugin.plug_label("midi_output");
+ m_plugin.name("Om Patch MIDI Output Node");
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/MidiOutputNode.h b/src/libs/engine/MidiOutputNode.h
new file mode 100644
index 00000000..06d8a892
--- /dev/null
+++ b/src/libs/engine/MidiOutputNode.h
@@ -0,0 +1,43 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef MIDIOUTPUTNODE_H
+#define MIDIOUTPUTNODE_H
+
+#include <string>
+#include "BridgeNode.h"
+
+using std::string;
+
+namespace Om {
+
+class MidiMessage;
+
+
+/** MIDI output BridgeNode.
+ *
+ * \ingroup engine
+ */
+class MidiOutputNode : public BridgeNode<MidiMessage>
+{
+public:
+ MidiOutputNode(const string& path, size_t poly, Patch* parent, samplerate srate, size_t buffer_size);
+};
+
+
+} // namespace Om
+
+#endif // MIDIOUTPUTNODE_H
diff --git a/src/libs/engine/MidiTriggerNode.cpp b/src/libs/engine/MidiTriggerNode.cpp
new file mode 100644
index 00000000..4fda80e7
--- /dev/null
+++ b/src/libs/engine/MidiTriggerNode.cpp
@@ -0,0 +1,124 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "MidiTriggerNode.h"
+#include <cmath>
+#include "InputPort.h"
+#include "OutputPort.h"
+#include "PortInfo.h"
+#include "Plugin.h"
+#include "util.h"
+#include "midi.h"
+
+namespace Om {
+
+
+MidiTriggerNode::MidiTriggerNode(const string& path, size_t poly, Patch* parent, samplerate srate, size_t buffer_size)
+: InternalNode(path, 1, parent, srate, buffer_size)
+{
+ m_num_ports = 5;
+ m_ports.alloc(m_num_ports);
+
+ m_midi_in_port = new InputPort<MidiMessage>(this, "MIDI In", 0, 1,
+ new PortInfo("MIDI In", MIDI, INPUT), m_buffer_size);
+ m_ports.at(0) = m_midi_in_port;
+
+ m_note_port = new InputPort<sample>(this, "Note Number", 1, 1,
+ new PortInfo("Note Number", CONTROL, INPUT, INTEGER, 60, 0, 127), 1);
+ m_ports.at(1) = m_note_port;
+
+ m_gate_port = new OutputPort<sample>(this, "Gate", 2, 1,
+ new PortInfo("Gate", AUDIO, OUTPUT, 0, 0, 1), m_buffer_size);
+ m_ports.at(2) = m_gate_port;
+
+ m_trig_port = new OutputPort<sample>(this, "Trigger", 3, 1,
+ new PortInfo("Trigger", AUDIO, OUTPUT, 0, 0, 1), m_buffer_size);
+ m_ports.at(3) = m_trig_port;
+
+ m_vel_port = new OutputPort<sample>(this, "Velocity", 4, poly,
+ new PortInfo("Velocity", AUDIO, OUTPUT, 0, 0, 1), m_buffer_size);
+ m_ports.at(4) = m_vel_port;
+
+ m_plugin.type(Plugin::Internal);
+ m_plugin.plug_label("trigger_in");
+ m_plugin.name("Om Trigger Node (MIDI, OSC)");
+}
+
+
+void
+MidiTriggerNode::run(size_t nframes)
+{
+ InternalNode::run(nframes);
+
+ MidiMessage ev;
+
+ for (size_t i=0; i < m_midi_in_port->buffer(0)->filled_size(); ++i) {
+ ev = m_midi_in_port->buffer(0)->value_at(i);
+
+ switch (ev.buffer[0] & 0xF0) {
+ case MIDI_CMD_NOTE_ON:
+ if (ev.buffer[2] == 0)
+ note_off(ev.buffer[1], ev.time);
+ else
+ note_on(ev.buffer[1], ev.buffer[2], ev.time);
+ break;
+ case MIDI_CMD_NOTE_OFF:
+ note_off(ev.buffer[1], ev.time);
+ break;
+ case MIDI_CMD_CONTROL:
+ if (ev.buffer[1] == MIDI_CTL_ALL_NOTES_OFF
+ || ev.buffer[1] == MIDI_CTL_ALL_SOUNDS_OFF)
+ m_gate_port->buffer(0)->set(0.0f, ev.time);
+ default:
+ break;
+ }
+ }
+}
+
+
+void
+MidiTriggerNode::note_on(uchar note_num, uchar velocity, samplecount offset)
+{
+ //std::cerr << "Note on starting at sample " << offset << std::endl;
+ assert(offset < m_buffer_size);
+
+ const sample filter_note = m_note_port->buffer(0)->value_at(0);
+ if (filter_note >= 0.0 && filter_note < 127.0 && (note_num == (uchar)filter_note)){
+
+ // See comments in MidiNoteNode::note_on (FIXME)
+ if (offset == (samplecount)(m_buffer_size-1))
+ --offset;
+
+ m_gate_port->buffer(0)->set(1.0f, offset);
+ m_trig_port->buffer(0)->set(1.0f, offset, offset);
+ m_trig_port->buffer(0)->set(0.0f, offset+1);
+ m_vel_port->buffer(0)->set(velocity/127.0f, offset);
+ }
+}
+
+
+void
+MidiTriggerNode::note_off(uchar note_num, samplecount offset)
+{
+ assert(offset < m_buffer_size);
+
+ if (note_num == lrintf(m_note_port->buffer(0)->value_at(0)))
+ m_gate_port->buffer(0)->set(0.0f, offset);
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/MidiTriggerNode.h b/src/libs/engine/MidiTriggerNode.h
new file mode 100644
index 00000000..2f91c631
--- /dev/null
+++ b/src/libs/engine/MidiTriggerNode.h
@@ -0,0 +1,64 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef MIDITRIGGERNODE_H
+#define MIDITRIGGERNODE_H
+
+#include <string>
+#include "InternalNode.h"
+
+using std::string;
+
+namespace Om {
+
+class MidiMessage;
+template <typename T> class InputPort;
+template <typename T> class OutputPort;
+
+
+/** MIDI trigger input node.
+ *
+ * Just has a gate, for drums etc. A control port is used to select
+ * which note number is responded to.
+ *
+ * Note that this node is always monophonic, the poly parameter is ignored.
+ * (Should that change?)
+ *
+ * \ingroup engine
+ */
+class MidiTriggerNode : public InternalNode
+{
+public:
+ MidiTriggerNode(const string& path, size_t poly, Patch* parent, samplerate srate, size_t buffer_size);
+
+ void run(size_t nframes);
+
+ void note_on(uchar note_num, uchar velocity, samplecount offset);
+ void note_off(uchar note_num, samplecount offset);
+
+private:
+ InputPort<MidiMessage>* m_midi_in_port;
+ InputPort<sample>* m_note_port;
+ OutputPort<sample>* m_gate_port;
+ OutputPort<sample>* m_trig_port;
+ OutputPort<sample>* m_vel_port;
+};
+
+
+} // namespace Om
+
+#endif // MIDITRIGGERNODE_H
diff --git a/src/libs/engine/Node.h b/src/libs/engine/Node.h
new file mode 100644
index 00000000..d169e772
--- /dev/null
+++ b/src/libs/engine/Node.h
@@ -0,0 +1,120 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef NODE_H
+#define NODE_H
+
+#include <string>
+#include "util/types.h"
+#include "OmObject.h"
+#include "Array.h"
+
+using std::string;
+
+template <typename T> class List;
+
+namespace Om {
+
+class Port;
+template <typename T> class OutputPort;
+class Plugin;
+class Patch;
+namespace Shared {
+ class ClientInterface;
+}
+
+
+/** A Node (or "module") in a Patch (which is also a Node).
+ *
+ * A Node is a unit with input/output ports, a run() method, and some other
+ * things.
+ *
+ * This is a pure abstract base class for any Node, it contains no
+ * implementation details/data whatsoever. This is the interface you need to
+ * implement to add a new Node type to Om.
+ *
+ * \ingroup engine
+ */
+class Node : public OmObject
+{
+public:
+ Node(OmObject* parent, const string& name) : OmObject(parent, name) {}
+ virtual ~Node() {}
+
+ Node* as_node() { return static_cast<Node*>(this); }
+
+ /** Activate this Node.
+ *
+ * This function will be called in a non-realtime thread before it is
+ * inserted in to a patch. Any non-realtime actions that need to be
+ * done before the Node is ready for use should be done here.
+ */
+ virtual void activate() = 0;
+ virtual void deactivate() = 0;
+ virtual bool activated() = 0;
+
+ virtual void run(size_t nframes) = 0;
+
+ virtual void set_port_buffer(size_t voice, size_t port_num, void* buf) = 0;
+
+ // FIXME: Only used by client senders. Remove?
+ virtual const Array<Port*>& ports() const = 0;
+
+ virtual size_t num_ports() const = 0;
+ virtual size_t poly() const = 0;
+
+ /** Used by the process order finding algorithm (ie during connections) */
+ virtual bool traversed() const = 0;
+ virtual void traversed(bool b) = 0;
+
+ /** Nodes that are connected to this Node's inputs.
+ * (This Node depends on them)
+ */
+ virtual List<Node*>* providers() = 0;
+ virtual void providers(List<Node*>* l) = 0;
+
+ /** Nodes are are connected to this Node's outputs.
+ * (They depend on this Node)
+ */
+ virtual List<Node*>* dependants() = 0;
+ virtual void dependants(List<Node*>* l) = 0;
+
+ /** The Patch this Node belongs to. */
+ virtual Patch* parent_patch() const = 0;
+
+ /** Information about what 'plugin' this Node is an instance of.
+ * Not the best name - not all nodes come from plugins (ie Patch)
+ */
+ virtual const Plugin* plugin() const = 0;
+ virtual void plugin(const Plugin* const pi) = 0;
+
+ /** Add self to a Patch.
+ *
+ * This function must be realtime-safe! Any non-realtime actions that
+ * need to be done before adding to a patch can be done in activate().
+ */
+ virtual void add_to_patch() = 0;
+
+ virtual void remove_from_patch() = 0;
+
+ /** Send any necessary notification to client on node creation. */
+ //virtual void send_creation_messages(Shared::ClientInterface* client) const = 0;
+};
+
+
+} // namespace Om
+
+#endif // NODE_H
diff --git a/src/libs/engine/NodeBase.cpp b/src/libs/engine/NodeBase.cpp
new file mode 100644
index 00000000..b1628539
--- /dev/null
+++ b/src/libs/engine/NodeBase.cpp
@@ -0,0 +1,171 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "NodeBase.h"
+#include <cassert>
+#include <iostream>
+#include <stdint.h>
+#include "Om.h"
+#include "OmApp.h"
+#include "util.h"
+#include "Array.h"
+#include "Plugin.h"
+#include "ClientBroadcaster.h"
+#include "Port.h"
+#include "List.h"
+#include "Patch.h"
+#include "ObjectStore.h"
+
+using std::cout; using std::cerr; using std::endl;
+
+namespace Om {
+
+
+NodeBase::NodeBase(const string& name, size_t poly, Patch* parent, samplerate srate, size_t buffer_size)
+: Node(parent, name),
+ m_poly(poly),
+ m_srate(srate),
+ m_buffer_size(buffer_size),
+ m_activated(false),
+ m_num_ports(0),
+ m_traversed(false),
+ m_providers(new List<Node*>()),
+ m_dependants(new List<Node*>())
+{
+ assert(m_poly > 0);
+ assert(m_parent == NULL || (m_poly == parent->internal_poly() || m_poly == 1));
+}
+
+
+NodeBase::~NodeBase()
+{
+ assert(!m_activated);
+
+ delete m_providers;
+ delete m_dependants;
+
+ for (size_t i=0; i < m_ports.size(); ++i)
+ delete m_ports.at(i);
+}
+
+
+void
+NodeBase::activate()
+{
+ assert(!m_activated);
+ m_activated = true;
+}
+
+
+void
+NodeBase::deactivate()
+{
+ assert(m_activated);
+ m_activated = false;
+}
+
+
+/*
+void
+NodeBase::send_creation_messages(ClientInterface* client) const
+{
+ cerr << "FIXME: send_creation\n";
+ //om->client_broadcaster()->send_node_to(client, this);
+}
+*/
+
+void
+NodeBase::add_to_store()
+{
+ om->object_store()->add(this);
+ for (size_t i=0; i < num_ports(); ++i)
+ om->object_store()->add(m_ports.at(i));
+}
+
+
+void
+NodeBase::remove_from_store()
+{
+ // Remove self
+ TreeNode<OmObject*>* node = om->object_store()->remove(path());
+ if (node != NULL) {
+ assert(om->object_store()->find(path()) == NULL);
+ delete node;
+ }
+
+ // Remove ports
+ for (size_t i=0; i < m_num_ports; ++i) {
+ node = om->object_store()->remove(m_ports.at(i)->path());
+ if (node != NULL) {
+ assert(om->object_store()->find(m_ports.at(i)->path()) == NULL);
+ delete node;
+ }
+ }
+}
+
+
+/** Runs the Node for the specified number of frames (block size)
+ */
+void
+NodeBase::run(size_t nframes)
+{
+ assert(m_activated);
+ // Mix down any ports with multiple inputs
+ Port* p;
+ for (size_t i=0; i < m_ports.size(); ++i) {
+ p = m_ports.at(i);
+ p->prepare_buffers(nframes);
+ }
+}
+
+
+/** Rename this Node.
+ *
+ * This is responsible for updating the ObjectStore so the Node can be
+ * found at it's new path, as well as all it's children.
+ */
+void
+NodeBase::set_path(const Path& new_path)
+{
+ const Path old_path = path();
+ //cerr << "Renaming " << old_path << " -> " << new_path << endl;
+
+ TreeNode<OmObject*>* treenode = NULL;
+
+ // Reinsert ports
+ for (size_t i=0; i < m_num_ports; ++i) {
+ treenode = om->object_store()->remove(old_path +"/"+ m_ports.at(i)->name());
+ assert(treenode != NULL);
+ assert(treenode->node() == m_ports.at(i));
+ treenode->key(new_path +"/" + m_ports.at(i)->name());
+ om->object_store()->add(treenode);
+ }
+
+ // Rename and reinsert self
+ treenode = om->object_store()->remove(old_path);
+ assert(treenode != NULL);
+ assert(treenode->node() == this);
+ OmObject::set_path(new_path);
+ treenode->key(new_path);
+ om->object_store()->add(treenode);
+
+
+ assert(om->object_store()->find(new_path) == this);
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/NodeBase.h b/src/libs/engine/NodeBase.h
new file mode 100644
index 00000000..796abbca
--- /dev/null
+++ b/src/libs/engine/NodeBase.h
@@ -0,0 +1,105 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef NODEBASE_H
+#define NODEBASE_H
+
+#include <string>
+#include <cstdlib>
+#include "Node.h"
+using std::string;
+
+namespace Om {
+
+class Plugin;
+class Patch;
+namespace Shared {
+ class ClientInterface;
+} using Shared::ClientInterface;
+
+
+/** Common implementation stuff for Node.
+ *
+ * Pretty much just attributes and getters/setters are here.
+ *
+ * \ingroup engine
+ */
+class NodeBase : public Node
+{
+public:
+ NodeBase(const string& name, size_t poly, Patch* parent, samplerate srate, size_t buffer_size);
+
+ virtual ~NodeBase();
+
+ virtual void activate();
+ virtual void deactivate();
+ bool activated() { return m_activated; }
+
+ virtual void run(size_t nframes);
+
+ virtual void set_port_buffer(size_t voice, size_t port_num, void* buf) {}
+
+ virtual void add_to_patch() {}
+ virtual void remove_from_patch() {}
+
+ void add_to_store();
+ void remove_from_store();
+
+ //void send_creation_messages(ClientInterface* client) const;
+
+ size_t num_ports() const { return m_num_ports; }
+ size_t poly() const { return m_poly; }
+ bool traversed() const { return m_traversed; }
+ void traversed(bool b) { m_traversed = b; }
+
+ const Array<Port*>& ports() const { return m_ports; }
+
+ virtual List<Node*>* providers() { return m_providers; }
+ virtual void providers(List<Node*>* l) { m_providers = l; }
+
+ virtual List<Node*>* dependants() { return m_dependants; }
+ virtual void dependants(List<Node*>* l) { m_dependants = l; }
+
+ Patch* parent_patch() const { return (m_parent == NULL) ? NULL : m_parent->as_patch(); }
+
+ virtual const Plugin* plugin() const { exit(EXIT_FAILURE); }
+ virtual void plugin(const Plugin* const pi) { exit(EXIT_FAILURE); }
+
+ void set_path(const Path& new_path);
+
+protected:
+ // Disallow copies (undefined)
+ NodeBase(const NodeBase&);
+ NodeBase& operator=(const NodeBase&);
+
+ size_t m_poly;
+
+ samplerate m_srate;
+ size_t m_buffer_size;
+ bool m_activated;
+
+ size_t m_num_ports; // number of ports PER VOICE
+ Array<Port*> m_ports;
+
+ bool m_traversed;
+ List<Node*>* m_providers; // Nodes connected to this one's input ports
+ List<Node*>* m_dependants; // Nodes this one's output ports are connected to
+};
+
+
+} // namespace Om
+
+#endif // NODEBASE_H
diff --git a/src/libs/engine/NodeFactory.cpp b/src/libs/engine/NodeFactory.cpp
new file mode 100644
index 00000000..176d3f47
--- /dev/null
+++ b/src/libs/engine/NodeFactory.cpp
@@ -0,0 +1,707 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "NodeFactory.h"
+#include "config.h"
+#include <cstdlib>
+#include <pthread.h>
+#include <dirent.h>
+#include <float.h>
+#include <cmath>
+#include <dlfcn.h>
+#include "AudioDriver.h"
+#include "MidiNoteNode.h"
+#include "MidiTriggerNode.h"
+#include "MidiControlNode.h"
+#include "AudioInputNode.h"
+#include "AudioOutputNode.h"
+#include "ControlInputNode.h"
+#include "ControlOutputNode.h"
+#include "MidiInputNode.h"
+#include "MidiOutputNode.h"
+#include "TransportNode.h"
+#include "PluginLibrary.h"
+#include "Plugin.h"
+#include "Patch.h"
+#include "Om.h"
+#include "OmApp.h"
+#ifdef HAVE_SLV2
+#include "LV2Plugin.h"
+#include <slv2/slv2.h>
+#endif
+#ifdef HAVE_LADSPA
+#include "LADSPAPlugin.h"
+#endif
+#ifdef HAVE_DSSI
+#include "DSSIPlugin.h"
+#endif
+
+using std::string;
+using std::cerr; using std::cout; using std::endl;
+
+
+namespace Om {
+
+
+/* I am perfectly aware that the vast majority of this class is a
+ * vomit inducing nightmare at the moment ;)
+ */
+
+
+
+NodeFactory::NodeFactory()
+: m_has_loaded(false)
+{
+ pthread_mutex_init(&m_plugin_list_mutex, NULL);
+
+ // Add builtin plugin types to m_internal_plugins list
+ // FIXME: ewwww, definitely a better way to do this!
+ //Plugin* pi = NULL;
+
+ Patch* parent = new Patch("dummy", 1, NULL, 1, 1, 1);
+
+ Node* n = NULL;
+ n = new AudioInputNode("foo", 1, parent, 1, 1);
+ m_internal_plugins.push_back(new Plugin(n->plugin()));
+ delete n;
+ n = new AudioOutputNode("foo", 1, parent, 1, 1);
+ m_internal_plugins.push_back(new Plugin(n->plugin()));
+ delete n;
+ n = new ControlInputNode("foo", 1, parent, 1, 1);
+ m_internal_plugins.push_back(new Plugin(n->plugin()));
+ delete n;
+ n = new ControlOutputNode("foo", 1, parent, 1, 1);
+ m_internal_plugins.push_back(new Plugin(n->plugin()));
+ delete n;
+ n = new MidiInputNode("foo", 1, parent, 1, 1);
+ m_internal_plugins.push_back(new Plugin(n->plugin()));
+ delete n;
+ n = new MidiOutputNode("foo", 1, parent, 1, 1);
+ m_internal_plugins.push_back(new Plugin(n->plugin()));
+ delete n;
+ n = new MidiNoteNode("foo", 1, parent, 1, 1);
+ m_internal_plugins.push_back(new Plugin(n->plugin()));
+ delete n;
+ n = new MidiTriggerNode("foo", 1, parent, 1, 1);
+ m_internal_plugins.push_back(new Plugin(n->plugin()));
+ delete n;
+ n = new MidiControlNode("foo", 1, parent, 1, 1);
+ m_internal_plugins.push_back(new Plugin(n->plugin()));
+ delete n;
+ n = new TransportNode("foo", 1, parent, 1, 1);
+ m_internal_plugins.push_back(new Plugin(n->plugin()));
+ delete n;
+
+
+ delete parent;
+}
+
+
+NodeFactory::~NodeFactory()
+{
+ for (list<Plugin*>::iterator i = m_plugins.begin(); i != m_plugins.end(); ++i)
+ delete (*i);
+
+ for (list<PluginLibrary*>::iterator i = m_libraries.begin(); i != m_libraries.end(); ++i) {
+ (*i)->close();
+ delete (*i);
+ }
+}
+
+
+void
+NodeFactory::load_plugins()
+{
+ // Only load if we havn't already, so every client connecting doesn't cause
+ // this (expensive!) stuff to happen. Not the best solution - would be nice
+ // if clients could refresh plugins list for whatever reason :/
+ if (!m_has_loaded) {
+ pthread_mutex_lock(&m_plugin_list_mutex);
+
+ m_plugins.clear();
+ m_plugins = m_internal_plugins;
+
+#if HAVE_SLV2
+ load_lv2_plugins();
+#endif
+#if HAVE_DSSI
+ load_dssi_plugins();
+#endif
+#if HAVE_LADSPA
+ load_ladspa_plugins();
+#endif
+
+ m_has_loaded = true;
+
+ pthread_mutex_unlock(&m_plugin_list_mutex);
+ }
+}
+
+
+/** Loads a plugin.
+ *
+ * Calls the load_*_plugin functions to actually do things, just a wrapper.
+ */
+Node*
+NodeFactory::load_plugin(const Plugin* a_plugin, const string& name, size_t poly, Patch* parent)
+{
+ assert(parent != NULL);
+ assert(poly == 1 || poly == parent->internal_poly());
+ assert(a_plugin);
+
+ pthread_mutex_lock(&m_plugin_list_mutex);
+
+ Node* r = NULL;
+ Plugin* plugin = NULL;
+
+ // Attempt to find the plugin in loaded DB
+ if (a_plugin->type() != Plugin::Internal) {
+ list<Plugin*>::iterator i;
+ if (a_plugin->plug_label().length() == 0) {
+ for (i = m_plugins.begin(); i != m_plugins.end(); ++i) {
+ if (a_plugin->uri() == (*i)->uri()) {
+ plugin = *i;
+ break;
+ }
+ }
+ } else {
+ for (i = m_plugins.begin(); i != m_plugins.end(); ++i) {
+ if (a_plugin->uri() == (*i)->uri()) {
+ plugin = *i;
+ break;
+ }
+ }
+ }
+
+ if (plugin == NULL)
+ return NULL;
+ }
+
+ switch (a_plugin->type()) {
+#if HAVE_SLV2
+ case Plugin::LV2:
+ r = load_lv2_plugin(plugin->uri(), name, poly, parent);
+ break;
+#endif
+#if HAVE_DSSI
+ case Plugin::DSSI:
+ r = load_dssi_plugin(plugin->uri(), name, poly, parent);
+ break;
+#endif
+#if HAVE_LADSPA
+ case Plugin::LADSPA:
+ r = load_ladspa_plugin(plugin->uri(), name, poly, parent);
+ break;
+#endif
+ case Plugin::Internal:
+ r = load_internal_plugin(a_plugin->uri(), name, poly, parent);
+ break;
+ default:
+ cerr << "[NodeFactory] WARNING: Unknown plugin type." << endl;
+ }
+
+ pthread_mutex_unlock(&m_plugin_list_mutex);
+
+ return r;
+}
+
+
+/** Loads an internal plugin.
+ */
+Node*
+NodeFactory::load_internal_plugin(const string& uri, const string& name, size_t poly, Patch* parent)
+{
+ assert(parent != NULL);
+ assert(poly == 1 || poly == parent->internal_poly());
+ assert(uri.length() > 3);
+ assert(uri.substr(0, 3) == "om:");
+
+ string plug_label = uri.substr(3);
+
+ if (plug_label == "midi_input") {
+ MidiInputNode* tn = new MidiInputNode(name, 1, parent, om->audio_driver()->sample_rate(), om->audio_driver()->buffer_size());
+ return tn;
+ } else if (plug_label == "midi_output") {
+ MidiOutputNode* tn = new MidiOutputNode(name, 1, parent, om->audio_driver()->sample_rate(), om->audio_driver()->buffer_size());
+ return tn;
+ } else if (plug_label == "audio_input") {
+ AudioInputNode* in = new AudioInputNode(name, poly, parent,
+ om->audio_driver()->sample_rate(), om->audio_driver()->buffer_size());
+ return in;
+ } else if (plug_label == "control_input") {
+ ControlInputNode* in = new ControlInputNode(name, poly, parent,
+ om->audio_driver()->sample_rate(), om->audio_driver()->buffer_size());
+ return in;
+ } else if (plug_label == "audio_output") {
+ AudioOutputNode* on = new AudioOutputNode(name, poly, parent,
+ om->audio_driver()->sample_rate(), om->audio_driver()->buffer_size());
+ return on;
+ } else if (plug_label == "control_output") {
+ ControlOutputNode* on = new ControlOutputNode(name, poly, parent,
+ om->audio_driver()->sample_rate(), om->audio_driver()->buffer_size());
+ return on;
+ } else if (plug_label == "note_in" || plug_label == "midi_note_in") {
+ MidiNoteNode* mn = new MidiNoteNode(name, poly, parent, om->audio_driver()->sample_rate(), om->audio_driver()->buffer_size());
+ return mn;
+ } else if (plug_label == "trigger_in" || plug_label == "midi_trigger_in") {
+ MidiTriggerNode* mn = new MidiTriggerNode(name, 1, parent, om->audio_driver()->sample_rate(), om->audio_driver()->buffer_size());
+ return mn;
+ } else if (plug_label == "midi_control_in") {
+ MidiControlNode* mn = new MidiControlNode(name, 1, parent, om->audio_driver()->sample_rate(), om->audio_driver()->buffer_size());
+ return mn;
+ } else if (plug_label == "transport") {
+ TransportNode* tn = new TransportNode(name, 1, parent, om->audio_driver()->sample_rate(), om->audio_driver()->buffer_size());
+ return tn;
+ } else {
+ cerr << "Unknown internal plugin type '" << plug_label << "'" << endl;
+ }
+
+ return NULL;
+}
+
+
+#ifdef HAVE_SLV2
+
+/** Loads information about all LV2 plugins into internal plugin database.
+ */
+void
+NodeFactory::load_lv2_plugins()
+{
+ SLV2List plugins = slv2_list_new();
+ slv2_list_load_all(plugins);
+
+ //cerr << "[NodeFactory] Found " << slv2_list_get_length(plugins) << " LV2 plugins." << endl;
+
+ for (unsigned long i=0; i < slv2_list_get_length(plugins); ++i) {
+
+ SLV2Plugin* lv2_plug = slv2_list_get_plugin_by_index(plugins, i);
+
+
+ //om_plug->library(plugin_library);
+
+ const char* uri = (const char*)slv2_plugin_get_uri(lv2_plug);
+ //cerr << "LV2 plugin: " << uri << endl;
+
+ bool found = false;
+ for (list<Plugin*>::const_iterator i = m_plugins.begin(); i != m_plugins.end(); ++i) {
+ if (!strcmp((*i)->uri().c_str(), uri)) {
+ cerr << "Warning: Duplicate LV2 plugin (" << uri << ").\nUsing "
+ << (*i)->lib_path() << " version." << endl;
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ //printf("[NodeFactory] Found LV2 plugin %s\n", uri);
+ Plugin* om_plug = new Plugin();
+ om_plug->type(Plugin::LV2);
+ om_plug->slv2_plugin(lv2_plug);
+ om_plug->uri(uri);
+ // FIXME FIXME FIXME temporary hack
+ om_plug->library(NULL);
+ om_plug->lib_path("FIXMEpath");
+ om_plug->plug_label("FIXMElabel");
+ unsigned char* name = slv2_plugin_get_name(lv2_plug);
+ om_plug->name((char*)name);
+ free(name);
+ om_plug->type(Plugin::LV2);
+ m_plugins.push_back(om_plug);
+ }
+ }
+
+ slv2_list_free(plugins);
+}
+
+
+/** Loads a LV2 plugin.
+ * Returns 'poly' independant plugins as a Node*
+ */
+Node*
+NodeFactory::load_lv2_plugin(const string& plug_uri,
+ const string& node_name,
+ size_t poly,
+ Patch* parent)
+{
+ // Find (Om) Plugin
+ Plugin* plugin = NULL;
+ list<Plugin*>::iterator i;
+ for (i = m_plugins.begin(); i != m_plugins.end(); ++i) {
+ plugin = (*i);
+ if ((*i)->uri() == plug_uri) break;
+ }
+
+ Node* n = NULL;
+
+ if (plugin) {
+ n = new Om::LV2Plugin(node_name, poly, parent, plugin->slv2_plugin(),
+ om->audio_driver()->sample_rate(), om->audio_driver()->buffer_size());
+ bool success = ((LV2Plugin*)n)->instantiate();
+ if (!success) {
+ delete n;
+ n = NULL;
+ }
+ n->plugin(plugin);
+ }
+
+ return n;
+}
+
+#endif // HAVE_SLV2
+
+
+#if HAVE_DSSI
+
+/** Loads information about all DSSI plugins into internal plugin database.
+ */
+void
+NodeFactory::load_dssi_plugins()
+{
+ // FIXME: too much code duplication with load_ladspa_plugin
+
+ char* env_dssi_path = getenv("DSSI_PATH");
+ string dssi_path;
+ if (!env_dssi_path) {
+ cerr << "[NodeFactory] DSSI_PATH is empty. Assuming /usr/lib/dssi:/usr/local/lib/dssi:~/.dssi" << endl;
+ dssi_path = string("/usr/lib/dssi:/usr/local/lib/dssi:").append(
+ getenv("HOME")).append("/.dssi");
+ } else {
+ dssi_path = env_dssi_path;
+ }
+
+ DSSI_Descriptor_Function df = NULL;
+ DSSI_Descriptor* descriptor = NULL;
+
+ string dir;
+ string full_lib_name;
+
+ // Yep, this should use an sstream alright..
+ while (dssi_path != "") {
+ dir = dssi_path.substr(0, dssi_path.find(':'));
+ if (dssi_path.find(':') != string::npos)
+ dssi_path = dssi_path.substr(dssi_path.find(':')+1);
+ else
+ dssi_path = "";
+
+ DIR* pdir = opendir(dir.c_str());
+ if (pdir == NULL) {
+ //cerr << "[NodeFactory] Unreadable directory in DSSI_PATH: " << dir.c_str() << endl;
+ continue;
+ }
+
+ struct dirent* pfile;
+ while ((pfile = readdir(pdir))) {
+
+ if (!strcmp(pfile->d_name, ".") || !strcmp(pfile->d_name, ".."))
+ continue;
+
+ full_lib_name = dir +"/"+ pfile->d_name;
+
+ // Load descriptor function
+ // Loaded with LAZY here, will be loaded with NOW on node loading
+ void* handle = dlopen(full_lib_name.c_str(), RTLD_LAZY);
+ if (handle == NULL)
+ continue;
+
+ df = (DSSI_Descriptor_Function)dlsym(handle, "dssi_descriptor");
+ if (df == NULL) {
+ // Not a DSSI plugin library
+ dlclose(handle);
+ continue;
+ }
+
+ PluginLibrary* plugin_library = new PluginLibrary(full_lib_name);
+ m_libraries.push_back(plugin_library);
+
+ const LADSPA_Descriptor* ld = NULL;
+
+ for (unsigned long i=0; (descriptor = (DSSI_Descriptor*)df(i)) != NULL; ++i) {
+ ld = descriptor->LADSPA_Plugin;
+ assert(ld != NULL);
+ Plugin* plugin = new Plugin();
+ assert(plugin_library != NULL);
+ plugin->library(plugin_library);
+ plugin->lib_path(dir + "/" + pfile->d_name);
+ plugin->plug_label(ld->Label);
+ plugin->name(ld->Name);
+ plugin->type(Plugin::DSSI);
+ plugin->id(ld->UniqueID);
+
+ bool found = false;
+ for (list<Plugin*>::const_iterator i = m_plugins.begin(); i != m_plugins.end(); ++i) {
+ if ((*i)->uri() == plugin->uri()) {
+ cerr << "Warning: Duplicate DSSI plugin (" << plugin->lib_name() << ":"
+ << plugin->plug_label() << ")" << " found.\nUsing " << (*i)->lib_path()
+ << " version." << endl;
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ m_plugins.push_back(plugin);
+ else
+ delete plugin;
+ }
+
+ df = NULL;
+ descriptor = NULL;
+ dlclose(handle);
+ }
+ closedir(pdir);
+ }
+}
+
+
+/** Creates a Node by instancing a DSSI plugin.
+ */
+Node*
+NodeFactory::load_dssi_plugin(const string& uri,
+ const string& name, size_t poly, Patch* parent)
+{
+ // FIXME: awful code duplication here
+
+ assert(uri != "");
+ assert(name != "");
+ assert(poly > 0);
+
+ DSSI_Descriptor_Function df = NULL;
+ const Plugin* plugin = NULL;
+ Node* n = NULL;
+ void* handle = NULL;
+
+ // Attempt to find the lib
+ list<Plugin*>::iterator i;
+ for (i = m_plugins.begin(); i != m_plugins.end(); ++i) {
+ plugin = (*i);
+ if (plugin->uri() == uri) break;
+ }
+
+ assert(plugin->id() != 0);
+
+ if (i == m_plugins.end()) {
+ cerr << "Did not find DSSI plugin " << uri << " in database." << endl;
+ return NULL;
+ } else {
+ assert(plugin != NULL);
+ plugin->library()->open();
+ handle = plugin->library()->handle();
+ assert(handle != NULL);
+
+ // Load descriptor function
+ dlerror();
+ df = (DSSI_Descriptor_Function)dlsym(handle, "dssi_descriptor");
+ if (df == NULL || dlerror() != NULL) {
+ cerr << "Looks like this isn't a DSSI plugin." << endl;
+ return NULL;
+ }
+ }
+
+ // Attempt to find the plugin in lib
+ DSSI_Descriptor* descriptor = NULL;
+ for (unsigned long i=0; (descriptor = (DSSI_Descriptor*)df(i)) != NULL; ++i) {
+ if (descriptor->LADSPA_Plugin != NULL
+ && descriptor->LADSPA_Plugin->UniqueID == plugin->id()) {
+ break;
+ }
+ }
+
+ if (descriptor == NULL) {
+ cerr << "Could not find plugin \"" << plugin->id() << "\" in " << plugin->lib_name() << endl;
+ return NULL;
+ }
+
+ n = new DSSIPlugin(name, poly, parent, descriptor,
+ om->audio_driver()->sample_rate(), om->audio_driver()->buffer_size());
+ bool success = ((DSSIPlugin*)n)->instantiate();
+ if (!success) {
+ delete n;
+ n = NULL;
+ }
+
+ n->plugin(plugin);
+
+ return n;
+}
+#endif // HAVE_DSSI
+
+
+#ifdef HAVE_LADSPA
+/** Loads information about all LADSPA plugins into internal plugin database.
+ */
+void
+NodeFactory::load_ladspa_plugins()
+{
+ char* env_ladspa_path = getenv("LADSPA_PATH");
+ string ladspa_path;
+ if (!env_ladspa_path) {
+ cerr << "[NodeFactory] LADSPA_PATH is empty. Assuming /usr/lib/ladspa:/usr/local/lib/ladspa:~/.ladspa" << endl;
+ ladspa_path = string("/usr/lib/ladspa:/usr/local/lib/ladspa:").append(
+ getenv("HOME")).append("/.ladspa");
+ } else {
+ ladspa_path = env_ladspa_path;
+ }
+
+ LADSPA_Descriptor_Function df = NULL;
+ LADSPA_Descriptor* descriptor = NULL;
+
+ string dir;
+ string full_lib_name;
+
+ // Yep, this should use an sstream alright..
+ while (ladspa_path != "") {
+ dir = ladspa_path.substr(0, ladspa_path.find(':'));
+ if (ladspa_path.find(':') != string::npos)
+ ladspa_path = ladspa_path.substr(ladspa_path.find(':')+1);
+ else
+ ladspa_path = "";
+
+ DIR* pdir = opendir(dir.c_str());
+ if (pdir == NULL) {
+ //cerr << "[NodeFactory] Unreadable directory in LADSPA_PATH: " << dir.c_str() << endl;
+ continue;
+ }
+
+ struct dirent* pfile;
+ while ((pfile = readdir(pdir))) {
+
+ if (!strcmp(pfile->d_name, ".") || !strcmp(pfile->d_name, ".."))
+ continue;
+
+ full_lib_name = dir +"/"+ pfile->d_name;
+
+ // Load descriptor function
+ // Loaded with LAZY here, will be loaded with NOW on node loading
+ void* handle = dlopen(full_lib_name.c_str(), RTLD_LAZY);
+ if (handle == NULL)
+ continue;
+
+ df = (LADSPA_Descriptor_Function)dlsym(handle, "ladspa_descriptor");
+ if (df == NULL) {
+ dlclose(handle);
+ continue;
+ }
+
+ PluginLibrary* plugin_library = new PluginLibrary(full_lib_name);
+ m_libraries.push_back(plugin_library);
+
+ for (unsigned long i=0; (descriptor = (LADSPA_Descriptor*)df(i)) != NULL; ++i) {
+ Plugin* plugin = new Plugin();
+ assert(plugin_library != NULL);
+ plugin->library(plugin_library);
+ plugin->lib_path(dir + "/" + pfile->d_name);
+ plugin->plug_label(descriptor->Label);
+ plugin->name(descriptor->Name);
+ plugin->type(Plugin::LADSPA);
+ plugin->id(descriptor->UniqueID);
+
+ bool found = false;
+ for (list<Plugin*>::const_iterator i = m_plugins.begin(); i != m_plugins.end(); ++i) {
+ if ((*i)->uri() == plugin->uri()) {
+ cerr << "Warning: Duplicate LADSPA plugin " << plugin->uri()
+ << " found.\nChoosing " << (*i)->lib_path()
+ << " over " << plugin->lib_path() << endl;
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ m_plugins.push_back(plugin);
+ else
+ delete plugin;
+ }
+
+ df = NULL;
+ descriptor = NULL;
+ dlclose(handle);
+ }
+ closedir(pdir);
+ }
+}
+
+
+/** Loads a LADSPA plugin.
+ * Returns 'poly' independant plugins as a Node*
+ */
+Node*
+NodeFactory::load_ladspa_plugin(const string& uri,
+ const string& name, size_t poly, Patch* parent)
+{
+ assert(uri != "");
+ assert(name != "");
+ assert(poly > 0);
+
+ LADSPA_Descriptor_Function df = NULL;
+ Plugin* plugin = NULL;
+ Node* n = NULL;
+ void* plugin_lib = NULL;
+
+ // Attempt to find the lib
+ list<Plugin*>::iterator i;
+ for (i = m_plugins.begin(); i != m_plugins.end(); ++i) {
+ plugin = (*i);
+ if (plugin->uri() == uri) break;
+ }
+
+ assert(plugin->id() != 0);
+
+ if (i == m_plugins.end()) {
+ cerr << "Did not find LADSPA plugin " << uri << " in database." << endl;
+ return NULL;
+ } else {
+ assert(plugin != NULL);
+ plugin->library()->open();
+ plugin_lib = plugin->library()->handle();
+ assert(plugin_lib != NULL);
+
+ // Load descriptor function
+ dlerror();
+ df = (LADSPA_Descriptor_Function)dlsym(plugin_lib, "ladspa_descriptor");
+ if (df == NULL || dlerror() != NULL) {
+ cerr << "Looks like this isn't a LADSPA plugin." << endl;
+ return NULL;
+ }
+ }
+
+ // Attempt to find the plugin in lib
+ LADSPA_Descriptor* descriptor = NULL;
+ for (unsigned long i=0; (descriptor = (LADSPA_Descriptor*)df(i)) != NULL; ++i) {
+ if (descriptor->UniqueID == plugin->id()) {
+ break;
+ }
+ }
+
+ if (descriptor == NULL) {
+ cerr << "Could not find plugin \"" << plugin->id() << "\" in " << plugin->lib_path() << endl;
+ return NULL;
+ }
+
+ n = new LADSPAPlugin(name, poly, parent, descriptor,
+ om->audio_driver()->sample_rate(), om->audio_driver()->buffer_size());
+ bool success = ((LADSPAPlugin*)n)->instantiate();
+ if (!success) {
+ delete n;
+ n = NULL;
+ }
+
+ n->plugin(plugin);
+
+ return n;
+}
+
+
+#endif // HAVE_LADSPA
+
+
+} // namespace Om
diff --git a/src/libs/engine/NodeFactory.h b/src/libs/engine/NodeFactory.h
new file mode 100644
index 00000000..ed6a35dc
--- /dev/null
+++ b/src/libs/engine/NodeFactory.h
@@ -0,0 +1,93 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef NODEFACTORY_H
+#define NODEFACTORY_H
+
+#include "config.h"
+#include <list>
+#include <string>
+#include <ladspa.h>
+#include <pthread.h>
+
+using std::string; using std::list;
+
+namespace Om {
+
+class Node;
+class PortInfo;
+class Patch;
+class PluginLibrary;
+class Plugin;
+
+
+/** Loads plugins and creates Nodes from them.
+ *
+ * NodeFactory's responsibility is to get enough information to allow the
+ * loading of a plugin possible (ie finding/opening shared libraries etc)
+ *
+ * The constructor of various Node types (ie LADSPAPlugin) are responsible
+ * for actually creating a Node instance of the plugin.
+ *
+ * \ingroup engine
+ */
+class NodeFactory
+{
+public:
+ NodeFactory();
+ ~NodeFactory();
+
+ void load_plugins();
+ Node* load_plugin(const Plugin* info, const string& name, size_t poly, Patch* parent);
+
+ const list<Plugin*>& plugins() { return m_plugins; }
+
+ void lock_plugin_list() { pthread_mutex_lock(&m_plugin_list_mutex); }
+ void unlock_plugin_list() { pthread_mutex_unlock(&m_plugin_list_mutex); }
+
+private:
+#ifdef HAVE_LADSPA
+ void load_ladspa_plugins();
+ Node* load_ladspa_plugin(const string& plugin_uri, const string& name, size_t poly, Patch* parent);
+#endif
+
+#ifdef HAVE_SLV2
+ void load_lv2_plugins();
+ Node* load_lv2_plugin(const string& plugin_uri, const string& name, size_t poly, Patch* parent);
+#endif
+
+#ifdef HAVE_DSSI
+ void load_dssi_plugins();
+ Node* load_dssi_plugin(const string& plugin_uri, const string& name, size_t poly, Patch* parent);
+#endif
+
+ Node* load_internal_plugin(const string& plug_label, const string& name, size_t poly, Patch* parent);
+
+ list<PluginLibrary*> m_libraries;
+ list<Plugin*> m_internal_plugins;
+ list<Plugin*> m_plugins;
+
+ /** Used to protect the list while load_plugins is building it. */
+ pthread_mutex_t m_plugin_list_mutex;
+
+ bool m_has_loaded;
+};
+
+
+} // namespace Om
+
+#endif // NODEFACTORY_H
diff --git a/src/libs/engine/OSCClient.cpp b/src/libs/engine/OSCClient.cpp
new file mode 100644
index 00000000..332a2144
--- /dev/null
+++ b/src/libs/engine/OSCClient.cpp
@@ -0,0 +1,503 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "OSCClient.h"
+#include <cassert>
+#include <iostream>
+#include <unistd.h>
+#include "Om.h"
+#include "OmApp.h"
+#include "ObjectStore.h"
+#include "NodeFactory.h"
+#include "util.h"
+#include "Patch.h"
+#include "Node.h"
+#include "PortInfo.h"
+#include "Plugin.h"
+#include "PortBase.h"
+#include "Connection.h"
+#include "AudioDriver.h"
+#include "interface/ClientInterface.h"
+#include "Responder.h"
+
+using std::cout; using std::cerr; using std::endl;
+
+namespace Om {
+
+
+/*! \page client_osc_namespace Client OSC Namespace Documentation
+ *
+ * <p>These are all the messages sent from the engine to the client. Om
+ * communication takes place over two distinct bands: control band and
+ * notification band.</p>
+ * <p>The control band is where clients send commands, and receive a simple
+ * response, either OK or an error.</p>
+ * <p>All notifications of engine state (ie new nodes) are sent over the
+ * notification band <em>which is seperate from the control band</em>. The
+ * reasoning behind this is that many clients may be connected at the same
+ * time - a client may receive notifications that are not a direct consequence
+ * of some message it sent.</p>
+ * <p>The notification band can be thought of as a stream of events representing
+ * the changing engine state. For example, It is possible for a client to send
+ * commands and receive aknowledgements, and not listen to the notification band
+ * at all; or (in the near future anyway) for a client to use UDP for the control
+ * band (for speed), and TCP for the notification band (for reliability and
+ * order guarantees).</p>
+ * \n\n
+ */
+
+
+/* Documentation for namespace portion implemented in Responder.cpp */
+
+/** \page client_osc_namespace
+ * \n
+ * <h3>Notification Band</h3>
+ */
+
+/** \page client_osc_namespace
+ * <p> \b /om/response/ok - Respond successfully to a user command
+ * \arg \b responder-id (int) - Responder ID this is a response to
+ * </p> \n \n
+ */
+
+/** \page client_osc_namespace
+ * <p> \b /om/response/error - Respond negatively to a user command
+ * \arg \b responder-id (int) - Request ID this is a response to
+ * \arg \b message (string) - Error message (natural language text)
+ * </p> \n \n
+ */
+
+
+
+/** \page client_osc_namespace
+ * \n
+ * <h3>Notification Band</h3>
+ */
+
+/** \page client_osc_namespace
+ * <p> \b /om/error - Notification that an error has occurred
+ * \arg \b message (string) - Error message (natural language text)
+ *
+ * \li This is for notification of errors that aren't a direct response to a
+ * user command, ie "unexpected" errors.</p> \n \n
+ */
+void
+OSCClient::error(const string& msg)
+{
+ lo_send(_address, "/om/error", "s", msg.c_str());
+}
+
+
+/** \page client_osc_namespace
+ * <p> \b /om/num_plugins
+ * \arg \b num (int) - Number of plugins engine has loaded
+ * \li This is sent before sending the list of plugins, so the client is aware
+ * of how many plugins (/om/plugin messages) to expect.</p> \n \n
+ */
+
+
+/** \page client_osc_namespace
+ * <p> \b /om/num_plugins
+ * \arg \b num (int) - Number of plugins engine has loaded
+ * \li This is sent before sending the list of plugins, so the client is aware
+ * of how many plugins (/om/plugin messages) to expect.</p> \n \n
+ */
+void
+OSCClient::num_plugins(uint32_t num)
+{
+ lo_message m = lo_message_new();
+ lo_message_add_int32(m, num);
+ lo_send_message(_address, "/om/num_plugins", m);
+}
+
+
+/** \page client_osc_namespace
+ * <p> \b /om/plugin - Notification of the existance of a plugin
+ * \arg \b type (string) - Type if plugin ("LADSPA", "DSSI", or "Internal")
+ * \arg \b uri (string) - URI of the plugin (see engine namespace documentation) \n
+ * \arg \b lib-name (string) - Name of shared library plugin resides in (ie "cmt.so")
+ * \arg \b plug-label (string) - Label of the plugin (ie "dahdsr_iaoa")
+ * \arg \b name (string) - Descriptive human-readable name of plugin (ie "ADSR Envelope")
+ * </p> \n \n
+ */
+/*
+void
+OSCClient::plugins()
+{
+ om->node_factory()->lock_plugin_list();
+
+ const list<Plugin*>& plugs = om->node_factory()->plugins();
+ const Plugin* plugin;
+
+ lo_timetag tt;
+ lo_timetag_now(&tt);
+ lo_bundle b = lo_bundle_new(tt);
+ lo_message m = lo_message_new();
+ list<lo_message> msgs;
+
+ lo_message_add_int32(m, plugs.size());
+ lo_bundle_add_message(b, "/om/num_plugins", m);
+ msgs.push_back(m);
+
+ for (list<Plugin*>::const_iterator j = plugs.begin(); j != plugs.end(); ++j) {
+ plugin = (*j);
+ m = lo_message_new();
+
+ lo_message_add_string(m, plugin->type_string());
+ lo_message_add_string(m, plugin->uri().c_str());
+ lo_message_add_string(m, plugin->plug_label().c_str());
+ lo_message_add_string(m, plugin->name().c_str());
+ lo_bundle_add_message(b, "/om/plugin", m);
+ msgs.push_back(m);
+ if (lo_bundle_length(b) > 1024) {
+ lo_send_bundle(_address, b);
+ lo_bundle_free(b);
+ b = lo_bundle_new(tt);
+ }
+ }
+
+ if (lo_bundle_length(b) > 0) {
+ lo_send_bundle(_address, b);
+ lo_bundle_free(b);
+ } else {
+ lo_bundle_free(b);
+ }
+ for (list<lo_bundle>::const_iterator i = msgs.begin(); i != msgs.end(); ++i)
+ lo_message_free(*i);
+
+ om->node_factory()->unlock_plugin_list();
+}
+*/
+
+/** \page client_osc_namespace
+ * <p> \b /om/new_node - Notification of a new node's creation.
+ * \arg \b plug-uri (string) - URI of the plugin new node is an instance of
+ * \arg \b path (string) - Path of the new node
+ * \arg \b polyphonic (integer-boolean) - Node is polyphonic (1 = yes, 0 = no)
+ * \arg \b num-ports (integer) - Number of ports (number of new_port messages to expect)
+ * \li New nodes are sent as a bundle. The first message in the bundle will be
+ * this one (/om/new_node), followed by a series of /om/new_port commands,
+ * followed by /om/new_node_end. </p> \n \n
+ */
+void OSCClient::new_node(const string& plugin_type,
+ const string& plugin_uri,
+ const string& node_path,
+ bool is_polyphonic,
+ uint32_t num_ports)
+{
+ lo_send(_address, "/om/new_node", "sssii", plugin_type.c_str(), plugin_uri.c_str(),
+ node_path.c_str(), is_polyphonic ? 1 : 0, num_ports);
+#if 0
+ /*
+ lo_timetag tt;
+ lo_timetag_now(&tt);
+ lo_bundle b = lo_bundle_new(tt);
+ lo_message m = lo_message_new();
+ list<lo_message> msgs;
+
+ lo_message_add_string(m, plugin_type.c_str());
+ lo_message_add_string(m, plugin_uri.c_str());
+ lo_message_add_string(m, node_path.c_str());
+ lo_message_add_int32(m, is_polyphonic ? 1 : 0);
+ lo_message_add_int32(m, num_ports);
+
+ lo_bundle_add_message(b, "/om/new_node", m);
+ msgs.push_back(m);
+*/
+
+
+ /*
+ const Array<Port*>& ports = node->ports();
+ Port* port;
+ PortInfo* info;
+ for (size_t j=0; j < ports.size(); ++j) {
+ port = ports.at(j);
+ info = port->port_info();
+
+ assert(port != NULL);
+ assert(info != NULL);
+
+ m = lo_message_new();
+ lo_message_add_string(m, port->path().c_str());
+ lo_message_add_string(m, info->type_string().c_str());
+ lo_message_add_string(m, info->direction_string().c_str());
+ lo_message_add_string(m, info->hint_string().c_str());
+ lo_message_add_float(m, info->default_val());
+ lo_message_add_float(m, info->min_val());
+ lo_message_add_float(m, info->max_val());
+ lo_bundle_add_message(b, "/om/new_port", m);
+ msgs.push_back(m);
+
+ // If the bundle is getting very large, send it and start
+ // a new one
+ if (lo_bundle_length(b) > 1024) {
+ lo_send_bundle(_address, b);
+ lo_bundle_free(b);
+ b = lo_bundle_new(tt);
+ }
+ }
+*/
+ /*m = lo_message_new();
+ //lo_bundle_add_message(b, "/om/new_node_end", m);
+ //msgs.push_back(m);
+
+ lo_send_bundle(_address, b);
+ lo_bundle_free(b);
+
+ for (list<lo_bundle>::const_iterator i = msgs.begin(); i != msgs.end(); ++i)
+ lo_message_free(*i);
+
+ usleep(100);
+*/
+ /*
+ const map<string, string>& data = node->metadata();
+ // Send node metadata
+ for (map<string, string>::const_iterator i = data.begin(); i != data.end(); ++i)
+ metadata_update(node->path(), (*i).first, (*i).second);
+
+
+ // Send port metadata
+ for (size_t j=0; j < ports.size(); ++j) {
+ port = ports.at(j);
+ const map<string, string>& data = port->metadata();
+ for (map<string, string>::const_iterator i = data.begin(); i != data.end(); ++i)
+ metadata_update(port->path(), (*i).first, (*i).second);
+ }
+
+ // Send control values
+ for (size_t i=0; i < node->ports().size(); ++i) {
+ PortBase<sample>* port = (PortBase<sample>*)node->ports().at(i);
+ if (port->port_info()->is_input() && port->port_info()->is_control())
+ control_change(port->path(), port->buffer(0)->value_at(0));
+ }
+ */
+#endif
+}
+
+
+
+/** \page client_osc_namespace
+ * <p> \b /om/new_port - Notification of a new port's creation.
+ * \arg \b path (string) - Path of new port
+ * \arg \b data-type (string) - Type of port (CONTROL or AUDIO)
+ * \arg \b direction ("is-output") (integer) - Direction of data flow (Input = 0, Output = 1)
+ *
+ * \li Note that in the event of loading a patch, this message could be
+ * followed immediately by a control change, meaning the default-value is
+ * not actually the current value of the port.
+ * \li The minimum and maximum values are suggestions only, they are not
+ * enforced in any way, and going outside them is perfectly fine. Also note
+ * that the port ranges in om_gtk are not these ones! Those ranges are set
+ * as metadata.</p> \n \n
+ */
+void
+OSCClient::new_port(const string& path,
+ const string& data_type,
+ bool is_output)
+{
+ //PortInfo* info = port->port_info();
+
+ lo_send(_address, "/om/new_port", "ssi", path.c_str(), data_type.c_str(), is_output);
+
+ // Send metadata
+ /*const map<string, string>& data = port->metadata();
+ for (map<string, string>::const_iterator i = data.begin(); i != data.end(); ++i)
+ metadata_update(port->path(), (*i).first, (*i).second);*/
+}
+
+
+/** \page client_osc_namespace
+ * <p> \b /om/destroyed - Notification an object has been destroyed
+ * \arg \b path (string) - Path of object (which no longer exists) </p> \n \n
+ */
+void
+OSCClient::object_destroyed(const string& path)
+{
+ assert(path != "/");
+
+ lo_send(_address, "/om/destroyed", "s", path.c_str());
+}
+
+
+/** \page client_osc_namespace
+ * <p> \b /om/patch_cleared - Notification a patch has been cleared (all children destroyed)
+ * \arg \b path (string) - Path of patch (which is now empty)</p> \n \n
+ */
+void
+OSCClient::patch_cleared(const string& patch_path)
+{
+ lo_send(_address, "/om/patch_cleared", "s", patch_path.c_str());
+}
+
+
+/** \page client_osc_namespace
+ * <p> \b /om/patch_enabled - Notification a patch's DSP processing has been enabled.
+ * \arg \b path (string) - Path of enabled patch</p> \n \n
+ */
+void
+OSCClient::patch_enabled(const string& patch_path)
+{
+ lo_send(_address, "/om/patch_enabled", "s", patch_path.c_str());
+}
+
+
+/** \page client_osc_namespace
+ * <p> \b /om/patch_disabled - Notification a patch's DSP processing has been disabled.
+ * \arg \b path (string) - Path of disabled patch</p> \n \n
+ */
+void
+OSCClient::patch_disabled(const string& patch_path)
+{
+ lo_send(_address, "/om/patch_disabled", "s", patch_path.c_str());
+}
+
+
+/** \page client_osc_namespace
+ * <p> \b /om/new_connection - Notification a new connection has been made.
+ * \arg \b src-path (string) - Path of the source port
+ * \arg \b dst-path (string) - Path of the destination port</p> \n \n
+ */
+void
+OSCClient::connection(const string& src_port_path, const string& dst_port_path)
+{
+ lo_send(_address, "/om/new_connection", "ss", src_port_path.c_str(), dst_port_path.c_str());
+}
+
+
+/** \page client_osc_namespace
+ * <p> \b /om/disconnection - Notification a connection has been unmade.
+ * \arg \b src-path (string) - Path of the source port
+ * \arg \b dst-path (string) - Path of the destination port</p> \n \n
+ */
+void
+OSCClient::disconnection(const string& src_port_path, const string& dst_port_path)
+{
+ lo_send(_address, "/om/disconnection", "ss", src_port_path.c_str(), dst_port_path.c_str());
+}
+
+
+/** \page client_osc_namespace
+ * <p> \b /om/metadata/update - Notification of a piece of metadata.
+ * \arg \b path (string) - Path of the object associated with metadata (can be a node, patch, or port)
+ * \arg \b key (string)
+ * \arg \b value (string)</p> \n \n
+ */
+void
+OSCClient::metadata_update(const string& path, const string& key, const string& value)
+{
+ lo_send(_address, "/om/metadata/update", "sss", path.c_str(), key.c_str(), value.c_str());
+}
+
+
+/** \page client_osc_namespace
+ * <p> \b /om/control_change - Notification the value of a port has changed
+ * \arg \b path (string) - Path of port
+ * \arg \b value (float) - New value of port
+ *
+ * \li This will only send updates for values set by clients of course - not values
+ * changing because of connections to other ports!</p> \n \n
+ */
+void
+OSCClient::control_change(const string& port_path, float value)
+{
+ lo_send(_address, "/om/control_change", "sf", port_path.c_str(), value);
+}
+
+
+/** \page client_osc_namespace
+ * <p> \b /om/plugin - Notification of the existance of a plugin
+ * \arg \b type (string) - Type if plugin ("LADSPA", "DSSI", or "Internal")</p> \n \n
+ * \arg \b uri (string) - Type if plugin ("LADSPA", "DSSI", or "Internal")</p> \n \n
+ * \arg \b name (string) - Descriptive human-readable name of plugin (ie "ADSR Envelope")
+ */
+void
+OSCClient::new_plugin(const string& type, const string& uri, const string& name)
+{
+ lo_message m = lo_message_new();
+ lo_message_add_string(m, type.c_str());
+ lo_message_add_string(m, uri.c_str());
+ lo_message_add_string(m, name.c_str());
+ lo_send_message(_address, "/om/plugin", m);
+}
+
+
+/** \page client_osc_namespace
+ * <p> \b /om/new_patch - Notification of a new patch
+ * \arg \b path (string) - Path of new patch
+ * \arg \b poly (int) - Polyphony of new patch (\em not a boolean like new_node) </p> \n \n
+ */
+void
+OSCClient::new_patch(const string& path, uint32_t poly)
+{
+ lo_send(_address, "/om/new_patch", "si", path.c_str(), poly);
+
+ /*
+ if (p->process())
+ patch_enabled(p->path());
+
+ // Send metadata
+ const map<string, string>& data = p->metadata();
+ for (map<string, string>::const_iterator i = data.begin(); i != data.end(); ++i) {
+ metadata_update(p->path(), (*i).first, (*i).second);
+ }
+ */
+}
+
+
+/** \page client_osc_namespace
+ * <p> \b /om/object_renamed - Notification of an object's renaming
+ * \arg \b old-path (string) - Old path of object
+ * \arg \b new-path (string) - New path of object </p> \n \n
+ */
+void
+OSCClient::object_renamed(const string& old_path, const string& new_path)
+{
+ lo_send(_address, "/om/object_renamed", "ss", old_path.c_str(), new_path.c_str());
+}
+
+
+/** Sends all OmObjects known to the engine.
+ */
+/*
+void
+OSCClient::all_objects()
+{
+ for (Tree<OmObject*>::iterator i = om->object_store()->objects().begin();
+ i != om->object_store()->objects().end(); ++i)
+ if ((*i)->as_node() != NULL && (*i)->parent() == NULL)
+ (*i)->as_node()->send_creation_messages(this);
+}
+*/
+
+/** Sends information about a program associated with a DSSI plugin node.
+ */
+void
+OSCClient::program_add(const string& node_path, uint32_t bank, uint32_t program, const string& name)
+{
+ lo_send(_address, "/om/program_add", "siis",
+ node_path.c_str(), bank, program, name.c_str());
+}
+
+
+void
+OSCClient::program_remove(const string& node_path, uint32_t bank, uint32_t program)
+{
+ lo_send(_address, "/om/program_remove", "sii",
+ node_path.c_str(), bank, program);
+}
+
+
+} // namespace Om
diff --git a/src/libs/engine/OSCClient.h b/src/libs/engine/OSCClient.h
new file mode 100644
index 00000000..db14c696
--- /dev/null
+++ b/src/libs/engine/OSCClient.h
@@ -0,0 +1,131 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef OSCCLIENT_H
+#define OSCCLIENT_H
+
+#include <string>
+#include <iostream>
+#include <list>
+#include <lo/lo.h>
+#include <pthread.h>
+#include "util/types.h"
+#include "interface/ClientInterface.h"
+
+using std::list; using std::string;
+using std::cerr;
+
+namespace Om {
+
+
+/** Implements ClientInterface for OSC clients (sends OSC messages).
+ *
+ * \ingroup engine
+ */
+class OSCClient : public Shared::ClientInterface
+{
+public:
+ OSCClient(const string& url)
+ : _url(url),
+ _address(lo_address_new_from_url(url.c_str()))
+ {}
+
+ virtual ~OSCClient()
+ { lo_address_free(_address); }
+
+ const string& url() const { return _url; }
+ const lo_address address() const { return _address; }
+
+ //void plugins(); // FIXME remove
+
+
+
+ /* *** ClientInterface Implementation Below *** */
+
+
+ //void client_registration(const string& url, int client_id);
+
+ // need a liblo feature to make this possible :/
+ void bundle_begin() {}
+ void bundle_end() {}
+
+ void num_plugins(uint32_t num);
+
+ void error(const string& msg);
+
+ virtual void new_plugin(const string& type,
+ const string& uri,
+ const string& name);
+
+ virtual void new_patch(const string& path, uint32_t poly);
+
+ virtual void new_node(const string& plugin_type,
+ const string& plugin_uri,
+ const string& node_path,
+ bool is_polyphonic,
+ uint32_t num_ports);
+
+ virtual void new_port(const string& path,
+ const string& data_type,
+ bool is_output);
+
+ virtual void patch_enabled(const string& path);
+
+ virtual void patch_disabled(const string& path);
+
+ virtual void patch_cleared(const string& path);
+
+ virtual void object_destroyed(const string& path);
+
+ virtual void object_renamed(const string& old_path,
+ const string& new_path);
+
+ virtual void connection(const string& src_port_path,
+ const string& dst_port_path);
+
+ virtual void disconnection(const string& src_port_path,
+ const string& dst_port_path);
+
+ virtual void metadata_update(const string& subject_path,
+ const string& predicate,
+ const string& value);
+
+ virtual void control_change(const string& port_path,
+ float value);
+
+ virtual void program_add(const string& node_path,
+ uint32_t bank,
+ uint32_t program,
+ const string& program_name);
+
+ virtual void program_remove(const string& node_path,
+ uint32_t bank,
+ uint32_t program);
+
+private:
+ // Prevent copies (undefined)
+ OSCClient(const OSCClient&);
+ OSCClient& operator=(const OSCClient&);
+
+ string _url;
+ lo_address _address;
+};
+
+
+} // namespace Om
+
+#endif // OSCCLIENT_H
+
diff --git a/src/libs/engine/OSCReceiver.cpp b/src/libs/engine/OSCReceiver.cpp
new file mode 100644
index 00000000..8edab8b1
--- /dev/null
+++ b/src/libs/engine/OSCReceiver.cpp
@@ -0,0 +1,926 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "OSCReceiver.h"
+#include <iostream>
+#include <cstdlib>
+#include <string>
+#include <lo/lo.h>
+#include "util/types.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "util/Queue.h"
+#include "util/CountedPtr.h"
+#include "QueuedEventSource.h"
+#include "interface/ClientKey.h"
+#include "interface/ClientInterface.h"
+#include "OSCClient.h"
+#include "OSCResponder.h"
+#include "ClientBroadcaster.h"
+#include "Plugin.h"
+
+using std::cerr; using std::cout; using std::endl;
+
+namespace Om {
+
+using Shared::ClientKey;
+
+
+/*! \page engine_osc_namespace Engine OSC Namespace Documentation
+ *
+ * <p>These are the commands the engine recognizes. A client can control every
+ * aspect of the engine entirely with these commands.</p>
+ *
+ * <p>All commands on this page are in the "control band". If a client needs to
+ * know about the state of the engine, it must listen to the "notification band".
+ * See the "Client OSC Namespace Documentation" for details.
+ */
+
+
+OSCReceiver::OSCReceiver(size_t queue_size, const char* const port)
+: QueuedEngineInterface(queue_size),
+ _port(port),
+ _is_activated(false),
+ _st(NULL),
+ _osc_responder(NULL)
+{
+ _st = lo_server_thread_new(port, error_cb);
+
+ if (_st == NULL) {
+ cerr << "[OSC] Could not start OSC server. Aborting." << endl;
+ exit(EXIT_FAILURE);
+ } else {
+ char* lo_url = lo_server_thread_get_url(_st);
+ cout << "[OSC] Started OSC server at " << lo_url << endl;
+ free(lo_url);
+ }
+
+ // For debugging, print all incoming OSC messages
+ lo_server_thread_add_method(_st, NULL, NULL, generic_cb, NULL);
+
+ // Set response address for this message.
+ // It's important this is first and returns nonzero.
+ lo_server_thread_add_method(_st, NULL, NULL, set_response_address_cb, this);
+
+ // Commands
+ lo_server_thread_add_method(_st, "/om/ping", "i", ping_cb, this);
+ lo_server_thread_add_method(_st, "/om/ping_slow", "i", ping_slow_cb, this);
+ lo_server_thread_add_method(_st, "/om/engine/quit", "i", quit_cb, this);
+ //lo_server_thread_add_method(_st, "/om/engine/register_client", "is", register_client_cb, this);
+ lo_server_thread_add_method(_st, "/om/engine/register_client", "i", register_client_cb, this);
+ lo_server_thread_add_method(_st, "/om/engine/unregister_client", "i", unregister_client_cb, this);
+ lo_server_thread_add_method(_st, "/om/engine/load_plugins", "i", load_plugins_cb, this);
+ lo_server_thread_add_method(_st, "/om/engine/activate", "i", engine_activate_cb, this);
+ lo_server_thread_add_method(_st, "/om/engine/deactivate", "i", engine_deactivate_cb, this);
+ lo_server_thread_add_method(_st, "/om/synth/create_patch", "isi", create_patch_cb, this);
+ lo_server_thread_add_method(_st, "/om/synth/enable_patch", "is", enable_patch_cb, this);
+ lo_server_thread_add_method(_st, "/om/synth/disable_patch", "is", disable_patch_cb, this);
+ lo_server_thread_add_method(_st, "/om/synth/clear_patch", "is", clear_patch_cb, this);
+ lo_server_thread_add_method(_st, "/om/synth/create_node", "issssi", create_node_cb, this);
+ lo_server_thread_add_method(_st, "/om/synth/create_node", "isssi", create_node_by_uri_cb, this);
+ lo_server_thread_add_method(_st, "/om/synth/destroy", "is", destroy_cb, this);
+ lo_server_thread_add_method(_st, "/om/synth/rename", "iss", rename_cb, this);
+ lo_server_thread_add_method(_st, "/om/synth/connect", "iss", connect_cb, this);
+ lo_server_thread_add_method(_st, "/om/synth/disconnect", "iss", disconnect_cb, this);
+ lo_server_thread_add_method(_st, "/om/synth/disconnect_all", "is", disconnect_all_cb, this);
+ lo_server_thread_add_method(_st, "/om/synth/set_port_value", "isf", set_port_value_cb, this);
+ lo_server_thread_add_method(_st, "/om/synth/set_port_value", "isif", set_port_value_voice_cb, this);
+ lo_server_thread_add_method(_st, "/om/synth/set_port_value_slow", "isf", set_port_value_slow_cb, this);
+ lo_server_thread_add_method(_st, "/om/synth/note_on", "isii", note_on_cb, this);
+ lo_server_thread_add_method(_st, "/om/synth/note_off", "isi", note_off_cb, this);
+ lo_server_thread_add_method(_st, "/om/synth/all_notes_off", "isi", all_notes_off_cb, this);
+ lo_server_thread_add_method(_st, "/om/synth/midi_learn", "is", midi_learn_cb, this);
+#ifdef HAVE_LASH
+ lo_server_thread_add_method(_st, "/om/lash/restore_finished", "i", lash_restore_done_cb, this);
+#endif
+
+ lo_server_thread_add_method(_st, "/om/metadata/request", "isss", metadata_get_cb, this);
+ lo_server_thread_add_method(_st, "/om/metadata/set", "isss", metadata_set_cb, this);
+
+ // Queries
+ lo_server_thread_add_method(_st, "/om/request/plugins", "i", request_plugins_cb, this);
+ lo_server_thread_add_method(_st, "/om/request/all_objects", "i", request_all_objects_cb, this);
+ lo_server_thread_add_method(_st, "/om/request/port_value", "is", request_port_value_cb, this);
+
+ // DSSI support
+#ifdef HAVE_DSSI
+ // XXX WARNING: notice this is a catch-all
+ lo_server_thread_add_method(_st, NULL, NULL, dssi_cb, this);
+#endif
+
+ lo_server_thread_add_method(_st, NULL, NULL, unknown_cb, NULL);
+}
+
+
+OSCReceiver::~OSCReceiver()
+{
+ deactivate();
+
+ if (_st != NULL) {
+ lo_server_thread_free(_st);
+ _st = NULL;
+ }
+}
+
+
+void
+OSCReceiver::start()
+{
+ QueuedEventSource::start();
+
+ if (!_is_activated) {
+ lo_server_thread_start(_st);
+ _is_activated = true;
+ }
+
+ /* Waiting on the next liblo release
+ pthread_t lo_thread = lo_server_thread_get_thread(_st);
+
+ sched_param sp;
+ sp.sched_priority = 20;
+ int result = pthread_setschedparam(lo_thread, SCHED_FIFO, &sp);
+ if (!result)
+ cout << "[OSC] Set OSC thread to realtime scheduling (SCHED_FIFO, priority "
+ << sp.sched_priority << ")" << endl;
+ else
+ cout << "[OSC] Unable to set OSC thread to realtime scheduling ("
+ << strerror(result) << endl;
+ */
+}
+
+
+void
+OSCReceiver::stop()
+{
+ if (_is_activated) {
+ lo_server_thread_stop(_st);
+ cout << "[OSCReceiver] Stopped OSC server thread" << endl;
+ _is_activated = false;
+ }
+ QueuedEventSource::stop();
+}
+
+
+/** Create a new responder for this message, if necessary.
+ *
+ * This is based on the fact that the current responder is stored in a ref
+ * counted pointer, and events just take a reference to that. Thus, events
+ * may delete their responder if we've since switched to a new one, or the
+ * same one can stay around and serve a series of events. Reference counting
+ * is pretty sweet, eh?
+ *
+ * If this message came from the same source as the last message, no allocation
+ * of responders or lo_addresses or any of it needs to be done. Unfortunately
+ * the only way to check is by comparing URLs, because liblo addresses suck.
+ *
+ * Really, this entire thing is a basically just a crafty way of partially
+ * working around the fact that liblo addresses really suck. Oh well.
+ */
+int
+OSCReceiver::set_response_address_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg, void* user_data)
+{
+ OSCReceiver* const me = reinterpret_cast<OSCReceiver*>(user_data);
+
+ //cerr << "SET RESPONSE\n";
+
+ if (argc < 1 || types[0] != 'i') // Not a valid Om message
+ return 0; // Save liblo the trouble
+
+ //cerr << "** valid msg\n";
+
+ const int id = argv[0]->i;
+
+ // Need to respond
+ if (id != -1) {
+ const lo_address addr = lo_message_get_source(msg);
+ char* const url = lo_address_get_url(addr);
+
+ //cerr << "** need to respond\n";
+
+ // Currently have an OSC responder, check if it's still okay
+ if (me->_responder == me->_osc_responder) {
+ //cerr << "** osc responder\n";
+
+ if (!strcmp(url, me->_osc_responder->url())) {
+ // Nice one, same address
+ //cerr << "** Using cached response address, hooray" << endl;
+ } else {
+ // Shitty deal, make a new one
+ //cerr << "** Setting response address to " << url << "(2)" << endl;
+ me->_osc_responder = CountedPtr<OSCResponder>(new OSCResponder(id, url));
+ me->set_responder(me->_osc_responder);
+ // (responder takes ownership of url, no leak)
+ }
+
+ // Otherwise we have a NULL responder, definitely need to set a new one
+ } else {
+ //cerr << "** null responder\n";
+ me->_osc_responder = CountedPtr<OSCResponder>(new OSCResponder(id, url));
+ me->set_responder(me->_osc_responder);
+ //cerr << "** Setting response address to " << url << "(2)" << endl;
+ }
+
+ // Don't respond
+ } else {
+ me->disable_responses();
+ //cerr << "** Not responding." << endl;
+ }
+
+ // If this returns 0 no OSC commands will work
+ return 1;
+}
+
+
+void
+OSCReceiver::error_cb(int num, const char* msg, const char* path)
+{
+ cerr << "liblo server error " << num << " in path \"" << "\" - " << msg << endl;
+}
+
+
+/** \page engine_osc_namespace
+ * <p> \b /om/ping - Immediately sends a successful response to the given response id.
+ * \arg \b response-id (integer) </p> \n \n
+ */
+int
+OSCReceiver::m_ping_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg)
+{
+ _responder->respond_ok();
+
+ return 0;
+}
+
+
+/** \page engine_osc_namespace
+ * <p> \b /om/ping_slow - Sends response after going through the ("slow") event queue.
+ * \arg \b response-id (integer)
+ *
+ * \li See the documentation for /om/synth/set_port_value_slow for an explanation of how
+ * this differs from /om/ping. This is useful to send after sending a large cluster of
+ * events as a sentinel and wait on it's response, to know when the events are all
+ * finished processing.</p> \n \n
+ */
+int
+OSCReceiver::m_ping_slow_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg)
+{
+ ping();
+
+ return 0;
+}
+
+
+/** \page engine_osc_namespace
+ * <p> \b /om/engine/quit - Terminates the engine.
+ * \arg \b response-id (integer)
+ *
+ * \li Note that there is NO order guarantees with this command at all. You could
+ * send 10 messages then quit, and the quit reply could come immediately and the
+ * 10 messages would never get executed. </p> \n \n
+ */
+int
+OSCReceiver::m_quit_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg)
+{
+
+ quit();
+
+ return 0;
+}
+
+/** \page engine_osc_namespace
+ * <p> \b /om/engine/register_client - Registers a new client with the engine
+ * \arg \b response-id (integer)
+ *
+ * The incoming address will be used for the new registered client. If you
+ * want to register a different specific address, use the URL version.
+ */
+int
+OSCReceiver::m_register_client_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg)
+{
+ lo_address addr = lo_message_get_source(msg);
+
+ char* const url = lo_address_get_url(addr);
+ CountedPtr<ClientInterface> client(new OSCClient((const char*)url));
+ register_client(ClientKey(ClientKey::OSC_URL, (const char*)url), client);
+ free(url);
+
+ return 0;
+}
+
+
+/** \page engine_osc_namespace
+ * <p> \b /om/engine/unregister_client - Unregisters a client
+ * \arg \b response-id (integer) </p> \n \n
+ */
+int
+OSCReceiver::m_unregister_client_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg)
+{
+ lo_address addr = lo_message_get_source(msg);
+
+ char* url = lo_address_get_url(addr);
+ unregister_client(ClientKey(ClientKey::OSC_URL, url));
+ free(url);
+
+ return 0;
+}
+
+
+/** \page engine_osc_namespace
+ * <p> \b /om/engine/load_plugins - Locates all available plugins, making them available for use.
+ * \arg \b response-id (integer) </p> \n \n
+ */
+int
+OSCReceiver::m_load_plugins_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg)
+{
+ load_plugins();
+ return 0;
+}
+
+
+/** \page engine_osc_namespace
+ * <p> \b /om/engine/activate - Activate the engine (MIDI, audio, everything)
+ * \arg \b response-id (integer) </p>
+ *
+ * \li Note that you <b>must</b> send this message first if you want the engine to do
+ * anything at all - <em>including respond to your messages!</em> \n \n
+ */
+int
+OSCReceiver::m_engine_activate_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg)
+{
+ activate();
+ return 0;
+}
+
+
+/** \page engine_osc_namespace
+ * <p> \b /om/engine/deactivate - Deactivate the engine completely.
+ * \arg \b response-id (integer) </p> \n \n
+ */
+int
+OSCReceiver::m_engine_deactivate_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg)
+{
+ deactivate();
+ return 0;
+}
+
+
+/** \page engine_osc_namespace
+ * <p> \b /om/synth/create_patch - Creates a new, empty, toplevel patch.
+ * \arg \b response-id (integer)
+ * \arg \b patch-path (string) - Patch path (complete, ie /master/parent/new_patch)
+ * \arg \b poly (integer) - Patch's (internal) polyphony </p> \n \n
+ */
+int
+OSCReceiver::m_create_patch_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg)
+{
+ const char* patch_path = &argv[1]->s;
+ const int poly = argv[2]->i;
+
+ create_patch(patch_path, poly);
+
+ return 0;
+}
+
+
+/** \page engine_osc_namespace
+ * <p> \b /om/synth/rename - Rename an Object (only Nodes, for now)
+ * \arg \b response-id (integer)
+ * \arg \b path - Object's path
+ * \arg \b name - New name for object </p> \n \n
+ */
+int
+OSCReceiver::m_rename_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg)
+{
+ const char* object_path = &argv[1]->s;
+ const char* name = &argv[2]->s;
+
+ rename(object_path, name);
+
+ return 0;
+}
+
+
+/** \page engine_osc_namespace
+ * <p> \b /om/synth/enable_patch - Enable DSP processing of a patch
+ * \arg \b response-id (integer)
+ * \arg \b patch-path - Patch's path
+ */
+int
+OSCReceiver::m_enable_patch_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg)
+{
+ const char* patch_path = &argv[1]->s;
+
+ enable_patch(patch_path);
+
+ return 0;
+}
+
+
+/** \page engine_osc_namespace
+ * <p> \b /om/synth/disable_patch - Disable DSP processing of a patch
+ * \arg \b response-id (integer)
+ * \arg \b patch-path - Patch's path
+ */
+int
+OSCReceiver::m_disable_patch_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg)
+{
+ const char* patch_path = &argv[1]->s;
+
+ disable_patch(patch_path);
+
+ return 0;
+}
+
+
+/** \page engine_osc_namespace
+ * <p> \b /om/synth/clear_patch - Remove all nodes from a patch
+ * \arg \b response-id (integer)
+ * \arg \b patch-path - Patch's path
+ */
+int
+OSCReceiver::m_clear_patch_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg)
+{
+ const char* patch_path = &argv[1]->s;
+
+ clear_patch(patch_path);
+
+ return 0;
+}
+
+
+/** \page engine_osc_namespace
+ * <p> \b /om/synth/create_node - Add a node into a given patch (load a plugin by URI)
+ * \arg \b response-id (integer)
+ * \arg \b node-path (string) - Full path of the new node (ie. /patch2/subpatch/newnode)
+ * \arg \b type (string) - Plugin type ("Internal", "LV2", "DSSI", "LADSPA")
+ * \arg \b plug-uri (string) - URI of the plugin to load
+ * \arg \b poly (integer-boolean) - Whether node is polyphonic (0 = false, 1 = true) </p> \n \n
+ */
+int
+OSCReceiver::m_create_node_by_uri_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg)
+{
+ const char* node_path = &argv[1]->s;
+ const char* type = &argv[2]->s;
+ const char* plug_uri = &argv[3]->s;
+ const int poly = argv[4]->i;
+
+ // FIXME: make sure poly is valid
+
+ create_node(node_path, type, plug_uri, (poly == 1));
+
+ return 0;
+}
+
+
+/** \page engine_osc_namespace
+ * <p> \b /om/synth/create_node - Add a node into a given patch (load a plugin by libname, label)
+ * \arg \b response-id (integer)
+ * \arg \b node-path (string) - Full path of the new node (ie. /patch2/subpatch/newnode)
+ * \arg \b type (string) - Plugin type ("LADSPA" or "Internal")
+ * \arg \b lib-name (string) - Name of library where plugin resides (eg "cmt.so")
+ * \arg \b plug-label (string) - Label (ID) of plugin (eg "sine_fcaa")
+ * \arg \b poly (integer-boolean) - Whether node is polyphonic (0 = false, 1 = true)
+ *
+ * \li This is only here to provide backwards compatibility for old patches that store plugin
+ * references (particularly LADSPA) as libname, label.
+ * </p> \n \n
+ */
+int
+OSCReceiver::m_create_node_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg)
+{
+ /*
+
+ const char* node_path = &argv[1]->s;
+ const char* type = &argv[2]->s;
+ const char* lib_name = &argv[3]->s;
+ const char* plug_label = &argv[4]->s;
+ const int poly = argv[5]->i;
+ */
+ cerr << "LOAD NODE BY LIB LABEL\n";
+ return 0;
+ #if 0
+ // FIXME Event-ize
+
+ Plugin* plugin = new Plugin();
+ plugin->set_type(type);
+ plugin->lib_name(lib_name);
+ plugin->plug_label(plug_label);
+
+ if (poly != 0 && poly != 1) {
+ OSCResponder(addr).respond_error("Invalid poly parameter in create_node");
+ return 0;
+ }
+
+ add_node(node_path, plugin, (poly == 1));
+
+ return 0;
+ #endif
+}
+
+
+/** \page engine_osc_namespace
+ * <p> \b /om/synth/destroy - Removes (destroys) a Patch or a Node
+ * \arg \b response-id (integer)
+ * \arg \b node-path (string) - Full path of the object </p> \n \n
+ */
+int
+OSCReceiver::m_destroy_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg)
+{
+ const char* node_path = &argv[1]->s;
+
+ destroy(node_path);
+
+ return 0;
+}
+
+
+/** \page engine_osc_namespace
+ * <p> \b /om/synth/connect - Connects two ports (must be in the same patch)
+ * \arg \b response-id (integer)
+ * \arg \b src-port-path (string) - Full path of source port
+ * \arg \b dst-port-path (string) - Full path of destination port </p> \n \n
+ */
+int
+OSCReceiver::m_connect_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg)
+{
+ const char* src_port_path = &argv[1]->s;
+ const char* dst_port_path = &argv[2]->s;
+
+ connect(src_port_path, dst_port_path);
+
+ return 0;
+}
+
+
+/** \page engine_osc_namespace
+ * <p> \b /om/synth/disconnect - Disconnects two ports.
+ * \arg \b response-id (integer)
+ * \arg \b src-port-path (string) - Full path of source port
+ * \arg \b dst-port-path (string) - Full path of destination port </p> \n \n
+ */
+int
+OSCReceiver::m_disconnect_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg)
+{
+ const char* src_port_path = &argv[1]->s;
+ const char* dst_port_path = &argv[2]->s;
+
+ disconnect(src_port_path, dst_port_path);
+
+ return 0;
+}
+
+
+/** \page engine_osc_namespace
+ * <p> \b /om/synth/disconnect_all - Disconnect all connections to/from a node.
+ * \arg \b response-id (integer)
+ * \arg \b node-path (string) - Full path of node. </p> \n \n
+ */
+int
+OSCReceiver::m_disconnect_all_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg)
+{
+ const char* node_path = &argv[1]->s;
+
+ disconnect_all(node_path);
+
+ return 0;
+}
+
+
+/** \page engine_osc_namespace
+ * <p> \b /om/synth/set_port_value - Sets the value of a port for all voices (both AR and CR)
+ * \arg \b response-id (integer)
+ * \arg \b port-path (string) - Name of port
+ * \arg \b value (float) - Value to set port to
+ */
+int
+OSCReceiver::m_set_port_value_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg)
+{
+ const char* port_path = &argv[1]->s;
+ const float value = argv[2]->f;
+
+ set_port_value(port_path, value);
+
+ return 0;
+}
+
+
+/** \page engine_osc_namespace
+ * <p> \b /om/synth/set_port_value - Sets the value of a port for a specific voice (both AR and CR)
+ * \arg \b response-id (integer)
+ * \arg \b port-path (string) - Name of port
+ * \arg \b voice (integer) - Voice to set port value for
+ * \arg \b value (float) - Value to set port to
+ */
+int
+OSCReceiver::m_set_port_value_voice_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg)
+{
+ const char* port_path = &argv[1]->s;
+ const int voice = argv[2]->i;
+ const float value = argv[3]->f;
+
+ set_port_value(port_path, voice, value);
+
+ return 0;
+}
+
+
+/** \page engine_osc_namespace
+ * <p> \b /om/synth/set_port_value_slow - Sets the value of a port for all voices (as a QueuedEvent)
+ * \arg \b response-id (integer)
+ * \arg \b port-path (string) - Name of port
+ * \arg \b value (float) - Value to set port to
+ *
+ * \li This version exists so you can send it immediately after a QueuedEvent it may depend on (ie a
+ * node creation) and be sure it happens after the event (a normal set_port_value could beat the
+ * slow event and arrive out of order). </p> \n \n
+ */
+int
+OSCReceiver::m_set_port_value_slow_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg)
+{
+ const char* port_path = &argv[1]->s;
+ const float value = argv[2]->f;
+
+ set_port_value_queued(port_path, value);
+
+ return 0;
+}
+
+
+/** \page engine_osc_namespace
+ * <p> \b /om/synth/note_on - Triggers a note-on, just as if it came from MIDI
+ * \arg \b response-id (integer)
+ * \arg \b node-path (string) - Patch of Node to trigger (must be a trigger or note node)
+ * \arg \b note-num (int) - MIDI style note number (0-127)
+ * \arg \b velocity (int) - MIDI style velocity (0-127)</p> \n \n
+ */
+int
+OSCReceiver::m_note_on_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg)
+{
+ /*
+
+ const char* node_path = &argv[1]->s;
+ const uchar note_num = argv[2]->i;
+ const uchar velocity = argv[3]->i;
+ */
+ cerr << "FIXME: OSC note on\n";
+ //note_on(node_path, note_num, velocity);
+ return 0;
+}
+
+
+/** \page engine_osc_namespace
+ * <p> \b /om/synth/note_off - Triggers a note-off, just as if it came from MIDI
+ * \arg \b response-id (integer)
+ * \arg \b node-path (string) - Patch of Node to trigger (must be a trigger or note node)
+ * \arg \b note-num (int) - MIDI style note number (0-127)</p> \n \n
+ */
+int
+OSCReceiver::m_note_off_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg)
+{
+ /*
+
+ const char* patch_path = &argv[1]->s;
+ const uchar note_num = argv[2]->i;
+ */
+ cerr << "FIXME: OSC note off\n";
+ //note_off(patch_path, note_num);
+ return 0;
+}
+
+
+/** \page engine_osc_namespace
+ * <p> \b /om/synth/all_notes_off - Triggers a note-off for all voices, just as if it came from MIDI
+ * \arg \b response-id (integer)
+ * \arg \b patch-path (string) - Patch of patch to send event to </p> \n \n
+ */
+int
+OSCReceiver::m_all_notes_off_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg)
+{
+ /*
+
+ const char* patch_path = &argv[1]->s;
+ */
+ cerr << "FIXME: OSC all notes off\n";
+ //all_notes_off(patch_path);
+ return 0;
+}
+
+
+/** \page engine_osc_namespace
+ * <p> \b /om/synth/midi_learn - Initiate MIDI learn for a given (MIDI Control) Node
+ * \arg \b response-id (integer)
+ * \arg \b node-path (string) - Patch of the Node that should learn the next MIDI event.
+ *
+ * \li This of course will only do anything for MIDI control nodes. The node will learn the next MIDI
+ * event that arrives at it's MIDI input port - no behind the scenes voodoo happens here. It is planned
+ * that a plugin specification supporting arbitrary OSC commands for plugins will exist one day, and this
+ * method will go away completely. </p> \n \n
+ */
+int
+OSCReceiver::m_midi_learn_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg)
+{
+ const char* patch_path = &argv[1]->s;
+
+ midi_learn(patch_path);
+
+ return 0;
+}
+
+
+#ifdef HAVE_LASH
+/** \page engine_osc_namespace
+ * <p> \b /om/lash/restore_done - Notify LASH restoring is finished and connections can be made.
+ * \arg \b response-id (integer)
+ */
+int
+OSCReceiver::m_lash_restore_done_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg)
+{
+ lash_retore_done();
+
+ return 0;
+}
+#endif // HAVE_LASH
+
+
+/** \page engine_osc_namespace
+ * <p> \b /om/metadata/set - Sets a piece of metadata, associated with a synth-space object (node, etc)
+ * \arg \b response-id (integer)
+ * \arg \b object-path (string) - Full path of object to associate metadata with
+ * \arg \b key (string) - Key (index) for new piece of metadata
+ * \arg \b value (string) - Value of new piece of metadata
+ */
+int
+OSCReceiver::m_metadata_set_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg)
+{
+ const char* node_path = &argv[1]->s;
+ const char* key = &argv[2]->s;
+ const char* value = &argv[3]->s;
+
+ set_metadata(node_path, key, value);
+
+ return 0;
+}
+
+
+/** \page engine_osc_namespace
+ * <p> \b /om/metadata/responder - Requests the engine send a piece of metadata, associated with a synth-space object (node, etc)
+ * \arg \b response-id (integer)
+ * \arg \b object-path (string) - Full path of object metadata is associated with
+ * \arg \b key (string) - Key (index) for piece of metadata
+ *
+ * \li Reply will be sent to client registered with the source address of this message.</p> \n \n
+ */
+int
+OSCReceiver::m_metadata_get_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg)
+{
+ /*
+ const char* node_path = &argv[1]->s;
+ const char* key = &argv[2]->s;
+ */
+ cerr << "FIXME: OSC metadata request\n";
+ // FIXME: Equivalent?
+ /*
+ RequestMetadataEvent* ev = new RequestMetadataEvent(
+ new OSCResponder(ClientKey(addr)),
+ node_path, key);
+
+ */
+ return 0;
+}
+
+
+/** \page engine_osc_namespace
+ * <p> \b /om/responder/plugins - Requests the engine send a list of all known plugins.
+ * \arg \b response-id (integer)
+ *
+ * \li Reply will be sent to client registered with the source address of this message.</p> \n \n
+ */
+int
+OSCReceiver::m_request_plugins_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg)
+{
+ cerr << "REQUEST PLUGINS\n";
+
+ // FIXME
+ request_plugins();
+
+ return 0;
+}
+
+
+/** \page engine_osc_namespace
+ * <p> \b /om/responder/all_objects - Requests the engine send information about \em all objects (patches, nodes, etc)
+ * \arg \b response-id (integer)
+ *
+ * \li Reply will be sent to client registered with the source address of this message.</p> \n \n
+ */
+int
+OSCReceiver::m_request_all_objects_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg)
+{
+ request_all_objects();
+
+ return 0;
+}
+
+
+/** \page engine_osc_namespace
+ * <p> \b /om/responder/port_value - Requests the engine send the value of a port.
+ * \arg \b response-id (integer)
+ * \arg \b port-path (string) - Full path of port to send the value of </p> \n \n
+ *
+ * \li Reply will be sent to client registered with the source address of this message.</p> \n \n
+ */
+int
+OSCReceiver::m_request_port_value_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg)
+{
+ const char* port_path = &argv[1]->s;
+
+ request_port_value(port_path);
+
+ return 0;
+}
+
+
+#ifdef HAVE_DSSI
+int
+OSCReceiver::m_dssi_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg)
+{
+#if 0
+ string node_path(path);
+
+ if (node_path.substr(0, 5) != "/dssi")
+ return 1;
+
+ string command = node_path.substr(node_path.find_last_of("/")+1);
+ node_path = node_path.substr(5); // chop off leading "/dssi/"
+ node_path = node_path.substr(0, node_path.find_last_of("/")); // chop off command at end
+
+ //cout << "DSSI: Got message " << command << " for node " << node_path << endl;
+
+ QueuedEvent* ev = NULL;
+
+ if (command == "update" && !strcmp(types, "s"))
+ ev = new DSSIUpdateEvent(NULL, node_path, &argv[0]->s);
+ else if (command == "control" && !strcmp(types, "if"))
+ ev = new DSSIControlEvent(NULL, node_path, argv[0]->i, argv[1]->f);
+ else if (command == "configure" && ~strcmp(types, "ss"))
+ ev = new DSSIConfigureEvent(NULL, node_path, &argv[0]->s, &argv[1]->s);
+ else if (command == "program" && ~strcmp(types, "ii"))
+ ev = new DSSIProgramEvent(NULL, node_path, argv[0]->i, argv[1]->i);
+
+ if (ev != NULL)
+ push(ev);
+ else
+ cerr << "[OSCReceiver] Unknown DSSI command received: " << path << endl;
+#endif
+ return 0;
+}
+#endif
+
+
+// Static Callbacks //
+
+
+// Display incoming OSC messages (for debugging purposes)
+int
+OSCReceiver::generic_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg, void* user_data)
+{
+ printf("[OSCMsg] %s\n", path);
+
+ for (int i=0; i < argc; ++i) {
+ printf(" '%c' ", types[i]);
+ lo_arg_pp(lo_type(types[i]), argv[i]);
+ printf("\n");
+ }
+ printf("\n");
+
+ return 1; // not handled
+}
+
+
+int
+OSCReceiver::unknown_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg, void* user_data)
+{
+ cerr << "Unknown command " << path << " (" << types << "), sending error.\n";
+
+ string error_msg = "Unknown command: ";
+ error_msg.append(path).append(" ").append(types);
+
+ om->client_broadcaster()->send_error(error_msg);
+
+ return 0;
+}
+
+
+} // namespace Om
diff --git a/src/libs/engine/OSCReceiver.h b/src/libs/engine/OSCReceiver.h
new file mode 100644
index 00000000..b80c25c3
--- /dev/null
+++ b/src/libs/engine/OSCReceiver.h
@@ -0,0 +1,124 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef OSCRECEIVER_H
+#define OSCRECEIVER_H
+
+#include "config.h"
+#include <string>
+#include <lo/lo.h>
+#include "QueuedEngineInterface.h"
+#include "OSCResponder.h"
+using std::string;
+
+namespace Om {
+
+class JackDriver;
+class NodeFactory;
+class Patch;
+
+
+/* Some boilerplate killing macros... */
+#define LO_HANDLER_ARGS const char* path, const char* types, lo_arg** argv, int argc, lo_message msg
+
+/* Defines a static handler to be passed to lo_add_method, which is a trivial
+ * wrapper around a non-static method that does the real work. Makes a whoole
+ * lot of ugly boiler plate go away */
+#define LO_HANDLER(name) \
+int m_##name##_cb (LO_HANDLER_ARGS);\
+inline static int name##_cb(LO_HANDLER_ARGS, void* osc_receiver)\
+{ return ((OSCReceiver*)osc_receiver)->m_##name##_cb(path, types, argv, argc, msg); }
+
+
+
+/** Receives OSC messages from liblo.
+ *
+ * This inherits from QueuedEngineInterface and calls it's own functions
+ * via OSC. It's not actually a directly callable EngineInterface (it's
+ * callable via OSC...) so it is-implemented-as-a (privately inherits)
+ * QueuedEngineInterface.
+ *
+ * \ingroup engine
+ */
+class OSCReceiver : private QueuedEngineInterface
+{
+public:
+ OSCReceiver(size_t queue_size, const char* const port);
+ ~OSCReceiver();
+
+ void start();
+ void stop();
+
+private:
+ // Prevent copies (undefined)
+ OSCReceiver(const OSCReceiver&);
+ OSCReceiver& operator=(const OSCReceiver&);
+
+ static void error_cb(int num, const char* msg, const char* path);
+ static int set_response_address_cb(LO_HANDLER_ARGS, void* osc_receiver);
+ static int generic_cb(LO_HANDLER_ARGS, void* osc_receiver);
+ static int unknown_cb(LO_HANDLER_ARGS, void* osc_receiver);
+
+ LO_HANDLER(quit);
+ LO_HANDLER(ping);
+ LO_HANDLER(ping_slow);
+ LO_HANDLER(register_client);
+ LO_HANDLER(unregister_client);
+ LO_HANDLER(load_plugins);
+ LO_HANDLER(engine_activate);
+ LO_HANDLER(engine_deactivate);
+ LO_HANDLER(create_patch);
+ LO_HANDLER(rename);
+ LO_HANDLER(create_node);
+ LO_HANDLER(create_node_by_uri);
+ LO_HANDLER(enable_patch);
+ LO_HANDLER(disable_patch);
+ LO_HANDLER(clear_patch);
+ LO_HANDLER(destroy);
+ LO_HANDLER(connect);
+ LO_HANDLER(disconnect);
+ LO_HANDLER(disconnect_all);
+ LO_HANDLER(set_port_value);
+ LO_HANDLER(set_port_value_voice);
+ LO_HANDLER(set_port_value_slow);
+ LO_HANDLER(note_on);
+ LO_HANDLER(note_off);
+ LO_HANDLER(all_notes_off);
+ LO_HANDLER(midi_learn);
+ LO_HANDLER(metadata_get);
+ LO_HANDLER(metadata_set);
+ LO_HANDLER(request_plugins);
+ LO_HANDLER(request_all_objects);
+ LO_HANDLER(request_port_value);
+#ifdef HAVE_DSSI
+ LO_HANDLER(dssi);
+#endif
+#ifdef HAVE_LASH
+ LO_HANDLER(lash_restore_done);
+#endif
+
+ const char* const _port;
+ bool _is_activated;
+ lo_server_thread _st;
+
+ /** Cached OSC responder (for most recent incoming message) */
+ CountedPtr<OSCResponder> _osc_responder;
+};
+
+
+} // namespace Om
+
+#endif // OSCRECEIVER_H
diff --git a/src/libs/engine/OSCResponder.cpp b/src/libs/engine/OSCResponder.cpp
new file mode 100644
index 00000000..82e6b55d
--- /dev/null
+++ b/src/libs/engine/OSCResponder.cpp
@@ -0,0 +1,88 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "OSCResponder.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "ClientBroadcaster.h"
+#include "interface/ClientKey.h"
+#include <cstdlib>
+#include <iostream>
+#include <inttypes.h>
+#include <lo/lo.h>
+
+using std::cout; using std::cerr; using std::endl;
+
+namespace Om {
+
+
+/** Construct an OSCResponder from \a addr.
+ * Takes ownership of @a url.
+ */
+OSCResponder::OSCResponder(int32_t id, char* url)
+: Responder()
+, _id(id)
+, _url(url)
+, _addr(NULL)
+{
+ // If ID is -1 this shouldn't have even been created
+ assert(id != -1);
+}
+
+
+OSCResponder::~OSCResponder()
+{
+ //cerr << "DELETING " << _url << " RESPONDER\n";
+
+ if (_addr)
+ lo_address_free(_addr);
+}
+
+
+CountedPtr<Shared::ClientInterface>
+OSCResponder::find_client()
+{
+ return om->client_broadcaster()->client(ClientKey(ClientKey::OSC_URL, _url));
+}
+
+
+void
+OSCResponder::respond_ok()
+{
+ _addr = lo_address_new_from_url(_url);
+
+ //cerr << "OK " << _id << endl;
+ if (lo_send(_addr, "/om/response/ok", "i", _id) < 0) {
+ cerr << "Unable to send response " << _id << "! ("
+ << lo_address_errstr(_addr) << ")" << endl;
+ }
+}
+
+
+void
+OSCResponder::respond_error(const string& msg)
+{
+ _addr = lo_address_new_from_url(_url);
+
+ //cerr << "ERR " << _id << endl;
+ if (lo_send(_addr, "/om/response/error", "is",_id, msg.c_str()) < 0) {
+ cerr << "Unable to send response " << _id << "! ("
+ << lo_address_errstr(_addr) << endl;
+ }
+}
+
+} // namespace OM
+
diff --git a/src/libs/engine/OSCResponder.h b/src/libs/engine/OSCResponder.h
new file mode 100644
index 00000000..f579df98
--- /dev/null
+++ b/src/libs/engine/OSCResponder.h
@@ -0,0 +1,61 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef OSCRESPONDER_H
+#define OSCRESPONDER_H
+
+#include <inttypes.h>
+#include <memory>
+#include <lo/lo.h>
+#include "Responder.h"
+
+namespace Om {
+
+
+/** Responder for (liblo) OSC clients.
+ *
+ * OSC Clients use request IDs to be able to associate replies with sent
+ * events. If the ID is -1, a response will not be sent (and the overhead
+ * of searching for the client's record will be skipped). Any other integer
+ * is a valid response ID and will be responded to.
+ *
+ * Creation of the lo_address is deferred until needed to avoid bogging down
+ * the receiving thread as much as possible.
+ */
+class OSCResponder : public Responder
+{
+public:
+ OSCResponder(int32_t id, char* url);
+ ~OSCResponder();
+
+ CountedPtr<Shared::ClientInterface> find_client();
+
+ void respond_ok();
+ void respond_error(const string& msg);
+
+ const char* url() const { return _url; }
+
+private:
+ int32_t _id;
+ char* const _url;
+ lo_address _addr;
+};
+
+
+} // namespace Om
+
+#endif // OSCRESPONDER_H
+
diff --git a/src/libs/engine/ObjectSender.cpp b/src/libs/engine/ObjectSender.cpp
new file mode 100644
index 00000000..3db6915d
--- /dev/null
+++ b/src/libs/engine/ObjectSender.cpp
@@ -0,0 +1,207 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ObjectSender.h"
+#include "interface/ClientInterface.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "ObjectStore.h"
+#include "Patch.h"
+#include "Node.h"
+#include "Port.h"
+#include "PortInfo.h"
+#include "PortBase.h"
+#include "Connection.h"
+#include "NodeFactory.h"
+
+namespace Om {
+
+/** Send all currently existing objects to client.
+ */
+void
+ObjectSender::send_all(ClientInterface* client)
+{
+ for (Tree<OmObject*>::iterator i = om->object_store()->objects().begin();
+ i != om->object_store()->objects().end(); ++i)
+ if ((*i)->as_patch() != NULL && (*i)->parent() == NULL)
+ send_patch(client, (*i)->as_patch());
+ //(*i)->as_node()->send_creation_messages(client);
+
+}
+
+
+void
+ObjectSender::send_patch(ClientInterface* client, const Patch* patch)
+{
+ client->new_patch(patch->path(), patch->internal_poly());
+
+ for (List<Node*>::const_iterator j = patch->nodes().begin();
+ j != patch->nodes().end(); ++j) {
+ Node* const node = (*j);
+ Port* const port = node->as_port(); // NULL unless a bridge node
+ send_node(client, node);
+
+ usleep(100);
+
+ // If this is a bridge (input/output) node, send the patch control value as well
+ if (port && port->port_info()->is_control())
+ client->control_change(port->path(),
+ ((PortBase<sample>*)port)->buffer(0)->value_at(0));
+ }
+
+ for (List<Connection*>::const_iterator j = patch->connections().begin();
+ j != patch->connections().end(); ++j)
+ client->connection((*j)->src_port()->path(), (*j)->dst_port()->path());
+
+ // Send port information
+ /*for (size_t i=0; i < m_ports.size(); ++i) {
+ Port* const port = m_ports.at(i);
+
+ // Send metadata
+ const map<string, string>& data = port->metadata();
+ for (map<string, string>::const_iterator i = data.begin(); i != data.end(); ++i)
+ om->client_broadcaster()->send_metadata_update_to(client, port->path(), (*i).first, (*i).second);
+
+ if (port->port_info()->is_control())
+ om->client_broadcaster()->send_control_change_to(client, port->path(),
+ ((PortBase<sample>*)port)->buffer(0)->value_at(0));
+ }*/
+}
+
+
+void
+ObjectSender::send_node(ClientInterface* client, const Node* node)
+{
+ int polyphonic =
+ (node->poly() > 1
+ && node->poly() == node->parent_patch()->internal_poly()
+ ? 1 : 0);
+
+ assert(node->plugin()->uri().length() > 0);
+ assert(node->path().length() > 0);
+
+ client->bundle_begin();
+
+ // FIXME: bundleify
+
+ const Array<Port*>& ports = node->ports();
+
+ client->new_node(node->plugin()->type_string(), node->plugin()->uri(),
+ node->path(), polyphonic, ports.size());
+
+ // Send ports
+ for (size_t j=0; j < ports.size(); ++j) {
+ Port* const port = ports.at(j);
+ PortInfo* const info = port->port_info();
+
+ assert(port);
+ assert(info);
+
+ client->new_port(port->path(), info->type_string(), info->is_output());
+
+ /*m = lo_message_new();
+ lo_message_add_string(m, port->path().c_str());
+ lo_message_add_string(m, info->type_string().c_str());
+ lo_message_add_string(m, info->direction_string().c_str());
+ lo_message_add_string(m, info->hint_string().c_str());
+ lo_message_add_float(m, info->default_val());
+ lo_message_add_float(m, info->min_val());
+ lo_message_add_float(m, info->max_val());
+ lo_bundle_add_message(b, "/om/new_port", m);
+ msgs.push_back(m);*/
+
+ // If the bundle is getting very large, send it and start
+ // a new one
+ /*if (lo_bundle_length(b) > 1024) {
+ lo_send_bundle(_address, b);
+ lo_bundle_free(b);
+ b = lo_bundle_new(tt);
+ }*/
+ }
+
+ client->bundle_end();
+}
+
+
+void
+ObjectSender::send_port(ClientInterface* client, const Port* port)
+{
+ PortInfo* info = port->port_info();
+
+ client->new_port(port->path(), info->type_string(), info->is_output());
+
+ // Send metadata
+ const map<string, string>& data = port->metadata();
+ for (map<string, string>::const_iterator j = data.begin(); j != data.end(); ++j)
+ client->metadata_update(port->path(), (*j).first, (*j).second);
+}
+
+
+void
+ObjectSender::send_plugins(ClientInterface* client)
+{
+ om->node_factory()->lock_plugin_list();
+
+ const list<Plugin*>& plugs = om->node_factory()->plugins();
+
+/*
+ lo_timetag tt;
+ lo_timetag_now(&tt);
+ lo_bundle b = lo_bundle_new(tt);
+ lo_message m = lo_message_new();
+ list<lo_message> msgs;
+
+ lo_message_add_int32(m, plugs.size());
+ lo_bundle_add_message(b, "/om/num_plugins", m);
+ msgs.push_back(m);
+*/
+ for (list<Plugin*>::const_iterator j = plugs.begin(); j != plugs.end(); ++j) {
+ const Plugin* const p = *j;
+ client->new_plugin(p->type_string(), p->uri(), p->name());
+ }
+/*
+ plugin = (*j);
+ m = lo_message_new();
+
+ lo_message_add_string(m, plugin->type_string());
+ lo_message_add_string(m, plugin->uri().c_str());
+ lo_message_add_string(m, plugin->name().c_str());
+ lo_bundle_add_message(b, "/om/plugin", m);
+ msgs.push_back(m);
+ if (lo_bundle_length(b) > 1024) {
+ // FIXME FIXME FIXME dirty, dirty cast
+ lo_send_bundle(((OSCClient*)client)->address(), b);
+ lo_bundle_free(b);
+ b = lo_bundle_new(tt);
+ }
+ }*/
+/*
+ if (lo_bundle_length(b) > 0) {
+ // FIXME FIXME FIXME dirty, dirty cast
+ lo_send_bundle(((OSCClient*)client)->address(), b);
+ lo_bundle_free(b);
+ } else {
+ lo_bundle_free(b);
+ }
+ for (list<lo_bundle>::const_iterator i = msgs.begin(); i != msgs.end(); ++i)
+ lo_message_free(*i);
+*/
+ om->node_factory()->unlock_plugin_list();
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/ObjectSender.h b/src/libs/engine/ObjectSender.h
new file mode 100644
index 00000000..f97f1f9e
--- /dev/null
+++ b/src/libs/engine/ObjectSender.h
@@ -0,0 +1,55 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef OBJECTSENDER_H
+#define OBJECTSENDER_H
+
+namespace Om {
+
+namespace Shared {
+ class ClientInterface;
+} using Shared::ClientInterface;
+
+class Patch;
+class Node;
+class Port;
+
+
+/** Utility class for sending OmObjects to clients through ClientInterface.
+ *
+ * While ClientInterface is the direct low level message-based interface
+ * (protocol), this is used from the engine to easily send proper Objects
+ * with these messages (which is done in a few different parts of the code).
+ *
+ * Basically a serializer, except to calls on ClientInterface rather than
+ * eg a byte stream.
+ */
+class ObjectSender {
+public:
+
+ // FIXME: Make all object parameters const
+
+ static void send_all(ClientInterface* client);
+ static void send_patch(ClientInterface* client, const Patch* patch);
+ static void send_node(ClientInterface* client, const Node* node);
+ static void send_port(ClientInterface* client, const Port* port);
+ static void send_plugins(ClientInterface* client);
+};
+
+} // namespace Om
+
+#endif // OBJECTSENDER_H
+
diff --git a/src/libs/engine/ObjectStore.cpp b/src/libs/engine/ObjectStore.cpp
new file mode 100644
index 00000000..a1cf1287
--- /dev/null
+++ b/src/libs/engine/ObjectStore.cpp
@@ -0,0 +1,109 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ObjectStore.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "Patch.h"
+#include "Node.h"
+#include "Port.h"
+#include "List.h"
+#include "util/Path.h"
+#include "Tree.h"
+
+namespace Om {
+
+
+/** Find the Patch at the given path.
+ */
+Patch*
+ObjectStore::find_patch(const Path& path)
+{
+ OmObject* const object = find(path);
+ return (object == NULL) ? NULL : object->as_patch();
+}
+
+
+/** Find the Node at the given path.
+ */
+Node*
+ObjectStore::find_node(const Path& path)
+{
+ OmObject* const object = find(path);
+ return (object == NULL) ? NULL : object->as_node();
+}
+
+
+/** Find the Port at the given path.
+ */
+Port*
+ObjectStore::find_port(const Path& path)
+{
+ OmObject* const object = find(path);
+ return (object == NULL) ? NULL : object->as_port();
+}
+
+
+/** Find the Object at the given path.
+ */
+OmObject*
+ObjectStore::find(const Path& path)
+{
+ return m_objects.find(path);
+}
+
+
+/** Add an object to the store. Not realtime safe.
+ */
+void
+ObjectStore::add(OmObject* o)
+{
+ //cerr << "[ObjectStore] Adding " << o->path() << endl;
+ m_objects.insert(new TreeNode<OmObject*>(o->path(), o));
+}
+
+
+/** Add an object to the store. Not realtime safe.
+ */
+void
+ObjectStore::add(TreeNode<OmObject*>* tn)
+{
+ //cerr << "[ObjectStore] Adding " << tn->key() << endl;
+ m_objects.insert(tn);
+}
+
+
+/** Remove a patch from the store.
+ *
+ * It it the caller's responsibility to delete the returned ListNode.
+ *
+ * @returns TreeNode containing object removed on success, NULL if not found.
+ */
+TreeNode<OmObject*>*
+ObjectStore::remove(const string& path)
+{
+ TreeNode<OmObject*>* const removed = m_objects.remove(path);
+
+ if (removed == NULL)
+ cerr << "[ObjectStore] WARNING: Removing " << path << " failed." << endl;
+ //else
+ // cerr << "[ObjectStore] Removed " << path << endl;
+
+ return removed;
+}
+
+
+} // namespace Om
diff --git a/src/libs/engine/ObjectStore.h b/src/libs/engine/ObjectStore.h
new file mode 100644
index 00000000..8c71c002
--- /dev/null
+++ b/src/libs/engine/ObjectStore.h
@@ -0,0 +1,61 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef OBJECTSTORE_H
+#define OBJECTSTORE_H
+
+#include <string>
+#include "util/Path.h"
+#include "Tree.h"
+using std::string;
+
+namespace Om {
+
+class Patch;
+class Node;
+class Port;
+class OmObject;
+
+
+/** Storage for all OmObjects (tree of OmObject's sorted by path).
+ *
+ * All looking up in pre_process() methods (and anything else that isn't in-band
+ * with the audio thread) should use this (to read and modify the OmObject
+ * tree).
+ */
+class ObjectStore
+{
+public:
+ Patch* find_patch(const Path& path);
+ Node* find_node(const Path& path);
+ Port* find_port(const Path& path);
+ OmObject* find(const Path& path);
+
+ void add(OmObject* o);
+ void add(TreeNode<OmObject*>* o);
+ TreeNode<OmObject*>* remove(const string& key);
+
+ const Tree<OmObject*>& objects() { return m_objects; }
+
+private:
+ Tree<OmObject*> m_objects;
+};
+
+
+} // namespace Om
+
+#endif // OBJECTSTORE
diff --git a/src/libs/engine/Om.cpp b/src/libs/engine/Om.cpp
new file mode 100644
index 00000000..c04a7464
--- /dev/null
+++ b/src/libs/engine/Om.cpp
@@ -0,0 +1,36 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "config.h"
+#include "OmApp.h"
+#ifdef HAVE_LASH
+#include "LashDriver.h"
+#endif
+
+/** Om namespace.
+ *
+ * Everything internal to the Om engine lives in this namespace. Generally
+ * none of this code is used by clients - as special (possibly temporary)
+ * exceptions, some classes in src/common are used by clients but are part
+ * of the Om namespace.
+ */
+namespace Om
+{
+ OmApp* om = 0;
+#ifdef HAVE_LASH
+ LashDriver* lash_driver = 0;
+#endif
+}
diff --git a/src/libs/engine/Om.h b/src/libs/engine/Om.h
new file mode 100644
index 00000000..2fe3fd8c
--- /dev/null
+++ b/src/libs/engine/Om.h
@@ -0,0 +1,42 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef OM_H
+#define OM_H
+
+// FIXME: this dependency has to go
+#include "config.h"
+
+
+/* Things all over the place access various bits of Om through this.
+ * This was a bad design decision, it just evolved this way :/
+ * I may refactor it eventually, but it works.
+ */
+
+/** \defgroup engine Engine
+ */
+namespace Om
+{
+#ifdef HAVE_LASH
+ class LashDriver;
+ extern LashDriver* lash_driver;
+#endif
+ class OmApp;
+ extern OmApp* om;
+}
+
+#endif // OM_H
+
diff --git a/src/libs/engine/OmApp.cpp b/src/libs/engine/OmApp.cpp
new file mode 100644
index 00000000..ea7df1df
--- /dev/null
+++ b/src/libs/engine/OmApp.cpp
@@ -0,0 +1,231 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "Om.h"
+#include "OmApp.h"
+#include "config.h"
+#include "tuning.h"
+#include <sys/mman.h>
+#include <iostream>
+#include <unistd.h>
+#include "Event.h"
+#include "util/Queue.h"
+#include "JackAudioDriver.h"
+#include "NodeFactory.h"
+#include "OSCReceiver.h"
+#include "ClientBroadcaster.h"
+#include "Patch.h"
+#include "ObjectStore.h"
+#include "MaidObject.h"
+#include "Maid.h"
+#include "MidiDriver.h"
+#include "QueuedEventSource.h"
+#include "PostProcessor.h"
+#include "CreatePatchEvent.h"
+#include "EnablePatchEvent.h"
+#ifdef HAVE_JACK_MIDI
+#include "JackMidiDriver.h"
+#endif
+#ifdef HAVE_ALSA_MIDI
+#include "AlsaMidiDriver.h"
+#endif
+#ifdef HAVE_LASH
+#include "LashDriver.h"
+#endif
+using std::cout; using std::cerr; using std::endl;
+
+namespace Om {
+
+
+OmApp::OmApp(const char* port)
+: m_maid(new Maid(maid_queue_size)),
+ m_audio_driver(new JackAudioDriver()),
+#ifdef HAVE_JACK_MIDI
+ m_midi_driver(new JackMidiDriver(((JackAudioDriver*)m_audio_driver)->jack_client())),
+#elif HAVE_ALSA_MIDI
+ m_midi_driver(new AlsaMidiDriver()),
+#else
+ m_midi_driver(new DummyMidiDriver()),
+#endif
+ m_osc_receiver(new OSCReceiver(pre_processor_queue_size, port)),
+ m_client_broadcaster(new ClientBroadcaster()),
+ m_object_store(new ObjectStore()),
+ m_node_factory(new NodeFactory()),
+ m_event_queue(new Queue<Event*>(event_queue_size)),
+// m_pre_processor(new QueuedEventSource(pre_processor_queue_size)),
+ m_post_processor(new PostProcessor(post_processor_queue_size)),
+ m_quit_flag(false),
+ m_activated(false)
+{
+ mlock(m_audio_driver, sizeof(JackAudioDriver));
+ mlock(m_object_store, sizeof(ObjectStore));
+ mlock(m_osc_receiver, sizeof(OSCReceiver));
+#ifdef HAVE_ALSA_MIDI
+ mlock(m_midi_driver, sizeof(AlsaMidiDriver));
+#else
+ mlock(m_midi_driver, sizeof(DummyMidiDriver));
+#endif
+
+ m_osc_receiver->start();
+ m_post_processor->start();
+}
+
+
+OmApp::OmApp(const char* port, AudioDriver* audio_driver)
+: m_maid(new Maid(maid_queue_size)),
+ m_audio_driver(audio_driver),
+#ifdef HAVE_JACK_MIDI
+ m_midi_driver(new JackMidiDriver(((JackAudioDriver*)m_audio_driver)->jack_client())),
+#elif HAVE_ALSA_MIDI
+ m_midi_driver(new AlsaMidiDriver()),
+#else
+ m_midi_driver(new DummyMidiDriver()),
+#endif
+ m_osc_receiver(new OSCReceiver(pre_processor_queue_size, port)),
+ m_client_broadcaster(new ClientBroadcaster()),
+ m_object_store(new ObjectStore()),
+ m_node_factory(new NodeFactory()),
+ m_event_queue(new Queue<Event*>(event_queue_size)),
+ //m_pre_processor(new QueuedEventSource(pre_processor_queue_size)),
+ m_post_processor(new PostProcessor(post_processor_queue_size)),
+ m_quit_flag(false),
+ m_activated(false)
+{
+ mlock(m_audio_driver, sizeof(JackAudioDriver));
+ mlock(m_object_store, sizeof(ObjectStore));
+ mlock(m_osc_receiver, sizeof(OSCReceiver));
+#ifdef HAVE_ALSA_MIDI
+ mlock(m_midi_driver, sizeof(AlsaMidiDriver));
+#else
+ mlock(m_midi_driver, sizeof(DummyMidiDriver));
+#endif
+
+ m_osc_receiver->start();
+ m_post_processor->start();
+}
+
+
+OmApp::~OmApp()
+{
+ deactivate();
+
+ for (Tree<OmObject*>::iterator i = m_object_store->objects().begin();
+ i != m_object_store->objects().end(); ++i) {
+ if ((*i)->parent() == NULL)
+ delete (*i);
+ }
+
+ delete m_object_store;
+ delete m_client_broadcaster;
+ delete m_osc_receiver;
+ delete m_node_factory;
+ delete m_midi_driver;
+ delete m_audio_driver;
+
+ delete m_maid;
+
+ munlockall();
+}
+
+
+/* driver() template specializations.
+ * Due to the lack of RTTI, this needs to be implemented manually like this.
+ * If more types/drivers start getting added, it may be worth it to enable
+ * RTTI and put all the drivers into a map with typeid's as the key. That's
+ * more elegant and extensible, but this is faster and simpler - for now.
+ */
+template<>
+Driver<MidiMessage>* OmApp::driver<MidiMessage>() { return m_midi_driver; }
+template<>
+Driver<sample>* OmApp::driver<sample>() { return m_audio_driver; }
+
+
+int
+OmApp::main()
+{
+ // Loop until quit flag is set (by OSCReceiver)
+ while ( ! m_quit_flag) {
+ nanosleep(&main_rate, NULL);
+#ifdef HAVE_LASH
+ // Process any pending LASH events
+ if (lash_driver->enabled())
+ lash_driver->process_events();
+#endif
+ // Run the maid (garbage collector)
+ m_maid->cleanup();
+ }
+ cout << "[Main] Done main loop." << endl;
+
+ if (m_activated)
+ deactivate();
+
+ sleep(1);
+ cout << "[Main] Om exiting..." << endl;
+
+ return 0;
+}
+
+
+void
+OmApp::activate()
+{
+ if (m_activated)
+ return;
+
+ // Create root patch
+ CreatePatchEvent create_ev(CountedPtr<Responder>(new Responder()), "/", 1);
+ create_ev.pre_process();
+ create_ev.execute(0);
+ create_ev.post_process();
+ EnablePatchEvent enable_ev(CountedPtr<Responder>(new Responder()), "/");
+ enable_ev.pre_process();
+ enable_ev.execute(0);
+ enable_ev.post_process();
+
+ assert(m_audio_driver->root_patch() != NULL);
+
+ m_audio_driver->activate();
+#ifdef HAVE_ALSA_MIDI
+ m_midi_driver->activate();
+#endif
+ m_activated = true;
+}
+
+
+void
+OmApp::deactivate()
+{
+ if (!m_activated)
+ return;
+
+ m_audio_driver->root_patch()->process(false);
+
+ for (Tree<OmObject*>::iterator i = m_object_store->objects().begin();
+ i != m_object_store->objects().end(); ++i)
+ if ((*i)->as_node() != NULL && (*i)->as_node()->parent() == NULL)
+ (*i)->as_node()->deactivate();
+
+ if (m_midi_driver != NULL)
+ m_midi_driver->deactivate();
+
+ m_osc_receiver->stop();
+ m_audio_driver->deactivate();
+
+ m_activated = false;
+}
+
+
+} // namespace Om
diff --git a/src/libs/engine/OmApp.h b/src/libs/engine/OmApp.h
new file mode 100644
index 00000000..0fe49b81
--- /dev/null
+++ b/src/libs/engine/OmApp.h
@@ -0,0 +1,99 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef OMAPP_H
+#define OMAPP_H
+
+template<typename T> class Queue;
+class Maid;
+
+namespace Om {
+
+class AudioDriver;
+class MidiDriver;
+class NodeFactory;
+class OSCReceiver;
+class ClientBroadcaster;
+class Patch;
+class ObjectStore;
+class EventSource;
+class PostProcessor;
+class Event;
+class QueuedEvent;
+template <typename T> class Driver;
+
+
+/** The main class for Om, the whole app lives in here
+ *
+ * This class should not exist.
+ *
+ * \ingroup engine
+ */
+class OmApp
+{
+public:
+ OmApp(const char* const port);
+ OmApp(const char* const port, AudioDriver* audio_driver);
+ ~OmApp();
+
+ int main();
+
+ /** Set the quit flag that should kill all threads and exit cleanly.
+ * Note that it will take some time. */
+ void quit() { m_quit_flag = true; }
+
+ void activate();
+ void deactivate();
+
+ Maid* maid() const { return m_maid; }
+ AudioDriver* audio_driver() const { return m_audio_driver; }
+ MidiDriver* midi_driver() const { return m_midi_driver; }
+ OSCReceiver* osc_receiver() const { return m_osc_receiver; }
+ ClientBroadcaster* client_broadcaster() const { return m_client_broadcaster; }
+ ObjectStore* object_store() const { return m_object_store; }
+ NodeFactory* node_factory() const { return m_node_factory; }
+ Queue<Event*>* event_queue() const { return m_event_queue; }
+ //EventSource* pre_processor() const { return m_pre_processor; }
+ PostProcessor* post_processor() const { return m_post_processor; }
+
+ /** Return the active driver for the given (template parameter) type.
+ * This is a hook for BridgeNode. See OmApp.cpp for specializations. */
+ template<typename T> Driver<T>* driver();
+
+private:
+ // Prevent copies
+ OmApp(const OmApp&);
+ OmApp& operator=(const OmApp&);
+
+ Maid* m_maid;
+ AudioDriver* m_audio_driver;
+ MidiDriver* m_midi_driver;
+ OSCReceiver* m_osc_receiver;
+ ClientBroadcaster* m_client_broadcaster;
+ ObjectStore* m_object_store;
+ NodeFactory* m_node_factory;
+ Queue<Event*>* m_event_queue;
+ //EventSource* m_pre_processor;
+ PostProcessor* m_post_processor;
+
+ bool m_quit_flag;
+ bool m_activated;
+};
+
+
+} // namespace Om
+
+#endif // OMAPP_H
diff --git a/src/libs/engine/OmInProcess.cpp b/src/libs/engine/OmInProcess.cpp
new file mode 100644
index 00000000..971a1316
--- /dev/null
+++ b/src/libs/engine/OmInProcess.cpp
@@ -0,0 +1,74 @@
+/* This file is part of Om. Copyright (C) 2006 Mario Lang.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <memory.h>
+#include <jack/jack.h>
+
+#include "Om.h"
+#include "OmApp.h"
+#include "OSCReceiver.h"
+#include "JackAudioDriver.h"
+#ifdef HAVE_LASH
+#include "LashDriver.h"
+#endif
+
+extern "C"
+{
+ int jack_initialize(jack_client_t* client, const char* load_init);
+ void jack_finish(void* arg);
+}
+
+
+void*
+run_main(void* arg)
+{
+ Om::om->main();
+#ifdef HAVE_LASH
+
+ delete Om::lash_driver;
+#endif
+
+ delete Om::om;
+ return 0;
+}
+
+
+pthread_t main_thread;
+
+
+int
+jack_initialize(jack_client_t* client, const char* load_init)
+{
+ if ((Om::om = new Om::OmApp(load_init, new Om::JackAudioDriver(client))) != NULL) {
+ pthread_create(&main_thread, NULL, run_main, NULL);
+ return 0; // Success
+ } else {
+ return 1;
+ }
+}
+
+
+void
+jack_finish(void* arg)
+{
+ void* ret;
+ Om::om->quit();
+ pthread_join(main_thread, &ret);
+}
+
diff --git a/src/libs/engine/OmObject.h b/src/libs/engine/OmObject.h
new file mode 100644
index 00000000..606a3dba
--- /dev/null
+++ b/src/libs/engine/OmObject.h
@@ -0,0 +1,115 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef OMOBJECT_H
+#define OMOBJECT_H
+
+#include <string>
+#include <map>
+#include <cstddef>
+#include <cassert>
+#include "MaidObject.h"
+#include "util/Path.h"
+
+using std::string; using std::map;
+
+namespace Om {
+
+class Patch;
+class Node;
+class Port;
+
+
+/** An object in the "synth space" of Om - Patch, Node, Port, etc.
+ *
+ * Each of these is a MaidObject and so can be deleted in a realtime safe
+ * way from anywhere, and they all have a map of metadata for clients to store
+ * arbitrary values in (which the engine puts no significance to whatsoever).
+ *
+ * \ingroup engine
+ */
+class OmObject : public MaidObject
+{
+public:
+ OmObject(OmObject* parent, const string& name)
+ : m_parent(parent), m_name(name)
+ {
+ assert(parent == NULL || m_name.length() > 0);
+ assert(parent == NULL || m_name.find("/") == string::npos);
+ //assert(((string)path()).find("//") == string::npos);
+ }
+
+ virtual ~OmObject() {}
+
+ // Ghetto home-brew RTTI
+ virtual Patch* as_patch() { return NULL; }
+ virtual Node* as_node() { return NULL; }
+ virtual Port* as_port() { return NULL; }
+
+ OmObject* parent() const { return m_parent; }
+
+ inline const string& name() const { return m_name; }
+
+ virtual void set_path(const Path& new_path) {
+ m_name = new_path.name();
+ assert(m_name.find("/") == string::npos);
+ }
+
+ void set_metadata(const string& key, const string& value) { m_metadata[key] = value; }
+ const string& get_metadata(const string& key) {
+ static const string empty_string = "";
+ map<string, string>::iterator i = m_metadata.find(key);
+ if (i != m_metadata.end())
+ return (*i).second;
+ else
+ return empty_string;
+ }
+
+ const map<string, string>& metadata() const { return m_metadata; }
+
+ inline const Path path() const {
+ if (m_parent == NULL)
+ return Path(string("/").append(m_name));
+ else if (m_parent->path() == "/")
+ return Path(string("/").append(m_name));
+ else
+ return Path(m_parent->path() +"/"+ m_name);
+ }
+
+ /** Patch and Node override this to recursively add their children. */
+ virtual void add_to_store() = 0;
+
+ /** Patch and Node override this to recursively remove their children. */
+ virtual void remove_from_store() = 0;
+
+protected:
+ OmObject() {}
+
+ OmObject* m_parent;
+ string m_name;
+
+private:
+ // Prevent copies (undefined)
+ OmObject(const OmObject&);
+ OmObject& operator=(const OmObject& copy);
+
+ map<string, string> m_metadata;
+};
+
+
+} // namespace Om
+
+#endif // OMOBJECT_H
diff --git a/src/libs/engine/OutputPort.cpp b/src/libs/engine/OutputPort.cpp
new file mode 100644
index 00000000..bf971919
--- /dev/null
+++ b/src/libs/engine/OutputPort.cpp
@@ -0,0 +1,51 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "OutputPort.h"
+#include "InputPort.h"
+#include "PortInfo.h"
+#include <cassert>
+
+namespace Om {
+
+template<typename T>
+OutputPort<T>::OutputPort(Node* node, const string& name, size_t index, size_t poly, PortInfo* port_info, size_t buffer_size)
+: PortBase<T>(node, name, index, poly, port_info, buffer_size)
+{
+ assert(port_info->is_output() && !port_info->is_input());
+}
+template OutputPort<sample>::OutputPort(Node* node, const string& name, size_t index, size_t poly, PortInfo* port_info, size_t buffer_size);
+template OutputPort<MidiMessage>::OutputPort(Node* node, const string& name, size_t index, size_t poly, PortInfo* port_info, size_t buffer_size);
+
+
+template<typename T>
+void
+OutputPort<T>::set_tied_port(InputPort<T>* port)
+{
+ assert(!m_is_tied);
+ assert(m_tied_port == NULL);
+ assert(static_cast<PortBase<T>*>(port) != static_cast<PortBase<T>*>(this));
+ assert(port != NULL);
+
+ m_is_tied = true;
+ m_tied_port = (PortBase<T>*)port;
+}
+template void OutputPort<sample>::set_tied_port(InputPort<sample>* port);
+template void OutputPort<MidiMessage>::set_tied_port(InputPort<MidiMessage>* port);
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/OutputPort.h b/src/libs/engine/OutputPort.h
new file mode 100644
index 00000000..90488a7f
--- /dev/null
+++ b/src/libs/engine/OutputPort.h
@@ -0,0 +1,64 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef OUTPUTPORT_H
+#define OUTPUTPORT_H
+
+#include <string>
+#include <cstdlib>
+#include "PortBase.h"
+#include "util/types.h"
+
+namespace Om {
+
+template <typename T> class InputPort;
+
+
+/** An output port.
+ *
+ * Output ports always have a locally allocated buffer, and buffer() will
+ * always return that buffer. (This is very different from InputPort)
+ *
+ * This class actually adds no functionality to Port whatsoever right now,
+ * it will in the future when more advanced port types exist, and it makes
+ * things clearer throughout the engine.
+ *
+ * \ingroup engine
+ */
+template <typename T>
+class OutputPort : public PortBase<T>
+{
+public:
+ OutputPort(Node* node, const string& name, size_t index, size_t poly, PortInfo* port_info, size_t buffer_size);
+ virtual ~OutputPort() {}
+
+ void set_tied_port(InputPort<T>* port);
+
+private:
+ // Prevent copies (undefined)
+ OutputPort(const OutputPort& copy);
+ OutputPort<T>& operator=(const OutputPort<T>&);
+
+ using PortBase<T>::m_is_tied;
+ using PortBase<T>::m_tied_port;
+};
+
+
+template class OutputPort<sample>;
+
+} // namespace Om
+
+#endif // OUTPUTPORT_H
diff --git a/src/libs/engine/Patch.cpp b/src/libs/engine/Patch.cpp
new file mode 100644
index 00000000..be938cae
--- /dev/null
+++ b/src/libs/engine/Patch.cpp
@@ -0,0 +1,356 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <cassert>
+#include <cmath>
+#include <iostream>
+#include "Node.h"
+#include "Patch.h"
+#include "Plugin.h"
+#include "Port.h"
+#include "PortInfo.h"
+#include "ClientBroadcaster.h"
+#include "InternalNode.h"
+#include "Connection.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "PortBase.h"
+#include "ObjectStore.h"
+#include "interface/ClientInterface.h"
+
+using std::cerr; using std::cout; using std::endl;
+
+namespace Om {
+
+
+Patch::Patch(const string& path, size_t poly, Patch* parent, samplerate srate, size_t buffer_size, size_t internal_poly)
+: NodeBase(path, poly, parent, srate, buffer_size),
+ m_internal_poly(internal_poly),
+ m_process_order(NULL),
+ m_process(false)
+{
+ assert(internal_poly >= 1);
+
+ m_plugin.type(Plugin::Patch);
+ m_plugin.lib_path("");
+ m_plugin.plug_label("om_patch");
+ m_plugin.name("Om patch");
+
+ //std::cerr << "Creating patch " << m_name << ", poly = " << poly
+ // << ", internal poly = " << internal_poly << std::endl;
+}
+
+
+Patch::~Patch()
+{
+ assert(!m_activated);
+
+ for (List<Connection*>::iterator i = m_connections.begin(); i != m_connections.end(); ++i) {
+ delete (*i);
+ delete m_connections.remove(i);
+ }
+
+ for (List<Node*>::iterator i = m_nodes.begin(); i != m_nodes.end(); ++i) {
+ assert(!(*i)->activated());
+ delete (*i);
+ delete m_nodes.remove(i);
+ }
+
+ delete m_process_order;
+}
+
+
+void
+Patch::activate()
+{
+ NodeBase::activate();
+
+ for (List<Node*>::iterator i = m_nodes.begin(); i != m_nodes.end(); ++i)
+ (*i)->activate();
+
+ assert(m_activated);
+}
+
+
+void
+Patch::deactivate()
+{
+ if (m_activated) {
+
+ NodeBase::deactivate();
+
+ for (List<Node*>::iterator i = m_nodes.begin(); i != m_nodes.end(); ++i) {
+ if ((*i)->activated())
+ (*i)->deactivate();
+ assert(!(*i)->activated());
+ }
+ }
+ assert(!m_activated);
+}
+
+
+void
+Patch::process(bool b)
+{
+ if (!b) {
+ // Write output buffers to 0
+ for (List<InternalNode*>::iterator i = m_bridge_nodes.begin(); i != m_bridge_nodes.end(); ++i) {
+ assert((*i)->as_port() != NULL);
+ if ((*i)->as_port()->port_info()->is_output())
+ (*i)->as_port()->clear_buffers();
+ }
+ }
+ m_process = b;
+}
+
+
+/** Run the patch for the specified number of frames.
+ *
+ * Calls all Nodes in the order m_process_order specifies.
+ */
+inline void
+Patch::run(size_t nframes)
+{
+ if (m_process_order == NULL || !m_process)
+ return;
+
+ // Prepare all ports
+ for (List<InternalNode*>::iterator i = m_bridge_nodes.begin(); i != m_bridge_nodes.end(); ++i)
+ (*i)->as_port()->prepare_buffers(nframes);
+
+ // Run all nodes
+ for (size_t i=0; i < m_process_order->size(); ++i) {
+ // Could be a gap due to a node removal event (see RemoveNodeEvent.cpp)
+ // If you're thinking this isn't very nice, you're right.
+ if (m_process_order->at(i) != NULL)
+ m_process_order->at(i)->run(nframes);
+ }
+}
+
+
+/** Returns the number of ports.
+ *
+ * Needs to override the NodeBase implementation since a Patch's ports are really
+ * just it's input and output nodes' ports.
+ */
+size_t
+Patch::num_ports() const
+{
+ return m_bridge_nodes.size();
+}
+
+
+#if 0
+void
+Patch::send_creation_messages(ClientInterface* client) const
+{
+ cerr << "FIXME: creation\n";
+
+ om->client_broadcaster()->send_patch_to(client, this);
+
+ for (List<Node*>::const_iterator j = m_nodes.begin(); j != m_nodes.end(); ++j) {
+ Node* node = (*j);
+ Port* port = node->as_port(); // NULL unless a bridge node
+ node->send_creation_messages(client);
+
+ usleep(100);
+
+ // If this is a bridge (input/output) node, send the patch control value as well
+ if (port != NULL && port->port_info()->is_control())
+ om->client_broadcaster()->send_control_change_to(client, port->path(),
+ ((PortBase<sample>*)port)->buffer(0)->value_at(0));
+ }
+
+ for (List<Connection*>::const_iterator j = m_connections.begin(); j != m_connections.end(); ++j) {
+ om->client_broadcaster()->send_connection_to(client, *j);
+ }
+
+ // Send port information
+ /*for (size_t i=0; i < m_ports.size(); ++i) {
+ Port* const port = m_ports.at(i);
+
+ // Send metadata
+ const map<string, string>& data = port->metadata();
+ for (map<string, string>::const_iterator i = data.begin(); i != data.end(); ++i)
+ om->client_broadcaster()->send_metadata_update_to(client, port->path(), (*i).first, (*i).second);
+
+ if (port->port_info()->is_control())
+ om->client_broadcaster()->send_control_change_to(client, port->path(),
+ ((PortBase<sample>*)port)->buffer(0)->value_at(0));
+ }*/
+}
+#endif
+
+
+void
+Patch::add_to_store()
+{
+ // Add self and ports
+ NodeBase::add_to_store();
+
+ // Add nodes
+ for (List<Node*>::iterator j = m_nodes.begin(); j != m_nodes.end(); ++j)
+ (*j)->add_to_store();
+}
+
+
+void
+Patch::remove_from_store()
+{
+ // Remove self and ports
+ NodeBase::remove_from_store();
+
+ // Remove nodes
+ for (List<Node*>::iterator j = m_nodes.begin(); j != m_nodes.end(); ++j) {
+ (*j)->remove_from_store();
+ assert(om->object_store()->find((*j)->path()) == NULL);
+ }
+}
+
+
+// Patch specific stuff
+
+
+void
+Patch::add_node(ListNode<Node*>* ln)
+{
+ assert(ln != NULL);
+ assert(ln->elem() != NULL);
+ assert(ln->elem()->parent_patch() == this);
+ assert(ln->elem()->poly() == m_internal_poly || ln->elem()->poly() == 1);
+
+ m_nodes.push_back(ln);
+}
+
+
+ListNode<Node*>*
+Patch::remove_node(const string& name)
+{
+ for (List<Node*>::iterator i = m_nodes.begin(); i != m_nodes.end(); ++i)
+ if ((*i)->name() == name)
+ return m_nodes.remove(i);
+
+ return NULL;
+}
+
+
+/** Remove a connection. Realtime safe.
+ */
+ListNode<Connection*>*
+Patch::remove_connection(const Port* src_port, const Port* dst_port)
+{
+ bool found = false;
+ ListNode<Connection*>* connection = NULL;
+ for (List<Connection*>::iterator i = m_connections.begin(); i != m_connections.end(); ++i) {
+ if ((*i)->src_port() == src_port && (*i)->dst_port() == dst_port) {
+ connection = m_connections.remove(i);
+ found = true;
+ }
+ }
+
+ if ( ! found)
+ cerr << "WARNING: [Patch::remove_connection] Connection not found !" << endl;
+
+ return connection;
+}
+
+
+/** Remove a bridge_node. Realtime safe.
+ */
+ListNode<InternalNode*>*
+Patch::remove_bridge_node(const InternalNode* node)
+{
+ bool found = false;
+ ListNode<InternalNode*>* bridge_node = NULL;
+ for (List<InternalNode*>::iterator i = m_bridge_nodes.begin(); i != m_bridge_nodes.end(); ++i) {
+ if ((*i) == node) {
+ bridge_node = m_bridge_nodes.remove(i);
+ found = true;
+ }
+ }
+
+ if ( ! found)
+ cerr << "WARNING: [Patch::remove_bridge_node] InternalNode not found !" << endl;
+
+ return bridge_node;
+}
+
+
+/** Find the process order for this Patch.
+ *
+ * The process order is a flat list that the patch will execute in order
+ * when it's run() method is called. Return value is a newly allocated list
+ * which the caller is reponsible to delete. Note that this function does
+ * NOT actually set the process order, it is returned so it can be inserted
+ * at the beginning of an audio cycle (by various Events).
+ *
+ * This function is not realtime safe, due to the use of the List<Node*> iterator
+ */
+Array<Node*>*
+Patch::build_process_order() const
+{
+ Node* node = NULL;
+ Array<Node*>* const process_order = new Array<Node*>(m_nodes.size());
+
+ for (List<Node*>::const_iterator i = m_nodes.begin(); i != m_nodes.end(); ++i)
+ (*i)->traversed(false);
+
+ // Traverse backwards starting at outputs
+ for (List<InternalNode*>::const_iterator i = m_bridge_nodes.begin(); i != m_bridge_nodes.end(); ++i) {
+ node = (*i);
+ assert(node->as_port() != NULL);
+
+ // If the output node has been disconnected and has no connections left, don't traverse
+ // into it so it's not in the process order (and can be removed w/o flaming segfault death)
+ if (node->as_port()->port_info()->is_output() && node->providers()->size() > 0)
+ build_process_order_recursive(node, process_order);
+ }
+
+ // Add any nodes that weren't hit by the traversal (disjoint nodes)
+ for (List<Node*>::const_iterator i = m_nodes.begin(); i != m_nodes.end(); ++i) {
+ node = (*i);
+ if ( ! node->traversed()) {
+ process_order->push_back(*i);
+ node->traversed(true);
+ }
+ }
+
+ assert(process_order->size() == m_nodes.size());
+
+ return process_order;
+}
+
+
+/** Rename this Patch.
+ *
+ * This is responsible for updating the ObjectStore so the Patch can be
+ * found at it's new path, as well as all it's children.
+ */
+void
+Patch::set_path(const Path& new_path)
+{
+ const Path old_path = path();
+
+ // Update nodes
+ for (List<Node*>::iterator i = m_nodes.begin(); i != m_nodes.end(); ++i)
+ (*i)->set_path(new_path.base_path() + (*i)->name());
+
+ // Update self
+ NodeBase::set_path(new_path);
+}
+
+
+} // namespace Om
diff --git a/src/libs/engine/Patch.h b/src/libs/engine/Patch.h
new file mode 100644
index 00000000..e2a1ed48
--- /dev/null
+++ b/src/libs/engine/Patch.h
@@ -0,0 +1,136 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PATCH_H
+#define PATCH_H
+
+#include <cstdlib>
+#include <string>
+#include "NodeBase.h"
+#include "Plugin.h"
+#include "List.h"
+
+using std::string;
+
+template <typename T> class Array;
+
+namespace Om {
+
+class Connection;
+class InternalNode;
+namespace Shared {
+ class ClientInterface;
+} using Shared::ClientInterface;
+
+
+/** A group of nodes in a graph, possibly polyphonic.
+ *
+ * Note that this is also a Node, just one which contains Nodes.
+ * Therefore infinite subpatching is possible, of polyphonic
+ * patches of polyphonic nodes etc. etc.
+ *
+ * \ingroup engine
+ */
+class Patch : public NodeBase
+{
+public:
+ Patch(const string& name, size_t poly, Patch* parent, samplerate srate, size_t buffer_size, size_t local_poly);
+ virtual ~Patch();
+
+ Patch* as_patch() { return static_cast<Patch*>(this); }
+
+ void activate();
+ void deactivate();
+
+ void run(size_t nframes);
+
+ size_t num_ports() const;
+
+ //void send_creation_messages(ClientInterface* client) const;
+
+ void add_to_store();
+ void remove_from_store();
+
+ void set_path(const Path& new_path);
+
+ // Patch specific stuff not inherited from Node
+
+ void add_node(ListNode<Node*>* tn);
+ ListNode<Node*>* remove_node(const string& name);
+
+ List<Node*>& nodes() { return m_nodes; }
+ List<Connection*>& connections() { return m_connections; }
+
+ const List<Node*>& nodes() const { return m_nodes; }
+ const List<Connection*>& connections() const { return m_connections; }
+
+ void add_bridge_node(ListNode<InternalNode*>* n) { m_bridge_nodes.push_back(n); }
+ ListNode<InternalNode*>* remove_bridge_node(const InternalNode* n);
+
+ void add_connection(ListNode<Connection*>* c) { m_connections.push_back(c); }
+ ListNode<Connection*>* remove_connection(const Port* src_port, const Port* dst_port);
+
+ Array<Node*>* process_order() { return m_process_order; }
+ void process_order(Array<Node*>* po) { m_process_order = po; }
+
+ Array<Node*>* build_process_order() const;
+ inline void build_process_order_recursive(Node* n, Array<Node*>* order) const;
+
+ /** Whether to run this patch's DSP in the audio thread */
+ bool process() const { return m_process; }
+ void process(bool b);
+
+ size_t internal_poly() const { return m_internal_poly; }
+
+ const Plugin* plugin() const { return &m_plugin; }
+ void plugin(const Plugin* const) { exit(EXIT_FAILURE); }
+
+private:
+ // Prevent copies (undefined)
+ Patch(const Patch&);
+ Patch& operator=(const Patch&);
+
+ size_t m_internal_poly;
+ Array<Node*>* m_process_order;
+ List<Connection*> m_connections;
+ List<InternalNode*> m_bridge_nodes; ///< Inputs and outputs
+ List<Node*> m_nodes;
+ bool m_process;
+
+ Plugin m_plugin;
+};
+
+
+
+/** Private helper for build_process_order */
+inline void
+Patch::build_process_order_recursive(Node* n, Array<Node*>* order) const
+{
+ if (n == NULL || n->traversed()) return;
+ n->traversed(true);
+ assert(order != NULL);
+
+ for (List<Node*>::iterator i = n->providers()->begin(); i != n->providers()->end(); ++i)
+ if ( ! (*i)->traversed() )
+ build_process_order_recursive((*i), order);
+
+ order->push_back(n);
+}
+
+
+} // namespace Om
+
+#endif // PATCH_H
diff --git a/src/libs/engine/Plugin.h b/src/libs/engine/Plugin.h
new file mode 100644
index 00000000..d350b1e8
--- /dev/null
+++ b/src/libs/engine/Plugin.h
@@ -0,0 +1,149 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PLUGIN_H
+#define PLUGIN_H
+
+#include "config.h"
+
+#include <cstdlib>
+#include <dlfcn.h>
+#include <string>
+#include <iostream>
+#ifdef HAVE_SLV2
+#include <slv2/slv2.h>
+#endif
+using std::string;
+using std::cerr; using std::endl;
+
+
+namespace Om {
+
+class PluginLibrary;
+
+
+/** Representation of a plugin (of various types).
+ *
+ * A Node is an instance of this, conceptually.
+ * FIXME: This whole thing is a filthy mess and needs a rewrite. Probably
+ * with derived classes for each plugin type.
+ */
+class Plugin
+{
+public:
+ enum Type { LV2, LADSPA, DSSI, Internal, Patch };
+
+ Plugin() : m_type(Internal), m_lib_path("/Om"),
+ m_id(0), m_library(NULL)
+ {
+#ifdef HAVE_SLV2
+ m_slv2_plugin = NULL;
+#endif
+ }
+
+ Plugin(const Plugin* const copy) {
+ // Copying only allowed for Internal plugins. Bit of a hack, but
+ // allows the PluginInfo to be defined in the Node class which keeps
+ // things localized and convenient (FIXME?)
+ if (copy->m_type != Internal)
+ exit(EXIT_FAILURE);
+ m_type = copy->m_type;
+ m_lib_path = copy->m_lib_path;
+ m_plug_label = copy->m_plug_label;
+ m_name = copy->m_name;
+ m_library = copy->m_library;
+ }
+
+ Type type() const { return m_type; }
+ void type(Type t) { m_type = t; }
+ const string& lib_path() const { return m_lib_path; }
+ void lib_path(const string& s) { m_lib_path = s; }
+ string lib_name() const { return m_lib_path.substr(m_lib_path.find_last_of("/")); }
+ const string& plug_label() const { return m_plug_label; }
+ void plug_label(const string& s) { m_plug_label = s; }
+ const string& name() const { return m_name; }
+ void name(const string& s) { m_name = s; }
+ unsigned long id() const { return m_id; }
+ void id(unsigned long i) { m_id = i; }
+ void uri(const string& s) { m_uri = s; }
+ const string uri() const
+ {
+ char id_str[11];
+ snprintf(id_str, 11, "%lu", m_id);
+
+ if (m_uri.length() > 0) {
+ return m_uri;
+ } else if (m_type == Internal) {
+ return string("om:") + m_plug_label;
+ } else if (m_type == LADSPA) {
+ return string("ladspa:").append(id_str);
+ } else if (m_type == DSSI) {
+ return string("dssi:") + lib_name() +":"+ m_plug_label;
+ } else {
+ return "";
+ }
+ }
+
+ PluginLibrary* library() const { return m_library; }
+ void library(PluginLibrary* const library) { m_library = library; }
+
+ const char* type_string() const {
+ if (m_type == LADSPA) return "LADSPA";
+ else if (m_type == LV2) return "LV2";
+ else if (m_type == DSSI) return "DSSI";
+ else if (m_type == Internal) return "Internal";
+ else if (m_type == Patch) return "Patch";
+ else return "";
+ }
+
+ void set_type(const string& type_string) {
+ if (type_string == "LADSPA") m_type = LADSPA;
+ else if (type_string == "LV2") m_type = LV2;
+ else if (type_string == "DSSI") m_type = DSSI;
+ else if (type_string == "Internal") m_type = Internal;
+ else if (type_string == "Patch") m_type = Patch;
+ }
+
+ // FIXME: ew
+#ifdef HAVE_SLV2
+ SLV2Plugin* slv2_plugin() { return m_slv2_plugin; }
+ void slv2_plugin(const SLV2Plugin* p) { m_slv2_plugin = p; }
+
+#endif
+private:
+ // Disallow copies (undefined)
+ Plugin(const Plugin&);
+ Plugin& operator=(const Plugin&);
+
+ Type m_type;
+ string m_uri; ///< LV2 only
+ string m_lib_path; ///< LADSPA/DSSI only
+ string m_plug_label; ///< LADSPA/DSSI only
+ string m_name; ///< LADSPA/DSSI only
+ unsigned long m_id; ///< LADSPA/DSSI only
+
+ PluginLibrary* m_library;
+
+#ifdef HAVE_SLV2
+ SLV2Plugin* m_slv2_plugin;
+#endif
+};
+
+
+} // namespace Om
+
+#endif // PLUGIN_H
+
diff --git a/src/libs/engine/PluginLibrary.h b/src/libs/engine/PluginLibrary.h
new file mode 100644
index 00000000..53279d8a
--- /dev/null
+++ b/src/libs/engine/PluginLibrary.h
@@ -0,0 +1,100 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef PLUGINLIBRARY_H
+#define PLUGINLIBRARY_H
+
+#include <dlfcn.h>
+#include <string>
+#include <iostream>
+using std::string;
+using std::cerr; using std::endl;
+
+
+namespace Om {
+
+
+/** Representation of a shared library containing at least one Plugin.
+ *
+ * In the NodeFactory, this represents one loaded shared library instance,
+ * which is what handle() returns.
+ */
+class PluginLibrary
+{
+public:
+ /** Construct a new PluginLibrary.
+ *
+ * Path is assumed to be the path of a valid shared library that can be
+ * successfully dlopen'ed.
+ */
+ PluginLibrary(const string& path)
+ : m_path(path), m_handle(NULL)
+ {}
+
+ ~PluginLibrary()
+ {
+ close();
+ }
+
+ /** Load and resolve all symbols in this shared library
+ * (dlopen with RTLD_NOW).
+ *
+ * It is okay to call this many times, the library will only be opened
+ * once.
+ */
+ void open()
+ {
+ if (m_handle == NULL) {
+ dlerror();
+ m_handle = dlopen(m_path.c_str(), RTLD_NOW);
+ if (m_handle == NULL)
+ cerr << "[PluginLibrary] Warning: Error opening shared library "
+ << m_path << "(" << dlerror() << ")" << endl;
+ }
+ }
+
+ /** Close the dynamic library.
+ *
+ * This can be called on an already closed PluginLibrary without problems.
+ */
+ void close()
+ {
+ if (m_handle != NULL) {
+ dlerror();
+ if (dlclose(m_handle))
+ cerr << "[PluginLibrary] Error closing shared library " << m_path
+ << "(" << dlerror() << ")" << endl;
+ }
+ m_handle = NULL;
+ }
+
+ void* handle() const { return m_handle; }
+
+private:
+ // Disallow copies (undefined)
+ PluginLibrary(const PluginLibrary&);
+ PluginLibrary& operator=(const PluginLibrary&);
+
+ string m_path;
+ void* m_handle;
+};
+
+
+} // namespace Om
+
+#endif // PLUGINLIBRARY_H
+
diff --git a/src/libs/engine/Port.cpp b/src/libs/engine/Port.cpp
new file mode 100644
index 00000000..987d232d
--- /dev/null
+++ b/src/libs/engine/Port.cpp
@@ -0,0 +1,56 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "Port.h"
+#include "Node.h"
+#include "PortInfo.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "ObjectStore.h"
+
+namespace Om {
+
+Port::Port(Node* const node, const string& name, size_t index, size_t poly, PortInfo* port_info)
+: OmObject(node, name),
+ m_index(index),
+ m_poly(poly),
+ m_port_info(port_info)
+{
+ assert(node != NULL);
+ assert(port_info != NULL);
+ assert(m_poly > 0);
+}
+
+
+void
+Port::add_to_store()
+{
+ om->object_store()->add(this);
+}
+
+
+void
+Port::remove_from_store()
+{
+ // Remove self
+ TreeNode<OmObject*>* node = om->object_store()->remove(path());
+ assert(node != NULL);
+ assert(om->object_store()->find(path()) == NULL);
+ delete node;
+}
+
+
+} // namespace Om
diff --git a/src/libs/engine/Port.h b/src/libs/engine/Port.h
new file mode 100644
index 00000000..f51d7382
--- /dev/null
+++ b/src/libs/engine/Port.h
@@ -0,0 +1,80 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PORT_H
+#define PORT_H
+
+#include <cstdlib>
+#include <string>
+#include "util/types.h"
+#include "OmObject.h"
+
+using std::string;
+
+namespace Om {
+
+class Node;
+class PortInfo;
+
+
+/** A port on a Node.
+ *
+ * This is a non-template abstract base class, which basically exists so
+ * things can pass around Port pointers and not have to worry about type,
+ * templates, etc.
+ *
+ * \ingroup engine
+ */
+class Port : public OmObject
+{
+public:
+ virtual ~Port() {}
+
+ Port* as_port() { return this; }
+
+ PortInfo* port_info() const { return m_port_info; }
+ void port_info(PortInfo* pi) { m_port_info = pi; }
+
+ void add_to_store();
+ void remove_from_store();
+
+ /** Called once per process cycle */
+ virtual void prepare_buffers(size_t nframes) = 0;
+
+ /** Empty buffer contents completely (ie silence) */
+ virtual void clear_buffers() = 0;
+
+ Node* parent_node() const { return m_parent->as_node(); }
+ bool is_sample() const { return false; }
+ size_t num() const { return m_index; }
+ size_t poly() const { return m_poly; }
+
+protected:
+ Port(Node* const node, const string& name, size_t index, size_t poly, PortInfo* port_info);
+
+ // Prevent copies (undefined)
+ Port(const Port&);
+ Port& operator=(const Port&);
+
+ size_t m_index;
+ size_t m_poly;
+ PortInfo* m_port_info;
+};
+
+
+} // namespace Om
+
+#endif // PORT_H
diff --git a/src/libs/engine/PortBase.cpp b/src/libs/engine/PortBase.cpp
new file mode 100644
index 00000000..3d8dbc67
--- /dev/null
+++ b/src/libs/engine/PortBase.cpp
@@ -0,0 +1,133 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "PortBase.h"
+#include <cmath>
+#include <cstdlib>
+#include <iostream>
+#include <cassert>
+#include <sys/mman.h>
+#include "util.h"
+#include "Node.h"
+#include "PortInfo.h"
+#include "MidiMessage.h"
+
+namespace Om {
+
+
+/** Constructor for a Port.
+ */
+template <typename T>
+PortBase<T>::PortBase(Node* node, const string& name, size_t index, size_t poly, PortInfo* port_info, size_t buffer_size)
+: Port(node, name, index, poly, port_info),
+ m_buffer_size(buffer_size),
+ m_fixed_buffers(false),
+ m_is_tied(false),
+ m_tied_port(NULL)
+{
+ allocate_buffers();
+ clear_buffers();
+
+ assert(m_buffers.size() > 0);
+}
+template
+PortBase<sample>::PortBase(Node* node, const string& name, size_t index, size_t poly, PortInfo* port_info, size_t buffer_size);
+template
+PortBase<MidiMessage>::PortBase(Node* node, const string& name, size_t index, size_t poly, PortInfo* port_info, size_t buffer_size);
+
+
+template <typename T>
+PortBase<T>::~PortBase()
+{
+ for (size_t i=0; i < m_poly; ++i)
+ delete m_buffers.at(i);
+
+ delete m_port_info;
+}
+template PortBase<sample>::~PortBase();
+template PortBase<MidiMessage>::~PortBase();
+
+
+/** Set the port's value for all voices.
+ */
+template<>
+void
+PortBase<sample>::set_value(sample val, size_t offset)
+{
+ if (m_port_info->is_control())
+ offset = 0;
+ assert(offset < m_buffer_size);
+
+ for (size_t v=0; v < m_poly; ++v)
+ m_buffers.at(v)->set(val, offset);
+}
+
+/** Set the port's value for a specific voice.
+ */
+template<>
+void
+PortBase<sample>::set_value(size_t voice, sample val, size_t offset)
+{
+ if (m_port_info->is_control())
+ offset = 0;
+ assert(offset < m_buffer_size);
+
+ m_buffers.at(voice)->set(val, offset);
+}
+
+
+template <typename T>
+void
+PortBase<T>::allocate_buffers()
+{
+ m_buffers.alloc(m_poly);
+
+ for (size_t i=0; i < m_poly; ++i)
+ m_buffers.at(i) = new Buffer<T>(m_buffer_size);
+}
+template void PortBase<sample>::allocate_buffers();
+template void PortBase<MidiMessage>::allocate_buffers();
+
+
+template<>
+void
+PortBase<sample>::prepare_buffers(size_t nframes)
+{
+ for (size_t i=0; i < m_poly; ++i)
+ m_buffers.at(i)->prepare(nframes);
+}
+
+
+template<>
+void
+PortBase<MidiMessage>::prepare_buffers(size_t nframes)
+{
+}
+
+
+template<typename T>
+void
+PortBase<T>::clear_buffers()
+{
+ for (size_t i=0; i < m_poly; ++i)
+ m_buffers.at(i)->clear();
+}
+template void PortBase<sample>::clear_buffers();
+template void PortBase<MidiMessage>::clear_buffers();
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/PortBase.h b/src/libs/engine/PortBase.h
new file mode 100644
index 00000000..9962538e
--- /dev/null
+++ b/src/libs/engine/PortBase.h
@@ -0,0 +1,87 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PORTBASE_H
+#define PORTBASE_H
+
+#include <string>
+#include "util/types.h"
+#include "Array.h"
+#include "Port.h"
+#include "Buffer.h"
+
+using std::string;
+
+namespace Om {
+
+class MidiMessage;
+class Node;
+
+
+/** A port (with a type).
+ *
+ * This is basically just a buffer and a bunch of flags and helper methods.
+ * All the interesting functionality of ports is in InputPort.
+ *
+ * \ingroup engine
+ */
+template <typename T>
+class PortBase : public Port
+{
+public:
+ virtual ~PortBase();
+
+ void set_value(size_t voice, T val, size_t offset);
+ void set_value(T val, size_t offset);
+
+ Buffer<T>* buffer(size_t voice) const { return m_buffers.at(voice); }
+
+ virtual void prepare_buffers(size_t nframes);
+ virtual void clear_buffers();
+
+ PortBase* tied_port() const { return m_tied_port; }
+ void untie() { m_is_tied = false; m_tied_port = NULL; }
+
+ size_t buffer_size() const { return m_buffer_size; }
+
+ /** Used by drivers to prevent port from changing buffers */
+ void fixed_buffers(bool b) { m_fixed_buffers = b; }
+ bool fixed_buffers() { return m_fixed_buffers; }
+
+protected:
+ PortBase(Node* const node, const string& name, size_t index, size_t poly, PortInfo* port_info, size_t buffer_size);
+
+ // Prevent copies (undefined)
+ PortBase(const PortBase<T>& copy);
+ PortBase& operator=(const Port&);
+
+ void allocate_buffers();
+
+ size_t m_buffer_size;
+ bool m_fixed_buffers;
+ bool m_is_tied;
+ PortBase* m_tied_port;
+
+ Array<Buffer<T>*> m_buffers;
+};
+
+
+template class PortBase<sample>;
+template class PortBase<MidiMessage>;
+
+} // namespace Om
+
+#endif // PORTBASE_H
diff --git a/src/libs/engine/PortInfo.h b/src/libs/engine/PortInfo.h
new file mode 100644
index 00000000..3fa4215a
--- /dev/null
+++ b/src/libs/engine/PortInfo.h
@@ -0,0 +1,153 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef PORTINFO_H
+#define PORTINFO_H
+
+#include <cstdlib>
+#include <string>
+#include <ladspa.h>
+
+using std::string;
+
+namespace Om {
+
+
+enum PortType { CONTROL, AUDIO, MIDI };
+enum PortDirection { INPUT = 0, OUTPUT = 1 }; // FIXME: dupe of ClientInterface::PortDirection
+enum PortHint { NONE, INTEGER, TOGGLE, LOGARITHMIC };
+
+
+/** Information about a Port.
+ *
+ * I'm not sure this actually has a reason for existing anymore. This is the
+ * model for a Port, but no other OmObjects need a model, soo....
+ *
+ * \ingroup engine
+ */
+class PortInfo
+{
+public:
+ PortInfo(const string& port_name, PortType type, PortDirection dir, PortHint hint, float default_val, float min, float max)
+ : m_name(port_name),
+ m_type(type),
+ m_direction(dir),
+ m_hint(hint),
+ m_default_val(default_val),
+ m_min_val(min),
+ m_max_val(max)
+ {}
+
+ PortInfo(const string& port_name, PortType type, PortDirection dir, float default_val, float min, float max)
+ : m_name(port_name),
+ m_type(type),
+ m_direction(dir),
+ m_hint(NONE),
+ m_default_val(default_val),
+ m_min_val(min),
+ m_max_val(max)
+ {}
+
+ PortInfo(const string& port_name, PortType type, PortDirection dir, PortHint hint = NONE)
+ : m_name(port_name),
+ m_type(type),
+ m_direction(dir),
+ m_hint(hint),
+ m_default_val(0.0f),
+ m_min_val(0.0f),
+ m_max_val(1.0f)
+ {}
+
+ PortInfo(const string& port_name, LADSPA_PortDescriptor d, LADSPA_PortRangeHintDescriptor hint)
+ : m_name(port_name),
+ m_default_val(1.0f),
+ m_min_val(0.0f),
+ m_max_val(1.0f)
+ {
+ if (LADSPA_IS_PORT_AUDIO(d)) m_type = AUDIO;
+ else if (LADSPA_IS_PORT_CONTROL(d)) m_type = CONTROL;
+ else exit(EXIT_FAILURE);
+
+ if (LADSPA_IS_PORT_INPUT(d)) m_direction = INPUT;
+ else if (LADSPA_IS_PORT_OUTPUT(d)) m_direction = OUTPUT;
+ else exit(EXIT_FAILURE);
+
+ if (LADSPA_IS_HINT_TOGGLED(hint)) {
+ m_hint = TOGGLE; m_min_val = 0; m_max_val = 1; m_default_val = 0;
+ } else if (LADSPA_IS_HINT_LOGARITHMIC(hint))
+ m_hint = LOGARITHMIC;
+ else if (LADSPA_IS_HINT_INTEGER(hint))
+ m_hint = INTEGER;
+ else
+ m_hint = NONE;
+ }
+
+ PortType type() const { return m_type; }
+ void type(PortType t) { m_type = t; }
+ float min_val() const { return m_min_val; }
+ void min_val(float f) { m_min_val = f; }
+ float default_val() const { return m_default_val; }
+ void default_val(float f) { m_default_val = f; }
+ float max_val() const { return m_max_val; }
+ void max_val(float f) { m_max_val = f; }
+ PortDirection direction() const { return m_direction; }
+
+ string type_string() {
+ switch (m_type) {
+ case CONTROL: return "CONTROL"; break;
+ case AUDIO: return "AUDIO"; break;
+ case MIDI: return "MIDI"; break;
+ default: return "UNKNOWN";
+ }
+ }
+ string direction_string() { if (m_direction == INPUT) return "INPUT"; else return "OUTPUT"; }
+ string hint_string() { if (m_hint == INTEGER) return "INTEGER";
+ else if (m_hint == LOGARITHMIC) return "LOGARITHMIC";
+ else if (m_hint == TOGGLE) return "TOGGLE";
+ else return "NONE"; }
+
+ bool is_control() const { return (m_type == CONTROL); }
+ bool is_audio() const { return (m_type == AUDIO); }
+ bool is_midi() const { return (m_type == MIDI); }
+ bool is_input() const { return (m_direction == INPUT); }
+ bool is_output() const { return (m_direction == OUTPUT); }
+
+ bool is_logarithmic() const { return (m_hint == LOGARITHMIC); }
+ bool is_integer() const { return (m_hint == INTEGER); }
+ bool is_toggle() const { return (m_hint == TOGGLE); }
+
+ const string& name() const { return m_name; }
+ void name(const string& n) { m_name = n; }
+
+private:
+ // Prevent copies (undefined)
+ PortInfo(const PortInfo&);
+ PortInfo& operator=(const PortInfo&);
+
+ string m_name;
+ PortType m_type;
+ PortDirection m_direction;
+ PortHint m_hint;
+ float m_default_val;
+ float m_min_val;
+ float m_max_val;
+};
+
+
+} // namespace Om
+
+#endif // PORTINFO_H
diff --git a/src/libs/engine/PostProcessor.cpp b/src/libs/engine/PostProcessor.cpp
new file mode 100644
index 00000000..65b92015
--- /dev/null
+++ b/src/libs/engine/PostProcessor.cpp
@@ -0,0 +1,122 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "PostProcessor.h"
+#include <cassert>
+#include <iostream>
+#include <pthread.h>
+#include "Om.h"
+#include "OmApp.h"
+#include "Event.h"
+#include "util/Queue.h"
+#include "Maid.h"
+
+
+using std::cerr; using std::cout; using std::endl;
+
+namespace Om {
+
+bool PostProcessor::m_process_thread_exit_flag = false;
+
+
+PostProcessor::PostProcessor(size_t queue_size)
+: m_events(queue_size),
+ m_thread_exists(false),
+ m_semaphore(0)
+{
+}
+
+
+PostProcessor::~PostProcessor()
+{
+ stop();
+}
+
+
+/** Start the process thread.
+ */
+void
+PostProcessor::start()
+{
+ cout << "[PostProcessor] Starting." << endl;
+
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setstacksize(&attr, 1500000);
+
+ pthread_create(&m_process_thread, &attr, process_events, this);
+ m_thread_exists = true;
+}
+
+
+/** Stop the process thread.
+ */
+void
+PostProcessor::stop()
+{
+ if (m_thread_exists) {
+ m_process_thread_exit_flag = true;
+ pthread_cancel(m_process_thread);
+ pthread_join(m_process_thread, NULL);
+ m_thread_exists = false;
+ }
+}
+
+
+/** Signal the PostProcessor to process all pending events.
+ */
+void
+PostProcessor::signal()
+{
+ m_semaphore.post();
+}
+
+
+void*
+PostProcessor::process_events(void* osc_processer)
+{
+ PostProcessor* me = (PostProcessor*)osc_processer;
+ return me->m_process_events();
+}
+
+
+/** OSC message processing thread.
+ */
+void*
+PostProcessor::m_process_events()
+{
+ Event* ev = NULL;
+
+ while (true) {
+ m_semaphore.wait();
+
+ if (m_process_thread_exit_flag)
+ break;
+
+ while (!m_events.is_empty()) {
+ ev = m_events.pop();
+ assert(ev != NULL);
+ ev->post_process();
+ om->maid()->push(ev);
+ }
+ }
+
+ cout << "[PostProcessor] Exiting post processor thread." << endl;
+
+ return NULL;
+}
+
+} // namespace Om
diff --git a/src/libs/engine/PostProcessor.h b/src/libs/engine/PostProcessor.h
new file mode 100644
index 00000000..843569d8
--- /dev/null
+++ b/src/libs/engine/PostProcessor.h
@@ -0,0 +1,78 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef POSTPROCESSOR_H
+#define POSTPROCESSOR_H
+
+#include <pthread.h>
+#include "util/types.h"
+#include "util/Queue.h"
+#include "util/Semaphore.h"
+
+namespace Om {
+
+class Event;
+
+
+/** Processor for Events after leaving the audio thread.
+ *
+ * The audio thread pushes events to this when it is done with them (which
+ * is realtime-safe), which signals the processing thread through a semaphore
+ * to handle the event and pass it on to the Maid.
+ *
+ * \ingroup engine
+ */
+class PostProcessor
+{
+public:
+ PostProcessor(size_t queue_size);
+ ~PostProcessor();
+
+ void start();
+ void stop();
+
+ inline void push(Event* const ev);
+ void signal();
+
+private:
+ // Prevent copies
+ PostProcessor(const PostProcessor&);
+ PostProcessor& operator=(const PostProcessor&);
+
+ Queue<Event*> m_events;
+
+ static void* process_events(void* me);
+ void* m_process_events();
+
+ pthread_t m_process_thread;
+ bool m_thread_exists;
+ static bool m_process_thread_exit_flag;
+ Semaphore m_semaphore;
+};
+
+
+/** Push an event on to the process queue, realtime-safe, not thread-safe.
+ */
+inline void
+PostProcessor::push(Event* const ev)
+{
+ m_events.push(ev);
+}
+
+
+} // namespace Om
+
+#endif // POSTPROCESSOR_H
diff --git a/src/libs/engine/QueuedEngineInterface.cpp b/src/libs/engine/QueuedEngineInterface.cpp
new file mode 100644
index 00000000..0734eb7a
--- /dev/null
+++ b/src/libs/engine/QueuedEngineInterface.cpp
@@ -0,0 +1,299 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "QueuedEngineInterface.h"
+#include "QueuedEventSource.h"
+#include "events.h"
+#include "Om.h"
+#include "util/Queue.h"
+#include "OmApp.h"
+
+namespace Om {
+
+QueuedEngineInterface::QueuedEngineInterface(size_t queue_size)
+: QueuedEventSource(queue_size)
+, _responder(CountedPtr<Responder>(new Responder())) // NULL responder
+{
+}
+
+
+/** Set the Responder to send responses to commands with, once the commands
+ * are preprocessed and ready to be executed (or not).
+ *
+ * Ownership of @a responder is taken.
+ */
+void
+QueuedEngineInterface::set_responder(CountedPtr<Responder> responder)
+{
+ //cerr << "SET\n";
+ _responder = responder;
+}
+
+
+void
+QueuedEngineInterface::disable_responses()
+{
+ static CountedPtr<Responder> null_responder(new Responder());
+ //cerr << "DISABLE\n";
+ set_responder(null_responder);
+}
+
+
+/* *** EngineInterface implementation below here *** */
+
+
+void
+QueuedEngineInterface::register_client(ClientKey key, CountedPtr<ClientInterface> client)
+{
+ RegisterClientEvent* ev = new RegisterClientEvent(_responder, key, client);
+ push(ev);
+}
+
+
+void
+QueuedEngineInterface::unregister_client(ClientKey key)
+{
+ UnregisterClientEvent* ev = new UnregisterClientEvent(_responder, key);
+ push(ev);
+}
+
+
+
+// Engine commands
+void
+QueuedEngineInterface::load_plugins()
+{
+ LoadPluginsEvent* ev = new LoadPluginsEvent(_responder);
+ push(ev);
+
+}
+
+
+void
+QueuedEngineInterface::activate()
+{
+ ActivateEvent* ev = new ActivateEvent(_responder);
+ push(ev);
+}
+
+
+void
+QueuedEngineInterface::deactivate()
+{
+ DeactivateEvent* ev = new DeactivateEvent(_responder);
+ push(ev);
+}
+
+
+void
+QueuedEngineInterface::quit()
+{
+ _responder->respond_ok();
+ om->quit();
+}
+
+
+
+// Object commands
+
+void
+QueuedEngineInterface::create_patch(const string& path,
+ uint32_t poly)
+{
+ CreatePatchEvent* ev = new CreatePatchEvent(_responder, path, poly);
+ push(ev);
+
+}
+
+
+void
+QueuedEngineInterface::create_node(const string& path,
+ const string& plugin_type,
+ const string& plugin_uri,
+ bool polyphonic)
+{
+ // FIXME: ew
+
+ Plugin* plugin = new Plugin();
+ plugin->set_type(plugin_type);
+ plugin->uri(plugin_uri);
+
+ AddNodeEvent* ev = new AddNodeEvent(_responder,
+ path, plugin, polyphonic);
+ push(ev);
+}
+
+
+void
+QueuedEngineInterface::rename(const string& old_path,
+ const string& new_name)
+{
+ RenameEvent* ev = new RenameEvent(_responder, old_path, new_name);
+ push(ev);
+}
+
+
+void
+QueuedEngineInterface::destroy(const string& path)
+{
+ DestroyEvent* ev = new DestroyEvent(_responder, path);
+ push(ev);
+}
+
+
+void
+QueuedEngineInterface::clear_patch(const string& patch_path)
+{
+}
+
+
+void
+QueuedEngineInterface::enable_patch(const string& patch_path)
+{
+ EnablePatchEvent* ev = new EnablePatchEvent(_responder, patch_path);
+ push(ev);
+}
+
+
+void
+QueuedEngineInterface::disable_patch(const string& patch_path)
+{
+ DisablePatchEvent* ev = new DisablePatchEvent(_responder, patch_path);
+ push(ev);
+}
+
+
+void
+QueuedEngineInterface::connect(const string& src_port_path,
+ const string& dst_port_path)
+{
+ ConnectionEvent* ev = new ConnectionEvent(_responder, src_port_path, dst_port_path);
+ push(ev);
+
+}
+
+
+void
+QueuedEngineInterface::disconnect(const string& src_port_path,
+ const string& dst_port_path)
+{
+ DisconnectionEvent* ev = new DisconnectionEvent(_responder, src_port_path, dst_port_path);
+ push(ev);
+}
+
+
+void
+QueuedEngineInterface::disconnect_all(const string& node_path)
+{
+ DisconnectNodeEvent* ev = new DisconnectNodeEvent(_responder, node_path);
+ push(ev);
+}
+
+
+void
+QueuedEngineInterface::set_port_value(const string& port_path,
+ float value)
+{
+ SetPortValueEvent* ev = new SetPortValueEvent(_responder, port_path, value);
+ om->event_queue()->push(ev);
+}
+
+
+void
+QueuedEngineInterface::set_port_value(const string& port_path,
+ uint32_t voice,
+ float value)
+{
+ SetPortValueEvent* ev = new SetPortValueEvent(_responder, voice, port_path, value);
+ om->event_queue()->push(ev);
+}
+
+
+void
+QueuedEngineInterface::set_port_value_queued(const string& port_path,
+ float value)
+{
+ SetPortValueQueuedEvent* ev = new SetPortValueQueuedEvent(_responder, port_path, value);
+ push(ev);
+}
+
+
+void
+QueuedEngineInterface::set_program(const string& node_path,
+ uint32_t bank,
+ uint32_t program)
+{
+ push(new DSSIProgramEvent(_responder, node_path, bank, program));
+}
+
+
+void
+QueuedEngineInterface::midi_learn(const string& node_path)
+{
+ MidiLearnEvent* ev = new MidiLearnEvent(_responder, node_path);
+ push(ev);
+}
+
+
+void
+QueuedEngineInterface::set_metadata(const string& path,
+ const string& predicate,
+ const string& value)
+{
+ SetMetadataEvent* ev = new SetMetadataEvent(_responder,
+ path, predicate, value);
+
+ push(ev);
+}
+
+
+// Requests //
+
+void
+QueuedEngineInterface::ping()
+{
+ PingQueuedEvent* ev = new PingQueuedEvent(_responder);
+ push(ev);
+}
+
+
+void
+QueuedEngineInterface::request_port_value(const string& port_path)
+{
+ RequestPortValueEvent* ev = new RequestPortValueEvent(_responder, port_path);
+ push(ev);
+}
+
+
+void
+QueuedEngineInterface::request_plugins()
+{
+ RequestPluginsEvent* ev = new RequestPluginsEvent(_responder);
+ push(ev);
+}
+
+
+void
+QueuedEngineInterface::request_all_objects()
+{
+ RequestAllObjectsEvent* ev = new RequestAllObjectsEvent(_responder);
+ push(ev);
+}
+
+
+} // namespace Om
+
+
diff --git a/src/libs/engine/QueuedEngineInterface.h b/src/libs/engine/QueuedEngineInterface.h
new file mode 100644
index 00000000..cc9c575f
--- /dev/null
+++ b/src/libs/engine/QueuedEngineInterface.h
@@ -0,0 +1,145 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef QUEUEDENGINEINTERFACE_H
+#define QUEUEDENGINEINTERFACE_H
+
+#include <inttypes.h>
+#include <string>
+#include <memory>
+#include "util/CountedPtr.h"
+#include "interface/ClientInterface.h"
+#include "interface/ClientKey.h"
+#include "QueuedEventSource.h"
+#include "Responder.h"
+using std::string;
+
+namespace Om {
+
+using Shared::ClientKey;
+using Shared::ClientInterface;
+
+
+/** A queued (preprocessed) event source / interface.
+ *
+ * This is the bridge between the EngineInterface presented to the client, and
+ * the EventSource that needs to be presented to the AudioDriver.
+ *
+ * This is sort of a state machine, \ref set_responder sets the Responder that
+ * will be used to send the response from all future function calls. Stateless
+ * protocols like UDP/OSC can use this to set a different response address for
+ * each event (eg incoming UDP port), but engine/client interfaces that don't
+ * need to change an 'address' constantly can just set it once on initialisation.
+ * Blocking control interfaces can be made by setting a Responder which signals
+ * the caller when the 'response' is 'sent'.
+ *
+ * If you do not register a responder, you have no way of knowing if your calls
+ * are successful.
+ *
+ * FIXME: this isn't really "queued" entirely, since some events aren't queued
+ * events and get pushed directly into the realtime event queue. Should that
+ * be separated into a different interface/client?
+ */
+class QueuedEngineInterface : public Om::QueuedEventSource
+{
+public:
+ QueuedEngineInterface(size_t queue_size);
+ virtual ~QueuedEngineInterface() {}
+
+ virtual void set_responder(CountedPtr<Responder> responder);
+ virtual void disable_responses();
+
+ // Client registration
+ virtual void register_client(ClientKey key, CountedPtr<ClientInterface> client);
+ virtual void unregister_client(ClientKey key);
+
+
+ // Engine commands
+ virtual void load_plugins();
+ virtual void activate();
+ virtual void deactivate();
+ virtual void quit();
+
+ // Object commands
+
+ virtual void create_patch(const string& path,
+ uint32_t poly);
+
+ virtual void create_node(const string& path,
+ const string& plugin_type,
+ const string& plugin_uri,
+ bool polyphonic);
+
+ virtual void rename(const string& old_path,
+ const string& new_name);
+
+ virtual void destroy(const string& path);
+
+ virtual void clear_patch(const string& patch_path);
+
+ virtual void enable_patch(const string& patch_path);
+
+ virtual void disable_patch(const string& patch_path);
+
+ virtual void connect(const string& src_port_path,
+ const string& dst_port_path);
+
+ virtual void disconnect(const string& src_port_path,
+ const string& dst_port_path);
+
+ virtual void disconnect_all(const string& node_path);
+
+ virtual void set_port_value(const string& port_path,
+ float val);
+
+ virtual void set_port_value(const string& port_path,
+ uint32_t voice,
+ float val);
+
+ virtual void set_port_value_queued(const string& port_path,
+ float val);
+
+ virtual void set_program(const string& node_path,
+ uint32_t bank,
+ uint32_t program);
+
+ virtual void midi_learn(const string& node_path);
+
+ virtual void set_metadata(const string& path,
+ const string& predicate,
+ const string& value);
+
+ // Requests //
+
+ virtual void ping();
+
+ virtual void request_port_value(const string& port_path);
+
+ virtual void request_plugins();
+
+ virtual void request_all_objects();
+
+protected:
+
+ /** Where responses to current messages will go. */
+ CountedPtr<Responder> _responder;
+};
+
+
+} // namespace Om
+
+#endif // QUEUEDENGINEINTERFACE_H
+
diff --git a/src/libs/engine/QueuedEvent.h b/src/libs/engine/QueuedEvent.h
new file mode 100644
index 00000000..16560aaa
--- /dev/null
+++ b/src/libs/engine/QueuedEvent.h
@@ -0,0 +1,86 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef QUEUEDEVENT_H
+#define QUEUEDEVENT_H
+
+#include "Event.h"
+
+namespace Om {
+
+class Responder;
+class QueuedEventSource;
+
+
+/** An Event with a not-time-critical preprocessing stage.
+ *
+ * These events are events that aren't able to be executed immediately by the
+ * Jack thread (because they allocate memory or whatever). They are pushed
+ * on to the QueuedEventQueue where they are preprocessed then pushed on
+ * to the realtime Event Queue when they are ready.
+ *
+ * Lookups for these events should go in the pre_process() method, since they are
+ * not time critical and shouldn't waste time in the audio thread doing
+ * lookups they can do beforehand. (This applies for any expensive operation that
+ * could be done before the execute() method).
+ *
+ * \ingroup engine
+ */
+class QueuedEvent : public Event
+{
+public:
+ /** Process this event into a realtime-suitable event.
+ */
+ virtual void pre_process() {
+ assert(m_pre_processed == false);
+ m_pre_processed = true;
+ }
+
+ virtual void execute(samplecount offset) {
+ assert(m_pre_processed);
+ Event::execute(offset);
+ }
+
+ virtual void post_process() {}
+
+ /** If this event blocks the prepare phase of other slow events */
+ bool is_blocking() { return m_blocking; }
+
+ bool is_prepared() { return m_pre_processed; }
+
+protected:
+ // Prevent copies
+ QueuedEvent(const QueuedEvent& copy);
+ QueuedEvent& operator=(const QueuedEvent&);
+
+ QueuedEvent(CountedPtr<Responder> responder, bool blocking = false, QueuedEventSource* source=NULL)
+ : Event(responder), m_pre_processed(false), m_blocking(blocking), m_source(source)
+ {}
+
+ // NULL event base (for internal events)
+ QueuedEvent()
+ : Event(), m_pre_processed(false), m_blocking(false), m_source(NULL)
+ {}
+
+ bool m_pre_processed;
+ bool m_blocking;
+ QueuedEventSource* m_source;
+};
+
+
+} // namespace Om
+
+#endif // QUEUEDEVENT_H
diff --git a/src/libs/engine/QueuedEventSource.cpp b/src/libs/engine/QueuedEventSource.cpp
new file mode 100644
index 00000000..953eb5b1
--- /dev/null
+++ b/src/libs/engine/QueuedEventSource.cpp
@@ -0,0 +1,201 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "QueuedEventSource.h"
+#include "QueuedEvent.h"
+#include <sys/mman.h>
+#include <iostream>
+using std::cout; using std::cerr; using std::endl;
+
+
+namespace Om {
+
+
+QueuedEventSource::QueuedEventSource(size_t size)
+: m_front(0),
+ m_back(0),
+ m_prepared_back(0),
+ m_size(size+1),
+ m_thread_exists(false),
+ m_prepare_thread_exit_flag(false),
+ m_semaphore(0)
+{
+ m_events = (QueuedEvent**)calloc(m_size, sizeof(QueuedEvent*));
+
+ pthread_mutex_init(&m_blocking_mutex, NULL);
+ pthread_cond_init(&m_blocking_cond, NULL);
+
+ mlock(m_events, m_size * sizeof(QueuedEvent*));
+}
+
+
+QueuedEventSource::~QueuedEventSource()
+{
+ stop();
+
+ free(m_events);
+ pthread_mutex_destroy(&m_blocking_mutex);
+ pthread_cond_destroy(&m_blocking_cond);
+}
+
+
+/** Start the prepare thread.
+ */
+void
+QueuedEventSource::start()
+{
+ if (m_thread_exists) {
+ cerr << "[QueuedEventSource] Thread already launched?" << endl;
+ return;
+ } else {
+ cout << "[QueuedEventSource] Launching thread." << endl;
+ }
+
+ m_prepare_thread_exit_flag = false;
+
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setstacksize(&attr, 1500000);
+
+ pthread_create(&m_prepare_thread, &attr, &QueuedEventSource::prepare_loop, this);
+ pthread_attr_destroy(&attr);
+
+ m_thread_exists = true;
+}
+
+
+/** Destroy the prepare thread.
+ */
+void
+QueuedEventSource::stop()
+{
+ if (m_thread_exists) {
+ m_prepare_thread_exit_flag = true;
+ pthread_cancel(m_prepare_thread);
+ pthread_join(m_prepare_thread, NULL);
+ m_thread_exists = false;
+ cout << "[QueuedEventSource] Stopped thread." << endl;
+ }
+}
+
+
+/** Push an unprepared event onto the queue.
+ */
+void
+QueuedEventSource::push(QueuedEvent* const ev)
+{
+ assert(!ev->is_prepared());
+
+ if (m_events[m_back] != NULL) {
+ cerr << "[QueuedEventSource] Error: Queue is full! Event is lost, please report!" << endl;
+ delete ev;
+ } else {
+ m_events[m_back] = ev;
+ m_back = (m_back + 1) % m_size;
+ m_semaphore.post();
+ }
+}
+
+
+/** Pops the prepared event at the front of the queue, if it exists.
+ *
+ * This method will only pop events that have been prepared, and are
+ * stamped before the time passed. In other words, it may return NULL
+ * even if there are events pending in the queue. The events returned are
+ * actually QueuedEvent*s, but after this they are "normal" events and the
+ * engine deals with them just like a realtime in-band event.
+ */
+Event*
+QueuedEventSource::pop_earliest_event_before(const samplecount time)
+{
+ QueuedEvent* front_event = m_events[m_front];
+
+ // Pop
+ if (front_event != NULL && front_event->time_stamp() < time && front_event->is_prepared()) {
+ m_events[m_front] = NULL;
+ m_front = (m_front + 1) % m_size;
+ return front_event;
+ } else {
+ return NULL;
+ }
+}
+
+
+// Private //
+
+
+
+/** Signal that the blocking event is finished.
+ *
+ * When this is called preparing will resume. This will be called by
+ * blocking events in their post_process() method.
+ */
+void
+QueuedEventSource::unblock()
+{
+ /* FIXME: Make this a semaphore, and have events signal at the end of their
+ * execute() methods so the preprocessor can start preparing events immediately
+ * instead of waiting for the postprocessor to get around to finalizing the event? */
+ pthread_mutex_lock(&m_blocking_mutex);
+ pthread_cond_signal(&m_blocking_cond);
+ pthread_mutex_unlock(&m_blocking_mutex);
+}
+
+
+void*
+QueuedEventSource::m_prepare_loop()
+{
+ QueuedEvent* ev = NULL;
+
+ while (true) {
+ m_semaphore.wait();
+
+ if (m_prepare_thread_exit_flag)
+ break; // exit signalled
+
+ ev = m_events[m_prepared_back];
+ assert(ev != NULL);
+
+ if (ev == NULL) {
+ cerr << "[QueuedEventSource] ERROR: Signalled, but event is NULL." << endl;
+ continue;
+ }
+
+ assert(ev != NULL);
+ assert(!ev->is_prepared());
+
+ if (ev->is_blocking())
+ pthread_mutex_lock(&m_blocking_mutex);
+
+ ev->pre_process();
+
+ m_prepared_back = (m_prepared_back+1) % m_size;
+
+ // If a blocking event, wait for event to finish passing through
+ // the audio cycle before preparing the next event
+ if (ev->is_blocking()) {
+ pthread_cond_wait(&m_blocking_cond, &m_blocking_mutex);
+ pthread_mutex_unlock(&m_blocking_mutex);
+ }
+ }
+
+ cout << "[QueuedEventSource] Exiting slow event queue thread." << endl;
+ return NULL;
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/QueuedEventSource.h b/src/libs/engine/QueuedEventSource.h
new file mode 100644
index 00000000..002fc642
--- /dev/null
+++ b/src/libs/engine/QueuedEventSource.h
@@ -0,0 +1,83 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef QUEUEDEVENTSOURCE_H
+#define QUEUEDEVENTSOURCE_H
+
+#include <cstdlib>
+#include <pthread.h>
+#include "util/types.h"
+#include "util/Semaphore.h"
+#include "EventSource.h"
+
+namespace Om {
+
+class Event;
+class QueuedEvent;
+
+
+/** Queue of events that need processing before reaching the audio thread.
+ *
+ * Implemented as a deque (ringbuffer) in a circular array. Pushing and
+ * popping are threadsafe, as long as a single thread pushes and a single
+ * thread pops (ie this data structure is threadsafe, but the push and pop
+ * methods themselves are not).
+ */
+class QueuedEventSource : public EventSource
+{
+public:
+ QueuedEventSource(size_t size);
+ ~QueuedEventSource();
+
+ Event* pop_earliest_event_before(const samplecount time);
+
+ void unblock();
+
+ void start();
+ void stop();
+
+protected:
+ void push(QueuedEvent* const ev);
+
+private:
+ // Prevent copies (undefined)
+ QueuedEventSource(const QueuedEventSource&);
+ QueuedEventSource& operator=(const QueuedEventSource&);
+
+ // Note that it's crucially important which functions access which of these
+ // variables, to maintain threadsafeness.
+
+ size_t m_front; ///< Front of queue
+ size_t m_back; ///< Back of entire queue (1 past index of back element)
+ size_t m_prepared_back; ///< Back of prepared section (1 past index of back prepared element)
+ const size_t m_size;
+ QueuedEvent** m_events;
+
+ bool m_thread_exists;
+ bool m_prepare_thread_exit_flag;
+ pthread_t m_prepare_thread;
+ Semaphore m_semaphore; ///< Counting semaphor for driving prepare thread
+ pthread_mutex_t m_blocking_mutex;
+ pthread_cond_t m_blocking_cond;
+
+ static void* prepare_loop(void* q) { return ((QueuedEventSource*)q)->m_prepare_loop(); }
+ void* m_prepare_loop();
+};
+
+
+} // namespace Om
+
+#endif // QUEUEDEVENTSOURCE_H
diff --git a/src/libs/engine/Responder.h b/src/libs/engine/Responder.h
new file mode 100644
index 00000000..cf16a20a
--- /dev/null
+++ b/src/libs/engine/Responder.h
@@ -0,0 +1,63 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef RESPONDER_H
+#define RESPONDER_H
+
+#include <inttypes.h>
+#include <string>
+#include "util/CountedPtr.h"
+#include "interface/ClientInterface.h"
+using std::string;
+
+namespace Om {
+
+using Shared::ClientInterface;
+
+
+/** Class to handle responding to clients.
+ *
+ * This is an abstract base class to fully abstract the details of client
+ * communication from the internals of the engine.
+ *
+ * Note that this class only handles sending responses to commands from
+ * clients, (ie OK or an error), <b>not</b> notifications (ie new node,
+ * disconnection) - that's what ClientInterface is for. If a command is
+ * a request, \ref find_client can find the corresponding ClientInterface
+ * for this client to send the reply to.
+ *
+ * ClientInterface and Responder are seperate because responding might not
+ * actually get exposed to the client interface (eg in simulated blocking
+ * interfaces that wait for responses before returning).
+ */
+class Responder
+{
+public:
+ Responder() {}
+ virtual ~Responder() {}
+
+ virtual CountedPtr<Shared::ClientInterface> find_client()
+ { return CountedPtr<Shared::ClientInterface>(NULL); }
+
+ virtual void respond_ok() {}
+ virtual void respond_error(const string& msg) {}
+};
+
+
+} // namespace Om
+
+#endif // RESPONDER_H
+
diff --git a/src/libs/engine/TransportNode.cpp b/src/libs/engine/TransportNode.cpp
new file mode 100644
index 00000000..f5183d56
--- /dev/null
+++ b/src/libs/engine/TransportNode.cpp
@@ -0,0 +1,155 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "TransportNode.h"
+#include <jack/transport.h>
+#include "OutputPort.h"
+#include "Plugin.h"
+#include "JackAudioDriver.h"
+#include "Port.h"
+#include "util.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "PortInfo.h"
+
+namespace Om {
+
+
+TransportNode::TransportNode(const string& path, size_t poly, Patch* parent, samplerate srate, size_t buffer_size)
+: InternalNode(path, 1, parent, srate, buffer_size)
+{
+ m_num_ports = 10;
+ m_ports.alloc(m_num_ports);
+
+ OutputPort<sample>* spb_port = new OutputPort<sample>(this, "Seconds per Beat", 0, 1,
+ new PortInfo("Seconds per Beat", CONTROL, OUTPUT, 0, 0, 1), 1);
+ m_ports.at(0) = spb_port;
+
+ OutputPort<sample>* bpb_port = new OutputPort<sample>(this, "Beats per Bar", 1, 1,
+ new PortInfo("Beats per Bar", CONTROL, OUTPUT, 0, 0, 1), 1);
+ m_ports.at(1) = bpb_port;
+
+ OutputPort<sample>* bar_port = new OutputPort<sample>(this, "Bar", 3, 1,
+ new PortInfo("Bar", CONTROL, OUTPUT, 0, 0, 1), buffer_size);
+ m_ports.at(2) = bar_port;
+
+ OutputPort<sample>* beat_port = new OutputPort<sample>(this, "Beat", 3, 1,
+ new PortInfo("Beat", CONTROL, OUTPUT, 0, 0, 1), buffer_size);
+ m_ports.at(3) = beat_port;
+
+ OutputPort<sample>* frame_port = new OutputPort<sample>(this, "Frame", 3, 1,
+ new PortInfo("Frame", CONTROL, OUTPUT, 0, 0, 1), buffer_size);
+ m_ports.at(4) = frame_port;
+
+ OutputPort<sample>* hour_port = new OutputPort<sample>(this, "Hour", 3, 1,
+ new PortInfo("Hour", CONTROL, OUTPUT, 0, 0, 1), buffer_size);
+ m_ports.at(5) = hour_port;
+
+ OutputPort<sample>* minute_port = new OutputPort<sample>(this, "Minute", 3, 1,
+ new PortInfo("Minute", CONTROL, OUTPUT, 0, 0, 1), buffer_size);
+ m_ports.at(6) = minute_port;
+
+ OutputPort<sample>* second_port = new OutputPort<sample>(this, "Second", 3, 1,
+ new PortInfo("Second", CONTROL, OUTPUT, 0, 0, 1), buffer_size);
+ m_ports.at(7) = second_port;
+
+ OutputPort<sample>* trg_port = new OutputPort<sample>(this, "Beat Tick", 2, 1,
+ new PortInfo("Beat Tick", AUDIO, OUTPUT, 0, 0, 1), buffer_size);
+ m_ports.at(8) = trg_port;
+
+ OutputPort<sample>* bar_trig_port = new OutputPort<sample>(this, "Bar Tick", 3, 1,
+ new PortInfo("Bar Tick", AUDIO, OUTPUT, 0, 0, 1), buffer_size);
+ m_ports.at(9) = bar_trig_port;
+
+ m_plugin.type(Plugin::Internal);
+ m_plugin.plug_label("transport");
+ m_plugin.name("Om Transport Node (BROKEN)");
+}
+
+
+void
+TransportNode::run(size_t nframes)
+{
+ NodeBase::run(nframes);
+#if 0
+
+ // FIXME: this will die horribly with any driver other than jack (in theory)
+ const jack_position_t* const position = ((JackAudioDriver*)om->audio_driver())->position();
+ jack_transport_state_t state = ((JackAudioDriver*)om->audio_driver())->transport_state();
+ double bpm = position->beats_per_minute;
+ float bpb = position->beats_per_bar;
+ float spb = 60.0 / bpm;
+
+ //cerr << "bpm = " << bpm << endl;
+ //cerr << "spb = " << spb << endl;
+
+ if (position->valid & JackPositionBBT) {
+ cerr << "bar: " << position->bar << endl;
+ cerr << "beat: " << position->beat << endl;
+ cerr << "tick: " << position->tick << endl;
+ } else {
+ cerr << "No BBT" << endl;
+ }
+
+ if (position->valid & JackBBTFrameOffset) {
+ cerr << "bbt_offset: " << position->bbt_offset << endl;
+ } else {
+ cerr << "No BBT offset" << endl;
+ }
+
+ if (position->valid & JackPositionTimecode) {
+ double time = position->frame_time;
+ cerr << "Seconds: " << time << " : " << endl;
+ /*time /= 60.0;
+ cerr << "Minutes: " << time << " : ";
+ time /= 60.0;
+ cerr << "Hours: " << time << " : ";*/
+ } else {
+ cerr << "No timecode." << endl;
+ }
+
+
+ ((OutputPort<sample>*)m_ports.at(0))->buffer(0)->set(spb, 0, 0);
+ ((OutputPort<sample>*)m_ports.at(1))->buffer(0)->set(bpb, 0, 0);
+
+ // fill the trigger buffers with zeros
+ ((OutputPort<sample>*)m_ports.at(2))->buffer(0)->set(0.0f, 0, nframes - 1);
+ ((OutputPort<sample>*)m_ports.at(3))->buffer(0)->set(0.0f, 0, nframes - 1);
+
+ // if the transport is rolling, add triggers at the right frame positions
+ if ((position->valid & JackTransportBBT) && (state == JackTransportRolling)) {
+ double frames_per_beat = position->frame_rate * spb;
+ double first_beat = (1.0f - position->tick / position->ticks_per_beat) * frames_per_beat;
+ int first_beat_no = position->beat;
+ if (first_beat >= frames_per_beat) {
+ first_beat -= frames_per_beat;
+ --first_beat_no;
+ }
+ for ( ; first_beat < nframes; first_beat += frames_per_beat) {
+ ((OutputPort<sample>*)m_ports.at(2))->buffer(0)->set(1.0f, size_t(first_beat));
+ if (first_beat_no % int(bpb) == 0) {
+ ((OutputPort<sample>*)m_ports.at(3))->buffer(0)->set(1.0f, size_t(first_beat));
+ ++first_beat_no;
+ }
+ }
+ }
+ #endif
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/TransportNode.h b/src/libs/engine/TransportNode.h
new file mode 100644
index 00000000..15b1059c
--- /dev/null
+++ b/src/libs/engine/TransportNode.h
@@ -0,0 +1,48 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef TRANSPORTNODE_H
+#define TRANSPORTNODE_H
+
+
+#include <string>
+#include <jack/transport.h>
+#include "InternalNode.h"
+
+namespace Om {
+
+using std::string;
+
+
+/** Transport Node, brings timing information into patches.
+ *
+ * This node uses the Jack transport API to get information about BPM, time
+ * signature, etc.. all sample accurate. Using this you can do
+ * tempo-synced effects or even synthesis, etc.
+ */
+class TransportNode : public InternalNode
+{
+public:
+ TransportNode(const string& path, size_t poly, Patch* parent, samplerate srate, size_t buffer_size);
+
+ void run(size_t nframes);
+};
+
+
+} // namespace Om
+
+#endif // TRANSPORTNODE_H
diff --git a/src/libs/engine/Tree.h b/src/libs/engine/Tree.h
new file mode 100644
index 00000000..033d48b7
--- /dev/null
+++ b/src/libs/engine/Tree.h
@@ -0,0 +1,155 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef NODETREE_H
+#define NODETREE_H
+
+#include <string>
+#include <cassert>
+#include "MaidObject.h"
+using std::string;
+
+template<typename T> class Tree;
+
+
+/** A node in a Tree.
+ */
+template <typename T>
+class TreeNode : public MaidObject
+{
+public:
+ TreeNode(const string& key)
+ : m_parent(NULL), m_left_child(NULL), m_right_child(NULL),
+ m_key(key), m_node(NULL) {}
+
+ TreeNode(const string& key, T n)
+ : m_parent(NULL), m_left_child(NULL), m_right_child(NULL),
+ m_key(key), m_node(n) {}
+
+ ~TreeNode() {
+ assert(m_parent == NULL || m_parent->left_child() != this);
+ assert(m_parent == NULL || m_parent->right_child() != this);
+ assert(m_left_child == NULL || m_left_child->parent() != this);
+ assert(m_right_child == NULL || m_right_child->parent() != this);
+ m_parent = m_left_child = m_right_child = NULL;
+ }
+
+ string key() const { return m_key; }
+ void key(const string& key) { m_key = key; }
+ TreeNode<T>* parent() const { return m_parent; }
+ void parent(TreeNode<T>* n) { m_parent = n; }
+ TreeNode<T>* left_child() const { return m_left_child; }
+ void left_child(TreeNode<T>* n) { m_left_child = n; }
+ TreeNode<T>* right_child() const { return m_right_child; }
+ void right_child(TreeNode<T>* n) { m_right_child = n; }
+
+ bool is_leaf() { return (m_left_child == NULL && m_right_child == NULL); }
+ bool is_left_child() { return (m_parent != NULL && m_parent->left_child() == this); }
+ bool is_right_child() { return (m_parent != NULL && m_parent->right_child() == this); }
+
+ T node() { return m_node; }
+
+ friend class Tree<T>;
+
+protected:
+ // Prevent copies (undefined)
+ TreeNode(const TreeNode&);
+ TreeNode& operator=(const TreeNode&);
+
+ TreeNode<T>* m_parent;
+ TreeNode<T>* m_left_child;
+ TreeNode<T>* m_right_child;
+ string m_key;
+ T m_node;
+};
+
+
+/** The tree all objects are stored in.
+ *
+ * Textbook naive (unbalanced) Binary Search Tree. Slightly different
+ * from a usual BST implementation in that the "Node" classes (TreeNode) are
+ * exposed to the user. This is so QueuedEvent's can create the TreeNode in
+ * another thread, and the realtime jack thread can insert them (without having
+ * to allocating a TreeNode which is a no-no).
+ *
+ * It's also a more annoying implementation because there's no leaf type (since
+ * a leaf object would have to be deleted on insert).
+ *
+ * Tree<T>::iterator is not realtime safe, but the insert/remove/find methods
+ * of Tree<T> do not use them.
+ */
+template <typename T>
+class Tree
+{
+public:
+ Tree<T>() : m_root(0), m_size(0) {}
+ ~Tree<T>();
+
+ void insert(TreeNode<T>* const n);
+ TreeNode<T>* remove(const string& key);
+ T find(const string& key) const;
+ TreeNode<T>* find_treenode(const string& key) const;
+
+ size_t size() const { return m_size; }
+
+ /** NON realtime safe iterator for a Tree<T>. */
+ class iterator
+ {
+ public:
+ iterator(const Tree<T>* tree, size_t size);
+ ~iterator();
+
+ T operator*() const;
+ iterator& operator++();
+ bool operator!=(const iterator& iter) const;
+
+ friend class Tree<T>;
+
+ iterator(const iterator& copy);
+ iterator& operator=(const iterator& copy);
+
+ private:
+ int m_depth;
+ size_t m_size;
+ TreeNode<T>** m_stack;
+ const Tree<T>* m_tree;
+ };
+
+ iterator begin() const;
+ iterator end() const;
+
+private:
+ // Prevent copies (undefined)
+ Tree<T>(const Tree<T>&);
+ Tree<T>& operator=(const Tree<T>&);
+
+ void m_set_all_traversed_recursive(TreeNode<T>* root, bool b);
+
+ TreeNode<T>* m_find_smallest(TreeNode<T>* root);
+ TreeNode<T>* m_find_largest(TreeNode<T>* root);
+
+ TreeNode<T>* m_root;
+ size_t m_size;
+};
+
+
+/* This needs to be done so the templates are defined and can get instantiated
+ * automatically by the compilter.
+ */
+//#include "TreeImplementation.h"
+
+
+#endif // NODETREE_H
diff --git a/src/libs/engine/TreeImplementation.h b/src/libs/engine/TreeImplementation.h
new file mode 100644
index 00000000..a61ec407
--- /dev/null
+++ b/src/libs/engine/TreeImplementation.h
@@ -0,0 +1,410 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "Tree.h"
+#include <cstdlib>
+#include <iostream>
+#include <cassert>
+using std::cerr; using std::endl;
+
+
+/* FIXME: this is all in horrible need of a rewrite. */
+
+
+/** Destroy the tree.
+ *
+ * Note that this does not delete any TreeNodes still inside the tree,
+ * that is the user's responsibility.
+ */
+template <typename T>
+Tree<T>::~Tree()
+{
+}
+
+
+/** Insert a node into the tree. Realtime safe.
+ *
+ * @a n will be inserted using the key() field for searches.
+ * n->key() must not be the empty string.
+ */
+template<typename T>
+void
+Tree<T>::insert(TreeNode<T>* const n)
+{
+ assert(n != NULL);
+ assert(n->left_child() == NULL);
+ assert(n->right_child() == NULL);
+ assert(n->parent() == NULL);
+ assert(n->key().length() > 0);
+ assert(find_treenode(n->key()) == NULL);
+
+ if (m_root == NULL) {
+ m_root = n;
+ } else {
+ bool left = false; // which child to insert at
+ bool right = false;
+ TreeNode<T>* i = m_root;
+ while (true) {
+ assert(i != NULL);
+ if (n->key() <= i->key()) {
+ if (i->left_child() == NULL) {
+ left = true;
+ break;
+ } else {
+ i = i->left_child();
+ }
+ } else {
+ if (i->right_child() == NULL) {
+ right = true;
+ break;
+ } else {
+ i = i->right_child();
+ }
+ }
+ }
+
+ assert(i != NULL);
+ assert(left || right);
+ assert( ! (left && right) );
+
+ if (left) {
+ assert(i->left_child() == NULL);
+ i->left_child(n);
+ } else if (right) {
+ assert(i->right_child() == NULL);
+ i->right_child(n);
+ }
+ n->parent(i);
+ }
+ ++m_size;
+}
+
+
+/** Remove a node from the tree.
+ *
+ * Realtime safe, caller is responsible to delete returned value.
+ *
+ * @return NULL if object with @a key is not in tree.
+ */
+template<typename T>
+TreeNode<T>*
+Tree<T>::remove(const string& key)
+{
+ TreeNode<T>* node = find_treenode(key);
+ TreeNode<T>* n = node;
+ TreeNode<T>* swap = NULL;
+ T temp_node;
+ string temp_key;
+
+ if (node == NULL)
+ return NULL;
+
+ // Node is not even in tree
+ if (node->parent() == NULL && m_root != node)
+ return NULL;
+ // FIXME: What if the node is in a different tree? Check for this?
+
+#ifndef NDEBUG
+ const T& remove_node = node->node(); // for error checking
+#endif // NDEBUG
+
+ // n has two children
+ if (n->left_child() != NULL && n->right_child() != NULL) {
+ if (rand()%2)
+ swap = m_find_largest(n->left_child());
+ else
+ swap = m_find_smallest(n->right_child());
+
+ // Swap node's elements
+ temp_node = swap->m_node;
+ swap->m_node = n->m_node;
+ n->m_node = temp_node;
+
+ // Swap node's keys
+ temp_key = swap->m_key;
+ swap->m_key = n->m_key;
+ n->m_key = temp_key;
+
+ n = swap;
+ assert(n != NULL);
+ }
+
+ // be sure we swapped correctly (ie right node is getting removed)
+ assert(n->node() == remove_node);
+
+ // n now has at most one child
+ assert(n->left_child() == NULL || n->right_child() == NULL);
+
+ if (n->is_leaf()) {
+ if (n->is_left_child())
+ n->parent()->left_child(NULL);
+ else if (n->is_right_child())
+ n->parent()->right_child(NULL);
+
+ if (m_root == n) m_root = NULL;
+ } else { // has a single child
+ TreeNode<T>* child = NULL;
+ if (n->left_child() != NULL)
+ child = n->left_child();
+ else if (n->right_child() != NULL)
+ child = n->right_child();
+ else
+ exit(EXIT_FAILURE);
+
+ assert(child != n);
+ assert(child != NULL);
+ assert(n->parent() != n);
+
+ if (n->is_left_child()) {
+ assert(n->parent() != child);
+ n->parent()->left_child(child);
+ child->parent(n->parent());
+ } else if (n->is_right_child()) {
+ assert(n->parent() != child);
+ n->parent()->right_child(child);
+ child->parent(n->parent());
+ } else {
+ child->parent(NULL);
+ }
+ if (m_root == n) m_root = child;
+ }
+
+ // Be sure node is cut off completely
+ assert(n != NULL);
+ assert(n->parent() == NULL || n->parent()->left_child() != n);
+ assert(n->parent() == NULL || n->parent()->right_child() != n);
+ assert(n->left_child() == NULL || n->left_child()->parent() != n);
+ assert(n->right_child() == NULL || n->right_child()->parent() != n);
+ assert(m_root != n);
+
+ n->parent(NULL);
+ n->left_child(NULL);
+ n->right_child(NULL);
+
+ --m_size;
+
+ if (m_size == 0) m_root = NULL;
+
+ // Be sure right node is being removed
+ assert(n->node() == remove_node);
+
+ return n;
+}
+
+
+template<typename T>
+T
+Tree<T>::find(const string& name) const
+{
+ TreeNode<T>* tn = find_treenode(name);
+
+ return (tn == NULL) ? NULL : tn->node();
+}
+
+
+template<typename T>
+TreeNode<T>*
+Tree<T>::find_treenode(const string& name) const
+{
+ TreeNode<T>* i = m_root;
+ int cmp = 0;
+
+ while (i != NULL) {
+ cmp = name.compare(i->key());
+ if (cmp < 0)
+ i = i->left_child();
+ else if (cmp > 0)
+ i = i->right_child();
+ else
+ break;
+ }
+
+ return i;
+}
+
+
+/// Private ///
+template<typename T>
+void
+Tree<T>::m_set_all_traversed_recursive(TreeNode<T>* root, bool b)
+{
+ assert(root != NULL);
+
+ // Preorder traversal
+ root->node()->traversed(b);
+ if (root->left_child() != NULL)
+ m_set_all_traversed_recursive(root->left_child(), b);
+ if (root->right_child() != NULL)
+ m_set_all_traversed_recursive(root->right_child(), b);
+}
+
+
+/** Finds the smallest (key) node in the subtree rooted at "root"
+ */
+template<typename T>
+TreeNode<T>*
+Tree<T>::m_find_smallest(TreeNode<T>* root)
+{
+ TreeNode<T>* r = root;
+
+ while (r->left_child() != NULL)
+ r = r->left_child();
+
+ return r;
+}
+
+
+/** Finds the largest (key) node in the subtree rooted at "root".
+ */
+template<typename T>
+TreeNode<T>*
+Tree<T>::m_find_largest(TreeNode<T>* root)
+{
+ TreeNode<T>* r = root;
+
+ while (r->right_child() != NULL)
+ r = r->right_child();
+
+ return r;
+
+}
+
+
+
+//// Iterator Stuff ////
+
+
+
+template<typename T>
+Tree<T>::iterator::iterator(const Tree *tree, size_t size)
+: m_depth(-1),
+ m_size(size),
+ m_stack(NULL),
+ m_tree(tree)
+{
+ if (size > 0)
+ m_stack = new TreeNode<T>*[size];
+}
+
+
+template<typename T>
+Tree<T>::iterator::~iterator()
+{
+ delete[] m_stack;
+}
+
+
+/* FIXME: Make these next two not memcpy (possibly have to force a single
+ * iterator existing at any given time) for speed.
+ */
+
+// Copy constructor (for the typical for loop usage)
+template<typename T>
+Tree<T>::iterator::iterator(const Tree<T>::iterator& copy)
+: m_depth(copy.m_depth),
+ m_size(copy.m_size),
+ m_tree(copy.m_tree)
+{
+ if (m_size > 0) {
+ m_stack = new TreeNode<T>*[m_size];
+ memcpy(m_stack, copy.m_stack, m_size * sizeof(TreeNode<T>*));
+ }
+}
+
+
+// Assignment operator
+template<typename T>
+typename Tree<T>::iterator&
+Tree<T>::iterator::operator=(const Tree<T>::iterator& copy) {
+ m_depth = copy.m_depth;
+ m_size = copy.m_size;
+ m_tree = copy.m_tree;
+
+ if (m_size > 0) {
+ m_stack = new TreeNode<T>*[m_size];
+ memcpy(m_stack, copy.m_stack, m_size * sizeof(TreeNode<T>*));
+ }
+ return *this;
+}
+
+
+template<typename T>
+T
+Tree<T>::iterator::operator*() const
+{
+ assert(m_depth >= 0);
+ return m_stack[m_depth]->node();
+}
+
+
+template<typename T>
+typename Tree<T>::iterator&
+Tree<T>::iterator::operator++()
+{
+ assert(m_depth >= 0);
+
+ TreeNode<T>* tn = m_stack[m_depth];
+ --m_depth;
+
+ tn = tn->right_child();
+ while (tn != NULL) {
+ ++m_depth;
+ m_stack[m_depth] = tn;
+ tn = tn->left_child();
+ }
+
+ return *this;
+}
+
+
+template<typename T>
+bool
+Tree<T>::iterator::operator!=(const Tree<T>::iterator& iter) const
+{
+ // (DeMorgan's Law)
+ return (m_tree != iter.m_tree || m_depth != iter.m_depth);
+}
+
+
+template<typename T>
+typename Tree<T>::iterator
+Tree<T>::begin() const
+{
+ typename Tree<T>::iterator iter(this, m_size);
+ iter.m_depth = -1;
+
+ TreeNode<T> *ptr = m_root;
+ while (ptr != NULL) {
+ iter.m_depth++;
+ iter.m_stack[iter.m_depth] = ptr;
+ ptr = ptr->left_child();
+ }
+
+ return iter;
+}
+
+
+template<typename T>
+typename Tree<T>::iterator
+Tree<T>::end() const
+{
+ typename Tree<T>::iterator iter(this, 0);
+ iter.m_depth = -1;
+
+ return iter;
+}
+
+
diff --git a/src/libs/engine/cmdline.c b/src/libs/engine/cmdline.c
new file mode 100644
index 00000000..6b7ddf6a
--- /dev/null
+++ b/src/libs/engine/cmdline.c
@@ -0,0 +1,150 @@
+/*
+ File autogenerated by gengetopt version 2.10
+ generated with the following command:
+ gengetopt
+
+ The developers of gengetopt consider the fixed text that goes in all
+ gengetopt output files to be in the public domain:
+ we make no copyright claims on it.
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* If we use autoconf. */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "getopt.h"
+
+#include "cmdline.h"
+
+void
+cmdline_parser_print_version (void)
+{
+ printf ("%s %s\n", CMDLINE_PARSER_PACKAGE, CMDLINE_PARSER_VERSION);
+}
+
+void
+cmdline_parser_print_help (void)
+{
+ cmdline_parser_print_version ();
+ printf("\n"
+ "Usage: %s [OPTIONS]...\n", CMDLINE_PARSER_PACKAGE);
+ printf(" -h --help Print help and exit\n");
+ printf(" -V --version Print version and exit\n");
+ printf(" -pSTRING --port=STRING OSC port to listen on (default='16180')\n");
+ printf(" -i --in-jackd Run engine as in-process JACK client (default=off)\n");
+}
+
+
+static char *gengetopt_strdup (const char *s);
+
+/* gengetopt_strdup() */
+/* strdup.c replacement of strdup, which is not standard */
+char *
+gengetopt_strdup (const char *s)
+{
+ char *result = (char*)malloc(strlen(s) + 1);
+ if (result == (char*)0)
+ return (char*)0;
+ strcpy(result, s);
+ return result;
+}
+
+int
+cmdline_parser (int argc, char * const *argv, struct gengetopt_args_info *args_info)
+{
+ int c; /* Character of the parsed option. */
+ int missing_required_options = 0;
+
+ args_info->help_given = 0 ;
+ args_info->version_given = 0 ;
+ args_info->port_given = 0 ;
+ args_info->in_jackd_given = 0 ;
+#define clear_args() { \
+ args_info->port_arg = gengetopt_strdup("16180") ;\
+ args_info->in_jackd_flag = 0;\
+}
+
+ clear_args();
+
+ optarg = 0;
+ optind = 1;
+ opterr = 1;
+ optopt = '?';
+
+ while (1)
+ {
+ int option_index = 0;
+ char *stop_char;
+
+ static struct option long_options[] = {
+ { "help", 0, NULL, 'h' },
+ { "version", 0, NULL, 'V' },
+ { "port", 1, NULL, 'p' },
+ { "in-jackd", 0, NULL, 'i' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ stop_char = 0;
+ c = getopt_long (argc, argv, "hVp:i", long_options, &option_index);
+
+ if (c == -1) break; /* Exit from `while (1)' loop. */
+
+ switch (c)
+ {
+ case 'h': /* Print help and exit. */
+ clear_args ();
+ cmdline_parser_print_help ();
+ exit (EXIT_SUCCESS);
+
+ case 'V': /* Print version and exit. */
+ clear_args ();
+ cmdline_parser_print_version ();
+ exit (EXIT_SUCCESS);
+
+ case 'p': /* OSC port to listen on. */
+ if (args_info->port_given)
+ {
+ fprintf (stderr, "%s: `--port' (`-p') option given more than once\n", CMDLINE_PARSER_PACKAGE);
+ clear_args ();
+ exit (EXIT_FAILURE);
+ }
+ args_info->port_given = 1;
+ args_info->port_arg = gengetopt_strdup (optarg);
+ break;
+
+ case 'i': /* Run engine as in-process JACK client. */
+ if (args_info->in_jackd_given)
+ {
+ fprintf (stderr, "%s: `--in-jackd' (`-i') option given more than once\n", CMDLINE_PARSER_PACKAGE);
+ clear_args ();
+ exit (EXIT_FAILURE);
+ }
+ args_info->in_jackd_given = 1;
+ args_info->in_jackd_flag = !(args_info->in_jackd_flag);
+ break;
+
+
+ case 0: /* Long option with no short option */
+
+ case '?': /* Invalid option. */
+ /* `getopt_long' already printed an error message. */
+ exit (EXIT_FAILURE);
+
+ default: /* bug: option not considered. */
+ fprintf (stderr, "%s: option unknown: %c\n", CMDLINE_PARSER_PACKAGE, c);
+ abort ();
+ } /* switch */
+ } /* while */
+
+
+ if ( missing_required_options )
+ exit (EXIT_FAILURE);
+
+ return 0;
+}
diff --git a/src/libs/engine/cmdline.ggo b/src/libs/engine/cmdline.ggo
new file mode 100644
index 00000000..8c006ca7
--- /dev/null
+++ b/src/libs/engine/cmdline.ggo
@@ -0,0 +1,7 @@
+# Process this file with gengetopt -u to generate the necessary code (in cmdline.h, cmdline.c)
+
+package "Om - An OSC controlled realtime modular synthesizer"
+
+option "port" p "OSC port to listen on" string default="16180" no
+option "in-jackd" i "Run engine as in-process JACK client" flag off
+
diff --git a/src/libs/engine/cmdline.h b/src/libs/engine/cmdline.h
new file mode 100644
index 00000000..fe36a969
--- /dev/null
+++ b/src/libs/engine/cmdline.h
@@ -0,0 +1,45 @@
+/* cmdline.h */
+
+/* File autogenerated by gengetopt version 2.10 */
+
+#ifndef CMDLINE_H
+#define CMDLINE_H
+
+/* If we use autoconf. */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#ifndef CMDLINE_PARSER_PACKAGE
+#define CMDLINE_PARSER_PACKAGE "Om - An OSC controlled realtime modular synthesizer"
+#endif
+
+#ifndef CMDLINE_PARSER_VERSION
+#define CMDLINE_PARSER_VERSION VERSION
+#endif
+
+struct gengetopt_args_info
+{
+ char * port_arg; /* OSC port to listen on (default='16180'). */
+ int in_jackd_flag; /* Run engine as in-process JACK client (default=off). */
+
+ int help_given ; /* Whether help was given. */
+ int version_given ; /* Whether version was given. */
+ int port_given ; /* Whether port was given. */
+ int in_jackd_given ; /* Whether in-jackd was given. */
+
+} ;
+
+int cmdline_parser (int argc, char * const *argv, struct gengetopt_args_info *args_info);
+
+void cmdline_parser_print_help(void);
+void cmdline_parser_print_version(void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* CMDLINE_H */
diff --git a/src/libs/engine/events.h b/src/libs/engine/events.h
new file mode 100644
index 00000000..f1bc5f58
--- /dev/null
+++ b/src/libs/engine/events.h
@@ -0,0 +1,62 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef EVENTS_H
+#define EVENTS_H
+
+#include "config.h"
+
+#include "ActivateEvent.h"
+#include "DeactivateEvent.h"
+#include "EnablePatchEvent.h"
+#include "DisablePatchEvent.h"
+#include "ClearPatchEvent.h"
+#include "SetPortValueEvent.h"
+#include "SetPortValueQueuedEvent.h"
+#include "ConnectionEvent.h"
+#include "DisconnectionEvent.h"
+#include "AddNodeEvent.h"
+#include "CreatePatchEvent.h"
+#include "DestroyEvent.h"
+#include "SetMetadataEvent.h"
+#include "RequestMetadataEvent.h"
+#include "RequestPortValueEvent.h"
+#include "RequestAllObjectsEvent.h"
+#include "RequestPluginsEvent.h"
+#include "LoadPluginsEvent.h"
+#include "NoteOnEvent.h"
+#include "NoteOffEvent.h"
+#include "AllNotesOffEvent.h"
+#include "DisconnectNodeEvent.h"
+#include "RegisterClientEvent.h"
+#include "UnregisterClientEvent.h"
+#include "RenameEvent.h"
+#include "PingQueuedEvent.h"
+#include "MidiLearnEvent.h"
+
+#ifdef HAVE_LASH
+#include "LashRestoreDoneEvent.h"
+#endif
+
+#ifdef HAVE_DSSI
+#include "DSSIUpdateEvent.h"
+#include "DSSIControlEvent.h"
+#include "DSSIConfigureEvent.h"
+#include "DSSIProgramEvent.h"
+#endif
+
+#endif // EVENTS_H
+
diff --git a/src/libs/engine/events/ActivateEvent.cpp b/src/libs/engine/events/ActivateEvent.cpp
new file mode 100644
index 00000000..671b26d5
--- /dev/null
+++ b/src/libs/engine/events/ActivateEvent.cpp
@@ -0,0 +1,52 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ActivateEvent.h"
+#include "Responder.h"
+#include "Om.h"
+#include "OmApp.h"
+
+namespace Om {
+
+
+ActivateEvent::ActivateEvent(CountedPtr<Responder> responder)
+: QueuedEvent(responder)
+{
+}
+
+
+void
+ActivateEvent::pre_process()
+{
+ QueuedEvent::pre_process();
+
+ if (om != NULL)
+ om->activate();
+}
+
+
+void
+ActivateEvent::post_process()
+{
+ if (om != NULL)
+ m_responder->respond_ok();
+ else
+ m_responder->respond_error("Not ready to activate yet.");
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/events/ActivateEvent.h b/src/libs/engine/events/ActivateEvent.h
new file mode 100644
index 00000000..280b5523
--- /dev/null
+++ b/src/libs/engine/events/ActivateEvent.h
@@ -0,0 +1,41 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef ACTIVATEEVENT_H
+#define ACTIVATEEVENT_H
+
+#include "QueuedEvent.h"
+
+namespace Om {
+
+
+/** Activates the engine.
+ *
+ * \ingroup engine
+ */
+class ActivateEvent : public QueuedEvent
+{
+public:
+ ActivateEvent(CountedPtr<Responder> responder);
+
+ void pre_process();
+ void post_process();
+};
+
+
+} // namespace Om
+
+#endif // ACTIVATEEVENT_H
diff --git a/src/libs/engine/events/AddNodeEvent.cpp b/src/libs/engine/events/AddNodeEvent.cpp
new file mode 100644
index 00000000..2b31ef4a
--- /dev/null
+++ b/src/libs/engine/events/AddNodeEvent.cpp
@@ -0,0 +1,128 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "AddNodeEvent.h"
+#include "Responder.h"
+#include "Patch.h"
+#include "Node.h"
+#include "Tree.h"
+#include "Plugin.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "Patch.h"
+#include "NodeFactory.h"
+#include "ClientBroadcaster.h"
+#include "Maid.h"
+#include "util/Path.h"
+#include "ObjectStore.h"
+#include "util/Path.h"
+#include "Port.h"
+
+namespace Om {
+
+
+AddNodeEvent::AddNodeEvent(CountedPtr<Responder> responder, const string& path, Plugin* plugin, bool poly)
+: QueuedEvent(responder),
+ m_path(path),
+ m_plugin(plugin),
+ m_poly(poly),
+ m_patch(NULL),
+ m_node(NULL),
+ m_process_order(NULL),
+ m_node_already_exists(false)
+{
+}
+
+
+AddNodeEvent::~AddNodeEvent()
+{
+ delete m_plugin;
+}
+
+
+void
+AddNodeEvent::pre_process()
+{
+ if (om->object_store()->find(m_path) != NULL) {
+ m_node_already_exists = true;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ m_patch = om->object_store()->find_patch(m_path.parent());
+
+ if (m_patch != NULL) {
+ if (m_poly)
+ m_node = om->node_factory()->load_plugin(m_plugin, m_path.name(), m_patch->internal_poly(), m_patch);
+ else
+ m_node = om->node_factory()->load_plugin(m_plugin, m_path.name(), 1, m_patch);
+
+ if (m_node != NULL) {
+ m_node->activate();
+
+ // This can be done here because the audio thread doesn't touch the
+ // node tree - just the process order array
+ m_patch->add_node(new ListNode<Node*>(m_node));
+ m_node->add_to_store();
+
+ if (m_patch->process())
+ m_process_order = m_patch->build_process_order();
+ }
+ }
+ QueuedEvent::pre_process();
+}
+
+
+void
+AddNodeEvent::execute(samplecount offset)
+{
+ QueuedEvent::execute(offset);
+
+ if (m_node != NULL) {
+ m_node->add_to_patch();
+
+ if (m_patch->process_order() != NULL)
+ om->maid()->push(m_patch->process_order());
+ m_patch->process_order(m_process_order);
+ }
+}
+
+
+void
+AddNodeEvent::post_process()
+{
+ string msg;
+ if (m_node_already_exists) {
+ msg = string("Could not create node - ").append(m_path);// + " already exists.";
+ m_responder->respond_error(msg);
+ } else if (m_patch == NULL) {
+ msg = "Could not find patch '" + m_path.parent() +"' for add_node.";
+ m_responder->respond_error(msg);
+ } else if (m_node == NULL) {
+ msg = "Unable to load node ";
+ msg.append(m_path).append(" (you're missing the plugin \"").append(
+ m_plugin->lib_name()).append(":").append(m_plugin->plug_label()).append("\")");;
+ m_responder->respond_error(msg);
+ } else {
+ m_responder->respond_ok();
+ //om->client_broadcaster()->send_node_creation_messages(m_node);
+ om->client_broadcaster()->send_node(m_node);
+ }
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/events/AddNodeEvent.h b/src/libs/engine/events/AddNodeEvent.h
new file mode 100644
index 00000000..fe0236ba
--- /dev/null
+++ b/src/libs/engine/events/AddNodeEvent.h
@@ -0,0 +1,63 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef ADDNODEEVENT_H
+#define ADDNODEEVENT_H
+
+#include "QueuedEvent.h"
+#include "util/Path.h"
+#include <string>
+using std::string;
+
+template <typename T> class Array;
+template<typename T> class TreeNode;
+
+namespace Om {
+
+class Patch;
+class Node;
+class Plugin;
+
+
+/** An event to load a Node and insert it into a Patch.
+ *
+ * \ingroup engine
+ */
+class AddNodeEvent : public QueuedEvent
+{
+public:
+ AddNodeEvent(CountedPtr<Responder> responder, const string& path, Plugin* plugin, bool poly);
+ ~AddNodeEvent();
+
+ void pre_process();
+ void execute(samplecount offset);
+ void post_process();
+
+private:
+ string m_patch_name;
+ Path m_path;
+ Plugin* m_plugin;
+ bool m_poly;
+ Patch* m_patch;
+ Node* m_node;
+ Array<Node*>* m_process_order; // Patch's new process order
+ bool m_node_already_exists;
+};
+
+
+} // namespace Om
+
+#endif // ADDNODEEVENT_H
diff --git a/src/libs/engine/events/AllNotesOffEvent.cpp b/src/libs/engine/events/AllNotesOffEvent.cpp
new file mode 100644
index 00000000..aa3a00f1
--- /dev/null
+++ b/src/libs/engine/events/AllNotesOffEvent.cpp
@@ -0,0 +1,67 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "AllNotesOffEvent.h"
+#include "Responder.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "ObjectStore.h"
+
+namespace Om {
+
+
+/** Note off with patch explicitly passed - triggered by MIDI.
+ */
+AllNotesOffEvent::AllNotesOffEvent(CountedPtr<Responder> responder, Patch* patch)
+: Event(responder),
+ m_patch(patch)
+{
+}
+
+
+/** Note off event with lookup - triggered by OSC.
+ */
+AllNotesOffEvent::AllNotesOffEvent(CountedPtr<Responder> responder, const string& patch_path)
+: Event(responder),
+ m_patch(NULL),
+ m_patch_path(patch_path)
+{
+}
+
+
+void
+AllNotesOffEvent::execute(samplecount offset)
+{
+ if (m_patch == NULL && m_patch_path != "")
+ m_patch = om->object_store()->find_patch(m_patch_path);
+
+ //if (m_patch != NULL)
+ // for (List<MidiInNode*>::iterator j = m_patch->midi_in_nodes().begin(); j != m_patch->midi_in_nodes().end(); ++j)
+ // (*j)->all_notes_off(offset);
+}
+
+
+void
+AllNotesOffEvent::post_process()
+{
+ if (m_patch != NULL)
+ m_responder->respond_ok();
+}
+
+
+} // namespace Om
+
+
diff --git a/src/libs/engine/events/AllNotesOffEvent.h b/src/libs/engine/events/AllNotesOffEvent.h
new file mode 100644
index 00000000..ea23301b
--- /dev/null
+++ b/src/libs/engine/events/AllNotesOffEvent.h
@@ -0,0 +1,50 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef ALLNOTESOFFEVENT_H
+#define ALLNOTESOFFEVENT_H
+
+#include "Event.h"
+#include <string>
+using std::string;
+
+namespace Om {
+
+class Patch;
+
+
+/** A note off event for all active voices.
+ *
+ * \ingroup engine
+ */
+class AllNotesOffEvent : public Event
+{
+public:
+ AllNotesOffEvent(CountedPtr<Responder> responder, Patch* patch);
+ AllNotesOffEvent(CountedPtr<Responder> responder, const string& patch_path);
+
+ void execute(samplecount offset);
+ void post_process();
+
+private:
+ Patch* m_patch;
+ string m_patch_path;
+};
+
+
+} // namespace Om
+
+#endif // ALLNOTESOFFEVENT_H
diff --git a/src/libs/engine/events/ClearPatchEvent.cpp b/src/libs/engine/events/ClearPatchEvent.cpp
new file mode 100644
index 00000000..8b8fc223
--- /dev/null
+++ b/src/libs/engine/events/ClearPatchEvent.cpp
@@ -0,0 +1,114 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ClearPatchEvent.h"
+#include "Responder.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "Patch.h"
+#include "ClientBroadcaster.h"
+#include "util.h"
+#include "ObjectStore.h"
+#include "Port.h"
+#include "Maid.h"
+#include "Node.h"
+#include "Connection.h"
+#include "QueuedEventSource.h"
+
+namespace Om {
+
+
+ClearPatchEvent::ClearPatchEvent(CountedPtr<Responder> responder, const string& patch_path)
+: QueuedEvent(responder, true),
+ m_patch_path(patch_path),
+ m_patch(NULL),
+ m_process(false)
+{
+}
+
+
+void
+ClearPatchEvent::pre_process()
+{
+ m_patch = om->object_store()->find_patch(m_patch_path);
+
+ if (m_patch != NULL) {
+
+ m_process = m_patch->process();
+
+ for (List<Node*>::const_iterator i = m_patch->nodes().begin(); i != m_patch->nodes().end(); ++i)
+ (*i)->remove_from_store();
+ }
+
+ QueuedEvent::pre_process();
+}
+
+
+void
+ClearPatchEvent::execute(samplecount offset)
+{
+ if (m_patch != NULL) {
+ m_patch->process(false);
+
+ for (List<Node*>::const_iterator i = m_patch->nodes().begin(); i != m_patch->nodes().end(); ++i)
+ (*i)->remove_from_patch();
+
+ if (m_patch->process_order() != NULL) {
+ om->maid()->push(m_patch->process_order());
+ m_patch->process_order(NULL);
+ }
+ }
+
+ QueuedEvent::execute(offset);
+}
+
+
+void
+ClearPatchEvent::post_process()
+{
+ if (m_patch != NULL) {
+ // Delete all nodes
+ for (List<Node*>::iterator i = m_patch->nodes().begin(); i != m_patch->nodes().end(); ++i) {
+ (*i)->deactivate();
+ delete *i;
+ }
+ m_patch->nodes().clear();
+
+ // Delete all connections
+ for (List<Connection*>::iterator i = m_patch->connections().begin(); i != m_patch->connections().end(); ++i)
+ delete *i;
+ m_patch->connections().clear();
+
+ // Restore patch's run state
+ m_patch->process(m_process);
+
+ // Make sure everything's sane
+ assert(m_patch->nodes().size() == 0);
+ assert(m_patch->connections().size() == 0);
+
+ // Reply
+ m_responder->respond_ok();
+ om->client_broadcaster()->send_patch_cleared(m_patch_path);
+ } else {
+ m_responder->respond_error(string("Patch ") + m_patch_path + " not found");
+ }
+
+ m_source->unblock();
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/events/ClearPatchEvent.h b/src/libs/engine/events/ClearPatchEvent.h
new file mode 100644
index 00000000..c6e531a8
--- /dev/null
+++ b/src/libs/engine/events/ClearPatchEvent.h
@@ -0,0 +1,54 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef CLEARPATCHEVENT_H
+#define CLEARPATCHEVENT_H
+
+#include <string>
+#include "QueuedEvent.h"
+#include "Array.h"
+
+using std::string;
+
+namespace Om {
+
+class Patch;
+
+
+/** Delete all nodes from a patch.
+ *
+ * \ingroup engine
+ */
+class ClearPatchEvent : public QueuedEvent
+{
+public:
+ ClearPatchEvent(CountedPtr<Responder> responder, const string& patch_path);
+
+ void pre_process();
+ void execute(samplecount offset);
+ void post_process();
+
+private:
+ string m_patch_path;
+ Patch* m_patch;
+ bool m_process;
+};
+
+
+} // namespace Om
+
+
+#endif // CLEARPATCHEVENT_H
diff --git a/src/libs/engine/events/ConnectionEvent.cpp b/src/libs/engine/events/ConnectionEvent.cpp
new file mode 100644
index 00000000..fe3b991e
--- /dev/null
+++ b/src/libs/engine/events/ConnectionEvent.cpp
@@ -0,0 +1,240 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ConnectionEvent.h"
+#include <string>
+#include "Responder.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "ConnectionBase.h"
+#include "InputPort.h"
+#include "OutputPort.h"
+#include "Patch.h"
+#include "ClientBroadcaster.h"
+#include "Port.h"
+#include "PortInfo.h"
+#include "Maid.h"
+#include "ObjectStore.h"
+#include "util/Path.h"
+
+using std::string;
+namespace Om {
+
+
+//// ConnectionEvent ////
+
+
+ConnectionEvent::ConnectionEvent(CountedPtr<Responder> responder, const string& src_port_path, const string& dst_port_path)
+: QueuedEvent(responder),
+ m_src_port_path(src_port_path),
+ m_dst_port_path(dst_port_path),
+ m_patch(NULL),
+ m_src_port(NULL),
+ m_dst_port(NULL),
+ m_typed_event(NULL),
+ m_error(NO_ERROR)
+{
+}
+
+
+ConnectionEvent::~ConnectionEvent()
+{
+ delete m_typed_event;
+}
+
+
+void
+ConnectionEvent::pre_process()
+{
+ if (m_src_port_path.parent().parent() != m_dst_port_path.parent().parent()) {
+ m_error = PARENT_PATCH_DIFFERENT;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ /*m_patch = om->object_store()->find_patch(m_src_port_path.parent().parent());
+
+ if (m_patch == NULL) {
+ m_error = PORT_NOT_FOUND;
+ QueuedEvent::pre_process();
+ return;
+ }*/
+
+ Port* port1 = om->object_store()->find_port(m_src_port_path);
+ Port* port2 = om->object_store()->find_port(m_dst_port_path);
+
+ if (port1 == NULL || port2 == NULL) {
+ m_error = PORT_NOT_FOUND;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ if (port1->port_info()->type() != port2->port_info()->type()) {
+ m_error = TYPE_MISMATCH;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ if (port1->port_info()->is_output() && port2->port_info()->is_input()) {
+ m_src_port = port1;
+ m_dst_port = port2;
+ } else if (port2->port_info()->is_output() && port1->port_info()->is_input()) {
+ m_src_port = port2;
+ m_dst_port = port1;
+ } else {
+ m_error = TYPE_MISMATCH;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ // Create the typed event to actually do the work
+ const PortType type = port1->port_info()->type();
+ if (type == AUDIO || type == CONTROL) {
+ m_typed_event = new TypedConnectionEvent<sample>(m_responder,
+ (OutputPort<sample>*)m_src_port, (InputPort<sample>*)m_dst_port);
+ } else if (type == MIDI) {
+ m_typed_event = new TypedConnectionEvent<MidiMessage>(m_responder,
+ (OutputPort<MidiMessage>*)m_src_port, (InputPort<MidiMessage>*)m_dst_port);
+ } else {
+ m_error = TYPE_MISMATCH;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ m_typed_event->pre_process();
+
+ QueuedEvent::pre_process();
+}
+
+
+void
+ConnectionEvent::execute(samplecount offset)
+{
+ QueuedEvent::execute(offset);
+
+ if (m_error == NO_ERROR)
+ m_typed_event->execute(offset);
+}
+
+
+void
+ConnectionEvent::post_process()
+{
+ if (m_error == NO_ERROR) {
+ m_typed_event->post_process();
+ } else {
+ // FIXME: better error messages
+ string msg = "Unable to make connection ";
+ msg.append(m_src_port_path + " -> " + m_dst_port_path);
+ m_responder->respond_error(msg);
+ }
+}
+
+
+
+//// TypedConnectionEvent ////
+
+
+template <typename T>
+TypedConnectionEvent<T>::TypedConnectionEvent(CountedPtr<Responder> responder, OutputPort<T>* src_port, InputPort<T>* dst_port)
+: QueuedEvent(responder),
+ m_src_port(src_port),
+ m_dst_port(dst_port),
+ m_patch(NULL),
+ m_process_order(NULL),
+ m_connection(NULL),
+ m_port_listnode(NULL),
+ m_succeeded(true)
+{
+ assert(src_port != NULL);
+ assert(dst_port != NULL);
+}
+
+template <typename T>
+TypedConnectionEvent<T>::~TypedConnectionEvent()
+{
+ // FIXME: haaaack, prevent a double delete
+ // this class is unusable by anything other than ConnectionEvent because of this
+ //m_responder = NULL;
+}
+
+
+template <typename T>
+void
+TypedConnectionEvent<T>::pre_process()
+{
+ Node* const src_node = m_src_port->parent_node();
+ Node* const dst_node = m_dst_port->parent_node();
+
+ m_patch = src_node->parent_patch();
+
+ if (src_node == NULL || dst_node == NULL) {
+ m_succeeded = false;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ if (src_node->parent() != m_patch || dst_node->parent() != m_patch) {
+ m_succeeded = false;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ m_connection = new ConnectionBase<T>(m_src_port, m_dst_port);
+ m_port_listnode = new ListNode<ConnectionBase<T>*>(m_connection);
+ m_patch_listnode = new ListNode<Connection*>(m_connection);
+
+ dst_node->providers()->push_back(new ListNode<Node*>(src_node));
+ src_node->dependants()->push_back(new ListNode<Node*>(dst_node));
+
+ if (m_patch->process())
+ m_process_order = m_patch->build_process_order();
+}
+
+
+template <typename T>
+void
+TypedConnectionEvent<T>::execute(samplecount offset)
+{
+ if (m_succeeded) {
+ // These must be inserted here, since they're actually used by the audio thread
+ m_dst_port->add_connection(m_port_listnode);
+ m_patch->add_connection(m_patch_listnode);
+ if (m_patch->process_order() != NULL)
+ om->maid()->push(m_patch->process_order());
+ m_patch->process_order(m_process_order);
+ }
+}
+
+
+template <typename T>
+void
+TypedConnectionEvent<T>::post_process()
+{
+ if (m_succeeded) {
+ assert(m_connection != NULL);
+
+ m_responder->respond_ok();
+
+ om->client_broadcaster()->send_connection(m_connection);
+ } else {
+ m_responder->respond_error("Unable to make connection.");
+ }
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/events/ConnectionEvent.h b/src/libs/engine/events/ConnectionEvent.h
new file mode 100644
index 00000000..8aaf2292
--- /dev/null
+++ b/src/libs/engine/events/ConnectionEvent.h
@@ -0,0 +1,107 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef CONNECTIONEVENT_H
+#define CONNECTIONEVENT_H
+
+#include <string>
+#include "QueuedEvent.h"
+#include "util/Path.h"
+#include "util/types.h"
+using std::string;
+
+template <typename T> class ListNode;
+template <typename T> class Array;
+
+namespace Om {
+
+class Patch;
+class Node;
+class Connection;
+class MidiMessage;
+class Port;
+template <typename T> class ConnectionBase;
+template <typename T> class InputPort;
+template <typename T> class OutputPort;
+template <typename T> class TypedConnectionEvent; // helper, defined below
+
+
+/** Make a Connection between two Ports.
+ *
+ * \ingroup engine
+ */
+class ConnectionEvent : public QueuedEvent
+{
+public:
+ ConnectionEvent(CountedPtr<Responder> responder, const string& src_port_path, const string& dst_port_path);
+ ~ConnectionEvent();
+
+ void pre_process();
+ void execute(samplecount offset);
+ void post_process();
+
+private:
+
+ enum ErrorType { NO_ERROR, PARENT_PATCH_DIFFERENT, PORT_NOT_FOUND, TYPE_MISMATCH };
+
+ Path m_src_port_path;
+ Path m_dst_port_path;
+
+ Patch* m_patch;
+ Port* m_src_port;
+ Port* m_dst_port;
+
+ QueuedEvent* m_typed_event;
+
+ ErrorType m_error;
+};
+
+
+/** Templated ConnectionEvent.
+ *
+ * Intended to be called from ConnectionEvent so callers (ie OSCReceiver)
+ * can use ConnectionEvent without knowing anything about types (which
+ * they can't, since all they have is Port paths).
+ */
+template <typename T>
+class TypedConnectionEvent : public QueuedEvent
+{
+public:
+ TypedConnectionEvent(CountedPtr<Responder> responder, OutputPort<T>* src_port, InputPort<T>* dst_port);
+ ~TypedConnectionEvent();
+
+ void pre_process();
+ void execute(samplecount offset);
+ void post_process();
+
+private:
+ OutputPort<T>* m_src_port;
+ InputPort<T>* m_dst_port;
+
+ Patch* m_patch;
+ Array<Node*>* m_process_order; ///< New process order for Patch
+ ConnectionBase<T>* m_connection;
+ ListNode<Connection*>* m_patch_listnode;
+ ListNode<ConnectionBase<T>*>* m_port_listnode;
+
+ bool m_succeeded;
+};
+
+
+
+} // namespace Om
+
+#endif // CONNECTIONEVENT_H
diff --git a/src/libs/engine/events/CreatePatchEvent.cpp b/src/libs/engine/events/CreatePatchEvent.cpp
new file mode 100644
index 00000000..9f0ae7f2
--- /dev/null
+++ b/src/libs/engine/events/CreatePatchEvent.cpp
@@ -0,0 +1,150 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "CreatePatchEvent.h"
+#include "Responder.h"
+#include "Patch.h"
+#include "Node.h"
+#include "Tree.h"
+#include "Plugin.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "Maid.h"
+#include "ClientBroadcaster.h"
+#include "AudioDriver.h"
+#include "util/Path.h"
+#include "ObjectStore.h"
+
+namespace Om {
+
+
+CreatePatchEvent::CreatePatchEvent(CountedPtr<Responder> responder, const string& path, int poly)
+: QueuedEvent(responder),
+ m_path(path),
+ m_patch(NULL),
+ m_parent(NULL),
+ m_process_order(NULL),
+ m_poly(poly),
+ m_error(NO_ERROR)
+{
+}
+
+
+void
+CreatePatchEvent::pre_process()
+{
+ if (om->object_store()->find(m_path) != NULL) {
+ m_error = OBJECT_EXISTS;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ if (m_poly < 1) {
+ m_error = INVALID_POLY;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ if (m_path != "/") {
+ m_parent = om->object_store()->find_patch(m_path.parent());
+ if (m_parent == NULL) {
+ m_error = PARENT_NOT_FOUND;
+ QueuedEvent::pre_process();
+ return;
+ }
+ }
+
+ size_t poly = 1;
+ if (m_parent != NULL && m_poly > 1 && m_poly == static_cast<int>(m_parent->internal_poly()))
+ poly = m_poly;
+
+ m_patch = new Patch(m_path.name(), poly, m_parent, om->audio_driver()->sample_rate(), om->audio_driver()->buffer_size(), m_poly);
+
+ if (m_parent != NULL) {
+ m_parent->add_node(new ListNode<Node*>(m_patch->as_node()));
+
+ if (m_parent->process())
+ m_process_order = m_parent->build_process_order();
+ }
+
+ m_patch->activate();
+
+ // Insert into ObjectStore
+ m_patch->add_to_store();
+
+ QueuedEvent::pre_process();
+}
+
+
+void
+CreatePatchEvent::execute(samplecount offset)
+{
+ QueuedEvent::execute(offset);
+
+ if (m_patch != NULL) {
+ if (m_parent == NULL) {
+ assert(m_path == "/");
+ assert(m_patch->parent_patch() == NULL);
+ om->audio_driver()->set_root_patch(m_patch);
+ } else {
+ assert(m_parent != NULL);
+ assert(m_path != "/");
+
+ m_patch->add_to_patch();
+
+ if (m_parent->process_order() != NULL)
+ om->maid()->push(m_parent->process_order());
+ m_parent->process_order(m_process_order);
+ }
+ }
+}
+
+
+void
+CreatePatchEvent::post_process()
+{
+ if (m_responder.get()) {
+ if (m_error == NO_ERROR) {
+
+ m_responder->respond_ok();
+
+ // Don't want to send nodes that have been added since prepare()
+ //om->client_broadcaster()->send_node_creation_messages(m_patch);
+
+ // Patches are always empty on creation, so this is fine
+ om->client_broadcaster()->send_patch(m_patch);
+
+ } else if (m_error == OBJECT_EXISTS) {
+ string msg = "Unable to create patch: ";
+ msg += m_path += " already exists.";
+ m_responder->respond_error(msg);
+ } else if (m_error == PARENT_NOT_FOUND) {
+ string msg = "Unable to create patch: Parent ";
+ msg += m_path.parent() += " not found.";
+ m_responder->respond_error(msg);
+ } else if (m_error == INVALID_POLY) {
+ string msg = "Unable to create patch ";
+ msg.append(m_path).append(": ").append("Invalid polyphony respondered.");
+ m_responder->respond_error(msg);
+ } else {
+ m_responder->respond_error("Unable to load patch.");
+ }
+ }
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/events/CreatePatchEvent.h b/src/libs/engine/events/CreatePatchEvent.h
new file mode 100644
index 00000000..507d03c7
--- /dev/null
+++ b/src/libs/engine/events/CreatePatchEvent.h
@@ -0,0 +1,64 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef CREATEPATCHEVENT_H
+#define CREATEPATCHEVENT_H
+
+#include "util/Path.h"
+#include "QueuedEvent.h"
+#include <string>
+using std::string;
+
+template<typename T> class Array;
+template<typename T> class TreeNode;
+
+namespace Om {
+
+class Patch;
+class Node;
+class Plugin;
+
+
+/** Creates a new Patch.
+ *
+ * \ingroup engine
+ */
+class CreatePatchEvent : public QueuedEvent
+{
+public:
+ CreatePatchEvent(CountedPtr<Responder> responder, const string& path, int poly);
+
+ void pre_process();
+ void execute(samplecount offset);
+ void post_process();
+
+private:
+ enum ErrorType { NO_ERROR, OBJECT_EXISTS, PARENT_NOT_FOUND, INVALID_POLY };
+
+ Path m_path;
+ Patch* m_patch;
+ Patch* m_parent;
+ Array<Node*>* m_process_order;
+ TreeNode<Node*>* m_patch_treenode;
+ int m_poly;
+ ErrorType m_error;
+};
+
+
+} // namespace Om
+
+
+#endif // CREATEPATCHEVENT_H
diff --git a/src/libs/engine/events/DSSIConfigureEvent.cpp b/src/libs/engine/events/DSSIConfigureEvent.cpp
new file mode 100644
index 00000000..2ade4671
--- /dev/null
+++ b/src/libs/engine/events/DSSIConfigureEvent.cpp
@@ -0,0 +1,73 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "DSSIConfigureEvent.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "Node.h"
+#include "ClientBroadcaster.h"
+#include "Plugin.h"
+#include "ObjectStore.h"
+
+namespace Om {
+
+
+DSSIConfigureEvent::DSSIConfigureEvent(CountedPtr<Responder> responder, const string& node_path, const string& key, const string& val)
+: QueuedEvent(responder),
+ m_node_path(node_path),
+ m_key(key),
+ m_val(val),
+ m_node(NULL)
+{
+}
+
+
+void
+DSSIConfigureEvent::pre_process()
+{
+ Node* node = om->object_store()->find_node(m_node_path);
+
+ if (node != NULL && node->plugin()->type() == Plugin::DSSI) {
+ m_node = (DSSIPlugin*)node;
+ m_node->configure(m_key, m_val);
+ }
+
+ QueuedEvent::pre_process();
+}
+
+
+void
+DSSIConfigureEvent::execute(samplecount offset)
+{
+ // Nothing.
+}
+
+
+void
+DSSIConfigureEvent::post_process()
+{
+ if (m_node == NULL) {
+ cerr << "Unable to find DSSI node " << m_node_path << endl;
+ } else {
+ string key = "dssi-configure--";
+ key += m_key;
+ om->client_broadcaster()->send_metadata_update(m_node_path, key, m_val);
+ }
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/events/DSSIConfigureEvent.h b/src/libs/engine/events/DSSIConfigureEvent.h
new file mode 100644
index 00000000..00b4a134
--- /dev/null
+++ b/src/libs/engine/events/DSSIConfigureEvent.h
@@ -0,0 +1,49 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef DSSICONFIGUREEVENT_H
+#define DSSICONFIGUREEVENT_H
+
+#include "QueuedEvent.h"
+#include "DSSIPlugin.h"
+
+namespace Om {
+
+
+/** Change of a 'configure' key/value pair for a DSSI plugin.
+ *
+ * \ingroup engine
+ */
+class DSSIConfigureEvent : public QueuedEvent
+{
+public:
+ DSSIConfigureEvent(CountedPtr<Responder> responder, const string& node_path, const string& key, const string& val);
+
+ void pre_process();
+ void execute(samplecount offset);
+ void post_process();
+
+private:
+ string m_node_path;
+ string m_key;
+ string m_val;
+ DSSIPlugin* m_node;
+};
+
+
+} // namespace Om
+
+#endif // DSSICONFIGUREEVENT_H
diff --git a/src/libs/engine/events/DSSIControlEvent.cpp b/src/libs/engine/events/DSSIControlEvent.cpp
new file mode 100644
index 00000000..ea3e70ac
--- /dev/null
+++ b/src/libs/engine/events/DSSIControlEvent.cpp
@@ -0,0 +1,68 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "DSSIControlEvent.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "Node.h"
+#include "Plugin.h"
+#include "ObjectStore.h"
+
+namespace Om {
+
+
+DSSIControlEvent::DSSIControlEvent(CountedPtr<Responder> responder, const string& node_path, int port_num, sample val)
+: QueuedEvent(responder),
+ m_node_path(node_path),
+ m_port_num(port_num),
+ m_val(val),
+ m_node(NULL)
+{
+}
+
+
+void
+DSSIControlEvent::pre_process()
+{
+ Node* node = om->object_store()->find_node(m_node_path);
+
+ if (node->plugin()->type() != Plugin::DSSI)
+ m_node = NULL;
+ else
+ m_node = (DSSIPlugin*)node;
+
+ QueuedEvent::pre_process();
+}
+
+
+void
+DSSIControlEvent::execute(samplecount offset)
+{
+ if (m_node != NULL)
+ m_node->set_control(m_port_num, m_val);
+}
+
+
+void
+DSSIControlEvent::post_process()
+{
+ if (m_node == NULL)
+ std::cerr << "Unable to find DSSI node " << m_node_path << std::endl;
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/events/DSSIControlEvent.h b/src/libs/engine/events/DSSIControlEvent.h
new file mode 100644
index 00000000..30a5279e
--- /dev/null
+++ b/src/libs/engine/events/DSSIControlEvent.h
@@ -0,0 +1,51 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef DSSICONTROLEVENT_H
+#define DSSICONTROLEVENT_H
+
+#include "QueuedEvent.h"
+#include "DSSIPlugin.h"
+
+namespace Om {
+
+
+/** A control change event for a DSSI plugin.
+ *
+ * This does essentially the same thing as a SetPortValueEvent.
+ *
+ * \ingroup engine
+ */
+class DSSIControlEvent : public QueuedEvent
+{
+public:
+ DSSIControlEvent(CountedPtr<Responder> responder, const string& node_path, int port_num, sample val);
+
+ void pre_process();
+ void execute(samplecount offset);
+ void post_process();
+
+private:
+ string m_node_path;
+ int m_port_num;
+ float m_val;
+ DSSIPlugin* m_node;
+};
+
+
+} // namespace Om
+
+#endif // DSSICONTROLEVENT_H
diff --git a/src/libs/engine/events/DSSIProgramEvent.cpp b/src/libs/engine/events/DSSIProgramEvent.cpp
new file mode 100644
index 00000000..eb68ef77
--- /dev/null
+++ b/src/libs/engine/events/DSSIProgramEvent.cpp
@@ -0,0 +1,77 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "DSSIProgramEvent.h"
+#include <cstdio>
+#include <iostream>
+#include "Om.h"
+#include "OmApp.h"
+#include "Node.h"
+#include "ClientBroadcaster.h"
+#include "Plugin.h"
+#include "ObjectStore.h"
+using std::cout; using std::cerr; using std::endl;
+
+
+namespace Om {
+
+
+DSSIProgramEvent::DSSIProgramEvent(CountedPtr<Responder> responder, const string& node_path, int bank, int program)
+: QueuedEvent(responder),
+ m_node_path(node_path),
+ m_bank(bank),
+ m_program(program),
+ m_node(NULL)
+{
+}
+
+
+void
+DSSIProgramEvent::pre_process()
+{
+ Node* node = om->object_store()->find_node(m_node_path);
+
+ if (node != NULL && node->plugin()->type() == Plugin::DSSI)
+ m_node = (DSSIPlugin*)node;
+
+ QueuedEvent::pre_process();
+}
+
+
+void
+DSSIProgramEvent::execute(samplecount offset)
+{
+ if (m_node != NULL)
+ m_node->program(m_bank, m_program);
+}
+
+
+void
+DSSIProgramEvent::post_process()
+{
+ if (m_node == NULL) {
+ cerr << "Unable to find DSSI node " << m_node_path << endl;
+ } else {
+ // sends program as metadata in the form bank/program
+ char* temp_buf = new char[16];
+ snprintf(temp_buf, 16, "%d/%d", m_bank, m_program);
+ om->client_broadcaster()->send_metadata_update(m_node_path, "dssi-program", temp_buf);
+ }
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/events/DSSIProgramEvent.h b/src/libs/engine/events/DSSIProgramEvent.h
new file mode 100644
index 00000000..152f3cb1
--- /dev/null
+++ b/src/libs/engine/events/DSSIProgramEvent.h
@@ -0,0 +1,49 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef DSSIPROGRAMEVENT_H
+#define DSSIPROGRAMEVENT_H
+
+#include "QueuedEvent.h"
+#include "DSSIPlugin.h"
+
+namespace Om {
+
+
+/** A program change for a DSSI plugin.
+ *
+ * \ingroup engine
+ */
+class DSSIProgramEvent : public QueuedEvent
+{
+public:
+ DSSIProgramEvent(CountedPtr<Responder> responder, const string& node_path, int bank, int program);
+
+ void pre_process();
+ void execute(samplecount offset);
+ void post_process();
+
+private:
+ string m_node_path;
+ int m_bank;
+ int m_program;
+ DSSIPlugin* m_node;
+};
+
+
+} // namespace Om
+
+#endif // DSSIPROGRAMEVENT_H
diff --git a/src/libs/engine/events/DSSIUpdateEvent.cpp b/src/libs/engine/events/DSSIUpdateEvent.cpp
new file mode 100644
index 00000000..5650dd63
--- /dev/null
+++ b/src/libs/engine/events/DSSIUpdateEvent.cpp
@@ -0,0 +1,80 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "DSSIUpdateEvent.h"
+#include <iostream>
+#include "Node.h"
+#include "ObjectStore.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "DSSIPlugin.h"
+#include "Plugin.h"
+
+using std::cerr; using std::endl;
+
+namespace Om {
+
+
+DSSIUpdateEvent::DSSIUpdateEvent(CountedPtr<Responder> responder, const string& path, const string& url)
+: QueuedEvent(responder),
+ m_path(path),
+ m_url(url),
+ m_node(NULL)
+{
+}
+
+
+void
+DSSIUpdateEvent::pre_process()
+{
+ Node* node = om->object_store()->find_node(m_path);
+
+ if (node == NULL || node->plugin()->type() != Plugin::DSSI) {
+ m_node = NULL;
+ QueuedEvent::pre_process();
+ return;
+ } else {
+ m_node = (DSSIPlugin*)node;
+ }
+
+ QueuedEvent::pre_process();
+}
+
+
+void
+DSSIUpdateEvent::execute(samplecount offset)
+{
+ if (m_node != NULL) {
+ m_node->set_ui_url(m_url);
+ }
+
+ QueuedEvent::execute(offset);
+}
+
+
+void
+DSSIUpdateEvent::post_process()
+{
+ cerr << "DSSI update event: " << m_url << endl;
+
+ if (m_node != NULL) {
+ m_node->send_update();
+ }
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/events/DSSIUpdateEvent.h b/src/libs/engine/events/DSSIUpdateEvent.h
new file mode 100644
index 00000000..cdd8851e
--- /dev/null
+++ b/src/libs/engine/events/DSSIUpdateEvent.h
@@ -0,0 +1,54 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef DSSIUPDATEEVENT_H
+#define DSSIUPDATEEVENT_H
+
+#include "QueuedEvent.h"
+#include <string>
+
+using std::string;
+
+namespace Om {
+
+class DSSIPlugin;
+
+
+/** A DSSI "update" responder for a DSSI plugin/node.
+ *
+ * This sends all information about the plugin to the UI (over OSC).
+ *
+ * \ingroup engine
+ */
+class DSSIUpdateEvent : public QueuedEvent
+{
+public:
+ DSSIUpdateEvent(CountedPtr<Responder> responder, const string& path, const string& url);
+
+ void pre_process();
+ void execute(samplecount offset);
+ void post_process();
+
+private:
+ string m_path;
+ string m_url;
+ DSSIPlugin* m_node;
+};
+
+
+} // namespace Om
+
+#endif // DSSIUPDATEEVENT_H
diff --git a/src/libs/engine/events/DeactivateEvent.cpp b/src/libs/engine/events/DeactivateEvent.cpp
new file mode 100644
index 00000000..48bff55a
--- /dev/null
+++ b/src/libs/engine/events/DeactivateEvent.cpp
@@ -0,0 +1,54 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "DeactivateEvent.h"
+#include "Responder.h"
+#include "Om.h"
+#include "OmApp.h"
+
+namespace Om {
+
+
+DeactivateEvent::DeactivateEvent(CountedPtr<Responder> responder)
+: QueuedEvent(responder)
+{
+}
+
+
+void
+DeactivateEvent::pre_process()
+{
+ QueuedEvent::pre_process();
+}
+
+
+void
+DeactivateEvent::execute(samplecount offset)
+{
+ QueuedEvent::execute(offset);
+}
+
+
+void
+DeactivateEvent::post_process()
+{
+ m_responder->respond_ok();
+ om->deactivate();
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/events/DeactivateEvent.h b/src/libs/engine/events/DeactivateEvent.h
new file mode 100644
index 00000000..8401f332
--- /dev/null
+++ b/src/libs/engine/events/DeactivateEvent.h
@@ -0,0 +1,42 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef DEACTIVATEEVENT_H
+#define DEACTIVATEEVENT_H
+
+#include "QueuedEvent.h"
+
+namespace Om {
+
+
+/** Deactivates the engine.
+ *
+ * \ingroup engine
+ */
+class DeactivateEvent : public QueuedEvent
+{
+public:
+ DeactivateEvent(CountedPtr<Responder> responder);
+
+ void pre_process();
+ void execute(samplecount offset);
+ void post_process();
+};
+
+
+} // namespace Om
+
+#endif // DEACTIVATEEVENT_H
diff --git a/src/libs/engine/events/DestroyEvent.cpp b/src/libs/engine/events/DestroyEvent.cpp
new file mode 100644
index 00000000..3988195a
--- /dev/null
+++ b/src/libs/engine/events/DestroyEvent.cpp
@@ -0,0 +1,168 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "DestroyEvent.h"
+#include "Responder.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "Patch.h"
+#include "Tree.h"
+#include "Node.h"
+#include "Plugin.h"
+#include "InternalNode.h"
+#include "DisconnectNodeEvent.h"
+#include "DisconnectPortEvent.h"
+#include "ClientBroadcaster.h"
+#include "Maid.h"
+#include "ObjectStore.h"
+#include "util/Path.h"
+#include "QueuedEventSource.h"
+#include "Port.h"
+
+namespace Om {
+
+
+DestroyEvent::DestroyEvent(CountedPtr<Responder> responder, const string& path, bool lock_mutex)
+: QueuedEvent(responder, true),
+ m_path(path),
+ m_node(NULL),
+ m_patch_listnode(NULL),
+ m_store_treenode(NULL),
+ m_process_order(NULL),
+ m_disconnect_event(NULL),
+ m_parent_disconnect_event(NULL)
+{
+}
+
+
+DestroyEvent::DestroyEvent(CountedPtr<Responder> responder, Node* node, bool lock_mutex)
+: QueuedEvent(responder, true),
+ m_path(node->path()),
+ m_node(node),
+ m_patch_listnode(NULL),
+ m_store_treenode(NULL),
+ m_process_order(NULL),
+ m_disconnect_event(NULL),
+ m_parent_disconnect_event(NULL)
+{
+}
+
+
+DestroyEvent::~DestroyEvent()
+{
+ delete m_disconnect_event;
+ delete m_parent_disconnect_event;
+}
+
+
+void
+DestroyEvent::pre_process()
+{
+ if (m_node == NULL) {
+ OmObject* const obj = om->object_store()->find_node(m_path);
+
+ if (obj != NULL && obj->as_node() != NULL)
+ m_node = obj->as_node();
+ }
+
+ if (m_node != NULL && m_path != "/") {
+ assert(m_node->parent_patch() != NULL);
+ m_patch_listnode = m_node->parent_patch()->remove_node(m_path.name());
+ if (m_patch_listnode != NULL) {
+ assert(m_patch_listnode->elem() == m_node);
+
+ m_node->remove_from_store();
+
+ if (m_node->providers()->size() != 0 || m_node->dependants()->size() != 0) {
+ m_disconnect_event = new DisconnectNodeEvent(m_node);
+ m_disconnect_event->pre_process();
+ }
+
+ // Create a recursive disconnect event for the parent port, if a bridge node
+ Port* parent_port = m_patch_listnode->elem()->as_port();
+ if (parent_port != NULL) { // Bridge node
+ m_parent_disconnect_event = new DisconnectPortEvent(parent_port);
+ m_parent_disconnect_event->pre_process();
+ }
+
+ if (m_node->parent_patch()->process()) {
+ m_process_order = m_node->parent_patch()->build_process_order();
+ // Remove node to be removed from the process order so it isn't executed by
+ // Patch::run and can safely be destroyed
+ //for (size_t i=0; i < m_process_order->size(); ++i)
+ // if (m_process_order->at(i) == m_node)
+ // m_process_order->at(i) = NULL; // ew, gap
+
+#ifdef DEBUG
+ // Be sure node is removed from process order, so it can be destroyed
+ for (size_t i=0; i < m_process_order->size(); ++i)
+ assert(m_process_order->at(i) != m_node);
+#endif
+ }
+ }
+ }
+
+ QueuedEvent::pre_process();
+}
+
+
+void
+DestroyEvent::execute(samplecount offset)
+{
+ QueuedEvent::execute(offset);
+
+ if (m_patch_listnode != NULL) {
+ m_node->remove_from_patch();
+
+ if (m_disconnect_event != NULL)
+ m_disconnect_event->execute(offset);
+ if (m_parent_disconnect_event != NULL)
+ m_parent_disconnect_event->execute(offset);
+
+ if (m_node->parent_patch()->process_order() != NULL)
+ om->maid()->push(m_node->parent_patch()->process_order());
+ m_node->parent_patch()->process_order(m_process_order);
+ }
+}
+
+
+void
+DestroyEvent::post_process()
+{
+ m_source->unblock();
+
+ if (m_node == NULL) {
+ if (m_path == "/")
+ m_responder->respond_error("You can not destroy the root patch (/)");
+ else
+ m_responder->respond_error("Could not find node to destroy");
+ } else if (m_patch_listnode != NULL) {
+ m_node->deactivate();
+ m_responder->respond_ok();
+ if (m_disconnect_event != NULL)
+ m_disconnect_event->post_process();
+ if (m_parent_disconnect_event != NULL)
+ m_parent_disconnect_event->post_process();
+ om->client_broadcaster()->send_destroyed(m_path);
+ om->maid()->push(m_patch_listnode);
+ om->maid()->push(m_node);
+ } else {
+ m_responder->respond_error("Unable to destroy object");
+ }
+}
+
+
+} // namespace Om
diff --git a/src/libs/engine/events/DestroyEvent.h b/src/libs/engine/events/DestroyEvent.h
new file mode 100644
index 00000000..fc579bf4
--- /dev/null
+++ b/src/libs/engine/events/DestroyEvent.h
@@ -0,0 +1,68 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef DESTROYEVENT_H
+#define DESTROYEVENT_H
+
+#include "util/Path.h"
+#include "QueuedEvent.h"
+#include <string>
+
+using std::string;
+
+template<typename T> class Array;
+template<typename T> class ListNode;
+template<typename T> class TreeNode;
+
+namespace Om {
+
+class OmObject;
+class Patch;
+class Node;
+class Plugin;
+class DisconnectNodeEvent;
+class DisconnectPortEvent;
+
+
+/** An event to remove and delete a Node.
+ *
+ * \ingroup engine
+ */
+class DestroyEvent : public QueuedEvent
+{
+public:
+ DestroyEvent(CountedPtr<Responder> responder, const string& path, bool lock_mutex = true);
+ DestroyEvent(CountedPtr<Responder> responder, Node* node, bool lock_mutex = true);
+ ~DestroyEvent();
+
+ void pre_process();
+ void execute(samplecount offset);
+ void post_process();
+
+private:
+ Path m_path;
+ Node* m_node;
+ ListNode<Node*>* m_patch_listnode;
+ TreeNode<OmObject*>* m_store_treenode;
+ Array<Node*>* m_process_order; // Patch's new process order
+ DisconnectNodeEvent* m_disconnect_event;
+ DisconnectPortEvent* m_parent_disconnect_event; // used for input/output nodes
+};
+
+
+} // namespace Om
+
+#endif // DESTROYEVENT_H
diff --git a/src/libs/engine/events/DisablePatchEvent.cpp b/src/libs/engine/events/DisablePatchEvent.cpp
new file mode 100644
index 00000000..a772e6e9
--- /dev/null
+++ b/src/libs/engine/events/DisablePatchEvent.cpp
@@ -0,0 +1,70 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "DisablePatchEvent.h"
+#include "Responder.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "Patch.h"
+#include "ClientBroadcaster.h"
+#include "util.h"
+#include "ObjectStore.h"
+#include "Port.h"
+
+namespace Om {
+
+
+DisablePatchEvent::DisablePatchEvent(CountedPtr<Responder> responder, const string& patch_path)
+: QueuedEvent(responder),
+ m_patch_path(patch_path),
+ m_patch(NULL)
+{
+}
+
+
+void
+DisablePatchEvent::pre_process()
+{
+ m_patch = om->object_store()->find_patch(m_patch_path);
+
+ QueuedEvent::pre_process();
+}
+
+
+void
+DisablePatchEvent::execute(samplecount offset)
+{
+ if (m_patch != NULL)
+ m_patch->process(false);
+
+ QueuedEvent::execute(offset);
+}
+
+
+void
+DisablePatchEvent::post_process()
+{
+ if (m_patch != NULL) {
+ m_responder->respond_ok();
+ om->client_broadcaster()->send_patch_disable(m_patch_path);
+ } else {
+ m_responder->respond_error(string("Patch ") + m_patch_path + " not found");
+ }
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/events/DisablePatchEvent.h b/src/libs/engine/events/DisablePatchEvent.h
new file mode 100644
index 00000000..f38f14af
--- /dev/null
+++ b/src/libs/engine/events/DisablePatchEvent.h
@@ -0,0 +1,52 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef DISABLEPATCHEVENT_H
+#define DISABLEPATCHEVENT_H
+
+#include <string>
+#include "QueuedEvent.h"
+
+using std::string;
+
+namespace Om {
+
+class Patch;
+
+
+/** Disables a Patch's DSP processing.
+ *
+ * \ingroup engine
+ */
+class DisablePatchEvent : public QueuedEvent
+{
+public:
+ DisablePatchEvent(CountedPtr<Responder> responder, const string& patch_path);
+
+ void pre_process();
+ void execute(samplecount offset);
+ void post_process();
+
+private:
+ string m_patch_path;
+ Patch* m_patch;
+};
+
+
+} // namespace Om
+
+
+#endif // DISABLEPATCHEVENT_H
diff --git a/src/libs/engine/events/DisconnectNodeEvent.cpp b/src/libs/engine/events/DisconnectNodeEvent.cpp
new file mode 100644
index 00000000..17367dd4
--- /dev/null
+++ b/src/libs/engine/events/DisconnectNodeEvent.cpp
@@ -0,0 +1,140 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "DisconnectNodeEvent.h"
+#include <iostream>
+#include "Responder.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "Maid.h"
+#include "List.h"
+#include "Node.h"
+#include "ConnectionBase.h"
+#include "DisconnectionEvent.h"
+#include "Port.h"
+#include "Array.h"
+#include "InputPort.h"
+#include "OutputPort.h"
+#include "Patch.h"
+#include "ClientBroadcaster.h"
+#include "util.h"
+#include "ObjectStore.h"
+#include "util/Path.h"
+
+using std::cerr; using std::endl;
+
+namespace Om {
+
+
+DisconnectNodeEvent::DisconnectNodeEvent(CountedPtr<Responder> responder, const string& node_path)
+: QueuedEvent(responder),
+ m_node_path(node_path),
+ m_patch(NULL),
+ m_node(NULL),
+ m_succeeded(true),
+ m_lookup(true)
+{
+}
+
+
+/** Internal version, disconnects parent port as well (in the case of InputNode, etc).
+ */
+DisconnectNodeEvent::DisconnectNodeEvent(Node* node)
+: QueuedEvent(),
+ m_node_path(""),
+ m_patch(node->parent_patch()),
+ m_node(node),
+ m_succeeded(true),
+ m_lookup(false)
+{
+}
+
+
+DisconnectNodeEvent::~DisconnectNodeEvent()
+{
+ for (List<DisconnectionEvent*>::iterator i = m_disconnection_events.begin(); i != m_disconnection_events.end(); ++i)
+ delete (*i);
+}
+
+
+void
+DisconnectNodeEvent::pre_process()
+{
+ typedef List<Connection*>::const_iterator ConnectionListIterator;
+
+ // cerr << "Preparing disconnection event...\n";
+
+ if (m_lookup) {
+ m_patch = om->object_store()->find_patch(m_node_path.parent());
+
+ if (m_patch == NULL) {
+ m_succeeded = false;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ m_node = om->object_store()->find_node(m_node_path);
+
+ if (m_node == NULL) {
+ m_succeeded = false;
+ QueuedEvent::pre_process();
+ return;
+ }
+ }
+
+ Connection* c = NULL;
+ for (ConnectionListIterator i = m_patch->connections().begin(); i != m_patch->connections().end(); ++i) {
+ c = (*i);
+ if ((c->src_port()->parent_node() == m_node || c->dst_port()->parent_node() == m_node) && !c->pending_disconnection()) {
+ DisconnectionEvent* ev = new DisconnectionEvent(CountedPtr<Responder>(new Responder()), c->src_port(), c->dst_port());
+ ev->pre_process();
+ m_disconnection_events.push_back(new ListNode<DisconnectionEvent*>(ev));
+ c->pending_disconnection(true);
+ }
+ }
+
+ m_succeeded = true;
+ QueuedEvent::pre_process();
+}
+
+
+void
+DisconnectNodeEvent::execute(samplecount offset)
+{
+ if (m_succeeded) {
+ for (List<DisconnectionEvent*>::iterator i = m_disconnection_events.begin(); i != m_disconnection_events.end(); ++i)
+ (*i)->execute(offset);
+ }
+
+ QueuedEvent::execute(offset);
+}
+
+
+void
+DisconnectNodeEvent::post_process()
+{
+ if (m_succeeded) {
+ m_responder->respond_ok();
+ for (List<DisconnectionEvent*>::iterator i = m_disconnection_events.begin(); i != m_disconnection_events.end(); ++i)
+ (*i)->post_process();
+ } else {
+ m_responder->respond_error("Unable to disconnect all ports.");
+ }
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/events/DisconnectNodeEvent.h b/src/libs/engine/events/DisconnectNodeEvent.h
new file mode 100644
index 00000000..a82fbaec
--- /dev/null
+++ b/src/libs/engine/events/DisconnectNodeEvent.h
@@ -0,0 +1,68 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef DISCONNECTNODEEVENT_H
+#define DISCONNECTNODEEVENT_H
+
+#include <string>
+#include "util/Path.h"
+#include "QueuedEvent.h"
+#include "List.h"
+using std::string;
+
+namespace Om {
+
+class DisconnectionEvent;
+class Patch;
+class Node;
+class Connection;
+template <typename T> class ConnectionBase;
+class Port;
+template <typename T> class InputPort;
+template <typename T> class OutputPort;
+
+
+/** An event to disconnect all connections to a Node.
+ *
+ * \ingroup engine
+ */
+class DisconnectNodeEvent : public QueuedEvent
+{
+public:
+ DisconnectNodeEvent(CountedPtr<Responder> responder, const string& node_path);
+ DisconnectNodeEvent(Node* node);
+ ~DisconnectNodeEvent();
+
+ void pre_process();
+ void execute(samplecount offset);
+ void post_process();
+
+private:
+ Path m_node_path;
+ Patch* m_patch;
+ Node* m_node;
+ List<DisconnectionEvent*> m_disconnection_events;
+
+ bool m_succeeded;
+ bool m_lookup;
+ bool m_disconnect_parent;
+};
+
+
+} // namespace Om
+
+
+#endif // DISCONNECTNODEEVENT_H
diff --git a/src/libs/engine/events/DisconnectPortEvent.cpp b/src/libs/engine/events/DisconnectPortEvent.cpp
new file mode 100644
index 00000000..a4c213d5
--- /dev/null
+++ b/src/libs/engine/events/DisconnectPortEvent.cpp
@@ -0,0 +1,145 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "DisconnectPortEvent.h"
+#include <iostream>
+#include "Responder.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "Maid.h"
+#include "List.h"
+#include "Node.h"
+#include "Connection.h"
+#include "DisconnectionEvent.h"
+#include "Port.h"
+#include "Array.h"
+#include "InputPort.h"
+#include "OutputPort.h"
+#include "Patch.h"
+#include "ClientBroadcaster.h"
+#include "util.h"
+#include "ObjectStore.h"
+#include "util/Path.h"
+
+using std::cerr; using std::endl;
+
+namespace Om {
+
+
+DisconnectPortEvent::DisconnectPortEvent(CountedPtr<Responder> responder, const string& port_path)
+: QueuedEvent(responder),
+ m_port_path(port_path),
+ m_patch(NULL),
+ m_port(NULL),
+ m_process_order(NULL),
+ m_succeeded(true),
+ m_lookup(true)
+{
+}
+
+
+DisconnectPortEvent::DisconnectPortEvent(Port* port)
+: QueuedEvent(),
+ m_port_path(""),
+ m_patch((port->parent_node() == NULL) ? NULL : port->parent_node()->parent_patch()),
+ m_port(port),
+ m_process_order(NULL),
+ m_succeeded(true),
+ m_lookup(false)
+{
+ //cerr << "DisconnectPortEvent()\n";
+}
+
+
+DisconnectPortEvent::~DisconnectPortEvent()
+{
+ for (List<DisconnectionEvent*>::iterator i = m_disconnection_events.begin(); i != m_disconnection_events.end(); ++i)
+ delete (*i);
+}
+
+
+void
+DisconnectPortEvent::pre_process()
+{
+ // cerr << "Preparing disconnection event...\n";
+
+ if (m_lookup) {
+ m_patch = om->object_store()->find_patch(m_port_path.parent().parent());
+
+ if (m_patch == NULL) {
+ m_succeeded = false;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ m_port = om->object_store()->find_port(m_port_path);
+
+ if (m_port == NULL) {
+ m_succeeded = false;
+ QueuedEvent::pre_process();
+ return;
+ }
+ }
+
+ if (m_patch == NULL) {
+ m_succeeded = false;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ Connection* c = NULL;
+ for (List<Connection*>::const_iterator i = m_patch->connections().begin(); i != m_patch->connections().end(); ++i) {
+ c = (*i);
+ if ((c->src_port() == m_port || c->dst_port() == m_port) && !c->pending_disconnection()) {
+ DisconnectionEvent* ev = new DisconnectionEvent(CountedPtr<Responder>(new Responder()), c->src_port(), c->dst_port());
+ ev->pre_process();
+ m_disconnection_events.push_back(new ListNode<DisconnectionEvent*>(ev));
+ c->pending_disconnection(true);
+ }
+ }
+
+ m_succeeded = true;
+ QueuedEvent::pre_process();
+}
+
+
+void
+DisconnectPortEvent::execute(samplecount offset)
+{
+ if (m_succeeded) {
+ for (List<DisconnectionEvent*>::iterator i = m_disconnection_events.begin(); i != m_disconnection_events.end(); ++i)
+ (*i)->execute(offset);
+ }
+
+ QueuedEvent::execute(offset);
+}
+
+
+void
+DisconnectPortEvent::post_process()
+{
+ if (m_succeeded) {
+ m_responder->respond_ok();
+ for (List<DisconnectionEvent*>::iterator i = m_disconnection_events.begin(); i != m_disconnection_events.end(); ++i)
+ (*i)->post_process();
+ } else {
+ m_responder->respond_error("Unable to disconnect port.");
+ }
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/events/DisconnectPortEvent.h b/src/libs/engine/events/DisconnectPortEvent.h
new file mode 100644
index 00000000..e8de4120
--- /dev/null
+++ b/src/libs/engine/events/DisconnectPortEvent.h
@@ -0,0 +1,70 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef DISCONNECTPORTEVENT_H
+#define DISCONNECTPORTEVENT_H
+
+#include <string>
+#include "util/Path.h"
+#include "QueuedEvent.h"
+#include "List.h"
+
+template <typename T> class Array;
+
+namespace Om {
+
+
+class Patch;
+class Node;
+class Connection;
+class Port;
+class DisconnectionEvent;
+
+using std::string;
+
+
+/** An event to disconnect all connections to a Port.
+ *
+ * \ingroup engine
+ */
+class DisconnectPortEvent : public QueuedEvent
+{
+public:
+ DisconnectPortEvent(CountedPtr<Responder> responder, const string& port_path);
+ DisconnectPortEvent(Port* port);
+ ~DisconnectPortEvent();
+
+ void pre_process();
+ void execute(samplecount offset);
+ void post_process();
+
+private:
+ Path m_port_path;
+ Patch* m_patch;
+ Port* m_port;
+ List<DisconnectionEvent*> m_disconnection_events;
+
+ Array<Node*>* m_process_order; // Patch's new process order
+
+ bool m_succeeded;
+ bool m_lookup;
+};
+
+
+} // namespace Om
+
+
+#endif // DISCONNECTPORTEVENT_H
diff --git a/src/libs/engine/events/DisconnectionEvent.cpp b/src/libs/engine/events/DisconnectionEvent.cpp
new file mode 100644
index 00000000..e7d06eab
--- /dev/null
+++ b/src/libs/engine/events/DisconnectionEvent.cpp
@@ -0,0 +1,295 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "DisconnectionEvent.h"
+#include <string>
+#include "Responder.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "ConnectionBase.h"
+#include "InputPort.h"
+#include "OutputPort.h"
+#include "Patch.h"
+#include "ClientBroadcaster.h"
+#include "Port.h"
+#include "PortInfo.h"
+#include "Maid.h"
+#include "ObjectStore.h"
+#include "util/Path.h"
+
+using std::string;
+namespace Om {
+
+
+//// DisconnectionEvent ////
+
+
+DisconnectionEvent::DisconnectionEvent(CountedPtr<Responder> responder, const string& src_port_path, const string& dst_port_path)
+: QueuedEvent(responder),
+ m_src_port_path(src_port_path),
+ m_dst_port_path(dst_port_path),
+ m_patch(NULL),
+ m_src_port(NULL),
+ m_dst_port(NULL),
+ m_lookup(true),
+ m_typed_event(NULL),
+ m_error(NO_ERROR)
+{
+}
+
+
+DisconnectionEvent::DisconnectionEvent(CountedPtr<Responder> responder, Port* const src_port, Port* const dst_port)
+: QueuedEvent(responder),
+ m_src_port_path(src_port->path()),
+ m_dst_port_path(dst_port->path()),
+ m_patch(src_port->parent_node()->parent_patch()),
+ m_src_port(src_port),
+ m_dst_port(dst_port),
+ m_lookup(false),
+ m_typed_event(NULL),
+ m_error(NO_ERROR)
+{
+ assert(src_port->port_info()->is_output());
+ assert(dst_port->port_info()->is_input());
+ assert(src_port->port_info()->type() == dst_port->port_info()->type());
+ assert(src_port->parent_node()->parent_patch()
+ == dst_port->parent_node()->parent_patch());
+}
+
+DisconnectionEvent::~DisconnectionEvent()
+{
+ delete m_typed_event;
+}
+
+
+void
+DisconnectionEvent::pre_process()
+{
+ if (m_lookup) {
+ if (m_src_port_path.parent().parent() != m_dst_port_path.parent().parent()) {
+ m_error = PARENT_PATCH_DIFFERENT;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ /*m_patch = om->object_store()->find_patch(m_src_port_path.parent().parent());
+
+ if (m_patch == NULL) {
+ m_error = PORT_NOT_FOUND;
+ QueuedEvent::pre_process();
+ return;
+ }*/
+
+ Port* port1 = om->object_store()->find_port(m_src_port_path);
+ Port* port2 = om->object_store()->find_port(m_dst_port_path);
+
+ if (port1 == NULL || port2 == NULL) {
+ m_error = PORT_NOT_FOUND;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ if (port1->port_info()->type() != port2->port_info()->type()) {
+ m_error = TYPE_MISMATCH;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ if (port1->port_info()->is_output() && port2->port_info()->is_input()) {
+ m_src_port = port1;
+ m_dst_port = port2;
+ } else if (port2->port_info()->is_output() && port1->port_info()->is_input()) {
+ m_src_port = port2;
+ m_dst_port = port1;
+ } else {
+ m_error = TYPE_MISMATCH;
+ QueuedEvent::pre_process();
+ return;
+ }
+ }
+
+ // Create the typed event to actually do the work
+ const PortType type = m_src_port->port_info()->type();
+ if (type == AUDIO || type == CONTROL) {
+ m_typed_event = new TypedDisconnectionEvent<sample>(m_responder,
+ (OutputPort<sample>*)m_src_port, (InputPort<sample>*)m_dst_port);
+ } else if (type == MIDI) {
+ m_typed_event = new TypedDisconnectionEvent<MidiMessage>(m_responder,
+ (OutputPort<MidiMessage>*)m_src_port, (InputPort<MidiMessage>*)m_dst_port);
+ } else {
+ m_error = TYPE_MISMATCH;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ m_typed_event->pre_process();
+
+ QueuedEvent::pre_process();
+}
+
+
+void
+DisconnectionEvent::execute(samplecount offset)
+{
+ QueuedEvent::execute(offset);
+
+ if (m_error == NO_ERROR)
+ m_typed_event->execute(offset);
+}
+
+
+void
+DisconnectionEvent::post_process()
+{
+ if (m_error == NO_ERROR) {
+ m_typed_event->post_process();
+ } else {
+ // FIXME: better error messages
+ string msg = "Unable to make connection ";
+ msg.append(m_src_port_path + " -> " + m_dst_port_path);
+ m_responder->respond_error(msg);
+ }
+}
+
+
+
+//// TypedDisconnectionEvent ////
+
+
+template <typename T>
+TypedDisconnectionEvent<T>::TypedDisconnectionEvent(CountedPtr<Responder> responder, OutputPort<T>* src_port, InputPort<T>* dst_port)
+: QueuedEvent(responder),
+ m_src_port(src_port),
+ m_dst_port(dst_port),
+ m_patch(NULL),
+ m_process_order(NULL),
+ m_succeeded(true)
+{
+ assert(src_port != NULL);
+ assert(dst_port != NULL);
+}
+
+template <typename T>
+TypedDisconnectionEvent<T>::~TypedDisconnectionEvent()
+{
+ // FIXME: haaaack, prevent a double delete
+ // this class is unusable by anything other than DisconnectionEvent because of this
+ //m_responder = NULL;
+}
+
+
+template <typename T>
+void
+TypedDisconnectionEvent<T>::pre_process()
+{
+ if (!m_dst_port->is_connected_to(m_src_port)) {
+ m_succeeded = false;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ Node* const src_node = m_src_port->parent_node();
+ Node* const dst_node = m_dst_port->parent_node();
+
+ m_patch = src_node->parent_patch();
+
+ if (src_node == NULL || dst_node == NULL) {
+ m_succeeded = false;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ if (src_node->parent() != m_patch || dst_node->parent() != m_patch) {
+ m_succeeded = false;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ bool removed = false;
+
+ for (List<Node*>::iterator i = dst_node->providers()->begin(); i != dst_node->providers()->end(); ++i)
+ if ((*i) == src_node) {
+ delete dst_node->providers()->remove(i);
+ removed = true;
+ break;
+ }
+ assert(removed);
+ removed = false;
+
+ for (List<Node*>::iterator i = src_node->dependants()->begin(); i != src_node->dependants()->end(); ++i)
+ if ((*i) == dst_node) {
+ delete src_node->dependants()->remove(i);
+ removed = true;
+ break;
+ }
+ assert(removed);
+
+ if (m_patch->process())
+ m_process_order = m_patch->build_process_order();
+
+ m_succeeded = true;
+ QueuedEvent::pre_process();
+}
+
+
+template <typename T>
+void
+TypedDisconnectionEvent<T>::execute(samplecount offset)
+{
+ if (m_succeeded) {
+
+ ListNode<ConnectionBase<T>*>* const port_connection
+ = m_dst_port->remove_connection(m_src_port);
+
+ if (port_connection != NULL) {
+ ListNode<Connection*>* const patch_connection
+ = m_patch->remove_connection(m_src_port, m_dst_port);
+
+ assert((Connection*)port_connection->elem() == patch_connection->elem());
+
+ // Clean up both the list node and the connection itself...
+ om->maid()->push(port_connection);
+ om->maid()->push(patch_connection);
+ om->maid()->push(port_connection->elem());
+
+ if (m_patch->process_order() != NULL)
+ om->maid()->push(m_patch->process_order());
+ m_patch->process_order(m_process_order);
+ } else {
+ m_succeeded = false; // Ports weren't connected
+ }
+ }
+ QueuedEvent::execute(offset);
+}
+
+
+template <typename T>
+void
+TypedDisconnectionEvent<T>::post_process()
+{
+ if (m_succeeded) {
+
+ m_responder->respond_ok();
+
+ om->client_broadcaster()->send_disconnection(m_src_port->path(), m_dst_port->path());
+ } else {
+ m_responder->respond_error("Unable to disconnect ports.");
+ }
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/events/DisconnectionEvent.h b/src/libs/engine/events/DisconnectionEvent.h
new file mode 100644
index 00000000..bbb70f3c
--- /dev/null
+++ b/src/libs/engine/events/DisconnectionEvent.h
@@ -0,0 +1,106 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef DISCONNECTIONEVENT_H
+#define DISCONNECTIONEVENT_H
+
+#include <string>
+#include "util/Path.h"
+#include "QueuedEvent.h"
+#include "util/types.h"
+using std::string;
+
+template <typename T> class ListNode;
+template <typename T> class Array;
+
+namespace Om {
+
+class Patch;
+class Node;
+class Connection;
+class MidiMessage;
+class Port;
+template <typename T> class ConnectionBase;
+template <typename T> class InputPort;
+template <typename T> class OutputPort;
+template <typename T> class TypedDisconnectionEvent; // helper, defined below
+
+
+/** Make a Connection between two Ports.
+ *
+ * \ingroup engine
+ */
+class DisconnectionEvent : public QueuedEvent
+{
+public:
+ DisconnectionEvent(CountedPtr<Responder> responder, const string& src_port_path, const string& dst_port_path);
+ DisconnectionEvent(CountedPtr<Responder> responder, Port* const src_port, Port* const dst_port);
+ ~DisconnectionEvent();
+
+ void pre_process();
+ void execute(samplecount offset);
+ void post_process();
+
+private:
+
+ enum ErrorType { NO_ERROR, PARENT_PATCH_DIFFERENT, PORT_NOT_FOUND, TYPE_MISMATCH };
+
+ Path m_src_port_path;
+ Path m_dst_port_path;
+
+ Patch* m_patch;
+ Port* m_src_port;
+ Port* m_dst_port;
+
+ bool m_lookup;
+ QueuedEvent* m_typed_event;
+
+ ErrorType m_error;
+};
+
+
+/** Templated DisconnectionEvent.
+ *
+ * Intended to be called from DisconnectionEvent so callers (ie OSCReceiver)
+ * can use DisconnectionEvent without knowing anything about types (which
+ * they can't, since all they have is Port paths).
+ */
+template <typename T>
+class TypedDisconnectionEvent : public QueuedEvent
+{
+public:
+ TypedDisconnectionEvent(CountedPtr<Responder> responder, OutputPort<T>* src_port, InputPort<T>* dst_port);
+ ~TypedDisconnectionEvent();
+
+ void pre_process();
+ void execute(samplecount offset);
+ void post_process();
+
+private:
+ OutputPort<T>* m_src_port;
+ InputPort<T>* m_dst_port;
+
+ Patch* m_patch;
+ Array<Node*>* m_process_order; ///< New process order for Patch
+
+ bool m_succeeded;
+};
+
+
+
+} // namespace Om
+
+#endif // DISCONNECTIONEVENT_H
diff --git a/src/libs/engine/events/EnablePatchEvent.cpp b/src/libs/engine/events/EnablePatchEvent.cpp
new file mode 100644
index 00000000..6af3b844
--- /dev/null
+++ b/src/libs/engine/events/EnablePatchEvent.cpp
@@ -0,0 +1,82 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "EnablePatchEvent.h"
+#include "Responder.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "Patch.h"
+#include "util.h"
+#include "ClientBroadcaster.h"
+#include "ObjectStore.h"
+
+namespace Om {
+
+
+EnablePatchEvent::EnablePatchEvent(CountedPtr<Responder> responder, const string& patch_path)
+: QueuedEvent(responder),
+ m_patch_path(patch_path),
+ m_patch(NULL),
+ m_process_order(NULL)
+{
+}
+
+
+void
+EnablePatchEvent::pre_process()
+{
+ m_patch = om->object_store()->find_patch(m_patch_path);
+
+ if (m_patch != NULL) {
+ /* Any event that requires a new process order will set the patch's
+ * process order to NULL if it is executed when the patch is not
+ * active. So, if the PO is NULL, calculate it here */
+ if (m_patch->process_order() == NULL)
+ m_process_order = m_patch->build_process_order();
+ }
+
+ QueuedEvent::pre_process();
+}
+
+
+void
+EnablePatchEvent::execute(samplecount offset)
+{
+ if (m_patch != NULL) {
+ m_patch->process(true);
+
+ if (m_patch->process_order() == NULL)
+ m_patch->process_order(m_process_order);
+ }
+
+ QueuedEvent::execute(offset);
+}
+
+
+void
+EnablePatchEvent::post_process()
+{
+ if (m_patch != NULL) {
+ m_responder->respond_ok();
+ om->client_broadcaster()->send_patch_enable(m_patch_path);
+ } else {
+ m_responder->respond_error(string("Patch ") + m_patch_path + " not found");
+ }
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/events/EnablePatchEvent.h b/src/libs/engine/events/EnablePatchEvent.h
new file mode 100644
index 00000000..f3d22e69
--- /dev/null
+++ b/src/libs/engine/events/EnablePatchEvent.h
@@ -0,0 +1,56 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef ENABLEPATCHEVENT_H
+#define ENABLEPATCHEVENT_H
+
+#include <string>
+#include "QueuedEvent.h"
+
+using std::string;
+
+template <typename T> class Array;
+
+namespace Om {
+
+class Patch;
+class Node;
+
+
+/** Enables a patch's DSP processing.
+ *
+ * \ingroup engine
+ */
+class EnablePatchEvent : public QueuedEvent
+{
+public:
+ EnablePatchEvent(CountedPtr<Responder> responder, const string& patch_path);
+
+ void pre_process();
+ void execute(samplecount offset);
+ void post_process();
+
+private:
+ string m_patch_path;
+ Patch* m_patch;
+ Array<Node*>* m_process_order; // Patch's new process order
+};
+
+
+} // namespace Om
+
+
+#endif // ENABLEPATCHEVENT_H
diff --git a/src/libs/engine/events/LashRestoreDoneEvent.h b/src/libs/engine/events/LashRestoreDoneEvent.h
new file mode 100644
index 00000000..dfc5b120
--- /dev/null
+++ b/src/libs/engine/events/LashRestoreDoneEvent.h
@@ -0,0 +1,54 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LASHRESTOREDONEEVENT_H
+#define LASHRESTOREDONEEVENT_H
+
+#include "QueuedEvent.h"
+#include "LashDriver.h"
+#include "util/types.h"
+
+#include <iostream>
+using std::cerr;
+
+namespace Om {
+
+class Port;
+
+
+/** Notification from a client that the LASH session is finished restoring.
+ *
+ * This is a bit of a hack, but needed to defer notifying LASH of our
+ * existance until all ports are created.
+ *
+ * \ingroup engine
+ */
+class LashRestoreDoneEvent : public QueuedEvent
+{
+public:
+ LashRestoreDoneEvent(CountedPtr<Responder> responder) : QueuedEvent(responder) {}
+
+ void post_process()
+ {
+ m_responder->respond_ok();
+ lash_driver->restore_finished();
+ }
+};
+
+
+} // namespace Om
+
+#endif // LASHRESTOREDONEEVENT_H
diff --git a/src/libs/engine/events/LoadPluginsEvent.cpp b/src/libs/engine/events/LoadPluginsEvent.cpp
new file mode 100644
index 00000000..656e9010
--- /dev/null
+++ b/src/libs/engine/events/LoadPluginsEvent.cpp
@@ -0,0 +1,44 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "LoadPluginsEvent.h"
+#include "Responder.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "NodeFactory.h"
+
+namespace Om {
+
+
+LoadPluginsEvent::LoadPluginsEvent(CountedPtr<Responder> responder)
+: QueuedEvent(responder)
+{
+}
+
+
+void
+LoadPluginsEvent::post_process()
+{
+ // Why is this done here and not in pre_process()???
+ om->node_factory()->load_plugins();
+ m_responder->respond_ok();
+
+ //cerr << "Load plugins post finished\n";
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/events/LoadPluginsEvent.h b/src/libs/engine/events/LoadPluginsEvent.h
new file mode 100644
index 00000000..b69dbb38
--- /dev/null
+++ b/src/libs/engine/events/LoadPluginsEvent.h
@@ -0,0 +1,40 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LOADPLUGINSEVENT_H
+#define LOADPLUGINSEVENT_H
+
+#include "QueuedEvent.h"
+
+namespace Om {
+
+
+/** Loads all plugins into the internal plugin database (in NodeFactory).
+ *
+ * \ingroup engine
+ */
+class LoadPluginsEvent : public QueuedEvent
+{
+public:
+ LoadPluginsEvent(CountedPtr<Responder> responder);
+
+ void post_process();
+};
+
+
+} // namespace Om
+
+#endif // LOADPLUGINSEVENT_H
diff --git a/src/libs/engine/events/Makefile.am b/src/libs/engine/events/Makefile.am
new file mode 100644
index 00000000..5b29e12b
--- /dev/null
+++ b/src/libs/engine/events/Makefile.am
@@ -0,0 +1,67 @@
+MAINTAINERCLEANFILES = Makefile.in
+
+EXTRA_DIST = \
+ events/RegisterClientEvent.h \
+ events/RegisterClientEvent.cpp \
+ events/UnregisterClientEvent.h \
+ events/UnregisterClientEvent.cpp \
+ events/PingQueuedEvent.h \
+ events/ActivateEvent.h \
+ events/ActivateEvent.cpp \
+ events/DeactivateEvent.h \
+ events/DeactivateEvent.cpp \
+ events/SetPortValueEvent.h \
+ events/SetPortValueEvent.cpp \
+ events/SetPortValueQueuedEvent.h \
+ events/SetPortValueQueuedEvent.cpp \
+ events/NoteOnEvent.h \
+ events/NoteOnEvent.cpp \
+ events/NoteOffEvent.h \
+ events/NoteOffEvent.cpp \
+ events/AllNotesOffEvent.h \
+ events/AllNotesOffEvent.cpp \
+ events/ConnectionEvent.h \
+ events/ConnectionEvent.cpp \
+ events/DisconnectionEvent.h \
+ events/DisconnectionEvent.cpp \
+ events/DisconnectNodeEvent.h \
+ events/DisconnectNodeEvent.cpp \
+ events/DisconnectPortEvent.h \
+ events/DisconnectPortEvent.cpp \
+ events/DestroyEvent.h \
+ events/DestroyEvent.cpp \
+ events/AddNodeEvent.h \
+ events/AddNodeEvent.cpp \
+ events/SetMetadataEvent.h \
+ events/SetMetadataEvent.cpp \
+ events/RequestMetadataEvent.h \
+ events/RequestMetadataEvent.cpp \
+ events/RequestPortValueEvent.h \
+ events/RequestPortValueEvent.cpp \
+ events/RequestAllObjectsEvent.h \
+ events/RequestAllObjectsEvent.cpp \
+ events/RequestPluginsEvent.h \
+ events/RequestPluginsEvent.cpp \
+ events/CreatePatchEvent.h \
+ events/CreatePatchEvent.cpp \
+ events/LoadPluginsEvent.h \
+ events/LoadPluginsEvent.cpp \
+ events/EnablePatchEvent.h \
+ events/EnablePatchEvent.cpp \
+ events/DisablePatchEvent.h \
+ events/DisablePatchEvent.cpp \
+ events/ClearPatchEvent.h \
+ events/ClearPatchEvent.cpp \
+ events/RenameEvent.h \
+ events/RenameEvent.cpp \
+ events/MidiLearnEvent.h \
+ events/MidiLearnEvent.cpp \
+ DSSIConfigureEvent.cpp \
+ DSSIConfigureEvent.h \
+ DSSIControlEvent.cpp \
+ DSSIControlEvent.h \
+ DSSIProgramEvent.cpp \
+ DSSIProgramEvent.h \
+ DSSIUpdateEvent.cpp \
+ DSSIUpdateEvent.h
+
diff --git a/src/libs/engine/events/MidiLearnEvent.cpp b/src/libs/engine/events/MidiLearnEvent.cpp
new file mode 100644
index 00000000..63ad82fc
--- /dev/null
+++ b/src/libs/engine/events/MidiLearnEvent.cpp
@@ -0,0 +1,88 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "MidiLearnEvent.h"
+#include "Responder.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "ObjectStore.h"
+#include "Node.h"
+#include "MidiControlNode.h"
+#include "ClientBroadcaster.h"
+
+namespace Om {
+
+
+// MidiLearnResponseEvent
+
+void
+MidiLearnResponseEvent::post_process()
+{
+ om->client_broadcaster()->send_control_change(m_port_path, m_value);
+}
+
+
+
+// MidiLearnEvent
+
+MidiLearnEvent::MidiLearnEvent(CountedPtr<Responder> responder, const string& node_path)
+: QueuedEvent(responder),
+ m_node_path(node_path),
+ m_node(NULL),
+ m_response_event(NULL)
+{
+}
+
+
+void
+MidiLearnEvent::pre_process()
+{
+ m_node = om->object_store()->find_node(m_node_path);
+ m_response_event = new MidiLearnResponseEvent(m_node_path + "/Controller Number");
+
+ QueuedEvent::pre_process();
+}
+
+
+void
+MidiLearnEvent::execute(samplecount offset)
+{
+ QueuedEvent::execute(offset);
+
+ // FIXME: this isn't very good at all.
+ if (m_node != NULL && m_node->plugin()->type() == Plugin::Internal
+ && m_node->plugin()->plug_label() == "midi_control_in") {
+ ((MidiControlNode*)m_node)->learn(m_response_event);
+ }
+}
+
+
+void
+MidiLearnEvent::post_process()
+{
+ if (m_node != NULL) {
+ m_responder->respond_ok();
+ } else {
+ string msg = "Did not find node '";
+ msg.append(m_node_path).append("' for MIDI learn.");
+ m_responder->respond_error(msg);
+ }
+}
+
+
+} // namespace Om
+
+
diff --git a/src/libs/engine/events/MidiLearnEvent.h b/src/libs/engine/events/MidiLearnEvent.h
new file mode 100644
index 00000000..793e675a
--- /dev/null
+++ b/src/libs/engine/events/MidiLearnEvent.h
@@ -0,0 +1,84 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef MIDILEARNEVENT_H
+#define MIDILEARNEVENT_H
+
+#include "QueuedEvent.h"
+#include "MidiControlNode.h"
+#include "util/types.h"
+#include <string>
+using std::string;
+
+namespace Om {
+
+class Node;
+class ControlChangeEvent;
+
+
+/** Response event for a MIDI learn.
+ *
+ * This is a trivial event that sends a control change in it's post_process
+ * method (used by MidiLearnEvent to notify clients when the learn happens)
+ */
+class MidiLearnResponseEvent : public Event
+{
+public:
+ MidiLearnResponseEvent(const string& port_path)
+ : Event(CountedPtr<Responder>(NULL)),
+ m_port_path(port_path),
+ m_value(0.0f)
+ {}
+
+ void set_value(sample val) { m_value = val; }
+ void post_process();
+
+private:
+ string m_port_path;
+ sample m_value;
+};
+
+
+
+/** A MIDI learn event.
+ *
+ * This creates a MidiLearnResponseEvent and passes it to the learning node, which
+ * will push it to the post-processor once the learn happens in order to reply
+ * to the client with the new port (learned controller) value.
+ *
+ * \ingroup engine
+ */
+class MidiLearnEvent : public QueuedEvent
+{
+public:
+ MidiLearnEvent(CountedPtr<Responder> responder, const string& node_path);
+
+ void pre_process();
+ void execute(samplecount offset);
+ void post_process();
+
+private:
+ string m_node_path;
+ Node* m_node;
+
+ /// Event to respond with when learned
+ MidiLearnResponseEvent* m_response_event;
+};
+
+
+} // namespace Om
+
+#endif // MIDILEARNEVENT_H
diff --git a/src/libs/engine/events/NoteOffEvent.cpp b/src/libs/engine/events/NoteOffEvent.cpp
new file mode 100644
index 00000000..fde2e520
--- /dev/null
+++ b/src/libs/engine/events/NoteOffEvent.cpp
@@ -0,0 +1,78 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "NoteOffEvent.h"
+#include "Responder.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "ObjectStore.h"
+#include "Node.h"
+#include "MidiNoteNode.h"
+#include "MidiTriggerNode.h"
+
+namespace Om {
+
+
+/** Note off with patch explicitly passed - triggered by MIDI.
+ */
+NoteOffEvent::NoteOffEvent(CountedPtr<Responder> responder, Node* node, uchar note_num)
+: Event(responder),
+ m_node(node),
+ m_note_num(note_num)
+{
+}
+
+
+/** Note off event with lookup - triggered by OSC.
+ */
+NoteOffEvent::NoteOffEvent(CountedPtr<Responder> responder, const string& node_path, uchar note_num)
+: Event(responder),
+ m_node(NULL),
+ m_node_path(node_path),
+ m_note_num(note_num)
+{
+}
+
+
+void
+NoteOffEvent::execute(samplecount offset)
+{
+ if (m_node == NULL && m_node_path != "")
+ m_node = om->object_store()->find_node(m_node_path);
+
+ // FIXME: this isn't very good at all.
+ if (m_node != NULL && m_node->plugin()->type() == Plugin::Internal) {
+ if (m_node->plugin()->plug_label() == "note_in")
+ ((MidiNoteNode*)m_node)->note_off(m_note_num, offset);
+ else if (m_node->plugin()->plug_label() == "trigger_in")
+ ((MidiTriggerNode*)m_node)->note_off(m_note_num, offset);
+ }
+}
+
+
+void
+NoteOffEvent::post_process()
+{
+ if (m_node != NULL)
+ m_responder->respond_ok();
+ else
+ m_responder->respond_error("Did not find node for note_off");
+}
+
+
+} // namespace Om
+
+
diff --git a/src/libs/engine/events/NoteOffEvent.h b/src/libs/engine/events/NoteOffEvent.h
new file mode 100644
index 00000000..edd0b373
--- /dev/null
+++ b/src/libs/engine/events/NoteOffEvent.h
@@ -0,0 +1,52 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef NOTEOFFEVENT_H
+#define NOTEOFFEVENT_H
+
+#include "Event.h"
+#include "util/types.h"
+#include <string>
+using std::string;
+
+namespace Om {
+
+class Node;
+
+
+/** A note off event.
+ *
+ * \ingroup engine
+ */
+class NoteOffEvent : public Event
+{
+public:
+ NoteOffEvent(CountedPtr<Responder> responder, Node* node, uchar note_num);
+ NoteOffEvent(CountedPtr<Responder> responder, const string& node_path, uchar note_num);
+
+ void execute(samplecount offset);
+ void post_process();
+
+private:
+ Node* m_node;
+ string m_node_path;
+ uchar m_note_num;
+};
+
+
+} // namespace Om
+
+#endif // NOTEOFFEVENT_H
diff --git a/src/libs/engine/events/NoteOnEvent.cpp b/src/libs/engine/events/NoteOnEvent.cpp
new file mode 100644
index 00000000..9688af79
--- /dev/null
+++ b/src/libs/engine/events/NoteOnEvent.cpp
@@ -0,0 +1,89 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "NoteOnEvent.h"
+#include "Responder.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "ObjectStore.h"
+#include "Node.h"
+#include "MidiNoteNode.h"
+#include "MidiTriggerNode.h"
+#include "Plugin.h"
+
+namespace Om {
+
+
+/** Note on with Patch explicitly passed.
+ *
+ * Used to be triggered by MIDI. Not used anymore.
+ */
+NoteOnEvent::NoteOnEvent(CountedPtr<Responder> responder, Node* patch, uchar note_num, uchar velocity)
+: Event(responder),
+ m_node(patch),
+ m_note_num(note_num),
+ m_velocity(velocity),
+ m_is_osc_triggered(false)
+{
+}
+
+
+/** Note on with Node lookup.
+ *
+ * Triggered by OSC.
+ */
+NoteOnEvent::NoteOnEvent(CountedPtr<Responder> responder, const string& node_path, uchar note_num, uchar velocity)
+: Event(responder),
+ m_node(NULL),
+ m_node_path(node_path),
+ m_note_num(note_num),
+ m_velocity(velocity),
+ m_is_osc_triggered(true)
+{
+}
+
+
+void
+NoteOnEvent::execute(samplecount offset)
+{
+ // Lookup if neccessary
+ if (m_is_osc_triggered)
+ m_node = om->object_store()->find_node(m_node_path);
+
+ // FIXME: this isn't very good at all.
+ if (m_node != NULL && m_node->plugin()->type() == Plugin::Internal) {
+ if (m_node->plugin()->plug_label() == "note_in")
+ ((MidiNoteNode*)m_node)->note_on(m_note_num, m_velocity, offset);
+ else if (m_node->plugin()->plug_label() == "trigger_in")
+ ((MidiTriggerNode*)m_node)->note_on(m_note_num, m_velocity, offset);
+ }
+}
+
+
+void
+NoteOnEvent::post_process()
+{
+ if (m_is_osc_triggered) {
+ if (m_node != NULL)
+ m_responder->respond_ok();
+ else
+ m_responder->respond_error("Did not find node for note_on");
+ }
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/events/NoteOnEvent.h b/src/libs/engine/events/NoteOnEvent.h
new file mode 100644
index 00000000..1e09e450
--- /dev/null
+++ b/src/libs/engine/events/NoteOnEvent.h
@@ -0,0 +1,54 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef NOTEONEVENT_H
+#define NOTEONEVENT_H
+
+#include "Event.h"
+#include "util/types.h"
+#include <string>
+using std::string;
+
+namespace Om {
+
+class Node;
+
+
+/** A note on event.
+ *
+ * \ingroup engine
+ */
+class NoteOnEvent : public Event
+{
+public:
+ NoteOnEvent(CountedPtr<Responder> responder, Node* patch, uchar note_num, uchar velocity);
+ NoteOnEvent(CountedPtr<Responder> responder, const string& node_path, uchar note_num, uchar velocity);
+
+ void execute(samplecount offset);
+ void post_process();
+
+private:
+ Node* m_node;
+ string m_node_path;
+ uchar m_note_num;
+ uchar m_velocity;
+ bool m_is_osc_triggered;
+};
+
+
+} // namespace Om
+
+#endif // NOTEONEVENT_H
diff --git a/src/libs/engine/events/PingQueuedEvent.h b/src/libs/engine/events/PingQueuedEvent.h
new file mode 100644
index 00000000..cfba7058
--- /dev/null
+++ b/src/libs/engine/events/PingQueuedEvent.h
@@ -0,0 +1,44 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PINGQUEUEDEVENT_H
+#define PINGQUEUEDEVENT_H
+
+#include "QueuedEvent.h"
+#include "util/types.h"
+#include "Responder.h"
+
+namespace Om {
+
+class Port;
+
+
+/** A "blocking" ping that travels through the event queue before responding.
+ *
+ * \ingroup engine
+ */
+class PingQueuedEvent : public QueuedEvent
+{
+public:
+ PingQueuedEvent(CountedPtr<Responder> responder) : QueuedEvent(responder) {}
+
+ void post_process() { m_responder->respond_ok(); }
+};
+
+
+} // namespace Om
+
+#endif // PINGQUEUEDEVENT_H
diff --git a/src/libs/engine/events/RegisterClientEvent.cpp b/src/libs/engine/events/RegisterClientEvent.cpp
new file mode 100644
index 00000000..ca7dd1f6
--- /dev/null
+++ b/src/libs/engine/events/RegisterClientEvent.cpp
@@ -0,0 +1,53 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "RegisterClientEvent.h"
+#include "Responder.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "ClientBroadcaster.h"
+
+namespace Om {
+
+
+RegisterClientEvent::RegisterClientEvent(CountedPtr<Responder> responder,
+ ClientKey key,
+ CountedPtr<ClientInterface> client)
+: QueuedEvent(responder)
+, _key(key)
+, _client(client)
+{
+}
+
+
+void
+RegisterClientEvent::pre_process()
+{
+ om->client_broadcaster()->register_client(_key, _client);
+
+ QueuedEvent::pre_process();
+}
+
+
+void
+RegisterClientEvent::post_process()
+{
+ m_responder->respond_ok();
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/events/RegisterClientEvent.h b/src/libs/engine/events/RegisterClientEvent.h
new file mode 100644
index 00000000..f4b6b60f
--- /dev/null
+++ b/src/libs/engine/events/RegisterClientEvent.h
@@ -0,0 +1,53 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef REGISTERCLIENTEVENT_H
+#define REGISTERCLIENTEVENT_H
+
+#include "QueuedEvent.h"
+#include "interface/ClientKey.h"
+#include "interface/ClientInterface.h"
+#include <string>
+using std::string;
+using Om::Shared::ClientInterface;
+using Om::Shared::ClientKey;
+
+namespace Om {
+
+
+/** Registers a new client with the OSC system, so it can receive updates.
+ *
+ * \ingroup engine
+ */
+class RegisterClientEvent : public QueuedEvent
+{
+public:
+ RegisterClientEvent(CountedPtr<Responder> responder,
+ ClientKey key,
+ CountedPtr<ClientInterface> client);
+
+ void pre_process();
+ void post_process();
+
+private:
+ ClientKey _key;
+ CountedPtr<ClientInterface> _client;
+};
+
+
+} // namespace Om
+
+#endif // REGISTERCLIENTEVENT_H
diff --git a/src/libs/engine/events/RenameEvent.cpp b/src/libs/engine/events/RenameEvent.cpp
new file mode 100644
index 00000000..e2e98dd0
--- /dev/null
+++ b/src/libs/engine/events/RenameEvent.cpp
@@ -0,0 +1,123 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "RenameEvent.h"
+#include "Responder.h"
+#include "Patch.h"
+#include "Node.h"
+#include "Tree.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "ClientBroadcaster.h"
+#include "util/Path.h"
+#include "ObjectStore.h"
+
+namespace Om {
+
+
+RenameEvent::RenameEvent(CountedPtr<Responder> responder, const string& path, const string& name)
+: QueuedEvent(responder),
+ m_old_path(path),
+ m_name(name),
+ m_new_path(m_old_path.parent().base_path() + name),
+ m_parent_patch(NULL),
+ m_store_treenode(NULL),
+ m_error(NO_ERROR)
+{
+ /*
+ if (m_old_path.parent() == "/")
+ m_new_path = string("/") + m_name;
+ else
+ m_new_path = m_old_path.parent() +"/"+ m_name;*/
+}
+
+
+RenameEvent::~RenameEvent()
+{
+}
+
+
+void
+RenameEvent::pre_process()
+{
+ if (m_name.find("/") != string::npos) {
+ m_error = INVALID_NAME;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ if (om->object_store()->find(m_new_path)) {
+ m_error = OBJECT_EXISTS;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ OmObject* obj = om->object_store()->find(m_old_path);
+
+ if (obj == NULL) {
+ m_error = OBJECT_NOT_FOUND;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ // Renaming only works for Nodes and Patches (which are Nodes)
+ if (obj->as_node() == NULL) {
+ m_error = OBJECT_NOT_RENAMABLE;
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ if (obj != NULL) {
+ obj->set_path(m_new_path);
+ assert(obj->path() == m_new_path);
+ }
+
+ QueuedEvent::pre_process();
+}
+
+
+void
+RenameEvent::execute(samplecount offset)
+{
+ //cout << "Executing rename event...";
+ QueuedEvent::execute(offset);
+}
+
+
+void
+RenameEvent::post_process()
+{
+ string msg = "Unable to rename object - ";
+
+ if (m_error == NO_ERROR) {
+ m_responder->respond_ok();
+ om->client_broadcaster()->send_rename(m_old_path, m_new_path);
+ } else {
+ if (m_error == OBJECT_EXISTS)
+ msg.append("Object already exists at ").append(m_new_path);
+ else if (m_error == OBJECT_NOT_FOUND)
+ msg.append("Could not find object ").append(m_old_path);
+ else if (m_error == OBJECT_NOT_RENAMABLE)
+ msg.append(m_old_path).append(" is not renamable");
+ else if (m_error == INVALID_NAME)
+ msg.append(m_name).append(" is not a valid name");
+
+ m_responder->respond_error(msg);
+ }
+}
+
+
+} // namespace Om
diff --git a/src/libs/engine/events/RenameEvent.h b/src/libs/engine/events/RenameEvent.h
new file mode 100644
index 00000000..c1c9fe91
--- /dev/null
+++ b/src/libs/engine/events/RenameEvent.h
@@ -0,0 +1,66 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef RENAMEEVENT_H
+#define RENAMEEVENT_H
+
+#include "QueuedEvent.h"
+#include "util/Path.h"
+#include <string>
+using std::string;
+
+template<typename T> class TreeNode;
+template<typename T> class ListNode;
+
+namespace Om {
+
+class OmObject;
+class Patch;
+class Node;
+class Plugin;
+class DisconnectNodeEvent;
+class DisconnectPortEvent;
+
+
+/** An event to change the name of an OmObject.
+ *
+ * \ingroup engine
+ */
+class RenameEvent : public QueuedEvent
+{
+public:
+ RenameEvent(CountedPtr<Responder> responder, const string& path, const string& name);
+ ~RenameEvent();
+
+ void pre_process();
+ void execute(samplecount offset);
+ void post_process();
+
+private:
+ enum ErrorType { NO_ERROR, OBJECT_NOT_FOUND, OBJECT_EXISTS, OBJECT_NOT_RENAMABLE, INVALID_NAME };
+
+ Path m_old_path;
+ string m_name;
+ Path m_new_path;
+ Patch* m_parent_patch;
+ TreeNode<OmObject*>* m_store_treenode;
+ ErrorType m_error;
+};
+
+
+} // namespace Om
+
+#endif // RENAMEEVENT_H
diff --git a/src/libs/engine/events/RequestAllObjectsEvent.cpp b/src/libs/engine/events/RequestAllObjectsEvent.cpp
new file mode 100644
index 00000000..ef40a0de
--- /dev/null
+++ b/src/libs/engine/events/RequestAllObjectsEvent.cpp
@@ -0,0 +1,55 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "RequestAllObjectsEvent.h"
+#include "Responder.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "ObjectSender.h"
+
+namespace Om {
+
+
+RequestAllObjectsEvent::RequestAllObjectsEvent(CountedPtr<Responder> responder)
+: QueuedEvent(responder),
+ m_client(CountedPtr<ClientInterface>(NULL))
+{
+}
+
+
+void
+RequestAllObjectsEvent::pre_process()
+{
+ m_client = m_responder->find_client();
+
+ QueuedEvent::pre_process();
+}
+
+
+void
+RequestAllObjectsEvent::post_process()
+{
+ if (m_client) {
+ m_responder->respond_ok();
+ ObjectSender::send_all(m_client.get());
+ } else {
+ m_responder->respond_error("Invalid URL");
+ }
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/events/RequestAllObjectsEvent.h b/src/libs/engine/events/RequestAllObjectsEvent.h
new file mode 100644
index 00000000..df63a577
--- /dev/null
+++ b/src/libs/engine/events/RequestAllObjectsEvent.h
@@ -0,0 +1,50 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef REQUESTALLOBJECTSEVENT_H
+#define REQUESTALLOBJECTSEVENT_H
+
+#include <string>
+#include "QueuedEvent.h"
+using std::string;
+
+namespace Om {
+
+namespace Shared {
+ class ClientInterface;
+} using Shared::ClientInterface;
+
+
+/** A request from a client to send notification of all objects (ie refresh).
+ *
+ * \ingroup engine
+ */
+class RequestAllObjectsEvent : public QueuedEvent
+{
+public:
+ RequestAllObjectsEvent(CountedPtr<Responder> responder);
+
+ void pre_process();
+ void post_process();
+
+private:
+ CountedPtr<ClientInterface> m_client;
+};
+
+
+} // namespace Om
+
+#endif // REQUESTALLOBJECTSEVENT_H
diff --git a/src/libs/engine/events/RequestMetadataEvent.cpp b/src/libs/engine/events/RequestMetadataEvent.cpp
new file mode 100644
index 00000000..10e1007c
--- /dev/null
+++ b/src/libs/engine/events/RequestMetadataEvent.cpp
@@ -0,0 +1,80 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "RequestMetadataEvent.h"
+#include <string>
+#include "Responder.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "OmObject.h"
+#include "ObjectStore.h"
+#include "interface/ClientInterface.h"
+#include "ClientBroadcaster.h"
+using std::string;
+
+namespace Om {
+
+
+RequestMetadataEvent::RequestMetadataEvent(CountedPtr<Responder> responder, const string& node_path, const string& key)
+: QueuedEvent(responder),
+ m_path(node_path),
+ m_key(key),
+ m_value(""),
+ m_object(NULL),
+ m_client(CountedPtr<ClientInterface>(NULL))
+{
+}
+
+
+void
+RequestMetadataEvent::pre_process()
+{
+ m_client = m_responder->find_client();
+
+ if (m_client) {
+ m_object = om->object_store()->find(m_path);
+ if (m_object == NULL) {
+ QueuedEvent::pre_process();
+ return;
+ }
+ }
+
+ m_value = m_object->get_metadata(m_key);
+
+ QueuedEvent::pre_process();
+}
+
+
+void
+RequestMetadataEvent::post_process()
+{
+ if (m_client) {
+ if (m_value == "") {
+ string msg = "Unable to find object ";
+ msg += m_path;
+ m_responder->respond_error(msg);
+ } else {
+ m_responder->respond_ok();
+ m_client->metadata_update(m_path, m_key, m_value);
+ }
+ } else {
+ m_responder->respond_error("Unknown client");
+ }
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/events/RequestMetadataEvent.h b/src/libs/engine/events/RequestMetadataEvent.h
new file mode 100644
index 00000000..e4243eb3
--- /dev/null
+++ b/src/libs/engine/events/RequestMetadataEvent.h
@@ -0,0 +1,56 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef REQUESTMETADATAEVENT_H
+#define REQUESTMETADATAEVENT_H
+
+#include <string>
+#include "QueuedEvent.h"
+
+using std::string;
+
+namespace Om {
+
+class OmObject;
+namespace Shared {
+ class ClientInterface;
+} using Shared::ClientInterface;
+
+
+/** A request from a client for a piece of metadata.
+ *
+ * \ingroup engine
+ */
+class RequestMetadataEvent : public QueuedEvent
+{
+public:
+ RequestMetadataEvent(CountedPtr<Responder> responder, const string& path, const string& key);
+
+ void pre_process();
+ void post_process();
+
+private:
+ string m_path;
+ string m_key;
+ string m_value;
+ OmObject* m_object;
+ CountedPtr<ClientInterface> m_client;
+};
+
+
+} // namespace Om
+
+#endif // REQUESTMETADATAEVENT_H
diff --git a/src/libs/engine/events/RequestPluginsEvent.cpp b/src/libs/engine/events/RequestPluginsEvent.cpp
new file mode 100644
index 00000000..89dbefd5
--- /dev/null
+++ b/src/libs/engine/events/RequestPluginsEvent.cpp
@@ -0,0 +1,55 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "RequestPluginsEvent.h"
+#include "Responder.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "ClientBroadcaster.h"
+
+namespace Om {
+
+
+RequestPluginsEvent::RequestPluginsEvent(CountedPtr<Responder> responder)
+: QueuedEvent(responder),
+ m_client(CountedPtr<ClientInterface>(NULL))
+{
+}
+
+
+void
+RequestPluginsEvent::pre_process()
+{
+ m_client = m_responder->find_client();
+
+ QueuedEvent::pre_process();
+}
+
+
+void
+RequestPluginsEvent::post_process()
+{
+ if (m_client) {
+ om->client_broadcaster()->send_plugins_to(m_client.get());
+ m_responder->respond_ok();
+ } else {
+ m_responder->respond_error("Invalid URL");
+ }
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/events/RequestPluginsEvent.h b/src/libs/engine/events/RequestPluginsEvent.h
new file mode 100644
index 00000000..83447fb3
--- /dev/null
+++ b/src/libs/engine/events/RequestPluginsEvent.h
@@ -0,0 +1,51 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef REQUESTPLUGINSEVENT_H
+#define REQUESTPLUGINSEVENT_H
+
+#include <string>
+#include "QueuedEvent.h"
+using std::string;
+
+namespace Om {
+
+class Responder;
+namespace Shared {
+ class ClientInterface;
+} using Shared::ClientInterface;
+
+
+/** A request from a client to send notification of all objects (ie refresh).
+ *
+ * \ingroup engine
+ */
+class RequestPluginsEvent : public QueuedEvent
+{
+public:
+ RequestPluginsEvent(CountedPtr<Responder> responder);
+
+ void pre_process();
+ void post_process();
+
+private:
+ CountedPtr<ClientInterface> m_client;
+};
+
+
+} // namespace Om
+
+#endif // REQUESTPLUGINSEVENT_H
diff --git a/src/libs/engine/events/RequestPortValueEvent.cpp b/src/libs/engine/events/RequestPortValueEvent.cpp
new file mode 100644
index 00000000..e4d3985e
--- /dev/null
+++ b/src/libs/engine/events/RequestPortValueEvent.cpp
@@ -0,0 +1,81 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "RequestPortValueEvent.h"
+#include <string>
+#include "Responder.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "interface/ClientInterface.h"
+#include "PortBase.h"
+#include "PortInfo.h"
+#include "ObjectStore.h"
+#include "ClientBroadcaster.h"
+
+using std::string;
+
+namespace Om {
+
+
+RequestPortValueEvent::RequestPortValueEvent(CountedPtr<Responder> responder, const string& port_path)
+: QueuedEvent(responder),
+ m_port_path(port_path),
+ m_port(NULL),
+ m_value(0.0),
+ m_client(CountedPtr<ClientInterface>(NULL))
+{
+}
+
+
+void
+RequestPortValueEvent::pre_process()
+{
+ m_client = m_responder->find_client();
+ m_port = om->object_store()->find_port(m_port_path);
+
+ QueuedEvent::pre_process();
+}
+
+
+void
+RequestPortValueEvent::execute(samplecount offset)
+{
+ if (m_port != NULL && m_port->port_info()->is_audio() || m_port->port_info()->is_control())
+ m_value = ((PortBase<sample>*)m_port)->buffer(0)->value_at(offset);
+ else
+ m_port = NULL; // triggers error response
+
+ QueuedEvent::execute(offset);
+}
+
+
+void
+RequestPortValueEvent::post_process()
+{
+ string msg;
+ if (m_port) {
+ m_responder->respond_error("Unable to find port for get_value responder.");
+ } else if (m_client) {
+ m_responder->respond_ok();
+ m_client->control_change(m_port_path, m_value);
+ } else {
+ m_responder->respond_error("Invalid URL");
+ }
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/events/RequestPortValueEvent.h b/src/libs/engine/events/RequestPortValueEvent.h
new file mode 100644
index 00000000..d970ee9c
--- /dev/null
+++ b/src/libs/engine/events/RequestPortValueEvent.h
@@ -0,0 +1,56 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef REQUESTPORTVALUEEVENT_H
+#define REQUESTPORTVALUEEVENT_H
+
+#include <string>
+#include "QueuedEvent.h"
+#include "util/types.h"
+
+using std::string;
+
+namespace Om {
+
+class Port;
+namespace Shared { class ClientInterface; }
+using Shared::ClientInterface;
+
+
+/** A request from a client to send the value of a port.
+ *
+ * \ingroup engine
+ */
+class RequestPortValueEvent : public QueuedEvent
+{
+public:
+ RequestPortValueEvent(CountedPtr<Responder> responder, const string& port_path);
+
+ void pre_process();
+ void execute(samplecount offset);
+ void post_process();
+
+private:
+ string m_port_path;
+ Port* m_port;
+ sample m_value;
+ CountedPtr<ClientInterface> m_client;
+};
+
+
+} // namespace Om
+
+#endif // REQUESTPORTVALUEEVENT_H
diff --git a/src/libs/engine/events/SetMetadataEvent.cpp b/src/libs/engine/events/SetMetadataEvent.cpp
new file mode 100644
index 00000000..614f7ca9
--- /dev/null
+++ b/src/libs/engine/events/SetMetadataEvent.cpp
@@ -0,0 +1,79 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "SetMetadataEvent.h"
+#include <string>
+#include "Responder.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "ClientBroadcaster.h"
+#include "OmObject.h"
+#include "ObjectStore.h"
+
+using std::string;
+
+namespace Om {
+
+
+SetMetadataEvent::SetMetadataEvent(CountedPtr<Responder> responder, const string& path, const string& key, const string& value)
+: QueuedEvent(responder),
+ m_path(path),
+ m_key(key),
+ m_value(value),
+ m_object(NULL)
+{
+}
+
+
+void
+SetMetadataEvent::pre_process()
+{
+ m_object = om->object_store()->find(m_path);
+ if (m_object == NULL) {
+ QueuedEvent::pre_process();
+ return;
+ }
+
+ m_object->set_metadata(m_key, m_value);
+
+ QueuedEvent::pre_process();
+}
+
+
+void
+SetMetadataEvent::execute(samplecount offset)
+{
+ // Do nothing
+
+ QueuedEvent::execute(offset);
+}
+
+
+void
+SetMetadataEvent::post_process()
+{
+ if (m_object == NULL) {
+ string msg = "Unable to find object ";
+ msg += m_path;
+ m_responder->respond_error(msg);
+ } else {
+ m_responder->respond_ok();
+ om->client_broadcaster()->send_metadata_update(m_path, m_key, m_value);
+ }
+}
+
+
+} // namespace Om
diff --git a/src/libs/engine/events/SetMetadataEvent.h b/src/libs/engine/events/SetMetadataEvent.h
new file mode 100644
index 00000000..ef471033
--- /dev/null
+++ b/src/libs/engine/events/SetMetadataEvent.h
@@ -0,0 +1,53 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef SETMETADATAEVENT_H
+#define SETMETADATAEVENT_H
+
+#include <string>
+#include "QueuedEvent.h"
+
+using std::string;
+
+namespace Om {
+
+class OmObject;
+
+
+/** An event to set a piece of metadata for an OmObject.
+ *
+ * \ingroup engine
+ */
+class SetMetadataEvent : public QueuedEvent
+{
+public:
+ SetMetadataEvent(CountedPtr<Responder> responder, const string& path, const string& key, const string& value);
+
+ void pre_process();
+ void execute(samplecount offset);
+ void post_process();
+
+private:
+ string m_path;
+ string m_key;
+ string m_value;
+ OmObject* m_object;
+};
+
+
+} // namespace Om
+
+#endif // SETMETADATAEVENT_H
diff --git a/src/libs/engine/events/SetPortValueEvent.cpp b/src/libs/engine/events/SetPortValueEvent.cpp
new file mode 100644
index 00000000..964cd9ca
--- /dev/null
+++ b/src/libs/engine/events/SetPortValueEvent.cpp
@@ -0,0 +1,104 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "SetPortValueEvent.h"
+#include "Responder.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "PortBase.h"
+#include "PortInfo.h"
+#include "ClientBroadcaster.h"
+#include "Node.h"
+#include "ObjectStore.h"
+
+namespace Om {
+
+
+/** Voice-specific control setting
+ */
+SetPortValueEvent::SetPortValueEvent(CountedPtr<Responder> responder, size_t voice_num, const string& port_path, sample val)
+: Event(responder),
+ m_voice_num(voice_num),
+ m_port_path(port_path),
+ m_val(val),
+ m_port(NULL),
+ m_error(NO_ERROR)
+{
+}
+
+
+SetPortValueEvent::SetPortValueEvent(CountedPtr<Responder> responder, const string& port_path, sample val)
+: Event(responder),
+ m_voice_num(-1),
+ m_port_path(port_path),
+ m_val(val),
+ m_port(NULL),
+ m_error(NO_ERROR)
+{
+}
+
+
+void
+SetPortValueEvent::execute(samplecount offset)
+{
+ if (m_port == NULL)
+ m_port = om->object_store()->find_port(m_port_path);
+
+ if (m_port == NULL) {
+ m_error = PORT_NOT_FOUND;
+ } else if (!m_port->port_info()->is_audio() && !m_port->port_info()->is_control()) {
+ m_error = TYPE_MISMATCH;
+ } else {
+ if (m_voice_num == -1)
+ ((PortBase<sample>*)m_port)->set_value(m_val, offset);
+ else
+ ((PortBase<sample>*)m_port)->set_value(m_voice_num, m_val, offset);
+ //((PortBase<sample>*)m_port)->buffer(m_voice_num)->set(m_val, offset); // FIXME: check range
+ }
+}
+
+
+void
+SetPortValueEvent::post_process()
+{
+ if (m_error == NO_ERROR) {
+ assert(m_port != NULL);
+
+ m_responder->respond_ok();
+ om->client_broadcaster()->send_control_change(m_port_path, m_val);
+
+ // Send patch port control change, if this is a bridge port
+ Port* parent_port = m_port->parent_node()->as_port();
+ if (parent_port != NULL) {
+ assert(parent_port->port_info()->is_control() || parent_port->port_info()->is_audio());
+ om->client_broadcaster()->send_control_change(parent_port->path(), m_val);
+ }
+
+ } else if (m_error == PORT_NOT_FOUND) {
+ string msg = "Unable to find port ";
+ msg.append(m_port_path).append(" for set_port_value");
+ m_responder->respond_error(msg);
+
+ } else if (m_error == TYPE_MISMATCH) {
+ string msg = "Attempt to set ";
+ msg.append(m_port_path).append(" to incompatible type");
+ m_responder->respond_error(msg);
+ }
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/events/SetPortValueEvent.h b/src/libs/engine/events/SetPortValueEvent.h
new file mode 100644
index 00000000..0a9614b9
--- /dev/null
+++ b/src/libs/engine/events/SetPortValueEvent.h
@@ -0,0 +1,56 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef SETPORTVALUEEVENT_H
+#define SETPORTVALUEEVENT_H
+
+#include <string>
+#include "Event.h"
+#include "util/types.h"
+using std::string;
+
+namespace Om {
+
+class Port;
+
+
+/** An event to change the value of a port.
+ *
+ * \ingroup engine
+ */
+class SetPortValueEvent : public Event
+{
+public:
+ SetPortValueEvent(CountedPtr<Responder> responder, const string& port_path, sample val);
+ SetPortValueEvent(CountedPtr<Responder> responder, size_t voice_num, const string& port_path, sample val);
+
+ void execute(samplecount offset);
+ void post_process();
+
+private:
+ enum ErrorType { NO_ERROR, PORT_NOT_FOUND, TYPE_MISMATCH };
+
+ int m_voice_num;
+ string m_port_path;
+ float m_val;
+ Port* m_port;
+ ErrorType m_error;
+};
+
+
+} // namespace Om
+
+#endif // SETPORTVALUEEVENT_H
diff --git a/src/libs/engine/events/SetPortValueQueuedEvent.cpp b/src/libs/engine/events/SetPortValueQueuedEvent.cpp
new file mode 100644
index 00000000..e73c0486
--- /dev/null
+++ b/src/libs/engine/events/SetPortValueQueuedEvent.cpp
@@ -0,0 +1,116 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "SetPortValueQueuedEvent.h"
+#include "Responder.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "PortBase.h"
+#include "PortInfo.h"
+#include "ClientBroadcaster.h"
+#include "Plugin.h"
+#include "Node.h"
+#include "ObjectStore.h"
+
+namespace Om {
+
+
+/** Voice-specific control setting
+ */
+SetPortValueQueuedEvent::SetPortValueQueuedEvent(CountedPtr<Responder> responder, size_t voice_num, const string& port_path, sample val)
+: QueuedEvent(responder),
+ m_voice_num(voice_num),
+ m_port_path(port_path),
+ m_val(val),
+ m_port(NULL),
+ m_error(NO_ERROR)
+{
+}
+
+
+SetPortValueQueuedEvent::SetPortValueQueuedEvent(CountedPtr<Responder> responder, const string& port_path, sample val)
+: QueuedEvent(responder),
+ m_voice_num(-1),
+ m_port_path(port_path),
+ m_val(val),
+ m_port(NULL),
+ m_error(NO_ERROR)
+{
+}
+
+
+void
+SetPortValueQueuedEvent::pre_process()
+{
+ if (m_port == NULL)
+ m_port = om->object_store()->find_port(m_port_path);
+
+ if (m_port == NULL) {
+ m_error = PORT_NOT_FOUND;
+ } else if (!m_port->port_info()->is_audio() && !m_port->port_info()->is_control()) {
+ m_error = TYPE_MISMATCH;
+ }
+
+ QueuedEvent::pre_process();
+}
+
+
+void
+SetPortValueQueuedEvent::execute(samplecount offset)
+{
+ QueuedEvent::execute(offset);
+
+ if (m_error == NO_ERROR) {
+ assert(m_port != NULL);
+ if (m_voice_num == -1)
+ ((PortBase<sample>*)m_port)->set_value(m_val, offset);
+ else
+ ((PortBase<sample>*)m_port)->buffer(m_voice_num)->set(m_val, offset); // FIXME: check range
+ }
+}
+
+
+void
+SetPortValueQueuedEvent::post_process()
+{
+ if (m_error == NO_ERROR) {
+ assert(m_port != NULL);
+
+ m_responder->respond_ok();
+ om->client_broadcaster()->send_control_change(m_port_path, m_val);
+
+ // Send patch port control change, if this is a bridge port
+ Port* parent_port = m_port->parent_node()->as_port();
+ if (parent_port != NULL) {
+ assert(parent_port->port_info()->is_control() || parent_port->port_info()->is_audio());
+ om->client_broadcaster()->send_control_change(parent_port->path(), m_val);
+ }
+
+ } else if (m_error == PORT_NOT_FOUND) {
+ string msg = "Unable to find port ";
+ msg.append(m_port_path).append(" for set_port_value_slow");
+ m_responder->respond_error(msg);
+
+ } else if (m_error == TYPE_MISMATCH) {
+ string msg = "Attempt to set ";
+ msg.append(m_port_path).append(" to incompatible type");
+ m_responder->respond_error(msg);
+ }
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/events/SetPortValueQueuedEvent.h b/src/libs/engine/events/SetPortValueQueuedEvent.h
new file mode 100644
index 00000000..b92beacf
--- /dev/null
+++ b/src/libs/engine/events/SetPortValueQueuedEvent.h
@@ -0,0 +1,57 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef SETPORTVALUEQUEUEDEVENT_H
+#define SETPORTVALUEQUEUEDEVENT_H
+
+#include "QueuedEvent.h"
+#include "util/types.h"
+#include <string>
+using std::string;
+
+namespace Om {
+
+class Port;
+
+
+/** An event to change the value of a port.
+ *
+ * \ingroup engine
+ */
+class SetPortValueQueuedEvent : public QueuedEvent
+{
+public:
+ SetPortValueQueuedEvent(CountedPtr<Responder> responder, const string& port_path, sample val);
+ SetPortValueQueuedEvent(CountedPtr<Responder> responder, size_t voice_num, const string& port_path, sample val);
+
+ void pre_process();
+ void execute(samplecount offset);
+ void post_process();
+
+private:
+ enum ErrorType { NO_ERROR, PORT_NOT_FOUND, TYPE_MISMATCH };
+
+ int m_voice_num;
+ string m_port_path;
+ float m_val;
+ Port* m_port;
+ ErrorType m_error;
+};
+
+
+} // namespace Om
+
+#endif // SETPORTVALUEQUEUEDEVENT_H
diff --git a/src/libs/engine/events/UnregisterClientEvent.cpp b/src/libs/engine/events/UnregisterClientEvent.cpp
new file mode 100644
index 00000000..ef5a9979
--- /dev/null
+++ b/src/libs/engine/events/UnregisterClientEvent.cpp
@@ -0,0 +1,45 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "UnregisterClientEvent.h"
+#include "Responder.h"
+#include "Om.h"
+#include "OmApp.h"
+#include "ClientBroadcaster.h"
+#include "interface/ClientInterface.h"
+
+namespace Om {
+
+
+UnregisterClientEvent::UnregisterClientEvent(CountedPtr<Responder> responder, ClientKey key)
+: QueuedEvent(responder)
+, _key(key)
+{
+}
+
+
+void
+UnregisterClientEvent::post_process()
+{
+ if (om->client_broadcaster()->unregister_client(_key))
+ m_responder->respond_ok();
+ else
+ m_responder->respond_error("Unable to unregister client");
+}
+
+
+} // namespace Om
+
diff --git a/src/libs/engine/events/UnregisterClientEvent.h b/src/libs/engine/events/UnregisterClientEvent.h
new file mode 100644
index 00000000..fdbdb314
--- /dev/null
+++ b/src/libs/engine/events/UnregisterClientEvent.h
@@ -0,0 +1,53 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UNREGISTERCLIENTEVENT_H
+#define UNREGISTERCLIENTEVENT_H
+
+#include "QueuedEvent.h"
+#include "interface/ClientKey.h"
+#include <string>
+using std::string;
+
+namespace Om {
+
+namespace Shared {
+ class ClientInterface;
+ class ClientKey;
+}
+using Shared::ClientInterface;
+using Shared::ClientKey;
+
+
+/** Unregisters an OSC client so it no longer receives notifications.
+ *
+ * \ingroup engine
+ */
+class UnregisterClientEvent : public QueuedEvent
+{
+public:
+ UnregisterClientEvent(CountedPtr<Responder> responder, ClientKey key);
+
+ void post_process();
+
+private:
+ ClientKey _key;
+};
+
+
+} // namespace Om
+
+#endif // UNREGISTERCLIENTEVENT_H
diff --git a/src/libs/engine/instantiations.cpp b/src/libs/engine/instantiations.cpp
new file mode 100644
index 00000000..e2b0088d
--- /dev/null
+++ b/src/libs/engine/instantiations.cpp
@@ -0,0 +1,49 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/** @file
+ * Explicit template instantiations.
+ *
+ * Need to do this to avoid undefined references, because GCC doesn't seem to
+ * know how to recursively instantiate templates. Cleaner to do it all here
+ * than pollute everything with it. :/
+ */
+
+#include "Tree.h"
+#include "TreeImplementation.h"
+#include "OmObject.h"
+#include "Node.h"
+
+
+/* Tree */
+template class Tree<Om::Node*>;
+template class TreeNode<Om::Node*>;
+
+template Tree<Om::OmObject*>::Tree();
+template Tree<Om::OmObject*>::~Tree();
+template void Tree<Om::OmObject*>::insert(TreeNode<Om::OmObject*>* const n);
+template TreeNode<Om::OmObject*>* Tree<Om::OmObject*>::remove(const string& key);
+template Om::OmObject* Tree<Om::OmObject*>::find(const string& key) const;
+template TreeNode<Om::OmObject*>* Tree<Om::OmObject*>::find_treenode(const string& key) const;
+
+template Tree<Om::OmObject*>::iterator Tree<Om::OmObject*>::begin() const;
+template Tree<Om::OmObject*>::iterator Tree<Om::OmObject*>::end() const;
+
+template Tree<Om::OmObject*>::iterator::~iterator();
+template Om::OmObject* Tree<Om::OmObject*>::iterator::operator*() const;
+template Tree<Om::OmObject*>::iterator& Tree<Om::OmObject*>::iterator::operator++();
+template bool Tree<Om::OmObject*>::iterator::operator!=(const iterator& iter) const;
+
diff --git a/src/libs/engine/main.cpp b/src/libs/engine/main.cpp
new file mode 100644
index 00000000..a69c7e76
--- /dev/null
+++ b/src/libs/engine/main.cpp
@@ -0,0 +1,153 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <iostream>
+#include <cstddef>
+#include <signal.h>
+#include "config.h"
+#include "util.h"
+#include "cmdline.h"
+#include "Om.h"
+#include "OmApp.h"
+#ifdef HAVE_LASH
+#include "LashDriver.h"
+#endif
+#ifdef BUILD_IN_PROCESS_ENGINE
+#include <jack/jack.h>
+#include <jack/intclient.h>
+#endif
+
+using std::cout; using std::endl; using std::cerr;
+
+
+void
+catch_int(int)
+{
+ signal(SIGINT, catch_int);
+ signal(SIGTERM, catch_int);
+
+ std::cout << "[Main] Om interrupted." << std::endl;
+ Om::om->quit();
+}
+
+
+#ifdef BUILD_IN_PROCESS_ENGINE
+
+jack_client_t* jack_client;
+jack_intclient_t jack_intclient;
+
+
+void
+unload_in_process_engine(int)
+{
+ jack_status_t status;
+ int ret = EXIT_SUCCESS;
+
+ cout << "Unloading...";
+ status = jack_internal_client_unload(jack_client, jack_intclient);
+ if (status & JackFailure) {
+ cout << "failed" << endl;
+ ret = EXIT_FAILURE;
+ } else {
+ cout << "done" << endl;
+ }
+ jack_client_close(jack_client);
+ exit(ret);
+}
+
+
+int
+load_in_process_engine(const char* port)
+{
+ int ret = EXIT_SUCCESS;
+
+ jack_status_t status;
+
+ if ((jack_client = jack_client_open("om_load", JackNoStartServer,
+ &status)) != NULL) {
+ jack_intclient =
+ jack_internal_client_load(jack_client, "Om",
+ (jack_options_t)(JackLoadName|JackLoadInit),
+ &status, "om", port);
+ if (status == 0) {
+ cout << "Engine loaded" << endl;
+ signal(SIGINT, unload_in_process_engine);
+ signal(SIGTERM, unload_in_process_engine);
+
+ while (1) {
+ sleep(1);
+ }
+ } else if (status & JackFailure) {
+ cerr << "Could not load om.so" << endl;
+ ret = EXIT_FAILURE;
+ }
+
+ jack_client_close(jack_client);
+ } else {
+ cerr << "jack_client_open failed" << endl;
+ ret = EXIT_FAILURE;
+ }
+}
+
+#endif // BUILD_IN_PROCESS_ENGINE
+
+
+int
+main(int argc, char** argv)
+{
+#ifdef HAVE_LASH
+ lash_args_t* lash_args = lash_extract_args(&argc, &argv);
+#endif
+
+ int ret = EXIT_SUCCESS;
+
+ /* Parse command line options */
+ gengetopt_args_info args_info;
+ if (cmdline_parser (argc, argv, &args_info) != 0)
+ return EXIT_FAILURE;
+
+
+ if (args_info.in_jackd_flag) {
+#ifdef BUILD_IN_PROCESS_ENGINE
+ ret = load_in_process_engine(args_info.port_arg);
+#else
+ cerr << "In-process Jack client support not enabled in this build." << endl;
+ ret = EXIT_FAILURE;
+#endif // JACK_IN_PROCESS_ENGINE
+ } else {
+ signal(SIGINT, catch_int);
+ signal(SIGTERM, catch_int);
+
+ Om::set_denormal_flags();
+
+ Om::om = new Om::OmApp(args_info.port_arg);
+
+#ifdef HAVE_LASH
+ Om::lash_driver = new Om::LashDriver(Om::om, lash_args);
+#endif
+
+ Om::om->main();
+
+#ifdef HAVE_LASH
+ delete Om::lash_driver;
+#endif
+
+ delete Om::om;
+ }
+
+ return ret;
+}
+
diff --git a/src/libs/engine/midi.h b/src/libs/engine/midi.h
new file mode 100644
index 00000000..6575ede7
--- /dev/null
+++ b/src/libs/engine/midi.h
@@ -0,0 +1,135 @@
+/* Definitions to ease working with raw MIDI.
+ *
+ * Stolen from Alsa's asounddef.h
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef MIDI_H
+#define MIDI_H
+
+
+/**
+ * \defgroup midi MIDI Definitions
+ * MIDI command and controller number definitions.
+ * \{
+ */
+
+// Commands:
+
+#define MIDI_CMD_NOTE_OFF 0x80 /**< note off */
+#define MIDI_CMD_NOTE_ON 0x90 /**< note on */
+#define MIDI_CMD_NOTE_PRESSURE 0xA0 /**< key pressure */
+#define MIDI_CMD_CONTROL 0xB0 /**< control change */
+#define MIDI_CMD_PGM_CHANGE 0xC0 /**< program change */
+#define MIDI_CMD_CHANNEL_PRESSURE 0xD0 /**< channel pressure */
+#define MIDI_CMD_BENDER 0xE0 /**< pitch bender */
+
+#define MIDI_CMD_COMMON_SYSEX 0xF0 /**< sysex (system exclusive) begin */
+#define MIDI_CMD_COMMON_MTC_QUARTER 0xF1 /**< MTC quarter frame */
+#define MIDI_CMD_COMMON_SONG_POS 0xF2 /**< song position */
+#define MIDI_CMD_COMMON_SONG_SELECT 0xF3 /**< song select */
+#define MIDI_CMD_COMMON_TUNE_REQUEST 0xF6 /**< tune request */
+#define MIDI_CMD_COMMON_SYSEX_END 0xF7 /**< end of sysex */
+#define MIDI_CMD_COMMON_CLOCK 0xF8 /**< clock */
+#define MIDI_CMD_COMMON_START 0xFA /**< start */
+#define MIDI_CMD_COMMON_CONTINUE 0xFB /**< continue */
+#define MIDI_CMD_COMMON_STOP 0xFC /**< stop */
+#define MIDI_CMD_COMMON_SENSING 0xFE /**< active sensing */
+#define MIDI_CMD_COMMON_RESET 0xFF /**< reset */
+
+
+// Controllers:
+
+#define MIDI_CTL_MSB_BANK 0x00 /**< Bank selection */
+#define MIDI_CTL_MSB_MODWHEEL 0x01 /**< Modulation */
+#define MIDI_CTL_MSB_BREATH 0x02 /**< Breath */
+#define MIDI_CTL_MSB_FOOT 0x04 /**< Foot */
+#define MIDI_CTL_MSB_PORTAMENTO_TIME 0x05 /**< Portamento time */
+#define MIDI_CTL_MSB_DATA_ENTRY 0x06 /**< Data entry */
+#define MIDI_CTL_MSB_MAIN_VOLUME 0x07 /**< Main volume */
+#define MIDI_CTL_MSB_BALANCE 0x08 /**< Balance */
+#define MIDI_CTL_MSB_PAN 0x0A /**< Panpot */
+#define MIDI_CTL_MSB_EXPRESSION 0x0B /**< Expression */
+#define MIDI_CTL_MSB_EFFECT1 0x0C /**< Effect1 */
+#define MIDI_CTL_MSB_EFFECT2 0x0D /**< Effect2 */
+#define MIDI_CTL_MSB_GENERAL_PURPOSE1 0x10 /**< General purpose 1 */
+#define MIDI_CTL_MSB_GENERAL_PURPOSE2 0x11 /**< General purpose 2 */
+#define MIDI_CTL_MSB_GENERAL_PURPOSE3 0x12 /**< General purpose 3 */
+#define MIDI_CTL_MSB_GENERAL_PURPOSE4 0x13 /**< General purpose 4 */
+#define MIDI_CTL_LSB_BANK 0x20 /**< Bank selection */
+#define MIDI_CTL_LSB_MODWHEEL 0x21 /**< Modulation */
+#define MIDI_CTL_LSB_BREATH 0x22 /**< Breath */
+#define MIDI_CTL_LSB_FOOT 0x24 /**< Foot */
+#define MIDI_CTL_LSB_PORTAMENTO_TIME 0x25 /**< Portamento time */
+#define MIDI_CTL_LSB_DATA_ENTRY 0x26 /**< Data entry */
+#define MIDI_CTL_LSB_MAIN_VOLUME 0x27 /**< Main volume */
+#define MIDI_CTL_LSB_BALANCE 0x28 /**< Balance */
+#define MIDI_CTL_LSB_PAN 0x2A /**< Panpot */
+#define MIDI_CTL_LSB_EXPRESSION 0x2B /**< Expression */
+#define MIDI_CTL_LSB_EFFECT1 0x2C /**< Effect1 */
+#define MIDI_CTL_LSB_EFFECT2 0x2D /**< Effect2 */
+#define MIDI_CTL_LSB_GENERAL_PURPOSE1 0x30 /**< General purpose 1 */
+#define MIDI_CTL_LSB_GENERAL_PURPOSE2 0x31 /**< General purpose 2 */
+#define MIDI_CTL_LSB_GENERAL_PURPOSE3 0x32 /**< General purpose 3 */
+#define MIDI_CTL_LSB_GENERAL_PURPOSE4 0x33 /**< General purpose 4 */
+#define MIDI_CTL_SUSTAIN 0x40 /**< Sustain pedal */
+#define MIDI_CTL_PORTAMENTO 0x41 /**< Portamento */
+#define MIDI_CTL_SOSTENUTO 0x42 /**< Sostenuto */
+#define MIDI_CTL_SUSTENUTO 0x42 /**< Sostenuto (a typo in the older version) */
+#define MIDI_CTL_SOFT_PEDAL 0x43 /**< Soft pedal */
+#define MIDI_CTL_LEGATO_FOOTSWITCH 0x44 /**< Legato foot switch */
+#define MIDI_CTL_HOLD2 0x45 /**< Hold2 */
+#define MIDI_CTL_SC1_SOUND_VARIATION 0x46 /**< SC1 Sound Variation */
+#define MIDI_CTL_SC2_TIMBRE 0x47 /**< SC2 Timbre */
+#define MIDI_CTL_SC3_RELEASE_TIME 0x48 /**< SC3 Release Time */
+#define MIDI_CTL_SC4_ATTACK_TIME 0x49 /**< SC4 Attack Time */
+#define MIDI_CTL_SC5_BRIGHTNESS 0x4A /**< SC5 Brightness */
+#define MIDI_CTL_SC6 0x4B /**< SC6 */
+#define MIDI_CTL_SC7 0x4C /**< SC7 */
+#define MIDI_CTL_SC8 0x4D /**< SC8 */
+#define MIDI_CTL_SC9 0x4E /**< SC9 */
+#define MIDI_CTL_SC10 0x4F /**< SC10 */
+#define MIDI_CTL_GENERAL_PURPOSE5 0x50 /**< General purpose 5 */
+#define MIDI_CTL_GENERAL_PURPOSE6 0x51 /**< General purpose 6 */
+#define MIDI_CTL_GENERAL_PURPOSE7 0x52 /**< General purpose 7 */
+#define MIDI_CTL_GENERAL_PURPOSE8 0x53 /**< General purpose 8 */
+#define MIDI_CTL_PORTAMENTO_CONTROL 0x54 /**< Portamento control */
+#define MIDI_CTL_E1_REVERB_DEPTH 0x5B /**< E1 Reverb Depth */
+#define MIDI_CTL_E2_TREMOLO_DEPTH 0x5C /**< E2 Tremolo Depth */
+#define MIDI_CTL_E3_CHORUS_DEPTH 0x5D /**< E3 Chorus Depth */
+#define MIDI_CTL_E4_DETUNE_DEPTH 0x5E /**< E4 Detune Depth */
+#define MIDI_CTL_E5_PHASER_DEPTH 0x5F /**< E5 Phaser Depth */
+#define MIDI_CTL_DATA_INCREMENT 0x60 /**< Data Increment */
+#define MIDI_CTL_DATA_DECREMENT 0x61 /**< Data Decrement */
+#define MIDI_CTL_NONREG_PARM_NUM_LSB 0x62 /**< Non-registered parameter number */
+#define MIDI_CTL_NONREG_PARM_NUM_MSB 0x63 /**< Non-registered parameter number */
+#define MIDI_CTL_REGIST_PARM_NUM_LSB 0x64 /**< Registered parameter number */
+#define MIDI_CTL_REGIST_PARM_NUM_MSB 0x65 /**< Registered parameter number */
+#define MIDI_CTL_ALL_SOUNDS_OFF 0x78 /**< All sounds off */
+#define MIDI_CTL_RESET_CONTROLLERS 0x79 /**< Reset Controllers */
+#define MIDI_CTL_LOCAL_CONTROL_SWITCH 0x7A /**< Local control switch */
+#define MIDI_CTL_ALL_NOTES_OFF 0x7B /**< All notes off */
+#define MIDI_CTL_OMNI_OFF 0x7C /**< Omni off */
+#define MIDI_CTL_OMNI_ON 0x7D /**< Omni on */
+#define MIDI_CTL_MONO1 0x7E /**< Mono1 */
+#define MIDI_CTL_MONO2 0x7F /**< Mono2 */
+//@}
+
+
+/** \} */
+
+#endif /* MIDI_H */
diff --git a/src/libs/engine/tests/Makefile.am b/src/libs/engine/tests/Makefile.am
new file mode 100644
index 00000000..54f6ad1f
--- /dev/null
+++ b/src/libs/engine/tests/Makefile.am
@@ -0,0 +1,27 @@
+if BUILD_UNIT_TESTS
+
+AM_CXXFLAGS = @JACK_CFLAGS@ @LOSC_CFLAGS@ @ALSA_CFLAGS@ -I../../common
+common_ldadd = @JACK_LIBS@ @LOSC_LIBS@ @ALSA_LIBS@ -lrt
+node_tree_test_LDADD = $(common_ldadd)
+queue_test_LDADD = $(common_ldadd)
+
+bin_PROGRAMS = node_tree_test queue_test list_test path_test
+
+list_test_SOURCES = \
+ ../List.h \
+ list_test.cpp
+
+path_test_SOURCES = \
+ ../../common/Path.h \
+ path_test.cpp
+
+node_tree_test_SOURCES = \
+ node_tree_test.cpp \
+ ../Tree.h \
+ ../TreeImplementation.h
+
+queue_test_SOURCES = \
+ queue_test.cpp \
+ ../../common/Queue.h
+
+endif # BUILD_UNIT_TESTS
diff --git a/src/libs/engine/tests/list_test.cpp b/src/libs/engine/tests/list_test.cpp
new file mode 100644
index 00000000..d2f91c9d
--- /dev/null
+++ b/src/libs/engine/tests/list_test.cpp
@@ -0,0 +1,93 @@
+#include "../List.h"
+#include <iostream>
+#include <cstddef>
+
+using std::cout; using std::endl;
+
+
+int main()
+{
+ List<int> l;
+
+ l.push_back(new ListNode<int>(1));
+ l.push_back(new ListNode<int>(2));
+ l.push_back(new ListNode<int>(3));
+ l.push_back(new ListNode<int>(4));
+ l.push_back(new ListNode<int>(5));
+ l.push_back(new ListNode<int>(6));
+ l.push_back(new ListNode<int>(7));
+ l.push_back(new ListNode<int>(8));
+
+ cout << "List:" << endl;
+ for (List<int>::iterator i = l.begin(); i != l.end(); ++i) {
+ cout << *i << endl;
+ }
+ cout << endl;
+
+
+ for (List<int>::iterator i = l.begin(); i != l.end(); ++i) {
+ if ((*i) == 4)
+ l.remove(i);
+ }
+
+ std::cerr << "Removed 4 (by iterator)...\n";
+ for (List<int>::iterator i = l.begin(); i != l.end(); ++i) {
+ cout << *i << endl;
+ }
+ cout << endl;
+
+ l.remove(1);
+
+ std::cerr << "Removed 1 (head) (by value)...\n";
+ for (List<int>::iterator i = l.begin(); i != l.end(); ++i) {
+ cout << *i << endl;
+ }
+ cout << endl;
+
+ for (List<int>::iterator i = l.begin(); i != l.end(); ++i) {
+ if ((*i) == 2)
+ l.remove(i);
+ }
+
+ std::cerr << "Removed 2 (head) (by iterator)...\n";
+ for (List<int>::iterator i = l.begin(); i != l.end(); ++i) {
+ cout << *i << endl;
+ }
+ cout << endl;
+
+ l.remove(5);
+
+ std::cerr << "Removed 5 (by value)...\n";
+ for (List<int>::iterator i = l.begin(); i != l.end(); ++i) {
+ cout << *i << endl;
+ }
+ cout << endl;
+
+ l.remove(8);
+
+ std::cerr << "Removed 8 (tail) (by value)...\n";
+ for (List<int>::iterator i = l.begin(); i != l.end(); ++i) {
+ cout << *i << endl;
+ }
+ cout << endl;
+
+ for (List<int>::iterator i = l.begin(); i != l.end(); ++i) {
+ if ((*i) == 7)
+ l.remove(i);
+ }
+
+ std::cerr << "Removed 7 (tail) (by iterator)...\n";
+ for (List<int>::iterator i = l.begin(); i != l.end(); ++i) {
+ cout << *i << endl;
+ }
+ cout << endl;
+
+ List<int> r;
+ r.push_back(new ListNode<int>(9));
+ r.remove(9);
+ std::cerr << "Should not see ANY numbers:\n";
+ for (List<int>::iterator i = r.begin(); i != r.end(); ++i) {
+ cout << *i << endl;
+ }
+ return 0;
+}
diff --git a/src/libs/engine/tests/node_tree_test.cpp b/src/libs/engine/tests/node_tree_test.cpp
new file mode 100644
index 00000000..2bce9b97
--- /dev/null
+++ b/src/libs/engine/tests/node_tree_test.cpp
@@ -0,0 +1,94 @@
+#include <cstdlib>
+#include <iostream>
+#include <vector>
+#include "../Tree.h"
+#include "../TreeImplementation.h"
+
+using std::vector;
+using std::cout; using std::cerr; using std::endl;
+
+static const uint NUM_NODES = 20;
+
+int
+main()
+{
+ cout << "\n\n\n\n";
+
+ Tree<string> tree;
+ vector<string> names;
+ vector<bool> in_tree; // arrays
+ uint num_in_tree = 0;
+
+
+ string name;
+
+ for (uint i=0; i < NUM_NODES; ++i) {
+ name = (char)(i+'A');
+ TreeNode<string>* n = new TreeNode<string>(name, name);
+ tree.insert(n);
+ names.push_back(name);
+ in_tree.push_back(true);
+ ++num_in_tree;
+ }
+
+ cout << "Added " << NUM_NODES << " nodes." << endl;
+ cout << "Tree size: " << tree.size() << endl << endl;
+
+ cout << "Tree contents: " << endl;
+ for (Tree<string>::iterator i = tree.begin(); i != tree.end(); ++i) {
+ cout << (*i) << ", ";
+ }
+ cout << endl;
+
+
+ while (true) {
+ bool insert;
+ int num = rand() % NUM_NODES;
+
+ if (num_in_tree == 0)
+ insert = true;
+ else if (num_in_tree == NUM_NODES)
+ insert = false;
+ else {
+ while (true) {
+ insert = rand() % 2;
+ num = rand() % NUM_NODES;
+ if ((insert && !in_tree[num]) || (!insert && in_tree[num]))
+ break;
+ }
+ }
+
+ string name = names[num];
+
+ if (insert) {
+ assert(in_tree[num] == false);
+ cout << "\nInserting '" << name << "'" << endl;
+ tree.insert(new TreeNode<string>(name, name));
+ in_tree[num] = true;
+ ++num_in_tree;
+ cout << "Tree size: " << tree.size() << endl;
+ assert(num_in_tree == tree.size());
+ } else {
+ assert(in_tree[num] == true);
+ cout << "\nRemoving '" << name << "'" << endl;
+ TreeNode<string>* removed = tree.remove(name);
+ assert(removed != NULL);
+ assert(removed->node() == name);
+ assert(removed->key() == name);
+ delete removed;
+ in_tree[num] = false;
+ assert(names[num] == name);
+ --num_in_tree;
+ cout << "Tree size: " << tree.size() << endl;
+ assert(num_in_tree == tree.size());
+ }
+ assert(num_in_tree == tree.size());
+ cout << "Tree contents: " << endl;
+ for (Tree<string>::iterator i = tree.begin(); i != tree.end(); ++i) {
+ cout << (*i) << ", ";
+ }
+ cout << endl;
+ }
+
+ return 0;
+}
diff --git a/src/libs/engine/tests/old_node_tree_test.cpp b/src/libs/engine/tests/old_node_tree_test.cpp
new file mode 100644
index 00000000..3b5ed485
--- /dev/null
+++ b/src/libs/engine/tests/old_node_tree_test.cpp
@@ -0,0 +1,72 @@
+#include <iostream>
+#include "../NodeTree.h"
+#include "../NodeBase.h"
+
+using std::cout; using std::cerr; using std::endl;
+
+int main()
+{
+ cout << "\n\n\n\n";
+
+ NodeBase* n = NULL;
+ TreeNode* tn = NULL;
+ TreeNode* baz = NULL;
+ TreeNode* quuux = NULL;
+ TreeNode* bar = NULL;
+
+ NodeTree tree;
+ n = new NodeBase("foo", 0, 0, 0);
+ tn = new TreeNode(n);
+ tree.insert(tn);
+ n = new NodeBase("bar", 0, 0, 0);
+ bar = new TreeNode(n);
+ tree.insert(bar);
+ n = new NodeBase("baz", 0, 0, 0);
+ baz = new TreeNode(n);
+ tree.insert(baz);
+ n = new NodeBase("quux", 0, 0, 0);
+ tn = new TreeNode(n);
+ tree.insert(tn);
+ n = new NodeBase("quuux", 0, 0, 0);
+ quuux = new TreeNode(n);
+ tree.insert(quuux);
+ n = new NodeBase("quuuux", 0, 0, 0);
+ tn = new TreeNode(n);
+ tree.insert(tn);
+
+
+ cout << "Added 6 nodes." << endl;
+ cout << "Tree size: " << tree.size() << endl << endl;
+
+ cout << "Iterating: " << endl;
+ for (NodeTree::iterator i = tree.begin(); i != tree.end(); ++i) {
+ cout << (*i)->name() << endl;
+ }
+
+ cout << endl << "Search 'foo' - " << tree.find("foo")->name() << endl;
+ cout << endl << "Search 'bar' - " << tree.find("bar")->name() << endl;
+ cout << endl << "Search 'baz' - " << tree.find("baz")->name() << endl;
+ cout << endl << "Search 'quux' - " << tree.find("quux")->name() << endl;
+ cout << endl << "Search 'quuux' - " << tree.find("quuux")->name() << endl;
+ cout << endl << "Search 'quuuux' - " << tree.find("quuuux")->name() << endl;
+ cout << endl << "Search 'dave' - " << tree.find("dave") << endl;
+ cout << endl << "Search 'fo' - " << tree.find("fo") << endl << endl;
+
+ cout << "Removing 'baz'." << endl;
+ tree.remove(baz);
+
+ cout << "Iterating: " << endl;
+ for (NodeTree::iterator i = tree.begin(); i != tree.end(); ++i) {
+ cout << (*i)->name() << endl;
+ }
+
+ cout << "Removing 'bar' (the root): " << endl;
+ tree.remove(bar);
+
+ cout << "Iterating: " << endl;
+ for (NodeTree::iterator i = tree.begin(); i != tree.end(); ++i) {
+ cout << (*i)->name() << endl;
+ }
+
+ return 0;
+}
diff --git a/src/libs/engine/tests/queue_test.cpp b/src/libs/engine/tests/queue_test.cpp
new file mode 100644
index 00000000..3381a329
--- /dev/null
+++ b/src/libs/engine/tests/queue_test.cpp
@@ -0,0 +1,47 @@
+#include <iostream>
+#include <string>
+#include "../../common/Queue.h"
+
+using std::string; using std::cerr; using std::cout; using std::endl;
+
+
+int main()
+{
+ Queue<int> q(10);
+
+ cout << "New queue. Should be empty: " << q.is_empty() << endl;
+ cout << "Capacity: " << q.capacity() << endl;
+ cout << "Fill: " << q.fill() << endl;
+
+ for (uint i=0; i < 5; ++i) {
+ q.push(i);
+ assert(!q.is_full());
+ q.pop();
+ }
+ cout << "Pushed and popped 5 elements. Queue should be empty: " << q.is_empty() << endl;
+ cout << "Fill: " << q.fill() << endl;
+
+ for (uint i=10; i < 20; ++i) {
+ q.push(i);
+ }
+ cout << "Pushed 10 elements. Queue should be full: " << q.is_full() << endl;
+ cout << "Fill: " << q.fill() << endl;
+
+ cout << "The digits 10->19 should print: " << endl;
+ while (!q.is_empty()) {
+ int foo = q.pop();
+ cout << "Popped: " << foo << endl;
+ }
+ cout << "Queue should be empty: " << q.is_empty() << endl;
+ cout << "Fill: " << q.fill() << endl;
+
+ cout << "Attempting to add eleven elements to queue of size 10. Only first 10 should succeed:" << endl;
+ for (uint i=20; i <= 39; ++i) {
+ cout << i;
+ cout << " - Fill: " << q.fill();
+ cout << ", is full: " << q.is_full();
+ cout << ", succeeded: " << q.push(i) << endl;
+ }
+
+ return 0;
+}
diff --git a/src/libs/engine/tuning.h b/src/libs/engine/tuning.h
new file mode 100644
index 00000000..aeec81da
--- /dev/null
+++ b/src/libs/engine/tuning.h
@@ -0,0 +1,39 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef TUNING_H
+#define TUNING_H
+
+#include <stddef.h>
+#include <time.h>
+
+namespace Om {
+
+// FIXME: put this in a Config class
+
+static const size_t event_queue_size = 1024;
+static const size_t pre_processor_queue_size = 1024;
+static const size_t post_processor_queue_size = 1024;
+static const size_t maid_queue_size = 1024;
+
+// This controls both the LASH event processing rate and the Maid cleanup rate
+// (both of which are driven from the main thread)
+static const timespec main_rate = { 0, 500000000 }; // 1/2 second
+
+
+} // namespace Om
+
+#endif // TUNING_H
diff --git a/src/libs/engine/util.h b/src/libs/engine/util.h
new file mode 100644
index 00000000..9cf4bf7f
--- /dev/null
+++ b/src/libs/engine/util.h
@@ -0,0 +1,73 @@
+/* This file is part of Om. Copyright (C) 2006 Dave Robillard.
+ *
+ * Om is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Om is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UTIL_H
+#define UTIL_H
+
+#include <iostream>
+#include <cstdlib>
+
+#include <fenv.h>
+#ifdef __SSE__
+#include <xmmintrin.h>
+#endif
+
+using std::cerr; using std::endl;
+
+namespace Om {
+
+/** Set flags to disable denormal processing.
+ */
+inline void
+set_denormal_flags()
+{
+#ifdef __SSE__
+ unsigned long a, b, c, d;
+
+ asm("cpuid": "=a" (a), "=b" (b), "=c" (c), "=d" (d) : "a" (1));
+ if (d & 1<<25) { /* It has SSE support */
+ _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
+
+ asm("cpuid": "=a" (a), "=b" (b), "=c" (c), "=d" (d) : "a" (0));
+ if (b == 0x756e6547) { /* It's an Intel */
+ int stepping, model, family, extfamily;
+
+ family = (a >> 8) & 0xf;
+ extfamily = (a >> 20) & 0xff;
+ model = (a >> 4) & 0xf;
+ stepping = a & 0xf;
+ if (family == 15 && extfamily == 0 && model == 0 && stepping < 7) {
+ return;
+ }
+ }
+ asm("cpuid": "=a" (a), "=b" (b), "=c" (c), "=d" (d) : "a" (1));
+ if (d & 1<<26) { /* bit 26, SSE2 support */
+ _mm_setcsr(_mm_getcsr() | 0x40);
+ //cerr << "Set SSE denormal fix flag." << endl;
+ }
+ } else {
+ cerr << "This code has been built with SSE support, but your processor does"
+ << " not support the SSE instruction set." << endl << "Exiting." << endl;
+ exit(EXIT_FAILURE);
+ }
+#endif
+ // Set 387 control register via standard C99 interface
+ fesetround(FE_TOWARDZERO);
+}
+
+} // namespace Om
+
+#endif // UTIL_H