From 93850c202de8b073a1ce1dd8bd246d407bce4e2f Mon Sep 17 00:00:00 2001 From: David Robillard Date: Tue, 30 Sep 2008 16:50:21 +0000 Subject: Flatten ingen source directory heirarchy a bit. git-svn-id: http://svn.drobilla.net/lad/trunk/ingen@1551 a436a847-0d15-0410-975c-d299462d15a1 --- src/client/ClientStore.cpp | 648 ++++ src/client/ClientStore.hpp | 151 + src/client/ConnectionModel.hpp | 76 + src/client/DeprecatedLoader.cpp | 711 +++++ src/client/DeprecatedLoader.hpp | 94 + src/client/HTTPClientReceiver.cpp | 97 + src/client/HTTPClientReceiver.hpp | 62 + src/client/HTTPEngineSender.cpp | 285 ++ src/client/HTTPEngineSender.hpp | 155 + src/client/Makefile.am | 69 + src/client/NodeModel.cpp | 224 ++ src/client/NodeModel.hpp | 101 + src/client/OSCClientReceiver.cpp | 394 +++ src/client/OSCClientReceiver.hpp | 107 + src/client/OSCEngineSender.cpp | 420 +++ src/client/OSCEngineSender.hpp | 154 + src/client/ObjectModel.cpp | 148 + src/client/ObjectModel.hpp | 115 + src/client/PatchModel.cpp | 190 ++ src/client/PatchModel.hpp | 105 + src/client/PluginModel.cpp | 143 + src/client/PluginModel.hpp | 142 + src/client/PluginUI.cpp | 157 + src/client/PluginUI.hpp | 65 + src/client/PortModel.cpp | 66 + src/client/PortModel.hpp | 112 + src/client/SigClientInterface.hpp | 156 + src/client/ThreadedSigClientInterface.cpp | 78 + src/client/ThreadedSigClientInterface.hpp | 184 ++ src/client/client.cpp | 57 + src/client/client.hpp | 43 + src/client/wscript | 35 + src/engine/AudioBuffer.cpp | 299 ++ src/engine/AudioBuffer.hpp | 84 + src/engine/AudioDriver.hpp | 62 + src/engine/Buffer.cpp | 38 + src/engine/Buffer.hpp | 76 + src/engine/ClientBroadcaster.cpp | 271 ++ src/engine/ClientBroadcaster.hpp | 97 + src/engine/CompiledPatch.hpp | 83 + src/engine/ConnectionImpl.cpp | 185 ++ src/engine/ConnectionImpl.hpp | 101 + src/engine/Context.hpp | 51 + src/engine/Driver.hpp | 98 + src/engine/DuplexPort.cpp | 108 + src/engine/DuplexPort.hpp | 68 + src/engine/Engine.cpp | 317 ++ src/engine/Engine.hpp | 130 + src/engine/EngineStore.cpp | 182 ++ src/engine/EngineStore.hpp | 68 + src/engine/Event.cpp | 49 + src/engine/Event.hpp | 75 + src/engine/EventBuffer.cpp | 318 ++ src/engine/EventBuffer.hpp | 104 + src/engine/EventSink.cpp | 74 + src/engine/EventSink.hpp | 64 + src/engine/EventSource.hpp | 60 + src/engine/GraphObjectImpl.cpp | 39 + src/engine/GraphObjectImpl.hpp | 132 + src/engine/HTTPEngineReceiver.cpp | 207 ++ src/engine/HTTPEngineReceiver.hpp | 59 + src/engine/InputPort.cpp | 297 ++ src/engine/InputPort.hpp | 88 + src/engine/InternalPlugin.cpp | 55 + src/engine/InternalPlugin.hpp | 74 + src/engine/JackAudioDriver.cpp | 384 +++ src/engine/JackAudioDriver.hpp | 185 ++ src/engine/JackMidiDriver.cpp | 267 ++ src/engine/JackMidiDriver.hpp | 113 + src/engine/LADSPANode.cpp | 373 +++ src/engine/LADSPANode.hpp | 74 + src/engine/LADSPAPlugin.cpp | 79 + src/engine/LADSPAPlugin.hpp | 78 + src/engine/LV2Info.cpp | 70 + src/engine/LV2Info.hpp | 66 + src/engine/LV2Node.cpp | 305 ++ src/engine/LV2Node.hpp | 75 + src/engine/LV2Plugin.cpp | 94 + src/engine/LV2Plugin.hpp | 84 + src/engine/Makefile.am | 205 ++ src/engine/MessageContext.cpp | 32 + src/engine/MessageContext.hpp | 51 + src/engine/MidiControlNode.cpp | 141 + src/engine/MidiControlNode.hpp | 65 + src/engine/MidiDriver.hpp | 100 + src/engine/MidiNoteNode.cpp | 390 +++ src/engine/MidiNoteNode.hpp | 88 + src/engine/MidiTriggerNode.cpp | 135 + src/engine/MidiTriggerNode.hpp | 61 + src/engine/NodeBase.cpp | 230 ++ src/engine/NodeBase.hpp | 132 + src/engine/NodeFactory.cpp | 285 ++ src/engine/NodeFactory.hpp | 92 + src/engine/NodeImpl.hpp | 170 + src/engine/OSCClientSender.cpp | 358 +++ src/engine/OSCClientSender.hpp | 136 + src/engine/OSCDriver.hpp | 82 + src/engine/OSCEngineReceiver.cpp | 884 ++++++ src/engine/OSCEngineReceiver.hpp | 129 + src/engine/ObjectSender.cpp | 150 + src/engine/ObjectSender.hpp | 57 + src/engine/OmInProcess.cpp | 66 + src/engine/OutputPort.cpp | 62 + src/engine/OutputPort.hpp | 63 + src/engine/PatchImpl.cpp | 481 +++ src/engine/PatchImpl.hpp | 168 + src/engine/PatchPlugin.hpp | 64 + src/engine/PluginImpl.cpp | 54 + src/engine/PluginImpl.hpp | 106 + src/engine/PortImpl.cpp | 190 ++ src/engine/PortImpl.hpp | 147 + src/engine/PostProcessor.cpp | 73 + src/engine/PostProcessor.hpp | 73 + src/engine/ProcessContext.hpp | 71 + src/engine/ProcessSlave.cpp | 75 + src/engine/ProcessSlave.hpp | 100 + src/engine/QueuedEngineInterface.cpp | 370 +++ src/engine/QueuedEngineInterface.hpp | 170 + src/engine/QueuedEvent.cpp | 50 + src/engine/QueuedEvent.hpp | 83 + src/engine/QueuedEventSource.cpp | 183 ++ src/engine/QueuedEventSource.hpp | 128 + src/engine/Responder.hpp | 71 + src/engine/ThreadManager.hpp | 43 + src/engine/TransportNode.cpp | 154 + src/engine/TransportNode.hpp | 45 + src/engine/engine.cpp | 56 + src/engine/engine.hpp | 42 + src/engine/events.hpp | 53 + src/engine/events/AllNotesOffEvent.cpp | 71 + src/engine/events/AllNotesOffEvent.hpp | 51 + src/engine/events/ClearPatchEvent.cpp | 133 + src/engine/events/ClearPatchEvent.hpp | 65 + src/engine/events/ConnectionEvent.cpp | 202 ++ src/engine/events/ConnectionEvent.hpp | 92 + src/engine/events/CreateNodeEvent.cpp | 151 + src/engine/events/CreateNodeEvent.hpp | 81 + src/engine/events/CreatePatchEvent.cpp | 157 + src/engine/events/CreatePatchEvent.hpp | 64 + src/engine/events/CreatePortEvent.cpp | 173 + src/engine/events/CreatePortEvent.hpp | 72 + src/engine/events/DeactivateEvent.cpp | 54 + src/engine/events/DeactivateEvent.hpp | 43 + src/engine/events/DestroyEvent.cpp | 201 ++ src/engine/events/DestroyEvent.hpp | 77 + src/engine/events/DisablePortMonitoringEvent.cpp | 87 + src/engine/events/DisablePortMonitoringEvent.hpp | 58 + src/engine/events/DisconnectAllEvent.cpp | 183 ++ src/engine/events/DisconnectAllEvent.hpp | 79 + src/engine/events/DisconnectionEvent.cpp | 213 ++ src/engine/events/DisconnectionEvent.hpp | 90 + src/engine/events/EnablePatchEvent.cpp | 86 + src/engine/events/EnablePatchEvent.hpp | 63 + src/engine/events/LoadPluginsEvent.cpp | 56 + src/engine/events/LoadPluginsEvent.hpp | 46 + src/engine/events/Makefile.am | 63 + src/engine/events/MidiLearnEvent.cpp | 89 + src/engine/events/MidiLearnEvent.hpp | 85 + src/engine/events/NoteEvent.cpp | 102 + src/engine/events/NoteEvent.hpp | 68 + src/engine/events/PingQueuedEvent.hpp | 48 + src/engine/events/RegisterClientEvent.cpp | 55 + src/engine/events/RegisterClientEvent.hpp | 55 + src/engine/events/RenameEvent.cpp | 152 + src/engine/events/RenameEvent.hpp | 64 + src/engine/events/RequestAllObjectsEvent.cpp | 59 + src/engine/events/RequestAllObjectsEvent.hpp | 48 + src/engine/events/RequestMetadataEvent.cpp | 85 + src/engine/events/RequestMetadataEvent.hpp | 62 + src/engine/events/RequestObjectEvent.cpp | 98 + src/engine/events/RequestObjectEvent.hpp | 55 + src/engine/events/RequestPluginEvent.cpp | 79 + src/engine/events/RequestPluginEvent.hpp | 53 + src/engine/events/RequestPluginsEvent.cpp | 57 + src/engine/events/RequestPluginsEvent.hpp | 47 + src/engine/events/RequestPortValueEvent.cpp | 81 + src/engine/events/RequestPortValueEvent.hpp | 56 + src/engine/events/SendPortActivityEvent.cpp | 34 + src/engine/events/SendPortActivityEvent.hpp | 67 + src/engine/events/SendPortValueEvent.cpp | 43 + src/engine/events/SendPortValueEvent.hpp | 78 + src/engine/events/SetMetadataEvent.cpp | 114 + src/engine/events/SetMetadataEvent.hpp | 65 + src/engine/events/SetPolyphonicEvent.cpp | 82 + src/engine/events/SetPolyphonicEvent.hpp | 56 + src/engine/events/SetPolyphonyEvent.cpp | 79 + src/engine/events/SetPolyphonyEvent.hpp | 56 + src/engine/events/SetPortValueEvent.cpp | 224 ++ src/engine/events/SetPortValueEvent.hpp | 80 + src/engine/events/UnregisterClientEvent.cpp | 45 + src/engine/events/UnregisterClientEvent.hpp | 54 + src/engine/jack_compat.h | 56 + src/engine/lv2_contexts.h | 1 + src/engine/tuning.hpp | 39 + src/engine/types.hpp | 33 + src/engine/util.hpp | 78 + src/engine/wscript | 73 + src/gui/App.cpp | 440 +++ src/gui/App.hpp | 173 + src/gui/BreadCrumb.hpp | 81 + src/gui/BreadCrumbBox.cpp | 210 ++ src/gui/BreadCrumbBox.hpp | 70 + src/gui/Configuration.cpp | 109 + src/gui/Configuration.hpp | 80 + src/gui/ConnectWindow.cpp | 458 +++ src/gui/ConnectWindow.hpp | 105 + src/gui/Connection.hpp | 60 + src/gui/ControlPanel.cpp | 269 ++ src/gui/ControlPanel.hpp | 91 + src/gui/Controls.cpp | 467 +++ src/gui/Controls.hpp | 164 + src/gui/GladeFactory.cpp | 76 + src/gui/GladeFactory.hpp | 47 + src/gui/LoadPatchWindow.cpp | 153 + src/gui/LoadPatchWindow.hpp | 82 + src/gui/LoadPluginWindow.cpp | 464 +++ src/gui/LoadPluginWindow.hpp | 155 + src/gui/LoadRemotePatchWindow.cpp | 164 + src/gui/LoadRemotePatchWindow.hpp | 94 + src/gui/LoadSubpatchWindow.cpp | 187 ++ src/gui/LoadSubpatchWindow.hpp | 80 + src/gui/Makefile.am | 125 + src/gui/MessagesWindow.cpp | 67 + src/gui/MessagesWindow.hpp | 58 + src/gui/NewSubpatchWindow.cpp | 113 + src/gui/NewSubpatchWindow.hpp | 68 + src/gui/NodeControlWindow.cpp | 139 + src/gui/NodeControlWindow.hpp | 76 + src/gui/NodeMenu.cpp | 169 + src/gui/NodeMenu.hpp | 72 + src/gui/NodeModule.cpp | 386 +++ src/gui/NodeModule.hpp | 102 + src/gui/NodePropertiesWindow.cpp | 66 + src/gui/NodePropertiesWindow.hpp | 58 + src/gui/ObjectMenu.cpp | 112 + src/gui/ObjectMenu.hpp | 69 + src/gui/PatchCanvas.cpp | 773 +++++ src/gui/PatchCanvas.hpp | 155 + src/gui/PatchPortModule.cpp | 158 + src/gui/PatchPortModule.hpp | 79 + src/gui/PatchPropertiesWindow.cpp | 92 + src/gui/PatchPropertiesWindow.hpp | 64 + src/gui/PatchTreeWindow.cpp | 243 ++ src/gui/PatchTreeWindow.hpp | 111 + src/gui/PatchView.cpp | 194 ++ src/gui/PatchView.hpp | 105 + src/gui/PatchWindow.cpp | 556 ++++ src/gui/PatchWindow.hpp | 155 + src/gui/Port.cpp | 151 + src/gui/Port.hpp | 70 + src/gui/PortMenu.cpp | 69 + src/gui/PortMenu.hpp | 55 + src/gui/PortPropertiesWindow.cpp | 156 + src/gui/PortPropertiesWindow.hpp | 68 + src/gui/RenameWindow.cpp | 119 + src/gui/RenameWindow.hpp | 60 + src/gui/SubpatchModule.cpp | 95 + src/gui/SubpatchModule.hpp | 71 + src/gui/ThreadedLoader.cpp | 155 + src/gui/ThreadedLoader.hpp | 92 + src/gui/UploadPatchWindow.cpp | 282 ++ src/gui/UploadPatchWindow.hpp | 104 + src/gui/WindowFactory.cpp | 381 +++ src/gui/WindowFactory.hpp | 109 + src/gui/cmdline.h | 86 + src/gui/gui.cpp | 35 + src/gui/gui.hpp | 45 + src/gui/ingen-icon.svg | 54 + src/gui/ingen.svg | 54 + src/gui/ingen_gui.glade | 3337 ++++++++++++++++++++ src/gui/ingen_gui.gladep | 9 + src/gui/wscript | 65 + src/ingen/Makefile.am | 30 + src/ingen/cmdline.c | 665 ++++ src/ingen/cmdline.ggo | 36 + src/ingen/cmdline.h | 200 ++ src/ingen/ingen.dbg | 5 + src/ingen/ingen.desktop.in | 9 + src/ingen/ingen.dev | 5 + src/ingen/ingen.grind | 5 + src/ingen/main.cpp | 259 ++ src/ingen/wscript | 11 + src/libs/client/ClientStore.cpp | 648 ---- src/libs/client/ClientStore.hpp | 151 - src/libs/client/ConnectionModel.hpp | 76 - src/libs/client/DeprecatedLoader.cpp | 711 ----- src/libs/client/DeprecatedLoader.hpp | 94 - src/libs/client/HTTPClientReceiver.cpp | 97 - src/libs/client/HTTPClientReceiver.hpp | 62 - src/libs/client/HTTPEngineSender.cpp | 285 -- src/libs/client/HTTPEngineSender.hpp | 155 - src/libs/client/Makefile.am | 69 - src/libs/client/NodeModel.cpp | 224 -- src/libs/client/NodeModel.hpp | 101 - src/libs/client/OSCClientReceiver.cpp | 394 --- src/libs/client/OSCClientReceiver.hpp | 107 - src/libs/client/OSCEngineSender.cpp | 420 --- src/libs/client/OSCEngineSender.hpp | 154 - src/libs/client/ObjectModel.cpp | 148 - src/libs/client/ObjectModel.hpp | 115 - src/libs/client/PatchModel.cpp | 190 -- src/libs/client/PatchModel.hpp | 105 - src/libs/client/PluginModel.cpp | 143 - src/libs/client/PluginModel.hpp | 142 - src/libs/client/PluginUI.cpp | 157 - src/libs/client/PluginUI.hpp | 65 - src/libs/client/PortModel.cpp | 66 - src/libs/client/PortModel.hpp | 112 - src/libs/client/SigClientInterface.hpp | 156 - src/libs/client/ThreadedSigClientInterface.cpp | 78 - src/libs/client/ThreadedSigClientInterface.hpp | 184 -- src/libs/client/client.cpp | 57 - src/libs/client/client.hpp | 43 - src/libs/client/wscript | 35 - src/libs/engine/AudioBuffer.cpp | 299 -- src/libs/engine/AudioBuffer.hpp | 84 - src/libs/engine/AudioDriver.hpp | 62 - src/libs/engine/Buffer.cpp | 38 - src/libs/engine/Buffer.hpp | 76 - src/libs/engine/ClientBroadcaster.cpp | 271 -- src/libs/engine/ClientBroadcaster.hpp | 97 - src/libs/engine/CompiledPatch.hpp | 83 - src/libs/engine/ConnectionImpl.cpp | 185 -- src/libs/engine/ConnectionImpl.hpp | 101 - src/libs/engine/Context.hpp | 51 - src/libs/engine/Driver.hpp | 98 - src/libs/engine/DuplexPort.cpp | 108 - src/libs/engine/DuplexPort.hpp | 68 - src/libs/engine/Engine.cpp | 317 -- src/libs/engine/Engine.hpp | 130 - src/libs/engine/EngineStore.cpp | 182 -- src/libs/engine/EngineStore.hpp | 68 - src/libs/engine/Event.cpp | 49 - src/libs/engine/Event.hpp | 75 - src/libs/engine/EventBuffer.cpp | 318 -- src/libs/engine/EventBuffer.hpp | 104 - src/libs/engine/EventSink.cpp | 74 - src/libs/engine/EventSink.hpp | 64 - src/libs/engine/EventSource.hpp | 60 - src/libs/engine/GraphObjectImpl.cpp | 39 - src/libs/engine/GraphObjectImpl.hpp | 132 - src/libs/engine/HTTPEngineReceiver.cpp | 207 -- src/libs/engine/HTTPEngineReceiver.hpp | 59 - src/libs/engine/InputPort.cpp | 297 -- src/libs/engine/InputPort.hpp | 88 - src/libs/engine/InternalPlugin.cpp | 55 - src/libs/engine/InternalPlugin.hpp | 74 - src/libs/engine/JackAudioDriver.cpp | 384 --- src/libs/engine/JackAudioDriver.hpp | 185 -- src/libs/engine/JackMidiDriver.cpp | 267 -- src/libs/engine/JackMidiDriver.hpp | 113 - src/libs/engine/LADSPANode.cpp | 373 --- src/libs/engine/LADSPANode.hpp | 74 - src/libs/engine/LADSPAPlugin.cpp | 79 - src/libs/engine/LADSPAPlugin.hpp | 78 - src/libs/engine/LV2Info.cpp | 70 - src/libs/engine/LV2Info.hpp | 66 - src/libs/engine/LV2Node.cpp | 305 -- src/libs/engine/LV2Node.hpp | 75 - src/libs/engine/LV2Plugin.cpp | 94 - src/libs/engine/LV2Plugin.hpp | 84 - src/libs/engine/Makefile.am | 205 -- src/libs/engine/MessageContext.cpp | 32 - src/libs/engine/MessageContext.hpp | 51 - src/libs/engine/MidiControlNode.cpp | 141 - src/libs/engine/MidiControlNode.hpp | 65 - src/libs/engine/MidiDriver.hpp | 100 - src/libs/engine/MidiNoteNode.cpp | 390 --- src/libs/engine/MidiNoteNode.hpp | 88 - src/libs/engine/MidiTriggerNode.cpp | 135 - src/libs/engine/MidiTriggerNode.hpp | 61 - src/libs/engine/NodeBase.cpp | 230 -- src/libs/engine/NodeBase.hpp | 132 - src/libs/engine/NodeFactory.cpp | 285 -- src/libs/engine/NodeFactory.hpp | 92 - src/libs/engine/NodeImpl.hpp | 170 - src/libs/engine/OSCClientSender.cpp | 358 --- src/libs/engine/OSCClientSender.hpp | 136 - src/libs/engine/OSCDriver.hpp | 82 - src/libs/engine/OSCEngineReceiver.cpp | 884 ------ src/libs/engine/OSCEngineReceiver.hpp | 129 - src/libs/engine/ObjectSender.cpp | 150 - src/libs/engine/ObjectSender.hpp | 57 - src/libs/engine/OmInProcess.cpp | 66 - src/libs/engine/OutputPort.cpp | 62 - src/libs/engine/OutputPort.hpp | 63 - src/libs/engine/PatchImpl.cpp | 481 --- src/libs/engine/PatchImpl.hpp | 168 - src/libs/engine/PatchPlugin.hpp | 64 - src/libs/engine/PluginImpl.cpp | 54 - src/libs/engine/PluginImpl.hpp | 106 - src/libs/engine/PortImpl.cpp | 190 -- src/libs/engine/PortImpl.hpp | 147 - src/libs/engine/PostProcessor.cpp | 73 - src/libs/engine/PostProcessor.hpp | 73 - src/libs/engine/ProcessContext.hpp | 71 - src/libs/engine/ProcessSlave.cpp | 75 - src/libs/engine/ProcessSlave.hpp | 100 - src/libs/engine/QueuedEngineInterface.cpp | 370 --- src/libs/engine/QueuedEngineInterface.hpp | 170 - src/libs/engine/QueuedEvent.cpp | 50 - src/libs/engine/QueuedEvent.hpp | 83 - src/libs/engine/QueuedEventSource.cpp | 183 -- src/libs/engine/QueuedEventSource.hpp | 128 - src/libs/engine/Responder.hpp | 71 - src/libs/engine/ThreadManager.hpp | 43 - src/libs/engine/TransportNode.cpp | 154 - src/libs/engine/TransportNode.hpp | 45 - src/libs/engine/engine.cpp | 56 - src/libs/engine/engine.hpp | 42 - src/libs/engine/events.hpp | 53 - src/libs/engine/events/AllNotesOffEvent.cpp | 71 - src/libs/engine/events/AllNotesOffEvent.hpp | 51 - src/libs/engine/events/ClearPatchEvent.cpp | 133 - src/libs/engine/events/ClearPatchEvent.hpp | 65 - src/libs/engine/events/ConnectionEvent.cpp | 202 -- src/libs/engine/events/ConnectionEvent.hpp | 92 - src/libs/engine/events/CreateNodeEvent.cpp | 151 - src/libs/engine/events/CreateNodeEvent.hpp | 81 - src/libs/engine/events/CreatePatchEvent.cpp | 157 - src/libs/engine/events/CreatePatchEvent.hpp | 64 - src/libs/engine/events/CreatePortEvent.cpp | 173 - src/libs/engine/events/CreatePortEvent.hpp | 72 - src/libs/engine/events/DeactivateEvent.cpp | 54 - src/libs/engine/events/DeactivateEvent.hpp | 43 - src/libs/engine/events/DestroyEvent.cpp | 201 -- src/libs/engine/events/DestroyEvent.hpp | 77 - .../engine/events/DisablePortMonitoringEvent.cpp | 87 - .../engine/events/DisablePortMonitoringEvent.hpp | 58 - src/libs/engine/events/DisconnectAllEvent.cpp | 183 -- src/libs/engine/events/DisconnectAllEvent.hpp | 79 - src/libs/engine/events/DisconnectionEvent.cpp | 213 -- src/libs/engine/events/DisconnectionEvent.hpp | 90 - src/libs/engine/events/EnablePatchEvent.cpp | 86 - src/libs/engine/events/EnablePatchEvent.hpp | 63 - src/libs/engine/events/LoadPluginsEvent.cpp | 56 - src/libs/engine/events/LoadPluginsEvent.hpp | 46 - src/libs/engine/events/Makefile.am | 63 - src/libs/engine/events/MidiLearnEvent.cpp | 89 - src/libs/engine/events/MidiLearnEvent.hpp | 85 - src/libs/engine/events/NoteEvent.cpp | 102 - src/libs/engine/events/NoteEvent.hpp | 68 - src/libs/engine/events/PingQueuedEvent.hpp | 48 - src/libs/engine/events/RegisterClientEvent.cpp | 55 - src/libs/engine/events/RegisterClientEvent.hpp | 55 - src/libs/engine/events/RenameEvent.cpp | 152 - src/libs/engine/events/RenameEvent.hpp | 64 - src/libs/engine/events/RequestAllObjectsEvent.cpp | 59 - src/libs/engine/events/RequestAllObjectsEvent.hpp | 48 - src/libs/engine/events/RequestMetadataEvent.cpp | 85 - src/libs/engine/events/RequestMetadataEvent.hpp | 62 - src/libs/engine/events/RequestObjectEvent.cpp | 98 - src/libs/engine/events/RequestObjectEvent.hpp | 55 - src/libs/engine/events/RequestPluginEvent.cpp | 79 - src/libs/engine/events/RequestPluginEvent.hpp | 53 - src/libs/engine/events/RequestPluginsEvent.cpp | 57 - src/libs/engine/events/RequestPluginsEvent.hpp | 47 - src/libs/engine/events/RequestPortValueEvent.cpp | 81 - src/libs/engine/events/RequestPortValueEvent.hpp | 56 - src/libs/engine/events/SendPortActivityEvent.cpp | 34 - src/libs/engine/events/SendPortActivityEvent.hpp | 67 - src/libs/engine/events/SendPortValueEvent.cpp | 43 - src/libs/engine/events/SendPortValueEvent.hpp | 78 - src/libs/engine/events/SetMetadataEvent.cpp | 114 - src/libs/engine/events/SetMetadataEvent.hpp | 65 - src/libs/engine/events/SetPolyphonicEvent.cpp | 82 - src/libs/engine/events/SetPolyphonicEvent.hpp | 56 - src/libs/engine/events/SetPolyphonyEvent.cpp | 79 - src/libs/engine/events/SetPolyphonyEvent.hpp | 56 - src/libs/engine/events/SetPortValueEvent.cpp | 224 -- src/libs/engine/events/SetPortValueEvent.hpp | 80 - src/libs/engine/events/UnregisterClientEvent.cpp | 45 - src/libs/engine/events/UnregisterClientEvent.hpp | 54 - src/libs/engine/jack_compat.h | 56 - src/libs/engine/lv2_contexts.h | 1 - src/libs/engine/tuning.hpp | 39 - src/libs/engine/types.hpp | 33 - src/libs/engine/util.hpp | 78 - src/libs/engine/wscript | 73 - src/libs/gui/App.cpp | 440 --- src/libs/gui/App.hpp | 173 - src/libs/gui/BreadCrumb.hpp | 81 - src/libs/gui/BreadCrumbBox.cpp | 210 -- src/libs/gui/BreadCrumbBox.hpp | 70 - src/libs/gui/Configuration.cpp | 109 - src/libs/gui/Configuration.hpp | 80 - src/libs/gui/ConnectWindow.cpp | 458 --- src/libs/gui/ConnectWindow.hpp | 105 - src/libs/gui/Connection.hpp | 60 - src/libs/gui/ControlPanel.cpp | 269 -- src/libs/gui/ControlPanel.hpp | 91 - src/libs/gui/Controls.cpp | 467 --- src/libs/gui/Controls.hpp | 164 - src/libs/gui/GladeFactory.cpp | 76 - src/libs/gui/GladeFactory.hpp | 47 - src/libs/gui/LoadPatchWindow.cpp | 153 - src/libs/gui/LoadPatchWindow.hpp | 82 - src/libs/gui/LoadPluginWindow.cpp | 464 --- src/libs/gui/LoadPluginWindow.hpp | 155 - src/libs/gui/LoadRemotePatchWindow.cpp | 164 - src/libs/gui/LoadRemotePatchWindow.hpp | 94 - src/libs/gui/LoadSubpatchWindow.cpp | 187 -- src/libs/gui/LoadSubpatchWindow.hpp | 80 - src/libs/gui/Makefile.am | 125 - src/libs/gui/MessagesWindow.cpp | 67 - src/libs/gui/MessagesWindow.hpp | 58 - src/libs/gui/NewSubpatchWindow.cpp | 113 - src/libs/gui/NewSubpatchWindow.hpp | 68 - src/libs/gui/NodeControlWindow.cpp | 139 - src/libs/gui/NodeControlWindow.hpp | 76 - src/libs/gui/NodeMenu.cpp | 169 - src/libs/gui/NodeMenu.hpp | 72 - src/libs/gui/NodeModule.cpp | 386 --- src/libs/gui/NodeModule.hpp | 102 - src/libs/gui/NodePropertiesWindow.cpp | 66 - src/libs/gui/NodePropertiesWindow.hpp | 58 - src/libs/gui/ObjectMenu.cpp | 112 - src/libs/gui/ObjectMenu.hpp | 69 - src/libs/gui/PatchCanvas.cpp | 773 ----- src/libs/gui/PatchCanvas.hpp | 155 - src/libs/gui/PatchPortModule.cpp | 158 - src/libs/gui/PatchPortModule.hpp | 79 - src/libs/gui/PatchPropertiesWindow.cpp | 92 - src/libs/gui/PatchPropertiesWindow.hpp | 64 - src/libs/gui/PatchTreeWindow.cpp | 243 -- src/libs/gui/PatchTreeWindow.hpp | 111 - src/libs/gui/PatchView.cpp | 194 -- src/libs/gui/PatchView.hpp | 105 - src/libs/gui/PatchWindow.cpp | 556 ---- src/libs/gui/PatchWindow.hpp | 155 - src/libs/gui/Port.cpp | 151 - src/libs/gui/Port.hpp | 70 - src/libs/gui/PortMenu.cpp | 69 - src/libs/gui/PortMenu.hpp | 55 - src/libs/gui/PortPropertiesWindow.cpp | 156 - src/libs/gui/PortPropertiesWindow.hpp | 68 - src/libs/gui/RenameWindow.cpp | 119 - src/libs/gui/RenameWindow.hpp | 60 - src/libs/gui/SubpatchModule.cpp | 95 - src/libs/gui/SubpatchModule.hpp | 71 - src/libs/gui/ThreadedLoader.cpp | 155 - src/libs/gui/ThreadedLoader.hpp | 92 - src/libs/gui/UploadPatchWindow.cpp | 282 -- src/libs/gui/UploadPatchWindow.hpp | 104 - src/libs/gui/WindowFactory.cpp | 381 --- src/libs/gui/WindowFactory.hpp | 109 - src/libs/gui/cmdline.h | 86 - src/libs/gui/gui.cpp | 35 - src/libs/gui/gui.hpp | 45 - src/libs/gui/ingen-icon.svg | 54 - src/libs/gui/ingen.svg | 54 - src/libs/gui/ingen_gui.glade | 3337 -------------------- src/libs/gui/ingen_gui.gladep | 9 - src/libs/gui/wscript | 65 - src/libs/module/Makefile.am | 22 - src/libs/module/Module.cpp | 90 - src/libs/module/Module.hpp | 35 - src/libs/module/World.hpp | 78 - src/libs/module/global.cpp | 74 - src/libs/module/global.hpp | 33 - src/libs/module/wscript | 17 - src/libs/serialisation/Makefile.am | 24 - src/libs/serialisation/Parser.cpp | 587 ---- src/libs/serialisation/Parser.hpp | 126 - src/libs/serialisation/Serialiser.cpp | 501 --- src/libs/serialisation/Serialiser.hpp | 109 - src/libs/serialisation/serialisation.cpp | 46 - src/libs/serialisation/serialisation.hpp | 44 - src/libs/serialisation/wscript | 16 - src/libs/shared/Builder.cpp | 98 - src/libs/shared/Builder.hpp | 57 - src/libs/shared/ClashAvoider.cpp | 214 -- src/libs/shared/ClashAvoider.hpp | 108 - src/libs/shared/LV2Features.cpp | 66 - src/libs/shared/LV2Features.hpp | 63 - src/libs/shared/LV2URIMap.cpp | 75 - src/libs/shared/LV2URIMap.hpp | 64 - src/libs/shared/Makefile.am | 22 - src/libs/shared/OSCSender.cpp | 125 - src/libs/shared/OSCSender.hpp | 59 - src/libs/shared/Store.cpp | 108 - src/libs/shared/Store.hpp | 60 - src/libs/shared/wscript | 19 - src/module/Makefile.am | 22 + src/module/Module.cpp | 90 + src/module/Module.hpp | 35 + src/module/World.hpp | 78 + src/module/global.cpp | 74 + src/module/global.hpp | 33 + src/module/wscript | 17 + src/progs/ingen/Makefile.am | 30 - src/progs/ingen/cmdline.c | 665 ---- src/progs/ingen/cmdline.ggo | 36 - src/progs/ingen/cmdline.h | 200 -- src/progs/ingen/ingen.dbg | 5 - src/progs/ingen/ingen.desktop.in | 9 - src/progs/ingen/ingen.dev | 5 - src/progs/ingen/ingen.grind | 5 - src/progs/ingen/main.cpp | 259 -- src/progs/ingen/wscript | 11 - src/progs/python/Makefile.am | 4 - src/progs/python/OSC.py | 373 --- src/progs/python/ingen.py | 636 ---- src/progs/python/ingenecho.py | 40 - src/progs/python/scripts/Makefile.am | 2 - src/progs/python/scripts/flatten.py | 232 -- src/progs/python/scripts/sillysinepatch.py | 41 - src/progs/supercollider/Ingen.sc | 746 ----- src/progs/supercollider/Makefile.am | 2 - src/progs/supercollider/README | 11 - src/progs/supercollider/example.sc | 27 - src/scripts/python/Makefile.am | 4 + src/scripts/python/OSC.py | 373 +++ src/scripts/python/ingen.py | 636 ++++ src/scripts/python/ingenecho.py | 40 + src/scripts/python/scripts/Makefile.am | 2 + src/scripts/python/scripts/flatten.py | 232 ++ src/scripts/python/scripts/sillysinepatch.py | 41 + src/scripts/supercollider/Ingen.sc | 746 +++++ src/scripts/supercollider/Makefile.am | 2 + src/scripts/supercollider/README | 11 + src/scripts/supercollider/example.sc | 27 + src/serialisation/Makefile.am | 24 + src/serialisation/Parser.cpp | 587 ++++ src/serialisation/Parser.hpp | 126 + src/serialisation/Serialiser.cpp | 501 +++ src/serialisation/Serialiser.hpp | 109 + src/serialisation/serialisation.cpp | 46 + src/serialisation/serialisation.hpp | 44 + src/serialisation/wscript | 16 + src/shared/Builder.cpp | 98 + src/shared/Builder.hpp | 57 + src/shared/ClashAvoider.cpp | 214 ++ src/shared/ClashAvoider.hpp | 108 + src/shared/LV2Features.cpp | 66 + src/shared/LV2Features.hpp | 63 + src/shared/LV2URIMap.cpp | 75 + src/shared/LV2URIMap.hpp | 64 + src/shared/Makefile.am | 22 + src/shared/OSCSender.cpp | 125 + src/shared/OSCSender.hpp | 59 + src/shared/Store.cpp | 108 + src/shared/Store.hpp | 60 + src/shared/wscript | 19 + 644 files changed, 45229 insertions(+), 45229 deletions(-) create mode 100644 src/client/ClientStore.cpp create mode 100644 src/client/ClientStore.hpp create mode 100644 src/client/ConnectionModel.hpp create mode 100644 src/client/DeprecatedLoader.cpp create mode 100644 src/client/DeprecatedLoader.hpp create mode 100644 src/client/HTTPClientReceiver.cpp create mode 100644 src/client/HTTPClientReceiver.hpp create mode 100644 src/client/HTTPEngineSender.cpp create mode 100644 src/client/HTTPEngineSender.hpp create mode 100644 src/client/Makefile.am create mode 100644 src/client/NodeModel.cpp create mode 100644 src/client/NodeModel.hpp create mode 100644 src/client/OSCClientReceiver.cpp create mode 100644 src/client/OSCClientReceiver.hpp create mode 100644 src/client/OSCEngineSender.cpp create mode 100644 src/client/OSCEngineSender.hpp create mode 100644 src/client/ObjectModel.cpp create mode 100644 src/client/ObjectModel.hpp create mode 100644 src/client/PatchModel.cpp create mode 100644 src/client/PatchModel.hpp create mode 100644 src/client/PluginModel.cpp create mode 100644 src/client/PluginModel.hpp create mode 100644 src/client/PluginUI.cpp create mode 100644 src/client/PluginUI.hpp create mode 100644 src/client/PortModel.cpp create mode 100644 src/client/PortModel.hpp create mode 100644 src/client/SigClientInterface.hpp create mode 100644 src/client/ThreadedSigClientInterface.cpp create mode 100644 src/client/ThreadedSigClientInterface.hpp create mode 100644 src/client/client.cpp create mode 100644 src/client/client.hpp create mode 100644 src/client/wscript create mode 100644 src/engine/AudioBuffer.cpp create mode 100644 src/engine/AudioBuffer.hpp create mode 100644 src/engine/AudioDriver.hpp create mode 100644 src/engine/Buffer.cpp create mode 100644 src/engine/Buffer.hpp create mode 100644 src/engine/ClientBroadcaster.cpp create mode 100644 src/engine/ClientBroadcaster.hpp create mode 100644 src/engine/CompiledPatch.hpp create mode 100644 src/engine/ConnectionImpl.cpp create mode 100644 src/engine/ConnectionImpl.hpp create mode 100644 src/engine/Context.hpp create mode 100644 src/engine/Driver.hpp create mode 100644 src/engine/DuplexPort.cpp create mode 100644 src/engine/DuplexPort.hpp create mode 100644 src/engine/Engine.cpp create mode 100644 src/engine/Engine.hpp create mode 100644 src/engine/EngineStore.cpp create mode 100644 src/engine/EngineStore.hpp create mode 100644 src/engine/Event.cpp create mode 100644 src/engine/Event.hpp create mode 100644 src/engine/EventBuffer.cpp create mode 100644 src/engine/EventBuffer.hpp create mode 100644 src/engine/EventSink.cpp create mode 100644 src/engine/EventSink.hpp create mode 100644 src/engine/EventSource.hpp create mode 100644 src/engine/GraphObjectImpl.cpp create mode 100644 src/engine/GraphObjectImpl.hpp create mode 100644 src/engine/HTTPEngineReceiver.cpp create mode 100644 src/engine/HTTPEngineReceiver.hpp create mode 100644 src/engine/InputPort.cpp create mode 100644 src/engine/InputPort.hpp create mode 100644 src/engine/InternalPlugin.cpp create mode 100644 src/engine/InternalPlugin.hpp create mode 100644 src/engine/JackAudioDriver.cpp create mode 100644 src/engine/JackAudioDriver.hpp create mode 100644 src/engine/JackMidiDriver.cpp create mode 100644 src/engine/JackMidiDriver.hpp create mode 100644 src/engine/LADSPANode.cpp create mode 100644 src/engine/LADSPANode.hpp create mode 100644 src/engine/LADSPAPlugin.cpp create mode 100644 src/engine/LADSPAPlugin.hpp create mode 100644 src/engine/LV2Info.cpp create mode 100644 src/engine/LV2Info.hpp create mode 100644 src/engine/LV2Node.cpp create mode 100644 src/engine/LV2Node.hpp create mode 100644 src/engine/LV2Plugin.cpp create mode 100644 src/engine/LV2Plugin.hpp create mode 100644 src/engine/Makefile.am create mode 100644 src/engine/MessageContext.cpp create mode 100644 src/engine/MessageContext.hpp create mode 100644 src/engine/MidiControlNode.cpp create mode 100644 src/engine/MidiControlNode.hpp create mode 100644 src/engine/MidiDriver.hpp create mode 100644 src/engine/MidiNoteNode.cpp create mode 100644 src/engine/MidiNoteNode.hpp create mode 100644 src/engine/MidiTriggerNode.cpp create mode 100644 src/engine/MidiTriggerNode.hpp create mode 100644 src/engine/NodeBase.cpp create mode 100644 src/engine/NodeBase.hpp create mode 100644 src/engine/NodeFactory.cpp create mode 100644 src/engine/NodeFactory.hpp create mode 100644 src/engine/NodeImpl.hpp create mode 100644 src/engine/OSCClientSender.cpp create mode 100644 src/engine/OSCClientSender.hpp create mode 100644 src/engine/OSCDriver.hpp create mode 100644 src/engine/OSCEngineReceiver.cpp create mode 100644 src/engine/OSCEngineReceiver.hpp create mode 100644 src/engine/ObjectSender.cpp create mode 100644 src/engine/ObjectSender.hpp create mode 100644 src/engine/OmInProcess.cpp create mode 100644 src/engine/OutputPort.cpp create mode 100644 src/engine/OutputPort.hpp create mode 100644 src/engine/PatchImpl.cpp create mode 100644 src/engine/PatchImpl.hpp create mode 100644 src/engine/PatchPlugin.hpp create mode 100644 src/engine/PluginImpl.cpp create mode 100644 src/engine/PluginImpl.hpp create mode 100644 src/engine/PortImpl.cpp create mode 100644 src/engine/PortImpl.hpp create mode 100644 src/engine/PostProcessor.cpp create mode 100644 src/engine/PostProcessor.hpp create mode 100644 src/engine/ProcessContext.hpp create mode 100644 src/engine/ProcessSlave.cpp create mode 100644 src/engine/ProcessSlave.hpp create mode 100644 src/engine/QueuedEngineInterface.cpp create mode 100644 src/engine/QueuedEngineInterface.hpp create mode 100644 src/engine/QueuedEvent.cpp create mode 100644 src/engine/QueuedEvent.hpp create mode 100644 src/engine/QueuedEventSource.cpp create mode 100644 src/engine/QueuedEventSource.hpp create mode 100644 src/engine/Responder.hpp create mode 100644 src/engine/ThreadManager.hpp create mode 100644 src/engine/TransportNode.cpp create mode 100644 src/engine/TransportNode.hpp create mode 100644 src/engine/engine.cpp create mode 100644 src/engine/engine.hpp create mode 100644 src/engine/events.hpp create mode 100644 src/engine/events/AllNotesOffEvent.cpp create mode 100644 src/engine/events/AllNotesOffEvent.hpp create mode 100644 src/engine/events/ClearPatchEvent.cpp create mode 100644 src/engine/events/ClearPatchEvent.hpp create mode 100644 src/engine/events/ConnectionEvent.cpp create mode 100644 src/engine/events/ConnectionEvent.hpp create mode 100644 src/engine/events/CreateNodeEvent.cpp create mode 100644 src/engine/events/CreateNodeEvent.hpp create mode 100644 src/engine/events/CreatePatchEvent.cpp create mode 100644 src/engine/events/CreatePatchEvent.hpp create mode 100644 src/engine/events/CreatePortEvent.cpp create mode 100644 src/engine/events/CreatePortEvent.hpp create mode 100644 src/engine/events/DeactivateEvent.cpp create mode 100644 src/engine/events/DeactivateEvent.hpp create mode 100644 src/engine/events/DestroyEvent.cpp create mode 100644 src/engine/events/DestroyEvent.hpp create mode 100644 src/engine/events/DisablePortMonitoringEvent.cpp create mode 100644 src/engine/events/DisablePortMonitoringEvent.hpp create mode 100644 src/engine/events/DisconnectAllEvent.cpp create mode 100644 src/engine/events/DisconnectAllEvent.hpp create mode 100644 src/engine/events/DisconnectionEvent.cpp create mode 100644 src/engine/events/DisconnectionEvent.hpp create mode 100644 src/engine/events/EnablePatchEvent.cpp create mode 100644 src/engine/events/EnablePatchEvent.hpp create mode 100644 src/engine/events/LoadPluginsEvent.cpp create mode 100644 src/engine/events/LoadPluginsEvent.hpp create mode 100644 src/engine/events/Makefile.am create mode 100644 src/engine/events/MidiLearnEvent.cpp create mode 100644 src/engine/events/MidiLearnEvent.hpp create mode 100644 src/engine/events/NoteEvent.cpp create mode 100644 src/engine/events/NoteEvent.hpp create mode 100644 src/engine/events/PingQueuedEvent.hpp create mode 100644 src/engine/events/RegisterClientEvent.cpp create mode 100644 src/engine/events/RegisterClientEvent.hpp create mode 100644 src/engine/events/RenameEvent.cpp create mode 100644 src/engine/events/RenameEvent.hpp create mode 100644 src/engine/events/RequestAllObjectsEvent.cpp create mode 100644 src/engine/events/RequestAllObjectsEvent.hpp create mode 100644 src/engine/events/RequestMetadataEvent.cpp create mode 100644 src/engine/events/RequestMetadataEvent.hpp create mode 100644 src/engine/events/RequestObjectEvent.cpp create mode 100644 src/engine/events/RequestObjectEvent.hpp create mode 100644 src/engine/events/RequestPluginEvent.cpp create mode 100644 src/engine/events/RequestPluginEvent.hpp create mode 100644 src/engine/events/RequestPluginsEvent.cpp create mode 100644 src/engine/events/RequestPluginsEvent.hpp create mode 100644 src/engine/events/RequestPortValueEvent.cpp create mode 100644 src/engine/events/RequestPortValueEvent.hpp create mode 100644 src/engine/events/SendPortActivityEvent.cpp create mode 100644 src/engine/events/SendPortActivityEvent.hpp create mode 100644 src/engine/events/SendPortValueEvent.cpp create mode 100644 src/engine/events/SendPortValueEvent.hpp create mode 100644 src/engine/events/SetMetadataEvent.cpp create mode 100644 src/engine/events/SetMetadataEvent.hpp create mode 100644 src/engine/events/SetPolyphonicEvent.cpp create mode 100644 src/engine/events/SetPolyphonicEvent.hpp create mode 100644 src/engine/events/SetPolyphonyEvent.cpp create mode 100644 src/engine/events/SetPolyphonyEvent.hpp create mode 100644 src/engine/events/SetPortValueEvent.cpp create mode 100644 src/engine/events/SetPortValueEvent.hpp create mode 100644 src/engine/events/UnregisterClientEvent.cpp create mode 100644 src/engine/events/UnregisterClientEvent.hpp create mode 100644 src/engine/jack_compat.h create mode 120000 src/engine/lv2_contexts.h create mode 100644 src/engine/tuning.hpp create mode 100644 src/engine/types.hpp create mode 100644 src/engine/util.hpp create mode 100644 src/engine/wscript create mode 100644 src/gui/App.cpp create mode 100644 src/gui/App.hpp create mode 100644 src/gui/BreadCrumb.hpp create mode 100644 src/gui/BreadCrumbBox.cpp create mode 100644 src/gui/BreadCrumbBox.hpp create mode 100644 src/gui/Configuration.cpp create mode 100644 src/gui/Configuration.hpp create mode 100644 src/gui/ConnectWindow.cpp create mode 100644 src/gui/ConnectWindow.hpp create mode 100644 src/gui/Connection.hpp create mode 100644 src/gui/ControlPanel.cpp create mode 100644 src/gui/ControlPanel.hpp create mode 100644 src/gui/Controls.cpp create mode 100644 src/gui/Controls.hpp create mode 100644 src/gui/GladeFactory.cpp create mode 100644 src/gui/GladeFactory.hpp create mode 100644 src/gui/LoadPatchWindow.cpp create mode 100644 src/gui/LoadPatchWindow.hpp create mode 100644 src/gui/LoadPluginWindow.cpp create mode 100644 src/gui/LoadPluginWindow.hpp create mode 100644 src/gui/LoadRemotePatchWindow.cpp create mode 100644 src/gui/LoadRemotePatchWindow.hpp create mode 100644 src/gui/LoadSubpatchWindow.cpp create mode 100644 src/gui/LoadSubpatchWindow.hpp create mode 100644 src/gui/Makefile.am create mode 100644 src/gui/MessagesWindow.cpp create mode 100644 src/gui/MessagesWindow.hpp create mode 100644 src/gui/NewSubpatchWindow.cpp create mode 100644 src/gui/NewSubpatchWindow.hpp create mode 100644 src/gui/NodeControlWindow.cpp create mode 100644 src/gui/NodeControlWindow.hpp create mode 100644 src/gui/NodeMenu.cpp create mode 100644 src/gui/NodeMenu.hpp create mode 100644 src/gui/NodeModule.cpp create mode 100644 src/gui/NodeModule.hpp create mode 100644 src/gui/NodePropertiesWindow.cpp create mode 100644 src/gui/NodePropertiesWindow.hpp create mode 100644 src/gui/ObjectMenu.cpp create mode 100644 src/gui/ObjectMenu.hpp create mode 100644 src/gui/PatchCanvas.cpp create mode 100644 src/gui/PatchCanvas.hpp create mode 100644 src/gui/PatchPortModule.cpp create mode 100644 src/gui/PatchPortModule.hpp create mode 100644 src/gui/PatchPropertiesWindow.cpp create mode 100644 src/gui/PatchPropertiesWindow.hpp create mode 100644 src/gui/PatchTreeWindow.cpp create mode 100644 src/gui/PatchTreeWindow.hpp create mode 100644 src/gui/PatchView.cpp create mode 100644 src/gui/PatchView.hpp create mode 100644 src/gui/PatchWindow.cpp create mode 100644 src/gui/PatchWindow.hpp create mode 100644 src/gui/Port.cpp create mode 100644 src/gui/Port.hpp create mode 100644 src/gui/PortMenu.cpp create mode 100644 src/gui/PortMenu.hpp create mode 100644 src/gui/PortPropertiesWindow.cpp create mode 100644 src/gui/PortPropertiesWindow.hpp create mode 100644 src/gui/RenameWindow.cpp create mode 100644 src/gui/RenameWindow.hpp create mode 100644 src/gui/SubpatchModule.cpp create mode 100644 src/gui/SubpatchModule.hpp create mode 100644 src/gui/ThreadedLoader.cpp create mode 100644 src/gui/ThreadedLoader.hpp create mode 100644 src/gui/UploadPatchWindow.cpp create mode 100644 src/gui/UploadPatchWindow.hpp create mode 100644 src/gui/WindowFactory.cpp create mode 100644 src/gui/WindowFactory.hpp create mode 100644 src/gui/cmdline.h create mode 100644 src/gui/gui.cpp create mode 100644 src/gui/gui.hpp create mode 100644 src/gui/ingen-icon.svg create mode 100644 src/gui/ingen.svg create mode 100644 src/gui/ingen_gui.glade create mode 100644 src/gui/ingen_gui.gladep create mode 100644 src/gui/wscript create mode 100644 src/ingen/Makefile.am create mode 100644 src/ingen/cmdline.c create mode 100644 src/ingen/cmdline.ggo create mode 100644 src/ingen/cmdline.h create mode 100755 src/ingen/ingen.dbg create mode 100644 src/ingen/ingen.desktop.in create mode 100755 src/ingen/ingen.dev create mode 100644 src/ingen/ingen.grind create mode 100644 src/ingen/main.cpp create mode 100644 src/ingen/wscript delete mode 100644 src/libs/client/ClientStore.cpp delete mode 100644 src/libs/client/ClientStore.hpp delete mode 100644 src/libs/client/ConnectionModel.hpp delete mode 100644 src/libs/client/DeprecatedLoader.cpp delete mode 100644 src/libs/client/DeprecatedLoader.hpp delete mode 100644 src/libs/client/HTTPClientReceiver.cpp delete mode 100644 src/libs/client/HTTPClientReceiver.hpp delete mode 100644 src/libs/client/HTTPEngineSender.cpp delete mode 100644 src/libs/client/HTTPEngineSender.hpp delete mode 100644 src/libs/client/Makefile.am delete mode 100644 src/libs/client/NodeModel.cpp delete mode 100644 src/libs/client/NodeModel.hpp delete mode 100644 src/libs/client/OSCClientReceiver.cpp delete mode 100644 src/libs/client/OSCClientReceiver.hpp delete mode 100644 src/libs/client/OSCEngineSender.cpp delete mode 100644 src/libs/client/OSCEngineSender.hpp delete mode 100644 src/libs/client/ObjectModel.cpp delete mode 100644 src/libs/client/ObjectModel.hpp delete mode 100644 src/libs/client/PatchModel.cpp delete mode 100644 src/libs/client/PatchModel.hpp delete mode 100644 src/libs/client/PluginModel.cpp delete mode 100644 src/libs/client/PluginModel.hpp delete mode 100644 src/libs/client/PluginUI.cpp delete mode 100644 src/libs/client/PluginUI.hpp delete mode 100644 src/libs/client/PortModel.cpp delete mode 100644 src/libs/client/PortModel.hpp delete mode 100644 src/libs/client/SigClientInterface.hpp delete mode 100644 src/libs/client/ThreadedSigClientInterface.cpp delete mode 100644 src/libs/client/ThreadedSigClientInterface.hpp delete mode 100644 src/libs/client/client.cpp delete mode 100644 src/libs/client/client.hpp delete mode 100644 src/libs/client/wscript delete mode 100644 src/libs/engine/AudioBuffer.cpp delete mode 100644 src/libs/engine/AudioBuffer.hpp delete mode 100644 src/libs/engine/AudioDriver.hpp delete mode 100644 src/libs/engine/Buffer.cpp delete mode 100644 src/libs/engine/Buffer.hpp delete mode 100644 src/libs/engine/ClientBroadcaster.cpp delete mode 100644 src/libs/engine/ClientBroadcaster.hpp delete mode 100644 src/libs/engine/CompiledPatch.hpp delete mode 100644 src/libs/engine/ConnectionImpl.cpp delete mode 100644 src/libs/engine/ConnectionImpl.hpp delete mode 100644 src/libs/engine/Context.hpp delete mode 100644 src/libs/engine/Driver.hpp delete mode 100644 src/libs/engine/DuplexPort.cpp delete mode 100644 src/libs/engine/DuplexPort.hpp delete mode 100644 src/libs/engine/Engine.cpp delete mode 100644 src/libs/engine/Engine.hpp delete mode 100644 src/libs/engine/EngineStore.cpp delete mode 100644 src/libs/engine/EngineStore.hpp delete mode 100644 src/libs/engine/Event.cpp delete mode 100644 src/libs/engine/Event.hpp delete mode 100644 src/libs/engine/EventBuffer.cpp delete mode 100644 src/libs/engine/EventBuffer.hpp delete mode 100644 src/libs/engine/EventSink.cpp delete mode 100644 src/libs/engine/EventSink.hpp delete mode 100644 src/libs/engine/EventSource.hpp delete mode 100644 src/libs/engine/GraphObjectImpl.cpp delete mode 100644 src/libs/engine/GraphObjectImpl.hpp delete mode 100644 src/libs/engine/HTTPEngineReceiver.cpp delete mode 100644 src/libs/engine/HTTPEngineReceiver.hpp delete mode 100644 src/libs/engine/InputPort.cpp delete mode 100644 src/libs/engine/InputPort.hpp delete mode 100644 src/libs/engine/InternalPlugin.cpp delete mode 100644 src/libs/engine/InternalPlugin.hpp delete mode 100644 src/libs/engine/JackAudioDriver.cpp delete mode 100644 src/libs/engine/JackAudioDriver.hpp delete mode 100644 src/libs/engine/JackMidiDriver.cpp delete mode 100644 src/libs/engine/JackMidiDriver.hpp delete mode 100644 src/libs/engine/LADSPANode.cpp delete mode 100644 src/libs/engine/LADSPANode.hpp delete mode 100644 src/libs/engine/LADSPAPlugin.cpp delete mode 100644 src/libs/engine/LADSPAPlugin.hpp delete mode 100644 src/libs/engine/LV2Info.cpp delete mode 100644 src/libs/engine/LV2Info.hpp delete mode 100644 src/libs/engine/LV2Node.cpp delete mode 100644 src/libs/engine/LV2Node.hpp delete mode 100644 src/libs/engine/LV2Plugin.cpp delete mode 100644 src/libs/engine/LV2Plugin.hpp delete mode 100644 src/libs/engine/Makefile.am delete mode 100644 src/libs/engine/MessageContext.cpp delete mode 100644 src/libs/engine/MessageContext.hpp delete mode 100644 src/libs/engine/MidiControlNode.cpp delete mode 100644 src/libs/engine/MidiControlNode.hpp delete mode 100644 src/libs/engine/MidiDriver.hpp delete mode 100644 src/libs/engine/MidiNoteNode.cpp delete mode 100644 src/libs/engine/MidiNoteNode.hpp delete mode 100644 src/libs/engine/MidiTriggerNode.cpp delete mode 100644 src/libs/engine/MidiTriggerNode.hpp delete mode 100644 src/libs/engine/NodeBase.cpp delete mode 100644 src/libs/engine/NodeBase.hpp delete mode 100644 src/libs/engine/NodeFactory.cpp delete mode 100644 src/libs/engine/NodeFactory.hpp delete mode 100644 src/libs/engine/NodeImpl.hpp delete mode 100644 src/libs/engine/OSCClientSender.cpp delete mode 100644 src/libs/engine/OSCClientSender.hpp delete mode 100644 src/libs/engine/OSCDriver.hpp delete mode 100644 src/libs/engine/OSCEngineReceiver.cpp delete mode 100644 src/libs/engine/OSCEngineReceiver.hpp delete mode 100644 src/libs/engine/ObjectSender.cpp delete mode 100644 src/libs/engine/ObjectSender.hpp delete mode 100644 src/libs/engine/OmInProcess.cpp delete mode 100644 src/libs/engine/OutputPort.cpp delete mode 100644 src/libs/engine/OutputPort.hpp delete mode 100644 src/libs/engine/PatchImpl.cpp delete mode 100644 src/libs/engine/PatchImpl.hpp delete mode 100644 src/libs/engine/PatchPlugin.hpp delete mode 100644 src/libs/engine/PluginImpl.cpp delete mode 100644 src/libs/engine/PluginImpl.hpp delete mode 100644 src/libs/engine/PortImpl.cpp delete mode 100644 src/libs/engine/PortImpl.hpp delete mode 100644 src/libs/engine/PostProcessor.cpp delete mode 100644 src/libs/engine/PostProcessor.hpp delete mode 100644 src/libs/engine/ProcessContext.hpp delete mode 100644 src/libs/engine/ProcessSlave.cpp delete mode 100644 src/libs/engine/ProcessSlave.hpp delete mode 100644 src/libs/engine/QueuedEngineInterface.cpp delete mode 100644 src/libs/engine/QueuedEngineInterface.hpp delete mode 100644 src/libs/engine/QueuedEvent.cpp delete mode 100644 src/libs/engine/QueuedEvent.hpp delete mode 100644 src/libs/engine/QueuedEventSource.cpp delete mode 100644 src/libs/engine/QueuedEventSource.hpp delete mode 100644 src/libs/engine/Responder.hpp delete mode 100644 src/libs/engine/ThreadManager.hpp delete mode 100644 src/libs/engine/TransportNode.cpp delete mode 100644 src/libs/engine/TransportNode.hpp delete mode 100644 src/libs/engine/engine.cpp delete mode 100644 src/libs/engine/engine.hpp delete mode 100644 src/libs/engine/events.hpp delete mode 100644 src/libs/engine/events/AllNotesOffEvent.cpp delete mode 100644 src/libs/engine/events/AllNotesOffEvent.hpp delete mode 100644 src/libs/engine/events/ClearPatchEvent.cpp delete mode 100644 src/libs/engine/events/ClearPatchEvent.hpp delete mode 100644 src/libs/engine/events/ConnectionEvent.cpp delete mode 100644 src/libs/engine/events/ConnectionEvent.hpp delete mode 100644 src/libs/engine/events/CreateNodeEvent.cpp delete mode 100644 src/libs/engine/events/CreateNodeEvent.hpp delete mode 100644 src/libs/engine/events/CreatePatchEvent.cpp delete mode 100644 src/libs/engine/events/CreatePatchEvent.hpp delete mode 100644 src/libs/engine/events/CreatePortEvent.cpp delete mode 100644 src/libs/engine/events/CreatePortEvent.hpp delete mode 100644 src/libs/engine/events/DeactivateEvent.cpp delete mode 100644 src/libs/engine/events/DeactivateEvent.hpp delete mode 100644 src/libs/engine/events/DestroyEvent.cpp delete mode 100644 src/libs/engine/events/DestroyEvent.hpp delete mode 100644 src/libs/engine/events/DisablePortMonitoringEvent.cpp delete mode 100644 src/libs/engine/events/DisablePortMonitoringEvent.hpp delete mode 100644 src/libs/engine/events/DisconnectAllEvent.cpp delete mode 100644 src/libs/engine/events/DisconnectAllEvent.hpp delete mode 100644 src/libs/engine/events/DisconnectionEvent.cpp delete mode 100644 src/libs/engine/events/DisconnectionEvent.hpp delete mode 100644 src/libs/engine/events/EnablePatchEvent.cpp delete mode 100644 src/libs/engine/events/EnablePatchEvent.hpp delete mode 100644 src/libs/engine/events/LoadPluginsEvent.cpp delete mode 100644 src/libs/engine/events/LoadPluginsEvent.hpp delete mode 100644 src/libs/engine/events/Makefile.am delete mode 100644 src/libs/engine/events/MidiLearnEvent.cpp delete mode 100644 src/libs/engine/events/MidiLearnEvent.hpp delete mode 100644 src/libs/engine/events/NoteEvent.cpp delete mode 100644 src/libs/engine/events/NoteEvent.hpp delete mode 100644 src/libs/engine/events/PingQueuedEvent.hpp delete mode 100644 src/libs/engine/events/RegisterClientEvent.cpp delete mode 100644 src/libs/engine/events/RegisterClientEvent.hpp delete mode 100644 src/libs/engine/events/RenameEvent.cpp delete mode 100644 src/libs/engine/events/RenameEvent.hpp delete mode 100644 src/libs/engine/events/RequestAllObjectsEvent.cpp delete mode 100644 src/libs/engine/events/RequestAllObjectsEvent.hpp delete mode 100644 src/libs/engine/events/RequestMetadataEvent.cpp delete mode 100644 src/libs/engine/events/RequestMetadataEvent.hpp delete mode 100644 src/libs/engine/events/RequestObjectEvent.cpp delete mode 100644 src/libs/engine/events/RequestObjectEvent.hpp delete mode 100644 src/libs/engine/events/RequestPluginEvent.cpp delete mode 100644 src/libs/engine/events/RequestPluginEvent.hpp delete mode 100644 src/libs/engine/events/RequestPluginsEvent.cpp delete mode 100644 src/libs/engine/events/RequestPluginsEvent.hpp delete mode 100644 src/libs/engine/events/RequestPortValueEvent.cpp delete mode 100644 src/libs/engine/events/RequestPortValueEvent.hpp delete mode 100644 src/libs/engine/events/SendPortActivityEvent.cpp delete mode 100644 src/libs/engine/events/SendPortActivityEvent.hpp delete mode 100644 src/libs/engine/events/SendPortValueEvent.cpp delete mode 100644 src/libs/engine/events/SendPortValueEvent.hpp delete mode 100644 src/libs/engine/events/SetMetadataEvent.cpp delete mode 100644 src/libs/engine/events/SetMetadataEvent.hpp delete mode 100644 src/libs/engine/events/SetPolyphonicEvent.cpp delete mode 100644 src/libs/engine/events/SetPolyphonicEvent.hpp delete mode 100644 src/libs/engine/events/SetPolyphonyEvent.cpp delete mode 100644 src/libs/engine/events/SetPolyphonyEvent.hpp delete mode 100644 src/libs/engine/events/SetPortValueEvent.cpp delete mode 100644 src/libs/engine/events/SetPortValueEvent.hpp delete mode 100644 src/libs/engine/events/UnregisterClientEvent.cpp delete mode 100644 src/libs/engine/events/UnregisterClientEvent.hpp delete mode 100644 src/libs/engine/jack_compat.h delete mode 120000 src/libs/engine/lv2_contexts.h delete mode 100644 src/libs/engine/tuning.hpp delete mode 100644 src/libs/engine/types.hpp delete mode 100644 src/libs/engine/util.hpp delete mode 100644 src/libs/engine/wscript delete mode 100644 src/libs/gui/App.cpp delete mode 100644 src/libs/gui/App.hpp delete mode 100644 src/libs/gui/BreadCrumb.hpp delete mode 100644 src/libs/gui/BreadCrumbBox.cpp delete mode 100644 src/libs/gui/BreadCrumbBox.hpp delete mode 100644 src/libs/gui/Configuration.cpp delete mode 100644 src/libs/gui/Configuration.hpp delete mode 100644 src/libs/gui/ConnectWindow.cpp delete mode 100644 src/libs/gui/ConnectWindow.hpp delete mode 100644 src/libs/gui/Connection.hpp delete mode 100644 src/libs/gui/ControlPanel.cpp delete mode 100644 src/libs/gui/ControlPanel.hpp delete mode 100644 src/libs/gui/Controls.cpp delete mode 100644 src/libs/gui/Controls.hpp delete mode 100644 src/libs/gui/GladeFactory.cpp delete mode 100644 src/libs/gui/GladeFactory.hpp delete mode 100644 src/libs/gui/LoadPatchWindow.cpp delete mode 100644 src/libs/gui/LoadPatchWindow.hpp delete mode 100644 src/libs/gui/LoadPluginWindow.cpp delete mode 100644 src/libs/gui/LoadPluginWindow.hpp delete mode 100644 src/libs/gui/LoadRemotePatchWindow.cpp delete mode 100644 src/libs/gui/LoadRemotePatchWindow.hpp delete mode 100644 src/libs/gui/LoadSubpatchWindow.cpp delete mode 100644 src/libs/gui/LoadSubpatchWindow.hpp delete mode 100644 src/libs/gui/Makefile.am delete mode 100644 src/libs/gui/MessagesWindow.cpp delete mode 100644 src/libs/gui/MessagesWindow.hpp delete mode 100644 src/libs/gui/NewSubpatchWindow.cpp delete mode 100644 src/libs/gui/NewSubpatchWindow.hpp delete mode 100644 src/libs/gui/NodeControlWindow.cpp delete mode 100644 src/libs/gui/NodeControlWindow.hpp delete mode 100644 src/libs/gui/NodeMenu.cpp delete mode 100644 src/libs/gui/NodeMenu.hpp delete mode 100644 src/libs/gui/NodeModule.cpp delete mode 100644 src/libs/gui/NodeModule.hpp delete mode 100644 src/libs/gui/NodePropertiesWindow.cpp delete mode 100644 src/libs/gui/NodePropertiesWindow.hpp delete mode 100644 src/libs/gui/ObjectMenu.cpp delete mode 100644 src/libs/gui/ObjectMenu.hpp delete mode 100644 src/libs/gui/PatchCanvas.cpp delete mode 100644 src/libs/gui/PatchCanvas.hpp delete mode 100644 src/libs/gui/PatchPortModule.cpp delete mode 100644 src/libs/gui/PatchPortModule.hpp delete mode 100644 src/libs/gui/PatchPropertiesWindow.cpp delete mode 100644 src/libs/gui/PatchPropertiesWindow.hpp delete mode 100644 src/libs/gui/PatchTreeWindow.cpp delete mode 100644 src/libs/gui/PatchTreeWindow.hpp delete mode 100644 src/libs/gui/PatchView.cpp delete mode 100644 src/libs/gui/PatchView.hpp delete mode 100644 src/libs/gui/PatchWindow.cpp delete mode 100644 src/libs/gui/PatchWindow.hpp delete mode 100644 src/libs/gui/Port.cpp delete mode 100644 src/libs/gui/Port.hpp delete mode 100644 src/libs/gui/PortMenu.cpp delete mode 100644 src/libs/gui/PortMenu.hpp delete mode 100644 src/libs/gui/PortPropertiesWindow.cpp delete mode 100644 src/libs/gui/PortPropertiesWindow.hpp delete mode 100644 src/libs/gui/RenameWindow.cpp delete mode 100644 src/libs/gui/RenameWindow.hpp delete mode 100644 src/libs/gui/SubpatchModule.cpp delete mode 100644 src/libs/gui/SubpatchModule.hpp delete mode 100644 src/libs/gui/ThreadedLoader.cpp delete mode 100644 src/libs/gui/ThreadedLoader.hpp delete mode 100644 src/libs/gui/UploadPatchWindow.cpp delete mode 100644 src/libs/gui/UploadPatchWindow.hpp delete mode 100644 src/libs/gui/WindowFactory.cpp delete mode 100644 src/libs/gui/WindowFactory.hpp delete mode 100644 src/libs/gui/cmdline.h delete mode 100644 src/libs/gui/gui.cpp delete mode 100644 src/libs/gui/gui.hpp delete mode 100644 src/libs/gui/ingen-icon.svg delete mode 100644 src/libs/gui/ingen.svg delete mode 100644 src/libs/gui/ingen_gui.glade delete mode 100644 src/libs/gui/ingen_gui.gladep delete mode 100644 src/libs/gui/wscript delete mode 100644 src/libs/module/Makefile.am delete mode 100644 src/libs/module/Module.cpp delete mode 100644 src/libs/module/Module.hpp delete mode 100644 src/libs/module/World.hpp delete mode 100644 src/libs/module/global.cpp delete mode 100644 src/libs/module/global.hpp delete mode 100644 src/libs/module/wscript delete mode 100644 src/libs/serialisation/Makefile.am delete mode 100644 src/libs/serialisation/Parser.cpp delete mode 100644 src/libs/serialisation/Parser.hpp delete mode 100644 src/libs/serialisation/Serialiser.cpp delete mode 100644 src/libs/serialisation/Serialiser.hpp delete mode 100644 src/libs/serialisation/serialisation.cpp delete mode 100644 src/libs/serialisation/serialisation.hpp delete mode 100644 src/libs/serialisation/wscript delete mode 100644 src/libs/shared/Builder.cpp delete mode 100644 src/libs/shared/Builder.hpp delete mode 100644 src/libs/shared/ClashAvoider.cpp delete mode 100644 src/libs/shared/ClashAvoider.hpp delete mode 100644 src/libs/shared/LV2Features.cpp delete mode 100644 src/libs/shared/LV2Features.hpp delete mode 100644 src/libs/shared/LV2URIMap.cpp delete mode 100644 src/libs/shared/LV2URIMap.hpp delete mode 100644 src/libs/shared/Makefile.am delete mode 100644 src/libs/shared/OSCSender.cpp delete mode 100644 src/libs/shared/OSCSender.hpp delete mode 100644 src/libs/shared/Store.cpp delete mode 100644 src/libs/shared/Store.hpp delete mode 100644 src/libs/shared/wscript create mode 100644 src/module/Makefile.am create mode 100644 src/module/Module.cpp create mode 100644 src/module/Module.hpp create mode 100644 src/module/World.hpp create mode 100644 src/module/global.cpp create mode 100644 src/module/global.hpp create mode 100644 src/module/wscript delete mode 100644 src/progs/ingen/Makefile.am delete mode 100644 src/progs/ingen/cmdline.c delete mode 100644 src/progs/ingen/cmdline.ggo delete mode 100644 src/progs/ingen/cmdline.h delete mode 100755 src/progs/ingen/ingen.dbg delete mode 100644 src/progs/ingen/ingen.desktop.in delete mode 100755 src/progs/ingen/ingen.dev delete mode 100644 src/progs/ingen/ingen.grind delete mode 100644 src/progs/ingen/main.cpp delete mode 100644 src/progs/ingen/wscript delete mode 100644 src/progs/python/Makefile.am delete mode 100755 src/progs/python/OSC.py delete mode 100644 src/progs/python/ingen.py delete mode 100644 src/progs/python/ingenecho.py delete mode 100644 src/progs/python/scripts/Makefile.am delete mode 100755 src/progs/python/scripts/flatten.py delete mode 100644 src/progs/python/scripts/sillysinepatch.py delete mode 100644 src/progs/supercollider/Ingen.sc delete mode 100644 src/progs/supercollider/Makefile.am delete mode 100644 src/progs/supercollider/README delete mode 100644 src/progs/supercollider/example.sc create mode 100644 src/scripts/python/Makefile.am create mode 100755 src/scripts/python/OSC.py create mode 100644 src/scripts/python/ingen.py create mode 100644 src/scripts/python/ingenecho.py create mode 100644 src/scripts/python/scripts/Makefile.am create mode 100755 src/scripts/python/scripts/flatten.py create mode 100644 src/scripts/python/scripts/sillysinepatch.py create mode 100644 src/scripts/supercollider/Ingen.sc create mode 100644 src/scripts/supercollider/Makefile.am create mode 100644 src/scripts/supercollider/README create mode 100644 src/scripts/supercollider/example.sc create mode 100644 src/serialisation/Makefile.am create mode 100644 src/serialisation/Parser.cpp create mode 100644 src/serialisation/Parser.hpp create mode 100644 src/serialisation/Serialiser.cpp create mode 100644 src/serialisation/Serialiser.hpp create mode 100644 src/serialisation/serialisation.cpp create mode 100644 src/serialisation/serialisation.hpp create mode 100644 src/serialisation/wscript create mode 100644 src/shared/Builder.cpp create mode 100644 src/shared/Builder.hpp create mode 100644 src/shared/ClashAvoider.cpp create mode 100644 src/shared/ClashAvoider.hpp create mode 100644 src/shared/LV2Features.cpp create mode 100644 src/shared/LV2Features.hpp create mode 100644 src/shared/LV2URIMap.cpp create mode 100644 src/shared/LV2URIMap.hpp create mode 100644 src/shared/Makefile.am create mode 100644 src/shared/OSCSender.cpp create mode 100644 src/shared/OSCSender.hpp create mode 100644 src/shared/Store.cpp create mode 100644 src/shared/Store.hpp create mode 100644 src/shared/wscript diff --git a/src/client/ClientStore.cpp b/src/client/ClientStore.cpp new file mode 100644 index 00000000..18582046 --- /dev/null +++ b/src/client/ClientStore.cpp @@ -0,0 +1,648 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "ClientStore.hpp" +#include "ObjectModel.hpp" +#include "PatchModel.hpp" +#include "NodeModel.hpp" +#include "PortModel.hpp" +#include "PluginModel.hpp" +#include "PatchModel.hpp" +#include "SigClientInterface.hpp" + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Client { + + +ClientStore::ClientStore(SharedPtr engine, SharedPtr emitter) + : _engine(engine) + , _emitter(emitter) + , _plugins(new Plugins()) +{ + _handle_orphans = (engine && emitter); + + if (!emitter) + return; + + emitter->signal_object_destroyed.connect(sigc::mem_fun(this, &ClientStore::destroy)); + emitter->signal_object_renamed.connect(sigc::mem_fun(this, &ClientStore::rename)); + emitter->signal_new_plugin.connect(sigc::mem_fun(this, &ClientStore::new_plugin)); + emitter->signal_new_patch.connect(sigc::mem_fun(this, &ClientStore::new_patch)); + emitter->signal_new_node.connect(sigc::mem_fun(this, &ClientStore::new_node)); + emitter->signal_new_port.connect(sigc::mem_fun(this, &ClientStore::new_port)); + emitter->signal_patch_cleared.connect(sigc::mem_fun(this, &ClientStore::patch_cleared)); + emitter->signal_connection.connect(sigc::mem_fun(this, &ClientStore::connect)); + emitter->signal_disconnection.connect(sigc::mem_fun(this, &ClientStore::disconnect)); + emitter->signal_variable_change.connect(sigc::mem_fun(this, &ClientStore::set_variable)); + emitter->signal_property_change.connect(sigc::mem_fun(this, &ClientStore::set_property)); + emitter->signal_port_value.connect(sigc::mem_fun(this, &ClientStore::set_port_value)); + emitter->signal_voice_value.connect(sigc::mem_fun(this, &ClientStore::set_voice_value)); + emitter->signal_port_activity.connect(sigc::mem_fun(this, &ClientStore::port_activity)); +} + + +void +ClientStore::clear() +{ + Store::clear(); + _plugins->clear(); +} + + +void +ClientStore::add_plugin_orphan(SharedPtr node) +{ + if (!_handle_orphans) + return; + + Raul::Table > >::iterator spawn + = _plugin_orphans.find(node->plugin_uri()); + + if (spawn != _plugin_orphans.end()) { + spawn->second.push_back(node); + } else { + cerr << "WARNING: Orphans of plugin " << node->plugin_uri() << " received" << endl; + _engine->request_plugin(node->plugin_uri()); + list > l; + l.push_back(node); + _plugin_orphans[node->plugin_uri()] = l; + } +} + + +void +ClientStore::resolve_plugin_orphans(SharedPtr plugin) +{ + if (!_handle_orphans) + return; + Raul::Table > >::iterator n + = _plugin_orphans.find(plugin->uri()); + + if (n != _plugin_orphans.end()) { + + list > spawn = n->second; // take a copy + cerr << "Missing dependant " << plugin->uri() << " received" << endl; + + _plugin_orphans.erase(plugin->uri()); // prevent infinite recursion + + for (list >::iterator i = spawn.begin(); + i != spawn.end(); ++i) { + (*i)->_plugin = plugin; + //add_object(*i); + } + } +} + + +void +ClientStore::add_connection_orphan(std::pair orphan) +{ + // Do this anyway, it's needed to get the connections for copy&paste + //if (!_handle_orphans) + //return; + + if (_handle_orphans) + cerr << "WARNING: Orphan connection " << orphan.first + << " -> " << orphan.second << " received." << endl; + + _connection_orphans.push_back(orphan); +} + + +void +ClientStore::resolve_connection_orphans(SharedPtr port) +{ + if (!_handle_orphans) + return; + assert(port->parent()); + + for (list< pair >::iterator c = _connection_orphans.begin(); + c != _connection_orphans.end(); ) { + + list< pair >::iterator next = c; + ++next; + + if (c->first == port->path() || c->second == port->path()) { + cerr << "Missing dependant (" << c->first << " -> " << c->second << ") received" << endl; + bool success = attempt_connection(c->first, c->second); + if (success) + _connection_orphans.erase(c); + } + + c = next; + } +} + + +void +ClientStore::add_orphan(SharedPtr child) +{ + if (!_handle_orphans) + return; + cerr << "WARNING: Orphan object " << child->path() << " received." << endl; + + Raul::PathTable > >::iterator children + = _orphans.find(child->path().parent()); + + _engine->request_object(child->path().parent()); + + if (children != _orphans.end()) { + children->second.push_back(child); + } else { + list > l; + l.push_back(child); + _orphans.insert(make_pair(child->path().parent(), l)); + } +} + + +void +ClientStore::add_variable_orphan(const Path& subject_path, const string& predicate, const Atom& value) +{ + if (!_handle_orphans) + return; + Raul::PathTable > >::iterator orphans + = _variable_orphans.find(subject_path); + + _engine->request_object(subject_path); + + if (orphans != _variable_orphans.end()) { + orphans->second.push_back(std::pair(predicate, value)); + } else { + list > l; + l.push_back(std::pair(predicate, value)); + _variable_orphans[subject_path] = l; + } +} + + +void +ClientStore::resolve_variable_orphans(SharedPtr subject) +{ + if (!_handle_orphans) + return; + Raul::PathTable > >::iterator v + = _variable_orphans.find(subject->path()); + + if (v != _variable_orphans.end()) { + + list > values = v->second; // take a copy + + _variable_orphans.erase(subject->path()); + cerr << "Missing dependant " << subject->path() << " received" << endl; + + for (list >::iterator i = values.begin(); + i != values.end(); ++i) { + subject->set_variable(i->first, i->second); + } + } +} + + +void +ClientStore::resolve_orphans(SharedPtr parent) +{ + if (!_handle_orphans) + return; + Raul::PathTable > >::iterator c + = _orphans.find(parent->path()); + + if (c != _orphans.end()) { + + list > children = c->second; // take a copy + + _orphans.erase(parent->path()); // prevent infinite recursion + + for (list >::iterator i = children.begin(); + i != children.end(); ++i) { + add_object(*i); + } + } +} + + +void +ClientStore::add_object(SharedPtr object) +{ + // If we already have "this" object, merge the existing one into the new + // one (with precedence to the new values). + iterator existing = find(object->path()); + if (existing != end()) { + PtrCast(existing->second)->set(object); + } else { + + if (object->path() != "/") { + SharedPtr parent = this->object(object->path().parent()); + if (parent) { + assert(object->path().is_child_of(parent->path())); + object->set_parent(parent); + parent->add_child(object); + assert(parent && (object->parent() == parent)); + + (*this)[object->path()] = object; + signal_new_object.emit(object); + + resolve_variable_orphans(parent); + resolve_orphans(parent); + + SharedPtr port = PtrCast(object); + if (port) + resolve_connection_orphans(port); + + } else { + add_orphan(object); + } + } else { + (*this)[object->path()] = object; + signal_new_object.emit(object); + } + + } + + /*cout << "[Store] Added " << object->path() << " {" << endl; + for (iterator i = begin(); i != end(); ++i) { + cout << "\t" << i->first << endl; + } + cout << "}" << endl;*/ +} + + +SharedPtr +ClientStore::remove_object(const Path& path) +{ + iterator i = find(path); + + if (i != end()) { + assert((*i).second->path() == path); + SharedPtr result = PtrCast((*i).second); + assert(result); + //erase(i); + iterator descendants_end = find_descendants_end(i); + SharedPtr removed = yank(i, descendants_end); + + /*cout << "[Store] Removing " << i->first << " {" << endl; + for (iterator i = removed.begin(); i != removed.end(); ++i) { + cout << "\t" << i->first << endl; + } + cout << "}" << endl;*/ + + if (result) + result->signal_destroyed.emit(); + + if (result->path() != "/") { + assert(result->parent()); + + SharedPtr parent = this->object(result->path().parent()); + if (parent) { + parent->remove_child(result); + } + } + + assert(!object(path)); + + return result; + + } else { + return SharedPtr(); + } +} + + +SharedPtr +ClientStore::plugin(const string& uri) +{ + assert(uri.length() > 0); + Plugins::iterator i = _plugins->find(uri); + if (i == _plugins->end()) + return SharedPtr(); + else + return (*i).second; +} + + +SharedPtr +ClientStore::object(const Path& path) +{ + assert(path.length() > 0); + iterator i = find(path); + if (i == end()) { + return SharedPtr(); + } else { + SharedPtr model = PtrCast(i->second); + assert(model); + assert(model->path() == "/" || model->parent()); + return model; + } +} + +void +ClientStore::add_plugin(SharedPtr pm) +{ + // FIXME: dupes? merge, like with objects? + + (*_plugins)[pm->uri()] = pm; + signal_new_plugin(pm); + //cerr << "Plugin: " << pm->uri() << ", # plugins: " << _plugins->size() << endl; +} + + +/* ****** Signal Handlers ******** */ + + +void +ClientStore::destroy(const std::string& path) +{ + SharedPtr removed = remove_object(path); + removed.reset(); + //cerr << "[ClientStore] removed object " << path << ", count: " << removed.use_count(); +} + +void +ClientStore::rename(const Path& old_path, const Path& new_path) +{ + iterator parent = find(old_path); + if (parent == end()) { + cerr << "[Store] Failed to find object " << old_path << " to rename." << endl; + return; + } + + iterator descendants_end = find_descendants_end(parent); + + SharedPtr< Table > > removed + = yank(parent, descendants_end); + + assert(removed->size() > 0); + + for (Table >::iterator i = removed->begin(); i != removed->end(); ++i) { + const Path& child_old_path = i->first; + assert(Path::descendant_comparator(old_path, child_old_path)); + + Path child_new_path; + if (child_old_path == old_path) + child_new_path = new_path; + else + child_new_path = new_path.base() + child_old_path.substr(old_path.length()+1); + + cerr << "[Store] Renamed " << child_old_path << " -> " << child_new_path << endl; + PtrCast(i->second)->set_path(child_new_path); + i->first = child_new_path; + } + + cram(*removed.get()); + + //cerr << "[Store] Table:" << endl; + //for (size_t i=0; i < removed.size(); ++i) { + // cerr << removed[i].first << "\t\t: " << removed[i].second << endl; + //} + /*for (iterator i = begin(); i != end(); ++i) { + cerr << i->first << "\t\t: " << i->second << endl; + }*/ +} + +void +ClientStore::new_plugin(const string& uri, const string& type_uri, const string& symbol, const string& name) +{ + SharedPtr p(new PluginModel(uri, type_uri, symbol, name)); + add_plugin(p); + resolve_plugin_orphans(p); +} + + +void +ClientStore::new_patch(const string& path, uint32_t poly) +{ + SharedPtr p(new PatchModel(path, poly)); + add_object(p); +} + + +void +ClientStore::new_node(const string& path, const string& plugin_uri) +{ + SharedPtr plug = plugin(plugin_uri); + if (!plug) { + SharedPtr n(new NodeModel(plugin_uri, path)); + add_plugin_orphan(n); + add_object(n); + } else { + SharedPtr n(new NodeModel(plug, path)); + add_object(n); + } +} + + +void +ClientStore::new_port(const string& path, uint32_t index, const string& type, bool is_output) +{ + PortModel::Direction pdir = is_output ? PortModel::OUTPUT : PortModel::INPUT; + + SharedPtr p(new PortModel(path, index, type, pdir)); + add_object(p); + if (p->parent()) + resolve_connection_orphans(p); +} + + +void +ClientStore::patch_cleared(const Path& path) +{ + iterator i = find(path); + if (i != end()) { + assert((*i).second->path() == path); + SharedPtr patch = PtrCast(i->second); + + iterator first_descendant = i; + ++first_descendant; + iterator descendants_end = find_descendants_end(i); + SharedPtr< Table > > removed + = yank(first_descendant, descendants_end); + + for (iterator i = removed->begin(); i != removed->end(); ++i) { + SharedPtr model = PtrCast(i->second); + assert(model); + model->signal_destroyed.emit(); + if (model->parent() == patch) + patch->remove_child(model); + } + + } else { + cerr << "[Store] Unable to find patch " << path << " to clear." << endl; + } +} + + +void +ClientStore::set_variable(const string& subject_path, const string& predicate, const Atom& value) +{ + SharedPtr subject = object(subject_path); + + if (!value.is_valid()) { + cerr << "ERROR: variable '" << predicate << "' has no type" << endl; + } else if (subject) { + subject->set_variable(predicate, value); + } else { + add_variable_orphan(subject_path, predicate, value); + cerr << "WARNING: variable for unknown object " << subject_path << endl; + } +} + + +void +ClientStore::set_property(const string& subject_path, const string& predicate, const Atom& value) +{ + SharedPtr subject = object(subject_path); + + if (!value.is_valid()) { + cerr << "ERROR: property '" << predicate << "' has no type" << endl; + } else if (subject) { + subject->set_property(predicate, value); + } else { + cerr << "WARNING: property for unknown object " << subject_path + << " lost. Client must refresh!" << endl; + } +} + + +void +ClientStore::set_port_value(const string& port_path, const Raul::Atom& value) +{ + SharedPtr port = PtrCast(object(port_path)); + if (port) + port->value(value); + else + cerr << "ERROR: control change for nonexistant port " << port_path << endl; +} + + +void +ClientStore::set_voice_value(const string& port_path, uint32_t voice, const Raul::Atom& value) +{ + SharedPtr port = PtrCast(object(port_path)); + if (port) + port->value(voice, value); + else + cerr << "ERROR: poly control change for nonexistant port " << port_path << endl; +} + + +void +ClientStore::port_activity(const Path& port_path) +{ + SharedPtr port = PtrCast(object(port_path)); + if (port) + port->signal_activity.emit(); + else + cerr << "ERROR: activity for nonexistant port " << port_path << endl; +} + + +SharedPtr +ClientStore::connection_patch(const Path& src_port_path, const Path& dst_port_path) +{ + SharedPtr patch; + + if (src_port_path.parent() == dst_port_path.parent()) + patch = PtrCast(this->object(src_port_path.parent())); + + if (!patch && src_port_path.parent() == dst_port_path.parent().parent()) + patch = PtrCast(this->object(src_port_path.parent())); + + if (!patch && src_port_path.parent().parent() == dst_port_path.parent()) + patch = PtrCast(this->object(dst_port_path.parent())); + + if (!patch) + patch = PtrCast(this->object(src_port_path.parent().parent())); + + if (!patch) + cerr << "ERROR: Unable to find connection patch " << src_port_path + << " -> " << dst_port_path << endl; + + return patch; +} + + +bool +ClientStore::attempt_connection(const Path& src_port_path, const Path& dst_port_path, bool add_orphan) +{ + SharedPtr src_port = PtrCast(object(src_port_path)); + SharedPtr dst_port = PtrCast(object(dst_port_path)); + + if (src_port && dst_port) { + assert(src_port->parent()); + assert(dst_port->parent()); + + SharedPtr patch = connection_patch(src_port_path, dst_port_path); + assert(patch); + + SharedPtr cm(new ConnectionModel(src_port, dst_port)); + + src_port->connected_to(dst_port); + dst_port->connected_to(src_port); + + patch->add_connection(cm); + return true; + } else if (add_orphan) { + add_connection_orphan(make_pair(src_port_path, dst_port_path)); + } + + return false; +} + + +void +ClientStore::connect(const string& src_port_path, const string& dst_port_path) +{ + attempt_connection(src_port_path, dst_port_path, true); +} + + +void +ClientStore::disconnect(const string& src_port_path, const string& dst_port_path) +{ + // Find the ports and create a ConnectionModel just to get at the parent path + // finding logic in ConnectionModel. So I'm lazy. + + SharedPtr src_port = PtrCast(object(src_port_path)); + SharedPtr dst_port = PtrCast(object(dst_port_path)); + + if (src_port) + src_port->disconnected_from(dst_port); + else + cerr << "WARNING: Disconnection from nonexistant src port " << src_port_path << endl; + + if (dst_port) + dst_port->disconnected_from(dst_port); + else + cerr << "WARNING: Disconnection from nonexistant dst port " << dst_port_path << endl; + + SharedPtr patch = connection_patch(src_port_path, dst_port_path); + + if (patch) + patch->remove_connection(src_port_path, dst_port_path); + else + cerr << "ERROR: disconnection in nonexistant patch: " + << src_port_path << " -> " << dst_port_path << endl; +} + + +} // namespace Client +} // namespace Ingen + diff --git a/src/client/ClientStore.hpp b/src/client/ClientStore.hpp new file mode 100644 index 00000000..f08fcd9b --- /dev/null +++ b/src/client/ClientStore.hpp @@ -0,0 +1,151 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CLIENT_STORE_HPP +#define CLIENT_STORE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "interface/EngineInterface.hpp" +#include "shared/Store.hpp" +using std::string; using std::list; +using Ingen::Shared::EngineInterface; +using Raul::Path; +using Raul::Atom; + +namespace Ingen { + +namespace Shared { class GraphObject; } + +namespace Client { + +class SigClientInterface; +class ObjectModel; +class PluginModel; +class PatchModel; +class NodeModel; +class PortModel; +class ConnectionModel; + + +/** Automatically manages models of objects in the engine. + * + * \ingroup IngenClient + */ +class ClientStore : public Shared::Store, public Shared::CommonInterface, public sigc::trackable { +public: + ClientStore(SharedPtr engine=SharedPtr(), + SharedPtr emitter=SharedPtr()); + + SharedPtr plugin(const string& uri); + SharedPtr object(const Path& path); + + void clear(); + + typedef Raul::Table > Plugins; + SharedPtr plugins() const { return _plugins; } + SharedPtr plugins() { return _plugins; } + void set_plugins(SharedPtr p) { _plugins = p; } + + // CommonInterface + void new_plugin(const string& uri, const string& type_uri, const string& symbol, const string& name); + void new_patch(const string& path, uint32_t poly); + void new_node(const string& path, const string& plugin_uri); + void new_port(const string& path, uint32_t index, const string& data_type, bool is_output); + void set_variable(const string& subject_path, const string& predicate, const Atom& value); + void set_property(const string& subject_path, const string& predicate, const Atom& value); + void set_port_value(const string& port_path, const Raul::Atom& value); + void set_voice_value(const string& port_path, uint32_t voice, const Raul::Atom& value); + void connect(const string& src_port_path, const string& dst_port_path); + void disconnect(const string& src_port_path, const string& dst_port_path); + void destroy(const string& path); + + typedef list< std::pair > ConnectionRecords; + const ConnectionRecords& connection_records() { return _connection_orphans; } + + sigc::signal > signal_new_object; + sigc::signal > signal_new_plugin; + +private: + + void add(Shared::GraphObject* o) { throw; } + + void add_object(SharedPtr object); + SharedPtr remove_object(const Path& path); + + void add_plugin(SharedPtr plugin); + + SharedPtr connection_patch(const Path& src_port_path, const Path& dst_port_path); + + // It would be nice to integrate these somehow.. + + void add_orphan(SharedPtr orphan); + void resolve_orphans(SharedPtr parent); + + void add_connection_orphan(std::pair orphan); + void resolve_connection_orphans(SharedPtr port); + + void add_plugin_orphan(SharedPtr orphan); + void resolve_plugin_orphans(SharedPtr plugin); + + void add_variable_orphan(const Path& subject, const string& predicate, const Atom& value); + void resolve_variable_orphans(SharedPtr subject); + + void bundle_begin() {} + void bundle_end() {} + + // Slots for SigClientInterface signals + void rename(const Path& old_path, const Path& new_path); + void patch_cleared(const Path& path); + void port_activity(const Path& port_path); + + bool attempt_connection(const Path& src_port_path, const Path& dst_port_path, bool add_orphan=false); + + bool _handle_orphans; + + SharedPtr _engine; + SharedPtr _emitter; + + SharedPtr _plugins; ///< Map, keyed by plugin URI + + /** Objects we've received, but depend on the existance of another unknown object. + * Keyed by the path of the depended-on object (for tolerance of orderless comms) */ + Raul::PathTable > > _orphans; + + /** Same idea, except with plugins instead of parents. + * It's unfortunate everything doesn't just have a URI and this was the same.. ahem.. */ + Raul::Table > > _plugin_orphans; + + /** Not orphans OF variable like the above, but orphans which are variable */ + Raul::PathTable > > _variable_orphans; + + /** Ditto */ + ConnectionRecords _connection_orphans; +}; + + +} // namespace Client +} // namespace Ingen + +#endif // CLIENT_STORE_HPP diff --git a/src/client/ConnectionModel.hpp b/src/client/ConnectionModel.hpp new file mode 100644 index 00000000..91c448df --- /dev/null +++ b/src/client/ConnectionModel.hpp @@ -0,0 +1,76 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CONNECTIONMODEL_H +#define CONNECTIONMODEL_H + +#include +#include +#include +#include +#include +#include "interface/Connection.hpp" +#include "PortModel.hpp" + +namespace Ingen { +namespace Client { + +class ClientStore; + + +/** Class to represent a port->port connection in the engine. + * + * This can either have pointers to the connection ports' models, or just + * paths as strings. The engine passes just strings (by necessity), but + * clients can set the pointers then they don't have to worry about port + * renaming, as the connections will always return the port's path, even + * if it changes. + * + * \ingroup IngenClient + */ +class ConnectionModel : public Shared::Connection +{ +public: + SharedPtr src_port() const { return _src_port; } + SharedPtr dst_port() const { return _dst_port; } + + const Path src_port_path() const { return _src_port->path(); } + const Path dst_port_path() const { return _dst_port->path(); } + +private: + friend class ClientStore; + + ConnectionModel(SharedPtr src, SharedPtr dst) + : _src_port(src) + , _dst_port(dst) + { + assert(_src_port); + assert(_dst_port); + assert(_src_port->parent()); + assert(_dst_port->parent()); + assert(_src_port->path() != _dst_port->path()); + } + + const SharedPtr _src_port; + const SharedPtr _dst_port; +}; + + +} // namespace Client +} // namespace Ingen + +#endif // CONNECTIONMODEL_H diff --git a/src/client/DeprecatedLoader.cpp b/src/client/DeprecatedLoader.cpp new file mode 100644 index 00000000..a07893f7 --- /dev/null +++ b/src/client/DeprecatedLoader.cpp @@ -0,0 +1,711 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include +#include +#include +#include +#include // for pair, make_pair +#include +#include +#include +#include // for atof +#include +#include +#include +#include +#include +#include "interface/EngineInterface.hpp" +#include "PatchModel.hpp" +#include "NodeModel.hpp" +#include "ConnectionModel.hpp" +#include "PortModel.hpp" +#include "PluginModel.hpp" +#include "DeprecatedLoader.hpp" + +#define NS_INGEN "http://drobilla.net/ns/ingen#" + +using namespace std; + +namespace Ingen { +namespace Client { + + +/** A single port's control setting (in a preset). + * + * \ingroup IngenClient + */ +class ControlModel +{ +public: + ControlModel(const Path& port_path, float value) + : _port_path(port_path) + , _value(value) + { + assert(_port_path.find("//") == string::npos); + } + + const Path& port_path() const { return _port_path; } + void port_path(const string& p) { _port_path = p; } + float value() const { return _value; } + void value(float v) { _value = v; } + +private: + Path _port_path; + float _value; +}; + + +/** Model of a preset (a collection of control settings). + * + * \ingroup IngenClient + */ +class PresetModel +{ +public: + PresetModel(const string& base_path) : _base_path(base_path) {} + + /** Add a control value to this preset. An empty string for a node_name + * means the port is on the patch itself (not a node in the patch). */ + void add_control(const string& node_name, string port_name, float value) { + if (port_name == "note_number") // FIXME: filthy kludge + port_name = "note"; + + if (node_name != "") + _controls.push_back(ControlModel(_base_path + node_name +"/"+ port_name, value)); + else + _controls.push_back(ControlModel(_base_path + port_name, value)); + } + + const string& name() const { return _name; } + void name(const string& n) { _name = n; } + + const list& controls() const { return _controls; } + +private: + string _name; + string _base_path; + list _controls; +}; + + +string +DeprecatedLoader::nameify_if_invalid(const string& name) +{ + if (Path::is_valid_name(name)) { + return name; + } else { + const string new_name = Path::nameify(name); + assert(Path::is_valid_name(new_name)); + if (new_name != name) + cerr << "WARNING: Illegal name '" << name << "' converted to '" + << new_name << "'" << endl; + return new_name; + } +} + + +string +DeprecatedLoader::translate_load_path(const string& path) +{ + std::map::iterator t = _load_path_translations.find(path); + + if (t != _load_path_translations.end()) { + assert(Path::is_valid((*t).second)); + return (*t).second; + // Filthy, filthy kludges + // (FIXME: apply these less heavy handedly, only when it's an internal module) + } else if (path.find("midi") != string::npos) { + assert(Path::is_valid(path)); + if (path.substr(path.find_last_of("/")) == "/MIDI_In") + return path.substr(0, path.find_last_of("/")) + "/input"; + else if (path.substr(path.find_last_of("/")) == "/Note_Number") + return path.substr(0, path.find_last_of("/")) + "/note"; + else if (path.substr(path.find_last_of("/")) == "/Gate") + return path.substr(0, path.find_last_of("/")) + "/gate"; + else if (path.substr(path.find_last_of("/")) == "/Trigger") + return path.substr(0, path.find_last_of("/")) + "/trigger"; + else if (path.substr(path.find_last_of("/")) == "/Velocity") + return path.substr(0, path.find_last_of("/")) + "/velocity"; + else + return path; + } else { + return path; + } +} + + +/** Add a piece of data to a Variables, translating from deprecated unqualified keys + * + * Adds a namespace prefix for known keys, and ignores the rest. + */ +void +DeprecatedLoader::add_variable(GraphObject::Variables& data, string old_key, string value) +{ + string key = ""; + if (old_key == "module-x") + key = "ingenuity:canvas-x"; + else if (old_key == "module-y") + key = "ingenuity:canvas-y"; + + if (key != "") { + // FIXME: should this overwrite existing values? + if (data.find(key) == data.end()) { + // Hack to make module-x and module-y set as floats + char* c_val = strdup(value.c_str()); + char* endptr = NULL; + + // FIXME: locale kludges + char* locale = strdup(setlocale(LC_NUMERIC, NULL)); + + float fval = strtof(c_val, &endptr); + + setlocale(LC_NUMERIC, locale); + free(locale); + + if (endptr != c_val && *endptr == '\0') + data[key] = Atom(fval); + else + data[key] = Atom(value); + + free(c_val); + } + } +} + + +/** Load a patch in to the engine (and client) from a patch file. + * + * The name and poly from the passed PatchModel are used. If the name is + * the empty string, the name will be loaded from the file. If the poly + * is 0, it will be loaded from file. Otherwise the given values will + * be used. + * + * @param filename Local name of file to load patch from + * + * @param parent_path Patch to load this patch as a child of (empty string to load + * to the root patch) + * + * @param name Name of this patch (loaded/generated if the empty string) + * @param poly Polyphony of this patch (loaded/generated if 0) + * + * @param initial_data will be set last, so values passed there will override + * any values loaded from the patch file. + * + * @param existing If true, the patch will be loaded into a currently + * existing patch (ie a merging will take place). Errors will result + * if Nodes of conflicting names exist. + * + * Returns the path of the newly created patch. + */ +string +DeprecatedLoader::load_patch(const Glib::ustring& filename, + boost::optional parent_path, + string name, + GraphObject::Variables initial_data, + bool existing) +{ + cerr << "[DeprecatedLoader] Loading patch " << filename << " under " + << parent_path << " / " << name << endl; + + Path path = parent_path ? (parent_path.get().base() + name) + : "/" + name; + + const bool load_name = (name == ""); + + size_t poly = 0; + + /* Use parameter overridden polyphony, if given */ + GraphObject::Variables::iterator poly_param = initial_data.find("ingen:polyphony"); + if (poly_param != initial_data.end() && poly_param->second.type() == Atom::INT) + poly = poly_param->second.get_int32(); + + if (initial_data.find("filename") == initial_data.end()) + initial_data["filename"] = Atom(filename.c_str()); // FIXME: URL? + + xmlDocPtr doc = xmlParseFile(filename.c_str()); + + if (!doc) { + cerr << "Unable to parse patch file." << endl; + return ""; + } + + xmlNodePtr cur = xmlDocGetRootElement(doc); + + if (!cur) { + cerr << "Empty document." << endl; + xmlFreeDoc(doc); + return ""; + } + + if (xmlStrcmp(cur->name, (const xmlChar*) "patch")) { + cerr << "File is not an Ingen patch file (root node != )" << endl; + xmlFreeDoc(doc); + return ""; + } + + xmlChar* key = NULL; + cur = cur->xmlChildrenNode; + + // Load Patch attributes + while (cur != NULL) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + + if ((!xmlStrcmp(cur->name, (const xmlChar*)"name"))) { + if (load_name && key) { + if (parent_path) + path = Path(parent_path.get()).base() + nameify_if_invalid((char*)key); + else + path = Path("/"); + } + } else if ((!xmlStrcmp(cur->name, (const xmlChar*)"polyphony"))) { + if (poly == 0) { + poly = atoi((char*)key); + } + } else if (xmlStrcmp(cur->name, (const xmlChar*)"connection") + && xmlStrcmp(cur->name, (const xmlChar*)"node") + && xmlStrcmp(cur->name, (const xmlChar*)"subpatch") + && xmlStrcmp(cur->name, (const xmlChar*)"filename") + && xmlStrcmp(cur->name, (const xmlChar*)"preset")) { + // Don't know what this tag is, add it as variable without overwriting + // (so caller can set arbitrary parameters which will be preserved) + if (key) + add_variable(initial_data, (const char*)cur->name, (const char*)key); + } + + xmlFree(key); + key = NULL; // Avoid a (possible?) double free + + cur = cur->next; + } + + if (poly == 0) + poly = 1; + + cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!! LOADING " << path << endl; + + // Create it, if we're not merging + if (!existing && path != "/") { + _engine->new_patch(path, poly); + for (GraphObject::Variables::const_iterator i = initial_data.begin(); i != initial_data.end(); ++i) + _engine->set_variable(path, i->first, i->second); + } + + // Load nodes + cur = xmlDocGetRootElement(doc)->xmlChildrenNode; + while (cur != NULL) { + if ((!xmlStrcmp(cur->name, (const xmlChar*)"node"))) + load_node(path, doc, cur); + + cur = cur->next; + } + + // Load subpatches + cur = xmlDocGetRootElement(doc)->xmlChildrenNode; + while (cur != NULL) { + if ((!xmlStrcmp(cur->name, (const xmlChar*)"subpatch"))) { + load_subpatch(filename.substr(0, filename.find_last_of("/")), path, doc, cur); + } + cur = cur->next; + } + + // Load connections + cur = xmlDocGetRootElement(doc)->xmlChildrenNode; + while (cur != NULL) { + if ((!xmlStrcmp(cur->name, (const xmlChar*)"connection"))) { + load_connection(path, doc, cur); + } + cur = cur->next; + } + + // Load presets (control values) + cur = xmlDocGetRootElement(doc)->xmlChildrenNode; + while (cur != NULL) { + // I don't think Om ever wrote any preset other than "default"... + if ((!xmlStrcmp(cur->name, (const xmlChar*)"preset"))) { + SharedPtr pm = load_preset(path, doc, cur); + assert(pm != NULL); + if (pm->name() == "default") { + list::const_iterator i = pm->controls().begin(); + for ( ; i != pm->controls().end(); ++i) { + const float value = i->value(); + _engine->set_port_value(translate_load_path(i->port_path()), Atom(value)); + } + } else { + cerr << "WARNING: Unknown preset: \"" << pm->name() << endl; + } + } + cur = cur->next; + } + + xmlFreeDoc(doc); + xmlCleanupParser(); + + // Done above.. late enough? + //for (Variables::const_iterator i = data.begin(); i != data.end(); ++i) + // _engine->set_variable(subject, i->first, i->second); + + if (!existing) + _engine->set_property(path, "ingen:enabled", (bool)true); + + _load_path_translations.clear(); + + return path; +} + + +/** Build a NodeModel given a pointer to a Node in a patch file. + */ +bool +DeprecatedLoader::load_node(const Path& parent, xmlDocPtr doc, const xmlNodePtr node) +{ + xmlChar* key; + xmlNodePtr cur = node->xmlChildrenNode; + + string path = ""; + bool polyphonic = false; + + string plugin_uri; + + string plugin_type; // deprecated + string library_name; // deprecated + string plugin_label; // deprecated + + GraphObject::Variables initial_data; + + while (cur != NULL) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + + if ((!xmlStrcmp(cur->name, (const xmlChar*)"name"))) { + path = parent.base() + nameify_if_invalid((char*)key); + } else if ((!xmlStrcmp(cur->name, (const xmlChar*)"polyphonic"))) { + polyphonic = !strcmp((char*)key, "true"); + } else if ((!xmlStrcmp(cur->name, (const xmlChar*)"type"))) { + plugin_type = (const char*)key; + } else if ((!xmlStrcmp(cur->name, (const xmlChar*)"library-name"))) { + library_name = (char*)key; + } else if ((!xmlStrcmp(cur->name, (const xmlChar*)"plugin-label"))) { + plugin_label = (char*)key; + } else if ((!xmlStrcmp(cur->name, (const xmlChar*)"plugin-uri"))) { + plugin_uri = (char*)key; + } else if ((!xmlStrcmp(cur->name, (const xmlChar*)"port"))) { + cerr << "FIXME: load port\n"; +#if 0 + xmlNodePtr child = cur->xmlChildrenNode; + + string port_name; + float user_min = 0.0; + float user_max = 0.0; + + while (child != NULL) { + key = xmlNodeListGetString(doc, child->xmlChildrenNode, 1); + + if ((!xmlStrcmp(child->name, (const xmlChar*)"name"))) { + port_name = nameify_if_invalid((char*)key); + } else if ((!xmlStrcmp(child->name, (const xmlChar*)"user-min"))) { + user_min = atof((char*)key); + } else if ((!xmlStrcmp(child->name, (const xmlChar*)"user-max"))) { + user_max = atof((char*)key); + } + + xmlFree(key); + key = NULL; // Avoid a (possible?) double free + + child = child->next; + } + + assert(path.length() > 0); + assert(Path::is_valid(path)); + + // FIXME: /nasty/ assumptions + SharedPtr pm(new PortModel(Path(path).base() + port_name, + PortModel::CONTROL, PortModel::INPUT, PortModel::NONE, + 0.0, user_min, user_max)); + //pm->set_parent(nm); + nm->add_port(pm); +#endif + + } else { // Don't know what this tag is, add it as variable + if (key) + add_variable(initial_data, (const char*)cur->name, (const char*)key); + } + xmlFree(key); + key = NULL; + + cur = cur->next; + } + + if (path == "") { + cerr << "[DeprecatedLoader] Malformed patch file (node tag has empty children)" << endl; + cerr << "[DeprecatedLoader] Node ignored." << endl; + return false; + } + + // Compatibility hacks for old patches that represent patch ports as nodes + if (plugin_uri == "") { + bool is_port = false; + + if (plugin_type == "Internal") { + // FIXME: indices + if (plugin_label == "audio_input") { + _engine->new_port(path, 0, "ingen:AudioPort", false); + is_port = true; + } else if (plugin_label == "audio_output") { + _engine->new_port(path, 0, "ingen:AudioPort", true); + is_port = true; + } else if (plugin_label == "control_input") { + _engine->new_port(path, 0, "ingen:ControlPort", false); + is_port = true; + } else if (plugin_label == "control_output" ) { + _engine->new_port(path, 0, "ingen:ControlPort", true); + is_port = true; + } else if (plugin_label == "midi_input") { + _engine->new_port(path, 0, "ingen:MIDIPort", false); + is_port = true; + } else if (plugin_label == "midi_output" ) { + _engine->new_port(path, 0, "ingen:MIDIPort", true); + is_port = true; + } else { + cerr << "WARNING: Unknown internal plugin label \"" << plugin_label << "\"" << endl; + } + } + + if (is_port) { + const string old_path = path; + const string new_path = (Path::is_valid(old_path) ? old_path : Path::pathify(old_path)); + + if (!Path::is_valid(old_path)) + cerr << "WARNING: Translating invalid port path \"" << old_path << "\" => \"" + << new_path << "\"" << endl; + + // Set up translations (for connections etc) to alias both the old + // module path and the old module/port path to the new port path + _load_path_translations[old_path] = new_path; + _load_path_translations[old_path + "/in"] = new_path; + _load_path_translations[old_path + "/out"] = new_path; + + path = new_path; + + for (GraphObject::Variables::const_iterator i = initial_data.begin(); i != initial_data.end(); ++i) + _engine->set_variable(path, i->first, i->second); + + return SharedPtr(); + + } else { + if (plugin_label == "note_in") { + plugin_uri = NS_INGEN "note_node"; + } else if (plugin_label == "control_input") { + plugin_uri = NS_INGEN "control_node"; + } else if (plugin_label == "transport") { + plugin_uri = NS_INGEN "transport_node"; + } else if (plugin_label == "trigger_in") { + plugin_uri = NS_INGEN "trigger_node"; + } else { + cerr << "WARNING: Unknown deprecated node (label " << plugin_label + << ")." << endl; + } + + if (plugin_uri != "") + _engine->new_node(path, plugin_uri); + else + _engine->new_node_deprecated(path, plugin_type, library_name, plugin_label); + + _engine->set_property(path, "ingen:polyphonic", polyphonic); + + for (GraphObject::Variables::const_iterator i = initial_data.begin(); i != initial_data.end(); ++i) + _engine->set_variable(path, i->first, i->second); + + return true; + } + + // Not deprecated + } else { + _engine->new_node(path, plugin_uri); + _engine->set_property(path, "ingen:polyphonic", polyphonic); + for (GraphObject::Variables::const_iterator i = initial_data.begin(); i != initial_data.end(); ++i) + _engine->set_variable(path, i->first, i->second); + return true; + } + + // (shouldn't get here) +} + + +bool +DeprecatedLoader::load_subpatch(const string& base_filename, const Path& parent, xmlDocPtr doc, const xmlNodePtr subpatch) +{ + xmlChar *key; + xmlNodePtr cur = subpatch->xmlChildrenNode; + + string name = ""; + string filename = ""; + size_t poly = 0; + + GraphObject::Variables initial_data; + + while (cur != NULL) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + + if ((!xmlStrcmp(cur->name, (const xmlChar*)"name"))) { + name = (const char*)key; + } else if ((!xmlStrcmp(cur->name, (const xmlChar*)"polyphony"))) { + initial_data.insert(make_pair("ingen::polyphony", (int)poly)); + } else if ((!xmlStrcmp(cur->name, (const xmlChar*)"filename"))) { + filename = base_filename + "/" + (const char*)key; + } else { // Don't know what this tag is, add it as variable + if (key != NULL && strlen((const char*)key) > 0) + add_variable(initial_data, (const char*)cur->name, (const char*)key); + } + xmlFree(key); + key = NULL; + + cur = cur->next; + } + + cout << "Loading subpatch " << filename << " under " << parent << endl; + // load_patch sets the passed variable last, so values stored in the parent + // will override values stored in the child patch file + /*string path = */load_patch(filename, parent, name, initial_data, false); + + return false; +} + + +/** Build a ConnectionModel given a pointer to a connection in a patch file. + */ +bool +DeprecatedLoader::load_connection(const Path& parent, xmlDocPtr doc, const xmlNodePtr node) +{ + xmlChar *key; + xmlNodePtr cur = node->xmlChildrenNode; + + string source_node, source_port, dest_node, dest_port; + + while (cur != NULL) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + + if ((!xmlStrcmp(cur->name, (const xmlChar*)"source-node"))) { + source_node = (char*)key; + } else if ((!xmlStrcmp(cur->name, (const xmlChar*)"source-port"))) { + source_port = (char*)key; + } else if ((!xmlStrcmp(cur->name, (const xmlChar*)"destination-node"))) { + dest_node = (char*)key; + } else if ((!xmlStrcmp(cur->name, (const xmlChar*)"destination-port"))) { + dest_port = (char*)key; + } + + xmlFree(key); + key = NULL; // Avoid a (possible?) double free + + cur = cur->next; + } + + if (source_node == "" || source_port == "" || dest_node == "" || dest_port == "") { + cerr << "ERROR: Malformed patch file (connection tag has empty children)" << endl; + cerr << "ERROR: Connection ignored." << endl; + return false; + } + + // Compatibility fixes for old (fundamentally broken) patches + source_node = nameify_if_invalid(source_node); + source_port = nameify_if_invalid(source_port); + dest_node = nameify_if_invalid(dest_node); + dest_port = nameify_if_invalid(dest_port); + + _engine->connect( + translate_load_path(parent.base() + source_node +"/"+ source_port), + translate_load_path(parent.base() + dest_node +"/"+ dest_port)); + + return true; +} + + +/** Build a PresetModel given a pointer to a preset in a patch file. + */ +SharedPtr +DeprecatedLoader::load_preset(const Path& parent, xmlDocPtr doc, const xmlNodePtr node) +{ + xmlNodePtr cur = node->xmlChildrenNode; + xmlChar* key; + + SharedPtr pm(new PresetModel(parent.base())); + + while (cur != NULL) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + + if ((!xmlStrcmp(cur->name, (const xmlChar*)"name"))) { + assert(key != NULL); + pm->name((char*)key); + } else if ((!xmlStrcmp(cur->name, (const xmlChar*)"control"))) { + xmlNodePtr child = cur->xmlChildrenNode; + + string node_name = "", port_name = ""; + float val = 0.0; + + while (child != NULL) { + key = xmlNodeListGetString(doc, child->xmlChildrenNode, 1); + + if ((!xmlStrcmp(child->name, (const xmlChar*)"node-name"))) { + node_name = (char*)key; + } else if ((!xmlStrcmp(child->name, (const xmlChar*)"port-name"))) { + port_name = (char*)key; + } else if ((!xmlStrcmp(child->name, (const xmlChar*)"value"))) { + val = atof((char*)key); + } + + xmlFree(key); + key = NULL; // Avoid a (possible?) double free + + child = child->next; + } + + // Compatibility fixes for old patch files + if (node_name != "") + node_name = nameify_if_invalid(node_name); + port_name = nameify_if_invalid(port_name); + + if (port_name == "") { + string msg = "Unable to parse control in patch file ( node = "; + msg.append(node_name).append(", port = ").append(port_name).append(")"); + cerr << "ERROR: " << msg << endl; + //m_client_hooks->error(msg); + } else { + // FIXME: temporary compatibility, remove any slashes from port name + // remove this soon once patches have migrated + string::size_type slash_index; + while ((slash_index = port_name.find("/")) != string::npos) + port_name[slash_index] = '-'; + + pm->add_control(node_name, port_name, val); + } + } + xmlFree(key); + key = NULL; + cur = cur->next; + } + if (pm->name() == "") { + cerr << "Preset in patch file has no name." << endl; + //m_client_hooks->error("Preset in patch file has no name."); + pm->name("Unnamed"); + } + + return pm; +} + +} // namespace Client +} // namespace Ingen diff --git a/src/client/DeprecatedLoader.hpp b/src/client/DeprecatedLoader.hpp new file mode 100644 index 00000000..c1af52c2 --- /dev/null +++ b/src/client/DeprecatedLoader.hpp @@ -0,0 +1,94 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PATCHLIBRARIAN_H +#define PATCHLIBRARIAN_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "interface/EngineInterface.hpp" +#include "interface/GraphObject.hpp" +#include "ObjectModel.hpp" + +using std::string; +using Ingen::Shared::EngineInterface; +using Ingen::Shared::GraphObject; + +namespace Ingen { +namespace Client { + +class PatchModel; +class NodeModel; +class ConnectionModel; +class PresetModel; // defined in DeprecatedLoader.cpp + + +/** Loads deprecated (XML) patch files (from the Om days). + * + * \ingroup IngenClient + */ +class DeprecatedLoader +{ +public: + DeprecatedLoader(SharedPtr engine) + : /*_patch_search_path(".")*/ _engine(engine) + { + assert(_engine); + } + + /*void path(const string& path) { _patch_search_path = path; } + const string& path() { return _patch_search_path; }*/ + + string find_file(const string& filename, const string& additional_path = ""); + + string load_patch(const Glib::ustring& filename, + boost::optional parent_path, + string name, + GraphObject::Variables initial_data, + bool existing = false); + +private: + void add_variable(GraphObject::Variables& data, string key, string value); + + string nameify_if_invalid(const string& name); + string translate_load_path(const string& path); + + //string _patch_search_path; + SharedPtr _engine; + + /// Translations of paths from the loading file to actual paths (for deprecated patches) + std::map _load_path_translations; + + bool load_node(const Path& parent, xmlDocPtr doc, const xmlNodePtr cur); + bool load_connection(const Path& parent, xmlDocPtr doc, const xmlNodePtr cur); + bool load_subpatch(const string& base_filename, const Path& parent, xmlDocPtr doc, const xmlNodePtr cur); + + SharedPtr load_preset(const Path& parent, xmlDocPtr doc, const xmlNodePtr cur); +}; + + +} // namespace Client +} // namespace Ingen + +#endif // PATCHLIBRARIAN_H diff --git a/src/client/HTTPClientReceiver.cpp b/src/client/HTTPClientReceiver.cpp new file mode 100644 index 00000000..ece55ab2 --- /dev/null +++ b/src/client/HTTPClientReceiver.cpp @@ -0,0 +1,97 @@ +/* This file is part of Ingen. + * Copyright (C) 2008 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include "module/Module.hpp" +#include "HTTPClientReceiver.hpp" + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Client { + + +HTTPClientReceiver::HTTPClientReceiver( + Shared::World* world, + const std::string& url, + SharedPtr target) + : _target(target) + , _world(world) + , _url(url) + , _session(NULL) +{ + start(false); +} + + +HTTPClientReceiver::~HTTPClientReceiver() +{ + stop(); +} + + +void +HTTPClientReceiver::message_callback(SoupSession* session, SoupMessage* msg, void* ptr) +{ + HTTPClientReceiver* me = (HTTPClientReceiver*)ptr; + cout << "RECEIVED ASYNC MESSAGE: " << msg->response_body->data << endl; + me->_target->response_ok(0); + me->_target->enable(); + me->_parser->parse_string(me->_world, me->_target.get(), Glib::ustring(msg->response_body->data), + Glib::ustring("/"), Glib::ustring("")); +} + + +void +HTTPClientReceiver::start(bool dump) +{ + Glib::Mutex::Lock lock(_world->rdf_world->mutex()); + if (!_parser) { + if (!_world->serialisation_module) + _world->serialisation_module = Ingen::Shared::load_module("ingen_serialisation"); + + if (_world->serialisation_module) { + Parser* (*new_parser)() = NULL; + if (_world->serialisation_module->get_symbol("new_parser", (void*&)new_parser)) + _parser = SharedPtr(new_parser()); + } + } + _session = soup_session_async_new(); + SoupMessage* msg = soup_message_new("GET", _url.c_str()); + soup_session_queue_message (_session, msg, message_callback, this); +} + + +void +HTTPClientReceiver::stop() +{ + if (_session != NULL) { + //unregister_client(); + soup_session_abort(_session); + _session = NULL; + } +} + + +} // namespace Client +} // namespace Ingen diff --git a/src/client/HTTPClientReceiver.hpp b/src/client/HTTPClientReceiver.hpp new file mode 100644 index 00000000..bab55578 --- /dev/null +++ b/src/client/HTTPClientReceiver.hpp @@ -0,0 +1,62 @@ +/* This file is part of Ingen. + * Copyright (C) 2008 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef HTTPCLIENTRECEIVER_H +#define HTTPCLIENTRECEIVER_H + +#include +#include +#include +#include "interface/ClientInterface.hpp" +#include "serialisation/Parser.hpp" +#include "redlandmm/World.hpp" +#include "raul/Deletable.hpp" + +namespace Ingen { +namespace Client { + + +class HTTPClientReceiver : public boost::noncopyable, public Raul::Deletable +{ +public: + HTTPClientReceiver(Shared::World* world, + const std::string& url, + SharedPtr target); + + ~HTTPClientReceiver(); + + std::string uri() const { return _url; } + + void start(bool dump); + void stop(); + +private: + static void message_callback(SoupSession* session, SoupMessage* msg, void* ptr); + + SharedPtr _target; + + Shared::World* _world; + const std::string _url; + SoupSession* _session; + SharedPtr _parser; +}; + + +} // namespace Client +} // namespace Ingen + +#endif // HTTPCLIENTRECEIVER_H diff --git a/src/client/HTTPEngineSender.cpp b/src/client/HTTPEngineSender.cpp new file mode 100644 index 00000000..733e0ac7 --- /dev/null +++ b/src/client/HTTPEngineSender.cpp @@ -0,0 +1,285 @@ +/* This file is part of Ingen. + * Copyright (C) 2008 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "HTTPEngineSender.hpp" + +using namespace std; + +namespace Ingen { +namespace Client { + + +HTTPEngineSender::HTTPEngineSender(const string& engine_url) + : _engine_url(engine_url) + , _id(0) + , _enabled(true) +{ + _session = soup_session_sync_new(); +} + + +HTTPEngineSender::~HTTPEngineSender() +{ + soup_session_abort(_session); +} + + +void +HTTPEngineSender::attach(int32_t ping_id, bool block) +{ + /*SoupMessage *msg; + msg = soup_message_new ("GET", _engine_url.c_str()); + int status = soup_session_send_message (_session, msg); + cout << "STATUS: " << status << endl; + cout << "RESPONSE: " << msg->response_body->data << endl;*/ +} + + +/* *** EngineInterface implementation below here *** */ + + +/** Register with the engine via HTTP. + * + * Note that this does not actually use 'key', since the engine creates + * it's own key for HTTP clients (namely the incoming URL), for NAT + * traversal. It is a parameter to remain compatible with EngineInterface. + */ +void +HTTPEngineSender::register_client(ClientInterface* client) +{ + +} + + +void +HTTPEngineSender::unregister_client(const string& uri) +{ + +} + + +// Engine commands +void +HTTPEngineSender::load_plugins() +{ + +} + + +void +HTTPEngineSender::activate() +{ + +} + + +void +HTTPEngineSender::deactivate() +{ + +} + + +void +HTTPEngineSender::quit() +{ + +} + + + +// Object commands + +void +HTTPEngineSender::new_patch(const string& path, + uint32_t poly) +{ +} + + +void +HTTPEngineSender::new_port(const string& path, + uint32_t index, + const string& data_type, + bool is_output) +{ +} + + +void +HTTPEngineSender::new_node(const string& path, + const string& plugin_uri) +{ +} + + +/** Create a node using library name and plugin label (DEPRECATED). + * + * DO NOT USE THIS. + */ +void +HTTPEngineSender::new_node_deprecated(const string& path, + const string& plugin_type, + const string& library_name, + const string& plugin_label) +{ +} + + +void +HTTPEngineSender::rename(const string& old_path, + const string& new_name) +{ +} + + +void +HTTPEngineSender::destroy(const string& path) +{ +} + + +void +HTTPEngineSender::clear_patch(const string& patch_path) +{ +} + + +void +HTTPEngineSender::connect(const string& src_port_path, + const string& dst_port_path) +{ +} + + +void +HTTPEngineSender::disconnect(const string& src_port_path, + const string& dst_port_path) +{ +} + + +void +HTTPEngineSender::disconnect_all(const string& parent_patch_path, + const string& node_path) +{ +} + + +void +HTTPEngineSender::set_port_value(const string& port_path, + const Raul::Atom& value) +{ +} + + +void +HTTPEngineSender::set_voice_value(const string& port_path, + uint32_t voice, + const Raul::Atom& value) +{ +} + + +void +HTTPEngineSender::set_program(const string& node_path, + uint32_t bank, + uint32_t program) +{ +} + + +void +HTTPEngineSender::midi_learn(const string& node_path) +{ +} + + +void +HTTPEngineSender::set_variable(const string& obj_path, + const string& predicate, + const Raul::Atom& value) +{ +} + + +void +HTTPEngineSender::set_property(const string& obj_path, + const string& predicate, + const Raul::Atom& value) +{ +} + + + +// Requests // + +void +HTTPEngineSender::ping() +{ +} + + +void +HTTPEngineSender::request_plugin(const string& uri) +{ +} + + +void +HTTPEngineSender::request_object(const string& path) +{ +} + + +void +HTTPEngineSender::request_port_value(const string& port_path) +{ +} + + +void +HTTPEngineSender::request_variable(const string& object_path, const string& key) +{ +} + + +void +HTTPEngineSender::request_property(const string& object_path, const string& key) +{ +} + + +void +HTTPEngineSender::request_plugins() +{ +} + + +void +HTTPEngineSender::request_all_objects() +{ +} + + + +} // namespace Client +} // namespace Ingen + + diff --git a/src/client/HTTPEngineSender.hpp b/src/client/HTTPEngineSender.hpp new file mode 100644 index 00000000..411ddfd5 --- /dev/null +++ b/src/client/HTTPEngineSender.hpp @@ -0,0 +1,155 @@ +/* This file is part of Ingen. + * Copyright (C) 2008 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef HTTPENGINESENDER_H +#define HTTPENGINESENDER_H + +#include +#include +#include +#include "interface/EngineInterface.hpp" +using std::string; +using Ingen::Shared::EngineInterface; +using Ingen::Shared::ClientInterface; + +namespace Ingen { +namespace Client { + + +/* HTTP (via libsoup) interface to the engine. + * + * Clients can use this opaquely as an EngineInterface to control the engine + * over HTTP (whether over a network or not). + * + * \ingroup IngenClient + */ +class HTTPEngineSender : public EngineInterface { +public: + HTTPEngineSender(const string& engine_url); + ~HTTPEngineSender(); + + string uri() const { return _engine_url; } + + inline int32_t next_id() + { int32_t ret = (_id == -1) ? -1 : _id++; return ret; } + + void set_next_response_id(int32_t id) { _id = id; } + void disable_responses() { _id = -1; } + + void attach(int32_t ping_id, bool block); + + + /* *** EngineInterface implementation below here *** */ + + void enable() { _enabled = true; } + void disable() { _enabled = false; } + + void bundle_begin() { transfer_begin(); } + void bundle_end() { transfer_end(); } + + void transfer_begin(); + void transfer_end(); + + // Client registration + void register_client(ClientInterface* client); + void unregister_client(const string& uri); + + // Engine commands + void load_plugins(); + void activate(); + void deactivate(); + void quit(); + + // Object commands + + void new_patch(const string& path, + uint32_t poly); + + void new_port(const string& path, + uint32_t index, + const string& data_type, + bool is_output); + + void new_node(const string& path, + const string& plugin_uri); + + void new_node_deprecated(const string& path, + const string& plugin_type, + const string& library_name, + const string& plugin_label); + + void rename(const string& old_path, + const string& new_name); + + void destroy(const string& path); + + void clear_patch(const string& patch_path); + + void connect(const string& src_port_path, + const string& dst_port_path); + + void disconnect(const string& src_port_path, + const string& dst_port_path); + + void disconnect_all(const string& parent_patch_path, + const string& node_path); + + void set_port_value(const string& port_path, + const Raul::Atom& value); + + void set_voice_value(const string& port_path, + uint32_t voice, + const Raul::Atom& value); + + void set_program(const string& node_path, + uint32_t bank, + uint32_t program); + + void midi_learn(const string& node_path); + + void set_variable(const string& obj_path, + const string& predicate, + const Raul::Atom& value); + + void set_property(const string& obj_path, + const string& predicate, + const Raul::Atom& value); + + // Requests // + void ping(); + void request_plugin(const string& uri); + void request_object(const string& path); + void request_port_value(const string& port_path); + void request_variable(const string& path, const string& key); + void request_property(const string& path, const string& key); + void request_plugins(); + void request_all_objects(); + +protected: + SoupSession* _session; + const string _engine_url; + int _client_port; + int32_t _id; + bool _enabled; +}; + + +} // namespace Client +} // namespace Ingen + +#endif // HTTPENGINESENDER_H + diff --git a/src/client/Makefile.am b/src/client/Makefile.am new file mode 100644 index 00000000..4af7c243 --- /dev/null +++ b/src/client/Makefile.am @@ -0,0 +1,69 @@ +if BUILD_CLIENT_LIB + + +moduledir = $(libdir)/ingen + +module_LTLIBRARIES = libingen_client.la + +libingen_client_la_CXXFLAGS = \ + -DPKGDATADIR=\"$(pkgdatadir)\" \ + @INGEN_CFLAGS@ \ + @GLIBMM_CFLAGS@ \ + @LIBLO_CFLAGS@ \ + @LSIGCPP_CFLAGS@ \ + @GLIBMM_CFLAGS@ \ + @LXML2_CFLAGS@ \ + @RAUL_CFLAGS@ \ + @REDLANDMM_CFLAGS@ \ + @SLV2_CFLAGS@ \ + @SOUP_CFLAGS@ + +libingen_client_la_LIBADD = \ + ../shared/libingen_shared.la \ + @GLIBMM_LIBS@ \ + @LIBLO_LIBS@ \ + @LSIGCPP_LIBS@ \ + @LXML2_LIBS@ \ + @RAUL_LIBS@ \ + @REDLANDMM_LIBS@ \ + @SLV2_LIBS@ \ + @SOUP_LIBS@ + +libingen_client_la_SOURCES = \ + ClientStore.cpp \ + ClientStore.hpp \ + ConnectionModel.hpp \ + DeprecatedLoader.cpp \ + DeprecatedLoader.hpp \ + NodeModel.cpp \ + NodeModel.hpp \ + OSCClientReceiver.cpp \ + OSCClientReceiver.hpp \ + OSCEngineSender.cpp \ + OSCEngineSender.hpp \ + ObjectModel.cpp \ + ObjectModel.hpp \ + PatchModel.cpp \ + PatchModel.hpp \ + PluginModel.cpp \ + PluginModel.hpp \ + PluginUI.cpp \ + PluginUI.hpp \ + PortModel.cpp \ + PortModel.hpp \ + SigClientInterface.hpp \ + ThreadedSigClientInterface.cpp \ + ThreadedSigClientInterface.hpp \ + client.cpp \ + client.hpp + +if WITH_SOUP +libingen_client_la_SOURCES += \ + HTTPClientReceiver.cpp \ + HTTPClientReceiver.hpp \ + HTTPEngineSender.cpp \ + HTTPEngineSender.hpp +endif + +endif # BUILD_CLIENT_LIB + diff --git a/src/client/NodeModel.cpp b/src/client/NodeModel.cpp new file mode 100644 index 00000000..ac0c8e68 --- /dev/null +++ b/src/client/NodeModel.cpp @@ -0,0 +1,224 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include CONFIG_H_PATH + +#include +#include +#include "interface/Port.hpp" +#include "NodeModel.hpp" +#include "PatchModel.hpp" + +namespace Ingen { +namespace Client { + + +NodeModel::NodeModel(SharedPtr plugin, const Path& path) + : ObjectModel(path) + , _plugin_uri(plugin->uri()) + , _plugin(plugin) + , _num_values(0) + , _min_values(0) + , _max_values(0) +{ +} + +NodeModel::NodeModel(const string& plugin_uri, const Path& path) + : ObjectModel(path) + , _plugin_uri(plugin_uri) + , _num_values(0) + , _min_values(0) + , _max_values(0) +{ +} + + +NodeModel::NodeModel(const NodeModel& copy) + : ObjectModel(copy) + , _plugin_uri(copy._plugin_uri) + , _num_values(copy._num_values) + , _min_values((float*)malloc(sizeof(float) * _num_values)) + , _max_values((float*)malloc(sizeof(float) * _num_values)) +{ + memcpy(_min_values, copy._min_values, sizeof(float) * _num_values); + memcpy(_max_values, copy._max_values, sizeof(float) * _num_values); +} + + +NodeModel::~NodeModel() +{ + clear(); +} + + +void +NodeModel::remove_port(SharedPtr port) +{ + // FIXME: slow + for (Ports::iterator i = _ports.begin(); i != _ports.end(); ++i) { + if ((*i) == port) { + _ports.erase(i); + break; + } + } + signal_removed_port.emit(port); +} + + +void +NodeModel::remove_port(const Path& port_path) +{ + // FIXME: slow + for (Ports::iterator i = _ports.begin(); i != _ports.end(); ++i) { + if ((*i)->path() == port_path) { + _ports.erase(i); + break; + } + } +} + + +void +NodeModel::clear() +{ + _ports.clear(); + assert(_ports.empty()); + delete[] _min_values; + delete[] _max_values; + _min_values = 0; + _max_values = 0; +} + + +void +NodeModel::add_child(SharedPtr c) +{ + assert(c->parent().get() == this); + + //ObjectModel::add_child(c); + + SharedPtr pm = PtrCast(c); + assert(pm); + add_port(pm); +} + + +bool +NodeModel::remove_child(SharedPtr c) +{ + assert(c->path().is_child_of(_path)); + assert(c->parent().get() == this); + + //bool ret = ObjectModel::remove_child(c); + + SharedPtr pm = PtrCast(c); + assert(pm); + remove_port(pm); + + //return ret; + return true; +} + + +void +NodeModel::add_port(SharedPtr pm) +{ + assert(pm); + assert(pm->path().is_child_of(_path)); + assert(pm->parent().get() == this); + + Ports::iterator existing = find(_ports.begin(), _ports.end(), pm); + + // Store should have handled this by merging the two + assert(existing == _ports.end()); + + _ports.push_back(pm); + signal_new_port.emit(pm); +} + + +SharedPtr +NodeModel::get_port(const string& port_name) const +{ + assert(port_name.find("/") == string::npos); + for (Ports::const_iterator i = _ports.begin(); i != _ports.end(); ++i) + if ((*i)->path().name() == port_name) + return (*i); + return SharedPtr(); +} + + +Shared::Port* +NodeModel::port(uint32_t index) const +{ + assert(index < num_ports()); + return dynamic_cast(_ports[index].get()); +} + + +void +NodeModel::port_value_range(SharedPtr port, float& min, float& max) +{ + assert(port->parent().get() == this); + +#ifdef HAVE_SLV2 + // Plugin value first + if (_plugin && _plugin->type() == PluginModel::LV2) { + + if (!_min_values) { + + Glib::Mutex::Lock lock(PluginModel::rdf_world()->mutex()); + + _num_values = slv2_plugin_get_num_ports(_plugin->slv2_plugin()); + _min_values = new float[_num_values]; + _max_values = new float[_num_values]; + slv2_plugin_get_port_ranges_float(_plugin->slv2_plugin(), + _min_values, _max_values, 0); + } + + if (!std::isnan(_min_values[port->index()])) + min = _min_values[port->index()]; + if (!std::isnan(_max_values[port->index()])) + max = _max_values[port->index()]; + } +#endif + + // Possibly overriden + const Atom& min_atom = port->get_variable("ingen:minimum"); + const Atom& max_atom = port->get_variable("ingen:maximum"); + if (min_atom.type() == Atom::FLOAT) + min = min_atom.get_float(); + if (max_atom.type() == Atom::FLOAT) + max = max_atom.get_float(); +} + + +void +NodeModel::set(SharedPtr model) +{ + SharedPtr node = PtrCast(model); + if (node) { + _plugin_uri = node->_plugin_uri; + _plugin = node->_plugin; + } + + ObjectModel::set(model); +} + + +} // namespace Client +} // namespace Ingen diff --git a/src/client/NodeModel.hpp b/src/client/NodeModel.hpp new file mode 100644 index 00000000..03afc17c --- /dev/null +++ b/src/client/NodeModel.hpp @@ -0,0 +1,101 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef NODEMODEL_H +#define NODEMODEL_H + +#include +#include +#include +#include +#include +#include +#include +#include "interface/Node.hpp" +#include "interface/Port.hpp" +#include "ObjectModel.hpp" +#include "PortModel.hpp" +#include "PluginModel.hpp" + +using std::string; +using Raul::Table; + +namespace Ingen { +namespace Client { + +class PluginModel; +class ClientStore; + + +/** Node model class, used by the client to store engine's state. + * + * \ingroup IngenClient + */ +class NodeModel : public ObjectModel, virtual public Ingen::Shared::Node +{ +public: + NodeModel(const NodeModel& copy); + virtual ~NodeModel(); + + typedef vector > Ports; + + SharedPtr get_port(const string& port_name) const; + + Shared::Port* port(uint32_t index) const; + + const string& plugin_uri() const { return _plugin_uri; } + const Shared::Plugin* plugin() const { return _plugin.get(); } + uint32_t num_ports() const { return _ports.size(); } + const Ports& ports() const { return _ports; } + + void port_value_range(SharedPtr port, float& min, float& max); + + // Signals + sigc::signal > signal_new_port; + sigc::signal > signal_removed_port; + +protected: + friend class ClientStore; + + NodeModel(const string& plugin_uri, const Path& path); + NodeModel(SharedPtr plugin, const Path& path); + + NodeModel(const Path& path); + void add_child(SharedPtr c); + bool remove_child(SharedPtr c); + void add_port(SharedPtr pm); + void remove_port(SharedPtr pm); + void remove_port(const Path& port_path); + void add_program(int bank, int program, const string& name); + void remove_program(int bank, int program); + void set(SharedPtr model); + + virtual void clear(); + + Ports _ports; ///< Vector of ports (not a Table to preserve order) + string _plugin_uri; ///< Plugin URI (if PluginModel is unknown) + SharedPtr _plugin; ///< The plugin this node is an instance of + uint32_t _num_values; ///< Size of _min_values and _max_values + float* _min_values; ///< Port min values (cached for LV2) + float* _max_values; ///< Port max values (cached for LV2) +}; + + +} // namespace Client +} // namespace Ingen + +#endif // NODEMODEL_H diff --git a/src/client/OSCClientReceiver.cpp b/src/client/OSCClientReceiver.cpp new file mode 100644 index 00000000..8ebd9d8e --- /dev/null +++ b/src/client/OSCClientReceiver.cpp @@ -0,0 +1,394 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "OSCClientReceiver.hpp" +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Client { + + +OSCClientReceiver::OSCClientReceiver(int listen_port, SharedPtr target) + : _target(target) + , _listen_port(listen_port) + , _st(NULL) +{ + start(false); // true = dump, false = shutup +} + + +OSCClientReceiver::~OSCClientReceiver() +{ + stop(); +} + + +void +OSCClientReceiver::start(bool dump_osc) +{ + if (_st != NULL) + return; + + // Attempt preferred port + if (_listen_port != 0) { + char port_str[8]; + snprintf(port_str, 8, "%d", _listen_port); + _st = lo_server_thread_new(port_str, lo_error_cb); + } + + // Find a free port + if (!_st) { + _st = lo_server_thread_new(NULL, lo_error_cb); + _listen_port = lo_server_thread_get_port(_st); + } + + if (_st == NULL) { + cerr << "[OSCClientReceiver] Could not start OSC listener. Aborting." << endl; + exit(EXIT_FAILURE); + } else { + cout << "[OSCClientReceiver] Started OSC listener on port " << lo_server_thread_get_port(_st) << endl; + } + + // Print all incoming messages + if (dump_osc) + lo_server_thread_add_method(_st, NULL, NULL, generic_cb, NULL); + + setup_callbacks(); + + // Display any uncaught messages to the console + //lo_server_thread_add_method(_st, NULL, NULL, unknown_cb, NULL); + + lo_server_thread_start(_st); +} + + +void +OSCClientReceiver::stop() +{ + if (_st != NULL) { + //unregister_client(); + lo_server_thread_free(_st); + _st = NULL; + } +} + + +int +OSCClientReceiver::generic_cb(const char* path, const char* types, lo_arg** argv, int argc, void* data, void* user_data) +{ + printf("[OSCMsg] %s (%s)\t", path, types); + + for (int i=0; i < argc; ++i) { + lo_arg_pp(lo_type(types[i]), argv[i]); + printf("\t"); + } + printf("\n"); + + /*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 +} + + +void +OSCClientReceiver::lo_error_cb(int num, const char* msg, const char* path) +{ + cerr << "Got error from server: " << msg << endl; +} + + + +int +OSCClientReceiver::unknown_cb(const char* path, const char* types, lo_arg** argv, int argc, void* data, void* user_data) +{ + std::string msg = "Received unknown OSC message: "; + msg += path; + + cerr << msg << endl; + + return 0; +} + + +void +OSCClientReceiver::setup_callbacks() +{ + lo_server_thread_add_method(_st, "/ingen/ok", "i", response_ok_cb, this); + lo_server_thread_add_method(_st, "/ingen/error", "is", response_error_cb, this); + lo_server_thread_add_method(_st, "/ingen/plugin", "ssss", plugin_cb, this); + lo_server_thread_add_method(_st, "/ingen/new_patch", "si", new_patch_cb, this); + lo_server_thread_add_method(_st, "/ingen/destroyed", "s", destroyed_cb, this); + lo_server_thread_add_method(_st, "/ingen/patch_cleared", "s", patch_cleared_cb, this); + lo_server_thread_add_method(_st, "/ingen/object_renamed", "ss", object_renamed_cb, this); + lo_server_thread_add_method(_st, "/ingen/new_connection", "ss", connection_cb, this); + lo_server_thread_add_method(_st, "/ingen/disconnection", "ss", disconnection_cb, this); + lo_server_thread_add_method(_st, "/ingen/new_node", "ss", new_node_cb, this); + lo_server_thread_add_method(_st, "/ingen/new_port", "sisi", new_port_cb, this); + lo_server_thread_add_method(_st, "/ingen/set_variable", NULL, set_variable_cb, this); + lo_server_thread_add_method(_st, "/ingen/set_property", NULL, set_property_cb, this); + lo_server_thread_add_method(_st, "/ingen/set_port_value", "sf", set_port_value_cb, this); + lo_server_thread_add_method(_st, "/ingen/set_voice_value", "sif", set_voice_value_cb, this); + lo_server_thread_add_method(_st, "/ingen/port_activity", "s", port_activity_cb, this); + lo_server_thread_add_method(_st, "/ingen/program_add", "siis", program_add_cb, this); + lo_server_thread_add_method(_st, "/ingen/program_remove", "sii", program_remove_cb, this); +} + + +/** Catches errors that aren't a direct result of a client request. + */ +int +OSCClientReceiver::_error_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + _target->error((char*)argv[0]); + return 0; +} + + +int +OSCClientReceiver::_new_patch_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + _target->new_patch(&argv[0]->s, argv[1]->i); // path, poly + return 0; +} + + +int +OSCClientReceiver::_destroyed_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + _target->destroy((const char*)&argv[0]->s); + return 0; +} + + +int +OSCClientReceiver::_patch_cleared_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + _target->patch_cleared((const char*)&argv[0]->s); + return 0; +} + + +int +OSCClientReceiver::_object_renamed_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + _target->object_renamed((const char*)&argv[0]->s, (const char*)&argv[1]->s); + return 0; +} + + +int +OSCClientReceiver::_connection_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* const src_port_path = &argv[0]->s; + const char* const dst_port_path = &argv[1]->s; + + _target->connect(src_port_path, dst_port_path); + + return 0; +} + + +int +OSCClientReceiver::_disconnection_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* src_port_path = &argv[0]->s; + const char* dst_port_path = &argv[1]->s; + + _target->disconnect(src_port_path, dst_port_path); + + return 0; +} + + +/** Notification of a new node creation. + */ +int +OSCClientReceiver::_new_node_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* uri = &argv[0]->s; + const char* node_path = &argv[1]->s; + + _target->new_node(uri, node_path); + + return 0; +} + + +/** Notification of a new port creation. + */ +int +OSCClientReceiver::_new_port_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* port_path = &argv[0]->s; + const uint32_t index = argv[1]->i; + const char* type = &argv[2]->s; + const bool is_output = (argv[3]->i == 1); + + _target->new_port(port_path, index, type, is_output); + + return 0; +} + + +/** Notification of a new or updated variable. + */ +int +OSCClientReceiver::_set_variable_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + if (argc != 3 || types[0] != 's' || types[1] != 's') + return 1; + + const char* obj_path = &argv[0]->s; + const char* key = &argv[1]->s; + + Atom value = AtomLiblo::lo_arg_to_atom(types[2], argv[2]); + + _target->set_variable(obj_path, key, value); + + return 0; +} + + +/** Notification of a new or updated property. + */ +int +OSCClientReceiver::_set_property_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + if (argc != 3 || types[0] != 's' || types[1] != 's') + return 1; + + const char* obj_path = &argv[0]->s; + const char* key = &argv[1]->s; + + Atom value = AtomLiblo::lo_arg_to_atom(types[2], argv[2]); + + _target->set_property(obj_path, key, value); + + return 0; +} + + +int +OSCClientReceiver::_set_port_value_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* const port_path = &argv[0]->s; + const float value = argv[1]->f; + + _target->set_port_value(port_path, value); + + return 0; +} + + +int +OSCClientReceiver::_set_voice_value_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* const port_path = &argv[0]->s; + const int voice = argv[1]->i; + const float value = argv[2]->f; + + _target->set_voice_value(port_path, voice, value); + + return 0; +} + + +int +OSCClientReceiver::_port_activity_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* const port_path = &argv[0]->s; + + _target->port_activity(port_path); + + return 0; +} + + +int +OSCClientReceiver::_response_ok_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + assert(!strcmp(types, "i")); + _target->response_ok(argv[0]->i); + + return 0; +} + + +int +OSCClientReceiver::_response_error_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + assert(!strcmp(types, "is")); + _target->response_error(argv[0]->i, &argv[1]->s); + + return 0; +} + + +/** A plugin info response from the server, in response to an /ingen/send_plugins + */ +int +OSCClientReceiver::_plugin_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + assert(argc == 4 && !strcmp(types, "ssss")); + _target->new_plugin(&argv[0]->s, &argv[1]->s, &argv[2]->s, &argv[3]->s); // uri, type, symbol, name + + return 0; +} + + +int +OSCClientReceiver::_program_add_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* node_path = &argv[0]->s; + int32_t bank = argv[1]->i; + int32_t program = argv[2]->i; + const char* name = &argv[3]->s; + + _target->program_add(node_path, bank, program, name); + + return 0; +} + + +int +OSCClientReceiver::_program_remove_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* node_path = &argv[0]->s; + int32_t bank = argv[1]->i; + int32_t program = argv[2]->i; + + _target->program_remove(node_path, bank, program); + + return 0; +} + + +} // namespace Client +} // namespace Ingen diff --git a/src/client/OSCClientReceiver.hpp b/src/client/OSCClientReceiver.hpp new file mode 100644 index 00000000..ea5871b3 --- /dev/null +++ b/src/client/OSCClientReceiver.hpp @@ -0,0 +1,107 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef OSCCLIENTRECEIVER_H +#define OSCCLIENTRECEIVER_H + +#include +#include +#include +#include "interface/ClientInterface.hpp" +#include "raul/Deletable.hpp" + +namespace Ingen { +namespace Client { + +/** Arguments to a liblo handler */ +#define LO_HANDLER_ARGS const char* path, const char* types, lo_arg** argv, int argc, lo_message msg + +/** Define 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. */ +#define LO_HANDLER(name) \ +int _##name##_cb (LO_HANDLER_ARGS);\ +inline static int name##_cb(LO_HANDLER_ARGS, void* osc_listener)\ +{ return ((OSCClientReceiver*)osc_listener)->_##name##_cb(path, types, argv, argc, msg); } + + +/** Callbacks for "notification band" OSC messages. + * + * Receives all notification of engine state, but not replies on the "control + * band". See OSC namespace documentation for details. + * + * Right now this class and Comm share the same lo_server_thread and the barrier + * between them is a bit odd, but eventually this class will be able to listen + * on a completely different port (ie have it's own lo_server_thread) to allow + * things like listening to the notification band over TCP while sending commands + * on the control band over UDP. + * + * \ingroup IngenClient + */ +class OSCClientReceiver : public boost::noncopyable, public Raul::Deletable +{ +public: + OSCClientReceiver(int listen_port, SharedPtr target); + ~OSCClientReceiver(); + + std::string uri() const { return lo_server_thread_get_url(_st); } + + void start(bool dump_osc); + void stop(); + + int listen_port() { return _listen_port; } + std::string listen_url() { return lo_server_thread_get_url(_st); } + +private: + void setup_callbacks(); + + static void lo_error_cb(int num, const char* msg, const char* path); + + static int generic_cb(const char* path, const char* types, lo_arg** argv, int argc, void* data, void* user_data); + static int unknown_cb(const char* path, const char* types, lo_arg** argv, int argc, void* data, void* osc_receiver); + + SharedPtr _target; + + int _listen_port; + lo_server_thread _st; + + LO_HANDLER(error); + LO_HANDLER(response_ok); + LO_HANDLER(response_error); + LO_HANDLER(plugin); + LO_HANDLER(plugin_list_end); + LO_HANDLER(new_patch); + LO_HANDLER(destroyed); + LO_HANDLER(patch_cleared); + LO_HANDLER(object_renamed); + LO_HANDLER(connection); + LO_HANDLER(disconnection); + LO_HANDLER(new_node); + LO_HANDLER(new_port); + LO_HANDLER(set_variable); + LO_HANDLER(set_property); + LO_HANDLER(set_port_value); + LO_HANDLER(set_voice_value); + LO_HANDLER(port_activity); + LO_HANDLER(program_add); + LO_HANDLER(program_remove); +}; + + +} // namespace Client +} // namespace Ingen + +#endif // OSCCLIENTRECEIVER_H diff --git a/src/client/OSCEngineSender.cpp b/src/client/OSCEngineSender.cpp new file mode 100644 index 00000000..c21d16ce --- /dev/null +++ b/src/client/OSCEngineSender.cpp @@ -0,0 +1,420 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "OSCEngineSender.hpp" + +using namespace std; +using Raul::Atom; + +namespace Ingen { +namespace Client { + + +/** Note the sending port is implicitly set by liblo, lo_send by default sends + * from the most recently created server, so create the OSC listener before + * this to have it all happen on the same port. Yeah, this is a big magic :/ + */ +OSCEngineSender::OSCEngineSender(const string& engine_url) + : _engine_url(engine_url) + , _id(0) +{ + _address = lo_address_new_from_url(engine_url.c_str()); +} + + +OSCEngineSender::~OSCEngineSender() +{ + lo_address_free(_address); +} + + +/** Attempt to connect to the engine (by pinging it). + * + * This doesn't register a client (or otherwise affect the client/engine state). + * To check for success wait for the ping response with id @a ping_id (using the + * relevant OSCClientReceiver). + * + * Passing a client_port of 0 will automatically choose a free port. If the + * @a block parameter is true, this function will not return until a connection + * has successfully been made. + */ +void +OSCEngineSender::attach(int32_t ping_id, bool block) +{ + if (!_address) + _address = lo_address_new_from_url(_engine_url.c_str()); + + if (_address == NULL) { + cerr << "Aborting: Unable to connect to " << _engine_url << endl; + exit(EXIT_FAILURE); + } + + cout << "[OSCEngineSender] Attempting to contact engine at " << _engine_url << " ..." << endl; + + _id = ping_id; + this->ping(); +} + +/* *** EngineInterface implementation below here *** */ + + +/** Register with the engine via OSC. + * + * Note that this does not actually use 'key', since the engine creates + * it's own key for OSC clients (namely the incoming URL), for NAT + * traversal. It is a parameter to remain compatible with EngineInterface. + */ +void +OSCEngineSender::register_client(ClientInterface* client) +{ + // FIXME: use parameters.. er, somehow. + send("/ingen/register_client", "i", next_id(), LO_ARGS_END, LO_ARGS_END); +} + + +void +OSCEngineSender::unregister_client(const string& uri) +{ + send("/ingen/unregister_client", "i", next_id(), LO_ARGS_END); +} + + +// Engine commands +void +OSCEngineSender::load_plugins() +{ + send("/ingen/load_plugins", "i", next_id(), LO_ARGS_END); +} + + +void +OSCEngineSender::activate() +{ + send("/ingen/activate", "i", next_id(), LO_ARGS_END); +} + + +void +OSCEngineSender::deactivate() +{ + send("/ingen/deactivate", "i", next_id(), LO_ARGS_END); +} + + +void +OSCEngineSender::quit() +{ + send("/ingen/quit", "i", next_id(), LO_ARGS_END); +} + + + +// Object commands + +void +OSCEngineSender::new_patch(const string& path, + uint32_t poly) +{ + send("/ingen/new_patch", "isi", + next_id(), + path.c_str(), + poly, + LO_ARGS_END); +} + + +void +OSCEngineSender::new_port(const string& path, + uint32_t index, + const string& data_type, + bool is_output) +{ + // FIXME: use index + send("/ingen/new_port", "issi", + next_id(), + path.c_str(), + data_type.c_str(), + (is_output ? 1 : 0), + LO_ARGS_END); +} + + +void +OSCEngineSender::new_node(const string& path, + const string& plugin_uri) +{ + + send("/ingen/new_node", "iss", + next_id(), + path.c_str(), + plugin_uri.c_str(), + LO_ARGS_END); +} + + +/** Create a node using library name and plugin label (DEPRECATED). + * + * DO NOT USE THIS. + */ +void +OSCEngineSender::new_node_deprecated(const string& path, + const string& plugin_type, + const string& library_name, + const string& plugin_label) +{ + send("/ingen/new_node", "issss", + next_id(), + path.c_str(), + plugin_type.c_str(), + library_name.c_str(), + plugin_label.c_str(), + LO_ARGS_END); +} + + +void +OSCEngineSender::rename(const string& old_path, + const string& new_name) +{ + send("/ingen/rename", "iss", + next_id(), + old_path.c_str(), + new_name.c_str(), + LO_ARGS_END); +} + + +void +OSCEngineSender::destroy(const string& path) +{ + send("/ingen/destroy", "is", + next_id(), + path.c_str(), + LO_ARGS_END); +} + + +void +OSCEngineSender::clear_patch(const string& patch_path) +{ + send("/ingen/clear_patch", "is", + next_id(), + patch_path.c_str(), + LO_ARGS_END); +} + + +void +OSCEngineSender::connect(const string& src_port_path, + const string& dst_port_path) +{ + send("/ingen/connect", "iss", + next_id(), + src_port_path.c_str(), + dst_port_path.c_str(), + LO_ARGS_END); +} + + +void +OSCEngineSender::disconnect(const string& src_port_path, + const string& dst_port_path) +{ + send("/ingen/disconnect", "iss", + next_id(), + src_port_path.c_str(), + dst_port_path.c_str(), + LO_ARGS_END); +} + + +void +OSCEngineSender::disconnect_all(const string& parent_patch_path, + const string& node_path) +{ + send("/ingen/disconnect_all", "iss", + next_id(), + parent_patch_path.c_str(), + node_path.c_str(), + LO_ARGS_END); +} + + +void +OSCEngineSender::set_port_value(const string& port_path, + const Raul::Atom& value) +{ + lo_message m = lo_message_new(); + lo_message_add_int32(m, next_id()); + lo_message_add_string(m, port_path.c_str()); + if (value.type() == Atom::BLOB) + lo_message_add_string(m, value.get_blob_type()); + Raul::AtomLiblo::lo_message_add_atom(m, value); + send_message("/ingen/set_port_value", m); +} + + +void +OSCEngineSender::set_voice_value(const string& port_path, + uint32_t voice, + const Raul::Atom& value) +{ + lo_message m = lo_message_new(); + lo_message_add_int32(m, next_id()); + lo_message_add_string(m, port_path.c_str()); + lo_message_add_int32(m, voice); + if (value.type() == Atom::BLOB) + lo_message_add_string(m, value.get_blob_type()); + Raul::AtomLiblo::lo_message_add_atom(m, value); + send_message("/ingen/set_port_value", m); +} + + +void +OSCEngineSender::set_program(const string& node_path, + uint32_t bank, + uint32_t program) +{ + send((string("/dssi") + node_path + "/program").c_str(), + "ii", + bank, + program, + LO_ARGS_END); +} + + +void +OSCEngineSender::midi_learn(const string& node_path) +{ + send("/ingen/midi_learn", "is", + next_id(), + node_path.c_str(), + LO_ARGS_END); +} + + +void +OSCEngineSender::set_variable(const string& obj_path, + const string& predicate, + const Raul::Atom& value) +{ + lo_message m = lo_message_new(); + lo_message_add_int32(m, next_id()); + lo_message_add_string(m, obj_path.c_str()); + lo_message_add_string(m, predicate.c_str()); + Raul::AtomLiblo::lo_message_add_atom(m, value); + send_message("/ingen/set_variable", m); +} + + +void +OSCEngineSender::set_property(const string& obj_path, + const string& predicate, + const Raul::Atom& value) +{ + lo_message m = lo_message_new(); + lo_message_add_int32(m, next_id()); + lo_message_add_string(m, obj_path.c_str()); + lo_message_add_string(m, predicate.c_str()); + Raul::AtomLiblo::lo_message_add_atom(m, value); + send_message("/ingen/set_property", m); +} + + + +// Requests // + +void +OSCEngineSender::ping() +{ + send("/ingen/ping", "i", next_id(), LO_ARGS_END); +} + + +void +OSCEngineSender::request_plugin(const string& uri) +{ + send("/ingen/request_plugin", "is", + next_id(), + uri.c_str(), + LO_ARGS_END); +} + + +void +OSCEngineSender::request_object(const string& path) +{ + send("/ingen/request_object", "is", + next_id(), + path.c_str(), + LO_ARGS_END); +} + + +void +OSCEngineSender::request_port_value(const string& port_path) +{ + send("/ingen/request_port_value", "is", + next_id(), + port_path.c_str(), + LO_ARGS_END); +} + + +void +OSCEngineSender::request_variable(const string& object_path, const string& key) +{ + send("/ingen/request_variable", "iss", + next_id(), + object_path.c_str(), + key.c_str(), + LO_ARGS_END); +} + + +void +OSCEngineSender::request_property(const string& object_path, const string& key) +{ + send("/ingen/request_property", "iss", + next_id(), + object_path.c_str(), + key.c_str(), + LO_ARGS_END); +} + + +void +OSCEngineSender::request_plugins() +{ + send("/ingen/request_plugins", "i", next_id(), LO_ARGS_END); +} + + +void +OSCEngineSender::request_all_objects() +{ + send("/ingen/request_all_objects", "i", next_id(), LO_ARGS_END); +} + + + +} // namespace Client +} // namespace Ingen + + diff --git a/src/client/OSCEngineSender.hpp b/src/client/OSCEngineSender.hpp new file mode 100644 index 00000000..ef4a2fa3 --- /dev/null +++ b/src/client/OSCEngineSender.hpp @@ -0,0 +1,154 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef OSCENGINESENDER_H +#define OSCENGINESENDER_H + +#include +#include +#include +#include "interface/EngineInterface.hpp" +#include "shared/OSCSender.hpp" +using std::string; +using Ingen::Shared::EngineInterface; +using Ingen::Shared::ClientInterface; + +namespace Ingen { +namespace Client { + + +/* OSC (via liblo) interface to the engine. + * + * Clients can use this opaquely as an EngineInterface* to control the engine + * over OSC (whether over a network or not, etc). + * + * \ingroup IngenClient + */ +class OSCEngineSender : public EngineInterface, public Shared::OSCSender { +public: + OSCEngineSender(const string& engine_url); + + ~OSCEngineSender(); + + std::string uri() const { return _engine_url; } + + inline int32_t next_id() + { int32_t ret = (_id == -1) ? -1 : _id++; return ret; } + + void set_next_response_id(int32_t id) { _id = id; } + void disable_responses() { _id = -1; } + + void attach(int32_t ping_id, bool block); + + + /* *** EngineInterface implementation below here *** */ + + void enable() { _enabled = true; } + void disable() { _enabled = false; } + + void bundle_begin() { OSCSender::bundle_begin(); } + void bundle_end() { OSCSender::bundle_end(); } + void transfer_begin() { OSCSender::transfer_begin(); } + void transfer_end() { OSCSender::transfer_end(); } + + // Client registration + void register_client(ClientInterface* client); + void unregister_client(const string& uri); + + // Engine commands + void load_plugins(); + void activate(); + void deactivate(); + void quit(); + + // Object commands + + void new_patch(const string& path, + uint32_t poly); + + void new_port(const string& path, + uint32_t index, + const string& data_type, + bool is_output); + + void new_node(const string& path, + const string& plugin_uri); + + void new_node_deprecated(const string& path, + const string& plugin_type, + const string& library_name, + const string& plugin_label); + + void rename(const string& old_path, + const string& new_name); + + void destroy(const string& path); + + void clear_patch(const string& patch_path); + + void connect(const string& src_port_path, + const string& dst_port_path); + + void disconnect(const string& src_port_path, + const string& dst_port_path); + + void disconnect_all(const string& parent_patch_path, + const string& node_path); + + void set_port_value(const string& port_path, + const Raul::Atom& value); + + void set_voice_value(const string& port_path, + uint32_t voice, + const Raul::Atom& value); + + void set_program(const string& node_path, + uint32_t bank, + uint32_t program); + + void midi_learn(const string& node_path); + + void set_variable(const string& obj_path, + const string& predicate, + const Raul::Atom& value); + + void set_property(const string& obj_path, + const string& predicate, + const Raul::Atom& value); + + // Requests // + void ping(); + void request_plugin(const string& uri); + void request_object(const string& path); + void request_port_value(const string& port_path); + void request_variable(const string& path, const string& key); + void request_property(const string& path, const string& key); + void request_plugins(); + void request_all_objects(); + +protected: + const string _engine_url; + int _client_port; + int32_t _id; +}; + + +} // namespace Client +} // namespace Ingen + +#endif // OSCENGINESENDER_H + diff --git a/src/client/ObjectModel.cpp b/src/client/ObjectModel.cpp new file mode 100644 index 00000000..ede5f822 --- /dev/null +++ b/src/client/ObjectModel.cpp @@ -0,0 +1,148 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "interface/GraphObject.hpp" +#include "ObjectModel.hpp" + +using namespace std; + +namespace Ingen { +namespace Client { + + +ObjectModel::ObjectModel(const Path& path) + : _path(path) +{ +} + + +ObjectModel::~ObjectModel() +{ +} + + +/** Get a variable for this object. + * + * @return Metadata value with key @a key, empty string otherwise. + */ +const Atom& +ObjectModel::get_variable(const string& key) const +{ + static const Atom null_atom; + + Variables::const_iterator i = _variables.find(key); + if (i != _variables.end()) + return i->second; + else + return null_atom; +} + + +/** Get a variable for this object. + * + * @return Metadata value with key @a key, empty string otherwise. + */ +Atom& +ObjectModel::get_variable( string& key) +{ + static Atom null_atom; + + Variables::iterator i = _variables.find(key); + if (i != _variables.end()) + return i->second; + else + return null_atom; +} + + +/** Get a property of this object. + * + * @return Metadata value with key @a key, empty string otherwise. + */ +const Atom& +ObjectModel::get_property(const string& key) const +{ + static const Atom null_atom; + + Properties::const_iterator i = _properties.find(key); + if (i != _properties.end()) + return i->second; + else + return null_atom; +} + + +/** Get a property of this object. + * + * @return Metadata value with key @a key, empty string otherwise. + */ +Atom& +ObjectModel::get_property(const string& key) +{ + static Atom null_atom; + + Properties::iterator i = _properties.find(key); + if (i != _properties.end()) + return i->second; + else + return null_atom; +} + + +bool +ObjectModel::polyphonic() const +{ + Properties::const_iterator i = _properties.find("ingen:polyphonic"); + return (i != _properties.end() && i->second.type() == Atom::BOOL && i->second.get_bool()); +} + + +/** Merge the data of @a model with self, as much as possible. + * + * This will merge the two models, but with any conflict take the value in + * @a model as correct. The paths of the two models MUST be equal. + */ +void +ObjectModel::set(SharedPtr o) +{ + assert(_path == o->path()); + if (o->_parent) + _parent = o->_parent; + + for (Variables::const_iterator v = o->variables().begin(); v != o->variables().end(); ++v) { + Variables::const_iterator mine = _variables.find(v->first); + if (mine != _variables.end()) + cerr << "WARNING: " << _path << "Client/Server variable mismatch: " << v->first << endl; + _variables[v->first] = v->second; + signal_variable.emit(v->first, v->second); + } + + for (Properties::const_iterator v = o->properties().begin(); v != o->properties().end(); ++v) { + Properties::const_iterator mine = _properties.find(v->first); + if (mine != _properties.end()) + cerr << "WARNING: " << _path << "Client/Server property mismatch: " << v->first << endl; + _properties[v->first] = v->second; + signal_variable.emit(v->first, v->second); + } +} + + +} // namespace Client +} // namespace Ingen + diff --git a/src/client/ObjectModel.hpp b/src/client/ObjectModel.hpp new file mode 100644 index 00000000..11cc87a4 --- /dev/null +++ b/src/client/ObjectModel.hpp @@ -0,0 +1,115 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef OBJECTMODEL_H +#define OBJECTMODEL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "interface/GraphObject.hpp" + +using Raul::PathTable; +using std::string; +using Raul::Atom; +using Raul::Path; +using Raul::Symbol; + +namespace Ingen { +namespace Client { + +class ClientStore; + + +/** Base class for all GraphObject models (NodeModel, PatchModel, PortModel). + * + * There are no non-const public methods intentionally, models are not allowed + * to be manipulated directly by anything (but the Store) because of the + * asynchronous nature of engine control. To change something, use the + * controller (which the model probably shouldn't have a reference to but oh + * well, it reduces Collection Hell) and wait for the result (as a signal + * from this Model). + * + * \ingroup IngenClient + */ +class ObjectModel : virtual public Ingen::Shared::GraphObject +{ +public: + virtual ~ObjectModel(); + + const Atom& get_variable(const string& key) const; + Atom& get_variable( string& key); + const Atom& get_property(const string& key) const; + Atom& get_property(const string& key); + + virtual void set_variable(const string& key, const Atom& value) + { _variables[key] = value; signal_variable.emit(key, value); } + + virtual void set_property(const string& key, const Atom& value) + { _properties[key] = value; signal_property.emit(key, value); } + + const Variables& variables() const { return _variables; } + const Properties& properties() const { return _properties; } + Variables& variables() { return _variables; } + Properties& properties() { return _properties; } + const Path path() const { return _path; } + const Symbol symbol() const { return _path.name(); } + SharedPtr parent() const { return _parent; } + bool polyphonic() const; + + GraphObject* graph_parent() const { return _parent.get(); } + + // Signals + sigc::signal > signal_new_child; + sigc::signal > signal_removed_child; + sigc::signal signal_variable; + sigc::signal signal_property; + sigc::signal signal_destroyed; + sigc::signal signal_renamed; + +protected: + friend class ClientStore; + + ObjectModel(const Path& path); + + virtual void set_path(const Path& p) { _path = p; signal_renamed.emit(); } + virtual void set_parent(SharedPtr p) { assert(p); _parent = p; } + virtual void add_child(SharedPtr c) {} + virtual bool remove_child(SharedPtr c) { return true; } + + virtual void set(SharedPtr model); + + Path _path; + SharedPtr _parent; + + Variables _variables; + Properties _properties; +}; + + +} // namespace Client +} // namespace Ingen + +#endif // OBJECTMODEL_H diff --git a/src/client/PatchModel.cpp b/src/client/PatchModel.cpp new file mode 100644 index 00000000..af20c9f8 --- /dev/null +++ b/src/client/PatchModel.cpp @@ -0,0 +1,190 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "PatchModel.hpp" +#include "NodeModel.hpp" +#include "ConnectionModel.hpp" +#include "ClientStore.hpp" +#include +#include + +using std::cerr; using std::cout; using std::endl; + +namespace Ingen { +namespace Client { + + +void +PatchModel::add_child(SharedPtr c) +{ + assert(c->parent().get() == this); + + SharedPtr pm = PtrCast(c); + if (pm) { + add_port(pm); + return; + } + + SharedPtr nm = PtrCast(c); + if (nm) + signal_new_node.emit(nm); +} + + +bool +PatchModel::remove_child(SharedPtr o) +{ + assert(o->path().is_child_of(_path)); + assert(o->parent().get() == this); + + SharedPtr pm = PtrCast(o); + if (pm) + remove_port(pm); + + // Remove any connections which referred to this object, + // since they can't possibly exist anymore + for (Connections::iterator j = _connections->begin(); j != _connections->end() ; ) { + + Connections::iterator next = j; + ++next; + + SharedPtr cm = PtrCast(*j); + assert(cm); + + if (cm->src_port_path().parent() == o->path() + || cm->src_port_path() == o->path() + || cm->dst_port_path().parent() == o->path() + || cm->dst_port_path() == o->path()) { + signal_removed_connection.emit(cm); + _connections->erase(j); // cuts our reference + assert(!get_connection(cm->src_port_path(), cm->dst_port_path())); // no duplicates + } + j = next; + } + + SharedPtr nm = PtrCast(o); + if (nm) + signal_removed_node.emit(nm); + + return true; +} + + +void +PatchModel::clear() +{ + _connections->clear(); + + NodeModel::clear(); + + assert(_connections->empty()); + assert(_ports.empty()); +} + + +SharedPtr +PatchModel::get_connection(const string& src_port_path, const string& dst_port_path) const +{ + for (Connections::const_iterator i = _connections->begin(); i != _connections->end(); ++i) + if ((*i)->src_port_path() == src_port_path && (*i)->dst_port_path() == dst_port_path) + return PtrCast(*i); + + return SharedPtr(); +} + + +/** Add a connection to this patch. + * + * A reference to @a cm is taken, released on deletion or removal. + * If @a cm only contains paths (not pointers to the actual ports), the ports + * will be found and set. The ports referred to not existing as children of + * this patch is a fatal error. + */ +void +PatchModel::add_connection(SharedPtr cm) +{ + // Store should have 'resolved' the connection already + assert(cm); + assert(cm->src_port()); + assert(cm->dst_port()); + assert(cm->src_port()->parent()); + assert(cm->dst_port()->parent()); + assert(cm->src_port_path() != cm->dst_port_path()); + assert(cm->src_port()->parent().get() == this + || cm->src_port()->parent()->parent().get() == this); + assert(cm->dst_port()->parent().get() == this + || cm->dst_port()->parent()->parent().get() == this); + + SharedPtr existing = get_connection(cm->src_port_path(), cm->dst_port_path()); + + if (existing) { + assert(cm->src_port() == existing->src_port()); + assert(cm->dst_port() == existing->dst_port()); + } else { + _connections->push_back(new Connections::Node(cm)); + signal_new_connection.emit(cm); + } +} + + +void +PatchModel::remove_connection(const string& src_port_path, const string& dst_port_path) +{ + for (Connections::iterator i = _connections->begin(); i != _connections->end(); ++i) { + SharedPtr cm = PtrCast(*i); + assert(cm); + if (cm->src_port_path() == src_port_path && cm->dst_port_path() == dst_port_path) { + signal_removed_connection.emit(cm); + delete _connections->erase(i); // cuts our reference + assert(!get_connection(src_port_path, dst_port_path)); // no duplicates + return; + } + } + + cerr << "[PatchModel::remove_connection] WARNING: Failed to find connection " << + src_port_path << " -> " << dst_port_path << endl; +} + + +bool +PatchModel::enabled() const +{ + Properties::const_iterator i = _properties.find("ingen:enabled"); + return (i != _properties.end() && i->second.type() == Atom::BOOL && i->second.get_bool()); +} + + +void +PatchModel::set_property(const string& key, const Atom& value) +{ + ObjectModel::set_property(key, value); + if (key == "ingen:polyphony") + _poly = value.get_int32(); +} + + +bool +PatchModel::polyphonic() const +{ + return (_parent) + ? (_poly > 1) && _poly == PtrCast(_parent)->poly() && _poly > 1 + : (_poly > 1); +} + + +} // namespace Client +} // namespace Ingen diff --git a/src/client/PatchModel.hpp b/src/client/PatchModel.hpp new file mode 100644 index 00000000..70c8df0e --- /dev/null +++ b/src/client/PatchModel.hpp @@ -0,0 +1,105 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PATCHMODEL_H +#define PATCHMODEL_H + +#include +#include +#include +#include +#include +#include "interface/Patch.hpp" +#include "NodeModel.hpp" + +#include "ConnectionModel.hpp" + +using std::list; using std::string; + +namespace Ingen { +namespace Client { + +class ClientStore; + + +/** Client's model of a patch. + * + * \ingroup IngenClient + */ +class PatchModel : public NodeModel, public Ingen::Shared::Patch +{ +public: + /* WARNING: Copy constructor creates a shallow copy WRT connections */ + + const Connections& connections() const { return *_connections.get(); } + + SharedPtr get_connection(const string& src_port_path, + const string& dst_port_path) const; + + uint32_t poly() const { return _poly; } + uint32_t internal_polyphony() const { return _poly; } + bool enabled() const; + bool polyphonic() const; + + /** "editable" = arranging,connecting,adding,deleting,etc + * not editable (control mode) you can just change controllers (performing) + */ + bool get_editable() const { return _editable; } + void set_editable(bool e) { if (_editable != e) { + _editable = e; + signal_editable.emit(e); + } } + + virtual void set_property(const string& key, const Atom& value); + + // Signals + sigc::signal > signal_new_node; + sigc::signal > signal_removed_node; + sigc::signal > signal_new_connection; + sigc::signal > signal_removed_connection; + sigc::signal signal_editable; + +private: + friend class ClientStore; + + PatchModel(const Path& patch_path, size_t internal_poly) + : NodeModel("ingen:Patch", patch_path) + , _connections(new Connections()) + , _poly(internal_poly) + , _editable(true) + { + } + + void clear(); + void add_child(SharedPtr c); + bool remove_child(SharedPtr c); + + void add_connection(SharedPtr cm); + void remove_connection(const string& src_port_path, const string& dst_port_path); + + SharedPtr _connections; + uint32_t _poly; + bool _editable; +}; + +typedef Table > PatchModelMap; + + +} // namespace Client +} // namespace Ingen + +#endif // PATCHMODEL_H diff --git a/src/client/PluginModel.cpp b/src/client/PluginModel.cpp new file mode 100644 index 00000000..ff7e5b5c --- /dev/null +++ b/src/client/PluginModel.cpp @@ -0,0 +1,143 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "PluginModel.hpp" +#include "PatchModel.hpp" +#include "PluginUI.hpp" + +using namespace std; +using Ingen::Shared::EngineInterface; + +namespace Ingen { +namespace Client { + +#ifdef HAVE_SLV2 +SLV2World PluginModel::_slv2_world = NULL; +SLV2Plugins PluginModel::_slv2_plugins = NULL; +#endif + +Redland::World* PluginModel::_rdf_world = NULL; + + +string +PluginModel::default_node_symbol() +{ + return Raul::Path::nameify(_symbol); +} + + +string +PluginModel::human_name() +{ +#ifdef HAVE_SLV2 + if (_slv2_plugin) { + SLV2Value name = slv2_plugin_get_name(_slv2_plugin); + string ret = slv2_value_as_string(name); + slv2_value_free(name); + return ret; + } +#endif + return default_node_symbol(); +} + + +string +PluginModel::port_human_name(uint32_t index) +{ +#ifdef HAVE_SLV2 + if (_slv2_plugin) { + Glib::Mutex::Lock lock(_rdf_world->mutex()); + SLV2Port port = slv2_plugin_get_port_by_index(_slv2_plugin, index); + SLV2Value name = slv2_port_get_name(_slv2_plugin, port); + string ret = slv2_value_as_string(name); + slv2_value_free(name); + return ret; + } +#endif + return ""; +} + + +#ifdef HAVE_SLV2 +bool +PluginModel::has_ui() const +{ + Glib::Mutex::Lock lock(_rdf_world->mutex()); + + SLV2Value gtk_gui_uri = slv2_value_new_uri(_slv2_world, + "http://lv2plug.in/ns/extensions/ui#GtkUI"); + + SLV2UIs uis = slv2_plugin_get_uis(_slv2_plugin); + + if (slv2_values_size(uis) > 0) + for (unsigned i=0; i < slv2_uis_size(uis); ++i) + if (slv2_ui_is_a(slv2_uis_get_at(uis, i), gtk_gui_uri)) + return true; + + return false; +} + + +SharedPtr +PluginModel::ui(Ingen::Shared::World* world, SharedPtr node) const +{ + if (_type != LV2) + return SharedPtr(); + + SharedPtr ret = PluginUI::create(world, node, _slv2_plugin); + return ret; +} + + +const string& +PluginModel::icon_path() const +{ + if (_icon_path == "" && _type == LV2) { + Glib::Mutex::Lock lock(_rdf_world->mutex()); + _icon_path = get_lv2_icon_path(_slv2_plugin); + } + + return _icon_path; +} + + +/** RDF world mutex must be held by the caller */ +string +PluginModel::get_lv2_icon_path(SLV2Plugin plugin) +{ + string result; + SLV2Value svg_icon_pred = slv2_value_new_uri(_slv2_world, + "http://ll-plugins.nongnu.org/lv2/namespace#svgIcon"); + + SLV2Values paths = slv2_plugin_get_value(plugin, svg_icon_pred); + + if (slv2_values_size(paths) > 0) { + SLV2Value value = slv2_values_get_at(paths, 0); + if (slv2_value_is_uri(value)) + result = slv2_uri_to_path(slv2_value_as_string(value)); + slv2_values_free(paths); + } + + slv2_value_free(svg_icon_pred); + return result; +} +#endif + +} // namespace Client +} // namespace Ingen diff --git a/src/client/PluginModel.hpp b/src/client/PluginModel.hpp new file mode 100644 index 00000000..e2137e19 --- /dev/null +++ b/src/client/PluginModel.hpp @@ -0,0 +1,142 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PLUGINMODEL_H +#define PLUGINMODEL_H + +#include CONFIG_H_PATH +#include +#include +#include +#include +#include +#ifdef HAVE_SLV2 +#include +#endif +#include "interface/EngineInterface.hpp" +#include "interface/Plugin.hpp" +#include "module/World.hpp" + +using std::string; + +namespace Ingen { +namespace Client { + +class PatchModel; +class NodeModel; +class PluginUI; + + +/** Model for a plugin available for loading. + * + * \ingroup IngenClient + */ +class PluginModel : public Ingen::Shared::Plugin +{ +public: + PluginModel(const string& uri, const string& type_uri, const string& symbol, const string& name) + : _type(type_from_uri(type_uri)) + , _uri(uri) + , _symbol(symbol) + , _name(name) + { +#ifdef HAVE_SLV2 + Glib::Mutex::Lock lock(_rdf_world->mutex()); + SLV2Value plugin_uri = slv2_value_new_uri(_slv2_world, uri.c_str()); + _slv2_plugin = slv2_plugins_get_by_uri(_slv2_plugins, plugin_uri); + slv2_value_free(plugin_uri); +#endif + } + + Type type() const { return _type; } + const string& uri() const { return _uri; } + const string& name() const { return _name; } + + /** DEPRECATED */ + Type type_from_string(const string& type_string) { + if (type_string == "LV2") return LV2; + else if (type_string == "LADSPA") return LADSPA; + else if (type_string == "Internal") return Internal; + else if (type_string == "Patch") return Patch; + else return Internal; // ? + } + + Type type_from_uri(const string& type_uri) { + if (type_uri.substr(0, 6) != "ingen:") { + return Plugin::Internal; // ? + } else { + return type_from_string(type_uri.substr(6)); + } + } + + string default_node_symbol(); + string human_name(); + string port_human_name(uint32_t index); + +#ifdef HAVE_SLV2 + static SLV2World slv2_world() { return _slv2_world; } + SLV2Plugin slv2_plugin() { return _slv2_plugin; } + + SLV2Port slv2_port(uint32_t index) { + Glib::Mutex::Lock lock(_rdf_world->mutex()); + return slv2_plugin_get_port_by_index(_slv2_plugin, index); + } + + static void set_slv2_world(SLV2World world) { + Glib::Mutex::Lock lock(_rdf_world->mutex()); + _slv2_world = world; + _slv2_plugins = slv2_world_get_all_plugins(_slv2_world); + } + + bool has_ui() const; + + SharedPtr ui(Ingen::Shared::World* world, + SharedPtr node) const; + + const string& icon_path() const; + static string get_lv2_icon_path(SLV2Plugin plugin); +#endif + + static void set_rdf_world(Redland::World& world) { + _rdf_world = &world; + } + + static Redland::World* rdf_world() { return _rdf_world; } + +private: + const Type _type; + const string _uri; + const string _symbol; + const string _name; + +#ifdef HAVE_SLV2 + static SLV2World _slv2_world; + static SLV2Plugins _slv2_plugins; + + SLV2Plugin _slv2_plugin; + mutable string _icon_path; +#endif + + static Redland::World* _rdf_world; +}; + + +} // namespace Client +} // namespace Ingen + +#endif // PLUGINMODEL_H + diff --git a/src/client/PluginUI.cpp b/src/client/PluginUI.cpp new file mode 100644 index 00000000..9c562135 --- /dev/null +++ b/src/client/PluginUI.cpp @@ -0,0 +1,157 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "lv2ext/lv2_event_helpers.h" +#include "shared/LV2URIMap.hpp" +#include "PluginUI.hpp" +#include "NodeModel.hpp" +#include "PortModel.hpp" + +using namespace std; +using Ingen::Shared::EngineInterface; +using Ingen::Shared::LV2URIMap; +using Ingen::Shared::LV2Features; + +namespace Ingen { +namespace Client { + +static void +lv2_ui_write(LV2UI_Controller controller, + uint32_t port_index, + uint32_t buffer_size, + uint32_t format, + const void* buffer) +{ + /* + cerr << "lv2_ui_write (format " << format << "):" << endl; + fprintf(stderr, "RAW:\n"); + for (uint32_t i=0; i < buffer_size; ++i) { + unsigned char byte = ((unsigned char*)buffer)[i]; + if (byte >= 32 && byte <= 126) + fprintf(stderr, "%c ", ((unsigned char*)buffer)[i]); + else + fprintf(stderr, "%2X ", ((unsigned char*)buffer)[i]); + } + fprintf(stderr, "\n"); + */ + + PluginUI* ui = (PluginUI*)controller; + + SharedPtr port = ui->node()->ports()[port_index]; + + const LV2Features::Feature* f = ui->world()->lv2_features->feature(LV2_URI_MAP_URI); + LV2URIMap* map = (LV2URIMap*)f->controller; + assert(map); + + // float (special case, always 0) + if (format == 0) { + assert(buffer_size == 4); + if (*(float*)buffer == port->value().get_float()) + return; // do nothing (handle stupid plugin UIs that feed back) + + ui->world()->engine->set_port_value(port->path(), Atom(*(float*)buffer)); + + // FIXME: slow, need to cache ID + } else if (format == map->uri_to_id(NULL, "http://lv2plug.in/ns/extensions/ui#Events")) { + uint32_t midi_event_type = map->uri_to_id(NULL, "http://lv2plug.in/ns/ext/midi#MidiEvent"); + LV2_Event_Buffer* buf = (LV2_Event_Buffer*)buffer; + LV2_Event_Iterator iter; + uint8_t* data; + lv2_event_begin(&iter, buf); + while (lv2_event_is_valid(&iter)) { + LV2_Event* const ev = lv2_event_get(&iter, &data); + if (ev->type == midi_event_type) { + // FIXME: bundle multiple events by writing an entire buffer here + ui->world()->engine->set_port_value(port->path(), + Atom("lv2_midi:MidiEvent", ev->size, data)); + } else { + cerr << "WARNING: Unable to send event type " << ev->type << + " over OSC, ignoring event" << endl; + } + + lv2_event_increment(&iter); + } + } else { + cerr << "WARNING: Unknown value format " << format + << ", either plugin " << ui->node()->plugin()->uri() << " is broken" + << " or this is an Ingen bug" << endl; + } +} + + +PluginUI::PluginUI(Ingen::Shared::World* world, + SharedPtr node) + : _world(world) + , _node(node) + , _instance(NULL) +{ +} + + +PluginUI::~PluginUI() +{ + Glib::Mutex::Lock lock(PluginModel::rdf_world()->mutex()); + slv2_ui_instance_free(_instance); +} + + +SharedPtr +PluginUI::create(Ingen::Shared::World* world, + SharedPtr node, + SLV2Plugin plugin) +{ + Glib::Mutex::Lock lock(PluginModel::rdf_world()->mutex()); + SharedPtr ret; + + SLV2Value gtk_gui_uri = slv2_value_new_uri(world->slv2_world, + "http://lv2plug.in/ns/extensions/ui#GtkUI"); + + SLV2UIs uis = slv2_plugin_get_uis(plugin); + SLV2UI ui = NULL; + + if (slv2_values_size(uis) > 0) { + for (unsigned i=0; i < slv2_uis_size(uis); ++i) { + SLV2UI this_ui = slv2_uis_get_at(uis, i); + if (slv2_ui_is_a(this_ui, gtk_gui_uri)) { + ui = this_ui; + break; + } + } + } + + if (ui) { + cout << "Found GTK Plugin UI: " << slv2_ui_get_uri(ui) << endl; + ret = SharedPtr(new PluginUI(world, node)); + SLV2UIInstance inst = slv2_ui_instantiate( + plugin, ui, lv2_ui_write, ret.get(), world->lv2_features->lv2_features()); + + if (inst) { + ret->set_instance(inst); + } else { + cerr << "ERROR: Failed to instantiate Plugin UI" << endl; + ret = SharedPtr(); + } + } + + slv2_value_free(gtk_gui_uri); + return ret; +} + + +} // namespace Client +} // namespace Ingen diff --git a/src/client/PluginUI.hpp b/src/client/PluginUI.hpp new file mode 100644 index 00000000..d20dd16a --- /dev/null +++ b/src/client/PluginUI.hpp @@ -0,0 +1,65 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PLUGINUI_H +#define PLUGINUI_H + +#include +#include +#include "module/World.hpp" + +namespace Ingen { +namespace Shared { class EngineInterface; } +namespace Client { + +class NodeModel; + + +/** Model for a plugin available for loading. + * + * \ingroup IngenClient + */ +class PluginUI { +public: + ~PluginUI(); + + static SharedPtr create(Ingen::Shared::World* world, + SharedPtr node, + SLV2Plugin plugin); + + Ingen::Shared::World* world() const { return _world; } + SharedPtr node() const { return _node; } + SLV2UIInstance instance() const { return _instance; } + +private: + PluginUI(Ingen::Shared::World* world, + SharedPtr node); + + void set_instance(SLV2UIInstance instance) { _instance = instance; } + + Ingen::Shared::World* _world; + SharedPtr _node; + SLV2UIInstance _instance; +}; + + +} // namespace Client +} // namespace Ingen + +#endif // PLUGINUI_H + + diff --git a/src/client/PortModel.cpp b/src/client/PortModel.cpp new file mode 100644 index 00000000..c18378db --- /dev/null +++ b/src/client/PortModel.cpp @@ -0,0 +1,66 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "PortModel.hpp" +#include "NodeModel.hpp" + +namespace Ingen { +namespace Client { + + +bool +PortModel::is_logarithmic() const +{ + const Atom& hint = get_variable("ingen:logarithmic"); + return (hint.is_valid() && hint.get_bool() > 0); +} + + +bool +PortModel::is_integer() const +{ + const Atom& hint = get_variable("ingen:integer"); + return (hint.is_valid() && hint.get_bool() > 0); +} + + +bool +PortModel::is_toggle() const +{ + const Atom& hint = get_variable("ingen:toggled"); + return (hint.is_valid() && hint.get_bool() > 0); +} + + +void +PortModel::set(SharedPtr model) +{ + SharedPtr port = PtrCast(model); + if (port) { + _index = port->_index; + _type = port->_type; + _direction = port->_direction; + _current_val = port->_current_val; + _connections = port->_connections; + signal_value_changed.emit(_current_val); + } + + ObjectModel::set(model); +} + +} // namespace Client +} // namespace Ingen diff --git a/src/client/PortModel.hpp b/src/client/PortModel.hpp new file mode 100644 index 00000000..a7f52679 --- /dev/null +++ b/src/client/PortModel.hpp @@ -0,0 +1,112 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PORTMODEL_H +#define PORTMODEL_H + +#include +#include +#include +#include +#include +#include +#include +#include "interface/Port.hpp" +#include "ObjectModel.hpp" + +using std::string; using std::vector; + +namespace Ingen { +namespace Client { + + +/** Model of a port. + * + * \ingroup IngenClient + */ +class PortModel : public ObjectModel, public Ingen::Shared::Port +{ +public: + enum Direction { INPUT, OUTPUT }; + + inline uint32_t index() const { return _index; } + inline DataType type() const { return _type; } + inline const Atom& value() const { return _current_val; } + inline bool connected() const { return (_connections > 0); } + inline bool is_input() const { return (_direction == INPUT); } + inline bool is_output() const { return (_direction == OUTPUT); } + + bool is_logarithmic() const; + bool is_integer() const; + bool is_toggle() const; + + inline bool operator==(const PortModel& pm) const { return (_path == pm._path); } + + inline void value(const Atom& val) { + if (val != _current_val) { + _current_val = val; + signal_value_changed.emit(val); + } + } + + inline void value(uint32_t voice, const Atom& val) { + // FIXME: implement properly + signal_voice_changed.emit(voice, val); + } + + // Signals + sigc::signal signal_value_changed; ///< Value ports + sigc::signal signal_voice_changed; ///< Polyphonic value ports + sigc::signal signal_activity; ///< Message ports + sigc::signal > signal_connection; + sigc::signal > signal_disconnection; + +private: + friend class ClientStore; + + PortModel(const Path& path, uint32_t index, DataType type, Direction dir) + : ObjectModel(path) + , _index(index) + , _type(type) + , _direction(dir) + , _current_val(0.0f) + , _connections(0) + { + if (_type == DataType::UNKNOWN) + std::cerr << "[PortModel] Warning: Unknown port type" << std::endl; + } + + void add_child(SharedPtr c) { throw; } + bool remove_child(SharedPtr c) { throw; } + + void connected_to(SharedPtr p) { ++_connections; signal_connection.emit(p); } + void disconnected_from(SharedPtr p) { --_connections; signal_disconnection.emit(p); } + + void set(SharedPtr model); + + uint32_t _index; + DataType _type; + Direction _direction; + Atom _current_val; + size_t _connections; +}; + + +} // namespace Client +} // namespace Ingen + +#endif // PORTMODEL_H diff --git a/src/client/SigClientInterface.hpp b/src/client/SigClientInterface.hpp new file mode 100644 index 00000000..7ab32c12 --- /dev/null +++ b/src/client/SigClientInterface.hpp @@ -0,0 +1,156 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SIGCLIENTINTERFACE_H +#define SIGCLIENTINTERFACE_H + +#include +#include +#include +#include "interface/ClientInterface.hpp" +using std::string; + +namespace Ingen { +namespace Client { + + +/** A LibSigC++ signal emitting interface for clients to use. + * + * This simply emits an sigc signal for every event (eg OSC message) coming from + * the engine. Use Store (which extends this) if you want a nice client-side + * model of the engine. + * + * The signals here match the calls to ClientInterface exactly. See the + * documentation for ClientInterface for meanings of signal parameters. + */ +class SigClientInterface : public Ingen::Shared::ClientInterface, public sigc::trackable +{ +public: + SigClientInterface() : _enabled(true) {} + + bool enabled() const { return _enabled; } + + std::string uri() const { return "(internal)"; } + + // Signal parameters match up directly with ClientInterface calls + + sigc::signal signal_response_ok; + sigc::signal signal_response_error; + sigc::signal signal_bundle_begin; + sigc::signal signal_bundle_end; + sigc::signal signal_error; + sigc::signal signal_new_plugin; + sigc::signal signal_new_patch; + sigc::signal signal_new_node; + sigc::signal signal_new_port; + sigc::signal signal_patch_cleared; + sigc::signal signal_object_renamed; + sigc::signal signal_object_destroyed; + sigc::signal signal_connection; + sigc::signal signal_disconnection; + sigc::signal signal_variable_change; + sigc::signal signal_property_change; + sigc::signal signal_port_value; + sigc::signal signal_voice_value; + sigc::signal signal_port_activity; + sigc::signal signal_program_add; + sigc::signal signal_program_remove; + + /** Fire pending signals. Only does anything on derived classes (that may queue) */ + virtual bool emit_signals() { return false; } + +protected: + + bool _enabled; + + // ClientInterface hooks that fire the above signals + + void enable() { _enabled = true; } + void disable() { _enabled = false ; } + + void bundle_begin() + { if (_enabled) signal_bundle_begin.emit(); } + + void bundle_end() + { if (_enabled) signal_bundle_end.emit(); } + + void transfer_begin() {} + void transfer_end() {} + + void response_ok(int32_t id) + { if (_enabled) signal_response_ok.emit(id); } + + void response_error(int32_t id, const string& msg) + { if (_enabled) signal_response_error.emit(id, msg); } + + void error(const string& msg) + { if (_enabled) signal_error.emit(msg); } + + void new_plugin(const string& uri, const string& type_uri, const string& symbol, const string& name) + { if (_enabled) signal_new_plugin.emit(uri, type_uri, symbol, name); } + + void new_patch(const string& path, uint32_t poly) + { if (_enabled) signal_new_patch.emit(path, poly); } + + void new_node(const string& path, const string& plugin_uri) + { if (_enabled) signal_new_node.emit(path, plugin_uri); } + + void new_port(const string& path, uint32_t index, const string& data_type, bool is_output) + { if (_enabled) signal_new_port.emit(path, index, data_type, is_output); } + + void connect(const string& src_port_path, const string& dst_port_path) + { if (_enabled) signal_connection.emit(src_port_path, dst_port_path); } + + void destroy(const string& path) + { if (_enabled) signal_object_destroyed.emit(path); } + + void patch_cleared(const string& path) + { if (_enabled) signal_patch_cleared.emit(path); } + + void object_renamed(const string& old_path, const string& new_path) + { if (_enabled) signal_object_renamed.emit(old_path, new_path); } + + void disconnect(const string& src_port_path, const string& dst_port_path) + { if (_enabled) signal_disconnection.emit(src_port_path, dst_port_path); } + + void set_variable(const string& path, const string& key, const Raul::Atom& value) + { if (_enabled) signal_variable_change.emit(path, key, value); } + + void set_property(const string& path, const string& key, const Raul::Atom& value) + { if (_enabled) signal_property_change.emit(path, key, value); } + + void set_port_value(const string& port_path, const Raul::Atom& value) + { if (_enabled) signal_port_value.emit(port_path, value); } + + void set_voice_value(const string& port_path, uint32_t voice, const Raul::Atom& value) + { if (_enabled) signal_voice_value.emit(port_path, voice, value); } + + void port_activity(const string& port_path) + { if (_enabled) signal_port_activity.emit(port_path); } + + void program_add(const string& path, uint32_t bank, uint32_t program, const string& name) + { if (_enabled) signal_program_add.emit(path, bank, program, name); } + + void program_remove(const string& path, uint32_t bank, uint32_t program) + { if (_enabled) signal_program_remove.emit(path, bank, program); } +}; + + +} // namespace Client +} // namespace Ingen + +#endif diff --git a/src/client/ThreadedSigClientInterface.cpp b/src/client/ThreadedSigClientInterface.cpp new file mode 100644 index 00000000..ef95133b --- /dev/null +++ b/src/client/ThreadedSigClientInterface.cpp @@ -0,0 +1,78 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "ThreadedSigClientInterface.hpp" +#include + +using namespace std; + +namespace Ingen { +namespace Client { + + +/** Push an event (from the engine, ie 'new patch') on to the queue. + */ +void +ThreadedSigClientInterface::push_sig(Closure ev) +{ + _attached = true; + if (!_enabled) + return; + + bool success = false; + while (!success) { + success = _sigs.push(ev); + if (!success) { + cerr << "WARNING: Client event queue full. Waiting..." << endl; + _mutex.lock(); + _cond.wait(_mutex); + _mutex.unlock(); + cerr << "Queue drained, continuing" << endl; + } + } +} + + +/** Process all queued events that came from the OSC thread. + * + * This function should be called from the Gtk thread to emit signals and cause + * the connected methods to execute. + */ +bool +ThreadedSigClientInterface::emit_signals() +{ + // Process a limited number of events, to prevent locking the GTK + // thread indefinitely while processing continually arriving events + + size_t num_processed = 0; + while (!_sigs.empty() && num_processed++ < (_sigs.capacity() * 3 / 4)) { + Closure& ev = _sigs.front(); + ev(); + ev.disconnect(); + _sigs.pop(); + } + + _mutex.lock(); + _cond.broadcast(); + _mutex.unlock(); + + return true; +} + + +} // namespace Client +} // namespace Ingen diff --git a/src/client/ThreadedSigClientInterface.hpp b/src/client/ThreadedSigClientInterface.hpp new file mode 100644 index 00000000..3014c139 --- /dev/null +++ b/src/client/ThreadedSigClientInterface.hpp @@ -0,0 +1,184 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef THREADEDSIGCLIENTINTERFACE_H +#define THREADEDSIGCLIENTINTERFACE_H + +#include +#include +#include +#include +#include "interface/ClientInterface.hpp" +#include "SigClientInterface.hpp" +#include +#include + +using std::string; + +/** Returns nothing and takes no parameters (because they have all been bound) */ +typedef sigc::slot Closure; + +namespace Ingen { +namespace Shared { class EngineInterface; } +namespace Client { + + +/** A LibSigC++ signal emitting interface for clients to use. + * + * This emits signals (possibly) in a different thread than the ClientInterface + * functions are called. It must be explicitly driven with the emit_signals() + * function, which fires all enqueued signals up until the present. You can + * use this in a GTK idle callback for receiving thread safe engine signals. + */ +class ThreadedSigClientInterface : public SigClientInterface +{ +public: + ThreadedSigClientInterface(uint32_t queue_size) + : _sigs(queue_size) + , response_ok_slot(signal_response_ok.make_slot()) + , response_error_slot(signal_response_error.make_slot()) + , error_slot(signal_error.make_slot()) + , new_plugin_slot(signal_new_plugin.make_slot()) + , new_patch_slot(signal_new_patch.make_slot()) + , new_node_slot(signal_new_node.make_slot()) + , new_port_slot(signal_new_port.make_slot()) + , connection_slot(signal_connection.make_slot()) + , patch_cleared_slot(signal_patch_cleared.make_slot()) + , object_destroyed_slot(signal_object_destroyed.make_slot()) + , object_renamed_slot(signal_object_renamed.make_slot()) + , disconnection_slot(signal_disconnection.make_slot()) + , variable_change_slot(signal_variable_change.make_slot()) + , property_change_slot(signal_property_change.make_slot()) + , port_value_slot(signal_port_value.make_slot()) + , port_activity_slot(signal_port_activity.make_slot()) + , program_add_slot(signal_program_add.make_slot()) + , program_remove_slot(signal_program_remove.make_slot()) + { + } + + virtual std::string uri() const { return "(internal)"; } + + virtual void subscribe(Shared::EngineInterface* engine) { throw; } + + bool enabled() const { return _attached; } + + void bundle_begin() + { push_sig(bundle_begin_slot); } + + void bundle_end() + { push_sig(bundle_end_slot); } + + void transfer_begin() {} + void transfer_end() {} + + void response_ok(int32_t id) + { push_sig(sigc::bind(response_ok_slot, id)); } + + void response_error(int32_t id, const string& msg) + { push_sig(sigc::bind(response_error_slot, id, msg)); } + + void error(const string& msg) + { push_sig(sigc::bind(error_slot, msg)); } + + void new_plugin(const string& uri, const string& type_uri, const string& symbol, const string& name) + { push_sig(sigc::bind(new_plugin_slot, uri, type_uri, symbol, name)); } + + void new_patch(const string& path, uint32_t poly) + { push_sig(sigc::bind(new_patch_slot, path, poly)); } + + void new_node(const string& path, const string& plugin_uri) + { push_sig(sigc::bind(new_node_slot, path, plugin_uri)); } + + void new_port(const string& path, uint32_t index, const string& data_type, bool is_output) + { push_sig(sigc::bind(new_port_slot, path, index, data_type, is_output)); } + + void connect(const string& src_port_path, const string& dst_port_path) + { push_sig(sigc::bind(connection_slot, src_port_path, dst_port_path)); } + + void destroy(const string& path) + { push_sig(sigc::bind(object_destroyed_slot, path)); } + + void patch_cleared(const string& path) + { push_sig(sigc::bind(patch_cleared_slot, path)); } + + void object_renamed(const string& old_path, const string& new_path) + { push_sig(sigc::bind(object_renamed_slot, old_path, new_path)); } + + void disconnect(const string& src_port_path, const string& dst_port_path) + { push_sig(sigc::bind(disconnection_slot, src_port_path, dst_port_path)); } + + void set_variable(const string& path, const string& key, const Raul::Atom& value) + { push_sig(sigc::bind(variable_change_slot, path, key, value)); } + + void set_property(const string& path, const string& key, const Raul::Atom& value) + { push_sig(sigc::bind(property_change_slot, path, key, value)); } + + void set_port_value(const string& port_path, const Raul::Atom& value) + { push_sig(sigc::bind(port_value_slot, port_path, value)); } + + void set_voice_value(const string& port_path, uint32_t voice, const Raul::Atom& value) + { push_sig(sigc::bind(voice_value_slot, port_path, voice, value)); } + + void port_activity(const string& port_path) + { push_sig(sigc::bind(port_activity_slot, port_path)); } + + void program_add(const string& path, uint32_t bank, uint32_t program, const string& name) + { push_sig(sigc::bind(program_add_slot, path, bank, program, name)); } + + void program_remove(const string& path, uint32_t bank, uint32_t program) + { push_sig(sigc::bind(program_remove_slot, path, bank, program)); } + + /** Process all queued events - Called from GTK thread to emit signals. */ + bool emit_signals(); + +private: + void push_sig(Closure ev); + + Glib::Mutex _mutex; + Glib::Cond _cond; + + Raul::SRSWQueue _sigs; + bool _attached; + + sigc::slot bundle_begin_slot; + sigc::slot bundle_end_slot; + sigc::slot response_ok_slot; + sigc::slot response_error_slot; + sigc::slot error_slot; + sigc::slot new_plugin_slot; + sigc::slot new_patch_slot; + sigc::slot new_node_slot; + sigc::slot new_port_slot; + sigc::slot connection_slot; + sigc::slot patch_cleared_slot; + sigc::slot object_destroyed_slot; + sigc::slot object_renamed_slot; + sigc::slot disconnection_slot; + sigc::slot variable_change_slot; + sigc::slot property_change_slot; + sigc::slot port_value_slot; + sigc::slot voice_value_slot; + sigc::slot port_activity_slot; + sigc::slot program_add_slot; + sigc::slot program_remove_slot; +}; + + +} // namespace Client +} // namespace Ingen + +#endif diff --git a/src/client/client.cpp b/src/client/client.cpp new file mode 100644 index 00000000..f3d62471 --- /dev/null +++ b/src/client/client.cpp @@ -0,0 +1,57 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include CONFIG_H_PATH + +#include +#include "client.hpp" +#include "OSCEngineSender.hpp" +#ifdef WITH_SOUP +#include "HTTPEngineSender.hpp" +#endif + +using namespace std; + +namespace Ingen { +namespace Client { + + +SharedPtr +new_remote_interface(const std::string& url) +{ + const string scheme = url.substr(0, url.find(":")); + cout << "SCHEME: " << scheme << endl; + if (scheme == "osc.udp" || scheme == "osc.tcp") { + OSCEngineSender* oes = new OSCEngineSender(url); + oes->attach(rand(), true); + return SharedPtr(oes); +#ifdef WITH_SOUP + } else if (scheme == "http") { + HTTPEngineSender* hes = new HTTPEngineSender(url); + hes->attach(rand(), true); + return SharedPtr(hes); +#endif + } else { + cerr << "WARNING: Unknown URI scheme '" << scheme << "'" << endl; + return SharedPtr(); + } +} + + +} // namespace Client +} // namespace Ingen + diff --git a/src/client/client.hpp b/src/client/client.hpp new file mode 100644 index 00000000..82166da5 --- /dev/null +++ b/src/client/client.hpp @@ -0,0 +1,43 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef INGEN_CLIENT_H +#define INGEN_CLIENT_H + +#include + +namespace Ingen { + +class Engine; + +namespace Shared { class EngineInterface; } + +namespace Client { + +extern "C" { + + SharedPtr new_remote_interface(const std::string& url); + SharedPtr new_queued_interface(SharedPtr engine); + +} + + +} // namespace Client +} // namespace Ingen + +#endif // INGEN_CLIENT_H + diff --git a/src/client/wscript b/src/client/wscript new file mode 100644 index 00000000..6b4408a7 --- /dev/null +++ b/src/client/wscript @@ -0,0 +1,35 @@ +#!/usr/bin/env python +import Params + +def build(bld): + obj = bld.create_obj('cpp', 'shlib') + obj.source = ''' + ClientStore.cpp + NodeModel.cpp + ObjectModel.cpp + PatchModel.cpp + PluginModel.cpp + PluginUI.cpp + PortModel.cpp + ThreadedSigClientInterface.cpp + client.cpp + ''' + + if bld.env()['HAVE_SOUP']: + obj.source += ''' + HTTPClientReceiver.cpp + HTTPEngineSender.cpp + ''' + + if bld.env()['HAVE_XML2']: + obj.source += ' DeprecatedLoader.cpp ' + + if bld.env()['HAVE_LIBLO']: + obj.source += ' OSCClientReceiver.cpp OSCEngineSender.cpp ' + + obj.includes = ['..', '../../common', '../..'] + obj.name = 'libingen_client' + obj.target = 'ingen_client' + obj.uselib = 'GLIBMM SLV2 RAUL REDLANDMM SOUP XML2 SIGCPP' + obj.vnum = '0.0.0' + diff --git a/src/engine/AudioBuffer.cpp b/src/engine/AudioBuffer.cpp new file mode 100644 index 00000000..098103c0 --- /dev/null +++ b/src/engine/AudioBuffer.cpp @@ -0,0 +1,299 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include "AudioBuffer.hpp" + +using namespace std; + +/* 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 Ingen { + + +AudioBuffer::AudioBuffer(size_t size) + : Buffer((size == 1) ? DataType::CONTROL : DataType::AUDIO, size) + , _data(NULL) + , _local_data(NULL) + , _size(size) + , _filled_size(0) + , _state(OK) + , _set_value(0) + , _set_time(0) +{ + assert(_size > 0); + allocate(); + assert(data()); +} + + +void +AudioBuffer::resize(size_t size) +{ + _size = size; + + Sample* const old_data = _data; + + const bool using_local_data = (_data == _local_data); + + deallocate(); + + const int ret = posix_memalign((void**)&_local_data, 16, _size * sizeof(Sample)); + if (ret != 0) { + cerr << "[Buffer] Failed to allocate buffer. Aborting." << endl; + exit(EXIT_FAILURE); + } + + assert(ret == 0); + assert(_local_data); + + if (using_local_data) + _data = _local_data; + else + _data = old_data; + + set_block(0, 0, _size-1); +} + + +/** Allocate and use a locally managed buffer (data). + */ +void +AudioBuffer::allocate() +{ + assert(!_joined_buf); + assert(_local_data == NULL); + assert(_size > 0); + + const int ret = posix_memalign((void**)&_local_data, 16, _size * sizeof(Sample)); + if (ret != 0) { + cerr << "[Buffer] Failed to allocate buffer. Aborting." << endl; + exit(EXIT_FAILURE); + } + + assert(ret == 0); + assert(_local_data); + + _data = _local_data; + + set_block(0, 0, _size-1); +} + + +/** Free locally allocated buffer. + */ +void +AudioBuffer::deallocate() +{ + assert(!_joined_buf); + free(_local_data); + _local_data = NULL; + _data = NULL; +} + + +/** Empty (ie zero) the buffer. + */ +void +AudioBuffer::clear() +{ + set_block(0, 0, _size-1); + _state = OK; + _filled_size = 0; +} + + +/** 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. + */ +void +AudioBuffer::set_value(Sample val, FrameTime cycle_start, FrameTime time) +{ + if (_size == 1) + time = cycle_start; + + FrameTime offset = time - cycle_start; + assert(offset <= _size); + + if (offset < _size) { + set_block(val, offset, _size - 1); + + if (offset > 0) + _state = HALF_SET_CYCLE_1; + } // else trigger at very end of block + + _set_time = time; + _set_value = val; +} + + +/** Set a block of buffer to @a val. + * + * @a start_sample and @a end_sample define the inclusive range to be set. + */ +void +AudioBuffer::set_block(Sample val, size_t start_offset, size_t end_offset) +{ + assert(end_offset >= start_offset); + assert(end_offset < _size); + + Sample* const buf = data(); + assert(buf); + + for (size_t i = start_offset; i <= end_offset; ++i) + buf[i] = val; +} + + +/** Scale a block of buffer by @a val. + * + * @a start_sample and @a end_sample define the inclusive range to be set. + */ +void +AudioBuffer::scale(Sample val, size_t start_sample, size_t end_sample) +{ + assert(end_sample >= start_sample); + assert(end_sample < _size); + + Sample* const buf = data(); + assert(buf); + + for (size_t i=start_sample; i <= end_sample; ++i) + buf[i] *= val; +} + + +/** 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. + */ +void +AudioBuffer::copy(const Buffer* src, size_t start_sample, size_t end_sample) +{ + assert(end_sample >= start_sample); + assert(end_sample < _size); + assert(src); + assert(src->type() == DataType::CONTROL || DataType::AUDIO); + + Sample* const buf = data(); + assert(buf); + + const Sample* const src_buf = ((AudioBuffer*)src)->data(); + assert(src_buf); + + for (size_t i=start_sample; i <= end_sample; ++i) + buf[i] = src_buf[i]; +} + + +/** 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. + */ +void +AudioBuffer::accumulate(const AudioBuffer* const src, size_t start_sample, size_t end_sample) +{ + assert(end_sample >= start_sample); + assert(end_sample < _size); + assert(src); + + Sample* const buf = data(); + assert(buf); + + const Sample* const src_buf = src->data(); + assert(src_buf); + + for (size_t i=start_sample; i <= end_sample; ++i) + buf[i] += src_buf[i]; + +} + + +/** Use another buffer's data instead of the local one. + * + * This buffer will essentially be identical to @a buf after this call. + */ +bool +AudioBuffer::join(Buffer* buf) +{ + AudioBuffer* abuf = dynamic_cast(buf); + if (!abuf) + return false; + + assert(abuf->size() >= _size); + + _joined_buf = abuf; + _filled_size = abuf->filled_size(); + + assert(_filled_size <= _size); + + return true; +} + + +void +AudioBuffer::unjoin() +{ + _joined_buf = NULL; + _data = _local_data; +} + + +void +AudioBuffer::prepare_read(FrameTime start, SampleCount nframes) +{ + // FIXME: nframes parameter doesn't actually work, + // writing starts from 0 every time + assert(_size == 1 || nframes == _size); + + switch (_state) { + case HALF_SET_CYCLE_1: + if (start > _set_time) + _state = HALF_SET_CYCLE_2; + break; + case HALF_SET_CYCLE_2: + set_block(_set_value, 0, _size-1); + _state = OK; + break; + default: + break; + } +} + + +/** Set the buffer (data) used. + * + * This is only to be used by Drivers (to provide zero-copy processing). + */ +void +AudioBuffer::set_data(Sample* buf) +{ + assert(buf); + assert(!_joined_buf); + _data = buf; +} + + +} // namespace Ingen diff --git a/src/engine/AudioBuffer.hpp b/src/engine/AudioBuffer.hpp new file mode 100644 index 00000000..513c188f --- /dev/null +++ b/src/engine/AudioBuffer.hpp @@ -0,0 +1,84 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AUDIOBUFFER_H +#define AUDIOBUFFER_H + +#include +#include +#include +#include "types.hpp" +#include "Buffer.hpp" + +namespace Ingen { + + +class AudioBuffer : public Buffer +{ +public: + AudioBuffer(size_t capacity); + + void clear(); + void set_value(Sample val, FrameTime cycle_start, FrameTime time); + void set_block(Sample val, size_t start_offset, size_t end_offset); + void scale(Sample val, size_t start_sample, size_t end_sample); + void copy(const Buffer* src, size_t start_sample, size_t end_sample); + void accumulate(const AudioBuffer* src, size_t start_sample, size_t end_sample); + + bool join(Buffer* buf); + void unjoin(); + + /** For driver use only!! */ + void set_data(Sample* data); + + inline const void* raw_data() const { return _data; } + inline void* raw_data() { return _data; } + + inline Sample* data() const { return _data; } + + inline Sample& value_at(size_t offset) const + { assert(offset < _size); return data()[offset]; } + + void prepare_read(FrameTime start, SampleCount nframes); + void prepare_write(FrameTime start, SampleCount nframes) {} + + void rewind() const {} + void resize(size_t size); + + void filled_size(size_t size) { _filled_size = size; } + size_t filled_size() const { return _filled_size; } + size_t size() const { return _size; } + +private: + enum State { OK, HALF_SET_CYCLE_1, HALF_SET_CYCLE_2 }; + + void allocate(); + void deallocate(); + + Sample* _data; ///< Used data pointer (probably same as _local_data) + Sample* _local_data; ///< Locally allocated buffer (possibly unused if joined or set_data used) + size_t _size; ///< Allocated buffer size + size_t _filled_size; ///< Usable buffer size (for MIDI ports etc) + State _state; ///< State of buffer for setting values next cycle + Sample _set_value; ///< Value set by @ref set (may need to be set next cycle) + FrameTime _set_time; ///< Time _set_value was set (to reset next cycle) +}; + + +} // namespace Ingen + +#endif // AUDIOBUFFER_H diff --git a/src/engine/AudioDriver.hpp b/src/engine/AudioDriver.hpp new file mode 100644 index 00000000..6b5ea8dc --- /dev/null +++ b/src/engine/AudioDriver.hpp @@ -0,0 +1,62 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AUDIODRIVER_H +#define AUDIODRIVER_H + +#include +#include +#include "Driver.hpp" +#include "types.hpp" +#include "interface/DataType.hpp" + +namespace Ingen { + +class PatchImpl; +class AudioDriver; +class PortImpl; +class ProcessContext; + + +/** Audio driver abstract base class. + * + * \ingroup engine + */ +class AudioDriver : public Driver +{ +public: + AudioDriver() : Driver(DataType::AUDIO) {} + + virtual void set_root_patch(PatchImpl* patch) = 0; + virtual PatchImpl* root_patch() = 0; + + virtual void add_port(DriverPort* port) = 0; + virtual DriverPort* remove_port(const Raul::Path& path) = 0; + + virtual SampleCount buffer_size() const = 0; + virtual SampleCount sample_rate() const = 0; + virtual SampleCount frame_time() const = 0; + + virtual bool is_realtime() const = 0; + + virtual ProcessContext& context() = 0; +}; + + +} // namespace Ingen + +#endif // AUDIODRIVER_H diff --git a/src/engine/Buffer.cpp b/src/engine/Buffer.cpp new file mode 100644 index 00000000..d019146c --- /dev/null +++ b/src/engine/Buffer.cpp @@ -0,0 +1,38 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "AudioBuffer.hpp" +#include "EventBuffer.hpp" + +namespace Ingen { + + +Buffer* +Buffer::create(DataType type, size_t size) +{ + if (type.is_control()) + return new AudioBuffer(1); + else if (type.is_audio()) + return new AudioBuffer(size); + else if (type.is_event()) + return new EventBuffer(size); + else + throw; +} + + +} // namespace Ingen diff --git a/src/engine/Buffer.hpp b/src/engine/Buffer.hpp new file mode 100644 index 00000000..e388e2e8 --- /dev/null +++ b/src/engine/Buffer.hpp @@ -0,0 +1,76 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef BUFFER_H +#define BUFFER_H + +#include +#include +#include +#include +#include "types.hpp" +#include "interface/DataType.hpp" + +namespace Ingen { + + +class Buffer : public boost::noncopyable, public Raul::Deletable +{ +public: + Buffer(Shared::DataType type, size_t size) + : _type(type) + , _size(size) + , _joined_buf(NULL) + {} + + static Buffer* create(Shared::DataType type, size_t size); + + /** Clear contents and reset state */ + virtual void clear() = 0; + + virtual void* raw_data() = 0; + virtual const void* raw_data() const = 0; + + /** Rewind (ie reset read pointer), but leave contents unchanged */ + virtual void rewind() const = 0; + + virtual void prepare_read(FrameTime start, SampleCount nframes) = 0; + virtual void prepare_write(FrameTime start, SampleCount nframes) = 0; + + bool is_joined() const { return (_joined_buf != NULL); } + Buffer* joined_buffer() const { return _joined_buf; } + + virtual bool join(Buffer* buf) = 0; + virtual void unjoin() = 0; + + virtual void copy(const Buffer* src, size_t start_sample, size_t end_sample) = 0; + + virtual void resize(size_t size) { _size = size; } + + Shared::DataType type() const { return _type; } + size_t size() const { return _size; } + +protected: + Shared::DataType _type; + size_t _size; + Buffer* _joined_buf; +}; + + +} // namespace Ingen + +#endif // BUFFER_H diff --git a/src/engine/ClientBroadcaster.cpp b/src/engine/ClientBroadcaster.cpp new file mode 100644 index 00000000..d754f072 --- /dev/null +++ b/src/engine/ClientBroadcaster.cpp @@ -0,0 +1,271 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include "interface/ClientInterface.hpp" +#include "ClientBroadcaster.hpp" +#include "EngineStore.hpp" +#include "NodeFactory.hpp" +#include "util.hpp" +#include "PatchImpl.hpp" +#include "NodeImpl.hpp" +#include "PluginImpl.hpp" +#include "PortImpl.hpp" +#include "ConnectionImpl.hpp" +#include "AudioDriver.hpp" +#include "ObjectSender.hpp" +#include "OSCClientSender.hpp" + +using namespace std; +using Ingen::Shared::ClientInterface; + +namespace Ingen { + + +/** Register a client to receive messages over the notification band. + */ +void +ClientBroadcaster::register_client(const string& uri, ClientInterface* client) +{ + Clients::iterator i = _clients.find(uri); + + if (i == _clients.end()) { + _clients[uri] = client; + cout << "[ClientBroadcaster] Registered client: " << uri << endl; + } else { + cout << "[ClientBroadcaster] Client already registered: " << uri << endl; + } +} + + +/** Remove a client from the list of registered clients. + * + * @return true if client was found and removed. + */ +bool +ClientBroadcaster::unregister_client(const string& uri) +{ + size_t erased = _clients.erase(uri); + + if (erased > 0) + cout << "Unregistered client: " << uri << endl; + else + cout << "Failed to find client to unregister: " << uri << endl; + + return (erased > 0); +} + + + +/** 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). + */ +ClientInterface* +ClientBroadcaster::client(const string& uri) +{ + Clients::iterator i = _clients.find(uri); + if (i != _clients.end()) { + return (*i).second; + } else { + return NULL; + } +} + + +void +ClientBroadcaster::bundle_begin() +{ + for (Clients::const_iterator i = _clients.begin(); i != _clients.end(); ++i) + (*i).second->bundle_begin(); +} + + +void +ClientBroadcaster::bundle_end() +{ + for (Clients::const_iterator i = _clients.begin(); i != _clients.end(); ++i) + (*i).second->bundle_end(); +} + + +void +ClientBroadcaster::send_error(const string& msg) +{ + for (Clients::const_iterator i = _clients.begin(); i != _clients.end(); ++i) + (*i).second->error(msg); +} + +void +ClientBroadcaster::send_plugins_to(ClientInterface* client, const NodeFactory::Plugins& plugins) +{ + client->transfer_begin(); + + for (NodeFactory::Plugins::const_iterator i = plugins.begin(); i != plugins.end(); ++i) { + const PluginImpl* const plugin = i->second; + client->new_plugin(plugin->uri(), plugin->type_uri(), plugin->symbol(), plugin->name()); + } + + client->transfer_end(); +} + + +void +ClientBroadcaster::send_plugins(const NodeFactory::Plugins& plugins) +{ + for (Clients::const_iterator c = _clients.begin(); c != _clients.end(); ++c) + send_plugins_to((*c).second, plugins); +} + + +void +ClientBroadcaster::send_node(const NodeImpl* node, bool recursive) +{ + for (Clients::const_iterator i = _clients.begin(); i != _clients.end(); ++i) + ObjectSender::send_node((*i).second, node, recursive); +} + + +void +ClientBroadcaster::send_port(const PortImpl* port) +{ + for (Clients::const_iterator i = _clients.begin(); i != _clients.end(); ++i) + ObjectSender::send_port((*i).second, port); +} + + +void +ClientBroadcaster::send_destroyed(const string& path) +{ + assert(path != "/"); + for (Clients::const_iterator i = _clients.begin(); i != _clients.end(); ++i) + (*i).second->destroy(path); +} + + +void +ClientBroadcaster::send_patch_cleared(const string& patch_path) +{ + for (Clients::const_iterator i = _clients.begin(); i != _clients.end(); ++i) + (*i).second->patch_cleared(patch_path); +} + +void +ClientBroadcaster::send_connection(const SharedPtr c) +{ + for (Clients::const_iterator i = _clients.begin(); i != _clients.end(); ++i) + (*i).second->connect(c->src_port()->path(), c->dst_port()->path()); +} + + +void +ClientBroadcaster::send_disconnection(const string& src_port_path, const string& dst_port_path) +{ + for (Clients::const_iterator i = _clients.begin(); i != _clients.end(); ++i) + (*i).second->disconnect(src_port_path, dst_port_path); +} + + +/** Send notification of a variable update. + * + * Like control changes, does not send update to client that set the variable, if applicable. + */ +void +ClientBroadcaster::send_variable_change(const string& node_path, const string& key, const Atom& value) +{ + for (Clients::const_iterator i = _clients.begin(); i != _clients.end(); ++i) + (*i).second->set_variable(node_path, key, value); +} + + +/** Send notification of a property update. + * + * Like control changes, does not send update to client that set the property, if applicable. + */ +void +ClientBroadcaster::send_property_change(const string& node_path, const string& key, const Atom& value) +{ + for (Clients::const_iterator i = _clients.begin(); i != _clients.end(); ++i) + (*i).second->set_property(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_port_value(const string& port_path, const Raul::Atom& value) +{ + for (Clients::const_iterator i = _clients.begin(); i != _clients.end(); ++i) + (*i).second->set_port_value(port_path, value); +} + + +void +ClientBroadcaster::send_port_activity(const string& port_path) +{ + for (Clients::const_iterator i = _clients.begin(); i != _clients.end(); ++i) + (*i).second->port_activity(port_path); +} + + +void +ClientBroadcaster::send_program_add(const string& node_path, int bank, int program, const string& name) +{ + for (Clients::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 (Clients::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 PatchImpl* p, bool recursive) +{ + for (Clients::const_iterator i = _clients.begin(); i != _clients.end(); ++i) + ObjectSender::send_patch((*i).second, p, recursive); +} + + +/** Sends notification of an GraphObject's renaming + */ +void +ClientBroadcaster::send_rename(const string& old_path, const string& new_path) +{ + for (Clients::const_iterator i = _clients.begin(); i != _clients.end(); ++i) + (*i).second->object_renamed(old_path, new_path); +} + + +} // namespace Ingen diff --git a/src/engine/ClientBroadcaster.hpp b/src/engine/ClientBroadcaster.hpp new file mode 100644 index 00000000..b0963610 --- /dev/null +++ b/src/engine/ClientBroadcaster.hpp @@ -0,0 +1,97 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CLIENTBROADCASTER_H +#define CLIENTBROADCASTER_H + +#include +#include +#include +#include +#include +#include +#include "interface/ClientInterface.hpp" +#include "types.hpp" +#include "NodeFactory.hpp" + +using std::string; + +namespace Ingen { + +class NodeImpl; +class PortImpl; +class PluginImpl; +class PatchImpl; +class ConnectionImpl; +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 string& uri, ClientInterface* client); + bool unregister_client(const string& uri); + + ClientInterface* client(const string& uri); + + //void send_client_registration(const string& url, int client_id); + + void bundle_begin(); + void bundle_end(); + + // Error that isn't the direct result of a request + void send_error(const string& msg); + + void send_plugins(const NodeFactory::Plugins& plugin_list); + void send_patch(const PatchImpl* p, bool recursive); + void send_node(const NodeImpl* node, bool recursive); + void send_port(const PortImpl* port); + void send_destroyed(const string& path); + void send_patch_cleared(const string& patch_path); + void send_connection(const SharedPtr 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_variable_change(const string& node_path, const string& key, const Raul::Atom& value); + void send_property_change(const string& node_path, const string& key, const Raul::Atom& value); + void send_port_value(const string& port_path, const Raul::Atom& value); + void send_port_activity(const string& port_path); + 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); + + void send_plugins_to(ClientInterface*, const NodeFactory::Plugins& plugin_list); + +private: + typedef std::map Clients; + Clients _clients; +}; + + + +} // namespace Ingen + +#endif // CLIENTBROADCASTER_H + diff --git a/src/engine/CompiledPatch.hpp b/src/engine/CompiledPatch.hpp new file mode 100644 index 00000000..9ca1d6e9 --- /dev/null +++ b/src/engine/CompiledPatch.hpp @@ -0,0 +1,83 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef COMPILED_PATCH_HPP +#define COMPILED_PATCH_HPP + +#include +#include +#include +#include +#include + +using Raul::List; + +using namespace std; + +namespace Ingen { + + +/** All information required about a node to execute it in an audio thread. + */ +struct CompiledNode { + CompiledNode(NodeImpl* n, size_t np, List* d) + : _node(n), _n_providers(np) + { + // Copy to a vector for maximum iteration speed and cache optimization + // (Need to take a copy anyway) + + _dependants.reserve(d->size()); + for (List::iterator i = d->begin(); i != d->end(); ++i) + _dependants.push_back(*i); + } + + NodeImpl* node() const { return _node; } + size_t n_providers() const { return _n_providers; } + const vector& dependants() const { return _dependants; } + +private: + NodeImpl* _node; + size_t _n_providers; ///< Number of input ready signals to trigger run + vector _dependants; ///< Nodes this one's output ports are connected to +}; + + +/** A patch and a set of connections, "compiled" into a flat structure with + * the correct order so the audio thread(s) can execute it without + * threading problems (since the preprocessor thread fiddles with other + * things). + * + * Currently objects still have some 'heavyweight' connection state, but + * eventually this should be the only place a particular set of connections + * in a patch is stored, so various "connection presets" can be switched + * in a realtime safe way. + * + * The nodes contained here are sorted in the order they must be executed. + * The parallel processing algorithm guarantees no node will be executed + * before it's providers, using this order as well as semaphores. + */ +struct CompiledPatch : public std::vector + , public Raul::Deletable + , public boost::noncopyable { + /*CompiledPatch() : std::vector() {} + CompiledPatch(size_t reserve) : std::vector(reserve) {}*/ +}; + + +} // namespace Ingen + +#endif // COMPILED_PATCH_HPP diff --git a/src/engine/ConnectionImpl.cpp b/src/engine/ConnectionImpl.cpp new file mode 100644 index 00000000..d226b4dd --- /dev/null +++ b/src/engine/ConnectionImpl.cpp @@ -0,0 +1,185 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "util.hpp" +#include "ConnectionImpl.hpp" +#include "NodeImpl.hpp" +#include "PortImpl.hpp" +#include "AudioBuffer.hpp" +#include "ProcessContext.hpp" + +#include +using namespace std; + +namespace Ingen { + + +/** Constructor for a connection from a node's output port. + * + * This handles both polyphonic and monophonic nodes, transparently to the + * user (InputPort). + */ +ConnectionImpl::ConnectionImpl(PortImpl* src_port, PortImpl* dst_port) + : _src_port(src_port) + , _dst_port(dst_port) + , _local_buffer(NULL) + , _buffer_size(dst_port->buffer_size()) + /*, _must_mix( (src_port->poly() != dst_port->poly()) + || (src_port->buffer(0)->size() < dst_port->buffer(0)->size()) )*/ + , _must_mix( (src_port->polyphonic() && (! dst_port->polyphonic())) + || (src_port->poly() != dst_port->poly() ) + || (src_port->buffer(0)->size() < dst_port->buffer(0)->size()) ) + , _pending_disconnection(false) +{ + assert(src_port); + assert(dst_port); + assert(src_port != dst_port); + assert(src_port->path() != dst_port->path()); + assert(src_port->type() == dst_port->type() + || ( (src_port->type() == DataType::CONTROL || src_port->type() == DataType::AUDIO) + && (dst_port->type() == DataType::CONTROL || dst_port->type() == DataType::AUDIO) )); + + /*assert((src_port->parent_node()->poly() == dst_port->parent_node()->poly()) + || (src_port->parent_node()->poly() == 1 || dst_port->parent_node()->poly() == 1));*/ + + if (type() == DataType::EVENT) + _must_mix = false; // FIXME: kludge + + if (_must_mix) + _local_buffer = Buffer::create(dst_port->type(), dst_port->buffer(0)->size()); + + /* FIXME: 1->1 connections with a destination with fixed buffers copies unecessarily */ + //cerr << src_port->path() << " -> " << dst_port->path() << " must mix: " << _must_mix << endl; +} + + +ConnectionImpl::~ConnectionImpl() +{ + delete _local_buffer; +} + + +void +ConnectionImpl::set_buffer_size(size_t size) +{ + if (_must_mix) { + assert(_local_buffer); + delete _local_buffer; + + _local_buffer = Buffer::create(_dst_port->type(), _dst_port->buffer(0)->size()); + } + + _buffer_size = size; +} + + +void +ConnectionImpl::prepare_poly(uint32_t poly) +{ + _src_port->prepare_poly(poly); + + if (type() == DataType::CONTROL || type() == DataType::AUDIO) + _must_mix = (poly > 1) && ( + (_src_port->poly() != _dst_port->poly()) + || (_src_port->polyphonic() && !_dst_port->polyphonic()) + || (_src_port->parent()->polyphonic() && !_dst_port->parent()->polyphonic()) ); + + /*cerr << src_port()->path() << " * " << src_port()->poly() + << " -> " << dst_port()->path() << " * " << dst_port()->poly() + << "\t\tmust mix: " << _must_mix << " at poly " << poly << endl;*/ + + if (_must_mix && ! _local_buffer) + _local_buffer = Buffer::create(_dst_port->type(), _dst_port->buffer(0)->size()); +} + + +void +ConnectionImpl::apply_poly(Raul::Maid& maid, uint32_t poly) +{ + _src_port->apply_poly(maid, poly); + if (poly == 1 && _local_buffer && !_must_mix) { + maid.push(_local_buffer); + _local_buffer = NULL; + } +} + + +void +ConnectionImpl::process(ProcessContext& context) +{ + // FIXME: nframes parameter not used + assert(_buffer_size == 1 || _buffer_size == context.nframes()); + + /* 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. */ + + /*cerr << src_port()->path() << " * " << src_port()->poly() + << " -> " << dst_port()->path() << " * " << dst_port()->poly() + << "\t\tmust mix: " << _must_mix << endl;*/ + + if (_must_mix && (type() == DataType::CONTROL || type() == DataType::AUDIO)) { + + const AudioBuffer* const src_buffer = (AudioBuffer*)src_port()->buffer(0); + AudioBuffer* mix_buf = (AudioBuffer*)_local_buffer; + + assert(mix_buf); + + const size_t copy_size = std::min(src_buffer->size(), mix_buf->size()); + + // Copy src buffer to start of mix buffer + mix_buf->copy((AudioBuffer*)src_port()->buffer(0), 0, copy_size-1); + + // Write last value of src buffer to remainder of dst buffer, if necessary + if (copy_size < mix_buf->size()) + mix_buf->set_block(src_buffer->value_at(copy_size-1), copy_size, mix_buf->size()-1); + + // Accumulate the source's voices into local buffer starting at the second + // voice (buffer is already set to first voice above) + for (uint32_t j=1; j < src_port()->poly(); ++j) { + mix_buf->accumulate((AudioBuffer*)src_port()->buffer(j), 0, copy_size-1); + } + + // Find the summed value and write it to the remainder of dst buffer + if (copy_size < mix_buf->size()) { + float src_value = src_buffer->value_at(copy_size-1); + for (uint32_t j=1; j < src_port()->poly(); ++j) + src_value += ((AudioBuffer*)src_port()->buffer(j))->value_at(copy_size-1); + + mix_buf->set_block(src_value, copy_size, mix_buf->size()-1); + } + + // Scale the buffer down. + if (src_port()->poly() > 1) + mix_buf->scale(1.0f/(float)src_port()->poly(), 0, _buffer_size-1); + + } else if (_must_mix && type() == DataType::EVENT) { + + std::cerr << "WARNING: No event mixing." << std::endl; + + } + +} + + +} // namespace Ingen + diff --git a/src/engine/ConnectionImpl.hpp b/src/engine/ConnectionImpl.hpp new file mode 100644 index 00000000..b3da1b54 --- /dev/null +++ b/src/engine/ConnectionImpl.hpp @@ -0,0 +1,101 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CONNECTIONIMPL_H +#define CONNECTIONIMPL_H + +#include +#include +#include +#include "interface/DataType.hpp" +#include "interface/Connection.hpp" +#include "PortImpl.hpp" +#include "types.hpp" + +namespace Ingen { + +class PortImpl; +class Buffer; + + +/** 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 ConnectionImpl : public Raul::Deletable, public Shared::Connection +{ +public: + ConnectionImpl(PortImpl* src_port, PortImpl* dst_port); + virtual ~ConnectionImpl(); + + PortImpl* src_port() const { return _src_port; } + PortImpl* dst_port() const { return _dst_port; } + + const Raul::Path src_port_path() const { return _src_port->path(); } + const Raul::Path dst_port_path() const { return _dst_port->path(); } + + /** Used by some (recursive) events to prevent double disconnections */ + bool pending_disconnection() { return _pending_disconnection; } + void pending_disconnection(bool b) { _pending_disconnection = b; } + + void process(ProcessContext& context); + + /** Get the buffer for a particular voice. + * A Connection is smart - it knows the destination port requesting the + * buffer, and will return accordingly (ie the same buffer for every voice + * in a mono->poly connection). + */ + inline Buffer* buffer(size_t voice) const; + + void set_buffer_size(size_t size); + void prepare_poly(uint32_t poly); + void apply_poly(Raul::Maid& maid, uint32_t poly); + + DataType type() const { return _src_port->type(); } + +protected: + PortImpl* const _src_port; + PortImpl* const _dst_port; + Buffer* _local_buffer; + size_t _buffer_size; + bool _must_mix; + bool _pending_disconnection; +}; + + +inline Buffer* +ConnectionImpl::buffer(size_t voice) const +{ + if (_must_mix) { + return _local_buffer; + } else if ( ! _src_port->polyphonic()) { + return _src_port->buffer(0); + } else { + return _src_port->buffer(voice); + } +} + + +} // namespace Ingen + +#endif // CONNECTIONIMPL_H diff --git a/src/engine/Context.hpp b/src/engine/Context.hpp new file mode 100644 index 00000000..2e000fb1 --- /dev/null +++ b/src/engine/Context.hpp @@ -0,0 +1,51 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CONTEXT_H +#define CONTEXT_H + +namespace Ingen { + +class Engine; + +class Context +{ +public: + enum ID { + AUDIO, + MESSAGE + }; + + Context(Engine& engine, ID id) + : _id(id) + , _engine(engine) + {} + + virtual ~Context() {} + + inline Engine& engine() const { return _engine; } + +protected: + ID _id; ///< Fast ID for this context + Engine& _engine; ///< Engine we're running in +}; + + +} // namespace Ingen + +#endif // CONTEXT_H + diff --git a/src/engine/Driver.hpp b/src/engine/Driver.hpp new file mode 100644 index 00000000..590b66b5 --- /dev/null +++ b/src/engine/Driver.hpp @@ -0,0 +1,98 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef DRIVER_H +#define DRIVER_H + +#include +#include +#include +#include "interface/DataType.hpp" +#include "DuplexPort.hpp" + +namespace Ingen { + +class DuplexPort; + + +/** Representation of a "system" (eg outside Ingen) 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 : boost::noncopyable { +public: + virtual ~DriverPort() {} + + /** Set the name of the system port */ + virtual void set_name(const std::string& name) = 0; + + bool is_input() const { return _patch_port->is_input(); } + DuplexPort* patch_port() const { return _patch_port; } + +protected: + /** is_input from the perspective outside of ingen */ + DriverPort(DuplexPort* port) : _patch_port(port) {} + + DuplexPort* _patch_port; +}; + + +/** Driver abstract base class. + * + * A Driver is, from the perspective of GraphObjects (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. + * + * \ingroup engine + */ +class Driver : boost::noncopyable +{ +public: + Driver(DataType type) + : _type(type) + {} + + 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(DuplexPort* patch_port) = 0; + + virtual DriverPort* driver_port(const Raul::Path& path) = 0; + + virtual void add_port(DriverPort* port) = 0; + virtual DriverPort* remove_port(const Raul::Path& path) = 0; + +protected: + DataType _type; +}; + + +} // namespace Ingen + +#endif // DRIVER_H diff --git a/src/engine/DuplexPort.cpp b/src/engine/DuplexPort.cpp new file mode 100644 index 00000000..31bfbe09 --- /dev/null +++ b/src/engine/DuplexPort.cpp @@ -0,0 +1,108 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include "util.hpp" +#include "DuplexPort.hpp" +#include "ConnectionImpl.hpp" +#include "OutputPort.hpp" +#include "NodeImpl.hpp" +#include "ProcessContext.hpp" +#include "EventBuffer.hpp" + +using namespace std; + +namespace Ingen { + + +DuplexPort::DuplexPort(NodeImpl* parent, const string& name, uint32_t index, uint32_t poly, DataType type, const Atom& value, size_t buffer_size, bool is_output) + : PortImpl(parent, name, index, poly, type, value, buffer_size) + , InputPort(parent, name, index, poly, type, value, buffer_size) + , OutputPort(parent, name, index, poly, type, value, buffer_size) + , _is_output(is_output) +{ + assert(PortImpl::_parent == parent); +} + + +void +DuplexPort::pre_process(ProcessContext& context) +{ + // + + /*cerr << endl << "{ duplex pre" << endl; + cerr << path() << " duplex pre: fixed buffers: " << fixed_buffers() << endl; + cerr << path() << " duplex pre: buffer: " << buffer(0) << endl; + cerr << path() << " duplex pre: is_output: " << _is_output << " { " << endl;*/ + + /*if (type() == DataType::EVENT) + for (uint32_t i=0; i < _poly; ++i) + cerr << path() << " (" << buffer(i) << ") # events: " + << ((EventBuffer*)buffer(i))->event_count() + << ", joined: " << _buffers->at(i)->is_joined() << endl;*/ + + if (_is_output) { + + for (uint32_t i=0; i < _poly; ++i) + if (!_buffers->at(i)->is_joined()) + _buffers->at(i)->prepare_write(context.start(), context.nframes()); + + } else { + + for (uint32_t i=0; i < _poly; ++i) + _buffers->at(i)->prepare_read(context.start(), context.nframes()); + + broadcast(context); + } + + //cerr << "} duplex pre " << path() << endl; + + // +} + + +void +DuplexPort::post_process(ProcessContext& context) +{ + // + + /*cerr << endl << "{ duplex post" << endl; + cerr << path() << " duplex post: fixed buffers: " << fixed_buffers() << endl; + cerr << path() << " duplex post: buffer: " << buffer(0) << endl; + cerr << path() << " duplex post: is_output: " << _is_output << " { " << endl; + + if (type() == DataType::EVENT) + for (uint32_t i=0; i < _poly; ++i) + cerr << path() << " (" << buffer(i) << ") # events: " + << ((EventBuffer*)buffer(i))->event_count() + << ", joined: " << _buffers->at(i)->is_joined() << endl;*/ + + if (_is_output) { + InputPort::pre_process(context); // Mix down inputs + broadcast(context); + } + + //cerr << "} duplex post " << path() << endl; + + // +} + + +} // namespace Ingen + diff --git a/src/engine/DuplexPort.hpp b/src/engine/DuplexPort.hpp new file mode 100644 index 00000000..43d202a9 --- /dev/null +++ b/src/engine/DuplexPort.hpp @@ -0,0 +1,68 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef DUPLEXPORT_H +#define DUPLEXPORT_H + +#include +#include +#include "types.hpp" +#include "Buffer.hpp" +#include "InputPort.hpp" +#include "OutputPort.hpp" + +namespace Ingen { + +class NodeImpl; + + +/** A duplex port (which is both an InputPort and an OutputPort) + * + * This is used for Patch ports, since they need to appear as both an input + * and an output port based on context. Eg. a patch output appears as an + * input inside the patch, so nodes inside the patch can feed it data. + * + * \ingroup engine + */ +class DuplexPort : public InputPort, public OutputPort +{ +public: + DuplexPort(NodeImpl* parent, + const std::string& name, + uint32_t index, + uint32_t poly, + DataType type, + const Atom& value, + size_t buffer_size, + bool is_output); + + virtual ~DuplexPort() {} + + void pre_process(ProcessContext& context); + void post_process(ProcessContext& context); + + bool is_input() const { return !_is_output; } + bool is_output() const { return _is_output; } + +protected: + bool _is_output; +}; + + +} // namespace Ingen + +#endif // DUPLEXPORT_H diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp new file mode 100644 index 00000000..a08a37ca --- /dev/null +++ b/src/engine/Engine.cpp @@ -0,0 +1,317 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include "Engine.hpp" +#include CONFIG_H_PATH +#include "tuning.hpp" +#include "Event.hpp" +#include "common/interface/EventType.hpp" +#include "shared/Store.hpp" +#include "JackAudioDriver.hpp" +#include "NodeFactory.hpp" +#include "ClientBroadcaster.hpp" +#include "PatchImpl.hpp" +#include "EngineStore.hpp" +#include "MidiDriver.hpp" +#include "OSCDriver.hpp" +#include "QueuedEventSource.hpp" +#include "PostProcessor.hpp" +#include "CreatePatchEvent.hpp" +#include "EnablePatchEvent.hpp" +#include "OSCEngineReceiver.hpp" +#ifdef HAVE_SOUP +#include "HTTPEngineReceiver.hpp" +#endif +#include "PostProcessor.hpp" +#include "ProcessSlave.hpp" +#include "ProcessContext.hpp" +#include "MessageContext.hpp" +#include "ThreadManager.hpp" +#ifdef HAVE_JACK_MIDI +#include "JackMidiDriver.hpp" +#endif +using namespace std; + +namespace Ingen { + + +Engine::Engine(Ingen::Shared::World* world) + : _world(world) + , _midi_driver(NULL) + , _osc_driver(NULL) + , _maid(new Raul::Maid(maid_queue_size)) + , _post_processor(new PostProcessor(*this, /**_maid, */post_processor_queue_size)) + , _broadcaster(new ClientBroadcaster()) + , _node_factory(new NodeFactory(world)) + , _message_context(new MessageContext(*this)) + , _quit_flag(false) + , _activated(false) +{ + if (world->store) { + assert(PtrCast(world->store)); + } else { + world->store = SharedPtr(new EngineStore()); + } +} + + +Engine::~Engine() +{ + deactivate(); + + for (EngineStore::iterator i = engine_store()->begin(); + i != engine_store()->end(); ++i) { + if ( ! PtrCast(i->second)->parent() ) + i->second.reset(); + } + + delete _broadcaster; + delete _node_factory; + delete _osc_driver; + delete _post_processor; + //delete _lash_driver; + + delete _maid; + + munlockall(); +} + + +SharedPtr +Engine::engine_store() const +{ + return PtrCast(_world->store); +} + + +Driver* +Engine::driver(DataType type, EventType event_type) +{ + if (type == DataType::AUDIO) { + return _audio_driver.get(); + } else if (type == DataType::EVENT) { + if (event_type == EventType::MIDI) { + return _midi_driver; + } else if (event_type == EventType::OSC) { + return _osc_driver; + } + } + + return NULL; +} + + +int +Engine::main() +{ + Thread::get().set_context(THREAD_POST_PROCESS); + + // Loop until quit flag is set (by OSCReceiver) + while ( ! _quit_flag) { + nanosleep(&main_rate, NULL); + main_iteration(); + } + cout << "[Main] Done main loop." << endl; + + deactivate(); + + return 0; +} + + +/** Run one iteration of the main loop. + * + * NOT realtime safe (this is where deletion actually occurs) + */ +bool +Engine::main_iteration() +{ +/*#ifdef HAVE_LASH + // Process any pending LASH events + if (lash_driver->enabled()) + lash_driver->process_events(); +#endif*/ + // Run the maid (garbage collector) + _post_processor->process(); + _maid->cleanup(); + + return !_quit_flag; +} + + +void +Engine::start_jack_driver() +{ + if ( ! _audio_driver) + _audio_driver = SharedPtr(new JackAudioDriver(*this)); + else + cerr << "[Engine::start_jack_driver] Audio driver already running" << endl; +} + + +void +Engine::start_osc_driver(int port) +{ + if (_event_source) { + cerr << "WARNING: Replacing event source" << endl; + _event_source.reset(); + } + + _event_source = SharedPtr(new OSCEngineReceiver( + *this, pre_processor_queue_size, port)); +} + + +void +Engine::start_http_driver(int port) +{ +#ifdef HAVE_SOUP + // FIXE: leak + HTTPEngineReceiver* server = new HTTPEngineReceiver(*this, port); + server->activate(); +#endif +} + + +SharedPtr +Engine::new_queued_interface() +{ + if (_event_source) { + cerr << "WARNING: Replacing event source" << endl; + _event_source.reset(); + } + + SharedPtr result(new QueuedEngineInterface( + *this, Ingen::event_queue_size, Ingen::event_queue_size)); + + _event_source = result; + + return result; +} + +/* +void +Engine::set_event_source(SharedPtr source) +{ + if (_event_source) + cerr << "Warning: Dropped event source (engine interface)" << endl; + + _event_source = source; +} +*/ + + +bool +Engine::activate(size_t parallelism) +{ + if (_activated) + return false; + + assert(_audio_driver); + assert(_event_source); + +#ifdef HAVE_JACK_MIDI + _midi_driver = new JackMidiDriver(((JackAudioDriver*)_audio_driver.get())->jack_client()); +#else + _midi_driver = new DummyMidiDriver(); +#endif + + _event_source->activate(); + + // Create root patch + + PatchImpl* root_patch = new PatchImpl(*this, "", 1, NULL, + _audio_driver->sample_rate(), _audio_driver->buffer_size(), 1); + root_patch->activate(); + _world->store->add(root_patch); + root_patch->compiled_patch(root_patch->compile()); + + assert(_audio_driver->root_patch() == NULL); + _audio_driver->set_root_patch(root_patch); + + _audio_driver->activate(); + + _process_slaves.clear(); + _process_slaves.reserve(parallelism); + for (size_t i=0; i < parallelism - 1; ++i) + _process_slaves.push_back(new ProcessSlave(*this, _audio_driver->is_realtime())); + + root_patch->enable(); + + //_post_processor->start(); + + _activated = true; + + return true; +} + + +void +Engine::deactivate() +{ + if (!_activated) + return; + + _event_source->deactivate(); + + /*for (Tree::iterator i = _engine_store->objects().begin(); + i != _engine_store->objects().end(); ++i) + if ((*i)->as_node() != NULL && (*i)->as_node()->parent() == NULL) + (*i)->as_node()->deactivate();*/ + + if (_midi_driver != NULL) { + _midi_driver->deactivate(); + delete _midi_driver; + _midi_driver = NULL; + } + + _audio_driver->deactivate(); + + _audio_driver->root_patch()->deactivate(); + + for (size_t i=0; i < _process_slaves.size(); ++i) { + delete _process_slaves[i]; + } + + _process_slaves.clear(); + + // Finalize any lingering events (unlikely) + _post_processor->process(); + + _audio_driver.reset(); + _event_source.reset(); + + _activated = false; +} + + +void +Engine::process_events(ProcessContext& context) +{ + if (_event_source) + _event_source->process(*_post_processor, context); +} + + +} // namespace Ingen diff --git a/src/engine/Engine.hpp b/src/engine/Engine.hpp new file mode 100644 index 00000000..fce826db --- /dev/null +++ b/src/engine/Engine.hpp @@ -0,0 +1,130 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef ENGINE_H +#define ENGINE_H + +#include CONFIG_H_PATH +#include +#include +#include +#include +#include "module/global.hpp" +#include "interface/DataType.hpp" +#include "interface/EventType.hpp" + +template class Queue; + +namespace Raul { class Maid; } + +namespace Ingen { + +class AudioDriver; +class MidiDriver; +class OSCDriver; +class NodeFactory; +class ClientBroadcaster; +class EngineStore; +class EventSource; +class PostProcessor; +class Event; +class QueuedEvent; +class QueuedEngineInterface; +class Driver; +class ProcessSlave; +class ProcessContext; +class MessageContext; + + +/** The main class for the Engine. + * + * This is a (GoF) facade for the engine. Pointers to all components are + * available for more advanced control than this facade allows. + * + * \ingroup engine + */ +class Engine : boost::noncopyable +{ +public: + Engine(Ingen::Shared::World* world); + + virtual ~Engine(); + + virtual int main(); + virtual bool main_iteration(); + + /** Set the quit flag that should kill all threads and exit cleanly. + * Note that it will take some time. */ + virtual void quit() { _quit_flag = true; } + + virtual void start_jack_driver(); + virtual void start_osc_driver(int port); + virtual void start_http_driver(int port); + + virtual SharedPtr new_queued_interface(); + + virtual bool activate(size_t parallelism); + virtual void deactivate(); + + void process_events(ProcessContext& context); + + virtual bool activated() { return _activated; } + + Raul::Maid* maid() const { return _maid; } + EventSource* event_source() const { return _event_source.get(); } + AudioDriver* audio_driver() const { return _audio_driver.get(); } + MidiDriver* midi_driver() const { return _midi_driver; } + OSCDriver* osc_driver() const { return _osc_driver; } + PostProcessor* post_processor() const { return _post_processor; } + ClientBroadcaster* broadcaster() const { return _broadcaster; } + NodeFactory* node_factory() const { return _node_factory; } + MessageContext* message_context() const { return _message_context; } + + SharedPtr engine_store() const; + + /** Return the active driver for the given type */ + Driver* driver(DataType type, EventType event_type); + + Ingen::Shared::World* world() { return _world; } + + typedef std::vector ProcessSlaves; + inline const ProcessSlaves& process_slaves() const { return _process_slaves; } + inline ProcessSlaves& process_slaves() { return _process_slaves; } + +private: + ProcessSlaves _process_slaves; + + Ingen::Shared::World* _world; + + SharedPtr _event_source; + SharedPtr _audio_driver; + MidiDriver* _midi_driver; + OSCDriver* _osc_driver; + Raul::Maid* _maid; + PostProcessor* _post_processor; + ClientBroadcaster* _broadcaster; + NodeFactory* _node_factory; + MessageContext* _message_context; + + bool _quit_flag; + bool _activated; +}; + + +} // namespace Ingen + +#endif // ENGINE_H diff --git a/src/engine/EngineStore.cpp b/src/engine/EngineStore.cpp new file mode 100644 index 00000000..9fcd3806 --- /dev/null +++ b/src/engine/EngineStore.cpp @@ -0,0 +1,182 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include "EngineStore.hpp" +#include "PatchImpl.hpp" +#include "NodeImpl.hpp" +#include "PortImpl.hpp" +#include "ThreadManager.hpp" + +using namespace std; +using namespace Raul; + +namespace Ingen { + + +/** Find the Patch at the given path. + */ +PatchImpl* +EngineStore::find_patch(const Path& path) +{ + GraphObjectImpl* const object = find_object(path); + return dynamic_cast(object); +} + + +/** Find the Node at the given path. + */ +NodeImpl* +EngineStore::find_node(const Path& path) +{ + GraphObjectImpl* const object = find_object(path); + return dynamic_cast(object); +} + + +/** Find the Port at the given path. + */ +PortImpl* +EngineStore::find_port(const Path& path) +{ + GraphObjectImpl* const object = find_object(path); + return dynamic_cast(object); +} + + +/** Find the Object at the given path. + */ +GraphObjectImpl* +EngineStore::find_object(const Path& path) +{ + iterator i = find(path); + return ((i == end()) ? NULL : dynamic_cast(i->second.get())); +} + + +/** Add an object to the store. Not realtime safe. + */ +void +EngineStore::add(GraphObject* obj) +{ + GraphObjectImpl* o = dynamic_cast(obj); + assert(o); + + assert(ThreadManager::current_thread_id() == THREAD_PRE_PROCESS); + + Store::add(obj); +} + + +/** Add a family of objects to the store. Not realtime safe. + */ +void +EngineStore::add(const Objects& table) +{ + assert(ThreadManager::current_thread_id() == THREAD_PRE_PROCESS); + + //cerr << "[EngineStore] Adding " << o[0].second->path() << endl; + cram(table); + + /*cerr << "[EngineStore] Adding Table:" << endl; + for (const_iterator i = table.begin(); i != table.end(); ++i) { + cerr << i->first << " = " << i->second->path() << endl; + }*/ +} + + +/** Remove an object from the store. + * + * Returned is a vector containing all descendants of the object removed + * including the object itself, in lexicographically sorted order by Path. + */ +SharedPtr +EngineStore::remove(const Path& path) +{ + return remove(find(path)); +} + + +/** Remove an object from the store. + * + * Returned is a vector containing all descendants of the object removed + * including the object itself, in lexicographically sorted order by Path. + */ +SharedPtr +EngineStore::remove(iterator object) +{ + assert(ThreadManager::current_thread_id() == THREAD_PRE_PROCESS); + + if (object != end()) { + iterator descendants_end = find_descendants_end(object); + //cout << "[EngineStore] Removing " << object->first << " {" << endl; + SharedPtr removed = yank(object, descendants_end); + /*for (iterator i = removed->begin(); i != removed->end(); ++i) { + cout << "\t" << i->first << endl; + } + cout << "}" << endl;*/ + + return removed; + + } else { + cerr << "[EngineStore] WARNING: Removing " << object->first << " failed." << endl; + return SharedPtr(); + } +} + + +/** Remove all children of an object from the store. + * + * Returned is a vector containing all descendants of the object removed + * in lexicographically sorted order by Path. + */ +SharedPtr +EngineStore::remove_children(const Path& path) +{ + return remove_children(find(path)); +} + + +/** Remove all children of an object from the store. + * + * Returned is a vector containing all descendants of the object removed + * in lexicographically sorted order by Path. + */ +SharedPtr +EngineStore::remove_children(iterator object) +{ + if (object != end()) { + iterator descendants_end = find_descendants_end(object); + if (descendants_end != object) { + iterator first_child = object; + ++first_child; + return yank(first_child, descendants_end); + } + } else { + cerr << "[EngineStore] WARNING: Removing children of " << object->first << " failed." << endl; + return SharedPtr(); + } + + return SharedPtr(); +} + + +} // namespace Ingen diff --git a/src/engine/EngineStore.hpp b/src/engine/EngineStore.hpp new file mode 100644 index 00000000..ad656aeb --- /dev/null +++ b/src/engine/EngineStore.hpp @@ -0,0 +1,68 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef OBJECTSTORE_H +#define OBJECTSTORE_H + +#include +#include +#include +#include "shared/Store.hpp" + +using std::string; +using namespace Raul; + +namespace Ingen { + +namespace Shared { class GraphObject; } + +class PatchImpl; +class NodeImpl; +class PortImpl; +class GraphObjectImpl; + + +/** Storage for all GraphObjects (tree of GraphObject'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 GraphObject + * tree). + * + * Searching with find*() is fast (O(log(n)) binary search on contiguous + * memory) and realtime safe, but modification (add or remove) are neither. + */ +class EngineStore : public Shared::Store +{ +public: + PatchImpl* find_patch(const Path& path); + NodeImpl* find_node(const Path& path); + PortImpl* find_port(const Path& path); + GraphObjectImpl* find_object(const Path& path); + + void add(Shared::GraphObject* o); + void add(const Objects& family); + + SharedPtr remove(const Path& path); + SharedPtr remove(Objects::iterator i); + SharedPtr remove_children(const Path& path); + SharedPtr remove_children(Objects::iterator i); +}; + + +} // namespace Ingen + +#endif // OBJECTSTORE diff --git a/src/engine/Event.cpp b/src/engine/Event.cpp new file mode 100644 index 00000000..8e5c33da --- /dev/null +++ b/src/engine/Event.cpp @@ -0,0 +1,49 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "Event.hpp" +#include "ThreadManager.hpp" +#include "ProcessContext.hpp" + +namespace Ingen { + + +void +Event::execute(ProcessContext& context) +{ + assert(ThreadManager::current_thread_id() == THREAD_PROCESS); + assert(!_executed); + assert(_time <= context.end()); + + // Missed the event, jitter, damnit. + if (_time < context.start()) + _time = context.start(); + + _executed = true; +} + + +void +Event::post_process() +{ + // FIXME: Not true witn monolithic GUI/engine + //assert(ThreadManager::current_thread_id() == THREAD_POST_PROCESS); +} + + +} // namespace Ingen + diff --git a/src/engine/Event.hpp b/src/engine/Event.hpp new file mode 100644 index 00000000..5860da2a --- /dev/null +++ b/src/engine/Event.hpp @@ -0,0 +1,75 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef EVENT_H +#define EVENT_H + +#include +#include +#include +#include "types.hpp" + +namespace Ingen { + +class Engine; +class Responder; +class ProcessContext; + + +/** 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 Raul::Deletable +{ +public: + virtual ~Event() {} + + /** Execute this event in the audio thread (MUST be realtime safe). */ + virtual void execute(ProcessContext& context); + + /** Perform any actions after execution (ie send replies to commands) + * (no realtime requirements). */ + virtual void post_process(); + + inline SampleCount time() const { return _time; } + +protected: + Event(Engine& engine, SharedPtr responder, FrameTime time) + : _engine(engine) + , _responder(responder) + , _time(time) + , _executed(false) + {} + + Engine& _engine; + SharedPtr _responder; + FrameTime _time; + bool _executed; +}; + + +} // namespace Ingen + +#endif // EVENT_H diff --git a/src/engine/EventBuffer.cpp b/src/engine/EventBuffer.cpp new file mode 100644 index 00000000..e8584597 --- /dev/null +++ b/src/engine/EventBuffer.cpp @@ -0,0 +1,318 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define __STDC_LIMIT_MACROS 1 +#include +#include +#include "EventBuffer.hpp" +#include "lv2ext/lv2_event.h" +#include "lv2ext/lv2_event_helpers.h" + +using namespace std; + +namespace Ingen { + + +/** Allocate a new event buffer. + * \a capacity is in bytes (not number of events). + */ +EventBuffer::EventBuffer(size_t capacity) + : Buffer(DataType(DataType::EVENT), capacity) + , _latest_frames(0) + , _latest_subframes(0) +{ + if (capacity > UINT32_MAX) { + cerr << "Event buffer size " << capacity << " too large, aborting." << endl; + throw std::bad_alloc(); + } + + int ret = posix_memalign((void**)&_local_buf, 16, sizeof(LV2_Event_Buffer) + capacity); + if (ret) { + cerr << "Failed to allocate event buffer. Aborting." << endl; + exit(EXIT_FAILURE); + } + + _local_buf->event_count = 0; + _local_buf->capacity = (uint32_t)capacity; + _local_buf->size = 0; + _local_buf->data = reinterpret_cast(_local_buf + 1); + _buf = _local_buf; + + reset(0); + + //cerr << "Creating MIDI Buffer " << _buf << ", capacity = " << _buf->capacity << endl; +} + + +EventBuffer::~EventBuffer() +{ + free(_local_buf); +} + + +/** Use another buffer's data instead of the local one. + * + * This buffer will essentially be identical to @a buf after this call. + */ +bool +EventBuffer::join(Buffer* buf) +{ + EventBuffer* mbuf = dynamic_cast(buf); + if (mbuf) { + _buf = mbuf->local_data(); + _joined_buf = mbuf; + _iter = mbuf->_iter; + _iter.buf = _buf; + return false; + } else { + return false; + } + + //assert(mbuf->size() == _size); + + _joined_buf = mbuf; + + return true; +} + + +void +EventBuffer::unjoin() +{ + _joined_buf = NULL; + _buf = _local_buf; + reset(_this_nframes); +} + + +void +EventBuffer::prepare_read(FrameTime start, SampleCount nframes) +{ + //cerr << "\t" << this << " prepare_read: " << event_count() << endl; + rewind(); + _this_nframes = nframes; +} + + +void +EventBuffer::prepare_write(FrameTime start, SampleCount nframes) +{ + //cerr << "\t" << this << " prepare_write: " << event_count() << endl; + reset(nframes); +} + +/** FIXME: parameters ignored */ +void +EventBuffer::copy(const Buffer* src_buf, size_t start_sample, size_t end_sample) +{ + const EventBuffer* src = dynamic_cast(src_buf); + assert(src); + assert(_buf->capacity >= src->_buf->capacity); + + clear(); + src->rewind(); + + memcpy(_buf, src->_buf, src->_buf->size); +} + + +/** Increment the read position by one event. + * + * \return true if increment was successful, or false if end of buffer reached. + */ +bool +EventBuffer::increment() const +{ + if (lv2_event_is_valid(&_iter)) { + lv2_event_increment(&_iter); + return true; + } else { + return false; + } +} + + +/** + * \return true iff the cursor is valid (ie get_event is safe) + */ +bool +EventBuffer::is_valid() const +{ + return lv2_event_is_valid(&_iter); +} + + +/** Append an event to the buffer. + * + * \a timestamp must be >= the latest event in the buffer, + * and < this_nframes() + * + * \return true on success + */ +bool +EventBuffer::append(uint32_t frames, + uint32_t subframes, + uint16_t type, + uint16_t size, + const uint8_t* data) +{ +#ifndef NDEBUG + if (lv2_event_is_valid(&_iter)) { + LV2_Event* last_event = lv2_event_get(&_iter, NULL); + assert(last_event->frames < frames + || (last_event->frames == frames && last_event->subframes <= subframes)); + } +#endif + + /*cout << "Appending event type " << type << ", size " << size + << " @ " << frames << "." << subframes << endl;*/ + + const bool ret = lv2_event_write(&_iter, frames, subframes, type, size, data); + + if (!ret) + cerr << "ERROR: Failed to write event." << endl; + + _latest_frames = frames; + _latest_subframes = subframes; + + return ret; +} + + +/** Append a buffer of events to the buffer. + * + * \a timestamp must be >= the latest event in the buffer, + * and < this_nframes() + * + * \return true on success + */ +bool +EventBuffer::append(const LV2_Event_Buffer* buf) +{ + uint8_t** data; + bool ret = true; + + LV2_Event_Iterator iter; + for (lv2_event_begin(&iter, _buf); lv2_event_is_valid(&iter); lv2_event_increment(&iter)) { + LV2_Event* ev = lv2_event_get(&iter, data); + +#ifndef NDEBUG + assert((ev->frames > _latest_frames) + || (ev->frames == _latest_frames + && ev->subframes >= _latest_subframes)); +#endif + + if (!(ret = append(ev->frames, ev->subframes, ev->type, ev->size, *data))) { + cerr << "ERROR: Failed to write event." << endl; + break; + } + + _latest_frames = ev->frames; + _latest_subframes = ev->subframes; + } + + return ret; +} + + +/** Read an event from the current position in the buffer + * + * \return true if read was successful, or false if end of buffer reached + */ +bool +EventBuffer::get_event(uint32_t* frames, + uint32_t* subframes, + uint16_t* type, + uint16_t* size, + uint8_t** data) const +{ + if (lv2_event_is_valid(&_iter)) { + LV2_Event* ev = lv2_event_get(&_iter, data); + *frames = ev->frames; + *subframes = ev->subframes; + *type = ev->type; + *size = ev->size; + return true; + } else { + return false; + } +} + + +/** Clear, and merge \a a and \a b into this buffer. + * + * FIXME: This is slow. + * + * \return true if complete merge was successful + */ +bool +EventBuffer::merge(const EventBuffer& a, const EventBuffer& b) +{ + // Die if a merge isn't necessary as it's expensive + assert(a.size() > 0 && b.size() > 0); + + reset(_this_nframes); + + a.rewind(); + b.rewind(); + +#if 0 + uint32_t a_frames; + uint32_t a_subframes; + uint16_t a_type; + uint16_t a_size; + uint8_t* a_data; + + uint32_t b_frames; + uint32_t b_subframes; + uint16_t b_type; + uint16_t b_size; + uint8_t* b_data; +#endif + + cout << "FIXME: merge" << endl; +#if 0 + a.get_event(&a_frames, &a_subframes, &a_type, &a_size, &a_data); + b.get_event(&b_frames, &b_subframes, &b_type, &b_size, &b_data); + + while (true) { + if (a_data && (!b_data || (a_time < b_time))) { + append(a_time, a_size, a_data); + if (a.increment()) + a.get_event(&a_time, &a_size, &a_data); + else + a_data = NULL; + } else if (b_data) { + append(b_time, b_size, b_data); + if (b.increment()) + b.get_event(&b_time, &b_size, &b_data); + else + b_data = NULL; + } else { + break; + } + } + + _latest_stamp = max(a_time, b_time); +#endif + + return true; +} + + +} // namespace Ingen + diff --git a/src/engine/EventBuffer.hpp b/src/engine/EventBuffer.hpp new file mode 100644 index 00000000..0c80d452 --- /dev/null +++ b/src/engine/EventBuffer.hpp @@ -0,0 +1,104 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef EVENTBUFFER_H +#define EVENTBUFFER_H + +#include +#include +#include +#include "Buffer.hpp" +#include "interface/DataType.hpp" + +namespace Ingen { + + +class EventBuffer : public Buffer { +public: + EventBuffer(size_t capacity); + + ~EventBuffer(); + + void prepare_read(FrameTime start, SampleCount nframes); + void prepare_write(FrameTime start, SampleCount nframes); + + bool join(Buffer* buf); + void unjoin(); + + inline uint32_t this_nframes() const { return _this_nframes; } + inline uint32_t event_count() const { return _buf->event_count; } + + inline void* raw_data() { return _buf; } + inline const void* raw_data() const { return _buf; } + + inline LV2_Event_Buffer* local_data() { return _local_buf; } + inline const LV2_Event_Buffer* local_data() const { return _local_buf; } + + inline LV2_Event_Buffer* data() { return _buf; } + inline const LV2_Event_Buffer* data() const { return _buf; } + + void copy(const Buffer* src, size_t start_sample, size_t end_sample); + + inline void rewind() const { lv2_event_begin(&_iter, _buf); } + inline void clear() { reset(_this_nframes); } + inline void reset(SampleCount nframes) { + //std::cerr << this << " reset" << std::endl; + _this_nframes = nframes; + _latest_frames = 0; + _latest_subframes = 0; + _buf->event_count = 0; + _buf->size = 0; + rewind(); + } + + bool increment() const; + bool is_valid() const; + + uint32_t latest_frames() const { return _latest_frames; } + uint32_t latest_subframes() const { return _latest_subframes; } + + bool get_event(uint32_t* frames, + uint32_t* subframes, + uint16_t* type, + uint16_t* size, + uint8_t** data) const; + + bool append(uint32_t frames, + uint32_t subframes, + uint16_t type, + uint16_t size, + const uint8_t* data); + + bool append(const LV2_Event_Buffer* buf); + + bool merge(const EventBuffer& a, const EventBuffer& b); + +private: + LV2_Event_Buffer* _buf; ///< Contents (maybe belong to _joined_buf) + LV2_Event_Buffer* _local_buf; ///< Local contents + + mutable LV2_Event_Iterator _iter; ///< Iterator into _buf + + uint32_t _latest_frames; ///< Latest time of all events (frames) + uint32_t _latest_subframes; ///< Latest time of all events (subframes) + uint32_t _this_nframes; ///< Current cycle nframes +}; + + +} // namespace Ingen + +#endif // EVENTBUFFER_H diff --git a/src/engine/EventSink.cpp b/src/engine/EventSink.cpp new file mode 100644 index 00000000..6c775d4c --- /dev/null +++ b/src/engine/EventSink.cpp @@ -0,0 +1,74 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "events/SendPortValueEvent.hpp" +#include "EventSink.hpp" +#include "PortImpl.hpp" + +using namespace std; + +namespace Ingen { + +#if 0 +void +EventSink::control_change(Port* port, FrameTime time, float val) +{ + //cerr << "CONTROL CHANGE: " << port->path() << " == " << val << endl; + SendPortValueEvent ev(_engine, time, port, false, 0, val); + _events.write(sizeof(ev), (uchar*)&ev); +} +#endif + +/** \a size is not size_t because an event will never be even remotely close + * to UINT32_MAX in size, so uint32_t saves wasted space on 64-bit. + */ +bool +EventSink::write(uint32_t size, const Event* ev) +{ + if (size > _events.write_space()) + return false; + + _events.write(sizeof(uint32_t), (uint8_t*)&size); + _events.write(size, (uint8_t*)ev); + + return true; +} + + +/** Read the next event into event_buffer. + * + * \a event_buffer can be casted to Event* and virtual methods called. + */ +bool +EventSink::read(uint32_t event_buffer_size, uint8_t* event_buffer) +{ + uint32_t read_size; + bool success = _events.full_read(sizeof(uint32_t), (uint8_t*)&read_size); + if (!success) + return false; + + assert(read_size <= event_buffer_size); + + if (read_size > 0) + return _events.full_read(read_size, event_buffer); + else + return false; +} + + +} // namespace Ingen diff --git a/src/engine/EventSink.hpp b/src/engine/EventSink.hpp new file mode 100644 index 00000000..9e937d1a --- /dev/null +++ b/src/engine/EventSink.hpp @@ -0,0 +1,64 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef EVENTSINK_H +#define EVENTSINK_H + +#include +#include +#include +#include "events/SendPortValueEvent.hpp" +#include "types.hpp" + +namespace Ingen { + +class PortImpl; +class Engine; +class SendPortValueEvent; + + +/** Sink for events generated in the audio thread. + * + * Implemented as a flat ringbuffer of events, which are constructed directly + * in the ringbuffer rather than allocated on the heap (in order to make + * writing realtime safe). + * + * \ingroup engine + */ +class EventSink +{ +public: + EventSink(Engine& engine, size_t capacity) : _engine(engine), _events(capacity) {} + + /* FIXME: Figure out variable sized event queues and make this a generic + * interface (ie don't add a method for every event type, crap..) */ + + bool write(uint32_t size, const Event* ev); + + bool read(uint32_t event_buffer_size, uint8_t* event_buffer); + +private: + Engine& _engine; + Raul::RingBuffer _events; +}; + + + +} // namespace Ingen + +#endif // EVENTSINK_H + diff --git a/src/engine/EventSource.hpp b/src/engine/EventSource.hpp new file mode 100644 index 00000000..68532d16 --- /dev/null +++ b/src/engine/EventSource.hpp @@ -0,0 +1,60 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef EVENTSOURCE_H +#define EVENTSOURCE_H + +#include "types.hpp" + +namespace Ingen { + +class Event; +class QueuedEvent; +class PostProcessor; + + +/** 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). + * + * There are two distinct classes of events - "queued" and "stamped". Queued + * events are events that require non-realtime pre-processing before being + * executed in the process thread. Stamped events are timestamped realtime + * events that require no pre-processing and can be executed immediately + * (with sample accuracy). + */ +class EventSource +{ +public: + virtual ~EventSource() {} + + virtual void activate() = 0; + virtual void deactivate() = 0; + + virtual void process(PostProcessor& dest, ProcessContext& context) = 0; + +protected: + size_t _capacity; +}; + + +} // namespace Ingen + +#endif // EVENTSOURCE_H + diff --git a/src/engine/GraphObjectImpl.cpp b/src/engine/GraphObjectImpl.cpp new file mode 100644 index 00000000..0e1abc57 --- /dev/null +++ b/src/engine/GraphObjectImpl.cpp @@ -0,0 +1,39 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "GraphObjectImpl.hpp" +#include "PatchImpl.hpp" +#include "EngineStore.hpp" + +namespace Ingen { + + +PatchImpl* +GraphObjectImpl::parent_patch() const +{ + return dynamic_cast((NodeImpl*)_parent); +} + + +SharedPtr +GraphObjectImpl::find_child(const string& name) const +{ + throw; +} + + +} // namespace Ingen diff --git a/src/engine/GraphObjectImpl.hpp b/src/engine/GraphObjectImpl.hpp new file mode 100644 index 00000000..9b1f675d --- /dev/null +++ b/src/engine/GraphObjectImpl.hpp @@ -0,0 +1,132 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef GRAPHOBJECTIMPL_H +#define GRAPHOBJECTIMPL_H + +#include +#include +#include +#include +#include +#include +#include +#include "interface/GraphObject.hpp" +#include "types.hpp" + +using Raul::Atom; +using Raul::Path; +using Raul::Symbol; + +namespace Raul { class Maid; } + +namespace Ingen { + +class PatchImpl; +class ProcessContext; + + +/** An object on the audio graph - Patch, Node, Port, etc. + * + * Each of these is a Raul::Deletable and so can be deleted in a realtime safe + * way from anywhere, and they all have a map of variable for clients to store + * arbitrary values in (which the engine puts no significance to whatsoever). + * + * \ingroup engine + */ +class GraphObjectImpl : virtual public Ingen::Shared::GraphObject +{ +public: + virtual ~GraphObjectImpl() {} + + bool polyphonic() const { return _polyphonic; } + virtual bool set_polyphonic(Raul::Maid& maid, bool p) { _polyphonic = p; return true; } + + GraphObject* graph_parent() const { return _parent; } + + inline GraphObjectImpl* parent() const { return _parent; } + const Symbol symbol() const { return _name; } + + virtual void process(ProcessContext& context) = 0; + + /** Rename */ + virtual void set_path(const Path& new_path) { + assert(new_path.parent() == path().parent()); + _name = new_path.name(); + assert(_name.find("/") == std::string::npos); + } + + void set_variable(const std::string& key, const Atom& value) + { _variables[key] = value; } + + void set_property(const std::string& key, const Atom& value) + { _properties[key] = value; } + + const Atom& get_variable(const std::string& key) { + static Atom null_atom; + Variables::iterator i = _variables.find(key); + return (i != _variables.end()) ? (*i).second : null_atom; + } + + const Atom& get_property(const std::string& key) { + static Atom null_atom; + Properties::iterator i = _properties.find(key); + return (i != _properties.end()) ? (*i).second : null_atom; + } + + const Variables& variables() const { return _variables; } + const Properties& properties() const { return _properties; } + Variables& variables() { return _variables; } + Properties& properties() { return _properties; } + + /** The Patch this object is a child of. */ + virtual PatchImpl* parent_patch() const; + + /** Path is dynamically generated from parent to ease renaming */ + const Path path() const { + if (_parent == NULL) + return Path(std::string("/").append(_name)); + else if (_parent->path() == "/") + return Path(std::string("/").append(_name)); + else + return Path(_parent->path() +"/"+ _name); + } + + SharedPtr find_child(const std::string& name) const; + +protected: + GraphObjectImpl(GraphObjectImpl* parent, const std::string& name, bool polyphonic=false) + : _parent(parent), _name(name), _polyphonic(polyphonic) + { + assert(parent == NULL || _name.length() > 0); + assert(_name.find("/") == std::string::npos); + assert(path().find("//") == std::string::npos); + } + + GraphObjectImpl* _parent; + std::string _name; + bool _polyphonic; + +private: + Variables _variables; + Properties _properties; +}; + + +} // namespace Ingen + +#endif // GRAPHOBJECTIMPL_H diff --git a/src/engine/HTTPEngineReceiver.cpp b/src/engine/HTTPEngineReceiver.cpp new file mode 100644 index 00000000..1b21e184 --- /dev/null +++ b/src/engine/HTTPEngineReceiver.cpp @@ -0,0 +1,207 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include "types.hpp" +#include +#include +#include "interface/ClientInterface.hpp" +#include "module/Module.hpp" +#include "serialisation/serialisation.hpp" +#include "serialisation/Serialiser.hpp" +#include "serialisation/Parser.hpp" +#include "engine/ThreadManager.hpp" +#include "HTTPEngineReceiver.hpp" +#include "QueuedEventSource.hpp" +#include "ClientBroadcaster.hpp" +#include "EngineStore.hpp" + +using namespace std; +using namespace Ingen::Shared; + +namespace Ingen { + + +HTTPEngineReceiver::HTTPEngineReceiver(Engine& engine, uint16_t port) + : QueuedEngineInterface(engine, 2, 2) + , _server(soup_server_new(SOUP_SERVER_PORT, port, NULL)) +{ + _receive_thread = new ReceiveThread(*this); + + soup_server_add_handler(_server, NULL, message_callback, this, NULL); + + cout << "Started HTTP server on port " << soup_server_get_port(_server) << endl; + Thread::set_name("HTTP receiver"); + + if (!engine.world()->serialisation_module) + engine.world()->serialisation_module = Ingen::Shared::load_module("ingen_serialisation"); + + if (engine.world()->serialisation_module) { + if (!engine.world()->serialiser) + engine.world()->serialiser = SharedPtr( + Ingen::Serialisation::new_serialiser(engine.world(), engine.engine_store())); + + if (!engine.world()->parser) + engine.world()->parser = SharedPtr( + Ingen::Serialisation::new_parser()); + } else { + cerr << "WARNING: Failed to load ingen_serialisation module, HTTP disabled." << endl; + } +} + + +HTTPEngineReceiver::~HTTPEngineReceiver() +{ + deactivate(); + + if (_server != NULL) { + soup_server_quit(_server); + _server = NULL; + } +} + + +void +HTTPEngineReceiver::activate() +{ + QueuedEventSource::activate(); + _receive_thread->set_name("HTTP Receiver"); + _receive_thread->start(); +} + + +void +HTTPEngineReceiver::deactivate() +{ + cout << "[HTTPEngineReceiver] Stopped HTTP listening thread" << endl; + _receive_thread->stop(); + QueuedEventSource::deactivate(); +} + + +void +HTTPEngineReceiver::message_callback(SoupServer* server, SoupMessage* msg, const char* path, + GHashTable *query, SoupClientContext* client, void* data) +{ + HTTPEngineReceiver* me = (HTTPEngineReceiver*)data; + + SharedPtr store = me->_engine.world()->store; + if (!store) { + soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); + return; + } + + if (!Path::is_valid(path)) { + soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST); + const string& err = (boost::format("Bad path: %1%") % path).str(); + soup_message_set_response (msg, "text/plain", SOUP_MEMORY_COPY, + err.c_str(), err.length()); + return; + } + + if (msg->method == SOUP_METHOD_GET) { + Glib::RWLock::ReaderLock lock(store->lock()); + + // Find object + Store::const_iterator start = store->find(path); + if (start == store->end()) { + soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND); + const string& err = (boost::format("No such object: %1%") % path).str(); + soup_message_set_response (msg, "text/plain", SOUP_MEMORY_COPY, + err.c_str(), err.length()); + return; + } + + // Get serialiser + SharedPtr serialiser = me->_engine.world()->serialiser; + if (!serialiser) { + soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); + soup_message_set_response (msg, "text/plain", SOUP_MEMORY_STATIC, + "No serialiser available\n", 24); + return; + } + +#if 0 + SoupMessageHeaders* in_head = msg->request_headers; + const char* str = soup_message_headers_get(in_head, "Accept"); + cout << "Accept: " << str << endl; +#endif + + // Serialise object + const string response = serialiser->to_string(start->second, + "http://example.org", GraphObject::Variables()); + +#if 0 + FILE* xhtml_file = fopen("/home/dave/ingen_ui.xhtml", "r"); + string response; + while (!feof(xhtml_file)) { + int c = fgetc(xhtml_file); + if (c != EOF) + response += (char)c; + } + fclose(xhtml_file); +#endif + + soup_message_set_status (msg, SOUP_STATUS_OK); + soup_message_set_response (msg, "text/plain", SOUP_MEMORY_COPY, + response.c_str(), response.length()); + + } else if (msg->method == SOUP_METHOD_PUT) { + Glib::RWLock::WriterLock lock(store->lock()); + + // Be sure object doesn't exist + Store::const_iterator start = store->find(path); + if (start != store->end()) { + soup_message_set_status (msg, SOUP_STATUS_CONFLICT); + return; + } + + // Get parser + SharedPtr parser = me->_engine.world()->parser; + if (!parser) { + soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); + return; + } + + //cout << "POST: " << msg->request_body->data << endl; + + // Load object + soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED); + } else if (msg->method == SOUP_METHOD_POST) { + //cout << "PUT: " << msg->request_body->data << endl; + soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED); + } else { + soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED); + } +} + + +/** Override the semaphore driven _run method of QueuedEngineInterface + * to wait on HTTP requests and process them immediately in this thread. + */ +void +HTTPEngineReceiver::ReceiveThread::_run() +{ + soup_server_run(_receiver._server); +} + + +} // namespace Ingen diff --git a/src/engine/HTTPEngineReceiver.hpp b/src/engine/HTTPEngineReceiver.hpp new file mode 100644 index 00000000..34c425b2 --- /dev/null +++ b/src/engine/HTTPEngineReceiver.hpp @@ -0,0 +1,59 @@ +/* This file is part of Ingen. + * Copyright (C) 2008 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef HTTPENGINERECEIVER_H +#define HTTPENGINERECEIVER_H + +#include CONFIG_H_PATH +#include +#include +#include +#include +#include "QueuedEngineInterface.hpp" + +namespace Ingen { + +class HTTPEngineReceiver : public QueuedEngineInterface +{ +public: + HTTPEngineReceiver(Engine& engine, uint16_t port); + ~HTTPEngineReceiver(); + + void activate(); + void deactivate(); + +private: + struct ReceiveThread : public Raul::Thread { + ReceiveThread(HTTPEngineReceiver& receiver) : _receiver(receiver) {} + virtual void _run(); + private: + HTTPEngineReceiver& _receiver; + }; + + friend class ReceiveThread; + + static void message_callback(SoupServer* server, SoupMessage* msg, const char* path, + GHashTable *query, SoupClientContext* client, void* data); + + ReceiveThread* _receive_thread; + SoupServer* _server; +}; + + +} // namespace Ingen + +#endif // HTTPENGINERECEIVER_H diff --git a/src/engine/InputPort.cpp b/src/engine/InputPort.cpp new file mode 100644 index 00000000..cf5501ca --- /dev/null +++ b/src/engine/InputPort.cpp @@ -0,0 +1,297 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "InputPort.hpp" +#include +#include +#include +#include "AudioBuffer.hpp" +#include "EventBuffer.hpp" +#include "ConnectionImpl.hpp" +#include "OutputPort.hpp" +#include "NodeImpl.hpp" +#include "ProcessContext.hpp" +#include "util.hpp" + +using namespace std; + +namespace Ingen { + + +InputPort::InputPort(NodeImpl* parent, + const string& name, + uint32_t index, + uint32_t poly, + DataType type, + const Atom& value, + size_t buffer_size) + : PortImpl(parent, name, index, poly, type, value, buffer_size) +{ +} + + +void +InputPort::set_buffer_size(size_t size) +{ + PortImpl::set_buffer_size(size); + assert(_buffer_size = size); + + for (Connections::iterator c = _connections.begin(); c != _connections.end(); ++c) + ((ConnectionImpl*)c->get())->set_buffer_size(size); + +} + + +bool +InputPort::prepare_poly(uint32_t poly) +{ + PortImpl::prepare_poly(poly); + + for (Connections::iterator c = _connections.begin(); c != _connections.end(); ++c) + ((ConnectionImpl*)c->get())->prepare_poly(poly); + + connect_buffers(); + return true; +} + + +bool +InputPort::apply_poly(Raul::Maid& maid, uint32_t poly) +{ + if (!_polyphonic || !_parent->polyphonic()) + return true; + + for (Connections::iterator c = _connections.begin(); c != _connections.end(); ++c) + ((ConnectionImpl*)c->get())->apply_poly(maid, poly); + + PortImpl::apply_poly(maid, poly); + assert(this->poly() == poly); + + if (_connections.size() == 1) { + ConnectionImpl* c = _connections.begin()->get(); + for (uint32_t i=0; i < _poly; ++i) + _buffers->at(i)->join(c->buffer(i)); + } + + connect_buffers(); + + return true; +} + + +/** 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. + */ +void +InputPort::add_connection(Connections::Node* const c) +{ + _connections.push_back(c); + + bool modify_buffers = !_fixed_buffers; + + if (modify_buffers) { + if (_connections.size() == 1) { + // Use buffer directly to avoid copying + for (uint32_t i=0; i < _poly; ++i) { + _buffers->at(i)->join(c->elem()->buffer(i)); + } + } else if (_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 (uint32_t i=0; i < _poly; ++i) { + _buffers->at(i)->unjoin(); + } + } + PortImpl::connect_buffers(); + } + + // Automatically broadcast connected control inputs + if (_type == DataType::CONTROL) + _broadcast = true; +} + + +/** Remove a connection. Realtime safe. + */ +InputPort::Connections::Node* +InputPort::remove_connection(const OutputPort* src_port) +{ + bool modify_buffers = !_fixed_buffers; + + bool found = false; + Connections::Node* connection = NULL; + for (Connections::iterator i = _connections.begin(); i != _connections.end(); ++i) { + if ((*i)->src_port()->path() == src_port->path()) { + connection = _connections.erase(i); + found = true; + } + } + + if ( ! found) { + cerr << "WARNING: [InputPort::remove_connection] Connection not found !" << endl; + exit(EXIT_FAILURE); + } else { + if (_connections.size() == 0) { + for (uint32_t i=0; i < _poly; ++i) { + // Use a local buffer + if (modify_buffers) + _buffers->at(i)->unjoin(); + _buffers->at(i)->clear(); // Write silence + } + } else if (modify_buffers && _connections.size() == 1) { + // Share a buffer + for (uint32_t i=0; i < _poly; ++i) { + _buffers->at(i)->join((*_connections.begin())->buffer(i)); + } + } + } + + if (modify_buffers) + PortImpl::connect_buffers(); + + // Turn off broadcasting if we're not connected any more (FIXME: not quite right..) + if (_type == DataType::CONTROL && _connections.size() == 0) + _broadcast = false; + + return connection; +} + + +/** Returns whether this port is connected to the passed port. + */ +/*bool +InputPort::is_connected_to(const OutputPort* port) const +{ + for (Connections::const_iterator i = _connections.begin(); i != _connections.end(); ++i) + if ((*i)->src_port() == port) + return true; + + return false; +}*/ + + +/** Prepare buffer for access, mixing if necessary. Realtime safe. + * FIXME: nframes parameter not used, + */ +void +InputPort::pre_process(ProcessContext& context) +{ + // If value has been set (e.g. events pushed) by the user, + // don't do anything this cycle to avoid smashing the value + if (_set_by_user) + return; + + bool do_mixdown = true; + + if (_connections.size() == 0) { + for (uint32_t i=0; i < _poly; ++i) + buffer(i)->prepare_read(context.start(), context.nframes()); + return; + } + + for (Connections::iterator c = _connections.begin(); c != _connections.end(); ++c) + (*c)->process(context); + + if ( ! _fixed_buffers) { + // If only one connection, try to use buffer directly (zero copy) + if (_connections.size() == 1) { + for (uint32_t i=0; i < _poly; ++i) { + //cerr << path() << " joining to " << (*_connections.begin())->buffer(i) << endl; + _buffers->at(i)->join((*_connections.begin())->buffer(i)); + } + do_mixdown = false; + } + connect_buffers(); + } else { + do_mixdown = true; + } + + for (uint32_t i=0; i < _poly; ++i) + buffer(i)->prepare_read(context.start(), context.nframes()); + + /*cerr << path() << " poly = " << _poly << ", mixdown: " << do_mixdown + << ", fixed buffers: " << _fixed_buffers << ", joined: " << _buffers->at(0)->is_joined() + << " to " << _buffers->at(0)->joined_buffer() << endl;*/ + + /*if (type() == DataType::EVENT) + for (uint32_t i=0; i < _poly; ++i) + cerr << path() << " (" << buffer(i) << ") # events: " + << ((EventBuffer*)buffer(i))->event_count() + << ", joined: " << _buffers->at(i)->is_joined() << endl;*/ + + if (!do_mixdown) { + /*#ifndef NDEBUG + for (uint32_t i=0; i < _poly; ++i) + assert(buffer(i) == (*_connections.begin())->buffer(i)); + #endif*/ + return; + } + + if (_type == DataType::CONTROL || _type == DataType::AUDIO) { + for (uint32_t voice=0; voice < _poly; ++voice) { + // Copy first connection + buffer(voice)->copy( + (*_connections.begin())->buffer(voice), 0, _buffer_size-1); + + // Accumulate the rest + if (_connections.size() > 1) { + + Connections::iterator c = _connections.begin(); + + for (++c; c != _connections.end(); ++c) + ((AudioBuffer*)buffer(voice))->accumulate( + ((AudioBuffer*)(*c)->buffer(voice)), 0, _buffer_size-1); + } + } + } else { + assert(_poly == 1); + + // FIXME + if (_connections.size() > 1) + cerr << "WARNING: MIDI mixing not implemented, only first connection used." << endl; + + // Copy first connection + _buffers->at(0)->copy( + (*_connections.begin())->buffer(0), 0, _buffer_size-1); + } +} + + +void +InputPort::post_process(ProcessContext& context) +{ + broadcast(context); + + // Prepare for next cycle + for (uint32_t i=0; i < _poly; ++i) + buffer(i)->prepare_write(context.start(), context.nframes()); + + _set_by_user = false; + + /*if (_broadcast && (_type == DataType::CONTROL)) { + const Sample value = ((AudioBuffer*)(*_buffers)[0])->value_at(0); + + cerr << path() << " input post: buffer: " << buffer(0) << " value = " + << value << " (last " << _last_broadcasted_value << ")" < + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef INPUTPORT_H +#define INPUTPORT_H + +#include +#include +#include +#include +#include +#include "PortImpl.hpp" +using std::string; + +namespace Ingen { + +class ConnectionImpl; +class OutputPort; +class NodeImpl; + + +/** 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 + */ +class InputPort : virtual public PortImpl +{ +public: + InputPort(NodeImpl* parent, + const string& name, + uint32_t index, + uint32_t poly, + DataType type, + const Atom& value, + size_t buffer_size); + + virtual ~InputPort() {} + + typedef Raul::List< SharedPtr > Connections; + + void add_connection(Connections::Node* c); + Connections::Node* remove_connection(const OutputPort* src_port); + + const Connections& connections() { return _connections; } + + bool prepare_poly(uint32_t poly); + bool apply_poly(Raul::Maid& maid, uint32_t poly); + + void pre_process(ProcessContext& context); + void post_process(ProcessContext& context); + + bool is_connected() const { return (_connections.size() > 0); } + //bool is_connected_to(const OutputPort* port) const; + + bool is_input() const { return true; } + bool is_output() const { return false; } + + virtual void set_buffer_size(size_t size); + +private: + Connections _connections; +}; + + +} // namespace Ingen + +#endif // INPUTPORT_H diff --git a/src/engine/InternalPlugin.cpp b/src/engine/InternalPlugin.cpp new file mode 100644 index 00000000..1c6a92a5 --- /dev/null +++ b/src/engine/InternalPlugin.cpp @@ -0,0 +1,55 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "InternalPlugin.hpp" +#include "MidiNoteNode.hpp" +#include "MidiTriggerNode.hpp" +#include "MidiControlNode.hpp" +#include "TransportNode.hpp" +#include "Engine.hpp" +#include "AudioDriver.hpp" + +namespace Ingen { + + +NodeImpl* +InternalPlugin::instantiate(const string& name, + bool polyphonic, + Ingen::PatchImpl* parent, + Engine& engine) +{ + assert(_type == Internal); + + SampleCount srate = engine.audio_driver()->sample_rate(); + SampleCount buffer_size = engine.audio_driver()->buffer_size(); + + if (_uri == NS_INGEN "note_node") { + return new MidiNoteNode(name, polyphonic, parent, srate, buffer_size); + } else if (_uri == NS_INGEN "trigger_node") { + return new MidiTriggerNode(name, polyphonic, parent, srate, buffer_size); + } else if (_uri == NS_INGEN "control_node") { + return new MidiControlNode(name, polyphonic, parent, srate, buffer_size); + } else if (_uri == NS_INGEN "transport_node") { + return new TransportNode(name, polyphonic, parent, srate, buffer_size); + } else { + return NULL; + } +} + + +} // namespace Ingen diff --git a/src/engine/InternalPlugin.hpp b/src/engine/InternalPlugin.hpp new file mode 100644 index 00000000..c04c9015 --- /dev/null +++ b/src/engine/InternalPlugin.hpp @@ -0,0 +1,74 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef INTERNALPLUGIN_H +#define INTERNALPLUGIN_H + +#include CONFIG_H_PATH + +#ifndef HAVE_SLV2 +#error "This file requires SLV2, but HAVE_SLV2 is not defined. Please report." +#endif + +#include +#include +#include +#include +#include +#include +#include +#include "types.hpp" +#include "PluginImpl.hpp" + +#define NS_INGEN "http://drobilla.net/ns/ingen#" + +namespace Ingen { + +class NodeImpl; + + +/** Implementation of an Internal plugin. + */ +class InternalPlugin : public PluginImpl +{ +public: + InternalPlugin(const std::string& uri, + const std::string& symbol, + const std::string& name) + : PluginImpl(Plugin::Internal, uri) + , _symbol(symbol) + , _name(name) + {} + + NodeImpl* instantiate(const std::string& name, + bool polyphonic, + Ingen::PatchImpl* parent, + Engine& engine); + + const string symbol() const { return _symbol; } + const string name() const { return _name; } + +private: + const string _symbol; + const string _name; +}; + + +} // namespace Ingen + +#endif // INTERNALPLUGIN_H + diff --git a/src/engine/JackAudioDriver.cpp b/src/engine/JackAudioDriver.cpp new file mode 100644 index 00000000..eff04653 --- /dev/null +++ b/src/engine/JackAudioDriver.cpp @@ -0,0 +1,384 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "JackAudioDriver.hpp" +#include CONFIG_H_PATH +#include "tuning.hpp" +#include +#include +#include +#include "Engine.hpp" +#include "util.hpp" +#include "Event.hpp" +#include "ThreadManager.hpp" +#include "QueuedEvent.hpp" +#include "EventSource.hpp" +#include "PostProcessor.hpp" +#include "NodeImpl.hpp" +#include "PatchImpl.hpp" +#include "PortImpl.hpp" +#include "MidiDriver.hpp" +#include "DuplexPort.hpp" +#include "EventSource.hpp" +#include "AudioBuffer.hpp" +#include "ProcessSlave.hpp" + +using namespace std; + +namespace Ingen { + + +//// JackAudioPort //// + +JackAudioPort::JackAudioPort(JackAudioDriver* driver, DuplexPort* patch_port) + : DriverPort(patch_port) + , Raul::List::Node(this) + , _driver(driver) + , _jack_port(NULL) + , _jack_buffer(NULL) +{ + assert(patch_port->poly() == 1); + + _jack_port = jack_port_register(_driver->jack_client(), + patch_port->path().c_str(), JACK_DEFAULT_AUDIO_TYPE, + (patch_port->is_input()) ? JackPortIsInput : JackPortIsOutput, + 0); + + if (_jack_port == NULL) { + cerr << "[JackAudioPort] ERROR: Failed to register port " << patch_port->path() << endl; + throw JackAudioDriver::PortRegistrationFailedException(); + } + + patch_port->buffer(0)->clear(); + patch_port->fixed_buffers(true); +} + + +JackAudioPort::~JackAudioPort() +{ + jack_port_unregister(_driver->jack_client(), _jack_port); +} + + +void +JackAudioPort::prepare_buffer(jack_nframes_t nframes) +{ + jack_sample_t* jack_buf = (jack_sample_t*)jack_port_get_buffer(_jack_port, nframes); + + AudioBuffer* patch_buf = (AudioBuffer*)_patch_port->buffer(0); + + //cerr << "[JACK] " << _patch_port->path() << " buffer: " << patch_buf << endl; + + if (jack_buf != _jack_buffer) { + patch_buf->set_data(jack_buf); + _jack_buffer = jack_buf; + } + + assert(patch_buf->data() == jack_buf); +} + + +//// JackAudioDriver //// + +JackAudioDriver::JackAudioDriver(Engine& engine, + std::string server_name, + jack_client_t* jack_client) + : _engine(engine) + , _jack_thread(NULL) + , _client(jack_client) + , _buffer_size(jack_client ? jack_get_buffer_size(jack_client) : 0) + , _sample_rate(jack_client ? jack_get_sample_rate(jack_client) : 0) + , _is_activated(false) + , _local_client(true) // FIXME + , _process_context(engine) + , _root_patch(NULL) +{ + if (!_client) { + // Try supplied server name + if (server_name != "") { + _client = jack_client_open("Ingen", JackServerName, NULL, server_name.c_str()); + if (_client) + cerr << "[JackAudioDriver] Connected to JACK server '" << + server_name << "'" << endl; + } + + // Either server name not specified, or supplied server name does not exist + // Connect to default server + if (!_client) { + _client = jack_client_open("Ingen", JackNullOption, NULL); + + if (_client) + cerr << "[JackAudioDriver] Connected to default JACK server." << endl; + } + + // Still failed + if (!_client) { + cerr << "[JackAudioDriver] Unable to connect to Jack. Exiting." << endl; + exit(EXIT_FAILURE); + } + + _buffer_size = jack_get_buffer_size(_client); + _sample_rate = jack_get_sample_rate(_client); + } + + jack_on_shutdown(_client, shutdown_cb, this); + + jack_set_thread_init_callback(_client, thread_init_cb, this); + jack_set_sample_rate_callback(_client, sample_rate_cb, this); + jack_set_buffer_size_callback(_client, buffer_size_cb, this); +} + + +JackAudioDriver::~JackAudioDriver() +{ + deactivate(); + + if (_local_client) + jack_client_close(_client); +} + + +void +JackAudioDriver::activate() +{ + if (_is_activated) { + cerr << "[JackAudioDriver] Jack driver already activated." << endl; + return; + } + + jack_set_process_callback(_client, process_cb, this); + + _is_activated = true; + + if (jack_activate(_client)) { + cerr << "[JackAudioDriver] Could not activate Jack client, aborting." << endl; + exit(EXIT_FAILURE); + } else { + cout << "[JackAudioDriver] Activated Jack client." << endl; +/*#ifdef HAVE_LASH + _engine.lash_driver()->set_jack_client_name(jack_client_get_name(_client)); +#endif*/ + } +} + + +void +JackAudioDriver::deactivate() +{ + if (_is_activated) { + + //for (Raul::List::iterator i = _ports.begin(); i != _ports.end(); ++i) + // jack_port_unregister(_client, (*i)->jack_port()); + + jack_deactivate(_client); + _is_activated = false; + + _ports.clear(); + + cout << "[JackAudioDriver] Deactivated Jack client." << endl; + + //_engine.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(DriverPort* port) +{ + assert(ThreadManager::current_thread_id() == THREAD_PROCESS); + + assert(dynamic_cast(port)); + _ports.push_back((JackAudioPort*)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. + */ +DriverPort* +JackAudioDriver::remove_port(const Path& path) +{ + assert(ThreadManager::current_thread_id() == THREAD_PROCESS); + + for (Raul::List::iterator i = _ports.begin(); i != _ports.end(); ++i) + if ((*i)->patch_port()->path() == path) + return _ports.erase(i)->elem(); // FIXME: LEAK + + cerr << "[JackAudioDriver::remove_port] WARNING: Unable to find Jack port " << path << endl; + return NULL; +} + + +DriverPort* +JackAudioDriver::port(const Path& path) +{ + for (Raul::List::iterator i = _ports.begin(); i != _ports.end(); ++i) + if ((*i)->patch_port()->path() == path) + return (*i); + + return NULL; +} + + +DriverPort* +JackAudioDriver::create_port(DuplexPort* patch_port) +{ + try { + if (patch_port->buffer_size() == _buffer_size) + return new JackAudioPort(this, patch_port); + else + return NULL; + } catch (...) { + return NULL; + } +} + + +DriverPort* +JackAudioDriver::driver_port(const Path& path) +{ + assert(ThreadManager::current_thread_id() == THREAD_PROCESS); + + for (Raul::List::iterator i = _ports.begin(); i != _ports.end(); ++i) + if ((*i)->patch_port()->path() == path) + return (*i); + + return NULL; +} + + +/**** Jack Callbacks ****/ + + + +/** Jack process callback, drives entire audio thread. + * + * \callgraph + */ +int +JackAudioDriver::_process_cb(jack_nframes_t nframes) +{ + if (nframes == 0 || ! _is_activated) + return 0; + + // FIXME: all of this time stuff is screwy + + // FIXME: support nframes != buffer_size, even though that never damn well happens + assert(nframes == _buffer_size); + + // Jack can elect to not call this function for a cycle, if overloaded + // FIXME: this doesn't make sense, and the start time isn't used anyway + const jack_nframes_t start_of_current_cycle = jack_last_frame_time(_client); + + const jack_nframes_t end_of_current_cycle = start_of_current_cycle + nframes; +#ifndef NDEBUG + // FIXME: support changing cycle length + const jack_nframes_t start_of_last_cycle = start_of_current_cycle - nframes; + assert(start_of_current_cycle - start_of_last_cycle == nframes); +#endif + + _transport_state = jack_transport_query(_client, &_position); + + _process_context.set_time_slice(nframes, start_of_current_cycle, end_of_current_cycle); + + for (Engine::ProcessSlaves::iterator i = _engine.process_slaves().begin(); + i != _engine.process_slaves().end(); ++i) { + (*i)->context().set_time_slice(nframes, start_of_current_cycle, end_of_current_cycle); + } + + // Process events that came in during the last cycle + // (Aiming for jitter-free 1 block event latency, ideally) + _engine.process_events(_process_context); + + // Set buffers of patch ports to Jack port buffers (zero-copy processing) + for (Raul::List::iterator i = _ports.begin(); i != _ports.end(); ++i) { + assert(*i); + (*i)->prepare_buffer(nframes); + } + + assert(_engine.midi_driver()); + _engine.midi_driver()->pre_process(_process_context); + + // Run root patch + if (_root_patch) + _root_patch->process(_process_context); + + _engine.midi_driver()->post_process(_process_context); + + _engine.post_processor()->set_end_time(_process_context.end()); + + return 0; +} + + +void +JackAudioDriver::_thread_init_cb() +{ + // Initialize thread specific data + _jack_thread = Thread::create_for_this_thread("Jack"); + assert(&Thread::get() == _jack_thread); + _jack_thread->set_context(THREAD_PROCESS); + assert(ThreadManager::current_thread_id() == THREAD_PROCESS); +} + +void +JackAudioDriver::_shutdown_cb() +{ + cout << "[JackAudioDriver] Jack shutdown. Exiting." << endl; + _engine.quit(); + _jack_thread = NULL; +} + + +int +JackAudioDriver::_sample_rate_cb(jack_nframes_t nframes) +{ + if (_is_activated) { + cerr << "[JackAudioDriver] On-the-fly sample rate changing not supported (yet). Aborting." << endl; + exit(EXIT_FAILURE); + } else { + _sample_rate = nframes; + } + return 0; +} + + +int +JackAudioDriver::_buffer_size_cb(jack_nframes_t nframes) +{ + if (_root_patch) { + _root_patch->set_buffer_size(nframes); + _buffer_size = nframes; + } + return 0; +} + + +} // namespace Ingen + diff --git a/src/engine/JackAudioDriver.hpp b/src/engine/JackAudioDriver.hpp new file mode 100644 index 00000000..3beb775a --- /dev/null +++ b/src/engine/JackAudioDriver.hpp @@ -0,0 +1,185 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef JACKAUDIODRIVER_H +#define JACKAUDIODRIVER_H + +#include +#include +#include +#include +#include +#include "AudioDriver.hpp" +#include "Buffer.hpp" +#include "ProcessContext.hpp" + +namespace Ingen { + +class Engine; +class PatchImpl; +class PortImpl; +class DuplexPort; +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 Raul::List::Node +{ +public: + JackAudioPort(JackAudioDriver* driver, DuplexPort* patch_port); + ~JackAudioPort(); + + void set_name(const std::string& name) { jack_port_set_name(_jack_port, name.c_str()); }; + + void prepare_buffer(jack_nframes_t nframes); + + jack_port_t* jack_port() const { return _jack_port; } + +private: + JackAudioDriver* _driver; + jack_port_t* _jack_port; + jack_sample_t* _jack_buffer; ///< Cached for output ports +}; + + + +/** 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(Engine& engine, + std::string server_name = "", + jack_client_t* jack_client = 0); + + ~JackAudioDriver(); + + void activate(); + void deactivate(); + void enable(); + void disable(); + + DriverPort* port(const Raul::Path& path); + DriverPort* create_port(DuplexPort* patch_port); + + void add_port(DriverPort* port); + DriverPort* remove_port(const Raul::Path& path); + + DriverPort* driver_port(const Raul::Path& path); + + PatchImpl* root_patch() { return _root_patch; } + void set_root_patch(PatchImpl* patch) { _root_patch = patch; } + + ProcessContext& context() { return _process_context; } + + /** Transport state for this frame. + * Intended to only be called from the audio thread. */ + inline const jack_position_t* position() { return &_position; } + inline jack_transport_state_t transport_state() { return _transport_state; } + + bool is_realtime() const { return jack_is_realtime(_client); } + + jack_client_t* jack_client() const { return _client; } + SampleCount buffer_size() const { return _buffer_size; } + SampleCount sample_rate() const { return _sample_rate; } + bool is_activated() const { return _is_activated; } + + inline SampleCount frame_time() const { return jack_frame_time(_client); } + + class PortRegistrationFailedException : public std::exception {}; + +private: + friend class JackAudioPort; + + // These are the static versions of the callbacks, they call + // the non-static ones below + inline static void thread_init_cb(void* const jack_driver); + inline static void shutdown_cb(void* const jack_driver); + inline static int process_cb(jack_nframes_t nframes, 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 + void _thread_init_cb(); + void _shutdown_cb(); + int _process_cb(jack_nframes_t nframes); + int _buffer_size_cb(jack_nframes_t nframes); + int _sample_rate_cb(jack_nframes_t nframes); + + Engine& _engine; + Raul::Thread* _jack_thread; + jack_client_t* _client; + jack_nframes_t _buffer_size; + jack_nframes_t _sample_rate; + bool _is_activated; + bool _local_client; ///< Whether _client should be closed on destruction + jack_position_t _position; + jack_transport_state_t _transport_state; + + Raul::List _ports; + ProcessContext _process_context; + + PatchImpl* _root_patch; +}; + + +inline int JackAudioDriver::process_cb(jack_nframes_t nframes, void* jack_driver) +{ + assert(jack_driver); + return ((JackAudioDriver*)jack_driver)->_process_cb(nframes); +} + +inline void JackAudioDriver::thread_init_cb(void* jack_driver) +{ + assert(jack_driver); + return ((JackAudioDriver*)jack_driver)->_thread_init_cb(); +} + +inline void JackAudioDriver::shutdown_cb(void* jack_driver) +{ + assert(jack_driver); + return ((JackAudioDriver*)jack_driver)->_shutdown_cb(); +} + + +inline int JackAudioDriver::buffer_size_cb(jack_nframes_t nframes, void* jack_driver) +{ + assert(jack_driver); + return ((JackAudioDriver*)jack_driver)->_buffer_size_cb(nframes); +} + + +inline int JackAudioDriver::sample_rate_cb(jack_nframes_t nframes, void* jack_driver) +{ + assert(jack_driver); + return ((JackAudioDriver*)jack_driver)->_sample_rate_cb(nframes); +} + + +} // namespace Ingen + +#endif // JACKAUDIODRIVER_H diff --git a/src/engine/JackMidiDriver.cpp b/src/engine/JackMidiDriver.cpp new file mode 100644 index 00000000..9e236541 --- /dev/null +++ b/src/engine/JackMidiDriver.cpp @@ -0,0 +1,267 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +() */ + +#include +#include +#include +#include +#include +#include "types.hpp" +#include "JackMidiDriver.hpp" +#include "JackAudioDriver.hpp" +#include "ThreadManager.hpp" +#include "AudioDriver.hpp" +#include "EventBuffer.hpp" +#include "DuplexPort.hpp" +#include "ProcessContext.hpp" +#include "jack_compat.h" +using namespace std; + +namespace Ingen { + + +//// JackMidiPort //// + +JackMidiPort::JackMidiPort(JackMidiDriver* driver, DuplexPort* patch_port) + : DriverPort(patch_port) + , Raul::List::Node(this) + , _driver(driver) + , _jack_port(NULL) +{ + assert(patch_port->poly() == 1); + + _jack_port = jack_port_register(_driver->jack_client(), + patch_port->path().c_str(), JACK_DEFAULT_MIDI_TYPE, + (patch_port->is_input()) ? JackPortIsInput : JackPortIsOutput, + 0); + + if (_jack_port == NULL) { + cerr << "[JackMidiPort] ERROR: Failed to register port " << patch_port->path() << endl; + throw JackAudioDriver::PortRegistrationFailedException(); + } + + patch_port->buffer(0)->clear(); +} + + +JackMidiPort::~JackMidiPort() +{ + jack_port_unregister(_driver->jack_client(), _jack_port); +} + + +/** Prepare input for a block before a cycle is run, in the audio thread. + * + * This is simple since Jack MIDI is in-band with audio. + */ +void +JackMidiPort::pre_process(ProcessContext& context) +{ + if ( ! is_input() ) + return; + + assert(_patch_port->poly() == 1); + + EventBuffer* patch_buf = dynamic_cast(_patch_port->buffer(0)); + assert(patch_buf); + + void* jack_buffer = jack_port_get_buffer(_jack_port, context.nframes()); + const jack_nframes_t event_count = jack_midi_get_event_count(jack_buffer); + + patch_buf->prepare_write(context.start(), context.nframes()); + + // 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_get(&ev, jack_buffer, i); + + // FIXME: type is hardcoded for now, we should get it from + // the type map instead + const bool success = patch_buf->append(ev.time, 0, 1, ev.size, ev.buffer); + if (!success) + cerr << "WARNING: Failed to write MIDI to port buffer, event(s) lost!" << endl; + } + + //if (event_count) + // cerr << "Jack MIDI got " << event_count << " events." << endl; +} + + +/** Prepare output for a block after a cycle is run, in the audio thread. + * + * This is simple since Jack MIDI is in-band with audio. + */ +void +JackMidiPort::post_process(ProcessContext& context) +{ + if (is_input()) + return; + + EventBuffer* patch_buf = dynamic_cast(_patch_port->buffer(0)); + void* jack_buf = jack_port_get_buffer(_jack_port, context.nframes()); + + assert(_patch_port->poly() == 1); + assert(patch_buf); + + patch_buf->prepare_read(context.start(), context.nframes()); + jack_midi_clear_buffer(jack_buf); + + uint32_t frames = 0; + uint32_t subframes = 0; + uint16_t type = 0; + uint16_t size = 0; + uint8_t* data = NULL; + + // Copy events from Jack port buffer into patch port buffer + for (patch_buf->rewind(); patch_buf->is_valid(); patch_buf->increment()) { + patch_buf->get_event(&frames, &subframes, &type, &size, &data); + jack_midi_event_write(jack_buf, frames, data, size); + } + + //if (event_count) + // cerr << "Jack MIDI wrote " << event_count << " events." << endl; +} + + + +//// JackMidiDriver //// + + +bool JackMidiDriver::_midi_thread_exit_flag = true; + + +JackMidiDriver::JackMidiDriver(jack_client_t* client) + : _client(client) + , _is_activated(false) + , _is_enabled(false) +{ +} + + +JackMidiDriver::~JackMidiDriver() +{ + deactivate(); +} + + +/** Launch and start the MIDI thread. + */ +void +JackMidiDriver::activate() +{ + _is_activated = true; +} + + +/** Terminate the MIDI thread. + */ +void +JackMidiDriver::deactivate() +{ + _is_activated = false; +} + + +/** Build flat arrays of events to be used as input for the given cycle. + */ +void +JackMidiDriver::pre_process(ProcessContext& context) +{ + for (Raul::List::iterator i = _in_ports.begin(); i != _in_ports.end(); ++i) + (*i)->pre_process(context); +} + + +/** Write the output from any (top-level, exported) MIDI output ports to Jack ports. + */ +void +JackMidiDriver::post_process(ProcessContext& context) +{ + for (Raul::List::iterator i = _out_ports.begin(); i != _out_ports.end(); ++i) + (*i)->post_process(context); +} + + +/** 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 new_port() and remove_port(). + */ +void +JackMidiDriver::add_port(DriverPort* port) +{ + assert(ThreadManager::current_thread_id() == THREAD_PROCESS); + assert(dynamic_cast(port)); + + if (port->is_input()) + _in_ports.push_back((JackMidiPort*)port); + else + _out_ports.push_back((JackMidiPort*)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. + */ +DriverPort* +JackMidiDriver::remove_port(const Path& path) +{ + assert(ThreadManager::current_thread_id() == THREAD_PROCESS); + + // FIXME: duplex? + + for (Raul::List::iterator i = _in_ports.begin(); i != _in_ports.end(); ++i) + if ((*i)->patch_port()->path() == path) + return _in_ports.erase(i)->elem(); // FIXME: LEAK + + for (Raul::List::iterator i = _out_ports.begin(); i != _out_ports.end(); ++i) + if ((*i)->patch_port()->path() == path) + return _out_ports.erase(i)->elem(); // FIXME: LEAK + + cerr << "[JackMidiDriver::remove_input] WARNING: Unable to find Jack port " << path << endl; + return NULL; +} + + +DriverPort* +JackMidiDriver::driver_port(const Path& path) +{ + assert(ThreadManager::current_thread_id() == THREAD_PROCESS); + + // FIXME: duplex? + + for (Raul::List::iterator i = _in_ports.begin(); i != _in_ports.end(); ++i) + if ((*i)->patch_port()->path() == path) + return (*i); + + for (Raul::List::iterator i = _out_ports.begin(); i != _out_ports.end(); ++i) + if ((*i)->patch_port()->path() == path) + return (*i); + + return NULL; +} + + +} // namespace Ingen + diff --git a/src/engine/JackMidiDriver.hpp b/src/engine/JackMidiDriver.hpp new file mode 100644 index 00000000..7889c4fa --- /dev/null +++ b/src/engine/JackMidiDriver.hpp @@ -0,0 +1,113 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef JACKMIDIDRIVER_H +#define JACKMIDIDRIVER_H + +#include +#include +#include +#include CONFIG_H_PATH +#include "MidiDriver.hpp" + +namespace Ingen { + +class NodeImpl; +class SetPortValueEvent; +class JackMidiDriver; +class DuplexPort; + + +/** Representation of an JACK MIDI port. + * + * \ingroup engine + */ +class JackMidiPort : public DriverPort, public Raul::List::Node +{ +public: + JackMidiPort(JackMidiDriver* driver, DuplexPort* port); + virtual ~JackMidiPort(); + + void pre_process(ProcessContext& context); + void post_process(ProcessContext& context); + + void set_name(const std::string& name) { jack_port_set_name(_jack_port, name.c_str()); }; + +private: + JackMidiDriver* _driver; + jack_port_t* _jack_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() { _is_enabled = true; } + void disable() { _is_enabled = false; } + + bool is_activated() const { return _is_activated; } + bool is_enabled() const { return _is_enabled; } + + void pre_process(ProcessContext& context); + void post_process(ProcessContext& context); + + JackMidiPort* create_port(DuplexPort* patch_port) + { return new JackMidiPort(this, patch_port); } + + void add_port(DriverPort* port); + DriverPort* remove_port(const Raul::Path& path); + + DriverPort* driver_port(const Raul::Path& path); + + jack_client_t* jack_client() { return _client; } + +private: + Raul::List _in_ports; + Raul::List _out_ports; + + friend class JackMidiPort; + + void add_output(Raul::List::Node* port); + Raul::List::Node* remove_output(JackMidiPort* port); + + // MIDI thread + static void* process_midi_in(void* me); + + jack_client_t* _client; + pthread_t _process_thread; + bool _is_activated; + bool _is_enabled; + static bool _midi_thread_exit_flag; +}; + + +} // namespace Ingen + + +#endif // JACKMIDIDRIVER_H diff --git a/src/engine/LADSPANode.cpp b/src/engine/LADSPANode.cpp new file mode 100644 index 00000000..22f5bbf3 --- /dev/null +++ b/src/engine/LADSPANode.cpp @@ -0,0 +1,373 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include "LADSPANode.hpp" +#include "AudioBuffer.hpp" +#include "InputPort.hpp" +#include "OutputPort.hpp" +#include "PluginImpl.hpp" +#include "ProcessContext.hpp" + +using namespace std; + +namespace Ingen { + + +/** Partially construct a LADSPANode. + * + * Object is not usable until instantiate() is called with success. + * (It _will_ crash!) + */ +LADSPANode::LADSPANode(PluginImpl* plugin, const string& path, bool polyphonic, PatchImpl* parent, const LADSPA_Descriptor* descriptor, SampleRate srate, size_t buffer_size) + : NodeBase(plugin, path, polyphonic, parent, srate, buffer_size) + , _descriptor(descriptor) + , _instances(NULL) + , _prepared_instances(NULL) +{ + assert(_descriptor != NULL); +} + + +LADSPANode::~LADSPANode() +{ + for (uint32_t i=0; i < _polyphony; ++i) + _descriptor->cleanup((*_instances)[i]); + + delete _instances; +} + + +bool +LADSPANode::prepare_poly(uint32_t poly) +{ + NodeBase::prepare_poly(poly); + + if ( (!_polyphonic) + || (_prepared_instances && poly <= _prepared_instances->size()) ) { + return true; + } + + _prepared_instances = new Raul::Array(poly, *_instances); + for (uint32_t i = _polyphony; i < _prepared_instances->size(); ++i) { + _prepared_instances->at(i) = _descriptor->instantiate(_descriptor, _srate); + if (_prepared_instances->at(i) == NULL) { + cerr << "Failed to instantiate plugin!" << endl; + return false; + } + + if (_activated && _descriptor->activate) + _descriptor->activate(_prepared_instances->at(i)); + } + + return true; +} + + +bool +LADSPANode::apply_poly(Raul::Maid& maid, uint32_t poly) +{ + if (!_polyphonic) + return true; + + if (_prepared_instances) { + assert(poly <= _prepared_instances->size()); + maid.push(_instances); + _instances = _prepared_instances; + _prepared_instances = NULL; + } + + assert(poly <= _instances->size()); + _polyphony = poly; + + return NodeBase::apply_poly(maid, poly); +} + + +static string +nameify_if_invalid(const string& name) +{ + if (Path::is_valid_name(name)) { + return name; + } else { + const string new_name = Path::nameify(name); + assert(Path::is_valid_name(new_name)); + if (new_name != name) + cerr << "Symbol '" << new_name << "' generated from LADSPA name '" << name << endl; + return new_name; + } +} + + +/** 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 +LADSPANode::instantiate() +{ + if (!_ports) + _ports = new Raul::Array(_descriptor->PortCount); + + _instances = new Raul::Array(_polyphony, NULL); + + size_t port_buffer_size = 0; + + for (uint32_t i=0; i < _polyphony; ++i) { + (*_instances)[i] = _descriptor->instantiate(_descriptor, _srate); + if ((*_instances)[i] == NULL) { + cerr << "Failed to instantiate plugin!" << endl; + return false; + } + } + + string port_name; + string port_path; + + PortImpl* port = NULL; + + for (size_t j=0; j < _descriptor->PortCount; ++j) { + port_name = nameify_if_invalid(_descriptor->PortNames[j]); + + string::size_type slash_index; + + // Name mangling, to guarantee port names are unique + for (size_t k=0; k < _descriptor->PortCount; ++k) { + assert(_descriptor->PortNames[k] != NULL); + if (k != j && port_name == _descriptor->PortNames[k]) { // clash + if (LADSPA_IS_PORT_CONTROL(_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] = '-'; + } + + /*if (_descriptor->PortNames[j] != port_name) + cerr << "NOTICE: Translated LADSPA port name: " << + _descriptor->PortNames[j] << " -> " << port_name << endl;*/ + + port_path = path() + "/" + port_name; + + DataType type = DataType::AUDIO; + port_buffer_size = _buffer_size; + + if (LADSPA_IS_PORT_CONTROL(_descriptor->PortDescriptors[j])) { + type = DataType::CONTROL; + port_buffer_size = 1; + } else { + assert(LADSPA_IS_PORT_AUDIO(_descriptor->PortDescriptors[j])); + } + assert (LADSPA_IS_PORT_INPUT(_descriptor->PortDescriptors[j]) + || LADSPA_IS_PORT_OUTPUT(_descriptor->PortDescriptors[j])); + + boost::optional default_val, min, max; + get_port_limits(j, default_val, min, max); + + const float value = default_val ? default_val.get() : 0.0f; + + if (LADSPA_IS_PORT_INPUT(_descriptor->PortDescriptors[j])) { + port = new InputPort(this, port_name, j, _polyphony, type, value, port_buffer_size); + _ports->at(j) = port; + } else if (LADSPA_IS_PORT_OUTPUT(_descriptor->PortDescriptors[j])) { + port = new OutputPort(this, port_name, j, _polyphony, type, value, port_buffer_size); + _ports->at(j) = port; + } + + assert(port); + assert(_ports->at(j) == port); + + // Work around broke-ass crackhead plugins + if (default_val && default_val.get() < min.get()) { + cerr << "WARNING: Broken LADSPA " << _descriptor->UniqueID + << ": Port default < minimum. Minimum adjusted." << endl; + min = default_val; + } + + if (default_val && default_val.get() > max.get()) { + cerr << "WARNING: Broken LADSPA " << _descriptor->UniqueID + << ": Maximum adjusted." << endl; + max = default_val; + } + + // Set initial/default value + if (port->buffer_size() == 1) { + for (uint32_t i=0; i < _polyphony; ++i) + ((AudioBuffer*)port->buffer(i))->set_value(value, 0, 0); + } + + if (port->is_input() && port->buffer_size() == 1) { + if (min) + port->set_variable("ingen:minimum", min.get()); + if (max) + port->set_variable("ingen:maximum", max.get()); + if (default_val) + port->set_variable("ingen:default", default_val.get()); + } + } + + return true; +} + + +void +LADSPANode::activate() +{ + NodeBase::activate(); + + for (uint32_t i=0; i < _polyphony; ++i) { + for (unsigned long j=0; j < _descriptor->PortCount; ++j) { + set_port_buffer(i, j, _ports->at(j)->buffer(i)); + /* if (port->type() == DataType::FLOAT && port->buffer_size() == 1) + port->set_value(0.0f, 0); // FIXME + else if (port->type() == DataType::FLOAT && port->buffer_size() > 1) + port->set_value(0.0f, 0);*/ + } + if (_descriptor->activate != NULL) + _descriptor->activate((*_instances)[i]); + } +} + + +void +LADSPANode::deactivate() +{ + NodeBase::deactivate(); + + for (uint32_t i=0; i < _polyphony; ++i) + if (_descriptor->deactivate != NULL) + _descriptor->deactivate((*_instances)[i]); +} + + +void +LADSPANode::process(ProcessContext& context) +{ + NodeBase::pre_process(context); + + for (uint32_t i=0; i < _polyphony; ++i) + _descriptor->run((*_instances)[i], context.nframes()); + + NodeBase::post_process(context); +} + + +void +LADSPANode::set_port_buffer(uint32_t voice, uint32_t port_num, Buffer* buf) +{ + assert(voice < _polyphony); + + AudioBuffer* audio_buffer = dynamic_cast(buf); + assert(audio_buffer); + + if (port_num < _descriptor->PortCount) + _descriptor->connect_port((*_instances)[voice], port_num, + audio_buffer->data()); +} + + +void +LADSPANode::get_port_limits(unsigned long port_index, + boost::optional& normal, + boost::optional& lower, + boost::optional& upper) +{ + LADSPA_PortRangeHintDescriptor hint_descriptor = _descriptor->PortRangeHints[port_index].HintDescriptor; + + /* set upper and lower, possibly adjusted to the sample rate */ + if (LADSPA_IS_HINT_SAMPLE_RATE(hint_descriptor)) { + upper = _descriptor->PortRangeHints[port_index].UpperBound * _srate; + lower = _descriptor->PortRangeHints[port_index].LowerBound * _srate; + } else { + upper = _descriptor->PortRangeHints[port_index].UpperBound; + lower = _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.get() < 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)) { + assert(lower); + assert(upper); + if (LADSPA_IS_HINT_LOGARITHMIC(hint_descriptor)) { + normal = exp(log(lower.get()) * 0.75 + log(upper.get()) * 0.25); + } else { + normal = lower.get() * 0.75 + upper.get() * 0.25; + } + } else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(hint_descriptor)) { + assert(lower); + assert(upper); + if (LADSPA_IS_HINT_LOGARITHMIC(hint_descriptor)) { + normal = exp(log(lower.get()) * 0.5 + log(upper.get()) * 0.5); + } else { + normal = lower.get() * 0.5 + upper.get() * 0.5; + } + } else if (LADSPA_IS_HINT_DEFAULT_HIGH(hint_descriptor)) { + assert(lower); + assert(upper); + if (LADSPA_IS_HINT_LOGARITHMIC(hint_descriptor)) { + normal = exp(log(lower.get()) * 0.25 + log(upper.get()) * 0.75); + } else { + normal = lower.get() * 0.25 + upper.get() * 0.75; + } + } else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(hint_descriptor)) { + assert(upper); + normal = upper.get(); + } 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)) { + assert(lower); + normal = lower.get(); + } else if (LADSPA_IS_HINT_BOUNDED_ABOVE(hint_descriptor)) { + assert(upper); + normal = upper.get(); + } + } +} + + +} // namespace Ingen + diff --git a/src/engine/LADSPANode.hpp b/src/engine/LADSPANode.hpp new file mode 100644 index 00000000..53d7ad9e --- /dev/null +++ b/src/engine/LADSPANode.hpp @@ -0,0 +1,74 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LADSPANODE_H +#define LADSPANODE_H + +#include +#include +#include +#include "types.hpp" +#include "NodeBase.hpp" +#include "PluginImpl.hpp" + +namespace Ingen { + + +/** An instance of a LADSPA plugin. + * + * \ingroup engine + */ +class LADSPANode : public NodeBase +{ +public: + LADSPANode(PluginImpl* plugin, + const string& name, + bool polyphonic, + PatchImpl* parent, + const LADSPA_Descriptor* descriptor, + SampleRate srate, + size_t buffer_size); + + ~LADSPANode(); + + bool instantiate(); + + bool prepare_poly(uint32_t poly); + bool apply_poly(Raul::Maid& maid, uint32_t poly); + + void activate(); + void deactivate(); + + void process(ProcessContext& context); + + void set_port_buffer(uint32_t voice, uint32_t port_num, Buffer* buf); + +protected: + void get_port_limits(unsigned long port_index, + boost::optional& default_value, + boost::optional& lower_bound, + boost::optional& upper_bound); + + const LADSPA_Descriptor* _descriptor; + Raul::Array* _instances; + Raul::Array* _prepared_instances; +}; + + +} // namespace Ingen + +#endif // LADSPANODE_H diff --git a/src/engine/LADSPAPlugin.cpp b/src/engine/LADSPAPlugin.cpp new file mode 100644 index 00000000..4a0b5c14 --- /dev/null +++ b/src/engine/LADSPAPlugin.cpp @@ -0,0 +1,79 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include "LADSPAPlugin.hpp" +#include "LADSPANode.hpp" +#include "NodeImpl.hpp" +#include "Engine.hpp" +#include "AudioDriver.hpp" + +using namespace std; + +namespace Ingen { + + +NodeImpl* +LADSPAPlugin::instantiate(const string& name, + bool polyphonic, + Ingen::PatchImpl* parent, + Engine& engine) +{ + assert(_id != 0); + + SampleCount srate = engine.audio_driver()->sample_rate(); + SampleCount buffer_size = engine.audio_driver()->buffer_size(); + + LADSPA_Descriptor_Function df = NULL; + LADSPANode* n = NULL; + + load(); // FIXME: unload at some point + assert(_module); + assert(*_module); + + if (!_module->get_symbol("ladspa_descriptor", (void*&)df)) { + cerr << "Looks like this isn't a LADSPA plugin." << endl; + return NULL; + } + + // Attempt to find the plugin in library + LADSPA_Descriptor* descriptor = NULL; + for (unsigned long i=0; (descriptor = (LADSPA_Descriptor*)df(i)) != NULL; ++i) { + if (descriptor->UniqueID == _id) { + break; + } + } + + if (descriptor != NULL) { + n = new LADSPANode(this, name, polyphonic, parent, descriptor, srate, buffer_size); + + if ( ! n->instantiate() ) { + delete n; + n = NULL; + } + + } else { + cerr << "Could not find plugin \"" << _id << "\" in " << _library_path << endl; + } + + return n; +} + + +} // namespace Ingen diff --git a/src/engine/LADSPAPlugin.hpp b/src/engine/LADSPAPlugin.hpp new file mode 100644 index 00000000..2414be7c --- /dev/null +++ b/src/engine/LADSPAPlugin.hpp @@ -0,0 +1,78 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LADSPAPLUGIN_H +#define LADSPAPLUGIN_H + +#include CONFIG_H_PATH + +#include +#include +#include +#include +#include +#include +#include +#include "types.hpp" +#include "PluginImpl.hpp" + +namespace Ingen { + +class NodeImpl; + + +/** Implementation of a LADSPA plugin (loaded shared library). + */ +class LADSPAPlugin : public PluginImpl +{ +public: + LADSPAPlugin(const std::string& library_path, + const std::string& uri, + unsigned long id, + const string& label, + const string& name) + : PluginImpl(Plugin::LADSPA, uri, library_path) + , _id(id) + , _label(label) + , _name(name) + {} + + NodeImpl* instantiate(const std::string& name, + bool polyphonic, + Ingen::PatchImpl* parent, + Engine& engine); + + const std::string& label() const { return _label; } + unsigned long id() const { return _id; } + const string symbol() const { return Raul::Path::nameify(_label); } + const string name() const { return _name; } + + const string library_name() const { + return _library_path.substr(_library_path.find_last_of("/")+1); + } + +private: + const unsigned long _id; + const std::string _label; + const std::string _name; +}; + + +} // namespace Ingen + +#endif // LADSPAPLUGIN_H + diff --git a/src/engine/LV2Info.cpp b/src/engine/LV2Info.cpp new file mode 100644 index 00000000..43dd014b --- /dev/null +++ b/src/engine/LV2Info.cpp @@ -0,0 +1,70 @@ +/* This file is part of Ingen. + * Copyright (C) 2008 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define __STDC_LIMIT_MACROS 1 +#include +#include +#include +#include "LV2Info.hpp" +#include + +using namespace std; + +namespace Ingen { + +LV2Info::LV2Info(Ingen::Shared::World* world) + : input_class(slv2_value_new_uri(world->slv2_world, SLV2_PORT_CLASS_INPUT)) + , output_class(slv2_value_new_uri(world->slv2_world, SLV2_PORT_CLASS_OUTPUT)) + , control_class(slv2_value_new_uri(world->slv2_world, SLV2_PORT_CLASS_CONTROL)) + , audio_class(slv2_value_new_uri(world->slv2_world, SLV2_PORT_CLASS_AUDIO)) + , event_class(slv2_value_new_uri(world->slv2_world, SLV2_PORT_CLASS_EVENT)) + , _world(world) +{ + assert(world); + + LV2_Event_Feature* ev_data = (LV2_Event_Feature*)malloc(sizeof(LV2_Event_Feature)); + ev_data->lv2_event_ref = &LV2Info::event_ref; + ev_data->lv2_event_unref = &LV2Info::event_ref; + ev_data->callback_data = this; + LV2_Feature* ev_feature = (LV2_Feature*)malloc(sizeof(LV2_Event_Feature)); + ev_feature->URI = LV2_EVENT_URI; + ev_feature->data = ev_data; + + world->lv2_features->add_feature(LV2_EVENT_URI, ev_feature, ev_data); +} + + +LV2Info::~LV2Info() +{ + slv2_value_free(input_class); + slv2_value_free(output_class); + slv2_value_free(control_class); + slv2_value_free(audio_class); + slv2_value_free(event_class); +} + + +uint32_t +LV2Info::event_ref(LV2_Event_Callback_Data callback_data, + LV2_Event* event) +{ + return 0; +} + + + +} // namespace Ingen diff --git a/src/engine/LV2Info.hpp b/src/engine/LV2Info.hpp new file mode 100644 index 00000000..f4859ac7 --- /dev/null +++ b/src/engine/LV2Info.hpp @@ -0,0 +1,66 @@ +/* This file is part of Ingen. + * Copyright (C) 2008 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LV2INFO_H +#define LV2INFO_H + +#include CONFIG_H_PATH +#ifndef HAVE_SLV2 +#error "This file requires SLV2, but HAVE_SLV2 is not defined. Please report." +#endif + +#include +#include +#include +#include "module/global.hpp" +#include "module/World.hpp" +#include "shared/LV2URIMap.hpp" +#include "lv2ext/lv2_uri_map.h" +#include "lv2ext/lv2_event.h" + +namespace Ingen { + + +/** Stuff that may need to be passed to an LV2 plugin (i.e. LV2 features). + */ +class LV2Info : public Shared::LV2URIMap { +public: + LV2Info(Ingen::Shared::World* world); + ~LV2Info(); + + SLV2Value input_class; + SLV2Value output_class; + SLV2Value control_class; + SLV2Value audio_class; + SLV2Value event_class; + + Ingen::Shared::World& world() { return *_world; } + SLV2World lv2_world() { return _world->slv2_world; } + + static uint32_t event_ref(LV2_Event_Callback_Data callback_data, + LV2_Event* event); + + LV2_Feature** lv2_features() const { return _world->lv2_features->lv2_features(); } + +private: + Ingen::Shared::World* _world; +}; + + +} // namespace Ingen + +#endif // LV2INFO_H diff --git a/src/engine/LV2Node.cpp b/src/engine/LV2Node.cpp new file mode 100644 index 00000000..a06cc55a --- /dev/null +++ b/src/engine/LV2Node.cpp @@ -0,0 +1,305 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include "AudioBuffer.hpp" +#include "InputPort.hpp" +#include "LV2Node.hpp" +#include "LV2Plugin.hpp" +#include "EventBuffer.hpp" +#include "OutputPort.hpp" +#include "ProcessContext.hpp" +#include "lv2_contexts.h" + +using namespace std; + +namespace Ingen { + + +/** Partially construct a LV2Node. + * + * Object is not usable until instantiate() is called with success. + * (It _will_ crash!) + */ +LV2Node::LV2Node(LV2Plugin* plugin, + const string& name, + bool polyphonic, + PatchImpl* parent, + SampleRate srate, + size_t buffer_size) + : NodeBase(plugin, name, polyphonic, parent, srate, buffer_size) + , _lv2_plugin(plugin) + , _instances(NULL) + , _prepared_instances(NULL) + , _message_run(NULL) +{ + assert(_lv2_plugin); +} + + +LV2Node::~LV2Node() +{ + for (uint32_t i=0; i < _polyphony; ++i) + slv2_instance_free((*_instances)[i]); + + delete _instances; +} + + +bool +LV2Node::prepare_poly(uint32_t poly) +{ + NodeBase::prepare_poly(poly); + + if ( (!_polyphonic) + || (_prepared_instances && poly <= _prepared_instances->size()) ) { + return true; + } + + _prepared_instances = new Raul::Array(poly, *_instances); + for (uint32_t i = _polyphony; i < _prepared_instances->size(); ++i) { + // FIXME: features array (in NodeFactory) must be passed! + _prepared_instances->at(i) = slv2_plugin_instantiate( + _lv2_plugin->slv2_plugin(), _srate, NULL); + + if (_prepared_instances->at(i) == NULL) { + cerr << "Failed to instantiate plugin!" << endl; + return false; + } + + if (_activated) + slv2_instance_activate(_prepared_instances->at(i)); + } + + return true; +} + + +bool +LV2Node::apply_poly(Raul::Maid& maid, uint32_t poly) +{ + if (!_polyphonic) + return true; + + if (_prepared_instances) { + assert(poly <= _prepared_instances->size()); + maid.push(_instances); + _instances = _prepared_instances; + _prepared_instances = NULL; + } + + assert(poly <= _instances->size()); + _polyphony = poly; + + return NodeBase::apply_poly(maid, poly); +} + + +/** 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 +LV2Node::instantiate() +{ + SharedPtr info = _lv2_plugin->lv2_info(); + SLV2Plugin plug = _lv2_plugin->slv2_plugin(); + + uint32_t num_ports = slv2_plugin_get_num_ports(plug); + assert(num_ports > 0); + + _ports = new Raul::Array(num_ports, NULL); + _instances = new Raul::Array(_polyphony, NULL); + + uint32_t port_buffer_size = 0; + + for (uint32_t i=0; i < _polyphony; ++i) { + (*_instances)[i] = slv2_plugin_instantiate(plug, _srate, info->lv2_features()); + if ((*_instances)[i] == NULL) { + cerr << "Failed to instantiate plugin!" << endl; + return false; + } + + const void* ctx_ext = slv2_instance_get_extension_data( + (*_instances)[i], LV2_CONTEXT_MESSAGE); + + if (ctx_ext) { + cerr << "HAS CONTEXT EXTENSION" << endl; + if (_message_run == NULL) + _message_run = new MessageRunFuncs(_polyphony, NULL); + LV2MessageContext* mc = (LV2MessageContext*)ctx_ext; + (*_message_run)[i] = mc->message_run; + } + } + + string port_name; + string port_path; + + PortImpl* port = NULL; + + float* def_values = new float[num_ports]; + slv2_plugin_get_port_ranges_float(plug, 0, 0, def_values); + + for (uint32_t j=0; j < num_ports; ++j) { + SLV2Port id = slv2_plugin_get_port_by_index(plug, j); + + // LV2 shortnames are guaranteed to be unique, valid C identifiers + port_name = slv2_value_as_string(slv2_port_get_symbol(plug, id)); + + assert(port_name.find("/") == string::npos); + + port_path = path() + "/" + port_name; + + DataType data_type = DataType::UNKNOWN; + if (slv2_port_is_a(plug, id, info->control_class)) { + data_type = DataType::CONTROL; + port_buffer_size = 1; + } else if (slv2_port_is_a(plug, id, info->audio_class)) { + data_type = DataType::AUDIO; + port_buffer_size = _buffer_size; + } else if (slv2_port_is_a(plug, id, info->event_class)) { + data_type = DataType::EVENT; + port_buffer_size = _buffer_size; + } + + enum { UNKNOWN, INPUT, OUTPUT } direction = UNKNOWN; + if (slv2_port_is_a(plug, id, info->input_class)) { + direction = INPUT; + } else if (slv2_port_is_a(plug, id, info->output_class)) { + direction = OUTPUT; + } + + if (data_type == DataType::UNKNOWN || direction == UNKNOWN) { + delete _ports; + _ports = NULL; + delete _instances; + _instances = NULL; + return false; + } + + // FIXME: need nice type preserving SLV2Value -> Raul::Atom conversion + float def = isnan(def_values[j]) ? 0.0f : def_values[j]; + Atom defatm = def; + + if (direction == INPUT) + port = new InputPort(this, port_name, j, _polyphony, data_type, defatm, port_buffer_size); + else + port = new OutputPort(this, port_name, j, _polyphony, data_type, defatm, port_buffer_size); + + if (direction == INPUT && data_type == DataType::CONTROL) + ((AudioBuffer*)port->buffer(0))->set_value(def, 0, 0); + + SLV2Value pred = slv2_value_new_uri(info->lv2_world(), + "http://lv2plug.in/ns/dev/contexts#context"); + SLV2Values contexts = slv2_port_get_value(plug, id, pred); + for (uint32_t i = 0; i < slv2_values_size(contexts); ++i) { + SLV2Value c = slv2_values_get_at(contexts, i); + const char* context = slv2_value_as_string(c); + if (!strcmp("http://lv2plug.in/ns/dev/contexts#MessageContext", context)) { + cout << "MESSAGE CONTEXT!" << endl; + port->set_context(Context::MESSAGE); + } else { + cout << "UNKNOWN CONTEXT: " + << slv2_value_as_string(slv2_values_get_at(contexts, i)) + << endl; + } + } + + _ports->at(j) = port; + } + + delete [] def_values; + + return true; +} + + +void +LV2Node::activate() +{ + NodeBase::activate(); + + for (uint32_t i=0; i < _polyphony; ++i) { + for (unsigned long j=0; j < num_ports(); ++j) { + PortImpl* const port = _ports->at(j); + + set_port_buffer(i, j, port->buffer(i)); + + if (port->type() == DataType::CONTROL) { + ((AudioBuffer*)port->buffer(i))->set_value(port->value().get_float(), 0, 0); + } else if (port->type() == DataType::AUDIO) { + ((AudioBuffer*)port->buffer(i))->set_value(0.0f, 0, 0); + } + } + slv2_instance_activate((*_instances)[i]); + } +} + + +void +LV2Node::deactivate() +{ + NodeBase::deactivate(); + + for (uint32_t i=0; i < _polyphony; ++i) + slv2_instance_deactivate((*_instances)[i]); +} + + +void +LV2Node::message_process(MessageContext& context, uint32_t* output) +{ + // FIXME: voice + if (_message_run) + (*_message_run)[0]((*_instances)[0], output); + + /* MESSAGE PROCESS */ +} + + +void +LV2Node::process(ProcessContext& context) +{ + NodeBase::pre_process(context); + + for (uint32_t i=0; i < _polyphony; ++i) + slv2_instance_run((*_instances)[i], context.nframes()); + + NodeBase::post_process(context); +} + + +void +LV2Node::set_port_buffer(uint32_t voice, uint32_t port_num, Buffer* buf) +{ + assert(voice < _polyphony); + + slv2_instance_connect_port((*_instances)[voice], port_num, buf->raw_data()); +} + + +} // namespace Ingen + diff --git a/src/engine/LV2Node.hpp b/src/engine/LV2Node.hpp new file mode 100644 index 00000000..3187005c --- /dev/null +++ b/src/engine/LV2Node.hpp @@ -0,0 +1,75 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LV2NODE_H +#define LV2NODE_H + +#include +#include +#include "types.hpp" +#include "NodeBase.hpp" + +namespace Ingen { + +class LV2Plugin; + + +/** An instance of a LV2 plugin. + * + * \ingroup engine + */ +class LV2Node : public NodeBase +{ +public: + LV2Node(LV2Plugin* plugin, + const string& name, + bool polyphonic, + PatchImpl* parent, + SampleRate srate, + size_t buffer_size); + + ~LV2Node(); + + bool instantiate(); + + bool prepare_poly(uint32_t poly); + bool apply_poly(Raul::Maid& maid, uint32_t poly); + + void activate(); + void deactivate(); + + void message_process(MessageContext& context, uint32_t* output); + + void process(ProcessContext& context); + + void set_port_buffer(uint32_t voice, uint32_t port_num, Buffer* buf); + +protected: + LV2Plugin* _lv2_plugin; + Raul::Array* _instances; + Raul::Array* _prepared_instances; + + typedef bool (*MessageRunFunc)(LV2_Handle, uint32_t*); + typedef Raul::Array MessageRunFuncs; + MessageRunFuncs* _message_run; +}; + + +} // namespace Ingen + +#endif // LV2NODE_H + diff --git a/src/engine/LV2Plugin.cpp b/src/engine/LV2Plugin.cpp new file mode 100644 index 00000000..90a3a6b8 --- /dev/null +++ b/src/engine/LV2Plugin.cpp @@ -0,0 +1,94 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include "LV2Plugin.hpp" +#include "LV2Node.hpp" +#include "NodeImpl.hpp" +#include "Engine.hpp" +#include "AudioDriver.hpp" + +namespace Ingen { + + +const string +LV2Plugin::symbol() const +{ + string working = _uri; + if (working[working.length()-1] == '/') + working = working.substr(0, working.length()-1); + + while (working.length() > 0) { + size_t last_slash = working.find_last_of("/"); + const string symbol = working.substr(last_slash+1); + if ( (symbol[0] >= 'a' && symbol[0] <= 'z') + || (symbol[0] >= 'A' && symbol[0] <= 'Z') ) + return Path::nameify(symbol); + else + working = working.substr(0, last_slash); + } + + return "lv2_symbol"; +} + + +const string +LV2Plugin::name() const +{ + if (_name) + return slv2_value_as_string(_name); + else + return "(no name)"; +} + + +NodeImpl* +LV2Plugin::instantiate(const string& name, + bool polyphonic, + Ingen::PatchImpl* parent, + Engine& engine) +{ + SampleCount srate = engine.audio_driver()->sample_rate(); + SampleCount buffer_size = engine.audio_driver()->buffer_size(); + + load(); // FIXME: unload at some point + + Glib::Mutex::Lock lock(engine.world()->rdf_world->mutex()); + LV2Node* n = new LV2Node(this, name, polyphonic, parent, srate, buffer_size); + + if ( ! n->instantiate() ) { + delete n; + n = NULL; + } + + return n; +} + + +void +LV2Plugin::slv2_plugin(SLV2Plugin p) +{ + _slv2_plugin = p; + if (_name) + slv2_value_free(_name); + _name = slv2_plugin_get_name(_slv2_plugin); +} + + +} // namespace Ingen diff --git a/src/engine/LV2Plugin.hpp b/src/engine/LV2Plugin.hpp new file mode 100644 index 00000000..c9bc4ff9 --- /dev/null +++ b/src/engine/LV2Plugin.hpp @@ -0,0 +1,84 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LV2PLUGIN_H +#define LV2PLUGIN_H + +#include CONFIG_H_PATH + +#ifndef HAVE_SLV2 +#error "This file requires SLV2, but HAVE_SLV2 is not defined. Please report." +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include "types.hpp" +#include "PluginImpl.hpp" +#include "LV2Info.hpp" + +using std::string; +using Ingen::Shared::Plugin; + + +namespace Ingen { + +class PatchImpl; +class NodeImpl; + + +/** Implementation of an LV2 plugin (loaded shared library). + */ +class LV2Plugin : public PluginImpl +{ +public: + LV2Plugin(SharedPtr lv2_info, const string& uri) + : PluginImpl(Plugin::LV2, uri) + , _name(NULL) + , _slv2_plugin(NULL) + , _lv2_info(lv2_info) + {} + + NodeImpl* instantiate(const string& name, + bool polyphonic, + Ingen::PatchImpl* parent, + Engine& engine); + + const string symbol() const; + const string name() const; + + SharedPtr lv2_info() const { return _lv2_info; } + + SLV2Plugin slv2_plugin() const { return _slv2_plugin; } + void slv2_plugin(SLV2Plugin p); + +private: + SLV2Value _name; + SLV2Plugin _slv2_plugin; + SharedPtr _lv2_info; +}; + + +} // namespace Ingen + +#endif // LV2PLUGIN_H + diff --git a/src/engine/Makefile.am b/src/engine/Makefile.am new file mode 100644 index 00000000..3c464de3 --- /dev/null +++ b/src/engine/Makefile.am @@ -0,0 +1,205 @@ +SUBDIRS = events + +MAINTAINERCLEANFILES = Makefile.in + +moduledir = $(libdir)/ingen + +module_LTLIBRARIES = libingen_engine.la +libingen_engine_la_CXXFLAGS = \ + @GLIBMM_CFLAGS@ \ + @INGEN_CFLAGS@ \ + @JACK_CFLAGS@ \ + @LIBLO_CFLAGS@ \ + @RAUL_CFLAGS@ \ + @REDLANDMM_CFLAGS@ \ + @SLV2_CFLAGS@ \ + @SOUP_CFLAGS@ + +libingen_engine_la_LDFLAGS = -no-undefined -module -avoid-version +libingen_engine_la_LIBADD = \ + ../shared/libingen_shared.la \ + ../module/libingen_module.la \ + @GLIBMM_LIBS@ \ + @JACK_LIBS@ \ + @LIBLO_LIBS@ \ + @RAUL_LIBS@ \ + @REDLANDMM_LIBS@ \ + @SLV2_LIBS@ \ + @SOUP_LIBS@ + +AM_CFLAGS=-std=c99 + +libingen_engine_la_SOURCES = \ + AudioBuffer.cpp \ + AudioBuffer.hpp \ + AudioDriver.hpp \ + Buffer.cpp \ + Buffer.hpp \ + ClientBroadcaster.cpp \ + ClientBroadcaster.hpp \ + CompiledPatch.hpp \ + ConnectionImpl.cpp \ + ConnectionImpl.hpp \ + Driver.hpp \ + DuplexPort.cpp \ + DuplexPort.hpp \ + Engine.cpp \ + Engine.hpp \ + EngineStore.cpp \ + EngineStore.hpp \ + Event.cpp \ + Event.hpp \ + EventBuffer.cpp \ + EventBuffer.hpp \ + EventSink.cpp \ + EventSink.hpp \ + EventSource.hpp \ + GraphObjectImpl.cpp \ + GraphObjectImpl.hpp \ + InputPort.cpp \ + InputPort.hpp \ + InternalPlugin.cpp \ + InternalPlugin.hpp \ + JackAudioDriver.cpp \ + JackAudioDriver.hpp \ + JackMidiDriver.cpp \ + JackMidiDriver.hpp \ + LADSPAPlugin.cpp \ + LADSPAPlugin.hpp \ + LV2Info.cpp \ + LV2Info.hpp \ + LV2Plugin.cpp \ + LV2Plugin.hpp \ + MessageContext.cpp \ + MessageContext.hpp \ + MidiControlNode.cpp \ + MidiControlNode.hpp \ + MidiDriver.hpp \ + MidiNoteNode.cpp \ + MidiNoteNode.hpp \ + MidiTriggerNode.cpp \ + MidiTriggerNode.hpp \ + NodeBase.cpp \ + NodeBase.hpp \ + NodeFactory.cpp \ + NodeFactory.hpp \ + NodeImpl.hpp \ + OSCClientSender.cpp \ + OSCClientSender.hpp \ + OSCDriver.hpp \ + OSCEngineReceiver.cpp \ + OSCEngineReceiver.hpp \ + ObjectSender.cpp \ + ObjectSender.hpp \ + OutputPort.cpp \ + OutputPort.hpp \ + PatchImpl.cpp \ + PatchImpl.hpp \ + PatchPlugin.hpp \ + PluginImpl.cpp \ + PluginImpl.hpp \ + PortImpl.cpp \ + PortImpl.hpp \ + PostProcessor.cpp \ + PostProcessor.hpp \ + ProcessContext.hpp \ + ProcessSlave.cpp \ + ProcessSlave.hpp \ + QueuedEngineInterface.cpp \ + QueuedEngineInterface.hpp \ + QueuedEvent.cpp \ + QueuedEvent.hpp \ + QueuedEventSource.cpp \ + QueuedEventSource.hpp \ + Responder.hpp \ + ThreadManager.hpp \ + TransportNode.cpp \ + TransportNode.hpp \ + engine.cpp \ + engine.hpp \ + events.hpp \ + events/AllNotesOffEvent.cpp \ + events/AllNotesOffEvent.hpp \ + events/ClearPatchEvent.cpp \ + events/ClearPatchEvent.hpp \ + events/ConnectionEvent.cpp \ + events/ConnectionEvent.hpp \ + events/CreateNodeEvent.cpp \ + events/CreateNodeEvent.hpp \ + events/CreatePatchEvent.cpp \ + events/CreatePatchEvent.hpp \ + events/CreatePortEvent.cpp \ + events/CreatePortEvent.hpp \ + events/DeactivateEvent.cpp \ + events/DeactivateEvent.hpp \ + events/DestroyEvent.cpp \ + events/DestroyEvent.hpp \ + events/DisconnectAllEvent.cpp \ + events/DisconnectAllEvent.hpp \ + events/DisconnectionEvent.cpp \ + events/DisconnectionEvent.hpp \ + events/EnablePatchEvent.cpp \ + events/EnablePatchEvent.hpp \ + events/LoadPluginsEvent.cpp \ + events/LoadPluginsEvent.hpp \ + events/MidiLearnEvent.cpp \ + events/MidiLearnEvent.hpp \ + events/NoteEvent.cpp \ + events/NoteEvent.hpp \ + events/PingQueuedEvent.hpp \ + events/RegisterClientEvent.cpp \ + events/RegisterClientEvent.hpp \ + events/RenameEvent.cpp \ + events/RenameEvent.hpp \ + events/RequestAllObjectsEvent.cpp \ + events/RequestAllObjectsEvent.hpp \ + events/RequestMetadataEvent.cpp \ + events/RequestMetadataEvent.hpp \ + events/RequestObjectEvent.cpp \ + events/RequestObjectEvent.hpp \ + events/RequestPluginEvent.cpp \ + events/RequestPluginEvent.hpp \ + events/RequestPluginsEvent.cpp \ + events/RequestPluginsEvent.hpp \ + events/RequestPortValueEvent.cpp \ + events/RequestPortValueEvent.hpp \ + events/SendPortActivityEvent.cpp \ + events/SendPortActivityEvent.hpp \ + events/SendPortValueEvent.cpp \ + events/SendPortValueEvent.hpp \ + events/SetMetadataEvent.cpp \ + events/SetMetadataEvent.hpp \ + events/SetPolyphonicEvent.cpp \ + events/SetPolyphonicEvent.hpp \ + events/SetPolyphonyEvent.cpp \ + events/SetPolyphonyEvent.hpp \ + events/SetPortValueEvent.cpp \ + events/SetPortValueEvent.hpp \ + events/UnregisterClientEvent.cpp \ + events/UnregisterClientEvent.hpp \ + jack_compat.h \ + lv2_contexts.h \ + tuning.hpp \ + types.hpp \ + util.hpp + +if WITH_LADSPA +libingen_engine_la_SOURCES += \ + LADSPANode.hpp \ + LADSPANode.cpp +endif + +if WITH_LV2 +libingen_engine_la_SOURCES += \ + LV2Node.hpp \ + LV2Node.cpp +endif + +if WITH_SOUP +libingen_engine_la_SOURCES += \ + HTTPEngineReceiver.cpp \ + HTTPEngineReceiver.hpp +endif + + + diff --git a/src/engine/MessageContext.cpp b/src/engine/MessageContext.cpp new file mode 100644 index 00000000..30f04b05 --- /dev/null +++ b/src/engine/MessageContext.cpp @@ -0,0 +1,32 @@ +/* This file is part of Ingen. + * Copyright (C) 2008 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "MessageContext.hpp" +#include "NodeImpl.hpp" + +namespace Ingen { + +void +MessageContext::run(NodeImpl* node) +{ + uint32_t outputs; + node->message_process(*this, &outputs); + + // Don't care what the plugin output, yet... +} + +} // namespace Ingen diff --git a/src/engine/MessageContext.hpp b/src/engine/MessageContext.hpp new file mode 100644 index 00000000..31531521 --- /dev/null +++ b/src/engine/MessageContext.hpp @@ -0,0 +1,51 @@ +/* This file is part of Ingen. + * Copyright (C) 2007-2008 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MESSAGECONTEXT_H +#define MESSAGECONTEXT_H + +#include "EventSink.hpp" +#include "Context.hpp" + +namespace Ingen { + +class NodeImpl; + +/** Context of a message_process() call. + * + * The message context is a non-hard-realtime thread used to execute things + * that can take too long to execute in an audio thread, and do sloppy timed + * event propagation and scheduling. Interface to plugins via the + * LV2 contexts extension. + * + * \ingroup engine + */ +class MessageContext : public Context +{ +public: + MessageContext(Engine& engine) + : Context(engine, MESSAGE) + {} + + void run(NodeImpl* node); +}; + + +} // namespace Ingen + +#endif // MESSAGECONTEXT_H + diff --git a/src/engine/MidiControlNode.cpp b/src/engine/MidiControlNode.cpp new file mode 100644 index 00000000..bfe0e57e --- /dev/null +++ b/src/engine/MidiControlNode.cpp @@ -0,0 +1,141 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "MidiControlNode.hpp" +#include "PostProcessor.hpp" +#include "MidiLearnEvent.hpp" +#include "InputPort.hpp" +#include "OutputPort.hpp" +#include "InternalPlugin.hpp" +#include "AudioBuffer.hpp" +#include "ProcessContext.hpp" +#include "EventBuffer.hpp" +#include "util.hpp" + +namespace Ingen { + + +MidiControlNode::MidiControlNode(const string& path, + bool polyphonic, + PatchImpl* parent, + SampleRate srate, + size_t buffer_size) + : NodeBase(new InternalPlugin(NS_INGEN "control_node", "controller", "MIDI Controller") + , path, false, parent, srate, buffer_size) + , _learning(false) +{ + _ports = new Raul::Array(6); + + _midi_in_port = new InputPort(this, "input", 0, 1, DataType::EVENT, Atom(), _buffer_size); + _ports->at(0) = _midi_in_port; + + _param_port = new InputPort(this, "controller", 1, 1, DataType::CONTROL, 0.0f, 1); + _param_port->set_variable("ingen:minimum", 0.0f); + _param_port->set_variable("ingen:maximum", 127.0f); + _param_port->set_variable("ingen:integer", true); + _ports->at(1) = _param_port; + + _log_port = new InputPort(this, "logarithmic", 2, 1, DataType::CONTROL, 0.0f, 1); + _log_port->set_variable("ingen:toggled", true); + _ports->at(2) = _log_port; + + _min_port = new InputPort(this, "minimum", 3, 1, DataType::CONTROL, 0.0f, 1); + _ports->at(3) = _min_port; + + _max_port = new InputPort(this, "maximum", 4, 1, DataType::CONTROL, 1.0f, 1); + _ports->at(4) = _max_port; + + _audio_port = new OutputPort(this, "ar_output", 5, 1, DataType::AUDIO, 0.0f, _buffer_size); + _ports->at(5) = _audio_port; +} + + +void +MidiControlNode::process(ProcessContext& context) +{ + NodeBase::pre_process(context); + + uint32_t frames = 0; + uint32_t subframes = 0; + uint16_t type = 0; + uint16_t size = 0; + uint8_t* buf = NULL; + + EventBuffer* const midi_in = (EventBuffer*)_midi_in_port->buffer(0); + assert(midi_in->this_nframes() == context.nframes()); + + while (midi_in->get_event(&frames, &subframes, &type, &size, &buf)) { + // FIXME: type + if (size >= 3 && (buf[0] & 0xF0) == MIDI_CMD_CONTROL) + control(context, buf[1], buf[2], frames + context.start()); + + midi_in->increment(); + } + + NodeBase::post_process(context); +} + + +void +MidiControlNode::control(ProcessContext& context, uchar control_num, uchar val, FrameTime time) +{ + assert(time - context.start() < _buffer_size); + + Sample scaled_value; + + const Sample nval = (val / 127.0f); // normalized [0, 1] + + if (_learning) { + assert(false); // FIXME FIXME FIXME +#if 0 + assert(_learn_event != NULL); + _param_port->set_value(control_num, offset); + assert(_param_port->buffer(0)->value_at(0) == control_num); + _learn_event->set_value(control_num); + _learn_event->execute(offset); + //Engine::instance().post_processor()->push(_learn_event); + //Engine::instance().post_processor()->whip(); + _learning = false; + _learn_event = NULL; +#endif + } + + const Sample min_port_val = ((AudioBuffer*)_min_port->buffer(0))->value_at(0); + const Sample max_port_val = ((AudioBuffer*)_max_port->buffer(0))->value_at(0); + const Sample log_port_val = ((AudioBuffer*)_log_port->buffer(0))->value_at(0); + + if (log_port_val > 0.0f) { + // haaaaack, stupid negatives and logarithms + Sample log_offset = 0; + if (min_port_val < 0) + log_offset = fabs(min_port_val); + const Sample min = log(min_port_val + 1 + log_offset); + const Sample max = log(max_port_val + 1 + log_offset); + scaled_value = expf(nval * (max - min) + min) - 1 - log_offset; + } else { + scaled_value = ((nval) * (max_port_val - min_port_val)) + min_port_val; + } + + if (control_num == ((AudioBuffer*)_param_port->buffer(0))->value_at(0)) + ((AudioBuffer*)_audio_port->buffer(0))->set_value(scaled_value, context.start(), time); +} + + +} // namespace Ingen + diff --git a/src/engine/MidiControlNode.hpp b/src/engine/MidiControlNode.hpp new file mode 100644 index 00000000..2f0496a3 --- /dev/null +++ b/src/engine/MidiControlNode.hpp @@ -0,0 +1,65 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MIDICONTROLNODE_H +#define MIDICONTROLNODE_H + +#include +#include "NodeBase.hpp" + +namespace Ingen { + +class MidiLearnResponseEvent; +class InputPort; +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 NodeBase +{ +public: + MidiControlNode(const std::string& path, bool polyphonic, PatchImpl* parent, SampleRate srate, size_t buffer_size); + + void process(ProcessContext& context); + + void control(ProcessContext& context, uchar control_num, uchar val, FrameTime time); + + void learn(MidiLearnResponseEvent* ev) { _learning = true; _learn_event = ev; } + +private: + bool _learning; + + InputPort* _midi_in_port; + InputPort* _param_port; + InputPort* _log_port; + InputPort* _min_port; + InputPort* _max_port; + OutputPort* _audio_port; + + MidiLearnResponseEvent* _learn_event; +}; + + +} // namespace Ingen + +#endif // MIDICONTROLNODE_H diff --git a/src/engine/MidiDriver.hpp b/src/engine/MidiDriver.hpp new file mode 100644 index 00000000..e268124b --- /dev/null +++ b/src/engine/MidiDriver.hpp @@ -0,0 +1,100 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MIDIDRIVER_H +#define MIDIDRIVER_H + +#include +#include "types.hpp" +#include "Driver.hpp" +#include "EventBuffer.hpp" +#include "ProcessContext.hpp" + +namespace Ingen { + +class ProcessContext; + + +/** Midi driver abstract base class. + * + * \ingroup engine + */ +class MidiDriver : public Driver +{ +public: + MidiDriver() : Driver(DataType::EVENT) {} + + /** Prepare input for the specified (upcoming) cycle. + * + * Realtime safe, run in audio thread before executing the graph for a cycle. + */ + virtual void pre_process(ProcessContext& context) = 0; + + /** Prepare output for the specified (just completed) cycle. + * + * Realtime safe, run in audio thread after executing the graph for a cycle. + */ + virtual void post_process(ProcessContext& context) = 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() { + std::cout << "[DummyMidiDriver] Started Dummy MIDI driver." << std::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(DuplexPort* patch_port) { return NULL; } + + DriverPort* driver_port(const Raul::Path& path) { return NULL; } + + DriverPort* new_port(DuplexPort* patch_port) { return NULL; } + + void add_port(DriverPort* port) {} + DriverPort* remove_port(const Raul::Path& path) { return NULL; } + + void pre_process(ProcessContext& context) {} + void post_process(ProcessContext& context) {} +}; + + + +} // namespace Ingen + +#endif // MIDIDRIVER_H diff --git a/src/engine/MidiNoteNode.cpp b/src/engine/MidiNoteNode.cpp new file mode 100644 index 00000000..ae7a7f0e --- /dev/null +++ b/src/engine/MidiNoteNode.cpp @@ -0,0 +1,390 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include "AudioBuffer.hpp" +#include "AudioDriver.hpp" +#include "InputPort.hpp" +#include "InternalPlugin.hpp" +#include "EventBuffer.hpp" +#include "MidiNoteNode.hpp" +#include "OutputPort.hpp" +#include "PatchImpl.hpp" +#include "ProcessContext.hpp" +#include "util.hpp" + +using namespace std; + +namespace Ingen { + + +MidiNoteNode::MidiNoteNode(const string& path, bool polyphonic, PatchImpl* parent, SampleRate srate, size_t buffer_size) + : NodeBase(new InternalPlugin(NS_INGEN "note_node", "note", "MIDI Note"), + path, polyphonic, parent, srate, buffer_size) + , _voices(new Raul::Array(_polyphony)) + , _prepared_voices(NULL) + , _sustain(false) +{ + _ports = new Raul::Array(5); + + _midi_in_port = new InputPort(this, "input", 0, 1, DataType::EVENT, Atom(), _buffer_size); + _ports->at(0) = _midi_in_port; + + _freq_port = new OutputPort(this, "frequency", 1, _polyphony, DataType::AUDIO, 440.0f, _buffer_size); + _ports->at(1) = _freq_port; + + _vel_port = new OutputPort(this, "velocity", 2, _polyphony, DataType::AUDIO, 0.0f, _buffer_size); + _vel_port->set_variable("ingen:minimum", 0.0f); + _vel_port->set_variable("ingen:maximum", 1.0f); + _ports->at(2) = _vel_port; + + _gate_port = new OutputPort(this, "gate", 3, _polyphony, DataType::AUDIO, 0.0f, _buffer_size); + _gate_port->set_variable("ingen:toggled", true); + _ports->at(3) = _gate_port; + + _trig_port = new OutputPort(this, "trigger", 4, _polyphony, DataType::AUDIO, 0.0f, _buffer_size); + _trig_port->set_variable("ingen:toggled", true); + _ports->at(4) = _trig_port; +} + + +MidiNoteNode::~MidiNoteNode() +{ + delete _voices; +} + + +bool +MidiNoteNode::prepare_poly(uint32_t poly) +{ + if (!_polyphonic) + return true; + + NodeBase::prepare_poly(poly); + + if (_prepared_voices && poly <= _prepared_voices->size()) + return true; + + _prepared_voices = new Raul::Array(poly, *_voices); + + return true; +} + + +bool +MidiNoteNode::apply_poly(Raul::Maid& maid, uint32_t poly) +{ + if (!_polyphonic) + return true; + + NodeBase::apply_poly(maid, poly); + + if (_prepared_voices) { + assert(poly <= _prepared_voices->size()); + maid.push(_voices); + _voices = _prepared_voices; + _prepared_voices = NULL; + } + + _polyphony = poly; + assert(_voices->size() >= _polyphony); + + return true; +} + + +void +MidiNoteNode::process(ProcessContext& context) +{ + NodeBase::pre_process(context); + + uint32_t frames = 0; + uint32_t subframes = 0; + uint16_t type = 0; + uint16_t size = 0; + unsigned char* buf = NULL; + + EventBuffer* const midi_in = (EventBuffer*)_midi_in_port->buffer(0); + assert(midi_in->this_nframes() == context.nframes()); + + //cerr << path() << " # input events: " << midi_in->event_count() << endl; + + if (midi_in->event_count() > 0) + while (midi_in->get_event(&frames, &subframes, &type, &size, &buf)) { + + /*cout << "EVENT TYPE " << type << " @ " << frames << "." << subframes << ": "; + for (uint16_t i = 0; i < size; ++i) + cout << (int)((char)buf[i]) << " "; + cout << endl;*/ + + const FrameTime time = context.start() + (FrameTime)frames; + + if (size >= 3) { + switch (buf[0] & 0xF0) { + case MIDI_CMD_NOTE_ON: + if (buf[2] == 0) + note_off(context, buf[1], time); + else + note_on(context, buf[1], buf[2], time); + break; + case MIDI_CMD_NOTE_OFF: + note_off(context, buf[1], time); + break; + case MIDI_CMD_CONTROL: + switch (buf[1]) { + case MIDI_CTL_ALL_NOTES_OFF: + case MIDI_CTL_ALL_SOUNDS_OFF: + all_notes_off(context, time); + break; + case MIDI_CTL_SUSTAIN: + if (buf[2] > 63) + sustain_on(context, time); + else + sustain_off(context, time); + break; + case MIDI_CMD_BENDER: + // ? + break; + default: + //cerr << "Ignored controller " << buf[1] << endl; + break; + } + break; + default: + //fprintf(stderr, "Unknown (size %d) MIDI event %X\n", size, buf[0]); + break; + } + } else { + //fprintf(stderr, "Unknown (size %d) MIDI event %X\n", size, buf[0]); + } + + if (midi_in->increment() == midi_in->this_nframes()) + break; + } + + NodeBase::post_process(context); +} + + +void +MidiNoteNode::note_on(ProcessContext& context, uchar note_num, uchar velocity, FrameTime time) +{ + assert(time >= context.start() && time <= context.end()); + assert(time - context.start() < _buffer_size); + assert(note_num <= 127); + + Key* key = &_keys[note_num]; + Voice* voice = NULL; + uint32_t voice_num = 0; + + if (key->state != Key::OFF) { + //cerr << "[MidiNoteNode] Double midi note received" << endl; + return; + } + + // Look for free voices + for (uint32_t i=0; i < _polyphony; ++i) { + if ((*_voices)[i].state == Voice::Voice::FREE) { + voice = &(*_voices)[i]; + voice_num = i; + break; + } + } + + // If we didn't find a free one, steal the oldest + if (voice == NULL) { + voice_num = 0; + voice = &(*_voices)[0]; + jack_nframes_t oldest_time = (*_voices)[0].time; + for (uint32_t i=1; i < _polyphony; ++i) { + if ((*_voices)[i].time < oldest_time) { + voice = &(*_voices)[i]; + voice_num = i; + oldest_time = voice->time; + } + } + } + assert(voice != NULL); + assert(voice == &(*_voices)[voice_num]); + + /*cerr << "[MidiNoteNode] Note " << (int)note_num << " on @ " << time + << ". Voice " << voice_num << " / " << _polyphony << endl;*/ + + // Update stolen key, if applicable + if (voice->state == Voice::Voice::ACTIVE) { + assert(_keys[voice->note].state == Key::ON_ASSIGNED); + assert(_keys[voice->note].voice == voice_num); + _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; + + // Trigger voice + voice->state = Voice::Voice::ACTIVE; + voice->note = note_num; + voice->time = time; + + assert(_keys[voice->note].state == Key::Key::ON_ASSIGNED); + assert(_keys[voice->note].voice == voice_num); + + ((AudioBuffer*)_freq_port->buffer(voice_num))->set_value(note_to_freq(note_num), context.start(), time); + ((AudioBuffer*)_vel_port->buffer(voice_num))->set_value(velocity/127.0, context.start(), time); + ((AudioBuffer*)_gate_port->buffer(voice_num))->set_value(1.0f, context.start(), time); + + // trigger (one sample) + ((AudioBuffer*)_trig_port->buffer(voice_num))->set_value(1.0f, context.start(), time); + ((AudioBuffer*)_trig_port->buffer(voice_num))->set_value(0.0f, context.start(), time + 1); + + assert(key->state == Key::Key::ON_ASSIGNED); + assert(voice->state == Voice::Voice::ACTIVE); + assert(key->voice == voice_num); + assert((*_voices)[key->voice].note == note_num); +} + + +void +MidiNoteNode::note_off(ProcessContext& context, uchar note_num, FrameTime time) +{ + assert(time >= context.start() && time <= context.end()); + assert(time - context.start() < _buffer_size); + + Key* key = &_keys[note_num]; + + //cerr << "[MidiNoteNode] Note " << (int)note_num << " off @ " << time << endl; + + if (key->state == Key::ON_ASSIGNED) { + // Assigned key, turn off voice and key + if ((*_voices)[key->voice].state == Voice::ACTIVE) { + assert((*_voices)[key->voice].note == note_num); + + if ( ! _sustain) { + //cerr << "... free voice " << key->voice << endl; + free_voice(context, key->voice, time); + } else { + //cerr << "... hold voice " << key->voice << endl; + (*_voices)[key->voice].state = Voice::HOLDING; + } + + } else { +#ifndef NDEBUG + cerr << "WARNING: Assigned key, but voice not active" << endl; +#endif + } + } + + key->state = Key::OFF; +} + + +void +MidiNoteNode::free_voice(ProcessContext& context, uint32_t voice, FrameTime time) +{ + assert(time >= context.start() && time <= context.end()); + assert(time - context.start() < _buffer_size); + + // 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 (_keys[i].state == Key::ON_UNASSIGNED) { + if (replace_key == NULL || _keys[i].time > replace_key->time) { + replace_key = &_keys[i]; + replace_key_num = i; + } + } + } + + if (replace_key != NULL) { // Found a key to assign to freed voice + assert(&_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 + ((AudioBuffer*)_freq_port->buffer(voice))->set_value(note_to_freq(replace_key_num), context.start(), time); + + replace_key->state = Key::ON_ASSIGNED; + replace_key->voice = voice; + _keys[(*_voices)[voice].note].state = Key::ON_UNASSIGNED; + (*_voices)[voice].note = replace_key_num; + (*_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; + ((AudioBuffer*)_gate_port->buffer(voice))->set_value(0.0f, context.start(), time); + (*_voices)[voice].state = Voice::FREE; + } +} + + +void +MidiNoteNode::all_notes_off(ProcessContext& context, FrameTime time) +{ + assert(time >= context.start() && time <= context.end()); + assert(time - context.start() < _buffer_size); + + //cerr << "All notes off @ " << offset << endl; + + // FIXME: set all keys to Key::OFF? + + for (uint32_t i = 0; i < _polyphony; ++i) { + ((AudioBuffer*)_gate_port->buffer(i))->set_value(0.0f, context.start(), time); + (*_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(ProcessContext& context, FrameTime time) +{ + _sustain = true; +} + + +void +MidiNoteNode::sustain_off(ProcessContext& context, FrameTime time) +{ + assert(time >= context.start() && time <= context.end()); + assert(time - context.start() < _buffer_size); + + _sustain = false; + + for (uint32_t i=0; i < _polyphony; ++i) + if ((*_voices)[i].state == Voice::HOLDING) + free_voice(context, i, time); +} + + +} // namespace Ingen + diff --git a/src/engine/MidiNoteNode.hpp b/src/engine/MidiNoteNode.hpp new file mode 100644 index 00000000..eebcbda6 --- /dev/null +++ b/src/engine/MidiNoteNode.hpp @@ -0,0 +1,88 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MIDINOTENODE_H +#define MIDINOTENODE_H + +#include +#include "types.hpp" +#include "NodeBase.hpp" + +namespace Ingen { + +class InputPort; +class OutputPort; + + +/** MIDI note input node. + * + * For pitched instruments like keyboard, etc. + * + * \ingroup engine + */ +class MidiNoteNode : public NodeBase +{ +public: + MidiNoteNode(const std::string& path, bool polyphonic, PatchImpl* parent, SampleRate srate, size_t buffer_size); + ~MidiNoteNode(); + + bool prepare_poly(uint32_t poly); + bool apply_poly(Raul::Maid& maid, uint32_t poly); + + void process(ProcessContext& context); + + void note_on(ProcessContext& context, uchar note_num, uchar velocity, FrameTime time); + void note_off(ProcessContext& context, uchar note_num, FrameTime time); + void all_notes_off(ProcessContext& context, FrameTime time); + + void sustain_on(ProcessContext& context, FrameTime time); + void sustain_off(ProcessContext& context, FrameTime time); + +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; uint32_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(ProcessContext& context, uint32_t voice, FrameTime time); + + Raul::Array* _voices; + Raul::Array* _prepared_voices; + Key _keys[128]; + bool _sustain; ///< Whether or not hold pedal is depressed + + InputPort* _midi_in_port; + OutputPort* _freq_port; + OutputPort* _vel_port; + OutputPort* _gate_port; + OutputPort* _trig_port; +}; + + +} // namespace Ingen + +#endif // MIDINOTENODE_H diff --git a/src/engine/MidiTriggerNode.cpp b/src/engine/MidiTriggerNode.cpp new file mode 100644 index 00000000..aa2c272f --- /dev/null +++ b/src/engine/MidiTriggerNode.cpp @@ -0,0 +1,135 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "MidiTriggerNode.hpp" +#include "AudioBuffer.hpp" +#include "InputPort.hpp" +#include "OutputPort.hpp" +#include "InternalPlugin.hpp" +#include "ProcessContext.hpp" +#include "EventBuffer.hpp" +#include "util.hpp" + +using namespace std; + +namespace Ingen { + + +MidiTriggerNode::MidiTriggerNode(const string& path, bool polyphonic, PatchImpl* parent, SampleRate srate, size_t buffer_size) + : NodeBase(new InternalPlugin(NS_INGEN "trigger_node", "trigger", "MIDI Trigger"), + path, false, parent, srate, buffer_size) +{ + _ports = new Raul::Array(5); + + _midi_in_port = new InputPort(this, "input", 0, 1, DataType::EVENT, Atom(), _buffer_size); + _ports->at(0) = _midi_in_port; + + _note_port = new InputPort(this, "note", 1, 1, DataType::CONTROL, 60.0f, 1); + _note_port->set_variable("ingen:minimum", 0.0f); + _note_port->set_variable("ingen:maximum", 127.0f); + _note_port->set_variable("ingen:integer", true); + _ports->at(1) = _note_port; + + _gate_port = new OutputPort(this, "gate", 2, 1, DataType::AUDIO, 0.0f, _buffer_size); + _ports->at(2) = _gate_port; + + _trig_port = new OutputPort(this, "trigger", 3, 1, DataType::AUDIO, 0.0f, _buffer_size); + _ports->at(3) = _trig_port; + + _vel_port = new OutputPort(this, "velocity", 4, 1, DataType::AUDIO, 0.0f, _buffer_size); + _ports->at(4) = _vel_port; +} + + +void +MidiTriggerNode::process(ProcessContext& context) +{ + NodeBase::pre_process(context); + + uint32_t frames = 0; + uint32_t subframes = 0; + uint16_t type = 0; + uint16_t size = 0; + uint8_t* buf = NULL; + + EventBuffer* const midi_in = (EventBuffer*)_midi_in_port->buffer(0); + assert(midi_in->this_nframes() == context.nframes()); + + while (midi_in->get_event(&frames, &subframes, &type, &size, &buf)) { + const FrameTime time = context.start() + (FrameTime)frames; + + if (size >= 3) { + switch (buf[0] & 0xF0) { + case MIDI_CMD_NOTE_ON: + if (buf[2] == 0) + note_off(context, buf[1], time); + else + note_on(context, buf[1], buf[2], time); + break; + case MIDI_CMD_NOTE_OFF: + note_off(context, buf[1], time); + break; + case MIDI_CMD_CONTROL: + if (buf[1] == MIDI_CTL_ALL_NOTES_OFF + || buf[1] == MIDI_CTL_ALL_SOUNDS_OFF) + ((AudioBuffer*)_gate_port->buffer(0))->set_value(0.0f, context.start(), time); + default: + break; + } + } + + midi_in->increment(); + } + + NodeBase::post_process(context); +} + + +void +MidiTriggerNode::note_on(ProcessContext& context, uchar note_num, uchar velocity, FrameTime time) +{ + assert(time >= context.start() && time <= context.end()); + assert(time - context.start() < _buffer_size); + + /*cerr << "[MidiTriggerNode] " << path() << " Note " << (int)note_num << " on @ " << time << endl;*/ + + Sample filter_note = ((AudioBuffer*)_note_port->buffer(0))->value_at(0); + if (filter_note >= 0.0 && filter_note < 127.0 && (note_num == (uchar)filter_note)) { + ((AudioBuffer*)_gate_port->buffer(0))->set_value(1.0f, context.start(), time); + ((AudioBuffer*)_trig_port->buffer(0))->set_value(1.0f, context.start(), time); + ((AudioBuffer*)_trig_port->buffer(0))->set_value(0.0f, context.start(), time + 1); + ((AudioBuffer*)_vel_port->buffer(0))->set_value(velocity / 127.0f, context.start(), time); + assert(((AudioBuffer*)_trig_port->buffer(0))->data()[time - context.start()] == 1.0f); + } +} + + +void +MidiTriggerNode::note_off(ProcessContext& context, uchar note_num, FrameTime time) +{ + assert(time >= context.start() && time <= context.end()); + assert(time - context.start() < _buffer_size); + + if (note_num == lrintf(((AudioBuffer*)_note_port->buffer(0))->value_at(0))) + ((AudioBuffer*)_gate_port->buffer(0))->set_value(0.0f, context.start(), time); +} + + +} // namespace Ingen + diff --git a/src/engine/MidiTriggerNode.hpp b/src/engine/MidiTriggerNode.hpp new file mode 100644 index 00000000..ba96589b --- /dev/null +++ b/src/engine/MidiTriggerNode.hpp @@ -0,0 +1,61 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MIDITRIGGERNODE_H +#define MIDITRIGGERNODE_H + +#include +#include "NodeBase.hpp" + +namespace Ingen { + +class InputPort; +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 NodeBase +{ +public: + MidiTriggerNode(const std::string& path, bool polyphonic, PatchImpl* parent, SampleRate srate, size_t buffer_size); + + void process(ProcessContext& context); + + void note_on(ProcessContext& context, uchar note_num, uchar velocity, FrameTime time); + void note_off(ProcessContext& context, uchar note_num, FrameTime time); + +private: + InputPort* _midi_in_port; + InputPort* _note_port; + OutputPort* _gate_port; + OutputPort* _trig_port; + OutputPort* _vel_port; +}; + + +} // namespace Ingen + +#endif // MIDITRIGGERNODE_H diff --git a/src/engine/NodeBase.cpp b/src/engine/NodeBase.cpp new file mode 100644 index 00000000..bb4f0e5c --- /dev/null +++ b/src/engine/NodeBase.cpp @@ -0,0 +1,230 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "NodeBase.hpp" +#include +#include +#include +#include +#include +#include "util.hpp" +#include "PluginImpl.hpp" +#include "ClientBroadcaster.hpp" +#include "PortImpl.hpp" +#include "PatchImpl.hpp" +#include "EngineStore.hpp" +#include "ThreadManager.hpp" + +using namespace std; + +namespace Ingen { + + +NodeBase::NodeBase(PluginImpl* plugin, const string& name, bool polyphonic, PatchImpl* parent, SampleRate srate, size_t buffer_size) + : NodeImpl(parent, name, polyphonic) + , _plugin(plugin) + , _polyphony((polyphonic && parent) ? parent->internal_polyphony() : 1) + , _srate(srate) + , _buffer_size(buffer_size) + , _activated(false) + , _traversed(false) + , _input_ready(1) + , _process_lock(0) + , _n_inputs_ready(0) + , _ports(NULL) + , _providers(new Raul::List()) + , _dependants(new Raul::List()) +{ + assert(_plugin); + assert(_polyphony > 0); + assert(_parent == NULL || (_polyphony == parent->internal_polyphony() || _polyphony == 1)); +} + + +NodeBase::~NodeBase() +{ + if (_activated) + deactivate(); + + delete _providers; + delete _dependants; +} + + +Port* +NodeBase::port(uint32_t index) const +{ + return (*_ports)[index]; +} + + +const Plugin* +NodeBase::plugin() const +{ + return _plugin; +} + + +void +NodeBase::activate() +{ + assert(ThreadManager::current_thread_id() == THREAD_PRE_PROCESS); + assert(!_activated); + _activated = true; +} + + +void +NodeBase::deactivate() +{ + // FIXME: Not true witn monolithic GUI/engine + //assert(ThreadManager::current_thread_id() == THREAD_POST_PROCESS); + assert(_activated); + _activated = false; +} + + +bool +NodeBase::prepare_poly(uint32_t poly) +{ + assert(ThreadManager::current_thread_id() == THREAD_PRE_PROCESS); + + if (!_polyphonic) + return true; + + if (_ports) + for (size_t i=0; i < _ports->size(); ++i) + _ports->at(i)->prepare_poly(poly); + + return true; +} + + +bool +NodeBase::apply_poly(Raul::Maid& maid, uint32_t poly) +{ + assert(ThreadManager::current_thread_id() == THREAD_PROCESS); + + if (!_polyphonic) + return true; + + for (size_t i=0; i < num_ports(); ++i) { + _ports->at(i)->apply_poly(maid, poly); + assert(_ports->at(i)->poly() == poly); + } + + for (uint32_t i=0; i < num_ports(); ++i) + for (uint32_t j=0; j < _polyphony; ++j) + set_port_buffer(j, i, _ports->at(i)->buffer(j)); + + return true; +} + + +void +NodeBase::set_buffer_size(size_t size) +{ + assert(ThreadManager::current_thread_id() == THREAD_PROCESS); + + _buffer_size = size; + + if (_ports) + for (size_t i=0; i < _ports->size(); ++i) + _ports->at(i)->set_buffer_size(size); +} + + +void +NodeBase::reset_input_ready() +{ + //cout << path() << " RESET" << endl; + _n_inputs_ready = 0; + _process_lock = 0; + _input_ready.reset(0); +} + + +bool +NodeBase::process_lock() +{ + return _process_lock.compare_and_exchange(0, 1); +} + + +void +NodeBase::process_unlock() +{ + _process_lock = 0; +} + + +void +NodeBase::wait_for_input(size_t num_providers) +{ + assert(ThreadManager::current_thread_id() == THREAD_PROCESS); + assert(_process_lock.get() == 1); + + while ((unsigned)_n_inputs_ready.get() < num_providers) { + //cout << path() << " WAITING " << _n_inputs_ready.get() << endl; + _input_ready.wait(); + //cout << path() << " CAUGHT SIGNAL" << endl; + //++_n_inputs_ready; + } + + //cout << path() << " READY" << endl; +} + + +void +NodeBase::signal_input_ready() +{ + assert(ThreadManager::current_thread_id() == THREAD_PROCESS); + //cout << path() << " SIGNAL" << endl; + ++_n_inputs_ready; + _input_ready.post(); +} + + +/** Prepare to run a cycle (in the audio thread) + */ +void +NodeBase::pre_process(ProcessContext& context) +{ + assert(ThreadManager::current_thread_id() == THREAD_PROCESS); + + // Mix down any ports with multiple inputs + for (size_t i=0; i < num_ports(); ++i) + _ports->at(i)->pre_process(context); +} + + +/** Prepare to run a cycle (in the audio thread) + */ +void +NodeBase::post_process(ProcessContext& context) +{ + assert(ThreadManager::current_thread_id() == THREAD_PROCESS); + + /* Write output ports */ + if (_ports) + for (size_t i=0; i < _ports->size(); ++i) + _ports->at(i)->post_process(context); +} + + +} // namespace Ingen + diff --git a/src/engine/NodeBase.hpp b/src/engine/NodeBase.hpp new file mode 100644 index 00000000..e3710aa9 --- /dev/null +++ b/src/engine/NodeBase.hpp @@ -0,0 +1,132 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef NODEBASE_H +#define NODEBASE_H + +#include "types.hpp" +#include +#include +#include +#include +#include "interface/Port.hpp" +#include "NodeImpl.hpp" + +using std::string; + +namespace Ingen { + +class PluginImpl; +class PatchImpl; +class EngineStore; + +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 NodeImpl +{ +public: + NodeBase(PluginImpl* plugin, + const string& name, + bool poly, + PatchImpl* parent, + SampleRate rate, + size_t buffer_size); + + virtual ~NodeBase(); + + virtual void activate(); + virtual void deactivate(); + bool activated() { return _activated; } + + virtual bool prepare_poly(uint32_t poly); + virtual bool apply_poly(Raul::Maid& maid, uint32_t poly); + + virtual void reset_input_ready(); + virtual bool process_lock(); + virtual void process_unlock(); + virtual void wait_for_input(size_t num_providers); + virtual unsigned n_inputs_ready() const { return _n_inputs_ready.get(); } + + virtual void message_process(MessageContext& context, uint32_t* output) {} + + virtual void pre_process(ProcessContext& context); + virtual void process(ProcessContext& context) = 0; + virtual void post_process(ProcessContext& context); + + virtual void set_port_buffer(uint32_t voice, uint32_t port_num, Buffer* buf) {} + + virtual void set_buffer_size(size_t size); + + SampleRate sample_rate() const { return _srate; } + size_t buffer_size() const { return _buffer_size; } + uint32_t num_ports() const { return _ports ? _ports->size() : 0; } + uint32_t polyphony() const { return _polyphony; } + bool traversed() const { return _traversed; } + void traversed(bool b) { _traversed = b; } + + virtual Port* port(uint32_t index) const; + virtual PortImpl* port_impl(uint32_t index) const { return (*_ports)[index]; } + + /* These are NOT to be used in the audio thread! + * The providers and dependants in CompiledNode are for that + */ + + Raul::List* providers() { return _providers; } + void providers(Raul::List* l) { _providers = l; } + + Raul::List* dependants() { return _dependants; } + void dependants(Raul::List* l) { _dependants = l; } + + virtual const Plugin* plugin() const; + virtual PluginImpl* plugin_impl() const { return _plugin; } + virtual void plugin(PluginImpl* pi) { _plugin = pi; } + + /** A node's parent is always a patch, so static cast should be safe */ + inline PatchImpl* parent_patch() const { return (PatchImpl*)_parent; } + +protected: + virtual void signal_input_ready(); + + PluginImpl* _plugin; + + uint32_t _polyphony; + SampleRate _srate; + size_t _buffer_size; + bool _activated; + + bool _traversed; ///< Flag for process order algorithm + Raul::Semaphore _input_ready; ///< Parallelism: input ready signal + Raul::AtomicInt _process_lock; ///< Parallelism: Waiting on inputs 'lock' + Raul::AtomicInt _n_inputs_ready; ///< Parallelism: # input ready signals this cycle + Raul::Array* _ports; ///< Access in audio thread only + Raul::List* _providers; ///< Nodes connected to this one's input ports + Raul::List* _dependants; ///< Nodes this one's output ports are connected to +}; + + +} // namespace Ingen + +#endif // NODEBASE_H diff --git a/src/engine/NodeFactory.cpp b/src/engine/NodeFactory.cpp new file mode 100644 index 00000000..c868d067 --- /dev/null +++ b/src/engine/NodeFactory.cpp @@ -0,0 +1,285 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include CONFIG_H_PATH +#include +#include +#include +#include +#include +#include +#include "module/World.hpp" +#include "NodeFactory.hpp" +#include "ThreadManager.hpp" +#include "MidiNoteNode.hpp" +#include "MidiTriggerNode.hpp" +#include "MidiControlNode.hpp" +#include "TransportNode.hpp" +#include "PatchImpl.hpp" +#include "InternalPlugin.hpp" +#ifdef HAVE_LADSPA +#include "LADSPANode.hpp" +#include "LADSPAPlugin.hpp" +#endif +#ifdef HAVE_SLV2 +#include +#include "LV2Plugin.hpp" +#include "LV2Node.hpp" +#endif + +using namespace std; + +namespace Ingen { + + +NodeFactory::NodeFactory(Ingen::Shared::World* world) + : _world(world) + , _has_loaded(false) +#ifdef HAVE_SLV2 + , _lv2_info(new LV2Info(world)) +#endif +{ +} + + +NodeFactory::~NodeFactory() +{ + for (Plugins::iterator i = _plugins.begin(); i != _plugins.end(); ++i) + if (i->second->type() != Plugin::Internal) + delete i->second; + + _plugins.clear(); +} + + +PluginImpl* +NodeFactory::plugin(const string& uri) +{ + const Plugins::const_iterator i = _plugins.find(uri); + return ((i != _plugins.end()) ? i->second : NULL); +} + + +/** DEPRECATED: Find a plugin by type, lib, label. + * + * Slow. Evil. Do not use. + */ +PluginImpl* +NodeFactory::plugin(const string& type, const string& lib, const string& label) +{ + if (type != "LADSPA" || lib == "" || label == "") + return NULL; + +#ifdef HAVE_LADSPA + for (Plugins::const_iterator i = _plugins.begin(); i != _plugins.end(); ++i) { + LADSPAPlugin* lp = dynamic_cast(i->second); + if (lp && lp->type_string() == type + && lp->library_name() == lib + && lp->label() == label) + return lp; + } +#endif + + cerr << "ERROR: Failed to find " << type << " plugin " << lib << " / " << label << endl; + + return NULL; +} + + +void +NodeFactory::load_plugins() +{ + assert(ThreadManager::current_thread_id() == THREAD_PRE_PROCESS); + + _world->rdf_world->mutex().lock(); + + // 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 (!_has_loaded) { + _plugins.clear(); // FIXME: assert empty? + + load_internal_plugins(); + +#ifdef HAVE_SLV2 + load_lv2_plugins(); +#endif + +#ifdef HAVE_LADSPA + load_ladspa_plugins(); +#endif + + _has_loaded = true; + } + + _world->rdf_world->mutex().unlock(); + + //cerr << "[NodeFactory] # Plugins: " << _plugins.size() << endl; +} + + +void +NodeFactory::load_internal_plugins() +{ + // This is a touch gross... + + PatchImpl* parent = new PatchImpl(*_world->local_engine, "dummy", 1, NULL, 1, 1, 1); + + NodeImpl* n = NULL; + n = new MidiNoteNode("foo", 1, parent, 1, 1); + _plugins.insert(make_pair(n->plugin_impl()->uri(), n->plugin_impl())); + delete n; + n = new MidiTriggerNode("foo", 1, parent, 1, 1); + _plugins.insert(make_pair(n->plugin_impl()->uri(), n->plugin_impl())); + delete n; + n = new MidiControlNode("foo", 1, parent, 1, 1); + _plugins.insert(make_pair(n->plugin_impl()->uri(), n->plugin_impl())); + delete n; + n = new TransportNode("foo", 1, parent, 1, 1); + _plugins.insert(make_pair(n->plugin_impl()->uri(), n->plugin_impl())); + delete n; + + delete parent; +} + + +#ifdef HAVE_SLV2 +/** Loads information about all LV2 plugins into internal plugin database. + */ +void +NodeFactory::load_lv2_plugins() +{ + SLV2Plugins plugins = slv2_world_get_all_plugins(_world->slv2_world); + + //cerr << "[NodeFactory] Found " << slv2_plugins_size(plugins) << " LV2 plugins:" << endl; + + for (unsigned i=0; i < slv2_plugins_size(plugins); ++i) { + + SLV2Plugin lv2_plug = slv2_plugins_get_at(plugins, i); + + const string uri(slv2_value_as_uri(slv2_plugin_get_uri(lv2_plug))); + +#ifndef NDEBUG + assert(_plugins.find(uri) == _plugins.end()); +#endif + + LV2Plugin* const plugin = new LV2Plugin(_lv2_info, uri); + + plugin->slv2_plugin(lv2_plug); + plugin->library_path(slv2_uri_to_path(slv2_value_as_uri( + slv2_plugin_get_library_uri(lv2_plug)))); + _plugins.insert(make_pair(uri, plugin)); + } + + slv2_plugins_free(_world->slv2_world, plugins); +} +#endif // HAVE_SLV2 + + +#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; + } + + // Yep, this should use an sstream alright.. + while (ladspa_path != "") { + const string 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))) { + + LADSPA_Descriptor_Function df = NULL; + LADSPA_Descriptor* descriptor = NULL; + + if (!strcmp(pfile->d_name, ".") || !strcmp(pfile->d_name, "..")) + continue; + + const string lib_path = dir +"/"+ pfile->d_name; + + // Ignore stupid libtool files. Kludge alert. + if (lib_path.substr(lib_path.length()-3) == ".la") { + //cerr << "WARNING: Skipping stupid libtool file " << pfile->d_name << endl; + continue; + } + + Glib::Module* plugin_library = new Glib::Module(lib_path, Glib::MODULE_BIND_LOCAL); + if (!plugin_library || !(*plugin_library)) { + cerr << "WARNING: Failed to load LADSPA library " << lib_path << endl; + continue; + } + + bool found = plugin_library->get_symbol("ladspa_descriptor", (void*&)df); + if (!found || !df) { + cerr << "WARNING: Non-LADSPA library found in LADSPA path: " << + lib_path << endl; + // Not a LADSPA plugin library + delete plugin_library; + continue; + } + + for (unsigned long i=0; (descriptor = (LADSPA_Descriptor*)df(i)) != NULL; ++i) { + char id_str[11]; + snprintf(id_str, 11, "%lu", descriptor->UniqueID); + const string uri = string("ladspa:").append(id_str); + + const Plugins::const_iterator i = _plugins.find(uri); + + if (i == _plugins.end()) { + LADSPAPlugin* plugin = new LADSPAPlugin(lib_path, uri, + descriptor->UniqueID, + descriptor->Label, + descriptor->Name); + + _plugins.insert(make_pair(uri, plugin)); + + } else { + cerr << "Warning: Duplicate " << uri + << " - Using " << i->second->library_path() + << " over " << lib_path << endl; + } + } + + delete plugin_library; + } + closedir(pdir); + } +} +#endif // HAVE_LADSPA + + +} // namespace Ingen diff --git a/src/engine/NodeFactory.hpp b/src/engine/NodeFactory.hpp new file mode 100644 index 00000000..95194350 --- /dev/null +++ b/src/engine/NodeFactory.hpp @@ -0,0 +1,92 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef NODEFACTORY_H +#define NODEFACTORY_H + +#include CONFIG_H_PATH +#include "module/global.hpp" + +#include +#include +#include +#include +#include +#ifdef HAVE_SLV2 +#include +#include "LV2Info.hpp" +#endif +#include "types.hpp" + +using std::string; + +namespace Ingen { + +class NodeImpl; +class PatchImpl; +class PluginImpl; + + +/** 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 LADSPANode) are responsible + * for actually creating a Node instance of the plugin. + * + * \ingroup engine + */ +class NodeFactory +{ +public: + NodeFactory(Ingen::Shared::World* world); + ~NodeFactory(); + + void load_plugins(); + + typedef std::map Plugins; + const Plugins& plugins() const { return _plugins; } + + PluginImpl* plugin(const string& uri); + + /** DEPRECATED */ + PluginImpl* plugin(const string& type, const string& lib, const string& label); + +private: +#ifdef HAVE_LADSPA + void load_ladspa_plugins(); +#endif + +#ifdef HAVE_SLV2 + void load_lv2_plugins(); +#endif + + void load_internal_plugins(); + + Plugins _plugins; + Ingen::Shared::World* _world; + bool _has_loaded; +#ifdef HAVE_SLV2 + SharedPtr _lv2_info; +#endif +}; + + +} // namespace Ingen + +#endif // NODEFACTORY_H diff --git a/src/engine/NodeImpl.hpp b/src/engine/NodeImpl.hpp new file mode 100644 index 00000000..ac125c13 --- /dev/null +++ b/src/engine/NodeImpl.hpp @@ -0,0 +1,170 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef NODEIMPL_H +#define NODEIMPL_H + +#include +#include +#include "interface/Node.hpp" +#include "types.hpp" +#include "GraphObjectImpl.hpp" + +namespace Raul { template class List; class Maid; } + +namespace Ingen { + +namespace Shared { class Plugin; class Node; class Port; } + +using namespace Shared; + +class Buffer; +class PluginImpl; +class PatchImpl; +class PortImpl; +class MessageContext; + + +/** A Node (or "module") in a Patch (which is also a Node). + * + * A Node is a unit with input/output ports, a process() 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. + * + * \ingroup engine + */ +class NodeImpl : public GraphObjectImpl, virtual public Ingen::Shared::Node +{ +public: + NodeImpl(GraphObjectImpl* parent, const std::string& name, bool poly) + : GraphObjectImpl(parent, name, poly) + {} + + /** 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; + + /** Prepare for a new (external) polyphony value. + * + * Preprocessor thread, poly is actually applied by apply_poly. + * \return true on success. + */ + virtual bool prepare_poly(uint32_t poly) = 0; + + /** Apply a new (external) polyphony value. + * + * Audio thread. + * + * \param poly Must be < the most recent value passed to prepare_poly. + * \param maid Any objects no longer needed will be pushed to this + */ + virtual bool apply_poly(Raul::Maid& maid, uint32_t poly) = 0; + + /** Parallelism: Reset flags for start of a new cycle. + */ + virtual void reset_input_ready() = 0; + + /** Parallelism: Claim this node (to wait on its input). + * Only one thread will ever take this lock on a particular Node. + * \return true if lock was aquired, false otherwise + */ + virtual bool process_lock() = 0; + + /** Parallelism: Unclaim this node (let someone else wait on its input). + * Only a thread which successfully called process_lock may call this. + */ + virtual void process_unlock() = 0; + + /** Parallelism: Wait for signal that input is ready. + * Only a thread which successfully called process_lock may call this. + */ + virtual void wait_for_input(size_t num_providers) = 0; + + /** Parallelism: Signal that input is ready. Realtime safe. + * Calling this will wake up the thread which blocked on wait_for_input + * if there is one, and otherwise cause it to return true the next call. + * \return true if lock was aquired and input is ready, false otherwise + */ + virtual void signal_input_ready() = 0; + + /** Parallelism: Return the number of providers that have signalled. + */ + virtual unsigned n_inputs_ready() const = 0; + + /** Run the node for one instant in the message thread. + */ + virtual void message_process(MessageContext& context, uint32_t* output) = 0; + + /** Run the node for @a nframes input/output. + * + * @a start and @a end are transport times: end is not redundant in the case + * of varispeed, where end-start != nframes. + */ + virtual void process(ProcessContext& context) = 0; + + virtual void set_port_buffer(uint32_t voice, uint32_t port_num, Buffer* buf) = 0; + + virtual uint32_t num_ports() const = 0; + + virtual Port* port(uint32_t index) const = 0; + virtual PortImpl* port_impl(uint32_t index) 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 Raul::List* providers() = 0; + virtual void providers(Raul::List* l) = 0; + + /** Nodes are are connected to this Node's outputs. + * (They depend on this Node) + */ + virtual Raul::List* dependants() = 0; + virtual void dependants(Raul::List* l) = 0; + + /** The Patch this Node belongs to. */ + virtual PatchImpl* parent_patch() const = 0; + + /** Information about the Plugin this Node is an instance of. + * Not the best name - not all nodes come from plugins (ie Patch) + */ + virtual PluginImpl* plugin_impl() const = 0; + + /** Information about the Plugin this Node is an instance of. + * Not the best name - not all nodes come from plugins (ie Patch) + */ + virtual const Shared::Plugin* plugin() const = 0; + + virtual void set_buffer_size(size_t size) = 0; +}; + + +} // namespace Ingen + +#endif // NODEIMPL_H diff --git a/src/engine/OSCClientSender.cpp b/src/engine/OSCClientSender.cpp new file mode 100644 index 00000000..dca2e0ed --- /dev/null +++ b/src/engine/OSCClientSender.cpp @@ -0,0 +1,358 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "OSCClientSender.hpp" +#include +#include +#include +#include +#include "EngineStore.hpp" +#include "NodeFactory.hpp" +#include "util.hpp" +#include "PatchImpl.hpp" +#include "NodeImpl.hpp" +#include "PluginImpl.hpp" +#include "PortImpl.hpp" +#include "ConnectionImpl.hpp" +#include "AudioDriver.hpp" +#include "interface/ClientInterface.hpp" + +using namespace std; + +namespace Ingen { + + +/*! \page client_osc_namespace Client OSC Namespace Documentation + * + *

NOTE: this comment doesn't really apply any longer.. sort of. + * but maybe it still should.. maybe. so it remains...

+ * + *

These are all the messages sent from the engine to the client. + * Communication takes place over two distinct bands: control band and + * notification band.

+ *

The control band is where clients send commands, and receive a simple + * response, either OK or an error.

+ *

All notifications of engine state (ie new nodes) are sent over the + * notification band which is seperate from the control band. 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.

+ *

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).

+ * \n\n + */ + + +/** \page client_osc_namespace + * \n + *

Control Band

+ */ + +/** \page client_osc_namespace + *

\b /ingen/ok - Respond to a successful user command + * \arg \b response-id (int) - Request ID this is a response to + *

\n \n + */ +void +OSCClientSender::response_ok(int32_t id) +{ + if (!_enabled) + return; + + if (lo_send(_address, "/ingen/ok", "i", id) < 0) { + cerr << "Unable to send ok " << id << "! (" + << lo_address_errstr(_address) << ")" << endl; + } +} + + +/** \page client_osc_namespace + *

\b /ingen/response - Respond to a user command + * \arg \b response-id (int) - Request ID this is a response to + * \arg \b message (string) - Error message (natural language text) + *

\n \n + */ +void +OSCClientSender::response_error(int32_t id, const std::string& msg) +{ + if (!_enabled) + return; + + if (lo_send(_address, "/ingen/error", "is", id, msg.c_str()) < 0) { + cerr << "Unable to send error " << id << "! (" + << lo_address_errstr(_address) << ")" << endl; + } +} + + +/** \page client_osc_namespace + * \n + *

Notification Band

+ */ + + +/** \page client_osc_namespace + *

\b /ingen/error - Notification that an error has occurred + * \arg \b message (string) - Error message (natural language text) \n\n + * \li This is for notification of errors that aren't a direct response to a + * user command, ie "unexpected" errors.

\n \n + */ +void +OSCClientSender::error(const std::string& msg) +{ + send("/ingen/error", "s", msg.c_str(), LO_ARGS_END); +} + + +/** \page client_osc_namespace + *

\b /ingen/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 (boolean) - Node is polyphonic\n\n + * \li New nodes are sent as a bundle. The first message in the bundle will be + * this one (/ingen/new_node), followed by a series of /ingen/new_port commands, + * followed by /ingen/new_node_end.

\n \n + */ +void OSCClientSender::new_node(const std::string& node_path, + const std::string& plugin_uri) +{ + send("/ingen/new_node", "ss", node_path.c_str(), plugin_uri.c_str(), LO_ARGS_END); +} + + + +/** \page client_osc_namespace + *

\b /ingen/new_port - Notification of a new port's creation. + * \arg \b path (string) - Path of new port + * \arg \b index (integer) - Index (or sort key) of port on parent + * \arg \b data-type (string) - Type of port (ingen:AudioPort, ingen:ControlPort, ingen:MIDIPort, or ingen:OSCPort) + * \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 variable.

\n \n + */ +void +OSCClientSender::new_port(const std::string& path, + uint32_t index, + const std::string& data_type, + bool is_output) +{ + send("/ingen/new_port", "sisi", path.c_str(), index, data_type.c_str(), is_output, LO_ARGS_END); +} + + +/** \page client_osc_namespace + *

\b /ingen/destroyed - Notification an object has been destroyed + * \arg \b path (string) - Path of object (which no longer exists)

\n \n + */ +void +OSCClientSender::destroy(const std::string& path) +{ + assert(path != "/"); + + send("/ingen/destroyed", "s", path.c_str(), LO_ARGS_END); +} + + +/** \page client_osc_namespace + *

\b /ingen/patch_cleared - Notification a patch has been cleared (all children destroyed) + * \arg \b path (string) - Path of patch (which is now empty)

\n \n + */ +void +OSCClientSender::patch_cleared(const std::string& patch_path) +{ + send("/ingen/patch_cleared", "s", patch_path.c_str(), LO_ARGS_END); +} + + +/** \page client_osc_namespace + *

\b /ingen/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

\n \n + */ +void +OSCClientSender::connect(const std::string& src_port_path, const std::string& dst_port_path) +{ + send("/ingen/new_connection", "ss", src_port_path.c_str(), dst_port_path.c_str(), LO_ARGS_END); +} + + +/** \page client_osc_namespace + *

\b /ingen/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

\n \n + */ +void +OSCClientSender::disconnect(const std::string& src_port_path, const std::string& dst_port_path) +{ + send("/ingen/disconnection", "ss", src_port_path.c_str(), dst_port_path.c_str(), LO_ARGS_END); +} + + +/** \page client_osc_namespace + *

\b /ingen/set_variable - Notification of a variable. + * \arg \b path (string) - Path of the object associated with variable (node, patch, or port) + * \arg \b key (string) + * \arg \b value (string)

\n \n + */ +void +OSCClientSender::set_variable(const std::string& path, const std::string& key, const Atom& value) +{ + lo_message m = lo_message_new(); + lo_message_add_string(m, path.c_str()); + lo_message_add_string(m, key.c_str()); + Raul::AtomLiblo::lo_message_add_atom(m, value); + send_message("/ingen/set_variable", m); +} + + +/** \page client_osc_namespace + *

\b /ingen/set_property - Notification of a property. + * \arg \b path (string) - Path of the object associated with property (node, patch, or port) + * \arg \b key (string) + * \arg \b value (string)

\n \n + */ +void +OSCClientSender::set_property(const std::string& path, const std::string& key, const Atom& value) +{ + lo_message m = lo_message_new(); + lo_message_add_string(m, path.c_str()); + lo_message_add_string(m, key.c_str()); + Raul::AtomLiblo::lo_message_add_atom(m, value); + send_message("/ingen/set_property", m); +} + + +/** \page client_osc_namespace + *

\b /ingen/set_port_value - Notification the value of a port has changed + * \arg \b path (string) - Path of port + * \arg \b value (any) - New value of port

\n \n + */ +void +OSCClientSender::set_port_value(const std::string& port_path, const Raul::Atom& value) +{ + lo_message m = lo_message_new(); + lo_message_add_string(m, port_path.c_str()); + Raul::AtomLiblo::lo_message_add_atom(m, value); + send_message("/ingen/set_port_value", m); +} + + +/** \page client_osc_namespace + *

\b /ingen/set_port_value - Notification the value of a port has changed + * \arg \b path (string) - Path of port + * \arg \b voice (int) - Voice which is set to this value + * \arg \b value (any) - New value of port

\n \n + */ +void +OSCClientSender::set_voice_value(const std::string& port_path, uint32_t voice, const Raul::Atom& value) +{ + lo_message m = lo_message_new(); + lo_message_add_string(m, port_path.c_str()); + Raul::AtomLiblo::lo_message_add_atom(m, value); + send_message("/ingen/set_port_value", m); +} + + +/** \page client_osc_namespace + *

\b /ingen/port_activity - Notification of activity for a port (e.g. MIDI messages) + * \arg \b path (string) - Path of port

\n \n + */ +void +OSCClientSender::port_activity(const std::string& port_path) +{ + if (!_enabled) + return; + + lo_send(_address, "/ingen/port_activity", "s", port_path.c_str(), LO_ARGS_END); +} + + +/** \page client_osc_namespace + *

\b /ingen/plugin - Notification of the existance of a plugin + * \arg \b uri (string) - URI of plugin (e.g. http://example.org/filtermatic) + * \arg \b type (string) - Type of plugin (e.g. "ingen:LV2Plugin") + * \arg \b symbol (string) - Valid symbol for plugin (default symbol for nodes) (e.g. "adsr") + * \arg \b name (string) - Descriptive human-readable name of plugin (e.g. "ADSR Envelope") + */ +void +OSCClientSender::new_plugin(const std::string& uri, + const std::string& type_uri, + const std::string& symbol, + const std::string& name) +{ + lo_message m = lo_message_new(); + lo_message_add_string(m, uri.c_str()); + lo_message_add_string(m, type_uri.c_str()); + lo_message_add_string(m, symbol.c_str()); + lo_message_add_string(m, name.c_str()); + send_message("/ingen/plugin", m); +} + + +/** \page client_osc_namespace + *

\b /ingen/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)

\n \n + */ +void +OSCClientSender::new_patch(const std::string& path, uint32_t poly) +{ + send("/ingen/new_patch", "si", path.c_str(), poly, LO_ARGS_END); +} + + +/** \page client_osc_namespace + *

\b /ingen/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

\n \n + */ +void +OSCClientSender::object_renamed(const std::string& old_path, const std::string& new_path) +{ + send("/ingen/object_renamed", "ss", old_path.c_str(), new_path.c_str(), LO_ARGS_END); +} + + +/** Sends information about a program associated with a node. + */ +void +OSCClientSender::program_add(const std::string& node_path, uint32_t bank, uint32_t program, const std::string& name) +{ + send("/ingen/program_add", "siis", + node_path.c_str(), bank, program, name.c_str(), LO_ARGS_END); +} + + +void +OSCClientSender::program_remove(const std::string& node_path, uint32_t bank, uint32_t program) +{ + send("/ingen/program_remove", "sii", + node_path.c_str(), bank, program, LO_ARGS_END); +} + + +} // namespace Ingen diff --git a/src/engine/OSCClientSender.hpp b/src/engine/OSCClientSender.hpp new file mode 100644 index 00000000..c8c8418f --- /dev/null +++ b/src/engine/OSCClientSender.hpp @@ -0,0 +1,136 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef OSCCLIENTSENDER_H +#define OSCCLIENTSENDER_H + +#include +#include +#include +#include +#include +#include "types.hpp" +#include "interface/ClientInterface.hpp" +#include "shared/OSCSender.hpp" + +namespace Ingen { + +namespace Shared { class EngineInterface; } + + +/** Implements ClientInterface for OSC clients (sends OSC messages). + * + * \ingroup engine + */ +class OSCClientSender : public Shared::ClientInterface, public Shared::OSCSender +{ +public: + OSCClientSender(const std::string& url) + : _url(url) + { + _address = lo_address_new_from_url(url.c_str()); + } + + virtual ~OSCClientSender() + { lo_address_free(_address); } + + bool enabled() const { return _enabled; } + + void enable() { _enabled = true; } + void disable() { _enabled = false; } + + void bundle_begin() { OSCSender::bundle_begin(); } + void bundle_end() { OSCSender::bundle_end(); } + void transfer_begin() { OSCSender::transfer_begin(); } + void transfer_end() { OSCSender::transfer_end(); } + + std::string uri() const { return lo_address_get_url(_address); } + + void subscribe(Shared::EngineInterface* engine) { } + + /* *** ClientInterface Implementation Below *** */ + + //void client_registration(const std::string& url, int client_id); + + void response_ok(int32_t id); + void response_error(int32_t id, const std::string& msg); + + void error(const std::string& msg); + + virtual void new_plugin(const std::string& uri, + const std::string& type_uri, + const std::string& symbol, + const std::string& name); + + virtual void new_patch(const std::string& path, uint32_t poly); + + virtual void new_node(const std::string& path, + const std::string& plugin_uri); + + virtual void new_port(const std::string& path, + uint32_t index, + const std::string& data_type, + bool is_output); + + virtual void patch_cleared(const std::string& path); + + virtual void destroy(const std::string& path); + + virtual void object_renamed(const std::string& old_path, + const std::string& new_path); + + virtual void connect(const std::string& src_port_path, + const std::string& dst_port_path); + + virtual void disconnect(const std::string& src_port_path, + const std::string& dst_port_path); + + virtual void set_variable(const std::string& subject_path, + const std::string& predicate, + const Raul::Atom& value); + + virtual void set_property(const std::string& subject_path, + const std::string& predicate, + const Raul::Atom& value); + + virtual void set_port_value(const std::string& port_path, + const Raul::Atom& value); + + virtual void set_voice_value(const std::string& port_path, + uint32_t voice, + const Raul::Atom& value); + + virtual void port_activity(const std::string& port_path); + + virtual void program_add(const std::string& node_path, + uint32_t bank, + uint32_t program, + const std::string& program_name); + + virtual void program_remove(const std::string& node_path, + uint32_t bank, + uint32_t program); + +private: + std::string _url; +}; + + +} // namespace Ingen + +#endif // OSCCLIENTSENDER_H + diff --git a/src/engine/OSCDriver.hpp b/src/engine/OSCDriver.hpp new file mode 100644 index 00000000..06125217 --- /dev/null +++ b/src/engine/OSCDriver.hpp @@ -0,0 +1,82 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef OSCDRIVER_H +#define OSCDRIVER_H + +#include "types.hpp" +#include "Driver.hpp" +#include + +namespace Ingen { + + +/** OSC driver abstract base class. + * + * \ingroup engine + */ +class OSCDriver : public Driver +{ +public: + OSCDriver() : Driver(DataType::EVENT) {} + + /** Prepare events (however neccessary) for the specified block (realtime safe) */ + virtual void prepare_block(const SampleCount block_start, const SampleCount block_end) = 0; +}; + + + +/** Dummy OSCDriver. + * + * Not abstract, all functions are dummies. One of these will be allocated and + * "used" if no working OSC driver is loaded. (Doing it this way as opposed to + * just making OSCDriver have dummy functions makes sure any existing OSCDriver + * derived class actually implements the required functions). + * + * \ingroup engine + */ +class DummyOSCDriver : public OSCDriver +{ +public: + DummyOSCDriver() { + std::cout << "[DummyOSCDriver] Started Dummy OSC driver." << std::endl; + } + + ~DummyOSCDriver() {} + + void activate() {} + void deactivate() {} + + bool is_activated() const { return false; } + bool is_enabled() const { return false; } + + void enable() {} + void disable() {} + + DriverPort* create_port(DuplexPort* patch_port) { return NULL; } + + void add_port(DriverPort* port) {} + DriverPort* remove_port(const Raul::Path& path) { return NULL; } + + void prepare_block(const SampleCount block_start, const SampleCount block_end) {} +}; + + + +} // namespace Ingen + +#endif // OSCDRIVER_H diff --git a/src/engine/OSCEngineReceiver.cpp b/src/engine/OSCEngineReceiver.cpp new file mode 100644 index 00000000..e4e2fec1 --- /dev/null +++ b/src/engine/OSCEngineReceiver.cpp @@ -0,0 +1,884 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +//#define ENABLE_AVAHI 1 +#include +#include "types.hpp" +#include +#include +#include "interface/ClientInterface.hpp" +#include "engine/ThreadManager.hpp" +#include "OSCEngineReceiver.hpp" +#include "QueuedEventSource.hpp" +#include "OSCClientSender.hpp" +#include "ClientBroadcaster.hpp" + +using namespace std; + +namespace Ingen { + + +/*! \page engine_osc_namespace Engine OSC Namespace Documentation + * + *

These are the commands the engine recognizes. A client can control every + * aspect of the engine entirely with these commands.

+ * + *

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.

\n\n + */ + + +OSCEngineReceiver::OSCEngineReceiver(Engine& engine, size_t queue_size, uint16_t port) + : QueuedEngineInterface(engine, queue_size, queue_size) // FIXME + , _server(NULL) +{ + _receive_thread = new ReceiveThread(*this); + + char port_str[6]; + snprintf(port_str, 6, "%u", port); + + _server = lo_server_new(port_str, error_cb); +#ifdef ENABLE_AVAHI + lo_server_avahi_init(_server, "ingen"); +#endif + + if (_server == NULL) { + cerr << "[OSC] Could not start OSC server. Aborting." << endl; + exit(EXIT_FAILURE); + } else { + char* lo_url = lo_server_get_url(_server); + cout << "[OSC] Started OSC server at " << lo_url << endl; + free(lo_url); + } + + // For debugging, print all incoming OSC messages + //lo_server_add_method(_server, NULL, NULL, generic_cb, NULL); + + // Set response address for this message. + // It's important this is first and returns nonzero. + lo_server_add_method(_server, NULL, NULL, set_response_address_cb, this); + + // Commands + lo_server_add_method(_server, "/ingen/ping", "i", ping_cb, this); + lo_server_add_method(_server, "/ingen/ping_queued", "i", ping_slow_cb, this); + lo_server_add_method(_server, "/ingen/quit", "i", quit_cb, this); + //lo_server_add_method(_server, "/ingen/register_client", "is", register_client_cb, this); + lo_server_add_method(_server, "/ingen/register_client", "i", register_client_cb, this); + lo_server_add_method(_server, "/ingen/unregister_client", "i", unregister_client_cb, this); + lo_server_add_method(_server, "/ingen/load_plugins", "i", load_plugins_cb, this); + lo_server_add_method(_server, "/ingen/activate", "i", engine_activate_cb, this); + lo_server_add_method(_server, "/ingen/deactivate", "i", engine_deactivate_cb, this); + lo_server_add_method(_server, "/ingen/new_patch", "isi", new_patch_cb, this); + lo_server_add_method(_server, "/ingen/clear_patch", "is", clear_patch_cb, this); + lo_server_add_method(_server, "/ingen/set_polyphony", "isi", set_polyphony_cb, this); + lo_server_add_method(_server, "/ingen/set_polyphonic", "isT", set_polyphonic_cb, this); + lo_server_add_method(_server, "/ingen/set_polyphonic", "isF", set_polyphonic_cb, this); + lo_server_add_method(_server, "/ingen/new_port", "issi", new_port_cb, this); + lo_server_add_method(_server, "/ingen/new_node", "issss", new_node_cb, this); + lo_server_add_method(_server, "/ingen/new_node", "issss", new_node_cb, this); + lo_server_add_method(_server, "/ingen/new_node", "iss", new_node_by_uri_cb, this); + lo_server_add_method(_server, "/ingen/new_node", "iss", new_node_by_uri_cb, this); + lo_server_add_method(_server, "/ingen/destroy", "is", destroy_cb, this); + lo_server_add_method(_server, "/ingen/rename", "iss", rename_cb, this); + lo_server_add_method(_server, "/ingen/connect", "iss", connect_cb, this); + lo_server_add_method(_server, "/ingen/disconnect", "iss", disconnect_cb, this); + lo_server_add_method(_server, "/ingen/disconnect_all", "iss", disconnect_all_cb, this); + lo_server_add_method(_server, "/ingen/set_port_value", NULL, set_port_value_cb, this); + lo_server_add_method(_server, "/ingen/note_on", "isii", note_on_cb, this); + lo_server_add_method(_server, "/ingen/note_off", "isi", note_off_cb, this); + lo_server_add_method(_server, "/ingen/all_notes_off", "isi", all_notes_off_cb, this); + lo_server_add_method(_server, "/ingen/midi_learn", "is", midi_learn_cb, this); + lo_server_add_method(_server, "/ingen/set_variable", NULL, variable_set_cb, this); + lo_server_add_method(_server, "/ingen/set_property", NULL, property_set_cb, this); + + // Queries + lo_server_add_method(_server, "/ingen/request_variable", "iss", variable_get_cb, this); + lo_server_add_method(_server, "/ingen/request_plugin", "is", request_plugin_cb, this); + lo_server_add_method(_server, "/ingen/request_object", "is", request_object_cb, this); + lo_server_add_method(_server, "/ingen/request_port_value", "is", request_port_value_cb, this); + lo_server_add_method(_server, "/ingen/request_plugins", "i", request_plugins_cb, this); + lo_server_add_method(_server, "/ingen/request_all_objects", "i", request_all_objects_cb, this); + + lo_server_add_method(_server, NULL, NULL, unknown_cb, NULL); + + Thread::set_name("OSC Pre-Processor"); +} + + +OSCEngineReceiver::~OSCEngineReceiver() +{ + deactivate(); + stop(); + _receive_thread->stop(); + delete _receive_thread; + + if (_server != NULL) { +#ifdef ENABLE_AVAHI + lo_server_avahi_free(_server); +#endif + lo_server_free(_server); + _server = NULL; + } +} + + +void +OSCEngineReceiver::activate() +{ + QueuedEventSource::activate(); + _receive_thread->set_name("OSC Receiver"); + _receive_thread->start(); + _receive_thread->set_scheduling(SCHED_FIFO, 5); // Jack default appears to be 10 +} + + +void +OSCEngineReceiver::deactivate() +{ + cout << "[OSCEngineReceiver] Stopped OSC listening thread" << endl; + _receive_thread->stop(); + QueuedEventSource::deactivate(); +} + + +/** Override the semaphore driven _run method of QueuedEngineInterface + * to wait on OSC messages and prepare them right away in the same thread. + */ +void +OSCEngineReceiver::ReceiveThread::_run() +{ + Thread::get().set_context(THREAD_PRE_PROCESS); + + /* get a timestamp here and stamp all the events with the same time so + * they all get executed in the same cycle */ + + while (true) { + assert(_receiver._server); + /*if ( ! _server) { + cout << "[OSCEngineReceiver] Server is NULL, exiting" << endl; + break; + }*/ + + // Wait on a message and enqueue it + lo_server_recv(_receiver._server); + + // Enqueue every other message that is here "now" + // (would this provide truly atomic bundles?) + while (lo_server_recv_noblock(_receiver._server, 0) > 0) + if (_receiver.unprepared_events()) + _receiver.whip(); + + // No more unprepared events + } +} + + +/** 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. + * Hooray for reference counting. + * + * 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. + * Lack of a fast liblo address comparison really sucks here, in any case. + */ +int +OSCEngineReceiver::set_response_address_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg, void* user_data) +{ + OSCEngineReceiver* const me = reinterpret_cast(user_data); + + if (argc < 1 || types[0] != 'i') // Not a valid Ingen message + return 0; // Save liblo the trouble + + const int32_t id = argv[0]->i; + + const lo_address addr = lo_message_get_source(msg); + char* const url = lo_address_get_url(addr); + + const SharedPtr r = me->_responder; + + /* Different address than last time, have to do a lookup */ + if (!r || !r->client() || strcmp(url, r->client()->uri().c_str())) { + ClientInterface* client = me->_engine.broadcaster()->client(url); + if (client) + me->_responder = SharedPtr(new Responder(client, id)); + else + me->_responder = SharedPtr(new Responder()); + } + + if (id != -1) { + me->set_next_response_id(id); + } else { + me->disable_responses(); + } + + // If this returns 0 no OSC commands will work + return 1; +} + + +void +OSCEngineReceiver::error_cb(int num, const char* msg, const char* path) +{ + cerr << "liblo server error " << num << " in path \"" << "\" - " << msg << endl; +} + + +/** \page engine_osc_namespace + *

\b /ingen/ping - Immediately sends a successful response to the given response id. + * \arg \b response-id (integer)

\n \n + */ +int +OSCEngineReceiver::_ping_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const lo_address addr = lo_message_get_source(msg); + if (lo_send(addr, "/ingen/ok", "i", argv[0]->i) < 0) + cerr << "WARNING: Unable to send response: " << lo_address_errstr(addr) << endl; + return 0; +} + + +/** \page engine_osc_namespace + *

\b /ingen/ping_queued - Sends response after going through the event queue. + * \arg \b response-id (integer) + * + * \li See the documentation for /ingen/set_port_value_queued for an explanation of how + * this differs from /ingen/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.

\n \n + */ +int +OSCEngineReceiver::_ping_slow_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + ping(); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /ingen/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.

\n \n + */ +int +OSCEngineReceiver::_quit_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + quit(); + return 0; +} + +/** \page engine_osc_namespace + *

\b /ingen/register_client - Registers a new client with the engine + * \arg \b response-id (integer) \n\n + * \li The incoming address will be used for the new registered client. If you + * want to register a different specific address, use the URL version.

\n \n + */ +int +OSCEngineReceiver::_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); + ClientInterface* client = new OSCClientSender((const char*)url); + register_client(client); + free(url); + + return 0; +} + + +/** \page engine_osc_namespace + *

\b /ingen/unregister_client - Unregisters a client + * \arg \b response-id (integer)

\n \n + */ +int +OSCEngineReceiver::_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(url); + free(url); + + return 0; +} + + +/** \page engine_osc_namespace + *

\b /ingen/load_plugins - Locates all available plugins, making them available for use. + * \arg \b response-id (integer)

\n \n + */ +int +OSCEngineReceiver::_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 + *

\b /ingen/activate - Activate the engine (MIDI, audio, everything) + * \arg \b response-id (integer)

+ * + * \li Note that you must send this message first if you want the engine to do + * anything at all - including respond to your messages! \n \n + */ +int +OSCEngineReceiver::_engine_activate_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + QueuedEngineInterface::activate(); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /ingen/deactivate - Deactivate the engine completely. + * \arg \b response-id (integer)

\n \n + */ +int +OSCEngineReceiver::_engine_deactivate_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + QueuedEngineInterface::deactivate(); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /ingen/new_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

\n \n + */ +int +OSCEngineReceiver::_new_patch_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* patch_path = &argv[1]->s; + const int32_t poly = argv[2]->i; + + new_patch(patch_path, poly); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /ingen/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

\n \n + */ +int +OSCEngineReceiver::_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 + *

\b /ingen/clear_patch - Remove all nodes from a patch + * \arg \b response-id (integer) + * \arg \b patch-path - Patch's path

\n \n + */ +int +OSCEngineReceiver::_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 + *

\b /ingen/set_polyphony - Set the polyphony of a patch + * \arg \b response-id (integer) + * \arg \b patch-path - Patch's path + * \arg \b poly (integer)

\n \n + */ +int +OSCEngineReceiver::_set_polyphony_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* patch_path = &argv[1]->s; + const uint32_t poly = argv[2]->i; + + set_polyphony(patch_path, poly); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /ingen/set_polyphonic - Toggle a node's or port's polyphonic mode + * \arg \b response-id (integer) + * \arg \b path - Object's path + * \arg \b polyphonic (bool)

\n \n + */ +int +OSCEngineReceiver::_set_polyphonic_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* object_path = &argv[1]->s; + bool polyphonic = (types[2] == 'T'); + + set_polyphonic(object_path, polyphonic); + return 0; +} + + +// FIXME: add index +/** \page engine_osc_namespace + *

\b /ingen/new_port - Add a port into a given patch (load a plugin by URI) + * \arg \b response-id (integer) + * \arg \b path (string) - Full path of the new port (ie. /patch2/subpatch/newport) + * \arg \b data-type (string) - Type of port (ingen:AudioPort, ingen:ControlPort, ingen:MIDIPort, or ingen:OSCPort) + * \arg \b direction ("is-output") (integer) - Direction of data flow (Input = 0, Output = 1)

\n \n + */ +int +OSCEngineReceiver::_new_port_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* port_path = &argv[1]->s; + const char* data_type = &argv[2]->s; + const int32_t direction = argv[3]->i; + + new_port(port_path, 0, data_type, (direction == 1)); + return 0; +} + +/** \page engine_osc_namespace + *

\b /ingen/new_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 plug-uri (string) - URI of the plugin to load \n \n + */ +int +OSCEngineReceiver::_new_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* plug_uri = &argv[2]->s; + + new_node(node_path, plug_uri); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /ingen/new_node - Add a node into a given patch (load a plugin by libname, label) \b DEPRECATED + * \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") \n \n + * + * \li This is only here to provide backwards compatibility for old patches that store LADSPA plugin + * references as libname, label. It is to be removed ASAP, don't use it. + *

\n \n + */ +int +OSCEngineReceiver::_new_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; + + new_node_deprecated(node_path, type, lib_name, plug_label); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /ingen/destroy - Removes (destroys) a Patch or a Node + * \arg \b response-id (integer) + * \arg \b node-path (string) - Full path of the object

\n \n + */ +int +OSCEngineReceiver::_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 + *

\b /ingen/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

\n \n + */ +int +OSCEngineReceiver::_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 + *

\b /ingen/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

\n \n + */ +int +OSCEngineReceiver::_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 + *

\b /ingen/disconnect_all - Disconnect all connections to/from a node/port. + * \arg \b response-id (integer) + * \arg \b patch-path (string) - The (parent) patch in which to disconnect object.

\n \n + * \arg \b node-path (string) - Full path of object.

\n \n + */ +int +OSCEngineReceiver::_disconnect_all_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* patch_path = &argv[1]->s; + const char* object_path = &argv[2]->s; + + disconnect_all(patch_path, object_path); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /ingen/set_port_value - 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.

\n \n + */ +int +OSCEngineReceiver::_set_port_value_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + if (argc < 3 || argc > 5 || strncmp(types, "is", 2)) + return 1; + + const char* port_path = &argv[1]->s; + + using Raul::Atom; + + if (!strcmp(types, "isf")) { // float, all voices + const float value = argv[2]->f; + set_port_value(port_path, Atom(value)); + } else if (!strcmp(types, "isif")) { // float, specific voice + const float value = argv[3]->f; + set_voice_value(port_path, argv[2]->i, Atom(value)); + } else if (!strcmp(types, "issb")) { // blob (event), all voices + const char* type = &argv[2]->s; + lo_blob b = argv[3]; + size_t data_size = lo_blob_datasize(b); + void* data = lo_blob_dataptr(b); + set_port_value(port_path, Atom(type, data_size, data)); + } else if (!strcmp(types, "isisb")) { // blob (event), specific voice + const char* type = &argv[3]->s; + lo_blob b = argv[4]; + size_t data_size = lo_blob_datasize(b); + void* data = lo_blob_dataptr(b); + set_voice_value(port_path, argv[2]->i, Atom(type, data_size, data)); + } else if (!strcmp(types, "issN")) { // empty event (type only), all voices + const char* type = &argv[2]->s; + set_port_value(port_path, Atom(type, 0, NULL)); + } else if (!strcmp(types, "isisN")) { // empty event (type only), specific voice + const char* type = &argv[3]->s; + set_voice_value(port_path, argv[2]->i, Atom(type, 0, NULL)); + } else { + return 1; + } + + return 0; +} + + +/** \page engine_osc_namespace + *

\b /ingen/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)

\n \n + */ +int +OSCEngineReceiver::_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 + *

\b /ingen/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)

\n \n + */ +int +OSCEngineReceiver::_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 + *

\b /ingen/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

\n \n + */ +int +OSCEngineReceiver::_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 + *

\b /ingen/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.

\n \n + */ +int +OSCEngineReceiver::_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; +} + + +/** \page engine_osc_namespace + *

\b /ingen/set_variable - Set a variable, associated with a synth-space object (node, etc) + * \arg \b response-id (integer) + * \arg \b object-path (string) - Full path of object to associate variable with + * \arg \b key (string) - Key (index/predicate/ID) for new variable + * \arg \b value (string) - Value of new variable

\n \n + */ +int +OSCEngineReceiver::_variable_set_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + if (argc != 4 || types[0] != 'i' || types[1] != 's' || types[2] != 's') + return 1; + + const char* object_path = &argv[1]->s; + const char* key = &argv[2]->s; + + Raul::Atom value = Raul::AtomLiblo::lo_arg_to_atom(types[3], argv[3]); + + set_variable(object_path, key, value); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /ingen/set_property - Set an (RDF) property, associated with a synth-space object (node, etc) + * \arg \b response-id (integer) + * \arg \b object-path (string) - Full path of object to associate variable with + * \arg \b key (string) - URI/QName for predicate of this property (e.g. "ingen:enabled") + * \arg \b value (string) - Value of property

\n \n + */ +int +OSCEngineReceiver::_property_set_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + if (argc != 4 || types[0] != 'i' || types[1] != 's' || types[2] != 's') + return 1; + + const char* object_path = &argv[1]->s; + const char* key = &argv[2]->s; + + Raul::Atom value = Raul::AtomLiblo::lo_arg_to_atom(types[3], argv[3]); + + set_property(object_path, key, value); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /ingen/request_variable - Requests the engine send a piece of variable, associated with a synth-space object (node, etc) + * \arg \b response-id (integer) + * \arg \b object-path (string) - Full path of object variable is associated with + * \arg \b key (string) - Key (index) for piece of variable + * + * \li Reply will be sent to client registered with the source address of this message.

\n \n + */ +int +OSCEngineReceiver::_variable_get_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* object_path = &argv[1]->s; + const char* key = &argv[2]->s; + + request_variable(object_path, key); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /ingen/request_plugin - 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 \n\n + * \li Reply will be sent to client registered with the source address of this message.

\n\n + */ +int +OSCEngineReceiver::_request_plugin_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* uri = &argv[1]->s; + + request_plugin(uri); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /ingen/request_object - 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 \n\n + * \li Reply will be sent to client registered with the source address of this message.

\n\n + */ +int +OSCEngineReceiver::_request_object_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* object_path = &argv[1]->s; + + request_object(object_path); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /ingen/request_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 \n\n + * \li Reply will be sent to client registered with the source address of this message.

\n\n + */ +int +OSCEngineReceiver::_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; +} + + +/** \page engine_osc_namespace + *

\b /ingen/request_plugins - Requests the engine send a list of all known plugins. + * \arg \b response-id (integer) \n\n + * \li Reply will be sent to client registered with the source address of this message.

\n\n + */ +int +OSCEngineReceiver::_request_plugins_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + request_plugins(); + return 0; +} + + +/** \page engine_osc_namespace + *

\b /ingen/request_all_objects - Requests the engine send information about \em all objects (patches, nodes, etc) + * \arg \b response-id (integer)\n\n + * \li Reply will be sent to client registered with the source address of this message.

\n \n + */ +int +OSCEngineReceiver::_request_all_objects_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + request_all_objects(); + return 0; +} + + +// Static Callbacks // + + +// Display incoming OSC messages (for debugging purposes) +int +OSCEngineReceiver::generic_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg, void* user_data) +{ + printf("[OSCMsg] %s (%s)\t", path, types); + + for (int i=0; i < argc; ++i) { + lo_arg_pp(lo_type(types[i]), argv[i]); + printf("\t"); + } + printf("\n"); + + return 1; // not handled +} + + +int +OSCEngineReceiver::unknown_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg, void* user_data) +{ + const lo_address addr = lo_message_get_source(msg); + char* const url = lo_address_get_url(addr); + + cerr << "Unknown command " << path << " (" << types << "), sending error.\n"; + + string error_msg = "Unknown command: "; + error_msg.append(path).append(" ").append(types); + + OSCClientSender(url).error(error_msg); + + return 0; +} + + +} // namespace Ingen diff --git a/src/engine/OSCEngineReceiver.hpp b/src/engine/OSCEngineReceiver.hpp new file mode 100644 index 00000000..c6e0bf59 --- /dev/null +++ b/src/engine/OSCEngineReceiver.hpp @@ -0,0 +1,129 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef OSCENGINERECEIVER_H +#define OSCENGINERECEIVER_H + +#include CONFIG_H_PATH +#include +#include +#include +#include +#include "QueuedEngineInterface.hpp" +#include "Responder.hpp" +using std::string; + +namespace Ingen { + +class JackDriver; +class NodeFactory; +class PatchImpl; + + +/* 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 _##name##_cb (LO_HANDLER_ARGS);\ +inline static int name##_cb(LO_HANDLER_ARGS, void* myself)\ +{ return ((OSCEngineReceiver*)myself)->_##name##_cb(path, types, argv, argc, msg); } + + +/* FIXME: Make this receive and preprocess in the same thread? */ + + +/** 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 should be implemented-as-a (privately inherit) + * QueuedEngineInterface, but it needs to be public so it's an EventSource + * the Driver can use. This probably should be fixed somehow.. + * + * \ingroup engine + */ +class OSCEngineReceiver : public QueuedEngineInterface +{ +public: + OSCEngineReceiver(Engine& engine, size_t queue_size, uint16_t port); + ~OSCEngineReceiver(); + + void activate(); + void deactivate(); + +private: + struct ReceiveThread : public Raul::Thread { + ReceiveThread(OSCEngineReceiver& receiver) : _receiver(receiver) {} + virtual void _run(); + private: + OSCEngineReceiver& _receiver; + }; + + friend class ReceiveThread; + + ReceiveThread* _receive_thread; + + static void error_cb(int num, const char* msg, const char* path); + static int set_response_address_cb(LO_HANDLER_ARGS, void* myself); + static int generic_cb(LO_HANDLER_ARGS, void* myself); + static int unknown_cb(LO_HANDLER_ARGS, void* myself); + + 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(new_patch); + LO_HANDLER(rename); + LO_HANDLER(new_port); + LO_HANDLER(new_node); + LO_HANDLER(new_node_by_uri); + LO_HANDLER(clear_patch); + LO_HANDLER(set_polyphony); + LO_HANDLER(set_polyphonic); + LO_HANDLER(destroy); + LO_HANDLER(connect); + LO_HANDLER(disconnect); + LO_HANDLER(disconnect_all); + LO_HANDLER(set_port_value); + LO_HANDLER(note_on); + LO_HANDLER(note_off); + LO_HANDLER(all_notes_off); + LO_HANDLER(midi_learn); + LO_HANDLER(variable_get); + LO_HANDLER(variable_set); + LO_HANDLER(property_set); + LO_HANDLER(request_plugin); + LO_HANDLER(request_object); + LO_HANDLER(request_port_value); + LO_HANDLER(request_variable); + LO_HANDLER(request_plugins); + LO_HANDLER(request_all_objects); + + lo_server _server; +}; + + +} // namespace Ingen + +#endif // OSCENGINERECEIVER_H diff --git a/src/engine/ObjectSender.cpp b/src/engine/ObjectSender.cpp new file mode 100644 index 00000000..688cea8e --- /dev/null +++ b/src/engine/ObjectSender.cpp @@ -0,0 +1,150 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "ObjectSender.hpp" +#include "interface/ClientInterface.hpp" +#include "EngineStore.hpp" +#include "PatchImpl.hpp" +#include "NodeImpl.hpp" +#include "PortImpl.hpp" +#include "PortImpl.hpp" +#include "ConnectionImpl.hpp" +#include "NodeFactory.hpp" +#include "interface/DataType.hpp" +#include "AudioBuffer.hpp" + +namespace Ingen { + + +void +ObjectSender::send_patch(ClientInterface* client, const PatchImpl* patch, bool recursive) +{ + client->bundle_begin(); + + client->new_patch(patch->path(), patch->internal_polyphony()); + client->set_property(patch->path(), "ingen:polyphonic", patch->polyphonic()); + + // Send variable + const GraphObjectImpl::Variables& data = patch->variables(); + for (GraphObjectImpl::Variables::const_iterator j = data.begin(); j != data.end(); ++j) + client->set_variable(patch->path(), (*j).first, (*j).second); + + client->set_property(patch->path(), "ingen:enabled", (bool)patch->enabled()); + + client->bundle_end(); + + if (recursive) { + + // Send nodes + for (List::const_iterator j = patch->nodes().begin(); + j != patch->nodes().end(); ++j) { + const NodeImpl* const node = (*j); + send_node(client, node, true); + } + + // Send ports + for (uint32_t i=0; i < patch->num_ports(); ++i) { + PortImpl* const port = patch->port_impl(i); + send_port(client, port); + } + + // Send connections + client->transfer_begin(); + for (PatchImpl::Connections::const_iterator j = patch->connections().begin(); + j != patch->connections().end(); ++j) + client->connect((*j)->src_port_path(), (*j)->dst_port_path()); + client->transfer_end(); + } +} + + +/** Sends a node or a patch */ +void +ObjectSender::send_node(ClientInterface* client, const NodeImpl* node, bool recursive) +{ + PluginImpl* const plugin = node->plugin_impl(); + + assert(node->path().length() > 0); + + if (plugin->type() == Plugin::Patch) { + send_patch(client, (PatchImpl*)node, recursive); + return; + } + + if (plugin->uri().length() == 0) { + cerr << "Node " << node->path() << " plugin has no URI! Not sending." << endl; + return; + } + + client->bundle_begin(); + + client->new_node(node->path(), node->plugin()->uri()); + client->set_property(node->path(), "ingen:polyphonic", node->polyphonic()); + + // Send variables + const GraphObjectImpl::Variables& data = node->variables(); + for (GraphObjectImpl::Variables::const_iterator j = data.begin(); j != data.end(); ++j) + client->set_variable(node->path(), (*j).first, (*j).second); + + // Send properties + const GraphObjectImpl::Properties& prop = node->properties(); + for (GraphObjectImpl::Properties::const_iterator j = prop.begin(); j != prop.end(); ++j) + client->set_property(node->path(), (*j).first, (*j).second); + + client->bundle_end(); + + if (recursive) { + // Send ports + for (size_t j=0; j < node->num_ports(); ++j) + send_port(client, node->port_impl(j)); + } +} + + +void +ObjectSender::send_port(ClientInterface* client, const PortImpl* port) +{ + assert(port); + + client->bundle_begin(); + + client->new_port(port->path(), port->index(), port->type().uri(), port->is_output()); + client->set_property(port->path(), "ingen:polyphonic", port->polyphonic()); + + // Send variable + const GraphObjectImpl::Variables& data = port->variables(); + for (GraphObjectImpl::Variables::const_iterator j = data.begin(); j != data.end(); ++j) + client->set_variable(port->path(), (*j).first, (*j).second); + + // Send properties + const GraphObjectImpl::Properties& prop = port->properties(); + for (GraphObjectImpl::Properties::const_iterator j = prop.begin(); j != prop.end(); ++j) + client->set_property(port->path(), (*j).first, (*j).second); + + // Send control value + if (port->type() == DataType::CONTROL) { + const Sample value = dynamic_cast(port->buffer(0))->value_at(0); + //cerr << port->path() << " sending default value " << default_value << endl; + client->set_port_value(port->path(), value); + } + + client->bundle_end(); +} + + +} // namespace Ingen + diff --git a/src/engine/ObjectSender.hpp b/src/engine/ObjectSender.hpp new file mode 100644 index 00000000..9b6eb000 --- /dev/null +++ b/src/engine/ObjectSender.hpp @@ -0,0 +1,57 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef OBJECTSENDER_H +#define OBJECTSENDER_H + +#include + +namespace Ingen { + +namespace Shared { + class ClientInterface; +} using Shared::ClientInterface; + +class PatchImpl; +class NodeImpl; +class PortImpl; +class PluginImpl; + + +/** Utility class for sending GraphObjects 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 serialiser, except to calls on ClientInterface rather than + * eg a byte stream. + */ +class ObjectSender { +public: + + // FIXME: Make all object parameters const + + static void send_patch(ClientInterface* client, const PatchImpl* patch, bool recursive); + static void send_node(ClientInterface* client, const NodeImpl* node, bool recursive); + static void send_port(ClientInterface* client, const PortImpl* port); +}; + +} // namespace Ingen + +#endif // OBJECTSENDER_H + diff --git a/src/engine/OmInProcess.cpp b/src/engine/OmInProcess.cpp new file mode 100644 index 00000000..6a65cf38 --- /dev/null +++ b/src/engine/OmInProcess.cpp @@ -0,0 +1,66 @@ +/* This file is part of Ingen. Copyright (C) 2006 Mario Lang. + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include "Engine.hpp" +#include "OSCReceiver.hpp" +#include "JackAudioDriver.hpp" + +extern "C" +{ + int jack_initialize(jack_client_t* client, const char* load_init); + void jack_finish(void* arg); +} + + +void* +run_main(void* arg) +{ + Engine::instance().main(); + + // FIXME: cleanup + + return 0; +} + + +pthread_t main_thread; + + +int +jack_initialize(jack_client_t* client, const char* load_init) +{ + if ((Ingen::om = new Engine(load_init, new Ingen::JackAudioDriver(client))) != NULL) { + pthread_create(&main_thread, NULL, run_main, NULL); + return 0; // Success + } else { + return 1; + } +} + + +void +jack_finish(void* arg) +{ + void* ret; + Engine::instance().quit(); + pthread_join(main_thread, &ret); +} + diff --git a/src/engine/OutputPort.cpp b/src/engine/OutputPort.cpp new file mode 100644 index 00000000..116d9a3e --- /dev/null +++ b/src/engine/OutputPort.cpp @@ -0,0 +1,62 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "OutputPort.hpp" +#include "Buffer.hpp" +#include "ProcessContext.hpp" + +using namespace std; + +namespace Ingen { + + +OutputPort::OutputPort(NodeImpl* parent, + const string& name, + uint32_t index, + uint32_t poly, + DataType type, + const Atom& value, + size_t buffer_size) + : PortImpl(parent, name, index, poly, type, value, buffer_size) +{ + if (type == DataType::CONTROL) + _broadcast = true; +} + + +void +OutputPort::pre_process(ProcessContext& context) +{ + for (uint32_t i=0; i < _poly; ++i) + buffer(i)->prepare_write(context.start(), context.nframes()); +} + + +void +OutputPort::post_process(ProcessContext& context) +{ + for (uint32_t i=0; i < _poly; ++i) + buffer(i)->prepare_read(context.start(), context.nframes()); + + //cerr << path() << " output post: buffer: " << buffer(0) << endl; + + broadcast(context); +} + + +} // namespace Ingen diff --git a/src/engine/OutputPort.hpp b/src/engine/OutputPort.hpp new file mode 100644 index 00000000..8d441b5c --- /dev/null +++ b/src/engine/OutputPort.hpp @@ -0,0 +1,63 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef OUTPUTPORT_H +#define OUTPUTPORT_H + +#include +#include +#include "PortImpl.hpp" +#include "types.hpp" + +namespace Ingen { + + +/** 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 + */ +class OutputPort : virtual public PortImpl +{ +public: + OutputPort(NodeImpl* parent, + const std::string& name, + uint32_t index, + uint32_t poly, + DataType type, + const Atom& value, + size_t buffer_size); + + void pre_process(ProcessContext& context); + void post_process(ProcessContext& context); + + virtual ~OutputPort() {} + + bool is_input() const { return false; } + bool is_output() const { return true; } +}; + + +} // namespace Ingen + +#endif // OUTPUTPORT_H diff --git a/src/engine/PatchImpl.cpp b/src/engine/PatchImpl.cpp new file mode 100644 index 00000000..9f0ae701 --- /dev/null +++ b/src/engine/PatchImpl.cpp @@ -0,0 +1,481 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include "ThreadManager.hpp" +#include "NodeImpl.hpp" +#include "PatchImpl.hpp" +#include "PatchPlugin.hpp" +#include "PortImpl.hpp" +#include "ConnectionImpl.hpp" +#include "DuplexPort.hpp" +#include "Engine.hpp" +#include "ProcessSlave.hpp" + +using namespace std; + +namespace Ingen { + + +PatchImpl::PatchImpl(Engine& engine, const string& path, uint32_t poly, PatchImpl* parent, SampleRate srate, size_t buffer_size, uint32_t internal_poly) + : NodeBase(new PatchPlugin("http://example.org/FIXME", "patch", "Ingen Patch"), + path, poly, parent, srate, buffer_size) + , _engine(engine) + , _internal_poly(internal_poly) + , _compiled_patch(NULL) + , _process(false) +{ + assert(internal_poly >= 1); +} + + +PatchImpl::~PatchImpl() +{ + assert(!_activated); + + delete _compiled_patch; +} + + +void +PatchImpl::activate() +{ + NodeBase::activate(); + + for (List::iterator i = _nodes.begin(); i != _nodes.end(); ++i) + (*i)->activate(); + + assert(_activated); +} + + +void +PatchImpl::deactivate() +{ + if (_activated) { + + NodeBase::deactivate(); + + for (List::iterator i = _nodes.begin(); i != _nodes.end(); ++i) { + if ((*i)->activated()) + (*i)->deactivate(); + assert(!(*i)->activated()); + } + } + assert(!_activated); +} + + +void +PatchImpl::disable() +{ + assert(ThreadManager::current_thread_id() == THREAD_PROCESS); + + _process = false; + + for (List::iterator i = _output_ports.begin(); i != _output_ports.end(); ++i) + (*i)->clear_buffers(); +} + + +bool +PatchImpl::prepare_internal_poly(uint32_t poly) +{ + assert(ThreadManager::current_thread_id() == THREAD_PRE_PROCESS); + + /* TODO: ports? internal/external poly? */ + + for (List::iterator i = _nodes.begin(); i != _nodes.end(); ++i) + (*i)->prepare_poly(poly); + + for (Connections::iterator i = _connections.begin(); i != _connections.end(); ++i) + ((ConnectionImpl*)i->get())->prepare_poly(poly); + + /* FIXME: Deal with failure */ + + return true; +} + + +bool +PatchImpl::apply_internal_poly(Raul::Maid& maid, uint32_t poly) +{ + assert(ThreadManager::current_thread_id() == THREAD_PROCESS); + + /* TODO: ports? internal/external poly? */ + + for (List::iterator i = _nodes.begin(); i != _nodes.end(); ++i) + (*i)->apply_poly(maid, poly); + + _internal_poly = poly; + + return true; +} + + +/** Run the patch for the specified number of frames. + * + * Calls all Nodes in (roughly, if parallel) the order _compiled_patch specifies. + */ +void +PatchImpl::process(ProcessContext& context) +{ + if (!_process) + return; + + NodeBase::pre_process(context); + + /*if (_ports) + for (size_t i=0; i < _ports->size(); ++i) + if (_ports->at(i)->is_input() && _ports->at(i)->type() == DataType::MIDI) + cerr << _ports->at(i)->path() << " " + << _ports->at(i)->buffer(0) << " # events: " + << ((MidiBuffer*)_ports->at(i)->buffer(0))->event_count() << endl;*/ + + /* Run */ + if (_compiled_patch && _compiled_patch->size() > 0) { + if (_engine.process_slaves().size() > 0) + process_parallel(context); + else + process_single(context); + } + + NodeBase::post_process(context); +} + + +void +PatchImpl::process_parallel(ProcessContext& context) +{ + size_t n_slaves = _engine.process_slaves().size(); + + CompiledPatch* const cp = _compiled_patch; + + /* Start p-1 slaves */ + + if (n_slaves >= cp->size()) + n_slaves = cp->size()-1; + + if (n_slaves > 0) { + for (size_t i=0; i < cp->size(); ++i) + (*cp)[i].node()->reset_input_ready(); + + for (size_t i=0; i < n_slaves; ++i) + _engine.process_slaves()[i]->whip(cp, i+1, context); + } + + + /* Process ourself until everything is done + * This is analogous to ProcessSlave::_whipped(), but this is the master + * (i.e. what the main Jack process thread calls). Where ProcessSlave + * waits on input, this just skips the node and tries the next, to avoid + * waiting in the Jack thread which pisses Jack off. + */ + + size_t index = 0; + size_t num_finished = 0; // Number of consecutive finished nodes hit + + while (num_finished < cp->size()) { + + CompiledNode& n = (*cp)[index]; + + if (n.node()->process_lock()) { + if (n.node()->n_inputs_ready() == n.n_providers()) { + n.node()->process(context); + + /* Signal dependants their input is ready */ + for (size_t i=0; i < n.dependants().size(); ++i) + n.dependants()[i]->signal_input_ready(); + + ++num_finished; + } else { + n.node()->process_unlock(); + num_finished = 0; + } + } else { + if (n.node()->n_inputs_ready() == n.n_providers()) + ++num_finished; + else + num_finished = 0; + } + + index = (index + 1) % cp->size(); + } + + /* Tell slaves we're done in case we beat them, and pray they're + * really done by the start of next cycle. + * FIXME: This probably breaks (race) at extremely small nframes where + * ingen is the majority of the DSP load. + */ + for (size_t i=0; i < n_slaves; ++i) + _engine.process_slaves()[i]->finish(); +} + + +void +PatchImpl::process_single(ProcessContext& context) +{ + CompiledPatch* const cp = _compiled_patch; + + for (size_t i=0; i < cp->size(); ++i) + (*cp)[i].node()->process(context); +} + + +void +PatchImpl::set_buffer_size(size_t size) +{ + NodeBase::set_buffer_size(size); + assert(_buffer_size == size); + + CompiledPatch* const cp = _compiled_patch; + + for (size_t i=0; i < cp->size(); ++i) + (*cp)[i].node()->set_buffer_size(size); +} + + +// Patch specific stuff + + +/** Add a node. + * Preprocessing thread only. + */ +void +PatchImpl::add_node(List::Node* ln) +{ + assert(ThreadManager::current_thread_id() == THREAD_PRE_PROCESS); + assert(ln != NULL); + assert(ln->elem() != NULL); + assert(ln->elem()->parent_patch() == this); + //assert(ln->elem()->polyphony() == _internal_poly); + + _nodes.push_back(ln); +} + + +/** Remove a node. + * Preprocessing thread only. + */ +PatchImpl::Nodes::Node* +PatchImpl::remove_node(const string& symbol) +{ + assert(ThreadManager::current_thread_id() == THREAD_PRE_PROCESS); + for (List::iterator i = _nodes.begin(); i != _nodes.end(); ++i) + if ((*i)->symbol() == symbol) + return _nodes.erase(i); + + return NULL; +} + + +/** Remove a connection. + * Preprocessing thread only. + */ +PatchImpl::Connections::Node* +PatchImpl::remove_connection(const PortImpl* src_port, const PortImpl* dst_port) +{ + assert(ThreadManager::current_thread_id() == THREAD_PRE_PROCESS); + bool found = false; + Connections::Node* connection = NULL; + for (Connections::iterator i = _connections.begin(); i != _connections.end(); ++i) { + ConnectionImpl* const c = (ConnectionImpl*)i->get(); + if (c->src_port() == src_port && c->dst_port() == dst_port) { + connection = _connections.erase(i); + found = true; + break; + } + } + + if ( ! found) + cerr << "WARNING: [PatchImpl::remove_connection] Connection not found !" << endl; + + return connection; +} + + +bool +PatchImpl::has_connection(const PortImpl* src_port, const PortImpl* dst_port) const +{ + // FIXME: Doesn't scale + for (Connections::const_iterator i = _connections.begin(); i != _connections.end(); ++i) { + ConnectionImpl* const c = (ConnectionImpl*)i->get(); + if (c->src_port() == src_port && c->dst_port() == dst_port) + return true; + } + + return false; +} + + +uint32_t +PatchImpl::num_ports() const +{ + ThreadID context = ThreadManager::current_thread_id(); + + if (context == THREAD_PROCESS) + return NodeBase::num_ports(); + else + return _input_ports.size() + _output_ports.size(); +} + + +/** Create a port. Not realtime safe. + */ +PortImpl* +PatchImpl::create_port(const string& name, DataType type, size_t buffer_size, bool is_output) +{ + if (type == DataType::UNKNOWN) { + cerr << "[PatchImpl::create_port] Unknown port type " << type.uri() << endl; + return NULL; + } + + assert( !(type == DataType::UNKNOWN) ); + + return new DuplexPort(this, name, num_ports(), _polyphony, type, Atom(), buffer_size, is_output); +} + + +/** Remove port from ports list used in pre-processing thread. + * + * Port is not removed from ports array for process thread (which could be + * simultaneously running). + * + * Realtime safe. Preprocessing thread only. + */ +List::Node* +PatchImpl::remove_port(const string& symbol) +{ + assert(ThreadManager::current_thread_id() == THREAD_PRE_PROCESS); + + bool found = false; + List::Node* ret = NULL; + for (List::iterator i = _input_ports.begin(); i != _input_ports.end(); ++i) { + if ((*i)->symbol() == symbol) { + ret = _input_ports.erase(i); + found = true; + } + } + + if (!found) + for (List::iterator i = _output_ports.begin(); i != _output_ports.end(); ++i) { + if ((*i)->symbol() == symbol) { + ret = _output_ports.erase(i); + found = true; + } + } + + if ( ! found) + cerr << "WARNING: [PatchImpl::remove_port] Port not found !" << endl; + + return ret; +} + + +/** Remove all ports from ports list used in pre-processing thread. + * + * Ports are not removed from ports array for process thread (which could be + * simultaneously running). Returned is a (inputs, outputs) pair. + * + * Realtime safe. Preprocessing thread only. + */ +void +PatchImpl::clear_ports() +{ + assert(ThreadManager::current_thread_id() == THREAD_PROCESS); + + _input_ports.clear(); + _output_ports.clear(); +} + + +Raul::Array* +PatchImpl::build_ports_array() const +{ + assert(ThreadManager::current_thread_id() == THREAD_PRE_PROCESS); + + Raul::Array* const result = new Raul::Array(_input_ports.size() + _output_ports.size()); + + size_t i = 0; + + for (List::const_iterator p = _input_ports.begin(); p != _input_ports.end(); ++p,++i) + result->at(i) = *p; + + for (List::const_iterator p = _output_ports.begin(); p != _output_ports.end(); ++p,++i) + result->at(i) = *p; + + return result; +} + + +/** 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). + * + * Not realtime safe. + */ +CompiledPatch* +PatchImpl::compile() const +{ + assert(ThreadManager::current_thread_id() == THREAD_PRE_PROCESS); + + //cerr << "*********** Building process order for " << path() << endl; + + CompiledPatch* const compiled_patch = new CompiledPatch();//_nodes.size()); + + for (Nodes::const_iterator i = _nodes.begin(); i != _nodes.end(); ++i) + (*i)->traversed(false); + + for (Nodes::const_iterator i = _nodes.begin(); i != _nodes.end(); ++i) { + NodeImpl* const node = (*i); + // Either a sink or connected to our output ports: + if ( ( ! node->traversed()) && node->dependants()->size() == 0) + compile_recursive(node, compiled_patch); + } + + // Traverse any nodes we didn't hit yet + for (Nodes::const_iterator i = _nodes.begin(); i != _nodes.end(); ++i) { + NodeImpl* const node = (*i); + if ( ! node->traversed()) + compile_recursive(node, compiled_patch); + } + + /*cerr << "----------------------------------------\n"; + for (size_t i=0; i < process_order->size(); ++i) { + assert(process_order->at(i)); + cerr << process_order->at(i)->path() << endl; + } + cerr << "----------------------------------------\n";*/ + + assert(compiled_patch->size() == _nodes.size()); + +#ifndef NDEBUG + for (size_t i=0; i < compiled_patch->size(); ++i) + assert(compiled_patch->at(i).node()); +#endif + + return compiled_patch; +} + + +} // namespace Ingen diff --git a/src/engine/PatchImpl.hpp b/src/engine/PatchImpl.hpp new file mode 100644 index 00000000..3629f6e5 --- /dev/null +++ b/src/engine/PatchImpl.hpp @@ -0,0 +1,168 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PATCHIMPL_H +#define PATCHIMPL_H + +#include +#include +#include +#include +#include "interface/DataType.hpp" +#include "interface/Patch.hpp" +#include "NodeBase.hpp" +#include "PluginImpl.hpp" +#include "CompiledPatch.hpp" + +using std::string; + +template class Array; +using Raul::List; + +namespace Ingen { + +namespace Shared { class Connection; } + +class ConnectionImpl; +class Engine; +class CompiledPatch; + + +/** 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 PatchImpl : public NodeBase, public Ingen::Shared::Patch +{ +public: + PatchImpl(Engine& engine, + const string& name, + uint32_t poly, + PatchImpl* parent, + SampleRate srate, + size_t buffer_size, + uint32_t local_poly); + + virtual ~PatchImpl(); + + void activate(); + void deactivate(); + + void process(ProcessContext& context); + + void set_buffer_size(size_t size); + + /** Prepare for a new (internal) polyphony value. + * + * Preprocessor thread, poly is actually applied by apply_internal_poly. + * \return true on success. + */ + bool prepare_internal_poly(uint32_t poly); + + /** Apply a new (internal) polyphony value. + * + * Audio thread. + * + * \param poly Must be < the most recent value passed to prepare_internal_poly. + * \param maid Any objects no longer needed will be pushed to this + */ + bool apply_internal_poly(Raul::Maid& maid, uint32_t poly); + + // Patch specific stuff not inherited from Node + + typedef List Nodes; + + void add_node(Nodes::Node* tn); + Nodes::Node* remove_node(const string& name); + + Nodes& nodes() { return _nodes; } + Connections& connections() { return _connections; } + + const Nodes& nodes() const { return _nodes; } + const Connections& connections() const { return _connections; } + + uint32_t num_ports() const; + + PortImpl* create_port(const string& name, DataType type, size_t buffer_size, bool is_output); + void add_input(List::Node* port) { _input_ports.push_back(port); } ///< Preprocesser thread + void add_output(List::Node* port) { _output_ports.push_back(port); } ///< Preprocessor thread + List::Node* remove_port(const string& name); + void clear_ports(); + + void add_connection(Connections::Node* c) { _connections.push_back(c); } + Connections::Node* remove_connection(const PortImpl* src_port, const PortImpl* dst_port); + + bool has_connection(const PortImpl* src_port, const PortImpl* dst_port) const; + + CompiledPatch* compiled_patch() { return _compiled_patch; } + void compiled_patch(CompiledPatch* cp) { _compiled_patch = cp; } + + Raul::Array* external_ports() { return _ports; } + void external_ports(Raul::Array* pa) { _ports = pa; } + + CompiledPatch* compile() const; + Raul::Array* build_ports_array() const; + + /** Whether to run this patch's DSP bits in the audio thread */ + bool enabled() const { return _process; } + void enable() { _process = true; } + void disable(); + + uint32_t internal_polyphony() const { return _internal_poly; } + +private: + inline void compile_recursive(NodeImpl* n, CompiledPatch* output) const; + void process_parallel(ProcessContext& context); + void process_single(ProcessContext& context); + + Engine& _engine; + uint32_t _internal_poly; + CompiledPatch* _compiled_patch; ///< Accessed in audio thread only + Connections _connections; ///< Accessed in preprocessing thread only + List _input_ports; ///< Accessed in preprocessing thread only + List _output_ports; ///< Accessed in preprocessing thread only + Nodes _nodes; ///< Accessed in preprocessing thread only + bool _process; +}; + + + +/** Private helper for compile */ +inline void +PatchImpl::compile_recursive(NodeImpl* n, CompiledPatch* output) const +{ + if (n == NULL || n->traversed()) + return; + + n->traversed(true); + assert(output != NULL); + + for (List::iterator i = n->providers()->begin(); i != n->providers()->end(); ++i) + if ( ! (*i)->traversed() ) + compile_recursive((*i), output); + + output->push_back(CompiledNode(n, n->providers()->size(), n->dependants())); +} + + +} // namespace Ingen + +#endif // PATCHIMPL_H diff --git a/src/engine/PatchPlugin.hpp b/src/engine/PatchPlugin.hpp new file mode 100644 index 00000000..a7334392 --- /dev/null +++ b/src/engine/PatchPlugin.hpp @@ -0,0 +1,64 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PATCHPLUGIN_H +#define PATCHPLUGIN_H + +#include CONFIG_H_PATH + +#include +#include "PluginImpl.hpp" + +namespace Ingen { + +class NodeImpl; + + +/** Implementation of a Patch plugin. + * + * Patches don't actually work like this yet... + */ +class PatchPlugin : public PluginImpl +{ +public: + PatchPlugin(const std::string& uri, + const std::string& symbol, + const std::string& name) + : PluginImpl(Plugin::Patch, uri) + {} + + NodeImpl* instantiate(const std::string& name, + bool polyphonic, + Ingen::PatchImpl* parent, + Engine& engine) + { + return NULL; + } + + const string symbol() const { return "patch"; } + const string name() const { return "Ingen Patch"; } + +private: + const string _symbol; + const string _name; +}; + + +} // namespace Ingen + +#endif // PATCHPLUGIN_H + diff --git a/src/engine/PluginImpl.cpp b/src/engine/PluginImpl.cpp new file mode 100644 index 00000000..215cf4ce --- /dev/null +++ b/src/engine/PluginImpl.cpp @@ -0,0 +1,54 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "PluginImpl.hpp" +#include "MidiNoteNode.hpp" +#include "MidiTriggerNode.hpp" +#include "MidiControlNode.hpp" +#include "TransportNode.hpp" + +using namespace std; + +namespace Ingen { + + +void +PluginImpl::load() +{ + if (!_module) { + //cerr << "Loading " << _library_path << " library" << endl; + _module = new Glib::Module(_library_path, Glib::MODULE_BIND_LOCAL); + if (!(*_module)) + delete _module; + } +} + + +void +PluginImpl::unload() +{ + if (_module) { + //cerr << "Unloading " << _library_path << endl; + delete _module; + _module = NULL; + } +} + + +} // namespace Ingen + diff --git a/src/engine/PluginImpl.hpp b/src/engine/PluginImpl.hpp new file mode 100644 index 00000000..0301d942 --- /dev/null +++ b/src/engine/PluginImpl.hpp @@ -0,0 +1,106 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PLUGINIMPL_H +#define PLUGINIMPL_H + +#include CONFIG_H_PATH + +#include +#include +#include +#include +#include +#include +#include "types.hpp" +#include "interface/Plugin.hpp" + +using std::string; +using Ingen::Shared::Plugin; + +namespace Ingen { + +class PatchImpl; +class NodeImpl; +class Engine; + + +/** Implementation of a plugin (internal code, or a loaded shared library). + * + * Conceptually, a Node is an instance of this. + */ +class PluginImpl : public Ingen::Shared::Plugin, public boost::noncopyable +{ +public: + PluginImpl(Type type, const string& uri, const string library_path="") + : _type(type) + , _uri(uri) + , _library_path(library_path) + , _module(NULL) + {} + + virtual NodeImpl* instantiate(const std::string& name, + bool polyphonic, + Ingen::PatchImpl* parent, + Engine& engine) = 0; + + virtual const string symbol() const = 0; + virtual const string name() const = 0; + + const std::string& library_path() const { return _library_path; } + void library_path(const std::string& s) { _library_path = s;} + + void load(); + void unload(); + + const char* type_string() const { + if (_type == LADSPA) return "LADSPA"; + else if (_type == LV2) return "LV2"; + else if (_type == Internal) return "Internal"; + else if (_type == Patch) return "Patch"; + else return ""; + } + + const string type_uri() const { + return string("ingen:").append(type_string()); + } + + void set_type(const string& type_string) { + if (type_string == "LADSPA") _type = LADSPA; + else if (type_string == "LV2") _type = LV2; + else if (type_string == "Internal") _type = Internal; + else if (type_string == "Patch") _type = Patch; + } + + Plugin::Type type() const { return _type; } + void type(Plugin::Type t) { _type = t; } + const string& uri() const { return _uri; } + Glib::Module* module() const { return _module; } + void module(Glib::Module* module) { _module = module; } + +protected: + Plugin::Type _type; + const string _uri; + string _library_path; + Glib::Module* _module; +}; + + +} // namespace Ingen + +#endif // PLUGINIMPL_H + diff --git a/src/engine/PortImpl.cpp b/src/engine/PortImpl.cpp new file mode 100644 index 00000000..3eac65ca --- /dev/null +++ b/src/engine/PortImpl.cpp @@ -0,0 +1,190 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include "PortImpl.hpp" +#include "NodeImpl.hpp" +#include "interface/DataType.hpp" +#include "AudioBuffer.hpp" +#include "EventBuffer.hpp" +#include "ProcessContext.hpp" +#include "SendPortActivityEvent.hpp" + +using namespace std; + +namespace Ingen { + + +PortImpl::PortImpl(NodeImpl* const node, + const string& name, + uint32_t index, + uint32_t poly, + DataType type, + const Atom& value, + size_t buffer_size) + : GraphObjectImpl(node, name, (type == DataType::AUDIO || type == DataType::CONTROL)) + , _index(index) + , _poly(poly) + , _buffer_size(buffer_size) + , _type(type) + , _value(value) + , _fixed_buffers(false) + , _broadcast(false) + , _set_by_user(false) + , _last_broadcasted_value(_value.type() == Atom::FLOAT ? _value.get_float() : 0.0f) // default? + , _context(Context::AUDIO) + , _buffers(new Raul::Array(poly)) +{ + assert(node != NULL); + assert(_poly > 0); + + allocate_buffers(); + clear_buffers(); + + if (node->parent() == NULL) + _polyphonic = false; + else + _polyphonic = true; + + if (type == DataType::EVENT) + _broadcast = true; // send activity blips + + assert(_buffers->size() > 0); +} + + +PortImpl::~PortImpl() +{ + for (uint32_t i=0; i < _poly; ++i) + delete _buffers->at(i); + + delete _buffers; +} + + +bool +PortImpl::set_polyphonic(Raul::Maid& maid, bool p) +{ + if (_type == DataType::CONTROL || _type == DataType::AUDIO) + return GraphObjectImpl::set_polyphonic(maid, p); + else + return (!p); +} + + +bool +PortImpl::prepare_poly(uint32_t poly) +{ + if (!_polyphonic || !_parent->polyphonic()) + return true; + + /* FIXME: poly never goes down, harsh on memory.. */ + if (poly > _poly) { + _prepared_buffers = new Raul::Array(poly, *_buffers); + for (uint32_t i = _poly; i < _prepared_buffers->size(); ++i) + _prepared_buffers->at(i) = Buffer::create(_type, _buffer_size); + } + + return true; +} + + +bool +PortImpl::apply_poly(Raul::Maid& maid, uint32_t poly) +{ + if (!_polyphonic || !_parent->polyphonic()) + return true; + + assert(poly <= _prepared_buffers->size()); + + // Apply a new set of buffers from a preceding call to prepare_poly + if (_prepared_buffers && _buffers != _prepared_buffers) { + maid.push(_buffers); + _buffers = _prepared_buffers; + } + + _poly = poly; + assert(_buffers->size() >= poly); + assert(this->poly() == poly); + + return true; +} + + +void +PortImpl::allocate_buffers() +{ + _buffers->alloc(_poly); + + for (uint32_t i=0; i < _poly; ++i) + _buffers->at(i) = Buffer::create(_type, _buffer_size); +} + + +void +PortImpl::set_buffer_size(size_t size) +{ + _buffer_size = size; + + for (uint32_t i=0; i < _poly; ++i) + _buffers->at(i)->resize(size); + + connect_buffers(); +} + + +void +PortImpl::connect_buffers() +{ + for (uint32_t i=0; i < _poly; ++i) + PortImpl::parent_node()->set_port_buffer(i, _index, buffer(i)); +} + + +void +PortImpl::clear_buffers() +{ + for (uint32_t i=0; i < _poly; ++i) + buffer(i)->clear(); +} + + +void +PortImpl::broadcast(ProcessContext& context) +{ + if (_broadcast) { + if (_type == DataType::CONTROL || _type == DataType::AUDIO) { + const Sample value = ((AudioBuffer*)buffer(0))->value_at(0); + if (value != _last_broadcasted_value) { + const SendPortValueEvent ev(context.engine(), context.start(), this, false, 0, value); + context.event_sink().write(sizeof(ev), &ev); + _last_broadcasted_value = value; + } + } else if (_type == DataType::EVENT) { + if (((EventBuffer*)buffer(0))->event_count() > 0) { + const SendPortActivityEvent ev(context.engine(), context.start(), this); + context.event_sink().write(sizeof(ev), &ev); + } + } + } +} + + + +} // namespace Ingen diff --git a/src/engine/PortImpl.hpp b/src/engine/PortImpl.hpp new file mode 100644 index 00000000..be96a910 --- /dev/null +++ b/src/engine/PortImpl.hpp @@ -0,0 +1,147 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PORTIMPL_H +#define PORTIMPL_H + +#include +#include +#include +#include "interface/Port.hpp" +#include "types.hpp" +#include "GraphObjectImpl.hpp" +#include "interface/DataType.hpp" +#include "Buffer.hpp" +#include "Context.hpp" + +namespace Raul { class Maid; class Atom; } + +namespace Ingen { + +class NodeImpl; +class Buffer; +class ProcessContext; + + +/** 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 PortImpl : public GraphObjectImpl, public Ingen::Shared::Port +{ +public: + virtual ~PortImpl(); + + /** A port's parent is always a node, so static cast should be safe */ + NodeImpl* parent_node() const { return (NodeImpl*)_parent; } + + bool set_polyphonic(Raul::Maid& maid, bool p); + + /** Prepare for a new (external) polyphony value. + * + * Preprocessor thread, poly is actually applied by apply_poly. + */ + virtual bool prepare_poly(uint32_t poly); + + /** Apply a new polyphony value. + * + * Audio thread. + * + * \param poly Must be < the most recent value passed to prepare_poly. + */ + virtual bool apply_poly(Raul::Maid& maid, uint32_t poly); + + const Raul::Atom& value() const { return _value; } + void set_value(const Raul::Atom& v) { _value = v; } + + inline Buffer* buffer(uint32_t voice) const { + Buffer* const buf = _buffers->at(voice); + if (buf->is_joined()) { + assert(buf->joined_buffer()); + return buf->joined_buffer(); + } else { + return buf; + } + } + + /** Called once per process cycle */ + virtual void pre_process(ProcessContext& context) = 0; + virtual void process(ProcessContext& context) {}; + virtual void post_process(ProcessContext& context) = 0; + + /** Empty buffer contents completely (ie silence) */ + virtual void clear_buffers(); + + virtual bool is_input() const = 0; + virtual bool is_output() const = 0; + + uint32_t index() const { return _index; } + uint32_t poly() const { return _poly; } + DataType type() const { return _type; } + size_t buffer_size() const { return _buffer_size; } + + virtual void set_buffer_size(size_t size); + + void fixed_buffers(bool b) { _fixed_buffers = b; } + bool fixed_buffers() { return _fixed_buffers; } + + void broadcast(bool b) { _broadcast = b; } + bool broadcast() { return _broadcast; } + + void raise_set_by_user_flag() { _set_by_user = true; } + + Context::ID context() const { return _context; } + void set_context(Context::ID c) { _context = c; } + +protected: + PortImpl(NodeImpl* node, + const std::string& name, + uint32_t index, + uint32_t poly, + DataType type, + const Raul::Atom& value, + size_t buffer_size); + + virtual void allocate_buffers(); + virtual void connect_buffers(); + virtual void broadcast(ProcessContext& context); + + uint32_t _index; + uint32_t _poly; + uint32_t _buffer_size; + DataType _type; + Raul::Atom _value; + bool _fixed_buffers; + bool _broadcast; + bool _set_by_user; + Sample _last_broadcasted_value; + + Context::ID _context; + Raul::Array* _buffers; + + // Dynamic polyphony + Raul::Array* _prepared_buffers; +}; + + +} // namespace Ingen + +#endif // PORTIMPL_H diff --git a/src/engine/PostProcessor.cpp b/src/engine/PostProcessor.cpp new file mode 100644 index 00000000..a50ca275 --- /dev/null +++ b/src/engine/PostProcessor.cpp @@ -0,0 +1,73 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +//#include +#include "events/SendPortValueEvent.hpp" +#include "Event.hpp" +#include "PostProcessor.hpp" +#include "Engine.hpp" +#include "AudioDriver.hpp" +#include "ProcessContext.hpp" + +using namespace std; + +namespace Ingen { + + +PostProcessor::PostProcessor(Engine& engine, size_t queue_size) + : _engine(engine) + , _max_time(0) + , _events(queue_size) + , _event_buffer_size(sizeof(SendPortValueEvent)) // FIXME: make generic + , _event_buffer((uint8_t*)malloc(_event_buffer_size)) +{ +} + + +void +PostProcessor::process() +{ + const FrameTime end_time = _max_time.get(); + + /* Process any audio thread generated events */ + /* FIXME: process events from all threads if parallel */ + + while (_engine.audio_driver()->context().event_sink().read( + _event_buffer_size, _event_buffer)) { + if (((Event*)_event_buffer)->time() > end_time) + break; // FIXME: loses event? + ((Event*)_event_buffer)->post_process(); + } + + /* Process normal events */ + while ( ! _events.empty()) { + Event* const ev = _events.front(); + if (ev->time() > end_time) + break; + _events.pop(); + assert(ev); + ev->post_process(); + delete ev; + } +} + + +} // namespace Ingen diff --git a/src/engine/PostProcessor.hpp b/src/engine/PostProcessor.hpp new file mode 100644 index 00000000..3d51136d --- /dev/null +++ b/src/engine/PostProcessor.hpp @@ -0,0 +1,73 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef POSTPROCESSOR_H +#define POSTPROCESSOR_H + +#include +#include "types.hpp" +#include +//#include + +//namespace Raul { class Maid; } + +namespace Ingen { + +class Event; +class Engine; + + +/** 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. + * + * Update: This is all run from main_iteration now to solve scripting + * thread issues. Not sure if this is permanent/ideal or not... + * + * \ingroup engine + */ +class PostProcessor //: public Raul::Slave +{ +public: + PostProcessor(Engine& engine, /*Raul::Maid& maid, */size_t queue_size); + + /** Push an event on to the process queue, realtime-safe, not thread-safe. */ + inline void push(Event* const ev) { _events.push(ev); } + + /** Post-process and delete all pending events */ + void process(); + + /** Set the latest event time that should be post-processed */ + void set_end_time(FrameTime time) { _max_time = time; } + +private: + Engine& _engine; + Raul::AtomicInt _max_time; + //Raul::Maid& _maid; + Raul::SRSWQueue _events; + uint32_t _event_buffer_size; + uint8_t* _event_buffer; + + //virtual void _whipped(); +}; + + +} // namespace Ingen + +#endif // POSTPROCESSOR_H diff --git a/src/engine/ProcessContext.hpp b/src/engine/ProcessContext.hpp new file mode 100644 index 00000000..57677126 --- /dev/null +++ b/src/engine/ProcessContext.hpp @@ -0,0 +1,71 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PROCESSCONTEXT_H +#define PROCESSCONTEXT_H + +#include "EventSink.hpp" +#include "Context.hpp" + +namespace Ingen { + + +/** Context of a process() call. + * + * This is used to pass whatever information a GraphObject might need to + * process in the audio thread, e.g. the available thread pool, sink for + * events (generated in the audio thread, not user initiated events), etc. + * + * Note the distinction between nframes and start/end. If transport speed + * != 1.0, end-start != nframes (though currently that is never the case, it + * may be in the future with sequencerey things). + * + * \ingroup engine + */ +class ProcessContext : public Context +{ +public: + ProcessContext(Engine& engine) + : Context(engine, AUDIO) + , _event_sink(engine, 1024) // FIXME: size? + {} + + void set_time_slice(SampleCount nframes, FrameTime start, FrameTime end) { + _nframes = nframes; + _start = start; + _end = end; + } + + inline SampleCount nframes() const { return _nframes; } + inline FrameTime start() const { return _start; } + inline FrameTime end() const { return _end; } + inline const EventSink& event_sink() const { return _event_sink; } + inline EventSink& event_sink() { return _event_sink; } + +private: + SampleCount _nframes; ///< Number of actual time (Jack) frames this cycle + FrameTime _start; ///< Start frame of this cycle, timeline relative + FrameTime _end; ///< End frame of this cycle, timeline relative + EventSink _event_sink; ///< Sink for events generated in the audio thread +}; + + + +} // namespace Ingen + +#endif // PROCESSCONTEXT_H + diff --git a/src/engine/ProcessSlave.cpp b/src/engine/ProcessSlave.cpp new file mode 100644 index 00000000..c7c868e8 --- /dev/null +++ b/src/engine/ProcessSlave.cpp @@ -0,0 +1,75 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "ProcessSlave.hpp" +#include "NodeImpl.hpp" +#include "CompiledPatch.hpp" + +using namespace std; + +namespace Ingen { + + +uint32_t ProcessSlave::_next_id = 0; + + +void +ProcessSlave::_whipped() +{ + assert(_compiled_patch); + CompiledPatch* const cp = _compiled_patch; + + /* Iterate over all nodes attempting to run immediately or block then run, + * until we've been through the entire array without getting a lock, + * and thus are finished this cycle. + */ + + size_t num_finished = 0; // Number of consecutive finished nodes hit + + while (_state == STATE_RUNNING) { + + CompiledNode& n = (*cp)[_index]; + + if (n.node()->process_lock()) { + + n.node()->wait_for_input(n.n_providers()); + + n.node()->process(_process_context); + + /* Signal dependants their input is ready */ + for (size_t i=0; i < n.dependants().size(); ++i) + n.dependants()[i]->signal_input_ready(); + + num_finished = 1; + } else { + ++num_finished; + } + + _index = (_index + 1) % cp->size(); + + if (num_finished >= cp->size()) + break; + } + + _index = 0; + _compiled_patch = NULL; + _state = STATE_FINISHED; +} + + +} // namespace Ingen diff --git a/src/engine/ProcessSlave.hpp b/src/engine/ProcessSlave.hpp new file mode 100644 index 00000000..40becd48 --- /dev/null +++ b/src/engine/ProcessSlave.hpp @@ -0,0 +1,100 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PROCESS_SLAVE_HPP +#define PROCESS_SLAVE_HPP + +#include CONFIG_H_PATH + +#include +#include +#include +#include +#include "ProcessContext.hpp" +#include "types.hpp" + +namespace Ingen { + +class NodeImpl; +class CompiledPatch; + + +class ProcessSlave : protected Raul::Slave { +public: + ProcessSlave(Engine& engine, bool realtime) + : _id(_next_id++) + , _index(0) + , _state(STATE_FINISHED) + , _compiled_patch(NULL) + , _process_context(engine) + { + std::stringstream ss; + ss << "Process Slave "; + ss << _id; + set_name(ss.str()); + + start(); + + if (realtime) + set_scheduling(SCHED_FIFO, 40); + } + + ~ProcessSlave() { + stop(); + } + + inline void whip(CompiledPatch* compiled_patch, uint32_t start_index, ProcessContext& context) { + assert(_state == STATE_FINISHED); + _index = start_index; + _state = STATE_RUNNING; + _compiled_patch = compiled_patch; + _process_context.set_time_slice(context.nframes(), context.start(), context.end()); + + Raul::Slave::whip(); + } + + inline void finish() { + while (_state.get() != STATE_FINISHED) + _state.compare_and_exchange(STATE_RUNNING, STATE_FINISH_SIGNALLED); + } + + inline uint32_t id() const { return _id; } + inline const ProcessContext& context() const { return _process_context; } + inline ProcessContext& context() { return _process_context; } + +private: + + void _whipped(); + + static uint32_t _next_id; + + static const int STATE_RUNNING = 0; + static const int STATE_FINISH_SIGNALLED = 1; + static const int STATE_FINISHED = 2; + + uint32_t _id; + uint32_t _index; + Raul::AtomicInt _state; + CompiledPatch* _compiled_patch; + ProcessContext _process_context; +}; + + +} // namespace Ingen + +#endif // PROCESS_SLAVE_HPP + diff --git a/src/engine/QueuedEngineInterface.cpp b/src/engine/QueuedEngineInterface.cpp new file mode 100644 index 00000000..962410b2 --- /dev/null +++ b/src/engine/QueuedEngineInterface.cpp @@ -0,0 +1,370 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "QueuedEngineInterface.hpp" +#include CONFIG_H_PATH +#include "QueuedEventSource.hpp" +#include "events.hpp" +#include "Engine.hpp" +#include "AudioDriver.hpp" + +namespace Ingen { + +QueuedEngineInterface::QueuedEngineInterface(Engine& engine, size_t queued_size, size_t stamped_size) + : QueuedEventSource(queued_size, stamped_size) + , _responder(new Responder(NULL, 0)) + , _engine(engine) + , _in_bundle(false) +{ +} + + +SampleCount +QueuedEngineInterface::now() const +{ + // Exactly one cycle latency (some could run ASAP if we get lucky, but not always, and a slight + // constant latency is far better than jittery lower (average) latency + assert(_engine.audio_driver()); + return _engine.audio_driver()->frame_time() + _engine.audio_driver()->buffer_size(); +} + + +void +QueuedEngineInterface::set_next_response_id(int32_t id) +{ + if (_responder) + _responder->set_id(id); +} + + +void +QueuedEngineInterface::disable_responses() +{ + _responder->set_client(NULL); + _responder->set_id(0); +} + + +/* *** EngineInterface implementation below here *** */ + + +void +QueuedEngineInterface::register_client(ClientInterface* client) +{ + push_queued(new RegisterClientEvent(_engine, _responder, now(), client->uri(), client)); + if (!_responder) { + _responder = SharedPtr(new Responder(client, 1)); + } else { + _responder->set_id(1); + _responder->set_client(client); + } +} + + +void +QueuedEngineInterface::unregister_client(const string& uri) +{ + push_queued(new UnregisterClientEvent(_engine, _responder, now(), uri)); + if (_responder && _responder->client() && _responder->client()->uri() == uri) { + _responder->set_id(0); + _responder->set_client(NULL); + } +} + + + +// Engine commands +void +QueuedEngineInterface::load_plugins() +{ + push_queued(new LoadPluginsEvent(_engine, _responder, now(), this)); +} + + +void +QueuedEngineInterface::activate() +{ + QueuedEventSource::activate(); + push_queued(new PingQueuedEvent(_engine, _responder, now())); +} + + +void +QueuedEngineInterface::deactivate() +{ + push_queued(new DeactivateEvent(_engine, _responder, now())); +} + + +void +QueuedEngineInterface::quit() +{ + _responder->respond_ok(); + _engine.quit(); +} + + +// Bundle commands + +void +QueuedEngineInterface::bundle_begin() +{ + _in_bundle = true; +} + + +void +QueuedEngineInterface::bundle_end() +{ + _in_bundle = false; +} + + +// Object commands + +void +QueuedEngineInterface::new_patch(const string& path, + uint32_t poly) +{ + push_queued(new CreatePatchEvent(_engine, _responder, now(), path, poly)); +} + + +// FIXME: use index +void QueuedEngineInterface::new_port(const string& path, + uint32_t index, + const string& data_type, + bool direction) +{ + push_queued(new CreatePortEvent(_engine, _responder, now(), path, data_type, direction, this)); +} + + +void +QueuedEngineInterface::new_node(const string& path, + const string& plugin_uri) +{ + push_queued(new CreateNodeEvent(_engine, _responder, now(), + path, plugin_uri, true)); // FIXME: polyphonic by default +} + + +void +QueuedEngineInterface::new_node_deprecated(const string& path, + const string& plugin_type, + const string& plugin_lib, + const string& plugin_label) +{ + push_queued(new CreateNodeEvent(_engine, _responder, now(), + path, plugin_type, plugin_lib, plugin_label, true)); // FIXME: polyphonic by default +} + +void +QueuedEngineInterface::rename(const string& old_path, + const string& new_symbol) +{ + push_queued(new RenameEvent(_engine, _responder, now(), old_path, new_symbol)); +} + + +void +QueuedEngineInterface::destroy(const string& path) +{ + push_queued(new DestroyEvent(_engine, _responder, now(), this, path)); +} + + +void +QueuedEngineInterface::clear_patch(const string& patch_path) +{ + push_queued(new ClearPatchEvent(_engine, _responder, now(), this, patch_path)); +} + + +void +QueuedEngineInterface::set_polyphony(const string& patch_path, uint32_t poly) +{ + push_queued(new SetPolyphonyEvent(_engine, _responder, now(), this, patch_path, poly)); +} + + +void +QueuedEngineInterface::set_polyphonic(const string& path, bool poly) +{ + push_queued(new SetPolyphonicEvent(_engine, _responder, now(), this, path, poly)); +} + + +void +QueuedEngineInterface::connect(const string& src_port_path, + const string& dst_port_path) +{ + push_queued(new ConnectionEvent(_engine, _responder, now(), src_port_path, dst_port_path)); + +} + + +void +QueuedEngineInterface::disconnect(const string& src_port_path, + const string& dst_port_path) +{ + push_queued(new DisconnectionEvent(_engine, _responder, now(), src_port_path, dst_port_path)); +} + + +void +QueuedEngineInterface::disconnect_all(const string& patch_path, + const string& node_path) +{ + push_queued(new DisconnectAllEvent(_engine, _responder, now(), patch_path, node_path)); +} + + +void +QueuedEngineInterface::set_port_value(const string& port_path, + const Raul::Atom& value) +{ + push_queued(new SetPortValueEvent(_engine, _responder, true, now(), port_path, value)); +} + + +void +QueuedEngineInterface::set_voice_value(const string& port_path, + uint32_t voice, + const Raul::Atom& value) +{ + push_queued(new SetPortValueEvent(_engine, _responder, true, now(), voice, port_path, value)); +} + + +void +QueuedEngineInterface::set_program(const string& node_path, + uint32_t bank, + uint32_t program) +{ + std::cerr << "FIXME: set program" << std::endl; +} + + +void +QueuedEngineInterface::midi_learn(const string& node_path) +{ + push_queued(new MidiLearnEvent(_engine, _responder, now(), node_path)); +} + + +void +QueuedEngineInterface::set_variable(const string& path, + const string& predicate, + const Atom& value) +{ + push_queued(new SetMetadataEvent(_engine, _responder, now(), false, path, predicate, value)); +} + + +void +QueuedEngineInterface::set_property(const string& path, + const string& predicate, + const Atom& value) +{ + // FIXME: implement generically + if (predicate == "ingen:enabled") { + if (value.type() == Atom::BOOL) { + push_queued(new EnablePatchEvent(_engine, _responder, now(), path, value.get_bool())); + return; + } + } else if (predicate == "ingen:polyphonic") { + if (value.type() == Atom::BOOL) { + push_queued(new SetPolyphonicEvent(_engine, _responder, now(), this, path, value.get_bool())); + return; + } + } else if (predicate == "ingen:polyphony") { + if (value.type() == Atom::INT) { + push_queued(new SetPolyphonyEvent(_engine, _responder, now(), this, path, value.get_int32())); + return; + } + } else { + push_queued(new SetMetadataEvent(_engine, _responder, now(), true, path, predicate, value)); + } +} + +// Requests // + +void +QueuedEngineInterface::ping() +{ + if (_engine.activated()) { + push_queued(new PingQueuedEvent(_engine, _responder, now())); + } else if (_responder) { + _responder->respond_ok(); + } +} + + +void +QueuedEngineInterface::request_plugin(const string& uri) +{ + push_queued(new RequestPluginEvent(_engine, _responder, now(), uri)); +} + + +void +QueuedEngineInterface::request_object(const string& path) +{ + push_queued(new RequestObjectEvent(_engine, _responder, now(), path)); +} + + +void +QueuedEngineInterface::request_port_value(const string& port_path) +{ + push_queued(new RequestPortValueEvent(_engine, _responder, now(), port_path)); +} + + +void +QueuedEngineInterface::request_variable(const string& object_path, const string& key) +{ + push_queued(new RequestMetadataEvent(_engine, _responder, now(), false, object_path, key)); +} + + +void +QueuedEngineInterface::request_property(const string& object_path, const string& key) +{ + push_queued(new RequestMetadataEvent(_engine, _responder, now(), true, object_path, key)); +} + + +void +QueuedEngineInterface::request_plugins() +{ + push_queued(new RequestPluginsEvent(_engine, _responder, now())); +} + + +void +QueuedEngineInterface::request_all_objects() +{ + push_queued(new RequestAllObjectsEvent(_engine, _responder, now())); +} + + +} // namespace Ingen + + diff --git a/src/engine/QueuedEngineInterface.hpp b/src/engine/QueuedEngineInterface.hpp new file mode 100644 index 00000000..73c790c8 --- /dev/null +++ b/src/engine/QueuedEngineInterface.hpp @@ -0,0 +1,170 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef QUEUEDENGINEINTERFACE_H +#define QUEUEDENGINEINTERFACE_H + +#include +#include +#include +#include +#include "interface/EngineInterface.hpp" +#include "interface/ClientInterface.hpp" +#include "Responder.hpp" +#include "QueuedEventSource.hpp" +#include "Engine.hpp" +using std::string; + +namespace Ingen { + +using Shared::ClientInterface; +using Shared::EngineInterface; +class Engine; + + +/** 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 QueuedEventSource, public EngineInterface +{ +public: + QueuedEngineInterface(Engine& engine, size_t queued_size, size_t stamped_size); + virtual ~QueuedEngineInterface() {} + + std::string uri() const { return "ingen:internal"; } + + void set_next_response_id(int32_t id); + + // Client registration + virtual void register_client(ClientInterface* client); + virtual void unregister_client(const string& uri); + + // Engine commands + virtual void load_plugins(); + virtual void activate(); + virtual void deactivate(); + virtual void quit(); + + // Bundles + virtual void bundle_begin(); + virtual void bundle_end(); + + // Object commands + + virtual void new_patch(const string& path, + uint32_t poly); + + virtual void new_port(const string& path, + uint32_t index, + const string& data_type, + bool direction); + + virtual void new_node(const string& path, + const string& plugin_uri); + + /** FIXME: DEPRECATED, REMOVE */ + virtual void new_node_deprecated(const string& path, + const string& plugin_type, + const string& lib_path, + const string& plug_label); + + 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 set_polyphony(const string& patch_path, uint32_t poly); + + virtual void set_polyphonic(const string& path, bool poly); + + 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& patch_path, + const string& node_path); + + virtual void set_port_value(const string& port_path, + const Raul::Atom& value); + + virtual void set_voice_value(const string& port_path, + uint32_t voice, + const Raul::Atom& value); + + 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_variable(const string& path, + const string& predicate, + const Raul::Atom& value); + + virtual void set_property(const string& path, + const string& predicate, + const Raul::Atom& value); + + // Requests // + + virtual void ping(); + virtual void request_plugin(const string& uri); + virtual void request_object(const string& path); + virtual void request_port_value(const string& port_path); + virtual void request_variable(const string& object_path, const string& key); + virtual void request_property(const string& object_path, const string& key); + virtual void request_plugins(); + virtual void request_all_objects(); + +protected: + + virtual void disable_responses(); + + SharedPtr _responder; ///< NULL if responding disabled + Engine& _engine; + bool _in_bundle; ///< True iff a bundle is currently being received + +private: + SampleCount now() const; +}; + + +} // namespace Ingen + +#endif // QUEUEDENGINEINTERFACE_H + diff --git a/src/engine/QueuedEvent.cpp b/src/engine/QueuedEvent.cpp new file mode 100644 index 00000000..8ed6fa02 --- /dev/null +++ b/src/engine/QueuedEvent.cpp @@ -0,0 +1,50 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "QueuedEvent.hpp" +#include "ThreadManager.hpp" +#include "ProcessContext.hpp" + +namespace Ingen { + + +void +QueuedEvent::pre_process() +{ + assert(ThreadManager::current_thread_id() == THREAD_PRE_PROCESS); + assert(_pre_processed == false); + _pre_processed = true; +} + + +void +QueuedEvent::execute(ProcessContext& context) +{ + assert(_pre_processed); + assert(_time <= context.end()); + + // Didn't prepare in time. QueuedEvents aren't (necessarily) sample accurate + // so just run at the beginning of this cycle + if (_time <= context.start()) + _time = context.start(); + + Event::execute(context); +} + + +} // namespace Ingen + diff --git a/src/engine/QueuedEvent.hpp b/src/engine/QueuedEvent.hpp new file mode 100644 index 00000000..e616d269 --- /dev/null +++ b/src/engine/QueuedEvent.hpp @@ -0,0 +1,83 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef QUEUEDEVENT_H +#define QUEUEDEVENT_H + +#include "Event.hpp" + +namespace Ingen { + +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(); + + virtual void execute(ProcessContext& context); + + /** If this event blocks the prepare phase of other slow events */ + bool is_blocking() { return _blocking; } + + bool is_prepared() { return _pre_processed; } + +protected: + QueuedEvent(Engine& engine, + SharedPtr responder, + FrameTime time, + bool blocking = false, + QueuedEventSource* source = NULL) + : Event(engine, responder, time) + , _pre_processed(false), _blocking(blocking), _source(source) + { + if (blocking) + assert(_source); + } + + // NULL event base (for internal events only!) + QueuedEvent(Engine& engine) + : Event(engine, SharedPtr(), 0) + , _pre_processed(false), _blocking(false), _source(NULL) + {} + + bool _pre_processed; + bool _blocking; + QueuedEventSource* _source; +}; + + +} // namespace Ingen + +#endif // QUEUEDEVENT_H diff --git a/src/engine/QueuedEventSource.cpp b/src/engine/QueuedEventSource.cpp new file mode 100644 index 00000000..69ab805a --- /dev/null +++ b/src/engine/QueuedEventSource.cpp @@ -0,0 +1,183 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "QueuedEventSource.hpp" +#include "QueuedEvent.hpp" +#include "PostProcessor.hpp" +#include "ThreadManager.hpp" +#include "ProcessContext.hpp" + +using namespace std; + +namespace Ingen { + + +QueuedEventSource::QueuedEventSource(size_t queued_size, size_t stamped_size) + : _front(0) + , _back(0) + , _prepared_back(0) + , _size(queued_size+1) + , _blocking_semaphore(0) + , _full_semaphore(0) + , _stamped_queue(stamped_size) +{ + _events = (QueuedEvent**)calloc(_size, sizeof(QueuedEvent*)); + + mlock(_events, _size * sizeof(QueuedEvent*)); + + Thread::set_context(THREAD_PRE_PROCESS); + assert(context() == THREAD_PRE_PROCESS); + + set_name("QueuedEventSource"); +} + + +QueuedEventSource::~QueuedEventSource() +{ + Thread::stop(); + + free(_events); +} + + +/** Push an unprepared event onto the queue. + */ +void +QueuedEventSource::push_queued(QueuedEvent* const ev) +{ + assert(!ev->is_prepared()); + + unsigned back = _back.get(); + bool full = (((_front.get() - back + _size) % _size) == 1); + while (full) { + whip(); + cerr << "WARNING: Event queue full. Waiting..." << endl; + _full_semaphore.wait(); + back = _back.get(); + full = (((_front.get() - back + _size) % _size) == 1); + } + + assert(_events[back] == NULL); + _events[back] = ev; + _back = (back + 1) % _size; + whip(); +} + + +/** Process all events for a cycle. + * + * Executed events will be pushed to @a dest. + */ +void +QueuedEventSource::process(PostProcessor& dest, ProcessContext& context) +{ + assert(ThreadManager::current_thread_id() == THREAD_PROCESS); + + Event* ev = NULL; + + /* Limit the maximum number of queued events to process per cycle. This + * makes the process callback (more) realtime-safe by preventing being + * choked by events coming in faster than they can be processed. + * FIXME: test this and figure out a good value */ + const unsigned int MAX_QUEUED_EVENTS = context.nframes() / 100; + + unsigned int num_events_processed = 0; + + /* FIXME: Merge these next two loops into one */ + + while ((ev = pop_earliest_queued_before(context.end()))) { + ev->execute(context); + dest.push(ev); + if (++num_events_processed > MAX_QUEUED_EVENTS) + break; + } + + while ((ev = pop_earliest_stamped_before(context.end()))) { + ev->execute(context); + dest.push(ev); + ++num_events_processed; + } + + if (_full_semaphore.has_waiter() && num_events_processed > 0) + _full_semaphore.post(); + + /*if (num_events_processed > 0) + dest.whip();*/ + //else + // cerr << "NO PROC: queued: " << unprepared_events() << ", stamped: " << !_stamped_queue.empty() << endl; +} + + +/** Pops the prepared event at the front of the prepare 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 QueuedEvents, but after this they are "normal" events and the + * engine deals with them just like a realtime in-band event. The engine will + * not use the timestamps of the returned events in any way, since it is free + * to execute these non-time-stamped events whenever it wants (at whatever rate + * it wants). + */ +Event* +QueuedEventSource::pop_earliest_queued_before(const SampleCount time) +{ + assert(ThreadManager::current_thread_id() == THREAD_PROCESS); + + const unsigned front = _front.get(); + QueuedEvent* const front_event = _events[front]; + + // Pop + if (front_event && front_event->is_prepared() && front_event->time() < time) { + _events[front] = NULL; + _front = (front + 1) % _size; + return front_event; + } else { + return NULL; + } +} + + +// Private // + + +/** Pre-process a single event */ +void +QueuedEventSource::_whipped() +{ + const unsigned prepared_back = _prepared_back.get(); + QueuedEvent* const ev = _events[prepared_back]; + if (!ev) + return; + + assert(!ev->is_prepared()); + ev->pre_process(); + assert(ev->is_prepared()); + + _prepared_back = (prepared_back + 1) % _size; + + // If event was blocking, wait for event to being run through the + // process thread before preparing the next event + if (ev->is_blocking()) + _blocking_semaphore.wait(); +} + + +} // namespace Ingen + diff --git a/src/engine/QueuedEventSource.hpp b/src/engine/QueuedEventSource.hpp new file mode 100644 index 00000000..6dea092d --- /dev/null +++ b/src/engine/QueuedEventSource.hpp @@ -0,0 +1,128 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef QUEUEDEVENTSOURCE_H +#define QUEUEDEVENTSOURCE_H + +#include +#include +#include "types.hpp" +#include +#include +#include +#include +#include "Event.hpp" +#include "EventSource.hpp" + +using Raul::AtomicInt; + +namespace Ingen { + +class QueuedEvent; +class PostProcessor; + + +/** 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). Creating an instance of this class spawns + * a pre-processing thread to prepare queued events. + * + * This class is it's own slave. :) + */ +class QueuedEventSource : public EventSource, protected Raul::Slave +{ +public: + QueuedEventSource(size_t queued_size, size_t stamped_size); + ~QueuedEventSource(); + + void activate() { Slave::start(); } + void deactivate() { Slave::stop(); } + + void process(PostProcessor& dest, ProcessContext& context); + + void unblock(); + +protected: + void push_queued(QueuedEvent* const ev); + inline void push_stamped(Event* const ev) { _stamped_queue.push(ev); } + Event* pop_earliest_queued_before(const SampleCount time); + inline Event* pop_earliest_stamped_before(const SampleCount time); + + inline bool unprepared_events() { return (_prepared_back.get() != _back.get()); } + + virtual void _whipped(); ///< Prepare 1 event + +private: + // Note that it's crucially important which functions access which of these + // variables, to maintain threadsafeness. + + //(FIXME: make this a separate class?) + // 2-part queue for events that require pre-processing: + AtomicInt _front; ///< Front of queue + AtomicInt _back; ///< Back of entire queue (1 past index of back element) + AtomicInt _prepared_back; ///< Back of prepared section (1 past index of back prepared element) + const size_t _size; + QueuedEvent** _events; + Raul::Semaphore _blocking_semaphore; + + Raul::Semaphore _full_semaphore; + + /** Queue for timestamped events (no pre-processing). */ + Raul::SRSWQueue _stamped_queue; +}; + + +/** Pops the realtime (timestamped, not preprocessed) event off the realtime queue. + * + * Engine will use the sample timestamps of returned events directly and execute the + * event with sample accuracy. Timestamps in the past will be bumped forward to + * the beginning of the cycle (offset 0), when eg. skipped cycles occur. + */ +inline Event* +QueuedEventSource::pop_earliest_stamped_before(const SampleCount time) +{ + Event* ret = NULL; + + if (!_stamped_queue.empty()) { + if (_stamped_queue.front()->time() < time) { + ret = _stamped_queue.front(); + _stamped_queue.pop(); + } + } + + return ret; +} + + +/** Signal that the blocking event is finished. + * + * When this is called preparing will resume. This MUST be called by + * blocking events in their post_process() method. + */ +inline void +QueuedEventSource::unblock() +{ + _blocking_semaphore.post(); +} + + +} // namespace Ingen + +#endif // QUEUEDEVENTSOURCE_H diff --git a/src/engine/Responder.hpp b/src/engine/Responder.hpp new file mode 100644 index 00000000..80d2e24c --- /dev/null +++ b/src/engine/Responder.hpp @@ -0,0 +1,71 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef RESPONDER_H +#define RESPONDER_H + +#include +#include +#include +#include "interface/ClientInterface.hpp" + +namespace Ingen { + + +/** Class to handle responding to clients. + * + * This is a glorified std::pair for replying + * to numbered messages from a client. + * + * For responses that involve more messages, the response will come first + * followed by the messages (eg object notifications, values, errors, etc.) + * in a bundle (or "transfer" if too large). + */ +class Responder +{ +public: + Responder(Shared::ClientInterface* client=0, int32_t id=1) + : _client(client) + , _id(id) + {} + + int32_t id() const { return _id; } + void set_id(int32_t id) { _id = id; } + + Shared::ClientInterface* client() const { return _client; } + void set_client(Shared::ClientInterface* client) { _client = client; } + + void respond_ok() { + if (_client) + _client->response_ok(_id); + } + + void respond_error(const std::string& msg) { + if (_client) + _client->response_error(_id, msg); + } + +private: + Shared::ClientInterface* _client; + int32_t _id; +}; + + +} // namespace Ingen + +#endif // RESPONDER_H + diff --git a/src/engine/ThreadManager.hpp b/src/engine/ThreadManager.hpp new file mode 100644 index 00000000..12f27000 --- /dev/null +++ b/src/engine/ThreadManager.hpp @@ -0,0 +1,43 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef THREADMANAGER_H +#define THREADMANAGER_H + +#include + +using Raul::Thread; + +namespace Ingen { + + +enum ThreadID { + THREAD_PRE_PROCESS, + THREAD_PROCESS, + THREAD_POST_PROCESS +}; + + +class ThreadManager { +public: + inline static ThreadID current_thread_id() { return (ThreadID)Thread::get().context(); } +}; + + +} // namespace Ingen + +#endif // THREADMANAGER_H diff --git a/src/engine/TransportNode.cpp b/src/engine/TransportNode.cpp new file mode 100644 index 00000000..3fc14a43 --- /dev/null +++ b/src/engine/TransportNode.cpp @@ -0,0 +1,154 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "TransportNode.hpp" +#include +#include "OutputPort.hpp" +#include "InternalPlugin.hpp" +#include "JackAudioDriver.hpp" +#include "PortImpl.hpp" +#include "util.hpp" +//#include "Engine.hpp" + +namespace Ingen { + + +TransportNode::TransportNode(const string& path, bool polyphonic, PatchImpl* parent, SampleRate srate, size_t buffer_size) +: NodeBase(new InternalPlugin(NS_INGEN "transport_node", "transport", "Transport Follower"), + path, false, parent, srate, buffer_size) +{ +#if 0 + _num_ports = 10; + _ports.alloc(_num_ports); + + OutputPort* spb_port = new OutputPort(this, "Seconds per Beat", 0, 1, + // new PortInfo("Seconds per Beat", CONTROL, OUTPUT, 0, 0, 1), 1); + _ports.at(0) = spb_port; + + OutputPort* bpb_port = new OutputPort(this, "Beats per Bar", 1, 1, + // new PortInfo("Beats per Bar", CONTROL, OUTPUT, 0, 0, 1), 1); + _ports.at(1) = bpb_port; + + OutputPort* bar_port = new OutputPort(this, "Bar", 3, 1, +// new PortInfo("Bar", CONTROL, OUTPUT, 0, 0, 1), buffer_size); + _ports.at(2) = bar_port; + + OutputPort* beat_port = new OutputPort(this, "Beat", 3, 1, + // new PortInfo("Beat", CONTROL, OUTPUT, 0, 0, 1), buffer_size); + _ports.at(3) = beat_port; + + OutputPort* frame_port = new OutputPort(this, "Frame", 3, 1, + // new PortInfo("Frame", CONTROL, OUTPUT, 0, 0, 1), buffer_size); + _ports.at(4) = frame_port; + + OutputPort* hour_port = new OutputPort(this, "Hour", 3, 1, + // new PortInfo("Hour", CONTROL, OUTPUT, 0, 0, 1), buffer_size); + _ports.at(5) = hour_port; + + OutputPort* minute_port = new OutputPort(this, "Minute", 3, 1, + // new PortInfo("Minute", CONTROL, OUTPUT, 0, 0, 1), buffer_size); + _ports.at(6) = minute_port; + + OutputPort* second_port = new OutputPort(this, "Second", 3, 1, + // new PortInfo("Second", CONTROL, OUTPUT, 0, 0, 1), buffer_size); + _ports.at(7) = second_port; + + OutputPort* trg_port = new OutputPort(this, "Beat Tick", 2, 1, + // new PortInfo("Beat Tick", AUDIO, OUTPUT, 0, 0, 1), buffer_size); + _ports.at(8) = trg_port; + + OutputPort* bar_trig_port = new OutputPort(this, "Bar Tick", 3, 1, + // new PortInfo("Bar Tick", AUDIO, OUTPUT, 0, 0, 1), buffer_size); + _ports.at(9) = bar_trig_port; +#endif +} + + +void +TransportNode::process(ProcessContext& context) +{ + NodeBase::pre_process(context); +#if 0 + + // FIXME: this will die horribly with any driver other than jack (in theory) + const jack_position_t* const position = ((JackAudioDriver*)Engine::instance().audio_driver())->position(); + jack_transport_state_t state = ((JackAudioDriver*)Engine::instance().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*)_ports.at(0))->buffer(0)->set(spb, 0, 0); + ((OutputPort*)_ports.at(1))->buffer(0)->set(bpb, 0, 0); + + // fill the trigger buffers with zeros + ((OutputPort*)_ports.at(2))->buffer(0)->set(0.0f, 0, nframes - 1); + ((OutputPort*)_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*)_ports.at(2))->buffer(0)->set(1.0f, size_t(first_beat)); + if (first_beat_no % int(bpb) == 0) { + ((OutputPort*)_ports.at(3))->buffer(0)->set(1.0f, size_t(first_beat)); + ++first_beat_no; + } + } + } + #endif + + NodeBase::post_process(context); +} + + +} // namespace Ingen + diff --git a/src/engine/TransportNode.hpp b/src/engine/TransportNode.hpp new file mode 100644 index 00000000..af0ed207 --- /dev/null +++ b/src/engine/TransportNode.hpp @@ -0,0 +1,45 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef TRANSPORTNODE_H +#define TRANSPORTNODE_H + +#include +#include +#include "NodeBase.hpp" + +namespace Ingen { + + +/** 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 NodeBase +{ +public: + TransportNode(const std::string& path, bool polyphonic, PatchImpl* parent, SampleRate srate, size_t buffer_size); + + virtual void process(ProcessContext& context); +}; + + +} // namespace Ingen + +#endif // TRANSPORTNODE_H diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp new file mode 100644 index 00000000..45c17354 --- /dev/null +++ b/src/engine/engine.cpp @@ -0,0 +1,56 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include CONFIG_H_PATH + +#include +#include "engine.hpp" +#include "Engine.hpp" +#include "QueuedEngineInterface.hpp" +#include "tuning.hpp" +#include "util.hpp" + +namespace Ingen { + +Engine* +new_engine(Ingen::Shared::World* world) +{ + set_denormal_flags(); + return new Engine(world); +} + + +bool +launch_osc_engine(int port) +{ + char port_str[6]; + snprintf(port_str, 6, "%u", port); + const string cmd = string("ingen -e --engine-port=").append(port_str); + + if (Raul::Process::launch(cmd)) { + return true; + //return SharedPtr(new OSCEngineSender( + // string("osc.udp://localhost:").append(port_str))); + } else { + std::cerr << "Failed to launch engine process." << std::endl; + //return SharedPtr(); + return false; + } +} + +} // namespace Ingen + diff --git a/src/engine/engine.hpp b/src/engine/engine.hpp new file mode 100644 index 00000000..93b426ac --- /dev/null +++ b/src/engine/engine.hpp @@ -0,0 +1,42 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef INGEN_ENGINE_H +#define INGEN_ENGINE_H + +namespace Ingen { + +namespace Shared { class World; } + +class Engine; + +extern "C" { + + /** Create a new engine in this process */ + Engine* new_engine(Ingen::Shared::World* world); + + /** Launch an OSC engine as a completely separate process + * \return true if successful + */ + bool launch_osc_engine(int port); +} + + +} // namespace Ingen + +#endif // INGEN_ENGINE_H + diff --git a/src/engine/events.hpp b/src/engine/events.hpp new file mode 100644 index 00000000..14f5230c --- /dev/null +++ b/src/engine/events.hpp @@ -0,0 +1,53 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef EVENTS_H +#define EVENTS_H + +#include CONFIG_H_PATH + +#include "AllNotesOffEvent.hpp" +#include "ClearPatchEvent.hpp" +#include "ConnectionEvent.hpp" +#include "CreateNodeEvent.hpp" +#include "CreatePatchEvent.hpp" +#include "CreatePortEvent.hpp" +#include "DeactivateEvent.hpp" +#include "DestroyEvent.hpp" +#include "DisconnectAllEvent.hpp" +#include "DisconnectionEvent.hpp" +#include "EnablePatchEvent.hpp" +#include "LoadPluginsEvent.hpp" +#include "MidiLearnEvent.hpp" +#include "NoteEvent.hpp" +#include "PingQueuedEvent.hpp" +#include "RegisterClientEvent.hpp" +#include "RenameEvent.hpp" +#include "RequestAllObjectsEvent.hpp" +#include "RequestMetadataEvent.hpp" +#include "RequestObjectEvent.hpp" +#include "RequestPluginEvent.hpp" +#include "RequestPluginsEvent.hpp" +#include "RequestPortValueEvent.hpp" +#include "SetMetadataEvent.hpp" +#include "SetPolyphonicEvent.hpp" +#include "SetPolyphonyEvent.hpp" +#include "SetPortValueEvent.hpp" +#include "UnregisterClientEvent.hpp" + +#endif // EVENTS_H + diff --git a/src/engine/events/AllNotesOffEvent.cpp b/src/engine/events/AllNotesOffEvent.cpp new file mode 100644 index 00000000..fcb68b31 --- /dev/null +++ b/src/engine/events/AllNotesOffEvent.cpp @@ -0,0 +1,71 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "AllNotesOffEvent.hpp" +#include "Responder.hpp" +#include "Engine.hpp" +#include "EngineStore.hpp" +#include "module/World.hpp" +#include "shared/Store.hpp" + +namespace Ingen { + + +/** Note off with patch explicitly passed - triggered by MIDI. + */ +AllNotesOffEvent::AllNotesOffEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, PatchImpl* patch) +: Event(engine, responder, timestamp), + _patch(patch) +{ +} + + +/** Note off event with lookup - triggered by OSC. + */ +AllNotesOffEvent::AllNotesOffEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& patch_path) +: Event(engine, responder, timestamp), + _patch_path(patch_path), + _patch(NULL) +{ +} + + +void +AllNotesOffEvent::execute(ProcessContext& context) +{ + Event::execute(context); + + if (_patch == NULL && _patch_path != "") + _patch = _engine.engine_store()->find_patch(_patch_path); + + //if (_patch != NULL) + // for (Raul::List::iterator j = _patch->midi_in_nodes().begin(); j != _patch->midi_in_nodes().end(); ++j) + // (*j)->all_notes_off(offset); +} + + +void +AllNotesOffEvent::post_process() +{ + if (_patch != NULL) + _responder->respond_ok(); +} + + +} // namespace Ingen + + diff --git a/src/engine/events/AllNotesOffEvent.hpp b/src/engine/events/AllNotesOffEvent.hpp new file mode 100644 index 00000000..3e4d56b3 --- /dev/null +++ b/src/engine/events/AllNotesOffEvent.hpp @@ -0,0 +1,51 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef ALLNOTESOFFEVENT_H +#define ALLNOTESOFFEVENT_H + +#include "Event.hpp" +#include +using std::string; + +namespace Ingen { + +class PatchImpl; + + +/** A note off event for all active voices. + * + * \ingroup engine + */ +class AllNotesOffEvent : public Event +{ +public: + AllNotesOffEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, PatchImpl* patch); + AllNotesOffEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& patch_path); + + void execute(ProcessContext& context); + void post_process(); + +private: + const string _patch_path; + PatchImpl* _patch; +}; + + +} // namespace Ingen + +#endif // ALLNOTESOFFEVENT_H diff --git a/src/engine/events/ClearPatchEvent.cpp b/src/engine/events/ClearPatchEvent.cpp new file mode 100644 index 00000000..c1fb0749 --- /dev/null +++ b/src/engine/events/ClearPatchEvent.cpp @@ -0,0 +1,133 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "ClearPatchEvent.hpp" +#include "Responder.hpp" +#include "Engine.hpp" +#include "PatchImpl.hpp" +#include "ClientBroadcaster.hpp" +#include "util.hpp" +#include "EngineStore.hpp" +#include "PortImpl.hpp" +#include "NodeImpl.hpp" +#include "ConnectionImpl.hpp" +#include "QueuedEventSource.hpp" +#include "AudioDriver.hpp" +#include "MidiDriver.hpp" + +namespace Ingen { + + +ClearPatchEvent::ClearPatchEvent(Engine& engine, SharedPtr responder, FrameTime time, QueuedEventSource* source, const string& patch_path) + : QueuedEvent(engine, responder, time, true, source) + , _patch_path(patch_path) + , _driver_port(NULL) + , _process(false) + , _ports_array(NULL) + , _compiled_patch(NULL) +{ +} + + +void +ClearPatchEvent::pre_process() +{ + EngineStore::Objects::iterator patch_iterator = _engine.engine_store()->find(_patch_path); + + if (patch_iterator != _engine.engine_store()->end()) { + _patch = PtrCast(patch_iterator->second); + if (_patch) { + _process = _patch->enabled(); + _removed_table = _engine.engine_store()->remove_children(patch_iterator); + _patch->nodes().clear(); + _patch->connections().clear(); + _ports_array = _patch->build_ports_array(); + if (_patch->enabled()) + _compiled_patch = _patch->compile(); + } + } + + QueuedEvent::pre_process(); +} + + +void +ClearPatchEvent::execute(ProcessContext& context) +{ + QueuedEvent::execute(context); + + if (_patch && _removed_table) { + _patch->disable(); + + if (_patch->compiled_patch() != NULL) { + _engine.maid()->push(_patch->compiled_patch()); + _patch->compiled_patch(NULL); + } + + _patch->clear_ports(); + _patch->connections().clear(); + _patch->compiled_patch(_compiled_patch); + Raul::Array* old_ports = _patch->external_ports(); + _patch->external_ports(_ports_array); + _ports_array = old_ports; + + // Remove driver ports, if necessary + if (_patch->parent() == NULL) { + for (EngineStore::Objects::iterator i = _removed_table->begin(); i != _removed_table->end(); ++i) { + SharedPtr port = PtrCast(i->second); + if (port && port->type() == DataType::AUDIO) + _driver_port = _engine.audio_driver()->remove_port(port->path()); + else if (port && port->type() == DataType::EVENT) + _driver_port = _engine.midi_driver()->remove_port(port->path()); + } + } + } +} + + +void +ClearPatchEvent::post_process() +{ + if (_patch != NULL) { + delete _ports_array; + delete _driver_port; + + // Restore patch's run state + if (_process) + _patch->enable(); + else + _patch->disable(); + + // Make sure everything's sane + assert(_patch->nodes().size() == 0); + assert(_patch->num_ports() == 0); + assert(_patch->connections().size() == 0); + + // Reply + _responder->respond_ok(); + _engine.broadcaster()->send_patch_cleared(_patch_path); + } else { + _responder->respond_error(string("Patch ") + _patch_path + " not found"); + } + + _source->unblock(); // FIXME: can be done earlier in execute? +} + + +} // namespace Ingen + diff --git a/src/engine/events/ClearPatchEvent.hpp b/src/engine/events/ClearPatchEvent.hpp new file mode 100644 index 00000000..803721fb --- /dev/null +++ b/src/engine/events/ClearPatchEvent.hpp @@ -0,0 +1,65 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CLEARPATCHEVENT_H +#define CLEARPATCHEVENT_H + +#include +#include +#include +#include +#include "QueuedEvent.hpp" +#include "EngineStore.hpp" +#include "PatchImpl.hpp" + +using std::string; + +namespace Ingen { + +class PatchImpl; +class DriverPort; + + +/** Delete all nodes from a patch. + * + * \ingroup engine + */ +class ClearPatchEvent : public QueuedEvent +{ +public: + ClearPatchEvent(Engine& engine, SharedPtr responder, FrameTime time, QueuedEventSource* source, const string& patch_path); + + void pre_process(); + void execute(ProcessContext& context); + void post_process(); + +private: + const string _patch_path; + SharedPtr _patch; + DriverPort* _driver_port; + bool _process; + Raul::Array* _ports_array; ///< New (external) ports for Patch + CompiledPatch* _compiled_patch; ///< Patch's new process order + + SharedPtr< Table > > _removed_table; +}; + + +} // namespace Ingen + + +#endif // CLEARPATCHEVENT_H diff --git a/src/engine/events/ConnectionEvent.cpp b/src/engine/events/ConnectionEvent.cpp new file mode 100644 index 00000000..24b80dee --- /dev/null +++ b/src/engine/events/ConnectionEvent.cpp @@ -0,0 +1,202 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include +#include +#include +#include +#include "ClientBroadcaster.hpp" +#include "ConnectionEvent.hpp" +#include "ConnectionImpl.hpp" +#include "Engine.hpp" +#include "InputPort.hpp" +#include "EngineStore.hpp" +#include "OutputPort.hpp" +#include "PatchImpl.hpp" +#include "PortImpl.hpp" +#include "Responder.hpp" +#include "types.hpp" + +using std::string; +namespace Ingen { + + +ConnectionEvent::ConnectionEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& src_port_path, const string& dst_port_path) +: QueuedEvent(engine, responder, timestamp), + _src_port_path(src_port_path), + _dst_port_path(dst_port_path), + _patch(NULL), + _src_port(NULL), + _dst_port(NULL), + _compiled_patch(NULL), + _patch_listnode(NULL), + _port_listnode(NULL), + _error(NO_ERROR) +{ +} + + +void +ConnectionEvent::pre_process() +{ + if (_src_port_path.parent().parent() != _dst_port_path.parent().parent() + && _src_port_path.parent() != _dst_port_path.parent().parent() + && _src_port_path.parent().parent() != _dst_port_path.parent()) { + _error = PARENT_PATCH_DIFFERENT; + QueuedEvent::pre_process(); + return; + } + + _src_port = _engine.engine_store()->find_port(_src_port_path); + _dst_port = _engine.engine_store()->find_port(_dst_port_path); + + if (_src_port == NULL || _dst_port == NULL) { + _error = PORT_NOT_FOUND; + QueuedEvent::pre_process(); + return; + } + + if ( ! (_src_port->type() == _dst_port->type() + || ( (_src_port->type() == DataType::CONTROL || _src_port->type() == DataType::AUDIO) + && (_dst_port->type() == DataType::CONTROL || _dst_port->type() == DataType::AUDIO) ))) { + _error = TYPE_MISMATCH; + QueuedEvent::pre_process(); + return; + } + + _dst_input_port = dynamic_cast(_dst_port); + _src_output_port = dynamic_cast(_src_port); + + if (!_dst_input_port || !_src_output_port) { + _error = DIRECTION_MISMATCH; + QueuedEvent::pre_process(); + return; + } + + NodeImpl* const src_node = _src_port->parent_node(); + NodeImpl* const dst_node = _dst_port->parent_node(); + + // Connection to a patch port from inside the patch + if (src_node->parent_patch() != dst_node->parent_patch()) { + + assert(src_node->parent() == dst_node || dst_node->parent() == src_node); + if (src_node->parent() == dst_node) + _patch = dynamic_cast(dst_node); + else + _patch = dynamic_cast(src_node); + + // Connection from a patch input to a patch output (pass through) + } else if (src_node == dst_node && dynamic_cast(src_node)) { + _patch = dynamic_cast(src_node); + + // Normal connection between nodes with the same parent + } else { + _patch = src_node->parent_patch(); + } + + assert(_patch); + + //if (_dst_input_port->is_connected_to(_src_output_port)) { + if (_patch->has_connection(_src_output_port, _dst_input_port)) { + _error = ALREADY_CONNECTED; + QueuedEvent::pre_process(); + return; + } + + if (src_node == NULL || dst_node == NULL) { + _error = PARENTS_NOT_FOUND; + QueuedEvent::pre_process(); + return; + } + + if (_patch != src_node && src_node->parent() != _patch && dst_node->parent() != _patch) { + _error = PARENTS_NOT_FOUND; + QueuedEvent::pre_process(); + return; + } + + _connection = SharedPtr(new ConnectionImpl(_src_port, _dst_port)); + _patch_listnode = new PatchImpl::Connections::Node(_connection); + _port_listnode = new InputPort::Connections::Node(_connection); + + // Need to be careful about patch port connections here and adding a node's + // parent as a dependant/provider, or adding a patch as it's own provider... + if (src_node != dst_node && src_node->parent() == dst_node->parent()) { + dst_node->providers()->push_back(new Raul::List::Node(src_node)); + src_node->dependants()->push_back(new Raul::List::Node(dst_node)); + } + + _patch->add_connection(_patch_listnode); + + if (_patch->enabled()) + _compiled_patch = _patch->compile(); + + QueuedEvent::pre_process(); +} + + +void +ConnectionEvent::execute(ProcessContext& context) +{ + QueuedEvent::execute(context); + + if (_error == NO_ERROR) { + // This must be inserted here, since they're actually used by the audio thread + _dst_input_port->add_connection(_port_listnode); + if (_patch->compiled_patch() != NULL) + _engine.maid()->push(_patch->compiled_patch()); + _patch->compiled_patch(_compiled_patch); + } +} + + +void +ConnectionEvent::post_process() +{ + std::ostringstream ss; + if (_error == NO_ERROR) { + _responder->respond_ok(); + _engine.broadcaster()->send_connection(_connection); + return; + } + + ss << boost::format("Unable to make connection %1% -> %2% (") % _src_port_path % _dst_port_path; + + switch (_error) { + case PARENT_PATCH_DIFFERENT: + ss << "Ports have mismatched parents"; break; + case PORT_NOT_FOUND: + ss << "Port not found"; break; + case TYPE_MISMATCH: + ss << "Type mismatch"; break; + case DIRECTION_MISMATCH: + ss << "Direction mismatch"; break; + case ALREADY_CONNECTED: + ss << "Already connected"; break; + case PARENTS_NOT_FOUND: + ss << "Parents not found"; break; + default: + ss << "Unknown error"; + } + ss << ")"; + _responder->respond_error(ss.str()); +} + + +} // namespace Ingen + diff --git a/src/engine/events/ConnectionEvent.hpp b/src/engine/events/ConnectionEvent.hpp new file mode 100644 index 00000000..89407957 --- /dev/null +++ b/src/engine/events/ConnectionEvent.hpp @@ -0,0 +1,92 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CONNECTIONEVENT_H +#define CONNECTIONEVENT_H + +#include +#include +#include "QueuedEvent.hpp" +#include "PatchImpl.hpp" +#include "InputPort.hpp" +#include "types.hpp" +using std::string; + +namespace Raul { + template class ListNode; + template class Array; +} + +namespace Ingen { + +class PatchImpl; +class NodeImpl; +class ConnectionImpl; +class MidiMessage; +class PortImpl; +class InputPort; +class OutputPort; +class CompiledPatch; + + +/** Make a Connection between two Ports. + * + * \ingroup engine + */ +class ConnectionEvent : public QueuedEvent +{ +public: + ConnectionEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& src_port_path, const string& dst_port_path); + + void pre_process(); + void execute(ProcessContext& context); + void post_process(); + +private: + + enum ErrorType { + NO_ERROR, + PARENT_PATCH_DIFFERENT, + PORT_NOT_FOUND, + TYPE_MISMATCH, + DIRECTION_MISMATCH, + ALREADY_CONNECTED, + PARENTS_NOT_FOUND + }; + + Raul::Path _src_port_path; + Raul::Path _dst_port_path; + + PatchImpl* _patch; + PortImpl* _src_port; + PortImpl* _dst_port; + OutputPort* _src_output_port; + InputPort* _dst_input_port; + + CompiledPatch* _compiled_patch; ///< New process order for Patch + + SharedPtr _connection; + PatchImpl::Connections::Node* _patch_listnode; + InputPort::Connections::Node* _port_listnode; + + ErrorType _error; +}; + + +} // namespace Ingen + +#endif // CONNECTIONEVENT_H diff --git a/src/engine/events/CreateNodeEvent.cpp b/src/engine/events/CreateNodeEvent.cpp new file mode 100644 index 00000000..b58bc2c3 --- /dev/null +++ b/src/engine/events/CreateNodeEvent.cpp @@ -0,0 +1,151 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include "module/World.hpp" +#include "CreateNodeEvent.hpp" +#include "Responder.hpp" +#include "PatchImpl.hpp" +#include "NodeImpl.hpp" +#include "PluginImpl.hpp" +#include "Engine.hpp" +#include "PatchImpl.hpp" +#include "NodeFactory.hpp" +#include "ClientBroadcaster.hpp" +#include "EngineStore.hpp" +#include "PortImpl.hpp" +#include "AudioDriver.hpp" + +namespace Ingen { + + +CreateNodeEvent::CreateNodeEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& path, + const string& plugin_uri, bool polyphonic) +: QueuedEvent(engine, responder, timestamp), + _path(path), + _plugin_uri(plugin_uri), + _polyphonic(polyphonic), + _patch(NULL), + _node(NULL), + _compiled_patch(NULL), + _node_already_exists(false) +{ +} + + +/** DEPRECATED: Construct from type, library name, and plugin label. + * + * Do not use. + */ +CreateNodeEvent::CreateNodeEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& path, + const string& plugin_type, const string& plugin_lib, const string& plugin_label, bool polyphonic) +: QueuedEvent(engine, responder, timestamp), + _path(path), + _plugin_type(plugin_type), + _plugin_lib(plugin_lib), + _plugin_label(plugin_label), + _polyphonic(polyphonic), + _patch(NULL), + _node(NULL), + _compiled_patch(NULL), + _node_already_exists(false) +{ +} + + +void +CreateNodeEvent::pre_process() +{ + if (_engine.engine_store()->find_object(_path) != NULL) { + _node_already_exists = true; + QueuedEvent::pre_process(); + return; + } + + _patch = _engine.engine_store()->find_patch(_path.parent()); + + PluginImpl* const plugin = (_plugin_uri != "") + ? _engine.node_factory()->plugin(_plugin_uri) + : _engine.node_factory()->plugin(_plugin_type, _plugin_lib, _plugin_label); + + if (_patch && plugin) { + + _node = plugin->instantiate(_path.name(), _polyphonic, _patch, _engine); + + if (_node != NULL) { + _node->activate(); + + // This can be done here because the audio thread doesn't touch the + // node tree - just the process order array + _patch->add_node(new PatchImpl::Nodes::Node(_node)); + //_node->add_to_store(_engine.engine_store()); + _engine.engine_store()->add(_node); + + // FIXME: not really necessary to build process order since it's not connected, + // just append to the list + if (_patch->enabled()) + _compiled_patch = _patch->compile(); + } + } + QueuedEvent::pre_process(); +} + + +void +CreateNodeEvent::execute(ProcessContext& context) +{ + QueuedEvent::execute(context); + + if (_node != NULL) { + if (_patch->compiled_patch() != NULL) + _engine.maid()->push(_patch->compiled_patch()); + _patch->compiled_patch(_compiled_patch); + } +} + + +void +CreateNodeEvent::post_process() +{ + string msg; + if (_node_already_exists) { + msg = string("Could not create node - ").append(_path);// + " already exists."; + _responder->respond_error(msg); + } else if (_patch == NULL) { + msg = "Could not find patch '" + _path.parent() +"' for add_node."; + _responder->respond_error(msg); + } else if (_node == NULL) { + msg = "Unable to load node "; + msg += _path + " (you're missing the plugin "; + if (_plugin_uri != "") + msg += _plugin_uri; + else + msg += _plugin_lib + ":" + _plugin_label + " (" + _plugin_type + ")"; + msg += ")"; + _responder->respond_error(msg); + } else { + _responder->respond_ok(); + _engine.broadcaster()->send_node(_node, true); // yes, send ports + } +} + + +} // namespace Ingen + diff --git a/src/engine/events/CreateNodeEvent.hpp b/src/engine/events/CreateNodeEvent.hpp new file mode 100644 index 00000000..c3ef6313 --- /dev/null +++ b/src/engine/events/CreateNodeEvent.hpp @@ -0,0 +1,81 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CREATENODEEVENT_H +#define CREATENODEEVENT_H + +#include "QueuedEvent.hpp" +#include +#include +using std::string; + +namespace Raul { template class Array; } +template class TreeNode; + +namespace Ingen { + +class PatchImpl; +class NodeImpl; +class CompiledPatch; + + +/** An event to load a Node and insert it into a Patch. + * + * \ingroup engine + */ +class CreateNodeEvent : public QueuedEvent +{ +public: + CreateNodeEvent(Engine& engine, + SharedPtr responder, + SampleCount timestamp, + const string& node_path, + const string& plugin_uri, + bool poly); + + // DEPRECATED + CreateNodeEvent(Engine& engine, + SharedPtr responder, + SampleCount timestamp, + const string& node_path, + const string& plugin_type, + const string& lib_name, + const string& plugin_label, + bool poly); + + void pre_process(); + void execute(ProcessContext& context); + void post_process(); + +private: + string _patch_name; + Raul::Path _path; + string _plugin_uri; ///< If nonempty then type, library, label, are ignored + string _plugin_type; + string _plugin_lib; + string _plugin_label; + bool _polyphonic; + PatchImpl* _patch; + NodeImpl* _node; + CompiledPatch* _compiled_patch; ///< Patch's new process order + bool _node_already_exists; +}; + + +} // namespace Ingen + +#endif // CREATENODEEVENT_H diff --git a/src/engine/events/CreatePatchEvent.cpp b/src/engine/events/CreatePatchEvent.cpp new file mode 100644 index 00000000..64fe5c63 --- /dev/null +++ b/src/engine/events/CreatePatchEvent.cpp @@ -0,0 +1,157 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "CreatePatchEvent.hpp" +#include "Responder.hpp" +#include "PatchImpl.hpp" +#include "NodeImpl.hpp" +#include "PluginImpl.hpp" +#include "Engine.hpp" +#include "ClientBroadcaster.hpp" +#include "AudioDriver.hpp" +#include "EngineStore.hpp" + +namespace Ingen { + + +CreatePatchEvent::CreatePatchEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& path, int poly) +: QueuedEvent(engine, responder, timestamp), + _path(path), + _patch(NULL), + _parent(NULL), + _compiled_patch(NULL), + _poly(poly), + _error(NO_ERROR) +{ +} + + +void +CreatePatchEvent::pre_process() +{ + if (!Path::is_valid(_path)) { + _error = INVALID_PATH; + QueuedEvent::pre_process(); + return; + } + + if (_path == "/" || _engine.engine_store()->find_object(_path) != NULL) { + _error = OBJECT_EXISTS; + QueuedEvent::pre_process(); + return; + } + + if (_poly < 1) { + _error = INVALID_POLY; + QueuedEvent::pre_process(); + return; + } + + const Path& path = (const Path&)_path; + + _parent = _engine.engine_store()->find_patch(path.parent()); + if (_parent == NULL) { + _error = PARENT_NOT_FOUND; + QueuedEvent::pre_process(); + return; + } + + uint32_t poly = 1; + if (_parent != NULL && _poly > 1 && _poly == static_cast(_parent->internal_polyphony())) + poly = _poly; + + _patch = new PatchImpl(_engine, path.name(), poly, _parent, _engine.audio_driver()->sample_rate(), _engine.audio_driver()->buffer_size(), _poly); + + if (_parent != NULL) { + _parent->add_node(new PatchImpl::Nodes::Node(_patch)); + + if (_parent->enabled()) + _compiled_patch = _parent->compile(); + } + + _patch->activate(); + + // Insert into EngineStore + //_patch->add_to_store(_engine.engine_store()); + _engine.engine_store()->add(_patch); + + QueuedEvent::pre_process(); +} + + +void +CreatePatchEvent::execute(ProcessContext& context) +{ + QueuedEvent::execute(context); + + if (_patch != NULL) { + if (_parent == NULL) { + assert(_path == "/"); + assert(_patch->parent_patch() == NULL); + _engine.audio_driver()->set_root_patch(_patch); + } else { + assert(_parent != NULL); + assert(_path != "/"); + + if (_parent->compiled_patch() != NULL) + _engine.maid()->push(_parent->compiled_patch()); + _parent->compiled_patch(_compiled_patch); + } + } +} + + +void +CreatePatchEvent::post_process() +{ + if (_responder.get()) { + if (_error == NO_ERROR) { + + _responder->respond_ok(); + + // Don't send ports/nodes that have been added since prepare() + // (otherwise they would be sent twice) + _engine.broadcaster()->send_patch(_patch, false); + + } else if (_error == INVALID_PATH) { + string msg = "Attempt to create patch with illegal path "; + msg.append(_path); + _responder->respond_error(msg); + } else if (_error == OBJECT_EXISTS) { + _responder->respond_ok(); + /*string msg = "Unable to create patch: "; + msg.append(_path).append(" already exists."); + _responder->respond_error(msg);*/ + } else if (_error == PARENT_NOT_FOUND) { + string msg = "Unable to create patch: Parent "; + msg.append(Path(_path).parent()).append(" not found."); + _responder->respond_error(msg); + } else if (_error == INVALID_POLY) { + string msg = "Unable to create patch "; + msg.append(_path).append(": ").append("Invalid polyphony respondered."); + _responder->respond_error(msg); + } else { + _responder->respond_error("Unable to load patch."); + } + } +} + + +} // namespace Ingen + diff --git a/src/engine/events/CreatePatchEvent.hpp b/src/engine/events/CreatePatchEvent.hpp new file mode 100644 index 00000000..733aba17 --- /dev/null +++ b/src/engine/events/CreatePatchEvent.hpp @@ -0,0 +1,64 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CREATEPATCHEVENT_H +#define CREATEPATCHEVENT_H + +#include +#include +#include "QueuedEvent.hpp" + +using std::string; + +namespace Raul { template class Array; } +template class TreeNode; + +namespace Ingen { + +class PatchImpl; +class CompiledPatch; + + +/** Creates a new Patch. + * + * \ingroup engine + */ +class CreatePatchEvent : public QueuedEvent +{ +public: + CreatePatchEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& path, int poly); + + void pre_process(); + void execute(ProcessContext& context); + void post_process(); + +private: + enum ErrorType { NO_ERROR, OBJECT_EXISTS, PARENT_NOT_FOUND, INVALID_POLY, INVALID_PATH }; + + const std::string _path; + PatchImpl* _patch; + PatchImpl* _parent; + CompiledPatch* _compiled_patch; + int _poly; + ErrorType _error; +}; + + +} // namespace Ingen + + +#endif // CREATEPATCHEVENT_H diff --git a/src/engine/events/CreatePortEvent.cpp b/src/engine/events/CreatePortEvent.cpp new file mode 100644 index 00000000..e767f522 --- /dev/null +++ b/src/engine/events/CreatePortEvent.cpp @@ -0,0 +1,173 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include "Responder.hpp" +#include "CreatePortEvent.hpp" +#include "PatchImpl.hpp" +#include "PluginImpl.hpp" +#include "Engine.hpp" +#include "PatchImpl.hpp" +#include "QueuedEventSource.hpp" +#include "EngineStore.hpp" +#include "ClientBroadcaster.hpp" +#include "PortImpl.hpp" +#include "AudioDriver.hpp" +#include "MidiDriver.hpp" +#include "OSCDriver.hpp" +#include "DuplexPort.hpp" + +namespace Ingen { + + +CreatePortEvent::CreatePortEvent(Engine& engine, + SharedPtr responder, + SampleCount timestamp, + const string& path, + const string& type, + bool is_output, + QueuedEventSource* source) +: QueuedEvent(engine, responder, timestamp, true, source), + _error(NO_ERROR), + _path(path), + _type(type), + _is_output(is_output), + _data_type(type), + _patch(NULL), + _patch_port(NULL), + _driver_port(NULL) +{ + /* This is blocking because of the two different sets of Patch ports, the array used in the + * audio thread (inherited from NodeBase), and the arrays used in the pre processor thread. + * If two add port events arrive in the same cycle and the second pre processes before the + * first executes, bad things happen (ports are lost). + * + * FIXME: fix this using RCU + */ + + if (_data_type == DataType::UNKNOWN) { + cerr << "[CreatePortEvent] Unknown port type " << type << endl; + _error = UNKNOWN_TYPE; + } +} + + +void +CreatePortEvent::pre_process() +{ + if (_error == UNKNOWN_TYPE || _engine.engine_store()->find_object(_path)) { + QueuedEvent::pre_process(); + return; + } + + // FIXME: this is just a mess :/ + + _patch = _engine.engine_store()->find_patch(_path.parent()); + + if (_patch != NULL) { + assert(_patch->path() == _path.parent()); + + size_t buffer_size = 1; + if (_type != "ingen:Float") + buffer_size = _engine.audio_driver()->buffer_size(); + + const uint32_t old_num_ports = _patch->num_ports(); + + _patch_port = _patch->create_port(_path.name(), _data_type, buffer_size, _is_output); + + if (_patch_port) { + + if (_is_output) + _patch->add_output(new Raul::List::Node(_patch_port)); + else + _patch->add_input(new Raul::List::Node(_patch_port)); + + if (_patch->external_ports()) + _ports_array = new Raul::Array(old_num_ports + 1, *_patch->external_ports()); + else + _ports_array = new Raul::Array(old_num_ports + 1, NULL); + + + _ports_array->at(_patch->num_ports()-1) = _patch_port; + //_patch_port->add_to_store(_engine.engine_store()); + _engine.engine_store()->add(_patch_port); + + if (!_patch->parent()) { + if (_type == "ingen:AudioPort") + _driver_port = _engine.audio_driver()->create_port( + dynamic_cast(_patch_port)); + else if (_type == "ingen:MIDIPort" || _type == "ingen:EventPort") + _driver_port = _engine.midi_driver()->create_port( + dynamic_cast(_patch_port)); + else if (_type == "ingen:OSCPort" && _engine.osc_driver()) + _driver_port = _engine.osc_driver()->create_port( + dynamic_cast(_patch_port)); + } + + assert(_ports_array->size() == _patch->num_ports()); + + } + } + QueuedEvent::pre_process(); +} + + +void +CreatePortEvent::execute(ProcessContext& context) +{ + QueuedEvent::execute(context); + + if (_patch_port) { + + _engine.maid()->push(_patch->external_ports()); + //_patch->add_port(_port); + + _patch->external_ports(_ports_array); + } + + if (_driver_port) { + if (_type == "ingen:AudioPort") + _engine.audio_driver()->add_port(_driver_port); + else if (_type == "ingen:MIDIPort" || _type == "ingen:EventPort") + _engine.midi_driver()->add_port(_driver_port); + else if (_type == "ingen:OSCPort") + cerr << "OSC DRIVER PORT" << endl; + } + + if (_source) + _source->unblock(); +} + + +void +CreatePortEvent::post_process() +{ + if (_error != NO_ERROR || !_patch_port) { + const string msg = string("Could not create port - ").append(_path); + _responder->respond_error(msg); + } else { + _responder->respond_ok(); + _engine.broadcaster()->send_port(_patch_port); + } +} + + +} // namespace Ingen + diff --git a/src/engine/events/CreatePortEvent.hpp b/src/engine/events/CreatePortEvent.hpp new file mode 100644 index 00000000..5ddd8aa3 --- /dev/null +++ b/src/engine/events/CreatePortEvent.hpp @@ -0,0 +1,72 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CREATEPORTEVENT_H +#define CREATEPORTEVENT_H + +#include "QueuedEvent.hpp" +#include +#include +#include "interface/DataType.hpp" +#include +using std::string; + +template class Array; + +namespace Ingen { + +class PatchImpl; +class PortImpl; +class DriverPort; + + +/** An event to add a Port to a Patch. + * + * \ingroup engine + */ +class CreatePortEvent : public QueuedEvent +{ +public: + CreatePortEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& path, const string& type, bool is_output, QueuedEventSource* source); + + void pre_process(); + void execute(ProcessContext& context); + void post_process(); + +private: + + enum ErrorType { + NO_ERROR, + UNKNOWN_TYPE + }; + + ErrorType _error; + Raul::Path _path; + string _type; + bool _is_output; + DataType _data_type; + PatchImpl* _patch; + PortImpl* _patch_port; + Raul::Array* _ports_array; ///< New (external) ports array for Patch + DriverPort* _driver_port; ///< Driver (eg Jack) port if this is a toplevel port + bool _succeeded; +}; + + +} // namespace Ingen + +#endif // CREATEPORTEVENT_H diff --git a/src/engine/events/DeactivateEvent.cpp b/src/engine/events/DeactivateEvent.cpp new file mode 100644 index 00000000..a68419f0 --- /dev/null +++ b/src/engine/events/DeactivateEvent.cpp @@ -0,0 +1,54 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "DeactivateEvent.hpp" +#include "Responder.hpp" +#include "Engine.hpp" + +namespace Ingen { + + +DeactivateEvent::DeactivateEvent(Engine& engine, SharedPtr responder, SampleCount timestamp) +: QueuedEvent(engine, responder, timestamp) +{ +} + + +void +DeactivateEvent::pre_process() +{ + QueuedEvent::pre_process(); +} + + +void +DeactivateEvent::execute(ProcessContext& context) +{ + QueuedEvent::execute(context); +} + + +void +DeactivateEvent::post_process() +{ + _responder->respond_ok(); + _engine.deactivate(); +} + + +} // namespace Ingen + diff --git a/src/engine/events/DeactivateEvent.hpp b/src/engine/events/DeactivateEvent.hpp new file mode 100644 index 00000000..5a6750ba --- /dev/null +++ b/src/engine/events/DeactivateEvent.hpp @@ -0,0 +1,43 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef DEACTIVATEEVENT_H +#define DEACTIVATEEVENT_H + +#include "QueuedEvent.hpp" + +namespace Ingen { + + +/** Deactivates the engine. + * + * \ingroup engine + */ +class DeactivateEvent : public QueuedEvent +{ +public: + DeactivateEvent(Engine& engine, SharedPtr responder, SampleCount timestamp); + + void pre_process(); + void execute(ProcessContext& context); + void post_process(); +}; + + +} // namespace Ingen + +#endif // DEACTIVATEEVENT_H diff --git a/src/engine/events/DestroyEvent.cpp b/src/engine/events/DestroyEvent.cpp new file mode 100644 index 00000000..07770c0a --- /dev/null +++ b/src/engine/events/DestroyEvent.cpp @@ -0,0 +1,201 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "DestroyEvent.hpp" +#include "Responder.hpp" +#include "Engine.hpp" +#include "PatchImpl.hpp" +#include "NodeBase.hpp" +#include "PluginImpl.hpp" +#include "AudioDriver.hpp" +#include "MidiDriver.hpp" +#include "DisconnectAllEvent.hpp" +#include "ClientBroadcaster.hpp" +#include "EngineStore.hpp" +#include "QueuedEventSource.hpp" +#include "PortImpl.hpp" + +namespace Ingen { + + +DestroyEvent::DestroyEvent(Engine& engine, SharedPtr responder, FrameTime time, QueuedEventSource* source, const string& path, bool block) + : QueuedEvent(engine, responder, time, source, source) + , _path(path) + , _store_iterator(engine.engine_store()->end()) + , _driver_port(NULL) + , _patch_node_listnode(NULL) + , _patch_port_listnode(NULL) + , _ports_array(NULL) + , _compiled_patch(NULL) + , _disconnect_event(NULL) +{ + assert(_source); +} + + +DestroyEvent::~DestroyEvent() +{ + delete _disconnect_event; +} + + +void +DestroyEvent::pre_process() +{ + _store_iterator = _engine.engine_store()->find(_path); + + if (_store_iterator != _engine.engine_store()->end()) { + _node = PtrCast(_store_iterator->second); + + if (!_node) + _port = PtrCast(_store_iterator->second); + } + + if (_store_iterator != _engine.engine_store()->end()) { + _removed_table = _engine.engine_store()->remove(_store_iterator); + } + + if (_node != NULL && _path != "/") { + assert(_node->parent_patch()); + _patch_node_listnode = _node->parent_patch()->remove_node(_path.name()); + if (_patch_node_listnode) { + assert(_patch_node_listnode->elem() == _node.get()); + + _disconnect_event = new DisconnectAllEvent(_engine, _node->parent_patch(), _node.get()); + _disconnect_event->pre_process(); + + if (_node->parent_patch()->enabled()) { + // FIXME: is this called multiple times? + _compiled_patch = _node->parent_patch()->compile(); +#ifndef NDEBUG + // Be sure node is removed from process order, so it can be destroyed + for (size_t i=0; i < _compiled_patch->size(); ++i) { + assert(_compiled_patch->at(i).node() != _node.get()); + // FIXME: check providers/dependants too + } +#endif + } + } + } else if (_port) { + assert(_port->parent_patch()); + _patch_port_listnode = _port->parent_patch()->remove_port(_path.name()); + if (_patch_port_listnode) { + assert(_patch_port_listnode->elem() == _port.get()); + + _disconnect_event = new DisconnectAllEvent(_engine, _port->parent_patch(), _port.get()); + _disconnect_event->pre_process(); + + if (_port->parent_patch()->enabled()) { + // FIXME: is this called multiple times? + _compiled_patch = _port->parent_patch()->compile(); + _ports_array = _port->parent_patch()->build_ports_array(); + assert(_ports_array->size() == _port->parent_patch()->num_ports()); + } + } + + } + + QueuedEvent::pre_process(); +} + + +void +DestroyEvent::execute(ProcessContext& context) +{ + QueuedEvent::execute(context); + + if (_patch_node_listnode) { + assert(_node); + + if (_disconnect_event) + _disconnect_event->execute(context); + + if (_node->parent_patch()->compiled_patch()) + _engine.maid()->push(_node->parent_patch()->compiled_patch()); + _node->parent_patch()->compiled_patch(_compiled_patch); + + } else if (_patch_port_listnode) { + assert(_port); + + if (_disconnect_event) + _disconnect_event->execute(context); + + if (_port->parent_patch()->compiled_patch()) + _engine.maid()->push(_port->parent_patch()->compiled_patch()); + + _port->parent_patch()->compiled_patch(_compiled_patch); + + if (_port->parent_patch()->external_ports()) + _engine.maid()->push(_port->parent_patch()->external_ports()); + + _port->parent_patch()->external_ports(_ports_array); + + if ( ! _port->parent_patch()->parent()) { + if (_port->type() == DataType::AUDIO) + _driver_port = _engine.audio_driver()->remove_port(_port->path()); + else if (_port->type() == DataType::EVENT) + _driver_port = _engine.midi_driver()->remove_port(_port->path()); + } + } + + if (_source) + _source->unblock(); +} + + +void +DestroyEvent::post_process() +{ + if (!_node && !_port) { + if (_path == "/") { + _responder->respond_error("You can not destroy the root patch (/)"); + } else { + string msg = string("Could not find object ") + _path + " to destroy"; + _responder->respond_error(msg); + } + } + + if (_patch_node_listnode) { + assert(_node); + _node->deactivate(); + _responder->respond_ok(); + _engine.broadcaster()->bundle_begin(); + if (_disconnect_event) + _disconnect_event->post_process(); + _engine.broadcaster()->send_destroyed(_path); + _engine.broadcaster()->bundle_end(); + _engine.maid()->push(_patch_node_listnode); + } else if (_patch_port_listnode) { + assert(_port); + _responder->respond_ok(); + _engine.broadcaster()->bundle_begin(); + if (_disconnect_event) + _disconnect_event->post_process(); + _engine.broadcaster()->send_destroyed(_path); + _engine.broadcaster()->bundle_end(); + _engine.maid()->push(_patch_port_listnode); + } else { + _responder->respond_error("Unable to destroy object"); + } + + delete _driver_port; +} + + +} // namespace Ingen diff --git a/src/engine/events/DestroyEvent.hpp b/src/engine/events/DestroyEvent.hpp new file mode 100644 index 00000000..b24934f8 --- /dev/null +++ b/src/engine/events/DestroyEvent.hpp @@ -0,0 +1,77 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef DESTROYEVENT_H +#define DESTROYEVENT_H + +#include +#include +#include "QueuedEvent.hpp" +#include "EngineStore.hpp" +#include "PatchImpl.hpp" + +using std::string; + +namespace Raul { + template class Array; + template class ListNode; +} +template class TreeNode; + +namespace Ingen { + +class GraphObjectImpl; +class NodeImpl; +class PortImpl; +class DriverPort; +class DisconnectAllEvent; +class CompiledPatch; + + +/** An event to remove and delete a Node. + * + * \ingroup engine + */ +class DestroyEvent : public QueuedEvent +{ +public: + DestroyEvent(Engine& engine, SharedPtr responder, FrameTime timestamp, QueuedEventSource* source, const string& path, bool block = true); + ~DestroyEvent(); + + void pre_process(); + void execute(ProcessContext& context); + void post_process(); + +private: + Path _path; + EngineStore::iterator _store_iterator; + SharedPtr _node; ///< Non-NULL iff a node + SharedPtr _port; ///< Non-NULL iff a port + DriverPort* _driver_port; + PatchImpl::Nodes::Node* _patch_node_listnode; + Raul::List::Node* _patch_port_listnode; + Raul::Array* _ports_array; ///< New (external) ports for Patch + CompiledPatch* _compiled_patch; ///< Patch's new process order + DisconnectAllEvent* _disconnect_event; + + SharedPtr< Table > > _removed_table; +}; + + +} // namespace Ingen + +#endif // DESTROYEVENT_H diff --git a/src/engine/events/DisablePortMonitoringEvent.cpp b/src/engine/events/DisablePortMonitoringEvent.cpp new file mode 100644 index 00000000..cecc8dfd --- /dev/null +++ b/src/engine/events/DisablePortMonitoringEvent.cpp @@ -0,0 +1,87 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "interface/ClientInterface.hpp" +#include "events/DisablePortMonitoringEvent.hpp" +#include "Responder.hpp" +#include "Engine.hpp" +#include "PortImpl.hpp" +#include "EngineStore.hpp" +#include "ClientBroadcaster.hpp" +#include "AudioBuffer.hpp" + +using std::string; + +namespace Ingen { + + +DisablePortMonitoringEvent::DisablePortMonitoringEvent(Engine& engine, + SharedPtr responder, + SampleCount timestamp, + const std::string& port_path) +: QueuedEvent(engine, responder, timestamp), + _port_path(port_path), + _port(NULL) +{ +} + + +void +DisablePortMonitoringEvent::pre_process() +{ + _port = _engine.engine_store()->find_port(_port_path); + + QueuedEvent::pre_process(); +} + + +void +DisablePortMonitoringEvent::execute(ProcessContext& context) +{ + QueuedEvent::execute(context); + +#if 0 + assert(_time >= start && _time <= end); + + if (_port != NULL && _port->type() == DataType::FLOAT) + _value = ((AudioBuffer*)_port->buffer(0))->value_at(0/*_time - start*/); + else + _port = NULL; // triggers error response +#endif +} + + +void +DisablePortMonitoringEvent::post_process() +{ +#if 0 + string msg; + if (!_port) { + _responder->respond_error("Unable to find port for get_value responder."); + } else if (_responder->client()) { + _responder->respond_ok(); + _responder->client()->control_change(_port_path, _value); + } else { + _responder->respond_error("Unable to find client to send port value"); + } +#endif +} + + +} // namespace Ingen + diff --git a/src/engine/events/DisablePortMonitoringEvent.hpp b/src/engine/events/DisablePortMonitoringEvent.hpp new file mode 100644 index 00000000..7a8e23f7 --- /dev/null +++ b/src/engine/events/DisablePortMonitoringEvent.hpp @@ -0,0 +1,58 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef DISABLEPORTNOTIFICATIONEVENT_H +#define DISABLEPORTNOTIFICATIONEVENT_H + +#include +#include "QueuedEvent.hpp" +#include "types.hpp" + +using std::string; + +namespace Ingen { + +class PortImpl; +namespace Shared { class ClientInterface; } +using Shared::ClientInterface; + + +/** Disable sending of dynamic value change notifications for a port. + * + * \ingroup engine + */ +class DisablePortMonitoringEvent : public QueuedEvent +{ +public: + DisablePortMonitoringEvent(Engine& engine, + SharedPtr responder, + SampleCount timestamp, + const std::string& port_path); + + void pre_process(); + void execute(ProcessContext& context); + void post_process(); + +private: + const std::string _port_path; + Port* _port; +}; + + +} // namespace Ingen + +#endif // DISABLEPORTNOTIFICATIONEVENT_H diff --git a/src/engine/events/DisconnectAllEvent.cpp b/src/engine/events/DisconnectAllEvent.cpp new file mode 100644 index 00000000..77b1b1b3 --- /dev/null +++ b/src/engine/events/DisconnectAllEvent.cpp @@ -0,0 +1,183 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include "ClientBroadcaster.hpp" +#include "ConnectionImpl.hpp" +#include "DisconnectAllEvent.hpp" +#include "DisconnectionEvent.hpp" +#include "Engine.hpp" +#include "InputPort.hpp" +#include "NodeImpl.hpp" +#include "EngineStore.hpp" +#include "OutputPort.hpp" +#include "PatchImpl.hpp" +#include "PortImpl.hpp" +#include "Responder.hpp" +#include "util.hpp" + +namespace Ingen { + + +DisconnectAllEvent::DisconnectAllEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& parent_path, const string& node_path) + : QueuedEvent(engine, responder, timestamp) + , _parent_path(parent_path) + , _path(node_path) + , _parent(NULL) + , _node(NULL) + , _port(NULL) + , _lookup(true) + , _error(NO_ERROR) +{ +} + + +/** Internal version for use by other events. + */ +DisconnectAllEvent::DisconnectAllEvent(Engine& engine, PatchImpl* parent, GraphObjectImpl* object) + : QueuedEvent(engine) + , _parent_path(parent->path()) + , _path(object->path()) + , _parent(parent) + , _node(dynamic_cast(object)) + , _port(dynamic_cast(object)) + , _lookup(false) + , _error(NO_ERROR) +{ +} + + +DisconnectAllEvent::~DisconnectAllEvent() +{ + for (Raul::List::iterator i = _disconnection_events.begin(); i != _disconnection_events.end(); ++i) + delete (*i); +} + + +void +DisconnectAllEvent::pre_process() +{ + if (_lookup) { + _parent = _engine.engine_store()->find_patch(_parent_path); + + if (_parent == NULL) { + _error = PARENT_NOT_FOUND; + QueuedEvent::pre_process(); + return; + } + + GraphObjectImpl* object = _engine.engine_store()->find_object(_path); + + if (object == NULL) { + _error = OBJECT_NOT_FOUND; + QueuedEvent::pre_process(); + return; + } + + if (object->parent_patch() != _parent && object->parent()->parent_patch() != _parent) { + _error = INVALID_PARENT_PATH; + QueuedEvent::pre_process(); + return; + } + + // Only one of these will succeed + _node = dynamic_cast(object); + _port = dynamic_cast(object); + + assert((_node || _port) && !(_node && _port)); + } + + if (_node) { + for (PatchImpl::Connections::const_iterator i = _parent->connections().begin(); + i != _parent->connections().end(); ++i) { + ConnectionImpl* c = (ConnectionImpl*)i->get(); + if ((c->src_port()->parent_node() == _node || c->dst_port()->parent_node() == _node) + && !c->pending_disconnection()) { + DisconnectionEvent* ev = new DisconnectionEvent(_engine, + SharedPtr(new Responder()), _time, c->src_port(), c->dst_port()); + ev->pre_process(); + _disconnection_events.push_back(new Raul::List::Node(ev)); + c->pending_disconnection(true); + } + } + } else { // _port + for (PatchImpl::Connections::const_iterator i = _parent->connections().begin(); + i != _parent->connections().end(); ++i) { + ConnectionImpl* c = (ConnectionImpl*)i->get(); + if ((c->src_port() == _port || c->dst_port() == _port) && !c->pending_disconnection()) { + DisconnectionEvent* ev = new DisconnectionEvent(_engine, + SharedPtr(new Responder()), _time, c->src_port(), c->dst_port()); + ev->pre_process(); + _disconnection_events.push_back(new Raul::List::Node(ev)); + c->pending_disconnection(true); + } + } + } + + QueuedEvent::pre_process(); +} + + +void +DisconnectAllEvent::execute(ProcessContext& context) +{ + QueuedEvent::execute(context); + + if (_error == NO_ERROR) { + for (Raul::List::iterator i = _disconnection_events.begin(); i != _disconnection_events.end(); ++i) + (*i)->execute(context); + } +} + + +void +DisconnectAllEvent::post_process() +{ + if (_error == NO_ERROR) { + if (_responder) + _responder->respond_ok(); + for (Raul::List::iterator i = _disconnection_events.begin(); + i != _disconnection_events.end(); ++i) + (*i)->post_process(); + } else { + if (_responder) { + boost::format fmt("Unable to disconnect %1% (%2%)"); + fmt % _path; + switch (_error) { + case INVALID_PARENT_PATH: + fmt % string("Invalid parent path: ").append(_parent_path); + break; + case PARENT_NOT_FOUND: + fmt % string("Unable to find parent: ").append(_parent_path); + break; + case OBJECT_NOT_FOUND: + fmt % string("Unable to find object"); + default: + break; + } + _responder->respond_error(fmt.str()); + } + } +} + + +} // namespace Ingen + diff --git a/src/engine/events/DisconnectAllEvent.hpp b/src/engine/events/DisconnectAllEvent.hpp new file mode 100644 index 00000000..6b75e6df --- /dev/null +++ b/src/engine/events/DisconnectAllEvent.hpp @@ -0,0 +1,79 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef DISCONNECTNODEEVENT_H +#define DISCONNECTNODEEVENT_H + +#include +#include +#include +#include "QueuedEvent.hpp" + +using std::string; + +namespace Ingen { + +class DisconnectionEvent; +class PatchImpl; +class NodeImpl; +class Connection; +class PortImpl; +class InputPort; +class OutputPort; + + +/** An event to disconnect all connections to a Node. + * + * \ingroup engine + */ +class DisconnectAllEvent : public QueuedEvent +{ +public: + DisconnectAllEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& parent_path, const string& node_path); + DisconnectAllEvent(Engine& engine, PatchImpl* parent, GraphObjectImpl* object); + ~DisconnectAllEvent(); + + void pre_process(); + void execute(ProcessContext& context); + void post_process(); + +private: + enum ErrorType { + NO_ERROR, + INVALID_PARENT_PATH, + PARENT_NOT_FOUND, + OBJECT_NOT_FOUND, + }; + + Raul::Path _parent_path; + Raul::Path _path; + PatchImpl* _parent; + NodeImpl* _node; + PortImpl* _port; + Raul::List _disconnection_events; + + bool _lookup; + bool _disconnect_parent; + + ErrorType _error; +}; + + +} // namespace Ingen + + +#endif // DISCONNECTNODEEVENT_H diff --git a/src/engine/events/DisconnectionEvent.cpp b/src/engine/events/DisconnectionEvent.cpp new file mode 100644 index 00000000..86ad8b4e --- /dev/null +++ b/src/engine/events/DisconnectionEvent.cpp @@ -0,0 +1,213 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "DisconnectionEvent.hpp" +#include +#include +#include +#include "Responder.hpp" +#include "Engine.hpp" +#include "ConnectionImpl.hpp" +#include "InputPort.hpp" +#include "OutputPort.hpp" +#include "PatchImpl.hpp" +#include "ClientBroadcaster.hpp" +#include "PortImpl.hpp" +#include "EngineStore.hpp" + +using std::string; +namespace Ingen { + + +//// DisconnectionEvent //// + + +DisconnectionEvent::DisconnectionEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& src_port_path, const string& dst_port_path) +: QueuedEvent(engine, responder, timestamp), + _src_port_path(src_port_path), + _dst_port_path(dst_port_path), + _patch(NULL), + _src_port(NULL), + _dst_port(NULL), + _lookup(true), + _patch_connection(NULL), + _compiled_patch(NULL), + _error(NO_ERROR) +{ +} + + +DisconnectionEvent::DisconnectionEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, PortImpl* const src_port, PortImpl* const dst_port) +: QueuedEvent(engine, responder, timestamp), + _src_port_path(src_port->path()), + _dst_port_path(dst_port->path()), + _patch(src_port->parent_node()->parent_patch()), + _src_port(src_port), + _dst_port(dst_port), + _lookup(false), + _compiled_patch(NULL), + _error(NO_ERROR) +{ + // FIXME: These break for patch ports.. is that ok? + /*assert(src_port->is_output()); + assert(dst_port->is_input()); + assert(src_port->type() == dst_port->type()); + assert(src_port->parent_node()->parent_patch() + == dst_port->parent_node()->parent_patch()); */ +} + + +void +DisconnectionEvent::pre_process() +{ + if (_lookup) { + if (_src_port_path.parent().parent() != _dst_port_path.parent().parent() + && _src_port_path.parent() != _dst_port_path.parent().parent() + && _src_port_path.parent().parent() != _dst_port_path.parent()) { + _error = PARENT_PATCH_DIFFERENT; + QueuedEvent::pre_process(); + return; + } + + _src_port = _engine.engine_store()->find_port(_src_port_path); + _dst_port = _engine.engine_store()->find_port(_dst_port_path); + } + + if (_src_port == NULL || _dst_port == NULL) { + _error = PORT_NOT_FOUND; + QueuedEvent::pre_process(); + return; + } + + _dst_input_port = dynamic_cast(_dst_port); + _src_output_port = dynamic_cast(_src_port); + assert(_src_output_port); + assert(_dst_input_port); + + NodeImpl* const src_node = _src_port->parent_node(); + NodeImpl* const dst_node = _dst_port->parent_node(); + + // Connection to a patch port from inside the patch + if (src_node->parent_patch() != dst_node->parent_patch()) { + + assert(src_node->parent() == dst_node || dst_node->parent() == src_node); + if (src_node->parent() == dst_node) + _patch = dynamic_cast(dst_node); + else + _patch = dynamic_cast(src_node); + + // Connection from a patch input to a patch output (pass through) + } else if (src_node == dst_node && dynamic_cast(src_node)) { + _patch = dynamic_cast(src_node); + + // Normal connection between nodes with the same parent + } else { + _patch = src_node->parent_patch(); + } + + assert(_patch); + + //if (_dst_input_port->is_connected_to(_src_output_port)) { + if (!_patch->has_connection(_src_output_port, _dst_input_port)) { + _error = NOT_CONNECTED; + QueuedEvent::pre_process(); + return; + } + + if (src_node == NULL || dst_node == NULL) { + _error = PARENTS_NOT_FOUND; + QueuedEvent::pre_process(); + return; + } + + for (Raul::List::iterator i = dst_node->providers()->begin(); i != dst_node->providers()->end(); ++i) + if ((*i) == src_node) { + delete dst_node->providers()->erase(i); + break; + } + + for (Raul::List::iterator i = src_node->dependants()->begin(); i != src_node->dependants()->end(); ++i) + if ((*i) == dst_node) { + delete src_node->dependants()->erase(i); + break; + } + + _patch_connection = _patch->remove_connection(_src_port, _dst_port); + + if (_patch->enabled()) + _compiled_patch = _patch->compile(); + + QueuedEvent::pre_process(); +} + + +void +DisconnectionEvent::execute(ProcessContext& context) +{ + QueuedEvent::execute(context); + + if (_error == NO_ERROR) { + InputPort::Connections::Node* const port_connection + = _dst_input_port->remove_connection(_src_output_port); + + if (port_connection != NULL) { + assert(_patch_connection); + + if (port_connection->elem() != _patch_connection->elem()) { + cerr << "ERROR: Corrupt connections:" << endl; + cerr << "\t" << port_connection->elem() << ": " + << port_connection->elem()->src_port_path() + << " -> " << port_connection->elem()->dst_port_path() << endl + << "!=" << endl + << "\t" << _patch_connection->elem() << ": " + << _patch_connection->elem()->src_port_path() + << " -> " << _patch_connection->elem()->dst_port_path() << endl; + } + assert(port_connection->elem() == _patch_connection->elem()); + + // Destroy list node, which will drop reference to connection itself + _engine.maid()->push(port_connection); + _engine.maid()->push(_patch_connection); + + if (_patch->compiled_patch() != NULL) + _engine.maid()->push(_patch->compiled_patch()); + _patch->compiled_patch(_compiled_patch); + } else { + _error = CONNECTION_NOT_FOUND; + } + } +} + + +void +DisconnectionEvent::post_process() +{ + if (_error == NO_ERROR) { + _responder->respond_ok(); + _engine.broadcaster()->send_disconnection(_src_port->path(), _dst_port->path()); + } else { + // FIXME: better error messages + string msg = "Unable to disconnect "; + msg.append(_src_port_path + " -> " + _dst_port_path); + cerr << "DISCONNECTION ERROR " << (unsigned)_error << endl; + _responder->respond_error(msg); + } +} + + +} // namespace Ingen + diff --git a/src/engine/events/DisconnectionEvent.hpp b/src/engine/events/DisconnectionEvent.hpp new file mode 100644 index 00000000..700febeb --- /dev/null +++ b/src/engine/events/DisconnectionEvent.hpp @@ -0,0 +1,90 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef DISCONNECTIONEVENT_H +#define DISCONNECTIONEVENT_H + +#include +#include +#include "QueuedEvent.hpp" +#include "types.hpp" +#include "PatchImpl.hpp" +using std::string; + +namespace Raul { + template class ListNode; + template class Array; +} + +namespace Ingen { + +class NodeImpl; +class ConnectionImpl; +class MidiMessage; +class PortImpl; +class InputPort; +class OutputPort; +class CompiledPatch; + + +/** Make a Connection between two Ports. + * + * \ingroup engine + */ +class DisconnectionEvent : public QueuedEvent +{ +public: + DisconnectionEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& src_port_path, const string& dst_port_path); + DisconnectionEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, PortImpl* const src_port, PortImpl* const dst_port); + + void pre_process(); + void execute(ProcessContext& context); + void post_process(); + +private: + + enum ErrorType { + NO_ERROR, + PARENT_PATCH_DIFFERENT, + PORT_NOT_FOUND, + TYPE_MISMATCH, + NOT_CONNECTED, + PARENTS_NOT_FOUND, + CONNECTION_NOT_FOUND + }; + + Raul::Path _src_port_path; + Raul::Path _dst_port_path; + + PatchImpl* _patch; + PortImpl* _src_port; + PortImpl* _dst_port; + OutputPort* _src_output_port; + InputPort* _dst_input_port; + + bool _lookup; + + PatchImpl::Connections::Node* _patch_connection; + CompiledPatch* _compiled_patch; ///< New process order for Patch + + ErrorType _error; +}; + + +} // namespace Ingen + +#endif // DISCONNECTIONEVENT_H diff --git a/src/engine/events/EnablePatchEvent.cpp b/src/engine/events/EnablePatchEvent.cpp new file mode 100644 index 00000000..04759cea --- /dev/null +++ b/src/engine/events/EnablePatchEvent.cpp @@ -0,0 +1,86 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "EnablePatchEvent.hpp" +#include "Responder.hpp" +#include "Engine.hpp" +#include "PatchImpl.hpp" +#include "util.hpp" +#include "ClientBroadcaster.hpp" +#include "EngineStore.hpp" + +namespace Ingen { + + +EnablePatchEvent::EnablePatchEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& patch_path, bool enable) +: QueuedEvent(engine, responder, timestamp), + _patch_path(patch_path), + _patch(NULL), + _compiled_patch(NULL), + _enable(enable) +{ +} + + +void +EnablePatchEvent::pre_process() +{ + _patch = _engine.engine_store()->find_patch(_patch_path); + + if (_enable && _patch) { + /* Any event that requires a new process order will set the patch's + * compiled_patch to NULL if it is executed when the patch is not + * active. So, if the CP is NULL, calculate it here */ + if (_patch->compiled_patch() == NULL) + _compiled_patch = _patch->compile(); + } + + QueuedEvent::pre_process(); +} + + +void +EnablePatchEvent::execute(ProcessContext& context) +{ + QueuedEvent::execute(context); + + if (_patch != NULL) { + if (_enable) + _patch->enable(); + else + _patch->disable(); + + if (_enable && _patch->compiled_patch() == NULL) + _patch->compiled_patch(_compiled_patch); + } +} + + +void +EnablePatchEvent::post_process() +{ + if (_patch != NULL) { + _responder->respond_ok(); + _engine.broadcaster()->send_property_change(_patch_path, "ingen:enabled", (bool)_enable); + } else { + _responder->respond_error(string("Patch ") + _patch_path + " not found"); + } +} + + +} // namespace Ingen + diff --git a/src/engine/events/EnablePatchEvent.hpp b/src/engine/events/EnablePatchEvent.hpp new file mode 100644 index 00000000..dad1803a --- /dev/null +++ b/src/engine/events/EnablePatchEvent.hpp @@ -0,0 +1,63 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef ENABLEPATCHEVENT_H +#define ENABLEPATCHEVENT_H + +#include +#include "QueuedEvent.hpp" + +using std::string; + +namespace Raul { template class Array; } + +namespace Ingen { + +class PatchImpl; +class NodeImpl; +class CompiledPatch; + + +/** Enables a patch's DSP processing. + * + * \ingroup engine + */ +class EnablePatchEvent : public QueuedEvent +{ +public: + EnablePatchEvent(Engine& engine, + SharedPtr responder, + SampleCount timestamp, + const string& patch_path, + bool enable); + + void pre_process(); + void execute(ProcessContext& context); + void post_process(); + +private: + string _patch_path; + PatchImpl* _patch; + CompiledPatch* _compiled_patch; // Patch's new process order + bool _enable; +}; + + +} // namespace Ingen + + +#endif // ENABLEPATCHEVENT_H diff --git a/src/engine/events/LoadPluginsEvent.cpp b/src/engine/events/LoadPluginsEvent.cpp new file mode 100644 index 00000000..df5ff5d9 --- /dev/null +++ b/src/engine/events/LoadPluginsEvent.cpp @@ -0,0 +1,56 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "LoadPluginsEvent.hpp" +#include "Responder.hpp" +#include "Engine.hpp" +#include "NodeFactory.hpp" +#include "ClientBroadcaster.hpp" +#include "QueuedEventSource.hpp" + +#include +using std::cerr; + +namespace Ingen { + + +LoadPluginsEvent::LoadPluginsEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, QueuedEventSource* source) +: QueuedEvent(engine, responder, timestamp, true, source) +{ + /* FIXME: Not sure why this has to be blocking, but it fixes some nasty bugs.. */ +} + +void +LoadPluginsEvent::pre_process() +{ + _engine.node_factory()->load_plugins(); + + QueuedEvent::pre_process(); +} + +void +LoadPluginsEvent::post_process() +{ + if (_source) + _source->unblock(); + + _responder->respond_ok(); +} + + +} // namespace Ingen + diff --git a/src/engine/events/LoadPluginsEvent.hpp b/src/engine/events/LoadPluginsEvent.hpp new file mode 100644 index 00000000..cd9a2884 --- /dev/null +++ b/src/engine/events/LoadPluginsEvent.hpp @@ -0,0 +1,46 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LOADPLUGINSEVENT_H +#define LOADPLUGINSEVENT_H + +#include +#include "QueuedEvent.hpp" + +namespace Ingen { + + +/** Loads all plugins into the internal plugin database (in NodeFactory). + * + * \ingroup engine + */ +class LoadPluginsEvent : public QueuedEvent +{ +public: + LoadPluginsEvent(Engine& engine, + SharedPtr responder, + SampleCount timestamp, + QueuedEventSource* source); + + void pre_process(); + void post_process(); +}; + + +} // namespace Ingen + +#endif // LOADPLUGINSEVENT_H diff --git a/src/engine/events/Makefile.am b/src/engine/events/Makefile.am new file mode 100644 index 00000000..9a8cfbd5 --- /dev/null +++ b/src/engine/events/Makefile.am @@ -0,0 +1,63 @@ +MAINTAINERCLEANFILES = Makefile.in + +EXTRA_DIST = \ + AllNotesOffEvent.cpp \ + AllNotesOffEvent.hpp \ + ClearPatchEvent.cpp \ + ClearPatchEvent.hpp \ + ConnectionEvent.cpp \ + ConnectionEvent.hpp \ + CreateNodeEvent.cpp \ + CreateNodeEvent.hpp \ + CreatePatchEvent.cpp \ + CreatePatchEvent.hpp \ + CreatePortEvent.cpp \ + CreatePortEvent.hpp \ + DeactivateEvent.cpp \ + DeactivateEvent.hpp \ + DestroyEvent.cpp \ + DestroyEvent.hpp \ + DisconnectAllEvent.cpp \ + DisconnectAllEvent.hpp \ + DisconnectionEvent.cpp \ + DisconnectionEvent.hpp \ + EnablePatchEvent.cpp \ + EnablePatchEvent.hpp \ + LoadPluginsEvent.cpp \ + LoadPluginsEvent.hpp \ + MidiLearnEvent.cpp \ + MidiLearnEvent.hpp \ + NoteEvent.cpp \ + NoteEvent.hpp \ + PingQueuedEvent.hpp \ + RegisterClientEvent.cpp \ + RegisterClientEvent.hpp \ + RenameEvent.cpp \ + RenameEvent.hpp \ + RequestAllObjectsEvent.cpp \ + RequestAllObjectsEvent.hpp \ + RequestMetadataEvent.cpp \ + RequestMetadataEvent.hpp \ + RequestObjectEvent.cpp \ + RequestObjectEvent.hpp \ + RequestPluginEvent.cpp \ + RequestPluginEvent.hpp \ + RequestPluginsEvent.cpp \ + RequestPluginsEvent.hpp \ + RequestPortValueEvent.cpp \ + RequestPortValueEvent.hpp \ + SendPortActivityEvent.cpp \ + SendPortActivityEvent.hpp \ + SendPortValueEvent.cpp \ + SendPortValueEvent.hpp \ + SetMetadataEvent.cpp \ + SetMetadataEvent.hpp \ + SetPolyphonicEvent.cpp \ + SetPolyphonicEvent.hpp \ + SetPolyphonyEvent.cpp \ + SetPolyphonyEvent.hpp \ + SetPortValueEvent.cpp \ + SetPortValueEvent.hpp \ + UnregisterClientEvent.cpp \ + UnregisterClientEvent.hpp + diff --git a/src/engine/events/MidiLearnEvent.cpp b/src/engine/events/MidiLearnEvent.cpp new file mode 100644 index 00000000..2f37f30d --- /dev/null +++ b/src/engine/events/MidiLearnEvent.cpp @@ -0,0 +1,89 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "MidiLearnEvent.hpp" +#include "Responder.hpp" +#include "Engine.hpp" +#include "EngineStore.hpp" +#include "NodeImpl.hpp" +#include "MidiControlNode.hpp" +#include "ClientBroadcaster.hpp" +#include "PluginImpl.hpp" + +namespace Ingen { + + +// MidiLearnResponseEvent + +void +MidiLearnResponseEvent::post_process() +{ + _engine.broadcaster()->send_port_value(_port_path, _value); +} + + + +// MidiLearnEvent + +MidiLearnEvent::MidiLearnEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& node_path) +: QueuedEvent(engine, responder, timestamp), + _node_path(node_path), + _node(NULL), + _response_event(NULL) +{ +} + + +void +MidiLearnEvent::pre_process() +{ + _node = _engine.engine_store()->find_node(_node_path); + _response_event = new MidiLearnResponseEvent(_engine, _node_path + "/Controller_Number", _time); + + QueuedEvent::pre_process(); +} + + +void +MidiLearnEvent::execute(ProcessContext& context) +{ + QueuedEvent::execute(context); + + // FIXME: this isn't very good at all. + if (_node != NULL && _node->plugin_impl()->type() == Plugin::Internal + && _node->plugin_impl()->uri() == "ingen:control_node") { + ((MidiControlNode*)_node)->learn(_response_event); + } +} + + +void +MidiLearnEvent::post_process() +{ + if (_node != NULL) { + _responder->respond_ok(); + } else { + string msg = "Did not find node '"; + msg.append(_node_path).append("' for MIDI learn."); + _responder->respond_error(msg); + } +} + + +} // namespace Ingen + + diff --git a/src/engine/events/MidiLearnEvent.hpp b/src/engine/events/MidiLearnEvent.hpp new file mode 100644 index 00000000..c0fc4a17 --- /dev/null +++ b/src/engine/events/MidiLearnEvent.hpp @@ -0,0 +1,85 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MIDILEARNEVENT_H +#define MIDILEARNEVENT_H + +#include "QueuedEvent.hpp" +#include "MidiControlNode.hpp" +#include "types.hpp" +#include +using std::string; + +namespace Ingen { + +class NodeImpl; +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(Engine& engine, const string& port_path, SampleCount timestamp) + : Event(engine, SharedPtr(), timestamp), + _port_path(port_path), + _value(0.0f) + {} + + void set_value(Sample val) { _value = val; } + void post_process(); + +private: + string _port_path; + Sample _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(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& node_path); + + void pre_process(); + void execute(ProcessContext& context); + void post_process(); + +private: + const string _node_path; + NodeImpl* _node; + + /// Event to respond with when learned + MidiLearnResponseEvent* _response_event; +}; + + +} // namespace Ingen + +#endif // MIDILEARNEVENT_H diff --git a/src/engine/events/NoteEvent.cpp b/src/engine/events/NoteEvent.cpp new file mode 100644 index 00000000..58842ae6 --- /dev/null +++ b/src/engine/events/NoteEvent.cpp @@ -0,0 +1,102 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "NoteEvent.hpp" +#include "Responder.hpp" +#include "Engine.hpp" +#include "EngineStore.hpp" +#include "NodeImpl.hpp" +#include "MidiNoteNode.hpp" +#include "MidiTriggerNode.hpp" +#include "PluginImpl.hpp" +#include "InternalPlugin.hpp" +#include "ProcessContext.hpp" + +namespace Ingen { + + +/** Note on with Patch explicitly passed. + * + * Used to be triggered by MIDI. Not used anymore. + */ +NoteEvent::NoteEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, NodeImpl* node, bool on, uchar note_num, uchar velocity) +: Event(engine, responder, timestamp), + _node(node), + _on(on), + _note_num(note_num), + _velocity(velocity) +{ +} + + +/** Note on with Node lookup. + * + * Triggered by OSC. + */ +NoteEvent::NoteEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& node_path, bool on, uchar note_num, uchar velocity) +: Event(engine, responder, timestamp), + _node(NULL), + _node_path(node_path), + _on(on), + _note_num(note_num), + _velocity(velocity) +{ +} + + +void +NoteEvent::execute(ProcessContext& context) +{ + Event::execute(context); + assert(_time >= context.start() && _time <= context.end()); + + // Lookup if neccessary + if (!_node) + _node = _engine.engine_store()->find_node(_node_path); + + // FIXME: barf + + if (_node != NULL && _node->plugin()->type() == Plugin::Internal) { + if (_on) { + if (_node->plugin_impl()->uri() == NS_INGEN "note_node") + ((MidiNoteNode*)_node)->note_on(context, _note_num, _velocity, _time); + else if (_node->plugin_impl()->uri() == NS_INGEN "trigger_node") + ((MidiTriggerNode*)_node)->note_on(context, _note_num, _velocity, _time); + } else { + if (_node->plugin_impl()->uri() == NS_INGEN "note_node") + ((MidiNoteNode*)_node)->note_off(context, _note_num, _time); + else if (_node->plugin_impl()->uri() == NS_INGEN "trigger_node") + ((MidiTriggerNode*)_node)->note_off(context, _note_num, _time); + } + } +} + + +void +NoteEvent::post_process() +{ + if (_responder) { + if (_node) + _responder->respond_ok(); + else + _responder->respond_error("Did not find node for note_on"); + } +} + + +} // namespace Ingen + diff --git a/src/engine/events/NoteEvent.hpp b/src/engine/events/NoteEvent.hpp new file mode 100644 index 00000000..31ae9d27 --- /dev/null +++ b/src/engine/events/NoteEvent.hpp @@ -0,0 +1,68 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef NOTEEVENT_H +#define NOTEEVENT_H + +#include "Event.hpp" +#include "types.hpp" +#include +using std::string; + +namespace Ingen { + +class NodeImpl; + + +/** A note on event. + * + * \ingroup engine + */ +class NoteEvent : public Event +{ +public: + NoteEvent(Engine& engine, + SharedPtr responder, + SampleCount timestamp, + NodeImpl* node, + bool on, + uchar note_num, + uchar velocity); + + NoteEvent(Engine& engine, + SharedPtr responder, + SampleCount timestamp, + const string& node_path, + bool on, + uchar note_num, + uchar velocity); + + void execute(ProcessContext& context); + void post_process(); + +private: + NodeImpl* _node; + const string _node_path; + bool _on; + uchar _note_num; + uchar _velocity; +}; + + +} // namespace Ingen + +#endif // NOTEEVENT_H diff --git a/src/engine/events/PingQueuedEvent.hpp b/src/engine/events/PingQueuedEvent.hpp new file mode 100644 index 00000000..08897bfe --- /dev/null +++ b/src/engine/events/PingQueuedEvent.hpp @@ -0,0 +1,48 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PINGQUEUEDEVENT_H +#define PINGQUEUEDEVENT_H + +#include "QueuedEvent.hpp" +#include "types.hpp" +#include "Responder.hpp" + +namespace Ingen { + +class PortImpl; + + +/** A ping that travels through the pre-processed event queue before responding + * (useful for the order guarantee). + * + * \ingroup engine + */ +class PingQueuedEvent : public QueuedEvent +{ +public: + PingQueuedEvent(Engine& engine, SharedPtr responder, SampleCount timestamp) + : QueuedEvent(engine, responder, timestamp) + {} + + void post_process() { _responder->respond_ok(); } +}; + + +} // namespace Ingen + +#endif // PINGQUEUEDEVENT_H diff --git a/src/engine/events/RegisterClientEvent.cpp b/src/engine/events/RegisterClientEvent.cpp new file mode 100644 index 00000000..a8f68e21 --- /dev/null +++ b/src/engine/events/RegisterClientEvent.cpp @@ -0,0 +1,55 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "Responder.hpp" +#include "RegisterClientEvent.hpp" +#include "Engine.hpp" +#include "ClientBroadcaster.hpp" + +namespace Ingen { + + +RegisterClientEvent::RegisterClientEvent(Engine& engine, + SharedPtr responder, + SampleCount timestamp, + const string& uri, + ClientInterface* client) + : QueuedEvent(engine, responder, timestamp) + , _uri(uri) + , _client(client) +{ +} + + +void +RegisterClientEvent::pre_process() +{ + _engine.broadcaster()->register_client(_uri, _client); + + QueuedEvent::pre_process(); +} + + +void +RegisterClientEvent::post_process() +{ + _responder->respond_ok(); +} + + +} // namespace Ingen + diff --git a/src/engine/events/RegisterClientEvent.hpp b/src/engine/events/RegisterClientEvent.hpp new file mode 100644 index 00000000..9e12b5ba --- /dev/null +++ b/src/engine/events/RegisterClientEvent.hpp @@ -0,0 +1,55 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef REGISTERCLIENTEVENT_H +#define REGISTERCLIENTEVENT_H + +#include "QueuedEvent.hpp" +#include "interface/ClientInterface.hpp" +#include +using std::string; +using Ingen::Shared::ClientInterface; +using Ingen::Responder; + +namespace Ingen { + + +/** Registers a new client with the OSC system, so it can receive updates. + * + * \ingroup engine + */ +class RegisterClientEvent : public QueuedEvent +{ +public: + RegisterClientEvent(Engine& engine, + SharedPtr responder, + SampleCount timestamp, + const string& uri, + ClientInterface* client); + + void pre_process(); + void post_process(); + +private: + string _uri; + ClientInterface* _client; +}; + + +} // namespace Ingen + +#endif // REGISTERCLIENTEVENT_H diff --git a/src/engine/events/RenameEvent.cpp b/src/engine/events/RenameEvent.cpp new file mode 100644 index 00000000..164676aa --- /dev/null +++ b/src/engine/events/RenameEvent.cpp @@ -0,0 +1,152 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "ClientBroadcaster.hpp" +#include "Engine.hpp" +#include "NodeImpl.hpp" +#include "EngineStore.hpp" +#include "PatchImpl.hpp" +#include "RenameEvent.hpp" +#include "Responder.hpp" +#include "AudioDriver.hpp" +#include "MidiDriver.hpp" + +using namespace std; + +namespace Ingen { + + +RenameEvent::RenameEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& path, const string& name) +: QueuedEvent(engine, responder, timestamp), + _old_path(path), + _name(name), + _new_path("/"), + _parent_patch(NULL), + _store_iterator(engine.engine_store()->end()), + _error(NO_ERROR) +{ + /* + if (_old_path.parent() == "/") + _new_path = string("/") + _name; + else + _new_path = _old_path.parent() +"/"+ _name;*/ +} + + +RenameEvent::~RenameEvent() +{ +} + + +void +RenameEvent::pre_process() +{ + if ((!Raul::Path::is_valid_name(_name)) || _name.find("/") != string::npos) { + _error = INVALID_NAME; + QueuedEvent::pre_process(); + return; + } + + _new_path = _old_path.parent().base() + _name; + + _store_iterator = _engine.engine_store()->find(_old_path); + if (_store_iterator == _engine.engine_store()->end()) { + _error = OBJECT_NOT_FOUND; + QueuedEvent::pre_process(); + return; + } + + if (_engine.engine_store()->find_object(_new_path)) { + _error = OBJECT_EXISTS; + QueuedEvent::pre_process(); + return; + } + + SharedPtr< Table > > removed + = _engine.engine_store()->remove(_store_iterator); + + assert(removed->size() > 0); + + for (Table >::iterator i = removed->begin(); i != removed->end(); ++i) { + const Path& child_old_path = i->first; + assert(Path::descendant_comparator(_old_path, child_old_path)); + + Path child_new_path; + if (child_old_path == _old_path) + child_new_path = _new_path; + else + child_new_path = _new_path.base() + child_old_path.substr(_old_path.length()+1); + + PtrCast(i->second)->set_path(child_new_path); + i->first = child_new_path; + } + + _engine.engine_store()->add(*removed.get()); + + QueuedEvent::pre_process(); +} + + +void +RenameEvent::execute(ProcessContext& context) +{ + QueuedEvent::execute(context); + + SharedPtr port = PtrCast(_store_iterator->second); + if (port && port->parent()->parent() == NULL) { + DriverPort* driver_port = NULL; + + if (port->type() == DataType::AUDIO) + driver_port = _engine.audio_driver()->driver_port(_new_path); + else if (port->type() == DataType::EVENT) + driver_port = _engine.midi_driver()->driver_port(_new_path); + + if (driver_port) { + cerr << "DRIVER PORT :)!" << endl; + driver_port->set_name(_new_path); + } else { + cerr << "NO DRIVER PORT :(" << endl; + } + } +} + + +void +RenameEvent::post_process() +{ + string msg = "Unable to rename object - "; + + if (_error == NO_ERROR) { + _responder->respond_ok(); + _engine.broadcaster()->send_rename(_old_path, _new_path); + } else { + if (_error == OBJECT_EXISTS) + msg.append("Object already exists at ").append(_new_path); + else if (_error == OBJECT_NOT_FOUND) + msg.append("Could not find object ").append(_old_path); + else if (_error == OBJECT_NOT_RENAMABLE) + msg.append(_old_path).append(" is not renamable"); + else if (_error == INVALID_NAME) + msg.append(_name).append(" is not a valid name"); + + _responder->respond_error(msg); + } +} + + +} // namespace Ingen diff --git a/src/engine/events/RenameEvent.hpp b/src/engine/events/RenameEvent.hpp new file mode 100644 index 00000000..e230c589 --- /dev/null +++ b/src/engine/events/RenameEvent.hpp @@ -0,0 +1,64 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef RENAMEEVENT_H +#define RENAMEEVENT_H + +#include +#include +#include "QueuedEvent.hpp" +#include "EngineStore.hpp" + +using std::string; + +template class TreeNode; +template class ListNode; + +namespace Ingen { + +class PatchImpl; + + +/** An event to change the name of an GraphObjectImpl. + * + * \ingroup engine + */ +class RenameEvent : public QueuedEvent +{ +public: + RenameEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& path, const string& name); + ~RenameEvent(); + + void pre_process(); + void execute(ProcessContext& context); + void post_process(); + +private: + enum ErrorType { NO_ERROR, OBJECT_NOT_FOUND, OBJECT_EXISTS, OBJECT_NOT_RENAMABLE, INVALID_NAME }; + + Path _old_path; + string _name; + Path _new_path; + PatchImpl* _parent_patch; + EngineStore::iterator _store_iterator; + ErrorType _error; +}; + + +} // namespace Ingen + +#endif // RENAMEEVENT_H diff --git a/src/engine/events/RequestAllObjectsEvent.cpp b/src/engine/events/RequestAllObjectsEvent.cpp new file mode 100644 index 00000000..d57080aa --- /dev/null +++ b/src/engine/events/RequestAllObjectsEvent.cpp @@ -0,0 +1,59 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "RequestAllObjectsEvent.hpp" +#include "Responder.hpp" +#include "Engine.hpp" +#include "ObjectSender.hpp" +#include "ClientBroadcaster.hpp" +#include "EngineStore.hpp" + +namespace Ingen { + + +RequestAllObjectsEvent::RequestAllObjectsEvent(Engine& engine, SharedPtr responder, SampleCount timestamp) +: QueuedEvent(engine, responder, timestamp) +{ +} + + +void +RequestAllObjectsEvent::pre_process() +{ + QueuedEvent::pre_process(); +} + + +void +RequestAllObjectsEvent::post_process() +{ + if (_responder->client()) { + _responder->respond_ok(); + + // Everything is a child of the root patch, so this sends it all + PatchImpl* root = _engine.engine_store()->find_patch("/"); + if (root && _responder->client()) + ObjectSender::send_patch(_responder->client(), root, true); + + } else { + _responder->respond_error("Unable to find client to send all objects"); + } +} + + +} // namespace Ingen + diff --git a/src/engine/events/RequestAllObjectsEvent.hpp b/src/engine/events/RequestAllObjectsEvent.hpp new file mode 100644 index 00000000..0537575a --- /dev/null +++ b/src/engine/events/RequestAllObjectsEvent.hpp @@ -0,0 +1,48 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef REQUESTALLOBJECTSEVENT_H +#define REQUESTALLOBJECTSEVENT_H + +#include +#include "QueuedEvent.hpp" +using std::string; + +namespace Ingen { + +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(Engine& engine, SharedPtr responder, SampleCount timestamp); + + void pre_process(); + void post_process(); +}; + + +} // namespace Ingen + +#endif // REQUESTALLOBJECTSEVENT_H diff --git a/src/engine/events/RequestMetadataEvent.cpp b/src/engine/events/RequestMetadataEvent.cpp new file mode 100644 index 00000000..733a6a82 --- /dev/null +++ b/src/engine/events/RequestMetadataEvent.cpp @@ -0,0 +1,85 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "RequestMetadataEvent.hpp" +#include +#include "Responder.hpp" +#include "Engine.hpp" +#include "GraphObjectImpl.hpp" +#include "EngineStore.hpp" +#include "interface/ClientInterface.hpp" +#include "ClientBroadcaster.hpp" +using std::string; + +namespace Ingen { + + +RequestMetadataEvent::RequestMetadataEvent(Engine& engine, + SharedPtr responder, + SampleCount timestamp, + bool property, + const string& node_path, + const string& key) + : QueuedEvent(engine, responder, timestamp) + , _path(node_path) + , _property(property) + , _key(key) + , _object(NULL) +{ +} + + +void +RequestMetadataEvent::pre_process() +{ + if (_responder->client()) { + _object = _engine.engine_store()->find_object(_path); + if (_object == NULL) { + QueuedEvent::pre_process(); + return; + } + } + + if (_property) + _value = _object->get_property(_key); + else + _value = _object->get_variable(_key); + + QueuedEvent::pre_process(); +} + + +void +RequestMetadataEvent::post_process() +{ + if (_responder->client()) { + if (!_object) { + string msg = "Unable to find variable subject "; + msg += _path; + _responder->respond_error(msg); + } else { + _responder->respond_ok(); + _responder->client()->set_variable(_path, _key, _value); + } + } else { + _responder->respond_error("Unknown client"); + } +} + + +} // namespace Ingen + diff --git a/src/engine/events/RequestMetadataEvent.hpp b/src/engine/events/RequestMetadataEvent.hpp new file mode 100644 index 00000000..f6a18dfc --- /dev/null +++ b/src/engine/events/RequestMetadataEvent.hpp @@ -0,0 +1,62 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef REQUESTMETADATAEVENT_H +#define REQUESTMETADATAEVENT_H + +#include +#include "QueuedEvent.hpp" +#include +using std::string; + +namespace Ingen { + +class GraphObjectImpl; +namespace Shared { + class ClientInterface; +} using Shared::ClientInterface; + + +/** A request from a client for a piece of variable. + * + * \ingroup engine + */ +class RequestMetadataEvent : public QueuedEvent +{ +public: + RequestMetadataEvent(Engine& engine, + SharedPtr responder, + SampleCount timestamp, + bool property, + const string& path, + const string& key); + + void pre_process(); + void post_process(); + +private: + string _path; + bool _property; + string _key; + Raul::Atom _value; + GraphObjectImpl* _object; +}; + + +} // namespace Ingen + +#endif // REQUESTMETADATAEVENT_H diff --git a/src/engine/events/RequestObjectEvent.cpp b/src/engine/events/RequestObjectEvent.cpp new file mode 100644 index 00000000..88479482 --- /dev/null +++ b/src/engine/events/RequestObjectEvent.cpp @@ -0,0 +1,98 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "RequestObjectEvent.hpp" +#include +#include "interface/ClientInterface.hpp" +#include "Responder.hpp" +#include "Engine.hpp" +#include "EngineStore.hpp" +#include "ClientBroadcaster.hpp" +#include "PatchImpl.hpp" +#include "NodeImpl.hpp" +#include "PortImpl.hpp" +#include "ObjectSender.hpp" +#include "ProcessContext.hpp" + +using std::string; + +namespace Ingen { + + +RequestObjectEvent::RequestObjectEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& path) +: QueuedEvent(engine, responder, timestamp), + _path(path), + _object(NULL) +{ +} + + +void +RequestObjectEvent::pre_process() +{ + _object = _engine.engine_store()->find_object(_path); + + QueuedEvent::pre_process(); +} + + +void +RequestObjectEvent::execute(ProcessContext& context) +{ + QueuedEvent::execute(context); + assert(_time >= context.start() && _time <= context.end()); +} + + +void +RequestObjectEvent::post_process() +{ + if (!_object) { + _responder->respond_error("Unable to find object requested."); + + } else if (_responder->client()) { + PatchImpl* const patch = dynamic_cast(_object); + if (patch) { + _responder->respond_ok(); + ObjectSender::send_patch(_responder->client(), patch, true); + return; + } + + NodeImpl* const node = dynamic_cast(_object); + if (node) { + _responder->respond_ok(); + ObjectSender::send_node(_responder->client(), node, true); + return; + } + + PortImpl* const port = dynamic_cast(_object); + if (port) { + _responder->respond_ok(); + ObjectSender::send_port(_responder->client(), port); + return; + } + + _responder->respond_error("Object of unknown type requested."); + + } else { + _responder->respond_error("Unable to find client to send object."); + } +} + + +} // namespace Ingen + diff --git a/src/engine/events/RequestObjectEvent.hpp b/src/engine/events/RequestObjectEvent.hpp new file mode 100644 index 00000000..52459ada --- /dev/null +++ b/src/engine/events/RequestObjectEvent.hpp @@ -0,0 +1,55 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef REQUESTOBJECTEVENT_H +#define REQUESTOBJECTEVENT_H + +#include +#include "QueuedEvent.hpp" +#include "types.hpp" + +using std::string; + +namespace Ingen { + +class GraphObjectImpl; +namespace Shared { class ClientInterface; } +using Shared::ClientInterface; + + +/** A request from a client to send the value of a port. + * + * \ingroup engine + */ +class RequestObjectEvent : public QueuedEvent +{ +public: + RequestObjectEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& port_path); + + void pre_process(); + void execute(ProcessContext& context); + void post_process(); + +private: + const string _path; + GraphObjectImpl* _object; +}; + + +} // namespace Ingen + +#endif // REQUESTOBJECTEVENT_H diff --git a/src/engine/events/RequestPluginEvent.cpp b/src/engine/events/RequestPluginEvent.cpp new file mode 100644 index 00000000..36358df7 --- /dev/null +++ b/src/engine/events/RequestPluginEvent.cpp @@ -0,0 +1,79 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "interface/ClientInterface.hpp" +#include "RequestPluginEvent.hpp" +#include "Responder.hpp" +#include "Engine.hpp" +#include "PortImpl.hpp" +#include "EngineStore.hpp" +#include "ClientBroadcaster.hpp" +#include "NodeFactory.hpp" +#include "PluginImpl.hpp" +#include "ProcessContext.hpp" + +using std::string; + +namespace Ingen { + + +RequestPluginEvent::RequestPluginEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& uri) +: QueuedEvent(engine, responder, timestamp), + _uri(uri), + _plugin(NULL) +{ +} + + +void +RequestPluginEvent::pre_process() +{ + _plugin = _engine.node_factory()->plugin(_uri); + + QueuedEvent::pre_process(); +} + + +void +RequestPluginEvent::execute(ProcessContext& context) +{ + QueuedEvent::execute(context); + assert(_time >= context.start() && _time <= context.end()); +} + + +void +RequestPluginEvent::post_process() +{ + if (!_plugin) { + _responder->respond_error("Unable to find plugin requested."); + + } else if (_responder->client()) { + + _responder->respond_ok(); + assert(_plugin->uri() == _uri); + _responder->client()->new_plugin(_uri, _plugin->type_uri(), _plugin->symbol(), _plugin->name()); + + } else { + _responder->respond_error("Unable to find client to send plugin."); + } +} + + +} // namespace Ingen + diff --git a/src/engine/events/RequestPluginEvent.hpp b/src/engine/events/RequestPluginEvent.hpp new file mode 100644 index 00000000..8f936098 --- /dev/null +++ b/src/engine/events/RequestPluginEvent.hpp @@ -0,0 +1,53 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef REQUESTPLUGINEVENT_H +#define REQUESTPLUGINEVENT_H + +#include +#include "QueuedEvent.hpp" +#include "types.hpp" + +using std::string; + +namespace Ingen { + +class PluginImpl; + + +/** A request from a client to send information about a plugin. + * + * \ingroup engine + */ +class RequestPluginEvent : public QueuedEvent +{ +public: + RequestPluginEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& uri); + + void pre_process(); + void execute(ProcessContext& context); + void post_process(); + +private: + const string _uri; + const PluginImpl* _plugin; +}; + + +} // namespace Ingen + +#endif // REQUESTPLUGINEVENT_H diff --git a/src/engine/events/RequestPluginsEvent.cpp b/src/engine/events/RequestPluginsEvent.cpp new file mode 100644 index 00000000..8d7fc1ba --- /dev/null +++ b/src/engine/events/RequestPluginsEvent.cpp @@ -0,0 +1,57 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "RequestPluginsEvent.hpp" +#include "Responder.hpp" +#include "Engine.hpp" +#include "ClientBroadcaster.hpp" +#include "NodeFactory.hpp" + +namespace Ingen { + + +RequestPluginsEvent::RequestPluginsEvent(Engine& engine, SharedPtr responder, SampleCount timestamp) +: QueuedEvent(engine, responder, timestamp) +{ +} + + +void +RequestPluginsEvent::pre_process() +{ + // Take a copy to send in the post processing thread (to avoid problems + // because std::map isn't thread safe) + _plugins = _engine.node_factory()->plugins(); + + QueuedEvent::pre_process(); +} + + +void +RequestPluginsEvent::post_process() +{ + if (_responder->client()) { + _engine.broadcaster()->send_plugins_to(_responder->client(), _plugins); + _responder->respond_ok(); + } else { + _responder->respond_error("Unable to find client to send plugins"); + } +} + + +} // namespace Ingen + diff --git a/src/engine/events/RequestPluginsEvent.hpp b/src/engine/events/RequestPluginsEvent.hpp new file mode 100644 index 00000000..f6b41d7a --- /dev/null +++ b/src/engine/events/RequestPluginsEvent.hpp @@ -0,0 +1,47 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef REQUESTPLUGINSEVENT_H +#define REQUESTPLUGINSEVENT_H + +#include "QueuedEvent.hpp" +#include "NodeFactory.hpp" + +namespace Ingen { + +class Responder; + +/** A request from a client to send notification of all objects (ie refresh). + * + * \ingroup engine + */ +class RequestPluginsEvent : public QueuedEvent +{ +public: + RequestPluginsEvent(Engine& engine, SharedPtr responder, SampleCount timestamp); + + void pre_process(); + void post_process(); + +private: + NodeFactory::Plugins _plugins; +}; + + +} // namespace Ingen + +#endif // REQUESTPLUGINSEVENT_H diff --git a/src/engine/events/RequestPortValueEvent.cpp b/src/engine/events/RequestPortValueEvent.cpp new file mode 100644 index 00000000..025d3700 --- /dev/null +++ b/src/engine/events/RequestPortValueEvent.cpp @@ -0,0 +1,81 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "RequestPortValueEvent.hpp" +#include +#include "interface/ClientInterface.hpp" +#include "Responder.hpp" +#include "Engine.hpp" +#include "PortImpl.hpp" +#include "EngineStore.hpp" +#include "ClientBroadcaster.hpp" +#include "AudioBuffer.hpp" +#include "ProcessContext.hpp" + +using std::string; + +namespace Ingen { + + +RequestPortValueEvent::RequestPortValueEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& port_path) +: QueuedEvent(engine, responder, timestamp), + _port_path(port_path), + _port(NULL), + _value(0.0) +{ +} + + +void +RequestPortValueEvent::pre_process() +{ + _port = _engine.engine_store()->find_port(_port_path); + + QueuedEvent::pre_process(); +} + + +void +RequestPortValueEvent::execute(ProcessContext& context) +{ + QueuedEvent::execute(context); + assert(_time >= context.start() && _time <= context.end()); + + if (_port != NULL && (_port->type() == DataType::CONTROL || _port->type() == DataType::AUDIO)) + _value = ((AudioBuffer*)_port->buffer(0))->value_at(0/*_time - start*/); + else + _port = NULL; // triggers error response +} + + +void +RequestPortValueEvent::post_process() +{ + string msg; + if (!_port) { + _responder->respond_error("Unable to find port for get_value responder."); + } else if (_responder->client()) { + _responder->respond_ok(); + _responder->client()->set_port_value(_port_path, _value); + } else { + _responder->respond_error("Unable to find client to send port value"); + } +} + + +} // namespace Ingen + diff --git a/src/engine/events/RequestPortValueEvent.hpp b/src/engine/events/RequestPortValueEvent.hpp new file mode 100644 index 00000000..dd52c535 --- /dev/null +++ b/src/engine/events/RequestPortValueEvent.hpp @@ -0,0 +1,56 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef REQUESTPORTVALUEEVENT_H +#define REQUESTPORTVALUEEVENT_H + +#include +#include "QueuedEvent.hpp" +#include "types.hpp" + +using std::string; + +namespace Ingen { + +class PortImpl; +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(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& port_path); + + void pre_process(); + void execute(ProcessContext& context); + void post_process(); + +private: + const string _port_path; + PortImpl* _port; + Sample _value; +}; + + +} // namespace Ingen + +#endif // REQUESTPORTVALUEEVENT_H diff --git a/src/engine/events/SendPortActivityEvent.cpp b/src/engine/events/SendPortActivityEvent.cpp new file mode 100644 index 00000000..0ab3abdd --- /dev/null +++ b/src/engine/events/SendPortActivityEvent.cpp @@ -0,0 +1,34 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "SendPortActivityEvent.hpp" +#include "Engine.hpp" +#include "PortImpl.hpp" +#include "ClientBroadcaster.hpp" + +namespace Ingen { + + +void +SendPortActivityEvent::post_process() +{ + _engine.broadcaster()->send_port_activity(_port->path()); +} + + +} // namespace Ingen + diff --git a/src/engine/events/SendPortActivityEvent.hpp b/src/engine/events/SendPortActivityEvent.hpp new file mode 100644 index 00000000..dfbb8a10 --- /dev/null +++ b/src/engine/events/SendPortActivityEvent.hpp @@ -0,0 +1,67 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SENDPORTACTIVITYEVENT_H +#define SENDPORTACTIVITYEVENT_H + +#include +#include "Event.hpp" +#include "types.hpp" +using std::string; + +namespace Ingen { + +class PortImpl; + + +/** A special event used internally to send port activity notification (e.g. + * MIDI event activity) from the audio thread. + * + * This is created in the audio thread (directly in a ringbuffer, not new'd) + * for processing in the post processing thread (unlike normal events which + * are created in the pre-processor thread then run through the audio + * thread). This event's job is done entirely in post_process. + * + * This only really makes sense for message ports. + * + * \ingroup engine + */ +class SendPortActivityEvent : public Event +{ +public: + inline SendPortActivityEvent(Engine& engine, + SampleCount timestamp, + PortImpl* port) + : Event(engine, SharedPtr(), timestamp) + , _port(port) + { + } + + inline void operator=(const SendPortActivityEvent& ev) { + _port = ev._port; + } + + void post_process(); + +private: + PortImpl* _port; +}; + + +} // namespace Ingen + +#endif // SENDPORTACTIVITYEVENT_H diff --git a/src/engine/events/SendPortValueEvent.cpp b/src/engine/events/SendPortValueEvent.cpp new file mode 100644 index 00000000..d3fb0d36 --- /dev/null +++ b/src/engine/events/SendPortValueEvent.cpp @@ -0,0 +1,43 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "SendPortValueEvent.hpp" +#include "Engine.hpp" +#include "PortImpl.hpp" +#include "ClientBroadcaster.hpp" + +using namespace std; + +namespace Ingen { + + +void +SendPortValueEvent::post_process() +{ + // FIXME... + + if (_omni) { + _engine.broadcaster()->send_port_value(_port->path(), _value); + } else { + _engine.broadcaster()->send_port_value(_port->path(), _value); + } +} + + +} // namespace Ingen + diff --git a/src/engine/events/SendPortValueEvent.hpp b/src/engine/events/SendPortValueEvent.hpp new file mode 100644 index 00000000..e8505914 --- /dev/null +++ b/src/engine/events/SendPortValueEvent.hpp @@ -0,0 +1,78 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SENDPORTVALUEEVENT_H +#define SENDPORTVALUEEVENT_H + +#include +#include "Event.hpp" +#include "types.hpp" +using std::string; + +namespace Ingen { + +class PortImpl; + + +/** A special event used internally to send port values from the audio thread. + * + * This is created in the audio thread (using in-place new on a preallocated + * buffer) for processing in the post processing thread (unlike normal events + * which are created in the pre-processor thread then run through the audio + * thread). This event's job is done entirely in post_process. + * + * This only works for control ports right now. + * + * \ingroup engine + */ +class SendPortValueEvent : public Event +{ +public: + inline SendPortValueEvent(Engine& engine, + SampleCount timestamp, + PortImpl* port, + bool omni, + uint32_t voice_num, + Sample value) + : Event(engine, SharedPtr(), timestamp) + , _port(port) + , _omni(omni) + , _voice_num(voice_num) + , _value(value) + { + } + + inline void operator=(const SendPortValueEvent& ev) { + _port = ev._port; + _omni = ev._omni; + _voice_num = ev._voice_num; + _value = ev._value; + } + + void post_process(); + +private: + PortImpl* _port; + bool _omni; + uint32_t _voice_num; + Sample _value; +}; + + +} // namespace Ingen + +#endif // SENDPORTVALUEEVENT_H diff --git a/src/engine/events/SetMetadataEvent.cpp b/src/engine/events/SetMetadataEvent.cpp new file mode 100644 index 00000000..db4bbc30 --- /dev/null +++ b/src/engine/events/SetMetadataEvent.cpp @@ -0,0 +1,114 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "SetMetadataEvent.hpp" +#include +#include +#include "Responder.hpp" +#include "Engine.hpp" +#include "PortImpl.hpp" +#include "ClientBroadcaster.hpp" +#include "GraphObjectImpl.hpp" +#include "EngineStore.hpp" + +using std::string; + +namespace Ingen { + + +SetMetadataEvent::SetMetadataEvent( + Engine& engine, + SharedPtr responder, + SampleCount timestamp, + bool property, + const string& path, + const string& key, + const Atom& value) + : QueuedEvent(engine, responder, timestamp) + , _error(NO_ERROR) + , _special_type(NONE) + , _property(property) + , _path(path) + , _key(key) + , _value(value) + , _object(NULL) +{ +} + + +void +SetMetadataEvent::pre_process() +{ + if (!Path::is_valid(_path)) { + _error = INVALID_PATH; + QueuedEvent::pre_process(); + return; + } + + _object = _engine.engine_store()->find_object(_path); + if (_object == NULL) { + QueuedEvent::pre_process(); + return; + } + + if (_property) + _object->set_property(_key, _value); + else + _object->set_variable(_key, _value); + + if (_key == "ingen:broadcast") { + std::cout << "BROADCAST" << std::endl; + _special_type = ENABLE_BROADCAST; + } + + QueuedEvent::pre_process(); +} + + +void +SetMetadataEvent::execute(ProcessContext& context) +{ + if (_special_type == ENABLE_BROADCAST) { + PortImpl* port = dynamic_cast(_object); + if (port) + port->broadcast(_value.get_bool()); + } + + QueuedEvent::execute(context); + // Do nothing +} + + +void +SetMetadataEvent::post_process() +{ + if (_error == INVALID_PATH) { + _responder->respond_error((boost::format("Invalid path %1% setting %2%") % _path % _key).str()); + } else if (_object == NULL) { + string msg = (boost::format("Unable to find object %1% to set %2%") % _path % _key).str(); + _responder->respond_error(msg); + } else { + _responder->respond_ok(); + if (_property) + _engine.broadcaster()->send_property_change(_path, _key, _value); + else + _engine.broadcaster()->send_variable_change(_path, _key, _value); + } +} + + +} // namespace Ingen diff --git a/src/engine/events/SetMetadataEvent.hpp b/src/engine/events/SetMetadataEvent.hpp new file mode 100644 index 00000000..6b9be81c --- /dev/null +++ b/src/engine/events/SetMetadataEvent.hpp @@ -0,0 +1,65 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SETMETADATAEVENT_H +#define SETMETADATAEVENT_H + +#include +#include "QueuedEvent.hpp" +#include + +using std::string; + +namespace Ingen { + +class GraphObjectImpl; + + +/** An event to set a piece of variable for an GraphObjectImpl. + * + * \ingroup engine + */ +class SetMetadataEvent : public QueuedEvent +{ +public: + SetMetadataEvent(Engine& engine, + SharedPtr responder, + SampleCount timestamp, + bool property, + const string& path, + const string& key, + const Raul::Atom& value); + + void pre_process(); + void execute(ProcessContext& context); + void post_process(); + +private: + enum { NO_ERROR, INVALID_PATH } _error; + enum { NONE, ENABLE_BROADCAST } _special_type; + + bool _property; + string _path; + string _key; + Raul::Atom _value; + GraphObjectImpl* _object; +}; + + +} // namespace Ingen + +#endif // SETMETADATAEVENT_H diff --git a/src/engine/events/SetPolyphonicEvent.cpp b/src/engine/events/SetPolyphonicEvent.cpp new file mode 100644 index 00000000..d1fc6a7c --- /dev/null +++ b/src/engine/events/SetPolyphonicEvent.cpp @@ -0,0 +1,82 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "SetPolyphonicEvent.hpp" +#include "Responder.hpp" +#include "Engine.hpp" +#include "PatchImpl.hpp" +#include "ClientBroadcaster.hpp" +#include "util.hpp" +#include "EngineStore.hpp" +#include "PortImpl.hpp" +#include "NodeImpl.hpp" +#include "ConnectionImpl.hpp" +#include "QueuedEventSource.hpp" + +namespace Ingen { + + +SetPolyphonicEvent::SetPolyphonicEvent(Engine& engine, SharedPtr responder, FrameTime time, QueuedEventSource* source, const string& path, bool poly) +: QueuedEvent(engine, responder, time, true, source), + _path(path), + _object(NULL), + _poly(poly), + _success(false) +{ +} + + +void +SetPolyphonicEvent::pre_process() +{ + _object = _engine.engine_store()->find_object(_path); + + QueuedEvent::pre_process(); +} + + +void +SetPolyphonicEvent::execute(ProcessContext& context) +{ + QueuedEvent::execute(context); + + if (_object) + _success = _object->set_polyphonic(*_engine.maid(), _poly); + + _source->unblock(); +} + + +void +SetPolyphonicEvent::post_process() +{ + if (_object) { + if (_success) { + _responder->respond_ok(); + _engine.broadcaster()->send_property_change(_path, "ingen:polyphonic", _poly); + } else { + _responder->respond_error("Unable to set object as polyphonic"); + } + } else { + _responder->respond_error("Unable to find object to set polyphonic"); + } +} + + +} // namespace Ingen + diff --git a/src/engine/events/SetPolyphonicEvent.hpp b/src/engine/events/SetPolyphonicEvent.hpp new file mode 100644 index 00000000..9079d49f --- /dev/null +++ b/src/engine/events/SetPolyphonicEvent.hpp @@ -0,0 +1,56 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SETPOLYPHONICEVENT_H +#define SETPOLYPHONICEVENT_H + +#include +#include +#include "QueuedEvent.hpp" + +using std::string; + +namespace Ingen { + +class GraphObjectImpl; + + +/** Delete all nodes from a patch. + * + * \ingroup engine + */ +class SetPolyphonicEvent : public QueuedEvent +{ +public: + SetPolyphonicEvent(Engine& engine, SharedPtr responder, FrameTime time, QueuedEventSource* source, const string& path, bool poly); + + void pre_process(); + void execute(ProcessContext& context); + void post_process(); + +private: + const string _path; + GraphObjectImpl* _object; + bool _poly; + bool _success; +}; + + +} // namespace Ingen + + +#endif // SETPOLYPHONICEVENT_H diff --git a/src/engine/events/SetPolyphonyEvent.cpp b/src/engine/events/SetPolyphonyEvent.cpp new file mode 100644 index 00000000..eb6550cb --- /dev/null +++ b/src/engine/events/SetPolyphonyEvent.cpp @@ -0,0 +1,79 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "SetPolyphonyEvent.hpp" +#include "Responder.hpp" +#include "Engine.hpp" +#include "PatchImpl.hpp" +#include "ClientBroadcaster.hpp" +#include "util.hpp" +#include "EngineStore.hpp" +#include "PortImpl.hpp" +#include "NodeImpl.hpp" +#include "ConnectionImpl.hpp" +#include "QueuedEventSource.hpp" + +namespace Ingen { + + +SetPolyphonyEvent::SetPolyphonyEvent(Engine& engine, SharedPtr responder, FrameTime time, QueuedEventSource* source, const string& patch_path, uint32_t poly) +: QueuedEvent(engine, responder, time, true, source), + _patch_path(patch_path), + _patch(NULL), + _poly(poly) +{ +} + + +void +SetPolyphonyEvent::pre_process() +{ + _patch = _engine.engine_store()->find_patch(_patch_path); + if (_patch && _poly > _patch->internal_polyphony()) + _patch->prepare_internal_poly(_poly); + + QueuedEvent::pre_process(); +} + + +void +SetPolyphonyEvent::execute(ProcessContext& context) +{ + QueuedEvent::execute(context); + + if (_patch) + _patch->apply_internal_poly(*_engine.maid(), _poly); + + _source->unblock(); +} + + +void +SetPolyphonyEvent::post_process() +{ + if (_patch) { + _responder->respond_ok(); + _engine.broadcaster()->send_property_change(_patch_path, "ingen:polyphony", (int32_t)_poly); + } else { + _responder->respond_error("Unable to find patch"); + } +} + + +} // namespace Ingen + diff --git a/src/engine/events/SetPolyphonyEvent.hpp b/src/engine/events/SetPolyphonyEvent.hpp new file mode 100644 index 00000000..8aba997a --- /dev/null +++ b/src/engine/events/SetPolyphonyEvent.hpp @@ -0,0 +1,56 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SETPOLYPHONYEVENT_H +#define SETPOLYPHONYEVENT_H + +#include +#include +#include "QueuedEvent.hpp" + +using std::string; + +namespace Ingen { + +class PatchImpl; + + +/** Delete all nodes from a patch. + * + * \ingroup engine + */ +class SetPolyphonyEvent : public QueuedEvent +{ +public: + SetPolyphonyEvent(Engine& engine, SharedPtr responder, FrameTime time, QueuedEventSource* source, const string& patch_path, uint32_t poly); + + void pre_process(); + void execute(ProcessContext& context); + void post_process(); + +private: + const string _patch_path; + PatchImpl* _patch; + const uint32_t _poly; + +}; + + +} // namespace Ingen + + +#endif // SETPOLYPHONYEVENT_H diff --git a/src/engine/events/SetPortValueEvent.cpp b/src/engine/events/SetPortValueEvent.cpp new file mode 100644 index 00000000..a99b9f9a --- /dev/null +++ b/src/engine/events/SetPortValueEvent.cpp @@ -0,0 +1,224 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "Responder.hpp" +#include "SetPortValueEvent.hpp" +#include "Engine.hpp" +#include "PortImpl.hpp" +#include "ClientBroadcaster.hpp" +#include "NodeImpl.hpp" +#include "EngineStore.hpp" +#include "AudioBuffer.hpp" +#include "EventBuffer.hpp" +#include "ProcessContext.hpp" +#include "MessageContext.hpp" + +using namespace std; + +namespace Ingen { + + +/** Omni (all voices) control setting */ +SetPortValueEvent::SetPortValueEvent(Engine& engine, + SharedPtr responder, + bool queued, + SampleCount timestamp, + const string& port_path, + const Raul::Atom& value) + : QueuedEvent(engine, responder, timestamp) + , _queued(queued) + , _omni(true) + , _voice_num(0) + , _port_path(port_path) + , _value(value) + , _port(NULL) + , _error(NO_ERROR) +{ +} + + +/** Voice-specific control setting */ +SetPortValueEvent::SetPortValueEvent(Engine& engine, + SharedPtr responder, + bool queued, + SampleCount timestamp, + uint32_t voice_num, + const string& port_path, + const Raul::Atom& value) + : QueuedEvent(engine, responder, timestamp) + , _queued(queued) + , _omni(false) + , _voice_num(voice_num) + , _port_path(port_path) + , _value(value) + , _port(NULL) + , _error(NO_ERROR) +{ +} + + +SetPortValueEvent::~SetPortValueEvent() +{ +} + + +void +SetPortValueEvent::pre_process() +{ + if (_queued) { + if (_port == NULL) { + if (Path::is_valid(_port_path)) + _port = _engine.engine_store()->find_port(_port_path); + else + _error = ILLEGAL_PATH; + } + + if (_port == NULL && _error == NO_ERROR) + _error = PORT_NOT_FOUND; + } + + // Port is a message context port, set its value and + // call the plugin's message run function once + if (_port && _port->context() == Context::MESSAGE) { + _engine.message_context()->run(_port->parent_node()); + } + + QueuedEvent::pre_process(); +} + + +void +SetPortValueEvent::execute(ProcessContext& context) +{ + Event::execute(context); + assert(_time >= context.start() && _time <= context.end()); + + if (_port && _port->context() == Context::MESSAGE) + return; + + if (_error == NO_ERROR && _port == NULL) { + if (Path::is_valid(_port_path)) + _port = _engine.engine_store()->find_port(_port_path); + else + _error = ILLEGAL_PATH; + } + + if (_port == NULL) { + if (_error == NO_ERROR) + _error = PORT_NOT_FOUND; + /*} else if (_port->buffer(0)->capacity() < _data_size) { + _error = NO_SPACE;*/ + } else { + Buffer* const buf = _port->buffer(0); + AudioBuffer* const abuf = dynamic_cast(buf); + if (abuf) { + if (_value.type() != Atom::FLOAT) { + _error = TYPE_MISMATCH; + return; + } + + if (_omni) { + for (uint32_t i=0; i < _port->poly(); ++i) + ((AudioBuffer*)_port->buffer(i))->set_value( + _value.get_float(), context.start(), _time); + } else { + if (_voice_num < _port->poly()) + ((AudioBuffer*)_port->buffer(_voice_num))->set_value( + _value.get_float(), context.start(), _time); + else + _error = ILLEGAL_VOICE; + } + return; + } + + EventBuffer* const ebuf = dynamic_cast(buf); + + const LV2Features::Feature* f = _engine.world()->lv2_features->feature(LV2_URI_MAP_URI); + LV2URIMap* map = (LV2URIMap*)f->controller; + + // FIXME: eliminate lookups + // FIXME: need a proper prefix system + if (ebuf && _value.type() == Atom::BLOB) { + const uint32_t frames = std::max( + (uint32_t)(_time - context.start()), + ebuf->latest_frames()); + + // Size 0 event, pass it along to the plugin as a typed but empty event + if (_value.data_size() == 0) { + cout << "BANG!" << endl; + const uint32_t type_id = map->uri_to_id(NULL, _value.get_blob_type()); + ebuf->append(frames, 0, type_id, 0, NULL); + _port->raise_set_by_user_flag(); + return; + + } else if (!strcmp(_value.get_blob_type(), "lv2_midi:MidiEvent")) { + const uint32_t type_id = map->uri_to_id(NULL, + "http://lv2plug.in/ns/ext/midi#MidiEvent"); + + ebuf->prepare_write(context.start(), context.nframes()); + // FIXME: use OSC midi type? avoid MIDI over OSC entirely? + ebuf->append(frames, 0, type_id, _value.data_size(), + (const uint8_t*)_value.get_blob()); + _port->raise_set_by_user_flag(); + return; + } + } + + if (_value.type() == Atom::BLOB) + cerr << "WARNING: Unknown value blob type " << _value.get_blob_type() << endl; + else + cerr << "WARNING: Unknown value type " << (int)_value.type() << endl; + } +} + + +void +SetPortValueEvent::post_process() +{ + if (_error == NO_ERROR) { + assert(_port != NULL); + _responder->respond_ok(); + _engine.broadcaster()->send_port_value(_port_path, _value); + + } else if (_error == ILLEGAL_PATH) { + string msg = "Illegal port path \""; + msg.append(_port_path).append("\""); + _responder->respond_error(msg); + + } else if (_error == ILLEGAL_VOICE) { + std::ostringstream ss; + ss << "Illegal voice number " << _voice_num; + _responder->respond_error(ss.str()); + + } else if (_error == PORT_NOT_FOUND) { + string msg = "Unable to find port "; + msg.append(_port_path).append(" for set_port_value"); + _responder->respond_error(msg); + + } else if (_error == NO_SPACE) { + std::ostringstream msg("Attempt to write "); + msg << _value.data_size() << " bytes to " << _port_path << ", with capacity " + << _port->buffer_size() << endl; + _responder->respond_error(msg.str()); + } +} + + +} // namespace Ingen + diff --git a/src/engine/events/SetPortValueEvent.hpp b/src/engine/events/SetPortValueEvent.hpp new file mode 100644 index 00000000..2fc68d9b --- /dev/null +++ b/src/engine/events/SetPortValueEvent.hpp @@ -0,0 +1,80 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SETPORTVALUEEVENT_H +#define SETPORTVALUEEVENT_H + +#include +#include "QueuedEvent.hpp" +#include "types.hpp" +using std::string; + +namespace Ingen { + +class PortImpl; + + +/** An event to change the value of a port. + * + * This event can either be queued or immediate, depending on the queued + * parameter passed to the constructor. It must be passed to the appropriate + * place (ie queued event passed to the event queue and non-queued event + * processed in the audio thread) or nasty things will happen. + * + * \ingroup engine + */ +class SetPortValueEvent : public QueuedEvent +{ +public: + SetPortValueEvent(Engine& engine, + SharedPtr responder, + bool queued, + SampleCount timestamp, + const string& port_path, + const Raul::Atom& value); + + SetPortValueEvent(Engine& engine, + SharedPtr responder, + bool queued, + SampleCount timestamp, + uint32_t voice_num, + const string& port_path, + const Raul::Atom& value); + + ~SetPortValueEvent(); + + void pre_process(); + void execute(ProcessContext& context); + void post_process(); + +private: + enum ErrorType { NO_ERROR, PORT_NOT_FOUND, NO_SPACE, + ILLEGAL_PATH, ILLEGAL_VOICE, TYPE_MISMATCH }; + + bool _queued; + bool _omni; + uint32_t _voice_num; + const string _port_path; + const Raul::Atom _value; + PortImpl* _port; + ErrorType _error; +}; + + +} // namespace Ingen + +#endif // SETPORTVALUEEVENT_H diff --git a/src/engine/events/UnregisterClientEvent.cpp b/src/engine/events/UnregisterClientEvent.cpp new file mode 100644 index 00000000..72ab8048 --- /dev/null +++ b/src/engine/events/UnregisterClientEvent.cpp @@ -0,0 +1,45 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "interface/ClientInterface.hpp" +#include "Responder.hpp" +#include "UnregisterClientEvent.hpp" +#include "Engine.hpp" +#include "ClientBroadcaster.hpp" + +namespace Ingen { + + +UnregisterClientEvent::UnregisterClientEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& uri) +: QueuedEvent(engine, responder, timestamp) +, _uri(uri) +{ +} + + +void +UnregisterClientEvent::post_process() +{ + if (_engine.broadcaster()->unregister_client(_uri)) + _responder->respond_ok(); + else + _responder->respond_error("Unable to unregister client"); +} + + +} // namespace Ingen + diff --git a/src/engine/events/UnregisterClientEvent.hpp b/src/engine/events/UnregisterClientEvent.hpp new file mode 100644 index 00000000..c21bc5da --- /dev/null +++ b/src/engine/events/UnregisterClientEvent.hpp @@ -0,0 +1,54 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef UNREGISTERCLIENTEVENT_H +#define UNREGISTERCLIENTEVENT_H + +#include "QueuedEvent.hpp" +#include +using std::string; + +namespace Ingen { + +namespace Shared { + class ClientInterface; +} +using Shared::ClientInterface; + + +/** Unregisters an OSC client so it no longer receives notifications. + * + * \ingroup engine + */ +class UnregisterClientEvent : public QueuedEvent +{ +public: + UnregisterClientEvent(Engine& engine, + SharedPtr responder, + SampleCount timestamp, + const string& uri); + + void post_process(); + +private: + string _uri; +}; + + +} // namespace Ingen + +#endif // UNREGISTERCLIENTEVENT_H diff --git a/src/engine/jack_compat.h b/src/engine/jack_compat.h new file mode 100644 index 00000000..792148e8 --- /dev/null +++ b/src/engine/jack_compat.h @@ -0,0 +1,56 @@ +/* JACK MIDI API compatibility hacks. + * Copyright (C) 2007 Nedko Arnaudov + * + * This program 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; version 2 of the License + * + * 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 General Public License for more 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef JACK_COMPAT_H +#define JACK_COMPAT_H + + +#if defined(JACK_MIDI_NEEDS_NFRAMES) + +jack_nframes_t +jack_midi_get_event_count_compat( + void * port_buffer) +{ +#if defined(HAVE_OLD_JACK_MIDI) + return jack_midi_port_get_info(port_buffer, 0)->event_count; +#else + return jack_midi_get_event_count(port_buffer, 0); +#endif +} + +#define jack_midi_get_event_count jack_midi_get_event_count_compat + +int +jack_midi_event_get_compat( + jack_midi_event_t * event, + void * port_buffer, + jack_nframes_t event_index) +{ + return jack_midi_event_get(event, port_buffer, event_index, 0); +} + +#define jack_midi_event_get jack_midi_event_get_compat + +#else + +#if defined(HAVE_OLD_JACK_MIDI) +#error "Old (0.102.20) JACK MIDI API needs nframes (autotools probably gone mad)" +#endif + +#endif /* #if defined(JACK_MIDI_NEEDS_NFRAMES) */ + +#endif /* JACK_COMPAT_H */ diff --git a/src/engine/lv2_contexts.h b/src/engine/lv2_contexts.h new file mode 120000 index 00000000..8556a582 --- /dev/null +++ b/src/engine/lv2_contexts.h @@ -0,0 +1 @@ +../../../../lv2/lv2/contexts/lv2_contexts.h \ No newline at end of file diff --git a/src/engine/tuning.hpp b/src/engine/tuning.hpp new file mode 100644 index 00000000..7ec55ae4 --- /dev/null +++ b/src/engine/tuning.hpp @@ -0,0 +1,39 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef TUNING_H +#define TUNING_H + +#include +#include + +namespace Ingen { + +// 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; + +//static const timespec main_rate = { 0, 500000000 }; // 1/2 second +static const timespec main_rate = { 0, 125000000 }; // 1/8 second + + +} // namespace Ingen + +#endif // TUNING_H diff --git a/src/engine/types.hpp b/src/engine/types.hpp new file mode 100644 index 00000000..d242e61d --- /dev/null +++ b/src/engine/types.hpp @@ -0,0 +1,33 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef TYPES_HPP +#define TYPES_HPP + +#include // for NULL, size_t, etc +#include + +typedef unsigned char uchar; +typedef unsigned int uint; +typedef unsigned long ulong; + +typedef jack_default_audio_sample_t Sample; +typedef jack_nframes_t SampleCount; +typedef jack_nframes_t SampleRate; +typedef jack_nframes_t FrameTime; + +#endif // TYPES_HPP diff --git a/src/engine/util.hpp b/src/engine/util.hpp new file mode 100644 index 00000000..8f97beb3 --- /dev/null +++ b/src/engine/util.hpp @@ -0,0 +1,78 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef UTIL_HPP +#define UTIL_HPP + +#include CONFIG_H_PATH +#include +#include + +#include +#ifdef __SSE__ +#include +#endif + +#ifdef USE_ASSEMBLY +# if SIZEOF_VOID_P==8 +# define cpuid(a,b,c,d,n) asm("xchgq %%rbx, %1; cpuid; xchgq %%rbx, %1": "=a" (a), "=r" (b), "=c" (c), "=d" (d) : "a" (n)); +# else +# define cpuid(a,b,c,d,n) asm("xchgl %%ebx, %1; cpuid; xchgl %%ebx, %1": "=a" (a), "=r" (b), "=c" (c), "=d" (d) : "a" (n)); +# endif +#endif + +namespace Ingen { + +/** Set flags to disable denormal processing. + */ +inline void +set_denormal_flags() +{ +#ifdef USE_ASSEMBLY +#ifdef __SSE__ + unsigned long a, b, c, d0, d1; + int stepping, model, family, extfamily; + + cpuid(a,b,c,d1,1); + if (d1 & 1<<25) { /* It has SSE support */ + _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); + family = (a >> 8) & 0xf; + extfamily = (a >> 20) & 0xff; + model = (a >> 4) & 0xf; + stepping = a & 0xf; + cpuid(a,b,c,d0,0); + if (b == 0x756e6547) { /* It's an Intel */ + if (family == 15 && extfamily == 0 && model == 0 && stepping < 7) { + return; + } + } + if (d1 & 1<<26) { /* bit 26, SSE2 support */ + _mm_setcsr(_mm_getcsr() | 0x8040); // set DAZ and FZ bits of MXCSR + //cerr << "Set SSE denormal fix flag." << endl; + } + } else { + std::cerr << "This code has been built with SSE support, but your processor does" + << " not support the SSE instruction set." << std::endl << "Exiting." << std::endl; + exit(EXIT_FAILURE); + } +#endif +#endif +} + +} // namespace Ingen + +#endif // UTIL_HPP diff --git a/src/engine/wscript b/src/engine/wscript new file mode 100644 index 00000000..1ad135d3 --- /dev/null +++ b/src/engine/wscript @@ -0,0 +1,73 @@ +#!/usr/bin/env python +import Params + +def build(bld): + obj = bld.create_obj('cpp', 'shlib') + obj.source = ''' + AudioBuffer.cpp + Buffer.cpp + ClientBroadcaster.cpp + ConnectionImpl.cpp + DuplexPort.cpp + Engine.cpp + EngineStore.cpp + Event.cpp + EventBuffer.cpp + EventSink.cpp + InputPort.cpp + InternalPlugin.cpp + JackAudioDriver.cpp + JackMidiDriver.cpp + LADSPAPlugin.cpp + LV2Info.cpp + LV2Plugin.cpp + MessageContext.cpp + MidiControlNode.cpp + MidiNoteNode.cpp + MidiTriggerNode.cpp + NodeBase.cpp + NodeFactory.cpp + OSCClientSender.cpp + OSCEngineReceiver.cpp + ObjectSender.cpp + OutputPort.cpp + PluginImpl.cpp + PortImpl.cpp + PostProcessor.cpp + ProcessSlave.cpp + QueuedEngineInterface.cpp + QueuedEvent.cpp + QueuedEventSource.cpp + TransportNode.cpp + engine.cpp + events/AllNotesOffEvent.cpp + events/ConnectionEvent.cpp + events/CreateNodeEvent.cpp + events/CreatePortEvent.cpp + events/DeactivateEvent.cpp + events/DestroyEvent.cpp + events/DisconnectAllEvent.cpp + events/DisconnectionEvent.cpp + events/LoadPluginsEvent.cpp + events/MidiLearnEvent.cpp + events/NoteEvent.cpp + events/RegisterClientEvent.cpp + events/RenameEvent.cpp + events/RequestAllObjectsEvent.cpp + events/RequestMetadataEvent.cpp + events/RequestObjectEvent.cpp + events/RequestPluginEvent.cpp + events/RequestPluginsEvent.cpp + events/RequestPortValueEvent.cpp + events/SendPortActivityEvent.cpp + events/SendPortValueEvent.cpp + events/SetMetadataEvent.cpp + events/SetPortValueEvent.cpp + events/UnregisterClientEvent.cpp + ''' + obj.includes = ['..', '../../common', '../..', './events'] + obj.name = 'libingen_engine' + obj.target = 'ingen_engine' + obj.uselib = 'GLIBMM GTHREAD SLV2 JACK LIBLO RAUL REDLANDMM SOUP' + obj.vnum = '0.0.0' + diff --git a/src/gui/App.cpp b/src/gui/App.cpp new file mode 100644 index 00000000..f49d394c --- /dev/null +++ b/src/gui/App.cpp @@ -0,0 +1,440 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include CONFIG_H_PATH +#include "App.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include "module/global.hpp" +#include "module/Module.hpp" +#include "module/World.hpp" +#include "interface/EngineInterface.hpp" +#include "serialisation/serialisation.hpp" +#include "client/ObjectModel.hpp" +#include "client/PatchModel.hpp" +#include "client/ClientStore.hpp" +#include "engine/Engine.hpp" +#include "NodeModule.hpp" +#include "ControlPanel.hpp" +#include "SubpatchModule.hpp" +#include "LoadPluginWindow.hpp" +#include "PatchWindow.hpp" +#include "MessagesWindow.hpp" +#include "GladeFactory.hpp" +#include "PatchTreeWindow.hpp" +#include "Configuration.hpp" +#include "ConnectWindow.hpp" +#include "ThreadedLoader.hpp" +#include "WindowFactory.hpp" +#include "Port.hpp" +#ifdef HAVE_SLV2 +#include +#endif + +using namespace std; +using namespace Ingen::Client; + +namespace Ingen { namespace Client { class PluginModel; } } + +namespace Ingen { +namespace GUI { + +class Port; + + +/// Singleton instance +App* App::_instance = 0; + + +App::App(Ingen::Shared::World* world) + : _configuration(new Configuration()) + , _about_dialog(NULL) + , _window_factory(new WindowFactory()) + , _world(world) + , _enable_signal(true) +{ + Glib::RefPtr glade_xml = GladeFactory::new_glade_reference(); + + glade_xml->get_widget_derived("connect_win", _connect_window); + glade_xml->get_widget_derived("messages_win", _messages_window); + glade_xml->get_widget_derived("patch_tree_win", _patch_tree_window); + glade_xml->get_widget("about_win", _about_dialog); + _about_dialog->property_program_name() = "Ingen"; + + PluginModel::set_rdf_world(*world->rdf_world); + +#ifdef HAVE_SLV2 + PluginModel::set_slv2_world(world->slv2_world); +#endif +} + + +App::~App() +{ +} + +void +App::run(int argc, char** argv, Ingen::Shared::World* world) +{ + Gnome::Canvas::init(); + Gtk::Main main(argc, argv); + + if (!_instance) + _instance = new App(world); + + // Load configuration settings + _instance->configuration()->load_settings(); + _instance->configuration()->apply_settings(); + + // Set default window icon + const Glib::ustring icon_path = INGEN_DATA_DIR "/ingen.svg"; + try { + if (Glib::file_test(icon_path, Glib::FILE_TEST_EXISTS)) + Gtk::Window::set_default_icon_from_file(icon_path); + } catch (Gdk::PixbufError err) { + cerr << "Unable to load window icon " << icon_path << ": " << err.what() << endl; + } + + // Set style for embedded node GUIs + const string rc_style = + "style \"ingen_embedded_node_gui_style\" {" + " bg[NORMAL] = \"#212222\"" + " bg[ACTIVE] = \"#505050\"" + " bg[PRELIGHT] = \"#525454\"" + " bg[SELECTED] = \"#99A0A0\"" + " bg[INSENSITIVE] = \"#F03030\"" + " fg[NORMAL] = \"#FFFFFF\"" + " fg[ACTIVE] = \"#FFFFFF\"" + " fg[PRELIGHT] = \"#FFFFFF\"" + " fg[SELECTED] = \"#FFFFFF\"" + " fg[INSENSITIVE] = \"#FFFFFF\"" + "}\n" + "widget \"*ingen_embedded_node_gui_container*\" style \"ingen_embedded_node_gui_style\"\n"; + + Gtk::RC::parse_string(rc_style); + + App::instance().connect_window()->start(world); + + main.run(); + + cout << "Gtk exiting." << endl; +} + + +void +App::attach(SharedPtr client, + SharedPtr handle) +{ + assert( ! _client); + assert( ! _store); + assert( ! _loader); + + _world->engine->register_client(client.get()); + + _client = client; + _handle = handle; + _store = SharedPtr(new ClientStore(_world->engine, client)); + _loader = SharedPtr(new ThreadedLoader(_world->engine)); + + _patch_tree_window->init(*_store); + + _client->signal_response_error.connect(sigc::mem_fun(this, &App::error_response)); + _client->signal_error.connect(sigc::mem_fun(this, &App::error_message)); +} + + +void +App::detach() +{ + if (_world->engine) { + _window_factory->clear(); + _store->clear(); + + _loader.reset(); + _store.reset(); + _client.reset(); + _handle.reset(); + _world->engine.reset(); + } +} + + +const SharedPtr& +App::serialiser() +{ + if (!_serialiser) { + if (!_world->serialisation_module) + _world->serialisation_module = Ingen::Shared::load_module("ingen_serialisation"); + + if (_world->serialisation_module) + _serialiser = SharedPtr(Ingen::Serialisation::new_serialiser(_world, _store)); + + if (!_serialiser) + cerr << "WARNING: Failed to load ingen_serialisation module, save disabled." << endl; + } + return _serialiser; +} + + +void +App::error_response(int32_t id, const string& str) +{ + error_message(str); +} + + +void +App::error_message(const string& str) +{ + _messages_window->post(str); + + if (!_messages_window->is_visible()) + _messages_window->present(); + + _messages_window->set_urgency_hint(true); +} + + +void +App::port_activity(Port* port) +{ + std::pair inserted = _activity_ports.insert(make_pair(port, false)); + if (inserted.second) + inserted.first->second = false; + + if (port->is_output()) { + for (Port::Connections::const_iterator i = port->connections().begin(); i != port->connections().end(); ++i) { + const SharedPtr dst = PtrCast(i->lock()->dest().lock()); + if (dst) + port_activity(dst.get()); + } + } + + port->set_highlighted(true, false, true, false); +} + + +void +App::activity_port_destroyed(Port* port) +{ + ActivityPorts::iterator i = _activity_ports.find(port); + if (i != _activity_ports.end()) + _activity_ports.erase(i); + + return; +} + + +bool +App::animate() +{ + for (ActivityPorts::iterator i = _activity_ports.begin(); i != _activity_ports.end() ; ) { + ActivityPorts::iterator next = i; + ++next; + + if ((*i).second) { // saw it last time, unhighlight and pop + (*i).first->set_highlighted(false, false, true, false); + _activity_ports.erase(i); + } else { + (*i).second = true; + } + + i = next; + } + + return true; +} + + + +/******** Event Handlers ************/ + + +#if 0 +App::event_load_session() +{ + Gtk::FileChooserDialog* dialog + = new Gtk::FileChooserDialog(*_main_window, "Load Session", Gtk::FILE_CHOOSER_ACTION_OPEN); + + dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); + dialog->add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK); + int result = dialog->run(); + string filename = dialog->get_filename(); + delete dialog; + + cout << result << endl; + + assert(result == Gtk::RESPONSE_OK || result == Gtk::RESPONSE_CANCEL || result == Gtk::RESPONSE_NONE); + + if (result == Gtk::RESPONSE_OK) + //configuration->load_session(filename); + _controller->load_session(filename); +} + + +void +App::event_save_session_as() +{ + Gtk::FileChooserDialog dialog(*_main_window, "Save Session", Gtk::FILE_CHOOSER_ACTION_SAVE); + + /* + Gtk::VBox* box = dialog.get_vbox(); + Gtk::Label warning("Warning: Recursively saving will overwrite any subpatch files \ + without confirmation."); + box->pack_start(warning, false, false, 2); + Gtk::CheckButton recursive_checkbutton("Recursively save all subpatches"); + box->pack_start(recursive_checkbutton, false, false, 0); + recursive_checkbutton.show(); + */ + dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); + dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK); + + int result = dialog.run(); + //bool recursive = recursive_checkbutton.get_active(); + + assert(result == Gtk::RESPONSE_OK || result == Gtk::RESPONSE_CANCEL || result == Gtk::RESPONSE_NONE); + + if (result == Gtk::RESPONSE_OK) { + string filename = dialog.get_filename(); + if (filename.length() < 11 || filename.substr(filename.length()-10) != ".omsession") + filename += ".omsession"; + + bool confirm = false; + std::fstream fin; + fin.open(filename.c_str(), std::ios::in); + if (fin.is_open()) { // File exists + string msg = "File already exists! Are you sure you want to overwrite "; + msg += filename + "?"; + Gtk::MessageDialog confir_dialog(*_main_window, + msg, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_YES_NO, true); + if (confir_dialog.run() == Gtk::RESPONSE_YES) + confirm = true; + else + confirm = false; + } else { // File doesn't exist + confirm = true; + } + fin.close(); + + if (confirm) { + _controller->save_session(filename); + } + } +} +#endif + + +void +App::register_callbacks() +{ + Glib::signal_timeout().connect( + sigc::mem_fun(App::instance(), &App::gtk_main_iteration), 25, G_PRIORITY_DEFAULT); + + Glib::signal_timeout().connect( + sigc::mem_fun(App::instance(), &App::animate), 50, G_PRIORITY_DEFAULT); +} + + +bool +App::gtk_main_iteration() +{ + if (!_client) + return false; + + if (_world->local_engine) { + _world->local_engine->main_iteration(); + } else { + _enable_signal = false; + _client->emit_signals(); + _enable_signal = true; + } + + return true; +} + + +void +App::show_about() +{ + _about_dialog->run(); + _about_dialog->hide(); +} + + +void +App::quit() +{ + Gtk::Main::quit(); +} + + +Glib::RefPtr +App::icon_from_path(const string& path, int size) +{ + /* If weak references to Glib::Objects are needed somewhere else it will + probably be a good idea to create a proper WeakPtr class instead of + using raw pointers, but for a single use this will do. */ + + Glib::RefPtr buf; + if (path.length() == 0) + return buf; + + Icons::iterator iter = _icons.find(make_pair(path, size)); + + if (iter != _icons.end()) { + // we need to reference manually since the RefPtr constructor doesn't do it + iter->second->reference(); + return Glib::RefPtr(iter->second); + } + + try { + buf = Gdk::Pixbuf::create_from_file(path, size, size); + _icons.insert(make_pair(make_pair(path, size), buf.operator->())); + buf->add_destroy_notify_callback(new pair(path, size), + &App::icon_destroyed); + cerr << "Loaded icon " << path << " with size " << size << endl; + } catch (Glib::Error e) { + cerr << "Error loading icon: " << e.what() << endl; + } + return buf; +} + + +void* +App::icon_destroyed(void* data) +{ + pair* p = static_cast*>(data); + cerr << "Destroyed icon " << p->first << " with size " << p->second << endl; + Icons::iterator iter = instance()._icons.find(*p); + if (iter != instance()._icons.end()) + instance()._icons.erase(iter); + + delete p; // allocated in App::icon_from_path + + return NULL; +} + + +} // namespace GUI +} // namespace Ingen + diff --git a/src/gui/App.hpp b/src/gui/App.hpp new file mode 100644 index 00000000..4a4b6580 --- /dev/null +++ b/src/gui/App.hpp @@ -0,0 +1,173 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef INGEN_APP_HPP +#define INGEN_APP_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Ingen { + class Engine; + namespace Shared { + class EngineInterface; + class ClientInterface; + class World; + } + namespace Client { + class PatchModel; + class PluginModel; + class ClientStore; + class SigClientInterface; + } + namespace Serialisation { + class Serialiser; + } +} + +/** \defgroup GUI GTK GUI + */ + +namespace Ingen { +namespace GUI { + +class MessagesWindow; +class PatchCanvas; +class PatchTreeView; +class PatchTreeWindow; +class ConnectWindow; +class Configuration; +class ThreadedLoader; +class WindowFactory; +class Port; + + +/** Singleton master class most everything is contained within. + * + * This is a horrible god-object, but it's shrinking in size as things are + * moved out. Hopefully it will go away entirely some day.. + * + * \ingroup GUI + */ +class App +{ +public: + ~App(); + + void error_message(const std::string& msg); + + void attach(SharedPtr client, + SharedPtr handle=SharedPtr()); + + void detach(); + + void register_callbacks(); + bool gtk_main_iteration(); + + void show_about(); + void quit(); + + void port_activity(Port* port); + void activity_port_destroyed(Port* port); + + bool signal() const { return _enable_signal; } + bool disable_signals() { bool old = _enable_signal; _enable_signal = false; return old; } + void enable_signals(bool b) { _enable_signal = b; } + + ConnectWindow* connect_window() const { return _connect_window; } + MessagesWindow* messages_dialog() const { return _messages_window; } + PatchTreeWindow* patch_tree() const { return _patch_tree_window; } + Configuration* configuration() const { return _configuration; } + WindowFactory* window_factory() const { return _window_factory; } + + Glib::RefPtr icon_from_path(const std::string& path, int size); + + const SharedPtr& engine() const { return _world->engine; } + const SharedPtr& client() const { return _client; } + const SharedPtr& store() const { return _store; } + const SharedPtr& loader() const { return _loader; } + + const SharedPtr& serialiser(); + + static inline App& instance() { assert(_instance); return *_instance; } + + static void run(int argc, char** argv, Ingen::Shared::World* world); + + Ingen::Shared::World* world() { return _world; } + +protected: + + /** This is needed for the icon map. */ + template + struct LexicalCompare { + bool operator()(const std::pair& p1, const std::pair& p2) { + return (p1.first < p2.first) || + ((p1.first == p2.first) && (p1.second < p2.second)); + } + }; + + typedef std::map< std::pair, + Gdk::Pixbuf*, + LexicalCompare > Icons; + Icons _icons; + + App(Ingen::Shared::World* world); + + bool animate(); + void error_response(int32_t id, const std::string& str); + + static void* icon_destroyed(void* data); + + static App* _instance; + + SharedPtr _client; + SharedPtr _handle; + SharedPtr _store; + SharedPtr _serialiser; + SharedPtr _loader; + + Configuration* _configuration; + + ConnectWindow* _connect_window; + MessagesWindow* _messages_window; + PatchTreeWindow* _patch_tree_window; + Gtk::AboutDialog* _about_dialog; + WindowFactory* _window_factory; + + Ingen::Shared::World* _world; + + typedef std::map ActivityPorts; + ActivityPorts _activity_ports; + + bool _enable_signal; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // INGEN_APP_HPP + diff --git a/src/gui/BreadCrumb.hpp b/src/gui/BreadCrumb.hpp new file mode 100644 index 00000000..c3b05e01 --- /dev/null +++ b/src/gui/BreadCrumb.hpp @@ -0,0 +1,81 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef BREADCRUMB_H +#define BREADCRUMB_H + +#include +#include +#include +#include "PatchView.hpp" + +namespace Ingen { +namespace GUI { + + +/** Breadcrumb button in a PatchWindow. + * + * Each Breadcrumb stores a reference to a PatchView for quick switching. + * So, the amount of allocated PatchViews at a given time is equal to the + * number of visible breadcrumbs (which is the perfect cache for GUI + * responsiveness balanced with mem consumption). + * + * \ingroup GUI + */ +class BreadCrumb : public Gtk::ToggleButton +{ +public: + BreadCrumb(const Path& path, SharedPtr view = SharedPtr()) + : _path(path) + , _view(view) + { + assert( !view || view->patch()->path() == path); + set_border_width(0); + set_path(path); + show_all(); + } + + void set_view(SharedPtr view) { + assert( !view || view->patch()->path() == _path); + _view = view; + } + + const Path& path() const { return _path; } + SharedPtr view() const { return _view; } + + void set_path(const Path& path) + { + remove(); + const string text = (path == "/") ? "/" : path.name().c_str(); + Gtk::Label* lab = manage(new Gtk::Label(text)); + lab->set_padding(0, 0); + lab->show(); + add(*lab); + + if (_view && _view->patch()->path() != path) + _view.reset(); + } + +private: + Path _path; + SharedPtr _view; +}; + +} // namespace GUI +} // namespace Ingen + +#endif // BREADCRUMB_H diff --git a/src/gui/BreadCrumbBox.cpp b/src/gui/BreadCrumbBox.cpp new file mode 100644 index 00000000..7edaed9f --- /dev/null +++ b/src/gui/BreadCrumbBox.cpp @@ -0,0 +1,210 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "BreadCrumbBox.hpp" +#include "BreadCrumb.hpp" +#include "App.hpp" +#include "client/SigClientInterface.hpp" +namespace Ingen { +namespace GUI { + + +BreadCrumbBox::BreadCrumbBox() + : Gtk::HBox() + , _active_path("/") + , _full_path("/") + , _enable_signal(true) +{ + App::instance().client()->signal_object_destroyed.connect( + sigc::mem_fun(this, &BreadCrumbBox::object_destroyed)); +} + + +SharedPtr +BreadCrumbBox::view(const Path& path) +{ + for (std::list::const_iterator i = _breadcrumbs.begin(); i != _breadcrumbs.end(); ++i) + if ((*i)->path() == path) + return (*i)->view(); + + return SharedPtr(); +} + + +/** Sets up the crumbs to display @a path. + * + * If @a path is already part of the shown path, it will be selected and the + * children preserved. + */ +void +BreadCrumbBox::build(Path path, SharedPtr view) +{ + bool old_enable_signal = _enable_signal; + _enable_signal = false; + + // Moving to a path we already contain, just switch the active button + if (_breadcrumbs.size() > 0 && (path.is_parent_of(_full_path) || path == _full_path)) { + + for (std::list::iterator i = _breadcrumbs.begin(); i != _breadcrumbs.end(); ++i) { + if ((*i)->path() == path) { + (*i)->set_active(true); + if (!(*i)->view()) + (*i)->set_view(view); + + // views are expensive, having two around for the same patch is a bug + assert((*i)->view() == view); + + } else { + (*i)->set_active(false); + } + } + + _active_path = path; + _enable_signal = old_enable_signal; + + + // Moving to a child of the full path, just append crumbs (preserve view cache) + } else if (_breadcrumbs.size() > 0 && (path.is_child_of(_full_path))) { + + string suffix = path.substr(_full_path.length()); + while (suffix.length() > 0) { + if (suffix[0] == '/') + suffix = suffix.substr(1); + const string name = suffix.substr(0, suffix.find("/")); + _full_path = _full_path.base() + name; + BreadCrumb* but = create_crumb(_full_path, view); + pack_start(*but, false, false, 1); + _breadcrumbs.push_back(but); + but->show(); + if (suffix.find("/") == string::npos) + break; + else + suffix = suffix.substr(suffix.find("/")+1); + } + + for (std::list::iterator i = _breadcrumbs.begin(); i != _breadcrumbs.end(); ++i) + (*i)->set_active(false); + _breadcrumbs.back()->set_active(true); + + + // Rebuild from scratch + // Getting here is bad unless absolutely necessary, since the PatchView cache is lost + } else { + + _full_path = path; + _active_path = path; + + // Empty existing breadcrumbs + for (std::list::iterator i = _breadcrumbs.begin(); i != _breadcrumbs.end(); ++i) + remove(**i); + _breadcrumbs.clear(); + + // Add root + BreadCrumb* root_but = create_crumb("/", view); + pack_start(*root_but, false, false, 1); + _breadcrumbs.push_front(root_but); + root_but->set_active(root_but->path() == _active_path); + + Path working_path = "/"; + string suffix = path.substr(1); + while (suffix.length() > 0) { + if (suffix[0] == '/') + suffix = suffix.substr(1); + const string name = suffix.substr(0, suffix.find("/")); + working_path = working_path.base() + name; + BreadCrumb* but = create_crumb(working_path, view); + pack_start(*but, false, false, 1); + _breadcrumbs.push_back(but); + but->set_active(working_path == _active_path); + but->show(); + if (suffix.find("/") == string::npos) + break; + else + suffix = suffix.substr(suffix.find("/")+1); + } + } + + _enable_signal = old_enable_signal; +} + + +/** Create a new crumb, assigning it a reference to @a view if their paths + * match, otherwise ignoring @a view. + */ +BreadCrumb* +BreadCrumbBox::create_crumb(const Path& path, + SharedPtr view) +{ + BreadCrumb* but = manage(new BreadCrumb(path, + (view && path == view->patch()->path()) ? view : SharedPtr())); + + but->signal_toggled().connect(sigc::bind(sigc::mem_fun( + this, &BreadCrumbBox::breadcrumb_clicked), but)); + + return but; +} + + +void +BreadCrumbBox::breadcrumb_clicked(BreadCrumb* crumb) +{ + if (_enable_signal) { + _enable_signal = false; + + if (!crumb->get_active()) { + // Tried to turn off the current active button, bad user, no cookie + crumb->set_active(true); + } else { + signal_patch_selected.emit(crumb->path(), crumb->view()); + if (crumb->path() != _active_path) + crumb->set_active(false); + } + _enable_signal = true; + } +} + + +void +BreadCrumbBox::object_destroyed(const Path& path) +{ + for (std::list::iterator i = _breadcrumbs.begin(); i != _breadcrumbs.end(); ++i) { + if ((*i)->path() == path) { + // Remove all crumbs after the removed one (inclusive) + for (std::list::iterator j = i; j != _breadcrumbs.end(); ) { + BreadCrumb* bc = *j; + j = _breadcrumbs.erase(j); + remove(*bc); + } + break; + } + } +} + + +void +BreadCrumbBox::object_renamed(const Path& old_path, const Path& new_path) +{ + for (std::list::iterator i = _breadcrumbs.begin(); i != _breadcrumbs.end(); ++i) { + if ((*i)->path() == old_path) + (*i)->set_path(new_path); + } +} + + +} // namespace GUI +} // namespace Ingen + diff --git a/src/gui/BreadCrumbBox.hpp b/src/gui/BreadCrumbBox.hpp new file mode 100644 index 00000000..b5650252 --- /dev/null +++ b/src/gui/BreadCrumbBox.hpp @@ -0,0 +1,70 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef BREADCRUMBBOX_H +#define BREADCRUMBBOX_H + +#include +#include +#include +#include +#include +#include +#include "PatchView.hpp" + +namespace Ingen { +namespace GUI { + +class BreadCrumb; + + +/** Collection of breadcrumb buttons forming a path. + * + * This doubles as a cache for PatchViews. + * + * \ingroup GUI + */ +class BreadCrumbBox : public Gtk::HBox +{ +public: + BreadCrumbBox(); + + SharedPtr view(const Path& path); + + void build(Path path, SharedPtr view); + + sigc::signal > signal_patch_selected; + +private: + BreadCrumb* create_crumb(const Path& path, + SharedPtr view = SharedPtr()); + + void breadcrumb_clicked(BreadCrumb* crumb); + + void object_destroyed(const Path& path); + void object_renamed(const Path& old_path, const Path& new_path); + + Path _active_path; + Path _full_path; + bool _enable_signal; + std::list _breadcrumbs; +}; + +} // namespace GUI +} // namespace Ingen + +#endif // BREADCRUMBBOX_H diff --git a/src/gui/Configuration.cpp b/src/gui/Configuration.cpp new file mode 100644 index 00000000..758dc744 --- /dev/null +++ b/src/gui/Configuration.cpp @@ -0,0 +1,109 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "Configuration.hpp" +#include +#include +#include +#include +#include +#include "client/PortModel.hpp" +#include "client/PluginModel.hpp" +#include "serialisation/Parser.hpp" +#include "App.hpp" + +using namespace std; + +namespace Ingen { +namespace GUI { + +using namespace Ingen::Client; + + +Configuration::Configuration() + // Colours from the Tango palette with modified V and alpha + : _name_style(HUMAN) + , _audio_port_color( 0x244678C0) + , _control_port_color(0x4A8A0EC0) + , _event_port_color( 0x960909C0) +// , _midi_port_color( 0x960909C0) +// , _osc_port_color( 0x5C3566C0) +{ +} + + +Configuration::~Configuration() +{ +} + + +/** Loads settings from the rc file. Passing no parameter will load from + * the default location. + */ +void +Configuration::load_settings(string filename) +{ + /* ... */ +} + + +/** Saves settings to rc file. Passing no parameter will save to the + * default location. + */ +void +Configuration::save_settings(string filename) +{ + /* ... */ +} + + +/** Applies the current loaded settings to whichever parts of the app + * need updating. + */ +void +Configuration::apply_settings() +{ + /* ... */ +} + + +uint32_t +Configuration::get_port_color(const PortModel* p) +{ + assert(p != NULL); + + if (p->type().is_control()) { + return _control_port_color; + } else if (p->type().is_audio()) { + return _audio_port_color; + } else if (p->type().is_event()) { + return _event_port_color; + }/* else if (p->type().is_midi()) { + return _midi_port_color; + } else if (p->type().is_osc()) { + return _osc_port_color; + }*/ + + cerr << "[Configuration] Unknown port type " << p->type().uri() + << ", port will appear black." << endl; + + return 0x000000FF; +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/Configuration.hpp b/src/gui/Configuration.hpp new file mode 100644 index 00000000..124b41c8 --- /dev/null +++ b/src/gui/Configuration.hpp @@ -0,0 +1,80 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CONFIG_H +#define CONFIG_H + +#include + +namespace Ingen { namespace Client { class PortModel; } } +using Ingen::Client::PortModel; +using std::string; + +struct Coord { double x; double y; }; + +namespace Ingen { +namespace GUI { + + +/** Singleton state manager for the entire app. + * + * Stores settings like color preferences, search paths, etc. + * (ie any user-defined preferences to be stoed in the rc file). + * + * \ingroup GUI + */ +class Configuration +{ +public: + Configuration(); + ~Configuration(); + + void load_settings(string filename = ""); + void save_settings(string filename = ""); + + void apply_settings(); + + const string& patch_folder() { return _patch_folder; } + void set_patch_folder(const string& f) { _patch_folder = f; } + + uint32_t get_port_color(const PortModel* pi); + + enum NameStyle { PATH, HUMAN }; + + NameStyle name_style() const { return _name_style; } + void set_name_style(NameStyle s) { _name_style = s; } + +private: + /** Most recent patch folder shown in open dialog */ + string _patch_folder; + + NameStyle _name_style; + + uint32_t _audio_port_color; + uint32_t _control_port_color; + uint32_t _event_port_color; + //uint32_t _midi_port_color; + //uint32_t _osc_port_color; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // CONFIG_H + + diff --git a/src/gui/ConnectWindow.cpp b/src/gui/ConnectWindow.cpp new file mode 100644 index 00000000..5600a0b2 --- /dev/null +++ b/src/gui/ConnectWindow.cpp @@ -0,0 +1,458 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include CONFIG_H_PATH +#include "interface/EngineInterface.hpp" +#include "module/World.hpp" +#include "engine/tuning.hpp" +#include "engine/Engine.hpp" +#include "engine/QueuedEngineInterface.hpp" +#include "client/OSCClientReceiver.hpp" +#include "client/HTTPClientReceiver.hpp" +#include "client/OSCEngineSender.hpp" +#include "client/ThreadedSigClientInterface.hpp" +#include "client/ClientStore.hpp" +#include "client/PatchModel.hpp" +#include "module/Module.hpp" +#include "App.hpp" +#include "WindowFactory.hpp" +#include "ConnectWindow.hpp" +using Ingen::QueuedEngineInterface; +using Ingen::Client::ThreadedSigClientInterface; +using namespace std; + +namespace Ingen { +namespace GUI { + + +// ConnectWindow + + +ConnectWindow::ConnectWindow(BaseObjectType* cobject, const Glib::RefPtr& xml) + : Gtk::Dialog(cobject) + , _xml(xml) + , _mode(CONNECT_REMOTE) + , _ping_id(-1) + , _attached(false) + , _widgets_loaded(false) + , _connect_stage(0) + , _new_engine(NULL) +{ +} + + +void +ConnectWindow::start(Ingen::Shared::World* world) +{ + if (world->local_engine) { + _mode = INTERNAL; + if (_widgets_loaded) + _internal_radio->set_active(true); + } + + set_connected_to(world->engine); + + connect(true); +} + + +void +ConnectWindow::set_connected_to(SharedPtr engine) +{ + App::instance().world()->engine = engine; + + if (!_widgets_loaded) + return; + + if (engine) { + _icon->set(Gtk::Stock::CONNECT, Gtk::ICON_SIZE_LARGE_TOOLBAR); + _progress_bar->set_fraction(1.0); + _progress_label->set_text("Connected to engine"); + _url_entry->set_sensitive(false); + _connect_button->set_sensitive(false); + _disconnect_button->set_label("gtk-disconnect"); + _disconnect_button->set_sensitive(true); + _port_spinbutton->set_sensitive(false); + _launch_radio->set_sensitive(false); + _internal_radio->set_sensitive(false); + } else { + _icon->set(Gtk::Stock::DISCONNECT, Gtk::ICON_SIZE_LARGE_TOOLBAR); + _progress_bar->set_fraction(0.0); + _connect_button->set_sensitive(true); + _disconnect_button->set_sensitive(false); + + if (_new_engine) + _internal_radio->set_sensitive(true); + else + _internal_radio->set_sensitive(false); + + _server_radio->set_sensitive(true); + _launch_radio->set_sensitive(true); + + if (_mode == CONNECT_REMOTE ) + _url_entry->set_sensitive(true); + else if (_mode == LAUNCH_REMOTE ) + _port_spinbutton->set_sensitive(true); + + _progress_label->set_text(string("Disconnected")); + } +} + + +void +ConnectWindow::set_connecting_widget_states() +{ + if (!_widgets_loaded) + return; + + _connect_button->set_sensitive(false); + _disconnect_button->set_label("gtk-cancel"); + _disconnect_button->set_sensitive(true); + _server_radio->set_sensitive(false); + _launch_radio->set_sensitive(false); + _internal_radio->set_sensitive(false); + _url_entry->set_sensitive(false); + _port_spinbutton->set_sensitive(false); +} + + +/** Launch (if applicable) and connect to the Engine. + * + * This will create the EngineInterface and ClientInterface and initialize + * the App with them. + */ +void +ConnectWindow::connect(bool existing) +{ + if (_attached) + _attached = false; + + assert(!App::instance().client()); + + _connect_stage = 0; + set_connecting_widget_states(); + + Ingen::Shared::World* world = App::instance().world(); + + if (_mode == CONNECT_REMOTE) { + if (!existing) { + const string url = (_widgets_loaded ? (string)_url_entry->get_text() : world->engine->uri()); + world->engine = SharedPtr(new OSCEngineSender(url)); + } + + SharedPtr tsci(new ThreadedSigClientInterface(1024)); + SharedPtr client; + + const string& uri = world->engine->uri(); + const string& scheme = uri.substr(0, uri.find(":")); + if (scheme == "osc.udp" || scheme == "osc.tcp") + client = SharedPtr(new OSCClientReceiver(16181, tsci)); // FIXME: port + else if (scheme == "http") + client = SharedPtr(new HTTPClientReceiver(world, uri, tsci)); + + App::instance().attach(tsci, client); + App::instance().register_callbacks(); + + Glib::signal_timeout().connect( + sigc::mem_fun(this, &ConnectWindow::gtk_callback), 40); + + } else if (_mode == LAUNCH_REMOTE) { + + int port = _port_spinbutton->get_value_as_int(); + char port_str[6]; + snprintf(port_str, 6, "%u", port); + const string cmd = string("ingen -e --engine-port=").append(port_str); + + if (Raul::Process::launch(cmd)) { + world->engine = SharedPtr( + new OSCEngineSender(string("osc.udp://localhost:").append(port_str))); + + // FIXME: static args + SharedPtr tsci(new ThreadedSigClientInterface(1024)); + SharedPtr client(new OSCClientReceiver(16181, tsci)); + + App::instance().attach(tsci, client); + App::instance().register_callbacks(); + + Glib::signal_timeout().connect( + sigc::mem_fun(this, &ConnectWindow::gtk_callback), 40); + + } else { + cerr << "Failed to launch ingen process." << endl; + } + + } else if (_mode == INTERNAL) { + Ingen::Shared::World* world = App::instance().world(); + if ( ! world->local_engine) { + assert(_new_engine); + world->local_engine = SharedPtr(_new_engine(world)); + } + + if ( ! world->engine) + world->engine = world->local_engine->new_queued_interface(); + + SharedPtr client(new SigClientInterface()); + + world->local_engine->start_jack_driver(); + world->local_engine->activate(1); // FIXME: parallelism + + App::instance().attach(client); + App::instance().register_callbacks(); + + Glib::signal_timeout().connect( + sigc::mem_fun(this, &ConnectWindow::gtk_callback), 10); + } +} + + +void +ConnectWindow::disconnect() +{ + _connect_stage = -1; + _attached = false; + + App::instance().detach(); + set_connected_to(SharedPtr()); + + if (!_widgets_loaded) + return; + + _progress_bar->set_fraction(0.0); + _connect_button->set_sensitive(false); + _disconnect_button->set_sensitive(false); + + _connect_button->set_sensitive(true); + _disconnect_button->set_sensitive(false); +} + + +void +ConnectWindow::on_show() +{ + if (!_widgets_loaded) { + load_widgets(); + if (_attached) + set_connected_to(App::instance().engine()); + } + + Gtk::Dialog::on_show(); +} + + +void +ConnectWindow::load_widgets() +{ + _xml->get_widget("connect_icon", _icon); + _xml->get_widget("connect_progress_bar", _progress_bar); + _xml->get_widget("connect_progress_label", _progress_label); + _xml->get_widget("connect_server_radiobutton", _server_radio); + _xml->get_widget("connect_url_entry", _url_entry); + _xml->get_widget("connect_launch_radiobutton", _launch_radio); + _xml->get_widget("connect_port_spinbutton", _port_spinbutton); + _xml->get_widget("connect_internal_radiobutton", _internal_radio); + _xml->get_widget("connect_disconnect_button", _disconnect_button); + _xml->get_widget("connect_connect_button", _connect_button); + _xml->get_widget("connect_quit_button", _quit_button); + + _server_radio->signal_toggled().connect(sigc::mem_fun(this, &ConnectWindow::server_toggled)); + _launch_radio->signal_toggled().connect(sigc::mem_fun(this, &ConnectWindow::launch_toggled)); + _internal_radio->signal_clicked().connect(sigc::mem_fun(this, &ConnectWindow::internal_toggled)); + _disconnect_button->signal_clicked().connect(sigc::mem_fun(this, &ConnectWindow::disconnect)); + _connect_button->signal_clicked().connect(sigc::bind( + sigc::mem_fun(this, &ConnectWindow::connect), false)); + _quit_button->signal_clicked().connect(sigc::mem_fun(this, &ConnectWindow::quit)); + + _progress_bar->set_pulse_step(0.01); + _widgets_loaded = true; + + _engine_module = Ingen::Shared::load_module("ingen_engine"); + if (!_engine_module) + cerr << "Unable to load ingen_engine module, internal engine unavailable." << endl; + bool found = _engine_module->get_symbol("new_engine", (void*&)_new_engine); + if (!found) { + cerr << "Unable to find module entry point, internal engine unavailable." << endl; + _engine_module.reset(); + } + + server_toggled(); +} + + +void +ConnectWindow::on_hide() +{ + Gtk::Dialog::on_hide(); + if (!_attached) + Gtk::Main::quit(); +} + + +void +ConnectWindow::quit() +{ + if (_attached) { + Gtk::MessageDialog d(*this, "This will exit the GUI, but the engine will " + "remain running (if it is remote).\n\nAre you sure you want to quit?", + true, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_NONE, true); + d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); + d.add_button(Gtk::Stock::QUIT, Gtk::RESPONSE_CLOSE); + int ret = d.run(); + if (ret == Gtk::RESPONSE_CLOSE) + Gtk::Main::quit(); + } else { + Gtk::Main::quit(); + } +} + + +void +ConnectWindow::server_toggled() +{ + _url_entry->set_sensitive(true); + _port_spinbutton->set_sensitive(false); + _mode = CONNECT_REMOTE; +} + + +void +ConnectWindow::launch_toggled() +{ + _url_entry->set_sensitive(false); + _port_spinbutton->set_sensitive(true); + _mode = LAUNCH_REMOTE; +} + + +void +ConnectWindow::internal_toggled() +{ + _url_entry->set_sensitive(false); + _port_spinbutton->set_sensitive(false); + _mode = INTERNAL; +} + + +bool +ConnectWindow::gtk_callback() +{ + /* If I call this a "state machine" it's not ugly code any more */ + + // Timing stuff for repeated attach attempts + timeval now; + gettimeofday(&now, NULL); + static const timeval start = now; + static timeval last = now; + + // Show if attempted connection goes on for a noticeable amount of time + if (!is_visible()) { + const float ms_since_start = (now.tv_sec - start.tv_sec) * 1000.0f + + (now.tv_usec - start.tv_usec) * 0.001f; + if (ms_since_start > 500) { + present(); + set_connecting_widget_states(); + } + } + + /* Connecting to engine */ + if (_connect_stage == 0) { + + _attached = false; + + assert(App::instance().engine()); + assert(App::instance().client()); + + App::instance().client()->signal_response_ok.connect( + sigc::mem_fun(this, &ConnectWindow::on_response)); + + _ping_id = abs(rand()) / 2 * 2; // avoid -1 + App::instance().engine()->set_next_response_id(_ping_id); + App::instance().engine()->ping(); + + if (_widgets_loaded) { + _progress_label->set_text("Connecting to engine..."); + _progress_bar->set_pulse_step(0.01); + } + + ++_connect_stage; + + } else if (_connect_stage == 1) { + if (_attached || App::instance().client()->enabled()) { + App::instance().engine()->activate(); + ++_connect_stage; + } else { + const float ms_since_last = (now.tv_sec - last.tv_sec) * 1000.0f + + (now.tv_usec - last.tv_usec) * 0.001f; + if (ms_since_last > 1000) { + App::instance().engine()->set_next_response_id(_ping_id); + App::instance().engine()->ping(); + last = now; + } + } + } else if (_connect_stage == 2) { + App::instance().engine()->request_all_objects(); + if (_widgets_loaded) + _progress_label->set_text(string("Requesting root patch...")); + ++_connect_stage; + } else if (_connect_stage == 3) { + if (App::instance().store()->size() > 0) { + SharedPtr root = PtrCast(App::instance().store()->object("/")); + if (root) { + set_connected_to(App::instance().engine()); + App::instance().window_factory()->present_patch(root); + App::instance().engine()->load_plugins(); + if (_widgets_loaded) + _progress_label->set_text(string("Loading plugins...")); + ++_connect_stage; + } + } + } else if (_connect_stage == 4) { + App::instance().engine()->request_plugins(); + hide(); + if (_widgets_loaded) + _progress_label->set_text("Connected to engine"); + _connect_stage = 0; // set ourselves up for next time (if there is one) + return false; // deregister this callback + } + + if (_widgets_loaded) + _progress_bar->pulse(); + + if (_connect_stage == -1) { // we were cancelled + if (_widgets_loaded) { + _icon->set(Gtk::Stock::DISCONNECT, Gtk::ICON_SIZE_LARGE_TOOLBAR); + _progress_bar->set_fraction(0.0); + _connect_button->set_sensitive(true); + _disconnect_button->set_sensitive(false); + _disconnect_button->set_label("gtk-disconnect"); + _progress_label->set_text(string("Disconnected")); + } + return false; + } else { + return true; + } +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/ConnectWindow.hpp b/src/gui/ConnectWindow.hpp new file mode 100644 index 00000000..fb75d4b2 --- /dev/null +++ b/src/gui/ConnectWindow.hpp @@ -0,0 +1,105 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CONNECT_WINDOW_H +#define CONNECT_WINDOW_H + +#include CONFIG_H_PATH + +#ifdef HAVE_SLV2 +#include +#endif + +#include +#include +#include +#include +#include "client/ThreadedSigClientInterface.hpp" +using Ingen::Client::SigClientInterface; + +namespace Ingen { class Engine; class QueuedEngineInterface; } + +namespace Ingen { +namespace GUI { + +class App; + + +/** The initially visible "Connect to engine" window. + * + * This handles actually connecting to the engine and making sure everything + * is ready before really launching the app (eg wait for the root patch). + * + * \ingroup GUI + */ +class ConnectWindow : public Gtk::Dialog +{ +public: + ConnectWindow(BaseObjectType* cobject, const Glib::RefPtr& xml); + + void set_connected_to(SharedPtr engine); + void start(Ingen::Shared::World* world); + void on_response(int32_t id) { _attached = true; } + +private: + enum Mode { CONNECT_REMOTE, LAUNCH_REMOTE, INTERNAL }; + + void server_toggled(); + void launch_toggled(); + void internal_toggled(); + + void disconnect(); + void connect(bool existing); + void quit(); + void on_show(); + void on_hide(); + + void load_widgets(); + void set_connecting_widget_states(); + + bool gtk_callback(); + + const Glib::RefPtr _xml; + + Mode _mode; + int32_t _ping_id; + bool _attached; + + bool _widgets_loaded; + int _connect_stage; + + SharedPtr _engine_module; + Ingen::Engine* (*_new_engine)(Ingen::Shared::World* world); + + Gtk::Image* _icon; + Gtk::ProgressBar* _progress_bar; + Gtk::Label* _progress_label; + Gtk::Entry* _url_entry; + Gtk::RadioButton* _server_radio; + Gtk::SpinButton* _port_spinbutton; + Gtk::RadioButton* _launch_radio; + Gtk::RadioButton* _internal_radio; + Gtk::Button* _disconnect_button; + Gtk::Button* _connect_button; + Gtk::Button* _quit_button; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // CONNECT_WINDOW_H diff --git a/src/gui/Connection.hpp b/src/gui/Connection.hpp new file mode 100644 index 00000000..55378891 --- /dev/null +++ b/src/gui/Connection.hpp @@ -0,0 +1,60 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef GUI_CONNECTION_H +#define GUI_CONNECTION_H + +#include +#include +#include +#include +#include "client/ConnectionModel.hpp" +using Ingen::Client::ConnectionModel; + +namespace Ingen { +namespace GUI { + + +/** A Connection in a Patch. + * + * \ingroup GUI + */ +class Connection : public FlowCanvas::Connection +{ +public: + Connection(boost::shared_ptr canvas, + boost::shared_ptr model, + boost::shared_ptr src, + boost::shared_ptr dst, + uint32_t color) + : FlowCanvas::Connection(canvas, src, dst, color) + , _connection_model(model) + {} + + virtual ~Connection() {} + + SharedPtr model() const { return _connection_model; } + +private: + SharedPtr _connection_model; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // GUI_CONNECTION_H diff --git a/src/gui/ControlPanel.cpp b/src/gui/ControlPanel.cpp new file mode 100644 index 00000000..7da07dbb --- /dev/null +++ b/src/gui/ControlPanel.cpp @@ -0,0 +1,269 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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 alongCont + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "interface/EngineInterface.hpp" +#include "client/PatchModel.hpp" +#include "client/NodeModel.hpp" +#include "client/PortModel.hpp" +#include "client/PluginModel.hpp" +#include "App.hpp" +#include "ControlPanel.hpp" +#include "Controls.hpp" +#include "GladeFactory.hpp" + +using namespace std; + +namespace Ingen { +namespace GUI { + + +ControlPanel::ControlPanel(BaseObjectType* cobject, const Glib::RefPtr& xml) + : Gtk::HBox(cobject) + , _callback_enabled(true) +{ + xml->get_widget("control_panel_controls_box", _control_box); + xml->get_widget("control_panel_voice_controls_box", _voice_control_box); + xml->get_widget("control_panel_all_voices_radio", _all_voices_radio); + xml->get_widget("control_panel_specific_voice_radio", _specific_voice_radio); + xml->get_widget("control_panel_voice_spinbutton", _voice_spinbutton); + + _all_voices_radio->signal_toggled().connect( + sigc::mem_fun(this, &ControlPanel::all_voices_selected)); + + _specific_voice_radio->signal_toggled().connect( + sigc::mem_fun(this, &ControlPanel::specific_voice_selected)); + + show_all(); +} + + +ControlPanel::~ControlPanel() +{ + for (vector::iterator i = _controls.begin(); i != _controls.end(); ++i) + delete (*i); +} + + +void +ControlPanel::init(SharedPtr node, uint32_t poly) +{ + assert(node != NULL); + assert(poly > 0); + + if (node->polyphonic()) { + _voice_spinbutton->set_range(0, poly - 1); + _voice_control_box->show(); + } else { + _voice_control_box->hide(); + } + + for (NodeModel::Ports::const_iterator i = node->ports().begin(); i != node->ports().end(); ++i) { + add_port(*i); + } + + node->signal_property.connect(bind( + sigc::mem_fun(this, &ControlPanel::property_changed), false)); + + if (node->parent()) { + node->signal_property.connect(bind( + sigc::mem_fun(this, &ControlPanel::property_changed), true)); + } else { + cerr << "[ControlPanel] No parent, polyphonic controls disabled" << endl; + } + + _callback_enabled = true; +} + + +Control* +ControlPanel::find_port(const Path& path) const +{ + for (vector::const_iterator cg = _controls.begin(); cg != _controls.end(); ++cg) + if ((*cg)->port_model()->path() == path) + return (*cg); + + return NULL; +} + + +/** Add a control to the panel for the given port. + */ +void +ControlPanel::add_port(SharedPtr pm) +{ + assert(pm); + + // Already have port, don't add another + if (find_port(pm->path()) != NULL) + return; + + // Add port + if (pm->type().is_control() && pm->is_input()) { + Control* control = NULL; + + if (pm->is_toggle()) { + ToggleControl* tc; + Glib::RefPtr xml = GladeFactory::new_glade_reference("toggle_control"); + xml->get_widget_derived("toggle_control", tc); + control = tc; + } else { + SliderControl* sc; + Glib::RefPtr xml = GladeFactory::new_glade_reference("control_strip"); + xml->get_widget_derived("control_strip", sc); + control = sc; + } + + control->init(this, pm); + + if (_controls.size() > 0) + _control_box->pack_start(*Gtk::manage(new Gtk::HSeparator()), false, false, 4); + + _controls.push_back(control); + _control_box->pack_start(*control, false, false, 0); + + control->enable(); + control->show(); + } + + Gtk::Requisition controls_size; + _control_box->size_request(controls_size); + _ideal_size.first = controls_size.width; + _ideal_size.second = controls_size.height; + + Gtk::Requisition voice_size; + _voice_control_box->size_request(voice_size); + _ideal_size.first += voice_size.width; + _ideal_size.second += voice_size.height; + + //cerr << "Setting ideal size to " << _ideal_size.first + // << " x " << _ideal_size.second << endl; +} + + +/** Remove the control for the given port. + */ +void +ControlPanel::remove_port(const Path& path) +{ + bool was_first = false; + for (vector::iterator cg = _controls.begin(); cg != _controls.end(); ++cg) { + if ((*cg)->port_model()->path() == path) { + _control_box->remove(**cg); + if (cg == _controls.begin()) + was_first = true; + _controls.erase(cg); + break; + } + } +} + + +/** Rename the control for the given port. + */ +/* +void +ControlPanel::rename_port(const Path& old_path, const Path& new_path) +{ + for (vector::iterator cg = _controls.begin(); cg != _controls.end(); ++cg) { + if ((*cg)->port_model()->path() == old_path) { + (*cg)->set_name(new_path.name()); + return; + } + } +} +*/ + +#if 0 +/** Enable the control for the given port. + * + * Used when all connections to port are un-made. + */ +void +ControlPanel::enable_port(const Path& path) +{ + for (vector::iterator i = _controls.begin(); i != _controls.end(); ++i) { + if ((*i)->port_model()->path() == path) { + (*i)->enable(); + return; + } + } +} + + +/** Disable the control for the given port. + * + * Used when port is connected. + */ +void +ControlPanel::disable_port(const Path& path) +{ + for (vector::iterator i = _controls.begin(); i != _controls.end(); ++i) { + if ((*i)->port_model()->path() == path) { + (*i)->disable(); + return; + } + } +} +#endif + +/** Callback for Controls to notify this of a change. + */ +void +ControlPanel::value_changed(SharedPtr port, float val) +{ + if (_callback_enabled) { + if (_all_voices_radio->get_active()) { + App::instance().engine()->set_port_value(port->path(), Atom(val)); + port->value(val); + } else { + int voice = _voice_spinbutton->get_value_as_int() - 1; + App::instance().engine()->set_voice_value(port->path(), voice, Atom(val)); + port->value(val); + } + } +} + +void +ControlPanel::all_voices_selected() +{ + _voice_spinbutton->property_sensitive() = false; +} + + +void +ControlPanel::specific_voice_selected() +{ + _voice_spinbutton->property_sensitive() = true; +} + + +void +ControlPanel::property_changed(const std::string& predicate, const Raul::Atom& value, bool parent) +{ + if (!parent && predicate == "ingen:polyphonic" && value.type() == Atom::BOOL) { + if (value.get_bool()) + _voice_control_box->show(); + else + _voice_control_box->hide(); + } else if (parent && predicate == "ingen:polyphony" && value.type() == Atom::INT) { + _voice_spinbutton->set_range(0, value.get_int32() - 1); + } +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/ControlPanel.hpp b/src/gui/ControlPanel.hpp new file mode 100644 index 00000000..8af5a728 --- /dev/null +++ b/src/gui/ControlPanel.hpp @@ -0,0 +1,91 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CONTROLPANEL_H +#define CONTROLPANEL_H + +#include +#include +#include +#include // for pair<> +#include +#include +#include +#include +#include +#include "Controls.hpp" + +namespace Ingen { namespace Client { + class PortModel; + class NodeModel; +} } +using namespace Ingen::Client; + +namespace Ingen { +namespace GUI { + + +/** A group of controls for a node (or patch). + * + * Used by both NodeControlWindow and the main window (for patch controls). + * + * \ingroup GUI + */ +class ControlPanel : public Gtk::HBox { +public: + ControlPanel(BaseObjectType* cobject, const Glib::RefPtr& glade_xml); + virtual ~ControlPanel(); + + void init(SharedPtr node, uint32_t poly); + + Control* find_port(const Path& path) const; + + void add_port(SharedPtr port); + void remove_port(const Path& path); + + void enable_port(const Path& path); + void disable_port(const Path& path); + + size_t num_controls() const { return _controls.size(); } + std::pair ideal_size() const { return _ideal_size; } + + // Callback for Control + void value_changed(SharedPtr port_path, float val); + +private: + void all_voices_selected(); + void specific_voice_selected(); + + void property_changed(const std::string& predicate, const Raul::Atom& value, bool parent); + + bool _callback_enabled; + + std::pair _ideal_size; + + std::vector _controls; + Gtk::VBox* _control_box; + Gtk::Box* _voice_control_box; + Gtk::RadioButton* _all_voices_radio; + Gtk::RadioButton* _specific_voice_radio; + Gtk::SpinButton* _voice_spinbutton; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // CONTROLPANEL_H diff --git a/src/gui/Controls.cpp b/src/gui/Controls.cpp new file mode 100644 index 00000000..cc5383a8 --- /dev/null +++ b/src/gui/Controls.cpp @@ -0,0 +1,467 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include CONFIG_H_PATH + +#include +#include +#include +#include "interface/EngineInterface.hpp" +#include "client/PluginModel.hpp" +#include "client/NodeModel.hpp" +#include "client/PortModel.hpp" +#include "Controls.hpp" +#include "ControlPanel.hpp" +#include "PortPropertiesWindow.hpp" +#include "GladeFactory.hpp" +#include "App.hpp" + +using namespace std; +using namespace Ingen::Client; + +namespace Ingen { +namespace GUI { + + +// ////////////////////// Control ///////////////////////////////// // + +Control::Control(BaseObjectType* cobject, const Glib::RefPtr& glade_xml) + : Gtk::VBox(cobject) + , _control_panel(NULL) + , _enable_signal(false) +{ + Glib::RefPtr menu_xml = GladeFactory::new_glade_reference("port_control_menu"); + menu_xml->get_widget("port_control_menu", _menu); + menu_xml->get_widget("port_control_menu_properties", _menu_properties); + + _menu_properties->signal_activate().connect( + sigc::mem_fun(this, &SliderControl::menu_properties)); +} + + +Control::~Control() +{ + _enable_signal = false; + _control_connection.disconnect(); + _port_model.reset(); +} + + +void +Control::init(ControlPanel* panel, SharedPtr pm) +{ + _control_panel = panel; + _port_model = pm, + + assert(_port_model); + assert(panel); + + _control_connection = pm->signal_value_changed.connect(sigc::mem_fun(this, &Control::set_value)); +} + + +void +Control::menu_properties() +{ + Glib::RefPtr xml = GladeFactory::new_glade_reference(); + + PortPropertiesWindow* window; + xml->get_widget_derived("port_properties_win", window); + window->present(_port_model); +} + + +// ////////////////// SliderControl ////////////////////// // + + +SliderControl::SliderControl(BaseObjectType* cobject, const Glib::RefPtr& xml) + : Control(cobject, xml) + , _enabled(true) +{ + xml->get_widget("control_strip_name_label", _name_label); + xml->get_widget("control_strip_slider", _slider); + xml->get_widget("control_strip_spinner", _value_spinner); +} + + +void +SliderControl::init(ControlPanel* panel, SharedPtr pm) +{ + _enable_signal = false; + _enabled = true; + + Control::init(panel, pm); + + assert(_name_label); + assert(_slider); + + set_name(pm->path().name()); + + _slider->set_draw_value(false); + + signal_button_press_event().connect(sigc::mem_fun(*this, &SliderControl::clicked)); + _slider->signal_button_press_event().connect(sigc::mem_fun(*this, &SliderControl::clicked)); + + _slider->signal_event().connect( + sigc::mem_fun(*this, &SliderControl::slider_pressed)); + + _slider->signal_value_changed().connect( + sigc::mem_fun(*this, &SliderControl::update_value_from_slider)); + + _value_spinner->signal_value_changed().connect( + sigc::mem_fun(*this, &SliderControl::update_value_from_spinner)); + + float min = 0.0f, max = 1.0f; + + boost::shared_ptr parent = PtrCast(_port_model->parent()); + if (parent) + parent->port_value_range(_port_model, min, max); + + if (pm->is_integer() || pm->is_toggle()) { + _slider->set_increments(1, 10); + _value_spinner->set_digits(0); + } else { + _slider->set_increments(0, 0); + } + + pm->signal_variable.connect(sigc::mem_fun(this, &SliderControl::port_variable_change)); + + _slider->set_range(std::min(min, pm->value().get_float()), std::max(max, pm->value().get_float())); + //_value_spinner->set_range(min, max); + + set_value(pm->value()); + + _enable_signal = true; + + show_all(); +} + + +bool +SliderControl::clicked(GdkEventButton* ev) +{ + if (ev->button == 3) { + _menu->popup(ev->button, ev->time); + return true; + } else { + return false; + } +} + + +void +SliderControl::set_value(const Atom& atom) +{ + float val = atom.get_float(); + + if (_port_model->is_integer()) + val = lrintf(val); + + _enable_signal = false; + if (_enabled) { + if (_slider->get_value() != val) { + const Gtk::Adjustment* range = _slider->get_adjustment(); + const float lower = range->get_lower(); + const float upper = range->get_upper(); + if (val < lower || val > upper) + set_range(min(lower, val), max(lower, val)); + _slider->set_value(val); + } + if (_value_spinner->get_value() != val) + _value_spinner->set_value(val); + } + _enable_signal = true; +} + + +void +SliderControl::port_variable_change(const string& key, const Atom& value) +{ + if ( (key == "ingen:minimum") && value.type() == Atom::FLOAT) + set_range(value.get_float(), _slider->get_adjustment()->get_upper()); + else if ( (key == "ingen:maximum") && value.type() == Atom::FLOAT) + set_range(_slider->get_adjustment()->get_lower(), value.get_float()); +} + + +void +SliderControl::set_range(float min, float max) +{ + _slider->set_range(min, max); + //_value_spinner->set_range(min, max); +} + + +void +SliderControl::set_name(const string& name) +{ + string name_label = ""; + name_label += name + ""; + _name_label->set_markup(name_label); +} + + +void +SliderControl::enable() +{ + _slider->property_sensitive() = true; + //_min_spinner->property_sensitive() = true; + //_max_spinner->property_sensitive() = true; + _value_spinner->property_sensitive() = true; + _name_label->property_sensitive() = true; +} + + +void +SliderControl::disable() +{ + _slider->property_sensitive() = false; + //_min_spinner->property_sensitive() = false; + //_max_spinner->property_sensitive() = false; + _value_spinner->property_sensitive() = false; + _name_label->property_sensitive() = false; +} + + +void +SliderControl::update_value_from_slider() +{ + if (_enable_signal) { + float value = _slider->get_value(); + bool change = true; + + _enable_signal = false; + + if (_port_model->is_integer()) { + value = lrintf(value); + if (value == lrintf(_port_model->value().get_float())) + change = false; + } + + if (change) { + _value_spinner->set_value(value); + _control_panel->value_changed(_port_model, value); + } + + _enable_signal = true; + } +} + + +void +SliderControl::update_value_from_spinner() +{ + if (_enable_signal) { + _enable_signal = false; + const float value = _value_spinner->get_value(); + + set_value(value); + + _control_panel->value_changed(_port_model, value); + + //m_port_model->value(value); + _enable_signal = true; + } +} + + +/** Callback for when slider is grabbed so we can ignore set_control + * events for this port (and avoid nasty feedback issues). + */ +bool +SliderControl::slider_pressed(GdkEvent* ev) +{ + //cerr << "Pressed: " << ev->type << endl; + if (ev->type == GDK_BUTTON_PRESS) { + _enabled = false; + //GtkClientInterface::instance()->set_ignore_port(_port_model->path()); + } else if (ev->type == GDK_BUTTON_RELEASE) { + _enabled = true; + //GtkClientInterface::instance()->clear_ignore_port(); + } + + return false; +} + + +// ///////////// IntegerControl ////////////// // + +#if 0 +IntegerControl::IntegerControl(ControlPanel* panel, SharedPtr pm) +: Control(panel, pm), + _enable_signal(false), + _alignment(0.5, 0.5, 0.0, 0.0), + _name_label(pm->path().name()), + _spinner(1.0, 0) +{ + set_name(pm->path().name()); + + _spinner.set_range(-99999, 99999); + _spinner.set_value(_port_model->value()); + _spinner.signal_value_changed().connect( + sigc::mem_fun(*this, &IntegerControl::update_value)); + _spinner.set_increments(1, 10); + + _alignment.add(_spinner); + pack_start(_name_label); + pack_start(_alignment); + + _enable_signal = true; + + show_all(); +} + + +void +IntegerControl::set_name(const string& name) +{ + string name_label = ""; + name_label += name + ""; + _name_label->set_markup(name_label); +} + + +void +IntegerControl::set_value(float val) +{ + //cerr << "[IntegerControl] Setting value to " << val << endl; + _enable_signal = false; + _spinner.set_value(val); + _enable_signal = true; +} + + +void +IntegerControl::enable() +{ + _spinner.property_sensitive() = true; + _name_label->property_sensitive() = true; +} + + +void +IntegerControl::disable() +{ + _spinner.property_sensitive() = false; + _name_label->property_sensitive() = false; +} + + +void +IntegerControl::update_value() +{ + if (_enable_signal) { + float value = _spinner.get_value(); + _control_panel->value_changed(_port_model, value); + //m_port_model->value(value); + } +} +#endif + + +// ///////////// ToggleControl ////////////// // + + +ToggleControl::ToggleControl(BaseObjectType* cobject, const Glib::RefPtr& xml) + : Control(cobject, xml) +{ + xml->get_widget("toggle_control_name_label", _name_label); + xml->get_widget("toggle_control_check", _checkbutton); +} + + +void +ToggleControl::init(ControlPanel* panel, SharedPtr pm) +{ + _enable_signal = false; + + Control::init(panel, pm); + + assert(_name_label); + assert(_checkbutton); + + set_name(pm->path().name()); + + _checkbutton->signal_toggled().connect(sigc::mem_fun(*this, &ToggleControl::toggled)); + set_value(pm->value()); + + _enable_signal = true; + show_all(); +} + + +void +ToggleControl::set_name(const string& name) +{ + string name_label = ""; + name_label += name + ""; + _name_label->set_markup(name_label); +} + + +void +ToggleControl::set_value(const Atom& val) +{ + bool enable = false; + switch (val.type()) { + case Atom::FLOAT: + enable = (val.get_float() != 0.0f); + break; + case Atom::INT: + enable = (val.get_int32() != 0); + break; + case Atom::BOOL: + enable = (val.get_bool()); + default: + cerr << "Unsupported value type for toggle control" << endl; + } + + _enable_signal = false; + _checkbutton->set_active(enable); + _enable_signal = true; +} + + +void +ToggleControl::enable() +{ + _checkbutton->property_sensitive() = true; + _name_label->property_sensitive() = true; +} + + +void +ToggleControl::disable() +{ + _checkbutton->property_sensitive() = false; + _name_label->property_sensitive() = false; +} + + +void +ToggleControl::toggled() +{ + if (_enable_signal) { + float value = _checkbutton->get_active() ? 1.0f : 0.0f; + _control_panel->value_changed(_port_model, value); + //m_port_model->value(value); + } +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/Controls.hpp b/src/gui/Controls.hpp new file mode 100644 index 00000000..8a9c4064 --- /dev/null +++ b/src/gui/Controls.hpp @@ -0,0 +1,164 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CONTROLGROUPS_H +#define CONTROLGROUPS_H + +#include +#include +#include +#include +#include "client/PortModel.hpp" +#include + +namespace Ingen { namespace Client { class PortModel; } } +using namespace Ingen::Client; + +namespace Ingen { +namespace GUI { + +class ControlPanel; + + +/** A group of controls (for a single Port) in a ControlPanel. + * + * \ingroup GUI + */ +class Control : public Gtk::VBox +{ +public: + Control(BaseObjectType* cobject, const Glib::RefPtr& glade_xml); + virtual ~Control(); + + virtual void init(ControlPanel* panel, SharedPtr pm); + + virtual void enable() = 0; + virtual void disable() = 0; + + inline const SharedPtr port_model() const { return _port_model; } + +protected: + virtual void set_value(const Atom& value) = 0; + virtual void set_range(float min, float max) {} + + void menu_properties(); + + ControlPanel* _control_panel; + SharedPtr _port_model; + sigc::connection _control_connection; + bool _enable_signal; + + Gtk::Menu* _menu; + Gtk::MenuItem* _menu_properties; +}; + + +/** A slider for a continuous control. + * + * \ingroup GUI + */ +class SliderControl : public Control +{ +public: + SliderControl(BaseObjectType* cobject, const Glib::RefPtr& glade_xml); + void init(ControlPanel* panel, SharedPtr pm); + + void enable(); + void disable(); + + void set_min(float val); + void set_max(float val); + +private: + void set_name(const string& name); + void set_value(const Atom& value); + void set_range(float min, float max); + + void port_variable_change(const string& key, const Raul::Atom& value); + + void update_range(); + void update_value_from_slider(); + void update_value_from_spinner(); + + bool slider_pressed(GdkEvent* ev); + bool clicked(GdkEventButton* ev); + + bool _enabled; + + Gtk::Label* _name_label; + Gtk::SpinButton* _value_spinner; + Gtk::HScale* _slider; +}; + + +#if 0 + +/** A spinbutton for integer controls. + * + * \ingroup GUI + */ +class IntegerControl : public Control +{ +public: + IntegerControl(ControlPanel* panel, SharedPtr pm); + + void enable(); + void disable(); + +private: + void set_name(const string& name); + void set_value(float val); + + void update_value(); + + bool _enable_signal; + Gtk::Alignment _alignment; + Gtk::Label _name_label; + Gtk::SpinButton _spinner; +}; +#endif + + +/** A radio button for toggle controls. + * + * \ingroup GUI + */ +class ToggleControl : public Control +{ +public: + ToggleControl(BaseObjectType* cobject, const Glib::RefPtr& xml); + + void init(ControlPanel* panel, SharedPtr pm); + + void enable(); + void disable(); + +private: + void set_name(const string& name); + void set_value(const Atom& value); + + void toggled(); + + Gtk::Label* _name_label; + Gtk::CheckButton* _checkbutton; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // CONTROLGROUPS_H diff --git a/src/gui/GladeFactory.cpp b/src/gui/GladeFactory.cpp new file mode 100644 index 00000000..2b4d3ec9 --- /dev/null +++ b/src/gui/GladeFactory.cpp @@ -0,0 +1,76 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "GladeFactory.hpp" +#include +#include + +using namespace std; + +namespace Ingen { +namespace GUI { + + +Glib::ustring GladeFactory::glade_filename = ""; + + +void +GladeFactory::find_glade_file() +{ + char* env_path = getenv("INGEN_GLADE_PATH"); + if (env_path) + glade_filename = env_path; + else + glade_filename = "./ingen_gui.glade"; + + ifstream fs(glade_filename.c_str()); + if (fs.fail()) { // didn't find it, check INGEN_DATA_DIR + fs.clear(); + glade_filename = INGEN_DATA_DIR; + glade_filename += "/ingen_gui.glade"; + + fs.open(glade_filename.c_str()); + if (fs.fail()) { + cerr << "[GladeFactory] Unable to find ingen_gui.glade in current directory or " << INGEN_DATA_DIR << "." << endl; + throw; + } + fs.close(); + } + cerr << "[GladeFactory] Loading widgets from " << glade_filename.c_str() << endl; +} + + +Glib::RefPtr +GladeFactory::new_glade_reference(const string& toplevel_widget) +{ + if (glade_filename == "") + find_glade_file(); + + try { + if (toplevel_widget == "") + return Gnome::Glade::Xml::create(glade_filename); + else + return Gnome::Glade::Xml::create(glade_filename, toplevel_widget.c_str()); + } catch (const Gnome::Glade::XmlError& ex) { + cerr << ex.what() << endl; + throw ex; + } +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/GladeFactory.hpp b/src/gui/GladeFactory.hpp new file mode 100644 index 00000000..6da2da5f --- /dev/null +++ b/src/gui/GladeFactory.hpp @@ -0,0 +1,47 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef GLADEFACTORY_H +#define GLADEFACTORY_H + +#include +#include + +namespace Ingen { +namespace GUI { + + +/** Creates glade references, so various objects can create widgets. + * Purely static. + * + * \ingroup GUI + */ +class GladeFactory { +public: + static Glib::RefPtr + new_glade_reference(const std::string& toplevel_widget = ""); + +private: + static void find_glade_file(); + static Glib::ustring glade_filename; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // GLADEFACTORY_H diff --git a/src/gui/LoadPatchWindow.cpp b/src/gui/LoadPatchWindow.cpp new file mode 100644 index 00000000..70d3ec5b --- /dev/null +++ b/src/gui/LoadPatchWindow.cpp @@ -0,0 +1,153 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include "LoadPatchWindow.hpp" +#include "interface/EngineInterface.hpp" +#include "client/PatchModel.hpp" +#include "App.hpp" +#include "Configuration.hpp" +#include "ThreadedLoader.hpp" + +using namespace Ingen::Serialisation; +using boost::optional; +using namespace std; + +namespace Ingen { +namespace GUI { + + +LoadPatchWindow::LoadPatchWindow(BaseObjectType* cobject, const Glib::RefPtr& xml) +: Gtk::FileChooserDialog(cobject), + _replace(true) +{ + xml->get_widget("load_patch_poly_from_current_radio", _poly_from_current_radio); + xml->get_widget("load_patch_poly_from_file_radio", _poly_from_file_radio); + xml->get_widget("load_patch_poly_from_user_radio", _poly_from_user_radio); + xml->get_widget("load_patch_poly_spinbutton", _poly_spinbutton); + xml->get_widget("load_patch_ok_button", _ok_button); + xml->get_widget("load_patch_cancel_button", _cancel_button); + + _poly_from_current_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadPatchWindow::poly_from_file_selected)); + _poly_from_file_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadPatchWindow::poly_from_file_selected)); + _poly_from_user_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadPatchWindow::poly_from_user_selected)); + _ok_button->signal_clicked().connect(sigc::mem_fun(this, &LoadPatchWindow::ok_clicked)); + _cancel_button->signal_clicked().connect(sigc::mem_fun(this, &LoadPatchWindow::cancel_clicked)); + + _poly_from_current_radio->set_active(true); + + Gtk::FileFilter filt; + filt.add_pattern("*.om"); + filt.set_name("Om patch files (XML, DEPRECATED) (*.om)"); + filt.add_pattern("*.ingen.ttl"); + filt.set_name("Ingen patch files (RDF, *.ingen.ttl)"); + set_filter(filt); + + // Add global examples directory to "shortcut folders" (bookmarks) + string examples_dir = INGEN_DATA_DIR; + examples_dir.append("/patches"); + DIR* d = opendir(examples_dir.c_str()); + if (d != NULL) + add_shortcut_folder(examples_dir); +} + + +void +LoadPatchWindow::present(SharedPtr patch, GraphObject::Variables data) +{ + set_patch(patch); + _initial_data = data; + Gtk::Window::present(); +} + + +/** Sets the patch controller for this window and initializes everything. + * + * This function MUST be called before using the window in any way! + */ +void +LoadPatchWindow::set_patch(SharedPtr patch) +{ + _patch = patch; +} + + +void +LoadPatchWindow::on_show() +{ + if (App::instance().configuration()->patch_folder().length() > 0) + set_current_folder(App::instance().configuration()->patch_folder()); + Gtk::FileChooserDialog::on_show(); +} + + +///// Event Handlers ////// + + +void +LoadPatchWindow::poly_from_file_selected() +{ + _poly_spinbutton->property_sensitive() = false; +} + + +void +LoadPatchWindow::poly_from_user_selected() +{ + _poly_spinbutton->property_sensitive() = true; +} + + +void +LoadPatchWindow::ok_clicked() +{ + if (!_patch) + return; + + // If unset load_patch will load value + optional parent; + optional symbol; + + if (_poly_from_user_radio->get_active()) + _initial_data.insert(make_pair("ingen:polyphony", _poly_spinbutton->get_value_as_int())); + + if (_replace) + App::instance().engine()->clear_patch(_patch->path()); + + //if (_patch->path() != "/") + // parent = _patch->path().parent(); + parent = _patch->path(); + + _patch.reset(); + hide(); + + App::instance().loader()->load_patch(true, get_uri(), "/", + _initial_data, parent, symbol); +} + + +void +LoadPatchWindow::cancel_clicked() +{ + hide(); +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/LoadPatchWindow.hpp b/src/gui/LoadPatchWindow.hpp new file mode 100644 index 00000000..4f3521e5 --- /dev/null +++ b/src/gui/LoadPatchWindow.hpp @@ -0,0 +1,82 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LOADPATCHWINDOW_H +#define LOADPATCHWINDOW_H + +#include +#include +#include +#include "interface/GraphObject.hpp" +#include "client/PluginModel.hpp" +#include "client/PatchModel.hpp" +using Ingen::Client::PatchModel; +using namespace Ingen::Shared; + +namespace Ingen { +namespace GUI { + + +/** 'Load Patch' window. + * + * Loaded by glade as a derived object. Used for both "Import" and "Load" + * (e.g. Replace, clear-then-import) operations (the radio button state + * should be changed with the provided methods before presenting). + * + * This is not for loading subpatches. See @a LoadSubpatchWindow for that. + * + * \ingroup GUI + */ +class LoadPatchWindow : public Gtk::FileChooserDialog +{ +public: + LoadPatchWindow(BaseObjectType* cobject, const Glib::RefPtr& xml); + + void set_patch(SharedPtr patch); + + void set_replace() { _replace = true; } + void set_merge() { _replace = false; } + + void present(SharedPtr patch, GraphObject::Variables data); + +protected: + void on_show(); + +private: + void poly_from_file_selected(); + void poly_from_user_selected(); + void ok_clicked(); + void cancel_clicked(); + + GraphObject::Variables _initial_data; + + SharedPtr _patch; + bool _replace; + + Gtk::RadioButton* _poly_from_current_radio; + Gtk::RadioButton* _poly_from_file_radio; + Gtk::RadioButton* _poly_from_user_radio; + Gtk::SpinButton* _poly_spinbutton; + Gtk::Button* _ok_button; + Gtk::Button* _cancel_button; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // LOADPATCHWINDOW_H diff --git a/src/gui/LoadPluginWindow.cpp b/src/gui/LoadPluginWindow.cpp new file mode 100644 index 00000000..28815ba9 --- /dev/null +++ b/src/gui/LoadPluginWindow.cpp @@ -0,0 +1,464 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include "interface/EngineInterface.hpp" +#include "client/NodeModel.hpp" +#include "client/PatchModel.hpp" +#include "client/ClientStore.hpp" +#include "App.hpp" +#include "LoadPluginWindow.hpp" +#include "PatchWindow.hpp" +#include "PatchView.hpp" +#include "PatchCanvas.hpp" + +using namespace std; + + +namespace Ingen { +namespace GUI { + +LoadPluginWindow::LoadPluginWindow(BaseObjectType* cobject, const Glib::RefPtr& xml) + : Gtk::Window(cobject) + , _plugin_name_offset(0) + , _has_shown(false) + , _refresh_list(true) +{ + xml->get_widget("load_plugin_plugins_treeview", _plugins_treeview); + xml->get_widget("load_plugin_polyphonic_checkbutton", _polyphonic_checkbutton); + xml->get_widget("load_plugin_name_entry", _node_name_entry); + xml->get_widget("load_plugin_clear_button", _clear_button); + xml->get_widget("load_plugin_add_button", _add_button); + //xml->get_widget("load_plugin_close_button", _close_button); + //xml->get_widget("load_plugin_ok_button", _add_button); + + xml->get_widget("load_plugin_filter_combo", _filter_combo); + xml->get_widget("load_plugin_search_entry", _search_entry); + + // Set up the plugins list + _plugins_liststore = Gtk::ListStore::create(_plugins_columns); + _plugins_treeview->set_model(_plugins_liststore); + _plugins_treeview->append_column("", _plugins_columns._col_icon); + _plugins_treeview->append_column("Name", _plugins_columns._col_name); + _plugins_treeview->append_column("Type", _plugins_columns._col_type); + _plugins_treeview->append_column("URI", _plugins_columns._col_uri); + //m_plugins_treeview->append_column("Library", _plugins_columns._col_library); + //m_plugins_treeview->append_column("Label", _plugins_columns._col_label); + + // This could be nicer.. store the TreeViewColumns locally maybe? + _plugins_treeview->get_column(1)->set_sort_column(_plugins_columns._col_name); + _plugins_treeview->get_column(2)->set_sort_column(_plugins_columns._col_type); + _plugins_treeview->get_column(3)->set_sort_column(_plugins_columns._col_uri); + //m_plugins_treeview->get_column(3)->set_sort_column(_plugins_columns._col_library); + //m_plugins_treeview->get_column(4)->set_sort_column(_plugins_columns._col_label); + for (int i=0; i < 3; ++i) + _plugins_treeview->get_column(i)->set_resizable(true); + + _plugins_liststore->set_default_sort_func(sigc::mem_fun(this, &LoadPluginWindow::plugin_compare)); + + // Set up the search criteria combobox + _criteria_liststore = Gtk::ListStore::create(_criteria_columns); + _filter_combo->set_model(_criteria_liststore); + Gtk::TreeModel::iterator iter = _criteria_liststore->append(); + Gtk::TreeModel::Row row = *iter; + row[_criteria_columns._col_label] = "Name contains"; + row[_criteria_columns._col_criteria] = CriteriaColumns::NAME; + _filter_combo->set_active(iter); + iter = _criteria_liststore->append(); row = *iter; + row[_criteria_columns._col_label] = "Type contains"; + row[_criteria_columns._col_criteria] = CriteriaColumns::TYPE; + iter = _criteria_liststore->append(); row = *iter; + row[_criteria_columns._col_label] = "URI contains"; + row[_criteria_columns._col_criteria] = CriteriaColumns::URI; + /*iter = _criteria_liststore->append(); row = *iter; + row[_criteria_columns._col_label] = "Library contains: "; + row[_criteria_columns._col_criteria] = CriteriaColumns::LIBRARY; + iter = _criteria_liststore->append(); row = *iter; + row[_criteria_columns._col_label] = "Label contains: "; + row[_criteria_columns._col_criteria] = CriteriaColumns::LABEL;*/ + + _clear_button->signal_clicked().connect( sigc::mem_fun(this, &LoadPluginWindow::clear_clicked)); + _add_button->signal_clicked().connect( sigc::mem_fun(this, &LoadPluginWindow::add_clicked)); + //m_close_button->signal_clicked().connect( sigc::mem_fun(this, &LoadPluginWindow::close_clicked)); + //m_add_button->signal_clicked().connect( sigc::mem_fun(this, &LoadPluginWindow::ok_clicked)); + _plugins_treeview->signal_row_activated().connect(sigc::mem_fun(this, &LoadPluginWindow::plugin_activated)); + _search_entry->signal_activate().connect( sigc::mem_fun(this, &LoadPluginWindow::add_clicked)); + _search_entry->signal_changed().connect( sigc::mem_fun(this, &LoadPluginWindow::filter_changed)); + _node_name_entry->signal_changed().connect( sigc::mem_fun(this, &LoadPluginWindow::name_changed)); + + _selection = _plugins_treeview->get_selection(); + _selection->signal_changed().connect(sigc::mem_fun(this, &LoadPluginWindow::plugin_selection_changed)); + + //m_add_button->grab_default(); +} + + +void +LoadPluginWindow::present(SharedPtr patch, GraphObject::Variables data) +{ + set_patch(patch); + _initial_data = data; + Gtk::Window::present(); +} + + +/** Called every time the user types into the name input box. + * Used to display warning messages, and enable/disable the OK button. + */ +void +LoadPluginWindow::name_changed() +{ + string name = _node_name_entry->get_text(); + if (!Path::is_valid_name(name)) { + //m_message_label->set_text("Name contains invalid characters."); + _add_button->property_sensitive() = false; + } else if (App::instance().store()->find_child(_patch, name)) { + //m_message_label->set_text("An object already exists with that name."); + _add_button->property_sensitive() = false; + } else if (name.length() == 0) { + //m_message_label->set_text(""); + _add_button->property_sensitive() = false; + } else { + //m_message_label->set_text(""); + _add_button->property_sensitive() = true; + } +} + + +/** Sets the patch controller for this window and initializes everything. + * + * This function MUST be called before using the window in any way! + */ +void +LoadPluginWindow::set_patch(SharedPtr patch) +{ + if (_patch) { + _patch = patch; + plugin_selection_changed(); + } else { + _patch = patch; + } + + /*if (patch->poly() <= 1) + _polyphonic_checkbutton->property_sensitive() = false; + else + _polyphonic_checkbutton->property_sensitive() = true;*/ +} + + +/** Populates the plugin list on the first show. + * + * This is done here instead of construction time as the list population is + * really expensive and bogs down creation of a patch. This is especially + * important when many patch notifications are sent at one time from the + * engine. + */ +void +LoadPluginWindow::on_show() +{ + if (!_has_shown) { + App::instance().store()->signal_new_plugin.connect( + sigc::mem_fun(this, &LoadPluginWindow::add_plugin)); + _has_shown = true; + } + + if (_refresh_list) { + set_plugins(App::instance().store()->plugins()); + _refresh_list = false; + } + + Gtk::Window::on_show(); +} + + +int +LoadPluginWindow::plugin_compare(const Gtk::TreeModel::iterator& a_i, + const Gtk::TreeModel::iterator& b_i) +{ + SharedPtr a = a_i->get_value(_plugins_columns._col_plugin_model); + SharedPtr b = b_i->get_value(_plugins_columns._col_plugin_model); + + // FIXME: haaack + if (!a && !b) + return 0; + else if (!a) + return 1; + else if (!b) + return -1; + + if (a->type() == b->type()) + return strcmp(a->name().c_str(), b->name().c_str()); + else + return ((int)a->type() < (int)b->type()) ? -1 : 1; +} + + +void +LoadPluginWindow::set_plugins(SharedPtr m) +{ + _plugins_liststore->clear(); + + for (ClientStore::Plugins::const_iterator i = m->begin(); i != m->end(); ++i) { + SharedPtr plugin = (*i).second; + + Gtk::TreeModel::iterator iter = _plugins_liststore->append(); + Gtk::TreeModel::Row row = *iter; + + row[_plugins_columns._col_icon] = App::instance().icon_from_path(plugin->icon_path(), 20); + row[_plugins_columns._col_name] = plugin->name(); + //row[_plugins_columns._col_label] = plugin->plug_label(); + if (!strcmp(plugin->type_uri(), "ingen:Internal")) + row[_plugins_columns._col_type] = "Internal"; + else if (!strcmp(plugin->type_uri(), "ingen:LV2")) + row[_plugins_columns._col_type] = "LV2"; + else if (!strcmp(plugin->type_uri(), "ingen:LADSPA")) + row[_plugins_columns._col_type] = "LADSPA"; + else + row[_plugins_columns._col_type] = plugin->type_uri(); + row[_plugins_columns._col_uri] = plugin->uri(); + row[_plugins_columns._col_label] = plugin->name(); + //row[_plugins_columns._col_library] = plugin->lib_name(); + row[_plugins_columns._col_plugin_model] = plugin; + } + + _plugins_liststore->set_sort_column(Gtk::TreeSortable::DEFAULT_SORT_COLUMN_ID, Gtk::SORT_ASCENDING); + + _plugins_treeview->columns_autosize(); +} + + +void +LoadPluginWindow::new_plugin(SharedPtr pm) +{ + if (is_visible()) + add_plugin(pm); + else + _refresh_list = true; +} + + +void +LoadPluginWindow::add_plugin(SharedPtr plugin) +{ + Gtk::TreeModel::iterator iter = _plugins_liststore->append(); + Gtk::TreeModel::Row row = *iter; + + row[_plugins_columns._col_name] = plugin->name(); + //row[_plugins_columns._col_label] = plugin->plug_label(); + row[_plugins_columns._col_type] = plugin->type_uri(); + row[_plugins_columns._col_uri] = plugin->uri(); + row[_plugins_columns._col_label] = plugin->name(); + //row[_plugins_columns._col_library] = plugin->lib_name(); + row[_plugins_columns._col_plugin_model] = plugin; +} + + + +///// Event Handlers ////// + + +void +LoadPluginWindow::plugin_activated(const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* col) +{ + add_clicked(); +} + + +void +LoadPluginWindow::plugin_selection_changed() +{ + Gtk::TreeModel::iterator iter = _selection->get_selected(); + if (iter) { + Gtk::TreeModel::Row row = *iter; + boost::shared_ptr p = row.get_value(_plugins_columns._col_plugin_model); + _plugin_name_offset = App::instance().store()->child_name_offset( + _patch->path(), p->default_node_symbol()); + _node_name_entry->set_text(generate_module_name(_plugin_name_offset)); + } else { + _plugin_name_offset = 0; + _node_name_entry->set_text(""); + } +} + + +/** Generate an automatic name for this Node. + * + * Offset is an offset of the number that will be appended to the plugin's + * label, needed if the user adds multiple plugins faster than the engine + * sends the notification back. + */ +string +LoadPluginWindow::generate_module_name(int offset) +{ + string name = ""; + + Gtk::TreeModel::iterator iter = _selection->get_selected(); + + if (iter) { + Gtk::TreeModel::Row row = *iter; + SharedPtr plugin = row.get_value(_plugins_columns._col_plugin_model); + std::stringstream ss; + ss << plugin->default_node_symbol(); + if (offset != 0) + ss << "_" << offset + 1; + name = ss.str(); + } + + return name; +} + + +void +LoadPluginWindow::add_clicked() +{ + Gtk::TreeModel::iterator iter = _selection->get_selected(); + bool polyphonic = _polyphonic_checkbutton->get_active(); + + if (iter) { // If anything is selected + Gtk::TreeModel::Row row = *iter; + SharedPtr plugin = row.get_value(_plugins_columns._col_plugin_model); + string name = _node_name_entry->get_text(); + if (name == "") { + name = generate_module_name(); + } + if (name == "") { + Gtk::MessageDialog dialog(*this, + "Unable to chose a default name for this node. Please enter a name.", + false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); + + dialog.run(); + } else { + Path path = _patch->path().base() + Path::nameify(name); + App::instance().engine()->new_node(path, plugin->uri()); + App::instance().engine()->set_property(path, "ingen:polyphonic", polyphonic); + for (GraphObject::Variables::const_iterator i = _initial_data.begin(); i != _initial_data.end(); ++i) + App::instance().engine()->set_variable(path, i->first, i->second); + _node_name_entry->set_text(generate_module_name(++_plugin_name_offset)); + + // Cascade + Atom& x = _initial_data["ingenuity:canvas-x"]; + x = Atom(x.get_float() + 20.0f); + Atom& y = _initial_data["ingenuity:canvas-y"]; + y = Atom(y.get_float() + 20.0f); + } + } +} + + +/* +void +LoadPluginWindow::close_clicked() +{ + hide(); +} + + +void +LoadPluginWindow::ok_clicked() +{ + add_clicked(); + close_clicked(); +} +*/ + +void +LoadPluginWindow::filter_changed() +{ + _plugins_liststore->clear(); + + string search = _search_entry->get_text(); + transform(search.begin(), search.end(), search.begin(), ::toupper); + + // Get selected criteria + const Gtk::TreeModel::Row row = *(_filter_combo->get_active()); + CriteriaColumns::Criteria criteria = row[_criteria_columns._col_criteria]; + + string field; + + Gtk::TreeModel::Row model_row; + Gtk::TreeModel::iterator model_iter; + size_t num_visible = 0; + + + for (ClientStore::Plugins::const_iterator i = App::instance().store()->plugins()->begin(); + i != App::instance().store()->plugins()->end(); ++i) { + + const SharedPtr plugin = (*i).second; + + switch (criteria) { + case CriteriaColumns::NAME: + field = plugin->name(); break; + case CriteriaColumns::TYPE: + field = plugin->type_uri(); break; + case CriteriaColumns::URI: + field = plugin->uri(); break; + /*case CriteriaColumns::LIBRARY: + field = plugin->lib_name(); break; + case CriteriaColumns::LABEL: + field = plugin->plug_label(); break;*/ + default: + throw; + } + + transform(field.begin(), field.end(), field.begin(), ::toupper); + + if (field.find(search) != string::npos) { + model_iter = _plugins_liststore->append(); + model_row = *model_iter; + + model_row[_plugins_columns._col_name] = plugin->name(); + //model_row[_plugins_columns._col_label] = plugin->plug_label(); + model_row[_plugins_columns._col_type] = plugin->type_uri(); + model_row[_plugins_columns._col_uri] = plugin->uri(); + model_row[_plugins_columns._col_plugin_model] = plugin; + + ++num_visible; + } + } + + if (num_visible == 1) { + _selection->unselect_all(); + _selection->select(model_iter); + } +} + + +void +LoadPluginWindow::clear_clicked() +{ + _search_entry->set_text(""); + set_plugins(App::instance().store()->plugins()); +} + + +bool +LoadPluginWindow::on_key_press_event(GdkEventKey* event) +{ + if (event->keyval == GDK_w && event->state & GDK_CONTROL_MASK) { + hide(); + return true; + } else { + return Gtk::Window::on_key_press_event(event); + } +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/LoadPluginWindow.hpp b/src/gui/LoadPluginWindow.hpp new file mode 100644 index 00000000..98644dde --- /dev/null +++ b/src/gui/LoadPluginWindow.hpp @@ -0,0 +1,155 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LOADPLUGINWINDOW_H +#define LOADPLUGINWINDOW_H + +#include +#include +#include +#include +#include +#include "interface/GraphObject.hpp" +#include "client/PatchModel.hpp" +#include "client/PluginModel.hpp" +#include "client/ClientStore.hpp" +using Ingen::Client::PluginModel; +using Ingen::Client::PatchModel; +using namespace Ingen::Shared; + +namespace Ingen { +namespace GUI { + + +// Gtkmm _really_ needs to add some helper to abstract away this stupid nonsense + +/** Columns for the plugin list in the load plugin window. + * + * \ingroup GUI + */ +class ModelColumns : public Gtk::TreeModel::ColumnRecord +{ +public: + ModelColumns() { + add(_col_icon); + add(_col_name); + add(_col_type); + add(_col_uri); + add(_col_label); + //add(_col_library); + //add(_col_label); + add(_col_plugin_model); + } + + Gtk::TreeModelColumn > _col_icon; + Gtk::TreeModelColumn _col_name; + Gtk::TreeModelColumn _col_type; + Gtk::TreeModelColumn _col_uri; + + // Not displayed: + Gtk::TreeModelColumn _col_label; + Gtk::TreeModelColumn > _col_plugin_model; +}; + + +/** Column for the criteria combo box in the load plugin window. + * + * \ingroup GUI + */ +class CriteriaColumns : public Gtk::TreeModel::ColumnRecord +{ +public: + enum Criteria { NAME, TYPE, URI, }; + + CriteriaColumns() { add(_col_label); add(_col_criteria); } + + Gtk::TreeModelColumn _col_label; + Gtk::TreeModelColumn _col_criteria; +}; + + +/** 'Load Plugin' window. + * + * Loaded by glade as a derived object. + * + * \ingroup GUI + */ +class LoadPluginWindow : public Gtk::Window +{ +public: + LoadPluginWindow(BaseObjectType* cobject, const Glib::RefPtr& xml); + + void set_patch(SharedPtr patch); + void set_plugins(SharedPtr plugins); + + void add_plugin(SharedPtr plugin); + + void present(SharedPtr patch, GraphObject::Variables data); + +protected: + void on_show(); + bool on_key_press_event(GdkEventKey* event); + +private: + void add_clicked(); + //void close_clicked(); + //void ok_clicked(); + void filter_changed(); + void clear_clicked(); + void name_changed(); + + void new_plugin(SharedPtr plugin); + + int plugin_compare(const Gtk::TreeModel::iterator& a, + const Gtk::TreeModel::iterator& b); + + void plugin_activated(const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* col); + void plugin_selection_changed(); + string generate_module_name(int offset = 0); + + GraphObject::Variables _initial_data; + + SharedPtr _patch; + + Glib::RefPtr _plugins_liststore; + ModelColumns _plugins_columns; + + Glib::RefPtr _criteria_liststore; + CriteriaColumns _criteria_columns; + + Glib::RefPtr _selection; + + int _plugin_name_offset; // see comments for generate_plugin_name + + bool _has_shown; + bool _refresh_list; + Gtk::TreeView* _plugins_treeview; + Gtk::CheckButton* _polyphonic_checkbutton; + Gtk::Entry* _node_name_entry; + Gtk::Button* _clear_button; + Gtk::Button* _add_button; + //Gtk::Button* _close_button; + //Gtk::Button* _ok_button; + Gtk::ComboBox* _filter_combo; + Gtk::Entry* _search_entry; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // LOADPLUGINWINDOW_H diff --git a/src/gui/LoadRemotePatchWindow.cpp b/src/gui/LoadRemotePatchWindow.cpp new file mode 100644 index 00000000..5567d67b --- /dev/null +++ b/src/gui/LoadRemotePatchWindow.cpp @@ -0,0 +1,164 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include "client/PatchModel.hpp" +#include "interface/EngineInterface.hpp" +#include "module/World.hpp" +#include "module/global.hpp" +#include "App.hpp" +#include "Configuration.hpp" +#include "LoadRemotePatchWindow.hpp" +#include "ThreadedLoader.hpp" + +using boost::optional; +using namespace Raul; +using namespace std; + +namespace Ingen { +namespace GUI { + + +LoadRemotePatchWindow::LoadRemotePatchWindow(BaseObjectType* cobject, const Glib::RefPtr& xml) +: Gtk::Dialog(cobject), + _replace(true) +{ + xml->get_widget("load_remote_patch_treeview", _treeview); + xml->get_widget("load_remote_patch_uri_entry", _uri_entry); + xml->get_widget("load_remote_patch_cancel_button", _cancel_button); + xml->get_widget("load_remote_patch_open_button", _open_button); + + _liststore = Gtk::ListStore::create(_columns); + _treeview->set_model(_liststore); + _treeview->append_column("Name", _columns._col_name); + _treeview->append_column("URI", _columns._col_uri); + + _selection = _treeview->get_selection(); + _selection->signal_changed().connect(sigc::mem_fun(this, &LoadRemotePatchWindow::patch_selected)); + _treeview->signal_row_activated().connect(sigc::mem_fun(this, &LoadRemotePatchWindow::patch_activated)); + + _open_button->signal_clicked().connect(sigc::mem_fun(this, &LoadRemotePatchWindow::open_clicked)); + _cancel_button->signal_clicked().connect(sigc::mem_fun(this, &LoadRemotePatchWindow::cancel_clicked)); + _uri_entry->signal_changed().connect(sigc::mem_fun(this, &LoadRemotePatchWindow::uri_changed)); +} + + +void +LoadRemotePatchWindow::present(SharedPtr patch, GraphObject::Variables data) +{ + _liststore->clear(); + + set_patch(patch); + _initial_data = data; + + Redland::Model model(*App::instance().world()->rdf_world, + "http://rdf.drobilla.net/ingen_patches/index.ttl", + "http://rdf.drobilla.net/ingen_patches/"); + + Redland::Query query(*App::instance().world()->rdf_world, Glib::ustring( + "SELECT DISTINCT ?name ?uri WHERE {" + " ?uri a ingen:Patch ;" + " doap:name ?name ." + "}")); + + Redland::Query::Results results = query.run(*App::instance().world()->rdf_world, model); + + for (Redland::Query::Results::iterator i = results.begin(); i != results.end(); ++i) { + Gtk::TreeModel::iterator iter = _liststore->append(); + (*iter)[_columns._col_name] = (*i)["name"].to_string(); + (*iter)[_columns._col_uri] = (*i)["uri"].to_string(); + } + + _treeview->columns_autosize(); + + Gtk::Window::present(); +} + + +/** Sets the patch controller for this window and initializes everything. + * + * This function MUST be called before using the window in any way! + */ +void +LoadRemotePatchWindow::set_patch(SharedPtr patch) +{ + _patch = patch; +} + + +void +LoadRemotePatchWindow::uri_changed() +{ + _open_button->property_sensitive() = (_uri_entry->get_text().length() > 0); +} + + +void +LoadRemotePatchWindow::patch_activated(const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* col) +{ + open_clicked(); +} + + +void +LoadRemotePatchWindow::patch_selected() +{ + Gtk::TreeModel::iterator selected_i = _selection->get_selected(); + + if (selected_i) { // If anything is selected + const Glib::ustring uri = selected_i->get_value(_columns._col_uri); + _uri_entry->set_text(uri); + } +} + + +void +LoadRemotePatchWindow::open_clicked() +{ + Glib::ustring uri = _uri_entry->get_text(); + + cerr << "OPEN URI: " << uri << endl; + + // If unset load_patch will load values + optional parent; + optional symbol; + + if (_replace) + App::instance().engine()->clear_patch(_patch->path()); + + if (_patch->path() != "/") + parent = _patch->path().parent(); + + App::instance().loader()->load_patch(true, uri, "/", + _initial_data, parent, symbol); + + hide(); +} + + +void +LoadRemotePatchWindow::cancel_clicked() +{ + hide(); +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/LoadRemotePatchWindow.hpp b/src/gui/LoadRemotePatchWindow.hpp new file mode 100644 index 00000000..ca0ca814 --- /dev/null +++ b/src/gui/LoadRemotePatchWindow.hpp @@ -0,0 +1,94 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LOADREMOTEPATCHWINDOW_H +#define LOADREMOTEPATCHWINDOW_H + +#include +#include +#include +#include "interface/GraphObject.hpp" +#include "client/PatchModel.hpp" +#include "client/PluginModel.hpp" +using namespace Ingen::Shared; +using Ingen::Client::PatchModel; + +namespace Ingen { +namespace GUI { + + +/** Columns for the remote patch list. + * + * \ingroup GUI + */ +class PatchColumns : public Gtk::TreeModel::ColumnRecord +{ +public: + PatchColumns() { + add(_col_name); + add(_col_uri); + } + + Gtk::TreeModelColumn _col_name; + Gtk::TreeModelColumn _col_uri; +}; + + + +/* Load remote patch ("import location") dialog. + * + * \ingroup GUI + */ +class LoadRemotePatchWindow : public Gtk::Dialog +{ +public: + LoadRemotePatchWindow(BaseObjectType* cobject, const Glib::RefPtr& xml); + + void set_patch(SharedPtr patch); + + void set_replace() { _replace = true; } + void set_merge() { _replace = false; } + + void present(SharedPtr patch, GraphObject::Variables data); + +private: + void patch_activated(const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* col); + void patch_selected(); + void uri_changed(); + void open_clicked(); + void cancel_clicked(); + + GraphObject::Variables _initial_data; + + SharedPtr _patch; + bool _replace; + + Glib::RefPtr _selection; + Glib::RefPtr _liststore; + PatchColumns _columns; + + Gtk::TreeView* _treeview; + Gtk::Entry* _uri_entry; + Gtk::Button* _open_button; + Gtk::Button* _cancel_button; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // LOADREMOTEPATCHWINDOW_H diff --git a/src/gui/LoadSubpatchWindow.cpp b/src/gui/LoadSubpatchWindow.cpp new file mode 100644 index 00000000..71dbfb36 --- /dev/null +++ b/src/gui/LoadSubpatchWindow.cpp @@ -0,0 +1,187 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include "interface/EngineInterface.hpp" +#include "client/NodeModel.hpp" +#include "client/PatchModel.hpp" +#include "App.hpp" +#include "LoadSubpatchWindow.hpp" +#include "PatchView.hpp" +#include "Configuration.hpp" +#include "ThreadedLoader.hpp" +using boost::optional; +using namespace std; + +namespace Ingen { +namespace GUI { + + +LoadSubpatchWindow::LoadSubpatchWindow(BaseObjectType* cobject, const Glib::RefPtr& xml) +: Gtk::FileChooserDialog(cobject) +{ + xml->get_widget("load_subpatch_name_from_file_radio", _name_from_file_radio); + xml->get_widget("load_subpatch_name_from_user_radio", _name_from_user_radio); + xml->get_widget("load_subpatch_name_entry", _name_entry); + xml->get_widget("load_subpatch_poly_from_file_radio", _poly_from_file_radio); + xml->get_widget("load_subpatch_poly_from_parent_radio", _poly_from_parent_radio); + xml->get_widget("load_subpatch_poly_from_user_radio", _poly_from_user_radio); + xml->get_widget("load_subpatch_poly_spinbutton", _poly_spinbutton); + xml->get_widget("load_subpatch_ok_button", _ok_button); + xml->get_widget("load_subpatch_cancel_button", _cancel_button); + + _name_from_file_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadSubpatchWindow::disable_name_entry)); + _name_from_user_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadSubpatchWindow::enable_name_entry)); + _poly_from_file_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadSubpatchWindow::disable_poly_spinner)); + _poly_from_parent_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadSubpatchWindow::disable_poly_spinner)); + _poly_from_user_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadSubpatchWindow::enable_poly_spinner)); + _ok_button->signal_clicked().connect(sigc::mem_fun(this, &LoadSubpatchWindow::ok_clicked)); + _cancel_button->signal_clicked().connect(sigc::mem_fun(this, &LoadSubpatchWindow::cancel_clicked)); + + Gtk::FileFilter filt; + filt.add_pattern("*.om"); + filt.set_name("Om patch files (XML, DEPRECATED) (*.om)"); + filt.add_pattern("*.ingen.ttl"); + filt.set_name("Ingen patch files (RDF, *.ingen.ttl)"); + set_filter(filt); + + property_select_multiple() = true; + + // Add global examples directory to "shortcut folders" (bookmarks) + string examples_dir = INGEN_DATA_DIR; + examples_dir.append("/patches"); + DIR* d = opendir(examples_dir.c_str()); + if (d != NULL) + add_shortcut_folder(examples_dir); +} + + +void +LoadSubpatchWindow::present(SharedPtr patch, GraphObject::Variables data) +{ + set_patch(patch); + _initial_data = data; + Gtk::Window::present(); +} + + +/** Sets the patch controller for this window and initializes everything. + * + * This function MUST be called before using the window in any way! + */ +void +LoadSubpatchWindow::set_patch(SharedPtr patch) +{ + _patch = patch; + + char temp_buf[4]; + snprintf(temp_buf, 4, "%u", patch->poly()); + Glib::ustring txt = "Same as parent ("; + txt.append(temp_buf).append(")"); + _poly_from_parent_radio->set_label(txt); +} + + +void +LoadSubpatchWindow::on_show() +{ + if (App::instance().configuration()->patch_folder().length() > 0) + set_current_folder(App::instance().configuration()->patch_folder()); + Gtk::FileChooserDialog::on_show(); +} + + +///// Event Handlers ////// + + + +void +LoadSubpatchWindow::disable_name_entry() +{ + _name_entry->property_sensitive() = false; +} + + +void +LoadSubpatchWindow::enable_name_entry() +{ + _name_entry->property_sensitive() = true; +} + + +void +LoadSubpatchWindow::disable_poly_spinner() +{ + _poly_spinbutton->property_sensitive() = false; +} + + +void +LoadSubpatchWindow::enable_poly_spinner() +{ + _poly_spinbutton->property_sensitive() = true; +} + + +void +LoadSubpatchWindow::ok_clicked() +{ + assert(_patch); + + // If unset load_patch will load values + optional symbol; + string name_str = ""; + + if (_name_from_user_radio->get_active()) { + name_str = _name_entry->get_text(); + symbol = Symbol::symbolify(name_str); + } + + if (_poly_from_user_radio->get_active()) { + cerr << "Overriding poly: " << _poly_spinbutton->get_value_as_int() << endl; + _initial_data.insert(make_pair("ingen:polyphony", (int)_poly_spinbutton->get_value_as_int())); + } else if (_poly_from_parent_radio->get_active()) { + _initial_data.insert(make_pair("ingen:polyphony", (int)_patch->poly())); + } + + std::list uris = get_uris(); + for (std::list::iterator i = uris.begin(); i != uris.end(); ++i) { + // Cascade + Atom& x = _initial_data["ingenuity:canvas-x"]; + x = Atom(x.get_float() + 20.0f); + Atom& y = _initial_data["ingenuity:canvas-y"]; + y = Atom(y.get_float() + 20.0f); + + App::instance().loader()->load_patch(false, *i, "/", _initial_data, _patch->path(), symbol); + } + + hide(); +} + + +void +LoadSubpatchWindow::cancel_clicked() +{ + hide(); +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/LoadSubpatchWindow.hpp b/src/gui/LoadSubpatchWindow.hpp new file mode 100644 index 00000000..ee8a5a16 --- /dev/null +++ b/src/gui/LoadSubpatchWindow.hpp @@ -0,0 +1,80 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LOADSUBPATCHWINDOW_H +#define LOADSUBPATCHWINDOW_H + +#include +#include +#include +#include "client/PatchModel.hpp" +#include "client/PluginModel.hpp" +#include "interface/GraphObject.hpp" +using namespace Ingen::Shared; +using Ingen::Client::PatchModel; + +namespace Ingen { +namespace GUI { + + +/** 'Add Subpatch' window. + * + * Loaded by glade as a derived object. + * + * \ingroup GUI + */ +class LoadSubpatchWindow : public Gtk::FileChooserDialog +{ +public: + LoadSubpatchWindow(BaseObjectType* cobject, const Glib::RefPtr& xml); + + void set_patch(SharedPtr patch); + + void present(SharedPtr patch, GraphObject::Variables data); + +protected: + void on_show(); + +private: + void disable_name_entry(); + void enable_name_entry(); + void disable_poly_spinner(); + void enable_poly_spinner(); + + void ok_clicked(); + void cancel_clicked(); + + GraphObject::Variables _initial_data; + + SharedPtr _patch; + + Gtk::RadioButton* _name_from_file_radio; + Gtk::RadioButton* _name_from_user_radio; + Gtk::Entry* _name_entry; + Gtk::RadioButton* _poly_from_file_radio; + Gtk::RadioButton* _poly_from_parent_radio; + Gtk::RadioButton* _poly_from_user_radio; + Gtk::SpinButton* _poly_spinbutton; + Gtk::Button* _ok_button; + Gtk::Button* _cancel_button; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // LOADSUBPATCHWINDOW_H diff --git a/src/gui/Makefile.am b/src/gui/Makefile.am new file mode 100644 index 00000000..41458812 --- /dev/null +++ b/src/gui/Makefile.am @@ -0,0 +1,125 @@ +MAINTAINERCLEANFILES = Makefile.in +EXTRA_DIST = ingen_gui.gladep + +if BUILD_GTK_CLIENT + +globalpixmapsdir = $(datadir)/pixmaps +dist_globalpixmaps_DATA = ingen.svg ingen-icon.svg + +sharefilesdir = $(pkgdatadir) +dist_sharefiles_DATA = ingen_gui.glade ingen.svg ingen-icon.svg + +moduledir = $(libdir)/ingen + +module_LTLIBRARIES = libingen_gui.la + +libingen_gui_la_CXXFLAGS = \ + -DINGEN_MODULE_DIR=\"$(libdir)/ingen\" \ + -DINGEN_DATA_DIR=\"$(pkgdatadir)\" \ + @CURL_CFLAGS@ \ + @FLOWCANVAS_CFLAGS@ \ + @GNOMECANVASMM_CFLAGS@ \ + @GTKMM_CFLAGS@ \ + @INGEN_CFLAGS@ \ + @LIBGLADEMM_CFLAGS@ \ + @LIBLO_CFLAGS@ \ + @REDLANDMM_CFLAGS@ \ + @RAUL_CFLAGS@ \ + @SLV2_CFLAGS@ \ + @SOUP_CFLAGS@ + + +libingen_gui_la_LDFLAGS = -no-undefined -module -avoid-version + +libingen_gui_la_LIBADD = \ + ../module/libingen_module.la \ + ../shared/libingen_shared.la \ + ../client/libingen_client.la \ + @CURL_LIBS@ \ + @FLOWCANVAS_LIBS@ \ + @GNOMECANVASMM_LIBS@ \ + @GTKMM_LIBS@ \ + @LIBGLADEMM_LIBS@ \ + @LIBLO_LIBS@ \ + @REDLANDMM_LIBS@ \ + @RAUL_LIBS@ \ + @SLV2_LIBS@ \ + @SOUP_LIBS@ + +libingen_gui_la_SOURCES = \ + gui.hpp \ + gui.cpp \ + App.cpp \ + App.hpp \ + BreadCrumb.hpp \ + BreadCrumbBox.cpp \ + BreadCrumbBox.hpp \ + Configuration.cpp \ + Configuration.hpp \ + ConnectWindow.cpp \ + ConnectWindow.hpp \ + Connection.hpp \ + ControlPanel.cpp \ + ControlPanel.hpp \ + Controls.cpp \ + Controls.hpp \ + GladeFactory.cpp \ + GladeFactory.hpp \ + LoadPatchWindow.cpp \ + LoadPatchWindow.hpp \ + LoadPluginWindow.cpp \ + LoadPluginWindow.hpp \ + LoadRemotePatchWindow.cpp \ + LoadRemotePatchWindow.hpp \ + LoadSubpatchWindow.cpp \ + LoadSubpatchWindow.hpp \ + MessagesWindow.cpp \ + MessagesWindow.hpp \ + NewSubpatchWindow.cpp \ + NewSubpatchWindow.hpp \ + NodeControlWindow.cpp \ + NodeControlWindow.hpp \ + ObjectMenu.hpp \ + ObjectMenu.cpp \ + NodeMenu.hpp \ + NodeMenu.cpp \ + PortMenu.hpp \ + PortMenu.cpp \ + NodeModule.cpp \ + NodeModule.hpp \ + NodePropertiesWindow.cpp \ + NodePropertiesWindow.hpp \ + PatchCanvas.cpp \ + PatchCanvas.hpp \ + PatchPortModule.cpp \ + PatchPortModule.hpp \ + PatchPropertiesWindow.cpp \ + PatchPropertiesWindow.hpp \ + PatchTreeWindow.cpp \ + PatchTreeWindow.hpp \ + PatchView.cpp \ + PatchView.hpp \ + PatchWindow.cpp \ + PatchWindow.hpp \ + Port.cpp \ + Port.hpp \ + PortPropertiesWindow.cpp \ + PortPropertiesWindow.hpp \ + RenameWindow.cpp \ + RenameWindow.hpp \ + SubpatchModule.cpp \ + SubpatchModule.hpp \ + ThreadedLoader.cpp \ + ThreadedLoader.hpp \ + WindowFactory.cpp \ + WindowFactory.hpp + +if WITH_CURL +libingen_gui_la_SOURCES += \ + UploadPatchWindow.cpp \ + UploadPatchWindow.hpp +endif + +endif + + diff --git a/src/gui/MessagesWindow.cpp b/src/gui/MessagesWindow.cpp new file mode 100644 index 00000000..3da810f6 --- /dev/null +++ b/src/gui/MessagesWindow.cpp @@ -0,0 +1,67 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "MessagesWindow.hpp" +#include + +namespace Ingen { +namespace GUI { +using std::string; + + +MessagesWindow::MessagesWindow(BaseObjectType* cobject, const Glib::RefPtr& glade_xml) +: Gtk::Window(cobject) +{ + glade_xml->get_widget("messages_textview", _textview); + glade_xml->get_widget("messages_clear_button", _clear_button); + glade_xml->get_widget("messages_close_button", _close_button); + + _clear_button->signal_clicked().connect(sigc::mem_fun(this, &MessagesWindow::clear_clicked)); + _close_button->signal_clicked().connect(sigc::mem_fun(this, &MessagesWindow::close_clicked)); +} + + +void +MessagesWindow::post(const string& msg) +{ + Glib::RefPtr text_buf = _textview->get_buffer(); + text_buf->insert(text_buf->end(), msg); + text_buf->insert(text_buf->end(), "\n"); + + if (!_clear_button->is_sensitive()) + _clear_button->set_sensitive(true); +} + + +void +MessagesWindow::close_clicked() +{ + hide(); +} + + +void +MessagesWindow::clear_clicked() +{ + Glib::RefPtr text_buf = _textview->get_buffer(); + text_buf->erase(text_buf->begin(), text_buf->end()); + _clear_button->set_sensitive(false); +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/MessagesWindow.hpp b/src/gui/MessagesWindow.hpp new file mode 100644 index 00000000..dea0fdd4 --- /dev/null +++ b/src/gui/MessagesWindow.hpp @@ -0,0 +1,58 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MESSAGESWINDOW_H +#define MESSAGESWINDOW_H + +#include +#include +#include +using std::string; + + +namespace Ingen { +namespace GUI { + + +/** Messages Window. + * + * Loaded by libglade as a derived object. + * This is shown when errors occur (ie during patch loading). + * + * \ingroup GUI + */ +class MessagesWindow : public Gtk::Window +{ +public: + MessagesWindow(BaseObjectType* cobject, const Glib::RefPtr& refGlade); + + void post(const string& str); + +private: + void clear_clicked(); + void close_clicked(); + + Gtk::TextView* _textview; + Gtk::Button* _clear_button; + Gtk::Button* _close_button; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // MESSAGESWINDOW_H diff --git a/src/gui/NewSubpatchWindow.cpp b/src/gui/NewSubpatchWindow.cpp new file mode 100644 index 00000000..580ebb51 --- /dev/null +++ b/src/gui/NewSubpatchWindow.cpp @@ -0,0 +1,113 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "App.hpp" +#include "interface/EngineInterface.hpp" +#include "client/NodeModel.hpp" +#include "client/PatchModel.hpp" +#include "client/ClientStore.hpp" +#include "NewSubpatchWindow.hpp" +#include "PatchView.hpp" + +namespace Ingen { +namespace GUI { + + +NewSubpatchWindow::NewSubpatchWindow(BaseObjectType* cobject, const Glib::RefPtr& xml) +: Gtk::Window(cobject) +{ + xml->get_widget("new_subpatch_name_entry", _name_entry); + xml->get_widget("new_subpatch_message_label", _message_label); + xml->get_widget("new_subpatch_polyphony_spinbutton", _poly_spinbutton); + xml->get_widget("new_subpatch_ok_button", _ok_button); + xml->get_widget("new_subpatch_cancel_button", _cancel_button); + + _name_entry->signal_changed().connect(sigc::mem_fun(this, &NewSubpatchWindow::name_changed)); + _ok_button->signal_clicked().connect(sigc::mem_fun(this, &NewSubpatchWindow::ok_clicked)); + _cancel_button->signal_clicked().connect(sigc::mem_fun(this, &NewSubpatchWindow::cancel_clicked)); + + _ok_button->property_sensitive() = false; +} + +void +NewSubpatchWindow::present(SharedPtr patch, GraphObject::Variables data) +{ + set_patch(patch); + _initial_data = data; + Gtk::Window::present(); +} + +/** Sets the patch controller for this window and initializes everything. + * + * This function MUST be called before using the window in any way! + */ +void +NewSubpatchWindow::set_patch(SharedPtr patch) +{ + _patch = patch; +} + + +/** Called every time the user types into the name input box. + * Used to display warning messages, and enable/disable the OK button. + */ +void +NewSubpatchWindow::name_changed() +{ + string name = _name_entry->get_text(); + if (!Path::is_valid_name(name)) { + _message_label->set_text("Name contains invalid characters."); + _ok_button->property_sensitive() = false; + } else if (App::instance().store()->find(_patch->path().base() + name) + != App::instance().store()->end()) { + _message_label->set_text("An object already exists with that name."); + _ok_button->property_sensitive() = false; + } else if (name.length() == 0) { + _message_label->set_text(""); + _ok_button->property_sensitive() = false; + } else { + _message_label->set_text(""); + _ok_button->property_sensitive() = true; + } +} + + +void +NewSubpatchWindow::ok_clicked() +{ + const Path path = _patch->path().base() + Path::nameify(_name_entry->get_text()); + const uint32_t poly = _poly_spinbutton->get_value_as_int(); + + App::instance().engine()->new_patch(path, poly); + for (GraphObject::Variables::const_iterator i = _initial_data.begin(); i != _initial_data.end(); ++i) + App::instance().engine()->set_variable(path, i->first, i->second); + + App::instance().engine()->set_property(_patch->path(), "ingen:enabled", (bool)true); + + hide(); +} + + +void +NewSubpatchWindow::cancel_clicked() +{ + hide(); +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/NewSubpatchWindow.hpp b/src/gui/NewSubpatchWindow.hpp new file mode 100644 index 00000000..00fb6a3b --- /dev/null +++ b/src/gui/NewSubpatchWindow.hpp @@ -0,0 +1,68 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef NEWSUBPATCHWINDOW_H +#define NEWSUBPATCHWINDOW_H + +#include +#include +#include +#include "interface/GraphObject.hpp" +#include "client/PatchModel.hpp" +#include "client/PluginModel.hpp" +using namespace Ingen::Shared; +using Ingen::Client::PatchModel; + +namespace Ingen { +namespace GUI { + + +/** 'New Subpatch' window. + * + * Loaded by glade as a derived object. + * + * \ingroup GUI + */ +class NewSubpatchWindow : public Gtk::Window +{ +public: + NewSubpatchWindow(BaseObjectType* cobject, const Glib::RefPtr& xml); + + void set_patch(SharedPtr patch); + + void present(SharedPtr patch, GraphObject::Variables data); + +private: + void name_changed(); + void ok_clicked(); + void cancel_clicked(); + + GraphObject::Variables _initial_data; + SharedPtr _patch; + + Gtk::Entry* _name_entry; + Gtk::Label* _message_label; + Gtk::SpinButton* _poly_spinbutton; + Gtk::Button* _ok_button; + Gtk::Button* _cancel_button; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // NEWSUBPATCHWINDOW_H diff --git a/src/gui/NodeControlWindow.cpp b/src/gui/NodeControlWindow.cpp new file mode 100644 index 00000000..293d28f5 --- /dev/null +++ b/src/gui/NodeControlWindow.cpp @@ -0,0 +1,139 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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 alongCont + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "interface/EngineInterface.hpp" +#include "client/NodeModel.hpp" +#include "App.hpp" +#include "NodeControlWindow.hpp" +#include "GladeFactory.hpp" +#include "Controls.hpp" +#include "ControlPanel.hpp" +#include "PatchWindow.hpp" + +using namespace std; + +namespace Ingen { +namespace GUI { + + +/** Create a node control window and load a new ControlPanel for it. + */ +NodeControlWindow::NodeControlWindow(SharedPtr node, uint32_t poly) + : _node(node) + , _position_stored(false) + , _x(0) + , _y(0) +{ + assert(_node != NULL); + + property_resizable() = true; + set_border_width(5); + + set_title(_node->path() + " Controls"); + + Glib::RefPtr xml = GladeFactory::new_glade_reference("warehouse_win"); + xml->get_widget_derived("control_panel_vbox", _control_panel); + + show_all_children(); + + _control_panel->reparent(*this); + _control_panel->init(_node, poly); + _control_panel->show(); + + resize(); + + _callback_enabled = true; +} + + +/** Create a node control window and with an existing ControlPanel. + */ +NodeControlWindow::NodeControlWindow(SharedPtr node, ControlPanel* panel) + : _node(node) + , _control_panel(panel) +{ + assert(_node); + + property_resizable() = true; + set_border_width(5); + + set_title(_node->path() + " Controls"); + + _control_panel->reparent(*this); + + show_all_children(); + resize(); + + _callback_enabled = true; +} + + +NodeControlWindow::~NodeControlWindow() +{ + delete _control_panel; +} + + +void +NodeControlWindow::resize() +{ + pair controls_size = _control_panel->ideal_size(); + /*int width = 400; + int height = controls_size.second + + ((_node->polyphonic()) ? 4 : 40);*/ + int width = controls_size.first; + int height = controls_size.second; + + if (height > property_screen().get_value()->get_height() - 64) + height = property_screen().get_value()->get_height() - 64; + if (width > property_screen().get_value()->get_width() - 64) + width = property_screen().get_value()->get_width() - 64; + + //cerr << "Resizing to: " << width << " x " << height << endl; + + Gtk::Window::resize(width, height); +} + + +void +NodeControlWindow::on_show() +{ + for (NodeModel::Ports::const_iterator i = _node->ports().begin(); + i != _node->ports().end(); ++i) + if ((*i)->type().is_control() && (*i)->is_input()) + App::instance().engine()->request_port_value((*i)->path()); + + if (_position_stored) + move(_x, _y); + + Gtk::Window::on_show(); +} + + +void +NodeControlWindow::on_hide() +{ + _position_stored = true; + get_position(_x, _y); + Gtk::Window::on_hide(); +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/NodeControlWindow.hpp b/src/gui/NodeControlWindow.hpp new file mode 100644 index 00000000..84e462c5 --- /dev/null +++ b/src/gui/NodeControlWindow.hpp @@ -0,0 +1,76 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef NODECONTROLWINDOW_H +#define NODECONTROLWINDOW_H + +#include +#include +#include +#include +#include +#include +using std::string; using std::vector; + +namespace Ingen { namespace Client { + class NodeModel; +} } +using Ingen::Client::NodeModel; + +namespace Ingen { +namespace GUI { + +class ControlGroup; +class ControlPanel; + + +/** Window with controls (sliders) for all control-rate ports on a Node. + * + * \ingroup GUI + */ +class NodeControlWindow : public Gtk::Window +{ +public: + NodeControlWindow(SharedPtr node, uint32_t poly); + NodeControlWindow(SharedPtr node, ControlPanel* panel); + virtual ~NodeControlWindow(); + + SharedPtr node() { return _node; } + + ControlPanel* control_panel() const { return _control_panel; } + + void resize(); + +protected: + void on_show(); + void on_hide(); + +private: + SharedPtr _node; + ControlPanel* _control_panel; + bool _callback_enabled; + + bool _position_stored; + int _x; + int _y; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // NODECONTROLWINDOW_H diff --git a/src/gui/NodeMenu.cpp b/src/gui/NodeMenu.cpp new file mode 100644 index 00000000..529eb52c --- /dev/null +++ b/src/gui/NodeMenu.cpp @@ -0,0 +1,169 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "interface/EngineInterface.hpp" +#include "client/NodeModel.hpp" +#include "App.hpp" +#include "NodeMenu.hpp" +#include "WindowFactory.hpp" + +using namespace std; +using std::cerr; using std::endl; + +namespace Ingen { +namespace GUI { + + +NodeMenu::NodeMenu(BaseObjectType* cobject, const Glib::RefPtr& xml) + : ObjectMenu(cobject, xml) + , _controls_menuitem(NULL) +{ + Gtk::Menu* node_menu = NULL; + xml->get_widget("node_menu", node_menu); + xml->get_widget("node_controls_menuitem", _controls_menuitem); + xml->get_widget("node_popup_gui_menuitem", _popup_gui_menuitem); + xml->get_widget("node_embed_gui_menuitem", _embed_gui_menuitem); + + node_menu->remove(*_controls_menuitem); + node_menu->remove(*_popup_gui_menuitem); + node_menu->remove(*_embed_gui_menuitem); + items().push_front(Gtk::Menu_Helpers::SeparatorElem()); + insert(*_controls_menuitem, 0); + insert(*_popup_gui_menuitem, 0); + insert(*_embed_gui_menuitem, 0); +} + + +void +NodeMenu::init(SharedPtr node) +{ + ObjectMenu::init(node); + + _controls_menuitem->signal_activate().connect(sigc::bind( + sigc::mem_fun(App::instance().window_factory(), &WindowFactory::present_controls), + node)); + + _popup_gui_menuitem->signal_activate().connect(sigc::mem_fun(signal_popup_gui, + &sigc::signal::emit)); + _embed_gui_menuitem->signal_toggled().connect(sigc::mem_fun(this, + &NodeMenu::on_menu_embed_gui)); + + const PluginModel* plugin = dynamic_cast(node->plugin()); + if (plugin && plugin->type() == PluginModel::LV2 && plugin->has_ui()) { + _popup_gui_menuitem->show(); + _embed_gui_menuitem->show(); + } else { + _popup_gui_menuitem->hide(); + _embed_gui_menuitem->hide(); + } + + _enable_signal = true; +} + + +void +NodeMenu::on_menu_embed_gui() +{ + signal_embed_gui.emit(_embed_gui_menuitem->get_active()); +} + + +void +NodeMenu::on_menu_clone() +{ + cerr << "[NodeMenu] FIXME: clone broken\n"; + /* + assert(_node); + //assert(_parent != NULL); + //assert(_parent->model() != NULL); + + string clone_name = _node->name(); + int i = 2; // postfix number (ie oldname_2) + + // Check if name already has a number postfix + if (clone_name.find_last_of("_") != string::npos) { + string name_postfix = clone_name.substr(clone_name.find_last_of("_")+1); + clone_name = clone_name.substr(0, clone_name.find_last_of("_")); + if (sscanf(name_postfix.c_str(), "%d", &i)) + ++i; + } + + char clone_postfix[4]; + for ( ; i < 100; ++i) { + snprintf(clone_postfix, 4, "_%d", i); + if (_node->parent_patch()->get_node(clone_name + clone_postfix) == NULL) + break; + } + + clone_name = clone_name + clone_postfix; + + const string path = _node->parent_patch()->base() + clone_name; + NodeModel* nm = new NodeModel(_node->plugin(), path); + nm->polyphonic(_node->polyphonic()); + nm->x(_node->x() + 20); + nm->y(_node->y() + 20); + App::instance().engine()->create_node_from_model(nm); + */ +} + + +void +NodeMenu::on_menu_learn() +{ + App::instance().engine()->midi_learn(_object->path()); +} + + +void +NodeMenu::on_menu_disconnect() +{ + App::instance().engine()->disconnect_all(_object->parent()->path(), _object->path()); +} + + +bool +NodeMenu::has_control_inputs() +{ + const NodeModel* const nm = (NodeModel*)_object.get(); + for (NodeModel::Ports::const_iterator i = nm->ports().begin(); i != nm->ports().end(); ++i) + if ((*i)->is_input() && (*i)->type().is_control()) + return true; + + return false; +} + + +void +NodeMenu::enable_controls_menuitem() +{ + _controls_menuitem->property_sensitive() = true; +} + + +void +NodeMenu::disable_controls_menuitem() +{ + _controls_menuitem->property_sensitive() = false; +} + + + +} // namespace GUI +} // namespace Ingen + diff --git a/src/gui/NodeMenu.hpp b/src/gui/NodeMenu.hpp new file mode 100644 index 00000000..2a10e473 --- /dev/null +++ b/src/gui/NodeMenu.hpp @@ -0,0 +1,72 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef NODEMENU_H +#define NODEMENU_H + +#include +#include +#include +#include +#include "client/NodeModel.hpp" +#include "ObjectMenu.hpp" + +using Ingen::Client::NodeModel; + +namespace Ingen { +namespace GUI { + + +/** Menu for a Node. + * + * \ingroup GUI + */ +class NodeMenu : public ObjectMenu +{ +public: + NodeMenu(BaseObjectType* cobject, const Glib::RefPtr& xml); + + virtual void program_add(int bank, int program, const std::string& name) {} + virtual void program_remove(int bank, int program) {} + + void init(SharedPtr node); + + bool has_control_inputs(); + + sigc::signal signal_popup_gui; + sigc::signal signal_embed_gui; + +protected: + + virtual void enable_controls_menuitem(); + virtual void disable_controls_menuitem(); + + void on_menu_disconnect(); + void on_menu_clone(); + void on_menu_learn(); + void on_menu_embed_gui(); + + Gtk::MenuItem* _controls_menuitem; + Gtk::MenuItem* _popup_gui_menuitem; + Gtk::CheckMenuItem* _embed_gui_menuitem; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // NODEMENU_H diff --git a/src/gui/NodeModule.cpp b/src/gui/NodeModule.cpp new file mode 100644 index 00000000..97edaa84 --- /dev/null +++ b/src/gui/NodeModule.cpp @@ -0,0 +1,386 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "interface/EngineInterface.hpp" +#include "client/NodeModel.hpp" +#include "client/PatchModel.hpp" +#include "client/PluginUI.hpp" +#include "App.hpp" +#include "GladeFactory.hpp" +#include "NodeControlWindow.hpp" +#include "NodeModule.hpp" +#include "PatchCanvas.hpp" +#include "PatchWindow.hpp" +#include "Port.hpp" +#include "RenameWindow.hpp" +#include "SubpatchModule.hpp" +#include "WindowFactory.hpp" +#include "Configuration.hpp" + +using namespace std; + +namespace Ingen { +namespace GUI { + + +NodeModule::NodeModule(boost::shared_ptr canvas, SharedPtr node) + : FlowCanvas::Module(canvas, node->path().name()) + , _node(node) + , _gui_widget(NULL) + , _gui_window(NULL) +{ + assert(_node); + + node->signal_new_port.connect(sigc::bind(sigc::mem_fun(this, &NodeModule::add_port), true)); + node->signal_removed_port.connect(sigc::mem_fun(this, &NodeModule::remove_port)); + node->signal_variable.connect(sigc::mem_fun(this, &NodeModule::set_variable)); + node->signal_property.connect(sigc::mem_fun(this, &NodeModule::set_property)); + node->signal_renamed.connect(sigc::mem_fun(this, &NodeModule::rename)); +} + + +NodeModule::~NodeModule() +{ + NodeControlWindow* win = App::instance().window_factory()->control_window(_node); + + if (win) { + // Should remove from window factory via signal + delete win; + } +} + + +void +NodeModule::create_menu() +{ + Glib::RefPtr xml = GladeFactory::new_glade_reference(); + xml->get_widget_derived("object_menu", _menu); + _menu->init(_node); + _menu->signal_embed_gui.connect(sigc::mem_fun(this, &NodeModule::embed_gui)); + _menu->signal_popup_gui.connect(sigc::hide_return(sigc::mem_fun(this, &NodeModule::popup_gui))); + + set_menu(_menu); +} + + +boost::shared_ptr +NodeModule::create(boost::shared_ptr canvas, SharedPtr node, bool human) +{ + boost::shared_ptr ret; + + SharedPtr patch = PtrCast(node); + if (patch) + ret = boost::shared_ptr(new SubpatchModule(canvas, patch)); + else + ret = boost::shared_ptr(new NodeModule(canvas, node)); + + for (GraphObject::Variables::const_iterator m = node->variables().begin(); m != node->variables().end(); ++m) + ret->set_variable(m->first, m->second); + + for (NodeModel::Ports::const_iterator p = node->ports().begin(); p != node->ports().end(); ++p) { + ret->add_port(*p, false); + } + + if (human) + ret->show_human_names(human); + + ret->resize(); + ret->set_stacked_border(node->polyphonic()); + + return ret; +} + + +void +NodeModule::show_human_names(bool b) +{ + if (b && node()->plugin()) + set_name(((PluginModel*)node()->plugin())->human_name()); + else + b = false; + + if (!b) + set_name(node()->symbol()); + + uint32_t index = 0; + for (PortVector::const_iterator i = ports().begin(); i != ports().end(); ++i) { + if (b) { + string hn = ((PluginModel*)node()->plugin())->port_human_name(index); + if (hn != "") + (*i)->set_name(hn); + } else { + (*i)->set_name(node()->port(index)->symbol()); + } + ++index; + } + + resize(); +} + + +void +NodeModule::value_changed(uint32_t index, const Atom& value) +{ + float control = 0.0f; + switch (value.type()) { + case Atom::FLOAT: + control = value.get_float(); + if (_plugin_ui) { + SLV2UIInstance inst = _plugin_ui->instance(); + const LV2UI_Descriptor* ui_descriptor = slv2_ui_instance_get_descriptor(inst); + LV2UI_Handle ui_handle = slv2_ui_instance_get_handle(inst); + if (ui_descriptor->port_event) + ui_descriptor->port_event(ui_handle, index, 4, 0, &control); + } + break; + case Atom::STRING: + cout << "Port value type is a string? (\"" << value.get_string() << "\")" << endl; + break; + default: + break; + } +} + + +void +NodeModule::embed_gui(bool embed) +{ + if (embed) { + + if (_gui_window) { + cerr << "LV2 GUI already popped up, cannot embed" << endl; + return; + } + + if (!_plugin_ui) { + const PluginModel* const pm = dynamic_cast(_node->plugin()); + assert(pm); + _plugin_ui = pm->ui(App::instance().world(), _node); + } + + if (_plugin_ui) { + GtkWidget* c_widget = (GtkWidget*)slv2_ui_instance_get_widget(_plugin_ui->instance()); + _gui_widget = Glib::wrap(c_widget); + assert(_gui_widget); + + Gtk::Container* container = new Gtk::EventBox(); + container->set_name("ingen_embedded_node_gui_container"); + container->add(*_gui_widget); + FlowCanvas::Module::embed(container); + } else { + cerr << "ERROR: Failed to create LV2 UI" << endl; + } + + if (_gui_widget) { + _gui_widget->show_all(); + + for (NodeModel::Ports::const_iterator p = _node->ports().begin(); + p != _node->ports().end(); ++p) + if ((*p)->type().is_control() && (*p)->is_output()) + App::instance().engine()->set_property((*p)->path(), "ingen:broadcast", true); + } + + } else { // un-embed + + FlowCanvas::Module::embed(NULL); + _plugin_ui.reset(); + + for (NodeModel::Ports::const_iterator p = _node->ports().begin(); p != _node->ports().end(); ++p) + if ((*p)->type().is_control() && (*p)->is_output()) + App::instance().engine()->set_property((*p)->path(), "ingen:broadcast", false); + } + + if (embed && _embed_item) { + initialise_gui_values(); + set_base_color(0x212222FF); + } else { + set_default_base_color(); + } + + resize(); +} + + +void +NodeModule::rename() +{ + set_name(_node->path().name()); + resize(); +} + + +void +NodeModule::add_port(SharedPtr port, bool resize_to_fit) +{ + uint32_t index = _ports.size(); // FIXME: kludge, engine needs to tell us this + + string name = port->path().name(); + if (App::instance().configuration()->name_style() == Configuration::HUMAN && node()->plugin()) + name = ((PluginModel*)node()->plugin())->port_human_name(index); + + Module::add_port(boost::shared_ptr( + new Port(PtrCast(shared_from_this()), port, name))); + + port->signal_value_changed.connect(sigc::bind<0>( + sigc::mem_fun(this, &NodeModule::value_changed), index)); + + if (resize_to_fit) + resize(); +} + + +void +NodeModule::remove_port(SharedPtr port) +{ + SharedPtr p = Module::remove_port(port->path().name()); + p.reset(); +} + + +bool +NodeModule::popup_gui() +{ +#ifdef HAVE_SLV2 + if (_node->plugin() && _node->plugin()->type() == PluginModel::LV2) { + if (_plugin_ui) { + cerr << "LV2 GUI already embedded, cannot pop up" << endl; + return false; + } + + const PluginModel* const plugin = dynamic_cast(_node->plugin()); + assert(plugin); + + _plugin_ui = plugin->ui(App::instance().world(), _node); + + if (_plugin_ui) { + GtkWidget* c_widget = (GtkWidget*)slv2_ui_instance_get_widget(_plugin_ui->instance()); + _gui_widget = Glib::wrap(c_widget); + + _gui_window = new Gtk::Window(); + _gui_window->add(*_gui_widget); + _gui_widget->show_all(); + initialise_gui_values(); + + _gui_window->signal_unmap().connect( + sigc::mem_fun(this, &NodeModule::on_gui_window_close)); + _gui_window->present(); + + return true; + } else { + cerr << "No LV2 GUI" << endl; + } + } +#endif + return false; +} + + +void +NodeModule::on_gui_window_close() +{ + delete _gui_window; + _gui_window = NULL; + _plugin_ui.reset(); + _gui_widget = NULL; +} + + +void +NodeModule::initialise_gui_values() +{ + uint32_t index=0; + for (NodeModel::Ports::const_iterator p = _node->ports().begin(); p != _node->ports().end(); ++p) { + if ((*p)->type().is_control()) + value_changed(index, (*p)->value()); + ++index; + } +} + + +void +NodeModule::show_control_window() +{ + App::instance().window_factory()->present_controls(_node); +} + + +void +NodeModule::on_double_click(GdkEventButton* ev) +{ + if ( ! popup_gui() ) + show_control_window(); +} + + +void +NodeModule::store_location() +{ + const float x = static_cast(property_x()); + const float y = static_cast(property_y()); + + const Atom& existing_x = _node->get_variable("ingenuity:canvas-x"); + const Atom& existing_y = _node->get_variable("ingenuity:canvas-y"); + + if (existing_x.type() != Atom::FLOAT || existing_y.type() != Atom::FLOAT + || existing_x.get_float() != x || existing_y.get_float() != y) { + App::instance().engine()->set_variable(_node->path(), "ingenuity:canvas-x", Atom(x)); + App::instance().engine()->set_variable(_node->path(), "ingenuity:canvas-y", Atom(y)); + } +} + + +void +NodeModule::set_variable(const string& key, const Atom& value) +{ + if (key == "ingenuity:canvas-x" && value.type() == Atom::FLOAT) + move_to(value.get_float(), property_y()); + else if (key == "ingenuity:canvas-y" && value.type() == Atom::FLOAT) + move_to(property_x(), value.get_float()); +} + + +void +NodeModule::set_property(const string& key, const Atom& value) +{ + if (key == "ingen:polyphonic" && value.type() == Atom::BOOL) { + set_stacked_border(value.get_bool()); + } else if (key == "ingen:selected" && value.type() == Atom::BOOL) { + if (value.get_bool() != selected()) { + if (value.get_bool()) + _canvas.lock()->select_item(shared_from_this()); + else + _canvas.lock()->unselect_item(shared_from_this()); + } + } +} + + +void +NodeModule::set_selected(bool b) +{ + if (b != selected()) { + Module::set_selected(b); + if (App::instance().signal()) + App::instance().engine()->set_property(_node->path(), "ingen:selected", b); + } +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/NodeModule.hpp b/src/gui/NodeModule.hpp new file mode 100644 index 00000000..de9556fd --- /dev/null +++ b/src/gui/NodeModule.hpp @@ -0,0 +1,102 @@ +/* This file is part of In* Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef NODEMODULE_H +#define NODEMODULE_H + +#include +#include +#include +#include +#include "Port.hpp" +#include "NodeMenu.hpp" + +class Atom; + +namespace Ingen { namespace Client { + class PortModel; + class NodeModel; +} } +using namespace Ingen::Client; + +namespace Ingen { +namespace GUI { + +class PatchCanvas; +class Port; + + +/** A module in a patch. + * + * This base class is extended for various types of modules. + * + * \ingroup GUI + */ +class NodeModule : public FlowCanvas::Module +{ +public: + static boost::shared_ptr create ( + boost::shared_ptr canvas, + SharedPtr node, + bool human_names); + + virtual ~NodeModule(); + + boost::shared_ptr port(const std::string& port_name) { + return boost::dynamic_pointer_cast( + Module::get_port(port_name)); + } + + virtual void store_location(); + void show_human_names(bool b); + + SharedPtr node() const { return _node; } + +protected: + NodeModule(boost::shared_ptr canvas, SharedPtr node); + + void on_double_click(GdkEventButton* ev); + + void show_control_window(); + void embed_gui(bool embed); + bool popup_gui(); + void on_gui_window_close(); + void set_selected(bool b); + + void rename(); + void set_variable(const std::string& key, const Atom& value); + void set_property(const std::string& predicate, const Raul::Atom& value); + + void add_port(SharedPtr port, bool resize=true); + void remove_port(SharedPtr port); + + void value_changed(uint32_t index, const Atom& value); + void initialise_gui_values(); + + void create_menu(); + + SharedPtr _node; + NodeMenu* _menu; + SharedPtr _plugin_ui; + Gtk::Widget* _gui_widget; + Gtk::Window* _gui_window; ///< iff popped up +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // NODEMODULE_H diff --git a/src/gui/NodePropertiesWindow.cpp b/src/gui/NodePropertiesWindow.cpp new file mode 100644 index 00000000..ae2c23b5 --- /dev/null +++ b/src/gui/NodePropertiesWindow.cpp @@ -0,0 +1,66 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "client/NodeModel.hpp" +#include "client/PluginModel.hpp" +#include "NodePropertiesWindow.hpp" + +namespace Ingen { +namespace GUI { +using std::string; + + +NodePropertiesWindow::NodePropertiesWindow(BaseObjectType* cobject, const Glib::RefPtr& glade_xml) +: Gtk::Window(cobject) +{ + glade_xml->get_widget("node_properties_path_label", _node_path_label); + glade_xml->get_widget("node_properties_polyphonic_checkbutton", _node_polyphonic_toggle); + glade_xml->get_widget("node_properties_plugin_type_label", _plugin_type_label); + glade_xml->get_widget("node_properties_plugin_uri_label", _plugin_uri_label); + glade_xml->get_widget("node_properties_plugin_name_label", _plugin_name_label); +} + + +/** Set the node this window is associated with. + * This function MUST be called before using this object in any way. + */ +void +NodePropertiesWindow::set_node(SharedPtr node_model) +{ + assert(node_model); + + _node_model = node_model; + + set_title(node_model->path() + " Properties"); + + _node_path_label->set_text(node_model->path()); + _node_polyphonic_toggle->set_active(node_model->polyphonic()); + + const PluginModel* pm = dynamic_cast(node_model->plugin()); + if (pm) { + _plugin_type_label->set_text(pm->type_uri()); + _plugin_uri_label->set_text(pm->uri()); + _plugin_name_label->set_text(pm->name()); + } +} + + +} // namespace GUI +} // namespace Ingen + diff --git a/src/gui/NodePropertiesWindow.hpp b/src/gui/NodePropertiesWindow.hpp new file mode 100644 index 00000000..248c8b42 --- /dev/null +++ b/src/gui/NodePropertiesWindow.hpp @@ -0,0 +1,58 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef NODEPROPERTIESWINDOW_H +#define NODEPROPERTIESWINDOW_H + +#include +#include +#include +#include "client/NodeModel.hpp" +using namespace Ingen::Client; + +namespace Ingen { +namespace GUI { + + +/** Node properties window. + * + * Loaded by libglade as a derived object. + * + * \ingroup GUI + */ +class NodePropertiesWindow : public Gtk::Window +{ +public: + NodePropertiesWindow(BaseObjectType* cobject, const Glib::RefPtr& refGlade); + + void present(SharedPtr node_model) { set_node(node_model); Gtk::Window::present(); } + void set_node(SharedPtr node_model); + +private: + + SharedPtr _node_model; + Gtk::Label* _node_path_label; + Gtk::CheckButton* _node_polyphonic_toggle; + Gtk::Label* _plugin_type_label; + Gtk::Label* _plugin_uri_label; + Gtk::Label* _plugin_name_label; +}; + +} // namespace GUI +} // namespace Ingen + +#endif // NODEPROPERTIESWINDOW_H diff --git a/src/gui/ObjectMenu.cpp b/src/gui/ObjectMenu.cpp new file mode 100644 index 00000000..becbf964 --- /dev/null +++ b/src/gui/ObjectMenu.cpp @@ -0,0 +1,112 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "interface/EngineInterface.hpp" +#include "client/ObjectModel.hpp" +#include "App.hpp" +#include "ObjectMenu.hpp" +#include "WindowFactory.hpp" + +namespace Ingen { +namespace GUI { + + +ObjectMenu::ObjectMenu(BaseObjectType* cobject, const Glib::RefPtr& xml) + : Gtk::Menu(cobject) + , _enable_signal(false) + , _polyphonic_menuitem(NULL) + , _disconnect_menuitem(NULL) + , _rename_menuitem(NULL) + , _destroy_menuitem(NULL) + , _properties_menuitem(NULL) +{ + xml->get_widget("object_polyphonic_menuitem", _polyphonic_menuitem); + xml->get_widget("object_disconnect_menuitem", _disconnect_menuitem); + xml->get_widget("object_rename_menuitem", _rename_menuitem); + xml->get_widget("object_destroy_menuitem", _destroy_menuitem); + xml->get_widget("object_properties_menuitem", _properties_menuitem); +} + + +void +ObjectMenu::init(SharedPtr object) +{ + _object = object; + + App& app = App::instance(); + + _polyphonic_menuitem->signal_toggled().connect( + sigc::mem_fun(this, &ObjectMenu::on_menu_polyphonic)); + + _polyphonic_menuitem->set_active(object->polyphonic()); + + _disconnect_menuitem->signal_activate().connect( + sigc::mem_fun(this, &ObjectMenu::on_menu_disconnect)); + + _rename_menuitem->signal_activate().connect(sigc::bind( + sigc::mem_fun(app.window_factory(), &WindowFactory::present_rename), + object)); + + _destroy_menuitem->signal_activate().connect( + sigc::mem_fun(this, &ObjectMenu::on_menu_destroy)); + + _properties_menuitem->signal_activate().connect( + sigc::mem_fun(this, &ObjectMenu::on_menu_properties)); + + object->signal_property.connect(sigc::mem_fun(this, &ObjectMenu::property_changed)); + + _enable_signal = true; +} + + +void +ObjectMenu::on_menu_polyphonic() +{ + if (_enable_signal) + App::instance().engine()->set_property( + _object->path(), "ingen:polyphonic", _polyphonic_menuitem->get_active()); +} + + +void +ObjectMenu::property_changed(const std::string& predicate, const Raul::Atom& value) +{ + _enable_signal = false; + if (predicate == "ingen:polyphonic" && value.type() == Atom::BOOL) + _polyphonic_menuitem->set_active(value.get_bool()); + _enable_signal = true; +} + + +void +ObjectMenu::on_menu_destroy() +{ + App::instance().engine()->destroy(_object->path()); +} + + +void +ObjectMenu::on_menu_properties() +{ + App::instance().window_factory()->present_properties(_object); +} + + +} // namespace GUI +} // namespace Ingen + diff --git a/src/gui/ObjectMenu.hpp b/src/gui/ObjectMenu.hpp new file mode 100644 index 00000000..b5c5bf49 --- /dev/null +++ b/src/gui/ObjectMenu.hpp @@ -0,0 +1,69 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef OBJECTMENU_H +#define OBJECTMENU_H + +#include +#include +#include +#include +#include +#include "client/ObjectModel.hpp" +using Ingen::Client::ObjectModel; + +namespace Ingen { +namespace GUI { + +class ObjectControlWindow; +class ObjectPropertiesWindow; +class PatchCanvas; + +/** Menu for a Object. + * + * \ingroup GUI + */ +class ObjectMenu : public Gtk::Menu +{ +public: + ObjectMenu(BaseObjectType* cobject, const Glib::RefPtr& xml); + + void init(SharedPtr object); + +protected: + + virtual void on_menu_disconnect() = 0; + void on_menu_polyphonic(); + void on_menu_destroy(); + void on_menu_properties(); + + void property_changed(const std::string& predicate, const Raul::Atom& value); + + bool _enable_signal; + SharedPtr _object; + Gtk::CheckMenuItem* _polyphonic_menuitem; + Gtk::MenuItem* _disconnect_menuitem; + Gtk::MenuItem* _rename_menuitem; + Gtk::MenuItem* _destroy_menuitem; + Gtk::MenuItem* _properties_menuitem; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // OBJECTMENU_H diff --git a/src/gui/PatchCanvas.cpp b/src/gui/PatchCanvas.cpp new file mode 100644 index 00000000..4f01a7f7 --- /dev/null +++ b/src/gui/PatchCanvas.cpp @@ -0,0 +1,773 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include CONFIG_H_PATH +#include "module/global.hpp" +#include "module/World.hpp" + +#include +#include +#include +#include "interface/EngineInterface.hpp" +#include "shared/Builder.hpp" +#include "shared/ClashAvoider.hpp" +#include "serialisation/Serialiser.hpp" +#include "client/PluginModel.hpp" +#include "client/PatchModel.hpp" +#include "client/NodeModel.hpp" +#include "client/ClientStore.hpp" +#include "App.hpp" +#include "PatchCanvas.hpp" +#include "PatchWindow.hpp" +#include "PatchPortModule.hpp" +#include "LoadPluginWindow.hpp" +#include "LoadSubpatchWindow.hpp" +#include "NewSubpatchWindow.hpp" +#include "Port.hpp" +#include "Connection.hpp" +#include "NodeModule.hpp" +#include "SubpatchModule.hpp" +#include "GladeFactory.hpp" +#include "WindowFactory.hpp" +#include "ThreadedLoader.hpp" +using Ingen::Client::ClientStore; +using Ingen::Serialisation::Serialiser; +using Ingen::Client::PluginModel; +using namespace std; + +namespace Ingen { +namespace GUI { + + +PatchCanvas::PatchCanvas(SharedPtr patch, int width, int height) + : Canvas(width, height) + , _patch(patch) + , _last_click_x(0) + , _last_click_y(0) + , _refresh_menu(false) + , _human_names(true) + , _menu(NULL) + , _internal_menu(NULL) + , _plugin_menu(NULL) +{ + Glib::RefPtr xml = GladeFactory::new_glade_reference(); + xml->get_widget("canvas_menu", _menu); + + /*xml->get_widget("canvas_menu_add_number_control", _menu_add_number_control); + xml->get_widget("canvas_menu_add_button_control", _menu_add_button_control);*/ + xml->get_widget("canvas_menu_add_audio_input", _menu_add_audio_input); + xml->get_widget("canvas_menu_add_audio_output", _menu_add_audio_output); + xml->get_widget("canvas_menu_add_control_input", _menu_add_control_input); + xml->get_widget("canvas_menu_add_control_output", _menu_add_control_output); + xml->get_widget("canvas_menu_add_midi_input", _menu_add_midi_input); + xml->get_widget("canvas_menu_add_midi_output", _menu_add_midi_output); + xml->get_widget("canvas_menu_add_osc_input", _menu_add_osc_input); + xml->get_widget("canvas_menu_add_osc_output", _menu_add_osc_output); + xml->get_widget("canvas_menu_add_event_input", _menu_add_event_input); + xml->get_widget("canvas_menu_add_event_output", _menu_add_event_output); + xml->get_widget("canvas_menu_load_plugin", _menu_load_plugin); + xml->get_widget("canvas_menu_load_patch", _menu_load_patch); + xml->get_widget("canvas_menu_new_patch", _menu_new_patch); + + // Add port menu items + _menu_add_audio_input->signal_activate().connect( + sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port), + "audio_input", "ingen:AudioPort", false)); + _menu_add_audio_output->signal_activate().connect( + sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port), + "audio_output", "ingen:AudioPort", true)); + _menu_add_control_input->signal_activate().connect( + sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port), + "control_input", "ingen:ControlPort", false)); + _menu_add_control_output->signal_activate().connect( + sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port), + "control_output", "ingen:ControlPort", true)); + _menu_add_midi_input->signal_activate().connect( + sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port), + "midi_input", "ingen:MIDIPort", false)); + _menu_add_midi_output->signal_activate().connect( + sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port), + "midi_output", "ingen:MIDIPort", true)); + _menu_add_osc_input->signal_activate().connect( + sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port), + "osc_input", "ingen:OSCPort", false)); + _menu_add_osc_output->signal_activate().connect( + sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port), + "osc_output", "ingen:OSCPort", true)); + _menu_add_event_input->signal_activate().connect( + sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port), + "event_input", "ingen:EventPort", false)); + _menu_add_event_output->signal_activate().connect( + sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port), + "event_output", "ingen:EventPort", true)); + + // Add control menu items + /*_menu_add_number_control->signal_activate().connect( + sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_control), NUMBER)); + _menu_add_button_control->signal_activate().connect( + sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_control), BUTTON));*/ + + // Connect to model signals to track state + _patch->signal_new_node.connect(sigc::mem_fun(this, &PatchCanvas::add_node)); + _patch->signal_removed_node.connect(sigc::mem_fun(this, &PatchCanvas::remove_node)); + _patch->signal_new_port.connect(sigc::mem_fun(this, &PatchCanvas::add_port)); + _patch->signal_removed_port.connect(sigc::mem_fun(this, &PatchCanvas::remove_port)); + _patch->signal_new_connection.connect(sigc::mem_fun(this, &PatchCanvas::connection)); + _patch->signal_removed_connection.connect(sigc::mem_fun(this, &PatchCanvas::disconnection)); + + App::instance().store()->signal_new_plugin.connect(sigc::mem_fun(this, &PatchCanvas::add_plugin)); + + // Connect widget signals to do things + _menu_load_plugin->signal_activate().connect(sigc::mem_fun(this, &PatchCanvas::menu_load_plugin)); + _menu_load_patch->signal_activate().connect(sigc::mem_fun(this, &PatchCanvas::menu_load_patch)); + _menu_new_patch->signal_activate().connect(sigc::mem_fun(this, &PatchCanvas::menu_new_patch)); +} + + +void +PatchCanvas::show_menu(GdkEvent* event) +{ + if (!_internal_menu || !_plugin_menu || _refresh_menu) { + build_internal_menu(); +#ifdef HAVE_SLV2 + build_plugin_menu(); +#endif + _refresh_menu = false; + } + _menu->popup(event->button.button, event->button.time); +} + + +void +PatchCanvas::build_internal_menu() +{ + if (_internal_menu) { + _internal_menu->items().clear(); + } else { + _menu->items().push_back(Gtk::Menu_Helpers::ImageMenuElem("Internal", + *(manage(new Gtk::Image(Gtk::Stock::EXECUTE, Gtk::ICON_SIZE_MENU))))); + Gtk::MenuItem* internal_menu_item = &(_menu->items().back()); + _internal_menu = Gtk::manage(new Gtk::Menu()); + internal_menu_item->set_submenu(*_internal_menu); + _menu->reorder_child(*internal_menu_item, 2); + } + + SharedPtr plugins = App::instance().store()->plugins(); + + // Add Internal plugins + for (ClientStore::Plugins::const_iterator i = plugins->begin(); i != plugins->end(); ++i) { + SharedPtr p = i->second; + if (p->type() == Plugin::Internal) { + _internal_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(p->name(), + sigc::bind(sigc::mem_fun(this, &PatchCanvas::load_plugin), p))); + } + } +} + + +#ifdef HAVE_SLV2 +size_t +PatchCanvas::build_plugin_class_menu(Gtk::Menu* menu, + SLV2PluginClass plugin_class, SLV2PluginClasses classes) +{ + size_t num_items = 0; + + // Add submenus + for (unsigned i=0; i < slv2_plugin_classes_size(classes); ++i) { + SLV2PluginClass c = slv2_plugin_classes_get_at(classes, i); + SLV2Value parent = slv2_plugin_class_get_parent_uri(c); + + if (parent && slv2_value_equals(parent, slv2_plugin_class_get_uri(plugin_class))) { + Gtk::Menu_Helpers::MenuElem menu_elem = Gtk::Menu_Helpers::MenuElem( + slv2_value_as_string(slv2_plugin_class_get_label(c))); + + Gtk::Menu* submenu = Gtk::manage(new Gtk::Menu()); + size_t sub_num_items = build_plugin_class_menu(submenu, c, classes); + + if (sub_num_items > 0) { + menu->items().push_back(menu_elem); + Gtk::MenuItem* menu_item = &(menu->items().back()); + menu_item->set_submenu(*submenu); + ++num_items; + } + } + } + + SharedPtr plugins = App::instance().store()->plugins(); + + // Add LV2 plugins + for (ClientStore::Plugins::const_iterator i = plugins->begin(); i != plugins->end(); ++i) { + SLV2Plugin p = i->second->slv2_plugin(); + + if (p && slv2_plugin_get_class(p) == plugin_class) { + Glib::RefPtr icon + = App::instance().icon_from_path(PluginModel::get_lv2_icon_path(p), 16); + if (icon) { + Gtk::Image* image = new Gtk::Image(icon); + menu->items().push_back(Gtk::Menu_Helpers::ImageMenuElem(i->second->name(), + *image, + sigc::bind(sigc::mem_fun(this, &PatchCanvas::load_plugin), i->second))); + } else { + menu->items().push_back(Gtk::Menu_Helpers::MenuElem(i->second->name(), + sigc::bind(sigc::mem_fun(this, &PatchCanvas::load_plugin), i->second))); + ++num_items; + } + } + } + + return num_items; +} + + +void +PatchCanvas::build_plugin_menu() +{ + if (_plugin_menu) { + _plugin_menu->items().clear(); + } else { + _menu->items().push_back(Gtk::Menu_Helpers::ImageMenuElem("Plugin", + *(manage(new Gtk::Image(Gtk::Stock::EXECUTE, Gtk::ICON_SIZE_MENU))))); + Gtk::MenuItem* plugin_menu_item = &(_menu->items().back()); + _plugin_menu = Gtk::manage(new Gtk::Menu()); + plugin_menu_item->set_submenu(*_plugin_menu); + _menu->reorder_child(*plugin_menu_item, 3); + } + + Glib::Mutex::Lock lock(PluginModel::rdf_world()->mutex()); + SLV2PluginClass lv2_plugin = slv2_world_get_plugin_class(PluginModel::slv2_world()); + SLV2PluginClasses classes = slv2_world_get_plugin_classes(PluginModel::slv2_world()); + + build_plugin_class_menu(_plugin_menu, lv2_plugin, classes); +} +#endif + + +void +PatchCanvas::build() +{ + boost::shared_ptr shared_this = + boost::dynamic_pointer_cast(shared_from_this()); + + // Create modules for nodes + for (ObjectModel::const_iterator i = App::instance().store()->children_begin(_patch); + i != App::instance().store()->children_end(_patch); ++i) { + SharedPtr node = PtrCast(i->second); + if (node && node->parent() == _patch) + add_node(node); + } + + // Create pseudo modules for ports (ports on this canvas, not on our module) + for (NodeModel::Ports::const_iterator i = _patch->ports().begin(); + i != _patch->ports().end(); ++i) { + add_port(*i); + } + + // Create connections + for (PatchModel::Connections::const_iterator i = _patch->connections().begin(); + i != _patch->connections().end(); ++i) { + connection(PtrCast(*i)); + } +} + + +void +PatchCanvas::arrange(bool ingen_doesnt_use_length_hints) +{ + FlowCanvas::Canvas::arrange(false); + + for (list >::iterator i = _items.begin(); i != _items.end(); ++i) + (*i)->store_location(); +} + + +void +PatchCanvas::show_human_names(bool b) +{ + _human_names = b; + for (ItemList::iterator m = _items.begin(); m != _items.end(); ++m) { + boost::shared_ptr mod = boost::dynamic_pointer_cast(*m); + if (mod) + mod->show_human_names(b); + } +} + + +void +PatchCanvas::add_plugin(SharedPtr pm) +{ + _refresh_menu = true; +} + + +void +PatchCanvas::add_node(SharedPtr nm) +{ + boost::shared_ptr shared_this = + boost::dynamic_pointer_cast(shared_from_this()); + + SharedPtr pm = PtrCast(nm); + SharedPtr module; + if (pm) { + module = SubpatchModule::create(shared_this, pm, _human_names); + } else { + module = NodeModule::create(shared_this, nm, _human_names); + const PluginModel* plugm = dynamic_cast(nm->plugin()); + if (plugm && plugm->icon_path() != "") + module->set_icon(App::instance().icon_from_path(plugm->icon_path(), 100)); + } + + add_item(module); + module->show(); + _views.insert(std::make_pair(nm, module)); +} + + +void +PatchCanvas::remove_node(SharedPtr nm) +{ + Views::iterator i = _views.find(nm); + + if (i != _views.end()) { + remove_item(i->second); + _views.erase(i); + } +} + + +void +PatchCanvas::add_port(SharedPtr pm) +{ + boost::shared_ptr shared_this = + boost::dynamic_pointer_cast(shared_from_this()); + + SharedPtr view = PatchPortModule::create(shared_this, pm); + _views.insert(std::make_pair(pm, view)); + add_item(view); + view->show(); +} + + +void +PatchCanvas::remove_port(SharedPtr pm) +{ + Views::iterator i = _views.find(pm); + + if (i != _views.end()) { + remove_item(i->second); + _views.erase(i); + } +} + + +SharedPtr +PatchCanvas::get_port_view(SharedPtr port) +{ + SharedPtr module = _views[port]; + + // Port on this patch + if (module) { + return (PtrCast(module)) + ? *(PtrCast(module)->ports().begin()) + : PtrCast(module); + } else { + module = PtrCast(_views[port->parent()]); + if (module) { + for (PortVector::const_iterator p = module->ports().begin(); + p != module->ports().end(); ++p) { + boost::shared_ptr pv = boost::dynamic_pointer_cast(*p); + if (pv && pv->model() == port) + return pv; + } + } + } + + return SharedPtr(); +} + + +void +PatchCanvas::connection(SharedPtr cm) +{ + assert(cm); + + const SharedPtr src = get_port_view(cm->src_port()); + const SharedPtr dst = get_port_view(cm->dst_port()); + + if (src && dst) { + add_connection(boost::shared_ptr(new GUI::Connection(shared_from_this(), + cm, src, dst, src->color() + 0x22222200))); + } else { + cerr << "[PatchCanvas] ERROR: Unable to find ports to connect " + << cm->src_port_path() << " -> " << cm->dst_port_path() << endl; + } +} + + +void +PatchCanvas::disconnection(SharedPtr cm) +{ + const SharedPtr src = get_port_view(cm->src_port()); + const SharedPtr dst = get_port_view(cm->dst_port()); + + if (src && dst) + remove_connection(src, dst); + else + cerr << "[PatchCanvas] ERROR: Unable to find ports to disconnect " + << cm->src_port_path() << " -> " << cm->dst_port_path() << endl; +} + + +void +PatchCanvas::connect(boost::shared_ptr src_port, + boost::shared_ptr dst_port) +{ + const boost::shared_ptr src + = boost::dynamic_pointer_cast(src_port); + + const boost::shared_ptr dst + = boost::dynamic_pointer_cast(dst_port); + + if (!src || !dst) + return; + + // Midi binding/learn shortcut + if (src->model()->type().is_event() && dst->model()->type().is_control()) { + cerr << "[PatchCanvas] FIXME: MIDI binding shortcut" << endl; +#if 0 + SharedPtr pm(new PluginModel(PluginModel::Internal, "", "midi_control_in", "")); + SharedPtr nm(new NodeModel(pm, _patch->path().base() + + src->name() + "-" + dst->name(), false)); + nm->set_variable("canvas-x", Atom((float) + (dst->module()->property_x() - dst->module()->width() - 20))); + nm->set_variable("canvas-y", Atom((float) + (dst->module()->property_y()))); + App::instance().engine()->create_node_from_model(nm.get()); + App::instance().engine()->connect(src->model()->path(), nm->path() + "/MIDI_In"); + App::instance().engine()->connect(nm->path() + "/Out_(CR)", dst->model()->path()); + App::instance().engine()->midi_learn(nm->path()); + + // Set control node range to port's user range + + App::instance().engine()->set_port_value_queued(nm->path().base() + "Min", + dst->model()->get_variable("user-min").get_float()); + App::instance().engine()->set_port_value_queued(nm->path().base() + "Max", + dst->model()->get_variable("user-max").get_float()); +#endif + } else { + App::instance().engine()->connect(src->model()->path(), dst->model()->path()); + } +} + + +void +PatchCanvas::disconnect(boost::shared_ptr src_port, + boost::shared_ptr dst_port) +{ + const boost::shared_ptr src + = boost::dynamic_pointer_cast(src_port); + + const boost::shared_ptr dst + = boost::dynamic_pointer_cast(dst_port); + + App::instance().engine()->disconnect(src->model()->path(), + dst->model()->path()); +} + + +bool +PatchCanvas::canvas_event(GdkEvent* event) +{ + assert(event); + + bool ret = false; + + switch (event->type) { + + case GDK_BUTTON_PRESS: + if (event->button.button == 3) { + _last_click_x = (int)event->button.x; + _last_click_y = (int)event->button.y; + show_menu(event); + ret = true; + } + break; + + case GDK_KEY_PRESS: + case GDK_KEY_RELEASE: + ret = canvas_key_event(&event->key); + + default: + break; + } + + return (ret ? true : Canvas::canvas_event(event)); +} + + +bool +PatchCanvas::canvas_key_event(GdkEventKey* event) +{ + switch (event->type) { + case GDK_KEY_PRESS: + switch (event->keyval) { + case GDK_Delete: + destroy_selection(); + return true; + case GDK_e: + if (event->state == 0) { + if (_patch->get_editable() == true) + _patch->set_editable(false); + else + _patch->set_editable(true); + return true; + } else { + return false; + } + default: + return false; + } + default: + return false; + } +} + + +void +PatchCanvas::destroy_selection() +{ + for (list >::iterator m = _selected_items.begin(); m != _selected_items.end(); ++m) { + boost::shared_ptr module = boost::dynamic_pointer_cast(*m); + if (module) { + App::instance().engine()->destroy(module->node()->path()); + } else { + boost::shared_ptr port_module = boost::dynamic_pointer_cast(*m); + if (port_module) + App::instance().engine()->destroy(port_module->port()->path()); + } + } +} + +void +PatchCanvas::select_all() +{ + unselect_ports(); + for (list >::iterator m = _items.begin(); m != _items.end(); ++m) + if (boost::dynamic_pointer_cast(*m)) + if (!(*m)->selected()) + select_item(*m); +} + +void +PatchCanvas::copy_selection() +{ + Serialiser serialiser(*App::instance().world(), App::instance().store()); + serialiser.start_to_string(_patch->path(), "http://example.org/"); + + for (list >::iterator m = _selected_items.begin(); m != _selected_items.end(); ++m) { + boost::shared_ptr module = boost::dynamic_pointer_cast(*m); + if (module) { + serialiser.serialise(module->node()); + } else { + boost::shared_ptr port_module = boost::dynamic_pointer_cast(*m); + if (port_module) + serialiser.serialise(port_module->port()); + } + } + + for (list >::iterator c = _selected_connections.begin(); + c != _selected_connections.end(); ++c) { + boost::shared_ptr connection = boost::dynamic_pointer_cast(*c); + if (connection) + serialiser.serialise_connection(_patch, connection->model()); + } + + string result = serialiser.finish(); + + Glib::RefPtr clipboard = Gtk::Clipboard::get(); + clipboard->set_text(result); +} + + +void +PatchCanvas::paste() +{ + Glib::ustring str = Gtk::Clipboard::get()->wait_for_text(); + SharedPtr parser = App::instance().loader()->parser(); + if (!parser) { + cerr << "Unable to load parser, paste unavailable" << endl; + return; + } + + clear_selection(); + + Builder builder(*App::instance().engine()); + ClientStore clipboard; + clipboard.set_plugins(App::instance().store()->plugins()); + clipboard.new_patch("/", _patch->poly()); + + ClashAvoider avoider(*App::instance().store().get(), _patch->path(), clipboard, &clipboard); + parser->parse_string(App::instance().world(), &avoider, str, "/", _patch->path()); + + for (Store::iterator i = clipboard.begin(); i != clipboard.end(); ++i) { + cout << "************ OBJECT: " << i->first << endl; + if (_patch->path() == "/" && i->first == "/") { + //cout << "SKIPPING ROOT " << _patch->path() << " :: " << i->first << endl; + continue; + } else if (i->first.parent() != "/") { + //cout << "SKIPPING NON ROOTED OBJECT " << i->first << endl; + continue; + } + GraphObject::Variables::iterator x = i->second->variables().find("ingenuity:canvas-x"); + if (x != i->second->variables().end()) + x->second = x->second.get_float() + 20.0f; + GraphObject::Variables::iterator y = i->second->variables().find("ingenuity:canvas-y"); + if (y != i->second->variables().end()) + y->second = y->second.get_float() + 20.0f; + if (i->first.parent() == "/") { + GraphObject::Properties::iterator s = i->second->properties().find("ingen:selected"); + if (s != i->second->properties().end()) + s->second = true; + else + i->second->properties().insert(make_pair("ingen:selected", true)); + } + builder.build(_patch->path(), i->second); + } + + //avoider.set_target(*App::instance().engine()); + + for (ClientStore::ConnectionRecords::const_iterator i = clipboard.connection_records().begin(); + i != clipboard.connection_records().end(); ++i) { + cout << "CONNECTING " << i->first << " -> " << i->second << endl; + App::instance().engine()->connect(i->first, i->second); + } +} + + +string +PatchCanvas::generate_port_name(const string& base) +{ + string name = base; + + char num_buf[5]; + for (uint i=1; i < 9999; ++i) { + snprintf(num_buf, 5, "%u", i); + name = base + "_"; + name += num_buf; + if (!_patch->get_port(name)) + break; + } + + assert(Path::is_valid(string("/") + name)); + + return name; +} + +void +PatchCanvas::menu_add_control(ControlType type) +{ + // FIXME: bundleify + + GraphObject::Variables data = get_initial_data(); + float x = data["ingenuity:canvas-x"].get_float(); + float y = data["ingenuity:canvas-y"].get_float(); + + cerr << "ADD CONTROL: " << (unsigned)type << " @ " << x << ", " << y << endl; + + add_item(boost::shared_ptr( + new FlowCanvas::Ellipse(shared_from_this(), "control", x, y, 20, 20, true))); +} + +void +PatchCanvas::menu_add_port(const string& name, const string& type, bool is_output) +{ + const Path& path = _patch->path().base() + generate_port_name(name); + App::instance().engine()->bundle_begin(); + App::instance().engine()->new_port(path, _patch->num_ports(), type, is_output); + GraphObject::Variables data = get_initial_data(); + for (GraphObject::Variables::const_iterator i = data.begin(); i != data.end(); ++i) + App::instance().engine()->set_variable(path, i->first, i->second); + App::instance().engine()->bundle_end(); +} + + +void +PatchCanvas::load_plugin(SharedPtr plugin) +{ + string name = plugin->default_node_symbol(); + unsigned offset = App::instance().store()->child_name_offset(_patch->path(), name); + if (offset != 0) { + std::stringstream ss; + ss << name << "_" << offset; + name = ss.str(); + } + + const Path path = _patch->path().base() + name; + // FIXME: polyphony? + App::instance().engine()->new_node(path, plugin->uri()); + GraphObject::Variables data = get_initial_data(); + for (GraphObject::Variables::const_iterator i = data.begin(); i != data.end(); ++i) + App::instance().engine()->set_variable(path, i->first, i->second); +} + + +/** Try to guess a suitable location for a new module. + */ +void +PatchCanvas::get_new_module_location(double& x, double& y) +{ + int scroll_x; + int scroll_y; + get_scroll_offsets(scroll_x, scroll_y); + x = scroll_x + 20; + y = scroll_y + 20; +} + + +GraphObject::Variables +PatchCanvas::get_initial_data() +{ + GraphObject::Variables result; + + result["ingenuity:canvas-x"] = Atom((float)_last_click_x); + result["ingenuity:canvas-y"] = Atom((float)_last_click_y); + + return result; +} + +void +PatchCanvas::menu_load_plugin() +{ + App::instance().window_factory()->present_load_plugin(_patch, get_initial_data()); +} + + +void +PatchCanvas::menu_load_patch() +{ + App::instance().window_factory()->present_load_subpatch(_patch, get_initial_data()); +} + + +void +PatchCanvas::menu_new_patch() +{ + App::instance().window_factory()->present_new_subpatch(_patch, get_initial_data()); +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/PatchCanvas.hpp b/src/gui/PatchCanvas.hpp new file mode 100644 index 00000000..fcf68e76 --- /dev/null +++ b/src/gui/PatchCanvas.hpp @@ -0,0 +1,155 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PATCHCANVAS_H +#define PATCHCANVAS_H + +#include CONFIG_H_PATH + +#include +#include +#include +#include +#include +#include +#include +#include "client/ConnectionModel.hpp" +#include "client/PatchModel.hpp" +#include "interface/GraphObject.hpp" +#include "NodeModule.hpp" + +using namespace FlowCanvas; +using namespace Ingen::Shared; + +using std::string; +using FlowCanvas::Port; +using Ingen::Client::ConnectionModel; +using Ingen::Client::PatchModel; +using Ingen::Client::NodeModel; +using Ingen::Client::PortModel; + +namespace Ingen { +namespace GUI { + +class NodeModule; + + +/** Patch canvas widget. + * + * \ingroup GUI + */ +class PatchCanvas : public FlowCanvas::Canvas +{ +public: + PatchCanvas(SharedPtr patch, int width, int height); + + virtual ~PatchCanvas() {} + + /*boost::shared_ptr find_module(const string& name) { + return boost::dynamic_pointer_cast( + Canvas::get_item(name)); + }*/ + + void build(); + void arrange(bool use_length_hints); + void show_human_names(bool show); + + void add_plugin(SharedPtr pm); + void add_node(SharedPtr nm); + void remove_node(SharedPtr nm); + void add_port(SharedPtr pm); + void remove_port(SharedPtr pm); + void connection(SharedPtr cm); + void disconnection(SharedPtr cm); + + void get_new_module_location(double& x, double& y); + + void destroy_selection(); + void copy_selection(); + void paste(); + void select_all(); + + void show_menu(GdkEvent* event); + + bool canvas_key_event(GdkEventKey* event); + +private: + enum ControlType { NUMBER, BUTTON }; + void menu_add_control(ControlType type); + + string generate_port_name(const string& base); + void menu_add_port(const string& name, const string& type, bool is_output); + + void menu_load_plugin(); + void menu_new_patch(); + void menu_load_patch(); + void load_plugin(SharedPtr plugin); + + void build_internal_menu(); +#ifdef HAVE_SLV2 + void build_plugin_menu(); + size_t build_plugin_class_menu(Gtk::Menu* menu, + SLV2PluginClass plugin_class, SLV2PluginClasses classes); +#endif + + GraphObject::Variables get_initial_data(); + + bool canvas_event(GdkEvent* event); + + SharedPtr get_port_view(SharedPtr port); + + void connect(boost::shared_ptr src, + boost::shared_ptr dst); + + void disconnect(boost::shared_ptr src, + boost::shared_ptr dst); + + SharedPtr _patch; + + typedef std::map, SharedPtr > Views; + Views _views; + + int _last_click_x; + int _last_click_y; + + bool _refresh_menu; + bool _human_names; + Gtk::Menu* _menu; + Gtk::Menu* _internal_menu; + Gtk::Menu* _plugin_menu; + /*Gtk::MenuItem* _menu_add_number_control; + Gtk::MenuItem* _menu_add_button_control;*/ + Gtk::MenuItem* _menu_add_audio_input; + Gtk::MenuItem* _menu_add_audio_output; + Gtk::MenuItem* _menu_add_control_input; + Gtk::MenuItem* _menu_add_control_output; + Gtk::MenuItem* _menu_add_event_input; + Gtk::MenuItem* _menu_add_event_output; + Gtk::MenuItem* _menu_add_midi_input; + Gtk::MenuItem* _menu_add_midi_output; + Gtk::MenuItem* _menu_add_osc_input; + Gtk::MenuItem* _menu_add_osc_output; + Gtk::MenuItem* _menu_load_plugin; + Gtk::MenuItem* _menu_load_patch; + Gtk::MenuItem* _menu_new_patch; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // PATCHCANVAS_H diff --git a/src/gui/PatchPortModule.cpp b/src/gui/PatchPortModule.cpp new file mode 100644 index 00000000..d8aaa91d --- /dev/null +++ b/src/gui/PatchPortModule.cpp @@ -0,0 +1,158 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "PatchPortModule.hpp" +#include "interface/EngineInterface.hpp" +#include "client/PatchModel.hpp" +#include "client/NodeModel.hpp" +#include "App.hpp" +#include "PatchCanvas.hpp" +#include "Port.hpp" +#include "GladeFactory.hpp" +#include "RenameWindow.hpp" +#include "PatchWindow.hpp" +#include "WindowFactory.hpp" +#include "PortMenu.hpp" + +namespace Ingen { +namespace GUI { + + +PatchPortModule::PatchPortModule(boost::shared_ptr canvas, SharedPtr port) +: FlowCanvas::Module(canvas, port->path().name(), 0, 0, false), // FIXME: coords? + _port(port) +{ + assert(canvas); + assert(port); + + assert(PtrCast(port->parent())); + + /*resize(); + + const Atom& x_atom = port->get_variable("ingenuity:canvas-x"); + const Atom& y_atom = port->get_variable("ingenuity:canvas-y"); + + if (x_atom && y_atom && x_atom.type() == Atom::FLOAT && y_atom.type() == Atom::FLOAT) { + move_to(x_atom.get_float(), y_atom.get_float()); + } else { + double default_x; + double default_y; + canvas->get_new_module_location(default_x, default_y); + move_to(default_x, default_y); + }*/ + + set_stacked_border(port->polyphonic()); + + port->signal_variable.connect(sigc::mem_fun(this, &PatchPortModule::set_variable)); + port->signal_property.connect(sigc::mem_fun(this, &PatchPortModule::set_property)); +} + + +boost::shared_ptr +PatchPortModule::create(boost::shared_ptr canvas, SharedPtr port) +{ + boost::shared_ptr ret = boost::shared_ptr( + new PatchPortModule(canvas, port)); + assert(ret); + + ret->_patch_port = boost::shared_ptr(new Port(ret, port, port->symbol(), true)); + + ret->add_port(ret->_patch_port); + + ret->set_menu(ret->_patch_port->menu()); + + for (GraphObject::Variables::const_iterator m = port->variables().begin(); m != port->variables().end(); ++m) + ret->set_variable(m->first, m->second); + + for (GraphObject::Properties::const_iterator m = port->properties().begin(); m != port->properties().end(); ++m) + ret->set_property(m->first, m->second); + + ret->resize(); + + return ret; +} + + +void +PatchPortModule::create_menu() +{ + Glib::RefPtr xml = GladeFactory::new_glade_reference(); + xml->get_widget_derived("object_menu", _menu); + _menu->init(_port, true); + + set_menu(_menu); +} + + +void +PatchPortModule::store_location() +{ + const float x = static_cast(property_x()); + const float y = static_cast(property_y()); + + const Atom& existing_x = _port->get_variable("ingenuity:canvas-x"); + const Atom& existing_y = _port->get_variable("ingenuity:canvas-y"); + + if (existing_x.type() != Atom::FLOAT || existing_y.type() != Atom::FLOAT + || existing_x.get_float() != x || existing_y.get_float() != y) { + App::instance().engine()->set_variable(_port->path(), "ingenuity:canvas-x", Atom(x)); + App::instance().engine()->set_variable(_port->path(), "ingenuity:canvas-y", Atom(y)); + } +} + + +void +PatchPortModule::set_variable(const string& key, const Atom& value) +{ + if (key == "ingenuity:canvas-x" && value.type() == Atom::FLOAT) + move_to(value.get_float(), property_y()); + else if (key == "ingenuity:canvas-y" && value.type() == Atom::FLOAT) + move_to(property_x(), value.get_float()); +} + + +void +PatchPortModule::set_property(const string& key, const Atom& value) +{ + if (key == "ingen:polyphonic" && value.type() == Atom::BOOL) { + set_stacked_border(value.get_bool()); + } else if (key == "ingen:selected" && value.type() == Atom::BOOL) { + if (value.get_bool() != selected()) { + if (value.get_bool()) + _canvas.lock()->select_item(shared_from_this()); + else + _canvas.lock()->unselect_item(shared_from_this()); + } + } +} + + +void +PatchPortModule::set_selected(bool b) +{ + if (b != selected()) { + Module::set_selected(b); + if (App::instance().signal()) + App::instance().engine()->set_property(_port->path(), "ingen:selected", b); + } +} + + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/PatchPortModule.hpp b/src/gui/PatchPortModule.hpp new file mode 100644 index 00000000..d6715834 --- /dev/null +++ b/src/gui/PatchPortModule.hpp @@ -0,0 +1,79 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PATCHPORTMODULE_H +#define PATCHPORTMODULE_H + +#include +#include +#include +#include +#include +#include "Port.hpp" +using std::string; + +namespace Ingen { namespace Client { + class PortModel; + class NodeModel; +} } +using namespace Ingen::Client; + +namespace Ingen { +namespace GUI { + +class PatchCanvas; +class Port; +class PortMenu; + + +/** A "module" to represent a patch's port on it's own canvas. + * + * Translation: This is the nameless single port pseudo module thingy. + * + * \ingroup GUI + */ +class PatchPortModule : public FlowCanvas::Module +{ +public: + static boost::shared_ptr create(boost::shared_ptr canvas, + SharedPtr port); + + virtual ~PatchPortModule() {} + + virtual void store_location(); + + SharedPtr port() const { return _port; } + +protected: + PatchPortModule(boost::shared_ptr canvas, SharedPtr port); + + void create_menu(); + void set_selected(bool b); + + void set_variable(const std::string& predicate, const Raul::Atom& value); + void set_property(const std::string& predicate, const Raul::Atom& value); + + SharedPtr _port; + PortMenu* _menu; + SharedPtr _patch_port; ///< Port on this 'anonymous' module +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // PATCHPORTMODULE_H diff --git a/src/gui/PatchPropertiesWindow.cpp b/src/gui/PatchPropertiesWindow.cpp new file mode 100644 index 00000000..f4003d31 --- /dev/null +++ b/src/gui/PatchPropertiesWindow.cpp @@ -0,0 +1,92 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "client/PatchModel.hpp" +#include "PatchPropertiesWindow.hpp" +#include "App.hpp" + +using namespace std; + +namespace Ingen { +namespace GUI { + + +PatchPropertiesWindow::PatchPropertiesWindow(BaseObjectType* cobject, const Glib::RefPtr& glade_xml) +: Gtk::Window(cobject) +{ + glade_xml->get_widget("properties_author_entry", _author_entry); + glade_xml->get_widget("properties_description_textview", _textview); + glade_xml->get_widget("properties_cancel_button", _cancel_button); + glade_xml->get_widget("properties_ok_button", _ok_button); + + _cancel_button->signal_clicked().connect(sigc::mem_fun(this, &PatchPropertiesWindow::cancel_clicked)); + _ok_button->signal_clicked().connect(sigc::mem_fun(this, &PatchPropertiesWindow::ok_clicked)); +} + + +/** Set the patch model this description is for. + * + * This function is a "post-constructor" - it MUST be called before using + * the window in any way. + */ +void +PatchPropertiesWindow::set_patch(SharedPtr patch_model) +{ + property_title() = patch_model->path() + " Properties"; + _patch_model = patch_model; + + const Atom& author_atom = _patch_model->get_variable("dc:creator"); + _author_entry->set_text( + (author_atom.type() == Atom::STRING) ? author_atom.get_string() : "" ); + + const Atom& desc_atom = _patch_model->get_variable("dc:description"); + _textview->get_buffer()->set_text( + (desc_atom.type() == Atom::STRING) ? desc_atom.get_string() : "" ); +} + + +void +PatchPropertiesWindow::cancel_clicked() +{ + const Atom& author_atom = _patch_model->get_variable("dc:creator"); + _author_entry->set_text( + (author_atom.type() == Atom::STRING) ? author_atom.get_string() : "" ); + + const Atom& desc_atom = _patch_model->get_variable("dc:description"); + _textview->get_buffer()->set_text( + (desc_atom.type() == Atom::STRING) ? desc_atom.get_string() : "" ); + + hide(); +} + + +void +PatchPropertiesWindow::ok_clicked() +{ + App::instance().engine()->set_variable(_patch_model->path(), "dc:creator", + Atom(_author_entry->get_text())); + App::instance().engine()->set_variable(_patch_model->path(), "dc:description", + Atom(_textview->get_buffer()->get_text())); + hide(); +} + + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/PatchPropertiesWindow.hpp b/src/gui/PatchPropertiesWindow.hpp new file mode 100644 index 00000000..0f60b147 --- /dev/null +++ b/src/gui/PatchPropertiesWindow.hpp @@ -0,0 +1,64 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PATCHPROPERTIESWINDOW_H +#define PATCHPROPERTIESWINDOW_H + +#include +#include +#include +#include +using std::string; + +namespace Ingen { namespace Client { class PatchModel; } } +using Ingen::Client::PatchModel; + +namespace Ingen { +namespace GUI { + + +/** Patch Properties Window. + * + * Loaded by libglade as a derived object. + * + * \ingroup GUI + */ +class PatchPropertiesWindow : public Gtk::Window +{ +public: + PatchPropertiesWindow(BaseObjectType* cobject, const Glib::RefPtr& refGlade); + + void present(SharedPtr patch_model) { set_patch(patch_model); Gtk::Window::present(); } + void set_patch(SharedPtr patch_model); + + void cancel_clicked(); + void ok_clicked(); + +private: + SharedPtr _patch_model; + + Gtk::Entry* _author_entry; + Gtk::TextView* _textview; + Gtk::Button* _cancel_button; + Gtk::Button* _ok_button; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // PATCHPROPERTIESWINDOW_H diff --git a/src/gui/PatchTreeWindow.cpp b/src/gui/PatchTreeWindow.cpp new file mode 100644 index 00000000..4730da2b --- /dev/null +++ b/src/gui/PatchTreeWindow.cpp @@ -0,0 +1,243 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "interface/EngineInterface.hpp" +#include "client/OSCEngineSender.hpp" +#include "client/ClientStore.hpp" +#include "client/PatchModel.hpp" +#include "App.hpp" +#include "PatchTreeWindow.hpp" +#include "SubpatchModule.hpp" +#include "WindowFactory.hpp" + +using namespace std; + +namespace Ingen { +namespace GUI { + + +PatchTreeWindow::PatchTreeWindow(BaseObjectType* cobject, + const Glib::RefPtr& xml) + : Gtk::Window(cobject) + , _enable_signal(true) +{ + xml->get_widget_derived("patches_treeview", _patches_treeview); + + _patch_treestore = Gtk::TreeStore::create(_patch_tree_columns); + _patches_treeview->set_window(this); + _patches_treeview->set_model(_patch_treestore); + Gtk::TreeViewColumn* name_col = Gtk::manage(new Gtk::TreeViewColumn( + "Patch", _patch_tree_columns.name_col)); + Gtk::TreeViewColumn* enabled_col = Gtk::manage(new Gtk::TreeViewColumn( + "Run", _patch_tree_columns.enabled_col)); + name_col->set_resizable(true); + name_col->set_expand(true); + + _patches_treeview->append_column(*name_col); + _patches_treeview->append_column(*enabled_col); + Gtk::CellRendererToggle* enabled_renderer = dynamic_cast( + _patches_treeview->get_column_cell_renderer(1)); + enabled_renderer->property_activatable() = true; + + _patch_tree_selection = _patches_treeview->get_selection(); + + //m_patch_tree_selection->signal_changed().connect( + // sigc::mem_fun(this, &PatchTreeWindow::event_patch_selected)); + _patches_treeview->signal_row_activated().connect( + sigc::mem_fun(this, &PatchTreeWindow::event_patch_activated)); + enabled_renderer->signal_toggled().connect( + sigc::mem_fun(this, &PatchTreeWindow::event_patch_enabled_toggled)); + + _patches_treeview->columns_autosize(); +} + + +void +PatchTreeWindow::init(ClientStore& store) +{ + store.signal_new_object.connect(sigc::mem_fun(this, &PatchTreeWindow::new_object)); +} + + +void +PatchTreeWindow::new_object(SharedPtr object) +{ + SharedPtr patch = PtrCast(object); + if (patch) + add_patch(patch); +} + + +void +PatchTreeWindow::add_patch(SharedPtr pm) +{ + if (!pm->parent()) { + Gtk::TreeModel::iterator iter = _patch_treestore->append(); + Gtk::TreeModel::Row row = *iter; + if (pm->path() == "/") { + SharedPtr osc_sender = PtrCast(App::instance().engine()); + string root_name = osc_sender ? osc_sender->uri() : "Internal"; + // Hack off trailing '/' if it's there (ugly) + //if (root_name.substr(root_name.length()-1,1) == "/") + // root_name = root_name.substr(0, root_name.length()-1); + //root_name.append(":/"); + row[_patch_tree_columns.name_col] = root_name; + } else { + row[_patch_tree_columns.name_col] = pm->path().name(); + } + row[_patch_tree_columns.enabled_col] = false; + row[_patch_tree_columns.patch_model_col] = pm; + _patches_treeview->expand_row(_patch_treestore->get_path(iter), true); + } else { + Gtk::TreeModel::Children children = _patch_treestore->children(); + Gtk::TreeModel::iterator c = find_patch(children, pm->parent()->path()); + + if (c != children.end()) { + Gtk::TreeModel::iterator iter = _patch_treestore->append(c->children()); + Gtk::TreeModel::Row row = *iter; + row[_patch_tree_columns.name_col] = pm->path().name(); + row[_patch_tree_columns.enabled_col] = false; + row[_patch_tree_columns.patch_model_col] = pm; + _patches_treeview->expand_row(_patch_treestore->get_path(iter), true); + } + } + + pm->signal_property.connect(sigc::bind(sigc::mem_fun(this, &PatchTreeWindow::patch_property_changed), pm->path())); +} + + +void +PatchTreeWindow::remove_patch(const Path& path) +{ + Gtk::TreeModel::iterator i = find_patch(_patch_treestore->children(), path); + if (i != _patch_treestore->children().end()) + _patch_treestore->erase(i); +} + + +Gtk::TreeModel::iterator +PatchTreeWindow::find_patch(Gtk::TreeModel::Children root, const Path& path) +{ + for (Gtk::TreeModel::iterator c = root.begin(); c != root.end(); ++c) { + SharedPtr pm = (*c)[_patch_tree_columns.patch_model_col]; + if (pm->path() == path) { + return c; + } else if ((*c)->children().size() > 0) { + Gtk::TreeModel::iterator ret = find_patch(c->children(), path); + if (ret != c->children().end()) + return ret; + } + } + return root.end(); +} + +/* +void +PatchTreeWindow::event_patch_selected() +{ + Gtk::TreeModel::iterator active = _patch_tree_selection->get_selected(); + if (active) { + Gtk::TreeModel::Row row = *active; + SharedPtr pm = row[_patch_tree_columns.patch_model_col]; + } +} +*/ + + +/** Show the context menu for the selected patch in the patches treeview. + */ +void +PatchTreeWindow::show_patch_menu(GdkEventButton* ev) +{ + Gtk::TreeModel::iterator active = _patch_tree_selection->get_selected(); + if (active) { + Gtk::TreeModel::Row row = *active; + SharedPtr pm = row[_patch_tree_columns.patch_model_col]; + if (pm) + cerr << "FIXME: patch menu\n"; + //pm->show_menu(ev); + } +} + + +void +PatchTreeWindow::event_patch_activated(const Gtk::TreeModel::Path& path, Gtk::TreeView::Column* col) +{ + Gtk::TreeModel::iterator active = _patch_treestore->get_iter(path); + Gtk::TreeModel::Row row = *active; + SharedPtr pm = row[_patch_tree_columns.patch_model_col]; + + App::instance().window_factory()->present_patch(pm); +} + + +void +PatchTreeWindow::event_patch_enabled_toggled(const Glib::ustring& path_str) +{ + Gtk::TreeModel::Path path(path_str); + Gtk::TreeModel::iterator active = _patch_treestore->get_iter(path); + Gtk::TreeModel::Row row = *active; + + SharedPtr pm = row[_patch_tree_columns.patch_model_col]; + Glib::ustring patch_path = pm->path(); + + assert(pm); + + if (_enable_signal) + App::instance().engine()->set_property(patch_path, "ingen:enabled", (bool)!pm->enabled()); +} + + +void +PatchTreeWindow::patch_property_changed(const string& key, const Raul::Atom& value, const Path& path) +{ + _enable_signal = false; + if (key == "ingen:enabled" && value.type() == Atom::BOOL) { + Gtk::TreeModel::iterator i = find_patch(_patch_treestore->children(), path); + if (i != _patch_treestore->children().end()) { + Gtk::TreeModel::Row row = *i; + row[_patch_tree_columns.enabled_col] = value.get_bool(); + } else { + cerr << "[PatchTreeWindow] Unable to find patch " << path << endl; + } + } + _enable_signal = true; +} + + +void +PatchTreeWindow::patch_renamed(const Path& old_path, const Path& new_path) +{ + _enable_signal = false; + + Gtk::TreeModel::iterator i + = find_patch(_patch_treestore->children(), old_path); + + if (i != _patch_treestore->children().end()) { + Gtk::TreeModel::Row row = *i; + row[_patch_tree_columns.name_col] = new_path.name(); + } else { + cerr << "[PatchTreeWindow] Unable to find patch " << old_path << endl; + } + + _enable_signal = true; +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/PatchTreeWindow.hpp b/src/gui/PatchTreeWindow.hpp new file mode 100644 index 00000000..f549a322 --- /dev/null +++ b/src/gui/PatchTreeWindow.hpp @@ -0,0 +1,111 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PATCHTREEWINDOW_H +#define PATCHTREEWINDOW_H + +#include +#include +#include + +namespace Ingen { namespace Client { + class ClientStore; +} } +using Ingen::Client::ClientStore; + +namespace Ingen { +namespace GUI { + +class PatchWindow; +class PatchTreeView; + + +/** Window with a TreeView of all loaded patches. + * + * \ingroup GUI + */ +class PatchTreeWindow : public Gtk::Window +{ +public: + PatchTreeWindow(BaseObjectType* cobject, const Glib::RefPtr& refGlade); + + void init(ClientStore& store); + + void new_object(SharedPtr object); + + void patch_property_changed(const string& key, const Raul::Atom& value, const Path& path); + void patch_renamed(const Path& old_path, const Path& new_path); + + void add_patch(SharedPtr pm); + void remove_patch(const Path& path); + void show_patch_menu(GdkEventButton* ev); + +protected: + //void event_patch_selected(); + void event_patch_activated(const Gtk::TreeModel::Path& path, Gtk::TreeView::Column* col); + void event_patch_enabled_toggled(const Glib::ustring& path_str); + + Gtk::TreeModel::iterator find_patch(Gtk::TreeModel::Children root, const Path& path); + + PatchTreeView* _patches_treeview; + + struct PatchTreeModelColumns : public Gtk::TreeModel::ColumnRecord + { + PatchTreeModelColumns() + { add(name_col); add(enabled_col); add(patch_model_col); } + + Gtk::TreeModelColumn name_col; + Gtk::TreeModelColumn enabled_col; + Gtk::TreeModelColumn > patch_model_col; + }; + + bool _enable_signal; + PatchTreeModelColumns _patch_tree_columns; + Glib::RefPtr _patch_treestore; + Glib::RefPtr _patch_tree_selection; +}; + + +/** Derived TreeView class to support context menus for patches */ +class PatchTreeView : public Gtk::TreeView +{ +public: + PatchTreeView(BaseObjectType* cobject, const Glib::RefPtr& xml) + : Gtk::TreeView(cobject) + {} + + void set_window(PatchTreeWindow* win) { _window = win; } + + bool on_button_press_event(GdkEventButton* ev) { + bool ret = Gtk::TreeView::on_button_press_event(ev); + + if ((ev->type == GDK_BUTTON_PRESS) && (ev->button == 3)) + _window->show_patch_menu(ev); + + return ret; + } + +private: + PatchTreeWindow* _window; + +}; // struct PatchTreeView + + +} // namespace GUI +} // namespace Ingen + +#endif // PATCHTREEWINDOW_H diff --git a/src/gui/PatchView.cpp b/src/gui/PatchView.cpp new file mode 100644 index 00000000..13689806 --- /dev/null +++ b/src/gui/PatchView.cpp @@ -0,0 +1,194 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include "interface/EngineInterface.hpp" +#include "client/PatchModel.hpp" +#include "App.hpp" +#include "PatchView.hpp" +#include "PatchCanvas.hpp" +#include "LoadPluginWindow.hpp" +#include "NewSubpatchWindow.hpp" +#include "LoadSubpatchWindow.hpp" +#include "NodeControlWindow.hpp" +#include "PatchPropertiesWindow.hpp" +#include "PatchTreeWindow.hpp" +#include "GladeFactory.hpp" + +namespace Ingen { +namespace GUI { + + +PatchView::PatchView(BaseObjectType* cobject, const Glib::RefPtr& xml) +: Gtk::Box(cobject), + _breadcrumb_container(NULL), + _enable_signal(true) +{ + property_visible() = false; + + xml->get_widget("patch_view_breadcrumb_container", _breadcrumb_container); + xml->get_widget("patch_view_toolbar", _toolbar); + xml->get_widget("patch_view_process_but", _process_but); + xml->get_widget("patch_view_poly_spin", _poly_spin); + xml->get_widget("patch_view_clear_but", _clear_but); + xml->get_widget("patch_view_destroy_but", _destroy_but); + xml->get_widget("patch_view_refresh_but", _refresh_but); + xml->get_widget("patch_view_save_but", _save_but); + xml->get_widget("patch_view_zoom_full_but", _zoom_full_but); + xml->get_widget("patch_view_zoom_normal_but", _zoom_normal_but); + xml->get_widget("patch_view_edit_mode_but", _edit_mode_but); + xml->get_widget("patch_view_scrolledwindow", _canvas_scrolledwindow); + + _toolbar->set_toolbar_style(Gtk::TOOLBAR_ICONS); + _canvas_scrolledwindow->property_hadjustment().get_value()->set_step_increment(10); + _canvas_scrolledwindow->property_vadjustment().get_value()->set_step_increment(10); + +} + + +void +PatchView::set_patch(SharedPtr patch) +{ + assert(!_canvas); // FIXME: remove + + //cerr << "Creating view for " << patch->path() << endl; + + assert(_breadcrumb_container); // ensure created + + _patch = patch; + _canvas = SharedPtr(new PatchCanvas(patch, 1600*2, 1200*2)); + _canvas->build(); + + _canvas_scrolledwindow->add(*_canvas); + + _poly_spin->set_value(patch->poly()); + _destroy_but->set_sensitive(patch->path() != "/"); + + for (GraphObject::Properties::const_iterator i = patch->properties().begin(); + i != patch->properties().end(); ++i) + property_changed(i->first, i->second); + + // Connect model signals to track state + patch->signal_property.connect(sigc::mem_fun(this, &PatchView::property_changed)); + + // Connect widget signals to do things + _process_but->signal_toggled().connect(sigc::mem_fun(this, &PatchView::process_toggled)); + _clear_but->signal_clicked().connect(sigc::mem_fun(this, &PatchView::clear_clicked)); + _refresh_but->signal_clicked().connect(sigc::mem_fun(this, &PatchView::refresh_clicked)); + + _zoom_normal_but->signal_clicked().connect(sigc::bind(sigc::mem_fun( + _canvas.get(), &PatchCanvas::set_zoom), 1.0)); + + _zoom_full_but->signal_clicked().connect( + sigc::mem_fun(_canvas.get(), &PatchCanvas::zoom_full)); + + patch->signal_editable.connect(sigc::mem_fun( + *this, &PatchView::on_editable_sig)); + + _edit_mode_but->signal_toggled().connect(sigc::mem_fun( + *this, &PatchView::editable_toggled)); + + _poly_spin->signal_value_changed().connect( + sigc::mem_fun(*this, &PatchView::poly_changed)); + + _canvas->grab_focus(); +} + + +PatchView::~PatchView() +{ + //cerr << "Destroying view for " << _patch->path() << endl; +} + + +SharedPtr +PatchView::create(SharedPtr patch) + +{ + const Glib::RefPtr& xml = GladeFactory::new_glade_reference("patch_view_box"); + PatchView* result = NULL; + xml->get_widget_derived("patch_view_box", result); + assert(result); + result->set_patch(patch); + return SharedPtr(result); +} + + +void +PatchView::on_editable_sig(bool editable) +{ + _edit_mode_but->set_active(editable); + _canvas->lock(!editable); +} + + +void +PatchView::editable_toggled() +{ + const bool editable = _edit_mode_but->get_active(); + _patch->set_editable(editable); + _canvas->lock(!editable); +} + + +void +PatchView::process_toggled() +{ + if (!_enable_signal) + return; + + App::instance().engine()->set_property(_patch->path(), "ingen:enabled", + (bool)_process_but->get_active()); +} + + +void +PatchView::poly_changed() +{ + App::instance().engine()->set_property(_patch->path(), "ingen:polyphony", + _poly_spin->get_value_as_int()); +} + + +void +PatchView::clear_clicked() +{ + App::instance().engine()->clear_patch(_patch->path()); +} + + +void +PatchView::refresh_clicked() +{ + App::instance().engine()->request_object(_patch->path()); +} + + +void +PatchView::property_changed(const std::string& predicate, const Raul::Atom& value) +{ + _enable_signal = false; + if (predicate == "ingen:enabled" && value.type() == Atom::BOOL) + _process_but->set_active(value.get_bool()); + _enable_signal = true; +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/PatchView.hpp b/src/gui/PatchView.hpp new file mode 100644 index 00000000..2c0570bd --- /dev/null +++ b/src/gui/PatchView.hpp @@ -0,0 +1,105 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PATCHVIEW_H +#define PATCHVIEW_H + +#include +#include +#include +#include +#include +#include "client/PatchModel.hpp" + +using std::string; + +namespace Ingen { namespace Client { + class PortModel; + class MetadataModel; +} } +using namespace Ingen::Client; + + +namespace Ingen { +namespace GUI { + +class PatchCanvas; +class LoadPluginWindow; +class NewSubpatchWindow; +class LoadSubpatchWindow; +class NewSubpatchWindow; +class NodeControlWindow; +class PatchDescriptionWindow; +class SubpatchModule; +class OmPort; + + +/** The patch specific contents of a PatchWindow (ie the canvas and whatever else). + * + * \ingroup GUI + */ +class PatchView : public Gtk::Box +{ +public: + PatchView(BaseObjectType* cobject, const Glib::RefPtr& glade_xml); + ~PatchView(); + + SharedPtr canvas() const { return _canvas; } + SharedPtr patch() const { return _patch; } + Gtk::Viewport* breadcrumb_container() const { return _breadcrumb_container; } + + static SharedPtr create(SharedPtr patch); + +private: + void set_patch(SharedPtr patch); + + void process_toggled(); + void poly_changed(); + void clear_clicked(); + void refresh_clicked(); + void on_editable_sig(bool locked); + void editable_toggled(); + + void property_changed(const std::string& predicate, const Raul::Atom& value); + + void zoom_full(); + + SharedPtr _patch; + SharedPtr _canvas; + + Gtk::ScrolledWindow* _canvas_scrolledwindow; + + Gtk::Toolbar* _toolbar; + Gtk::ToggleToolButton* _process_but; + Gtk::SpinButton* _poly_spin; + Gtk::ToolButton* _clear_but; + Gtk::ToolButton* _destroy_but; + Gtk::ToolButton* _refresh_but; + Gtk::ToolButton* _save_but; + Gtk::ToolButton* _zoom_normal_but; + Gtk::ToolButton* _zoom_full_but; + Gtk::ToggleToolButton* _edit_mode_but; + Gtk::Viewport* _breadcrumb_container; + + bool _enable_signal; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // PATCHVIEW_H diff --git a/src/gui/PatchWindow.cpp b/src/gui/PatchWindow.cpp new file mode 100644 index 00000000..d7499bfb --- /dev/null +++ b/src/gui/PatchWindow.cpp @@ -0,0 +1,556 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "PatchWindow.hpp" +#include +#include +#include +#include "interface/EngineInterface.hpp" +#include "client/PatchModel.hpp" +#include "client/ClientStore.hpp" +#include "App.hpp" +#include "PatchCanvas.hpp" +#include "LoadPluginWindow.hpp" +#include "NewSubpatchWindow.hpp" +#include "LoadPatchWindow.hpp" +#include "LoadSubpatchWindow.hpp" +#include "NodeControlWindow.hpp" +#include "PatchPropertiesWindow.hpp" +#include "Configuration.hpp" +#include "MessagesWindow.hpp" +#include "PatchTreeWindow.hpp" +#include "BreadCrumbBox.hpp" +#include "ConnectWindow.hpp" +#include "ThreadedLoader.hpp" +#include "WindowFactory.hpp" +#include "PatchView.hpp" + +namespace Ingen { +namespace GUI { + + +PatchWindow::PatchWindow(BaseObjectType* cobject, const Glib::RefPtr& xml) + : Gtk::Window(cobject) + , _enable_signal(true) + , _position_stored(false) + , _x(0) + , _y(0) + , _breadcrumb_box(NULL) +{ + property_visible() = false; + + xml->get_widget("patch_win_vbox", _vbox); + xml->get_widget("patch_win_viewport", _viewport); + //xml->get_widget("patch_win_status_bar", _status_bar); + //xml->get_widget("patch_open_menuitem", _menu_open); + xml->get_widget("patch_import_menuitem", _menu_import); + xml->get_widget("patch_import_location_menuitem", _menu_import_location); + //xml->get_widget("patch_open_into_menuitem", _menu_open_into); + xml->get_widget("patch_save_menuitem", _menu_save); + xml->get_widget("patch_save_as_menuitem", _menu_save_as); + xml->get_widget("patch_upload_menuitem", _menu_upload); + xml->get_widget("patch_cut_menuitem", _menu_cut); + xml->get_widget("patch_copy_menuitem", _menu_copy); + xml->get_widget("patch_paste_menuitem", _menu_paste); + xml->get_widget("patch_delete_menuitem", _menu_delete); + xml->get_widget("patch_select_all_menuitem", _menu_select_all); + xml->get_widget("patch_close_menuitem", _menu_close); + xml->get_widget("patch_quit_menuitem", _menu_quit); + xml->get_widget("patch_view_control_window_menuitem", _menu_view_control_window); + xml->get_widget("patch_view_engine_window_menuitem", _menu_view_engine_window); + xml->get_widget("patch_properties_menuitem", _menu_view_patch_properties); + xml->get_widget("patch_fullscreen_menuitem", _menu_fullscreen); + xml->get_widget("patch_human_names_menuitem", _menu_human_names); + xml->get_widget("patch_arrange_menuitem", _menu_arrange); + xml->get_widget("patch_clear_menuitem", _menu_clear); + xml->get_widget("patch_destroy_menuitem", _menu_destroy_patch); + xml->get_widget("patch_view_messages_window_menuitem", _menu_view_messages_window); + xml->get_widget("patch_view_patch_tree_window_menuitem", _menu_view_patch_tree_window); + xml->get_widget("patch_help_about_menuitem", _menu_help_about); + + _menu_view_control_window->property_sensitive() = false; + //m_status_bar->push(App::instance().engine()->engine_url()); + //m_status_bar->pack_start(*Gtk::manage(new Gtk::Image(Gtk::Stock::CONNECT, Gtk::ICON_SIZE_MENU)), false, false); + + /*_menu_open->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_open));*/ + _menu_import->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_import)); + _menu_import_location->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_import_location)); + _menu_save->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_save)); + _menu_save_as->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_save_as)); + _menu_upload->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_upload)); + _menu_copy->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_copy)); + _menu_paste->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_paste)); + _menu_delete->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_delete)); + _menu_select_all->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_select_all)); + _menu_quit->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_quit)); + _menu_fullscreen->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_fullscreen_toggled)); + _menu_human_names->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_human_names_toggled)); + _menu_arrange->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_arrange)); + _menu_view_engine_window->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_show_engine)); + _menu_view_control_window->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_show_controls)); + _menu_view_patch_properties->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_show_properties)); + _menu_destroy_patch->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_destroy)); + _menu_clear->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_clear)); + _menu_view_messages_window->signal_activate().connect( + sigc::mem_fun(App::instance().messages_dialog(), &MessagesWindow::present)); + _menu_view_patch_tree_window->signal_activate().connect( + sigc::mem_fun(App::instance().patch_tree(), &PatchTreeWindow::present)); + + _menu_help_about->signal_activate().connect(sigc::hide_return( + sigc::mem_fun(App::instance(), &App::show_about))); + + _breadcrumb_box = new BreadCrumbBox(); + _breadcrumb_box->signal_patch_selected.connect(sigc::mem_fun(this, &PatchWindow::set_patch_from_path)); + +#ifndef HAVE_CURL + _menu_upload->hide(); +#endif + + Glib::RefPtr clipboard = Gtk::Clipboard::get(); + clipboard->signal_owner_change().connect(sigc::mem_fun(this, &PatchWindow::event_clipboard_changed)); +} + + +PatchWindow::~PatchWindow() +{ + // Prevents deletion + //m_patch->claim_patch_view(); + + delete _breadcrumb_box; +} + + +/** Set the patch controller from a Path (for use by eg. BreadCrumbBox) + */ +void +PatchWindow::set_patch_from_path(const Path& path, SharedPtr view) +{ + if (view) { + assert(view->patch()->path() == path); + App::instance().window_factory()->present_patch(view->patch(), this, view); + } else { + SharedPtr model = PtrCast(App::instance().store()->object(path)); + if (model) + App::instance().window_factory()->present_patch(model, this); + } +} + + +/** Sets the patch controller for this window and initializes everything. + * + * If @a view is NULL, a new view will be created. + */ +void +PatchWindow::set_patch(SharedPtr patch, SharedPtr view) +{ + if (!patch || patch == _patch) + return; + + _enable_signal = false; + + new_port_connection.disconnect(); + removed_port_connection.disconnect(); + + _patch = patch; + + _view = view; + + if (!_view) + _view = _breadcrumb_box->view(patch->path()); + + if (!_view) + _view = PatchView::create(patch); + + assert(_view); + + // Add view to our viewport + if (_view->get_parent()) + _view->get_parent()->remove(*_view.get()); + + _viewport->remove(); + _viewport->add(*_view.get()); + + + if (_breadcrumb_box->get_parent()) + _breadcrumb_box->get_parent()->remove(*_breadcrumb_box); + + _view->breadcrumb_container()->remove(); + _view->breadcrumb_container()->add(*_breadcrumb_box); + _view->breadcrumb_container()->show(); + + _breadcrumb_box->build(patch->path(), _view); + _breadcrumb_box->show(); + + _menu_view_control_window->property_sensitive() = false; + + for (NodeModel::Ports::const_iterator p = patch->ports().begin(); + p != patch->ports().end(); ++p) { + if ((*p)->type().is_control() && (*p)->is_input()) { + _menu_view_control_window->property_sensitive() = true; + break; + } + } + + int width, height; + get_size(width, height); + _view->canvas()->scroll_to( + ((int)_view->canvas()->width() - width)/2, + ((int)_view->canvas()->height() - height)/2); + + set_title(_patch->path() + " - Ingen"); + + //m_properties_window->patch_model(pc->patch_model()); + + if (patch->path() == "/") + _menu_destroy_patch->set_sensitive(false); + else + _menu_destroy_patch->set_sensitive(true); + + new_port_connection = patch->signal_new_port.connect(sigc::mem_fun(this, &PatchWindow::patch_port_added)); + removed_port_connection = patch->signal_removed_port.connect(sigc::mem_fun(this, &PatchWindow::patch_port_removed)); + show_all(); + + _enable_signal = true; +} + + +void +PatchWindow::patch_port_added(SharedPtr port) +{ + if (port->type().is_control() && port->is_input()) { + _menu_view_control_window->property_sensitive() = true; + } +} + + +void +PatchWindow::patch_port_removed(SharedPtr port) +{ + if (port->type().is_control() && port->is_input()) { + + bool found_control = false; + + for (NodeModel::Ports::const_iterator i = _patch->ports().begin(); i != _patch->ports().end(); ++i) { + if ((*i)->type().is_control() && (*i)->is_input()) { + found_control = true; + break; + } + } + + _menu_view_control_window->property_sensitive() = found_control; + } +} + + + +void +PatchWindow::event_show_engine() +{ + if (_patch) + App::instance().connect_window()->show(); +} + + +void +PatchWindow::event_clipboard_changed(GdkEventOwnerChange* ev) +{ + Glib::RefPtr clipboard = Gtk::Clipboard::get(); + _menu_paste->set_sensitive(clipboard->wait_is_text_available()); +} + + +void +PatchWindow::event_show_controls() +{ + App::instance().window_factory()->present_controls(_patch); +} + + +void +PatchWindow::event_show_properties() +{ + App::instance().window_factory()->present_properties(_patch); +} + + +void +PatchWindow::event_import() +{ + App::instance().window_factory()->present_load_patch(_patch); +} + + +void +PatchWindow::event_import_location() +{ + App::instance().window_factory()->present_load_remote_patch(_patch); +} + + +void +PatchWindow::event_save() +{ + GraphObject::Variables::const_iterator doc = _patch->variables().find("ingen:document"); + if (doc == _patch->variables().end()) + event_save_as(); + else + App::instance().loader()->save_patch(_patch, doc->second.get_string()); +} + + +void +PatchWindow::event_save_as() +{ + Gtk::FileChooserDialog dialog(*this, "Save Patch", Gtk::FILE_CHOOSER_ACTION_SAVE); + + /*Gtk::VBox* box = dialog.get_vbox(); + Gtk::Label warning("Warning: Recursively saving will overwrite any subpatch files \ + without confirmation."); + box->pack_start(warning, false, false, 2); + Gtk::CheckButton recursive_checkbutton("Recursively save all subpatches"); + box->pack_start(recursive_checkbutton, false, false, 0); + recursive_checkbutton.show(); + */ + + dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); + Gtk::Button* save_button = dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK); + save_button->property_has_default() = true; + + // Set current folder to most sensible default + GraphObject::Variables::const_iterator doc = _patch->variables().find("ingen:document"); + if (doc != _patch->variables().end()) + dialog.set_uri(doc->second.get_string()); + else if (App::instance().configuration()->patch_folder().length() > 0) + dialog.set_current_folder(App::instance().configuration()->patch_folder()); + + int result = dialog.run(); + //bool recursive = recursive_checkbutton.get_active(); + + if (result == Gtk::RESPONSE_OK) { + string filename = dialog.get_filename(); + if (filename.length() < 11 || filename.substr(filename.length()-10) != ".ingen.ttl") + filename += ".ingen.ttl"; + + bool confirm = false; + std::fstream fin; + fin.open(filename.c_str(), std::ios::in); + if (fin.is_open()) { // File exists + string msg = "File already exists! Are you sure you want to overwrite "; + msg += filename + "?"; + Gtk::MessageDialog confirm_dialog(*this, + msg, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_YES_NO, true); + if (confirm_dialog.run() == Gtk::RESPONSE_YES) + confirm = true; + else + confirm = false; + } else { // File doesn't exist + confirm = true; + } + fin.close(); + + if (confirm) { + App::instance().loader()->save_patch(_patch, filename); + } + } + App::instance().configuration()->set_patch_folder(dialog.get_current_folder()); +} + + +void +PatchWindow::event_upload() +{ + App::instance().window_factory()->present_upload_patch(_patch); +} + + +void +PatchWindow::event_copy() +{ + if (_view) + _view->canvas()->copy_selection(); +} + + +void +PatchWindow::event_paste() +{ + if (_view) + _view->canvas()->paste(); +} + + +void +PatchWindow::event_delete() +{ + if (_view) + _view->canvas()->destroy_selection(); +} + + +void +PatchWindow::event_select_all() +{ + if (_view) + _view->canvas()->select_all(); +} + + +void +PatchWindow::on_show() +{ + if (_position_stored) + move(_x, _y); + + Gtk::Window::on_show(); +} + + +void +PatchWindow::on_hide() +{ + _position_stored = true; + get_position(_x, _y); + Gtk::Window::on_hide(); +} + + +bool +PatchWindow::on_key_press_event(GdkEventKey* event) +{ + bool ret = false; + + ret = _view->canvas()->canvas_key_event(event); + + if (!ret) + ret = Gtk::Window::on_key_press_event(event); + + return ret; +} + + +bool +PatchWindow::on_key_release_event(GdkEventKey* event) +{ + bool ret = false; + + ret = _view->canvas()->canvas_key_event(event); + + if (!ret) + ret = Gtk::Window::on_key_release_event(event); + + return ret; +} + + +void +PatchWindow::event_quit() +{ + Gtk::MessageDialog d(*this, "Would you like to quit just this GUI\nor kill the engine as well?", + true, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_NONE, true); + d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); + + Gtk::Button* b = d.add_button(Gtk::Stock::REMOVE, 2); // kill + b->set_label("_Kill Engine"); + Gtk::Widget* kill_img = Gtk::manage(new Gtk::Image(Gtk::Stock::CLOSE, Gtk::ICON_SIZE_BUTTON)); + b->set_image(*kill_img); + + b = d.add_button(Gtk::Stock::QUIT, 1); // just exit + b->set_label("_Quit"); + Gtk::Widget* close_img = Gtk::manage(new Gtk::Image(Gtk::Stock::QUIT, Gtk::ICON_SIZE_BUTTON)); + b->set_image(*close_img); + b->grab_default(); + + int ret = d.run(); + if (ret == 1) { + App::instance().quit(); + } else if (ret == 2) { + App::instance().engine()->quit(); + App::instance().quit(); + } + // Otherwise cancelled, do nothing +} + + +void +PatchWindow::event_destroy() +{ + App::instance().engine()->destroy(_patch->path()); +} + + +void +PatchWindow::event_clear() +{ + App::instance().engine()->clear_patch(_patch->path()); +} + + +void +PatchWindow::event_arrange() +{ + _view->canvas()->arrange(false); +} + + +void +PatchWindow::event_fullscreen_toggled() +{ + // FIXME: ugh, use GTK signals to track state and know for sure + static bool is_fullscreen = false; + + if (!is_fullscreen) { + fullscreen(); + is_fullscreen = true; + } else { + unfullscreen(); + is_fullscreen = false; + } +} + + +void +PatchWindow::event_human_names_toggled() +{ + _view->canvas()->show_human_names(_menu_human_names->get_active()); + if (_menu_human_names->get_active()) + App::instance().configuration()->set_name_style(Configuration::HUMAN); + else + App::instance().configuration()->set_name_style(Configuration::PATH); +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/PatchWindow.hpp b/src/gui/PatchWindow.hpp new file mode 100644 index 00000000..4d4f0f1e --- /dev/null +++ b/src/gui/PatchWindow.hpp @@ -0,0 +1,155 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PATCHWINDOW_H +#define PATCHWINDOW_H + +#include +#include +#include +#include +#include +#include +#include +#include "client/PatchModel.hpp" +#include "PatchView.hpp" +using Ingen::Client::PatchModel; + +using std::string; using std::list; + + +namespace Ingen { namespace Client { + class PatchModel; + class PortModel; + class MetadataModel; +} } +using namespace Ingen::Client; + + +namespace Ingen { +namespace GUI { + +class LoadPluginWindow; +class LoadPatchWindow; +class NewSubpatchWindow; +class LoadSubpatchWindow; +class NewSubpatchWindow; +class NodeControlWindow; +class PatchDescriptionWindow; +class SubpatchModule; +class OmPort; +class BreadCrumbBox; + + +/** A window for a patch. + * + * \ingroup GUI + */ +class PatchWindow : public Gtk::Window +{ +public: + PatchWindow(BaseObjectType* cobject, const Glib::RefPtr& glade_xml); + ~PatchWindow(); + + void set_patch_from_path(const Path& path, SharedPtr view); + void set_patch(SharedPtr pc, SharedPtr view); + + SharedPtr patch() const { return _patch; } + + Gtk::MenuItem* menu_view_control_window() { return _menu_view_control_window; } + +protected: + void on_show(); + void on_hide(); + bool on_key_press_event(GdkEventKey* event); + bool on_key_release_event(GdkEventKey* event); + +private: + + void patch_port_added(SharedPtr port); + void patch_port_removed(SharedPtr port); + + void event_import(); + void event_import_location(); + void event_save(); + void event_save_as(); + void event_upload(); + void event_copy(); + void event_paste(); + void event_delete(); + void event_select_all(); + void event_quit(); + void event_destroy(); + void event_clear(); + void event_fullscreen_toggled(); + void event_human_names_toggled(); + void event_arrange(); + void event_show_properties(); + void event_show_controls(); + void event_show_engine(); + void event_clipboard_changed(GdkEventOwnerChange* ev); + + SharedPtr _patch; + SharedPtr _view; + + sigc::connection new_port_connection; + sigc::connection removed_port_connection; + + bool _enable_signal; + bool _position_stored; + int _x; + int _y; + + Gtk::MenuItem* _menu_import; + Gtk::MenuItem* _menu_import_location; + Gtk::MenuItem* _menu_save; + Gtk::MenuItem* _menu_save_as; + Gtk::MenuItem* _menu_upload; + Gtk::MenuItem* _menu_cut; + Gtk::MenuItem* _menu_copy; + Gtk::MenuItem* _menu_paste; + Gtk::MenuItem* _menu_delete; + Gtk::MenuItem* _menu_select_all; + Gtk::MenuItem* _menu_close; + Gtk::MenuItem* _menu_quit; + Gtk::CheckMenuItem* _menu_human_names; + Gtk::MenuItem* _menu_fullscreen; + Gtk::MenuItem* _menu_clear; + Gtk::MenuItem* _menu_destroy_patch; + Gtk::MenuItem* _menu_arrange; + Gtk::MenuItem* _menu_view_engine_window; + Gtk::MenuItem* _menu_view_control_window; + Gtk::MenuItem* _menu_view_patch_properties; + Gtk::MenuItem* _menu_view_messages_window; + Gtk::MenuItem* _menu_view_patch_tree_window; + Gtk::MenuItem* _menu_help_about; + + Gtk::VBox* _vbox; + Gtk::Viewport* _viewport; + BreadCrumbBox* _breadcrumb_box; + + //Gtk::Statusbar* _status_bar; + + /** Invisible bin used to store breadcrumbs when not shown by a view */ + Gtk::Alignment _breadcrumb_bin; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // PATCHWINDOW_H diff --git a/src/gui/Port.cpp b/src/gui/Port.cpp new file mode 100644 index 00000000..031c74c5 --- /dev/null +++ b/src/gui/Port.cpp @@ -0,0 +1,151 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "interface/EngineInterface.hpp" +#include "flowcanvas/Module.hpp" +#include "client/PatchModel.hpp" +#include "client/PortModel.hpp" +#include "Configuration.hpp" +#include "App.hpp" +#include "Port.hpp" +#include "PortMenu.hpp" +#include "GladeFactory.hpp" + +using namespace Ingen::Client; +using namespace std; + +namespace Ingen { +namespace GUI { + + +/** @param flip Make an input port appear as an output port, and vice versa. + */ +Port::Port( + boost::shared_ptr module, + SharedPtr pm, + const string& name, + bool flip) + : FlowCanvas::Port(module, name, + flip ? (!pm->is_input()) : pm->is_input(), + App::instance().configuration()->get_port_color(pm.get())) + , _port_model(pm) + , _flipped(flip) +{ + assert(module); + assert(_port_model); + + delete _menu; + _menu = NULL; + + _port_model->signal_renamed.connect(sigc::mem_fun(this, &Port::renamed)); + + if (pm->type().is_control()) { + set_toggled(pm->is_toggle()); + show_control(); + + float min = 0.0f, max = 1.0f; + boost::shared_ptr parent = PtrCast(_port_model->parent()); + if (parent) + parent->port_value_range(_port_model, min, max); + + set_control_min(min); + set_control_max(max); + + pm->signal_variable.connect(sigc::mem_fun(this, &Port::variable_changed)); + _port_model->signal_value_changed.connect(sigc::mem_fun(this, &Port::value_changed)); + } + + _port_model->signal_activity.connect(sigc::mem_fun(this, &Port::activity)); + + value_changed(_port_model->value()); +} + + +Port::~Port() +{ + App::instance().activity_port_destroyed(this); +} + + +void +Port::create_menu() +{ + PortMenu* menu = NULL; + Glib::RefPtr xml = GladeFactory::new_glade_reference(); + xml->get_widget_derived("object_menu", menu); + menu->init(_port_model, _flipped); + set_menu(menu); +} + + +void +Port::renamed() +{ + set_name(_port_model->path().name()); + module().lock()->resize(); +} + + +void +Port::value_changed(const Atom& value) +{ + if (value.type() == Atom::FLOAT) + FlowCanvas::Port::set_control(value.get_float()); + else + cerr << "WARNING: Unknown port value type " << (unsigned)value.type() << endl; +} + + +void +Port::activity() +{ + App::instance().port_activity(this); +} + + +void +Port::set_control(float value, bool signal) +{ + if (signal) { + if (_port_model->type() == DataType::CONTROL) { + App::instance().engine()->set_port_value(_port_model->path(), Atom(value)); + } else if (_port_model->type() == DataType::EVENT) { + App::instance().engine()->set_port_value(_port_model->path(), + Atom("", 0, NULL)); + } + } + + FlowCanvas::Port::set_control(value); +} + + +void +Port::variable_changed(const string& key, const Atom& value) +{ + if ( (key == "ingen:minimum") && value.type() == Atom::FLOAT) + set_control_min(value.get_float()); + else if ( (key == "ingen:maximum") && value.type() == Atom::FLOAT) + set_control_max(value.get_float()); + else if ( (key == "ingen:toggled") && value.type() == Atom::BOOL) + set_toggled(value.get_bool()); +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/Port.hpp b/src/gui/Port.hpp new file mode 100644 index 00000000..7b347194 --- /dev/null +++ b/src/gui/Port.hpp @@ -0,0 +1,70 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef GUI_PORT_H +#define GUI_PORT_H + +#include +#include +#include +#include +#include + +namespace Ingen { namespace Client { class PortModel; } } +using Ingen::Client::PortModel; + +namespace Ingen { +namespace GUI { + + +/** A Port on an Module. + * + * \ingroup GUI + */ +class Port : public FlowCanvas::Port +{ +public: + Port(boost::shared_ptr module, + SharedPtr pm, + const std::string& name, + bool flip=false); + + ~Port(); + + SharedPtr model() const { return _port_model; } + + void create_menu(); + + virtual void set_control(float value, bool signal); + void value_changed(const Raul::Atom& value); + void activity(); + +private: + + void variable_changed(const std::string& key, const Raul::Atom& value); + + void renamed(); + + SharedPtr _port_model; + bool _flipped; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // GUI_PORT_H diff --git a/src/gui/PortMenu.cpp b/src/gui/PortMenu.cpp new file mode 100644 index 00000000..45b216b6 --- /dev/null +++ b/src/gui/PortMenu.cpp @@ -0,0 +1,69 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include "interface/EngineInterface.hpp" +#include "client/PortModel.hpp" +#include "App.hpp" +#include "PortMenu.hpp" +#include "WindowFactory.hpp" + +namespace Ingen { +namespace GUI { + + +PortMenu::PortMenu(BaseObjectType* cobject, const Glib::RefPtr& xml) + : ObjectMenu(cobject, xml) + , _patch_port(NULL) +{ +} + + +void +PortMenu::init(SharedPtr port, bool patch_port) +{ + ObjectMenu::init(port); + _patch_port = patch_port; + + if ( ! PtrCast(port->parent()) ) { + _polyphonic_menuitem->set_sensitive(false); + _rename_menuitem->hide(); + _destroy_menuitem->hide(); + } + + _enable_signal = true; +} + + +void +PortMenu::on_menu_disconnect() +{ + if (_patch_port) { + App::instance().engine()->disconnect_all( + _object->parent()->path(), _object->path()); + } else { + App::instance().engine()->disconnect_all( + _object->parent()->path().parent(), _object->path()); + } +} + + +} // namespace GUI +} // namespace Ingen + diff --git a/src/gui/PortMenu.hpp b/src/gui/PortMenu.hpp new file mode 100644 index 00000000..520c5809 --- /dev/null +++ b/src/gui/PortMenu.hpp @@ -0,0 +1,55 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PORTMENU_H +#define PORTMENU_H + +#include +#include +#include +#include +#include "client/PortModel.hpp" +#include "ObjectMenu.hpp" + +using Ingen::Client::PortModel; + +namespace Ingen { +namespace GUI { + + +/** Menu for a Port. + * + * \ingroup GUI + */ +class PortMenu : public ObjectMenu +{ +public: + PortMenu(BaseObjectType* cobject, const Glib::RefPtr& xml); + + void init(SharedPtr port, bool patch_port = false); + +private: + void on_menu_disconnect(); + + bool _patch_port; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // PORTMENU_H diff --git a/src/gui/PortPropertiesWindow.cpp b/src/gui/PortPropertiesWindow.cpp new file mode 100644 index 00000000..ddab5715 --- /dev/null +++ b/src/gui/PortPropertiesWindow.cpp @@ -0,0 +1,156 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "interface/EngineInterface.hpp" +#include "client/NodeModel.hpp" +#include "client/PluginModel.hpp" +#include "App.hpp" +#include "Controls.hpp" +#include "PortPropertiesWindow.hpp" + +using namespace std; + +namespace Ingen { +namespace GUI { + + +PortPropertiesWindow::PortPropertiesWindow(BaseObjectType* cobject, const Glib::RefPtr& xml) + : Gtk::Window(cobject) + , _enable_signal(false) +{ + xml->get_widget("port_properties_min_spinner", _min_spinner); + xml->get_widget("port_properties_max_spinner", _max_spinner); + xml->get_widget("port_properties_cancel_button", _cancel_button); + xml->get_widget("port_properties_ok_button", _ok_button); + + _cancel_button->signal_clicked().connect(sigc::mem_fun(this, + &PortPropertiesWindow::cancel)); + + _ok_button->signal_clicked().connect(sigc::mem_fun(this, + &PortPropertiesWindow::ok)); +} + + +/** Set the port this window is associated with. + * This function MUST be called before using this object in any way. + */ +void +PortPropertiesWindow::present(SharedPtr pm) +{ + assert(pm); + + for (list::iterator i = _connections.begin(); i != _connections.end(); ++i) + (*i).disconnect(); + + _connections.clear(); + + _port_model = pm; + + set_title(pm->path() + " Properties"); + + float min = 0.0f, max = 1.0f; + boost::shared_ptr parent = PtrCast(_port_model->parent()); + if (parent) + parent->port_value_range(_port_model, min, max); + + _initial_min = min; + _initial_max = max; + + _min_spinner->set_value(min); + _connections.push_back(_min_spinner->signal_value_changed().connect( + sigc::mem_fun(*this, &PortPropertiesWindow::min_changed))); + + _max_spinner->set_value(max); + _connections.push_back(_max_spinner->signal_value_changed().connect( + sigc::mem_fun(*this, &PortPropertiesWindow::max_changed))); + + _connections.push_back(pm->signal_variable.connect( + sigc::mem_fun(this, &PortPropertiesWindow::variable_change))); + + _enable_signal = true; + + Gtk::Window::present(); +} + + +void +PortPropertiesWindow::variable_change(const string& key, const Atom& value) +{ + _enable_signal = false; + + if ( (key == "ingen:minimum") && value.type() == Atom::FLOAT) + _min_spinner->set_value(value.get_float()); + else if ( (key == "ingen:maximum") && value.type() == Atom::FLOAT) + _max_spinner->set_value(value.get_float()); + + _enable_signal = true; +} + + +void +PortPropertiesWindow::min_changed() +{ + const float min = _min_spinner->get_value(); + float max = _max_spinner->get_value(); + + if (max <= min) { + max = min + 1.0; + _max_spinner->set_value(max); + } + + if (_enable_signal) + App::instance().engine()->set_variable(_port_model->path(), "ingen:minimum", min); +} + + +void +PortPropertiesWindow::max_changed() +{ + float min = _min_spinner->get_value(); + const float max = _max_spinner->get_value(); + + if (min >= max) { + min = max - 1.0; + _min_spinner->set_value(min); + } + + if (_enable_signal) + App::instance().engine()->set_variable(_port_model->path(), "ingen:maximum", max); +} + + +void +PortPropertiesWindow::cancel() +{ + App::instance().engine()->set_variable(_port_model->path(), "ingen:minimum", _initial_min); + App::instance().engine()->set_variable(_port_model->path(), "ingen:maximum", _initial_max); + hide(); +} + + +void +PortPropertiesWindow::ok() +{ + hide(); +} + + +} // namespace GUI +} // namespace Ingen + diff --git a/src/gui/PortPropertiesWindow.hpp b/src/gui/PortPropertiesWindow.hpp new file mode 100644 index 00000000..4580c7c6 --- /dev/null +++ b/src/gui/PortPropertiesWindow.hpp @@ -0,0 +1,68 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PORTPROPERTIESWINDOW_H +#define PORTPROPERTIESWINDOW_H + +#include +#include +#include +#include "client/PortModel.hpp" +using namespace Ingen::Client; + +namespace Ingen { +namespace GUI { + + +/** Port properties window. + * + * Loaded by libglade as a derived object. + * + * \ingroup GUI + */ +class PortPropertiesWindow : public Gtk::Window +{ +public: + PortPropertiesWindow(BaseObjectType* cobject, const Glib::RefPtr& refGlade); + + void present(SharedPtr port_model); + +private: + void variable_change(const string& key, const Atom& value); + void min_changed(); + void max_changed(); + + void ok(); + void cancel(); + + bool _enable_signal; + + float _initial_min; + float _initial_max; + + SharedPtr _port_model; + Gtk::SpinButton* _min_spinner; + Gtk::SpinButton* _max_spinner; + Gtk::Button* _cancel_button; + Gtk::Button* _ok_button; + std::list _connections; +}; + +} // namespace GUI +} // namespace Ingen + +#endif // PORTPROPERTIESWINDOW_H diff --git a/src/gui/RenameWindow.cpp b/src/gui/RenameWindow.cpp new file mode 100644 index 00000000..b9c252d0 --- /dev/null +++ b/src/gui/RenameWindow.cpp @@ -0,0 +1,119 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "interface/EngineInterface.hpp" +#include "client/ObjectModel.hpp" +#include "client/ClientStore.hpp" +#include "App.hpp" +#include "RenameWindow.hpp" + +using std::string; + +namespace Ingen { +namespace GUI { + + +RenameWindow::RenameWindow(BaseObjectType* cobject, const Glib::RefPtr& glade_xml) +: Gtk::Window(cobject) +{ + glade_xml->get_widget("rename_name_entry", _name_entry); + glade_xml->get_widget("rename_message_label", _message_label); + glade_xml->get_widget("rename_cancel_button", _cancel_button); + glade_xml->get_widget("rename_ok_button", _ok_button); + + _name_entry->signal_changed().connect(sigc::mem_fun(this, &RenameWindow::name_changed)); + _cancel_button->signal_clicked().connect(sigc::mem_fun(this, &RenameWindow::cancel_clicked)); + _ok_button->signal_clicked().connect(sigc::mem_fun(this, &RenameWindow::ok_clicked)); + + _ok_button->property_sensitive() = false; +} + + +/** Set the object this window is renaming. + * This function MUST be called before using this object in any way. + */ +void +RenameWindow::set_object(SharedPtr object) +{ + _object = object; + _name_entry->set_text(object->path().name()); +} + + +/** Called every time the user types into the name input box. + * Used to display warning messages, and enable/disable the rename button. + */ +void +RenameWindow::name_changed() +{ + assert(_name_entry); + assert(_message_label); + assert(_object); + assert(_object->parent()); + + string name = _name_entry->get_text(); + if (name.find("/") != string::npos) { + _message_label->set_text("Name may not contain '/'"); + _ok_button->property_sensitive() = false; + } else if (!Path::is_valid_name(name)) { + _message_label->set_text("Name contains invalid characters"); + _ok_button->property_sensitive() = false; + } else if ((App::instance().store()->object(_object->parent()->path().base() + name)) && + (name != _object->path().name())) { + _message_label->set_text("An object already exists with that name."); + _ok_button->property_sensitive() = false; + } else if (name.length() == 0) { + _message_label->set_text(""); + _ok_button->property_sensitive() = false; + } else { + _message_label->set_text(""); + _ok_button->property_sensitive() = true; + } +} + + +void +RenameWindow::cancel_clicked() +{ + _name_entry->set_text(""); + hide(); +} + + +/** Rename the object. + * + * It shouldn't be possible for this to be called with an invalid name set + * (since the Rename button should be deactivated). This is just shinification + * though - the engine will handle invalid names gracefully. + */ +void +RenameWindow::ok_clicked() +{ + string name = _name_entry->get_text(); + assert(name.length() > 0); + assert(name.find("/") == string::npos); + + App::instance().engine()->rename(_object->path(), name); + + hide(); +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/RenameWindow.hpp b/src/gui/RenameWindow.hpp new file mode 100644 index 00000000..3cf37f7c --- /dev/null +++ b/src/gui/RenameWindow.hpp @@ -0,0 +1,60 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef RENAMEWINDOW_H +#define RENAMEWINDOW_H + +#include +#include +#include +#include "client/ObjectModel.hpp" +using Ingen::Client::ObjectModel; + +namespace Ingen { +namespace GUI { + + +/** Rename window. Handles renaming of any (Ingen) object. + * + * \ingroup GUI + */ +class RenameWindow : public Gtk::Window +{ +public: + RenameWindow(BaseObjectType* cobject, const Glib::RefPtr& refGlade); + + void present(SharedPtr object) { set_object(object); Gtk::Window::present(); } + +private: + void set_object(SharedPtr object); + + void name_changed(); + void cancel_clicked(); + void ok_clicked(); + + SharedPtr _object; + + Gtk::Entry* _name_entry; + Gtk::Label* _message_label; + Gtk::Button* _cancel_button; + Gtk::Button* _ok_button; +}; + +} // namespace GUI +} // namespace Ingen + +#endif // RENAMEWINDOW_H diff --git a/src/gui/SubpatchModule.cpp b/src/gui/SubpatchModule.cpp new file mode 100644 index 00000000..b2a4d43f --- /dev/null +++ b/src/gui/SubpatchModule.cpp @@ -0,0 +1,95 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "SubpatchModule.hpp" +#include +#include +#include "interface/EngineInterface.hpp" +#include "client/PatchModel.hpp" +#include "App.hpp" +#include "NodeModule.hpp" +#include "NodeControlWindow.hpp" +#include "PatchWindow.hpp" +#include "PatchCanvas.hpp" +#include "Port.hpp" +#include "WindowFactory.hpp" +using std::cerr; using std::cout; using std::endl; + +namespace Ingen { +namespace GUI { + + +SubpatchModule::SubpatchModule(boost::shared_ptr canvas, SharedPtr patch) +: NodeModule(canvas, patch), + _patch(patch) +{ + assert(canvas); + assert(patch); +} + + +void +SubpatchModule::on_double_click(GdkEventButton* event) +{ + assert(_patch); + + SharedPtr parent = PtrCast(_patch->parent()); + + PatchWindow* const preferred = ( (parent && (event->state & GDK_SHIFT_MASK)) + ? NULL + : App::instance().window_factory()->patch_window(parent) ); + + App::instance().window_factory()->present_patch(_patch, preferred); +} + + + +/** Browse to this patch in current (parent's) window + * (unless an existing window is displaying it) + */ +void +SubpatchModule::browse_to_patch() +{ + assert(_patch->parent()); + + SharedPtr parent = PtrCast(_patch->parent()); + + PatchWindow* const preferred = ( (parent) + ? App::instance().window_factory()->patch_window(parent) + : NULL ); + + App::instance().window_factory()->present_patch(_patch, preferred); +} + + + +void +SubpatchModule::show_dialog() +{ + cerr << "FIXME: dialog\n"; + //m_patch->show_control_window(); +} + + +void +SubpatchModule::menu_remove() +{ + App::instance().engine()->destroy(_patch->path()); +} + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/SubpatchModule.hpp b/src/gui/SubpatchModule.hpp new file mode 100644 index 00000000..44e37e62 --- /dev/null +++ b/src/gui/SubpatchModule.hpp @@ -0,0 +1,71 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#ifndef SUBPATCHMODULE_H +#define SUBPATCHMODULE_H + +#include +#include +#include +#include "client/PatchModel.hpp" +#include "PatchPortModule.hpp" +#include "NodeModule.hpp" +using std::string; using std::list; + +namespace Ingen { namespace Client { + class PatchModel; + class NodeModel; + class PortModel; + class PatchWindow; +} } +using namespace Ingen::Client; + +namespace Ingen { +namespace GUI { + +class PatchCanvas; +class NodeControlWindow; + + +/** A module to represent a subpatch + * + * \ingroup GUI + */ +class SubpatchModule : public NodeModule +{ +public: + SubpatchModule(boost::shared_ptr canvas, SharedPtr controller); + virtual ~SubpatchModule() {} + + void on_double_click(GdkEventButton* ev); + + void show_dialog(); + void browse_to_patch(); + void menu_remove(); + + SharedPtr patch() { return _patch; } + +protected: + SharedPtr _patch; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // SUBPATCHMODULE_H diff --git a/src/gui/ThreadedLoader.cpp b/src/gui/ThreadedLoader.cpp new file mode 100644 index 00000000..008b14c4 --- /dev/null +++ b/src/gui/ThreadedLoader.cpp @@ -0,0 +1,155 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "module/global.hpp" +#include "module/World.hpp" +#include "module/Module.hpp" +#include "client/PatchModel.hpp" +#include "App.hpp" +#include "ThreadedLoader.hpp" + +using namespace std; + +namespace Ingen { +namespace GUI { + + +ThreadedLoader::ThreadedLoader(SharedPtr engine) + : _engine(engine) + , _deprecated_loader(engine) +{ + set_name("Loader"); + + if (parser()) + start(); + else + cerr << "WARNING: Failed to load ingen_serialisation module, load disabled." << endl; +} + + +SharedPtr +ThreadedLoader::parser() +{ + if (_parser) + return _parser; + + World* world = App::instance().world(); + if (!world->serialisation_module) + world->serialisation_module = Ingen::Shared::load_module("ingen_serialisation"); + + if (world->serialisation_module) { + Parser* (*new_parser)() = NULL; + + bool found = App::instance().world()->serialisation_module->get_symbol( + "new_parser", (void*&)new_parser); + + if (found) + _parser = SharedPtr(new_parser()); + } + + return _parser; +} + + +void +ThreadedLoader::_whipped() +{ + _mutex.lock(); + + while ( ! _events.empty() ) { + _events.front()(); + _events.pop_front(); + } + + _mutex.unlock(); +} + +void +ThreadedLoader::load_patch(bool merge, + const Glib::ustring& data_base_uri, + const Path& data_path, + GraphObject::Variables engine_data, + optional engine_parent, + optional engine_symbol) +{ + _mutex.lock(); + + // FIXME: Filthy hack to load deprecated patches based on file extension + if (data_base_uri.substr(data_base_uri.length()-3) == ".om") { + _events.push_back(sigc::hide_return(sigc::bind( + sigc::mem_fun(_deprecated_loader, &DeprecatedLoader::load_patch), + data_base_uri, + engine_parent, + (engine_symbol) ? engine_symbol.get() : "", + engine_data, + false))); + } else { + Glib::ustring engine_base = ""; + if (engine_parent) { + if (merge) + engine_base = engine_parent.get(); + else + engine_base = engine_parent.get().base(); + } + + if (merge && (!engine_parent || engine_parent.get() == "/")) + engine_base = engine_base.substr(0, engine_base.find_last_of("/")); + + _events.push_back(sigc::hide_return(sigc::bind( + sigc::mem_fun(_parser.get(), &Ingen::Serialisation::Parser::parse_document), + App::instance().world(), + App::instance().world()->engine.get(), + data_base_uri, // document + data_base_uri + data_path.substr(1), // object URI document + engine_base, + engine_symbol, + engine_data))); + } + + whip(); + + _mutex.unlock(); +} + + +void +ThreadedLoader::save_patch(SharedPtr model, const string& filename) +{ + _mutex.lock(); + + _events.push_back(sigc::hide_return(sigc::bind( + sigc::mem_fun(this, &ThreadedLoader::save_patch_event), + model, filename))); + + _mutex.unlock(); + + whip(); +} + + +void +ThreadedLoader::save_patch_event(SharedPtr model, const string& filename) +{ + if (App::instance().serialiser()) + App::instance().serialiser()->to_file(model, filename); +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/ThreadedLoader.hpp b/src/gui/ThreadedLoader.hpp new file mode 100644 index 00000000..3cf88cf9 --- /dev/null +++ b/src/gui/ThreadedLoader.hpp @@ -0,0 +1,92 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef THREADEDLOADER_H +#define THREADEDLOADER_H + +#include +#include +#include +#include +#include +#include +#include +#include "interface/EngineInterface.hpp" +#include "client/PatchModel.hpp" +#include "client/DeprecatedLoader.hpp" +#include "serialisation/Serialiser.hpp" +#include "serialisation/Parser.hpp" +using std::string; +using std::list; +using boost::optional; + +using namespace Ingen::Client; +using namespace Ingen::Serialisation; + +namespace Ingen { +namespace GUI { + + +/** Thread for loading patch files. + * + * This is a seperate thread so it can send all the loading message without + * blocking everything else, so the app can respond to the incoming events + * caused as a result of the patch loading, while the patch loads. + * + * Implemented as a slave with a list of closures (events) which processes + * all events in the (mutex protected) list each time it's whipped. + * + * \ingroup GUI + */ +class ThreadedLoader : public Raul::Slave +{ +public: + ThreadedLoader(SharedPtr engine); + + void load_patch(bool merge, + const Glib::ustring& data_base_uri, + const Path& data_path, + GraphObject::Variables engine_data, + optional engine_parent = optional(), + optional engine_symbol = optional()); + + void save_patch(SharedPtr model, const string& filename); + + SharedPtr parser(); + +private: + + void save_patch_event(SharedPtr model, const string& filename); + + /** Returns nothing and takes no parameters (because they have all been bound) */ + typedef sigc::slot Closure; + + void _whipped(); + + SharedPtr _engine; + SharedPtr _parser; + + DeprecatedLoader _deprecated_loader; + Glib::Mutex _mutex; + list _events; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // LOADERRTHREAD_H diff --git a/src/gui/UploadPatchWindow.cpp b/src/gui/UploadPatchWindow.cpp new file mode 100644 index 00000000..2ca54f3c --- /dev/null +++ b/src/gui/UploadPatchWindow.cpp @@ -0,0 +1,282 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include "module/global.hpp" +#include "module/World.hpp" +#include "client/ClientStore.hpp" +#include "interface/EngineInterface.hpp" +#include "serialisation/Serialiser.hpp" +#include "client/PatchModel.hpp" +#include "UploadPatchWindow.hpp" +#include "App.hpp" +#include "Configuration.hpp" +#include "ThreadedLoader.hpp" + +using boost::optional; +using namespace Raul; +using namespace std; + +namespace Ingen { +namespace GUI { + + +UploadPatchWindow::UploadPatchWindow(BaseObjectType* cobject, const Glib::RefPtr& xml) + : Gtk::Dialog(cobject) + , _thread(NULL) + , _progress_pct(0) + , _response(0) +{ + xml->get_widget("upload_patch_symbol_entry", _symbol_entry); + xml->get_widget("upload_patch_short_name_entry", _short_name_entry); + xml->get_widget("upload_patch_progress", _upload_progress); + xml->get_widget("upload_patch_cancel_button", _cancel_button); + xml->get_widget("upload_patch_upload_button", _upload_button); + + + _symbol_entry->signal_changed().connect(sigc::mem_fun(this, &UploadPatchWindow::symbol_changed)); + _short_name_entry->signal_changed().connect(sigc::mem_fun(this, &UploadPatchWindow::short_name_changed)); + _cancel_button->signal_clicked().connect(sigc::mem_fun(this, &UploadPatchWindow::cancel_clicked)); + _upload_button->signal_clicked().connect(sigc::mem_fun(this, &UploadPatchWindow::upload_clicked)); +} + + +void +UploadPatchWindow::present(SharedPtr patch) +{ + _patch = patch; + + Gtk::Window::present(); +} + + +void +UploadPatchWindow::on_show() +{ + Gtk::Dialog::on_show(); + + Raul::Atom atom = _patch->get_variable("lv2:symbol"); + if (atom.is_valid()) + _symbol_entry->set_text(atom.get_string()); + + atom = _patch->get_variable("doap:name"); + if (atom.is_valid()) + _short_name_entry->set_text(atom.get_string()); +} + + +void +UploadPatchWindow::on_hide() +{ + Gtk::Dialog::on_hide(); + + delete _thread; + _thread = NULL; +} + + +bool +UploadPatchWindow::is_symbol(const Glib::ustring& s) +{ + if (s.length() == 0) + return false; + + for (unsigned i=0; i < s.length(); ++i) + if ( !( (s[i] >= 'a' && s[i] <= 'z') + || (s[i] >= 'A' && s[i] <= 'Z') + || (s[i] == '_') + || (i > 0 && s[i] >= '0' && s[i] <= '9') ) ) + return false; + + return true; +} + + +void +UploadPatchWindow::symbol_changed() +{ + _upload_button->property_sensitive() = ( + is_symbol(_symbol_entry->get_text()) + && _short_name_entry->get_text().length() > 0); +} + + +void +UploadPatchWindow::short_name_changed() +{ + _upload_button->property_sensitive() = ( + is_symbol(_symbol_entry->get_text()) + && _short_name_entry->get_text().length() > 0); +} + + +size_t +UploadThread::curl_read_cb(void *ptr, size_t size, size_t nmemb, void *data) +{ + assert(size == 1); + + istringstream* ss = (istringstream*)data; + + return ss->readsome((char*)ptr, nmemb); +} + + +int +UploadThread::curl_progress_cb(void *thread, + double dltotal, + double dlnow, + double ultotal, + double ulnow) +{ + UploadThread* me = (UploadThread*)thread; + me->_win->set_progress(min( + (int)(min(ulnow, (double)me->_length) / me->_length * 100.0), + 99)); + return 0; +} + + +UploadThread::UploadThread(UploadPatchWindow* win, const string& str, const string& url) + : Thread("Upload") + , _curl(NULL) + , _headers(NULL) + , _win(win) + , _length(str.length()) + , _stream(str) + , _url(url) +{ + _curl = curl_easy_init(); + _headers = curl_slist_append(NULL, "Content-type: application/x-turtle"); + + curl_easy_setopt(_curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(_curl, CURLOPT_HTTPHEADER, _headers); + curl_easy_setopt(_curl, CURLOPT_UPLOAD, 1); + curl_easy_setopt(_curl, CURLOPT_READDATA, &_stream); + curl_easy_setopt(_curl, CURLOPT_READFUNCTION, &UploadThread::curl_read_cb); + curl_easy_setopt(_curl, CURLOPT_INFILESIZE, sizeof(char) * str.length()); + curl_easy_setopt(_curl, CURLOPT_NOPROGRESS, FALSE); + curl_easy_setopt(_curl, CURLOPT_PROGRESSFUNCTION, &UploadThread::curl_progress_cb); + curl_easy_setopt(_curl, CURLOPT_PROGRESSDATA, this); +} + + +void +UploadThread::_run() +{ + curl_easy_perform(_curl); + + long response; + curl_easy_getinfo(_curl, CURLINFO_RESPONSE_CODE, &response); + + printf("Server returned %ld\n", response); + + _win->set_response(response); + _win->set_progress(100); + + curl_slist_free_all(_headers); + curl_easy_cleanup(_curl); + + _headers = NULL; + _curl = NULL; +} + + +bool +UploadPatchWindow::progress_callback() +{ + const int progress = _progress_pct.get(); + const int response = _response.get(); + + _upload_progress->set_fraction(progress / 100.0); + + if (progress == 100) { + if (response == 200) { + _upload_progress->set_text("Transfer completed"); + } else { + _upload_progress->set_fraction(0.0); + char status[4]; + snprintf(status, 4, "%d", (unsigned)response); + string msg = "Transfer failed: Server returned "; + msg.append(status); + _upload_progress->set_text(msg); + } + delete _thread; + _thread = NULL; + _upload_button->set_sensitive(true); + return false; + } else { + return true; + } +} + + +void +UploadPatchWindow::upload_clicked() +{ + assert(!_thread); + + Glib::ustring symbol = _symbol_entry->get_text(); + Glib::ustring short_name = _short_name_entry->get_text(); + + GraphObject::Variables extra_rdf; + extra_rdf["lv2:symbol"] = Atom(symbol); + extra_rdf["doap:name"] = Atom(short_name); + + _response = 0; + _progress_pct = 0; + + _upload_progress->set_fraction(0.0); + _upload_progress->set_text(""); + + Serialiser s(*App::instance().world(), App::instance().store()); + + const string uri = string("http://rdf.drobilla.net/ingen_patches/") + .append(symbol).append(".ingen.ttl"); + + const string str = s.to_string(_patch, uri, extra_rdf); + + _thread = new UploadThread(this, str, uri); + + _thread->start(); + + _upload_button->set_sensitive(false); + + Glib::signal_timeout().connect( + sigc::mem_fun(this, &UploadPatchWindow::progress_callback), 100); +} + + +void +UploadPatchWindow::cancel_clicked() +{ + if (_thread) { + delete _thread; + _thread = NULL; + } + + hide(); +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/UploadPatchWindow.hpp b/src/gui/UploadPatchWindow.hpp new file mode 100644 index 00000000..af064bdb --- /dev/null +++ b/src/gui/UploadPatchWindow.hpp @@ -0,0 +1,104 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef UPLOADPATCHWINDOW_H +#define UPLOADPATCHWINDOW_H + +#include +#include +#include +#include +#include +#include +#include +#include "client/PatchModel.hpp" +#include "client/PluginModel.hpp" +using Ingen::Client::PatchModel; + +namespace Ingen { +namespace GUI { + +class UploadPatchWindow; + + +class UploadThread : public Raul::Thread { +public: + UploadThread(UploadPatchWindow* win, + const string& str, + const string& url); + +private: + static size_t curl_read_cb(void* ptr, size_t size, size_t nmemb, void *stream); + static int curl_progress_cb(void* thread, double dltotal, double dlnow, double ultotal, double ulnow); + + void _run(); + + CURL* _curl; + curl_slist* _headers; + UploadPatchWindow* _win; + size_t _length; + std::istringstream _stream; + std::string _url; +}; + + +/* Upload patch dialog. + * + * \ingroup GUI + */ +class UploadPatchWindow : public Gtk::Dialog +{ +public: + UploadPatchWindow(BaseObjectType* cobject, const Glib::RefPtr& xml); + + void present(SharedPtr patch); + + Gtk::ProgressBar* progress_bar() { return _upload_progress; } + + void set_response(int response) { _response = response; } + void set_progress(int pct) { _progress_pct = pct; } + +private: + bool is_symbol(const Glib::ustring& str); + void symbol_changed(); + void short_name_changed(); + void cancel_clicked(); + void upload_clicked(); + void on_show(); + void on_hide(); + bool progress_callback(); + + UploadThread* _thread; + + SharedPtr _patch; + + Raul::AtomicInt _progress_pct; + Raul::AtomicInt _response; + + Gtk::Entry* _symbol_entry; + Gtk::Entry* _short_name_entry; + Gtk::ProgressBar* _upload_progress; + Gtk::Button* _cancel_button; + Gtk::Button* _upload_button; + +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // UPLOADPATCHWINDOW_H diff --git a/src/gui/WindowFactory.cpp b/src/gui/WindowFactory.cpp new file mode 100644 index 00000000..125a242b --- /dev/null +++ b/src/gui/WindowFactory.cpp @@ -0,0 +1,381 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include CONFIG_H_PATH +#include "WindowFactory.hpp" +#include "App.hpp" +#include "PatchWindow.hpp" +#include "GladeFactory.hpp" +#include "PatchPropertiesWindow.hpp" +#include "NodePropertiesWindow.hpp" +#include "PortPropertiesWindow.hpp" +#include "NodeControlWindow.hpp" +#include "LoadPluginWindow.hpp" +#include "LoadPatchWindow.hpp" +#include "LoadRemotePatchWindow.hpp" +#include "LoadSubpatchWindow.hpp" +#include "RenameWindow.hpp" +#include "NewSubpatchWindow.hpp" +#ifdef HAVE_CURL +#include "UploadPatchWindow.hpp" +#endif + +namespace Ingen { +namespace GUI { + + +WindowFactory::WindowFactory() + : _load_plugin_win(NULL) + , _load_patch_win(NULL) + , _load_remote_patch_win(NULL) + , _upload_patch_win(NULL) + , _new_subpatch_win(NULL) + , _load_subpatch_win(NULL) + , _patch_properties_win(NULL) + , _node_properties_win(NULL) + , _port_properties_win(NULL) +{ + Glib::RefPtr xml = GladeFactory::new_glade_reference(); + + xml->get_widget_derived("load_plugin_win", _load_plugin_win); + xml->get_widget_derived("load_patch_win", _load_patch_win); + xml->get_widget_derived("load_remote_patch_win", _load_remote_patch_win); + xml->get_widget_derived("new_subpatch_win", _new_subpatch_win); + xml->get_widget_derived("load_subpatch_win", _load_subpatch_win); + xml->get_widget_derived("patch_properties_win", _patch_properties_win); + xml->get_widget_derived("node_properties_win", _node_properties_win); + xml->get_widget_derived("port_properties_win", _port_properties_win); + xml->get_widget_derived("rename_win", _rename_win); + +#ifdef HAVE_CURL + xml->get_widget_derived("upload_patch_win", _upload_patch_win); +#endif +} + + +WindowFactory::~WindowFactory() +{ + for (PatchWindowMap::iterator i = _patch_windows.begin(); i != _patch_windows.end(); ++i) + delete i->second; + + for (ControlWindowMap::iterator i = _control_windows.begin(); i != _control_windows.end(); ++i) + delete i->second; + +} + + +void +WindowFactory::clear() +{ + for (PatchWindowMap::iterator i = _patch_windows.begin(); i != _patch_windows.end(); ++i) + delete i->second; + + _patch_windows.clear(); + + for (ControlWindowMap::iterator i = _control_windows.begin(); i != _control_windows.end(); ++i) + delete i->second; + + _control_windows.clear(); +} + + +/** Returns the number of Patch windows currently visible. + */ +size_t +WindowFactory::num_open_patch_windows() +{ + size_t ret = 0; + for (PatchWindowMap::iterator i = _patch_windows.begin(); i != _patch_windows.end(); ++i) + if (i->second->is_visible()) + ++ret; + + return ret; +} + + + +PatchWindow* +WindowFactory::patch_window(SharedPtr patch) +{ + PatchWindowMap::iterator w = _patch_windows.find(patch->path()); + + return (w == _patch_windows.end()) ? NULL : w->second; +} + + +NodeControlWindow* +WindowFactory::control_window(SharedPtr node) +{ + ControlWindowMap::iterator w = _control_windows.find(node->path()); + + return (w == _control_windows.end()) ? NULL : w->second; +} + + +/** Present a PatchWindow for a Patch. + * + * If @a preferred is not NULL, it will be set to display @a patch if the patch + * does not already have a visible window, otherwise that window will be presented and + * @a preferred left unmodified. + */ +void +WindowFactory::present_patch(SharedPtr patch, PatchWindow* preferred, SharedPtr view) +{ + assert( !view || view->patch() == patch); + + PatchWindowMap::iterator w = _patch_windows.find(patch->path()); + + if (w != _patch_windows.end()) { + (*w).second->present(); + } else if (preferred) { + w = _patch_windows.find(preferred->patch()->path()); + assert((*w).second == preferred); + + preferred->set_patch(patch, view); + _patch_windows.erase(w); + _patch_windows[patch->path()] = preferred; + preferred->present(); + + } else { + PatchWindow* win = new_patch_window(patch, view); + win->present(); + } +} + + +PatchWindow* +WindowFactory::new_patch_window(SharedPtr patch, SharedPtr view) +{ + assert( !view || view->patch() == patch); + + Glib::RefPtr xml = GladeFactory::new_glade_reference("patch_win"); + + PatchWindow* win = NULL; + xml->get_widget_derived("patch_win", win); + assert(win); + + win->set_patch(patch, view); + _patch_windows[patch->path()] = win; + + win->signal_delete_event().connect(sigc::bind<0>( + sigc::mem_fun(this, &WindowFactory::remove_patch_window), win)); + + return win; +} + + +bool +WindowFactory::remove_patch_window(PatchWindow* win, GdkEventAny* ignored) +{ + if (_patch_windows.size() <= 1) { + Gtk::MessageDialog d(*win, "This is the last remaining open patch " + "window. Closing this window will exit the GUI (the engine will " + "remain running).\n\nAre you sure you want to quit?", + true, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_NONE, true); + d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); + d.add_button(Gtk::Stock::QUIT, Gtk::RESPONSE_CLOSE); + int ret = d.run(); + if (ret == Gtk::RESPONSE_CLOSE) + App::instance().quit(); + else + return true; + } + + PatchWindowMap::iterator w = _patch_windows.find(win->patch()->path()); + + assert((*w).second == win); + _patch_windows.erase(w); + + delete win; + + return false; +} + + +void +WindowFactory::present_controls(SharedPtr node) +{ + NodeControlWindow* win = control_window(node); + + if (win) { + win->present(); + } else { + win = new_control_window(node); + win->present(); + } +} + + +NodeControlWindow* +WindowFactory::new_control_window(SharedPtr node) +{ + uint32_t poly = 1; + if (node->polyphonic() && node->parent()) + poly = ((PatchModel*)node->parent().get())->internal_polyphony(); + + NodeControlWindow* win = new NodeControlWindow(node, poly); + + _control_windows[node->path()] = win; + + win->signal_delete_event().connect(sigc::bind<0>( + sigc::mem_fun(this, &WindowFactory::remove_control_window), win)); + + return win; +} + + +bool +WindowFactory::remove_control_window(NodeControlWindow* win, GdkEventAny* ignored) +{ + ControlWindowMap::iterator w = _control_windows.find(win->node()->path()); + + assert((*w).second == win); + _control_windows.erase(w); + + delete win; + + return true; +} + + +void +WindowFactory::present_load_plugin(SharedPtr patch, GraphObject::Variables data) +{ + PatchWindowMap::iterator w = _patch_windows.find(patch->path()); + + if (w != _patch_windows.end()) + _load_plugin_win->set_transient_for(*w->second); + + _load_plugin_win->set_modal(false); + _load_plugin_win->present(patch, data); +} + + +void +WindowFactory::present_load_patch(SharedPtr patch, GraphObject::Variables data) +{ + PatchWindowMap::iterator w = _patch_windows.find(patch->path()); + + if (w != _patch_windows.end()) + _load_patch_win->set_transient_for(*w->second); + + _load_patch_win->set_merge(); // Import is the only choice + + _load_patch_win->present(patch, data); +} + + +void +WindowFactory::present_load_remote_patch(SharedPtr patch, GraphObject::Variables data) +{ + PatchWindowMap::iterator w = _patch_windows.find(patch->path()); + + if (w != _patch_windows.end()) + _load_remote_patch_win->set_transient_for(*w->second); + + _load_remote_patch_win->set_merge(); // Import is the only choice + + _load_remote_patch_win->present(patch, data); +} + + +void +WindowFactory::present_upload_patch(SharedPtr patch) +{ +#ifdef HAVE_CURL + PatchWindowMap::iterator w = _patch_windows.find(patch->path()); + + if (w != _patch_windows.end()) + _upload_patch_win->set_transient_for(*w->second); + + _upload_patch_win->present(patch); +#endif +} + +void +WindowFactory::present_new_subpatch(SharedPtr patch, GraphObject::Variables data) +{ + PatchWindowMap::iterator w = _patch_windows.find(patch->path()); + + if (w != _patch_windows.end()) + _new_subpatch_win->set_transient_for(*w->second); + + _new_subpatch_win->present(patch, data); +} + + +void +WindowFactory::present_load_subpatch(SharedPtr patch, GraphObject::Variables data) +{ + PatchWindowMap::iterator w = _patch_windows.find(patch->path()); + + if (w != _patch_windows.end()) + _load_subpatch_win->set_transient_for(*w->second); + + _load_subpatch_win->present(patch, data); +} + + +void +WindowFactory::present_rename(SharedPtr object) +{ + PatchWindowMap::iterator w = _patch_windows.find(object->path()); + + if (w != _patch_windows.end()) + _rename_win->set_transient_for(*w->second); + + _rename_win->present(object); +} + + +void +WindowFactory::present_properties(SharedPtr object) +{ + SharedPtr patch = PtrCast(object); + if (patch) { + PatchWindowMap::iterator w = _patch_windows.find(patch->path()); + if (w != _patch_windows.end()) + _patch_properties_win->set_transient_for(*w->second); + + _patch_properties_win->present(patch); + return; + } + + SharedPtr node = PtrCast(object); + if (node) { + PatchWindowMap::iterator w = _patch_windows.find(node->path().parent()); + if (w != _patch_windows.end()) + _node_properties_win->set_transient_for(*w->second); + + _node_properties_win->present(node); + return; + } + + SharedPtr port = PtrCast(object); + if (port) { + PatchWindowMap::iterator w = _patch_windows.find(port->path().parent().parent()); + if (w != _patch_windows.end()) + _patch_properties_win->set_transient_for(*w->second); + + _port_properties_win->present(port); + return; + } +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/gui/WindowFactory.hpp b/src/gui/WindowFactory.hpp new file mode 100644 index 00000000..618b027c --- /dev/null +++ b/src/gui/WindowFactory.hpp @@ -0,0 +1,109 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef WINDOW_FACTORY_H +#define WINDOW_FACTORY_H + +#include +#include +#include +#include "interface/GraphObject.hpp" +#include "client/PatchModel.hpp" +#include "PatchView.hpp" + +using Ingen::Client::PatchModel; +using namespace Ingen::Shared; + +namespace Ingen { +namespace GUI { + +class PatchWindow; +class NodeControlWindow; +class PatchPropertiesWindow; +class NodePropertiesWindow; +class PortPropertiesWindow; +class LoadPatchWindow; +class LoadRemotePatchWindow; +class UploadPatchWindow; +class RenameWindow; + + +/** Manager/Factory for all windows. + * + * This serves as a nice centralized spot for all window management issues, + * as well as an enumeration of all windows (the goal being to reduce that + * number as much as possible). + */ +class WindowFactory { +public: + WindowFactory(); + ~WindowFactory(); + + size_t num_open_patch_windows(); + + PatchWindow* patch_window(SharedPtr patch); + NodeControlWindow* control_window(SharedPtr node); + + void present_patch(SharedPtr model, + PatchWindow* preferred = NULL, + SharedPtr view = SharedPtr()); + + void present_controls(SharedPtr node); + + void present_load_plugin(SharedPtr patch, GraphObject::Variables data=GraphObject::Variables()); + void present_load_patch(SharedPtr patch, GraphObject::Variables data=GraphObject::Variables()); + void present_load_remote_patch(SharedPtr patch, GraphObject::Variables data=GraphObject::Variables()); + void present_upload_patch(SharedPtr patch); + void present_new_subpatch(SharedPtr patch, GraphObject::Variables data=GraphObject::Variables()); + void present_load_subpatch(SharedPtr patch, GraphObject::Variables data=GraphObject::Variables()); + void present_rename(SharedPtr object); + void present_properties(SharedPtr object); + + bool remove_patch_window(PatchWindow* win, GdkEventAny* ignored = NULL); + + void clear(); + +private: + typedef std::map PatchWindowMap; + typedef std::map ControlWindowMap; + + PatchWindow* new_patch_window(SharedPtr patch, SharedPtr view); + + + NodeControlWindow* new_control_window(SharedPtr node); + bool remove_control_window(NodeControlWindow* win, GdkEventAny* ignored); + + PatchWindowMap _patch_windows; + ControlWindowMap _control_windows; + + LoadPluginWindow* _load_plugin_win; + LoadPatchWindow* _load_patch_win; + LoadRemotePatchWindow* _load_remote_patch_win; + UploadPatchWindow* _upload_patch_win; + NewSubpatchWindow* _new_subpatch_win; + LoadSubpatchWindow* _load_subpatch_win; + PatchPropertiesWindow* _patch_properties_win; + NodePropertiesWindow* _node_properties_win; + PortPropertiesWindow* _port_properties_win; + RenameWindow* _rename_win; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // WINDOW_FACTORY_H diff --git a/src/gui/cmdline.h b/src/gui/cmdline.h new file mode 100644 index 00000000..6f1f9259 --- /dev/null +++ b/src/gui/cmdline.h @@ -0,0 +1,86 @@ +/* cmdline.h */ + +/* File autogenerated by gengetopt version 2.19.1 */ + +#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 "ingen" +#endif + +#ifndef CMDLINE_PARSER_VERSION +#define CMDLINE_PARSER_VERSION VERSION +#endif + +struct gengetopt_args_info +{ + const char *help_help; /* Print help and exit help description. */ + const char *version_help; /* Print version and exit help description. */ + int engine_flag; /* Run (JACK) engine (default=off). */ + const char *engine_help; /* Run (JACK) engine help description. */ + int engine_port_arg; /* Engine OSC port (default='16180'). */ + char * engine_port_orig; /* Engine OSC port original value given at command line. */ + const char *engine_port_help; /* Engine OSC port help description. */ + char * connect_arg; /* Connect to existing engine at OSC URI (default='osc.udp://localhost:16180'). */ + char * connect_orig; /* Connect to existing engine at OSC URI original value given at command line. */ + const char *connect_help; /* Connect to existing engine at OSC URI help description. */ + int gui_flag; /* Launch the GTK graphical interface (default=on). */ + const char *gui_help; /* Launch the GTK graphical interface help description. */ + int client_port_arg; /* Client OSC port. */ + char * client_port_orig; /* Client OSC port original value given at command line. */ + const char *client_port_help; /* Client OSC port help description. */ + char * load_arg; /* Load patch. */ + char * load_orig; /* Load patch original value given at command line. */ + const char *load_help; /* Load patch help description. */ + char * path_arg; /* Target path for loaded patch. */ + char * path_orig; /* Target path for loaded patch original value given at command line. */ + const char *path_help; /* Target path for loaded patch help description. */ + + int help_given ; /* Whether help was given. */ + int version_given ; /* Whether version was given. */ + int engine_given ; /* Whether engine was given. */ + int engine_port_given ; /* Whether engine-port was given. */ + int connect_given ; /* Whether connect was given. */ + int gui_given ; /* Whether gui was given. */ + int client_port_given ; /* Whether client-port was given. */ + int load_given ; /* Whether load was given. */ + int path_given ; /* Whether path was given. */ + +} ; + +extern const char *gengetopt_args_info_purpose; +extern const char *gengetopt_args_info_usage; +extern const char *gengetopt_args_info_help[]; + +int cmdline_parser (int argc, char * const *argv, + struct gengetopt_args_info *args_info); +int cmdline_parser2 (int argc, char * const *argv, + struct gengetopt_args_info *args_info, + int override, int initialize, int check_required); +int cmdline_parser_file_save(const char *filename, + struct gengetopt_args_info *args_info); + +void cmdline_parser_print_help(void); +void cmdline_parser_print_version(void); + +void cmdline_parser_init (struct gengetopt_args_info *args_info); +void cmdline_parser_free (struct gengetopt_args_info *args_info); + +int cmdline_parser_required (struct gengetopt_args_info *args_info, + const char *prog_name); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* CMDLINE_H */ diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp new file mode 100644 index 00000000..10af6d9b --- /dev/null +++ b/src/gui/gui.cpp @@ -0,0 +1,35 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "gui.hpp" +#include "ConnectWindow.hpp" +#include "App.hpp" +#include "Configuration.hpp" + +namespace Ingen { +namespace GUI { + + +void run(int argc, char** argv, Ingen::Shared::World* world) +{ + App::run(argc, argv, world); +} + + +} // namespace GUI +} // namespace Ingen + diff --git a/src/gui/gui.hpp b/src/gui/gui.hpp new file mode 100644 index 00000000..a959f17a --- /dev/null +++ b/src/gui/gui.hpp @@ -0,0 +1,45 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef INGEN_GUI_H +#define INGEN_GUI_H + +#include CONFIG_H_PATH +#include "module/global.hpp" +#include + +namespace Ingen { + +class Engine; + +namespace Shared { class EngineInterface; } + +namespace GUI { + + +extern "C" { + + void run(int argc, char** argv, Ingen::Shared::World* world); + +} + + +} // namesace GUI +} // namespace Ingen + +#endif // INGEN_GUI_H + diff --git a/src/gui/ingen-icon.svg b/src/gui/ingen-icon.svg new file mode 100644 index 00000000..a15ed7e7 --- /dev/null +++ b/src/gui/ingen-icon.svg @@ -0,0 +1,54 @@ + + + + + + + image/svg+xml + + + + + + + + diff --git a/src/gui/ingen.svg b/src/gui/ingen.svg new file mode 100644 index 00000000..a15ed7e7 --- /dev/null +++ b/src/gui/ingen.svg @@ -0,0 +1,54 @@ + + + + + + + image/svg+xml + + + + + + + + diff --git a/src/gui/ingen_gui.glade b/src/gui/ingen_gui.glade new file mode 100644 index 00000000..aa0680ea --- /dev/null +++ b/src/gui/ingen_gui.glade @@ -0,0 +1,3337 @@ + + + + + + Ingen + 640 + 480 + ingen.svg + + + True + + + True + + + True + _File + True + + + + + True + Load a patch into the current patch (merge with existing contents). + _Import... + True + + + + + True + gtk-open + 1 + + + + + + + True + Import a patch from a URI + Import _Location... + True + + + + + True + gtk-open + 1 + + + + + + + True + + + + + True + Save this patch + gtk-save + True + True + + + + + + True + Save this patch to a specific file name + Save _As... + True + + + + + True + gtk-save-as + 1 + + + + + + + True + _Upload... + True + + + + + True + gtk-network + 1 + + + + + + + True + + + + + True + Close this window (patch will not be destroyed) + gtk-close + True + True + + + + + + True + + + + + True + Quit GUI (engine may continue running) + gtk-quit + True + True + + + + + + + + + + True + _Edit + True + + + + + True + False + gtk-cut + True + True + + + + + + True + gtk-copy + True + True + + + + + + True + False + gtk-paste + True + True + + + + + + True + Delete the selected object(s) + gtk-delete + True + True + + + + + + + True + Select all objects in a patch + gtk-select-all + True + True + + + + + + + True + + + + + True + Automatically arrange canvas + _Arrange + True + + + + True + gtk-sort-ascending + 1 + + + + + + + True + Remove all objects from patch + gtk-clear + True + True + + + + + + True + Destoy this patch (remove it from the engine) + _Destroy Patch + True + + + + True + gtk-delete + 1 + + + + + + + True + + + + + True + View/Edit controls for this patch + C_ontrols... + True + + + + + True + gtk-preferences + 1 + + + + + + + True + View/Edit properties for this patch + _Properties... + True + + + + + True + gtk-properties + 1 + + + + + + + + + + + True + _View + True + + + + + True + gtk-fullscreen + True + True + + + + + + + True + Show human readable names instead of identifiers + _Human names + True + True + + + + + + + + + + True + _Windows + True + + + + + + True + Connect to, Disconnect from, or Launch Engine + _Engine... + True + + + + + True + gtk-execute + 1 + + + + + + + True + View all patches in the engine as a heirarchial list + _Patch Tree... + True + + + + + True + gtk-index + 1 + + + + + + + True + View error messages from the engine + _Messages... + True + + + + + True + gtk-info + 1 + + + + + + + + + + + True + _Help + True + + + + + + True + Right-click the canvas to add objects + True + + + True + gtk-info + 1 + + + + + + + True + Press 'e' to toggle edit mode + True + + + True + gtk-info + 1 + + + + + + + True + + + + + True + gtk-about + True + True + + + + + + + + + + False + False + + + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + + + True + GTK_SHADOW_NONE + + + + + + + + 1 + + + + + + + 8 + Load Plugin + GTK_WIN_POS_CENTER_ON_PARENT + 640 + 480 + ingen.svg + + + True + 1 + + + True + True + 2 + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + + + True + True + 2 + True + True + + + + + + + True + 3 + 3 + 12 + + + True + True + Clear filter text (show all plugins) + gtk-clear + True + 0 + + + 2 + 3 + GTK_FILL + + + + + + True + Name contains: + + + GTK_FILL + + + + + + True + True + True + Search string to filter plugin list + * + + + 1 + 2 + 6 + + + + + True + False + True + Add selected plugin to patch + gtk-add + True + 0 + + + 2 + 3 + 2 + 3 + GTK_FILL + + + + + + True + + + True + True + Name of new Module + * + + + + + True + True + Polyphonic + True + 0 + True + True + + + False + False + 8 + 1 + + + + + 1 + 2 + 2 + 3 + GTK_FILL + GTK_FILL + 6 + + + + + True + + + 2 + 3 + 1 + 2 + GTK_FILL + GTK_FILL + + + + + True + + + 1 + 2 + GTK_FILL + GTK_FILL + + + + + True + + + 1 + 2 + 1 + 2 + GTK_FILL + + + + + True + 1 + Node Name: + True + + + 2 + 3 + GTK_FILL + + + + + + False + False + 1 + + + + + + + 320 + 8 + Create Subpatch + GTK_WIN_POS_CENTER_ON_PARENT + ingen.svg + GDK_WINDOW_TYPE_HINT_DIALOG + + + True + + + True + 2 + 2 + + + True + True + True + * + True + + + 1 + 2 + + 4 + + + + + True + True + 1 0 100 1 10 10 + 1 + + + 1 + 2 + 1 + 2 + GTK_FILL + + 4 + + + + + True + 0 + Polyphony: + + + 1 + 2 + GTK_FILL + GTK_EXPAND + 5 + + + + + True + 0 + Name: + + + GTK_FILL + GTK_EXPAND + 5 + + + + + + + True + True + + + False + False + 1 + + + + + True + 4 + GTK_BUTTONBOX_END + + + True + True + gtk-cancel + True + 0 + + + + + True + True + True + True + 0 + + + True + 0 + 0 + + + True + 2 + + + True + gtk-ok + + + False + False + + + + + True + Create + True + + + False + False + 1 + + + + + + + + + 1 + + + + + 2 + + + + + + + Load Subpatch + GTK_WIN_POS_CENTER_ON_PARENT + ingen.svg + GDK_WINDOW_TYPE_HINT_DIALOG + + + True + 24 + + + True + 2 + 4 + 12 + 4 + + + True + + + True + True + Specify the name for the new patch + Specify: + True + 0 + True + load_subpatch_name_from_file_radio + + + False + False + + + + + True + False + True + Specify the name for the new patch + * + True + + + False + 1 + + + + + 3 + 4 + GTK_FILL + + + + + True + 0 + + + 2 + 3 + GTK_FILL + + + + + + True + True + Set polyphony to the same value as the parent (containing) patch + Same as parent (?) + True + 0 + True + load_subpatch_poly_from_file_radio + + + 2 + 3 + 1 + 2 + GTK_FILL + + + + + + True + + + True + True + Specify a custom polyphony value for new patch + Specify: + True + 0 + True + load_subpatch_poly_from_file_radio + + + False + False + + + + + True + False + True + Specify a custom polyphony value for new patch + 1 0 1000 1 10 10 + 1 + + + False + 1 + + + + + 3 + 4 + 1 + 2 + GTK_FILL + GTK_FILL + + + + + True + True + Use the polyphony value stored in the patch file + Load from file + True + 0 + True + True + + + 1 + 2 + 1 + 2 + GTK_FILL + + + + + + True + True + Use the name stored in the patch file + Load from file + True + 0 + True + True + + + 1 + 2 + GTK_FILL + + + + + + True + 0 + <b>Polyphony: </b> + True + + + 1 + 2 + GTK_FILL + + + + + + True + 0 + <b>Name: </b> + True + + + GTK_FILL + + + + + + False + 2 + + + + + True + GTK_BUTTONBOX_END + + + True + True + True + gtk-cancel + True + -6 + + + + + True + True + True + True + gtk-open + True + -5 + + + 1 + + + + + False + GTK_PACK_END + + + + + + + Load Patch + GTK_WIN_POS_CENTER_ON_PARENT + ingen.svg + GDK_WINDOW_TYPE_HINT_DIALOG + + + 24 + + + True + 1 + 4 + 12 + 4 + + + True + + + True + True + Specify: + True + 0 + True + load_patch_poly_from_current_radio + + + False + False + + + + + True + False + True + Specify a custom polyphony value for new patch + 1 0 100 1 10 10 + 1 + + + False + 1 + + + + + 3 + 4 + GTK_FILL + GTK_FILL + + + + + True + True + Use the polyphony value stored in the patch file + Load from file + True + 0 + True + load_patch_poly_from_current_radio + + + 2 + 3 + GTK_FILL + + + + + + True + True + Use the same polyphony as the current patch + Keep current + True + 0 + True + True + + + 1 + 2 + GTK_FILL + + + + + + True + 0 + <b>Polyphony: </b> + True + + + GTK_FILL + + + + + + False + 2 + + + + + True + GTK_BUTTONBOX_END + + + True + True + True + gtk-cancel + True + -6 + + + + + True + True + True + True + gtk-open + True + -5 + + + 1 + + + + + False + GTK_PACK_END + + + + + + + window1 + + + True + 5 + 2 + + + + + + + + + + + + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + 0 + 1 + 4 + <b>Name</b> + True + True + + + False + False + + + + + True + 1 + 0 + 1 + 1 + 4 + + + True + + + + + 1 + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + 0 -9.9999999999999999e+45 1.0000000000000001e+63 1 10 10 + 4 + True + + + False + 2 + + + + + False + False + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 9.0923076923076922e+136 -1e+113 1e+137 0 0 0 + 63 + False + + + False + 1 + + + + + 3 + 4 + 8 + + + + + True + + + 1 + 2 + GTK_FILL + + + + + True + + + True + + + True + GTK_TOOLBAR_BOTH_HORIZ + + + True + + + True + + + True + GTK_SHADOW_NONE + + + + + + + + + + False + False + + + + + True + + + True + + + + + + False + False + + + + + + + True + False + + + True + + + False + False + + + + + True + Enable DSP processing + True + gtk-execute + True + + + False + + + + + True + + + True + 4 + gtk-copy + + + + + False + False + + + + + True + + + True + True + 1 1 512 1 10 10 + 1 + True + + + + + False + False + + + + + True + + + False + False + + + + + True + Save patch to a file + gtk-save + + + False + + + + + True + + + False + False + + + + + True + Clear (Destroy all children) + True + gtk-clear + + + False + + + + + True + Destroy this patch + gtk-delete + + + False + + + + + True + + + False + False + + + + + True + Refresh view + gtk-refresh + + + False + + + + + True + Zoom to normal size + True + gtk-zoom-100 + + + False + + + + + True + Fit patch to window + True + gtk-zoom-fit + + + False + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + False + False + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Enable edit mode to move objects and make connections, disable to play controls ('e' to toggle) + Edit + gtk-edit + True + + + False + + + + + False + 1 + + + + + False + False + + + + + True + True + True + True + True + True + GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_MOTION_MASK | GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK | GDK_BUTTON3_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_FOCUS_CHANGE_MASK | GDK_STRUCTURE_MASK | GDK_PROPERTY_CHANGE_MASK | GDK_VISIBILITY_NOTIFY_MASK | GDK_PROXIMITY_IN_MASK | GDK_PROXIMITY_OUT_MASK | GDK_SUBSTRUCTURE_MASK | GDK_SCROLL_MASK + 1 + GTK_SHADOW_IN + + + + + + 1 + + + + + 2 + 3 + GTK_FILL + + + + + True + + + True + 0 + + + True + True + GTK_POLICY_NEVER + GTK_POLICY_AUTOMATIC + + + True + GTK_SHADOW_NONE + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + + + + + + + + + True + True + + + True + True + Apply changed controls to all voices + All Voices + True + 0 + True + + + False + False + + + + + True + 5 + + + True + True + Apply changed controls to one voice only + Specific Voice: + True + 0 + True + control_panel_all_voices_radio + + + False + False + + + + + True + False + True + Voice control changes are applied to + 1 1 100 1 10 10 + 1 + True + + + 1 + + + + + False + False + 1 + + + + + False + 5 + 1 + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + 0 + 1 + 4 + <b>Name</b> + True + True + + + False + False + + + + + True + 1 + 0 + 1 + 1 + 4 + + + True + + + + + 1 + + + + + True + True + 0 + True + + + False + 2 + + + + + False + False + + + + + 4 + 5 + 8 + + + + + + + 400 + 180 + 8 + Messages - Ingen + ingen.svg + + + True + 6 + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + + + True + True + Error messages from the engine since the last time "Clear" was pressed + 5 + 5 + False + GTK_WRAP_WORD + 5 + 5 + False + False + + + + + + + True + 6 + GTK_BUTTONBOX_END + + + True + False + True + True + gtk-clear + True + 0 + + + + + True + True + gtk-close + True + 0 + + + 1 + + + + + False + 1 + + + + + + + 8 + Configuration - Ingen + ingen.svg + + + True + 6 + + + True + 2 + 2 + + + True + 0 + + + 1 + 2 + GTK_FILL + + + + + + True + <i>Example: /foo/bar:/home/john/patches:/usr/share/om/patches</i> + True + + + 1 + 2 + 1 + 2 + GTK_FILL + + + + + + True + True + * + + + 1 + 2 + + + + + + True + <b>Patch Search Path: </b> + True + + + GTK_FILL + + + + + + False + + + + + True + 6 + GTK_BUTTONBOX_END + + + True + True + Save these settings for future sessions + gtk-save + True + 0 + + + + + True + True + gtk-cancel + True + 0 + + + 1 + + + + + True + True + Apply these settings to this session only + gtk-ok + True + 0 + + + 2 + + + + + False + 1 + + + + + + + 400 + 200 + 8 + Patch Description + GTK_WIN_POS_CENTER_ON_PARENT + ingen.svg + + + True + 6 + + + True + 5 + + + True + Author: + + + False + False + + + + + True + True + * + + + 1 + + + + + False + False + + + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + + + True + True + A short description of the patch to be included in the patch file + GTK_WRAP_WORD + + + + + 1 + + + + + True + 5 + GTK_BUTTONBOX_END + + + True + True + True + gtk-cancel + True + 0 + + + + + True + True + True + Apply these changes to be saved the next time the patch is saved + gtk-ok + True + 0 + + + 1 + + + + + False + False + 2 + + + + + + + 250 + Rename + GTK_WIN_POS_CENTER_ON_PARENT + ingen.svg + + + True + 5 + + + True + + + True + New name: + + + False + False + + + + + True + True + * + True + + + 1 + + + + + + + True + True + + + False + 6 + 1 + + + + + True + 5 + GTK_BUTTONBOX_END + + + True + True + True + gtk-cancel + True + 0 + + + + + True + True + True + True + 0 + + + True + 0 + 0 + + + True + 2 + + + True + gtk-ok + + + False + False + + + + + True + Rename + True + + + False + False + 1 + + + + + + + + + 1 + + + + + False + 2 + + + + + + + 8 + Node Properties - Ingen + GTK_WIN_POS_MOUSE + ingen.svg + + + True + + + True + 0 + <b>Node</b> + True + + + False + False + + + + + True + 12 + 6 + + + True + 4 + + + True + Path: + + + False + False + + + + + True + - + + + False + False + 1 + + + + + False + False + + + + + True + False + True + Polyphonic + True + 0 + True + + + False + False + 1 + + + + + 6 + 1 + + + + + 240 + True + 0 + <b>Plugin</b> + True + + + False + False + 2 + + + + + True + 12 + 3 + 2 + 10 + 6 + + + True + 0 + - + + + 1 + 2 + 2 + 3 + GTK_FILL + + + + + + True + 0 + Name: + + + 2 + 3 + GTK_FILL + + + + + + True + 0 + - + + + 1 + 2 + 1 + 2 + GTK_FILL + + + + + + True + 0 + URI: + + + 1 + 2 + GTK_FILL + + + + + + True + 0 + Type: + + + GTK_FILL + + + + + + True + 0 + - + + + 1 + 2 + GTK_FILL + + + + + + 3 + + + + + + + True + GDK_WINDOW_TYPE_HINT_NORMAL + Copyright (C) 2005-2007 Dave Robillard <http://drobilla.net> + http://drobilla.net/software/ingen + Licensed under the GNU GPL, Version 2. + +See COPYING file included with this distribution, or http://www.gnu.org/licenses/gpl.txt for more information + Author: + Dave Robillard <dave@drobilla.net> + +Contributors: + Lars Luthman - DSSI enhancements, bugfixes + Mario Lang - SuperCollider bindings, bugfixes + Leonard Ritter - Python bindings + + translator-credits + Usability / UI Design: + Thorsten Wilms + ingen.svg + True + + + + + + + False + GTK_PACK_END + + + + + + + 320 + 340 + 8 + Patches - Ingen + ingen.svg + + + True + True + 3 + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + + + True + True + All patches loaded in the engine + True + + + + + + + 6 + Engine - Ingen + False + ingen.svg + GDK_WINDOW_TYPE_HINT_DIALOG + + + True + 6 + + + True + + + True + + + True + 12 + gtk-disconnect + 3 + + + False + + + + + True + 5 + True + + + True + 0.10000000149 + + + + False + False + + + + + True + 0 + Not Connected + + + False + 1 + + + + + 1 + + + + + False + + + + + True + + + False + 4 + 1 + + + + + True + 3 + 2 + 8 + + + True + 0 + + + 1 + 2 + 2 + 3 + GTK_FILL + + + + + + True + False + True + Use internal engine + True + 0 + True + connect_server_radiobutton + + + 2 + 3 + GTK_FILL + + + + + + True + True + Launch and connect to server on port: + True + 0 + True + connect_server_radiobutton + + + 1 + 2 + GTK_FILL + + + + + + True + True + Connect to running server at: + True + 0 + True + + + GTK_FILL + + + + + + True + + + True + True + * + True + 28 + osc.udp://localhost:16180 + + + + + 1 + 2 + GTK_FILL + GTK_FILL + 8 + + + + + True + + + True + False + True + 16180 1 65535 1 10 10 + 1 + True + + + False + False + + + + + 1 + 2 + 1 + 2 + GTK_FILL + 8 + + + + + 2 + + + + + 2 + + + + + True + GTK_BUTTONBOX_END + + + True + True + True + gtk-quit + True + 0 + + + + + True + False + True + True + gtk-disconnect + True + -6 + + + 1 + + + + + True + True + True + True + gtk-connect + True + -6 + + + 2 + + + + + False + False + GTK_PACK_END + + + + + + + True + + + True + Input + True + + + + + True + Add an audio input to this patch + Audio + True + + + + + + True + Add a control input (and a control slider for it) to this patch + Control + True + + + + + + True + Add a MIDI input to this patch + MIDI + True + + + + + + True + Add a generic event input to this patch + Event + True + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Add an OSC input to this patch + OSC + True + + + + + + + True + gtk-connect + 1 + + + + + + + True + Output + True + + + + + True + Add an audio output to this patch + Audio + True + + + + + + True + Add a control output to this patch + Control + True + + + + + + True + Add a MIDI output to this patch + MIDI + True + + + + + + True + Add a generic event output to this patch + Event + True + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Add an OSC output to this patch + OSC + True + + + + + + + True + gtk-connect + 1 + + + + + + + True + Load a plugin as a child of this patch + _Plugin... + True + + + + True + gtk-execute + 1 + + + + + + + True + Load a patch as a child of this patch + _Load Patch... + True + + + + True + gtk-open + 1 + + + + + + + True + Create a new (empty) patch as a child of this patch + _New Patch... + True + + + + True + gtk-new + 1 + + + + + + + 8 + Load Remote Patch + GDK_WINDOW_TYPE_HINT_DIALOG + + + True + 8 + + + True + 8 + + + True + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + + + True + True + + + + + + + True + + + True + URI: + + + False + False + + + + + True + True + * + 78 + + + 1 + + + + + False + 1 + + + + + 2 + + + + + True + GTK_BUTTONBOX_END + + + True + True + True + gtk-cancel + True + -6 + + + + + True + False + True + True + True + gtk-open + True + -5 + + + 1 + + + + + False + GTK_PACK_END + + + + + + + 8 + Upload Patch + False + GDK_WINDOW_TYPE_HINT_DIALOG + + + True + 9 + + + True + 2 + 2 + 8 + + + True + 0 + Short Name: + + + 1 + 2 + GTK_FILL + + + + + + True + 0 + Symbol: + + + GTK_FILL + + + + + + True + True + Enter a short name for this patch, e.g. "Mega Synth" + * + True + + + 1 + 2 + 1 + 2 + + + + + + True + True + Enter a short name suitable for use as an identifier or filename. + +The first character must be one of _, a-z or A-Z and subsequenct characters can be from _, a-z, A-Z or 0-9. + + * + True + + + 1 + 2 + + + + + + 2 + + + + + True + 4 + Succesfully uploaded patches will be available immediately in the remote patch browser. + +By uploading patches, you agree to license them under the Creative Commons Attribution-Share Alike 3.0 License. + +Thank you for contributing. + True + + + False + False + 3 + + + + + True + Upload progress + 0.10000000149 + + + False + False + 4 + + + + + True + GTK_BUTTONBOX_END + + + True + True + True + gtk-close + True + -7 + + + + + True + False + True + True + True + -5 + + + True + 0 + 0 + + + True + 2 + + + True + gtk-ok + + + False + False + + + + + True + Upload + True + + + False + False + 1 + + + + + + + + + 1 + + + + + False + GTK_PACK_END + + + + + + + + + True + _Properties... + True + + + + True + gtk-properties + 1 + + + + + + + 8 + Port Properties - Ingen + False + GTK_WIN_POS_MOUSE + + + True + 8 + + + True + 2 + 2 + 2 + 4 + + + True + 0 + Maximum Value: + + + 1 + 2 + GTK_FILL + + + + + + True + 0 + Minimum Value: + + + GTK_FILL + + + + + + True + True + 1 -99999 99999 1 10 10 + 1 + 5 + True + + + 1 + 2 + 1 + 2 + + + + + + True + True + 0 -100000000 100000000 1 10 10 + 1 + 5 + True + + + 1 + 2 + + + + + + 2 + + + + + True + GTK_BUTTONBOX_END + + + True + True + True + gtk-cancel + True + -6 + + + + + True + True + True + True + gtk-ok + True + -5 + + + 1 + + + + + False + GTK_PACK_END + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Expose individual voices + Polyphonic + True + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Disconnect all connections + gtk-disconnect + True + True + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Rename this object + Rename... + True + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-find-and-replace + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Destroy this object + gtk-delete + True + True + + + + + True + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + View and edit properties + gtk-properties + True + True + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Manipulate controls in a separate window + Controls... + True + + + gtk-edit + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Show this node's custom graphical interface in a separate window + GUI... + True + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-edit + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Embed the custom GUI for this plugin in the patch canvas + Embed GUI + True + + + + diff --git a/src/gui/ingen_gui.gladep b/src/gui/ingen_gui.gladep new file mode 100644 index 00000000..7cd9c6ce --- /dev/null +++ b/src/gui/ingen_gui.gladep @@ -0,0 +1,9 @@ + + + + + Ingenuity + ingenuity + C++ + FALSE + diff --git a/src/gui/wscript b/src/gui/wscript new file mode 100644 index 00000000..8895c7bc --- /dev/null +++ b/src/gui/wscript @@ -0,0 +1,65 @@ +#!/usr/bin/env python +import Params + +def build(bld): + obj = bld.create_obj('cpp', 'shlib') + obj.source = ''' + App.cpp + BreadCrumbBox.cpp + Configuration.cpp + ConnectWindow.cpp + ControlPanel.cpp + Controls.cpp + GladeFactory.cpp + LoadPatchWindow.cpp + LoadPluginWindow.cpp + LoadRemotePatchWindow.cpp + LoadSubpatchWindow.cpp + MessagesWindow.cpp + NewSubpatchWindow.cpp + NodeControlWindow.cpp + NodeMenu.cpp + NodeModule.cpp + NodePropertiesWindow.cpp + ObjectMenu.cpp + PatchCanvas.cpp + PatchPortModule.cpp + PatchPropertiesWindow.cpp + PatchTreeWindow.cpp + PatchView.cpp + PatchWindow.cpp + Port.cpp + PortMenu.cpp + PortPropertiesWindow.cpp + RenameWindow.cpp + SubpatchModule.cpp + ThreadedLoader.cpp + WindowFactory.cpp + gui.cpp + ''' + + if bld.env()['HAVE_CURL']: + obj.source += 'UploadPatchWindow.cpp' + + dir = Params.g_options.moduledir or bld.env()['PREFIX'] + 'lib/ingen' + + obj.includes = ['..', '../../common', '../..'] + obj.defines = 'INGEN_DATA_DIR=\\\"' + dir + '\\\"' + obj.name = 'libingen_gui' + obj.target = 'ingen_gui' + obj.vnum = '0.0.0' + obj.uselib_local = 'libingen_client libingen_shared' + obj.uselib = ''' + CURL + FLOWCANVAS + GLADEMM + GLIBMM + GNOMECANVASMM + GTKMM + RAUL + REDLANDMM + SIGCPP + SLV2 + SOUP + XML2 + ''' diff --git a/src/ingen/Makefile.am b/src/ingen/Makefile.am new file mode 100644 index 00000000..c48a47a2 --- /dev/null +++ b/src/ingen/Makefile.am @@ -0,0 +1,30 @@ +AM_CXXFLAGS = \ + -DINGEN_MODULE_DIR=\"$(libdir)/ingen\" \ + @GTHREAD_CFLAGS@ \ + @GLIBMM_CFLAGS@ \ + @REDLANDMM_CFLAGS@ \ + @RAUL_CFLAGS@ \ + @SLV2_CFLAGS@ \ + @INGEN_CFLAGS@ + +MAINTAINERCLEANFILES = Makefile.in + +desktopfilesdir = $(datadir)/applications +dist_desktopfiles_DATA = ingen.desktop + +bin_PROGRAMS = ingen +ingen_LDADD = \ + ../../libs/module/libingen_module.la \ + ../../libs/shared/libingen_shared.la \ + @SLV2_LIBS@ \ + @REDLANDMM_LIBS@ \ + @RAUL_LIBS@ \ + @GTHREAD_LIBS@ \ + @GLIBMM_LIBS@ + +ingen_DEPENDENCIES = ../../libs/module/libingen_module.la + +ingen_SOURCES = \ + main.cpp \ + cmdline.h \ + cmdline.c diff --git a/src/ingen/cmdline.c b/src/ingen/cmdline.c new file mode 100644 index 00000000..ea48135c --- /dev/null +++ b/src/ingen/cmdline.c @@ -0,0 +1,665 @@ +/* + File autogenerated by gengetopt + generated with the following command: + gengetopt -g + + 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. +*/ + +/* If we use autoconf. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "getopt.h" + +#include "cmdline.h" + +const char *gengetopt_args_info_purpose = "A modular realtime audio processing system"; + +const char *gengetopt_args_info_usage = "Usage: ingen [OPTIONS]..."; + +const char *gengetopt_args_info_description = "Ingen can be run in various configurations. The engine can\nrun as a stand-alone server controlled by OSC, or internal to\nanother process (e.g. the GUI). The GUI can communicate with the engine\nvia either method, and many GUIs (or other things) may connect to an\nengine via OSC.\n\nExamples:\n\n ingen -e - Run an engine, listen for OSC \n ingen -g - Run a GUI, connect via OSC \n ingen -eg - Run an engine and a GUI in one process\n\nThe -l (load) option can be used in all cases:\n \n ingen -el patch.ingen.ttl - Run an engine and load a patch\n ingen -gl patch.ingen.ttl - Run a GUI and load a patch\n ingen -egl patch.ingen.ttl - Run an engine and a GUI and load a patch\n\nOptions:\n"; + +const char *gengetopt_args_info_help[] = { + " -h, --help Print help and exit", + " -V, --version Print version and exit", + " -e, --engine Run (JACK) engine (default=off)", + " -E, --engine-port=INT Engine OSC port (default=`16180')", + " -c, --connect=STRING Connect to existing engine at OSC URI \n (default=`osc.udp://localhost:16180')", + " -g, --gui Launch the GTK graphical interface (default=off)", + " -C, --client-port=INT Client OSC port", + " -l, --load=STRING Load patch", + " -L, --path=STRING Target path for loaded patch", + " -r, --run=STRING Run script", + " -p, --parallelism=INT Number of concurrent process threads (default=`1')", + 0 +}; + +typedef enum {ARG_NO + , ARG_FLAG + , ARG_STRING + , ARG_INT +} cmdline_parser_arg_type; + +static +void clear_given (struct gengetopt_args_info *args_info); +static +void clear_args (struct gengetopt_args_info *args_info); + +static int +cmdline_parser_internal (int argc, char * const *argv, struct gengetopt_args_info *args_info, + struct cmdline_parser_params *params, const char *additional_error); + + +static char * +gengetopt_strdup (const char *s); + +static +void clear_given (struct gengetopt_args_info *args_info) +{ + args_info->help_given = 0 ; + args_info->version_given = 0 ; + args_info->engine_given = 0 ; + args_info->engine_port_given = 0 ; + args_info->connect_given = 0 ; + args_info->gui_given = 0 ; + args_info->client_port_given = 0 ; + args_info->load_given = 0 ; + args_info->path_given = 0 ; + args_info->run_given = 0 ; + args_info->parallelism_given = 0 ; +} + +static +void clear_args (struct gengetopt_args_info *args_info) +{ + args_info->engine_flag = 0; + args_info->engine_port_arg = 16180; + args_info->engine_port_orig = NULL; + args_info->connect_arg = gengetopt_strdup ("osc.udp://localhost:16180"); + args_info->connect_orig = NULL; + args_info->gui_flag = 0; + args_info->client_port_orig = NULL; + args_info->load_arg = NULL; + args_info->load_orig = NULL; + args_info->path_arg = NULL; + args_info->path_orig = NULL; + args_info->run_arg = NULL; + args_info->run_orig = NULL; + args_info->parallelism_arg = 1; + args_info->parallelism_orig = NULL; + +} + +static +void init_args_info(struct gengetopt_args_info *args_info) +{ + + + args_info->help_help = gengetopt_args_info_help[0] ; + args_info->version_help = gengetopt_args_info_help[1] ; + args_info->engine_help = gengetopt_args_info_help[2] ; + args_info->engine_port_help = gengetopt_args_info_help[3] ; + args_info->connect_help = gengetopt_args_info_help[4] ; + args_info->gui_help = gengetopt_args_info_help[5] ; + args_info->client_port_help = gengetopt_args_info_help[6] ; + args_info->load_help = gengetopt_args_info_help[7] ; + args_info->path_help = gengetopt_args_info_help[8] ; + args_info->run_help = gengetopt_args_info_help[9] ; + args_info->parallelism_help = gengetopt_args_info_help[10] ; + +} + +void +cmdline_parser_print_version (void) +{ + printf ("%s %s\n", CMDLINE_PARSER_PACKAGE, CMDLINE_PARSER_VERSION); +} + +static void print_help_common(void) { + cmdline_parser_print_version (); + + if (strlen(gengetopt_args_info_purpose) > 0) + printf("\n%s\n", gengetopt_args_info_purpose); + + if (strlen(gengetopt_args_info_usage) > 0) + printf("\n%s\n", gengetopt_args_info_usage); + + printf("\n"); + + if (strlen(gengetopt_args_info_description) > 0) + printf("%s\n\n", gengetopt_args_info_description); +} + +void +cmdline_parser_print_help (void) +{ + int i = 0; + print_help_common(); + while (gengetopt_args_info_help[i]) + printf("%s\n", gengetopt_args_info_help[i++]); +} + +void +cmdline_parser_init (struct gengetopt_args_info *args_info) +{ + clear_given (args_info); + clear_args (args_info); + init_args_info (args_info); +} + +void +cmdline_parser_params_init(struct cmdline_parser_params *params) +{ + if (params) + { + params->override = 0; + params->initialize = 1; + params->check_required = 1; + params->check_ambiguity = 0; + params->print_errors = 1; + } +} + +struct cmdline_parser_params * +cmdline_parser_params_create(void) +{ + struct cmdline_parser_params *params = + (struct cmdline_parser_params *)malloc(sizeof(struct cmdline_parser_params)); + cmdline_parser_params_init(params); + return params; +} + +static void +free_string_field (char **s) +{ + if (*s) + { + free (*s); + *s = 0; + } +} + + +static void +cmdline_parser_release (struct gengetopt_args_info *args_info) +{ + + free_string_field (&(args_info->engine_port_orig)); + free_string_field (&(args_info->connect_arg)); + free_string_field (&(args_info->connect_orig)); + free_string_field (&(args_info->client_port_orig)); + free_string_field (&(args_info->load_arg)); + free_string_field (&(args_info->load_orig)); + free_string_field (&(args_info->path_arg)); + free_string_field (&(args_info->path_orig)); + free_string_field (&(args_info->run_arg)); + free_string_field (&(args_info->run_orig)); + free_string_field (&(args_info->parallelism_orig)); + + + + clear_given (args_info); +} + + +static void +write_into_file(FILE *outfile, const char *opt, const char *arg, char *values[]) +{ + if (arg) { + fprintf(outfile, "%s=\"%s\"\n", opt, arg); + } else { + fprintf(outfile, "%s\n", opt); + } +} + + +int +cmdline_parser_dump(FILE *outfile, struct gengetopt_args_info *args_info) +{ + int i = 0; + + if (!outfile) + { + fprintf (stderr, "%s: cannot dump options to stream\n", CMDLINE_PARSER_PACKAGE); + return EXIT_FAILURE; + } + + if (args_info->help_given) + write_into_file(outfile, "help", 0, 0 ); + if (args_info->version_given) + write_into_file(outfile, "version", 0, 0 ); + if (args_info->engine_given) + write_into_file(outfile, "engine", 0, 0 ); + if (args_info->engine_port_given) + write_into_file(outfile, "engine-port", args_info->engine_port_orig, 0); + if (args_info->connect_given) + write_into_file(outfile, "connect", args_info->connect_orig, 0); + if (args_info->gui_given) + write_into_file(outfile, "gui", 0, 0 ); + if (args_info->client_port_given) + write_into_file(outfile, "client-port", args_info->client_port_orig, 0); + if (args_info->load_given) + write_into_file(outfile, "load", args_info->load_orig, 0); + if (args_info->path_given) + write_into_file(outfile, "path", args_info->path_orig, 0); + if (args_info->run_given) + write_into_file(outfile, "run", args_info->run_orig, 0); + if (args_info->parallelism_given) + write_into_file(outfile, "parallelism", args_info->parallelism_orig, 0); + + + i = EXIT_SUCCESS; + return i; +} + +int +cmdline_parser_file_save(const char *filename, struct gengetopt_args_info *args_info) +{ + FILE *outfile; + int i = 0; + + outfile = fopen(filename, "w"); + + if (!outfile) + { + fprintf (stderr, "%s: cannot open file for writing: %s\n", CMDLINE_PARSER_PACKAGE, filename); + return EXIT_FAILURE; + } + + i = cmdline_parser_dump(outfile, args_info); + fclose (outfile); + + return i; +} + +void +cmdline_parser_free (struct gengetopt_args_info *args_info) +{ + cmdline_parser_release (args_info); +} + +/** @brief replacement of strdup, which is not standard */ +char * +gengetopt_strdup (const char *s) +{ + char *result = NULL; + if (!s) + return result; + + 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) +{ + return cmdline_parser2 (argc, argv, args_info, 0, 1, 1); +} + +int +cmdline_parser_ext (int argc, char * const *argv, struct gengetopt_args_info *args_info, + struct cmdline_parser_params *params) +{ + int result; + result = cmdline_parser_internal (argc, argv, args_info, params, NULL); + + if (result == EXIT_FAILURE) + { + cmdline_parser_free (args_info); + exit (EXIT_FAILURE); + } + + return result; +} + +int +cmdline_parser2 (int argc, char * const *argv, struct gengetopt_args_info *args_info, int override, int initialize, int check_required) +{ + int result; + struct cmdline_parser_params params; + + params.override = override; + params.initialize = initialize; + params.check_required = check_required; + params.check_ambiguity = 0; + params.print_errors = 1; + + result = cmdline_parser_internal (argc, argv, args_info, ¶ms, NULL); + + if (result == EXIT_FAILURE) + { + cmdline_parser_free (args_info); + exit (EXIT_FAILURE); + } + + return result; +} + +int +cmdline_parser_required (struct gengetopt_args_info *args_info, const char *prog_name) +{ + return EXIT_SUCCESS; +} + + +static char *package_name = 0; + +/** + * @brief updates an option + * @param field the generic pointer to the field to update + * @param orig_field the pointer to the orig field + * @param field_given the pointer to the number of occurrence of this option + * @param prev_given the pointer to the number of occurrence already seen + * @param value the argument for this option (if null no arg was specified) + * @param possible_values the possible values for this option (if specified) + * @param default_value the default value (in case the option only accepts fixed values) + * @param arg_type the type of this option + * @param check_ambiguity @see cmdline_parser_params.check_ambiguity + * @param override @see cmdline_parser_params.override + * @param no_free whether to free a possible previous value + * @param multiple_option whether this is a multiple option + * @param long_opt the corresponding long option + * @param short_opt the corresponding short option (or '-' if none) + * @param additional_error possible further error specification + */ +static +int update_arg(void *field, char **orig_field, + unsigned int *field_given, unsigned int *prev_given, + char *value, char *possible_values[], const char *default_value, + cmdline_parser_arg_type arg_type, + int check_ambiguity, int override, + int no_free, int multiple_option, + const char *long_opt, char short_opt, + const char *additional_error) +{ + char *stop_char = 0; + const char *val = value; + int found; + char **string_field; + + stop_char = 0; + found = 0; + + if (!multiple_option && prev_given && (*prev_given || (check_ambiguity && *field_given))) + { + if (short_opt != '-') + fprintf (stderr, "%s: `--%s' (`-%c') option given more than once%s\n", + package_name, long_opt, short_opt, + (additional_error ? additional_error : "")); + else + fprintf (stderr, "%s: `--%s' option given more than once%s\n", + package_name, long_opt, + (additional_error ? additional_error : "")); + return 1; /* failure */ + } + + + if (field_given && *field_given && ! override) + return 0; + if (prev_given) + (*prev_given)++; + if (field_given) + (*field_given)++; + if (possible_values) + val = possible_values[found]; + + switch(arg_type) { + case ARG_FLAG: + *((int *)field) = !*((int *)field); + break; + case ARG_INT: + if (val) *((int *)field) = strtol (val, &stop_char, 0); + break; + case ARG_STRING: + if (val) { + string_field = (char **)field; + if (!no_free && *string_field) + free (*string_field); /* free previous string */ + *string_field = gengetopt_strdup (val); + } + break; + default: + break; + }; + + /* check numeric conversion */ + switch(arg_type) { + case ARG_INT: + if (val && !(stop_char && *stop_char == '\0')) { + fprintf(stderr, "%s: invalid numeric value: %s\n", package_name, val); + return 1; /* failure */ + } + break; + default: + ; + }; + + /* store the original value */ + switch(arg_type) { + case ARG_NO: + case ARG_FLAG: + break; + default: + if (value && orig_field) { + if (no_free) { + *orig_field = value; + } else { + if (*orig_field) + free (*orig_field); /* free previous string */ + *orig_field = gengetopt_strdup (value); + } + } + }; + + return 0; /* OK */ +} + + +int +cmdline_parser_internal (int argc, char * const *argv, struct gengetopt_args_info *args_info, + struct cmdline_parser_params *params, const char *additional_error) +{ + int c; /* Character of the parsed option. */ + + int error = 0; + struct gengetopt_args_info local_args_info; + + int override; + int initialize; + int check_required; + int check_ambiguity; + + package_name = argv[0]; + + override = params->override; + initialize = params->initialize; + check_required = params->check_required; + check_ambiguity = params->check_ambiguity; + + if (initialize) + cmdline_parser_init (args_info); + + cmdline_parser_init (&local_args_info); + + optarg = 0; + optind = 0; + opterr = params->print_errors; + optopt = '?'; + + while (1) + { + int option_index = 0; + + static struct option long_options[] = { + { "help", 0, NULL, 'h' }, + { "version", 0, NULL, 'V' }, + { "engine", 0, NULL, 'e' }, + { "engine-port", 1, NULL, 'E' }, + { "connect", 1, NULL, 'c' }, + { "gui", 0, NULL, 'g' }, + { "client-port", 1, NULL, 'C' }, + { "load", 1, NULL, 'l' }, + { "path", 1, NULL, 'L' }, + { "run", 1, NULL, 'r' }, + { "parallelism", 1, NULL, 'p' }, + { NULL, 0, NULL, 0 } + }; + + c = getopt_long (argc, argv, "hVeE:c:gC:l:L:r:p:", long_options, &option_index); + + if (c == -1) break; /* Exit from `while (1)' loop. */ + + switch (c) + { + case 'h': /* Print help and exit. */ + cmdline_parser_print_help (); + cmdline_parser_free (&local_args_info); + exit (EXIT_SUCCESS); + + case 'V': /* Print version and exit. */ + cmdline_parser_print_version (); + cmdline_parser_free (&local_args_info); + exit (EXIT_SUCCESS); + + case 'e': /* Run (JACK) engine. */ + + + if (update_arg((void *)&(args_info->engine_flag), 0, &(args_info->engine_given), + &(local_args_info.engine_given), optarg, 0, 0, ARG_FLAG, + check_ambiguity, override, 1, 0, "engine", 'e', + additional_error)) + goto failure; + + break; + case 'E': /* Engine OSC port. */ + + + if (update_arg( (void *)&(args_info->engine_port_arg), + &(args_info->engine_port_orig), &(args_info->engine_port_given), + &(local_args_info.engine_port_given), optarg, 0, "16180", ARG_INT, + check_ambiguity, override, 0, 0, + "engine-port", 'E', + additional_error)) + goto failure; + + break; + case 'c': /* Connect to existing engine at OSC URI. */ + + + if (update_arg( (void *)&(args_info->connect_arg), + &(args_info->connect_orig), &(args_info->connect_given), + &(local_args_info.connect_given), optarg, 0, "osc.udp://localhost:16180", ARG_STRING, + check_ambiguity, override, 0, 0, + "connect", 'c', + additional_error)) + goto failure; + + break; + case 'g': /* Launch the GTK graphical interface. */ + + + if (update_arg((void *)&(args_info->gui_flag), 0, &(args_info->gui_given), + &(local_args_info.gui_given), optarg, 0, 0, ARG_FLAG, + check_ambiguity, override, 1, 0, "gui", 'g', + additional_error)) + goto failure; + + break; + case 'C': /* Client OSC port. */ + + + if (update_arg( (void *)&(args_info->client_port_arg), + &(args_info->client_port_orig), &(args_info->client_port_given), + &(local_args_info.client_port_given), optarg, 0, 0, ARG_INT, + check_ambiguity, override, 0, 0, + "client-port", 'C', + additional_error)) + goto failure; + + break; + case 'l': /* Load patch. */ + + + if (update_arg( (void *)&(args_info->load_arg), + &(args_info->load_orig), &(args_info->load_given), + &(local_args_info.load_given), optarg, 0, 0, ARG_STRING, + check_ambiguity, override, 0, 0, + "load", 'l', + additional_error)) + goto failure; + + break; + case 'L': /* Target path for loaded patch. */ + + + if (update_arg( (void *)&(args_info->path_arg), + &(args_info->path_orig), &(args_info->path_given), + &(local_args_info.path_given), optarg, 0, 0, ARG_STRING, + check_ambiguity, override, 0, 0, + "path", 'L', + additional_error)) + goto failure; + + break; + case 'r': /* Run script. */ + + + if (update_arg( (void *)&(args_info->run_arg), + &(args_info->run_orig), &(args_info->run_given), + &(local_args_info.run_given), optarg, 0, 0, ARG_STRING, + check_ambiguity, override, 0, 0, + "run", 'r', + additional_error)) + goto failure; + + break; + case 'p': /* Number of concurrent process threads. */ + + + if (update_arg( (void *)&(args_info->parallelism_arg), + &(args_info->parallelism_orig), &(args_info->parallelism_given), + &(local_args_info.parallelism_given), optarg, 0, "1", ARG_INT, + check_ambiguity, override, 0, 0, + "parallelism", 'p', + additional_error)) + goto failure; + + break; + + case 0: /* Long option with no short option */ + case '?': /* Invalid option. */ + /* `getopt_long' already printed an error message. */ + goto failure; + + default: /* bug: option not considered. */ + fprintf (stderr, "%s: option unknown: %c%s\n", CMDLINE_PARSER_PACKAGE, c, (additional_error ? additional_error : "")); + abort (); + } /* switch */ + } /* while */ + + + + + cmdline_parser_release (&local_args_info); + + if ( error ) + return (EXIT_FAILURE); + + return 0; + +failure: + + cmdline_parser_release (&local_args_info); + return (EXIT_FAILURE); +} diff --git a/src/ingen/cmdline.ggo b/src/ingen/cmdline.ggo new file mode 100644 index 00000000..994e6ff9 --- /dev/null +++ b/src/ingen/cmdline.ggo @@ -0,0 +1,36 @@ +# Process this file with gengetopt to generate the necessary code (in cmdline.h, cmdline.c) + +package "ingen" +purpose "A modular realtime audio processing system" + +description "Ingen can be run in various configurations. The engine can +run as a stand-alone server controlled by OSC, or internal to +another process (e.g. the GUI). The GUI can communicate with the engine +via either method, and many GUIs (or other things) may connect to an +engine via OSC. + +Examples: + + ingen -e - Run an engine, listen for OSC + ingen -g - Run a GUI, connect via OSC + ingen -eg - Run an engine and a GUI in one process + +The -l (load) option can be used in all cases: + + ingen -el patch.ingen.ttl - Run an engine and load a patch + ingen -gl patch.ingen.ttl - Run a GUI and load a patch + ingen -egl patch.ingen.ttl - Run an engine and a GUI and load a patch + +Options: +" + +option "engine" e "Run (JACK) engine" flag off +option "engine-port" E "Engine OSC port" int no default="16180" +option "connect" c "Connect to existing engine at OSC URI" string no default="osc.udp://localhost:16180" +option "gui" g "Launch the GTK graphical interface" flag off +option "client-port" C "Client OSC port" int no +option "load" l "Load patch" string no +option "path" L "Target path for loaded patch" string no +option "run" r "Run script" string no +option "parallelism" p "Number of concurrent process threads" int no default="1" + diff --git a/src/ingen/cmdline.h b/src/ingen/cmdline.h new file mode 100644 index 00000000..80358089 --- /dev/null +++ b/src/ingen/cmdline.h @@ -0,0 +1,200 @@ +/** @file cmdline.h + * @brief The header file for the command line option parser + * generated by GNU Gengetopt + * http://www.gnu.org/software/gengetopt. + * DO NOT modify this file, since it can be overwritten + * @author GNU Gengetopt by Lorenzo Bettini */ + +#ifndef CMDLINE_H +#define CMDLINE_H + +/* If we use autoconf. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include /* for FILE */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef CMDLINE_PARSER_PACKAGE +/** @brief the program name */ +#define CMDLINE_PARSER_PACKAGE "ingen" +#endif + +#ifndef CMDLINE_PARSER_VERSION +/** @brief the program version */ +#define CMDLINE_PARSER_VERSION VERSION +#endif + +/** @brief Where the command line options are stored */ +struct gengetopt_args_info +{ + const char *help_help; /**< @brief Print help and exit help description. */ + const char *version_help; /**< @brief Print version and exit help description. */ + int engine_flag; /**< @brief Run (JACK) engine (default=off). */ + const char *engine_help; /**< @brief Run (JACK) engine help description. */ + int engine_port_arg; /**< @brief Engine OSC port (default='16180'). */ + char * engine_port_orig; /**< @brief Engine OSC port original value given at command line. */ + const char *engine_port_help; /**< @brief Engine OSC port help description. */ + char * connect_arg; /**< @brief Connect to existing engine at OSC URI (default='osc.udp://localhost:16180'). */ + char * connect_orig; /**< @brief Connect to existing engine at OSC URI original value given at command line. */ + const char *connect_help; /**< @brief Connect to existing engine at OSC URI help description. */ + int gui_flag; /**< @brief Launch the GTK graphical interface (default=off). */ + const char *gui_help; /**< @brief Launch the GTK graphical interface help description. */ + int client_port_arg; /**< @brief Client OSC port. */ + char * client_port_orig; /**< @brief Client OSC port original value given at command line. */ + const char *client_port_help; /**< @brief Client OSC port help description. */ + char * load_arg; /**< @brief Load patch. */ + char * load_orig; /**< @brief Load patch original value given at command line. */ + const char *load_help; /**< @brief Load patch help description. */ + char * path_arg; /**< @brief Target path for loaded patch. */ + char * path_orig; /**< @brief Target path for loaded patch original value given at command line. */ + const char *path_help; /**< @brief Target path for loaded patch help description. */ + char * run_arg; /**< @brief Run script. */ + char * run_orig; /**< @brief Run script original value given at command line. */ + const char *run_help; /**< @brief Run script help description. */ + int parallelism_arg; /**< @brief Number of concurrent process threads (default='1'). */ + char * parallelism_orig; /**< @brief Number of concurrent process threads original value given at command line. */ + const char *parallelism_help; /**< @brief Number of concurrent process threads help description. */ + + unsigned int help_given ; /**< @brief Whether help was given. */ + unsigned int version_given ; /**< @brief Whether version was given. */ + unsigned int engine_given ; /**< @brief Whether engine was given. */ + unsigned int engine_port_given ; /**< @brief Whether engine-port was given. */ + unsigned int connect_given ; /**< @brief Whether connect was given. */ + unsigned int gui_given ; /**< @brief Whether gui was given. */ + unsigned int client_port_given ; /**< @brief Whether client-port was given. */ + unsigned int load_given ; /**< @brief Whether load was given. */ + unsigned int path_given ; /**< @brief Whether path was given. */ + unsigned int run_given ; /**< @brief Whether run was given. */ + unsigned int parallelism_given ; /**< @brief Whether parallelism was given. */ + +} ; + +/** @brief The additional parameters to pass to parser functions */ +struct cmdline_parser_params +{ + int override; /**< @brief whether to override possibly already present options (default 0) */ + int initialize; /**< @brief whether to initialize the option structure gengetopt_args_info (default 1) */ + int check_required; /**< @brief whether to check that all required options were provided (default 1) */ + int check_ambiguity; /**< @brief whether to check for options already specified in the option structure gengetopt_args_info (default 0) */ + int print_errors; /**< @brief whether getopt_long should print an error message for a bad option (default 1) */ +} ; + +/** @brief the purpose string of the program */ +extern const char *gengetopt_args_info_purpose; +/** @brief the usage string of the program */ +extern const char *gengetopt_args_info_usage; +/** @brief all the lines making the help output */ +extern const char *gengetopt_args_info_help[]; + +/** + * The command line parser + * @param argc the number of command line options + * @param argv the command line options + * @param args_info the structure where option information will be stored + * @return 0 if everything went fine, NON 0 if an error took place + */ +int cmdline_parser (int argc, char * const *argv, + struct gengetopt_args_info *args_info); + +/** + * The command line parser (version with additional parameters - deprecated) + * @param argc the number of command line options + * @param argv the command line options + * @param args_info the structure where option information will be stored + * @param override whether to override possibly already present options + * @param initialize whether to initialize the option structure my_args_info + * @param check_required whether to check that all required options were provided + * @return 0 if everything went fine, NON 0 if an error took place + * @deprecated use cmdline_parser_ext() instead + */ +int cmdline_parser2 (int argc, char * const *argv, + struct gengetopt_args_info *args_info, + int override, int initialize, int check_required); + +/** + * The command line parser (version with additional parameters) + * @param argc the number of command line options + * @param argv the command line options + * @param args_info the structure where option information will be stored + * @param params additional parameters for the parser + * @return 0 if everything went fine, NON 0 if an error took place + */ +int cmdline_parser_ext (int argc, char * const *argv, + struct gengetopt_args_info *args_info, + struct cmdline_parser_params *params); + +/** + * Save the contents of the option struct into an already open FILE stream. + * @param outfile the stream where to dump options + * @param args_info the option struct to dump + * @return 0 if everything went fine, NON 0 if an error took place + */ +int cmdline_parser_dump(FILE *outfile, + struct gengetopt_args_info *args_info); + +/** + * Save the contents of the option struct into a (text) file. + * This file can be read by the config file parser (if generated by gengetopt) + * @param filename the file where to save + * @param args_info the option struct to save + * @return 0 if everything went fine, NON 0 if an error took place + */ +int cmdline_parser_file_save(const char *filename, + struct gengetopt_args_info *args_info); + +/** + * Print the help + */ +void cmdline_parser_print_help(void); +/** + * Print the version + */ +void cmdline_parser_print_version(void); + +/** + * Initializes all the fields a cmdline_parser_params structure + * to their default values + * @param params the structure to initialize + */ +void cmdline_parser_params_init(struct cmdline_parser_params *params); + +/** + * Allocates dynamically a cmdline_parser_params structure and initializes + * all its fields to their default values + * @return the created and initialized cmdline_parser_params structure + */ +struct cmdline_parser_params *cmdline_parser_params_create(void); + +/** + * Initializes the passed gengetopt_args_info structure's fields + * (also set default values for options that have a default) + * @param args_info the structure to initialize + */ +void cmdline_parser_init (struct gengetopt_args_info *args_info); +/** + * Deallocates the string fields of the gengetopt_args_info structure + * (but does not deallocate the structure itself) + * @param args_info the structure to deallocate + */ +void cmdline_parser_free (struct gengetopt_args_info *args_info); + +/** + * Checks that all the required options were specified + * @param args_info the structure to check + * @param prog_name the name of the program that will be used to print + * possible errors + * @return + */ +int cmdline_parser_required (struct gengetopt_args_info *args_info, + const char *prog_name); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* CMDLINE_H */ diff --git a/src/ingen/ingen.dbg b/src/ingen/ingen.dbg new file mode 100755 index 00000000..412c89bc --- /dev/null +++ b/src/ingen/ingen.dbg @@ -0,0 +1,5 @@ +#!/usr/bin/env sh + +export INGEN_MODULE_PATH="`pwd`/../../libs/engine/.libs:`pwd`/../../libs/serialisation/.libs:`pwd`/../../libs/gui/.libs:`pwd`/../../libs/client/.libs:`pwd`/../../bindings/.libs" +export INGEN_GLADE_PATH="`pwd`/../../libs/gui/ingen_gui.glade" +libtool --mode=execute gdb --args ./ingen $@ diff --git a/src/ingen/ingen.desktop.in b/src/ingen/ingen.desktop.in new file mode 100644 index 00000000..6bd00e05 --- /dev/null +++ b/src/ingen/ingen.desktop.in @@ -0,0 +1,9 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=Ingen +Comment=Create synthesizers and effects processors in a modular environment +Exec=ingen -g +Terminal=false +Icon=ingen-icon.svg +Type=Application +Categories=Application;AudioVideo;Sound;Audio diff --git a/src/ingen/ingen.dev b/src/ingen/ingen.dev new file mode 100755 index 00000000..023eba7e --- /dev/null +++ b/src/ingen/ingen.dev @@ -0,0 +1,5 @@ +#!/usr/bin/env sh + +export INGEN_MODULE_PATH="`pwd`/../../libs/engine/.libs:`pwd`/../../libs/serialisation/.libs:`pwd`/../../libs/gui/.libs:`pwd`/../../libs/client/.libs:`pwd`/../../bindings/.libs" +export INGEN_GLADE_PATH="`pwd`/../../libs/gui/ingen_gui.glade" +./ingen $@ diff --git a/src/ingen/ingen.grind b/src/ingen/ingen.grind new file mode 100644 index 00000000..9e597d44 --- /dev/null +++ b/src/ingen/ingen.grind @@ -0,0 +1,5 @@ +#!/usr/bin/env sh + +export INGEN_MODULE_PATH="`pwd`/../../libs/engine/.libs:`pwd`/../../libs/serialisation/.libs:`pwd`/../../libs/gui/.libs:`pwd`/../../libs/client/.libs:`pwd`/../../bindings/.libs" +export INGEN_GLADE_PATH="`pwd`/../../libs/gui/ingen_gui.glade" +libtool --mode=execute valgrind ./ingen $@ diff --git a/src/ingen/main.cpp b/src/ingen/main.cpp new file mode 100644 index 00000000..c8adf207 --- /dev/null +++ b/src/ingen/main.cpp @@ -0,0 +1,259 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include CONFIG_H_PATH +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "module/global.hpp" +#include "module/Module.hpp" +#include "module/World.hpp" +#include "engine/Engine.hpp" +#include "engine/QueuedEngineInterface.hpp" +#include "serialisation/Parser.hpp" +#include "cmdline.h" + +#ifdef WITH_BINDINGS +#include "bindings/ingen_bindings.hpp" +#endif + + +using namespace std; +using namespace Ingen; + + +SharedPtr engine; + +void +catch_int(int) +{ + signal(SIGINT, catch_int); + signal(SIGTERM, catch_int); + + cout << "[Main] Ingen interrupted." << endl; + engine->quit(); +} + +int +main(int argc, char** argv) +{ + /* Parse command line options */ + gengetopt_args_info args; + if (cmdline_parser (argc, argv, &args) != 0) + return 1; + + if (argc <= 1) { + cmdline_parser_print_help(); + cerr << endl << "*** Ingen requires at least one command line parameter" << endl; + cerr << "*** Just want to use a graphical app? Try 'ingen -eg'" << endl; + return 1; + } else if (args.connect_given && args.engine_flag) { + cerr << "\n*** Nonsense arguments, can't both run a local engine " + << "and connect to a remote one." << endl + << "*** Run separate instances if that is what you want" << endl; + return 1; + } + + SharedPtr engine_module; + SharedPtr client_module; + SharedPtr gui_module; + SharedPtr bindings_module; + + SharedPtr engine_interface; + + Glib::thread_init(); +#if HAVE_SOUP + g_type_init(); +#endif + + Ingen::Shared::World* world = Ingen::Shared::get_world(); + + /* Set up RDF world */ + world->rdf_world->add_prefix("xsd", "http://www.w3.org/2001/XMLSchema#"); + world->rdf_world->add_prefix("ingen", "http://drobilla.net/ns/ingen#"); + world->rdf_world->add_prefix("ingenuity", "http://drobilla.net/ns/ingenuity#"); + world->rdf_world->add_prefix("lv2", "http://lv2plug.in/ns/lv2core#"); + world->rdf_world->add_prefix("lv2var", "http://lv2plug.in/ns/ext/instance-var#"); + world->rdf_world->add_prefix("lv2_midi", "http://lv2plug.in/ns/ext/midi"); + world->rdf_world->add_prefix("rdfs", "http://www.w3.org/2000/01/rdf-schema#"); + world->rdf_world->add_prefix("doap", "http://usefulinc.com/ns/doap#"); + world->rdf_world->add_prefix("dc", "http://purl.org/dc/elements/1.1/"); + + /* Run engine */ + if (args.engine_flag) { + engine_module = Ingen::Shared::load_module("ingen_engine"); + if (engine_module) { + Engine* (*new_engine)(Ingen::Shared::World* world) = NULL; + if (engine_module->get_symbol("new_engine", (void*&)new_engine)) { + engine = SharedPtr(new_engine(world)); + world->local_engine = engine; + /* Load queued (direct in-process) engine interface */ + if (args.gui_given) { + engine_interface = engine->new_queued_interface(); + world->engine = engine_interface; + } else { + engine->start_osc_driver(args.engine_port_arg); + engine->start_http_driver(args.engine_port_arg); + } + } else { + engine_module.reset(); + } + } else { + cerr << "Unable to load engine module." << endl; + } + } + + /* Load client library */ + if (args.load_given || args.gui_given) { + client_module = Ingen::Shared::load_module("ingen_client"); + if (!client_module) + cerr << "Unable to load client module." << endl; + } + + /* If we don't have a local engine interface (for GUI), use network */ + if (client_module && ! engine_interface) { + SharedPtr (*new_remote_interface)(const std::string&) = NULL; + + if (client_module->get_symbol("new_remote_interface", (void*&)new_remote_interface)) { + engine_interface = new_remote_interface(args.connect_arg); + } else { + cerr << "Unable to find symbol 'new_remote_interface' in " + "ingen_client module, aborting." << endl; + return -1; + } + } + + /* Activate the engine, if we have one */ + if (engine) { + engine->start_jack_driver(); + engine->activate(args.parallelism_arg); + } + + world->engine = engine_interface; + + /* Load a patch */ + if (args.load_given && engine_interface) { + + Glib::ustring engine_base = "/"; + if (args.path_given) + engine_base = string(args.path_arg) + "/"; + + bool found = false; + if (!world->serialisation_module) + world->serialisation_module = Ingen::Shared::load_module("ingen_serialisation"); + + Serialisation::Parser* (*new_parser)() = NULL; + + if (world->serialisation_module) + found = world->serialisation_module->get_symbol("new_parser", (void*&)new_parser); + + if (world->serialisation_module && found) { + SharedPtr parser(new_parser()); + + // Assumption: Containing ':' means URI, otherwise filename + string uri = args.load_arg; + if (uri.find(':') == string::npos) { + if (Glib::path_is_absolute(args.load_arg)) + uri = Glib::filename_to_uri(args.load_arg); + else + uri = Glib::filename_to_uri(Glib::build_filename( + Glib::get_current_dir(), args.load_arg)); + } + + + engine_interface->load_plugins(); + parser->parse_document(world, engine_interface.get(), uri, engine_base, uri); + + } else { + cerr << "Unable to load serialisation module, aborting." << endl; + return -1; + } + } + + + /* Run GUI */ + bool ran_gui = false; + if (args.gui_given) { + gui_module = Ingen::Shared::load_module("ingen_gui"); + void (*run)(int, char**, Ingen::Shared::World*); + bool found = gui_module->get_symbol("run", (void*&)run); + + if (found) { + ran_gui = true; + run(argc, argv, world); + } else { + cerr << "Unable to find GUI module, GUI not loaded." << endl; + } + } + + /* Run a script */ + if (args.run_given) { +#ifdef WITH_BINDINGS + bool (*run_script)(Ingen::Shared::World*, const char*) = NULL; + SharedPtr bindings_module = Ingen::Shared::load_module("ingen_bindings"); + if (!bindings_module) + cerr << Glib::Module::get_last_error() << endl; + + bindings_module->make_resident(); + + bool found = bindings_module->get_symbol("run", (void*&)(run_script)); + if (found) { + cerr << "WORLD: " << world << endl; + cerr << "ENGINE: " << world->engine << endl; + setenv("PYTHONPATH", "../../bindings", 1); + run_script(world, args.run_arg); + } else { + cerr << "FAILED: " << Glib::Module::get_last_error() << endl; + } +#else + cerr << "This build of ingen does not support scripting." << endl; +#endif + + /* Listen to OSC and do our own main thing. */ + } else if (engine && !ran_gui) { + signal(SIGINT, catch_int); + signal(SIGTERM, catch_int); + engine->main(); + } + + cout << "Exiting." << endl; + + if (engine) { + engine->deactivate(); + engine.reset(); + } + + engine_interface.reset(); + client_module.reset(); + world->serialisation_module.reset(); + gui_module.reset(); + engine_module.reset(); + + Ingen::Shared::destroy_world(); + + return 0; +} + diff --git a/src/ingen/wscript b/src/ingen/wscript new file mode 100644 index 00000000..d2e42c51 --- /dev/null +++ b/src/ingen/wscript @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +def build(bld): + obj = bld.create_obj('cpp', 'program') + obj.target = 'ingen' + obj.source = 'main.cpp cmdline.c' + obj.includes = ['.', '../../common', '../..', '../../libs'] + obj.defines = 'VERSION=\\\"' + bld.env()['INGEN_VERSION'] + '\\\"' + obj.uselib = 'GTHREAD GLIBMM REDLANDMM RAUL SLV2 INGEN LIBLO' + obj.uselib_local = 'libingen_module libingen_shared' + diff --git a/src/libs/client/ClientStore.cpp b/src/libs/client/ClientStore.cpp deleted file mode 100644 index 18582046..00000000 --- a/src/libs/client/ClientStore.cpp +++ /dev/null @@ -1,648 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include "ClientStore.hpp" -#include "ObjectModel.hpp" -#include "PatchModel.hpp" -#include "NodeModel.hpp" -#include "PortModel.hpp" -#include "PluginModel.hpp" -#include "PatchModel.hpp" -#include "SigClientInterface.hpp" - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Client { - - -ClientStore::ClientStore(SharedPtr engine, SharedPtr emitter) - : _engine(engine) - , _emitter(emitter) - , _plugins(new Plugins()) -{ - _handle_orphans = (engine && emitter); - - if (!emitter) - return; - - emitter->signal_object_destroyed.connect(sigc::mem_fun(this, &ClientStore::destroy)); - emitter->signal_object_renamed.connect(sigc::mem_fun(this, &ClientStore::rename)); - emitter->signal_new_plugin.connect(sigc::mem_fun(this, &ClientStore::new_plugin)); - emitter->signal_new_patch.connect(sigc::mem_fun(this, &ClientStore::new_patch)); - emitter->signal_new_node.connect(sigc::mem_fun(this, &ClientStore::new_node)); - emitter->signal_new_port.connect(sigc::mem_fun(this, &ClientStore::new_port)); - emitter->signal_patch_cleared.connect(sigc::mem_fun(this, &ClientStore::patch_cleared)); - emitter->signal_connection.connect(sigc::mem_fun(this, &ClientStore::connect)); - emitter->signal_disconnection.connect(sigc::mem_fun(this, &ClientStore::disconnect)); - emitter->signal_variable_change.connect(sigc::mem_fun(this, &ClientStore::set_variable)); - emitter->signal_property_change.connect(sigc::mem_fun(this, &ClientStore::set_property)); - emitter->signal_port_value.connect(sigc::mem_fun(this, &ClientStore::set_port_value)); - emitter->signal_voice_value.connect(sigc::mem_fun(this, &ClientStore::set_voice_value)); - emitter->signal_port_activity.connect(sigc::mem_fun(this, &ClientStore::port_activity)); -} - - -void -ClientStore::clear() -{ - Store::clear(); - _plugins->clear(); -} - - -void -ClientStore::add_plugin_orphan(SharedPtr node) -{ - if (!_handle_orphans) - return; - - Raul::Table > >::iterator spawn - = _plugin_orphans.find(node->plugin_uri()); - - if (spawn != _plugin_orphans.end()) { - spawn->second.push_back(node); - } else { - cerr << "WARNING: Orphans of plugin " << node->plugin_uri() << " received" << endl; - _engine->request_plugin(node->plugin_uri()); - list > l; - l.push_back(node); - _plugin_orphans[node->plugin_uri()] = l; - } -} - - -void -ClientStore::resolve_plugin_orphans(SharedPtr plugin) -{ - if (!_handle_orphans) - return; - Raul::Table > >::iterator n - = _plugin_orphans.find(plugin->uri()); - - if (n != _plugin_orphans.end()) { - - list > spawn = n->second; // take a copy - cerr << "Missing dependant " << plugin->uri() << " received" << endl; - - _plugin_orphans.erase(plugin->uri()); // prevent infinite recursion - - for (list >::iterator i = spawn.begin(); - i != spawn.end(); ++i) { - (*i)->_plugin = plugin; - //add_object(*i); - } - } -} - - -void -ClientStore::add_connection_orphan(std::pair orphan) -{ - // Do this anyway, it's needed to get the connections for copy&paste - //if (!_handle_orphans) - //return; - - if (_handle_orphans) - cerr << "WARNING: Orphan connection " << orphan.first - << " -> " << orphan.second << " received." << endl; - - _connection_orphans.push_back(orphan); -} - - -void -ClientStore::resolve_connection_orphans(SharedPtr port) -{ - if (!_handle_orphans) - return; - assert(port->parent()); - - for (list< pair >::iterator c = _connection_orphans.begin(); - c != _connection_orphans.end(); ) { - - list< pair >::iterator next = c; - ++next; - - if (c->first == port->path() || c->second == port->path()) { - cerr << "Missing dependant (" << c->first << " -> " << c->second << ") received" << endl; - bool success = attempt_connection(c->first, c->second); - if (success) - _connection_orphans.erase(c); - } - - c = next; - } -} - - -void -ClientStore::add_orphan(SharedPtr child) -{ - if (!_handle_orphans) - return; - cerr << "WARNING: Orphan object " << child->path() << " received." << endl; - - Raul::PathTable > >::iterator children - = _orphans.find(child->path().parent()); - - _engine->request_object(child->path().parent()); - - if (children != _orphans.end()) { - children->second.push_back(child); - } else { - list > l; - l.push_back(child); - _orphans.insert(make_pair(child->path().parent(), l)); - } -} - - -void -ClientStore::add_variable_orphan(const Path& subject_path, const string& predicate, const Atom& value) -{ - if (!_handle_orphans) - return; - Raul::PathTable > >::iterator orphans - = _variable_orphans.find(subject_path); - - _engine->request_object(subject_path); - - if (orphans != _variable_orphans.end()) { - orphans->second.push_back(std::pair(predicate, value)); - } else { - list > l; - l.push_back(std::pair(predicate, value)); - _variable_orphans[subject_path] = l; - } -} - - -void -ClientStore::resolve_variable_orphans(SharedPtr subject) -{ - if (!_handle_orphans) - return; - Raul::PathTable > >::iterator v - = _variable_orphans.find(subject->path()); - - if (v != _variable_orphans.end()) { - - list > values = v->second; // take a copy - - _variable_orphans.erase(subject->path()); - cerr << "Missing dependant " << subject->path() << " received" << endl; - - for (list >::iterator i = values.begin(); - i != values.end(); ++i) { - subject->set_variable(i->first, i->second); - } - } -} - - -void -ClientStore::resolve_orphans(SharedPtr parent) -{ - if (!_handle_orphans) - return; - Raul::PathTable > >::iterator c - = _orphans.find(parent->path()); - - if (c != _orphans.end()) { - - list > children = c->second; // take a copy - - _orphans.erase(parent->path()); // prevent infinite recursion - - for (list >::iterator i = children.begin(); - i != children.end(); ++i) { - add_object(*i); - } - } -} - - -void -ClientStore::add_object(SharedPtr object) -{ - // If we already have "this" object, merge the existing one into the new - // one (with precedence to the new values). - iterator existing = find(object->path()); - if (existing != end()) { - PtrCast(existing->second)->set(object); - } else { - - if (object->path() != "/") { - SharedPtr parent = this->object(object->path().parent()); - if (parent) { - assert(object->path().is_child_of(parent->path())); - object->set_parent(parent); - parent->add_child(object); - assert(parent && (object->parent() == parent)); - - (*this)[object->path()] = object; - signal_new_object.emit(object); - - resolve_variable_orphans(parent); - resolve_orphans(parent); - - SharedPtr port = PtrCast(object); - if (port) - resolve_connection_orphans(port); - - } else { - add_orphan(object); - } - } else { - (*this)[object->path()] = object; - signal_new_object.emit(object); - } - - } - - /*cout << "[Store] Added " << object->path() << " {" << endl; - for (iterator i = begin(); i != end(); ++i) { - cout << "\t" << i->first << endl; - } - cout << "}" << endl;*/ -} - - -SharedPtr -ClientStore::remove_object(const Path& path) -{ - iterator i = find(path); - - if (i != end()) { - assert((*i).second->path() == path); - SharedPtr result = PtrCast((*i).second); - assert(result); - //erase(i); - iterator descendants_end = find_descendants_end(i); - SharedPtr removed = yank(i, descendants_end); - - /*cout << "[Store] Removing " << i->first << " {" << endl; - for (iterator i = removed.begin(); i != removed.end(); ++i) { - cout << "\t" << i->first << endl; - } - cout << "}" << endl;*/ - - if (result) - result->signal_destroyed.emit(); - - if (result->path() != "/") { - assert(result->parent()); - - SharedPtr parent = this->object(result->path().parent()); - if (parent) { - parent->remove_child(result); - } - } - - assert(!object(path)); - - return result; - - } else { - return SharedPtr(); - } -} - - -SharedPtr -ClientStore::plugin(const string& uri) -{ - assert(uri.length() > 0); - Plugins::iterator i = _plugins->find(uri); - if (i == _plugins->end()) - return SharedPtr(); - else - return (*i).second; -} - - -SharedPtr -ClientStore::object(const Path& path) -{ - assert(path.length() > 0); - iterator i = find(path); - if (i == end()) { - return SharedPtr(); - } else { - SharedPtr model = PtrCast(i->second); - assert(model); - assert(model->path() == "/" || model->parent()); - return model; - } -} - -void -ClientStore::add_plugin(SharedPtr pm) -{ - // FIXME: dupes? merge, like with objects? - - (*_plugins)[pm->uri()] = pm; - signal_new_plugin(pm); - //cerr << "Plugin: " << pm->uri() << ", # plugins: " << _plugins->size() << endl; -} - - -/* ****** Signal Handlers ******** */ - - -void -ClientStore::destroy(const std::string& path) -{ - SharedPtr removed = remove_object(path); - removed.reset(); - //cerr << "[ClientStore] removed object " << path << ", count: " << removed.use_count(); -} - -void -ClientStore::rename(const Path& old_path, const Path& new_path) -{ - iterator parent = find(old_path); - if (parent == end()) { - cerr << "[Store] Failed to find object " << old_path << " to rename." << endl; - return; - } - - iterator descendants_end = find_descendants_end(parent); - - SharedPtr< Table > > removed - = yank(parent, descendants_end); - - assert(removed->size() > 0); - - for (Table >::iterator i = removed->begin(); i != removed->end(); ++i) { - const Path& child_old_path = i->first; - assert(Path::descendant_comparator(old_path, child_old_path)); - - Path child_new_path; - if (child_old_path == old_path) - child_new_path = new_path; - else - child_new_path = new_path.base() + child_old_path.substr(old_path.length()+1); - - cerr << "[Store] Renamed " << child_old_path << " -> " << child_new_path << endl; - PtrCast(i->second)->set_path(child_new_path); - i->first = child_new_path; - } - - cram(*removed.get()); - - //cerr << "[Store] Table:" << endl; - //for (size_t i=0; i < removed.size(); ++i) { - // cerr << removed[i].first << "\t\t: " << removed[i].second << endl; - //} - /*for (iterator i = begin(); i != end(); ++i) { - cerr << i->first << "\t\t: " << i->second << endl; - }*/ -} - -void -ClientStore::new_plugin(const string& uri, const string& type_uri, const string& symbol, const string& name) -{ - SharedPtr p(new PluginModel(uri, type_uri, symbol, name)); - add_plugin(p); - resolve_plugin_orphans(p); -} - - -void -ClientStore::new_patch(const string& path, uint32_t poly) -{ - SharedPtr p(new PatchModel(path, poly)); - add_object(p); -} - - -void -ClientStore::new_node(const string& path, const string& plugin_uri) -{ - SharedPtr plug = plugin(plugin_uri); - if (!plug) { - SharedPtr n(new NodeModel(plugin_uri, path)); - add_plugin_orphan(n); - add_object(n); - } else { - SharedPtr n(new NodeModel(plug, path)); - add_object(n); - } -} - - -void -ClientStore::new_port(const string& path, uint32_t index, const string& type, bool is_output) -{ - PortModel::Direction pdir = is_output ? PortModel::OUTPUT : PortModel::INPUT; - - SharedPtr p(new PortModel(path, index, type, pdir)); - add_object(p); - if (p->parent()) - resolve_connection_orphans(p); -} - - -void -ClientStore::patch_cleared(const Path& path) -{ - iterator i = find(path); - if (i != end()) { - assert((*i).second->path() == path); - SharedPtr patch = PtrCast(i->second); - - iterator first_descendant = i; - ++first_descendant; - iterator descendants_end = find_descendants_end(i); - SharedPtr< Table > > removed - = yank(first_descendant, descendants_end); - - for (iterator i = removed->begin(); i != removed->end(); ++i) { - SharedPtr model = PtrCast(i->second); - assert(model); - model->signal_destroyed.emit(); - if (model->parent() == patch) - patch->remove_child(model); - } - - } else { - cerr << "[Store] Unable to find patch " << path << " to clear." << endl; - } -} - - -void -ClientStore::set_variable(const string& subject_path, const string& predicate, const Atom& value) -{ - SharedPtr subject = object(subject_path); - - if (!value.is_valid()) { - cerr << "ERROR: variable '" << predicate << "' has no type" << endl; - } else if (subject) { - subject->set_variable(predicate, value); - } else { - add_variable_orphan(subject_path, predicate, value); - cerr << "WARNING: variable for unknown object " << subject_path << endl; - } -} - - -void -ClientStore::set_property(const string& subject_path, const string& predicate, const Atom& value) -{ - SharedPtr subject = object(subject_path); - - if (!value.is_valid()) { - cerr << "ERROR: property '" << predicate << "' has no type" << endl; - } else if (subject) { - subject->set_property(predicate, value); - } else { - cerr << "WARNING: property for unknown object " << subject_path - << " lost. Client must refresh!" << endl; - } -} - - -void -ClientStore::set_port_value(const string& port_path, const Raul::Atom& value) -{ - SharedPtr port = PtrCast(object(port_path)); - if (port) - port->value(value); - else - cerr << "ERROR: control change for nonexistant port " << port_path << endl; -} - - -void -ClientStore::set_voice_value(const string& port_path, uint32_t voice, const Raul::Atom& value) -{ - SharedPtr port = PtrCast(object(port_path)); - if (port) - port->value(voice, value); - else - cerr << "ERROR: poly control change for nonexistant port " << port_path << endl; -} - - -void -ClientStore::port_activity(const Path& port_path) -{ - SharedPtr port = PtrCast(object(port_path)); - if (port) - port->signal_activity.emit(); - else - cerr << "ERROR: activity for nonexistant port " << port_path << endl; -} - - -SharedPtr -ClientStore::connection_patch(const Path& src_port_path, const Path& dst_port_path) -{ - SharedPtr patch; - - if (src_port_path.parent() == dst_port_path.parent()) - patch = PtrCast(this->object(src_port_path.parent())); - - if (!patch && src_port_path.parent() == dst_port_path.parent().parent()) - patch = PtrCast(this->object(src_port_path.parent())); - - if (!patch && src_port_path.parent().parent() == dst_port_path.parent()) - patch = PtrCast(this->object(dst_port_path.parent())); - - if (!patch) - patch = PtrCast(this->object(src_port_path.parent().parent())); - - if (!patch) - cerr << "ERROR: Unable to find connection patch " << src_port_path - << " -> " << dst_port_path << endl; - - return patch; -} - - -bool -ClientStore::attempt_connection(const Path& src_port_path, const Path& dst_port_path, bool add_orphan) -{ - SharedPtr src_port = PtrCast(object(src_port_path)); - SharedPtr dst_port = PtrCast(object(dst_port_path)); - - if (src_port && dst_port) { - assert(src_port->parent()); - assert(dst_port->parent()); - - SharedPtr patch = connection_patch(src_port_path, dst_port_path); - assert(patch); - - SharedPtr cm(new ConnectionModel(src_port, dst_port)); - - src_port->connected_to(dst_port); - dst_port->connected_to(src_port); - - patch->add_connection(cm); - return true; - } else if (add_orphan) { - add_connection_orphan(make_pair(src_port_path, dst_port_path)); - } - - return false; -} - - -void -ClientStore::connect(const string& src_port_path, const string& dst_port_path) -{ - attempt_connection(src_port_path, dst_port_path, true); -} - - -void -ClientStore::disconnect(const string& src_port_path, const string& dst_port_path) -{ - // Find the ports and create a ConnectionModel just to get at the parent path - // finding logic in ConnectionModel. So I'm lazy. - - SharedPtr src_port = PtrCast(object(src_port_path)); - SharedPtr dst_port = PtrCast(object(dst_port_path)); - - if (src_port) - src_port->disconnected_from(dst_port); - else - cerr << "WARNING: Disconnection from nonexistant src port " << src_port_path << endl; - - if (dst_port) - dst_port->disconnected_from(dst_port); - else - cerr << "WARNING: Disconnection from nonexistant dst port " << dst_port_path << endl; - - SharedPtr patch = connection_patch(src_port_path, dst_port_path); - - if (patch) - patch->remove_connection(src_port_path, dst_port_path); - else - cerr << "ERROR: disconnection in nonexistant patch: " - << src_port_path << " -> " << dst_port_path << endl; -} - - -} // namespace Client -} // namespace Ingen - diff --git a/src/libs/client/ClientStore.hpp b/src/libs/client/ClientStore.hpp deleted file mode 100644 index f08fcd9b..00000000 --- a/src/libs/client/ClientStore.hpp +++ /dev/null @@ -1,151 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef CLIENT_STORE_HPP -#define CLIENT_STORE_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "interface/EngineInterface.hpp" -#include "shared/Store.hpp" -using std::string; using std::list; -using Ingen::Shared::EngineInterface; -using Raul::Path; -using Raul::Atom; - -namespace Ingen { - -namespace Shared { class GraphObject; } - -namespace Client { - -class SigClientInterface; -class ObjectModel; -class PluginModel; -class PatchModel; -class NodeModel; -class PortModel; -class ConnectionModel; - - -/** Automatically manages models of objects in the engine. - * - * \ingroup IngenClient - */ -class ClientStore : public Shared::Store, public Shared::CommonInterface, public sigc::trackable { -public: - ClientStore(SharedPtr engine=SharedPtr(), - SharedPtr emitter=SharedPtr()); - - SharedPtr plugin(const string& uri); - SharedPtr object(const Path& path); - - void clear(); - - typedef Raul::Table > Plugins; - SharedPtr plugins() const { return _plugins; } - SharedPtr plugins() { return _plugins; } - void set_plugins(SharedPtr p) { _plugins = p; } - - // CommonInterface - void new_plugin(const string& uri, const string& type_uri, const string& symbol, const string& name); - void new_patch(const string& path, uint32_t poly); - void new_node(const string& path, const string& plugin_uri); - void new_port(const string& path, uint32_t index, const string& data_type, bool is_output); - void set_variable(const string& subject_path, const string& predicate, const Atom& value); - void set_property(const string& subject_path, const string& predicate, const Atom& value); - void set_port_value(const string& port_path, const Raul::Atom& value); - void set_voice_value(const string& port_path, uint32_t voice, const Raul::Atom& value); - void connect(const string& src_port_path, const string& dst_port_path); - void disconnect(const string& src_port_path, const string& dst_port_path); - void destroy(const string& path); - - typedef list< std::pair > ConnectionRecords; - const ConnectionRecords& connection_records() { return _connection_orphans; } - - sigc::signal > signal_new_object; - sigc::signal > signal_new_plugin; - -private: - - void add(Shared::GraphObject* o) { throw; } - - void add_object(SharedPtr object); - SharedPtr remove_object(const Path& path); - - void add_plugin(SharedPtr plugin); - - SharedPtr connection_patch(const Path& src_port_path, const Path& dst_port_path); - - // It would be nice to integrate these somehow.. - - void add_orphan(SharedPtr orphan); - void resolve_orphans(SharedPtr parent); - - void add_connection_orphan(std::pair orphan); - void resolve_connection_orphans(SharedPtr port); - - void add_plugin_orphan(SharedPtr orphan); - void resolve_plugin_orphans(SharedPtr plugin); - - void add_variable_orphan(const Path& subject, const string& predicate, const Atom& value); - void resolve_variable_orphans(SharedPtr subject); - - void bundle_begin() {} - void bundle_end() {} - - // Slots for SigClientInterface signals - void rename(const Path& old_path, const Path& new_path); - void patch_cleared(const Path& path); - void port_activity(const Path& port_path); - - bool attempt_connection(const Path& src_port_path, const Path& dst_port_path, bool add_orphan=false); - - bool _handle_orphans; - - SharedPtr _engine; - SharedPtr _emitter; - - SharedPtr _plugins; ///< Map, keyed by plugin URI - - /** Objects we've received, but depend on the existance of another unknown object. - * Keyed by the path of the depended-on object (for tolerance of orderless comms) */ - Raul::PathTable > > _orphans; - - /** Same idea, except with plugins instead of parents. - * It's unfortunate everything doesn't just have a URI and this was the same.. ahem.. */ - Raul::Table > > _plugin_orphans; - - /** Not orphans OF variable like the above, but orphans which are variable */ - Raul::PathTable > > _variable_orphans; - - /** Ditto */ - ConnectionRecords _connection_orphans; -}; - - -} // namespace Client -} // namespace Ingen - -#endif // CLIENT_STORE_HPP diff --git a/src/libs/client/ConnectionModel.hpp b/src/libs/client/ConnectionModel.hpp deleted file mode 100644 index 91c448df..00000000 --- a/src/libs/client/ConnectionModel.hpp +++ /dev/null @@ -1,76 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef CONNECTIONMODEL_H -#define CONNECTIONMODEL_H - -#include -#include -#include -#include -#include -#include "interface/Connection.hpp" -#include "PortModel.hpp" - -namespace Ingen { -namespace Client { - -class ClientStore; - - -/** Class to represent a port->port connection in the engine. - * - * This can either have pointers to the connection ports' models, or just - * paths as strings. The engine passes just strings (by necessity), but - * clients can set the pointers then they don't have to worry about port - * renaming, as the connections will always return the port's path, even - * if it changes. - * - * \ingroup IngenClient - */ -class ConnectionModel : public Shared::Connection -{ -public: - SharedPtr src_port() const { return _src_port; } - SharedPtr dst_port() const { return _dst_port; } - - const Path src_port_path() const { return _src_port->path(); } - const Path dst_port_path() const { return _dst_port->path(); } - -private: - friend class ClientStore; - - ConnectionModel(SharedPtr src, SharedPtr dst) - : _src_port(src) - , _dst_port(dst) - { - assert(_src_port); - assert(_dst_port); - assert(_src_port->parent()); - assert(_dst_port->parent()); - assert(_src_port->path() != _dst_port->path()); - } - - const SharedPtr _src_port; - const SharedPtr _dst_port; -}; - - -} // namespace Client -} // namespace Ingen - -#endif // CONNECTIONMODEL_H diff --git a/src/libs/client/DeprecatedLoader.cpp b/src/libs/client/DeprecatedLoader.cpp deleted file mode 100644 index a07893f7..00000000 --- a/src/libs/client/DeprecatedLoader.cpp +++ /dev/null @@ -1,711 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - - -#include -#include -#include -#include -#include // for pair, make_pair -#include -#include -#include -#include // for atof -#include -#include -#include -#include -#include -#include "interface/EngineInterface.hpp" -#include "PatchModel.hpp" -#include "NodeModel.hpp" -#include "ConnectionModel.hpp" -#include "PortModel.hpp" -#include "PluginModel.hpp" -#include "DeprecatedLoader.hpp" - -#define NS_INGEN "http://drobilla.net/ns/ingen#" - -using namespace std; - -namespace Ingen { -namespace Client { - - -/** A single port's control setting (in a preset). - * - * \ingroup IngenClient - */ -class ControlModel -{ -public: - ControlModel(const Path& port_path, float value) - : _port_path(port_path) - , _value(value) - { - assert(_port_path.find("//") == string::npos); - } - - const Path& port_path() const { return _port_path; } - void port_path(const string& p) { _port_path = p; } - float value() const { return _value; } - void value(float v) { _value = v; } - -private: - Path _port_path; - float _value; -}; - - -/** Model of a preset (a collection of control settings). - * - * \ingroup IngenClient - */ -class PresetModel -{ -public: - PresetModel(const string& base_path) : _base_path(base_path) {} - - /** Add a control value to this preset. An empty string for a node_name - * means the port is on the patch itself (not a node in the patch). */ - void add_control(const string& node_name, string port_name, float value) { - if (port_name == "note_number") // FIXME: filthy kludge - port_name = "note"; - - if (node_name != "") - _controls.push_back(ControlModel(_base_path + node_name +"/"+ port_name, value)); - else - _controls.push_back(ControlModel(_base_path + port_name, value)); - } - - const string& name() const { return _name; } - void name(const string& n) { _name = n; } - - const list& controls() const { return _controls; } - -private: - string _name; - string _base_path; - list _controls; -}; - - -string -DeprecatedLoader::nameify_if_invalid(const string& name) -{ - if (Path::is_valid_name(name)) { - return name; - } else { - const string new_name = Path::nameify(name); - assert(Path::is_valid_name(new_name)); - if (new_name != name) - cerr << "WARNING: Illegal name '" << name << "' converted to '" - << new_name << "'" << endl; - return new_name; - } -} - - -string -DeprecatedLoader::translate_load_path(const string& path) -{ - std::map::iterator t = _load_path_translations.find(path); - - if (t != _load_path_translations.end()) { - assert(Path::is_valid((*t).second)); - return (*t).second; - // Filthy, filthy kludges - // (FIXME: apply these less heavy handedly, only when it's an internal module) - } else if (path.find("midi") != string::npos) { - assert(Path::is_valid(path)); - if (path.substr(path.find_last_of("/")) == "/MIDI_In") - return path.substr(0, path.find_last_of("/")) + "/input"; - else if (path.substr(path.find_last_of("/")) == "/Note_Number") - return path.substr(0, path.find_last_of("/")) + "/note"; - else if (path.substr(path.find_last_of("/")) == "/Gate") - return path.substr(0, path.find_last_of("/")) + "/gate"; - else if (path.substr(path.find_last_of("/")) == "/Trigger") - return path.substr(0, path.find_last_of("/")) + "/trigger"; - else if (path.substr(path.find_last_of("/")) == "/Velocity") - return path.substr(0, path.find_last_of("/")) + "/velocity"; - else - return path; - } else { - return path; - } -} - - -/** Add a piece of data to a Variables, translating from deprecated unqualified keys - * - * Adds a namespace prefix for known keys, and ignores the rest. - */ -void -DeprecatedLoader::add_variable(GraphObject::Variables& data, string old_key, string value) -{ - string key = ""; - if (old_key == "module-x") - key = "ingenuity:canvas-x"; - else if (old_key == "module-y") - key = "ingenuity:canvas-y"; - - if (key != "") { - // FIXME: should this overwrite existing values? - if (data.find(key) == data.end()) { - // Hack to make module-x and module-y set as floats - char* c_val = strdup(value.c_str()); - char* endptr = NULL; - - // FIXME: locale kludges - char* locale = strdup(setlocale(LC_NUMERIC, NULL)); - - float fval = strtof(c_val, &endptr); - - setlocale(LC_NUMERIC, locale); - free(locale); - - if (endptr != c_val && *endptr == '\0') - data[key] = Atom(fval); - else - data[key] = Atom(value); - - free(c_val); - } - } -} - - -/** Load a patch in to the engine (and client) from a patch file. - * - * The name and poly from the passed PatchModel are used. If the name is - * the empty string, the name will be loaded from the file. If the poly - * is 0, it will be loaded from file. Otherwise the given values will - * be used. - * - * @param filename Local name of file to load patch from - * - * @param parent_path Patch to load this patch as a child of (empty string to load - * to the root patch) - * - * @param name Name of this patch (loaded/generated if the empty string) - * @param poly Polyphony of this patch (loaded/generated if 0) - * - * @param initial_data will be set last, so values passed there will override - * any values loaded from the patch file. - * - * @param existing If true, the patch will be loaded into a currently - * existing patch (ie a merging will take place). Errors will result - * if Nodes of conflicting names exist. - * - * Returns the path of the newly created patch. - */ -string -DeprecatedLoader::load_patch(const Glib::ustring& filename, - boost::optional parent_path, - string name, - GraphObject::Variables initial_data, - bool existing) -{ - cerr << "[DeprecatedLoader] Loading patch " << filename << " under " - << parent_path << " / " << name << endl; - - Path path = parent_path ? (parent_path.get().base() + name) - : "/" + name; - - const bool load_name = (name == ""); - - size_t poly = 0; - - /* Use parameter overridden polyphony, if given */ - GraphObject::Variables::iterator poly_param = initial_data.find("ingen:polyphony"); - if (poly_param != initial_data.end() && poly_param->second.type() == Atom::INT) - poly = poly_param->second.get_int32(); - - if (initial_data.find("filename") == initial_data.end()) - initial_data["filename"] = Atom(filename.c_str()); // FIXME: URL? - - xmlDocPtr doc = xmlParseFile(filename.c_str()); - - if (!doc) { - cerr << "Unable to parse patch file." << endl; - return ""; - } - - xmlNodePtr cur = xmlDocGetRootElement(doc); - - if (!cur) { - cerr << "Empty document." << endl; - xmlFreeDoc(doc); - return ""; - } - - if (xmlStrcmp(cur->name, (const xmlChar*) "patch")) { - cerr << "File is not an Ingen patch file (root node != )" << endl; - xmlFreeDoc(doc); - return ""; - } - - xmlChar* key = NULL; - cur = cur->xmlChildrenNode; - - // Load Patch attributes - while (cur != NULL) { - key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); - - if ((!xmlStrcmp(cur->name, (const xmlChar*)"name"))) { - if (load_name && key) { - if (parent_path) - path = Path(parent_path.get()).base() + nameify_if_invalid((char*)key); - else - path = Path("/"); - } - } else if ((!xmlStrcmp(cur->name, (const xmlChar*)"polyphony"))) { - if (poly == 0) { - poly = atoi((char*)key); - } - } else if (xmlStrcmp(cur->name, (const xmlChar*)"connection") - && xmlStrcmp(cur->name, (const xmlChar*)"node") - && xmlStrcmp(cur->name, (const xmlChar*)"subpatch") - && xmlStrcmp(cur->name, (const xmlChar*)"filename") - && xmlStrcmp(cur->name, (const xmlChar*)"preset")) { - // Don't know what this tag is, add it as variable without overwriting - // (so caller can set arbitrary parameters which will be preserved) - if (key) - add_variable(initial_data, (const char*)cur->name, (const char*)key); - } - - xmlFree(key); - key = NULL; // Avoid a (possible?) double free - - cur = cur->next; - } - - if (poly == 0) - poly = 1; - - cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!! LOADING " << path << endl; - - // Create it, if we're not merging - if (!existing && path != "/") { - _engine->new_patch(path, poly); - for (GraphObject::Variables::const_iterator i = initial_data.begin(); i != initial_data.end(); ++i) - _engine->set_variable(path, i->first, i->second); - } - - // Load nodes - cur = xmlDocGetRootElement(doc)->xmlChildrenNode; - while (cur != NULL) { - if ((!xmlStrcmp(cur->name, (const xmlChar*)"node"))) - load_node(path, doc, cur); - - cur = cur->next; - } - - // Load subpatches - cur = xmlDocGetRootElement(doc)->xmlChildrenNode; - while (cur != NULL) { - if ((!xmlStrcmp(cur->name, (const xmlChar*)"subpatch"))) { - load_subpatch(filename.substr(0, filename.find_last_of("/")), path, doc, cur); - } - cur = cur->next; - } - - // Load connections - cur = xmlDocGetRootElement(doc)->xmlChildrenNode; - while (cur != NULL) { - if ((!xmlStrcmp(cur->name, (const xmlChar*)"connection"))) { - load_connection(path, doc, cur); - } - cur = cur->next; - } - - // Load presets (control values) - cur = xmlDocGetRootElement(doc)->xmlChildrenNode; - while (cur != NULL) { - // I don't think Om ever wrote any preset other than "default"... - if ((!xmlStrcmp(cur->name, (const xmlChar*)"preset"))) { - SharedPtr pm = load_preset(path, doc, cur); - assert(pm != NULL); - if (pm->name() == "default") { - list::const_iterator i = pm->controls().begin(); - for ( ; i != pm->controls().end(); ++i) { - const float value = i->value(); - _engine->set_port_value(translate_load_path(i->port_path()), Atom(value)); - } - } else { - cerr << "WARNING: Unknown preset: \"" << pm->name() << endl; - } - } - cur = cur->next; - } - - xmlFreeDoc(doc); - xmlCleanupParser(); - - // Done above.. late enough? - //for (Variables::const_iterator i = data.begin(); i != data.end(); ++i) - // _engine->set_variable(subject, i->first, i->second); - - if (!existing) - _engine->set_property(path, "ingen:enabled", (bool)true); - - _load_path_translations.clear(); - - return path; -} - - -/** Build a NodeModel given a pointer to a Node in a patch file. - */ -bool -DeprecatedLoader::load_node(const Path& parent, xmlDocPtr doc, const xmlNodePtr node) -{ - xmlChar* key; - xmlNodePtr cur = node->xmlChildrenNode; - - string path = ""; - bool polyphonic = false; - - string plugin_uri; - - string plugin_type; // deprecated - string library_name; // deprecated - string plugin_label; // deprecated - - GraphObject::Variables initial_data; - - while (cur != NULL) { - key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); - - if ((!xmlStrcmp(cur->name, (const xmlChar*)"name"))) { - path = parent.base() + nameify_if_invalid((char*)key); - } else if ((!xmlStrcmp(cur->name, (const xmlChar*)"polyphonic"))) { - polyphonic = !strcmp((char*)key, "true"); - } else if ((!xmlStrcmp(cur->name, (const xmlChar*)"type"))) { - plugin_type = (const char*)key; - } else if ((!xmlStrcmp(cur->name, (const xmlChar*)"library-name"))) { - library_name = (char*)key; - } else if ((!xmlStrcmp(cur->name, (const xmlChar*)"plugin-label"))) { - plugin_label = (char*)key; - } else if ((!xmlStrcmp(cur->name, (const xmlChar*)"plugin-uri"))) { - plugin_uri = (char*)key; - } else if ((!xmlStrcmp(cur->name, (const xmlChar*)"port"))) { - cerr << "FIXME: load port\n"; -#if 0 - xmlNodePtr child = cur->xmlChildrenNode; - - string port_name; - float user_min = 0.0; - float user_max = 0.0; - - while (child != NULL) { - key = xmlNodeListGetString(doc, child->xmlChildrenNode, 1); - - if ((!xmlStrcmp(child->name, (const xmlChar*)"name"))) { - port_name = nameify_if_invalid((char*)key); - } else if ((!xmlStrcmp(child->name, (const xmlChar*)"user-min"))) { - user_min = atof((char*)key); - } else if ((!xmlStrcmp(child->name, (const xmlChar*)"user-max"))) { - user_max = atof((char*)key); - } - - xmlFree(key); - key = NULL; // Avoid a (possible?) double free - - child = child->next; - } - - assert(path.length() > 0); - assert(Path::is_valid(path)); - - // FIXME: /nasty/ assumptions - SharedPtr pm(new PortModel(Path(path).base() + port_name, - PortModel::CONTROL, PortModel::INPUT, PortModel::NONE, - 0.0, user_min, user_max)); - //pm->set_parent(nm); - nm->add_port(pm); -#endif - - } else { // Don't know what this tag is, add it as variable - if (key) - add_variable(initial_data, (const char*)cur->name, (const char*)key); - } - xmlFree(key); - key = NULL; - - cur = cur->next; - } - - if (path == "") { - cerr << "[DeprecatedLoader] Malformed patch file (node tag has empty children)" << endl; - cerr << "[DeprecatedLoader] Node ignored." << endl; - return false; - } - - // Compatibility hacks for old patches that represent patch ports as nodes - if (plugin_uri == "") { - bool is_port = false; - - if (plugin_type == "Internal") { - // FIXME: indices - if (plugin_label == "audio_input") { - _engine->new_port(path, 0, "ingen:AudioPort", false); - is_port = true; - } else if (plugin_label == "audio_output") { - _engine->new_port(path, 0, "ingen:AudioPort", true); - is_port = true; - } else if (plugin_label == "control_input") { - _engine->new_port(path, 0, "ingen:ControlPort", false); - is_port = true; - } else if (plugin_label == "control_output" ) { - _engine->new_port(path, 0, "ingen:ControlPort", true); - is_port = true; - } else if (plugin_label == "midi_input") { - _engine->new_port(path, 0, "ingen:MIDIPort", false); - is_port = true; - } else if (plugin_label == "midi_output" ) { - _engine->new_port(path, 0, "ingen:MIDIPort", true); - is_port = true; - } else { - cerr << "WARNING: Unknown internal plugin label \"" << plugin_label << "\"" << endl; - } - } - - if (is_port) { - const string old_path = path; - const string new_path = (Path::is_valid(old_path) ? old_path : Path::pathify(old_path)); - - if (!Path::is_valid(old_path)) - cerr << "WARNING: Translating invalid port path \"" << old_path << "\" => \"" - << new_path << "\"" << endl; - - // Set up translations (for connections etc) to alias both the old - // module path and the old module/port path to the new port path - _load_path_translations[old_path] = new_path; - _load_path_translations[old_path + "/in"] = new_path; - _load_path_translations[old_path + "/out"] = new_path; - - path = new_path; - - for (GraphObject::Variables::const_iterator i = initial_data.begin(); i != initial_data.end(); ++i) - _engine->set_variable(path, i->first, i->second); - - return SharedPtr(); - - } else { - if (plugin_label == "note_in") { - plugin_uri = NS_INGEN "note_node"; - } else if (plugin_label == "control_input") { - plugin_uri = NS_INGEN "control_node"; - } else if (plugin_label == "transport") { - plugin_uri = NS_INGEN "transport_node"; - } else if (plugin_label == "trigger_in") { - plugin_uri = NS_INGEN "trigger_node"; - } else { - cerr << "WARNING: Unknown deprecated node (label " << plugin_label - << ")." << endl; - } - - if (plugin_uri != "") - _engine->new_node(path, plugin_uri); - else - _engine->new_node_deprecated(path, plugin_type, library_name, plugin_label); - - _engine->set_property(path, "ingen:polyphonic", polyphonic); - - for (GraphObject::Variables::const_iterator i = initial_data.begin(); i != initial_data.end(); ++i) - _engine->set_variable(path, i->first, i->second); - - return true; - } - - // Not deprecated - } else { - _engine->new_node(path, plugin_uri); - _engine->set_property(path, "ingen:polyphonic", polyphonic); - for (GraphObject::Variables::const_iterator i = initial_data.begin(); i != initial_data.end(); ++i) - _engine->set_variable(path, i->first, i->second); - return true; - } - - // (shouldn't get here) -} - - -bool -DeprecatedLoader::load_subpatch(const string& base_filename, const Path& parent, xmlDocPtr doc, const xmlNodePtr subpatch) -{ - xmlChar *key; - xmlNodePtr cur = subpatch->xmlChildrenNode; - - string name = ""; - string filename = ""; - size_t poly = 0; - - GraphObject::Variables initial_data; - - while (cur != NULL) { - key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); - - if ((!xmlStrcmp(cur->name, (const xmlChar*)"name"))) { - name = (const char*)key; - } else if ((!xmlStrcmp(cur->name, (const xmlChar*)"polyphony"))) { - initial_data.insert(make_pair("ingen::polyphony", (int)poly)); - } else if ((!xmlStrcmp(cur->name, (const xmlChar*)"filename"))) { - filename = base_filename + "/" + (const char*)key; - } else { // Don't know what this tag is, add it as variable - if (key != NULL && strlen((const char*)key) > 0) - add_variable(initial_data, (const char*)cur->name, (const char*)key); - } - xmlFree(key); - key = NULL; - - cur = cur->next; - } - - cout << "Loading subpatch " << filename << " under " << parent << endl; - // load_patch sets the passed variable last, so values stored in the parent - // will override values stored in the child patch file - /*string path = */load_patch(filename, parent, name, initial_data, false); - - return false; -} - - -/** Build a ConnectionModel given a pointer to a connection in a patch file. - */ -bool -DeprecatedLoader::load_connection(const Path& parent, xmlDocPtr doc, const xmlNodePtr node) -{ - xmlChar *key; - xmlNodePtr cur = node->xmlChildrenNode; - - string source_node, source_port, dest_node, dest_port; - - while (cur != NULL) { - key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); - - if ((!xmlStrcmp(cur->name, (const xmlChar*)"source-node"))) { - source_node = (char*)key; - } else if ((!xmlStrcmp(cur->name, (const xmlChar*)"source-port"))) { - source_port = (char*)key; - } else if ((!xmlStrcmp(cur->name, (const xmlChar*)"destination-node"))) { - dest_node = (char*)key; - } else if ((!xmlStrcmp(cur->name, (const xmlChar*)"destination-port"))) { - dest_port = (char*)key; - } - - xmlFree(key); - key = NULL; // Avoid a (possible?) double free - - cur = cur->next; - } - - if (source_node == "" || source_port == "" || dest_node == "" || dest_port == "") { - cerr << "ERROR: Malformed patch file (connection tag has empty children)" << endl; - cerr << "ERROR: Connection ignored." << endl; - return false; - } - - // Compatibility fixes for old (fundamentally broken) patches - source_node = nameify_if_invalid(source_node); - source_port = nameify_if_invalid(source_port); - dest_node = nameify_if_invalid(dest_node); - dest_port = nameify_if_invalid(dest_port); - - _engine->connect( - translate_load_path(parent.base() + source_node +"/"+ source_port), - translate_load_path(parent.base() + dest_node +"/"+ dest_port)); - - return true; -} - - -/** Build a PresetModel given a pointer to a preset in a patch file. - */ -SharedPtr -DeprecatedLoader::load_preset(const Path& parent, xmlDocPtr doc, const xmlNodePtr node) -{ - xmlNodePtr cur = node->xmlChildrenNode; - xmlChar* key; - - SharedPtr pm(new PresetModel(parent.base())); - - while (cur != NULL) { - key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); - - if ((!xmlStrcmp(cur->name, (const xmlChar*)"name"))) { - assert(key != NULL); - pm->name((char*)key); - } else if ((!xmlStrcmp(cur->name, (const xmlChar*)"control"))) { - xmlNodePtr child = cur->xmlChildrenNode; - - string node_name = "", port_name = ""; - float val = 0.0; - - while (child != NULL) { - key = xmlNodeListGetString(doc, child->xmlChildrenNode, 1); - - if ((!xmlStrcmp(child->name, (const xmlChar*)"node-name"))) { - node_name = (char*)key; - } else if ((!xmlStrcmp(child->name, (const xmlChar*)"port-name"))) { - port_name = (char*)key; - } else if ((!xmlStrcmp(child->name, (const xmlChar*)"value"))) { - val = atof((char*)key); - } - - xmlFree(key); - key = NULL; // Avoid a (possible?) double free - - child = child->next; - } - - // Compatibility fixes for old patch files - if (node_name != "") - node_name = nameify_if_invalid(node_name); - port_name = nameify_if_invalid(port_name); - - if (port_name == "") { - string msg = "Unable to parse control in patch file ( node = "; - msg.append(node_name).append(", port = ").append(port_name).append(")"); - cerr << "ERROR: " << msg << endl; - //m_client_hooks->error(msg); - } else { - // FIXME: temporary compatibility, remove any slashes from port name - // remove this soon once patches have migrated - string::size_type slash_index; - while ((slash_index = port_name.find("/")) != string::npos) - port_name[slash_index] = '-'; - - pm->add_control(node_name, port_name, val); - } - } - xmlFree(key); - key = NULL; - cur = cur->next; - } - if (pm->name() == "") { - cerr << "Preset in patch file has no name." << endl; - //m_client_hooks->error("Preset in patch file has no name."); - pm->name("Unnamed"); - } - - return pm; -} - -} // namespace Client -} // namespace Ingen diff --git a/src/libs/client/DeprecatedLoader.hpp b/src/libs/client/DeprecatedLoader.hpp deleted file mode 100644 index c1af52c2..00000000 --- a/src/libs/client/DeprecatedLoader.hpp +++ /dev/null @@ -1,94 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef PATCHLIBRARIAN_H -#define PATCHLIBRARIAN_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "interface/EngineInterface.hpp" -#include "interface/GraphObject.hpp" -#include "ObjectModel.hpp" - -using std::string; -using Ingen::Shared::EngineInterface; -using Ingen::Shared::GraphObject; - -namespace Ingen { -namespace Client { - -class PatchModel; -class NodeModel; -class ConnectionModel; -class PresetModel; // defined in DeprecatedLoader.cpp - - -/** Loads deprecated (XML) patch files (from the Om days). - * - * \ingroup IngenClient - */ -class DeprecatedLoader -{ -public: - DeprecatedLoader(SharedPtr engine) - : /*_patch_search_path(".")*/ _engine(engine) - { - assert(_engine); - } - - /*void path(const string& path) { _patch_search_path = path; } - const string& path() { return _patch_search_path; }*/ - - string find_file(const string& filename, const string& additional_path = ""); - - string load_patch(const Glib::ustring& filename, - boost::optional parent_path, - string name, - GraphObject::Variables initial_data, - bool existing = false); - -private: - void add_variable(GraphObject::Variables& data, string key, string value); - - string nameify_if_invalid(const string& name); - string translate_load_path(const string& path); - - //string _patch_search_path; - SharedPtr _engine; - - /// Translations of paths from the loading file to actual paths (for deprecated patches) - std::map _load_path_translations; - - bool load_node(const Path& parent, xmlDocPtr doc, const xmlNodePtr cur); - bool load_connection(const Path& parent, xmlDocPtr doc, const xmlNodePtr cur); - bool load_subpatch(const string& base_filename, const Path& parent, xmlDocPtr doc, const xmlNodePtr cur); - - SharedPtr load_preset(const Path& parent, xmlDocPtr doc, const xmlNodePtr cur); -}; - - -} // namespace Client -} // namespace Ingen - -#endif // PATCHLIBRARIAN_H diff --git a/src/libs/client/HTTPClientReceiver.cpp b/src/libs/client/HTTPClientReceiver.cpp deleted file mode 100644 index ece55ab2..00000000 --- a/src/libs/client/HTTPClientReceiver.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2008 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include -#include -#include "module/Module.hpp" -#include "HTTPClientReceiver.hpp" - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Client { - - -HTTPClientReceiver::HTTPClientReceiver( - Shared::World* world, - const std::string& url, - SharedPtr target) - : _target(target) - , _world(world) - , _url(url) - , _session(NULL) -{ - start(false); -} - - -HTTPClientReceiver::~HTTPClientReceiver() -{ - stop(); -} - - -void -HTTPClientReceiver::message_callback(SoupSession* session, SoupMessage* msg, void* ptr) -{ - HTTPClientReceiver* me = (HTTPClientReceiver*)ptr; - cout << "RECEIVED ASYNC MESSAGE: " << msg->response_body->data << endl; - me->_target->response_ok(0); - me->_target->enable(); - me->_parser->parse_string(me->_world, me->_target.get(), Glib::ustring(msg->response_body->data), - Glib::ustring("/"), Glib::ustring("")); -} - - -void -HTTPClientReceiver::start(bool dump) -{ - Glib::Mutex::Lock lock(_world->rdf_world->mutex()); - if (!_parser) { - if (!_world->serialisation_module) - _world->serialisation_module = Ingen::Shared::load_module("ingen_serialisation"); - - if (_world->serialisation_module) { - Parser* (*new_parser)() = NULL; - if (_world->serialisation_module->get_symbol("new_parser", (void*&)new_parser)) - _parser = SharedPtr(new_parser()); - } - } - _session = soup_session_async_new(); - SoupMessage* msg = soup_message_new("GET", _url.c_str()); - soup_session_queue_message (_session, msg, message_callback, this); -} - - -void -HTTPClientReceiver::stop() -{ - if (_session != NULL) { - //unregister_client(); - soup_session_abort(_session); - _session = NULL; - } -} - - -} // namespace Client -} // namespace Ingen diff --git a/src/libs/client/HTTPClientReceiver.hpp b/src/libs/client/HTTPClientReceiver.hpp deleted file mode 100644 index bab55578..00000000 --- a/src/libs/client/HTTPClientReceiver.hpp +++ /dev/null @@ -1,62 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2008 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef HTTPCLIENTRECEIVER_H -#define HTTPCLIENTRECEIVER_H - -#include -#include -#include -#include "interface/ClientInterface.hpp" -#include "serialisation/Parser.hpp" -#include "redlandmm/World.hpp" -#include "raul/Deletable.hpp" - -namespace Ingen { -namespace Client { - - -class HTTPClientReceiver : public boost::noncopyable, public Raul::Deletable -{ -public: - HTTPClientReceiver(Shared::World* world, - const std::string& url, - SharedPtr target); - - ~HTTPClientReceiver(); - - std::string uri() const { return _url; } - - void start(bool dump); - void stop(); - -private: - static void message_callback(SoupSession* session, SoupMessage* msg, void* ptr); - - SharedPtr _target; - - Shared::World* _world; - const std::string _url; - SoupSession* _session; - SharedPtr _parser; -}; - - -} // namespace Client -} // namespace Ingen - -#endif // HTTPCLIENTRECEIVER_H diff --git a/src/libs/client/HTTPEngineSender.cpp b/src/libs/client/HTTPEngineSender.cpp deleted file mode 100644 index 733e0ac7..00000000 --- a/src/libs/client/HTTPEngineSender.cpp +++ /dev/null @@ -1,285 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2008 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include "HTTPEngineSender.hpp" - -using namespace std; - -namespace Ingen { -namespace Client { - - -HTTPEngineSender::HTTPEngineSender(const string& engine_url) - : _engine_url(engine_url) - , _id(0) - , _enabled(true) -{ - _session = soup_session_sync_new(); -} - - -HTTPEngineSender::~HTTPEngineSender() -{ - soup_session_abort(_session); -} - - -void -HTTPEngineSender::attach(int32_t ping_id, bool block) -{ - /*SoupMessage *msg; - msg = soup_message_new ("GET", _engine_url.c_str()); - int status = soup_session_send_message (_session, msg); - cout << "STATUS: " << status << endl; - cout << "RESPONSE: " << msg->response_body->data << endl;*/ -} - - -/* *** EngineInterface implementation below here *** */ - - -/** Register with the engine via HTTP. - * - * Note that this does not actually use 'key', since the engine creates - * it's own key for HTTP clients (namely the incoming URL), for NAT - * traversal. It is a parameter to remain compatible with EngineInterface. - */ -void -HTTPEngineSender::register_client(ClientInterface* client) -{ - -} - - -void -HTTPEngineSender::unregister_client(const string& uri) -{ - -} - - -// Engine commands -void -HTTPEngineSender::load_plugins() -{ - -} - - -void -HTTPEngineSender::activate() -{ - -} - - -void -HTTPEngineSender::deactivate() -{ - -} - - -void -HTTPEngineSender::quit() -{ - -} - - - -// Object commands - -void -HTTPEngineSender::new_patch(const string& path, - uint32_t poly) -{ -} - - -void -HTTPEngineSender::new_port(const string& path, - uint32_t index, - const string& data_type, - bool is_output) -{ -} - - -void -HTTPEngineSender::new_node(const string& path, - const string& plugin_uri) -{ -} - - -/** Create a node using library name and plugin label (DEPRECATED). - * - * DO NOT USE THIS. - */ -void -HTTPEngineSender::new_node_deprecated(const string& path, - const string& plugin_type, - const string& library_name, - const string& plugin_label) -{ -} - - -void -HTTPEngineSender::rename(const string& old_path, - const string& new_name) -{ -} - - -void -HTTPEngineSender::destroy(const string& path) -{ -} - - -void -HTTPEngineSender::clear_patch(const string& patch_path) -{ -} - - -void -HTTPEngineSender::connect(const string& src_port_path, - const string& dst_port_path) -{ -} - - -void -HTTPEngineSender::disconnect(const string& src_port_path, - const string& dst_port_path) -{ -} - - -void -HTTPEngineSender::disconnect_all(const string& parent_patch_path, - const string& node_path) -{ -} - - -void -HTTPEngineSender::set_port_value(const string& port_path, - const Raul::Atom& value) -{ -} - - -void -HTTPEngineSender::set_voice_value(const string& port_path, - uint32_t voice, - const Raul::Atom& value) -{ -} - - -void -HTTPEngineSender::set_program(const string& node_path, - uint32_t bank, - uint32_t program) -{ -} - - -void -HTTPEngineSender::midi_learn(const string& node_path) -{ -} - - -void -HTTPEngineSender::set_variable(const string& obj_path, - const string& predicate, - const Raul::Atom& value) -{ -} - - -void -HTTPEngineSender::set_property(const string& obj_path, - const string& predicate, - const Raul::Atom& value) -{ -} - - - -// Requests // - -void -HTTPEngineSender::ping() -{ -} - - -void -HTTPEngineSender::request_plugin(const string& uri) -{ -} - - -void -HTTPEngineSender::request_object(const string& path) -{ -} - - -void -HTTPEngineSender::request_port_value(const string& port_path) -{ -} - - -void -HTTPEngineSender::request_variable(const string& object_path, const string& key) -{ -} - - -void -HTTPEngineSender::request_property(const string& object_path, const string& key) -{ -} - - -void -HTTPEngineSender::request_plugins() -{ -} - - -void -HTTPEngineSender::request_all_objects() -{ -} - - - -} // namespace Client -} // namespace Ingen - - diff --git a/src/libs/client/HTTPEngineSender.hpp b/src/libs/client/HTTPEngineSender.hpp deleted file mode 100644 index 411ddfd5..00000000 --- a/src/libs/client/HTTPEngineSender.hpp +++ /dev/null @@ -1,155 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2008 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef HTTPENGINESENDER_H -#define HTTPENGINESENDER_H - -#include -#include -#include -#include "interface/EngineInterface.hpp" -using std::string; -using Ingen::Shared::EngineInterface; -using Ingen::Shared::ClientInterface; - -namespace Ingen { -namespace Client { - - -/* HTTP (via libsoup) interface to the engine. - * - * Clients can use this opaquely as an EngineInterface to control the engine - * over HTTP (whether over a network or not). - * - * \ingroup IngenClient - */ -class HTTPEngineSender : public EngineInterface { -public: - HTTPEngineSender(const string& engine_url); - ~HTTPEngineSender(); - - string uri() const { return _engine_url; } - - inline int32_t next_id() - { int32_t ret = (_id == -1) ? -1 : _id++; return ret; } - - void set_next_response_id(int32_t id) { _id = id; } - void disable_responses() { _id = -1; } - - void attach(int32_t ping_id, bool block); - - - /* *** EngineInterface implementation below here *** */ - - void enable() { _enabled = true; } - void disable() { _enabled = false; } - - void bundle_begin() { transfer_begin(); } - void bundle_end() { transfer_end(); } - - void transfer_begin(); - void transfer_end(); - - // Client registration - void register_client(ClientInterface* client); - void unregister_client(const string& uri); - - // Engine commands - void load_plugins(); - void activate(); - void deactivate(); - void quit(); - - // Object commands - - void new_patch(const string& path, - uint32_t poly); - - void new_port(const string& path, - uint32_t index, - const string& data_type, - bool is_output); - - void new_node(const string& path, - const string& plugin_uri); - - void new_node_deprecated(const string& path, - const string& plugin_type, - const string& library_name, - const string& plugin_label); - - void rename(const string& old_path, - const string& new_name); - - void destroy(const string& path); - - void clear_patch(const string& patch_path); - - void connect(const string& src_port_path, - const string& dst_port_path); - - void disconnect(const string& src_port_path, - const string& dst_port_path); - - void disconnect_all(const string& parent_patch_path, - const string& node_path); - - void set_port_value(const string& port_path, - const Raul::Atom& value); - - void set_voice_value(const string& port_path, - uint32_t voice, - const Raul::Atom& value); - - void set_program(const string& node_path, - uint32_t bank, - uint32_t program); - - void midi_learn(const string& node_path); - - void set_variable(const string& obj_path, - const string& predicate, - const Raul::Atom& value); - - void set_property(const string& obj_path, - const string& predicate, - const Raul::Atom& value); - - // Requests // - void ping(); - void request_plugin(const string& uri); - void request_object(const string& path); - void request_port_value(const string& port_path); - void request_variable(const string& path, const string& key); - void request_property(const string& path, const string& key); - void request_plugins(); - void request_all_objects(); - -protected: - SoupSession* _session; - const string _engine_url; - int _client_port; - int32_t _id; - bool _enabled; -}; - - -} // namespace Client -} // namespace Ingen - -#endif // HTTPENGINESENDER_H - diff --git a/src/libs/client/Makefile.am b/src/libs/client/Makefile.am deleted file mode 100644 index 4af7c243..00000000 --- a/src/libs/client/Makefile.am +++ /dev/null @@ -1,69 +0,0 @@ -if BUILD_CLIENT_LIB - - -moduledir = $(libdir)/ingen - -module_LTLIBRARIES = libingen_client.la - -libingen_client_la_CXXFLAGS = \ - -DPKGDATADIR=\"$(pkgdatadir)\" \ - @INGEN_CFLAGS@ \ - @GLIBMM_CFLAGS@ \ - @LIBLO_CFLAGS@ \ - @LSIGCPP_CFLAGS@ \ - @GLIBMM_CFLAGS@ \ - @LXML2_CFLAGS@ \ - @RAUL_CFLAGS@ \ - @REDLANDMM_CFLAGS@ \ - @SLV2_CFLAGS@ \ - @SOUP_CFLAGS@ - -libingen_client_la_LIBADD = \ - ../shared/libingen_shared.la \ - @GLIBMM_LIBS@ \ - @LIBLO_LIBS@ \ - @LSIGCPP_LIBS@ \ - @LXML2_LIBS@ \ - @RAUL_LIBS@ \ - @REDLANDMM_LIBS@ \ - @SLV2_LIBS@ \ - @SOUP_LIBS@ - -libingen_client_la_SOURCES = \ - ClientStore.cpp \ - ClientStore.hpp \ - ConnectionModel.hpp \ - DeprecatedLoader.cpp \ - DeprecatedLoader.hpp \ - NodeModel.cpp \ - NodeModel.hpp \ - OSCClientReceiver.cpp \ - OSCClientReceiver.hpp \ - OSCEngineSender.cpp \ - OSCEngineSender.hpp \ - ObjectModel.cpp \ - ObjectModel.hpp \ - PatchModel.cpp \ - PatchModel.hpp \ - PluginModel.cpp \ - PluginModel.hpp \ - PluginUI.cpp \ - PluginUI.hpp \ - PortModel.cpp \ - PortModel.hpp \ - SigClientInterface.hpp \ - ThreadedSigClientInterface.cpp \ - ThreadedSigClientInterface.hpp \ - client.cpp \ - client.hpp - -if WITH_SOUP -libingen_client_la_SOURCES += \ - HTTPClientReceiver.cpp \ - HTTPClientReceiver.hpp \ - HTTPEngineSender.cpp \ - HTTPEngineSender.hpp -endif - -endif # BUILD_CLIENT_LIB - diff --git a/src/libs/client/NodeModel.cpp b/src/libs/client/NodeModel.cpp deleted file mode 100644 index ac0c8e68..00000000 --- a/src/libs/client/NodeModel.cpp +++ /dev/null @@ -1,224 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include CONFIG_H_PATH - -#include -#include -#include "interface/Port.hpp" -#include "NodeModel.hpp" -#include "PatchModel.hpp" - -namespace Ingen { -namespace Client { - - -NodeModel::NodeModel(SharedPtr plugin, const Path& path) - : ObjectModel(path) - , _plugin_uri(plugin->uri()) - , _plugin(plugin) - , _num_values(0) - , _min_values(0) - , _max_values(0) -{ -} - -NodeModel::NodeModel(const string& plugin_uri, const Path& path) - : ObjectModel(path) - , _plugin_uri(plugin_uri) - , _num_values(0) - , _min_values(0) - , _max_values(0) -{ -} - - -NodeModel::NodeModel(const NodeModel& copy) - : ObjectModel(copy) - , _plugin_uri(copy._plugin_uri) - , _num_values(copy._num_values) - , _min_values((float*)malloc(sizeof(float) * _num_values)) - , _max_values((float*)malloc(sizeof(float) * _num_values)) -{ - memcpy(_min_values, copy._min_values, sizeof(float) * _num_values); - memcpy(_max_values, copy._max_values, sizeof(float) * _num_values); -} - - -NodeModel::~NodeModel() -{ - clear(); -} - - -void -NodeModel::remove_port(SharedPtr port) -{ - // FIXME: slow - for (Ports::iterator i = _ports.begin(); i != _ports.end(); ++i) { - if ((*i) == port) { - _ports.erase(i); - break; - } - } - signal_removed_port.emit(port); -} - - -void -NodeModel::remove_port(const Path& port_path) -{ - // FIXME: slow - for (Ports::iterator i = _ports.begin(); i != _ports.end(); ++i) { - if ((*i)->path() == port_path) { - _ports.erase(i); - break; - } - } -} - - -void -NodeModel::clear() -{ - _ports.clear(); - assert(_ports.empty()); - delete[] _min_values; - delete[] _max_values; - _min_values = 0; - _max_values = 0; -} - - -void -NodeModel::add_child(SharedPtr c) -{ - assert(c->parent().get() == this); - - //ObjectModel::add_child(c); - - SharedPtr pm = PtrCast(c); - assert(pm); - add_port(pm); -} - - -bool -NodeModel::remove_child(SharedPtr c) -{ - assert(c->path().is_child_of(_path)); - assert(c->parent().get() == this); - - //bool ret = ObjectModel::remove_child(c); - - SharedPtr pm = PtrCast(c); - assert(pm); - remove_port(pm); - - //return ret; - return true; -} - - -void -NodeModel::add_port(SharedPtr pm) -{ - assert(pm); - assert(pm->path().is_child_of(_path)); - assert(pm->parent().get() == this); - - Ports::iterator existing = find(_ports.begin(), _ports.end(), pm); - - // Store should have handled this by merging the two - assert(existing == _ports.end()); - - _ports.push_back(pm); - signal_new_port.emit(pm); -} - - -SharedPtr -NodeModel::get_port(const string& port_name) const -{ - assert(port_name.find("/") == string::npos); - for (Ports::const_iterator i = _ports.begin(); i != _ports.end(); ++i) - if ((*i)->path().name() == port_name) - return (*i); - return SharedPtr(); -} - - -Shared::Port* -NodeModel::port(uint32_t index) const -{ - assert(index < num_ports()); - return dynamic_cast(_ports[index].get()); -} - - -void -NodeModel::port_value_range(SharedPtr port, float& min, float& max) -{ - assert(port->parent().get() == this); - -#ifdef HAVE_SLV2 - // Plugin value first - if (_plugin && _plugin->type() == PluginModel::LV2) { - - if (!_min_values) { - - Glib::Mutex::Lock lock(PluginModel::rdf_world()->mutex()); - - _num_values = slv2_plugin_get_num_ports(_plugin->slv2_plugin()); - _min_values = new float[_num_values]; - _max_values = new float[_num_values]; - slv2_plugin_get_port_ranges_float(_plugin->slv2_plugin(), - _min_values, _max_values, 0); - } - - if (!std::isnan(_min_values[port->index()])) - min = _min_values[port->index()]; - if (!std::isnan(_max_values[port->index()])) - max = _max_values[port->index()]; - } -#endif - - // Possibly overriden - const Atom& min_atom = port->get_variable("ingen:minimum"); - const Atom& max_atom = port->get_variable("ingen:maximum"); - if (min_atom.type() == Atom::FLOAT) - min = min_atom.get_float(); - if (max_atom.type() == Atom::FLOAT) - max = max_atom.get_float(); -} - - -void -NodeModel::set(SharedPtr model) -{ - SharedPtr node = PtrCast(model); - if (node) { - _plugin_uri = node->_plugin_uri; - _plugin = node->_plugin; - } - - ObjectModel::set(model); -} - - -} // namespace Client -} // namespace Ingen diff --git a/src/libs/client/NodeModel.hpp b/src/libs/client/NodeModel.hpp deleted file mode 100644 index 03afc17c..00000000 --- a/src/libs/client/NodeModel.hpp +++ /dev/null @@ -1,101 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef NODEMODEL_H -#define NODEMODEL_H - -#include -#include -#include -#include -#include -#include -#include -#include "interface/Node.hpp" -#include "interface/Port.hpp" -#include "ObjectModel.hpp" -#include "PortModel.hpp" -#include "PluginModel.hpp" - -using std::string; -using Raul::Table; - -namespace Ingen { -namespace Client { - -class PluginModel; -class ClientStore; - - -/** Node model class, used by the client to store engine's state. - * - * \ingroup IngenClient - */ -class NodeModel : public ObjectModel, virtual public Ingen::Shared::Node -{ -public: - NodeModel(const NodeModel& copy); - virtual ~NodeModel(); - - typedef vector > Ports; - - SharedPtr get_port(const string& port_name) const; - - Shared::Port* port(uint32_t index) const; - - const string& plugin_uri() const { return _plugin_uri; } - const Shared::Plugin* plugin() const { return _plugin.get(); } - uint32_t num_ports() const { return _ports.size(); } - const Ports& ports() const { return _ports; } - - void port_value_range(SharedPtr port, float& min, float& max); - - // Signals - sigc::signal > signal_new_port; - sigc::signal > signal_removed_port; - -protected: - friend class ClientStore; - - NodeModel(const string& plugin_uri, const Path& path); - NodeModel(SharedPtr plugin, const Path& path); - - NodeModel(const Path& path); - void add_child(SharedPtr c); - bool remove_child(SharedPtr c); - void add_port(SharedPtr pm); - void remove_port(SharedPtr pm); - void remove_port(const Path& port_path); - void add_program(int bank, int program, const string& name); - void remove_program(int bank, int program); - void set(SharedPtr model); - - virtual void clear(); - - Ports _ports; ///< Vector of ports (not a Table to preserve order) - string _plugin_uri; ///< Plugin URI (if PluginModel is unknown) - SharedPtr _plugin; ///< The plugin this node is an instance of - uint32_t _num_values; ///< Size of _min_values and _max_values - float* _min_values; ///< Port min values (cached for LV2) - float* _max_values; ///< Port max values (cached for LV2) -}; - - -} // namespace Client -} // namespace Ingen - -#endif // NODEMODEL_H diff --git a/src/libs/client/OSCClientReceiver.cpp b/src/libs/client/OSCClientReceiver.cpp deleted file mode 100644 index 8ebd9d8e..00000000 --- a/src/libs/client/OSCClientReceiver.cpp +++ /dev/null @@ -1,394 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "OSCClientReceiver.hpp" -#include -#include -#include -#include -#include -#include - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Client { - - -OSCClientReceiver::OSCClientReceiver(int listen_port, SharedPtr target) - : _target(target) - , _listen_port(listen_port) - , _st(NULL) -{ - start(false); // true = dump, false = shutup -} - - -OSCClientReceiver::~OSCClientReceiver() -{ - stop(); -} - - -void -OSCClientReceiver::start(bool dump_osc) -{ - if (_st != NULL) - return; - - // Attempt preferred port - if (_listen_port != 0) { - char port_str[8]; - snprintf(port_str, 8, "%d", _listen_port); - _st = lo_server_thread_new(port_str, lo_error_cb); - } - - // Find a free port - if (!_st) { - _st = lo_server_thread_new(NULL, lo_error_cb); - _listen_port = lo_server_thread_get_port(_st); - } - - if (_st == NULL) { - cerr << "[OSCClientReceiver] Could not start OSC listener. Aborting." << endl; - exit(EXIT_FAILURE); - } else { - cout << "[OSCClientReceiver] Started OSC listener on port " << lo_server_thread_get_port(_st) << endl; - } - - // Print all incoming messages - if (dump_osc) - lo_server_thread_add_method(_st, NULL, NULL, generic_cb, NULL); - - setup_callbacks(); - - // Display any uncaught messages to the console - //lo_server_thread_add_method(_st, NULL, NULL, unknown_cb, NULL); - - lo_server_thread_start(_st); -} - - -void -OSCClientReceiver::stop() -{ - if (_st != NULL) { - //unregister_client(); - lo_server_thread_free(_st); - _st = NULL; - } -} - - -int -OSCClientReceiver::generic_cb(const char* path, const char* types, lo_arg** argv, int argc, void* data, void* user_data) -{ - printf("[OSCMsg] %s (%s)\t", path, types); - - for (int i=0; i < argc; ++i) { - lo_arg_pp(lo_type(types[i]), argv[i]); - printf("\t"); - } - printf("\n"); - - /*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 -} - - -void -OSCClientReceiver::lo_error_cb(int num, const char* msg, const char* path) -{ - cerr << "Got error from server: " << msg << endl; -} - - - -int -OSCClientReceiver::unknown_cb(const char* path, const char* types, lo_arg** argv, int argc, void* data, void* user_data) -{ - std::string msg = "Received unknown OSC message: "; - msg += path; - - cerr << msg << endl; - - return 0; -} - - -void -OSCClientReceiver::setup_callbacks() -{ - lo_server_thread_add_method(_st, "/ingen/ok", "i", response_ok_cb, this); - lo_server_thread_add_method(_st, "/ingen/error", "is", response_error_cb, this); - lo_server_thread_add_method(_st, "/ingen/plugin", "ssss", plugin_cb, this); - lo_server_thread_add_method(_st, "/ingen/new_patch", "si", new_patch_cb, this); - lo_server_thread_add_method(_st, "/ingen/destroyed", "s", destroyed_cb, this); - lo_server_thread_add_method(_st, "/ingen/patch_cleared", "s", patch_cleared_cb, this); - lo_server_thread_add_method(_st, "/ingen/object_renamed", "ss", object_renamed_cb, this); - lo_server_thread_add_method(_st, "/ingen/new_connection", "ss", connection_cb, this); - lo_server_thread_add_method(_st, "/ingen/disconnection", "ss", disconnection_cb, this); - lo_server_thread_add_method(_st, "/ingen/new_node", "ss", new_node_cb, this); - lo_server_thread_add_method(_st, "/ingen/new_port", "sisi", new_port_cb, this); - lo_server_thread_add_method(_st, "/ingen/set_variable", NULL, set_variable_cb, this); - lo_server_thread_add_method(_st, "/ingen/set_property", NULL, set_property_cb, this); - lo_server_thread_add_method(_st, "/ingen/set_port_value", "sf", set_port_value_cb, this); - lo_server_thread_add_method(_st, "/ingen/set_voice_value", "sif", set_voice_value_cb, this); - lo_server_thread_add_method(_st, "/ingen/port_activity", "s", port_activity_cb, this); - lo_server_thread_add_method(_st, "/ingen/program_add", "siis", program_add_cb, this); - lo_server_thread_add_method(_st, "/ingen/program_remove", "sii", program_remove_cb, this); -} - - -/** Catches errors that aren't a direct result of a client request. - */ -int -OSCClientReceiver::_error_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - _target->error((char*)argv[0]); - return 0; -} - - -int -OSCClientReceiver::_new_patch_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - _target->new_patch(&argv[0]->s, argv[1]->i); // path, poly - return 0; -} - - -int -OSCClientReceiver::_destroyed_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - _target->destroy((const char*)&argv[0]->s); - return 0; -} - - -int -OSCClientReceiver::_patch_cleared_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - _target->patch_cleared((const char*)&argv[0]->s); - return 0; -} - - -int -OSCClientReceiver::_object_renamed_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - _target->object_renamed((const char*)&argv[0]->s, (const char*)&argv[1]->s); - return 0; -} - - -int -OSCClientReceiver::_connection_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* const src_port_path = &argv[0]->s; - const char* const dst_port_path = &argv[1]->s; - - _target->connect(src_port_path, dst_port_path); - - return 0; -} - - -int -OSCClientReceiver::_disconnection_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* src_port_path = &argv[0]->s; - const char* dst_port_path = &argv[1]->s; - - _target->disconnect(src_port_path, dst_port_path); - - return 0; -} - - -/** Notification of a new node creation. - */ -int -OSCClientReceiver::_new_node_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* uri = &argv[0]->s; - const char* node_path = &argv[1]->s; - - _target->new_node(uri, node_path); - - return 0; -} - - -/** Notification of a new port creation. - */ -int -OSCClientReceiver::_new_port_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* port_path = &argv[0]->s; - const uint32_t index = argv[1]->i; - const char* type = &argv[2]->s; - const bool is_output = (argv[3]->i == 1); - - _target->new_port(port_path, index, type, is_output); - - return 0; -} - - -/** Notification of a new or updated variable. - */ -int -OSCClientReceiver::_set_variable_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - if (argc != 3 || types[0] != 's' || types[1] != 's') - return 1; - - const char* obj_path = &argv[0]->s; - const char* key = &argv[1]->s; - - Atom value = AtomLiblo::lo_arg_to_atom(types[2], argv[2]); - - _target->set_variable(obj_path, key, value); - - return 0; -} - - -/** Notification of a new or updated property. - */ -int -OSCClientReceiver::_set_property_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - if (argc != 3 || types[0] != 's' || types[1] != 's') - return 1; - - const char* obj_path = &argv[0]->s; - const char* key = &argv[1]->s; - - Atom value = AtomLiblo::lo_arg_to_atom(types[2], argv[2]); - - _target->set_property(obj_path, key, value); - - return 0; -} - - -int -OSCClientReceiver::_set_port_value_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* const port_path = &argv[0]->s; - const float value = argv[1]->f; - - _target->set_port_value(port_path, value); - - return 0; -} - - -int -OSCClientReceiver::_set_voice_value_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* const port_path = &argv[0]->s; - const int voice = argv[1]->i; - const float value = argv[2]->f; - - _target->set_voice_value(port_path, voice, value); - - return 0; -} - - -int -OSCClientReceiver::_port_activity_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* const port_path = &argv[0]->s; - - _target->port_activity(port_path); - - return 0; -} - - -int -OSCClientReceiver::_response_ok_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - assert(!strcmp(types, "i")); - _target->response_ok(argv[0]->i); - - return 0; -} - - -int -OSCClientReceiver::_response_error_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - assert(!strcmp(types, "is")); - _target->response_error(argv[0]->i, &argv[1]->s); - - return 0; -} - - -/** A plugin info response from the server, in response to an /ingen/send_plugins - */ -int -OSCClientReceiver::_plugin_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - assert(argc == 4 && !strcmp(types, "ssss")); - _target->new_plugin(&argv[0]->s, &argv[1]->s, &argv[2]->s, &argv[3]->s); // uri, type, symbol, name - - return 0; -} - - -int -OSCClientReceiver::_program_add_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* node_path = &argv[0]->s; - int32_t bank = argv[1]->i; - int32_t program = argv[2]->i; - const char* name = &argv[3]->s; - - _target->program_add(node_path, bank, program, name); - - return 0; -} - - -int -OSCClientReceiver::_program_remove_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* node_path = &argv[0]->s; - int32_t bank = argv[1]->i; - int32_t program = argv[2]->i; - - _target->program_remove(node_path, bank, program); - - return 0; -} - - -} // namespace Client -} // namespace Ingen diff --git a/src/libs/client/OSCClientReceiver.hpp b/src/libs/client/OSCClientReceiver.hpp deleted file mode 100644 index ea5871b3..00000000 --- a/src/libs/client/OSCClientReceiver.hpp +++ /dev/null @@ -1,107 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef OSCCLIENTRECEIVER_H -#define OSCCLIENTRECEIVER_H - -#include -#include -#include -#include "interface/ClientInterface.hpp" -#include "raul/Deletable.hpp" - -namespace Ingen { -namespace Client { - -/** Arguments to a liblo handler */ -#define LO_HANDLER_ARGS const char* path, const char* types, lo_arg** argv, int argc, lo_message msg - -/** Define 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. */ -#define LO_HANDLER(name) \ -int _##name##_cb (LO_HANDLER_ARGS);\ -inline static int name##_cb(LO_HANDLER_ARGS, void* osc_listener)\ -{ return ((OSCClientReceiver*)osc_listener)->_##name##_cb(path, types, argv, argc, msg); } - - -/** Callbacks for "notification band" OSC messages. - * - * Receives all notification of engine state, but not replies on the "control - * band". See OSC namespace documentation for details. - * - * Right now this class and Comm share the same lo_server_thread and the barrier - * between them is a bit odd, but eventually this class will be able to listen - * on a completely different port (ie have it's own lo_server_thread) to allow - * things like listening to the notification band over TCP while sending commands - * on the control band over UDP. - * - * \ingroup IngenClient - */ -class OSCClientReceiver : public boost::noncopyable, public Raul::Deletable -{ -public: - OSCClientReceiver(int listen_port, SharedPtr target); - ~OSCClientReceiver(); - - std::string uri() const { return lo_server_thread_get_url(_st); } - - void start(bool dump_osc); - void stop(); - - int listen_port() { return _listen_port; } - std::string listen_url() { return lo_server_thread_get_url(_st); } - -private: - void setup_callbacks(); - - static void lo_error_cb(int num, const char* msg, const char* path); - - static int generic_cb(const char* path, const char* types, lo_arg** argv, int argc, void* data, void* user_data); - static int unknown_cb(const char* path, const char* types, lo_arg** argv, int argc, void* data, void* osc_receiver); - - SharedPtr _target; - - int _listen_port; - lo_server_thread _st; - - LO_HANDLER(error); - LO_HANDLER(response_ok); - LO_HANDLER(response_error); - LO_HANDLER(plugin); - LO_HANDLER(plugin_list_end); - LO_HANDLER(new_patch); - LO_HANDLER(destroyed); - LO_HANDLER(patch_cleared); - LO_HANDLER(object_renamed); - LO_HANDLER(connection); - LO_HANDLER(disconnection); - LO_HANDLER(new_node); - LO_HANDLER(new_port); - LO_HANDLER(set_variable); - LO_HANDLER(set_property); - LO_HANDLER(set_port_value); - LO_HANDLER(set_voice_value); - LO_HANDLER(port_activity); - LO_HANDLER(program_add); - LO_HANDLER(program_remove); -}; - - -} // namespace Client -} // namespace Ingen - -#endif // OSCCLIENTRECEIVER_H diff --git a/src/libs/client/OSCEngineSender.cpp b/src/libs/client/OSCEngineSender.cpp deleted file mode 100644 index c21d16ce..00000000 --- a/src/libs/client/OSCEngineSender.cpp +++ /dev/null @@ -1,420 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include "OSCEngineSender.hpp" - -using namespace std; -using Raul::Atom; - -namespace Ingen { -namespace Client { - - -/** Note the sending port is implicitly set by liblo, lo_send by default sends - * from the most recently created server, so create the OSC listener before - * this to have it all happen on the same port. Yeah, this is a big magic :/ - */ -OSCEngineSender::OSCEngineSender(const string& engine_url) - : _engine_url(engine_url) - , _id(0) -{ - _address = lo_address_new_from_url(engine_url.c_str()); -} - - -OSCEngineSender::~OSCEngineSender() -{ - lo_address_free(_address); -} - - -/** Attempt to connect to the engine (by pinging it). - * - * This doesn't register a client (or otherwise affect the client/engine state). - * To check for success wait for the ping response with id @a ping_id (using the - * relevant OSCClientReceiver). - * - * Passing a client_port of 0 will automatically choose a free port. If the - * @a block parameter is true, this function will not return until a connection - * has successfully been made. - */ -void -OSCEngineSender::attach(int32_t ping_id, bool block) -{ - if (!_address) - _address = lo_address_new_from_url(_engine_url.c_str()); - - if (_address == NULL) { - cerr << "Aborting: Unable to connect to " << _engine_url << endl; - exit(EXIT_FAILURE); - } - - cout << "[OSCEngineSender] Attempting to contact engine at " << _engine_url << " ..." << endl; - - _id = ping_id; - this->ping(); -} - -/* *** EngineInterface implementation below here *** */ - - -/** Register with the engine via OSC. - * - * Note that this does not actually use 'key', since the engine creates - * it's own key for OSC clients (namely the incoming URL), for NAT - * traversal. It is a parameter to remain compatible with EngineInterface. - */ -void -OSCEngineSender::register_client(ClientInterface* client) -{ - // FIXME: use parameters.. er, somehow. - send("/ingen/register_client", "i", next_id(), LO_ARGS_END, LO_ARGS_END); -} - - -void -OSCEngineSender::unregister_client(const string& uri) -{ - send("/ingen/unregister_client", "i", next_id(), LO_ARGS_END); -} - - -// Engine commands -void -OSCEngineSender::load_plugins() -{ - send("/ingen/load_plugins", "i", next_id(), LO_ARGS_END); -} - - -void -OSCEngineSender::activate() -{ - send("/ingen/activate", "i", next_id(), LO_ARGS_END); -} - - -void -OSCEngineSender::deactivate() -{ - send("/ingen/deactivate", "i", next_id(), LO_ARGS_END); -} - - -void -OSCEngineSender::quit() -{ - send("/ingen/quit", "i", next_id(), LO_ARGS_END); -} - - - -// Object commands - -void -OSCEngineSender::new_patch(const string& path, - uint32_t poly) -{ - send("/ingen/new_patch", "isi", - next_id(), - path.c_str(), - poly, - LO_ARGS_END); -} - - -void -OSCEngineSender::new_port(const string& path, - uint32_t index, - const string& data_type, - bool is_output) -{ - // FIXME: use index - send("/ingen/new_port", "issi", - next_id(), - path.c_str(), - data_type.c_str(), - (is_output ? 1 : 0), - LO_ARGS_END); -} - - -void -OSCEngineSender::new_node(const string& path, - const string& plugin_uri) -{ - - send("/ingen/new_node", "iss", - next_id(), - path.c_str(), - plugin_uri.c_str(), - LO_ARGS_END); -} - - -/** Create a node using library name and plugin label (DEPRECATED). - * - * DO NOT USE THIS. - */ -void -OSCEngineSender::new_node_deprecated(const string& path, - const string& plugin_type, - const string& library_name, - const string& plugin_label) -{ - send("/ingen/new_node", "issss", - next_id(), - path.c_str(), - plugin_type.c_str(), - library_name.c_str(), - plugin_label.c_str(), - LO_ARGS_END); -} - - -void -OSCEngineSender::rename(const string& old_path, - const string& new_name) -{ - send("/ingen/rename", "iss", - next_id(), - old_path.c_str(), - new_name.c_str(), - LO_ARGS_END); -} - - -void -OSCEngineSender::destroy(const string& path) -{ - send("/ingen/destroy", "is", - next_id(), - path.c_str(), - LO_ARGS_END); -} - - -void -OSCEngineSender::clear_patch(const string& patch_path) -{ - send("/ingen/clear_patch", "is", - next_id(), - patch_path.c_str(), - LO_ARGS_END); -} - - -void -OSCEngineSender::connect(const string& src_port_path, - const string& dst_port_path) -{ - send("/ingen/connect", "iss", - next_id(), - src_port_path.c_str(), - dst_port_path.c_str(), - LO_ARGS_END); -} - - -void -OSCEngineSender::disconnect(const string& src_port_path, - const string& dst_port_path) -{ - send("/ingen/disconnect", "iss", - next_id(), - src_port_path.c_str(), - dst_port_path.c_str(), - LO_ARGS_END); -} - - -void -OSCEngineSender::disconnect_all(const string& parent_patch_path, - const string& node_path) -{ - send("/ingen/disconnect_all", "iss", - next_id(), - parent_patch_path.c_str(), - node_path.c_str(), - LO_ARGS_END); -} - - -void -OSCEngineSender::set_port_value(const string& port_path, - const Raul::Atom& value) -{ - lo_message m = lo_message_new(); - lo_message_add_int32(m, next_id()); - lo_message_add_string(m, port_path.c_str()); - if (value.type() == Atom::BLOB) - lo_message_add_string(m, value.get_blob_type()); - Raul::AtomLiblo::lo_message_add_atom(m, value); - send_message("/ingen/set_port_value", m); -} - - -void -OSCEngineSender::set_voice_value(const string& port_path, - uint32_t voice, - const Raul::Atom& value) -{ - lo_message m = lo_message_new(); - lo_message_add_int32(m, next_id()); - lo_message_add_string(m, port_path.c_str()); - lo_message_add_int32(m, voice); - if (value.type() == Atom::BLOB) - lo_message_add_string(m, value.get_blob_type()); - Raul::AtomLiblo::lo_message_add_atom(m, value); - send_message("/ingen/set_port_value", m); -} - - -void -OSCEngineSender::set_program(const string& node_path, - uint32_t bank, - uint32_t program) -{ - send((string("/dssi") + node_path + "/program").c_str(), - "ii", - bank, - program, - LO_ARGS_END); -} - - -void -OSCEngineSender::midi_learn(const string& node_path) -{ - send("/ingen/midi_learn", "is", - next_id(), - node_path.c_str(), - LO_ARGS_END); -} - - -void -OSCEngineSender::set_variable(const string& obj_path, - const string& predicate, - const Raul::Atom& value) -{ - lo_message m = lo_message_new(); - lo_message_add_int32(m, next_id()); - lo_message_add_string(m, obj_path.c_str()); - lo_message_add_string(m, predicate.c_str()); - Raul::AtomLiblo::lo_message_add_atom(m, value); - send_message("/ingen/set_variable", m); -} - - -void -OSCEngineSender::set_property(const string& obj_path, - const string& predicate, - const Raul::Atom& value) -{ - lo_message m = lo_message_new(); - lo_message_add_int32(m, next_id()); - lo_message_add_string(m, obj_path.c_str()); - lo_message_add_string(m, predicate.c_str()); - Raul::AtomLiblo::lo_message_add_atom(m, value); - send_message("/ingen/set_property", m); -} - - - -// Requests // - -void -OSCEngineSender::ping() -{ - send("/ingen/ping", "i", next_id(), LO_ARGS_END); -} - - -void -OSCEngineSender::request_plugin(const string& uri) -{ - send("/ingen/request_plugin", "is", - next_id(), - uri.c_str(), - LO_ARGS_END); -} - - -void -OSCEngineSender::request_object(const string& path) -{ - send("/ingen/request_object", "is", - next_id(), - path.c_str(), - LO_ARGS_END); -} - - -void -OSCEngineSender::request_port_value(const string& port_path) -{ - send("/ingen/request_port_value", "is", - next_id(), - port_path.c_str(), - LO_ARGS_END); -} - - -void -OSCEngineSender::request_variable(const string& object_path, const string& key) -{ - send("/ingen/request_variable", "iss", - next_id(), - object_path.c_str(), - key.c_str(), - LO_ARGS_END); -} - - -void -OSCEngineSender::request_property(const string& object_path, const string& key) -{ - send("/ingen/request_property", "iss", - next_id(), - object_path.c_str(), - key.c_str(), - LO_ARGS_END); -} - - -void -OSCEngineSender::request_plugins() -{ - send("/ingen/request_plugins", "i", next_id(), LO_ARGS_END); -} - - -void -OSCEngineSender::request_all_objects() -{ - send("/ingen/request_all_objects", "i", next_id(), LO_ARGS_END); -} - - - -} // namespace Client -} // namespace Ingen - - diff --git a/src/libs/client/OSCEngineSender.hpp b/src/libs/client/OSCEngineSender.hpp deleted file mode 100644 index ef4a2fa3..00000000 --- a/src/libs/client/OSCEngineSender.hpp +++ /dev/null @@ -1,154 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef OSCENGINESENDER_H -#define OSCENGINESENDER_H - -#include -#include -#include -#include "interface/EngineInterface.hpp" -#include "shared/OSCSender.hpp" -using std::string; -using Ingen::Shared::EngineInterface; -using Ingen::Shared::ClientInterface; - -namespace Ingen { -namespace Client { - - -/* OSC (via liblo) interface to the engine. - * - * Clients can use this opaquely as an EngineInterface* to control the engine - * over OSC (whether over a network or not, etc). - * - * \ingroup IngenClient - */ -class OSCEngineSender : public EngineInterface, public Shared::OSCSender { -public: - OSCEngineSender(const string& engine_url); - - ~OSCEngineSender(); - - std::string uri() const { return _engine_url; } - - inline int32_t next_id() - { int32_t ret = (_id == -1) ? -1 : _id++; return ret; } - - void set_next_response_id(int32_t id) { _id = id; } - void disable_responses() { _id = -1; } - - void attach(int32_t ping_id, bool block); - - - /* *** EngineInterface implementation below here *** */ - - void enable() { _enabled = true; } - void disable() { _enabled = false; } - - void bundle_begin() { OSCSender::bundle_begin(); } - void bundle_end() { OSCSender::bundle_end(); } - void transfer_begin() { OSCSender::transfer_begin(); } - void transfer_end() { OSCSender::transfer_end(); } - - // Client registration - void register_client(ClientInterface* client); - void unregister_client(const string& uri); - - // Engine commands - void load_plugins(); - void activate(); - void deactivate(); - void quit(); - - // Object commands - - void new_patch(const string& path, - uint32_t poly); - - void new_port(const string& path, - uint32_t index, - const string& data_type, - bool is_output); - - void new_node(const string& path, - const string& plugin_uri); - - void new_node_deprecated(const string& path, - const string& plugin_type, - const string& library_name, - const string& plugin_label); - - void rename(const string& old_path, - const string& new_name); - - void destroy(const string& path); - - void clear_patch(const string& patch_path); - - void connect(const string& src_port_path, - const string& dst_port_path); - - void disconnect(const string& src_port_path, - const string& dst_port_path); - - void disconnect_all(const string& parent_patch_path, - const string& node_path); - - void set_port_value(const string& port_path, - const Raul::Atom& value); - - void set_voice_value(const string& port_path, - uint32_t voice, - const Raul::Atom& value); - - void set_program(const string& node_path, - uint32_t bank, - uint32_t program); - - void midi_learn(const string& node_path); - - void set_variable(const string& obj_path, - const string& predicate, - const Raul::Atom& value); - - void set_property(const string& obj_path, - const string& predicate, - const Raul::Atom& value); - - // Requests // - void ping(); - void request_plugin(const string& uri); - void request_object(const string& path); - void request_port_value(const string& port_path); - void request_variable(const string& path, const string& key); - void request_property(const string& path, const string& key); - void request_plugins(); - void request_all_objects(); - -protected: - const string _engine_url; - int _client_port; - int32_t _id; -}; - - -} // namespace Client -} // namespace Ingen - -#endif // OSCENGINESENDER_H - diff --git a/src/libs/client/ObjectModel.cpp b/src/libs/client/ObjectModel.cpp deleted file mode 100644 index ede5f822..00000000 --- a/src/libs/client/ObjectModel.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include "interface/GraphObject.hpp" -#include "ObjectModel.hpp" - -using namespace std; - -namespace Ingen { -namespace Client { - - -ObjectModel::ObjectModel(const Path& path) - : _path(path) -{ -} - - -ObjectModel::~ObjectModel() -{ -} - - -/** Get a variable for this object. - * - * @return Metadata value with key @a key, empty string otherwise. - */ -const Atom& -ObjectModel::get_variable(const string& key) const -{ - static const Atom null_atom; - - Variables::const_iterator i = _variables.find(key); - if (i != _variables.end()) - return i->second; - else - return null_atom; -} - - -/** Get a variable for this object. - * - * @return Metadata value with key @a key, empty string otherwise. - */ -Atom& -ObjectModel::get_variable( string& key) -{ - static Atom null_atom; - - Variables::iterator i = _variables.find(key); - if (i != _variables.end()) - return i->second; - else - return null_atom; -} - - -/** Get a property of this object. - * - * @return Metadata value with key @a key, empty string otherwise. - */ -const Atom& -ObjectModel::get_property(const string& key) const -{ - static const Atom null_atom; - - Properties::const_iterator i = _properties.find(key); - if (i != _properties.end()) - return i->second; - else - return null_atom; -} - - -/** Get a property of this object. - * - * @return Metadata value with key @a key, empty string otherwise. - */ -Atom& -ObjectModel::get_property(const string& key) -{ - static Atom null_atom; - - Properties::iterator i = _properties.find(key); - if (i != _properties.end()) - return i->second; - else - return null_atom; -} - - -bool -ObjectModel::polyphonic() const -{ - Properties::const_iterator i = _properties.find("ingen:polyphonic"); - return (i != _properties.end() && i->second.type() == Atom::BOOL && i->second.get_bool()); -} - - -/** Merge the data of @a model with self, as much as possible. - * - * This will merge the two models, but with any conflict take the value in - * @a model as correct. The paths of the two models MUST be equal. - */ -void -ObjectModel::set(SharedPtr o) -{ - assert(_path == o->path()); - if (o->_parent) - _parent = o->_parent; - - for (Variables::const_iterator v = o->variables().begin(); v != o->variables().end(); ++v) { - Variables::const_iterator mine = _variables.find(v->first); - if (mine != _variables.end()) - cerr << "WARNING: " << _path << "Client/Server variable mismatch: " << v->first << endl; - _variables[v->first] = v->second; - signal_variable.emit(v->first, v->second); - } - - for (Properties::const_iterator v = o->properties().begin(); v != o->properties().end(); ++v) { - Properties::const_iterator mine = _properties.find(v->first); - if (mine != _properties.end()) - cerr << "WARNING: " << _path << "Client/Server property mismatch: " << v->first << endl; - _properties[v->first] = v->second; - signal_variable.emit(v->first, v->second); - } -} - - -} // namespace Client -} // namespace Ingen - diff --git a/src/libs/client/ObjectModel.hpp b/src/libs/client/ObjectModel.hpp deleted file mode 100644 index 11cc87a4..00000000 --- a/src/libs/client/ObjectModel.hpp +++ /dev/null @@ -1,115 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef OBJECTMODEL_H -#define OBJECTMODEL_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "interface/GraphObject.hpp" - -using Raul::PathTable; -using std::string; -using Raul::Atom; -using Raul::Path; -using Raul::Symbol; - -namespace Ingen { -namespace Client { - -class ClientStore; - - -/** Base class for all GraphObject models (NodeModel, PatchModel, PortModel). - * - * There are no non-const public methods intentionally, models are not allowed - * to be manipulated directly by anything (but the Store) because of the - * asynchronous nature of engine control. To change something, use the - * controller (which the model probably shouldn't have a reference to but oh - * well, it reduces Collection Hell) and wait for the result (as a signal - * from this Model). - * - * \ingroup IngenClient - */ -class ObjectModel : virtual public Ingen::Shared::GraphObject -{ -public: - virtual ~ObjectModel(); - - const Atom& get_variable(const string& key) const; - Atom& get_variable( string& key); - const Atom& get_property(const string& key) const; - Atom& get_property(const string& key); - - virtual void set_variable(const string& key, const Atom& value) - { _variables[key] = value; signal_variable.emit(key, value); } - - virtual void set_property(const string& key, const Atom& value) - { _properties[key] = value; signal_property.emit(key, value); } - - const Variables& variables() const { return _variables; } - const Properties& properties() const { return _properties; } - Variables& variables() { return _variables; } - Properties& properties() { return _properties; } - const Path path() const { return _path; } - const Symbol symbol() const { return _path.name(); } - SharedPtr parent() const { return _parent; } - bool polyphonic() const; - - GraphObject* graph_parent() const { return _parent.get(); } - - // Signals - sigc::signal > signal_new_child; - sigc::signal > signal_removed_child; - sigc::signal signal_variable; - sigc::signal signal_property; - sigc::signal signal_destroyed; - sigc::signal signal_renamed; - -protected: - friend class ClientStore; - - ObjectModel(const Path& path); - - virtual void set_path(const Path& p) { _path = p; signal_renamed.emit(); } - virtual void set_parent(SharedPtr p) { assert(p); _parent = p; } - virtual void add_child(SharedPtr c) {} - virtual bool remove_child(SharedPtr c) { return true; } - - virtual void set(SharedPtr model); - - Path _path; - SharedPtr _parent; - - Variables _variables; - Properties _properties; -}; - - -} // namespace Client -} // namespace Ingen - -#endif // OBJECTMODEL_H diff --git a/src/libs/client/PatchModel.cpp b/src/libs/client/PatchModel.cpp deleted file mode 100644 index af20c9f8..00000000 --- a/src/libs/client/PatchModel.cpp +++ /dev/null @@ -1,190 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "PatchModel.hpp" -#include "NodeModel.hpp" -#include "ConnectionModel.hpp" -#include "ClientStore.hpp" -#include -#include - -using std::cerr; using std::cout; using std::endl; - -namespace Ingen { -namespace Client { - - -void -PatchModel::add_child(SharedPtr c) -{ - assert(c->parent().get() == this); - - SharedPtr pm = PtrCast(c); - if (pm) { - add_port(pm); - return; - } - - SharedPtr nm = PtrCast(c); - if (nm) - signal_new_node.emit(nm); -} - - -bool -PatchModel::remove_child(SharedPtr o) -{ - assert(o->path().is_child_of(_path)); - assert(o->parent().get() == this); - - SharedPtr pm = PtrCast(o); - if (pm) - remove_port(pm); - - // Remove any connections which referred to this object, - // since they can't possibly exist anymore - for (Connections::iterator j = _connections->begin(); j != _connections->end() ; ) { - - Connections::iterator next = j; - ++next; - - SharedPtr cm = PtrCast(*j); - assert(cm); - - if (cm->src_port_path().parent() == o->path() - || cm->src_port_path() == o->path() - || cm->dst_port_path().parent() == o->path() - || cm->dst_port_path() == o->path()) { - signal_removed_connection.emit(cm); - _connections->erase(j); // cuts our reference - assert(!get_connection(cm->src_port_path(), cm->dst_port_path())); // no duplicates - } - j = next; - } - - SharedPtr nm = PtrCast(o); - if (nm) - signal_removed_node.emit(nm); - - return true; -} - - -void -PatchModel::clear() -{ - _connections->clear(); - - NodeModel::clear(); - - assert(_connections->empty()); - assert(_ports.empty()); -} - - -SharedPtr -PatchModel::get_connection(const string& src_port_path, const string& dst_port_path) const -{ - for (Connections::const_iterator i = _connections->begin(); i != _connections->end(); ++i) - if ((*i)->src_port_path() == src_port_path && (*i)->dst_port_path() == dst_port_path) - return PtrCast(*i); - - return SharedPtr(); -} - - -/** Add a connection to this patch. - * - * A reference to @a cm is taken, released on deletion or removal. - * If @a cm only contains paths (not pointers to the actual ports), the ports - * will be found and set. The ports referred to not existing as children of - * this patch is a fatal error. - */ -void -PatchModel::add_connection(SharedPtr cm) -{ - // Store should have 'resolved' the connection already - assert(cm); - assert(cm->src_port()); - assert(cm->dst_port()); - assert(cm->src_port()->parent()); - assert(cm->dst_port()->parent()); - assert(cm->src_port_path() != cm->dst_port_path()); - assert(cm->src_port()->parent().get() == this - || cm->src_port()->parent()->parent().get() == this); - assert(cm->dst_port()->parent().get() == this - || cm->dst_port()->parent()->parent().get() == this); - - SharedPtr existing = get_connection(cm->src_port_path(), cm->dst_port_path()); - - if (existing) { - assert(cm->src_port() == existing->src_port()); - assert(cm->dst_port() == existing->dst_port()); - } else { - _connections->push_back(new Connections::Node(cm)); - signal_new_connection.emit(cm); - } -} - - -void -PatchModel::remove_connection(const string& src_port_path, const string& dst_port_path) -{ - for (Connections::iterator i = _connections->begin(); i != _connections->end(); ++i) { - SharedPtr cm = PtrCast(*i); - assert(cm); - if (cm->src_port_path() == src_port_path && cm->dst_port_path() == dst_port_path) { - signal_removed_connection.emit(cm); - delete _connections->erase(i); // cuts our reference - assert(!get_connection(src_port_path, dst_port_path)); // no duplicates - return; - } - } - - cerr << "[PatchModel::remove_connection] WARNING: Failed to find connection " << - src_port_path << " -> " << dst_port_path << endl; -} - - -bool -PatchModel::enabled() const -{ - Properties::const_iterator i = _properties.find("ingen:enabled"); - return (i != _properties.end() && i->second.type() == Atom::BOOL && i->second.get_bool()); -} - - -void -PatchModel::set_property(const string& key, const Atom& value) -{ - ObjectModel::set_property(key, value); - if (key == "ingen:polyphony") - _poly = value.get_int32(); -} - - -bool -PatchModel::polyphonic() const -{ - return (_parent) - ? (_poly > 1) && _poly == PtrCast(_parent)->poly() && _poly > 1 - : (_poly > 1); -} - - -} // namespace Client -} // namespace Ingen diff --git a/src/libs/client/PatchModel.hpp b/src/libs/client/PatchModel.hpp deleted file mode 100644 index 70c8df0e..00000000 --- a/src/libs/client/PatchModel.hpp +++ /dev/null @@ -1,105 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef PATCHMODEL_H -#define PATCHMODEL_H - -#include -#include -#include -#include -#include -#include "interface/Patch.hpp" -#include "NodeModel.hpp" - -#include "ConnectionModel.hpp" - -using std::list; using std::string; - -namespace Ingen { -namespace Client { - -class ClientStore; - - -/** Client's model of a patch. - * - * \ingroup IngenClient - */ -class PatchModel : public NodeModel, public Ingen::Shared::Patch -{ -public: - /* WARNING: Copy constructor creates a shallow copy WRT connections */ - - const Connections& connections() const { return *_connections.get(); } - - SharedPtr get_connection(const string& src_port_path, - const string& dst_port_path) const; - - uint32_t poly() const { return _poly; } - uint32_t internal_polyphony() const { return _poly; } - bool enabled() const; - bool polyphonic() const; - - /** "editable" = arranging,connecting,adding,deleting,etc - * not editable (control mode) you can just change controllers (performing) - */ - bool get_editable() const { return _editable; } - void set_editable(bool e) { if (_editable != e) { - _editable = e; - signal_editable.emit(e); - } } - - virtual void set_property(const string& key, const Atom& value); - - // Signals - sigc::signal > signal_new_node; - sigc::signal > signal_removed_node; - sigc::signal > signal_new_connection; - sigc::signal > signal_removed_connection; - sigc::signal signal_editable; - -private: - friend class ClientStore; - - PatchModel(const Path& patch_path, size_t internal_poly) - : NodeModel("ingen:Patch", patch_path) - , _connections(new Connections()) - , _poly(internal_poly) - , _editable(true) - { - } - - void clear(); - void add_child(SharedPtr c); - bool remove_child(SharedPtr c); - - void add_connection(SharedPtr cm); - void remove_connection(const string& src_port_path, const string& dst_port_path); - - SharedPtr _connections; - uint32_t _poly; - bool _editable; -}; - -typedef Table > PatchModelMap; - - -} // namespace Client -} // namespace Ingen - -#endif // PATCHMODEL_H diff --git a/src/libs/client/PluginModel.cpp b/src/libs/client/PluginModel.cpp deleted file mode 100644 index ff7e5b5c..00000000 --- a/src/libs/client/PluginModel.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include "PluginModel.hpp" -#include "PatchModel.hpp" -#include "PluginUI.hpp" - -using namespace std; -using Ingen::Shared::EngineInterface; - -namespace Ingen { -namespace Client { - -#ifdef HAVE_SLV2 -SLV2World PluginModel::_slv2_world = NULL; -SLV2Plugins PluginModel::_slv2_plugins = NULL; -#endif - -Redland::World* PluginModel::_rdf_world = NULL; - - -string -PluginModel::default_node_symbol() -{ - return Raul::Path::nameify(_symbol); -} - - -string -PluginModel::human_name() -{ -#ifdef HAVE_SLV2 - if (_slv2_plugin) { - SLV2Value name = slv2_plugin_get_name(_slv2_plugin); - string ret = slv2_value_as_string(name); - slv2_value_free(name); - return ret; - } -#endif - return default_node_symbol(); -} - - -string -PluginModel::port_human_name(uint32_t index) -{ -#ifdef HAVE_SLV2 - if (_slv2_plugin) { - Glib::Mutex::Lock lock(_rdf_world->mutex()); - SLV2Port port = slv2_plugin_get_port_by_index(_slv2_plugin, index); - SLV2Value name = slv2_port_get_name(_slv2_plugin, port); - string ret = slv2_value_as_string(name); - slv2_value_free(name); - return ret; - } -#endif - return ""; -} - - -#ifdef HAVE_SLV2 -bool -PluginModel::has_ui() const -{ - Glib::Mutex::Lock lock(_rdf_world->mutex()); - - SLV2Value gtk_gui_uri = slv2_value_new_uri(_slv2_world, - "http://lv2plug.in/ns/extensions/ui#GtkUI"); - - SLV2UIs uis = slv2_plugin_get_uis(_slv2_plugin); - - if (slv2_values_size(uis) > 0) - for (unsigned i=0; i < slv2_uis_size(uis); ++i) - if (slv2_ui_is_a(slv2_uis_get_at(uis, i), gtk_gui_uri)) - return true; - - return false; -} - - -SharedPtr -PluginModel::ui(Ingen::Shared::World* world, SharedPtr node) const -{ - if (_type != LV2) - return SharedPtr(); - - SharedPtr ret = PluginUI::create(world, node, _slv2_plugin); - return ret; -} - - -const string& -PluginModel::icon_path() const -{ - if (_icon_path == "" && _type == LV2) { - Glib::Mutex::Lock lock(_rdf_world->mutex()); - _icon_path = get_lv2_icon_path(_slv2_plugin); - } - - return _icon_path; -} - - -/** RDF world mutex must be held by the caller */ -string -PluginModel::get_lv2_icon_path(SLV2Plugin plugin) -{ - string result; - SLV2Value svg_icon_pred = slv2_value_new_uri(_slv2_world, - "http://ll-plugins.nongnu.org/lv2/namespace#svgIcon"); - - SLV2Values paths = slv2_plugin_get_value(plugin, svg_icon_pred); - - if (slv2_values_size(paths) > 0) { - SLV2Value value = slv2_values_get_at(paths, 0); - if (slv2_value_is_uri(value)) - result = slv2_uri_to_path(slv2_value_as_string(value)); - slv2_values_free(paths); - } - - slv2_value_free(svg_icon_pred); - return result; -} -#endif - -} // namespace Client -} // namespace Ingen diff --git a/src/libs/client/PluginModel.hpp b/src/libs/client/PluginModel.hpp deleted file mode 100644 index e2137e19..00000000 --- a/src/libs/client/PluginModel.hpp +++ /dev/null @@ -1,142 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef PLUGINMODEL_H -#define PLUGINMODEL_H - -#include CONFIG_H_PATH -#include -#include -#include -#include -#include -#ifdef HAVE_SLV2 -#include -#endif -#include "interface/EngineInterface.hpp" -#include "interface/Plugin.hpp" -#include "module/World.hpp" - -using std::string; - -namespace Ingen { -namespace Client { - -class PatchModel; -class NodeModel; -class PluginUI; - - -/** Model for a plugin available for loading. - * - * \ingroup IngenClient - */ -class PluginModel : public Ingen::Shared::Plugin -{ -public: - PluginModel(const string& uri, const string& type_uri, const string& symbol, const string& name) - : _type(type_from_uri(type_uri)) - , _uri(uri) - , _symbol(symbol) - , _name(name) - { -#ifdef HAVE_SLV2 - Glib::Mutex::Lock lock(_rdf_world->mutex()); - SLV2Value plugin_uri = slv2_value_new_uri(_slv2_world, uri.c_str()); - _slv2_plugin = slv2_plugins_get_by_uri(_slv2_plugins, plugin_uri); - slv2_value_free(plugin_uri); -#endif - } - - Type type() const { return _type; } - const string& uri() const { return _uri; } - const string& name() const { return _name; } - - /** DEPRECATED */ - Type type_from_string(const string& type_string) { - if (type_string == "LV2") return LV2; - else if (type_string == "LADSPA") return LADSPA; - else if (type_string == "Internal") return Internal; - else if (type_string == "Patch") return Patch; - else return Internal; // ? - } - - Type type_from_uri(const string& type_uri) { - if (type_uri.substr(0, 6) != "ingen:") { - return Plugin::Internal; // ? - } else { - return type_from_string(type_uri.substr(6)); - } - } - - string default_node_symbol(); - string human_name(); - string port_human_name(uint32_t index); - -#ifdef HAVE_SLV2 - static SLV2World slv2_world() { return _slv2_world; } - SLV2Plugin slv2_plugin() { return _slv2_plugin; } - - SLV2Port slv2_port(uint32_t index) { - Glib::Mutex::Lock lock(_rdf_world->mutex()); - return slv2_plugin_get_port_by_index(_slv2_plugin, index); - } - - static void set_slv2_world(SLV2World world) { - Glib::Mutex::Lock lock(_rdf_world->mutex()); - _slv2_world = world; - _slv2_plugins = slv2_world_get_all_plugins(_slv2_world); - } - - bool has_ui() const; - - SharedPtr ui(Ingen::Shared::World* world, - SharedPtr node) const; - - const string& icon_path() const; - static string get_lv2_icon_path(SLV2Plugin plugin); -#endif - - static void set_rdf_world(Redland::World& world) { - _rdf_world = &world; - } - - static Redland::World* rdf_world() { return _rdf_world; } - -private: - const Type _type; - const string _uri; - const string _symbol; - const string _name; - -#ifdef HAVE_SLV2 - static SLV2World _slv2_world; - static SLV2Plugins _slv2_plugins; - - SLV2Plugin _slv2_plugin; - mutable string _icon_path; -#endif - - static Redland::World* _rdf_world; -}; - - -} // namespace Client -} // namespace Ingen - -#endif // PLUGINMODEL_H - diff --git a/src/libs/client/PluginUI.cpp b/src/libs/client/PluginUI.cpp deleted file mode 100644 index 9c562135..00000000 --- a/src/libs/client/PluginUI.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include "lv2ext/lv2_event_helpers.h" -#include "shared/LV2URIMap.hpp" -#include "PluginUI.hpp" -#include "NodeModel.hpp" -#include "PortModel.hpp" - -using namespace std; -using Ingen::Shared::EngineInterface; -using Ingen::Shared::LV2URIMap; -using Ingen::Shared::LV2Features; - -namespace Ingen { -namespace Client { - -static void -lv2_ui_write(LV2UI_Controller controller, - uint32_t port_index, - uint32_t buffer_size, - uint32_t format, - const void* buffer) -{ - /* - cerr << "lv2_ui_write (format " << format << "):" << endl; - fprintf(stderr, "RAW:\n"); - for (uint32_t i=0; i < buffer_size; ++i) { - unsigned char byte = ((unsigned char*)buffer)[i]; - if (byte >= 32 && byte <= 126) - fprintf(stderr, "%c ", ((unsigned char*)buffer)[i]); - else - fprintf(stderr, "%2X ", ((unsigned char*)buffer)[i]); - } - fprintf(stderr, "\n"); - */ - - PluginUI* ui = (PluginUI*)controller; - - SharedPtr port = ui->node()->ports()[port_index]; - - const LV2Features::Feature* f = ui->world()->lv2_features->feature(LV2_URI_MAP_URI); - LV2URIMap* map = (LV2URIMap*)f->controller; - assert(map); - - // float (special case, always 0) - if (format == 0) { - assert(buffer_size == 4); - if (*(float*)buffer == port->value().get_float()) - return; // do nothing (handle stupid plugin UIs that feed back) - - ui->world()->engine->set_port_value(port->path(), Atom(*(float*)buffer)); - - // FIXME: slow, need to cache ID - } else if (format == map->uri_to_id(NULL, "http://lv2plug.in/ns/extensions/ui#Events")) { - uint32_t midi_event_type = map->uri_to_id(NULL, "http://lv2plug.in/ns/ext/midi#MidiEvent"); - LV2_Event_Buffer* buf = (LV2_Event_Buffer*)buffer; - LV2_Event_Iterator iter; - uint8_t* data; - lv2_event_begin(&iter, buf); - while (lv2_event_is_valid(&iter)) { - LV2_Event* const ev = lv2_event_get(&iter, &data); - if (ev->type == midi_event_type) { - // FIXME: bundle multiple events by writing an entire buffer here - ui->world()->engine->set_port_value(port->path(), - Atom("lv2_midi:MidiEvent", ev->size, data)); - } else { - cerr << "WARNING: Unable to send event type " << ev->type << - " over OSC, ignoring event" << endl; - } - - lv2_event_increment(&iter); - } - } else { - cerr << "WARNING: Unknown value format " << format - << ", either plugin " << ui->node()->plugin()->uri() << " is broken" - << " or this is an Ingen bug" << endl; - } -} - - -PluginUI::PluginUI(Ingen::Shared::World* world, - SharedPtr node) - : _world(world) - , _node(node) - , _instance(NULL) -{ -} - - -PluginUI::~PluginUI() -{ - Glib::Mutex::Lock lock(PluginModel::rdf_world()->mutex()); - slv2_ui_instance_free(_instance); -} - - -SharedPtr -PluginUI::create(Ingen::Shared::World* world, - SharedPtr node, - SLV2Plugin plugin) -{ - Glib::Mutex::Lock lock(PluginModel::rdf_world()->mutex()); - SharedPtr ret; - - SLV2Value gtk_gui_uri = slv2_value_new_uri(world->slv2_world, - "http://lv2plug.in/ns/extensions/ui#GtkUI"); - - SLV2UIs uis = slv2_plugin_get_uis(plugin); - SLV2UI ui = NULL; - - if (slv2_values_size(uis) > 0) { - for (unsigned i=0; i < slv2_uis_size(uis); ++i) { - SLV2UI this_ui = slv2_uis_get_at(uis, i); - if (slv2_ui_is_a(this_ui, gtk_gui_uri)) { - ui = this_ui; - break; - } - } - } - - if (ui) { - cout << "Found GTK Plugin UI: " << slv2_ui_get_uri(ui) << endl; - ret = SharedPtr(new PluginUI(world, node)); - SLV2UIInstance inst = slv2_ui_instantiate( - plugin, ui, lv2_ui_write, ret.get(), world->lv2_features->lv2_features()); - - if (inst) { - ret->set_instance(inst); - } else { - cerr << "ERROR: Failed to instantiate Plugin UI" << endl; - ret = SharedPtr(); - } - } - - slv2_value_free(gtk_gui_uri); - return ret; -} - - -} // namespace Client -} // namespace Ingen diff --git a/src/libs/client/PluginUI.hpp b/src/libs/client/PluginUI.hpp deleted file mode 100644 index d20dd16a..00000000 --- a/src/libs/client/PluginUI.hpp +++ /dev/null @@ -1,65 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef PLUGINUI_H -#define PLUGINUI_H - -#include -#include -#include "module/World.hpp" - -namespace Ingen { -namespace Shared { class EngineInterface; } -namespace Client { - -class NodeModel; - - -/** Model for a plugin available for loading. - * - * \ingroup IngenClient - */ -class PluginUI { -public: - ~PluginUI(); - - static SharedPtr create(Ingen::Shared::World* world, - SharedPtr node, - SLV2Plugin plugin); - - Ingen::Shared::World* world() const { return _world; } - SharedPtr node() const { return _node; } - SLV2UIInstance instance() const { return _instance; } - -private: - PluginUI(Ingen::Shared::World* world, - SharedPtr node); - - void set_instance(SLV2UIInstance instance) { _instance = instance; } - - Ingen::Shared::World* _world; - SharedPtr _node; - SLV2UIInstance _instance; -}; - - -} // namespace Client -} // namespace Ingen - -#endif // PLUGINUI_H - - diff --git a/src/libs/client/PortModel.cpp b/src/libs/client/PortModel.cpp deleted file mode 100644 index c18378db..00000000 --- a/src/libs/client/PortModel.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "PortModel.hpp" -#include "NodeModel.hpp" - -namespace Ingen { -namespace Client { - - -bool -PortModel::is_logarithmic() const -{ - const Atom& hint = get_variable("ingen:logarithmic"); - return (hint.is_valid() && hint.get_bool() > 0); -} - - -bool -PortModel::is_integer() const -{ - const Atom& hint = get_variable("ingen:integer"); - return (hint.is_valid() && hint.get_bool() > 0); -} - - -bool -PortModel::is_toggle() const -{ - const Atom& hint = get_variable("ingen:toggled"); - return (hint.is_valid() && hint.get_bool() > 0); -} - - -void -PortModel::set(SharedPtr model) -{ - SharedPtr port = PtrCast(model); - if (port) { - _index = port->_index; - _type = port->_type; - _direction = port->_direction; - _current_val = port->_current_val; - _connections = port->_connections; - signal_value_changed.emit(_current_val); - } - - ObjectModel::set(model); -} - -} // namespace Client -} // namespace Ingen diff --git a/src/libs/client/PortModel.hpp b/src/libs/client/PortModel.hpp deleted file mode 100644 index a7f52679..00000000 --- a/src/libs/client/PortModel.hpp +++ /dev/null @@ -1,112 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef PORTMODEL_H -#define PORTMODEL_H - -#include -#include -#include -#include -#include -#include -#include -#include "interface/Port.hpp" -#include "ObjectModel.hpp" - -using std::string; using std::vector; - -namespace Ingen { -namespace Client { - - -/** Model of a port. - * - * \ingroup IngenClient - */ -class PortModel : public ObjectModel, public Ingen::Shared::Port -{ -public: - enum Direction { INPUT, OUTPUT }; - - inline uint32_t index() const { return _index; } - inline DataType type() const { return _type; } - inline const Atom& value() const { return _current_val; } - inline bool connected() const { return (_connections > 0); } - inline bool is_input() const { return (_direction == INPUT); } - inline bool is_output() const { return (_direction == OUTPUT); } - - bool is_logarithmic() const; - bool is_integer() const; - bool is_toggle() const; - - inline bool operator==(const PortModel& pm) const { return (_path == pm._path); } - - inline void value(const Atom& val) { - if (val != _current_val) { - _current_val = val; - signal_value_changed.emit(val); - } - } - - inline void value(uint32_t voice, const Atom& val) { - // FIXME: implement properly - signal_voice_changed.emit(voice, val); - } - - // Signals - sigc::signal signal_value_changed; ///< Value ports - sigc::signal signal_voice_changed; ///< Polyphonic value ports - sigc::signal signal_activity; ///< Message ports - sigc::signal > signal_connection; - sigc::signal > signal_disconnection; - -private: - friend class ClientStore; - - PortModel(const Path& path, uint32_t index, DataType type, Direction dir) - : ObjectModel(path) - , _index(index) - , _type(type) - , _direction(dir) - , _current_val(0.0f) - , _connections(0) - { - if (_type == DataType::UNKNOWN) - std::cerr << "[PortModel] Warning: Unknown port type" << std::endl; - } - - void add_child(SharedPtr c) { throw; } - bool remove_child(SharedPtr c) { throw; } - - void connected_to(SharedPtr p) { ++_connections; signal_connection.emit(p); } - void disconnected_from(SharedPtr p) { --_connections; signal_disconnection.emit(p); } - - void set(SharedPtr model); - - uint32_t _index; - DataType _type; - Direction _direction; - Atom _current_val; - size_t _connections; -}; - - -} // namespace Client -} // namespace Ingen - -#endif // PORTMODEL_H diff --git a/src/libs/client/SigClientInterface.hpp b/src/libs/client/SigClientInterface.hpp deleted file mode 100644 index 7ab32c12..00000000 --- a/src/libs/client/SigClientInterface.hpp +++ /dev/null @@ -1,156 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef SIGCLIENTINTERFACE_H -#define SIGCLIENTINTERFACE_H - -#include -#include -#include -#include "interface/ClientInterface.hpp" -using std::string; - -namespace Ingen { -namespace Client { - - -/** A LibSigC++ signal emitting interface for clients to use. - * - * This simply emits an sigc signal for every event (eg OSC message) coming from - * the engine. Use Store (which extends this) if you want a nice client-side - * model of the engine. - * - * The signals here match the calls to ClientInterface exactly. See the - * documentation for ClientInterface for meanings of signal parameters. - */ -class SigClientInterface : public Ingen::Shared::ClientInterface, public sigc::trackable -{ -public: - SigClientInterface() : _enabled(true) {} - - bool enabled() const { return _enabled; } - - std::string uri() const { return "(internal)"; } - - // Signal parameters match up directly with ClientInterface calls - - sigc::signal signal_response_ok; - sigc::signal signal_response_error; - sigc::signal signal_bundle_begin; - sigc::signal signal_bundle_end; - sigc::signal signal_error; - sigc::signal signal_new_plugin; - sigc::signal signal_new_patch; - sigc::signal signal_new_node; - sigc::signal signal_new_port; - sigc::signal signal_patch_cleared; - sigc::signal signal_object_renamed; - sigc::signal signal_object_destroyed; - sigc::signal signal_connection; - sigc::signal signal_disconnection; - sigc::signal signal_variable_change; - sigc::signal signal_property_change; - sigc::signal signal_port_value; - sigc::signal signal_voice_value; - sigc::signal signal_port_activity; - sigc::signal signal_program_add; - sigc::signal signal_program_remove; - - /** Fire pending signals. Only does anything on derived classes (that may queue) */ - virtual bool emit_signals() { return false; } - -protected: - - bool _enabled; - - // ClientInterface hooks that fire the above signals - - void enable() { _enabled = true; } - void disable() { _enabled = false ; } - - void bundle_begin() - { if (_enabled) signal_bundle_begin.emit(); } - - void bundle_end() - { if (_enabled) signal_bundle_end.emit(); } - - void transfer_begin() {} - void transfer_end() {} - - void response_ok(int32_t id) - { if (_enabled) signal_response_ok.emit(id); } - - void response_error(int32_t id, const string& msg) - { if (_enabled) signal_response_error.emit(id, msg); } - - void error(const string& msg) - { if (_enabled) signal_error.emit(msg); } - - void new_plugin(const string& uri, const string& type_uri, const string& symbol, const string& name) - { if (_enabled) signal_new_plugin.emit(uri, type_uri, symbol, name); } - - void new_patch(const string& path, uint32_t poly) - { if (_enabled) signal_new_patch.emit(path, poly); } - - void new_node(const string& path, const string& plugin_uri) - { if (_enabled) signal_new_node.emit(path, plugin_uri); } - - void new_port(const string& path, uint32_t index, const string& data_type, bool is_output) - { if (_enabled) signal_new_port.emit(path, index, data_type, is_output); } - - void connect(const string& src_port_path, const string& dst_port_path) - { if (_enabled) signal_connection.emit(src_port_path, dst_port_path); } - - void destroy(const string& path) - { if (_enabled) signal_object_destroyed.emit(path); } - - void patch_cleared(const string& path) - { if (_enabled) signal_patch_cleared.emit(path); } - - void object_renamed(const string& old_path, const string& new_path) - { if (_enabled) signal_object_renamed.emit(old_path, new_path); } - - void disconnect(const string& src_port_path, const string& dst_port_path) - { if (_enabled) signal_disconnection.emit(src_port_path, dst_port_path); } - - void set_variable(const string& path, const string& key, const Raul::Atom& value) - { if (_enabled) signal_variable_change.emit(path, key, value); } - - void set_property(const string& path, const string& key, const Raul::Atom& value) - { if (_enabled) signal_property_change.emit(path, key, value); } - - void set_port_value(const string& port_path, const Raul::Atom& value) - { if (_enabled) signal_port_value.emit(port_path, value); } - - void set_voice_value(const string& port_path, uint32_t voice, const Raul::Atom& value) - { if (_enabled) signal_voice_value.emit(port_path, voice, value); } - - void port_activity(const string& port_path) - { if (_enabled) signal_port_activity.emit(port_path); } - - void program_add(const string& path, uint32_t bank, uint32_t program, const string& name) - { if (_enabled) signal_program_add.emit(path, bank, program, name); } - - void program_remove(const string& path, uint32_t bank, uint32_t program) - { if (_enabled) signal_program_remove.emit(path, bank, program); } -}; - - -} // namespace Client -} // namespace Ingen - -#endif diff --git a/src/libs/client/ThreadedSigClientInterface.cpp b/src/libs/client/ThreadedSigClientInterface.cpp deleted file mode 100644 index ef95133b..00000000 --- a/src/libs/client/ThreadedSigClientInterface.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "ThreadedSigClientInterface.hpp" -#include - -using namespace std; - -namespace Ingen { -namespace Client { - - -/** Push an event (from the engine, ie 'new patch') on to the queue. - */ -void -ThreadedSigClientInterface::push_sig(Closure ev) -{ - _attached = true; - if (!_enabled) - return; - - bool success = false; - while (!success) { - success = _sigs.push(ev); - if (!success) { - cerr << "WARNING: Client event queue full. Waiting..." << endl; - _mutex.lock(); - _cond.wait(_mutex); - _mutex.unlock(); - cerr << "Queue drained, continuing" << endl; - } - } -} - - -/** Process all queued events that came from the OSC thread. - * - * This function should be called from the Gtk thread to emit signals and cause - * the connected methods to execute. - */ -bool -ThreadedSigClientInterface::emit_signals() -{ - // Process a limited number of events, to prevent locking the GTK - // thread indefinitely while processing continually arriving events - - size_t num_processed = 0; - while (!_sigs.empty() && num_processed++ < (_sigs.capacity() * 3 / 4)) { - Closure& ev = _sigs.front(); - ev(); - ev.disconnect(); - _sigs.pop(); - } - - _mutex.lock(); - _cond.broadcast(); - _mutex.unlock(); - - return true; -} - - -} // namespace Client -} // namespace Ingen diff --git a/src/libs/client/ThreadedSigClientInterface.hpp b/src/libs/client/ThreadedSigClientInterface.hpp deleted file mode 100644 index 3014c139..00000000 --- a/src/libs/client/ThreadedSigClientInterface.hpp +++ /dev/null @@ -1,184 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef THREADEDSIGCLIENTINTERFACE_H -#define THREADEDSIGCLIENTINTERFACE_H - -#include -#include -#include -#include -#include "interface/ClientInterface.hpp" -#include "SigClientInterface.hpp" -#include -#include - -using std::string; - -/** Returns nothing and takes no parameters (because they have all been bound) */ -typedef sigc::slot Closure; - -namespace Ingen { -namespace Shared { class EngineInterface; } -namespace Client { - - -/** A LibSigC++ signal emitting interface for clients to use. - * - * This emits signals (possibly) in a different thread than the ClientInterface - * functions are called. It must be explicitly driven with the emit_signals() - * function, which fires all enqueued signals up until the present. You can - * use this in a GTK idle callback for receiving thread safe engine signals. - */ -class ThreadedSigClientInterface : public SigClientInterface -{ -public: - ThreadedSigClientInterface(uint32_t queue_size) - : _sigs(queue_size) - , response_ok_slot(signal_response_ok.make_slot()) - , response_error_slot(signal_response_error.make_slot()) - , error_slot(signal_error.make_slot()) - , new_plugin_slot(signal_new_plugin.make_slot()) - , new_patch_slot(signal_new_patch.make_slot()) - , new_node_slot(signal_new_node.make_slot()) - , new_port_slot(signal_new_port.make_slot()) - , connection_slot(signal_connection.make_slot()) - , patch_cleared_slot(signal_patch_cleared.make_slot()) - , object_destroyed_slot(signal_object_destroyed.make_slot()) - , object_renamed_slot(signal_object_renamed.make_slot()) - , disconnection_slot(signal_disconnection.make_slot()) - , variable_change_slot(signal_variable_change.make_slot()) - , property_change_slot(signal_property_change.make_slot()) - , port_value_slot(signal_port_value.make_slot()) - , port_activity_slot(signal_port_activity.make_slot()) - , program_add_slot(signal_program_add.make_slot()) - , program_remove_slot(signal_program_remove.make_slot()) - { - } - - virtual std::string uri() const { return "(internal)"; } - - virtual void subscribe(Shared::EngineInterface* engine) { throw; } - - bool enabled() const { return _attached; } - - void bundle_begin() - { push_sig(bundle_begin_slot); } - - void bundle_end() - { push_sig(bundle_end_slot); } - - void transfer_begin() {} - void transfer_end() {} - - void response_ok(int32_t id) - { push_sig(sigc::bind(response_ok_slot, id)); } - - void response_error(int32_t id, const string& msg) - { push_sig(sigc::bind(response_error_slot, id, msg)); } - - void error(const string& msg) - { push_sig(sigc::bind(error_slot, msg)); } - - void new_plugin(const string& uri, const string& type_uri, const string& symbol, const string& name) - { push_sig(sigc::bind(new_plugin_slot, uri, type_uri, symbol, name)); } - - void new_patch(const string& path, uint32_t poly) - { push_sig(sigc::bind(new_patch_slot, path, poly)); } - - void new_node(const string& path, const string& plugin_uri) - { push_sig(sigc::bind(new_node_slot, path, plugin_uri)); } - - void new_port(const string& path, uint32_t index, const string& data_type, bool is_output) - { push_sig(sigc::bind(new_port_slot, path, index, data_type, is_output)); } - - void connect(const string& src_port_path, const string& dst_port_path) - { push_sig(sigc::bind(connection_slot, src_port_path, dst_port_path)); } - - void destroy(const string& path) - { push_sig(sigc::bind(object_destroyed_slot, path)); } - - void patch_cleared(const string& path) - { push_sig(sigc::bind(patch_cleared_slot, path)); } - - void object_renamed(const string& old_path, const string& new_path) - { push_sig(sigc::bind(object_renamed_slot, old_path, new_path)); } - - void disconnect(const string& src_port_path, const string& dst_port_path) - { push_sig(sigc::bind(disconnection_slot, src_port_path, dst_port_path)); } - - void set_variable(const string& path, const string& key, const Raul::Atom& value) - { push_sig(sigc::bind(variable_change_slot, path, key, value)); } - - void set_property(const string& path, const string& key, const Raul::Atom& value) - { push_sig(sigc::bind(property_change_slot, path, key, value)); } - - void set_port_value(const string& port_path, const Raul::Atom& value) - { push_sig(sigc::bind(port_value_slot, port_path, value)); } - - void set_voice_value(const string& port_path, uint32_t voice, const Raul::Atom& value) - { push_sig(sigc::bind(voice_value_slot, port_path, voice, value)); } - - void port_activity(const string& port_path) - { push_sig(sigc::bind(port_activity_slot, port_path)); } - - void program_add(const string& path, uint32_t bank, uint32_t program, const string& name) - { push_sig(sigc::bind(program_add_slot, path, bank, program, name)); } - - void program_remove(const string& path, uint32_t bank, uint32_t program) - { push_sig(sigc::bind(program_remove_slot, path, bank, program)); } - - /** Process all queued events - Called from GTK thread to emit signals. */ - bool emit_signals(); - -private: - void push_sig(Closure ev); - - Glib::Mutex _mutex; - Glib::Cond _cond; - - Raul::SRSWQueue _sigs; - bool _attached; - - sigc::slot bundle_begin_slot; - sigc::slot bundle_end_slot; - sigc::slot response_ok_slot; - sigc::slot response_error_slot; - sigc::slot error_slot; - sigc::slot new_plugin_slot; - sigc::slot new_patch_slot; - sigc::slot new_node_slot; - sigc::slot new_port_slot; - sigc::slot connection_slot; - sigc::slot patch_cleared_slot; - sigc::slot object_destroyed_slot; - sigc::slot object_renamed_slot; - sigc::slot disconnection_slot; - sigc::slot variable_change_slot; - sigc::slot property_change_slot; - sigc::slot port_value_slot; - sigc::slot voice_value_slot; - sigc::slot port_activity_slot; - sigc::slot program_add_slot; - sigc::slot program_remove_slot; -}; - - -} // namespace Client -} // namespace Ingen - -#endif diff --git a/src/libs/client/client.cpp b/src/libs/client/client.cpp deleted file mode 100644 index f3d62471..00000000 --- a/src/libs/client/client.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include CONFIG_H_PATH - -#include -#include "client.hpp" -#include "OSCEngineSender.hpp" -#ifdef WITH_SOUP -#include "HTTPEngineSender.hpp" -#endif - -using namespace std; - -namespace Ingen { -namespace Client { - - -SharedPtr -new_remote_interface(const std::string& url) -{ - const string scheme = url.substr(0, url.find(":")); - cout << "SCHEME: " << scheme << endl; - if (scheme == "osc.udp" || scheme == "osc.tcp") { - OSCEngineSender* oes = new OSCEngineSender(url); - oes->attach(rand(), true); - return SharedPtr(oes); -#ifdef WITH_SOUP - } else if (scheme == "http") { - HTTPEngineSender* hes = new HTTPEngineSender(url); - hes->attach(rand(), true); - return SharedPtr(hes); -#endif - } else { - cerr << "WARNING: Unknown URI scheme '" << scheme << "'" << endl; - return SharedPtr(); - } -} - - -} // namespace Client -} // namespace Ingen - diff --git a/src/libs/client/client.hpp b/src/libs/client/client.hpp deleted file mode 100644 index 82166da5..00000000 --- a/src/libs/client/client.hpp +++ /dev/null @@ -1,43 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef INGEN_CLIENT_H -#define INGEN_CLIENT_H - -#include - -namespace Ingen { - -class Engine; - -namespace Shared { class EngineInterface; } - -namespace Client { - -extern "C" { - - SharedPtr new_remote_interface(const std::string& url); - SharedPtr new_queued_interface(SharedPtr engine); - -} - - -} // namespace Client -} // namespace Ingen - -#endif // INGEN_CLIENT_H - diff --git a/src/libs/client/wscript b/src/libs/client/wscript deleted file mode 100644 index 6b4408a7..00000000 --- a/src/libs/client/wscript +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python -import Params - -def build(bld): - obj = bld.create_obj('cpp', 'shlib') - obj.source = ''' - ClientStore.cpp - NodeModel.cpp - ObjectModel.cpp - PatchModel.cpp - PluginModel.cpp - PluginUI.cpp - PortModel.cpp - ThreadedSigClientInterface.cpp - client.cpp - ''' - - if bld.env()['HAVE_SOUP']: - obj.source += ''' - HTTPClientReceiver.cpp - HTTPEngineSender.cpp - ''' - - if bld.env()['HAVE_XML2']: - obj.source += ' DeprecatedLoader.cpp ' - - if bld.env()['HAVE_LIBLO']: - obj.source += ' OSCClientReceiver.cpp OSCEngineSender.cpp ' - - obj.includes = ['..', '../../common', '../..'] - obj.name = 'libingen_client' - obj.target = 'ingen_client' - obj.uselib = 'GLIBMM SLV2 RAUL REDLANDMM SOUP XML2 SIGCPP' - obj.vnum = '0.0.0' - diff --git a/src/libs/engine/AudioBuffer.cpp b/src/libs/engine/AudioBuffer.cpp deleted file mode 100644 index 098103c0..00000000 --- a/src/libs/engine/AudioBuffer.cpp +++ /dev/null @@ -1,299 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include "AudioBuffer.hpp" - -using namespace std; - -/* 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 Ingen { - - -AudioBuffer::AudioBuffer(size_t size) - : Buffer((size == 1) ? DataType::CONTROL : DataType::AUDIO, size) - , _data(NULL) - , _local_data(NULL) - , _size(size) - , _filled_size(0) - , _state(OK) - , _set_value(0) - , _set_time(0) -{ - assert(_size > 0); - allocate(); - assert(data()); -} - - -void -AudioBuffer::resize(size_t size) -{ - _size = size; - - Sample* const old_data = _data; - - const bool using_local_data = (_data == _local_data); - - deallocate(); - - const int ret = posix_memalign((void**)&_local_data, 16, _size * sizeof(Sample)); - if (ret != 0) { - cerr << "[Buffer] Failed to allocate buffer. Aborting." << endl; - exit(EXIT_FAILURE); - } - - assert(ret == 0); - assert(_local_data); - - if (using_local_data) - _data = _local_data; - else - _data = old_data; - - set_block(0, 0, _size-1); -} - - -/** Allocate and use a locally managed buffer (data). - */ -void -AudioBuffer::allocate() -{ - assert(!_joined_buf); - assert(_local_data == NULL); - assert(_size > 0); - - const int ret = posix_memalign((void**)&_local_data, 16, _size * sizeof(Sample)); - if (ret != 0) { - cerr << "[Buffer] Failed to allocate buffer. Aborting." << endl; - exit(EXIT_FAILURE); - } - - assert(ret == 0); - assert(_local_data); - - _data = _local_data; - - set_block(0, 0, _size-1); -} - - -/** Free locally allocated buffer. - */ -void -AudioBuffer::deallocate() -{ - assert(!_joined_buf); - free(_local_data); - _local_data = NULL; - _data = NULL; -} - - -/** Empty (ie zero) the buffer. - */ -void -AudioBuffer::clear() -{ - set_block(0, 0, _size-1); - _state = OK; - _filled_size = 0; -} - - -/** 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. - */ -void -AudioBuffer::set_value(Sample val, FrameTime cycle_start, FrameTime time) -{ - if (_size == 1) - time = cycle_start; - - FrameTime offset = time - cycle_start; - assert(offset <= _size); - - if (offset < _size) { - set_block(val, offset, _size - 1); - - if (offset > 0) - _state = HALF_SET_CYCLE_1; - } // else trigger at very end of block - - _set_time = time; - _set_value = val; -} - - -/** Set a block of buffer to @a val. - * - * @a start_sample and @a end_sample define the inclusive range to be set. - */ -void -AudioBuffer::set_block(Sample val, size_t start_offset, size_t end_offset) -{ - assert(end_offset >= start_offset); - assert(end_offset < _size); - - Sample* const buf = data(); - assert(buf); - - for (size_t i = start_offset; i <= end_offset; ++i) - buf[i] = val; -} - - -/** Scale a block of buffer by @a val. - * - * @a start_sample and @a end_sample define the inclusive range to be set. - */ -void -AudioBuffer::scale(Sample val, size_t start_sample, size_t end_sample) -{ - assert(end_sample >= start_sample); - assert(end_sample < _size); - - Sample* const buf = data(); - assert(buf); - - for (size_t i=start_sample; i <= end_sample; ++i) - buf[i] *= val; -} - - -/** 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. - */ -void -AudioBuffer::copy(const Buffer* src, size_t start_sample, size_t end_sample) -{ - assert(end_sample >= start_sample); - assert(end_sample < _size); - assert(src); - assert(src->type() == DataType::CONTROL || DataType::AUDIO); - - Sample* const buf = data(); - assert(buf); - - const Sample* const src_buf = ((AudioBuffer*)src)->data(); - assert(src_buf); - - for (size_t i=start_sample; i <= end_sample; ++i) - buf[i] = src_buf[i]; -} - - -/** 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. - */ -void -AudioBuffer::accumulate(const AudioBuffer* const src, size_t start_sample, size_t end_sample) -{ - assert(end_sample >= start_sample); - assert(end_sample < _size); - assert(src); - - Sample* const buf = data(); - assert(buf); - - const Sample* const src_buf = src->data(); - assert(src_buf); - - for (size_t i=start_sample; i <= end_sample; ++i) - buf[i] += src_buf[i]; - -} - - -/** Use another buffer's data instead of the local one. - * - * This buffer will essentially be identical to @a buf after this call. - */ -bool -AudioBuffer::join(Buffer* buf) -{ - AudioBuffer* abuf = dynamic_cast(buf); - if (!abuf) - return false; - - assert(abuf->size() >= _size); - - _joined_buf = abuf; - _filled_size = abuf->filled_size(); - - assert(_filled_size <= _size); - - return true; -} - - -void -AudioBuffer::unjoin() -{ - _joined_buf = NULL; - _data = _local_data; -} - - -void -AudioBuffer::prepare_read(FrameTime start, SampleCount nframes) -{ - // FIXME: nframes parameter doesn't actually work, - // writing starts from 0 every time - assert(_size == 1 || nframes == _size); - - switch (_state) { - case HALF_SET_CYCLE_1: - if (start > _set_time) - _state = HALF_SET_CYCLE_2; - break; - case HALF_SET_CYCLE_2: - set_block(_set_value, 0, _size-1); - _state = OK; - break; - default: - break; - } -} - - -/** Set the buffer (data) used. - * - * This is only to be used by Drivers (to provide zero-copy processing). - */ -void -AudioBuffer::set_data(Sample* buf) -{ - assert(buf); - assert(!_joined_buf); - _data = buf; -} - - -} // namespace Ingen diff --git a/src/libs/engine/AudioBuffer.hpp b/src/libs/engine/AudioBuffer.hpp deleted file mode 100644 index 513c188f..00000000 --- a/src/libs/engine/AudioBuffer.hpp +++ /dev/null @@ -1,84 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef AUDIOBUFFER_H -#define AUDIOBUFFER_H - -#include -#include -#include -#include "types.hpp" -#include "Buffer.hpp" - -namespace Ingen { - - -class AudioBuffer : public Buffer -{ -public: - AudioBuffer(size_t capacity); - - void clear(); - void set_value(Sample val, FrameTime cycle_start, FrameTime time); - void set_block(Sample val, size_t start_offset, size_t end_offset); - void scale(Sample val, size_t start_sample, size_t end_sample); - void copy(const Buffer* src, size_t start_sample, size_t end_sample); - void accumulate(const AudioBuffer* src, size_t start_sample, size_t end_sample); - - bool join(Buffer* buf); - void unjoin(); - - /** For driver use only!! */ - void set_data(Sample* data); - - inline const void* raw_data() const { return _data; } - inline void* raw_data() { return _data; } - - inline Sample* data() const { return _data; } - - inline Sample& value_at(size_t offset) const - { assert(offset < _size); return data()[offset]; } - - void prepare_read(FrameTime start, SampleCount nframes); - void prepare_write(FrameTime start, SampleCount nframes) {} - - void rewind() const {} - void resize(size_t size); - - void filled_size(size_t size) { _filled_size = size; } - size_t filled_size() const { return _filled_size; } - size_t size() const { return _size; } - -private: - enum State { OK, HALF_SET_CYCLE_1, HALF_SET_CYCLE_2 }; - - void allocate(); - void deallocate(); - - Sample* _data; ///< Used data pointer (probably same as _local_data) - Sample* _local_data; ///< Locally allocated buffer (possibly unused if joined or set_data used) - size_t _size; ///< Allocated buffer size - size_t _filled_size; ///< Usable buffer size (for MIDI ports etc) - State _state; ///< State of buffer for setting values next cycle - Sample _set_value; ///< Value set by @ref set (may need to be set next cycle) - FrameTime _set_time; ///< Time _set_value was set (to reset next cycle) -}; - - -} // namespace Ingen - -#endif // AUDIOBUFFER_H diff --git a/src/libs/engine/AudioDriver.hpp b/src/libs/engine/AudioDriver.hpp deleted file mode 100644 index 6b5ea8dc..00000000 --- a/src/libs/engine/AudioDriver.hpp +++ /dev/null @@ -1,62 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef AUDIODRIVER_H -#define AUDIODRIVER_H - -#include -#include -#include "Driver.hpp" -#include "types.hpp" -#include "interface/DataType.hpp" - -namespace Ingen { - -class PatchImpl; -class AudioDriver; -class PortImpl; -class ProcessContext; - - -/** Audio driver abstract base class. - * - * \ingroup engine - */ -class AudioDriver : public Driver -{ -public: - AudioDriver() : Driver(DataType::AUDIO) {} - - virtual void set_root_patch(PatchImpl* patch) = 0; - virtual PatchImpl* root_patch() = 0; - - virtual void add_port(DriverPort* port) = 0; - virtual DriverPort* remove_port(const Raul::Path& path) = 0; - - virtual SampleCount buffer_size() const = 0; - virtual SampleCount sample_rate() const = 0; - virtual SampleCount frame_time() const = 0; - - virtual bool is_realtime() const = 0; - - virtual ProcessContext& context() = 0; -}; - - -} // namespace Ingen - -#endif // AUDIODRIVER_H diff --git a/src/libs/engine/Buffer.cpp b/src/libs/engine/Buffer.cpp deleted file mode 100644 index d019146c..00000000 --- a/src/libs/engine/Buffer.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "AudioBuffer.hpp" -#include "EventBuffer.hpp" - -namespace Ingen { - - -Buffer* -Buffer::create(DataType type, size_t size) -{ - if (type.is_control()) - return new AudioBuffer(1); - else if (type.is_audio()) - return new AudioBuffer(size); - else if (type.is_event()) - return new EventBuffer(size); - else - throw; -} - - -} // namespace Ingen diff --git a/src/libs/engine/Buffer.hpp b/src/libs/engine/Buffer.hpp deleted file mode 100644 index e388e2e8..00000000 --- a/src/libs/engine/Buffer.hpp +++ /dev/null @@ -1,76 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef BUFFER_H -#define BUFFER_H - -#include -#include -#include -#include -#include "types.hpp" -#include "interface/DataType.hpp" - -namespace Ingen { - - -class Buffer : public boost::noncopyable, public Raul::Deletable -{ -public: - Buffer(Shared::DataType type, size_t size) - : _type(type) - , _size(size) - , _joined_buf(NULL) - {} - - static Buffer* create(Shared::DataType type, size_t size); - - /** Clear contents and reset state */ - virtual void clear() = 0; - - virtual void* raw_data() = 0; - virtual const void* raw_data() const = 0; - - /** Rewind (ie reset read pointer), but leave contents unchanged */ - virtual void rewind() const = 0; - - virtual void prepare_read(FrameTime start, SampleCount nframes) = 0; - virtual void prepare_write(FrameTime start, SampleCount nframes) = 0; - - bool is_joined() const { return (_joined_buf != NULL); } - Buffer* joined_buffer() const { return _joined_buf; } - - virtual bool join(Buffer* buf) = 0; - virtual void unjoin() = 0; - - virtual void copy(const Buffer* src, size_t start_sample, size_t end_sample) = 0; - - virtual void resize(size_t size) { _size = size; } - - Shared::DataType type() const { return _type; } - size_t size() const { return _size; } - -protected: - Shared::DataType _type; - size_t _size; - Buffer* _joined_buf; -}; - - -} // namespace Ingen - -#endif // BUFFER_H diff --git a/src/libs/engine/ClientBroadcaster.cpp b/src/libs/engine/ClientBroadcaster.cpp deleted file mode 100644 index d754f072..00000000 --- a/src/libs/engine/ClientBroadcaster.cpp +++ /dev/null @@ -1,271 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include "interface/ClientInterface.hpp" -#include "ClientBroadcaster.hpp" -#include "EngineStore.hpp" -#include "NodeFactory.hpp" -#include "util.hpp" -#include "PatchImpl.hpp" -#include "NodeImpl.hpp" -#include "PluginImpl.hpp" -#include "PortImpl.hpp" -#include "ConnectionImpl.hpp" -#include "AudioDriver.hpp" -#include "ObjectSender.hpp" -#include "OSCClientSender.hpp" - -using namespace std; -using Ingen::Shared::ClientInterface; - -namespace Ingen { - - -/** Register a client to receive messages over the notification band. - */ -void -ClientBroadcaster::register_client(const string& uri, ClientInterface* client) -{ - Clients::iterator i = _clients.find(uri); - - if (i == _clients.end()) { - _clients[uri] = client; - cout << "[ClientBroadcaster] Registered client: " << uri << endl; - } else { - cout << "[ClientBroadcaster] Client already registered: " << uri << endl; - } -} - - -/** Remove a client from the list of registered clients. - * - * @return true if client was found and removed. - */ -bool -ClientBroadcaster::unregister_client(const string& uri) -{ - size_t erased = _clients.erase(uri); - - if (erased > 0) - cout << "Unregistered client: " << uri << endl; - else - cout << "Failed to find client to unregister: " << uri << endl; - - return (erased > 0); -} - - - -/** 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). - */ -ClientInterface* -ClientBroadcaster::client(const string& uri) -{ - Clients::iterator i = _clients.find(uri); - if (i != _clients.end()) { - return (*i).second; - } else { - return NULL; - } -} - - -void -ClientBroadcaster::bundle_begin() -{ - for (Clients::const_iterator i = _clients.begin(); i != _clients.end(); ++i) - (*i).second->bundle_begin(); -} - - -void -ClientBroadcaster::bundle_end() -{ - for (Clients::const_iterator i = _clients.begin(); i != _clients.end(); ++i) - (*i).second->bundle_end(); -} - - -void -ClientBroadcaster::send_error(const string& msg) -{ - for (Clients::const_iterator i = _clients.begin(); i != _clients.end(); ++i) - (*i).second->error(msg); -} - -void -ClientBroadcaster::send_plugins_to(ClientInterface* client, const NodeFactory::Plugins& plugins) -{ - client->transfer_begin(); - - for (NodeFactory::Plugins::const_iterator i = plugins.begin(); i != plugins.end(); ++i) { - const PluginImpl* const plugin = i->second; - client->new_plugin(plugin->uri(), plugin->type_uri(), plugin->symbol(), plugin->name()); - } - - client->transfer_end(); -} - - -void -ClientBroadcaster::send_plugins(const NodeFactory::Plugins& plugins) -{ - for (Clients::const_iterator c = _clients.begin(); c != _clients.end(); ++c) - send_plugins_to((*c).second, plugins); -} - - -void -ClientBroadcaster::send_node(const NodeImpl* node, bool recursive) -{ - for (Clients::const_iterator i = _clients.begin(); i != _clients.end(); ++i) - ObjectSender::send_node((*i).second, node, recursive); -} - - -void -ClientBroadcaster::send_port(const PortImpl* port) -{ - for (Clients::const_iterator i = _clients.begin(); i != _clients.end(); ++i) - ObjectSender::send_port((*i).second, port); -} - - -void -ClientBroadcaster::send_destroyed(const string& path) -{ - assert(path != "/"); - for (Clients::const_iterator i = _clients.begin(); i != _clients.end(); ++i) - (*i).second->destroy(path); -} - - -void -ClientBroadcaster::send_patch_cleared(const string& patch_path) -{ - for (Clients::const_iterator i = _clients.begin(); i != _clients.end(); ++i) - (*i).second->patch_cleared(patch_path); -} - -void -ClientBroadcaster::send_connection(const SharedPtr c) -{ - for (Clients::const_iterator i = _clients.begin(); i != _clients.end(); ++i) - (*i).second->connect(c->src_port()->path(), c->dst_port()->path()); -} - - -void -ClientBroadcaster::send_disconnection(const string& src_port_path, const string& dst_port_path) -{ - for (Clients::const_iterator i = _clients.begin(); i != _clients.end(); ++i) - (*i).second->disconnect(src_port_path, dst_port_path); -} - - -/** Send notification of a variable update. - * - * Like control changes, does not send update to client that set the variable, if applicable. - */ -void -ClientBroadcaster::send_variable_change(const string& node_path, const string& key, const Atom& value) -{ - for (Clients::const_iterator i = _clients.begin(); i != _clients.end(); ++i) - (*i).second->set_variable(node_path, key, value); -} - - -/** Send notification of a property update. - * - * Like control changes, does not send update to client that set the property, if applicable. - */ -void -ClientBroadcaster::send_property_change(const string& node_path, const string& key, const Atom& value) -{ - for (Clients::const_iterator i = _clients.begin(); i != _clients.end(); ++i) - (*i).second->set_property(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_port_value(const string& port_path, const Raul::Atom& value) -{ - for (Clients::const_iterator i = _clients.begin(); i != _clients.end(); ++i) - (*i).second->set_port_value(port_path, value); -} - - -void -ClientBroadcaster::send_port_activity(const string& port_path) -{ - for (Clients::const_iterator i = _clients.begin(); i != _clients.end(); ++i) - (*i).second->port_activity(port_path); -} - - -void -ClientBroadcaster::send_program_add(const string& node_path, int bank, int program, const string& name) -{ - for (Clients::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 (Clients::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 PatchImpl* p, bool recursive) -{ - for (Clients::const_iterator i = _clients.begin(); i != _clients.end(); ++i) - ObjectSender::send_patch((*i).second, p, recursive); -} - - -/** Sends notification of an GraphObject's renaming - */ -void -ClientBroadcaster::send_rename(const string& old_path, const string& new_path) -{ - for (Clients::const_iterator i = _clients.begin(); i != _clients.end(); ++i) - (*i).second->object_renamed(old_path, new_path); -} - - -} // namespace Ingen diff --git a/src/libs/engine/ClientBroadcaster.hpp b/src/libs/engine/ClientBroadcaster.hpp deleted file mode 100644 index b0963610..00000000 --- a/src/libs/engine/ClientBroadcaster.hpp +++ /dev/null @@ -1,97 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef CLIENTBROADCASTER_H -#define CLIENTBROADCASTER_H - -#include -#include -#include -#include -#include -#include -#include "interface/ClientInterface.hpp" -#include "types.hpp" -#include "NodeFactory.hpp" - -using std::string; - -namespace Ingen { - -class NodeImpl; -class PortImpl; -class PluginImpl; -class PatchImpl; -class ConnectionImpl; -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 string& uri, ClientInterface* client); - bool unregister_client(const string& uri); - - ClientInterface* client(const string& uri); - - //void send_client_registration(const string& url, int client_id); - - void bundle_begin(); - void bundle_end(); - - // Error that isn't the direct result of a request - void send_error(const string& msg); - - void send_plugins(const NodeFactory::Plugins& plugin_list); - void send_patch(const PatchImpl* p, bool recursive); - void send_node(const NodeImpl* node, bool recursive); - void send_port(const PortImpl* port); - void send_destroyed(const string& path); - void send_patch_cleared(const string& patch_path); - void send_connection(const SharedPtr 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_variable_change(const string& node_path, const string& key, const Raul::Atom& value); - void send_property_change(const string& node_path, const string& key, const Raul::Atom& value); - void send_port_value(const string& port_path, const Raul::Atom& value); - void send_port_activity(const string& port_path); - 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); - - void send_plugins_to(ClientInterface*, const NodeFactory::Plugins& plugin_list); - -private: - typedef std::map Clients; - Clients _clients; -}; - - - -} // namespace Ingen - -#endif // CLIENTBROADCASTER_H - diff --git a/src/libs/engine/CompiledPatch.hpp b/src/libs/engine/CompiledPatch.hpp deleted file mode 100644 index 9ca1d6e9..00000000 --- a/src/libs/engine/CompiledPatch.hpp +++ /dev/null @@ -1,83 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef COMPILED_PATCH_HPP -#define COMPILED_PATCH_HPP - -#include -#include -#include -#include -#include - -using Raul::List; - -using namespace std; - -namespace Ingen { - - -/** All information required about a node to execute it in an audio thread. - */ -struct CompiledNode { - CompiledNode(NodeImpl* n, size_t np, List* d) - : _node(n), _n_providers(np) - { - // Copy to a vector for maximum iteration speed and cache optimization - // (Need to take a copy anyway) - - _dependants.reserve(d->size()); - for (List::iterator i = d->begin(); i != d->end(); ++i) - _dependants.push_back(*i); - } - - NodeImpl* node() const { return _node; } - size_t n_providers() const { return _n_providers; } - const vector& dependants() const { return _dependants; } - -private: - NodeImpl* _node; - size_t _n_providers; ///< Number of input ready signals to trigger run - vector _dependants; ///< Nodes this one's output ports are connected to -}; - - -/** A patch and a set of connections, "compiled" into a flat structure with - * the correct order so the audio thread(s) can execute it without - * threading problems (since the preprocessor thread fiddles with other - * things). - * - * Currently objects still have some 'heavyweight' connection state, but - * eventually this should be the only place a particular set of connections - * in a patch is stored, so various "connection presets" can be switched - * in a realtime safe way. - * - * The nodes contained here are sorted in the order they must be executed. - * The parallel processing algorithm guarantees no node will be executed - * before it's providers, using this order as well as semaphores. - */ -struct CompiledPatch : public std::vector - , public Raul::Deletable - , public boost::noncopyable { - /*CompiledPatch() : std::vector() {} - CompiledPatch(size_t reserve) : std::vector(reserve) {}*/ -}; - - -} // namespace Ingen - -#endif // COMPILED_PATCH_HPP diff --git a/src/libs/engine/ConnectionImpl.cpp b/src/libs/engine/ConnectionImpl.cpp deleted file mode 100644 index d226b4dd..00000000 --- a/src/libs/engine/ConnectionImpl.cpp +++ /dev/null @@ -1,185 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include "util.hpp" -#include "ConnectionImpl.hpp" -#include "NodeImpl.hpp" -#include "PortImpl.hpp" -#include "AudioBuffer.hpp" -#include "ProcessContext.hpp" - -#include -using namespace std; - -namespace Ingen { - - -/** Constructor for a connection from a node's output port. - * - * This handles both polyphonic and monophonic nodes, transparently to the - * user (InputPort). - */ -ConnectionImpl::ConnectionImpl(PortImpl* src_port, PortImpl* dst_port) - : _src_port(src_port) - , _dst_port(dst_port) - , _local_buffer(NULL) - , _buffer_size(dst_port->buffer_size()) - /*, _must_mix( (src_port->poly() != dst_port->poly()) - || (src_port->buffer(0)->size() < dst_port->buffer(0)->size()) )*/ - , _must_mix( (src_port->polyphonic() && (! dst_port->polyphonic())) - || (src_port->poly() != dst_port->poly() ) - || (src_port->buffer(0)->size() < dst_port->buffer(0)->size()) ) - , _pending_disconnection(false) -{ - assert(src_port); - assert(dst_port); - assert(src_port != dst_port); - assert(src_port->path() != dst_port->path()); - assert(src_port->type() == dst_port->type() - || ( (src_port->type() == DataType::CONTROL || src_port->type() == DataType::AUDIO) - && (dst_port->type() == DataType::CONTROL || dst_port->type() == DataType::AUDIO) )); - - /*assert((src_port->parent_node()->poly() == dst_port->parent_node()->poly()) - || (src_port->parent_node()->poly() == 1 || dst_port->parent_node()->poly() == 1));*/ - - if (type() == DataType::EVENT) - _must_mix = false; // FIXME: kludge - - if (_must_mix) - _local_buffer = Buffer::create(dst_port->type(), dst_port->buffer(0)->size()); - - /* FIXME: 1->1 connections with a destination with fixed buffers copies unecessarily */ - //cerr << src_port->path() << " -> " << dst_port->path() << " must mix: " << _must_mix << endl; -} - - -ConnectionImpl::~ConnectionImpl() -{ - delete _local_buffer; -} - - -void -ConnectionImpl::set_buffer_size(size_t size) -{ - if (_must_mix) { - assert(_local_buffer); - delete _local_buffer; - - _local_buffer = Buffer::create(_dst_port->type(), _dst_port->buffer(0)->size()); - } - - _buffer_size = size; -} - - -void -ConnectionImpl::prepare_poly(uint32_t poly) -{ - _src_port->prepare_poly(poly); - - if (type() == DataType::CONTROL || type() == DataType::AUDIO) - _must_mix = (poly > 1) && ( - (_src_port->poly() != _dst_port->poly()) - || (_src_port->polyphonic() && !_dst_port->polyphonic()) - || (_src_port->parent()->polyphonic() && !_dst_port->parent()->polyphonic()) ); - - /*cerr << src_port()->path() << " * " << src_port()->poly() - << " -> " << dst_port()->path() << " * " << dst_port()->poly() - << "\t\tmust mix: " << _must_mix << " at poly " << poly << endl;*/ - - if (_must_mix && ! _local_buffer) - _local_buffer = Buffer::create(_dst_port->type(), _dst_port->buffer(0)->size()); -} - - -void -ConnectionImpl::apply_poly(Raul::Maid& maid, uint32_t poly) -{ - _src_port->apply_poly(maid, poly); - if (poly == 1 && _local_buffer && !_must_mix) { - maid.push(_local_buffer); - _local_buffer = NULL; - } -} - - -void -ConnectionImpl::process(ProcessContext& context) -{ - // FIXME: nframes parameter not used - assert(_buffer_size == 1 || _buffer_size == context.nframes()); - - /* 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. */ - - /*cerr << src_port()->path() << " * " << src_port()->poly() - << " -> " << dst_port()->path() << " * " << dst_port()->poly() - << "\t\tmust mix: " << _must_mix << endl;*/ - - if (_must_mix && (type() == DataType::CONTROL || type() == DataType::AUDIO)) { - - const AudioBuffer* const src_buffer = (AudioBuffer*)src_port()->buffer(0); - AudioBuffer* mix_buf = (AudioBuffer*)_local_buffer; - - assert(mix_buf); - - const size_t copy_size = std::min(src_buffer->size(), mix_buf->size()); - - // Copy src buffer to start of mix buffer - mix_buf->copy((AudioBuffer*)src_port()->buffer(0), 0, copy_size-1); - - // Write last value of src buffer to remainder of dst buffer, if necessary - if (copy_size < mix_buf->size()) - mix_buf->set_block(src_buffer->value_at(copy_size-1), copy_size, mix_buf->size()-1); - - // Accumulate the source's voices into local buffer starting at the second - // voice (buffer is already set to first voice above) - for (uint32_t j=1; j < src_port()->poly(); ++j) { - mix_buf->accumulate((AudioBuffer*)src_port()->buffer(j), 0, copy_size-1); - } - - // Find the summed value and write it to the remainder of dst buffer - if (copy_size < mix_buf->size()) { - float src_value = src_buffer->value_at(copy_size-1); - for (uint32_t j=1; j < src_port()->poly(); ++j) - src_value += ((AudioBuffer*)src_port()->buffer(j))->value_at(copy_size-1); - - mix_buf->set_block(src_value, copy_size, mix_buf->size()-1); - } - - // Scale the buffer down. - if (src_port()->poly() > 1) - mix_buf->scale(1.0f/(float)src_port()->poly(), 0, _buffer_size-1); - - } else if (_must_mix && type() == DataType::EVENT) { - - std::cerr << "WARNING: No event mixing." << std::endl; - - } - -} - - -} // namespace Ingen - diff --git a/src/libs/engine/ConnectionImpl.hpp b/src/libs/engine/ConnectionImpl.hpp deleted file mode 100644 index b3da1b54..00000000 --- a/src/libs/engine/ConnectionImpl.hpp +++ /dev/null @@ -1,101 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef CONNECTIONIMPL_H -#define CONNECTIONIMPL_H - -#include -#include -#include -#include "interface/DataType.hpp" -#include "interface/Connection.hpp" -#include "PortImpl.hpp" -#include "types.hpp" - -namespace Ingen { - -class PortImpl; -class Buffer; - - -/** 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 ConnectionImpl : public Raul::Deletable, public Shared::Connection -{ -public: - ConnectionImpl(PortImpl* src_port, PortImpl* dst_port); - virtual ~ConnectionImpl(); - - PortImpl* src_port() const { return _src_port; } - PortImpl* dst_port() const { return _dst_port; } - - const Raul::Path src_port_path() const { return _src_port->path(); } - const Raul::Path dst_port_path() const { return _dst_port->path(); } - - /** Used by some (recursive) events to prevent double disconnections */ - bool pending_disconnection() { return _pending_disconnection; } - void pending_disconnection(bool b) { _pending_disconnection = b; } - - void process(ProcessContext& context); - - /** Get the buffer for a particular voice. - * A Connection is smart - it knows the destination port requesting the - * buffer, and will return accordingly (ie the same buffer for every voice - * in a mono->poly connection). - */ - inline Buffer* buffer(size_t voice) const; - - void set_buffer_size(size_t size); - void prepare_poly(uint32_t poly); - void apply_poly(Raul::Maid& maid, uint32_t poly); - - DataType type() const { return _src_port->type(); } - -protected: - PortImpl* const _src_port; - PortImpl* const _dst_port; - Buffer* _local_buffer; - size_t _buffer_size; - bool _must_mix; - bool _pending_disconnection; -}; - - -inline Buffer* -ConnectionImpl::buffer(size_t voice) const -{ - if (_must_mix) { - return _local_buffer; - } else if ( ! _src_port->polyphonic()) { - return _src_port->buffer(0); - } else { - return _src_port->buffer(voice); - } -} - - -} // namespace Ingen - -#endif // CONNECTIONIMPL_H diff --git a/src/libs/engine/Context.hpp b/src/libs/engine/Context.hpp deleted file mode 100644 index 2e000fb1..00000000 --- a/src/libs/engine/Context.hpp +++ /dev/null @@ -1,51 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef CONTEXT_H -#define CONTEXT_H - -namespace Ingen { - -class Engine; - -class Context -{ -public: - enum ID { - AUDIO, - MESSAGE - }; - - Context(Engine& engine, ID id) - : _id(id) - , _engine(engine) - {} - - virtual ~Context() {} - - inline Engine& engine() const { return _engine; } - -protected: - ID _id; ///< Fast ID for this context - Engine& _engine; ///< Engine we're running in -}; - - -} // namespace Ingen - -#endif // CONTEXT_H - diff --git a/src/libs/engine/Driver.hpp b/src/libs/engine/Driver.hpp deleted file mode 100644 index 590b66b5..00000000 --- a/src/libs/engine/Driver.hpp +++ /dev/null @@ -1,98 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef DRIVER_H -#define DRIVER_H - -#include -#include -#include -#include "interface/DataType.hpp" -#include "DuplexPort.hpp" - -namespace Ingen { - -class DuplexPort; - - -/** Representation of a "system" (eg outside Ingen) 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 : boost::noncopyable { -public: - virtual ~DriverPort() {} - - /** Set the name of the system port */ - virtual void set_name(const std::string& name) = 0; - - bool is_input() const { return _patch_port->is_input(); } - DuplexPort* patch_port() const { return _patch_port; } - -protected: - /** is_input from the perspective outside of ingen */ - DriverPort(DuplexPort* port) : _patch_port(port) {} - - DuplexPort* _patch_port; -}; - - -/** Driver abstract base class. - * - * A Driver is, from the perspective of GraphObjects (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. - * - * \ingroup engine - */ -class Driver : boost::noncopyable -{ -public: - Driver(DataType type) - : _type(type) - {} - - 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(DuplexPort* patch_port) = 0; - - virtual DriverPort* driver_port(const Raul::Path& path) = 0; - - virtual void add_port(DriverPort* port) = 0; - virtual DriverPort* remove_port(const Raul::Path& path) = 0; - -protected: - DataType _type; -}; - - -} // namespace Ingen - -#endif // DRIVER_H diff --git a/src/libs/engine/DuplexPort.cpp b/src/libs/engine/DuplexPort.cpp deleted file mode 100644 index 31bfbe09..00000000 --- a/src/libs/engine/DuplexPort.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include "util.hpp" -#include "DuplexPort.hpp" -#include "ConnectionImpl.hpp" -#include "OutputPort.hpp" -#include "NodeImpl.hpp" -#include "ProcessContext.hpp" -#include "EventBuffer.hpp" - -using namespace std; - -namespace Ingen { - - -DuplexPort::DuplexPort(NodeImpl* parent, const string& name, uint32_t index, uint32_t poly, DataType type, const Atom& value, size_t buffer_size, bool is_output) - : PortImpl(parent, name, index, poly, type, value, buffer_size) - , InputPort(parent, name, index, poly, type, value, buffer_size) - , OutputPort(parent, name, index, poly, type, value, buffer_size) - , _is_output(is_output) -{ - assert(PortImpl::_parent == parent); -} - - -void -DuplexPort::pre_process(ProcessContext& context) -{ - // - - /*cerr << endl << "{ duplex pre" << endl; - cerr << path() << " duplex pre: fixed buffers: " << fixed_buffers() << endl; - cerr << path() << " duplex pre: buffer: " << buffer(0) << endl; - cerr << path() << " duplex pre: is_output: " << _is_output << " { " << endl;*/ - - /*if (type() == DataType::EVENT) - for (uint32_t i=0; i < _poly; ++i) - cerr << path() << " (" << buffer(i) << ") # events: " - << ((EventBuffer*)buffer(i))->event_count() - << ", joined: " << _buffers->at(i)->is_joined() << endl;*/ - - if (_is_output) { - - for (uint32_t i=0; i < _poly; ++i) - if (!_buffers->at(i)->is_joined()) - _buffers->at(i)->prepare_write(context.start(), context.nframes()); - - } else { - - for (uint32_t i=0; i < _poly; ++i) - _buffers->at(i)->prepare_read(context.start(), context.nframes()); - - broadcast(context); - } - - //cerr << "} duplex pre " << path() << endl; - - // -} - - -void -DuplexPort::post_process(ProcessContext& context) -{ - // - - /*cerr << endl << "{ duplex post" << endl; - cerr << path() << " duplex post: fixed buffers: " << fixed_buffers() << endl; - cerr << path() << " duplex post: buffer: " << buffer(0) << endl; - cerr << path() << " duplex post: is_output: " << _is_output << " { " << endl; - - if (type() == DataType::EVENT) - for (uint32_t i=0; i < _poly; ++i) - cerr << path() << " (" << buffer(i) << ") # events: " - << ((EventBuffer*)buffer(i))->event_count() - << ", joined: " << _buffers->at(i)->is_joined() << endl;*/ - - if (_is_output) { - InputPort::pre_process(context); // Mix down inputs - broadcast(context); - } - - //cerr << "} duplex post " << path() << endl; - - // -} - - -} // namespace Ingen - diff --git a/src/libs/engine/DuplexPort.hpp b/src/libs/engine/DuplexPort.hpp deleted file mode 100644 index 43d202a9..00000000 --- a/src/libs/engine/DuplexPort.hpp +++ /dev/null @@ -1,68 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef DUPLEXPORT_H -#define DUPLEXPORT_H - -#include -#include -#include "types.hpp" -#include "Buffer.hpp" -#include "InputPort.hpp" -#include "OutputPort.hpp" - -namespace Ingen { - -class NodeImpl; - - -/** A duplex port (which is both an InputPort and an OutputPort) - * - * This is used for Patch ports, since they need to appear as both an input - * and an output port based on context. Eg. a patch output appears as an - * input inside the patch, so nodes inside the patch can feed it data. - * - * \ingroup engine - */ -class DuplexPort : public InputPort, public OutputPort -{ -public: - DuplexPort(NodeImpl* parent, - const std::string& name, - uint32_t index, - uint32_t poly, - DataType type, - const Atom& value, - size_t buffer_size, - bool is_output); - - virtual ~DuplexPort() {} - - void pre_process(ProcessContext& context); - void post_process(ProcessContext& context); - - bool is_input() const { return !_is_output; } - bool is_output() const { return _is_output; } - -protected: - bool _is_output; -}; - - -} // namespace Ingen - -#endif // DUPLEXPORT_H diff --git a/src/libs/engine/Engine.cpp b/src/libs/engine/Engine.cpp deleted file mode 100644 index a08a37ca..00000000 --- a/src/libs/engine/Engine.cpp +++ /dev/null @@ -1,317 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include "Engine.hpp" -#include CONFIG_H_PATH -#include "tuning.hpp" -#include "Event.hpp" -#include "common/interface/EventType.hpp" -#include "shared/Store.hpp" -#include "JackAudioDriver.hpp" -#include "NodeFactory.hpp" -#include "ClientBroadcaster.hpp" -#include "PatchImpl.hpp" -#include "EngineStore.hpp" -#include "MidiDriver.hpp" -#include "OSCDriver.hpp" -#include "QueuedEventSource.hpp" -#include "PostProcessor.hpp" -#include "CreatePatchEvent.hpp" -#include "EnablePatchEvent.hpp" -#include "OSCEngineReceiver.hpp" -#ifdef HAVE_SOUP -#include "HTTPEngineReceiver.hpp" -#endif -#include "PostProcessor.hpp" -#include "ProcessSlave.hpp" -#include "ProcessContext.hpp" -#include "MessageContext.hpp" -#include "ThreadManager.hpp" -#ifdef HAVE_JACK_MIDI -#include "JackMidiDriver.hpp" -#endif -using namespace std; - -namespace Ingen { - - -Engine::Engine(Ingen::Shared::World* world) - : _world(world) - , _midi_driver(NULL) - , _osc_driver(NULL) - , _maid(new Raul::Maid(maid_queue_size)) - , _post_processor(new PostProcessor(*this, /**_maid, */post_processor_queue_size)) - , _broadcaster(new ClientBroadcaster()) - , _node_factory(new NodeFactory(world)) - , _message_context(new MessageContext(*this)) - , _quit_flag(false) - , _activated(false) -{ - if (world->store) { - assert(PtrCast(world->store)); - } else { - world->store = SharedPtr(new EngineStore()); - } -} - - -Engine::~Engine() -{ - deactivate(); - - for (EngineStore::iterator i = engine_store()->begin(); - i != engine_store()->end(); ++i) { - if ( ! PtrCast(i->second)->parent() ) - i->second.reset(); - } - - delete _broadcaster; - delete _node_factory; - delete _osc_driver; - delete _post_processor; - //delete _lash_driver; - - delete _maid; - - munlockall(); -} - - -SharedPtr -Engine::engine_store() const -{ - return PtrCast(_world->store); -} - - -Driver* -Engine::driver(DataType type, EventType event_type) -{ - if (type == DataType::AUDIO) { - return _audio_driver.get(); - } else if (type == DataType::EVENT) { - if (event_type == EventType::MIDI) { - return _midi_driver; - } else if (event_type == EventType::OSC) { - return _osc_driver; - } - } - - return NULL; -} - - -int -Engine::main() -{ - Thread::get().set_context(THREAD_POST_PROCESS); - - // Loop until quit flag is set (by OSCReceiver) - while ( ! _quit_flag) { - nanosleep(&main_rate, NULL); - main_iteration(); - } - cout << "[Main] Done main loop." << endl; - - deactivate(); - - return 0; -} - - -/** Run one iteration of the main loop. - * - * NOT realtime safe (this is where deletion actually occurs) - */ -bool -Engine::main_iteration() -{ -/*#ifdef HAVE_LASH - // Process any pending LASH events - if (lash_driver->enabled()) - lash_driver->process_events(); -#endif*/ - // Run the maid (garbage collector) - _post_processor->process(); - _maid->cleanup(); - - return !_quit_flag; -} - - -void -Engine::start_jack_driver() -{ - if ( ! _audio_driver) - _audio_driver = SharedPtr(new JackAudioDriver(*this)); - else - cerr << "[Engine::start_jack_driver] Audio driver already running" << endl; -} - - -void -Engine::start_osc_driver(int port) -{ - if (_event_source) { - cerr << "WARNING: Replacing event source" << endl; - _event_source.reset(); - } - - _event_source = SharedPtr(new OSCEngineReceiver( - *this, pre_processor_queue_size, port)); -} - - -void -Engine::start_http_driver(int port) -{ -#ifdef HAVE_SOUP - // FIXE: leak - HTTPEngineReceiver* server = new HTTPEngineReceiver(*this, port); - server->activate(); -#endif -} - - -SharedPtr -Engine::new_queued_interface() -{ - if (_event_source) { - cerr << "WARNING: Replacing event source" << endl; - _event_source.reset(); - } - - SharedPtr result(new QueuedEngineInterface( - *this, Ingen::event_queue_size, Ingen::event_queue_size)); - - _event_source = result; - - return result; -} - -/* -void -Engine::set_event_source(SharedPtr source) -{ - if (_event_source) - cerr << "Warning: Dropped event source (engine interface)" << endl; - - _event_source = source; -} -*/ - - -bool -Engine::activate(size_t parallelism) -{ - if (_activated) - return false; - - assert(_audio_driver); - assert(_event_source); - -#ifdef HAVE_JACK_MIDI - _midi_driver = new JackMidiDriver(((JackAudioDriver*)_audio_driver.get())->jack_client()); -#else - _midi_driver = new DummyMidiDriver(); -#endif - - _event_source->activate(); - - // Create root patch - - PatchImpl* root_patch = new PatchImpl(*this, "", 1, NULL, - _audio_driver->sample_rate(), _audio_driver->buffer_size(), 1); - root_patch->activate(); - _world->store->add(root_patch); - root_patch->compiled_patch(root_patch->compile()); - - assert(_audio_driver->root_patch() == NULL); - _audio_driver->set_root_patch(root_patch); - - _audio_driver->activate(); - - _process_slaves.clear(); - _process_slaves.reserve(parallelism); - for (size_t i=0; i < parallelism - 1; ++i) - _process_slaves.push_back(new ProcessSlave(*this, _audio_driver->is_realtime())); - - root_patch->enable(); - - //_post_processor->start(); - - _activated = true; - - return true; -} - - -void -Engine::deactivate() -{ - if (!_activated) - return; - - _event_source->deactivate(); - - /*for (Tree::iterator i = _engine_store->objects().begin(); - i != _engine_store->objects().end(); ++i) - if ((*i)->as_node() != NULL && (*i)->as_node()->parent() == NULL) - (*i)->as_node()->deactivate();*/ - - if (_midi_driver != NULL) { - _midi_driver->deactivate(); - delete _midi_driver; - _midi_driver = NULL; - } - - _audio_driver->deactivate(); - - _audio_driver->root_patch()->deactivate(); - - for (size_t i=0; i < _process_slaves.size(); ++i) { - delete _process_slaves[i]; - } - - _process_slaves.clear(); - - // Finalize any lingering events (unlikely) - _post_processor->process(); - - _audio_driver.reset(); - _event_source.reset(); - - _activated = false; -} - - -void -Engine::process_events(ProcessContext& context) -{ - if (_event_source) - _event_source->process(*_post_processor, context); -} - - -} // namespace Ingen diff --git a/src/libs/engine/Engine.hpp b/src/libs/engine/Engine.hpp deleted file mode 100644 index fce826db..00000000 --- a/src/libs/engine/Engine.hpp +++ /dev/null @@ -1,130 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef ENGINE_H -#define ENGINE_H - -#include CONFIG_H_PATH -#include -#include -#include -#include -#include "module/global.hpp" -#include "interface/DataType.hpp" -#include "interface/EventType.hpp" - -template class Queue; - -namespace Raul { class Maid; } - -namespace Ingen { - -class AudioDriver; -class MidiDriver; -class OSCDriver; -class NodeFactory; -class ClientBroadcaster; -class EngineStore; -class EventSource; -class PostProcessor; -class Event; -class QueuedEvent; -class QueuedEngineInterface; -class Driver; -class ProcessSlave; -class ProcessContext; -class MessageContext; - - -/** The main class for the Engine. - * - * This is a (GoF) facade for the engine. Pointers to all components are - * available for more advanced control than this facade allows. - * - * \ingroup engine - */ -class Engine : boost::noncopyable -{ -public: - Engine(Ingen::Shared::World* world); - - virtual ~Engine(); - - virtual int main(); - virtual bool main_iteration(); - - /** Set the quit flag that should kill all threads and exit cleanly. - * Note that it will take some time. */ - virtual void quit() { _quit_flag = true; } - - virtual void start_jack_driver(); - virtual void start_osc_driver(int port); - virtual void start_http_driver(int port); - - virtual SharedPtr new_queued_interface(); - - virtual bool activate(size_t parallelism); - virtual void deactivate(); - - void process_events(ProcessContext& context); - - virtual bool activated() { return _activated; } - - Raul::Maid* maid() const { return _maid; } - EventSource* event_source() const { return _event_source.get(); } - AudioDriver* audio_driver() const { return _audio_driver.get(); } - MidiDriver* midi_driver() const { return _midi_driver; } - OSCDriver* osc_driver() const { return _osc_driver; } - PostProcessor* post_processor() const { return _post_processor; } - ClientBroadcaster* broadcaster() const { return _broadcaster; } - NodeFactory* node_factory() const { return _node_factory; } - MessageContext* message_context() const { return _message_context; } - - SharedPtr engine_store() const; - - /** Return the active driver for the given type */ - Driver* driver(DataType type, EventType event_type); - - Ingen::Shared::World* world() { return _world; } - - typedef std::vector ProcessSlaves; - inline const ProcessSlaves& process_slaves() const { return _process_slaves; } - inline ProcessSlaves& process_slaves() { return _process_slaves; } - -private: - ProcessSlaves _process_slaves; - - Ingen::Shared::World* _world; - - SharedPtr _event_source; - SharedPtr _audio_driver; - MidiDriver* _midi_driver; - OSCDriver* _osc_driver; - Raul::Maid* _maid; - PostProcessor* _post_processor; - ClientBroadcaster* _broadcaster; - NodeFactory* _node_factory; - MessageContext* _message_context; - - bool _quit_flag; - bool _activated; -}; - - -} // namespace Ingen - -#endif // ENGINE_H diff --git a/src/libs/engine/EngineStore.cpp b/src/libs/engine/EngineStore.cpp deleted file mode 100644 index 9fcd3806..00000000 --- a/src/libs/engine/EngineStore.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include -#include "EngineStore.hpp" -#include "PatchImpl.hpp" -#include "NodeImpl.hpp" -#include "PortImpl.hpp" -#include "ThreadManager.hpp" - -using namespace std; -using namespace Raul; - -namespace Ingen { - - -/** Find the Patch at the given path. - */ -PatchImpl* -EngineStore::find_patch(const Path& path) -{ - GraphObjectImpl* const object = find_object(path); - return dynamic_cast(object); -} - - -/** Find the Node at the given path. - */ -NodeImpl* -EngineStore::find_node(const Path& path) -{ - GraphObjectImpl* const object = find_object(path); - return dynamic_cast(object); -} - - -/** Find the Port at the given path. - */ -PortImpl* -EngineStore::find_port(const Path& path) -{ - GraphObjectImpl* const object = find_object(path); - return dynamic_cast(object); -} - - -/** Find the Object at the given path. - */ -GraphObjectImpl* -EngineStore::find_object(const Path& path) -{ - iterator i = find(path); - return ((i == end()) ? NULL : dynamic_cast(i->second.get())); -} - - -/** Add an object to the store. Not realtime safe. - */ -void -EngineStore::add(GraphObject* obj) -{ - GraphObjectImpl* o = dynamic_cast(obj); - assert(o); - - assert(ThreadManager::current_thread_id() == THREAD_PRE_PROCESS); - - Store::add(obj); -} - - -/** Add a family of objects to the store. Not realtime safe. - */ -void -EngineStore::add(const Objects& table) -{ - assert(ThreadManager::current_thread_id() == THREAD_PRE_PROCESS); - - //cerr << "[EngineStore] Adding " << o[0].second->path() << endl; - cram(table); - - /*cerr << "[EngineStore] Adding Table:" << endl; - for (const_iterator i = table.begin(); i != table.end(); ++i) { - cerr << i->first << " = " << i->second->path() << endl; - }*/ -} - - -/** Remove an object from the store. - * - * Returned is a vector containing all descendants of the object removed - * including the object itself, in lexicographically sorted order by Path. - */ -SharedPtr -EngineStore::remove(const Path& path) -{ - return remove(find(path)); -} - - -/** Remove an object from the store. - * - * Returned is a vector containing all descendants of the object removed - * including the object itself, in lexicographically sorted order by Path. - */ -SharedPtr -EngineStore::remove(iterator object) -{ - assert(ThreadManager::current_thread_id() == THREAD_PRE_PROCESS); - - if (object != end()) { - iterator descendants_end = find_descendants_end(object); - //cout << "[EngineStore] Removing " << object->first << " {" << endl; - SharedPtr removed = yank(object, descendants_end); - /*for (iterator i = removed->begin(); i != removed->end(); ++i) { - cout << "\t" << i->first << endl; - } - cout << "}" << endl;*/ - - return removed; - - } else { - cerr << "[EngineStore] WARNING: Removing " << object->first << " failed." << endl; - return SharedPtr(); - } -} - - -/** Remove all children of an object from the store. - * - * Returned is a vector containing all descendants of the object removed - * in lexicographically sorted order by Path. - */ -SharedPtr -EngineStore::remove_children(const Path& path) -{ - return remove_children(find(path)); -} - - -/** Remove all children of an object from the store. - * - * Returned is a vector containing all descendants of the object removed - * in lexicographically sorted order by Path. - */ -SharedPtr -EngineStore::remove_children(iterator object) -{ - if (object != end()) { - iterator descendants_end = find_descendants_end(object); - if (descendants_end != object) { - iterator first_child = object; - ++first_child; - return yank(first_child, descendants_end); - } - } else { - cerr << "[EngineStore] WARNING: Removing children of " << object->first << " failed." << endl; - return SharedPtr(); - } - - return SharedPtr(); -} - - -} // namespace Ingen diff --git a/src/libs/engine/EngineStore.hpp b/src/libs/engine/EngineStore.hpp deleted file mode 100644 index ad656aeb..00000000 --- a/src/libs/engine/EngineStore.hpp +++ /dev/null @@ -1,68 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef OBJECTSTORE_H -#define OBJECTSTORE_H - -#include -#include -#include -#include "shared/Store.hpp" - -using std::string; -using namespace Raul; - -namespace Ingen { - -namespace Shared { class GraphObject; } - -class PatchImpl; -class NodeImpl; -class PortImpl; -class GraphObjectImpl; - - -/** Storage for all GraphObjects (tree of GraphObject'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 GraphObject - * tree). - * - * Searching with find*() is fast (O(log(n)) binary search on contiguous - * memory) and realtime safe, but modification (add or remove) are neither. - */ -class EngineStore : public Shared::Store -{ -public: - PatchImpl* find_patch(const Path& path); - NodeImpl* find_node(const Path& path); - PortImpl* find_port(const Path& path); - GraphObjectImpl* find_object(const Path& path); - - void add(Shared::GraphObject* o); - void add(const Objects& family); - - SharedPtr remove(const Path& path); - SharedPtr remove(Objects::iterator i); - SharedPtr remove_children(const Path& path); - SharedPtr remove_children(Objects::iterator i); -}; - - -} // namespace Ingen - -#endif // OBJECTSTORE diff --git a/src/libs/engine/Event.cpp b/src/libs/engine/Event.cpp deleted file mode 100644 index 8e5c33da..00000000 --- a/src/libs/engine/Event.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "Event.hpp" -#include "ThreadManager.hpp" -#include "ProcessContext.hpp" - -namespace Ingen { - - -void -Event::execute(ProcessContext& context) -{ - assert(ThreadManager::current_thread_id() == THREAD_PROCESS); - assert(!_executed); - assert(_time <= context.end()); - - // Missed the event, jitter, damnit. - if (_time < context.start()) - _time = context.start(); - - _executed = true; -} - - -void -Event::post_process() -{ - // FIXME: Not true witn monolithic GUI/engine - //assert(ThreadManager::current_thread_id() == THREAD_POST_PROCESS); -} - - -} // namespace Ingen - diff --git a/src/libs/engine/Event.hpp b/src/libs/engine/Event.hpp deleted file mode 100644 index 5860da2a..00000000 --- a/src/libs/engine/Event.hpp +++ /dev/null @@ -1,75 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef EVENT_H -#define EVENT_H - -#include -#include -#include -#include "types.hpp" - -namespace Ingen { - -class Engine; -class Responder; -class ProcessContext; - - -/** 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 Raul::Deletable -{ -public: - virtual ~Event() {} - - /** Execute this event in the audio thread (MUST be realtime safe). */ - virtual void execute(ProcessContext& context); - - /** Perform any actions after execution (ie send replies to commands) - * (no realtime requirements). */ - virtual void post_process(); - - inline SampleCount time() const { return _time; } - -protected: - Event(Engine& engine, SharedPtr responder, FrameTime time) - : _engine(engine) - , _responder(responder) - , _time(time) - , _executed(false) - {} - - Engine& _engine; - SharedPtr _responder; - FrameTime _time; - bool _executed; -}; - - -} // namespace Ingen - -#endif // EVENT_H diff --git a/src/libs/engine/EventBuffer.cpp b/src/libs/engine/EventBuffer.cpp deleted file mode 100644 index e8584597..00000000 --- a/src/libs/engine/EventBuffer.cpp +++ /dev/null @@ -1,318 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#define __STDC_LIMIT_MACROS 1 -#include -#include -#include "EventBuffer.hpp" -#include "lv2ext/lv2_event.h" -#include "lv2ext/lv2_event_helpers.h" - -using namespace std; - -namespace Ingen { - - -/** Allocate a new event buffer. - * \a capacity is in bytes (not number of events). - */ -EventBuffer::EventBuffer(size_t capacity) - : Buffer(DataType(DataType::EVENT), capacity) - , _latest_frames(0) - , _latest_subframes(0) -{ - if (capacity > UINT32_MAX) { - cerr << "Event buffer size " << capacity << " too large, aborting." << endl; - throw std::bad_alloc(); - } - - int ret = posix_memalign((void**)&_local_buf, 16, sizeof(LV2_Event_Buffer) + capacity); - if (ret) { - cerr << "Failed to allocate event buffer. Aborting." << endl; - exit(EXIT_FAILURE); - } - - _local_buf->event_count = 0; - _local_buf->capacity = (uint32_t)capacity; - _local_buf->size = 0; - _local_buf->data = reinterpret_cast(_local_buf + 1); - _buf = _local_buf; - - reset(0); - - //cerr << "Creating MIDI Buffer " << _buf << ", capacity = " << _buf->capacity << endl; -} - - -EventBuffer::~EventBuffer() -{ - free(_local_buf); -} - - -/** Use another buffer's data instead of the local one. - * - * This buffer will essentially be identical to @a buf after this call. - */ -bool -EventBuffer::join(Buffer* buf) -{ - EventBuffer* mbuf = dynamic_cast(buf); - if (mbuf) { - _buf = mbuf->local_data(); - _joined_buf = mbuf; - _iter = mbuf->_iter; - _iter.buf = _buf; - return false; - } else { - return false; - } - - //assert(mbuf->size() == _size); - - _joined_buf = mbuf; - - return true; -} - - -void -EventBuffer::unjoin() -{ - _joined_buf = NULL; - _buf = _local_buf; - reset(_this_nframes); -} - - -void -EventBuffer::prepare_read(FrameTime start, SampleCount nframes) -{ - //cerr << "\t" << this << " prepare_read: " << event_count() << endl; - rewind(); - _this_nframes = nframes; -} - - -void -EventBuffer::prepare_write(FrameTime start, SampleCount nframes) -{ - //cerr << "\t" << this << " prepare_write: " << event_count() << endl; - reset(nframes); -} - -/** FIXME: parameters ignored */ -void -EventBuffer::copy(const Buffer* src_buf, size_t start_sample, size_t end_sample) -{ - const EventBuffer* src = dynamic_cast(src_buf); - assert(src); - assert(_buf->capacity >= src->_buf->capacity); - - clear(); - src->rewind(); - - memcpy(_buf, src->_buf, src->_buf->size); -} - - -/** Increment the read position by one event. - * - * \return true if increment was successful, or false if end of buffer reached. - */ -bool -EventBuffer::increment() const -{ - if (lv2_event_is_valid(&_iter)) { - lv2_event_increment(&_iter); - return true; - } else { - return false; - } -} - - -/** - * \return true iff the cursor is valid (ie get_event is safe) - */ -bool -EventBuffer::is_valid() const -{ - return lv2_event_is_valid(&_iter); -} - - -/** Append an event to the buffer. - * - * \a timestamp must be >= the latest event in the buffer, - * and < this_nframes() - * - * \return true on success - */ -bool -EventBuffer::append(uint32_t frames, - uint32_t subframes, - uint16_t type, - uint16_t size, - const uint8_t* data) -{ -#ifndef NDEBUG - if (lv2_event_is_valid(&_iter)) { - LV2_Event* last_event = lv2_event_get(&_iter, NULL); - assert(last_event->frames < frames - || (last_event->frames == frames && last_event->subframes <= subframes)); - } -#endif - - /*cout << "Appending event type " << type << ", size " << size - << " @ " << frames << "." << subframes << endl;*/ - - const bool ret = lv2_event_write(&_iter, frames, subframes, type, size, data); - - if (!ret) - cerr << "ERROR: Failed to write event." << endl; - - _latest_frames = frames; - _latest_subframes = subframes; - - return ret; -} - - -/** Append a buffer of events to the buffer. - * - * \a timestamp must be >= the latest event in the buffer, - * and < this_nframes() - * - * \return true on success - */ -bool -EventBuffer::append(const LV2_Event_Buffer* buf) -{ - uint8_t** data; - bool ret = true; - - LV2_Event_Iterator iter; - for (lv2_event_begin(&iter, _buf); lv2_event_is_valid(&iter); lv2_event_increment(&iter)) { - LV2_Event* ev = lv2_event_get(&iter, data); - -#ifndef NDEBUG - assert((ev->frames > _latest_frames) - || (ev->frames == _latest_frames - && ev->subframes >= _latest_subframes)); -#endif - - if (!(ret = append(ev->frames, ev->subframes, ev->type, ev->size, *data))) { - cerr << "ERROR: Failed to write event." << endl; - break; - } - - _latest_frames = ev->frames; - _latest_subframes = ev->subframes; - } - - return ret; -} - - -/** Read an event from the current position in the buffer - * - * \return true if read was successful, or false if end of buffer reached - */ -bool -EventBuffer::get_event(uint32_t* frames, - uint32_t* subframes, - uint16_t* type, - uint16_t* size, - uint8_t** data) const -{ - if (lv2_event_is_valid(&_iter)) { - LV2_Event* ev = lv2_event_get(&_iter, data); - *frames = ev->frames; - *subframes = ev->subframes; - *type = ev->type; - *size = ev->size; - return true; - } else { - return false; - } -} - - -/** Clear, and merge \a a and \a b into this buffer. - * - * FIXME: This is slow. - * - * \return true if complete merge was successful - */ -bool -EventBuffer::merge(const EventBuffer& a, const EventBuffer& b) -{ - // Die if a merge isn't necessary as it's expensive - assert(a.size() > 0 && b.size() > 0); - - reset(_this_nframes); - - a.rewind(); - b.rewind(); - -#if 0 - uint32_t a_frames; - uint32_t a_subframes; - uint16_t a_type; - uint16_t a_size; - uint8_t* a_data; - - uint32_t b_frames; - uint32_t b_subframes; - uint16_t b_type; - uint16_t b_size; - uint8_t* b_data; -#endif - - cout << "FIXME: merge" << endl; -#if 0 - a.get_event(&a_frames, &a_subframes, &a_type, &a_size, &a_data); - b.get_event(&b_frames, &b_subframes, &b_type, &b_size, &b_data); - - while (true) { - if (a_data && (!b_data || (a_time < b_time))) { - append(a_time, a_size, a_data); - if (a.increment()) - a.get_event(&a_time, &a_size, &a_data); - else - a_data = NULL; - } else if (b_data) { - append(b_time, b_size, b_data); - if (b.increment()) - b.get_event(&b_time, &b_size, &b_data); - else - b_data = NULL; - } else { - break; - } - } - - _latest_stamp = max(a_time, b_time); -#endif - - return true; -} - - -} // namespace Ingen - diff --git a/src/libs/engine/EventBuffer.hpp b/src/libs/engine/EventBuffer.hpp deleted file mode 100644 index 0c80d452..00000000 --- a/src/libs/engine/EventBuffer.hpp +++ /dev/null @@ -1,104 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef EVENTBUFFER_H -#define EVENTBUFFER_H - -#include -#include -#include -#include "Buffer.hpp" -#include "interface/DataType.hpp" - -namespace Ingen { - - -class EventBuffer : public Buffer { -public: - EventBuffer(size_t capacity); - - ~EventBuffer(); - - void prepare_read(FrameTime start, SampleCount nframes); - void prepare_write(FrameTime start, SampleCount nframes); - - bool join(Buffer* buf); - void unjoin(); - - inline uint32_t this_nframes() const { return _this_nframes; } - inline uint32_t event_count() const { return _buf->event_count; } - - inline void* raw_data() { return _buf; } - inline const void* raw_data() const { return _buf; } - - inline LV2_Event_Buffer* local_data() { return _local_buf; } - inline const LV2_Event_Buffer* local_data() const { return _local_buf; } - - inline LV2_Event_Buffer* data() { return _buf; } - inline const LV2_Event_Buffer* data() const { return _buf; } - - void copy(const Buffer* src, size_t start_sample, size_t end_sample); - - inline void rewind() const { lv2_event_begin(&_iter, _buf); } - inline void clear() { reset(_this_nframes); } - inline void reset(SampleCount nframes) { - //std::cerr << this << " reset" << std::endl; - _this_nframes = nframes; - _latest_frames = 0; - _latest_subframes = 0; - _buf->event_count = 0; - _buf->size = 0; - rewind(); - } - - bool increment() const; - bool is_valid() const; - - uint32_t latest_frames() const { return _latest_frames; } - uint32_t latest_subframes() const { return _latest_subframes; } - - bool get_event(uint32_t* frames, - uint32_t* subframes, - uint16_t* type, - uint16_t* size, - uint8_t** data) const; - - bool append(uint32_t frames, - uint32_t subframes, - uint16_t type, - uint16_t size, - const uint8_t* data); - - bool append(const LV2_Event_Buffer* buf); - - bool merge(const EventBuffer& a, const EventBuffer& b); - -private: - LV2_Event_Buffer* _buf; ///< Contents (maybe belong to _joined_buf) - LV2_Event_Buffer* _local_buf; ///< Local contents - - mutable LV2_Event_Iterator _iter; ///< Iterator into _buf - - uint32_t _latest_frames; ///< Latest time of all events (frames) - uint32_t _latest_subframes; ///< Latest time of all events (subframes) - uint32_t _this_nframes; ///< Current cycle nframes -}; - - -} // namespace Ingen - -#endif // EVENTBUFFER_H diff --git a/src/libs/engine/EventSink.cpp b/src/libs/engine/EventSink.cpp deleted file mode 100644 index 6c775d4c..00000000 --- a/src/libs/engine/EventSink.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include "events/SendPortValueEvent.hpp" -#include "EventSink.hpp" -#include "PortImpl.hpp" - -using namespace std; - -namespace Ingen { - -#if 0 -void -EventSink::control_change(Port* port, FrameTime time, float val) -{ - //cerr << "CONTROL CHANGE: " << port->path() << " == " << val << endl; - SendPortValueEvent ev(_engine, time, port, false, 0, val); - _events.write(sizeof(ev), (uchar*)&ev); -} -#endif - -/** \a size is not size_t because an event will never be even remotely close - * to UINT32_MAX in size, so uint32_t saves wasted space on 64-bit. - */ -bool -EventSink::write(uint32_t size, const Event* ev) -{ - if (size > _events.write_space()) - return false; - - _events.write(sizeof(uint32_t), (uint8_t*)&size); - _events.write(size, (uint8_t*)ev); - - return true; -} - - -/** Read the next event into event_buffer. - * - * \a event_buffer can be casted to Event* and virtual methods called. - */ -bool -EventSink::read(uint32_t event_buffer_size, uint8_t* event_buffer) -{ - uint32_t read_size; - bool success = _events.full_read(sizeof(uint32_t), (uint8_t*)&read_size); - if (!success) - return false; - - assert(read_size <= event_buffer_size); - - if (read_size > 0) - return _events.full_read(read_size, event_buffer); - else - return false; -} - - -} // namespace Ingen diff --git a/src/libs/engine/EventSink.hpp b/src/libs/engine/EventSink.hpp deleted file mode 100644 index 9e937d1a..00000000 --- a/src/libs/engine/EventSink.hpp +++ /dev/null @@ -1,64 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef EVENTSINK_H -#define EVENTSINK_H - -#include -#include -#include -#include "events/SendPortValueEvent.hpp" -#include "types.hpp" - -namespace Ingen { - -class PortImpl; -class Engine; -class SendPortValueEvent; - - -/** Sink for events generated in the audio thread. - * - * Implemented as a flat ringbuffer of events, which are constructed directly - * in the ringbuffer rather than allocated on the heap (in order to make - * writing realtime safe). - * - * \ingroup engine - */ -class EventSink -{ -public: - EventSink(Engine& engine, size_t capacity) : _engine(engine), _events(capacity) {} - - /* FIXME: Figure out variable sized event queues and make this a generic - * interface (ie don't add a method for every event type, crap..) */ - - bool write(uint32_t size, const Event* ev); - - bool read(uint32_t event_buffer_size, uint8_t* event_buffer); - -private: - Engine& _engine; - Raul::RingBuffer _events; -}; - - - -} // namespace Ingen - -#endif // EVENTSINK_H - diff --git a/src/libs/engine/EventSource.hpp b/src/libs/engine/EventSource.hpp deleted file mode 100644 index 68532d16..00000000 --- a/src/libs/engine/EventSource.hpp +++ /dev/null @@ -1,60 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef EVENTSOURCE_H -#define EVENTSOURCE_H - -#include "types.hpp" - -namespace Ingen { - -class Event; -class QueuedEvent; -class PostProcessor; - - -/** 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). - * - * There are two distinct classes of events - "queued" and "stamped". Queued - * events are events that require non-realtime pre-processing before being - * executed in the process thread. Stamped events are timestamped realtime - * events that require no pre-processing and can be executed immediately - * (with sample accuracy). - */ -class EventSource -{ -public: - virtual ~EventSource() {} - - virtual void activate() = 0; - virtual void deactivate() = 0; - - virtual void process(PostProcessor& dest, ProcessContext& context) = 0; - -protected: - size_t _capacity; -}; - - -} // namespace Ingen - -#endif // EVENTSOURCE_H - diff --git a/src/libs/engine/GraphObjectImpl.cpp b/src/libs/engine/GraphObjectImpl.cpp deleted file mode 100644 index 0e1abc57..00000000 --- a/src/libs/engine/GraphObjectImpl.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "GraphObjectImpl.hpp" -#include "PatchImpl.hpp" -#include "EngineStore.hpp" - -namespace Ingen { - - -PatchImpl* -GraphObjectImpl::parent_patch() const -{ - return dynamic_cast((NodeImpl*)_parent); -} - - -SharedPtr -GraphObjectImpl::find_child(const string& name) const -{ - throw; -} - - -} // namespace Ingen diff --git a/src/libs/engine/GraphObjectImpl.hpp b/src/libs/engine/GraphObjectImpl.hpp deleted file mode 100644 index 9b1f675d..00000000 --- a/src/libs/engine/GraphObjectImpl.hpp +++ /dev/null @@ -1,132 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef GRAPHOBJECTIMPL_H -#define GRAPHOBJECTIMPL_H - -#include -#include -#include -#include -#include -#include -#include -#include "interface/GraphObject.hpp" -#include "types.hpp" - -using Raul::Atom; -using Raul::Path; -using Raul::Symbol; - -namespace Raul { class Maid; } - -namespace Ingen { - -class PatchImpl; -class ProcessContext; - - -/** An object on the audio graph - Patch, Node, Port, etc. - * - * Each of these is a Raul::Deletable and so can be deleted in a realtime safe - * way from anywhere, and they all have a map of variable for clients to store - * arbitrary values in (which the engine puts no significance to whatsoever). - * - * \ingroup engine - */ -class GraphObjectImpl : virtual public Ingen::Shared::GraphObject -{ -public: - virtual ~GraphObjectImpl() {} - - bool polyphonic() const { return _polyphonic; } - virtual bool set_polyphonic(Raul::Maid& maid, bool p) { _polyphonic = p; return true; } - - GraphObject* graph_parent() const { return _parent; } - - inline GraphObjectImpl* parent() const { return _parent; } - const Symbol symbol() const { return _name; } - - virtual void process(ProcessContext& context) = 0; - - /** Rename */ - virtual void set_path(const Path& new_path) { - assert(new_path.parent() == path().parent()); - _name = new_path.name(); - assert(_name.find("/") == std::string::npos); - } - - void set_variable(const std::string& key, const Atom& value) - { _variables[key] = value; } - - void set_property(const std::string& key, const Atom& value) - { _properties[key] = value; } - - const Atom& get_variable(const std::string& key) { - static Atom null_atom; - Variables::iterator i = _variables.find(key); - return (i != _variables.end()) ? (*i).second : null_atom; - } - - const Atom& get_property(const std::string& key) { - static Atom null_atom; - Properties::iterator i = _properties.find(key); - return (i != _properties.end()) ? (*i).second : null_atom; - } - - const Variables& variables() const { return _variables; } - const Properties& properties() const { return _properties; } - Variables& variables() { return _variables; } - Properties& properties() { return _properties; } - - /** The Patch this object is a child of. */ - virtual PatchImpl* parent_patch() const; - - /** Path is dynamically generated from parent to ease renaming */ - const Path path() const { - if (_parent == NULL) - return Path(std::string("/").append(_name)); - else if (_parent->path() == "/") - return Path(std::string("/").append(_name)); - else - return Path(_parent->path() +"/"+ _name); - } - - SharedPtr find_child(const std::string& name) const; - -protected: - GraphObjectImpl(GraphObjectImpl* parent, const std::string& name, bool polyphonic=false) - : _parent(parent), _name(name), _polyphonic(polyphonic) - { - assert(parent == NULL || _name.length() > 0); - assert(_name.find("/") == std::string::npos); - assert(path().find("//") == std::string::npos); - } - - GraphObjectImpl* _parent; - std::string _name; - bool _polyphonic; - -private: - Variables _variables; - Properties _properties; -}; - - -} // namespace Ingen - -#endif // GRAPHOBJECTIMPL_H diff --git a/src/libs/engine/HTTPEngineReceiver.cpp b/src/libs/engine/HTTPEngineReceiver.cpp deleted file mode 100644 index 1b21e184..00000000 --- a/src/libs/engine/HTTPEngineReceiver.cpp +++ /dev/null @@ -1,207 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include -#include "types.hpp" -#include -#include -#include "interface/ClientInterface.hpp" -#include "module/Module.hpp" -#include "serialisation/serialisation.hpp" -#include "serialisation/Serialiser.hpp" -#include "serialisation/Parser.hpp" -#include "engine/ThreadManager.hpp" -#include "HTTPEngineReceiver.hpp" -#include "QueuedEventSource.hpp" -#include "ClientBroadcaster.hpp" -#include "EngineStore.hpp" - -using namespace std; -using namespace Ingen::Shared; - -namespace Ingen { - - -HTTPEngineReceiver::HTTPEngineReceiver(Engine& engine, uint16_t port) - : QueuedEngineInterface(engine, 2, 2) - , _server(soup_server_new(SOUP_SERVER_PORT, port, NULL)) -{ - _receive_thread = new ReceiveThread(*this); - - soup_server_add_handler(_server, NULL, message_callback, this, NULL); - - cout << "Started HTTP server on port " << soup_server_get_port(_server) << endl; - Thread::set_name("HTTP receiver"); - - if (!engine.world()->serialisation_module) - engine.world()->serialisation_module = Ingen::Shared::load_module("ingen_serialisation"); - - if (engine.world()->serialisation_module) { - if (!engine.world()->serialiser) - engine.world()->serialiser = SharedPtr( - Ingen::Serialisation::new_serialiser(engine.world(), engine.engine_store())); - - if (!engine.world()->parser) - engine.world()->parser = SharedPtr( - Ingen::Serialisation::new_parser()); - } else { - cerr << "WARNING: Failed to load ingen_serialisation module, HTTP disabled." << endl; - } -} - - -HTTPEngineReceiver::~HTTPEngineReceiver() -{ - deactivate(); - - if (_server != NULL) { - soup_server_quit(_server); - _server = NULL; - } -} - - -void -HTTPEngineReceiver::activate() -{ - QueuedEventSource::activate(); - _receive_thread->set_name("HTTP Receiver"); - _receive_thread->start(); -} - - -void -HTTPEngineReceiver::deactivate() -{ - cout << "[HTTPEngineReceiver] Stopped HTTP listening thread" << endl; - _receive_thread->stop(); - QueuedEventSource::deactivate(); -} - - -void -HTTPEngineReceiver::message_callback(SoupServer* server, SoupMessage* msg, const char* path, - GHashTable *query, SoupClientContext* client, void* data) -{ - HTTPEngineReceiver* me = (HTTPEngineReceiver*)data; - - SharedPtr store = me->_engine.world()->store; - if (!store) { - soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); - return; - } - - if (!Path::is_valid(path)) { - soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST); - const string& err = (boost::format("Bad path: %1%") % path).str(); - soup_message_set_response (msg, "text/plain", SOUP_MEMORY_COPY, - err.c_str(), err.length()); - return; - } - - if (msg->method == SOUP_METHOD_GET) { - Glib::RWLock::ReaderLock lock(store->lock()); - - // Find object - Store::const_iterator start = store->find(path); - if (start == store->end()) { - soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND); - const string& err = (boost::format("No such object: %1%") % path).str(); - soup_message_set_response (msg, "text/plain", SOUP_MEMORY_COPY, - err.c_str(), err.length()); - return; - } - - // Get serialiser - SharedPtr serialiser = me->_engine.world()->serialiser; - if (!serialiser) { - soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); - soup_message_set_response (msg, "text/plain", SOUP_MEMORY_STATIC, - "No serialiser available\n", 24); - return; - } - -#if 0 - SoupMessageHeaders* in_head = msg->request_headers; - const char* str = soup_message_headers_get(in_head, "Accept"); - cout << "Accept: " << str << endl; -#endif - - // Serialise object - const string response = serialiser->to_string(start->second, - "http://example.org", GraphObject::Variables()); - -#if 0 - FILE* xhtml_file = fopen("/home/dave/ingen_ui.xhtml", "r"); - string response; - while (!feof(xhtml_file)) { - int c = fgetc(xhtml_file); - if (c != EOF) - response += (char)c; - } - fclose(xhtml_file); -#endif - - soup_message_set_status (msg, SOUP_STATUS_OK); - soup_message_set_response (msg, "text/plain", SOUP_MEMORY_COPY, - response.c_str(), response.length()); - - } else if (msg->method == SOUP_METHOD_PUT) { - Glib::RWLock::WriterLock lock(store->lock()); - - // Be sure object doesn't exist - Store::const_iterator start = store->find(path); - if (start != store->end()) { - soup_message_set_status (msg, SOUP_STATUS_CONFLICT); - return; - } - - // Get parser - SharedPtr parser = me->_engine.world()->parser; - if (!parser) { - soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); - return; - } - - //cout << "POST: " << msg->request_body->data << endl; - - // Load object - soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED); - } else if (msg->method == SOUP_METHOD_POST) { - //cout << "PUT: " << msg->request_body->data << endl; - soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED); - } else { - soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED); - } -} - - -/** Override the semaphore driven _run method of QueuedEngineInterface - * to wait on HTTP requests and process them immediately in this thread. - */ -void -HTTPEngineReceiver::ReceiveThread::_run() -{ - soup_server_run(_receiver._server); -} - - -} // namespace Ingen diff --git a/src/libs/engine/HTTPEngineReceiver.hpp b/src/libs/engine/HTTPEngineReceiver.hpp deleted file mode 100644 index 34c425b2..00000000 --- a/src/libs/engine/HTTPEngineReceiver.hpp +++ /dev/null @@ -1,59 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2008 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef HTTPENGINERECEIVER_H -#define HTTPENGINERECEIVER_H - -#include CONFIG_H_PATH -#include -#include -#include -#include -#include "QueuedEngineInterface.hpp" - -namespace Ingen { - -class HTTPEngineReceiver : public QueuedEngineInterface -{ -public: - HTTPEngineReceiver(Engine& engine, uint16_t port); - ~HTTPEngineReceiver(); - - void activate(); - void deactivate(); - -private: - struct ReceiveThread : public Raul::Thread { - ReceiveThread(HTTPEngineReceiver& receiver) : _receiver(receiver) {} - virtual void _run(); - private: - HTTPEngineReceiver& _receiver; - }; - - friend class ReceiveThread; - - static void message_callback(SoupServer* server, SoupMessage* msg, const char* path, - GHashTable *query, SoupClientContext* client, void* data); - - ReceiveThread* _receive_thread; - SoupServer* _server; -}; - - -} // namespace Ingen - -#endif // HTTPENGINERECEIVER_H diff --git a/src/libs/engine/InputPort.cpp b/src/libs/engine/InputPort.cpp deleted file mode 100644 index cf5501ca..00000000 --- a/src/libs/engine/InputPort.cpp +++ /dev/null @@ -1,297 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "InputPort.hpp" -#include -#include -#include -#include "AudioBuffer.hpp" -#include "EventBuffer.hpp" -#include "ConnectionImpl.hpp" -#include "OutputPort.hpp" -#include "NodeImpl.hpp" -#include "ProcessContext.hpp" -#include "util.hpp" - -using namespace std; - -namespace Ingen { - - -InputPort::InputPort(NodeImpl* parent, - const string& name, - uint32_t index, - uint32_t poly, - DataType type, - const Atom& value, - size_t buffer_size) - : PortImpl(parent, name, index, poly, type, value, buffer_size) -{ -} - - -void -InputPort::set_buffer_size(size_t size) -{ - PortImpl::set_buffer_size(size); - assert(_buffer_size = size); - - for (Connections::iterator c = _connections.begin(); c != _connections.end(); ++c) - ((ConnectionImpl*)c->get())->set_buffer_size(size); - -} - - -bool -InputPort::prepare_poly(uint32_t poly) -{ - PortImpl::prepare_poly(poly); - - for (Connections::iterator c = _connections.begin(); c != _connections.end(); ++c) - ((ConnectionImpl*)c->get())->prepare_poly(poly); - - connect_buffers(); - return true; -} - - -bool -InputPort::apply_poly(Raul::Maid& maid, uint32_t poly) -{ - if (!_polyphonic || !_parent->polyphonic()) - return true; - - for (Connections::iterator c = _connections.begin(); c != _connections.end(); ++c) - ((ConnectionImpl*)c->get())->apply_poly(maid, poly); - - PortImpl::apply_poly(maid, poly); - assert(this->poly() == poly); - - if (_connections.size() == 1) { - ConnectionImpl* c = _connections.begin()->get(); - for (uint32_t i=0; i < _poly; ++i) - _buffers->at(i)->join(c->buffer(i)); - } - - connect_buffers(); - - return true; -} - - -/** 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. - */ -void -InputPort::add_connection(Connections::Node* const c) -{ - _connections.push_back(c); - - bool modify_buffers = !_fixed_buffers; - - if (modify_buffers) { - if (_connections.size() == 1) { - // Use buffer directly to avoid copying - for (uint32_t i=0; i < _poly; ++i) { - _buffers->at(i)->join(c->elem()->buffer(i)); - } - } else if (_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 (uint32_t i=0; i < _poly; ++i) { - _buffers->at(i)->unjoin(); - } - } - PortImpl::connect_buffers(); - } - - // Automatically broadcast connected control inputs - if (_type == DataType::CONTROL) - _broadcast = true; -} - - -/** Remove a connection. Realtime safe. - */ -InputPort::Connections::Node* -InputPort::remove_connection(const OutputPort* src_port) -{ - bool modify_buffers = !_fixed_buffers; - - bool found = false; - Connections::Node* connection = NULL; - for (Connections::iterator i = _connections.begin(); i != _connections.end(); ++i) { - if ((*i)->src_port()->path() == src_port->path()) { - connection = _connections.erase(i); - found = true; - } - } - - if ( ! found) { - cerr << "WARNING: [InputPort::remove_connection] Connection not found !" << endl; - exit(EXIT_FAILURE); - } else { - if (_connections.size() == 0) { - for (uint32_t i=0; i < _poly; ++i) { - // Use a local buffer - if (modify_buffers) - _buffers->at(i)->unjoin(); - _buffers->at(i)->clear(); // Write silence - } - } else if (modify_buffers && _connections.size() == 1) { - // Share a buffer - for (uint32_t i=0; i < _poly; ++i) { - _buffers->at(i)->join((*_connections.begin())->buffer(i)); - } - } - } - - if (modify_buffers) - PortImpl::connect_buffers(); - - // Turn off broadcasting if we're not connected any more (FIXME: not quite right..) - if (_type == DataType::CONTROL && _connections.size() == 0) - _broadcast = false; - - return connection; -} - - -/** Returns whether this port is connected to the passed port. - */ -/*bool -InputPort::is_connected_to(const OutputPort* port) const -{ - for (Connections::const_iterator i = _connections.begin(); i != _connections.end(); ++i) - if ((*i)->src_port() == port) - return true; - - return false; -}*/ - - -/** Prepare buffer for access, mixing if necessary. Realtime safe. - * FIXME: nframes parameter not used, - */ -void -InputPort::pre_process(ProcessContext& context) -{ - // If value has been set (e.g. events pushed) by the user, - // don't do anything this cycle to avoid smashing the value - if (_set_by_user) - return; - - bool do_mixdown = true; - - if (_connections.size() == 0) { - for (uint32_t i=0; i < _poly; ++i) - buffer(i)->prepare_read(context.start(), context.nframes()); - return; - } - - for (Connections::iterator c = _connections.begin(); c != _connections.end(); ++c) - (*c)->process(context); - - if ( ! _fixed_buffers) { - // If only one connection, try to use buffer directly (zero copy) - if (_connections.size() == 1) { - for (uint32_t i=0; i < _poly; ++i) { - //cerr << path() << " joining to " << (*_connections.begin())->buffer(i) << endl; - _buffers->at(i)->join((*_connections.begin())->buffer(i)); - } - do_mixdown = false; - } - connect_buffers(); - } else { - do_mixdown = true; - } - - for (uint32_t i=0; i < _poly; ++i) - buffer(i)->prepare_read(context.start(), context.nframes()); - - /*cerr << path() << " poly = " << _poly << ", mixdown: " << do_mixdown - << ", fixed buffers: " << _fixed_buffers << ", joined: " << _buffers->at(0)->is_joined() - << " to " << _buffers->at(0)->joined_buffer() << endl;*/ - - /*if (type() == DataType::EVENT) - for (uint32_t i=0; i < _poly; ++i) - cerr << path() << " (" << buffer(i) << ") # events: " - << ((EventBuffer*)buffer(i))->event_count() - << ", joined: " << _buffers->at(i)->is_joined() << endl;*/ - - if (!do_mixdown) { - /*#ifndef NDEBUG - for (uint32_t i=0; i < _poly; ++i) - assert(buffer(i) == (*_connections.begin())->buffer(i)); - #endif*/ - return; - } - - if (_type == DataType::CONTROL || _type == DataType::AUDIO) { - for (uint32_t voice=0; voice < _poly; ++voice) { - // Copy first connection - buffer(voice)->copy( - (*_connections.begin())->buffer(voice), 0, _buffer_size-1); - - // Accumulate the rest - if (_connections.size() > 1) { - - Connections::iterator c = _connections.begin(); - - for (++c; c != _connections.end(); ++c) - ((AudioBuffer*)buffer(voice))->accumulate( - ((AudioBuffer*)(*c)->buffer(voice)), 0, _buffer_size-1); - } - } - } else { - assert(_poly == 1); - - // FIXME - if (_connections.size() > 1) - cerr << "WARNING: MIDI mixing not implemented, only first connection used." << endl; - - // Copy first connection - _buffers->at(0)->copy( - (*_connections.begin())->buffer(0), 0, _buffer_size-1); - } -} - - -void -InputPort::post_process(ProcessContext& context) -{ - broadcast(context); - - // Prepare for next cycle - for (uint32_t i=0; i < _poly; ++i) - buffer(i)->prepare_write(context.start(), context.nframes()); - - _set_by_user = false; - - /*if (_broadcast && (_type == DataType::CONTROL)) { - const Sample value = ((AudioBuffer*)(*_buffers)[0])->value_at(0); - - cerr << path() << " input post: buffer: " << buffer(0) << " value = " - << value << " (last " << _last_broadcasted_value << ")" < - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef INPUTPORT_H -#define INPUTPORT_H - -#include -#include -#include -#include -#include -#include "PortImpl.hpp" -using std::string; - -namespace Ingen { - -class ConnectionImpl; -class OutputPort; -class NodeImpl; - - -/** 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 - */ -class InputPort : virtual public PortImpl -{ -public: - InputPort(NodeImpl* parent, - const string& name, - uint32_t index, - uint32_t poly, - DataType type, - const Atom& value, - size_t buffer_size); - - virtual ~InputPort() {} - - typedef Raul::List< SharedPtr > Connections; - - void add_connection(Connections::Node* c); - Connections::Node* remove_connection(const OutputPort* src_port); - - const Connections& connections() { return _connections; } - - bool prepare_poly(uint32_t poly); - bool apply_poly(Raul::Maid& maid, uint32_t poly); - - void pre_process(ProcessContext& context); - void post_process(ProcessContext& context); - - bool is_connected() const { return (_connections.size() > 0); } - //bool is_connected_to(const OutputPort* port) const; - - bool is_input() const { return true; } - bool is_output() const { return false; } - - virtual void set_buffer_size(size_t size); - -private: - Connections _connections; -}; - - -} // namespace Ingen - -#endif // INPUTPORT_H diff --git a/src/libs/engine/InternalPlugin.cpp b/src/libs/engine/InternalPlugin.cpp deleted file mode 100644 index 1c6a92a5..00000000 --- a/src/libs/engine/InternalPlugin.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include "InternalPlugin.hpp" -#include "MidiNoteNode.hpp" -#include "MidiTriggerNode.hpp" -#include "MidiControlNode.hpp" -#include "TransportNode.hpp" -#include "Engine.hpp" -#include "AudioDriver.hpp" - -namespace Ingen { - - -NodeImpl* -InternalPlugin::instantiate(const string& name, - bool polyphonic, - Ingen::PatchImpl* parent, - Engine& engine) -{ - assert(_type == Internal); - - SampleCount srate = engine.audio_driver()->sample_rate(); - SampleCount buffer_size = engine.audio_driver()->buffer_size(); - - if (_uri == NS_INGEN "note_node") { - return new MidiNoteNode(name, polyphonic, parent, srate, buffer_size); - } else if (_uri == NS_INGEN "trigger_node") { - return new MidiTriggerNode(name, polyphonic, parent, srate, buffer_size); - } else if (_uri == NS_INGEN "control_node") { - return new MidiControlNode(name, polyphonic, parent, srate, buffer_size); - } else if (_uri == NS_INGEN "transport_node") { - return new TransportNode(name, polyphonic, parent, srate, buffer_size); - } else { - return NULL; - } -} - - -} // namespace Ingen diff --git a/src/libs/engine/InternalPlugin.hpp b/src/libs/engine/InternalPlugin.hpp deleted file mode 100644 index c04c9015..00000000 --- a/src/libs/engine/InternalPlugin.hpp +++ /dev/null @@ -1,74 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef INTERNALPLUGIN_H -#define INTERNALPLUGIN_H - -#include CONFIG_H_PATH - -#ifndef HAVE_SLV2 -#error "This file requires SLV2, but HAVE_SLV2 is not defined. Please report." -#endif - -#include -#include -#include -#include -#include -#include -#include -#include "types.hpp" -#include "PluginImpl.hpp" - -#define NS_INGEN "http://drobilla.net/ns/ingen#" - -namespace Ingen { - -class NodeImpl; - - -/** Implementation of an Internal plugin. - */ -class InternalPlugin : public PluginImpl -{ -public: - InternalPlugin(const std::string& uri, - const std::string& symbol, - const std::string& name) - : PluginImpl(Plugin::Internal, uri) - , _symbol(symbol) - , _name(name) - {} - - NodeImpl* instantiate(const std::string& name, - bool polyphonic, - Ingen::PatchImpl* parent, - Engine& engine); - - const string symbol() const { return _symbol; } - const string name() const { return _name; } - -private: - const string _symbol; - const string _name; -}; - - -} // namespace Ingen - -#endif // INTERNALPLUGIN_H - diff --git a/src/libs/engine/JackAudioDriver.cpp b/src/libs/engine/JackAudioDriver.cpp deleted file mode 100644 index eff04653..00000000 --- a/src/libs/engine/JackAudioDriver.cpp +++ /dev/null @@ -1,384 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "JackAudioDriver.hpp" -#include CONFIG_H_PATH -#include "tuning.hpp" -#include -#include -#include -#include "Engine.hpp" -#include "util.hpp" -#include "Event.hpp" -#include "ThreadManager.hpp" -#include "QueuedEvent.hpp" -#include "EventSource.hpp" -#include "PostProcessor.hpp" -#include "NodeImpl.hpp" -#include "PatchImpl.hpp" -#include "PortImpl.hpp" -#include "MidiDriver.hpp" -#include "DuplexPort.hpp" -#include "EventSource.hpp" -#include "AudioBuffer.hpp" -#include "ProcessSlave.hpp" - -using namespace std; - -namespace Ingen { - - -//// JackAudioPort //// - -JackAudioPort::JackAudioPort(JackAudioDriver* driver, DuplexPort* patch_port) - : DriverPort(patch_port) - , Raul::List::Node(this) - , _driver(driver) - , _jack_port(NULL) - , _jack_buffer(NULL) -{ - assert(patch_port->poly() == 1); - - _jack_port = jack_port_register(_driver->jack_client(), - patch_port->path().c_str(), JACK_DEFAULT_AUDIO_TYPE, - (patch_port->is_input()) ? JackPortIsInput : JackPortIsOutput, - 0); - - if (_jack_port == NULL) { - cerr << "[JackAudioPort] ERROR: Failed to register port " << patch_port->path() << endl; - throw JackAudioDriver::PortRegistrationFailedException(); - } - - patch_port->buffer(0)->clear(); - patch_port->fixed_buffers(true); -} - - -JackAudioPort::~JackAudioPort() -{ - jack_port_unregister(_driver->jack_client(), _jack_port); -} - - -void -JackAudioPort::prepare_buffer(jack_nframes_t nframes) -{ - jack_sample_t* jack_buf = (jack_sample_t*)jack_port_get_buffer(_jack_port, nframes); - - AudioBuffer* patch_buf = (AudioBuffer*)_patch_port->buffer(0); - - //cerr << "[JACK] " << _patch_port->path() << " buffer: " << patch_buf << endl; - - if (jack_buf != _jack_buffer) { - patch_buf->set_data(jack_buf); - _jack_buffer = jack_buf; - } - - assert(patch_buf->data() == jack_buf); -} - - -//// JackAudioDriver //// - -JackAudioDriver::JackAudioDriver(Engine& engine, - std::string server_name, - jack_client_t* jack_client) - : _engine(engine) - , _jack_thread(NULL) - , _client(jack_client) - , _buffer_size(jack_client ? jack_get_buffer_size(jack_client) : 0) - , _sample_rate(jack_client ? jack_get_sample_rate(jack_client) : 0) - , _is_activated(false) - , _local_client(true) // FIXME - , _process_context(engine) - , _root_patch(NULL) -{ - if (!_client) { - // Try supplied server name - if (server_name != "") { - _client = jack_client_open("Ingen", JackServerName, NULL, server_name.c_str()); - if (_client) - cerr << "[JackAudioDriver] Connected to JACK server '" << - server_name << "'" << endl; - } - - // Either server name not specified, or supplied server name does not exist - // Connect to default server - if (!_client) { - _client = jack_client_open("Ingen", JackNullOption, NULL); - - if (_client) - cerr << "[JackAudioDriver] Connected to default JACK server." << endl; - } - - // Still failed - if (!_client) { - cerr << "[JackAudioDriver] Unable to connect to Jack. Exiting." << endl; - exit(EXIT_FAILURE); - } - - _buffer_size = jack_get_buffer_size(_client); - _sample_rate = jack_get_sample_rate(_client); - } - - jack_on_shutdown(_client, shutdown_cb, this); - - jack_set_thread_init_callback(_client, thread_init_cb, this); - jack_set_sample_rate_callback(_client, sample_rate_cb, this); - jack_set_buffer_size_callback(_client, buffer_size_cb, this); -} - - -JackAudioDriver::~JackAudioDriver() -{ - deactivate(); - - if (_local_client) - jack_client_close(_client); -} - - -void -JackAudioDriver::activate() -{ - if (_is_activated) { - cerr << "[JackAudioDriver] Jack driver already activated." << endl; - return; - } - - jack_set_process_callback(_client, process_cb, this); - - _is_activated = true; - - if (jack_activate(_client)) { - cerr << "[JackAudioDriver] Could not activate Jack client, aborting." << endl; - exit(EXIT_FAILURE); - } else { - cout << "[JackAudioDriver] Activated Jack client." << endl; -/*#ifdef HAVE_LASH - _engine.lash_driver()->set_jack_client_name(jack_client_get_name(_client)); -#endif*/ - } -} - - -void -JackAudioDriver::deactivate() -{ - if (_is_activated) { - - //for (Raul::List::iterator i = _ports.begin(); i != _ports.end(); ++i) - // jack_port_unregister(_client, (*i)->jack_port()); - - jack_deactivate(_client); - _is_activated = false; - - _ports.clear(); - - cout << "[JackAudioDriver] Deactivated Jack client." << endl; - - //_engine.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(DriverPort* port) -{ - assert(ThreadManager::current_thread_id() == THREAD_PROCESS); - - assert(dynamic_cast(port)); - _ports.push_back((JackAudioPort*)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. - */ -DriverPort* -JackAudioDriver::remove_port(const Path& path) -{ - assert(ThreadManager::current_thread_id() == THREAD_PROCESS); - - for (Raul::List::iterator i = _ports.begin(); i != _ports.end(); ++i) - if ((*i)->patch_port()->path() == path) - return _ports.erase(i)->elem(); // FIXME: LEAK - - cerr << "[JackAudioDriver::remove_port] WARNING: Unable to find Jack port " << path << endl; - return NULL; -} - - -DriverPort* -JackAudioDriver::port(const Path& path) -{ - for (Raul::List::iterator i = _ports.begin(); i != _ports.end(); ++i) - if ((*i)->patch_port()->path() == path) - return (*i); - - return NULL; -} - - -DriverPort* -JackAudioDriver::create_port(DuplexPort* patch_port) -{ - try { - if (patch_port->buffer_size() == _buffer_size) - return new JackAudioPort(this, patch_port); - else - return NULL; - } catch (...) { - return NULL; - } -} - - -DriverPort* -JackAudioDriver::driver_port(const Path& path) -{ - assert(ThreadManager::current_thread_id() == THREAD_PROCESS); - - for (Raul::List::iterator i = _ports.begin(); i != _ports.end(); ++i) - if ((*i)->patch_port()->path() == path) - return (*i); - - return NULL; -} - - -/**** Jack Callbacks ****/ - - - -/** Jack process callback, drives entire audio thread. - * - * \callgraph - */ -int -JackAudioDriver::_process_cb(jack_nframes_t nframes) -{ - if (nframes == 0 || ! _is_activated) - return 0; - - // FIXME: all of this time stuff is screwy - - // FIXME: support nframes != buffer_size, even though that never damn well happens - assert(nframes == _buffer_size); - - // Jack can elect to not call this function for a cycle, if overloaded - // FIXME: this doesn't make sense, and the start time isn't used anyway - const jack_nframes_t start_of_current_cycle = jack_last_frame_time(_client); - - const jack_nframes_t end_of_current_cycle = start_of_current_cycle + nframes; -#ifndef NDEBUG - // FIXME: support changing cycle length - const jack_nframes_t start_of_last_cycle = start_of_current_cycle - nframes; - assert(start_of_current_cycle - start_of_last_cycle == nframes); -#endif - - _transport_state = jack_transport_query(_client, &_position); - - _process_context.set_time_slice(nframes, start_of_current_cycle, end_of_current_cycle); - - for (Engine::ProcessSlaves::iterator i = _engine.process_slaves().begin(); - i != _engine.process_slaves().end(); ++i) { - (*i)->context().set_time_slice(nframes, start_of_current_cycle, end_of_current_cycle); - } - - // Process events that came in during the last cycle - // (Aiming for jitter-free 1 block event latency, ideally) - _engine.process_events(_process_context); - - // Set buffers of patch ports to Jack port buffers (zero-copy processing) - for (Raul::List::iterator i = _ports.begin(); i != _ports.end(); ++i) { - assert(*i); - (*i)->prepare_buffer(nframes); - } - - assert(_engine.midi_driver()); - _engine.midi_driver()->pre_process(_process_context); - - // Run root patch - if (_root_patch) - _root_patch->process(_process_context); - - _engine.midi_driver()->post_process(_process_context); - - _engine.post_processor()->set_end_time(_process_context.end()); - - return 0; -} - - -void -JackAudioDriver::_thread_init_cb() -{ - // Initialize thread specific data - _jack_thread = Thread::create_for_this_thread("Jack"); - assert(&Thread::get() == _jack_thread); - _jack_thread->set_context(THREAD_PROCESS); - assert(ThreadManager::current_thread_id() == THREAD_PROCESS); -} - -void -JackAudioDriver::_shutdown_cb() -{ - cout << "[JackAudioDriver] Jack shutdown. Exiting." << endl; - _engine.quit(); - _jack_thread = NULL; -} - - -int -JackAudioDriver::_sample_rate_cb(jack_nframes_t nframes) -{ - if (_is_activated) { - cerr << "[JackAudioDriver] On-the-fly sample rate changing not supported (yet). Aborting." << endl; - exit(EXIT_FAILURE); - } else { - _sample_rate = nframes; - } - return 0; -} - - -int -JackAudioDriver::_buffer_size_cb(jack_nframes_t nframes) -{ - if (_root_patch) { - _root_patch->set_buffer_size(nframes); - _buffer_size = nframes; - } - return 0; -} - - -} // namespace Ingen - diff --git a/src/libs/engine/JackAudioDriver.hpp b/src/libs/engine/JackAudioDriver.hpp deleted file mode 100644 index 3beb775a..00000000 --- a/src/libs/engine/JackAudioDriver.hpp +++ /dev/null @@ -1,185 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef JACKAUDIODRIVER_H -#define JACKAUDIODRIVER_H - -#include -#include -#include -#include -#include -#include "AudioDriver.hpp" -#include "Buffer.hpp" -#include "ProcessContext.hpp" - -namespace Ingen { - -class Engine; -class PatchImpl; -class PortImpl; -class DuplexPort; -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 Raul::List::Node -{ -public: - JackAudioPort(JackAudioDriver* driver, DuplexPort* patch_port); - ~JackAudioPort(); - - void set_name(const std::string& name) { jack_port_set_name(_jack_port, name.c_str()); }; - - void prepare_buffer(jack_nframes_t nframes); - - jack_port_t* jack_port() const { return _jack_port; } - -private: - JackAudioDriver* _driver; - jack_port_t* _jack_port; - jack_sample_t* _jack_buffer; ///< Cached for output ports -}; - - - -/** 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(Engine& engine, - std::string server_name = "", - jack_client_t* jack_client = 0); - - ~JackAudioDriver(); - - void activate(); - void deactivate(); - void enable(); - void disable(); - - DriverPort* port(const Raul::Path& path); - DriverPort* create_port(DuplexPort* patch_port); - - void add_port(DriverPort* port); - DriverPort* remove_port(const Raul::Path& path); - - DriverPort* driver_port(const Raul::Path& path); - - PatchImpl* root_patch() { return _root_patch; } - void set_root_patch(PatchImpl* patch) { _root_patch = patch; } - - ProcessContext& context() { return _process_context; } - - /** Transport state for this frame. - * Intended to only be called from the audio thread. */ - inline const jack_position_t* position() { return &_position; } - inline jack_transport_state_t transport_state() { return _transport_state; } - - bool is_realtime() const { return jack_is_realtime(_client); } - - jack_client_t* jack_client() const { return _client; } - SampleCount buffer_size() const { return _buffer_size; } - SampleCount sample_rate() const { return _sample_rate; } - bool is_activated() const { return _is_activated; } - - inline SampleCount frame_time() const { return jack_frame_time(_client); } - - class PortRegistrationFailedException : public std::exception {}; - -private: - friend class JackAudioPort; - - // These are the static versions of the callbacks, they call - // the non-static ones below - inline static void thread_init_cb(void* const jack_driver); - inline static void shutdown_cb(void* const jack_driver); - inline static int process_cb(jack_nframes_t nframes, 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 - void _thread_init_cb(); - void _shutdown_cb(); - int _process_cb(jack_nframes_t nframes); - int _buffer_size_cb(jack_nframes_t nframes); - int _sample_rate_cb(jack_nframes_t nframes); - - Engine& _engine; - Raul::Thread* _jack_thread; - jack_client_t* _client; - jack_nframes_t _buffer_size; - jack_nframes_t _sample_rate; - bool _is_activated; - bool _local_client; ///< Whether _client should be closed on destruction - jack_position_t _position; - jack_transport_state_t _transport_state; - - Raul::List _ports; - ProcessContext _process_context; - - PatchImpl* _root_patch; -}; - - -inline int JackAudioDriver::process_cb(jack_nframes_t nframes, void* jack_driver) -{ - assert(jack_driver); - return ((JackAudioDriver*)jack_driver)->_process_cb(nframes); -} - -inline void JackAudioDriver::thread_init_cb(void* jack_driver) -{ - assert(jack_driver); - return ((JackAudioDriver*)jack_driver)->_thread_init_cb(); -} - -inline void JackAudioDriver::shutdown_cb(void* jack_driver) -{ - assert(jack_driver); - return ((JackAudioDriver*)jack_driver)->_shutdown_cb(); -} - - -inline int JackAudioDriver::buffer_size_cb(jack_nframes_t nframes, void* jack_driver) -{ - assert(jack_driver); - return ((JackAudioDriver*)jack_driver)->_buffer_size_cb(nframes); -} - - -inline int JackAudioDriver::sample_rate_cb(jack_nframes_t nframes, void* jack_driver) -{ - assert(jack_driver); - return ((JackAudioDriver*)jack_driver)->_sample_rate_cb(nframes); -} - - -} // namespace Ingen - -#endif // JACKAUDIODRIVER_H diff --git a/src/libs/engine/JackMidiDriver.cpp b/src/libs/engine/JackMidiDriver.cpp deleted file mode 100644 index 9e236541..00000000 --- a/src/libs/engine/JackMidiDriver.cpp +++ /dev/null @@ -1,267 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -() */ - -#include -#include -#include -#include -#include -#include "types.hpp" -#include "JackMidiDriver.hpp" -#include "JackAudioDriver.hpp" -#include "ThreadManager.hpp" -#include "AudioDriver.hpp" -#include "EventBuffer.hpp" -#include "DuplexPort.hpp" -#include "ProcessContext.hpp" -#include "jack_compat.h" -using namespace std; - -namespace Ingen { - - -//// JackMidiPort //// - -JackMidiPort::JackMidiPort(JackMidiDriver* driver, DuplexPort* patch_port) - : DriverPort(patch_port) - , Raul::List::Node(this) - , _driver(driver) - , _jack_port(NULL) -{ - assert(patch_port->poly() == 1); - - _jack_port = jack_port_register(_driver->jack_client(), - patch_port->path().c_str(), JACK_DEFAULT_MIDI_TYPE, - (patch_port->is_input()) ? JackPortIsInput : JackPortIsOutput, - 0); - - if (_jack_port == NULL) { - cerr << "[JackMidiPort] ERROR: Failed to register port " << patch_port->path() << endl; - throw JackAudioDriver::PortRegistrationFailedException(); - } - - patch_port->buffer(0)->clear(); -} - - -JackMidiPort::~JackMidiPort() -{ - jack_port_unregister(_driver->jack_client(), _jack_port); -} - - -/** Prepare input for a block before a cycle is run, in the audio thread. - * - * This is simple since Jack MIDI is in-band with audio. - */ -void -JackMidiPort::pre_process(ProcessContext& context) -{ - if ( ! is_input() ) - return; - - assert(_patch_port->poly() == 1); - - EventBuffer* patch_buf = dynamic_cast(_patch_port->buffer(0)); - assert(patch_buf); - - void* jack_buffer = jack_port_get_buffer(_jack_port, context.nframes()); - const jack_nframes_t event_count = jack_midi_get_event_count(jack_buffer); - - patch_buf->prepare_write(context.start(), context.nframes()); - - // 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_get(&ev, jack_buffer, i); - - // FIXME: type is hardcoded for now, we should get it from - // the type map instead - const bool success = patch_buf->append(ev.time, 0, 1, ev.size, ev.buffer); - if (!success) - cerr << "WARNING: Failed to write MIDI to port buffer, event(s) lost!" << endl; - } - - //if (event_count) - // cerr << "Jack MIDI got " << event_count << " events." << endl; -} - - -/** Prepare output for a block after a cycle is run, in the audio thread. - * - * This is simple since Jack MIDI is in-band with audio. - */ -void -JackMidiPort::post_process(ProcessContext& context) -{ - if (is_input()) - return; - - EventBuffer* patch_buf = dynamic_cast(_patch_port->buffer(0)); - void* jack_buf = jack_port_get_buffer(_jack_port, context.nframes()); - - assert(_patch_port->poly() == 1); - assert(patch_buf); - - patch_buf->prepare_read(context.start(), context.nframes()); - jack_midi_clear_buffer(jack_buf); - - uint32_t frames = 0; - uint32_t subframes = 0; - uint16_t type = 0; - uint16_t size = 0; - uint8_t* data = NULL; - - // Copy events from Jack port buffer into patch port buffer - for (patch_buf->rewind(); patch_buf->is_valid(); patch_buf->increment()) { - patch_buf->get_event(&frames, &subframes, &type, &size, &data); - jack_midi_event_write(jack_buf, frames, data, size); - } - - //if (event_count) - // cerr << "Jack MIDI wrote " << event_count << " events." << endl; -} - - - -//// JackMidiDriver //// - - -bool JackMidiDriver::_midi_thread_exit_flag = true; - - -JackMidiDriver::JackMidiDriver(jack_client_t* client) - : _client(client) - , _is_activated(false) - , _is_enabled(false) -{ -} - - -JackMidiDriver::~JackMidiDriver() -{ - deactivate(); -} - - -/** Launch and start the MIDI thread. - */ -void -JackMidiDriver::activate() -{ - _is_activated = true; -} - - -/** Terminate the MIDI thread. - */ -void -JackMidiDriver::deactivate() -{ - _is_activated = false; -} - - -/** Build flat arrays of events to be used as input for the given cycle. - */ -void -JackMidiDriver::pre_process(ProcessContext& context) -{ - for (Raul::List::iterator i = _in_ports.begin(); i != _in_ports.end(); ++i) - (*i)->pre_process(context); -} - - -/** Write the output from any (top-level, exported) MIDI output ports to Jack ports. - */ -void -JackMidiDriver::post_process(ProcessContext& context) -{ - for (Raul::List::iterator i = _out_ports.begin(); i != _out_ports.end(); ++i) - (*i)->post_process(context); -} - - -/** 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 new_port() and remove_port(). - */ -void -JackMidiDriver::add_port(DriverPort* port) -{ - assert(ThreadManager::current_thread_id() == THREAD_PROCESS); - assert(dynamic_cast(port)); - - if (port->is_input()) - _in_ports.push_back((JackMidiPort*)port); - else - _out_ports.push_back((JackMidiPort*)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. - */ -DriverPort* -JackMidiDriver::remove_port(const Path& path) -{ - assert(ThreadManager::current_thread_id() == THREAD_PROCESS); - - // FIXME: duplex? - - for (Raul::List::iterator i = _in_ports.begin(); i != _in_ports.end(); ++i) - if ((*i)->patch_port()->path() == path) - return _in_ports.erase(i)->elem(); // FIXME: LEAK - - for (Raul::List::iterator i = _out_ports.begin(); i != _out_ports.end(); ++i) - if ((*i)->patch_port()->path() == path) - return _out_ports.erase(i)->elem(); // FIXME: LEAK - - cerr << "[JackMidiDriver::remove_input] WARNING: Unable to find Jack port " << path << endl; - return NULL; -} - - -DriverPort* -JackMidiDriver::driver_port(const Path& path) -{ - assert(ThreadManager::current_thread_id() == THREAD_PROCESS); - - // FIXME: duplex? - - for (Raul::List::iterator i = _in_ports.begin(); i != _in_ports.end(); ++i) - if ((*i)->patch_port()->path() == path) - return (*i); - - for (Raul::List::iterator i = _out_ports.begin(); i != _out_ports.end(); ++i) - if ((*i)->patch_port()->path() == path) - return (*i); - - return NULL; -} - - -} // namespace Ingen - diff --git a/src/libs/engine/JackMidiDriver.hpp b/src/libs/engine/JackMidiDriver.hpp deleted file mode 100644 index 7889c4fa..00000000 --- a/src/libs/engine/JackMidiDriver.hpp +++ /dev/null @@ -1,113 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef JACKMIDIDRIVER_H -#define JACKMIDIDRIVER_H - -#include -#include -#include -#include CONFIG_H_PATH -#include "MidiDriver.hpp" - -namespace Ingen { - -class NodeImpl; -class SetPortValueEvent; -class JackMidiDriver; -class DuplexPort; - - -/** Representation of an JACK MIDI port. - * - * \ingroup engine - */ -class JackMidiPort : public DriverPort, public Raul::List::Node -{ -public: - JackMidiPort(JackMidiDriver* driver, DuplexPort* port); - virtual ~JackMidiPort(); - - void pre_process(ProcessContext& context); - void post_process(ProcessContext& context); - - void set_name(const std::string& name) { jack_port_set_name(_jack_port, name.c_str()); }; - -private: - JackMidiDriver* _driver; - jack_port_t* _jack_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() { _is_enabled = true; } - void disable() { _is_enabled = false; } - - bool is_activated() const { return _is_activated; } - bool is_enabled() const { return _is_enabled; } - - void pre_process(ProcessContext& context); - void post_process(ProcessContext& context); - - JackMidiPort* create_port(DuplexPort* patch_port) - { return new JackMidiPort(this, patch_port); } - - void add_port(DriverPort* port); - DriverPort* remove_port(const Raul::Path& path); - - DriverPort* driver_port(const Raul::Path& path); - - jack_client_t* jack_client() { return _client; } - -private: - Raul::List _in_ports; - Raul::List _out_ports; - - friend class JackMidiPort; - - void add_output(Raul::List::Node* port); - Raul::List::Node* remove_output(JackMidiPort* port); - - // MIDI thread - static void* process_midi_in(void* me); - - jack_client_t* _client; - pthread_t _process_thread; - bool _is_activated; - bool _is_enabled; - static bool _midi_thread_exit_flag; -}; - - -} // namespace Ingen - - -#endif // JACKMIDIDRIVER_H diff --git a/src/libs/engine/LADSPANode.cpp b/src/libs/engine/LADSPANode.cpp deleted file mode 100644 index 22f5bbf3..00000000 --- a/src/libs/engine/LADSPANode.cpp +++ /dev/null @@ -1,373 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include -#include -#include "LADSPANode.hpp" -#include "AudioBuffer.hpp" -#include "InputPort.hpp" -#include "OutputPort.hpp" -#include "PluginImpl.hpp" -#include "ProcessContext.hpp" - -using namespace std; - -namespace Ingen { - - -/** Partially construct a LADSPANode. - * - * Object is not usable until instantiate() is called with success. - * (It _will_ crash!) - */ -LADSPANode::LADSPANode(PluginImpl* plugin, const string& path, bool polyphonic, PatchImpl* parent, const LADSPA_Descriptor* descriptor, SampleRate srate, size_t buffer_size) - : NodeBase(plugin, path, polyphonic, parent, srate, buffer_size) - , _descriptor(descriptor) - , _instances(NULL) - , _prepared_instances(NULL) -{ - assert(_descriptor != NULL); -} - - -LADSPANode::~LADSPANode() -{ - for (uint32_t i=0; i < _polyphony; ++i) - _descriptor->cleanup((*_instances)[i]); - - delete _instances; -} - - -bool -LADSPANode::prepare_poly(uint32_t poly) -{ - NodeBase::prepare_poly(poly); - - if ( (!_polyphonic) - || (_prepared_instances && poly <= _prepared_instances->size()) ) { - return true; - } - - _prepared_instances = new Raul::Array(poly, *_instances); - for (uint32_t i = _polyphony; i < _prepared_instances->size(); ++i) { - _prepared_instances->at(i) = _descriptor->instantiate(_descriptor, _srate); - if (_prepared_instances->at(i) == NULL) { - cerr << "Failed to instantiate plugin!" << endl; - return false; - } - - if (_activated && _descriptor->activate) - _descriptor->activate(_prepared_instances->at(i)); - } - - return true; -} - - -bool -LADSPANode::apply_poly(Raul::Maid& maid, uint32_t poly) -{ - if (!_polyphonic) - return true; - - if (_prepared_instances) { - assert(poly <= _prepared_instances->size()); - maid.push(_instances); - _instances = _prepared_instances; - _prepared_instances = NULL; - } - - assert(poly <= _instances->size()); - _polyphony = poly; - - return NodeBase::apply_poly(maid, poly); -} - - -static string -nameify_if_invalid(const string& name) -{ - if (Path::is_valid_name(name)) { - return name; - } else { - const string new_name = Path::nameify(name); - assert(Path::is_valid_name(new_name)); - if (new_name != name) - cerr << "Symbol '" << new_name << "' generated from LADSPA name '" << name << endl; - return new_name; - } -} - - -/** 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 -LADSPANode::instantiate() -{ - if (!_ports) - _ports = new Raul::Array(_descriptor->PortCount); - - _instances = new Raul::Array(_polyphony, NULL); - - size_t port_buffer_size = 0; - - for (uint32_t i=0; i < _polyphony; ++i) { - (*_instances)[i] = _descriptor->instantiate(_descriptor, _srate); - if ((*_instances)[i] == NULL) { - cerr << "Failed to instantiate plugin!" << endl; - return false; - } - } - - string port_name; - string port_path; - - PortImpl* port = NULL; - - for (size_t j=0; j < _descriptor->PortCount; ++j) { - port_name = nameify_if_invalid(_descriptor->PortNames[j]); - - string::size_type slash_index; - - // Name mangling, to guarantee port names are unique - for (size_t k=0; k < _descriptor->PortCount; ++k) { - assert(_descriptor->PortNames[k] != NULL); - if (k != j && port_name == _descriptor->PortNames[k]) { // clash - if (LADSPA_IS_PORT_CONTROL(_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] = '-'; - } - - /*if (_descriptor->PortNames[j] != port_name) - cerr << "NOTICE: Translated LADSPA port name: " << - _descriptor->PortNames[j] << " -> " << port_name << endl;*/ - - port_path = path() + "/" + port_name; - - DataType type = DataType::AUDIO; - port_buffer_size = _buffer_size; - - if (LADSPA_IS_PORT_CONTROL(_descriptor->PortDescriptors[j])) { - type = DataType::CONTROL; - port_buffer_size = 1; - } else { - assert(LADSPA_IS_PORT_AUDIO(_descriptor->PortDescriptors[j])); - } - assert (LADSPA_IS_PORT_INPUT(_descriptor->PortDescriptors[j]) - || LADSPA_IS_PORT_OUTPUT(_descriptor->PortDescriptors[j])); - - boost::optional default_val, min, max; - get_port_limits(j, default_val, min, max); - - const float value = default_val ? default_val.get() : 0.0f; - - if (LADSPA_IS_PORT_INPUT(_descriptor->PortDescriptors[j])) { - port = new InputPort(this, port_name, j, _polyphony, type, value, port_buffer_size); - _ports->at(j) = port; - } else if (LADSPA_IS_PORT_OUTPUT(_descriptor->PortDescriptors[j])) { - port = new OutputPort(this, port_name, j, _polyphony, type, value, port_buffer_size); - _ports->at(j) = port; - } - - assert(port); - assert(_ports->at(j) == port); - - // Work around broke-ass crackhead plugins - if (default_val && default_val.get() < min.get()) { - cerr << "WARNING: Broken LADSPA " << _descriptor->UniqueID - << ": Port default < minimum. Minimum adjusted." << endl; - min = default_val; - } - - if (default_val && default_val.get() > max.get()) { - cerr << "WARNING: Broken LADSPA " << _descriptor->UniqueID - << ": Maximum adjusted." << endl; - max = default_val; - } - - // Set initial/default value - if (port->buffer_size() == 1) { - for (uint32_t i=0; i < _polyphony; ++i) - ((AudioBuffer*)port->buffer(i))->set_value(value, 0, 0); - } - - if (port->is_input() && port->buffer_size() == 1) { - if (min) - port->set_variable("ingen:minimum", min.get()); - if (max) - port->set_variable("ingen:maximum", max.get()); - if (default_val) - port->set_variable("ingen:default", default_val.get()); - } - } - - return true; -} - - -void -LADSPANode::activate() -{ - NodeBase::activate(); - - for (uint32_t i=0; i < _polyphony; ++i) { - for (unsigned long j=0; j < _descriptor->PortCount; ++j) { - set_port_buffer(i, j, _ports->at(j)->buffer(i)); - /* if (port->type() == DataType::FLOAT && port->buffer_size() == 1) - port->set_value(0.0f, 0); // FIXME - else if (port->type() == DataType::FLOAT && port->buffer_size() > 1) - port->set_value(0.0f, 0);*/ - } - if (_descriptor->activate != NULL) - _descriptor->activate((*_instances)[i]); - } -} - - -void -LADSPANode::deactivate() -{ - NodeBase::deactivate(); - - for (uint32_t i=0; i < _polyphony; ++i) - if (_descriptor->deactivate != NULL) - _descriptor->deactivate((*_instances)[i]); -} - - -void -LADSPANode::process(ProcessContext& context) -{ - NodeBase::pre_process(context); - - for (uint32_t i=0; i < _polyphony; ++i) - _descriptor->run((*_instances)[i], context.nframes()); - - NodeBase::post_process(context); -} - - -void -LADSPANode::set_port_buffer(uint32_t voice, uint32_t port_num, Buffer* buf) -{ - assert(voice < _polyphony); - - AudioBuffer* audio_buffer = dynamic_cast(buf); - assert(audio_buffer); - - if (port_num < _descriptor->PortCount) - _descriptor->connect_port((*_instances)[voice], port_num, - audio_buffer->data()); -} - - -void -LADSPANode::get_port_limits(unsigned long port_index, - boost::optional& normal, - boost::optional& lower, - boost::optional& upper) -{ - LADSPA_PortRangeHintDescriptor hint_descriptor = _descriptor->PortRangeHints[port_index].HintDescriptor; - - /* set upper and lower, possibly adjusted to the sample rate */ - if (LADSPA_IS_HINT_SAMPLE_RATE(hint_descriptor)) { - upper = _descriptor->PortRangeHints[port_index].UpperBound * _srate; - lower = _descriptor->PortRangeHints[port_index].LowerBound * _srate; - } else { - upper = _descriptor->PortRangeHints[port_index].UpperBound; - lower = _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.get() < 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)) { - assert(lower); - assert(upper); - if (LADSPA_IS_HINT_LOGARITHMIC(hint_descriptor)) { - normal = exp(log(lower.get()) * 0.75 + log(upper.get()) * 0.25); - } else { - normal = lower.get() * 0.75 + upper.get() * 0.25; - } - } else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(hint_descriptor)) { - assert(lower); - assert(upper); - if (LADSPA_IS_HINT_LOGARITHMIC(hint_descriptor)) { - normal = exp(log(lower.get()) * 0.5 + log(upper.get()) * 0.5); - } else { - normal = lower.get() * 0.5 + upper.get() * 0.5; - } - } else if (LADSPA_IS_HINT_DEFAULT_HIGH(hint_descriptor)) { - assert(lower); - assert(upper); - if (LADSPA_IS_HINT_LOGARITHMIC(hint_descriptor)) { - normal = exp(log(lower.get()) * 0.25 + log(upper.get()) * 0.75); - } else { - normal = lower.get() * 0.25 + upper.get() * 0.75; - } - } else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(hint_descriptor)) { - assert(upper); - normal = upper.get(); - } 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)) { - assert(lower); - normal = lower.get(); - } else if (LADSPA_IS_HINT_BOUNDED_ABOVE(hint_descriptor)) { - assert(upper); - normal = upper.get(); - } - } -} - - -} // namespace Ingen - diff --git a/src/libs/engine/LADSPANode.hpp b/src/libs/engine/LADSPANode.hpp deleted file mode 100644 index 53d7ad9e..00000000 --- a/src/libs/engine/LADSPANode.hpp +++ /dev/null @@ -1,74 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef LADSPANODE_H -#define LADSPANODE_H - -#include -#include -#include -#include "types.hpp" -#include "NodeBase.hpp" -#include "PluginImpl.hpp" - -namespace Ingen { - - -/** An instance of a LADSPA plugin. - * - * \ingroup engine - */ -class LADSPANode : public NodeBase -{ -public: - LADSPANode(PluginImpl* plugin, - const string& name, - bool polyphonic, - PatchImpl* parent, - const LADSPA_Descriptor* descriptor, - SampleRate srate, - size_t buffer_size); - - ~LADSPANode(); - - bool instantiate(); - - bool prepare_poly(uint32_t poly); - bool apply_poly(Raul::Maid& maid, uint32_t poly); - - void activate(); - void deactivate(); - - void process(ProcessContext& context); - - void set_port_buffer(uint32_t voice, uint32_t port_num, Buffer* buf); - -protected: - void get_port_limits(unsigned long port_index, - boost::optional& default_value, - boost::optional& lower_bound, - boost::optional& upper_bound); - - const LADSPA_Descriptor* _descriptor; - Raul::Array* _instances; - Raul::Array* _prepared_instances; -}; - - -} // namespace Ingen - -#endif // LADSPANODE_H diff --git a/src/libs/engine/LADSPAPlugin.cpp b/src/libs/engine/LADSPAPlugin.cpp deleted file mode 100644 index 4a0b5c14..00000000 --- a/src/libs/engine/LADSPAPlugin.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include "LADSPAPlugin.hpp" -#include "LADSPANode.hpp" -#include "NodeImpl.hpp" -#include "Engine.hpp" -#include "AudioDriver.hpp" - -using namespace std; - -namespace Ingen { - - -NodeImpl* -LADSPAPlugin::instantiate(const string& name, - bool polyphonic, - Ingen::PatchImpl* parent, - Engine& engine) -{ - assert(_id != 0); - - SampleCount srate = engine.audio_driver()->sample_rate(); - SampleCount buffer_size = engine.audio_driver()->buffer_size(); - - LADSPA_Descriptor_Function df = NULL; - LADSPANode* n = NULL; - - load(); // FIXME: unload at some point - assert(_module); - assert(*_module); - - if (!_module->get_symbol("ladspa_descriptor", (void*&)df)) { - cerr << "Looks like this isn't a LADSPA plugin." << endl; - return NULL; - } - - // Attempt to find the plugin in library - LADSPA_Descriptor* descriptor = NULL; - for (unsigned long i=0; (descriptor = (LADSPA_Descriptor*)df(i)) != NULL; ++i) { - if (descriptor->UniqueID == _id) { - break; - } - } - - if (descriptor != NULL) { - n = new LADSPANode(this, name, polyphonic, parent, descriptor, srate, buffer_size); - - if ( ! n->instantiate() ) { - delete n; - n = NULL; - } - - } else { - cerr << "Could not find plugin \"" << _id << "\" in " << _library_path << endl; - } - - return n; -} - - -} // namespace Ingen diff --git a/src/libs/engine/LADSPAPlugin.hpp b/src/libs/engine/LADSPAPlugin.hpp deleted file mode 100644 index 2414be7c..00000000 --- a/src/libs/engine/LADSPAPlugin.hpp +++ /dev/null @@ -1,78 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef LADSPAPLUGIN_H -#define LADSPAPLUGIN_H - -#include CONFIG_H_PATH - -#include -#include -#include -#include -#include -#include -#include -#include "types.hpp" -#include "PluginImpl.hpp" - -namespace Ingen { - -class NodeImpl; - - -/** Implementation of a LADSPA plugin (loaded shared library). - */ -class LADSPAPlugin : public PluginImpl -{ -public: - LADSPAPlugin(const std::string& library_path, - const std::string& uri, - unsigned long id, - const string& label, - const string& name) - : PluginImpl(Plugin::LADSPA, uri, library_path) - , _id(id) - , _label(label) - , _name(name) - {} - - NodeImpl* instantiate(const std::string& name, - bool polyphonic, - Ingen::PatchImpl* parent, - Engine& engine); - - const std::string& label() const { return _label; } - unsigned long id() const { return _id; } - const string symbol() const { return Raul::Path::nameify(_label); } - const string name() const { return _name; } - - const string library_name() const { - return _library_path.substr(_library_path.find_last_of("/")+1); - } - -private: - const unsigned long _id; - const std::string _label; - const std::string _name; -}; - - -} // namespace Ingen - -#endif // LADSPAPLUGIN_H - diff --git a/src/libs/engine/LV2Info.cpp b/src/libs/engine/LV2Info.cpp deleted file mode 100644 index 43dd014b..00000000 --- a/src/libs/engine/LV2Info.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2008 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#define __STDC_LIMIT_MACROS 1 -#include -#include -#include -#include "LV2Info.hpp" -#include - -using namespace std; - -namespace Ingen { - -LV2Info::LV2Info(Ingen::Shared::World* world) - : input_class(slv2_value_new_uri(world->slv2_world, SLV2_PORT_CLASS_INPUT)) - , output_class(slv2_value_new_uri(world->slv2_world, SLV2_PORT_CLASS_OUTPUT)) - , control_class(slv2_value_new_uri(world->slv2_world, SLV2_PORT_CLASS_CONTROL)) - , audio_class(slv2_value_new_uri(world->slv2_world, SLV2_PORT_CLASS_AUDIO)) - , event_class(slv2_value_new_uri(world->slv2_world, SLV2_PORT_CLASS_EVENT)) - , _world(world) -{ - assert(world); - - LV2_Event_Feature* ev_data = (LV2_Event_Feature*)malloc(sizeof(LV2_Event_Feature)); - ev_data->lv2_event_ref = &LV2Info::event_ref; - ev_data->lv2_event_unref = &LV2Info::event_ref; - ev_data->callback_data = this; - LV2_Feature* ev_feature = (LV2_Feature*)malloc(sizeof(LV2_Event_Feature)); - ev_feature->URI = LV2_EVENT_URI; - ev_feature->data = ev_data; - - world->lv2_features->add_feature(LV2_EVENT_URI, ev_feature, ev_data); -} - - -LV2Info::~LV2Info() -{ - slv2_value_free(input_class); - slv2_value_free(output_class); - slv2_value_free(control_class); - slv2_value_free(audio_class); - slv2_value_free(event_class); -} - - -uint32_t -LV2Info::event_ref(LV2_Event_Callback_Data callback_data, - LV2_Event* event) -{ - return 0; -} - - - -} // namespace Ingen diff --git a/src/libs/engine/LV2Info.hpp b/src/libs/engine/LV2Info.hpp deleted file mode 100644 index f4859ac7..00000000 --- a/src/libs/engine/LV2Info.hpp +++ /dev/null @@ -1,66 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2008 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef LV2INFO_H -#define LV2INFO_H - -#include CONFIG_H_PATH -#ifndef HAVE_SLV2 -#error "This file requires SLV2, but HAVE_SLV2 is not defined. Please report." -#endif - -#include -#include -#include -#include "module/global.hpp" -#include "module/World.hpp" -#include "shared/LV2URIMap.hpp" -#include "lv2ext/lv2_uri_map.h" -#include "lv2ext/lv2_event.h" - -namespace Ingen { - - -/** Stuff that may need to be passed to an LV2 plugin (i.e. LV2 features). - */ -class LV2Info : public Shared::LV2URIMap { -public: - LV2Info(Ingen::Shared::World* world); - ~LV2Info(); - - SLV2Value input_class; - SLV2Value output_class; - SLV2Value control_class; - SLV2Value audio_class; - SLV2Value event_class; - - Ingen::Shared::World& world() { return *_world; } - SLV2World lv2_world() { return _world->slv2_world; } - - static uint32_t event_ref(LV2_Event_Callback_Data callback_data, - LV2_Event* event); - - LV2_Feature** lv2_features() const { return _world->lv2_features->lv2_features(); } - -private: - Ingen::Shared::World* _world; -}; - - -} // namespace Ingen - -#endif // LV2INFO_H diff --git a/src/libs/engine/LV2Node.cpp b/src/libs/engine/LV2Node.cpp deleted file mode 100644 index a06cc55a..00000000 --- a/src/libs/engine/LV2Node.cpp +++ /dev/null @@ -1,305 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include -#include -#include "AudioBuffer.hpp" -#include "InputPort.hpp" -#include "LV2Node.hpp" -#include "LV2Plugin.hpp" -#include "EventBuffer.hpp" -#include "OutputPort.hpp" -#include "ProcessContext.hpp" -#include "lv2_contexts.h" - -using namespace std; - -namespace Ingen { - - -/** Partially construct a LV2Node. - * - * Object is not usable until instantiate() is called with success. - * (It _will_ crash!) - */ -LV2Node::LV2Node(LV2Plugin* plugin, - const string& name, - bool polyphonic, - PatchImpl* parent, - SampleRate srate, - size_t buffer_size) - : NodeBase(plugin, name, polyphonic, parent, srate, buffer_size) - , _lv2_plugin(plugin) - , _instances(NULL) - , _prepared_instances(NULL) - , _message_run(NULL) -{ - assert(_lv2_plugin); -} - - -LV2Node::~LV2Node() -{ - for (uint32_t i=0; i < _polyphony; ++i) - slv2_instance_free((*_instances)[i]); - - delete _instances; -} - - -bool -LV2Node::prepare_poly(uint32_t poly) -{ - NodeBase::prepare_poly(poly); - - if ( (!_polyphonic) - || (_prepared_instances && poly <= _prepared_instances->size()) ) { - return true; - } - - _prepared_instances = new Raul::Array(poly, *_instances); - for (uint32_t i = _polyphony; i < _prepared_instances->size(); ++i) { - // FIXME: features array (in NodeFactory) must be passed! - _prepared_instances->at(i) = slv2_plugin_instantiate( - _lv2_plugin->slv2_plugin(), _srate, NULL); - - if (_prepared_instances->at(i) == NULL) { - cerr << "Failed to instantiate plugin!" << endl; - return false; - } - - if (_activated) - slv2_instance_activate(_prepared_instances->at(i)); - } - - return true; -} - - -bool -LV2Node::apply_poly(Raul::Maid& maid, uint32_t poly) -{ - if (!_polyphonic) - return true; - - if (_prepared_instances) { - assert(poly <= _prepared_instances->size()); - maid.push(_instances); - _instances = _prepared_instances; - _prepared_instances = NULL; - } - - assert(poly <= _instances->size()); - _polyphony = poly; - - return NodeBase::apply_poly(maid, poly); -} - - -/** 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 -LV2Node::instantiate() -{ - SharedPtr info = _lv2_plugin->lv2_info(); - SLV2Plugin plug = _lv2_plugin->slv2_plugin(); - - uint32_t num_ports = slv2_plugin_get_num_ports(plug); - assert(num_ports > 0); - - _ports = new Raul::Array(num_ports, NULL); - _instances = new Raul::Array(_polyphony, NULL); - - uint32_t port_buffer_size = 0; - - for (uint32_t i=0; i < _polyphony; ++i) { - (*_instances)[i] = slv2_plugin_instantiate(plug, _srate, info->lv2_features()); - if ((*_instances)[i] == NULL) { - cerr << "Failed to instantiate plugin!" << endl; - return false; - } - - const void* ctx_ext = slv2_instance_get_extension_data( - (*_instances)[i], LV2_CONTEXT_MESSAGE); - - if (ctx_ext) { - cerr << "HAS CONTEXT EXTENSION" << endl; - if (_message_run == NULL) - _message_run = new MessageRunFuncs(_polyphony, NULL); - LV2MessageContext* mc = (LV2MessageContext*)ctx_ext; - (*_message_run)[i] = mc->message_run; - } - } - - string port_name; - string port_path; - - PortImpl* port = NULL; - - float* def_values = new float[num_ports]; - slv2_plugin_get_port_ranges_float(plug, 0, 0, def_values); - - for (uint32_t j=0; j < num_ports; ++j) { - SLV2Port id = slv2_plugin_get_port_by_index(plug, j); - - // LV2 shortnames are guaranteed to be unique, valid C identifiers - port_name = slv2_value_as_string(slv2_port_get_symbol(plug, id)); - - assert(port_name.find("/") == string::npos); - - port_path = path() + "/" + port_name; - - DataType data_type = DataType::UNKNOWN; - if (slv2_port_is_a(plug, id, info->control_class)) { - data_type = DataType::CONTROL; - port_buffer_size = 1; - } else if (slv2_port_is_a(plug, id, info->audio_class)) { - data_type = DataType::AUDIO; - port_buffer_size = _buffer_size; - } else if (slv2_port_is_a(plug, id, info->event_class)) { - data_type = DataType::EVENT; - port_buffer_size = _buffer_size; - } - - enum { UNKNOWN, INPUT, OUTPUT } direction = UNKNOWN; - if (slv2_port_is_a(plug, id, info->input_class)) { - direction = INPUT; - } else if (slv2_port_is_a(plug, id, info->output_class)) { - direction = OUTPUT; - } - - if (data_type == DataType::UNKNOWN || direction == UNKNOWN) { - delete _ports; - _ports = NULL; - delete _instances; - _instances = NULL; - return false; - } - - // FIXME: need nice type preserving SLV2Value -> Raul::Atom conversion - float def = isnan(def_values[j]) ? 0.0f : def_values[j]; - Atom defatm = def; - - if (direction == INPUT) - port = new InputPort(this, port_name, j, _polyphony, data_type, defatm, port_buffer_size); - else - port = new OutputPort(this, port_name, j, _polyphony, data_type, defatm, port_buffer_size); - - if (direction == INPUT && data_type == DataType::CONTROL) - ((AudioBuffer*)port->buffer(0))->set_value(def, 0, 0); - - SLV2Value pred = slv2_value_new_uri(info->lv2_world(), - "http://lv2plug.in/ns/dev/contexts#context"); - SLV2Values contexts = slv2_port_get_value(plug, id, pred); - for (uint32_t i = 0; i < slv2_values_size(contexts); ++i) { - SLV2Value c = slv2_values_get_at(contexts, i); - const char* context = slv2_value_as_string(c); - if (!strcmp("http://lv2plug.in/ns/dev/contexts#MessageContext", context)) { - cout << "MESSAGE CONTEXT!" << endl; - port->set_context(Context::MESSAGE); - } else { - cout << "UNKNOWN CONTEXT: " - << slv2_value_as_string(slv2_values_get_at(contexts, i)) - << endl; - } - } - - _ports->at(j) = port; - } - - delete [] def_values; - - return true; -} - - -void -LV2Node::activate() -{ - NodeBase::activate(); - - for (uint32_t i=0; i < _polyphony; ++i) { - for (unsigned long j=0; j < num_ports(); ++j) { - PortImpl* const port = _ports->at(j); - - set_port_buffer(i, j, port->buffer(i)); - - if (port->type() == DataType::CONTROL) { - ((AudioBuffer*)port->buffer(i))->set_value(port->value().get_float(), 0, 0); - } else if (port->type() == DataType::AUDIO) { - ((AudioBuffer*)port->buffer(i))->set_value(0.0f, 0, 0); - } - } - slv2_instance_activate((*_instances)[i]); - } -} - - -void -LV2Node::deactivate() -{ - NodeBase::deactivate(); - - for (uint32_t i=0; i < _polyphony; ++i) - slv2_instance_deactivate((*_instances)[i]); -} - - -void -LV2Node::message_process(MessageContext& context, uint32_t* output) -{ - // FIXME: voice - if (_message_run) - (*_message_run)[0]((*_instances)[0], output); - - /* MESSAGE PROCESS */ -} - - -void -LV2Node::process(ProcessContext& context) -{ - NodeBase::pre_process(context); - - for (uint32_t i=0; i < _polyphony; ++i) - slv2_instance_run((*_instances)[i], context.nframes()); - - NodeBase::post_process(context); -} - - -void -LV2Node::set_port_buffer(uint32_t voice, uint32_t port_num, Buffer* buf) -{ - assert(voice < _polyphony); - - slv2_instance_connect_port((*_instances)[voice], port_num, buf->raw_data()); -} - - -} // namespace Ingen - diff --git a/src/libs/engine/LV2Node.hpp b/src/libs/engine/LV2Node.hpp deleted file mode 100644 index 3187005c..00000000 --- a/src/libs/engine/LV2Node.hpp +++ /dev/null @@ -1,75 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef LV2NODE_H -#define LV2NODE_H - -#include -#include -#include "types.hpp" -#include "NodeBase.hpp" - -namespace Ingen { - -class LV2Plugin; - - -/** An instance of a LV2 plugin. - * - * \ingroup engine - */ -class LV2Node : public NodeBase -{ -public: - LV2Node(LV2Plugin* plugin, - const string& name, - bool polyphonic, - PatchImpl* parent, - SampleRate srate, - size_t buffer_size); - - ~LV2Node(); - - bool instantiate(); - - bool prepare_poly(uint32_t poly); - bool apply_poly(Raul::Maid& maid, uint32_t poly); - - void activate(); - void deactivate(); - - void message_process(MessageContext& context, uint32_t* output); - - void process(ProcessContext& context); - - void set_port_buffer(uint32_t voice, uint32_t port_num, Buffer* buf); - -protected: - LV2Plugin* _lv2_plugin; - Raul::Array* _instances; - Raul::Array* _prepared_instances; - - typedef bool (*MessageRunFunc)(LV2_Handle, uint32_t*); - typedef Raul::Array MessageRunFuncs; - MessageRunFuncs* _message_run; -}; - - -} // namespace Ingen - -#endif // LV2NODE_H - diff --git a/src/libs/engine/LV2Plugin.cpp b/src/libs/engine/LV2Plugin.cpp deleted file mode 100644 index 90a3a6b8..00000000 --- a/src/libs/engine/LV2Plugin.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include "LV2Plugin.hpp" -#include "LV2Node.hpp" -#include "NodeImpl.hpp" -#include "Engine.hpp" -#include "AudioDriver.hpp" - -namespace Ingen { - - -const string -LV2Plugin::symbol() const -{ - string working = _uri; - if (working[working.length()-1] == '/') - working = working.substr(0, working.length()-1); - - while (working.length() > 0) { - size_t last_slash = working.find_last_of("/"); - const string symbol = working.substr(last_slash+1); - if ( (symbol[0] >= 'a' && symbol[0] <= 'z') - || (symbol[0] >= 'A' && symbol[0] <= 'Z') ) - return Path::nameify(symbol); - else - working = working.substr(0, last_slash); - } - - return "lv2_symbol"; -} - - -const string -LV2Plugin::name() const -{ - if (_name) - return slv2_value_as_string(_name); - else - return "(no name)"; -} - - -NodeImpl* -LV2Plugin::instantiate(const string& name, - bool polyphonic, - Ingen::PatchImpl* parent, - Engine& engine) -{ - SampleCount srate = engine.audio_driver()->sample_rate(); - SampleCount buffer_size = engine.audio_driver()->buffer_size(); - - load(); // FIXME: unload at some point - - Glib::Mutex::Lock lock(engine.world()->rdf_world->mutex()); - LV2Node* n = new LV2Node(this, name, polyphonic, parent, srate, buffer_size); - - if ( ! n->instantiate() ) { - delete n; - n = NULL; - } - - return n; -} - - -void -LV2Plugin::slv2_plugin(SLV2Plugin p) -{ - _slv2_plugin = p; - if (_name) - slv2_value_free(_name); - _name = slv2_plugin_get_name(_slv2_plugin); -} - - -} // namespace Ingen diff --git a/src/libs/engine/LV2Plugin.hpp b/src/libs/engine/LV2Plugin.hpp deleted file mode 100644 index c9bc4ff9..00000000 --- a/src/libs/engine/LV2Plugin.hpp +++ /dev/null @@ -1,84 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef LV2PLUGIN_H -#define LV2PLUGIN_H - -#include CONFIG_H_PATH - -#ifndef HAVE_SLV2 -#error "This file requires SLV2, but HAVE_SLV2 is not defined. Please report." -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include "types.hpp" -#include "PluginImpl.hpp" -#include "LV2Info.hpp" - -using std::string; -using Ingen::Shared::Plugin; - - -namespace Ingen { - -class PatchImpl; -class NodeImpl; - - -/** Implementation of an LV2 plugin (loaded shared library). - */ -class LV2Plugin : public PluginImpl -{ -public: - LV2Plugin(SharedPtr lv2_info, const string& uri) - : PluginImpl(Plugin::LV2, uri) - , _name(NULL) - , _slv2_plugin(NULL) - , _lv2_info(lv2_info) - {} - - NodeImpl* instantiate(const string& name, - bool polyphonic, - Ingen::PatchImpl* parent, - Engine& engine); - - const string symbol() const; - const string name() const; - - SharedPtr lv2_info() const { return _lv2_info; } - - SLV2Plugin slv2_plugin() const { return _slv2_plugin; } - void slv2_plugin(SLV2Plugin p); - -private: - SLV2Value _name; - SLV2Plugin _slv2_plugin; - SharedPtr _lv2_info; -}; - - -} // namespace Ingen - -#endif // LV2PLUGIN_H - diff --git a/src/libs/engine/Makefile.am b/src/libs/engine/Makefile.am deleted file mode 100644 index 3c464de3..00000000 --- a/src/libs/engine/Makefile.am +++ /dev/null @@ -1,205 +0,0 @@ -SUBDIRS = events - -MAINTAINERCLEANFILES = Makefile.in - -moduledir = $(libdir)/ingen - -module_LTLIBRARIES = libingen_engine.la -libingen_engine_la_CXXFLAGS = \ - @GLIBMM_CFLAGS@ \ - @INGEN_CFLAGS@ \ - @JACK_CFLAGS@ \ - @LIBLO_CFLAGS@ \ - @RAUL_CFLAGS@ \ - @REDLANDMM_CFLAGS@ \ - @SLV2_CFLAGS@ \ - @SOUP_CFLAGS@ - -libingen_engine_la_LDFLAGS = -no-undefined -module -avoid-version -libingen_engine_la_LIBADD = \ - ../shared/libingen_shared.la \ - ../module/libingen_module.la \ - @GLIBMM_LIBS@ \ - @JACK_LIBS@ \ - @LIBLO_LIBS@ \ - @RAUL_LIBS@ \ - @REDLANDMM_LIBS@ \ - @SLV2_LIBS@ \ - @SOUP_LIBS@ - -AM_CFLAGS=-std=c99 - -libingen_engine_la_SOURCES = \ - AudioBuffer.cpp \ - AudioBuffer.hpp \ - AudioDriver.hpp \ - Buffer.cpp \ - Buffer.hpp \ - ClientBroadcaster.cpp \ - ClientBroadcaster.hpp \ - CompiledPatch.hpp \ - ConnectionImpl.cpp \ - ConnectionImpl.hpp \ - Driver.hpp \ - DuplexPort.cpp \ - DuplexPort.hpp \ - Engine.cpp \ - Engine.hpp \ - EngineStore.cpp \ - EngineStore.hpp \ - Event.cpp \ - Event.hpp \ - EventBuffer.cpp \ - EventBuffer.hpp \ - EventSink.cpp \ - EventSink.hpp \ - EventSource.hpp \ - GraphObjectImpl.cpp \ - GraphObjectImpl.hpp \ - InputPort.cpp \ - InputPort.hpp \ - InternalPlugin.cpp \ - InternalPlugin.hpp \ - JackAudioDriver.cpp \ - JackAudioDriver.hpp \ - JackMidiDriver.cpp \ - JackMidiDriver.hpp \ - LADSPAPlugin.cpp \ - LADSPAPlugin.hpp \ - LV2Info.cpp \ - LV2Info.hpp \ - LV2Plugin.cpp \ - LV2Plugin.hpp \ - MessageContext.cpp \ - MessageContext.hpp \ - MidiControlNode.cpp \ - MidiControlNode.hpp \ - MidiDriver.hpp \ - MidiNoteNode.cpp \ - MidiNoteNode.hpp \ - MidiTriggerNode.cpp \ - MidiTriggerNode.hpp \ - NodeBase.cpp \ - NodeBase.hpp \ - NodeFactory.cpp \ - NodeFactory.hpp \ - NodeImpl.hpp \ - OSCClientSender.cpp \ - OSCClientSender.hpp \ - OSCDriver.hpp \ - OSCEngineReceiver.cpp \ - OSCEngineReceiver.hpp \ - ObjectSender.cpp \ - ObjectSender.hpp \ - OutputPort.cpp \ - OutputPort.hpp \ - PatchImpl.cpp \ - PatchImpl.hpp \ - PatchPlugin.hpp \ - PluginImpl.cpp \ - PluginImpl.hpp \ - PortImpl.cpp \ - PortImpl.hpp \ - PostProcessor.cpp \ - PostProcessor.hpp \ - ProcessContext.hpp \ - ProcessSlave.cpp \ - ProcessSlave.hpp \ - QueuedEngineInterface.cpp \ - QueuedEngineInterface.hpp \ - QueuedEvent.cpp \ - QueuedEvent.hpp \ - QueuedEventSource.cpp \ - QueuedEventSource.hpp \ - Responder.hpp \ - ThreadManager.hpp \ - TransportNode.cpp \ - TransportNode.hpp \ - engine.cpp \ - engine.hpp \ - events.hpp \ - events/AllNotesOffEvent.cpp \ - events/AllNotesOffEvent.hpp \ - events/ClearPatchEvent.cpp \ - events/ClearPatchEvent.hpp \ - events/ConnectionEvent.cpp \ - events/ConnectionEvent.hpp \ - events/CreateNodeEvent.cpp \ - events/CreateNodeEvent.hpp \ - events/CreatePatchEvent.cpp \ - events/CreatePatchEvent.hpp \ - events/CreatePortEvent.cpp \ - events/CreatePortEvent.hpp \ - events/DeactivateEvent.cpp \ - events/DeactivateEvent.hpp \ - events/DestroyEvent.cpp \ - events/DestroyEvent.hpp \ - events/DisconnectAllEvent.cpp \ - events/DisconnectAllEvent.hpp \ - events/DisconnectionEvent.cpp \ - events/DisconnectionEvent.hpp \ - events/EnablePatchEvent.cpp \ - events/EnablePatchEvent.hpp \ - events/LoadPluginsEvent.cpp \ - events/LoadPluginsEvent.hpp \ - events/MidiLearnEvent.cpp \ - events/MidiLearnEvent.hpp \ - events/NoteEvent.cpp \ - events/NoteEvent.hpp \ - events/PingQueuedEvent.hpp \ - events/RegisterClientEvent.cpp \ - events/RegisterClientEvent.hpp \ - events/RenameEvent.cpp \ - events/RenameEvent.hpp \ - events/RequestAllObjectsEvent.cpp \ - events/RequestAllObjectsEvent.hpp \ - events/RequestMetadataEvent.cpp \ - events/RequestMetadataEvent.hpp \ - events/RequestObjectEvent.cpp \ - events/RequestObjectEvent.hpp \ - events/RequestPluginEvent.cpp \ - events/RequestPluginEvent.hpp \ - events/RequestPluginsEvent.cpp \ - events/RequestPluginsEvent.hpp \ - events/RequestPortValueEvent.cpp \ - events/RequestPortValueEvent.hpp \ - events/SendPortActivityEvent.cpp \ - events/SendPortActivityEvent.hpp \ - events/SendPortValueEvent.cpp \ - events/SendPortValueEvent.hpp \ - events/SetMetadataEvent.cpp \ - events/SetMetadataEvent.hpp \ - events/SetPolyphonicEvent.cpp \ - events/SetPolyphonicEvent.hpp \ - events/SetPolyphonyEvent.cpp \ - events/SetPolyphonyEvent.hpp \ - events/SetPortValueEvent.cpp \ - events/SetPortValueEvent.hpp \ - events/UnregisterClientEvent.cpp \ - events/UnregisterClientEvent.hpp \ - jack_compat.h \ - lv2_contexts.h \ - tuning.hpp \ - types.hpp \ - util.hpp - -if WITH_LADSPA -libingen_engine_la_SOURCES += \ - LADSPANode.hpp \ - LADSPANode.cpp -endif - -if WITH_LV2 -libingen_engine_la_SOURCES += \ - LV2Node.hpp \ - LV2Node.cpp -endif - -if WITH_SOUP -libingen_engine_la_SOURCES += \ - HTTPEngineReceiver.cpp \ - HTTPEngineReceiver.hpp -endif - - - diff --git a/src/libs/engine/MessageContext.cpp b/src/libs/engine/MessageContext.cpp deleted file mode 100644 index 30f04b05..00000000 --- a/src/libs/engine/MessageContext.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2008 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "MessageContext.hpp" -#include "NodeImpl.hpp" - -namespace Ingen { - -void -MessageContext::run(NodeImpl* node) -{ - uint32_t outputs; - node->message_process(*this, &outputs); - - // Don't care what the plugin output, yet... -} - -} // namespace Ingen diff --git a/src/libs/engine/MessageContext.hpp b/src/libs/engine/MessageContext.hpp deleted file mode 100644 index 31531521..00000000 --- a/src/libs/engine/MessageContext.hpp +++ /dev/null @@ -1,51 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007-2008 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef MESSAGECONTEXT_H -#define MESSAGECONTEXT_H - -#include "EventSink.hpp" -#include "Context.hpp" - -namespace Ingen { - -class NodeImpl; - -/** Context of a message_process() call. - * - * The message context is a non-hard-realtime thread used to execute things - * that can take too long to execute in an audio thread, and do sloppy timed - * event propagation and scheduling. Interface to plugins via the - * LV2 contexts extension. - * - * \ingroup engine - */ -class MessageContext : public Context -{ -public: - MessageContext(Engine& engine) - : Context(engine, MESSAGE) - {} - - void run(NodeImpl* node); -}; - - -} // namespace Ingen - -#endif // MESSAGECONTEXT_H - diff --git a/src/libs/engine/MidiControlNode.cpp b/src/libs/engine/MidiControlNode.cpp deleted file mode 100644 index bfe0e57e..00000000 --- a/src/libs/engine/MidiControlNode.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include "MidiControlNode.hpp" -#include "PostProcessor.hpp" -#include "MidiLearnEvent.hpp" -#include "InputPort.hpp" -#include "OutputPort.hpp" -#include "InternalPlugin.hpp" -#include "AudioBuffer.hpp" -#include "ProcessContext.hpp" -#include "EventBuffer.hpp" -#include "util.hpp" - -namespace Ingen { - - -MidiControlNode::MidiControlNode(const string& path, - bool polyphonic, - PatchImpl* parent, - SampleRate srate, - size_t buffer_size) - : NodeBase(new InternalPlugin(NS_INGEN "control_node", "controller", "MIDI Controller") - , path, false, parent, srate, buffer_size) - , _learning(false) -{ - _ports = new Raul::Array(6); - - _midi_in_port = new InputPort(this, "input", 0, 1, DataType::EVENT, Atom(), _buffer_size); - _ports->at(0) = _midi_in_port; - - _param_port = new InputPort(this, "controller", 1, 1, DataType::CONTROL, 0.0f, 1); - _param_port->set_variable("ingen:minimum", 0.0f); - _param_port->set_variable("ingen:maximum", 127.0f); - _param_port->set_variable("ingen:integer", true); - _ports->at(1) = _param_port; - - _log_port = new InputPort(this, "logarithmic", 2, 1, DataType::CONTROL, 0.0f, 1); - _log_port->set_variable("ingen:toggled", true); - _ports->at(2) = _log_port; - - _min_port = new InputPort(this, "minimum", 3, 1, DataType::CONTROL, 0.0f, 1); - _ports->at(3) = _min_port; - - _max_port = new InputPort(this, "maximum", 4, 1, DataType::CONTROL, 1.0f, 1); - _ports->at(4) = _max_port; - - _audio_port = new OutputPort(this, "ar_output", 5, 1, DataType::AUDIO, 0.0f, _buffer_size); - _ports->at(5) = _audio_port; -} - - -void -MidiControlNode::process(ProcessContext& context) -{ - NodeBase::pre_process(context); - - uint32_t frames = 0; - uint32_t subframes = 0; - uint16_t type = 0; - uint16_t size = 0; - uint8_t* buf = NULL; - - EventBuffer* const midi_in = (EventBuffer*)_midi_in_port->buffer(0); - assert(midi_in->this_nframes() == context.nframes()); - - while (midi_in->get_event(&frames, &subframes, &type, &size, &buf)) { - // FIXME: type - if (size >= 3 && (buf[0] & 0xF0) == MIDI_CMD_CONTROL) - control(context, buf[1], buf[2], frames + context.start()); - - midi_in->increment(); - } - - NodeBase::post_process(context); -} - - -void -MidiControlNode::control(ProcessContext& context, uchar control_num, uchar val, FrameTime time) -{ - assert(time - context.start() < _buffer_size); - - Sample scaled_value; - - const Sample nval = (val / 127.0f); // normalized [0, 1] - - if (_learning) { - assert(false); // FIXME FIXME FIXME -#if 0 - assert(_learn_event != NULL); - _param_port->set_value(control_num, offset); - assert(_param_port->buffer(0)->value_at(0) == control_num); - _learn_event->set_value(control_num); - _learn_event->execute(offset); - //Engine::instance().post_processor()->push(_learn_event); - //Engine::instance().post_processor()->whip(); - _learning = false; - _learn_event = NULL; -#endif - } - - const Sample min_port_val = ((AudioBuffer*)_min_port->buffer(0))->value_at(0); - const Sample max_port_val = ((AudioBuffer*)_max_port->buffer(0))->value_at(0); - const Sample log_port_val = ((AudioBuffer*)_log_port->buffer(0))->value_at(0); - - if (log_port_val > 0.0f) { - // haaaaack, stupid negatives and logarithms - Sample log_offset = 0; - if (min_port_val < 0) - log_offset = fabs(min_port_val); - const Sample min = log(min_port_val + 1 + log_offset); - const Sample max = log(max_port_val + 1 + log_offset); - scaled_value = expf(nval * (max - min) + min) - 1 - log_offset; - } else { - scaled_value = ((nval) * (max_port_val - min_port_val)) + min_port_val; - } - - if (control_num == ((AudioBuffer*)_param_port->buffer(0))->value_at(0)) - ((AudioBuffer*)_audio_port->buffer(0))->set_value(scaled_value, context.start(), time); -} - - -} // namespace Ingen - diff --git a/src/libs/engine/MidiControlNode.hpp b/src/libs/engine/MidiControlNode.hpp deleted file mode 100644 index 2f0496a3..00000000 --- a/src/libs/engine/MidiControlNode.hpp +++ /dev/null @@ -1,65 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef MIDICONTROLNODE_H -#define MIDICONTROLNODE_H - -#include -#include "NodeBase.hpp" - -namespace Ingen { - -class MidiLearnResponseEvent; -class InputPort; -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 NodeBase -{ -public: - MidiControlNode(const std::string& path, bool polyphonic, PatchImpl* parent, SampleRate srate, size_t buffer_size); - - void process(ProcessContext& context); - - void control(ProcessContext& context, uchar control_num, uchar val, FrameTime time); - - void learn(MidiLearnResponseEvent* ev) { _learning = true; _learn_event = ev; } - -private: - bool _learning; - - InputPort* _midi_in_port; - InputPort* _param_port; - InputPort* _log_port; - InputPort* _min_port; - InputPort* _max_port; - OutputPort* _audio_port; - - MidiLearnResponseEvent* _learn_event; -}; - - -} // namespace Ingen - -#endif // MIDICONTROLNODE_H diff --git a/src/libs/engine/MidiDriver.hpp b/src/libs/engine/MidiDriver.hpp deleted file mode 100644 index e268124b..00000000 --- a/src/libs/engine/MidiDriver.hpp +++ /dev/null @@ -1,100 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef MIDIDRIVER_H -#define MIDIDRIVER_H - -#include -#include "types.hpp" -#include "Driver.hpp" -#include "EventBuffer.hpp" -#include "ProcessContext.hpp" - -namespace Ingen { - -class ProcessContext; - - -/** Midi driver abstract base class. - * - * \ingroup engine - */ -class MidiDriver : public Driver -{ -public: - MidiDriver() : Driver(DataType::EVENT) {} - - /** Prepare input for the specified (upcoming) cycle. - * - * Realtime safe, run in audio thread before executing the graph for a cycle. - */ - virtual void pre_process(ProcessContext& context) = 0; - - /** Prepare output for the specified (just completed) cycle. - * - * Realtime safe, run in audio thread after executing the graph for a cycle. - */ - virtual void post_process(ProcessContext& context) = 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() { - std::cout << "[DummyMidiDriver] Started Dummy MIDI driver." << std::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(DuplexPort* patch_port) { return NULL; } - - DriverPort* driver_port(const Raul::Path& path) { return NULL; } - - DriverPort* new_port(DuplexPort* patch_port) { return NULL; } - - void add_port(DriverPort* port) {} - DriverPort* remove_port(const Raul::Path& path) { return NULL; } - - void pre_process(ProcessContext& context) {} - void post_process(ProcessContext& context) {} -}; - - - -} // namespace Ingen - -#endif // MIDIDRIVER_H diff --git a/src/libs/engine/MidiNoteNode.cpp b/src/libs/engine/MidiNoteNode.cpp deleted file mode 100644 index ae7a7f0e..00000000 --- a/src/libs/engine/MidiNoteNode.cpp +++ /dev/null @@ -1,390 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include -#include "AudioBuffer.hpp" -#include "AudioDriver.hpp" -#include "InputPort.hpp" -#include "InternalPlugin.hpp" -#include "EventBuffer.hpp" -#include "MidiNoteNode.hpp" -#include "OutputPort.hpp" -#include "PatchImpl.hpp" -#include "ProcessContext.hpp" -#include "util.hpp" - -using namespace std; - -namespace Ingen { - - -MidiNoteNode::MidiNoteNode(const string& path, bool polyphonic, PatchImpl* parent, SampleRate srate, size_t buffer_size) - : NodeBase(new InternalPlugin(NS_INGEN "note_node", "note", "MIDI Note"), - path, polyphonic, parent, srate, buffer_size) - , _voices(new Raul::Array(_polyphony)) - , _prepared_voices(NULL) - , _sustain(false) -{ - _ports = new Raul::Array(5); - - _midi_in_port = new InputPort(this, "input", 0, 1, DataType::EVENT, Atom(), _buffer_size); - _ports->at(0) = _midi_in_port; - - _freq_port = new OutputPort(this, "frequency", 1, _polyphony, DataType::AUDIO, 440.0f, _buffer_size); - _ports->at(1) = _freq_port; - - _vel_port = new OutputPort(this, "velocity", 2, _polyphony, DataType::AUDIO, 0.0f, _buffer_size); - _vel_port->set_variable("ingen:minimum", 0.0f); - _vel_port->set_variable("ingen:maximum", 1.0f); - _ports->at(2) = _vel_port; - - _gate_port = new OutputPort(this, "gate", 3, _polyphony, DataType::AUDIO, 0.0f, _buffer_size); - _gate_port->set_variable("ingen:toggled", true); - _ports->at(3) = _gate_port; - - _trig_port = new OutputPort(this, "trigger", 4, _polyphony, DataType::AUDIO, 0.0f, _buffer_size); - _trig_port->set_variable("ingen:toggled", true); - _ports->at(4) = _trig_port; -} - - -MidiNoteNode::~MidiNoteNode() -{ - delete _voices; -} - - -bool -MidiNoteNode::prepare_poly(uint32_t poly) -{ - if (!_polyphonic) - return true; - - NodeBase::prepare_poly(poly); - - if (_prepared_voices && poly <= _prepared_voices->size()) - return true; - - _prepared_voices = new Raul::Array(poly, *_voices); - - return true; -} - - -bool -MidiNoteNode::apply_poly(Raul::Maid& maid, uint32_t poly) -{ - if (!_polyphonic) - return true; - - NodeBase::apply_poly(maid, poly); - - if (_prepared_voices) { - assert(poly <= _prepared_voices->size()); - maid.push(_voices); - _voices = _prepared_voices; - _prepared_voices = NULL; - } - - _polyphony = poly; - assert(_voices->size() >= _polyphony); - - return true; -} - - -void -MidiNoteNode::process(ProcessContext& context) -{ - NodeBase::pre_process(context); - - uint32_t frames = 0; - uint32_t subframes = 0; - uint16_t type = 0; - uint16_t size = 0; - unsigned char* buf = NULL; - - EventBuffer* const midi_in = (EventBuffer*)_midi_in_port->buffer(0); - assert(midi_in->this_nframes() == context.nframes()); - - //cerr << path() << " # input events: " << midi_in->event_count() << endl; - - if (midi_in->event_count() > 0) - while (midi_in->get_event(&frames, &subframes, &type, &size, &buf)) { - - /*cout << "EVENT TYPE " << type << " @ " << frames << "." << subframes << ": "; - for (uint16_t i = 0; i < size; ++i) - cout << (int)((char)buf[i]) << " "; - cout << endl;*/ - - const FrameTime time = context.start() + (FrameTime)frames; - - if (size >= 3) { - switch (buf[0] & 0xF0) { - case MIDI_CMD_NOTE_ON: - if (buf[2] == 0) - note_off(context, buf[1], time); - else - note_on(context, buf[1], buf[2], time); - break; - case MIDI_CMD_NOTE_OFF: - note_off(context, buf[1], time); - break; - case MIDI_CMD_CONTROL: - switch (buf[1]) { - case MIDI_CTL_ALL_NOTES_OFF: - case MIDI_CTL_ALL_SOUNDS_OFF: - all_notes_off(context, time); - break; - case MIDI_CTL_SUSTAIN: - if (buf[2] > 63) - sustain_on(context, time); - else - sustain_off(context, time); - break; - case MIDI_CMD_BENDER: - // ? - break; - default: - //cerr << "Ignored controller " << buf[1] << endl; - break; - } - break; - default: - //fprintf(stderr, "Unknown (size %d) MIDI event %X\n", size, buf[0]); - break; - } - } else { - //fprintf(stderr, "Unknown (size %d) MIDI event %X\n", size, buf[0]); - } - - if (midi_in->increment() == midi_in->this_nframes()) - break; - } - - NodeBase::post_process(context); -} - - -void -MidiNoteNode::note_on(ProcessContext& context, uchar note_num, uchar velocity, FrameTime time) -{ - assert(time >= context.start() && time <= context.end()); - assert(time - context.start() < _buffer_size); - assert(note_num <= 127); - - Key* key = &_keys[note_num]; - Voice* voice = NULL; - uint32_t voice_num = 0; - - if (key->state != Key::OFF) { - //cerr << "[MidiNoteNode] Double midi note received" << endl; - return; - } - - // Look for free voices - for (uint32_t i=0; i < _polyphony; ++i) { - if ((*_voices)[i].state == Voice::Voice::FREE) { - voice = &(*_voices)[i]; - voice_num = i; - break; - } - } - - // If we didn't find a free one, steal the oldest - if (voice == NULL) { - voice_num = 0; - voice = &(*_voices)[0]; - jack_nframes_t oldest_time = (*_voices)[0].time; - for (uint32_t i=1; i < _polyphony; ++i) { - if ((*_voices)[i].time < oldest_time) { - voice = &(*_voices)[i]; - voice_num = i; - oldest_time = voice->time; - } - } - } - assert(voice != NULL); - assert(voice == &(*_voices)[voice_num]); - - /*cerr << "[MidiNoteNode] Note " << (int)note_num << " on @ " << time - << ". Voice " << voice_num << " / " << _polyphony << endl;*/ - - // Update stolen key, if applicable - if (voice->state == Voice::Voice::ACTIVE) { - assert(_keys[voice->note].state == Key::ON_ASSIGNED); - assert(_keys[voice->note].voice == voice_num); - _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; - - // Trigger voice - voice->state = Voice::Voice::ACTIVE; - voice->note = note_num; - voice->time = time; - - assert(_keys[voice->note].state == Key::Key::ON_ASSIGNED); - assert(_keys[voice->note].voice == voice_num); - - ((AudioBuffer*)_freq_port->buffer(voice_num))->set_value(note_to_freq(note_num), context.start(), time); - ((AudioBuffer*)_vel_port->buffer(voice_num))->set_value(velocity/127.0, context.start(), time); - ((AudioBuffer*)_gate_port->buffer(voice_num))->set_value(1.0f, context.start(), time); - - // trigger (one sample) - ((AudioBuffer*)_trig_port->buffer(voice_num))->set_value(1.0f, context.start(), time); - ((AudioBuffer*)_trig_port->buffer(voice_num))->set_value(0.0f, context.start(), time + 1); - - assert(key->state == Key::Key::ON_ASSIGNED); - assert(voice->state == Voice::Voice::ACTIVE); - assert(key->voice == voice_num); - assert((*_voices)[key->voice].note == note_num); -} - - -void -MidiNoteNode::note_off(ProcessContext& context, uchar note_num, FrameTime time) -{ - assert(time >= context.start() && time <= context.end()); - assert(time - context.start() < _buffer_size); - - Key* key = &_keys[note_num]; - - //cerr << "[MidiNoteNode] Note " << (int)note_num << " off @ " << time << endl; - - if (key->state == Key::ON_ASSIGNED) { - // Assigned key, turn off voice and key - if ((*_voices)[key->voice].state == Voice::ACTIVE) { - assert((*_voices)[key->voice].note == note_num); - - if ( ! _sustain) { - //cerr << "... free voice " << key->voice << endl; - free_voice(context, key->voice, time); - } else { - //cerr << "... hold voice " << key->voice << endl; - (*_voices)[key->voice].state = Voice::HOLDING; - } - - } else { -#ifndef NDEBUG - cerr << "WARNING: Assigned key, but voice not active" << endl; -#endif - } - } - - key->state = Key::OFF; -} - - -void -MidiNoteNode::free_voice(ProcessContext& context, uint32_t voice, FrameTime time) -{ - assert(time >= context.start() && time <= context.end()); - assert(time - context.start() < _buffer_size); - - // 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 (_keys[i].state == Key::ON_UNASSIGNED) { - if (replace_key == NULL || _keys[i].time > replace_key->time) { - replace_key = &_keys[i]; - replace_key_num = i; - } - } - } - - if (replace_key != NULL) { // Found a key to assign to freed voice - assert(&_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 - ((AudioBuffer*)_freq_port->buffer(voice))->set_value(note_to_freq(replace_key_num), context.start(), time); - - replace_key->state = Key::ON_ASSIGNED; - replace_key->voice = voice; - _keys[(*_voices)[voice].note].state = Key::ON_UNASSIGNED; - (*_voices)[voice].note = replace_key_num; - (*_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; - ((AudioBuffer*)_gate_port->buffer(voice))->set_value(0.0f, context.start(), time); - (*_voices)[voice].state = Voice::FREE; - } -} - - -void -MidiNoteNode::all_notes_off(ProcessContext& context, FrameTime time) -{ - assert(time >= context.start() && time <= context.end()); - assert(time - context.start() < _buffer_size); - - //cerr << "All notes off @ " << offset << endl; - - // FIXME: set all keys to Key::OFF? - - for (uint32_t i = 0; i < _polyphony; ++i) { - ((AudioBuffer*)_gate_port->buffer(i))->set_value(0.0f, context.start(), time); - (*_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(ProcessContext& context, FrameTime time) -{ - _sustain = true; -} - - -void -MidiNoteNode::sustain_off(ProcessContext& context, FrameTime time) -{ - assert(time >= context.start() && time <= context.end()); - assert(time - context.start() < _buffer_size); - - _sustain = false; - - for (uint32_t i=0; i < _polyphony; ++i) - if ((*_voices)[i].state == Voice::HOLDING) - free_voice(context, i, time); -} - - -} // namespace Ingen - diff --git a/src/libs/engine/MidiNoteNode.hpp b/src/libs/engine/MidiNoteNode.hpp deleted file mode 100644 index eebcbda6..00000000 --- a/src/libs/engine/MidiNoteNode.hpp +++ /dev/null @@ -1,88 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef MIDINOTENODE_H -#define MIDINOTENODE_H - -#include -#include "types.hpp" -#include "NodeBase.hpp" - -namespace Ingen { - -class InputPort; -class OutputPort; - - -/** MIDI note input node. - * - * For pitched instruments like keyboard, etc. - * - * \ingroup engine - */ -class MidiNoteNode : public NodeBase -{ -public: - MidiNoteNode(const std::string& path, bool polyphonic, PatchImpl* parent, SampleRate srate, size_t buffer_size); - ~MidiNoteNode(); - - bool prepare_poly(uint32_t poly); - bool apply_poly(Raul::Maid& maid, uint32_t poly); - - void process(ProcessContext& context); - - void note_on(ProcessContext& context, uchar note_num, uchar velocity, FrameTime time); - void note_off(ProcessContext& context, uchar note_num, FrameTime time); - void all_notes_off(ProcessContext& context, FrameTime time); - - void sustain_on(ProcessContext& context, FrameTime time); - void sustain_off(ProcessContext& context, FrameTime time); - -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; uint32_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(ProcessContext& context, uint32_t voice, FrameTime time); - - Raul::Array* _voices; - Raul::Array* _prepared_voices; - Key _keys[128]; - bool _sustain; ///< Whether or not hold pedal is depressed - - InputPort* _midi_in_port; - OutputPort* _freq_port; - OutputPort* _vel_port; - OutputPort* _gate_port; - OutputPort* _trig_port; -}; - - -} // namespace Ingen - -#endif // MIDINOTENODE_H diff --git a/src/libs/engine/MidiTriggerNode.cpp b/src/libs/engine/MidiTriggerNode.cpp deleted file mode 100644 index aa2c272f..00000000 --- a/src/libs/engine/MidiTriggerNode.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include "MidiTriggerNode.hpp" -#include "AudioBuffer.hpp" -#include "InputPort.hpp" -#include "OutputPort.hpp" -#include "InternalPlugin.hpp" -#include "ProcessContext.hpp" -#include "EventBuffer.hpp" -#include "util.hpp" - -using namespace std; - -namespace Ingen { - - -MidiTriggerNode::MidiTriggerNode(const string& path, bool polyphonic, PatchImpl* parent, SampleRate srate, size_t buffer_size) - : NodeBase(new InternalPlugin(NS_INGEN "trigger_node", "trigger", "MIDI Trigger"), - path, false, parent, srate, buffer_size) -{ - _ports = new Raul::Array(5); - - _midi_in_port = new InputPort(this, "input", 0, 1, DataType::EVENT, Atom(), _buffer_size); - _ports->at(0) = _midi_in_port; - - _note_port = new InputPort(this, "note", 1, 1, DataType::CONTROL, 60.0f, 1); - _note_port->set_variable("ingen:minimum", 0.0f); - _note_port->set_variable("ingen:maximum", 127.0f); - _note_port->set_variable("ingen:integer", true); - _ports->at(1) = _note_port; - - _gate_port = new OutputPort(this, "gate", 2, 1, DataType::AUDIO, 0.0f, _buffer_size); - _ports->at(2) = _gate_port; - - _trig_port = new OutputPort(this, "trigger", 3, 1, DataType::AUDIO, 0.0f, _buffer_size); - _ports->at(3) = _trig_port; - - _vel_port = new OutputPort(this, "velocity", 4, 1, DataType::AUDIO, 0.0f, _buffer_size); - _ports->at(4) = _vel_port; -} - - -void -MidiTriggerNode::process(ProcessContext& context) -{ - NodeBase::pre_process(context); - - uint32_t frames = 0; - uint32_t subframes = 0; - uint16_t type = 0; - uint16_t size = 0; - uint8_t* buf = NULL; - - EventBuffer* const midi_in = (EventBuffer*)_midi_in_port->buffer(0); - assert(midi_in->this_nframes() == context.nframes()); - - while (midi_in->get_event(&frames, &subframes, &type, &size, &buf)) { - const FrameTime time = context.start() + (FrameTime)frames; - - if (size >= 3) { - switch (buf[0] & 0xF0) { - case MIDI_CMD_NOTE_ON: - if (buf[2] == 0) - note_off(context, buf[1], time); - else - note_on(context, buf[1], buf[2], time); - break; - case MIDI_CMD_NOTE_OFF: - note_off(context, buf[1], time); - break; - case MIDI_CMD_CONTROL: - if (buf[1] == MIDI_CTL_ALL_NOTES_OFF - || buf[1] == MIDI_CTL_ALL_SOUNDS_OFF) - ((AudioBuffer*)_gate_port->buffer(0))->set_value(0.0f, context.start(), time); - default: - break; - } - } - - midi_in->increment(); - } - - NodeBase::post_process(context); -} - - -void -MidiTriggerNode::note_on(ProcessContext& context, uchar note_num, uchar velocity, FrameTime time) -{ - assert(time >= context.start() && time <= context.end()); - assert(time - context.start() < _buffer_size); - - /*cerr << "[MidiTriggerNode] " << path() << " Note " << (int)note_num << " on @ " << time << endl;*/ - - Sample filter_note = ((AudioBuffer*)_note_port->buffer(0))->value_at(0); - if (filter_note >= 0.0 && filter_note < 127.0 && (note_num == (uchar)filter_note)) { - ((AudioBuffer*)_gate_port->buffer(0))->set_value(1.0f, context.start(), time); - ((AudioBuffer*)_trig_port->buffer(0))->set_value(1.0f, context.start(), time); - ((AudioBuffer*)_trig_port->buffer(0))->set_value(0.0f, context.start(), time + 1); - ((AudioBuffer*)_vel_port->buffer(0))->set_value(velocity / 127.0f, context.start(), time); - assert(((AudioBuffer*)_trig_port->buffer(0))->data()[time - context.start()] == 1.0f); - } -} - - -void -MidiTriggerNode::note_off(ProcessContext& context, uchar note_num, FrameTime time) -{ - assert(time >= context.start() && time <= context.end()); - assert(time - context.start() < _buffer_size); - - if (note_num == lrintf(((AudioBuffer*)_note_port->buffer(0))->value_at(0))) - ((AudioBuffer*)_gate_port->buffer(0))->set_value(0.0f, context.start(), time); -} - - -} // namespace Ingen - diff --git a/src/libs/engine/MidiTriggerNode.hpp b/src/libs/engine/MidiTriggerNode.hpp deleted file mode 100644 index ba96589b..00000000 --- a/src/libs/engine/MidiTriggerNode.hpp +++ /dev/null @@ -1,61 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef MIDITRIGGERNODE_H -#define MIDITRIGGERNODE_H - -#include -#include "NodeBase.hpp" - -namespace Ingen { - -class InputPort; -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 NodeBase -{ -public: - MidiTriggerNode(const std::string& path, bool polyphonic, PatchImpl* parent, SampleRate srate, size_t buffer_size); - - void process(ProcessContext& context); - - void note_on(ProcessContext& context, uchar note_num, uchar velocity, FrameTime time); - void note_off(ProcessContext& context, uchar note_num, FrameTime time); - -private: - InputPort* _midi_in_port; - InputPort* _note_port; - OutputPort* _gate_port; - OutputPort* _trig_port; - OutputPort* _vel_port; -}; - - -} // namespace Ingen - -#endif // MIDITRIGGERNODE_H diff --git a/src/libs/engine/NodeBase.cpp b/src/libs/engine/NodeBase.cpp deleted file mode 100644 index bb4f0e5c..00000000 --- a/src/libs/engine/NodeBase.cpp +++ /dev/null @@ -1,230 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "NodeBase.hpp" -#include -#include -#include -#include -#include -#include "util.hpp" -#include "PluginImpl.hpp" -#include "ClientBroadcaster.hpp" -#include "PortImpl.hpp" -#include "PatchImpl.hpp" -#include "EngineStore.hpp" -#include "ThreadManager.hpp" - -using namespace std; - -namespace Ingen { - - -NodeBase::NodeBase(PluginImpl* plugin, const string& name, bool polyphonic, PatchImpl* parent, SampleRate srate, size_t buffer_size) - : NodeImpl(parent, name, polyphonic) - , _plugin(plugin) - , _polyphony((polyphonic && parent) ? parent->internal_polyphony() : 1) - , _srate(srate) - , _buffer_size(buffer_size) - , _activated(false) - , _traversed(false) - , _input_ready(1) - , _process_lock(0) - , _n_inputs_ready(0) - , _ports(NULL) - , _providers(new Raul::List()) - , _dependants(new Raul::List()) -{ - assert(_plugin); - assert(_polyphony > 0); - assert(_parent == NULL || (_polyphony == parent->internal_polyphony() || _polyphony == 1)); -} - - -NodeBase::~NodeBase() -{ - if (_activated) - deactivate(); - - delete _providers; - delete _dependants; -} - - -Port* -NodeBase::port(uint32_t index) const -{ - return (*_ports)[index]; -} - - -const Plugin* -NodeBase::plugin() const -{ - return _plugin; -} - - -void -NodeBase::activate() -{ - assert(ThreadManager::current_thread_id() == THREAD_PRE_PROCESS); - assert(!_activated); - _activated = true; -} - - -void -NodeBase::deactivate() -{ - // FIXME: Not true witn monolithic GUI/engine - //assert(ThreadManager::current_thread_id() == THREAD_POST_PROCESS); - assert(_activated); - _activated = false; -} - - -bool -NodeBase::prepare_poly(uint32_t poly) -{ - assert(ThreadManager::current_thread_id() == THREAD_PRE_PROCESS); - - if (!_polyphonic) - return true; - - if (_ports) - for (size_t i=0; i < _ports->size(); ++i) - _ports->at(i)->prepare_poly(poly); - - return true; -} - - -bool -NodeBase::apply_poly(Raul::Maid& maid, uint32_t poly) -{ - assert(ThreadManager::current_thread_id() == THREAD_PROCESS); - - if (!_polyphonic) - return true; - - for (size_t i=0; i < num_ports(); ++i) { - _ports->at(i)->apply_poly(maid, poly); - assert(_ports->at(i)->poly() == poly); - } - - for (uint32_t i=0; i < num_ports(); ++i) - for (uint32_t j=0; j < _polyphony; ++j) - set_port_buffer(j, i, _ports->at(i)->buffer(j)); - - return true; -} - - -void -NodeBase::set_buffer_size(size_t size) -{ - assert(ThreadManager::current_thread_id() == THREAD_PROCESS); - - _buffer_size = size; - - if (_ports) - for (size_t i=0; i < _ports->size(); ++i) - _ports->at(i)->set_buffer_size(size); -} - - -void -NodeBase::reset_input_ready() -{ - //cout << path() << " RESET" << endl; - _n_inputs_ready = 0; - _process_lock = 0; - _input_ready.reset(0); -} - - -bool -NodeBase::process_lock() -{ - return _process_lock.compare_and_exchange(0, 1); -} - - -void -NodeBase::process_unlock() -{ - _process_lock = 0; -} - - -void -NodeBase::wait_for_input(size_t num_providers) -{ - assert(ThreadManager::current_thread_id() == THREAD_PROCESS); - assert(_process_lock.get() == 1); - - while ((unsigned)_n_inputs_ready.get() < num_providers) { - //cout << path() << " WAITING " << _n_inputs_ready.get() << endl; - _input_ready.wait(); - //cout << path() << " CAUGHT SIGNAL" << endl; - //++_n_inputs_ready; - } - - //cout << path() << " READY" << endl; -} - - -void -NodeBase::signal_input_ready() -{ - assert(ThreadManager::current_thread_id() == THREAD_PROCESS); - //cout << path() << " SIGNAL" << endl; - ++_n_inputs_ready; - _input_ready.post(); -} - - -/** Prepare to run a cycle (in the audio thread) - */ -void -NodeBase::pre_process(ProcessContext& context) -{ - assert(ThreadManager::current_thread_id() == THREAD_PROCESS); - - // Mix down any ports with multiple inputs - for (size_t i=0; i < num_ports(); ++i) - _ports->at(i)->pre_process(context); -} - - -/** Prepare to run a cycle (in the audio thread) - */ -void -NodeBase::post_process(ProcessContext& context) -{ - assert(ThreadManager::current_thread_id() == THREAD_PROCESS); - - /* Write output ports */ - if (_ports) - for (size_t i=0; i < _ports->size(); ++i) - _ports->at(i)->post_process(context); -} - - -} // namespace Ingen - diff --git a/src/libs/engine/NodeBase.hpp b/src/libs/engine/NodeBase.hpp deleted file mode 100644 index e3710aa9..00000000 --- a/src/libs/engine/NodeBase.hpp +++ /dev/null @@ -1,132 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef NODEBASE_H -#define NODEBASE_H - -#include "types.hpp" -#include -#include -#include -#include -#include "interface/Port.hpp" -#include "NodeImpl.hpp" - -using std::string; - -namespace Ingen { - -class PluginImpl; -class PatchImpl; -class EngineStore; - -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 NodeImpl -{ -public: - NodeBase(PluginImpl* plugin, - const string& name, - bool poly, - PatchImpl* parent, - SampleRate rate, - size_t buffer_size); - - virtual ~NodeBase(); - - virtual void activate(); - virtual void deactivate(); - bool activated() { return _activated; } - - virtual bool prepare_poly(uint32_t poly); - virtual bool apply_poly(Raul::Maid& maid, uint32_t poly); - - virtual void reset_input_ready(); - virtual bool process_lock(); - virtual void process_unlock(); - virtual void wait_for_input(size_t num_providers); - virtual unsigned n_inputs_ready() const { return _n_inputs_ready.get(); } - - virtual void message_process(MessageContext& context, uint32_t* output) {} - - virtual void pre_process(ProcessContext& context); - virtual void process(ProcessContext& context) = 0; - virtual void post_process(ProcessContext& context); - - virtual void set_port_buffer(uint32_t voice, uint32_t port_num, Buffer* buf) {} - - virtual void set_buffer_size(size_t size); - - SampleRate sample_rate() const { return _srate; } - size_t buffer_size() const { return _buffer_size; } - uint32_t num_ports() const { return _ports ? _ports->size() : 0; } - uint32_t polyphony() const { return _polyphony; } - bool traversed() const { return _traversed; } - void traversed(bool b) { _traversed = b; } - - virtual Port* port(uint32_t index) const; - virtual PortImpl* port_impl(uint32_t index) const { return (*_ports)[index]; } - - /* These are NOT to be used in the audio thread! - * The providers and dependants in CompiledNode are for that - */ - - Raul::List* providers() { return _providers; } - void providers(Raul::List* l) { _providers = l; } - - Raul::List* dependants() { return _dependants; } - void dependants(Raul::List* l) { _dependants = l; } - - virtual const Plugin* plugin() const; - virtual PluginImpl* plugin_impl() const { return _plugin; } - virtual void plugin(PluginImpl* pi) { _plugin = pi; } - - /** A node's parent is always a patch, so static cast should be safe */ - inline PatchImpl* parent_patch() const { return (PatchImpl*)_parent; } - -protected: - virtual void signal_input_ready(); - - PluginImpl* _plugin; - - uint32_t _polyphony; - SampleRate _srate; - size_t _buffer_size; - bool _activated; - - bool _traversed; ///< Flag for process order algorithm - Raul::Semaphore _input_ready; ///< Parallelism: input ready signal - Raul::AtomicInt _process_lock; ///< Parallelism: Waiting on inputs 'lock' - Raul::AtomicInt _n_inputs_ready; ///< Parallelism: # input ready signals this cycle - Raul::Array* _ports; ///< Access in audio thread only - Raul::List* _providers; ///< Nodes connected to this one's input ports - Raul::List* _dependants; ///< Nodes this one's output ports are connected to -}; - - -} // namespace Ingen - -#endif // NODEBASE_H diff --git a/src/libs/engine/NodeFactory.cpp b/src/libs/engine/NodeFactory.cpp deleted file mode 100644 index c868d067..00000000 --- a/src/libs/engine/NodeFactory.cpp +++ /dev/null @@ -1,285 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include CONFIG_H_PATH -#include -#include -#include -#include -#include -#include -#include "module/World.hpp" -#include "NodeFactory.hpp" -#include "ThreadManager.hpp" -#include "MidiNoteNode.hpp" -#include "MidiTriggerNode.hpp" -#include "MidiControlNode.hpp" -#include "TransportNode.hpp" -#include "PatchImpl.hpp" -#include "InternalPlugin.hpp" -#ifdef HAVE_LADSPA -#include "LADSPANode.hpp" -#include "LADSPAPlugin.hpp" -#endif -#ifdef HAVE_SLV2 -#include -#include "LV2Plugin.hpp" -#include "LV2Node.hpp" -#endif - -using namespace std; - -namespace Ingen { - - -NodeFactory::NodeFactory(Ingen::Shared::World* world) - : _world(world) - , _has_loaded(false) -#ifdef HAVE_SLV2 - , _lv2_info(new LV2Info(world)) -#endif -{ -} - - -NodeFactory::~NodeFactory() -{ - for (Plugins::iterator i = _plugins.begin(); i != _plugins.end(); ++i) - if (i->second->type() != Plugin::Internal) - delete i->second; - - _plugins.clear(); -} - - -PluginImpl* -NodeFactory::plugin(const string& uri) -{ - const Plugins::const_iterator i = _plugins.find(uri); - return ((i != _plugins.end()) ? i->second : NULL); -} - - -/** DEPRECATED: Find a plugin by type, lib, label. - * - * Slow. Evil. Do not use. - */ -PluginImpl* -NodeFactory::plugin(const string& type, const string& lib, const string& label) -{ - if (type != "LADSPA" || lib == "" || label == "") - return NULL; - -#ifdef HAVE_LADSPA - for (Plugins::const_iterator i = _plugins.begin(); i != _plugins.end(); ++i) { - LADSPAPlugin* lp = dynamic_cast(i->second); - if (lp && lp->type_string() == type - && lp->library_name() == lib - && lp->label() == label) - return lp; - } -#endif - - cerr << "ERROR: Failed to find " << type << " plugin " << lib << " / " << label << endl; - - return NULL; -} - - -void -NodeFactory::load_plugins() -{ - assert(ThreadManager::current_thread_id() == THREAD_PRE_PROCESS); - - _world->rdf_world->mutex().lock(); - - // 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 (!_has_loaded) { - _plugins.clear(); // FIXME: assert empty? - - load_internal_plugins(); - -#ifdef HAVE_SLV2 - load_lv2_plugins(); -#endif - -#ifdef HAVE_LADSPA - load_ladspa_plugins(); -#endif - - _has_loaded = true; - } - - _world->rdf_world->mutex().unlock(); - - //cerr << "[NodeFactory] # Plugins: " << _plugins.size() << endl; -} - - -void -NodeFactory::load_internal_plugins() -{ - // This is a touch gross... - - PatchImpl* parent = new PatchImpl(*_world->local_engine, "dummy", 1, NULL, 1, 1, 1); - - NodeImpl* n = NULL; - n = new MidiNoteNode("foo", 1, parent, 1, 1); - _plugins.insert(make_pair(n->plugin_impl()->uri(), n->plugin_impl())); - delete n; - n = new MidiTriggerNode("foo", 1, parent, 1, 1); - _plugins.insert(make_pair(n->plugin_impl()->uri(), n->plugin_impl())); - delete n; - n = new MidiControlNode("foo", 1, parent, 1, 1); - _plugins.insert(make_pair(n->plugin_impl()->uri(), n->plugin_impl())); - delete n; - n = new TransportNode("foo", 1, parent, 1, 1); - _plugins.insert(make_pair(n->plugin_impl()->uri(), n->plugin_impl())); - delete n; - - delete parent; -} - - -#ifdef HAVE_SLV2 -/** Loads information about all LV2 plugins into internal plugin database. - */ -void -NodeFactory::load_lv2_plugins() -{ - SLV2Plugins plugins = slv2_world_get_all_plugins(_world->slv2_world); - - //cerr << "[NodeFactory] Found " << slv2_plugins_size(plugins) << " LV2 plugins:" << endl; - - for (unsigned i=0; i < slv2_plugins_size(plugins); ++i) { - - SLV2Plugin lv2_plug = slv2_plugins_get_at(plugins, i); - - const string uri(slv2_value_as_uri(slv2_plugin_get_uri(lv2_plug))); - -#ifndef NDEBUG - assert(_plugins.find(uri) == _plugins.end()); -#endif - - LV2Plugin* const plugin = new LV2Plugin(_lv2_info, uri); - - plugin->slv2_plugin(lv2_plug); - plugin->library_path(slv2_uri_to_path(slv2_value_as_uri( - slv2_plugin_get_library_uri(lv2_plug)))); - _plugins.insert(make_pair(uri, plugin)); - } - - slv2_plugins_free(_world->slv2_world, plugins); -} -#endif // HAVE_SLV2 - - -#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; - } - - // Yep, this should use an sstream alright.. - while (ladspa_path != "") { - const string 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))) { - - LADSPA_Descriptor_Function df = NULL; - LADSPA_Descriptor* descriptor = NULL; - - if (!strcmp(pfile->d_name, ".") || !strcmp(pfile->d_name, "..")) - continue; - - const string lib_path = dir +"/"+ pfile->d_name; - - // Ignore stupid libtool files. Kludge alert. - if (lib_path.substr(lib_path.length()-3) == ".la") { - //cerr << "WARNING: Skipping stupid libtool file " << pfile->d_name << endl; - continue; - } - - Glib::Module* plugin_library = new Glib::Module(lib_path, Glib::MODULE_BIND_LOCAL); - if (!plugin_library || !(*plugin_library)) { - cerr << "WARNING: Failed to load LADSPA library " << lib_path << endl; - continue; - } - - bool found = plugin_library->get_symbol("ladspa_descriptor", (void*&)df); - if (!found || !df) { - cerr << "WARNING: Non-LADSPA library found in LADSPA path: " << - lib_path << endl; - // Not a LADSPA plugin library - delete plugin_library; - continue; - } - - for (unsigned long i=0; (descriptor = (LADSPA_Descriptor*)df(i)) != NULL; ++i) { - char id_str[11]; - snprintf(id_str, 11, "%lu", descriptor->UniqueID); - const string uri = string("ladspa:").append(id_str); - - const Plugins::const_iterator i = _plugins.find(uri); - - if (i == _plugins.end()) { - LADSPAPlugin* plugin = new LADSPAPlugin(lib_path, uri, - descriptor->UniqueID, - descriptor->Label, - descriptor->Name); - - _plugins.insert(make_pair(uri, plugin)); - - } else { - cerr << "Warning: Duplicate " << uri - << " - Using " << i->second->library_path() - << " over " << lib_path << endl; - } - } - - delete plugin_library; - } - closedir(pdir); - } -} -#endif // HAVE_LADSPA - - -} // namespace Ingen diff --git a/src/libs/engine/NodeFactory.hpp b/src/libs/engine/NodeFactory.hpp deleted file mode 100644 index 95194350..00000000 --- a/src/libs/engine/NodeFactory.hpp +++ /dev/null @@ -1,92 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef NODEFACTORY_H -#define NODEFACTORY_H - -#include CONFIG_H_PATH -#include "module/global.hpp" - -#include -#include -#include -#include -#include -#ifdef HAVE_SLV2 -#include -#include "LV2Info.hpp" -#endif -#include "types.hpp" - -using std::string; - -namespace Ingen { - -class NodeImpl; -class PatchImpl; -class PluginImpl; - - -/** 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 LADSPANode) are responsible - * for actually creating a Node instance of the plugin. - * - * \ingroup engine - */ -class NodeFactory -{ -public: - NodeFactory(Ingen::Shared::World* world); - ~NodeFactory(); - - void load_plugins(); - - typedef std::map Plugins; - const Plugins& plugins() const { return _plugins; } - - PluginImpl* plugin(const string& uri); - - /** DEPRECATED */ - PluginImpl* plugin(const string& type, const string& lib, const string& label); - -private: -#ifdef HAVE_LADSPA - void load_ladspa_plugins(); -#endif - -#ifdef HAVE_SLV2 - void load_lv2_plugins(); -#endif - - void load_internal_plugins(); - - Plugins _plugins; - Ingen::Shared::World* _world; - bool _has_loaded; -#ifdef HAVE_SLV2 - SharedPtr _lv2_info; -#endif -}; - - -} // namespace Ingen - -#endif // NODEFACTORY_H diff --git a/src/libs/engine/NodeImpl.hpp b/src/libs/engine/NodeImpl.hpp deleted file mode 100644 index ac125c13..00000000 --- a/src/libs/engine/NodeImpl.hpp +++ /dev/null @@ -1,170 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef NODEIMPL_H -#define NODEIMPL_H - -#include -#include -#include "interface/Node.hpp" -#include "types.hpp" -#include "GraphObjectImpl.hpp" - -namespace Raul { template class List; class Maid; } - -namespace Ingen { - -namespace Shared { class Plugin; class Node; class Port; } - -using namespace Shared; - -class Buffer; -class PluginImpl; -class PatchImpl; -class PortImpl; -class MessageContext; - - -/** A Node (or "module") in a Patch (which is also a Node). - * - * A Node is a unit with input/output ports, a process() 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. - * - * \ingroup engine - */ -class NodeImpl : public GraphObjectImpl, virtual public Ingen::Shared::Node -{ -public: - NodeImpl(GraphObjectImpl* parent, const std::string& name, bool poly) - : GraphObjectImpl(parent, name, poly) - {} - - /** 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; - - /** Prepare for a new (external) polyphony value. - * - * Preprocessor thread, poly is actually applied by apply_poly. - * \return true on success. - */ - virtual bool prepare_poly(uint32_t poly) = 0; - - /** Apply a new (external) polyphony value. - * - * Audio thread. - * - * \param poly Must be < the most recent value passed to prepare_poly. - * \param maid Any objects no longer needed will be pushed to this - */ - virtual bool apply_poly(Raul::Maid& maid, uint32_t poly) = 0; - - /** Parallelism: Reset flags for start of a new cycle. - */ - virtual void reset_input_ready() = 0; - - /** Parallelism: Claim this node (to wait on its input). - * Only one thread will ever take this lock on a particular Node. - * \return true if lock was aquired, false otherwise - */ - virtual bool process_lock() = 0; - - /** Parallelism: Unclaim this node (let someone else wait on its input). - * Only a thread which successfully called process_lock may call this. - */ - virtual void process_unlock() = 0; - - /** Parallelism: Wait for signal that input is ready. - * Only a thread which successfully called process_lock may call this. - */ - virtual void wait_for_input(size_t num_providers) = 0; - - /** Parallelism: Signal that input is ready. Realtime safe. - * Calling this will wake up the thread which blocked on wait_for_input - * if there is one, and otherwise cause it to return true the next call. - * \return true if lock was aquired and input is ready, false otherwise - */ - virtual void signal_input_ready() = 0; - - /** Parallelism: Return the number of providers that have signalled. - */ - virtual unsigned n_inputs_ready() const = 0; - - /** Run the node for one instant in the message thread. - */ - virtual void message_process(MessageContext& context, uint32_t* output) = 0; - - /** Run the node for @a nframes input/output. - * - * @a start and @a end are transport times: end is not redundant in the case - * of varispeed, where end-start != nframes. - */ - virtual void process(ProcessContext& context) = 0; - - virtual void set_port_buffer(uint32_t voice, uint32_t port_num, Buffer* buf) = 0; - - virtual uint32_t num_ports() const = 0; - - virtual Port* port(uint32_t index) const = 0; - virtual PortImpl* port_impl(uint32_t index) 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 Raul::List* providers() = 0; - virtual void providers(Raul::List* l) = 0; - - /** Nodes are are connected to this Node's outputs. - * (They depend on this Node) - */ - virtual Raul::List* dependants() = 0; - virtual void dependants(Raul::List* l) = 0; - - /** The Patch this Node belongs to. */ - virtual PatchImpl* parent_patch() const = 0; - - /** Information about the Plugin this Node is an instance of. - * Not the best name - not all nodes come from plugins (ie Patch) - */ - virtual PluginImpl* plugin_impl() const = 0; - - /** Information about the Plugin this Node is an instance of. - * Not the best name - not all nodes come from plugins (ie Patch) - */ - virtual const Shared::Plugin* plugin() const = 0; - - virtual void set_buffer_size(size_t size) = 0; -}; - - -} // namespace Ingen - -#endif // NODEIMPL_H diff --git a/src/libs/engine/OSCClientSender.cpp b/src/libs/engine/OSCClientSender.cpp deleted file mode 100644 index dca2e0ed..00000000 --- a/src/libs/engine/OSCClientSender.cpp +++ /dev/null @@ -1,358 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "OSCClientSender.hpp" -#include -#include -#include -#include -#include "EngineStore.hpp" -#include "NodeFactory.hpp" -#include "util.hpp" -#include "PatchImpl.hpp" -#include "NodeImpl.hpp" -#include "PluginImpl.hpp" -#include "PortImpl.hpp" -#include "ConnectionImpl.hpp" -#include "AudioDriver.hpp" -#include "interface/ClientInterface.hpp" - -using namespace std; - -namespace Ingen { - - -/*! \page client_osc_namespace Client OSC Namespace Documentation - * - *

NOTE: this comment doesn't really apply any longer.. sort of. - * but maybe it still should.. maybe. so it remains...

- * - *

These are all the messages sent from the engine to the client. - * Communication takes place over two distinct bands: control band and - * notification band.

- *

The control band is where clients send commands, and receive a simple - * response, either OK or an error.

- *

All notifications of engine state (ie new nodes) are sent over the - * notification band which is seperate from the control band. 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.

- *

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).

- * \n\n - */ - - -/** \page client_osc_namespace - * \n - *

Control Band

- */ - -/** \page client_osc_namespace - *

\b /ingen/ok - Respond to a successful user command - * \arg \b response-id (int) - Request ID this is a response to - *

\n \n - */ -void -OSCClientSender::response_ok(int32_t id) -{ - if (!_enabled) - return; - - if (lo_send(_address, "/ingen/ok", "i", id) < 0) { - cerr << "Unable to send ok " << id << "! (" - << lo_address_errstr(_address) << ")" << endl; - } -} - - -/** \page client_osc_namespace - *

\b /ingen/response - Respond to a user command - * \arg \b response-id (int) - Request ID this is a response to - * \arg \b message (string) - Error message (natural language text) - *

\n \n - */ -void -OSCClientSender::response_error(int32_t id, const std::string& msg) -{ - if (!_enabled) - return; - - if (lo_send(_address, "/ingen/error", "is", id, msg.c_str()) < 0) { - cerr << "Unable to send error " << id << "! (" - << lo_address_errstr(_address) << ")" << endl; - } -} - - -/** \page client_osc_namespace - * \n - *

Notification Band

- */ - - -/** \page client_osc_namespace - *

\b /ingen/error - Notification that an error has occurred - * \arg \b message (string) - Error message (natural language text) \n\n - * \li This is for notification of errors that aren't a direct response to a - * user command, ie "unexpected" errors.

\n \n - */ -void -OSCClientSender::error(const std::string& msg) -{ - send("/ingen/error", "s", msg.c_str(), LO_ARGS_END); -} - - -/** \page client_osc_namespace - *

\b /ingen/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 (boolean) - Node is polyphonic\n\n - * \li New nodes are sent as a bundle. The first message in the bundle will be - * this one (/ingen/new_node), followed by a series of /ingen/new_port commands, - * followed by /ingen/new_node_end.

\n \n - */ -void OSCClientSender::new_node(const std::string& node_path, - const std::string& plugin_uri) -{ - send("/ingen/new_node", "ss", node_path.c_str(), plugin_uri.c_str(), LO_ARGS_END); -} - - - -/** \page client_osc_namespace - *

\b /ingen/new_port - Notification of a new port's creation. - * \arg \b path (string) - Path of new port - * \arg \b index (integer) - Index (or sort key) of port on parent - * \arg \b data-type (string) - Type of port (ingen:AudioPort, ingen:ControlPort, ingen:MIDIPort, or ingen:OSCPort) - * \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 variable.

\n \n - */ -void -OSCClientSender::new_port(const std::string& path, - uint32_t index, - const std::string& data_type, - bool is_output) -{ - send("/ingen/new_port", "sisi", path.c_str(), index, data_type.c_str(), is_output, LO_ARGS_END); -} - - -/** \page client_osc_namespace - *

\b /ingen/destroyed - Notification an object has been destroyed - * \arg \b path (string) - Path of object (which no longer exists)

\n \n - */ -void -OSCClientSender::destroy(const std::string& path) -{ - assert(path != "/"); - - send("/ingen/destroyed", "s", path.c_str(), LO_ARGS_END); -} - - -/** \page client_osc_namespace - *

\b /ingen/patch_cleared - Notification a patch has been cleared (all children destroyed) - * \arg \b path (string) - Path of patch (which is now empty)

\n \n - */ -void -OSCClientSender::patch_cleared(const std::string& patch_path) -{ - send("/ingen/patch_cleared", "s", patch_path.c_str(), LO_ARGS_END); -} - - -/** \page client_osc_namespace - *

\b /ingen/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

\n \n - */ -void -OSCClientSender::connect(const std::string& src_port_path, const std::string& dst_port_path) -{ - send("/ingen/new_connection", "ss", src_port_path.c_str(), dst_port_path.c_str(), LO_ARGS_END); -} - - -/** \page client_osc_namespace - *

\b /ingen/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

\n \n - */ -void -OSCClientSender::disconnect(const std::string& src_port_path, const std::string& dst_port_path) -{ - send("/ingen/disconnection", "ss", src_port_path.c_str(), dst_port_path.c_str(), LO_ARGS_END); -} - - -/** \page client_osc_namespace - *

\b /ingen/set_variable - Notification of a variable. - * \arg \b path (string) - Path of the object associated with variable (node, patch, or port) - * \arg \b key (string) - * \arg \b value (string)

\n \n - */ -void -OSCClientSender::set_variable(const std::string& path, const std::string& key, const Atom& value) -{ - lo_message m = lo_message_new(); - lo_message_add_string(m, path.c_str()); - lo_message_add_string(m, key.c_str()); - Raul::AtomLiblo::lo_message_add_atom(m, value); - send_message("/ingen/set_variable", m); -} - - -/** \page client_osc_namespace - *

\b /ingen/set_property - Notification of a property. - * \arg \b path (string) - Path of the object associated with property (node, patch, or port) - * \arg \b key (string) - * \arg \b value (string)

\n \n - */ -void -OSCClientSender::set_property(const std::string& path, const std::string& key, const Atom& value) -{ - lo_message m = lo_message_new(); - lo_message_add_string(m, path.c_str()); - lo_message_add_string(m, key.c_str()); - Raul::AtomLiblo::lo_message_add_atom(m, value); - send_message("/ingen/set_property", m); -} - - -/** \page client_osc_namespace - *

\b /ingen/set_port_value - Notification the value of a port has changed - * \arg \b path (string) - Path of port - * \arg \b value (any) - New value of port

\n \n - */ -void -OSCClientSender::set_port_value(const std::string& port_path, const Raul::Atom& value) -{ - lo_message m = lo_message_new(); - lo_message_add_string(m, port_path.c_str()); - Raul::AtomLiblo::lo_message_add_atom(m, value); - send_message("/ingen/set_port_value", m); -} - - -/** \page client_osc_namespace - *

\b /ingen/set_port_value - Notification the value of a port has changed - * \arg \b path (string) - Path of port - * \arg \b voice (int) - Voice which is set to this value - * \arg \b value (any) - New value of port

\n \n - */ -void -OSCClientSender::set_voice_value(const std::string& port_path, uint32_t voice, const Raul::Atom& value) -{ - lo_message m = lo_message_new(); - lo_message_add_string(m, port_path.c_str()); - Raul::AtomLiblo::lo_message_add_atom(m, value); - send_message("/ingen/set_port_value", m); -} - - -/** \page client_osc_namespace - *

\b /ingen/port_activity - Notification of activity for a port (e.g. MIDI messages) - * \arg \b path (string) - Path of port

\n \n - */ -void -OSCClientSender::port_activity(const std::string& port_path) -{ - if (!_enabled) - return; - - lo_send(_address, "/ingen/port_activity", "s", port_path.c_str(), LO_ARGS_END); -} - - -/** \page client_osc_namespace - *

\b /ingen/plugin - Notification of the existance of a plugin - * \arg \b uri (string) - URI of plugin (e.g. http://example.org/filtermatic) - * \arg \b type (string) - Type of plugin (e.g. "ingen:LV2Plugin") - * \arg \b symbol (string) - Valid symbol for plugin (default symbol for nodes) (e.g. "adsr") - * \arg \b name (string) - Descriptive human-readable name of plugin (e.g. "ADSR Envelope") - */ -void -OSCClientSender::new_plugin(const std::string& uri, - const std::string& type_uri, - const std::string& symbol, - const std::string& name) -{ - lo_message m = lo_message_new(); - lo_message_add_string(m, uri.c_str()); - lo_message_add_string(m, type_uri.c_str()); - lo_message_add_string(m, symbol.c_str()); - lo_message_add_string(m, name.c_str()); - send_message("/ingen/plugin", m); -} - - -/** \page client_osc_namespace - *

\b /ingen/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)

\n \n - */ -void -OSCClientSender::new_patch(const std::string& path, uint32_t poly) -{ - send("/ingen/new_patch", "si", path.c_str(), poly, LO_ARGS_END); -} - - -/** \page client_osc_namespace - *

\b /ingen/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

\n \n - */ -void -OSCClientSender::object_renamed(const std::string& old_path, const std::string& new_path) -{ - send("/ingen/object_renamed", "ss", old_path.c_str(), new_path.c_str(), LO_ARGS_END); -} - - -/** Sends information about a program associated with a node. - */ -void -OSCClientSender::program_add(const std::string& node_path, uint32_t bank, uint32_t program, const std::string& name) -{ - send("/ingen/program_add", "siis", - node_path.c_str(), bank, program, name.c_str(), LO_ARGS_END); -} - - -void -OSCClientSender::program_remove(const std::string& node_path, uint32_t bank, uint32_t program) -{ - send("/ingen/program_remove", "sii", - node_path.c_str(), bank, program, LO_ARGS_END); -} - - -} // namespace Ingen diff --git a/src/libs/engine/OSCClientSender.hpp b/src/libs/engine/OSCClientSender.hpp deleted file mode 100644 index c8c8418f..00000000 --- a/src/libs/engine/OSCClientSender.hpp +++ /dev/null @@ -1,136 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef OSCCLIENTSENDER_H -#define OSCCLIENTSENDER_H - -#include -#include -#include -#include -#include -#include "types.hpp" -#include "interface/ClientInterface.hpp" -#include "shared/OSCSender.hpp" - -namespace Ingen { - -namespace Shared { class EngineInterface; } - - -/** Implements ClientInterface for OSC clients (sends OSC messages). - * - * \ingroup engine - */ -class OSCClientSender : public Shared::ClientInterface, public Shared::OSCSender -{ -public: - OSCClientSender(const std::string& url) - : _url(url) - { - _address = lo_address_new_from_url(url.c_str()); - } - - virtual ~OSCClientSender() - { lo_address_free(_address); } - - bool enabled() const { return _enabled; } - - void enable() { _enabled = true; } - void disable() { _enabled = false; } - - void bundle_begin() { OSCSender::bundle_begin(); } - void bundle_end() { OSCSender::bundle_end(); } - void transfer_begin() { OSCSender::transfer_begin(); } - void transfer_end() { OSCSender::transfer_end(); } - - std::string uri() const { return lo_address_get_url(_address); } - - void subscribe(Shared::EngineInterface* engine) { } - - /* *** ClientInterface Implementation Below *** */ - - //void client_registration(const std::string& url, int client_id); - - void response_ok(int32_t id); - void response_error(int32_t id, const std::string& msg); - - void error(const std::string& msg); - - virtual void new_plugin(const std::string& uri, - const std::string& type_uri, - const std::string& symbol, - const std::string& name); - - virtual void new_patch(const std::string& path, uint32_t poly); - - virtual void new_node(const std::string& path, - const std::string& plugin_uri); - - virtual void new_port(const std::string& path, - uint32_t index, - const std::string& data_type, - bool is_output); - - virtual void patch_cleared(const std::string& path); - - virtual void destroy(const std::string& path); - - virtual void object_renamed(const std::string& old_path, - const std::string& new_path); - - virtual void connect(const std::string& src_port_path, - const std::string& dst_port_path); - - virtual void disconnect(const std::string& src_port_path, - const std::string& dst_port_path); - - virtual void set_variable(const std::string& subject_path, - const std::string& predicate, - const Raul::Atom& value); - - virtual void set_property(const std::string& subject_path, - const std::string& predicate, - const Raul::Atom& value); - - virtual void set_port_value(const std::string& port_path, - const Raul::Atom& value); - - virtual void set_voice_value(const std::string& port_path, - uint32_t voice, - const Raul::Atom& value); - - virtual void port_activity(const std::string& port_path); - - virtual void program_add(const std::string& node_path, - uint32_t bank, - uint32_t program, - const std::string& program_name); - - virtual void program_remove(const std::string& node_path, - uint32_t bank, - uint32_t program); - -private: - std::string _url; -}; - - -} // namespace Ingen - -#endif // OSCCLIENTSENDER_H - diff --git a/src/libs/engine/OSCDriver.hpp b/src/libs/engine/OSCDriver.hpp deleted file mode 100644 index 06125217..00000000 --- a/src/libs/engine/OSCDriver.hpp +++ /dev/null @@ -1,82 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef OSCDRIVER_H -#define OSCDRIVER_H - -#include "types.hpp" -#include "Driver.hpp" -#include - -namespace Ingen { - - -/** OSC driver abstract base class. - * - * \ingroup engine - */ -class OSCDriver : public Driver -{ -public: - OSCDriver() : Driver(DataType::EVENT) {} - - /** Prepare events (however neccessary) for the specified block (realtime safe) */ - virtual void prepare_block(const SampleCount block_start, const SampleCount block_end) = 0; -}; - - - -/** Dummy OSCDriver. - * - * Not abstract, all functions are dummies. One of these will be allocated and - * "used" if no working OSC driver is loaded. (Doing it this way as opposed to - * just making OSCDriver have dummy functions makes sure any existing OSCDriver - * derived class actually implements the required functions). - * - * \ingroup engine - */ -class DummyOSCDriver : public OSCDriver -{ -public: - DummyOSCDriver() { - std::cout << "[DummyOSCDriver] Started Dummy OSC driver." << std::endl; - } - - ~DummyOSCDriver() {} - - void activate() {} - void deactivate() {} - - bool is_activated() const { return false; } - bool is_enabled() const { return false; } - - void enable() {} - void disable() {} - - DriverPort* create_port(DuplexPort* patch_port) { return NULL; } - - void add_port(DriverPort* port) {} - DriverPort* remove_port(const Raul::Path& path) { return NULL; } - - void prepare_block(const SampleCount block_start, const SampleCount block_end) {} -}; - - - -} // namespace Ingen - -#endif // OSCDRIVER_H diff --git a/src/libs/engine/OSCEngineReceiver.cpp b/src/libs/engine/OSCEngineReceiver.cpp deleted file mode 100644 index e4e2fec1..00000000 --- a/src/libs/engine/OSCEngineReceiver.cpp +++ /dev/null @@ -1,884 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -//#define ENABLE_AVAHI 1 -#include -#include "types.hpp" -#include -#include -#include "interface/ClientInterface.hpp" -#include "engine/ThreadManager.hpp" -#include "OSCEngineReceiver.hpp" -#include "QueuedEventSource.hpp" -#include "OSCClientSender.hpp" -#include "ClientBroadcaster.hpp" - -using namespace std; - -namespace Ingen { - - -/*! \page engine_osc_namespace Engine OSC Namespace Documentation - * - *

These are the commands the engine recognizes. A client can control every - * aspect of the engine entirely with these commands.

- * - *

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.

\n\n - */ - - -OSCEngineReceiver::OSCEngineReceiver(Engine& engine, size_t queue_size, uint16_t port) - : QueuedEngineInterface(engine, queue_size, queue_size) // FIXME - , _server(NULL) -{ - _receive_thread = new ReceiveThread(*this); - - char port_str[6]; - snprintf(port_str, 6, "%u", port); - - _server = lo_server_new(port_str, error_cb); -#ifdef ENABLE_AVAHI - lo_server_avahi_init(_server, "ingen"); -#endif - - if (_server == NULL) { - cerr << "[OSC] Could not start OSC server. Aborting." << endl; - exit(EXIT_FAILURE); - } else { - char* lo_url = lo_server_get_url(_server); - cout << "[OSC] Started OSC server at " << lo_url << endl; - free(lo_url); - } - - // For debugging, print all incoming OSC messages - //lo_server_add_method(_server, NULL, NULL, generic_cb, NULL); - - // Set response address for this message. - // It's important this is first and returns nonzero. - lo_server_add_method(_server, NULL, NULL, set_response_address_cb, this); - - // Commands - lo_server_add_method(_server, "/ingen/ping", "i", ping_cb, this); - lo_server_add_method(_server, "/ingen/ping_queued", "i", ping_slow_cb, this); - lo_server_add_method(_server, "/ingen/quit", "i", quit_cb, this); - //lo_server_add_method(_server, "/ingen/register_client", "is", register_client_cb, this); - lo_server_add_method(_server, "/ingen/register_client", "i", register_client_cb, this); - lo_server_add_method(_server, "/ingen/unregister_client", "i", unregister_client_cb, this); - lo_server_add_method(_server, "/ingen/load_plugins", "i", load_plugins_cb, this); - lo_server_add_method(_server, "/ingen/activate", "i", engine_activate_cb, this); - lo_server_add_method(_server, "/ingen/deactivate", "i", engine_deactivate_cb, this); - lo_server_add_method(_server, "/ingen/new_patch", "isi", new_patch_cb, this); - lo_server_add_method(_server, "/ingen/clear_patch", "is", clear_patch_cb, this); - lo_server_add_method(_server, "/ingen/set_polyphony", "isi", set_polyphony_cb, this); - lo_server_add_method(_server, "/ingen/set_polyphonic", "isT", set_polyphonic_cb, this); - lo_server_add_method(_server, "/ingen/set_polyphonic", "isF", set_polyphonic_cb, this); - lo_server_add_method(_server, "/ingen/new_port", "issi", new_port_cb, this); - lo_server_add_method(_server, "/ingen/new_node", "issss", new_node_cb, this); - lo_server_add_method(_server, "/ingen/new_node", "issss", new_node_cb, this); - lo_server_add_method(_server, "/ingen/new_node", "iss", new_node_by_uri_cb, this); - lo_server_add_method(_server, "/ingen/new_node", "iss", new_node_by_uri_cb, this); - lo_server_add_method(_server, "/ingen/destroy", "is", destroy_cb, this); - lo_server_add_method(_server, "/ingen/rename", "iss", rename_cb, this); - lo_server_add_method(_server, "/ingen/connect", "iss", connect_cb, this); - lo_server_add_method(_server, "/ingen/disconnect", "iss", disconnect_cb, this); - lo_server_add_method(_server, "/ingen/disconnect_all", "iss", disconnect_all_cb, this); - lo_server_add_method(_server, "/ingen/set_port_value", NULL, set_port_value_cb, this); - lo_server_add_method(_server, "/ingen/note_on", "isii", note_on_cb, this); - lo_server_add_method(_server, "/ingen/note_off", "isi", note_off_cb, this); - lo_server_add_method(_server, "/ingen/all_notes_off", "isi", all_notes_off_cb, this); - lo_server_add_method(_server, "/ingen/midi_learn", "is", midi_learn_cb, this); - lo_server_add_method(_server, "/ingen/set_variable", NULL, variable_set_cb, this); - lo_server_add_method(_server, "/ingen/set_property", NULL, property_set_cb, this); - - // Queries - lo_server_add_method(_server, "/ingen/request_variable", "iss", variable_get_cb, this); - lo_server_add_method(_server, "/ingen/request_plugin", "is", request_plugin_cb, this); - lo_server_add_method(_server, "/ingen/request_object", "is", request_object_cb, this); - lo_server_add_method(_server, "/ingen/request_port_value", "is", request_port_value_cb, this); - lo_server_add_method(_server, "/ingen/request_plugins", "i", request_plugins_cb, this); - lo_server_add_method(_server, "/ingen/request_all_objects", "i", request_all_objects_cb, this); - - lo_server_add_method(_server, NULL, NULL, unknown_cb, NULL); - - Thread::set_name("OSC Pre-Processor"); -} - - -OSCEngineReceiver::~OSCEngineReceiver() -{ - deactivate(); - stop(); - _receive_thread->stop(); - delete _receive_thread; - - if (_server != NULL) { -#ifdef ENABLE_AVAHI - lo_server_avahi_free(_server); -#endif - lo_server_free(_server); - _server = NULL; - } -} - - -void -OSCEngineReceiver::activate() -{ - QueuedEventSource::activate(); - _receive_thread->set_name("OSC Receiver"); - _receive_thread->start(); - _receive_thread->set_scheduling(SCHED_FIFO, 5); // Jack default appears to be 10 -} - - -void -OSCEngineReceiver::deactivate() -{ - cout << "[OSCEngineReceiver] Stopped OSC listening thread" << endl; - _receive_thread->stop(); - QueuedEventSource::deactivate(); -} - - -/** Override the semaphore driven _run method of QueuedEngineInterface - * to wait on OSC messages and prepare them right away in the same thread. - */ -void -OSCEngineReceiver::ReceiveThread::_run() -{ - Thread::get().set_context(THREAD_PRE_PROCESS); - - /* get a timestamp here and stamp all the events with the same time so - * they all get executed in the same cycle */ - - while (true) { - assert(_receiver._server); - /*if ( ! _server) { - cout << "[OSCEngineReceiver] Server is NULL, exiting" << endl; - break; - }*/ - - // Wait on a message and enqueue it - lo_server_recv(_receiver._server); - - // Enqueue every other message that is here "now" - // (would this provide truly atomic bundles?) - while (lo_server_recv_noblock(_receiver._server, 0) > 0) - if (_receiver.unprepared_events()) - _receiver.whip(); - - // No more unprepared events - } -} - - -/** 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. - * Hooray for reference counting. - * - * 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. - * Lack of a fast liblo address comparison really sucks here, in any case. - */ -int -OSCEngineReceiver::set_response_address_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg, void* user_data) -{ - OSCEngineReceiver* const me = reinterpret_cast(user_data); - - if (argc < 1 || types[0] != 'i') // Not a valid Ingen message - return 0; // Save liblo the trouble - - const int32_t id = argv[0]->i; - - const lo_address addr = lo_message_get_source(msg); - char* const url = lo_address_get_url(addr); - - const SharedPtr r = me->_responder; - - /* Different address than last time, have to do a lookup */ - if (!r || !r->client() || strcmp(url, r->client()->uri().c_str())) { - ClientInterface* client = me->_engine.broadcaster()->client(url); - if (client) - me->_responder = SharedPtr(new Responder(client, id)); - else - me->_responder = SharedPtr(new Responder()); - } - - if (id != -1) { - me->set_next_response_id(id); - } else { - me->disable_responses(); - } - - // If this returns 0 no OSC commands will work - return 1; -} - - -void -OSCEngineReceiver::error_cb(int num, const char* msg, const char* path) -{ - cerr << "liblo server error " << num << " in path \"" << "\" - " << msg << endl; -} - - -/** \page engine_osc_namespace - *

\b /ingen/ping - Immediately sends a successful response to the given response id. - * \arg \b response-id (integer)

\n \n - */ -int -OSCEngineReceiver::_ping_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const lo_address addr = lo_message_get_source(msg); - if (lo_send(addr, "/ingen/ok", "i", argv[0]->i) < 0) - cerr << "WARNING: Unable to send response: " << lo_address_errstr(addr) << endl; - return 0; -} - - -/** \page engine_osc_namespace - *

\b /ingen/ping_queued - Sends response after going through the event queue. - * \arg \b response-id (integer) - * - * \li See the documentation for /ingen/set_port_value_queued for an explanation of how - * this differs from /ingen/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.

\n \n - */ -int -OSCEngineReceiver::_ping_slow_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - ping(); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /ingen/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.

\n \n - */ -int -OSCEngineReceiver::_quit_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - quit(); - return 0; -} - -/** \page engine_osc_namespace - *

\b /ingen/register_client - Registers a new client with the engine - * \arg \b response-id (integer) \n\n - * \li The incoming address will be used for the new registered client. If you - * want to register a different specific address, use the URL version.

\n \n - */ -int -OSCEngineReceiver::_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); - ClientInterface* client = new OSCClientSender((const char*)url); - register_client(client); - free(url); - - return 0; -} - - -/** \page engine_osc_namespace - *

\b /ingen/unregister_client - Unregisters a client - * \arg \b response-id (integer)

\n \n - */ -int -OSCEngineReceiver::_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(url); - free(url); - - return 0; -} - - -/** \page engine_osc_namespace - *

\b /ingen/load_plugins - Locates all available plugins, making them available for use. - * \arg \b response-id (integer)

\n \n - */ -int -OSCEngineReceiver::_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 - *

\b /ingen/activate - Activate the engine (MIDI, audio, everything) - * \arg \b response-id (integer)

- * - * \li Note that you must send this message first if you want the engine to do - * anything at all - including respond to your messages! \n \n - */ -int -OSCEngineReceiver::_engine_activate_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - QueuedEngineInterface::activate(); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /ingen/deactivate - Deactivate the engine completely. - * \arg \b response-id (integer)

\n \n - */ -int -OSCEngineReceiver::_engine_deactivate_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - QueuedEngineInterface::deactivate(); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /ingen/new_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

\n \n - */ -int -OSCEngineReceiver::_new_patch_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* patch_path = &argv[1]->s; - const int32_t poly = argv[2]->i; - - new_patch(patch_path, poly); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /ingen/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

\n \n - */ -int -OSCEngineReceiver::_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 - *

\b /ingen/clear_patch - Remove all nodes from a patch - * \arg \b response-id (integer) - * \arg \b patch-path - Patch's path

\n \n - */ -int -OSCEngineReceiver::_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 - *

\b /ingen/set_polyphony - Set the polyphony of a patch - * \arg \b response-id (integer) - * \arg \b patch-path - Patch's path - * \arg \b poly (integer)

\n \n - */ -int -OSCEngineReceiver::_set_polyphony_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* patch_path = &argv[1]->s; - const uint32_t poly = argv[2]->i; - - set_polyphony(patch_path, poly); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /ingen/set_polyphonic - Toggle a node's or port's polyphonic mode - * \arg \b response-id (integer) - * \arg \b path - Object's path - * \arg \b polyphonic (bool)

\n \n - */ -int -OSCEngineReceiver::_set_polyphonic_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* object_path = &argv[1]->s; - bool polyphonic = (types[2] == 'T'); - - set_polyphonic(object_path, polyphonic); - return 0; -} - - -// FIXME: add index -/** \page engine_osc_namespace - *

\b /ingen/new_port - Add a port into a given patch (load a plugin by URI) - * \arg \b response-id (integer) - * \arg \b path (string) - Full path of the new port (ie. /patch2/subpatch/newport) - * \arg \b data-type (string) - Type of port (ingen:AudioPort, ingen:ControlPort, ingen:MIDIPort, or ingen:OSCPort) - * \arg \b direction ("is-output") (integer) - Direction of data flow (Input = 0, Output = 1)

\n \n - */ -int -OSCEngineReceiver::_new_port_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* port_path = &argv[1]->s; - const char* data_type = &argv[2]->s; - const int32_t direction = argv[3]->i; - - new_port(port_path, 0, data_type, (direction == 1)); - return 0; -} - -/** \page engine_osc_namespace - *

\b /ingen/new_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 plug-uri (string) - URI of the plugin to load \n \n - */ -int -OSCEngineReceiver::_new_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* plug_uri = &argv[2]->s; - - new_node(node_path, plug_uri); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /ingen/new_node - Add a node into a given patch (load a plugin by libname, label) \b DEPRECATED - * \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") \n \n - * - * \li This is only here to provide backwards compatibility for old patches that store LADSPA plugin - * references as libname, label. It is to be removed ASAP, don't use it. - *

\n \n - */ -int -OSCEngineReceiver::_new_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; - - new_node_deprecated(node_path, type, lib_name, plug_label); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /ingen/destroy - Removes (destroys) a Patch or a Node - * \arg \b response-id (integer) - * \arg \b node-path (string) - Full path of the object

\n \n - */ -int -OSCEngineReceiver::_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 - *

\b /ingen/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

\n \n - */ -int -OSCEngineReceiver::_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 - *

\b /ingen/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

\n \n - */ -int -OSCEngineReceiver::_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 - *

\b /ingen/disconnect_all - Disconnect all connections to/from a node/port. - * \arg \b response-id (integer) - * \arg \b patch-path (string) - The (parent) patch in which to disconnect object.

\n \n - * \arg \b node-path (string) - Full path of object.

\n \n - */ -int -OSCEngineReceiver::_disconnect_all_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* patch_path = &argv[1]->s; - const char* object_path = &argv[2]->s; - - disconnect_all(patch_path, object_path); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /ingen/set_port_value - 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.

\n \n - */ -int -OSCEngineReceiver::_set_port_value_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - if (argc < 3 || argc > 5 || strncmp(types, "is", 2)) - return 1; - - const char* port_path = &argv[1]->s; - - using Raul::Atom; - - if (!strcmp(types, "isf")) { // float, all voices - const float value = argv[2]->f; - set_port_value(port_path, Atom(value)); - } else if (!strcmp(types, "isif")) { // float, specific voice - const float value = argv[3]->f; - set_voice_value(port_path, argv[2]->i, Atom(value)); - } else if (!strcmp(types, "issb")) { // blob (event), all voices - const char* type = &argv[2]->s; - lo_blob b = argv[3]; - size_t data_size = lo_blob_datasize(b); - void* data = lo_blob_dataptr(b); - set_port_value(port_path, Atom(type, data_size, data)); - } else if (!strcmp(types, "isisb")) { // blob (event), specific voice - const char* type = &argv[3]->s; - lo_blob b = argv[4]; - size_t data_size = lo_blob_datasize(b); - void* data = lo_blob_dataptr(b); - set_voice_value(port_path, argv[2]->i, Atom(type, data_size, data)); - } else if (!strcmp(types, "issN")) { // empty event (type only), all voices - const char* type = &argv[2]->s; - set_port_value(port_path, Atom(type, 0, NULL)); - } else if (!strcmp(types, "isisN")) { // empty event (type only), specific voice - const char* type = &argv[3]->s; - set_voice_value(port_path, argv[2]->i, Atom(type, 0, NULL)); - } else { - return 1; - } - - return 0; -} - - -/** \page engine_osc_namespace - *

\b /ingen/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)

\n \n - */ -int -OSCEngineReceiver::_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 - *

\b /ingen/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)

\n \n - */ -int -OSCEngineReceiver::_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 - *

\b /ingen/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

\n \n - */ -int -OSCEngineReceiver::_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 - *

\b /ingen/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.

\n \n - */ -int -OSCEngineReceiver::_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; -} - - -/** \page engine_osc_namespace - *

\b /ingen/set_variable - Set a variable, associated with a synth-space object (node, etc) - * \arg \b response-id (integer) - * \arg \b object-path (string) - Full path of object to associate variable with - * \arg \b key (string) - Key (index/predicate/ID) for new variable - * \arg \b value (string) - Value of new variable

\n \n - */ -int -OSCEngineReceiver::_variable_set_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - if (argc != 4 || types[0] != 'i' || types[1] != 's' || types[2] != 's') - return 1; - - const char* object_path = &argv[1]->s; - const char* key = &argv[2]->s; - - Raul::Atom value = Raul::AtomLiblo::lo_arg_to_atom(types[3], argv[3]); - - set_variable(object_path, key, value); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /ingen/set_property - Set an (RDF) property, associated with a synth-space object (node, etc) - * \arg \b response-id (integer) - * \arg \b object-path (string) - Full path of object to associate variable with - * \arg \b key (string) - URI/QName for predicate of this property (e.g. "ingen:enabled") - * \arg \b value (string) - Value of property

\n \n - */ -int -OSCEngineReceiver::_property_set_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - if (argc != 4 || types[0] != 'i' || types[1] != 's' || types[2] != 's') - return 1; - - const char* object_path = &argv[1]->s; - const char* key = &argv[2]->s; - - Raul::Atom value = Raul::AtomLiblo::lo_arg_to_atom(types[3], argv[3]); - - set_property(object_path, key, value); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /ingen/request_variable - Requests the engine send a piece of variable, associated with a synth-space object (node, etc) - * \arg \b response-id (integer) - * \arg \b object-path (string) - Full path of object variable is associated with - * \arg \b key (string) - Key (index) for piece of variable - * - * \li Reply will be sent to client registered with the source address of this message.

\n \n - */ -int -OSCEngineReceiver::_variable_get_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* object_path = &argv[1]->s; - const char* key = &argv[2]->s; - - request_variable(object_path, key); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /ingen/request_plugin - 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 \n\n - * \li Reply will be sent to client registered with the source address of this message.

\n\n - */ -int -OSCEngineReceiver::_request_plugin_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* uri = &argv[1]->s; - - request_plugin(uri); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /ingen/request_object - 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 \n\n - * \li Reply will be sent to client registered with the source address of this message.

\n\n - */ -int -OSCEngineReceiver::_request_object_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* object_path = &argv[1]->s; - - request_object(object_path); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /ingen/request_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 \n\n - * \li Reply will be sent to client registered with the source address of this message.

\n\n - */ -int -OSCEngineReceiver::_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; -} - - -/** \page engine_osc_namespace - *

\b /ingen/request_plugins - Requests the engine send a list of all known plugins. - * \arg \b response-id (integer) \n\n - * \li Reply will be sent to client registered with the source address of this message.

\n\n - */ -int -OSCEngineReceiver::_request_plugins_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - request_plugins(); - return 0; -} - - -/** \page engine_osc_namespace - *

\b /ingen/request_all_objects - Requests the engine send information about \em all objects (patches, nodes, etc) - * \arg \b response-id (integer)\n\n - * \li Reply will be sent to client registered with the source address of this message.

\n \n - */ -int -OSCEngineReceiver::_request_all_objects_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - request_all_objects(); - return 0; -} - - -// Static Callbacks // - - -// Display incoming OSC messages (for debugging purposes) -int -OSCEngineReceiver::generic_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg, void* user_data) -{ - printf("[OSCMsg] %s (%s)\t", path, types); - - for (int i=0; i < argc; ++i) { - lo_arg_pp(lo_type(types[i]), argv[i]); - printf("\t"); - } - printf("\n"); - - return 1; // not handled -} - - -int -OSCEngineReceiver::unknown_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg, void* user_data) -{ - const lo_address addr = lo_message_get_source(msg); - char* const url = lo_address_get_url(addr); - - cerr << "Unknown command " << path << " (" << types << "), sending error.\n"; - - string error_msg = "Unknown command: "; - error_msg.append(path).append(" ").append(types); - - OSCClientSender(url).error(error_msg); - - return 0; -} - - -} // namespace Ingen diff --git a/src/libs/engine/OSCEngineReceiver.hpp b/src/libs/engine/OSCEngineReceiver.hpp deleted file mode 100644 index c6e0bf59..00000000 --- a/src/libs/engine/OSCEngineReceiver.hpp +++ /dev/null @@ -1,129 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef OSCENGINERECEIVER_H -#define OSCENGINERECEIVER_H - -#include CONFIG_H_PATH -#include -#include -#include -#include -#include "QueuedEngineInterface.hpp" -#include "Responder.hpp" -using std::string; - -namespace Ingen { - -class JackDriver; -class NodeFactory; -class PatchImpl; - - -/* 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 _##name##_cb (LO_HANDLER_ARGS);\ -inline static int name##_cb(LO_HANDLER_ARGS, void* myself)\ -{ return ((OSCEngineReceiver*)myself)->_##name##_cb(path, types, argv, argc, msg); } - - -/* FIXME: Make this receive and preprocess in the same thread? */ - - -/** 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 should be implemented-as-a (privately inherit) - * QueuedEngineInterface, but it needs to be public so it's an EventSource - * the Driver can use. This probably should be fixed somehow.. - * - * \ingroup engine - */ -class OSCEngineReceiver : public QueuedEngineInterface -{ -public: - OSCEngineReceiver(Engine& engine, size_t queue_size, uint16_t port); - ~OSCEngineReceiver(); - - void activate(); - void deactivate(); - -private: - struct ReceiveThread : public Raul::Thread { - ReceiveThread(OSCEngineReceiver& receiver) : _receiver(receiver) {} - virtual void _run(); - private: - OSCEngineReceiver& _receiver; - }; - - friend class ReceiveThread; - - ReceiveThread* _receive_thread; - - static void error_cb(int num, const char* msg, const char* path); - static int set_response_address_cb(LO_HANDLER_ARGS, void* myself); - static int generic_cb(LO_HANDLER_ARGS, void* myself); - static int unknown_cb(LO_HANDLER_ARGS, void* myself); - - 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(new_patch); - LO_HANDLER(rename); - LO_HANDLER(new_port); - LO_HANDLER(new_node); - LO_HANDLER(new_node_by_uri); - LO_HANDLER(clear_patch); - LO_HANDLER(set_polyphony); - LO_HANDLER(set_polyphonic); - LO_HANDLER(destroy); - LO_HANDLER(connect); - LO_HANDLER(disconnect); - LO_HANDLER(disconnect_all); - LO_HANDLER(set_port_value); - LO_HANDLER(note_on); - LO_HANDLER(note_off); - LO_HANDLER(all_notes_off); - LO_HANDLER(midi_learn); - LO_HANDLER(variable_get); - LO_HANDLER(variable_set); - LO_HANDLER(property_set); - LO_HANDLER(request_plugin); - LO_HANDLER(request_object); - LO_HANDLER(request_port_value); - LO_HANDLER(request_variable); - LO_HANDLER(request_plugins); - LO_HANDLER(request_all_objects); - - lo_server _server; -}; - - -} // namespace Ingen - -#endif // OSCENGINERECEIVER_H diff --git a/src/libs/engine/ObjectSender.cpp b/src/libs/engine/ObjectSender.cpp deleted file mode 100644 index 688cea8e..00000000 --- a/src/libs/engine/ObjectSender.cpp +++ /dev/null @@ -1,150 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "ObjectSender.hpp" -#include "interface/ClientInterface.hpp" -#include "EngineStore.hpp" -#include "PatchImpl.hpp" -#include "NodeImpl.hpp" -#include "PortImpl.hpp" -#include "PortImpl.hpp" -#include "ConnectionImpl.hpp" -#include "NodeFactory.hpp" -#include "interface/DataType.hpp" -#include "AudioBuffer.hpp" - -namespace Ingen { - - -void -ObjectSender::send_patch(ClientInterface* client, const PatchImpl* patch, bool recursive) -{ - client->bundle_begin(); - - client->new_patch(patch->path(), patch->internal_polyphony()); - client->set_property(patch->path(), "ingen:polyphonic", patch->polyphonic()); - - // Send variable - const GraphObjectImpl::Variables& data = patch->variables(); - for (GraphObjectImpl::Variables::const_iterator j = data.begin(); j != data.end(); ++j) - client->set_variable(patch->path(), (*j).first, (*j).second); - - client->set_property(patch->path(), "ingen:enabled", (bool)patch->enabled()); - - client->bundle_end(); - - if (recursive) { - - // Send nodes - for (List::const_iterator j = patch->nodes().begin(); - j != patch->nodes().end(); ++j) { - const NodeImpl* const node = (*j); - send_node(client, node, true); - } - - // Send ports - for (uint32_t i=0; i < patch->num_ports(); ++i) { - PortImpl* const port = patch->port_impl(i); - send_port(client, port); - } - - // Send connections - client->transfer_begin(); - for (PatchImpl::Connections::const_iterator j = patch->connections().begin(); - j != patch->connections().end(); ++j) - client->connect((*j)->src_port_path(), (*j)->dst_port_path()); - client->transfer_end(); - } -} - - -/** Sends a node or a patch */ -void -ObjectSender::send_node(ClientInterface* client, const NodeImpl* node, bool recursive) -{ - PluginImpl* const plugin = node->plugin_impl(); - - assert(node->path().length() > 0); - - if (plugin->type() == Plugin::Patch) { - send_patch(client, (PatchImpl*)node, recursive); - return; - } - - if (plugin->uri().length() == 0) { - cerr << "Node " << node->path() << " plugin has no URI! Not sending." << endl; - return; - } - - client->bundle_begin(); - - client->new_node(node->path(), node->plugin()->uri()); - client->set_property(node->path(), "ingen:polyphonic", node->polyphonic()); - - // Send variables - const GraphObjectImpl::Variables& data = node->variables(); - for (GraphObjectImpl::Variables::const_iterator j = data.begin(); j != data.end(); ++j) - client->set_variable(node->path(), (*j).first, (*j).second); - - // Send properties - const GraphObjectImpl::Properties& prop = node->properties(); - for (GraphObjectImpl::Properties::const_iterator j = prop.begin(); j != prop.end(); ++j) - client->set_property(node->path(), (*j).first, (*j).second); - - client->bundle_end(); - - if (recursive) { - // Send ports - for (size_t j=0; j < node->num_ports(); ++j) - send_port(client, node->port_impl(j)); - } -} - - -void -ObjectSender::send_port(ClientInterface* client, const PortImpl* port) -{ - assert(port); - - client->bundle_begin(); - - client->new_port(port->path(), port->index(), port->type().uri(), port->is_output()); - client->set_property(port->path(), "ingen:polyphonic", port->polyphonic()); - - // Send variable - const GraphObjectImpl::Variables& data = port->variables(); - for (GraphObjectImpl::Variables::const_iterator j = data.begin(); j != data.end(); ++j) - client->set_variable(port->path(), (*j).first, (*j).second); - - // Send properties - const GraphObjectImpl::Properties& prop = port->properties(); - for (GraphObjectImpl::Properties::const_iterator j = prop.begin(); j != prop.end(); ++j) - client->set_property(port->path(), (*j).first, (*j).second); - - // Send control value - if (port->type() == DataType::CONTROL) { - const Sample value = dynamic_cast(port->buffer(0))->value_at(0); - //cerr << port->path() << " sending default value " << default_value << endl; - client->set_port_value(port->path(), value); - } - - client->bundle_end(); -} - - -} // namespace Ingen - diff --git a/src/libs/engine/ObjectSender.hpp b/src/libs/engine/ObjectSender.hpp deleted file mode 100644 index 9b6eb000..00000000 --- a/src/libs/engine/ObjectSender.hpp +++ /dev/null @@ -1,57 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef OBJECTSENDER_H -#define OBJECTSENDER_H - -#include - -namespace Ingen { - -namespace Shared { - class ClientInterface; -} using Shared::ClientInterface; - -class PatchImpl; -class NodeImpl; -class PortImpl; -class PluginImpl; - - -/** Utility class for sending GraphObjects 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 serialiser, except to calls on ClientInterface rather than - * eg a byte stream. - */ -class ObjectSender { -public: - - // FIXME: Make all object parameters const - - static void send_patch(ClientInterface* client, const PatchImpl* patch, bool recursive); - static void send_node(ClientInterface* client, const NodeImpl* node, bool recursive); - static void send_port(ClientInterface* client, const PortImpl* port); -}; - -} // namespace Ingen - -#endif // OBJECTSENDER_H - diff --git a/src/libs/engine/OmInProcess.cpp b/src/libs/engine/OmInProcess.cpp deleted file mode 100644 index 6a65cf38..00000000 --- a/src/libs/engine/OmInProcess.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* This file is part of Ingen. Copyright (C) 2006 Mario Lang. - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include -#include "Engine.hpp" -#include "OSCReceiver.hpp" -#include "JackAudioDriver.hpp" - -extern "C" -{ - int jack_initialize(jack_client_t* client, const char* load_init); - void jack_finish(void* arg); -} - - -void* -run_main(void* arg) -{ - Engine::instance().main(); - - // FIXME: cleanup - - return 0; -} - - -pthread_t main_thread; - - -int -jack_initialize(jack_client_t* client, const char* load_init) -{ - if ((Ingen::om = new Engine(load_init, new Ingen::JackAudioDriver(client))) != NULL) { - pthread_create(&main_thread, NULL, run_main, NULL); - return 0; // Success - } else { - return 1; - } -} - - -void -jack_finish(void* arg) -{ - void* ret; - Engine::instance().quit(); - pthread_join(main_thread, &ret); -} - diff --git a/src/libs/engine/OutputPort.cpp b/src/libs/engine/OutputPort.cpp deleted file mode 100644 index 116d9a3e..00000000 --- a/src/libs/engine/OutputPort.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include "OutputPort.hpp" -#include "Buffer.hpp" -#include "ProcessContext.hpp" - -using namespace std; - -namespace Ingen { - - -OutputPort::OutputPort(NodeImpl* parent, - const string& name, - uint32_t index, - uint32_t poly, - DataType type, - const Atom& value, - size_t buffer_size) - : PortImpl(parent, name, index, poly, type, value, buffer_size) -{ - if (type == DataType::CONTROL) - _broadcast = true; -} - - -void -OutputPort::pre_process(ProcessContext& context) -{ - for (uint32_t i=0; i < _poly; ++i) - buffer(i)->prepare_write(context.start(), context.nframes()); -} - - -void -OutputPort::post_process(ProcessContext& context) -{ - for (uint32_t i=0; i < _poly; ++i) - buffer(i)->prepare_read(context.start(), context.nframes()); - - //cerr << path() << " output post: buffer: " << buffer(0) << endl; - - broadcast(context); -} - - -} // namespace Ingen diff --git a/src/libs/engine/OutputPort.hpp b/src/libs/engine/OutputPort.hpp deleted file mode 100644 index 8d441b5c..00000000 --- a/src/libs/engine/OutputPort.hpp +++ /dev/null @@ -1,63 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef OUTPUTPORT_H -#define OUTPUTPORT_H - -#include -#include -#include "PortImpl.hpp" -#include "types.hpp" - -namespace Ingen { - - -/** 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 - */ -class OutputPort : virtual public PortImpl -{ -public: - OutputPort(NodeImpl* parent, - const std::string& name, - uint32_t index, - uint32_t poly, - DataType type, - const Atom& value, - size_t buffer_size); - - void pre_process(ProcessContext& context); - void post_process(ProcessContext& context); - - virtual ~OutputPort() {} - - bool is_input() const { return false; } - bool is_output() const { return true; } -}; - - -} // namespace Ingen - -#endif // OUTPUTPORT_H diff --git a/src/libs/engine/PatchImpl.cpp b/src/libs/engine/PatchImpl.cpp deleted file mode 100644 index 9f0ae701..00000000 --- a/src/libs/engine/PatchImpl.cpp +++ /dev/null @@ -1,481 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include "ThreadManager.hpp" -#include "NodeImpl.hpp" -#include "PatchImpl.hpp" -#include "PatchPlugin.hpp" -#include "PortImpl.hpp" -#include "ConnectionImpl.hpp" -#include "DuplexPort.hpp" -#include "Engine.hpp" -#include "ProcessSlave.hpp" - -using namespace std; - -namespace Ingen { - - -PatchImpl::PatchImpl(Engine& engine, const string& path, uint32_t poly, PatchImpl* parent, SampleRate srate, size_t buffer_size, uint32_t internal_poly) - : NodeBase(new PatchPlugin("http://example.org/FIXME", "patch", "Ingen Patch"), - path, poly, parent, srate, buffer_size) - , _engine(engine) - , _internal_poly(internal_poly) - , _compiled_patch(NULL) - , _process(false) -{ - assert(internal_poly >= 1); -} - - -PatchImpl::~PatchImpl() -{ - assert(!_activated); - - delete _compiled_patch; -} - - -void -PatchImpl::activate() -{ - NodeBase::activate(); - - for (List::iterator i = _nodes.begin(); i != _nodes.end(); ++i) - (*i)->activate(); - - assert(_activated); -} - - -void -PatchImpl::deactivate() -{ - if (_activated) { - - NodeBase::deactivate(); - - for (List::iterator i = _nodes.begin(); i != _nodes.end(); ++i) { - if ((*i)->activated()) - (*i)->deactivate(); - assert(!(*i)->activated()); - } - } - assert(!_activated); -} - - -void -PatchImpl::disable() -{ - assert(ThreadManager::current_thread_id() == THREAD_PROCESS); - - _process = false; - - for (List::iterator i = _output_ports.begin(); i != _output_ports.end(); ++i) - (*i)->clear_buffers(); -} - - -bool -PatchImpl::prepare_internal_poly(uint32_t poly) -{ - assert(ThreadManager::current_thread_id() == THREAD_PRE_PROCESS); - - /* TODO: ports? internal/external poly? */ - - for (List::iterator i = _nodes.begin(); i != _nodes.end(); ++i) - (*i)->prepare_poly(poly); - - for (Connections::iterator i = _connections.begin(); i != _connections.end(); ++i) - ((ConnectionImpl*)i->get())->prepare_poly(poly); - - /* FIXME: Deal with failure */ - - return true; -} - - -bool -PatchImpl::apply_internal_poly(Raul::Maid& maid, uint32_t poly) -{ - assert(ThreadManager::current_thread_id() == THREAD_PROCESS); - - /* TODO: ports? internal/external poly? */ - - for (List::iterator i = _nodes.begin(); i != _nodes.end(); ++i) - (*i)->apply_poly(maid, poly); - - _internal_poly = poly; - - return true; -} - - -/** Run the patch for the specified number of frames. - * - * Calls all Nodes in (roughly, if parallel) the order _compiled_patch specifies. - */ -void -PatchImpl::process(ProcessContext& context) -{ - if (!_process) - return; - - NodeBase::pre_process(context); - - /*if (_ports) - for (size_t i=0; i < _ports->size(); ++i) - if (_ports->at(i)->is_input() && _ports->at(i)->type() == DataType::MIDI) - cerr << _ports->at(i)->path() << " " - << _ports->at(i)->buffer(0) << " # events: " - << ((MidiBuffer*)_ports->at(i)->buffer(0))->event_count() << endl;*/ - - /* Run */ - if (_compiled_patch && _compiled_patch->size() > 0) { - if (_engine.process_slaves().size() > 0) - process_parallel(context); - else - process_single(context); - } - - NodeBase::post_process(context); -} - - -void -PatchImpl::process_parallel(ProcessContext& context) -{ - size_t n_slaves = _engine.process_slaves().size(); - - CompiledPatch* const cp = _compiled_patch; - - /* Start p-1 slaves */ - - if (n_slaves >= cp->size()) - n_slaves = cp->size()-1; - - if (n_slaves > 0) { - for (size_t i=0; i < cp->size(); ++i) - (*cp)[i].node()->reset_input_ready(); - - for (size_t i=0; i < n_slaves; ++i) - _engine.process_slaves()[i]->whip(cp, i+1, context); - } - - - /* Process ourself until everything is done - * This is analogous to ProcessSlave::_whipped(), but this is the master - * (i.e. what the main Jack process thread calls). Where ProcessSlave - * waits on input, this just skips the node and tries the next, to avoid - * waiting in the Jack thread which pisses Jack off. - */ - - size_t index = 0; - size_t num_finished = 0; // Number of consecutive finished nodes hit - - while (num_finished < cp->size()) { - - CompiledNode& n = (*cp)[index]; - - if (n.node()->process_lock()) { - if (n.node()->n_inputs_ready() == n.n_providers()) { - n.node()->process(context); - - /* Signal dependants their input is ready */ - for (size_t i=0; i < n.dependants().size(); ++i) - n.dependants()[i]->signal_input_ready(); - - ++num_finished; - } else { - n.node()->process_unlock(); - num_finished = 0; - } - } else { - if (n.node()->n_inputs_ready() == n.n_providers()) - ++num_finished; - else - num_finished = 0; - } - - index = (index + 1) % cp->size(); - } - - /* Tell slaves we're done in case we beat them, and pray they're - * really done by the start of next cycle. - * FIXME: This probably breaks (race) at extremely small nframes where - * ingen is the majority of the DSP load. - */ - for (size_t i=0; i < n_slaves; ++i) - _engine.process_slaves()[i]->finish(); -} - - -void -PatchImpl::process_single(ProcessContext& context) -{ - CompiledPatch* const cp = _compiled_patch; - - for (size_t i=0; i < cp->size(); ++i) - (*cp)[i].node()->process(context); -} - - -void -PatchImpl::set_buffer_size(size_t size) -{ - NodeBase::set_buffer_size(size); - assert(_buffer_size == size); - - CompiledPatch* const cp = _compiled_patch; - - for (size_t i=0; i < cp->size(); ++i) - (*cp)[i].node()->set_buffer_size(size); -} - - -// Patch specific stuff - - -/** Add a node. - * Preprocessing thread only. - */ -void -PatchImpl::add_node(List::Node* ln) -{ - assert(ThreadManager::current_thread_id() == THREAD_PRE_PROCESS); - assert(ln != NULL); - assert(ln->elem() != NULL); - assert(ln->elem()->parent_patch() == this); - //assert(ln->elem()->polyphony() == _internal_poly); - - _nodes.push_back(ln); -} - - -/** Remove a node. - * Preprocessing thread only. - */ -PatchImpl::Nodes::Node* -PatchImpl::remove_node(const string& symbol) -{ - assert(ThreadManager::current_thread_id() == THREAD_PRE_PROCESS); - for (List::iterator i = _nodes.begin(); i != _nodes.end(); ++i) - if ((*i)->symbol() == symbol) - return _nodes.erase(i); - - return NULL; -} - - -/** Remove a connection. - * Preprocessing thread only. - */ -PatchImpl::Connections::Node* -PatchImpl::remove_connection(const PortImpl* src_port, const PortImpl* dst_port) -{ - assert(ThreadManager::current_thread_id() == THREAD_PRE_PROCESS); - bool found = false; - Connections::Node* connection = NULL; - for (Connections::iterator i = _connections.begin(); i != _connections.end(); ++i) { - ConnectionImpl* const c = (ConnectionImpl*)i->get(); - if (c->src_port() == src_port && c->dst_port() == dst_port) { - connection = _connections.erase(i); - found = true; - break; - } - } - - if ( ! found) - cerr << "WARNING: [PatchImpl::remove_connection] Connection not found !" << endl; - - return connection; -} - - -bool -PatchImpl::has_connection(const PortImpl* src_port, const PortImpl* dst_port) const -{ - // FIXME: Doesn't scale - for (Connections::const_iterator i = _connections.begin(); i != _connections.end(); ++i) { - ConnectionImpl* const c = (ConnectionImpl*)i->get(); - if (c->src_port() == src_port && c->dst_port() == dst_port) - return true; - } - - return false; -} - - -uint32_t -PatchImpl::num_ports() const -{ - ThreadID context = ThreadManager::current_thread_id(); - - if (context == THREAD_PROCESS) - return NodeBase::num_ports(); - else - return _input_ports.size() + _output_ports.size(); -} - - -/** Create a port. Not realtime safe. - */ -PortImpl* -PatchImpl::create_port(const string& name, DataType type, size_t buffer_size, bool is_output) -{ - if (type == DataType::UNKNOWN) { - cerr << "[PatchImpl::create_port] Unknown port type " << type.uri() << endl; - return NULL; - } - - assert( !(type == DataType::UNKNOWN) ); - - return new DuplexPort(this, name, num_ports(), _polyphony, type, Atom(), buffer_size, is_output); -} - - -/** Remove port from ports list used in pre-processing thread. - * - * Port is not removed from ports array for process thread (which could be - * simultaneously running). - * - * Realtime safe. Preprocessing thread only. - */ -List::Node* -PatchImpl::remove_port(const string& symbol) -{ - assert(ThreadManager::current_thread_id() == THREAD_PRE_PROCESS); - - bool found = false; - List::Node* ret = NULL; - for (List::iterator i = _input_ports.begin(); i != _input_ports.end(); ++i) { - if ((*i)->symbol() == symbol) { - ret = _input_ports.erase(i); - found = true; - } - } - - if (!found) - for (List::iterator i = _output_ports.begin(); i != _output_ports.end(); ++i) { - if ((*i)->symbol() == symbol) { - ret = _output_ports.erase(i); - found = true; - } - } - - if ( ! found) - cerr << "WARNING: [PatchImpl::remove_port] Port not found !" << endl; - - return ret; -} - - -/** Remove all ports from ports list used in pre-processing thread. - * - * Ports are not removed from ports array for process thread (which could be - * simultaneously running). Returned is a (inputs, outputs) pair. - * - * Realtime safe. Preprocessing thread only. - */ -void -PatchImpl::clear_ports() -{ - assert(ThreadManager::current_thread_id() == THREAD_PROCESS); - - _input_ports.clear(); - _output_ports.clear(); -} - - -Raul::Array* -PatchImpl::build_ports_array() const -{ - assert(ThreadManager::current_thread_id() == THREAD_PRE_PROCESS); - - Raul::Array* const result = new Raul::Array(_input_ports.size() + _output_ports.size()); - - size_t i = 0; - - for (List::const_iterator p = _input_ports.begin(); p != _input_ports.end(); ++p,++i) - result->at(i) = *p; - - for (List::const_iterator p = _output_ports.begin(); p != _output_ports.end(); ++p,++i) - result->at(i) = *p; - - return result; -} - - -/** 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). - * - * Not realtime safe. - */ -CompiledPatch* -PatchImpl::compile() const -{ - assert(ThreadManager::current_thread_id() == THREAD_PRE_PROCESS); - - //cerr << "*********** Building process order for " << path() << endl; - - CompiledPatch* const compiled_patch = new CompiledPatch();//_nodes.size()); - - for (Nodes::const_iterator i = _nodes.begin(); i != _nodes.end(); ++i) - (*i)->traversed(false); - - for (Nodes::const_iterator i = _nodes.begin(); i != _nodes.end(); ++i) { - NodeImpl* const node = (*i); - // Either a sink or connected to our output ports: - if ( ( ! node->traversed()) && node->dependants()->size() == 0) - compile_recursive(node, compiled_patch); - } - - // Traverse any nodes we didn't hit yet - for (Nodes::const_iterator i = _nodes.begin(); i != _nodes.end(); ++i) { - NodeImpl* const node = (*i); - if ( ! node->traversed()) - compile_recursive(node, compiled_patch); - } - - /*cerr << "----------------------------------------\n"; - for (size_t i=0; i < process_order->size(); ++i) { - assert(process_order->at(i)); - cerr << process_order->at(i)->path() << endl; - } - cerr << "----------------------------------------\n";*/ - - assert(compiled_patch->size() == _nodes.size()); - -#ifndef NDEBUG - for (size_t i=0; i < compiled_patch->size(); ++i) - assert(compiled_patch->at(i).node()); -#endif - - return compiled_patch; -} - - -} // namespace Ingen diff --git a/src/libs/engine/PatchImpl.hpp b/src/libs/engine/PatchImpl.hpp deleted file mode 100644 index 3629f6e5..00000000 --- a/src/libs/engine/PatchImpl.hpp +++ /dev/null @@ -1,168 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef PATCHIMPL_H -#define PATCHIMPL_H - -#include -#include -#include -#include -#include "interface/DataType.hpp" -#include "interface/Patch.hpp" -#include "NodeBase.hpp" -#include "PluginImpl.hpp" -#include "CompiledPatch.hpp" - -using std::string; - -template class Array; -using Raul::List; - -namespace Ingen { - -namespace Shared { class Connection; } - -class ConnectionImpl; -class Engine; -class CompiledPatch; - - -/** 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 PatchImpl : public NodeBase, public Ingen::Shared::Patch -{ -public: - PatchImpl(Engine& engine, - const string& name, - uint32_t poly, - PatchImpl* parent, - SampleRate srate, - size_t buffer_size, - uint32_t local_poly); - - virtual ~PatchImpl(); - - void activate(); - void deactivate(); - - void process(ProcessContext& context); - - void set_buffer_size(size_t size); - - /** Prepare for a new (internal) polyphony value. - * - * Preprocessor thread, poly is actually applied by apply_internal_poly. - * \return true on success. - */ - bool prepare_internal_poly(uint32_t poly); - - /** Apply a new (internal) polyphony value. - * - * Audio thread. - * - * \param poly Must be < the most recent value passed to prepare_internal_poly. - * \param maid Any objects no longer needed will be pushed to this - */ - bool apply_internal_poly(Raul::Maid& maid, uint32_t poly); - - // Patch specific stuff not inherited from Node - - typedef List Nodes; - - void add_node(Nodes::Node* tn); - Nodes::Node* remove_node(const string& name); - - Nodes& nodes() { return _nodes; } - Connections& connections() { return _connections; } - - const Nodes& nodes() const { return _nodes; } - const Connections& connections() const { return _connections; } - - uint32_t num_ports() const; - - PortImpl* create_port(const string& name, DataType type, size_t buffer_size, bool is_output); - void add_input(List::Node* port) { _input_ports.push_back(port); } ///< Preprocesser thread - void add_output(List::Node* port) { _output_ports.push_back(port); } ///< Preprocessor thread - List::Node* remove_port(const string& name); - void clear_ports(); - - void add_connection(Connections::Node* c) { _connections.push_back(c); } - Connections::Node* remove_connection(const PortImpl* src_port, const PortImpl* dst_port); - - bool has_connection(const PortImpl* src_port, const PortImpl* dst_port) const; - - CompiledPatch* compiled_patch() { return _compiled_patch; } - void compiled_patch(CompiledPatch* cp) { _compiled_patch = cp; } - - Raul::Array* external_ports() { return _ports; } - void external_ports(Raul::Array* pa) { _ports = pa; } - - CompiledPatch* compile() const; - Raul::Array* build_ports_array() const; - - /** Whether to run this patch's DSP bits in the audio thread */ - bool enabled() const { return _process; } - void enable() { _process = true; } - void disable(); - - uint32_t internal_polyphony() const { return _internal_poly; } - -private: - inline void compile_recursive(NodeImpl* n, CompiledPatch* output) const; - void process_parallel(ProcessContext& context); - void process_single(ProcessContext& context); - - Engine& _engine; - uint32_t _internal_poly; - CompiledPatch* _compiled_patch; ///< Accessed in audio thread only - Connections _connections; ///< Accessed in preprocessing thread only - List _input_ports; ///< Accessed in preprocessing thread only - List _output_ports; ///< Accessed in preprocessing thread only - Nodes _nodes; ///< Accessed in preprocessing thread only - bool _process; -}; - - - -/** Private helper for compile */ -inline void -PatchImpl::compile_recursive(NodeImpl* n, CompiledPatch* output) const -{ - if (n == NULL || n->traversed()) - return; - - n->traversed(true); - assert(output != NULL); - - for (List::iterator i = n->providers()->begin(); i != n->providers()->end(); ++i) - if ( ! (*i)->traversed() ) - compile_recursive((*i), output); - - output->push_back(CompiledNode(n, n->providers()->size(), n->dependants())); -} - - -} // namespace Ingen - -#endif // PATCHIMPL_H diff --git a/src/libs/engine/PatchPlugin.hpp b/src/libs/engine/PatchPlugin.hpp deleted file mode 100644 index a7334392..00000000 --- a/src/libs/engine/PatchPlugin.hpp +++ /dev/null @@ -1,64 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef PATCHPLUGIN_H -#define PATCHPLUGIN_H - -#include CONFIG_H_PATH - -#include -#include "PluginImpl.hpp" - -namespace Ingen { - -class NodeImpl; - - -/** Implementation of a Patch plugin. - * - * Patches don't actually work like this yet... - */ -class PatchPlugin : public PluginImpl -{ -public: - PatchPlugin(const std::string& uri, - const std::string& symbol, - const std::string& name) - : PluginImpl(Plugin::Patch, uri) - {} - - NodeImpl* instantiate(const std::string& name, - bool polyphonic, - Ingen::PatchImpl* parent, - Engine& engine) - { - return NULL; - } - - const string symbol() const { return "patch"; } - const string name() const { return "Ingen Patch"; } - -private: - const string _symbol; - const string _name; -}; - - -} // namespace Ingen - -#endif // PATCHPLUGIN_H - diff --git a/src/libs/engine/PluginImpl.cpp b/src/libs/engine/PluginImpl.cpp deleted file mode 100644 index 215cf4ce..00000000 --- a/src/libs/engine/PluginImpl.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include "PluginImpl.hpp" -#include "MidiNoteNode.hpp" -#include "MidiTriggerNode.hpp" -#include "MidiControlNode.hpp" -#include "TransportNode.hpp" - -using namespace std; - -namespace Ingen { - - -void -PluginImpl::load() -{ - if (!_module) { - //cerr << "Loading " << _library_path << " library" << endl; - _module = new Glib::Module(_library_path, Glib::MODULE_BIND_LOCAL); - if (!(*_module)) - delete _module; - } -} - - -void -PluginImpl::unload() -{ - if (_module) { - //cerr << "Unloading " << _library_path << endl; - delete _module; - _module = NULL; - } -} - - -} // namespace Ingen - diff --git a/src/libs/engine/PluginImpl.hpp b/src/libs/engine/PluginImpl.hpp deleted file mode 100644 index 0301d942..00000000 --- a/src/libs/engine/PluginImpl.hpp +++ /dev/null @@ -1,106 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef PLUGINIMPL_H -#define PLUGINIMPL_H - -#include CONFIG_H_PATH - -#include -#include -#include -#include -#include -#include -#include "types.hpp" -#include "interface/Plugin.hpp" - -using std::string; -using Ingen::Shared::Plugin; - -namespace Ingen { - -class PatchImpl; -class NodeImpl; -class Engine; - - -/** Implementation of a plugin (internal code, or a loaded shared library). - * - * Conceptually, a Node is an instance of this. - */ -class PluginImpl : public Ingen::Shared::Plugin, public boost::noncopyable -{ -public: - PluginImpl(Type type, const string& uri, const string library_path="") - : _type(type) - , _uri(uri) - , _library_path(library_path) - , _module(NULL) - {} - - virtual NodeImpl* instantiate(const std::string& name, - bool polyphonic, - Ingen::PatchImpl* parent, - Engine& engine) = 0; - - virtual const string symbol() const = 0; - virtual const string name() const = 0; - - const std::string& library_path() const { return _library_path; } - void library_path(const std::string& s) { _library_path = s;} - - void load(); - void unload(); - - const char* type_string() const { - if (_type == LADSPA) return "LADSPA"; - else if (_type == LV2) return "LV2"; - else if (_type == Internal) return "Internal"; - else if (_type == Patch) return "Patch"; - else return ""; - } - - const string type_uri() const { - return string("ingen:").append(type_string()); - } - - void set_type(const string& type_string) { - if (type_string == "LADSPA") _type = LADSPA; - else if (type_string == "LV2") _type = LV2; - else if (type_string == "Internal") _type = Internal; - else if (type_string == "Patch") _type = Patch; - } - - Plugin::Type type() const { return _type; } - void type(Plugin::Type t) { _type = t; } - const string& uri() const { return _uri; } - Glib::Module* module() const { return _module; } - void module(Glib::Module* module) { _module = module; } - -protected: - Plugin::Type _type; - const string _uri; - string _library_path; - Glib::Module* _module; -}; - - -} // namespace Ingen - -#endif // PLUGINIMPL_H - diff --git a/src/libs/engine/PortImpl.cpp b/src/libs/engine/PortImpl.cpp deleted file mode 100644 index 3eac65ca..00000000 --- a/src/libs/engine/PortImpl.cpp +++ /dev/null @@ -1,190 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include "PortImpl.hpp" -#include "NodeImpl.hpp" -#include "interface/DataType.hpp" -#include "AudioBuffer.hpp" -#include "EventBuffer.hpp" -#include "ProcessContext.hpp" -#include "SendPortActivityEvent.hpp" - -using namespace std; - -namespace Ingen { - - -PortImpl::PortImpl(NodeImpl* const node, - const string& name, - uint32_t index, - uint32_t poly, - DataType type, - const Atom& value, - size_t buffer_size) - : GraphObjectImpl(node, name, (type == DataType::AUDIO || type == DataType::CONTROL)) - , _index(index) - , _poly(poly) - , _buffer_size(buffer_size) - , _type(type) - , _value(value) - , _fixed_buffers(false) - , _broadcast(false) - , _set_by_user(false) - , _last_broadcasted_value(_value.type() == Atom::FLOAT ? _value.get_float() : 0.0f) // default? - , _context(Context::AUDIO) - , _buffers(new Raul::Array(poly)) -{ - assert(node != NULL); - assert(_poly > 0); - - allocate_buffers(); - clear_buffers(); - - if (node->parent() == NULL) - _polyphonic = false; - else - _polyphonic = true; - - if (type == DataType::EVENT) - _broadcast = true; // send activity blips - - assert(_buffers->size() > 0); -} - - -PortImpl::~PortImpl() -{ - for (uint32_t i=0; i < _poly; ++i) - delete _buffers->at(i); - - delete _buffers; -} - - -bool -PortImpl::set_polyphonic(Raul::Maid& maid, bool p) -{ - if (_type == DataType::CONTROL || _type == DataType::AUDIO) - return GraphObjectImpl::set_polyphonic(maid, p); - else - return (!p); -} - - -bool -PortImpl::prepare_poly(uint32_t poly) -{ - if (!_polyphonic || !_parent->polyphonic()) - return true; - - /* FIXME: poly never goes down, harsh on memory.. */ - if (poly > _poly) { - _prepared_buffers = new Raul::Array(poly, *_buffers); - for (uint32_t i = _poly; i < _prepared_buffers->size(); ++i) - _prepared_buffers->at(i) = Buffer::create(_type, _buffer_size); - } - - return true; -} - - -bool -PortImpl::apply_poly(Raul::Maid& maid, uint32_t poly) -{ - if (!_polyphonic || !_parent->polyphonic()) - return true; - - assert(poly <= _prepared_buffers->size()); - - // Apply a new set of buffers from a preceding call to prepare_poly - if (_prepared_buffers && _buffers != _prepared_buffers) { - maid.push(_buffers); - _buffers = _prepared_buffers; - } - - _poly = poly; - assert(_buffers->size() >= poly); - assert(this->poly() == poly); - - return true; -} - - -void -PortImpl::allocate_buffers() -{ - _buffers->alloc(_poly); - - for (uint32_t i=0; i < _poly; ++i) - _buffers->at(i) = Buffer::create(_type, _buffer_size); -} - - -void -PortImpl::set_buffer_size(size_t size) -{ - _buffer_size = size; - - for (uint32_t i=0; i < _poly; ++i) - _buffers->at(i)->resize(size); - - connect_buffers(); -} - - -void -PortImpl::connect_buffers() -{ - for (uint32_t i=0; i < _poly; ++i) - PortImpl::parent_node()->set_port_buffer(i, _index, buffer(i)); -} - - -void -PortImpl::clear_buffers() -{ - for (uint32_t i=0; i < _poly; ++i) - buffer(i)->clear(); -} - - -void -PortImpl::broadcast(ProcessContext& context) -{ - if (_broadcast) { - if (_type == DataType::CONTROL || _type == DataType::AUDIO) { - const Sample value = ((AudioBuffer*)buffer(0))->value_at(0); - if (value != _last_broadcasted_value) { - const SendPortValueEvent ev(context.engine(), context.start(), this, false, 0, value); - context.event_sink().write(sizeof(ev), &ev); - _last_broadcasted_value = value; - } - } else if (_type == DataType::EVENT) { - if (((EventBuffer*)buffer(0))->event_count() > 0) { - const SendPortActivityEvent ev(context.engine(), context.start(), this); - context.event_sink().write(sizeof(ev), &ev); - } - } - } -} - - - -} // namespace Ingen diff --git a/src/libs/engine/PortImpl.hpp b/src/libs/engine/PortImpl.hpp deleted file mode 100644 index be96a910..00000000 --- a/src/libs/engine/PortImpl.hpp +++ /dev/null @@ -1,147 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef PORTIMPL_H -#define PORTIMPL_H - -#include -#include -#include -#include "interface/Port.hpp" -#include "types.hpp" -#include "GraphObjectImpl.hpp" -#include "interface/DataType.hpp" -#include "Buffer.hpp" -#include "Context.hpp" - -namespace Raul { class Maid; class Atom; } - -namespace Ingen { - -class NodeImpl; -class Buffer; -class ProcessContext; - - -/** 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 PortImpl : public GraphObjectImpl, public Ingen::Shared::Port -{ -public: - virtual ~PortImpl(); - - /** A port's parent is always a node, so static cast should be safe */ - NodeImpl* parent_node() const { return (NodeImpl*)_parent; } - - bool set_polyphonic(Raul::Maid& maid, bool p); - - /** Prepare for a new (external) polyphony value. - * - * Preprocessor thread, poly is actually applied by apply_poly. - */ - virtual bool prepare_poly(uint32_t poly); - - /** Apply a new polyphony value. - * - * Audio thread. - * - * \param poly Must be < the most recent value passed to prepare_poly. - */ - virtual bool apply_poly(Raul::Maid& maid, uint32_t poly); - - const Raul::Atom& value() const { return _value; } - void set_value(const Raul::Atom& v) { _value = v; } - - inline Buffer* buffer(uint32_t voice) const { - Buffer* const buf = _buffers->at(voice); - if (buf->is_joined()) { - assert(buf->joined_buffer()); - return buf->joined_buffer(); - } else { - return buf; - } - } - - /** Called once per process cycle */ - virtual void pre_process(ProcessContext& context) = 0; - virtual void process(ProcessContext& context) {}; - virtual void post_process(ProcessContext& context) = 0; - - /** Empty buffer contents completely (ie silence) */ - virtual void clear_buffers(); - - virtual bool is_input() const = 0; - virtual bool is_output() const = 0; - - uint32_t index() const { return _index; } - uint32_t poly() const { return _poly; } - DataType type() const { return _type; } - size_t buffer_size() const { return _buffer_size; } - - virtual void set_buffer_size(size_t size); - - void fixed_buffers(bool b) { _fixed_buffers = b; } - bool fixed_buffers() { return _fixed_buffers; } - - void broadcast(bool b) { _broadcast = b; } - bool broadcast() { return _broadcast; } - - void raise_set_by_user_flag() { _set_by_user = true; } - - Context::ID context() const { return _context; } - void set_context(Context::ID c) { _context = c; } - -protected: - PortImpl(NodeImpl* node, - const std::string& name, - uint32_t index, - uint32_t poly, - DataType type, - const Raul::Atom& value, - size_t buffer_size); - - virtual void allocate_buffers(); - virtual void connect_buffers(); - virtual void broadcast(ProcessContext& context); - - uint32_t _index; - uint32_t _poly; - uint32_t _buffer_size; - DataType _type; - Raul::Atom _value; - bool _fixed_buffers; - bool _broadcast; - bool _set_by_user; - Sample _last_broadcasted_value; - - Context::ID _context; - Raul::Array* _buffers; - - // Dynamic polyphony - Raul::Array* _prepared_buffers; -}; - - -} // namespace Ingen - -#endif // PORTIMPL_H diff --git a/src/libs/engine/PostProcessor.cpp b/src/libs/engine/PostProcessor.cpp deleted file mode 100644 index a50ca275..00000000 --- a/src/libs/engine/PostProcessor.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -//#include -#include "events/SendPortValueEvent.hpp" -#include "Event.hpp" -#include "PostProcessor.hpp" -#include "Engine.hpp" -#include "AudioDriver.hpp" -#include "ProcessContext.hpp" - -using namespace std; - -namespace Ingen { - - -PostProcessor::PostProcessor(Engine& engine, size_t queue_size) - : _engine(engine) - , _max_time(0) - , _events(queue_size) - , _event_buffer_size(sizeof(SendPortValueEvent)) // FIXME: make generic - , _event_buffer((uint8_t*)malloc(_event_buffer_size)) -{ -} - - -void -PostProcessor::process() -{ - const FrameTime end_time = _max_time.get(); - - /* Process any audio thread generated events */ - /* FIXME: process events from all threads if parallel */ - - while (_engine.audio_driver()->context().event_sink().read( - _event_buffer_size, _event_buffer)) { - if (((Event*)_event_buffer)->time() > end_time) - break; // FIXME: loses event? - ((Event*)_event_buffer)->post_process(); - } - - /* Process normal events */ - while ( ! _events.empty()) { - Event* const ev = _events.front(); - if (ev->time() > end_time) - break; - _events.pop(); - assert(ev); - ev->post_process(); - delete ev; - } -} - - -} // namespace Ingen diff --git a/src/libs/engine/PostProcessor.hpp b/src/libs/engine/PostProcessor.hpp deleted file mode 100644 index 3d51136d..00000000 --- a/src/libs/engine/PostProcessor.hpp +++ /dev/null @@ -1,73 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef POSTPROCESSOR_H -#define POSTPROCESSOR_H - -#include -#include "types.hpp" -#include -//#include - -//namespace Raul { class Maid; } - -namespace Ingen { - -class Event; -class Engine; - - -/** 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. - * - * Update: This is all run from main_iteration now to solve scripting - * thread issues. Not sure if this is permanent/ideal or not... - * - * \ingroup engine - */ -class PostProcessor //: public Raul::Slave -{ -public: - PostProcessor(Engine& engine, /*Raul::Maid& maid, */size_t queue_size); - - /** Push an event on to the process queue, realtime-safe, not thread-safe. */ - inline void push(Event* const ev) { _events.push(ev); } - - /** Post-process and delete all pending events */ - void process(); - - /** Set the latest event time that should be post-processed */ - void set_end_time(FrameTime time) { _max_time = time; } - -private: - Engine& _engine; - Raul::AtomicInt _max_time; - //Raul::Maid& _maid; - Raul::SRSWQueue _events; - uint32_t _event_buffer_size; - uint8_t* _event_buffer; - - //virtual void _whipped(); -}; - - -} // namespace Ingen - -#endif // POSTPROCESSOR_H diff --git a/src/libs/engine/ProcessContext.hpp b/src/libs/engine/ProcessContext.hpp deleted file mode 100644 index 57677126..00000000 --- a/src/libs/engine/ProcessContext.hpp +++ /dev/null @@ -1,71 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef PROCESSCONTEXT_H -#define PROCESSCONTEXT_H - -#include "EventSink.hpp" -#include "Context.hpp" - -namespace Ingen { - - -/** Context of a process() call. - * - * This is used to pass whatever information a GraphObject might need to - * process in the audio thread, e.g. the available thread pool, sink for - * events (generated in the audio thread, not user initiated events), etc. - * - * Note the distinction between nframes and start/end. If transport speed - * != 1.0, end-start != nframes (though currently that is never the case, it - * may be in the future with sequencerey things). - * - * \ingroup engine - */ -class ProcessContext : public Context -{ -public: - ProcessContext(Engine& engine) - : Context(engine, AUDIO) - , _event_sink(engine, 1024) // FIXME: size? - {} - - void set_time_slice(SampleCount nframes, FrameTime start, FrameTime end) { - _nframes = nframes; - _start = start; - _end = end; - } - - inline SampleCount nframes() const { return _nframes; } - inline FrameTime start() const { return _start; } - inline FrameTime end() const { return _end; } - inline const EventSink& event_sink() const { return _event_sink; } - inline EventSink& event_sink() { return _event_sink; } - -private: - SampleCount _nframes; ///< Number of actual time (Jack) frames this cycle - FrameTime _start; ///< Start frame of this cycle, timeline relative - FrameTime _end; ///< End frame of this cycle, timeline relative - EventSink _event_sink; ///< Sink for events generated in the audio thread -}; - - - -} // namespace Ingen - -#endif // PROCESSCONTEXT_H - diff --git a/src/libs/engine/ProcessSlave.cpp b/src/libs/engine/ProcessSlave.cpp deleted file mode 100644 index c7c868e8..00000000 --- a/src/libs/engine/ProcessSlave.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include "ProcessSlave.hpp" -#include "NodeImpl.hpp" -#include "CompiledPatch.hpp" - -using namespace std; - -namespace Ingen { - - -uint32_t ProcessSlave::_next_id = 0; - - -void -ProcessSlave::_whipped() -{ - assert(_compiled_patch); - CompiledPatch* const cp = _compiled_patch; - - /* Iterate over all nodes attempting to run immediately or block then run, - * until we've been through the entire array without getting a lock, - * and thus are finished this cycle. - */ - - size_t num_finished = 0; // Number of consecutive finished nodes hit - - while (_state == STATE_RUNNING) { - - CompiledNode& n = (*cp)[_index]; - - if (n.node()->process_lock()) { - - n.node()->wait_for_input(n.n_providers()); - - n.node()->process(_process_context); - - /* Signal dependants their input is ready */ - for (size_t i=0; i < n.dependants().size(); ++i) - n.dependants()[i]->signal_input_ready(); - - num_finished = 1; - } else { - ++num_finished; - } - - _index = (_index + 1) % cp->size(); - - if (num_finished >= cp->size()) - break; - } - - _index = 0; - _compiled_patch = NULL; - _state = STATE_FINISHED; -} - - -} // namespace Ingen diff --git a/src/libs/engine/ProcessSlave.hpp b/src/libs/engine/ProcessSlave.hpp deleted file mode 100644 index 40becd48..00000000 --- a/src/libs/engine/ProcessSlave.hpp +++ /dev/null @@ -1,100 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef PROCESS_SLAVE_HPP -#define PROCESS_SLAVE_HPP - -#include CONFIG_H_PATH - -#include -#include -#include -#include -#include "ProcessContext.hpp" -#include "types.hpp" - -namespace Ingen { - -class NodeImpl; -class CompiledPatch; - - -class ProcessSlave : protected Raul::Slave { -public: - ProcessSlave(Engine& engine, bool realtime) - : _id(_next_id++) - , _index(0) - , _state(STATE_FINISHED) - , _compiled_patch(NULL) - , _process_context(engine) - { - std::stringstream ss; - ss << "Process Slave "; - ss << _id; - set_name(ss.str()); - - start(); - - if (realtime) - set_scheduling(SCHED_FIFO, 40); - } - - ~ProcessSlave() { - stop(); - } - - inline void whip(CompiledPatch* compiled_patch, uint32_t start_index, ProcessContext& context) { - assert(_state == STATE_FINISHED); - _index = start_index; - _state = STATE_RUNNING; - _compiled_patch = compiled_patch; - _process_context.set_time_slice(context.nframes(), context.start(), context.end()); - - Raul::Slave::whip(); - } - - inline void finish() { - while (_state.get() != STATE_FINISHED) - _state.compare_and_exchange(STATE_RUNNING, STATE_FINISH_SIGNALLED); - } - - inline uint32_t id() const { return _id; } - inline const ProcessContext& context() const { return _process_context; } - inline ProcessContext& context() { return _process_context; } - -private: - - void _whipped(); - - static uint32_t _next_id; - - static const int STATE_RUNNING = 0; - static const int STATE_FINISH_SIGNALLED = 1; - static const int STATE_FINISHED = 2; - - uint32_t _id; - uint32_t _index; - Raul::AtomicInt _state; - CompiledPatch* _compiled_patch; - ProcessContext _process_context; -}; - - -} // namespace Ingen - -#endif // PROCESS_SLAVE_HPP - diff --git a/src/libs/engine/QueuedEngineInterface.cpp b/src/libs/engine/QueuedEngineInterface.cpp deleted file mode 100644 index 962410b2..00000000 --- a/src/libs/engine/QueuedEngineInterface.cpp +++ /dev/null @@ -1,370 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include "QueuedEngineInterface.hpp" -#include CONFIG_H_PATH -#include "QueuedEventSource.hpp" -#include "events.hpp" -#include "Engine.hpp" -#include "AudioDriver.hpp" - -namespace Ingen { - -QueuedEngineInterface::QueuedEngineInterface(Engine& engine, size_t queued_size, size_t stamped_size) - : QueuedEventSource(queued_size, stamped_size) - , _responder(new Responder(NULL, 0)) - , _engine(engine) - , _in_bundle(false) -{ -} - - -SampleCount -QueuedEngineInterface::now() const -{ - // Exactly one cycle latency (some could run ASAP if we get lucky, but not always, and a slight - // constant latency is far better than jittery lower (average) latency - assert(_engine.audio_driver()); - return _engine.audio_driver()->frame_time() + _engine.audio_driver()->buffer_size(); -} - - -void -QueuedEngineInterface::set_next_response_id(int32_t id) -{ - if (_responder) - _responder->set_id(id); -} - - -void -QueuedEngineInterface::disable_responses() -{ - _responder->set_client(NULL); - _responder->set_id(0); -} - - -/* *** EngineInterface implementation below here *** */ - - -void -QueuedEngineInterface::register_client(ClientInterface* client) -{ - push_queued(new RegisterClientEvent(_engine, _responder, now(), client->uri(), client)); - if (!_responder) { - _responder = SharedPtr(new Responder(client, 1)); - } else { - _responder->set_id(1); - _responder->set_client(client); - } -} - - -void -QueuedEngineInterface::unregister_client(const string& uri) -{ - push_queued(new UnregisterClientEvent(_engine, _responder, now(), uri)); - if (_responder && _responder->client() && _responder->client()->uri() == uri) { - _responder->set_id(0); - _responder->set_client(NULL); - } -} - - - -// Engine commands -void -QueuedEngineInterface::load_plugins() -{ - push_queued(new LoadPluginsEvent(_engine, _responder, now(), this)); -} - - -void -QueuedEngineInterface::activate() -{ - QueuedEventSource::activate(); - push_queued(new PingQueuedEvent(_engine, _responder, now())); -} - - -void -QueuedEngineInterface::deactivate() -{ - push_queued(new DeactivateEvent(_engine, _responder, now())); -} - - -void -QueuedEngineInterface::quit() -{ - _responder->respond_ok(); - _engine.quit(); -} - - -// Bundle commands - -void -QueuedEngineInterface::bundle_begin() -{ - _in_bundle = true; -} - - -void -QueuedEngineInterface::bundle_end() -{ - _in_bundle = false; -} - - -// Object commands - -void -QueuedEngineInterface::new_patch(const string& path, - uint32_t poly) -{ - push_queued(new CreatePatchEvent(_engine, _responder, now(), path, poly)); -} - - -// FIXME: use index -void QueuedEngineInterface::new_port(const string& path, - uint32_t index, - const string& data_type, - bool direction) -{ - push_queued(new CreatePortEvent(_engine, _responder, now(), path, data_type, direction, this)); -} - - -void -QueuedEngineInterface::new_node(const string& path, - const string& plugin_uri) -{ - push_queued(new CreateNodeEvent(_engine, _responder, now(), - path, plugin_uri, true)); // FIXME: polyphonic by default -} - - -void -QueuedEngineInterface::new_node_deprecated(const string& path, - const string& plugin_type, - const string& plugin_lib, - const string& plugin_label) -{ - push_queued(new CreateNodeEvent(_engine, _responder, now(), - path, plugin_type, plugin_lib, plugin_label, true)); // FIXME: polyphonic by default -} - -void -QueuedEngineInterface::rename(const string& old_path, - const string& new_symbol) -{ - push_queued(new RenameEvent(_engine, _responder, now(), old_path, new_symbol)); -} - - -void -QueuedEngineInterface::destroy(const string& path) -{ - push_queued(new DestroyEvent(_engine, _responder, now(), this, path)); -} - - -void -QueuedEngineInterface::clear_patch(const string& patch_path) -{ - push_queued(new ClearPatchEvent(_engine, _responder, now(), this, patch_path)); -} - - -void -QueuedEngineInterface::set_polyphony(const string& patch_path, uint32_t poly) -{ - push_queued(new SetPolyphonyEvent(_engine, _responder, now(), this, patch_path, poly)); -} - - -void -QueuedEngineInterface::set_polyphonic(const string& path, bool poly) -{ - push_queued(new SetPolyphonicEvent(_engine, _responder, now(), this, path, poly)); -} - - -void -QueuedEngineInterface::connect(const string& src_port_path, - const string& dst_port_path) -{ - push_queued(new ConnectionEvent(_engine, _responder, now(), src_port_path, dst_port_path)); - -} - - -void -QueuedEngineInterface::disconnect(const string& src_port_path, - const string& dst_port_path) -{ - push_queued(new DisconnectionEvent(_engine, _responder, now(), src_port_path, dst_port_path)); -} - - -void -QueuedEngineInterface::disconnect_all(const string& patch_path, - const string& node_path) -{ - push_queued(new DisconnectAllEvent(_engine, _responder, now(), patch_path, node_path)); -} - - -void -QueuedEngineInterface::set_port_value(const string& port_path, - const Raul::Atom& value) -{ - push_queued(new SetPortValueEvent(_engine, _responder, true, now(), port_path, value)); -} - - -void -QueuedEngineInterface::set_voice_value(const string& port_path, - uint32_t voice, - const Raul::Atom& value) -{ - push_queued(new SetPortValueEvent(_engine, _responder, true, now(), voice, port_path, value)); -} - - -void -QueuedEngineInterface::set_program(const string& node_path, - uint32_t bank, - uint32_t program) -{ - std::cerr << "FIXME: set program" << std::endl; -} - - -void -QueuedEngineInterface::midi_learn(const string& node_path) -{ - push_queued(new MidiLearnEvent(_engine, _responder, now(), node_path)); -} - - -void -QueuedEngineInterface::set_variable(const string& path, - const string& predicate, - const Atom& value) -{ - push_queued(new SetMetadataEvent(_engine, _responder, now(), false, path, predicate, value)); -} - - -void -QueuedEngineInterface::set_property(const string& path, - const string& predicate, - const Atom& value) -{ - // FIXME: implement generically - if (predicate == "ingen:enabled") { - if (value.type() == Atom::BOOL) { - push_queued(new EnablePatchEvent(_engine, _responder, now(), path, value.get_bool())); - return; - } - } else if (predicate == "ingen:polyphonic") { - if (value.type() == Atom::BOOL) { - push_queued(new SetPolyphonicEvent(_engine, _responder, now(), this, path, value.get_bool())); - return; - } - } else if (predicate == "ingen:polyphony") { - if (value.type() == Atom::INT) { - push_queued(new SetPolyphonyEvent(_engine, _responder, now(), this, path, value.get_int32())); - return; - } - } else { - push_queued(new SetMetadataEvent(_engine, _responder, now(), true, path, predicate, value)); - } -} - -// Requests // - -void -QueuedEngineInterface::ping() -{ - if (_engine.activated()) { - push_queued(new PingQueuedEvent(_engine, _responder, now())); - } else if (_responder) { - _responder->respond_ok(); - } -} - - -void -QueuedEngineInterface::request_plugin(const string& uri) -{ - push_queued(new RequestPluginEvent(_engine, _responder, now(), uri)); -} - - -void -QueuedEngineInterface::request_object(const string& path) -{ - push_queued(new RequestObjectEvent(_engine, _responder, now(), path)); -} - - -void -QueuedEngineInterface::request_port_value(const string& port_path) -{ - push_queued(new RequestPortValueEvent(_engine, _responder, now(), port_path)); -} - - -void -QueuedEngineInterface::request_variable(const string& object_path, const string& key) -{ - push_queued(new RequestMetadataEvent(_engine, _responder, now(), false, object_path, key)); -} - - -void -QueuedEngineInterface::request_property(const string& object_path, const string& key) -{ - push_queued(new RequestMetadataEvent(_engine, _responder, now(), true, object_path, key)); -} - - -void -QueuedEngineInterface::request_plugins() -{ - push_queued(new RequestPluginsEvent(_engine, _responder, now())); -} - - -void -QueuedEngineInterface::request_all_objects() -{ - push_queued(new RequestAllObjectsEvent(_engine, _responder, now())); -} - - -} // namespace Ingen - - diff --git a/src/libs/engine/QueuedEngineInterface.hpp b/src/libs/engine/QueuedEngineInterface.hpp deleted file mode 100644 index 73c790c8..00000000 --- a/src/libs/engine/QueuedEngineInterface.hpp +++ /dev/null @@ -1,170 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef QUEUEDENGINEINTERFACE_H -#define QUEUEDENGINEINTERFACE_H - -#include -#include -#include -#include -#include "interface/EngineInterface.hpp" -#include "interface/ClientInterface.hpp" -#include "Responder.hpp" -#include "QueuedEventSource.hpp" -#include "Engine.hpp" -using std::string; - -namespace Ingen { - -using Shared::ClientInterface; -using Shared::EngineInterface; -class Engine; - - -/** 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 QueuedEventSource, public EngineInterface -{ -public: - QueuedEngineInterface(Engine& engine, size_t queued_size, size_t stamped_size); - virtual ~QueuedEngineInterface() {} - - std::string uri() const { return "ingen:internal"; } - - void set_next_response_id(int32_t id); - - // Client registration - virtual void register_client(ClientInterface* client); - virtual void unregister_client(const string& uri); - - // Engine commands - virtual void load_plugins(); - virtual void activate(); - virtual void deactivate(); - virtual void quit(); - - // Bundles - virtual void bundle_begin(); - virtual void bundle_end(); - - // Object commands - - virtual void new_patch(const string& path, - uint32_t poly); - - virtual void new_port(const string& path, - uint32_t index, - const string& data_type, - bool direction); - - virtual void new_node(const string& path, - const string& plugin_uri); - - /** FIXME: DEPRECATED, REMOVE */ - virtual void new_node_deprecated(const string& path, - const string& plugin_type, - const string& lib_path, - const string& plug_label); - - 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 set_polyphony(const string& patch_path, uint32_t poly); - - virtual void set_polyphonic(const string& path, bool poly); - - 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& patch_path, - const string& node_path); - - virtual void set_port_value(const string& port_path, - const Raul::Atom& value); - - virtual void set_voice_value(const string& port_path, - uint32_t voice, - const Raul::Atom& value); - - 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_variable(const string& path, - const string& predicate, - const Raul::Atom& value); - - virtual void set_property(const string& path, - const string& predicate, - const Raul::Atom& value); - - // Requests // - - virtual void ping(); - virtual void request_plugin(const string& uri); - virtual void request_object(const string& path); - virtual void request_port_value(const string& port_path); - virtual void request_variable(const string& object_path, const string& key); - virtual void request_property(const string& object_path, const string& key); - virtual void request_plugins(); - virtual void request_all_objects(); - -protected: - - virtual void disable_responses(); - - SharedPtr _responder; ///< NULL if responding disabled - Engine& _engine; - bool _in_bundle; ///< True iff a bundle is currently being received - -private: - SampleCount now() const; -}; - - -} // namespace Ingen - -#endif // QUEUEDENGINEINTERFACE_H - diff --git a/src/libs/engine/QueuedEvent.cpp b/src/libs/engine/QueuedEvent.cpp deleted file mode 100644 index 8ed6fa02..00000000 --- a/src/libs/engine/QueuedEvent.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "QueuedEvent.hpp" -#include "ThreadManager.hpp" -#include "ProcessContext.hpp" - -namespace Ingen { - - -void -QueuedEvent::pre_process() -{ - assert(ThreadManager::current_thread_id() == THREAD_PRE_PROCESS); - assert(_pre_processed == false); - _pre_processed = true; -} - - -void -QueuedEvent::execute(ProcessContext& context) -{ - assert(_pre_processed); - assert(_time <= context.end()); - - // Didn't prepare in time. QueuedEvents aren't (necessarily) sample accurate - // so just run at the beginning of this cycle - if (_time <= context.start()) - _time = context.start(); - - Event::execute(context); -} - - -} // namespace Ingen - diff --git a/src/libs/engine/QueuedEvent.hpp b/src/libs/engine/QueuedEvent.hpp deleted file mode 100644 index e616d269..00000000 --- a/src/libs/engine/QueuedEvent.hpp +++ /dev/null @@ -1,83 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef QUEUEDEVENT_H -#define QUEUEDEVENT_H - -#include "Event.hpp" - -namespace Ingen { - -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(); - - virtual void execute(ProcessContext& context); - - /** If this event blocks the prepare phase of other slow events */ - bool is_blocking() { return _blocking; } - - bool is_prepared() { return _pre_processed; } - -protected: - QueuedEvent(Engine& engine, - SharedPtr responder, - FrameTime time, - bool blocking = false, - QueuedEventSource* source = NULL) - : Event(engine, responder, time) - , _pre_processed(false), _blocking(blocking), _source(source) - { - if (blocking) - assert(_source); - } - - // NULL event base (for internal events only!) - QueuedEvent(Engine& engine) - : Event(engine, SharedPtr(), 0) - , _pre_processed(false), _blocking(false), _source(NULL) - {} - - bool _pre_processed; - bool _blocking; - QueuedEventSource* _source; -}; - - -} // namespace Ingen - -#endif // QUEUEDEVENT_H diff --git a/src/libs/engine/QueuedEventSource.cpp b/src/libs/engine/QueuedEventSource.cpp deleted file mode 100644 index 69ab805a..00000000 --- a/src/libs/engine/QueuedEventSource.cpp +++ /dev/null @@ -1,183 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include "QueuedEventSource.hpp" -#include "QueuedEvent.hpp" -#include "PostProcessor.hpp" -#include "ThreadManager.hpp" -#include "ProcessContext.hpp" - -using namespace std; - -namespace Ingen { - - -QueuedEventSource::QueuedEventSource(size_t queued_size, size_t stamped_size) - : _front(0) - , _back(0) - , _prepared_back(0) - , _size(queued_size+1) - , _blocking_semaphore(0) - , _full_semaphore(0) - , _stamped_queue(stamped_size) -{ - _events = (QueuedEvent**)calloc(_size, sizeof(QueuedEvent*)); - - mlock(_events, _size * sizeof(QueuedEvent*)); - - Thread::set_context(THREAD_PRE_PROCESS); - assert(context() == THREAD_PRE_PROCESS); - - set_name("QueuedEventSource"); -} - - -QueuedEventSource::~QueuedEventSource() -{ - Thread::stop(); - - free(_events); -} - - -/** Push an unprepared event onto the queue. - */ -void -QueuedEventSource::push_queued(QueuedEvent* const ev) -{ - assert(!ev->is_prepared()); - - unsigned back = _back.get(); - bool full = (((_front.get() - back + _size) % _size) == 1); - while (full) { - whip(); - cerr << "WARNING: Event queue full. Waiting..." << endl; - _full_semaphore.wait(); - back = _back.get(); - full = (((_front.get() - back + _size) % _size) == 1); - } - - assert(_events[back] == NULL); - _events[back] = ev; - _back = (back + 1) % _size; - whip(); -} - - -/** Process all events for a cycle. - * - * Executed events will be pushed to @a dest. - */ -void -QueuedEventSource::process(PostProcessor& dest, ProcessContext& context) -{ - assert(ThreadManager::current_thread_id() == THREAD_PROCESS); - - Event* ev = NULL; - - /* Limit the maximum number of queued events to process per cycle. This - * makes the process callback (more) realtime-safe by preventing being - * choked by events coming in faster than they can be processed. - * FIXME: test this and figure out a good value */ - const unsigned int MAX_QUEUED_EVENTS = context.nframes() / 100; - - unsigned int num_events_processed = 0; - - /* FIXME: Merge these next two loops into one */ - - while ((ev = pop_earliest_queued_before(context.end()))) { - ev->execute(context); - dest.push(ev); - if (++num_events_processed > MAX_QUEUED_EVENTS) - break; - } - - while ((ev = pop_earliest_stamped_before(context.end()))) { - ev->execute(context); - dest.push(ev); - ++num_events_processed; - } - - if (_full_semaphore.has_waiter() && num_events_processed > 0) - _full_semaphore.post(); - - /*if (num_events_processed > 0) - dest.whip();*/ - //else - // cerr << "NO PROC: queued: " << unprepared_events() << ", stamped: " << !_stamped_queue.empty() << endl; -} - - -/** Pops the prepared event at the front of the prepare 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 QueuedEvents, but after this they are "normal" events and the - * engine deals with them just like a realtime in-band event. The engine will - * not use the timestamps of the returned events in any way, since it is free - * to execute these non-time-stamped events whenever it wants (at whatever rate - * it wants). - */ -Event* -QueuedEventSource::pop_earliest_queued_before(const SampleCount time) -{ - assert(ThreadManager::current_thread_id() == THREAD_PROCESS); - - const unsigned front = _front.get(); - QueuedEvent* const front_event = _events[front]; - - // Pop - if (front_event && front_event->is_prepared() && front_event->time() < time) { - _events[front] = NULL; - _front = (front + 1) % _size; - return front_event; - } else { - return NULL; - } -} - - -// Private // - - -/** Pre-process a single event */ -void -QueuedEventSource::_whipped() -{ - const unsigned prepared_back = _prepared_back.get(); - QueuedEvent* const ev = _events[prepared_back]; - if (!ev) - return; - - assert(!ev->is_prepared()); - ev->pre_process(); - assert(ev->is_prepared()); - - _prepared_back = (prepared_back + 1) % _size; - - // If event was blocking, wait for event to being run through the - // process thread before preparing the next event - if (ev->is_blocking()) - _blocking_semaphore.wait(); -} - - -} // namespace Ingen - diff --git a/src/libs/engine/QueuedEventSource.hpp b/src/libs/engine/QueuedEventSource.hpp deleted file mode 100644 index 6dea092d..00000000 --- a/src/libs/engine/QueuedEventSource.hpp +++ /dev/null @@ -1,128 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef QUEUEDEVENTSOURCE_H -#define QUEUEDEVENTSOURCE_H - -#include -#include -#include "types.hpp" -#include -#include -#include -#include -#include "Event.hpp" -#include "EventSource.hpp" - -using Raul::AtomicInt; - -namespace Ingen { - -class QueuedEvent; -class PostProcessor; - - -/** 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). Creating an instance of this class spawns - * a pre-processing thread to prepare queued events. - * - * This class is it's own slave. :) - */ -class QueuedEventSource : public EventSource, protected Raul::Slave -{ -public: - QueuedEventSource(size_t queued_size, size_t stamped_size); - ~QueuedEventSource(); - - void activate() { Slave::start(); } - void deactivate() { Slave::stop(); } - - void process(PostProcessor& dest, ProcessContext& context); - - void unblock(); - -protected: - void push_queued(QueuedEvent* const ev); - inline void push_stamped(Event* const ev) { _stamped_queue.push(ev); } - Event* pop_earliest_queued_before(const SampleCount time); - inline Event* pop_earliest_stamped_before(const SampleCount time); - - inline bool unprepared_events() { return (_prepared_back.get() != _back.get()); } - - virtual void _whipped(); ///< Prepare 1 event - -private: - // Note that it's crucially important which functions access which of these - // variables, to maintain threadsafeness. - - //(FIXME: make this a separate class?) - // 2-part queue for events that require pre-processing: - AtomicInt _front; ///< Front of queue - AtomicInt _back; ///< Back of entire queue (1 past index of back element) - AtomicInt _prepared_back; ///< Back of prepared section (1 past index of back prepared element) - const size_t _size; - QueuedEvent** _events; - Raul::Semaphore _blocking_semaphore; - - Raul::Semaphore _full_semaphore; - - /** Queue for timestamped events (no pre-processing). */ - Raul::SRSWQueue _stamped_queue; -}; - - -/** Pops the realtime (timestamped, not preprocessed) event off the realtime queue. - * - * Engine will use the sample timestamps of returned events directly and execute the - * event with sample accuracy. Timestamps in the past will be bumped forward to - * the beginning of the cycle (offset 0), when eg. skipped cycles occur. - */ -inline Event* -QueuedEventSource::pop_earliest_stamped_before(const SampleCount time) -{ - Event* ret = NULL; - - if (!_stamped_queue.empty()) { - if (_stamped_queue.front()->time() < time) { - ret = _stamped_queue.front(); - _stamped_queue.pop(); - } - } - - return ret; -} - - -/** Signal that the blocking event is finished. - * - * When this is called preparing will resume. This MUST be called by - * blocking events in their post_process() method. - */ -inline void -QueuedEventSource::unblock() -{ - _blocking_semaphore.post(); -} - - -} // namespace Ingen - -#endif // QUEUEDEVENTSOURCE_H diff --git a/src/libs/engine/Responder.hpp b/src/libs/engine/Responder.hpp deleted file mode 100644 index 80d2e24c..00000000 --- a/src/libs/engine/Responder.hpp +++ /dev/null @@ -1,71 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef RESPONDER_H -#define RESPONDER_H - -#include -#include -#include -#include "interface/ClientInterface.hpp" - -namespace Ingen { - - -/** Class to handle responding to clients. - * - * This is a glorified std::pair for replying - * to numbered messages from a client. - * - * For responses that involve more messages, the response will come first - * followed by the messages (eg object notifications, values, errors, etc.) - * in a bundle (or "transfer" if too large). - */ -class Responder -{ -public: - Responder(Shared::ClientInterface* client=0, int32_t id=1) - : _client(client) - , _id(id) - {} - - int32_t id() const { return _id; } - void set_id(int32_t id) { _id = id; } - - Shared::ClientInterface* client() const { return _client; } - void set_client(Shared::ClientInterface* client) { _client = client; } - - void respond_ok() { - if (_client) - _client->response_ok(_id); - } - - void respond_error(const std::string& msg) { - if (_client) - _client->response_error(_id, msg); - } - -private: - Shared::ClientInterface* _client; - int32_t _id; -}; - - -} // namespace Ingen - -#endif // RESPONDER_H - diff --git a/src/libs/engine/ThreadManager.hpp b/src/libs/engine/ThreadManager.hpp deleted file mode 100644 index 12f27000..00000000 --- a/src/libs/engine/ThreadManager.hpp +++ /dev/null @@ -1,43 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef THREADMANAGER_H -#define THREADMANAGER_H - -#include - -using Raul::Thread; - -namespace Ingen { - - -enum ThreadID { - THREAD_PRE_PROCESS, - THREAD_PROCESS, - THREAD_POST_PROCESS -}; - - -class ThreadManager { -public: - inline static ThreadID current_thread_id() { return (ThreadID)Thread::get().context(); } -}; - - -} // namespace Ingen - -#endif // THREADMANAGER_H diff --git a/src/libs/engine/TransportNode.cpp b/src/libs/engine/TransportNode.cpp deleted file mode 100644 index 3fc14a43..00000000 --- a/src/libs/engine/TransportNode.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "TransportNode.hpp" -#include -#include "OutputPort.hpp" -#include "InternalPlugin.hpp" -#include "JackAudioDriver.hpp" -#include "PortImpl.hpp" -#include "util.hpp" -//#include "Engine.hpp" - -namespace Ingen { - - -TransportNode::TransportNode(const string& path, bool polyphonic, PatchImpl* parent, SampleRate srate, size_t buffer_size) -: NodeBase(new InternalPlugin(NS_INGEN "transport_node", "transport", "Transport Follower"), - path, false, parent, srate, buffer_size) -{ -#if 0 - _num_ports = 10; - _ports.alloc(_num_ports); - - OutputPort* spb_port = new OutputPort(this, "Seconds per Beat", 0, 1, - // new PortInfo("Seconds per Beat", CONTROL, OUTPUT, 0, 0, 1), 1); - _ports.at(0) = spb_port; - - OutputPort* bpb_port = new OutputPort(this, "Beats per Bar", 1, 1, - // new PortInfo("Beats per Bar", CONTROL, OUTPUT, 0, 0, 1), 1); - _ports.at(1) = bpb_port; - - OutputPort* bar_port = new OutputPort(this, "Bar", 3, 1, -// new PortInfo("Bar", CONTROL, OUTPUT, 0, 0, 1), buffer_size); - _ports.at(2) = bar_port; - - OutputPort* beat_port = new OutputPort(this, "Beat", 3, 1, - // new PortInfo("Beat", CONTROL, OUTPUT, 0, 0, 1), buffer_size); - _ports.at(3) = beat_port; - - OutputPort* frame_port = new OutputPort(this, "Frame", 3, 1, - // new PortInfo("Frame", CONTROL, OUTPUT, 0, 0, 1), buffer_size); - _ports.at(4) = frame_port; - - OutputPort* hour_port = new OutputPort(this, "Hour", 3, 1, - // new PortInfo("Hour", CONTROL, OUTPUT, 0, 0, 1), buffer_size); - _ports.at(5) = hour_port; - - OutputPort* minute_port = new OutputPort(this, "Minute", 3, 1, - // new PortInfo("Minute", CONTROL, OUTPUT, 0, 0, 1), buffer_size); - _ports.at(6) = minute_port; - - OutputPort* second_port = new OutputPort(this, "Second", 3, 1, - // new PortInfo("Second", CONTROL, OUTPUT, 0, 0, 1), buffer_size); - _ports.at(7) = second_port; - - OutputPort* trg_port = new OutputPort(this, "Beat Tick", 2, 1, - // new PortInfo("Beat Tick", AUDIO, OUTPUT, 0, 0, 1), buffer_size); - _ports.at(8) = trg_port; - - OutputPort* bar_trig_port = new OutputPort(this, "Bar Tick", 3, 1, - // new PortInfo("Bar Tick", AUDIO, OUTPUT, 0, 0, 1), buffer_size); - _ports.at(9) = bar_trig_port; -#endif -} - - -void -TransportNode::process(ProcessContext& context) -{ - NodeBase::pre_process(context); -#if 0 - - // FIXME: this will die horribly with any driver other than jack (in theory) - const jack_position_t* const position = ((JackAudioDriver*)Engine::instance().audio_driver())->position(); - jack_transport_state_t state = ((JackAudioDriver*)Engine::instance().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*)_ports.at(0))->buffer(0)->set(spb, 0, 0); - ((OutputPort*)_ports.at(1))->buffer(0)->set(bpb, 0, 0); - - // fill the trigger buffers with zeros - ((OutputPort*)_ports.at(2))->buffer(0)->set(0.0f, 0, nframes - 1); - ((OutputPort*)_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*)_ports.at(2))->buffer(0)->set(1.0f, size_t(first_beat)); - if (first_beat_no % int(bpb) == 0) { - ((OutputPort*)_ports.at(3))->buffer(0)->set(1.0f, size_t(first_beat)); - ++first_beat_no; - } - } - } - #endif - - NodeBase::post_process(context); -} - - -} // namespace Ingen - diff --git a/src/libs/engine/TransportNode.hpp b/src/libs/engine/TransportNode.hpp deleted file mode 100644 index af0ed207..00000000 --- a/src/libs/engine/TransportNode.hpp +++ /dev/null @@ -1,45 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef TRANSPORTNODE_H -#define TRANSPORTNODE_H - -#include -#include -#include "NodeBase.hpp" - -namespace Ingen { - - -/** 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 NodeBase -{ -public: - TransportNode(const std::string& path, bool polyphonic, PatchImpl* parent, SampleRate srate, size_t buffer_size); - - virtual void process(ProcessContext& context); -}; - - -} // namespace Ingen - -#endif // TRANSPORTNODE_H diff --git a/src/libs/engine/engine.cpp b/src/libs/engine/engine.cpp deleted file mode 100644 index 45c17354..00000000 --- a/src/libs/engine/engine.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include CONFIG_H_PATH - -#include -#include "engine.hpp" -#include "Engine.hpp" -#include "QueuedEngineInterface.hpp" -#include "tuning.hpp" -#include "util.hpp" - -namespace Ingen { - -Engine* -new_engine(Ingen::Shared::World* world) -{ - set_denormal_flags(); - return new Engine(world); -} - - -bool -launch_osc_engine(int port) -{ - char port_str[6]; - snprintf(port_str, 6, "%u", port); - const string cmd = string("ingen -e --engine-port=").append(port_str); - - if (Raul::Process::launch(cmd)) { - return true; - //return SharedPtr(new OSCEngineSender( - // string("osc.udp://localhost:").append(port_str))); - } else { - std::cerr << "Failed to launch engine process." << std::endl; - //return SharedPtr(); - return false; - } -} - -} // namespace Ingen - diff --git a/src/libs/engine/engine.hpp b/src/libs/engine/engine.hpp deleted file mode 100644 index 93b426ac..00000000 --- a/src/libs/engine/engine.hpp +++ /dev/null @@ -1,42 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef INGEN_ENGINE_H -#define INGEN_ENGINE_H - -namespace Ingen { - -namespace Shared { class World; } - -class Engine; - -extern "C" { - - /** Create a new engine in this process */ - Engine* new_engine(Ingen::Shared::World* world); - - /** Launch an OSC engine as a completely separate process - * \return true if successful - */ - bool launch_osc_engine(int port); -} - - -} // namespace Ingen - -#endif // INGEN_ENGINE_H - diff --git a/src/libs/engine/events.hpp b/src/libs/engine/events.hpp deleted file mode 100644 index 14f5230c..00000000 --- a/src/libs/engine/events.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef EVENTS_H -#define EVENTS_H - -#include CONFIG_H_PATH - -#include "AllNotesOffEvent.hpp" -#include "ClearPatchEvent.hpp" -#include "ConnectionEvent.hpp" -#include "CreateNodeEvent.hpp" -#include "CreatePatchEvent.hpp" -#include "CreatePortEvent.hpp" -#include "DeactivateEvent.hpp" -#include "DestroyEvent.hpp" -#include "DisconnectAllEvent.hpp" -#include "DisconnectionEvent.hpp" -#include "EnablePatchEvent.hpp" -#include "LoadPluginsEvent.hpp" -#include "MidiLearnEvent.hpp" -#include "NoteEvent.hpp" -#include "PingQueuedEvent.hpp" -#include "RegisterClientEvent.hpp" -#include "RenameEvent.hpp" -#include "RequestAllObjectsEvent.hpp" -#include "RequestMetadataEvent.hpp" -#include "RequestObjectEvent.hpp" -#include "RequestPluginEvent.hpp" -#include "RequestPluginsEvent.hpp" -#include "RequestPortValueEvent.hpp" -#include "SetMetadataEvent.hpp" -#include "SetPolyphonicEvent.hpp" -#include "SetPolyphonyEvent.hpp" -#include "SetPortValueEvent.hpp" -#include "UnregisterClientEvent.hpp" - -#endif // EVENTS_H - diff --git a/src/libs/engine/events/AllNotesOffEvent.cpp b/src/libs/engine/events/AllNotesOffEvent.cpp deleted file mode 100644 index fcb68b31..00000000 --- a/src/libs/engine/events/AllNotesOffEvent.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "AllNotesOffEvent.hpp" -#include "Responder.hpp" -#include "Engine.hpp" -#include "EngineStore.hpp" -#include "module/World.hpp" -#include "shared/Store.hpp" - -namespace Ingen { - - -/** Note off with patch explicitly passed - triggered by MIDI. - */ -AllNotesOffEvent::AllNotesOffEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, PatchImpl* patch) -: Event(engine, responder, timestamp), - _patch(patch) -{ -} - - -/** Note off event with lookup - triggered by OSC. - */ -AllNotesOffEvent::AllNotesOffEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& patch_path) -: Event(engine, responder, timestamp), - _patch_path(patch_path), - _patch(NULL) -{ -} - - -void -AllNotesOffEvent::execute(ProcessContext& context) -{ - Event::execute(context); - - if (_patch == NULL && _patch_path != "") - _patch = _engine.engine_store()->find_patch(_patch_path); - - //if (_patch != NULL) - // for (Raul::List::iterator j = _patch->midi_in_nodes().begin(); j != _patch->midi_in_nodes().end(); ++j) - // (*j)->all_notes_off(offset); -} - - -void -AllNotesOffEvent::post_process() -{ - if (_patch != NULL) - _responder->respond_ok(); -} - - -} // namespace Ingen - - diff --git a/src/libs/engine/events/AllNotesOffEvent.hpp b/src/libs/engine/events/AllNotesOffEvent.hpp deleted file mode 100644 index 3e4d56b3..00000000 --- a/src/libs/engine/events/AllNotesOffEvent.hpp +++ /dev/null @@ -1,51 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef ALLNOTESOFFEVENT_H -#define ALLNOTESOFFEVENT_H - -#include "Event.hpp" -#include -using std::string; - -namespace Ingen { - -class PatchImpl; - - -/** A note off event for all active voices. - * - * \ingroup engine - */ -class AllNotesOffEvent : public Event -{ -public: - AllNotesOffEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, PatchImpl* patch); - AllNotesOffEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& patch_path); - - void execute(ProcessContext& context); - void post_process(); - -private: - const string _patch_path; - PatchImpl* _patch; -}; - - -} // namespace Ingen - -#endif // ALLNOTESOFFEVENT_H diff --git a/src/libs/engine/events/ClearPatchEvent.cpp b/src/libs/engine/events/ClearPatchEvent.cpp deleted file mode 100644 index c1fb0749..00000000 --- a/src/libs/engine/events/ClearPatchEvent.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include "ClearPatchEvent.hpp" -#include "Responder.hpp" -#include "Engine.hpp" -#include "PatchImpl.hpp" -#include "ClientBroadcaster.hpp" -#include "util.hpp" -#include "EngineStore.hpp" -#include "PortImpl.hpp" -#include "NodeImpl.hpp" -#include "ConnectionImpl.hpp" -#include "QueuedEventSource.hpp" -#include "AudioDriver.hpp" -#include "MidiDriver.hpp" - -namespace Ingen { - - -ClearPatchEvent::ClearPatchEvent(Engine& engine, SharedPtr responder, FrameTime time, QueuedEventSource* source, const string& patch_path) - : QueuedEvent(engine, responder, time, true, source) - , _patch_path(patch_path) - , _driver_port(NULL) - , _process(false) - , _ports_array(NULL) - , _compiled_patch(NULL) -{ -} - - -void -ClearPatchEvent::pre_process() -{ - EngineStore::Objects::iterator patch_iterator = _engine.engine_store()->find(_patch_path); - - if (patch_iterator != _engine.engine_store()->end()) { - _patch = PtrCast(patch_iterator->second); - if (_patch) { - _process = _patch->enabled(); - _removed_table = _engine.engine_store()->remove_children(patch_iterator); - _patch->nodes().clear(); - _patch->connections().clear(); - _ports_array = _patch->build_ports_array(); - if (_patch->enabled()) - _compiled_patch = _patch->compile(); - } - } - - QueuedEvent::pre_process(); -} - - -void -ClearPatchEvent::execute(ProcessContext& context) -{ - QueuedEvent::execute(context); - - if (_patch && _removed_table) { - _patch->disable(); - - if (_patch->compiled_patch() != NULL) { - _engine.maid()->push(_patch->compiled_patch()); - _patch->compiled_patch(NULL); - } - - _patch->clear_ports(); - _patch->connections().clear(); - _patch->compiled_patch(_compiled_patch); - Raul::Array* old_ports = _patch->external_ports(); - _patch->external_ports(_ports_array); - _ports_array = old_ports; - - // Remove driver ports, if necessary - if (_patch->parent() == NULL) { - for (EngineStore::Objects::iterator i = _removed_table->begin(); i != _removed_table->end(); ++i) { - SharedPtr port = PtrCast(i->second); - if (port && port->type() == DataType::AUDIO) - _driver_port = _engine.audio_driver()->remove_port(port->path()); - else if (port && port->type() == DataType::EVENT) - _driver_port = _engine.midi_driver()->remove_port(port->path()); - } - } - } -} - - -void -ClearPatchEvent::post_process() -{ - if (_patch != NULL) { - delete _ports_array; - delete _driver_port; - - // Restore patch's run state - if (_process) - _patch->enable(); - else - _patch->disable(); - - // Make sure everything's sane - assert(_patch->nodes().size() == 0); - assert(_patch->num_ports() == 0); - assert(_patch->connections().size() == 0); - - // Reply - _responder->respond_ok(); - _engine.broadcaster()->send_patch_cleared(_patch_path); - } else { - _responder->respond_error(string("Patch ") + _patch_path + " not found"); - } - - _source->unblock(); // FIXME: can be done earlier in execute? -} - - -} // namespace Ingen - diff --git a/src/libs/engine/events/ClearPatchEvent.hpp b/src/libs/engine/events/ClearPatchEvent.hpp deleted file mode 100644 index 803721fb..00000000 --- a/src/libs/engine/events/ClearPatchEvent.hpp +++ /dev/null @@ -1,65 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef CLEARPATCHEVENT_H -#define CLEARPATCHEVENT_H - -#include -#include -#include -#include -#include "QueuedEvent.hpp" -#include "EngineStore.hpp" -#include "PatchImpl.hpp" - -using std::string; - -namespace Ingen { - -class PatchImpl; -class DriverPort; - - -/** Delete all nodes from a patch. - * - * \ingroup engine - */ -class ClearPatchEvent : public QueuedEvent -{ -public: - ClearPatchEvent(Engine& engine, SharedPtr responder, FrameTime time, QueuedEventSource* source, const string& patch_path); - - void pre_process(); - void execute(ProcessContext& context); - void post_process(); - -private: - const string _patch_path; - SharedPtr _patch; - DriverPort* _driver_port; - bool _process; - Raul::Array* _ports_array; ///< New (external) ports for Patch - CompiledPatch* _compiled_patch; ///< Patch's new process order - - SharedPtr< Table > > _removed_table; -}; - - -} // namespace Ingen - - -#endif // CLEARPATCHEVENT_H diff --git a/src/libs/engine/events/ConnectionEvent.cpp b/src/libs/engine/events/ConnectionEvent.cpp deleted file mode 100644 index 24b80dee..00000000 --- a/src/libs/engine/events/ConnectionEvent.cpp +++ /dev/null @@ -1,202 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - - -#include -#include -#include -#include -#include "ClientBroadcaster.hpp" -#include "ConnectionEvent.hpp" -#include "ConnectionImpl.hpp" -#include "Engine.hpp" -#include "InputPort.hpp" -#include "EngineStore.hpp" -#include "OutputPort.hpp" -#include "PatchImpl.hpp" -#include "PortImpl.hpp" -#include "Responder.hpp" -#include "types.hpp" - -using std::string; -namespace Ingen { - - -ConnectionEvent::ConnectionEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& src_port_path, const string& dst_port_path) -: QueuedEvent(engine, responder, timestamp), - _src_port_path(src_port_path), - _dst_port_path(dst_port_path), - _patch(NULL), - _src_port(NULL), - _dst_port(NULL), - _compiled_patch(NULL), - _patch_listnode(NULL), - _port_listnode(NULL), - _error(NO_ERROR) -{ -} - - -void -ConnectionEvent::pre_process() -{ - if (_src_port_path.parent().parent() != _dst_port_path.parent().parent() - && _src_port_path.parent() != _dst_port_path.parent().parent() - && _src_port_path.parent().parent() != _dst_port_path.parent()) { - _error = PARENT_PATCH_DIFFERENT; - QueuedEvent::pre_process(); - return; - } - - _src_port = _engine.engine_store()->find_port(_src_port_path); - _dst_port = _engine.engine_store()->find_port(_dst_port_path); - - if (_src_port == NULL || _dst_port == NULL) { - _error = PORT_NOT_FOUND; - QueuedEvent::pre_process(); - return; - } - - if ( ! (_src_port->type() == _dst_port->type() - || ( (_src_port->type() == DataType::CONTROL || _src_port->type() == DataType::AUDIO) - && (_dst_port->type() == DataType::CONTROL || _dst_port->type() == DataType::AUDIO) ))) { - _error = TYPE_MISMATCH; - QueuedEvent::pre_process(); - return; - } - - _dst_input_port = dynamic_cast(_dst_port); - _src_output_port = dynamic_cast(_src_port); - - if (!_dst_input_port || !_src_output_port) { - _error = DIRECTION_MISMATCH; - QueuedEvent::pre_process(); - return; - } - - NodeImpl* const src_node = _src_port->parent_node(); - NodeImpl* const dst_node = _dst_port->parent_node(); - - // Connection to a patch port from inside the patch - if (src_node->parent_patch() != dst_node->parent_patch()) { - - assert(src_node->parent() == dst_node || dst_node->parent() == src_node); - if (src_node->parent() == dst_node) - _patch = dynamic_cast(dst_node); - else - _patch = dynamic_cast(src_node); - - // Connection from a patch input to a patch output (pass through) - } else if (src_node == dst_node && dynamic_cast(src_node)) { - _patch = dynamic_cast(src_node); - - // Normal connection between nodes with the same parent - } else { - _patch = src_node->parent_patch(); - } - - assert(_patch); - - //if (_dst_input_port->is_connected_to(_src_output_port)) { - if (_patch->has_connection(_src_output_port, _dst_input_port)) { - _error = ALREADY_CONNECTED; - QueuedEvent::pre_process(); - return; - } - - if (src_node == NULL || dst_node == NULL) { - _error = PARENTS_NOT_FOUND; - QueuedEvent::pre_process(); - return; - } - - if (_patch != src_node && src_node->parent() != _patch && dst_node->parent() != _patch) { - _error = PARENTS_NOT_FOUND; - QueuedEvent::pre_process(); - return; - } - - _connection = SharedPtr(new ConnectionImpl(_src_port, _dst_port)); - _patch_listnode = new PatchImpl::Connections::Node(_connection); - _port_listnode = new InputPort::Connections::Node(_connection); - - // Need to be careful about patch port connections here and adding a node's - // parent as a dependant/provider, or adding a patch as it's own provider... - if (src_node != dst_node && src_node->parent() == dst_node->parent()) { - dst_node->providers()->push_back(new Raul::List::Node(src_node)); - src_node->dependants()->push_back(new Raul::List::Node(dst_node)); - } - - _patch->add_connection(_patch_listnode); - - if (_patch->enabled()) - _compiled_patch = _patch->compile(); - - QueuedEvent::pre_process(); -} - - -void -ConnectionEvent::execute(ProcessContext& context) -{ - QueuedEvent::execute(context); - - if (_error == NO_ERROR) { - // This must be inserted here, since they're actually used by the audio thread - _dst_input_port->add_connection(_port_listnode); - if (_patch->compiled_patch() != NULL) - _engine.maid()->push(_patch->compiled_patch()); - _patch->compiled_patch(_compiled_patch); - } -} - - -void -ConnectionEvent::post_process() -{ - std::ostringstream ss; - if (_error == NO_ERROR) { - _responder->respond_ok(); - _engine.broadcaster()->send_connection(_connection); - return; - } - - ss << boost::format("Unable to make connection %1% -> %2% (") % _src_port_path % _dst_port_path; - - switch (_error) { - case PARENT_PATCH_DIFFERENT: - ss << "Ports have mismatched parents"; break; - case PORT_NOT_FOUND: - ss << "Port not found"; break; - case TYPE_MISMATCH: - ss << "Type mismatch"; break; - case DIRECTION_MISMATCH: - ss << "Direction mismatch"; break; - case ALREADY_CONNECTED: - ss << "Already connected"; break; - case PARENTS_NOT_FOUND: - ss << "Parents not found"; break; - default: - ss << "Unknown error"; - } - ss << ")"; - _responder->respond_error(ss.str()); -} - - -} // namespace Ingen - diff --git a/src/libs/engine/events/ConnectionEvent.hpp b/src/libs/engine/events/ConnectionEvent.hpp deleted file mode 100644 index 89407957..00000000 --- a/src/libs/engine/events/ConnectionEvent.hpp +++ /dev/null @@ -1,92 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef CONNECTIONEVENT_H -#define CONNECTIONEVENT_H - -#include -#include -#include "QueuedEvent.hpp" -#include "PatchImpl.hpp" -#include "InputPort.hpp" -#include "types.hpp" -using std::string; - -namespace Raul { - template class ListNode; - template class Array; -} - -namespace Ingen { - -class PatchImpl; -class NodeImpl; -class ConnectionImpl; -class MidiMessage; -class PortImpl; -class InputPort; -class OutputPort; -class CompiledPatch; - - -/** Make a Connection between two Ports. - * - * \ingroup engine - */ -class ConnectionEvent : public QueuedEvent -{ -public: - ConnectionEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& src_port_path, const string& dst_port_path); - - void pre_process(); - void execute(ProcessContext& context); - void post_process(); - -private: - - enum ErrorType { - NO_ERROR, - PARENT_PATCH_DIFFERENT, - PORT_NOT_FOUND, - TYPE_MISMATCH, - DIRECTION_MISMATCH, - ALREADY_CONNECTED, - PARENTS_NOT_FOUND - }; - - Raul::Path _src_port_path; - Raul::Path _dst_port_path; - - PatchImpl* _patch; - PortImpl* _src_port; - PortImpl* _dst_port; - OutputPort* _src_output_port; - InputPort* _dst_input_port; - - CompiledPatch* _compiled_patch; ///< New process order for Patch - - SharedPtr _connection; - PatchImpl::Connections::Node* _patch_listnode; - InputPort::Connections::Node* _port_listnode; - - ErrorType _error; -}; - - -} // namespace Ingen - -#endif // CONNECTIONEVENT_H diff --git a/src/libs/engine/events/CreateNodeEvent.cpp b/src/libs/engine/events/CreateNodeEvent.cpp deleted file mode 100644 index b58bc2c3..00000000 --- a/src/libs/engine/events/CreateNodeEvent.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include "module/World.hpp" -#include "CreateNodeEvent.hpp" -#include "Responder.hpp" -#include "PatchImpl.hpp" -#include "NodeImpl.hpp" -#include "PluginImpl.hpp" -#include "Engine.hpp" -#include "PatchImpl.hpp" -#include "NodeFactory.hpp" -#include "ClientBroadcaster.hpp" -#include "EngineStore.hpp" -#include "PortImpl.hpp" -#include "AudioDriver.hpp" - -namespace Ingen { - - -CreateNodeEvent::CreateNodeEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& path, - const string& plugin_uri, bool polyphonic) -: QueuedEvent(engine, responder, timestamp), - _path(path), - _plugin_uri(plugin_uri), - _polyphonic(polyphonic), - _patch(NULL), - _node(NULL), - _compiled_patch(NULL), - _node_already_exists(false) -{ -} - - -/** DEPRECATED: Construct from type, library name, and plugin label. - * - * Do not use. - */ -CreateNodeEvent::CreateNodeEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& path, - const string& plugin_type, const string& plugin_lib, const string& plugin_label, bool polyphonic) -: QueuedEvent(engine, responder, timestamp), - _path(path), - _plugin_type(plugin_type), - _plugin_lib(plugin_lib), - _plugin_label(plugin_label), - _polyphonic(polyphonic), - _patch(NULL), - _node(NULL), - _compiled_patch(NULL), - _node_already_exists(false) -{ -} - - -void -CreateNodeEvent::pre_process() -{ - if (_engine.engine_store()->find_object(_path) != NULL) { - _node_already_exists = true; - QueuedEvent::pre_process(); - return; - } - - _patch = _engine.engine_store()->find_patch(_path.parent()); - - PluginImpl* const plugin = (_plugin_uri != "") - ? _engine.node_factory()->plugin(_plugin_uri) - : _engine.node_factory()->plugin(_plugin_type, _plugin_lib, _plugin_label); - - if (_patch && plugin) { - - _node = plugin->instantiate(_path.name(), _polyphonic, _patch, _engine); - - if (_node != NULL) { - _node->activate(); - - // This can be done here because the audio thread doesn't touch the - // node tree - just the process order array - _patch->add_node(new PatchImpl::Nodes::Node(_node)); - //_node->add_to_store(_engine.engine_store()); - _engine.engine_store()->add(_node); - - // FIXME: not really necessary to build process order since it's not connected, - // just append to the list - if (_patch->enabled()) - _compiled_patch = _patch->compile(); - } - } - QueuedEvent::pre_process(); -} - - -void -CreateNodeEvent::execute(ProcessContext& context) -{ - QueuedEvent::execute(context); - - if (_node != NULL) { - if (_patch->compiled_patch() != NULL) - _engine.maid()->push(_patch->compiled_patch()); - _patch->compiled_patch(_compiled_patch); - } -} - - -void -CreateNodeEvent::post_process() -{ - string msg; - if (_node_already_exists) { - msg = string("Could not create node - ").append(_path);// + " already exists."; - _responder->respond_error(msg); - } else if (_patch == NULL) { - msg = "Could not find patch '" + _path.parent() +"' for add_node."; - _responder->respond_error(msg); - } else if (_node == NULL) { - msg = "Unable to load node "; - msg += _path + " (you're missing the plugin "; - if (_plugin_uri != "") - msg += _plugin_uri; - else - msg += _plugin_lib + ":" + _plugin_label + " (" + _plugin_type + ")"; - msg += ")"; - _responder->respond_error(msg); - } else { - _responder->respond_ok(); - _engine.broadcaster()->send_node(_node, true); // yes, send ports - } -} - - -} // namespace Ingen - diff --git a/src/libs/engine/events/CreateNodeEvent.hpp b/src/libs/engine/events/CreateNodeEvent.hpp deleted file mode 100644 index c3ef6313..00000000 --- a/src/libs/engine/events/CreateNodeEvent.hpp +++ /dev/null @@ -1,81 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef CREATENODEEVENT_H -#define CREATENODEEVENT_H - -#include "QueuedEvent.hpp" -#include -#include -using std::string; - -namespace Raul { template class Array; } -template class TreeNode; - -namespace Ingen { - -class PatchImpl; -class NodeImpl; -class CompiledPatch; - - -/** An event to load a Node and insert it into a Patch. - * - * \ingroup engine - */ -class CreateNodeEvent : public QueuedEvent -{ -public: - CreateNodeEvent(Engine& engine, - SharedPtr responder, - SampleCount timestamp, - const string& node_path, - const string& plugin_uri, - bool poly); - - // DEPRECATED - CreateNodeEvent(Engine& engine, - SharedPtr responder, - SampleCount timestamp, - const string& node_path, - const string& plugin_type, - const string& lib_name, - const string& plugin_label, - bool poly); - - void pre_process(); - void execute(ProcessContext& context); - void post_process(); - -private: - string _patch_name; - Raul::Path _path; - string _plugin_uri; ///< If nonempty then type, library, label, are ignored - string _plugin_type; - string _plugin_lib; - string _plugin_label; - bool _polyphonic; - PatchImpl* _patch; - NodeImpl* _node; - CompiledPatch* _compiled_patch; ///< Patch's new process order - bool _node_already_exists; -}; - - -} // namespace Ingen - -#endif // CREATENODEEVENT_H diff --git a/src/libs/engine/events/CreatePatchEvent.cpp b/src/libs/engine/events/CreatePatchEvent.cpp deleted file mode 100644 index 64fe5c63..00000000 --- a/src/libs/engine/events/CreatePatchEvent.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include "CreatePatchEvent.hpp" -#include "Responder.hpp" -#include "PatchImpl.hpp" -#include "NodeImpl.hpp" -#include "PluginImpl.hpp" -#include "Engine.hpp" -#include "ClientBroadcaster.hpp" -#include "AudioDriver.hpp" -#include "EngineStore.hpp" - -namespace Ingen { - - -CreatePatchEvent::CreatePatchEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& path, int poly) -: QueuedEvent(engine, responder, timestamp), - _path(path), - _patch(NULL), - _parent(NULL), - _compiled_patch(NULL), - _poly(poly), - _error(NO_ERROR) -{ -} - - -void -CreatePatchEvent::pre_process() -{ - if (!Path::is_valid(_path)) { - _error = INVALID_PATH; - QueuedEvent::pre_process(); - return; - } - - if (_path == "/" || _engine.engine_store()->find_object(_path) != NULL) { - _error = OBJECT_EXISTS; - QueuedEvent::pre_process(); - return; - } - - if (_poly < 1) { - _error = INVALID_POLY; - QueuedEvent::pre_process(); - return; - } - - const Path& path = (const Path&)_path; - - _parent = _engine.engine_store()->find_patch(path.parent()); - if (_parent == NULL) { - _error = PARENT_NOT_FOUND; - QueuedEvent::pre_process(); - return; - } - - uint32_t poly = 1; - if (_parent != NULL && _poly > 1 && _poly == static_cast(_parent->internal_polyphony())) - poly = _poly; - - _patch = new PatchImpl(_engine, path.name(), poly, _parent, _engine.audio_driver()->sample_rate(), _engine.audio_driver()->buffer_size(), _poly); - - if (_parent != NULL) { - _parent->add_node(new PatchImpl::Nodes::Node(_patch)); - - if (_parent->enabled()) - _compiled_patch = _parent->compile(); - } - - _patch->activate(); - - // Insert into EngineStore - //_patch->add_to_store(_engine.engine_store()); - _engine.engine_store()->add(_patch); - - QueuedEvent::pre_process(); -} - - -void -CreatePatchEvent::execute(ProcessContext& context) -{ - QueuedEvent::execute(context); - - if (_patch != NULL) { - if (_parent == NULL) { - assert(_path == "/"); - assert(_patch->parent_patch() == NULL); - _engine.audio_driver()->set_root_patch(_patch); - } else { - assert(_parent != NULL); - assert(_path != "/"); - - if (_parent->compiled_patch() != NULL) - _engine.maid()->push(_parent->compiled_patch()); - _parent->compiled_patch(_compiled_patch); - } - } -} - - -void -CreatePatchEvent::post_process() -{ - if (_responder.get()) { - if (_error == NO_ERROR) { - - _responder->respond_ok(); - - // Don't send ports/nodes that have been added since prepare() - // (otherwise they would be sent twice) - _engine.broadcaster()->send_patch(_patch, false); - - } else if (_error == INVALID_PATH) { - string msg = "Attempt to create patch with illegal path "; - msg.append(_path); - _responder->respond_error(msg); - } else if (_error == OBJECT_EXISTS) { - _responder->respond_ok(); - /*string msg = "Unable to create patch: "; - msg.append(_path).append(" already exists."); - _responder->respond_error(msg);*/ - } else if (_error == PARENT_NOT_FOUND) { - string msg = "Unable to create patch: Parent "; - msg.append(Path(_path).parent()).append(" not found."); - _responder->respond_error(msg); - } else if (_error == INVALID_POLY) { - string msg = "Unable to create patch "; - msg.append(_path).append(": ").append("Invalid polyphony respondered."); - _responder->respond_error(msg); - } else { - _responder->respond_error("Unable to load patch."); - } - } -} - - -} // namespace Ingen - diff --git a/src/libs/engine/events/CreatePatchEvent.hpp b/src/libs/engine/events/CreatePatchEvent.hpp deleted file mode 100644 index 733aba17..00000000 --- a/src/libs/engine/events/CreatePatchEvent.hpp +++ /dev/null @@ -1,64 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef CREATEPATCHEVENT_H -#define CREATEPATCHEVENT_H - -#include -#include -#include "QueuedEvent.hpp" - -using std::string; - -namespace Raul { template class Array; } -template class TreeNode; - -namespace Ingen { - -class PatchImpl; -class CompiledPatch; - - -/** Creates a new Patch. - * - * \ingroup engine - */ -class CreatePatchEvent : public QueuedEvent -{ -public: - CreatePatchEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& path, int poly); - - void pre_process(); - void execute(ProcessContext& context); - void post_process(); - -private: - enum ErrorType { NO_ERROR, OBJECT_EXISTS, PARENT_NOT_FOUND, INVALID_POLY, INVALID_PATH }; - - const std::string _path; - PatchImpl* _patch; - PatchImpl* _parent; - CompiledPatch* _compiled_patch; - int _poly; - ErrorType _error; -}; - - -} // namespace Ingen - - -#endif // CREATEPATCHEVENT_H diff --git a/src/libs/engine/events/CreatePortEvent.cpp b/src/libs/engine/events/CreatePortEvent.cpp deleted file mode 100644 index e767f522..00000000 --- a/src/libs/engine/events/CreatePortEvent.cpp +++ /dev/null @@ -1,173 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include "Responder.hpp" -#include "CreatePortEvent.hpp" -#include "PatchImpl.hpp" -#include "PluginImpl.hpp" -#include "Engine.hpp" -#include "PatchImpl.hpp" -#include "QueuedEventSource.hpp" -#include "EngineStore.hpp" -#include "ClientBroadcaster.hpp" -#include "PortImpl.hpp" -#include "AudioDriver.hpp" -#include "MidiDriver.hpp" -#include "OSCDriver.hpp" -#include "DuplexPort.hpp" - -namespace Ingen { - - -CreatePortEvent::CreatePortEvent(Engine& engine, - SharedPtr responder, - SampleCount timestamp, - const string& path, - const string& type, - bool is_output, - QueuedEventSource* source) -: QueuedEvent(engine, responder, timestamp, true, source), - _error(NO_ERROR), - _path(path), - _type(type), - _is_output(is_output), - _data_type(type), - _patch(NULL), - _patch_port(NULL), - _driver_port(NULL) -{ - /* This is blocking because of the two different sets of Patch ports, the array used in the - * audio thread (inherited from NodeBase), and the arrays used in the pre processor thread. - * If two add port events arrive in the same cycle and the second pre processes before the - * first executes, bad things happen (ports are lost). - * - * FIXME: fix this using RCU - */ - - if (_data_type == DataType::UNKNOWN) { - cerr << "[CreatePortEvent] Unknown port type " << type << endl; - _error = UNKNOWN_TYPE; - } -} - - -void -CreatePortEvent::pre_process() -{ - if (_error == UNKNOWN_TYPE || _engine.engine_store()->find_object(_path)) { - QueuedEvent::pre_process(); - return; - } - - // FIXME: this is just a mess :/ - - _patch = _engine.engine_store()->find_patch(_path.parent()); - - if (_patch != NULL) { - assert(_patch->path() == _path.parent()); - - size_t buffer_size = 1; - if (_type != "ingen:Float") - buffer_size = _engine.audio_driver()->buffer_size(); - - const uint32_t old_num_ports = _patch->num_ports(); - - _patch_port = _patch->create_port(_path.name(), _data_type, buffer_size, _is_output); - - if (_patch_port) { - - if (_is_output) - _patch->add_output(new Raul::List::Node(_patch_port)); - else - _patch->add_input(new Raul::List::Node(_patch_port)); - - if (_patch->external_ports()) - _ports_array = new Raul::Array(old_num_ports + 1, *_patch->external_ports()); - else - _ports_array = new Raul::Array(old_num_ports + 1, NULL); - - - _ports_array->at(_patch->num_ports()-1) = _patch_port; - //_patch_port->add_to_store(_engine.engine_store()); - _engine.engine_store()->add(_patch_port); - - if (!_patch->parent()) { - if (_type == "ingen:AudioPort") - _driver_port = _engine.audio_driver()->create_port( - dynamic_cast(_patch_port)); - else if (_type == "ingen:MIDIPort" || _type == "ingen:EventPort") - _driver_port = _engine.midi_driver()->create_port( - dynamic_cast(_patch_port)); - else if (_type == "ingen:OSCPort" && _engine.osc_driver()) - _driver_port = _engine.osc_driver()->create_port( - dynamic_cast(_patch_port)); - } - - assert(_ports_array->size() == _patch->num_ports()); - - } - } - QueuedEvent::pre_process(); -} - - -void -CreatePortEvent::execute(ProcessContext& context) -{ - QueuedEvent::execute(context); - - if (_patch_port) { - - _engine.maid()->push(_patch->external_ports()); - //_patch->add_port(_port); - - _patch->external_ports(_ports_array); - } - - if (_driver_port) { - if (_type == "ingen:AudioPort") - _engine.audio_driver()->add_port(_driver_port); - else if (_type == "ingen:MIDIPort" || _type == "ingen:EventPort") - _engine.midi_driver()->add_port(_driver_port); - else if (_type == "ingen:OSCPort") - cerr << "OSC DRIVER PORT" << endl; - } - - if (_source) - _source->unblock(); -} - - -void -CreatePortEvent::post_process() -{ - if (_error != NO_ERROR || !_patch_port) { - const string msg = string("Could not create port - ").append(_path); - _responder->respond_error(msg); - } else { - _responder->respond_ok(); - _engine.broadcaster()->send_port(_patch_port); - } -} - - -} // namespace Ingen - diff --git a/src/libs/engine/events/CreatePortEvent.hpp b/src/libs/engine/events/CreatePortEvent.hpp deleted file mode 100644 index 5ddd8aa3..00000000 --- a/src/libs/engine/events/CreatePortEvent.hpp +++ /dev/null @@ -1,72 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef CREATEPORTEVENT_H -#define CREATEPORTEVENT_H - -#include "QueuedEvent.hpp" -#include -#include -#include "interface/DataType.hpp" -#include -using std::string; - -template class Array; - -namespace Ingen { - -class PatchImpl; -class PortImpl; -class DriverPort; - - -/** An event to add a Port to a Patch. - * - * \ingroup engine - */ -class CreatePortEvent : public QueuedEvent -{ -public: - CreatePortEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& path, const string& type, bool is_output, QueuedEventSource* source); - - void pre_process(); - void execute(ProcessContext& context); - void post_process(); - -private: - - enum ErrorType { - NO_ERROR, - UNKNOWN_TYPE - }; - - ErrorType _error; - Raul::Path _path; - string _type; - bool _is_output; - DataType _data_type; - PatchImpl* _patch; - PortImpl* _patch_port; - Raul::Array* _ports_array; ///< New (external) ports array for Patch - DriverPort* _driver_port; ///< Driver (eg Jack) port if this is a toplevel port - bool _succeeded; -}; - - -} // namespace Ingen - -#endif // CREATEPORTEVENT_H diff --git a/src/libs/engine/events/DeactivateEvent.cpp b/src/libs/engine/events/DeactivateEvent.cpp deleted file mode 100644 index a68419f0..00000000 --- a/src/libs/engine/events/DeactivateEvent.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "DeactivateEvent.hpp" -#include "Responder.hpp" -#include "Engine.hpp" - -namespace Ingen { - - -DeactivateEvent::DeactivateEvent(Engine& engine, SharedPtr responder, SampleCount timestamp) -: QueuedEvent(engine, responder, timestamp) -{ -} - - -void -DeactivateEvent::pre_process() -{ - QueuedEvent::pre_process(); -} - - -void -DeactivateEvent::execute(ProcessContext& context) -{ - QueuedEvent::execute(context); -} - - -void -DeactivateEvent::post_process() -{ - _responder->respond_ok(); - _engine.deactivate(); -} - - -} // namespace Ingen - diff --git a/src/libs/engine/events/DeactivateEvent.hpp b/src/libs/engine/events/DeactivateEvent.hpp deleted file mode 100644 index 5a6750ba..00000000 --- a/src/libs/engine/events/DeactivateEvent.hpp +++ /dev/null @@ -1,43 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef DEACTIVATEEVENT_H -#define DEACTIVATEEVENT_H - -#include "QueuedEvent.hpp" - -namespace Ingen { - - -/** Deactivates the engine. - * - * \ingroup engine - */ -class DeactivateEvent : public QueuedEvent -{ -public: - DeactivateEvent(Engine& engine, SharedPtr responder, SampleCount timestamp); - - void pre_process(); - void execute(ProcessContext& context); - void post_process(); -}; - - -} // namespace Ingen - -#endif // DEACTIVATEEVENT_H diff --git a/src/libs/engine/events/DestroyEvent.cpp b/src/libs/engine/events/DestroyEvent.cpp deleted file mode 100644 index 07770c0a..00000000 --- a/src/libs/engine/events/DestroyEvent.cpp +++ /dev/null @@ -1,201 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include "DestroyEvent.hpp" -#include "Responder.hpp" -#include "Engine.hpp" -#include "PatchImpl.hpp" -#include "NodeBase.hpp" -#include "PluginImpl.hpp" -#include "AudioDriver.hpp" -#include "MidiDriver.hpp" -#include "DisconnectAllEvent.hpp" -#include "ClientBroadcaster.hpp" -#include "EngineStore.hpp" -#include "QueuedEventSource.hpp" -#include "PortImpl.hpp" - -namespace Ingen { - - -DestroyEvent::DestroyEvent(Engine& engine, SharedPtr responder, FrameTime time, QueuedEventSource* source, const string& path, bool block) - : QueuedEvent(engine, responder, time, source, source) - , _path(path) - , _store_iterator(engine.engine_store()->end()) - , _driver_port(NULL) - , _patch_node_listnode(NULL) - , _patch_port_listnode(NULL) - , _ports_array(NULL) - , _compiled_patch(NULL) - , _disconnect_event(NULL) -{ - assert(_source); -} - - -DestroyEvent::~DestroyEvent() -{ - delete _disconnect_event; -} - - -void -DestroyEvent::pre_process() -{ - _store_iterator = _engine.engine_store()->find(_path); - - if (_store_iterator != _engine.engine_store()->end()) { - _node = PtrCast(_store_iterator->second); - - if (!_node) - _port = PtrCast(_store_iterator->second); - } - - if (_store_iterator != _engine.engine_store()->end()) { - _removed_table = _engine.engine_store()->remove(_store_iterator); - } - - if (_node != NULL && _path != "/") { - assert(_node->parent_patch()); - _patch_node_listnode = _node->parent_patch()->remove_node(_path.name()); - if (_patch_node_listnode) { - assert(_patch_node_listnode->elem() == _node.get()); - - _disconnect_event = new DisconnectAllEvent(_engine, _node->parent_patch(), _node.get()); - _disconnect_event->pre_process(); - - if (_node->parent_patch()->enabled()) { - // FIXME: is this called multiple times? - _compiled_patch = _node->parent_patch()->compile(); -#ifndef NDEBUG - // Be sure node is removed from process order, so it can be destroyed - for (size_t i=0; i < _compiled_patch->size(); ++i) { - assert(_compiled_patch->at(i).node() != _node.get()); - // FIXME: check providers/dependants too - } -#endif - } - } - } else if (_port) { - assert(_port->parent_patch()); - _patch_port_listnode = _port->parent_patch()->remove_port(_path.name()); - if (_patch_port_listnode) { - assert(_patch_port_listnode->elem() == _port.get()); - - _disconnect_event = new DisconnectAllEvent(_engine, _port->parent_patch(), _port.get()); - _disconnect_event->pre_process(); - - if (_port->parent_patch()->enabled()) { - // FIXME: is this called multiple times? - _compiled_patch = _port->parent_patch()->compile(); - _ports_array = _port->parent_patch()->build_ports_array(); - assert(_ports_array->size() == _port->parent_patch()->num_ports()); - } - } - - } - - QueuedEvent::pre_process(); -} - - -void -DestroyEvent::execute(ProcessContext& context) -{ - QueuedEvent::execute(context); - - if (_patch_node_listnode) { - assert(_node); - - if (_disconnect_event) - _disconnect_event->execute(context); - - if (_node->parent_patch()->compiled_patch()) - _engine.maid()->push(_node->parent_patch()->compiled_patch()); - _node->parent_patch()->compiled_patch(_compiled_patch); - - } else if (_patch_port_listnode) { - assert(_port); - - if (_disconnect_event) - _disconnect_event->execute(context); - - if (_port->parent_patch()->compiled_patch()) - _engine.maid()->push(_port->parent_patch()->compiled_patch()); - - _port->parent_patch()->compiled_patch(_compiled_patch); - - if (_port->parent_patch()->external_ports()) - _engine.maid()->push(_port->parent_patch()->external_ports()); - - _port->parent_patch()->external_ports(_ports_array); - - if ( ! _port->parent_patch()->parent()) { - if (_port->type() == DataType::AUDIO) - _driver_port = _engine.audio_driver()->remove_port(_port->path()); - else if (_port->type() == DataType::EVENT) - _driver_port = _engine.midi_driver()->remove_port(_port->path()); - } - } - - if (_source) - _source->unblock(); -} - - -void -DestroyEvent::post_process() -{ - if (!_node && !_port) { - if (_path == "/") { - _responder->respond_error("You can not destroy the root patch (/)"); - } else { - string msg = string("Could not find object ") + _path + " to destroy"; - _responder->respond_error(msg); - } - } - - if (_patch_node_listnode) { - assert(_node); - _node->deactivate(); - _responder->respond_ok(); - _engine.broadcaster()->bundle_begin(); - if (_disconnect_event) - _disconnect_event->post_process(); - _engine.broadcaster()->send_destroyed(_path); - _engine.broadcaster()->bundle_end(); - _engine.maid()->push(_patch_node_listnode); - } else if (_patch_port_listnode) { - assert(_port); - _responder->respond_ok(); - _engine.broadcaster()->bundle_begin(); - if (_disconnect_event) - _disconnect_event->post_process(); - _engine.broadcaster()->send_destroyed(_path); - _engine.broadcaster()->bundle_end(); - _engine.maid()->push(_patch_port_listnode); - } else { - _responder->respond_error("Unable to destroy object"); - } - - delete _driver_port; -} - - -} // namespace Ingen diff --git a/src/libs/engine/events/DestroyEvent.hpp b/src/libs/engine/events/DestroyEvent.hpp deleted file mode 100644 index b24934f8..00000000 --- a/src/libs/engine/events/DestroyEvent.hpp +++ /dev/null @@ -1,77 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef DESTROYEVENT_H -#define DESTROYEVENT_H - -#include -#include -#include "QueuedEvent.hpp" -#include "EngineStore.hpp" -#include "PatchImpl.hpp" - -using std::string; - -namespace Raul { - template class Array; - template class ListNode; -} -template class TreeNode; - -namespace Ingen { - -class GraphObjectImpl; -class NodeImpl; -class PortImpl; -class DriverPort; -class DisconnectAllEvent; -class CompiledPatch; - - -/** An event to remove and delete a Node. - * - * \ingroup engine - */ -class DestroyEvent : public QueuedEvent -{ -public: - DestroyEvent(Engine& engine, SharedPtr responder, FrameTime timestamp, QueuedEventSource* source, const string& path, bool block = true); - ~DestroyEvent(); - - void pre_process(); - void execute(ProcessContext& context); - void post_process(); - -private: - Path _path; - EngineStore::iterator _store_iterator; - SharedPtr _node; ///< Non-NULL iff a node - SharedPtr _port; ///< Non-NULL iff a port - DriverPort* _driver_port; - PatchImpl::Nodes::Node* _patch_node_listnode; - Raul::List::Node* _patch_port_listnode; - Raul::Array* _ports_array; ///< New (external) ports for Patch - CompiledPatch* _compiled_patch; ///< Patch's new process order - DisconnectAllEvent* _disconnect_event; - - SharedPtr< Table > > _removed_table; -}; - - -} // namespace Ingen - -#endif // DESTROYEVENT_H diff --git a/src/libs/engine/events/DisablePortMonitoringEvent.cpp b/src/libs/engine/events/DisablePortMonitoringEvent.cpp deleted file mode 100644 index cecc8dfd..00000000 --- a/src/libs/engine/events/DisablePortMonitoringEvent.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include "interface/ClientInterface.hpp" -#include "events/DisablePortMonitoringEvent.hpp" -#include "Responder.hpp" -#include "Engine.hpp" -#include "PortImpl.hpp" -#include "EngineStore.hpp" -#include "ClientBroadcaster.hpp" -#include "AudioBuffer.hpp" - -using std::string; - -namespace Ingen { - - -DisablePortMonitoringEvent::DisablePortMonitoringEvent(Engine& engine, - SharedPtr responder, - SampleCount timestamp, - const std::string& port_path) -: QueuedEvent(engine, responder, timestamp), - _port_path(port_path), - _port(NULL) -{ -} - - -void -DisablePortMonitoringEvent::pre_process() -{ - _port = _engine.engine_store()->find_port(_port_path); - - QueuedEvent::pre_process(); -} - - -void -DisablePortMonitoringEvent::execute(ProcessContext& context) -{ - QueuedEvent::execute(context); - -#if 0 - assert(_time >= start && _time <= end); - - if (_port != NULL && _port->type() == DataType::FLOAT) - _value = ((AudioBuffer*)_port->buffer(0))->value_at(0/*_time - start*/); - else - _port = NULL; // triggers error response -#endif -} - - -void -DisablePortMonitoringEvent::post_process() -{ -#if 0 - string msg; - if (!_port) { - _responder->respond_error("Unable to find port for get_value responder."); - } else if (_responder->client()) { - _responder->respond_ok(); - _responder->client()->control_change(_port_path, _value); - } else { - _responder->respond_error("Unable to find client to send port value"); - } -#endif -} - - -} // namespace Ingen - diff --git a/src/libs/engine/events/DisablePortMonitoringEvent.hpp b/src/libs/engine/events/DisablePortMonitoringEvent.hpp deleted file mode 100644 index 7a8e23f7..00000000 --- a/src/libs/engine/events/DisablePortMonitoringEvent.hpp +++ /dev/null @@ -1,58 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef DISABLEPORTNOTIFICATIONEVENT_H -#define DISABLEPORTNOTIFICATIONEVENT_H - -#include -#include "QueuedEvent.hpp" -#include "types.hpp" - -using std::string; - -namespace Ingen { - -class PortImpl; -namespace Shared { class ClientInterface; } -using Shared::ClientInterface; - - -/** Disable sending of dynamic value change notifications for a port. - * - * \ingroup engine - */ -class DisablePortMonitoringEvent : public QueuedEvent -{ -public: - DisablePortMonitoringEvent(Engine& engine, - SharedPtr responder, - SampleCount timestamp, - const std::string& port_path); - - void pre_process(); - void execute(ProcessContext& context); - void post_process(); - -private: - const std::string _port_path; - Port* _port; -}; - - -} // namespace Ingen - -#endif // DISABLEPORTNOTIFICATIONEVENT_H diff --git a/src/libs/engine/events/DisconnectAllEvent.cpp b/src/libs/engine/events/DisconnectAllEvent.cpp deleted file mode 100644 index 77b1b1b3..00000000 --- a/src/libs/engine/events/DisconnectAllEvent.cpp +++ /dev/null @@ -1,183 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include -#include "ClientBroadcaster.hpp" -#include "ConnectionImpl.hpp" -#include "DisconnectAllEvent.hpp" -#include "DisconnectionEvent.hpp" -#include "Engine.hpp" -#include "InputPort.hpp" -#include "NodeImpl.hpp" -#include "EngineStore.hpp" -#include "OutputPort.hpp" -#include "PatchImpl.hpp" -#include "PortImpl.hpp" -#include "Responder.hpp" -#include "util.hpp" - -namespace Ingen { - - -DisconnectAllEvent::DisconnectAllEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& parent_path, const string& node_path) - : QueuedEvent(engine, responder, timestamp) - , _parent_path(parent_path) - , _path(node_path) - , _parent(NULL) - , _node(NULL) - , _port(NULL) - , _lookup(true) - , _error(NO_ERROR) -{ -} - - -/** Internal version for use by other events. - */ -DisconnectAllEvent::DisconnectAllEvent(Engine& engine, PatchImpl* parent, GraphObjectImpl* object) - : QueuedEvent(engine) - , _parent_path(parent->path()) - , _path(object->path()) - , _parent(parent) - , _node(dynamic_cast(object)) - , _port(dynamic_cast(object)) - , _lookup(false) - , _error(NO_ERROR) -{ -} - - -DisconnectAllEvent::~DisconnectAllEvent() -{ - for (Raul::List::iterator i = _disconnection_events.begin(); i != _disconnection_events.end(); ++i) - delete (*i); -} - - -void -DisconnectAllEvent::pre_process() -{ - if (_lookup) { - _parent = _engine.engine_store()->find_patch(_parent_path); - - if (_parent == NULL) { - _error = PARENT_NOT_FOUND; - QueuedEvent::pre_process(); - return; - } - - GraphObjectImpl* object = _engine.engine_store()->find_object(_path); - - if (object == NULL) { - _error = OBJECT_NOT_FOUND; - QueuedEvent::pre_process(); - return; - } - - if (object->parent_patch() != _parent && object->parent()->parent_patch() != _parent) { - _error = INVALID_PARENT_PATH; - QueuedEvent::pre_process(); - return; - } - - // Only one of these will succeed - _node = dynamic_cast(object); - _port = dynamic_cast(object); - - assert((_node || _port) && !(_node && _port)); - } - - if (_node) { - for (PatchImpl::Connections::const_iterator i = _parent->connections().begin(); - i != _parent->connections().end(); ++i) { - ConnectionImpl* c = (ConnectionImpl*)i->get(); - if ((c->src_port()->parent_node() == _node || c->dst_port()->parent_node() == _node) - && !c->pending_disconnection()) { - DisconnectionEvent* ev = new DisconnectionEvent(_engine, - SharedPtr(new Responder()), _time, c->src_port(), c->dst_port()); - ev->pre_process(); - _disconnection_events.push_back(new Raul::List::Node(ev)); - c->pending_disconnection(true); - } - } - } else { // _port - for (PatchImpl::Connections::const_iterator i = _parent->connections().begin(); - i != _parent->connections().end(); ++i) { - ConnectionImpl* c = (ConnectionImpl*)i->get(); - if ((c->src_port() == _port || c->dst_port() == _port) && !c->pending_disconnection()) { - DisconnectionEvent* ev = new DisconnectionEvent(_engine, - SharedPtr(new Responder()), _time, c->src_port(), c->dst_port()); - ev->pre_process(); - _disconnection_events.push_back(new Raul::List::Node(ev)); - c->pending_disconnection(true); - } - } - } - - QueuedEvent::pre_process(); -} - - -void -DisconnectAllEvent::execute(ProcessContext& context) -{ - QueuedEvent::execute(context); - - if (_error == NO_ERROR) { - for (Raul::List::iterator i = _disconnection_events.begin(); i != _disconnection_events.end(); ++i) - (*i)->execute(context); - } -} - - -void -DisconnectAllEvent::post_process() -{ - if (_error == NO_ERROR) { - if (_responder) - _responder->respond_ok(); - for (Raul::List::iterator i = _disconnection_events.begin(); - i != _disconnection_events.end(); ++i) - (*i)->post_process(); - } else { - if (_responder) { - boost::format fmt("Unable to disconnect %1% (%2%)"); - fmt % _path; - switch (_error) { - case INVALID_PARENT_PATH: - fmt % string("Invalid parent path: ").append(_parent_path); - break; - case PARENT_NOT_FOUND: - fmt % string("Unable to find parent: ").append(_parent_path); - break; - case OBJECT_NOT_FOUND: - fmt % string("Unable to find object"); - default: - break; - } - _responder->respond_error(fmt.str()); - } - } -} - - -} // namespace Ingen - diff --git a/src/libs/engine/events/DisconnectAllEvent.hpp b/src/libs/engine/events/DisconnectAllEvent.hpp deleted file mode 100644 index 6b75e6df..00000000 --- a/src/libs/engine/events/DisconnectAllEvent.hpp +++ /dev/null @@ -1,79 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef DISCONNECTNODEEVENT_H -#define DISCONNECTNODEEVENT_H - -#include -#include -#include -#include "QueuedEvent.hpp" - -using std::string; - -namespace Ingen { - -class DisconnectionEvent; -class PatchImpl; -class NodeImpl; -class Connection; -class PortImpl; -class InputPort; -class OutputPort; - - -/** An event to disconnect all connections to a Node. - * - * \ingroup engine - */ -class DisconnectAllEvent : public QueuedEvent -{ -public: - DisconnectAllEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& parent_path, const string& node_path); - DisconnectAllEvent(Engine& engine, PatchImpl* parent, GraphObjectImpl* object); - ~DisconnectAllEvent(); - - void pre_process(); - void execute(ProcessContext& context); - void post_process(); - -private: - enum ErrorType { - NO_ERROR, - INVALID_PARENT_PATH, - PARENT_NOT_FOUND, - OBJECT_NOT_FOUND, - }; - - Raul::Path _parent_path; - Raul::Path _path; - PatchImpl* _parent; - NodeImpl* _node; - PortImpl* _port; - Raul::List _disconnection_events; - - bool _lookup; - bool _disconnect_parent; - - ErrorType _error; -}; - - -} // namespace Ingen - - -#endif // DISCONNECTNODEEVENT_H diff --git a/src/libs/engine/events/DisconnectionEvent.cpp b/src/libs/engine/events/DisconnectionEvent.cpp deleted file mode 100644 index 86ad8b4e..00000000 --- a/src/libs/engine/events/DisconnectionEvent.cpp +++ /dev/null @@ -1,213 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "DisconnectionEvent.hpp" -#include -#include -#include -#include "Responder.hpp" -#include "Engine.hpp" -#include "ConnectionImpl.hpp" -#include "InputPort.hpp" -#include "OutputPort.hpp" -#include "PatchImpl.hpp" -#include "ClientBroadcaster.hpp" -#include "PortImpl.hpp" -#include "EngineStore.hpp" - -using std::string; -namespace Ingen { - - -//// DisconnectionEvent //// - - -DisconnectionEvent::DisconnectionEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& src_port_path, const string& dst_port_path) -: QueuedEvent(engine, responder, timestamp), - _src_port_path(src_port_path), - _dst_port_path(dst_port_path), - _patch(NULL), - _src_port(NULL), - _dst_port(NULL), - _lookup(true), - _patch_connection(NULL), - _compiled_patch(NULL), - _error(NO_ERROR) -{ -} - - -DisconnectionEvent::DisconnectionEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, PortImpl* const src_port, PortImpl* const dst_port) -: QueuedEvent(engine, responder, timestamp), - _src_port_path(src_port->path()), - _dst_port_path(dst_port->path()), - _patch(src_port->parent_node()->parent_patch()), - _src_port(src_port), - _dst_port(dst_port), - _lookup(false), - _compiled_patch(NULL), - _error(NO_ERROR) -{ - // FIXME: These break for patch ports.. is that ok? - /*assert(src_port->is_output()); - assert(dst_port->is_input()); - assert(src_port->type() == dst_port->type()); - assert(src_port->parent_node()->parent_patch() - == dst_port->parent_node()->parent_patch()); */ -} - - -void -DisconnectionEvent::pre_process() -{ - if (_lookup) { - if (_src_port_path.parent().parent() != _dst_port_path.parent().parent() - && _src_port_path.parent() != _dst_port_path.parent().parent() - && _src_port_path.parent().parent() != _dst_port_path.parent()) { - _error = PARENT_PATCH_DIFFERENT; - QueuedEvent::pre_process(); - return; - } - - _src_port = _engine.engine_store()->find_port(_src_port_path); - _dst_port = _engine.engine_store()->find_port(_dst_port_path); - } - - if (_src_port == NULL || _dst_port == NULL) { - _error = PORT_NOT_FOUND; - QueuedEvent::pre_process(); - return; - } - - _dst_input_port = dynamic_cast(_dst_port); - _src_output_port = dynamic_cast(_src_port); - assert(_src_output_port); - assert(_dst_input_port); - - NodeImpl* const src_node = _src_port->parent_node(); - NodeImpl* const dst_node = _dst_port->parent_node(); - - // Connection to a patch port from inside the patch - if (src_node->parent_patch() != dst_node->parent_patch()) { - - assert(src_node->parent() == dst_node || dst_node->parent() == src_node); - if (src_node->parent() == dst_node) - _patch = dynamic_cast(dst_node); - else - _patch = dynamic_cast(src_node); - - // Connection from a patch input to a patch output (pass through) - } else if (src_node == dst_node && dynamic_cast(src_node)) { - _patch = dynamic_cast(src_node); - - // Normal connection between nodes with the same parent - } else { - _patch = src_node->parent_patch(); - } - - assert(_patch); - - //if (_dst_input_port->is_connected_to(_src_output_port)) { - if (!_patch->has_connection(_src_output_port, _dst_input_port)) { - _error = NOT_CONNECTED; - QueuedEvent::pre_process(); - return; - } - - if (src_node == NULL || dst_node == NULL) { - _error = PARENTS_NOT_FOUND; - QueuedEvent::pre_process(); - return; - } - - for (Raul::List::iterator i = dst_node->providers()->begin(); i != dst_node->providers()->end(); ++i) - if ((*i) == src_node) { - delete dst_node->providers()->erase(i); - break; - } - - for (Raul::List::iterator i = src_node->dependants()->begin(); i != src_node->dependants()->end(); ++i) - if ((*i) == dst_node) { - delete src_node->dependants()->erase(i); - break; - } - - _patch_connection = _patch->remove_connection(_src_port, _dst_port); - - if (_patch->enabled()) - _compiled_patch = _patch->compile(); - - QueuedEvent::pre_process(); -} - - -void -DisconnectionEvent::execute(ProcessContext& context) -{ - QueuedEvent::execute(context); - - if (_error == NO_ERROR) { - InputPort::Connections::Node* const port_connection - = _dst_input_port->remove_connection(_src_output_port); - - if (port_connection != NULL) { - assert(_patch_connection); - - if (port_connection->elem() != _patch_connection->elem()) { - cerr << "ERROR: Corrupt connections:" << endl; - cerr << "\t" << port_connection->elem() << ": " - << port_connection->elem()->src_port_path() - << " -> " << port_connection->elem()->dst_port_path() << endl - << "!=" << endl - << "\t" << _patch_connection->elem() << ": " - << _patch_connection->elem()->src_port_path() - << " -> " << _patch_connection->elem()->dst_port_path() << endl; - } - assert(port_connection->elem() == _patch_connection->elem()); - - // Destroy list node, which will drop reference to connection itself - _engine.maid()->push(port_connection); - _engine.maid()->push(_patch_connection); - - if (_patch->compiled_patch() != NULL) - _engine.maid()->push(_patch->compiled_patch()); - _patch->compiled_patch(_compiled_patch); - } else { - _error = CONNECTION_NOT_FOUND; - } - } -} - - -void -DisconnectionEvent::post_process() -{ - if (_error == NO_ERROR) { - _responder->respond_ok(); - _engine.broadcaster()->send_disconnection(_src_port->path(), _dst_port->path()); - } else { - // FIXME: better error messages - string msg = "Unable to disconnect "; - msg.append(_src_port_path + " -> " + _dst_port_path); - cerr << "DISCONNECTION ERROR " << (unsigned)_error << endl; - _responder->respond_error(msg); - } -} - - -} // namespace Ingen - diff --git a/src/libs/engine/events/DisconnectionEvent.hpp b/src/libs/engine/events/DisconnectionEvent.hpp deleted file mode 100644 index 700febeb..00000000 --- a/src/libs/engine/events/DisconnectionEvent.hpp +++ /dev/null @@ -1,90 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef DISCONNECTIONEVENT_H -#define DISCONNECTIONEVENT_H - -#include -#include -#include "QueuedEvent.hpp" -#include "types.hpp" -#include "PatchImpl.hpp" -using std::string; - -namespace Raul { - template class ListNode; - template class Array; -} - -namespace Ingen { - -class NodeImpl; -class ConnectionImpl; -class MidiMessage; -class PortImpl; -class InputPort; -class OutputPort; -class CompiledPatch; - - -/** Make a Connection between two Ports. - * - * \ingroup engine - */ -class DisconnectionEvent : public QueuedEvent -{ -public: - DisconnectionEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& src_port_path, const string& dst_port_path); - DisconnectionEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, PortImpl* const src_port, PortImpl* const dst_port); - - void pre_process(); - void execute(ProcessContext& context); - void post_process(); - -private: - - enum ErrorType { - NO_ERROR, - PARENT_PATCH_DIFFERENT, - PORT_NOT_FOUND, - TYPE_MISMATCH, - NOT_CONNECTED, - PARENTS_NOT_FOUND, - CONNECTION_NOT_FOUND - }; - - Raul::Path _src_port_path; - Raul::Path _dst_port_path; - - PatchImpl* _patch; - PortImpl* _src_port; - PortImpl* _dst_port; - OutputPort* _src_output_port; - InputPort* _dst_input_port; - - bool _lookup; - - PatchImpl::Connections::Node* _patch_connection; - CompiledPatch* _compiled_patch; ///< New process order for Patch - - ErrorType _error; -}; - - -} // namespace Ingen - -#endif // DISCONNECTIONEVENT_H diff --git a/src/libs/engine/events/EnablePatchEvent.cpp b/src/libs/engine/events/EnablePatchEvent.cpp deleted file mode 100644 index 04759cea..00000000 --- a/src/libs/engine/events/EnablePatchEvent.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "EnablePatchEvent.hpp" -#include "Responder.hpp" -#include "Engine.hpp" -#include "PatchImpl.hpp" -#include "util.hpp" -#include "ClientBroadcaster.hpp" -#include "EngineStore.hpp" - -namespace Ingen { - - -EnablePatchEvent::EnablePatchEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& patch_path, bool enable) -: QueuedEvent(engine, responder, timestamp), - _patch_path(patch_path), - _patch(NULL), - _compiled_patch(NULL), - _enable(enable) -{ -} - - -void -EnablePatchEvent::pre_process() -{ - _patch = _engine.engine_store()->find_patch(_patch_path); - - if (_enable && _patch) { - /* Any event that requires a new process order will set the patch's - * compiled_patch to NULL if it is executed when the patch is not - * active. So, if the CP is NULL, calculate it here */ - if (_patch->compiled_patch() == NULL) - _compiled_patch = _patch->compile(); - } - - QueuedEvent::pre_process(); -} - - -void -EnablePatchEvent::execute(ProcessContext& context) -{ - QueuedEvent::execute(context); - - if (_patch != NULL) { - if (_enable) - _patch->enable(); - else - _patch->disable(); - - if (_enable && _patch->compiled_patch() == NULL) - _patch->compiled_patch(_compiled_patch); - } -} - - -void -EnablePatchEvent::post_process() -{ - if (_patch != NULL) { - _responder->respond_ok(); - _engine.broadcaster()->send_property_change(_patch_path, "ingen:enabled", (bool)_enable); - } else { - _responder->respond_error(string("Patch ") + _patch_path + " not found"); - } -} - - -} // namespace Ingen - diff --git a/src/libs/engine/events/EnablePatchEvent.hpp b/src/libs/engine/events/EnablePatchEvent.hpp deleted file mode 100644 index dad1803a..00000000 --- a/src/libs/engine/events/EnablePatchEvent.hpp +++ /dev/null @@ -1,63 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef ENABLEPATCHEVENT_H -#define ENABLEPATCHEVENT_H - -#include -#include "QueuedEvent.hpp" - -using std::string; - -namespace Raul { template class Array; } - -namespace Ingen { - -class PatchImpl; -class NodeImpl; -class CompiledPatch; - - -/** Enables a patch's DSP processing. - * - * \ingroup engine - */ -class EnablePatchEvent : public QueuedEvent -{ -public: - EnablePatchEvent(Engine& engine, - SharedPtr responder, - SampleCount timestamp, - const string& patch_path, - bool enable); - - void pre_process(); - void execute(ProcessContext& context); - void post_process(); - -private: - string _patch_path; - PatchImpl* _patch; - CompiledPatch* _compiled_patch; // Patch's new process order - bool _enable; -}; - - -} // namespace Ingen - - -#endif // ENABLEPATCHEVENT_H diff --git a/src/libs/engine/events/LoadPluginsEvent.cpp b/src/libs/engine/events/LoadPluginsEvent.cpp deleted file mode 100644 index df5ff5d9..00000000 --- a/src/libs/engine/events/LoadPluginsEvent.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "LoadPluginsEvent.hpp" -#include "Responder.hpp" -#include "Engine.hpp" -#include "NodeFactory.hpp" -#include "ClientBroadcaster.hpp" -#include "QueuedEventSource.hpp" - -#include -using std::cerr; - -namespace Ingen { - - -LoadPluginsEvent::LoadPluginsEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, QueuedEventSource* source) -: QueuedEvent(engine, responder, timestamp, true, source) -{ - /* FIXME: Not sure why this has to be blocking, but it fixes some nasty bugs.. */ -} - -void -LoadPluginsEvent::pre_process() -{ - _engine.node_factory()->load_plugins(); - - QueuedEvent::pre_process(); -} - -void -LoadPluginsEvent::post_process() -{ - if (_source) - _source->unblock(); - - _responder->respond_ok(); -} - - -} // namespace Ingen - diff --git a/src/libs/engine/events/LoadPluginsEvent.hpp b/src/libs/engine/events/LoadPluginsEvent.hpp deleted file mode 100644 index cd9a2884..00000000 --- a/src/libs/engine/events/LoadPluginsEvent.hpp +++ /dev/null @@ -1,46 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef LOADPLUGINSEVENT_H -#define LOADPLUGINSEVENT_H - -#include -#include "QueuedEvent.hpp" - -namespace Ingen { - - -/** Loads all plugins into the internal plugin database (in NodeFactory). - * - * \ingroup engine - */ -class LoadPluginsEvent : public QueuedEvent -{ -public: - LoadPluginsEvent(Engine& engine, - SharedPtr responder, - SampleCount timestamp, - QueuedEventSource* source); - - void pre_process(); - void post_process(); -}; - - -} // namespace Ingen - -#endif // LOADPLUGINSEVENT_H diff --git a/src/libs/engine/events/Makefile.am b/src/libs/engine/events/Makefile.am deleted file mode 100644 index 9a8cfbd5..00000000 --- a/src/libs/engine/events/Makefile.am +++ /dev/null @@ -1,63 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -EXTRA_DIST = \ - AllNotesOffEvent.cpp \ - AllNotesOffEvent.hpp \ - ClearPatchEvent.cpp \ - ClearPatchEvent.hpp \ - ConnectionEvent.cpp \ - ConnectionEvent.hpp \ - CreateNodeEvent.cpp \ - CreateNodeEvent.hpp \ - CreatePatchEvent.cpp \ - CreatePatchEvent.hpp \ - CreatePortEvent.cpp \ - CreatePortEvent.hpp \ - DeactivateEvent.cpp \ - DeactivateEvent.hpp \ - DestroyEvent.cpp \ - DestroyEvent.hpp \ - DisconnectAllEvent.cpp \ - DisconnectAllEvent.hpp \ - DisconnectionEvent.cpp \ - DisconnectionEvent.hpp \ - EnablePatchEvent.cpp \ - EnablePatchEvent.hpp \ - LoadPluginsEvent.cpp \ - LoadPluginsEvent.hpp \ - MidiLearnEvent.cpp \ - MidiLearnEvent.hpp \ - NoteEvent.cpp \ - NoteEvent.hpp \ - PingQueuedEvent.hpp \ - RegisterClientEvent.cpp \ - RegisterClientEvent.hpp \ - RenameEvent.cpp \ - RenameEvent.hpp \ - RequestAllObjectsEvent.cpp \ - RequestAllObjectsEvent.hpp \ - RequestMetadataEvent.cpp \ - RequestMetadataEvent.hpp \ - RequestObjectEvent.cpp \ - RequestObjectEvent.hpp \ - RequestPluginEvent.cpp \ - RequestPluginEvent.hpp \ - RequestPluginsEvent.cpp \ - RequestPluginsEvent.hpp \ - RequestPortValueEvent.cpp \ - RequestPortValueEvent.hpp \ - SendPortActivityEvent.cpp \ - SendPortActivityEvent.hpp \ - SendPortValueEvent.cpp \ - SendPortValueEvent.hpp \ - SetMetadataEvent.cpp \ - SetMetadataEvent.hpp \ - SetPolyphonicEvent.cpp \ - SetPolyphonicEvent.hpp \ - SetPolyphonyEvent.cpp \ - SetPolyphonyEvent.hpp \ - SetPortValueEvent.cpp \ - SetPortValueEvent.hpp \ - UnregisterClientEvent.cpp \ - UnregisterClientEvent.hpp - diff --git a/src/libs/engine/events/MidiLearnEvent.cpp b/src/libs/engine/events/MidiLearnEvent.cpp deleted file mode 100644 index 2f37f30d..00000000 --- a/src/libs/engine/events/MidiLearnEvent.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "MidiLearnEvent.hpp" -#include "Responder.hpp" -#include "Engine.hpp" -#include "EngineStore.hpp" -#include "NodeImpl.hpp" -#include "MidiControlNode.hpp" -#include "ClientBroadcaster.hpp" -#include "PluginImpl.hpp" - -namespace Ingen { - - -// MidiLearnResponseEvent - -void -MidiLearnResponseEvent::post_process() -{ - _engine.broadcaster()->send_port_value(_port_path, _value); -} - - - -// MidiLearnEvent - -MidiLearnEvent::MidiLearnEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& node_path) -: QueuedEvent(engine, responder, timestamp), - _node_path(node_path), - _node(NULL), - _response_event(NULL) -{ -} - - -void -MidiLearnEvent::pre_process() -{ - _node = _engine.engine_store()->find_node(_node_path); - _response_event = new MidiLearnResponseEvent(_engine, _node_path + "/Controller_Number", _time); - - QueuedEvent::pre_process(); -} - - -void -MidiLearnEvent::execute(ProcessContext& context) -{ - QueuedEvent::execute(context); - - // FIXME: this isn't very good at all. - if (_node != NULL && _node->plugin_impl()->type() == Plugin::Internal - && _node->plugin_impl()->uri() == "ingen:control_node") { - ((MidiControlNode*)_node)->learn(_response_event); - } -} - - -void -MidiLearnEvent::post_process() -{ - if (_node != NULL) { - _responder->respond_ok(); - } else { - string msg = "Did not find node '"; - msg.append(_node_path).append("' for MIDI learn."); - _responder->respond_error(msg); - } -} - - -} // namespace Ingen - - diff --git a/src/libs/engine/events/MidiLearnEvent.hpp b/src/libs/engine/events/MidiLearnEvent.hpp deleted file mode 100644 index c0fc4a17..00000000 --- a/src/libs/engine/events/MidiLearnEvent.hpp +++ /dev/null @@ -1,85 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef MIDILEARNEVENT_H -#define MIDILEARNEVENT_H - -#include "QueuedEvent.hpp" -#include "MidiControlNode.hpp" -#include "types.hpp" -#include -using std::string; - -namespace Ingen { - -class NodeImpl; -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(Engine& engine, const string& port_path, SampleCount timestamp) - : Event(engine, SharedPtr(), timestamp), - _port_path(port_path), - _value(0.0f) - {} - - void set_value(Sample val) { _value = val; } - void post_process(); - -private: - string _port_path; - Sample _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(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& node_path); - - void pre_process(); - void execute(ProcessContext& context); - void post_process(); - -private: - const string _node_path; - NodeImpl* _node; - - /// Event to respond with when learned - MidiLearnResponseEvent* _response_event; -}; - - -} // namespace Ingen - -#endif // MIDILEARNEVENT_H diff --git a/src/libs/engine/events/NoteEvent.cpp b/src/libs/engine/events/NoteEvent.cpp deleted file mode 100644 index 58842ae6..00000000 --- a/src/libs/engine/events/NoteEvent.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "NoteEvent.hpp" -#include "Responder.hpp" -#include "Engine.hpp" -#include "EngineStore.hpp" -#include "NodeImpl.hpp" -#include "MidiNoteNode.hpp" -#include "MidiTriggerNode.hpp" -#include "PluginImpl.hpp" -#include "InternalPlugin.hpp" -#include "ProcessContext.hpp" - -namespace Ingen { - - -/** Note on with Patch explicitly passed. - * - * Used to be triggered by MIDI. Not used anymore. - */ -NoteEvent::NoteEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, NodeImpl* node, bool on, uchar note_num, uchar velocity) -: Event(engine, responder, timestamp), - _node(node), - _on(on), - _note_num(note_num), - _velocity(velocity) -{ -} - - -/** Note on with Node lookup. - * - * Triggered by OSC. - */ -NoteEvent::NoteEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& node_path, bool on, uchar note_num, uchar velocity) -: Event(engine, responder, timestamp), - _node(NULL), - _node_path(node_path), - _on(on), - _note_num(note_num), - _velocity(velocity) -{ -} - - -void -NoteEvent::execute(ProcessContext& context) -{ - Event::execute(context); - assert(_time >= context.start() && _time <= context.end()); - - // Lookup if neccessary - if (!_node) - _node = _engine.engine_store()->find_node(_node_path); - - // FIXME: barf - - if (_node != NULL && _node->plugin()->type() == Plugin::Internal) { - if (_on) { - if (_node->plugin_impl()->uri() == NS_INGEN "note_node") - ((MidiNoteNode*)_node)->note_on(context, _note_num, _velocity, _time); - else if (_node->plugin_impl()->uri() == NS_INGEN "trigger_node") - ((MidiTriggerNode*)_node)->note_on(context, _note_num, _velocity, _time); - } else { - if (_node->plugin_impl()->uri() == NS_INGEN "note_node") - ((MidiNoteNode*)_node)->note_off(context, _note_num, _time); - else if (_node->plugin_impl()->uri() == NS_INGEN "trigger_node") - ((MidiTriggerNode*)_node)->note_off(context, _note_num, _time); - } - } -} - - -void -NoteEvent::post_process() -{ - if (_responder) { - if (_node) - _responder->respond_ok(); - else - _responder->respond_error("Did not find node for note_on"); - } -} - - -} // namespace Ingen - diff --git a/src/libs/engine/events/NoteEvent.hpp b/src/libs/engine/events/NoteEvent.hpp deleted file mode 100644 index 31ae9d27..00000000 --- a/src/libs/engine/events/NoteEvent.hpp +++ /dev/null @@ -1,68 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef NOTEEVENT_H -#define NOTEEVENT_H - -#include "Event.hpp" -#include "types.hpp" -#include -using std::string; - -namespace Ingen { - -class NodeImpl; - - -/** A note on event. - * - * \ingroup engine - */ -class NoteEvent : public Event -{ -public: - NoteEvent(Engine& engine, - SharedPtr responder, - SampleCount timestamp, - NodeImpl* node, - bool on, - uchar note_num, - uchar velocity); - - NoteEvent(Engine& engine, - SharedPtr responder, - SampleCount timestamp, - const string& node_path, - bool on, - uchar note_num, - uchar velocity); - - void execute(ProcessContext& context); - void post_process(); - -private: - NodeImpl* _node; - const string _node_path; - bool _on; - uchar _note_num; - uchar _velocity; -}; - - -} // namespace Ingen - -#endif // NOTEEVENT_H diff --git a/src/libs/engine/events/PingQueuedEvent.hpp b/src/libs/engine/events/PingQueuedEvent.hpp deleted file mode 100644 index 08897bfe..00000000 --- a/src/libs/engine/events/PingQueuedEvent.hpp +++ /dev/null @@ -1,48 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef PINGQUEUEDEVENT_H -#define PINGQUEUEDEVENT_H - -#include "QueuedEvent.hpp" -#include "types.hpp" -#include "Responder.hpp" - -namespace Ingen { - -class PortImpl; - - -/** A ping that travels through the pre-processed event queue before responding - * (useful for the order guarantee). - * - * \ingroup engine - */ -class PingQueuedEvent : public QueuedEvent -{ -public: - PingQueuedEvent(Engine& engine, SharedPtr responder, SampleCount timestamp) - : QueuedEvent(engine, responder, timestamp) - {} - - void post_process() { _responder->respond_ok(); } -}; - - -} // namespace Ingen - -#endif // PINGQUEUEDEVENT_H diff --git a/src/libs/engine/events/RegisterClientEvent.cpp b/src/libs/engine/events/RegisterClientEvent.cpp deleted file mode 100644 index a8f68e21..00000000 --- a/src/libs/engine/events/RegisterClientEvent.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "Responder.hpp" -#include "RegisterClientEvent.hpp" -#include "Engine.hpp" -#include "ClientBroadcaster.hpp" - -namespace Ingen { - - -RegisterClientEvent::RegisterClientEvent(Engine& engine, - SharedPtr responder, - SampleCount timestamp, - const string& uri, - ClientInterface* client) - : QueuedEvent(engine, responder, timestamp) - , _uri(uri) - , _client(client) -{ -} - - -void -RegisterClientEvent::pre_process() -{ - _engine.broadcaster()->register_client(_uri, _client); - - QueuedEvent::pre_process(); -} - - -void -RegisterClientEvent::post_process() -{ - _responder->respond_ok(); -} - - -} // namespace Ingen - diff --git a/src/libs/engine/events/RegisterClientEvent.hpp b/src/libs/engine/events/RegisterClientEvent.hpp deleted file mode 100644 index 9e12b5ba..00000000 --- a/src/libs/engine/events/RegisterClientEvent.hpp +++ /dev/null @@ -1,55 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef REGISTERCLIENTEVENT_H -#define REGISTERCLIENTEVENT_H - -#include "QueuedEvent.hpp" -#include "interface/ClientInterface.hpp" -#include -using std::string; -using Ingen::Shared::ClientInterface; -using Ingen::Responder; - -namespace Ingen { - - -/** Registers a new client with the OSC system, so it can receive updates. - * - * \ingroup engine - */ -class RegisterClientEvent : public QueuedEvent -{ -public: - RegisterClientEvent(Engine& engine, - SharedPtr responder, - SampleCount timestamp, - const string& uri, - ClientInterface* client); - - void pre_process(); - void post_process(); - -private: - string _uri; - ClientInterface* _client; -}; - - -} // namespace Ingen - -#endif // REGISTERCLIENTEVENT_H diff --git a/src/libs/engine/events/RenameEvent.cpp b/src/libs/engine/events/RenameEvent.cpp deleted file mode 100644 index 164676aa..00000000 --- a/src/libs/engine/events/RenameEvent.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include "ClientBroadcaster.hpp" -#include "Engine.hpp" -#include "NodeImpl.hpp" -#include "EngineStore.hpp" -#include "PatchImpl.hpp" -#include "RenameEvent.hpp" -#include "Responder.hpp" -#include "AudioDriver.hpp" -#include "MidiDriver.hpp" - -using namespace std; - -namespace Ingen { - - -RenameEvent::RenameEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& path, const string& name) -: QueuedEvent(engine, responder, timestamp), - _old_path(path), - _name(name), - _new_path("/"), - _parent_patch(NULL), - _store_iterator(engine.engine_store()->end()), - _error(NO_ERROR) -{ - /* - if (_old_path.parent() == "/") - _new_path = string("/") + _name; - else - _new_path = _old_path.parent() +"/"+ _name;*/ -} - - -RenameEvent::~RenameEvent() -{ -} - - -void -RenameEvent::pre_process() -{ - if ((!Raul::Path::is_valid_name(_name)) || _name.find("/") != string::npos) { - _error = INVALID_NAME; - QueuedEvent::pre_process(); - return; - } - - _new_path = _old_path.parent().base() + _name; - - _store_iterator = _engine.engine_store()->find(_old_path); - if (_store_iterator == _engine.engine_store()->end()) { - _error = OBJECT_NOT_FOUND; - QueuedEvent::pre_process(); - return; - } - - if (_engine.engine_store()->find_object(_new_path)) { - _error = OBJECT_EXISTS; - QueuedEvent::pre_process(); - return; - } - - SharedPtr< Table > > removed - = _engine.engine_store()->remove(_store_iterator); - - assert(removed->size() > 0); - - for (Table >::iterator i = removed->begin(); i != removed->end(); ++i) { - const Path& child_old_path = i->first; - assert(Path::descendant_comparator(_old_path, child_old_path)); - - Path child_new_path; - if (child_old_path == _old_path) - child_new_path = _new_path; - else - child_new_path = _new_path.base() + child_old_path.substr(_old_path.length()+1); - - PtrCast(i->second)->set_path(child_new_path); - i->first = child_new_path; - } - - _engine.engine_store()->add(*removed.get()); - - QueuedEvent::pre_process(); -} - - -void -RenameEvent::execute(ProcessContext& context) -{ - QueuedEvent::execute(context); - - SharedPtr port = PtrCast(_store_iterator->second); - if (port && port->parent()->parent() == NULL) { - DriverPort* driver_port = NULL; - - if (port->type() == DataType::AUDIO) - driver_port = _engine.audio_driver()->driver_port(_new_path); - else if (port->type() == DataType::EVENT) - driver_port = _engine.midi_driver()->driver_port(_new_path); - - if (driver_port) { - cerr << "DRIVER PORT :)!" << endl; - driver_port->set_name(_new_path); - } else { - cerr << "NO DRIVER PORT :(" << endl; - } - } -} - - -void -RenameEvent::post_process() -{ - string msg = "Unable to rename object - "; - - if (_error == NO_ERROR) { - _responder->respond_ok(); - _engine.broadcaster()->send_rename(_old_path, _new_path); - } else { - if (_error == OBJECT_EXISTS) - msg.append("Object already exists at ").append(_new_path); - else if (_error == OBJECT_NOT_FOUND) - msg.append("Could not find object ").append(_old_path); - else if (_error == OBJECT_NOT_RENAMABLE) - msg.append(_old_path).append(" is not renamable"); - else if (_error == INVALID_NAME) - msg.append(_name).append(" is not a valid name"); - - _responder->respond_error(msg); - } -} - - -} // namespace Ingen diff --git a/src/libs/engine/events/RenameEvent.hpp b/src/libs/engine/events/RenameEvent.hpp deleted file mode 100644 index e230c589..00000000 --- a/src/libs/engine/events/RenameEvent.hpp +++ /dev/null @@ -1,64 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef RENAMEEVENT_H -#define RENAMEEVENT_H - -#include -#include -#include "QueuedEvent.hpp" -#include "EngineStore.hpp" - -using std::string; - -template class TreeNode; -template class ListNode; - -namespace Ingen { - -class PatchImpl; - - -/** An event to change the name of an GraphObjectImpl. - * - * \ingroup engine - */ -class RenameEvent : public QueuedEvent -{ -public: - RenameEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& path, const string& name); - ~RenameEvent(); - - void pre_process(); - void execute(ProcessContext& context); - void post_process(); - -private: - enum ErrorType { NO_ERROR, OBJECT_NOT_FOUND, OBJECT_EXISTS, OBJECT_NOT_RENAMABLE, INVALID_NAME }; - - Path _old_path; - string _name; - Path _new_path; - PatchImpl* _parent_patch; - EngineStore::iterator _store_iterator; - ErrorType _error; -}; - - -} // namespace Ingen - -#endif // RENAMEEVENT_H diff --git a/src/libs/engine/events/RequestAllObjectsEvent.cpp b/src/libs/engine/events/RequestAllObjectsEvent.cpp deleted file mode 100644 index d57080aa..00000000 --- a/src/libs/engine/events/RequestAllObjectsEvent.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "RequestAllObjectsEvent.hpp" -#include "Responder.hpp" -#include "Engine.hpp" -#include "ObjectSender.hpp" -#include "ClientBroadcaster.hpp" -#include "EngineStore.hpp" - -namespace Ingen { - - -RequestAllObjectsEvent::RequestAllObjectsEvent(Engine& engine, SharedPtr responder, SampleCount timestamp) -: QueuedEvent(engine, responder, timestamp) -{ -} - - -void -RequestAllObjectsEvent::pre_process() -{ - QueuedEvent::pre_process(); -} - - -void -RequestAllObjectsEvent::post_process() -{ - if (_responder->client()) { - _responder->respond_ok(); - - // Everything is a child of the root patch, so this sends it all - PatchImpl* root = _engine.engine_store()->find_patch("/"); - if (root && _responder->client()) - ObjectSender::send_patch(_responder->client(), root, true); - - } else { - _responder->respond_error("Unable to find client to send all objects"); - } -} - - -} // namespace Ingen - diff --git a/src/libs/engine/events/RequestAllObjectsEvent.hpp b/src/libs/engine/events/RequestAllObjectsEvent.hpp deleted file mode 100644 index 0537575a..00000000 --- a/src/libs/engine/events/RequestAllObjectsEvent.hpp +++ /dev/null @@ -1,48 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef REQUESTALLOBJECTSEVENT_H -#define REQUESTALLOBJECTSEVENT_H - -#include -#include "QueuedEvent.hpp" -using std::string; - -namespace Ingen { - -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(Engine& engine, SharedPtr responder, SampleCount timestamp); - - void pre_process(); - void post_process(); -}; - - -} // namespace Ingen - -#endif // REQUESTALLOBJECTSEVENT_H diff --git a/src/libs/engine/events/RequestMetadataEvent.cpp b/src/libs/engine/events/RequestMetadataEvent.cpp deleted file mode 100644 index 733a6a82..00000000 --- a/src/libs/engine/events/RequestMetadataEvent.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "RequestMetadataEvent.hpp" -#include -#include "Responder.hpp" -#include "Engine.hpp" -#include "GraphObjectImpl.hpp" -#include "EngineStore.hpp" -#include "interface/ClientInterface.hpp" -#include "ClientBroadcaster.hpp" -using std::string; - -namespace Ingen { - - -RequestMetadataEvent::RequestMetadataEvent(Engine& engine, - SharedPtr responder, - SampleCount timestamp, - bool property, - const string& node_path, - const string& key) - : QueuedEvent(engine, responder, timestamp) - , _path(node_path) - , _property(property) - , _key(key) - , _object(NULL) -{ -} - - -void -RequestMetadataEvent::pre_process() -{ - if (_responder->client()) { - _object = _engine.engine_store()->find_object(_path); - if (_object == NULL) { - QueuedEvent::pre_process(); - return; - } - } - - if (_property) - _value = _object->get_property(_key); - else - _value = _object->get_variable(_key); - - QueuedEvent::pre_process(); -} - - -void -RequestMetadataEvent::post_process() -{ - if (_responder->client()) { - if (!_object) { - string msg = "Unable to find variable subject "; - msg += _path; - _responder->respond_error(msg); - } else { - _responder->respond_ok(); - _responder->client()->set_variable(_path, _key, _value); - } - } else { - _responder->respond_error("Unknown client"); - } -} - - -} // namespace Ingen - diff --git a/src/libs/engine/events/RequestMetadataEvent.hpp b/src/libs/engine/events/RequestMetadataEvent.hpp deleted file mode 100644 index f6a18dfc..00000000 --- a/src/libs/engine/events/RequestMetadataEvent.hpp +++ /dev/null @@ -1,62 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef REQUESTMETADATAEVENT_H -#define REQUESTMETADATAEVENT_H - -#include -#include "QueuedEvent.hpp" -#include -using std::string; - -namespace Ingen { - -class GraphObjectImpl; -namespace Shared { - class ClientInterface; -} using Shared::ClientInterface; - - -/** A request from a client for a piece of variable. - * - * \ingroup engine - */ -class RequestMetadataEvent : public QueuedEvent -{ -public: - RequestMetadataEvent(Engine& engine, - SharedPtr responder, - SampleCount timestamp, - bool property, - const string& path, - const string& key); - - void pre_process(); - void post_process(); - -private: - string _path; - bool _property; - string _key; - Raul::Atom _value; - GraphObjectImpl* _object; -}; - - -} // namespace Ingen - -#endif // REQUESTMETADATAEVENT_H diff --git a/src/libs/engine/events/RequestObjectEvent.cpp b/src/libs/engine/events/RequestObjectEvent.cpp deleted file mode 100644 index 88479482..00000000 --- a/src/libs/engine/events/RequestObjectEvent.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "RequestObjectEvent.hpp" -#include -#include "interface/ClientInterface.hpp" -#include "Responder.hpp" -#include "Engine.hpp" -#include "EngineStore.hpp" -#include "ClientBroadcaster.hpp" -#include "PatchImpl.hpp" -#include "NodeImpl.hpp" -#include "PortImpl.hpp" -#include "ObjectSender.hpp" -#include "ProcessContext.hpp" - -using std::string; - -namespace Ingen { - - -RequestObjectEvent::RequestObjectEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& path) -: QueuedEvent(engine, responder, timestamp), - _path(path), - _object(NULL) -{ -} - - -void -RequestObjectEvent::pre_process() -{ - _object = _engine.engine_store()->find_object(_path); - - QueuedEvent::pre_process(); -} - - -void -RequestObjectEvent::execute(ProcessContext& context) -{ - QueuedEvent::execute(context); - assert(_time >= context.start() && _time <= context.end()); -} - - -void -RequestObjectEvent::post_process() -{ - if (!_object) { - _responder->respond_error("Unable to find object requested."); - - } else if (_responder->client()) { - PatchImpl* const patch = dynamic_cast(_object); - if (patch) { - _responder->respond_ok(); - ObjectSender::send_patch(_responder->client(), patch, true); - return; - } - - NodeImpl* const node = dynamic_cast(_object); - if (node) { - _responder->respond_ok(); - ObjectSender::send_node(_responder->client(), node, true); - return; - } - - PortImpl* const port = dynamic_cast(_object); - if (port) { - _responder->respond_ok(); - ObjectSender::send_port(_responder->client(), port); - return; - } - - _responder->respond_error("Object of unknown type requested."); - - } else { - _responder->respond_error("Unable to find client to send object."); - } -} - - -} // namespace Ingen - diff --git a/src/libs/engine/events/RequestObjectEvent.hpp b/src/libs/engine/events/RequestObjectEvent.hpp deleted file mode 100644 index 52459ada..00000000 --- a/src/libs/engine/events/RequestObjectEvent.hpp +++ /dev/null @@ -1,55 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef REQUESTOBJECTEVENT_H -#define REQUESTOBJECTEVENT_H - -#include -#include "QueuedEvent.hpp" -#include "types.hpp" - -using std::string; - -namespace Ingen { - -class GraphObjectImpl; -namespace Shared { class ClientInterface; } -using Shared::ClientInterface; - - -/** A request from a client to send the value of a port. - * - * \ingroup engine - */ -class RequestObjectEvent : public QueuedEvent -{ -public: - RequestObjectEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& port_path); - - void pre_process(); - void execute(ProcessContext& context); - void post_process(); - -private: - const string _path; - GraphObjectImpl* _object; -}; - - -} // namespace Ingen - -#endif // REQUESTOBJECTEVENT_H diff --git a/src/libs/engine/events/RequestPluginEvent.cpp b/src/libs/engine/events/RequestPluginEvent.cpp deleted file mode 100644 index 36358df7..00000000 --- a/src/libs/engine/events/RequestPluginEvent.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include "interface/ClientInterface.hpp" -#include "RequestPluginEvent.hpp" -#include "Responder.hpp" -#include "Engine.hpp" -#include "PortImpl.hpp" -#include "EngineStore.hpp" -#include "ClientBroadcaster.hpp" -#include "NodeFactory.hpp" -#include "PluginImpl.hpp" -#include "ProcessContext.hpp" - -using std::string; - -namespace Ingen { - - -RequestPluginEvent::RequestPluginEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& uri) -: QueuedEvent(engine, responder, timestamp), - _uri(uri), - _plugin(NULL) -{ -} - - -void -RequestPluginEvent::pre_process() -{ - _plugin = _engine.node_factory()->plugin(_uri); - - QueuedEvent::pre_process(); -} - - -void -RequestPluginEvent::execute(ProcessContext& context) -{ - QueuedEvent::execute(context); - assert(_time >= context.start() && _time <= context.end()); -} - - -void -RequestPluginEvent::post_process() -{ - if (!_plugin) { - _responder->respond_error("Unable to find plugin requested."); - - } else if (_responder->client()) { - - _responder->respond_ok(); - assert(_plugin->uri() == _uri); - _responder->client()->new_plugin(_uri, _plugin->type_uri(), _plugin->symbol(), _plugin->name()); - - } else { - _responder->respond_error("Unable to find client to send plugin."); - } -} - - -} // namespace Ingen - diff --git a/src/libs/engine/events/RequestPluginEvent.hpp b/src/libs/engine/events/RequestPluginEvent.hpp deleted file mode 100644 index 8f936098..00000000 --- a/src/libs/engine/events/RequestPluginEvent.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef REQUESTPLUGINEVENT_H -#define REQUESTPLUGINEVENT_H - -#include -#include "QueuedEvent.hpp" -#include "types.hpp" - -using std::string; - -namespace Ingen { - -class PluginImpl; - - -/** A request from a client to send information about a plugin. - * - * \ingroup engine - */ -class RequestPluginEvent : public QueuedEvent -{ -public: - RequestPluginEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& uri); - - void pre_process(); - void execute(ProcessContext& context); - void post_process(); - -private: - const string _uri; - const PluginImpl* _plugin; -}; - - -} // namespace Ingen - -#endif // REQUESTPLUGINEVENT_H diff --git a/src/libs/engine/events/RequestPluginsEvent.cpp b/src/libs/engine/events/RequestPluginsEvent.cpp deleted file mode 100644 index 8d7fc1ba..00000000 --- a/src/libs/engine/events/RequestPluginsEvent.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "RequestPluginsEvent.hpp" -#include "Responder.hpp" -#include "Engine.hpp" -#include "ClientBroadcaster.hpp" -#include "NodeFactory.hpp" - -namespace Ingen { - - -RequestPluginsEvent::RequestPluginsEvent(Engine& engine, SharedPtr responder, SampleCount timestamp) -: QueuedEvent(engine, responder, timestamp) -{ -} - - -void -RequestPluginsEvent::pre_process() -{ - // Take a copy to send in the post processing thread (to avoid problems - // because std::map isn't thread safe) - _plugins = _engine.node_factory()->plugins(); - - QueuedEvent::pre_process(); -} - - -void -RequestPluginsEvent::post_process() -{ - if (_responder->client()) { - _engine.broadcaster()->send_plugins_to(_responder->client(), _plugins); - _responder->respond_ok(); - } else { - _responder->respond_error("Unable to find client to send plugins"); - } -} - - -} // namespace Ingen - diff --git a/src/libs/engine/events/RequestPluginsEvent.hpp b/src/libs/engine/events/RequestPluginsEvent.hpp deleted file mode 100644 index f6b41d7a..00000000 --- a/src/libs/engine/events/RequestPluginsEvent.hpp +++ /dev/null @@ -1,47 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef REQUESTPLUGINSEVENT_H -#define REQUESTPLUGINSEVENT_H - -#include "QueuedEvent.hpp" -#include "NodeFactory.hpp" - -namespace Ingen { - -class Responder; - -/** A request from a client to send notification of all objects (ie refresh). - * - * \ingroup engine - */ -class RequestPluginsEvent : public QueuedEvent -{ -public: - RequestPluginsEvent(Engine& engine, SharedPtr responder, SampleCount timestamp); - - void pre_process(); - void post_process(); - -private: - NodeFactory::Plugins _plugins; -}; - - -} // namespace Ingen - -#endif // REQUESTPLUGINSEVENT_H diff --git a/src/libs/engine/events/RequestPortValueEvent.cpp b/src/libs/engine/events/RequestPortValueEvent.cpp deleted file mode 100644 index 025d3700..00000000 --- a/src/libs/engine/events/RequestPortValueEvent.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "RequestPortValueEvent.hpp" -#include -#include "interface/ClientInterface.hpp" -#include "Responder.hpp" -#include "Engine.hpp" -#include "PortImpl.hpp" -#include "EngineStore.hpp" -#include "ClientBroadcaster.hpp" -#include "AudioBuffer.hpp" -#include "ProcessContext.hpp" - -using std::string; - -namespace Ingen { - - -RequestPortValueEvent::RequestPortValueEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& port_path) -: QueuedEvent(engine, responder, timestamp), - _port_path(port_path), - _port(NULL), - _value(0.0) -{ -} - - -void -RequestPortValueEvent::pre_process() -{ - _port = _engine.engine_store()->find_port(_port_path); - - QueuedEvent::pre_process(); -} - - -void -RequestPortValueEvent::execute(ProcessContext& context) -{ - QueuedEvent::execute(context); - assert(_time >= context.start() && _time <= context.end()); - - if (_port != NULL && (_port->type() == DataType::CONTROL || _port->type() == DataType::AUDIO)) - _value = ((AudioBuffer*)_port->buffer(0))->value_at(0/*_time - start*/); - else - _port = NULL; // triggers error response -} - - -void -RequestPortValueEvent::post_process() -{ - string msg; - if (!_port) { - _responder->respond_error("Unable to find port for get_value responder."); - } else if (_responder->client()) { - _responder->respond_ok(); - _responder->client()->set_port_value(_port_path, _value); - } else { - _responder->respond_error("Unable to find client to send port value"); - } -} - - -} // namespace Ingen - diff --git a/src/libs/engine/events/RequestPortValueEvent.hpp b/src/libs/engine/events/RequestPortValueEvent.hpp deleted file mode 100644 index dd52c535..00000000 --- a/src/libs/engine/events/RequestPortValueEvent.hpp +++ /dev/null @@ -1,56 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef REQUESTPORTVALUEEVENT_H -#define REQUESTPORTVALUEEVENT_H - -#include -#include "QueuedEvent.hpp" -#include "types.hpp" - -using std::string; - -namespace Ingen { - -class PortImpl; -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(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& port_path); - - void pre_process(); - void execute(ProcessContext& context); - void post_process(); - -private: - const string _port_path; - PortImpl* _port; - Sample _value; -}; - - -} // namespace Ingen - -#endif // REQUESTPORTVALUEEVENT_H diff --git a/src/libs/engine/events/SendPortActivityEvent.cpp b/src/libs/engine/events/SendPortActivityEvent.cpp deleted file mode 100644 index 0ab3abdd..00000000 --- a/src/libs/engine/events/SendPortActivityEvent.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "SendPortActivityEvent.hpp" -#include "Engine.hpp" -#include "PortImpl.hpp" -#include "ClientBroadcaster.hpp" - -namespace Ingen { - - -void -SendPortActivityEvent::post_process() -{ - _engine.broadcaster()->send_port_activity(_port->path()); -} - - -} // namespace Ingen - diff --git a/src/libs/engine/events/SendPortActivityEvent.hpp b/src/libs/engine/events/SendPortActivityEvent.hpp deleted file mode 100644 index dfbb8a10..00000000 --- a/src/libs/engine/events/SendPortActivityEvent.hpp +++ /dev/null @@ -1,67 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef SENDPORTACTIVITYEVENT_H -#define SENDPORTACTIVITYEVENT_H - -#include -#include "Event.hpp" -#include "types.hpp" -using std::string; - -namespace Ingen { - -class PortImpl; - - -/** A special event used internally to send port activity notification (e.g. - * MIDI event activity) from the audio thread. - * - * This is created in the audio thread (directly in a ringbuffer, not new'd) - * for processing in the post processing thread (unlike normal events which - * are created in the pre-processor thread then run through the audio - * thread). This event's job is done entirely in post_process. - * - * This only really makes sense for message ports. - * - * \ingroup engine - */ -class SendPortActivityEvent : public Event -{ -public: - inline SendPortActivityEvent(Engine& engine, - SampleCount timestamp, - PortImpl* port) - : Event(engine, SharedPtr(), timestamp) - , _port(port) - { - } - - inline void operator=(const SendPortActivityEvent& ev) { - _port = ev._port; - } - - void post_process(); - -private: - PortImpl* _port; -}; - - -} // namespace Ingen - -#endif // SENDPORTACTIVITYEVENT_H diff --git a/src/libs/engine/events/SendPortValueEvent.cpp b/src/libs/engine/events/SendPortValueEvent.cpp deleted file mode 100644 index d3fb0d36..00000000 --- a/src/libs/engine/events/SendPortValueEvent.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include "SendPortValueEvent.hpp" -#include "Engine.hpp" -#include "PortImpl.hpp" -#include "ClientBroadcaster.hpp" - -using namespace std; - -namespace Ingen { - - -void -SendPortValueEvent::post_process() -{ - // FIXME... - - if (_omni) { - _engine.broadcaster()->send_port_value(_port->path(), _value); - } else { - _engine.broadcaster()->send_port_value(_port->path(), _value); - } -} - - -} // namespace Ingen - diff --git a/src/libs/engine/events/SendPortValueEvent.hpp b/src/libs/engine/events/SendPortValueEvent.hpp deleted file mode 100644 index e8505914..00000000 --- a/src/libs/engine/events/SendPortValueEvent.hpp +++ /dev/null @@ -1,78 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef SENDPORTVALUEEVENT_H -#define SENDPORTVALUEEVENT_H - -#include -#include "Event.hpp" -#include "types.hpp" -using std::string; - -namespace Ingen { - -class PortImpl; - - -/** A special event used internally to send port values from the audio thread. - * - * This is created in the audio thread (using in-place new on a preallocated - * buffer) for processing in the post processing thread (unlike normal events - * which are created in the pre-processor thread then run through the audio - * thread). This event's job is done entirely in post_process. - * - * This only works for control ports right now. - * - * \ingroup engine - */ -class SendPortValueEvent : public Event -{ -public: - inline SendPortValueEvent(Engine& engine, - SampleCount timestamp, - PortImpl* port, - bool omni, - uint32_t voice_num, - Sample value) - : Event(engine, SharedPtr(), timestamp) - , _port(port) - , _omni(omni) - , _voice_num(voice_num) - , _value(value) - { - } - - inline void operator=(const SendPortValueEvent& ev) { - _port = ev._port; - _omni = ev._omni; - _voice_num = ev._voice_num; - _value = ev._value; - } - - void post_process(); - -private: - PortImpl* _port; - bool _omni; - uint32_t _voice_num; - Sample _value; -}; - - -} // namespace Ingen - -#endif // SENDPORTVALUEEVENT_H diff --git a/src/libs/engine/events/SetMetadataEvent.cpp b/src/libs/engine/events/SetMetadataEvent.cpp deleted file mode 100644 index db4bbc30..00000000 --- a/src/libs/engine/events/SetMetadataEvent.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "SetMetadataEvent.hpp" -#include -#include -#include "Responder.hpp" -#include "Engine.hpp" -#include "PortImpl.hpp" -#include "ClientBroadcaster.hpp" -#include "GraphObjectImpl.hpp" -#include "EngineStore.hpp" - -using std::string; - -namespace Ingen { - - -SetMetadataEvent::SetMetadataEvent( - Engine& engine, - SharedPtr responder, - SampleCount timestamp, - bool property, - const string& path, - const string& key, - const Atom& value) - : QueuedEvent(engine, responder, timestamp) - , _error(NO_ERROR) - , _special_type(NONE) - , _property(property) - , _path(path) - , _key(key) - , _value(value) - , _object(NULL) -{ -} - - -void -SetMetadataEvent::pre_process() -{ - if (!Path::is_valid(_path)) { - _error = INVALID_PATH; - QueuedEvent::pre_process(); - return; - } - - _object = _engine.engine_store()->find_object(_path); - if (_object == NULL) { - QueuedEvent::pre_process(); - return; - } - - if (_property) - _object->set_property(_key, _value); - else - _object->set_variable(_key, _value); - - if (_key == "ingen:broadcast") { - std::cout << "BROADCAST" << std::endl; - _special_type = ENABLE_BROADCAST; - } - - QueuedEvent::pre_process(); -} - - -void -SetMetadataEvent::execute(ProcessContext& context) -{ - if (_special_type == ENABLE_BROADCAST) { - PortImpl* port = dynamic_cast(_object); - if (port) - port->broadcast(_value.get_bool()); - } - - QueuedEvent::execute(context); - // Do nothing -} - - -void -SetMetadataEvent::post_process() -{ - if (_error == INVALID_PATH) { - _responder->respond_error((boost::format("Invalid path %1% setting %2%") % _path % _key).str()); - } else if (_object == NULL) { - string msg = (boost::format("Unable to find object %1% to set %2%") % _path % _key).str(); - _responder->respond_error(msg); - } else { - _responder->respond_ok(); - if (_property) - _engine.broadcaster()->send_property_change(_path, _key, _value); - else - _engine.broadcaster()->send_variable_change(_path, _key, _value); - } -} - - -} // namespace Ingen diff --git a/src/libs/engine/events/SetMetadataEvent.hpp b/src/libs/engine/events/SetMetadataEvent.hpp deleted file mode 100644 index 6b9be81c..00000000 --- a/src/libs/engine/events/SetMetadataEvent.hpp +++ /dev/null @@ -1,65 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef SETMETADATAEVENT_H -#define SETMETADATAEVENT_H - -#include -#include "QueuedEvent.hpp" -#include - -using std::string; - -namespace Ingen { - -class GraphObjectImpl; - - -/** An event to set a piece of variable for an GraphObjectImpl. - * - * \ingroup engine - */ -class SetMetadataEvent : public QueuedEvent -{ -public: - SetMetadataEvent(Engine& engine, - SharedPtr responder, - SampleCount timestamp, - bool property, - const string& path, - const string& key, - const Raul::Atom& value); - - void pre_process(); - void execute(ProcessContext& context); - void post_process(); - -private: - enum { NO_ERROR, INVALID_PATH } _error; - enum { NONE, ENABLE_BROADCAST } _special_type; - - bool _property; - string _path; - string _key; - Raul::Atom _value; - GraphObjectImpl* _object; -}; - - -} // namespace Ingen - -#endif // SETMETADATAEVENT_H diff --git a/src/libs/engine/events/SetPolyphonicEvent.cpp b/src/libs/engine/events/SetPolyphonicEvent.cpp deleted file mode 100644 index d1fc6a7c..00000000 --- a/src/libs/engine/events/SetPolyphonicEvent.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include "SetPolyphonicEvent.hpp" -#include "Responder.hpp" -#include "Engine.hpp" -#include "PatchImpl.hpp" -#include "ClientBroadcaster.hpp" -#include "util.hpp" -#include "EngineStore.hpp" -#include "PortImpl.hpp" -#include "NodeImpl.hpp" -#include "ConnectionImpl.hpp" -#include "QueuedEventSource.hpp" - -namespace Ingen { - - -SetPolyphonicEvent::SetPolyphonicEvent(Engine& engine, SharedPtr responder, FrameTime time, QueuedEventSource* source, const string& path, bool poly) -: QueuedEvent(engine, responder, time, true, source), - _path(path), - _object(NULL), - _poly(poly), - _success(false) -{ -} - - -void -SetPolyphonicEvent::pre_process() -{ - _object = _engine.engine_store()->find_object(_path); - - QueuedEvent::pre_process(); -} - - -void -SetPolyphonicEvent::execute(ProcessContext& context) -{ - QueuedEvent::execute(context); - - if (_object) - _success = _object->set_polyphonic(*_engine.maid(), _poly); - - _source->unblock(); -} - - -void -SetPolyphonicEvent::post_process() -{ - if (_object) { - if (_success) { - _responder->respond_ok(); - _engine.broadcaster()->send_property_change(_path, "ingen:polyphonic", _poly); - } else { - _responder->respond_error("Unable to set object as polyphonic"); - } - } else { - _responder->respond_error("Unable to find object to set polyphonic"); - } -} - - -} // namespace Ingen - diff --git a/src/libs/engine/events/SetPolyphonicEvent.hpp b/src/libs/engine/events/SetPolyphonicEvent.hpp deleted file mode 100644 index 9079d49f..00000000 --- a/src/libs/engine/events/SetPolyphonicEvent.hpp +++ /dev/null @@ -1,56 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef SETPOLYPHONICEVENT_H -#define SETPOLYPHONICEVENT_H - -#include -#include -#include "QueuedEvent.hpp" - -using std::string; - -namespace Ingen { - -class GraphObjectImpl; - - -/** Delete all nodes from a patch. - * - * \ingroup engine - */ -class SetPolyphonicEvent : public QueuedEvent -{ -public: - SetPolyphonicEvent(Engine& engine, SharedPtr responder, FrameTime time, QueuedEventSource* source, const string& path, bool poly); - - void pre_process(); - void execute(ProcessContext& context); - void post_process(); - -private: - const string _path; - GraphObjectImpl* _object; - bool _poly; - bool _success; -}; - - -} // namespace Ingen - - -#endif // SETPOLYPHONICEVENT_H diff --git a/src/libs/engine/events/SetPolyphonyEvent.cpp b/src/libs/engine/events/SetPolyphonyEvent.cpp deleted file mode 100644 index eb6550cb..00000000 --- a/src/libs/engine/events/SetPolyphonyEvent.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include "SetPolyphonyEvent.hpp" -#include "Responder.hpp" -#include "Engine.hpp" -#include "PatchImpl.hpp" -#include "ClientBroadcaster.hpp" -#include "util.hpp" -#include "EngineStore.hpp" -#include "PortImpl.hpp" -#include "NodeImpl.hpp" -#include "ConnectionImpl.hpp" -#include "QueuedEventSource.hpp" - -namespace Ingen { - - -SetPolyphonyEvent::SetPolyphonyEvent(Engine& engine, SharedPtr responder, FrameTime time, QueuedEventSource* source, const string& patch_path, uint32_t poly) -: QueuedEvent(engine, responder, time, true, source), - _patch_path(patch_path), - _patch(NULL), - _poly(poly) -{ -} - - -void -SetPolyphonyEvent::pre_process() -{ - _patch = _engine.engine_store()->find_patch(_patch_path); - if (_patch && _poly > _patch->internal_polyphony()) - _patch->prepare_internal_poly(_poly); - - QueuedEvent::pre_process(); -} - - -void -SetPolyphonyEvent::execute(ProcessContext& context) -{ - QueuedEvent::execute(context); - - if (_patch) - _patch->apply_internal_poly(*_engine.maid(), _poly); - - _source->unblock(); -} - - -void -SetPolyphonyEvent::post_process() -{ - if (_patch) { - _responder->respond_ok(); - _engine.broadcaster()->send_property_change(_patch_path, "ingen:polyphony", (int32_t)_poly); - } else { - _responder->respond_error("Unable to find patch"); - } -} - - -} // namespace Ingen - diff --git a/src/libs/engine/events/SetPolyphonyEvent.hpp b/src/libs/engine/events/SetPolyphonyEvent.hpp deleted file mode 100644 index 8aba997a..00000000 --- a/src/libs/engine/events/SetPolyphonyEvent.hpp +++ /dev/null @@ -1,56 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef SETPOLYPHONYEVENT_H -#define SETPOLYPHONYEVENT_H - -#include -#include -#include "QueuedEvent.hpp" - -using std::string; - -namespace Ingen { - -class PatchImpl; - - -/** Delete all nodes from a patch. - * - * \ingroup engine - */ -class SetPolyphonyEvent : public QueuedEvent -{ -public: - SetPolyphonyEvent(Engine& engine, SharedPtr responder, FrameTime time, QueuedEventSource* source, const string& patch_path, uint32_t poly); - - void pre_process(); - void execute(ProcessContext& context); - void post_process(); - -private: - const string _patch_path; - PatchImpl* _patch; - const uint32_t _poly; - -}; - - -} // namespace Ingen - - -#endif // SETPOLYPHONYEVENT_H diff --git a/src/libs/engine/events/SetPortValueEvent.cpp b/src/libs/engine/events/SetPortValueEvent.cpp deleted file mode 100644 index a99b9f9a..00000000 --- a/src/libs/engine/events/SetPortValueEvent.cpp +++ /dev/null @@ -1,224 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include "Responder.hpp" -#include "SetPortValueEvent.hpp" -#include "Engine.hpp" -#include "PortImpl.hpp" -#include "ClientBroadcaster.hpp" -#include "NodeImpl.hpp" -#include "EngineStore.hpp" -#include "AudioBuffer.hpp" -#include "EventBuffer.hpp" -#include "ProcessContext.hpp" -#include "MessageContext.hpp" - -using namespace std; - -namespace Ingen { - - -/** Omni (all voices) control setting */ -SetPortValueEvent::SetPortValueEvent(Engine& engine, - SharedPtr responder, - bool queued, - SampleCount timestamp, - const string& port_path, - const Raul::Atom& value) - : QueuedEvent(engine, responder, timestamp) - , _queued(queued) - , _omni(true) - , _voice_num(0) - , _port_path(port_path) - , _value(value) - , _port(NULL) - , _error(NO_ERROR) -{ -} - - -/** Voice-specific control setting */ -SetPortValueEvent::SetPortValueEvent(Engine& engine, - SharedPtr responder, - bool queued, - SampleCount timestamp, - uint32_t voice_num, - const string& port_path, - const Raul::Atom& value) - : QueuedEvent(engine, responder, timestamp) - , _queued(queued) - , _omni(false) - , _voice_num(voice_num) - , _port_path(port_path) - , _value(value) - , _port(NULL) - , _error(NO_ERROR) -{ -} - - -SetPortValueEvent::~SetPortValueEvent() -{ -} - - -void -SetPortValueEvent::pre_process() -{ - if (_queued) { - if (_port == NULL) { - if (Path::is_valid(_port_path)) - _port = _engine.engine_store()->find_port(_port_path); - else - _error = ILLEGAL_PATH; - } - - if (_port == NULL && _error == NO_ERROR) - _error = PORT_NOT_FOUND; - } - - // Port is a message context port, set its value and - // call the plugin's message run function once - if (_port && _port->context() == Context::MESSAGE) { - _engine.message_context()->run(_port->parent_node()); - } - - QueuedEvent::pre_process(); -} - - -void -SetPortValueEvent::execute(ProcessContext& context) -{ - Event::execute(context); - assert(_time >= context.start() && _time <= context.end()); - - if (_port && _port->context() == Context::MESSAGE) - return; - - if (_error == NO_ERROR && _port == NULL) { - if (Path::is_valid(_port_path)) - _port = _engine.engine_store()->find_port(_port_path); - else - _error = ILLEGAL_PATH; - } - - if (_port == NULL) { - if (_error == NO_ERROR) - _error = PORT_NOT_FOUND; - /*} else if (_port->buffer(0)->capacity() < _data_size) { - _error = NO_SPACE;*/ - } else { - Buffer* const buf = _port->buffer(0); - AudioBuffer* const abuf = dynamic_cast(buf); - if (abuf) { - if (_value.type() != Atom::FLOAT) { - _error = TYPE_MISMATCH; - return; - } - - if (_omni) { - for (uint32_t i=0; i < _port->poly(); ++i) - ((AudioBuffer*)_port->buffer(i))->set_value( - _value.get_float(), context.start(), _time); - } else { - if (_voice_num < _port->poly()) - ((AudioBuffer*)_port->buffer(_voice_num))->set_value( - _value.get_float(), context.start(), _time); - else - _error = ILLEGAL_VOICE; - } - return; - } - - EventBuffer* const ebuf = dynamic_cast(buf); - - const LV2Features::Feature* f = _engine.world()->lv2_features->feature(LV2_URI_MAP_URI); - LV2URIMap* map = (LV2URIMap*)f->controller; - - // FIXME: eliminate lookups - // FIXME: need a proper prefix system - if (ebuf && _value.type() == Atom::BLOB) { - const uint32_t frames = std::max( - (uint32_t)(_time - context.start()), - ebuf->latest_frames()); - - // Size 0 event, pass it along to the plugin as a typed but empty event - if (_value.data_size() == 0) { - cout << "BANG!" << endl; - const uint32_t type_id = map->uri_to_id(NULL, _value.get_blob_type()); - ebuf->append(frames, 0, type_id, 0, NULL); - _port->raise_set_by_user_flag(); - return; - - } else if (!strcmp(_value.get_blob_type(), "lv2_midi:MidiEvent")) { - const uint32_t type_id = map->uri_to_id(NULL, - "http://lv2plug.in/ns/ext/midi#MidiEvent"); - - ebuf->prepare_write(context.start(), context.nframes()); - // FIXME: use OSC midi type? avoid MIDI over OSC entirely? - ebuf->append(frames, 0, type_id, _value.data_size(), - (const uint8_t*)_value.get_blob()); - _port->raise_set_by_user_flag(); - return; - } - } - - if (_value.type() == Atom::BLOB) - cerr << "WARNING: Unknown value blob type " << _value.get_blob_type() << endl; - else - cerr << "WARNING: Unknown value type " << (int)_value.type() << endl; - } -} - - -void -SetPortValueEvent::post_process() -{ - if (_error == NO_ERROR) { - assert(_port != NULL); - _responder->respond_ok(); - _engine.broadcaster()->send_port_value(_port_path, _value); - - } else if (_error == ILLEGAL_PATH) { - string msg = "Illegal port path \""; - msg.append(_port_path).append("\""); - _responder->respond_error(msg); - - } else if (_error == ILLEGAL_VOICE) { - std::ostringstream ss; - ss << "Illegal voice number " << _voice_num; - _responder->respond_error(ss.str()); - - } else if (_error == PORT_NOT_FOUND) { - string msg = "Unable to find port "; - msg.append(_port_path).append(" for set_port_value"); - _responder->respond_error(msg); - - } else if (_error == NO_SPACE) { - std::ostringstream msg("Attempt to write "); - msg << _value.data_size() << " bytes to " << _port_path << ", with capacity " - << _port->buffer_size() << endl; - _responder->respond_error(msg.str()); - } -} - - -} // namespace Ingen - diff --git a/src/libs/engine/events/SetPortValueEvent.hpp b/src/libs/engine/events/SetPortValueEvent.hpp deleted file mode 100644 index 2fc68d9b..00000000 --- a/src/libs/engine/events/SetPortValueEvent.hpp +++ /dev/null @@ -1,80 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef SETPORTVALUEEVENT_H -#define SETPORTVALUEEVENT_H - -#include -#include "QueuedEvent.hpp" -#include "types.hpp" -using std::string; - -namespace Ingen { - -class PortImpl; - - -/** An event to change the value of a port. - * - * This event can either be queued or immediate, depending on the queued - * parameter passed to the constructor. It must be passed to the appropriate - * place (ie queued event passed to the event queue and non-queued event - * processed in the audio thread) or nasty things will happen. - * - * \ingroup engine - */ -class SetPortValueEvent : public QueuedEvent -{ -public: - SetPortValueEvent(Engine& engine, - SharedPtr responder, - bool queued, - SampleCount timestamp, - const string& port_path, - const Raul::Atom& value); - - SetPortValueEvent(Engine& engine, - SharedPtr responder, - bool queued, - SampleCount timestamp, - uint32_t voice_num, - const string& port_path, - const Raul::Atom& value); - - ~SetPortValueEvent(); - - void pre_process(); - void execute(ProcessContext& context); - void post_process(); - -private: - enum ErrorType { NO_ERROR, PORT_NOT_FOUND, NO_SPACE, - ILLEGAL_PATH, ILLEGAL_VOICE, TYPE_MISMATCH }; - - bool _queued; - bool _omni; - uint32_t _voice_num; - const string _port_path; - const Raul::Atom _value; - PortImpl* _port; - ErrorType _error; -}; - - -} // namespace Ingen - -#endif // SETPORTVALUEEVENT_H diff --git a/src/libs/engine/events/UnregisterClientEvent.cpp b/src/libs/engine/events/UnregisterClientEvent.cpp deleted file mode 100644 index 72ab8048..00000000 --- a/src/libs/engine/events/UnregisterClientEvent.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "interface/ClientInterface.hpp" -#include "Responder.hpp" -#include "UnregisterClientEvent.hpp" -#include "Engine.hpp" -#include "ClientBroadcaster.hpp" - -namespace Ingen { - - -UnregisterClientEvent::UnregisterClientEvent(Engine& engine, SharedPtr responder, SampleCount timestamp, const string& uri) -: QueuedEvent(engine, responder, timestamp) -, _uri(uri) -{ -} - - -void -UnregisterClientEvent::post_process() -{ - if (_engine.broadcaster()->unregister_client(_uri)) - _responder->respond_ok(); - else - _responder->respond_error("Unable to unregister client"); -} - - -} // namespace Ingen - diff --git a/src/libs/engine/events/UnregisterClientEvent.hpp b/src/libs/engine/events/UnregisterClientEvent.hpp deleted file mode 100644 index c21bc5da..00000000 --- a/src/libs/engine/events/UnregisterClientEvent.hpp +++ /dev/null @@ -1,54 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef UNREGISTERCLIENTEVENT_H -#define UNREGISTERCLIENTEVENT_H - -#include "QueuedEvent.hpp" -#include -using std::string; - -namespace Ingen { - -namespace Shared { - class ClientInterface; -} -using Shared::ClientInterface; - - -/** Unregisters an OSC client so it no longer receives notifications. - * - * \ingroup engine - */ -class UnregisterClientEvent : public QueuedEvent -{ -public: - UnregisterClientEvent(Engine& engine, - SharedPtr responder, - SampleCount timestamp, - const string& uri); - - void post_process(); - -private: - string _uri; -}; - - -} // namespace Ingen - -#endif // UNREGISTERCLIENTEVENT_H diff --git a/src/libs/engine/jack_compat.h b/src/libs/engine/jack_compat.h deleted file mode 100644 index 792148e8..00000000 --- a/src/libs/engine/jack_compat.h +++ /dev/null @@ -1,56 +0,0 @@ -/* JACK MIDI API compatibility hacks. - * Copyright (C) 2007 Nedko Arnaudov - * - * This program 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; version 2 of the License - * - * 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 General Public License for more 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef JACK_COMPAT_H -#define JACK_COMPAT_H - - -#if defined(JACK_MIDI_NEEDS_NFRAMES) - -jack_nframes_t -jack_midi_get_event_count_compat( - void * port_buffer) -{ -#if defined(HAVE_OLD_JACK_MIDI) - return jack_midi_port_get_info(port_buffer, 0)->event_count; -#else - return jack_midi_get_event_count(port_buffer, 0); -#endif -} - -#define jack_midi_get_event_count jack_midi_get_event_count_compat - -int -jack_midi_event_get_compat( - jack_midi_event_t * event, - void * port_buffer, - jack_nframes_t event_index) -{ - return jack_midi_event_get(event, port_buffer, event_index, 0); -} - -#define jack_midi_event_get jack_midi_event_get_compat - -#else - -#if defined(HAVE_OLD_JACK_MIDI) -#error "Old (0.102.20) JACK MIDI API needs nframes (autotools probably gone mad)" -#endif - -#endif /* #if defined(JACK_MIDI_NEEDS_NFRAMES) */ - -#endif /* JACK_COMPAT_H */ diff --git a/src/libs/engine/lv2_contexts.h b/src/libs/engine/lv2_contexts.h deleted file mode 120000 index 8556a582..00000000 --- a/src/libs/engine/lv2_contexts.h +++ /dev/null @@ -1 +0,0 @@ -../../../../lv2/lv2/contexts/lv2_contexts.h \ No newline at end of file diff --git a/src/libs/engine/tuning.hpp b/src/libs/engine/tuning.hpp deleted file mode 100644 index 7ec55ae4..00000000 --- a/src/libs/engine/tuning.hpp +++ /dev/null @@ -1,39 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef TUNING_H -#define TUNING_H - -#include -#include - -namespace Ingen { - -// 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; - -//static const timespec main_rate = { 0, 500000000 }; // 1/2 second -static const timespec main_rate = { 0, 125000000 }; // 1/8 second - - -} // namespace Ingen - -#endif // TUNING_H diff --git a/src/libs/engine/types.hpp b/src/libs/engine/types.hpp deleted file mode 100644 index d242e61d..00000000 --- a/src/libs/engine/types.hpp +++ /dev/null @@ -1,33 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef TYPES_HPP -#define TYPES_HPP - -#include // for NULL, size_t, etc -#include - -typedef unsigned char uchar; -typedef unsigned int uint; -typedef unsigned long ulong; - -typedef jack_default_audio_sample_t Sample; -typedef jack_nframes_t SampleCount; -typedef jack_nframes_t SampleRate; -typedef jack_nframes_t FrameTime; - -#endif // TYPES_HPP diff --git a/src/libs/engine/util.hpp b/src/libs/engine/util.hpp deleted file mode 100644 index 8f97beb3..00000000 --- a/src/libs/engine/util.hpp +++ /dev/null @@ -1,78 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef UTIL_HPP -#define UTIL_HPP - -#include CONFIG_H_PATH -#include -#include - -#include -#ifdef __SSE__ -#include -#endif - -#ifdef USE_ASSEMBLY -# if SIZEOF_VOID_P==8 -# define cpuid(a,b,c,d,n) asm("xchgq %%rbx, %1; cpuid; xchgq %%rbx, %1": "=a" (a), "=r" (b), "=c" (c), "=d" (d) : "a" (n)); -# else -# define cpuid(a,b,c,d,n) asm("xchgl %%ebx, %1; cpuid; xchgl %%ebx, %1": "=a" (a), "=r" (b), "=c" (c), "=d" (d) : "a" (n)); -# endif -#endif - -namespace Ingen { - -/** Set flags to disable denormal processing. - */ -inline void -set_denormal_flags() -{ -#ifdef USE_ASSEMBLY -#ifdef __SSE__ - unsigned long a, b, c, d0, d1; - int stepping, model, family, extfamily; - - cpuid(a,b,c,d1,1); - if (d1 & 1<<25) { /* It has SSE support */ - _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); - family = (a >> 8) & 0xf; - extfamily = (a >> 20) & 0xff; - model = (a >> 4) & 0xf; - stepping = a & 0xf; - cpuid(a,b,c,d0,0); - if (b == 0x756e6547) { /* It's an Intel */ - if (family == 15 && extfamily == 0 && model == 0 && stepping < 7) { - return; - } - } - if (d1 & 1<<26) { /* bit 26, SSE2 support */ - _mm_setcsr(_mm_getcsr() | 0x8040); // set DAZ and FZ bits of MXCSR - //cerr << "Set SSE denormal fix flag." << endl; - } - } else { - std::cerr << "This code has been built with SSE support, but your processor does" - << " not support the SSE instruction set." << std::endl << "Exiting." << std::endl; - exit(EXIT_FAILURE); - } -#endif -#endif -} - -} // namespace Ingen - -#endif // UTIL_HPP diff --git a/src/libs/engine/wscript b/src/libs/engine/wscript deleted file mode 100644 index 1ad135d3..00000000 --- a/src/libs/engine/wscript +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env python -import Params - -def build(bld): - obj = bld.create_obj('cpp', 'shlib') - obj.source = ''' - AudioBuffer.cpp - Buffer.cpp - ClientBroadcaster.cpp - ConnectionImpl.cpp - DuplexPort.cpp - Engine.cpp - EngineStore.cpp - Event.cpp - EventBuffer.cpp - EventSink.cpp - InputPort.cpp - InternalPlugin.cpp - JackAudioDriver.cpp - JackMidiDriver.cpp - LADSPAPlugin.cpp - LV2Info.cpp - LV2Plugin.cpp - MessageContext.cpp - MidiControlNode.cpp - MidiNoteNode.cpp - MidiTriggerNode.cpp - NodeBase.cpp - NodeFactory.cpp - OSCClientSender.cpp - OSCEngineReceiver.cpp - ObjectSender.cpp - OutputPort.cpp - PluginImpl.cpp - PortImpl.cpp - PostProcessor.cpp - ProcessSlave.cpp - QueuedEngineInterface.cpp - QueuedEvent.cpp - QueuedEventSource.cpp - TransportNode.cpp - engine.cpp - events/AllNotesOffEvent.cpp - events/ConnectionEvent.cpp - events/CreateNodeEvent.cpp - events/CreatePortEvent.cpp - events/DeactivateEvent.cpp - events/DestroyEvent.cpp - events/DisconnectAllEvent.cpp - events/DisconnectionEvent.cpp - events/LoadPluginsEvent.cpp - events/MidiLearnEvent.cpp - events/NoteEvent.cpp - events/RegisterClientEvent.cpp - events/RenameEvent.cpp - events/RequestAllObjectsEvent.cpp - events/RequestMetadataEvent.cpp - events/RequestObjectEvent.cpp - events/RequestPluginEvent.cpp - events/RequestPluginsEvent.cpp - events/RequestPortValueEvent.cpp - events/SendPortActivityEvent.cpp - events/SendPortValueEvent.cpp - events/SetMetadataEvent.cpp - events/SetPortValueEvent.cpp - events/UnregisterClientEvent.cpp - ''' - obj.includes = ['..', '../../common', '../..', './events'] - obj.name = 'libingen_engine' - obj.target = 'ingen_engine' - obj.uselib = 'GLIBMM GTHREAD SLV2 JACK LIBLO RAUL REDLANDMM SOUP' - obj.vnum = '0.0.0' - diff --git a/src/libs/gui/App.cpp b/src/libs/gui/App.cpp deleted file mode 100644 index f49d394c..00000000 --- a/src/libs/gui/App.cpp +++ /dev/null @@ -1,440 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include CONFIG_H_PATH -#include "App.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include "module/global.hpp" -#include "module/Module.hpp" -#include "module/World.hpp" -#include "interface/EngineInterface.hpp" -#include "serialisation/serialisation.hpp" -#include "client/ObjectModel.hpp" -#include "client/PatchModel.hpp" -#include "client/ClientStore.hpp" -#include "engine/Engine.hpp" -#include "NodeModule.hpp" -#include "ControlPanel.hpp" -#include "SubpatchModule.hpp" -#include "LoadPluginWindow.hpp" -#include "PatchWindow.hpp" -#include "MessagesWindow.hpp" -#include "GladeFactory.hpp" -#include "PatchTreeWindow.hpp" -#include "Configuration.hpp" -#include "ConnectWindow.hpp" -#include "ThreadedLoader.hpp" -#include "WindowFactory.hpp" -#include "Port.hpp" -#ifdef HAVE_SLV2 -#include -#endif - -using namespace std; -using namespace Ingen::Client; - -namespace Ingen { namespace Client { class PluginModel; } } - -namespace Ingen { -namespace GUI { - -class Port; - - -/// Singleton instance -App* App::_instance = 0; - - -App::App(Ingen::Shared::World* world) - : _configuration(new Configuration()) - , _about_dialog(NULL) - , _window_factory(new WindowFactory()) - , _world(world) - , _enable_signal(true) -{ - Glib::RefPtr glade_xml = GladeFactory::new_glade_reference(); - - glade_xml->get_widget_derived("connect_win", _connect_window); - glade_xml->get_widget_derived("messages_win", _messages_window); - glade_xml->get_widget_derived("patch_tree_win", _patch_tree_window); - glade_xml->get_widget("about_win", _about_dialog); - _about_dialog->property_program_name() = "Ingen"; - - PluginModel::set_rdf_world(*world->rdf_world); - -#ifdef HAVE_SLV2 - PluginModel::set_slv2_world(world->slv2_world); -#endif -} - - -App::~App() -{ -} - -void -App::run(int argc, char** argv, Ingen::Shared::World* world) -{ - Gnome::Canvas::init(); - Gtk::Main main(argc, argv); - - if (!_instance) - _instance = new App(world); - - // Load configuration settings - _instance->configuration()->load_settings(); - _instance->configuration()->apply_settings(); - - // Set default window icon - const Glib::ustring icon_path = INGEN_DATA_DIR "/ingen.svg"; - try { - if (Glib::file_test(icon_path, Glib::FILE_TEST_EXISTS)) - Gtk::Window::set_default_icon_from_file(icon_path); - } catch (Gdk::PixbufError err) { - cerr << "Unable to load window icon " << icon_path << ": " << err.what() << endl; - } - - // Set style for embedded node GUIs - const string rc_style = - "style \"ingen_embedded_node_gui_style\" {" - " bg[NORMAL] = \"#212222\"" - " bg[ACTIVE] = \"#505050\"" - " bg[PRELIGHT] = \"#525454\"" - " bg[SELECTED] = \"#99A0A0\"" - " bg[INSENSITIVE] = \"#F03030\"" - " fg[NORMAL] = \"#FFFFFF\"" - " fg[ACTIVE] = \"#FFFFFF\"" - " fg[PRELIGHT] = \"#FFFFFF\"" - " fg[SELECTED] = \"#FFFFFF\"" - " fg[INSENSITIVE] = \"#FFFFFF\"" - "}\n" - "widget \"*ingen_embedded_node_gui_container*\" style \"ingen_embedded_node_gui_style\"\n"; - - Gtk::RC::parse_string(rc_style); - - App::instance().connect_window()->start(world); - - main.run(); - - cout << "Gtk exiting." << endl; -} - - -void -App::attach(SharedPtr client, - SharedPtr handle) -{ - assert( ! _client); - assert( ! _store); - assert( ! _loader); - - _world->engine->register_client(client.get()); - - _client = client; - _handle = handle; - _store = SharedPtr(new ClientStore(_world->engine, client)); - _loader = SharedPtr(new ThreadedLoader(_world->engine)); - - _patch_tree_window->init(*_store); - - _client->signal_response_error.connect(sigc::mem_fun(this, &App::error_response)); - _client->signal_error.connect(sigc::mem_fun(this, &App::error_message)); -} - - -void -App::detach() -{ - if (_world->engine) { - _window_factory->clear(); - _store->clear(); - - _loader.reset(); - _store.reset(); - _client.reset(); - _handle.reset(); - _world->engine.reset(); - } -} - - -const SharedPtr& -App::serialiser() -{ - if (!_serialiser) { - if (!_world->serialisation_module) - _world->serialisation_module = Ingen::Shared::load_module("ingen_serialisation"); - - if (_world->serialisation_module) - _serialiser = SharedPtr(Ingen::Serialisation::new_serialiser(_world, _store)); - - if (!_serialiser) - cerr << "WARNING: Failed to load ingen_serialisation module, save disabled." << endl; - } - return _serialiser; -} - - -void -App::error_response(int32_t id, const string& str) -{ - error_message(str); -} - - -void -App::error_message(const string& str) -{ - _messages_window->post(str); - - if (!_messages_window->is_visible()) - _messages_window->present(); - - _messages_window->set_urgency_hint(true); -} - - -void -App::port_activity(Port* port) -{ - std::pair inserted = _activity_ports.insert(make_pair(port, false)); - if (inserted.second) - inserted.first->second = false; - - if (port->is_output()) { - for (Port::Connections::const_iterator i = port->connections().begin(); i != port->connections().end(); ++i) { - const SharedPtr dst = PtrCast(i->lock()->dest().lock()); - if (dst) - port_activity(dst.get()); - } - } - - port->set_highlighted(true, false, true, false); -} - - -void -App::activity_port_destroyed(Port* port) -{ - ActivityPorts::iterator i = _activity_ports.find(port); - if (i != _activity_ports.end()) - _activity_ports.erase(i); - - return; -} - - -bool -App::animate() -{ - for (ActivityPorts::iterator i = _activity_ports.begin(); i != _activity_ports.end() ; ) { - ActivityPorts::iterator next = i; - ++next; - - if ((*i).second) { // saw it last time, unhighlight and pop - (*i).first->set_highlighted(false, false, true, false); - _activity_ports.erase(i); - } else { - (*i).second = true; - } - - i = next; - } - - return true; -} - - - -/******** Event Handlers ************/ - - -#if 0 -App::event_load_session() -{ - Gtk::FileChooserDialog* dialog - = new Gtk::FileChooserDialog(*_main_window, "Load Session", Gtk::FILE_CHOOSER_ACTION_OPEN); - - dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); - dialog->add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK); - int result = dialog->run(); - string filename = dialog->get_filename(); - delete dialog; - - cout << result << endl; - - assert(result == Gtk::RESPONSE_OK || result == Gtk::RESPONSE_CANCEL || result == Gtk::RESPONSE_NONE); - - if (result == Gtk::RESPONSE_OK) - //configuration->load_session(filename); - _controller->load_session(filename); -} - - -void -App::event_save_session_as() -{ - Gtk::FileChooserDialog dialog(*_main_window, "Save Session", Gtk::FILE_CHOOSER_ACTION_SAVE); - - /* - Gtk::VBox* box = dialog.get_vbox(); - Gtk::Label warning("Warning: Recursively saving will overwrite any subpatch files \ - without confirmation."); - box->pack_start(warning, false, false, 2); - Gtk::CheckButton recursive_checkbutton("Recursively save all subpatches"); - box->pack_start(recursive_checkbutton, false, false, 0); - recursive_checkbutton.show(); - */ - dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); - dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK); - - int result = dialog.run(); - //bool recursive = recursive_checkbutton.get_active(); - - assert(result == Gtk::RESPONSE_OK || result == Gtk::RESPONSE_CANCEL || result == Gtk::RESPONSE_NONE); - - if (result == Gtk::RESPONSE_OK) { - string filename = dialog.get_filename(); - if (filename.length() < 11 || filename.substr(filename.length()-10) != ".omsession") - filename += ".omsession"; - - bool confirm = false; - std::fstream fin; - fin.open(filename.c_str(), std::ios::in); - if (fin.is_open()) { // File exists - string msg = "File already exists! Are you sure you want to overwrite "; - msg += filename + "?"; - Gtk::MessageDialog confir_dialog(*_main_window, - msg, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_YES_NO, true); - if (confir_dialog.run() == Gtk::RESPONSE_YES) - confirm = true; - else - confirm = false; - } else { // File doesn't exist - confirm = true; - } - fin.close(); - - if (confirm) { - _controller->save_session(filename); - } - } -} -#endif - - -void -App::register_callbacks() -{ - Glib::signal_timeout().connect( - sigc::mem_fun(App::instance(), &App::gtk_main_iteration), 25, G_PRIORITY_DEFAULT); - - Glib::signal_timeout().connect( - sigc::mem_fun(App::instance(), &App::animate), 50, G_PRIORITY_DEFAULT); -} - - -bool -App::gtk_main_iteration() -{ - if (!_client) - return false; - - if (_world->local_engine) { - _world->local_engine->main_iteration(); - } else { - _enable_signal = false; - _client->emit_signals(); - _enable_signal = true; - } - - return true; -} - - -void -App::show_about() -{ - _about_dialog->run(); - _about_dialog->hide(); -} - - -void -App::quit() -{ - Gtk::Main::quit(); -} - - -Glib::RefPtr -App::icon_from_path(const string& path, int size) -{ - /* If weak references to Glib::Objects are needed somewhere else it will - probably be a good idea to create a proper WeakPtr class instead of - using raw pointers, but for a single use this will do. */ - - Glib::RefPtr buf; - if (path.length() == 0) - return buf; - - Icons::iterator iter = _icons.find(make_pair(path, size)); - - if (iter != _icons.end()) { - // we need to reference manually since the RefPtr constructor doesn't do it - iter->second->reference(); - return Glib::RefPtr(iter->second); - } - - try { - buf = Gdk::Pixbuf::create_from_file(path, size, size); - _icons.insert(make_pair(make_pair(path, size), buf.operator->())); - buf->add_destroy_notify_callback(new pair(path, size), - &App::icon_destroyed); - cerr << "Loaded icon " << path << " with size " << size << endl; - } catch (Glib::Error e) { - cerr << "Error loading icon: " << e.what() << endl; - } - return buf; -} - - -void* -App::icon_destroyed(void* data) -{ - pair* p = static_cast*>(data); - cerr << "Destroyed icon " << p->first << " with size " << p->second << endl; - Icons::iterator iter = instance()._icons.find(*p); - if (iter != instance()._icons.end()) - instance()._icons.erase(iter); - - delete p; // allocated in App::icon_from_path - - return NULL; -} - - -} // namespace GUI -} // namespace Ingen - diff --git a/src/libs/gui/App.hpp b/src/libs/gui/App.hpp deleted file mode 100644 index 4a4b6580..00000000 --- a/src/libs/gui/App.hpp +++ /dev/null @@ -1,173 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef INGEN_APP_HPP -#define INGEN_APP_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Ingen { - class Engine; - namespace Shared { - class EngineInterface; - class ClientInterface; - class World; - } - namespace Client { - class PatchModel; - class PluginModel; - class ClientStore; - class SigClientInterface; - } - namespace Serialisation { - class Serialiser; - } -} - -/** \defgroup GUI GTK GUI - */ - -namespace Ingen { -namespace GUI { - -class MessagesWindow; -class PatchCanvas; -class PatchTreeView; -class PatchTreeWindow; -class ConnectWindow; -class Configuration; -class ThreadedLoader; -class WindowFactory; -class Port; - - -/** Singleton master class most everything is contained within. - * - * This is a horrible god-object, but it's shrinking in size as things are - * moved out. Hopefully it will go away entirely some day.. - * - * \ingroup GUI - */ -class App -{ -public: - ~App(); - - void error_message(const std::string& msg); - - void attach(SharedPtr client, - SharedPtr handle=SharedPtr()); - - void detach(); - - void register_callbacks(); - bool gtk_main_iteration(); - - void show_about(); - void quit(); - - void port_activity(Port* port); - void activity_port_destroyed(Port* port); - - bool signal() const { return _enable_signal; } - bool disable_signals() { bool old = _enable_signal; _enable_signal = false; return old; } - void enable_signals(bool b) { _enable_signal = b; } - - ConnectWindow* connect_window() const { return _connect_window; } - MessagesWindow* messages_dialog() const { return _messages_window; } - PatchTreeWindow* patch_tree() const { return _patch_tree_window; } - Configuration* configuration() const { return _configuration; } - WindowFactory* window_factory() const { return _window_factory; } - - Glib::RefPtr icon_from_path(const std::string& path, int size); - - const SharedPtr& engine() const { return _world->engine; } - const SharedPtr& client() const { return _client; } - const SharedPtr& store() const { return _store; } - const SharedPtr& loader() const { return _loader; } - - const SharedPtr& serialiser(); - - static inline App& instance() { assert(_instance); return *_instance; } - - static void run(int argc, char** argv, Ingen::Shared::World* world); - - Ingen::Shared::World* world() { return _world; } - -protected: - - /** This is needed for the icon map. */ - template - struct LexicalCompare { - bool operator()(const std::pair& p1, const std::pair& p2) { - return (p1.first < p2.first) || - ((p1.first == p2.first) && (p1.second < p2.second)); - } - }; - - typedef std::map< std::pair, - Gdk::Pixbuf*, - LexicalCompare > Icons; - Icons _icons; - - App(Ingen::Shared::World* world); - - bool animate(); - void error_response(int32_t id, const std::string& str); - - static void* icon_destroyed(void* data); - - static App* _instance; - - SharedPtr _client; - SharedPtr _handle; - SharedPtr _store; - SharedPtr _serialiser; - SharedPtr _loader; - - Configuration* _configuration; - - ConnectWindow* _connect_window; - MessagesWindow* _messages_window; - PatchTreeWindow* _patch_tree_window; - Gtk::AboutDialog* _about_dialog; - WindowFactory* _window_factory; - - Ingen::Shared::World* _world; - - typedef std::map ActivityPorts; - ActivityPorts _activity_ports; - - bool _enable_signal; -}; - - -} // namespace GUI -} // namespace Ingen - -#endif // INGEN_APP_HPP - diff --git a/src/libs/gui/BreadCrumb.hpp b/src/libs/gui/BreadCrumb.hpp deleted file mode 100644 index c3b05e01..00000000 --- a/src/libs/gui/BreadCrumb.hpp +++ /dev/null @@ -1,81 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef BREADCRUMB_H -#define BREADCRUMB_H - -#include -#include -#include -#include "PatchView.hpp" - -namespace Ingen { -namespace GUI { - - -/** Breadcrumb button in a PatchWindow. - * - * Each Breadcrumb stores a reference to a PatchView for quick switching. - * So, the amount of allocated PatchViews at a given time is equal to the - * number of visible breadcrumbs (which is the perfect cache for GUI - * responsiveness balanced with mem consumption). - * - * \ingroup GUI - */ -class BreadCrumb : public Gtk::ToggleButton -{ -public: - BreadCrumb(const Path& path, SharedPtr view = SharedPtr()) - : _path(path) - , _view(view) - { - assert( !view || view->patch()->path() == path); - set_border_width(0); - set_path(path); - show_all(); - } - - void set_view(SharedPtr view) { - assert( !view || view->patch()->path() == _path); - _view = view; - } - - const Path& path() const { return _path; } - SharedPtr view() const { return _view; } - - void set_path(const Path& path) - { - remove(); - const string text = (path == "/") ? "/" : path.name().c_str(); - Gtk::Label* lab = manage(new Gtk::Label(text)); - lab->set_padding(0, 0); - lab->show(); - add(*lab); - - if (_view && _view->patch()->path() != path) - _view.reset(); - } - -private: - Path _path; - SharedPtr _view; -}; - -} // namespace GUI -} // namespace Ingen - -#endif // BREADCRUMB_H diff --git a/src/libs/gui/BreadCrumbBox.cpp b/src/libs/gui/BreadCrumbBox.cpp deleted file mode 100644 index 7edaed9f..00000000 --- a/src/libs/gui/BreadCrumbBox.cpp +++ /dev/null @@ -1,210 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "BreadCrumbBox.hpp" -#include "BreadCrumb.hpp" -#include "App.hpp" -#include "client/SigClientInterface.hpp" -namespace Ingen { -namespace GUI { - - -BreadCrumbBox::BreadCrumbBox() - : Gtk::HBox() - , _active_path("/") - , _full_path("/") - , _enable_signal(true) -{ - App::instance().client()->signal_object_destroyed.connect( - sigc::mem_fun(this, &BreadCrumbBox::object_destroyed)); -} - - -SharedPtr -BreadCrumbBox::view(const Path& path) -{ - for (std::list::const_iterator i = _breadcrumbs.begin(); i != _breadcrumbs.end(); ++i) - if ((*i)->path() == path) - return (*i)->view(); - - return SharedPtr(); -} - - -/** Sets up the crumbs to display @a path. - * - * If @a path is already part of the shown path, it will be selected and the - * children preserved. - */ -void -BreadCrumbBox::build(Path path, SharedPtr view) -{ - bool old_enable_signal = _enable_signal; - _enable_signal = false; - - // Moving to a path we already contain, just switch the active button - if (_breadcrumbs.size() > 0 && (path.is_parent_of(_full_path) || path == _full_path)) { - - for (std::list::iterator i = _breadcrumbs.begin(); i != _breadcrumbs.end(); ++i) { - if ((*i)->path() == path) { - (*i)->set_active(true); - if (!(*i)->view()) - (*i)->set_view(view); - - // views are expensive, having two around for the same patch is a bug - assert((*i)->view() == view); - - } else { - (*i)->set_active(false); - } - } - - _active_path = path; - _enable_signal = old_enable_signal; - - - // Moving to a child of the full path, just append crumbs (preserve view cache) - } else if (_breadcrumbs.size() > 0 && (path.is_child_of(_full_path))) { - - string suffix = path.substr(_full_path.length()); - while (suffix.length() > 0) { - if (suffix[0] == '/') - suffix = suffix.substr(1); - const string name = suffix.substr(0, suffix.find("/")); - _full_path = _full_path.base() + name; - BreadCrumb* but = create_crumb(_full_path, view); - pack_start(*but, false, false, 1); - _breadcrumbs.push_back(but); - but->show(); - if (suffix.find("/") == string::npos) - break; - else - suffix = suffix.substr(suffix.find("/")+1); - } - - for (std::list::iterator i = _breadcrumbs.begin(); i != _breadcrumbs.end(); ++i) - (*i)->set_active(false); - _breadcrumbs.back()->set_active(true); - - - // Rebuild from scratch - // Getting here is bad unless absolutely necessary, since the PatchView cache is lost - } else { - - _full_path = path; - _active_path = path; - - // Empty existing breadcrumbs - for (std::list::iterator i = _breadcrumbs.begin(); i != _breadcrumbs.end(); ++i) - remove(**i); - _breadcrumbs.clear(); - - // Add root - BreadCrumb* root_but = create_crumb("/", view); - pack_start(*root_but, false, false, 1); - _breadcrumbs.push_front(root_but); - root_but->set_active(root_but->path() == _active_path); - - Path working_path = "/"; - string suffix = path.substr(1); - while (suffix.length() > 0) { - if (suffix[0] == '/') - suffix = suffix.substr(1); - const string name = suffix.substr(0, suffix.find("/")); - working_path = working_path.base() + name; - BreadCrumb* but = create_crumb(working_path, view); - pack_start(*but, false, false, 1); - _breadcrumbs.push_back(but); - but->set_active(working_path == _active_path); - but->show(); - if (suffix.find("/") == string::npos) - break; - else - suffix = suffix.substr(suffix.find("/")+1); - } - } - - _enable_signal = old_enable_signal; -} - - -/** Create a new crumb, assigning it a reference to @a view if their paths - * match, otherwise ignoring @a view. - */ -BreadCrumb* -BreadCrumbBox::create_crumb(const Path& path, - SharedPtr view) -{ - BreadCrumb* but = manage(new BreadCrumb(path, - (view && path == view->patch()->path()) ? view : SharedPtr())); - - but->signal_toggled().connect(sigc::bind(sigc::mem_fun( - this, &BreadCrumbBox::breadcrumb_clicked), but)); - - return but; -} - - -void -BreadCrumbBox::breadcrumb_clicked(BreadCrumb* crumb) -{ - if (_enable_signal) { - _enable_signal = false; - - if (!crumb->get_active()) { - // Tried to turn off the current active button, bad user, no cookie - crumb->set_active(true); - } else { - signal_patch_selected.emit(crumb->path(), crumb->view()); - if (crumb->path() != _active_path) - crumb->set_active(false); - } - _enable_signal = true; - } -} - - -void -BreadCrumbBox::object_destroyed(const Path& path) -{ - for (std::list::iterator i = _breadcrumbs.begin(); i != _breadcrumbs.end(); ++i) { - if ((*i)->path() == path) { - // Remove all crumbs after the removed one (inclusive) - for (std::list::iterator j = i; j != _breadcrumbs.end(); ) { - BreadCrumb* bc = *j; - j = _breadcrumbs.erase(j); - remove(*bc); - } - break; - } - } -} - - -void -BreadCrumbBox::object_renamed(const Path& old_path, const Path& new_path) -{ - for (std::list::iterator i = _breadcrumbs.begin(); i != _breadcrumbs.end(); ++i) { - if ((*i)->path() == old_path) - (*i)->set_path(new_path); - } -} - - -} // namespace GUI -} // namespace Ingen - diff --git a/src/libs/gui/BreadCrumbBox.hpp b/src/libs/gui/BreadCrumbBox.hpp deleted file mode 100644 index b5650252..00000000 --- a/src/libs/gui/BreadCrumbBox.hpp +++ /dev/null @@ -1,70 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef BREADCRUMBBOX_H -#define BREADCRUMBBOX_H - -#include -#include -#include -#include -#include -#include -#include "PatchView.hpp" - -namespace Ingen { -namespace GUI { - -class BreadCrumb; - - -/** Collection of breadcrumb buttons forming a path. - * - * This doubles as a cache for PatchViews. - * - * \ingroup GUI - */ -class BreadCrumbBox : public Gtk::HBox -{ -public: - BreadCrumbBox(); - - SharedPtr view(const Path& path); - - void build(Path path, SharedPtr view); - - sigc::signal > signal_patch_selected; - -private: - BreadCrumb* create_crumb(const Path& path, - SharedPtr view = SharedPtr()); - - void breadcrumb_clicked(BreadCrumb* crumb); - - void object_destroyed(const Path& path); - void object_renamed(const Path& old_path, const Path& new_path); - - Path _active_path; - Path _full_path; - bool _enable_signal; - std::list _breadcrumbs; -}; - -} // namespace GUI -} // namespace Ingen - -#endif // BREADCRUMBBOX_H diff --git a/src/libs/gui/Configuration.cpp b/src/libs/gui/Configuration.cpp deleted file mode 100644 index 758dc744..00000000 --- a/src/libs/gui/Configuration.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "Configuration.hpp" -#include -#include -#include -#include -#include -#include "client/PortModel.hpp" -#include "client/PluginModel.hpp" -#include "serialisation/Parser.hpp" -#include "App.hpp" - -using namespace std; - -namespace Ingen { -namespace GUI { - -using namespace Ingen::Client; - - -Configuration::Configuration() - // Colours from the Tango palette with modified V and alpha - : _name_style(HUMAN) - , _audio_port_color( 0x244678C0) - , _control_port_color(0x4A8A0EC0) - , _event_port_color( 0x960909C0) -// , _midi_port_color( 0x960909C0) -// , _osc_port_color( 0x5C3566C0) -{ -} - - -Configuration::~Configuration() -{ -} - - -/** Loads settings from the rc file. Passing no parameter will load from - * the default location. - */ -void -Configuration::load_settings(string filename) -{ - /* ... */ -} - - -/** Saves settings to rc file. Passing no parameter will save to the - * default location. - */ -void -Configuration::save_settings(string filename) -{ - /* ... */ -} - - -/** Applies the current loaded settings to whichever parts of the app - * need updating. - */ -void -Configuration::apply_settings() -{ - /* ... */ -} - - -uint32_t -Configuration::get_port_color(const PortModel* p) -{ - assert(p != NULL); - - if (p->type().is_control()) { - return _control_port_color; - } else if (p->type().is_audio()) { - return _audio_port_color; - } else if (p->type().is_event()) { - return _event_port_color; - }/* else if (p->type().is_midi()) { - return _midi_port_color; - } else if (p->type().is_osc()) { - return _osc_port_color; - }*/ - - cerr << "[Configuration] Unknown port type " << p->type().uri() - << ", port will appear black." << endl; - - return 0x000000FF; -} - - -} // namespace GUI -} // namespace Ingen diff --git a/src/libs/gui/Configuration.hpp b/src/libs/gui/Configuration.hpp deleted file mode 100644 index 124b41c8..00000000 --- a/src/libs/gui/Configuration.hpp +++ /dev/null @@ -1,80 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef CONFIG_H -#define CONFIG_H - -#include - -namespace Ingen { namespace Client { class PortModel; } } -using Ingen::Client::PortModel; -using std::string; - -struct Coord { double x; double y; }; - -namespace Ingen { -namespace GUI { - - -/** Singleton state manager for the entire app. - * - * Stores settings like color preferences, search paths, etc. - * (ie any user-defined preferences to be stoed in the rc file). - * - * \ingroup GUI - */ -class Configuration -{ -public: - Configuration(); - ~Configuration(); - - void load_settings(string filename = ""); - void save_settings(string filename = ""); - - void apply_settings(); - - const string& patch_folder() { return _patch_folder; } - void set_patch_folder(const string& f) { _patch_folder = f; } - - uint32_t get_port_color(const PortModel* pi); - - enum NameStyle { PATH, HUMAN }; - - NameStyle name_style() const { return _name_style; } - void set_name_style(NameStyle s) { _name_style = s; } - -private: - /** Most recent patch folder shown in open dialog */ - string _patch_folder; - - NameStyle _name_style; - - uint32_t _audio_port_color; - uint32_t _control_port_color; - uint32_t _event_port_color; - //uint32_t _midi_port_color; - //uint32_t _osc_port_color; -}; - - -} // namespace GUI -} // namespace Ingen - -#endif // CONFIG_H - - diff --git a/src/libs/gui/ConnectWindow.cpp b/src/libs/gui/ConnectWindow.cpp deleted file mode 100644 index 5600a0b2..00000000 --- a/src/libs/gui/ConnectWindow.cpp +++ /dev/null @@ -1,458 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include CONFIG_H_PATH -#include "interface/EngineInterface.hpp" -#include "module/World.hpp" -#include "engine/tuning.hpp" -#include "engine/Engine.hpp" -#include "engine/QueuedEngineInterface.hpp" -#include "client/OSCClientReceiver.hpp" -#include "client/HTTPClientReceiver.hpp" -#include "client/OSCEngineSender.hpp" -#include "client/ThreadedSigClientInterface.hpp" -#include "client/ClientStore.hpp" -#include "client/PatchModel.hpp" -#include "module/Module.hpp" -#include "App.hpp" -#include "WindowFactory.hpp" -#include "ConnectWindow.hpp" -using Ingen::QueuedEngineInterface; -using Ingen::Client::ThreadedSigClientInterface; -using namespace std; - -namespace Ingen { -namespace GUI { - - -// ConnectWindow - - -ConnectWindow::ConnectWindow(BaseObjectType* cobject, const Glib::RefPtr& xml) - : Gtk::Dialog(cobject) - , _xml(xml) - , _mode(CONNECT_REMOTE) - , _ping_id(-1) - , _attached(false) - , _widgets_loaded(false) - , _connect_stage(0) - , _new_engine(NULL) -{ -} - - -void -ConnectWindow::start(Ingen::Shared::World* world) -{ - if (world->local_engine) { - _mode = INTERNAL; - if (_widgets_loaded) - _internal_radio->set_active(true); - } - - set_connected_to(world->engine); - - connect(true); -} - - -void -ConnectWindow::set_connected_to(SharedPtr engine) -{ - App::instance().world()->engine = engine; - - if (!_widgets_loaded) - return; - - if (engine) { - _icon->set(Gtk::Stock::CONNECT, Gtk::ICON_SIZE_LARGE_TOOLBAR); - _progress_bar->set_fraction(1.0); - _progress_label->set_text("Connected to engine"); - _url_entry->set_sensitive(false); - _connect_button->set_sensitive(false); - _disconnect_button->set_label("gtk-disconnect"); - _disconnect_button->set_sensitive(true); - _port_spinbutton->set_sensitive(false); - _launch_radio->set_sensitive(false); - _internal_radio->set_sensitive(false); - } else { - _icon->set(Gtk::Stock::DISCONNECT, Gtk::ICON_SIZE_LARGE_TOOLBAR); - _progress_bar->set_fraction(0.0); - _connect_button->set_sensitive(true); - _disconnect_button->set_sensitive(false); - - if (_new_engine) - _internal_radio->set_sensitive(true); - else - _internal_radio->set_sensitive(false); - - _server_radio->set_sensitive(true); - _launch_radio->set_sensitive(true); - - if (_mode == CONNECT_REMOTE ) - _url_entry->set_sensitive(true); - else if (_mode == LAUNCH_REMOTE ) - _port_spinbutton->set_sensitive(true); - - _progress_label->set_text(string("Disconnected")); - } -} - - -void -ConnectWindow::set_connecting_widget_states() -{ - if (!_widgets_loaded) - return; - - _connect_button->set_sensitive(false); - _disconnect_button->set_label("gtk-cancel"); - _disconnect_button->set_sensitive(true); - _server_radio->set_sensitive(false); - _launch_radio->set_sensitive(false); - _internal_radio->set_sensitive(false); - _url_entry->set_sensitive(false); - _port_spinbutton->set_sensitive(false); -} - - -/** Launch (if applicable) and connect to the Engine. - * - * This will create the EngineInterface and ClientInterface and initialize - * the App with them. - */ -void -ConnectWindow::connect(bool existing) -{ - if (_attached) - _attached = false; - - assert(!App::instance().client()); - - _connect_stage = 0; - set_connecting_widget_states(); - - Ingen::Shared::World* world = App::instance().world(); - - if (_mode == CONNECT_REMOTE) { - if (!existing) { - const string url = (_widgets_loaded ? (string)_url_entry->get_text() : world->engine->uri()); - world->engine = SharedPtr(new OSCEngineSender(url)); - } - - SharedPtr tsci(new ThreadedSigClientInterface(1024)); - SharedPtr client; - - const string& uri = world->engine->uri(); - const string& scheme = uri.substr(0, uri.find(":")); - if (scheme == "osc.udp" || scheme == "osc.tcp") - client = SharedPtr(new OSCClientReceiver(16181, tsci)); // FIXME: port - else if (scheme == "http") - client = SharedPtr(new HTTPClientReceiver(world, uri, tsci)); - - App::instance().attach(tsci, client); - App::instance().register_callbacks(); - - Glib::signal_timeout().connect( - sigc::mem_fun(this, &ConnectWindow::gtk_callback), 40); - - } else if (_mode == LAUNCH_REMOTE) { - - int port = _port_spinbutton->get_value_as_int(); - char port_str[6]; - snprintf(port_str, 6, "%u", port); - const string cmd = string("ingen -e --engine-port=").append(port_str); - - if (Raul::Process::launch(cmd)) { - world->engine = SharedPtr( - new OSCEngineSender(string("osc.udp://localhost:").append(port_str))); - - // FIXME: static args - SharedPtr tsci(new ThreadedSigClientInterface(1024)); - SharedPtr client(new OSCClientReceiver(16181, tsci)); - - App::instance().attach(tsci, client); - App::instance().register_callbacks(); - - Glib::signal_timeout().connect( - sigc::mem_fun(this, &ConnectWindow::gtk_callback), 40); - - } else { - cerr << "Failed to launch ingen process." << endl; - } - - } else if (_mode == INTERNAL) { - Ingen::Shared::World* world = App::instance().world(); - if ( ! world->local_engine) { - assert(_new_engine); - world->local_engine = SharedPtr(_new_engine(world)); - } - - if ( ! world->engine) - world->engine = world->local_engine->new_queued_interface(); - - SharedPtr client(new SigClientInterface()); - - world->local_engine->start_jack_driver(); - world->local_engine->activate(1); // FIXME: parallelism - - App::instance().attach(client); - App::instance().register_callbacks(); - - Glib::signal_timeout().connect( - sigc::mem_fun(this, &ConnectWindow::gtk_callback), 10); - } -} - - -void -ConnectWindow::disconnect() -{ - _connect_stage = -1; - _attached = false; - - App::instance().detach(); - set_connected_to(SharedPtr()); - - if (!_widgets_loaded) - return; - - _progress_bar->set_fraction(0.0); - _connect_button->set_sensitive(false); - _disconnect_button->set_sensitive(false); - - _connect_button->set_sensitive(true); - _disconnect_button->set_sensitive(false); -} - - -void -ConnectWindow::on_show() -{ - if (!_widgets_loaded) { - load_widgets(); - if (_attached) - set_connected_to(App::instance().engine()); - } - - Gtk::Dialog::on_show(); -} - - -void -ConnectWindow::load_widgets() -{ - _xml->get_widget("connect_icon", _icon); - _xml->get_widget("connect_progress_bar", _progress_bar); - _xml->get_widget("connect_progress_label", _progress_label); - _xml->get_widget("connect_server_radiobutton", _server_radio); - _xml->get_widget("connect_url_entry", _url_entry); - _xml->get_widget("connect_launch_radiobutton", _launch_radio); - _xml->get_widget("connect_port_spinbutton", _port_spinbutton); - _xml->get_widget("connect_internal_radiobutton", _internal_radio); - _xml->get_widget("connect_disconnect_button", _disconnect_button); - _xml->get_widget("connect_connect_button", _connect_button); - _xml->get_widget("connect_quit_button", _quit_button); - - _server_radio->signal_toggled().connect(sigc::mem_fun(this, &ConnectWindow::server_toggled)); - _launch_radio->signal_toggled().connect(sigc::mem_fun(this, &ConnectWindow::launch_toggled)); - _internal_radio->signal_clicked().connect(sigc::mem_fun(this, &ConnectWindow::internal_toggled)); - _disconnect_button->signal_clicked().connect(sigc::mem_fun(this, &ConnectWindow::disconnect)); - _connect_button->signal_clicked().connect(sigc::bind( - sigc::mem_fun(this, &ConnectWindow::connect), false)); - _quit_button->signal_clicked().connect(sigc::mem_fun(this, &ConnectWindow::quit)); - - _progress_bar->set_pulse_step(0.01); - _widgets_loaded = true; - - _engine_module = Ingen::Shared::load_module("ingen_engine"); - if (!_engine_module) - cerr << "Unable to load ingen_engine module, internal engine unavailable." << endl; - bool found = _engine_module->get_symbol("new_engine", (void*&)_new_engine); - if (!found) { - cerr << "Unable to find module entry point, internal engine unavailable." << endl; - _engine_module.reset(); - } - - server_toggled(); -} - - -void -ConnectWindow::on_hide() -{ - Gtk::Dialog::on_hide(); - if (!_attached) - Gtk::Main::quit(); -} - - -void -ConnectWindow::quit() -{ - if (_attached) { - Gtk::MessageDialog d(*this, "This will exit the GUI, but the engine will " - "remain running (if it is remote).\n\nAre you sure you want to quit?", - true, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_NONE, true); - d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); - d.add_button(Gtk::Stock::QUIT, Gtk::RESPONSE_CLOSE); - int ret = d.run(); - if (ret == Gtk::RESPONSE_CLOSE) - Gtk::Main::quit(); - } else { - Gtk::Main::quit(); - } -} - - -void -ConnectWindow::server_toggled() -{ - _url_entry->set_sensitive(true); - _port_spinbutton->set_sensitive(false); - _mode = CONNECT_REMOTE; -} - - -void -ConnectWindow::launch_toggled() -{ - _url_entry->set_sensitive(false); - _port_spinbutton->set_sensitive(true); - _mode = LAUNCH_REMOTE; -} - - -void -ConnectWindow::internal_toggled() -{ - _url_entry->set_sensitive(false); - _port_spinbutton->set_sensitive(false); - _mode = INTERNAL; -} - - -bool -ConnectWindow::gtk_callback() -{ - /* If I call this a "state machine" it's not ugly code any more */ - - // Timing stuff for repeated attach attempts - timeval now; - gettimeofday(&now, NULL); - static const timeval start = now; - static timeval last = now; - - // Show if attempted connection goes on for a noticeable amount of time - if (!is_visible()) { - const float ms_since_start = (now.tv_sec - start.tv_sec) * 1000.0f + - (now.tv_usec - start.tv_usec) * 0.001f; - if (ms_since_start > 500) { - present(); - set_connecting_widget_states(); - } - } - - /* Connecting to engine */ - if (_connect_stage == 0) { - - _attached = false; - - assert(App::instance().engine()); - assert(App::instance().client()); - - App::instance().client()->signal_response_ok.connect( - sigc::mem_fun(this, &ConnectWindow::on_response)); - - _ping_id = abs(rand()) / 2 * 2; // avoid -1 - App::instance().engine()->set_next_response_id(_ping_id); - App::instance().engine()->ping(); - - if (_widgets_loaded) { - _progress_label->set_text("Connecting to engine..."); - _progress_bar->set_pulse_step(0.01); - } - - ++_connect_stage; - - } else if (_connect_stage == 1) { - if (_attached || App::instance().client()->enabled()) { - App::instance().engine()->activate(); - ++_connect_stage; - } else { - const float ms_since_last = (now.tv_sec - last.tv_sec) * 1000.0f + - (now.tv_usec - last.tv_usec) * 0.001f; - if (ms_since_last > 1000) { - App::instance().engine()->set_next_response_id(_ping_id); - App::instance().engine()->ping(); - last = now; - } - } - } else if (_connect_stage == 2) { - App::instance().engine()->request_all_objects(); - if (_widgets_loaded) - _progress_label->set_text(string("Requesting root patch...")); - ++_connect_stage; - } else if (_connect_stage == 3) { - if (App::instance().store()->size() > 0) { - SharedPtr root = PtrCast(App::instance().store()->object("/")); - if (root) { - set_connected_to(App::instance().engine()); - App::instance().window_factory()->present_patch(root); - App::instance().engine()->load_plugins(); - if (_widgets_loaded) - _progress_label->set_text(string("Loading plugins...")); - ++_connect_stage; - } - } - } else if (_connect_stage == 4) { - App::instance().engine()->request_plugins(); - hide(); - if (_widgets_loaded) - _progress_label->set_text("Connected to engine"); - _connect_stage = 0; // set ourselves up for next time (if there is one) - return false; // deregister this callback - } - - if (_widgets_loaded) - _progress_bar->pulse(); - - if (_connect_stage == -1) { // we were cancelled - if (_widgets_loaded) { - _icon->set(Gtk::Stock::DISCONNECT, Gtk::ICON_SIZE_LARGE_TOOLBAR); - _progress_bar->set_fraction(0.0); - _connect_button->set_sensitive(true); - _disconnect_button->set_sensitive(false); - _disconnect_button->set_label("gtk-disconnect"); - _progress_label->set_text(string("Disconnected")); - } - return false; - } else { - return true; - } -} - - -} // namespace GUI -} // namespace Ingen diff --git a/src/libs/gui/ConnectWindow.hpp b/src/libs/gui/ConnectWindow.hpp deleted file mode 100644 index fb75d4b2..00000000 --- a/src/libs/gui/ConnectWindow.hpp +++ /dev/null @@ -1,105 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef CONNECT_WINDOW_H -#define CONNECT_WINDOW_H - -#include CONFIG_H_PATH - -#ifdef HAVE_SLV2 -#include -#endif - -#include -#include -#include -#include -#include "client/ThreadedSigClientInterface.hpp" -using Ingen::Client::SigClientInterface; - -namespace Ingen { class Engine; class QueuedEngineInterface; } - -namespace Ingen { -namespace GUI { - -class App; - - -/** The initially visible "Connect to engine" window. - * - * This handles actually connecting to the engine and making sure everything - * is ready before really launching the app (eg wait for the root patch). - * - * \ingroup GUI - */ -class ConnectWindow : public Gtk::Dialog -{ -public: - ConnectWindow(BaseObjectType* cobject, const Glib::RefPtr& xml); - - void set_connected_to(SharedPtr engine); - void start(Ingen::Shared::World* world); - void on_response(int32_t id) { _attached = true; } - -private: - enum Mode { CONNECT_REMOTE, LAUNCH_REMOTE, INTERNAL }; - - void server_toggled(); - void launch_toggled(); - void internal_toggled(); - - void disconnect(); - void connect(bool existing); - void quit(); - void on_show(); - void on_hide(); - - void load_widgets(); - void set_connecting_widget_states(); - - bool gtk_callback(); - - const Glib::RefPtr _xml; - - Mode _mode; - int32_t _ping_id; - bool _attached; - - bool _widgets_loaded; - int _connect_stage; - - SharedPtr _engine_module; - Ingen::Engine* (*_new_engine)(Ingen::Shared::World* world); - - Gtk::Image* _icon; - Gtk::ProgressBar* _progress_bar; - Gtk::Label* _progress_label; - Gtk::Entry* _url_entry; - Gtk::RadioButton* _server_radio; - Gtk::SpinButton* _port_spinbutton; - Gtk::RadioButton* _launch_radio; - Gtk::RadioButton* _internal_radio; - Gtk::Button* _disconnect_button; - Gtk::Button* _connect_button; - Gtk::Button* _quit_button; -}; - - -} // namespace GUI -} // namespace Ingen - -#endif // CONNECT_WINDOW_H diff --git a/src/libs/gui/Connection.hpp b/src/libs/gui/Connection.hpp deleted file mode 100644 index 55378891..00000000 --- a/src/libs/gui/Connection.hpp +++ /dev/null @@ -1,60 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef GUI_CONNECTION_H -#define GUI_CONNECTION_H - -#include -#include -#include -#include -#include "client/ConnectionModel.hpp" -using Ingen::Client::ConnectionModel; - -namespace Ingen { -namespace GUI { - - -/** A Connection in a Patch. - * - * \ingroup GUI - */ -class Connection : public FlowCanvas::Connection -{ -public: - Connection(boost::shared_ptr canvas, - boost::shared_ptr model, - boost::shared_ptr src, - boost::shared_ptr dst, - uint32_t color) - : FlowCanvas::Connection(canvas, src, dst, color) - , _connection_model(model) - {} - - virtual ~Connection() {} - - SharedPtr model() const { return _connection_model; } - -private: - SharedPtr _connection_model; -}; - - -} // namespace GUI -} // namespace Ingen - -#endif // GUI_CONNECTION_H diff --git a/src/libs/gui/ControlPanel.cpp b/src/libs/gui/ControlPanel.cpp deleted file mode 100644 index 7da07dbb..00000000 --- a/src/libs/gui/ControlPanel.cpp +++ /dev/null @@ -1,269 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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 alongCont - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "interface/EngineInterface.hpp" -#include "client/PatchModel.hpp" -#include "client/NodeModel.hpp" -#include "client/PortModel.hpp" -#include "client/PluginModel.hpp" -#include "App.hpp" -#include "ControlPanel.hpp" -#include "Controls.hpp" -#include "GladeFactory.hpp" - -using namespace std; - -namespace Ingen { -namespace GUI { - - -ControlPanel::ControlPanel(BaseObjectType* cobject, const Glib::RefPtr& xml) - : Gtk::HBox(cobject) - , _callback_enabled(true) -{ - xml->get_widget("control_panel_controls_box", _control_box); - xml->get_widget("control_panel_voice_controls_box", _voice_control_box); - xml->get_widget("control_panel_all_voices_radio", _all_voices_radio); - xml->get_widget("control_panel_specific_voice_radio", _specific_voice_radio); - xml->get_widget("control_panel_voice_spinbutton", _voice_spinbutton); - - _all_voices_radio->signal_toggled().connect( - sigc::mem_fun(this, &ControlPanel::all_voices_selected)); - - _specific_voice_radio->signal_toggled().connect( - sigc::mem_fun(this, &ControlPanel::specific_voice_selected)); - - show_all(); -} - - -ControlPanel::~ControlPanel() -{ - for (vector::iterator i = _controls.begin(); i != _controls.end(); ++i) - delete (*i); -} - - -void -ControlPanel::init(SharedPtr node, uint32_t poly) -{ - assert(node != NULL); - assert(poly > 0); - - if (node->polyphonic()) { - _voice_spinbutton->set_range(0, poly - 1); - _voice_control_box->show(); - } else { - _voice_control_box->hide(); - } - - for (NodeModel::Ports::const_iterator i = node->ports().begin(); i != node->ports().end(); ++i) { - add_port(*i); - } - - node->signal_property.connect(bind( - sigc::mem_fun(this, &ControlPanel::property_changed), false)); - - if (node->parent()) { - node->signal_property.connect(bind( - sigc::mem_fun(this, &ControlPanel::property_changed), true)); - } else { - cerr << "[ControlPanel] No parent, polyphonic controls disabled" << endl; - } - - _callback_enabled = true; -} - - -Control* -ControlPanel::find_port(const Path& path) const -{ - for (vector::const_iterator cg = _controls.begin(); cg != _controls.end(); ++cg) - if ((*cg)->port_model()->path() == path) - return (*cg); - - return NULL; -} - - -/** Add a control to the panel for the given port. - */ -void -ControlPanel::add_port(SharedPtr pm) -{ - assert(pm); - - // Already have port, don't add another - if (find_port(pm->path()) != NULL) - return; - - // Add port - if (pm->type().is_control() && pm->is_input()) { - Control* control = NULL; - - if (pm->is_toggle()) { - ToggleControl* tc; - Glib::RefPtr xml = GladeFactory::new_glade_reference("toggle_control"); - xml->get_widget_derived("toggle_control", tc); - control = tc; - } else { - SliderControl* sc; - Glib::RefPtr xml = GladeFactory::new_glade_reference("control_strip"); - xml->get_widget_derived("control_strip", sc); - control = sc; - } - - control->init(this, pm); - - if (_controls.size() > 0) - _control_box->pack_start(*Gtk::manage(new Gtk::HSeparator()), false, false, 4); - - _controls.push_back(control); - _control_box->pack_start(*control, false, false, 0); - - control->enable(); - control->show(); - } - - Gtk::Requisition controls_size; - _control_box->size_request(controls_size); - _ideal_size.first = controls_size.width; - _ideal_size.second = controls_size.height; - - Gtk::Requisition voice_size; - _voice_control_box->size_request(voice_size); - _ideal_size.first += voice_size.width; - _ideal_size.second += voice_size.height; - - //cerr << "Setting ideal size to " << _ideal_size.first - // << " x " << _ideal_size.second << endl; -} - - -/** Remove the control for the given port. - */ -void -ControlPanel::remove_port(const Path& path) -{ - bool was_first = false; - for (vector::iterator cg = _controls.begin(); cg != _controls.end(); ++cg) { - if ((*cg)->port_model()->path() == path) { - _control_box->remove(**cg); - if (cg == _controls.begin()) - was_first = true; - _controls.erase(cg); - break; - } - } -} - - -/** Rename the control for the given port. - */ -/* -void -ControlPanel::rename_port(const Path& old_path, const Path& new_path) -{ - for (vector::iterator cg = _controls.begin(); cg != _controls.end(); ++cg) { - if ((*cg)->port_model()->path() == old_path) { - (*cg)->set_name(new_path.name()); - return; - } - } -} -*/ - -#if 0 -/** Enable the control for the given port. - * - * Used when all connections to port are un-made. - */ -void -ControlPanel::enable_port(const Path& path) -{ - for (vector::iterator i = _controls.begin(); i != _controls.end(); ++i) { - if ((*i)->port_model()->path() == path) { - (*i)->enable(); - return; - } - } -} - - -/** Disable the control for the given port. - * - * Used when port is connected. - */ -void -ControlPanel::disable_port(const Path& path) -{ - for (vector::iterator i = _controls.begin(); i != _controls.end(); ++i) { - if ((*i)->port_model()->path() == path) { - (*i)->disable(); - return; - } - } -} -#endif - -/** Callback for Controls to notify this of a change. - */ -void -ControlPanel::value_changed(SharedPtr port, float val) -{ - if (_callback_enabled) { - if (_all_voices_radio->get_active()) { - App::instance().engine()->set_port_value(port->path(), Atom(val)); - port->value(val); - } else { - int voice = _voice_spinbutton->get_value_as_int() - 1; - App::instance().engine()->set_voice_value(port->path(), voice, Atom(val)); - port->value(val); - } - } -} - -void -ControlPanel::all_voices_selected() -{ - _voice_spinbutton->property_sensitive() = false; -} - - -void -ControlPanel::specific_voice_selected() -{ - _voice_spinbutton->property_sensitive() = true; -} - - -void -ControlPanel::property_changed(const std::string& predicate, const Raul::Atom& value, bool parent) -{ - if (!parent && predicate == "ingen:polyphonic" && value.type() == Atom::BOOL) { - if (value.get_bool()) - _voice_control_box->show(); - else - _voice_control_box->hide(); - } else if (parent && predicate == "ingen:polyphony" && value.type() == Atom::INT) { - _voice_spinbutton->set_range(0, value.get_int32() - 1); - } -} - - -} // namespace GUI -} // namespace Ingen diff --git a/src/libs/gui/ControlPanel.hpp b/src/libs/gui/ControlPanel.hpp deleted file mode 100644 index 8af5a728..00000000 --- a/src/libs/gui/ControlPanel.hpp +++ /dev/null @@ -1,91 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef CONTROLPANEL_H -#define CONTROLPANEL_H - -#include -#include -#include -#include // for pair<> -#include -#include -#include -#include -#include -#include "Controls.hpp" - -namespace Ingen { namespace Client { - class PortModel; - class NodeModel; -} } -using namespace Ingen::Client; - -namespace Ingen { -namespace GUI { - - -/** A group of controls for a node (or patch). - * - * Used by both NodeControlWindow and the main window (for patch controls). - * - * \ingroup GUI - */ -class ControlPanel : public Gtk::HBox { -public: - ControlPanel(BaseObjectType* cobject, const Glib::RefPtr& glade_xml); - virtual ~ControlPanel(); - - void init(SharedPtr node, uint32_t poly); - - Control* find_port(const Path& path) const; - - void add_port(SharedPtr port); - void remove_port(const Path& path); - - void enable_port(const Path& path); - void disable_port(const Path& path); - - size_t num_controls() const { return _controls.size(); } - std::pair ideal_size() const { return _ideal_size; } - - // Callback for Control - void value_changed(SharedPtr port_path, float val); - -private: - void all_voices_selected(); - void specific_voice_selected(); - - void property_changed(const std::string& predicate, const Raul::Atom& value, bool parent); - - bool _callback_enabled; - - std::pair _ideal_size; - - std::vector _controls; - Gtk::VBox* _control_box; - Gtk::Box* _voice_control_box; - Gtk::RadioButton* _all_voices_radio; - Gtk::RadioButton* _specific_voice_radio; - Gtk::SpinButton* _voice_spinbutton; -}; - - -} // namespace GUI -} // namespace Ingen - -#endif // CONTROLPANEL_H diff --git a/src/libs/gui/Controls.cpp b/src/libs/gui/Controls.cpp deleted file mode 100644 index cc5383a8..00000000 --- a/src/libs/gui/Controls.cpp +++ /dev/null @@ -1,467 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include CONFIG_H_PATH - -#include -#include -#include -#include "interface/EngineInterface.hpp" -#include "client/PluginModel.hpp" -#include "client/NodeModel.hpp" -#include "client/PortModel.hpp" -#include "Controls.hpp" -#include "ControlPanel.hpp" -#include "PortPropertiesWindow.hpp" -#include "GladeFactory.hpp" -#include "App.hpp" - -using namespace std; -using namespace Ingen::Client; - -namespace Ingen { -namespace GUI { - - -// ////////////////////// Control ///////////////////////////////// // - -Control::Control(BaseObjectType* cobject, const Glib::RefPtr& glade_xml) - : Gtk::VBox(cobject) - , _control_panel(NULL) - , _enable_signal(false) -{ - Glib::RefPtr menu_xml = GladeFactory::new_glade_reference("port_control_menu"); - menu_xml->get_widget("port_control_menu", _menu); - menu_xml->get_widget("port_control_menu_properties", _menu_properties); - - _menu_properties->signal_activate().connect( - sigc::mem_fun(this, &SliderControl::menu_properties)); -} - - -Control::~Control() -{ - _enable_signal = false; - _control_connection.disconnect(); - _port_model.reset(); -} - - -void -Control::init(ControlPanel* panel, SharedPtr pm) -{ - _control_panel = panel; - _port_model = pm, - - assert(_port_model); - assert(panel); - - _control_connection = pm->signal_value_changed.connect(sigc::mem_fun(this, &Control::set_value)); -} - - -void -Control::menu_properties() -{ - Glib::RefPtr xml = GladeFactory::new_glade_reference(); - - PortPropertiesWindow* window; - xml->get_widget_derived("port_properties_win", window); - window->present(_port_model); -} - - -// ////////////////// SliderControl ////////////////////// // - - -SliderControl::SliderControl(BaseObjectType* cobject, const Glib::RefPtr& xml) - : Control(cobject, xml) - , _enabled(true) -{ - xml->get_widget("control_strip_name_label", _name_label); - xml->get_widget("control_strip_slider", _slider); - xml->get_widget("control_strip_spinner", _value_spinner); -} - - -void -SliderControl::init(ControlPanel* panel, SharedPtr pm) -{ - _enable_signal = false; - _enabled = true; - - Control::init(panel, pm); - - assert(_name_label); - assert(_slider); - - set_name(pm->path().name()); - - _slider->set_draw_value(false); - - signal_button_press_event().connect(sigc::mem_fun(*this, &SliderControl::clicked)); - _slider->signal_button_press_event().connect(sigc::mem_fun(*this, &SliderControl::clicked)); - - _slider->signal_event().connect( - sigc::mem_fun(*this, &SliderControl::slider_pressed)); - - _slider->signal_value_changed().connect( - sigc::mem_fun(*this, &SliderControl::update_value_from_slider)); - - _value_spinner->signal_value_changed().connect( - sigc::mem_fun(*this, &SliderControl::update_value_from_spinner)); - - float min = 0.0f, max = 1.0f; - - boost::shared_ptr parent = PtrCast(_port_model->parent()); - if (parent) - parent->port_value_range(_port_model, min, max); - - if (pm->is_integer() || pm->is_toggle()) { - _slider->set_increments(1, 10); - _value_spinner->set_digits(0); - } else { - _slider->set_increments(0, 0); - } - - pm->signal_variable.connect(sigc::mem_fun(this, &SliderControl::port_variable_change)); - - _slider->set_range(std::min(min, pm->value().get_float()), std::max(max, pm->value().get_float())); - //_value_spinner->set_range(min, max); - - set_value(pm->value()); - - _enable_signal = true; - - show_all(); -} - - -bool -SliderControl::clicked(GdkEventButton* ev) -{ - if (ev->button == 3) { - _menu->popup(ev->button, ev->time); - return true; - } else { - return false; - } -} - - -void -SliderControl::set_value(const Atom& atom) -{ - float val = atom.get_float(); - - if (_port_model->is_integer()) - val = lrintf(val); - - _enable_signal = false; - if (_enabled) { - if (_slider->get_value() != val) { - const Gtk::Adjustment* range = _slider->get_adjustment(); - const float lower = range->get_lower(); - const float upper = range->get_upper(); - if (val < lower || val > upper) - set_range(min(lower, val), max(lower, val)); - _slider->set_value(val); - } - if (_value_spinner->get_value() != val) - _value_spinner->set_value(val); - } - _enable_signal = true; -} - - -void -SliderControl::port_variable_change(const string& key, const Atom& value) -{ - if ( (key == "ingen:minimum") && value.type() == Atom::FLOAT) - set_range(value.get_float(), _slider->get_adjustment()->get_upper()); - else if ( (key == "ingen:maximum") && value.type() == Atom::FLOAT) - set_range(_slider->get_adjustment()->get_lower(), value.get_float()); -} - - -void -SliderControl::set_range(float min, float max) -{ - _slider->set_range(min, max); - //_value_spinner->set_range(min, max); -} - - -void -SliderControl::set_name(const string& name) -{ - string name_label = ""; - name_label += name + ""; - _name_label->set_markup(name_label); -} - - -void -SliderControl::enable() -{ - _slider->property_sensitive() = true; - //_min_spinner->property_sensitive() = true; - //_max_spinner->property_sensitive() = true; - _value_spinner->property_sensitive() = true; - _name_label->property_sensitive() = true; -} - - -void -SliderControl::disable() -{ - _slider->property_sensitive() = false; - //_min_spinner->property_sensitive() = false; - //_max_spinner->property_sensitive() = false; - _value_spinner->property_sensitive() = false; - _name_label->property_sensitive() = false; -} - - -void -SliderControl::update_value_from_slider() -{ - if (_enable_signal) { - float value = _slider->get_value(); - bool change = true; - - _enable_signal = false; - - if (_port_model->is_integer()) { - value = lrintf(value); - if (value == lrintf(_port_model->value().get_float())) - change = false; - } - - if (change) { - _value_spinner->set_value(value); - _control_panel->value_changed(_port_model, value); - } - - _enable_signal = true; - } -} - - -void -SliderControl::update_value_from_spinner() -{ - if (_enable_signal) { - _enable_signal = false; - const float value = _value_spinner->get_value(); - - set_value(value); - - _control_panel->value_changed(_port_model, value); - - //m_port_model->value(value); - _enable_signal = true; - } -} - - -/** Callback for when slider is grabbed so we can ignore set_control - * events for this port (and avoid nasty feedback issues). - */ -bool -SliderControl::slider_pressed(GdkEvent* ev) -{ - //cerr << "Pressed: " << ev->type << endl; - if (ev->type == GDK_BUTTON_PRESS) { - _enabled = false; - //GtkClientInterface::instance()->set_ignore_port(_port_model->path()); - } else if (ev->type == GDK_BUTTON_RELEASE) { - _enabled = true; - //GtkClientInterface::instance()->clear_ignore_port(); - } - - return false; -} - - -// ///////////// IntegerControl ////////////// // - -#if 0 -IntegerControl::IntegerControl(ControlPanel* panel, SharedPtr pm) -: Control(panel, pm), - _enable_signal(false), - _alignment(0.5, 0.5, 0.0, 0.0), - _name_label(pm->path().name()), - _spinner(1.0, 0) -{ - set_name(pm->path().name()); - - _spinner.set_range(-99999, 99999); - _spinner.set_value(_port_model->value()); - _spinner.signal_value_changed().connect( - sigc::mem_fun(*this, &IntegerControl::update_value)); - _spinner.set_increments(1, 10); - - _alignment.add(_spinner); - pack_start(_name_label); - pack_start(_alignment); - - _enable_signal = true; - - show_all(); -} - - -void -IntegerControl::set_name(const string& name) -{ - string name_label = ""; - name_label += name + ""; - _name_label->set_markup(name_label); -} - - -void -IntegerControl::set_value(float val) -{ - //cerr << "[IntegerControl] Setting value to " << val << endl; - _enable_signal = false; - _spinner.set_value(val); - _enable_signal = true; -} - - -void -IntegerControl::enable() -{ - _spinner.property_sensitive() = true; - _name_label->property_sensitive() = true; -} - - -void -IntegerControl::disable() -{ - _spinner.property_sensitive() = false; - _name_label->property_sensitive() = false; -} - - -void -IntegerControl::update_value() -{ - if (_enable_signal) { - float value = _spinner.get_value(); - _control_panel->value_changed(_port_model, value); - //m_port_model->value(value); - } -} -#endif - - -// ///////////// ToggleControl ////////////// // - - -ToggleControl::ToggleControl(BaseObjectType* cobject, const Glib::RefPtr& xml) - : Control(cobject, xml) -{ - xml->get_widget("toggle_control_name_label", _name_label); - xml->get_widget("toggle_control_check", _checkbutton); -} - - -void -ToggleControl::init(ControlPanel* panel, SharedPtr pm) -{ - _enable_signal = false; - - Control::init(panel, pm); - - assert(_name_label); - assert(_checkbutton); - - set_name(pm->path().name()); - - _checkbutton->signal_toggled().connect(sigc::mem_fun(*this, &ToggleControl::toggled)); - set_value(pm->value()); - - _enable_signal = true; - show_all(); -} - - -void -ToggleControl::set_name(const string& name) -{ - string name_label = ""; - name_label += name + ""; - _name_label->set_markup(name_label); -} - - -void -ToggleControl::set_value(const Atom& val) -{ - bool enable = false; - switch (val.type()) { - case Atom::FLOAT: - enable = (val.get_float() != 0.0f); - break; - case Atom::INT: - enable = (val.get_int32() != 0); - break; - case Atom::BOOL: - enable = (val.get_bool()); - default: - cerr << "Unsupported value type for toggle control" << endl; - } - - _enable_signal = false; - _checkbutton->set_active(enable); - _enable_signal = true; -} - - -void -ToggleControl::enable() -{ - _checkbutton->property_sensitive() = true; - _name_label->property_sensitive() = true; -} - - -void -ToggleControl::disable() -{ - _checkbutton->property_sensitive() = false; - _name_label->property_sensitive() = false; -} - - -void -ToggleControl::toggled() -{ - if (_enable_signal) { - float value = _checkbutton->get_active() ? 1.0f : 0.0f; - _control_panel->value_changed(_port_model, value); - //m_port_model->value(value); - } -} - - -} // namespace GUI -} // namespace Ingen diff --git a/src/libs/gui/Controls.hpp b/src/libs/gui/Controls.hpp deleted file mode 100644 index 8a9c4064..00000000 --- a/src/libs/gui/Controls.hpp +++ /dev/null @@ -1,164 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef CONTROLGROUPS_H -#define CONTROLGROUPS_H - -#include -#include -#include -#include -#include "client/PortModel.hpp" -#include - -namespace Ingen { namespace Client { class PortModel; } } -using namespace Ingen::Client; - -namespace Ingen { -namespace GUI { - -class ControlPanel; - - -/** A group of controls (for a single Port) in a ControlPanel. - * - * \ingroup GUI - */ -class Control : public Gtk::VBox -{ -public: - Control(BaseObjectType* cobject, const Glib::RefPtr& glade_xml); - virtual ~Control(); - - virtual void init(ControlPanel* panel, SharedPtr pm); - - virtual void enable() = 0; - virtual void disable() = 0; - - inline const SharedPtr port_model() const { return _port_model; } - -protected: - virtual void set_value(const Atom& value) = 0; - virtual void set_range(float min, float max) {} - - void menu_properties(); - - ControlPanel* _control_panel; - SharedPtr _port_model; - sigc::connection _control_connection; - bool _enable_signal; - - Gtk::Menu* _menu; - Gtk::MenuItem* _menu_properties; -}; - - -/** A slider for a continuous control. - * - * \ingroup GUI - */ -class SliderControl : public Control -{ -public: - SliderControl(BaseObjectType* cobject, const Glib::RefPtr& glade_xml); - void init(ControlPanel* panel, SharedPtr pm); - - void enable(); - void disable(); - - void set_min(float val); - void set_max(float val); - -private: - void set_name(const string& name); - void set_value(const Atom& value); - void set_range(float min, float max); - - void port_variable_change(const string& key, const Raul::Atom& value); - - void update_range(); - void update_value_from_slider(); - void update_value_from_spinner(); - - bool slider_pressed(GdkEvent* ev); - bool clicked(GdkEventButton* ev); - - bool _enabled; - - Gtk::Label* _name_label; - Gtk::SpinButton* _value_spinner; - Gtk::HScale* _slider; -}; - - -#if 0 - -/** A spinbutton for integer controls. - * - * \ingroup GUI - */ -class IntegerControl : public Control -{ -public: - IntegerControl(ControlPanel* panel, SharedPtr pm); - - void enable(); - void disable(); - -private: - void set_name(const string& name); - void set_value(float val); - - void update_value(); - - bool _enable_signal; - Gtk::Alignment _alignment; - Gtk::Label _name_label; - Gtk::SpinButton _spinner; -}; -#endif - - -/** A radio button for toggle controls. - * - * \ingroup GUI - */ -class ToggleControl : public Control -{ -public: - ToggleControl(BaseObjectType* cobject, const Glib::RefPtr& xml); - - void init(ControlPanel* panel, SharedPtr pm); - - void enable(); - void disable(); - -private: - void set_name(const string& name); - void set_value(const Atom& value); - - void toggled(); - - Gtk::Label* _name_label; - Gtk::CheckButton* _checkbutton; -}; - - -} // namespace GUI -} // namespace Ingen - -#endif // CONTROLGROUPS_H diff --git a/src/libs/gui/GladeFactory.cpp b/src/libs/gui/GladeFactory.cpp deleted file mode 100644 index 2b4d3ec9..00000000 --- a/src/libs/gui/GladeFactory.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "GladeFactory.hpp" -#include -#include - -using namespace std; - -namespace Ingen { -namespace GUI { - - -Glib::ustring GladeFactory::glade_filename = ""; - - -void -GladeFactory::find_glade_file() -{ - char* env_path = getenv("INGEN_GLADE_PATH"); - if (env_path) - glade_filename = env_path; - else - glade_filename = "./ingen_gui.glade"; - - ifstream fs(glade_filename.c_str()); - if (fs.fail()) { // didn't find it, check INGEN_DATA_DIR - fs.clear(); - glade_filename = INGEN_DATA_DIR; - glade_filename += "/ingen_gui.glade"; - - fs.open(glade_filename.c_str()); - if (fs.fail()) { - cerr << "[GladeFactory] Unable to find ingen_gui.glade in current directory or " << INGEN_DATA_DIR << "." << endl; - throw; - } - fs.close(); - } - cerr << "[GladeFactory] Loading widgets from " << glade_filename.c_str() << endl; -} - - -Glib::RefPtr -GladeFactory::new_glade_reference(const string& toplevel_widget) -{ - if (glade_filename == "") - find_glade_file(); - - try { - if (toplevel_widget == "") - return Gnome::Glade::Xml::create(glade_filename); - else - return Gnome::Glade::Xml::create(glade_filename, toplevel_widget.c_str()); - } catch (const Gnome::Glade::XmlError& ex) { - cerr << ex.what() << endl; - throw ex; - } -} - - -} // namespace GUI -} // namespace Ingen diff --git a/src/libs/gui/GladeFactory.hpp b/src/libs/gui/GladeFactory.hpp deleted file mode 100644 index 6da2da5f..00000000 --- a/src/libs/gui/GladeFactory.hpp +++ /dev/null @@ -1,47 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef GLADEFACTORY_H -#define GLADEFACTORY_H - -#include -#include - -namespace Ingen { -namespace GUI { - - -/** Creates glade references, so various objects can create widgets. - * Purely static. - * - * \ingroup GUI - */ -class GladeFactory { -public: - static Glib::RefPtr - new_glade_reference(const std::string& toplevel_widget = ""); - -private: - static void find_glade_file(); - static Glib::ustring glade_filename; -}; - - -} // namespace GUI -} // namespace Ingen - -#endif // GLADEFACTORY_H diff --git a/src/libs/gui/LoadPatchWindow.cpp b/src/libs/gui/LoadPatchWindow.cpp deleted file mode 100644 index 70d3ec5b..00000000 --- a/src/libs/gui/LoadPatchWindow.cpp +++ /dev/null @@ -1,153 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include "LoadPatchWindow.hpp" -#include "interface/EngineInterface.hpp" -#include "client/PatchModel.hpp" -#include "App.hpp" -#include "Configuration.hpp" -#include "ThreadedLoader.hpp" - -using namespace Ingen::Serialisation; -using boost::optional; -using namespace std; - -namespace Ingen { -namespace GUI { - - -LoadPatchWindow::LoadPatchWindow(BaseObjectType* cobject, const Glib::RefPtr& xml) -: Gtk::FileChooserDialog(cobject), - _replace(true) -{ - xml->get_widget("load_patch_poly_from_current_radio", _poly_from_current_radio); - xml->get_widget("load_patch_poly_from_file_radio", _poly_from_file_radio); - xml->get_widget("load_patch_poly_from_user_radio", _poly_from_user_radio); - xml->get_widget("load_patch_poly_spinbutton", _poly_spinbutton); - xml->get_widget("load_patch_ok_button", _ok_button); - xml->get_widget("load_patch_cancel_button", _cancel_button); - - _poly_from_current_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadPatchWindow::poly_from_file_selected)); - _poly_from_file_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadPatchWindow::poly_from_file_selected)); - _poly_from_user_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadPatchWindow::poly_from_user_selected)); - _ok_button->signal_clicked().connect(sigc::mem_fun(this, &LoadPatchWindow::ok_clicked)); - _cancel_button->signal_clicked().connect(sigc::mem_fun(this, &LoadPatchWindow::cancel_clicked)); - - _poly_from_current_radio->set_active(true); - - Gtk::FileFilter filt; - filt.add_pattern("*.om"); - filt.set_name("Om patch files (XML, DEPRECATED) (*.om)"); - filt.add_pattern("*.ingen.ttl"); - filt.set_name("Ingen patch files (RDF, *.ingen.ttl)"); - set_filter(filt); - - // Add global examples directory to "shortcut folders" (bookmarks) - string examples_dir = INGEN_DATA_DIR; - examples_dir.append("/patches"); - DIR* d = opendir(examples_dir.c_str()); - if (d != NULL) - add_shortcut_folder(examples_dir); -} - - -void -LoadPatchWindow::present(SharedPtr patch, GraphObject::Variables data) -{ - set_patch(patch); - _initial_data = data; - Gtk::Window::present(); -} - - -/** Sets the patch controller for this window and initializes everything. - * - * This function MUST be called before using the window in any way! - */ -void -LoadPatchWindow::set_patch(SharedPtr patch) -{ - _patch = patch; -} - - -void -LoadPatchWindow::on_show() -{ - if (App::instance().configuration()->patch_folder().length() > 0) - set_current_folder(App::instance().configuration()->patch_folder()); - Gtk::FileChooserDialog::on_show(); -} - - -///// Event Handlers ////// - - -void -LoadPatchWindow::poly_from_file_selected() -{ - _poly_spinbutton->property_sensitive() = false; -} - - -void -LoadPatchWindow::poly_from_user_selected() -{ - _poly_spinbutton->property_sensitive() = true; -} - - -void -LoadPatchWindow::ok_clicked() -{ - if (!_patch) - return; - - // If unset load_patch will load value - optional parent; - optional symbol; - - if (_poly_from_user_radio->get_active()) - _initial_data.insert(make_pair("ingen:polyphony", _poly_spinbutton->get_value_as_int())); - - if (_replace) - App::instance().engine()->clear_patch(_patch->path()); - - //if (_patch->path() != "/") - // parent = _patch->path().parent(); - parent = _patch->path(); - - _patch.reset(); - hide(); - - App::instance().loader()->load_patch(true, get_uri(), "/", - _initial_data, parent, symbol); -} - - -void -LoadPatchWindow::cancel_clicked() -{ - hide(); -} - - -} // namespace GUI -} // namespace Ingen diff --git a/src/libs/gui/LoadPatchWindow.hpp b/src/libs/gui/LoadPatchWindow.hpp deleted file mode 100644 index 4f3521e5..00000000 --- a/src/libs/gui/LoadPatchWindow.hpp +++ /dev/null @@ -1,82 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef LOADPATCHWINDOW_H -#define LOADPATCHWINDOW_H - -#include -#include -#include -#include "interface/GraphObject.hpp" -#include "client/PluginModel.hpp" -#include "client/PatchModel.hpp" -using Ingen::Client::PatchModel; -using namespace Ingen::Shared; - -namespace Ingen { -namespace GUI { - - -/** 'Load Patch' window. - * - * Loaded by glade as a derived object. Used for both "Import" and "Load" - * (e.g. Replace, clear-then-import) operations (the radio button state - * should be changed with the provided methods before presenting). - * - * This is not for loading subpatches. See @a LoadSubpatchWindow for that. - * - * \ingroup GUI - */ -class LoadPatchWindow : public Gtk::FileChooserDialog -{ -public: - LoadPatchWindow(BaseObjectType* cobject, const Glib::RefPtr& xml); - - void set_patch(SharedPtr patch); - - void set_replace() { _replace = true; } - void set_merge() { _replace = false; } - - void present(SharedPtr patch, GraphObject::Variables data); - -protected: - void on_show(); - -private: - void poly_from_file_selected(); - void poly_from_user_selected(); - void ok_clicked(); - void cancel_clicked(); - - GraphObject::Variables _initial_data; - - SharedPtr _patch; - bool _replace; - - Gtk::RadioButton* _poly_from_current_radio; - Gtk::RadioButton* _poly_from_file_radio; - Gtk::RadioButton* _poly_from_user_radio; - Gtk::SpinButton* _poly_spinbutton; - Gtk::Button* _ok_button; - Gtk::Button* _cancel_button; -}; - - -} // namespace GUI -} // namespace Ingen - -#endif // LOADPATCHWINDOW_H diff --git a/src/libs/gui/LoadPluginWindow.cpp b/src/libs/gui/LoadPluginWindow.cpp deleted file mode 100644 index 28815ba9..00000000 --- a/src/libs/gui/LoadPluginWindow.cpp +++ /dev/null @@ -1,464 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include "interface/EngineInterface.hpp" -#include "client/NodeModel.hpp" -#include "client/PatchModel.hpp" -#include "client/ClientStore.hpp" -#include "App.hpp" -#include "LoadPluginWindow.hpp" -#include "PatchWindow.hpp" -#include "PatchView.hpp" -#include "PatchCanvas.hpp" - -using namespace std; - - -namespace Ingen { -namespace GUI { - -LoadPluginWindow::LoadPluginWindow(BaseObjectType* cobject, const Glib::RefPtr& xml) - : Gtk::Window(cobject) - , _plugin_name_offset(0) - , _has_shown(false) - , _refresh_list(true) -{ - xml->get_widget("load_plugin_plugins_treeview", _plugins_treeview); - xml->get_widget("load_plugin_polyphonic_checkbutton", _polyphonic_checkbutton); - xml->get_widget("load_plugin_name_entry", _node_name_entry); - xml->get_widget("load_plugin_clear_button", _clear_button); - xml->get_widget("load_plugin_add_button", _add_button); - //xml->get_widget("load_plugin_close_button", _close_button); - //xml->get_widget("load_plugin_ok_button", _add_button); - - xml->get_widget("load_plugin_filter_combo", _filter_combo); - xml->get_widget("load_plugin_search_entry", _search_entry); - - // Set up the plugins list - _plugins_liststore = Gtk::ListStore::create(_plugins_columns); - _plugins_treeview->set_model(_plugins_liststore); - _plugins_treeview->append_column("", _plugins_columns._col_icon); - _plugins_treeview->append_column("Name", _plugins_columns._col_name); - _plugins_treeview->append_column("Type", _plugins_columns._col_type); - _plugins_treeview->append_column("URI", _plugins_columns._col_uri); - //m_plugins_treeview->append_column("Library", _plugins_columns._col_library); - //m_plugins_treeview->append_column("Label", _plugins_columns._col_label); - - // This could be nicer.. store the TreeViewColumns locally maybe? - _plugins_treeview->get_column(1)->set_sort_column(_plugins_columns._col_name); - _plugins_treeview->get_column(2)->set_sort_column(_plugins_columns._col_type); - _plugins_treeview->get_column(3)->set_sort_column(_plugins_columns._col_uri); - //m_plugins_treeview->get_column(3)->set_sort_column(_plugins_columns._col_library); - //m_plugins_treeview->get_column(4)->set_sort_column(_plugins_columns._col_label); - for (int i=0; i < 3; ++i) - _plugins_treeview->get_column(i)->set_resizable(true); - - _plugins_liststore->set_default_sort_func(sigc::mem_fun(this, &LoadPluginWindow::plugin_compare)); - - // Set up the search criteria combobox - _criteria_liststore = Gtk::ListStore::create(_criteria_columns); - _filter_combo->set_model(_criteria_liststore); - Gtk::TreeModel::iterator iter = _criteria_liststore->append(); - Gtk::TreeModel::Row row = *iter; - row[_criteria_columns._col_label] = "Name contains"; - row[_criteria_columns._col_criteria] = CriteriaColumns::NAME; - _filter_combo->set_active(iter); - iter = _criteria_liststore->append(); row = *iter; - row[_criteria_columns._col_label] = "Type contains"; - row[_criteria_columns._col_criteria] = CriteriaColumns::TYPE; - iter = _criteria_liststore->append(); row = *iter; - row[_criteria_columns._col_label] = "URI contains"; - row[_criteria_columns._col_criteria] = CriteriaColumns::URI; - /*iter = _criteria_liststore->append(); row = *iter; - row[_criteria_columns._col_label] = "Library contains: "; - row[_criteria_columns._col_criteria] = CriteriaColumns::LIBRARY; - iter = _criteria_liststore->append(); row = *iter; - row[_criteria_columns._col_label] = "Label contains: "; - row[_criteria_columns._col_criteria] = CriteriaColumns::LABEL;*/ - - _clear_button->signal_clicked().connect( sigc::mem_fun(this, &LoadPluginWindow::clear_clicked)); - _add_button->signal_clicked().connect( sigc::mem_fun(this, &LoadPluginWindow::add_clicked)); - //m_close_button->signal_clicked().connect( sigc::mem_fun(this, &LoadPluginWindow::close_clicked)); - //m_add_button->signal_clicked().connect( sigc::mem_fun(this, &LoadPluginWindow::ok_clicked)); - _plugins_treeview->signal_row_activated().connect(sigc::mem_fun(this, &LoadPluginWindow::plugin_activated)); - _search_entry->signal_activate().connect( sigc::mem_fun(this, &LoadPluginWindow::add_clicked)); - _search_entry->signal_changed().connect( sigc::mem_fun(this, &LoadPluginWindow::filter_changed)); - _node_name_entry->signal_changed().connect( sigc::mem_fun(this, &LoadPluginWindow::name_changed)); - - _selection = _plugins_treeview->get_selection(); - _selection->signal_changed().connect(sigc::mem_fun(this, &LoadPluginWindow::plugin_selection_changed)); - - //m_add_button->grab_default(); -} - - -void -LoadPluginWindow::present(SharedPtr patch, GraphObject::Variables data) -{ - set_patch(patch); - _initial_data = data; - Gtk::Window::present(); -} - - -/** Called every time the user types into the name input box. - * Used to display warning messages, and enable/disable the OK button. - */ -void -LoadPluginWindow::name_changed() -{ - string name = _node_name_entry->get_text(); - if (!Path::is_valid_name(name)) { - //m_message_label->set_text("Name contains invalid characters."); - _add_button->property_sensitive() = false; - } else if (App::instance().store()->find_child(_patch, name)) { - //m_message_label->set_text("An object already exists with that name."); - _add_button->property_sensitive() = false; - } else if (name.length() == 0) { - //m_message_label->set_text(""); - _add_button->property_sensitive() = false; - } else { - //m_message_label->set_text(""); - _add_button->property_sensitive() = true; - } -} - - -/** Sets the patch controller for this window and initializes everything. - * - * This function MUST be called before using the window in any way! - */ -void -LoadPluginWindow::set_patch(SharedPtr patch) -{ - if (_patch) { - _patch = patch; - plugin_selection_changed(); - } else { - _patch = patch; - } - - /*if (patch->poly() <= 1) - _polyphonic_checkbutton->property_sensitive() = false; - else - _polyphonic_checkbutton->property_sensitive() = true;*/ -} - - -/** Populates the plugin list on the first show. - * - * This is done here instead of construction time as the list population is - * really expensive and bogs down creation of a patch. This is especially - * important when many patch notifications are sent at one time from the - * engine. - */ -void -LoadPluginWindow::on_show() -{ - if (!_has_shown) { - App::instance().store()->signal_new_plugin.connect( - sigc::mem_fun(this, &LoadPluginWindow::add_plugin)); - _has_shown = true; - } - - if (_refresh_list) { - set_plugins(App::instance().store()->plugins()); - _refresh_list = false; - } - - Gtk::Window::on_show(); -} - - -int -LoadPluginWindow::plugin_compare(const Gtk::TreeModel::iterator& a_i, - const Gtk::TreeModel::iterator& b_i) -{ - SharedPtr a = a_i->get_value(_plugins_columns._col_plugin_model); - SharedPtr b = b_i->get_value(_plugins_columns._col_plugin_model); - - // FIXME: haaack - if (!a && !b) - return 0; - else if (!a) - return 1; - else if (!b) - return -1; - - if (a->type() == b->type()) - return strcmp(a->name().c_str(), b->name().c_str()); - else - return ((int)a->type() < (int)b->type()) ? -1 : 1; -} - - -void -LoadPluginWindow::set_plugins(SharedPtr m) -{ - _plugins_liststore->clear(); - - for (ClientStore::Plugins::const_iterator i = m->begin(); i != m->end(); ++i) { - SharedPtr plugin = (*i).second; - - Gtk::TreeModel::iterator iter = _plugins_liststore->append(); - Gtk::TreeModel::Row row = *iter; - - row[_plugins_columns._col_icon] = App::instance().icon_from_path(plugin->icon_path(), 20); - row[_plugins_columns._col_name] = plugin->name(); - //row[_plugins_columns._col_label] = plugin->plug_label(); - if (!strcmp(plugin->type_uri(), "ingen:Internal")) - row[_plugins_columns._col_type] = "Internal"; - else if (!strcmp(plugin->type_uri(), "ingen:LV2")) - row[_plugins_columns._col_type] = "LV2"; - else if (!strcmp(plugin->type_uri(), "ingen:LADSPA")) - row[_plugins_columns._col_type] = "LADSPA"; - else - row[_plugins_columns._col_type] = plugin->type_uri(); - row[_plugins_columns._col_uri] = plugin->uri(); - row[_plugins_columns._col_label] = plugin->name(); - //row[_plugins_columns._col_library] = plugin->lib_name(); - row[_plugins_columns._col_plugin_model] = plugin; - } - - _plugins_liststore->set_sort_column(Gtk::TreeSortable::DEFAULT_SORT_COLUMN_ID, Gtk::SORT_ASCENDING); - - _plugins_treeview->columns_autosize(); -} - - -void -LoadPluginWindow::new_plugin(SharedPtr pm) -{ - if (is_visible()) - add_plugin(pm); - else - _refresh_list = true; -} - - -void -LoadPluginWindow::add_plugin(SharedPtr plugin) -{ - Gtk::TreeModel::iterator iter = _plugins_liststore->append(); - Gtk::TreeModel::Row row = *iter; - - row[_plugins_columns._col_name] = plugin->name(); - //row[_plugins_columns._col_label] = plugin->plug_label(); - row[_plugins_columns._col_type] = plugin->type_uri(); - row[_plugins_columns._col_uri] = plugin->uri(); - row[_plugins_columns._col_label] = plugin->name(); - //row[_plugins_columns._col_library] = plugin->lib_name(); - row[_plugins_columns._col_plugin_model] = plugin; -} - - - -///// Event Handlers ////// - - -void -LoadPluginWindow::plugin_activated(const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* col) -{ - add_clicked(); -} - - -void -LoadPluginWindow::plugin_selection_changed() -{ - Gtk::TreeModel::iterator iter = _selection->get_selected(); - if (iter) { - Gtk::TreeModel::Row row = *iter; - boost::shared_ptr p = row.get_value(_plugins_columns._col_plugin_model); - _plugin_name_offset = App::instance().store()->child_name_offset( - _patch->path(), p->default_node_symbol()); - _node_name_entry->set_text(generate_module_name(_plugin_name_offset)); - } else { - _plugin_name_offset = 0; - _node_name_entry->set_text(""); - } -} - - -/** Generate an automatic name for this Node. - * - * Offset is an offset of the number that will be appended to the plugin's - * label, needed if the user adds multiple plugins faster than the engine - * sends the notification back. - */ -string -LoadPluginWindow::generate_module_name(int offset) -{ - string name = ""; - - Gtk::TreeModel::iterator iter = _selection->get_selected(); - - if (iter) { - Gtk::TreeModel::Row row = *iter; - SharedPtr plugin = row.get_value(_plugins_columns._col_plugin_model); - std::stringstream ss; - ss << plugin->default_node_symbol(); - if (offset != 0) - ss << "_" << offset + 1; - name = ss.str(); - } - - return name; -} - - -void -LoadPluginWindow::add_clicked() -{ - Gtk::TreeModel::iterator iter = _selection->get_selected(); - bool polyphonic = _polyphonic_checkbutton->get_active(); - - if (iter) { // If anything is selected - Gtk::TreeModel::Row row = *iter; - SharedPtr plugin = row.get_value(_plugins_columns._col_plugin_model); - string name = _node_name_entry->get_text(); - if (name == "") { - name = generate_module_name(); - } - if (name == "") { - Gtk::MessageDialog dialog(*this, - "Unable to chose a default name for this node. Please enter a name.", - false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); - - dialog.run(); - } else { - Path path = _patch->path().base() + Path::nameify(name); - App::instance().engine()->new_node(path, plugin->uri()); - App::instance().engine()->set_property(path, "ingen:polyphonic", polyphonic); - for (GraphObject::Variables::const_iterator i = _initial_data.begin(); i != _initial_data.end(); ++i) - App::instance().engine()->set_variable(path, i->first, i->second); - _node_name_entry->set_text(generate_module_name(++_plugin_name_offset)); - - // Cascade - Atom& x = _initial_data["ingenuity:canvas-x"]; - x = Atom(x.get_float() + 20.0f); - Atom& y = _initial_data["ingenuity:canvas-y"]; - y = Atom(y.get_float() + 20.0f); - } - } -} - - -/* -void -LoadPluginWindow::close_clicked() -{ - hide(); -} - - -void -LoadPluginWindow::ok_clicked() -{ - add_clicked(); - close_clicked(); -} -*/ - -void -LoadPluginWindow::filter_changed() -{ - _plugins_liststore->clear(); - - string search = _search_entry->get_text(); - transform(search.begin(), search.end(), search.begin(), ::toupper); - - // Get selected criteria - const Gtk::TreeModel::Row row = *(_filter_combo->get_active()); - CriteriaColumns::Criteria criteria = row[_criteria_columns._col_criteria]; - - string field; - - Gtk::TreeModel::Row model_row; - Gtk::TreeModel::iterator model_iter; - size_t num_visible = 0; - - - for (ClientStore::Plugins::const_iterator i = App::instance().store()->plugins()->begin(); - i != App::instance().store()->plugins()->end(); ++i) { - - const SharedPtr plugin = (*i).second; - - switch (criteria) { - case CriteriaColumns::NAME: - field = plugin->name(); break; - case CriteriaColumns::TYPE: - field = plugin->type_uri(); break; - case CriteriaColumns::URI: - field = plugin->uri(); break; - /*case CriteriaColumns::LIBRARY: - field = plugin->lib_name(); break; - case CriteriaColumns::LABEL: - field = plugin->plug_label(); break;*/ - default: - throw; - } - - transform(field.begin(), field.end(), field.begin(), ::toupper); - - if (field.find(search) != string::npos) { - model_iter = _plugins_liststore->append(); - model_row = *model_iter; - - model_row[_plugins_columns._col_name] = plugin->name(); - //model_row[_plugins_columns._col_label] = plugin->plug_label(); - model_row[_plugins_columns._col_type] = plugin->type_uri(); - model_row[_plugins_columns._col_uri] = plugin->uri(); - model_row[_plugins_columns._col_plugin_model] = plugin; - - ++num_visible; - } - } - - if (num_visible == 1) { - _selection->unselect_all(); - _selection->select(model_iter); - } -} - - -void -LoadPluginWindow::clear_clicked() -{ - _search_entry->set_text(""); - set_plugins(App::instance().store()->plugins()); -} - - -bool -LoadPluginWindow::on_key_press_event(GdkEventKey* event) -{ - if (event->keyval == GDK_w && event->state & GDK_CONTROL_MASK) { - hide(); - return true; - } else { - return Gtk::Window::on_key_press_event(event); - } -} - - -} // namespace GUI -} // namespace Ingen diff --git a/src/libs/gui/LoadPluginWindow.hpp b/src/libs/gui/LoadPluginWindow.hpp deleted file mode 100644 index 98644dde..00000000 --- a/src/libs/gui/LoadPluginWindow.hpp +++ /dev/null @@ -1,155 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef LOADPLUGINWINDOW_H -#define LOADPLUGINWINDOW_H - -#include -#include -#include -#include -#include -#include "interface/GraphObject.hpp" -#include "client/PatchModel.hpp" -#include "client/PluginModel.hpp" -#include "client/ClientStore.hpp" -using Ingen::Client::PluginModel; -using Ingen::Client::PatchModel; -using namespace Ingen::Shared; - -namespace Ingen { -namespace GUI { - - -// Gtkmm _really_ needs to add some helper to abstract away this stupid nonsense - -/** Columns for the plugin list in the load plugin window. - * - * \ingroup GUI - */ -class ModelColumns : public Gtk::TreeModel::ColumnRecord -{ -public: - ModelColumns() { - add(_col_icon); - add(_col_name); - add(_col_type); - add(_col_uri); - add(_col_label); - //add(_col_library); - //add(_col_label); - add(_col_plugin_model); - } - - Gtk::TreeModelColumn > _col_icon; - Gtk::TreeModelColumn _col_name; - Gtk::TreeModelColumn _col_type; - Gtk::TreeModelColumn _col_uri; - - // Not displayed: - Gtk::TreeModelColumn _col_label; - Gtk::TreeModelColumn > _col_plugin_model; -}; - - -/** Column for the criteria combo box in the load plugin window. - * - * \ingroup GUI - */ -class CriteriaColumns : public Gtk::TreeModel::ColumnRecord -{ -public: - enum Criteria { NAME, TYPE, URI, }; - - CriteriaColumns() { add(_col_label); add(_col_criteria); } - - Gtk::TreeModelColumn _col_label; - Gtk::TreeModelColumn _col_criteria; -}; - - -/** 'Load Plugin' window. - * - * Loaded by glade as a derived object. - * - * \ingroup GUI - */ -class LoadPluginWindow : public Gtk::Window -{ -public: - LoadPluginWindow(BaseObjectType* cobject, const Glib::RefPtr& xml); - - void set_patch(SharedPtr patch); - void set_plugins(SharedPtr plugins); - - void add_plugin(SharedPtr plugin); - - void present(SharedPtr patch, GraphObject::Variables data); - -protected: - void on_show(); - bool on_key_press_event(GdkEventKey* event); - -private: - void add_clicked(); - //void close_clicked(); - //void ok_clicked(); - void filter_changed(); - void clear_clicked(); - void name_changed(); - - void new_plugin(SharedPtr plugin); - - int plugin_compare(const Gtk::TreeModel::iterator& a, - const Gtk::TreeModel::iterator& b); - - void plugin_activated(const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* col); - void plugin_selection_changed(); - string generate_module_name(int offset = 0); - - GraphObject::Variables _initial_data; - - SharedPtr _patch; - - Glib::RefPtr _plugins_liststore; - ModelColumns _plugins_columns; - - Glib::RefPtr _criteria_liststore; - CriteriaColumns _criteria_columns; - - Glib::RefPtr _selection; - - int _plugin_name_offset; // see comments for generate_plugin_name - - bool _has_shown; - bool _refresh_list; - Gtk::TreeView* _plugins_treeview; - Gtk::CheckButton* _polyphonic_checkbutton; - Gtk::Entry* _node_name_entry; - Gtk::Button* _clear_button; - Gtk::Button* _add_button; - //Gtk::Button* _close_button; - //Gtk::Button* _ok_button; - Gtk::ComboBox* _filter_combo; - Gtk::Entry* _search_entry; -}; - - -} // namespace GUI -} // namespace Ingen - -#endif // LOADPLUGINWINDOW_H diff --git a/src/libs/gui/LoadRemotePatchWindow.cpp b/src/libs/gui/LoadRemotePatchWindow.cpp deleted file mode 100644 index 5567d67b..00000000 --- a/src/libs/gui/LoadRemotePatchWindow.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include "client/PatchModel.hpp" -#include "interface/EngineInterface.hpp" -#include "module/World.hpp" -#include "module/global.hpp" -#include "App.hpp" -#include "Configuration.hpp" -#include "LoadRemotePatchWindow.hpp" -#include "ThreadedLoader.hpp" - -using boost::optional; -using namespace Raul; -using namespace std; - -namespace Ingen { -namespace GUI { - - -LoadRemotePatchWindow::LoadRemotePatchWindow(BaseObjectType* cobject, const Glib::RefPtr& xml) -: Gtk::Dialog(cobject), - _replace(true) -{ - xml->get_widget("load_remote_patch_treeview", _treeview); - xml->get_widget("load_remote_patch_uri_entry", _uri_entry); - xml->get_widget("load_remote_patch_cancel_button", _cancel_button); - xml->get_widget("load_remote_patch_open_button", _open_button); - - _liststore = Gtk::ListStore::create(_columns); - _treeview->set_model(_liststore); - _treeview->append_column("Name", _columns._col_name); - _treeview->append_column("URI", _columns._col_uri); - - _selection = _treeview->get_selection(); - _selection->signal_changed().connect(sigc::mem_fun(this, &LoadRemotePatchWindow::patch_selected)); - _treeview->signal_row_activated().connect(sigc::mem_fun(this, &LoadRemotePatchWindow::patch_activated)); - - _open_button->signal_clicked().connect(sigc::mem_fun(this, &LoadRemotePatchWindow::open_clicked)); - _cancel_button->signal_clicked().connect(sigc::mem_fun(this, &LoadRemotePatchWindow::cancel_clicked)); - _uri_entry->signal_changed().connect(sigc::mem_fun(this, &LoadRemotePatchWindow::uri_changed)); -} - - -void -LoadRemotePatchWindow::present(SharedPtr patch, GraphObject::Variables data) -{ - _liststore->clear(); - - set_patch(patch); - _initial_data = data; - - Redland::Model model(*App::instance().world()->rdf_world, - "http://rdf.drobilla.net/ingen_patches/index.ttl", - "http://rdf.drobilla.net/ingen_patches/"); - - Redland::Query query(*App::instance().world()->rdf_world, Glib::ustring( - "SELECT DISTINCT ?name ?uri WHERE {" - " ?uri a ingen:Patch ;" - " doap:name ?name ." - "}")); - - Redland::Query::Results results = query.run(*App::instance().world()->rdf_world, model); - - for (Redland::Query::Results::iterator i = results.begin(); i != results.end(); ++i) { - Gtk::TreeModel::iterator iter = _liststore->append(); - (*iter)[_columns._col_name] = (*i)["name"].to_string(); - (*iter)[_columns._col_uri] = (*i)["uri"].to_string(); - } - - _treeview->columns_autosize(); - - Gtk::Window::present(); -} - - -/** Sets the patch controller for this window and initializes everything. - * - * This function MUST be called before using the window in any way! - */ -void -LoadRemotePatchWindow::set_patch(SharedPtr patch) -{ - _patch = patch; -} - - -void -LoadRemotePatchWindow::uri_changed() -{ - _open_button->property_sensitive() = (_uri_entry->get_text().length() > 0); -} - - -void -LoadRemotePatchWindow::patch_activated(const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* col) -{ - open_clicked(); -} - - -void -LoadRemotePatchWindow::patch_selected() -{ - Gtk::TreeModel::iterator selected_i = _selection->get_selected(); - - if (selected_i) { // If anything is selected - const Glib::ustring uri = selected_i->get_value(_columns._col_uri); - _uri_entry->set_text(uri); - } -} - - -void -LoadRemotePatchWindow::open_clicked() -{ - Glib::ustring uri = _uri_entry->get_text(); - - cerr << "OPEN URI: " << uri << endl; - - // If unset load_patch will load values - optional parent; - optional symbol; - - if (_replace) - App::instance().engine()->clear_patch(_patch->path()); - - if (_patch->path() != "/") - parent = _patch->path().parent(); - - App::instance().loader()->load_patch(true, uri, "/", - _initial_data, parent, symbol); - - hide(); -} - - -void -LoadRemotePatchWindow::cancel_clicked() -{ - hide(); -} - - -} // namespace GUI -} // namespace Ingen diff --git a/src/libs/gui/LoadRemotePatchWindow.hpp b/src/libs/gui/LoadRemotePatchWindow.hpp deleted file mode 100644 index ca0ca814..00000000 --- a/src/libs/gui/LoadRemotePatchWindow.hpp +++ /dev/null @@ -1,94 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef LOADREMOTEPATCHWINDOW_H -#define LOADREMOTEPATCHWINDOW_H - -#include -#include -#include -#include "interface/GraphObject.hpp" -#include "client/PatchModel.hpp" -#include "client/PluginModel.hpp" -using namespace Ingen::Shared; -using Ingen::Client::PatchModel; - -namespace Ingen { -namespace GUI { - - -/** Columns for the remote patch list. - * - * \ingroup GUI - */ -class PatchColumns : public Gtk::TreeModel::ColumnRecord -{ -public: - PatchColumns() { - add(_col_name); - add(_col_uri); - } - - Gtk::TreeModelColumn _col_name; - Gtk::TreeModelColumn _col_uri; -}; - - - -/* Load remote patch ("import location") dialog. - * - * \ingroup GUI - */ -class LoadRemotePatchWindow : public Gtk::Dialog -{ -public: - LoadRemotePatchWindow(BaseObjectType* cobject, const Glib::RefPtr& xml); - - void set_patch(SharedPtr patch); - - void set_replace() { _replace = true; } - void set_merge() { _replace = false; } - - void present(SharedPtr patch, GraphObject::Variables data); - -private: - void patch_activated(const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* col); - void patch_selected(); - void uri_changed(); - void open_clicked(); - void cancel_clicked(); - - GraphObject::Variables _initial_data; - - SharedPtr _patch; - bool _replace; - - Glib::RefPtr _selection; - Glib::RefPtr _liststore; - PatchColumns _columns; - - Gtk::TreeView* _treeview; - Gtk::Entry* _uri_entry; - Gtk::Button* _open_button; - Gtk::Button* _cancel_button; -}; - - -} // namespace GUI -} // namespace Ingen - -#endif // LOADREMOTEPATCHWINDOW_H diff --git a/src/libs/gui/LoadSubpatchWindow.cpp b/src/libs/gui/LoadSubpatchWindow.cpp deleted file mode 100644 index 71dbfb36..00000000 --- a/src/libs/gui/LoadSubpatchWindow.cpp +++ /dev/null @@ -1,187 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include "interface/EngineInterface.hpp" -#include "client/NodeModel.hpp" -#include "client/PatchModel.hpp" -#include "App.hpp" -#include "LoadSubpatchWindow.hpp" -#include "PatchView.hpp" -#include "Configuration.hpp" -#include "ThreadedLoader.hpp" -using boost::optional; -using namespace std; - -namespace Ingen { -namespace GUI { - - -LoadSubpatchWindow::LoadSubpatchWindow(BaseObjectType* cobject, const Glib::RefPtr& xml) -: Gtk::FileChooserDialog(cobject) -{ - xml->get_widget("load_subpatch_name_from_file_radio", _name_from_file_radio); - xml->get_widget("load_subpatch_name_from_user_radio", _name_from_user_radio); - xml->get_widget("load_subpatch_name_entry", _name_entry); - xml->get_widget("load_subpatch_poly_from_file_radio", _poly_from_file_radio); - xml->get_widget("load_subpatch_poly_from_parent_radio", _poly_from_parent_radio); - xml->get_widget("load_subpatch_poly_from_user_radio", _poly_from_user_radio); - xml->get_widget("load_subpatch_poly_spinbutton", _poly_spinbutton); - xml->get_widget("load_subpatch_ok_button", _ok_button); - xml->get_widget("load_subpatch_cancel_button", _cancel_button); - - _name_from_file_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadSubpatchWindow::disable_name_entry)); - _name_from_user_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadSubpatchWindow::enable_name_entry)); - _poly_from_file_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadSubpatchWindow::disable_poly_spinner)); - _poly_from_parent_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadSubpatchWindow::disable_poly_spinner)); - _poly_from_user_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadSubpatchWindow::enable_poly_spinner)); - _ok_button->signal_clicked().connect(sigc::mem_fun(this, &LoadSubpatchWindow::ok_clicked)); - _cancel_button->signal_clicked().connect(sigc::mem_fun(this, &LoadSubpatchWindow::cancel_clicked)); - - Gtk::FileFilter filt; - filt.add_pattern("*.om"); - filt.set_name("Om patch files (XML, DEPRECATED) (*.om)"); - filt.add_pattern("*.ingen.ttl"); - filt.set_name("Ingen patch files (RDF, *.ingen.ttl)"); - set_filter(filt); - - property_select_multiple() = true; - - // Add global examples directory to "shortcut folders" (bookmarks) - string examples_dir = INGEN_DATA_DIR; - examples_dir.append("/patches"); - DIR* d = opendir(examples_dir.c_str()); - if (d != NULL) - add_shortcut_folder(examples_dir); -} - - -void -LoadSubpatchWindow::present(SharedPtr patch, GraphObject::Variables data) -{ - set_patch(patch); - _initial_data = data; - Gtk::Window::present(); -} - - -/** Sets the patch controller for this window and initializes everything. - * - * This function MUST be called before using the window in any way! - */ -void -LoadSubpatchWindow::set_patch(SharedPtr patch) -{ - _patch = patch; - - char temp_buf[4]; - snprintf(temp_buf, 4, "%u", patch->poly()); - Glib::ustring txt = "Same as parent ("; - txt.append(temp_buf).append(")"); - _poly_from_parent_radio->set_label(txt); -} - - -void -LoadSubpatchWindow::on_show() -{ - if (App::instance().configuration()->patch_folder().length() > 0) - set_current_folder(App::instance().configuration()->patch_folder()); - Gtk::FileChooserDialog::on_show(); -} - - -///// Event Handlers ////// - - - -void -LoadSubpatchWindow::disable_name_entry() -{ - _name_entry->property_sensitive() = false; -} - - -void -LoadSubpatchWindow::enable_name_entry() -{ - _name_entry->property_sensitive() = true; -} - - -void -LoadSubpatchWindow::disable_poly_spinner() -{ - _poly_spinbutton->property_sensitive() = false; -} - - -void -LoadSubpatchWindow::enable_poly_spinner() -{ - _poly_spinbutton->property_sensitive() = true; -} - - -void -LoadSubpatchWindow::ok_clicked() -{ - assert(_patch); - - // If unset load_patch will load values - optional symbol; - string name_str = ""; - - if (_name_from_user_radio->get_active()) { - name_str = _name_entry->get_text(); - symbol = Symbol::symbolify(name_str); - } - - if (_poly_from_user_radio->get_active()) { - cerr << "Overriding poly: " << _poly_spinbutton->get_value_as_int() << endl; - _initial_data.insert(make_pair("ingen:polyphony", (int)_poly_spinbutton->get_value_as_int())); - } else if (_poly_from_parent_radio->get_active()) { - _initial_data.insert(make_pair("ingen:polyphony", (int)_patch->poly())); - } - - std::list uris = get_uris(); - for (std::list::iterator i = uris.begin(); i != uris.end(); ++i) { - // Cascade - Atom& x = _initial_data["ingenuity:canvas-x"]; - x = Atom(x.get_float() + 20.0f); - Atom& y = _initial_data["ingenuity:canvas-y"]; - y = Atom(y.get_float() + 20.0f); - - App::instance().loader()->load_patch(false, *i, "/", _initial_data, _patch->path(), symbol); - } - - hide(); -} - - -void -LoadSubpatchWindow::cancel_clicked() -{ - hide(); -} - - -} // namespace GUI -} // namespace Ingen diff --git a/src/libs/gui/LoadSubpatchWindow.hpp b/src/libs/gui/LoadSubpatchWindow.hpp deleted file mode 100644 index ee8a5a16..00000000 --- a/src/libs/gui/LoadSubpatchWindow.hpp +++ /dev/null @@ -1,80 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef LOADSUBPATCHWINDOW_H -#define LOADSUBPATCHWINDOW_H - -#include -#include -#include -#include "client/PatchModel.hpp" -#include "client/PluginModel.hpp" -#include "interface/GraphObject.hpp" -using namespace Ingen::Shared; -using Ingen::Client::PatchModel; - -namespace Ingen { -namespace GUI { - - -/** 'Add Subpatch' window. - * - * Loaded by glade as a derived object. - * - * \ingroup GUI - */ -class LoadSubpatchWindow : public Gtk::FileChooserDialog -{ -public: - LoadSubpatchWindow(BaseObjectType* cobject, const Glib::RefPtr& xml); - - void set_patch(SharedPtr patch); - - void present(SharedPtr patch, GraphObject::Variables data); - -protected: - void on_show(); - -private: - void disable_name_entry(); - void enable_name_entry(); - void disable_poly_spinner(); - void enable_poly_spinner(); - - void ok_clicked(); - void cancel_clicked(); - - GraphObject::Variables _initial_data; - - SharedPtr _patch; - - Gtk::RadioButton* _name_from_file_radio; - Gtk::RadioButton* _name_from_user_radio; - Gtk::Entry* _name_entry; - Gtk::RadioButton* _poly_from_file_radio; - Gtk::RadioButton* _poly_from_parent_radio; - Gtk::RadioButton* _poly_from_user_radio; - Gtk::SpinButton* _poly_spinbutton; - Gtk::Button* _ok_button; - Gtk::Button* _cancel_button; -}; - - -} // namespace GUI -} // namespace Ingen - -#endif // LOADSUBPATCHWINDOW_H diff --git a/src/libs/gui/Makefile.am b/src/libs/gui/Makefile.am deleted file mode 100644 index 41458812..00000000 --- a/src/libs/gui/Makefile.am +++ /dev/null @@ -1,125 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in -EXTRA_DIST = ingen_gui.gladep - -if BUILD_GTK_CLIENT - -globalpixmapsdir = $(datadir)/pixmaps -dist_globalpixmaps_DATA = ingen.svg ingen-icon.svg - -sharefilesdir = $(pkgdatadir) -dist_sharefiles_DATA = ingen_gui.glade ingen.svg ingen-icon.svg - -moduledir = $(libdir)/ingen - -module_LTLIBRARIES = libingen_gui.la - -libingen_gui_la_CXXFLAGS = \ - -DINGEN_MODULE_DIR=\"$(libdir)/ingen\" \ - -DINGEN_DATA_DIR=\"$(pkgdatadir)\" \ - @CURL_CFLAGS@ \ - @FLOWCANVAS_CFLAGS@ \ - @GNOMECANVASMM_CFLAGS@ \ - @GTKMM_CFLAGS@ \ - @INGEN_CFLAGS@ \ - @LIBGLADEMM_CFLAGS@ \ - @LIBLO_CFLAGS@ \ - @REDLANDMM_CFLAGS@ \ - @RAUL_CFLAGS@ \ - @SLV2_CFLAGS@ \ - @SOUP_CFLAGS@ - - -libingen_gui_la_LDFLAGS = -no-undefined -module -avoid-version - -libingen_gui_la_LIBADD = \ - ../module/libingen_module.la \ - ../shared/libingen_shared.la \ - ../client/libingen_client.la \ - @CURL_LIBS@ \ - @FLOWCANVAS_LIBS@ \ - @GNOMECANVASMM_LIBS@ \ - @GTKMM_LIBS@ \ - @LIBGLADEMM_LIBS@ \ - @LIBLO_LIBS@ \ - @REDLANDMM_LIBS@ \ - @RAUL_LIBS@ \ - @SLV2_LIBS@ \ - @SOUP_LIBS@ - -libingen_gui_la_SOURCES = \ - gui.hpp \ - gui.cpp \ - App.cpp \ - App.hpp \ - BreadCrumb.hpp \ - BreadCrumbBox.cpp \ - BreadCrumbBox.hpp \ - Configuration.cpp \ - Configuration.hpp \ - ConnectWindow.cpp \ - ConnectWindow.hpp \ - Connection.hpp \ - ControlPanel.cpp \ - ControlPanel.hpp \ - Controls.cpp \ - Controls.hpp \ - GladeFactory.cpp \ - GladeFactory.hpp \ - LoadPatchWindow.cpp \ - LoadPatchWindow.hpp \ - LoadPluginWindow.cpp \ - LoadPluginWindow.hpp \ - LoadRemotePatchWindow.cpp \ - LoadRemotePatchWindow.hpp \ - LoadSubpatchWindow.cpp \ - LoadSubpatchWindow.hpp \ - MessagesWindow.cpp \ - MessagesWindow.hpp \ - NewSubpatchWindow.cpp \ - NewSubpatchWindow.hpp \ - NodeControlWindow.cpp \ - NodeControlWindow.hpp \ - ObjectMenu.hpp \ - ObjectMenu.cpp \ - NodeMenu.hpp \ - NodeMenu.cpp \ - PortMenu.hpp \ - PortMenu.cpp \ - NodeModule.cpp \ - NodeModule.hpp \ - NodePropertiesWindow.cpp \ - NodePropertiesWindow.hpp \ - PatchCanvas.cpp \ - PatchCanvas.hpp \ - PatchPortModule.cpp \ - PatchPortModule.hpp \ - PatchPropertiesWindow.cpp \ - PatchPropertiesWindow.hpp \ - PatchTreeWindow.cpp \ - PatchTreeWindow.hpp \ - PatchView.cpp \ - PatchView.hpp \ - PatchWindow.cpp \ - PatchWindow.hpp \ - Port.cpp \ - Port.hpp \ - PortPropertiesWindow.cpp \ - PortPropertiesWindow.hpp \ - RenameWindow.cpp \ - RenameWindow.hpp \ - SubpatchModule.cpp \ - SubpatchModule.hpp \ - ThreadedLoader.cpp \ - ThreadedLoader.hpp \ - WindowFactory.cpp \ - WindowFactory.hpp - -if WITH_CURL -libingen_gui_la_SOURCES += \ - UploadPatchWindow.cpp \ - UploadPatchWindow.hpp -endif - -endif - - diff --git a/src/libs/gui/MessagesWindow.cpp b/src/libs/gui/MessagesWindow.cpp deleted file mode 100644 index 3da810f6..00000000 --- a/src/libs/gui/MessagesWindow.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "MessagesWindow.hpp" -#include - -namespace Ingen { -namespace GUI { -using std::string; - - -MessagesWindow::MessagesWindow(BaseObjectType* cobject, const Glib::RefPtr& glade_xml) -: Gtk::Window(cobject) -{ - glade_xml->get_widget("messages_textview", _textview); - glade_xml->get_widget("messages_clear_button", _clear_button); - glade_xml->get_widget("messages_close_button", _close_button); - - _clear_button->signal_clicked().connect(sigc::mem_fun(this, &MessagesWindow::clear_clicked)); - _close_button->signal_clicked().connect(sigc::mem_fun(this, &MessagesWindow::close_clicked)); -} - - -void -MessagesWindow::post(const string& msg) -{ - Glib::RefPtr text_buf = _textview->get_buffer(); - text_buf->insert(text_buf->end(), msg); - text_buf->insert(text_buf->end(), "\n"); - - if (!_clear_button->is_sensitive()) - _clear_button->set_sensitive(true); -} - - -void -MessagesWindow::close_clicked() -{ - hide(); -} - - -void -MessagesWindow::clear_clicked() -{ - Glib::RefPtr text_buf = _textview->get_buffer(); - text_buf->erase(text_buf->begin(), text_buf->end()); - _clear_button->set_sensitive(false); -} - - -} // namespace GUI -} // namespace Ingen diff --git a/src/libs/gui/MessagesWindow.hpp b/src/libs/gui/MessagesWindow.hpp deleted file mode 100644 index dea0fdd4..00000000 --- a/src/libs/gui/MessagesWindow.hpp +++ /dev/null @@ -1,58 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef MESSAGESWINDOW_H -#define MESSAGESWINDOW_H - -#include -#include -#include -using std::string; - - -namespace Ingen { -namespace GUI { - - -/** Messages Window. - * - * Loaded by libglade as a derived object. - * This is shown when errors occur (ie during patch loading). - * - * \ingroup GUI - */ -class MessagesWindow : public Gtk::Window -{ -public: - MessagesWindow(BaseObjectType* cobject, const Glib::RefPtr& refGlade); - - void post(const string& str); - -private: - void clear_clicked(); - void close_clicked(); - - Gtk::TextView* _textview; - Gtk::Button* _clear_button; - Gtk::Button* _close_button; -}; - - -} // namespace GUI -} // namespace Ingen - -#endif // MESSAGESWINDOW_H diff --git a/src/libs/gui/NewSubpatchWindow.cpp b/src/libs/gui/NewSubpatchWindow.cpp deleted file mode 100644 index 580ebb51..00000000 --- a/src/libs/gui/NewSubpatchWindow.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "App.hpp" -#include "interface/EngineInterface.hpp" -#include "client/NodeModel.hpp" -#include "client/PatchModel.hpp" -#include "client/ClientStore.hpp" -#include "NewSubpatchWindow.hpp" -#include "PatchView.hpp" - -namespace Ingen { -namespace GUI { - - -NewSubpatchWindow::NewSubpatchWindow(BaseObjectType* cobject, const Glib::RefPtr& xml) -: Gtk::Window(cobject) -{ - xml->get_widget("new_subpatch_name_entry", _name_entry); - xml->get_widget("new_subpatch_message_label", _message_label); - xml->get_widget("new_subpatch_polyphony_spinbutton", _poly_spinbutton); - xml->get_widget("new_subpatch_ok_button", _ok_button); - xml->get_widget("new_subpatch_cancel_button", _cancel_button); - - _name_entry->signal_changed().connect(sigc::mem_fun(this, &NewSubpatchWindow::name_changed)); - _ok_button->signal_clicked().connect(sigc::mem_fun(this, &NewSubpatchWindow::ok_clicked)); - _cancel_button->signal_clicked().connect(sigc::mem_fun(this, &NewSubpatchWindow::cancel_clicked)); - - _ok_button->property_sensitive() = false; -} - -void -NewSubpatchWindow::present(SharedPtr patch, GraphObject::Variables data) -{ - set_patch(patch); - _initial_data = data; - Gtk::Window::present(); -} - -/** Sets the patch controller for this window and initializes everything. - * - * This function MUST be called before using the window in any way! - */ -void -NewSubpatchWindow::set_patch(SharedPtr patch) -{ - _patch = patch; -} - - -/** Called every time the user types into the name input box. - * Used to display warning messages, and enable/disable the OK button. - */ -void -NewSubpatchWindow::name_changed() -{ - string name = _name_entry->get_text(); - if (!Path::is_valid_name(name)) { - _message_label->set_text("Name contains invalid characters."); - _ok_button->property_sensitive() = false; - } else if (App::instance().store()->find(_patch->path().base() + name) - != App::instance().store()->end()) { - _message_label->set_text("An object already exists with that name."); - _ok_button->property_sensitive() = false; - } else if (name.length() == 0) { - _message_label->set_text(""); - _ok_button->property_sensitive() = false; - } else { - _message_label->set_text(""); - _ok_button->property_sensitive() = true; - } -} - - -void -NewSubpatchWindow::ok_clicked() -{ - const Path path = _patch->path().base() + Path::nameify(_name_entry->get_text()); - const uint32_t poly = _poly_spinbutton->get_value_as_int(); - - App::instance().engine()->new_patch(path, poly); - for (GraphObject::Variables::const_iterator i = _initial_data.begin(); i != _initial_data.end(); ++i) - App::instance().engine()->set_variable(path, i->first, i->second); - - App::instance().engine()->set_property(_patch->path(), "ingen:enabled", (bool)true); - - hide(); -} - - -void -NewSubpatchWindow::cancel_clicked() -{ - hide(); -} - - -} // namespace GUI -} // namespace Ingen diff --git a/src/libs/gui/NewSubpatchWindow.hpp b/src/libs/gui/NewSubpatchWindow.hpp deleted file mode 100644 index 00fb6a3b..00000000 --- a/src/libs/gui/NewSubpatchWindow.hpp +++ /dev/null @@ -1,68 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef NEWSUBPATCHWINDOW_H -#define NEWSUBPATCHWINDOW_H - -#include -#include -#include -#include "interface/GraphObject.hpp" -#include "client/PatchModel.hpp" -#include "client/PluginModel.hpp" -using namespace Ingen::Shared; -using Ingen::Client::PatchModel; - -namespace Ingen { -namespace GUI { - - -/** 'New Subpatch' window. - * - * Loaded by glade as a derived object. - * - * \ingroup GUI - */ -class NewSubpatchWindow : public Gtk::Window -{ -public: - NewSubpatchWindow(BaseObjectType* cobject, const Glib::RefPtr& xml); - - void set_patch(SharedPtr patch); - - void present(SharedPtr patch, GraphObject::Variables data); - -private: - void name_changed(); - void ok_clicked(); - void cancel_clicked(); - - GraphObject::Variables _initial_data; - SharedPtr _patch; - - Gtk::Entry* _name_entry; - Gtk::Label* _message_label; - Gtk::SpinButton* _poly_spinbutton; - Gtk::Button* _ok_button; - Gtk::Button* _cancel_button; -}; - - -} // namespace GUI -} // namespace Ingen - -#endif // NEWSUBPATCHWINDOW_H diff --git a/src/libs/gui/NodeControlWindow.cpp b/src/libs/gui/NodeControlWindow.cpp deleted file mode 100644 index 293d28f5..00000000 --- a/src/libs/gui/NodeControlWindow.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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 alongCont - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include "interface/EngineInterface.hpp" -#include "client/NodeModel.hpp" -#include "App.hpp" -#include "NodeControlWindow.hpp" -#include "GladeFactory.hpp" -#include "Controls.hpp" -#include "ControlPanel.hpp" -#include "PatchWindow.hpp" - -using namespace std; - -namespace Ingen { -namespace GUI { - - -/** Create a node control window and load a new ControlPanel for it. - */ -NodeControlWindow::NodeControlWindow(SharedPtr node, uint32_t poly) - : _node(node) - , _position_stored(false) - , _x(0) - , _y(0) -{ - assert(_node != NULL); - - property_resizable() = true; - set_border_width(5); - - set_title(_node->path() + " Controls"); - - Glib::RefPtr xml = GladeFactory::new_glade_reference("warehouse_win"); - xml->get_widget_derived("control_panel_vbox", _control_panel); - - show_all_children(); - - _control_panel->reparent(*this); - _control_panel->init(_node, poly); - _control_panel->show(); - - resize(); - - _callback_enabled = true; -} - - -/** Create a node control window and with an existing ControlPanel. - */ -NodeControlWindow::NodeControlWindow(SharedPtr node, ControlPanel* panel) - : _node(node) - , _control_panel(panel) -{ - assert(_node); - - property_resizable() = true; - set_border_width(5); - - set_title(_node->path() + " Controls"); - - _control_panel->reparent(*this); - - show_all_children(); - resize(); - - _callback_enabled = true; -} - - -NodeControlWindow::~NodeControlWindow() -{ - delete _control_panel; -} - - -void -NodeControlWindow::resize() -{ - pair controls_size = _control_panel->ideal_size(); - /*int width = 400; - int height = controls_size.second - + ((_node->polyphonic()) ? 4 : 40);*/ - int width = controls_size.first; - int height = controls_size.second; - - if (height > property_screen().get_value()->get_height() - 64) - height = property_screen().get_value()->get_height() - 64; - if (width > property_screen().get_value()->get_width() - 64) - width = property_screen().get_value()->get_width() - 64; - - //cerr << "Resizing to: " << width << " x " << height << endl; - - Gtk::Window::resize(width, height); -} - - -void -NodeControlWindow::on_show() -{ - for (NodeModel::Ports::const_iterator i = _node->ports().begin(); - i != _node->ports().end(); ++i) - if ((*i)->type().is_control() && (*i)->is_input()) - App::instance().engine()->request_port_value((*i)->path()); - - if (_position_stored) - move(_x, _y); - - Gtk::Window::on_show(); -} - - -void -NodeControlWindow::on_hide() -{ - _position_stored = true; - get_position(_x, _y); - Gtk::Window::on_hide(); -} - - -} // namespace GUI -} // namespace Ingen diff --git a/src/libs/gui/NodeControlWindow.hpp b/src/libs/gui/NodeControlWindow.hpp deleted file mode 100644 index 84e462c5..00000000 --- a/src/libs/gui/NodeControlWindow.hpp +++ /dev/null @@ -1,76 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef NODECONTROLWINDOW_H -#define NODECONTROLWINDOW_H - -#include -#include -#include -#include -#include -#include -using std::string; using std::vector; - -namespace Ingen { namespace Client { - class NodeModel; -} } -using Ingen::Client::NodeModel; - -namespace Ingen { -namespace GUI { - -class ControlGroup; -class ControlPanel; - - -/** Window with controls (sliders) for all control-rate ports on a Node. - * - * \ingroup GUI - */ -class NodeControlWindow : public Gtk::Window -{ -public: - NodeControlWindow(SharedPtr node, uint32_t poly); - NodeControlWindow(SharedPtr node, ControlPanel* panel); - virtual ~NodeControlWindow(); - - SharedPtr node() { return _node; } - - ControlPanel* control_panel() const { return _control_panel; } - - void resize(); - -protected: - void on_show(); - void on_hide(); - -private: - SharedPtr _node; - ControlPanel* _control_panel; - bool _callback_enabled; - - bool _position_stored; - int _x; - int _y; -}; - - -} // namespace GUI -} // namespace Ingen - -#endif // NODECONTROLWINDOW_H diff --git a/src/libs/gui/NodeMenu.cpp b/src/libs/gui/NodeMenu.cpp deleted file mode 100644 index 529eb52c..00000000 --- a/src/libs/gui/NodeMenu.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include "interface/EngineInterface.hpp" -#include "client/NodeModel.hpp" -#include "App.hpp" -#include "NodeMenu.hpp" -#include "WindowFactory.hpp" - -using namespace std; -using std::cerr; using std::endl; - -namespace Ingen { -namespace GUI { - - -NodeMenu::NodeMenu(BaseObjectType* cobject, const Glib::RefPtr& xml) - : ObjectMenu(cobject, xml) - , _controls_menuitem(NULL) -{ - Gtk::Menu* node_menu = NULL; - xml->get_widget("node_menu", node_menu); - xml->get_widget("node_controls_menuitem", _controls_menuitem); - xml->get_widget("node_popup_gui_menuitem", _popup_gui_menuitem); - xml->get_widget("node_embed_gui_menuitem", _embed_gui_menuitem); - - node_menu->remove(*_controls_menuitem); - node_menu->remove(*_popup_gui_menuitem); - node_menu->remove(*_embed_gui_menuitem); - items().push_front(Gtk::Menu_Helpers::SeparatorElem()); - insert(*_controls_menuitem, 0); - insert(*_popup_gui_menuitem, 0); - insert(*_embed_gui_menuitem, 0); -} - - -void -NodeMenu::init(SharedPtr node) -{ - ObjectMenu::init(node); - - _controls_menuitem->signal_activate().connect(sigc::bind( - sigc::mem_fun(App::instance().window_factory(), &WindowFactory::present_controls), - node)); - - _popup_gui_menuitem->signal_activate().connect(sigc::mem_fun(signal_popup_gui, - &sigc::signal::emit)); - _embed_gui_menuitem->signal_toggled().connect(sigc::mem_fun(this, - &NodeMenu::on_menu_embed_gui)); - - const PluginModel* plugin = dynamic_cast(node->plugin()); - if (plugin && plugin->type() == PluginModel::LV2 && plugin->has_ui()) { - _popup_gui_menuitem->show(); - _embed_gui_menuitem->show(); - } else { - _popup_gui_menuitem->hide(); - _embed_gui_menuitem->hide(); - } - - _enable_signal = true; -} - - -void -NodeMenu::on_menu_embed_gui() -{ - signal_embed_gui.emit(_embed_gui_menuitem->get_active()); -} - - -void -NodeMenu::on_menu_clone() -{ - cerr << "[NodeMenu] FIXME: clone broken\n"; - /* - assert(_node); - //assert(_parent != NULL); - //assert(_parent->model() != NULL); - - string clone_name = _node->name(); - int i = 2; // postfix number (ie oldname_2) - - // Check if name already has a number postfix - if (clone_name.find_last_of("_") != string::npos) { - string name_postfix = clone_name.substr(clone_name.find_last_of("_")+1); - clone_name = clone_name.substr(0, clone_name.find_last_of("_")); - if (sscanf(name_postfix.c_str(), "%d", &i)) - ++i; - } - - char clone_postfix[4]; - for ( ; i < 100; ++i) { - snprintf(clone_postfix, 4, "_%d", i); - if (_node->parent_patch()->get_node(clone_name + clone_postfix) == NULL) - break; - } - - clone_name = clone_name + clone_postfix; - - const string path = _node->parent_patch()->base() + clone_name; - NodeModel* nm = new NodeModel(_node->plugin(), path); - nm->polyphonic(_node->polyphonic()); - nm->x(_node->x() + 20); - nm->y(_node->y() + 20); - App::instance().engine()->create_node_from_model(nm); - */ -} - - -void -NodeMenu::on_menu_learn() -{ - App::instance().engine()->midi_learn(_object->path()); -} - - -void -NodeMenu::on_menu_disconnect() -{ - App::instance().engine()->disconnect_all(_object->parent()->path(), _object->path()); -} - - -bool -NodeMenu::has_control_inputs() -{ - const NodeModel* const nm = (NodeModel*)_object.get(); - for (NodeModel::Ports::const_iterator i = nm->ports().begin(); i != nm->ports().end(); ++i) - if ((*i)->is_input() && (*i)->type().is_control()) - return true; - - return false; -} - - -void -NodeMenu::enable_controls_menuitem() -{ - _controls_menuitem->property_sensitive() = true; -} - - -void -NodeMenu::disable_controls_menuitem() -{ - _controls_menuitem->property_sensitive() = false; -} - - - -} // namespace GUI -} // namespace Ingen - diff --git a/src/libs/gui/NodeMenu.hpp b/src/libs/gui/NodeMenu.hpp deleted file mode 100644 index 2a10e473..00000000 --- a/src/libs/gui/NodeMenu.hpp +++ /dev/null @@ -1,72 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef NODEMENU_H -#define NODEMENU_H - -#include -#include -#include -#include -#include "client/NodeModel.hpp" -#include "ObjectMenu.hpp" - -using Ingen::Client::NodeModel; - -namespace Ingen { -namespace GUI { - - -/** Menu for a Node. - * - * \ingroup GUI - */ -class NodeMenu : public ObjectMenu -{ -public: - NodeMenu(BaseObjectType* cobject, const Glib::RefPtr& xml); - - virtual void program_add(int bank, int program, const std::string& name) {} - virtual void program_remove(int bank, int program) {} - - void init(SharedPtr node); - - bool has_control_inputs(); - - sigc::signal signal_popup_gui; - sigc::signal signal_embed_gui; - -protected: - - virtual void enable_controls_menuitem(); - virtual void disable_controls_menuitem(); - - void on_menu_disconnect(); - void on_menu_clone(); - void on_menu_learn(); - void on_menu_embed_gui(); - - Gtk::MenuItem* _controls_menuitem; - Gtk::MenuItem* _popup_gui_menuitem; - Gtk::CheckMenuItem* _embed_gui_menuitem; -}; - - -} // namespace GUI -} // namespace Ingen - -#endif // NODEMENU_H diff --git a/src/libs/gui/NodeModule.cpp b/src/libs/gui/NodeModule.cpp deleted file mode 100644 index 97edaa84..00000000 --- a/src/libs/gui/NodeModule.cpp +++ /dev/null @@ -1,386 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include "interface/EngineInterface.hpp" -#include "client/NodeModel.hpp" -#include "client/PatchModel.hpp" -#include "client/PluginUI.hpp" -#include "App.hpp" -#include "GladeFactory.hpp" -#include "NodeControlWindow.hpp" -#include "NodeModule.hpp" -#include "PatchCanvas.hpp" -#include "PatchWindow.hpp" -#include "Port.hpp" -#include "RenameWindow.hpp" -#include "SubpatchModule.hpp" -#include "WindowFactory.hpp" -#include "Configuration.hpp" - -using namespace std; - -namespace Ingen { -namespace GUI { - - -NodeModule::NodeModule(boost::shared_ptr canvas, SharedPtr node) - : FlowCanvas::Module(canvas, node->path().name()) - , _node(node) - , _gui_widget(NULL) - , _gui_window(NULL) -{ - assert(_node); - - node->signal_new_port.connect(sigc::bind(sigc::mem_fun(this, &NodeModule::add_port), true)); - node->signal_removed_port.connect(sigc::mem_fun(this, &NodeModule::remove_port)); - node->signal_variable.connect(sigc::mem_fun(this, &NodeModule::set_variable)); - node->signal_property.connect(sigc::mem_fun(this, &NodeModule::set_property)); - node->signal_renamed.connect(sigc::mem_fun(this, &NodeModule::rename)); -} - - -NodeModule::~NodeModule() -{ - NodeControlWindow* win = App::instance().window_factory()->control_window(_node); - - if (win) { - // Should remove from window factory via signal - delete win; - } -} - - -void -NodeModule::create_menu() -{ - Glib::RefPtr xml = GladeFactory::new_glade_reference(); - xml->get_widget_derived("object_menu", _menu); - _menu->init(_node); - _menu->signal_embed_gui.connect(sigc::mem_fun(this, &NodeModule::embed_gui)); - _menu->signal_popup_gui.connect(sigc::hide_return(sigc::mem_fun(this, &NodeModule::popup_gui))); - - set_menu(_menu); -} - - -boost::shared_ptr -NodeModule::create(boost::shared_ptr canvas, SharedPtr node, bool human) -{ - boost::shared_ptr ret; - - SharedPtr patch = PtrCast(node); - if (patch) - ret = boost::shared_ptr(new SubpatchModule(canvas, patch)); - else - ret = boost::shared_ptr(new NodeModule(canvas, node)); - - for (GraphObject::Variables::const_iterator m = node->variables().begin(); m != node->variables().end(); ++m) - ret->set_variable(m->first, m->second); - - for (NodeModel::Ports::const_iterator p = node->ports().begin(); p != node->ports().end(); ++p) { - ret->add_port(*p, false); - } - - if (human) - ret->show_human_names(human); - - ret->resize(); - ret->set_stacked_border(node->polyphonic()); - - return ret; -} - - -void -NodeModule::show_human_names(bool b) -{ - if (b && node()->plugin()) - set_name(((PluginModel*)node()->plugin())->human_name()); - else - b = false; - - if (!b) - set_name(node()->symbol()); - - uint32_t index = 0; - for (PortVector::const_iterator i = ports().begin(); i != ports().end(); ++i) { - if (b) { - string hn = ((PluginModel*)node()->plugin())->port_human_name(index); - if (hn != "") - (*i)->set_name(hn); - } else { - (*i)->set_name(node()->port(index)->symbol()); - } - ++index; - } - - resize(); -} - - -void -NodeModule::value_changed(uint32_t index, const Atom& value) -{ - float control = 0.0f; - switch (value.type()) { - case Atom::FLOAT: - control = value.get_float(); - if (_plugin_ui) { - SLV2UIInstance inst = _plugin_ui->instance(); - const LV2UI_Descriptor* ui_descriptor = slv2_ui_instance_get_descriptor(inst); - LV2UI_Handle ui_handle = slv2_ui_instance_get_handle(inst); - if (ui_descriptor->port_event) - ui_descriptor->port_event(ui_handle, index, 4, 0, &control); - } - break; - case Atom::STRING: - cout << "Port value type is a string? (\"" << value.get_string() << "\")" << endl; - break; - default: - break; - } -} - - -void -NodeModule::embed_gui(bool embed) -{ - if (embed) { - - if (_gui_window) { - cerr << "LV2 GUI already popped up, cannot embed" << endl; - return; - } - - if (!_plugin_ui) { - const PluginModel* const pm = dynamic_cast(_node->plugin()); - assert(pm); - _plugin_ui = pm->ui(App::instance().world(), _node); - } - - if (_plugin_ui) { - GtkWidget* c_widget = (GtkWidget*)slv2_ui_instance_get_widget(_plugin_ui->instance()); - _gui_widget = Glib::wrap(c_widget); - assert(_gui_widget); - - Gtk::Container* container = new Gtk::EventBox(); - container->set_name("ingen_embedded_node_gui_container"); - container->add(*_gui_widget); - FlowCanvas::Module::embed(container); - } else { - cerr << "ERROR: Failed to create LV2 UI" << endl; - } - - if (_gui_widget) { - _gui_widget->show_all(); - - for (NodeModel::Ports::const_iterator p = _node->ports().begin(); - p != _node->ports().end(); ++p) - if ((*p)->type().is_control() && (*p)->is_output()) - App::instance().engine()->set_property((*p)->path(), "ingen:broadcast", true); - } - - } else { // un-embed - - FlowCanvas::Module::embed(NULL); - _plugin_ui.reset(); - - for (NodeModel::Ports::const_iterator p = _node->ports().begin(); p != _node->ports().end(); ++p) - if ((*p)->type().is_control() && (*p)->is_output()) - App::instance().engine()->set_property((*p)->path(), "ingen:broadcast", false); - } - - if (embed && _embed_item) { - initialise_gui_values(); - set_base_color(0x212222FF); - } else { - set_default_base_color(); - } - - resize(); -} - - -void -NodeModule::rename() -{ - set_name(_node->path().name()); - resize(); -} - - -void -NodeModule::add_port(SharedPtr port, bool resize_to_fit) -{ - uint32_t index = _ports.size(); // FIXME: kludge, engine needs to tell us this - - string name = port->path().name(); - if (App::instance().configuration()->name_style() == Configuration::HUMAN && node()->plugin()) - name = ((PluginModel*)node()->plugin())->port_human_name(index); - - Module::add_port(boost::shared_ptr( - new Port(PtrCast(shared_from_this()), port, name))); - - port->signal_value_changed.connect(sigc::bind<0>( - sigc::mem_fun(this, &NodeModule::value_changed), index)); - - if (resize_to_fit) - resize(); -} - - -void -NodeModule::remove_port(SharedPtr port) -{ - SharedPtr p = Module::remove_port(port->path().name()); - p.reset(); -} - - -bool -NodeModule::popup_gui() -{ -#ifdef HAVE_SLV2 - if (_node->plugin() && _node->plugin()->type() == PluginModel::LV2) { - if (_plugin_ui) { - cerr << "LV2 GUI already embedded, cannot pop up" << endl; - return false; - } - - const PluginModel* const plugin = dynamic_cast(_node->plugin()); - assert(plugin); - - _plugin_ui = plugin->ui(App::instance().world(), _node); - - if (_plugin_ui) { - GtkWidget* c_widget = (GtkWidget*)slv2_ui_instance_get_widget(_plugin_ui->instance()); - _gui_widget = Glib::wrap(c_widget); - - _gui_window = new Gtk::Window(); - _gui_window->add(*_gui_widget); - _gui_widget->show_all(); - initialise_gui_values(); - - _gui_window->signal_unmap().connect( - sigc::mem_fun(this, &NodeModule::on_gui_window_close)); - _gui_window->present(); - - return true; - } else { - cerr << "No LV2 GUI" << endl; - } - } -#endif - return false; -} - - -void -NodeModule::on_gui_window_close() -{ - delete _gui_window; - _gui_window = NULL; - _plugin_ui.reset(); - _gui_widget = NULL; -} - - -void -NodeModule::initialise_gui_values() -{ - uint32_t index=0; - for (NodeModel::Ports::const_iterator p = _node->ports().begin(); p != _node->ports().end(); ++p) { - if ((*p)->type().is_control()) - value_changed(index, (*p)->value()); - ++index; - } -} - - -void -NodeModule::show_control_window() -{ - App::instance().window_factory()->present_controls(_node); -} - - -void -NodeModule::on_double_click(GdkEventButton* ev) -{ - if ( ! popup_gui() ) - show_control_window(); -} - - -void -NodeModule::store_location() -{ - const float x = static_cast(property_x()); - const float y = static_cast(property_y()); - - const Atom& existing_x = _node->get_variable("ingenuity:canvas-x"); - const Atom& existing_y = _node->get_variable("ingenuity:canvas-y"); - - if (existing_x.type() != Atom::FLOAT || existing_y.type() != Atom::FLOAT - || existing_x.get_float() != x || existing_y.get_float() != y) { - App::instance().engine()->set_variable(_node->path(), "ingenuity:canvas-x", Atom(x)); - App::instance().engine()->set_variable(_node->path(), "ingenuity:canvas-y", Atom(y)); - } -} - - -void -NodeModule::set_variable(const string& key, const Atom& value) -{ - if (key == "ingenuity:canvas-x" && value.type() == Atom::FLOAT) - move_to(value.get_float(), property_y()); - else if (key == "ingenuity:canvas-y" && value.type() == Atom::FLOAT) - move_to(property_x(), value.get_float()); -} - - -void -NodeModule::set_property(const string& key, const Atom& value) -{ - if (key == "ingen:polyphonic" && value.type() == Atom::BOOL) { - set_stacked_border(value.get_bool()); - } else if (key == "ingen:selected" && value.type() == Atom::BOOL) { - if (value.get_bool() != selected()) { - if (value.get_bool()) - _canvas.lock()->select_item(shared_from_this()); - else - _canvas.lock()->unselect_item(shared_from_this()); - } - } -} - - -void -NodeModule::set_selected(bool b) -{ - if (b != selected()) { - Module::set_selected(b); - if (App::instance().signal()) - App::instance().engine()->set_property(_node->path(), "ingen:selected", b); - } -} - - -} // namespace GUI -} // namespace Ingen diff --git a/src/libs/gui/NodeModule.hpp b/src/libs/gui/NodeModule.hpp deleted file mode 100644 index de9556fd..00000000 --- a/src/libs/gui/NodeModule.hpp +++ /dev/null @@ -1,102 +0,0 @@ -/* This file is part of In* Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef NODEMODULE_H -#define NODEMODULE_H - -#include -#include -#include -#include -#include "Port.hpp" -#include "NodeMenu.hpp" - -class Atom; - -namespace Ingen { namespace Client { - class PortModel; - class NodeModel; -} } -using namespace Ingen::Client; - -namespace Ingen { -namespace GUI { - -class PatchCanvas; -class Port; - - -/** A module in a patch. - * - * This base class is extended for various types of modules. - * - * \ingroup GUI - */ -class NodeModule : public FlowCanvas::Module -{ -public: - static boost::shared_ptr create ( - boost::shared_ptr canvas, - SharedPtr node, - bool human_names); - - virtual ~NodeModule(); - - boost::shared_ptr port(const std::string& port_name) { - return boost::dynamic_pointer_cast( - Module::get_port(port_name)); - } - - virtual void store_location(); - void show_human_names(bool b); - - SharedPtr node() const { return _node; } - -protected: - NodeModule(boost::shared_ptr canvas, SharedPtr node); - - void on_double_click(GdkEventButton* ev); - - void show_control_window(); - void embed_gui(bool embed); - bool popup_gui(); - void on_gui_window_close(); - void set_selected(bool b); - - void rename(); - void set_variable(const std::string& key, const Atom& value); - void set_property(const std::string& predicate, const Raul::Atom& value); - - void add_port(SharedPtr port, bool resize=true); - void remove_port(SharedPtr port); - - void value_changed(uint32_t index, const Atom& value); - void initialise_gui_values(); - - void create_menu(); - - SharedPtr _node; - NodeMenu* _menu; - SharedPtr _plugin_ui; - Gtk::Widget* _gui_widget; - Gtk::Window* _gui_window; ///< iff popped up -}; - - -} // namespace GUI -} // namespace Ingen - -#endif // NODEMODULE_H diff --git a/src/libs/gui/NodePropertiesWindow.cpp b/src/libs/gui/NodePropertiesWindow.cpp deleted file mode 100644 index ae2c23b5..00000000 --- a/src/libs/gui/NodePropertiesWindow.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include "client/NodeModel.hpp" -#include "client/PluginModel.hpp" -#include "NodePropertiesWindow.hpp" - -namespace Ingen { -namespace GUI { -using std::string; - - -NodePropertiesWindow::NodePropertiesWindow(BaseObjectType* cobject, const Glib::RefPtr& glade_xml) -: Gtk::Window(cobject) -{ - glade_xml->get_widget("node_properties_path_label", _node_path_label); - glade_xml->get_widget("node_properties_polyphonic_checkbutton", _node_polyphonic_toggle); - glade_xml->get_widget("node_properties_plugin_type_label", _plugin_type_label); - glade_xml->get_widget("node_properties_plugin_uri_label", _plugin_uri_label); - glade_xml->get_widget("node_properties_plugin_name_label", _plugin_name_label); -} - - -/** Set the node this window is associated with. - * This function MUST be called before using this object in any way. - */ -void -NodePropertiesWindow::set_node(SharedPtr node_model) -{ - assert(node_model); - - _node_model = node_model; - - set_title(node_model->path() + " Properties"); - - _node_path_label->set_text(node_model->path()); - _node_polyphonic_toggle->set_active(node_model->polyphonic()); - - const PluginModel* pm = dynamic_cast(node_model->plugin()); - if (pm) { - _plugin_type_label->set_text(pm->type_uri()); - _plugin_uri_label->set_text(pm->uri()); - _plugin_name_label->set_text(pm->name()); - } -} - - -} // namespace GUI -} // namespace Ingen - diff --git a/src/libs/gui/NodePropertiesWindow.hpp b/src/libs/gui/NodePropertiesWindow.hpp deleted file mode 100644 index 248c8b42..00000000 --- a/src/libs/gui/NodePropertiesWindow.hpp +++ /dev/null @@ -1,58 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef NODEPROPERTIESWINDOW_H -#define NODEPROPERTIESWINDOW_H - -#include -#include -#include -#include "client/NodeModel.hpp" -using namespace Ingen::Client; - -namespace Ingen { -namespace GUI { - - -/** Node properties window. - * - * Loaded by libglade as a derived object. - * - * \ingroup GUI - */ -class NodePropertiesWindow : public Gtk::Window -{ -public: - NodePropertiesWindow(BaseObjectType* cobject, const Glib::RefPtr& refGlade); - - void present(SharedPtr node_model) { set_node(node_model); Gtk::Window::present(); } - void set_node(SharedPtr node_model); - -private: - - SharedPtr _node_model; - Gtk::Label* _node_path_label; - Gtk::CheckButton* _node_polyphonic_toggle; - Gtk::Label* _plugin_type_label; - Gtk::Label* _plugin_uri_label; - Gtk::Label* _plugin_name_label; -}; - -} // namespace GUI -} // namespace Ingen - -#endif // NODEPROPERTIESWINDOW_H diff --git a/src/libs/gui/ObjectMenu.cpp b/src/libs/gui/ObjectMenu.cpp deleted file mode 100644 index becbf964..00000000 --- a/src/libs/gui/ObjectMenu.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include "interface/EngineInterface.hpp" -#include "client/ObjectModel.hpp" -#include "App.hpp" -#include "ObjectMenu.hpp" -#include "WindowFactory.hpp" - -namespace Ingen { -namespace GUI { - - -ObjectMenu::ObjectMenu(BaseObjectType* cobject, const Glib::RefPtr& xml) - : Gtk::Menu(cobject) - , _enable_signal(false) - , _polyphonic_menuitem(NULL) - , _disconnect_menuitem(NULL) - , _rename_menuitem(NULL) - , _destroy_menuitem(NULL) - , _properties_menuitem(NULL) -{ - xml->get_widget("object_polyphonic_menuitem", _polyphonic_menuitem); - xml->get_widget("object_disconnect_menuitem", _disconnect_menuitem); - xml->get_widget("object_rename_menuitem", _rename_menuitem); - xml->get_widget("object_destroy_menuitem", _destroy_menuitem); - xml->get_widget("object_properties_menuitem", _properties_menuitem); -} - - -void -ObjectMenu::init(SharedPtr object) -{ - _object = object; - - App& app = App::instance(); - - _polyphonic_menuitem->signal_toggled().connect( - sigc::mem_fun(this, &ObjectMenu::on_menu_polyphonic)); - - _polyphonic_menuitem->set_active(object->polyphonic()); - - _disconnect_menuitem->signal_activate().connect( - sigc::mem_fun(this, &ObjectMenu::on_menu_disconnect)); - - _rename_menuitem->signal_activate().connect(sigc::bind( - sigc::mem_fun(app.window_factory(), &WindowFactory::present_rename), - object)); - - _destroy_menuitem->signal_activate().connect( - sigc::mem_fun(this, &ObjectMenu::on_menu_destroy)); - - _properties_menuitem->signal_activate().connect( - sigc::mem_fun(this, &ObjectMenu::on_menu_properties)); - - object->signal_property.connect(sigc::mem_fun(this, &ObjectMenu::property_changed)); - - _enable_signal = true; -} - - -void -ObjectMenu::on_menu_polyphonic() -{ - if (_enable_signal) - App::instance().engine()->set_property( - _object->path(), "ingen:polyphonic", _polyphonic_menuitem->get_active()); -} - - -void -ObjectMenu::property_changed(const std::string& predicate, const Raul::Atom& value) -{ - _enable_signal = false; - if (predicate == "ingen:polyphonic" && value.type() == Atom::BOOL) - _polyphonic_menuitem->set_active(value.get_bool()); - _enable_signal = true; -} - - -void -ObjectMenu::on_menu_destroy() -{ - App::instance().engine()->destroy(_object->path()); -} - - -void -ObjectMenu::on_menu_properties() -{ - App::instance().window_factory()->present_properties(_object); -} - - -} // namespace GUI -} // namespace Ingen - diff --git a/src/libs/gui/ObjectMenu.hpp b/src/libs/gui/ObjectMenu.hpp deleted file mode 100644 index b5c5bf49..00000000 --- a/src/libs/gui/ObjectMenu.hpp +++ /dev/null @@ -1,69 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef OBJECTMENU_H -#define OBJECTMENU_H - -#include -#include -#include -#include -#include -#include "client/ObjectModel.hpp" -using Ingen::Client::ObjectModel; - -namespace Ingen { -namespace GUI { - -class ObjectControlWindow; -class ObjectPropertiesWindow; -class PatchCanvas; - -/** Menu for a Object. - * - * \ingroup GUI - */ -class ObjectMenu : public Gtk::Menu -{ -public: - ObjectMenu(BaseObjectType* cobject, const Glib::RefPtr& xml); - - void init(SharedPtr object); - -protected: - - virtual void on_menu_disconnect() = 0; - void on_menu_polyphonic(); - void on_menu_destroy(); - void on_menu_properties(); - - void property_changed(const std::string& predicate, const Raul::Atom& value); - - bool _enable_signal; - SharedPtr _object; - Gtk::CheckMenuItem* _polyphonic_menuitem; - Gtk::MenuItem* _disconnect_menuitem; - Gtk::MenuItem* _rename_menuitem; - Gtk::MenuItem* _destroy_menuitem; - Gtk::MenuItem* _properties_menuitem; -}; - - -} // namespace GUI -} // namespace Ingen - -#endif // OBJECTMENU_H diff --git a/src/libs/gui/PatchCanvas.cpp b/src/libs/gui/PatchCanvas.cpp deleted file mode 100644 index 4f01a7f7..00000000 --- a/src/libs/gui/PatchCanvas.cpp +++ /dev/null @@ -1,773 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include CONFIG_H_PATH -#include "module/global.hpp" -#include "module/World.hpp" - -#include -#include -#include -#include "interface/EngineInterface.hpp" -#include "shared/Builder.hpp" -#include "shared/ClashAvoider.hpp" -#include "serialisation/Serialiser.hpp" -#include "client/PluginModel.hpp" -#include "client/PatchModel.hpp" -#include "client/NodeModel.hpp" -#include "client/ClientStore.hpp" -#include "App.hpp" -#include "PatchCanvas.hpp" -#include "PatchWindow.hpp" -#include "PatchPortModule.hpp" -#include "LoadPluginWindow.hpp" -#include "LoadSubpatchWindow.hpp" -#include "NewSubpatchWindow.hpp" -#include "Port.hpp" -#include "Connection.hpp" -#include "NodeModule.hpp" -#include "SubpatchModule.hpp" -#include "GladeFactory.hpp" -#include "WindowFactory.hpp" -#include "ThreadedLoader.hpp" -using Ingen::Client::ClientStore; -using Ingen::Serialisation::Serialiser; -using Ingen::Client::PluginModel; -using namespace std; - -namespace Ingen { -namespace GUI { - - -PatchCanvas::PatchCanvas(SharedPtr patch, int width, int height) - : Canvas(width, height) - , _patch(patch) - , _last_click_x(0) - , _last_click_y(0) - , _refresh_menu(false) - , _human_names(true) - , _menu(NULL) - , _internal_menu(NULL) - , _plugin_menu(NULL) -{ - Glib::RefPtr xml = GladeFactory::new_glade_reference(); - xml->get_widget("canvas_menu", _menu); - - /*xml->get_widget("canvas_menu_add_number_control", _menu_add_number_control); - xml->get_widget("canvas_menu_add_button_control", _menu_add_button_control);*/ - xml->get_widget("canvas_menu_add_audio_input", _menu_add_audio_input); - xml->get_widget("canvas_menu_add_audio_output", _menu_add_audio_output); - xml->get_widget("canvas_menu_add_control_input", _menu_add_control_input); - xml->get_widget("canvas_menu_add_control_output", _menu_add_control_output); - xml->get_widget("canvas_menu_add_midi_input", _menu_add_midi_input); - xml->get_widget("canvas_menu_add_midi_output", _menu_add_midi_output); - xml->get_widget("canvas_menu_add_osc_input", _menu_add_osc_input); - xml->get_widget("canvas_menu_add_osc_output", _menu_add_osc_output); - xml->get_widget("canvas_menu_add_event_input", _menu_add_event_input); - xml->get_widget("canvas_menu_add_event_output", _menu_add_event_output); - xml->get_widget("canvas_menu_load_plugin", _menu_load_plugin); - xml->get_widget("canvas_menu_load_patch", _menu_load_patch); - xml->get_widget("canvas_menu_new_patch", _menu_new_patch); - - // Add port menu items - _menu_add_audio_input->signal_activate().connect( - sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port), - "audio_input", "ingen:AudioPort", false)); - _menu_add_audio_output->signal_activate().connect( - sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port), - "audio_output", "ingen:AudioPort", true)); - _menu_add_control_input->signal_activate().connect( - sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port), - "control_input", "ingen:ControlPort", false)); - _menu_add_control_output->signal_activate().connect( - sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port), - "control_output", "ingen:ControlPort", true)); - _menu_add_midi_input->signal_activate().connect( - sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port), - "midi_input", "ingen:MIDIPort", false)); - _menu_add_midi_output->signal_activate().connect( - sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port), - "midi_output", "ingen:MIDIPort", true)); - _menu_add_osc_input->signal_activate().connect( - sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port), - "osc_input", "ingen:OSCPort", false)); - _menu_add_osc_output->signal_activate().connect( - sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port), - "osc_output", "ingen:OSCPort", true)); - _menu_add_event_input->signal_activate().connect( - sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port), - "event_input", "ingen:EventPort", false)); - _menu_add_event_output->signal_activate().connect( - sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port), - "event_output", "ingen:EventPort", true)); - - // Add control menu items - /*_menu_add_number_control->signal_activate().connect( - sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_control), NUMBER)); - _menu_add_button_control->signal_activate().connect( - sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_control), BUTTON));*/ - - // Connect to model signals to track state - _patch->signal_new_node.connect(sigc::mem_fun(this, &PatchCanvas::add_node)); - _patch->signal_removed_node.connect(sigc::mem_fun(this, &PatchCanvas::remove_node)); - _patch->signal_new_port.connect(sigc::mem_fun(this, &PatchCanvas::add_port)); - _patch->signal_removed_port.connect(sigc::mem_fun(this, &PatchCanvas::remove_port)); - _patch->signal_new_connection.connect(sigc::mem_fun(this, &PatchCanvas::connection)); - _patch->signal_removed_connection.connect(sigc::mem_fun(this, &PatchCanvas::disconnection)); - - App::instance().store()->signal_new_plugin.connect(sigc::mem_fun(this, &PatchCanvas::add_plugin)); - - // Connect widget signals to do things - _menu_load_plugin->signal_activate().connect(sigc::mem_fun(this, &PatchCanvas::menu_load_plugin)); - _menu_load_patch->signal_activate().connect(sigc::mem_fun(this, &PatchCanvas::menu_load_patch)); - _menu_new_patch->signal_activate().connect(sigc::mem_fun(this, &PatchCanvas::menu_new_patch)); -} - - -void -PatchCanvas::show_menu(GdkEvent* event) -{ - if (!_internal_menu || !_plugin_menu || _refresh_menu) { - build_internal_menu(); -#ifdef HAVE_SLV2 - build_plugin_menu(); -#endif - _refresh_menu = false; - } - _menu->popup(event->button.button, event->button.time); -} - - -void -PatchCanvas::build_internal_menu() -{ - if (_internal_menu) { - _internal_menu->items().clear(); - } else { - _menu->items().push_back(Gtk::Menu_Helpers::ImageMenuElem("Internal", - *(manage(new Gtk::Image(Gtk::Stock::EXECUTE, Gtk::ICON_SIZE_MENU))))); - Gtk::MenuItem* internal_menu_item = &(_menu->items().back()); - _internal_menu = Gtk::manage(new Gtk::Menu()); - internal_menu_item->set_submenu(*_internal_menu); - _menu->reorder_child(*internal_menu_item, 2); - } - - SharedPtr plugins = App::instance().store()->plugins(); - - // Add Internal plugins - for (ClientStore::Plugins::const_iterator i = plugins->begin(); i != plugins->end(); ++i) { - SharedPtr p = i->second; - if (p->type() == Plugin::Internal) { - _internal_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(p->name(), - sigc::bind(sigc::mem_fun(this, &PatchCanvas::load_plugin), p))); - } - } -} - - -#ifdef HAVE_SLV2 -size_t -PatchCanvas::build_plugin_class_menu(Gtk::Menu* menu, - SLV2PluginClass plugin_class, SLV2PluginClasses classes) -{ - size_t num_items = 0; - - // Add submenus - for (unsigned i=0; i < slv2_plugin_classes_size(classes); ++i) { - SLV2PluginClass c = slv2_plugin_classes_get_at(classes, i); - SLV2Value parent = slv2_plugin_class_get_parent_uri(c); - - if (parent && slv2_value_equals(parent, slv2_plugin_class_get_uri(plugin_class))) { - Gtk::Menu_Helpers::MenuElem menu_elem = Gtk::Menu_Helpers::MenuElem( - slv2_value_as_string(slv2_plugin_class_get_label(c))); - - Gtk::Menu* submenu = Gtk::manage(new Gtk::Menu()); - size_t sub_num_items = build_plugin_class_menu(submenu, c, classes); - - if (sub_num_items > 0) { - menu->items().push_back(menu_elem); - Gtk::MenuItem* menu_item = &(menu->items().back()); - menu_item->set_submenu(*submenu); - ++num_items; - } - } - } - - SharedPtr plugins = App::instance().store()->plugins(); - - // Add LV2 plugins - for (ClientStore::Plugins::const_iterator i = plugins->begin(); i != plugins->end(); ++i) { - SLV2Plugin p = i->second->slv2_plugin(); - - if (p && slv2_plugin_get_class(p) == plugin_class) { - Glib::RefPtr icon - = App::instance().icon_from_path(PluginModel::get_lv2_icon_path(p), 16); - if (icon) { - Gtk::Image* image = new Gtk::Image(icon); - menu->items().push_back(Gtk::Menu_Helpers::ImageMenuElem(i->second->name(), - *image, - sigc::bind(sigc::mem_fun(this, &PatchCanvas::load_plugin), i->second))); - } else { - menu->items().push_back(Gtk::Menu_Helpers::MenuElem(i->second->name(), - sigc::bind(sigc::mem_fun(this, &PatchCanvas::load_plugin), i->second))); - ++num_items; - } - } - } - - return num_items; -} - - -void -PatchCanvas::build_plugin_menu() -{ - if (_plugin_menu) { - _plugin_menu->items().clear(); - } else { - _menu->items().push_back(Gtk::Menu_Helpers::ImageMenuElem("Plugin", - *(manage(new Gtk::Image(Gtk::Stock::EXECUTE, Gtk::ICON_SIZE_MENU))))); - Gtk::MenuItem* plugin_menu_item = &(_menu->items().back()); - _plugin_menu = Gtk::manage(new Gtk::Menu()); - plugin_menu_item->set_submenu(*_plugin_menu); - _menu->reorder_child(*plugin_menu_item, 3); - } - - Glib::Mutex::Lock lock(PluginModel::rdf_world()->mutex()); - SLV2PluginClass lv2_plugin = slv2_world_get_plugin_class(PluginModel::slv2_world()); - SLV2PluginClasses classes = slv2_world_get_plugin_classes(PluginModel::slv2_world()); - - build_plugin_class_menu(_plugin_menu, lv2_plugin, classes); -} -#endif - - -void -PatchCanvas::build() -{ - boost::shared_ptr shared_this = - boost::dynamic_pointer_cast(shared_from_this()); - - // Create modules for nodes - for (ObjectModel::const_iterator i = App::instance().store()->children_begin(_patch); - i != App::instance().store()->children_end(_patch); ++i) { - SharedPtr node = PtrCast(i->second); - if (node && node->parent() == _patch) - add_node(node); - } - - // Create pseudo modules for ports (ports on this canvas, not on our module) - for (NodeModel::Ports::const_iterator i = _patch->ports().begin(); - i != _patch->ports().end(); ++i) { - add_port(*i); - } - - // Create connections - for (PatchModel::Connections::const_iterator i = _patch->connections().begin(); - i != _patch->connections().end(); ++i) { - connection(PtrCast(*i)); - } -} - - -void -PatchCanvas::arrange(bool ingen_doesnt_use_length_hints) -{ - FlowCanvas::Canvas::arrange(false); - - for (list >::iterator i = _items.begin(); i != _items.end(); ++i) - (*i)->store_location(); -} - - -void -PatchCanvas::show_human_names(bool b) -{ - _human_names = b; - for (ItemList::iterator m = _items.begin(); m != _items.end(); ++m) { - boost::shared_ptr mod = boost::dynamic_pointer_cast(*m); - if (mod) - mod->show_human_names(b); - } -} - - -void -PatchCanvas::add_plugin(SharedPtr pm) -{ - _refresh_menu = true; -} - - -void -PatchCanvas::add_node(SharedPtr nm) -{ - boost::shared_ptr shared_this = - boost::dynamic_pointer_cast(shared_from_this()); - - SharedPtr pm = PtrCast(nm); - SharedPtr module; - if (pm) { - module = SubpatchModule::create(shared_this, pm, _human_names); - } else { - module = NodeModule::create(shared_this, nm, _human_names); - const PluginModel* plugm = dynamic_cast(nm->plugin()); - if (plugm && plugm->icon_path() != "") - module->set_icon(App::instance().icon_from_path(plugm->icon_path(), 100)); - } - - add_item(module); - module->show(); - _views.insert(std::make_pair(nm, module)); -} - - -void -PatchCanvas::remove_node(SharedPtr nm) -{ - Views::iterator i = _views.find(nm); - - if (i != _views.end()) { - remove_item(i->second); - _views.erase(i); - } -} - - -void -PatchCanvas::add_port(SharedPtr pm) -{ - boost::shared_ptr shared_this = - boost::dynamic_pointer_cast(shared_from_this()); - - SharedPtr view = PatchPortModule::create(shared_this, pm); - _views.insert(std::make_pair(pm, view)); - add_item(view); - view->show(); -} - - -void -PatchCanvas::remove_port(SharedPtr pm) -{ - Views::iterator i = _views.find(pm); - - if (i != _views.end()) { - remove_item(i->second); - _views.erase(i); - } -} - - -SharedPtr -PatchCanvas::get_port_view(SharedPtr port) -{ - SharedPtr module = _views[port]; - - // Port on this patch - if (module) { - return (PtrCast(module)) - ? *(PtrCast(module)->ports().begin()) - : PtrCast(module); - } else { - module = PtrCast(_views[port->parent()]); - if (module) { - for (PortVector::const_iterator p = module->ports().begin(); - p != module->ports().end(); ++p) { - boost::shared_ptr pv = boost::dynamic_pointer_cast(*p); - if (pv && pv->model() == port) - return pv; - } - } - } - - return SharedPtr(); -} - - -void -PatchCanvas::connection(SharedPtr cm) -{ - assert(cm); - - const SharedPtr src = get_port_view(cm->src_port()); - const SharedPtr dst = get_port_view(cm->dst_port()); - - if (src && dst) { - add_connection(boost::shared_ptr(new GUI::Connection(shared_from_this(), - cm, src, dst, src->color() + 0x22222200))); - } else { - cerr << "[PatchCanvas] ERROR: Unable to find ports to connect " - << cm->src_port_path() << " -> " << cm->dst_port_path() << endl; - } -} - - -void -PatchCanvas::disconnection(SharedPtr cm) -{ - const SharedPtr src = get_port_view(cm->src_port()); - const SharedPtr dst = get_port_view(cm->dst_port()); - - if (src && dst) - remove_connection(src, dst); - else - cerr << "[PatchCanvas] ERROR: Unable to find ports to disconnect " - << cm->src_port_path() << " -> " << cm->dst_port_path() << endl; -} - - -void -PatchCanvas::connect(boost::shared_ptr src_port, - boost::shared_ptr dst_port) -{ - const boost::shared_ptr src - = boost::dynamic_pointer_cast(src_port); - - const boost::shared_ptr dst - = boost::dynamic_pointer_cast(dst_port); - - if (!src || !dst) - return; - - // Midi binding/learn shortcut - if (src->model()->type().is_event() && dst->model()->type().is_control()) { - cerr << "[PatchCanvas] FIXME: MIDI binding shortcut" << endl; -#if 0 - SharedPtr pm(new PluginModel(PluginModel::Internal, "", "midi_control_in", "")); - SharedPtr nm(new NodeModel(pm, _patch->path().base() - + src->name() + "-" + dst->name(), false)); - nm->set_variable("canvas-x", Atom((float) - (dst->module()->property_x() - dst->module()->width() - 20))); - nm->set_variable("canvas-y", Atom((float) - (dst->module()->property_y()))); - App::instance().engine()->create_node_from_model(nm.get()); - App::instance().engine()->connect(src->model()->path(), nm->path() + "/MIDI_In"); - App::instance().engine()->connect(nm->path() + "/Out_(CR)", dst->model()->path()); - App::instance().engine()->midi_learn(nm->path()); - - // Set control node range to port's user range - - App::instance().engine()->set_port_value_queued(nm->path().base() + "Min", - dst->model()->get_variable("user-min").get_float()); - App::instance().engine()->set_port_value_queued(nm->path().base() + "Max", - dst->model()->get_variable("user-max").get_float()); -#endif - } else { - App::instance().engine()->connect(src->model()->path(), dst->model()->path()); - } -} - - -void -PatchCanvas::disconnect(boost::shared_ptr src_port, - boost::shared_ptr dst_port) -{ - const boost::shared_ptr src - = boost::dynamic_pointer_cast(src_port); - - const boost::shared_ptr dst - = boost::dynamic_pointer_cast(dst_port); - - App::instance().engine()->disconnect(src->model()->path(), - dst->model()->path()); -} - - -bool -PatchCanvas::canvas_event(GdkEvent* event) -{ - assert(event); - - bool ret = false; - - switch (event->type) { - - case GDK_BUTTON_PRESS: - if (event->button.button == 3) { - _last_click_x = (int)event->button.x; - _last_click_y = (int)event->button.y; - show_menu(event); - ret = true; - } - break; - - case GDK_KEY_PRESS: - case GDK_KEY_RELEASE: - ret = canvas_key_event(&event->key); - - default: - break; - } - - return (ret ? true : Canvas::canvas_event(event)); -} - - -bool -PatchCanvas::canvas_key_event(GdkEventKey* event) -{ - switch (event->type) { - case GDK_KEY_PRESS: - switch (event->keyval) { - case GDK_Delete: - destroy_selection(); - return true; - case GDK_e: - if (event->state == 0) { - if (_patch->get_editable() == true) - _patch->set_editable(false); - else - _patch->set_editable(true); - return true; - } else { - return false; - } - default: - return false; - } - default: - return false; - } -} - - -void -PatchCanvas::destroy_selection() -{ - for (list >::iterator m = _selected_items.begin(); m != _selected_items.end(); ++m) { - boost::shared_ptr module = boost::dynamic_pointer_cast(*m); - if (module) { - App::instance().engine()->destroy(module->node()->path()); - } else { - boost::shared_ptr port_module = boost::dynamic_pointer_cast(*m); - if (port_module) - App::instance().engine()->destroy(port_module->port()->path()); - } - } -} - -void -PatchCanvas::select_all() -{ - unselect_ports(); - for (list >::iterator m = _items.begin(); m != _items.end(); ++m) - if (boost::dynamic_pointer_cast(*m)) - if (!(*m)->selected()) - select_item(*m); -} - -void -PatchCanvas::copy_selection() -{ - Serialiser serialiser(*App::instance().world(), App::instance().store()); - serialiser.start_to_string(_patch->path(), "http://example.org/"); - - for (list >::iterator m = _selected_items.begin(); m != _selected_items.end(); ++m) { - boost::shared_ptr module = boost::dynamic_pointer_cast(*m); - if (module) { - serialiser.serialise(module->node()); - } else { - boost::shared_ptr port_module = boost::dynamic_pointer_cast(*m); - if (port_module) - serialiser.serialise(port_module->port()); - } - } - - for (list >::iterator c = _selected_connections.begin(); - c != _selected_connections.end(); ++c) { - boost::shared_ptr connection = boost::dynamic_pointer_cast(*c); - if (connection) - serialiser.serialise_connection(_patch, connection->model()); - } - - string result = serialiser.finish(); - - Glib::RefPtr clipboard = Gtk::Clipboard::get(); - clipboard->set_text(result); -} - - -void -PatchCanvas::paste() -{ - Glib::ustring str = Gtk::Clipboard::get()->wait_for_text(); - SharedPtr parser = App::instance().loader()->parser(); - if (!parser) { - cerr << "Unable to load parser, paste unavailable" << endl; - return; - } - - clear_selection(); - - Builder builder(*App::instance().engine()); - ClientStore clipboard; - clipboard.set_plugins(App::instance().store()->plugins()); - clipboard.new_patch("/", _patch->poly()); - - ClashAvoider avoider(*App::instance().store().get(), _patch->path(), clipboard, &clipboard); - parser->parse_string(App::instance().world(), &avoider, str, "/", _patch->path()); - - for (Store::iterator i = clipboard.begin(); i != clipboard.end(); ++i) { - cout << "************ OBJECT: " << i->first << endl; - if (_patch->path() == "/" && i->first == "/") { - //cout << "SKIPPING ROOT " << _patch->path() << " :: " << i->first << endl; - continue; - } else if (i->first.parent() != "/") { - //cout << "SKIPPING NON ROOTED OBJECT " << i->first << endl; - continue; - } - GraphObject::Variables::iterator x = i->second->variables().find("ingenuity:canvas-x"); - if (x != i->second->variables().end()) - x->second = x->second.get_float() + 20.0f; - GraphObject::Variables::iterator y = i->second->variables().find("ingenuity:canvas-y"); - if (y != i->second->variables().end()) - y->second = y->second.get_float() + 20.0f; - if (i->first.parent() == "/") { - GraphObject::Properties::iterator s = i->second->properties().find("ingen:selected"); - if (s != i->second->properties().end()) - s->second = true; - else - i->second->properties().insert(make_pair("ingen:selected", true)); - } - builder.build(_patch->path(), i->second); - } - - //avoider.set_target(*App::instance().engine()); - - for (ClientStore::ConnectionRecords::const_iterator i = clipboard.connection_records().begin(); - i != clipboard.connection_records().end(); ++i) { - cout << "CONNECTING " << i->first << " -> " << i->second << endl; - App::instance().engine()->connect(i->first, i->second); - } -} - - -string -PatchCanvas::generate_port_name(const string& base) -{ - string name = base; - - char num_buf[5]; - for (uint i=1; i < 9999; ++i) { - snprintf(num_buf, 5, "%u", i); - name = base + "_"; - name += num_buf; - if (!_patch->get_port(name)) - break; - } - - assert(Path::is_valid(string("/") + name)); - - return name; -} - -void -PatchCanvas::menu_add_control(ControlType type) -{ - // FIXME: bundleify - - GraphObject::Variables data = get_initial_data(); - float x = data["ingenuity:canvas-x"].get_float(); - float y = data["ingenuity:canvas-y"].get_float(); - - cerr << "ADD CONTROL: " << (unsigned)type << " @ " << x << ", " << y << endl; - - add_item(boost::shared_ptr( - new FlowCanvas::Ellipse(shared_from_this(), "control", x, y, 20, 20, true))); -} - -void -PatchCanvas::menu_add_port(const string& name, const string& type, bool is_output) -{ - const Path& path = _patch->path().base() + generate_port_name(name); - App::instance().engine()->bundle_begin(); - App::instance().engine()->new_port(path, _patch->num_ports(), type, is_output); - GraphObject::Variables data = get_initial_data(); - for (GraphObject::Variables::const_iterator i = data.begin(); i != data.end(); ++i) - App::instance().engine()->set_variable(path, i->first, i->second); - App::instance().engine()->bundle_end(); -} - - -void -PatchCanvas::load_plugin(SharedPtr plugin) -{ - string name = plugin->default_node_symbol(); - unsigned offset = App::instance().store()->child_name_offset(_patch->path(), name); - if (offset != 0) { - std::stringstream ss; - ss << name << "_" << offset; - name = ss.str(); - } - - const Path path = _patch->path().base() + name; - // FIXME: polyphony? - App::instance().engine()->new_node(path, plugin->uri()); - GraphObject::Variables data = get_initial_data(); - for (GraphObject::Variables::const_iterator i = data.begin(); i != data.end(); ++i) - App::instance().engine()->set_variable(path, i->first, i->second); -} - - -/** Try to guess a suitable location for a new module. - */ -void -PatchCanvas::get_new_module_location(double& x, double& y) -{ - int scroll_x; - int scroll_y; - get_scroll_offsets(scroll_x, scroll_y); - x = scroll_x + 20; - y = scroll_y + 20; -} - - -GraphObject::Variables -PatchCanvas::get_initial_data() -{ - GraphObject::Variables result; - - result["ingenuity:canvas-x"] = Atom((float)_last_click_x); - result["ingenuity:canvas-y"] = Atom((float)_last_click_y); - - return result; -} - -void -PatchCanvas::menu_load_plugin() -{ - App::instance().window_factory()->present_load_plugin(_patch, get_initial_data()); -} - - -void -PatchCanvas::menu_load_patch() -{ - App::instance().window_factory()->present_load_subpatch(_patch, get_initial_data()); -} - - -void -PatchCanvas::menu_new_patch() -{ - App::instance().window_factory()->present_new_subpatch(_patch, get_initial_data()); -} - - -} // namespace GUI -} // namespace Ingen diff --git a/src/libs/gui/PatchCanvas.hpp b/src/libs/gui/PatchCanvas.hpp deleted file mode 100644 index fcf68e76..00000000 --- a/src/libs/gui/PatchCanvas.hpp +++ /dev/null @@ -1,155 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef PATCHCANVAS_H -#define PATCHCANVAS_H - -#include CONFIG_H_PATH - -#include -#include -#include -#include -#include -#include -#include -#include "client/ConnectionModel.hpp" -#include "client/PatchModel.hpp" -#include "interface/GraphObject.hpp" -#include "NodeModule.hpp" - -using namespace FlowCanvas; -using namespace Ingen::Shared; - -using std::string; -using FlowCanvas::Port; -using Ingen::Client::ConnectionModel; -using Ingen::Client::PatchModel; -using Ingen::Client::NodeModel; -using Ingen::Client::PortModel; - -namespace Ingen { -namespace GUI { - -class NodeModule; - - -/** Patch canvas widget. - * - * \ingroup GUI - */ -class PatchCanvas : public FlowCanvas::Canvas -{ -public: - PatchCanvas(SharedPtr patch, int width, int height); - - virtual ~PatchCanvas() {} - - /*boost::shared_ptr find_module(const string& name) { - return boost::dynamic_pointer_cast( - Canvas::get_item(name)); - }*/ - - void build(); - void arrange(bool use_length_hints); - void show_human_names(bool show); - - void add_plugin(SharedPtr pm); - void add_node(SharedPtr nm); - void remove_node(SharedPtr nm); - void add_port(SharedPtr pm); - void remove_port(SharedPtr pm); - void connection(SharedPtr cm); - void disconnection(SharedPtr cm); - - void get_new_module_location(double& x, double& y); - - void destroy_selection(); - void copy_selection(); - void paste(); - void select_all(); - - void show_menu(GdkEvent* event); - - bool canvas_key_event(GdkEventKey* event); - -private: - enum ControlType { NUMBER, BUTTON }; - void menu_add_control(ControlType type); - - string generate_port_name(const string& base); - void menu_add_port(const string& name, const string& type, bool is_output); - - void menu_load_plugin(); - void menu_new_patch(); - void menu_load_patch(); - void load_plugin(SharedPtr plugin); - - void build_internal_menu(); -#ifdef HAVE_SLV2 - void build_plugin_menu(); - size_t build_plugin_class_menu(Gtk::Menu* menu, - SLV2PluginClass plugin_class, SLV2PluginClasses classes); -#endif - - GraphObject::Variables get_initial_data(); - - bool canvas_event(GdkEvent* event); - - SharedPtr get_port_view(SharedPtr port); - - void connect(boost::shared_ptr src, - boost::shared_ptr dst); - - void disconnect(boost::shared_ptr src, - boost::shared_ptr dst); - - SharedPtr _patch; - - typedef std::map, SharedPtr > Views; - Views _views; - - int _last_click_x; - int _last_click_y; - - bool _refresh_menu; - bool _human_names; - Gtk::Menu* _menu; - Gtk::Menu* _internal_menu; - Gtk::Menu* _plugin_menu; - /*Gtk::MenuItem* _menu_add_number_control; - Gtk::MenuItem* _menu_add_button_control;*/ - Gtk::MenuItem* _menu_add_audio_input; - Gtk::MenuItem* _menu_add_audio_output; - Gtk::MenuItem* _menu_add_control_input; - Gtk::MenuItem* _menu_add_control_output; - Gtk::MenuItem* _menu_add_event_input; - Gtk::MenuItem* _menu_add_event_output; - Gtk::MenuItem* _menu_add_midi_input; - Gtk::MenuItem* _menu_add_midi_output; - Gtk::MenuItem* _menu_add_osc_input; - Gtk::MenuItem* _menu_add_osc_output; - Gtk::MenuItem* _menu_load_plugin; - Gtk::MenuItem* _menu_load_patch; - Gtk::MenuItem* _menu_new_patch; -}; - - -} // namespace GUI -} // namespace Ingen - -#endif // PATCHCANVAS_H diff --git a/src/libs/gui/PatchPortModule.cpp b/src/libs/gui/PatchPortModule.cpp deleted file mode 100644 index d8aaa91d..00000000 --- a/src/libs/gui/PatchPortModule.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include "PatchPortModule.hpp" -#include "interface/EngineInterface.hpp" -#include "client/PatchModel.hpp" -#include "client/NodeModel.hpp" -#include "App.hpp" -#include "PatchCanvas.hpp" -#include "Port.hpp" -#include "GladeFactory.hpp" -#include "RenameWindow.hpp" -#include "PatchWindow.hpp" -#include "WindowFactory.hpp" -#include "PortMenu.hpp" - -namespace Ingen { -namespace GUI { - - -PatchPortModule::PatchPortModule(boost::shared_ptr canvas, SharedPtr port) -: FlowCanvas::Module(canvas, port->path().name(), 0, 0, false), // FIXME: coords? - _port(port) -{ - assert(canvas); - assert(port); - - assert(PtrCast(port->parent())); - - /*resize(); - - const Atom& x_atom = port->get_variable("ingenuity:canvas-x"); - const Atom& y_atom = port->get_variable("ingenuity:canvas-y"); - - if (x_atom && y_atom && x_atom.type() == Atom::FLOAT && y_atom.type() == Atom::FLOAT) { - move_to(x_atom.get_float(), y_atom.get_float()); - } else { - double default_x; - double default_y; - canvas->get_new_module_location(default_x, default_y); - move_to(default_x, default_y); - }*/ - - set_stacked_border(port->polyphonic()); - - port->signal_variable.connect(sigc::mem_fun(this, &PatchPortModule::set_variable)); - port->signal_property.connect(sigc::mem_fun(this, &PatchPortModule::set_property)); -} - - -boost::shared_ptr -PatchPortModule::create(boost::shared_ptr canvas, SharedPtr port) -{ - boost::shared_ptr ret = boost::shared_ptr( - new PatchPortModule(canvas, port)); - assert(ret); - - ret->_patch_port = boost::shared_ptr(new Port(ret, port, port->symbol(), true)); - - ret->add_port(ret->_patch_port); - - ret->set_menu(ret->_patch_port->menu()); - - for (GraphObject::Variables::const_iterator m = port->variables().begin(); m != port->variables().end(); ++m) - ret->set_variable(m->first, m->second); - - for (GraphObject::Properties::const_iterator m = port->properties().begin(); m != port->properties().end(); ++m) - ret->set_property(m->first, m->second); - - ret->resize(); - - return ret; -} - - -void -PatchPortModule::create_menu() -{ - Glib::RefPtr xml = GladeFactory::new_glade_reference(); - xml->get_widget_derived("object_menu", _menu); - _menu->init(_port, true); - - set_menu(_menu); -} - - -void -PatchPortModule::store_location() -{ - const float x = static_cast(property_x()); - const float y = static_cast(property_y()); - - const Atom& existing_x = _port->get_variable("ingenuity:canvas-x"); - const Atom& existing_y = _port->get_variable("ingenuity:canvas-y"); - - if (existing_x.type() != Atom::FLOAT || existing_y.type() != Atom::FLOAT - || existing_x.get_float() != x || existing_y.get_float() != y) { - App::instance().engine()->set_variable(_port->path(), "ingenuity:canvas-x", Atom(x)); - App::instance().engine()->set_variable(_port->path(), "ingenuity:canvas-y", Atom(y)); - } -} - - -void -PatchPortModule::set_variable(const string& key, const Atom& value) -{ - if (key == "ingenuity:canvas-x" && value.type() == Atom::FLOAT) - move_to(value.get_float(), property_y()); - else if (key == "ingenuity:canvas-y" && value.type() == Atom::FLOAT) - move_to(property_x(), value.get_float()); -} - - -void -PatchPortModule::set_property(const string& key, const Atom& value) -{ - if (key == "ingen:polyphonic" && value.type() == Atom::BOOL) { - set_stacked_border(value.get_bool()); - } else if (key == "ingen:selected" && value.type() == Atom::BOOL) { - if (value.get_bool() != selected()) { - if (value.get_bool()) - _canvas.lock()->select_item(shared_from_this()); - else - _canvas.lock()->unselect_item(shared_from_this()); - } - } -} - - -void -PatchPortModule::set_selected(bool b) -{ - if (b != selected()) { - Module::set_selected(b); - if (App::instance().signal()) - App::instance().engine()->set_property(_port->path(), "ingen:selected", b); - } -} - - - -} // namespace GUI -} // namespace Ingen diff --git a/src/libs/gui/PatchPortModule.hpp b/src/libs/gui/PatchPortModule.hpp deleted file mode 100644 index d6715834..00000000 --- a/src/libs/gui/PatchPortModule.hpp +++ /dev/null @@ -1,79 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef PATCHPORTMODULE_H -#define PATCHPORTMODULE_H - -#include -#include -#include -#include -#include -#include "Port.hpp" -using std::string; - -namespace Ingen { namespace Client { - class PortModel; - class NodeModel; -} } -using namespace Ingen::Client; - -namespace Ingen { -namespace GUI { - -class PatchCanvas; -class Port; -class PortMenu; - - -/** A "module" to represent a patch's port on it's own canvas. - * - * Translation: This is the nameless single port pseudo module thingy. - * - * \ingroup GUI - */ -class PatchPortModule : public FlowCanvas::Module -{ -public: - static boost::shared_ptr create(boost::shared_ptr canvas, - SharedPtr port); - - virtual ~PatchPortModule() {} - - virtual void store_location(); - - SharedPtr port() const { return _port; } - -protected: - PatchPortModule(boost::shared_ptr canvas, SharedPtr port); - - void create_menu(); - void set_selected(bool b); - - void set_variable(const std::string& predicate, const Raul::Atom& value); - void set_property(const std::string& predicate, const Raul::Atom& value); - - SharedPtr _port; - PortMenu* _menu; - SharedPtr _patch_port; ///< Port on this 'anonymous' module -}; - - -} // namespace GUI -} // namespace Ingen - -#endif // PATCHPORTMODULE_H diff --git a/src/libs/gui/PatchPropertiesWindow.cpp b/src/libs/gui/PatchPropertiesWindow.cpp deleted file mode 100644 index f4003d31..00000000 --- a/src/libs/gui/PatchPropertiesWindow.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include "client/PatchModel.hpp" -#include "PatchPropertiesWindow.hpp" -#include "App.hpp" - -using namespace std; - -namespace Ingen { -namespace GUI { - - -PatchPropertiesWindow::PatchPropertiesWindow(BaseObjectType* cobject, const Glib::RefPtr& glade_xml) -: Gtk::Window(cobject) -{ - glade_xml->get_widget("properties_author_entry", _author_entry); - glade_xml->get_widget("properties_description_textview", _textview); - glade_xml->get_widget("properties_cancel_button", _cancel_button); - glade_xml->get_widget("properties_ok_button", _ok_button); - - _cancel_button->signal_clicked().connect(sigc::mem_fun(this, &PatchPropertiesWindow::cancel_clicked)); - _ok_button->signal_clicked().connect(sigc::mem_fun(this, &PatchPropertiesWindow::ok_clicked)); -} - - -/** Set the patch model this description is for. - * - * This function is a "post-constructor" - it MUST be called before using - * the window in any way. - */ -void -PatchPropertiesWindow::set_patch(SharedPtr patch_model) -{ - property_title() = patch_model->path() + " Properties"; - _patch_model = patch_model; - - const Atom& author_atom = _patch_model->get_variable("dc:creator"); - _author_entry->set_text( - (author_atom.type() == Atom::STRING) ? author_atom.get_string() : "" ); - - const Atom& desc_atom = _patch_model->get_variable("dc:description"); - _textview->get_buffer()->set_text( - (desc_atom.type() == Atom::STRING) ? desc_atom.get_string() : "" ); -} - - -void -PatchPropertiesWindow::cancel_clicked() -{ - const Atom& author_atom = _patch_model->get_variable("dc:creator"); - _author_entry->set_text( - (author_atom.type() == Atom::STRING) ? author_atom.get_string() : "" ); - - const Atom& desc_atom = _patch_model->get_variable("dc:description"); - _textview->get_buffer()->set_text( - (desc_atom.type() == Atom::STRING) ? desc_atom.get_string() : "" ); - - hide(); -} - - -void -PatchPropertiesWindow::ok_clicked() -{ - App::instance().engine()->set_variable(_patch_model->path(), "dc:creator", - Atom(_author_entry->get_text())); - App::instance().engine()->set_variable(_patch_model->path(), "dc:description", - Atom(_textview->get_buffer()->get_text())); - hide(); -} - - - -} // namespace GUI -} // namespace Ingen diff --git a/src/libs/gui/PatchPropertiesWindow.hpp b/src/libs/gui/PatchPropertiesWindow.hpp deleted file mode 100644 index 0f60b147..00000000 --- a/src/libs/gui/PatchPropertiesWindow.hpp +++ /dev/null @@ -1,64 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef PATCHPROPERTIESWINDOW_H -#define PATCHPROPERTIESWINDOW_H - -#include -#include -#include -#include -using std::string; - -namespace Ingen { namespace Client { class PatchModel; } } -using Ingen::Client::PatchModel; - -namespace Ingen { -namespace GUI { - - -/** Patch Properties Window. - * - * Loaded by libglade as a derived object. - * - * \ingroup GUI - */ -class PatchPropertiesWindow : public Gtk::Window -{ -public: - PatchPropertiesWindow(BaseObjectType* cobject, const Glib::RefPtr& refGlade); - - void present(SharedPtr patch_model) { set_patch(patch_model); Gtk::Window::present(); } - void set_patch(SharedPtr patch_model); - - void cancel_clicked(); - void ok_clicked(); - -private: - SharedPtr _patch_model; - - Gtk::Entry* _author_entry; - Gtk::TextView* _textview; - Gtk::Button* _cancel_button; - Gtk::Button* _ok_button; -}; - - -} // namespace GUI -} // namespace Ingen - -#endif // PATCHPROPERTIESWINDOW_H diff --git a/src/libs/gui/PatchTreeWindow.cpp b/src/libs/gui/PatchTreeWindow.cpp deleted file mode 100644 index 4730da2b..00000000 --- a/src/libs/gui/PatchTreeWindow.cpp +++ /dev/null @@ -1,243 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include "interface/EngineInterface.hpp" -#include "client/OSCEngineSender.hpp" -#include "client/ClientStore.hpp" -#include "client/PatchModel.hpp" -#include "App.hpp" -#include "PatchTreeWindow.hpp" -#include "SubpatchModule.hpp" -#include "WindowFactory.hpp" - -using namespace std; - -namespace Ingen { -namespace GUI { - - -PatchTreeWindow::PatchTreeWindow(BaseObjectType* cobject, - const Glib::RefPtr& xml) - : Gtk::Window(cobject) - , _enable_signal(true) -{ - xml->get_widget_derived("patches_treeview", _patches_treeview); - - _patch_treestore = Gtk::TreeStore::create(_patch_tree_columns); - _patches_treeview->set_window(this); - _patches_treeview->set_model(_patch_treestore); - Gtk::TreeViewColumn* name_col = Gtk::manage(new Gtk::TreeViewColumn( - "Patch", _patch_tree_columns.name_col)); - Gtk::TreeViewColumn* enabled_col = Gtk::manage(new Gtk::TreeViewColumn( - "Run", _patch_tree_columns.enabled_col)); - name_col->set_resizable(true); - name_col->set_expand(true); - - _patches_treeview->append_column(*name_col); - _patches_treeview->append_column(*enabled_col); - Gtk::CellRendererToggle* enabled_renderer = dynamic_cast( - _patches_treeview->get_column_cell_renderer(1)); - enabled_renderer->property_activatable() = true; - - _patch_tree_selection = _patches_treeview->get_selection(); - - //m_patch_tree_selection->signal_changed().connect( - // sigc::mem_fun(this, &PatchTreeWindow::event_patch_selected)); - _patches_treeview->signal_row_activated().connect( - sigc::mem_fun(this, &PatchTreeWindow::event_patch_activated)); - enabled_renderer->signal_toggled().connect( - sigc::mem_fun(this, &PatchTreeWindow::event_patch_enabled_toggled)); - - _patches_treeview->columns_autosize(); -} - - -void -PatchTreeWindow::init(ClientStore& store) -{ - store.signal_new_object.connect(sigc::mem_fun(this, &PatchTreeWindow::new_object)); -} - - -void -PatchTreeWindow::new_object(SharedPtr object) -{ - SharedPtr patch = PtrCast(object); - if (patch) - add_patch(patch); -} - - -void -PatchTreeWindow::add_patch(SharedPtr pm) -{ - if (!pm->parent()) { - Gtk::TreeModel::iterator iter = _patch_treestore->append(); - Gtk::TreeModel::Row row = *iter; - if (pm->path() == "/") { - SharedPtr osc_sender = PtrCast(App::instance().engine()); - string root_name = osc_sender ? osc_sender->uri() : "Internal"; - // Hack off trailing '/' if it's there (ugly) - //if (root_name.substr(root_name.length()-1,1) == "/") - // root_name = root_name.substr(0, root_name.length()-1); - //root_name.append(":/"); - row[_patch_tree_columns.name_col] = root_name; - } else { - row[_patch_tree_columns.name_col] = pm->path().name(); - } - row[_patch_tree_columns.enabled_col] = false; - row[_patch_tree_columns.patch_model_col] = pm; - _patches_treeview->expand_row(_patch_treestore->get_path(iter), true); - } else { - Gtk::TreeModel::Children children = _patch_treestore->children(); - Gtk::TreeModel::iterator c = find_patch(children, pm->parent()->path()); - - if (c != children.end()) { - Gtk::TreeModel::iterator iter = _patch_treestore->append(c->children()); - Gtk::TreeModel::Row row = *iter; - row[_patch_tree_columns.name_col] = pm->path().name(); - row[_patch_tree_columns.enabled_col] = false; - row[_patch_tree_columns.patch_model_col] = pm; - _patches_treeview->expand_row(_patch_treestore->get_path(iter), true); - } - } - - pm->signal_property.connect(sigc::bind(sigc::mem_fun(this, &PatchTreeWindow::patch_property_changed), pm->path())); -} - - -void -PatchTreeWindow::remove_patch(const Path& path) -{ - Gtk::TreeModel::iterator i = find_patch(_patch_treestore->children(), path); - if (i != _patch_treestore->children().end()) - _patch_treestore->erase(i); -} - - -Gtk::TreeModel::iterator -PatchTreeWindow::find_patch(Gtk::TreeModel::Children root, const Path& path) -{ - for (Gtk::TreeModel::iterator c = root.begin(); c != root.end(); ++c) { - SharedPtr pm = (*c)[_patch_tree_columns.patch_model_col]; - if (pm->path() == path) { - return c; - } else if ((*c)->children().size() > 0) { - Gtk::TreeModel::iterator ret = find_patch(c->children(), path); - if (ret != c->children().end()) - return ret; - } - } - return root.end(); -} - -/* -void -PatchTreeWindow::event_patch_selected() -{ - Gtk::TreeModel::iterator active = _patch_tree_selection->get_selected(); - if (active) { - Gtk::TreeModel::Row row = *active; - SharedPtr pm = row[_patch_tree_columns.patch_model_col]; - } -} -*/ - - -/** Show the context menu for the selected patch in the patches treeview. - */ -void -PatchTreeWindow::show_patch_menu(GdkEventButton* ev) -{ - Gtk::TreeModel::iterator active = _patch_tree_selection->get_selected(); - if (active) { - Gtk::TreeModel::Row row = *active; - SharedPtr pm = row[_patch_tree_columns.patch_model_col]; - if (pm) - cerr << "FIXME: patch menu\n"; - //pm->show_menu(ev); - } -} - - -void -PatchTreeWindow::event_patch_activated(const Gtk::TreeModel::Path& path, Gtk::TreeView::Column* col) -{ - Gtk::TreeModel::iterator active = _patch_treestore->get_iter(path); - Gtk::TreeModel::Row row = *active; - SharedPtr pm = row[_patch_tree_columns.patch_model_col]; - - App::instance().window_factory()->present_patch(pm); -} - - -void -PatchTreeWindow::event_patch_enabled_toggled(const Glib::ustring& path_str) -{ - Gtk::TreeModel::Path path(path_str); - Gtk::TreeModel::iterator active = _patch_treestore->get_iter(path); - Gtk::TreeModel::Row row = *active; - - SharedPtr pm = row[_patch_tree_columns.patch_model_col]; - Glib::ustring patch_path = pm->path(); - - assert(pm); - - if (_enable_signal) - App::instance().engine()->set_property(patch_path, "ingen:enabled", (bool)!pm->enabled()); -} - - -void -PatchTreeWindow::patch_property_changed(const string& key, const Raul::Atom& value, const Path& path) -{ - _enable_signal = false; - if (key == "ingen:enabled" && value.type() == Atom::BOOL) { - Gtk::TreeModel::iterator i = find_patch(_patch_treestore->children(), path); - if (i != _patch_treestore->children().end()) { - Gtk::TreeModel::Row row = *i; - row[_patch_tree_columns.enabled_col] = value.get_bool(); - } else { - cerr << "[PatchTreeWindow] Unable to find patch " << path << endl; - } - } - _enable_signal = true; -} - - -void -PatchTreeWindow::patch_renamed(const Path& old_path, const Path& new_path) -{ - _enable_signal = false; - - Gtk::TreeModel::iterator i - = find_patch(_patch_treestore->children(), old_path); - - if (i != _patch_treestore->children().end()) { - Gtk::TreeModel::Row row = *i; - row[_patch_tree_columns.name_col] = new_path.name(); - } else { - cerr << "[PatchTreeWindow] Unable to find patch " << old_path << endl; - } - - _enable_signal = true; -} - - -} // namespace GUI -} // namespace Ingen diff --git a/src/libs/gui/PatchTreeWindow.hpp b/src/libs/gui/PatchTreeWindow.hpp deleted file mode 100644 index f549a322..00000000 --- a/src/libs/gui/PatchTreeWindow.hpp +++ /dev/null @@ -1,111 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef PATCHTREEWINDOW_H -#define PATCHTREEWINDOW_H - -#include -#include -#include - -namespace Ingen { namespace Client { - class ClientStore; -} } -using Ingen::Client::ClientStore; - -namespace Ingen { -namespace GUI { - -class PatchWindow; -class PatchTreeView; - - -/** Window with a TreeView of all loaded patches. - * - * \ingroup GUI - */ -class PatchTreeWindow : public Gtk::Window -{ -public: - PatchTreeWindow(BaseObjectType* cobject, const Glib::RefPtr& refGlade); - - void init(ClientStore& store); - - void new_object(SharedPtr object); - - void patch_property_changed(const string& key, const Raul::Atom& value, const Path& path); - void patch_renamed(const Path& old_path, const Path& new_path); - - void add_patch(SharedPtr pm); - void remove_patch(const Path& path); - void show_patch_menu(GdkEventButton* ev); - -protected: - //void event_patch_selected(); - void event_patch_activated(const Gtk::TreeModel::Path& path, Gtk::TreeView::Column* col); - void event_patch_enabled_toggled(const Glib::ustring& path_str); - - Gtk::TreeModel::iterator find_patch(Gtk::TreeModel::Children root, const Path& path); - - PatchTreeView* _patches_treeview; - - struct PatchTreeModelColumns : public Gtk::TreeModel::ColumnRecord - { - PatchTreeModelColumns() - { add(name_col); add(enabled_col); add(patch_model_col); } - - Gtk::TreeModelColumn name_col; - Gtk::TreeModelColumn enabled_col; - Gtk::TreeModelColumn > patch_model_col; - }; - - bool _enable_signal; - PatchTreeModelColumns _patch_tree_columns; - Glib::RefPtr _patch_treestore; - Glib::RefPtr _patch_tree_selection; -}; - - -/** Derived TreeView class to support context menus for patches */ -class PatchTreeView : public Gtk::TreeView -{ -public: - PatchTreeView(BaseObjectType* cobject, const Glib::RefPtr& xml) - : Gtk::TreeView(cobject) - {} - - void set_window(PatchTreeWindow* win) { _window = win; } - - bool on_button_press_event(GdkEventButton* ev) { - bool ret = Gtk::TreeView::on_button_press_event(ev); - - if ((ev->type == GDK_BUTTON_PRESS) && (ev->button == 3)) - _window->show_patch_menu(ev); - - return ret; - } - -private: - PatchTreeWindow* _window; - -}; // struct PatchTreeView - - -} // namespace GUI -} // namespace Ingen - -#endif // PATCHTREEWINDOW_H diff --git a/src/libs/gui/PatchView.cpp b/src/libs/gui/PatchView.cpp deleted file mode 100644 index 13689806..00000000 --- a/src/libs/gui/PatchView.cpp +++ /dev/null @@ -1,194 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include "interface/EngineInterface.hpp" -#include "client/PatchModel.hpp" -#include "App.hpp" -#include "PatchView.hpp" -#include "PatchCanvas.hpp" -#include "LoadPluginWindow.hpp" -#include "NewSubpatchWindow.hpp" -#include "LoadSubpatchWindow.hpp" -#include "NodeControlWindow.hpp" -#include "PatchPropertiesWindow.hpp" -#include "PatchTreeWindow.hpp" -#include "GladeFactory.hpp" - -namespace Ingen { -namespace GUI { - - -PatchView::PatchView(BaseObjectType* cobject, const Glib::RefPtr& xml) -: Gtk::Box(cobject), - _breadcrumb_container(NULL), - _enable_signal(true) -{ - property_visible() = false; - - xml->get_widget("patch_view_breadcrumb_container", _breadcrumb_container); - xml->get_widget("patch_view_toolbar", _toolbar); - xml->get_widget("patch_view_process_but", _process_but); - xml->get_widget("patch_view_poly_spin", _poly_spin); - xml->get_widget("patch_view_clear_but", _clear_but); - xml->get_widget("patch_view_destroy_but", _destroy_but); - xml->get_widget("patch_view_refresh_but", _refresh_but); - xml->get_widget("patch_view_save_but", _save_but); - xml->get_widget("patch_view_zoom_full_but", _zoom_full_but); - xml->get_widget("patch_view_zoom_normal_but", _zoom_normal_but); - xml->get_widget("patch_view_edit_mode_but", _edit_mode_but); - xml->get_widget("patch_view_scrolledwindow", _canvas_scrolledwindow); - - _toolbar->set_toolbar_style(Gtk::TOOLBAR_ICONS); - _canvas_scrolledwindow->property_hadjustment().get_value()->set_step_increment(10); - _canvas_scrolledwindow->property_vadjustment().get_value()->set_step_increment(10); - -} - - -void -PatchView::set_patch(SharedPtr patch) -{ - assert(!_canvas); // FIXME: remove - - //cerr << "Creating view for " << patch->path() << endl; - - assert(_breadcrumb_container); // ensure created - - _patch = patch; - _canvas = SharedPtr(new PatchCanvas(patch, 1600*2, 1200*2)); - _canvas->build(); - - _canvas_scrolledwindow->add(*_canvas); - - _poly_spin->set_value(patch->poly()); - _destroy_but->set_sensitive(patch->path() != "/"); - - for (GraphObject::Properties::const_iterator i = patch->properties().begin(); - i != patch->properties().end(); ++i) - property_changed(i->first, i->second); - - // Connect model signals to track state - patch->signal_property.connect(sigc::mem_fun(this, &PatchView::property_changed)); - - // Connect widget signals to do things - _process_but->signal_toggled().connect(sigc::mem_fun(this, &PatchView::process_toggled)); - _clear_but->signal_clicked().connect(sigc::mem_fun(this, &PatchView::clear_clicked)); - _refresh_but->signal_clicked().connect(sigc::mem_fun(this, &PatchView::refresh_clicked)); - - _zoom_normal_but->signal_clicked().connect(sigc::bind(sigc::mem_fun( - _canvas.get(), &PatchCanvas::set_zoom), 1.0)); - - _zoom_full_but->signal_clicked().connect( - sigc::mem_fun(_canvas.get(), &PatchCanvas::zoom_full)); - - patch->signal_editable.connect(sigc::mem_fun( - *this, &PatchView::on_editable_sig)); - - _edit_mode_but->signal_toggled().connect(sigc::mem_fun( - *this, &PatchView::editable_toggled)); - - _poly_spin->signal_value_changed().connect( - sigc::mem_fun(*this, &PatchView::poly_changed)); - - _canvas->grab_focus(); -} - - -PatchView::~PatchView() -{ - //cerr << "Destroying view for " << _patch->path() << endl; -} - - -SharedPtr -PatchView::create(SharedPtr patch) - -{ - const Glib::RefPtr& xml = GladeFactory::new_glade_reference("patch_view_box"); - PatchView* result = NULL; - xml->get_widget_derived("patch_view_box", result); - assert(result); - result->set_patch(patch); - return SharedPtr(result); -} - - -void -PatchView::on_editable_sig(bool editable) -{ - _edit_mode_but->set_active(editable); - _canvas->lock(!editable); -} - - -void -PatchView::editable_toggled() -{ - const bool editable = _edit_mode_but->get_active(); - _patch->set_editable(editable); - _canvas->lock(!editable); -} - - -void -PatchView::process_toggled() -{ - if (!_enable_signal) - return; - - App::instance().engine()->set_property(_patch->path(), "ingen:enabled", - (bool)_process_but->get_active()); -} - - -void -PatchView::poly_changed() -{ - App::instance().engine()->set_property(_patch->path(), "ingen:polyphony", - _poly_spin->get_value_as_int()); -} - - -void -PatchView::clear_clicked() -{ - App::instance().engine()->clear_patch(_patch->path()); -} - - -void -PatchView::refresh_clicked() -{ - App::instance().engine()->request_object(_patch->path()); -} - - -void -PatchView::property_changed(const std::string& predicate, const Raul::Atom& value) -{ - _enable_signal = false; - if (predicate == "ingen:enabled" && value.type() == Atom::BOOL) - _process_but->set_active(value.get_bool()); - _enable_signal = true; -} - - -} // namespace GUI -} // namespace Ingen diff --git a/src/libs/gui/PatchView.hpp b/src/libs/gui/PatchView.hpp deleted file mode 100644 index 2c0570bd..00000000 --- a/src/libs/gui/PatchView.hpp +++ /dev/null @@ -1,105 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef PATCHVIEW_H -#define PATCHVIEW_H - -#include -#include -#include -#include -#include -#include "client/PatchModel.hpp" - -using std::string; - -namespace Ingen { namespace Client { - class PortModel; - class MetadataModel; -} } -using namespace Ingen::Client; - - -namespace Ingen { -namespace GUI { - -class PatchCanvas; -class LoadPluginWindow; -class NewSubpatchWindow; -class LoadSubpatchWindow; -class NewSubpatchWindow; -class NodeControlWindow; -class PatchDescriptionWindow; -class SubpatchModule; -class OmPort; - - -/** The patch specific contents of a PatchWindow (ie the canvas and whatever else). - * - * \ingroup GUI - */ -class PatchView : public Gtk::Box -{ -public: - PatchView(BaseObjectType* cobject, const Glib::RefPtr& glade_xml); - ~PatchView(); - - SharedPtr canvas() const { return _canvas; } - SharedPtr patch() const { return _patch; } - Gtk::Viewport* breadcrumb_container() const { return _breadcrumb_container; } - - static SharedPtr create(SharedPtr patch); - -private: - void set_patch(SharedPtr patch); - - void process_toggled(); - void poly_changed(); - void clear_clicked(); - void refresh_clicked(); - void on_editable_sig(bool locked); - void editable_toggled(); - - void property_changed(const std::string& predicate, const Raul::Atom& value); - - void zoom_full(); - - SharedPtr _patch; - SharedPtr _canvas; - - Gtk::ScrolledWindow* _canvas_scrolledwindow; - - Gtk::Toolbar* _toolbar; - Gtk::ToggleToolButton* _process_but; - Gtk::SpinButton* _poly_spin; - Gtk::ToolButton* _clear_but; - Gtk::ToolButton* _destroy_but; - Gtk::ToolButton* _refresh_but; - Gtk::ToolButton* _save_but; - Gtk::ToolButton* _zoom_normal_but; - Gtk::ToolButton* _zoom_full_but; - Gtk::ToggleToolButton* _edit_mode_but; - Gtk::Viewport* _breadcrumb_container; - - bool _enable_signal; -}; - - -} // namespace GUI -} // namespace Ingen - -#endif // PATCHVIEW_H diff --git a/src/libs/gui/PatchWindow.cpp b/src/libs/gui/PatchWindow.cpp deleted file mode 100644 index d7499bfb..00000000 --- a/src/libs/gui/PatchWindow.cpp +++ /dev/null @@ -1,556 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "PatchWindow.hpp" -#include -#include -#include -#include "interface/EngineInterface.hpp" -#include "client/PatchModel.hpp" -#include "client/ClientStore.hpp" -#include "App.hpp" -#include "PatchCanvas.hpp" -#include "LoadPluginWindow.hpp" -#include "NewSubpatchWindow.hpp" -#include "LoadPatchWindow.hpp" -#include "LoadSubpatchWindow.hpp" -#include "NodeControlWindow.hpp" -#include "PatchPropertiesWindow.hpp" -#include "Configuration.hpp" -#include "MessagesWindow.hpp" -#include "PatchTreeWindow.hpp" -#include "BreadCrumbBox.hpp" -#include "ConnectWindow.hpp" -#include "ThreadedLoader.hpp" -#include "WindowFactory.hpp" -#include "PatchView.hpp" - -namespace Ingen { -namespace GUI { - - -PatchWindow::PatchWindow(BaseObjectType* cobject, const Glib::RefPtr& xml) - : Gtk::Window(cobject) - , _enable_signal(true) - , _position_stored(false) - , _x(0) - , _y(0) - , _breadcrumb_box(NULL) -{ - property_visible() = false; - - xml->get_widget("patch_win_vbox", _vbox); - xml->get_widget("patch_win_viewport", _viewport); - //xml->get_widget("patch_win_status_bar", _status_bar); - //xml->get_widget("patch_open_menuitem", _menu_open); - xml->get_widget("patch_import_menuitem", _menu_import); - xml->get_widget("patch_import_location_menuitem", _menu_import_location); - //xml->get_widget("patch_open_into_menuitem", _menu_open_into); - xml->get_widget("patch_save_menuitem", _menu_save); - xml->get_widget("patch_save_as_menuitem", _menu_save_as); - xml->get_widget("patch_upload_menuitem", _menu_upload); - xml->get_widget("patch_cut_menuitem", _menu_cut); - xml->get_widget("patch_copy_menuitem", _menu_copy); - xml->get_widget("patch_paste_menuitem", _menu_paste); - xml->get_widget("patch_delete_menuitem", _menu_delete); - xml->get_widget("patch_select_all_menuitem", _menu_select_all); - xml->get_widget("patch_close_menuitem", _menu_close); - xml->get_widget("patch_quit_menuitem", _menu_quit); - xml->get_widget("patch_view_control_window_menuitem", _menu_view_control_window); - xml->get_widget("patch_view_engine_window_menuitem", _menu_view_engine_window); - xml->get_widget("patch_properties_menuitem", _menu_view_patch_properties); - xml->get_widget("patch_fullscreen_menuitem", _menu_fullscreen); - xml->get_widget("patch_human_names_menuitem", _menu_human_names); - xml->get_widget("patch_arrange_menuitem", _menu_arrange); - xml->get_widget("patch_clear_menuitem", _menu_clear); - xml->get_widget("patch_destroy_menuitem", _menu_destroy_patch); - xml->get_widget("patch_view_messages_window_menuitem", _menu_view_messages_window); - xml->get_widget("patch_view_patch_tree_window_menuitem", _menu_view_patch_tree_window); - xml->get_widget("patch_help_about_menuitem", _menu_help_about); - - _menu_view_control_window->property_sensitive() = false; - //m_status_bar->push(App::instance().engine()->engine_url()); - //m_status_bar->pack_start(*Gtk::manage(new Gtk::Image(Gtk::Stock::CONNECT, Gtk::ICON_SIZE_MENU)), false, false); - - /*_menu_open->signal_activate().connect( - sigc::mem_fun(this, &PatchWindow::event_open));*/ - _menu_import->signal_activate().connect( - sigc::mem_fun(this, &PatchWindow::event_import)); - _menu_import_location->signal_activate().connect( - sigc::mem_fun(this, &PatchWindow::event_import_location)); - _menu_save->signal_activate().connect( - sigc::mem_fun(this, &PatchWindow::event_save)); - _menu_save_as->signal_activate().connect( - sigc::mem_fun(this, &PatchWindow::event_save_as)); - _menu_upload->signal_activate().connect( - sigc::mem_fun(this, &PatchWindow::event_upload)); - _menu_copy->signal_activate().connect( - sigc::mem_fun(this, &PatchWindow::event_copy)); - _menu_paste->signal_activate().connect( - sigc::mem_fun(this, &PatchWindow::event_paste)); - _menu_delete->signal_activate().connect( - sigc::mem_fun(this, &PatchWindow::event_delete)); - _menu_select_all->signal_activate().connect( - sigc::mem_fun(this, &PatchWindow::event_select_all)); - _menu_quit->signal_activate().connect( - sigc::mem_fun(this, &PatchWindow::event_quit)); - _menu_fullscreen->signal_activate().connect( - sigc::mem_fun(this, &PatchWindow::event_fullscreen_toggled)); - _menu_human_names->signal_activate().connect( - sigc::mem_fun(this, &PatchWindow::event_human_names_toggled)); - _menu_arrange->signal_activate().connect( - sigc::mem_fun(this, &PatchWindow::event_arrange)); - _menu_view_engine_window->signal_activate().connect( - sigc::mem_fun(this, &PatchWindow::event_show_engine)); - _menu_view_control_window->signal_activate().connect( - sigc::mem_fun(this, &PatchWindow::event_show_controls)); - _menu_view_patch_properties->signal_activate().connect( - sigc::mem_fun(this, &PatchWindow::event_show_properties)); - _menu_destroy_patch->signal_activate().connect( - sigc::mem_fun(this, &PatchWindow::event_destroy)); - _menu_clear->signal_activate().connect( - sigc::mem_fun(this, &PatchWindow::event_clear)); - _menu_view_messages_window->signal_activate().connect( - sigc::mem_fun(App::instance().messages_dialog(), &MessagesWindow::present)); - _menu_view_patch_tree_window->signal_activate().connect( - sigc::mem_fun(App::instance().patch_tree(), &PatchTreeWindow::present)); - - _menu_help_about->signal_activate().connect(sigc::hide_return( - sigc::mem_fun(App::instance(), &App::show_about))); - - _breadcrumb_box = new BreadCrumbBox(); - _breadcrumb_box->signal_patch_selected.connect(sigc::mem_fun(this, &PatchWindow::set_patch_from_path)); - -#ifndef HAVE_CURL - _menu_upload->hide(); -#endif - - Glib::RefPtr clipboard = Gtk::Clipboard::get(); - clipboard->signal_owner_change().connect(sigc::mem_fun(this, &PatchWindow::event_clipboard_changed)); -} - - -PatchWindow::~PatchWindow() -{ - // Prevents deletion - //m_patch->claim_patch_view(); - - delete _breadcrumb_box; -} - - -/** Set the patch controller from a Path (for use by eg. BreadCrumbBox) - */ -void -PatchWindow::set_patch_from_path(const Path& path, SharedPtr view) -{ - if (view) { - assert(view->patch()->path() == path); - App::instance().window_factory()->present_patch(view->patch(), this, view); - } else { - SharedPtr model = PtrCast(App::instance().store()->object(path)); - if (model) - App::instance().window_factory()->present_patch(model, this); - } -} - - -/** Sets the patch controller for this window and initializes everything. - * - * If @a view is NULL, a new view will be created. - */ -void -PatchWindow::set_patch(SharedPtr patch, SharedPtr view) -{ - if (!patch || patch == _patch) - return; - - _enable_signal = false; - - new_port_connection.disconnect(); - removed_port_connection.disconnect(); - - _patch = patch; - - _view = view; - - if (!_view) - _view = _breadcrumb_box->view(patch->path()); - - if (!_view) - _view = PatchView::create(patch); - - assert(_view); - - // Add view to our viewport - if (_view->get_parent()) - _view->get_parent()->remove(*_view.get()); - - _viewport->remove(); - _viewport->add(*_view.get()); - - - if (_breadcrumb_box->get_parent()) - _breadcrumb_box->get_parent()->remove(*_breadcrumb_box); - - _view->breadcrumb_container()->remove(); - _view->breadcrumb_container()->add(*_breadcrumb_box); - _view->breadcrumb_container()->show(); - - _breadcrumb_box->build(patch->path(), _view); - _breadcrumb_box->show(); - - _menu_view_control_window->property_sensitive() = false; - - for (NodeModel::Ports::const_iterator p = patch->ports().begin(); - p != patch->ports().end(); ++p) { - if ((*p)->type().is_control() && (*p)->is_input()) { - _menu_view_control_window->property_sensitive() = true; - break; - } - } - - int width, height; - get_size(width, height); - _view->canvas()->scroll_to( - ((int)_view->canvas()->width() - width)/2, - ((int)_view->canvas()->height() - height)/2); - - set_title(_patch->path() + " - Ingen"); - - //m_properties_window->patch_model(pc->patch_model()); - - if (patch->path() == "/") - _menu_destroy_patch->set_sensitive(false); - else - _menu_destroy_patch->set_sensitive(true); - - new_port_connection = patch->signal_new_port.connect(sigc::mem_fun(this, &PatchWindow::patch_port_added)); - removed_port_connection = patch->signal_removed_port.connect(sigc::mem_fun(this, &PatchWindow::patch_port_removed)); - show_all(); - - _enable_signal = true; -} - - -void -PatchWindow::patch_port_added(SharedPtr port) -{ - if (port->type().is_control() && port->is_input()) { - _menu_view_control_window->property_sensitive() = true; - } -} - - -void -PatchWindow::patch_port_removed(SharedPtr port) -{ - if (port->type().is_control() && port->is_input()) { - - bool found_control = false; - - for (NodeModel::Ports::const_iterator i = _patch->ports().begin(); i != _patch->ports().end(); ++i) { - if ((*i)->type().is_control() && (*i)->is_input()) { - found_control = true; - break; - } - } - - _menu_view_control_window->property_sensitive() = found_control; - } -} - - - -void -PatchWindow::event_show_engine() -{ - if (_patch) - App::instance().connect_window()->show(); -} - - -void -PatchWindow::event_clipboard_changed(GdkEventOwnerChange* ev) -{ - Glib::RefPtr clipboard = Gtk::Clipboard::get(); - _menu_paste->set_sensitive(clipboard->wait_is_text_available()); -} - - -void -PatchWindow::event_show_controls() -{ - App::instance().window_factory()->present_controls(_patch); -} - - -void -PatchWindow::event_show_properties() -{ - App::instance().window_factory()->present_properties(_patch); -} - - -void -PatchWindow::event_import() -{ - App::instance().window_factory()->present_load_patch(_patch); -} - - -void -PatchWindow::event_import_location() -{ - App::instance().window_factory()->present_load_remote_patch(_patch); -} - - -void -PatchWindow::event_save() -{ - GraphObject::Variables::const_iterator doc = _patch->variables().find("ingen:document"); - if (doc == _patch->variables().end()) - event_save_as(); - else - App::instance().loader()->save_patch(_patch, doc->second.get_string()); -} - - -void -PatchWindow::event_save_as() -{ - Gtk::FileChooserDialog dialog(*this, "Save Patch", Gtk::FILE_CHOOSER_ACTION_SAVE); - - /*Gtk::VBox* box = dialog.get_vbox(); - Gtk::Label warning("Warning: Recursively saving will overwrite any subpatch files \ - without confirmation."); - box->pack_start(warning, false, false, 2); - Gtk::CheckButton recursive_checkbutton("Recursively save all subpatches"); - box->pack_start(recursive_checkbutton, false, false, 0); - recursive_checkbutton.show(); - */ - - dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); - Gtk::Button* save_button = dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK); - save_button->property_has_default() = true; - - // Set current folder to most sensible default - GraphObject::Variables::const_iterator doc = _patch->variables().find("ingen:document"); - if (doc != _patch->variables().end()) - dialog.set_uri(doc->second.get_string()); - else if (App::instance().configuration()->patch_folder().length() > 0) - dialog.set_current_folder(App::instance().configuration()->patch_folder()); - - int result = dialog.run(); - //bool recursive = recursive_checkbutton.get_active(); - - if (result == Gtk::RESPONSE_OK) { - string filename = dialog.get_filename(); - if (filename.length() < 11 || filename.substr(filename.length()-10) != ".ingen.ttl") - filename += ".ingen.ttl"; - - bool confirm = false; - std::fstream fin; - fin.open(filename.c_str(), std::ios::in); - if (fin.is_open()) { // File exists - string msg = "File already exists! Are you sure you want to overwrite "; - msg += filename + "?"; - Gtk::MessageDialog confirm_dialog(*this, - msg, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_YES_NO, true); - if (confirm_dialog.run() == Gtk::RESPONSE_YES) - confirm = true; - else - confirm = false; - } else { // File doesn't exist - confirm = true; - } - fin.close(); - - if (confirm) { - App::instance().loader()->save_patch(_patch, filename); - } - } - App::instance().configuration()->set_patch_folder(dialog.get_current_folder()); -} - - -void -PatchWindow::event_upload() -{ - App::instance().window_factory()->present_upload_patch(_patch); -} - - -void -PatchWindow::event_copy() -{ - if (_view) - _view->canvas()->copy_selection(); -} - - -void -PatchWindow::event_paste() -{ - if (_view) - _view->canvas()->paste(); -} - - -void -PatchWindow::event_delete() -{ - if (_view) - _view->canvas()->destroy_selection(); -} - - -void -PatchWindow::event_select_all() -{ - if (_view) - _view->canvas()->select_all(); -} - - -void -PatchWindow::on_show() -{ - if (_position_stored) - move(_x, _y); - - Gtk::Window::on_show(); -} - - -void -PatchWindow::on_hide() -{ - _position_stored = true; - get_position(_x, _y); - Gtk::Window::on_hide(); -} - - -bool -PatchWindow::on_key_press_event(GdkEventKey* event) -{ - bool ret = false; - - ret = _view->canvas()->canvas_key_event(event); - - if (!ret) - ret = Gtk::Window::on_key_press_event(event); - - return ret; -} - - -bool -PatchWindow::on_key_release_event(GdkEventKey* event) -{ - bool ret = false; - - ret = _view->canvas()->canvas_key_event(event); - - if (!ret) - ret = Gtk::Window::on_key_release_event(event); - - return ret; -} - - -void -PatchWindow::event_quit() -{ - Gtk::MessageDialog d(*this, "Would you like to quit just this GUI\nor kill the engine as well?", - true, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_NONE, true); - d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); - - Gtk::Button* b = d.add_button(Gtk::Stock::REMOVE, 2); // kill - b->set_label("_Kill Engine"); - Gtk::Widget* kill_img = Gtk::manage(new Gtk::Image(Gtk::Stock::CLOSE, Gtk::ICON_SIZE_BUTTON)); - b->set_image(*kill_img); - - b = d.add_button(Gtk::Stock::QUIT, 1); // just exit - b->set_label("_Quit"); - Gtk::Widget* close_img = Gtk::manage(new Gtk::Image(Gtk::Stock::QUIT, Gtk::ICON_SIZE_BUTTON)); - b->set_image(*close_img); - b->grab_default(); - - int ret = d.run(); - if (ret == 1) { - App::instance().quit(); - } else if (ret == 2) { - App::instance().engine()->quit(); - App::instance().quit(); - } - // Otherwise cancelled, do nothing -} - - -void -PatchWindow::event_destroy() -{ - App::instance().engine()->destroy(_patch->path()); -} - - -void -PatchWindow::event_clear() -{ - App::instance().engine()->clear_patch(_patch->path()); -} - - -void -PatchWindow::event_arrange() -{ - _view->canvas()->arrange(false); -} - - -void -PatchWindow::event_fullscreen_toggled() -{ - // FIXME: ugh, use GTK signals to track state and know for sure - static bool is_fullscreen = false; - - if (!is_fullscreen) { - fullscreen(); - is_fullscreen = true; - } else { - unfullscreen(); - is_fullscreen = false; - } -} - - -void -PatchWindow::event_human_names_toggled() -{ - _view->canvas()->show_human_names(_menu_human_names->get_active()); - if (_menu_human_names->get_active()) - App::instance().configuration()->set_name_style(Configuration::HUMAN); - else - App::instance().configuration()->set_name_style(Configuration::PATH); -} - - -} // namespace GUI -} // namespace Ingen diff --git a/src/libs/gui/PatchWindow.hpp b/src/libs/gui/PatchWindow.hpp deleted file mode 100644 index 4d4f0f1e..00000000 --- a/src/libs/gui/PatchWindow.hpp +++ /dev/null @@ -1,155 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef PATCHWINDOW_H -#define PATCHWINDOW_H - -#include -#include -#include -#include -#include -#include -#include -#include "client/PatchModel.hpp" -#include "PatchView.hpp" -using Ingen::Client::PatchModel; - -using std::string; using std::list; - - -namespace Ingen { namespace Client { - class PatchModel; - class PortModel; - class MetadataModel; -} } -using namespace Ingen::Client; - - -namespace Ingen { -namespace GUI { - -class LoadPluginWindow; -class LoadPatchWindow; -class NewSubpatchWindow; -class LoadSubpatchWindow; -class NewSubpatchWindow; -class NodeControlWindow; -class PatchDescriptionWindow; -class SubpatchModule; -class OmPort; -class BreadCrumbBox; - - -/** A window for a patch. - * - * \ingroup GUI - */ -class PatchWindow : public Gtk::Window -{ -public: - PatchWindow(BaseObjectType* cobject, const Glib::RefPtr& glade_xml); - ~PatchWindow(); - - void set_patch_from_path(const Path& path, SharedPtr view); - void set_patch(SharedPtr pc, SharedPtr view); - - SharedPtr patch() const { return _patch; } - - Gtk::MenuItem* menu_view_control_window() { return _menu_view_control_window; } - -protected: - void on_show(); - void on_hide(); - bool on_key_press_event(GdkEventKey* event); - bool on_key_release_event(GdkEventKey* event); - -private: - - void patch_port_added(SharedPtr port); - void patch_port_removed(SharedPtr port); - - void event_import(); - void event_import_location(); - void event_save(); - void event_save_as(); - void event_upload(); - void event_copy(); - void event_paste(); - void event_delete(); - void event_select_all(); - void event_quit(); - void event_destroy(); - void event_clear(); - void event_fullscreen_toggled(); - void event_human_names_toggled(); - void event_arrange(); - void event_show_properties(); - void event_show_controls(); - void event_show_engine(); - void event_clipboard_changed(GdkEventOwnerChange* ev); - - SharedPtr _patch; - SharedPtr _view; - - sigc::connection new_port_connection; - sigc::connection removed_port_connection; - - bool _enable_signal; - bool _position_stored; - int _x; - int _y; - - Gtk::MenuItem* _menu_import; - Gtk::MenuItem* _menu_import_location; - Gtk::MenuItem* _menu_save; - Gtk::MenuItem* _menu_save_as; - Gtk::MenuItem* _menu_upload; - Gtk::MenuItem* _menu_cut; - Gtk::MenuItem* _menu_copy; - Gtk::MenuItem* _menu_paste; - Gtk::MenuItem* _menu_delete; - Gtk::MenuItem* _menu_select_all; - Gtk::MenuItem* _menu_close; - Gtk::MenuItem* _menu_quit; - Gtk::CheckMenuItem* _menu_human_names; - Gtk::MenuItem* _menu_fullscreen; - Gtk::MenuItem* _menu_clear; - Gtk::MenuItem* _menu_destroy_patch; - Gtk::MenuItem* _menu_arrange; - Gtk::MenuItem* _menu_view_engine_window; - Gtk::MenuItem* _menu_view_control_window; - Gtk::MenuItem* _menu_view_patch_properties; - Gtk::MenuItem* _menu_view_messages_window; - Gtk::MenuItem* _menu_view_patch_tree_window; - Gtk::MenuItem* _menu_help_about; - - Gtk::VBox* _vbox; - Gtk::Viewport* _viewport; - BreadCrumbBox* _breadcrumb_box; - - //Gtk::Statusbar* _status_bar; - - /** Invisible bin used to store breadcrumbs when not shown by a view */ - Gtk::Alignment _breadcrumb_bin; -}; - - -} // namespace GUI -} // namespace Ingen - -#endif // PATCHWINDOW_H diff --git a/src/libs/gui/Port.cpp b/src/libs/gui/Port.cpp deleted file mode 100644 index 031c74c5..00000000 --- a/src/libs/gui/Port.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include "interface/EngineInterface.hpp" -#include "flowcanvas/Module.hpp" -#include "client/PatchModel.hpp" -#include "client/PortModel.hpp" -#include "Configuration.hpp" -#include "App.hpp" -#include "Port.hpp" -#include "PortMenu.hpp" -#include "GladeFactory.hpp" - -using namespace Ingen::Client; -using namespace std; - -namespace Ingen { -namespace GUI { - - -/** @param flip Make an input port appear as an output port, and vice versa. - */ -Port::Port( - boost::shared_ptr module, - SharedPtr pm, - const string& name, - bool flip) - : FlowCanvas::Port(module, name, - flip ? (!pm->is_input()) : pm->is_input(), - App::instance().configuration()->get_port_color(pm.get())) - , _port_model(pm) - , _flipped(flip) -{ - assert(module); - assert(_port_model); - - delete _menu; - _menu = NULL; - - _port_model->signal_renamed.connect(sigc::mem_fun(this, &Port::renamed)); - - if (pm->type().is_control()) { - set_toggled(pm->is_toggle()); - show_control(); - - float min = 0.0f, max = 1.0f; - boost::shared_ptr parent = PtrCast(_port_model->parent()); - if (parent) - parent->port_value_range(_port_model, min, max); - - set_control_min(min); - set_control_max(max); - - pm->signal_variable.connect(sigc::mem_fun(this, &Port::variable_changed)); - _port_model->signal_value_changed.connect(sigc::mem_fun(this, &Port::value_changed)); - } - - _port_model->signal_activity.connect(sigc::mem_fun(this, &Port::activity)); - - value_changed(_port_model->value()); -} - - -Port::~Port() -{ - App::instance().activity_port_destroyed(this); -} - - -void -Port::create_menu() -{ - PortMenu* menu = NULL; - Glib::RefPtr xml = GladeFactory::new_glade_reference(); - xml->get_widget_derived("object_menu", menu); - menu->init(_port_model, _flipped); - set_menu(menu); -} - - -void -Port::renamed() -{ - set_name(_port_model->path().name()); - module().lock()->resize(); -} - - -void -Port::value_changed(const Atom& value) -{ - if (value.type() == Atom::FLOAT) - FlowCanvas::Port::set_control(value.get_float()); - else - cerr << "WARNING: Unknown port value type " << (unsigned)value.type() << endl; -} - - -void -Port::activity() -{ - App::instance().port_activity(this); -} - - -void -Port::set_control(float value, bool signal) -{ - if (signal) { - if (_port_model->type() == DataType::CONTROL) { - App::instance().engine()->set_port_value(_port_model->path(), Atom(value)); - } else if (_port_model->type() == DataType::EVENT) { - App::instance().engine()->set_port_value(_port_model->path(), - Atom("", 0, NULL)); - } - } - - FlowCanvas::Port::set_control(value); -} - - -void -Port::variable_changed(const string& key, const Atom& value) -{ - if ( (key == "ingen:minimum") && value.type() == Atom::FLOAT) - set_control_min(value.get_float()); - else if ( (key == "ingen:maximum") && value.type() == Atom::FLOAT) - set_control_max(value.get_float()); - else if ( (key == "ingen:toggled") && value.type() == Atom::BOOL) - set_toggled(value.get_bool()); -} - - -} // namespace GUI -} // namespace Ingen diff --git a/src/libs/gui/Port.hpp b/src/libs/gui/Port.hpp deleted file mode 100644 index 7b347194..00000000 --- a/src/libs/gui/Port.hpp +++ /dev/null @@ -1,70 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef GUI_PORT_H -#define GUI_PORT_H - -#include -#include -#include -#include -#include - -namespace Ingen { namespace Client { class PortModel; } } -using Ingen::Client::PortModel; - -namespace Ingen { -namespace GUI { - - -/** A Port on an Module. - * - * \ingroup GUI - */ -class Port : public FlowCanvas::Port -{ -public: - Port(boost::shared_ptr module, - SharedPtr pm, - const std::string& name, - bool flip=false); - - ~Port(); - - SharedPtr model() const { return _port_model; } - - void create_menu(); - - virtual void set_control(float value, bool signal); - void value_changed(const Raul::Atom& value); - void activity(); - -private: - - void variable_changed(const std::string& key, const Raul::Atom& value); - - void renamed(); - - SharedPtr _port_model; - bool _flipped; -}; - - -} // namespace GUI -} // namespace Ingen - -#endif // GUI_PORT_H diff --git a/src/libs/gui/PortMenu.cpp b/src/libs/gui/PortMenu.cpp deleted file mode 100644 index 45b216b6..00000000 --- a/src/libs/gui/PortMenu.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include "interface/EngineInterface.hpp" -#include "client/PortModel.hpp" -#include "App.hpp" -#include "PortMenu.hpp" -#include "WindowFactory.hpp" - -namespace Ingen { -namespace GUI { - - -PortMenu::PortMenu(BaseObjectType* cobject, const Glib::RefPtr& xml) - : ObjectMenu(cobject, xml) - , _patch_port(NULL) -{ -} - - -void -PortMenu::init(SharedPtr port, bool patch_port) -{ - ObjectMenu::init(port); - _patch_port = patch_port; - - if ( ! PtrCast(port->parent()) ) { - _polyphonic_menuitem->set_sensitive(false); - _rename_menuitem->hide(); - _destroy_menuitem->hide(); - } - - _enable_signal = true; -} - - -void -PortMenu::on_menu_disconnect() -{ - if (_patch_port) { - App::instance().engine()->disconnect_all( - _object->parent()->path(), _object->path()); - } else { - App::instance().engine()->disconnect_all( - _object->parent()->path().parent(), _object->path()); - } -} - - -} // namespace GUI -} // namespace Ingen - diff --git a/src/libs/gui/PortMenu.hpp b/src/libs/gui/PortMenu.hpp deleted file mode 100644 index 520c5809..00000000 --- a/src/libs/gui/PortMenu.hpp +++ /dev/null @@ -1,55 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef PORTMENU_H -#define PORTMENU_H - -#include -#include -#include -#include -#include "client/PortModel.hpp" -#include "ObjectMenu.hpp" - -using Ingen::Client::PortModel; - -namespace Ingen { -namespace GUI { - - -/** Menu for a Port. - * - * \ingroup GUI - */ -class PortMenu : public ObjectMenu -{ -public: - PortMenu(BaseObjectType* cobject, const Glib::RefPtr& xml); - - void init(SharedPtr port, bool patch_port = false); - -private: - void on_menu_disconnect(); - - bool _patch_port; -}; - - -} // namespace GUI -} // namespace Ingen - -#endif // PORTMENU_H diff --git a/src/libs/gui/PortPropertiesWindow.cpp b/src/libs/gui/PortPropertiesWindow.cpp deleted file mode 100644 index ddab5715..00000000 --- a/src/libs/gui/PortPropertiesWindow.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include "interface/EngineInterface.hpp" -#include "client/NodeModel.hpp" -#include "client/PluginModel.hpp" -#include "App.hpp" -#include "Controls.hpp" -#include "PortPropertiesWindow.hpp" - -using namespace std; - -namespace Ingen { -namespace GUI { - - -PortPropertiesWindow::PortPropertiesWindow(BaseObjectType* cobject, const Glib::RefPtr& xml) - : Gtk::Window(cobject) - , _enable_signal(false) -{ - xml->get_widget("port_properties_min_spinner", _min_spinner); - xml->get_widget("port_properties_max_spinner", _max_spinner); - xml->get_widget("port_properties_cancel_button", _cancel_button); - xml->get_widget("port_properties_ok_button", _ok_button); - - _cancel_button->signal_clicked().connect(sigc::mem_fun(this, - &PortPropertiesWindow::cancel)); - - _ok_button->signal_clicked().connect(sigc::mem_fun(this, - &PortPropertiesWindow::ok)); -} - - -/** Set the port this window is associated with. - * This function MUST be called before using this object in any way. - */ -void -PortPropertiesWindow::present(SharedPtr pm) -{ - assert(pm); - - for (list::iterator i = _connections.begin(); i != _connections.end(); ++i) - (*i).disconnect(); - - _connections.clear(); - - _port_model = pm; - - set_title(pm->path() + " Properties"); - - float min = 0.0f, max = 1.0f; - boost::shared_ptr parent = PtrCast(_port_model->parent()); - if (parent) - parent->port_value_range(_port_model, min, max); - - _initial_min = min; - _initial_max = max; - - _min_spinner->set_value(min); - _connections.push_back(_min_spinner->signal_value_changed().connect( - sigc::mem_fun(*this, &PortPropertiesWindow::min_changed))); - - _max_spinner->set_value(max); - _connections.push_back(_max_spinner->signal_value_changed().connect( - sigc::mem_fun(*this, &PortPropertiesWindow::max_changed))); - - _connections.push_back(pm->signal_variable.connect( - sigc::mem_fun(this, &PortPropertiesWindow::variable_change))); - - _enable_signal = true; - - Gtk::Window::present(); -} - - -void -PortPropertiesWindow::variable_change(const string& key, const Atom& value) -{ - _enable_signal = false; - - if ( (key == "ingen:minimum") && value.type() == Atom::FLOAT) - _min_spinner->set_value(value.get_float()); - else if ( (key == "ingen:maximum") && value.type() == Atom::FLOAT) - _max_spinner->set_value(value.get_float()); - - _enable_signal = true; -} - - -void -PortPropertiesWindow::min_changed() -{ - const float min = _min_spinner->get_value(); - float max = _max_spinner->get_value(); - - if (max <= min) { - max = min + 1.0; - _max_spinner->set_value(max); - } - - if (_enable_signal) - App::instance().engine()->set_variable(_port_model->path(), "ingen:minimum", min); -} - - -void -PortPropertiesWindow::max_changed() -{ - float min = _min_spinner->get_value(); - const float max = _max_spinner->get_value(); - - if (min >= max) { - min = max - 1.0; - _min_spinner->set_value(min); - } - - if (_enable_signal) - App::instance().engine()->set_variable(_port_model->path(), "ingen:maximum", max); -} - - -void -PortPropertiesWindow::cancel() -{ - App::instance().engine()->set_variable(_port_model->path(), "ingen:minimum", _initial_min); - App::instance().engine()->set_variable(_port_model->path(), "ingen:maximum", _initial_max); - hide(); -} - - -void -PortPropertiesWindow::ok() -{ - hide(); -} - - -} // namespace GUI -} // namespace Ingen - diff --git a/src/libs/gui/PortPropertiesWindow.hpp b/src/libs/gui/PortPropertiesWindow.hpp deleted file mode 100644 index 4580c7c6..00000000 --- a/src/libs/gui/PortPropertiesWindow.hpp +++ /dev/null @@ -1,68 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef PORTPROPERTIESWINDOW_H -#define PORTPROPERTIESWINDOW_H - -#include -#include -#include -#include "client/PortModel.hpp" -using namespace Ingen::Client; - -namespace Ingen { -namespace GUI { - - -/** Port properties window. - * - * Loaded by libglade as a derived object. - * - * \ingroup GUI - */ -class PortPropertiesWindow : public Gtk::Window -{ -public: - PortPropertiesWindow(BaseObjectType* cobject, const Glib::RefPtr& refGlade); - - void present(SharedPtr port_model); - -private: - void variable_change(const string& key, const Atom& value); - void min_changed(); - void max_changed(); - - void ok(); - void cancel(); - - bool _enable_signal; - - float _initial_min; - float _initial_max; - - SharedPtr _port_model; - Gtk::SpinButton* _min_spinner; - Gtk::SpinButton* _max_spinner; - Gtk::Button* _cancel_button; - Gtk::Button* _ok_button; - std::list _connections; -}; - -} // namespace GUI -} // namespace Ingen - -#endif // PORTPROPERTIESWINDOW_H diff --git a/src/libs/gui/RenameWindow.cpp b/src/libs/gui/RenameWindow.cpp deleted file mode 100644 index b9c252d0..00000000 --- a/src/libs/gui/RenameWindow.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include "interface/EngineInterface.hpp" -#include "client/ObjectModel.hpp" -#include "client/ClientStore.hpp" -#include "App.hpp" -#include "RenameWindow.hpp" - -using std::string; - -namespace Ingen { -namespace GUI { - - -RenameWindow::RenameWindow(BaseObjectType* cobject, const Glib::RefPtr& glade_xml) -: Gtk::Window(cobject) -{ - glade_xml->get_widget("rename_name_entry", _name_entry); - glade_xml->get_widget("rename_message_label", _message_label); - glade_xml->get_widget("rename_cancel_button", _cancel_button); - glade_xml->get_widget("rename_ok_button", _ok_button); - - _name_entry->signal_changed().connect(sigc::mem_fun(this, &RenameWindow::name_changed)); - _cancel_button->signal_clicked().connect(sigc::mem_fun(this, &RenameWindow::cancel_clicked)); - _ok_button->signal_clicked().connect(sigc::mem_fun(this, &RenameWindow::ok_clicked)); - - _ok_button->property_sensitive() = false; -} - - -/** Set the object this window is renaming. - * This function MUST be called before using this object in any way. - */ -void -RenameWindow::set_object(SharedPtr object) -{ - _object = object; - _name_entry->set_text(object->path().name()); -} - - -/** Called every time the user types into the name input box. - * Used to display warning messages, and enable/disable the rename button. - */ -void -RenameWindow::name_changed() -{ - assert(_name_entry); - assert(_message_label); - assert(_object); - assert(_object->parent()); - - string name = _name_entry->get_text(); - if (name.find("/") != string::npos) { - _message_label->set_text("Name may not contain '/'"); - _ok_button->property_sensitive() = false; - } else if (!Path::is_valid_name(name)) { - _message_label->set_text("Name contains invalid characters"); - _ok_button->property_sensitive() = false; - } else if ((App::instance().store()->object(_object->parent()->path().base() + name)) && - (name != _object->path().name())) { - _message_label->set_text("An object already exists with that name."); - _ok_button->property_sensitive() = false; - } else if (name.length() == 0) { - _message_label->set_text(""); - _ok_button->property_sensitive() = false; - } else { - _message_label->set_text(""); - _ok_button->property_sensitive() = true; - } -} - - -void -RenameWindow::cancel_clicked() -{ - _name_entry->set_text(""); - hide(); -} - - -/** Rename the object. - * - * It shouldn't be possible for this to be called with an invalid name set - * (since the Rename button should be deactivated). This is just shinification - * though - the engine will handle invalid names gracefully. - */ -void -RenameWindow::ok_clicked() -{ - string name = _name_entry->get_text(); - assert(name.length() > 0); - assert(name.find("/") == string::npos); - - App::instance().engine()->rename(_object->path(), name); - - hide(); -} - - -} // namespace GUI -} // namespace Ingen diff --git a/src/libs/gui/RenameWindow.hpp b/src/libs/gui/RenameWindow.hpp deleted file mode 100644 index 3cf37f7c..00000000 --- a/src/libs/gui/RenameWindow.hpp +++ /dev/null @@ -1,60 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef RENAMEWINDOW_H -#define RENAMEWINDOW_H - -#include -#include -#include -#include "client/ObjectModel.hpp" -using Ingen::Client::ObjectModel; - -namespace Ingen { -namespace GUI { - - -/** Rename window. Handles renaming of any (Ingen) object. - * - * \ingroup GUI - */ -class RenameWindow : public Gtk::Window -{ -public: - RenameWindow(BaseObjectType* cobject, const Glib::RefPtr& refGlade); - - void present(SharedPtr object) { set_object(object); Gtk::Window::present(); } - -private: - void set_object(SharedPtr object); - - void name_changed(); - void cancel_clicked(); - void ok_clicked(); - - SharedPtr _object; - - Gtk::Entry* _name_entry; - Gtk::Label* _message_label; - Gtk::Button* _cancel_button; - Gtk::Button* _ok_button; -}; - -} // namespace GUI -} // namespace Ingen - -#endif // RENAMEWINDOW_H diff --git a/src/libs/gui/SubpatchModule.cpp b/src/libs/gui/SubpatchModule.cpp deleted file mode 100644 index b2a4d43f..00000000 --- a/src/libs/gui/SubpatchModule.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "SubpatchModule.hpp" -#include -#include -#include "interface/EngineInterface.hpp" -#include "client/PatchModel.hpp" -#include "App.hpp" -#include "NodeModule.hpp" -#include "NodeControlWindow.hpp" -#include "PatchWindow.hpp" -#include "PatchCanvas.hpp" -#include "Port.hpp" -#include "WindowFactory.hpp" -using std::cerr; using std::cout; using std::endl; - -namespace Ingen { -namespace GUI { - - -SubpatchModule::SubpatchModule(boost::shared_ptr canvas, SharedPtr patch) -: NodeModule(canvas, patch), - _patch(patch) -{ - assert(canvas); - assert(patch); -} - - -void -SubpatchModule::on_double_click(GdkEventButton* event) -{ - assert(_patch); - - SharedPtr parent = PtrCast(_patch->parent()); - - PatchWindow* const preferred = ( (parent && (event->state & GDK_SHIFT_MASK)) - ? NULL - : App::instance().window_factory()->patch_window(parent) ); - - App::instance().window_factory()->present_patch(_patch, preferred); -} - - - -/** Browse to this patch in current (parent's) window - * (unless an existing window is displaying it) - */ -void -SubpatchModule::browse_to_patch() -{ - assert(_patch->parent()); - - SharedPtr parent = PtrCast(_patch->parent()); - - PatchWindow* const preferred = ( (parent) - ? App::instance().window_factory()->patch_window(parent) - : NULL ); - - App::instance().window_factory()->present_patch(_patch, preferred); -} - - - -void -SubpatchModule::show_dialog() -{ - cerr << "FIXME: dialog\n"; - //m_patch->show_control_window(); -} - - -void -SubpatchModule::menu_remove() -{ - App::instance().engine()->destroy(_patch->path()); -} - -} // namespace GUI -} // namespace Ingen diff --git a/src/libs/gui/SubpatchModule.hpp b/src/libs/gui/SubpatchModule.hpp deleted file mode 100644 index 44e37e62..00000000 --- a/src/libs/gui/SubpatchModule.hpp +++ /dev/null @@ -1,71 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - - -#ifndef SUBPATCHMODULE_H -#define SUBPATCHMODULE_H - -#include -#include -#include -#include "client/PatchModel.hpp" -#include "PatchPortModule.hpp" -#include "NodeModule.hpp" -using std::string; using std::list; - -namespace Ingen { namespace Client { - class PatchModel; - class NodeModel; - class PortModel; - class PatchWindow; -} } -using namespace Ingen::Client; - -namespace Ingen { -namespace GUI { - -class PatchCanvas; -class NodeControlWindow; - - -/** A module to represent a subpatch - * - * \ingroup GUI - */ -class SubpatchModule : public NodeModule -{ -public: - SubpatchModule(boost::shared_ptr canvas, SharedPtr controller); - virtual ~SubpatchModule() {} - - void on_double_click(GdkEventButton* ev); - - void show_dialog(); - void browse_to_patch(); - void menu_remove(); - - SharedPtr patch() { return _patch; } - -protected: - SharedPtr _patch; -}; - - -} // namespace GUI -} // namespace Ingen - -#endif // SUBPATCHMODULE_H diff --git a/src/libs/gui/ThreadedLoader.cpp b/src/libs/gui/ThreadedLoader.cpp deleted file mode 100644 index 008b14c4..00000000 --- a/src/libs/gui/ThreadedLoader.cpp +++ /dev/null @@ -1,155 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include "module/global.hpp" -#include "module/World.hpp" -#include "module/Module.hpp" -#include "client/PatchModel.hpp" -#include "App.hpp" -#include "ThreadedLoader.hpp" - -using namespace std; - -namespace Ingen { -namespace GUI { - - -ThreadedLoader::ThreadedLoader(SharedPtr engine) - : _engine(engine) - , _deprecated_loader(engine) -{ - set_name("Loader"); - - if (parser()) - start(); - else - cerr << "WARNING: Failed to load ingen_serialisation module, load disabled." << endl; -} - - -SharedPtr -ThreadedLoader::parser() -{ - if (_parser) - return _parser; - - World* world = App::instance().world(); - if (!world->serialisation_module) - world->serialisation_module = Ingen::Shared::load_module("ingen_serialisation"); - - if (world->serialisation_module) { - Parser* (*new_parser)() = NULL; - - bool found = App::instance().world()->serialisation_module->get_symbol( - "new_parser", (void*&)new_parser); - - if (found) - _parser = SharedPtr(new_parser()); - } - - return _parser; -} - - -void -ThreadedLoader::_whipped() -{ - _mutex.lock(); - - while ( ! _events.empty() ) { - _events.front()(); - _events.pop_front(); - } - - _mutex.unlock(); -} - -void -ThreadedLoader::load_patch(bool merge, - const Glib::ustring& data_base_uri, - const Path& data_path, - GraphObject::Variables engine_data, - optional engine_parent, - optional engine_symbol) -{ - _mutex.lock(); - - // FIXME: Filthy hack to load deprecated patches based on file extension - if (data_base_uri.substr(data_base_uri.length()-3) == ".om") { - _events.push_back(sigc::hide_return(sigc::bind( - sigc::mem_fun(_deprecated_loader, &DeprecatedLoader::load_patch), - data_base_uri, - engine_parent, - (engine_symbol) ? engine_symbol.get() : "", - engine_data, - false))); - } else { - Glib::ustring engine_base = ""; - if (engine_parent) { - if (merge) - engine_base = engine_parent.get(); - else - engine_base = engine_parent.get().base(); - } - - if (merge && (!engine_parent || engine_parent.get() == "/")) - engine_base = engine_base.substr(0, engine_base.find_last_of("/")); - - _events.push_back(sigc::hide_return(sigc::bind( - sigc::mem_fun(_parser.get(), &Ingen::Serialisation::Parser::parse_document), - App::instance().world(), - App::instance().world()->engine.get(), - data_base_uri, // document - data_base_uri + data_path.substr(1), // object URI document - engine_base, - engine_symbol, - engine_data))); - } - - whip(); - - _mutex.unlock(); -} - - -void -ThreadedLoader::save_patch(SharedPtr model, const string& filename) -{ - _mutex.lock(); - - _events.push_back(sigc::hide_return(sigc::bind( - sigc::mem_fun(this, &ThreadedLoader::save_patch_event), - model, filename))); - - _mutex.unlock(); - - whip(); -} - - -void -ThreadedLoader::save_patch_event(SharedPtr model, const string& filename) -{ - if (App::instance().serialiser()) - App::instance().serialiser()->to_file(model, filename); -} - - -} // namespace GUI -} // namespace Ingen diff --git a/src/libs/gui/ThreadedLoader.hpp b/src/libs/gui/ThreadedLoader.hpp deleted file mode 100644 index 3cf88cf9..00000000 --- a/src/libs/gui/ThreadedLoader.hpp +++ /dev/null @@ -1,92 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef THREADEDLOADER_H -#define THREADEDLOADER_H - -#include -#include -#include -#include -#include -#include -#include -#include "interface/EngineInterface.hpp" -#include "client/PatchModel.hpp" -#include "client/DeprecatedLoader.hpp" -#include "serialisation/Serialiser.hpp" -#include "serialisation/Parser.hpp" -using std::string; -using std::list; -using boost::optional; - -using namespace Ingen::Client; -using namespace Ingen::Serialisation; - -namespace Ingen { -namespace GUI { - - -/** Thread for loading patch files. - * - * This is a seperate thread so it can send all the loading message without - * blocking everything else, so the app can respond to the incoming events - * caused as a result of the patch loading, while the patch loads. - * - * Implemented as a slave with a list of closures (events) which processes - * all events in the (mutex protected) list each time it's whipped. - * - * \ingroup GUI - */ -class ThreadedLoader : public Raul::Slave -{ -public: - ThreadedLoader(SharedPtr engine); - - void load_patch(bool merge, - const Glib::ustring& data_base_uri, - const Path& data_path, - GraphObject::Variables engine_data, - optional engine_parent = optional(), - optional engine_symbol = optional()); - - void save_patch(SharedPtr model, const string& filename); - - SharedPtr parser(); - -private: - - void save_patch_event(SharedPtr model, const string& filename); - - /** Returns nothing and takes no parameters (because they have all been bound) */ - typedef sigc::slot Closure; - - void _whipped(); - - SharedPtr _engine; - SharedPtr _parser; - - DeprecatedLoader _deprecated_loader; - Glib::Mutex _mutex; - list _events; -}; - - -} // namespace GUI -} // namespace Ingen - -#endif // LOADERRTHREAD_H diff --git a/src/libs/gui/UploadPatchWindow.cpp b/src/libs/gui/UploadPatchWindow.cpp deleted file mode 100644 index 2ca54f3c..00000000 --- a/src/libs/gui/UploadPatchWindow.cpp +++ /dev/null @@ -1,282 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include "module/global.hpp" -#include "module/World.hpp" -#include "client/ClientStore.hpp" -#include "interface/EngineInterface.hpp" -#include "serialisation/Serialiser.hpp" -#include "client/PatchModel.hpp" -#include "UploadPatchWindow.hpp" -#include "App.hpp" -#include "Configuration.hpp" -#include "ThreadedLoader.hpp" - -using boost::optional; -using namespace Raul; -using namespace std; - -namespace Ingen { -namespace GUI { - - -UploadPatchWindow::UploadPatchWindow(BaseObjectType* cobject, const Glib::RefPtr& xml) - : Gtk::Dialog(cobject) - , _thread(NULL) - , _progress_pct(0) - , _response(0) -{ - xml->get_widget("upload_patch_symbol_entry", _symbol_entry); - xml->get_widget("upload_patch_short_name_entry", _short_name_entry); - xml->get_widget("upload_patch_progress", _upload_progress); - xml->get_widget("upload_patch_cancel_button", _cancel_button); - xml->get_widget("upload_patch_upload_button", _upload_button); - - - _symbol_entry->signal_changed().connect(sigc::mem_fun(this, &UploadPatchWindow::symbol_changed)); - _short_name_entry->signal_changed().connect(sigc::mem_fun(this, &UploadPatchWindow::short_name_changed)); - _cancel_button->signal_clicked().connect(sigc::mem_fun(this, &UploadPatchWindow::cancel_clicked)); - _upload_button->signal_clicked().connect(sigc::mem_fun(this, &UploadPatchWindow::upload_clicked)); -} - - -void -UploadPatchWindow::present(SharedPtr patch) -{ - _patch = patch; - - Gtk::Window::present(); -} - - -void -UploadPatchWindow::on_show() -{ - Gtk::Dialog::on_show(); - - Raul::Atom atom = _patch->get_variable("lv2:symbol"); - if (atom.is_valid()) - _symbol_entry->set_text(atom.get_string()); - - atom = _patch->get_variable("doap:name"); - if (atom.is_valid()) - _short_name_entry->set_text(atom.get_string()); -} - - -void -UploadPatchWindow::on_hide() -{ - Gtk::Dialog::on_hide(); - - delete _thread; - _thread = NULL; -} - - -bool -UploadPatchWindow::is_symbol(const Glib::ustring& s) -{ - if (s.length() == 0) - return false; - - for (unsigned i=0; i < s.length(); ++i) - if ( !( (s[i] >= 'a' && s[i] <= 'z') - || (s[i] >= 'A' && s[i] <= 'Z') - || (s[i] == '_') - || (i > 0 && s[i] >= '0' && s[i] <= '9') ) ) - return false; - - return true; -} - - -void -UploadPatchWindow::symbol_changed() -{ - _upload_button->property_sensitive() = ( - is_symbol(_symbol_entry->get_text()) - && _short_name_entry->get_text().length() > 0); -} - - -void -UploadPatchWindow::short_name_changed() -{ - _upload_button->property_sensitive() = ( - is_symbol(_symbol_entry->get_text()) - && _short_name_entry->get_text().length() > 0); -} - - -size_t -UploadThread::curl_read_cb(void *ptr, size_t size, size_t nmemb, void *data) -{ - assert(size == 1); - - istringstream* ss = (istringstream*)data; - - return ss->readsome((char*)ptr, nmemb); -} - - -int -UploadThread::curl_progress_cb(void *thread, - double dltotal, - double dlnow, - double ultotal, - double ulnow) -{ - UploadThread* me = (UploadThread*)thread; - me->_win->set_progress(min( - (int)(min(ulnow, (double)me->_length) / me->_length * 100.0), - 99)); - return 0; -} - - -UploadThread::UploadThread(UploadPatchWindow* win, const string& str, const string& url) - : Thread("Upload") - , _curl(NULL) - , _headers(NULL) - , _win(win) - , _length(str.length()) - , _stream(str) - , _url(url) -{ - _curl = curl_easy_init(); - _headers = curl_slist_append(NULL, "Content-type: application/x-turtle"); - - curl_easy_setopt(_curl, CURLOPT_URL, url.c_str()); - curl_easy_setopt(_curl, CURLOPT_HTTPHEADER, _headers); - curl_easy_setopt(_curl, CURLOPT_UPLOAD, 1); - curl_easy_setopt(_curl, CURLOPT_READDATA, &_stream); - curl_easy_setopt(_curl, CURLOPT_READFUNCTION, &UploadThread::curl_read_cb); - curl_easy_setopt(_curl, CURLOPT_INFILESIZE, sizeof(char) * str.length()); - curl_easy_setopt(_curl, CURLOPT_NOPROGRESS, FALSE); - curl_easy_setopt(_curl, CURLOPT_PROGRESSFUNCTION, &UploadThread::curl_progress_cb); - curl_easy_setopt(_curl, CURLOPT_PROGRESSDATA, this); -} - - -void -UploadThread::_run() -{ - curl_easy_perform(_curl); - - long response; - curl_easy_getinfo(_curl, CURLINFO_RESPONSE_CODE, &response); - - printf("Server returned %ld\n", response); - - _win->set_response(response); - _win->set_progress(100); - - curl_slist_free_all(_headers); - curl_easy_cleanup(_curl); - - _headers = NULL; - _curl = NULL; -} - - -bool -UploadPatchWindow::progress_callback() -{ - const int progress = _progress_pct.get(); - const int response = _response.get(); - - _upload_progress->set_fraction(progress / 100.0); - - if (progress == 100) { - if (response == 200) { - _upload_progress->set_text("Transfer completed"); - } else { - _upload_progress->set_fraction(0.0); - char status[4]; - snprintf(status, 4, "%d", (unsigned)response); - string msg = "Transfer failed: Server returned "; - msg.append(status); - _upload_progress->set_text(msg); - } - delete _thread; - _thread = NULL; - _upload_button->set_sensitive(true); - return false; - } else { - return true; - } -} - - -void -UploadPatchWindow::upload_clicked() -{ - assert(!_thread); - - Glib::ustring symbol = _symbol_entry->get_text(); - Glib::ustring short_name = _short_name_entry->get_text(); - - GraphObject::Variables extra_rdf; - extra_rdf["lv2:symbol"] = Atom(symbol); - extra_rdf["doap:name"] = Atom(short_name); - - _response = 0; - _progress_pct = 0; - - _upload_progress->set_fraction(0.0); - _upload_progress->set_text(""); - - Serialiser s(*App::instance().world(), App::instance().store()); - - const string uri = string("http://rdf.drobilla.net/ingen_patches/") - .append(symbol).append(".ingen.ttl"); - - const string str = s.to_string(_patch, uri, extra_rdf); - - _thread = new UploadThread(this, str, uri); - - _thread->start(); - - _upload_button->set_sensitive(false); - - Glib::signal_timeout().connect( - sigc::mem_fun(this, &UploadPatchWindow::progress_callback), 100); -} - - -void -UploadPatchWindow::cancel_clicked() -{ - if (_thread) { - delete _thread; - _thread = NULL; - } - - hide(); -} - - -} // namespace GUI -} // namespace Ingen diff --git a/src/libs/gui/UploadPatchWindow.hpp b/src/libs/gui/UploadPatchWindow.hpp deleted file mode 100644 index af064bdb..00000000 --- a/src/libs/gui/UploadPatchWindow.hpp +++ /dev/null @@ -1,104 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef UPLOADPATCHWINDOW_H -#define UPLOADPATCHWINDOW_H - -#include -#include -#include -#include -#include -#include -#include -#include "client/PatchModel.hpp" -#include "client/PluginModel.hpp" -using Ingen::Client::PatchModel; - -namespace Ingen { -namespace GUI { - -class UploadPatchWindow; - - -class UploadThread : public Raul::Thread { -public: - UploadThread(UploadPatchWindow* win, - const string& str, - const string& url); - -private: - static size_t curl_read_cb(void* ptr, size_t size, size_t nmemb, void *stream); - static int curl_progress_cb(void* thread, double dltotal, double dlnow, double ultotal, double ulnow); - - void _run(); - - CURL* _curl; - curl_slist* _headers; - UploadPatchWindow* _win; - size_t _length; - std::istringstream _stream; - std::string _url; -}; - - -/* Upload patch dialog. - * - * \ingroup GUI - */ -class UploadPatchWindow : public Gtk::Dialog -{ -public: - UploadPatchWindow(BaseObjectType* cobject, const Glib::RefPtr& xml); - - void present(SharedPtr patch); - - Gtk::ProgressBar* progress_bar() { return _upload_progress; } - - void set_response(int response) { _response = response; } - void set_progress(int pct) { _progress_pct = pct; } - -private: - bool is_symbol(const Glib::ustring& str); - void symbol_changed(); - void short_name_changed(); - void cancel_clicked(); - void upload_clicked(); - void on_show(); - void on_hide(); - bool progress_callback(); - - UploadThread* _thread; - - SharedPtr _patch; - - Raul::AtomicInt _progress_pct; - Raul::AtomicInt _response; - - Gtk::Entry* _symbol_entry; - Gtk::Entry* _short_name_entry; - Gtk::ProgressBar* _upload_progress; - Gtk::Button* _cancel_button; - Gtk::Button* _upload_button; - -}; - - -} // namespace GUI -} // namespace Ingen - -#endif // UPLOADPATCHWINDOW_H diff --git a/src/libs/gui/WindowFactory.cpp b/src/libs/gui/WindowFactory.cpp deleted file mode 100644 index 125a242b..00000000 --- a/src/libs/gui/WindowFactory.cpp +++ /dev/null @@ -1,381 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include CONFIG_H_PATH -#include "WindowFactory.hpp" -#include "App.hpp" -#include "PatchWindow.hpp" -#include "GladeFactory.hpp" -#include "PatchPropertiesWindow.hpp" -#include "NodePropertiesWindow.hpp" -#include "PortPropertiesWindow.hpp" -#include "NodeControlWindow.hpp" -#include "LoadPluginWindow.hpp" -#include "LoadPatchWindow.hpp" -#include "LoadRemotePatchWindow.hpp" -#include "LoadSubpatchWindow.hpp" -#include "RenameWindow.hpp" -#include "NewSubpatchWindow.hpp" -#ifdef HAVE_CURL -#include "UploadPatchWindow.hpp" -#endif - -namespace Ingen { -namespace GUI { - - -WindowFactory::WindowFactory() - : _load_plugin_win(NULL) - , _load_patch_win(NULL) - , _load_remote_patch_win(NULL) - , _upload_patch_win(NULL) - , _new_subpatch_win(NULL) - , _load_subpatch_win(NULL) - , _patch_properties_win(NULL) - , _node_properties_win(NULL) - , _port_properties_win(NULL) -{ - Glib::RefPtr xml = GladeFactory::new_glade_reference(); - - xml->get_widget_derived("load_plugin_win", _load_plugin_win); - xml->get_widget_derived("load_patch_win", _load_patch_win); - xml->get_widget_derived("load_remote_patch_win", _load_remote_patch_win); - xml->get_widget_derived("new_subpatch_win", _new_subpatch_win); - xml->get_widget_derived("load_subpatch_win", _load_subpatch_win); - xml->get_widget_derived("patch_properties_win", _patch_properties_win); - xml->get_widget_derived("node_properties_win", _node_properties_win); - xml->get_widget_derived("port_properties_win", _port_properties_win); - xml->get_widget_derived("rename_win", _rename_win); - -#ifdef HAVE_CURL - xml->get_widget_derived("upload_patch_win", _upload_patch_win); -#endif -} - - -WindowFactory::~WindowFactory() -{ - for (PatchWindowMap::iterator i = _patch_windows.begin(); i != _patch_windows.end(); ++i) - delete i->second; - - for (ControlWindowMap::iterator i = _control_windows.begin(); i != _control_windows.end(); ++i) - delete i->second; - -} - - -void -WindowFactory::clear() -{ - for (PatchWindowMap::iterator i = _patch_windows.begin(); i != _patch_windows.end(); ++i) - delete i->second; - - _patch_windows.clear(); - - for (ControlWindowMap::iterator i = _control_windows.begin(); i != _control_windows.end(); ++i) - delete i->second; - - _control_windows.clear(); -} - - -/** Returns the number of Patch windows currently visible. - */ -size_t -WindowFactory::num_open_patch_windows() -{ - size_t ret = 0; - for (PatchWindowMap::iterator i = _patch_windows.begin(); i != _patch_windows.end(); ++i) - if (i->second->is_visible()) - ++ret; - - return ret; -} - - - -PatchWindow* -WindowFactory::patch_window(SharedPtr patch) -{ - PatchWindowMap::iterator w = _patch_windows.find(patch->path()); - - return (w == _patch_windows.end()) ? NULL : w->second; -} - - -NodeControlWindow* -WindowFactory::control_window(SharedPtr node) -{ - ControlWindowMap::iterator w = _control_windows.find(node->path()); - - return (w == _control_windows.end()) ? NULL : w->second; -} - - -/** Present a PatchWindow for a Patch. - * - * If @a preferred is not NULL, it will be set to display @a patch if the patch - * does not already have a visible window, otherwise that window will be presented and - * @a preferred left unmodified. - */ -void -WindowFactory::present_patch(SharedPtr patch, PatchWindow* preferred, SharedPtr view) -{ - assert( !view || view->patch() == patch); - - PatchWindowMap::iterator w = _patch_windows.find(patch->path()); - - if (w != _patch_windows.end()) { - (*w).second->present(); - } else if (preferred) { - w = _patch_windows.find(preferred->patch()->path()); - assert((*w).second == preferred); - - preferred->set_patch(patch, view); - _patch_windows.erase(w); - _patch_windows[patch->path()] = preferred; - preferred->present(); - - } else { - PatchWindow* win = new_patch_window(patch, view); - win->present(); - } -} - - -PatchWindow* -WindowFactory::new_patch_window(SharedPtr patch, SharedPtr view) -{ - assert( !view || view->patch() == patch); - - Glib::RefPtr xml = GladeFactory::new_glade_reference("patch_win"); - - PatchWindow* win = NULL; - xml->get_widget_derived("patch_win", win); - assert(win); - - win->set_patch(patch, view); - _patch_windows[patch->path()] = win; - - win->signal_delete_event().connect(sigc::bind<0>( - sigc::mem_fun(this, &WindowFactory::remove_patch_window), win)); - - return win; -} - - -bool -WindowFactory::remove_patch_window(PatchWindow* win, GdkEventAny* ignored) -{ - if (_patch_windows.size() <= 1) { - Gtk::MessageDialog d(*win, "This is the last remaining open patch " - "window. Closing this window will exit the GUI (the engine will " - "remain running).\n\nAre you sure you want to quit?", - true, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_NONE, true); - d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); - d.add_button(Gtk::Stock::QUIT, Gtk::RESPONSE_CLOSE); - int ret = d.run(); - if (ret == Gtk::RESPONSE_CLOSE) - App::instance().quit(); - else - return true; - } - - PatchWindowMap::iterator w = _patch_windows.find(win->patch()->path()); - - assert((*w).second == win); - _patch_windows.erase(w); - - delete win; - - return false; -} - - -void -WindowFactory::present_controls(SharedPtr node) -{ - NodeControlWindow* win = control_window(node); - - if (win) { - win->present(); - } else { - win = new_control_window(node); - win->present(); - } -} - - -NodeControlWindow* -WindowFactory::new_control_window(SharedPtr node) -{ - uint32_t poly = 1; - if (node->polyphonic() && node->parent()) - poly = ((PatchModel*)node->parent().get())->internal_polyphony(); - - NodeControlWindow* win = new NodeControlWindow(node, poly); - - _control_windows[node->path()] = win; - - win->signal_delete_event().connect(sigc::bind<0>( - sigc::mem_fun(this, &WindowFactory::remove_control_window), win)); - - return win; -} - - -bool -WindowFactory::remove_control_window(NodeControlWindow* win, GdkEventAny* ignored) -{ - ControlWindowMap::iterator w = _control_windows.find(win->node()->path()); - - assert((*w).second == win); - _control_windows.erase(w); - - delete win; - - return true; -} - - -void -WindowFactory::present_load_plugin(SharedPtr patch, GraphObject::Variables data) -{ - PatchWindowMap::iterator w = _patch_windows.find(patch->path()); - - if (w != _patch_windows.end()) - _load_plugin_win->set_transient_for(*w->second); - - _load_plugin_win->set_modal(false); - _load_plugin_win->present(patch, data); -} - - -void -WindowFactory::present_load_patch(SharedPtr patch, GraphObject::Variables data) -{ - PatchWindowMap::iterator w = _patch_windows.find(patch->path()); - - if (w != _patch_windows.end()) - _load_patch_win->set_transient_for(*w->second); - - _load_patch_win->set_merge(); // Import is the only choice - - _load_patch_win->present(patch, data); -} - - -void -WindowFactory::present_load_remote_patch(SharedPtr patch, GraphObject::Variables data) -{ - PatchWindowMap::iterator w = _patch_windows.find(patch->path()); - - if (w != _patch_windows.end()) - _load_remote_patch_win->set_transient_for(*w->second); - - _load_remote_patch_win->set_merge(); // Import is the only choice - - _load_remote_patch_win->present(patch, data); -} - - -void -WindowFactory::present_upload_patch(SharedPtr patch) -{ -#ifdef HAVE_CURL - PatchWindowMap::iterator w = _patch_windows.find(patch->path()); - - if (w != _patch_windows.end()) - _upload_patch_win->set_transient_for(*w->second); - - _upload_patch_win->present(patch); -#endif -} - -void -WindowFactory::present_new_subpatch(SharedPtr patch, GraphObject::Variables data) -{ - PatchWindowMap::iterator w = _patch_windows.find(patch->path()); - - if (w != _patch_windows.end()) - _new_subpatch_win->set_transient_for(*w->second); - - _new_subpatch_win->present(patch, data); -} - - -void -WindowFactory::present_load_subpatch(SharedPtr patch, GraphObject::Variables data) -{ - PatchWindowMap::iterator w = _patch_windows.find(patch->path()); - - if (w != _patch_windows.end()) - _load_subpatch_win->set_transient_for(*w->second); - - _load_subpatch_win->present(patch, data); -} - - -void -WindowFactory::present_rename(SharedPtr object) -{ - PatchWindowMap::iterator w = _patch_windows.find(object->path()); - - if (w != _patch_windows.end()) - _rename_win->set_transient_for(*w->second); - - _rename_win->present(object); -} - - -void -WindowFactory::present_properties(SharedPtr object) -{ - SharedPtr patch = PtrCast(object); - if (patch) { - PatchWindowMap::iterator w = _patch_windows.find(patch->path()); - if (w != _patch_windows.end()) - _patch_properties_win->set_transient_for(*w->second); - - _patch_properties_win->present(patch); - return; - } - - SharedPtr node = PtrCast(object); - if (node) { - PatchWindowMap::iterator w = _patch_windows.find(node->path().parent()); - if (w != _patch_windows.end()) - _node_properties_win->set_transient_for(*w->second); - - _node_properties_win->present(node); - return; - } - - SharedPtr port = PtrCast(object); - if (port) { - PatchWindowMap::iterator w = _patch_windows.find(port->path().parent().parent()); - if (w != _patch_windows.end()) - _patch_properties_win->set_transient_for(*w->second); - - _port_properties_win->present(port); - return; - } -} - - -} // namespace GUI -} // namespace Ingen diff --git a/src/libs/gui/WindowFactory.hpp b/src/libs/gui/WindowFactory.hpp deleted file mode 100644 index 618b027c..00000000 --- a/src/libs/gui/WindowFactory.hpp +++ /dev/null @@ -1,109 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef WINDOW_FACTORY_H -#define WINDOW_FACTORY_H - -#include -#include -#include -#include "interface/GraphObject.hpp" -#include "client/PatchModel.hpp" -#include "PatchView.hpp" - -using Ingen::Client::PatchModel; -using namespace Ingen::Shared; - -namespace Ingen { -namespace GUI { - -class PatchWindow; -class NodeControlWindow; -class PatchPropertiesWindow; -class NodePropertiesWindow; -class PortPropertiesWindow; -class LoadPatchWindow; -class LoadRemotePatchWindow; -class UploadPatchWindow; -class RenameWindow; - - -/** Manager/Factory for all windows. - * - * This serves as a nice centralized spot for all window management issues, - * as well as an enumeration of all windows (the goal being to reduce that - * number as much as possible). - */ -class WindowFactory { -public: - WindowFactory(); - ~WindowFactory(); - - size_t num_open_patch_windows(); - - PatchWindow* patch_window(SharedPtr patch); - NodeControlWindow* control_window(SharedPtr node); - - void present_patch(SharedPtr model, - PatchWindow* preferred = NULL, - SharedPtr view = SharedPtr()); - - void present_controls(SharedPtr node); - - void present_load_plugin(SharedPtr patch, GraphObject::Variables data=GraphObject::Variables()); - void present_load_patch(SharedPtr patch, GraphObject::Variables data=GraphObject::Variables()); - void present_load_remote_patch(SharedPtr patch, GraphObject::Variables data=GraphObject::Variables()); - void present_upload_patch(SharedPtr patch); - void present_new_subpatch(SharedPtr patch, GraphObject::Variables data=GraphObject::Variables()); - void present_load_subpatch(SharedPtr patch, GraphObject::Variables data=GraphObject::Variables()); - void present_rename(SharedPtr object); - void present_properties(SharedPtr object); - - bool remove_patch_window(PatchWindow* win, GdkEventAny* ignored = NULL); - - void clear(); - -private: - typedef std::map PatchWindowMap; - typedef std::map ControlWindowMap; - - PatchWindow* new_patch_window(SharedPtr patch, SharedPtr view); - - - NodeControlWindow* new_control_window(SharedPtr node); - bool remove_control_window(NodeControlWindow* win, GdkEventAny* ignored); - - PatchWindowMap _patch_windows; - ControlWindowMap _control_windows; - - LoadPluginWindow* _load_plugin_win; - LoadPatchWindow* _load_patch_win; - LoadRemotePatchWindow* _load_remote_patch_win; - UploadPatchWindow* _upload_patch_win; - NewSubpatchWindow* _new_subpatch_win; - LoadSubpatchWindow* _load_subpatch_win; - PatchPropertiesWindow* _patch_properties_win; - NodePropertiesWindow* _node_properties_win; - PortPropertiesWindow* _port_properties_win; - RenameWindow* _rename_win; -}; - - -} // namespace GUI -} // namespace Ingen - -#endif // WINDOW_FACTORY_H diff --git a/src/libs/gui/cmdline.h b/src/libs/gui/cmdline.h deleted file mode 100644 index 6f1f9259..00000000 --- a/src/libs/gui/cmdline.h +++ /dev/null @@ -1,86 +0,0 @@ -/* cmdline.h */ - -/* File autogenerated by gengetopt version 2.19.1 */ - -#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 "ingen" -#endif - -#ifndef CMDLINE_PARSER_VERSION -#define CMDLINE_PARSER_VERSION VERSION -#endif - -struct gengetopt_args_info -{ - const char *help_help; /* Print help and exit help description. */ - const char *version_help; /* Print version and exit help description. */ - int engine_flag; /* Run (JACK) engine (default=off). */ - const char *engine_help; /* Run (JACK) engine help description. */ - int engine_port_arg; /* Engine OSC port (default='16180'). */ - char * engine_port_orig; /* Engine OSC port original value given at command line. */ - const char *engine_port_help; /* Engine OSC port help description. */ - char * connect_arg; /* Connect to existing engine at OSC URI (default='osc.udp://localhost:16180'). */ - char * connect_orig; /* Connect to existing engine at OSC URI original value given at command line. */ - const char *connect_help; /* Connect to existing engine at OSC URI help description. */ - int gui_flag; /* Launch the GTK graphical interface (default=on). */ - const char *gui_help; /* Launch the GTK graphical interface help description. */ - int client_port_arg; /* Client OSC port. */ - char * client_port_orig; /* Client OSC port original value given at command line. */ - const char *client_port_help; /* Client OSC port help description. */ - char * load_arg; /* Load patch. */ - char * load_orig; /* Load patch original value given at command line. */ - const char *load_help; /* Load patch help description. */ - char * path_arg; /* Target path for loaded patch. */ - char * path_orig; /* Target path for loaded patch original value given at command line. */ - const char *path_help; /* Target path for loaded patch help description. */ - - int help_given ; /* Whether help was given. */ - int version_given ; /* Whether version was given. */ - int engine_given ; /* Whether engine was given. */ - int engine_port_given ; /* Whether engine-port was given. */ - int connect_given ; /* Whether connect was given. */ - int gui_given ; /* Whether gui was given. */ - int client_port_given ; /* Whether client-port was given. */ - int load_given ; /* Whether load was given. */ - int path_given ; /* Whether path was given. */ - -} ; - -extern const char *gengetopt_args_info_purpose; -extern const char *gengetopt_args_info_usage; -extern const char *gengetopt_args_info_help[]; - -int cmdline_parser (int argc, char * const *argv, - struct gengetopt_args_info *args_info); -int cmdline_parser2 (int argc, char * const *argv, - struct gengetopt_args_info *args_info, - int override, int initialize, int check_required); -int cmdline_parser_file_save(const char *filename, - struct gengetopt_args_info *args_info); - -void cmdline_parser_print_help(void); -void cmdline_parser_print_version(void); - -void cmdline_parser_init (struct gengetopt_args_info *args_info); -void cmdline_parser_free (struct gengetopt_args_info *args_info); - -int cmdline_parser_required (struct gengetopt_args_info *args_info, - const char *prog_name); - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* CMDLINE_H */ diff --git a/src/libs/gui/gui.cpp b/src/libs/gui/gui.cpp deleted file mode 100644 index 10af6d9b..00000000 --- a/src/libs/gui/gui.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "gui.hpp" -#include "ConnectWindow.hpp" -#include "App.hpp" -#include "Configuration.hpp" - -namespace Ingen { -namespace GUI { - - -void run(int argc, char** argv, Ingen::Shared::World* world) -{ - App::run(argc, argv, world); -} - - -} // namespace GUI -} // namespace Ingen - diff --git a/src/libs/gui/gui.hpp b/src/libs/gui/gui.hpp deleted file mode 100644 index a959f17a..00000000 --- a/src/libs/gui/gui.hpp +++ /dev/null @@ -1,45 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef INGEN_GUI_H -#define INGEN_GUI_H - -#include CONFIG_H_PATH -#include "module/global.hpp" -#include - -namespace Ingen { - -class Engine; - -namespace Shared { class EngineInterface; } - -namespace GUI { - - -extern "C" { - - void run(int argc, char** argv, Ingen::Shared::World* world); - -} - - -} // namesace GUI -} // namespace Ingen - -#endif // INGEN_GUI_H - diff --git a/src/libs/gui/ingen-icon.svg b/src/libs/gui/ingen-icon.svg deleted file mode 100644 index a15ed7e7..00000000 --- a/src/libs/gui/ingen-icon.svg +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - image/svg+xml - - - - - - - - diff --git a/src/libs/gui/ingen.svg b/src/libs/gui/ingen.svg deleted file mode 100644 index a15ed7e7..00000000 --- a/src/libs/gui/ingen.svg +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - image/svg+xml - - - - - - - - diff --git a/src/libs/gui/ingen_gui.glade b/src/libs/gui/ingen_gui.glade deleted file mode 100644 index aa0680ea..00000000 --- a/src/libs/gui/ingen_gui.glade +++ /dev/null @@ -1,3337 +0,0 @@ - - - - - - Ingen - 640 - 480 - ingen.svg - - - True - - - True - - - True - _File - True - - - - - True - Load a patch into the current patch (merge with existing contents). - _Import... - True - - - - - True - gtk-open - 1 - - - - - - - True - Import a patch from a URI - Import _Location... - True - - - - - True - gtk-open - 1 - - - - - - - True - - - - - True - Save this patch - gtk-save - True - True - - - - - - True - Save this patch to a specific file name - Save _As... - True - - - - - True - gtk-save-as - 1 - - - - - - - True - _Upload... - True - - - - - True - gtk-network - 1 - - - - - - - True - - - - - True - Close this window (patch will not be destroyed) - gtk-close - True - True - - - - - - True - - - - - True - Quit GUI (engine may continue running) - gtk-quit - True - True - - - - - - - - - - True - _Edit - True - - - - - True - False - gtk-cut - True - True - - - - - - True - gtk-copy - True - True - - - - - - True - False - gtk-paste - True - True - - - - - - True - Delete the selected object(s) - gtk-delete - True - True - - - - - - - True - Select all objects in a patch - gtk-select-all - True - True - - - - - - - True - - - - - True - Automatically arrange canvas - _Arrange - True - - - - True - gtk-sort-ascending - 1 - - - - - - - True - Remove all objects from patch - gtk-clear - True - True - - - - - - True - Destoy this patch (remove it from the engine) - _Destroy Patch - True - - - - True - gtk-delete - 1 - - - - - - - True - - - - - True - View/Edit controls for this patch - C_ontrols... - True - - - - - True - gtk-preferences - 1 - - - - - - - True - View/Edit properties for this patch - _Properties... - True - - - - - True - gtk-properties - 1 - - - - - - - - - - - True - _View - True - - - - - True - gtk-fullscreen - True - True - - - - - - - True - Show human readable names instead of identifiers - _Human names - True - True - - - - - - - - - - True - _Windows - True - - - - - - True - Connect to, Disconnect from, or Launch Engine - _Engine... - True - - - - - True - gtk-execute - 1 - - - - - - - True - View all patches in the engine as a heirarchial list - _Patch Tree... - True - - - - - True - gtk-index - 1 - - - - - - - True - View error messages from the engine - _Messages... - True - - - - - True - gtk-info - 1 - - - - - - - - - - - True - _Help - True - - - - - - True - Right-click the canvas to add objects - True - - - True - gtk-info - 1 - - - - - - - True - Press 'e' to toggle edit mode - True - - - True - gtk-info - 1 - - - - - - - True - - - - - True - gtk-about - True - True - - - - - - - - - - False - False - - - - - True - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - - - True - GTK_SHADOW_NONE - - - - - - - - 1 - - - - - - - 8 - Load Plugin - GTK_WIN_POS_CENTER_ON_PARENT - 640 - 480 - ingen.svg - - - True - 1 - - - True - True - 2 - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - - - True - True - 2 - True - True - - - - - - - True - 3 - 3 - 12 - - - True - True - Clear filter text (show all plugins) - gtk-clear - True - 0 - - - 2 - 3 - GTK_FILL - - - - - - True - Name contains: - - - GTK_FILL - - - - - - True - True - True - Search string to filter plugin list - * - - - 1 - 2 - 6 - - - - - True - False - True - Add selected plugin to patch - gtk-add - True - 0 - - - 2 - 3 - 2 - 3 - GTK_FILL - - - - - - True - - - True - True - Name of new Module - * - - - - - True - True - Polyphonic - True - 0 - True - True - - - False - False - 8 - 1 - - - - - 1 - 2 - 2 - 3 - GTK_FILL - GTK_FILL - 6 - - - - - True - - - 2 - 3 - 1 - 2 - GTK_FILL - GTK_FILL - - - - - True - - - 1 - 2 - GTK_FILL - GTK_FILL - - - - - True - - - 1 - 2 - 1 - 2 - GTK_FILL - - - - - True - 1 - Node Name: - True - - - 2 - 3 - GTK_FILL - - - - - - False - False - 1 - - - - - - - 320 - 8 - Create Subpatch - GTK_WIN_POS_CENTER_ON_PARENT - ingen.svg - GDK_WINDOW_TYPE_HINT_DIALOG - - - True - - - True - 2 - 2 - - - True - True - True - * - True - - - 1 - 2 - - 4 - - - - - True - True - 1 0 100 1 10 10 - 1 - - - 1 - 2 - 1 - 2 - GTK_FILL - - 4 - - - - - True - 0 - Polyphony: - - - 1 - 2 - GTK_FILL - GTK_EXPAND - 5 - - - - - True - 0 - Name: - - - GTK_FILL - GTK_EXPAND - 5 - - - - - - - True - True - - - False - False - 1 - - - - - True - 4 - GTK_BUTTONBOX_END - - - True - True - gtk-cancel - True - 0 - - - - - True - True - True - True - 0 - - - True - 0 - 0 - - - True - 2 - - - True - gtk-ok - - - False - False - - - - - True - Create - True - - - False - False - 1 - - - - - - - - - 1 - - - - - 2 - - - - - - - Load Subpatch - GTK_WIN_POS_CENTER_ON_PARENT - ingen.svg - GDK_WINDOW_TYPE_HINT_DIALOG - - - True - 24 - - - True - 2 - 4 - 12 - 4 - - - True - - - True - True - Specify the name for the new patch - Specify: - True - 0 - True - load_subpatch_name_from_file_radio - - - False - False - - - - - True - False - True - Specify the name for the new patch - * - True - - - False - 1 - - - - - 3 - 4 - GTK_FILL - - - - - True - 0 - - - 2 - 3 - GTK_FILL - - - - - - True - True - Set polyphony to the same value as the parent (containing) patch - Same as parent (?) - True - 0 - True - load_subpatch_poly_from_file_radio - - - 2 - 3 - 1 - 2 - GTK_FILL - - - - - - True - - - True - True - Specify a custom polyphony value for new patch - Specify: - True - 0 - True - load_subpatch_poly_from_file_radio - - - False - False - - - - - True - False - True - Specify a custom polyphony value for new patch - 1 0 1000 1 10 10 - 1 - - - False - 1 - - - - - 3 - 4 - 1 - 2 - GTK_FILL - GTK_FILL - - - - - True - True - Use the polyphony value stored in the patch file - Load from file - True - 0 - True - True - - - 1 - 2 - 1 - 2 - GTK_FILL - - - - - - True - True - Use the name stored in the patch file - Load from file - True - 0 - True - True - - - 1 - 2 - GTK_FILL - - - - - - True - 0 - <b>Polyphony: </b> - True - - - 1 - 2 - GTK_FILL - - - - - - True - 0 - <b>Name: </b> - True - - - GTK_FILL - - - - - - False - 2 - - - - - True - GTK_BUTTONBOX_END - - - True - True - True - gtk-cancel - True - -6 - - - - - True - True - True - True - gtk-open - True - -5 - - - 1 - - - - - False - GTK_PACK_END - - - - - - - Load Patch - GTK_WIN_POS_CENTER_ON_PARENT - ingen.svg - GDK_WINDOW_TYPE_HINT_DIALOG - - - 24 - - - True - 1 - 4 - 12 - 4 - - - True - - - True - True - Specify: - True - 0 - True - load_patch_poly_from_current_radio - - - False - False - - - - - True - False - True - Specify a custom polyphony value for new patch - 1 0 100 1 10 10 - 1 - - - False - 1 - - - - - 3 - 4 - GTK_FILL - GTK_FILL - - - - - True - True - Use the polyphony value stored in the patch file - Load from file - True - 0 - True - load_patch_poly_from_current_radio - - - 2 - 3 - GTK_FILL - - - - - - True - True - Use the same polyphony as the current patch - Keep current - True - 0 - True - True - - - 1 - 2 - GTK_FILL - - - - - - True - 0 - <b>Polyphony: </b> - True - - - GTK_FILL - - - - - - False - 2 - - - - - True - GTK_BUTTONBOX_END - - - True - True - True - gtk-cancel - True - -6 - - - - - True - True - True - True - gtk-open - True - -5 - - - 1 - - - - - False - GTK_PACK_END - - - - - - - window1 - - - True - 5 - 2 - - - - - - - - - - - - - - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - - - True - 0 - 1 - 4 - <b>Name</b> - True - True - - - False - False - - - - - True - 1 - 0 - 1 - 1 - 4 - - - True - - - - - 1 - - - - - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 12 - 0 -9.9999999999999999e+45 1.0000000000000001e+63 1 10 10 - 4 - True - - - False - 2 - - - - - False - False - - - - - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 9.0923076923076922e+136 -1e+113 1e+137 0 0 0 - 63 - False - - - False - 1 - - - - - 3 - 4 - 8 - - - - - True - - - 1 - 2 - GTK_FILL - - - - - True - - - True - - - True - GTK_TOOLBAR_BOTH_HORIZ - - - True - - - True - - - True - GTK_SHADOW_NONE - - - - - - - - - - False - False - - - - - True - - - True - - - - - - False - False - - - - - - - True - False - - - True - - - False - False - - - - - True - Enable DSP processing - True - gtk-execute - True - - - False - - - - - True - - - True - 4 - gtk-copy - - - - - False - False - - - - - True - - - True - True - 1 1 512 1 10 10 - 1 - True - - - - - False - False - - - - - True - - - False - False - - - - - True - Save patch to a file - gtk-save - - - False - - - - - True - - - False - False - - - - - True - Clear (Destroy all children) - True - gtk-clear - - - False - - - - - True - Destroy this patch - gtk-delete - - - False - - - - - True - - - False - False - - - - - True - Refresh view - gtk-refresh - - - False - - - - - True - Zoom to normal size - True - gtk-zoom-100 - - - False - - - - - True - Fit patch to window - True - gtk-zoom-fit - - - False - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - - - False - False - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - Enable edit mode to move objects and make connections, disable to play controls ('e' to toggle) - Edit - gtk-edit - True - - - False - - - - - False - 1 - - - - - False - False - - - - - True - True - True - True - True - True - GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_MOTION_MASK | GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK | GDK_BUTTON3_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_FOCUS_CHANGE_MASK | GDK_STRUCTURE_MASK | GDK_PROPERTY_CHANGE_MASK | GDK_VISIBILITY_NOTIFY_MASK | GDK_PROXIMITY_IN_MASK | GDK_PROXIMITY_OUT_MASK | GDK_SUBSTRUCTURE_MASK | GDK_SCROLL_MASK - 1 - GTK_SHADOW_IN - - - - - - 1 - - - - - 2 - 3 - GTK_FILL - - - - - True - - - True - 0 - - - True - True - GTK_POLICY_NEVER - GTK_POLICY_AUTOMATIC - - - True - GTK_SHADOW_NONE - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - - - - - - - - - - - True - True - - - True - True - Apply changed controls to all voices - All Voices - True - 0 - True - - - False - False - - - - - True - 5 - - - True - True - Apply changed controls to one voice only - Specific Voice: - True - 0 - True - control_panel_all_voices_radio - - - False - False - - - - - True - False - True - Voice control changes are applied to - 1 1 100 1 10 10 - 1 - True - - - 1 - - - - - False - False - 1 - - - - - False - 5 - 1 - - - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - - - True - 0 - 1 - 4 - <b>Name</b> - True - True - - - False - False - - - - - True - 1 - 0 - 1 - 1 - 4 - - - True - - - - - 1 - - - - - True - True - 0 - True - - - False - 2 - - - - - False - False - - - - - 4 - 5 - 8 - - - - - - - 400 - 180 - 8 - Messages - Ingen - ingen.svg - - - True - 6 - - - True - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - GTK_SHADOW_IN - - - True - True - Error messages from the engine since the last time "Clear" was pressed - 5 - 5 - False - GTK_WRAP_WORD - 5 - 5 - False - False - - - - - - - True - 6 - GTK_BUTTONBOX_END - - - True - False - True - True - gtk-clear - True - 0 - - - - - True - True - gtk-close - True - 0 - - - 1 - - - - - False - 1 - - - - - - - 8 - Configuration - Ingen - ingen.svg - - - True - 6 - - - True - 2 - 2 - - - True - 0 - - - 1 - 2 - GTK_FILL - - - - - - True - <i>Example: /foo/bar:/home/john/patches:/usr/share/om/patches</i> - True - - - 1 - 2 - 1 - 2 - GTK_FILL - - - - - - True - True - * - - - 1 - 2 - - - - - - True - <b>Patch Search Path: </b> - True - - - GTK_FILL - - - - - - False - - - - - True - 6 - GTK_BUTTONBOX_END - - - True - True - Save these settings for future sessions - gtk-save - True - 0 - - - - - True - True - gtk-cancel - True - 0 - - - 1 - - - - - True - True - Apply these settings to this session only - gtk-ok - True - 0 - - - 2 - - - - - False - 1 - - - - - - - 400 - 200 - 8 - Patch Description - GTK_WIN_POS_CENTER_ON_PARENT - ingen.svg - - - True - 6 - - - True - 5 - - - True - Author: - - - False - False - - - - - True - True - * - - - 1 - - - - - False - False - - - - - True - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - GTK_SHADOW_IN - - - True - True - A short description of the patch to be included in the patch file - GTK_WRAP_WORD - - - - - 1 - - - - - True - 5 - GTK_BUTTONBOX_END - - - True - True - True - gtk-cancel - True - 0 - - - - - True - True - True - Apply these changes to be saved the next time the patch is saved - gtk-ok - True - 0 - - - 1 - - - - - False - False - 2 - - - - - - - 250 - Rename - GTK_WIN_POS_CENTER_ON_PARENT - ingen.svg - - - True - 5 - - - True - - - True - New name: - - - False - False - - - - - True - True - * - True - - - 1 - - - - - - - True - True - - - False - 6 - 1 - - - - - True - 5 - GTK_BUTTONBOX_END - - - True - True - True - gtk-cancel - True - 0 - - - - - True - True - True - True - 0 - - - True - 0 - 0 - - - True - 2 - - - True - gtk-ok - - - False - False - - - - - True - Rename - True - - - False - False - 1 - - - - - - - - - 1 - - - - - False - 2 - - - - - - - 8 - Node Properties - Ingen - GTK_WIN_POS_MOUSE - ingen.svg - - - True - - - True - 0 - <b>Node</b> - True - - - False - False - - - - - True - 12 - 6 - - - True - 4 - - - True - Path: - - - False - False - - - - - True - - - - - False - False - 1 - - - - - False - False - - - - - True - False - True - Polyphonic - True - 0 - True - - - False - False - 1 - - - - - 6 - 1 - - - - - 240 - True - 0 - <b>Plugin</b> - True - - - False - False - 2 - - - - - True - 12 - 3 - 2 - 10 - 6 - - - True - 0 - - - - - 1 - 2 - 2 - 3 - GTK_FILL - - - - - - True - 0 - Name: - - - 2 - 3 - GTK_FILL - - - - - - True - 0 - - - - - 1 - 2 - 1 - 2 - GTK_FILL - - - - - - True - 0 - URI: - - - 1 - 2 - GTK_FILL - - - - - - True - 0 - Type: - - - GTK_FILL - - - - - - True - 0 - - - - - 1 - 2 - GTK_FILL - - - - - - 3 - - - - - - - True - GDK_WINDOW_TYPE_HINT_NORMAL - Copyright (C) 2005-2007 Dave Robillard <http://drobilla.net> - http://drobilla.net/software/ingen - Licensed under the GNU GPL, Version 2. - -See COPYING file included with this distribution, or http://www.gnu.org/licenses/gpl.txt for more information - Author: - Dave Robillard <dave@drobilla.net> - -Contributors: - Lars Luthman - DSSI enhancements, bugfixes - Mario Lang - SuperCollider bindings, bugfixes - Leonard Ritter - Python bindings - - translator-credits - Usability / UI Design: - Thorsten Wilms - ingen.svg - True - - - - - - - False - GTK_PACK_END - - - - - - - 320 - 340 - 8 - Patches - Ingen - ingen.svg - - - True - True - 3 - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - GTK_SHADOW_IN - - - True - True - All patches loaded in the engine - True - - - - - - - 6 - Engine - Ingen - False - ingen.svg - GDK_WINDOW_TYPE_HINT_DIALOG - - - True - 6 - - - True - - - True - - - True - 12 - gtk-disconnect - 3 - - - False - - - - - True - 5 - True - - - True - 0.10000000149 - - - - False - False - - - - - True - 0 - Not Connected - - - False - 1 - - - - - 1 - - - - - False - - - - - True - - - False - 4 - 1 - - - - - True - 3 - 2 - 8 - - - True - 0 - - - 1 - 2 - 2 - 3 - GTK_FILL - - - - - - True - False - True - Use internal engine - True - 0 - True - connect_server_radiobutton - - - 2 - 3 - GTK_FILL - - - - - - True - True - Launch and connect to server on port: - True - 0 - True - connect_server_radiobutton - - - 1 - 2 - GTK_FILL - - - - - - True - True - Connect to running server at: - True - 0 - True - - - GTK_FILL - - - - - - True - - - True - True - * - True - 28 - osc.udp://localhost:16180 - - - - - 1 - 2 - GTK_FILL - GTK_FILL - 8 - - - - - True - - - True - False - True - 16180 1 65535 1 10 10 - 1 - True - - - False - False - - - - - 1 - 2 - 1 - 2 - GTK_FILL - 8 - - - - - 2 - - - - - 2 - - - - - True - GTK_BUTTONBOX_END - - - True - True - True - gtk-quit - True - 0 - - - - - True - False - True - True - gtk-disconnect - True - -6 - - - 1 - - - - - True - True - True - True - gtk-connect - True - -6 - - - 2 - - - - - False - False - GTK_PACK_END - - - - - - - True - - - True - Input - True - - - - - True - Add an audio input to this patch - Audio - True - - - - - - True - Add a control input (and a control slider for it) to this patch - Control - True - - - - - - True - Add a MIDI input to this patch - MIDI - True - - - - - - True - Add a generic event input to this patch - Event - True - - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - Add an OSC input to this patch - OSC - True - - - - - - - True - gtk-connect - 1 - - - - - - - True - Output - True - - - - - True - Add an audio output to this patch - Audio - True - - - - - - True - Add a control output to this patch - Control - True - - - - - - True - Add a MIDI output to this patch - MIDI - True - - - - - - True - Add a generic event output to this patch - Event - True - - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - Add an OSC output to this patch - OSC - True - - - - - - - True - gtk-connect - 1 - - - - - - - True - Load a plugin as a child of this patch - _Plugin... - True - - - - True - gtk-execute - 1 - - - - - - - True - Load a patch as a child of this patch - _Load Patch... - True - - - - True - gtk-open - 1 - - - - - - - True - Create a new (empty) patch as a child of this patch - _New Patch... - True - - - - True - gtk-new - 1 - - - - - - - 8 - Load Remote Patch - GDK_WINDOW_TYPE_HINT_DIALOG - - - True - 8 - - - True - 8 - - - True - True - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - GTK_SHADOW_IN - - - True - True - - - - - - - True - - - True - URI: - - - False - False - - - - - True - True - * - 78 - - - 1 - - - - - False - 1 - - - - - 2 - - - - - True - GTK_BUTTONBOX_END - - - True - True - True - gtk-cancel - True - -6 - - - - - True - False - True - True - True - gtk-open - True - -5 - - - 1 - - - - - False - GTK_PACK_END - - - - - - - 8 - Upload Patch - False - GDK_WINDOW_TYPE_HINT_DIALOG - - - True - 9 - - - True - 2 - 2 - 8 - - - True - 0 - Short Name: - - - 1 - 2 - GTK_FILL - - - - - - True - 0 - Symbol: - - - GTK_FILL - - - - - - True - True - Enter a short name for this patch, e.g. "Mega Synth" - * - True - - - 1 - 2 - 1 - 2 - - - - - - True - True - Enter a short name suitable for use as an identifier or filename. - -The first character must be one of _, a-z or A-Z and subsequenct characters can be from _, a-z, A-Z or 0-9. - - * - True - - - 1 - 2 - - - - - - 2 - - - - - True - 4 - Succesfully uploaded patches will be available immediately in the remote patch browser. - -By uploading patches, you agree to license them under the Creative Commons Attribution-Share Alike 3.0 License. - -Thank you for contributing. - True - - - False - False - 3 - - - - - True - Upload progress - 0.10000000149 - - - False - False - 4 - - - - - True - GTK_BUTTONBOX_END - - - True - True - True - gtk-close - True - -7 - - - - - True - False - True - True - True - -5 - - - True - 0 - 0 - - - True - 2 - - - True - gtk-ok - - - False - False - - - - - True - Upload - True - - - False - False - 1 - - - - - - - - - 1 - - - - - False - GTK_PACK_END - - - - - - - - - True - _Properties... - True - - - - True - gtk-properties - 1 - - - - - - - 8 - Port Properties - Ingen - False - GTK_WIN_POS_MOUSE - - - True - 8 - - - True - 2 - 2 - 2 - 4 - - - True - 0 - Maximum Value: - - - 1 - 2 - GTK_FILL - - - - - - True - 0 - Minimum Value: - - - GTK_FILL - - - - - - True - True - 1 -99999 99999 1 10 10 - 1 - 5 - True - - - 1 - 2 - 1 - 2 - - - - - - True - True - 0 -100000000 100000000 1 10 10 - 1 - 5 - True - - - 1 - 2 - - - - - - 2 - - - - - True - GTK_BUTTONBOX_END - - - True - True - True - gtk-cancel - True - -6 - - - - - True - True - True - True - gtk-ok - True - -5 - - - 1 - - - - - False - GTK_PACK_END - - - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - Expose individual voices - Polyphonic - True - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - Disconnect all connections - gtk-disconnect - True - True - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - Rename this object - Rename... - True - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - gtk-find-and-replace - - - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - Destroy this object - gtk-delete - True - True - - - - - True - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - View and edit properties - gtk-properties - True - True - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - Manipulate controls in a separate window - Controls... - True - - - gtk-edit - - - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - Show this node's custom graphical interface in a separate window - GUI... - True - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - gtk-edit - - - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - Embed the custom GUI for this plugin in the patch canvas - Embed GUI - True - - - - diff --git a/src/libs/gui/ingen_gui.gladep b/src/libs/gui/ingen_gui.gladep deleted file mode 100644 index 7cd9c6ce..00000000 --- a/src/libs/gui/ingen_gui.gladep +++ /dev/null @@ -1,9 +0,0 @@ - - - - - Ingenuity - ingenuity - C++ - FALSE - diff --git a/src/libs/gui/wscript b/src/libs/gui/wscript deleted file mode 100644 index 8895c7bc..00000000 --- a/src/libs/gui/wscript +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env python -import Params - -def build(bld): - obj = bld.create_obj('cpp', 'shlib') - obj.source = ''' - App.cpp - BreadCrumbBox.cpp - Configuration.cpp - ConnectWindow.cpp - ControlPanel.cpp - Controls.cpp - GladeFactory.cpp - LoadPatchWindow.cpp - LoadPluginWindow.cpp - LoadRemotePatchWindow.cpp - LoadSubpatchWindow.cpp - MessagesWindow.cpp - NewSubpatchWindow.cpp - NodeControlWindow.cpp - NodeMenu.cpp - NodeModule.cpp - NodePropertiesWindow.cpp - ObjectMenu.cpp - PatchCanvas.cpp - PatchPortModule.cpp - PatchPropertiesWindow.cpp - PatchTreeWindow.cpp - PatchView.cpp - PatchWindow.cpp - Port.cpp - PortMenu.cpp - PortPropertiesWindow.cpp - RenameWindow.cpp - SubpatchModule.cpp - ThreadedLoader.cpp - WindowFactory.cpp - gui.cpp - ''' - - if bld.env()['HAVE_CURL']: - obj.source += 'UploadPatchWindow.cpp' - - dir = Params.g_options.moduledir or bld.env()['PREFIX'] + 'lib/ingen' - - obj.includes = ['..', '../../common', '../..'] - obj.defines = 'INGEN_DATA_DIR=\\\"' + dir + '\\\"' - obj.name = 'libingen_gui' - obj.target = 'ingen_gui' - obj.vnum = '0.0.0' - obj.uselib_local = 'libingen_client libingen_shared' - obj.uselib = ''' - CURL - FLOWCANVAS - GLADEMM - GLIBMM - GNOMECANVASMM - GTKMM - RAUL - REDLANDMM - SIGCPP - SLV2 - SOUP - XML2 - ''' diff --git a/src/libs/module/Makefile.am b/src/libs/module/Makefile.am deleted file mode 100644 index 84d499e4..00000000 --- a/src/libs/module/Makefile.am +++ /dev/null @@ -1,22 +0,0 @@ -noinst_LTLIBRARIES = libingen_module.la - -libingen_module_la_CXXFLAGS = \ - @INGEN_CFLAGS@ \ - @GLIBMM_CFLAGS@ \ - @REDLANDMM_CFLAGS@ \ - @RAUL_CFLAGS@ \ - @SLV2_CFLAGS@ \ - -DINGEN_MODULE_DIR=\"$(libdir)/ingen\" - -libingen_module_la_LIBADD = \ - @GLIBMM_LIBS@ \ - @REDLANDMM_LIBS@ \ - @RAUL_LIBS@ \ - @SLV2_LIBS@ - -libingen_module_la_SOURCES = \ - global.hpp \ - global.cpp \ - World.hpp \ - Module.hpp \ - Module.cpp diff --git a/src/libs/module/Module.cpp b/src/libs/module/Module.cpp deleted file mode 100644 index 8d1399b1..00000000 --- a/src/libs/module/Module.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include -#include -#include - -#ifndef INGEN_MODULE_DIR -#error This file expects INGEN_MODULE_DIR to be defined. -#endif - -using namespace std; - -namespace Ingen { -namespace Shared { - - -/** Load a dynamic module from the default path. - * - * This will check in the directories specified in the environment variable - * INGEN_MODULE_PATH (typical colon delimited format), then the default module - * installation directory (ie /usr/local/lib/ingen), in that order. - * - * \param name The base name of the module, e.g. "ingen_serialisation" - */ -SharedPtr -load_module(const string& name) -{ - Glib::Module* module = NULL; - - // Search INGEN_MODULE_PATH first - bool module_path_found; - string module_path = Glib::getenv("INGEN_MODULE_PATH", module_path_found); - if (module_path_found) { - string dir; - istringstream iss(module_path); - while (getline(iss, dir, ':')) { - - string filename = Glib::Module::build_path(dir, name); - if (Glib::file_test(filename, Glib::FILE_TEST_EXISTS)) { - module = new Glib::Module(filename, Glib::MODULE_BIND_LAZY); - - if (*module) { - cerr << "Loaded module \"" << name << "\" from " << filename << endl; - return SharedPtr(module); - } else { - delete module; - cerr << Glib::Module::get_last_error() << endl; - } - } - } - } - - // Try default directory if not found - module = new Glib::Module( - Glib::Module::build_path(INGEN_MODULE_DIR, name), - Glib::MODULE_BIND_LAZY); - - if (*module) { - cerr << "Loaded module \"" << name << "\" from " << INGEN_MODULE_DIR << endl; - return SharedPtr(module); - } else { - cerr << "Unable to load module \"" << name << "\", is Ingen installed?" << endl - << "Use ./ingen.dev to run from the source tree." << endl; - return SharedPtr(); - } -} - - -} // namespace Shared -} // namespace Ingen - diff --git a/src/libs/module/Module.hpp b/src/libs/module/Module.hpp deleted file mode 100644 index 2d9ba382..00000000 --- a/src/libs/module/Module.hpp +++ /dev/null @@ -1,35 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include - -namespace Ingen { -namespace Shared { - - -/** Load a dynamic module from the default path. - * - * \param name The base name of the module, e.g. "ingen_serialisation" - */ -SharedPtr load_module(const std::string& name); - - -} // namespace Shared -} // namespace Ingen - diff --git a/src/libs/module/World.hpp b/src/libs/module/World.hpp deleted file mode 100644 index dbbac2bd..00000000 --- a/src/libs/module/World.hpp +++ /dev/null @@ -1,78 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef INGEN_WORLD_HPP -#define INGEN_WORLD_HPP - -#include CONFIG_H_PATH - -#include -#include -#include -#include "shared/LV2Features.hpp" - -#ifdef HAVE_SLV2 -#include -#endif - -namespace Redland { class World; } - -namespace Ingen { -class Engine; - -namespace Serialisation { class Serialiser; class Parser; } -using Serialisation::Serialiser; -using Serialisation::Parser; - -namespace Shared { -class EngineInterface; -class Store; - - -/** The "world" all Ingen modules may share. - * - * This is required for shared access to things like Redland, so locking can - * take place centrally and the engine/gui using the same library won't - * explode horribly. - * - * Hopefully at some point in the future it can allow some fun things like - * scripting bindings that play with all loaded components of - * The Ingen System(TM) and whatnot. - */ -struct World { -#ifdef HAVE_SLV2 - SLV2World slv2_world; - LV2Features* lv2_features; -#endif - - Redland::World* rdf_world; - - SharedPtr engine; - SharedPtr local_engine; - SharedPtr serialiser; - SharedPtr parser; - SharedPtr store; - - SharedPtr serialisation_module; -}; - - -} // namespace Shared -} // namespace Ingen - -#endif // INGEN_WORLD_HPP - diff --git a/src/libs/module/global.cpp b/src/libs/module/global.cpp deleted file mode 100644 index c78f9fc3..00000000 --- a/src/libs/module/global.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include "global.hpp" -#include "World.hpp" - -#include CONFIG_H_PATH -#ifdef HAVE_SLV2 -#include -#endif - -using namespace std; - -namespace Ingen { -namespace Shared { - -static World* world = NULL; - - -World* -get_world() -{ - static World* world = NULL; - - if (!world) { - world = new World(); - world->rdf_world = new Redland::World(); -#ifdef HAVE_SLV2 - world->slv2_world = slv2_world_new_using_rdf_world(world->rdf_world->world()); - world->lv2_features = new LV2Features(); - slv2_world_load_all(world->slv2_world); -#endif - world->engine.reset(); - world->local_engine.reset(); - } - - return world; -} - - -void -destroy_world() -{ - if (world) { -#ifdef HAVE_SLV2 - slv2_world_free(world->slv2_world); - delete world->lv2_features; -#endif - delete world->rdf_world; - delete world; - world = NULL; - } -} - - -} // namesace Shared -} // namespace Ingen - diff --git a/src/libs/module/global.hpp b/src/libs/module/global.hpp deleted file mode 100644 index 1aa46be6..00000000 --- a/src/libs/module/global.hpp +++ /dev/null @@ -1,33 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef INGEN_GLOBAL_H -#define INGEN_GLOBAL_H - -namespace Ingen { -namespace Shared { - -class World; - -World* get_world(); -void destroy_world(); - -} // namesace Shared -} // namespace Ingen - -#endif // INGEN_GLOBAL_H - diff --git a/src/libs/module/wscript b/src/libs/module/wscript deleted file mode 100644 index e202342d..00000000 --- a/src/libs/module/wscript +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env python -import Params - -def build(bld): - dir = Params.g_options.moduledir or bld.env()['PREFIX'] + 'lib/ingen' - obj = bld.create_obj('cpp', 'shlib') - obj.source = ''' - Module.cpp - global.cpp - ''' - obj.includes = ['..', '../../common', '../..'] - obj.defines = 'INGEN_MODULE_DIR=\\\"' + dir + '\\\"' - obj.name = 'libingen_module' - obj.target = 'ingen_module' - obj.uselib = 'GLIBMM SLV2 RAUL REDLANDMM' - obj.vnum = '0.0.0' - diff --git a/src/libs/serialisation/Makefile.am b/src/libs/serialisation/Makefile.am deleted file mode 100644 index 57e99055..00000000 --- a/src/libs/serialisation/Makefile.am +++ /dev/null @@ -1,24 +0,0 @@ -MAINTAINERCLEANFILES = Makefile.in - -moduledir = $(libdir)/ingen - -module_LTLIBRARIES = libingen_serialisation.la - -libingen_serialisation_la_CXXFLAGS = \ - @INGEN_CFLAGS@ \ - @REDLANDMM_CFLAGS@ \ - @RAUL_CFLAGS@ \ - @GLIBMM_CFLAGS@ \ - @SLV2_CFLAGS@ - -libingen_serialisation_la_LDFLAGS = -no-undefined -module -avoid-version -libingen_serialisation_la_LIBADD = @RAUL_LIBS@ @REDLANDMM_LIBS@ @GLIBMM_LIBS@ @SLV2_LIBS@ - -libingen_serialisation_la_SOURCES = \ - Parser.cpp \ - Parser.hpp \ - Serialiser.cpp \ - Serialiser.hpp \ - serialisation.cpp \ - serialisation.hpp - diff --git a/src/libs/serialisation/Parser.cpp b/src/libs/serialisation/Parser.cpp deleted file mode 100644 index aed49a38..00000000 --- a/src/libs/serialisation/Parser.cpp +++ /dev/null @@ -1,587 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "interface/EngineInterface.hpp" -#include "Parser.hpp" - -using namespace std; -using namespace Raul; -using namespace Ingen::Shared; - -namespace Ingen { -namespace Serialisation { - -#define NS_INGEN "http://drobilla.net/ns/ingen#" - - -Glib::ustring -Parser::uri_relative_to_base(Glib::ustring base, const Glib::ustring uri) -{ - base = base.substr(0, base.find_last_of("/")+1); - Glib::ustring ret; - if (uri.length() > base.length() && uri.substr(0, base.length()) == base) - ret = uri.substr(base.length()); - else - ret = uri; - return ret; -} - - -/** Parse a patch from RDF into a CommonInterface (engine or client). - * - * @param document_uri URI of file to load objects from. - * @param parent Path of parent under which to load objects. - * @return whether or not load was successful. - */ -bool -Parser::parse_document( - Ingen::Shared::World* world, - Ingen::Shared::CommonInterface* target, - const Glib::ustring& document_uri, - Glib::ustring object_uri, - Glib::ustring engine_base, - boost::optional symbol, - boost::optional data) -{ - Redland::Model model(*world->rdf_world, document_uri, document_uri); - - if (object_uri == document_uri || object_uri == "") - cout << "Parsing document " << object_uri << " (base " << document_uri << ")" << endl; - else - cout << "Parsing " << object_uri << " from " << document_uri << endl; - - return parse(world, target, model, document_uri, engine_base, object_uri, symbol, data);; -} - - -bool -Parser::parse_string( - Ingen::Shared::World* world, - Ingen::Shared::CommonInterface* target, - const Glib::ustring& str, - const Glib::ustring& base_uri, - Glib::ustring engine_base, - boost::optional object_uri, - boost::optional symbol, - boost::optional data) -{ - Redland::Model model(*world->rdf_world, str.c_str(), str.length(), base_uri); - - if (object_uri) - cout << "Parsing " << object_uri.get() << " (base " << base_uri << ")" << endl; - else - cout << "Parsing all objects found in string (base " << base_uri << ")" << endl; - - bool ret = parse(world, target, model, base_uri, engine_base, object_uri, symbol, data); - if (ret) { - const Glib::ustring subject = Glib::ustring("<") + base_uri + Glib::ustring(">"); - parse_connections(world, target, model, base_uri, subject, - Path((engine_base == "") ? "/" : engine_base)); - } - - return ret; -} - - -bool -Parser::parse( - Ingen::Shared::World* world, - Ingen::Shared::CommonInterface* target, - Redland::Model& model, - Glib::ustring base_uri, - Glib::ustring engine_base, - boost::optional object_uri, - boost::optional symbol, - boost::optional data) -{ - const Redland::Node::Type res = Redland::Node::RESOURCE; - Glib::ustring query_str; - if (object_uri && object_uri.get()[0] == '/') - object_uri = object_uri.get().substr(1); - - if (object_uri) - query_str = Glib::ustring("SELECT DISTINCT ?class WHERE { <") + object_uri.get() + "> a ?class . }"; - else - query_str = Glib::ustring("SELECT DISTINCT ?subject ?class WHERE { ?subject a ?class . }"); - - Redland::Query query(*world->rdf_world, query_str); - Redland::Query::Results results = query.run(*world->rdf_world, model, base_uri); - - const Redland::Node patch_class(*world->rdf_world, res, NS_INGEN "Patch"); - const Redland::Node node_class(*world->rdf_world, res, NS_INGEN "Node"); - const Redland::Node in_port_class(*world->rdf_world, res, NS_INGEN "InputPort"); - const Redland::Node out_port_class(*world->rdf_world, res, NS_INGEN "OutputPort"); - - string subject_str = ((object_uri && object_uri.get() != "") ? object_uri.get() : base_uri); - if (subject_str[0] == '/') - subject_str = subject_str.substr(1); - if (subject_str == "") - subject_str = base_uri; - - const Redland::Node subject_uri(*world->rdf_world, res, subject_str); - - bool ret = false; - - for (Redland::Query::Results::iterator i = results.begin(); i != results.end(); ++i) { - const Redland::Node& subject = (object_uri ? subject_uri : (*i)["subject"]); - const Redland::Node& rdf_class = (*i)["class"]; - if (!object_uri) { - std::string path_str = uri_relative_to_base(base_uri, subject.to_c_string()); - if (path_str[0] != '/') - path_str = string("/").append(path_str); - if (Path(path_str).parent() != "/") - continue; - } - - if (rdf_class == patch_class || rdf_class == node_class || - rdf_class == in_port_class || rdf_class == out_port_class) { - Raul::Path path("/"); - if (base_uri != subject.to_c_string()) { - string path_str = (string)uri_relative_to_base(base_uri, subject.to_c_string()); - if (path_str[0] != '/') - path_str = string("/").append(path_str); - if (Path::is_valid(path_str)) { - path = path_str; - } else { - cerr << "[Parser] ERROR: Invalid path '" << path << "'" << endl; - continue; - } - } - - if (path.parent() != "/") - continue; - - if (rdf_class == patch_class) { - ret = parse_patch(world, target, model, base_uri, engine_base, - subject.to_c_string(), data); - if (ret) - target->set_variable(path, "ingen:document", Atom(base_uri.c_str())); - } else if (rdf_class == node_class) { - ret = parse_node(world, target, model, - base_uri, Glib::ustring("<") + subject.to_c_string() + ">", path, data); - } else if (rdf_class == in_port_class || rdf_class == out_port_class) { - ret = parse_port(world, target, model, - base_uri, Glib::ustring("<") + subject.to_c_string() + ">", path, data); - } - if (ret == false) { - cerr << "Failed to parse object " << object_uri << endl; - return ret; - } - } - - } - - return ret; -} - - -bool -Parser::parse_patch( - Ingen::Shared::World* world, - Ingen::Shared::CommonInterface* target, - Redland::Model& model, - const Glib::ustring& base_uri, - Glib::ustring engine_base, - const Glib::ustring& object_uri, - boost::optional data=boost::optional()) -{ - std::set created; - uint32_t patch_poly = 0; - - /* Use parameter overridden polyphony, if given */ - if (data) { - GraphObject::Variables::iterator poly_param = data.get().find("ingen:polyphony"); - if (poly_param != data.get().end() && poly_param->second.type() == Atom::INT) - patch_poly = poly_param->second.get_int32(); - } - - Glib::ustring subject = ((object_uri[0] == '<') - ? object_uri : Glib::ustring("<") + object_uri + ">"); - - if (subject[0] == '<' && subject[1] == '/') - subject = string("<").append(subject.substr(2)); - - //cout << "**** LOADING PATCH URI " << object_uri << ", SUBJ " << subject - // << ", ENG BASE " << engine_base << endl; - - /* Get polyphony from file (mandatory if not specified in parameters) */ - if (patch_poly == 0) { - Redland::Query query(*world->rdf_world, Glib::ustring( - "SELECT DISTINCT ?poly WHERE { ") + subject + " ingen:polyphony ?poly\n }"); - - Redland::Query::Results results = query.run(*world->rdf_world, model, base_uri); - - if (results.size() == 0) { - cerr << "[Parser] ERROR: No polyphony found!" << endl; - cerr << "Query was:" << endl << query.string() << endl; - return false; - } - - const Redland::Node& poly_node = (*results.begin())["poly"]; - assert(poly_node.is_int()); - patch_poly = static_cast(poly_node.to_int()); - } - - string symbol = uri_relative_to_base(base_uri, object_uri); - symbol = symbol.substr(0, symbol.find(".")); - Path patch_path("/"); - if (engine_base == "") - patch_path = "/"; - else if (engine_base[engine_base.length()-1] == '/') - patch_path = Path(engine_base + symbol); - else if (Path::is_valid(engine_base)) - patch_path = (Path)engine_base; - else if (Path::is_valid(string("/").append(engine_base))) - patch_path = (Path)(string("/").append(engine_base)); - else - cerr << "WARNING: Illegal engine base path '" << engine_base << "', loading patch to root" << endl; - - //if (patch_path != "/") - target->new_patch(patch_path, patch_poly); - - /* Plugin nodes */ - Redland::Query query(*world->rdf_world, Glib::ustring( - "SELECT DISTINCT ?name ?plugin ?varkey ?varval ?poly WHERE {\n") + - subject + " ingen:node ?node .\n" - "?node lv2:symbol ?name ;\n" - " ingen:plugin ?plugin ;\n" - " ingen:polyphonic ?poly .\n" - "OPTIONAL { ?node lv2var:variable ?variable .\n" - " ?variable rdf:predicate ?varkey ;\n" - " rdf:value ?varval .\n" - " }" - "}"); - - Redland::Query::Results results = query.run(*world->rdf_world, model, base_uri); - world->rdf_world->mutex().lock(); - for (Redland::Query::Results::iterator i = results.begin(); i != results.end(); ++i) { - const string node_name = (*i)["name"].to_string(); - const Path node_path = patch_path.base() + node_name; - - if (created.find(node_path) == created.end()) { - const string node_plugin = (*i)["plugin"].to_string(); - bool node_polyphonic = false; - - const Redland::Node& poly_node = (*i)["poly"]; - if (poly_node.is_bool() && poly_node.to_bool() == true) - node_polyphonic = true; - - target->new_node(node_path, node_plugin); - target->set_property(node_path, "ingen:polyphonic", node_polyphonic); - created.insert(node_path); - } - - const string key = world->rdf_world->prefixes().qualify((*i)["varkey"].to_string()); - const Redland::Node& val_node = (*i)["varval"]; - - if (key != "") - target->set_variable(node_path, key, AtomRDF::node_to_atom(val_node)); - } - world->rdf_world->mutex().unlock(); - - - /* Load subpatches */ - query = Redland::Query(*world->rdf_world, Glib::ustring( - "SELECT DISTINCT ?subpatch ?symbol WHERE {\n") + - subject + " ingen:node ?subpatch .\n" - "?subpatch a ingen:Patch ;\n" - " lv2:symbol ?symbol .\n" - "}"); - - results = query.run(*world->rdf_world, model, base_uri); - for (Redland::Query::Results::iterator i = results.begin(); i != results.end(); ++i) { - const string symbol = (*i)["symbol"].to_string(); - const string subpatch = (*i)["subpatch"].to_string(); - - const Path subpatch_path = patch_path.base() + symbol; - - if (created.find(subpatch_path) == created.end()) { - string subpatch_rel = uri_relative_to_base(base_uri, subpatch); - string sub_base = engine_base; - if (sub_base[sub_base.length()-1] == '/') - sub_base = sub_base.substr(sub_base.length()-1); - sub_base.append("/").append(symbol); - created.insert(subpatch_path); - parse_patch(world, target, model, base_uri, subpatch_rel, sub_base); - } - } - - - /* Set node port control values */ - query = Redland::Query(*world->rdf_world, Glib::ustring( - "SELECT DISTINCT ?nodename ?portname ?portval WHERE {\n") + - subject + " ingen:node ?node .\n" - "?node lv2:symbol ?nodename ;\n" - " ingen:port ?port .\n" - "?port lv2:symbol ?portname ;\n" - " ingen:value ?portval .\n" - "FILTER ( datatype(?portval) = xsd:decimal )\n" - "}"); - - results = query.run(*world->rdf_world, model, base_uri); - for (Redland::Query::Results::iterator i = results.begin(); i != results.end(); ++i) { - const string node_name = (*i)["nodename"].to_string(); - const string port_name = (*i)["portname"].to_string(); - - assert(Path::is_valid_name(node_name)); - assert(Path::is_valid_name(port_name)); - const Path port_path = patch_path.base() + node_name + "/" + port_name; - - target->set_port_value(port_path, AtomRDF::node_to_atom((*i)["portval"])); - } - - - /* Load this patch's ports */ - query = Redland::Query(*world->rdf_world, Glib::ustring( - "SELECT DISTINCT ?port ?type ?name ?datatype ?varkey ?varval ?portval WHERE {\n") + - subject + " ingen:port ?port .\n" - "?port a ?type ;\n" - " a ?datatype ;\n" - " lv2:symbol ?name .\n" - " FILTER (?type != ?datatype && ((?type = ingen:InputPort) || (?type = ingen:OutputPort)))\n" - "OPTIONAL { ?port ingen:value ?portval . \n" - " FILTER ( datatype(?portval) = xsd:decimal ) }\n" - "OPTIONAL { ?port lv2var:variable ?variable .\n" - " ?variable rdf:predicate ?varkey ;\n" - " rdf:value ?varval .\n" - " }" - "}"); - - results = query.run(*world->rdf_world, model, base_uri); - world->rdf_world->mutex().lock(); - for (Redland::Query::Results::iterator i = results.begin(); i != results.end(); ++i) { - const string name = (*i)["name"].to_string(); - const string type = world->rdf_world->qualify((*i)["type"].to_string()); - const string datatype = world->rdf_world->qualify((*i)["datatype"].to_string()); - - assert(Path::is_valid_name(name)); - const Path port_path = patch_path.base() + name; - - if (created.find(port_path) == created.end()) { - bool is_output = (type == "ingen:OutputPort"); // FIXME: check validity - // FIXME: read index - target->new_port(port_path, 0, datatype, is_output); - created.insert(port_path); - } - - const Redland::Node& val_node = (*i)["portval"]; - target->set_port_value(patch_path.base() + name, AtomRDF::node_to_atom(val_node)); - - const string key = world->rdf_world->prefixes().qualify((*i)["varkey"].to_string()); - const Redland::Node& var_val_node = (*i)["varval"]; - - if (key != "") - target->set_variable(patch_path.base() + name, key, AtomRDF::node_to_atom(var_val_node)); - } - world->rdf_world->mutex().unlock(); - - created.clear(); - - parse_connections(world, target, model, base_uri, subject, patch_path); - parse_variables(world, target, model, base_uri, subject, patch_path, data); - - /* Enable */ - query = Redland::Query(*world->rdf_world, Glib::ustring( - "SELECT DISTINCT ?enabled WHERE { ") + subject + " ingen:enabled ?enabled }"); - - results = query.run(*world->rdf_world, model, base_uri); - for (Redland::Query::Results::iterator i = results.begin(); i != results.end(); ++i) { - const Redland::Node& enabled_node = (*i)["enabled"]; - if (enabled_node.is_bool() && enabled_node) { - target->set_property(patch_path, "ingen:enabled", (bool)true); - break; - } else { - cerr << "WARNING: Unknown type for property ingen:enabled" << endl; - } - } - - return true; -} - - -bool -Parser::parse_node( - Ingen::Shared::World* world, - Ingen::Shared::CommonInterface* target, - Redland::Model& model, - const Glib::ustring& base_uri, - const Glib::ustring& subject, - const Raul::Path& path, - boost::optional data=boost::optional()) -{ - /* Get plugin */ - Redland::Query query(*world->rdf_world, Glib::ustring( - "SELECT DISTINCT ?plug WHERE { ") + subject + " ingen:plugin ?plug }"); - - Redland::Query::Results results = query.run(*world->rdf_world, model, base_uri); - - if (results.size() == 0) { - cerr << "[Parser] ERROR: Node missing mandatory ingen:plugin property" << endl; - return false; - } - - const Redland::Node& plugin_node = (*results.begin())["plug"]; - if (plugin_node.type() != Redland::Node::RESOURCE) { - cerr << "[Parser] ERROR: node's ingen:plugin property is not a resource" << endl; - return false; - } - - target->new_node(path, world->rdf_world->expand_uri(plugin_node.to_c_string())); - parse_variables(world, target, model, base_uri, subject, path, data); - - return true; -} - - -bool -Parser::parse_port( - Ingen::Shared::World* world, - Ingen::Shared::CommonInterface* target, - Redland::Model& model, - const Glib::ustring& base_uri, - const Glib::ustring& subject, - const Raul::Path& path, - boost::optional data) -{ - Redland::Query query(*world->rdf_world, Glib::ustring( - "SELECT DISTINCT ?type ?datatype ?value WHERE {\n") + - subject + " a ?type ;\n" - " a ?datatype .\n" - " FILTER (?type != ?datatype && ((?type = ingen:InputPort) || (?type = ingen:OutputPort)))\n" - "OPTIONAL { " + subject + " ingen:value ?value . }\n" - "}"); - - Redland::Query::Results results = query.run(*world->rdf_world, model, base_uri); - world->rdf_world->mutex().lock(); - - for (Redland::Query::Results::iterator i = results.begin(); i != results.end(); ++i) { - const string type = world->rdf_world->qualify((*i)["type"].to_string()); - const string datatype = world->rdf_world->qualify((*i)["datatype"].to_string()); - - bool is_output = (type == "ingen:OutputPort"); - // FIXME: read index - target->new_port(path, 0, datatype, is_output); - - const Redland::Node& val_node = (*i)["value"]; - if (val_node.to_string() != "") - target->set_port_value(path, AtomRDF::node_to_atom(val_node)); - } - world->rdf_world->mutex().unlock(); - - return parse_variables(world, target, model, base_uri, subject, path, data); -} - - -bool -Parser::parse_connections( - Ingen::Shared::World* world, - Ingen::Shared::CommonInterface* target, - Redland::Model& model, - const Glib::ustring& base_uri, - const Glib::ustring& subject, - const Raul::Path& parent) -{ - Redland::Query query(*world->rdf_world, Glib::ustring( - "SELECT DISTINCT ?src ?dst WHERE {\n") - /*+ subject*/ + /*"?foo ingen:connection ?connection .\n"*/ - "?connection ingen:source ?src ;\n" - " ingen:destination ?dst .\n" - "}"); - - Redland::Query::Results results = query.run(*world->rdf_world, model, base_uri); - for (Redland::Query::Results::iterator i = results.begin(); i != results.end(); ++i) { - string src_path = parent.base() + uri_relative_to_base(base_uri, (*i)["src"].to_string()); - if (!Path::is_valid(src_path)) { - cerr << "ERROR: Invalid path in connection: " << src_path << endl; - continue; - } - - string dst_path = parent.base() + uri_relative_to_base(base_uri, (*i)["dst"].to_string()); - if (!Path::is_valid(dst_path)) { - cerr << "ERROR: Invalid path in connection: " << dst_path << endl; - continue; - } - - target->connect(src_path, dst_path); - } - - return true; -} - - -bool -Parser::parse_variables( - Ingen::Shared::World* world, - Ingen::Shared::CommonInterface* target, - Redland::Model& model, - const Glib::ustring& base_uri, - const Glib::ustring& subject, - const Raul::Path& path, - boost::optional data=boost::optional()) -{ - Redland::Query query(*world->rdf_world, Glib::ustring( - "SELECT DISTINCT ?varkey ?varval WHERE {\n") + - subject + " lv2var:variable ?variable .\n" - "?variable rdf:predicate ?varkey ;\n" - " rdf:value ?varval .\n" - "}"); - - Redland::Query::Results results = query.run(*world->rdf_world, model, base_uri); - for (Redland::Query::Results::iterator i = results.begin(); i != results.end(); ++i) { - const string key = world->rdf_world->prefixes().qualify(string((*i)["varkey"])); - const Redland::Node& val_node = (*i)["varval"]; - if (key != "") - target->set_variable(path, key, AtomRDF::node_to_atom(val_node)); - } - - query = Redland::Query(*world->rdf_world, Glib::ustring( - "SELECT DISTINCT ?key ?val WHERE {\n") + - subject + " ingen:property ?property .\n" - "?property rdf:predicate ?key ;\n" - " rdf:value ?val .\n" - "}"); - - results = query.run(*world->rdf_world, model, base_uri); - for (Redland::Query::Results::iterator i = results.begin(); i != results.end(); ++i) { - const string key = world->rdf_world->prefixes().qualify(string((*i)["key"])); - const Redland::Node& val_node = (*i)["val"]; - if (key != "") - target->set_property(path, key, AtomRDF::node_to_atom(val_node)); - } - - // Set passed variables last to override any loaded values - if (data) - for (GraphObject::Variables::const_iterator i = data.get().begin(); i != data.get().end(); ++i) - target->set_variable(path, i->first, i->second); - - return true; -} - - -} // namespace Serialisation -} // namespace Ingen - diff --git a/src/libs/serialisation/Parser.hpp b/src/libs/serialisation/Parser.hpp deleted file mode 100644 index 7b8a35eb..00000000 --- a/src/libs/serialisation/Parser.hpp +++ /dev/null @@ -1,126 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef LOADER_H -#define LOADER_H - -#include -#include -#include -#include -#include -#include -#include "interface/GraphObject.hpp" -#include "module/World.hpp" - -namespace Redland { class World; class Model; } -namespace Ingen { namespace Shared { class CommonInterface; } } - -using namespace Ingen::Shared; - -namespace Ingen { -namespace Serialisation { - - -class Parser { -public: - virtual ~Parser() {} - - virtual bool parse_document( - Ingen::Shared::World* world, - Shared::CommonInterface* target, - const Glib::ustring& document_uri, - Glib::ustring engine_base, - Glib::ustring object_uri, - boost::optional symbol=boost::optional(), - boost::optional data=boost::optional()); - - virtual bool parse_string( - Ingen::Shared::World* world, - Shared::CommonInterface* target, - const Glib::ustring& str, - const Glib::ustring& base_uri, - Glib::ustring engine_base, - boost::optional object_uri=boost::optional(), - boost::optional symbol=boost::optional(), - boost::optional data=boost::optional()); - -private: - - Glib::ustring uri_relative_to_base(Glib::ustring base, const Glib::ustring uri); - - bool parse( - Ingen::Shared::World* world, - Shared::CommonInterface* target, - Redland::Model& model, - Glib::ustring base_uri, - Glib::ustring engine_base, - boost::optional object_uri=boost::optional(), - boost::optional symbol=boost::optional(), - boost::optional data=boost::optional()); - - bool parse_patch( - Ingen::Shared::World* world, - Ingen::Shared::CommonInterface* target, - Redland::Model& model, - const Glib::ustring& base_uri, - Glib::ustring engine_base, - const Glib::ustring& object_uri, - boost::optional data); - - bool parse_node( - Ingen::Shared::World* world, - Ingen::Shared::CommonInterface* target, - Redland::Model& model, - const Glib::ustring& base_uri, - const Glib::ustring& subject, - const Raul::Path& path, - boost::optional data); - - bool parse_port( - Ingen::Shared::World* world, - Ingen::Shared::CommonInterface* target, - Redland::Model& model, - const Glib::ustring& base_uri, - const Glib::ustring& subject, - const Raul::Path& path, - boost::optional data=boost::optional()); - - bool parse_variables( - Ingen::Shared::World* world, - Ingen::Shared::CommonInterface* target, - Redland::Model& model, - const Glib::ustring& base_uri, - const Glib::ustring& subject, - const Raul::Path& path, - boost::optional data); - - bool parse_connections( - Ingen::Shared::World* world, - Ingen::Shared::CommonInterface* target, - Redland::Model& model, - const Glib::ustring& base_uri, - const Glib::ustring& subject, - const Raul::Path& parent); - -}; - - -} // namespace Serialisation -} // namespace Ingen - -#endif // LOADER_H diff --git a/src/libs/serialisation/Serialiser.cpp b/src/libs/serialisation/Serialiser.cpp deleted file mode 100644 index 2f0d9877..00000000 --- a/src/libs/serialisation/Serialiser.cpp +++ /dev/null @@ -1,501 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include // atof -#include -#include -#include -#include -#include -#include -#include // pair, make_pair -#include -#include -#include -#include -#include -#include -#include -#include -#include "module/World.hpp" -#include "interface/EngineInterface.hpp" -#include "interface/Plugin.hpp" -#include "interface/Patch.hpp" -#include "interface/Node.hpp" -#include "interface/Port.hpp" -#include "interface/Connection.hpp" -#include "Serialiser.hpp" - -using namespace std; -using namespace Raul; -using namespace Redland; -using namespace Ingen; -using namespace Ingen::Shared; - -namespace Ingen { -namespace Serialisation { - - -Serialiser::Serialiser(Shared::World& world, SharedPtr store) - : _root_path("/") - , _store(store) - , _world(*world.rdf_world) -{ -} - - -void -Serialiser::to_file(SharedPtr object, const string& filename) -{ - _root_path = object->path(); - start_to_filename(filename); - serialise(object); - finish(); -} - - - -string -Serialiser::to_string(SharedPtr object, - const string& base_uri, - const GraphObject::Variables& extra_rdf) -{ - start_to_string(object->path(), base_uri); - serialise(object); - - Redland::Node base_rdf_node(_model->world(), Redland::Node::RESOURCE, base_uri); - for (GraphObject::Variables::const_iterator v = extra_rdf.begin(); v != extra_rdf.end(); ++v) { - if (v->first.find(":") != string::npos) { - _model->add_statement(base_rdf_node, v->first, - AtomRDF::atom_to_node(_model->world(), v->second)); - } else { - cerr << "Warning: not serialising extra RDF with key '" << v->first << "'" << endl; - } - } - - return finish(); -} - - -/** Begin a serialization to a file. - * - * This must be called before any serializing methods. - */ -void -Serialiser::start_to_filename(const string& filename) -{ - setlocale(LC_NUMERIC, "C"); - - assert(filename.find(":") == string::npos || filename.substr(0, 5) == "file:"); - if (filename.find(":") == string::npos) - _base_uri = "file://" + filename; - else - _base_uri = filename; - _model = new Redland::Model(_world); - _model->set_base_uri(_base_uri); - _mode = TO_FILE; -} - - -/** Begin a serialization to a string. - * - * This must be called before any serializing methods. - * - * The results of the serialization will be returned by the finish() method after - * the desired objects have been serialised. - * - * All serialized paths will have the root path chopped from their prefix - * (therefore all serialized paths must be descendants of the root) - */ -void -Serialiser::start_to_string(const Raul::Path& root, const string& base_uri) -{ - setlocale(LC_NUMERIC, "C"); - - _root_path = root; - _base_uri = base_uri; - _model = new Redland::Model(_world); - _model->set_base_uri(base_uri); - _mode = TO_STRING; -} - - -/** Finish a serialization. - * - * If this was a serialization to a string, the serialization output - * will be returned, otherwise the empty string is returned. - */ -string -Serialiser::finish() -{ - string ret = ""; - if (_mode == TO_FILE) { - _model->serialise_to_file(_base_uri); - } else { - char* c_str = _model->serialise_to_string(); - ret = c_str; - free(c_str); - } - - _base_uri = ""; -#ifdef USE_BLANK_NODES - _node_map.clear(); -#endif - return ret; -} - - -Redland::Node -Serialiser::patch_path_to_rdf_node(const Path& path) -{ -#ifdef USE_BLANK_NODES - if (path == _root_path) { - return Redland::Node(_model->world(), Redland::Node::RESOURCE, _base_uri); - } else { - assert(path.length() > _root_path.length()); - return Redland::Node(_model->world(), Redland::Node::RESOURCE, - _base_uri + string("#") + path.substr(_root_path.length())); - } -#else - return path_to_rdf_node(path); -#endif -} - - - -/** Convert a path to an RDF blank node ID for serializing. - */ -Redland::Node -Serialiser::path_to_rdf_node(const Path& path) -{ -#if USE_BLANK_NODES - NodeMap::iterator i = _node_map.find(path); - if (i != _node_map.end()) { - assert(i->second); - assert(i->second.get_node()); - return i->second; - } else { - Redland::Node id = _world.blank_id(Path::nameify(path.substr(1))); - assert(id); - _node_map[path] = id; - return id; - } -#else - assert(_model); - assert(path.substr(0, _root_path.length()) == _root_path); - - if (path == _root_path) - return Redland::Node(_model->world(), Redland::Node::RESOURCE, _base_uri); - else - return Redland::Node(_model->world(), Redland::Node::RESOURCE, - path.substr(_root_path.base().length())); -#endif -} - - -#if 0 -/** Searches for the filename passed in the path, returning the full - * path of the file, or the empty string if not found. - * - * This function tries to be as friendly a black box as possible - if the path - * passed is an absolute path and the file is found there, it will return - * that path, etc. - * - * additional_path is a list (colon delimeted as usual) of additional - * directories to look in. ie the directory the parent patch resides in would - * be a good idea to pass as additional_path, in the case of a subpatch. - */ -string -Serialiser::find_file(const string& filename, const string& additional_path) -{ - string search_path = additional_path + ":" + _patch_search_path; - - // Try to open the raw filename first - std::ifstream is(filename.c_str(), std::ios::in); - if (is.good()) { - is.close(); - return filename; - } - - string directory; - string full_patch_path = ""; - - while (search_path != "") { - directory = search_path.substr(0, search_path.find(':')); - if (search_path.find(':') != string::npos) - search_path = search_path.substr(search_path.find(':')+1); - else - search_path = ""; - - full_patch_path = directory +"/"+ filename; - - std::ifstream is; - is.open(full_patch_path.c_str(), std::ios::in); - - if (is.good()) { - is.close(); - return full_patch_path; - } else { - cerr << "[Serialiser] Could not find patch file " << full_patch_path << endl; - } - } - - return ""; -} -#endif - -void -Serialiser::serialise(SharedPtr object) throw (std::logic_error) -{ - if (!_model) - throw std::logic_error("serialise called without serialization in progress"); - - SharedPtr patch = PtrCast(object); - if (patch) { - serialise_patch(patch); - return; - } - - SharedPtr node = PtrCast(object); - if (node) { - serialise_node(node, path_to_rdf_node(node->path())); - return; - } - - SharedPtr port = PtrCast(object); - if (port) { - serialise_port(port.get(), path_to_rdf_node(port->path())); - return; - } - - cerr << "[Serialiser] WARNING: Unsupported object type, " - << object->path() << " not serialised." << endl; -} - - -void -Serialiser::serialise_patch(SharedPtr patch) -{ - assert(_model); - - const Redland::Node patch_id = patch_path_to_rdf_node(patch->path()); - - _model->add_statement( - patch_id, - "rdf:type", - Redland::Node(_model->world(), Redland::Node::RESOURCE, "http://drobilla.net/ns/ingen#Patch")); - - GraphObject::Variables::const_iterator s = patch->variables().find("lv2:symbol"); - // If symbol is stored as a variable, write that - if (s != patch->variables().end()) { - _model->add_statement(patch_id, "lv2:symbol", - Redland::Node(_model->world(), Redland::Node::LITERAL, s->second.get_string())); - // Otherwise take the one from our path (if possible) - } else if (patch->path() != "/") { - _model->add_statement( - patch_id, "lv2:symbol", - Redland::Node(_model->world(), Redland::Node::LITERAL, patch->path().name())); - } - - _model->add_statement( - patch_id, - "ingen:polyphony", - AtomRDF::atom_to_node(_model->world(), Atom((int)patch->internal_polyphony()))); - - _model->add_statement( - patch_id, - "ingen:enabled", - AtomRDF::atom_to_node(_model->world(), Atom((bool)patch->enabled()))); - - serialise_variables(patch_id, patch->variables()); - - for (GraphObject::const_iterator n = _store->children_begin(patch); - n != _store->children_end(patch); ++n) { - - if (n->second->graph_parent() != patch.get()) - continue; - - SharedPtr patch = PtrCast(n->second); - SharedPtr node = PtrCast(n->second); - if (patch) { - _model->add_statement(patch_id, "ingen:node", patch_path_to_rdf_node(patch->path())); - serialise_patch(patch); - } else if (node) { - const Redland::Node node_id = path_to_rdf_node(n->second->path()); - _model->add_statement(patch_id, "ingen:node", node_id); - serialise_node(node, node_id); - } - } - - for (uint32_t i=0; i < patch->num_ports(); ++i) { - Port* p = patch->port(i); - const Redland::Node port_id = path_to_rdf_node(p->path()); - _model->add_statement(patch_id, "ingen:port", port_id); - serialise_port(p, port_id); - } - - for (Shared::Patch::Connections::const_iterator c = patch->connections().begin(); - c != patch->connections().end(); ++c) { - serialise_connection(patch, *c); - } -} - - -void -Serialiser::serialise_plugin(SharedPtr plugin) -{ - assert(_model); - - const Redland::Node plugin_id = Redland::Node(_model->world(), Redland::Node::RESOURCE, plugin->uri()); - - _model->add_statement( - plugin_id, - "rdf:type", - Redland::Node(_model->world(), Redland::Node::RESOURCE, plugin->type_uri())); -} - - -void -Serialiser::serialise_node(SharedPtr node, const Redland::Node& node_id) -{ - const Redland::Node plugin_id - = Redland::Node(_model->world(), Redland::Node::RESOURCE, node->plugin()->uri()); - - _model->add_statement( - node_id, - "rdf:type", - Redland::Node(_model->world(), Redland::Node::RESOURCE, "ingen:Node")); - - _model->add_statement( - node_id, - "lv2:symbol", - Redland::Node(_model->world(), Redland::Node::LITERAL, node->path().name())); - - _model->add_statement( - node_id, - "ingen:plugin", - plugin_id); - - _model->add_statement( - node_id, - "ingen:polyphonic", - AtomRDF::atom_to_node(_model->world(), Atom(node->polyphonic()))); - - //serialise_plugin(node->plugin()); - - for (uint32_t i=0; i < node->num_ports(); ++i) { - Port* p = node->port(i); - assert(p); - const Redland::Node port_id = path_to_rdf_node(p->path()); - serialise_port(p, port_id); - _model->add_statement(node_id, "ingen:port", port_id); - } - - serialise_variables(node_id, node->variables()); -} - - -/** Writes a port subject with various information only if there are some - * predicate/object pairs to go with it (eg if the port has variable, or a value, or..). - * Audio output ports with no variable will not be written, for example. - */ -void -Serialiser::serialise_port(const Port* port, const Redland::Node& port_id) -{ - if (port->is_input()) - _model->add_statement(port_id, "rdf:type", - Redland::Node(_model->world(), Redland::Node::RESOURCE, "ingen:InputPort")); - else - _model->add_statement(port_id, "rdf:type", - Redland::Node(_model->world(), Redland::Node::RESOURCE, "ingen:OutputPort")); - - _model->add_statement(port_id, "lv2:index", - AtomRDF::atom_to_node(_model->world(), Atom((int)port->index()))); - - _model->add_statement(port_id, "lv2:symbol", - Redland::Node(_model->world(), Redland::Node::LITERAL, port->path().name())); - - _model->add_statement(port_id, "rdf:type", - Redland::Node(_model->world(), Redland::Node::RESOURCE, port->type().uri())); - - if (port->type() == DataType::CONTROL && port->is_input()) - _model->add_statement(port_id, "ingen:value", - AtomRDF::atom_to_node(_model->world(), Atom(port->value()))); - - serialise_variables(port_id, port->variables()); -} - - -void -Serialiser::serialise_connection(SharedPtr parent, - SharedPtr connection) throw (std::logic_error) -{ - if (!_model) - throw std::logic_error("serialise_connection called without serialization in progress"); - - const Redland::Node src_node = path_to_rdf_node(connection->src_port_path()); - const Redland::Node dst_node = path_to_rdf_node(connection->dst_port_path()); - - /* This would allow associating data with the connection... */ - const Redland::Node connection_node = _world.blank_id(); - _model->add_statement(connection_node, "ingen:source", src_node); - _model->add_statement(connection_node, "ingen:destination", dst_node); - if (parent) { - const Redland::Node parent_node = path_to_rdf_node(parent->path()); - _model->add_statement(parent_node, "ingen:connection", connection_node); - } else { - _model->add_statement(connection_node, "rdf:type", - Redland::Node(_model->world(), Redland::Node::RESOURCE, "ingen:Connection")); - } - - /* ... but this is cleaner */ - //_model->add_statement(dst_node, "ingen:connectedTo", src_node); -} - - -void -Serialiser::serialise_variables(Redland::Node subject, const GraphObject::Variables& variables) -{ - for (GraphObject::Variables::const_iterator v = variables.begin(); v != variables.end(); ++v) { - if (v->first.find(":") != string::npos && v->first != "ingen:document") { - if (v->second.is_valid()) { - const Redland::Node var_id = _world.blank_id(); - const Redland::Node key(_model->world(), Redland::Node::RESOURCE, v->first); - const Redland::Node value = AtomRDF::atom_to_node(_model->world(), v->second); - if (value) { - _model->add_statement(subject, "lv2var:variable", var_id); - _model->add_statement(var_id, "rdf:predicate", key); - _model->add_statement(var_id, "rdf:value", value); - } else { - cerr << "Warning: can not serialise value: key '" << v->first << "', type " - << (int)v->second.type() << endl; - } - } else { - cerr << "Warning: variable with no value: key '" << v->first << "'" << endl; - } - } else { - cerr << "Warning: not serialising variable with invalid key '" << v->first << "'" << endl; - } - } -} - - -} // namespace Serialisation -} // namespace Ingen diff --git a/src/libs/serialisation/Serialiser.hpp b/src/libs/serialisation/Serialiser.hpp deleted file mode 100644 index f27cad83..00000000 --- a/src/libs/serialisation/Serialiser.hpp +++ /dev/null @@ -1,109 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef SERIALISER_H -#define SERIALISER_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "interface/GraphObject.hpp" -#include "shared/Store.hpp" - -using namespace Raul; -using namespace Ingen::Shared; - -namespace Ingen { - -namespace Shared { - class Plugin; - class GraphObject; - class Patch; - class Node; - class Port; - class Connection; - class World; -} - -namespace Serialisation { - - -/** Serialises Ingen objects (patches, nodes, etc) to RDF. - * - * \ingroup IngenClient - */ -class Serialiser -{ -public: - Serialiser(Shared::World& world, SharedPtr store); - - void to_file(SharedPtr object, const std::string& filename); - - std::string to_string(SharedPtr object, - const std::string& base_uri, - const GraphObject::Variables& extra_rdf); - - void start_to_string(const Raul::Path& root, const std::string& base_uri); - void serialise(SharedPtr object) throw (std::logic_error); - void serialise_connection(SharedPtr parent, - SharedPtr c) throw (std::logic_error); - - std::string finish(); - -private: - enum Mode { TO_FILE, TO_STRING }; - - void start_to_filename(const std::string& filename); - - void setup_prefixes(); - - void serialise_plugin(SharedPtr p); - - void serialise_patch(SharedPtr p); - void serialise_node(SharedPtr n, const Redland::Node& id); - void serialise_port(const Shared::Port* p, const Redland::Node& id); - - void serialise_variables(Redland::Node subject, const GraphObject::Variables& variables); - - Redland::Node path_to_rdf_node(const Path& path); - Redland::Node patch_path_to_rdf_node(const Path& path); - - Raul::Path _root_path; - SharedPtr _store; - Mode _mode; - std::string _base_uri; - Redland::World& _world; - Redland::Model* _model; - -#ifdef USE_BLANK_NODES - typedef std::map NodeMap; - NodeMap _node_map; -#endif -}; - - -} // namespace Serialisation -} // namespace Ingen - -#endif // SERIALISER_H diff --git a/src/libs/serialisation/serialisation.cpp b/src/libs/serialisation/serialisation.cpp deleted file mode 100644 index 1d08e76c..00000000 --- a/src/libs/serialisation/serialisation.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include CONFIG_H_PATH -#include "module/World.hpp" -#include "serialisation.hpp" -#include "Parser.hpp" -#include "Serialiser.hpp" - -namespace Ingen { -namespace Serialisation { - - -Ingen::Serialisation::Parser* -new_parser() -{ - return new Parser(); -} - - -Ingen::Serialisation::Serialiser* -new_serialiser(Ingen::Shared::World* world, SharedPtr store) -{ - assert(world->rdf_world); - return new Serialiser(*world, store); -} - - - -} // namespace Serialisation -} // namespace Ingen - diff --git a/src/libs/serialisation/serialisation.hpp b/src/libs/serialisation/serialisation.hpp deleted file mode 100644 index a250945b..00000000 --- a/src/libs/serialisation/serialisation.hpp +++ /dev/null @@ -1,44 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef INGEN_SERIALISATION_H -#define INGEN_SERIALISATION_H - -namespace Ingen { - -namespace Shared { class World; class Store; } - -namespace Serialisation { - -class Parser; -class Serialiser; - - -extern "C" { - - extern Parser* new_parser(); - extern Serialiser* new_serialiser(Ingen::Shared::World* world, - SharedPtr store); - -} - - -} // namespace Serialisation -} // namespace Ingen - -#endif // INGEN_SERIALISATION_H - diff --git a/src/libs/serialisation/wscript b/src/libs/serialisation/wscript deleted file mode 100644 index c3e98b81..00000000 --- a/src/libs/serialisation/wscript +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env python -import Params - -def build(bld): - obj = bld.create_obj('cpp', 'shlib') - obj.source = ''' - Parser.cpp - Serialiser.cpp - serialisation.cpp - ''' - obj.includes = ['..', '../../common', '../..'] - obj.name = 'libingen_serialisation' - obj.target = 'ingen_serialisation' - obj.uselib = 'GLIBMM SLV2 RAUL REDLANDMM' - obj.vnum = '0.0.0' - diff --git a/src/libs/shared/Builder.cpp b/src/libs/shared/Builder.cpp deleted file mode 100644 index 1eb127a5..00000000 --- a/src/libs/shared/Builder.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2008 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "Builder.hpp" -#include "common/interface/CommonInterface.hpp" -#include "common/interface/Patch.hpp" -#include "common/interface/Node.hpp" -#include "common/interface/Port.hpp" -#include "common/interface/Connection.hpp" -#include "common/interface/Plugin.hpp" -#include -using namespace std; - -namespace Ingen { -namespace Shared { - - -Builder::Builder(CommonInterface& interface) - : _interface(interface) -{ -} - - -void -Builder::build(const Raul::Path& prefix, SharedPtr object) -{ - SharedPtr patch = PtrCast(object); - if (patch) { - if (object->path() != "/") { - const std::string path_str = prefix.base() + object->path().substr(1); - //cout << "BUILDING PATCH " << path_str << endl; - _interface.new_patch(path_str, patch->internal_polyphony()); - } - - build_object(prefix, object); - for (Patch::Connections::const_iterator i = patch->connections().begin(); - i != patch->connections().end(); ++i) { - string base = prefix.base() + object->path().substr(1); - cout << "*********** BASE: " << base << endl; - _interface.connect(base + (*i)->src_port_path().substr(1), - base + (*i)->dst_port_path().substr(1)); - } - return; - } - - SharedPtr node = PtrCast(object); - if (node) { - Raul::Path path = prefix.base() + node->path().substr(1); - //cout << "BUILDING NODE " << path << endl; - _interface.new_node(path, node->plugin()->uri()); - build_object(prefix, object); - return; - } - - SharedPtr port = PtrCast(object); - if (port) { - Raul::Path path = prefix.base() + port->path().substr(1); - //cout << "BUILDING PORT " << path << endl; - _interface.new_port(path, port->index(), port->type().uri(), !port->is_input()); - build_object(prefix, object); - return; - } -} - - -void -Builder::build_object(const Raul::Path& prefix, SharedPtr object) -{ - for (GraphObject::Variables::const_iterator i = object->variables().begin(); - i != object->variables().end(); ++i) - _interface.set_variable(prefix.base() + object->path().substr(1), i->first, i->second); - - for (GraphObject::Properties::const_iterator i = object->properties().begin(); - i != object->properties().end(); ++i) { - if (object->path() == "/") - continue; - string path_str = prefix.base() + object->path().substr(1); - _interface.set_property(prefix.base() + object->path().substr(1), i->first, i->second); - } -} - - -} // namespace Shared -} // namespace Ingen diff --git a/src/libs/shared/Builder.hpp b/src/libs/shared/Builder.hpp deleted file mode 100644 index a980bad2..00000000 --- a/src/libs/shared/Builder.hpp +++ /dev/null @@ -1,57 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2008 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef BUILDER_H -#define BUILDER_H - -#include - -namespace Raul { class Path; } - -namespace Ingen { -namespace Shared { - -class GraphObject; -class CommonInterface; - - -/** Wrapper for CommonInterface to create existing objects/models. - * - * \ingroup interface - */ -class Builder -{ -public: - Builder(CommonInterface& interface); - virtual ~Builder() {} - - void build(const Raul::Path& prefix, - SharedPtr object); - -private: - void build_object(const Raul::Path& prefix, - SharedPtr object); - - CommonInterface& _interface; -}; - - -} // namespace Shared -} // namespace Ingen - -#endif // BUILDER_H - diff --git a/src/libs/shared/ClashAvoider.cpp b/src/libs/shared/ClashAvoider.cpp deleted file mode 100644 index d5d1d245..00000000 --- a/src/libs/shared/ClashAvoider.cpp +++ /dev/null @@ -1,214 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2008 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "ClashAvoider.hpp" -#include "Store.hpp" - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Shared { - - -const Raul::Path -ClashAvoider::map_path(const Raul::Path& in) -{ - //cout << "MAP PATH: " << in << endl; - - unsigned offset = 0; - bool has_offset = false; - size_t pos = in.find_last_of("_"); - if (pos != string::npos && pos != (in.length()-1)) { - const std::string trailing = in.substr(in.find_last_of("_")+1); - has_offset = (sscanf(trailing.c_str(), "%u", &offset) > 0); - } - - //cout << "HAS OFFSET: " << offset << endl; - - // Path without _n suffix - Path base_path = in; - if (has_offset) - base_path = base_path.substr(0, base_path.find_last_of("_")); - - //cout << "\tBASE: " << base_path << endl; - - SymbolMap::iterator m = _symbol_map.find(in); - if (m != _symbol_map.end()) { - //cout << " (1) " << m->second << endl; - return m->second; - } else { - typedef std::pair InsertRecord; - - // No clash, use symbol unmodified - if (!exists(in) && _symbol_map.find(in) == _symbol_map.end()) { - InsertRecord i = _symbol_map.insert(make_pair(in, in)); - assert(i.second); - //cout << " (2) " << i.first->second << endl;; - return i.first->second; - } else { - - // See if the parent is mapped - // FIXME: do this the other way around - Path parent = in.parent(); - do { - SymbolMap::iterator p = _symbol_map.find(parent); - if (p != _symbol_map.end()) { - const Path mapped = p->second.base() + in.substr(parent.base().length()); - InsertRecord i = _symbol_map.insert(make_pair(in, mapped)); - //cout << " (3) " << _prefix.base() + i.first->second.substr(1) << endl; - return i.first->second; - } - parent = parent.parent(); - } while (parent != "/"); - - // Append _2 _3 etc until an unused symbol is found - while (true) { - Offsets::iterator o = _offsets.find(base_path); - if (o != _offsets.end()) { - offset = ++o->second; - } else { - string parent_str = _prefix.base() + in.parent().base().substr(1); - parent_str = parent_str.substr(0, parent_str.find_last_of("/")); - if (parent_str == "") - parent_str = "/"; - //cout << "***** PARENT: " << parent_str << endl; - } - - std::stringstream ss; - ss << base_path << "_" << offset; - if (!exists(ss.str())) { - string str = ss.str(); - InsertRecord i = _symbol_map.insert(make_pair(in, str)); - //cout << "HIT: offset = " << offset << ", str = " << str << endl; - offset = _store.child_name_offset(in.parent(), base_path.name(), false); - _offsets.insert(make_pair(base_path, offset)); - //cout << " (4) " << i.first->second << endl;; - return i.first->second; - } else { - //cout << "MISSED OFFSET: " << in << " => " << ss.str() << endl; - if (o != _offsets.end()) - offset = ++o->second; - else - ++offset; - } - } - } - } -} - - -bool -ClashAvoider::exists(const Raul::Path& path) const -{ - bool exists = (_store.find(_prefix.base() + path.substr(1)) != _store.end()); - if (exists) - return true; - - if (_also_avoid) - return (_also_avoid->find(path) != _also_avoid->end()); - else - return false; -} - - -void -ClashAvoider::new_patch(const std::string& path, - uint32_t poly) -{ - _target.new_patch(map_path(path), poly); -} - - -void -ClashAvoider::new_node(const std::string& path, - const std::string& plugin_uri) -{ - _target.new_node(map_path(path), plugin_uri); -} - - -void -ClashAvoider::new_port(const std::string& path, - uint32_t index, - const std::string& data_type, - bool is_output) -{ - _target.new_port(map_path(path), index, data_type, is_output); -} - - -void -ClashAvoider::connect(const std::string& src_port_path, - const std::string& dst_port_path) -{ - _target.connect(map_path(src_port_path), map_path(dst_port_path)); -} - - -void -ClashAvoider::disconnect(const std::string& src_port_path, - const std::string& dst_port_path) -{ - _target.disconnect(map_path(src_port_path), map_path(dst_port_path)); -} - - -void -ClashAvoider::set_variable(const std::string& subject_path, - const std::string& predicate, - const Raul::Atom& value) -{ - _target.set_variable(map_path(subject_path), predicate, value); -} - - -void -ClashAvoider::set_property(const std::string& subject_path, - const std::string& predicate, - const Raul::Atom& value) -{ - _target.set_property(map_path(subject_path), predicate, value); -} - - -void -ClashAvoider::set_port_value(const std::string& port_path, - const Raul::Atom& value) -{ - _target.set_port_value(map_path(port_path), value); -} - - -void -ClashAvoider::set_voice_value(const std::string& port_path, - uint32_t voice, - const Raul::Atom& value) -{ - _target.set_voice_value(map_path(port_path), voice, value); -} - - -void -ClashAvoider::destroy(const std::string& path) -{ - _target.destroy(map_path(path)); -} - - -} // namespace Shared -} // namespace Ingen diff --git a/src/libs/shared/ClashAvoider.hpp b/src/libs/shared/ClashAvoider.hpp deleted file mode 100644 index f7016e4e..00000000 --- a/src/libs/shared/ClashAvoider.hpp +++ /dev/null @@ -1,108 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2008 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef CLASHAVOIDER_H -#define CLASHAVOIDER_H - -#include -#include -#include -#include -#include -#include "interface/CommonInterface.hpp" - -namespace Ingen { -namespace Shared { - -class Store; - - -/** A wrapper for a CommonInterface that creates objects but possibly maps - * symbol names to avoid clashes with the existing objects in a store. - */ -class ClashAvoider : public CommonInterface -{ -public: - ClashAvoider(Store& store, const Raul::Path& prefix, CommonInterface& target, - Store* also_avoid=NULL) - : _prefix(prefix), _store(store), _target(target), _also_avoid(also_avoid) {} - - void set_target(CommonInterface& target) { _target = target; } - - // Bundles - void bundle_begin() { _target.bundle_begin(); } - void bundle_end() { _target.bundle_end(); } - - // Object commands - - void new_patch(const std::string& path, - uint32_t poly); - - void new_node(const std::string& path, - const std::string& plugin_uri); - - void new_port(const std::string& path, - uint32_t index, - const std::string& data_type, - bool is_output); - - void connect(const std::string& src_port_path, - const std::string& dst_port_path); - - void disconnect(const std::string& src_port_path, - const std::string& dst_port_path); - - void set_variable(const std::string& subject_path, - const std::string& predicate, - const Raul::Atom& value); - - void set_property(const std::string& subject_path, - const std::string& predicate, - const Raul::Atom& value); - - void set_port_value(const std::string& port_path, - const Raul::Atom& value); - - void set_voice_value(const std::string& port_path, - uint32_t voice, - const Raul::Atom& value); - - void destroy(const std::string& path); - -private: - const Raul::Path map_path(const Raul::Path& in); - - const Raul::Path& _prefix; - Store& _store; - CommonInterface& _target; - - Store* _also_avoid; - bool exists(const Raul::Path& path) const; - - typedef std::map Offsets; - Offsets _offsets; - - typedef std::map SymbolMap; - SymbolMap _symbol_map; -}; - - -} // namespace Shared -} // namespace Ingen - -#endif // CLASHAVOIDER_H - diff --git a/src/libs/shared/LV2Features.cpp b/src/libs/shared/LV2Features.cpp deleted file mode 100644 index 2e7eb10e..00000000 --- a/src/libs/shared/LV2Features.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2008 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include "LV2Features.hpp" -#include "LV2URIMap.hpp" - -using namespace std; - -namespace Ingen { -namespace Shared { - - -LV2Features::LV2Features() - : _lv2_features((LV2_Feature**)malloc(sizeof(LV2_Feature*))) -{ - _lv2_features[0] = NULL; - - LV2URIMap* controller = new LV2URIMap(); - add_feature(LV2_URI_MAP_URI, controller->feature(), controller); -} - - -const LV2Features::Feature* -LV2Features::feature(const std::string& uri) -{ - Features::const_iterator i = _features.find(uri); - if (i != _features.end()) - return &i->second; - else - return NULL; -} - - -void -LV2Features::add_feature(const std::string& uri, LV2_Feature* feature, void* controller) -{ -#ifndef NDEBUG - Features::const_iterator i = _features.find(uri); - assert(i == _features.end()); - assert(_lv2_features[_features.size()] == NULL); -#endif - _features.insert(make_pair(uri, Feature(feature, controller))); - - _lv2_features = (LV2_Feature**)realloc(_lv2_features, sizeof(LV2_Feature*) * (_features.size() + 1)); - _lv2_features[_features.size()-1] = feature; - _lv2_features[_features.size()] = NULL; -} - - -} // namespace Shared -} // namespace Ingen diff --git a/src/libs/shared/LV2Features.hpp b/src/libs/shared/LV2Features.hpp deleted file mode 100644 index 3a174aa2..00000000 --- a/src/libs/shared/LV2Features.hpp +++ /dev/null @@ -1,63 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2008 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef LV2FEATURES_HPP -#define LV2FEATURES_HPP - -#include CONFIG_H_PATH -#ifndef HAVE_SLV2 -#error "This file requires SLV2, but HAVE_SLV2 is not defined. Please report." -#endif - -#include -#include -#include - -namespace Ingen { -namespace Shared { - - -/** Stuff that may need to be passed to an LV2 plugin (i.e. LV2 features). - */ -class LV2Features { -public: - LV2Features(); - - struct Feature { - Feature(LV2_Feature* f, void* c=NULL) : feature(f), controller(c) {} - LV2_Feature* feature; ///< LV2 feature struct (plugin exposed) - void* controller; ///< Ingen internals, not exposed to plugin - }; - - typedef std::map Features; - - const Feature* feature(const std::string& uri); - - void add_feature(const std::string& uri, LV2_Feature* feature, void* controller); - - LV2_Feature** lv2_features() const { return _lv2_features; } - -private: - Features _features; - LV2_Feature** _lv2_features; -}; - - -} // namespace Shared -} // namespace Ingen - -#endif // LV2FEATURES_HPP diff --git a/src/libs/shared/LV2URIMap.cpp b/src/libs/shared/LV2URIMap.cpp deleted file mode 100644 index c01dfea0..00000000 --- a/src/libs/shared/LV2URIMap.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2008 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#define __STDC_LIMIT_MACROS 1 -#include -#include -#include -#include "LV2URIMap.hpp" - -using namespace std; - -namespace Ingen { -namespace Shared { - - -LV2URIMap::LV2URIMap() - : next_uri_id(1) -{ - uri_map_feature_data.uri_to_id = &LV2URIMap::uri_map_uri_to_id; - uri_map_feature_data.callback_data = this; - uri_map_feature.URI = LV2_URI_MAP_URI; - uri_map_feature.data = &uri_map_feature_data; -} - - -uint32_t -LV2URIMap::uri_to_id(const char* map, - const char* uri) -{ - return uri_map_uri_to_id(this, map, uri); -} - - -uint32_t -LV2URIMap::uri_map_uri_to_id(LV2_URI_Map_Callback_Data callback_data, - const char* map, - const char* uri) -{ - // TODO: map ignored, < UINT16_MAX assumed - - LV2URIMap* me = (LV2URIMap*)callback_data; - uint32_t ret = 0; - - URIMap::iterator i = me->uri_map.find(uri); - if (i != me->uri_map.end()) { - ret = i->second; - } else { - ret = me->next_uri_id++; - me->uri_map.insert(make_pair(string(uri), ret)); - } - - /*cout << "URI MAP (" << (map ? (void*)map : NULL) - << "): " << uri << " -> " << ret << endl;*/ - - assert(ret <= UINT16_MAX); - return ret; -} - - -} // namespace Shared -} // namespace Ingen diff --git a/src/libs/shared/LV2URIMap.hpp b/src/libs/shared/LV2URIMap.hpp deleted file mode 100644 index 35130066..00000000 --- a/src/libs/shared/LV2URIMap.hpp +++ /dev/null @@ -1,64 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2008 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef LV2URIMAP_HPP -#define LV2URIMAP_HPP - -#include CONFIG_H_PATH -#ifndef HAVE_SLV2 -#error "This file requires SLV2, but HAVE_SLV2 is not defined. Please report." -#endif - -#include -#include -#include -#include -#include "common/lv2ext/lv2_uri_map.h" - -namespace Ingen { -namespace Shared { - - -/** Implementation of the LV2 URI Map extension - */ -class LV2URIMap : public boost::noncopyable { -public: - LV2URIMap(); - - LV2_Feature* feature() { return &uri_map_feature; } - - uint32_t uri_to_id(const char* map, - const char* uri); - -private: - typedef std::map URIMap; - - static uint32_t uri_map_uri_to_id(LV2_URI_Map_Callback_Data callback_data, - const char* map, - const char* uri); - - LV2_Feature uri_map_feature; - LV2_URI_Map_Feature uri_map_feature_data; - URIMap uri_map; - uint32_t next_uri_id; -}; - - -} // namespace Shared -} // namespace Ingen - -#endif // LV2URIMAP_HPP diff --git a/src/libs/shared/Makefile.am b/src/libs/shared/Makefile.am deleted file mode 100644 index c26c98a2..00000000 --- a/src/libs/shared/Makefile.am +++ /dev/null @@ -1,22 +0,0 @@ -noinst_LTLIBRARIES = libingen_shared.la - -libingen_shared_la_CXXFLAGS = \ - @GLIBMM_CFLAGS@ \ - @INGEN_CFLAGS@ \ - @RAUL_CFLAGS@ \ - @REDLANDMM_CFLAGS@ \ - @SLV2_CFLAGS@ - -libingen_shared_la_SOURCES = \ - Builder.cpp \ - Builder.hpp \ - ClashAvoider.cpp \ - ClashAvoider.hpp \ - LV2Features.cpp \ - LV2Features.hpp \ - LV2URIMap.cpp \ - LV2URIMap.hpp \ - OSCSender.cpp \ - OSCSender.hpp \ - Store.cpp \ - Store.hpp diff --git a/src/libs/shared/OSCSender.cpp b/src/libs/shared/OSCSender.cpp deleted file mode 100644 index f366b1a4..00000000 --- a/src/libs/shared/OSCSender.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2008 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "OSCSender.hpp" -#include -#include -#include - -using namespace std; - -namespace Ingen { -namespace Shared { - - -OSCSender::OSCSender() - : _transfer(NULL) - , _address(NULL) - , _enabled(true) -{ -} - - -void -OSCSender::bundle_begin() -{ - assert(!_transfer); - lo_timetag t; - lo_timetag_now(&t); - _transfer = lo_bundle_new(t); - _send_state = SendingBundle; -} - - -void -OSCSender::bundle_end() -{ - transfer_end(); -} - - -void -OSCSender::transfer_begin() -{ - assert(!_transfer); - lo_timetag t; - lo_timetag_now(&t); - _transfer = lo_bundle_new(t); - _send_state = SendingTransfer; -} - - -void -OSCSender::transfer_end() -{ - assert(_transfer); - lo_send_bundle(_address, _transfer); - lo_bundle_free(_transfer); - _transfer = NULL; - _send_state = Immediate; -} - - -int -OSCSender::send(const char *path, const char *types, ...) -{ - if (!_enabled) - return 0; - - va_list args; - va_start(args, types); - - lo_message msg = lo_message_new(); - int ret = lo_message_add_varargs(msg, types, args); - - if (!ret) - send_message(path, msg); - - va_end(args); - - return ret; -} - - -void -OSCSender::send_message(const char* path, lo_message msg) -{ - // FIXME: size? liblo doesn't export this. - // Don't want to exceed max UDP packet size (good default value?) - static const size_t MAX_BUNDLE_SIZE = 1024; - - if (!_enabled) - return; - - if (_transfer) { - if (lo_bundle_length(_transfer) + lo_message_length(msg, path) > MAX_BUNDLE_SIZE) { - if (_send_state == SendingBundle) - cerr << "WARNING: Maximum bundle size reached, bundle split" << endl; - lo_send_bundle(_address, _transfer); - lo_timetag t; - lo_timetag_now(&t); - _transfer = lo_bundle_new(t); - } - lo_bundle_add_message(_transfer, path, msg); - - } else { - lo_send_message(_address, path, msg); - } -} - -} // namespace Shared -} // namespace Ingen diff --git a/src/libs/shared/OSCSender.hpp b/src/libs/shared/OSCSender.hpp deleted file mode 100644 index da91caed..00000000 --- a/src/libs/shared/OSCSender.hpp +++ /dev/null @@ -1,59 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef OSCSENDER_H -#define OSCSENDER_H - -#include -#include - -namespace Ingen { -namespace Shared { - -class OSCSender { -public: - OSCSender(); - virtual ~OSCSender() {} - - lo_address address() const { return _address; } - - // Message bundling - void bundle_begin(); - void bundle_end(); - - // Transfers (loose bundling) - void transfer_begin(); - void transfer_end(); - -protected: - int send(const char *path, const char *types, ...); - void send_message(const char* path, lo_message m); - - enum SendState { Immediate, SendingBundle, SendingTransfer }; - - SendState _send_state; - lo_bundle _transfer; - lo_address _address; - bool _enabled; -}; - - -} // namespace Shared -} // namespace Ingen - -#endif // OSCSENDER_H - diff --git a/src/libs/shared/Store.cpp b/src/libs/shared/Store.cpp deleted file mode 100644 index 9f0f3624..00000000 --- a/src/libs/shared/Store.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2008 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include "common/interface/Node.hpp" -#include "common/interface/Port.hpp" -#include "Store.hpp" - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Shared { - - -void -Store::add(GraphObject* o) -{ - if (find(o->path()) != end()) { - cerr << "[Store] ERROR: Attempt to add duplicate object " << o->path() << endl; - return; - } - - insert(make_pair(o->path(), o)); - - Node* node = dynamic_cast(o); - if (node) { - for (uint32_t i=0; i < node->num_ports(); ++i) { - add(node->port(i)); - } - } -} - - -Store::const_iterator -Store::children_begin(SharedPtr o) const -{ - const_iterator parent = find(o->path()); - assert(parent != end()); - ++parent; - return parent; -} - - -Store::const_iterator -Store::children_end(SharedPtr o) const -{ - const_iterator parent = find(o->path()); - assert(parent != end()); - return find_descendants_end(parent); -} - - -SharedPtr -Store::find_child(SharedPtr parent, const string& child_name) const -{ - const_iterator pi = find(parent->path()); - assert(pi != end()); - const_iterator children_end = find_descendants_end(pi); - const_iterator child = find(pi, children_end, parent->path().base() + child_name); - if (child != end()) - return child->second; - else - return SharedPtr(); -} - - -unsigned -Store::child_name_offset(const Raul::Path& parent, - const Raul::Symbol& symbol, - bool allow_zero) -{ - unsigned offset = 0; - - while (true) { - std::stringstream ss; - ss << symbol; - if (offset > 0) - ss << "_" << offset; - if (find(parent.base() + ss.str()) == end() && (allow_zero || offset > 0)) - break; - else if (offset == 0) - offset = 2; - else - ++offset; - } - - return offset; -} - - -} // namespace Shared -} // namespace Ingen diff --git a/src/libs/shared/Store.hpp b/src/libs/shared/Store.hpp deleted file mode 100644 index 27754345..00000000 --- a/src/libs/shared/Store.hpp +++ /dev/null @@ -1,60 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2008 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef COMMON_STORE_H -#define COMMON_STORE_H - -#include -#include -#include -#include "interface/GraphObject.hpp" - -using Raul::PathTable; - -namespace Ingen { -namespace Shared { - - -class Store : public Raul::PathTable< SharedPtr > { -public: - virtual ~Store() {} - - virtual void add(Shared::GraphObject* o); - - typedef Raul::Table< Raul::Path, SharedPtr > Objects; - - const_iterator children_begin(SharedPtr o) const; - const_iterator children_end(SharedPtr o) const; - - SharedPtr find_child(SharedPtr parent, - const std::string& child_name) const; - - unsigned child_name_offset(const Raul::Path& parent, - const Raul::Symbol& symbol, - bool allow_zero=true); - - Glib::RWLock& lock() { return _lock; } - -private: - Glib::RWLock _lock; -}; - - -} // namespace Shared -} // namespace Ingen - -#endif // COMMON_STORE_H diff --git a/src/libs/shared/wscript b/src/libs/shared/wscript deleted file mode 100644 index 1974a827..00000000 --- a/src/libs/shared/wscript +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env python -import Params - -def build(bld): - obj = bld.create_obj('cpp', 'shlib') - obj.source = ''' - Builder.cpp - ClashAvoider.cpp - LV2Features.cpp - LV2URIMap.cpp - OSCSender.cpp - Store.cpp - ''' - obj.includes = ['..', '../../common', '../..'] - obj.name = 'libingen_shared' - obj.target = 'ingen_shared' - obj.uselib = 'GLIBMM SLV2 RAUL REDLANDMM' - obj.vnum = '0.0.0' - diff --git a/src/module/Makefile.am b/src/module/Makefile.am new file mode 100644 index 00000000..84d499e4 --- /dev/null +++ b/src/module/Makefile.am @@ -0,0 +1,22 @@ +noinst_LTLIBRARIES = libingen_module.la + +libingen_module_la_CXXFLAGS = \ + @INGEN_CFLAGS@ \ + @GLIBMM_CFLAGS@ \ + @REDLANDMM_CFLAGS@ \ + @RAUL_CFLAGS@ \ + @SLV2_CFLAGS@ \ + -DINGEN_MODULE_DIR=\"$(libdir)/ingen\" + +libingen_module_la_LIBADD = \ + @GLIBMM_LIBS@ \ + @REDLANDMM_LIBS@ \ + @RAUL_LIBS@ \ + @SLV2_LIBS@ + +libingen_module_la_SOURCES = \ + global.hpp \ + global.cpp \ + World.hpp \ + Module.hpp \ + Module.cpp diff --git a/src/module/Module.cpp b/src/module/Module.cpp new file mode 100644 index 00000000..8d1399b1 --- /dev/null +++ b/src/module/Module.cpp @@ -0,0 +1,90 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifndef INGEN_MODULE_DIR +#error This file expects INGEN_MODULE_DIR to be defined. +#endif + +using namespace std; + +namespace Ingen { +namespace Shared { + + +/** Load a dynamic module from the default path. + * + * This will check in the directories specified in the environment variable + * INGEN_MODULE_PATH (typical colon delimited format), then the default module + * installation directory (ie /usr/local/lib/ingen), in that order. + * + * \param name The base name of the module, e.g. "ingen_serialisation" + */ +SharedPtr +load_module(const string& name) +{ + Glib::Module* module = NULL; + + // Search INGEN_MODULE_PATH first + bool module_path_found; + string module_path = Glib::getenv("INGEN_MODULE_PATH", module_path_found); + if (module_path_found) { + string dir; + istringstream iss(module_path); + while (getline(iss, dir, ':')) { + + string filename = Glib::Module::build_path(dir, name); + if (Glib::file_test(filename, Glib::FILE_TEST_EXISTS)) { + module = new Glib::Module(filename, Glib::MODULE_BIND_LAZY); + + if (*module) { + cerr << "Loaded module \"" << name << "\" from " << filename << endl; + return SharedPtr(module); + } else { + delete module; + cerr << Glib::Module::get_last_error() << endl; + } + } + } + } + + // Try default directory if not found + module = new Glib::Module( + Glib::Module::build_path(INGEN_MODULE_DIR, name), + Glib::MODULE_BIND_LAZY); + + if (*module) { + cerr << "Loaded module \"" << name << "\" from " << INGEN_MODULE_DIR << endl; + return SharedPtr(module); + } else { + cerr << "Unable to load module \"" << name << "\", is Ingen installed?" << endl + << "Use ./ingen.dev to run from the source tree." << endl; + return SharedPtr(); + } +} + + +} // namespace Shared +} // namespace Ingen + diff --git a/src/module/Module.hpp b/src/module/Module.hpp new file mode 100644 index 00000000..2d9ba382 --- /dev/null +++ b/src/module/Module.hpp @@ -0,0 +1,35 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include + +namespace Ingen { +namespace Shared { + + +/** Load a dynamic module from the default path. + * + * \param name The base name of the module, e.g. "ingen_serialisation" + */ +SharedPtr load_module(const std::string& name); + + +} // namespace Shared +} // namespace Ingen + diff --git a/src/module/World.hpp b/src/module/World.hpp new file mode 100644 index 00000000..dbbac2bd --- /dev/null +++ b/src/module/World.hpp @@ -0,0 +1,78 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef INGEN_WORLD_HPP +#define INGEN_WORLD_HPP + +#include CONFIG_H_PATH + +#include +#include +#include +#include "shared/LV2Features.hpp" + +#ifdef HAVE_SLV2 +#include +#endif + +namespace Redland { class World; } + +namespace Ingen { +class Engine; + +namespace Serialisation { class Serialiser; class Parser; } +using Serialisation::Serialiser; +using Serialisation::Parser; + +namespace Shared { +class EngineInterface; +class Store; + + +/** The "world" all Ingen modules may share. + * + * This is required for shared access to things like Redland, so locking can + * take place centrally and the engine/gui using the same library won't + * explode horribly. + * + * Hopefully at some point in the future it can allow some fun things like + * scripting bindings that play with all loaded components of + * The Ingen System(TM) and whatnot. + */ +struct World { +#ifdef HAVE_SLV2 + SLV2World slv2_world; + LV2Features* lv2_features; +#endif + + Redland::World* rdf_world; + + SharedPtr engine; + SharedPtr local_engine; + SharedPtr serialiser; + SharedPtr parser; + SharedPtr store; + + SharedPtr serialisation_module; +}; + + +} // namespace Shared +} // namespace Ingen + +#endif // INGEN_WORLD_HPP + diff --git a/src/module/global.cpp b/src/module/global.cpp new file mode 100644 index 00000000..c78f9fc3 --- /dev/null +++ b/src/module/global.cpp @@ -0,0 +1,74 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "global.hpp" +#include "World.hpp" + +#include CONFIG_H_PATH +#ifdef HAVE_SLV2 +#include +#endif + +using namespace std; + +namespace Ingen { +namespace Shared { + +static World* world = NULL; + + +World* +get_world() +{ + static World* world = NULL; + + if (!world) { + world = new World(); + world->rdf_world = new Redland::World(); +#ifdef HAVE_SLV2 + world->slv2_world = slv2_world_new_using_rdf_world(world->rdf_world->world()); + world->lv2_features = new LV2Features(); + slv2_world_load_all(world->slv2_world); +#endif + world->engine.reset(); + world->local_engine.reset(); + } + + return world; +} + + +void +destroy_world() +{ + if (world) { +#ifdef HAVE_SLV2 + slv2_world_free(world->slv2_world); + delete world->lv2_features; +#endif + delete world->rdf_world; + delete world; + world = NULL; + } +} + + +} // namesace Shared +} // namespace Ingen + diff --git a/src/module/global.hpp b/src/module/global.hpp new file mode 100644 index 00000000..1aa46be6 --- /dev/null +++ b/src/module/global.hpp @@ -0,0 +1,33 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef INGEN_GLOBAL_H +#define INGEN_GLOBAL_H + +namespace Ingen { +namespace Shared { + +class World; + +World* get_world(); +void destroy_world(); + +} // namesace Shared +} // namespace Ingen + +#endif // INGEN_GLOBAL_H + diff --git a/src/module/wscript b/src/module/wscript new file mode 100644 index 00000000..e202342d --- /dev/null +++ b/src/module/wscript @@ -0,0 +1,17 @@ +#!/usr/bin/env python +import Params + +def build(bld): + dir = Params.g_options.moduledir or bld.env()['PREFIX'] + 'lib/ingen' + obj = bld.create_obj('cpp', 'shlib') + obj.source = ''' + Module.cpp + global.cpp + ''' + obj.includes = ['..', '../../common', '../..'] + obj.defines = 'INGEN_MODULE_DIR=\\\"' + dir + '\\\"' + obj.name = 'libingen_module' + obj.target = 'ingen_module' + obj.uselib = 'GLIBMM SLV2 RAUL REDLANDMM' + obj.vnum = '0.0.0' + diff --git a/src/progs/ingen/Makefile.am b/src/progs/ingen/Makefile.am deleted file mode 100644 index c48a47a2..00000000 --- a/src/progs/ingen/Makefile.am +++ /dev/null @@ -1,30 +0,0 @@ -AM_CXXFLAGS = \ - -DINGEN_MODULE_DIR=\"$(libdir)/ingen\" \ - @GTHREAD_CFLAGS@ \ - @GLIBMM_CFLAGS@ \ - @REDLANDMM_CFLAGS@ \ - @RAUL_CFLAGS@ \ - @SLV2_CFLAGS@ \ - @INGEN_CFLAGS@ - -MAINTAINERCLEANFILES = Makefile.in - -desktopfilesdir = $(datadir)/applications -dist_desktopfiles_DATA = ingen.desktop - -bin_PROGRAMS = ingen -ingen_LDADD = \ - ../../libs/module/libingen_module.la \ - ../../libs/shared/libingen_shared.la \ - @SLV2_LIBS@ \ - @REDLANDMM_LIBS@ \ - @RAUL_LIBS@ \ - @GTHREAD_LIBS@ \ - @GLIBMM_LIBS@ - -ingen_DEPENDENCIES = ../../libs/module/libingen_module.la - -ingen_SOURCES = \ - main.cpp \ - cmdline.h \ - cmdline.c diff --git a/src/progs/ingen/cmdline.c b/src/progs/ingen/cmdline.c deleted file mode 100644 index ea48135c..00000000 --- a/src/progs/ingen/cmdline.c +++ /dev/null @@ -1,665 +0,0 @@ -/* - File autogenerated by gengetopt - generated with the following command: - gengetopt -g - - 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. -*/ - -/* If we use autoconf. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include - -#include "getopt.h" - -#include "cmdline.h" - -const char *gengetopt_args_info_purpose = "A modular realtime audio processing system"; - -const char *gengetopt_args_info_usage = "Usage: ingen [OPTIONS]..."; - -const char *gengetopt_args_info_description = "Ingen can be run in various configurations. The engine can\nrun as a stand-alone server controlled by OSC, or internal to\nanother process (e.g. the GUI). The GUI can communicate with the engine\nvia either method, and many GUIs (or other things) may connect to an\nengine via OSC.\n\nExamples:\n\n ingen -e - Run an engine, listen for OSC \n ingen -g - Run a GUI, connect via OSC \n ingen -eg - Run an engine and a GUI in one process\n\nThe -l (load) option can be used in all cases:\n \n ingen -el patch.ingen.ttl - Run an engine and load a patch\n ingen -gl patch.ingen.ttl - Run a GUI and load a patch\n ingen -egl patch.ingen.ttl - Run an engine and a GUI and load a patch\n\nOptions:\n"; - -const char *gengetopt_args_info_help[] = { - " -h, --help Print help and exit", - " -V, --version Print version and exit", - " -e, --engine Run (JACK) engine (default=off)", - " -E, --engine-port=INT Engine OSC port (default=`16180')", - " -c, --connect=STRING Connect to existing engine at OSC URI \n (default=`osc.udp://localhost:16180')", - " -g, --gui Launch the GTK graphical interface (default=off)", - " -C, --client-port=INT Client OSC port", - " -l, --load=STRING Load patch", - " -L, --path=STRING Target path for loaded patch", - " -r, --run=STRING Run script", - " -p, --parallelism=INT Number of concurrent process threads (default=`1')", - 0 -}; - -typedef enum {ARG_NO - , ARG_FLAG - , ARG_STRING - , ARG_INT -} cmdline_parser_arg_type; - -static -void clear_given (struct gengetopt_args_info *args_info); -static -void clear_args (struct gengetopt_args_info *args_info); - -static int -cmdline_parser_internal (int argc, char * const *argv, struct gengetopt_args_info *args_info, - struct cmdline_parser_params *params, const char *additional_error); - - -static char * -gengetopt_strdup (const char *s); - -static -void clear_given (struct gengetopt_args_info *args_info) -{ - args_info->help_given = 0 ; - args_info->version_given = 0 ; - args_info->engine_given = 0 ; - args_info->engine_port_given = 0 ; - args_info->connect_given = 0 ; - args_info->gui_given = 0 ; - args_info->client_port_given = 0 ; - args_info->load_given = 0 ; - args_info->path_given = 0 ; - args_info->run_given = 0 ; - args_info->parallelism_given = 0 ; -} - -static -void clear_args (struct gengetopt_args_info *args_info) -{ - args_info->engine_flag = 0; - args_info->engine_port_arg = 16180; - args_info->engine_port_orig = NULL; - args_info->connect_arg = gengetopt_strdup ("osc.udp://localhost:16180"); - args_info->connect_orig = NULL; - args_info->gui_flag = 0; - args_info->client_port_orig = NULL; - args_info->load_arg = NULL; - args_info->load_orig = NULL; - args_info->path_arg = NULL; - args_info->path_orig = NULL; - args_info->run_arg = NULL; - args_info->run_orig = NULL; - args_info->parallelism_arg = 1; - args_info->parallelism_orig = NULL; - -} - -static -void init_args_info(struct gengetopt_args_info *args_info) -{ - - - args_info->help_help = gengetopt_args_info_help[0] ; - args_info->version_help = gengetopt_args_info_help[1] ; - args_info->engine_help = gengetopt_args_info_help[2] ; - args_info->engine_port_help = gengetopt_args_info_help[3] ; - args_info->connect_help = gengetopt_args_info_help[4] ; - args_info->gui_help = gengetopt_args_info_help[5] ; - args_info->client_port_help = gengetopt_args_info_help[6] ; - args_info->load_help = gengetopt_args_info_help[7] ; - args_info->path_help = gengetopt_args_info_help[8] ; - args_info->run_help = gengetopt_args_info_help[9] ; - args_info->parallelism_help = gengetopt_args_info_help[10] ; - -} - -void -cmdline_parser_print_version (void) -{ - printf ("%s %s\n", CMDLINE_PARSER_PACKAGE, CMDLINE_PARSER_VERSION); -} - -static void print_help_common(void) { - cmdline_parser_print_version (); - - if (strlen(gengetopt_args_info_purpose) > 0) - printf("\n%s\n", gengetopt_args_info_purpose); - - if (strlen(gengetopt_args_info_usage) > 0) - printf("\n%s\n", gengetopt_args_info_usage); - - printf("\n"); - - if (strlen(gengetopt_args_info_description) > 0) - printf("%s\n\n", gengetopt_args_info_description); -} - -void -cmdline_parser_print_help (void) -{ - int i = 0; - print_help_common(); - while (gengetopt_args_info_help[i]) - printf("%s\n", gengetopt_args_info_help[i++]); -} - -void -cmdline_parser_init (struct gengetopt_args_info *args_info) -{ - clear_given (args_info); - clear_args (args_info); - init_args_info (args_info); -} - -void -cmdline_parser_params_init(struct cmdline_parser_params *params) -{ - if (params) - { - params->override = 0; - params->initialize = 1; - params->check_required = 1; - params->check_ambiguity = 0; - params->print_errors = 1; - } -} - -struct cmdline_parser_params * -cmdline_parser_params_create(void) -{ - struct cmdline_parser_params *params = - (struct cmdline_parser_params *)malloc(sizeof(struct cmdline_parser_params)); - cmdline_parser_params_init(params); - return params; -} - -static void -free_string_field (char **s) -{ - if (*s) - { - free (*s); - *s = 0; - } -} - - -static void -cmdline_parser_release (struct gengetopt_args_info *args_info) -{ - - free_string_field (&(args_info->engine_port_orig)); - free_string_field (&(args_info->connect_arg)); - free_string_field (&(args_info->connect_orig)); - free_string_field (&(args_info->client_port_orig)); - free_string_field (&(args_info->load_arg)); - free_string_field (&(args_info->load_orig)); - free_string_field (&(args_info->path_arg)); - free_string_field (&(args_info->path_orig)); - free_string_field (&(args_info->run_arg)); - free_string_field (&(args_info->run_orig)); - free_string_field (&(args_info->parallelism_orig)); - - - - clear_given (args_info); -} - - -static void -write_into_file(FILE *outfile, const char *opt, const char *arg, char *values[]) -{ - if (arg) { - fprintf(outfile, "%s=\"%s\"\n", opt, arg); - } else { - fprintf(outfile, "%s\n", opt); - } -} - - -int -cmdline_parser_dump(FILE *outfile, struct gengetopt_args_info *args_info) -{ - int i = 0; - - if (!outfile) - { - fprintf (stderr, "%s: cannot dump options to stream\n", CMDLINE_PARSER_PACKAGE); - return EXIT_FAILURE; - } - - if (args_info->help_given) - write_into_file(outfile, "help", 0, 0 ); - if (args_info->version_given) - write_into_file(outfile, "version", 0, 0 ); - if (args_info->engine_given) - write_into_file(outfile, "engine", 0, 0 ); - if (args_info->engine_port_given) - write_into_file(outfile, "engine-port", args_info->engine_port_orig, 0); - if (args_info->connect_given) - write_into_file(outfile, "connect", args_info->connect_orig, 0); - if (args_info->gui_given) - write_into_file(outfile, "gui", 0, 0 ); - if (args_info->client_port_given) - write_into_file(outfile, "client-port", args_info->client_port_orig, 0); - if (args_info->load_given) - write_into_file(outfile, "load", args_info->load_orig, 0); - if (args_info->path_given) - write_into_file(outfile, "path", args_info->path_orig, 0); - if (args_info->run_given) - write_into_file(outfile, "run", args_info->run_orig, 0); - if (args_info->parallelism_given) - write_into_file(outfile, "parallelism", args_info->parallelism_orig, 0); - - - i = EXIT_SUCCESS; - return i; -} - -int -cmdline_parser_file_save(const char *filename, struct gengetopt_args_info *args_info) -{ - FILE *outfile; - int i = 0; - - outfile = fopen(filename, "w"); - - if (!outfile) - { - fprintf (stderr, "%s: cannot open file for writing: %s\n", CMDLINE_PARSER_PACKAGE, filename); - return EXIT_FAILURE; - } - - i = cmdline_parser_dump(outfile, args_info); - fclose (outfile); - - return i; -} - -void -cmdline_parser_free (struct gengetopt_args_info *args_info) -{ - cmdline_parser_release (args_info); -} - -/** @brief replacement of strdup, which is not standard */ -char * -gengetopt_strdup (const char *s) -{ - char *result = NULL; - if (!s) - return result; - - 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) -{ - return cmdline_parser2 (argc, argv, args_info, 0, 1, 1); -} - -int -cmdline_parser_ext (int argc, char * const *argv, struct gengetopt_args_info *args_info, - struct cmdline_parser_params *params) -{ - int result; - result = cmdline_parser_internal (argc, argv, args_info, params, NULL); - - if (result == EXIT_FAILURE) - { - cmdline_parser_free (args_info); - exit (EXIT_FAILURE); - } - - return result; -} - -int -cmdline_parser2 (int argc, char * const *argv, struct gengetopt_args_info *args_info, int override, int initialize, int check_required) -{ - int result; - struct cmdline_parser_params params; - - params.override = override; - params.initialize = initialize; - params.check_required = check_required; - params.check_ambiguity = 0; - params.print_errors = 1; - - result = cmdline_parser_internal (argc, argv, args_info, ¶ms, NULL); - - if (result == EXIT_FAILURE) - { - cmdline_parser_free (args_info); - exit (EXIT_FAILURE); - } - - return result; -} - -int -cmdline_parser_required (struct gengetopt_args_info *args_info, const char *prog_name) -{ - return EXIT_SUCCESS; -} - - -static char *package_name = 0; - -/** - * @brief updates an option - * @param field the generic pointer to the field to update - * @param orig_field the pointer to the orig field - * @param field_given the pointer to the number of occurrence of this option - * @param prev_given the pointer to the number of occurrence already seen - * @param value the argument for this option (if null no arg was specified) - * @param possible_values the possible values for this option (if specified) - * @param default_value the default value (in case the option only accepts fixed values) - * @param arg_type the type of this option - * @param check_ambiguity @see cmdline_parser_params.check_ambiguity - * @param override @see cmdline_parser_params.override - * @param no_free whether to free a possible previous value - * @param multiple_option whether this is a multiple option - * @param long_opt the corresponding long option - * @param short_opt the corresponding short option (or '-' if none) - * @param additional_error possible further error specification - */ -static -int update_arg(void *field, char **orig_field, - unsigned int *field_given, unsigned int *prev_given, - char *value, char *possible_values[], const char *default_value, - cmdline_parser_arg_type arg_type, - int check_ambiguity, int override, - int no_free, int multiple_option, - const char *long_opt, char short_opt, - const char *additional_error) -{ - char *stop_char = 0; - const char *val = value; - int found; - char **string_field; - - stop_char = 0; - found = 0; - - if (!multiple_option && prev_given && (*prev_given || (check_ambiguity && *field_given))) - { - if (short_opt != '-') - fprintf (stderr, "%s: `--%s' (`-%c') option given more than once%s\n", - package_name, long_opt, short_opt, - (additional_error ? additional_error : "")); - else - fprintf (stderr, "%s: `--%s' option given more than once%s\n", - package_name, long_opt, - (additional_error ? additional_error : "")); - return 1; /* failure */ - } - - - if (field_given && *field_given && ! override) - return 0; - if (prev_given) - (*prev_given)++; - if (field_given) - (*field_given)++; - if (possible_values) - val = possible_values[found]; - - switch(arg_type) { - case ARG_FLAG: - *((int *)field) = !*((int *)field); - break; - case ARG_INT: - if (val) *((int *)field) = strtol (val, &stop_char, 0); - break; - case ARG_STRING: - if (val) { - string_field = (char **)field; - if (!no_free && *string_field) - free (*string_field); /* free previous string */ - *string_field = gengetopt_strdup (val); - } - break; - default: - break; - }; - - /* check numeric conversion */ - switch(arg_type) { - case ARG_INT: - if (val && !(stop_char && *stop_char == '\0')) { - fprintf(stderr, "%s: invalid numeric value: %s\n", package_name, val); - return 1; /* failure */ - } - break; - default: - ; - }; - - /* store the original value */ - switch(arg_type) { - case ARG_NO: - case ARG_FLAG: - break; - default: - if (value && orig_field) { - if (no_free) { - *orig_field = value; - } else { - if (*orig_field) - free (*orig_field); /* free previous string */ - *orig_field = gengetopt_strdup (value); - } - } - }; - - return 0; /* OK */ -} - - -int -cmdline_parser_internal (int argc, char * const *argv, struct gengetopt_args_info *args_info, - struct cmdline_parser_params *params, const char *additional_error) -{ - int c; /* Character of the parsed option. */ - - int error = 0; - struct gengetopt_args_info local_args_info; - - int override; - int initialize; - int check_required; - int check_ambiguity; - - package_name = argv[0]; - - override = params->override; - initialize = params->initialize; - check_required = params->check_required; - check_ambiguity = params->check_ambiguity; - - if (initialize) - cmdline_parser_init (args_info); - - cmdline_parser_init (&local_args_info); - - optarg = 0; - optind = 0; - opterr = params->print_errors; - optopt = '?'; - - while (1) - { - int option_index = 0; - - static struct option long_options[] = { - { "help", 0, NULL, 'h' }, - { "version", 0, NULL, 'V' }, - { "engine", 0, NULL, 'e' }, - { "engine-port", 1, NULL, 'E' }, - { "connect", 1, NULL, 'c' }, - { "gui", 0, NULL, 'g' }, - { "client-port", 1, NULL, 'C' }, - { "load", 1, NULL, 'l' }, - { "path", 1, NULL, 'L' }, - { "run", 1, NULL, 'r' }, - { "parallelism", 1, NULL, 'p' }, - { NULL, 0, NULL, 0 } - }; - - c = getopt_long (argc, argv, "hVeE:c:gC:l:L:r:p:", long_options, &option_index); - - if (c == -1) break; /* Exit from `while (1)' loop. */ - - switch (c) - { - case 'h': /* Print help and exit. */ - cmdline_parser_print_help (); - cmdline_parser_free (&local_args_info); - exit (EXIT_SUCCESS); - - case 'V': /* Print version and exit. */ - cmdline_parser_print_version (); - cmdline_parser_free (&local_args_info); - exit (EXIT_SUCCESS); - - case 'e': /* Run (JACK) engine. */ - - - if (update_arg((void *)&(args_info->engine_flag), 0, &(args_info->engine_given), - &(local_args_info.engine_given), optarg, 0, 0, ARG_FLAG, - check_ambiguity, override, 1, 0, "engine", 'e', - additional_error)) - goto failure; - - break; - case 'E': /* Engine OSC port. */ - - - if (update_arg( (void *)&(args_info->engine_port_arg), - &(args_info->engine_port_orig), &(args_info->engine_port_given), - &(local_args_info.engine_port_given), optarg, 0, "16180", ARG_INT, - check_ambiguity, override, 0, 0, - "engine-port", 'E', - additional_error)) - goto failure; - - break; - case 'c': /* Connect to existing engine at OSC URI. */ - - - if (update_arg( (void *)&(args_info->connect_arg), - &(args_info->connect_orig), &(args_info->connect_given), - &(local_args_info.connect_given), optarg, 0, "osc.udp://localhost:16180", ARG_STRING, - check_ambiguity, override, 0, 0, - "connect", 'c', - additional_error)) - goto failure; - - break; - case 'g': /* Launch the GTK graphical interface. */ - - - if (update_arg((void *)&(args_info->gui_flag), 0, &(args_info->gui_given), - &(local_args_info.gui_given), optarg, 0, 0, ARG_FLAG, - check_ambiguity, override, 1, 0, "gui", 'g', - additional_error)) - goto failure; - - break; - case 'C': /* Client OSC port. */ - - - if (update_arg( (void *)&(args_info->client_port_arg), - &(args_info->client_port_orig), &(args_info->client_port_given), - &(local_args_info.client_port_given), optarg, 0, 0, ARG_INT, - check_ambiguity, override, 0, 0, - "client-port", 'C', - additional_error)) - goto failure; - - break; - case 'l': /* Load patch. */ - - - if (update_arg( (void *)&(args_info->load_arg), - &(args_info->load_orig), &(args_info->load_given), - &(local_args_info.load_given), optarg, 0, 0, ARG_STRING, - check_ambiguity, override, 0, 0, - "load", 'l', - additional_error)) - goto failure; - - break; - case 'L': /* Target path for loaded patch. */ - - - if (update_arg( (void *)&(args_info->path_arg), - &(args_info->path_orig), &(args_info->path_given), - &(local_args_info.path_given), optarg, 0, 0, ARG_STRING, - check_ambiguity, override, 0, 0, - "path", 'L', - additional_error)) - goto failure; - - break; - case 'r': /* Run script. */ - - - if (update_arg( (void *)&(args_info->run_arg), - &(args_info->run_orig), &(args_info->run_given), - &(local_args_info.run_given), optarg, 0, 0, ARG_STRING, - check_ambiguity, override, 0, 0, - "run", 'r', - additional_error)) - goto failure; - - break; - case 'p': /* Number of concurrent process threads. */ - - - if (update_arg( (void *)&(args_info->parallelism_arg), - &(args_info->parallelism_orig), &(args_info->parallelism_given), - &(local_args_info.parallelism_given), optarg, 0, "1", ARG_INT, - check_ambiguity, override, 0, 0, - "parallelism", 'p', - additional_error)) - goto failure; - - break; - - case 0: /* Long option with no short option */ - case '?': /* Invalid option. */ - /* `getopt_long' already printed an error message. */ - goto failure; - - default: /* bug: option not considered. */ - fprintf (stderr, "%s: option unknown: %c%s\n", CMDLINE_PARSER_PACKAGE, c, (additional_error ? additional_error : "")); - abort (); - } /* switch */ - } /* while */ - - - - - cmdline_parser_release (&local_args_info); - - if ( error ) - return (EXIT_FAILURE); - - return 0; - -failure: - - cmdline_parser_release (&local_args_info); - return (EXIT_FAILURE); -} diff --git a/src/progs/ingen/cmdline.ggo b/src/progs/ingen/cmdline.ggo deleted file mode 100644 index 994e6ff9..00000000 --- a/src/progs/ingen/cmdline.ggo +++ /dev/null @@ -1,36 +0,0 @@ -# Process this file with gengetopt to generate the necessary code (in cmdline.h, cmdline.c) - -package "ingen" -purpose "A modular realtime audio processing system" - -description "Ingen can be run in various configurations. The engine can -run as a stand-alone server controlled by OSC, or internal to -another process (e.g. the GUI). The GUI can communicate with the engine -via either method, and many GUIs (or other things) may connect to an -engine via OSC. - -Examples: - - ingen -e - Run an engine, listen for OSC - ingen -g - Run a GUI, connect via OSC - ingen -eg - Run an engine and a GUI in one process - -The -l (load) option can be used in all cases: - - ingen -el patch.ingen.ttl - Run an engine and load a patch - ingen -gl patch.ingen.ttl - Run a GUI and load a patch - ingen -egl patch.ingen.ttl - Run an engine and a GUI and load a patch - -Options: -" - -option "engine" e "Run (JACK) engine" flag off -option "engine-port" E "Engine OSC port" int no default="16180" -option "connect" c "Connect to existing engine at OSC URI" string no default="osc.udp://localhost:16180" -option "gui" g "Launch the GTK graphical interface" flag off -option "client-port" C "Client OSC port" int no -option "load" l "Load patch" string no -option "path" L "Target path for loaded patch" string no -option "run" r "Run script" string no -option "parallelism" p "Number of concurrent process threads" int no default="1" - diff --git a/src/progs/ingen/cmdline.h b/src/progs/ingen/cmdline.h deleted file mode 100644 index 80358089..00000000 --- a/src/progs/ingen/cmdline.h +++ /dev/null @@ -1,200 +0,0 @@ -/** @file cmdline.h - * @brief The header file for the command line option parser - * generated by GNU Gengetopt - * http://www.gnu.org/software/gengetopt. - * DO NOT modify this file, since it can be overwritten - * @author GNU Gengetopt by Lorenzo Bettini */ - -#ifndef CMDLINE_H -#define CMDLINE_H - -/* If we use autoconf. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include /* for FILE */ - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#ifndef CMDLINE_PARSER_PACKAGE -/** @brief the program name */ -#define CMDLINE_PARSER_PACKAGE "ingen" -#endif - -#ifndef CMDLINE_PARSER_VERSION -/** @brief the program version */ -#define CMDLINE_PARSER_VERSION VERSION -#endif - -/** @brief Where the command line options are stored */ -struct gengetopt_args_info -{ - const char *help_help; /**< @brief Print help and exit help description. */ - const char *version_help; /**< @brief Print version and exit help description. */ - int engine_flag; /**< @brief Run (JACK) engine (default=off). */ - const char *engine_help; /**< @brief Run (JACK) engine help description. */ - int engine_port_arg; /**< @brief Engine OSC port (default='16180'). */ - char * engine_port_orig; /**< @brief Engine OSC port original value given at command line. */ - const char *engine_port_help; /**< @brief Engine OSC port help description. */ - char * connect_arg; /**< @brief Connect to existing engine at OSC URI (default='osc.udp://localhost:16180'). */ - char * connect_orig; /**< @brief Connect to existing engine at OSC URI original value given at command line. */ - const char *connect_help; /**< @brief Connect to existing engine at OSC URI help description. */ - int gui_flag; /**< @brief Launch the GTK graphical interface (default=off). */ - const char *gui_help; /**< @brief Launch the GTK graphical interface help description. */ - int client_port_arg; /**< @brief Client OSC port. */ - char * client_port_orig; /**< @brief Client OSC port original value given at command line. */ - const char *client_port_help; /**< @brief Client OSC port help description. */ - char * load_arg; /**< @brief Load patch. */ - char * load_orig; /**< @brief Load patch original value given at command line. */ - const char *load_help; /**< @brief Load patch help description. */ - char * path_arg; /**< @brief Target path for loaded patch. */ - char * path_orig; /**< @brief Target path for loaded patch original value given at command line. */ - const char *path_help; /**< @brief Target path for loaded patch help description. */ - char * run_arg; /**< @brief Run script. */ - char * run_orig; /**< @brief Run script original value given at command line. */ - const char *run_help; /**< @brief Run script help description. */ - int parallelism_arg; /**< @brief Number of concurrent process threads (default='1'). */ - char * parallelism_orig; /**< @brief Number of concurrent process threads original value given at command line. */ - const char *parallelism_help; /**< @brief Number of concurrent process threads help description. */ - - unsigned int help_given ; /**< @brief Whether help was given. */ - unsigned int version_given ; /**< @brief Whether version was given. */ - unsigned int engine_given ; /**< @brief Whether engine was given. */ - unsigned int engine_port_given ; /**< @brief Whether engine-port was given. */ - unsigned int connect_given ; /**< @brief Whether connect was given. */ - unsigned int gui_given ; /**< @brief Whether gui was given. */ - unsigned int client_port_given ; /**< @brief Whether client-port was given. */ - unsigned int load_given ; /**< @brief Whether load was given. */ - unsigned int path_given ; /**< @brief Whether path was given. */ - unsigned int run_given ; /**< @brief Whether run was given. */ - unsigned int parallelism_given ; /**< @brief Whether parallelism was given. */ - -} ; - -/** @brief The additional parameters to pass to parser functions */ -struct cmdline_parser_params -{ - int override; /**< @brief whether to override possibly already present options (default 0) */ - int initialize; /**< @brief whether to initialize the option structure gengetopt_args_info (default 1) */ - int check_required; /**< @brief whether to check that all required options were provided (default 1) */ - int check_ambiguity; /**< @brief whether to check for options already specified in the option structure gengetopt_args_info (default 0) */ - int print_errors; /**< @brief whether getopt_long should print an error message for a bad option (default 1) */ -} ; - -/** @brief the purpose string of the program */ -extern const char *gengetopt_args_info_purpose; -/** @brief the usage string of the program */ -extern const char *gengetopt_args_info_usage; -/** @brief all the lines making the help output */ -extern const char *gengetopt_args_info_help[]; - -/** - * The command line parser - * @param argc the number of command line options - * @param argv the command line options - * @param args_info the structure where option information will be stored - * @return 0 if everything went fine, NON 0 if an error took place - */ -int cmdline_parser (int argc, char * const *argv, - struct gengetopt_args_info *args_info); - -/** - * The command line parser (version with additional parameters - deprecated) - * @param argc the number of command line options - * @param argv the command line options - * @param args_info the structure where option information will be stored - * @param override whether to override possibly already present options - * @param initialize whether to initialize the option structure my_args_info - * @param check_required whether to check that all required options were provided - * @return 0 if everything went fine, NON 0 if an error took place - * @deprecated use cmdline_parser_ext() instead - */ -int cmdline_parser2 (int argc, char * const *argv, - struct gengetopt_args_info *args_info, - int override, int initialize, int check_required); - -/** - * The command line parser (version with additional parameters) - * @param argc the number of command line options - * @param argv the command line options - * @param args_info the structure where option information will be stored - * @param params additional parameters for the parser - * @return 0 if everything went fine, NON 0 if an error took place - */ -int cmdline_parser_ext (int argc, char * const *argv, - struct gengetopt_args_info *args_info, - struct cmdline_parser_params *params); - -/** - * Save the contents of the option struct into an already open FILE stream. - * @param outfile the stream where to dump options - * @param args_info the option struct to dump - * @return 0 if everything went fine, NON 0 if an error took place - */ -int cmdline_parser_dump(FILE *outfile, - struct gengetopt_args_info *args_info); - -/** - * Save the contents of the option struct into a (text) file. - * This file can be read by the config file parser (if generated by gengetopt) - * @param filename the file where to save - * @param args_info the option struct to save - * @return 0 if everything went fine, NON 0 if an error took place - */ -int cmdline_parser_file_save(const char *filename, - struct gengetopt_args_info *args_info); - -/** - * Print the help - */ -void cmdline_parser_print_help(void); -/** - * Print the version - */ -void cmdline_parser_print_version(void); - -/** - * Initializes all the fields a cmdline_parser_params structure - * to their default values - * @param params the structure to initialize - */ -void cmdline_parser_params_init(struct cmdline_parser_params *params); - -/** - * Allocates dynamically a cmdline_parser_params structure and initializes - * all its fields to their default values - * @return the created and initialized cmdline_parser_params structure - */ -struct cmdline_parser_params *cmdline_parser_params_create(void); - -/** - * Initializes the passed gengetopt_args_info structure's fields - * (also set default values for options that have a default) - * @param args_info the structure to initialize - */ -void cmdline_parser_init (struct gengetopt_args_info *args_info); -/** - * Deallocates the string fields of the gengetopt_args_info structure - * (but does not deallocate the structure itself) - * @param args_info the structure to deallocate - */ -void cmdline_parser_free (struct gengetopt_args_info *args_info); - -/** - * Checks that all the required options were specified - * @param args_info the structure to check - * @param prog_name the name of the program that will be used to print - * possible errors - * @return - */ -int cmdline_parser_required (struct gengetopt_args_info *args_info, - const char *prog_name); - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* CMDLINE_H */ diff --git a/src/progs/ingen/ingen.dbg b/src/progs/ingen/ingen.dbg deleted file mode 100755 index 412c89bc..00000000 --- a/src/progs/ingen/ingen.dbg +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env sh - -export INGEN_MODULE_PATH="`pwd`/../../libs/engine/.libs:`pwd`/../../libs/serialisation/.libs:`pwd`/../../libs/gui/.libs:`pwd`/../../libs/client/.libs:`pwd`/../../bindings/.libs" -export INGEN_GLADE_PATH="`pwd`/../../libs/gui/ingen_gui.glade" -libtool --mode=execute gdb --args ./ingen $@ diff --git a/src/progs/ingen/ingen.desktop.in b/src/progs/ingen/ingen.desktop.in deleted file mode 100644 index 6bd00e05..00000000 --- a/src/progs/ingen/ingen.desktop.in +++ /dev/null @@ -1,9 +0,0 @@ -[Desktop Entry] -Encoding=UTF-8 -Name=Ingen -Comment=Create synthesizers and effects processors in a modular environment -Exec=ingen -g -Terminal=false -Icon=ingen-icon.svg -Type=Application -Categories=Application;AudioVideo;Sound;Audio diff --git a/src/progs/ingen/ingen.dev b/src/progs/ingen/ingen.dev deleted file mode 100755 index 023eba7e..00000000 --- a/src/progs/ingen/ingen.dev +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env sh - -export INGEN_MODULE_PATH="`pwd`/../../libs/engine/.libs:`pwd`/../../libs/serialisation/.libs:`pwd`/../../libs/gui/.libs:`pwd`/../../libs/client/.libs:`pwd`/../../bindings/.libs" -export INGEN_GLADE_PATH="`pwd`/../../libs/gui/ingen_gui.glade" -./ingen $@ diff --git a/src/progs/ingen/ingen.grind b/src/progs/ingen/ingen.grind deleted file mode 100644 index 9e597d44..00000000 --- a/src/progs/ingen/ingen.grind +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env sh - -export INGEN_MODULE_PATH="`pwd`/../../libs/engine/.libs:`pwd`/../../libs/serialisation/.libs:`pwd`/../../libs/gui/.libs:`pwd`/../../libs/client/.libs:`pwd`/../../bindings/.libs" -export INGEN_GLADE_PATH="`pwd`/../../libs/gui/ingen_gui.glade" -libtool --mode=execute valgrind ./ingen $@ diff --git a/src/progs/ingen/main.cpp b/src/progs/ingen/main.cpp deleted file mode 100644 index c8adf207..00000000 --- a/src/progs/ingen/main.cpp +++ /dev/null @@ -1,259 +0,0 @@ -/* This file is part of Ingen. - * Copyright (C) 2007 Dave Robillard - * - * Ingen 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. - * - * Ingen 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., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include CONFIG_H_PATH -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "module/global.hpp" -#include "module/Module.hpp" -#include "module/World.hpp" -#include "engine/Engine.hpp" -#include "engine/QueuedEngineInterface.hpp" -#include "serialisation/Parser.hpp" -#include "cmdline.h" - -#ifdef WITH_BINDINGS -#include "bindings/ingen_bindings.hpp" -#endif - - -using namespace std; -using namespace Ingen; - - -SharedPtr engine; - -void -catch_int(int) -{ - signal(SIGINT, catch_int); - signal(SIGTERM, catch_int); - - cout << "[Main] Ingen interrupted." << endl; - engine->quit(); -} - -int -main(int argc, char** argv) -{ - /* Parse command line options */ - gengetopt_args_info args; - if (cmdline_parser (argc, argv, &args) != 0) - return 1; - - if (argc <= 1) { - cmdline_parser_print_help(); - cerr << endl << "*** Ingen requires at least one command line parameter" << endl; - cerr << "*** Just want to use a graphical app? Try 'ingen -eg'" << endl; - return 1; - } else if (args.connect_given && args.engine_flag) { - cerr << "\n*** Nonsense arguments, can't both run a local engine " - << "and connect to a remote one." << endl - << "*** Run separate instances if that is what you want" << endl; - return 1; - } - - SharedPtr engine_module; - SharedPtr client_module; - SharedPtr gui_module; - SharedPtr bindings_module; - - SharedPtr engine_interface; - - Glib::thread_init(); -#if HAVE_SOUP - g_type_init(); -#endif - - Ingen::Shared::World* world = Ingen::Shared::get_world(); - - /* Set up RDF world */ - world->rdf_world->add_prefix("xsd", "http://www.w3.org/2001/XMLSchema#"); - world->rdf_world->add_prefix("ingen", "http://drobilla.net/ns/ingen#"); - world->rdf_world->add_prefix("ingenuity", "http://drobilla.net/ns/ingenuity#"); - world->rdf_world->add_prefix("lv2", "http://lv2plug.in/ns/lv2core#"); - world->rdf_world->add_prefix("lv2var", "http://lv2plug.in/ns/ext/instance-var#"); - world->rdf_world->add_prefix("lv2_midi", "http://lv2plug.in/ns/ext/midi"); - world->rdf_world->add_prefix("rdfs", "http://www.w3.org/2000/01/rdf-schema#"); - world->rdf_world->add_prefix("doap", "http://usefulinc.com/ns/doap#"); - world->rdf_world->add_prefix("dc", "http://purl.org/dc/elements/1.1/"); - - /* Run engine */ - if (args.engine_flag) { - engine_module = Ingen::Shared::load_module("ingen_engine"); - if (engine_module) { - Engine* (*new_engine)(Ingen::Shared::World* world) = NULL; - if (engine_module->get_symbol("new_engine", (void*&)new_engine)) { - engine = SharedPtr(new_engine(world)); - world->local_engine = engine; - /* Load queued (direct in-process) engine interface */ - if (args.gui_given) { - engine_interface = engine->new_queued_interface(); - world->engine = engine_interface; - } else { - engine->start_osc_driver(args.engine_port_arg); - engine->start_http_driver(args.engine_port_arg); - } - } else { - engine_module.reset(); - } - } else { - cerr << "Unable to load engine module." << endl; - } - } - - /* Load client library */ - if (args.load_given || args.gui_given) { - client_module = Ingen::Shared::load_module("ingen_client"); - if (!client_module) - cerr << "Unable to load client module." << endl; - } - - /* If we don't have a local engine interface (for GUI), use network */ - if (client_module && ! engine_interface) { - SharedPtr (*new_remote_interface)(const std::string&) = NULL; - - if (client_module->get_symbol("new_remote_interface", (void*&)new_remote_interface)) { - engine_interface = new_remote_interface(args.connect_arg); - } else { - cerr << "Unable to find symbol 'new_remote_interface' in " - "ingen_client module, aborting." << endl; - return -1; - } - } - - /* Activate the engine, if we have one */ - if (engine) { - engine->start_jack_driver(); - engine->activate(args.parallelism_arg); - } - - world->engine = engine_interface; - - /* Load a patch */ - if (args.load_given && engine_interface) { - - Glib::ustring engine_base = "/"; - if (args.path_given) - engine_base = string(args.path_arg) + "/"; - - bool found = false; - if (!world->serialisation_module) - world->serialisation_module = Ingen::Shared::load_module("ingen_serialisation"); - - Serialisation::Parser* (*new_parser)() = NULL; - - if (world->serialisation_module) - found = world->serialisation_module->get_symbol("new_parser", (void*&)new_parser); - - if (world->serialisation_module && found) { - SharedPtr parser(new_parser()); - - // Assumption: Containing ':' means URI, otherwise filename - string uri = args.load_arg; - if (uri.find(':') == string::npos) { - if (Glib::path_is_absolute(args.load_arg)) - uri = Glib::filename_to_uri(args.load_arg); - else - uri = Glib::filename_to_uri(Glib::build_filename( - Glib::get_current_dir(), args.load_arg)); - } - - - engine_interface->load_plugins(); - parser->parse_document(world, engine_interface.get(), uri, engine_base, uri); - - } else { - cerr << "Unable to load serialisation module, aborting." << endl; - return -1; - } - } - - - /* Run GUI */ - bool ran_gui = false; - if (args.gui_given) { - gui_module = Ingen::Shared::load_module("ingen_gui"); - void (*run)(int, char**, Ingen::Shared::World*); - bool found = gui_module->get_symbol("run", (void*&)run); - - if (found) { - ran_gui = true; - run(argc, argv, world); - } else { - cerr << "Unable to find GUI module, GUI not loaded." << endl; - } - } - - /* Run a script */ - if (args.run_given) { -#ifdef WITH_BINDINGS - bool (*run_script)(Ingen::Shared::World*, const char*) = NULL; - SharedPtr bindings_module = Ingen::Shared::load_module("ingen_bindings"); - if (!bindings_module) - cerr << Glib::Module::get_last_error() << endl; - - bindings_module->make_resident(); - - bool found = bindings_module->get_symbol("run", (void*&)(run_script)); - if (found) { - cerr << "WORLD: " << world << endl; - cerr << "ENGINE: " << world->engine << endl; - setenv("PYTHONPATH", "../../bindings", 1); - run_script(world, args.run_arg); - } else { - cerr << "FAILED: " << Glib::Module::get_last_error() << endl; - } -#else - cerr << "This build of ingen does not support scripting." << endl; -#endif - - /* Listen to OSC and do our own main thing. */ - } else if (engine && !ran_gui) { - signal(SIGINT, catch_int); - signal(SIGTERM, catch_int); - engine->main(); - } - - cout << "Exiting." << endl; - - if (engine) { - engine->deactivate(); - engine.reset(); - } - - engine_interface.reset(); - client_module.reset(); - world->serialisation_module.reset(); - gui_module.reset(); - engine_module.reset(); - - Ingen::Shared::destroy_world(); - - return 0; -} - diff --git a/src/progs/ingen/wscript b/src/progs/ingen/wscript deleted file mode 100644 index d2e42c51..00000000 --- a/src/progs/ingen/wscript +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -def build(bld): - obj = bld.create_obj('cpp', 'program') - obj.target = 'ingen' - obj.source = 'main.cpp cmdline.c' - obj.includes = ['.', '../../common', '../..', '../../libs'] - obj.defines = 'VERSION=\\\"' + bld.env()['INGEN_VERSION'] + '\\\"' - obj.uselib = 'GTHREAD GLIBMM REDLANDMM RAUL SLV2 INGEN LIBLO' - obj.uselib_local = 'libingen_module libingen_shared' - diff --git a/src/progs/python/Makefile.am b/src/progs/python/Makefile.am deleted file mode 100644 index c6da5467..00000000 --- a/src/progs/python/Makefile.am +++ /dev/null @@ -1,4 +0,0 @@ -SUBDIRS = scripts - -EXTRA_DIST = ingen.py ingenecho.py OSC.py - diff --git a/src/progs/python/OSC.py b/src/progs/python/OSC.py deleted file mode 100755 index ea93dc52..00000000 --- a/src/progs/python/OSC.py +++ /dev/null @@ -1,373 +0,0 @@ -#!/usr/bin/python -# -# Open SoundControl for Python -# Copyright (C) 2002 Daniel Holth, Clinton McChesney -# Modified by Leonard Ritter -# -# 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 library 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -# -# For questions regarding this module contact Daniel Holth -# or visit http://www.stetson.edu/~ProctoLogic/ - -import socket -import struct -import math -import sys -import string - - -def hexDump(bytes): - """Useful utility; prints the string in hexadecimal""" - for i in range(len(bytes)): - sys.stdout.write("%2x " % (ord(bytes[i]))) - if (i+1) % 8 == 0: - print repr(bytes[i-7:i+1]) - - if(len(bytes) % 8 != 0): - print string.rjust("", 11), repr(bytes[i-7:i+1]) - - -class OSCMessage: - """Builds typetagged OSC messages.""" - def __init__(self): - self.address = "" - self.typetags = "," - self.message = "" - - def setAddress(self, address): - self.address = address - - def setMessage(self, message): - self.message = message - - def setTypetags(self, typetags): - self.typetags = typetags - - def clear(self): - self.address = "" - self.clearData() - - def clearData(self): - self.typetags = "," - self.message = "" - - def append(self, argument, typehint = None): - """Appends data to the message, - updating the typetags based on - the argument's type. - If the argument is a blob (counted string) - pass in 'b' as typehint.""" - - if typehint == 'b': - binary = OSCBlob(argument) - else: - binary = OSCArgument(argument) - - self.typetags = self.typetags + binary[0] - self.rawAppend(binary[1]) - - def rawAppend(self, data): - """Appends raw data to the message. Use append().""" - self.message = self.message + data - - def getBinary(self): - """Returns the binary message (so far) with typetags.""" - address = OSCArgument(self.address)[1] - typetags = OSCArgument(self.typetags)[1] - return address + typetags + self.message - - def __repr__(self): - return self.getBinary() - -def readString(data): - length = string.find(data,"\0") - nextData = int(math.ceil((length+1) / 4.0) * 4) - return (data[0:length], data[nextData:]) - - -def readBlob(data): - length = struct.unpack(">i", data[0:4])[0] - nextData = int(math.ceil((length) / 4.0) * 4) + 4 - return (data[4:length+4], data[nextData:]) - - -def readInt(data): - if(len(data)<4): - print "Error: too few bytes for int", data, len(data) - rest = data - integer = 0 - else: - integer = struct.unpack(">i", data[0:4])[0] - rest = data[4:] - - return (integer, rest) - - - -def readLong(data): - """Tries to interpret the next 8 bytes of the data - as a 64-bit signed integer.""" - high, low = struct.unpack(">ll", data[0:8]) - big = (long(high) << 32) + low - rest = data[8:] - return (big, rest) - - - -def readFloat(data): - if(len(data)<4): - print "Error: too few bytes for float", data, len(data) - rest = data - float = 0 - else: - float = struct.unpack(">f", data[0:4])[0] - rest = data[4:] - - return (float, rest) - - -def OSCBlob(next): - """Convert a string into an OSC Blob, - returning a (typetag, data) tuple.""" - - if type(next) == type(""): - length = len(next) - padded = math.ceil((len(next)) / 4.0) * 4 - binary = struct.pack(">i%ds" % (padded), length, next) - tag = 'b' - else: - tag = '' - binary = '' - - return (tag, binary) - - -def OSCArgument(next): - """Convert some Python types to their - OSC binary representations, returning a - (typetag, data) tuple.""" - - if type(next) == type(""): - OSCstringLength = math.ceil((len(next)+1) / 4.0) * 4 - binary = struct.pack(">%ds" % (OSCstringLength), next) - tag = "s" - elif type(next) == type(42.5): - binary = struct.pack(">f", next) - tag = "f" - elif type(next) == type(13): - binary = struct.pack(">i", next) - tag = "i" - else: - binary = "" - tag = "" - - return (tag, binary) - - -def parseArgs(args): - """Given a list of strings, produces a list - where those strings have been parsed (where - possible) as floats or integers.""" - parsed = [] - for arg in args: - print arg - arg = arg.strip() - interpretation = None - try: - interpretation = float(arg) - if string.find(arg, ".") == -1: - interpretation = int(interpretation) - except: - # Oh - it was a string. - interpretation = arg - pass - parsed.append(interpretation) - return parsed - - - -def decodeOSC(data): - """Converts a typetagged OSC message to a Python list.""" - table = {"i":readInt, "f":readFloat, "s":readString, "b":readBlob} - decoded = [] - address, rest = readString(data) - typetags = "" - - if address == "#bundle": - time, rest = readLong(rest) - decoded.append(address) - decoded.append(time) - while len(rest)>0: - length, rest = readInt(rest) - decoded.append(decodeOSC(rest[:length])) - rest = rest[length:] - - elif len(rest)>0: - typetags, rest = readString(rest) - decoded.append(address) - decoded.append(typetags) - if(typetags[0] == ","): - for tag in typetags[1:]: - value, rest = table[tag](rest) - decoded.append(value) - else: - print "Oops, typetag lacks the magic ," - - # return only the data - return address,decoded[2:] - - -class CallbackManager: - """This utility class maps OSC addresses to callables. - - The CallbackManager calls its callbacks with a list - of decoded OSC arguments, including the address and - the typetags as the first two arguments.""" - - def __init__(self): - self.callbacks = {} - self.addCallback(self.unbundler, "#bundle") - - def handle(self, data, source = None): - """Given OSC data, tries to call the callback with the - right address.""" - decoded = decodeOSC(data) - self.dispatch(decoded) - - def dispatch(self, message): - """Sends decoded OSC data to an appropriate calback""" - try: - address = message[0] - self.callbacks[address](message) - except KeyError, e: - # address not found - print "no handler for '%s'" % address - pass - except None, e: - print "Exception in", address, "callback :", e - - return - - def addCallback(self, callback, name): - """Adds a callback to our set of callbacks, - or removes the callback with name if callback - is None.""" - if callback == None: - del self.callbacks[name] - else: - self.callbacks[name] = callback - - def unbundler(self, messages): - """Dispatch the messages in a decoded bundle.""" - # first two elements are #bundle and the time tag, rest are messages. - for message in messages[2:]: - self.dispatch(message) - - -if __name__ == "__main__": - hexDump("Welcome to the OSC testing program.") - print - message = OSCMessage() - message.setAddress("/foo/play") - message.append(44) - message.append(11) - message.append(4.5) - message.append("the white cliffs of dover") - hexDump(message.getBinary()) - - print "Making and unmaking a message.." - - strings = OSCMessage() - strings.append("Mary had a little lamb") - strings.append("its fleece was white as snow") - strings.append("and everywhere that Mary went,") - strings.append("the lamb was sure to go.") - strings.append(14.5) - strings.append(14.5) - strings.append(-400) - - raw = strings.getBinary() - - hexDump(raw) - - print "Retrieving arguments..." - data = raw - for i in range(6): - text, data = readString(data) - print text - - number, data = readFloat(data) - print number - - number, data = readFloat(data) - print number - - number, data = readInt(data) - print number - - hexDump(raw) - print decodeOSC(raw) - print decodeOSC(message.getBinary()) - - print "Testing Blob types." - - blob = OSCMessage() - blob.append("","b") - blob.append("b","b") - blob.append("bl","b") - blob.append("blo","b") - blob.append("blob","b") - blob.append("blobs","b") - blob.append(42) - - hexDump(blob.getBinary()) - - print decodeOSC(blob.getBinary()) - - def printingCallback(stuff): - sys.stdout.write("Got: ") - for i in stuff: - sys.stdout.write(str(i) + " ") - sys.stdout.write("\n") - - print "Testing the callback manager." - - c = CallbackManager() - c.add(printingCallback, "/print") - - c.handle(message.getBinary()) - message.setAddress("/print") - c.handle(message.getBinary()) - - print1 = OSCMessage() - print1.setAddress("/print") - print1.append("Hey man, that's cool.") - print1.append(42) - print1.append(3.1415926) - - c.handle(print1.getBinary()) - - bundle = OSCMessage() - bundle.setAddress("") - bundle.append("#bundle") - bundle.append(0) - bundle.append(0) - bundle.append(print1.getBinary(), 'b') - bundle.append(print1.getBinary(), 'b') - - bundlebinary = bundle.message - - print "sending a bundle to the callback manager" - c.handle(bundlebinary) diff --git a/src/progs/python/ingen.py b/src/progs/python/ingen.py deleted file mode 100644 index 07698719..00000000 --- a/src/progs/python/ingen.py +++ /dev/null @@ -1,636 +0,0 @@ -#!/usr/bin/python -# -# Python bindings for Ingen -# Copyright (C) 2005 Leonard Ritter -# Copyright (C) 2006 Dave Robillard - -# 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 library 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -import os,sys,thread,time - -from OSC import OSCMessage, decodeOSC - -from twisted.internet.protocol import DatagramProtocol -from twisted.internet import reactor - -INGEN_CALL_TIMEOUT = 5 -INGEN_CALL_POLLTIME = 0.1 - -class TreeElement: - def __init__(self,environment,parent,name): - self.environment = environment - self.parent = parent - self.name = name - self.metadata = {} - - def __del__(self): - print "'%s': gone" % self.name - - def removeChild(self, child): - pass - - def remove(self): - self.parent.removeChild(self.name) - self.parent = None - del self - - def getParent(self): - return self.parent - - def getName(self): - return self.name - - def getPath(self): - if self.parent: - return self.parent.getPath() + "/" + self.name - else: - return self.name - - def getDepth(self): - if self.parent: - return self.parent.getDepth() + 1 - else: - return 0 - - def setMetaData(self,key,value): - if (not self.metadata.has_key(value)) or (self.metadata[key] != value): - print "||| '%s': '%s' = '%s'" % (self.getPath(), key, value) - self.metadata[key] = value - -class Port(TreeElement): - def __init__(self,environment,parent,name): - TreeElement.__init__(self,environment,parent,name) - self.porttype = "" - self.direction = "" - self.hint = "" - self.defaultvalue = 0.0 - self.minvalue = 0.0 - self.maxvalue = 0.0 - self.value = 0.0 - self.connections = {} - print "*** '%s': new port" % self.getPath() - - def remove(self): - for connection in self.getConnections(): - connection.remove() - TreeElement.remove(self) - - def getConnections(self): - return self.connections - - def addConnection(self,target,connection): - self.connections[target] = connection - - def removeConnection(self,target): - del self.connections[target] - - def setPortType(self,porttype): - if self.porttype != porttype: - print "*** '%s': changing porttype from '%s' to '%s'" % (self.getPath(), self.porttype, porttype) - self.porttype = porttype - - def setDirection(self,direction): - if self.direction != direction: - print "*** '%s': changing direction from '%s' to '%s'" % (self.getPath(), self.direction, direction) - self.direction = direction - - def setHint(self,hint): - if self.hint != hint: - print "*** '%s': changing hint from '%s' to '%s'" % (self.getPath(), self.hint, hint) - self.hint = hint - - def setDefaultValue(self,defaultvalue): - if self.defaultvalue != defaultvalue: - print "*** '%s': changing defaultvalue from '%.3f' to '%.3f'" % (self.getPath(), self.defaultvalue, defaultvalue) - self.defaultvalue = defaultvalue - - def setMinValue(self,minvalue): - if self.minvalue != minvalue: - print "*** '%s': changing minvalue from '%.3f' to '%.3f'" % (self.getPath(), self.minvalue, minvalue) - self.minvalue = minvalue - - def setMaxValue(self,maxvalue): - if self.maxvalue != maxvalue: - print "*** '%s': changing maxvalue from '%.3f' to '%.3f'" % (self.getPath(), self.maxvalue, maxvalue) - self.maxvalue = maxvalue - - def setValue(self,value): - if self.value != value: - print "*** '%s': changing value from '%.3f' to '%.3f'" % (self.getPath(), self.value, value) - self.value = value - -class Node(TreeElement): - def __init__(self,environment,parent,name): - TreeElement.__init__(self,environment,parent,name) - self.ports = {} - self.polyphonic = 0 - self.plugintype = "" - self.libname = "" - self.pluginlabel = "" - print "+++ '%s': new node" % self.getPath() - - def remove(self): - for port in self.getPorts(): - port.remove() - TreeElement.remove(self) - - def removeChild(self, child): - del self.ports[child] - - def getPorts(self): - return self.ports.values() - - def setPluginLabel(self,pluginlabel): - if pluginlabel != self.pluginlabel: - print "+++ '%s': changing pluginlabel from '%s' to '%s'" % (self.getPath(), self.pluginlabel, pluginlabel) - self.pluginlabel = pluginlabel - - def setLibName(self,libname): - if libname != self.libname: - print "+++ '%s': changing libname from '%s' to '%s'" % (self.getPath(), self.libname, libname) - self.libname = libname - - def setPluginType(self,plugintype): - if plugintype != self.plugintype: - print "+++ '%s': changing plugintype from '%s' to '%s'" % (self.getPath(), self.plugintype, plugintype) - self.plugintype = plugintype - - def setPolyphonic(self,polyphonic): - if polyphonic != self.polyphonic: - print "+++ '%s': changing polyphony from %i to %i" % (self.getPath(), self.polyphonic, polyphonic) - self.polyphonic = polyphonic - - def hasPort(self,name): - return self.ports.has_key(name) - - def getPort(self,name,mustexist=False): - if not self.hasPort(name): - if mustexist: - return None - self.ports[name] = self.environment.getPortClass()(self.environment,self,name) - return self.ports[name] - -class Patch(Node): - def __init__(self,environment,parent,name): - Node.__init__(self,environment,parent,name) - self.nodes = {} - self.patches = {} - self.poly = 0 - self.enabled = False - print "### '%s': new patch" % self.getPath() - - def remove(self): - for patch in self.getPatches(): - patch.remove() - for node in self.getNodes(): - node.remove() - Node.remove(self) - - def removeChild(self, child): - if self.hasNode(child): - del self.nodes[child] - elif self.hasPatch(child): - del self.patches[child] - else: - Node.removeChild(self,child) - - def getPatches(self): - return self.patches.values() - - def getNodes(self): - return self.nodes.values() - - def getEnabled(self): - return self.enabled - - def setEnabled(self,enabled): - if enabled != self.enabled: - print "### '%s': changing enabled from %s to %s" % (self.getPath(), str(self.enabled), str(enabled)) - enabled = self.enabled - - def getPoly(self): - return self.poly - - def setPoly(self,poly): - if poly != self.poly: - print "### '%s': changing polyphony from %i to %i" % (self.getPath(), self.poly, poly) - self.poly = poly - - def hasNode(self,name): - return self.nodes.has_key(name) - - def getNode(self,name,mustexist=False): - if not self.hasNode(name): - if mustexist: - return None - self.nodes[name] = self.environment.getNodeClass()(self.environment,self,name) - return self.nodes[name] - - def hasPatch(self,name): - return self.patches.has_key(name) - - def getPatch(self,name,mustexist=False): - if not self.hasPatch(name): - if mustexist: - return None - self.patches[name] = self.environment.getPatchClass()(self.environment,self,name) - return self.patches[name] - -class Connection: - def __init__(self,environment,srcport,dstport): - self.environment = environment - self.srcport = srcport - self.dstport = dstport - self.srcport.addConnection(self.dstport,self) - self.dstport.addConnection(self.srcport,self) - print ">>> '%s'->'%s': new connection" % (self.srcport.getPath(),self.dstport.getPath()) - - def __del__(self): - print "connection gone" - - def remove(self): - self.srcport.removeConnection(self.dstport) - self.dstport.removeConnection(self.srcport) - del self - - def getSrcPort(self): - return self.srcport - - def getDstPort(self): - return self.dstport - - def getPortPair(self): - return self.srcport, self.dstport - -class Environment: - def __init__(self): - self.omPatch = self.getPatchClass()(self,None,"") - self.enabled = False - self.connections = {} - - def getConnectionClass(self): - return Connection - - def getPatchClass(self): - return Patch - - def getNodeClass(self): - return Node - - def getPortClass(self): - return Port - - def getConnection(self,srcportpath,dstportpath,mustexist=False): - srcport = self.getPort(srcportpath,True) - if not srcport: - return None - dstport = self.getPort(dstportpath,True) - if not dstport: - return None - if not self.connections.has_key((srcport,dstport)): - if mustexist: - return None - self.connections[(srcport,dstport)] = self.getConnectionClass()(self,srcport,dstport) - return self.connections[(srcport,dstport)] - - def getConnections(self): - return self.connections.values() - - def getPatch(self,path,mustexist=False): - elements = path.split("/") - currentPatch = None - for element in elements: - if element == "": - currentPatch = self.omPatch - else: - currentPatch = currentPatch.getPatch(element,mustexist) - if not currentPatch: - break - return currentPatch - - def getNode(self,path,mustexist=False): - elements = path.split("/") - basepath = "/".join(elements[:-1]) - nodename = elements[-1] - patch = self.getPatch(basepath,True) - if patch: - return patch.getNode(nodename,mustexist) - return None - - def getPort(self,path,mustexist=False): - elements = path.split("/") - basepath = "/".join(elements[:-1]) - portname = elements[-1] - node = self.getNode(basepath,True) - if node: - return node.getPort(portname,mustexist) - patch = self.getPatch(basepath,True) - if patch: - return patch.getPort(portname,mustexist) - return None - - def getObject(self,path): - patch = self.getPatch(path,True) - if patch: - return patch - node = self.getNode(path,True) - if node: - return node - return self.getPort(path,True) - - def printPatch(self,patch=None): - if not patch: - patch = self.omPatch - print patch.getDepth()*' ' + "### " + patch.getPath() - for node in patch.getNodes(): - print node.getDepth()*' ' + "+++ " + node.getPath() - for port in node.getPorts(): - print port.getDepth()*' ' + "*** " + port.getPath() - for port in patch.getPorts(): - print port.getDepth()*' ' + "*** " + port.getPath() - for subpatch in patch.getPatches(): - self.printPatch(subpatch) - - def printConnections(self): - for connection in self.getConnections(): - print ">>> %s -> %s" % (connection.getSrcPort().getPath(), connection.getDstPort().getPath()) - - #~ /ingen/engine_enabled - Notification engine's DSP has been enabled. - def __ingen__engine_enabled(self): - self.enabled = True - - #~ /ingen/engine_disabled - Notification engine's DSP has been disabled. - def __ingen__engine_disabled(self): - self.enabled = False - - #~ /ingen/new_node - Notification of a new node's creation. - #~ * path (string) - Path of the new node - #~ * polyphonic (integer-boolean) - Node is polyphonic (1 for yes, 0 for no) - #~ * type (string) - Type of plugin (LADSPA, DSSI, Internal, Patch) - #~ * lib-name (string) - Name of library if a plugin (ie cmt.so) - #~ * plug-label (string) - Label of plugin in library (ie adsr_env) - - #~ * New nodes are sent as a blob. The first message in the blob will be this one (/ingen/new_node), followed by a series of /om/new_port commands, followed by /om/new_node_end. - def __ingen__new_node(self,path,polyphonic,plugintype,libname,pluginlabel): - node = self.getNode(path) - node.setPolyphonic(polyphonic) - node.setPluginType(plugintype) - node.setLibName(libname) - node.setPluginLabel(pluginlabel) - - def __ingen__new_node_end(self): - pass - - #~ /ingen/node_removal - Notification of a node's destruction. - #~ * path (string) - Path of node (which no longer exists) - def __ingen__node_removal(self,path): - node = self.getNode(path) - node.remove() - - #~ /ingen/new_port - Notification of a node's destruction. - - #~ * path (string) - Path of new port - #~ * type (string) - Type of port (CONTROL or AUDIO) - #~ * direction (string) - Direction of data flow (INPUT or OUTPUT) - #~ * hint (string) - Hint (INTEGER, LOGARITHMIC, TOGGLE, or NONE) - #~ * default-value (float) - Default (initial) value - #~ * min-value (float) - Suggested minimum value - #~ * min-value (float) - Suggested maximum value - - #~ * 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 (ahem, Lachlan). - #~ * 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 ingenuity are not these ones! Those ranges are set as metadata. - def __ingen__new_port(self,path,porttype,direction,hint,defaultvalue,minvalue,maxvalue): - port = self.getPort(path) - port.setPortType(porttype) - port.setDirection(direction) - port.setHint(hint) - port.setDefaultValue(defaultvalue) - port.setMinValue(minvalue) - port.setMaxValue(maxvalue) - - #~ /ingen/port_removal - Notification of a port's destruction. - #~ * path (string) - Path of port (which no longer exists) - def __ingen__port_removal(self,path): - port = self.getPort(path) - port.remove() - - #~ /ingen/patch_destruction - Notification of a patch's destruction. - #~ * path (string) - Path of patch (which no longer exists) - def __ingen__patch_destruction(self,path): - patch = self.getPatch(path) - patch.remove() - - #~ /ingen/patch_enabled - Notification a patch's DSP processing has been enabled. - #~ * path (string) - Path of enabled patch - def __ingen__patch_enabled(self,path): - patch = self.getPatch(path) - patch.setEnabled(True) - - #~ /ingen/patch_disabled - Notification a patch's DSP processing has been disabled. - #~ * path (string) - Path of disabled patch - def __ingen__patch_disabled(self,path): - patch = self.getPatch(path) - patch.setEnabled(False) - - #~ /ingen/new_connection - Notification a new connection has been made. - #~ * src-path (string) - Path of the source port - #~ * dst-path (string) - Path of the destination port - def __ingen__new_connection(self,srcpath,dstpath): - self.getConnection(srcpath,dstpath) - - #~ /ingen/disconnection - Notification a connection has been unmade. - #~ * src-path (string) - Path of the source port - #~ * dst-path (string) - Path of the destination port - def __ingen__disconnection(self,srcpath,dstpath): - connection = self.getConnection(srcpath,dstpath) - portpair = connection.getPortPair() - connection.remove() - del self.connections[portpair] - - #~ /ingen/metadata/update - Notification of a piece of metadata. - #~ * path (string) - Path of the object associated with metadata (can be a node, patch, or port) - #~ * key (string) - #~ * value (string) - def __ingen__metadata__update(self,path,key,value): - object = self.getObject(path) - object.setMetaData(key,value) - - #~ /ingen/control_change - Notification the value of a port has changed - #~ * path (string) - Path of port - #~ * value (float) - New value of port - #~ * This will only send updates for values set by clients of course - not values changing because of connections to other ports! - def __ingen__control_change(self,path,value): - port = self.getPort(path) - port.setValue(value) - - #~ /ingen/new_patch - Notification of a new patch - #~ * path (string) - Path of new patch - #~ * poly (int) - Polyphony of new patch (not a boolean like new_node) - def __ingen__new_patch(self,path,poly): - patch = self.getPatch(path) - patch.setPoly(poly) - -class SocketError: - pass - -class Call: - pass - -class ClientProxy: - def __init__(self, om, name, is_async = False): - self.name = name - self.om = om - self.is_async = is_async - - def __call__(self, *args): - if (self.is_async): - self.om.sendMsg(self.name, *args) - return True - - result = self.om.sendMsgBlocking(self.name, *args) - if not result: - return None - if result[0] == "/ingen/response/ok": - return True - print "ERROR: %s" % result[1][1] - return False - - def __getattr__(self, name): - if (name[:2] == "__") and (name[-2:] == "__"): # special function - raise AttributeError, name - if name in self.__dict__: - raise AttributeError, name - if name == 'async': - return ClientProxy(self.om, self.name, True) - else: - return ClientProxy(self.om, self.name + '/' + name) - -class Client(DatagramProtocol, ClientProxy): - def __init__(self): - ClientProxy.__init__(self, self, "/ingen") - - def startProtocol(self): - self.transport.connect("127.0.0.1", 16180) - host = self.transport.getHost() - self.host = host.host - self.port = host.port - self.environment = None - self.handlers = {} - self.calls = {} - #print "opened port at %s" % (str((self.host,self.port))) - self.nextPacketNumber = 1 - - def setEnvironment(self, environment): - self.handlers = {} - self.environment = environment - for name in dir(self.environment): - element = getattr(self.environment,name) - if (type(element).__name__ == 'instancemethod') and (element.__name__[:6] == "__ingen__"): - handlername = element.__name__.replace("__","/") - print "registering handler for '%s'" % handlername - self.handlers[handlername] = element - - def getEnvironment(self): - return self.environment - - def connectionRefused(self): - print "Noone listening, aborting." - os._exit(-1) - - def messageReceived(self, (msg, args)): - if msg == "/ingen/error": - print "ERROR: %r" % args - return - if msg == "/ingen/response/ok": - omcall = self.calls[args[0]] - omcall.result = (msg,args) - omcall.done = True - return - if msg == "/ingen/response/error": - omcall = self.calls[args[0]] - omcall.result = (msg,args) - omcall.done = True - return - if msg == "#bundle": - for arg in args: - self.messageReceived(arg) - return - if self.handlers.has_key(msg): - try: - self.handlers[msg](*args) - except: - a,b,c = sys.exc_info() - sys.excepthook(a,b,c) - print "with '%s'" % repr((msg,args)) - return - print "no handler for '%s'" % repr((msg,args)) - - def datagramReceived(self, data, (host, port)): - self.messageReceived(decodeOSC(data)) - - def getPacketNumber(self): - packetNumber = self.nextPacketNumber - self.nextPacketNumber = (self.nextPacketNumber + 1) - return packetNumber - - def sendMsg(self,msg,*args): - packetNumber = self.getPacketNumber() - print "Sending %r (#%i)..." % (msg,packetNumber) - omcall = Call() - omcall.result = None - omcall.done = False - self.calls[packetNumber] = omcall - message = OSCMessage() - message.setAddress(msg) - message.append(packetNumber) - for arg in args: - message.append(arg) - self.transport.write(message.getBinary()) - time.sleep(0.01) - return True - - def sendMsgBlocking(self,msg,*args): - packetNumber = self.getPacketNumber() - print "Sending %r (#%i)..." % (msg,packetNumber) - omcall = Call() - omcall.result = None - omcall.done = False - self.calls[packetNumber] = omcall - message = OSCMessage() - message.setAddress(msg) - message.append(packetNumber) - for arg in args: - message.append(arg) - self.transport.write(message.getBinary()) - now = time.time() - while not omcall.done: - time.sleep(INGEN_CALL_POLLTIME) - distance = time.time() - now - if distance > INGEN_CALL_TIMEOUT: - print "timeout" - break - del self.calls[packetNumber] - return omcall.result - - def getAddressAsString(self): - return "osc.udp://%s:%d" % (self.host, self.port) - - - -def startClient(func): - om = Client() - reactor.listenUDP(0, om) - thread.start_new_thread(func,(om,)) - reactor.run() diff --git a/src/progs/python/ingenecho.py b/src/progs/python/ingenecho.py deleted file mode 100644 index 1dcfc313..00000000 --- a/src/progs/python/ingenecho.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/python -# -# Python bindings for Om -# Copyright (C) 2005 Leonard Ritter -# -# 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 library 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -import ingen -import os,time,sys - -def main(om): - om.setEnvironment(ingen.Environment()) - om.engine.activate() - om.engine.register_client(om.getAddressAsString()) - om.request.all_objects(om.getAddressAsString()) - - om.request.all_objects() - time.sleep(3) - om.getEnvironment().printPatch() - om.getEnvironment().printConnections() - print "ingenecho will now monitor and mirror changes in the structure" - print "hit return to exit when youre done" - sys.stdin.readline() - om.engine.unregister_client(om.getAddressAsString()) - os._exit(0) - -if __name__ == "__main__": - ingen.startClient(main) diff --git a/src/progs/python/scripts/Makefile.am b/src/progs/python/scripts/Makefile.am deleted file mode 100644 index 3e070601..00000000 --- a/src/progs/python/scripts/Makefile.am +++ /dev/null @@ -1,2 +0,0 @@ -EXTRA_DIST = sillysinepatch.py - diff --git a/src/progs/python/scripts/flatten.py b/src/progs/python/scripts/flatten.py deleted file mode 100755 index 530e4291..00000000 --- a/src/progs/python/scripts/flatten.py +++ /dev/null @@ -1,232 +0,0 @@ -#!/usr/bin/python - -############################################################################### -# -# flatten.py - a python script that merges all subpatches in an Ingen patch -# into the parent patch -# -# Copyright (C) 2005 Lars Luthman -# -# This program 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. -# -# 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 General Public License for more 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -# -############################################################################### - -import ingen -import os,time,sys - - -def getPatchBounds(patch): - """Returns the smallest rectangle that contains all modules in the patch.""" - min_x = None - min_y = None - max_x = None - max_y = None - for node in patch.getNodes(): - x = node.metadata['module-x'] - y = node.metadata['module-y'] - if (x != None): - if (min_x == None or float(x) < min_x): - min_x = float(x) - if (max_x == None or float(x) > max_x): - max_x = float(x) - if (y != None): - if (min_y == None or float(y) < min_y): - min_y = float(y) - if (max_y == None or float(y) > max_y): - max_y = float(y) - if min_x == None: - min_x = 0 - max_x = 0 - if min_y == None: - min_y = 0 - max_y = 0 - - return (min_x, min_y, max_x, max_y) - - -def cloneNode(om, node, patch): - """Copy a node into a patch, return the new node's name.""" - - # create a node with a free name in the parent - names = [] - for node2 in patch.getNodes(): - names.append(node2.getName()) - for patch2 in patch.getPatches(): - names.append(patch2.getName()) - names.sort() - name = node.getName() - for name2 in names: - if name2 == name: - name = name + '_' - om.synth.create_node.async(patch.getPath() + '/' + name, - node.plugintype, node.libname, - node.pluginlabel, node.polyphonic) - - # copy port values - for port in node.getPorts(): - path = '%s/%s/%s' % (patch.getPath(), name, port.getName()) - om.synth.set_port_value_slow.async(path, port.value) - om.metadata.set.async(path, 'user-min', '%f' % port.minvalue) - om.metadata.set.async(path, 'user-max', '%f' % port.maxvalue) - return name - - -def flatten(om, patch): - """Merge all subpatches into the parent patch.""" - - # something is wrong, we don't have a patch - if patch == None: - return - - # iterate over all subpatches - for subpatch in patch.getPatches(): - flatten(om, subpatch) - lookup = {} - - # copy all nodes from the subpatch to the parent patch - for node in subpatch.getNodes(): - lookup[node.getName()] = cloneNode(om, node, patch) - - # copy all connections - for node in subpatch.getNodes(): - for port in node.getPorts(): - if port.direction == 'OUTPUT': - for target in port.getConnections().keys(): - targetname = '%s/%s' % (lookup[target.getParent().getName()], target.getName()) - om.synth.connect.async(patch.getPath() + '/' + - lookup[node.getName()] + '/' + - port.getName(), - patch.getPath() + '/' + - targetname) - - # make external connections - for node in subpatch.getNodes(): - if node.libname == '': - lbl = node.pluginlabel - - if lbl == 'audio_input' or lbl == 'control_input': - port1 = node.getPort('in') - for port2 in port1.getConnections().keys(): - dst = '%s/%s/%s' % (patch.getPath(), lookup[port2.getParent().getName()], port2.getName()) - port4 = subpatch.getPort(node.getName()) - conns = port4.getConnections().keys() - if len(conns) == 0: - portValue = port4.value - om.synth.set_port_value_slow.async(dst, portValue) - else: - for port3 in port4.getConnections().keys(): - src = port3.getPath() - om.synth.connect.async(src, dst) - - if lbl == 'audio_output' or lbl == 'control_output': - port2 = node.getPort('out', True) - for port1 in port2.getConnections().keys(): - src = '%s/%s/%s' % (patch.getPath(), lookup[port1.getParent().getName()], port1.getName()) - port3 = subpatch.getPort(node.getName()) - for port4 in port3.getConnections().keys(): - dst = port4.getPath() - om.synth.connect.async(src, dst) - - # destroy all input and output nodes from the subpatch - for node in subpatch.getNodes(): - if node.libname == '': - lbl = node.pluginlabel - if (lbl == 'audio_input' or lbl == 'control_input' or - lbl == 'audio_output' or lbl == 'control_output'): - om.synth.destroy_node.async('%s/%s' % (patch.getPath(), - lookup[node.getName()])) - - # calculate where to move all the new nodes - (min_x, min_y, max_x, max_y) = getPatchBounds(subpatch) - sub_x = subpatch.metadata['module-x'] - if sub_x == None: - sub_x = 0 - sub_y = subpatch.metadata['module-y'] - if sub_y == None: - sub_y = 0 - x_offset = float(sub_x) - if min_x != None: - x_offset = float(sub_x) - min_x - y_offset = float(sub_y) - if min_y != None: - y_offset = float(sub_y) - min_y - - # move the new nodes - for node in subpatch.getNodes(): - x = float(node.metadata['module-x']) - if x == None: - x = 0 - om.metadata.set.async('%s/%s' % (patch.getPath(), - lookup[node.getName()]), - 'module-x', '%f' % (x + x_offset)) - y = float(node.metadata['module-y']) - if y == None: - y = 0 - om.metadata.set.async('%s/%s' % (patch.getPath(), - lookup[node.getName()]), - 'module-y', '%f' % (y + y_offset)) - - # move the old nodes in the patch - x_offset = 0 - if min_x != None and max_x != None: - x_offset = max_x - min_x - y_offset = 0 - if min_y != None and max_y != None: - y_offset = max_y - min_y - for node in patch.getNodes(): - if node.getName() not in lookup.values(): - x = node.metadata['module-x'] - if x != None and float(x) > float(sub_x): - om.metadata.set.async(node.getPath(), 'module-x', - '%f' % (float(x) + x_offset)) - y = node.metadata['module-y'] - if y != None and float(y) > float(sub_y): - om.metadata.set.async(node.getPath(), 'module-y', - '%f' % (float(y) + y_offset)) - # destroy the subpatch - om.synth.destroy_patch(subpatch.getPath()) - - -def main(om): - om.setEnvironment(ingen.Environment()) - om.engine.activate.async() - om.engine.load_plugins.async() - om.request.all_objects(om.getAddressAsString()) - - # wait for all the data to arrive (there should be a cleaner way) - time.sleep(3) - - patch = om.getEnvironment().getPatch(sys.argv[1], True); - flatten(om, patch) - - os._exit(0) - - -if len(sys.argv) > 1: - if sys.argv[1] == "--name": - print "Flatten patch" - os._exit(0) - elif sys.argv[1] == "--shortdesc": - print "Merge the contents of all subpatches into the parent patch"; - os._exit(0) - elif sys.argv[1] == "--signature": - print "%p"; - os._exit(0) - else: - ingen.startClient(main) - -else: - print "Which patch do you want to flatten?" - os._exit(0) diff --git a/src/progs/python/scripts/sillysinepatch.py b/src/progs/python/scripts/sillysinepatch.py deleted file mode 100644 index dcb37a97..00000000 --- a/src/progs/python/scripts/sillysinepatch.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/python -# -# Python bindings for Om -# Copyright (C) 2005 Leonard Ritter -# -# 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 library 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -import ingen -import os,time,sys - -def main(om): - om.setEnvironment(ingen.Environment()) - om.engine.activate() - om.engine.load_plugins() - om.engine.register_client(om.getAddressAsString()) - om.request.all_objects(om.getAddressAsString()) - om.synth.create_patch("/silly_sine", 1) - om.synth.create_node("/silly_sine/output", "Internal", "", "audio_output", 0) - om.synth.create_node("/silly_sine/sine", "LADSPA", "cmt.so", "sine_fcac", 0) - om.synth.set_port_value("/silly_sine/sine/Frequency", 440.0) - om.synth.set_port_value("/silly_sine/sine/Amplitude", 1.0) - om.synth.connect("/silly_sine/sine/Output", "/silly_sine/output/out") - om.synth.enable_patch("/silly_sine") - om.engine.enable() - om.engine.unregister_client(om.getAddressAsString()) - os._exit(0) - -if __name__ == "__main__": - ingen.startClient(main) diff --git a/src/progs/supercollider/Ingen.sc b/src/progs/supercollider/Ingen.sc deleted file mode 100644 index 873c8c2b..00000000 --- a/src/progs/supercollider/Ingen.sc +++ /dev/null @@ -1,746 +0,0 @@ -// TODO: -// * Keep track of established connections. -Ingen : Model { - classvar <>program = "om", <>patchLoader = "om_patch_loader"; - classvar <>oscURL, uiClass; - var loadIntoJack = true; - var allocator, requestResponders, requestHandlers, notificationResponders; - var creatingNode, newNodeEnd; - var IngenInternalNode, - \LADSPA -> IngenLADSPANode, - \DSSI -> IngenDSSINode - ]; - uiClass = IngenEmacsUI - } - *new { | netaddr | - ^super.new.init(netaddr) - } - gui { ^uiClass.new(this) } - init { |netaddr| - addr = netaddr ? NetAddr("127.0.0.1", 16180); - onNewPatch = IdentityDictionary.new; - allocator = StackNumberAllocator(0,1024); - requestHandlers = IdentityDictionary.new; - requestResponders = [ - "response/ok" -> {|id| - requestHandlers.removeAt(id).value; allocator.free(id) }, - "response/error" -> {|id,text| - requestHandlers.removeAt(id); - allocator.free(id); - ("Ingen"+text).error } - ].collect({|a| - var func = a.value; - OSCresponder(addr, "/om/"++a.key, {|time,resp,msg| - func.value(*msg[1..]) - }) - }); - notificationResponders = [ - "new_patch" -> {|path,poly| - var func = onNewPatch.removeAt(path); - if (func.notNil) { - func.value(this.getPatch(path,false).prSetPoly(poly)) - } - }, - "metadata/update" -> {|path,key,value| - this.getObject(path).metadata.prSetMetadata(key, value) }, - "new_node" -> {|path,poly,type,lib,label| - var patchPath, nodeName, patch, node; - var lastSlash = path.asString.inject(nil,{|last,char,i| - if(char==$/,i,last) - }); - if (lastSlash.notNil) { - patchPath = path.asString.copyFromStart(lastSlash-1); - nodeName = path.asString.copyToEnd(lastSlash+1); - patch = this.getPatch(patchPath); - if (patch.notNil) { - if (patch.hasNode(nodeName).not) { - node = nodeTypeMap[type].new - (nodeName, patch, poly, label, lib); - creatingNode = node; - patch.nodes[nodeName.asSymbol] = node; - patch.changed(\newNode, node); - } { - if (patch.getNode(nodeName).class != nodeTypeMap[type]) { - ("Ingen sent an existng node with differing type"+path).warn - } - } - } { - ("Ingen tried to create node in non-existing patch"+patchPath).warn - } - } { - ("Invalid path in node creation"+path).warn - } - }, - "new_node_end" -> { - newNodeEnd.value(creatingNode); - newNodeEnd = nil; - creatingNode = nil }, - "new_port" -> {|path,type,dir,hint,def,min,max| - var basePath, portName, parent, port; - var lastSlash = path.asString.inject(nil,{|last,char,i| - if(char==$/,i,last) - }); - if (lastSlash.notNil) { - basePath = path.asString.copyFromStart(lastSlash-1); - portName = path.asString.copyToEnd(lastSlash+1); - parent = this.getNode(basePath) ? this.getPatch(basePath); - if (parent.notNil) { - if (parent.hasPort(portName).not) { - port = IngenPort.new(portName, parent, type, dir, hint, def, min, max); - parent.ports[portName.asSymbol] = port; - parent.changed(\newPort, port) - } { - if (parent.getPort(portName).porttype != type) { - ("Ingen tried to create an already existing port with differing type" - +path).warn - } - } - } { - ("Ingen tried to create port on non-existing object"+basePath).warn - } - } { - ("Invalid path in port creation"+path).warn - } - }, - "control_change" -> {|path,value| - this.getPort(path).prSetValue(value) }, - "patch_enabled" -> {|path| this.getPatch(path).prSetEnabled(true) }, - "patch_disabled" -> {|path| this.getPatch(path).prSetEnabled(false) }, - "plugin" -> {|lib,label,name,type| - plugins.add(Event.new(4,nil,pluginParentEvent).putAll( - (type:type, lib:lib, label:label, name:name))) }, - "node_removal" -> {|path| - var node = this.getNode(path); - if (node.notNil) { - node.parent.nodes.removeAt(node.name.asSymbol).free - } { - ("Ingen attempting to remove non-existing node"+path).warn - } - }, - "port_removal" -> {|path| - var port = this.getPort(path); - if (port.notNil) { - port.parent.ports.removeAt(port.name.asSymbol).free - } { - ("Ingen attempting to remove non-existing port"+path).warn - } - }, - "patch_destruction" -> {|path| - var patch = this.getPatch(path); - if (patch.notNil) { - patch.parent.patches.removeAt(patch.name.asSymbol).free - } { - ("Ingen attempting to remove non-existing patch"+path).warn - } - }, - "program_add" -> {|path,bank,program,name| - var node = this.getNode(path); - if (node.respondsTo(\prProgramAdd)) { - node.prProgramAdd(bank,program,name) - } { - ("Ingen tried to add program info to"+node).warn - } - } - ].collect({|a| - var func = a.value; - OSCresponder(addr, "/om/"++a.key, {|time,resp,msg| - func.value(*msg[1..]) - }) - }); - pluginParentEvent = Event.new(2,nil,nil).putAll(( - engine:this, - new:{|self,path,poly=1,handler|self.engine.createNode(path?("/"++self.name),self.type,self.lib,self.label,poly,created:handler)} - )); - } - *waitForBoot {|func| ^this.new.waitForBoot(func) } - waitForBoot {|func| - var r, id = 727; - requestHandlers[id] = { - r.stop; - booting=false; - this.changed(\running, true); - func.value(this) - }; - if (booting.not) {this.boot}; - r = Routine.run { - 50.do { - 0.1.wait; - addr.sendMsg("/om/ping", id) - }; - requestHandlers.removeAt(id); - "Ingen engine boot failed".error; - } - } - getPatch {|path, mustExist=true| - var elements, currentPatch; - if (path.class == Array) { elements = path - } { elements = path.asString.split($/) }; - elements.do{|elem| - if (elem=="") { - currentPatch = root - } { - currentPatch = currentPatch.getPatch(elem,mustExist); - if (currentPatch.isNil) { ^nil } - } - }; - ^currentPatch; - } - getNode {|path| - var basePath, nodeName, patch; - if (path.class == Array) { basePath = path - } { basePath = path.asString.split($/) }; - nodeName = basePath.pop; - patch = this.getPatch(basePath,true); - if (patch.notNil) { - ^patch.getNode(nodeName) - }; - ^nil - } - getPort {|path| - var basePath, portName, node, patch; - basePath = path.asString.split($/); - portName = basePath.pop; - node = this.getNode(basePath.copy); - if (node.notNil) { ^node.getPort(portName) }; - patch = this.getPatch(basePath,true); - if (patch.notNil) { ^patch.getPort(portName) }; - ^nil - } - getObject {|path| - var patch,node,port; - patch = this.getPatch(path,true); - if (patch.notNil) { ^patch }; - node = this.getNode(path); - if (node.notNil) { ^node }; - port = this.getPort(path,true); - if (port.notNil) { ^port }; - ^nil - } - at {|path|^this.getObject(path.asString)} - *boot {|func| - ^Ingen.new.waitForBoot {|e| - e.activate { - e.register { - e.loadPlugins { - e.requestPlugins { - e.requestAllObjects { - func.value(e) - } - } - } - } - } - } - } - boot { - requestResponders.do({|resp| resp.add}); - booting = true; - if (addr.addr == 2130706433) { - if (loadIntoJack) { - ("jack_load"+"-i"+addr.port+"Ingen"+"om").unixCmd - } { - (program+"-p"+addr.port).unixCmd - } - } { - "You have to manually boot Ingen now".postln - } - } - loadPatch {|patchPath| (patchLoader + patchPath).unixCmd } - activate { | handler | - this.sendReq("engine/activate", { - root = IngenPatch("",nil,this); - this.changed(\newPatch, root); - handler.value - }) - } - register { | handler | - this.sendReq("engine/register_client", { - registered=true; - notificationResponders.do({|resp| resp.add}); - this.changed(\registered, registered); - handler.value(this) - }) - } - unregister { | handler | - this.sendReq("engine/unregister_client", { - registered=false; - notificationResponders.do({|resp| resp.remove}); - this.changed(\registered, registered); - handler.value(this) - }) - } - registered_ {|flag| - if (flag and: registered.not) { - this.register - } { - if (flag.not and: registered) { - this.unregister - } - } - } - loadPlugins { | handler | this.sendReq("engine/load_plugins", handler) } - requestPlugins {|handler| - var startTime = Main.elapsedTime; - plugins = Set.new; - this.sendReq("request/plugins", { - ("Received info about"+plugins.size+"plugins in"+(Main.elapsedTime-startTime)+"seconds").postln; - this.changed(\plugins, plugins); - handler.value(this); - }) - } - requestAllObjects { |handler| - this.sendReq("request/all_objects", handler) - } - createPatch { | path, poly=1, handler | - onNewPatch[path.asSymbol] = handler; - this.sendReq("synth/create_patch", nil, path.asString, poly.asInteger) - } - createNode { | path, type='LADSPA', lib, label, poly=1, created, handler | - newNodeEnd = created; - this.sendReq("synth/create_node",handler,path,type,lib,label,poly) - } - createAudioInput { | path, handler | - this.createNode(path,"Internal","","audio_input",0,handler) - } - createAudioOutput {|path,handler| - this.createNode(path,"Internal","","audio_output",0,handler) - } - createMIDIInput {|path,handler| - this.createNode(path,"Internal","","midi_input",1,handler) - } - createMIDIOutput {|path,handler| - this.createNode(path,"Internal","","midi_output",1,handler) - } - createNoteIn {|path| this.createNode(path,"Internal","","note_in") } - connect {|fromPath,toPath,handler| - this.sendReq("synth/connect",handler,fromPath.asString,toPath.asString) - } - disconnect { | fromPath, toPath, handler | - this.sendReq("synth/disconnect",handler,fromPath.asString,toPath.asString) - } - disconnectAll { | path, handler | - this.sendReq("synth/disconnect_all",handler,path); - } - sendReq { | path, handler...args | - var id = allocator.alloc; - requestHandlers[id] = handler; - addr.sendMsg("/om/"++path, id, *args) - } - quit { - if (loadIntoJack) { - ("jack_unload"+"Ingen").unixCmd; - booting=false; - requestResponders.do(_.remove); - notificationResponders.do(_.remove); - this.changed(\running, false); - } { - this.sendReq("engine/quit", { - booting=false; - requestResponders.do(_.remove); - notificationResponders.do(_.remove); - this.changed(\running, false); - }) - } - } - ping {| n=1, func | - var id, result, start; - id = allocator.alloc; - result = 0; - requestHandlers[id] = { - var end; - end = Main.elapsedTime; - result=max((end-start).postln,result); - n=n-1; - if (n > 0) { - start = Main.elapsedTime; - addr.sendMsg("/om/ping", id) - } { - allocator.free(id); - func.value(result) - } - }; - start = Main.elapsedTime; - addr.sendMsg("/om/ping", id) - } - setPortValue {|path, val| this.getPort(path.asString).value=val } - jackConnect {|path, jackPort| - this.getPort(path).jackConnect(jackPort) - } - noteOn {|path, note, vel| - var patch,node; - patch = this.getPatch(path,true); - if (patch.notNil) { patch.noteOn(note,vel) }; - node = this.getNode(path); - if (node.notNil) { node.noteOn(note,vel) }; - } - noteOff {|path, note| - var patch,node; - patch = this.getPatch(path,true); - if (patch.notNil) { patch.noteOff(note) }; - node = this.getNode(path); - if (node.notNil) { node.noteOff(note) }; - } - matchPlugins{ | label, lib, name, type | - ^plugins.select{ |p| - label.matchItem(p.label) and: { - lib.matchItem(p.lib) and: { - name.matchItem(p.name) and: { - type.matchItem(p.type) - } - } - } - } - } - dssiMsg {|path,reqType="program" ...args| - addr.sendMsg("/dssi"++path++$/++reqType,*args) - } -} - -IngenMetadata { - var object, dict; - *new {|obj|^super.new.metadataInit(obj)} - metadataInit {|obj| - dict=Dictionary.new; - object=obj - } - put {|key,val| - object.engine.sendReq("metadata/set", nil, - object.path, key.asString, val.asString) - } - at {|key|^dict.at(key.asSymbol)} - prSetMetadata {|key,val| - dict.put(key,val); - object.changed(\metadata, key, val) - } -} - -IngenObject : Model { - var diff --git a/src/progs/supercollider/example.sc b/src/progs/supercollider/example.sc deleted file mode 100644 index 80a72a7b..00000000 --- a/src/progs/supercollider/example.sc +++ /dev/null @@ -1,27 +0,0 @@ -// Boot Ingen (inside SC) and the scsynth server -( -o=Ingen.boot { - o.loadPatch("/home/mlang/src/om-synth/src/clients/patches/303.om"); - s.boot; -} -) -// Connect patch output to one SC input -o.jackConnect("/303/output", "SuperCollider:in_3"); -// Play this input on the default output bus (most simple postprocessor) -{AudioIn.ar(3).dup}.play; -// A simple 303 pattern -( -Tempo.bpm=170; -Ppar([ - Pbind(\type, \om, \target, o.getPatch("/303",true), - \dur, 1/1, \octave,2,\degree, Pseq([Pshuf([0,2,4,6],16)],2)), - Pbind(\type, \om, \target, o.getPatch("/303",true), - \dur, Prand([Pshuf([1/2,1/4,1/4],4),Pshuf([1/3,1/3,1/6,1/6],4)],inf), - \legato, Prand((0.5,0.55..0.9),inf), - \octave,3,\degree, Pseq([Pshuf([0,2,4,6],2)],40)), - Pbind(\type, \om, \omcmd, \setPortValue, - \target, o.getPort("/303/cutoff",true), - \dur, 1/10, \portValue, Pseq((0,0.005..1).mirror,2)), - Pbind(\type, \om, \omcmd, \setPortValue, - \target, o.getPort("/303/resonance",true), \portValue, 1)]).play(quant:4) -) diff --git a/src/scripts/python/Makefile.am b/src/scripts/python/Makefile.am new file mode 100644 index 00000000..c6da5467 --- /dev/null +++ b/src/scripts/python/Makefile.am @@ -0,0 +1,4 @@ +SUBDIRS = scripts + +EXTRA_DIST = ingen.py ingenecho.py OSC.py + diff --git a/src/scripts/python/OSC.py b/src/scripts/python/OSC.py new file mode 100755 index 00000000..ea93dc52 --- /dev/null +++ b/src/scripts/python/OSC.py @@ -0,0 +1,373 @@ +#!/usr/bin/python +# +# Open SoundControl for Python +# Copyright (C) 2002 Daniel Holth, Clinton McChesney +# Modified by Leonard Ritter +# +# 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 library 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +# For questions regarding this module contact Daniel Holth +# or visit http://www.stetson.edu/~ProctoLogic/ + +import socket +import struct +import math +import sys +import string + + +def hexDump(bytes): + """Useful utility; prints the string in hexadecimal""" + for i in range(len(bytes)): + sys.stdout.write("%2x " % (ord(bytes[i]))) + if (i+1) % 8 == 0: + print repr(bytes[i-7:i+1]) + + if(len(bytes) % 8 != 0): + print string.rjust("", 11), repr(bytes[i-7:i+1]) + + +class OSCMessage: + """Builds typetagged OSC messages.""" + def __init__(self): + self.address = "" + self.typetags = "," + self.message = "" + + def setAddress(self, address): + self.address = address + + def setMessage(self, message): + self.message = message + + def setTypetags(self, typetags): + self.typetags = typetags + + def clear(self): + self.address = "" + self.clearData() + + def clearData(self): + self.typetags = "," + self.message = "" + + def append(self, argument, typehint = None): + """Appends data to the message, + updating the typetags based on + the argument's type. + If the argument is a blob (counted string) + pass in 'b' as typehint.""" + + if typehint == 'b': + binary = OSCBlob(argument) + else: + binary = OSCArgument(argument) + + self.typetags = self.typetags + binary[0] + self.rawAppend(binary[1]) + + def rawAppend(self, data): + """Appends raw data to the message. Use append().""" + self.message = self.message + data + + def getBinary(self): + """Returns the binary message (so far) with typetags.""" + address = OSCArgument(self.address)[1] + typetags = OSCArgument(self.typetags)[1] + return address + typetags + self.message + + def __repr__(self): + return self.getBinary() + +def readString(data): + length = string.find(data,"\0") + nextData = int(math.ceil((length+1) / 4.0) * 4) + return (data[0:length], data[nextData:]) + + +def readBlob(data): + length = struct.unpack(">i", data[0:4])[0] + nextData = int(math.ceil((length) / 4.0) * 4) + 4 + return (data[4:length+4], data[nextData:]) + + +def readInt(data): + if(len(data)<4): + print "Error: too few bytes for int", data, len(data) + rest = data + integer = 0 + else: + integer = struct.unpack(">i", data[0:4])[0] + rest = data[4:] + + return (integer, rest) + + + +def readLong(data): + """Tries to interpret the next 8 bytes of the data + as a 64-bit signed integer.""" + high, low = struct.unpack(">ll", data[0:8]) + big = (long(high) << 32) + low + rest = data[8:] + return (big, rest) + + + +def readFloat(data): + if(len(data)<4): + print "Error: too few bytes for float", data, len(data) + rest = data + float = 0 + else: + float = struct.unpack(">f", data[0:4])[0] + rest = data[4:] + + return (float, rest) + + +def OSCBlob(next): + """Convert a string into an OSC Blob, + returning a (typetag, data) tuple.""" + + if type(next) == type(""): + length = len(next) + padded = math.ceil((len(next)) / 4.0) * 4 + binary = struct.pack(">i%ds" % (padded), length, next) + tag = 'b' + else: + tag = '' + binary = '' + + return (tag, binary) + + +def OSCArgument(next): + """Convert some Python types to their + OSC binary representations, returning a + (typetag, data) tuple.""" + + if type(next) == type(""): + OSCstringLength = math.ceil((len(next)+1) / 4.0) * 4 + binary = struct.pack(">%ds" % (OSCstringLength), next) + tag = "s" + elif type(next) == type(42.5): + binary = struct.pack(">f", next) + tag = "f" + elif type(next) == type(13): + binary = struct.pack(">i", next) + tag = "i" + else: + binary = "" + tag = "" + + return (tag, binary) + + +def parseArgs(args): + """Given a list of strings, produces a list + where those strings have been parsed (where + possible) as floats or integers.""" + parsed = [] + for arg in args: + print arg + arg = arg.strip() + interpretation = None + try: + interpretation = float(arg) + if string.find(arg, ".") == -1: + interpretation = int(interpretation) + except: + # Oh - it was a string. + interpretation = arg + pass + parsed.append(interpretation) + return parsed + + + +def decodeOSC(data): + """Converts a typetagged OSC message to a Python list.""" + table = {"i":readInt, "f":readFloat, "s":readString, "b":readBlob} + decoded = [] + address, rest = readString(data) + typetags = "" + + if address == "#bundle": + time, rest = readLong(rest) + decoded.append(address) + decoded.append(time) + while len(rest)>0: + length, rest = readInt(rest) + decoded.append(decodeOSC(rest[:length])) + rest = rest[length:] + + elif len(rest)>0: + typetags, rest = readString(rest) + decoded.append(address) + decoded.append(typetags) + if(typetags[0] == ","): + for tag in typetags[1:]: + value, rest = table[tag](rest) + decoded.append(value) + else: + print "Oops, typetag lacks the magic ," + + # return only the data + return address,decoded[2:] + + +class CallbackManager: + """This utility class maps OSC addresses to callables. + + The CallbackManager calls its callbacks with a list + of decoded OSC arguments, including the address and + the typetags as the first two arguments.""" + + def __init__(self): + self.callbacks = {} + self.addCallback(self.unbundler, "#bundle") + + def handle(self, data, source = None): + """Given OSC data, tries to call the callback with the + right address.""" + decoded = decodeOSC(data) + self.dispatch(decoded) + + def dispatch(self, message): + """Sends decoded OSC data to an appropriate calback""" + try: + address = message[0] + self.callbacks[address](message) + except KeyError, e: + # address not found + print "no handler for '%s'" % address + pass + except None, e: + print "Exception in", address, "callback :", e + + return + + def addCallback(self, callback, name): + """Adds a callback to our set of callbacks, + or removes the callback with name if callback + is None.""" + if callback == None: + del self.callbacks[name] + else: + self.callbacks[name] = callback + + def unbundler(self, messages): + """Dispatch the messages in a decoded bundle.""" + # first two elements are #bundle and the time tag, rest are messages. + for message in messages[2:]: + self.dispatch(message) + + +if __name__ == "__main__": + hexDump("Welcome to the OSC testing program.") + print + message = OSCMessage() + message.setAddress("/foo/play") + message.append(44) + message.append(11) + message.append(4.5) + message.append("the white cliffs of dover") + hexDump(message.getBinary()) + + print "Making and unmaking a message.." + + strings = OSCMessage() + strings.append("Mary had a little lamb") + strings.append("its fleece was white as snow") + strings.append("and everywhere that Mary went,") + strings.append("the lamb was sure to go.") + strings.append(14.5) + strings.append(14.5) + strings.append(-400) + + raw = strings.getBinary() + + hexDump(raw) + + print "Retrieving arguments..." + data = raw + for i in range(6): + text, data = readString(data) + print text + + number, data = readFloat(data) + print number + + number, data = readFloat(data) + print number + + number, data = readInt(data) + print number + + hexDump(raw) + print decodeOSC(raw) + print decodeOSC(message.getBinary()) + + print "Testing Blob types." + + blob = OSCMessage() + blob.append("","b") + blob.append("b","b") + blob.append("bl","b") + blob.append("blo","b") + blob.append("blob","b") + blob.append("blobs","b") + blob.append(42) + + hexDump(blob.getBinary()) + + print decodeOSC(blob.getBinary()) + + def printingCallback(stuff): + sys.stdout.write("Got: ") + for i in stuff: + sys.stdout.write(str(i) + " ") + sys.stdout.write("\n") + + print "Testing the callback manager." + + c = CallbackManager() + c.add(printingCallback, "/print") + + c.handle(message.getBinary()) + message.setAddress("/print") + c.handle(message.getBinary()) + + print1 = OSCMessage() + print1.setAddress("/print") + print1.append("Hey man, that's cool.") + print1.append(42) + print1.append(3.1415926) + + c.handle(print1.getBinary()) + + bundle = OSCMessage() + bundle.setAddress("") + bundle.append("#bundle") + bundle.append(0) + bundle.append(0) + bundle.append(print1.getBinary(), 'b') + bundle.append(print1.getBinary(), 'b') + + bundlebinary = bundle.message + + print "sending a bundle to the callback manager" + c.handle(bundlebinary) diff --git a/src/scripts/python/ingen.py b/src/scripts/python/ingen.py new file mode 100644 index 00000000..07698719 --- /dev/null +++ b/src/scripts/python/ingen.py @@ -0,0 +1,636 @@ +#!/usr/bin/python +# +# Python bindings for Ingen +# Copyright (C) 2005 Leonard Ritter +# Copyright (C) 2006 Dave Robillard + +# 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 library 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import os,sys,thread,time + +from OSC import OSCMessage, decodeOSC + +from twisted.internet.protocol import DatagramProtocol +from twisted.internet import reactor + +INGEN_CALL_TIMEOUT = 5 +INGEN_CALL_POLLTIME = 0.1 + +class TreeElement: + def __init__(self,environment,parent,name): + self.environment = environment + self.parent = parent + self.name = name + self.metadata = {} + + def __del__(self): + print "'%s': gone" % self.name + + def removeChild(self, child): + pass + + def remove(self): + self.parent.removeChild(self.name) + self.parent = None + del self + + def getParent(self): + return self.parent + + def getName(self): + return self.name + + def getPath(self): + if self.parent: + return self.parent.getPath() + "/" + self.name + else: + return self.name + + def getDepth(self): + if self.parent: + return self.parent.getDepth() + 1 + else: + return 0 + + def setMetaData(self,key,value): + if (not self.metadata.has_key(value)) or (self.metadata[key] != value): + print "||| '%s': '%s' = '%s'" % (self.getPath(), key, value) + self.metadata[key] = value + +class Port(TreeElement): + def __init__(self,environment,parent,name): + TreeElement.__init__(self,environment,parent,name) + self.porttype = "" + self.direction = "" + self.hint = "" + self.defaultvalue = 0.0 + self.minvalue = 0.0 + self.maxvalue = 0.0 + self.value = 0.0 + self.connections = {} + print "*** '%s': new port" % self.getPath() + + def remove(self): + for connection in self.getConnections(): + connection.remove() + TreeElement.remove(self) + + def getConnections(self): + return self.connections + + def addConnection(self,target,connection): + self.connections[target] = connection + + def removeConnection(self,target): + del self.connections[target] + + def setPortType(self,porttype): + if self.porttype != porttype: + print "*** '%s': changing porttype from '%s' to '%s'" % (self.getPath(), self.porttype, porttype) + self.porttype = porttype + + def setDirection(self,direction): + if self.direction != direction: + print "*** '%s': changing direction from '%s' to '%s'" % (self.getPath(), self.direction, direction) + self.direction = direction + + def setHint(self,hint): + if self.hint != hint: + print "*** '%s': changing hint from '%s' to '%s'" % (self.getPath(), self.hint, hint) + self.hint = hint + + def setDefaultValue(self,defaultvalue): + if self.defaultvalue != defaultvalue: + print "*** '%s': changing defaultvalue from '%.3f' to '%.3f'" % (self.getPath(), self.defaultvalue, defaultvalue) + self.defaultvalue = defaultvalue + + def setMinValue(self,minvalue): + if self.minvalue != minvalue: + print "*** '%s': changing minvalue from '%.3f' to '%.3f'" % (self.getPath(), self.minvalue, minvalue) + self.minvalue = minvalue + + def setMaxValue(self,maxvalue): + if self.maxvalue != maxvalue: + print "*** '%s': changing maxvalue from '%.3f' to '%.3f'" % (self.getPath(), self.maxvalue, maxvalue) + self.maxvalue = maxvalue + + def setValue(self,value): + if self.value != value: + print "*** '%s': changing value from '%.3f' to '%.3f'" % (self.getPath(), self.value, value) + self.value = value + +class Node(TreeElement): + def __init__(self,environment,parent,name): + TreeElement.__init__(self,environment,parent,name) + self.ports = {} + self.polyphonic = 0 + self.plugintype = "" + self.libname = "" + self.pluginlabel = "" + print "+++ '%s': new node" % self.getPath() + + def remove(self): + for port in self.getPorts(): + port.remove() + TreeElement.remove(self) + + def removeChild(self, child): + del self.ports[child] + + def getPorts(self): + return self.ports.values() + + def setPluginLabel(self,pluginlabel): + if pluginlabel != self.pluginlabel: + print "+++ '%s': changing pluginlabel from '%s' to '%s'" % (self.getPath(), self.pluginlabel, pluginlabel) + self.pluginlabel = pluginlabel + + def setLibName(self,libname): + if libname != self.libname: + print "+++ '%s': changing libname from '%s' to '%s'" % (self.getPath(), self.libname, libname) + self.libname = libname + + def setPluginType(self,plugintype): + if plugintype != self.plugintype: + print "+++ '%s': changing plugintype from '%s' to '%s'" % (self.getPath(), self.plugintype, plugintype) + self.plugintype = plugintype + + def setPolyphonic(self,polyphonic): + if polyphonic != self.polyphonic: + print "+++ '%s': changing polyphony from %i to %i" % (self.getPath(), self.polyphonic, polyphonic) + self.polyphonic = polyphonic + + def hasPort(self,name): + return self.ports.has_key(name) + + def getPort(self,name,mustexist=False): + if not self.hasPort(name): + if mustexist: + return None + self.ports[name] = self.environment.getPortClass()(self.environment,self,name) + return self.ports[name] + +class Patch(Node): + def __init__(self,environment,parent,name): + Node.__init__(self,environment,parent,name) + self.nodes = {} + self.patches = {} + self.poly = 0 + self.enabled = False + print "### '%s': new patch" % self.getPath() + + def remove(self): + for patch in self.getPatches(): + patch.remove() + for node in self.getNodes(): + node.remove() + Node.remove(self) + + def removeChild(self, child): + if self.hasNode(child): + del self.nodes[child] + elif self.hasPatch(child): + del self.patches[child] + else: + Node.removeChild(self,child) + + def getPatches(self): + return self.patches.values() + + def getNodes(self): + return self.nodes.values() + + def getEnabled(self): + return self.enabled + + def setEnabled(self,enabled): + if enabled != self.enabled: + print "### '%s': changing enabled from %s to %s" % (self.getPath(), str(self.enabled), str(enabled)) + enabled = self.enabled + + def getPoly(self): + return self.poly + + def setPoly(self,poly): + if poly != self.poly: + print "### '%s': changing polyphony from %i to %i" % (self.getPath(), self.poly, poly) + self.poly = poly + + def hasNode(self,name): + return self.nodes.has_key(name) + + def getNode(self,name,mustexist=False): + if not self.hasNode(name): + if mustexist: + return None + self.nodes[name] = self.environment.getNodeClass()(self.environment,self,name) + return self.nodes[name] + + def hasPatch(self,name): + return self.patches.has_key(name) + + def getPatch(self,name,mustexist=False): + if not self.hasPatch(name): + if mustexist: + return None + self.patches[name] = self.environment.getPatchClass()(self.environment,self,name) + return self.patches[name] + +class Connection: + def __init__(self,environment,srcport,dstport): + self.environment = environment + self.srcport = srcport + self.dstport = dstport + self.srcport.addConnection(self.dstport,self) + self.dstport.addConnection(self.srcport,self) + print ">>> '%s'->'%s': new connection" % (self.srcport.getPath(),self.dstport.getPath()) + + def __del__(self): + print "connection gone" + + def remove(self): + self.srcport.removeConnection(self.dstport) + self.dstport.removeConnection(self.srcport) + del self + + def getSrcPort(self): + return self.srcport + + def getDstPort(self): + return self.dstport + + def getPortPair(self): + return self.srcport, self.dstport + +class Environment: + def __init__(self): + self.omPatch = self.getPatchClass()(self,None,"") + self.enabled = False + self.connections = {} + + def getConnectionClass(self): + return Connection + + def getPatchClass(self): + return Patch + + def getNodeClass(self): + return Node + + def getPortClass(self): + return Port + + def getConnection(self,srcportpath,dstportpath,mustexist=False): + srcport = self.getPort(srcportpath,True) + if not srcport: + return None + dstport = self.getPort(dstportpath,True) + if not dstport: + return None + if not self.connections.has_key((srcport,dstport)): + if mustexist: + return None + self.connections[(srcport,dstport)] = self.getConnectionClass()(self,srcport,dstport) + return self.connections[(srcport,dstport)] + + def getConnections(self): + return self.connections.values() + + def getPatch(self,path,mustexist=False): + elements = path.split("/") + currentPatch = None + for element in elements: + if element == "": + currentPatch = self.omPatch + else: + currentPatch = currentPatch.getPatch(element,mustexist) + if not currentPatch: + break + return currentPatch + + def getNode(self,path,mustexist=False): + elements = path.split("/") + basepath = "/".join(elements[:-1]) + nodename = elements[-1] + patch = self.getPatch(basepath,True) + if patch: + return patch.getNode(nodename,mustexist) + return None + + def getPort(self,path,mustexist=False): + elements = path.split("/") + basepath = "/".join(elements[:-1]) + portname = elements[-1] + node = self.getNode(basepath,True) + if node: + return node.getPort(portname,mustexist) + patch = self.getPatch(basepath,True) + if patch: + return patch.getPort(portname,mustexist) + return None + + def getObject(self,path): + patch = self.getPatch(path,True) + if patch: + return patch + node = self.getNode(path,True) + if node: + return node + return self.getPort(path,True) + + def printPatch(self,patch=None): + if not patch: + patch = self.omPatch + print patch.getDepth()*' ' + "### " + patch.getPath() + for node in patch.getNodes(): + print node.getDepth()*' ' + "+++ " + node.getPath() + for port in node.getPorts(): + print port.getDepth()*' ' + "*** " + port.getPath() + for port in patch.getPorts(): + print port.getDepth()*' ' + "*** " + port.getPath() + for subpatch in patch.getPatches(): + self.printPatch(subpatch) + + def printConnections(self): + for connection in self.getConnections(): + print ">>> %s -> %s" % (connection.getSrcPort().getPath(), connection.getDstPort().getPath()) + + #~ /ingen/engine_enabled - Notification engine's DSP has been enabled. + def __ingen__engine_enabled(self): + self.enabled = True + + #~ /ingen/engine_disabled - Notification engine's DSP has been disabled. + def __ingen__engine_disabled(self): + self.enabled = False + + #~ /ingen/new_node - Notification of a new node's creation. + #~ * path (string) - Path of the new node + #~ * polyphonic (integer-boolean) - Node is polyphonic (1 for yes, 0 for no) + #~ * type (string) - Type of plugin (LADSPA, DSSI, Internal, Patch) + #~ * lib-name (string) - Name of library if a plugin (ie cmt.so) + #~ * plug-label (string) - Label of plugin in library (ie adsr_env) + + #~ * New nodes are sent as a blob. The first message in the blob will be this one (/ingen/new_node), followed by a series of /om/new_port commands, followed by /om/new_node_end. + def __ingen__new_node(self,path,polyphonic,plugintype,libname,pluginlabel): + node = self.getNode(path) + node.setPolyphonic(polyphonic) + node.setPluginType(plugintype) + node.setLibName(libname) + node.setPluginLabel(pluginlabel) + + def __ingen__new_node_end(self): + pass + + #~ /ingen/node_removal - Notification of a node's destruction. + #~ * path (string) - Path of node (which no longer exists) + def __ingen__node_removal(self,path): + node = self.getNode(path) + node.remove() + + #~ /ingen/new_port - Notification of a node's destruction. + + #~ * path (string) - Path of new port + #~ * type (string) - Type of port (CONTROL or AUDIO) + #~ * direction (string) - Direction of data flow (INPUT or OUTPUT) + #~ * hint (string) - Hint (INTEGER, LOGARITHMIC, TOGGLE, or NONE) + #~ * default-value (float) - Default (initial) value + #~ * min-value (float) - Suggested minimum value + #~ * min-value (float) - Suggested maximum value + + #~ * 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 (ahem, Lachlan). + #~ * 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 ingenuity are not these ones! Those ranges are set as metadata. + def __ingen__new_port(self,path,porttype,direction,hint,defaultvalue,minvalue,maxvalue): + port = self.getPort(path) + port.setPortType(porttype) + port.setDirection(direction) + port.setHint(hint) + port.setDefaultValue(defaultvalue) + port.setMinValue(minvalue) + port.setMaxValue(maxvalue) + + #~ /ingen/port_removal - Notification of a port's destruction. + #~ * path (string) - Path of port (which no longer exists) + def __ingen__port_removal(self,path): + port = self.getPort(path) + port.remove() + + #~ /ingen/patch_destruction - Notification of a patch's destruction. + #~ * path (string) - Path of patch (which no longer exists) + def __ingen__patch_destruction(self,path): + patch = self.getPatch(path) + patch.remove() + + #~ /ingen/patch_enabled - Notification a patch's DSP processing has been enabled. + #~ * path (string) - Path of enabled patch + def __ingen__patch_enabled(self,path): + patch = self.getPatch(path) + patch.setEnabled(True) + + #~ /ingen/patch_disabled - Notification a patch's DSP processing has been disabled. + #~ * path (string) - Path of disabled patch + def __ingen__patch_disabled(self,path): + patch = self.getPatch(path) + patch.setEnabled(False) + + #~ /ingen/new_connection - Notification a new connection has been made. + #~ * src-path (string) - Path of the source port + #~ * dst-path (string) - Path of the destination port + def __ingen__new_connection(self,srcpath,dstpath): + self.getConnection(srcpath,dstpath) + + #~ /ingen/disconnection - Notification a connection has been unmade. + #~ * src-path (string) - Path of the source port + #~ * dst-path (string) - Path of the destination port + def __ingen__disconnection(self,srcpath,dstpath): + connection = self.getConnection(srcpath,dstpath) + portpair = connection.getPortPair() + connection.remove() + del self.connections[portpair] + + #~ /ingen/metadata/update - Notification of a piece of metadata. + #~ * path (string) - Path of the object associated with metadata (can be a node, patch, or port) + #~ * key (string) + #~ * value (string) + def __ingen__metadata__update(self,path,key,value): + object = self.getObject(path) + object.setMetaData(key,value) + + #~ /ingen/control_change - Notification the value of a port has changed + #~ * path (string) - Path of port + #~ * value (float) - New value of port + #~ * This will only send updates for values set by clients of course - not values changing because of connections to other ports! + def __ingen__control_change(self,path,value): + port = self.getPort(path) + port.setValue(value) + + #~ /ingen/new_patch - Notification of a new patch + #~ * path (string) - Path of new patch + #~ * poly (int) - Polyphony of new patch (not a boolean like new_node) + def __ingen__new_patch(self,path,poly): + patch = self.getPatch(path) + patch.setPoly(poly) + +class SocketError: + pass + +class Call: + pass + +class ClientProxy: + def __init__(self, om, name, is_async = False): + self.name = name + self.om = om + self.is_async = is_async + + def __call__(self, *args): + if (self.is_async): + self.om.sendMsg(self.name, *args) + return True + + result = self.om.sendMsgBlocking(self.name, *args) + if not result: + return None + if result[0] == "/ingen/response/ok": + return True + print "ERROR: %s" % result[1][1] + return False + + def __getattr__(self, name): + if (name[:2] == "__") and (name[-2:] == "__"): # special function + raise AttributeError, name + if name in self.__dict__: + raise AttributeError, name + if name == 'async': + return ClientProxy(self.om, self.name, True) + else: + return ClientProxy(self.om, self.name + '/' + name) + +class Client(DatagramProtocol, ClientProxy): + def __init__(self): + ClientProxy.__init__(self, self, "/ingen") + + def startProtocol(self): + self.transport.connect("127.0.0.1", 16180) + host = self.transport.getHost() + self.host = host.host + self.port = host.port + self.environment = None + self.handlers = {} + self.calls = {} + #print "opened port at %s" % (str((self.host,self.port))) + self.nextPacketNumber = 1 + + def setEnvironment(self, environment): + self.handlers = {} + self.environment = environment + for name in dir(self.environment): + element = getattr(self.environment,name) + if (type(element).__name__ == 'instancemethod') and (element.__name__[:6] == "__ingen__"): + handlername = element.__name__.replace("__","/") + print "registering handler for '%s'" % handlername + self.handlers[handlername] = element + + def getEnvironment(self): + return self.environment + + def connectionRefused(self): + print "Noone listening, aborting." + os._exit(-1) + + def messageReceived(self, (msg, args)): + if msg == "/ingen/error": + print "ERROR: %r" % args + return + if msg == "/ingen/response/ok": + omcall = self.calls[args[0]] + omcall.result = (msg,args) + omcall.done = True + return + if msg == "/ingen/response/error": + omcall = self.calls[args[0]] + omcall.result = (msg,args) + omcall.done = True + return + if msg == "#bundle": + for arg in args: + self.messageReceived(arg) + return + if self.handlers.has_key(msg): + try: + self.handlers[msg](*args) + except: + a,b,c = sys.exc_info() + sys.excepthook(a,b,c) + print "with '%s'" % repr((msg,args)) + return + print "no handler for '%s'" % repr((msg,args)) + + def datagramReceived(self, data, (host, port)): + self.messageReceived(decodeOSC(data)) + + def getPacketNumber(self): + packetNumber = self.nextPacketNumber + self.nextPacketNumber = (self.nextPacketNumber + 1) + return packetNumber + + def sendMsg(self,msg,*args): + packetNumber = self.getPacketNumber() + print "Sending %r (#%i)..." % (msg,packetNumber) + omcall = Call() + omcall.result = None + omcall.done = False + self.calls[packetNumber] = omcall + message = OSCMessage() + message.setAddress(msg) + message.append(packetNumber) + for arg in args: + message.append(arg) + self.transport.write(message.getBinary()) + time.sleep(0.01) + return True + + def sendMsgBlocking(self,msg,*args): + packetNumber = self.getPacketNumber() + print "Sending %r (#%i)..." % (msg,packetNumber) + omcall = Call() + omcall.result = None + omcall.done = False + self.calls[packetNumber] = omcall + message = OSCMessage() + message.setAddress(msg) + message.append(packetNumber) + for arg in args: + message.append(arg) + self.transport.write(message.getBinary()) + now = time.time() + while not omcall.done: + time.sleep(INGEN_CALL_POLLTIME) + distance = time.time() - now + if distance > INGEN_CALL_TIMEOUT: + print "timeout" + break + del self.calls[packetNumber] + return omcall.result + + def getAddressAsString(self): + return "osc.udp://%s:%d" % (self.host, self.port) + + + +def startClient(func): + om = Client() + reactor.listenUDP(0, om) + thread.start_new_thread(func,(om,)) + reactor.run() diff --git a/src/scripts/python/ingenecho.py b/src/scripts/python/ingenecho.py new file mode 100644 index 00000000..1dcfc313 --- /dev/null +++ b/src/scripts/python/ingenecho.py @@ -0,0 +1,40 @@ +#!/usr/bin/python +# +# Python bindings for Om +# Copyright (C) 2005 Leonard Ritter +# +# 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 library 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import ingen +import os,time,sys + +def main(om): + om.setEnvironment(ingen.Environment()) + om.engine.activate() + om.engine.register_client(om.getAddressAsString()) + om.request.all_objects(om.getAddressAsString()) + + om.request.all_objects() + time.sleep(3) + om.getEnvironment().printPatch() + om.getEnvironment().printConnections() + print "ingenecho will now monitor and mirror changes in the structure" + print "hit return to exit when youre done" + sys.stdin.readline() + om.engine.unregister_client(om.getAddressAsString()) + os._exit(0) + +if __name__ == "__main__": + ingen.startClient(main) diff --git a/src/scripts/python/scripts/Makefile.am b/src/scripts/python/scripts/Makefile.am new file mode 100644 index 00000000..3e070601 --- /dev/null +++ b/src/scripts/python/scripts/Makefile.am @@ -0,0 +1,2 @@ +EXTRA_DIST = sillysinepatch.py + diff --git a/src/scripts/python/scripts/flatten.py b/src/scripts/python/scripts/flatten.py new file mode 100755 index 00000000..530e4291 --- /dev/null +++ b/src/scripts/python/scripts/flatten.py @@ -0,0 +1,232 @@ +#!/usr/bin/python + +############################################################################### +# +# flatten.py - a python script that merges all subpatches in an Ingen patch +# into the parent patch +# +# Copyright (C) 2005 Lars Luthman +# +# This program 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. +# +# 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 General Public License for more 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +############################################################################### + +import ingen +import os,time,sys + + +def getPatchBounds(patch): + """Returns the smallest rectangle that contains all modules in the patch.""" + min_x = None + min_y = None + max_x = None + max_y = None + for node in patch.getNodes(): + x = node.metadata['module-x'] + y = node.metadata['module-y'] + if (x != None): + if (min_x == None or float(x) < min_x): + min_x = float(x) + if (max_x == None or float(x) > max_x): + max_x = float(x) + if (y != None): + if (min_y == None or float(y) < min_y): + min_y = float(y) + if (max_y == None or float(y) > max_y): + max_y = float(y) + if min_x == None: + min_x = 0 + max_x = 0 + if min_y == None: + min_y = 0 + max_y = 0 + + return (min_x, min_y, max_x, max_y) + + +def cloneNode(om, node, patch): + """Copy a node into a patch, return the new node's name.""" + + # create a node with a free name in the parent + names = [] + for node2 in patch.getNodes(): + names.append(node2.getName()) + for patch2 in patch.getPatches(): + names.append(patch2.getName()) + names.sort() + name = node.getName() + for name2 in names: + if name2 == name: + name = name + '_' + om.synth.create_node.async(patch.getPath() + '/' + name, + node.plugintype, node.libname, + node.pluginlabel, node.polyphonic) + + # copy port values + for port in node.getPorts(): + path = '%s/%s/%s' % (patch.getPath(), name, port.getName()) + om.synth.set_port_value_slow.async(path, port.value) + om.metadata.set.async(path, 'user-min', '%f' % port.minvalue) + om.metadata.set.async(path, 'user-max', '%f' % port.maxvalue) + return name + + +def flatten(om, patch): + """Merge all subpatches into the parent patch.""" + + # something is wrong, we don't have a patch + if patch == None: + return + + # iterate over all subpatches + for subpatch in patch.getPatches(): + flatten(om, subpatch) + lookup = {} + + # copy all nodes from the subpatch to the parent patch + for node in subpatch.getNodes(): + lookup[node.getName()] = cloneNode(om, node, patch) + + # copy all connections + for node in subpatch.getNodes(): + for port in node.getPorts(): + if port.direction == 'OUTPUT': + for target in port.getConnections().keys(): + targetname = '%s/%s' % (lookup[target.getParent().getName()], target.getName()) + om.synth.connect.async(patch.getPath() + '/' + + lookup[node.getName()] + '/' + + port.getName(), + patch.getPath() + '/' + + targetname) + + # make external connections + for node in subpatch.getNodes(): + if node.libname == '': + lbl = node.pluginlabel + + if lbl == 'audio_input' or lbl == 'control_input': + port1 = node.getPort('in') + for port2 in port1.getConnections().keys(): + dst = '%s/%s/%s' % (patch.getPath(), lookup[port2.getParent().getName()], port2.getName()) + port4 = subpatch.getPort(node.getName()) + conns = port4.getConnections().keys() + if len(conns) == 0: + portValue = port4.value + om.synth.set_port_value_slow.async(dst, portValue) + else: + for port3 in port4.getConnections().keys(): + src = port3.getPath() + om.synth.connect.async(src, dst) + + if lbl == 'audio_output' or lbl == 'control_output': + port2 = node.getPort('out', True) + for port1 in port2.getConnections().keys(): + src = '%s/%s/%s' % (patch.getPath(), lookup[port1.getParent().getName()], port1.getName()) + port3 = subpatch.getPort(node.getName()) + for port4 in port3.getConnections().keys(): + dst = port4.getPath() + om.synth.connect.async(src, dst) + + # destroy all input and output nodes from the subpatch + for node in subpatch.getNodes(): + if node.libname == '': + lbl = node.pluginlabel + if (lbl == 'audio_input' or lbl == 'control_input' or + lbl == 'audio_output' or lbl == 'control_output'): + om.synth.destroy_node.async('%s/%s' % (patch.getPath(), + lookup[node.getName()])) + + # calculate where to move all the new nodes + (min_x, min_y, max_x, max_y) = getPatchBounds(subpatch) + sub_x = subpatch.metadata['module-x'] + if sub_x == None: + sub_x = 0 + sub_y = subpatch.metadata['module-y'] + if sub_y == None: + sub_y = 0 + x_offset = float(sub_x) + if min_x != None: + x_offset = float(sub_x) - min_x + y_offset = float(sub_y) + if min_y != None: + y_offset = float(sub_y) - min_y + + # move the new nodes + for node in subpatch.getNodes(): + x = float(node.metadata['module-x']) + if x == None: + x = 0 + om.metadata.set.async('%s/%s' % (patch.getPath(), + lookup[node.getName()]), + 'module-x', '%f' % (x + x_offset)) + y = float(node.metadata['module-y']) + if y == None: + y = 0 + om.metadata.set.async('%s/%s' % (patch.getPath(), + lookup[node.getName()]), + 'module-y', '%f' % (y + y_offset)) + + # move the old nodes in the patch + x_offset = 0 + if min_x != None and max_x != None: + x_offset = max_x - min_x + y_offset = 0 + if min_y != None and max_y != None: + y_offset = max_y - min_y + for node in patch.getNodes(): + if node.getName() not in lookup.values(): + x = node.metadata['module-x'] + if x != None and float(x) > float(sub_x): + om.metadata.set.async(node.getPath(), 'module-x', + '%f' % (float(x) + x_offset)) + y = node.metadata['module-y'] + if y != None and float(y) > float(sub_y): + om.metadata.set.async(node.getPath(), 'module-y', + '%f' % (float(y) + y_offset)) + # destroy the subpatch + om.synth.destroy_patch(subpatch.getPath()) + + +def main(om): + om.setEnvironment(ingen.Environment()) + om.engine.activate.async() + om.engine.load_plugins.async() + om.request.all_objects(om.getAddressAsString()) + + # wait for all the data to arrive (there should be a cleaner way) + time.sleep(3) + + patch = om.getEnvironment().getPatch(sys.argv[1], True); + flatten(om, patch) + + os._exit(0) + + +if len(sys.argv) > 1: + if sys.argv[1] == "--name": + print "Flatten patch" + os._exit(0) + elif sys.argv[1] == "--shortdesc": + print "Merge the contents of all subpatches into the parent patch"; + os._exit(0) + elif sys.argv[1] == "--signature": + print "%p"; + os._exit(0) + else: + ingen.startClient(main) + +else: + print "Which patch do you want to flatten?" + os._exit(0) diff --git a/src/scripts/python/scripts/sillysinepatch.py b/src/scripts/python/scripts/sillysinepatch.py new file mode 100644 index 00000000..dcb37a97 --- /dev/null +++ b/src/scripts/python/scripts/sillysinepatch.py @@ -0,0 +1,41 @@ +#!/usr/bin/python +# +# Python bindings for Om +# Copyright (C) 2005 Leonard Ritter +# +# 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 library 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import ingen +import os,time,sys + +def main(om): + om.setEnvironment(ingen.Environment()) + om.engine.activate() + om.engine.load_plugins() + om.engine.register_client(om.getAddressAsString()) + om.request.all_objects(om.getAddressAsString()) + om.synth.create_patch("/silly_sine", 1) + om.synth.create_node("/silly_sine/output", "Internal", "", "audio_output", 0) + om.synth.create_node("/silly_sine/sine", "LADSPA", "cmt.so", "sine_fcac", 0) + om.synth.set_port_value("/silly_sine/sine/Frequency", 440.0) + om.synth.set_port_value("/silly_sine/sine/Amplitude", 1.0) + om.synth.connect("/silly_sine/sine/Output", "/silly_sine/output/out") + om.synth.enable_patch("/silly_sine") + om.engine.enable() + om.engine.unregister_client(om.getAddressAsString()) + os._exit(0) + +if __name__ == "__main__": + ingen.startClient(main) diff --git a/src/scripts/supercollider/Ingen.sc b/src/scripts/supercollider/Ingen.sc new file mode 100644 index 00000000..873c8c2b --- /dev/null +++ b/src/scripts/supercollider/Ingen.sc @@ -0,0 +1,746 @@ +// TODO: +// * Keep track of established connections. +Ingen : Model { + classvar <>program = "om", <>patchLoader = "om_patch_loader"; + classvar <>oscURL, uiClass; + var loadIntoJack = true; + var allocator, requestResponders, requestHandlers, notificationResponders; + var creatingNode, newNodeEnd; + var IngenInternalNode, + \LADSPA -> IngenLADSPANode, + \DSSI -> IngenDSSINode + ]; + uiClass = IngenEmacsUI + } + *new { | netaddr | + ^super.new.init(netaddr) + } + gui { ^uiClass.new(this) } + init { |netaddr| + addr = netaddr ? NetAddr("127.0.0.1", 16180); + onNewPatch = IdentityDictionary.new; + allocator = StackNumberAllocator(0,1024); + requestHandlers = IdentityDictionary.new; + requestResponders = [ + "response/ok" -> {|id| + requestHandlers.removeAt(id).value; allocator.free(id) }, + "response/error" -> {|id,text| + requestHandlers.removeAt(id); + allocator.free(id); + ("Ingen"+text).error } + ].collect({|a| + var func = a.value; + OSCresponder(addr, "/om/"++a.key, {|time,resp,msg| + func.value(*msg[1..]) + }) + }); + notificationResponders = [ + "new_patch" -> {|path,poly| + var func = onNewPatch.removeAt(path); + if (func.notNil) { + func.value(this.getPatch(path,false).prSetPoly(poly)) + } + }, + "metadata/update" -> {|path,key,value| + this.getObject(path).metadata.prSetMetadata(key, value) }, + "new_node" -> {|path,poly,type,lib,label| + var patchPath, nodeName, patch, node; + var lastSlash = path.asString.inject(nil,{|last,char,i| + if(char==$/,i,last) + }); + if (lastSlash.notNil) { + patchPath = path.asString.copyFromStart(lastSlash-1); + nodeName = path.asString.copyToEnd(lastSlash+1); + patch = this.getPatch(patchPath); + if (patch.notNil) { + if (patch.hasNode(nodeName).not) { + node = nodeTypeMap[type].new + (nodeName, patch, poly, label, lib); + creatingNode = node; + patch.nodes[nodeName.asSymbol] = node; + patch.changed(\newNode, node); + } { + if (patch.getNode(nodeName).class != nodeTypeMap[type]) { + ("Ingen sent an existng node with differing type"+path).warn + } + } + } { + ("Ingen tried to create node in non-existing patch"+patchPath).warn + } + } { + ("Invalid path in node creation"+path).warn + } + }, + "new_node_end" -> { + newNodeEnd.value(creatingNode); + newNodeEnd = nil; + creatingNode = nil }, + "new_port" -> {|path,type,dir,hint,def,min,max| + var basePath, portName, parent, port; + var lastSlash = path.asString.inject(nil,{|last,char,i| + if(char==$/,i,last) + }); + if (lastSlash.notNil) { + basePath = path.asString.copyFromStart(lastSlash-1); + portName = path.asString.copyToEnd(lastSlash+1); + parent = this.getNode(basePath) ? this.getPatch(basePath); + if (parent.notNil) { + if (parent.hasPort(portName).not) { + port = IngenPort.new(portName, parent, type, dir, hint, def, min, max); + parent.ports[portName.asSymbol] = port; + parent.changed(\newPort, port) + } { + if (parent.getPort(portName).porttype != type) { + ("Ingen tried to create an already existing port with differing type" + +path).warn + } + } + } { + ("Ingen tried to create port on non-existing object"+basePath).warn + } + } { + ("Invalid path in port creation"+path).warn + } + }, + "control_change" -> {|path,value| + this.getPort(path).prSetValue(value) }, + "patch_enabled" -> {|path| this.getPatch(path).prSetEnabled(true) }, + "patch_disabled" -> {|path| this.getPatch(path).prSetEnabled(false) }, + "plugin" -> {|lib,label,name,type| + plugins.add(Event.new(4,nil,pluginParentEvent).putAll( + (type:type, lib:lib, label:label, name:name))) }, + "node_removal" -> {|path| + var node = this.getNode(path); + if (node.notNil) { + node.parent.nodes.removeAt(node.name.asSymbol).free + } { + ("Ingen attempting to remove non-existing node"+path).warn + } + }, + "port_removal" -> {|path| + var port = this.getPort(path); + if (port.notNil) { + port.parent.ports.removeAt(port.name.asSymbol).free + } { + ("Ingen attempting to remove non-existing port"+path).warn + } + }, + "patch_destruction" -> {|path| + var patch = this.getPatch(path); + if (patch.notNil) { + patch.parent.patches.removeAt(patch.name.asSymbol).free + } { + ("Ingen attempting to remove non-existing patch"+path).warn + } + }, + "program_add" -> {|path,bank,program,name| + var node = this.getNode(path); + if (node.respondsTo(\prProgramAdd)) { + node.prProgramAdd(bank,program,name) + } { + ("Ingen tried to add program info to"+node).warn + } + } + ].collect({|a| + var func = a.value; + OSCresponder(addr, "/om/"++a.key, {|time,resp,msg| + func.value(*msg[1..]) + }) + }); + pluginParentEvent = Event.new(2,nil,nil).putAll(( + engine:this, + new:{|self,path,poly=1,handler|self.engine.createNode(path?("/"++self.name),self.type,self.lib,self.label,poly,created:handler)} + )); + } + *waitForBoot {|func| ^this.new.waitForBoot(func) } + waitForBoot {|func| + var r, id = 727; + requestHandlers[id] = { + r.stop; + booting=false; + this.changed(\running, true); + func.value(this) + }; + if (booting.not) {this.boot}; + r = Routine.run { + 50.do { + 0.1.wait; + addr.sendMsg("/om/ping", id) + }; + requestHandlers.removeAt(id); + "Ingen engine boot failed".error; + } + } + getPatch {|path, mustExist=true| + var elements, currentPatch; + if (path.class == Array) { elements = path + } { elements = path.asString.split($/) }; + elements.do{|elem| + if (elem=="") { + currentPatch = root + } { + currentPatch = currentPatch.getPatch(elem,mustExist); + if (currentPatch.isNil) { ^nil } + } + }; + ^currentPatch; + } + getNode {|path| + var basePath, nodeName, patch; + if (path.class == Array) { basePath = path + } { basePath = path.asString.split($/) }; + nodeName = basePath.pop; + patch = this.getPatch(basePath,true); + if (patch.notNil) { + ^patch.getNode(nodeName) + }; + ^nil + } + getPort {|path| + var basePath, portName, node, patch; + basePath = path.asString.split($/); + portName = basePath.pop; + node = this.getNode(basePath.copy); + if (node.notNil) { ^node.getPort(portName) }; + patch = this.getPatch(basePath,true); + if (patch.notNil) { ^patch.getPort(portName) }; + ^nil + } + getObject {|path| + var patch,node,port; + patch = this.getPatch(path,true); + if (patch.notNil) { ^patch }; + node = this.getNode(path); + if (node.notNil) { ^node }; + port = this.getPort(path,true); + if (port.notNil) { ^port }; + ^nil + } + at {|path|^this.getObject(path.asString)} + *boot {|func| + ^Ingen.new.waitForBoot {|e| + e.activate { + e.register { + e.loadPlugins { + e.requestPlugins { + e.requestAllObjects { + func.value(e) + } + } + } + } + } + } + } + boot { + requestResponders.do({|resp| resp.add}); + booting = true; + if (addr.addr == 2130706433) { + if (loadIntoJack) { + ("jack_load"+"-i"+addr.port+"Ingen"+"om").unixCmd + } { + (program+"-p"+addr.port).unixCmd + } + } { + "You have to manually boot Ingen now".postln + } + } + loadPatch {|patchPath| (patchLoader + patchPath).unixCmd } + activate { | handler | + this.sendReq("engine/activate", { + root = IngenPatch("",nil,this); + this.changed(\newPatch, root); + handler.value + }) + } + register { | handler | + this.sendReq("engine/register_client", { + registered=true; + notificationResponders.do({|resp| resp.add}); + this.changed(\registered, registered); + handler.value(this) + }) + } + unregister { | handler | + this.sendReq("engine/unregister_client", { + registered=false; + notificationResponders.do({|resp| resp.remove}); + this.changed(\registered, registered); + handler.value(this) + }) + } + registered_ {|flag| + if (flag and: registered.not) { + this.register + } { + if (flag.not and: registered) { + this.unregister + } + } + } + loadPlugins { | handler | this.sendReq("engine/load_plugins", handler) } + requestPlugins {|handler| + var startTime = Main.elapsedTime; + plugins = Set.new; + this.sendReq("request/plugins", { + ("Received info about"+plugins.size+"plugins in"+(Main.elapsedTime-startTime)+"seconds").postln; + this.changed(\plugins, plugins); + handler.value(this); + }) + } + requestAllObjects { |handler| + this.sendReq("request/all_objects", handler) + } + createPatch { | path, poly=1, handler | + onNewPatch[path.asSymbol] = handler; + this.sendReq("synth/create_patch", nil, path.asString, poly.asInteger) + } + createNode { | path, type='LADSPA', lib, label, poly=1, created, handler | + newNodeEnd = created; + this.sendReq("synth/create_node",handler,path,type,lib,label,poly) + } + createAudioInput { | path, handler | + this.createNode(path,"Internal","","audio_input",0,handler) + } + createAudioOutput {|path,handler| + this.createNode(path,"Internal","","audio_output",0,handler) + } + createMIDIInput {|path,handler| + this.createNode(path,"Internal","","midi_input",1,handler) + } + createMIDIOutput {|path,handler| + this.createNode(path,"Internal","","midi_output",1,handler) + } + createNoteIn {|path| this.createNode(path,"Internal","","note_in") } + connect {|fromPath,toPath,handler| + this.sendReq("synth/connect",handler,fromPath.asString,toPath.asString) + } + disconnect { | fromPath, toPath, handler | + this.sendReq("synth/disconnect",handler,fromPath.asString,toPath.asString) + } + disconnectAll { | path, handler | + this.sendReq("synth/disconnect_all",handler,path); + } + sendReq { | path, handler...args | + var id = allocator.alloc; + requestHandlers[id] = handler; + addr.sendMsg("/om/"++path, id, *args) + } + quit { + if (loadIntoJack) { + ("jack_unload"+"Ingen").unixCmd; + booting=false; + requestResponders.do(_.remove); + notificationResponders.do(_.remove); + this.changed(\running, false); + } { + this.sendReq("engine/quit", { + booting=false; + requestResponders.do(_.remove); + notificationResponders.do(_.remove); + this.changed(\running, false); + }) + } + } + ping {| n=1, func | + var id, result, start; + id = allocator.alloc; + result = 0; + requestHandlers[id] = { + var end; + end = Main.elapsedTime; + result=max((end-start).postln,result); + n=n-1; + if (n > 0) { + start = Main.elapsedTime; + addr.sendMsg("/om/ping", id) + } { + allocator.free(id); + func.value(result) + } + }; + start = Main.elapsedTime; + addr.sendMsg("/om/ping", id) + } + setPortValue {|path, val| this.getPort(path.asString).value=val } + jackConnect {|path, jackPort| + this.getPort(path).jackConnect(jackPort) + } + noteOn {|path, note, vel| + var patch,node; + patch = this.getPatch(path,true); + if (patch.notNil) { patch.noteOn(note,vel) }; + node = this.getNode(path); + if (node.notNil) { node.noteOn(note,vel) }; + } + noteOff {|path, note| + var patch,node; + patch = this.getPatch(path,true); + if (patch.notNil) { patch.noteOff(note) }; + node = this.getNode(path); + if (node.notNil) { node.noteOff(note) }; + } + matchPlugins{ | label, lib, name, type | + ^plugins.select{ |p| + label.matchItem(p.label) and: { + lib.matchItem(p.lib) and: { + name.matchItem(p.name) and: { + type.matchItem(p.type) + } + } + } + } + } + dssiMsg {|path,reqType="program" ...args| + addr.sendMsg("/dssi"++path++$/++reqType,*args) + } +} + +IngenMetadata { + var object, dict; + *new {|obj|^super.new.metadataInit(obj)} + metadataInit {|obj| + dict=Dictionary.new; + object=obj + } + put {|key,val| + object.engine.sendReq("metadata/set", nil, + object.path, key.asString, val.asString) + } + at {|key|^dict.at(key.asSymbol)} + prSetMetadata {|key,val| + dict.put(key,val); + object.changed(\metadata, key, val) + } +} + +IngenObject : Model { + var diff --git a/src/scripts/supercollider/example.sc b/src/scripts/supercollider/example.sc new file mode 100644 index 00000000..80a72a7b --- /dev/null +++ b/src/scripts/supercollider/example.sc @@ -0,0 +1,27 @@ +// Boot Ingen (inside SC) and the scsynth server +( +o=Ingen.boot { + o.loadPatch("/home/mlang/src/om-synth/src/clients/patches/303.om"); + s.boot; +} +) +// Connect patch output to one SC input +o.jackConnect("/303/output", "SuperCollider:in_3"); +// Play this input on the default output bus (most simple postprocessor) +{AudioIn.ar(3).dup}.play; +// A simple 303 pattern +( +Tempo.bpm=170; +Ppar([ + Pbind(\type, \om, \target, o.getPatch("/303",true), + \dur, 1/1, \octave,2,\degree, Pseq([Pshuf([0,2,4,6],16)],2)), + Pbind(\type, \om, \target, o.getPatch("/303",true), + \dur, Prand([Pshuf([1/2,1/4,1/4],4),Pshuf([1/3,1/3,1/6,1/6],4)],inf), + \legato, Prand((0.5,0.55..0.9),inf), + \octave,3,\degree, Pseq([Pshuf([0,2,4,6],2)],40)), + Pbind(\type, \om, \omcmd, \setPortValue, + \target, o.getPort("/303/cutoff",true), + \dur, 1/10, \portValue, Pseq((0,0.005..1).mirror,2)), + Pbind(\type, \om, \omcmd, \setPortValue, + \target, o.getPort("/303/resonance",true), \portValue, 1)]).play(quant:4) +) diff --git a/src/serialisation/Makefile.am b/src/serialisation/Makefile.am new file mode 100644 index 00000000..57e99055 --- /dev/null +++ b/src/serialisation/Makefile.am @@ -0,0 +1,24 @@ +MAINTAINERCLEANFILES = Makefile.in + +moduledir = $(libdir)/ingen + +module_LTLIBRARIES = libingen_serialisation.la + +libingen_serialisation_la_CXXFLAGS = \ + @INGEN_CFLAGS@ \ + @REDLANDMM_CFLAGS@ \ + @RAUL_CFLAGS@ \ + @GLIBMM_CFLAGS@ \ + @SLV2_CFLAGS@ + +libingen_serialisation_la_LDFLAGS = -no-undefined -module -avoid-version +libingen_serialisation_la_LIBADD = @RAUL_LIBS@ @REDLANDMM_LIBS@ @GLIBMM_LIBS@ @SLV2_LIBS@ + +libingen_serialisation_la_SOURCES = \ + Parser.cpp \ + Parser.hpp \ + Serialiser.cpp \ + Serialiser.hpp \ + serialisation.cpp \ + serialisation.hpp + diff --git a/src/serialisation/Parser.cpp b/src/serialisation/Parser.cpp new file mode 100644 index 00000000..aed49a38 --- /dev/null +++ b/src/serialisation/Parser.cpp @@ -0,0 +1,587 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "interface/EngineInterface.hpp" +#include "Parser.hpp" + +using namespace std; +using namespace Raul; +using namespace Ingen::Shared; + +namespace Ingen { +namespace Serialisation { + +#define NS_INGEN "http://drobilla.net/ns/ingen#" + + +Glib::ustring +Parser::uri_relative_to_base(Glib::ustring base, const Glib::ustring uri) +{ + base = base.substr(0, base.find_last_of("/")+1); + Glib::ustring ret; + if (uri.length() > base.length() && uri.substr(0, base.length()) == base) + ret = uri.substr(base.length()); + else + ret = uri; + return ret; +} + + +/** Parse a patch from RDF into a CommonInterface (engine or client). + * + * @param document_uri URI of file to load objects from. + * @param parent Path of parent under which to load objects. + * @return whether or not load was successful. + */ +bool +Parser::parse_document( + Ingen::Shared::World* world, + Ingen::Shared::CommonInterface* target, + const Glib::ustring& document_uri, + Glib::ustring object_uri, + Glib::ustring engine_base, + boost::optional symbol, + boost::optional data) +{ + Redland::Model model(*world->rdf_world, document_uri, document_uri); + + if (object_uri == document_uri || object_uri == "") + cout << "Parsing document " << object_uri << " (base " << document_uri << ")" << endl; + else + cout << "Parsing " << object_uri << " from " << document_uri << endl; + + return parse(world, target, model, document_uri, engine_base, object_uri, symbol, data);; +} + + +bool +Parser::parse_string( + Ingen::Shared::World* world, + Ingen::Shared::CommonInterface* target, + const Glib::ustring& str, + const Glib::ustring& base_uri, + Glib::ustring engine_base, + boost::optional object_uri, + boost::optional symbol, + boost::optional data) +{ + Redland::Model model(*world->rdf_world, str.c_str(), str.length(), base_uri); + + if (object_uri) + cout << "Parsing " << object_uri.get() << " (base " << base_uri << ")" << endl; + else + cout << "Parsing all objects found in string (base " << base_uri << ")" << endl; + + bool ret = parse(world, target, model, base_uri, engine_base, object_uri, symbol, data); + if (ret) { + const Glib::ustring subject = Glib::ustring("<") + base_uri + Glib::ustring(">"); + parse_connections(world, target, model, base_uri, subject, + Path((engine_base == "") ? "/" : engine_base)); + } + + return ret; +} + + +bool +Parser::parse( + Ingen::Shared::World* world, + Ingen::Shared::CommonInterface* target, + Redland::Model& model, + Glib::ustring base_uri, + Glib::ustring engine_base, + boost::optional object_uri, + boost::optional symbol, + boost::optional data) +{ + const Redland::Node::Type res = Redland::Node::RESOURCE; + Glib::ustring query_str; + if (object_uri && object_uri.get()[0] == '/') + object_uri = object_uri.get().substr(1); + + if (object_uri) + query_str = Glib::ustring("SELECT DISTINCT ?class WHERE { <") + object_uri.get() + "> a ?class . }"; + else + query_str = Glib::ustring("SELECT DISTINCT ?subject ?class WHERE { ?subject a ?class . }"); + + Redland::Query query(*world->rdf_world, query_str); + Redland::Query::Results results = query.run(*world->rdf_world, model, base_uri); + + const Redland::Node patch_class(*world->rdf_world, res, NS_INGEN "Patch"); + const Redland::Node node_class(*world->rdf_world, res, NS_INGEN "Node"); + const Redland::Node in_port_class(*world->rdf_world, res, NS_INGEN "InputPort"); + const Redland::Node out_port_class(*world->rdf_world, res, NS_INGEN "OutputPort"); + + string subject_str = ((object_uri && object_uri.get() != "") ? object_uri.get() : base_uri); + if (subject_str[0] == '/') + subject_str = subject_str.substr(1); + if (subject_str == "") + subject_str = base_uri; + + const Redland::Node subject_uri(*world->rdf_world, res, subject_str); + + bool ret = false; + + for (Redland::Query::Results::iterator i = results.begin(); i != results.end(); ++i) { + const Redland::Node& subject = (object_uri ? subject_uri : (*i)["subject"]); + const Redland::Node& rdf_class = (*i)["class"]; + if (!object_uri) { + std::string path_str = uri_relative_to_base(base_uri, subject.to_c_string()); + if (path_str[0] != '/') + path_str = string("/").append(path_str); + if (Path(path_str).parent() != "/") + continue; + } + + if (rdf_class == patch_class || rdf_class == node_class || + rdf_class == in_port_class || rdf_class == out_port_class) { + Raul::Path path("/"); + if (base_uri != subject.to_c_string()) { + string path_str = (string)uri_relative_to_base(base_uri, subject.to_c_string()); + if (path_str[0] != '/') + path_str = string("/").append(path_str); + if (Path::is_valid(path_str)) { + path = path_str; + } else { + cerr << "[Parser] ERROR: Invalid path '" << path << "'" << endl; + continue; + } + } + + if (path.parent() != "/") + continue; + + if (rdf_class == patch_class) { + ret = parse_patch(world, target, model, base_uri, engine_base, + subject.to_c_string(), data); + if (ret) + target->set_variable(path, "ingen:document", Atom(base_uri.c_str())); + } else if (rdf_class == node_class) { + ret = parse_node(world, target, model, + base_uri, Glib::ustring("<") + subject.to_c_string() + ">", path, data); + } else if (rdf_class == in_port_class || rdf_class == out_port_class) { + ret = parse_port(world, target, model, + base_uri, Glib::ustring("<") + subject.to_c_string() + ">", path, data); + } + if (ret == false) { + cerr << "Failed to parse object " << object_uri << endl; + return ret; + } + } + + } + + return ret; +} + + +bool +Parser::parse_patch( + Ingen::Shared::World* world, + Ingen::Shared::CommonInterface* target, + Redland::Model& model, + const Glib::ustring& base_uri, + Glib::ustring engine_base, + const Glib::ustring& object_uri, + boost::optional data=boost::optional()) +{ + std::set created; + uint32_t patch_poly = 0; + + /* Use parameter overridden polyphony, if given */ + if (data) { + GraphObject::Variables::iterator poly_param = data.get().find("ingen:polyphony"); + if (poly_param != data.get().end() && poly_param->second.type() == Atom::INT) + patch_poly = poly_param->second.get_int32(); + } + + Glib::ustring subject = ((object_uri[0] == '<') + ? object_uri : Glib::ustring("<") + object_uri + ">"); + + if (subject[0] == '<' && subject[1] == '/') + subject = string("<").append(subject.substr(2)); + + //cout << "**** LOADING PATCH URI " << object_uri << ", SUBJ " << subject + // << ", ENG BASE " << engine_base << endl; + + /* Get polyphony from file (mandatory if not specified in parameters) */ + if (patch_poly == 0) { + Redland::Query query(*world->rdf_world, Glib::ustring( + "SELECT DISTINCT ?poly WHERE { ") + subject + " ingen:polyphony ?poly\n }"); + + Redland::Query::Results results = query.run(*world->rdf_world, model, base_uri); + + if (results.size() == 0) { + cerr << "[Parser] ERROR: No polyphony found!" << endl; + cerr << "Query was:" << endl << query.string() << endl; + return false; + } + + const Redland::Node& poly_node = (*results.begin())["poly"]; + assert(poly_node.is_int()); + patch_poly = static_cast(poly_node.to_int()); + } + + string symbol = uri_relative_to_base(base_uri, object_uri); + symbol = symbol.substr(0, symbol.find(".")); + Path patch_path("/"); + if (engine_base == "") + patch_path = "/"; + else if (engine_base[engine_base.length()-1] == '/') + patch_path = Path(engine_base + symbol); + else if (Path::is_valid(engine_base)) + patch_path = (Path)engine_base; + else if (Path::is_valid(string("/").append(engine_base))) + patch_path = (Path)(string("/").append(engine_base)); + else + cerr << "WARNING: Illegal engine base path '" << engine_base << "', loading patch to root" << endl; + + //if (patch_path != "/") + target->new_patch(patch_path, patch_poly); + + /* Plugin nodes */ + Redland::Query query(*world->rdf_world, Glib::ustring( + "SELECT DISTINCT ?name ?plugin ?varkey ?varval ?poly WHERE {\n") + + subject + " ingen:node ?node .\n" + "?node lv2:symbol ?name ;\n" + " ingen:plugin ?plugin ;\n" + " ingen:polyphonic ?poly .\n" + "OPTIONAL { ?node lv2var:variable ?variable .\n" + " ?variable rdf:predicate ?varkey ;\n" + " rdf:value ?varval .\n" + " }" + "}"); + + Redland::Query::Results results = query.run(*world->rdf_world, model, base_uri); + world->rdf_world->mutex().lock(); + for (Redland::Query::Results::iterator i = results.begin(); i != results.end(); ++i) { + const string node_name = (*i)["name"].to_string(); + const Path node_path = patch_path.base() + node_name; + + if (created.find(node_path) == created.end()) { + const string node_plugin = (*i)["plugin"].to_string(); + bool node_polyphonic = false; + + const Redland::Node& poly_node = (*i)["poly"]; + if (poly_node.is_bool() && poly_node.to_bool() == true) + node_polyphonic = true; + + target->new_node(node_path, node_plugin); + target->set_property(node_path, "ingen:polyphonic", node_polyphonic); + created.insert(node_path); + } + + const string key = world->rdf_world->prefixes().qualify((*i)["varkey"].to_string()); + const Redland::Node& val_node = (*i)["varval"]; + + if (key != "") + target->set_variable(node_path, key, AtomRDF::node_to_atom(val_node)); + } + world->rdf_world->mutex().unlock(); + + + /* Load subpatches */ + query = Redland::Query(*world->rdf_world, Glib::ustring( + "SELECT DISTINCT ?subpatch ?symbol WHERE {\n") + + subject + " ingen:node ?subpatch .\n" + "?subpatch a ingen:Patch ;\n" + " lv2:symbol ?symbol .\n" + "}"); + + results = query.run(*world->rdf_world, model, base_uri); + for (Redland::Query::Results::iterator i = results.begin(); i != results.end(); ++i) { + const string symbol = (*i)["symbol"].to_string(); + const string subpatch = (*i)["subpatch"].to_string(); + + const Path subpatch_path = patch_path.base() + symbol; + + if (created.find(subpatch_path) == created.end()) { + string subpatch_rel = uri_relative_to_base(base_uri, subpatch); + string sub_base = engine_base; + if (sub_base[sub_base.length()-1] == '/') + sub_base = sub_base.substr(sub_base.length()-1); + sub_base.append("/").append(symbol); + created.insert(subpatch_path); + parse_patch(world, target, model, base_uri, subpatch_rel, sub_base); + } + } + + + /* Set node port control values */ + query = Redland::Query(*world->rdf_world, Glib::ustring( + "SELECT DISTINCT ?nodename ?portname ?portval WHERE {\n") + + subject + " ingen:node ?node .\n" + "?node lv2:symbol ?nodename ;\n" + " ingen:port ?port .\n" + "?port lv2:symbol ?portname ;\n" + " ingen:value ?portval .\n" + "FILTER ( datatype(?portval) = xsd:decimal )\n" + "}"); + + results = query.run(*world->rdf_world, model, base_uri); + for (Redland::Query::Results::iterator i = results.begin(); i != results.end(); ++i) { + const string node_name = (*i)["nodename"].to_string(); + const string port_name = (*i)["portname"].to_string(); + + assert(Path::is_valid_name(node_name)); + assert(Path::is_valid_name(port_name)); + const Path port_path = patch_path.base() + node_name + "/" + port_name; + + target->set_port_value(port_path, AtomRDF::node_to_atom((*i)["portval"])); + } + + + /* Load this patch's ports */ + query = Redland::Query(*world->rdf_world, Glib::ustring( + "SELECT DISTINCT ?port ?type ?name ?datatype ?varkey ?varval ?portval WHERE {\n") + + subject + " ingen:port ?port .\n" + "?port a ?type ;\n" + " a ?datatype ;\n" + " lv2:symbol ?name .\n" + " FILTER (?type != ?datatype && ((?type = ingen:InputPort) || (?type = ingen:OutputPort)))\n" + "OPTIONAL { ?port ingen:value ?portval . \n" + " FILTER ( datatype(?portval) = xsd:decimal ) }\n" + "OPTIONAL { ?port lv2var:variable ?variable .\n" + " ?variable rdf:predicate ?varkey ;\n" + " rdf:value ?varval .\n" + " }" + "}"); + + results = query.run(*world->rdf_world, model, base_uri); + world->rdf_world->mutex().lock(); + for (Redland::Query::Results::iterator i = results.begin(); i != results.end(); ++i) { + const string name = (*i)["name"].to_string(); + const string type = world->rdf_world->qualify((*i)["type"].to_string()); + const string datatype = world->rdf_world->qualify((*i)["datatype"].to_string()); + + assert(Path::is_valid_name(name)); + const Path port_path = patch_path.base() + name; + + if (created.find(port_path) == created.end()) { + bool is_output = (type == "ingen:OutputPort"); // FIXME: check validity + // FIXME: read index + target->new_port(port_path, 0, datatype, is_output); + created.insert(port_path); + } + + const Redland::Node& val_node = (*i)["portval"]; + target->set_port_value(patch_path.base() + name, AtomRDF::node_to_atom(val_node)); + + const string key = world->rdf_world->prefixes().qualify((*i)["varkey"].to_string()); + const Redland::Node& var_val_node = (*i)["varval"]; + + if (key != "") + target->set_variable(patch_path.base() + name, key, AtomRDF::node_to_atom(var_val_node)); + } + world->rdf_world->mutex().unlock(); + + created.clear(); + + parse_connections(world, target, model, base_uri, subject, patch_path); + parse_variables(world, target, model, base_uri, subject, patch_path, data); + + /* Enable */ + query = Redland::Query(*world->rdf_world, Glib::ustring( + "SELECT DISTINCT ?enabled WHERE { ") + subject + " ingen:enabled ?enabled }"); + + results = query.run(*world->rdf_world, model, base_uri); + for (Redland::Query::Results::iterator i = results.begin(); i != results.end(); ++i) { + const Redland::Node& enabled_node = (*i)["enabled"]; + if (enabled_node.is_bool() && enabled_node) { + target->set_property(patch_path, "ingen:enabled", (bool)true); + break; + } else { + cerr << "WARNING: Unknown type for property ingen:enabled" << endl; + } + } + + return true; +} + + +bool +Parser::parse_node( + Ingen::Shared::World* world, + Ingen::Shared::CommonInterface* target, + Redland::Model& model, + const Glib::ustring& base_uri, + const Glib::ustring& subject, + const Raul::Path& path, + boost::optional data=boost::optional()) +{ + /* Get plugin */ + Redland::Query query(*world->rdf_world, Glib::ustring( + "SELECT DISTINCT ?plug WHERE { ") + subject + " ingen:plugin ?plug }"); + + Redland::Query::Results results = query.run(*world->rdf_world, model, base_uri); + + if (results.size() == 0) { + cerr << "[Parser] ERROR: Node missing mandatory ingen:plugin property" << endl; + return false; + } + + const Redland::Node& plugin_node = (*results.begin())["plug"]; + if (plugin_node.type() != Redland::Node::RESOURCE) { + cerr << "[Parser] ERROR: node's ingen:plugin property is not a resource" << endl; + return false; + } + + target->new_node(path, world->rdf_world->expand_uri(plugin_node.to_c_string())); + parse_variables(world, target, model, base_uri, subject, path, data); + + return true; +} + + +bool +Parser::parse_port( + Ingen::Shared::World* world, + Ingen::Shared::CommonInterface* target, + Redland::Model& model, + const Glib::ustring& base_uri, + const Glib::ustring& subject, + const Raul::Path& path, + boost::optional data) +{ + Redland::Query query(*world->rdf_world, Glib::ustring( + "SELECT DISTINCT ?type ?datatype ?value WHERE {\n") + + subject + " a ?type ;\n" + " a ?datatype .\n" + " FILTER (?type != ?datatype && ((?type = ingen:InputPort) || (?type = ingen:OutputPort)))\n" + "OPTIONAL { " + subject + " ingen:value ?value . }\n" + "}"); + + Redland::Query::Results results = query.run(*world->rdf_world, model, base_uri); + world->rdf_world->mutex().lock(); + + for (Redland::Query::Results::iterator i = results.begin(); i != results.end(); ++i) { + const string type = world->rdf_world->qualify((*i)["type"].to_string()); + const string datatype = world->rdf_world->qualify((*i)["datatype"].to_string()); + + bool is_output = (type == "ingen:OutputPort"); + // FIXME: read index + target->new_port(path, 0, datatype, is_output); + + const Redland::Node& val_node = (*i)["value"]; + if (val_node.to_string() != "") + target->set_port_value(path, AtomRDF::node_to_atom(val_node)); + } + world->rdf_world->mutex().unlock(); + + return parse_variables(world, target, model, base_uri, subject, path, data); +} + + +bool +Parser::parse_connections( + Ingen::Shared::World* world, + Ingen::Shared::CommonInterface* target, + Redland::Model& model, + const Glib::ustring& base_uri, + const Glib::ustring& subject, + const Raul::Path& parent) +{ + Redland::Query query(*world->rdf_world, Glib::ustring( + "SELECT DISTINCT ?src ?dst WHERE {\n") + /*+ subject*/ + /*"?foo ingen:connection ?connection .\n"*/ + "?connection ingen:source ?src ;\n" + " ingen:destination ?dst .\n" + "}"); + + Redland::Query::Results results = query.run(*world->rdf_world, model, base_uri); + for (Redland::Query::Results::iterator i = results.begin(); i != results.end(); ++i) { + string src_path = parent.base() + uri_relative_to_base(base_uri, (*i)["src"].to_string()); + if (!Path::is_valid(src_path)) { + cerr << "ERROR: Invalid path in connection: " << src_path << endl; + continue; + } + + string dst_path = parent.base() + uri_relative_to_base(base_uri, (*i)["dst"].to_string()); + if (!Path::is_valid(dst_path)) { + cerr << "ERROR: Invalid path in connection: " << dst_path << endl; + continue; + } + + target->connect(src_path, dst_path); + } + + return true; +} + + +bool +Parser::parse_variables( + Ingen::Shared::World* world, + Ingen::Shared::CommonInterface* target, + Redland::Model& model, + const Glib::ustring& base_uri, + const Glib::ustring& subject, + const Raul::Path& path, + boost::optional data=boost::optional()) +{ + Redland::Query query(*world->rdf_world, Glib::ustring( + "SELECT DISTINCT ?varkey ?varval WHERE {\n") + + subject + " lv2var:variable ?variable .\n" + "?variable rdf:predicate ?varkey ;\n" + " rdf:value ?varval .\n" + "}"); + + Redland::Query::Results results = query.run(*world->rdf_world, model, base_uri); + for (Redland::Query::Results::iterator i = results.begin(); i != results.end(); ++i) { + const string key = world->rdf_world->prefixes().qualify(string((*i)["varkey"])); + const Redland::Node& val_node = (*i)["varval"]; + if (key != "") + target->set_variable(path, key, AtomRDF::node_to_atom(val_node)); + } + + query = Redland::Query(*world->rdf_world, Glib::ustring( + "SELECT DISTINCT ?key ?val WHERE {\n") + + subject + " ingen:property ?property .\n" + "?property rdf:predicate ?key ;\n" + " rdf:value ?val .\n" + "}"); + + results = query.run(*world->rdf_world, model, base_uri); + for (Redland::Query::Results::iterator i = results.begin(); i != results.end(); ++i) { + const string key = world->rdf_world->prefixes().qualify(string((*i)["key"])); + const Redland::Node& val_node = (*i)["val"]; + if (key != "") + target->set_property(path, key, AtomRDF::node_to_atom(val_node)); + } + + // Set passed variables last to override any loaded values + if (data) + for (GraphObject::Variables::const_iterator i = data.get().begin(); i != data.get().end(); ++i) + target->set_variable(path, i->first, i->second); + + return true; +} + + +} // namespace Serialisation +} // namespace Ingen + diff --git a/src/serialisation/Parser.hpp b/src/serialisation/Parser.hpp new file mode 100644 index 00000000..7b8a35eb --- /dev/null +++ b/src/serialisation/Parser.hpp @@ -0,0 +1,126 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LOADER_H +#define LOADER_H + +#include +#include +#include +#include +#include +#include +#include "interface/GraphObject.hpp" +#include "module/World.hpp" + +namespace Redland { class World; class Model; } +namespace Ingen { namespace Shared { class CommonInterface; } } + +using namespace Ingen::Shared; + +namespace Ingen { +namespace Serialisation { + + +class Parser { +public: + virtual ~Parser() {} + + virtual bool parse_document( + Ingen::Shared::World* world, + Shared::CommonInterface* target, + const Glib::ustring& document_uri, + Glib::ustring engine_base, + Glib::ustring object_uri, + boost::optional symbol=boost::optional(), + boost::optional data=boost::optional()); + + virtual bool parse_string( + Ingen::Shared::World* world, + Shared::CommonInterface* target, + const Glib::ustring& str, + const Glib::ustring& base_uri, + Glib::ustring engine_base, + boost::optional object_uri=boost::optional(), + boost::optional symbol=boost::optional(), + boost::optional data=boost::optional()); + +private: + + Glib::ustring uri_relative_to_base(Glib::ustring base, const Glib::ustring uri); + + bool parse( + Ingen::Shared::World* world, + Shared::CommonInterface* target, + Redland::Model& model, + Glib::ustring base_uri, + Glib::ustring engine_base, + boost::optional object_uri=boost::optional(), + boost::optional symbol=boost::optional(), + boost::optional data=boost::optional()); + + bool parse_patch( + Ingen::Shared::World* world, + Ingen::Shared::CommonInterface* target, + Redland::Model& model, + const Glib::ustring& base_uri, + Glib::ustring engine_base, + const Glib::ustring& object_uri, + boost::optional data); + + bool parse_node( + Ingen::Shared::World* world, + Ingen::Shared::CommonInterface* target, + Redland::Model& model, + const Glib::ustring& base_uri, + const Glib::ustring& subject, + const Raul::Path& path, + boost::optional data); + + bool parse_port( + Ingen::Shared::World* world, + Ingen::Shared::CommonInterface* target, + Redland::Model& model, + const Glib::ustring& base_uri, + const Glib::ustring& subject, + const Raul::Path& path, + boost::optional data=boost::optional()); + + bool parse_variables( + Ingen::Shared::World* world, + Ingen::Shared::CommonInterface* target, + Redland::Model& model, + const Glib::ustring& base_uri, + const Glib::ustring& subject, + const Raul::Path& path, + boost::optional data); + + bool parse_connections( + Ingen::Shared::World* world, + Ingen::Shared::CommonInterface* target, + Redland::Model& model, + const Glib::ustring& base_uri, + const Glib::ustring& subject, + const Raul::Path& parent); + +}; + + +} // namespace Serialisation +} // namespace Ingen + +#endif // LOADER_H diff --git a/src/serialisation/Serialiser.cpp b/src/serialisation/Serialiser.cpp new file mode 100644 index 00000000..2f0d9877 --- /dev/null +++ b/src/serialisation/Serialiser.cpp @@ -0,0 +1,501 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include // atof +#include +#include +#include +#include +#include +#include +#include // pair, make_pair +#include +#include +#include +#include +#include +#include +#include +#include +#include "module/World.hpp" +#include "interface/EngineInterface.hpp" +#include "interface/Plugin.hpp" +#include "interface/Patch.hpp" +#include "interface/Node.hpp" +#include "interface/Port.hpp" +#include "interface/Connection.hpp" +#include "Serialiser.hpp" + +using namespace std; +using namespace Raul; +using namespace Redland; +using namespace Ingen; +using namespace Ingen::Shared; + +namespace Ingen { +namespace Serialisation { + + +Serialiser::Serialiser(Shared::World& world, SharedPtr store) + : _root_path("/") + , _store(store) + , _world(*world.rdf_world) +{ +} + + +void +Serialiser::to_file(SharedPtr object, const string& filename) +{ + _root_path = object->path(); + start_to_filename(filename); + serialise(object); + finish(); +} + + + +string +Serialiser::to_string(SharedPtr object, + const string& base_uri, + const GraphObject::Variables& extra_rdf) +{ + start_to_string(object->path(), base_uri); + serialise(object); + + Redland::Node base_rdf_node(_model->world(), Redland::Node::RESOURCE, base_uri); + for (GraphObject::Variables::const_iterator v = extra_rdf.begin(); v != extra_rdf.end(); ++v) { + if (v->first.find(":") != string::npos) { + _model->add_statement(base_rdf_node, v->first, + AtomRDF::atom_to_node(_model->world(), v->second)); + } else { + cerr << "Warning: not serialising extra RDF with key '" << v->first << "'" << endl; + } + } + + return finish(); +} + + +/** Begin a serialization to a file. + * + * This must be called before any serializing methods. + */ +void +Serialiser::start_to_filename(const string& filename) +{ + setlocale(LC_NUMERIC, "C"); + + assert(filename.find(":") == string::npos || filename.substr(0, 5) == "file:"); + if (filename.find(":") == string::npos) + _base_uri = "file://" + filename; + else + _base_uri = filename; + _model = new Redland::Model(_world); + _model->set_base_uri(_base_uri); + _mode = TO_FILE; +} + + +/** Begin a serialization to a string. + * + * This must be called before any serializing methods. + * + * The results of the serialization will be returned by the finish() method after + * the desired objects have been serialised. + * + * All serialized paths will have the root path chopped from their prefix + * (therefore all serialized paths must be descendants of the root) + */ +void +Serialiser::start_to_string(const Raul::Path& root, const string& base_uri) +{ + setlocale(LC_NUMERIC, "C"); + + _root_path = root; + _base_uri = base_uri; + _model = new Redland::Model(_world); + _model->set_base_uri(base_uri); + _mode = TO_STRING; +} + + +/** Finish a serialization. + * + * If this was a serialization to a string, the serialization output + * will be returned, otherwise the empty string is returned. + */ +string +Serialiser::finish() +{ + string ret = ""; + if (_mode == TO_FILE) { + _model->serialise_to_file(_base_uri); + } else { + char* c_str = _model->serialise_to_string(); + ret = c_str; + free(c_str); + } + + _base_uri = ""; +#ifdef USE_BLANK_NODES + _node_map.clear(); +#endif + return ret; +} + + +Redland::Node +Serialiser::patch_path_to_rdf_node(const Path& path) +{ +#ifdef USE_BLANK_NODES + if (path == _root_path) { + return Redland::Node(_model->world(), Redland::Node::RESOURCE, _base_uri); + } else { + assert(path.length() > _root_path.length()); + return Redland::Node(_model->world(), Redland::Node::RESOURCE, + _base_uri + string("#") + path.substr(_root_path.length())); + } +#else + return path_to_rdf_node(path); +#endif +} + + + +/** Convert a path to an RDF blank node ID for serializing. + */ +Redland::Node +Serialiser::path_to_rdf_node(const Path& path) +{ +#if USE_BLANK_NODES + NodeMap::iterator i = _node_map.find(path); + if (i != _node_map.end()) { + assert(i->second); + assert(i->second.get_node()); + return i->second; + } else { + Redland::Node id = _world.blank_id(Path::nameify(path.substr(1))); + assert(id); + _node_map[path] = id; + return id; + } +#else + assert(_model); + assert(path.substr(0, _root_path.length()) == _root_path); + + if (path == _root_path) + return Redland::Node(_model->world(), Redland::Node::RESOURCE, _base_uri); + else + return Redland::Node(_model->world(), Redland::Node::RESOURCE, + path.substr(_root_path.base().length())); +#endif +} + + +#if 0 +/** Searches for the filename passed in the path, returning the full + * path of the file, or the empty string if not found. + * + * This function tries to be as friendly a black box as possible - if the path + * passed is an absolute path and the file is found there, it will return + * that path, etc. + * + * additional_path is a list (colon delimeted as usual) of additional + * directories to look in. ie the directory the parent patch resides in would + * be a good idea to pass as additional_path, in the case of a subpatch. + */ +string +Serialiser::find_file(const string& filename, const string& additional_path) +{ + string search_path = additional_path + ":" + _patch_search_path; + + // Try to open the raw filename first + std::ifstream is(filename.c_str(), std::ios::in); + if (is.good()) { + is.close(); + return filename; + } + + string directory; + string full_patch_path = ""; + + while (search_path != "") { + directory = search_path.substr(0, search_path.find(':')); + if (search_path.find(':') != string::npos) + search_path = search_path.substr(search_path.find(':')+1); + else + search_path = ""; + + full_patch_path = directory +"/"+ filename; + + std::ifstream is; + is.open(full_patch_path.c_str(), std::ios::in); + + if (is.good()) { + is.close(); + return full_patch_path; + } else { + cerr << "[Serialiser] Could not find patch file " << full_patch_path << endl; + } + } + + return ""; +} +#endif + +void +Serialiser::serialise(SharedPtr object) throw (std::logic_error) +{ + if (!_model) + throw std::logic_error("serialise called without serialization in progress"); + + SharedPtr patch = PtrCast(object); + if (patch) { + serialise_patch(patch); + return; + } + + SharedPtr node = PtrCast(object); + if (node) { + serialise_node(node, path_to_rdf_node(node->path())); + return; + } + + SharedPtr port = PtrCast(object); + if (port) { + serialise_port(port.get(), path_to_rdf_node(port->path())); + return; + } + + cerr << "[Serialiser] WARNING: Unsupported object type, " + << object->path() << " not serialised." << endl; +} + + +void +Serialiser::serialise_patch(SharedPtr patch) +{ + assert(_model); + + const Redland::Node patch_id = patch_path_to_rdf_node(patch->path()); + + _model->add_statement( + patch_id, + "rdf:type", + Redland::Node(_model->world(), Redland::Node::RESOURCE, "http://drobilla.net/ns/ingen#Patch")); + + GraphObject::Variables::const_iterator s = patch->variables().find("lv2:symbol"); + // If symbol is stored as a variable, write that + if (s != patch->variables().end()) { + _model->add_statement(patch_id, "lv2:symbol", + Redland::Node(_model->world(), Redland::Node::LITERAL, s->second.get_string())); + // Otherwise take the one from our path (if possible) + } else if (patch->path() != "/") { + _model->add_statement( + patch_id, "lv2:symbol", + Redland::Node(_model->world(), Redland::Node::LITERAL, patch->path().name())); + } + + _model->add_statement( + patch_id, + "ingen:polyphony", + AtomRDF::atom_to_node(_model->world(), Atom((int)patch->internal_polyphony()))); + + _model->add_statement( + patch_id, + "ingen:enabled", + AtomRDF::atom_to_node(_model->world(), Atom((bool)patch->enabled()))); + + serialise_variables(patch_id, patch->variables()); + + for (GraphObject::const_iterator n = _store->children_begin(patch); + n != _store->children_end(patch); ++n) { + + if (n->second->graph_parent() != patch.get()) + continue; + + SharedPtr patch = PtrCast(n->second); + SharedPtr node = PtrCast(n->second); + if (patch) { + _model->add_statement(patch_id, "ingen:node", patch_path_to_rdf_node(patch->path())); + serialise_patch(patch); + } else if (node) { + const Redland::Node node_id = path_to_rdf_node(n->second->path()); + _model->add_statement(patch_id, "ingen:node", node_id); + serialise_node(node, node_id); + } + } + + for (uint32_t i=0; i < patch->num_ports(); ++i) { + Port* p = patch->port(i); + const Redland::Node port_id = path_to_rdf_node(p->path()); + _model->add_statement(patch_id, "ingen:port", port_id); + serialise_port(p, port_id); + } + + for (Shared::Patch::Connections::const_iterator c = patch->connections().begin(); + c != patch->connections().end(); ++c) { + serialise_connection(patch, *c); + } +} + + +void +Serialiser::serialise_plugin(SharedPtr plugin) +{ + assert(_model); + + const Redland::Node plugin_id = Redland::Node(_model->world(), Redland::Node::RESOURCE, plugin->uri()); + + _model->add_statement( + plugin_id, + "rdf:type", + Redland::Node(_model->world(), Redland::Node::RESOURCE, plugin->type_uri())); +} + + +void +Serialiser::serialise_node(SharedPtr node, const Redland::Node& node_id) +{ + const Redland::Node plugin_id + = Redland::Node(_model->world(), Redland::Node::RESOURCE, node->plugin()->uri()); + + _model->add_statement( + node_id, + "rdf:type", + Redland::Node(_model->world(), Redland::Node::RESOURCE, "ingen:Node")); + + _model->add_statement( + node_id, + "lv2:symbol", + Redland::Node(_model->world(), Redland::Node::LITERAL, node->path().name())); + + _model->add_statement( + node_id, + "ingen:plugin", + plugin_id); + + _model->add_statement( + node_id, + "ingen:polyphonic", + AtomRDF::atom_to_node(_model->world(), Atom(node->polyphonic()))); + + //serialise_plugin(node->plugin()); + + for (uint32_t i=0; i < node->num_ports(); ++i) { + Port* p = node->port(i); + assert(p); + const Redland::Node port_id = path_to_rdf_node(p->path()); + serialise_port(p, port_id); + _model->add_statement(node_id, "ingen:port", port_id); + } + + serialise_variables(node_id, node->variables()); +} + + +/** Writes a port subject with various information only if there are some + * predicate/object pairs to go with it (eg if the port has variable, or a value, or..). + * Audio output ports with no variable will not be written, for example. + */ +void +Serialiser::serialise_port(const Port* port, const Redland::Node& port_id) +{ + if (port->is_input()) + _model->add_statement(port_id, "rdf:type", + Redland::Node(_model->world(), Redland::Node::RESOURCE, "ingen:InputPort")); + else + _model->add_statement(port_id, "rdf:type", + Redland::Node(_model->world(), Redland::Node::RESOURCE, "ingen:OutputPort")); + + _model->add_statement(port_id, "lv2:index", + AtomRDF::atom_to_node(_model->world(), Atom((int)port->index()))); + + _model->add_statement(port_id, "lv2:symbol", + Redland::Node(_model->world(), Redland::Node::LITERAL, port->path().name())); + + _model->add_statement(port_id, "rdf:type", + Redland::Node(_model->world(), Redland::Node::RESOURCE, port->type().uri())); + + if (port->type() == DataType::CONTROL && port->is_input()) + _model->add_statement(port_id, "ingen:value", + AtomRDF::atom_to_node(_model->world(), Atom(port->value()))); + + serialise_variables(port_id, port->variables()); +} + + +void +Serialiser::serialise_connection(SharedPtr parent, + SharedPtr connection) throw (std::logic_error) +{ + if (!_model) + throw std::logic_error("serialise_connection called without serialization in progress"); + + const Redland::Node src_node = path_to_rdf_node(connection->src_port_path()); + const Redland::Node dst_node = path_to_rdf_node(connection->dst_port_path()); + + /* This would allow associating data with the connection... */ + const Redland::Node connection_node = _world.blank_id(); + _model->add_statement(connection_node, "ingen:source", src_node); + _model->add_statement(connection_node, "ingen:destination", dst_node); + if (parent) { + const Redland::Node parent_node = path_to_rdf_node(parent->path()); + _model->add_statement(parent_node, "ingen:connection", connection_node); + } else { + _model->add_statement(connection_node, "rdf:type", + Redland::Node(_model->world(), Redland::Node::RESOURCE, "ingen:Connection")); + } + + /* ... but this is cleaner */ + //_model->add_statement(dst_node, "ingen:connectedTo", src_node); +} + + +void +Serialiser::serialise_variables(Redland::Node subject, const GraphObject::Variables& variables) +{ + for (GraphObject::Variables::const_iterator v = variables.begin(); v != variables.end(); ++v) { + if (v->first.find(":") != string::npos && v->first != "ingen:document") { + if (v->second.is_valid()) { + const Redland::Node var_id = _world.blank_id(); + const Redland::Node key(_model->world(), Redland::Node::RESOURCE, v->first); + const Redland::Node value = AtomRDF::atom_to_node(_model->world(), v->second); + if (value) { + _model->add_statement(subject, "lv2var:variable", var_id); + _model->add_statement(var_id, "rdf:predicate", key); + _model->add_statement(var_id, "rdf:value", value); + } else { + cerr << "Warning: can not serialise value: key '" << v->first << "', type " + << (int)v->second.type() << endl; + } + } else { + cerr << "Warning: variable with no value: key '" << v->first << "'" << endl; + } + } else { + cerr << "Warning: not serialising variable with invalid key '" << v->first << "'" << endl; + } + } +} + + +} // namespace Serialisation +} // namespace Ingen diff --git a/src/serialisation/Serialiser.hpp b/src/serialisation/Serialiser.hpp new file mode 100644 index 00000000..f27cad83 --- /dev/null +++ b/src/serialisation/Serialiser.hpp @@ -0,0 +1,109 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SERIALISER_H +#define SERIALISER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "interface/GraphObject.hpp" +#include "shared/Store.hpp" + +using namespace Raul; +using namespace Ingen::Shared; + +namespace Ingen { + +namespace Shared { + class Plugin; + class GraphObject; + class Patch; + class Node; + class Port; + class Connection; + class World; +} + +namespace Serialisation { + + +/** Serialises Ingen objects (patches, nodes, etc) to RDF. + * + * \ingroup IngenClient + */ +class Serialiser +{ +public: + Serialiser(Shared::World& world, SharedPtr store); + + void to_file(SharedPtr object, const std::string& filename); + + std::string to_string(SharedPtr object, + const std::string& base_uri, + const GraphObject::Variables& extra_rdf); + + void start_to_string(const Raul::Path& root, const std::string& base_uri); + void serialise(SharedPtr object) throw (std::logic_error); + void serialise_connection(SharedPtr parent, + SharedPtr c) throw (std::logic_error); + + std::string finish(); + +private: + enum Mode { TO_FILE, TO_STRING }; + + void start_to_filename(const std::string& filename); + + void setup_prefixes(); + + void serialise_plugin(SharedPtr p); + + void serialise_patch(SharedPtr p); + void serialise_node(SharedPtr n, const Redland::Node& id); + void serialise_port(const Shared::Port* p, const Redland::Node& id); + + void serialise_variables(Redland::Node subject, const GraphObject::Variables& variables); + + Redland::Node path_to_rdf_node(const Path& path); + Redland::Node patch_path_to_rdf_node(const Path& path); + + Raul::Path _root_path; + SharedPtr _store; + Mode _mode; + std::string _base_uri; + Redland::World& _world; + Redland::Model* _model; + +#ifdef USE_BLANK_NODES + typedef std::map NodeMap; + NodeMap _node_map; +#endif +}; + + +} // namespace Serialisation +} // namespace Ingen + +#endif // SERIALISER_H diff --git a/src/serialisation/serialisation.cpp b/src/serialisation/serialisation.cpp new file mode 100644 index 00000000..1d08e76c --- /dev/null +++ b/src/serialisation/serialisation.cpp @@ -0,0 +1,46 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include CONFIG_H_PATH +#include "module/World.hpp" +#include "serialisation.hpp" +#include "Parser.hpp" +#include "Serialiser.hpp" + +namespace Ingen { +namespace Serialisation { + + +Ingen::Serialisation::Parser* +new_parser() +{ + return new Parser(); +} + + +Ingen::Serialisation::Serialiser* +new_serialiser(Ingen::Shared::World* world, SharedPtr store) +{ + assert(world->rdf_world); + return new Serialiser(*world, store); +} + + + +} // namespace Serialisation +} // namespace Ingen + diff --git a/src/serialisation/serialisation.hpp b/src/serialisation/serialisation.hpp new file mode 100644 index 00000000..a250945b --- /dev/null +++ b/src/serialisation/serialisation.hpp @@ -0,0 +1,44 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef INGEN_SERIALISATION_H +#define INGEN_SERIALISATION_H + +namespace Ingen { + +namespace Shared { class World; class Store; } + +namespace Serialisation { + +class Parser; +class Serialiser; + + +extern "C" { + + extern Parser* new_parser(); + extern Serialiser* new_serialiser(Ingen::Shared::World* world, + SharedPtr store); + +} + + +} // namespace Serialisation +} // namespace Ingen + +#endif // INGEN_SERIALISATION_H + diff --git a/src/serialisation/wscript b/src/serialisation/wscript new file mode 100644 index 00000000..c3e98b81 --- /dev/null +++ b/src/serialisation/wscript @@ -0,0 +1,16 @@ +#!/usr/bin/env python +import Params + +def build(bld): + obj = bld.create_obj('cpp', 'shlib') + obj.source = ''' + Parser.cpp + Serialiser.cpp + serialisation.cpp + ''' + obj.includes = ['..', '../../common', '../..'] + obj.name = 'libingen_serialisation' + obj.target = 'ingen_serialisation' + obj.uselib = 'GLIBMM SLV2 RAUL REDLANDMM' + obj.vnum = '0.0.0' + diff --git a/src/shared/Builder.cpp b/src/shared/Builder.cpp new file mode 100644 index 00000000..1eb127a5 --- /dev/null +++ b/src/shared/Builder.cpp @@ -0,0 +1,98 @@ +/* This file is part of Ingen. + * Copyright (C) 2008 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "Builder.hpp" +#include "common/interface/CommonInterface.hpp" +#include "common/interface/Patch.hpp" +#include "common/interface/Node.hpp" +#include "common/interface/Port.hpp" +#include "common/interface/Connection.hpp" +#include "common/interface/Plugin.hpp" +#include +using namespace std; + +namespace Ingen { +namespace Shared { + + +Builder::Builder(CommonInterface& interface) + : _interface(interface) +{ +} + + +void +Builder::build(const Raul::Path& prefix, SharedPtr object) +{ + SharedPtr patch = PtrCast(object); + if (patch) { + if (object->path() != "/") { + const std::string path_str = prefix.base() + object->path().substr(1); + //cout << "BUILDING PATCH " << path_str << endl; + _interface.new_patch(path_str, patch->internal_polyphony()); + } + + build_object(prefix, object); + for (Patch::Connections::const_iterator i = patch->connections().begin(); + i != patch->connections().end(); ++i) { + string base = prefix.base() + object->path().substr(1); + cout << "*********** BASE: " << base << endl; + _interface.connect(base + (*i)->src_port_path().substr(1), + base + (*i)->dst_port_path().substr(1)); + } + return; + } + + SharedPtr node = PtrCast(object); + if (node) { + Raul::Path path = prefix.base() + node->path().substr(1); + //cout << "BUILDING NODE " << path << endl; + _interface.new_node(path, node->plugin()->uri()); + build_object(prefix, object); + return; + } + + SharedPtr port = PtrCast(object); + if (port) { + Raul::Path path = prefix.base() + port->path().substr(1); + //cout << "BUILDING PORT " << path << endl; + _interface.new_port(path, port->index(), port->type().uri(), !port->is_input()); + build_object(prefix, object); + return; + } +} + + +void +Builder::build_object(const Raul::Path& prefix, SharedPtr object) +{ + for (GraphObject::Variables::const_iterator i = object->variables().begin(); + i != object->variables().end(); ++i) + _interface.set_variable(prefix.base() + object->path().substr(1), i->first, i->second); + + for (GraphObject::Properties::const_iterator i = object->properties().begin(); + i != object->properties().end(); ++i) { + if (object->path() == "/") + continue; + string path_str = prefix.base() + object->path().substr(1); + _interface.set_property(prefix.base() + object->path().substr(1), i->first, i->second); + } +} + + +} // namespace Shared +} // namespace Ingen diff --git a/src/shared/Builder.hpp b/src/shared/Builder.hpp new file mode 100644 index 00000000..a980bad2 --- /dev/null +++ b/src/shared/Builder.hpp @@ -0,0 +1,57 @@ +/* This file is part of Ingen. + * Copyright (C) 2008 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef BUILDER_H +#define BUILDER_H + +#include + +namespace Raul { class Path; } + +namespace Ingen { +namespace Shared { + +class GraphObject; +class CommonInterface; + + +/** Wrapper for CommonInterface to create existing objects/models. + * + * \ingroup interface + */ +class Builder +{ +public: + Builder(CommonInterface& interface); + virtual ~Builder() {} + + void build(const Raul::Path& prefix, + SharedPtr object); + +private: + void build_object(const Raul::Path& prefix, + SharedPtr object); + + CommonInterface& _interface; +}; + + +} // namespace Shared +} // namespace Ingen + +#endif // BUILDER_H + diff --git a/src/shared/ClashAvoider.cpp b/src/shared/ClashAvoider.cpp new file mode 100644 index 00000000..d5d1d245 --- /dev/null +++ b/src/shared/ClashAvoider.cpp @@ -0,0 +1,214 @@ +/* This file is part of Ingen. + * Copyright (C) 2008 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "ClashAvoider.hpp" +#include "Store.hpp" + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Shared { + + +const Raul::Path +ClashAvoider::map_path(const Raul::Path& in) +{ + //cout << "MAP PATH: " << in << endl; + + unsigned offset = 0; + bool has_offset = false; + size_t pos = in.find_last_of("_"); + if (pos != string::npos && pos != (in.length()-1)) { + const std::string trailing = in.substr(in.find_last_of("_")+1); + has_offset = (sscanf(trailing.c_str(), "%u", &offset) > 0); + } + + //cout << "HAS OFFSET: " << offset << endl; + + // Path without _n suffix + Path base_path = in; + if (has_offset) + base_path = base_path.substr(0, base_path.find_last_of("_")); + + //cout << "\tBASE: " << base_path << endl; + + SymbolMap::iterator m = _symbol_map.find(in); + if (m != _symbol_map.end()) { + //cout << " (1) " << m->second << endl; + return m->second; + } else { + typedef std::pair InsertRecord; + + // No clash, use symbol unmodified + if (!exists(in) && _symbol_map.find(in) == _symbol_map.end()) { + InsertRecord i = _symbol_map.insert(make_pair(in, in)); + assert(i.second); + //cout << " (2) " << i.first->second << endl;; + return i.first->second; + } else { + + // See if the parent is mapped + // FIXME: do this the other way around + Path parent = in.parent(); + do { + SymbolMap::iterator p = _symbol_map.find(parent); + if (p != _symbol_map.end()) { + const Path mapped = p->second.base() + in.substr(parent.base().length()); + InsertRecord i = _symbol_map.insert(make_pair(in, mapped)); + //cout << " (3) " << _prefix.base() + i.first->second.substr(1) << endl; + return i.first->second; + } + parent = parent.parent(); + } while (parent != "/"); + + // Append _2 _3 etc until an unused symbol is found + while (true) { + Offsets::iterator o = _offsets.find(base_path); + if (o != _offsets.end()) { + offset = ++o->second; + } else { + string parent_str = _prefix.base() + in.parent().base().substr(1); + parent_str = parent_str.substr(0, parent_str.find_last_of("/")); + if (parent_str == "") + parent_str = "/"; + //cout << "***** PARENT: " << parent_str << endl; + } + + std::stringstream ss; + ss << base_path << "_" << offset; + if (!exists(ss.str())) { + string str = ss.str(); + InsertRecord i = _symbol_map.insert(make_pair(in, str)); + //cout << "HIT: offset = " << offset << ", str = " << str << endl; + offset = _store.child_name_offset(in.parent(), base_path.name(), false); + _offsets.insert(make_pair(base_path, offset)); + //cout << " (4) " << i.first->second << endl;; + return i.first->second; + } else { + //cout << "MISSED OFFSET: " << in << " => " << ss.str() << endl; + if (o != _offsets.end()) + offset = ++o->second; + else + ++offset; + } + } + } + } +} + + +bool +ClashAvoider::exists(const Raul::Path& path) const +{ + bool exists = (_store.find(_prefix.base() + path.substr(1)) != _store.end()); + if (exists) + return true; + + if (_also_avoid) + return (_also_avoid->find(path) != _also_avoid->end()); + else + return false; +} + + +void +ClashAvoider::new_patch(const std::string& path, + uint32_t poly) +{ + _target.new_patch(map_path(path), poly); +} + + +void +ClashAvoider::new_node(const std::string& path, + const std::string& plugin_uri) +{ + _target.new_node(map_path(path), plugin_uri); +} + + +void +ClashAvoider::new_port(const std::string& path, + uint32_t index, + const std::string& data_type, + bool is_output) +{ + _target.new_port(map_path(path), index, data_type, is_output); +} + + +void +ClashAvoider::connect(const std::string& src_port_path, + const std::string& dst_port_path) +{ + _target.connect(map_path(src_port_path), map_path(dst_port_path)); +} + + +void +ClashAvoider::disconnect(const std::string& src_port_path, + const std::string& dst_port_path) +{ + _target.disconnect(map_path(src_port_path), map_path(dst_port_path)); +} + + +void +ClashAvoider::set_variable(const std::string& subject_path, + const std::string& predicate, + const Raul::Atom& value) +{ + _target.set_variable(map_path(subject_path), predicate, value); +} + + +void +ClashAvoider::set_property(const std::string& subject_path, + const std::string& predicate, + const Raul::Atom& value) +{ + _target.set_property(map_path(subject_path), predicate, value); +} + + +void +ClashAvoider::set_port_value(const std::string& port_path, + const Raul::Atom& value) +{ + _target.set_port_value(map_path(port_path), value); +} + + +void +ClashAvoider::set_voice_value(const std::string& port_path, + uint32_t voice, + const Raul::Atom& value) +{ + _target.set_voice_value(map_path(port_path), voice, value); +} + + +void +ClashAvoider::destroy(const std::string& path) +{ + _target.destroy(map_path(path)); +} + + +} // namespace Shared +} // namespace Ingen diff --git a/src/shared/ClashAvoider.hpp b/src/shared/ClashAvoider.hpp new file mode 100644 index 00000000..f7016e4e --- /dev/null +++ b/src/shared/ClashAvoider.hpp @@ -0,0 +1,108 @@ +/* This file is part of Ingen. + * Copyright (C) 2008 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CLASHAVOIDER_H +#define CLASHAVOIDER_H + +#include +#include +#include +#include +#include +#include "interface/CommonInterface.hpp" + +namespace Ingen { +namespace Shared { + +class Store; + + +/** A wrapper for a CommonInterface that creates objects but possibly maps + * symbol names to avoid clashes with the existing objects in a store. + */ +class ClashAvoider : public CommonInterface +{ +public: + ClashAvoider(Store& store, const Raul::Path& prefix, CommonInterface& target, + Store* also_avoid=NULL) + : _prefix(prefix), _store(store), _target(target), _also_avoid(also_avoid) {} + + void set_target(CommonInterface& target) { _target = target; } + + // Bundles + void bundle_begin() { _target.bundle_begin(); } + void bundle_end() { _target.bundle_end(); } + + // Object commands + + void new_patch(const std::string& path, + uint32_t poly); + + void new_node(const std::string& path, + const std::string& plugin_uri); + + void new_port(const std::string& path, + uint32_t index, + const std::string& data_type, + bool is_output); + + void connect(const std::string& src_port_path, + const std::string& dst_port_path); + + void disconnect(const std::string& src_port_path, + const std::string& dst_port_path); + + void set_variable(const std::string& subject_path, + const std::string& predicate, + const Raul::Atom& value); + + void set_property(const std::string& subject_path, + const std::string& predicate, + const Raul::Atom& value); + + void set_port_value(const std::string& port_path, + const Raul::Atom& value); + + void set_voice_value(const std::string& port_path, + uint32_t voice, + const Raul::Atom& value); + + void destroy(const std::string& path); + +private: + const Raul::Path map_path(const Raul::Path& in); + + const Raul::Path& _prefix; + Store& _store; + CommonInterface& _target; + + Store* _also_avoid; + bool exists(const Raul::Path& path) const; + + typedef std::map Offsets; + Offsets _offsets; + + typedef std::map SymbolMap; + SymbolMap _symbol_map; +}; + + +} // namespace Shared +} // namespace Ingen + +#endif // CLASHAVOIDER_H + diff --git a/src/shared/LV2Features.cpp b/src/shared/LV2Features.cpp new file mode 100644 index 00000000..2e7eb10e --- /dev/null +++ b/src/shared/LV2Features.cpp @@ -0,0 +1,66 @@ +/* This file is part of Ingen. + * Copyright (C) 2008 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "LV2Features.hpp" +#include "LV2URIMap.hpp" + +using namespace std; + +namespace Ingen { +namespace Shared { + + +LV2Features::LV2Features() + : _lv2_features((LV2_Feature**)malloc(sizeof(LV2_Feature*))) +{ + _lv2_features[0] = NULL; + + LV2URIMap* controller = new LV2URIMap(); + add_feature(LV2_URI_MAP_URI, controller->feature(), controller); +} + + +const LV2Features::Feature* +LV2Features::feature(const std::string& uri) +{ + Features::const_iterator i = _features.find(uri); + if (i != _features.end()) + return &i->second; + else + return NULL; +} + + +void +LV2Features::add_feature(const std::string& uri, LV2_Feature* feature, void* controller) +{ +#ifndef NDEBUG + Features::const_iterator i = _features.find(uri); + assert(i == _features.end()); + assert(_lv2_features[_features.size()] == NULL); +#endif + _features.insert(make_pair(uri, Feature(feature, controller))); + + _lv2_features = (LV2_Feature**)realloc(_lv2_features, sizeof(LV2_Feature*) * (_features.size() + 1)); + _lv2_features[_features.size()-1] = feature; + _lv2_features[_features.size()] = NULL; +} + + +} // namespace Shared +} // namespace Ingen diff --git a/src/shared/LV2Features.hpp b/src/shared/LV2Features.hpp new file mode 100644 index 00000000..3a174aa2 --- /dev/null +++ b/src/shared/LV2Features.hpp @@ -0,0 +1,63 @@ +/* This file is part of Ingen. + * Copyright (C) 2008 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LV2FEATURES_HPP +#define LV2FEATURES_HPP + +#include CONFIG_H_PATH +#ifndef HAVE_SLV2 +#error "This file requires SLV2, but HAVE_SLV2 is not defined. Please report." +#endif + +#include +#include +#include + +namespace Ingen { +namespace Shared { + + +/** Stuff that may need to be passed to an LV2 plugin (i.e. LV2 features). + */ +class LV2Features { +public: + LV2Features(); + + struct Feature { + Feature(LV2_Feature* f, void* c=NULL) : feature(f), controller(c) {} + LV2_Feature* feature; ///< LV2 feature struct (plugin exposed) + void* controller; ///< Ingen internals, not exposed to plugin + }; + + typedef std::map Features; + + const Feature* feature(const std::string& uri); + + void add_feature(const std::string& uri, LV2_Feature* feature, void* controller); + + LV2_Feature** lv2_features() const { return _lv2_features; } + +private: + Features _features; + LV2_Feature** _lv2_features; +}; + + +} // namespace Shared +} // namespace Ingen + +#endif // LV2FEATURES_HPP diff --git a/src/shared/LV2URIMap.cpp b/src/shared/LV2URIMap.cpp new file mode 100644 index 00000000..c01dfea0 --- /dev/null +++ b/src/shared/LV2URIMap.cpp @@ -0,0 +1,75 @@ +/* This file is part of Ingen. + * Copyright (C) 2008 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define __STDC_LIMIT_MACROS 1 +#include +#include +#include +#include "LV2URIMap.hpp" + +using namespace std; + +namespace Ingen { +namespace Shared { + + +LV2URIMap::LV2URIMap() + : next_uri_id(1) +{ + uri_map_feature_data.uri_to_id = &LV2URIMap::uri_map_uri_to_id; + uri_map_feature_data.callback_data = this; + uri_map_feature.URI = LV2_URI_MAP_URI; + uri_map_feature.data = &uri_map_feature_data; +} + + +uint32_t +LV2URIMap::uri_to_id(const char* map, + const char* uri) +{ + return uri_map_uri_to_id(this, map, uri); +} + + +uint32_t +LV2URIMap::uri_map_uri_to_id(LV2_URI_Map_Callback_Data callback_data, + const char* map, + const char* uri) +{ + // TODO: map ignored, < UINT16_MAX assumed + + LV2URIMap* me = (LV2URIMap*)callback_data; + uint32_t ret = 0; + + URIMap::iterator i = me->uri_map.find(uri); + if (i != me->uri_map.end()) { + ret = i->second; + } else { + ret = me->next_uri_id++; + me->uri_map.insert(make_pair(string(uri), ret)); + } + + /*cout << "URI MAP (" << (map ? (void*)map : NULL) + << "): " << uri << " -> " << ret << endl;*/ + + assert(ret <= UINT16_MAX); + return ret; +} + + +} // namespace Shared +} // namespace Ingen diff --git a/src/shared/LV2URIMap.hpp b/src/shared/LV2URIMap.hpp new file mode 100644 index 00000000..35130066 --- /dev/null +++ b/src/shared/LV2URIMap.hpp @@ -0,0 +1,64 @@ +/* This file is part of Ingen. + * Copyright (C) 2008 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LV2URIMAP_HPP +#define LV2URIMAP_HPP + +#include CONFIG_H_PATH +#ifndef HAVE_SLV2 +#error "This file requires SLV2, but HAVE_SLV2 is not defined. Please report." +#endif + +#include +#include +#include +#include +#include "common/lv2ext/lv2_uri_map.h" + +namespace Ingen { +namespace Shared { + + +/** Implementation of the LV2 URI Map extension + */ +class LV2URIMap : public boost::noncopyable { +public: + LV2URIMap(); + + LV2_Feature* feature() { return &uri_map_feature; } + + uint32_t uri_to_id(const char* map, + const char* uri); + +private: + typedef std::map URIMap; + + static uint32_t uri_map_uri_to_id(LV2_URI_Map_Callback_Data callback_data, + const char* map, + const char* uri); + + LV2_Feature uri_map_feature; + LV2_URI_Map_Feature uri_map_feature_data; + URIMap uri_map; + uint32_t next_uri_id; +}; + + +} // namespace Shared +} // namespace Ingen + +#endif // LV2URIMAP_HPP diff --git a/src/shared/Makefile.am b/src/shared/Makefile.am new file mode 100644 index 00000000..c26c98a2 --- /dev/null +++ b/src/shared/Makefile.am @@ -0,0 +1,22 @@ +noinst_LTLIBRARIES = libingen_shared.la + +libingen_shared_la_CXXFLAGS = \ + @GLIBMM_CFLAGS@ \ + @INGEN_CFLAGS@ \ + @RAUL_CFLAGS@ \ + @REDLANDMM_CFLAGS@ \ + @SLV2_CFLAGS@ + +libingen_shared_la_SOURCES = \ + Builder.cpp \ + Builder.hpp \ + ClashAvoider.cpp \ + ClashAvoider.hpp \ + LV2Features.cpp \ + LV2Features.hpp \ + LV2URIMap.cpp \ + LV2URIMap.hpp \ + OSCSender.cpp \ + OSCSender.hpp \ + Store.cpp \ + Store.hpp diff --git a/src/shared/OSCSender.cpp b/src/shared/OSCSender.cpp new file mode 100644 index 00000000..f366b1a4 --- /dev/null +++ b/src/shared/OSCSender.cpp @@ -0,0 +1,125 @@ +/* This file is part of Ingen. + * Copyright (C) 2008 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "OSCSender.hpp" +#include +#include +#include + +using namespace std; + +namespace Ingen { +namespace Shared { + + +OSCSender::OSCSender() + : _transfer(NULL) + , _address(NULL) + , _enabled(true) +{ +} + + +void +OSCSender::bundle_begin() +{ + assert(!_transfer); + lo_timetag t; + lo_timetag_now(&t); + _transfer = lo_bundle_new(t); + _send_state = SendingBundle; +} + + +void +OSCSender::bundle_end() +{ + transfer_end(); +} + + +void +OSCSender::transfer_begin() +{ + assert(!_transfer); + lo_timetag t; + lo_timetag_now(&t); + _transfer = lo_bundle_new(t); + _send_state = SendingTransfer; +} + + +void +OSCSender::transfer_end() +{ + assert(_transfer); + lo_send_bundle(_address, _transfer); + lo_bundle_free(_transfer); + _transfer = NULL; + _send_state = Immediate; +} + + +int +OSCSender::send(const char *path, const char *types, ...) +{ + if (!_enabled) + return 0; + + va_list args; + va_start(args, types); + + lo_message msg = lo_message_new(); + int ret = lo_message_add_varargs(msg, types, args); + + if (!ret) + send_message(path, msg); + + va_end(args); + + return ret; +} + + +void +OSCSender::send_message(const char* path, lo_message msg) +{ + // FIXME: size? liblo doesn't export this. + // Don't want to exceed max UDP packet size (good default value?) + static const size_t MAX_BUNDLE_SIZE = 1024; + + if (!_enabled) + return; + + if (_transfer) { + if (lo_bundle_length(_transfer) + lo_message_length(msg, path) > MAX_BUNDLE_SIZE) { + if (_send_state == SendingBundle) + cerr << "WARNING: Maximum bundle size reached, bundle split" << endl; + lo_send_bundle(_address, _transfer); + lo_timetag t; + lo_timetag_now(&t); + _transfer = lo_bundle_new(t); + } + lo_bundle_add_message(_transfer, path, msg); + + } else { + lo_send_message(_address, path, msg); + } +} + +} // namespace Shared +} // namespace Ingen diff --git a/src/shared/OSCSender.hpp b/src/shared/OSCSender.hpp new file mode 100644 index 00000000..da91caed --- /dev/null +++ b/src/shared/OSCSender.hpp @@ -0,0 +1,59 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef OSCSENDER_H +#define OSCSENDER_H + +#include +#include + +namespace Ingen { +namespace Shared { + +class OSCSender { +public: + OSCSender(); + virtual ~OSCSender() {} + + lo_address address() const { return _address; } + + // Message bundling + void bundle_begin(); + void bundle_end(); + + // Transfers (loose bundling) + void transfer_begin(); + void transfer_end(); + +protected: + int send(const char *path, const char *types, ...); + void send_message(const char* path, lo_message m); + + enum SendState { Immediate, SendingBundle, SendingTransfer }; + + SendState _send_state; + lo_bundle _transfer; + lo_address _address; + bool _enabled; +}; + + +} // namespace Shared +} // namespace Ingen + +#endif // OSCSENDER_H + diff --git a/src/shared/Store.cpp b/src/shared/Store.cpp new file mode 100644 index 00000000..9f0f3624 --- /dev/null +++ b/src/shared/Store.cpp @@ -0,0 +1,108 @@ +/* This file is part of Ingen. + * Copyright (C) 2008 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "common/interface/Node.hpp" +#include "common/interface/Port.hpp" +#include "Store.hpp" + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Shared { + + +void +Store::add(GraphObject* o) +{ + if (find(o->path()) != end()) { + cerr << "[Store] ERROR: Attempt to add duplicate object " << o->path() << endl; + return; + } + + insert(make_pair(o->path(), o)); + + Node* node = dynamic_cast(o); + if (node) { + for (uint32_t i=0; i < node->num_ports(); ++i) { + add(node->port(i)); + } + } +} + + +Store::const_iterator +Store::children_begin(SharedPtr o) const +{ + const_iterator parent = find(o->path()); + assert(parent != end()); + ++parent; + return parent; +} + + +Store::const_iterator +Store::children_end(SharedPtr o) const +{ + const_iterator parent = find(o->path()); + assert(parent != end()); + return find_descendants_end(parent); +} + + +SharedPtr +Store::find_child(SharedPtr parent, const string& child_name) const +{ + const_iterator pi = find(parent->path()); + assert(pi != end()); + const_iterator children_end = find_descendants_end(pi); + const_iterator child = find(pi, children_end, parent->path().base() + child_name); + if (child != end()) + return child->second; + else + return SharedPtr(); +} + + +unsigned +Store::child_name_offset(const Raul::Path& parent, + const Raul::Symbol& symbol, + bool allow_zero) +{ + unsigned offset = 0; + + while (true) { + std::stringstream ss; + ss << symbol; + if (offset > 0) + ss << "_" << offset; + if (find(parent.base() + ss.str()) == end() && (allow_zero || offset > 0)) + break; + else if (offset == 0) + offset = 2; + else + ++offset; + } + + return offset; +} + + +} // namespace Shared +} // namespace Ingen diff --git a/src/shared/Store.hpp b/src/shared/Store.hpp new file mode 100644 index 00000000..27754345 --- /dev/null +++ b/src/shared/Store.hpp @@ -0,0 +1,60 @@ +/* This file is part of Ingen. + * Copyright (C) 2008 Dave Robillard + * + * Ingen 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. + * + * Ingen 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef COMMON_STORE_H +#define COMMON_STORE_H + +#include +#include +#include +#include "interface/GraphObject.hpp" + +using Raul::PathTable; + +namespace Ingen { +namespace Shared { + + +class Store : public Raul::PathTable< SharedPtr > { +public: + virtual ~Store() {} + + virtual void add(Shared::GraphObject* o); + + typedef Raul::Table< Raul::Path, SharedPtr > Objects; + + const_iterator children_begin(SharedPtr o) const; + const_iterator children_end(SharedPtr o) const; + + SharedPtr find_child(SharedPtr parent, + const std::string& child_name) const; + + unsigned child_name_offset(const Raul::Path& parent, + const Raul::Symbol& symbol, + bool allow_zero=true); + + Glib::RWLock& lock() { return _lock; } + +private: + Glib::RWLock _lock; +}; + + +} // namespace Shared +} // namespace Ingen + +#endif // COMMON_STORE_H diff --git a/src/shared/wscript b/src/shared/wscript new file mode 100644 index 00000000..1974a827 --- /dev/null +++ b/src/shared/wscript @@ -0,0 +1,19 @@ +#!/usr/bin/env python +import Params + +def build(bld): + obj = bld.create_obj('cpp', 'shlib') + obj.source = ''' + Builder.cpp + ClashAvoider.cpp + LV2Features.cpp + LV2URIMap.cpp + OSCSender.cpp + Store.cpp + ''' + obj.includes = ['..', '../../common', '../..'] + obj.name = 'libingen_shared' + obj.target = 'ingen_shared' + obj.uselib = 'GLIBMM SLV2 RAUL REDLANDMM' + obj.vnum = '0.0.0' + -- cgit v1.2.1