From 138a87e915ad3aff184730415105f94c874174bf Mon Sep 17 00:00:00 2001 From: David Robillard Date: Wed, 20 Apr 2011 16:26:40 +0000 Subject: Rename Ingen::Engine to Ingen::Server (hopefully avoid odd name clases and fix #675). git-svn-id: http://svn.drobilla.net/lad/trunk/ingen@3184 a436a847-0d15-0410-975c-d299462d15a1 --- src/bindings/Client.hpp | 2 +- src/bindings/ingen_bindings.cpp | 2 +- src/client/ClientStore.cpp | 2 +- src/client/ClientStore.hpp | 6 +- src/client/HTTPEngineSender.cpp | 4 +- src/client/HTTPEngineSender.hpp | 8 +- src/client/OSCEngineSender.cpp | 4 +- src/client/OSCEngineSender.hpp | 8 +- src/client/PluginModel.hpp | 2 +- src/client/PluginUI.hpp | 2 +- src/client/ThreadedSigClientInterface.hpp | 2 +- src/client/ingen_client.cpp | 8 +- src/engine/AudioBuffer.cpp | 207 ----------- src/engine/AudioBuffer.hpp | 109 ------ src/engine/Buffer.hpp | 97 ----- src/engine/BufferFactory.cpp | 163 --------- src/engine/BufferFactory.hpp | 92 ----- src/engine/ClientBroadcaster.cpp | 116 ------ src/engine/ClientBroadcaster.hpp | 131 ------- src/engine/CompiledPatch.hpp | 76 ---- src/engine/ConnectionImpl.cpp | 153 -------- src/engine/ConnectionImpl.hpp | 110 ------ src/engine/Context.hpp | 107 ------ src/engine/ControlBindings.cpp | 384 -------------------- src/engine/ControlBindings.hpp | 102 ------ src/engine/Driver.hpp | 123 ------- src/engine/DuplexPort.cpp | 99 ----- src/engine/DuplexPort.hpp | 70 ---- src/engine/Engine.cpp | 225 ------------ src/engine/Engine.hpp | 115 ------ src/engine/EngineStore.cpp | 160 -------- src/engine/EngineStore.hpp | 65 ---- src/engine/Event.cpp | 56 --- src/engine/Event.hpp | 80 ---- src/engine/EventBuffer.cpp | 215 ----------- src/engine/EventBuffer.hpp | 86 ----- src/engine/EventSink.cpp | 62 ---- src/engine/EventSink.hpp | 59 --- src/engine/EventSource.cpp | 127 ------- src/engine/EventSource.hpp | 73 ---- src/engine/GraphObjectImpl.cpp | 73 ---- src/engine/GraphObjectImpl.hpp | 113 ------ src/engine/HTTPClientSender.cpp | 146 -------- src/engine/HTTPClientSender.hpp | 107 ------ src/engine/HTTPEngineReceiver.cpp | 230 ------------ src/engine/HTTPEngineReceiver.hpp | 64 ---- src/engine/InputPort.cpp | 229 ------------ src/engine/InputPort.hpp | 93 ----- src/engine/InternalPlugin.cpp | 72 ---- src/engine/InternalPlugin.hpp | 63 ---- src/engine/JackDriver.cpp | 561 ---------------------------- src/engine/JackDriver.hpp | 183 ---------- src/engine/LV2BlobFeature.hpp | 66 ---- src/engine/LV2EventFeature.hpp | 54 --- src/engine/LV2Info.cpp | 77 ---- src/engine/LV2Info.hpp | 62 ---- src/engine/LV2Node.cpp | 410 --------------------- src/engine/LV2Node.hpp | 82 ----- src/engine/LV2Plugin.cpp | 110 ------ src/engine/LV2Plugin.hpp | 76 ---- src/engine/LV2RequestRunFeature.hpp | 84 ----- src/engine/LV2ResizeFeature.hpp | 73 ---- src/engine/MessageContext.cpp | 126 ------- src/engine/MessageContext.hpp | 114 ------ src/engine/NodeFactory.cpp | 148 -------- src/engine/NodeFactory.hpp | 76 ---- src/engine/NodeImpl.cpp | 264 -------------- src/engine/NodeImpl.hpp | 223 ------------ src/engine/OSCClientSender.cpp | 247 ------------- src/engine/OSCClientSender.hpp | 108 ------ src/engine/OSCEngineReceiver.cpp | 586 ------------------------------ src/engine/OSCEngineReceiver.hpp | 118 ------ src/engine/ObjectBuffer.cpp | 147 -------- src/engine/ObjectBuffer.hpp | 56 --- src/engine/ObjectSender.cpp | 149 -------- src/engine/ObjectSender.hpp | 66 ---- src/engine/OutputPort.cpp | 76 ---- src/engine/OutputPort.hpp | 65 ---- src/engine/PatchImpl.cpp | 470 ------------------------ src/engine/PatchImpl.hpp | 165 --------- src/engine/PatchPlugin.hpp | 67 ---- src/engine/PluginImpl.cpp | 50 --- src/engine/PluginImpl.hpp | 88 ----- src/engine/PortImpl.cpp | 251 ------------- src/engine/PortImpl.hpp | 174 --------- src/engine/PostProcessor.cpp | 92 ----- src/engine/PostProcessor.hpp | 68 ---- src/engine/ProcessContext.cpp | 38 -- src/engine/ProcessContext.hpp | 54 --- src/engine/ProcessSlave.cpp | 73 ---- src/engine/ProcessSlave.hpp | 104 ------ src/engine/QueuedEngineInterface.cpp | 223 ------------ src/engine/QueuedEngineInterface.hpp | 116 ------ src/engine/QueuedEvent.cpp | 49 --- src/engine/QueuedEvent.hpp | 77 ---- src/engine/Request.hpp | 81 ----- src/engine/ThreadManager.hpp | 56 --- src/engine/events.hpp | 41 --- src/engine/events/Connect.cpp | 204 ----------- src/engine/events/Connect.hpp | 88 ----- src/engine/events/CreateNode.cpp | 146 -------- src/engine/events/CreateNode.hpp | 71 ---- src/engine/events/CreatePatch.cpp | 164 --------- src/engine/events/CreatePatch.hpp | 67 ---- src/engine/events/CreatePort.cpp | 192 ---------- src/engine/events/CreatePort.hpp | 81 ----- src/engine/events/Deactivate.hpp | 49 --- src/engine/events/Delete.cpp | 213 ----------- src/engine/events/Delete.hpp | 95 ----- src/engine/events/Disconnect.cpp | 269 -------------- src/engine/events/Disconnect.hpp | 106 ------ src/engine/events/DisconnectAll.cpp | 189 ---------- src/engine/events/DisconnectAll.hpp | 93 ----- src/engine/events/Get.cpp | 84 ----- src/engine/events/Get.hpp | 60 --- src/engine/events/Move.cpp | 130 ------- src/engine/events/Move.hpp | 79 ---- src/engine/events/Ping.hpp | 51 --- src/engine/events/RegisterClient.cpp | 57 --- src/engine/events/RegisterClient.hpp | 54 --- src/engine/events/RequestMetadata.cpp | 137 ------- src/engine/events/RequestMetadata.hpp | 79 ---- src/engine/events/SendBinding.cpp | 55 --- src/engine/events/SendBinding.hpp | 86 ----- src/engine/events/SendPortActivity.cpp | 36 -- src/engine/events/SendPortActivity.hpp | 69 ---- src/engine/events/SendPortValue.cpp | 42 --- src/engine/events/SendPortValue.hpp | 82 ----- src/engine/events/SetMetadata.cpp | 380 ------------------- src/engine/events/SetMetadata.hpp | 124 ------- src/engine/events/SetPortValue.cpp | 223 ------------ src/engine/events/SetPortValue.hpp | 83 ----- src/engine/events/UnregisterClient.cpp | 48 --- src/engine/events/UnregisterClient.hpp | 50 --- src/engine/ingen_engine.cpp | 48 --- src/engine/ingen_http.cpp | 45 --- src/engine/ingen_jack.cpp | 45 --- src/engine/ingen_lv2.cpp | 422 --------------------- src/engine/ingen_osc.cpp | 46 --- src/engine/internals/Controller.cpp | 147 -------- src/engine/internals/Controller.hpp | 74 ---- src/engine/internals/Delay.cpp | 208 ----------- src/engine/internals/Delay.hpp | 78 ---- src/engine/internals/Note.cpp | 416 --------------------- src/engine/internals/Note.hpp | 101 ----- src/engine/internals/Trigger.cpp | 168 --------- src/engine/internals/Trigger.hpp | 77 ---- src/engine/mix.hpp | 91 ----- src/engine/types.hpp | 29 -- src/engine/util.hpp | 90 ----- src/engine/wscript | 128 ------- src/gui/App.cpp | 6 +- src/gui/App.hpp | 4 +- src/gui/ConnectWindow.cpp | 18 +- src/gui/ConnectWindow.hpp | 2 +- src/gui/ControlPanel.cpp | 2 +- src/gui/Controls.cpp | 2 +- src/gui/LoadPatchWindow.cpp | 2 +- src/gui/LoadPluginWindow.cpp | 2 +- src/gui/LoadRemotePatchWindow.cpp | 2 +- src/gui/NewSubpatchWindow.cpp | 2 +- src/gui/NodeControlWindow.cpp | 2 +- src/gui/NodeMenu.cpp | 2 +- src/gui/NodeModule.cpp | 2 +- src/gui/ObjectMenu.cpp | 2 +- src/gui/PatchCanvas.cpp | 2 +- src/gui/PatchPortModule.cpp | 2 +- src/gui/PatchTreeWindow.cpp | 2 +- src/gui/PatchView.cpp | 2 +- src/gui/PatchWindow.cpp | 2 +- src/gui/Port.cpp | 2 +- src/gui/PortMenu.cpp | 2 +- src/gui/PortPropertiesWindow.cpp | 2 +- src/gui/RenameWindow.cpp | 2 +- src/gui/SubpatchModule.cpp | 2 +- src/gui/ThreadedLoader.cpp | 2 +- src/gui/ThreadedLoader.hpp | 6 +- src/gui/UploadPatchWindow.cpp | 2 +- src/ingen/main.cpp | 4 +- src/serialisation/Parser.cpp | 2 +- src/serialisation/Serialiser.cpp | 2 +- src/server/AudioBuffer.cpp | 207 +++++++++++ src/server/AudioBuffer.hpp | 109 ++++++ src/server/Buffer.hpp | 97 +++++ src/server/BufferFactory.cpp | 163 +++++++++ src/server/BufferFactory.hpp | 92 +++++ src/server/ClientBroadcaster.cpp | 116 ++++++ src/server/ClientBroadcaster.hpp | 131 +++++++ src/server/CompiledPatch.hpp | 76 ++++ src/server/ConnectionImpl.cpp | 153 ++++++++ src/server/ConnectionImpl.hpp | 110 ++++++ src/server/Context.hpp | 107 ++++++ src/server/ControlBindings.cpp | 384 ++++++++++++++++++++ src/server/ControlBindings.hpp | 102 ++++++ src/server/Driver.hpp | 123 +++++++ src/server/DuplexPort.cpp | 99 +++++ src/server/DuplexPort.hpp | 70 ++++ src/server/Engine.cpp | 225 ++++++++++++ src/server/Engine.hpp | 115 ++++++ src/server/EngineStore.cpp | 160 ++++++++ src/server/EngineStore.hpp | 65 ++++ src/server/Event.cpp | 56 +++ src/server/Event.hpp | 80 ++++ src/server/EventBuffer.cpp | 215 +++++++++++ src/server/EventBuffer.hpp | 86 +++++ src/server/EventSink.cpp | 62 ++++ src/server/EventSink.hpp | 59 +++ src/server/EventSource.cpp | 127 +++++++ src/server/EventSource.hpp | 73 ++++ src/server/GraphObjectImpl.cpp | 73 ++++ src/server/GraphObjectImpl.hpp | 113 ++++++ src/server/HTTPClientSender.cpp | 146 ++++++++ src/server/HTTPClientSender.hpp | 107 ++++++ src/server/HTTPEngineReceiver.cpp | 230 ++++++++++++ src/server/HTTPEngineReceiver.hpp | 64 ++++ src/server/InputPort.cpp | 229 ++++++++++++ src/server/InputPort.hpp | 93 +++++ src/server/InternalPlugin.cpp | 72 ++++ src/server/InternalPlugin.hpp | 63 ++++ src/server/JackDriver.cpp | 561 ++++++++++++++++++++++++++++ src/server/JackDriver.hpp | 183 ++++++++++ src/server/LV2BlobFeature.hpp | 66 ++++ src/server/LV2EventFeature.hpp | 54 +++ src/server/LV2Info.cpp | 77 ++++ src/server/LV2Info.hpp | 62 ++++ src/server/LV2Node.cpp | 410 +++++++++++++++++++++ src/server/LV2Node.hpp | 82 +++++ src/server/LV2Plugin.cpp | 110 ++++++ src/server/LV2Plugin.hpp | 76 ++++ src/server/LV2RequestRunFeature.hpp | 84 +++++ src/server/LV2ResizeFeature.hpp | 73 ++++ src/server/MessageContext.cpp | 126 +++++++ src/server/MessageContext.hpp | 114 ++++++ src/server/NodeFactory.cpp | 148 ++++++++ src/server/NodeFactory.hpp | 76 ++++ src/server/NodeImpl.cpp | 264 ++++++++++++++ src/server/NodeImpl.hpp | 223 ++++++++++++ src/server/OSCClientSender.cpp | 247 +++++++++++++ src/server/OSCClientSender.hpp | 108 ++++++ src/server/OSCEngineReceiver.cpp | 586 ++++++++++++++++++++++++++++++ src/server/OSCEngineReceiver.hpp | 118 ++++++ src/server/ObjectBuffer.cpp | 147 ++++++++ src/server/ObjectBuffer.hpp | 56 +++ src/server/ObjectSender.cpp | 149 ++++++++ src/server/ObjectSender.hpp | 66 ++++ src/server/OutputPort.cpp | 76 ++++ src/server/OutputPort.hpp | 65 ++++ src/server/PatchImpl.cpp | 470 ++++++++++++++++++++++++ src/server/PatchImpl.hpp | 165 +++++++++ src/server/PatchPlugin.hpp | 67 ++++ src/server/PluginImpl.cpp | 50 +++ src/server/PluginImpl.hpp | 88 +++++ src/server/PortImpl.cpp | 251 +++++++++++++ src/server/PortImpl.hpp | 174 +++++++++ src/server/PostProcessor.cpp | 92 +++++ src/server/PostProcessor.hpp | 68 ++++ src/server/ProcessContext.cpp | 38 ++ src/server/ProcessContext.hpp | 54 +++ src/server/ProcessSlave.cpp | 73 ++++ src/server/ProcessSlave.hpp | 104 ++++++ src/server/QueuedEngineInterface.cpp | 223 ++++++++++++ src/server/QueuedEngineInterface.hpp | 116 ++++++ src/server/QueuedEvent.cpp | 49 +++ src/server/QueuedEvent.hpp | 77 ++++ src/server/Request.hpp | 81 +++++ src/server/ThreadManager.hpp | 56 +++ src/server/events.hpp | 41 +++ src/server/events/Connect.cpp | 204 +++++++++++ src/server/events/Connect.hpp | 88 +++++ src/server/events/CreateNode.cpp | 146 ++++++++ src/server/events/CreateNode.hpp | 71 ++++ src/server/events/CreatePatch.cpp | 164 +++++++++ src/server/events/CreatePatch.hpp | 67 ++++ src/server/events/CreatePort.cpp | 192 ++++++++++ src/server/events/CreatePort.hpp | 81 +++++ src/server/events/Deactivate.hpp | 49 +++ src/server/events/Delete.cpp | 213 +++++++++++ src/server/events/Delete.hpp | 95 +++++ src/server/events/Disconnect.cpp | 269 ++++++++++++++ src/server/events/Disconnect.hpp | 106 ++++++ src/server/events/DisconnectAll.cpp | 189 ++++++++++ src/server/events/DisconnectAll.hpp | 93 +++++ src/server/events/Get.cpp | 84 +++++ src/server/events/Get.hpp | 60 +++ src/server/events/Move.cpp | 130 +++++++ src/server/events/Move.hpp | 79 ++++ src/server/events/Ping.hpp | 51 +++ src/server/events/RegisterClient.cpp | 57 +++ src/server/events/RegisterClient.hpp | 54 +++ src/server/events/RequestMetadata.cpp | 137 +++++++ src/server/events/RequestMetadata.hpp | 79 ++++ src/server/events/SendBinding.cpp | 55 +++ src/server/events/SendBinding.hpp | 86 +++++ src/server/events/SendPortActivity.cpp | 36 ++ src/server/events/SendPortActivity.hpp | 69 ++++ src/server/events/SendPortValue.cpp | 42 +++ src/server/events/SendPortValue.hpp | 82 +++++ src/server/events/SetMetadata.cpp | 380 +++++++++++++++++++ src/server/events/SetMetadata.hpp | 124 +++++++ src/server/events/SetPortValue.cpp | 223 ++++++++++++ src/server/events/SetPortValue.hpp | 83 +++++ src/server/events/UnregisterClient.cpp | 48 +++ src/server/events/UnregisterClient.hpp | 50 +++ src/server/ingen_engine.cpp | 48 +++ src/server/ingen_http.cpp | 45 +++ src/server/ingen_jack.cpp | 45 +++ src/server/ingen_lv2.cpp | 422 +++++++++++++++++++++ src/server/ingen_osc.cpp | 46 +++ src/server/internals/Controller.cpp | 147 ++++++++ src/server/internals/Controller.hpp | 74 ++++ src/server/internals/Delay.cpp | 208 +++++++++++ src/server/internals/Delay.hpp | 78 ++++ src/server/internals/Note.cpp | 416 +++++++++++++++++++++ src/server/internals/Note.hpp | 101 +++++ src/server/internals/Trigger.cpp | 168 +++++++++ src/server/internals/Trigger.hpp | 77 ++++ src/server/mix.hpp | 91 +++++ src/server/types.hpp | 29 ++ src/server/util.hpp | 90 +++++ src/server/wscript | 128 +++++++ src/shared/World.cpp | 10 +- src/shared/World.hpp | 10 +- 322 files changed, 17724 insertions(+), 17724 deletions(-) delete mode 100644 src/engine/AudioBuffer.cpp delete mode 100644 src/engine/AudioBuffer.hpp delete mode 100644 src/engine/Buffer.hpp delete mode 100644 src/engine/BufferFactory.cpp delete mode 100644 src/engine/BufferFactory.hpp delete mode 100644 src/engine/ClientBroadcaster.cpp delete mode 100644 src/engine/ClientBroadcaster.hpp delete mode 100644 src/engine/CompiledPatch.hpp delete mode 100644 src/engine/ConnectionImpl.cpp delete mode 100644 src/engine/ConnectionImpl.hpp delete mode 100644 src/engine/Context.hpp delete mode 100644 src/engine/ControlBindings.cpp delete mode 100644 src/engine/ControlBindings.hpp delete mode 100644 src/engine/Driver.hpp delete mode 100644 src/engine/DuplexPort.cpp delete mode 100644 src/engine/DuplexPort.hpp delete mode 100644 src/engine/Engine.cpp delete mode 100644 src/engine/Engine.hpp delete mode 100644 src/engine/EngineStore.cpp delete mode 100644 src/engine/EngineStore.hpp delete mode 100644 src/engine/Event.cpp delete mode 100644 src/engine/Event.hpp delete mode 100644 src/engine/EventBuffer.cpp delete mode 100644 src/engine/EventBuffer.hpp delete mode 100644 src/engine/EventSink.cpp delete mode 100644 src/engine/EventSink.hpp delete mode 100644 src/engine/EventSource.cpp delete mode 100644 src/engine/EventSource.hpp delete mode 100644 src/engine/GraphObjectImpl.cpp delete mode 100644 src/engine/GraphObjectImpl.hpp delete mode 100644 src/engine/HTTPClientSender.cpp delete mode 100644 src/engine/HTTPClientSender.hpp delete mode 100644 src/engine/HTTPEngineReceiver.cpp delete mode 100644 src/engine/HTTPEngineReceiver.hpp delete mode 100644 src/engine/InputPort.cpp delete mode 100644 src/engine/InputPort.hpp delete mode 100644 src/engine/InternalPlugin.cpp delete mode 100644 src/engine/InternalPlugin.hpp delete mode 100644 src/engine/JackDriver.cpp delete mode 100644 src/engine/JackDriver.hpp delete mode 100644 src/engine/LV2BlobFeature.hpp delete mode 100644 src/engine/LV2EventFeature.hpp delete mode 100644 src/engine/LV2Info.cpp delete mode 100644 src/engine/LV2Info.hpp delete mode 100644 src/engine/LV2Node.cpp delete mode 100644 src/engine/LV2Node.hpp delete mode 100644 src/engine/LV2Plugin.cpp delete mode 100644 src/engine/LV2Plugin.hpp delete mode 100644 src/engine/LV2RequestRunFeature.hpp delete mode 100644 src/engine/LV2ResizeFeature.hpp delete mode 100644 src/engine/MessageContext.cpp delete mode 100644 src/engine/MessageContext.hpp delete mode 100644 src/engine/NodeFactory.cpp delete mode 100644 src/engine/NodeFactory.hpp delete mode 100644 src/engine/NodeImpl.cpp delete mode 100644 src/engine/NodeImpl.hpp delete mode 100644 src/engine/OSCClientSender.cpp delete mode 100644 src/engine/OSCClientSender.hpp delete mode 100644 src/engine/OSCEngineReceiver.cpp delete mode 100644 src/engine/OSCEngineReceiver.hpp delete mode 100644 src/engine/ObjectBuffer.cpp delete mode 100644 src/engine/ObjectBuffer.hpp delete mode 100644 src/engine/ObjectSender.cpp delete mode 100644 src/engine/ObjectSender.hpp delete mode 100644 src/engine/OutputPort.cpp delete mode 100644 src/engine/OutputPort.hpp delete mode 100644 src/engine/PatchImpl.cpp delete mode 100644 src/engine/PatchImpl.hpp delete mode 100644 src/engine/PatchPlugin.hpp delete mode 100644 src/engine/PluginImpl.cpp delete mode 100644 src/engine/PluginImpl.hpp delete mode 100644 src/engine/PortImpl.cpp delete mode 100644 src/engine/PortImpl.hpp delete mode 100644 src/engine/PostProcessor.cpp delete mode 100644 src/engine/PostProcessor.hpp delete mode 100644 src/engine/ProcessContext.cpp delete mode 100644 src/engine/ProcessContext.hpp delete mode 100644 src/engine/ProcessSlave.cpp delete mode 100644 src/engine/ProcessSlave.hpp delete mode 100644 src/engine/QueuedEngineInterface.cpp delete mode 100644 src/engine/QueuedEngineInterface.hpp delete mode 100644 src/engine/QueuedEvent.cpp delete mode 100644 src/engine/QueuedEvent.hpp delete mode 100644 src/engine/Request.hpp delete mode 100644 src/engine/ThreadManager.hpp delete mode 100644 src/engine/events.hpp delete mode 100644 src/engine/events/Connect.cpp delete mode 100644 src/engine/events/Connect.hpp delete mode 100644 src/engine/events/CreateNode.cpp delete mode 100644 src/engine/events/CreateNode.hpp delete mode 100644 src/engine/events/CreatePatch.cpp delete mode 100644 src/engine/events/CreatePatch.hpp delete mode 100644 src/engine/events/CreatePort.cpp delete mode 100644 src/engine/events/CreatePort.hpp delete mode 100644 src/engine/events/Deactivate.hpp delete mode 100644 src/engine/events/Delete.cpp delete mode 100644 src/engine/events/Delete.hpp delete mode 100644 src/engine/events/Disconnect.cpp delete mode 100644 src/engine/events/Disconnect.hpp delete mode 100644 src/engine/events/DisconnectAll.cpp delete mode 100644 src/engine/events/DisconnectAll.hpp delete mode 100644 src/engine/events/Get.cpp delete mode 100644 src/engine/events/Get.hpp delete mode 100644 src/engine/events/Move.cpp delete mode 100644 src/engine/events/Move.hpp delete mode 100644 src/engine/events/Ping.hpp delete mode 100644 src/engine/events/RegisterClient.cpp delete mode 100644 src/engine/events/RegisterClient.hpp delete mode 100644 src/engine/events/RequestMetadata.cpp delete mode 100644 src/engine/events/RequestMetadata.hpp delete mode 100644 src/engine/events/SendBinding.cpp delete mode 100644 src/engine/events/SendBinding.hpp delete mode 100644 src/engine/events/SendPortActivity.cpp delete mode 100644 src/engine/events/SendPortActivity.hpp delete mode 100644 src/engine/events/SendPortValue.cpp delete mode 100644 src/engine/events/SendPortValue.hpp delete mode 100644 src/engine/events/SetMetadata.cpp delete mode 100644 src/engine/events/SetMetadata.hpp delete mode 100644 src/engine/events/SetPortValue.cpp delete mode 100644 src/engine/events/SetPortValue.hpp delete mode 100644 src/engine/events/UnregisterClient.cpp delete mode 100644 src/engine/events/UnregisterClient.hpp delete mode 100644 src/engine/ingen_engine.cpp delete mode 100644 src/engine/ingen_http.cpp delete mode 100644 src/engine/ingen_jack.cpp delete mode 100644 src/engine/ingen_lv2.cpp delete mode 100644 src/engine/ingen_osc.cpp delete mode 100644 src/engine/internals/Controller.cpp delete mode 100644 src/engine/internals/Controller.hpp delete mode 100644 src/engine/internals/Delay.cpp delete mode 100644 src/engine/internals/Delay.hpp delete mode 100644 src/engine/internals/Note.cpp delete mode 100644 src/engine/internals/Note.hpp delete mode 100644 src/engine/internals/Trigger.cpp delete mode 100644 src/engine/internals/Trigger.hpp delete mode 100644 src/engine/mix.hpp delete mode 100644 src/engine/types.hpp delete mode 100644 src/engine/util.hpp delete mode 100644 src/engine/wscript create mode 100644 src/server/AudioBuffer.cpp create mode 100644 src/server/AudioBuffer.hpp create mode 100644 src/server/Buffer.hpp create mode 100644 src/server/BufferFactory.cpp create mode 100644 src/server/BufferFactory.hpp create mode 100644 src/server/ClientBroadcaster.cpp create mode 100644 src/server/ClientBroadcaster.hpp create mode 100644 src/server/CompiledPatch.hpp create mode 100644 src/server/ConnectionImpl.cpp create mode 100644 src/server/ConnectionImpl.hpp create mode 100644 src/server/Context.hpp create mode 100644 src/server/ControlBindings.cpp create mode 100644 src/server/ControlBindings.hpp create mode 100644 src/server/Driver.hpp create mode 100644 src/server/DuplexPort.cpp create mode 100644 src/server/DuplexPort.hpp create mode 100644 src/server/Engine.cpp create mode 100644 src/server/Engine.hpp create mode 100644 src/server/EngineStore.cpp create mode 100644 src/server/EngineStore.hpp create mode 100644 src/server/Event.cpp create mode 100644 src/server/Event.hpp create mode 100644 src/server/EventBuffer.cpp create mode 100644 src/server/EventBuffer.hpp create mode 100644 src/server/EventSink.cpp create mode 100644 src/server/EventSink.hpp create mode 100644 src/server/EventSource.cpp create mode 100644 src/server/EventSource.hpp create mode 100644 src/server/GraphObjectImpl.cpp create mode 100644 src/server/GraphObjectImpl.hpp create mode 100644 src/server/HTTPClientSender.cpp create mode 100644 src/server/HTTPClientSender.hpp create mode 100644 src/server/HTTPEngineReceiver.cpp create mode 100644 src/server/HTTPEngineReceiver.hpp create mode 100644 src/server/InputPort.cpp create mode 100644 src/server/InputPort.hpp create mode 100644 src/server/InternalPlugin.cpp create mode 100644 src/server/InternalPlugin.hpp create mode 100644 src/server/JackDriver.cpp create mode 100644 src/server/JackDriver.hpp create mode 100644 src/server/LV2BlobFeature.hpp create mode 100644 src/server/LV2EventFeature.hpp create mode 100644 src/server/LV2Info.cpp create mode 100644 src/server/LV2Info.hpp create mode 100644 src/server/LV2Node.cpp create mode 100644 src/server/LV2Node.hpp create mode 100644 src/server/LV2Plugin.cpp create mode 100644 src/server/LV2Plugin.hpp create mode 100644 src/server/LV2RequestRunFeature.hpp create mode 100644 src/server/LV2ResizeFeature.hpp create mode 100644 src/server/MessageContext.cpp create mode 100644 src/server/MessageContext.hpp create mode 100644 src/server/NodeFactory.cpp create mode 100644 src/server/NodeFactory.hpp create mode 100644 src/server/NodeImpl.cpp create mode 100644 src/server/NodeImpl.hpp create mode 100644 src/server/OSCClientSender.cpp create mode 100644 src/server/OSCClientSender.hpp create mode 100644 src/server/OSCEngineReceiver.cpp create mode 100644 src/server/OSCEngineReceiver.hpp create mode 100644 src/server/ObjectBuffer.cpp create mode 100644 src/server/ObjectBuffer.hpp create mode 100644 src/server/ObjectSender.cpp create mode 100644 src/server/ObjectSender.hpp create mode 100644 src/server/OutputPort.cpp create mode 100644 src/server/OutputPort.hpp create mode 100644 src/server/PatchImpl.cpp create mode 100644 src/server/PatchImpl.hpp create mode 100644 src/server/PatchPlugin.hpp create mode 100644 src/server/PluginImpl.cpp create mode 100644 src/server/PluginImpl.hpp create mode 100644 src/server/PortImpl.cpp create mode 100644 src/server/PortImpl.hpp create mode 100644 src/server/PostProcessor.cpp create mode 100644 src/server/PostProcessor.hpp create mode 100644 src/server/ProcessContext.cpp create mode 100644 src/server/ProcessContext.hpp create mode 100644 src/server/ProcessSlave.cpp create mode 100644 src/server/ProcessSlave.hpp create mode 100644 src/server/QueuedEngineInterface.cpp create mode 100644 src/server/QueuedEngineInterface.hpp create mode 100644 src/server/QueuedEvent.cpp create mode 100644 src/server/QueuedEvent.hpp create mode 100644 src/server/Request.hpp create mode 100644 src/server/ThreadManager.hpp create mode 100644 src/server/events.hpp create mode 100644 src/server/events/Connect.cpp create mode 100644 src/server/events/Connect.hpp create mode 100644 src/server/events/CreateNode.cpp create mode 100644 src/server/events/CreateNode.hpp create mode 100644 src/server/events/CreatePatch.cpp create mode 100644 src/server/events/CreatePatch.hpp create mode 100644 src/server/events/CreatePort.cpp create mode 100644 src/server/events/CreatePort.hpp create mode 100644 src/server/events/Deactivate.hpp create mode 100644 src/server/events/Delete.cpp create mode 100644 src/server/events/Delete.hpp create mode 100644 src/server/events/Disconnect.cpp create mode 100644 src/server/events/Disconnect.hpp create mode 100644 src/server/events/DisconnectAll.cpp create mode 100644 src/server/events/DisconnectAll.hpp create mode 100644 src/server/events/Get.cpp create mode 100644 src/server/events/Get.hpp create mode 100644 src/server/events/Move.cpp create mode 100644 src/server/events/Move.hpp create mode 100644 src/server/events/Ping.hpp create mode 100644 src/server/events/RegisterClient.cpp create mode 100644 src/server/events/RegisterClient.hpp create mode 100644 src/server/events/RequestMetadata.cpp create mode 100644 src/server/events/RequestMetadata.hpp create mode 100644 src/server/events/SendBinding.cpp create mode 100644 src/server/events/SendBinding.hpp create mode 100644 src/server/events/SendPortActivity.cpp create mode 100644 src/server/events/SendPortActivity.hpp create mode 100644 src/server/events/SendPortValue.cpp create mode 100644 src/server/events/SendPortValue.hpp create mode 100644 src/server/events/SetMetadata.cpp create mode 100644 src/server/events/SetMetadata.hpp create mode 100644 src/server/events/SetPortValue.cpp create mode 100644 src/server/events/SetPortValue.hpp create mode 100644 src/server/events/UnregisterClient.cpp create mode 100644 src/server/events/UnregisterClient.hpp create mode 100644 src/server/ingen_engine.cpp create mode 100644 src/server/ingen_http.cpp create mode 100644 src/server/ingen_jack.cpp create mode 100644 src/server/ingen_lv2.cpp create mode 100644 src/server/ingen_osc.cpp create mode 100644 src/server/internals/Controller.cpp create mode 100644 src/server/internals/Controller.hpp create mode 100644 src/server/internals/Delay.cpp create mode 100644 src/server/internals/Delay.hpp create mode 100644 src/server/internals/Note.cpp create mode 100644 src/server/internals/Note.hpp create mode 100644 src/server/internals/Trigger.cpp create mode 100644 src/server/internals/Trigger.hpp create mode 100644 src/server/mix.hpp create mode 100644 src/server/types.hpp create mode 100644 src/server/util.hpp create mode 100644 src/server/wscript (limited to 'src') diff --git a/src/bindings/Client.hpp b/src/bindings/Client.hpp index 988b941c..703b4199 100644 --- a/src/bindings/Client.hpp +++ b/src/bindings/Client.hpp @@ -7,7 +7,7 @@ class Client : public Ingen::ClientInterface { public: /** Wrapper for engine->register_client to appease SWIG */ - virtual void subscribe(Ingen::yEngineInterface* engine) { + virtual void subscribe(Ingen::yServerInterface* engine) { engine->register_client(this); } diff --git a/src/bindings/ingen_bindings.cpp b/src/bindings/ingen_bindings.cpp index 51c61a22..2e5b753d 100644 --- a/src/bindings/ingen_bindings.cpp +++ b/src/bindings/ingen_bindings.cpp @@ -1,7 +1,7 @@ #include "python2.4/Python.h" #include "raul/log.hpp" #include "ingen_bindings.hpp" -#include "engine/Engine.hpp" +#include "server/Engine.hpp" #include "shared/World.hpp" bool diff --git a/src/client/ClientStore.cpp b/src/client/ClientStore.cpp index 48fa0d1e..e647c130 100644 --- a/src/client/ClientStore.cpp +++ b/src/client/ClientStore.cpp @@ -43,7 +43,7 @@ using namespace Shared; namespace Client { ClientStore::ClientStore(SharedPtr uris, - SharedPtr engine, + SharedPtr engine, SharedPtr emitter) : _uris(uris) , _engine(engine) diff --git a/src/client/ClientStore.hpp b/src/client/ClientStore.hpp index d904cbf2..a5a3cbc1 100644 --- a/src/client/ClientStore.hpp +++ b/src/client/ClientStore.hpp @@ -26,7 +26,7 @@ #include -#include "ingen/EngineInterface.hpp" +#include "ingen/ServerInterface.hpp" #include "raul/Path.hpp" #include "raul/PathTable.hpp" #include "raul/TableImpl.hpp" @@ -57,7 +57,7 @@ class ClientStore : public Shared::Store public: ClientStore( SharedPtr uris, - SharedPtr engine=SharedPtr(), + SharedPtr engine=SharedPtr(), SharedPtr emitter=SharedPtr()); SharedPtr plugin(const Raul::URI& uri); @@ -127,7 +127,7 @@ private: const Raul::Path& dst_port_path); SharedPtr _uris; - SharedPtr _engine; + SharedPtr _engine; SharedPtr _emitter; SharedPtr _plugins; ///< Map, keyed by plugin URI diff --git a/src/client/HTTPEngineSender.cpp b/src/client/HTTPEngineSender.cpp index d9b477a6..96b6a6cf 100644 --- a/src/client/HTTPEngineSender.cpp +++ b/src/client/HTTPEngineSender.cpp @@ -58,13 +58,13 @@ HTTPEngineSender::attach(int32_t ping_id, bool block) HTTPClientReceiver::send(msg); } -/* *** EngineInterface implementation below here *** */ +/* *** ServerInterface 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. + * traversal. It is a parameter to remain compatible with ServerInterface. */ void HTTPEngineSender::register_client(ClientInterface* client) diff --git a/src/client/HTTPEngineSender.hpp b/src/client/HTTPEngineSender.hpp index 75e2a2f7..27c34a51 100644 --- a/src/client/HTTPEngineSender.hpp +++ b/src/client/HTTPEngineSender.hpp @@ -25,7 +25,7 @@ #include "raul/Path.hpp" #include "sord/sordmm.hpp" -#include "ingen/EngineInterface.hpp" +#include "ingen/ServerInterface.hpp" typedef struct _SoupSession SoupSession; @@ -39,12 +39,12 @@ class HTTPClientReceiver; /* HTTP (via libsoup) interface to the engine. * - * Clients can use this opaquely as an EngineInterface to control the engine + * Clients can use this opaquely as an ServerInterface to control the engine * over HTTP (whether over a network or not). * * \ingroup IngenClient */ -class HTTPEngineSender : public EngineInterface +class HTTPEngineSender : public ServerInterface { public: HTTPEngineSender(Shared::World* world, const Raul::URI& engine_url); @@ -60,7 +60,7 @@ public: void attach(int32_t ping_id, bool block); - /* *** EngineInterface implementation below here *** */ + /* *** ServerInterface implementation below here *** */ void enable() { _enabled = true; } void disable() { _enabled = false; } diff --git a/src/client/OSCEngineSender.cpp b/src/client/OSCEngineSender.cpp index 51e6f8c9..9c3b836c 100644 --- a/src/client/OSCEngineSender.cpp +++ b/src/client/OSCEngineSender.cpp @@ -78,13 +78,13 @@ OSCEngineSender::attach(int32_t ping_id, bool block) this->ping(); } -/* *** EngineInterface implementation below here *** */ +/* *** ServerInterface implementation below here *** */ /** Register with the engine via OSC. * * Note that this does not actually use 'client', 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. + * traversal. It is a parameter to remain compatible with ServerInterface. */ void OSCEngineSender::register_client(ClientInterface* client) diff --git a/src/client/OSCEngineSender.hpp b/src/client/OSCEngineSender.hpp index 041a16b8..a29694fb 100644 --- a/src/client/OSCEngineSender.hpp +++ b/src/client/OSCEngineSender.hpp @@ -25,7 +25,7 @@ #include -#include "ingen/EngineInterface.hpp" +#include "ingen/ServerInterface.hpp" #include "shared/OSCSender.hpp" namespace Ingen { @@ -34,12 +34,12 @@ namespace Client { /* OSC (via liblo) interface to the engine. * - * Clients can use this opaquely as an EngineInterface* to control the engine + * Clients can use this opaquely as an ServerInterface* to control the engine * over OSC (whether over a network or not, etc). * * \ingroup IngenClient */ -class OSCEngineSender : public EngineInterface, public Shared::OSCSender { +class OSCEngineSender : public ServerInterface, public Shared::OSCSender { public: OSCEngineSender(const Raul::URI& engine_url, size_t max_packet_size); @@ -61,7 +61,7 @@ public: void attach(int32_t ping_id, bool block); - /* *** EngineInterface implementation below here *** */ + /* *** ServerInterface implementation below here *** */ void enable() { _enabled = true; } void disable() { _enabled = false; } diff --git a/src/client/PluginModel.hpp b/src/client/PluginModel.hpp index f03edd94..0d72af12 100644 --- a/src/client/PluginModel.hpp +++ b/src/client/PluginModel.hpp @@ -30,7 +30,7 @@ #ifdef HAVE_SLV2 #include "slv2/slv2.h" #endif -#include "ingen/EngineInterface.hpp" +#include "ingen/ServerInterface.hpp" #include "ingen/Plugin.hpp" #include "shared/World.hpp" #include "shared/ResourceImpl.hpp" diff --git a/src/client/PluginUI.hpp b/src/client/PluginUI.hpp index 266756e9..e07da923 100644 --- a/src/client/PluginUI.hpp +++ b/src/client/PluginUI.hpp @@ -25,7 +25,7 @@ namespace Ingen { -class EngineInterface; +class ServerInterface; namespace Shared { class World; } diff --git a/src/client/ThreadedSigClientInterface.hpp b/src/client/ThreadedSigClientInterface.hpp index 87c2f1a5..4fe85209 100644 --- a/src/client/ThreadedSigClientInterface.hpp +++ b/src/client/ThreadedSigClientInterface.hpp @@ -32,7 +32,7 @@ typedef sigc::slot Closure; namespace Ingen { -class EngineInterface; +class ServerInterface; namespace Client { diff --git a/src/client/ingen_client.cpp b/src/client/ingen_client.cpp index 93b00e59..dc4d7f20 100644 --- a/src/client/ingen_client.cpp +++ b/src/client/ingen_client.cpp @@ -29,23 +29,23 @@ using namespace Ingen; #ifdef HAVE_LIBLO -SharedPtr +SharedPtr new_osc_interface(Ingen::Shared::World* world, const std::string& url) { Client::OSCEngineSender* oes = Client::OSCEngineSender::create( url, world->conf()->option("packet-size").get_int32()); oes->attach(rand(), true); - return SharedPtr(oes); + return SharedPtr(oes); } #endif #ifdef HAVE_SOUP -SharedPtr +SharedPtr new_http_interface(Ingen::Shared::World* world, const std::string& url) { Client::HTTPEngineSender* hes = new Client::HTTPEngineSender(world, url); hes->attach(rand(), true); - return SharedPtr(hes); + return SharedPtr(hes); } #endif diff --git a/src/engine/AudioBuffer.cpp b/src/engine/AudioBuffer.cpp deleted file mode 100644 index 172e41ac..00000000 --- a/src/engine/AudioBuffer.cpp +++ /dev/null @@ -1,207 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "raul/log.hpp" -#include "raul/SharedPtr.hpp" -#include "lv2/lv2plug.in/ns/ext/atom/atom.h" -#include "ingen-config.h" -#include "AudioBuffer.hpp" -#include "ProcessContext.hpp" -#include "LV2Features.hpp" - -using namespace std; -using namespace Raul; - -/* TODO: Be sure these functions are vectorized by GCC when its vectorizer - * stops sucking. Probably a good idea to inline them as well */ - -namespace Ingen { -namespace Engine { - -AudioBuffer::AudioBuffer(BufferFactory& bufs, PortType type, size_t size) - : ObjectBuffer(bufs, size) - , _state(OK) - , _set_value(0) - , _set_time(0) -{ - assert(size >= sizeof(LV2_Atom) + sizeof(Sample)); - assert(this->size() >= size); - assert(data()); - _type = type; - - // Control port / Single float object - if (type == PortType::CONTROL) { - atom()->type = 0;//map->float_type; - - // Audio port / Vector of float - } else { - assert(type == PortType::AUDIO); - atom()->type = 0;//map->vector_type; - LV2_Atom_Vector* body = (LV2_Atom_Vector*)atom()->body; - body->elem_count = size / sizeof(Sample); - body->elem_type = 0;//map->float_type; - } - /*debug << "Created Audio Buffer" << endl - << "\tobject @ " << (void*)atom() << endl - << "\tbody @ " << (void*)atom()->body - << "\t(offset " << (char*)atom()->body - (char*)atom() << ")" << endl - << "\tdata @ " << (void*)data() - << "\t(offset " << (char*)data() - (char*)atom() << ")" - << endl;*/ - - clear(); -} - -void -AudioBuffer::resize(size_t size) -{ - if (_type == PortType::AUDIO) { - ObjectBuffer::resize(size + sizeof(LV2_Atom_Vector)); - vector()->elem_count = size / sizeof(Sample); - } - clear(); -} - -/** Empty (ie zero) the buffer. - */ -void -AudioBuffer::clear() -{ - assert(nframes() != 0); - set_block(0, 0, nframes() - 1); - _state = OK; -} - -/** 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 (is_control()) - time = cycle_start; - - const FrameTime offset = time - cycle_start; - assert(nframes() != 0); - assert(offset <= nframes()); - - if (offset < nframes()) { - set_block(val, offset, nframes() - 1); - - if (offset == 0) - _state = OK; - else - _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 < nframes()); - - Sample* const buf = data(); - assert(buf); - - for (size_t i = start_offset; i <= end_offset; ++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 Sample* src, size_t start_sample, size_t end_sample) -{ - assert(end_sample >= start_sample); - assert(nframes() != 0); - - Sample* const buf = data(); - assert(buf); - - const size_t copy_end = std::min(end_sample, (size_t)nframes() - 1); - for (size_t i = start_sample; i <= copy_end; ++i) - buf[i] = src[i]; -} - -void -AudioBuffer::copy(Context& context, const Buffer* src) -{ - const AudioBuffer* src_abuf = dynamic_cast(src); - if (!src_abuf) { - clear(); - return; - } - - // Control => Control - if (src_abuf->is_control() == is_control()) { - ObjectBuffer::copy(context, src); - - // Audio => Audio - } else if (!src_abuf->is_control() && !is_control()) { - copy(src_abuf->data(), - context.offset(), context.offset() + context.nframes() - 1); - - // Audio => Control - } else if (!src_abuf->is_control() && is_control()) { - data()[0] = src_abuf->data()[context.offset()]; - - // Control => Audio - } else if (src_abuf->is_control() && !is_control()) { - data()[context.offset()] = src_abuf->data()[0]; - - // Control => Audio or Audio => Control - } else { - set_block(src_abuf->data()[0], 0, nframes()); - } -} - -void -AudioBuffer::prepare_read(Context& context) -{ - assert(nframes() != 0); - switch (_state) { - case HALF_SET_CYCLE_1: - if (context.start() > _set_time) - _state = HALF_SET_CYCLE_2; - break; - case HALF_SET_CYCLE_2: - set_block(_set_value, 0, nframes() - 1); - _state = OK; - break; - default: - break; - } -} - -} // namespace Engine -} // namespace Ingen diff --git a/src/engine/AudioBuffer.hpp b/src/engine/AudioBuffer.hpp deleted file mode 100644 index d0c562a9..00000000 --- a/src/engine/AudioBuffer.hpp +++ /dev/null @@ -1,109 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_AUDIOBUFFER_HPP -#define INGEN_ENGINE_AUDIOBUFFER_HPP - -#include -#include -#include -#include "types.hpp" -#include "ObjectBuffer.hpp" -#include "Context.hpp" - -using namespace std; - -namespace Ingen { -namespace Engine { - -class AudioBuffer : public ObjectBuffer -{ -public: - AudioBuffer(BufferFactory& bufs, PortType type, 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 copy(const Sample* src, size_t start_sample, size_t end_sample); - void copy(Context& context, const Buffer* src); - void accumulate(Context& context, const AudioBuffer* src); - - inline bool is_control() const { return _type.symbol() == PortType::CONTROL; } - - inline Sample* data() const { - return (is_control()) - ? (Sample*)atom()->body - : (Sample*)(atom()->body + sizeof(LV2_Atom_Vector)); - } - - inline SampleCount nframes() const { - return (is_control()) - ? 1 - : (_size - sizeof(LV2_Atom) - sizeof(LV2_Atom_Vector)) / sizeof(Sample); - } - - inline Sample& value_at(size_t offset) const - { assert(offset < nframes()); return data()[offset]; } - - void prepare_read(Context& context); - void prepare_write(Context& context) {} - - void resize(size_t size); - -private: - enum State { OK, HALF_SET_CYCLE_1, HALF_SET_CYCLE_2 }; - - LV2_Atom_Vector* vector() { return(LV2_Atom_Vector*)atom()->body; } - - State _state; ///< State of buffer for setting values next cycle - Sample _set_value; ///< Value set by set_value (for completing the set next cycle) - FrameTime _set_time; ///< Time _set_value was set (to reset next cycle) -}; - -/** Accumulate a block of @a src into buffer. - */ -inline void -AudioBuffer::accumulate(Context& context, const AudioBuffer* const src) -{ - Sample* const buf = data(); - const Sample* const src_buf = src->data(); - - if (is_control()) { - if (src->is_control()) { // control => control - buf[0] += src_buf[0]; - } else { // audio => control - buf[0] += src_buf[context.offset()]; - } - } else { - const SampleCount end = context.offset() + context.nframes(); - if (src->is_control()) { // control => audio - for (SampleCount i = context.offset(); i < end; ++i) { - buf[i] += src_buf[0]; - } - } else { // audio => audio - for (SampleCount i = context.offset(); i < end; ++i) { - buf[i] += src_buf[i]; - } - } - } -} - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_AUDIOBUFFER_HPP diff --git a/src/engine/Buffer.hpp b/src/engine/Buffer.hpp deleted file mode 100644 index 56682228..00000000 --- a/src/engine/Buffer.hpp +++ /dev/null @@ -1,97 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_BUFFER_HPP -#define INGEN_ENGINE_BUFFER_HPP - -#include -#include -#include -#include -#include "raul/Deletable.hpp" -#include "raul/SharedPtr.hpp" -#include "ingen/PortType.hpp" -#include "types.hpp" -#include "BufferFactory.hpp" - -namespace Ingen { -namespace Engine { - -class Context; -class Engine; -class BufferFactory; - -class Buffer : public boost::noncopyable, public Raul::Deletable -{ -public: - Buffer(BufferFactory& bufs, PortType type, size_t size) - : _factory(bufs) - , _type(type) - , _size(size) - , _next(NULL) - , _refs(0) - {} - - /** Clear contents and reset state */ - virtual void clear() = 0; - - virtual void resize(size_t size) { _size = size; } - - virtual void* port_data(PortType port_type, SampleCount offset=0) = 0; - virtual const void* port_data(PortType port_type, SampleCount offset=0) const = 0; - - /** Rewind (ie reset read pointer), but leave contents unchanged */ - virtual void rewind() const {} - - virtual void copy(Context& context, const Buffer* src) = 0; - - virtual void prepare_read(Context& context) {} - virtual void prepare_write(Context& context) {} - - PortType type() const { return _type; } - size_t size() const { return _size; } - - inline void ref() { ++_refs; } - - inline void deref() { - assert(_refs > 0); - if ((--_refs) == 0) - _factory.recycle(this); - } - -protected: - BufferFactory& _factory; - PortType _type; - size_t _size; - - friend class BufferFactory; - virtual ~Buffer() {} - -private: - Buffer* _next; ///< Intrusive linked list for BufferFactory - size_t _refs; ///< Intrusive reference count for intrusive_ptr -}; - -} // namespace Engine -} // namespace Ingen - -namespace boost { -inline void intrusive_ptr_add_ref(Ingen::Engine::Buffer* b) { b->ref(); } -inline void intrusive_ptr_release(Ingen::Engine::Buffer* b) { b->deref(); } -} - -#endif // INGEN_ENGINE_BUFFER_HPP diff --git a/src/engine/BufferFactory.cpp b/src/engine/BufferFactory.cpp deleted file mode 100644 index 7aac798a..00000000 --- a/src/engine/BufferFactory.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "raul/log.hpp" -#include "shared/LV2URIMap.hpp" -#include "AudioBuffer.hpp" -#include "EventBuffer.hpp" -#include "ObjectBuffer.hpp" -#include "BufferFactory.hpp" -#include "Engine.hpp" -#include "Driver.hpp" -#include "ThreadManager.hpp" - -using namespace Raul; - -namespace Ingen { -namespace Engine { - -static const size_t EVENT_BYTES_PER_FRAME = 4; // FIXME - -BufferFactory::BufferFactory(Engine& engine, - SharedPtr uris) - : _engine(engine) - , _uris(uris) - , _silent_buffer(NULL) -{ - assert(_uris); -} - -BufferFactory::~BufferFactory() -{ - free_list(_free_audio.get()); - free_list(_free_control.get()); - free_list(_free_event.get()); - free_list(_free_object.get()); -} - -void -BufferFactory::free_list(Buffer* head) -{ - Buffer* next = head->_next; - delete head; - if (next) - free_list(next); -} - -void -BufferFactory::set_block_length(SampleCount block_length) -{ - _silent_buffer = create(PortType::AUDIO, audio_buffer_size(block_length)); -} - -size_t -BufferFactory::audio_buffer_size(SampleCount nframes) -{ - return sizeof(LV2_Atom) + sizeof(LV2_Atom_Vector) + (nframes * sizeof(float)); -} - -size_t -BufferFactory::default_buffer_size(PortType type) -{ - switch (type.symbol()) { - case PortType::AUDIO: - return audio_buffer_size(_engine.driver()->block_length()); - case PortType::CONTROL: - return sizeof(LV2_Atom) + sizeof(float); - case PortType::EVENTS: - return _engine.driver()->block_length() * EVENT_BYTES_PER_FRAME; - default: - return 1024; // Who knows - } -} - -BufferFactory::Ref -BufferFactory::get(PortType type, size_t size, bool force_create) -{ - Raul::AtomicPtr& head_ptr = free_list(type); - Buffer* try_head = NULL; - - if (!force_create) { - Buffer* next; - do { - try_head = head_ptr.get(); - if (!try_head) - break; - next = try_head->_next; - } while (!head_ptr.compare_and_exchange(try_head, next)); - } - - if (!try_head) { - if (!ThreadManager::thread_is(THREAD_PROCESS)) { - return create(type, size); - } else { - assert(false); - error << "Failed to obtain buffer" << endl; - return Ref(); - } - } - - try_head->_next = NULL; - return Ref(try_head); -} - -BufferFactory::Ref -BufferFactory::create(PortType type, size_t size) -{ - ThreadManager::assert_not_thread(THREAD_PROCESS); - - Buffer* buffer = NULL; - - if (size == 0) - size = default_buffer_size(type); - - if (type.is_control()) { - AudioBuffer* ret = new AudioBuffer(*this, type, size); - ret->atom()->type = _uris->atom_Vector.id; - ((LV2_Atom_Vector*)ret->atom()->body)->elem_type = _uris->atom_Float32.id; - buffer = ret; - } else if (type.is_audio()) { - AudioBuffer* ret = new AudioBuffer(*this, type, size); - ret->atom()->type = _uris->atom_Float32.id; - buffer = ret; - } else if (type.is_events()) { - buffer = new EventBuffer(*this, size); - } else if (type.is_value() || type.is_message()) { - buffer = new ObjectBuffer(*this, std::max(size, sizeof(LV2_Atom) + sizeof(void*))); - } else { - error << "Failed to create buffer of unknown type" << endl; - return Ref(); - } - - assert(buffer); - return Ref(buffer); -} - -void -BufferFactory::recycle(Buffer* buf) -{ - Raul::AtomicPtr& head_ptr = free_list(buf->type()); - Buffer* try_head; - do { - try_head = head_ptr.get(); - buf->_next = try_head; - } while (!head_ptr.compare_and_exchange(try_head, buf)); -} - -} // namespace Engine -} // namespace Ingen diff --git a/src/engine/BufferFactory.hpp b/src/engine/BufferFactory.hpp deleted file mode 100644 index 5d6ebf7c..00000000 --- a/src/engine/BufferFactory.hpp +++ /dev/null @@ -1,92 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2009-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_BUFFERFACTORY_HPP -#define INGEN_ENGINE_BUFFERFACTORY_HPP - -#include -#include -#include "ingen/PortType.hpp" -#include "glibmm/thread.h" -#include "raul/RingBuffer.hpp" -#include "raul/AtomicPtr.hpp" -#include "types.hpp" - -namespace Ingen { - -namespace Shared { class LV2URIMap; } - -namespace Engine { - -class Engine; -class Buffer; - -class BufferFactory { -public: - BufferFactory(Engine& engine, - SharedPtr uris); - - ~BufferFactory(); - - typedef boost::intrusive_ptr Ref; - - static size_t audio_buffer_size(SampleCount nframes); - size_t default_buffer_size(PortType type); - - Ref get(PortType type, size_t size=0, bool force_create=false); - - Ref silent_buffer() { return _silent_buffer; } - - void set_block_length(SampleCount block_length); - - Ingen::Shared::LV2URIMap& uris() { assert(_uris); return *_uris.get(); } - -private: - friend class Buffer; - void recycle(Buffer* buf); - - Ref create(PortType type, size_t size=0); - - inline Raul::AtomicPtr& free_list(PortType type) { - switch (type.symbol()) { - case PortType::AUDIO: return _free_audio; - case PortType::CONTROL: return _free_control; - case PortType::EVENTS: return _free_event; - case PortType::VALUE: - case PortType::MESSAGE: return _free_object; - default: throw; - } - } - - void free_list(Buffer* head); - - Raul::AtomicPtr _free_audio; - Raul::AtomicPtr _free_control; - Raul::AtomicPtr _free_event; - Raul::AtomicPtr _free_object; - - Glib::Mutex _mutex; - Engine& _engine; - SharedPtr _uris; - - Ref _silent_buffer; -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_BUFFERFACTORY_HPP diff --git a/src/engine/ClientBroadcaster.cpp b/src/engine/ClientBroadcaster.cpp deleted file mode 100644 index 46c50ea6..00000000 --- a/src/engine/ClientBroadcaster.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "raul/log.hpp" -#include "ingen/ClientInterface.hpp" -#include "ClientBroadcaster.hpp" -#include "PluginImpl.hpp" -#include "ConnectionImpl.hpp" -#include "EngineStore.hpp" -#include "ObjectSender.hpp" -#include "util.hpp" - -#define LOG(s) s << "[ClientBroadcaster] " - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { - -/** Register a client to receive messages over the notification band. - */ -void -ClientBroadcaster::register_client(const URI& uri, ClientInterface* client) -{ - Clients::iterator i = _clients.find(uri); - - if (i == _clients.end()) { - _clients[uri] = client; - LOG(info) << "Registered client: " << uri << endl; - } else { - LOG(warn) << "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 URI& uri) -{ - size_t erased = _clients.erase(uri); - - if (erased > 0) - LOG(info) << "Unregistered client: " << uri << endl; - else - LOG(warn) << "Failed to find client to unregister: " << uri << endl; - - return (erased > 0); -} - -/** Looks up the client with the given source @a uri (which is used as the - * unique identifier for registered clients). - */ -ClientInterface* -ClientBroadcaster::client(const URI& uri) -{ - Clients::iterator i = _clients.find(uri); - if (i != _clients.end()) { - return (*i).second; - } else { - return NULL; - } -} - -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_plugins_to(ClientInterface* client, const NodeFactory::Plugins& plugins) -{ - client->bundle_begin(); - - for (NodeFactory::Plugins::const_iterator i = plugins.begin(); i != plugins.end(); ++i) { - const PluginImpl* const plugin = i->second; - client->put(plugin->uri(), plugin->properties()); - } - - client->bundle_end(); -} - -/** Send an object to all clients. - * - * @param o Object to send - * @param recursive If true send all children of object - */ -void -ClientBroadcaster::send_object(const GraphObjectImpl* o, bool recursive) -{ - for (Clients::const_iterator i = _clients.begin(); i != _clients.end(); ++i) - ObjectSender::send_object((*i).second, o, recursive); -} - -} // namespace Engine -} // namespace Ingen diff --git a/src/engine/ClientBroadcaster.hpp b/src/engine/ClientBroadcaster.hpp deleted file mode 100644 index c72cdeec..00000000 --- a/src/engine/ClientBroadcaster.hpp +++ /dev/null @@ -1,131 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_CLIENTBROADCASTER_HPP -#define INGEN_ENGINE_CLIENTBROADCASTER_HPP - -#include -#include -#include -#include -#include "raul/SharedPtr.hpp" -#include "ingen/ClientInterface.hpp" -#include "NodeFactory.hpp" - -using namespace std; - -namespace Ingen { -namespace Engine { - -class GraphObjectImpl; -class NodeImpl; -class PortImpl; -class PluginImpl; -class PatchImpl; -class ConnectionImpl; - -/** Broadcaster for all clients. - * - * This is a ClientInterface that forwards all messages to all registered - * clients (for updating all clients on state changes in the engine). - * - * \ingroup engine - */ -class ClientBroadcaster : public ClientInterface -{ -public: - void register_client(const Raul::URI& uri, ClientInterface* client); - bool unregister_client(const Raul::URI& uri); - - ClientInterface* client(const Raul::URI& uri); - - void send_plugins(const NodeFactory::Plugins& plugin_list); - void send_plugins_to(ClientInterface*, const NodeFactory::Plugins& plugin_list); - - void send_object(const GraphObjectImpl* p, bool recursive); - -#define BROADCAST(msg, ...) \ - for (Clients::const_iterator i = _clients.begin(); i != _clients.end(); ++i) \ - (*i).second->msg(__VA_ARGS__) - - // CommonInterface - - void bundle_begin() { BROADCAST(bundle_begin); } - void bundle_end() { BROADCAST(bundle_end); } - - void put(const Raul::URI& uri, - const Resource::Properties& properties, - Resource::Graph ctx=Resource::DEFAULT) { - BROADCAST(put, uri, properties); - } - - void delta(const Raul::URI& uri, - const Resource::Properties& remove, - const Resource::Properties& add) { - BROADCAST(delta, uri, remove, add); - } - - void move(const Raul::Path& old_path, - const Raul::Path& new_path) { - BROADCAST(move, old_path, new_path); - } - - void del(const Raul::URI& uri) { - BROADCAST(del, uri); - } - - void connect(const Raul::Path& src_port_path, - const Raul::Path& dst_port_path) { - BROADCAST(connect, src_port_path, dst_port_path); - } - - void disconnect(const Raul::URI& src, - const Raul::URI& dst) { - BROADCAST(disconnect, src, dst); - } - - void disconnect_all(const Raul::Path& parent_patch_path, - const Raul::Path& path) { - BROADCAST(disconnect_all, parent_patch_path, path); - } - - void set_property(const Raul::URI& subject, - const Raul::URI& predicate, - const Raul::Atom& value) { - BROADCAST(set_property, subject, predicate, value); - } - - // ClientInterface - - Raul::URI uri() const { return "http://drobilla.net/ns/ingen#broadcaster"; } ///< N/A - - void response_ok(int32_t id) {} ///< N/A - void response_error(int32_t id, const std::string& msg) {} ///< N/A - - void error(const std::string& msg) { BROADCAST(error, msg); } - void activity(const Raul::Path& path) { BROADCAST(activity, path); } - -private: - typedef std::map Clients; - Clients _clients; -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_CLIENTBROADCASTER_HPP - diff --git a/src/engine/CompiledPatch.hpp b/src/engine/CompiledPatch.hpp deleted file mode 100644 index 274816fb..00000000 --- a/src/engine/CompiledPatch.hpp +++ /dev/null @@ -1,76 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_COMPILEDPATCH_HPP -#define INGEN_ENGINE_COMPILEDPATCH_HPP - -#include -#include "raul/List.hpp" -#include "raul/Deletable.hpp" -#include - -namespace Ingen { -namespace Engine { - -class ConnectionImpl; - -/** All information required about a node to execute it in an audio thread. - */ -struct CompiledNode { - CompiledNode(NodeImpl* n, size_t np, Raul::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 (Raul::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 std::vector& dependants() const { return _dependants; } - -private: - NodeImpl* _node; - size_t _n_providers; ///< Number of input ready signals to trigger run - std::vector _dependants; ///< Nodes this one's output ports are connected to -}; - -/** A patch ``compiled'' into a flat structure with the correct order so - * the audio thread(s) can execute it without threading problems (since - * the preprocessor thread modifies the graph). - * - * The nodes contained here are sorted in the order they must be executed. - * The parallel processing algorithm guarantees no node will be executed - * before its providers, using this order as well as semaphores. - */ -struct CompiledPatch : public std::vector - , public Raul::Deletable - , public boost::noncopyable -{ - typedef std::vector QueuedConnections; - - /** All (audio context => other context) connections */ - std::vector queued_connections; -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_COMPILEDPATCH_HPP diff --git a/src/engine/ConnectionImpl.cpp b/src/engine/ConnectionImpl.cpp deleted file mode 100644 index 90ac75ea..00000000 --- a/src/engine/ConnectionImpl.cpp +++ /dev/null @@ -1,153 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "raul/log.hpp" -#include "raul/Maid.hpp" -#include "raul/IntrusivePtr.hpp" -#include "shared/LV2URIMap.hpp" -#include "AudioBuffer.hpp" -#include "BufferFactory.hpp" -#include "ConnectionImpl.hpp" -#include "Engine.hpp" -#include "EventBuffer.hpp" -#include "InputPort.hpp" -#include "MessageContext.hpp" -#include "OutputPort.hpp" -#include "PortImpl.hpp" -#include "ProcessContext.hpp" -#include "mix.hpp" -#include "util.hpp" - -namespace Ingen { -namespace Engine { - -/** Constructor for a connection from a node's output port. - * - * This handles both polyphonic and monophonic nodes, transparently to the - * user (InputPort). - */ -ConnectionImpl::ConnectionImpl(BufferFactory& bufs, PortImpl* src_port, PortImpl* dst_port) - : _queue(NULL) - , _bufs(bufs) - , _src_port(src_port) - , _dst_port(dst_port) - , _pending_disconnection(false) -{ - assert(src_port); - assert(dst_port); - assert(src_port != dst_port); - assert(src_port->path() != dst_port->path()); - - if (must_queue()) - _queue = new Raul::RingBuffer(src_port->buffer_size() * 2); -} - -void -ConnectionImpl::dump() const -{ - debug << _src_port->path() << " -> " << _dst_port->path() - << (must_mix() ? " (MIX) " : " (DIRECT) ") - << (must_queue() ? " (QUEUE)" : " (NOQUEUE) ") - << "POLY: " << _src_port->poly() << " => " << _dst_port->poly() << endl; -} - -void -ConnectionImpl::get_sources(Context& context, uint32_t voice, - IntrusivePtr* srcs, uint32_t max_num_srcs, uint32_t& num_srcs) -{ - if (must_queue() && _queue->read_space() > 0) { - LV2_Atom obj; - _queue->peek(sizeof(LV2_Atom), &obj); - IntrusivePtr buf = context.engine().buffer_factory()->get( - dst_port()->buffer_type(), sizeof(LV2_Atom) + obj.size); - void* data = buf->port_data(PortType::MESSAGE, context.offset()); - _queue->full_read(sizeof(LV2_Atom) + obj.size, (LV2_Atom*)data); - srcs[num_srcs++] = buf; - } else if (must_mix()) { - // Mixing down voices: every src voice mixed into every dst voice - for (uint32_t v = 0; v < _src_port->poly(); ++v) { - assert(num_srcs < max_num_srcs); - srcs[num_srcs++] = _src_port->buffer(v).get(); - } - } else { - // Matching polyphony: each src voice mixed into corresponding dst voice - assert(_src_port->poly() == _dst_port->poly()); - assert(num_srcs < max_num_srcs); - srcs[num_srcs++] = _src_port->buffer(voice).get(); - } -} - -void -ConnectionImpl::queue(Context& context) -{ - if (!must_queue()) - return; - - IntrusivePtr src_buf = PtrCast(_src_port->buffer(0)); - if (!src_buf) { - error << "Queued connection but source is not an EventBuffer" << endl; - return; - } - - for (src_buf->rewind(); src_buf->is_valid(); src_buf->increment()) { - LV2_Event* ev = src_buf->get_event(); - LV2_Atom* obj = LV2_ATOM_FROM_EVENT(ev); - /*debug << _src_port->path() << " -> " << _dst_port->path() - << " QUEUE OBJECT TYPE " << obj->type << ":"; - for (size_t i = 0; i < obj->size; ++i) - debug << " " << std::hex << (int)obj->body[i]; - debug << endl;*/ - - _queue->write(sizeof(LV2_Atom) + obj->size, obj); - context.engine().message_context()->run(_dst_port->parent_node(), context.start() + ev->frames); - } -} - -bool -ConnectionImpl::can_connect(const OutputPort* src, const InputPort* dst) -{ - const Ingen::Shared::LV2URIMap& uris = src->bufs().uris(); - return ( - // (Audio | Control) => (Audio | Control) - ( (src->is_a(PortType::CONTROL) || src->is_a(PortType::AUDIO)) - && (dst->is_a(PortType::CONTROL) || dst->is_a(PortType::AUDIO))) - - // (Events | Message) => (Events | Message) - || ( (src->is_a(PortType::EVENTS) || src->is_a(PortType::MESSAGE)) - && (dst->is_a(PortType::EVENTS) || dst->is_a(PortType::MESSAGE))) - - // (Message | Value) => (Message | Value) - || ( (src->is_a(PortType::MESSAGE) || src->is_a(PortType::VALUE)) - && (dst->is_a(PortType::MESSAGE) || dst->is_a(PortType::VALUE))) - - // Control => atom:Float32 Value - || (src->is_a(PortType::CONTROL) && dst->supports(uris.atom_Float32)) - - // Audio => atom:Vector Value - || (src->is_a(PortType::AUDIO) && dst->supports(uris.atom_Vector)) - - // atom:Float32 Value => Control - || (src->supports(uris.atom_Float32) && dst->is_a(PortType::CONTROL)) - - // atom:Vector Value => Audio - || (src->supports(uris.atom_Vector) && dst->is_a(PortType::AUDIO))); -} - -} // namespace Engine -} // namespace Ingen - diff --git a/src/engine/ConnectionImpl.hpp b/src/engine/ConnectionImpl.hpp deleted file mode 100644 index d28196fc..00000000 --- a/src/engine/ConnectionImpl.hpp +++ /dev/null @@ -1,110 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_CONNECTIONIMPL_HPP -#define INGEN_ENGINE_CONNECTIONIMPL_HPP - -#include -#include -#include "raul/log.hpp" -#include "raul/Deletable.hpp" -#include "raul/IntrusivePtr.hpp" -#include "ingen/PortType.hpp" -#include "ingen/Connection.hpp" -#include "lv2/lv2plug.in/ns/ext/atom/atom.h" -#include "PortImpl.hpp" - -using namespace std; - -namespace Ingen { -namespace Engine { - -class PortImpl; -class OutputPort; -class InputPort; -class Buffer; -class BufferFactory; - -/** 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 Connection -{ -public: - ConnectionImpl(BufferFactory& bufs, PortImpl* src_port, PortImpl* dst_port); - - 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 queue(Context& context); - - void get_sources(Context& context, uint32_t voice, - IntrusivePtr* srcs, uint32_t max_num_srcs, uint32_t& num_srcs); - - /** Get the buffer for a particular voice. - * A Connection is smart - it knows the destination port requesting the - * buffer, and will return accordingly (e.g. the same buffer for every - * voice in a mono->poly connection). - */ - inline BufferFactory::Ref buffer(uint32_t voice) const { - assert(!must_mix()); - assert(!must_queue()); - assert(_src_port->poly() == 1 || _src_port->poly() > voice); - if (_src_port->poly() == 1) { - return _src_port->buffer(0); - } else { - return _src_port->buffer(voice); - } - } - - /** Returns true if this connection must mix down voices into a local buffer */ - inline bool must_mix() const { return _src_port->poly() > _dst_port->poly(); } - - /** Returns true if this connection crosses contexts and must buffer */ - inline bool must_queue() const { return _src_port->context() != _dst_port->context(); } - - static bool can_connect(const OutputPort* src, const InputPort* dst); - -protected: - void dump() const; - - Raul::RingBuffer* _queue; - - BufferFactory& _bufs; - PortImpl* const _src_port; - PortImpl* const _dst_port; - bool _pending_disconnection; -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_CONNECTIONIMPL_HPP diff --git a/src/engine/Context.hpp b/src/engine/Context.hpp deleted file mode 100644 index 5ffb8a93..00000000 --- a/src/engine/Context.hpp +++ /dev/null @@ -1,107 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_CONTEXT_HPP -#define INGEN_ENGINE_CONTEXT_HPP - -#include "shared/World.hpp" - -#include "Engine.hpp" -#include "EventSink.hpp" -#include "types.hpp" - -namespace Ingen { -namespace Engine { - -class Engine; - -/** Graph execution context. - * - * This is used to pass whatever information a GraphObject might need to - * process; such as the current time, a sink for generated events, etc. - * - * Note the logical distinction between nframes (jack relative) and start/end - * (timeline relative). If transport speed != 1.0, then end-start != nframes - * (though currently this is never the case, it may be if ingen incorporates - * tempo and varispeed). - * - * \ingroup engine - */ -class Context -{ -public: - enum ID { - AUDIO, - MESSAGE - }; - - Context(Engine& engine, ID id) - : _engine(engine) - , _id(id) - , _event_sink(engine, engine.event_queue_size()) - , _start(0) - , _end(0) - , _nframes(0) - , _offset(0) - , _realtime(true) - {} - - virtual ~Context() {} - - ID id() const { return _id; } - - void locate(FrameTime s, SampleCount nframes, SampleCount offset) { - _start = s; - _end = s + nframes; - _nframes = nframes; - _offset = offset; - } - - void locate(const Context& other) { - _start = other._start; - _end = other._end; - _nframes = other._nframes; - _offset = other._offset; - } - - inline Engine& engine() const { return _engine; } - inline FrameTime start() const { return _start; } - inline FrameTime end() const { return _end; } - inline SampleCount nframes() const { return _nframes; } - inline SampleCount offset() const { return _offset; } - inline bool realtime() const { return _realtime; } - - inline const EventSink& event_sink() const { return _event_sink; } - inline EventSink& event_sink() { return _event_sink; } - -protected: - Engine& _engine; ///< Engine we're running in - ID _id; ///< Fast ID for this context - - EventSink _event_sink; ///< Sink for events generated in a realtime context - FrameTime _start; ///< Start frame of this cycle, timeline relative - FrameTime _end; ///< End frame of this cycle, timeline relative - SampleCount _nframes; ///< Length of this cycle in frames - SampleCount _offset; ///< Start offset relative to start of driver buffers - bool _realtime; ///< True iff context is hard realtime -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_CONTEXT_HPP - diff --git a/src/engine/ControlBindings.cpp b/src/engine/ControlBindings.cpp deleted file mode 100644 index 6c5b2f95..00000000 --- a/src/engine/ControlBindings.cpp +++ /dev/null @@ -1,384 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2009-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "raul/log.hpp" -#include "raul/midi_events.h" -#include "shared/LV2URIMap.hpp" -#include "shared/World.hpp" -#include "events/SendPortValue.hpp" -#include "events/SendBinding.hpp" -#include "AudioBuffer.hpp" -#include "ControlBindings.hpp" -#include "Engine.hpp" -#include "EventBuffer.hpp" -#include "PortImpl.hpp" -#include "ProcessContext.hpp" -#include "ThreadManager.hpp" - -#define LOG(s) s << "[ControlBindings] " - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { - -ControlBindings::ControlBindings(Engine& engine) - : _engine(engine) - , _learn_port(NULL) - , _bindings(new Bindings()) - , _feedback(new EventBuffer(*_engine.buffer_factory(), 1024)) // FIXME: size -{ -} - -ControlBindings::~ControlBindings() -{ - delete _feedback; -} - -ControlBindings::Key -ControlBindings::port_binding(PortImpl* port) -{ - const Ingen::Shared::LV2URIMap& uris = *_engine.world()->uris().get(); - const Raul::Atom& binding = port->get_property(uris.ingen_controlBinding); - Key key; - if (binding.type() == Atom::DICT) { - const Atom::DictValue& dict = binding.get_dict(); - Atom::DictValue::const_iterator t = dict.find(uris.rdf_type); - Atom::DictValue::const_iterator n; - if (t == dict.end()) { - return key; - } else if (t->second == uris.midi_Bender) { - key = Key(MIDI_BENDER); - } else if (t->second == uris.midi_ChannelPressure) { - key = Key(MIDI_CHANNEL_PRESSURE); - } else if (t->second == uris.midi_Controller) { - if ((n = dict.find(uris.midi_controllerNumber)) != dict.end()) - key = Key(MIDI_CC, n->second.get_int32()); - } else if (t->second == uris.midi_Note) { - if ((n = dict.find(uris.midi_noteNumber)) != dict.end()) - key = Key(MIDI_NOTE, n->second.get_int32()); - } - } - return key; -} - -ControlBindings::Key -ControlBindings::midi_event_key(uint16_t size, uint8_t* buf, uint16_t& value) -{ - switch (buf[0] & 0xF0) { - case MIDI_CMD_CONTROL: - value = static_cast(buf[2]); - return Key(MIDI_CC, static_cast(buf[1])); - case MIDI_CMD_BENDER: - value = (static_cast(buf[2]) << 7) + static_cast(buf[1]); - return Key(MIDI_BENDER); - case MIDI_CMD_CHANNEL_PRESSURE: - value = static_cast(buf[1]); - return Key(MIDI_CHANNEL_PRESSURE); - case MIDI_CMD_NOTE_ON: - value = 1.0f; - return Key(MIDI_NOTE, static_cast(buf[1])); - default: - return Key(); - } -} - -void -ControlBindings::port_binding_changed(ProcessContext& context, PortImpl* port) -{ - Key key = port_binding(port); - if (key) - _bindings->insert(make_pair(key, port)); -} - -void -ControlBindings::port_value_changed(ProcessContext& context, PortImpl* port) -{ - const Ingen::Shared::LV2URIMap& uris = *_engine.world()->uris().get(); - Key key = port_binding(port); - if (key) { - int16_t value = port_value_to_control(port, key.type); - uint16_t size = 0; - uint8_t buf[4]; - switch (key.type) { - case MIDI_CC: - size = 3; - buf[0] = MIDI_CMD_CONTROL; - buf[1] = key.num; - buf[2] = static_cast(value); - break; - case MIDI_CHANNEL_PRESSURE: - size = 2; - buf[0] = MIDI_CMD_CHANNEL_PRESSURE; - buf[1] = static_cast(value); - break; - case MIDI_BENDER: - size = 3; - buf[0] = MIDI_CMD_BENDER; - buf[1] = (value & 0x007F); - buf[2] = (value & 0x7F00) >> 7; - break; - case MIDI_NOTE: - size = 3; - if (value == 1) - buf[0] = MIDI_CMD_NOTE_ON; - else if (value == 0) - buf[0] = MIDI_CMD_NOTE_OFF; - buf[1] = key.num; - buf[2] = 0x64; // MIDI spec default - break; - default: - break; - } - if (size > 0) { - _feedback->append(0, 0, - uris.global_to_event(uris.midi_MidiEvent.id).second, - size, buf); - } - } -} - -void -ControlBindings::learn(PortImpl* port) -{ - ThreadManager::assert_thread(THREAD_PRE_PROCESS); - _learn_port = port; -} - -Raul::Atom -ControlBindings::control_to_port_value(PortImpl* port, Type type, int16_t value) -{ - const Ingen::Shared::LV2URIMap& uris = *_engine.world()->uris().get(); - - // TODO: cache these to avoid the lookup - float min = port->get_property(uris.lv2_minimum).get_float(); - float max = port->get_property(uris.lv2_maximum).get_float(); - bool toggled = port->has_property(uris.lv2_portProperty, uris.lv2_toggled); - - float normal = 0.0f; - switch (type) { - case MIDI_CC: - case MIDI_CHANNEL_PRESSURE: - normal = (float)value / 127.0f; - break; - case MIDI_BENDER: - normal = (float)value / 16383.0f; - break; - case MIDI_NOTE: - normal = (value == 0.0f) ? 0.0f : 1.0f; - break; - default: - break; - } - - float scaled_value = normal * (max - min) + min; - if (toggled) - scaled_value = (scaled_value < 0.5) ? 0.0 : 1.0; - - return Raul::Atom(scaled_value); -} - -int16_t -ControlBindings::port_value_to_control(PortImpl* port, Type type) -{ - if (port->value().type() != Atom::FLOAT) - return 0; - - const Ingen::Shared::LV2URIMap& uris = *_engine.world()->uris().get(); - - // TODO: cache these to avoid the lookup - float min = port->get_property(uris.lv2_minimum).get_float(); - float max = port->get_property(uris.lv2_maximum).get_float(); - //bool toggled = port->has_property(uris.lv2_portProperty, uris.lv2_toggled); - float value = port->value().get_float(); - float normal = (value - min) / (max - min); - - if (normal < 0.0f) { - warn << "Value " << value << " (normal " << normal << ") for " - << port->path() << " out of range" << endl; - normal = 0.0f; - } - - if (normal > 1.0f) { - warn << "Value " << value << " (normal " << normal << ") for " - << port->path() << " out of range" << endl; - normal = 1.0f; - } - - switch (type) { - case MIDI_CC: - case MIDI_CHANNEL_PRESSURE: - return lrintf(normal * 127.0f); - case MIDI_BENDER: - return lrintf(normal * 16383.0f); - case MIDI_NOTE: - return (value > 0.0f) ? 1 : 0; - default: - return 0; - } -} - -void -ControlBindings::set_port_value(ProcessContext& context, PortImpl* port, Type type, int16_t value) -{ - const Raul::Atom port_value(control_to_port_value(port, type, value)); - port->set_value(port_value); - - assert(port_value.type() == Atom::FLOAT); - assert(dynamic_cast(port->buffer(0).get())); - - for (uint32_t v = 0; v < port->poly(); ++v) - reinterpret_cast(port->buffer(v).get())->set_value( - port_value.get_float(), context.start(), context.start()); - - const Events::SendPortValue ev(context.engine(), context.start(), port, true, 0, port_value); - context.event_sink().write(sizeof(ev), &ev); -} - -bool -ControlBindings::bind(ProcessContext& context, Key key) -{ - const Ingen::Shared::LV2URIMap& uris = *context.engine().world()->uris().get(); - assert(_learn_port); - if (key.type == MIDI_NOTE) { - bool toggled = _learn_port->has_property(uris.lv2_portProperty, uris.lv2_toggled); - if (!toggled) - return false; - } - - _bindings->insert(make_pair(key, _learn_port)); - - const Events::SendBinding ev(context.engine(), context.start(), _learn_port, key.type, key.num); - context.event_sink().write(sizeof(ev), &ev); - - _learn_port = NULL; - return true; -} - -SharedPtr -ControlBindings::remove(const Raul::Path& path) -{ - ThreadManager::assert_thread(THREAD_PRE_PROCESS); - - SharedPtr old_bindings(_bindings); - SharedPtr copy(new Bindings(*_bindings.get())); - - for (Bindings::iterator i = copy->begin(); i != copy->end();) { - Bindings::iterator next = i; - ++next; - - if (i->second->path() == path || i->second->path().is_child_of(path)) - copy->erase(i); - - i = next; - } - - _bindings = copy; - return old_bindings; -} - -SharedPtr -ControlBindings::remove(PortImpl* port) -{ - ThreadManager::assert_thread(THREAD_PRE_PROCESS); - - SharedPtr old_bindings(_bindings); - SharedPtr copy(new Bindings(*_bindings.get())); - - for (Bindings::iterator i = copy->begin(); i != copy->end();) { - Bindings::iterator next = i; - ++next; - - if (i->second == port) - copy->erase(i); - - i = next; - } - - _bindings = copy; - return old_bindings; -} - -void -ControlBindings::pre_process(ProcessContext& context, EventBuffer* buffer) -{ - uint32_t frames = 0; - uint32_t subframes = 0; - uint16_t type = 0; - uint16_t size = 0; - uint8_t* buf = NULL; - uint16_t value = 0; - - SharedPtr bindings = _bindings; - _feedback->clear(); - - const Ingen::Shared::LV2URIMap& uris = *context.engine().world()->uris().get(); - - // TODO: cache - const uint32_t midi_event_type = uris.global_to_event(uris.midi_MidiEvent.id).second; - - // Learn from input if necessary - if (_learn_port) { - for (buffer->rewind(); - buffer->get_event(&frames, &subframes, &type, &size, &buf); - buffer->increment()) { - if (type != midi_event_type) - continue; - - const Key key = midi_event_key(size, buf, value); - if (key && bind(context, key)) - break; - } - } - - // If bindings are empty, no sense reading input - if (bindings->empty()) - return; - - // Read input and apply control values - for (buffer->rewind(); - buffer->get_event(&frames, &subframes, &type, &size, &buf); - buffer->increment()) { - if (type != midi_event_type) - continue; - - const Key key = midi_event_key(size, buf, value); - if (!key) - continue; - - Bindings::const_iterator i = bindings->find(key); - if (i == bindings->end()) - continue; - - set_port_value(context, i->second, key.type, value); - } -} - -void -ControlBindings::post_process(ProcessContext& context, EventBuffer* buffer) -{ - if (_feedback->event_count() > 0) { - // TODO: merge buffer's existing contents (anything send to it in the patch) - _feedback->rewind(); - buffer->copy(context, _feedback); - } -} - -} // namespace Engine -} // namespace Ingen diff --git a/src/engine/ControlBindings.hpp b/src/engine/ControlBindings.hpp deleted file mode 100644 index 2b2946ec..00000000 --- a/src/engine/ControlBindings.hpp +++ /dev/null @@ -1,102 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2009-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_CONTROLBINDINGS_HPP -#define INGEN_ENGINE_CONTROLBINDINGS_HPP - -#include -#include -#include "raul/SharedPtr.hpp" -#include "raul/Path.hpp" -#include "shared/LV2URIMap.hpp" -#include "BufferFactory.hpp" - -namespace Ingen { -namespace Engine { - -class Engine; -class ProcessContext; -class EventBuffer; -class PortImpl; - -class ControlBindings { -public: - enum Type { - NULL_CONTROL, - MIDI_BENDER, - MIDI_CC, - MIDI_RPN, - MIDI_NRPN, - MIDI_CHANNEL_PRESSURE, - MIDI_NOTE - }; - - struct Key { - Key(Type t=NULL_CONTROL, int16_t n=0) : type(t), num(n) {} - inline bool operator<(const Key& other) const { - return (type == other.type) ? (num < other.num) : (type < other.type); - } - inline operator bool() const { return type != NULL_CONTROL; } - Type type; - int16_t num; - }; - - typedef std::map Bindings; - - explicit ControlBindings(Engine& engine); - ~ControlBindings(); - - void learn(PortImpl* port); - - void port_binding_changed(ProcessContext& context, PortImpl* port); - void port_value_changed(ProcessContext& context, PortImpl* port); - void pre_process(ProcessContext& context, EventBuffer* control_in); - void post_process(ProcessContext& context, EventBuffer* control_out); - - /** Remove all bindings for @a path or children of @a path. - * The caller must safely drop the returned reference in the - * post-processing thread after at least one process thread has run. - */ - SharedPtr remove(const Raul::Path& path); - - /** Remove binding for a particular port. - * The caller must safely drop the returned reference in the - * post-processing thread after at least one process thread has run. - */ - SharedPtr remove(PortImpl* port); - -private: - Key port_binding(PortImpl* port); - Key midi_event_key(uint16_t size, uint8_t* buf, uint16_t& value); - - void set_port_value(ProcessContext& context, PortImpl* port, Type type, int16_t value); - bool bind(ProcessContext& context, Key key); - - Raul::Atom control_to_port_value(PortImpl* port, Type type, int16_t value); - int16_t port_value_to_control(PortImpl* port, Type type); - - Engine& _engine; - PortImpl* _learn_port; - - SharedPtr _bindings; - EventBuffer* _feedback; -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_CONTROLBINDINGS_HPP diff --git a/src/engine/Driver.hpp b/src/engine/Driver.hpp deleted file mode 100644 index f47f2b44..00000000 --- a/src/engine/Driver.hpp +++ /dev/null @@ -1,123 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_DRIVER_HPP -#define INGEN_ENGINE_DRIVER_HPP - -#include -#include -#include "raul/Deletable.hpp" -#include "ingen/PortType.hpp" -#include "ingen/EventType.hpp" -#include "DuplexPort.hpp" - -namespace Raul { class Path; } - -namespace Ingen { -namespace Engine { - -class DuplexPort; -class ProcessContext; - -/** 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 Raul::Deletable { -public: - virtual ~DriverPort() {} - - /** Set the name of the system port according to new path */ - virtual void move(const Raul::Path& path) = 0; - - /** Create system port */ - virtual void create() = 0; - - /** Destroy system port */ - virtual void destroy() = 0; - - bool is_input() const { return _patch_port->is_input(); } - DuplexPort* patch_port() const { return _patch_port; } - -protected: - explicit 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: - virtual ~Driver() {} - - /** Activate driver (begin processing graph and events). */ - virtual void activate() {} - - /** Deactivate driver (stop processing graph and events). */ - virtual void deactivate() {} - - /** Create a port ready to be inserted with add_input (non realtime). - * May return NULL if the Driver can not create the port for some reason. - */ - virtual DriverPort* create_port(DuplexPort* patch_port) = 0; - - /** Return the DriverPort for a particular path, iff one exists. */ - virtual DriverPort* driver_port(const Raul::Path& path) = 0; - - /** Add a system visible port (e.g. a port on the root patch). */ - virtual void add_port(DriverPort* port) = 0; - - /** Remove a system visible port. */ - virtual Raul::Deletable* remove_port(const Raul::Path& path, - DriverPort** port=NULL) = 0; - - /** Return true iff this driver supports the given type of I/O */ - virtual bool supports(PortType port_type, - EventType event_type) = 0; - - virtual void set_root_patch(PatchImpl* patch) = 0; - virtual PatchImpl* root_patch() = 0; - - /** Return the audio buffer size in frames */ - virtual SampleCount block_length() const = 0; - - /** Return the sample rate in Hz */ - virtual SampleCount sample_rate() const = 0; - - /** Return the current frame time (running counter) */ - virtual SampleCount frame_time() const = 0; - - virtual bool is_realtime() const = 0; - - virtual ProcessContext& context() = 0; -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_DRIVER_HPP diff --git a/src/engine/DuplexPort.cpp b/src/engine/DuplexPort.cpp deleted file mode 100644 index ecd8da27..00000000 --- a/src/engine/DuplexPort.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "shared/LV2URIMap.hpp" - -#include "ConnectionImpl.hpp" -#include "DuplexPort.hpp" -#include "EventBuffer.hpp" -#include "NodeImpl.hpp" -#include "OutputPort.hpp" -#include "ProcessContext.hpp" -#include "util.hpp" - -using namespace std; - -namespace Ingen { -namespace Engine { - -DuplexPort::DuplexPort( - BufferFactory& bufs, - NodeImpl* parent, - const string& name, - uint32_t index, - bool polyphonic, - uint32_t poly, - PortType type, - const Raul::Atom& value, - size_t buffer_size, - bool is_output) - : PortImpl(bufs, parent, name, index, poly, type, value, buffer_size) - , InputPort(bufs, parent, name, index, poly, type, value, buffer_size) - , OutputPort(bufs, parent, name, index, poly, type, value, buffer_size) - , _is_output(is_output) -{ - assert(PortImpl::_parent == parent); - set_property(bufs.uris().ingen_polyphonic, polyphonic); -} - -bool -DuplexPort::get_buffers(BufferFactory& bufs, Raul::Array* buffers, uint32_t poly) -{ - if (_is_output) - return InputPort::get_buffers(bufs, buffers, poly); - else - return OutputPort::get_buffers(bufs, buffers, poly); -} - -/** Prepare for the execution of parent patch */ -void -DuplexPort::pre_process(Context& context) -{ - // If we're a patch output, we're an input from the internal perspective. - // Prepare buffers for write (so plugins can deliver to them) - if (_is_output) { - for (uint32_t v = 0; v < _poly; ++v) - _buffers->at(v)->prepare_write(context); - - // If we're a patch input, were an output from the internal perspective. - // Do whatever a normal node's input port does to prepare input for reading. - } else { - InputPort::pre_process(context); - } -} - -/** Finalize after the execution of parent patch (deliver outputs) */ -void -DuplexPort::post_process(Context& context) -{ - // If we're a patch output, we're an input from the internal perspective. - // Mix down input delivered by plugins so output (external perspective) is ready. - if (_is_output) { - InputPort::pre_process(context); - - if (_broadcast) - broadcast_value(context, false); - } -} - -} // namespace Engine -} // namespace Ingen - diff --git a/src/engine/DuplexPort.hpp b/src/engine/DuplexPort.hpp deleted file mode 100644 index 4341c6e3..00000000 --- a/src/engine/DuplexPort.hpp +++ /dev/null @@ -1,70 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_DUPLEXPORT_HPP -#define INGEN_ENGINE_DUPLEXPORT_HPP - -#include -#include "Buffer.hpp" -#include "InputPort.hpp" -#include "OutputPort.hpp" - -namespace Ingen { -namespace Engine { - -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(BufferFactory& bufs, - NodeImpl* parent, - const std::string& name, - uint32_t index, - bool polyphonic, - uint32_t poly, - PortType type, - const Raul::Atom& value, - size_t buffer_size, - bool is_output); - - virtual ~DuplexPort() {} - - bool get_buffers(BufferFactory& bufs, Raul::Array* buffers, uint32_t poly); - - void pre_process(Context& context); - void post_process(Context& context); - - bool is_input() const { return !_is_output; } - bool is_output() const { return _is_output; } - -protected: - bool _is_output; -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_DUPLEXPORT_HPP diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp deleted file mode 100644 index 357604ed..00000000 --- a/src/engine/Engine.cpp +++ /dev/null @@ -1,225 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "raul/log.hpp" -#include "raul/Deletable.hpp" -#include "raul/Maid.hpp" -#include "raul/SharedPtr.hpp" -#include "lv2/lv2plug.in/ns/ext/uri-map/uri-map.h" -#include "ingen/EventType.hpp" -#include "events/CreatePatch.hpp" -#include "events/CreatePort.hpp" -#include "shared/World.hpp" -#include "shared/LV2Features.hpp" -#include "shared/LV2URIMap.hpp" -#include "shared/Store.hpp" -#include "BufferFactory.hpp" -#include "ClientBroadcaster.hpp" -#include "ControlBindings.hpp" -#include "Driver.hpp" -#include "Engine.hpp" -#include "EngineStore.hpp" -#include "Event.hpp" -#include "EventSource.hpp" -#include "MessageContext.hpp" -#include "NodeFactory.hpp" -#include "PatchImpl.hpp" -#include "PostProcessor.hpp" -#include "ProcessContext.hpp" -#include "QueuedEngineInterface.hpp" -#include "ThreadManager.hpp" - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { - -bool ThreadManager::single_threaded = true; - -Engine::Engine(Ingen::Shared::World* a_world) - : _world(a_world) - , _broadcaster(new ClientBroadcaster()) - , _buffer_factory(new BufferFactory(*this, a_world->uris())) - , _control_bindings(new ControlBindings(*this)) - , _maid(new Raul::Maid(event_queue_size())) - , _message_context(new MessageContext(*this)) - , _node_factory(new NodeFactory(a_world)) - , _post_processor(new PostProcessor(*this, event_queue_size())) -{ - if (a_world->store()) { - assert(PtrCast(a_world->store())); - } else { - a_world->set_store(SharedPtr(new EngineStore())); - } -} - -Engine::~Engine() -{ - deactivate(); - - SharedPtr store = engine_store(); - if (store) - for (EngineStore::iterator i = store->begin(); i != store->end(); ++i) - if ( ! PtrCast(i->second)->parent() ) - i->second.reset(); - - delete _maid; - delete _post_processor; - delete _node_factory; - delete _broadcaster; - - munlockall(); -} - -SharedPtr -Engine::engine_store() const -{ - return PtrCast(_world->store()); -} - -size_t -Engine::event_queue_size() const -{ - return world()->conf()->option("queue-size").get_int32(); -} - -void -Engine::quit() -{ - _quit_flag = true; -} - -bool -Engine::main_iteration() -{ - _post_processor->process(); - _maid->cleanup(); - return !_quit_flag; -} - -void -Engine::add_event_source(SharedPtr source) -{ - _event_sources.insert(source); -} - -void -Engine::set_driver(SharedPtr driver) -{ - _driver = driver; -} - -static void -execute_and_delete_event(ProcessContext& context, QueuedEvent* ev) -{ - ev->pre_process(); - ev->execute(context); - ev->post_process(); - delete ev; -} - -bool -Engine::activate() -{ - assert(_driver); - ThreadManager::single_threaded = true; - - _buffer_factory->set_block_length(_driver->block_length()); - - _message_context->Thread::start(); - - const Ingen::Shared::LV2URIMap& uris = *world()->uris().get(); - - // Create root patch - PatchImpl* root_patch = _driver->root_patch(); - if (!root_patch) { - root_patch = new PatchImpl(*this, "root", 1, NULL, _driver->sample_rate(), 1); - root_patch->set_property(uris.rdf_type, - Resource::Property(uris.ingen_Patch, Resource::INTERNAL)); - root_patch->set_property(uris.ingen_polyphony, - Resource::Property(Raul::Atom(int32_t(1)), - Resource::INTERNAL)); - root_patch->activate(*_buffer_factory); - _world->store()->add(root_patch); - root_patch->compiled_patch(root_patch->compile()); - _driver->set_root_patch(root_patch); - - ProcessContext context(*this); - - Resource::Properties control_properties; - control_properties.insert(make_pair(uris.lv2_name, "Control")); - control_properties.insert(make_pair(uris.rdf_type, uris.ev_EventPort)); - - // Add control input - Resource::Properties in_properties(control_properties); - in_properties.insert(make_pair(uris.rdf_type, uris.lv2_InputPort)); - in_properties.insert(make_pair(uris.lv2_index, 0)); - in_properties.insert(make_pair(uris.ingenui_canvas_x, - Resource::Property(32.0f, Resource::EXTERNAL))); - in_properties.insert(make_pair(uris.ingenui_canvas_y, - Resource::Property(32.0f, Resource::EXTERNAL))); - - execute_and_delete_event(context, new Events::CreatePort( - *this, SharedPtr(), 0, - "/control_in", uris.ev_EventPort, false, in_properties)); - - // Add control out - Resource::Properties out_properties(control_properties); - out_properties.insert(make_pair(uris.rdf_type, uris.lv2_OutputPort)); - out_properties.insert(make_pair(uris.lv2_index, 1)); - out_properties.insert(make_pair(uris.ingenui_canvas_x, - Resource::Property(128.0f, Resource::EXTERNAL))); - out_properties.insert(make_pair(uris.ingenui_canvas_y, - Resource::Property(32.0f, Resource::EXTERNAL))); - - execute_and_delete_event(context, new Events::CreatePort( - *this, SharedPtr(), 0, - "/control_out", uris.ev_EventPort, true, out_properties)); - } - - _driver->activate(); - root_patch->enable(); - - ThreadManager::single_threaded = false; - - return true; -} - -void -Engine::deactivate() -{ - _driver->deactivate(); - _driver->root_patch()->deactivate(); - - ThreadManager::single_threaded = true; -} - -void -Engine::process_events(ProcessContext& context) -{ - ThreadManager::assert_thread(THREAD_PROCESS); - - for (EventSources::iterator i = _event_sources.begin(); i != _event_sources.end(); ++i) - (*i)->process(*_post_processor, context); -} - -} // namespace Engine -} // namespace Ingen diff --git a/src/engine/Engine.hpp b/src/engine/Engine.hpp deleted file mode 100644 index ffcd5dc9..00000000 --- a/src/engine/Engine.hpp +++ /dev/null @@ -1,115 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_ENGINE_HPP -#define INGEN_ENGINE_ENGINE_HPP - -#include -#include - -#include - -#include "raul/SharedPtr.hpp" - -#include "ingen/EngineBase.hpp" - -namespace Raul { class Maid; } - -namespace Ingen { - -namespace Shared { class World; } - -namespace Engine { - -class BufferFactory; -class ClientBroadcaster; -class ControlBindings; -class Driver; -class EngineStore; -class EventSource; -class MessageContext; -class NodeFactory; -class PostProcessor; -class ProcessContext; - -/** - The engine which executes the process graph. - - This is a simple class that provides pointers to the various components - that make up the engine implementation. In processes with a local engine, - it can be accessed via the Ingen::Shared::World. - - @ingroup engine -*/ -class Engine : public boost::noncopyable, public EngineBase -{ -public: - explicit Engine(Ingen::Shared::World* world); - - virtual ~Engine(); - - virtual bool activate(); - - virtual void deactivate(); - - virtual void quit(); - - virtual bool main_iteration(); - - void set_driver(SharedPtr driver); - - void add_event_source(SharedPtr source); - - void process_events(ProcessContext& context); - - Ingen::Shared::World* world() const { return _world; } - - ClientBroadcaster* broadcaster() const { return _broadcaster; } - BufferFactory* buffer_factory() const { return _buffer_factory; } - ControlBindings* control_bindings() const { return _control_bindings; } - Driver* driver() const { return _driver.get(); } - Raul::Maid* maid() const { return _maid; } - MessageContext* message_context() const { return _message_context; } - NodeFactory* node_factory() const { return _node_factory; } - PostProcessor* post_processor() const { return _post_processor; } - - SharedPtr engine_store() const; - - size_t event_queue_size() const; - -private: - Ingen::Shared::World* _world; - - ClientBroadcaster* _broadcaster; - BufferFactory* _buffer_factory; - ControlBindings* _control_bindings; - SharedPtr _driver; - Raul::Maid* _maid; - MessageContext* _message_context; - NodeFactory* _node_factory; - PostProcessor* _post_processor; - - typedef std::set< SharedPtr > EventSources; - EventSources _event_sources; - - bool _quit_flag; -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_ENGINE_HPP diff --git a/src/engine/EngineStore.cpp b/src/engine/EngineStore.cpp deleted file mode 100644 index 010edbc0..00000000 --- a/src/engine/EngineStore.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "raul/log.hpp" -#include "raul/List.hpp" -#include "raul/PathTable.hpp" -#include "raul/TableImpl.hpp" -#include "EngineStore.hpp" -#include "PatchImpl.hpp" -#include "NodeImpl.hpp" -#include "PortImpl.hpp" -#include "ThreadManager.hpp" - -#define LOG(s) s << "[EngineStore] " - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { - -/** 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) -{ - ThreadManager::assert_thread(THREAD_PRE_PROCESS); - Store::add(obj); -} - -/** Add a family of objects to the store. Not realtime safe. - */ -void -EngineStore::add(const Objects& table) -{ - ThreadManager::assert_thread(THREAD_PRE_PROCESS); - cram(table); -} - -/** 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) -{ - ThreadManager::assert_thread(THREAD_PRE_PROCESS); - - if (object != end()) { - iterator descendants_end = find_descendants_end(object); - SharedPtr removed = yank(object, descendants_end); - - return removed; - - } else { - LOG(warn) << "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 { - LOG(warn) << "Removing children of " << object->first << " failed." << endl; - return SharedPtr(); - } - - return SharedPtr(); -} - -} // namespace Engine -} // namespace Ingen diff --git a/src/engine/EngineStore.hpp b/src/engine/EngineStore.hpp deleted file mode 100644 index c6d1e024..00000000 --- a/src/engine/EngineStore.hpp +++ /dev/null @@ -1,65 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_OBJECTSTORE_HPP -#define INGEN_ENGINE_OBJECTSTORE_HPP - -#include "raul/SharedPtr.hpp" - -#include "shared/Store.hpp" - -namespace Ingen { - -class GraphObject; - -namespace Engine { - -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 Ingen::Shared::Store -{ -public: - PatchImpl* find_patch(const Raul::Path& path); - NodeImpl* find_node(const Raul::Path& path); - PortImpl* find_port(const Raul::Path& path); - GraphObjectImpl* find_object(const Raul::Path& path); - - void add(Ingen::GraphObject* o); - void add(const Objects& family); - - SharedPtr remove(const Raul::Path& path); - SharedPtr remove(Objects::iterator i); - SharedPtr remove_children(const Raul::Path& path); - SharedPtr remove_children(Objects::iterator i); -}; - -} // namespace Engine -} // namespace Ingen - -#endif // OBJECTSTORE diff --git a/src/engine/Event.cpp b/src/engine/Event.cpp deleted file mode 100644 index 76af56c0..00000000 --- a/src/engine/Event.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "Driver.hpp" -#include "Engine.hpp" -#include "Event.hpp" -#include "ProcessContext.hpp" -#include "ThreadManager.hpp" - -/*! \page methods Method Documentation - * - *

All changes in Ingen (both engine and client) occur as a result of - * a small set of methods defined in terms of RDF and matching the - * HTTP and WebDAV standards as closely as possible.

- */ - -namespace Ingen { -namespace Engine { - -void -Event::execute(ProcessContext& context) -{ - ThreadManager::assert_thread(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() -{ - ThreadManager::assert_not_thread(THREAD_PROCESS); -} - -} // namespace Engine -} // namespace Ingen - diff --git a/src/engine/Event.hpp b/src/engine/Event.hpp deleted file mode 100644 index d6664683..00000000 --- a/src/engine/Event.hpp +++ /dev/null @@ -1,80 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_EVENT_HPP -#define INGEN_ENGINE_EVENT_HPP - -#include -#include "raul/SharedPtr.hpp" -#include "raul/Deletable.hpp" -#include "raul/Path.hpp" -#include "types.hpp" - -namespace Ingen { -namespace Engine { - -class Engine; -class Request; -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; } - - int error() { return _error; } - -protected: - Event(Engine& engine, SharedPtr request, FrameTime time) - : _engine(engine) - , _request(request) - , _time(time) - , _error(0) // success - , _executed(false) - {} - - Engine& _engine; - SharedPtr _request; - FrameTime _time; - int _error; - bool _executed; -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_EVENT_HPP diff --git a/src/engine/EventBuffer.cpp b/src/engine/EventBuffer.cpp deleted file mode 100644 index 50f938ce..00000000 --- a/src/engine/EventBuffer.cpp +++ /dev/null @@ -1,215 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "raul/log.hpp" -#include "lv2/lv2plug.in/ns/ext/event/event.h" -#include "lv2/lv2plug.in/ns/ext/event/event-helpers.h" -#include "ingen-config.h" -#include "EventBuffer.hpp" -#include "ProcessContext.hpp" - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { - -/** Allocate a new event buffer. - * \a capacity is in bytes (not number of events). - */ -EventBuffer::EventBuffer(BufferFactory& bufs, size_t capacity) - : Buffer(bufs, PortType(PortType::EVENTS), capacity) - , _latest_frames(0) - , _latest_subframes(0) -{ - if (capacity > UINT32_MAX) { - error << "Event buffer size " << capacity << " too large, aborting." << endl; - throw std::bad_alloc(); - } - -#ifdef HAVE_POSIX_MEMALIGN - int ret = posix_memalign((void**)&_data, 16, sizeof(LV2_Event_Buffer) + capacity); -#else - _data = (LV2_Event_Buffer*)malloc(sizeof(LV2_Event_Buffer) + capacity); - int ret = (_data != NULL) ? 0 : -1; -#endif - - if (ret != 0) { - error << "Failed to allocate event buffer. Aborting." << endl; - exit(EXIT_FAILURE); - } - - _data->header_size = sizeof(LV2_Event_Buffer); - _data->data = reinterpret_cast(_data + _data->header_size); - _data->stamp_type = 0; - _data->event_count = 0; - _data->capacity = (uint32_t)capacity; - _data->size = 0; - - clear(); -} - -EventBuffer::~EventBuffer() -{ - free(_data); -} - -void -EventBuffer::prepare_read(Context& context) -{ - rewind(); -} - -void -EventBuffer::prepare_write(Context& context) -{ - if (context.offset() == 0) - clear(); -} - -void -EventBuffer::copy(Context& context, const Buffer* src_buf) -{ - const EventBuffer* src = dynamic_cast(src_buf); - if (src->_data == _data) - return; - - assert(src->_data->header_size == _data->header_size); - assert(capacity() >= _data->header_size + src->_data->size); - - rewind(); - - memcpy(_data, src->_data, _data->header_size + src->_data->size); - - _iter = src->_iter; - _iter.buf = _data; - - _latest_frames = src->_latest_frames; - _latest_subframes = src->_latest_subframes; - - assert(event_count() == src->event_count()); -} - -/** 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); -} - -/** 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; - } -} - -/** Get the object currently pointed to, or NULL if invalid. - */ -LV2_Atom* -EventBuffer::get_atom() const -{ - if (lv2_event_is_valid(&_iter)) { - uint8_t* data; - LV2_Event* ev = lv2_event_get(&_iter, &data); - return LV2_ATOM_FROM_EVENT(ev); - } - return NULL; -} - -/** Get the event currently pointed to, or NULL if invalid. - */ -LV2_Event* -EventBuffer::get_event() const -{ - if (lv2_event_is_valid(&_iter)) { - uint8_t* data; - return lv2_event_get(&_iter, &data); - } - return NULL; -} - -/** Append an event to the buffer. - * - * \a timestamp must be >= the latest event in the buffer. - * - * \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 - - /*debug << "Appending event type " << type << ", size " << size - << " @ " << frames << "." << subframes << endl;*/ - - if (!lv2_event_write(&_iter, frames, subframes, type, size, data)) { - error << "Failed to write event." << endl; - return false; - } else { - _latest_frames = frames; - _latest_subframes = subframes; - return true; - } -} - -} // namespace Engine -} // namespace Ingen diff --git a/src/engine/EventBuffer.hpp b/src/engine/EventBuffer.hpp deleted file mode 100644 index 16f5afbe..00000000 --- a/src/engine/EventBuffer.hpp +++ /dev/null @@ -1,86 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_EVENTBUFFER_HPP -#define INGEN_ENGINE_EVENTBUFFER_HPP - -#include "lv2/lv2plug.in/ns/ext/atom/atom.h" -#include "lv2/lv2plug.in/ns/ext/event/event.h" -#include "lv2/lv2plug.in/ns/ext/event/event-helpers.h" -#include "ingen/PortType.hpp" -#include "Buffer.hpp" - -namespace Ingen { -namespace Engine { - -class EventBuffer : public Buffer { -public: - EventBuffer(BufferFactory& bufs, size_t capacity); - ~EventBuffer(); - - void* port_data(PortType port_type, SampleCount offset=0) { return _data; } - const void* port_data(PortType port_type, SampleCount offset=0) const { return _data; } - - inline void rewind() const { lv2_event_begin(&_iter, _data); } - - inline void clear() { - _latest_frames = 0; - _latest_subframes = 0; - _data->event_count = 0; - _data->size = 0; - rewind(); - } - - void prepare_read(Context& context); - void prepare_write(Context& context); - - void copy(Context& context, const Buffer* src); - - inline size_t event_count() const { return _data->event_count; } - inline uint32_t capacity() const { return _data->capacity; } - inline uint32_t latest_frames() const { return _latest_frames; } - inline uint32_t latest_subframes() const { return _latest_subframes; } - - bool increment() const; - bool is_valid() const; - - bool get_event(uint32_t* frames, - uint32_t* subframes, - uint16_t* type, - uint16_t* size, - uint8_t** data) const; - - LV2_Atom* get_atom() const; - LV2_Event* get_event() const; - - bool append(uint32_t frames, - uint32_t subframes, - uint16_t type, - uint16_t size, - const uint8_t* data); - -private: - LV2_Event_Buffer* _data; ///< Contents - mutable LV2_Event_Iterator _iter; ///< Iterator into _data - uint32_t _latest_frames; ///< Latest time of all events (frames) - uint32_t _latest_subframes; ///< Latest time of all events (subframes) -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_EVENTBUFFER_HPP diff --git a/src/engine/EventSink.cpp b/src/engine/EventSink.cpp deleted file mode 100644 index bf31beaa..00000000 --- a/src/engine/EventSink.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "EventSink.hpp" -#include "PortImpl.hpp" - -using namespace std; - -namespace Ingen { -namespace Engine { - -/** \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 Engine -} // namespace Ingen diff --git a/src/engine/EventSink.hpp b/src/engine/EventSink.hpp deleted file mode 100644 index 50edea77..00000000 --- a/src/engine/EventSink.hpp +++ /dev/null @@ -1,59 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_EVENTSINK_HPP -#define INGEN_ENGINE_EVENTSINK_HPP - -#include -#include -#include -#include "raul/RingBuffer.hpp" - -namespace Ingen { -namespace Engine { - -class PortImpl; -class Engine; -class Event; - -/** 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) {} - - 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 Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_EVENTSINK_HPP - diff --git a/src/engine/EventSource.cpp b/src/engine/EventSource.cpp deleted file mode 100644 index 33ab94ab..00000000 --- a/src/engine/EventSource.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2008-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "EventSource.hpp" -#include "QueuedEvent.hpp" -#include "PostProcessor.hpp" -#include "ThreadManager.hpp" -#include "ProcessContext.hpp" - -using namespace std; - -namespace Ingen { -namespace Engine { - -EventSource::EventSource(size_t queue_size) - : _blocking_semaphore(0) -{ - Thread::set_context(THREAD_PRE_PROCESS); - set_name("EventSource"); -} - -EventSource::~EventSource() -{ - Thread::stop(); -} - -/** Push an unprepared event onto the queue. - */ -void -EventSource::push_queued(QueuedEvent* const ev) -{ - assert(!ev->is_prepared()); - Raul::List::Node* node = new Raul::List::Node(ev); - _events.push_back(node); - if (_prepared_back.get() == NULL) - _prepared_back = node; - - whip(); -} - -/** Process all events for a cycle. - * - * Executed events will be pushed to @a dest. - */ -void -EventSource::process(PostProcessor& dest, ProcessContext& context, bool limit) -{ - ThreadManager::assert_thread(THREAD_PROCESS); - - if (_events.empty()) - return; - - /* 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 size_t MAX_QUEUED_EVENTS = context.nframes() / 32; - - size_t num_events_processed = 0; - - Raul::List::Node* head = _events.head(); - Raul::List::Node* tail = head; - - if (!head) - return; - - QueuedEvent* ev = (QueuedEvent*)head->elem(); - - while (ev && ev->is_prepared() && ev->time() < context.end()) { - ev->execute(context); - tail = head; - head = head->next(); - ++num_events_processed; - if (limit && num_events_processed > MAX_QUEUED_EVENTS) - break; - ev = (head ? (QueuedEvent*)head->elem() : NULL); - } - - if (num_events_processed > 0) { - Raul::List front; - _events.chop_front(front, num_events_processed, tail); - dest.append(&front); - } -} - -/** Pre-process a single event */ -void -EventSource::_whipped() -{ - Raul::List::Node* pb = _prepared_back.get(); - if (!pb) - return; - - QueuedEvent* const ev = (QueuedEvent*)pb->elem(); - assert(ev); - - assert(!ev->is_prepared()); - ev->pre_process(); - assert(ev->is_prepared()); - - assert(_prepared_back.get() == pb); - _prepared_back = pb->next(); - - // 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 Engine -} // namespace Ingen - diff --git a/src/engine/EventSource.hpp b/src/engine/EventSource.hpp deleted file mode 100644 index 52bb08f6..00000000 --- a/src/engine/EventSource.hpp +++ /dev/null @@ -1,73 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_EVENTSOURCE_HPP -#define INGEN_ENGINE_EVENTSOURCE_HPP - -#include "raul/Semaphore.hpp" -#include "raul/Slave.hpp" -#include "raul/List.hpp" - -namespace Ingen { -namespace Engine { - -class Event; -class QueuedEvent; -class PostProcessor; -class ProcessContext; - -/** Source for events to run in the audio thread. - * - * The Driver gets events from an EventSource in the process callback - * (realtime audio thread) and executes them, then they are sent to the - * PostProcessor and finalised (post-processing thread). - */ -class EventSource : protected Raul::Slave -{ -public: - explicit EventSource(size_t queue_size); - virtual ~EventSource(); - - void process(PostProcessor& dest, ProcessContext& context, bool limit=true); - - bool empty() { return _events.empty(); } - - /** Signal that a blocking event is finished. - * - * This MUST be called by blocking events in their post_process() method - * to resume pre-processing of events. - */ - inline void unblock() { _blocking_semaphore.post(); } - -protected: - void push_queued(QueuedEvent* const ev); - - inline bool unprepared_events() { return (_prepared_back.get() != NULL); } - - virtual void _whipped(); ///< Prepare 1 event - -private: - Raul::List _events; - Raul::AtomicPtr::Node> _prepared_back; - Raul::Semaphore _blocking_semaphore; -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_EVENTSOURCE_HPP - diff --git a/src/engine/GraphObjectImpl.cpp b/src/engine/GraphObjectImpl.cpp deleted file mode 100644 index 6b8e8a08..00000000 --- a/src/engine/GraphObjectImpl.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "GraphObjectImpl.hpp" -#include "PatchImpl.hpp" -#include "EngineStore.hpp" - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { - -GraphObjectImpl::GraphObjectImpl(Ingen::Shared::LV2URIMap& uris, - GraphObjectImpl* parent, - const Symbol& symbol) - : ResourceImpl(uris, parent ? parent->path().child(symbol) : Raul::Path::root()) - , _parent(parent) - , _path(parent ? parent->path().child(symbol) : "/") - , _symbol(symbol) -{ -} - -void -GraphObjectImpl::add_meta_property(const Raul::URI& key, const Atom& value) -{ - add_property(key, Resource::Property(value, Resource::INTERNAL)); -} - -void -GraphObjectImpl::set_meta_property(const Raul::URI& key, const Atom& value) -{ - set_property(key, Resource::Property(value, Resource::INTERNAL)); -} - -const Atom& -GraphObjectImpl::get_property(const Raul::URI& key) const -{ - static const Atom null_atom; - Resource::Properties::const_iterator i = properties().find(key); - return (i != properties().end()) ? i->second : null_atom; -} - -PatchImpl* -GraphObjectImpl::parent_patch() const -{ - return dynamic_cast((NodeImpl*)_parent); -} - -SharedPtr -GraphObjectImpl::find_child(const std::string& name) const -{ - throw; -} - -} // namespace Engine -} // namespace Ingen diff --git a/src/engine/GraphObjectImpl.hpp b/src/engine/GraphObjectImpl.hpp deleted file mode 100644 index 88e18ff4..00000000 --- a/src/engine/GraphObjectImpl.hpp +++ /dev/null @@ -1,113 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_GRAPHOBJECTIMPL_HPP -#define INGEN_ENGINE_GRAPHOBJECTIMPL_HPP - -#include -#include -#include -#include -#include "raul/Deletable.hpp" -#include "raul/Path.hpp" -#include "raul/SharedPtr.hpp" -#include "ingen/GraphObject.hpp" -#include "shared/ResourceImpl.hpp" - -namespace Raul { class Maid; } - -namespace Ingen { - -namespace Shared { class LV2URIMap; } - -namespace Engine { - -class PatchImpl; -class Context; -class ProcessContext; -class BufferFactory; - -/** 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 GraphObject - , public Ingen::Shared::ResourceImpl -{ -public: - virtual ~GraphObjectImpl() {} - - const Raul::URI& uri() const { return _path; } - const Raul::Symbol& symbol() const { return _symbol; } - - GraphObject* graph_parent() const { return _parent; } - GraphObjectImpl* parent() const { return _parent; } - - //virtual void process(ProcessContext& context) = 0; - - /** Rename */ - virtual void set_path(const Raul::Path& new_path) { - _path = new_path; - _symbol = new_path.symbol(); - } - - const Raul::Atom& get_property(const Raul::URI& key) const; - void add_meta_property(const Raul::URI& key, const Raul::Atom& value); - void set_meta_property(const Raul::URI& key, const Raul::Atom& value); - - /** The Patch this object is a child of. */ - virtual PatchImpl* parent_patch() const; - - /** Raul::Path is dynamically generated from parent to ease renaming */ - const Raul::Path& path() const { return _path; } - - SharedPtr find_child(const std::string& name) const; - - /** Prepare for a new (external) polyphony value. - * - * Preprocessor thread, poly is actually applied by apply_poly. - * \return true on success. - */ - virtual bool prepare_poly(BufferFactory& bufs, 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; - -protected: - GraphObjectImpl(Ingen::Shared::LV2URIMap& uris, - GraphObjectImpl* parent, - const Raul::Symbol& symbol); - - GraphObjectImpl* _parent; - Raul::Path _path; - Raul::Symbol _symbol; -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_GRAPHOBJECTIMPL_HPP diff --git a/src/engine/HTTPClientSender.cpp b/src/engine/HTTPClientSender.cpp deleted file mode 100644 index 21dc11a3..00000000 --- a/src/engine/HTTPClientSender.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2008-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "raul/log.hpp" -#include "raul/Atom.hpp" -#include "raul/AtomRDF.hpp" -#include "serialisation/Serialiser.hpp" -#include "shared/World.hpp" -#include "HTTPClientSender.hpp" -#include "Engine.hpp" - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { - -void -HTTPClientSender::response_ok(int32_t id) -{ -} - -void -HTTPClientSender::response_error(int32_t id, const std::string& msg) -{ - warn << "HTTP Error " << id << " (" << msg << ")" << endl; -} - -void -HTTPClientSender::error(const std::string& msg) -{ - warn << "HTTP send error " << msg << endl; -} - -void -HTTPClientSender::put(const URI& uri, - const Resource::Properties& properties, - Resource::Graph ctx) -{ - const std::string request_uri = (Raul::Path::is_path(uri)) - ? _url + "/patch" + uri.substr(uri.find("/")) - : uri.str(); - - - Sord::Model model(*_engine.world()->rdf_world()); - for (Resource::Properties::const_iterator i = properties.begin(); - i != properties.end(); ++i) - model.add_statement( - Sord::URI(*_engine.world()->rdf_world(), request_uri), - AtomRDF::atom_to_node(model, i->first.str()), - AtomRDF::atom_to_node(model, i->second)); - - const string str = model.write_to_string("turtle"); - send_chunk(str); -} - -void -HTTPClientSender::delta(const URI& uri, - const Resource::Properties& remove, - const Resource::Properties& add) -{ -} - -void -HTTPClientSender::del(const URI& uri) -{ - send_chunk(string("<").append(uri.str()).append("> a .")); -} - -void -HTTPClientSender::connect(const Path& src_path, const Path& dst_path) -{ - const string msg = string( - "@prefix rdf: .\n" - "@prefix ingen: .\n").append( - "<> ingen:connection [\n" - "\tingen:destination <").append(dst_path.str()).append("> ;\n" - "\tingen:source <").append(src_path.str()).append(">\n] .\n"); - send_chunk(msg); -} - -void -HTTPClientSender::disconnect(const URI& src, - const URI& dst) -{ -} - -void -HTTPClientSender::disconnect_all(const Raul::Path& parent_patch_path, - const Raul::Path& path) -{ -} - -void -HTTPClientSender::set_property(const URI& subject, const URI& key, const Atom& value) -{ -#if 0 - Sord::Node node = AtomRDF::atom_to_node(*_engine.world()->rdf_world(), value); - const string msg = string( - "@prefix rdf: .\n" - "@prefix ingen: .\n" - "@prefix ingenui: .\n").append( - subject.str()).append("> ingen:property [\n" - "rdf:predicate ").append(key.str()).append(" ;\n" - "rdf:value ").append(node.to_string()).append("\n] .\n"); - send_chunk(msg); -#endif -} - -void -HTTPClientSender::activity(const Path& path) -{ - const string msg = string( - "@prefix ingen: .\n\n<").append( - path.str()).append("> ingen:activity true .\n"); - send_chunk(msg); -} - -void -HTTPClientSender::move(const Path& old_path, const Path& new_path) -{ - string msg = string( - "@prefix rdf: .\n" - "@prefix ingen: .\n\n<").append( - old_path.str()).append("> rdf:subject <").append(new_path.str()).append("> .\n"); - send_chunk(msg); -} - -} // namespace Engine -} // namespace Ingen diff --git a/src/engine/HTTPClientSender.hpp b/src/engine/HTTPClientSender.hpp deleted file mode 100644 index 5b3cdc73..00000000 --- a/src/engine/HTTPClientSender.hpp +++ /dev/null @@ -1,107 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2008-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_HTTPCLIENTSENDER_HPP -#define INGEN_ENGINE_HTTPCLIENTSENDER_HPP - -#include -#include -#include -#include "raul/Thread.hpp" -#include "ingen/ClientInterface.hpp" -#include "shared/HTTPSender.hpp" - -namespace Ingen { - -class EngineInterface; - -namespace Engine { - -class Engine; - -/** Implements ClientInterface for HTTP clients. - * Sends changes as RDF deltas over an HTTP stream - * (a single message with chunked encoding response). - * - * \ingroup engine - */ -class HTTPClientSender - : public ClientInterface - , public Ingen::Shared::HTTPSender -{ -public: - explicit HTTPClientSender(Engine& engine) - : _engine(engine) - , _enabled(true) - {} - - bool enabled() const { return _enabled; } - - void enable() { _enabled = true; } - void disable() { _enabled = false; } - - void bundle_begin() { HTTPSender::bundle_begin(); } - void bundle_end() { HTTPSender::bundle_end(); } - - Raul::URI uri() const { return "http://example.org/"; } - - /* *** ClientInterface Implementation Below *** */ - - void response_ok(int32_t id); - void response_error(int32_t id, const std::string& msg); - - void error(const std::string& msg); - - virtual void put(const Raul::URI& path, - const Resource::Properties& properties, - Resource::Graph ctx); - - virtual void delta(const Raul::URI& path, - const Resource::Properties& remove, - const Resource::Properties& add); - - virtual void del(const Raul::URI& uri); - - virtual void move(const Raul::Path& old_path, - const Raul::Path& new_path); - - virtual void connect(const Raul::Path& src_port_path, - const Raul::Path& dst_port_path); - - virtual void disconnect(const Raul::URI& src, - const Raul::URI& dst); - - virtual void disconnect_all(const Raul::Path& parent_patch_path, - const Raul::Path& path); - - virtual void set_property(const Raul::URI& subject_path, - const Raul::URI& predicate, - const Raul::Atom& value); - - virtual void activity(const Raul::Path& path); - -private: - Engine& _engine; - std::string _url; - bool _enabled; -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_HTTPCLIENTSENDER_HPP - diff --git a/src/engine/HTTPEngineReceiver.cpp b/src/engine/HTTPEngineReceiver.cpp deleted file mode 100644 index 910be584..00000000 --- a/src/engine/HTTPEngineReceiver.cpp +++ /dev/null @@ -1,230 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "raul/SharedPtr.hpp" -#include "raul/log.hpp" - -#include "ingen/ClientInterface.hpp" -#include "shared/Module.hpp" -#include "serialisation/Parser.hpp" -#include "serialisation/Serialiser.hpp" - -#include "ClientBroadcaster.hpp" -#include "Engine.hpp" -#include "EngineStore.hpp" -#include "EventSource.hpp" -#include "HTTPClientSender.hpp" -#include "HTTPEngineReceiver.hpp" -#include "ThreadManager.hpp" - -#define LOG(s) s << "[HTTPEngineReceiver] " - -using namespace std; -using namespace Raul; - -namespace Ingen { - -using namespace Serialisation; - -namespace Engine { - -HTTPEngineReceiver::HTTPEngineReceiver(Engine& engine, uint16_t port) - : QueuedEngineInterface(engine, 64) // FIXME - , _server(soup_server_new(SOUP_SERVER_PORT, port, NULL)) -{ - _receive_thread = new ReceiveThread(*this); - - soup_server_add_handler(_server, NULL, message_callback, this, NULL); - - LOG(info) << "Started HTTP server on port " << soup_server_get_port(_server) << endl; - - if (!engine.world()->parser() || !engine.world()->serialiser()) - engine.world()->load_module("serialisation"); - - Thread::set_name("HTTPEngineReceiver"); - start(); - _receive_thread->set_name("HTTPEngineReceiver Listener"); - _receive_thread->start(); -} - -HTTPEngineReceiver::~HTTPEngineReceiver() -{ - _receive_thread->stop(); - stop(); - delete _receive_thread; - - if (_server) { - soup_server_quit(_server); - _server = NULL; - } -} - -void -HTTPEngineReceiver::message_callback(SoupServer* server, - SoupMessage* msg, - const char* path_str, - GHashTable* query, - SoupClientContext* client, - void* data) -{ - HTTPEngineReceiver* me = (HTTPEngineReceiver*)data; - - using namespace Ingen::Shared; - - SharedPtr store = me->_engine.world()->store(); - if (!store) { - soup_message_set_status(msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); - return; - } - - string path = path_str; - if (path[path.length() - 1] == '/') { - path = path.substr(0, path.length()-1); - } - - SharedPtr serialiser = me->_engine.world()->serialiser(); - - const string base_uri = "path:/"; - const char* mime_type = "text/plain"; - - // Special GET paths - if (msg->method == SOUP_METHOD_GET) { - if (path == Path::root().str() || path.empty()) { - const string r = string("@prefix rdfs: .\n") - .append("\n<> rdfs:seeAlso ;") - .append("\n rdfs:seeAlso ;") - .append("\n rdfs:seeAlso ."); - soup_message_set_status(msg, SOUP_STATUS_OK); - soup_message_set_response(msg, mime_type, SOUP_MEMORY_COPY, r.c_str(), r.length()); - return; - - } else if (msg->method == SOUP_METHOD_GET && path.substr(0, 8) == "/plugins") { - // FIXME: kludge - #if 0 - me->get("ingen:plugins"); - me->_receive_thread->whip(); - - serialiser->start_to_string("/", base_uri); - for (NodeFactory::Plugins::const_iterator p = me->_engine.node_factory()->plugins().begin(); - p != me->_engine.node_factory()->plugins().end(); ++p) - serialiser->serialise_plugin(*(Shared::Plugin*)p->second); - const string r = serialiser->finish(); - soup_message_set_status(msg, SOUP_STATUS_OK); - soup_message_set_response(msg, mime_type, SOUP_MEMORY_COPY, r.c_str(), r.length()); - #endif - return; - - } else if (path.substr(0, 6) == "/patch") { - path = '/' + path.substr(6); - if (path.substr(0, 2) == "//") - path = path.substr(1); - - } else if (path.substr(0, 7) == "/stream") { - HTTPClientSender* client = new HTTPClientSender(me->_engine); - me->register_client(client); - - // Respond with port number of stream for client - const int port = client->listen_port(); - char buf[32]; - snprintf(buf, sizeof(buf), "%d", port); - soup_message_set_status(msg, SOUP_STATUS_OK); - soup_message_set_response(msg, mime_type, SOUP_MEMORY_COPY, buf, strlen(buf)); - return; - } - } - - if (!Path::is_valid(path)) { - LOG(error) << "Bad HTTP path: " << path << endl; - 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; - } - - // Serialise object - const string response = serialiser->to_string(start->second, - "http://localhost:16180/patch", GraphObject::Properties()); - - soup_message_set_status(msg, SOUP_STATUS_OK); - soup_message_set_response(msg, mime_type, SOUP_MEMORY_COPY, - response.c_str(), response.length()); - - } else if (msg->method == SOUP_METHOD_PUT) { - Glib::RWLock::WriterLock lock(store->lock()); - - // Get parser - SharedPtr parser = me->_engine.world()->parser(); - if (!parser) { - soup_message_set_status(msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); - return; - } - - parser->parse_string(me->_engine.world(), me, msg->request_body->data, base_uri); - soup_message_set_status(msg, SOUP_STATUS_OK); - - } else if (msg->method == SOUP_METHOD_DELETE) { - me->del(path); - soup_message_set_status(msg, SOUP_STATUS_OK); - - } 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 Engine -} // namespace Ingen - diff --git a/src/engine/HTTPEngineReceiver.hpp b/src/engine/HTTPEngineReceiver.hpp deleted file mode 100644 index 0241270a..00000000 --- a/src/engine/HTTPEngineReceiver.hpp +++ /dev/null @@ -1,64 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2008-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_HTTPENGINERECEIVER_HPP -#define INGEN_ENGINE_HTTPENGINERECEIVER_HPP - -#include - -#include - -#include "QueuedEngineInterface.hpp" - -typedef struct _SoupServer SoupServer; -typedef struct _SoupMessage SoupMessage; -typedef struct SoupClientContext SoupClientContext; - -namespace Ingen { -namespace Engine { - -class HTTPEngineReceiver : public QueuedEngineInterface -{ -public: - HTTPEngineReceiver(Engine& engine, uint16_t port); - ~HTTPEngineReceiver(); - -private: - struct ReceiveThread : public Raul::Thread { - explicit ReceiveThread(HTTPEngineReceiver& receiver) : _receiver(receiver) {} - virtual void _run(); - virtual void whip() { - while (_receiver.unprepared_events()) - _receiver.whip(); - } - 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 Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_HTTPENGINERECEIVER_HPP diff --git a/src/engine/InputPort.cpp b/src/engine/InputPort.cpp deleted file mode 100644 index 80058632..00000000 --- a/src/engine/InputPort.cpp +++ /dev/null @@ -1,229 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "ingen/Patch.hpp" -#include "shared/LV2URIMap.hpp" -#include "AudioBuffer.hpp" -#include "BufferFactory.hpp" -#include "ConnectionImpl.hpp" -#include "EventBuffer.hpp" -#include "NodeImpl.hpp" -#include "OutputPort.hpp" -#include "ProcessContext.hpp" -#include "ThreadManager.hpp" -#include "mix.hpp" -#include "util.hpp" - -using namespace std; - -namespace Ingen { -namespace Engine { - -InputPort::InputPort(BufferFactory& bufs, - NodeImpl* parent, - const Raul::Symbol& symbol, - uint32_t index, - uint32_t poly, - PortType type, - const Raul::Atom& value, - size_t buffer_size) - : PortImpl(bufs, parent, symbol, index, poly, type, value, buffer_size) - , _num_connections(0) -{ - const Ingen::Shared::LV2URIMap& uris = bufs.uris(); - - if (!dynamic_cast(parent)) - add_property(uris.rdf_type, uris.lv2_InputPort); - - // Set default control range - if (type == PortType::CONTROL) { - set_property(uris.lv2_minimum, 0.0f); - set_property(uris.lv2_maximum, 1.0f); - } -} - -bool -InputPort::apply_poly(Maid& maid, uint32_t poly) -{ - bool ret = PortImpl::apply_poly(maid, poly); - if (!ret) - poly = 1; - - assert(_buffers->size() >= poly); - - return true; -} - -/** Set \a buffers appropriately if this port has \a num_connections connections. - * \return true iff buffers are locally owned by the port - */ -bool -InputPort::get_buffers(BufferFactory& bufs, - Raul::Array* buffers, - uint32_t poly) -{ - size_t num_connections = (ThreadManager::thread_is(THREAD_PROCESS)) - ? _connections.size() : _num_connections; - - if (buffer_type() == PortType::AUDIO && num_connections == 0) { - // Audio input with no connections, use shared zero buffer - for (uint32_t v = 0; v < poly; ++v) - buffers->at(v) = bufs.silent_buffer(); - return false; - - } else if (num_connections == 1) { - if (ThreadManager::thread_is(THREAD_PROCESS)) { - if (!_connections.front()->must_mix() && !_connections.front()->must_queue()) { - // Single non-mixing conneciton, use buffers directly - for (uint32_t v = 0; v < poly; ++v) - buffers->at(v) = _connections.front()->buffer(v); - return false; - } - } - } - - // Otherwise, allocate local buffers - for (uint32_t v = 0; v < poly; ++v) { - buffers->at(v) = _bufs.get(buffer_type(), _buffer_size); - buffers->at(v)->clear(); - } - 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 copying/mixing needs to take place. - * - * Note that setup_buffers must be called after this before the change - * will audibly take effect. - */ -void -InputPort::add_connection(Connections::Node* const c) -{ - ThreadManager::assert_thread(THREAD_PROCESS); - - _connections.push_back(c); - - // Automatically broadcast connected control inputs - if (is_a(PortType::CONTROL)) - _broadcast = true; -} - -/** Remove a connection. Realtime safe. - * - * Note that setup_buffers must be called after this before the change - * will audibly take effect. - */ -InputPort::Connections::Node* -InputPort::remove_connection(ProcessContext& context, const OutputPort* src_port) -{ - ThreadManager::assert_thread(THREAD_PROCESS); - - Connections::Node* connection = NULL; - for (Connections::iterator i = _connections.begin(); i != _connections.end();) { - Connections::iterator next = i; - ++next; - - if ((*i)->src_port() == src_port) { - connection = _connections.erase(i); - break; - } - - i = next; - } - - if ( ! connection) { - error << "[InputPort::remove_connection] Connection not found!" << endl; - return NULL; - } - - // Turn off broadcasting if we're no longer connected - if (is_a(PortType::CONTROL) && _connections.size() == 0) - _broadcast = false; - - return connection; -} - -/** Prepare buffer for access, mixing if necessary. Realtime safe. - */ -void -InputPort::pre_process(Context& context) -{ - // If value has been set (e.g. events pushed) by the user, don't smash it - if (_set_by_user) - return; - - uint32_t max_num_srcs = 0; - for (Connections::iterator c = _connections.begin(); c != _connections.end(); ++c) - max_num_srcs += (*c)->src_port()->poly(); - - IntrusivePtr srcs[max_num_srcs]; - - if (_connections.empty()) { - for (uint32_t v = 0; v < _poly; ++v) { - buffer(v)->prepare_read(context); - } - } else if (direct_connect()) { - for (uint32_t v = 0; v < _poly; ++v) { - _buffers->at(v) = _connections.front()->buffer(v); - _buffers->at(v)->prepare_read(context); - } - } else { - for (uint32_t v = 0; v < _poly; ++v) { - uint32_t num_srcs = 0; - for (Connections::iterator c = _connections.begin(); c != _connections.end(); ++c) - (*c)->get_sources(context, v, srcs, max_num_srcs, num_srcs); - - mix(context, buffer(v).get(), srcs, num_srcs); - buffer(v)->prepare_read(context); - } - } - - if (_broadcast) - broadcast_value(context, false); -} - -void -InputPort::post_process(Context& context) -{ - if (_set_by_user) { - if (buffer_type() == PortType::EVENTS) { - // Clear events received via a SetPortValue - for (uint32_t v = 0; v < _poly; ++v) { - buffer(v)->clear(); - } - } - _set_by_user = false; - } -} - -bool -InputPort::direct_connect() const -{ - return (context() == Context::AUDIO) - && _connections.size() == 1 - && !_connections.front()->must_mix() - && !_connections.front()->must_queue(); -} - -} // namespace Engine -} // namespace Ingen - diff --git a/src/engine/InputPort.hpp b/src/engine/InputPort.hpp deleted file mode 100644 index 04fd2653..00000000 --- a/src/engine/InputPort.hpp +++ /dev/null @@ -1,93 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_INPUTPORT_HPP -#define INGEN_ENGINE_INPUTPORT_HPP - -#include -#include -#include -#include "raul/List.hpp" -#include "raul/SharedPtr.hpp" -#include "PortImpl.hpp" - -namespace Ingen { -namespace Engine { - -class ConnectionImpl; -class Context; -class NodeImpl; -class OutputPort; -class ProcessContext; - -/** 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(BufferFactory& bufs, - NodeImpl* parent, - const Raul::Symbol& symbol, - uint32_t index, - uint32_t poly, - PortType type, - const Raul::Atom& value, - size_t buffer_size=0); - - virtual ~InputPort() {} - - typedef Raul::List< SharedPtr > Connections; - - void add_connection(Connections::Node* c); - Connections::Node* remove_connection(ProcessContext& context, const OutputPort* src_port); - - bool apply_poly(Raul::Maid& maid, uint32_t poly); - - bool get_buffers(BufferFactory& bufs, - Raul::Array* buffers, - uint32_t poly); - - void pre_process(Context& context); - void post_process(Context& context); - - size_t num_connections() const { return _num_connections; } ///< Pre-process thread - void increment_num_connections() { ++_num_connections; } - void decrement_num_connections() { --_num_connections; } - - bool is_input() const { return true; } - bool is_output() const { return false; } - - bool direct_connect() const; - -protected: - size_t _num_connections; ///< Pre-process thread - Connections _connections; -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_INPUTPORT_HPP diff --git a/src/engine/InternalPlugin.cpp b/src/engine/InternalPlugin.cpp deleted file mode 100644 index f8022e71..00000000 --- a/src/engine/InternalPlugin.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "shared/LV2URIMap.hpp" -#include "internals/Controller.hpp" -#include "internals/Delay.hpp" -#include "internals/Note.hpp" -#include "internals/Trigger.hpp" -#include "Driver.hpp" -#include "Engine.hpp" -#include "InternalPlugin.hpp" - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { - -using namespace Internals; - -InternalPlugin::InternalPlugin(Shared::LV2URIMap& uris, - const std::string& uri, const std::string& symbol) - : PluginImpl(uris, Plugin::Internal, uri) - , _symbol(symbol) -{ - set_property(uris.rdf_type, uris.ingen_Internal); -} - -NodeImpl* -InternalPlugin::instantiate(BufferFactory& bufs, - const string& name, - bool polyphonic, - PatchImpl* parent, - Engine& engine) -{ - assert(_type == Internal); - - const SampleCount srate = engine.driver()->sample_rate(); - - const string uri_str = uri().str(); - - if (uri_str == NS_INTERNALS "Controller") { - return new ControllerNode(this, bufs, name, polyphonic, parent, srate); - } else if (uri_str == NS_INTERNALS "Delay") { - return new DelayNode(this, bufs, name, polyphonic, parent, srate); - } else if (uri_str == NS_INTERNALS "Note") { - return new NoteNode(this, bufs, name, polyphonic, parent, srate); - } else if (uri_str == NS_INTERNALS "Trigger") { - return new TriggerNode(this, bufs, name, polyphonic, parent, srate); - } else { - return NULL; - } -} - -} // namespace Engine -} // namespace Ingen diff --git a/src/engine/InternalPlugin.hpp b/src/engine/InternalPlugin.hpp deleted file mode 100644 index 6301b999..00000000 --- a/src/engine/InternalPlugin.hpp +++ /dev/null @@ -1,63 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_INTERNALPLUGIN_HPP -#define INGEN_ENGINE_INTERNALPLUGIN_HPP - -#include "ingen-config.h" - -#include -#include - -#include -#include - -#include "PluginImpl.hpp" - -#define NS_INTERNALS "http://drobilla.net/ns/ingen-internals#" - -namespace Ingen { -namespace Engine { - -class NodeImpl; -class BufferFactory; - -/** Implementation of an Internal plugin. - */ -class InternalPlugin : public PluginImpl -{ -public: - InternalPlugin(Shared::LV2URIMap& uris, - const std::string& uri, const std::string& symbol); - - NodeImpl* instantiate(BufferFactory& bufs, - const std::string& name, - bool polyphonic, - PatchImpl* parent, - Engine& engine); - - const std::string symbol() const { return _symbol; } - -private: - const std::string _symbol; -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_INTERNALPLUGIN_HPP - diff --git a/src/engine/JackDriver.cpp b/src/engine/JackDriver.cpp deleted file mode 100644 index 6a118778..00000000 --- a/src/engine/JackDriver.cpp +++ /dev/null @@ -1,561 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "ingen-config.h" - -#include -#include - -#include -#ifdef INGEN_JACK_SESSION -#include -#include -#include "serialisation/Serialiser.hpp" -#endif - -#include "raul/log.hpp" -#include "raul/List.hpp" - -#include "lv2/lv2plug.in/ns/ext/event/event.h" - -#include "AudioBuffer.hpp" -#include "ControlBindings.hpp" -#include "DuplexPort.hpp" -#include "Engine.hpp" -#include "Event.hpp" -#include "EventBuffer.hpp" -#include "EventSource.hpp" -#include "JackDriver.hpp" -#include "MessageContext.hpp" -#include "PatchImpl.hpp" -#include "PortImpl.hpp" -#include "PostProcessor.hpp" -#include "ProcessSlave.hpp" -#include "QueuedEvent.hpp" -#include "ThreadManager.hpp" -#include "shared/World.hpp" -#include "shared/LV2Features.hpp" -#include "shared/LV2URIMap.hpp" -#include "util.hpp" - -#define LOG(s) s << "[JackDriver] " - -using namespace std; -using namespace Raul; - -typedef jack_default_audio_sample_t jack_sample_t; - -namespace Ingen { -namespace Engine { - -//// JackPort //// - -JackPort::JackPort(JackDriver* driver, DuplexPort* patch_port) - : DriverPort(patch_port) - , Raul::List::Node(this) - , _driver(driver) - , _jack_port(NULL) -{ - patch_port->setup_buffers(*driver->_engine.buffer_factory(), patch_port->poly()); - create(); -} - -JackPort::~JackPort() -{ - assert(_jack_port == NULL); -} - -void -JackPort::create() -{ - _jack_port = jack_port_register( - _driver->jack_client(), - ingen_jack_port_name(_patch_port->path()).c_str(), - (_patch_port->buffer_type() == PortType::AUDIO) - ? JACK_DEFAULT_AUDIO_TYPE : JACK_DEFAULT_MIDI_TYPE, - (_patch_port->is_input()) - ? JackPortIsInput : JackPortIsOutput, - 0); - - if (_jack_port == NULL) { - error << "[JackPort] Failed to register port " << _patch_port->path() << endl; - throw JackDriver::PortRegistrationFailedException(); - } -} - -void -JackPort::destroy() -{ - assert(_jack_port); - if (jack_port_unregister(_driver->jack_client(), _jack_port)) - error << "[JackPort] Unable to unregister port" << endl; - _jack_port = NULL; -} - -void -JackPort::move(const Raul::Path& path) -{ - jack_port_set_name(_jack_port, ingen_jack_port_name(path).c_str()); -} - -void -JackPort::pre_process(ProcessContext& context) -{ - if (!is_input()) - return; - - const SampleCount nframes = context.nframes(); - - if (_patch_port->buffer_type() == PortType::AUDIO) { - jack_sample_t* jack_buf = (jack_sample_t*)jack_port_get_buffer(_jack_port, nframes); - AudioBuffer* patch_buf = (AudioBuffer*)_patch_port->buffer(0).get(); - - patch_buf->copy(jack_buf, 0, nframes - 1); - - } else if (_patch_port->buffer_type() == PortType::EVENTS) { - void* jack_buf = jack_port_get_buffer(_jack_port, nframes); - EventBuffer* patch_buf = (EventBuffer*)_patch_port->buffer(0).get(); - - const jack_nframes_t event_count = jack_midi_get_event_count(jack_buf); - - patch_buf->prepare_write(context); - - // 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_buf, i); - - if (!patch_buf->append(ev.time, 0, - _driver->_midi_event_type, - ev.size, ev.buffer)) - LOG(warn) << "Failed to write MIDI to port buffer, event(s) lost!" << endl; - } - } -} - -void -JackPort::post_process(ProcessContext& context) -{ - if (is_input()) - return; - - const SampleCount nframes = context.nframes(); - - if (_patch_port->buffer_type() == PortType::AUDIO) { - jack_sample_t* jack_buf = (jack_sample_t*)jack_port_get_buffer(_jack_port, nframes); - AudioBuffer* patch_buf = (AudioBuffer*)_patch_port->buffer(0).get(); - - memcpy(jack_buf, patch_buf->data(), nframes * sizeof(Sample)); - - } else if (_patch_port->buffer_type() == PortType::EVENTS) { - void* jack_buf = jack_port_get_buffer(_jack_port, context.nframes()); - EventBuffer* patch_buf = (EventBuffer*)_patch_port->buffer(0).get(); - - patch_buf->prepare_read(context); - 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); - } - } -} - -//// JackDriver //// - -JackDriver::JackDriver(Engine& engine) - : _engine(engine) - , _jack_thread(NULL) - , _sem(0) - , _flag(0) - , _client(NULL) - , _block_length(0) - , _sample_rate(0) - , _is_activated(false) - , _process_context(engine) - , _root_patch(NULL) -{ - _midi_event_type = _engine.world()->uris()->uri_to_id( - LV2_EVENT_URI, "http://lv2plug.in/ns/ext/midi#MidiEvent"); -} - -JackDriver::~JackDriver() -{ - deactivate(); - - if (_client) - jack_client_close(_client); -} - -bool -JackDriver::supports(PortType port_type, EventType event_type) -{ - return (port_type == PortType::AUDIO - || (port_type == PortType::EVENTS && event_type == EventType::MIDI)); -} - -bool -JackDriver::attach(const std::string& server_name, - const std::string& client_name, - void* jack_client) -{ - assert(!_client); - if (!jack_client) { - #ifdef INGEN_JACK_SESSION - const std::string uuid = _engine.world()->jack_uuid(); - if (!uuid.empty()) { - _client = jack_client_open(client_name.c_str(), - JackSessionID, NULL, - uuid.c_str()); - LOG(info) << "Connected to JACK server as client `" - << client_name.c_str() << "' UUID `" << uuid << "'" << endl; - } - #endif - - // Try supplied server name - if (!_client && !server_name.empty()) { - if ((_client = jack_client_open(client_name.c_str(), - JackServerName, NULL, - server_name.c_str()))) { - LOG(info) << "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) { - if ((_client = jack_client_open(client_name.c_str(), JackNullOption, NULL))) - LOG(info) << "Connected to default JACK server" << endl; - } - - // Still failed - if (!_client) { - LOG(error) << "Unable to connect to Jack" << endl; - return false; - } - } else { - _client = (jack_client_t*)jack_client; - } - - _block_length = 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, block_length_cb, this); -#ifdef INGEN_JACK_SESSION - jack_set_session_callback(_client, session_cb, this); -#endif - - for (Raul::List::iterator i = _ports.begin(); i != _ports.end(); ++i) - (*i)->create(); - - return true; -} - -void -JackDriver::activate() -{ - Shared::World* world = _engine.world(); - - if (_is_activated) { - LOG(warn) << "Jack driver already activated." << endl; - return; - } - - if (!_client) - attach(world->conf()->option("jack-server").get_string(), - world->conf()->option("jack-client").get_string(), NULL); - - jack_set_process_callback(_client, process_cb, this); - - _is_activated = true; - - _process_context.activate(world->conf()->option("parallelism").get_int32(), - is_realtime()); - - if (jack_activate(_client)) { - LOG(error) << "Could not activate Jack client, aborting." << endl; - exit(EXIT_FAILURE); - } else { - LOG(info) << "Activated Jack client." << endl; - } -} - -void -JackDriver::deactivate() -{ - if (_is_activated) { - _flag = 1; - _is_activated = false; - _sem.wait(); - - for (Raul::List::iterator i = _ports.begin(); i != _ports.end(); ++i) - (*i)->destroy(); - - if (_client) { - jack_deactivate(_client); - jack_client_close(_client); - _client = NULL; - } - - _jack_thread->stop(); - LOG(info) << "Deactivated Jack client" << endl; - } -} - -/** 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 -JackDriver::add_port(DriverPort* port) -{ - ThreadManager::assert_thread(THREAD_PROCESS); - assert(dynamic_cast(port)); - _ports.push_back((JackPort*)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. - */ -Raul::Deletable* -JackDriver::remove_port(const Path& path, DriverPort** port) -{ - ThreadManager::assert_thread(THREAD_PROCESS); - - for (Raul::List::iterator i = _ports.begin(); i != _ports.end(); ++i) { - if ((*i)->patch_port()->path() == path) { - Raul::List::Node* node = _ports.erase(i); - if (port) - *port = node->elem(); - return node; - } - } - - LOG(warn) << "Unable to find port " << path << endl; - return NULL; -} - -DriverPort* -JackDriver::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* -JackDriver::create_port(DuplexPort* patch_port) -{ - try { - if (patch_port->buffer_type() == PortType::AUDIO - || patch_port->buffer_type() == PortType::EVENTS) - return new JackPort(this, patch_port); - else - return NULL; - } catch (...) { - return NULL; - } -} - -DriverPort* -JackDriver::driver_port(const Path& path) -{ - ThreadManager::assert_thread(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 -JackDriver::_process_cb(jack_nframes_t nframes) -{ - if (nframes == 0 || ! _is_activated) { - if (_flag == 1) - _sem.post(); - return 0; - } - - // FIXME: all of this time stuff is screwy - - // FIXME: support nframes != buffer_size, even though that never damn well happens - //assert(nframes == _block_length); - - // Note that Jack can not call this function for a cycle, if overloaded - const jack_nframes_t start_of_current_cycle = jack_last_frame_time(_client); - - _transport_state = jack_transport_query(_client, &_position); - - _process_context.locate(start_of_current_cycle, nframes, 0); - - for (ProcessContext::Slaves::iterator i = _process_context.slaves().begin(); - i != _process_context.slaves().end(); ++i) { - (*i)->context().locate(start_of_current_cycle, nframes, 0); - } - - // Read input - for (Raul::List::iterator i = _ports.begin(); i != _ports.end(); ++i) - (*i)->pre_process(_process_context); - - // Apply control bindings to input - _engine.control_bindings()->pre_process(_process_context, - PtrCast(_root_patch->port_impl(0)->buffer(0)).get()); - - _engine.post_processor()->set_end_time(_process_context.end()); - - // Process events that came in during the last cycle - // (Aiming for jitter-free 1 block event latency, ideally) - _engine.process_events(_process_context); - - // Run root patch - if (_root_patch) { - _root_patch->process(_process_context); -#if 0 - static const SampleCount control_block_size = nframes / 2; - for (jack_nframes_t i = 0; i < nframes; i += control_block_size) { - const SampleCount block_size = (i + control_block_size < nframes) - ? control_block_size - : nframes - i; - _process_context.locate(start_of_current_cycle + i, block_size, i); - _root_patch->process(_process_context); - } -#endif - } - - // Emit control binding feedback - _engine.control_bindings()->post_process(_process_context, - PtrCast(_root_patch->port_impl(1)->buffer(0)).get()); - - // Signal message context to run if necessary - if (_engine.message_context()->has_requests()) - _engine.message_context()->signal(_process_context); - - // Write output - for (Raul::List::iterator i = _ports.begin(); i != _ports.end(); ++i) - (*i)->post_process(_process_context); - - return 0; -} - -void -JackDriver::_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); - ThreadManager::assert_thread(THREAD_PROCESS); -} - -void -JackDriver::_shutdown_cb() -{ - LOG(info) << "Jack shutdown. Exiting." << endl; - _is_activated = false; - delete _jack_thread; - _jack_thread = NULL; - _client = NULL; -} - -int -JackDriver::_sample_rate_cb(jack_nframes_t nframes) -{ - if (_is_activated) { - LOG(error) << "On-the-fly sample rate changing not supported (yet). Aborting." << endl; - exit(EXIT_FAILURE); - } else { - _sample_rate = nframes; - } - return 0; -} - -int -JackDriver::_block_length_cb(jack_nframes_t nframes) -{ - if (_root_patch) { - _block_length = nframes; - _root_patch->set_buffer_size(context(), *_engine.buffer_factory(), PortType::AUDIO, - _engine.buffer_factory()->audio_buffer_size(nframes)); - } - return 0; -} - -#ifdef INGEN_JACK_SESSION -void -JackDriver::_session_cb(jack_session_event_t* event) -{ - LOG(info) << "Jack session save to " << event->session_dir << endl; - - const string cmd = (boost::format("ingen -eg -n %1% -u %2% -l ${SESSION_DIR}") - % jack_get_client_name(_client) - % event->client_uuid).str(); - - SharedPtr serialiser = _engine.world()->serialiser(); - if (serialiser) { - SharedPtr root(_engine.driver()->root_patch(), NullDeleter); - serialiser->write_bundle(root, string("file://") + event->session_dir); - } - - event->command_line = strdup(cmd.c_str()); - jack_session_reply(_client, event); - - switch (event->type) { - case JackSessionSave: - break; - case JackSessionSaveAndQuit: - LOG(warn) << "Jack session quit" << endl; - _engine.quit(); - break; - case JackSessionSaveTemplate: - break; - } - - jack_session_event_free(event); -} -#endif - -} // namespace Engine -} // namespace Ingen diff --git a/src/engine/JackDriver.hpp b/src/engine/JackDriver.hpp deleted file mode 100644 index 702692b8..00000000 --- a/src/engine/JackDriver.hpp +++ /dev/null @@ -1,183 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_JACKAUDIODRIVER_HPP -#define INGEN_ENGINE_JACKAUDIODRIVER_HPP - -#include "ingen-config.h" - -#include - -#include -#include -#ifdef INGEN_JACK_SESSION -#include -#endif - -#include "raul/AtomicInt.hpp" -#include "raul/List.hpp" -#include "raul/Semaphore.hpp" -#include "raul/Thread.hpp" - -#include "Buffer.hpp" -#include "Driver.hpp" -#include "ProcessContext.hpp" - -namespace Raul { class Path; } - -namespace Ingen { -namespace Engine { - -class Engine; -class PatchImpl; -class PortImpl; -class DuplexPort; -class JackDriver; - -/** Used internally by JackDriver to represent a Jack port. - * - * A Jack port always has a one-to-one association with a Patch port. - */ -class JackPort : public DriverPort, public Raul::List::Node -{ -public: - JackPort(JackDriver* driver, DuplexPort* patch_port); - ~JackPort(); - - void create(); - void destroy(); - - void move(const Raul::Path& path); - - void pre_process(ProcessContext& context); - void post_process(ProcessContext& context); - - jack_port_t* jack_port() const { return _jack_port; } - -private: - JackDriver* _driver; - jack_port_t* _jack_port; -}; - -/** The Jack Driver. - * - * 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 JackDriver : public Driver -{ -public: - explicit JackDriver(Engine& engine); - ~JackDriver(); - - bool supports(PortType port_type, EventType event_type); - - bool attach(const std::string& server_name, - const std::string& client_name, - void* jack_client); - - 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* driver_port(const Raul::Path& path); - - Raul::Deletable* remove_port(const Raul::Path& path, DriverPort** port=NULL); - - 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 block_length() const { return _block_length; } - SampleCount sample_rate() const { return _sample_rate; } - - inline SampleCount frame_time() const { return _client ? jack_frame_time(_client) : 0; } - - class PortRegistrationFailedException : public std::exception {}; - -private: - friend class JackPort; - - // Static JACK callbacks which call the non-static callbacks (methods) - inline static void thread_init_cb(void* const jack_driver) { - return ((JackDriver*)jack_driver)->_thread_init_cb(); - } - inline static void shutdown_cb(void* const jack_driver) { - return ((JackDriver*)jack_driver)->_shutdown_cb(); - } - inline static int process_cb(jack_nframes_t nframes, void* const jack_driver) { - return ((JackDriver*)jack_driver)->_process_cb(nframes); - } - inline static int block_length_cb(jack_nframes_t nframes, void* const jack_driver) { - return ((JackDriver*)jack_driver)->_block_length_cb(nframes); - } - inline static int sample_rate_cb(jack_nframes_t nframes, void* const jack_driver) { - return ((JackDriver*)jack_driver)->_sample_rate_cb(nframes); - } -#ifdef INGEN_JACK_SESSION - inline static void session_cb(jack_session_event_t* event, void* jack_driver) { - ((JackDriver*)jack_driver)->_session_cb(event); - } -#endif - - // Non static callbacks (methods) - void _thread_init_cb(); - void _shutdown_cb(); - int _process_cb(jack_nframes_t nframes); - int _block_length_cb(jack_nframes_t nframes); - int _sample_rate_cb(jack_nframes_t nframes); -#ifdef INGEN_JACK_SESSION - void _session_cb(jack_session_event_t* event); -#endif - - Engine& _engine; - Raul::Thread* _jack_thread; - Raul::Semaphore _sem; - Raul::AtomicInt _flag; - jack_client_t* _client; - jack_nframes_t _block_length; - jack_nframes_t _sample_rate; - uint32_t _midi_event_type; - bool _is_activated; - jack_position_t _position; - jack_transport_state_t _transport_state; - Raul::List _ports; - ProcessContext _process_context; - PatchImpl* _root_patch; -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_JACKAUDIODRIVER_HPP diff --git a/src/engine/LV2BlobFeature.hpp b/src/engine/LV2BlobFeature.hpp deleted file mode 100644 index a23a3953..00000000 --- a/src/engine/LV2BlobFeature.hpp +++ /dev/null @@ -1,66 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2009-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_LV2BLOBFEATURE_HPP -#define INGEN_ENGINE_LV2BLOBFEATURE_HPP - -#include "shared/LV2Features.hpp" - -namespace Ingen { -namespace Engine { - -struct BlobFeature : public Ingen::Shared::LV2Features::Feature { - BlobFeature() { - LV2_Blob_Support* data = (LV2_Blob_Support*)malloc(sizeof(LV2_Blob_Support)); - data->data = NULL; - data->ref_size = sizeof(LV2_Blob); - data->ref_get = &ref_get; - data->ref_copy = &ref_copy; - data->ref_reset = &ref_reset; - data->blob_new = &blob_new; - _feature.URI = LV2_BLOB_SUPPORT_URI; - _feature.data = data; - } - - static LV2_Blob ref_get(LV2_Blob_Support_Data data, - LV2_Atom_Reference* ref) { return 0; } - - static void ref_copy(LV2_Blob_Support_Data data, - LV2_Atom_Reference* dst, - LV2_Atom_Reference* src) {} - - static void ref_reset(LV2_Blob_Support_Data data, - LV2_Atom_Reference* ref) {} - - static void blob_new(LV2_Blob_Support_Data data, - LV2_Atom_Reference* reference, - LV2_Blob_Destroy destroy, - uint32_t type, - size_t size) {} - - SharedPtr feature(Shared::World*, Node*) { - return SharedPtr(&_feature, NullDeleter); - } - -private: - LV2_Feature _feature; -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_LV2BLOBFEATURE_HPP diff --git a/src/engine/LV2EventFeature.hpp b/src/engine/LV2EventFeature.hpp deleted file mode 100644 index c9ac1c32..00000000 --- a/src/engine/LV2EventFeature.hpp +++ /dev/null @@ -1,54 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2009-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_LV2EVENTFEATURE_HPP -#define INGEN_ENGINE_LV2EVENTFEATURE_HPP - -#include "lv2/lv2plug.in/ns/ext/event/event.h" -#include "shared/LV2Features.hpp" - -namespace Ingen { -namespace Engine { - -struct EventFeature : public Ingen::Shared::LV2Features::Feature { - EventFeature() { - LV2_Event_Feature* data = (LV2_Event_Feature*)malloc(sizeof(LV2_Event_Feature)); - data->lv2_event_ref = &event_ref; - data->lv2_event_unref = &event_unref; - data->callback_data = this; - _feature.URI = LV2_EVENT_URI; - _feature.data = data; - } - - static uint32_t event_ref(LV2_Event_Callback_Data callback_data, - LV2_Event* event) { return 0; } - - static uint32_t event_unref(LV2_Event_Callback_Data callback_data, - LV2_Event* event) { return 0; } - - SharedPtr feature(Shared::World*, Node*) { - return SharedPtr(&_feature, NullDeleter); - } - -private: - LV2_Feature _feature; -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_LV2EVENTFEATURE_HPP diff --git a/src/engine/LV2Info.cpp b/src/engine/LV2Info.cpp deleted file mode 100644 index 4fd7885f..00000000 --- a/src/engine/LV2Info.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2008-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "lv2/lv2plug.in/ns/ext/atom/atom.h" -#include "lv2/lv2plug.in/ns/ext/contexts/contexts.h" - -#include "shared/World.hpp" - -#include "LV2BlobFeature.hpp" -#include "LV2EventFeature.hpp" -#include "LV2Features.hpp" -#include "LV2Info.hpp" -#include "LV2RequestRunFeature.hpp" -#include "LV2ResizeFeature.hpp" - -using namespace std; - -namespace Ingen { -namespace Engine { - -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)) - , value_port_class(slv2_value_new_uri(world->slv2_world(), - "http://lv2plug.in/ns/ext/atom#ValuePort")) - , message_port_class(slv2_value_new_uri(world->slv2_world(), - "http://lv2plug.in/ns/ext/atom#MessagePort")) - , _world(world) -{ - assert(world); - - world->lv2_features()->add_feature(LV2_EVENT_URI, - SharedPtr(new EventFeature())); - world->lv2_features()->add_feature(LV2_BLOB_SUPPORT_URI, - SharedPtr(new BlobFeature())); - world->lv2_features()->add_feature(LV2_RESIZE_PORT_URI, - SharedPtr(new ResizeFeature())); - world->lv2_features()->add_feature(LV2_CONTEXTS_URI "#RequestRunFeature", - SharedPtr(new RequestRunFeature())); -} - -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); - slv2_value_free(value_port_class); - slv2_value_free(message_port_class); -} - -} // namespace Engine -} // namespace Ingen diff --git a/src/engine/LV2Info.hpp b/src/engine/LV2Info.hpp deleted file mode 100644 index 292eb012..00000000 --- a/src/engine/LV2Info.hpp +++ /dev/null @@ -1,62 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2008-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_LV2INFO_HPP -#define INGEN_ENGINE_LV2INFO_HPP - -#include "ingen-config.h" -#ifndef HAVE_SLV2 -#error "This file requires SLV2, but HAVE_SLV2 is not defined. Please report." -#endif - -#include -#include -#include "slv2/slv2.h" -#include "shared/World.hpp" - -namespace Ingen { - -class Node; - -namespace Engine { - -/** Stuff that may need to be passed to an LV2 plugin (i.e. LV2 features). - */ -class LV2Info { -public: - explicit LV2Info(Ingen::Shared::World* world); - ~LV2Info(); - - SLV2Value input_class; - SLV2Value output_class; - SLV2Value control_class; - SLV2Value audio_class; - SLV2Value event_class; - SLV2Value value_port_class; - SLV2Value message_port_class; - - Ingen::Shared::World& world() { return *_world; } - SLV2World lv2_world() { return _world->slv2_world(); } - -private: - Ingen::Shared::World* _world; -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_LV2INFO_HPP diff --git a/src/engine/LV2Node.cpp b/src/engine/LV2Node.cpp deleted file mode 100644 index ea292b97..00000000 --- a/src/engine/LV2Node.cpp +++ /dev/null @@ -1,410 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "raul/log.hpp" -#include "raul/Maid.hpp" -#include "raul/Array.hpp" - -#include "AudioBuffer.hpp" -#include "EventBuffer.hpp" -#include "InputPort.hpp" -#include "LV2Node.hpp" -#include "LV2Plugin.hpp" -#include "LV2URIMap.hpp" -#include "MessageContext.hpp" -#include "OutputPort.hpp" -#include "ProcessContext.hpp" - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { - -/** 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) - : NodeImpl(plugin, name, polyphonic, parent, srate) - , _lv2_plugin(plugin) - , _instances(NULL) - , _prepared_instances(NULL) - , _message_funcs(NULL) -{ - assert(_lv2_plugin); -} - -LV2Node::~LV2Node() -{ - delete _instances; -} - -bool -LV2Node::prepare_poly(BufferFactory& bufs, uint32_t poly) -{ - if (!_polyphonic) - poly = 1; - - NodeImpl::prepare_poly(bufs, poly); - - if (_polyphony == poly) - return true; - - SharedPtr info = _lv2_plugin->lv2_info(); - _prepared_instances = new Instances(poly, *_instances, SharedPtr()); - for (uint32_t i = _polyphony; i < _prepared_instances->size(); ++i) { - _prepared_instances->at(i) = SharedPtr( - slv2_plugin_instantiate( - _lv2_plugin->slv2_plugin(), _srate, _features->array()), - slv2_instance_free); - - if (!_prepared_instances->at(i)) { - error << "Failed to instantiate plugin" << endl; - return false; - } - - // Initialize the values of new ports - for (uint32_t j = 0; j < num_ports(); ++j) { - PortImpl* const port = _ports->at(j); - Buffer* const buffer = port->prepared_buffer(i).get(); - if (buffer) { - if (port->is_a(PortType::CONTROL)) { - ((AudioBuffer*)buffer)->set_value(port->value().get_float(), 0, 0); - } else { - buffer->clear(); - } - } - } - - if (_activated) - slv2_instance_activate((SLV2Instance)(*_prepared_instances)[i].get()); - } - - return true; -} - -bool -LV2Node::apply_poly(Raul::Maid& maid, uint32_t poly) -{ - if (!_polyphonic) - poly = 1; - - if (_prepared_instances) { - maid.push(_instances); - _instances = _prepared_instances; - _prepared_instances = NULL; - } - assert(poly <= _instances->size()); - - return NodeImpl::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(BufferFactory& bufs) -{ - const Ingen::Shared::LV2URIMap& uris = bufs.uris(); - 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 Instances(_polyphony, SharedPtr()); - - _features = info->world().lv2_features()->lv2_features(&info->world(), this); - - uint32_t port_buffer_size = 0; - SLV2Value ctx_ext_uri = slv2_value_new_uri(info->lv2_world(), - LV2_CONTEXTS_URI "#MessageContext"); - - for (uint32_t i = 0; i < _polyphony; ++i) { - (*_instances)[i] = SharedPtr( - slv2_plugin_instantiate(plug, _srate, _features->array()), - slv2_instance_free); - - if (!instance(i)) { - error << "Failed to instantiate plugin " << _lv2_plugin->uri() - << " voice " << i << endl; - return false; - } - - if (!slv2_plugin_has_feature(plug, ctx_ext_uri)) - continue; - - const void* ctx_ext = slv2_instance_get_extension_data( - instance(i), LV2_CONTEXTS_URI "#MessageContext"); - - if (i == 0 && ctx_ext) { - assert(!_message_funcs); - _message_funcs = (LV2_Contexts_MessageContext*)ctx_ext; - } - } - - slv2_value_free(ctx_ext_uri); - - string port_name; - Path port_path; - - PortImpl* port = NULL; - bool ret = true; - - float* min_values = new float[num_ports]; - float* max_values = new float[num_ports]; - float* def_values = new float[num_ports]; - slv2_plugin_get_port_ranges_float(plug, min_values, max_values, def_values); - - SLV2Value context_pred = slv2_value_new_uri(info->lv2_world(), - "http://lv2plug.in/ns/ext/contexts#context"); - - SLV2Value default_pred = slv2_value_new_uri(info->lv2_world(), - "http://lv2plug.in/ns/lv2core#default"); - - SLV2Value min_size_pred = slv2_value_new_uri(info->lv2_world(), - "http://lv2plug.in/ns/ext/resize-port#minimumSize"); - - SLV2Value port_property_pred = slv2_value_new_uri(info->lv2_world(), - "http://lv2plug.in/ns/lv2core#portProperty"); - - SLV2Value supports_pred = slv2_value_new_uri(info->lv2_world(), - "http://lv2plug.in/ns/ext/atom#supports"); - - //SLV2Value as_large_as_pred = slv2_value_new_uri(info->lv2_world(), - // "http://lv2plug.in/ns/ext/resize-port#asLargeAs"); - - for (uint32_t j = 0; j < num_ports; ++j) { - SLV2Port id = slv2_plugin_get_port_by_index(plug, j); - - // LV2 port symbols are guaranteed to be unique, valid C identifiers - port_name = slv2_value_as_string(slv2_port_get_symbol(plug, id)); - - if (!Symbol::is_valid(port_name)) { - error << "Plugin " << _lv2_plugin->uri() << " port " << j - << " has illegal symbol `" << port_name << "'" << endl; - ret = false; - break; - } - - assert(port_name.find('/') == string::npos); - - port_path = path().child(port_name); - - Raul::Atom val; - PortType data_type = PortType::UNKNOWN; - if (slv2_port_is_a(plug, id, info->control_class)) { - data_type = PortType::CONTROL; - } else if (slv2_port_is_a(plug, id, info->audio_class)) { - data_type = PortType::AUDIO; - } else if (slv2_port_is_a(plug, id, info->event_class)) { - data_type = PortType::EVENTS; - } else if (slv2_port_is_a(plug, id, info->value_port_class)) { - data_type = PortType::VALUE; - } else if (slv2_port_is_a(plug, id, info->message_port_class)) { - data_type = PortType::MESSAGE; - } - - port_buffer_size = bufs.default_buffer_size(data_type); - - if (data_type == PortType::VALUE || data_type == PortType::MESSAGE) { - // Get default value, and its length - SLV2Values defaults = slv2_port_get_value(plug, id, default_pred); - SLV2_FOREACH(i, defaults) { - SLV2Value d = slv2_values_get(defaults, i); - if (slv2_value_is_string(d)) { - const char* str_val = slv2_value_as_string(d); - const size_t str_val_len = strlen(str_val); - val = str_val; - port_buffer_size = str_val_len; - } - } - - // Get minimum size, if set in data - SLV2Values sizes = slv2_port_get_value(plug, id, min_size_pred); - SLV2_FOREACH(i, sizes) { - SLV2Value d = slv2_values_get(sizes, i); - if (slv2_value_is_int(d)) { - size_t size_val = slv2_value_as_int(d); - port_buffer_size = size_val; - } - } - } - - 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 == PortType::UNKNOWN || direction == UNKNOWN) { - warn << "Unknown type or direction for port `" << port_name << "'" << endl; - ret = false; - break; - } - - if (val.type() == Atom::NIL) - val = isnan(def_values[j]) ? 0.0f : def_values[j]; - - if (direction == INPUT) - port = new InputPort(bufs, this, port_name, j, _polyphony, data_type, val); - else - port = new OutputPort(bufs, this, port_name, j, _polyphony, data_type, val); - - if (direction == INPUT && data_type == PortType::CONTROL) { - port->set_value(val); - if (!isnan(min_values[j])) { - port->set_property(uris.lv2_minimum, min_values[j]); - } - if (!isnan(max_values[j])) { - port->set_property(uris.lv2_maximum, max_values[j]); - } - } - - // Set lv2:portProperty properties - SLV2Values properties = slv2_port_get_value(plug, id, port_property_pred); - SLV2_FOREACH(i, properties) { - SLV2Value p = slv2_values_get(properties, i); - if (slv2_value_is_uri(p)) { - port->set_property(uris.lv2_portProperty, Raul::URI(slv2_value_as_uri(p))); - } - } - - // Set atom:supports properties - SLV2Values types = slv2_port_get_value(plug, id, supports_pred); - SLV2_FOREACH(i, types) { - SLV2Value type = slv2_values_get(types, i); - if (slv2_value_is_uri(type)) { - port->add_property(uris.atom_supports, Raul::URI(slv2_value_as_uri(type))); - } - } - - SLV2Values contexts = slv2_port_get_value(plug, id, context_pred); - SLV2_FOREACH(i, contexts) { - SLV2Value c = slv2_values_get(contexts, i); - const char* context = slv2_value_as_string(c); - if (!strcmp(LV2_CONTEXTS_URI "#MessageContext", context)) { - if (!_message_funcs) { - warn << _lv2_plugin->uri() - << " has a message port, but no context extension data." << endl; - } - port->set_context(Context::MESSAGE); - } else { - warn << _lv2_plugin->uri() << " port " << i << " has unknown context " - << slv2_value_as_string(c) - << endl; - } - } - - _ports->at(j) = port; - } - - if (!ret) { - delete _ports; - _ports = NULL; - delete _instances; - _instances = NULL; - } - - delete[] min_values; - delete[] max_values; - delete[] def_values; - slv2_value_free(context_pred); - slv2_value_free(default_pred); - slv2_value_free(min_size_pred); - slv2_value_free(port_property_pred); - - return ret; -} - -void -LV2Node::activate(BufferFactory& bufs) -{ - NodeImpl::activate(bufs); - - for (uint32_t i = 0; i < _polyphony; ++i) - slv2_instance_activate(instance(i)); -} - -void -LV2Node::deactivate() -{ - NodeImpl::deactivate(); - - for (uint32_t i = 0; i < _polyphony; ++i) - slv2_instance_deactivate(instance(i)); -} - -void -LV2Node::message_run(MessageContext& context) -{ - for (size_t i = 0; i < num_ports(); ++i) { - PortImpl* const port = _ports->at(i); - if (port->context() == Context::MESSAGE) - port->pre_process(context); - } - - if (!_valid_ports) - _valid_ports = calloc(num_ports() / 8, 1); - - if (_message_funcs) - (*_message_funcs->run)(instance(0)->lv2_handle, _valid_ports, _valid_ports); -} - -void -LV2Node::process(ProcessContext& context) -{ - NodeImpl::pre_process(context); - - for (uint32_t i = 0; i < _polyphony; ++i) - slv2_instance_run(instance(i), context.nframes()); - - NodeImpl::post_process(context); -} - -void -LV2Node::set_port_buffer(uint32_t voice, uint32_t port_num, - IntrusivePtr buf, SampleCount offset) -{ - NodeImpl::set_port_buffer(voice, port_num, buf, offset); - slv2_instance_connect_port(instance(voice), port_num, - buf ? buf->port_data(_ports->at(port_num)->buffer_type(), offset) : NULL); -} - -} // namespace Engine -} // namespace Ingen - diff --git a/src/engine/LV2Node.hpp b/src/engine/LV2Node.hpp deleted file mode 100644 index 7633f4d7..00000000 --- a/src/engine/LV2Node.hpp +++ /dev/null @@ -1,82 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_LV2NODE_HPP -#define INGEN_ENGINE_LV2NODE_HPP - -#include -#include "slv2/slv2.h" -#include "raul/IntrusivePtr.hpp" -#include "lv2/lv2plug.in/ns/ext/contexts/contexts.h" -#include "types.hpp" -#include "NodeImpl.hpp" -#include "LV2Features.hpp" - -namespace Ingen { -namespace Engine { - -class LV2Plugin; - -/** An instance of a LV2 plugin. - * - * \ingroup engine - */ -class LV2Node : public NodeImpl -{ -public: - LV2Node(LV2Plugin* plugin, - const std::string& name, - bool polyphonic, - PatchImpl* parent, - SampleRate srate); - - ~LV2Node(); - - bool instantiate(BufferFactory& bufs); - - bool prepare_poly(BufferFactory& bufs, uint32_t poly); - bool apply_poly(Raul::Maid& maid, uint32_t poly); - - void activate(BufferFactory& bufs); - void deactivate(); - - void message_run(MessageContext& context); - - void process(ProcessContext& context); - - void set_port_buffer(uint32_t voice, uint32_t port_num, - IntrusivePtr buf, SampleCount offset); - -protected: - inline SLV2Instance instance(uint32_t voice) { return (SLV2Instance)(*_instances)[voice].get(); } - - typedef Raul::Array< SharedPtr > Instances; - - LV2Plugin* _lv2_plugin; - Instances* _instances; - Instances* _prepared_instances; - - LV2_Contexts_MessageContext* _message_funcs; - - SharedPtr _features; -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_LV2NODE_HPP - diff --git a/src/engine/LV2Plugin.cpp b/src/engine/LV2Plugin.cpp deleted file mode 100644 index 0efc3abf..00000000 --- a/src/engine/LV2Plugin.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "sord/sordmm.hpp" - -#include "shared/LV2URIMap.hpp" -#include "Driver.hpp" -#include "Engine.hpp" -#include "LV2Node.hpp" -#include "LV2Plugin.hpp" -#include "NodeImpl.hpp" - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { - -LV2Plugin::LV2Plugin(SharedPtr lv2_info, const std::string& uri) - : PluginImpl(*lv2_info->world().uris().get(), Plugin::LV2, uri) - , _slv2_plugin(NULL) - , _lv2_info(lv2_info) -{ - set_property(_uris.rdf_type, _uris.lv2_Plugin); -} - -const string -LV2Plugin::symbol() const -{ - string working = uri().str(); - 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"; -} - -NodeImpl* -LV2Plugin::instantiate(BufferFactory& bufs, - const string& name, - bool polyphonic, - PatchImpl* parent, - Engine& engine) -{ - const SampleCount srate = engine.driver()->sample_rate(); - - load(); // FIXME: unload at some point - - LV2Node* n = new LV2Node(this, name, polyphonic, parent, srate); - - if ( ! n->instantiate(bufs) ) { - delete n; - n = NULL; - } - - return n; -} - -void -LV2Plugin::slv2_plugin(SLV2Plugin p) -{ - _slv2_plugin = p; -} - -const std::string& -LV2Plugin::library_path() const -{ - static const std::string empty_string; - if (_library_path.empty()) { - SLV2Value v = slv2_plugin_get_library_uri(_slv2_plugin); - if (v) { - _library_path = slv2_uri_to_path(slv2_value_as_uri(v)); - } else { - warn << uri() << " has no library path" << endl; - return empty_string; - } - } - - return _library_path; -} - -} // namespace Engine -} // namespace Ingen diff --git a/src/engine/LV2Plugin.hpp b/src/engine/LV2Plugin.hpp deleted file mode 100644 index 6bc9501a..00000000 --- a/src/engine/LV2Plugin.hpp +++ /dev/null @@ -1,76 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_LV2PLUGIN_HPP -#define INGEN_ENGINE_LV2PLUGIN_HPP - -#include "ingen-config.h" - -#ifndef HAVE_SLV2 -#error "This file requires SLV2, but HAVE_SLV2 is not defined. Please report." -#endif - -#include -#include - -#include -#include - -#include "slv2/slv2.h" -#include "raul/SharedPtr.hpp" - -#include "PluginImpl.hpp" -#include "LV2Info.hpp" - -namespace Ingen { -namespace Engine { - -class PatchImpl; -class NodeImpl; - -/** Implementation of an LV2 plugin (loaded shared library). - */ -class LV2Plugin : public PluginImpl -{ -public: - LV2Plugin(SharedPtr lv2_info, const std::string& uri); - - NodeImpl* instantiate(BufferFactory& bufs, - const std::string& name, - bool polyphonic, - PatchImpl* parent, - Engine& engine); - - const std::string symbol() const; - - SharedPtr lv2_info() const { return _lv2_info; } - - const std::string& library_path() const; - - SLV2Plugin slv2_plugin() const { return _slv2_plugin; } - void slv2_plugin(SLV2Plugin p); - -private: - SLV2Plugin _slv2_plugin; - SharedPtr _lv2_info; -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_LV2PLUGIN_HPP - diff --git a/src/engine/LV2RequestRunFeature.hpp b/src/engine/LV2RequestRunFeature.hpp deleted file mode 100644 index 324676a7..00000000 --- a/src/engine/LV2RequestRunFeature.hpp +++ /dev/null @@ -1,84 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2010-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_LV2_REQUEST_RUN_FEATURE_HPP -#define INGEN_ENGINE_LV2_REQUEST_RUN_FEATURE_HPP - -#include "lv2/lv2plug.in/ns/ext/contexts/contexts.h" - -#include "raul/log.hpp" - -#include "shared/LV2Features.hpp" - -#include "Driver.hpp" -#include "Engine.hpp" -#include "MessageContext.hpp" -#include "NodeImpl.hpp" -#include "PortImpl.hpp" - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { - -struct RequestRunFeature : public Ingen::Shared::LV2Features::Feature { - struct Data { - inline Data(Shared::World* w, Node* n) : world(w), node(n) {} - Shared::World* world; - Node* node; - }; - - static void request_run(LV2_Contexts_Request_Run_Data data_ptr, - uint32_t context_uri) { - Data* data = reinterpret_cast(data_ptr); - if (!data->world->local_engine()) - return; - - Engine* engine = (Engine*)data->world->local_engine().get(); - engine->message_context()->run( - dynamic_cast(data->node), - engine->driver()->frame_time()); - } - - static void delete_feature(LV2_Feature* feature) { - delete (Data*)feature->data; - free(feature); - } - - SharedPtr feature(Shared::World* world, Node* n) { - NodeImpl* node = dynamic_cast(n); - if (!node) - return SharedPtr(); - - typedef LV2_Contexts_Request_Run_Feature Feature; - Feature* data = (Feature*)malloc(sizeof(Feature)); - data->data = new Data(world, n); - data->request_run = &request_run; - - LV2_Feature* f = (LV2_Feature*)malloc(sizeof(LV2_Feature)); - f->URI = LV2_CONTEXTS_URI "#RequestRunFeature"; - f->data = data; - - return SharedPtr(f, &delete_feature); - } -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_LV2_REQUEST_RUN_FEATURE_HPP diff --git a/src/engine/LV2ResizeFeature.hpp b/src/engine/LV2ResizeFeature.hpp deleted file mode 100644 index 4d303070..00000000 --- a/src/engine/LV2ResizeFeature.hpp +++ /dev/null @@ -1,73 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2009-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_LV2RESIZEFEATURE_HPP -#define INGEN_ENGINE_LV2RESIZEFEATURE_HPP - -#include "raul/log.hpp" -#include "lv2/lv2plug.in/ns/ext/resize-port/resize-port.h" -#include "shared/LV2Features.hpp" -#include "NodeImpl.hpp" -#include "PortImpl.hpp" - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { - -struct ResizeFeature : public Ingen::Shared::LV2Features::Feature { - static bool resize_port(LV2_Resize_Port_Feature_Data data, - uint32_t index, - size_t size) { - NodeImpl* node = (NodeImpl*)data; - PortImpl* port = node->port_impl(index); - switch (port->context()) { - case Context::MESSAGE: - port->buffer(0)->resize(size); - port->connect_buffers(); - return true; - default: - // TODO: Implement realtime allocator and support this in audio thread - return false; - } - } - - static void delete_feature(LV2_Feature* feature) { - free(feature->data); - free(feature); - } - - SharedPtr feature(Shared::World* w, Node* n) { - NodeImpl* node = dynamic_cast(n); - if (!node) - return SharedPtr(); - LV2_Resize_Port_Feature* data - = (LV2_Resize_Port_Feature*)malloc(sizeof(LV2_Resize_Port_Feature)); - data->data = node; - data->resize_port = &resize_port; - LV2_Feature* f = (LV2_Feature*)malloc(sizeof(LV2_Feature)); - f->URI = LV2_RESIZE_PORT_URI; - f->data = data; - return SharedPtr(f, &delete_feature); - } -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_LV2RESIZEFEATURE_HPP diff --git a/src/engine/MessageContext.cpp b/src/engine/MessageContext.cpp deleted file mode 100644 index db65472a..00000000 --- a/src/engine/MessageContext.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2008-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "lv2/lv2plug.in/ns/ext/contexts/contexts.h" -#include "raul/log.hpp" -#include "ConnectionImpl.hpp" -#include "Engine.hpp" -#include "MessageContext.hpp" -#include "NodeImpl.hpp" -#include "PatchImpl.hpp" -#include "PortImpl.hpp" -#include "ProcessContext.hpp" -#include "ThreadManager.hpp" - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { - -void -MessageContext::run(NodeImpl* node, FrameTime time) -{ - if (ThreadManager::thread_is(THREAD_PROCESS)) { - const Request r(time, node); - _requests.write(sizeof(Request), &r); - // signal() will be called at the end of this process cycle - } else { - assert(node); - Glib::Mutex::Lock lock(_mutex); - _non_rt_request = Request(time, node); - _sem.post(); - _cond.wait(_mutex); - } -} - -void -MessageContext::_run() -{ - Request req; - - while (true) { - _sem.wait(); - - // Enqueue a request from the pre-process thread - { - Glib::Mutex::Lock lock(_mutex); - const Request req = _non_rt_request; - if (req.node) { - _queue.insert(req); - _end_time = std::max(_end_time, req.time); - _cond.broadcast(); // Notify caller we got the message - } - } - - // Enqueue (and thereby sort) requests from audio thread - while (has_requests()) { - _requests.full_read(sizeof(Request), &req); - if (req.node) { - _queue.insert(req); - } else { - _end_time = req.time; - break; - } - } - - // Run events in time increasing order - // Note that executing nodes may insert further events into the queue - while (!_queue.empty()) { - const Request req = *_queue.begin(); - - // Break if all events during this cycle have been consumed - // (the queue may contain generated events with later times) - if (req.time > _end_time) { - break; - } - - _queue.erase(_queue.begin()); - execute(req); - } - } -} - -void -MessageContext::execute(const Request& req) -{ - NodeImpl* node = req.node; - node->message_run(*this); - - void* valid_ports = node->valid_ports(); - PatchImpl* patch = node->parent_patch(); - - for (uint32_t i = 0; i < node->num_ports(); ++i) { - PortImpl* p = node->port_impl(i); - if (p->is_output() && p->context() == Context::MESSAGE && - lv2_contexts_port_is_valid(valid_ports, i)) { - PatchImpl::Connections& wires = patch->connections(); - for (PatchImpl::Connections::iterator c = wires.begin(); c != wires.end(); ++c) { - ConnectionImpl* ci = (ConnectionImpl*)c->second.get(); - if (ci->src_port() == p && ci->dst_port()->context() == Context::MESSAGE) { - _queue.insert(Request(req.time, ci->dst_port()->parent_node())); - } - } - } - } - - node->reset_valid_ports(); -} - -} // namespace Engine -} // namespace Ingen diff --git a/src/engine/MessageContext.hpp b/src/engine/MessageContext.hpp deleted file mode 100644 index ccdda448..00000000 --- a/src/engine/MessageContext.hpp +++ /dev/null @@ -1,114 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_MESSAGECONTEXT_HPP -#define INGEN_ENGINE_MESSAGECONTEXT_HPP - -#include -#include -#include "raul/Thread.hpp" -#include "raul/Semaphore.hpp" -#include "raul/AtomicPtr.hpp" -#include "lv2/lv2plug.in/ns/ext/atom/atom.h" -#include "Context.hpp" -#include "ProcessContext.hpp" -#include "ThreadManager.hpp" - -namespace Ingen { -namespace Engine { - -class NodeImpl; - -/** Context of a message_run() 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 Raul::Thread -{ -public: - explicit MessageContext(Engine& engine) - : Context(engine, MESSAGE) - , Raul::Thread("MessageContext") - , _sem(0) - , _requests(engine.event_queue_size()) - , _end_time(0) - { - Thread::set_context(THREAD_MESSAGE); - } - - /** Schedule a message context run at a certain time. - * Safe to call from either process thread or pre-process thread. - */ - void run(NodeImpl* node, FrameTime time); - -protected: - struct Request { - Request(FrameTime t=0, NodeImpl* n=0) : time(t), node(n) {} - FrameTime time; - NodeImpl* node; - }; - -public: - /** Signal the end of a cycle that has produced messages. - * AUDIO THREAD ONLY. - */ - inline void signal(ProcessContext& context) { - ThreadManager::assert_thread(THREAD_PROCESS); - const Request cycle_end_request(context.end(), NULL); - _requests.write(sizeof(Request), &cycle_end_request); - _sem.post(); - } - - /** Return true iff requests are pending. Safe from any thread. */ - inline bool has_requests() const { - return _requests.read_space() >= sizeof(Request); - } - -protected: - /** Thread run method (wait for and execute requests from process thread */ - void _run(); - - /** Execute a request (possibly enqueueing more requests) */ - void execute(const Request& req); - - Raul::Semaphore _sem; - Raul::RingBuffer _requests; - Glib::Mutex _mutex; - Glib::Cond _cond; - Request _non_rt_request; - - struct RequestEarlier { - bool operator()(const Request& r1, const Request& r2) { - return r1.time < r2.time; - } - }; - - typedef std::set Queue; - Queue _queue; - FrameTime _end_time; -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_MESSAGECONTEXT_HPP - diff --git a/src/engine/NodeFactory.cpp b/src/engine/NodeFactory.cpp deleted file mode 100644 index 20ad9fc2..00000000 --- a/src/engine/NodeFactory.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "sord/sordmm.hpp" -#include "raul/log.hpp" -#include "ingen-config.h" -#include "shared/World.hpp" -#include "internals/Controller.hpp" -#include "internals/Delay.hpp" -#include "internals/Note.hpp" -#include "internals/Trigger.hpp" -#include "Engine.hpp" -#include "InternalPlugin.hpp" -#include "NodeFactory.hpp" -#include "PatchImpl.hpp" -#include "ThreadManager.hpp" -#ifdef HAVE_SLV2 -#include "slv2/slv2.h" -#include "LV2Plugin.hpp" -#include "LV2Node.hpp" -#endif - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { - -using namespace Internals; - -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) - delete i->second; - - _plugins.clear(); -} - -const NodeFactory::Plugins& -NodeFactory::plugins() -{ - if (!_has_loaded) { - // TODO: Plugin list refreshing - load_plugins(); - } - return _plugins; -} - -PluginImpl* -NodeFactory::plugin(const Raul::URI& uri) -{ - const Plugins::const_iterator i = _plugins.find(uri); - return ((i != _plugins.end()) ? i->second : NULL); -} - -void -NodeFactory::load_plugins() -{ - ThreadManager::assert_thread(THREAD_PRE_PROCESS); - - // 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 - - _has_loaded = true; - } -} - -void -NodeFactory::load_internal_plugins() -{ - Ingen::Shared::LV2URIMap& uris = *_world->uris().get(); - InternalPlugin* controller_plug = ControllerNode::internal_plugin(uris); - _plugins.insert(make_pair(controller_plug->uri(), controller_plug)); - - InternalPlugin* delay_plug = DelayNode::internal_plugin(uris); - _plugins.insert(make_pair(delay_plug->uri(), delay_plug)); - - InternalPlugin* note_plug = NoteNode::internal_plugin(uris); - _plugins.insert(make_pair(note_plug->uri(), note_plug)); - - InternalPlugin* trigger_plug = TriggerNode::internal_plugin(uris); - _plugins.insert(make_pair(trigger_plug->uri(), trigger_plug)); -} - -#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()); - - SLV2_FOREACH(i, plugins) { - SLV2Plugin lv2_plug = slv2_plugins_get(plugins, i); - - const string uri(slv2_value_as_uri(slv2_plugin_get_uri(lv2_plug))); - - assert(_plugins.find(uri) == _plugins.end()); - - LV2Plugin* const plugin = new LV2Plugin(_lv2_info, uri); - - plugin->slv2_plugin(lv2_plug); - _plugins.insert(make_pair(uri, plugin)); - } -} -#endif // HAVE_SLV2 - -} // namespace Engine -} // namespace Ingen diff --git a/src/engine/NodeFactory.hpp b/src/engine/NodeFactory.hpp deleted file mode 100644 index 3c906663..00000000 --- a/src/engine/NodeFactory.hpp +++ /dev/null @@ -1,76 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_NODEFACTORY_HPP -#define INGEN_ENGINE_NODEFACTORY_HPP - -#include -#include -#include -#include -#include -#include "raul/SharedPtr.hpp" -#include "raul/URI.hpp" -#include "shared/World.hpp" -#include "ingen-config.h" - -namespace Ingen { -namespace Engine { - -class NodeImpl; -class PatchImpl; -class PluginImpl; -#ifdef HAVE_SLV2 -class LV2Info; -#endif - -/** Discovers and loads plugin libraries. - * - * \ingroup engine - */ -class NodeFactory -{ -public: - explicit NodeFactory(Ingen::Shared::World* world); - ~NodeFactory(); - - void load_plugins(); - - typedef std::map Plugins; - const Plugins& plugins(); - - PluginImpl* plugin(const Raul::URI& uri); - -private: -#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 Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_NODEFACTORY_HPP diff --git a/src/engine/NodeImpl.cpp b/src/engine/NodeImpl.cpp deleted file mode 100644 index fc08c8de..00000000 --- a/src/engine/NodeImpl.cpp +++ /dev/null @@ -1,264 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "lv2/lv2plug.in/ns/ext/contexts/contexts.h" -#include "raul/List.hpp" -#include "raul/Array.hpp" -#include "util.hpp" -#include "AudioBuffer.hpp" -#include "ClientBroadcaster.hpp" -#include "EngineStore.hpp" -#include "NodeImpl.hpp" -#include "PatchImpl.hpp" -#include "PluginImpl.hpp" -#include "PortImpl.hpp" -#include "ThreadManager.hpp" - -using namespace std; - -namespace Ingen { -namespace Engine { - -NodeImpl::NodeImpl(PluginImpl* plugin, const Raul::Symbol& symbol, bool polyphonic, PatchImpl* parent, SampleRate srate) - : GraphObjectImpl(plugin->uris(), parent, symbol) - , _plugin(plugin) - , _polyphonic(polyphonic) - , _polyphony((polyphonic && parent) ? parent->internal_poly() : 1) - , _srate(srate) - , _valid_ports(NULL) - , _input_ready(1) - , _process_lock(0) - , _n_inputs_ready(0) - , _ports(NULL) - , _providers(new Raul::List()) - , _dependants(new Raul::List()) - , _activated(false) - , _traversed(false) -{ - assert(_plugin); - assert(_polyphony > 0); - assert(_parent == NULL || (_polyphony == parent->internal_poly() || _polyphony == 1)); -} - -NodeImpl::~NodeImpl() -{ - if (_activated) - deactivate(); - - delete _providers; - delete _dependants; - delete _ports; - - free(_valid_ports); -} - -Port* -NodeImpl::port(uint32_t index) const -{ - return (*_ports)[index]; -} - -const Plugin* -NodeImpl::plugin() const -{ - return _plugin; -} - -void -NodeImpl::activate(BufferFactory& bufs) -{ - ThreadManager::assert_thread(THREAD_PRE_PROCESS); - assert(!_activated); - _activated = true; - - for (uint32_t p = 0; p < num_ports(); ++p) { - PortImpl* const port = _ports->at(p); - port->setup_buffers(bufs, port->poly()); - port->connect_buffers(); - for (uint32_t v = 0; v < _polyphony; ++v) { - if (!port->buffer(v)) - continue; - if (port->is_a(PortType::CONTROL)) - ((AudioBuffer*)port->buffer(v).get())->set_value(port->value().get_float(), 0, 0); - else - port->buffer(v)->clear(); - } - } -} - -void -NodeImpl::deactivate() -{ - assert(_activated); - _activated = false; - for (uint32_t i = 0; i < _polyphony; ++i) { - for (unsigned long j = 0; j < num_ports(); ++j) { - PortImpl* const port = _ports->at(j); - if (port->is_output() && port->buffer(i)) - port->buffer(i)->clear(); - } - } -} - -bool -NodeImpl::prepare_poly(BufferFactory& bufs, uint32_t poly) -{ - ThreadManager::assert_thread(THREAD_PRE_PROCESS); - - if (!_polyphonic) - poly = 1; - - if (_ports) - for (size_t i = 0; i < _ports->size(); ++i) - _ports->at(i)->prepare_poly(bufs, poly); - - return true; -} - -bool -NodeImpl::apply_poly(Raul::Maid& maid, uint32_t poly) -{ - ThreadManager::assert_thread(THREAD_PROCESS); - - if (!_polyphonic) - poly = 1; - - _polyphony = poly; - - if (_ports) - for (size_t i = 0; i < num_ports(); ++i) - _ports->at(i)->apply_poly(maid, poly); - - return true; -} - -void -NodeImpl::set_buffer_size(Context& context, BufferFactory& bufs, PortType type, size_t size) -{ - if (_ports) - for (size_t i = 0; i < _ports->size(); ++i) - if (_ports->at(i)->buffer_type() == type && _ports->at(i)->context() == context.id()) - _ports->at(i)->set_buffer_size(context, bufs, size); -} - -void -NodeImpl::reset_input_ready() -{ - _n_inputs_ready = 0; - _process_lock = 0; - _input_ready.reset(0); -} - -bool -NodeImpl::process_lock() -{ - return _process_lock.compare_and_exchange(0, 1); -} - -void -NodeImpl::process_unlock() -{ - _process_lock = 0; -} - -void -NodeImpl::wait_for_input(size_t num_providers) -{ - ThreadManager::assert_thread(THREAD_PROCESS); - assert(_process_lock.get() == 1); - - while ((unsigned)_n_inputs_ready.get() < num_providers) - _input_ready.wait(); -} - -void -NodeImpl::signal_input_ready() -{ - ThreadManager::assert_thread(THREAD_PROCESS); - ++_n_inputs_ready; - _input_ready.post(); -} - -/** Prepare to run a cycle (in the audio thread) - */ -void -NodeImpl::pre_process(Context& context) -{ - ThreadManager::assert_thread(THREAD_PROCESS); - - // Mix down input ports - for (uint32_t i = 0; i < num_ports(); ++i) { - PortImpl* const port = _ports->at(i); - if (port->context() == Context::AUDIO) { - port->pre_process(context); - port->connect_buffers(context.offset()); - } - } -} - -/** Prepare to run a cycle (in the audio thread) - */ -void -NodeImpl::post_process(Context& context) -{ - ThreadManager::assert_thread(THREAD_PROCESS); - - // Write output ports - for (size_t i = 0; _ports && i < _ports->size(); ++i) { - PortImpl* const port = _ports->at(i); - if (port->context() == Context::AUDIO) - _ports->at(i)->post_process(context); - } -} - -/** Flag a port as set (for message context) - */ -void -NodeImpl::set_port_valid(uint32_t port_index) -{ - // Allocate enough space for one bit per port - if (!_valid_ports) - _valid_ports = calloc(num_ports() / 8, 1); - lv2_contexts_set_port_valid(_valid_ports, port_index); -} - -void* -NodeImpl::valid_ports() -{ - return _valid_ports; -} - -void -NodeImpl::reset_valid_ports() -{ - if (_valid_ports) - memset(_valid_ports, '\0', num_ports() / 8); -} - -void -NodeImpl::set_port_buffer(uint32_t voice, uint32_t port_num, - BufferFactory::Ref buf, SampleCount offset) -{ - /*std::cout << path() << " set port " << port_num << " voice " << voice - << " buffer " << buf << " offset " << offset << std::endl;*/ -} - -} // namespace Engine -} // namespace Ingen - diff --git a/src/engine/NodeImpl.hpp b/src/engine/NodeImpl.hpp deleted file mode 100644 index d2c49e4f..00000000 --- a/src/engine/NodeImpl.hpp +++ /dev/null @@ -1,223 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_NODEIMPL_HPP -#define INGEN_ENGINE_NODEIMPL_HPP - -#include -#include "raul/Array.hpp" -#include "raul/AtomicInt.hpp" -#include "raul/IntrusivePtr.hpp" -#include "raul/Semaphore.hpp" -#include "ingen/Node.hpp" -#include "GraphObjectImpl.hpp" -#include "types.hpp" - -namespace Raul { template class List; class Maid; } - -namespace Ingen { - -class Plugin; -class Port; - -namespace Engine { - -class Buffer; -class BufferFactory; -class Context; -class MessageContext; -class PatchImpl; -class PluginImpl; -class PortImpl; -class ProcessContext; - -/** 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. - * - * \ingroup engine - */ -class NodeImpl : public GraphObjectImpl, virtual public Node -{ -public: - NodeImpl(PluginImpl* plugin, - const Raul::Symbol& symbol, - bool poly, - PatchImpl* parent, - SampleRate rate); - - virtual ~NodeImpl(); - - /** Activate this Node. - * - * This function must 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(BufferFactory& bufs); - - /** Deactivate this Node. - * - * This function must be called in a non-realtime thread after the - * node has been removed from its patch (i.e. processing is finished). - */ - virtual void deactivate(); - - /** Return true iff this node is activated */ - bool activated() { return _activated; } - - /** Parallelism: Reset flags for start of a new cycle. */ - virtual void reset_input_ready(); - - /** 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(); - - /** 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(); - - /** 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); - - /** 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(); - - /** Parallelism: Return the number of providers that have signalled. */ - virtual unsigned n_inputs_ready() const { return _n_inputs_ready.get(); } - - /** Learn the next incoming MIDI event (for internals) */ - virtual void learn() {} - - /** Run the node for one instant in the message thread. */ - virtual void message_run(MessageContext& context) {} - - /** Flag a port as valid (for message context) */ - virtual void set_port_valid(uint32_t index); - - /** Return a bit vector of which ports are valid */ - virtual void* valid_ports(); - - /** Clear all bits in valid_ports() */ - virtual void reset_valid_ports(); - - /** Do whatever needs doing in the process thread before process() is called */ - virtual void pre_process(Context& context); - - /** 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; - - /** Do whatever needs doing in the process thread after process() is called */ - virtual void post_process(Context& context); - - /** Set the buffer of a port to a given buffer (e.g. connect plugin to buffer) */ - virtual void set_port_buffer( - uint32_t voice, - uint32_t port_num, - IntrusivePtr buf, - SampleCount offset); - - virtual Port* port(uint32_t index) const; - virtual PortImpl* port_impl(uint32_t index) const { return (*_ports)[index]; } - - /** Nodes that are connected to this Node's inputs. - * (This Node depends on them) - */ - Raul::List* providers() { return _providers; } - void providers(Raul::List* l) { _providers = l; } - - /** Nodes are are connected to this Node's outputs. - * (They depend on this Node) - */ - Raul::List* dependants() { return _dependants; } - void dependants(Raul::List* l) { _dependants = l; } - - /** Flag node as polyphonic. - * - * Note this will not actually allocate voices etc., prepare_poly - * and apply_poly must be called after this function to truly make - * a node polyphonic. - */ - virtual void set_polyphonic(bool p) { _polyphonic = p; } - - virtual bool prepare_poly(BufferFactory& bufs, uint32_t poly); - virtual bool apply_poly(Raul::Maid& maid, uint32_t poly); - - /** 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 { return _plugin; } - - /** Information about the Plugin this Node is an instance of. - * Not the best name - not all nodes come from plugins (ie Patch) - */ - virtual const Plugin* plugin() const; - - virtual void plugin(PluginImpl* pi) { _plugin = pi; } - - virtual void set_buffer_size(Context& context, BufferFactory& bufs, - PortType type, size_t size); - - /** The Patch this Node belongs to. */ - inline PatchImpl* parent_patch() const { return (PatchImpl*)_parent; } - - SampleRate sample_rate() const { return _srate; } - virtual uint32_t num_ports() const { return _ports ? _ports->size() : 0; } - virtual uint32_t polyphony() const { return _polyphony; } - - /** Used by the process order finding algorithm (ie during connections) */ - bool traversed() const { return _traversed; } - void traversed(bool b) { _traversed = b; } - -protected: - PluginImpl* _plugin; - - bool _polyphonic; - uint32_t _polyphony; - SampleRate _srate; - - void* _valid_ports; ///< Valid port flags for message context - - 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 - - bool _activated; - bool _traversed; ///< Flag for process order algorithm -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_NODEIMPL_HPP diff --git a/src/engine/OSCClientSender.cpp b/src/engine/OSCClientSender.cpp deleted file mode 100644 index bcd3be10..00000000 --- a/src/engine/OSCClientSender.cpp +++ /dev/null @@ -1,247 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "raul/log.hpp" -#include "raul/AtomLiblo.hpp" - -#include "EngineStore.hpp" -#include "NodeImpl.hpp" -#include "OSCClientSender.hpp" -#include "PatchImpl.hpp" - -#include "PluginImpl.hpp" -#include "PortImpl.hpp" -#include "ingen/ClientInterface.hpp" -#include "util.hpp" - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { - -/** @page client_osc_namespace Client OSC Namespace Documentation - * - *

These are the commands the client recognizes. All monitoring of - * changes in the engine happens via these commands.

- */ - -/** @page client_osc_namespace - *

/ok

- * @arg @p response-id :: Integer - * - * @par - * Successful response to some command. - */ -void -OSCClientSender::response_ok(int32_t id) -{ - if (!_enabled) - return; - - - - if (lo_send(_address, "/ok", "i", id, LO_ARGS_END) < 0) { - Raul::error << "Unable to send OK " << id << "! (" - << lo_address_errstr(_address) << ")" << endl; - } -} - -/** @page client_osc_namespace - *

/error

- * @arg @p response-id :: Integer - * @arg @p message :: String - * - * @par - * Unsuccessful response to some command. - */ -void -OSCClientSender::response_error(int32_t id, const std::string& msg) -{ - if (!_enabled) - return; - - if (lo_send(_address, "/error", "is", id, msg.c_str(), LO_ARGS_END) < 0) { - Raul::error << "Unable to send error " << id << "! (" - << lo_address_errstr(_address) << ")" << endl; - } -} - -/** @page client_osc_namespace - *

/error

- * @arg @p message :: String - * - * @par - * Notification that an error has occurred. This is for notification of errors - * that aren't a direct response to a user command, i.e. "unexpected" errors. - */ -void -OSCClientSender::error(const std::string& msg) -{ - send("/error", "s", msg.c_str(), LO_ARGS_END); -} - -/** @page client_osc_namespace - *

/put

- * @arg @p path :: String - * @arg @p predicate :: URI String - * @arg @p value - * @arg @p ... - * - * @par - * PUT a set of properties to a path. - */ -void -OSCClientSender::put(const Raul::URI& path, - const Resource::Properties& properties, - Resource::Graph ctx) -{ - typedef Resource::Properties::const_iterator iterator; - lo_message m = lo_message_new(); - lo_message_add_string(m, path.c_str()); - for (iterator i = properties.begin(); i != properties.end(); ++i) { - lo_message_add_string(m, i->first.c_str()); - Raul::AtomLiblo::lo_message_add_atom(m, i->second); - } - send_message("/put", m); -} - -void -OSCClientSender::delta(const Raul::URI& path, - const Resource::Properties& remove, - const Resource::Properties& add) -{ - warn << "FIXME: OSC DELTA" << endl; -} - -/** @page client_osc_namespace - *

/move

- * @arg @p old-path :: String - * @arg @p new-path :: String - * - * @par - * MOVE an object to a new path. - * The new path will have the same parent as the old path. - */ -void -OSCClientSender::move(const Path& old_path, const Path& new_path) -{ - send("/move", "ss", old_path.c_str(), new_path.c_str(), LO_ARGS_END); -} - -/** @page client_osc_namespace - *

/delete

- * @arg @p path :: String - * - * @par - * DELETE an object. - */ -void -OSCClientSender::del(const URI& uri) -{ - send("/delete", "s", uri.c_str(), LO_ARGS_END); -} - -/** @page client_osc_namespace - *

/connect

- * @arg @p src-path :: String - * @arg @p dst-path :: String - * - * @par - * Notification a new connection has been made. - */ -void -OSCClientSender::connect(const Path& src_port_path, - const Path& dst_port_path) -{ - send("/connect", "ss", src_port_path.c_str(), dst_port_path.c_str(), LO_ARGS_END); -} - -/** @page client_osc_namespace - *

/disconnect

- * @arg @p src-path :: String - * @arg @p dst-path :: String - * - * @par - * Notification a connection has been unmade. - */ -void -OSCClientSender::disconnect(const URI& src, - const URI& dst) -{ - send("/disconnect", "ss", src.c_str(), dst.c_str(), LO_ARGS_END); -} - -/** @page client_osc_namespace - *

/disconnect_all

- * @arg @p parent-patch-path :: String - * @arg @p path :: String - * - * @par - * Notification all connections to an object have been disconnected. - */ -void -OSCClientSender::disconnect_all(const Raul::Path& parent_patch_path, - const Raul::Path& path) -{ - send("/disconnect_all", "ss", parent_patch_path.c_str(), path.c_str(), LO_ARGS_END); -} - -/** @page client_osc_namespace - *

/set_property

- * @arg @p path :: String - * @arg @p key :: URI String - * @arg @p value - * - * @par - * Notification of a property. - */ -void -OSCClientSender::set_property(const URI& path, - const URI& 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()); - AtomLiblo::lo_message_add_atom(m, value); - send_message("/set_property", m); -} - -/** @page client_osc_namespace - *

/activity

- * @arg @p path :: String - * - * @par - * Notification of "activity" (e.g. port message blinkenlights). - */ -void -OSCClientSender::activity(const Path& path) -{ - if (!_enabled) - return; - - lo_send(_address, "/activity", "s", path.c_str(), LO_ARGS_END); -} - -} // namespace Engine -} // namespace Ingen diff --git a/src/engine/OSCClientSender.hpp b/src/engine/OSCClientSender.hpp deleted file mode 100644 index 96263263..00000000 --- a/src/engine/OSCClientSender.hpp +++ /dev/null @@ -1,108 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_OSCCLIENTSENDER_HPP -#define INGEN_ENGINE_OSCCLIENTSENDER_HPP - -#include -#include -#include -#include -#include "ingen/ClientInterface.hpp" -#include "ingen/GraphObject.hpp" -#include "shared/OSCSender.hpp" - -namespace Ingen { - -class EngineInterface; - -namespace Engine { - - -/** Implements ClientInterface for OSC clients (sends OSC messages). - * - * \ingroup engine - */ -class OSCClientSender : public ClientInterface, - public Ingen::Shared::OSCSender -{ -public: - explicit OSCClientSender(const Raul::URI& url, - size_t max_packet_size) - : Shared::OSCSender(max_packet_size) - , _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(); } - - Raul::URI uri() const { return _url; } - - /* *** ClientInterface Implementation Below *** */ - - void response_ok(int32_t id); - void response_error(int32_t id, const std::string& msg); - - void error(const std::string& msg); - - virtual void put(const Raul::URI& path, - const Resource::Properties& properties, - Resource::Graph ctx=Resource::DEFAULT); - - virtual void delta(const Raul::URI& path, - const Resource::Properties& remove, - const Resource::Properties& add); - - virtual void del(const Raul::URI& uri); - - virtual void move(const Raul::Path& old_path, - const Raul::Path& new_path); - - virtual void connect(const Raul::Path& src_port_path, - const Raul::Path& dst_port_path); - - virtual void disconnect(const Raul::URI& src, - const Raul::URI& dst); - - virtual void disconnect_all(const Raul::Path& parent_patch_path, - const Raul::Path& path); - - virtual void set_property(const Raul::URI& subject, - const Raul::URI& predicate, - const Raul::Atom& value); - - virtual void activity(const Raul::Path& path); - -private: - Raul::URI _url; -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_OSCCLIENTSENDER_HPP - diff --git a/src/engine/OSCEngineReceiver.cpp b/src/engine/OSCEngineReceiver.cpp deleted file mode 100644 index fdb0bcc4..00000000 --- a/src/engine/OSCEngineReceiver.cpp +++ /dev/null @@ -1,586 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 ENABLE_AVAHI 1 - -#include -#include - -#include - -#include - -#include "raul/AtomLiblo.hpp" -#include "raul/Path.hpp" -#include "raul/SharedPtr.hpp" -#include "raul/log.hpp" - -#include "ingen-config.h" -#include "ingen/ClientInterface.hpp" - -#include "ClientBroadcaster.hpp" -#include "Engine.hpp" -#include "EventSource.hpp" -#include "OSCClientSender.hpp" -#include "OSCEngineReceiver.hpp" -#include "ThreadManager.hpp" - -#define LOG(s) s << "[OSCEngineReceiver] " - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { - -/** @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.

- */ - -OSCEngineReceiver::OSCEngineReceiver(Engine& engine, size_t queue_size, uint16_t port) - : QueuedEngineInterface(engine, queue_size) // FIXME - , _server(NULL) -{ - _receive_thread = new ReceiveThread(*this); - - char port_str[6]; - snprintf(port_str, sizeof(port_str), "%u", port); - - _server = lo_server_new(port_str, error_cb); -#ifdef ENABLE_AVAHI - lo_server_avahi_init(_server, "ingen"); -#endif - - if (_server == NULL) { - LOG(error) << "Could not start OSC server. Aborting." << endl; - exit(EXIT_FAILURE); - } else { - char* lo_url = lo_server_get_url(_server); - LOG(info) << "Started OSC server at " << lo_url << endl; - free(lo_url); - } - -#ifdef RAUL_LOG_DEBUG - lo_server_add_method(_server, NULL, NULL, generic_cb, NULL); -#endif - - // 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); - -#ifdef LIBLO_BUNDLES - lo_server_add_bundle_handlers(_server, bundle_start_cb, bundle_end_cb, this); -#endif - - // Commands - lo_server_add_method(_server, "/ping", "i", ping_cb, this); - lo_server_add_method(_server, "/ping_queued", "i", ping_slow_cb, this); - lo_server_add_method(_server, "/register_client", "i", register_client_cb, this); - lo_server_add_method(_server, "/unregister_client", "i", unregister_client_cb, this); - lo_server_add_method(_server, "/put", NULL, put_cb, this); - lo_server_add_method(_server, "/move", "iss", move_cb, this); - lo_server_add_method(_server, "/delete", "is", del_cb, this); - lo_server_add_method(_server, "/connect", "iss", connect_cb, this); - lo_server_add_method(_server, "/disconnect", "iss", disconnect_cb, this); - lo_server_add_method(_server, "/disconnect_all", "iss", disconnect_all_cb, this); - lo_server_add_method(_server, "/note_on", "isii", note_on_cb, this); - lo_server_add_method(_server, "/note_off", "isi", note_off_cb, this); - lo_server_add_method(_server, "/all_notes_off", "isi", all_notes_off_cb, this); - lo_server_add_method(_server, "/learn", "is", learn_cb, this); - lo_server_add_method(_server, "/set_property", NULL, set_property_cb, this); - - // Queries - lo_server_add_method(_server, "/request_property", "iss", request_property_cb, this); - lo_server_add_method(_server, "/get", "is", get_cb, this); - - lo_server_add_method(_server, NULL, NULL, unknown_cb, NULL); - - Thread::set_name("OSCEngineReceiver"); - start(); - _receive_thread->set_name("OSCEngineReceiver Listener"); - _receive_thread->start(); - _receive_thread->set_scheduling(SCHED_FIFO, 5); -} - -OSCEngineReceiver::~OSCEngineReceiver() -{ - _receive_thread->stop(); - stop(); - delete _receive_thread; - - if (_server != NULL) { -#ifdef ENABLE_AVAHI - lo_server_avahi_free(_server); -#endif - lo_server_free(_server); - _server = NULL; - } -} - -/** 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) { - // 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 request for this message, if necessary. - * - * This is based on the fact that the current request is stored in a ref - * counted pointer, and events just take a reference to that. Thus, events - * may delete their request 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 requests 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->_request; - - /* 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->_request = SharedPtr(new Request(me, client, id)); - else - me->_request = SharedPtr(new Request(me)); - } - - if (id != -1) { - me->set_next_response_id(id); - } else { - me->disable_responses(); - } - - free(url); - - // If this returns 0 no OSC commands will work - return 1; -} - -#ifdef LIBLO_BUNDLES -int -OSCEngineReceiver::_bundle_start_cb(lo_timetag time) -{ - info << "BUNDLE START" << endl; - return 0; -} - -int -OSCEngineReceiver::_bundle_end_cb() -{ - info << "BUNDLE END" << endl; - return 0; -} -#endif - -void -OSCEngineReceiver::error_cb(int num, const char* msg, const char* path) -{ - error << "liblo server error" << num; - if (path) { - error << " for path `" << path << "'"; - } - error << " (" << msg << ")" << endl; -} - -/** @page engine_osc_namespace - *

/ping

- * @arg @p response-id :: Integer - */ -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, "/ok", "i", argv[0]->i) < 0) - warn << "Unable to send response (" << lo_address_errstr(addr) << ")" << endl; - return 0; -} - -/** @page engine_osc_namespace - *

/ping_queued

- * @arg @p response-id :: Integer - * - * @par - * Reply to sender with a successful response after going through the - * event queue. This is useful for checking if the engine is actually active, - * or for sending after several events as a sentinel and wait on it's response, - * to know when all previous events have finished processing. - */ -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 - *

/register_client

- * @arg @p response-id :: Integer - * - * @par - * Register a new client with the engine. The incoming address will be - * used for the new registered client. - */ -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, - _engine.world()->conf()->option("packet-size").get_int32()); - register_client(client); - free(url); - - return 0; -} - -/** @page engine_osc_namespace - *

/unregister_client

- * @arg @p response-id :: Integer - * - * @par - * Unregister a client. - */ -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 - *

/get

- * @arg @p response-id :: Integer - * @arg @p uri :: URI String - * - * @par - * Request all properties of an object. - */ -int -OSCEngineReceiver::_get_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - get(&argv[1]->s); - return 0; -} - -/** @page engine_osc_namespace - *

/put

- * @arg @p response-id :: Integer - * @arg @p path :: String - * @arg @p predicate :: URI String - * @arg @p value - * @arg @p ... - * - * @par - * PUT a set of properties to a path. - */ -int -OSCEngineReceiver::_put_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* obj_path = &argv[1]->s; - Resource::Properties prop; - for (int i = 2; i < argc-1; i += 2) - prop.insert(make_pair(&argv[i]->s, AtomLiblo::lo_arg_to_atom(types[i+1], argv[i+1]))); - put(obj_path, prop); - return 0; -} - -/** @page engine_osc_namespace - *

/move

- * @arg @p response-id :: Integer - * @arg @p old-path :: String - * @arg @p new-path :: String - * - * @par - * MOVE an object to a new path. - */ -int -OSCEngineReceiver::_move_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* old_path = &argv[1]->s; - const char* new_path = &argv[2]->s; - - move(old_path, new_path); - return 0; -} - -/** @page engine_osc_namespace - *

/delete

- * @arg @p response-id :: Integer - * @arg @p path :: String - * - * @par - * DELETE an object. - */ -int -OSCEngineReceiver::_del_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) -{ - const char* uri = &argv[1]->s; - - del(uri); - return 0; -} - -/** @page engine_osc_namespace - *

/connect

- * @arg @p response-id :: Integer - * @arg @p src-port-path :: String - * @arg @p dst-port-path :: String - * - * @par - * Connect two ports (which must be in the same patch). - */ -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 - *

/disconnect

- * @arg @p response-id :: Integer - * @arg @p src-port-path :: String - * @arg @p dst-port-path :: String - * - * @par - * Disconnect two ports. - */ -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 - *

/disconnect_all

- * @arg @p response-id :: Integer - * @arg @p patch-path :: String - * @arg @p object-path :: String - * - * @par - * Disconnect all connections to/from a node/port. - */ -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 - *

/note_on

- * @arg @p response-id :: Integer - * @arg @p node-path :: String - * @arg @p note-num (int) - * @arg @p velocity (int) - * - * @par - * Trigger a note-on, just as if it came from MIDI. - */ -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 uint8_t note_num = argv[2]->i; - const uint8_t velocity = argv[3]->i; - */ - warn << "TODO: OSC note on" << endl; - //note_on(node_path, note_num, velocity); - return 0; -} - -/** @page engine_osc_namespace - *

/note_off

- * @arg @p response-id :: Integer - * @arg @p node-path :: String - * @arg @p note-num :: Integer - * - * @par - * Trigger a note-off, just as if it came from MIDI. - */ -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 uint8_t note_num = argv[2]->i; - */ - warn << "TODO: OSC note off" << endl; - //note_off(patch_path, note_num); - return 0; -} - -/** @page engine_osc_namespace - *

/all_notes_off

- * @arg @p response-id :: Integer - * @arg @p patch-path :: String - * - * @par - * Trigger a note-off for all voices, just as if it came from MIDI. - */ -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; - */ - warn << "TODO: OSC all notes off" << endl; - //all_notes_off(patch_path); - return 0; -} - -/** @page engine_osc_namespace - *

/set_property

- * @arg @p response-id :: Integer - * @arg @p uri :: URI String - * @arg @p key :: URI String - * @arg @p value :: String - * - * @par - * Set a property on a graph object. - */ -int -OSCEngineReceiver::_set_property_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 - *

/request_property

- * @arg @p response-id :: Integer - * @arg @p uri :: URI String - * @arg @p key :: URI String - * - * Request the value of a property on an object. - */ -int -OSCEngineReceiver::_request_property_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_property(object_path, key); - 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("[OSCEngineReceiver] %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); - - warn << "Unknown OSC command " << path << " (" << types << ") " - << "received from " << url << endl; - - string error_msg = "Unknown command: "; - error_msg.append(path).append(" ").append(types); - - lo_send(addr, "/error", "s", error_msg.c_str(), LO_ARGS_END); - free(url); - - return 0; -} - -} // namespace Engine -} // namespace Ingen diff --git a/src/engine/OSCEngineReceiver.hpp b/src/engine/OSCEngineReceiver.hpp deleted file mode 100644 index 4293f890..00000000 --- a/src/engine/OSCEngineReceiver.hpp +++ /dev/null @@ -1,118 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_OSCENGINERECEIVER_HPP -#define INGEN_ENGINE_OSCENGINERECEIVER_HPP - -#include -#include -#include "QueuedEngineInterface.hpp" -#include "Request.hpp" -#include "ingen-config.h" - -namespace Ingen { -namespace Engine { - -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(); - -private: - struct ReceiveThread : public Raul::Thread { - explicit ReceiveThread(OSCEngineReceiver& receiver) : _receiver(receiver) {} - virtual void _run(); - private: - OSCEngineReceiver& _receiver; - }; - - friend class ReceiveThread; - - ReceiveThread* _receive_thread; - -#ifdef LIBLO_BUNDLES - static int bundle_start_cb(lo_timetag time, void* myself) { - return ((OSCEngineReceiver*)myself)->_bundle_start_cb(time); - } - static int bundle_end_cb(void* myself) { - return ((OSCEngineReceiver*)myself)->_bundle_end_cb(); - } - - int _bundle_start_cb(lo_timetag time); - int _bundle_end_cb(); -#endif - - 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(get); - LO_HANDLER(put); - LO_HANDLER(move); - LO_HANDLER(del); - LO_HANDLER(connect); - LO_HANDLER(disconnect); - LO_HANDLER(disconnect_all); - LO_HANDLER(note_on); - LO_HANDLER(note_off); - LO_HANDLER(all_notes_off); - LO_HANDLER(learn); - LO_HANDLER(set_property); - LO_HANDLER(property_set); - LO_HANDLER(request_property); - - lo_server _server; -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_OSCENGINERECEIVER_HPP diff --git a/src/engine/ObjectBuffer.cpp b/src/engine/ObjectBuffer.cpp deleted file mode 100644 index ba0b0c7f..00000000 --- a/src/engine/ObjectBuffer.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2009-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "raul/log.hpp" -#include "lv2/lv2plug.in/ns/ext/uri-map/uri-map.h" -#include "ingen-config.h" -#include "shared/LV2Features.hpp" -#include "shared/LV2URIMap.hpp" -#include "ObjectBuffer.hpp" -#include "Engine.hpp" - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { - -/** Allocate a new object buffer. - * \a capacity is in bytes, including LV2_Atom header - */ -ObjectBuffer::ObjectBuffer(BufferFactory& bufs, size_t capacity) - : Buffer(bufs, PortType(PortType::VALUE), capacity) -{ - capacity += sizeof(LV2_Atom); - -#ifdef HAVE_POSIX_MEMALIGN - const int ret = posix_memalign((void**)&_buf, 16, capacity); -#else - _buf = (LV2_Atom*)malloc(capacity); - const int ret = (_buf != NULL) ? 0 : -1; -#endif - - if (ret != 0) { - error << "Failed to allocate object buffer. Aborting." << endl; - exit(EXIT_FAILURE); - } - - clear(); -} - -ObjectBuffer::~ObjectBuffer() -{ - free(_buf); -} - -void -ObjectBuffer::clear() -{ - // null - _buf->type = 0; - _buf->size = 0; -} - -void -ObjectBuffer::copy(Context& context, const Buffer* src_buf) -{ - const ObjectBuffer* src = dynamic_cast(src_buf); - if (!src || src == this || src->_buf == _buf) - return; - - // Copy only if src is a POD object that fits - if (src->_buf->type != 0 && src_buf->size() <= size()) - memcpy(_buf, src->_buf, sizeof(LV2_Atom) + src_buf->size()); -} - -void -ObjectBuffer::resize(size_t size) -{ - const uint32_t contents_size = sizeof(LV2_Atom) + _buf->size; - - _buf = (LV2_Atom*)realloc(_buf, sizeof(LV2_Atom) + size); - _size = size + sizeof(LV2_Atom); - - // If we shrunk and chopped the current contents, clear corrupt data - if (size < contents_size) - clear(); -} - -void* -ObjectBuffer::port_data(PortType port_type, SampleCount offset) -{ - switch (port_type.symbol()) { - case PortType::CONTROL: - case PortType::AUDIO: - switch (_type.symbol()) { - case PortType::CONTROL: - return (float*)atom()->body; - case PortType::AUDIO: - return (float*)((LV2_Atom_Vector*)atom()->body)->elems + offset; - default: - warn << "Audio data requested from non-audio buffer" << endl; - return NULL; - } - break; - default: - return _buf; - } -} - -const void* -ObjectBuffer::port_data(PortType port_type, SampleCount offset) const -{ - switch (port_type.symbol()) { - case PortType::CONTROL: - case PortType::AUDIO: - switch (_type.symbol()) { - case PortType::CONTROL: - return (float*)atom()->body; - case PortType::AUDIO: - return (float*)((LV2_Atom_Vector*)atom()->body)->elems + offset; - default: - warn << "Audio data requested from non-audio buffer" << endl; - return NULL; - } - break; - default: - return _buf; - } -} - -void -ObjectBuffer::prepare_write(Context& context) -{ - _buf->size = _size - sizeof(LV2_Atom); -} - -} // namespace Engine -} // namespace Ingen - diff --git a/src/engine/ObjectBuffer.hpp b/src/engine/ObjectBuffer.hpp deleted file mode 100644 index fe75bd71..00000000 --- a/src/engine/ObjectBuffer.hpp +++ /dev/null @@ -1,56 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2009-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_OBJECTBUFFER_HPP -#define INGEN_ENGINE_OBJECTBUFFER_HPP - -#include "lv2/lv2plug.in/ns/ext/atom/atom.h" -#include "ingen/PortType.hpp" -#include "Buffer.hpp" - -namespace Ingen { -namespace Engine { - -class Context; - -class ObjectBuffer : public Buffer { -public: - ObjectBuffer(BufferFactory& bufs, size_t capacity); - ~ObjectBuffer(); - - void clear(); - - void* port_data(PortType port_type, SampleCount offset); - const void* port_data(PortType port_type, SampleCount offset) const; - - void prepare_write(Context& context); - - void copy(Context& context, const Buffer* src); - - void resize(size_t size); - - LV2_Atom* atom() { return _buf; } - const LV2_Atom* atom() const { return _buf; } - -private: - LV2_Atom* _buf; ///< Contents -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_OBJECTBUFFER_HPP diff --git a/src/engine/ObjectSender.cpp b/src/engine/ObjectSender.cpp deleted file mode 100644 index 77b6c5e3..00000000 --- a/src/engine/ObjectSender.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "ingen/ClientInterface.hpp" -#include "shared/LV2URIMap.hpp" -#include "EngineStore.hpp" -#include "PatchImpl.hpp" -#include "NodeImpl.hpp" -#include "PortImpl.hpp" -#include "ConnectionImpl.hpp" -#include "NodeFactory.hpp" -#include "ingen/PortType.hpp" -#include "AudioBuffer.hpp" - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { - -void -ObjectSender::send_object(ClientInterface* client, - const GraphObjectImpl* object, - bool recursive) -{ - const PatchImpl* patch = dynamic_cast(object); - if (patch) { - send_patch(client, patch, recursive); - return; - } - - const NodeImpl* node = dynamic_cast(object); - if (node) { - send_node(client, node, recursive); - return; - } - - const PortImpl* port = dynamic_cast(object); - if (port) { - send_port(client, port); - return; - } -} - -void -ObjectSender::send_patch(ClientInterface* client, const PatchImpl* patch, bool recursive, bool bundle) -{ - if (bundle) - client->bundle_begin(); - - client->put(patch->path(), - patch->properties(Resource::INTERNAL), - Resource::INTERNAL); - - client->put(patch->path(), - patch->properties(Resource::EXTERNAL), - Resource::EXTERNAL); - - 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, false); - } - - // Send ports - for (uint32_t i=0; i < patch->num_ports(); ++i) { - PortImpl* const port = patch->port_impl(i); - send_port(client, port, false); - } - - // Send connections - for (PatchImpl::Connections::const_iterator j = patch->connections().begin(); - j != patch->connections().end(); ++j) - client->connect(j->second->src_port_path(), j->second->dst_port_path()); - } - - if (bundle) - client->bundle_end(); -} - -/** Sends a node or a patch */ -void -ObjectSender::send_node(ClientInterface* client, const NodeImpl* node, bool recursive, bool bundle) -{ - PluginImpl* const plugin = node->plugin_impl(); - - if (plugin->type() == Plugin::Patch) { - send_patch(client, (PatchImpl*)node, recursive); - return; - } - - if (plugin->uri().length() == 0) { - error << "Node " << node->path() << "'s plugin has no URI! Not sending." << endl; - return; - } - - if (bundle) - client->bundle_begin(); - - client->put(node->path(), node->properties()); - - if (recursive) { - // Send ports - for (size_t j=0; j < node->num_ports(); ++j) - send_port(client, node->port_impl(j), false); - } - - if (bundle) - client->bundle_end(); -} - -void -ObjectSender::send_port(ClientInterface* client, const PortImpl* port, bool bundle) -{ - assert(port); - - if (bundle) - client->bundle_begin(); - - client->put(port->path(), port->properties()); - - // Send control value - if (port->is_a(PortType::CONTROL)) - client->set_property(port->path(), port->bufs().uris().ingen_value, port->value()); - - if (bundle) - client->bundle_end(); -} - -} // namespace Engine -} // namespace Ingen - diff --git a/src/engine/ObjectSender.hpp b/src/engine/ObjectSender.hpp deleted file mode 100644 index 21f0e928..00000000 --- a/src/engine/ObjectSender.hpp +++ /dev/null @@ -1,66 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_OBJECTSENDER_HPP -#define INGEN_ENGINE_OBJECTSENDER_HPP - -namespace Ingen { - -class ClientInterface; - -namespace Engine { - -class GraphObjectImpl; -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: - static void send_object(ClientInterface* client, - const GraphObjectImpl* object, - bool recursive); - -private: - static void send_patch(ClientInterface* client, - const PatchImpl* patch, - bool recursive, - bool bundle=true); - static void send_node(ClientInterface* client, - const NodeImpl* node, - bool recursive, - bool bundle=true); - static void send_port(ClientInterface* client, - const PortImpl* port, - bool bundle=true); -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_OBJECTSENDER_HPP - diff --git a/src/engine/OutputPort.cpp b/src/engine/OutputPort.cpp deleted file mode 100644 index 0a16d2db..00000000 --- a/src/engine/OutputPort.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "ingen/Patch.hpp" -#include "shared/LV2URIMap.hpp" -#include "Buffer.hpp" -#include "NodeImpl.hpp" -#include "OutputPort.hpp" -#include "ProcessContext.hpp" - -using namespace std; - -namespace Ingen { -namespace Engine { - -OutputPort::OutputPort(BufferFactory& bufs, - NodeImpl* parent, - const Raul::Symbol& symbol, - uint32_t index, - uint32_t poly, - PortType type, - const Raul::Atom& value, - size_t buffer_size) - : PortImpl(bufs, parent, symbol, index, poly, type, value, buffer_size) -{ - if (!dynamic_cast(parent)) - add_property(bufs.uris().rdf_type, bufs.uris().lv2_OutputPort); - - if (type == PortType::CONTROL) - _broadcast = true; - - setup_buffers(bufs, poly); -} - -bool -OutputPort::get_buffers(BufferFactory& bufs, Raul::Array* buffers, uint32_t poly) -{ - for (uint32_t v = 0; v < poly; ++v) - buffers->at(v) = bufs.get(buffer_type(), _buffer_size); - - return true; -} - -void -OutputPort::pre_process(Context& context) -{ - for (uint32_t v = 0; v < _poly; ++v) - _buffers->at(v)->prepare_write(context); -} - -void -OutputPort::post_process(Context& context) -{ - for (uint32_t v = 0; v < _poly; ++v) - _buffers->at(v)->prepare_read(context); - - if (_broadcast) - broadcast_value(context, false); -} - -} // namespace Engine -} // namespace Ingen diff --git a/src/engine/OutputPort.hpp b/src/engine/OutputPort.hpp deleted file mode 100644 index c82de111..00000000 --- a/src/engine/OutputPort.hpp +++ /dev/null @@ -1,65 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_OUTPUTPORT_HPP -#define INGEN_ENGINE_OUTPUTPORT_HPP - -#include -#include -#include "PortImpl.hpp" - -namespace Ingen { -namespace Engine { - -/** 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(BufferFactory& bufs, - NodeImpl* parent, - const Raul::Symbol& symbol, - uint32_t index, - uint32_t poly, - PortType type, - const Raul::Atom& value, - size_t buffer_size=0); - - bool get_buffers(BufferFactory& bufs, Raul::Array* buffers, uint32_t poly); - - void pre_process(Context& context); - void post_process(Context& context); - - virtual ~OutputPort() {} - - bool is_input() const { return false; } - bool is_output() const { return true; } -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_OUTPUTPORT_HPP diff --git a/src/engine/PatchImpl.cpp b/src/engine/PatchImpl.cpp deleted file mode 100644 index 86693686..00000000 --- a/src/engine/PatchImpl.cpp +++ /dev/null @@ -1,470 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "raul/log.hpp" -#include "shared/World.hpp" -#include "shared/LV2URIMap.hpp" -#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" -#include "Driver.hpp" -#include "ingen-config.h" - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { - -PatchImpl::PatchImpl(Engine& engine, - const Raul::Symbol& symbol, - uint32_t poly, - PatchImpl* parent, - SampleRate srate, - uint32_t internal_poly) - : NodeImpl(new PatchPlugin(*engine.world()->uris().get(), - engine.world()->uris()->ingen_Patch.c_str(), "patch", "Ingen Patch"), - symbol, poly, parent, srate) - , _engine(engine) - , _internal_poly(internal_poly) - , _compiled_patch(NULL) - , _process(false) -{ - assert(internal_poly >= 1); -} - -PatchImpl::~PatchImpl() -{ - assert(!_activated); - - delete _compiled_patch; - delete _plugin; -} - -void -PatchImpl::activate(BufferFactory& bufs) -{ - NodeImpl::activate(bufs); - - for (List::iterator i = _nodes.begin(); i != _nodes.end(); ++i) - (*i)->activate(bufs); - - assert(_activated); -} - -void -PatchImpl::deactivate() -{ - if (_activated) { - NodeImpl::deactivate(); - - for (List::iterator i = _nodes.begin(); i != _nodes.end(); ++i) { - if ((*i)->activated()) - (*i)->deactivate(); - assert(!(*i)->activated()); - } - } - assert(!_activated); -} - -void -PatchImpl::disable() -{ - ThreadManager::assert_thread(THREAD_PROCESS); - - _process = false; - - for (List::iterator i = _output_ports.begin(); i != _output_ports.end(); ++i) - if ((*i)->context() == Context::AUDIO) - (*i)->clear_buffers(); -} - -bool -PatchImpl::prepare_internal_poly(BufferFactory& bufs, uint32_t poly) -{ - ThreadManager::assert_thread(THREAD_PRE_PROCESS); - - // TODO: Subpatch dynamic polyphony (i.e. changing port polyphony) - - for (List::iterator i = _nodes.begin(); i != _nodes.end(); ++i) - (*i)->prepare_poly(bufs, poly); - - for (List::iterator i = _nodes.begin(); i != _nodes.end(); ++i) - for (uint32_t j = 0; j < (*i)->num_ports(); ++j) - (*i)->port_impl(j)->prepare_poly_buffers(bufs); - - return true; -} - -bool -PatchImpl::apply_internal_poly(ProcessContext& context, BufferFactory& bufs, Raul::Maid& maid, uint32_t poly) -{ - ThreadManager::assert_thread(THREAD_PROCESS); - - // TODO: Subpatch dynamic polyphony (i.e. changing port polyphony) - - for (List::iterator i = _nodes.begin(); i != _nodes.end(); ++i) - (*i)->apply_poly(maid, poly); - - for (List::iterator i = _nodes.begin(); i != _nodes.end(); ++i) { - for (uint32_t j = 0; j < (*i)->num_ports(); ++j) { - PortImpl* const port = (*i)->port_impl(j); - if (port->is_input() && dynamic_cast(port)->direct_connect()) - port->setup_buffers(bufs, port->poly()); - port->connect_buffers(context.offset()); - } - } - - const bool polyphonic = parent_patch() && (poly == parent_patch()->internal_poly()); - for (List::iterator i = _output_ports.begin(); i != _output_ports.end(); ++i) - (*i)->setup_buffers(bufs, polyphonic ? poly : 1); - - _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; - - NodeImpl::pre_process(context); - - // Run all nodes - if (_compiled_patch && _compiled_patch->size() > 0) { - if (context.slaves().size() > 0) { - process_parallel(context); - } else { - process_single(context); - } - } - - // Queue any cross-context connections - for (CompiledPatch::QueuedConnections::iterator i = _compiled_patch->queued_connections.begin(); - i != _compiled_patch->queued_connections.end(); ++i) { - (*i)->queue(context); - } - - NodeImpl::post_process(context); -} - -void -PatchImpl::process_parallel(ProcessContext& context) -{ - size_t n_slaves = context.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) - context.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 (uint32_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 (uint32_t i = 0; i < n_slaves; ++i) - context.slaves()[i]->finish(); -} - -void -PatchImpl::process_single(ProcessContext& context) -{ - for (size_t i = 0; i < _compiled_patch->size(); ++i) - (*_compiled_patch)[i].node()->process(context); -} - -void -PatchImpl::set_buffer_size(Context& context, BufferFactory& bufs, PortType type, size_t size) -{ - NodeImpl::set_buffer_size(context, bufs, type, size); - - for (size_t i = 0; i < _compiled_patch->size(); ++i) - (*_compiled_patch)[i].node()->set_buffer_size(context, bufs, type, size); -} - -// Patch specific stuff - -/** Add a node. - * Preprocessing thread only. - */ -void -PatchImpl::add_node(List::Node* ln) -{ - ThreadManager::assert_thread(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 Raul::Symbol& symbol) -{ - ThreadManager::assert_thread(THREAD_PRE_PROCESS); - for (List::iterator i = _nodes.begin(); i != _nodes.end(); ++i) - if ((*i)->symbol() == symbol) - return _nodes.erase(i); - - return NULL; -} - -void -PatchImpl::add_connection(SharedPtr c) -{ - ThreadManager::assert_thread(THREAD_PRE_PROCESS); - _connections.insert(make_pair(make_pair(c->src_port(), c->dst_port()), c)); -} - -/** Remove a connection. - * Preprocessing thread only. - */ -SharedPtr -PatchImpl::remove_connection(const PortImpl* src_port, const PortImpl* dst_port) -{ - ThreadManager::assert_thread(THREAD_PRE_PROCESS); - Connections::iterator i = _connections.find(make_pair(src_port, dst_port)); - if (i != _connections.end()) { - SharedPtr c = PtrCast(i->second); - _connections.erase(i); - return c; - } else { - error << "[PatchImpl::remove_connection] Connection not found" << endl; - return SharedPtr(); - } -} - -bool -PatchImpl::has_connection(const PortImpl* src_port, const PortImpl* dst_port) const -{ - ThreadManager::assert_thread(THREAD_PRE_PROCESS); - Connections::const_iterator i = _connections.find(make_pair(src_port, dst_port)); - return (i != _connections.end()); -} - -uint32_t -PatchImpl::num_ports() const -{ - if (ThreadManager::thread_is(THREAD_PROCESS)) - return NodeImpl::num_ports(); - else - return _input_ports.size() + _output_ports.size(); -} - -/** Create a port. Not realtime safe. - */ -PortImpl* -PatchImpl::create_port(BufferFactory& bufs, const string& name, PortType type, size_t buffer_size, bool is_output, bool polyphonic) -{ - if (type == PortType::UNKNOWN) { - error << "[PatchImpl::create_port] Unknown port type " << type.uri() << endl; - return NULL; - } - - assert( !(type == PortType::UNKNOWN) ); - - return new DuplexPort(bufs, this, name, num_ports(), polyphonic, _polyphony, - type, Raul::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) -{ - ThreadManager::assert_thread(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; - break; - } - } - - 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; - break; - } - } - - if ( ! found) - error << "[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() -{ - ThreadManager::assert_thread(THREAD_PRE_PROCESS); - - _input_ports.clear(); - _output_ports.clear(); -} - -Raul::Array* -PatchImpl::build_ports_array() const -{ - ThreadManager::assert_thread(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 -{ - ThreadManager::assert_thread(THREAD_PRE_PROCESS); - - 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); - } - - // Add any queued connections that must be run after a cycle - for (Connections::const_iterator i = _connections.begin(); i != _connections.end(); ++i) { - SharedPtr c = PtrCast(i->second); - if (c->src_port()->context() == Context::AUDIO && - c->dst_port()->context() == Context::MESSAGE) { - compiled_patch->queued_connections.push_back(c.get()); - } - } - - assert(compiled_patch->size() == _nodes.size()); - - return compiled_patch; -} - -} // namespace Engine -} // namespace Ingen diff --git a/src/engine/PatchImpl.hpp b/src/engine/PatchImpl.hpp deleted file mode 100644 index 8429728f..00000000 --- a/src/engine/PatchImpl.hpp +++ /dev/null @@ -1,165 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_PATCHIMPL_HPP -#define INGEN_ENGINE_PATCHIMPL_HPP - -#include -#include -#include "raul/List.hpp" -#include "ingen/PortType.hpp" -#include "ingen/Patch.hpp" -#include "NodeImpl.hpp" -#include "PluginImpl.hpp" -#include "CompiledPatch.hpp" - -namespace Ingen { - -class Connection; - -namespace Engine { - -class CompiledPatch; -class ConnectionImpl; -class Context; -class Engine; -class ProcessContext; - -/** 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 NodeImpl, public Patch -{ -public: - PatchImpl(Engine& engine, - const Raul::Symbol& symbol, - uint32_t poly, - PatchImpl* parent, - SampleRate srate, - uint32_t local_poly); - - virtual ~PatchImpl(); - - void activate(BufferFactory& bufs); - void deactivate(); - - void process(ProcessContext& context); - - void set_buffer_size(Context& context, BufferFactory& bufs, PortType type, 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(BufferFactory& bufs, uint32_t poly); - - /** Apply a new (internal) polyphony value. - * - * Audio thread. - * - * \param context Process context - * \param bufs New set of buffers - * \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(ProcessContext& context, BufferFactory& bufs, Raul::Maid& maid, uint32_t poly); - - // Patch specific stuff not inherited from Node - - typedef Raul::List Nodes; - - void add_node(Nodes::Node* tn); - Nodes::Node* remove_node(const Raul::Symbol& symbol); - - 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(BufferFactory& bufs, const std::string& name, PortType type, size_t buffer_size, bool is_output, bool polyphonic); - void add_input(Raul::List::Node* port) { _input_ports.push_back(port); } ///< Preprocesser thread - void add_output(Raul::List::Node* port) { _output_ports.push_back(port); } ///< Preprocessor thread - Raul::List::Node* remove_port(const std::string& name); - void clear_ports(); - - void add_connection(SharedPtr c); - - SharedPtr 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_poly() 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 - Raul::List _input_ports; ///< Accessed in preprocessing thread only - Raul::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 (Raul::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 Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_PATCHIMPL_HPP diff --git a/src/engine/PatchPlugin.hpp b/src/engine/PatchPlugin.hpp deleted file mode 100644 index ba244ef6..00000000 --- a/src/engine/PatchPlugin.hpp +++ /dev/null @@ -1,67 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_PATCHPLUGIN_HPP -#define INGEN_ENGINE_PATCHPLUGIN_HPP - -#include "ingen-config.h" - -#include -#include "PluginImpl.hpp" - -namespace Ingen { -namespace Engine { - -class NodeImpl; - -/** Implementation of a Patch plugin. - * - * Patches don't actually work like this yet... - */ -class PatchPlugin : public PluginImpl -{ -public: - PatchPlugin( - Shared::LV2URIMap& uris, - const std::string& uri, - const std::string& symbol, - const std::string& name) - : PluginImpl(uris, Plugin::Patch, uri) - {} - - NodeImpl* instantiate(BufferFactory& bufs, - const std::string& name, - bool polyphonic, - PatchImpl* parent, - Engine& engine) - { - return NULL; - } - - const std::string symbol() const { return "patch"; } - const std::string name() const { return "Ingen Patch"; } - -private: - const std::string _symbol; - const std::string _name; -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_PATCHPLUGIN_HPP - diff --git a/src/engine/PluginImpl.cpp b/src/engine/PluginImpl.cpp deleted file mode 100644 index e121bd62..00000000 --- a/src/engine/PluginImpl.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "raul/log.hpp" -#include "PluginImpl.hpp" - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { - -void -PluginImpl::load() -{ - if (!_module) { - debug << "Loading plugin library " << _library_path << endl; - _module = new Glib::Module(_library_path, Glib::MODULE_BIND_LOCAL); - if (!(*_module)) - delete _module; - } -} - -void -PluginImpl::unload() -{ - if (_module) { - debug << "Unloading plugin library " << _library_path << endl; - delete _module; - _module = NULL; - } -} - -} // namespace Engine -} // namespace Ingen - diff --git a/src/engine/PluginImpl.hpp b/src/engine/PluginImpl.hpp deleted file mode 100644 index ab4e4a89..00000000 --- a/src/engine/PluginImpl.hpp +++ /dev/null @@ -1,88 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_PLUGINIMPL_HPP -#define INGEN_ENGINE_PLUGINIMPL_HPP - -#include -#include - -#include -#include - -#include "ingen/Plugin.hpp" -#include "shared/ResourceImpl.hpp" - -namespace Ingen { - -namespace Shared { class LV2URIMap; } - -namespace Engine { - -class PatchImpl; -class NodeImpl; -class Engine; -class BufferFactory; - -/** Implementation of a plugin (internal code, or a loaded shared library). - * - * Conceptually, a Node is an instance of this. - */ -class PluginImpl : public Plugin - , public Ingen::Shared::ResourceImpl - , public boost::noncopyable -{ -public: - PluginImpl(Ingen::Shared::LV2URIMap& uris, - Type type, - const std::string& uri, - const std::string library_path = "") - : ResourceImpl(uris, uri) - , _type(type) - , _library_path(library_path) - , _module(NULL) - {} - - virtual NodeImpl* instantiate(BufferFactory& bufs, - const std::string& name, - bool polyphonic, - PatchImpl* parent, - Engine& engine) = 0; - - virtual const std::string symbol() const = 0; - - virtual const std::string& library_path() const { return _library_path; } - - void load(); - void unload(); - - Plugin::Type type() const { return _type; } - void type(Plugin::Type t) { _type = t; } - Glib::Module* module() const { return _module; } - void module(Glib::Module* module) { _module = module; } - -protected: - Plugin::Type _type; - mutable std::string _library_path; - Glib::Module* _module; -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_PLUGINIMPL_HPP - diff --git a/src/engine/PortImpl.cpp b/src/engine/PortImpl.cpp deleted file mode 100644 index 62cc7577..00000000 --- a/src/engine/PortImpl.cpp +++ /dev/null @@ -1,251 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "raul/Array.hpp" -#include "raul/Maid.hpp" -#include "shared/LV2URIMap.hpp" -#include "lv2/lv2plug.in/ns/ext/contexts/contexts.h" -#include "ingen/PortType.hpp" -#include "events/SendPortValue.hpp" -#include "events/SendPortActivity.hpp" -#include "AudioBuffer.hpp" -#include "BufferFactory.hpp" -#include "Engine.hpp" -#include "EventBuffer.hpp" -#include "LV2Atom.hpp" -#include "NodeImpl.hpp" -#include "ObjectBuffer.hpp" -#include "PortImpl.hpp" -#include "ThreadManager.hpp" - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { - -PortImpl::PortImpl(BufferFactory& bufs, - NodeImpl* const node, - const Raul::Symbol& name, - uint32_t index, - uint32_t poly, - PortType type, - const Atom& value, - size_t buffer_size) - : GraphObjectImpl(bufs.uris(), node, name) - , _bufs(bufs) - , _index(index) - , _poly(poly) - , _buffer_size(buffer_size) - , _buffer_type(type) - , _value(value) - , _broadcast(false) - , _set_by_user(false) - , _last_broadcasted_value(value) - , _context(Context::AUDIO) - , _buffers(new Array(static_cast(poly))) - , _prepared_buffers(NULL) -{ - _types.insert(type); - assert(node != NULL); - assert(_poly > 0); - - if (_buffer_size == 0) - _buffer_size = bufs.default_buffer_size(type); - - const Ingen::Shared::LV2URIMap& uris = bufs.uris(); - add_property(uris.rdf_type, type.uri()); - set_property(uris.lv2_index, Atom((int32_t)index)); - set_context(_context); - - if (type == PortType::EVENTS) - _broadcast = true; // send activity blips -} - -PortImpl::~PortImpl() -{ - delete _buffers; -} - -bool -PortImpl::supports(const Raul::URI& value_type) const -{ - return has_property(_bufs.uris().atom_supports, value_type); -} - -Raul::Array* -PortImpl::set_buffers(Raul::Array* buffers) -{ - ThreadManager::assert_thread(THREAD_PROCESS); - - Raul::Array* ret = NULL; - if (buffers != _buffers) { - ret = _buffers; - _buffers = buffers; - } - - connect_buffers(); - - return ret; -} - -bool -PortImpl::prepare_poly(BufferFactory& bufs, uint32_t poly) -{ - ThreadManager::assert_thread(THREAD_PRE_PROCESS); - if (buffer_type() != PortType::CONTROL && buffer_type() != PortType::AUDIO) - return false; - - if (_poly == poly) - return true; - - if (_prepared_buffers && _prepared_buffers->size() != poly) { - delete _prepared_buffers; - _prepared_buffers = NULL; - } - - if (!_prepared_buffers) - _prepared_buffers = new Array(poly, *_buffers, NULL); - - return true; -} - -void -PortImpl::prepare_poly_buffers(BufferFactory& bufs) -{ - if (_prepared_buffers) - get_buffers(bufs, _prepared_buffers, _prepared_buffers->size()); -} - -bool -PortImpl::apply_poly(Maid& maid, uint32_t poly) -{ - ThreadManager::assert_thread(THREAD_PROCESS); - if (buffer_type() != PortType::CONTROL && buffer_type() != PortType::AUDIO) - return false; - - if (!_prepared_buffers) - return true; - - assert(poly == _prepared_buffers->size()); - - _poly = poly; - - // Apply a new set of buffers from a preceding call to prepare_poly - maid.push(set_buffers(_prepared_buffers)); - assert(_buffers == _prepared_buffers); - _prepared_buffers = NULL; - - if (is_a(PortType::CONTROL)) - for (uint32_t v = 0; v < _poly; ++v) - if (_buffers->at(v)) - boost::static_pointer_cast(_buffers->at(v))->set_value( - _value.get_float(), 0, 0); - - assert(_buffers->size() >= poly); - assert(this->poly() == poly); - assert(!_prepared_buffers); - - return true; -} - -void -PortImpl::set_buffer_size(Context& context, BufferFactory& bufs, size_t size) -{ - _buffer_size = size; - - for (uint32_t v = 0; v < _poly; ++v) - _buffers->at(v)->resize(size); - - connect_buffers(); -} - -void -PortImpl::connect_buffers(SampleCount offset) -{ - for (uint32_t v = 0; v < _poly; ++v) - PortImpl::parent_node()->set_port_buffer(v, _index, buffer(v), offset); -} - -void -PortImpl::recycle_buffers() -{ - for (uint32_t v = 0; v < _poly; ++v) - _buffers->at(v) = NULL; -} - -void -PortImpl::clear_buffers() -{ - for (uint32_t v = 0; v < _poly; ++v) - buffer(v)->clear(); -} - -void -PortImpl::broadcast_value(Context& context, bool force) -{ - Raul::Atom val; - switch (buffer_type().symbol()) { - case PortType::UNKNOWN: - break; - case PortType::AUDIO: - case PortType::CONTROL: - val = ((AudioBuffer*)buffer(0).get())->value_at(0); - break; - case PortType::EVENTS: - if (((EventBuffer*)buffer(0).get())->event_count() > 0) { - const Events::SendPortActivity ev(context.engine(), context.start(), this); - context.event_sink().write(sizeof(ev), &ev); - } - break; - case PortType::VALUE: - case PortType::MESSAGE: - Ingen::Shared::LV2Atom::to_atom(_bufs.uris(), ((ObjectBuffer*)buffer(0).get())->atom(), val); - break; - } - - if (val.is_valid() && (force || val != _last_broadcasted_value)) { - _last_broadcasted_value = val; - const Events::SendPortValue ev(context.engine(), context.start(), this, true, 0, val); - context.event_sink().write(sizeof(ev), &ev); - } -} - -void -PortImpl::set_context(Context::ID c) -{ - const Ingen::Shared::LV2URIMap& uris = _bufs.uris(); - _context = c; - switch (c) { - case Context::AUDIO: - remove_property(uris.ctx_context, uris.wildcard); - break; - case Context::MESSAGE: - set_property(uris.ctx_context, uris.ctx_MessageContext); - break; - } -} - -PortType -PortImpl::buffer_type() const -{ - // TODO: multiple types - return *_types.begin(); -} - -} // namespace Engine -} // namespace Ingen diff --git a/src/engine/PortImpl.hpp b/src/engine/PortImpl.hpp deleted file mode 100644 index a8f3a63e..00000000 --- a/src/engine/PortImpl.hpp +++ /dev/null @@ -1,174 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_PORTIMPL_HPP -#define INGEN_ENGINE_PORTIMPL_HPP - -#include -#include -#include -#include "raul/Array.hpp" -#include "raul/Atom.hpp" -#include "ingen/Port.hpp" -#include "types.hpp" -#include "GraphObjectImpl.hpp" -#include "ingen/PortType.hpp" -#include "Buffer.hpp" -#include "Context.hpp" - -namespace Raul { class Maid; } - -namespace Ingen { -namespace Engine { - -class NodeImpl; -class Buffer; -class BufferFactory; - -/** 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 Port -{ -public: - ~PortImpl(); - - /** A port's parent is always a node, so static cast should be safe */ - NodeImpl* parent_node() const { return (NodeImpl*)_parent; } - - /** Set the buffers array for this port. - * - * Audio thread. Returned value must be freed by caller. - * \a buffers must be poly() long - */ - Raul::Array* set_buffers(Raul::Array* buffers); - - /** Prepare for a new (external) polyphony value. - * - * Preprocessor thread, poly is actually applied by apply_poly. - */ - virtual bool prepare_poly(BufferFactory& bufs, uint32_t poly); - - virtual void prepare_poly_buffers(BufferFactory& bufs); - - /** Apply a new polyphony value. - * - * Audio thread. - * \a 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 BufferFactory::Ref buffer(uint32_t voice) const { - return _buffers->at((_poly == 1) ? 0 : voice); - } - inline BufferFactory::Ref prepared_buffer(uint32_t voice) const { - return _prepared_buffers->at(voice); - } - - /** Called once per process cycle */ - virtual void pre_process(Context& context) = 0; - virtual void post_process(Context& context) = 0; - - /** Empty buffer contents completely (ie silence) */ - virtual void clear_buffers(); - - virtual bool get_buffers(BufferFactory& bufs, - Raul::Array* buffers, - uint32_t poly) = 0; - - void setup_buffers(BufferFactory& bufs, uint32_t poly) { - get_buffers(bufs, _buffers, poly); - } - - virtual void connect_buffers(SampleCount offset=0); - virtual void recycle_buffers(); - - virtual bool is_input() const = 0; - virtual bool is_output() const = 0; - - uint32_t index() const { return _index; } - - const PortTypes& types() const { return _types; } - - PortType buffer_type() const; - - bool supports(const Raul::URI& value_type) const; - - size_t buffer_size() const { return _buffer_size; } - - uint32_t poly() const { - return _poly; - } - uint32_t prepared_poly() const { - return (_prepared_buffers) ? _prepared_buffers->size() : 1; - } - - void set_buffer_size(Context& context, BufferFactory& bufs, size_t size); - void set_buffer_type(PortType type); - - void broadcast(bool b) { _broadcast = b; } - bool broadcast() { return _broadcast; } - - void broadcast_value(Context& context, bool force=false); - - void raise_set_by_user_flag() { _set_by_user = true; } - - Context::ID context() const { return _context; } - void set_context(Context::ID c); - - BufferFactory& bufs() const { return _bufs; } - -protected: - PortImpl(BufferFactory& bufs, - NodeImpl* node, - const Raul::Symbol& name, - uint32_t index, - uint32_t poly, - PortType type, - const Raul::Atom& value, - size_t buffer_size); - - BufferFactory& _bufs; - uint32_t _index; - uint32_t _poly; - uint32_t _buffer_size; - PortType _buffer_type; - std::set _types; - Raul::Atom _value; - bool _broadcast; - bool _set_by_user; - Raul::Atom _last_broadcasted_value; - - Context::ID _context; - Raul::Array* _buffers; - - // Dynamic polyphony - Raul::Array* _prepared_buffers; -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_PORTIMPL_HPP diff --git a/src/engine/PostProcessor.cpp b/src/engine/PostProcessor.cpp deleted file mode 100644 index 29179b00..00000000 --- a/src/engine/PostProcessor.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "raul/log.hpp" -#include "raul/SRSWQueue.hpp" -#include "events/SendPortValue.hpp" -#include "Event.hpp" -#include "PostProcessor.hpp" -#include "Engine.hpp" -#include "Driver.hpp" -#include "ProcessContext.hpp" - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { - -PostProcessor::PostProcessor(Engine& engine, size_t queue_size) - : _engine(engine) - , _max_time(0) - , _events(queue_size) - , _event_buffer_size(sizeof(Events::SendPortValue)) // FIXME: make generic - , _event_buffer((uint8_t*)malloc(_event_buffer_size)) -{ -} - -PostProcessor::~PostProcessor() -{ - free(_event_buffer); -} - -void -PostProcessor::process() -{ - const FrameTime end_time = _max_time.get(); - - /* FIXME: The order here is a bit tricky: if the normal events are - * processed first, then a deleted port may still have a pending - * broadcast event which will segfault if executed afterwards. - * If it's the other way around, broadcasts will be sent for - * ports the client doesn't even know about yet... */ - - /* FIXME: process events from all threads if parallel */ - - /* Process audio thread generated events */ - while (true) { - Driver* driver = _engine.driver(); - if (driver && driver->context().event_sink().read(_event_buffer_size, _event_buffer)) { - if (((Event*)_event_buffer)->time() > end_time) { - warn << "Lost event with time " - << ((Event*)_event_buffer)->time() << " > " << end_time << endl; - break; - } - ((Event*)_event_buffer)->post_process(); - } else { - break; - } - } - - /* Process normal events */ - Raul::List::Node* n = _events.head(); - while (n) { - if (n->elem()->time() > end_time) - break; - Raul::List::Node* next = n->next(); - n->elem()->post_process(); - _events.erase(_events.begin()); - delete n->elem(); - delete n; - n = next; - } -} - -} // namespace Engine -} // namespace Ingen diff --git a/src/engine/PostProcessor.hpp b/src/engine/PostProcessor.hpp deleted file mode 100644 index f0888d2f..00000000 --- a/src/engine/PostProcessor.hpp +++ /dev/null @@ -1,68 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_POSTPROCESSOR_HPP -#define INGEN_ENGINE_POSTPROCESSOR_HPP - -#include -#include "raul/SRSWQueue.hpp" -#include "raul/List.hpp" - -namespace Ingen { -namespace Engine { - -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: - PostProcessor(Engine& engine, size_t queue_size); - ~PostProcessor(); - - /** Push a list of events on to the process queue, realtime-safe, not thread-safe. */ - inline void append(Raul::List* l) { _events.append(*l); } - - /** 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::List _events; - uint32_t _event_buffer_size; - uint8_t* _event_buffer; -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_POSTPROCESSOR_HPP diff --git a/src/engine/ProcessContext.cpp b/src/engine/ProcessContext.cpp deleted file mode 100644 index 2abe7cf2..00000000 --- a/src/engine/ProcessContext.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "ProcessContext.hpp" -#include "ProcessSlave.hpp" - -namespace Ingen { -namespace Engine { - -void -ProcessContext::activate(uint32_t parallelism, bool sched_rt) -{ - for (uint32_t i = 0; i < _slaves.size(); ++i) { - delete _slaves[i]; - } - _slaves.clear(); - _slaves.reserve(parallelism); - for (uint32_t i = 0; i < parallelism - 1; ++i) { - _slaves.push_back(new ProcessSlave(_engine, sched_rt)); - } -} - -} // namespace Engine -} // namespace Ingen diff --git a/src/engine/ProcessContext.hpp b/src/engine/ProcessContext.hpp deleted file mode 100644 index bbaa5ad2..00000000 --- a/src/engine/ProcessContext.hpp +++ /dev/null @@ -1,54 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_PROCESSCONTEXT_HPP -#define INGEN_ENGINE_PROCESSCONTEXT_HPP - -#include - -#include "Context.hpp" -#include "EventSink.hpp" -#include "types.hpp" - -namespace Ingen { -namespace Engine { - -class ProcessSlave; - -/** Context of a process() call (the audio context). - * \ingroup engine - */ -class ProcessContext : public Context -{ -public: - explicit ProcessContext(Engine& engine) : Context(engine, AUDIO) {} - - typedef std::vector Slaves; - - const Slaves& slaves() const { return _slaves; } - Slaves& slaves() { return _slaves; } - - void activate(uint32_t parallelism, bool sched_rt); - -private: - Slaves _slaves; -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_PROCESSCONTEXT_HPP diff --git a/src/engine/ProcessSlave.cpp b/src/engine/ProcessSlave.cpp deleted file mode 100644 index 44197d4a..00000000 --- a/src/engine/ProcessSlave.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "ProcessSlave.hpp" -#include "NodeImpl.hpp" -#include "CompiledPatch.hpp" - -using namespace std; - -namespace Ingen { -namespace Engine { - -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(*_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 Engine -} // namespace Ingen diff --git a/src/engine/ProcessSlave.hpp b/src/engine/ProcessSlave.hpp deleted file mode 100644 index 39c599af..00000000 --- a/src/engine/ProcessSlave.hpp +++ /dev/null @@ -1,104 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_PROCESSSLAVE_HPP -#define INGEN_ENGINE_PROCESSSLAVE_HPP - -#include - -#include "raul/Array.hpp" -#include "raul/AtomicInt.hpp" -#include "raul/Slave.hpp" - -#include "Driver.hpp" -#include "Engine.hpp" -#include "ProcessContext.hpp" - -namespace Ingen { -namespace Engine { - -class NodeImpl; -class CompiledPatch; - -class ProcessSlave : protected Raul::Slave { -public: - ProcessSlave(Engine& engine, bool realtime) - : _engine(engine) - , _id(_next_id++) - , _index(0) - , _state(STATE_FINISHED) - , _compiled_patch(NULL) - , _context(NULL) - { - 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; - _context = &context; - - 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 _engine.driver()->context(); } - inline ProcessContext& context() { return _engine.driver()->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; - - Engine& _engine; - uint32_t _id; - uint32_t _index; - Raul::AtomicInt _state; - CompiledPatch* _compiled_patch; - ProcessContext* _context; -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_PROCESSSLAVE_HPP diff --git a/src/engine/QueuedEngineInterface.cpp b/src/engine/QueuedEngineInterface.cpp deleted file mode 100644 index 24042b27..00000000 --- a/src/engine/QueuedEngineInterface.cpp +++ /dev/null @@ -1,223 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "raul/log.hpp" - -#include "Driver.hpp" -#include "Engine.hpp" -#include "EventSource.hpp" -#include "QueuedEngineInterface.hpp" -#include "events.hpp" - -#define LOG(s) s << "[QueuedEngineInterface] " - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { - -QueuedEngineInterface::QueuedEngineInterface(Engine& engine, size_t queue_size) - : EventSource(queue_size) - , _request(new Request(this, NULL, 0)) - , _engine(engine) - , _in_bundle(false) -{ - start(); -} - - -QueuedEngineInterface::~QueuedEngineInterface() -{ - stop(); -} - -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 - if (_engine.driver()) - return _engine.driver()->frame_time() + _engine.driver()->block_length(); - else - return 0; -} - -void -QueuedEngineInterface::set_next_response_id(int32_t id) -{ - if (_request) - _request->set_id(id); -} - -void -QueuedEngineInterface::disable_responses() -{ - _request->set_client(NULL); - _request->set_id(0); -} - -/* *** EngineInterface implementation below here *** */ - -void -QueuedEngineInterface::register_client(ClientInterface* client) -{ - push_queued(new Events::RegisterClient(_engine, _request, now(), client->uri(), client)); - if (!_request) { - _request = SharedPtr(new Request(this, client, 1)); - } else { - _request->set_id(1); - _request->set_client(client); - } -} - -void -QueuedEngineInterface::unregister_client(const URI& uri) -{ - push_queued(new Events::UnregisterClient(_engine, _request, now(), uri)); - if (_request && _request->client() && _request->client()->uri() == uri) { - _request->set_id(0); - _request->set_client(NULL); - } -} - -// Bundle commands - -void -QueuedEngineInterface::bundle_begin() -{ - _in_bundle = true; -} - -void -QueuedEngineInterface::bundle_end() -{ - _in_bundle = false; -} - -// Object commands - -void -QueuedEngineInterface::put(const URI& uri, - const Resource::Properties& properties, - const Resource::Graph ctx) -{ - push_queued(new Events::SetMetadata(_engine, _request, now(), true, ctx, uri, properties)); -} - -void -QueuedEngineInterface::delta(const URI& uri, - const Resource::Properties& remove, - const Resource::Properties& add) -{ - push_queued(new Events::SetMetadata(_engine, _request, now(), false, Resource::DEFAULT, uri, add, remove)); -} - -void -QueuedEngineInterface::move(const Path& old_path, - const Path& new_path) -{ - push_queued(new Events::Move(_engine, _request, now(), old_path, new_path)); -} - -void -QueuedEngineInterface::del(const URI& uri) -{ - if (uri == "ingen:engine") { - _request->respond_ok(); - _engine.quit(); - } else { - push_queued(new Events::Delete(_engine, _request, now(), uri)); - } -} - -void -QueuedEngineInterface::connect(const Path& src_port_path, - const Path& dst_port_path) -{ - push_queued(new Events::Connect(_engine, _request, now(), src_port_path, dst_port_path)); - -} - -void -QueuedEngineInterface::disconnect(const URI& src, - const URI& dst) -{ - if (!Path::is_path(src) && !Path::is_path(dst)) { - std::cerr << "Bad disconnect request " << src << " => " << dst << std::endl; - return; - } - - push_queued(new Events::Disconnect(_engine, _request, now(), - Path(src.str()), Path(dst.str()))); -} - -void -QueuedEngineInterface::disconnect_all(const Path& patch_path, - const Path& path) -{ - push_queued(new Events::DisconnectAll(_engine, _request, now(), patch_path, path)); -} - -void -QueuedEngineInterface::set_property(const URI& uri, - const URI& predicate, - const Atom& value) -{ - if (uri == "ingen:engine" && predicate == "ingen:enabled" - && value.type() == Atom::BOOL) { - if (value.get_bool()) { - _engine.activate(); - push_queued(new Events::Ping(_engine, _request, now())); - } else { - push_queued(new Events::Deactivate(_engine, _request, now())); - } - } else { - Resource::Properties remove; - remove.insert(make_pair(predicate, _engine.world()->uris()->wildcard)); - Resource::Properties add; - add.insert(make_pair(predicate, value)); - push_queued(new Events::SetMetadata( - _engine, _request, now(), false, Resource::DEFAULT, - uri, add, remove)); - } -} - -// Requests // - -void -QueuedEngineInterface::ping() -{ - push_queued(new Events::Ping(_engine, _request, now())); -} - -void -QueuedEngineInterface::get(const URI& uri) -{ - push_queued(new Events::Get(_engine, _request, now(), uri)); -} - -void -QueuedEngineInterface::request_property(const URI& uri, const URI& key) -{ - push_queued(new Events::RequestMetadata(_engine, _request, now(), Resource::DEFAULT, uri, key)); -} - -} // namespace Engine -} // namespace Ingen diff --git a/src/engine/QueuedEngineInterface.hpp b/src/engine/QueuedEngineInterface.hpp deleted file mode 100644 index b9f4f7b7..00000000 --- a/src/engine/QueuedEngineInterface.hpp +++ /dev/null @@ -1,116 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_QUEUEDENGINEINTERFACE_HPP -#define INGEN_ENGINE_QUEUEDENGINEINTERFACE_HPP - -#include -#include -#include -#include "raul/SharedPtr.hpp" -#include "ingen/ClientInterface.hpp" -#include "ingen/EngineInterface.hpp" -#include "ingen/Resource.hpp" -#include "EventSource.hpp" -#include "Request.hpp" -#include "types.hpp" - -namespace Ingen { -namespace Engine { - -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 Driver. - * - * Responses occur through the event mechanism (which notified clients in - * event post_process methods) and are related to an event by an integer ID. - * If you do not register a request, you have no way of knowing if your calls - * are successful. - */ -class QueuedEngineInterface : public EventSource, - public EngineInterface -{ -public: - QueuedEngineInterface(Engine& engine, size_t queue_size); - virtual ~QueuedEngineInterface(); - - Raul::URI uri() const { return "http://drobilla.net/ns/ingen#internal"; } - - void set_next_response_id(int32_t id); - - // Client registration - virtual void register_client(ClientInterface* client); - virtual void unregister_client(const Raul::URI& uri); - - // Bundles - virtual void bundle_begin(); - virtual void bundle_end(); - - // CommonInterface object commands - - virtual void put(const Raul::URI& path, - const Resource::Properties& properties, - const Resource::Graph g=Resource::DEFAULT); - - virtual void delta(const Raul::URI& path, - const Resource::Properties& remove, - const Resource::Properties& add); - - virtual void move(const Raul::Path& old_path, - const Raul::Path& new_path); - - virtual void connect(const Raul::Path& src_port_path, - const Raul::Path& dst_port_path); - - virtual void disconnect(const Raul::URI& src, - const Raul::URI& dst); - - virtual void set_property(const Raul::URI& subject_path, - const Raul::URI& predicate, - const Raul::Atom& value); - - virtual void del(const Raul::URI& uri); - - // EngineInterface object commands - - virtual void disconnect_all(const Raul::Path& parent_patch_path, - const Raul::Path& path); - - // Requests - virtual void ping(); - virtual void get(const Raul::URI& uri); - virtual void request_property(const Raul::URI& object_path, - const Raul::URI& key); - -protected: - virtual void disable_responses(); - - SharedPtr _request; ///< NULL if responding disabled - Engine& _engine; - bool _in_bundle; ///< True iff a bundle is currently being received - -private: - SampleCount now() const; -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_QUEUEDENGINEINTERFACE_HPP diff --git a/src/engine/QueuedEvent.cpp b/src/engine/QueuedEvent.cpp deleted file mode 100644 index e08b6b30..00000000 --- a/src/engine/QueuedEvent.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 { -namespace Engine { - -void -QueuedEvent::pre_process() -{ - ThreadManager::assert_thread(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 Engine -} // namespace Ingen - diff --git a/src/engine/QueuedEvent.hpp b/src/engine/QueuedEvent.hpp deleted file mode 100644 index 99c5ff33..00000000 --- a/src/engine/QueuedEvent.hpp +++ /dev/null @@ -1,77 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_QUEUEDEVENT_HPP -#define INGEN_ENGINE_QUEUEDEVENT_HPP - -#include "Event.hpp" - -namespace Ingen { -namespace Engine { - -/** 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); - - /** True iff this event blocks the prepare phase of other events. */ - bool is_blocking() { return _blocking; } - - bool is_prepared() { return _pre_processed; } - -protected: - QueuedEvent(Engine& engine, - SharedPtr request, - FrameTime time, - bool blocking=false) - : Event(engine, request, time) - , _pre_processed(false) - , _blocking(blocking) - {} - - // NULL event base (for internal events only!) - explicit QueuedEvent(Engine& engine) - : Event(engine, SharedPtr(), 0) - , _pre_processed(false) - , _blocking(false) - {} - - bool _pre_processed; - bool _blocking; -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_QUEUEDEVENT_HPP diff --git a/src/engine/Request.hpp b/src/engine/Request.hpp deleted file mode 100644 index 3e4372c9..00000000 --- a/src/engine/Request.hpp +++ /dev/null @@ -1,81 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_REQUEST_HPP -#define INGEN_ENGINE_REQUEST_HPP - -#include -#include -#include "ingen/ClientInterface.hpp" -#include "EventSource.hpp" - -namespace Ingen { -namespace Engine { - -/** Record of a request (used to respond to clients). - * - * This is a glorified std::pair for replying - * to numbered messages from a client. - * - * For responses that involve several 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 Request -{ -public: - Request(EventSource* source=0, - ClientInterface* client=0, - int32_t id=1) - : _source(source) - , _client(client) - , _id(id) - {} - - EventSource* source() { return _source; } - int32_t id() const { return _id; } - void set_id(int32_t id) { _id = id; } - - ClientInterface* client() const { return _client; } - void set_client(ClientInterface* client) { _client = client; } - - void unblock() { - if (_source) - _source->unblock(); - } - - void respond_ok() { - if (_client) - _client->response_ok(_id); - } - - void respond_error(const std::string& msg) { - if (_client) - _client->response_error(_id, msg); - } - -private: - EventSource* _source; - ClientInterface* _client; - int32_t _id; -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_REQUEST_HPP - diff --git a/src/engine/ThreadManager.hpp b/src/engine/ThreadManager.hpp deleted file mode 100644 index 9a73250d..00000000 --- a/src/engine/ThreadManager.hpp +++ /dev/null @@ -1,56 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_THREADMANAGER_HPP -#define INGEN_ENGINE_THREADMANAGER_HPP - -#include -#include "raul/Thread.hpp" - -namespace Ingen { -namespace Engine { - -enum ThreadID { - THREAD_PRE_PROCESS, - THREAD_PROCESS, - THREAD_MESSAGE, -}; - -class ThreadManager { -public: - inline static bool thread_is(ThreadID id) { - return Raul::Thread::get().is_context(id); - } - - inline static void assert_thread(ThreadID id) { - assert(single_threaded || Raul::Thread::get().is_context(id)); - } - - inline static void assert_not_thread(ThreadID id) { - assert(single_threaded || !Raul::Thread::get().is_context(id)); - } - - /** Set to true during initialisation so ensure_thread doesn't fail. - * Defined in Engine.cpp - */ - static bool single_threaded; -}; - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_THREADMANAGER_HPP diff --git a/src/engine/events.hpp b/src/engine/events.hpp deleted file mode 100644 index 7ddcfe52..00000000 --- a/src/engine/events.hpp +++ /dev/null @@ -1,41 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_EVENTS_HPP -#define INGEN_ENGINE_EVENTS_HPP - -#include "ingen-config.h" - -#include "events/Connect.hpp" -#include "events/CreateNode.hpp" -#include "events/CreatePatch.hpp" -#include "events/CreatePort.hpp" -#include "events/Deactivate.hpp" -#include "events/Delete.hpp" -#include "events/Disconnect.hpp" -#include "events/DisconnectAll.hpp" -#include "events/Get.hpp" -#include "events/Move.hpp" -#include "events/Ping.hpp" -#include "events/RegisterClient.hpp" -#include "events/RequestMetadata.hpp" -#include "events/SetMetadata.hpp" -#include "events/SetPortValue.hpp" -#include "events/UnregisterClient.hpp" - -#endif // INGEN_ENGINE_EVENTS_HPP - diff --git a/src/engine/events/Connect.cpp b/src/engine/events/Connect.cpp deleted file mode 100644 index 0f4bd465..00000000 --- a/src/engine/events/Connect.cpp +++ /dev/null @@ -1,204 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "raul/Maid.hpp" -#include "raul/Path.hpp" -#include "ClientBroadcaster.hpp" -#include "Connect.hpp" -#include "ConnectionImpl.hpp" -#include "DuplexPort.hpp" -#include "Engine.hpp" -#include "EngineStore.hpp" -#include "InputPort.hpp" -#include "OutputPort.hpp" -#include "PatchImpl.hpp" -#include "PortImpl.hpp" -#include "ProcessContext.hpp" -#include "Request.hpp" -#include "types.hpp" - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { -namespace Events { - -Connect::Connect(Engine& engine, SharedPtr request, SampleCount timestamp, const Path& src_port_path, const Path& dst_port_path) - : QueuedEvent(engine, request, timestamp) - , _src_port_path(src_port_path) - , _dst_port_path(dst_port_path) - , _patch(NULL) - , _src_output_port(NULL) - , _dst_input_port(NULL) - , _compiled_patch(NULL) - , _port_listnode(NULL) - , _buffers(NULL) -{ -} - -void -Connect::pre_process() -{ - PortImpl* src_port = _engine.engine_store()->find_port(_src_port_path); - PortImpl* dst_port = _engine.engine_store()->find_port(_dst_port_path); - if (!src_port || !dst_port) { - _error = PORT_NOT_FOUND; - 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(); - if (!src_node || !dst_node) { - _error = PARENTS_NOT_FOUND; - QueuedEvent::pre_process(); - return; - } - - if (src_node->parent() != dst_node->parent() - && src_node != dst_node->parent() - && src_node->parent() != dst_node) { - _error = PARENT_PATCH_DIFFERENT; - QueuedEvent::pre_process(); - return; - } - - if (!ConnectionImpl::can_connect(_src_output_port, _dst_input_port)) { - _error = TYPE_MISMATCH; - QueuedEvent::pre_process(); - return; - } - - // 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(); - } - - if (_patch->has_connection(_src_output_port, _dst_input_port)) { - _error = ALREADY_CONNECTED; - QueuedEvent::pre_process(); - return; - } - - _connection = SharedPtr( - new ConnectionImpl(*_engine.buffer_factory(), _src_output_port, _dst_input_port)); - - _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(_connection); - _dst_input_port->increment_num_connections(); - - /*if ((_dst_input_port->num_connections() == 1 - && (_connection->must_mix() || _connection->must_queue())) - || _dst_input_port->num_connections() == 2) {*/ - _buffers = new Raul::Array(_dst_input_port->poly()); - _dst_input_port->get_buffers(*_engine.buffer_factory(), - _buffers, _dst_input_port->poly()); - //} - - if (_patch->enabled()) - _compiled_patch = _patch->compile(); - - QueuedEvent::pre_process(); -} - -void -Connect::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); - assert(_buffers); - //if (_buffers) - _engine.maid()->push(_dst_input_port->set_buffers(_buffers)); - //else - // _dst_input_port->setup_buffers(*_engine.buffer_factory(), _dst_input_port->poly()); - _dst_input_port->connect_buffers(); - _engine.maid()->push(_patch->compiled_patch()); - _patch->compiled_patch(_compiled_patch); - } -} - -void -Connect::post_process() -{ - std::ostringstream ss; - if (_error == NO_ERROR) { - _request->respond_ok(); - _engine.broadcaster()->connect(_src_port_path, _dst_port_path); - return; - } - - ss << boost::format("Unable to make connection %1% -> %2% (") - % _src_port_path.chop_scheme() % _dst_port_path.chop_scheme(); - - 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 << ")"; - _request->respond_error(ss.str()); -} - -} // namespace Engine -} // namespace Ingen -} // namespace Events - diff --git a/src/engine/events/Connect.hpp b/src/engine/events/Connect.hpp deleted file mode 100644 index ec40f70b..00000000 --- a/src/engine/events/Connect.hpp +++ /dev/null @@ -1,88 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_EVENTS_CONNECT_HPP -#define INGEN_EVENTS_CONNECT_HPP - -#include "raul/Path.hpp" -#include "QueuedEvent.hpp" -#include "PatchImpl.hpp" -#include "InputPort.hpp" -#include "types.hpp" - -namespace Raul { - template class ListNode; - template class Array; -} - -namespace Ingen { -namespace Engine { - -class PatchImpl; -class NodeImpl; -class ConnectionImpl; -class PortImpl; -class InputPort; -class OutputPort; -class CompiledPatch; - -namespace Events { - -/** Make a Connection between two Ports. - * - * \ingroup engine - */ -class Connect : public QueuedEvent -{ -public: - Connect(Engine& engine, SharedPtr request, SampleCount timestamp, const Raul::Path& src_port_path, const Raul::Path& 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; - OutputPort* _src_output_port; - InputPort* _dst_input_port; - - CompiledPatch* _compiled_patch; ///< New process order for Patch - - SharedPtr _connection; - InputPort::Connections::Node* _port_listnode; - - Raul::Array* _buffers; -}; - -} // namespace Engine -} // namespace Ingen -} // namespace Events - -#endif // INGEN_EVENTS_CONNECT_HPP diff --git a/src/engine/events/CreateNode.cpp b/src/engine/events/CreateNode.cpp deleted file mode 100644 index 68e66f0a..00000000 --- a/src/engine/events/CreateNode.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "raul/log.hpp" -#include "raul/Maid.hpp" -#include "raul/Path.hpp" -#include "sord/sordmm.hpp" -#include "shared/LV2URIMap.hpp" -#include "CreateNode.hpp" -#include "Request.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 "Driver.hpp" - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { -namespace Events { - -CreateNode::CreateNode( - Engine& engine, - SharedPtr request, - SampleCount timestamp, - const Path& path, - const URI& plugin_uri, - const Resource::Properties& properties) - : QueuedEvent(engine, request, timestamp) - , _path(path) - , _plugin_uri(plugin_uri) - , _patch(NULL) - , _plugin(NULL) - , _node(NULL) - , _compiled_patch(NULL) - , _node_already_exists(false) - , _polyphonic(false) - , _properties(properties) -{ - const Resource::Properties::const_iterator p = properties.find( - engine.world()->uris()->ingen_polyphonic); - if (p != properties.end() && p->second.type() == Raul::Atom::BOOL - && p->second.get_bool()) - _polyphonic = true; -} - -void -CreateNode::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()); - _plugin = _engine.node_factory()->plugin(_plugin_uri.str()); - - if (_patch && _plugin) { - - _node = _plugin->instantiate(*_engine.buffer_factory(), _path.symbol(), _polyphonic, _patch, _engine); - - if (_node != NULL) { - _node->properties().insert(_properties.begin(), _properties.end()); - _node->activate(*_engine.buffer_factory()); - - // 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)); - _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(); - } - } - - if (!_node) - _error = 1; - - QueuedEvent::pre_process(); -} - -void -CreateNode::execute(ProcessContext& context) -{ - QueuedEvent::execute(context); - - if (_node) { - _engine.maid()->push(_patch->compiled_patch()); - _patch->compiled_patch(_compiled_patch); - } -} - -void -CreateNode::post_process() -{ - if (!_request) - return; - - string msg; - if (_node_already_exists) { - msg = string("Could not create node - ").append(_path.str());// + " already exists."; - _request->respond_error(msg); - } else if (_patch == NULL) { - msg = "Could not find patch '" + _path.parent().str() +"' to add node."; - _request->respond_error(msg); - } else if (_plugin == NULL) { - msg = "Unable to load node "; - msg += _path.str() + " (you're missing the plugin " + _plugin_uri.str() + ")"; - _request->respond_error(msg); - } else if (_node == NULL) { - msg = "Failed to instantiate plugin " + _plugin_uri.str(); - _request->respond_error(msg); - } else { - _request->respond_ok(); - _engine.broadcaster()->send_object(_node, true); // yes, send ports - } -} - -} // namespace Engine -} // namespace Ingen -} // namespace Events - diff --git a/src/engine/events/CreateNode.hpp b/src/engine/events/CreateNode.hpp deleted file mode 100644 index 844eca01..00000000 --- a/src/engine/events/CreateNode.hpp +++ /dev/null @@ -1,71 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_EVENTS_CREATENODE_HPP -#define INGEN_EVENTS_CREATENODE_HPP - -#include -#include "QueuedEvent.hpp" -#include "ingen/Resource.hpp" - -namespace Ingen { -namespace Engine { - -class PatchImpl; -class PluginImpl; -class NodeImpl; -class CompiledPatch; - -namespace Events { - -/** An event to load a Node and insert it into a Patch. - * - * \ingroup engine - */ -class CreateNode : public QueuedEvent -{ -public: - CreateNode( - Engine& engine, - SharedPtr request, - SampleCount timestamp, - const Raul::Path& node_path, - const Raul::URI& plugin_uri, - const Resource::Properties& properties); - - void pre_process(); - void execute(ProcessContext& context); - void post_process(); - -private: - Raul::Path _path; - Raul::URI _plugin_uri; - PatchImpl* _patch; - PluginImpl* _plugin; - NodeImpl* _node; - CompiledPatch* _compiled_patch; ///< Patch's new process order - bool _node_already_exists; - bool _polyphonic; - - Resource::Properties _properties; -}; - -} // namespace Engine -} // namespace Ingen -} // namespace Events - -#endif // INGEN_EVENTS_CREATENODE_HPP diff --git a/src/engine/events/CreatePatch.cpp b/src/engine/events/CreatePatch.cpp deleted file mode 100644 index dcc79513..00000000 --- a/src/engine/events/CreatePatch.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "raul/Maid.hpp" -#include "raul/Path.hpp" -#include "shared/LV2URIMap.hpp" -#include "events/CreatePatch.hpp" -#include "Request.hpp" -#include "PatchImpl.hpp" -#include "NodeImpl.hpp" -#include "PluginImpl.hpp" -#include "Engine.hpp" -#include "ClientBroadcaster.hpp" -#include "Driver.hpp" -#include "EngineStore.hpp" - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { -namespace Events { - -CreatePatch::CreatePatch( - Engine& engine, - SharedPtr request, - SampleCount timestamp, - const Raul::Path& path, - int poly, - const Resource::Properties& properties) - : QueuedEvent(engine, request, timestamp) - , _path(path) - , _patch(NULL) - , _parent(NULL) - , _compiled_patch(NULL) - , _poly(poly) - , _properties(properties) -{ -} - -void -CreatePatch::pre_process() -{ - if (_path.is_root() || _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_poly())) - poly = _poly; - - const Ingen::Shared::LV2URIMap& uris = *_engine.world()->uris().get(); - - _patch = new PatchImpl(_engine, path.symbol(), poly, _parent, - _engine.driver()->sample_rate(), _poly); - _patch->properties().insert(_properties.begin(), _properties.end()); - _patch->add_property(uris.rdf_type, uris.ingen_Patch); - _patch->add_property(uris.rdf_type, - Resource::Property(uris.ingen_Node, Resource::EXTERNAL)); - - if (_parent != NULL) { - _parent->add_node(new PatchImpl::Nodes::Node(_patch)); - - if (_parent->enabled()) - _compiled_patch = _parent->compile(); - } - - _patch->activate(*_engine.buffer_factory()); - - // Insert into EngineStore - //_patch->add_to_store(_engine.engine_store()); - _engine.engine_store()->add(_patch); - - QueuedEvent::pre_process(); -} - -void -CreatePatch::execute(ProcessContext& context) -{ - QueuedEvent::execute(context); - - if (_patch) { - if (!_parent) { - assert(_path.is_root()); - assert(_patch->parent_patch() == NULL); - _engine.driver()->set_root_patch(_patch); - } else { - assert(_parent); - assert(!_path.is_root()); - _engine.maid()->push(_parent->compiled_patch()); - _parent->compiled_patch(_compiled_patch); - } - } -} - -void -CreatePatch::post_process() -{ - string msg; - if (_request) { - switch (_error) { - case NO_ERROR: - _request->respond_ok(); - // Don't send ports/nodes that have been added since prepare() - // (otherwise they would be sent twice) - _engine.broadcaster()->send_object(_patch, false); - break; - case OBJECT_EXISTS: - _request->respond_ok(); - /*string msg = "Unable to create patch: "; - msg.append(_path).append(" already exists."); - _request->respond_error(msg);*/ - break; - case PARENT_NOT_FOUND: - msg = "Unable to create patch: Parent "; - msg.append(Path(_path).parent().str()).append(" not found."); - _request->respond_error(msg); - break; - case INVALID_POLY: - msg = "Unable to create patch "; - msg.append(_path.str()).append(": ").append("Invalid polyphony requested."); - _request->respond_error(msg); - break; - default: - _request->respond_error("Unable to load patch."); - } - } -} - -} // namespace Engine -} // namespace Ingen -} // namespace Events - diff --git a/src/engine/events/CreatePatch.hpp b/src/engine/events/CreatePatch.hpp deleted file mode 100644 index 847583a4..00000000 --- a/src/engine/events/CreatePatch.hpp +++ /dev/null @@ -1,67 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_EVENTS_CREATEPATCH_HPP -#define INGEN_EVENTS_CREATEPATCH_HPP - -#include "QueuedEvent.hpp" -#include "ingen/Resource.hpp" - -namespace Ingen { -namespace Engine { - -class PatchImpl; -class CompiledPatch; - -namespace Events { - -/** Creates a new Patch. - * - * \ingroup engine - */ -class CreatePatch : public QueuedEvent -{ -public: - CreatePatch( - Engine& engine, - SharedPtr request, - SampleCount timestamp, - const Raul::Path& path, - int poly, - const Resource::Properties& properties); - - void pre_process(); - void execute(ProcessContext& context); - void post_process(); - -private: - enum ErrorType { NO_ERROR, OBJECT_EXISTS, PARENT_NOT_FOUND, INVALID_POLY }; - - const Raul::Path _path; - PatchImpl* _patch; - PatchImpl* _parent; - CompiledPatch* _compiled_patch; - int _poly; - - Resource::Properties _properties; -}; - -} // namespace Engine -} // namespace Ingen -} // namespace Events - -#endif // INGEN_EVENTS_CREATEPATCH_HPP diff --git a/src/engine/events/CreatePort.cpp b/src/engine/events/CreatePort.cpp deleted file mode 100644 index de5b2219..00000000 --- a/src/engine/events/CreatePort.cpp +++ /dev/null @@ -1,192 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "raul/Array.hpp" -#include "raul/Atom.hpp" -#include "raul/List.hpp" -#include "raul/Maid.hpp" -#include "raul/Path.hpp" -#include "shared/LV2URIMap.hpp" -#include "ClientBroadcaster.hpp" -#include "ControlBindings.hpp" -#include "CreatePort.hpp" -#include "Driver.hpp" -#include "DuplexPort.hpp" -#include "Engine.hpp" -#include "EngineStore.hpp" -#include "PatchImpl.hpp" -#include "PatchImpl.hpp" -#include "PluginImpl.hpp" -#include "PortImpl.hpp" -#include "Request.hpp" - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { -namespace Events { - -CreatePort::CreatePort( - Engine& engine, - SharedPtr request, - SampleCount timestamp, - const Raul::Path& path, - const Raul::URI& type, - bool is_output, - const Resource::Properties& properties) - : QueuedEvent(engine, request, timestamp, bool(request)) - , _path(path) - , _type(type) - , _is_output(is_output) - , _data_type(type) - , _patch(NULL) - , _patch_port(NULL) - , _driver_port(NULL) - , _properties(properties) -{ - /* This is blocking because of the two different sets of Patch ports, the array used in the - * audio thread (inherited from NodeImpl), 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). - * - * TODO: fix this using RCU? - */ - - if (_data_type == PortType::UNKNOWN) - _error = UNKNOWN_TYPE; -} - -void -CreatePort::pre_process() -{ - if (_error == UNKNOWN_TYPE || _engine.engine_store()->find_object(_path)) { - QueuedEvent::pre_process(); - return; - } - - _patch = _engine.engine_store()->find_patch(_path.parent()); - - const Ingen::Shared::LV2URIMap& uris = *_engine.world()->uris().get(); - - if (_patch != NULL) { - assert(_patch->path() == _path.parent()); - - size_t buffer_size = _engine.buffer_factory()->default_buffer_size(_data_type); - - const uint32_t old_num_ports = (_patch->external_ports()) - ? _patch->external_ports()->size() - : 0; - - Resource::Properties::const_iterator index_i = _properties.find(uris.lv2_index); - if (index_i == _properties.end()) { - index_i = _properties.insert(make_pair(uris.lv2_index, (int)old_num_ports)); - } else if (index_i->second.type() != Atom::INT - || index_i->second.get_int32() != static_cast(old_num_ports)) { - QueuedEvent::pre_process(); - _error = BAD_INDEX; - return; - } - - Resource::Properties::const_iterator poly_i = _properties.find(uris.ingen_polyphonic); - bool polyphonic = (poly_i != _properties.end() && poly_i->second.type() == Atom::BOOL - && poly_i->second.get_bool()); - - _patch_port = _patch->create_port(*_engine.buffer_factory(), _path.symbol(), _data_type, buffer_size, _is_output, polyphonic); - - _patch_port->properties().insert(_properties.begin(), _properties.end()); - - assert(index_i->second == Atom((int)_patch_port->index())); - - 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(), NULL); - else - _ports_array = new Raul::Array(old_num_ports + 1, NULL); - - _ports_array->at(old_num_ports) = _patch_port; - _engine.engine_store()->add(_patch_port); - - if (!_patch->parent()) - _driver_port = _engine.driver()->create_port( - dynamic_cast(_patch_port)); - - assert(_ports_array->size() == _patch->num_ports()); - - } else { - _error = CREATION_FAILED; - } - } - QueuedEvent::pre_process(); -} - -void -CreatePort::execute(ProcessContext& context) -{ - QueuedEvent::execute(context); - - if (_patch_port) { - _engine.maid()->push(_patch->external_ports()); - _patch->external_ports(_ports_array); - _engine.control_bindings()->port_binding_changed(context, _patch_port); - } - - if (_driver_port) { - _engine.driver()->add_port(_driver_port); - } - - if (_request) - _request->unblock(); -} - -void -CreatePort::post_process() -{ - if (!_request) - return; - - string msg; - switch (_error) { - case NO_ERROR: - _request->respond_ok(); - _engine.broadcaster()->send_object(_patch_port, true); - break; - case BAD_INDEX: - msg = string("Could not create port ") + _path.str() + " (Illegal index given)"; - _request->respond_error(msg); - break; - case UNKNOWN_TYPE: - msg = string("Could not create port ") + _path.str() + " (Unknown type)"; - _request->respond_error(msg); - break; - case CREATION_FAILED: - msg = string("Could not create port ") + _path.str() + " (Creation failed)"; - _request->respond_error(msg); - break; - } -} - -} // namespace Engine -} // namespace Ingen -} // namespace Events - diff --git a/src/engine/events/CreatePort.hpp b/src/engine/events/CreatePort.hpp deleted file mode 100644 index a4be3a30..00000000 --- a/src/engine/events/CreatePort.hpp +++ /dev/null @@ -1,81 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_EVENTS_CREATEPORT_HPP -#define INGEN_EVENTS_CREATEPORT_HPP - -#include "QueuedEvent.hpp" -#include "raul/Path.hpp" -#include "raul/Array.hpp" -#include "ingen/PortType.hpp" -#include "ingen/Resource.hpp" - -namespace Ingen { -namespace Engine { - -class PatchImpl; -class PortImpl; -class DriverPort; - -namespace Events { - -/** An event to add a Port to a Patch. - * - * \ingroup engine - */ -class CreatePort : public QueuedEvent -{ -public: - CreatePort( - Engine& engine, - SharedPtr request, - SampleCount timestamp, - const Raul::Path& path, - const Raul::URI& type, - bool is_output, - const Resource::Properties& properties); - - void pre_process(); - void execute(ProcessContext& context); - void post_process(); - -private: - enum ErrorType { - NO_ERROR, - UNKNOWN_TYPE, - BAD_INDEX, - CREATION_FAILED - }; - - Raul::Path _path; - Raul::URI _type; - bool _is_output; - PortType _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; - - Resource::Properties _properties; -}; - -} // namespace Engine -} // namespace Ingen -} // namespace Events - -#endif // INGEN_EVENTS_CREATEPORT_HPP diff --git a/src/engine/events/Deactivate.hpp b/src/engine/events/Deactivate.hpp deleted file mode 100644 index 87bfe298..00000000 --- a/src/engine/events/Deactivate.hpp +++ /dev/null @@ -1,49 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_EVENTS_DEACTIVATE_HPP -#define INGEN_EVENTS_DEACTIVATE_HPP - -#include "QueuedEvent.hpp" -#include "Engine.hpp" - -namespace Ingen { -namespace Engine { -namespace Events { - -/** Deactivates the engine. - * - * \ingroup engine - */ -class Deactivate : public QueuedEvent -{ -public: - Deactivate(Engine& engine, SharedPtr request, SampleCount timestamp) - : QueuedEvent(engine, request, timestamp) - {} - - void post_process() { - _request->respond_ok(); - _engine.deactivate(); - } -}; - -} // namespace Engine -} // namespace Ingen -} // namespace Events - -#endif // INGEN_EVENTS_DEACTIVATE_HPP diff --git a/src/engine/events/Delete.cpp b/src/engine/events/Delete.cpp deleted file mode 100644 index 4a6e520b..00000000 --- a/src/engine/events/Delete.cpp +++ /dev/null @@ -1,213 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "raul/Maid.hpp" -#include "raul/Path.hpp" -#include "ClientBroadcaster.hpp" -#include "ControlBindings.hpp" -#include "Delete.hpp" -#include "DisconnectAll.hpp" -#include "Driver.hpp" -#include "Engine.hpp" -#include "EngineStore.hpp" -#include "NodeImpl.hpp" -#include "PatchImpl.hpp" -#include "PluginImpl.hpp" -#include "PortImpl.hpp" -#include "Request.hpp" - -using namespace std; - -namespace Ingen { -namespace Engine { -namespace Events { - -Delete::Delete(Engine& engine, - SharedPtr request, - FrameTime time, - const Raul::URI& uri) - : QueuedEvent(engine, request, time, true) - , _uri(uri) - , _store_iterator(engine.engine_store()->end()) - , _garbage(NULL) - , _driver_port(NULL) - , _patch_node_listnode(NULL) - , _patch_port_listnode(NULL) - , _ports_array(NULL) - , _compiled_patch(NULL) - , _disconnect_event(NULL) -{ - assert(request); - assert(request->source()); - - if (Raul::Path::is_path(uri)) - _path = Raul::Path(uri.str()); -} - -Delete::~Delete() -{ - delete _disconnect_event; -} - -void -Delete::pre_process() -{ - if (_path.is_root() || _path == "path:/control_in" || _path == "path:/control_out") { - QueuedEvent::pre_process(); - return; - } - - _removed_bindings = _engine.control_bindings()->remove(_path); - - _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 && !_path.is_root()) { - assert(_node->parent_patch()); - _patch_node_listnode = _node->parent_patch()->remove_node(_path.symbol()); - if (_patch_node_listnode) { - assert(_patch_node_listnode->elem() == _node.get()); - - _disconnect_event = new DisconnectAll(_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 deleted - 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.symbol()); - if (_patch_port_listnode) { - assert(_patch_port_listnode->elem() == _port.get()); - - _disconnect_event = new DisconnectAll(_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 -Delete::execute(ProcessContext& context) -{ - QueuedEvent::execute(context); - - PatchImpl* parent_patch = NULL; - - if (_patch_node_listnode) { - assert(_node); - - if (_disconnect_event) - _disconnect_event->execute(context); - - parent_patch = _node->parent_patch(); - - } else if (_patch_port_listnode) { - assert(_port); - - if (_disconnect_event) - _disconnect_event->execute(context); - - parent_patch = _port->parent_patch(); - - _engine.maid()->push(_port->parent_patch()->external_ports()); - _port->parent_patch()->external_ports(_ports_array); - - if ( ! _port->parent_patch()->parent()) - _garbage = _engine.driver()->remove_port(_port->path(), &_driver_port); - } - - if (parent_patch) { - _engine.maid()->push(parent_patch->compiled_patch()); - parent_patch->compiled_patch(_compiled_patch); - } - - _request->unblock(); -} - -void -Delete::post_process() -{ - _removed_bindings.reset(); - - if (!Raul::Path::is_path(_uri) - || _path.is_root() || _path == "path:/control_in" || _path == "path:/control_out") { - // XXX: Just ignore? - //_request->respond_error(_path.chop_scheme() + " can not be deleted"); - } else if (!_node && !_port) { - string msg = string("Could not find object ") + _path.chop_scheme() + " to delete"; - _request->respond_error(msg); - } else if (_patch_node_listnode) { - assert(_node); - _node->deactivate(); - _request->respond_ok(); - _engine.broadcaster()->bundle_begin(); - if (_disconnect_event) - _disconnect_event->post_process(); - _engine.broadcaster()->del(_path); - _engine.broadcaster()->bundle_end(); - _engine.maid()->push(_patch_node_listnode); - } else if (_patch_port_listnode) { - assert(_port); - _request->respond_ok(); - _engine.broadcaster()->bundle_begin(); - if (_disconnect_event) - _disconnect_event->post_process(); - _engine.broadcaster()->del(_path); - _engine.broadcaster()->bundle_end(); - _engine.maid()->push(_patch_port_listnode); - } else { - _request->respond_error("Unable to delete object " + _path.chop_scheme()); - } - - if (_driver_port) - _driver_port->destroy(); - - _engine.maid()->push(_garbage); -} - -} // namespace Engine -} // namespace Ingen -} // namespace Events diff --git a/src/engine/events/Delete.hpp b/src/engine/events/Delete.hpp deleted file mode 100644 index f00ad847..00000000 --- a/src/engine/events/Delete.hpp +++ /dev/null @@ -1,95 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_EVENTS_DELETE_HPP -#define INGEN_EVENTS_DELETE_HPP - -#include "QueuedEvent.hpp" -#include "EngineStore.hpp" -#include "PatchImpl.hpp" -#include "ControlBindings.hpp" - -namespace Raul { - template class Array; - template class ListNode; -} - -namespace Ingen { -namespace Engine { - -class GraphObjectImpl; -class NodeImpl; -class PortImpl; -class DriverPort; -class CompiledPatch; - -namespace Events { - -class DisconnectAll; - -/** \page methods - *

DELETE

- * As per WebDAV (RFC4918 S9.6). - * - * Remove an object from the engine and destroy it. - * - * \li All properties of the object are lost - * \li All references to the object are lost (e.g. the parent's reference to - * this child is lost, any connections to the object are removed, etc.) - */ - -/** DELETE a graph object (see \ref methods). - * \ingroup engine - */ -class Delete : public QueuedEvent -{ -public: - Delete(Engine& engine, - SharedPtr request, - FrameTime timestamp, - const Raul::URI& uri); - - ~Delete(); - - void pre_process(); - void execute(ProcessContext& context); - void post_process(); - -private: - Raul::URI _uri; - Raul::Path _path; - EngineStore::iterator _store_iterator; - SharedPtr _node; ///< Non-NULL iff a node - SharedPtr _port; ///< Non-NULL iff a port - Raul::Deletable* _garbage; - 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 - DisconnectAll* _disconnect_event; - - SharedPtr _removed_bindings; - - SharedPtr< Raul::Table > > _removed_table; -}; - -} // namespace Engine -} // namespace Ingen -} // namespace Events - -#endif // INGEN_EVENTS_DELETE_HPP diff --git a/src/engine/events/Disconnect.cpp b/src/engine/events/Disconnect.cpp deleted file mode 100644 index 9618826c..00000000 --- a/src/engine/events/Disconnect.cpp +++ /dev/null @@ -1,269 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "raul/log.hpp" -#include "raul/Maid.hpp" -#include "raul/Path.hpp" -#include "events/Disconnect.hpp" -#include "AudioBuffer.hpp" -#include "ClientBroadcaster.hpp" -#include "ConnectionImpl.hpp" -#include "DuplexPort.hpp" -#include "Engine.hpp" -#include "EngineStore.hpp" -#include "InputPort.hpp" -#include "OutputPort.hpp" -#include "PatchImpl.hpp" -#include "PortImpl.hpp" -#include "ProcessContext.hpp" -#include "Request.hpp" -#include "ThreadManager.hpp" - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { -namespace Events { - -Disconnect::Disconnect( - Engine& engine, - SharedPtr request, - SampleCount timestamp, - const Raul::Path& src_port_path, - const Raul::Path& dst_port_path) - : QueuedEvent(engine, request, timestamp) - , _src_port_path(src_port_path) - , _dst_port_path(dst_port_path) - , _patch(NULL) - , _src_port(NULL) - , _dst_port(NULL) - , _impl(NULL) - , _compiled_patch(NULL) -{ -} - -Disconnect::Impl::Impl(Engine& e, - PatchImpl* patch, - OutputPort* s, - InputPort* d) - : _engine(e) - , _src_output_port(s) - , _dst_input_port(d) - , _patch(patch) - , _connection(patch->remove_connection(_src_output_port, _dst_input_port)) - , _buffers(NULL) -{ - ThreadManager::assert_thread(THREAD_PRE_PROCESS); - - NodeImpl* const src_node = _src_output_port->parent_node(); - NodeImpl* const dst_node = _dst_input_port->parent_node(); - - 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; - } - } - - _dst_input_port->decrement_num_connections(); - - if (_dst_input_port->num_connections() == 0) { - _buffers = new Raul::Array(_dst_input_port->poly()); - _dst_input_port->get_buffers(*_engine.buffer_factory(), - _buffers, _dst_input_port->poly()); - - const bool is_control = _dst_input_port->is_a(PortType::CONTROL); - const float value = is_control ? _dst_input_port->value().get_float() : 0; - for (uint32_t i = 0; i < _buffers->size(); ++i) { - if (is_control) { - PtrCast(_buffers->at(i))->set_value(value, 0, 0); - } else { - _buffers->at(i)->clear(); - } - } - } - - _connection->pending_disconnection(true); -} - -void -Disconnect::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; - } - - 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 (!_patch->has_connection(_src_port, _dst_port)) { - _error = NOT_CONNECTED; - QueuedEvent::pre_process(); - return; - } - - if (src_node == NULL || dst_node == NULL) { - _error = PARENTS_NOT_FOUND; - QueuedEvent::pre_process(); - return; - } - - _impl = new Impl(_engine, - _patch, - dynamic_cast(_src_port), - dynamic_cast(_dst_port)); - - if (_patch->enabled()) - _compiled_patch = _patch->compile(); - - QueuedEvent::pre_process(); -} - -bool -Disconnect::Impl::execute(ProcessContext& context, bool set_dst_buffers) -{ - ThreadManager::assert_thread(THREAD_PROCESS); - - InputPort::Connections::Node* const port_connections_node - = _dst_input_port->remove_connection(context, _src_output_port); - if (!port_connections_node) { - return false; - } - - if (set_dst_buffers) { - if (_buffers) { - _engine.maid()->push(_dst_input_port->set_buffers(_buffers)); - } else { - _dst_input_port->setup_buffers(*_engine.buffer_factory(), - _dst_input_port->poly()); - } - _dst_input_port->connect_buffers(); - } else { - _dst_input_port->recycle_buffers(); - } - - assert(_connection); - assert(port_connections_node->elem() == _connection); - - _engine.maid()->push(port_connections_node); - return true; -} - -void -Disconnect::execute(ProcessContext& context) -{ - QueuedEvent::execute(context); - - if (_error == NO_ERROR) { - if (!_impl->execute(context, true)) { - _error = CONNECTION_NOT_FOUND; - return; - } - - _engine.maid()->push(_patch->compiled_patch()); - _patch->compiled_patch(_compiled_patch); - } -} - -void -Disconnect::post_process() -{ - if (_error == NO_ERROR) { - if (_request) - _request->respond_ok(); - _engine.broadcaster()->disconnect(_src_port->path(), _dst_port->path()); - } else { - string msg("Unable to disconnect "); - msg.append(_src_port_path.str() + " => " + _dst_port_path.str()); - msg.append(" ("); - switch (_error) { - case PARENT_PATCH_DIFFERENT: - msg.append("Ports exist in different patches"); - break; - case PORT_NOT_FOUND: - msg.append("Port not found"); - break; - case TYPE_MISMATCH: - msg.append("Ports have incompatible types"); - break; - case NOT_CONNECTED: - msg.append("Ports are not connected"); - break; - case PARENTS_NOT_FOUND: - msg.append("Parent node not found"); - break; - case CONNECTION_NOT_FOUND: - msg.append("Connection not found"); - break; - default: - break; - } - msg.append(")"); - if (_request) - _request->respond_error(msg); - } - - delete _impl; -} - -} // namespace Engine -} // namespace Ingen -} // namespace Events - diff --git a/src/engine/events/Disconnect.hpp b/src/engine/events/Disconnect.hpp deleted file mode 100644 index 7a22e474..00000000 --- a/src/engine/events/Disconnect.hpp +++ /dev/null @@ -1,106 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_EVENTS_DISCONNECT_HPP -#define INGEN_EVENTS_DISCONNECT_HPP - -#include "raul/Path.hpp" -#include "QueuedEvent.hpp" -#include "types.hpp" -#include "PatchImpl.hpp" -#include "BufferFactory.hpp" - -namespace Raul { - template class ListNode; - template class Array; -} - -namespace Ingen { -namespace Engine { - -class CompiledPatch; -class InputPort; -class OutputPort; -class PortImpl; - -namespace Events { - -/** Make a Connection between two Ports. - * - * \ingroup engine - */ -class Disconnect : public QueuedEvent -{ -public: - Disconnect( - Engine& engine, - SharedPtr request, - SampleCount timestamp, - const Raul::Path& src_port_path, - const Raul::Path& dst_port_path); - - void pre_process(); - void execute(ProcessContext& context); - void post_process(); - - class Impl { - public: - Impl(Engine& e, - PatchImpl* patch, - OutputPort* s, - InputPort* d); - - bool execute(ProcessContext& context, bool set_dst_buffers); - - InputPort* dst_port() { return _dst_input_port; } - - private: - Engine& _engine; - OutputPort* _src_output_port; - InputPort* _dst_input_port; - PatchImpl* _patch; - SharedPtr _connection; - Raul::Array* _buffers; - }; - -private: - enum ErrorType { - NO_ERROR, - PARENT_PATCH_DIFFERENT, - PORT_NOT_FOUND, - TYPE_MISMATCH, - NOT_CONNECTED, - PARENTS_NOT_FOUND, - CONNECTION_NOT_FOUND - }; - - const Raul::Path _src_port_path; - const Raul::Path _dst_port_path; - - PatchImpl* _patch; - PortImpl* _src_port; - PortImpl* _dst_port; - - Impl* _impl; - CompiledPatch* _compiled_patch; -}; - -} // namespace Events -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_EVENTS_DISCONNECT_HPP diff --git a/src/engine/events/DisconnectAll.cpp b/src/engine/events/DisconnectAll.cpp deleted file mode 100644 index 3509fd04..00000000 --- a/src/engine/events/DisconnectAll.cpp +++ /dev/null @@ -1,189 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "raul/Array.hpp" -#include "raul/Maid.hpp" -#include "raul/Path.hpp" - -#include "ClientBroadcaster.hpp" -#include "ConnectionImpl.hpp" -#include "Engine.hpp" -#include "EngineStore.hpp" -#include "InputPort.hpp" -#include "NodeImpl.hpp" -#include "OutputPort.hpp" -#include "PatchImpl.hpp" -#include "PortImpl.hpp" -#include "Request.hpp" -#include "events/Disconnect.hpp" -#include "events/DisconnectAll.hpp" -#include "util.hpp" - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { -namespace Events { - -DisconnectAll::DisconnectAll(Engine& engine, SharedPtr request, SampleCount timestamp, const Path& parent_path, const Path& node_path) - : QueuedEvent(engine, request, timestamp) - , _parent_path(parent_path) - , _path(node_path) - , _parent(NULL) - , _node(NULL) - , _port(NULL) - , _compiled_patch(NULL) - , _deleting(false) -{ -} - -/** Internal version for use by other events. - */ -DisconnectAll::DisconnectAll(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)) - , _compiled_patch(NULL) - , _deleting(true) -{ -} - -DisconnectAll::~DisconnectAll() -{ - for (Impls::iterator i = _impls.begin(); i != _impls.end(); ++i) - delete (*i); -} - -void -DisconnectAll::maybe_remove_connection(ConnectionImpl* c) -{ - if (c->pending_disconnection()) - return; - - OutputPort* src = dynamic_cast(c->src_port()); - InputPort* dst = dynamic_cast(c->dst_port()); - - if (_node) { - if (src->parent_node() == _node || dst->parent_node() == _node) { - _impls.push_back(new Disconnect::Impl(_engine, _parent, src, dst)); - } - } else { - assert(_port); - if (src == _port || dst == _port) { - _impls.push_back(new Disconnect::Impl(_engine, _parent, src, dst)); - } - } -} - -void -DisconnectAll::pre_process() -{ - if (!_deleting) { - _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)); - } - - for (Patch::Connections::const_iterator i = _parent->connections().begin(); - i != _parent->connections().end(); ++i) { - maybe_remove_connection((ConnectionImpl*)i->second.get()); - } - - if (!_deleting && _parent->enabled()) - _compiled_patch = _parent->compile(); - - QueuedEvent::pre_process(); -} - -void -DisconnectAll::execute(ProcessContext& context) -{ - QueuedEvent::execute(context); - - if (_error == NO_ERROR) { - for (Impls::iterator i = _impls.begin(); i != _impls.end(); ++i) { - (*i)->execute(context, - !_deleting || ((*i)->dst_port()->parent_node() != _node)); - } - } - - _engine.maid()->push(_parent->compiled_patch()); - _parent->compiled_patch(_compiled_patch); -} - -void -DisconnectAll::post_process() -{ - if (_error == NO_ERROR) { - if (_request) - _request->respond_ok(); - _engine.broadcaster()->disconnect_all(_parent_path, _path); - } else { - if (_request) { - boost::format fmt("Unable to disconnect %1% (%2%)"); - fmt % _path; - switch (_error) { - case INVALID_PARENT_PATH: - fmt % string("Invalid parent path: ").append(_parent_path.str()); - break; - case PARENT_NOT_FOUND: - fmt % string("Unable to find parent: ").append(_parent_path.str()); - break; - case OBJECT_NOT_FOUND: - fmt % string("Unable to find object"); - default: - break; - } - _request->respond_error(fmt.str()); - } - } -} - -} // namespace Engine -} // namespace Ingen -} // namespace Events - diff --git a/src/engine/events/DisconnectAll.hpp b/src/engine/events/DisconnectAll.hpp deleted file mode 100644 index b5dc258c..00000000 --- a/src/engine/events/DisconnectAll.hpp +++ /dev/null @@ -1,93 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_EVENTS_DISCONNECTALL_HPP -#define INGEN_EVENTS_DISCONNECTALL_HPP - -#include - -#include "raul/Path.hpp" - -#include "Disconnect.hpp" -#include "QueuedEvent.hpp" - -namespace Ingen { -namespace Engine { - -class CompiledPatch; -class NodeImpl; -class PatchImpl; -class PortImpl; - -namespace Events { - -class Disconnect; - -/** An event to disconnect all connections to a Node. - * - * \ingroup engine - */ -class DisconnectAll : public QueuedEvent -{ -public: - DisconnectAll( - Engine& engine, - SharedPtr request, - SampleCount timestamp, - const Raul::Path& parent, - const Raul::Path& object); - - DisconnectAll( - Engine& engine, - PatchImpl* parent, - GraphObjectImpl* object); - - ~DisconnectAll(); - - void pre_process(); - void execute(ProcessContext& context); - void post_process(); - -private: - enum ErrorType { - NO_ERROR, - INVALID_PARENT_PATH, - PARENT_NOT_FOUND, - OBJECT_NOT_FOUND, - }; - - void maybe_remove_connection(ConnectionImpl* c); - - Raul::Path _parent_path; - Raul::Path _path; - PatchImpl* _parent; - NodeImpl* _node; - PortImpl* _port; - - typedef std::list Impls; - Impls _impls; - - CompiledPatch* _compiled_patch; ///< New process order for Patch - - bool _deleting; -}; - -} // namespace Engine -} // namespace Ingen -} // namespace Events - -#endif // INGEN_EVENTS_DISCONNECTALL_HPP diff --git a/src/engine/events/Get.cpp b/src/engine/events/Get.cpp deleted file mode 100644 index 7425dd8a..00000000 --- a/src/engine/events/Get.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "Get.hpp" -#include "ingen/ClientInterface.hpp" -#include "Request.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 namespace Raul; - -namespace Ingen { -namespace Engine { -namespace Events { - -Get::Get( - Engine& engine, - SharedPtr request, - SampleCount timestamp, - const URI& uri) - : QueuedEvent(engine, request, timestamp) - , _uri(uri) - , _object(NULL) - , _plugin(NULL) -{ -} - -void -Get::pre_process() -{ - if (_uri == "ingen:plugins") { - _plugins = _engine.node_factory()->plugins(); - } else if (Path::is_valid(_uri.str())) { - _object = _engine.engine_store()->find_object(Path(_uri.str())); - } else { - _plugin = _engine.node_factory()->plugin(_uri); - } - - QueuedEvent::pre_process(); -} - -void -Get::post_process() -{ - if (_uri == "ingen:plugins") { - _request->respond_ok(); - _engine.broadcaster()->send_plugins_to(_request->client(), _plugins); - } else if (!_object && !_plugin) { - _request->respond_error("Unable to find object requested."); - } else if (_request->client()) { - _request->respond_ok(); - if (_object) - ObjectSender::send_object(_request->client(), _object, true); - else if (_plugin) - _request->client()->put(_uri, _plugin->properties()); - } else { - _request->respond_error("Unable to find client to send object."); - } -} - -} // namespace Engine -} // namespace Ingen -} // namespace Events - diff --git a/src/engine/events/Get.hpp b/src/engine/events/Get.hpp deleted file mode 100644 index 56bc5f17..00000000 --- a/src/engine/events/Get.hpp +++ /dev/null @@ -1,60 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_EVENTS_GET_HPP -#define INGEN_EVENTS_GET_HPP - -#include "QueuedEvent.hpp" -#include "NodeFactory.hpp" -#include "types.hpp" - -namespace Ingen { -namespace Engine { - -class GraphObjectImpl; -class PluginImpl; - -namespace Events { - -/** A request from a client to send an object. - * - * \ingroup engine - */ -class Get : public QueuedEvent -{ -public: - Get( - Engine& engine, - SharedPtr request, - SampleCount timestamp, - const Raul::URI& uri); - - void pre_process(); - void post_process(); - -private: - const Raul::URI _uri; - GraphObjectImpl* _object; - const PluginImpl* _plugin; - NodeFactory::Plugins _plugins; -}; - -} // namespace Engine -} // namespace Ingen -} // namespace Events - -#endif // INGEN_EVENTS_GET_HPP diff --git a/src/engine/events/Move.cpp b/src/engine/events/Move.cpp deleted file mode 100644 index bccb3856..00000000 --- a/src/engine/events/Move.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "raul/Path.hpp" -#include "events/Move.hpp" -#include "ClientBroadcaster.hpp" -#include "Engine.hpp" -#include "NodeImpl.hpp" -#include "EngineStore.hpp" -#include "PatchImpl.hpp" -#include "Request.hpp" -#include "Driver.hpp" - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { -namespace Events { - -Move::Move(Engine& engine, SharedPtr request, SampleCount timestamp, const Path& path, const Path& new_path) - : QueuedEvent(engine, request, timestamp) - , _old_path(path) - , _new_path(new_path) - , _parent_patch(NULL) - , _store_iterator(engine.engine_store()->end()) -{ -} - -Move::~Move() -{ -} - -void -Move::pre_process() -{ - if (!_old_path.parent().is_parent_of(_new_path)) { - _error = PARENT_DIFFERS; - QueuedEvent::pre_process(); - return; - } - _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 = 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 -Move::execute(ProcessContext& context) -{ - QueuedEvent::execute(context); - - SharedPtr port = PtrCast(_store_iterator->second); - if (port && port->parent()->parent() == NULL) { - DriverPort* driver_port = _engine.driver()->driver_port(_new_path); - if (driver_port) - driver_port->move(_new_path); - } -} - -void -Move::post_process() -{ - string msg = "Unable to rename object - "; - - if (_error == NO_ERROR) { - _request->respond_ok(); - _engine.broadcaster()->move(_old_path, _new_path); - } else { - if (_error == OBJECT_EXISTS) - msg.append("Object already exists at ").append(_new_path.str()); - else if (_error == OBJECT_NOT_FOUND) - msg.append("Could not find object ").append(_old_path.str()); - else if (_error == OBJECT_NOT_RENAMABLE) - msg.append(_old_path.str()).append(" is not renamable"); - else if (_error == PARENT_DIFFERS) - msg.append(_new_path.str()).append(" is a child of a different patch"); - - _request->respond_error(msg); - } -} - -} // namespace Engine -} // namespace Ingen -} // namespace Events diff --git a/src/engine/events/Move.hpp b/src/engine/events/Move.hpp deleted file mode 100644 index 03e7e1a6..00000000 --- a/src/engine/events/Move.hpp +++ /dev/null @@ -1,79 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_EVENTS_MOVE_HPP -#define INGEN_EVENTS_MOVE_HPP - -#include "raul/Path.hpp" -#include "QueuedEvent.hpp" -#include "EngineStore.hpp" - -namespace Ingen { -namespace Engine { - -class PatchImpl; - -namespace Events { - -/** \page methods - *

MOVE

- * As per WebDAV (RFC4918 S9.9). - * - * Move an object from its current location and insert it at a new location - * in a single operation. - * - * MOVE to a path with a different parent is currently not supported. - */ - -/** MOVE a graph object to a new path (see \ref methods). - * \ingroup engine - */ -class Move : public QueuedEvent -{ -public: - Move( - Engine& engine, - SharedPtr request, - SampleCount timestamp, - const Raul::Path& old_path, - const Raul::Path& new_path); - ~Move(); - - void pre_process(); - void execute(ProcessContext& context); - void post_process(); - -private: - enum ErrorType { - NO_ERROR, - OBJECT_NOT_FOUND, - OBJECT_EXISTS, - OBJECT_NOT_RENAMABLE, - PARENT_DIFFERS - }; - - Raul::Path _old_path; - Raul::Path _new_path; - PatchImpl* _parent_patch; - EngineStore::iterator _store_iterator; -}; - -} // namespace Engine -} // namespace Ingen -} // namespace Events - -#endif // INGEN_EVENTS_MOVE_HPP diff --git a/src/engine/events/Ping.hpp b/src/engine/events/Ping.hpp deleted file mode 100644 index ad4e1361..00000000 --- a/src/engine/events/Ping.hpp +++ /dev/null @@ -1,51 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_EVENTS_PING_HPP -#define INGEN_EVENTS_PING_HPP - -#include "QueuedEvent.hpp" -#include "types.hpp" -#include "Request.hpp" - -namespace Ingen { -namespace Engine { - -class PortImpl; - -namespace Events { - -/** A ping that travels through the pre-processed event queue before responding - * (useful for the order guarantee). - * - * \ingroup engine - */ -class Ping : public QueuedEvent -{ -public: - Ping(Engine& engine, SharedPtr request, SampleCount timestamp) - : QueuedEvent(engine, request, timestamp) - {} - - void post_process() { _request->respond_ok(); } -}; - -} // namespace Engine -} // namespace Ingen -} // namespace Events - -#endif // INGEN_EVENTS_PING_HPP diff --git a/src/engine/events/RegisterClient.cpp b/src/engine/events/RegisterClient.cpp deleted file mode 100644 index 50916e6a..00000000 --- a/src/engine/events/RegisterClient.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "Request.hpp" -#include "events/RegisterClient.hpp" -#include "Engine.hpp" -#include "ClientBroadcaster.hpp" - -using namespace Raul; - -namespace Ingen { -namespace Engine { -namespace Events { - -RegisterClient::RegisterClient(Engine& engine, - SharedPtr request, - SampleCount timestamp, - const URI& uri, - ClientInterface* client) - : QueuedEvent(engine, request, timestamp) - , _uri(uri) - , _client(client) -{ -} - -void -RegisterClient::pre_process() -{ - _engine.broadcaster()->register_client(_uri, _client); - - QueuedEvent::pre_process(); -} - -void -RegisterClient::post_process() -{ - _request->respond_ok(); -} - -} // namespace Engine -} // namespace Ingen -} // namespace Events - diff --git a/src/engine/events/RegisterClient.hpp b/src/engine/events/RegisterClient.hpp deleted file mode 100644 index 5e5c6a15..00000000 --- a/src/engine/events/RegisterClient.hpp +++ /dev/null @@ -1,54 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_EVENTS_REGISTERCLIENT_HPP -#define INGEN_EVENTS_REGISTERCLIENT_HPP - -#include "raul/URI.hpp" -#include "ingen/ClientInterface.hpp" -#include "QueuedEvent.hpp" - -namespace Ingen { -namespace Engine { -namespace Events { - -/** Registers a new client with the OSC system, so it can receive updates. - * - * \ingroup engine - */ -class RegisterClient : public QueuedEvent -{ -public: - RegisterClient(Engine& engine, - SharedPtr request, - SampleCount timestamp, - const Raul::URI& uri, - ClientInterface* client); - - void pre_process(); - void post_process(); - -private: - Raul::URI _uri; - ClientInterface* _client; -}; - -} // namespace Engine -} // namespace Ingen -} // namespace Events - -#endif // INGEN_EVENTS_REGISTERCLIENT_HPP diff --git a/src/engine/events/RequestMetadata.cpp b/src/engine/events/RequestMetadata.cpp deleted file mode 100644 index c5642104..00000000 --- a/src/engine/events/RequestMetadata.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "raul/IntrusivePtr.hpp" -#include "ingen/ClientInterface.hpp" -#include "events/RequestMetadata.hpp" -#include "shared/LV2Atom.hpp" -#include "shared/LV2URIMap.hpp" -#include "AudioBuffer.hpp" -#include "ClientBroadcaster.hpp" -#include "Engine.hpp" -#include "EngineStore.hpp" -#include "GraphObjectImpl.hpp" -#include "ObjectBuffer.hpp" -#include "PluginImpl.hpp" -#include "PortImpl.hpp" -#include "ProcessContext.hpp" -#include "Request.hpp" - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { -namespace Events { - -RequestMetadata::RequestMetadata(Engine& engine, - SharedPtr request, - SampleCount timestamp, - Resource::Graph ctx, - const URI& subject, - const URI& key) - : QueuedEvent(engine, request, timestamp) - , _special_type(NONE) - , _uri(subject) - , _key(key) - , _resource(0) - , _context(ctx) -{ -} - -void -RequestMetadata::pre_process() -{ - const bool is_object = Path::is_path(_uri); - if (_request->client()) { - if (is_object) - _resource = _engine.engine_store()->find_object(Path(_uri.str())); - else - _resource = _engine.node_factory()->plugin(_uri); - - if (!_resource) { - QueuedEvent::pre_process(); - return; - } - } - - GraphObjectImpl* obj = dynamic_cast(_resource); - if (obj) { - if (_key == _engine.world()->uris()->ingen_value) - _special_type = PORT_VALUE; - else - _value = obj->get_property(_key); - } else { - _value = _resource->get_property(_key); - } - - QueuedEvent::pre_process(); -} - -void -RequestMetadata::execute(ProcessContext& context) -{ - QueuedEvent::execute(context); - if (_special_type == PORT_VALUE) { - PortImpl* port = dynamic_cast(_resource); - if (port) { - IntrusivePtr abuf = PtrCast(port->buffer(0)); - if (abuf) { - _value = abuf->value_at(0); - } else { - IntrusivePtr obuf = PtrCast(port->buffer(0)); - if (obuf) { - Ingen::Shared::LV2Atom::to_atom(*_engine.world()->uris().get(), - obuf->atom(), - _value); - } - } - } else { - _resource = 0; - } - } -} - -void -RequestMetadata::post_process() -{ - if (_request->client()) { - if (_special_type == PORT_VALUE) { - if (_resource) { - _request->respond_ok(); - _request->client()->set_property(_uri.str(), - _engine.world()->uris()->ingen_value, _value); - } else { - const string msg = "Get value for non-port " + _uri.str(); - _request->respond_error(msg); - } - } else if (!_resource) { - const string msg = "Unable to find subject " + _uri.str(); - _request->respond_error(msg); - } else { - _request->respond_ok(); - _request->client()->set_property(_uri, _key, _value); - } - } else { - _request->respond_error("Unknown client"); - } -} - -} // namespace Engine -} // namespace Ingen -} // namespace Events - diff --git a/src/engine/events/RequestMetadata.hpp b/src/engine/events/RequestMetadata.hpp deleted file mode 100644 index e1730207..00000000 --- a/src/engine/events/RequestMetadata.hpp +++ /dev/null @@ -1,79 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_EVENTS_REQUESTMETADATA_HPP -#define INGEN_EVENTS_REQUESTMETADATA_HPP - -#include "raul/Atom.hpp" -#include "raul/URI.hpp" - -#include "QueuedEvent.hpp" - -namespace Ingen { - -namespace Shared { class ResourceImpl; } - -namespace Engine { - -class GraphObjectImpl; - -namespace Events { - -/** \page methods - *

GET

- * As per HTTP (RFC2616 S9.3). - * - * Get the description of a graph object. - */ - -/** GET an object (see \ref methods). - * - * \ingroup engine - */ -class RequestMetadata : public QueuedEvent -{ -public: - RequestMetadata(Engine& engine, - SharedPtr request, - SampleCount timestamp, - Resource::Graph context, - const Raul::URI& subject, - const Raul::URI& key); - - void pre_process(); - void execute(ProcessContext& context); - void post_process(); - -private: - enum ErrorType { NO_ERROR, NOT_FOUND }; - enum { - NONE, - PORT_VALUE - } _special_type; - - Raul::URI _uri; - Raul::URI _key; - Raul::Atom _value; - Ingen::Shared::ResourceImpl* _resource; - Resource::Graph _context; -}; - -} // namespace Engine -} // namespace Ingen -} // namespace Events - -#endif // INGEN_EVENTS_REQUESTMETADATA_HPP diff --git a/src/engine/events/SendBinding.cpp b/src/engine/events/SendBinding.cpp deleted file mode 100644 index b9a43366..00000000 --- a/src/engine/events/SendBinding.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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/SendBinding.hpp" -#include "shared/LV2URIMap.hpp" -#include "Engine.hpp" -#include "PortImpl.hpp" -#include "ClientBroadcaster.hpp" - -using namespace std; - -namespace Ingen { -namespace Engine { -namespace Events { - -void -SendBinding::post_process() -{ - const Ingen::Shared::LV2URIMap& uris = *_engine.world()->uris().get(); - Raul::Atom::DictValue dict; - if (_type == ControlBindings::MIDI_CC) { - dict[uris.rdf_type] = uris.midi_Controller; - dict[uris.midi_controllerNumber] = _num; - } else if (_type == ControlBindings::MIDI_BENDER) { - dict[uris.rdf_type] = uris.midi_Bender; - } else if (_type == ControlBindings::MIDI_CHANNEL_PRESSURE) { - dict[uris.rdf_type] = uris.midi_ChannelPressure; - } else if (_type == ControlBindings::MIDI_NOTE) { - dict[uris.rdf_type] = uris.midi_Note; - dict[uris.midi_noteNumber] = _num; - } - // TODO: other event types - _port->set_property(uris.ingen_controlBinding, dict); // FIXME: thread unsafe - _engine.broadcaster()->set_property(_port->path(), uris.ingen_controlBinding, dict); -} - -} // namespace Engine -} // namespace Ingen -} // namespace Events - diff --git a/src/engine/events/SendBinding.hpp b/src/engine/events/SendBinding.hpp deleted file mode 100644 index 1cd38ff2..00000000 --- a/src/engine/events/SendBinding.hpp +++ /dev/null @@ -1,86 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_EVENTS_SENDBINDING_HPP -#define INGEN_EVENTS_SENDBINDING_HPP - -#include "engine/Event.hpp" -#include "engine/ControlBindings.hpp" -#include "engine/types.hpp" - -namespace Ingen { -namespace Engine { - -class PortImpl; - -namespace Events { - -/** A special event used internally to send control bindings from the audio thread. - * - * See SendPortValue documentation for details. - * - * \ingroup engine - */ -class SendBinding : public Event -{ -public: - inline SendBinding( - Engine& engine, - SampleCount timestamp, - PortImpl* port, - ControlBindings::Type type, - int16_t num) - : Event(engine, SharedPtr(), timestamp) - , _port(port) - , _type(type) - , _num(num) - { - assert(_port); - switch (type) { - case ControlBindings::MIDI_CC: - assert(num >= 0 && num < 128); - break; - case ControlBindings::MIDI_RPN: - assert(num >= 0 && num < 16384); - break; - case ControlBindings::MIDI_NRPN: - assert(num >= 0 && num < 16384); - default: - break; - } - } - - inline SendBinding& operator=(const SendBinding& ev) { - _port = ev._port; - _type = ev._type; - _num = ev._num; - return *this; - } - - void post_process(); - -private: - PortImpl* _port; - ControlBindings::Type _type; - int16_t _num; -}; - -} // namespace Engine -} // namespace Ingen -} // namespace Events - -#endif // INGEN_EVENTS_SENDBINDING_HPP diff --git a/src/engine/events/SendPortActivity.cpp b/src/engine/events/SendPortActivity.cpp deleted file mode 100644 index e202c519..00000000 --- a/src/engine/events/SendPortActivity.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "events/SendPortActivity.hpp" -#include "Engine.hpp" -#include "PortImpl.hpp" -#include "ClientBroadcaster.hpp" - -namespace Ingen { -namespace Engine { -namespace Events { - -void -SendPortActivity::post_process() -{ - _engine.broadcaster()->activity(_port->path()); -} - -} // namespace Engine -} // namespace Ingen -} // namespace Events - diff --git a/src/engine/events/SendPortActivity.hpp b/src/engine/events/SendPortActivity.hpp deleted file mode 100644 index a99081ba..00000000 --- a/src/engine/events/SendPortActivity.hpp +++ /dev/null @@ -1,69 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_EVENTS_SENDPORTACTIVITY_HPP -#define INGEN_EVENTS_SENDPORTACTIVITY_HPP - -#include "Event.hpp" -#include "types.hpp" - -namespace Ingen { -namespace Engine { - -class PortImpl; - -namespace Events { - -/** 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 SendPortActivity : public Event -{ -public: - inline SendPortActivity(Engine& engine, - SampleCount timestamp, - PortImpl* port) - : Event(engine, SharedPtr(), timestamp) - , _port(port) - { - } - - inline SendPortActivity& operator=(const SendPortActivity& ev) { - _port = ev._port; - return *this; - } - - void post_process(); - -private: - PortImpl* _port; -}; - -} // namespace Engine -} // namespace Ingen -} // namespace Events - -#endif // INGEN_EVENTS_SENDPORTACTIVITY_HPP diff --git a/src/engine/events/SendPortValue.cpp b/src/engine/events/SendPortValue.cpp deleted file mode 100644 index 9ac60a05..00000000 --- a/src/engine/events/SendPortValue.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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/SendPortValue.hpp" -#include "shared/LV2URIMap.hpp" -#include "Engine.hpp" -#include "PortImpl.hpp" -#include "ClientBroadcaster.hpp" - -using namespace std; - -namespace Ingen { -namespace Engine { -namespace Events { - -void -SendPortValue::post_process() -{ - _engine.broadcaster()->set_property( - _port->path(), - _engine.world()->uris()->ingen_value, _value); -} - -} // namespace Engine -} // namespace Ingen -} // namespace Events - diff --git a/src/engine/events/SendPortValue.hpp b/src/engine/events/SendPortValue.hpp deleted file mode 100644 index 3ed1275e..00000000 --- a/src/engine/events/SendPortValue.hpp +++ /dev/null @@ -1,82 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_EVENTS_SENDPORTVALUE_HPP -#define INGEN_EVENTS_SENDPORTVALUE_HPP - -#include "raul/Atom.hpp" -#include "engine/Event.hpp" -#include "engine/types.hpp" - -namespace Ingen { -namespace Engine { - -class PortImpl; - -namespace Events { - -/** 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 SendPortValue : public Event -{ -public: - inline SendPortValue( - Engine& engine, - SampleCount timestamp, - PortImpl* port, - bool omni, - uint32_t voice_num, - const Raul::Atom& value) - : Event(engine, SharedPtr(), timestamp) - , _port(port) - , _omni(omni) - , _voice_num(voice_num) - , _value(value) - { - } - - inline SendPortValue& operator=(const SendPortValue& ev) { - _port = ev._port; - _omni = ev._omni; - _voice_num = ev._voice_num; - _value = ev._value; - return *this; - } - - void post_process(); - -private: - PortImpl* _port; - bool _omni; - uint32_t _voice_num; - Raul::Atom _value; -}; - -} // namespace Engine -} // namespace Ingen -} // namespace Events - -#endif // INGEN_EVENTS_SENDPORTVALUE_HPP diff --git a/src/engine/events/SetMetadata.cpp b/src/engine/events/SetMetadata.cpp deleted file mode 100644 index 8e7ef2bf..00000000 --- a/src/engine/events/SetMetadata.cpp +++ /dev/null @@ -1,380 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "raul/log.hpp" -#include "raul/Maid.hpp" -#include "ingen/PortType.hpp" -#include "shared/LV2URIMap.hpp" -#include "ClientBroadcaster.hpp" -#include "ControlBindings.hpp" -#include "CreateNode.hpp" -#include "CreatePatch.hpp" -#include "CreatePort.hpp" -#include "Driver.hpp" -#include "Engine.hpp" -#include "EngineStore.hpp" -#include "GraphObjectImpl.hpp" -#include "PatchImpl.hpp" -#include "PluginImpl.hpp" -#include "PortImpl.hpp" -#include "Request.hpp" -#include "SetMetadata.hpp" -#include "SetPortValue.hpp" - -#define LOG(s) s << "[SetMetadata] " - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { -namespace Events { - -typedef Resource::Properties Properties; - -SetMetadata::SetMetadata( - Engine& engine, - SharedPtr request, - SampleCount timestamp, - bool create, - Resource::Graph context, - const URI& subject, - const Properties& properties, - const Properties& remove) - : QueuedEvent(engine, request, timestamp, false) - , _create_event(NULL) - , _subject(subject) - , _properties(properties) - , _remove(remove) - , _object(NULL) - , _patch(NULL) - , _compiled_patch(NULL) - , _create(create) - , _context(context) -{ - if (context != Resource::DEFAULT) { - Resource::set_context(_properties, context); - } - - /* - LOG(info) << "Set " << subject << " : " << context << " {" << endl; - typedef Resource::Properties::const_iterator iterator; - for (iterator i = properties.begin(); i != properties.end(); ++i) - LOG(info) << " " << i->first << " = " << i->second << " :: " << i->second.type() << endl; - LOG(info) << "}" << endl; - - LOG(info) << "Unset " << subject << " {" << endl; - typedef Resource::Properties::const_iterator iterator; - for (iterator i = remove.begin(); i != remove.end(); ++i) - LOG(info) << " " << i->first << " = " << i->second << " :: " << i->second.type() << endl; - LOG(info) << "}" << endl; - */ -} - -SetMetadata::~SetMetadata() -{ - for (SetEvents::iterator i = _set_events.begin(); i != _set_events.end(); ++i) - delete *i; - - delete _create_event; -} - -void -SetMetadata::pre_process() -{ - typedef Properties::const_iterator iterator; - - const bool is_graph_object = Path::is_path(_subject); - - _object = is_graph_object - ? _engine.engine_store()->find_object(Path(_subject.str())) - : static_cast(_engine.node_factory()->plugin(_subject)); - - if (!_object && (!is_graph_object || !_create)) { - _error = NOT_FOUND; - QueuedEvent::pre_process(); - return; - } - - const Ingen::Shared::LV2URIMap& uris = *_engine.world()->uris().get(); - - if (is_graph_object && !_object) { - Path path(_subject.str()); - bool is_patch = false, is_node = false, is_port = false, is_output = false; - PortType data_type(PortType::UNKNOWN); - Shared::ResourceImpl::type(uris, _properties, is_patch, is_node, is_port, is_output, data_type); - - // Create a separate request without a source so EventSource isn't unblocked twice - SharedPtr sub_request(new Request(NULL, _request->client(), _request->id())); - - if (is_patch) { - uint32_t poly = 1; - iterator p = _properties.find(uris.ingen_polyphony); - if (p != _properties.end() && p->second.is_valid() && p->second.type() == Atom::INT) - poly = p->second.get_int32(); - _create_event = new CreatePatch(_engine, sub_request, _time, - path, poly, _properties); - } else if (is_node) { - const iterator p = _properties.find(uris.rdf_instanceOf); - _create_event = new CreateNode(_engine, sub_request, _time, - path, p->second.get_uri(), _properties); - } else if (is_port) { - _blocking = bool(_request); - _create_event = new CreatePort(_engine, sub_request, _time, - path, data_type.uri(), is_output, _properties); - } - if (_create_event) - _create_event->pre_process(); - else - _error = BAD_OBJECT_TYPE; - QueuedEvent::pre_process(); - return; - } - - _types.reserve(_properties.size()); - - GraphObjectImpl* obj = dynamic_cast(_object); - -#if 0 - // If we're replacing (i.e. this is a PUT, not a POST), first remove all properties - // with keys we will later set. This must be done first so a PUT with several properties - // of the same predicate (e.g. rdf:type) retains the multiple values. Only previously - // existing properties should be replaced - if (_replace) - for (Properties::iterator p = _properties.begin(); p != _properties.end(); ++p) - obj->properties().erase(p->first); -#endif - - for (Properties::const_iterator p = _remove.begin(); p != _remove.end(); ++p) { - const Raul::URI& key = p->first; - const Raul::Atom& value = p->second; - if (key == uris.ingen_controlBinding && value == uris.wildcard) { - PortImpl* port = dynamic_cast(_object); - if (port) - _old_bindings = _engine.control_bindings()->remove(port); - } - _object->remove_property(key, value); - } - - for (Properties::iterator p = _properties.begin(); p != _properties.end(); ++p) { - const Raul::URI& key = p->first; - const Raul::Atom& value = p->second; - SpecialType op = NONE; - if (obj) { - Resource& resource = *obj; - resource.add_property(key, value); - - PortImpl* port = dynamic_cast(_object); - if (port) { - if (key == uris.ingen_broadcast) { - if (value.type() == Atom::BOOL) { - op = ENABLE_BROADCAST; - } else { - _error = BAD_VALUE_TYPE; - } - } else if (key == uris.ingen_value) { - SetPortValue* ev = new SetPortValue(_engine, _request, _time, port, value); - ev->pre_process(); - _set_events.push_back(ev); - } else if (key == uris.ingen_controlBinding) { - if (port->is_a(PortType::CONTROL)) { - if (value == uris.wildcard) { - _engine.control_bindings()->learn(port); - } else if (value.type() == Atom::DICT) { - op = CONTROL_BINDING; - } else { - _error = BAD_VALUE_TYPE; - } - } else { - _error = BAD_OBJECT_TYPE; - } - } - } else if ((_patch = dynamic_cast(_object))) { - if (key == uris.ingen_enabled) { - if (value.type() == Atom::BOOL) { - op = ENABLE; - // FIXME: defer this until all other metadata has been processed - if (value.get_bool() && !_patch->enabled()) - _compiled_patch = _patch->compile(); - } else { - _error = BAD_VALUE_TYPE; - } - } else if (key == uris.ingen_polyphony) { - if (value.type() == Atom::INT) { - op = POLYPHONY; - _blocking = true; - _patch->prepare_internal_poly(*_engine.buffer_factory(), value.get_int32()); - } else { - _error = BAD_VALUE_TYPE; - } - } - } else if (key == uris.ingen_polyphonic) { - PatchImpl* parent = dynamic_cast(obj->parent()); - if (parent) { - if (value.type() == Atom::BOOL) { - op = POLYPHONIC; - _blocking = true; - obj->set_property(key, value.get_bool()); - NodeImpl* node = dynamic_cast(obj); - if (node) - node->set_polyphonic(value.get_bool()); - if (value.get_bool()) { - obj->prepare_poly(*_engine.buffer_factory(), parent->internal_poly()); - } else { - obj->prepare_poly(*_engine.buffer_factory(), 1); - } - } else { - _error = BAD_VALUE_TYPE; - } - } else { - _error = BAD_OBJECT_TYPE; - } - } - } - - if (_error != NO_ERROR) { - _error_predicate += key.str(); - break; - } - - _types.push_back(op); - } - - QueuedEvent::pre_process(); -} - -void -SetMetadata::execute(ProcessContext& context) -{ - if (_error != NO_ERROR) { - QueuedEvent::execute(context); - return; - } - - if (_create_event) { - QueuedEvent::execute(context); - _create_event->execute(context); - if (_blocking) - _request->unblock(); - return; - } - - for (SetEvents::iterator i = _set_events.begin(); i != _set_events.end(); ++i) - (*i)->execute(context); - - GraphObjectImpl* const object = dynamic_cast(_object); - NodeImpl* const node = dynamic_cast(_object); - PortImpl* const port = dynamic_cast(_object); - - std::vector::const_iterator t = _types.begin(); - for (Properties::const_iterator p = _properties.begin(); p != _properties.end(); ++p, ++t) { - const Raul::Atom& value = p->second; - switch (*t) { - case ENABLE_BROADCAST: - if (port) - port->broadcast(value.get_bool()); - break; - case ENABLE: - if (value.get_bool()) { - if (_compiled_patch) { - _engine.maid()->push(_patch->compiled_patch()); - _patch->compiled_patch(_compiled_patch); - } - _patch->enable(); - } else { - _patch->disable(); - } - break; - case POLYPHONIC: - { - PatchImpl* parent = reinterpret_cast(object->parent()); - if (value.get_bool()) - object->apply_poly(*_engine.maid(), parent->internal_poly()); - else - object->apply_poly(*_engine.maid(), 1); - } - break; - case POLYPHONY: - if (_patch->internal_poly() != static_cast(value.get_int32()) && - !_patch->apply_internal_poly(_engine.driver()->context(), - *_engine.buffer_factory(), - *_engine.maid(), value.get_int32())) { - _error = INTERNAL; - } - break; - case CONTROL_BINDING: - if (port) { - _engine.control_bindings()->port_binding_changed(context, port); - } else if (node) { - if (node->plugin_impl()->type() == Plugin::Internal) { - node->learn(); - } - } - break; - case NONE: - break; - } - } - - QueuedEvent::execute(context); - - if (_blocking) - _request->unblock(); -} - -void -SetMetadata::post_process() -{ - for (SetEvents::iterator i = _set_events.begin(); i != _set_events.end(); ++i) - (*i)->post_process(); - - switch (_error) { - case NO_ERROR: - if (_create_event) - _create_event->post_process(); - else - _request->respond_ok(); - if (_create) - _engine.broadcaster()->put(_subject, _properties, _context); - else - _engine.broadcaster()->delta(_subject, _remove, _properties); - break; - case NOT_FOUND: - _request->respond_error((boost::format( - "Unable to find object `%1%'") % _subject).str()); - break; - case INTERNAL: - _request->respond_error("Internal error"); - break; - case BAD_OBJECT_TYPE: - _request->respond_error((boost::format( - "Bad type for object `%1%'") % _subject).str()); - break; - case BAD_VALUE_TYPE: - _request->respond_error((boost::format( - "Bad metadata value type for subject `%1%' predicate `%2%'") - % _subject % _error_predicate).str()); - break; - } -} - -} // namespace Engine -} // namespace Ingen -} // namespace Events - diff --git a/src/engine/events/SetMetadata.hpp b/src/engine/events/SetMetadata.hpp deleted file mode 100644 index ca2d49ec..00000000 --- a/src/engine/events/SetMetadata.hpp +++ /dev/null @@ -1,124 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_EVENTS_SETMETADATA_HPP -#define INGEN_EVENTS_SETMETADATA_HPP - -#include -#include "raul/URI.hpp" -#include "shared/ResourceImpl.hpp" -#include "QueuedEvent.hpp" - -namespace Ingen { -namespace Engine { - -class GraphObjectImpl; -class PatchImpl; -class CompiledPatch; - -namespace Events { - -/** \page methods - *

POST

- * As per HTTP (RFC2616 S9.5). - * - * Append properties to a graph object. - * - * An object can have several properties with a single predicate. - * POST appends properties without modifying or removing existing properties. - */ - -/** \page methods - *

PUT

- * As per HTTP (RFC2616 S9.6). - * - * Set properties of a graph object, or create an object. - * - * An object can have several properties with a single predicate. - * \li If the object does not yet exist, the message must contain sufficient - * information to create the object (e.g. known rdf:type properties, etc.) - * \li If the object does exist, a PUT removes all existing object properties - * with predicates that match any property in the message, then adds all - * properties from the message. - */ - -class SetPortValue; - -/** Set properties of a graph object. - * \ingroup engine - */ -class SetMetadata : public QueuedEvent -{ -public: - SetMetadata( - Engine& engine, - SharedPtr request, - SampleCount timestamp, - bool create, - Resource::Graph context, - const Raul::URI& subject, - const Resource::Properties& properties, - const Resource::Properties& remove = Resource::Properties()); - - ~SetMetadata(); - - void pre_process(); - void execute(ProcessContext& context); - void post_process(); - -private: - enum ErrorType { - NO_ERROR, - NOT_FOUND, - INTERNAL, - BAD_OBJECT_TYPE, - BAD_VALUE_TYPE - }; - - enum SpecialType { - NONE, - ENABLE, - ENABLE_BROADCAST, - POLYPHONY, - POLYPHONIC, - CONTROL_BINDING - }; - - typedef std::vector SetEvents; - - QueuedEvent* _create_event; - SetEvents _set_events; - std::vector _types; - std::vector _remove_types; - Raul::URI _subject; - Resource::Properties _properties; - Resource::Properties _remove; - Ingen::Shared::ResourceImpl* _object; - PatchImpl* _patch; - CompiledPatch* _compiled_patch; - std::string _error_predicate; - bool _create; - Resource::Graph _context; - - SharedPtr _old_bindings; -}; - -} // namespace Engine -} // namespace Ingen -} // namespace Events - -#endif // INGEN_EVENTS_SETMETADATA_HPP diff --git a/src/engine/events/SetPortValue.cpp b/src/engine/events/SetPortValue.cpp deleted file mode 100644 index f68a6cab..00000000 --- a/src/engine/events/SetPortValue.cpp +++ /dev/null @@ -1,223 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "raul/log.hpp" -#include "lv2/lv2plug.in/ns/ext/event/event.h" -#include "shared/LV2URIMap.hpp" -#include "shared/LV2Features.hpp" -#include "shared/LV2Atom.hpp" -#include "shared/World.hpp" -#include "AudioBuffer.hpp" -#include "ClientBroadcaster.hpp" -#include "ControlBindings.hpp" -#include "Driver.hpp" -#include "Engine.hpp" -#include "EngineStore.hpp" -#include "EventBuffer.hpp" -#include "MessageContext.hpp" -#include "NodeImpl.hpp" -#include "ObjectBuffer.hpp" -#include "PortImpl.hpp" -#include "ProcessContext.hpp" -#include "ProcessContext.hpp" -#include "Request.hpp" -#include "SetPortValue.hpp" - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { -namespace Events { - -SetPortValue::SetPortValue(Engine& engine, - SharedPtr request, - bool queued, - SampleCount timestamp, - const Raul::Path& port_path, - const Raul::Atom& value) - : QueuedEvent(engine, request, timestamp) - , _queued(queued) - , _port_path(port_path) - , _value(value) - , _port(NULL) -{ -} - -/** Internal */ -SetPortValue::SetPortValue(Engine& engine, - SharedPtr request, - SampleCount timestamp, - PortImpl* port, - const Raul::Atom& value) - : QueuedEvent(engine, request, timestamp) - , _queued(false) - , _port_path(port->path()) - , _value(value) - , _port(port) -{ -} - -SetPortValue::~SetPortValue() -{ -} - -void -SetPortValue::pre_process() -{ - if (_queued) { - if (_port == NULL) - _port = _engine.engine_store()->find_port(_port_path); - if (_port == NULL) - _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) { - apply(*_engine.message_context()); - _port->parent_node()->set_port_valid(_port->index()); - _engine.message_context()->run(_port->parent_node(), - _engine.driver()->frame_time() + _engine.driver()->block_length()); - } - - if (_port) { - _port->set_value(_value); - _port->set_property(_engine.world()->uris()->ingen_value, _value); - } - - QueuedEvent::pre_process(); -} - -void -SetPortValue::execute(ProcessContext& context) -{ - Event::execute(context); - assert(_time >= context.start() && _time <= context.end()); - - if (_port && _port->context() == Context::MESSAGE) - return; - - apply(context); - _engine.control_bindings()->port_value_changed(context, _port); -} - -void -SetPortValue::apply(Context& context) -{ - uint32_t start = context.start(); - if (_error == NO_ERROR && !_port) - _port = _engine.engine_store()->find_port(_port_path); - - if (!_port) { - 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).get(); - AudioBuffer* const abuf = dynamic_cast(buf); - if (abuf) { - if (_value.type() != Atom::FLOAT) { - _error = TYPE_MISMATCH; - return; - } - - for (uint32_t v = 0; v < _port->poly(); ++v) { - ((AudioBuffer*)_port->buffer(v).get())->set_value( - _value.get_float(), start, _time); - } - return; - } - - Ingen::Shared::LV2URIMap& uris = *_engine.world()->uris().get(); - - EventBuffer* const ebuf = dynamic_cast(buf); - if (ebuf && _value.type() == Atom::BLOB) { - const uint32_t frames = std::max(uint32_t(_time - start), ebuf->latest_frames()); - - // Size 0 event, pass it along to the plugin as a typed but empty event - if (_value.data_size() == 0) { - const uint32_t type_id = uris.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(), - "http://lv2plug.in/ns/ext/midi#MidiEvent")) { - ebuf->prepare_write(context); - ebuf->append(frames, 0, - uris.global_to_event(uris.midi_MidiEvent.id).second, - _value.data_size(), - (const uint8_t*)_value.get_blob()); - _port->raise_set_by_user_flag(); - return; - } - } - - ObjectBuffer* const obuf = dynamic_cast(buf); - if (obuf) { - obuf->atom()->size = obuf->size() - sizeof(LV2_Atom); - if (Ingen::Shared::LV2Atom::from_atom(uris, _value, obuf->atom())) { - debug << "Converted atom " << _value << " :: " << obuf->atom()->type - << " * " << obuf->atom()->size << " @ " << obuf->atom() << endl; - return; - } else { - warn << "Failed to convert atom to LV2 object" << endl; - } - } - - warn << "Unknown value type " << (int)_value.type() << endl; - } -} - -void -SetPortValue::post_process() -{ - string msg; - std::ostringstream ss; - switch (_error) { - case NO_ERROR: - assert(_port != NULL); - _request->respond_ok(); - _engine.broadcaster()->set_property(_port_path, - _engine.world()->uris()->ingen_value, _value); - break; - case TYPE_MISMATCH: - ss << "Illegal value type " << _value.type() - << " for port " << _port_path << endl; - _request->respond_error(ss.str()); - break; - case PORT_NOT_FOUND: - msg = "Unable to find port "; - msg.append(_port_path.str()).append(" to set value"); - _request->respond_error(msg); - break; - case NO_SPACE: - ss << "Attempt to write " << _value.data_size() << " bytes to " - << _port_path.str() << ", with capacity " - << _port->buffer_size() << endl; - _request->respond_error(ss.str()); - break; - } -} - -} // namespace Engine -} // namespace Ingen -} // namespace Events - diff --git a/src/engine/events/SetPortValue.hpp b/src/engine/events/SetPortValue.hpp deleted file mode 100644 index 78ef0cf4..00000000 --- a/src/engine/events/SetPortValue.hpp +++ /dev/null @@ -1,83 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_EVENTS_SETPORTVALUE_HPP -#define INGEN_EVENTS_SETPORTVALUE_HPP - -#include "raul/Atom.hpp" -#include "QueuedEvent.hpp" -#include "types.hpp" - -namespace Ingen { -namespace Engine { - -class PortImpl; - -namespace Events { - -/** 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 SetPortValue : public QueuedEvent -{ -public: - SetPortValue(Engine& engine, - SharedPtr request, - bool queued, - SampleCount timestamp, - const Raul::Path& port_path, - const Raul::Atom& value); - - SetPortValue(Engine& engine, - SharedPtr request, - SampleCount timestamp, - PortImpl* port, - const Raul::Atom& value); - - ~SetPortValue(); - - void pre_process(); - void execute(ProcessContext& context); - void post_process(); - -private: - enum ErrorType { - NO_ERROR, - PORT_NOT_FOUND, - NO_SPACE, - TYPE_MISMATCH - }; - - void apply(Context& context); - - bool _queued; - const Raul::Path _port_path; - const Raul::Atom _value; - PortImpl* _port; -}; - -} // namespace Engine -} // namespace Ingen -} // namespace Events - -#endif // INGEN_EVENTS_SETPORTVALUE_HPP diff --git a/src/engine/events/UnregisterClient.cpp b/src/engine/events/UnregisterClient.cpp deleted file mode 100644 index 6f850596..00000000 --- a/src/engine/events/UnregisterClient.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "ingen/ClientInterface.hpp" -#include "Request.hpp" -#include "UnregisterClient.hpp" -#include "Engine.hpp" -#include "ClientBroadcaster.hpp" - -using namespace Raul; - -namespace Ingen { -namespace Engine { -namespace Events { - -UnregisterClient::UnregisterClient(Engine& engine, SharedPtr request, SampleCount timestamp, const URI& uri) - : QueuedEvent(engine, request, timestamp) - , _uri(uri) -{ -} - -void -UnregisterClient::post_process() -{ - if (_engine.broadcaster()->unregister_client(_uri)) - _request->respond_ok(); - else - _request->respond_error("Unable to unregister client"); -} - -} // namespace Engine -} // namespace Ingen -} // namespace Events - diff --git a/src/engine/events/UnregisterClient.hpp b/src/engine/events/UnregisterClient.hpp deleted file mode 100644 index 427fc897..00000000 --- a/src/engine/events/UnregisterClient.hpp +++ /dev/null @@ -1,50 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_EVENTS_UNREGISTERCLIENT_HPP -#define INGEN_EVENTS_UNREGISTERCLIENT_HPP - -#include "QueuedEvent.hpp" -#include "raul/URI.hpp" - -namespace Ingen { -namespace Engine { -namespace Events { - -/** Unregisters an OSC client so it no longer receives notifications. - * - * \ingroup engine - */ -class UnregisterClient : public QueuedEvent -{ -public: - UnregisterClient(Engine& engine, - SharedPtr request, - SampleCount timestamp, - const Raul::URI& uri); - - void post_process(); - -private: - Raul::URI _uri; -}; - -} // namespace Engine -} // namespace Ingen -} // namespace Events - -#endif // INGEN_EVENTS_UNREGISTERCLIENT_HPP diff --git a/src/engine/ingen_engine.cpp b/src/engine/ingen_engine.cpp deleted file mode 100644 index c31e5e33..00000000 --- a/src/engine/ingen_engine.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "shared/Module.hpp" -#include "shared/World.hpp" -#include "Engine.hpp" -#include "QueuedEngineInterface.hpp" -#include "util.hpp" - -using namespace Ingen; - -struct IngenEngineModule : public Ingen::Shared::Module { - virtual void load(Ingen::Shared::World* world) { - Engine::set_denormal_flags(); - SharedPtr engine(new Engine::Engine(world)); - world->set_local_engine(engine); - SharedPtr interface( - new Engine::QueuedEngineInterface(*engine.get(), - engine->event_queue_size())); - world->set_engine(interface); - engine->add_event_source(interface); - assert(world->local_engine() == engine); - } -}; - -extern "C" { - -Ingen::Shared::Module* -ingen_module_load() -{ - return new IngenEngineModule(); -} - -} // extern "C" diff --git a/src/engine/ingen_http.cpp b/src/engine/ingen_http.cpp deleted file mode 100644 index f7153ca2..00000000 --- a/src/engine/ingen_http.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "shared/Module.hpp" -#include "shared/World.hpp" -#include "HTTPEngineReceiver.hpp" -#include "Engine.hpp" - -using namespace std; -using namespace Ingen; - -struct IngenHTTPModule : public Ingen::Shared::Module { - void load(Ingen::Shared::World* world) { - Engine::Engine* engine = (Engine::Engine*)world->local_engine().get(); - SharedPtr interface( - new Engine::HTTPEngineReceiver( - *engine, - world->conf()->option("engine-port").get_int32())); - engine->add_event_source(interface); - } -}; - -extern "C" { - -Ingen::Shared::Module* -ingen_module_load() -{ - return new IngenHTTPModule(); -} - -} // extern "C" diff --git a/src/engine/ingen_jack.cpp b/src/engine/ingen_jack.cpp deleted file mode 100644 index e46dd8a6..00000000 --- a/src/engine/ingen_jack.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "shared/Module.hpp" -#include "shared/World.hpp" -#include "JackDriver.hpp" -#include "Engine.hpp" - -using namespace std; -using namespace Ingen; - -struct IngenJackModule : public Ingen::Shared::Module { - void load(Ingen::Shared::World* world) { - Engine::JackDriver* driver = new Engine::JackDriver( - *(Engine::Engine*)world->local_engine().get()); - driver->attach(world->conf()->option("jack-server").get_string(), - world->conf()->option("jack-client").get_string(), NULL); - ((Engine::Engine*)world->local_engine().get())->set_driver( - SharedPtr(driver)); - } -}; - -extern "C" { - -Ingen::Shared::Module* -ingen_module_load() -{ - return new IngenJackModule(); -} - -} // extern "C" diff --git a/src/engine/ingen_lv2.cpp b/src/engine/ingen_lv2.cpp deleted file mode 100644 index c6a250b7..00000000 --- a/src/engine/ingen_lv2.cpp +++ /dev/null @@ -1,422 +0,0 @@ -/* Ingen.LV2 - A thin wrapper which allows Ingen to run as an LV2 plugin. - * Copyright 2008-2011 David Robillard - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the 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 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 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. - */ - -#include - -#include -#include - -#include -#include -#include - -#include "lv2/lv2plug.in/ns/lv2core/lv2.h" - -#include "raul/SharedPtr.hpp" -#include "raul/Thread.hpp" -#include "raul/log.hpp" - -#include "engine/AudioBuffer.hpp" -#include "engine/Driver.hpp" -#include "engine/Engine.hpp" -#include "engine/PatchImpl.hpp" -#include "engine/PostProcessor.hpp" -#include "engine/ProcessContext.hpp" -#include "engine/QueuedEngineInterface.hpp" -#include "engine/ThreadManager.hpp" -#include "ingen/EngineInterface.hpp" -#include "shared/World.hpp" -#include "serialisation/Parser.hpp" -#include "shared/Configuration.hpp" -#include "shared/runtime_paths.hpp" - -#include "ingen-config.h" - -/** Record of a patch in this Ingen LV2 bundle */ -struct LV2Patch { - LV2Patch(const std::string& u, const std::string& f); - - const std::string uri; - const std::string filename; - LV2_Descriptor descriptor; -}; - -class Lib { -public: - Lib(); - ~Lib(); - - typedef std::vector< SharedPtr > Patches; - - Patches patches; - Ingen::Shared::Configuration conf; - int argc; - char** argv; -}; - -/** Library state (constructed/destructed on library load/unload) */ -Lib lib; - -namespace Ingen { -namespace Engine { - -class LV2Driver; - -class LV2Port : public DriverPort -{ -public: - LV2Port(LV2Driver* driver, DuplexPort* patch_port) - : DriverPort(patch_port) - , _driver(driver) - , _buffer(NULL) - {} - - // TODO: LV2 dynamic ports - void create() {} - void destroy() {} - void move(const Raul::Path& path) {} - - void pre_process(ProcessContext& context) { - if (!is_input() || !_buffer) - return; - - if (_patch_port->buffer_type() == PortType::AUDIO) { - AudioBuffer* patch_buf = (AudioBuffer*)_patch_port->buffer(0).get(); - patch_buf->copy((Sample*)_buffer, 0, context.nframes() - 1); - } else if (_patch_port->buffer_type() == PortType::EVENTS) { - //Raul::warn << "TODO: LV2 event I/O" << std::endl; - } - } - - void post_process(ProcessContext& context) { - if (is_input() || !_buffer) - return; - - if (_patch_port->buffer_type() == PortType::AUDIO) { - AudioBuffer* patch_buf = (AudioBuffer*)_patch_port->buffer(0).get(); - memcpy((Sample*)_buffer, patch_buf->data(), context.nframes() * sizeof(Sample)); - } else if (_patch_port->buffer_type() == PortType::EVENTS) { - //Raul::warn << "TODO: LV2 event I/O" << std::endl; - } - } - - void* buffer() const { return _buffer; } - void set_buffer(void* buf) { _buffer = buf; } - -private: - LV2Driver* _driver; - void* _buffer; -}; - -class LV2Driver : public Ingen::Engine::Driver { -private: - typedef std::vector Ports; - -public: - LV2Driver(Engine& engine, SampleCount buffer_size, SampleCount sample_rate) - : _context(engine) - , _root_patch(NULL) - , _buffer_size(buffer_size) - , _sample_rate(sample_rate) - , _frame_time(0) - {} - - void run(uint32_t nframes) { - _context.locate(_frame_time, nframes, 0); - - for (Ports::iterator i = _ports.begin(); i != _ports.end(); ++i) - (*i)->pre_process(_context); - - _context.engine().process_events(_context); - - if (_root_patch) - _root_patch->process(_context); - - for (Ports::iterator i = _ports.begin(); i != _ports.end(); ++i) - (*i)->post_process(_context); - - _frame_time += nframes; - } - - virtual void set_root_patch(PatchImpl* patch) { _root_patch = patch; } - virtual PatchImpl* root_patch() { return _root_patch; } - - virtual void add_port(DriverPort* port) { - // Note this doesn't have to be realtime safe since there's no dynamic LV2 ports - ThreadManager::assert_thread(THREAD_PROCESS); - assert(dynamic_cast(port)); - assert(port->patch_port()->index() == _ports.size()); - _ports.push_back((LV2Port*)port); - } - - virtual Raul::Deletable* remove_port(const Raul::Path& path, - DriverPort** port=NULL) { - // Note this doesn't have to be realtime safe since there's no dynamic LV2 ports - ThreadManager::assert_thread(THREAD_PROCESS); - - for (Ports::iterator i = _ports.begin(); i != _ports.end(); ++i) { - if ((*i)->patch_port()->path() == path) { - _ports.erase(i); - return NULL; - } - } - - Raul::warn << "Unable to find port " << path << std::endl; - return NULL; - } - - virtual bool supports(PortType port_type, EventType event_type) { - return true; - } - - virtual DriverPort* create_port(DuplexPort* patch_port) { - return new LV2Port(this, patch_port); - } - - virtual DriverPort* driver_port(const Raul::Path& path) { - ThreadManager::assert_thread(THREAD_PROCESS); - - for (Ports::iterator i = _ports.begin(); i != _ports.end(); ++i) - if ((*i)->patch_port()->path() == path) - return (*i); - - return NULL; - } - - virtual SampleCount block_length() const { return _buffer_size; } - virtual SampleCount sample_rate() const { return _sample_rate; } - virtual SampleCount frame_time() const { return _frame_time;} - - virtual bool is_realtime() const { return true; } - virtual ProcessContext& context() { return _context; } - - Ports& ports() { return _ports; } - -private: - ProcessContext _context; - PatchImpl* _root_patch; - SampleCount _buffer_size; - SampleCount _sample_rate; - SampleCount _frame_time; - Ports _ports; -}; - -} // namespace Engine -} // namespace Ingen - -extern "C" { - -using namespace Ingen; -using namespace Ingen::Engine; - -/** LV2 plugin library entry point */ -LV2_SYMBOL_EXPORT -const LV2_Descriptor* -lv2_descriptor(uint32_t index) -{ - return index < lib.patches.size() ? &lib.patches[index]->descriptor : NULL; -} - -struct IngenPlugin { - Ingen::Shared::World* world; -}; - -static LV2_Handle -ingen_instantiate(const LV2_Descriptor* descriptor, - double rate, - const char* bundle_path, - const LV2_Feature*const* features) -{ - Shared::set_bundle_path(bundle_path); - - const LV2Patch* patch = NULL; - for (Lib::Patches::iterator i = lib.patches.begin(); i != lib.patches.end(); ++i) { - if (&(*i)->descriptor == descriptor) { - patch = (*i).get(); - break; - } - } - - if (!patch) { - Raul::error << "Could not find patch " << descriptor->URI << std::endl; - return NULL; - } - - IngenPlugin* plugin = (IngenPlugin*)malloc(sizeof(IngenPlugin)); - plugin->world = new Ingen::Shared::World(&lib.conf, lib.argc, lib.argv); - if (!plugin->world->load_module("serialisation")) { - delete plugin->world; - return NULL; - } - - SharedPtr engine(new Engine::Engine(plugin->world)); - plugin->world->set_local_engine(engine); - - SharedPtr interface( - new Engine::QueuedEngineInterface( - *engine.get(), - engine->event_queue_size())); - - plugin->world->set_engine(interface); - engine->add_event_source(interface); - - Raul::Thread::get().set_context(Engine::THREAD_PRE_PROCESS); - Engine::ThreadManager::single_threaded = true; - - // FIXME: fixed (or at least maximum) buffer size - LV2Driver* driver = new LV2Driver(*engine.get(), rate, 4096); - engine->set_driver(SharedPtr(driver)); - - engine->activate(); - Engine::ThreadManager::single_threaded = true; - - ProcessContext context(*engine.get()); - context.locate(0, UINT_MAX, 0); - - engine->post_processor()->set_end_time(UINT_MAX); - - // TODO: Load only necessary plugins - plugin->world->engine()->get("ingen:plugins"); - interface->process(*engine->post_processor(), context, false); - engine->post_processor()->process(); - - plugin->world->parser()->parse_file(plugin->world, - plugin->world->engine().get(), - patch->filename); - - while (!interface->empty()) { - interface->process(*engine->post_processor(), context, false); - engine->post_processor()->process(); - } - - engine->deactivate(); - - return (LV2_Handle)plugin; -} - -static void -ingen_connect_port(LV2_Handle instance, uint32_t port, void* data) -{ - using namespace Ingen::Engine; - - IngenPlugin* me = (IngenPlugin*)instance; - Engine::Engine* engine = (Engine::Engine*)me->world->local_engine().get(); - LV2Driver* driver = (LV2Driver*)engine->driver(); - if (port < driver->ports().size()) { - driver->ports().at(port)->set_buffer(data); - assert(driver->ports().at(port)->patch_port()->index() == port); - } else { - Raul::warn << "Connect to non-existent port " << port << std::endl; - } -} - -static void -ingen_activate(LV2_Handle instance) -{ - IngenPlugin* me = (IngenPlugin*)instance; - me->world->local_engine()->activate(); -} - -static void -ingen_run(LV2_Handle instance, uint32_t sample_count) -{ - IngenPlugin* me = (IngenPlugin*)instance; - Engine::Engine* engine = (Engine::Engine*)me->world->local_engine().get(); - // FIXME: don't do this every call - Raul::Thread::get().set_context(Ingen::Engine::THREAD_PROCESS); - ((LV2Driver*)engine->driver())->run(sample_count); -} - -static void -ingen_deactivate(LV2_Handle instance) -{ - IngenPlugin* me = (IngenPlugin*)instance; - me->world->local_engine()->deactivate(); -} - -static void -ingen_cleanup(LV2_Handle instance) -{ - IngenPlugin* me = (IngenPlugin*)instance; - me->world->set_local_engine(SharedPtr()); - me->world->set_engine(SharedPtr()); - delete me->world; - free(instance); -} - -static const void* -ingen_extension_data(const char* uri) -{ - return NULL; -} - -LV2Patch::LV2Patch(const std::string& u, const std::string& f) - : uri(u) - , filename(f) -{ - descriptor.URI = uri.c_str(); - descriptor.instantiate = ingen_instantiate; - descriptor.connect_port = ingen_connect_port; - descriptor.activate = ingen_activate; - descriptor.run = ingen_run; - descriptor.deactivate = ingen_deactivate; - descriptor.cleanup = ingen_cleanup; - descriptor.extension_data = ingen_extension_data; -} - -/** Library constructor (called on shared library load) */ -Lib::Lib() - : argc(0) - , argv(NULL) -{ - if (!Glib::thread_supported()) - Glib::thread_init(); - - using namespace Ingen; - - Ingen::Shared::set_bundle_path_from_code((void*)&lv2_descriptor); - - Ingen::Shared::World* world = new Ingen::Shared::World(&conf, argc, argv); - if (!world->load_module("serialisation")) { - delete world; - return; - } - - assert(world->parser()); - - typedef Serialisation::Parser::PatchRecords Records; - - Records records(world->parser()->find_patches( - world, Glib::filename_to_uri( - Shared::bundle_file_path("manifest.ttl")))); - - for (Records::iterator i = records.begin(); i != records.end(); ++i) { - patches.push_back( - SharedPtr(new LV2Patch(i->patch_uri.str(), - i->file_uri))); - } - - delete world; -} - -/** Library destructor (called on shared library unload) */ -Lib::~Lib() -{ -} - -} // extern "C" diff --git a/src/engine/ingen_osc.cpp b/src/engine/ingen_osc.cpp deleted file mode 100644 index 252d984e..00000000 --- a/src/engine/ingen_osc.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "shared/Module.hpp" -#include "shared/World.hpp" -#include "OSCEngineReceiver.hpp" -#include "Engine.hpp" - -using namespace std; -using namespace Ingen; - -struct IngenOSCModule : public Ingen::Shared::Module { - void load(Ingen::Shared::World* world) { - Engine::Engine* engine = (Engine::Engine*)world->local_engine().get(); - SharedPtr interface( - new Engine::OSCEngineReceiver( - *engine, - engine->event_queue_size(), - world->conf()->option("engine-port").get_int32())); - engine->add_event_source(interface); - } -}; - -extern "C" { - -Ingen::Shared::Module* -ingen_module_load() -{ - return new IngenOSCModule(); -} - -} // extern "C" diff --git a/src/engine/internals/Controller.cpp b/src/engine/internals/Controller.cpp deleted file mode 100644 index 15156f08..00000000 --- a/src/engine/internals/Controller.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "raul/midi_events.h" -#include "shared/LV2URIMap.hpp" -#include "internals/Controller.hpp" -#include "PostProcessor.hpp" -#include "events/SendPortValue.hpp" -#include "InputPort.hpp" -#include "OutputPort.hpp" -#include "InternalPlugin.hpp" -#include "AudioBuffer.hpp" -#include "ProcessContext.hpp" -#include "EventBuffer.hpp" -#include "util.hpp" - -using namespace std; - -namespace Ingen { -namespace Engine { -namespace Internals { - -InternalPlugin* ControllerNode::internal_plugin(Shared::LV2URIMap& uris) { - return new InternalPlugin(uris, NS_INTERNALS "Controller", "controller"); -} - -ControllerNode::ControllerNode(InternalPlugin* plugin, - BufferFactory& bufs, - const string& path, - bool polyphonic, - PatchImpl* parent, - SampleRate srate) - : NodeImpl(plugin, path, false, parent, srate) - , _learning(false) -{ - const Ingen::Shared::LV2URIMap& uris = bufs.uris(); - _ports = new Raul::Array(6); - - _midi_in_port = new InputPort(bufs, this, "input", 0, 1, PortType::EVENTS, Raul::Atom()); - _midi_in_port->set_property(uris.lv2_name, "Input"); - _ports->at(0) = _midi_in_port; - - _param_port = new InputPort(bufs, this, "controller", 1, 1, PortType::CONTROL, 0.0f); - _param_port->set_property(uris.lv2_minimum, 0.0f); - _param_port->set_property(uris.lv2_maximum, 127.0f); - _param_port->set_property(uris.lv2_integer, true); - _param_port->set_property(uris.lv2_name, "Controller"); - _ports->at(1) = _param_port; - - _log_port = new InputPort(bufs, this, "logarithmic", 2, 1, PortType::CONTROL, 0.0f); - _log_port->set_property(uris.lv2_portProperty, uris.lv2_toggled); - _log_port->set_property(uris.lv2_name, "Logarithmic"); - _ports->at(2) = _log_port; - - _min_port = new InputPort(bufs, this, "minimum", 3, 1, PortType::CONTROL, 0.0f); - _min_port->set_property(uris.lv2_name, "Minimum"); - _ports->at(3) = _min_port; - - _max_port = new InputPort(bufs, this, "maximum", 4, 1, PortType::CONTROL, 1.0f); - _max_port->set_property(uris.lv2_name, "Maximum"); - _ports->at(4) = _max_port; - - _audio_port = new OutputPort(bufs, this, "ar_output", 5, 1, PortType::AUDIO, 0.0f); - _audio_port->set_property(uris.lv2_name, "Output"); - _ports->at(5) = _audio_port; -} - -void -ControllerNode::process(ProcessContext& context) -{ - NodeImpl::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).get(); - - midi_in->rewind(); - - 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(); - } - - NodeImpl::post_process(context); -} - -void -ControllerNode::control(ProcessContext& context, uint8_t control_num, uint8_t val, FrameTime time) -{ - Sample scaled_value; - - const Sample nval = (val / 127.0f); // normalized [0, 1] - - if (_learning) { - _param_port->set_value(control_num); - ((AudioBuffer*)_param_port->buffer(0).get())->set_value( - (float)control_num, context.start(), context.end()); - _param_port->broadcast_value(context, true); - _learning = false; - } - - const Sample min_port_val = ((AudioBuffer*)_min_port->buffer(0).get())->value_at(0); - const Sample max_port_val = ((AudioBuffer*)_max_port->buffer(0).get())->value_at(0); - const Sample log_port_val = ((AudioBuffer*)_log_port->buffer(0).get())->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).get())->value_at(0)) - ((AudioBuffer*)_audio_port->buffer(0).get())->set_value(scaled_value, context.start(), time); -} - -} // namespace Internals -} // namespace Engine -} // namespace Ingen - diff --git a/src/engine/internals/Controller.hpp b/src/engine/internals/Controller.hpp deleted file mode 100644 index 54b1d3ac..00000000 --- a/src/engine/internals/Controller.hpp +++ /dev/null @@ -1,74 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_INTERNALS_CONTROLLER_HPP -#define INGEN_INTERNALS_CONTROLLER_HPP - -#include -#include "NodeImpl.hpp" - -namespace Ingen { -namespace Engine { - -class InputPort; -class OutputPort; -class InternalPlugin; - -namespace Internals { - -/** 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 ControllerNode : public NodeImpl -{ -public: - ControllerNode( - InternalPlugin* plugin, - BufferFactory& bufs, - const std::string& path, - bool polyphonic, - PatchImpl* parent, - SampleRate srate); - - void process(ProcessContext& context); - - void control(ProcessContext& context, uint8_t control_num, uint8_t val, FrameTime time); - - void learn() { _learning = true; } - - static InternalPlugin* internal_plugin(Shared::LV2URIMap& uris); - -private: - bool _learning; - - InputPort* _midi_in_port; - InputPort* _param_port; - InputPort* _log_port; - InputPort* _min_port; - InputPort* _max_port; - OutputPort* _audio_port; -}; - -} // namespace Engine -} // namespace Ingen -} // namespace Internals - -#endif // INGEN_INTERNALS_CONTROLLER_HPP diff --git a/src/engine/internals/Delay.cpp b/src/engine/internals/Delay.cpp deleted file mode 100644 index bded108a..00000000 --- a/src/engine/internals/Delay.cpp +++ /dev/null @@ -1,208 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "raul/log.hpp" -#include "raul/Array.hpp" -#include "raul/Maid.hpp" -#include "raul/midi_events.h" -#include "shared/LV2URIMap.hpp" -#include "internals/Delay.hpp" -#include "AudioBuffer.hpp" -#include "Driver.hpp" -#include "EventBuffer.hpp" -#include "InputPort.hpp" -#include "InternalPlugin.hpp" -#include "OutputPort.hpp" -#include "PatchImpl.hpp" -#include "ProcessContext.hpp" -#include "ingen-config.h" -#include "util.hpp" - -#define LOG(s) s << "[DelayNode] " - -#define CALC_DELAY(delaytime) \ - (f_clamp (delaytime * (float)sample_rate, 1.0f, (float)(buffer_mask + 1))) - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { -namespace Internals { - -static const float MAX_DELAY_SECONDS = 8.0f; - -InternalPlugin* DelayNode::internal_plugin(Shared::LV2URIMap& uris) { - return new InternalPlugin(uris, NS_INTERNALS "Delay", "delay"); -} - -DelayNode::DelayNode( - InternalPlugin* plugin, - BufferFactory& bufs, - const std::string& path, - bool polyphonic, - PatchImpl* parent, - SampleRate srate) - : NodeImpl(plugin, path, polyphonic, parent, srate) - , _buffer(0) - , _buffer_length(0) - , _buffer_mask(0) - , _write_phase(0) -{ - const Ingen::Shared::LV2URIMap& uris = bufs.uris(); - _ports = new Raul::Array(3); - - const float default_delay = 1.0f; - _last_delay_time = default_delay; - _delay_samples = default_delay; - - _delay_port = new InputPort(bufs, this, "delay", 1, _polyphony, PortType::CONTROL, default_delay); - _delay_port->set_property(uris.lv2_name, "Delay"); - _delay_port->set_property(uris.lv2_default, default_delay); - _delay_port->set_property(uris.lv2_minimum, (float)(1.0/(double)srate)); - _delay_port->set_property(uris.lv2_maximum, MAX_DELAY_SECONDS); - _ports->at(0) = _delay_port; - - _in_port = new InputPort(bufs, this, "in", 0, 1, PortType::AUDIO, 0.0f); - _in_port->set_property(uris.lv2_name, "Input"); - _ports->at(1) = _in_port; - - _out_port = new OutputPort(bufs, this, "out", 0, 1, PortType::AUDIO, 0.0f); - _out_port->set_property(uris.lv2_name, "Output"); - _ports->at(2) = _out_port; - - //_buffer = bufs.get(PortType::AUDIO, bufs.audio_buffer_size(buffer_length_frames), true); - -} - -DelayNode::~DelayNode() -{ - //_buffer.reset(); - free(_buffer); -} - -void -DelayNode::activate(BufferFactory& bufs) -{ - NodeImpl::activate(bufs); - const SampleCount min_size = MAX_DELAY_SECONDS * _srate; - - // Smallest power of two larger than min_size - SampleCount size = 1; - while (size < min_size) - size <<= 1; - - _buffer = (float*)calloc(size, sizeof(float)); - _buffer_mask = size - 1; - _buffer_length = size; - //_buffer->clear(); - _write_phase = 0; -} - -static inline float f_clamp(float x, float a, float b) -{ - const float x1 = fabs(x - a); - const float x2 = fabs(x - b); - - x = x1 + a + b; - x -= x2; - x *= 0.5; - - return x; -} - -static inline float cube_interp(const float fr, const float inm1, const float - in, const float inp1, const float inp2) -{ - return in + 0.5f * fr * (inp1 - inm1 + - fr * (4.0f * inp1 + 2.0f * inm1 - 5.0f * in - inp2 + - fr * (3.0f * (in - inp1) - inm1 + inp2))); -} - -void -DelayNode::process(ProcessContext& context) -{ - AudioBuffer* const delay_buf = (AudioBuffer*)_delay_port->buffer(0).get(); - AudioBuffer* const in_buf = (AudioBuffer*)_in_port->buffer(0).get(); - AudioBuffer* const out_buf = (AudioBuffer*)_out_port->buffer(0).get(); - - NodeImpl::pre_process(context); - - DelayNode* plugin_data = this; - - const float* const in = in_buf->data(); - float* const out = out_buf->data(); - const float delay_time = delay_buf->data()[0]; - const uint32_t buffer_mask = plugin_data->_buffer_mask; - const unsigned int sample_rate = plugin_data->_srate; - float delay_samples = plugin_data->_delay_samples; - long write_phase = plugin_data->_write_phase; - const uint32_t sample_count = context.nframes(); - - if (write_phase == 0) { - _last_delay_time = delay_time; - _delay_samples = delay_samples = CALC_DELAY(delay_time); - } - - if (delay_time == _last_delay_time) { - const long idelay_samples = (long)delay_samples; - const float frac = delay_samples - idelay_samples; - - for (uint32_t i = 0; i < sample_count; i++) { - long read_phase = write_phase - (long)delay_samples; - const float read = cube_interp(frac, - buffer_at(read_phase - 1), - buffer_at(read_phase), - buffer_at(read_phase + 1), - buffer_at(read_phase + 2)); - buffer_at(write_phase++) = in[i]; - out[i] = read; - } - } else { - const float next_delay_samples = CALC_DELAY(delay_time); - const float delay_samples_slope = (next_delay_samples - delay_samples) / sample_count; - - for (uint32_t i = 0; i < sample_count; i++) { - delay_samples += delay_samples_slope; - write_phase++; - const long read_phase = write_phase - (long)delay_samples; - const long idelay_samples = (long)delay_samples; - const float frac = delay_samples - idelay_samples; - const float read = cube_interp(frac, - buffer_at(read_phase - 1), - buffer_at(read_phase), - buffer_at(read_phase + 1), - buffer_at(read_phase + 2)); - buffer_at(write_phase) = in[i]; - out[i] = read; - } - - _last_delay_time = delay_time; - _delay_samples = delay_samples; - } - - _write_phase = write_phase; - - NodeImpl::post_process(context); -} - -} // namespace Internals -} // namespace Engine -} // namespace Ingen - diff --git a/src/engine/internals/Delay.hpp b/src/engine/internals/Delay.hpp deleted file mode 100644 index bc6f2682..00000000 --- a/src/engine/internals/Delay.hpp +++ /dev/null @@ -1,78 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_INTERNALS_DELAY_HPP -#define INGEN_INTERNALS_DELAY_HPP - -#include -#include -#include "types.hpp" -#include "NodeImpl.hpp" - -namespace Ingen { -namespace Engine { - -class InputPort; -class OutputPort; -class InternalPlugin; -class BufferFactory; - -namespace Internals { - -class DelayNode : public NodeImpl -{ -public: - DelayNode( - InternalPlugin* plugin, - BufferFactory& bufs, - const std::string& path, - bool polyphonic, - PatchImpl* parent, - SampleRate srate); - - ~DelayNode(); - - void activate(BufferFactory& bufs); - - void process(ProcessContext& context); - - static InternalPlugin* internal_plugin(Shared::LV2URIMap& uris); - - float delay_samples() const { return _delay_samples; } - -private: - inline float& buffer_at(long phase) const { return _buffer[phase & _buffer_mask]; } - - InputPort* _delay_port; - InputPort* _in_port; - OutputPort* _out_port; - - typedef long Phase; - - float* _buffer; - uint32_t _buffer_length; - uint32_t _buffer_mask; - Phase _write_phase; - float _last_delay_time; - float _delay_samples; -}; - -} // namespace Engine -} // namespace Ingen -} // namespace Internals - -#endif // INGEN_INTERNALS_DELAY_HPP diff --git a/src/engine/internals/Note.cpp b/src/engine/internals/Note.cpp deleted file mode 100644 index 8545857e..00000000 --- a/src/engine/internals/Note.cpp +++ /dev/null @@ -1,416 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "raul/log.hpp" -#include "raul/Array.hpp" -#include "raul/Maid.hpp" -#include "raul/midi_events.h" -#include "shared/LV2URIMap.hpp" -#include "internals/Note.hpp" -#include "AudioBuffer.hpp" -#include "Driver.hpp" -#include "EventBuffer.hpp" -#include "InputPort.hpp" -#include "InternalPlugin.hpp" -#include "OutputPort.hpp" -#include "PatchImpl.hpp" -#include "ProcessContext.hpp" -#include "util.hpp" -#include "ingen-config.h" - -#define LOG(s) s << "[NoteNode] " - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { -namespace Internals { - -InternalPlugin* NoteNode::internal_plugin(Shared::LV2URIMap& uris) { - return new InternalPlugin(uris, NS_INTERNALS "Note", "note"); -} - -NoteNode::NoteNode( - InternalPlugin* plugin, - BufferFactory& bufs, - const std::string& path, - bool polyphonic, - PatchImpl* parent, - SampleRate srate) - : NodeImpl(plugin, path, polyphonic, parent, srate) - , _voices(new Raul::Array(_polyphony)) - , _prepared_voices(NULL) - , _sustain(false) -{ - const Ingen::Shared::LV2URIMap& uris = bufs.uris(); - _ports = new Raul::Array(5); - - _midi_in_port = new InputPort(bufs, this, "input", 0, 1, PortType::EVENTS, Raul::Atom()); - _midi_in_port->set_property(uris.lv2_name, "Input"); - _ports->at(0) = _midi_in_port; - - _freq_port = new OutputPort(bufs, this, "frequency", 1, _polyphony, PortType::AUDIO, 440.0f); - _freq_port->set_property(uris.lv2_name, "Frequency"); - _ports->at(1) = _freq_port; - - _vel_port = new OutputPort(bufs, this, "velocity", 2, _polyphony, PortType::AUDIO, 0.0f); - _vel_port->set_property(uris.lv2_minimum, 0.0f); - _vel_port->set_property(uris.lv2_maximum, 1.0f); - _vel_port->set_property(uris.lv2_name, "Velocity"); - _ports->at(2) = _vel_port; - - _gate_port = new OutputPort(bufs, this, "gate", 3, _polyphony, PortType::AUDIO, 0.0f); - _gate_port->set_property(uris.lv2_portProperty, uris.lv2_toggled); - _gate_port->set_property(uris.lv2_name, "Gate"); - _ports->at(3) = _gate_port; - - _trig_port = new OutputPort(bufs, this, "trigger", 4, _polyphony, PortType::AUDIO, 0.0f); - _trig_port->set_property(uris.lv2_portProperty, uris.lv2_toggled); - _trig_port->set_property(uris.lv2_name, "Trigger"); - _ports->at(4) = _trig_port; -} - -NoteNode::~NoteNode() -{ - delete _voices; -} - -bool -NoteNode::prepare_poly(BufferFactory& bufs, uint32_t poly) -{ - if (!_polyphonic) - return true; - - NodeImpl::prepare_poly(bufs, poly); - - if (_prepared_voices && poly <= _prepared_voices->size()) - return true; - - _prepared_voices = new Raul::Array(poly, *_voices, Voice()); - - return true; -} - -bool -NoteNode::apply_poly(Raul::Maid& maid, uint32_t poly) -{ - if (!NodeImpl::apply_poly(maid, poly)) - return false; - - if (_prepared_voices) { - assert(_polyphony <= _prepared_voices->size()); - maid.push(_voices); - _voices = _prepared_voices; - _prepared_voices = NULL; - } - assert(_polyphony <= _voices->size()); - - return true; -} - -void -NoteNode::process(ProcessContext& context) -{ - EventBuffer* const midi_in = (EventBuffer*)_midi_in_port->buffer(0).get(); - NodeImpl::pre_process(context); - - uint32_t frames = 0; - uint32_t subframes = 0; - uint16_t type = 0; - uint16_t size = 0; - uint8_t* buf = NULL; - - midi_in->rewind(); - - if (midi_in->event_count() > 0) - for (midi_in->rewind(); midi_in->get_event(&frames, &subframes, &type, &size, &buf); - midi_in->increment()) { - -#ifdef RAUL_LOG_DEBUG - LOG(debug) << "EVENT TYPE " << type << " @ " << frames << "." << subframes << ": "; - for (uint16_t i = 0; i < size; ++i) - debug << (int)((char)buf[i]) << " "; - debug << endl; -#endif - - if (frames < context.offset()) - continue; - if (frames > context.nframes()) - break; - - 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: - //warn << "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]); - } - } - - NodeImpl::post_process(context); -} - -void -NoteNode::note_on(ProcessContext& context, uint8_t note_num, uint8_t velocity, FrameTime time) -{ - assert(time >= context.start() && time <= context.end()); - assert(note_num <= 127); - - Key* key = &_keys[note_num]; - Voice* voice = NULL; - uint32_t voice_num = 0; - - if (key->state != Key::OFF) { -#ifdef RAUL_LOG_DEBUG - LOG(debug) << "Double midi note received" << endl; -#endif - 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]; - FrameTime 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]); - -#ifdef RAUL_LOG_DEBUG - LOG(debug) << "Note " << (int)note_num << " on @ " << time - << ". Voice " << voice_num << " / " << _polyphony << endl; -#endif - - // 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; -#ifdef RAUL_LOG_DEBUG - LOG(debug) << "Stole voice " << voice_num << endl; -#endif - } - - // 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).get())->set_value( - note_to_freq(note_num), context.start(), time); - ((AudioBuffer*)_vel_port->buffer(voice_num).get())->set_value( - velocity/127.0, context.start(), time); - ((AudioBuffer*)_gate_port->buffer(voice_num).get())->set_value( - 1.0f, context.start(), time); - - // trigger (one sample) - ((AudioBuffer*)_trig_port->buffer(voice_num).get())->set_value( - 1.0f, context.start(), time); - ((AudioBuffer*)_trig_port->buffer(voice_num).get())->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 -NoteNode::note_off(ProcessContext& context, uint8_t note_num, FrameTime time) -{ - assert(time >= context.start() && time <= context.end()); - - Key* key = &_keys[note_num]; - -#ifdef RAUL_LOG_DEBUG - debug << "Note " << (int)note_num << " off @ " << time << endl; -#endif - - 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) { -#ifdef RAUL_LOG_DEBUG - debug << "Free voice " << key->voice << endl; -#endif - free_voice(context, key->voice, time); - } else { -#ifdef RAUL_LOG_DEBUG - debug << "Hold voice " << key->voice << endl; -#endif - (*_voices)[key->voice].state = Voice::HOLDING; - } - - } else { -#ifdef RAUL_LOG_DEBUG - debug << "WARNING: Assigned key, but voice not active" << endl; -#endif - } - } - - key->state = Key::OFF; -} - -void -NoteNode::free_voice(ProcessContext& context, uint32_t voice, FrameTime time) -{ - assert(time >= context.start() && time <= context.end()); - - // Find a key to reassign to the freed voice (the newest, if there is one) - Key* replace_key = NULL; - uint8_t replace_key_num = 0; - - for (uint8_t 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).get())->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) -#ifdef RAUL_LOG_DEBUG - LOG(debug) << "Note off: key " << (*_voices)[voice].note << " voice " << voice << endl; -#endif - ((AudioBuffer*)_gate_port->buffer(voice).get())->set_value(0.0f, context.start(), time); - (*_voices)[voice].state = Voice::FREE; - } -} - -void -NoteNode::all_notes_off(ProcessContext& context, FrameTime time) -{ - assert(time >= context.start() && time <= context.end()); - -#ifdef RAUL_LOG_DEBUG - LOG(debug) << "All notes off @ " << time << endl; -#endif - - // FIXME: set all keys to Key::OFF? - - for (uint32_t i = 0; i < _polyphony; ++i) { - ((AudioBuffer*)_gate_port->buffer(i).get())->set_value(0.0f, context.start(), time); - (*_voices)[i].state = Voice::FREE; - } -} - -float -NoteNode::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; // Frequency of zero causes numerical problems... -} - -void -NoteNode::sustain_on(ProcessContext& context, FrameTime time) -{ - _sustain = true; -} - -void -NoteNode::sustain_off(ProcessContext& context, FrameTime time) -{ - assert(time >= context.start() && time <= context.end()); - - _sustain = false; - - for (uint32_t i=0; i < _polyphony; ++i) - if ((*_voices)[i].state == Voice::HOLDING) - free_voice(context, i, time); -} - -} // namespace Internals -} // namespace Engine -} // namespace Ingen - diff --git a/src/engine/internals/Note.hpp b/src/engine/internals/Note.hpp deleted file mode 100644 index 7a47dc76..00000000 --- a/src/engine/internals/Note.hpp +++ /dev/null @@ -1,101 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_INTERNALS_NOTE_HPP -#define INGEN_INTERNALS_NOTE_HPP - -#include -#include "types.hpp" -#include "NodeImpl.hpp" - -namespace Ingen { -namespace Engine { - -class InputPort; -class OutputPort; -class InternalPlugin; - -namespace Internals { - -/** MIDI note input node. - * - * For pitched instruments like keyboard, etc. - * - * \ingroup engine - */ -class NoteNode : public NodeImpl -{ -public: - NoteNode( - InternalPlugin* plugin, - BufferFactory& bufs, - const std::string& path, - bool polyphonic, - PatchImpl* parent, - SampleRate srate); - - ~NoteNode(); - - bool prepare_poly(BufferFactory& bufs, uint32_t poly); - bool apply_poly(Raul::Maid& maid, uint32_t poly); - - void process(ProcessContext& context); - - void note_on(ProcessContext& context, uint8_t note_num, uint8_t velocity, FrameTime time); - void note_off(ProcessContext& context, uint8_t 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); - - static InternalPlugin* internal_plugin(Shared::LV2URIMap& uris); - -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), time(0) {} - State state; uint8_t 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 Engine -} // namespace Ingen -} // namespace Internals - -#endif // INGEN_INTERNALS_NOTE_HPP diff --git a/src/engine/internals/Trigger.cpp b/src/engine/internals/Trigger.cpp deleted file mode 100644 index 55474618..00000000 --- a/src/engine/internals/Trigger.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public 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 "raul/log.hpp" -#include "raul/midi_events.h" -#include "shared/LV2URIMap.hpp" -#include "internals/Trigger.hpp" -#include "AudioBuffer.hpp" -#include "EventBuffer.hpp" -#include "InputPort.hpp" -#include "InternalPlugin.hpp" -#include "OutputPort.hpp" -#include "ProcessContext.hpp" -#include "util.hpp" -#include "ingen-config.h" - -#define LOG(s) s << "[TriggerNode] " - -using namespace std; -using namespace Raul; - -namespace Ingen { -namespace Engine { -namespace Internals { - -InternalPlugin* TriggerNode::internal_plugin(Shared::LV2URIMap& uris) { - return new InternalPlugin(uris, NS_INTERNALS "Trigger", "trigger"); -} - -TriggerNode::TriggerNode( - InternalPlugin* plugin, - BufferFactory& bufs, - const std::string& path, - bool polyphonic, - PatchImpl* parent, - SampleRate srate) - : NodeImpl(plugin, path, false, parent, srate) - , _learning(false) -{ - const Ingen::Shared::LV2URIMap& uris = bufs.uris(); - _ports = new Raul::Array(5); - - _midi_in_port = new InputPort(bufs, this, "input", 0, 1, PortType::EVENTS, Raul::Atom()); - _midi_in_port->set_property(uris.lv2_name, "Input"); - _ports->at(0) = _midi_in_port; - - _note_port = new InputPort(bufs, this, "note", 1, 1, PortType::CONTROL, 60.0f); - _note_port->set_property(uris.lv2_minimum, 0.0f); - _note_port->set_property(uris.lv2_maximum, 127.0f); - _note_port->set_property(uris.lv2_integer, true); - _note_port->set_property(uris.lv2_name, "Note"); - _ports->at(1) = _note_port; - - _gate_port = new OutputPort(bufs, this, "gate", 2, 1, PortType::AUDIO, 0.0f); - _gate_port->set_property(uris.lv2_portProperty, uris.lv2_toggled); - _gate_port->set_property(uris.lv2_name, "Gate"); - _ports->at(2) = _gate_port; - - _trig_port = new OutputPort(bufs, this, "trigger", 3, 1, PortType::AUDIO, 0.0f); - _trig_port->set_property(uris.lv2_portProperty, uris.lv2_toggled); - _trig_port->set_property(uris.lv2_name, "Trigger"); - _ports->at(3) = _trig_port; - - _vel_port = new OutputPort(bufs, this, "velocity", 4, 1, PortType::AUDIO, 0.0f); - _vel_port->set_property(uris.lv2_minimum, 0.0f); - _vel_port->set_property(uris.lv2_maximum, 1.0f); - _vel_port->set_property(uris.lv2_name, "Velocity"); - _ports->at(4) = _vel_port; -} - -void -TriggerNode::process(ProcessContext& context) -{ - NodeImpl::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).get(); - - midi_in->rewind(); - - 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).get())->set_value(0.0f, context.start(), time); - default: - break; - } - } - - midi_in->increment(); - } - - NodeImpl::post_process(context); -} - -void -TriggerNode::note_on(ProcessContext& context, uint8_t note_num, uint8_t velocity, FrameTime time) -{ - assert(time >= context.start() && time <= context.end()); - - if (_learning) { - _note_port->set_value(note_num); - ((AudioBuffer*)_note_port->buffer(0).get())->set_value( - (float)note_num, context.start(), context.end()); - _note_port->broadcast_value(context, true); - _learning = false; - } - -#ifdef RAUL_LOG_DEBUG - LOG(debug) << path() << " note " << (int)note_num << " on @ " << time << endl; -#endif - - Sample filter_note = ((AudioBuffer*)_note_port->buffer(0).get())->value_at(0); - if (filter_note >= 0.0 && filter_note < 127.0 && (note_num == (uint8_t)filter_note)) { - ((AudioBuffer*)_gate_port->buffer(0).get())->set_value(1.0f, context.start(), time); - ((AudioBuffer*)_trig_port->buffer(0).get())->set_value(1.0f, context.start(), time); - ((AudioBuffer*)_trig_port->buffer(0).get())->set_value(0.0f, context.start(), time + 1); - ((AudioBuffer*)_vel_port->buffer(0).get())->set_value(velocity / 127.0f, context.start(), time); - assert(((AudioBuffer*)_trig_port->buffer(0).get())->data()[time - context.start()] == 1.0f); - } -} - -void -TriggerNode::note_off(ProcessContext& context, uint8_t note_num, FrameTime time) -{ - assert(time >= context.start() && time <= context.end()); - - if (note_num == lrintf(((AudioBuffer*)_note_port->buffer(0).get())->value_at(0))) - ((AudioBuffer*)_gate_port->buffer(0).get())->set_value(0.0f, context.start(), time); -} - -} // namespace Internals -} // namespace Engine -} // namespace Ingen diff --git a/src/engine/internals/Trigger.hpp b/src/engine/internals/Trigger.hpp deleted file mode 100644 index da796f5e..00000000 --- a/src/engine/internals/Trigger.hpp +++ /dev/null @@ -1,77 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_INTERNALS_TRIGGER_HPP -#define INGEN_INTERNALS_TRIGGER_HPP - -#include -#include "NodeImpl.hpp" - -namespace Ingen { -namespace Engine { - -class InputPort; -class OutputPort; -class InternalPlugin; - -namespace Internals { - -/** 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 TriggerNode : public NodeImpl -{ -public: - TriggerNode( - InternalPlugin* plugin, - BufferFactory& bufs, - const std::string& path, - bool polyphonic, - PatchImpl* parent, - SampleRate srate); - - void process(ProcessContext& context); - - void note_on(ProcessContext& context, uint8_t note_num, uint8_t velocity, FrameTime time); - void note_off(ProcessContext& context, uint8_t note_num, FrameTime time); - - void learn() { _learning = true; } - - static InternalPlugin* internal_plugin(Shared::LV2URIMap& uris); - -private: - bool _learning; - - InputPort* _midi_in_port; - InputPort* _note_port; - OutputPort* _gate_port; - OutputPort* _trig_port; - OutputPort* _vel_port; -}; - -} // namespace Engine -} // namespace Ingen -} // namespace Internals - -#endif // INGEN_INTERNALS_TRIGGER_HPP diff --git a/src/engine/mix.hpp b/src/engine/mix.hpp deleted file mode 100644 index 3797396b..00000000 --- a/src/engine/mix.hpp +++ /dev/null @@ -1,91 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_MIX_HPP -#define INGEN_ENGINE_MIX_HPP - -#include "raul/log.hpp" -#include "ingen/PortType.hpp" -#include "Buffer.hpp" -#include "Context.hpp" - -using namespace Raul; - -namespace Ingen { -namespace Engine { - -inline void -mix(Context& context, Buffer* dst, const IntrusivePtr* srcs, uint32_t num_srcs) -{ - using Ingen::PortType; - switch (dst->type().symbol()) { - case PortType::AUDIO: - case PortType::CONTROL: - // Copy the first source - dst->copy(context, srcs[0].get()); - - // Mix in the rest - for (uint32_t i = 1; i < num_srcs; ++i) { - assert(srcs[i]->type() == PortType::AUDIO || srcs[i]->type() == PortType::CONTROL); - ((AudioBuffer*)dst)->accumulate(context, (AudioBuffer*)srcs[i].get()); - } - - break; - - case PortType::EVENTS: - dst->clear(); - for (uint32_t i = 0; i < num_srcs; ++i) { - assert(srcs[i]->type() == PortType::EVENTS); - srcs[i]->rewind(); - } - - while (true) { - const EventBuffer* first = NULL; - for (uint32_t i = 0; i < num_srcs; ++i) { - const EventBuffer* const src = (const EventBuffer*)srcs[i].get(); - if (src->is_valid()) { - if (!first || src->get_event()->frames < first->get_event()->frames) - first = src; - } - } - if (first) { - const LV2_Event* const ev = first->get_event(); - ((EventBuffer*)dst)->append( - ev->frames, ev->subframes, ev->type, ev->size, - (const uint8_t*)ev + sizeof(LV2_Event)); - first->increment(); - } else { - break; - } - } - - dst->rewind(); - break; - - default: - if (num_srcs == 1) - dst->copy(context, srcs[0].get()); - else - error << "Mix of unsupported buffer types" << std::endl; - return; - } -} - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_MIX_HPP diff --git a/src/engine/types.hpp b/src/engine/types.hpp deleted file mode 100644 index 1c6b217e..00000000 --- a/src/engine/types.hpp +++ /dev/null @@ -1,29 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_TYPES_HPP -#define INGEN_ENGINE_TYPES_HPP - -#include -#include - -typedef float Sample; -typedef uint32_t SampleCount; -typedef uint32_t SampleRate; -typedef uint32_t FrameTime; - -#endif // INGEN_ENGINE_TYPES_HPP diff --git a/src/engine/util.hpp b/src/engine/util.hpp deleted file mode 100644 index fb9ba161..00000000 --- a/src/engine/util.hpp +++ /dev/null @@ -1,90 +0,0 @@ -/* This file is part of Ingen. - * Copyright 2007-2011 David Robillard - * - * Ingen is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. - * - * You should have received a copy of the GNU General Public License 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_UTIL_HPP -#define INGEN_ENGINE_UTIL_HPP - -#include -#include - -#include "raul/log.hpp" -#include "raul/Path.hpp" - -#include "ingen-config.h" - -#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 { -namespace Engine { - -/** 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 - Raul::info << "Set SSE denormal fix flag" << endl; - } - } else { - Raul::warn << "This code has been built with SSE support, but your processor does" - << " not support the SSE instruction set, exiting." << std::endl; - exit(EXIT_FAILURE); - } -#endif -#endif -} - -static inline std::string -ingen_jack_port_name(const Raul::Path& path) -{ - return path.chop_start("/"); -} - -} // namespace Engine -} // namespace Ingen - -#endif // INGEN_ENGINE_UTIL_HPP diff --git a/src/engine/wscript b/src/engine/wscript deleted file mode 100644 index 2321da69..00000000 --- a/src/engine/wscript +++ /dev/null @@ -1,128 +0,0 @@ -#!/usr/bin/env python -from waflib.extras import autowaf as autowaf - -def build(bld): - # Headers - bld.install_files('${INCLUDEDIR}/ingen/engine', bld.path.ant_glob('*.hpp')) - - core_source = ''' - AudioBuffer.cpp - BufferFactory.cpp - ClientBroadcaster.cpp - ConnectionImpl.cpp - ControlBindings.cpp - DuplexPort.cpp - Engine.cpp - EngineStore.cpp - Event.cpp - EventBuffer.cpp - EventSink.cpp - EventSource.cpp - GraphObjectImpl.cpp - InputPort.cpp - InternalPlugin.cpp - MessageContext.cpp - NodeFactory.cpp - NodeImpl.cpp - ObjectBuffer.cpp - ObjectSender.cpp - OutputPort.cpp - PatchImpl.cpp - PluginImpl.cpp - PortImpl.cpp - PostProcessor.cpp - ProcessContext.cpp - ProcessSlave.cpp - QueuedEngineInterface.cpp - QueuedEvent.cpp - events/Connect.cpp - events/CreateNode.cpp - events/CreatePatch.cpp - events/CreatePort.cpp - events/Delete.cpp - events/Disconnect.cpp - events/DisconnectAll.cpp - events/Get.cpp - events/Move.cpp - events/RegisterClient.cpp - events/RequestMetadata.cpp - events/SendBinding.cpp - events/SendPortActivity.cpp - events/SendPortValue.cpp - events/SetMetadata.cpp - events/SetPortValue.cpp - events/UnregisterClient.cpp - ingen_engine.cpp - internals/Controller.cpp - internals/Delay.cpp - internals/Note.cpp - internals/Trigger.cpp - ''' - - if bld.is_defined('HAVE_SLV2'): - core_source += ' LV2Info.cpp LV2Plugin.cpp LV2Node.cpp ' - - obj = bld(features = 'cxx cxxshlib') - obj.source = core_source - obj.export_includes = ['.'] - obj.includes = ['.', '..', '../..', '../../include'] - obj.name = 'libingen_engine' - obj.target = 'ingen_engine' - obj.install_path = '${LIBDIR}' - obj.use = 'libingen_shared' - core_libs = 'GLIBMM GTHREAD LV2CORE SLV2 RAUL SORD' - autowaf.use_lib(bld, obj, core_libs) - - if bld.is_defined('HAVE_SOUP'): - obj = bld(features = 'cxx cxxshlib') - obj.source = ''' - EventSource.cpp - QueuedEngineInterface.cpp - HTTPClientSender.cpp - HTTPEngineReceiver.cpp - ingen_http.cpp - ''' - obj.includes = ['.', '..', '../..', '../../include', '../engine'] - obj.name = 'libingen_http' - obj.target = 'ingen_http' - obj.install_path = '${LIBDIR}' - autowaf.use_lib(bld, obj, core_libs + ' SOUP') - - if bld.is_defined('HAVE_LIBLO'): - obj = bld(features = 'cxx cxxshlib') - obj.source = ''' - EventSource.cpp - QueuedEngineInterface.cpp - OSCClientSender.cpp - OSCEngineReceiver.cpp - ingen_osc.cpp - ''' - obj.export_includes = ['.'] - obj.includes = ['.', '..', '../..', '../../include', '../engine'] - obj.name = 'libingen_osc' - obj.target = 'ingen_osc' - obj.install_path = '${LIBDIR}' - autowaf.use_lib(bld, obj, core_libs + ' LIBLO') - - if bld.is_defined('HAVE_JACK'): - obj = bld(features = 'cxx cxxshlib') - obj.source = 'JackDriver.cpp ingen_jack.cpp' - obj.export_includes = ['.'] - obj.includes = ['.', '..', '../..', '../../include', '../engine'] - obj.name = 'libingen_jack' - obj.target = 'ingen_jack' - obj.install_path = '${LIBDIR}' - obj.use = 'libingen_engine' - autowaf.use_lib(bld, obj, core_libs + ' JACK') - - # Ingen LV2 wrapper - obj = bld(features = 'cxx cxxshlib') - obj.source = ' ingen_lv2.cpp ' - obj.export_includes = ['.'] - obj.includes = ['.', '..', '../..', '../../include'] - obj.name = 'libingen_lv2' - obj.target = 'ingen_lv2' - obj.install_path = '${LIBDIR}' - obj.use = 'libingen_shared' - obj.add_objects = 'libingen_engine' - autowaf.use_lib(bld, obj, core_libs) diff --git a/src/gui/App.cpp b/src/gui/App.cpp index 0ed8b10a..e4a8c167 100644 --- a/src/gui/App.cpp +++ b/src/gui/App.cpp @@ -28,8 +28,8 @@ #include "raul/Path.hpp" #include "flowcanvas/Connection.hpp" #include "shared/World.hpp" -#include "engine/Engine.hpp" -#include "ingen/EngineInterface.hpp" +#include "server/Engine.hpp" +#include "ingen/ServerInterface.hpp" #include "shared/runtime_paths.hpp" #include "shared/LV2URIMap.hpp" #include "client/ObjectModel.hpp" @@ -189,7 +189,7 @@ App::detach() _store.reset(); _client.reset(); _handle.reset(); - _world->set_engine(SharedPtr()); + _world->set_engine(SharedPtr()); } } diff --git a/src/gui/App.hpp b/src/gui/App.hpp index 5a5fa0c2..82eab2f3 100644 --- a/src/gui/App.hpp +++ b/src/gui/App.hpp @@ -31,7 +31,7 @@ namespace Ingen { class ClientInterface; - class EngineInterface; + class ServerInterface; class Port; namespace Shared { class World; @@ -99,7 +99,7 @@ public: Glib::RefPtr icon_from_path(const std::string& path, int size); - SharedPtr engine() const { return _world->engine(); } + SharedPtr engine() const { return _world->engine(); } SharedPtr client() const { return _client; } SharedPtr store() const { return _store; } SharedPtr loader() const { return _loader; } diff --git a/src/gui/ConnectWindow.cpp b/src/gui/ConnectWindow.cpp index f93ebf4c..171113fb 100644 --- a/src/gui/ConnectWindow.cpp +++ b/src/gui/ConnectWindow.cpp @@ -25,10 +25,10 @@ #include "raul/log.hpp" #include "ingen-config.h" -#include "ingen/EngineInterface.hpp" +#include "ingen/ServerInterface.hpp" #include "shared/Module.hpp" #include "shared/World.hpp" -#include "engine/Engine.hpp" +#include "server/Engine.hpp" #ifdef HAVE_SOUP #include "client/HTTPClientReceiver.hpp" #include "client/HTTPEngineSender.hpp" @@ -83,7 +83,7 @@ ConnectWindow::start(Ingen::Shared::World* world) } void -ConnectWindow::set_connected_to(SharedPtr engine) +ConnectWindow::set_connected_to(SharedPtr engine) { App::instance().world()->set_engine(engine); @@ -142,7 +142,7 @@ ConnectWindow::set_connecting_widget_states() /** Launch (if applicable) and connect to the Engine. * - * This will create the EngineInterface and ClientInterface and initialize + * This will create the ServerInterface and ClientInterface and initialize * the App with them. */ void @@ -193,14 +193,14 @@ ConnectWindow::connect(bool existing) #ifdef HAVE_LIBLO if (scheme == "osc.udp" || scheme == "osc.tcp") world->set_engine( - SharedPtr( + SharedPtr( new OSCEngineSender( uri, world->conf()->option("packet-size").get_int32()))); #endif #ifdef HAVE_SOUP if (scheme == "http") - world->set_engine(SharedPtr( + world->set_engine(SharedPtr( new HTTPEngineSender(world, uri))); #endif } else { @@ -222,7 +222,7 @@ ConnectWindow::connect(bool existing) const string cmd = string("ingen -e --engine-port=").append(port_str); if (Raul::Process::launch(cmd)) { - world->set_engine(SharedPtr( + world->set_engine(SharedPtr( new OSCEngineSender( string("osc.udp://localhost:").append(port_str), world->conf()->option("packet-size").get_int32()))); @@ -252,7 +252,7 @@ ConnectWindow::connect(bool existing) SharedPtr client(new SigClientInterface()); - if (!((Engine::Engine*)world->local_engine().get())->driver()) + if (!((Server::Engine*)world->local_engine().get())->driver()) world->load_module("jack"); world->local_engine()->activate(); @@ -272,7 +272,7 @@ ConnectWindow::disconnect() _attached = false; App::instance().detach(); - set_connected_to(SharedPtr()); + set_connected_to(SharedPtr()); if (!_widgets_loaded) return; diff --git a/src/gui/ConnectWindow.hpp b/src/gui/ConnectWindow.hpp index b5434d29..1e5fdd37 100644 --- a/src/gui/ConnectWindow.hpp +++ b/src/gui/ConnectWindow.hpp @@ -49,7 +49,7 @@ class ConnectWindow : public Dialog public: ConnectWindow(BaseObjectType* cobject, const Glib::RefPtr& xml); - void set_connected_to(SharedPtr engine); + void set_connected_to(SharedPtr engine); void start(Ingen::Shared::World* world); void on_response(int32_t id) { _attached = true; } diff --git a/src/gui/ControlPanel.cpp b/src/gui/ControlPanel.cpp index 083c1457..03e044cc 100644 --- a/src/gui/ControlPanel.cpp +++ b/src/gui/ControlPanel.cpp @@ -15,7 +15,7 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "ingen/EngineInterface.hpp" +#include "ingen/ServerInterface.hpp" #include "ingen/PortType.hpp" #include "shared/LV2URIMap.hpp" #include "client/NodeModel.hpp" diff --git a/src/gui/Controls.cpp b/src/gui/Controls.cpp index a2c09e11..9322fd77 100644 --- a/src/gui/Controls.cpp +++ b/src/gui/Controls.cpp @@ -18,7 +18,7 @@ #include #include #include "raul/log.hpp" -#include "ingen/EngineInterface.hpp" +#include "ingen/ServerInterface.hpp" #include "shared/LV2URIMap.hpp" #include "client/PluginModel.hpp" #include "client/NodeModel.hpp" diff --git a/src/gui/LoadPatchWindow.cpp b/src/gui/LoadPatchWindow.cpp index edc47d4f..99c1c127 100644 --- a/src/gui/LoadPatchWindow.cpp +++ b/src/gui/LoadPatchWindow.cpp @@ -20,7 +20,7 @@ #include #include #include -#include "ingen/EngineInterface.hpp" +#include "ingen/ServerInterface.hpp" #include "shared/LV2URIMap.hpp" #include "client/NodeModel.hpp" #include "client/PatchModel.hpp" diff --git a/src/gui/LoadPluginWindow.cpp b/src/gui/LoadPluginWindow.cpp index b2cfc052..cf780f2d 100644 --- a/src/gui/LoadPluginWindow.cpp +++ b/src/gui/LoadPluginWindow.cpp @@ -19,7 +19,7 @@ #include #include #include "ingen-config.h" -#include "ingen/EngineInterface.hpp" +#include "ingen/ServerInterface.hpp" #include "shared/LV2URIMap.hpp" #include "client/PatchModel.hpp" #include "client/ClientStore.hpp" diff --git a/src/gui/LoadRemotePatchWindow.cpp b/src/gui/LoadRemotePatchWindow.cpp index e66b25a1..0318dba7 100644 --- a/src/gui/LoadRemotePatchWindow.cpp +++ b/src/gui/LoadRemotePatchWindow.cpp @@ -19,7 +19,7 @@ #include #include #include "client/PatchModel.hpp" -#include "ingen/EngineInterface.hpp" +#include "ingen/ServerInterface.hpp" #include "shared/World.hpp" #include "App.hpp" #include "Configuration.hpp" diff --git a/src/gui/NewSubpatchWindow.cpp b/src/gui/NewSubpatchWindow.cpp index 35e66e3b..26664693 100644 --- a/src/gui/NewSubpatchWindow.cpp +++ b/src/gui/NewSubpatchWindow.cpp @@ -16,7 +16,7 @@ */ #include "App.hpp" -#include "ingen/EngineInterface.hpp" +#include "ingen/ServerInterface.hpp" #include "shared/LV2URIMap.hpp" #include "client/PatchModel.hpp" #include "client/ClientStore.hpp" diff --git a/src/gui/NodeControlWindow.cpp b/src/gui/NodeControlWindow.cpp index b4ccbe73..ef09a68d 100644 --- a/src/gui/NodeControlWindow.cpp +++ b/src/gui/NodeControlWindow.cpp @@ -16,7 +16,7 @@ */ #include -#include "ingen/EngineInterface.hpp" +#include "ingen/ServerInterface.hpp" #include "shared/LV2URIMap.hpp" #include "client/NodeModel.hpp" #include "App.hpp" diff --git a/src/gui/NodeMenu.cpp b/src/gui/NodeMenu.cpp index 1e6b0f9f..3a683027 100644 --- a/src/gui/NodeMenu.cpp +++ b/src/gui/NodeMenu.cpp @@ -17,7 +17,7 @@ #include #include "ingen-config.h" -#include "ingen/EngineInterface.hpp" +#include "ingen/ServerInterface.hpp" #include "shared/LV2URIMap.hpp" #include "client/NodeModel.hpp" #include "client/PluginModel.hpp" diff --git a/src/gui/NodeModule.cpp b/src/gui/NodeModule.cpp index 984e84b8..d8f22ecd 100644 --- a/src/gui/NodeModule.cpp +++ b/src/gui/NodeModule.cpp @@ -19,7 +19,7 @@ #include "ingen-config.h" #include "raul/log.hpp" #include "raul/Atom.hpp" -#include "ingen/EngineInterface.hpp" +#include "ingen/ServerInterface.hpp" #include "shared/LV2URIMap.hpp" #include "client/PatchModel.hpp" #include "client/NodeModel.hpp" diff --git a/src/gui/ObjectMenu.cpp b/src/gui/ObjectMenu.cpp index 39afa071..d8a5e426 100644 --- a/src/gui/ObjectMenu.cpp +++ b/src/gui/ObjectMenu.cpp @@ -17,7 +17,7 @@ #include #include -#include "ingen/EngineInterface.hpp" +#include "ingen/ServerInterface.hpp" #include "shared/LV2URIMap.hpp" #include "client/ObjectModel.hpp" #include "App.hpp" diff --git a/src/gui/PatchCanvas.cpp b/src/gui/PatchCanvas.cpp index 8c7cb0fa..334b7035 100644 --- a/src/gui/PatchCanvas.cpp +++ b/src/gui/PatchCanvas.cpp @@ -22,7 +22,7 @@ #include "raul/log.hpp" #include "flowcanvas/Canvas.hpp" #include "flowcanvas/Ellipse.hpp" -#include "ingen/EngineInterface.hpp" +#include "ingen/ServerInterface.hpp" #include "shared/LV2URIMap.hpp" #include "shared/Builder.hpp" #include "shared/ClashAvoider.hpp" diff --git a/src/gui/PatchPortModule.cpp b/src/gui/PatchPortModule.cpp index 10bb60e6..51dd064a 100644 --- a/src/gui/PatchPortModule.cpp +++ b/src/gui/PatchPortModule.cpp @@ -18,7 +18,7 @@ #include #include #include "PatchPortModule.hpp" -#include "ingen/EngineInterface.hpp" +#include "ingen/ServerInterface.hpp" #include "shared/LV2URIMap.hpp" #include "client/PatchModel.hpp" #include "client/NodeModel.hpp" diff --git a/src/gui/PatchTreeWindow.cpp b/src/gui/PatchTreeWindow.cpp index 5ed578db..adb41a13 100644 --- a/src/gui/PatchTreeWindow.cpp +++ b/src/gui/PatchTreeWindow.cpp @@ -17,7 +17,7 @@ #include "raul/log.hpp" #include "raul/Path.hpp" -#include "ingen/EngineInterface.hpp" +#include "ingen/ServerInterface.hpp" #include "shared/LV2URIMap.hpp" #include "client/ClientStore.hpp" #include "client/PatchModel.hpp" diff --git a/src/gui/PatchView.cpp b/src/gui/PatchView.cpp index 55c6b4b6..afe73048 100644 --- a/src/gui/PatchView.cpp +++ b/src/gui/PatchView.cpp @@ -18,7 +18,7 @@ #include #include #include "raul/log.hpp" -#include "ingen/EngineInterface.hpp" +#include "ingen/ServerInterface.hpp" #include "shared/LV2URIMap.hpp" #include "client/PatchModel.hpp" #include "App.hpp" diff --git a/src/gui/PatchWindow.cpp b/src/gui/PatchWindow.cpp index 218453d6..cd97e3e3 100644 --- a/src/gui/PatchWindow.cpp +++ b/src/gui/PatchWindow.cpp @@ -22,7 +22,7 @@ #include #include #include "raul/AtomRDF.hpp" -#include "ingen/EngineInterface.hpp" +#include "ingen/ServerInterface.hpp" #include "shared/LV2URIMap.hpp" #include "client/PatchModel.hpp" #include "client/ClientStore.hpp" diff --git a/src/gui/Port.cpp b/src/gui/Port.cpp index f22af7e4..ba28078d 100644 --- a/src/gui/Port.cpp +++ b/src/gui/Port.cpp @@ -18,7 +18,7 @@ #include #include "raul/log.hpp" #include "flowcanvas/Module.hpp" -#include "ingen/EngineInterface.hpp" +#include "ingen/ServerInterface.hpp" #include "shared/LV2URIMap.hpp" #include "client/PatchModel.hpp" #include "client/PortModel.hpp" diff --git a/src/gui/PortMenu.cpp b/src/gui/PortMenu.cpp index 69a67dd9..079e3c3e 100644 --- a/src/gui/PortMenu.cpp +++ b/src/gui/PortMenu.cpp @@ -18,7 +18,7 @@ #include #include #include "raul/SharedPtr.hpp" -#include "ingen/EngineInterface.hpp" +#include "ingen/ServerInterface.hpp" #include "shared/LV2URIMap.hpp" #include "client/PatchModel.hpp" #include "client/PortModel.hpp" diff --git a/src/gui/PortPropertiesWindow.cpp b/src/gui/PortPropertiesWindow.cpp index 1f3be2fb..23d38ac9 100644 --- a/src/gui/PortPropertiesWindow.cpp +++ b/src/gui/PortPropertiesWindow.cpp @@ -17,7 +17,7 @@ #include #include -#include "ingen/EngineInterface.hpp" +#include "ingen/ServerInterface.hpp" #include "shared/LV2URIMap.hpp" #include "client/NodeModel.hpp" #include "client/PluginModel.hpp" diff --git a/src/gui/RenameWindow.cpp b/src/gui/RenameWindow.cpp index 52e3a339..7b8844f1 100644 --- a/src/gui/RenameWindow.cpp +++ b/src/gui/RenameWindow.cpp @@ -17,7 +17,7 @@ #include #include -#include "ingen/EngineInterface.hpp" +#include "ingen/ServerInterface.hpp" #include "shared/LV2URIMap.hpp" #include "client/ObjectModel.hpp" #include "client/ClientStore.hpp" diff --git a/src/gui/SubpatchModule.cpp b/src/gui/SubpatchModule.cpp index 4023a1f2..e2347bdd 100644 --- a/src/gui/SubpatchModule.cpp +++ b/src/gui/SubpatchModule.cpp @@ -17,7 +17,7 @@ #include "SubpatchModule.hpp" #include -#include "ingen/EngineInterface.hpp" +#include "ingen/ServerInterface.hpp" #include "client/PatchModel.hpp" #include "App.hpp" #include "NodeModule.hpp" diff --git a/src/gui/ThreadedLoader.cpp b/src/gui/ThreadedLoader.cpp index 5338b7ee..7a7a3ede 100644 --- a/src/gui/ThreadedLoader.cpp +++ b/src/gui/ThreadedLoader.cpp @@ -30,7 +30,7 @@ using namespace Raul; namespace Ingen { namespace GUI { -ThreadedLoader::ThreadedLoader(SharedPtr uris, SharedPtr engine) +ThreadedLoader::ThreadedLoader(SharedPtr uris, SharedPtr engine) : _engine(engine) { set_name("Loader"); diff --git a/src/gui/ThreadedLoader.hpp b/src/gui/ThreadedLoader.hpp index ef137d1e..c35da81f 100644 --- a/src/gui/ThreadedLoader.hpp +++ b/src/gui/ThreadedLoader.hpp @@ -25,7 +25,7 @@ #include "raul/Thread.hpp" #include "raul/Slave.hpp" #include -#include "ingen/EngineInterface.hpp" +#include "ingen/ServerInterface.hpp" #include "serialisation/Serialiser.hpp" #include "serialisation/Parser.hpp" using std::string; @@ -54,7 +54,7 @@ class ThreadedLoader : public Raul::Slave { public: ThreadedLoader(SharedPtr uris, - SharedPtr engine); + SharedPtr engine); void load_patch(bool merge, const Glib::ustring& document_uri, @@ -75,7 +75,7 @@ private: void _whipped(); - SharedPtr _engine; + SharedPtr _engine; Glib::Mutex _mutex; list _events; diff --git a/src/gui/UploadPatchWindow.cpp b/src/gui/UploadPatchWindow.cpp index a7526eee..326aa968 100644 --- a/src/gui/UploadPatchWindow.cpp +++ b/src/gui/UploadPatchWindow.cpp @@ -24,7 +24,7 @@ #include "shared/World.hpp" #include "shared/LV2URIMap.hpp" #include "client/ClientStore.hpp" -#include "ingen/EngineInterface.hpp" +#include "ingen/ServerInterface.hpp" #include "serialisation/Serialiser.hpp" #include "serialisation/names.hpp" #include "client/PatchModel.hpp" diff --git a/src/ingen/main.cpp b/src/ingen/main.cpp index 4691c302..57f59793 100644 --- a/src/ingen/main.cpp +++ b/src/ingen/main.cpp @@ -40,7 +40,7 @@ #include "ingen-config.h" #include "ingen/EngineBase.hpp" -#include "ingen/EngineInterface.hpp" +#include "ingen/ServerInterface.hpp" #include "serialisation/Parser.hpp" #include "shared/Configuration.hpp" #include "shared/World.hpp" @@ -102,7 +102,7 @@ main(int argc, char** argv) // Set bundle path from executable location so resources can be found Shared::set_bundle_path_from_code((void*)&main); - SharedPtr engine_interface; + SharedPtr engine_interface; Glib::thread_init(); #if HAVE_SOUP diff --git a/src/serialisation/Parser.cpp b/src/serialisation/Parser.cpp index f66cd38f..d1ff41f9 100644 --- a/src/serialisation/Parser.cpp +++ b/src/serialisation/Parser.cpp @@ -34,7 +34,7 @@ #include "serd/serd.h" #include "sord/sordmm.hpp" -#include "ingen/EngineInterface.hpp" +#include "ingen/ServerInterface.hpp" #include "shared/World.hpp" #include "shared/LV2URIMap.hpp" diff --git a/src/serialisation/Serialiser.cpp b/src/serialisation/Serialiser.cpp index 1eb76344..04b4c692 100644 --- a/src/serialisation/Serialiser.cpp +++ b/src/serialisation/Serialiser.cpp @@ -43,7 +43,7 @@ #include "sord/sordmm.hpp" #include "ingen/Connection.hpp" -#include "ingen/EngineInterface.hpp" +#include "ingen/ServerInterface.hpp" #include "ingen/Node.hpp" #include "ingen/Patch.hpp" #include "ingen/Plugin.hpp" diff --git a/src/server/AudioBuffer.cpp b/src/server/AudioBuffer.cpp new file mode 100644 index 00000000..f255ec2f --- /dev/null +++ b/src/server/AudioBuffer.cpp @@ -0,0 +1,207 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "raul/log.hpp" +#include "raul/SharedPtr.hpp" +#include "lv2/lv2plug.in/ns/ext/atom/atom.h" +#include "ingen-config.h" +#include "AudioBuffer.hpp" +#include "ProcessContext.hpp" +#include "LV2Features.hpp" + +using namespace std; +using namespace Raul; + +/* TODO: Be sure these functions are vectorized by GCC when its vectorizer + * stops sucking. Probably a good idea to inline them as well */ + +namespace Ingen { +namespace Server { + +AudioBuffer::AudioBuffer(BufferFactory& bufs, PortType type, size_t size) + : ObjectBuffer(bufs, size) + , _state(OK) + , _set_value(0) + , _set_time(0) +{ + assert(size >= sizeof(LV2_Atom) + sizeof(Sample)); + assert(this->size() >= size); + assert(data()); + _type = type; + + // Control port / Single float object + if (type == PortType::CONTROL) { + atom()->type = 0;//map->float_type; + + // Audio port / Vector of float + } else { + assert(type == PortType::AUDIO); + atom()->type = 0;//map->vector_type; + LV2_Atom_Vector* body = (LV2_Atom_Vector*)atom()->body; + body->elem_count = size / sizeof(Sample); + body->elem_type = 0;//map->float_type; + } + /*debug << "Created Audio Buffer" << endl + << "\tobject @ " << (void*)atom() << endl + << "\tbody @ " << (void*)atom()->body + << "\t(offset " << (char*)atom()->body - (char*)atom() << ")" << endl + << "\tdata @ " << (void*)data() + << "\t(offset " << (char*)data() - (char*)atom() << ")" + << endl;*/ + + clear(); +} + +void +AudioBuffer::resize(size_t size) +{ + if (_type == PortType::AUDIO) { + ObjectBuffer::resize(size + sizeof(LV2_Atom_Vector)); + vector()->elem_count = size / sizeof(Sample); + } + clear(); +} + +/** Empty (ie zero) the buffer. + */ +void +AudioBuffer::clear() +{ + assert(nframes() != 0); + set_block(0, 0, nframes() - 1); + _state = OK; +} + +/** 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 (is_control()) + time = cycle_start; + + const FrameTime offset = time - cycle_start; + assert(nframes() != 0); + assert(offset <= nframes()); + + if (offset < nframes()) { + set_block(val, offset, nframes() - 1); + + if (offset == 0) + _state = OK; + else + _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 < nframes()); + + Sample* const buf = data(); + assert(buf); + + for (size_t i = start_offset; i <= end_offset; ++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 Sample* src, size_t start_sample, size_t end_sample) +{ + assert(end_sample >= start_sample); + assert(nframes() != 0); + + Sample* const buf = data(); + assert(buf); + + const size_t copy_end = std::min(end_sample, (size_t)nframes() - 1); + for (size_t i = start_sample; i <= copy_end; ++i) + buf[i] = src[i]; +} + +void +AudioBuffer::copy(Context& context, const Buffer* src) +{ + const AudioBuffer* src_abuf = dynamic_cast(src); + if (!src_abuf) { + clear(); + return; + } + + // Control => Control + if (src_abuf->is_control() == is_control()) { + ObjectBuffer::copy(context, src); + + // Audio => Audio + } else if (!src_abuf->is_control() && !is_control()) { + copy(src_abuf->data(), + context.offset(), context.offset() + context.nframes() - 1); + + // Audio => Control + } else if (!src_abuf->is_control() && is_control()) { + data()[0] = src_abuf->data()[context.offset()]; + + // Control => Audio + } else if (src_abuf->is_control() && !is_control()) { + data()[context.offset()] = src_abuf->data()[0]; + + // Control => Audio or Audio => Control + } else { + set_block(src_abuf->data()[0], 0, nframes()); + } +} + +void +AudioBuffer::prepare_read(Context& context) +{ + assert(nframes() != 0); + switch (_state) { + case HALF_SET_CYCLE_1: + if (context.start() > _set_time) + _state = HALF_SET_CYCLE_2; + break; + case HALF_SET_CYCLE_2: + set_block(_set_value, 0, nframes() - 1); + _state = OK; + break; + default: + break; + } +} + +} // namespace Server +} // namespace Ingen diff --git a/src/server/AudioBuffer.hpp b/src/server/AudioBuffer.hpp new file mode 100644 index 00000000..4a7869e2 --- /dev/null +++ b/src/server/AudioBuffer.hpp @@ -0,0 +1,109 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_AUDIOBUFFER_HPP +#define INGEN_ENGINE_AUDIOBUFFER_HPP + +#include +#include +#include +#include "types.hpp" +#include "ObjectBuffer.hpp" +#include "Context.hpp" + +using namespace std; + +namespace Ingen { +namespace Server { + +class AudioBuffer : public ObjectBuffer +{ +public: + AudioBuffer(BufferFactory& bufs, PortType type, 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 copy(const Sample* src, size_t start_sample, size_t end_sample); + void copy(Context& context, const Buffer* src); + void accumulate(Context& context, const AudioBuffer* src); + + inline bool is_control() const { return _type.symbol() == PortType::CONTROL; } + + inline Sample* data() const { + return (is_control()) + ? (Sample*)atom()->body + : (Sample*)(atom()->body + sizeof(LV2_Atom_Vector)); + } + + inline SampleCount nframes() const { + return (is_control()) + ? 1 + : (_size - sizeof(LV2_Atom) - sizeof(LV2_Atom_Vector)) / sizeof(Sample); + } + + inline Sample& value_at(size_t offset) const + { assert(offset < nframes()); return data()[offset]; } + + void prepare_read(Context& context); + void prepare_write(Context& context) {} + + void resize(size_t size); + +private: + enum State { OK, HALF_SET_CYCLE_1, HALF_SET_CYCLE_2 }; + + LV2_Atom_Vector* vector() { return(LV2_Atom_Vector*)atom()->body; } + + State _state; ///< State of buffer for setting values next cycle + Sample _set_value; ///< Value set by set_value (for completing the set next cycle) + FrameTime _set_time; ///< Time _set_value was set (to reset next cycle) +}; + +/** Accumulate a block of @a src into buffer. + */ +inline void +AudioBuffer::accumulate(Context& context, const AudioBuffer* const src) +{ + Sample* const buf = data(); + const Sample* const src_buf = src->data(); + + if (is_control()) { + if (src->is_control()) { // control => control + buf[0] += src_buf[0]; + } else { // audio => control + buf[0] += src_buf[context.offset()]; + } + } else { + const SampleCount end = context.offset() + context.nframes(); + if (src->is_control()) { // control => audio + for (SampleCount i = context.offset(); i < end; ++i) { + buf[i] += src_buf[0]; + } + } else { // audio => audio + for (SampleCount i = context.offset(); i < end; ++i) { + buf[i] += src_buf[i]; + } + } + } +} + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_AUDIOBUFFER_HPP diff --git a/src/server/Buffer.hpp b/src/server/Buffer.hpp new file mode 100644 index 00000000..e44915b3 --- /dev/null +++ b/src/server/Buffer.hpp @@ -0,0 +1,97 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_BUFFER_HPP +#define INGEN_ENGINE_BUFFER_HPP + +#include +#include +#include +#include +#include "raul/Deletable.hpp" +#include "raul/SharedPtr.hpp" +#include "ingen/PortType.hpp" +#include "types.hpp" +#include "BufferFactory.hpp" + +namespace Ingen { +namespace Server { + +class Context; +class Engine; +class BufferFactory; + +class Buffer : public boost::noncopyable, public Raul::Deletable +{ +public: + Buffer(BufferFactory& bufs, PortType type, size_t size) + : _factory(bufs) + , _type(type) + , _size(size) + , _next(NULL) + , _refs(0) + {} + + /** Clear contents and reset state */ + virtual void clear() = 0; + + virtual void resize(size_t size) { _size = size; } + + virtual void* port_data(PortType port_type, SampleCount offset=0) = 0; + virtual const void* port_data(PortType port_type, SampleCount offset=0) const = 0; + + /** Rewind (ie reset read pointer), but leave contents unchanged */ + virtual void rewind() const {} + + virtual void copy(Context& context, const Buffer* src) = 0; + + virtual void prepare_read(Context& context) {} + virtual void prepare_write(Context& context) {} + + PortType type() const { return _type; } + size_t size() const { return _size; } + + inline void ref() { ++_refs; } + + inline void deref() { + assert(_refs > 0); + if ((--_refs) == 0) + _factory.recycle(this); + } + +protected: + BufferFactory& _factory; + PortType _type; + size_t _size; + + friend class BufferFactory; + virtual ~Buffer() {} + +private: + Buffer* _next; ///< Intrusive linked list for BufferFactory + size_t _refs; ///< Intrusive reference count for intrusive_ptr +}; + +} // namespace Server +} // namespace Ingen + +namespace boost { +inline void intrusive_ptr_add_ref(Ingen::Server::Buffer* b) { b->ref(); } +inline void intrusive_ptr_release(Ingen::Server::Buffer* b) { b->deref(); } +} + +#endif // INGEN_ENGINE_BUFFER_HPP diff --git a/src/server/BufferFactory.cpp b/src/server/BufferFactory.cpp new file mode 100644 index 00000000..f725079b --- /dev/null +++ b/src/server/BufferFactory.cpp @@ -0,0 +1,163 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "raul/log.hpp" +#include "shared/LV2URIMap.hpp" +#include "AudioBuffer.hpp" +#include "EventBuffer.hpp" +#include "ObjectBuffer.hpp" +#include "BufferFactory.hpp" +#include "Engine.hpp" +#include "Driver.hpp" +#include "ThreadManager.hpp" + +using namespace Raul; + +namespace Ingen { +namespace Server { + +static const size_t EVENT_BYTES_PER_FRAME = 4; // FIXME + +BufferFactory::BufferFactory(Engine& engine, + SharedPtr uris) + : _engine(engine) + , _uris(uris) + , _silent_buffer(NULL) +{ + assert(_uris); +} + +BufferFactory::~BufferFactory() +{ + free_list(_free_audio.get()); + free_list(_free_control.get()); + free_list(_free_event.get()); + free_list(_free_object.get()); +} + +void +BufferFactory::free_list(Buffer* head) +{ + Buffer* next = head->_next; + delete head; + if (next) + free_list(next); +} + +void +BufferFactory::set_block_length(SampleCount block_length) +{ + _silent_buffer = create(PortType::AUDIO, audio_buffer_size(block_length)); +} + +size_t +BufferFactory::audio_buffer_size(SampleCount nframes) +{ + return sizeof(LV2_Atom) + sizeof(LV2_Atom_Vector) + (nframes * sizeof(float)); +} + +size_t +BufferFactory::default_buffer_size(PortType type) +{ + switch (type.symbol()) { + case PortType::AUDIO: + return audio_buffer_size(_engine.driver()->block_length()); + case PortType::CONTROL: + return sizeof(LV2_Atom) + sizeof(float); + case PortType::EVENTS: + return _engine.driver()->block_length() * EVENT_BYTES_PER_FRAME; + default: + return 1024; // Who knows + } +} + +BufferFactory::Ref +BufferFactory::get(PortType type, size_t size, bool force_create) +{ + Raul::AtomicPtr& head_ptr = free_list(type); + Buffer* try_head = NULL; + + if (!force_create) { + Buffer* next; + do { + try_head = head_ptr.get(); + if (!try_head) + break; + next = try_head->_next; + } while (!head_ptr.compare_and_exchange(try_head, next)); + } + + if (!try_head) { + if (!ThreadManager::thread_is(THREAD_PROCESS)) { + return create(type, size); + } else { + assert(false); + error << "Failed to obtain buffer" << endl; + return Ref(); + } + } + + try_head->_next = NULL; + return Ref(try_head); +} + +BufferFactory::Ref +BufferFactory::create(PortType type, size_t size) +{ + ThreadManager::assert_not_thread(THREAD_PROCESS); + + Buffer* buffer = NULL; + + if (size == 0) + size = default_buffer_size(type); + + if (type.is_control()) { + AudioBuffer* ret = new AudioBuffer(*this, type, size); + ret->atom()->type = _uris->atom_Vector.id; + ((LV2_Atom_Vector*)ret->atom()->body)->elem_type = _uris->atom_Float32.id; + buffer = ret; + } else if (type.is_audio()) { + AudioBuffer* ret = new AudioBuffer(*this, type, size); + ret->atom()->type = _uris->atom_Float32.id; + buffer = ret; + } else if (type.is_events()) { + buffer = new EventBuffer(*this, size); + } else if (type.is_value() || type.is_message()) { + buffer = new ObjectBuffer(*this, std::max(size, sizeof(LV2_Atom) + sizeof(void*))); + } else { + error << "Failed to create buffer of unknown type" << endl; + return Ref(); + } + + assert(buffer); + return Ref(buffer); +} + +void +BufferFactory::recycle(Buffer* buf) +{ + Raul::AtomicPtr& head_ptr = free_list(buf->type()); + Buffer* try_head; + do { + try_head = head_ptr.get(); + buf->_next = try_head; + } while (!head_ptr.compare_and_exchange(try_head, buf)); +} + +} // namespace Server +} // namespace Ingen diff --git a/src/server/BufferFactory.hpp b/src/server/BufferFactory.hpp new file mode 100644 index 00000000..58dc55af --- /dev/null +++ b/src/server/BufferFactory.hpp @@ -0,0 +1,92 @@ +/* This file is part of Ingen. + * Copyright 2009-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_BUFFERFACTORY_HPP +#define INGEN_ENGINE_BUFFERFACTORY_HPP + +#include +#include +#include "ingen/PortType.hpp" +#include "glibmm/thread.h" +#include "raul/RingBuffer.hpp" +#include "raul/AtomicPtr.hpp" +#include "types.hpp" + +namespace Ingen { + +namespace Shared { class LV2URIMap; } + +namespace Server { + +class Engine; +class Buffer; + +class BufferFactory { +public: + BufferFactory(Engine& engine, + SharedPtr uris); + + ~BufferFactory(); + + typedef boost::intrusive_ptr Ref; + + static size_t audio_buffer_size(SampleCount nframes); + size_t default_buffer_size(PortType type); + + Ref get(PortType type, size_t size=0, bool force_create=false); + + Ref silent_buffer() { return _silent_buffer; } + + void set_block_length(SampleCount block_length); + + Ingen::Shared::LV2URIMap& uris() { assert(_uris); return *_uris.get(); } + +private: + friend class Buffer; + void recycle(Buffer* buf); + + Ref create(PortType type, size_t size=0); + + inline Raul::AtomicPtr& free_list(PortType type) { + switch (type.symbol()) { + case PortType::AUDIO: return _free_audio; + case PortType::CONTROL: return _free_control; + case PortType::EVENTS: return _free_event; + case PortType::VALUE: + case PortType::MESSAGE: return _free_object; + default: throw; + } + } + + void free_list(Buffer* head); + + Raul::AtomicPtr _free_audio; + Raul::AtomicPtr _free_control; + Raul::AtomicPtr _free_event; + Raul::AtomicPtr _free_object; + + Glib::Mutex _mutex; + Engine& _engine; + SharedPtr _uris; + + Ref _silent_buffer; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_BUFFERFACTORY_HPP diff --git a/src/server/ClientBroadcaster.cpp b/src/server/ClientBroadcaster.cpp new file mode 100644 index 00000000..2931497a --- /dev/null +++ b/src/server/ClientBroadcaster.cpp @@ -0,0 +1,116 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "raul/log.hpp" +#include "ingen/ClientInterface.hpp" +#include "ClientBroadcaster.hpp" +#include "PluginImpl.hpp" +#include "ConnectionImpl.hpp" +#include "EngineStore.hpp" +#include "ObjectSender.hpp" +#include "util.hpp" + +#define LOG(s) s << "[ClientBroadcaster] " + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { + +/** Register a client to receive messages over the notification band. + */ +void +ClientBroadcaster::register_client(const URI& uri, ClientInterface* client) +{ + Clients::iterator i = _clients.find(uri); + + if (i == _clients.end()) { + _clients[uri] = client; + LOG(info) << "Registered client: " << uri << endl; + } else { + LOG(warn) << "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 URI& uri) +{ + size_t erased = _clients.erase(uri); + + if (erased > 0) + LOG(info) << "Unregistered client: " << uri << endl; + else + LOG(warn) << "Failed to find client to unregister: " << uri << endl; + + return (erased > 0); +} + +/** Looks up the client with the given source @a uri (which is used as the + * unique identifier for registered clients). + */ +ClientInterface* +ClientBroadcaster::client(const URI& uri) +{ + Clients::iterator i = _clients.find(uri); + if (i != _clients.end()) { + return (*i).second; + } else { + return NULL; + } +} + +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_plugins_to(ClientInterface* client, const NodeFactory::Plugins& plugins) +{ + client->bundle_begin(); + + for (NodeFactory::Plugins::const_iterator i = plugins.begin(); i != plugins.end(); ++i) { + const PluginImpl* const plugin = i->second; + client->put(plugin->uri(), plugin->properties()); + } + + client->bundle_end(); +} + +/** Send an object to all clients. + * + * @param o Object to send + * @param recursive If true send all children of object + */ +void +ClientBroadcaster::send_object(const GraphObjectImpl* o, bool recursive) +{ + for (Clients::const_iterator i = _clients.begin(); i != _clients.end(); ++i) + ObjectSender::send_object((*i).second, o, recursive); +} + +} // namespace Server +} // namespace Ingen diff --git a/src/server/ClientBroadcaster.hpp b/src/server/ClientBroadcaster.hpp new file mode 100644 index 00000000..5db6961d --- /dev/null +++ b/src/server/ClientBroadcaster.hpp @@ -0,0 +1,131 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_CLIENTBROADCASTER_HPP +#define INGEN_ENGINE_CLIENTBROADCASTER_HPP + +#include +#include +#include +#include +#include "raul/SharedPtr.hpp" +#include "ingen/ClientInterface.hpp" +#include "NodeFactory.hpp" + +using namespace std; + +namespace Ingen { +namespace Server { + +class GraphObjectImpl; +class NodeImpl; +class PortImpl; +class PluginImpl; +class PatchImpl; +class ConnectionImpl; + +/** Broadcaster for all clients. + * + * This is a ClientInterface that forwards all messages to all registered + * clients (for updating all clients on state changes in the engine). + * + * \ingroup engine + */ +class ClientBroadcaster : public ClientInterface +{ +public: + void register_client(const Raul::URI& uri, ClientInterface* client); + bool unregister_client(const Raul::URI& uri); + + ClientInterface* client(const Raul::URI& uri); + + void send_plugins(const NodeFactory::Plugins& plugin_list); + void send_plugins_to(ClientInterface*, const NodeFactory::Plugins& plugin_list); + + void send_object(const GraphObjectImpl* p, bool recursive); + +#define BROADCAST(msg, ...) \ + for (Clients::const_iterator i = _clients.begin(); i != _clients.end(); ++i) \ + (*i).second->msg(__VA_ARGS__) + + // CommonInterface + + void bundle_begin() { BROADCAST(bundle_begin); } + void bundle_end() { BROADCAST(bundle_end); } + + void put(const Raul::URI& uri, + const Resource::Properties& properties, + Resource::Graph ctx=Resource::DEFAULT) { + BROADCAST(put, uri, properties); + } + + void delta(const Raul::URI& uri, + const Resource::Properties& remove, + const Resource::Properties& add) { + BROADCAST(delta, uri, remove, add); + } + + void move(const Raul::Path& old_path, + const Raul::Path& new_path) { + BROADCAST(move, old_path, new_path); + } + + void del(const Raul::URI& uri) { + BROADCAST(del, uri); + } + + void connect(const Raul::Path& src_port_path, + const Raul::Path& dst_port_path) { + BROADCAST(connect, src_port_path, dst_port_path); + } + + void disconnect(const Raul::URI& src, + const Raul::URI& dst) { + BROADCAST(disconnect, src, dst); + } + + void disconnect_all(const Raul::Path& parent_patch_path, + const Raul::Path& path) { + BROADCAST(disconnect_all, parent_patch_path, path); + } + + void set_property(const Raul::URI& subject, + const Raul::URI& predicate, + const Raul::Atom& value) { + BROADCAST(set_property, subject, predicate, value); + } + + // ClientInterface + + Raul::URI uri() const { return "http://drobilla.net/ns/ingen#broadcaster"; } ///< N/A + + void response_ok(int32_t id) {} ///< N/A + void response_error(int32_t id, const std::string& msg) {} ///< N/A + + void error(const std::string& msg) { BROADCAST(error, msg); } + void activity(const Raul::Path& path) { BROADCAST(activity, path); } + +private: + typedef std::map Clients; + Clients _clients; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_CLIENTBROADCASTER_HPP + diff --git a/src/server/CompiledPatch.hpp b/src/server/CompiledPatch.hpp new file mode 100644 index 00000000..65ee9fad --- /dev/null +++ b/src/server/CompiledPatch.hpp @@ -0,0 +1,76 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_COMPILEDPATCH_HPP +#define INGEN_ENGINE_COMPILEDPATCH_HPP + +#include +#include "raul/List.hpp" +#include "raul/Deletable.hpp" +#include + +namespace Ingen { +namespace Server { + +class ConnectionImpl; + +/** All information required about a node to execute it in an audio thread. + */ +struct CompiledNode { + CompiledNode(NodeImpl* n, size_t np, Raul::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 (Raul::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 std::vector& dependants() const { return _dependants; } + +private: + NodeImpl* _node; + size_t _n_providers; ///< Number of input ready signals to trigger run + std::vector _dependants; ///< Nodes this one's output ports are connected to +}; + +/** A patch ``compiled'' into a flat structure with the correct order so + * the audio thread(s) can execute it without threading problems (since + * the preprocessor thread modifies the graph). + * + * The nodes contained here are sorted in the order they must be executed. + * The parallel processing algorithm guarantees no node will be executed + * before its providers, using this order as well as semaphores. + */ +struct CompiledPatch : public std::vector + , public Raul::Deletable + , public boost::noncopyable +{ + typedef std::vector QueuedConnections; + + /** All (audio context => other context) connections */ + std::vector queued_connections; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_COMPILEDPATCH_HPP diff --git a/src/server/ConnectionImpl.cpp b/src/server/ConnectionImpl.cpp new file mode 100644 index 00000000..1e339910 --- /dev/null +++ b/src/server/ConnectionImpl.cpp @@ -0,0 +1,153 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "raul/log.hpp" +#include "raul/Maid.hpp" +#include "raul/IntrusivePtr.hpp" +#include "shared/LV2URIMap.hpp" +#include "AudioBuffer.hpp" +#include "BufferFactory.hpp" +#include "ConnectionImpl.hpp" +#include "Engine.hpp" +#include "EventBuffer.hpp" +#include "InputPort.hpp" +#include "MessageContext.hpp" +#include "OutputPort.hpp" +#include "PortImpl.hpp" +#include "ProcessContext.hpp" +#include "mix.hpp" +#include "util.hpp" + +namespace Ingen { +namespace Server { + +/** Constructor for a connection from a node's output port. + * + * This handles both polyphonic and monophonic nodes, transparently to the + * user (InputPort). + */ +ConnectionImpl::ConnectionImpl(BufferFactory& bufs, PortImpl* src_port, PortImpl* dst_port) + : _queue(NULL) + , _bufs(bufs) + , _src_port(src_port) + , _dst_port(dst_port) + , _pending_disconnection(false) +{ + assert(src_port); + assert(dst_port); + assert(src_port != dst_port); + assert(src_port->path() != dst_port->path()); + + if (must_queue()) + _queue = new Raul::RingBuffer(src_port->buffer_size() * 2); +} + +void +ConnectionImpl::dump() const +{ + debug << _src_port->path() << " -> " << _dst_port->path() + << (must_mix() ? " (MIX) " : " (DIRECT) ") + << (must_queue() ? " (QUEUE)" : " (NOQUEUE) ") + << "POLY: " << _src_port->poly() << " => " << _dst_port->poly() << endl; +} + +void +ConnectionImpl::get_sources(Context& context, uint32_t voice, + IntrusivePtr* srcs, uint32_t max_num_srcs, uint32_t& num_srcs) +{ + if (must_queue() && _queue->read_space() > 0) { + LV2_Atom obj; + _queue->peek(sizeof(LV2_Atom), &obj); + IntrusivePtr buf = context.engine().buffer_factory()->get( + dst_port()->buffer_type(), sizeof(LV2_Atom) + obj.size); + void* data = buf->port_data(PortType::MESSAGE, context.offset()); + _queue->full_read(sizeof(LV2_Atom) + obj.size, (LV2_Atom*)data); + srcs[num_srcs++] = buf; + } else if (must_mix()) { + // Mixing down voices: every src voice mixed into every dst voice + for (uint32_t v = 0; v < _src_port->poly(); ++v) { + assert(num_srcs < max_num_srcs); + srcs[num_srcs++] = _src_port->buffer(v).get(); + } + } else { + // Matching polyphony: each src voice mixed into corresponding dst voice + assert(_src_port->poly() == _dst_port->poly()); + assert(num_srcs < max_num_srcs); + srcs[num_srcs++] = _src_port->buffer(voice).get(); + } +} + +void +ConnectionImpl::queue(Context& context) +{ + if (!must_queue()) + return; + + IntrusivePtr src_buf = PtrCast(_src_port->buffer(0)); + if (!src_buf) { + error << "Queued connection but source is not an EventBuffer" << endl; + return; + } + + for (src_buf->rewind(); src_buf->is_valid(); src_buf->increment()) { + LV2_Event* ev = src_buf->get_event(); + LV2_Atom* obj = LV2_ATOM_FROM_EVENT(ev); + /*debug << _src_port->path() << " -> " << _dst_port->path() + << " QUEUE OBJECT TYPE " << obj->type << ":"; + for (size_t i = 0; i < obj->size; ++i) + debug << " " << std::hex << (int)obj->body[i]; + debug << endl;*/ + + _queue->write(sizeof(LV2_Atom) + obj->size, obj); + context.engine().message_context()->run(_dst_port->parent_node(), context.start() + ev->frames); + } +} + +bool +ConnectionImpl::can_connect(const OutputPort* src, const InputPort* dst) +{ + const Ingen::Shared::LV2URIMap& uris = src->bufs().uris(); + return ( + // (Audio | Control) => (Audio | Control) + ( (src->is_a(PortType::CONTROL) || src->is_a(PortType::AUDIO)) + && (dst->is_a(PortType::CONTROL) || dst->is_a(PortType::AUDIO))) + + // (Events | Message) => (Events | Message) + || ( (src->is_a(PortType::EVENTS) || src->is_a(PortType::MESSAGE)) + && (dst->is_a(PortType::EVENTS) || dst->is_a(PortType::MESSAGE))) + + // (Message | Value) => (Message | Value) + || ( (src->is_a(PortType::MESSAGE) || src->is_a(PortType::VALUE)) + && (dst->is_a(PortType::MESSAGE) || dst->is_a(PortType::VALUE))) + + // Control => atom:Float32 Value + || (src->is_a(PortType::CONTROL) && dst->supports(uris.atom_Float32)) + + // Audio => atom:Vector Value + || (src->is_a(PortType::AUDIO) && dst->supports(uris.atom_Vector)) + + // atom:Float32 Value => Control + || (src->supports(uris.atom_Float32) && dst->is_a(PortType::CONTROL)) + + // atom:Vector Value => Audio + || (src->supports(uris.atom_Vector) && dst->is_a(PortType::AUDIO))); +} + +} // namespace Server +} // namespace Ingen + diff --git a/src/server/ConnectionImpl.hpp b/src/server/ConnectionImpl.hpp new file mode 100644 index 00000000..ce30eaa4 --- /dev/null +++ b/src/server/ConnectionImpl.hpp @@ -0,0 +1,110 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_CONNECTIONIMPL_HPP +#define INGEN_ENGINE_CONNECTIONIMPL_HPP + +#include +#include +#include "raul/log.hpp" +#include "raul/Deletable.hpp" +#include "raul/IntrusivePtr.hpp" +#include "ingen/PortType.hpp" +#include "ingen/Connection.hpp" +#include "lv2/lv2plug.in/ns/ext/atom/atom.h" +#include "PortImpl.hpp" + +using namespace std; + +namespace Ingen { +namespace Server { + +class PortImpl; +class OutputPort; +class InputPort; +class Buffer; +class BufferFactory; + +/** 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 Connection +{ +public: + ConnectionImpl(BufferFactory& bufs, PortImpl* src_port, PortImpl* dst_port); + + 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 queue(Context& context); + + void get_sources(Context& context, uint32_t voice, + IntrusivePtr* srcs, uint32_t max_num_srcs, uint32_t& num_srcs); + + /** Get the buffer for a particular voice. + * A Connection is smart - it knows the destination port requesting the + * buffer, and will return accordingly (e.g. the same buffer for every + * voice in a mono->poly connection). + */ + inline BufferFactory::Ref buffer(uint32_t voice) const { + assert(!must_mix()); + assert(!must_queue()); + assert(_src_port->poly() == 1 || _src_port->poly() > voice); + if (_src_port->poly() == 1) { + return _src_port->buffer(0); + } else { + return _src_port->buffer(voice); + } + } + + /** Returns true if this connection must mix down voices into a local buffer */ + inline bool must_mix() const { return _src_port->poly() > _dst_port->poly(); } + + /** Returns true if this connection crosses contexts and must buffer */ + inline bool must_queue() const { return _src_port->context() != _dst_port->context(); } + + static bool can_connect(const OutputPort* src, const InputPort* dst); + +protected: + void dump() const; + + Raul::RingBuffer* _queue; + + BufferFactory& _bufs; + PortImpl* const _src_port; + PortImpl* const _dst_port; + bool _pending_disconnection; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_CONNECTIONIMPL_HPP diff --git a/src/server/Context.hpp b/src/server/Context.hpp new file mode 100644 index 00000000..d2d1b11f --- /dev/null +++ b/src/server/Context.hpp @@ -0,0 +1,107 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_CONTEXT_HPP +#define INGEN_ENGINE_CONTEXT_HPP + +#include "shared/World.hpp" + +#include "Engine.hpp" +#include "EventSink.hpp" +#include "types.hpp" + +namespace Ingen { +namespace Server { + +class Engine; + +/** Graph execution context. + * + * This is used to pass whatever information a GraphObject might need to + * process; such as the current time, a sink for generated events, etc. + * + * Note the logical distinction between nframes (jack relative) and start/end + * (timeline relative). If transport speed != 1.0, then end-start != nframes + * (though currently this is never the case, it may be if ingen incorporates + * tempo and varispeed). + * + * \ingroup engine + */ +class Context +{ +public: + enum ID { + AUDIO, + MESSAGE + }; + + Context(Engine& engine, ID id) + : _engine(engine) + , _id(id) + , _event_sink(engine, engine.event_queue_size()) + , _start(0) + , _end(0) + , _nframes(0) + , _offset(0) + , _realtime(true) + {} + + virtual ~Context() {} + + ID id() const { return _id; } + + void locate(FrameTime s, SampleCount nframes, SampleCount offset) { + _start = s; + _end = s + nframes; + _nframes = nframes; + _offset = offset; + } + + void locate(const Context& other) { + _start = other._start; + _end = other._end; + _nframes = other._nframes; + _offset = other._offset; + } + + inline Engine& engine() const { return _engine; } + inline FrameTime start() const { return _start; } + inline FrameTime end() const { return _end; } + inline SampleCount nframes() const { return _nframes; } + inline SampleCount offset() const { return _offset; } + inline bool realtime() const { return _realtime; } + + inline const EventSink& event_sink() const { return _event_sink; } + inline EventSink& event_sink() { return _event_sink; } + +protected: + Engine& _engine; ///< Engine we're running in + ID _id; ///< Fast ID for this context + + EventSink _event_sink; ///< Sink for events generated in a realtime context + FrameTime _start; ///< Start frame of this cycle, timeline relative + FrameTime _end; ///< End frame of this cycle, timeline relative + SampleCount _nframes; ///< Length of this cycle in frames + SampleCount _offset; ///< Start offset relative to start of driver buffers + bool _realtime; ///< True iff context is hard realtime +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_CONTEXT_HPP + diff --git a/src/server/ControlBindings.cpp b/src/server/ControlBindings.cpp new file mode 100644 index 00000000..6c2cf09c --- /dev/null +++ b/src/server/ControlBindings.cpp @@ -0,0 +1,384 @@ +/* This file is part of Ingen. + * Copyright 2009-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "raul/log.hpp" +#include "raul/midi_events.h" +#include "shared/LV2URIMap.hpp" +#include "shared/World.hpp" +#include "events/SendPortValue.hpp" +#include "events/SendBinding.hpp" +#include "AudioBuffer.hpp" +#include "ControlBindings.hpp" +#include "Engine.hpp" +#include "EventBuffer.hpp" +#include "PortImpl.hpp" +#include "ProcessContext.hpp" +#include "ThreadManager.hpp" + +#define LOG(s) s << "[ControlBindings] " + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { + +ControlBindings::ControlBindings(Engine& engine) + : _engine(engine) + , _learn_port(NULL) + , _bindings(new Bindings()) + , _feedback(new EventBuffer(*_engine.buffer_factory(), 1024)) // FIXME: size +{ +} + +ControlBindings::~ControlBindings() +{ + delete _feedback; +} + +ControlBindings::Key +ControlBindings::port_binding(PortImpl* port) +{ + const Ingen::Shared::LV2URIMap& uris = *_engine.world()->uris().get(); + const Raul::Atom& binding = port->get_property(uris.ingen_controlBinding); + Key key; + if (binding.type() == Atom::DICT) { + const Atom::DictValue& dict = binding.get_dict(); + Atom::DictValue::const_iterator t = dict.find(uris.rdf_type); + Atom::DictValue::const_iterator n; + if (t == dict.end()) { + return key; + } else if (t->second == uris.midi_Bender) { + key = Key(MIDI_BENDER); + } else if (t->second == uris.midi_ChannelPressure) { + key = Key(MIDI_CHANNEL_PRESSURE); + } else if (t->second == uris.midi_Controller) { + if ((n = dict.find(uris.midi_controllerNumber)) != dict.end()) + key = Key(MIDI_CC, n->second.get_int32()); + } else if (t->second == uris.midi_Note) { + if ((n = dict.find(uris.midi_noteNumber)) != dict.end()) + key = Key(MIDI_NOTE, n->second.get_int32()); + } + } + return key; +} + +ControlBindings::Key +ControlBindings::midi_event_key(uint16_t size, uint8_t* buf, uint16_t& value) +{ + switch (buf[0] & 0xF0) { + case MIDI_CMD_CONTROL: + value = static_cast(buf[2]); + return Key(MIDI_CC, static_cast(buf[1])); + case MIDI_CMD_BENDER: + value = (static_cast(buf[2]) << 7) + static_cast(buf[1]); + return Key(MIDI_BENDER); + case MIDI_CMD_CHANNEL_PRESSURE: + value = static_cast(buf[1]); + return Key(MIDI_CHANNEL_PRESSURE); + case MIDI_CMD_NOTE_ON: + value = 1.0f; + return Key(MIDI_NOTE, static_cast(buf[1])); + default: + return Key(); + } +} + +void +ControlBindings::port_binding_changed(ProcessContext& context, PortImpl* port) +{ + Key key = port_binding(port); + if (key) + _bindings->insert(make_pair(key, port)); +} + +void +ControlBindings::port_value_changed(ProcessContext& context, PortImpl* port) +{ + const Ingen::Shared::LV2URIMap& uris = *_engine.world()->uris().get(); + Key key = port_binding(port); + if (key) { + int16_t value = port_value_to_control(port, key.type); + uint16_t size = 0; + uint8_t buf[4]; + switch (key.type) { + case MIDI_CC: + size = 3; + buf[0] = MIDI_CMD_CONTROL; + buf[1] = key.num; + buf[2] = static_cast(value); + break; + case MIDI_CHANNEL_PRESSURE: + size = 2; + buf[0] = MIDI_CMD_CHANNEL_PRESSURE; + buf[1] = static_cast(value); + break; + case MIDI_BENDER: + size = 3; + buf[0] = MIDI_CMD_BENDER; + buf[1] = (value & 0x007F); + buf[2] = (value & 0x7F00) >> 7; + break; + case MIDI_NOTE: + size = 3; + if (value == 1) + buf[0] = MIDI_CMD_NOTE_ON; + else if (value == 0) + buf[0] = MIDI_CMD_NOTE_OFF; + buf[1] = key.num; + buf[2] = 0x64; // MIDI spec default + break; + default: + break; + } + if (size > 0) { + _feedback->append(0, 0, + uris.global_to_event(uris.midi_MidiEvent.id).second, + size, buf); + } + } +} + +void +ControlBindings::learn(PortImpl* port) +{ + ThreadManager::assert_thread(THREAD_PRE_PROCESS); + _learn_port = port; +} + +Raul::Atom +ControlBindings::control_to_port_value(PortImpl* port, Type type, int16_t value) +{ + const Ingen::Shared::LV2URIMap& uris = *_engine.world()->uris().get(); + + // TODO: cache these to avoid the lookup + float min = port->get_property(uris.lv2_minimum).get_float(); + float max = port->get_property(uris.lv2_maximum).get_float(); + bool toggled = port->has_property(uris.lv2_portProperty, uris.lv2_toggled); + + float normal = 0.0f; + switch (type) { + case MIDI_CC: + case MIDI_CHANNEL_PRESSURE: + normal = (float)value / 127.0f; + break; + case MIDI_BENDER: + normal = (float)value / 16383.0f; + break; + case MIDI_NOTE: + normal = (value == 0.0f) ? 0.0f : 1.0f; + break; + default: + break; + } + + float scaled_value = normal * (max - min) + min; + if (toggled) + scaled_value = (scaled_value < 0.5) ? 0.0 : 1.0; + + return Raul::Atom(scaled_value); +} + +int16_t +ControlBindings::port_value_to_control(PortImpl* port, Type type) +{ + if (port->value().type() != Atom::FLOAT) + return 0; + + const Ingen::Shared::LV2URIMap& uris = *_engine.world()->uris().get(); + + // TODO: cache these to avoid the lookup + float min = port->get_property(uris.lv2_minimum).get_float(); + float max = port->get_property(uris.lv2_maximum).get_float(); + //bool toggled = port->has_property(uris.lv2_portProperty, uris.lv2_toggled); + float value = port->value().get_float(); + float normal = (value - min) / (max - min); + + if (normal < 0.0f) { + warn << "Value " << value << " (normal " << normal << ") for " + << port->path() << " out of range" << endl; + normal = 0.0f; + } + + if (normal > 1.0f) { + warn << "Value " << value << " (normal " << normal << ") for " + << port->path() << " out of range" << endl; + normal = 1.0f; + } + + switch (type) { + case MIDI_CC: + case MIDI_CHANNEL_PRESSURE: + return lrintf(normal * 127.0f); + case MIDI_BENDER: + return lrintf(normal * 16383.0f); + case MIDI_NOTE: + return (value > 0.0f) ? 1 : 0; + default: + return 0; + } +} + +void +ControlBindings::set_port_value(ProcessContext& context, PortImpl* port, Type type, int16_t value) +{ + const Raul::Atom port_value(control_to_port_value(port, type, value)); + port->set_value(port_value); + + assert(port_value.type() == Atom::FLOAT); + assert(dynamic_cast(port->buffer(0).get())); + + for (uint32_t v = 0; v < port->poly(); ++v) + reinterpret_cast(port->buffer(v).get())->set_value( + port_value.get_float(), context.start(), context.start()); + + const Events::SendPortValue ev(context.engine(), context.start(), port, true, 0, port_value); + context.event_sink().write(sizeof(ev), &ev); +} + +bool +ControlBindings::bind(ProcessContext& context, Key key) +{ + const Ingen::Shared::LV2URIMap& uris = *context.engine().world()->uris().get(); + assert(_learn_port); + if (key.type == MIDI_NOTE) { + bool toggled = _learn_port->has_property(uris.lv2_portProperty, uris.lv2_toggled); + if (!toggled) + return false; + } + + _bindings->insert(make_pair(key, _learn_port)); + + const Events::SendBinding ev(context.engine(), context.start(), _learn_port, key.type, key.num); + context.event_sink().write(sizeof(ev), &ev); + + _learn_port = NULL; + return true; +} + +SharedPtr +ControlBindings::remove(const Raul::Path& path) +{ + ThreadManager::assert_thread(THREAD_PRE_PROCESS); + + SharedPtr old_bindings(_bindings); + SharedPtr copy(new Bindings(*_bindings.get())); + + for (Bindings::iterator i = copy->begin(); i != copy->end();) { + Bindings::iterator next = i; + ++next; + + if (i->second->path() == path || i->second->path().is_child_of(path)) + copy->erase(i); + + i = next; + } + + _bindings = copy; + return old_bindings; +} + +SharedPtr +ControlBindings::remove(PortImpl* port) +{ + ThreadManager::assert_thread(THREAD_PRE_PROCESS); + + SharedPtr old_bindings(_bindings); + SharedPtr copy(new Bindings(*_bindings.get())); + + for (Bindings::iterator i = copy->begin(); i != copy->end();) { + Bindings::iterator next = i; + ++next; + + if (i->second == port) + copy->erase(i); + + i = next; + } + + _bindings = copy; + return old_bindings; +} + +void +ControlBindings::pre_process(ProcessContext& context, EventBuffer* buffer) +{ + uint32_t frames = 0; + uint32_t subframes = 0; + uint16_t type = 0; + uint16_t size = 0; + uint8_t* buf = NULL; + uint16_t value = 0; + + SharedPtr bindings = _bindings; + _feedback->clear(); + + const Ingen::Shared::LV2URIMap& uris = *context.engine().world()->uris().get(); + + // TODO: cache + const uint32_t midi_event_type = uris.global_to_event(uris.midi_MidiEvent.id).second; + + // Learn from input if necessary + if (_learn_port) { + for (buffer->rewind(); + buffer->get_event(&frames, &subframes, &type, &size, &buf); + buffer->increment()) { + if (type != midi_event_type) + continue; + + const Key key = midi_event_key(size, buf, value); + if (key && bind(context, key)) + break; + } + } + + // If bindings are empty, no sense reading input + if (bindings->empty()) + return; + + // Read input and apply control values + for (buffer->rewind(); + buffer->get_event(&frames, &subframes, &type, &size, &buf); + buffer->increment()) { + if (type != midi_event_type) + continue; + + const Key key = midi_event_key(size, buf, value); + if (!key) + continue; + + Bindings::const_iterator i = bindings->find(key); + if (i == bindings->end()) + continue; + + set_port_value(context, i->second, key.type, value); + } +} + +void +ControlBindings::post_process(ProcessContext& context, EventBuffer* buffer) +{ + if (_feedback->event_count() > 0) { + // TODO: merge buffer's existing contents (anything send to it in the patch) + _feedback->rewind(); + buffer->copy(context, _feedback); + } +} + +} // namespace Server +} // namespace Ingen diff --git a/src/server/ControlBindings.hpp b/src/server/ControlBindings.hpp new file mode 100644 index 00000000..92eee7c7 --- /dev/null +++ b/src/server/ControlBindings.hpp @@ -0,0 +1,102 @@ +/* This file is part of Ingen. + * Copyright 2009-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_CONTROLBINDINGS_HPP +#define INGEN_ENGINE_CONTROLBINDINGS_HPP + +#include +#include +#include "raul/SharedPtr.hpp" +#include "raul/Path.hpp" +#include "shared/LV2URIMap.hpp" +#include "BufferFactory.hpp" + +namespace Ingen { +namespace Server { + +class Engine; +class ProcessContext; +class EventBuffer; +class PortImpl; + +class ControlBindings { +public: + enum Type { + NULL_CONTROL, + MIDI_BENDER, + MIDI_CC, + MIDI_RPN, + MIDI_NRPN, + MIDI_CHANNEL_PRESSURE, + MIDI_NOTE + }; + + struct Key { + Key(Type t=NULL_CONTROL, int16_t n=0) : type(t), num(n) {} + inline bool operator<(const Key& other) const { + return (type == other.type) ? (num < other.num) : (type < other.type); + } + inline operator bool() const { return type != NULL_CONTROL; } + Type type; + int16_t num; + }; + + typedef std::map Bindings; + + explicit ControlBindings(Engine& engine); + ~ControlBindings(); + + void learn(PortImpl* port); + + void port_binding_changed(ProcessContext& context, PortImpl* port); + void port_value_changed(ProcessContext& context, PortImpl* port); + void pre_process(ProcessContext& context, EventBuffer* control_in); + void post_process(ProcessContext& context, EventBuffer* control_out); + + /** Remove all bindings for @a path or children of @a path. + * The caller must safely drop the returned reference in the + * post-processing thread after at least one process thread has run. + */ + SharedPtr remove(const Raul::Path& path); + + /** Remove binding for a particular port. + * The caller must safely drop the returned reference in the + * post-processing thread after at least one process thread has run. + */ + SharedPtr remove(PortImpl* port); + +private: + Key port_binding(PortImpl* port); + Key midi_event_key(uint16_t size, uint8_t* buf, uint16_t& value); + + void set_port_value(ProcessContext& context, PortImpl* port, Type type, int16_t value); + bool bind(ProcessContext& context, Key key); + + Raul::Atom control_to_port_value(PortImpl* port, Type type, int16_t value); + int16_t port_value_to_control(PortImpl* port, Type type); + + Engine& _engine; + PortImpl* _learn_port; + + SharedPtr _bindings; + EventBuffer* _feedback; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_CONTROLBINDINGS_HPP diff --git a/src/server/Driver.hpp b/src/server/Driver.hpp new file mode 100644 index 00000000..f9416e3e --- /dev/null +++ b/src/server/Driver.hpp @@ -0,0 +1,123 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_DRIVER_HPP +#define INGEN_ENGINE_DRIVER_HPP + +#include +#include +#include "raul/Deletable.hpp" +#include "ingen/PortType.hpp" +#include "ingen/EventType.hpp" +#include "DuplexPort.hpp" + +namespace Raul { class Path; } + +namespace Ingen { +namespace Server { + +class DuplexPort; +class ProcessContext; + +/** 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 Raul::Deletable { +public: + virtual ~DriverPort() {} + + /** Set the name of the system port according to new path */ + virtual void move(const Raul::Path& path) = 0; + + /** Create system port */ + virtual void create() = 0; + + /** Destroy system port */ + virtual void destroy() = 0; + + bool is_input() const { return _patch_port->is_input(); } + DuplexPort* patch_port() const { return _patch_port; } + +protected: + explicit 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: + virtual ~Driver() {} + + /** Activate driver (begin processing graph and events). */ + virtual void activate() {} + + /** Deactivate driver (stop processing graph and events). */ + virtual void deactivate() {} + + /** Create a port ready to be inserted with add_input (non realtime). + * May return NULL if the Driver can not create the port for some reason. + */ + virtual DriverPort* create_port(DuplexPort* patch_port) = 0; + + /** Return the DriverPort for a particular path, iff one exists. */ + virtual DriverPort* driver_port(const Raul::Path& path) = 0; + + /** Add a system visible port (e.g. a port on the root patch). */ + virtual void add_port(DriverPort* port) = 0; + + /** Remove a system visible port. */ + virtual Raul::Deletable* remove_port(const Raul::Path& path, + DriverPort** port=NULL) = 0; + + /** Return true iff this driver supports the given type of I/O */ + virtual bool supports(PortType port_type, + EventType event_type) = 0; + + virtual void set_root_patch(PatchImpl* patch) = 0; + virtual PatchImpl* root_patch() = 0; + + /** Return the audio buffer size in frames */ + virtual SampleCount block_length() const = 0; + + /** Return the sample rate in Hz */ + virtual SampleCount sample_rate() const = 0; + + /** Return the current frame time (running counter) */ + virtual SampleCount frame_time() const = 0; + + virtual bool is_realtime() const = 0; + + virtual ProcessContext& context() = 0; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_DRIVER_HPP diff --git a/src/server/DuplexPort.cpp b/src/server/DuplexPort.cpp new file mode 100644 index 00000000..773aab37 --- /dev/null +++ b/src/server/DuplexPort.cpp @@ -0,0 +1,99 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "shared/LV2URIMap.hpp" + +#include "ConnectionImpl.hpp" +#include "DuplexPort.hpp" +#include "EventBuffer.hpp" +#include "NodeImpl.hpp" +#include "OutputPort.hpp" +#include "ProcessContext.hpp" +#include "util.hpp" + +using namespace std; + +namespace Ingen { +namespace Server { + +DuplexPort::DuplexPort( + BufferFactory& bufs, + NodeImpl* parent, + const string& name, + uint32_t index, + bool polyphonic, + uint32_t poly, + PortType type, + const Raul::Atom& value, + size_t buffer_size, + bool is_output) + : PortImpl(bufs, parent, name, index, poly, type, value, buffer_size) + , InputPort(bufs, parent, name, index, poly, type, value, buffer_size) + , OutputPort(bufs, parent, name, index, poly, type, value, buffer_size) + , _is_output(is_output) +{ + assert(PortImpl::_parent == parent); + set_property(bufs.uris().ingen_polyphonic, polyphonic); +} + +bool +DuplexPort::get_buffers(BufferFactory& bufs, Raul::Array* buffers, uint32_t poly) +{ + if (_is_output) + return InputPort::get_buffers(bufs, buffers, poly); + else + return OutputPort::get_buffers(bufs, buffers, poly); +} + +/** Prepare for the execution of parent patch */ +void +DuplexPort::pre_process(Context& context) +{ + // If we're a patch output, we're an input from the internal perspective. + // Prepare buffers for write (so plugins can deliver to them) + if (_is_output) { + for (uint32_t v = 0; v < _poly; ++v) + _buffers->at(v)->prepare_write(context); + + // If we're a patch input, were an output from the internal perspective. + // Do whatever a normal node's input port does to prepare input for reading. + } else { + InputPort::pre_process(context); + } +} + +/** Finalize after the execution of parent patch (deliver outputs) */ +void +DuplexPort::post_process(Context& context) +{ + // If we're a patch output, we're an input from the internal perspective. + // Mix down input delivered by plugins so output (external perspective) is ready. + if (_is_output) { + InputPort::pre_process(context); + + if (_broadcast) + broadcast_value(context, false); + } +} + +} // namespace Server +} // namespace Ingen + diff --git a/src/server/DuplexPort.hpp b/src/server/DuplexPort.hpp new file mode 100644 index 00000000..ed00e072 --- /dev/null +++ b/src/server/DuplexPort.hpp @@ -0,0 +1,70 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_DUPLEXPORT_HPP +#define INGEN_ENGINE_DUPLEXPORT_HPP + +#include +#include "Buffer.hpp" +#include "InputPort.hpp" +#include "OutputPort.hpp" + +namespace Ingen { +namespace Server { + +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(BufferFactory& bufs, + NodeImpl* parent, + const std::string& name, + uint32_t index, + bool polyphonic, + uint32_t poly, + PortType type, + const Raul::Atom& value, + size_t buffer_size, + bool is_output); + + virtual ~DuplexPort() {} + + bool get_buffers(BufferFactory& bufs, Raul::Array* buffers, uint32_t poly); + + void pre_process(Context& context); + void post_process(Context& context); + + bool is_input() const { return !_is_output; } + bool is_output() const { return _is_output; } + +protected: + bool _is_output; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_DUPLEXPORT_HPP diff --git a/src/server/Engine.cpp b/src/server/Engine.cpp new file mode 100644 index 00000000..4a4a9c0f --- /dev/null +++ b/src/server/Engine.cpp @@ -0,0 +1,225 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "raul/log.hpp" +#include "raul/Deletable.hpp" +#include "raul/Maid.hpp" +#include "raul/SharedPtr.hpp" +#include "lv2/lv2plug.in/ns/ext/uri-map/uri-map.h" +#include "ingen/EventType.hpp" +#include "events/CreatePatch.hpp" +#include "events/CreatePort.hpp" +#include "shared/World.hpp" +#include "shared/LV2Features.hpp" +#include "shared/LV2URIMap.hpp" +#include "shared/Store.hpp" +#include "BufferFactory.hpp" +#include "ClientBroadcaster.hpp" +#include "ControlBindings.hpp" +#include "Driver.hpp" +#include "Engine.hpp" +#include "EngineStore.hpp" +#include "Event.hpp" +#include "EventSource.hpp" +#include "MessageContext.hpp" +#include "NodeFactory.hpp" +#include "PatchImpl.hpp" +#include "PostProcessor.hpp" +#include "ProcessContext.hpp" +#include "QueuedEngineInterface.hpp" +#include "ThreadManager.hpp" + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { + +bool ThreadManager::single_threaded = true; + +Engine::Engine(Ingen::Shared::World* a_world) + : _world(a_world) + , _broadcaster(new ClientBroadcaster()) + , _buffer_factory(new BufferFactory(*this, a_world->uris())) + , _control_bindings(new ControlBindings(*this)) + , _maid(new Raul::Maid(event_queue_size())) + , _message_context(new MessageContext(*this)) + , _node_factory(new NodeFactory(a_world)) + , _post_processor(new PostProcessor(*this, event_queue_size())) +{ + if (a_world->store()) { + assert(PtrCast(a_world->store())); + } else { + a_world->set_store(SharedPtr(new EngineStore())); + } +} + +Engine::~Engine() +{ + deactivate(); + + SharedPtr store = engine_store(); + if (store) + for (EngineStore::iterator i = store->begin(); i != store->end(); ++i) + if ( ! PtrCast(i->second)->parent() ) + i->second.reset(); + + delete _maid; + delete _post_processor; + delete _node_factory; + delete _broadcaster; + + munlockall(); +} + +SharedPtr +Engine::engine_store() const +{ + return PtrCast(_world->store()); +} + +size_t +Engine::event_queue_size() const +{ + return world()->conf()->option("queue-size").get_int32(); +} + +void +Engine::quit() +{ + _quit_flag = true; +} + +bool +Engine::main_iteration() +{ + _post_processor->process(); + _maid->cleanup(); + return !_quit_flag; +} + +void +Engine::add_event_source(SharedPtr source) +{ + _event_sources.insert(source); +} + +void +Engine::set_driver(SharedPtr driver) +{ + _driver = driver; +} + +static void +execute_and_delete_event(ProcessContext& context, QueuedEvent* ev) +{ + ev->pre_process(); + ev->execute(context); + ev->post_process(); + delete ev; +} + +bool +Engine::activate() +{ + assert(_driver); + ThreadManager::single_threaded = true; + + _buffer_factory->set_block_length(_driver->block_length()); + + _message_context->Thread::start(); + + const Ingen::Shared::LV2URIMap& uris = *world()->uris().get(); + + // Create root patch + PatchImpl* root_patch = _driver->root_patch(); + if (!root_patch) { + root_patch = new PatchImpl(*this, "root", 1, NULL, _driver->sample_rate(), 1); + root_patch->set_property(uris.rdf_type, + Resource::Property(uris.ingen_Patch, Resource::INTERNAL)); + root_patch->set_property(uris.ingen_polyphony, + Resource::Property(Raul::Atom(int32_t(1)), + Resource::INTERNAL)); + root_patch->activate(*_buffer_factory); + _world->store()->add(root_patch); + root_patch->compiled_patch(root_patch->compile()); + _driver->set_root_patch(root_patch); + + ProcessContext context(*this); + + Resource::Properties control_properties; + control_properties.insert(make_pair(uris.lv2_name, "Control")); + control_properties.insert(make_pair(uris.rdf_type, uris.ev_EventPort)); + + // Add control input + Resource::Properties in_properties(control_properties); + in_properties.insert(make_pair(uris.rdf_type, uris.lv2_InputPort)); + in_properties.insert(make_pair(uris.lv2_index, 0)); + in_properties.insert(make_pair(uris.ingenui_canvas_x, + Resource::Property(32.0f, Resource::EXTERNAL))); + in_properties.insert(make_pair(uris.ingenui_canvas_y, + Resource::Property(32.0f, Resource::EXTERNAL))); + + execute_and_delete_event(context, new Events::CreatePort( + *this, SharedPtr(), 0, + "/control_in", uris.ev_EventPort, false, in_properties)); + + // Add control out + Resource::Properties out_properties(control_properties); + out_properties.insert(make_pair(uris.rdf_type, uris.lv2_OutputPort)); + out_properties.insert(make_pair(uris.lv2_index, 1)); + out_properties.insert(make_pair(uris.ingenui_canvas_x, + Resource::Property(128.0f, Resource::EXTERNAL))); + out_properties.insert(make_pair(uris.ingenui_canvas_y, + Resource::Property(32.0f, Resource::EXTERNAL))); + + execute_and_delete_event(context, new Events::CreatePort( + *this, SharedPtr(), 0, + "/control_out", uris.ev_EventPort, true, out_properties)); + } + + _driver->activate(); + root_patch->enable(); + + ThreadManager::single_threaded = false; + + return true; +} + +void +Engine::deactivate() +{ + _driver->deactivate(); + _driver->root_patch()->deactivate(); + + ThreadManager::single_threaded = true; +} + +void +Engine::process_events(ProcessContext& context) +{ + ThreadManager::assert_thread(THREAD_PROCESS); + + for (EventSources::iterator i = _event_sources.begin(); i != _event_sources.end(); ++i) + (*i)->process(*_post_processor, context); +} + +} // namespace Server +} // namespace Ingen diff --git a/src/server/Engine.hpp b/src/server/Engine.hpp new file mode 100644 index 00000000..b3cf6688 --- /dev/null +++ b/src/server/Engine.hpp @@ -0,0 +1,115 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_ENGINE_HPP +#define INGEN_ENGINE_ENGINE_HPP + +#include +#include + +#include + +#include "raul/SharedPtr.hpp" + +#include "ingen/EngineBase.hpp" + +namespace Raul { class Maid; } + +namespace Ingen { + +namespace Shared { class World; } + +namespace Server { + +class BufferFactory; +class ClientBroadcaster; +class ControlBindings; +class Driver; +class EngineStore; +class EventSource; +class MessageContext; +class NodeFactory; +class PostProcessor; +class ProcessContext; + +/** + The engine which executes the process graph. + + This is a simple class that provides pointers to the various components + that make up the engine implementation. In processes with a local engine, + it can be accessed via the Ingen::Shared::World. + + @ingroup engine +*/ +class Engine : public boost::noncopyable, public EngineBase +{ +public: + explicit Engine(Ingen::Shared::World* world); + + virtual ~Engine(); + + virtual bool activate(); + + virtual void deactivate(); + + virtual void quit(); + + virtual bool main_iteration(); + + void set_driver(SharedPtr driver); + + void add_event_source(SharedPtr source); + + void process_events(ProcessContext& context); + + Ingen::Shared::World* world() const { return _world; } + + ClientBroadcaster* broadcaster() const { return _broadcaster; } + BufferFactory* buffer_factory() const { return _buffer_factory; } + ControlBindings* control_bindings() const { return _control_bindings; } + Driver* driver() const { return _driver.get(); } + Raul::Maid* maid() const { return _maid; } + MessageContext* message_context() const { return _message_context; } + NodeFactory* node_factory() const { return _node_factory; } + PostProcessor* post_processor() const { return _post_processor; } + + SharedPtr engine_store() const; + + size_t event_queue_size() const; + +private: + Ingen::Shared::World* _world; + + ClientBroadcaster* _broadcaster; + BufferFactory* _buffer_factory; + ControlBindings* _control_bindings; + SharedPtr _driver; + Raul::Maid* _maid; + MessageContext* _message_context; + NodeFactory* _node_factory; + PostProcessor* _post_processor; + + typedef std::set< SharedPtr > EventSources; + EventSources _event_sources; + + bool _quit_flag; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_ENGINE_HPP diff --git a/src/server/EngineStore.cpp b/src/server/EngineStore.cpp new file mode 100644 index 00000000..ee0dc933 --- /dev/null +++ b/src/server/EngineStore.cpp @@ -0,0 +1,160 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "raul/log.hpp" +#include "raul/List.hpp" +#include "raul/PathTable.hpp" +#include "raul/TableImpl.hpp" +#include "EngineStore.hpp" +#include "PatchImpl.hpp" +#include "NodeImpl.hpp" +#include "PortImpl.hpp" +#include "ThreadManager.hpp" + +#define LOG(s) s << "[EngineStore] " + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { + +/** 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) +{ + ThreadManager::assert_thread(THREAD_PRE_PROCESS); + Store::add(obj); +} + +/** Add a family of objects to the store. Not realtime safe. + */ +void +EngineStore::add(const Objects& table) +{ + ThreadManager::assert_thread(THREAD_PRE_PROCESS); + cram(table); +} + +/** 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) +{ + ThreadManager::assert_thread(THREAD_PRE_PROCESS); + + if (object != end()) { + iterator descendants_end = find_descendants_end(object); + SharedPtr removed = yank(object, descendants_end); + + return removed; + + } else { + LOG(warn) << "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 { + LOG(warn) << "Removing children of " << object->first << " failed." << endl; + return SharedPtr(); + } + + return SharedPtr(); +} + +} // namespace Server +} // namespace Ingen diff --git a/src/server/EngineStore.hpp b/src/server/EngineStore.hpp new file mode 100644 index 00000000..ba8dd000 --- /dev/null +++ b/src/server/EngineStore.hpp @@ -0,0 +1,65 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_OBJECTSTORE_HPP +#define INGEN_ENGINE_OBJECTSTORE_HPP + +#include "raul/SharedPtr.hpp" + +#include "shared/Store.hpp" + +namespace Ingen { + +class GraphObject; + +namespace Server { + +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 Ingen::Shared::Store +{ +public: + PatchImpl* find_patch(const Raul::Path& path); + NodeImpl* find_node(const Raul::Path& path); + PortImpl* find_port(const Raul::Path& path); + GraphObjectImpl* find_object(const Raul::Path& path); + + void add(Ingen::GraphObject* o); + void add(const Objects& family); + + SharedPtr remove(const Raul::Path& path); + SharedPtr remove(Objects::iterator i); + SharedPtr remove_children(const Raul::Path& path); + SharedPtr remove_children(Objects::iterator i); +}; + +} // namespace Server +} // namespace Ingen + +#endif // OBJECTSTORE diff --git a/src/server/Event.cpp b/src/server/Event.cpp new file mode 100644 index 00000000..f967bfac --- /dev/null +++ b/src/server/Event.cpp @@ -0,0 +1,56 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "Driver.hpp" +#include "Engine.hpp" +#include "Event.hpp" +#include "ProcessContext.hpp" +#include "ThreadManager.hpp" + +/*! \page methods Method Documentation + * + *

All changes in Ingen (both engine and client) occur as a result of + * a small set of methods defined in terms of RDF and matching the + * HTTP and WebDAV standards as closely as possible.

+ */ + +namespace Ingen { +namespace Server { + +void +Event::execute(ProcessContext& context) +{ + ThreadManager::assert_thread(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() +{ + ThreadManager::assert_not_thread(THREAD_PROCESS); +} + +} // namespace Server +} // namespace Ingen + diff --git a/src/server/Event.hpp b/src/server/Event.hpp new file mode 100644 index 00000000..af39bfa4 --- /dev/null +++ b/src/server/Event.hpp @@ -0,0 +1,80 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_EVENT_HPP +#define INGEN_ENGINE_EVENT_HPP + +#include +#include "raul/SharedPtr.hpp" +#include "raul/Deletable.hpp" +#include "raul/Path.hpp" +#include "types.hpp" + +namespace Ingen { +namespace Server { + +class Engine; +class Request; +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; } + + int error() { return _error; } + +protected: + Event(Engine& engine, SharedPtr request, FrameTime time) + : _engine(engine) + , _request(request) + , _time(time) + , _error(0) // success + , _executed(false) + {} + + Engine& _engine; + SharedPtr _request; + FrameTime _time; + int _error; + bool _executed; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_EVENT_HPP diff --git a/src/server/EventBuffer.cpp b/src/server/EventBuffer.cpp new file mode 100644 index 00000000..1e8713ba --- /dev/null +++ b/src/server/EventBuffer.cpp @@ -0,0 +1,215 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "raul/log.hpp" +#include "lv2/lv2plug.in/ns/ext/event/event.h" +#include "lv2/lv2plug.in/ns/ext/event/event-helpers.h" +#include "ingen-config.h" +#include "EventBuffer.hpp" +#include "ProcessContext.hpp" + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { + +/** Allocate a new event buffer. + * \a capacity is in bytes (not number of events). + */ +EventBuffer::EventBuffer(BufferFactory& bufs, size_t capacity) + : Buffer(bufs, PortType(PortType::EVENTS), capacity) + , _latest_frames(0) + , _latest_subframes(0) +{ + if (capacity > UINT32_MAX) { + error << "Event buffer size " << capacity << " too large, aborting." << endl; + throw std::bad_alloc(); + } + +#ifdef HAVE_POSIX_MEMALIGN + int ret = posix_memalign((void**)&_data, 16, sizeof(LV2_Event_Buffer) + capacity); +#else + _data = (LV2_Event_Buffer*)malloc(sizeof(LV2_Event_Buffer) + capacity); + int ret = (_data != NULL) ? 0 : -1; +#endif + + if (ret != 0) { + error << "Failed to allocate event buffer. Aborting." << endl; + exit(EXIT_FAILURE); + } + + _data->header_size = sizeof(LV2_Event_Buffer); + _data->data = reinterpret_cast(_data + _data->header_size); + _data->stamp_type = 0; + _data->event_count = 0; + _data->capacity = (uint32_t)capacity; + _data->size = 0; + + clear(); +} + +EventBuffer::~EventBuffer() +{ + free(_data); +} + +void +EventBuffer::prepare_read(Context& context) +{ + rewind(); +} + +void +EventBuffer::prepare_write(Context& context) +{ + if (context.offset() == 0) + clear(); +} + +void +EventBuffer::copy(Context& context, const Buffer* src_buf) +{ + const EventBuffer* src = dynamic_cast(src_buf); + if (src->_data == _data) + return; + + assert(src->_data->header_size == _data->header_size); + assert(capacity() >= _data->header_size + src->_data->size); + + rewind(); + + memcpy(_data, src->_data, _data->header_size + src->_data->size); + + _iter = src->_iter; + _iter.buf = _data; + + _latest_frames = src->_latest_frames; + _latest_subframes = src->_latest_subframes; + + assert(event_count() == src->event_count()); +} + +/** 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); +} + +/** 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; + } +} + +/** Get the object currently pointed to, or NULL if invalid. + */ +LV2_Atom* +EventBuffer::get_atom() const +{ + if (lv2_event_is_valid(&_iter)) { + uint8_t* data; + LV2_Event* ev = lv2_event_get(&_iter, &data); + return LV2_ATOM_FROM_EVENT(ev); + } + return NULL; +} + +/** Get the event currently pointed to, or NULL if invalid. + */ +LV2_Event* +EventBuffer::get_event() const +{ + if (lv2_event_is_valid(&_iter)) { + uint8_t* data; + return lv2_event_get(&_iter, &data); + } + return NULL; +} + +/** Append an event to the buffer. + * + * \a timestamp must be >= the latest event in the buffer. + * + * \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 + + /*debug << "Appending event type " << type << ", size " << size + << " @ " << frames << "." << subframes << endl;*/ + + if (!lv2_event_write(&_iter, frames, subframes, type, size, data)) { + error << "Failed to write event." << endl; + return false; + } else { + _latest_frames = frames; + _latest_subframes = subframes; + return true; + } +} + +} // namespace Server +} // namespace Ingen diff --git a/src/server/EventBuffer.hpp b/src/server/EventBuffer.hpp new file mode 100644 index 00000000..de67ae0c --- /dev/null +++ b/src/server/EventBuffer.hpp @@ -0,0 +1,86 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_EVENTBUFFER_HPP +#define INGEN_ENGINE_EVENTBUFFER_HPP + +#include "lv2/lv2plug.in/ns/ext/atom/atom.h" +#include "lv2/lv2plug.in/ns/ext/event/event.h" +#include "lv2/lv2plug.in/ns/ext/event/event-helpers.h" +#include "ingen/PortType.hpp" +#include "Buffer.hpp" + +namespace Ingen { +namespace Server { + +class EventBuffer : public Buffer { +public: + EventBuffer(BufferFactory& bufs, size_t capacity); + ~EventBuffer(); + + void* port_data(PortType port_type, SampleCount offset=0) { return _data; } + const void* port_data(PortType port_type, SampleCount offset=0) const { return _data; } + + inline void rewind() const { lv2_event_begin(&_iter, _data); } + + inline void clear() { + _latest_frames = 0; + _latest_subframes = 0; + _data->event_count = 0; + _data->size = 0; + rewind(); + } + + void prepare_read(Context& context); + void prepare_write(Context& context); + + void copy(Context& context, const Buffer* src); + + inline size_t event_count() const { return _data->event_count; } + inline uint32_t capacity() const { return _data->capacity; } + inline uint32_t latest_frames() const { return _latest_frames; } + inline uint32_t latest_subframes() const { return _latest_subframes; } + + bool increment() const; + bool is_valid() const; + + bool get_event(uint32_t* frames, + uint32_t* subframes, + uint16_t* type, + uint16_t* size, + uint8_t** data) const; + + LV2_Atom* get_atom() const; + LV2_Event* get_event() const; + + bool append(uint32_t frames, + uint32_t subframes, + uint16_t type, + uint16_t size, + const uint8_t* data); + +private: + LV2_Event_Buffer* _data; ///< Contents + mutable LV2_Event_Iterator _iter; ///< Iterator into _data + uint32_t _latest_frames; ///< Latest time of all events (frames) + uint32_t _latest_subframes; ///< Latest time of all events (subframes) +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_EVENTBUFFER_HPP diff --git a/src/server/EventSink.cpp b/src/server/EventSink.cpp new file mode 100644 index 00000000..bb81f3ec --- /dev/null +++ b/src/server/EventSink.cpp @@ -0,0 +1,62 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "EventSink.hpp" +#include "PortImpl.hpp" + +using namespace std; + +namespace Ingen { +namespace Server { + +/** \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 Server +} // namespace Ingen diff --git a/src/server/EventSink.hpp b/src/server/EventSink.hpp new file mode 100644 index 00000000..bbc3c324 --- /dev/null +++ b/src/server/EventSink.hpp @@ -0,0 +1,59 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_EVENTSINK_HPP +#define INGEN_ENGINE_EVENTSINK_HPP + +#include +#include +#include +#include "raul/RingBuffer.hpp" + +namespace Ingen { +namespace Server { + +class PortImpl; +class Engine; +class Event; + +/** 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) {} + + 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 Server +} // namespace Ingen + +#endif // INGEN_ENGINE_EVENTSINK_HPP + diff --git a/src/server/EventSource.cpp b/src/server/EventSource.cpp new file mode 100644 index 00000000..273a4693 --- /dev/null +++ b/src/server/EventSource.cpp @@ -0,0 +1,127 @@ +/* This file is part of Ingen. + * Copyright 2008-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "EventSource.hpp" +#include "QueuedEvent.hpp" +#include "PostProcessor.hpp" +#include "ThreadManager.hpp" +#include "ProcessContext.hpp" + +using namespace std; + +namespace Ingen { +namespace Server { + +EventSource::EventSource(size_t queue_size) + : _blocking_semaphore(0) +{ + Thread::set_context(THREAD_PRE_PROCESS); + set_name("EventSource"); +} + +EventSource::~EventSource() +{ + Thread::stop(); +} + +/** Push an unprepared event onto the queue. + */ +void +EventSource::push_queued(QueuedEvent* const ev) +{ + assert(!ev->is_prepared()); + Raul::List::Node* node = new Raul::List::Node(ev); + _events.push_back(node); + if (_prepared_back.get() == NULL) + _prepared_back = node; + + whip(); +} + +/** Process all events for a cycle. + * + * Executed events will be pushed to @a dest. + */ +void +EventSource::process(PostProcessor& dest, ProcessContext& context, bool limit) +{ + ThreadManager::assert_thread(THREAD_PROCESS); + + if (_events.empty()) + return; + + /* 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 size_t MAX_QUEUED_EVENTS = context.nframes() / 32; + + size_t num_events_processed = 0; + + Raul::List::Node* head = _events.head(); + Raul::List::Node* tail = head; + + if (!head) + return; + + QueuedEvent* ev = (QueuedEvent*)head->elem(); + + while (ev && ev->is_prepared() && ev->time() < context.end()) { + ev->execute(context); + tail = head; + head = head->next(); + ++num_events_processed; + if (limit && num_events_processed > MAX_QUEUED_EVENTS) + break; + ev = (head ? (QueuedEvent*)head->elem() : NULL); + } + + if (num_events_processed > 0) { + Raul::List front; + _events.chop_front(front, num_events_processed, tail); + dest.append(&front); + } +} + +/** Pre-process a single event */ +void +EventSource::_whipped() +{ + Raul::List::Node* pb = _prepared_back.get(); + if (!pb) + return; + + QueuedEvent* const ev = (QueuedEvent*)pb->elem(); + assert(ev); + + assert(!ev->is_prepared()); + ev->pre_process(); + assert(ev->is_prepared()); + + assert(_prepared_back.get() == pb); + _prepared_back = pb->next(); + + // 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 Server +} // namespace Ingen + diff --git a/src/server/EventSource.hpp b/src/server/EventSource.hpp new file mode 100644 index 00000000..19d87ed9 --- /dev/null +++ b/src/server/EventSource.hpp @@ -0,0 +1,73 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_EVENTSOURCE_HPP +#define INGEN_ENGINE_EVENTSOURCE_HPP + +#include "raul/Semaphore.hpp" +#include "raul/Slave.hpp" +#include "raul/List.hpp" + +namespace Ingen { +namespace Server { + +class Event; +class QueuedEvent; +class PostProcessor; +class ProcessContext; + +/** Source for events to run in the audio thread. + * + * The Driver gets events from an EventSource in the process callback + * (realtime audio thread) and executes them, then they are sent to the + * PostProcessor and finalised (post-processing thread). + */ +class EventSource : protected Raul::Slave +{ +public: + explicit EventSource(size_t queue_size); + virtual ~EventSource(); + + void process(PostProcessor& dest, ProcessContext& context, bool limit=true); + + bool empty() { return _events.empty(); } + + /** Signal that a blocking event is finished. + * + * This MUST be called by blocking events in their post_process() method + * to resume pre-processing of events. + */ + inline void unblock() { _blocking_semaphore.post(); } + +protected: + void push_queued(QueuedEvent* const ev); + + inline bool unprepared_events() { return (_prepared_back.get() != NULL); } + + virtual void _whipped(); ///< Prepare 1 event + +private: + Raul::List _events; + Raul::AtomicPtr::Node> _prepared_back; + Raul::Semaphore _blocking_semaphore; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_EVENTSOURCE_HPP + diff --git a/src/server/GraphObjectImpl.cpp b/src/server/GraphObjectImpl.cpp new file mode 100644 index 00000000..334a8caa --- /dev/null +++ b/src/server/GraphObjectImpl.cpp @@ -0,0 +1,73 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "GraphObjectImpl.hpp" +#include "PatchImpl.hpp" +#include "EngineStore.hpp" + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { + +GraphObjectImpl::GraphObjectImpl(Ingen::Shared::LV2URIMap& uris, + GraphObjectImpl* parent, + const Symbol& symbol) + : ResourceImpl(uris, parent ? parent->path().child(symbol) : Raul::Path::root()) + , _parent(parent) + , _path(parent ? parent->path().child(symbol) : "/") + , _symbol(symbol) +{ +} + +void +GraphObjectImpl::add_meta_property(const Raul::URI& key, const Atom& value) +{ + add_property(key, Resource::Property(value, Resource::INTERNAL)); +} + +void +GraphObjectImpl::set_meta_property(const Raul::URI& key, const Atom& value) +{ + set_property(key, Resource::Property(value, Resource::INTERNAL)); +} + +const Atom& +GraphObjectImpl::get_property(const Raul::URI& key) const +{ + static const Atom null_atom; + Resource::Properties::const_iterator i = properties().find(key); + return (i != properties().end()) ? i->second : null_atom; +} + +PatchImpl* +GraphObjectImpl::parent_patch() const +{ + return dynamic_cast((NodeImpl*)_parent); +} + +SharedPtr +GraphObjectImpl::find_child(const std::string& name) const +{ + throw; +} + +} // namespace Server +} // namespace Ingen diff --git a/src/server/GraphObjectImpl.hpp b/src/server/GraphObjectImpl.hpp new file mode 100644 index 00000000..6c08c9c4 --- /dev/null +++ b/src/server/GraphObjectImpl.hpp @@ -0,0 +1,113 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_GRAPHOBJECTIMPL_HPP +#define INGEN_ENGINE_GRAPHOBJECTIMPL_HPP + +#include +#include +#include +#include +#include "raul/Deletable.hpp" +#include "raul/Path.hpp" +#include "raul/SharedPtr.hpp" +#include "ingen/GraphObject.hpp" +#include "shared/ResourceImpl.hpp" + +namespace Raul { class Maid; } + +namespace Ingen { + +namespace Shared { class LV2URIMap; } + +namespace Server { + +class PatchImpl; +class Context; +class ProcessContext; +class BufferFactory; + +/** 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 GraphObject + , public Ingen::Shared::ResourceImpl +{ +public: + virtual ~GraphObjectImpl() {} + + const Raul::URI& uri() const { return _path; } + const Raul::Symbol& symbol() const { return _symbol; } + + GraphObject* graph_parent() const { return _parent; } + GraphObjectImpl* parent() const { return _parent; } + + //virtual void process(ProcessContext& context) = 0; + + /** Rename */ + virtual void set_path(const Raul::Path& new_path) { + _path = new_path; + _symbol = new_path.symbol(); + } + + const Raul::Atom& get_property(const Raul::URI& key) const; + void add_meta_property(const Raul::URI& key, const Raul::Atom& value); + void set_meta_property(const Raul::URI& key, const Raul::Atom& value); + + /** The Patch this object is a child of. */ + virtual PatchImpl* parent_patch() const; + + /** Raul::Path is dynamically generated from parent to ease renaming */ + const Raul::Path& path() const { return _path; } + + SharedPtr find_child(const std::string& name) const; + + /** Prepare for a new (external) polyphony value. + * + * Preprocessor thread, poly is actually applied by apply_poly. + * \return true on success. + */ + virtual bool prepare_poly(BufferFactory& bufs, 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; + +protected: + GraphObjectImpl(Ingen::Shared::LV2URIMap& uris, + GraphObjectImpl* parent, + const Raul::Symbol& symbol); + + GraphObjectImpl* _parent; + Raul::Path _path; + Raul::Symbol _symbol; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_GRAPHOBJECTIMPL_HPP diff --git a/src/server/HTTPClientSender.cpp b/src/server/HTTPClientSender.cpp new file mode 100644 index 00000000..1334e6e7 --- /dev/null +++ b/src/server/HTTPClientSender.cpp @@ -0,0 +1,146 @@ +/* This file is part of Ingen. + * Copyright 2008-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "raul/log.hpp" +#include "raul/Atom.hpp" +#include "raul/AtomRDF.hpp" +#include "serialisation/Serialiser.hpp" +#include "shared/World.hpp" +#include "HTTPClientSender.hpp" +#include "Engine.hpp" + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { + +void +HTTPClientSender::response_ok(int32_t id) +{ +} + +void +HTTPClientSender::response_error(int32_t id, const std::string& msg) +{ + warn << "HTTP Error " << id << " (" << msg << ")" << endl; +} + +void +HTTPClientSender::error(const std::string& msg) +{ + warn << "HTTP send error " << msg << endl; +} + +void +HTTPClientSender::put(const URI& uri, + const Resource::Properties& properties, + Resource::Graph ctx) +{ + const std::string request_uri = (Raul::Path::is_path(uri)) + ? _url + "/patch" + uri.substr(uri.find("/")) + : uri.str(); + + + Sord::Model model(*_engine.world()->rdf_world()); + for (Resource::Properties::const_iterator i = properties.begin(); + i != properties.end(); ++i) + model.add_statement( + Sord::URI(*_engine.world()->rdf_world(), request_uri), + AtomRDF::atom_to_node(model, i->first.str()), + AtomRDF::atom_to_node(model, i->second)); + + const string str = model.write_to_string("turtle"); + send_chunk(str); +} + +void +HTTPClientSender::delta(const URI& uri, + const Resource::Properties& remove, + const Resource::Properties& add) +{ +} + +void +HTTPClientSender::del(const URI& uri) +{ + send_chunk(string("<").append(uri.str()).append("> a .")); +} + +void +HTTPClientSender::connect(const Path& src_path, const Path& dst_path) +{ + const string msg = string( + "@prefix rdf: .\n" + "@prefix ingen: .\n").append( + "<> ingen:connection [\n" + "\tingen:destination <").append(dst_path.str()).append("> ;\n" + "\tingen:source <").append(src_path.str()).append(">\n] .\n"); + send_chunk(msg); +} + +void +HTTPClientSender::disconnect(const URI& src, + const URI& dst) +{ +} + +void +HTTPClientSender::disconnect_all(const Raul::Path& parent_patch_path, + const Raul::Path& path) +{ +} + +void +HTTPClientSender::set_property(const URI& subject, const URI& key, const Atom& value) +{ +#if 0 + Sord::Node node = AtomRDF::atom_to_node(*_engine.world()->rdf_world(), value); + const string msg = string( + "@prefix rdf: .\n" + "@prefix ingen: .\n" + "@prefix ingenui: .\n").append( + subject.str()).append("> ingen:property [\n" + "rdf:predicate ").append(key.str()).append(" ;\n" + "rdf:value ").append(node.to_string()).append("\n] .\n"); + send_chunk(msg); +#endif +} + +void +HTTPClientSender::activity(const Path& path) +{ + const string msg = string( + "@prefix ingen: .\n\n<").append( + path.str()).append("> ingen:activity true .\n"); + send_chunk(msg); +} + +void +HTTPClientSender::move(const Path& old_path, const Path& new_path) +{ + string msg = string( + "@prefix rdf: .\n" + "@prefix ingen: .\n\n<").append( + old_path.str()).append("> rdf:subject <").append(new_path.str()).append("> .\n"); + send_chunk(msg); +} + +} // namespace Server +} // namespace Ingen diff --git a/src/server/HTTPClientSender.hpp b/src/server/HTTPClientSender.hpp new file mode 100644 index 00000000..63842c3a --- /dev/null +++ b/src/server/HTTPClientSender.hpp @@ -0,0 +1,107 @@ +/* This file is part of Ingen. + * Copyright 2008-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_HTTPCLIENTSENDER_HPP +#define INGEN_ENGINE_HTTPCLIENTSENDER_HPP + +#include +#include +#include +#include "raul/Thread.hpp" +#include "ingen/ClientInterface.hpp" +#include "shared/HTTPSender.hpp" + +namespace Ingen { + +class ServerInterface; + +namespace Server { + +class Engine; + +/** Implements ClientInterface for HTTP clients. + * Sends changes as RDF deltas over an HTTP stream + * (a single message with chunked encoding response). + * + * \ingroup engine + */ +class HTTPClientSender + : public ClientInterface + , public Ingen::Shared::HTTPSender +{ +public: + explicit HTTPClientSender(Engine& engine) + : _engine(engine) + , _enabled(true) + {} + + bool enabled() const { return _enabled; } + + void enable() { _enabled = true; } + void disable() { _enabled = false; } + + void bundle_begin() { HTTPSender::bundle_begin(); } + void bundle_end() { HTTPSender::bundle_end(); } + + Raul::URI uri() const { return "http://example.org/"; } + + /* *** ClientInterface Implementation Below *** */ + + void response_ok(int32_t id); + void response_error(int32_t id, const std::string& msg); + + void error(const std::string& msg); + + virtual void put(const Raul::URI& path, + const Resource::Properties& properties, + Resource::Graph ctx); + + virtual void delta(const Raul::URI& path, + const Resource::Properties& remove, + const Resource::Properties& add); + + virtual void del(const Raul::URI& uri); + + virtual void move(const Raul::Path& old_path, + const Raul::Path& new_path); + + virtual void connect(const Raul::Path& src_port_path, + const Raul::Path& dst_port_path); + + virtual void disconnect(const Raul::URI& src, + const Raul::URI& dst); + + virtual void disconnect_all(const Raul::Path& parent_patch_path, + const Raul::Path& path); + + virtual void set_property(const Raul::URI& subject_path, + const Raul::URI& predicate, + const Raul::Atom& value); + + virtual void activity(const Raul::Path& path); + +private: + Engine& _engine; + std::string _url; + bool _enabled; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_HTTPCLIENTSENDER_HPP + diff --git a/src/server/HTTPEngineReceiver.cpp b/src/server/HTTPEngineReceiver.cpp new file mode 100644 index 00000000..b027a6b3 --- /dev/null +++ b/src/server/HTTPEngineReceiver.cpp @@ -0,0 +1,230 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "raul/SharedPtr.hpp" +#include "raul/log.hpp" + +#include "ingen/ClientInterface.hpp" +#include "shared/Module.hpp" +#include "serialisation/Parser.hpp" +#include "serialisation/Serialiser.hpp" + +#include "ClientBroadcaster.hpp" +#include "Engine.hpp" +#include "EngineStore.hpp" +#include "EventSource.hpp" +#include "HTTPClientSender.hpp" +#include "HTTPEngineReceiver.hpp" +#include "ThreadManager.hpp" + +#define LOG(s) s << "[HTTPEngineReceiver] " + +using namespace std; +using namespace Raul; + +namespace Ingen { + +using namespace Serialisation; + +namespace Server { + +HTTPEngineReceiver::HTTPEngineReceiver(Engine& engine, uint16_t port) + : QueuedEngineInterface(engine, 64) // FIXME + , _server(soup_server_new(SOUP_SERVER_PORT, port, NULL)) +{ + _receive_thread = new ReceiveThread(*this); + + soup_server_add_handler(_server, NULL, message_callback, this, NULL); + + LOG(info) << "Started HTTP server on port " << soup_server_get_port(_server) << endl; + + if (!engine.world()->parser() || !engine.world()->serialiser()) + engine.world()->load_module("serialisation"); + + Thread::set_name("HTTPEngineReceiver"); + start(); + _receive_thread->set_name("HTTPEngineReceiver Listener"); + _receive_thread->start(); +} + +HTTPEngineReceiver::~HTTPEngineReceiver() +{ + _receive_thread->stop(); + stop(); + delete _receive_thread; + + if (_server) { + soup_server_quit(_server); + _server = NULL; + } +} + +void +HTTPEngineReceiver::message_callback(SoupServer* server, + SoupMessage* msg, + const char* path_str, + GHashTable* query, + SoupClientContext* client, + void* data) +{ + HTTPEngineReceiver* me = (HTTPEngineReceiver*)data; + + using namespace Ingen::Shared; + + SharedPtr store = me->_engine.world()->store(); + if (!store) { + soup_message_set_status(msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); + return; + } + + string path = path_str; + if (path[path.length() - 1] == '/') { + path = path.substr(0, path.length()-1); + } + + SharedPtr serialiser = me->_engine.world()->serialiser(); + + const string base_uri = "path:/"; + const char* mime_type = "text/plain"; + + // Special GET paths + if (msg->method == SOUP_METHOD_GET) { + if (path == Path::root().str() || path.empty()) { + const string r = string("@prefix rdfs: .\n") + .append("\n<> rdfs:seeAlso ;") + .append("\n rdfs:seeAlso ;") + .append("\n rdfs:seeAlso ."); + soup_message_set_status(msg, SOUP_STATUS_OK); + soup_message_set_response(msg, mime_type, SOUP_MEMORY_COPY, r.c_str(), r.length()); + return; + + } else if (msg->method == SOUP_METHOD_GET && path.substr(0, 8) == "/plugins") { + // FIXME: kludge + #if 0 + me->get("ingen:plugins"); + me->_receive_thread->whip(); + + serialiser->start_to_string("/", base_uri); + for (NodeFactory::Plugins::const_iterator p = me->_engine.node_factory()->plugins().begin(); + p != me->_engine.node_factory()->plugins().end(); ++p) + serialiser->serialise_plugin(*(Shared::Plugin*)p->second); + const string r = serialiser->finish(); + soup_message_set_status(msg, SOUP_STATUS_OK); + soup_message_set_response(msg, mime_type, SOUP_MEMORY_COPY, r.c_str(), r.length()); + #endif + return; + + } else if (path.substr(0, 6) == "/patch") { + path = '/' + path.substr(6); + if (path.substr(0, 2) == "//") + path = path.substr(1); + + } else if (path.substr(0, 7) == "/stream") { + HTTPClientSender* client = new HTTPClientSender(me->_engine); + me->register_client(client); + + // Respond with port number of stream for client + const int port = client->listen_port(); + char buf[32]; + snprintf(buf, sizeof(buf), "%d", port); + soup_message_set_status(msg, SOUP_STATUS_OK); + soup_message_set_response(msg, mime_type, SOUP_MEMORY_COPY, buf, strlen(buf)); + return; + } + } + + if (!Path::is_valid(path)) { + LOG(error) << "Bad HTTP path: " << path << endl; + 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; + } + + // Serialise object + const string response = serialiser->to_string(start->second, + "http://localhost:16180/patch", GraphObject::Properties()); + + soup_message_set_status(msg, SOUP_STATUS_OK); + soup_message_set_response(msg, mime_type, SOUP_MEMORY_COPY, + response.c_str(), response.length()); + + } else if (msg->method == SOUP_METHOD_PUT) { + Glib::RWLock::WriterLock lock(store->lock()); + + // Get parser + SharedPtr parser = me->_engine.world()->parser(); + if (!parser) { + soup_message_set_status(msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); + return; + } + + parser->parse_string(me->_engine.world(), me, msg->request_body->data, base_uri); + soup_message_set_status(msg, SOUP_STATUS_OK); + + } else if (msg->method == SOUP_METHOD_DELETE) { + me->del(path); + soup_message_set_status(msg, SOUP_STATUS_OK); + + } 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 Server +} // namespace Ingen + diff --git a/src/server/HTTPEngineReceiver.hpp b/src/server/HTTPEngineReceiver.hpp new file mode 100644 index 00000000..c261d0f1 --- /dev/null +++ b/src/server/HTTPEngineReceiver.hpp @@ -0,0 +1,64 @@ +/* This file is part of Ingen. + * Copyright 2008-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_HTTPENGINERECEIVER_HPP +#define INGEN_ENGINE_HTTPENGINERECEIVER_HPP + +#include + +#include + +#include "QueuedEngineInterface.hpp" + +typedef struct _SoupServer SoupServer; +typedef struct _SoupMessage SoupMessage; +typedef struct SoupClientContext SoupClientContext; + +namespace Ingen { +namespace Server { + +class HTTPEngineReceiver : public QueuedEngineInterface +{ +public: + HTTPEngineReceiver(Engine& engine, uint16_t port); + ~HTTPEngineReceiver(); + +private: + struct ReceiveThread : public Raul::Thread { + explicit ReceiveThread(HTTPEngineReceiver& receiver) : _receiver(receiver) {} + virtual void _run(); + virtual void whip() { + while (_receiver.unprepared_events()) + _receiver.whip(); + } + 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 Server +} // namespace Ingen + +#endif // INGEN_ENGINE_HTTPENGINERECEIVER_HPP diff --git a/src/server/InputPort.cpp b/src/server/InputPort.cpp new file mode 100644 index 00000000..a79f808b --- /dev/null +++ b/src/server/InputPort.cpp @@ -0,0 +1,229 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "ingen/Patch.hpp" +#include "shared/LV2URIMap.hpp" +#include "AudioBuffer.hpp" +#include "BufferFactory.hpp" +#include "ConnectionImpl.hpp" +#include "EventBuffer.hpp" +#include "NodeImpl.hpp" +#include "OutputPort.hpp" +#include "ProcessContext.hpp" +#include "ThreadManager.hpp" +#include "mix.hpp" +#include "util.hpp" + +using namespace std; + +namespace Ingen { +namespace Server { + +InputPort::InputPort(BufferFactory& bufs, + NodeImpl* parent, + const Raul::Symbol& symbol, + uint32_t index, + uint32_t poly, + PortType type, + const Raul::Atom& value, + size_t buffer_size) + : PortImpl(bufs, parent, symbol, index, poly, type, value, buffer_size) + , _num_connections(0) +{ + const Ingen::Shared::LV2URIMap& uris = bufs.uris(); + + if (!dynamic_cast(parent)) + add_property(uris.rdf_type, uris.lv2_InputPort); + + // Set default control range + if (type == PortType::CONTROL) { + set_property(uris.lv2_minimum, 0.0f); + set_property(uris.lv2_maximum, 1.0f); + } +} + +bool +InputPort::apply_poly(Maid& maid, uint32_t poly) +{ + bool ret = PortImpl::apply_poly(maid, poly); + if (!ret) + poly = 1; + + assert(_buffers->size() >= poly); + + return true; +} + +/** Set \a buffers appropriately if this port has \a num_connections connections. + * \return true iff buffers are locally owned by the port + */ +bool +InputPort::get_buffers(BufferFactory& bufs, + Raul::Array* buffers, + uint32_t poly) +{ + size_t num_connections = (ThreadManager::thread_is(THREAD_PROCESS)) + ? _connections.size() : _num_connections; + + if (buffer_type() == PortType::AUDIO && num_connections == 0) { + // Audio input with no connections, use shared zero buffer + for (uint32_t v = 0; v < poly; ++v) + buffers->at(v) = bufs.silent_buffer(); + return false; + + } else if (num_connections == 1) { + if (ThreadManager::thread_is(THREAD_PROCESS)) { + if (!_connections.front()->must_mix() && !_connections.front()->must_queue()) { + // Single non-mixing conneciton, use buffers directly + for (uint32_t v = 0; v < poly; ++v) + buffers->at(v) = _connections.front()->buffer(v); + return false; + } + } + } + + // Otherwise, allocate local buffers + for (uint32_t v = 0; v < poly; ++v) { + buffers->at(v) = _bufs.get(buffer_type(), _buffer_size); + buffers->at(v)->clear(); + } + 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 copying/mixing needs to take place. + * + * Note that setup_buffers must be called after this before the change + * will audibly take effect. + */ +void +InputPort::add_connection(Connections::Node* const c) +{ + ThreadManager::assert_thread(THREAD_PROCESS); + + _connections.push_back(c); + + // Automatically broadcast connected control inputs + if (is_a(PortType::CONTROL)) + _broadcast = true; +} + +/** Remove a connection. Realtime safe. + * + * Note that setup_buffers must be called after this before the change + * will audibly take effect. + */ +InputPort::Connections::Node* +InputPort::remove_connection(ProcessContext& context, const OutputPort* src_port) +{ + ThreadManager::assert_thread(THREAD_PROCESS); + + Connections::Node* connection = NULL; + for (Connections::iterator i = _connections.begin(); i != _connections.end();) { + Connections::iterator next = i; + ++next; + + if ((*i)->src_port() == src_port) { + connection = _connections.erase(i); + break; + } + + i = next; + } + + if ( ! connection) { + error << "[InputPort::remove_connection] Connection not found!" << endl; + return NULL; + } + + // Turn off broadcasting if we're no longer connected + if (is_a(PortType::CONTROL) && _connections.size() == 0) + _broadcast = false; + + return connection; +} + +/** Prepare buffer for access, mixing if necessary. Realtime safe. + */ +void +InputPort::pre_process(Context& context) +{ + // If value has been set (e.g. events pushed) by the user, don't smash it + if (_set_by_user) + return; + + uint32_t max_num_srcs = 0; + for (Connections::iterator c = _connections.begin(); c != _connections.end(); ++c) + max_num_srcs += (*c)->src_port()->poly(); + + IntrusivePtr srcs[max_num_srcs]; + + if (_connections.empty()) { + for (uint32_t v = 0; v < _poly; ++v) { + buffer(v)->prepare_read(context); + } + } else if (direct_connect()) { + for (uint32_t v = 0; v < _poly; ++v) { + _buffers->at(v) = _connections.front()->buffer(v); + _buffers->at(v)->prepare_read(context); + } + } else { + for (uint32_t v = 0; v < _poly; ++v) { + uint32_t num_srcs = 0; + for (Connections::iterator c = _connections.begin(); c != _connections.end(); ++c) + (*c)->get_sources(context, v, srcs, max_num_srcs, num_srcs); + + mix(context, buffer(v).get(), srcs, num_srcs); + buffer(v)->prepare_read(context); + } + } + + if (_broadcast) + broadcast_value(context, false); +} + +void +InputPort::post_process(Context& context) +{ + if (_set_by_user) { + if (buffer_type() == PortType::EVENTS) { + // Clear events received via a SetPortValue + for (uint32_t v = 0; v < _poly; ++v) { + buffer(v)->clear(); + } + } + _set_by_user = false; + } +} + +bool +InputPort::direct_connect() const +{ + return (context() == Context::AUDIO) + && _connections.size() == 1 + && !_connections.front()->must_mix() + && !_connections.front()->must_queue(); +} + +} // namespace Server +} // namespace Ingen + diff --git a/src/server/InputPort.hpp b/src/server/InputPort.hpp new file mode 100644 index 00000000..0ecd5317 --- /dev/null +++ b/src/server/InputPort.hpp @@ -0,0 +1,93 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_INPUTPORT_HPP +#define INGEN_ENGINE_INPUTPORT_HPP + +#include +#include +#include +#include "raul/List.hpp" +#include "raul/SharedPtr.hpp" +#include "PortImpl.hpp" + +namespace Ingen { +namespace Server { + +class ConnectionImpl; +class Context; +class NodeImpl; +class OutputPort; +class ProcessContext; + +/** 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(BufferFactory& bufs, + NodeImpl* parent, + const Raul::Symbol& symbol, + uint32_t index, + uint32_t poly, + PortType type, + const Raul::Atom& value, + size_t buffer_size=0); + + virtual ~InputPort() {} + + typedef Raul::List< SharedPtr > Connections; + + void add_connection(Connections::Node* c); + Connections::Node* remove_connection(ProcessContext& context, const OutputPort* src_port); + + bool apply_poly(Raul::Maid& maid, uint32_t poly); + + bool get_buffers(BufferFactory& bufs, + Raul::Array* buffers, + uint32_t poly); + + void pre_process(Context& context); + void post_process(Context& context); + + size_t num_connections() const { return _num_connections; } ///< Pre-process thread + void increment_num_connections() { ++_num_connections; } + void decrement_num_connections() { --_num_connections; } + + bool is_input() const { return true; } + bool is_output() const { return false; } + + bool direct_connect() const; + +protected: + size_t _num_connections; ///< Pre-process thread + Connections _connections; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_INPUTPORT_HPP diff --git a/src/server/InternalPlugin.cpp b/src/server/InternalPlugin.cpp new file mode 100644 index 00000000..bcf30a47 --- /dev/null +++ b/src/server/InternalPlugin.cpp @@ -0,0 +1,72 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "shared/LV2URIMap.hpp" +#include "internals/Controller.hpp" +#include "internals/Delay.hpp" +#include "internals/Note.hpp" +#include "internals/Trigger.hpp" +#include "Driver.hpp" +#include "Engine.hpp" +#include "InternalPlugin.hpp" + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { + +using namespace Internals; + +InternalPlugin::InternalPlugin(Shared::LV2URIMap& uris, + const std::string& uri, const std::string& symbol) + : PluginImpl(uris, Plugin::Internal, uri) + , _symbol(symbol) +{ + set_property(uris.rdf_type, uris.ingen_Internal); +} + +NodeImpl* +InternalPlugin::instantiate(BufferFactory& bufs, + const string& name, + bool polyphonic, + PatchImpl* parent, + Engine& engine) +{ + assert(_type == Internal); + + const SampleCount srate = engine.driver()->sample_rate(); + + const string uri_str = uri().str(); + + if (uri_str == NS_INTERNALS "Controller") { + return new ControllerNode(this, bufs, name, polyphonic, parent, srate); + } else if (uri_str == NS_INTERNALS "Delay") { + return new DelayNode(this, bufs, name, polyphonic, parent, srate); + } else if (uri_str == NS_INTERNALS "Note") { + return new NoteNode(this, bufs, name, polyphonic, parent, srate); + } else if (uri_str == NS_INTERNALS "Trigger") { + return new TriggerNode(this, bufs, name, polyphonic, parent, srate); + } else { + return NULL; + } +} + +} // namespace Server +} // namespace Ingen diff --git a/src/server/InternalPlugin.hpp b/src/server/InternalPlugin.hpp new file mode 100644 index 00000000..be1df1d0 --- /dev/null +++ b/src/server/InternalPlugin.hpp @@ -0,0 +1,63 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_INTERNALPLUGIN_HPP +#define INGEN_ENGINE_INTERNALPLUGIN_HPP + +#include "ingen-config.h" + +#include +#include + +#include +#include + +#include "PluginImpl.hpp" + +#define NS_INTERNALS "http://drobilla.net/ns/ingen-internals#" + +namespace Ingen { +namespace Server { + +class NodeImpl; +class BufferFactory; + +/** Implementation of an Internal plugin. + */ +class InternalPlugin : public PluginImpl +{ +public: + InternalPlugin(Shared::LV2URIMap& uris, + const std::string& uri, const std::string& symbol); + + NodeImpl* instantiate(BufferFactory& bufs, + const std::string& name, + bool polyphonic, + PatchImpl* parent, + Engine& engine); + + const std::string symbol() const { return _symbol; } + +private: + const std::string _symbol; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_INTERNALPLUGIN_HPP + diff --git a/src/server/JackDriver.cpp b/src/server/JackDriver.cpp new file mode 100644 index 00000000..e78c33af --- /dev/null +++ b/src/server/JackDriver.cpp @@ -0,0 +1,561 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "ingen-config.h" + +#include +#include + +#include +#ifdef INGEN_JACK_SESSION +#include +#include +#include "serialisation/Serialiser.hpp" +#endif + +#include "raul/log.hpp" +#include "raul/List.hpp" + +#include "lv2/lv2plug.in/ns/ext/event/event.h" + +#include "AudioBuffer.hpp" +#include "ControlBindings.hpp" +#include "DuplexPort.hpp" +#include "Engine.hpp" +#include "Event.hpp" +#include "EventBuffer.hpp" +#include "EventSource.hpp" +#include "JackDriver.hpp" +#include "MessageContext.hpp" +#include "PatchImpl.hpp" +#include "PortImpl.hpp" +#include "PostProcessor.hpp" +#include "ProcessSlave.hpp" +#include "QueuedEvent.hpp" +#include "ThreadManager.hpp" +#include "shared/World.hpp" +#include "shared/LV2Features.hpp" +#include "shared/LV2URIMap.hpp" +#include "util.hpp" + +#define LOG(s) s << "[JackDriver] " + +using namespace std; +using namespace Raul; + +typedef jack_default_audio_sample_t jack_sample_t; + +namespace Ingen { +namespace Server { + +//// JackPort //// + +JackPort::JackPort(JackDriver* driver, DuplexPort* patch_port) + : DriverPort(patch_port) + , Raul::List::Node(this) + , _driver(driver) + , _jack_port(NULL) +{ + patch_port->setup_buffers(*driver->_engine.buffer_factory(), patch_port->poly()); + create(); +} + +JackPort::~JackPort() +{ + assert(_jack_port == NULL); +} + +void +JackPort::create() +{ + _jack_port = jack_port_register( + _driver->jack_client(), + ingen_jack_port_name(_patch_port->path()).c_str(), + (_patch_port->buffer_type() == PortType::AUDIO) + ? JACK_DEFAULT_AUDIO_TYPE : JACK_DEFAULT_MIDI_TYPE, + (_patch_port->is_input()) + ? JackPortIsInput : JackPortIsOutput, + 0); + + if (_jack_port == NULL) { + error << "[JackPort] Failed to register port " << _patch_port->path() << endl; + throw JackDriver::PortRegistrationFailedException(); + } +} + +void +JackPort::destroy() +{ + assert(_jack_port); + if (jack_port_unregister(_driver->jack_client(), _jack_port)) + error << "[JackPort] Unable to unregister port" << endl; + _jack_port = NULL; +} + +void +JackPort::move(const Raul::Path& path) +{ + jack_port_set_name(_jack_port, ingen_jack_port_name(path).c_str()); +} + +void +JackPort::pre_process(ProcessContext& context) +{ + if (!is_input()) + return; + + const SampleCount nframes = context.nframes(); + + if (_patch_port->buffer_type() == PortType::AUDIO) { + jack_sample_t* jack_buf = (jack_sample_t*)jack_port_get_buffer(_jack_port, nframes); + AudioBuffer* patch_buf = (AudioBuffer*)_patch_port->buffer(0).get(); + + patch_buf->copy(jack_buf, 0, nframes - 1); + + } else if (_patch_port->buffer_type() == PortType::EVENTS) { + void* jack_buf = jack_port_get_buffer(_jack_port, nframes); + EventBuffer* patch_buf = (EventBuffer*)_patch_port->buffer(0).get(); + + const jack_nframes_t event_count = jack_midi_get_event_count(jack_buf); + + patch_buf->prepare_write(context); + + // 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_buf, i); + + if (!patch_buf->append(ev.time, 0, + _driver->_midi_event_type, + ev.size, ev.buffer)) + LOG(warn) << "Failed to write MIDI to port buffer, event(s) lost!" << endl; + } + } +} + +void +JackPort::post_process(ProcessContext& context) +{ + if (is_input()) + return; + + const SampleCount nframes = context.nframes(); + + if (_patch_port->buffer_type() == PortType::AUDIO) { + jack_sample_t* jack_buf = (jack_sample_t*)jack_port_get_buffer(_jack_port, nframes); + AudioBuffer* patch_buf = (AudioBuffer*)_patch_port->buffer(0).get(); + + memcpy(jack_buf, patch_buf->data(), nframes * sizeof(Sample)); + + } else if (_patch_port->buffer_type() == PortType::EVENTS) { + void* jack_buf = jack_port_get_buffer(_jack_port, context.nframes()); + EventBuffer* patch_buf = (EventBuffer*)_patch_port->buffer(0).get(); + + patch_buf->prepare_read(context); + 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); + } + } +} + +//// JackDriver //// + +JackDriver::JackDriver(Engine& engine) + : _engine(engine) + , _jack_thread(NULL) + , _sem(0) + , _flag(0) + , _client(NULL) + , _block_length(0) + , _sample_rate(0) + , _is_activated(false) + , _process_context(engine) + , _root_patch(NULL) +{ + _midi_event_type = _engine.world()->uris()->uri_to_id( + LV2_EVENT_URI, "http://lv2plug.in/ns/ext/midi#MidiEvent"); +} + +JackDriver::~JackDriver() +{ + deactivate(); + + if (_client) + jack_client_close(_client); +} + +bool +JackDriver::supports(PortType port_type, EventType event_type) +{ + return (port_type == PortType::AUDIO + || (port_type == PortType::EVENTS && event_type == EventType::MIDI)); +} + +bool +JackDriver::attach(const std::string& server_name, + const std::string& client_name, + void* jack_client) +{ + assert(!_client); + if (!jack_client) { + #ifdef INGEN_JACK_SESSION + const std::string uuid = _engine.world()->jack_uuid(); + if (!uuid.empty()) { + _client = jack_client_open(client_name.c_str(), + JackSessionID, NULL, + uuid.c_str()); + LOG(info) << "Connected to JACK server as client `" + << client_name.c_str() << "' UUID `" << uuid << "'" << endl; + } + #endif + + // Try supplied server name + if (!_client && !server_name.empty()) { + if ((_client = jack_client_open(client_name.c_str(), + JackServerName, NULL, + server_name.c_str()))) { + LOG(info) << "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) { + if ((_client = jack_client_open(client_name.c_str(), JackNullOption, NULL))) + LOG(info) << "Connected to default JACK server" << endl; + } + + // Still failed + if (!_client) { + LOG(error) << "Unable to connect to Jack" << endl; + return false; + } + } else { + _client = (jack_client_t*)jack_client; + } + + _block_length = 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, block_length_cb, this); +#ifdef INGEN_JACK_SESSION + jack_set_session_callback(_client, session_cb, this); +#endif + + for (Raul::List::iterator i = _ports.begin(); i != _ports.end(); ++i) + (*i)->create(); + + return true; +} + +void +JackDriver::activate() +{ + Shared::World* world = _engine.world(); + + if (_is_activated) { + LOG(warn) << "Jack driver already activated." << endl; + return; + } + + if (!_client) + attach(world->conf()->option("jack-server").get_string(), + world->conf()->option("jack-client").get_string(), NULL); + + jack_set_process_callback(_client, process_cb, this); + + _is_activated = true; + + _process_context.activate(world->conf()->option("parallelism").get_int32(), + is_realtime()); + + if (jack_activate(_client)) { + LOG(error) << "Could not activate Jack client, aborting." << endl; + exit(EXIT_FAILURE); + } else { + LOG(info) << "Activated Jack client." << endl; + } +} + +void +JackDriver::deactivate() +{ + if (_is_activated) { + _flag = 1; + _is_activated = false; + _sem.wait(); + + for (Raul::List::iterator i = _ports.begin(); i != _ports.end(); ++i) + (*i)->destroy(); + + if (_client) { + jack_deactivate(_client); + jack_client_close(_client); + _client = NULL; + } + + _jack_thread->stop(); + LOG(info) << "Deactivated Jack client" << endl; + } +} + +/** 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 +JackDriver::add_port(DriverPort* port) +{ + ThreadManager::assert_thread(THREAD_PROCESS); + assert(dynamic_cast(port)); + _ports.push_back((JackPort*)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. + */ +Raul::Deletable* +JackDriver::remove_port(const Path& path, DriverPort** port) +{ + ThreadManager::assert_thread(THREAD_PROCESS); + + for (Raul::List::iterator i = _ports.begin(); i != _ports.end(); ++i) { + if ((*i)->patch_port()->path() == path) { + Raul::List::Node* node = _ports.erase(i); + if (port) + *port = node->elem(); + return node; + } + } + + LOG(warn) << "Unable to find port " << path << endl; + return NULL; +} + +DriverPort* +JackDriver::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* +JackDriver::create_port(DuplexPort* patch_port) +{ + try { + if (patch_port->buffer_type() == PortType::AUDIO + || patch_port->buffer_type() == PortType::EVENTS) + return new JackPort(this, patch_port); + else + return NULL; + } catch (...) { + return NULL; + } +} + +DriverPort* +JackDriver::driver_port(const Path& path) +{ + ThreadManager::assert_thread(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 +JackDriver::_process_cb(jack_nframes_t nframes) +{ + if (nframes == 0 || ! _is_activated) { + if (_flag == 1) + _sem.post(); + return 0; + } + + // FIXME: all of this time stuff is screwy + + // FIXME: support nframes != buffer_size, even though that never damn well happens + //assert(nframes == _block_length); + + // Note that Jack can not call this function for a cycle, if overloaded + const jack_nframes_t start_of_current_cycle = jack_last_frame_time(_client); + + _transport_state = jack_transport_query(_client, &_position); + + _process_context.locate(start_of_current_cycle, nframes, 0); + + for (ProcessContext::Slaves::iterator i = _process_context.slaves().begin(); + i != _process_context.slaves().end(); ++i) { + (*i)->context().locate(start_of_current_cycle, nframes, 0); + } + + // Read input + for (Raul::List::iterator i = _ports.begin(); i != _ports.end(); ++i) + (*i)->pre_process(_process_context); + + // Apply control bindings to input + _engine.control_bindings()->pre_process(_process_context, + PtrCast(_root_patch->port_impl(0)->buffer(0)).get()); + + _engine.post_processor()->set_end_time(_process_context.end()); + + // Process events that came in during the last cycle + // (Aiming for jitter-free 1 block event latency, ideally) + _engine.process_events(_process_context); + + // Run root patch + if (_root_patch) { + _root_patch->process(_process_context); +#if 0 + static const SampleCount control_block_size = nframes / 2; + for (jack_nframes_t i = 0; i < nframes; i += control_block_size) { + const SampleCount block_size = (i + control_block_size < nframes) + ? control_block_size + : nframes - i; + _process_context.locate(start_of_current_cycle + i, block_size, i); + _root_patch->process(_process_context); + } +#endif + } + + // Emit control binding feedback + _engine.control_bindings()->post_process(_process_context, + PtrCast(_root_patch->port_impl(1)->buffer(0)).get()); + + // Signal message context to run if necessary + if (_engine.message_context()->has_requests()) + _engine.message_context()->signal(_process_context); + + // Write output + for (Raul::List::iterator i = _ports.begin(); i != _ports.end(); ++i) + (*i)->post_process(_process_context); + + return 0; +} + +void +JackDriver::_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); + ThreadManager::assert_thread(THREAD_PROCESS); +} + +void +JackDriver::_shutdown_cb() +{ + LOG(info) << "Jack shutdown. Exiting." << endl; + _is_activated = false; + delete _jack_thread; + _jack_thread = NULL; + _client = NULL; +} + +int +JackDriver::_sample_rate_cb(jack_nframes_t nframes) +{ + if (_is_activated) { + LOG(error) << "On-the-fly sample rate changing not supported (yet). Aborting." << endl; + exit(EXIT_FAILURE); + } else { + _sample_rate = nframes; + } + return 0; +} + +int +JackDriver::_block_length_cb(jack_nframes_t nframes) +{ + if (_root_patch) { + _block_length = nframes; + _root_patch->set_buffer_size(context(), *_engine.buffer_factory(), PortType::AUDIO, + _engine.buffer_factory()->audio_buffer_size(nframes)); + } + return 0; +} + +#ifdef INGEN_JACK_SESSION +void +JackDriver::_session_cb(jack_session_event_t* event) +{ + LOG(info) << "Jack session save to " << event->session_dir << endl; + + const string cmd = (boost::format("ingen -eg -n %1% -u %2% -l ${SESSION_DIR}") + % jack_get_client_name(_client) + % event->client_uuid).str(); + + SharedPtr serialiser = _engine.world()->serialiser(); + if (serialiser) { + SharedPtr root(_engine.driver()->root_patch(), NullDeleter); + serialiser->write_bundle(root, string("file://") + event->session_dir); + } + + event->command_line = strdup(cmd.c_str()); + jack_session_reply(_client, event); + + switch (event->type) { + case JackSessionSave: + break; + case JackSessionSaveAndQuit: + LOG(warn) << "Jack session quit" << endl; + _engine.quit(); + break; + case JackSessionSaveTemplate: + break; + } + + jack_session_event_free(event); +} +#endif + +} // namespace Server +} // namespace Ingen diff --git a/src/server/JackDriver.hpp b/src/server/JackDriver.hpp new file mode 100644 index 00000000..5439c95c --- /dev/null +++ b/src/server/JackDriver.hpp @@ -0,0 +1,183 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_JACKAUDIODRIVER_HPP +#define INGEN_ENGINE_JACKAUDIODRIVER_HPP + +#include "ingen-config.h" + +#include + +#include +#include +#ifdef INGEN_JACK_SESSION +#include +#endif + +#include "raul/AtomicInt.hpp" +#include "raul/List.hpp" +#include "raul/Semaphore.hpp" +#include "raul/Thread.hpp" + +#include "Buffer.hpp" +#include "Driver.hpp" +#include "ProcessContext.hpp" + +namespace Raul { class Path; } + +namespace Ingen { +namespace Server { + +class Engine; +class PatchImpl; +class PortImpl; +class DuplexPort; +class JackDriver; + +/** Used internally by JackDriver to represent a Jack port. + * + * A Jack port always has a one-to-one association with a Patch port. + */ +class JackPort : public DriverPort, public Raul::List::Node +{ +public: + JackPort(JackDriver* driver, DuplexPort* patch_port); + ~JackPort(); + + void create(); + void destroy(); + + void move(const Raul::Path& path); + + void pre_process(ProcessContext& context); + void post_process(ProcessContext& context); + + jack_port_t* jack_port() const { return _jack_port; } + +private: + JackDriver* _driver; + jack_port_t* _jack_port; +}; + +/** The Jack Driver. + * + * 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 JackDriver : public Driver +{ +public: + explicit JackDriver(Engine& engine); + ~JackDriver(); + + bool supports(PortType port_type, EventType event_type); + + bool attach(const std::string& server_name, + const std::string& client_name, + void* jack_client); + + 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* driver_port(const Raul::Path& path); + + Raul::Deletable* remove_port(const Raul::Path& path, DriverPort** port=NULL); + + 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 block_length() const { return _block_length; } + SampleCount sample_rate() const { return _sample_rate; } + + inline SampleCount frame_time() const { return _client ? jack_frame_time(_client) : 0; } + + class PortRegistrationFailedException : public std::exception {}; + +private: + friend class JackPort; + + // Static JACK callbacks which call the non-static callbacks (methods) + inline static void thread_init_cb(void* const jack_driver) { + return ((JackDriver*)jack_driver)->_thread_init_cb(); + } + inline static void shutdown_cb(void* const jack_driver) { + return ((JackDriver*)jack_driver)->_shutdown_cb(); + } + inline static int process_cb(jack_nframes_t nframes, void* const jack_driver) { + return ((JackDriver*)jack_driver)->_process_cb(nframes); + } + inline static int block_length_cb(jack_nframes_t nframes, void* const jack_driver) { + return ((JackDriver*)jack_driver)->_block_length_cb(nframes); + } + inline static int sample_rate_cb(jack_nframes_t nframes, void* const jack_driver) { + return ((JackDriver*)jack_driver)->_sample_rate_cb(nframes); + } +#ifdef INGEN_JACK_SESSION + inline static void session_cb(jack_session_event_t* event, void* jack_driver) { + ((JackDriver*)jack_driver)->_session_cb(event); + } +#endif + + // Non static callbacks (methods) + void _thread_init_cb(); + void _shutdown_cb(); + int _process_cb(jack_nframes_t nframes); + int _block_length_cb(jack_nframes_t nframes); + int _sample_rate_cb(jack_nframes_t nframes); +#ifdef INGEN_JACK_SESSION + void _session_cb(jack_session_event_t* event); +#endif + + Engine& _engine; + Raul::Thread* _jack_thread; + Raul::Semaphore _sem; + Raul::AtomicInt _flag; + jack_client_t* _client; + jack_nframes_t _block_length; + jack_nframes_t _sample_rate; + uint32_t _midi_event_type; + bool _is_activated; + jack_position_t _position; + jack_transport_state_t _transport_state; + Raul::List _ports; + ProcessContext _process_context; + PatchImpl* _root_patch; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_JACKAUDIODRIVER_HPP diff --git a/src/server/LV2BlobFeature.hpp b/src/server/LV2BlobFeature.hpp new file mode 100644 index 00000000..0e892219 --- /dev/null +++ b/src/server/LV2BlobFeature.hpp @@ -0,0 +1,66 @@ +/* This file is part of Ingen. + * Copyright 2009-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_LV2BLOBFEATURE_HPP +#define INGEN_ENGINE_LV2BLOBFEATURE_HPP + +#include "shared/LV2Features.hpp" + +namespace Ingen { +namespace Server { + +struct BlobFeature : public Ingen::Shared::LV2Features::Feature { + BlobFeature() { + LV2_Blob_Support* data = (LV2_Blob_Support*)malloc(sizeof(LV2_Blob_Support)); + data->data = NULL; + data->ref_size = sizeof(LV2_Blob); + data->ref_get = &ref_get; + data->ref_copy = &ref_copy; + data->ref_reset = &ref_reset; + data->blob_new = &blob_new; + _feature.URI = LV2_BLOB_SUPPORT_URI; + _feature.data = data; + } + + static LV2_Blob ref_get(LV2_Blob_Support_Data data, + LV2_Atom_Reference* ref) { return 0; } + + static void ref_copy(LV2_Blob_Support_Data data, + LV2_Atom_Reference* dst, + LV2_Atom_Reference* src) {} + + static void ref_reset(LV2_Blob_Support_Data data, + LV2_Atom_Reference* ref) {} + + static void blob_new(LV2_Blob_Support_Data data, + LV2_Atom_Reference* reference, + LV2_Blob_Destroy destroy, + uint32_t type, + size_t size) {} + + SharedPtr feature(Shared::World*, Node*) { + return SharedPtr(&_feature, NullDeleter); + } + +private: + LV2_Feature _feature; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_LV2BLOBFEATURE_HPP diff --git a/src/server/LV2EventFeature.hpp b/src/server/LV2EventFeature.hpp new file mode 100644 index 00000000..5e2f1f31 --- /dev/null +++ b/src/server/LV2EventFeature.hpp @@ -0,0 +1,54 @@ +/* This file is part of Ingen. + * Copyright 2009-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_LV2EVENTFEATURE_HPP +#define INGEN_ENGINE_LV2EVENTFEATURE_HPP + +#include "lv2/lv2plug.in/ns/ext/event/event.h" +#include "shared/LV2Features.hpp" + +namespace Ingen { +namespace Server { + +struct EventFeature : public Ingen::Shared::LV2Features::Feature { + EventFeature() { + LV2_Event_Feature* data = (LV2_Event_Feature*)malloc(sizeof(LV2_Event_Feature)); + data->lv2_event_ref = &event_ref; + data->lv2_event_unref = &event_unref; + data->callback_data = this; + _feature.URI = LV2_EVENT_URI; + _feature.data = data; + } + + static uint32_t event_ref(LV2_Event_Callback_Data callback_data, + LV2_Event* event) { return 0; } + + static uint32_t event_unref(LV2_Event_Callback_Data callback_data, + LV2_Event* event) { return 0; } + + SharedPtr feature(Shared::World*, Node*) { + return SharedPtr(&_feature, NullDeleter); + } + +private: + LV2_Feature _feature; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_LV2EVENTFEATURE_HPP diff --git a/src/server/LV2Info.cpp b/src/server/LV2Info.cpp new file mode 100644 index 00000000..6ce5792b --- /dev/null +++ b/src/server/LV2Info.cpp @@ -0,0 +1,77 @@ +/* This file is part of Ingen. + * Copyright 2008-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "lv2/lv2plug.in/ns/ext/atom/atom.h" +#include "lv2/lv2plug.in/ns/ext/contexts/contexts.h" + +#include "shared/World.hpp" + +#include "LV2BlobFeature.hpp" +#include "LV2EventFeature.hpp" +#include "LV2Features.hpp" +#include "LV2Info.hpp" +#include "LV2RequestRunFeature.hpp" +#include "LV2ResizeFeature.hpp" + +using namespace std; + +namespace Ingen { +namespace Server { + +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)) + , value_port_class(slv2_value_new_uri(world->slv2_world(), + "http://lv2plug.in/ns/ext/atom#ValuePort")) + , message_port_class(slv2_value_new_uri(world->slv2_world(), + "http://lv2plug.in/ns/ext/atom#MessagePort")) + , _world(world) +{ + assert(world); + + world->lv2_features()->add_feature(LV2_EVENT_URI, + SharedPtr(new EventFeature())); + world->lv2_features()->add_feature(LV2_BLOB_SUPPORT_URI, + SharedPtr(new BlobFeature())); + world->lv2_features()->add_feature(LV2_RESIZE_PORT_URI, + SharedPtr(new ResizeFeature())); + world->lv2_features()->add_feature(LV2_CONTEXTS_URI "#RequestRunFeature", + SharedPtr(new RequestRunFeature())); +} + +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); + slv2_value_free(value_port_class); + slv2_value_free(message_port_class); +} + +} // namespace Server +} // namespace Ingen diff --git a/src/server/LV2Info.hpp b/src/server/LV2Info.hpp new file mode 100644 index 00000000..de252d82 --- /dev/null +++ b/src/server/LV2Info.hpp @@ -0,0 +1,62 @@ +/* This file is part of Ingen. + * Copyright 2008-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_LV2INFO_HPP +#define INGEN_ENGINE_LV2INFO_HPP + +#include "ingen-config.h" +#ifndef HAVE_SLV2 +#error "This file requires SLV2, but HAVE_SLV2 is not defined. Please report." +#endif + +#include +#include +#include "slv2/slv2.h" +#include "shared/World.hpp" + +namespace Ingen { + +class Node; + +namespace Server { + +/** Stuff that may need to be passed to an LV2 plugin (i.e. LV2 features). + */ +class LV2Info { +public: + explicit LV2Info(Ingen::Shared::World* world); + ~LV2Info(); + + SLV2Value input_class; + SLV2Value output_class; + SLV2Value control_class; + SLV2Value audio_class; + SLV2Value event_class; + SLV2Value value_port_class; + SLV2Value message_port_class; + + Ingen::Shared::World& world() { return *_world; } + SLV2World lv2_world() { return _world->slv2_world(); } + +private: + Ingen::Shared::World* _world; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_LV2INFO_HPP diff --git a/src/server/LV2Node.cpp b/src/server/LV2Node.cpp new file mode 100644 index 00000000..26f1e918 --- /dev/null +++ b/src/server/LV2Node.cpp @@ -0,0 +1,410 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "raul/log.hpp" +#include "raul/Maid.hpp" +#include "raul/Array.hpp" + +#include "AudioBuffer.hpp" +#include "EventBuffer.hpp" +#include "InputPort.hpp" +#include "LV2Node.hpp" +#include "LV2Plugin.hpp" +#include "LV2URIMap.hpp" +#include "MessageContext.hpp" +#include "OutputPort.hpp" +#include "ProcessContext.hpp" + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { + +/** 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) + : NodeImpl(plugin, name, polyphonic, parent, srate) + , _lv2_plugin(plugin) + , _instances(NULL) + , _prepared_instances(NULL) + , _message_funcs(NULL) +{ + assert(_lv2_plugin); +} + +LV2Node::~LV2Node() +{ + delete _instances; +} + +bool +LV2Node::prepare_poly(BufferFactory& bufs, uint32_t poly) +{ + if (!_polyphonic) + poly = 1; + + NodeImpl::prepare_poly(bufs, poly); + + if (_polyphony == poly) + return true; + + SharedPtr info = _lv2_plugin->lv2_info(); + _prepared_instances = new Instances(poly, *_instances, SharedPtr()); + for (uint32_t i = _polyphony; i < _prepared_instances->size(); ++i) { + _prepared_instances->at(i) = SharedPtr( + slv2_plugin_instantiate( + _lv2_plugin->slv2_plugin(), _srate, _features->array()), + slv2_instance_free); + + if (!_prepared_instances->at(i)) { + error << "Failed to instantiate plugin" << endl; + return false; + } + + // Initialize the values of new ports + for (uint32_t j = 0; j < num_ports(); ++j) { + PortImpl* const port = _ports->at(j); + Buffer* const buffer = port->prepared_buffer(i).get(); + if (buffer) { + if (port->is_a(PortType::CONTROL)) { + ((AudioBuffer*)buffer)->set_value(port->value().get_float(), 0, 0); + } else { + buffer->clear(); + } + } + } + + if (_activated) + slv2_instance_activate((SLV2Instance)(*_prepared_instances)[i].get()); + } + + return true; +} + +bool +LV2Node::apply_poly(Raul::Maid& maid, uint32_t poly) +{ + if (!_polyphonic) + poly = 1; + + if (_prepared_instances) { + maid.push(_instances); + _instances = _prepared_instances; + _prepared_instances = NULL; + } + assert(poly <= _instances->size()); + + return NodeImpl::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(BufferFactory& bufs) +{ + const Ingen::Shared::LV2URIMap& uris = bufs.uris(); + 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 Instances(_polyphony, SharedPtr()); + + _features = info->world().lv2_features()->lv2_features(&info->world(), this); + + uint32_t port_buffer_size = 0; + SLV2Value ctx_ext_uri = slv2_value_new_uri(info->lv2_world(), + LV2_CONTEXTS_URI "#MessageContext"); + + for (uint32_t i = 0; i < _polyphony; ++i) { + (*_instances)[i] = SharedPtr( + slv2_plugin_instantiate(plug, _srate, _features->array()), + slv2_instance_free); + + if (!instance(i)) { + error << "Failed to instantiate plugin " << _lv2_plugin->uri() + << " voice " << i << endl; + return false; + } + + if (!slv2_plugin_has_feature(plug, ctx_ext_uri)) + continue; + + const void* ctx_ext = slv2_instance_get_extension_data( + instance(i), LV2_CONTEXTS_URI "#MessageContext"); + + if (i == 0 && ctx_ext) { + assert(!_message_funcs); + _message_funcs = (LV2_Contexts_MessageContext*)ctx_ext; + } + } + + slv2_value_free(ctx_ext_uri); + + string port_name; + Path port_path; + + PortImpl* port = NULL; + bool ret = true; + + float* min_values = new float[num_ports]; + float* max_values = new float[num_ports]; + float* def_values = new float[num_ports]; + slv2_plugin_get_port_ranges_float(plug, min_values, max_values, def_values); + + SLV2Value context_pred = slv2_value_new_uri(info->lv2_world(), + "http://lv2plug.in/ns/ext/contexts#context"); + + SLV2Value default_pred = slv2_value_new_uri(info->lv2_world(), + "http://lv2plug.in/ns/lv2core#default"); + + SLV2Value min_size_pred = slv2_value_new_uri(info->lv2_world(), + "http://lv2plug.in/ns/ext/resize-port#minimumSize"); + + SLV2Value port_property_pred = slv2_value_new_uri(info->lv2_world(), + "http://lv2plug.in/ns/lv2core#portProperty"); + + SLV2Value supports_pred = slv2_value_new_uri(info->lv2_world(), + "http://lv2plug.in/ns/ext/atom#supports"); + + //SLV2Value as_large_as_pred = slv2_value_new_uri(info->lv2_world(), + // "http://lv2plug.in/ns/ext/resize-port#asLargeAs"); + + for (uint32_t j = 0; j < num_ports; ++j) { + SLV2Port id = slv2_plugin_get_port_by_index(plug, j); + + // LV2 port symbols are guaranteed to be unique, valid C identifiers + port_name = slv2_value_as_string(slv2_port_get_symbol(plug, id)); + + if (!Symbol::is_valid(port_name)) { + error << "Plugin " << _lv2_plugin->uri() << " port " << j + << " has illegal symbol `" << port_name << "'" << endl; + ret = false; + break; + } + + assert(port_name.find('/') == string::npos); + + port_path = path().child(port_name); + + Raul::Atom val; + PortType data_type = PortType::UNKNOWN; + if (slv2_port_is_a(plug, id, info->control_class)) { + data_type = PortType::CONTROL; + } else if (slv2_port_is_a(plug, id, info->audio_class)) { + data_type = PortType::AUDIO; + } else if (slv2_port_is_a(plug, id, info->event_class)) { + data_type = PortType::EVENTS; + } else if (slv2_port_is_a(plug, id, info->value_port_class)) { + data_type = PortType::VALUE; + } else if (slv2_port_is_a(plug, id, info->message_port_class)) { + data_type = PortType::MESSAGE; + } + + port_buffer_size = bufs.default_buffer_size(data_type); + + if (data_type == PortType::VALUE || data_type == PortType::MESSAGE) { + // Get default value, and its length + SLV2Values defaults = slv2_port_get_value(plug, id, default_pred); + SLV2_FOREACH(i, defaults) { + SLV2Value d = slv2_values_get(defaults, i); + if (slv2_value_is_string(d)) { + const char* str_val = slv2_value_as_string(d); + const size_t str_val_len = strlen(str_val); + val = str_val; + port_buffer_size = str_val_len; + } + } + + // Get minimum size, if set in data + SLV2Values sizes = slv2_port_get_value(plug, id, min_size_pred); + SLV2_FOREACH(i, sizes) { + SLV2Value d = slv2_values_get(sizes, i); + if (slv2_value_is_int(d)) { + size_t size_val = slv2_value_as_int(d); + port_buffer_size = size_val; + } + } + } + + 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 == PortType::UNKNOWN || direction == UNKNOWN) { + warn << "Unknown type or direction for port `" << port_name << "'" << endl; + ret = false; + break; + } + + if (val.type() == Atom::NIL) + val = isnan(def_values[j]) ? 0.0f : def_values[j]; + + if (direction == INPUT) + port = new InputPort(bufs, this, port_name, j, _polyphony, data_type, val); + else + port = new OutputPort(bufs, this, port_name, j, _polyphony, data_type, val); + + if (direction == INPUT && data_type == PortType::CONTROL) { + port->set_value(val); + if (!isnan(min_values[j])) { + port->set_property(uris.lv2_minimum, min_values[j]); + } + if (!isnan(max_values[j])) { + port->set_property(uris.lv2_maximum, max_values[j]); + } + } + + // Set lv2:portProperty properties + SLV2Values properties = slv2_port_get_value(plug, id, port_property_pred); + SLV2_FOREACH(i, properties) { + SLV2Value p = slv2_values_get(properties, i); + if (slv2_value_is_uri(p)) { + port->set_property(uris.lv2_portProperty, Raul::URI(slv2_value_as_uri(p))); + } + } + + // Set atom:supports properties + SLV2Values types = slv2_port_get_value(plug, id, supports_pred); + SLV2_FOREACH(i, types) { + SLV2Value type = slv2_values_get(types, i); + if (slv2_value_is_uri(type)) { + port->add_property(uris.atom_supports, Raul::URI(slv2_value_as_uri(type))); + } + } + + SLV2Values contexts = slv2_port_get_value(plug, id, context_pred); + SLV2_FOREACH(i, contexts) { + SLV2Value c = slv2_values_get(contexts, i); + const char* context = slv2_value_as_string(c); + if (!strcmp(LV2_CONTEXTS_URI "#MessageContext", context)) { + if (!_message_funcs) { + warn << _lv2_plugin->uri() + << " has a message port, but no context extension data." << endl; + } + port->set_context(Context::MESSAGE); + } else { + warn << _lv2_plugin->uri() << " port " << i << " has unknown context " + << slv2_value_as_string(c) + << endl; + } + } + + _ports->at(j) = port; + } + + if (!ret) { + delete _ports; + _ports = NULL; + delete _instances; + _instances = NULL; + } + + delete[] min_values; + delete[] max_values; + delete[] def_values; + slv2_value_free(context_pred); + slv2_value_free(default_pred); + slv2_value_free(min_size_pred); + slv2_value_free(port_property_pred); + + return ret; +} + +void +LV2Node::activate(BufferFactory& bufs) +{ + NodeImpl::activate(bufs); + + for (uint32_t i = 0; i < _polyphony; ++i) + slv2_instance_activate(instance(i)); +} + +void +LV2Node::deactivate() +{ + NodeImpl::deactivate(); + + for (uint32_t i = 0; i < _polyphony; ++i) + slv2_instance_deactivate(instance(i)); +} + +void +LV2Node::message_run(MessageContext& context) +{ + for (size_t i = 0; i < num_ports(); ++i) { + PortImpl* const port = _ports->at(i); + if (port->context() == Context::MESSAGE) + port->pre_process(context); + } + + if (!_valid_ports) + _valid_ports = calloc(num_ports() / 8, 1); + + if (_message_funcs) + (*_message_funcs->run)(instance(0)->lv2_handle, _valid_ports, _valid_ports); +} + +void +LV2Node::process(ProcessContext& context) +{ + NodeImpl::pre_process(context); + + for (uint32_t i = 0; i < _polyphony; ++i) + slv2_instance_run(instance(i), context.nframes()); + + NodeImpl::post_process(context); +} + +void +LV2Node::set_port_buffer(uint32_t voice, uint32_t port_num, + IntrusivePtr buf, SampleCount offset) +{ + NodeImpl::set_port_buffer(voice, port_num, buf, offset); + slv2_instance_connect_port(instance(voice), port_num, + buf ? buf->port_data(_ports->at(port_num)->buffer_type(), offset) : NULL); +} + +} // namespace Server +} // namespace Ingen + diff --git a/src/server/LV2Node.hpp b/src/server/LV2Node.hpp new file mode 100644 index 00000000..4702d24d --- /dev/null +++ b/src/server/LV2Node.hpp @@ -0,0 +1,82 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_LV2NODE_HPP +#define INGEN_ENGINE_LV2NODE_HPP + +#include +#include "slv2/slv2.h" +#include "raul/IntrusivePtr.hpp" +#include "lv2/lv2plug.in/ns/ext/contexts/contexts.h" +#include "types.hpp" +#include "NodeImpl.hpp" +#include "LV2Features.hpp" + +namespace Ingen { +namespace Server { + +class LV2Plugin; + +/** An instance of a LV2 plugin. + * + * \ingroup engine + */ +class LV2Node : public NodeImpl +{ +public: + LV2Node(LV2Plugin* plugin, + const std::string& name, + bool polyphonic, + PatchImpl* parent, + SampleRate srate); + + ~LV2Node(); + + bool instantiate(BufferFactory& bufs); + + bool prepare_poly(BufferFactory& bufs, uint32_t poly); + bool apply_poly(Raul::Maid& maid, uint32_t poly); + + void activate(BufferFactory& bufs); + void deactivate(); + + void message_run(MessageContext& context); + + void process(ProcessContext& context); + + void set_port_buffer(uint32_t voice, uint32_t port_num, + IntrusivePtr buf, SampleCount offset); + +protected: + inline SLV2Instance instance(uint32_t voice) { return (SLV2Instance)(*_instances)[voice].get(); } + + typedef Raul::Array< SharedPtr > Instances; + + LV2Plugin* _lv2_plugin; + Instances* _instances; + Instances* _prepared_instances; + + LV2_Contexts_MessageContext* _message_funcs; + + SharedPtr _features; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_LV2NODE_HPP + diff --git a/src/server/LV2Plugin.cpp b/src/server/LV2Plugin.cpp new file mode 100644 index 00000000..ed25d1ac --- /dev/null +++ b/src/server/LV2Plugin.cpp @@ -0,0 +1,110 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "sord/sordmm.hpp" + +#include "shared/LV2URIMap.hpp" +#include "Driver.hpp" +#include "Engine.hpp" +#include "LV2Node.hpp" +#include "LV2Plugin.hpp" +#include "NodeImpl.hpp" + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { + +LV2Plugin::LV2Plugin(SharedPtr lv2_info, const std::string& uri) + : PluginImpl(*lv2_info->world().uris().get(), Plugin::LV2, uri) + , _slv2_plugin(NULL) + , _lv2_info(lv2_info) +{ + set_property(_uris.rdf_type, _uris.lv2_Plugin); +} + +const string +LV2Plugin::symbol() const +{ + string working = uri().str(); + 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"; +} + +NodeImpl* +LV2Plugin::instantiate(BufferFactory& bufs, + const string& name, + bool polyphonic, + PatchImpl* parent, + Engine& engine) +{ + const SampleCount srate = engine.driver()->sample_rate(); + + load(); // FIXME: unload at some point + + LV2Node* n = new LV2Node(this, name, polyphonic, parent, srate); + + if ( ! n->instantiate(bufs) ) { + delete n; + n = NULL; + } + + return n; +} + +void +LV2Plugin::slv2_plugin(SLV2Plugin p) +{ + _slv2_plugin = p; +} + +const std::string& +LV2Plugin::library_path() const +{ + static const std::string empty_string; + if (_library_path.empty()) { + SLV2Value v = slv2_plugin_get_library_uri(_slv2_plugin); + if (v) { + _library_path = slv2_uri_to_path(slv2_value_as_uri(v)); + } else { + warn << uri() << " has no library path" << endl; + return empty_string; + } + } + + return _library_path; +} + +} // namespace Server +} // namespace Ingen diff --git a/src/server/LV2Plugin.hpp b/src/server/LV2Plugin.hpp new file mode 100644 index 00000000..e4360398 --- /dev/null +++ b/src/server/LV2Plugin.hpp @@ -0,0 +1,76 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_LV2PLUGIN_HPP +#define INGEN_ENGINE_LV2PLUGIN_HPP + +#include "ingen-config.h" + +#ifndef HAVE_SLV2 +#error "This file requires SLV2, but HAVE_SLV2 is not defined. Please report." +#endif + +#include +#include + +#include +#include + +#include "slv2/slv2.h" +#include "raul/SharedPtr.hpp" + +#include "PluginImpl.hpp" +#include "LV2Info.hpp" + +namespace Ingen { +namespace Server { + +class PatchImpl; +class NodeImpl; + +/** Implementation of an LV2 plugin (loaded shared library). + */ +class LV2Plugin : public PluginImpl +{ +public: + LV2Plugin(SharedPtr lv2_info, const std::string& uri); + + NodeImpl* instantiate(BufferFactory& bufs, + const std::string& name, + bool polyphonic, + PatchImpl* parent, + Engine& engine); + + const std::string symbol() const; + + SharedPtr lv2_info() const { return _lv2_info; } + + const std::string& library_path() const; + + SLV2Plugin slv2_plugin() const { return _slv2_plugin; } + void slv2_plugin(SLV2Plugin p); + +private: + SLV2Plugin _slv2_plugin; + SharedPtr _lv2_info; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_LV2PLUGIN_HPP + diff --git a/src/server/LV2RequestRunFeature.hpp b/src/server/LV2RequestRunFeature.hpp new file mode 100644 index 00000000..88010bb9 --- /dev/null +++ b/src/server/LV2RequestRunFeature.hpp @@ -0,0 +1,84 @@ +/* This file is part of Ingen. + * Copyright 2010-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_LV2_REQUEST_RUN_FEATURE_HPP +#define INGEN_ENGINE_LV2_REQUEST_RUN_FEATURE_HPP + +#include "lv2/lv2plug.in/ns/ext/contexts/contexts.h" + +#include "raul/log.hpp" + +#include "shared/LV2Features.hpp" + +#include "Driver.hpp" +#include "Engine.hpp" +#include "MessageContext.hpp" +#include "NodeImpl.hpp" +#include "PortImpl.hpp" + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { + +struct RequestRunFeature : public Ingen::Shared::LV2Features::Feature { + struct Data { + inline Data(Shared::World* w, Node* n) : world(w), node(n) {} + Shared::World* world; + Node* node; + }; + + static void request_run(LV2_Contexts_Request_Run_Data data_ptr, + uint32_t context_uri) { + Data* data = reinterpret_cast(data_ptr); + if (!data->world->local_engine()) + return; + + Engine* engine = (Engine*)data->world->local_engine().get(); + engine->message_context()->run( + dynamic_cast(data->node), + engine->driver()->frame_time()); + } + + static void delete_feature(LV2_Feature* feature) { + delete (Data*)feature->data; + free(feature); + } + + SharedPtr feature(Shared::World* world, Node* n) { + NodeImpl* node = dynamic_cast(n); + if (!node) + return SharedPtr(); + + typedef LV2_Contexts_Request_Run_Feature Feature; + Feature* data = (Feature*)malloc(sizeof(Feature)); + data->data = new Data(world, n); + data->request_run = &request_run; + + LV2_Feature* f = (LV2_Feature*)malloc(sizeof(LV2_Feature)); + f->URI = LV2_CONTEXTS_URI "#RequestRunFeature"; + f->data = data; + + return SharedPtr(f, &delete_feature); + } +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_LV2_REQUEST_RUN_FEATURE_HPP diff --git a/src/server/LV2ResizeFeature.hpp b/src/server/LV2ResizeFeature.hpp new file mode 100644 index 00000000..095796a7 --- /dev/null +++ b/src/server/LV2ResizeFeature.hpp @@ -0,0 +1,73 @@ +/* This file is part of Ingen. + * Copyright 2009-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_LV2RESIZEFEATURE_HPP +#define INGEN_ENGINE_LV2RESIZEFEATURE_HPP + +#include "raul/log.hpp" +#include "lv2/lv2plug.in/ns/ext/resize-port/resize-port.h" +#include "shared/LV2Features.hpp" +#include "NodeImpl.hpp" +#include "PortImpl.hpp" + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { + +struct ResizeFeature : public Ingen::Shared::LV2Features::Feature { + static bool resize_port(LV2_Resize_Port_Feature_Data data, + uint32_t index, + size_t size) { + NodeImpl* node = (NodeImpl*)data; + PortImpl* port = node->port_impl(index); + switch (port->context()) { + case Context::MESSAGE: + port->buffer(0)->resize(size); + port->connect_buffers(); + return true; + default: + // TODO: Implement realtime allocator and support this in audio thread + return false; + } + } + + static void delete_feature(LV2_Feature* feature) { + free(feature->data); + free(feature); + } + + SharedPtr feature(Shared::World* w, Node* n) { + NodeImpl* node = dynamic_cast(n); + if (!node) + return SharedPtr(); + LV2_Resize_Port_Feature* data + = (LV2_Resize_Port_Feature*)malloc(sizeof(LV2_Resize_Port_Feature)); + data->data = node; + data->resize_port = &resize_port; + LV2_Feature* f = (LV2_Feature*)malloc(sizeof(LV2_Feature)); + f->URI = LV2_RESIZE_PORT_URI; + f->data = data; + return SharedPtr(f, &delete_feature); + } +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_LV2RESIZEFEATURE_HPP diff --git a/src/server/MessageContext.cpp b/src/server/MessageContext.cpp new file mode 100644 index 00000000..0ac385bd --- /dev/null +++ b/src/server/MessageContext.cpp @@ -0,0 +1,126 @@ +/* This file is part of Ingen. + * Copyright 2008-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "lv2/lv2plug.in/ns/ext/contexts/contexts.h" +#include "raul/log.hpp" +#include "ConnectionImpl.hpp" +#include "Engine.hpp" +#include "MessageContext.hpp" +#include "NodeImpl.hpp" +#include "PatchImpl.hpp" +#include "PortImpl.hpp" +#include "ProcessContext.hpp" +#include "ThreadManager.hpp" + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { + +void +MessageContext::run(NodeImpl* node, FrameTime time) +{ + if (ThreadManager::thread_is(THREAD_PROCESS)) { + const Request r(time, node); + _requests.write(sizeof(Request), &r); + // signal() will be called at the end of this process cycle + } else { + assert(node); + Glib::Mutex::Lock lock(_mutex); + _non_rt_request = Request(time, node); + _sem.post(); + _cond.wait(_mutex); + } +} + +void +MessageContext::_run() +{ + Request req; + + while (true) { + _sem.wait(); + + // Enqueue a request from the pre-process thread + { + Glib::Mutex::Lock lock(_mutex); + const Request req = _non_rt_request; + if (req.node) { + _queue.insert(req); + _end_time = std::max(_end_time, req.time); + _cond.broadcast(); // Notify caller we got the message + } + } + + // Enqueue (and thereby sort) requests from audio thread + while (has_requests()) { + _requests.full_read(sizeof(Request), &req); + if (req.node) { + _queue.insert(req); + } else { + _end_time = req.time; + break; + } + } + + // Run events in time increasing order + // Note that executing nodes may insert further events into the queue + while (!_queue.empty()) { + const Request req = *_queue.begin(); + + // Break if all events during this cycle have been consumed + // (the queue may contain generated events with later times) + if (req.time > _end_time) { + break; + } + + _queue.erase(_queue.begin()); + execute(req); + } + } +} + +void +MessageContext::execute(const Request& req) +{ + NodeImpl* node = req.node; + node->message_run(*this); + + void* valid_ports = node->valid_ports(); + PatchImpl* patch = node->parent_patch(); + + for (uint32_t i = 0; i < node->num_ports(); ++i) { + PortImpl* p = node->port_impl(i); + if (p->is_output() && p->context() == Context::MESSAGE && + lv2_contexts_port_is_valid(valid_ports, i)) { + PatchImpl::Connections& wires = patch->connections(); + for (PatchImpl::Connections::iterator c = wires.begin(); c != wires.end(); ++c) { + ConnectionImpl* ci = (ConnectionImpl*)c->second.get(); + if (ci->src_port() == p && ci->dst_port()->context() == Context::MESSAGE) { + _queue.insert(Request(req.time, ci->dst_port()->parent_node())); + } + } + } + } + + node->reset_valid_ports(); +} + +} // namespace Server +} // namespace Ingen diff --git a/src/server/MessageContext.hpp b/src/server/MessageContext.hpp new file mode 100644 index 00000000..d3bcfed6 --- /dev/null +++ b/src/server/MessageContext.hpp @@ -0,0 +1,114 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_MESSAGECONTEXT_HPP +#define INGEN_ENGINE_MESSAGECONTEXT_HPP + +#include +#include +#include "raul/Thread.hpp" +#include "raul/Semaphore.hpp" +#include "raul/AtomicPtr.hpp" +#include "lv2/lv2plug.in/ns/ext/atom/atom.h" +#include "Context.hpp" +#include "ProcessContext.hpp" +#include "ThreadManager.hpp" + +namespace Ingen { +namespace Server { + +class NodeImpl; + +/** Context of a message_run() 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 Raul::Thread +{ +public: + explicit MessageContext(Engine& engine) + : Context(engine, MESSAGE) + , Raul::Thread("MessageContext") + , _sem(0) + , _requests(engine.event_queue_size()) + , _end_time(0) + { + Thread::set_context(THREAD_MESSAGE); + } + + /** Schedule a message context run at a certain time. + * Safe to call from either process thread or pre-process thread. + */ + void run(NodeImpl* node, FrameTime time); + +protected: + struct Request { + Request(FrameTime t=0, NodeImpl* n=0) : time(t), node(n) {} + FrameTime time; + NodeImpl* node; + }; + +public: + /** Signal the end of a cycle that has produced messages. + * AUDIO THREAD ONLY. + */ + inline void signal(ProcessContext& context) { + ThreadManager::assert_thread(THREAD_PROCESS); + const Request cycle_end_request(context.end(), NULL); + _requests.write(sizeof(Request), &cycle_end_request); + _sem.post(); + } + + /** Return true iff requests are pending. Safe from any thread. */ + inline bool has_requests() const { + return _requests.read_space() >= sizeof(Request); + } + +protected: + /** Thread run method (wait for and execute requests from process thread */ + void _run(); + + /** Execute a request (possibly enqueueing more requests) */ + void execute(const Request& req); + + Raul::Semaphore _sem; + Raul::RingBuffer _requests; + Glib::Mutex _mutex; + Glib::Cond _cond; + Request _non_rt_request; + + struct RequestEarlier { + bool operator()(const Request& r1, const Request& r2) { + return r1.time < r2.time; + } + }; + + typedef std::set Queue; + Queue _queue; + FrameTime _end_time; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_MESSAGECONTEXT_HPP + diff --git a/src/server/NodeFactory.cpp b/src/server/NodeFactory.cpp new file mode 100644 index 00000000..9c6fc47c --- /dev/null +++ b/src/server/NodeFactory.cpp @@ -0,0 +1,148 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "sord/sordmm.hpp" +#include "raul/log.hpp" +#include "ingen-config.h" +#include "shared/World.hpp" +#include "internals/Controller.hpp" +#include "internals/Delay.hpp" +#include "internals/Note.hpp" +#include "internals/Trigger.hpp" +#include "Engine.hpp" +#include "InternalPlugin.hpp" +#include "NodeFactory.hpp" +#include "PatchImpl.hpp" +#include "ThreadManager.hpp" +#ifdef HAVE_SLV2 +#include "slv2/slv2.h" +#include "LV2Plugin.hpp" +#include "LV2Node.hpp" +#endif + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { + +using namespace Internals; + +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) + delete i->second; + + _plugins.clear(); +} + +const NodeFactory::Plugins& +NodeFactory::plugins() +{ + if (!_has_loaded) { + // TODO: Plugin list refreshing + load_plugins(); + } + return _plugins; +} + +PluginImpl* +NodeFactory::plugin(const Raul::URI& uri) +{ + const Plugins::const_iterator i = _plugins.find(uri); + return ((i != _plugins.end()) ? i->second : NULL); +} + +void +NodeFactory::load_plugins() +{ + ThreadManager::assert_thread(THREAD_PRE_PROCESS); + + // 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 + + _has_loaded = true; + } +} + +void +NodeFactory::load_internal_plugins() +{ + Ingen::Shared::LV2URIMap& uris = *_world->uris().get(); + InternalPlugin* controller_plug = ControllerNode::internal_plugin(uris); + _plugins.insert(make_pair(controller_plug->uri(), controller_plug)); + + InternalPlugin* delay_plug = DelayNode::internal_plugin(uris); + _plugins.insert(make_pair(delay_plug->uri(), delay_plug)); + + InternalPlugin* note_plug = NoteNode::internal_plugin(uris); + _plugins.insert(make_pair(note_plug->uri(), note_plug)); + + InternalPlugin* trigger_plug = TriggerNode::internal_plugin(uris); + _plugins.insert(make_pair(trigger_plug->uri(), trigger_plug)); +} + +#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()); + + SLV2_FOREACH(i, plugins) { + SLV2Plugin lv2_plug = slv2_plugins_get(plugins, i); + + const string uri(slv2_value_as_uri(slv2_plugin_get_uri(lv2_plug))); + + assert(_plugins.find(uri) == _plugins.end()); + + LV2Plugin* const plugin = new LV2Plugin(_lv2_info, uri); + + plugin->slv2_plugin(lv2_plug); + _plugins.insert(make_pair(uri, plugin)); + } +} +#endif // HAVE_SLV2 + +} // namespace Server +} // namespace Ingen diff --git a/src/server/NodeFactory.hpp b/src/server/NodeFactory.hpp new file mode 100644 index 00000000..f4e4ea41 --- /dev/null +++ b/src/server/NodeFactory.hpp @@ -0,0 +1,76 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_NODEFACTORY_HPP +#define INGEN_ENGINE_NODEFACTORY_HPP + +#include +#include +#include +#include +#include +#include "raul/SharedPtr.hpp" +#include "raul/URI.hpp" +#include "shared/World.hpp" +#include "ingen-config.h" + +namespace Ingen { +namespace Server { + +class NodeImpl; +class PatchImpl; +class PluginImpl; +#ifdef HAVE_SLV2 +class LV2Info; +#endif + +/** Discovers and loads plugin libraries. + * + * \ingroup engine + */ +class NodeFactory +{ +public: + explicit NodeFactory(Ingen::Shared::World* world); + ~NodeFactory(); + + void load_plugins(); + + typedef std::map Plugins; + const Plugins& plugins(); + + PluginImpl* plugin(const Raul::URI& uri); + +private: +#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 Server +} // namespace Ingen + +#endif // INGEN_ENGINE_NODEFACTORY_HPP diff --git a/src/server/NodeImpl.cpp b/src/server/NodeImpl.cpp new file mode 100644 index 00000000..eab31716 --- /dev/null +++ b/src/server/NodeImpl.cpp @@ -0,0 +1,264 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "lv2/lv2plug.in/ns/ext/contexts/contexts.h" +#include "raul/List.hpp" +#include "raul/Array.hpp" +#include "util.hpp" +#include "AudioBuffer.hpp" +#include "ClientBroadcaster.hpp" +#include "EngineStore.hpp" +#include "NodeImpl.hpp" +#include "PatchImpl.hpp" +#include "PluginImpl.hpp" +#include "PortImpl.hpp" +#include "ThreadManager.hpp" + +using namespace std; + +namespace Ingen { +namespace Server { + +NodeImpl::NodeImpl(PluginImpl* plugin, const Raul::Symbol& symbol, bool polyphonic, PatchImpl* parent, SampleRate srate) + : GraphObjectImpl(plugin->uris(), parent, symbol) + , _plugin(plugin) + , _polyphonic(polyphonic) + , _polyphony((polyphonic && parent) ? parent->internal_poly() : 1) + , _srate(srate) + , _valid_ports(NULL) + , _input_ready(1) + , _process_lock(0) + , _n_inputs_ready(0) + , _ports(NULL) + , _providers(new Raul::List()) + , _dependants(new Raul::List()) + , _activated(false) + , _traversed(false) +{ + assert(_plugin); + assert(_polyphony > 0); + assert(_parent == NULL || (_polyphony == parent->internal_poly() || _polyphony == 1)); +} + +NodeImpl::~NodeImpl() +{ + if (_activated) + deactivate(); + + delete _providers; + delete _dependants; + delete _ports; + + free(_valid_ports); +} + +Port* +NodeImpl::port(uint32_t index) const +{ + return (*_ports)[index]; +} + +const Plugin* +NodeImpl::plugin() const +{ + return _plugin; +} + +void +NodeImpl::activate(BufferFactory& bufs) +{ + ThreadManager::assert_thread(THREAD_PRE_PROCESS); + assert(!_activated); + _activated = true; + + for (uint32_t p = 0; p < num_ports(); ++p) { + PortImpl* const port = _ports->at(p); + port->setup_buffers(bufs, port->poly()); + port->connect_buffers(); + for (uint32_t v = 0; v < _polyphony; ++v) { + if (!port->buffer(v)) + continue; + if (port->is_a(PortType::CONTROL)) + ((AudioBuffer*)port->buffer(v).get())->set_value(port->value().get_float(), 0, 0); + else + port->buffer(v)->clear(); + } + } +} + +void +NodeImpl::deactivate() +{ + assert(_activated); + _activated = false; + for (uint32_t i = 0; i < _polyphony; ++i) { + for (unsigned long j = 0; j < num_ports(); ++j) { + PortImpl* const port = _ports->at(j); + if (port->is_output() && port->buffer(i)) + port->buffer(i)->clear(); + } + } +} + +bool +NodeImpl::prepare_poly(BufferFactory& bufs, uint32_t poly) +{ + ThreadManager::assert_thread(THREAD_PRE_PROCESS); + + if (!_polyphonic) + poly = 1; + + if (_ports) + for (size_t i = 0; i < _ports->size(); ++i) + _ports->at(i)->prepare_poly(bufs, poly); + + return true; +} + +bool +NodeImpl::apply_poly(Raul::Maid& maid, uint32_t poly) +{ + ThreadManager::assert_thread(THREAD_PROCESS); + + if (!_polyphonic) + poly = 1; + + _polyphony = poly; + + if (_ports) + for (size_t i = 0; i < num_ports(); ++i) + _ports->at(i)->apply_poly(maid, poly); + + return true; +} + +void +NodeImpl::set_buffer_size(Context& context, BufferFactory& bufs, PortType type, size_t size) +{ + if (_ports) + for (size_t i = 0; i < _ports->size(); ++i) + if (_ports->at(i)->buffer_type() == type && _ports->at(i)->context() == context.id()) + _ports->at(i)->set_buffer_size(context, bufs, size); +} + +void +NodeImpl::reset_input_ready() +{ + _n_inputs_ready = 0; + _process_lock = 0; + _input_ready.reset(0); +} + +bool +NodeImpl::process_lock() +{ + return _process_lock.compare_and_exchange(0, 1); +} + +void +NodeImpl::process_unlock() +{ + _process_lock = 0; +} + +void +NodeImpl::wait_for_input(size_t num_providers) +{ + ThreadManager::assert_thread(THREAD_PROCESS); + assert(_process_lock.get() == 1); + + while ((unsigned)_n_inputs_ready.get() < num_providers) + _input_ready.wait(); +} + +void +NodeImpl::signal_input_ready() +{ + ThreadManager::assert_thread(THREAD_PROCESS); + ++_n_inputs_ready; + _input_ready.post(); +} + +/** Prepare to run a cycle (in the audio thread) + */ +void +NodeImpl::pre_process(Context& context) +{ + ThreadManager::assert_thread(THREAD_PROCESS); + + // Mix down input ports + for (uint32_t i = 0; i < num_ports(); ++i) { + PortImpl* const port = _ports->at(i); + if (port->context() == Context::AUDIO) { + port->pre_process(context); + port->connect_buffers(context.offset()); + } + } +} + +/** Prepare to run a cycle (in the audio thread) + */ +void +NodeImpl::post_process(Context& context) +{ + ThreadManager::assert_thread(THREAD_PROCESS); + + // Write output ports + for (size_t i = 0; _ports && i < _ports->size(); ++i) { + PortImpl* const port = _ports->at(i); + if (port->context() == Context::AUDIO) + _ports->at(i)->post_process(context); + } +} + +/** Flag a port as set (for message context) + */ +void +NodeImpl::set_port_valid(uint32_t port_index) +{ + // Allocate enough space for one bit per port + if (!_valid_ports) + _valid_ports = calloc(num_ports() / 8, 1); + lv2_contexts_set_port_valid(_valid_ports, port_index); +} + +void* +NodeImpl::valid_ports() +{ + return _valid_ports; +} + +void +NodeImpl::reset_valid_ports() +{ + if (_valid_ports) + memset(_valid_ports, '\0', num_ports() / 8); +} + +void +NodeImpl::set_port_buffer(uint32_t voice, uint32_t port_num, + BufferFactory::Ref buf, SampleCount offset) +{ + /*std::cout << path() << " set port " << port_num << " voice " << voice + << " buffer " << buf << " offset " << offset << std::endl;*/ +} + +} // namespace Server +} // namespace Ingen + diff --git a/src/server/NodeImpl.hpp b/src/server/NodeImpl.hpp new file mode 100644 index 00000000..3263727a --- /dev/null +++ b/src/server/NodeImpl.hpp @@ -0,0 +1,223 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_NODEIMPL_HPP +#define INGEN_ENGINE_NODEIMPL_HPP + +#include +#include "raul/Array.hpp" +#include "raul/AtomicInt.hpp" +#include "raul/IntrusivePtr.hpp" +#include "raul/Semaphore.hpp" +#include "ingen/Node.hpp" +#include "GraphObjectImpl.hpp" +#include "types.hpp" + +namespace Raul { template class List; class Maid; } + +namespace Ingen { + +class Plugin; +class Port; + +namespace Server { + +class Buffer; +class BufferFactory; +class Context; +class MessageContext; +class PatchImpl; +class PluginImpl; +class PortImpl; +class ProcessContext; + +/** 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. + * + * \ingroup engine + */ +class NodeImpl : public GraphObjectImpl, virtual public Node +{ +public: + NodeImpl(PluginImpl* plugin, + const Raul::Symbol& symbol, + bool poly, + PatchImpl* parent, + SampleRate rate); + + virtual ~NodeImpl(); + + /** Activate this Node. + * + * This function must 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(BufferFactory& bufs); + + /** Deactivate this Node. + * + * This function must be called in a non-realtime thread after the + * node has been removed from its patch (i.e. processing is finished). + */ + virtual void deactivate(); + + /** Return true iff this node is activated */ + bool activated() { return _activated; } + + /** Parallelism: Reset flags for start of a new cycle. */ + virtual void reset_input_ready(); + + /** 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(); + + /** 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(); + + /** 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); + + /** 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(); + + /** Parallelism: Return the number of providers that have signalled. */ + virtual unsigned n_inputs_ready() const { return _n_inputs_ready.get(); } + + /** Learn the next incoming MIDI event (for internals) */ + virtual void learn() {} + + /** Run the node for one instant in the message thread. */ + virtual void message_run(MessageContext& context) {} + + /** Flag a port as valid (for message context) */ + virtual void set_port_valid(uint32_t index); + + /** Return a bit vector of which ports are valid */ + virtual void* valid_ports(); + + /** Clear all bits in valid_ports() */ + virtual void reset_valid_ports(); + + /** Do whatever needs doing in the process thread before process() is called */ + virtual void pre_process(Context& context); + + /** 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; + + /** Do whatever needs doing in the process thread after process() is called */ + virtual void post_process(Context& context); + + /** Set the buffer of a port to a given buffer (e.g. connect plugin to buffer) */ + virtual void set_port_buffer( + uint32_t voice, + uint32_t port_num, + IntrusivePtr buf, + SampleCount offset); + + virtual Port* port(uint32_t index) const; + virtual PortImpl* port_impl(uint32_t index) const { return (*_ports)[index]; } + + /** Nodes that are connected to this Node's inputs. + * (This Node depends on them) + */ + Raul::List* providers() { return _providers; } + void providers(Raul::List* l) { _providers = l; } + + /** Nodes are are connected to this Node's outputs. + * (They depend on this Node) + */ + Raul::List* dependants() { return _dependants; } + void dependants(Raul::List* l) { _dependants = l; } + + /** Flag node as polyphonic. + * + * Note this will not actually allocate voices etc., prepare_poly + * and apply_poly must be called after this function to truly make + * a node polyphonic. + */ + virtual void set_polyphonic(bool p) { _polyphonic = p; } + + virtual bool prepare_poly(BufferFactory& bufs, uint32_t poly); + virtual bool apply_poly(Raul::Maid& maid, uint32_t poly); + + /** 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 { return _plugin; } + + /** Information about the Plugin this Node is an instance of. + * Not the best name - not all nodes come from plugins (ie Patch) + */ + virtual const Plugin* plugin() const; + + virtual void plugin(PluginImpl* pi) { _plugin = pi; } + + virtual void set_buffer_size(Context& context, BufferFactory& bufs, + PortType type, size_t size); + + /** The Patch this Node belongs to. */ + inline PatchImpl* parent_patch() const { return (PatchImpl*)_parent; } + + SampleRate sample_rate() const { return _srate; } + virtual uint32_t num_ports() const { return _ports ? _ports->size() : 0; } + virtual uint32_t polyphony() const { return _polyphony; } + + /** Used by the process order finding algorithm (ie during connections) */ + bool traversed() const { return _traversed; } + void traversed(bool b) { _traversed = b; } + +protected: + PluginImpl* _plugin; + + bool _polyphonic; + uint32_t _polyphony; + SampleRate _srate; + + void* _valid_ports; ///< Valid port flags for message context + + 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 + + bool _activated; + bool _traversed; ///< Flag for process order algorithm +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_NODEIMPL_HPP diff --git a/src/server/OSCClientSender.cpp b/src/server/OSCClientSender.cpp new file mode 100644 index 00000000..df2a8f3a --- /dev/null +++ b/src/server/OSCClientSender.cpp @@ -0,0 +1,247 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "raul/log.hpp" +#include "raul/AtomLiblo.hpp" + +#include "EngineStore.hpp" +#include "NodeImpl.hpp" +#include "OSCClientSender.hpp" +#include "PatchImpl.hpp" + +#include "PluginImpl.hpp" +#include "PortImpl.hpp" +#include "ingen/ClientInterface.hpp" +#include "util.hpp" + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { + +/** @page client_osc_namespace Client OSC Namespace Documentation + * + *

These are the commands the client recognizes. All monitoring of + * changes in the engine happens via these commands.

+ */ + +/** @page client_osc_namespace + *

/ok

+ * @arg @p response-id :: Integer + * + * @par + * Successful response to some command. + */ +void +OSCClientSender::response_ok(int32_t id) +{ + if (!_enabled) + return; + + + + if (lo_send(_address, "/ok", "i", id, LO_ARGS_END) < 0) { + Raul::error << "Unable to send OK " << id << "! (" + << lo_address_errstr(_address) << ")" << endl; + } +} + +/** @page client_osc_namespace + *

/error

+ * @arg @p response-id :: Integer + * @arg @p message :: String + * + * @par + * Unsuccessful response to some command. + */ +void +OSCClientSender::response_error(int32_t id, const std::string& msg) +{ + if (!_enabled) + return; + + if (lo_send(_address, "/error", "is", id, msg.c_str(), LO_ARGS_END) < 0) { + Raul::error << "Unable to send error " << id << "! (" + << lo_address_errstr(_address) << ")" << endl; + } +} + +/** @page client_osc_namespace + *

/error

+ * @arg @p message :: String + * + * @par + * Notification that an error has occurred. This is for notification of errors + * that aren't a direct response to a user command, i.e. "unexpected" errors. + */ +void +OSCClientSender::error(const std::string& msg) +{ + send("/error", "s", msg.c_str(), LO_ARGS_END); +} + +/** @page client_osc_namespace + *

/put

+ * @arg @p path :: String + * @arg @p predicate :: URI String + * @arg @p value + * @arg @p ... + * + * @par + * PUT a set of properties to a path. + */ +void +OSCClientSender::put(const Raul::URI& path, + const Resource::Properties& properties, + Resource::Graph ctx) +{ + typedef Resource::Properties::const_iterator iterator; + lo_message m = lo_message_new(); + lo_message_add_string(m, path.c_str()); + for (iterator i = properties.begin(); i != properties.end(); ++i) { + lo_message_add_string(m, i->first.c_str()); + Raul::AtomLiblo::lo_message_add_atom(m, i->second); + } + send_message("/put", m); +} + +void +OSCClientSender::delta(const Raul::URI& path, + const Resource::Properties& remove, + const Resource::Properties& add) +{ + warn << "FIXME: OSC DELTA" << endl; +} + +/** @page client_osc_namespace + *

/move

+ * @arg @p old-path :: String + * @arg @p new-path :: String + * + * @par + * MOVE an object to a new path. + * The new path will have the same parent as the old path. + */ +void +OSCClientSender::move(const Path& old_path, const Path& new_path) +{ + send("/move", "ss", old_path.c_str(), new_path.c_str(), LO_ARGS_END); +} + +/** @page client_osc_namespace + *

/delete

+ * @arg @p path :: String + * + * @par + * DELETE an object. + */ +void +OSCClientSender::del(const URI& uri) +{ + send("/delete", "s", uri.c_str(), LO_ARGS_END); +} + +/** @page client_osc_namespace + *

/connect

+ * @arg @p src-path :: String + * @arg @p dst-path :: String + * + * @par + * Notification a new connection has been made. + */ +void +OSCClientSender::connect(const Path& src_port_path, + const Path& dst_port_path) +{ + send("/connect", "ss", src_port_path.c_str(), dst_port_path.c_str(), LO_ARGS_END); +} + +/** @page client_osc_namespace + *

/disconnect

+ * @arg @p src-path :: String + * @arg @p dst-path :: String + * + * @par + * Notification a connection has been unmade. + */ +void +OSCClientSender::disconnect(const URI& src, + const URI& dst) +{ + send("/disconnect", "ss", src.c_str(), dst.c_str(), LO_ARGS_END); +} + +/** @page client_osc_namespace + *

/disconnect_all

+ * @arg @p parent-patch-path :: String + * @arg @p path :: String + * + * @par + * Notification all connections to an object have been disconnected. + */ +void +OSCClientSender::disconnect_all(const Raul::Path& parent_patch_path, + const Raul::Path& path) +{ + send("/disconnect_all", "ss", parent_patch_path.c_str(), path.c_str(), LO_ARGS_END); +} + +/** @page client_osc_namespace + *

/set_property

+ * @arg @p path :: String + * @arg @p key :: URI String + * @arg @p value + * + * @par + * Notification of a property. + */ +void +OSCClientSender::set_property(const URI& path, + const URI& 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()); + AtomLiblo::lo_message_add_atom(m, value); + send_message("/set_property", m); +} + +/** @page client_osc_namespace + *

/activity

+ * @arg @p path :: String + * + * @par + * Notification of "activity" (e.g. port message blinkenlights). + */ +void +OSCClientSender::activity(const Path& path) +{ + if (!_enabled) + return; + + lo_send(_address, "/activity", "s", path.c_str(), LO_ARGS_END); +} + +} // namespace Server +} // namespace Ingen diff --git a/src/server/OSCClientSender.hpp b/src/server/OSCClientSender.hpp new file mode 100644 index 00000000..eb052bc7 --- /dev/null +++ b/src/server/OSCClientSender.hpp @@ -0,0 +1,108 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_OSCCLIENTSENDER_HPP +#define INGEN_ENGINE_OSCCLIENTSENDER_HPP + +#include +#include +#include +#include +#include "ingen/ClientInterface.hpp" +#include "ingen/GraphObject.hpp" +#include "shared/OSCSender.hpp" + +namespace Ingen { + +class ServerInterface; + +namespace Server { + + +/** Implements ClientInterface for OSC clients (sends OSC messages). + * + * \ingroup engine + */ +class OSCClientSender : public ClientInterface, + public Ingen::Shared::OSCSender +{ +public: + explicit OSCClientSender(const Raul::URI& url, + size_t max_packet_size) + : Shared::OSCSender(max_packet_size) + , _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(); } + + Raul::URI uri() const { return _url; } + + /* *** ClientInterface Implementation Below *** */ + + void response_ok(int32_t id); + void response_error(int32_t id, const std::string& msg); + + void error(const std::string& msg); + + virtual void put(const Raul::URI& path, + const Resource::Properties& properties, + Resource::Graph ctx=Resource::DEFAULT); + + virtual void delta(const Raul::URI& path, + const Resource::Properties& remove, + const Resource::Properties& add); + + virtual void del(const Raul::URI& uri); + + virtual void move(const Raul::Path& old_path, + const Raul::Path& new_path); + + virtual void connect(const Raul::Path& src_port_path, + const Raul::Path& dst_port_path); + + virtual void disconnect(const Raul::URI& src, + const Raul::URI& dst); + + virtual void disconnect_all(const Raul::Path& parent_patch_path, + const Raul::Path& path); + + virtual void set_property(const Raul::URI& subject, + const Raul::URI& predicate, + const Raul::Atom& value); + + virtual void activity(const Raul::Path& path); + +private: + Raul::URI _url; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_OSCCLIENTSENDER_HPP + diff --git a/src/server/OSCEngineReceiver.cpp b/src/server/OSCEngineReceiver.cpp new file mode 100644 index 00000000..635856da --- /dev/null +++ b/src/server/OSCEngineReceiver.cpp @@ -0,0 +1,586 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 ENABLE_AVAHI 1 + +#include +#include + +#include + +#include + +#include "raul/AtomLiblo.hpp" +#include "raul/Path.hpp" +#include "raul/SharedPtr.hpp" +#include "raul/log.hpp" + +#include "ingen-config.h" +#include "ingen/ClientInterface.hpp" + +#include "ClientBroadcaster.hpp" +#include "Engine.hpp" +#include "EventSource.hpp" +#include "OSCClientSender.hpp" +#include "OSCEngineReceiver.hpp" +#include "ThreadManager.hpp" + +#define LOG(s) s << "[OSCEngineReceiver] " + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { + +/** @page engine_osc_namespace Server 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.

+ */ + +OSCEngineReceiver::OSCEngineReceiver(Engine& engine, size_t queue_size, uint16_t port) + : QueuedEngineInterface(engine, queue_size) // FIXME + , _server(NULL) +{ + _receive_thread = new ReceiveThread(*this); + + char port_str[6]; + snprintf(port_str, sizeof(port_str), "%u", port); + + _server = lo_server_new(port_str, error_cb); +#ifdef ENABLE_AVAHI + lo_server_avahi_init(_server, "ingen"); +#endif + + if (_server == NULL) { + LOG(error) << "Could not start OSC server. Aborting." << endl; + exit(EXIT_FAILURE); + } else { + char* lo_url = lo_server_get_url(_server); + LOG(info) << "Started OSC server at " << lo_url << endl; + free(lo_url); + } + +#ifdef RAUL_LOG_DEBUG + lo_server_add_method(_server, NULL, NULL, generic_cb, NULL); +#endif + + // 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); + +#ifdef LIBLO_BUNDLES + lo_server_add_bundle_handlers(_server, bundle_start_cb, bundle_end_cb, this); +#endif + + // Commands + lo_server_add_method(_server, "/ping", "i", ping_cb, this); + lo_server_add_method(_server, "/ping_queued", "i", ping_slow_cb, this); + lo_server_add_method(_server, "/register_client", "i", register_client_cb, this); + lo_server_add_method(_server, "/unregister_client", "i", unregister_client_cb, this); + lo_server_add_method(_server, "/put", NULL, put_cb, this); + lo_server_add_method(_server, "/move", "iss", move_cb, this); + lo_server_add_method(_server, "/delete", "is", del_cb, this); + lo_server_add_method(_server, "/connect", "iss", connect_cb, this); + lo_server_add_method(_server, "/disconnect", "iss", disconnect_cb, this); + lo_server_add_method(_server, "/disconnect_all", "iss", disconnect_all_cb, this); + lo_server_add_method(_server, "/note_on", "isii", note_on_cb, this); + lo_server_add_method(_server, "/note_off", "isi", note_off_cb, this); + lo_server_add_method(_server, "/all_notes_off", "isi", all_notes_off_cb, this); + lo_server_add_method(_server, "/learn", "is", learn_cb, this); + lo_server_add_method(_server, "/set_property", NULL, set_property_cb, this); + + // Queries + lo_server_add_method(_server, "/request_property", "iss", request_property_cb, this); + lo_server_add_method(_server, "/get", "is", get_cb, this); + + lo_server_add_method(_server, NULL, NULL, unknown_cb, NULL); + + Thread::set_name("OSCEngineReceiver"); + start(); + _receive_thread->set_name("OSCEngineReceiver Listener"); + _receive_thread->start(); + _receive_thread->set_scheduling(SCHED_FIFO, 5); +} + +OSCEngineReceiver::~OSCEngineReceiver() +{ + _receive_thread->stop(); + stop(); + delete _receive_thread; + + if (_server != NULL) { +#ifdef ENABLE_AVAHI + lo_server_avahi_free(_server); +#endif + lo_server_free(_server); + _server = NULL; + } +} + +/** 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) { + // 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 request for this message, if necessary. + * + * This is based on the fact that the current request is stored in a ref + * counted pointer, and events just take a reference to that. Thus, events + * may delete their request 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 requests 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->_request; + + /* 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->_request = SharedPtr(new Request(me, client, id)); + else + me->_request = SharedPtr(new Request(me)); + } + + if (id != -1) { + me->set_next_response_id(id); + } else { + me->disable_responses(); + } + + free(url); + + // If this returns 0 no OSC commands will work + return 1; +} + +#ifdef LIBLO_BUNDLES +int +OSCEngineReceiver::_bundle_start_cb(lo_timetag time) +{ + info << "BUNDLE START" << endl; + return 0; +} + +int +OSCEngineReceiver::_bundle_end_cb() +{ + info << "BUNDLE END" << endl; + return 0; +} +#endif + +void +OSCEngineReceiver::error_cb(int num, const char* msg, const char* path) +{ + error << "liblo server error" << num; + if (path) { + error << " for path `" << path << "'"; + } + error << " (" << msg << ")" << endl; +} + +/** @page engine_osc_namespace + *

/ping

+ * @arg @p response-id :: Integer + */ +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, "/ok", "i", argv[0]->i) < 0) + warn << "Unable to send response (" << lo_address_errstr(addr) << ")" << endl; + return 0; +} + +/** @page engine_osc_namespace + *

/ping_queued

+ * @arg @p response-id :: Integer + * + * @par + * Reply to sender with a successful response after going through the + * event queue. This is useful for checking if the engine is actually active, + * or for sending after several events as a sentinel and wait on it's response, + * to know when all previous events have finished processing. + */ +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 + *

/register_client

+ * @arg @p response-id :: Integer + * + * @par + * Register a new client with the engine. The incoming address will be + * used for the new registered client. + */ +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, + _engine.world()->conf()->option("packet-size").get_int32()); + register_client(client); + free(url); + + return 0; +} + +/** @page engine_osc_namespace + *

/unregister_client

+ * @arg @p response-id :: Integer + * + * @par + * Unregister a client. + */ +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 + *

/get

+ * @arg @p response-id :: Integer + * @arg @p uri :: URI String + * + * @par + * Request all properties of an object. + */ +int +OSCEngineReceiver::_get_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + get(&argv[1]->s); + return 0; +} + +/** @page engine_osc_namespace + *

/put

+ * @arg @p response-id :: Integer + * @arg @p path :: String + * @arg @p predicate :: URI String + * @arg @p value + * @arg @p ... + * + * @par + * PUT a set of properties to a path. + */ +int +OSCEngineReceiver::_put_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* obj_path = &argv[1]->s; + Resource::Properties prop; + for (int i = 2; i < argc-1; i += 2) + prop.insert(make_pair(&argv[i]->s, AtomLiblo::lo_arg_to_atom(types[i+1], argv[i+1]))); + put(obj_path, prop); + return 0; +} + +/** @page engine_osc_namespace + *

/move

+ * @arg @p response-id :: Integer + * @arg @p old-path :: String + * @arg @p new-path :: String + * + * @par + * MOVE an object to a new path. + */ +int +OSCEngineReceiver::_move_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* old_path = &argv[1]->s; + const char* new_path = &argv[2]->s; + + move(old_path, new_path); + return 0; +} + +/** @page engine_osc_namespace + *

/delete

+ * @arg @p response-id :: Integer + * @arg @p path :: String + * + * @par + * DELETE an object. + */ +int +OSCEngineReceiver::_del_cb(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg) +{ + const char* uri = &argv[1]->s; + + del(uri); + return 0; +} + +/** @page engine_osc_namespace + *

/connect

+ * @arg @p response-id :: Integer + * @arg @p src-port-path :: String + * @arg @p dst-port-path :: String + * + * @par + * Connect two ports (which must be in the same patch). + */ +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 + *

/disconnect

+ * @arg @p response-id :: Integer + * @arg @p src-port-path :: String + * @arg @p dst-port-path :: String + * + * @par + * Disconnect two ports. + */ +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 + *

/disconnect_all

+ * @arg @p response-id :: Integer + * @arg @p patch-path :: String + * @arg @p object-path :: String + * + * @par + * Disconnect all connections to/from a node/port. + */ +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 + *

/note_on

+ * @arg @p response-id :: Integer + * @arg @p node-path :: String + * @arg @p note-num (int) + * @arg @p velocity (int) + * + * @par + * Trigger a note-on, just as if it came from MIDI. + */ +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 uint8_t note_num = argv[2]->i; + const uint8_t velocity = argv[3]->i; + */ + warn << "TODO: OSC note on" << endl; + //note_on(node_path, note_num, velocity); + return 0; +} + +/** @page engine_osc_namespace + *

/note_off

+ * @arg @p response-id :: Integer + * @arg @p node-path :: String + * @arg @p note-num :: Integer + * + * @par + * Trigger a note-off, just as if it came from MIDI. + */ +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 uint8_t note_num = argv[2]->i; + */ + warn << "TODO: OSC note off" << endl; + //note_off(patch_path, note_num); + return 0; +} + +/** @page engine_osc_namespace + *

/all_notes_off

+ * @arg @p response-id :: Integer + * @arg @p patch-path :: String + * + * @par + * Trigger a note-off for all voices, just as if it came from MIDI. + */ +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; + */ + warn << "TODO: OSC all notes off" << endl; + //all_notes_off(patch_path); + return 0; +} + +/** @page engine_osc_namespace + *

/set_property

+ * @arg @p response-id :: Integer + * @arg @p uri :: URI String + * @arg @p key :: URI String + * @arg @p value :: String + * + * @par + * Set a property on a graph object. + */ +int +OSCEngineReceiver::_set_property_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 + *

/request_property

+ * @arg @p response-id :: Integer + * @arg @p uri :: URI String + * @arg @p key :: URI String + * + * Request the value of a property on an object. + */ +int +OSCEngineReceiver::_request_property_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_property(object_path, key); + 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("[OSCEngineReceiver] %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); + + warn << "Unknown OSC command " << path << " (" << types << ") " + << "received from " << url << endl; + + string error_msg = "Unknown command: "; + error_msg.append(path).append(" ").append(types); + + lo_send(addr, "/error", "s", error_msg.c_str(), LO_ARGS_END); + free(url); + + return 0; +} + +} // namespace Server +} // namespace Ingen diff --git a/src/server/OSCEngineReceiver.hpp b/src/server/OSCEngineReceiver.hpp new file mode 100644 index 00000000..8a76fa01 --- /dev/null +++ b/src/server/OSCEngineReceiver.hpp @@ -0,0 +1,118 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_OSCENGINERECEIVER_HPP +#define INGEN_ENGINE_OSCENGINERECEIVER_HPP + +#include +#include +#include "QueuedEngineInterface.hpp" +#include "Request.hpp" +#include "ingen-config.h" + +namespace Ingen { +namespace Server { + +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 ServerInterface (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(); + +private: + struct ReceiveThread : public Raul::Thread { + explicit ReceiveThread(OSCEngineReceiver& receiver) : _receiver(receiver) {} + virtual void _run(); + private: + OSCEngineReceiver& _receiver; + }; + + friend class ReceiveThread; + + ReceiveThread* _receive_thread; + +#ifdef LIBLO_BUNDLES + static int bundle_start_cb(lo_timetag time, void* myself) { + return ((OSCEngineReceiver*)myself)->_bundle_start_cb(time); + } + static int bundle_end_cb(void* myself) { + return ((OSCEngineReceiver*)myself)->_bundle_end_cb(); + } + + int _bundle_start_cb(lo_timetag time); + int _bundle_end_cb(); +#endif + + 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(get); + LO_HANDLER(put); + LO_HANDLER(move); + LO_HANDLER(del); + LO_HANDLER(connect); + LO_HANDLER(disconnect); + LO_HANDLER(disconnect_all); + LO_HANDLER(note_on); + LO_HANDLER(note_off); + LO_HANDLER(all_notes_off); + LO_HANDLER(learn); + LO_HANDLER(set_property); + LO_HANDLER(property_set); + LO_HANDLER(request_property); + + lo_server _server; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_OSCENGINERECEIVER_HPP diff --git a/src/server/ObjectBuffer.cpp b/src/server/ObjectBuffer.cpp new file mode 100644 index 00000000..d09fbb96 --- /dev/null +++ b/src/server/ObjectBuffer.cpp @@ -0,0 +1,147 @@ +/* This file is part of Ingen. + * Copyright 2009-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "raul/log.hpp" +#include "lv2/lv2plug.in/ns/ext/uri-map/uri-map.h" +#include "ingen-config.h" +#include "shared/LV2Features.hpp" +#include "shared/LV2URIMap.hpp" +#include "ObjectBuffer.hpp" +#include "Engine.hpp" + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { + +/** Allocate a new object buffer. + * \a capacity is in bytes, including LV2_Atom header + */ +ObjectBuffer::ObjectBuffer(BufferFactory& bufs, size_t capacity) + : Buffer(bufs, PortType(PortType::VALUE), capacity) +{ + capacity += sizeof(LV2_Atom); + +#ifdef HAVE_POSIX_MEMALIGN + const int ret = posix_memalign((void**)&_buf, 16, capacity); +#else + _buf = (LV2_Atom*)malloc(capacity); + const int ret = (_buf != NULL) ? 0 : -1; +#endif + + if (ret != 0) { + error << "Failed to allocate object buffer. Aborting." << endl; + exit(EXIT_FAILURE); + } + + clear(); +} + +ObjectBuffer::~ObjectBuffer() +{ + free(_buf); +} + +void +ObjectBuffer::clear() +{ + // null + _buf->type = 0; + _buf->size = 0; +} + +void +ObjectBuffer::copy(Context& context, const Buffer* src_buf) +{ + const ObjectBuffer* src = dynamic_cast(src_buf); + if (!src || src == this || src->_buf == _buf) + return; + + // Copy only if src is a POD object that fits + if (src->_buf->type != 0 && src_buf->size() <= size()) + memcpy(_buf, src->_buf, sizeof(LV2_Atom) + src_buf->size()); +} + +void +ObjectBuffer::resize(size_t size) +{ + const uint32_t contents_size = sizeof(LV2_Atom) + _buf->size; + + _buf = (LV2_Atom*)realloc(_buf, sizeof(LV2_Atom) + size); + _size = size + sizeof(LV2_Atom); + + // If we shrunk and chopped the current contents, clear corrupt data + if (size < contents_size) + clear(); +} + +void* +ObjectBuffer::port_data(PortType port_type, SampleCount offset) +{ + switch (port_type.symbol()) { + case PortType::CONTROL: + case PortType::AUDIO: + switch (_type.symbol()) { + case PortType::CONTROL: + return (float*)atom()->body; + case PortType::AUDIO: + return (float*)((LV2_Atom_Vector*)atom()->body)->elems + offset; + default: + warn << "Audio data requested from non-audio buffer" << endl; + return NULL; + } + break; + default: + return _buf; + } +} + +const void* +ObjectBuffer::port_data(PortType port_type, SampleCount offset) const +{ + switch (port_type.symbol()) { + case PortType::CONTROL: + case PortType::AUDIO: + switch (_type.symbol()) { + case PortType::CONTROL: + return (float*)atom()->body; + case PortType::AUDIO: + return (float*)((LV2_Atom_Vector*)atom()->body)->elems + offset; + default: + warn << "Audio data requested from non-audio buffer" << endl; + return NULL; + } + break; + default: + return _buf; + } +} + +void +ObjectBuffer::prepare_write(Context& context) +{ + _buf->size = _size - sizeof(LV2_Atom); +} + +} // namespace Server +} // namespace Ingen + diff --git a/src/server/ObjectBuffer.hpp b/src/server/ObjectBuffer.hpp new file mode 100644 index 00000000..11481ad1 --- /dev/null +++ b/src/server/ObjectBuffer.hpp @@ -0,0 +1,56 @@ +/* This file is part of Ingen. + * Copyright 2009-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_OBJECTBUFFER_HPP +#define INGEN_ENGINE_OBJECTBUFFER_HPP + +#include "lv2/lv2plug.in/ns/ext/atom/atom.h" +#include "ingen/PortType.hpp" +#include "Buffer.hpp" + +namespace Ingen { +namespace Server { + +class Context; + +class ObjectBuffer : public Buffer { +public: + ObjectBuffer(BufferFactory& bufs, size_t capacity); + ~ObjectBuffer(); + + void clear(); + + void* port_data(PortType port_type, SampleCount offset); + const void* port_data(PortType port_type, SampleCount offset) const; + + void prepare_write(Context& context); + + void copy(Context& context, const Buffer* src); + + void resize(size_t size); + + LV2_Atom* atom() { return _buf; } + const LV2_Atom* atom() const { return _buf; } + +private: + LV2_Atom* _buf; ///< Contents +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_OBJECTBUFFER_HPP diff --git a/src/server/ObjectSender.cpp b/src/server/ObjectSender.cpp new file mode 100644 index 00000000..22aff285 --- /dev/null +++ b/src/server/ObjectSender.cpp @@ -0,0 +1,149 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "ingen/ClientInterface.hpp" +#include "shared/LV2URIMap.hpp" +#include "EngineStore.hpp" +#include "PatchImpl.hpp" +#include "NodeImpl.hpp" +#include "PortImpl.hpp" +#include "ConnectionImpl.hpp" +#include "NodeFactory.hpp" +#include "ingen/PortType.hpp" +#include "AudioBuffer.hpp" + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { + +void +ObjectSender::send_object(ClientInterface* client, + const GraphObjectImpl* object, + bool recursive) +{ + const PatchImpl* patch = dynamic_cast(object); + if (patch) { + send_patch(client, patch, recursive); + return; + } + + const NodeImpl* node = dynamic_cast(object); + if (node) { + send_node(client, node, recursive); + return; + } + + const PortImpl* port = dynamic_cast(object); + if (port) { + send_port(client, port); + return; + } +} + +void +ObjectSender::send_patch(ClientInterface* client, const PatchImpl* patch, bool recursive, bool bundle) +{ + if (bundle) + client->bundle_begin(); + + client->put(patch->path(), + patch->properties(Resource::INTERNAL), + Resource::INTERNAL); + + client->put(patch->path(), + patch->properties(Resource::EXTERNAL), + Resource::EXTERNAL); + + 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, false); + } + + // Send ports + for (uint32_t i=0; i < patch->num_ports(); ++i) { + PortImpl* const port = patch->port_impl(i); + send_port(client, port, false); + } + + // Send connections + for (PatchImpl::Connections::const_iterator j = patch->connections().begin(); + j != patch->connections().end(); ++j) + client->connect(j->second->src_port_path(), j->second->dst_port_path()); + } + + if (bundle) + client->bundle_end(); +} + +/** Sends a node or a patch */ +void +ObjectSender::send_node(ClientInterface* client, const NodeImpl* node, bool recursive, bool bundle) +{ + PluginImpl* const plugin = node->plugin_impl(); + + if (plugin->type() == Plugin::Patch) { + send_patch(client, (PatchImpl*)node, recursive); + return; + } + + if (plugin->uri().length() == 0) { + error << "Node " << node->path() << "'s plugin has no URI! Not sending." << endl; + return; + } + + if (bundle) + client->bundle_begin(); + + client->put(node->path(), node->properties()); + + if (recursive) { + // Send ports + for (size_t j=0; j < node->num_ports(); ++j) + send_port(client, node->port_impl(j), false); + } + + if (bundle) + client->bundle_end(); +} + +void +ObjectSender::send_port(ClientInterface* client, const PortImpl* port, bool bundle) +{ + assert(port); + + if (bundle) + client->bundle_begin(); + + client->put(port->path(), port->properties()); + + // Send control value + if (port->is_a(PortType::CONTROL)) + client->set_property(port->path(), port->bufs().uris().ingen_value, port->value()); + + if (bundle) + client->bundle_end(); +} + +} // namespace Server +} // namespace Ingen + diff --git a/src/server/ObjectSender.hpp b/src/server/ObjectSender.hpp new file mode 100644 index 00000000..a743c6d5 --- /dev/null +++ b/src/server/ObjectSender.hpp @@ -0,0 +1,66 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_OBJECTSENDER_HPP +#define INGEN_ENGINE_OBJECTSENDER_HPP + +namespace Ingen { + +class ClientInterface; + +namespace Server { + +class GraphObjectImpl; +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: + static void send_object(ClientInterface* client, + const GraphObjectImpl* object, + bool recursive); + +private: + static void send_patch(ClientInterface* client, + const PatchImpl* patch, + bool recursive, + bool bundle=true); + static void send_node(ClientInterface* client, + const NodeImpl* node, + bool recursive, + bool bundle=true); + static void send_port(ClientInterface* client, + const PortImpl* port, + bool bundle=true); +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_OBJECTSENDER_HPP + diff --git a/src/server/OutputPort.cpp b/src/server/OutputPort.cpp new file mode 100644 index 00000000..487832c3 --- /dev/null +++ b/src/server/OutputPort.cpp @@ -0,0 +1,76 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "ingen/Patch.hpp" +#include "shared/LV2URIMap.hpp" +#include "Buffer.hpp" +#include "NodeImpl.hpp" +#include "OutputPort.hpp" +#include "ProcessContext.hpp" + +using namespace std; + +namespace Ingen { +namespace Server { + +OutputPort::OutputPort(BufferFactory& bufs, + NodeImpl* parent, + const Raul::Symbol& symbol, + uint32_t index, + uint32_t poly, + PortType type, + const Raul::Atom& value, + size_t buffer_size) + : PortImpl(bufs, parent, symbol, index, poly, type, value, buffer_size) +{ + if (!dynamic_cast(parent)) + add_property(bufs.uris().rdf_type, bufs.uris().lv2_OutputPort); + + if (type == PortType::CONTROL) + _broadcast = true; + + setup_buffers(bufs, poly); +} + +bool +OutputPort::get_buffers(BufferFactory& bufs, Raul::Array* buffers, uint32_t poly) +{ + for (uint32_t v = 0; v < poly; ++v) + buffers->at(v) = bufs.get(buffer_type(), _buffer_size); + + return true; +} + +void +OutputPort::pre_process(Context& context) +{ + for (uint32_t v = 0; v < _poly; ++v) + _buffers->at(v)->prepare_write(context); +} + +void +OutputPort::post_process(Context& context) +{ + for (uint32_t v = 0; v < _poly; ++v) + _buffers->at(v)->prepare_read(context); + + if (_broadcast) + broadcast_value(context, false); +} + +} // namespace Server +} // namespace Ingen diff --git a/src/server/OutputPort.hpp b/src/server/OutputPort.hpp new file mode 100644 index 00000000..f16a5ff7 --- /dev/null +++ b/src/server/OutputPort.hpp @@ -0,0 +1,65 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_OUTPUTPORT_HPP +#define INGEN_ENGINE_OUTPUTPORT_HPP + +#include +#include +#include "PortImpl.hpp" + +namespace Ingen { +namespace Server { + +/** 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(BufferFactory& bufs, + NodeImpl* parent, + const Raul::Symbol& symbol, + uint32_t index, + uint32_t poly, + PortType type, + const Raul::Atom& value, + size_t buffer_size=0); + + bool get_buffers(BufferFactory& bufs, Raul::Array* buffers, uint32_t poly); + + void pre_process(Context& context); + void post_process(Context& context); + + virtual ~OutputPort() {} + + bool is_input() const { return false; } + bool is_output() const { return true; } +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_OUTPUTPORT_HPP diff --git a/src/server/PatchImpl.cpp b/src/server/PatchImpl.cpp new file mode 100644 index 00000000..72545b1b --- /dev/null +++ b/src/server/PatchImpl.cpp @@ -0,0 +1,470 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "raul/log.hpp" +#include "shared/World.hpp" +#include "shared/LV2URIMap.hpp" +#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" +#include "Driver.hpp" +#include "ingen-config.h" + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { + +PatchImpl::PatchImpl(Engine& engine, + const Raul::Symbol& symbol, + uint32_t poly, + PatchImpl* parent, + SampleRate srate, + uint32_t internal_poly) + : NodeImpl(new PatchPlugin(*engine.world()->uris().get(), + engine.world()->uris()->ingen_Patch.c_str(), "patch", "Ingen Patch"), + symbol, poly, parent, srate) + , _engine(engine) + , _internal_poly(internal_poly) + , _compiled_patch(NULL) + , _process(false) +{ + assert(internal_poly >= 1); +} + +PatchImpl::~PatchImpl() +{ + assert(!_activated); + + delete _compiled_patch; + delete _plugin; +} + +void +PatchImpl::activate(BufferFactory& bufs) +{ + NodeImpl::activate(bufs); + + for (List::iterator i = _nodes.begin(); i != _nodes.end(); ++i) + (*i)->activate(bufs); + + assert(_activated); +} + +void +PatchImpl::deactivate() +{ + if (_activated) { + NodeImpl::deactivate(); + + for (List::iterator i = _nodes.begin(); i != _nodes.end(); ++i) { + if ((*i)->activated()) + (*i)->deactivate(); + assert(!(*i)->activated()); + } + } + assert(!_activated); +} + +void +PatchImpl::disable() +{ + ThreadManager::assert_thread(THREAD_PROCESS); + + _process = false; + + for (List::iterator i = _output_ports.begin(); i != _output_ports.end(); ++i) + if ((*i)->context() == Context::AUDIO) + (*i)->clear_buffers(); +} + +bool +PatchImpl::prepare_internal_poly(BufferFactory& bufs, uint32_t poly) +{ + ThreadManager::assert_thread(THREAD_PRE_PROCESS); + + // TODO: Subpatch dynamic polyphony (i.e. changing port polyphony) + + for (List::iterator i = _nodes.begin(); i != _nodes.end(); ++i) + (*i)->prepare_poly(bufs, poly); + + for (List::iterator i = _nodes.begin(); i != _nodes.end(); ++i) + for (uint32_t j = 0; j < (*i)->num_ports(); ++j) + (*i)->port_impl(j)->prepare_poly_buffers(bufs); + + return true; +} + +bool +PatchImpl::apply_internal_poly(ProcessContext& context, BufferFactory& bufs, Raul::Maid& maid, uint32_t poly) +{ + ThreadManager::assert_thread(THREAD_PROCESS); + + // TODO: Subpatch dynamic polyphony (i.e. changing port polyphony) + + for (List::iterator i = _nodes.begin(); i != _nodes.end(); ++i) + (*i)->apply_poly(maid, poly); + + for (List::iterator i = _nodes.begin(); i != _nodes.end(); ++i) { + for (uint32_t j = 0; j < (*i)->num_ports(); ++j) { + PortImpl* const port = (*i)->port_impl(j); + if (port->is_input() && dynamic_cast(port)->direct_connect()) + port->setup_buffers(bufs, port->poly()); + port->connect_buffers(context.offset()); + } + } + + const bool polyphonic = parent_patch() && (poly == parent_patch()->internal_poly()); + for (List::iterator i = _output_ports.begin(); i != _output_ports.end(); ++i) + (*i)->setup_buffers(bufs, polyphonic ? poly : 1); + + _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; + + NodeImpl::pre_process(context); + + // Run all nodes + if (_compiled_patch && _compiled_patch->size() > 0) { + if (context.slaves().size() > 0) { + process_parallel(context); + } else { + process_single(context); + } + } + + // Queue any cross-context connections + for (CompiledPatch::QueuedConnections::iterator i = _compiled_patch->queued_connections.begin(); + i != _compiled_patch->queued_connections.end(); ++i) { + (*i)->queue(context); + } + + NodeImpl::post_process(context); +} + +void +PatchImpl::process_parallel(ProcessContext& context) +{ + size_t n_slaves = context.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) + context.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 (uint32_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 (uint32_t i = 0; i < n_slaves; ++i) + context.slaves()[i]->finish(); +} + +void +PatchImpl::process_single(ProcessContext& context) +{ + for (size_t i = 0; i < _compiled_patch->size(); ++i) + (*_compiled_patch)[i].node()->process(context); +} + +void +PatchImpl::set_buffer_size(Context& context, BufferFactory& bufs, PortType type, size_t size) +{ + NodeImpl::set_buffer_size(context, bufs, type, size); + + for (size_t i = 0; i < _compiled_patch->size(); ++i) + (*_compiled_patch)[i].node()->set_buffer_size(context, bufs, type, size); +} + +// Patch specific stuff + +/** Add a node. + * Preprocessing thread only. + */ +void +PatchImpl::add_node(List::Node* ln) +{ + ThreadManager::assert_thread(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 Raul::Symbol& symbol) +{ + ThreadManager::assert_thread(THREAD_PRE_PROCESS); + for (List::iterator i = _nodes.begin(); i != _nodes.end(); ++i) + if ((*i)->symbol() == symbol) + return _nodes.erase(i); + + return NULL; +} + +void +PatchImpl::add_connection(SharedPtr c) +{ + ThreadManager::assert_thread(THREAD_PRE_PROCESS); + _connections.insert(make_pair(make_pair(c->src_port(), c->dst_port()), c)); +} + +/** Remove a connection. + * Preprocessing thread only. + */ +SharedPtr +PatchImpl::remove_connection(const PortImpl* src_port, const PortImpl* dst_port) +{ + ThreadManager::assert_thread(THREAD_PRE_PROCESS); + Connections::iterator i = _connections.find(make_pair(src_port, dst_port)); + if (i != _connections.end()) { + SharedPtr c = PtrCast(i->second); + _connections.erase(i); + return c; + } else { + error << "[PatchImpl::remove_connection] Connection not found" << endl; + return SharedPtr(); + } +} + +bool +PatchImpl::has_connection(const PortImpl* src_port, const PortImpl* dst_port) const +{ + ThreadManager::assert_thread(THREAD_PRE_PROCESS); + Connections::const_iterator i = _connections.find(make_pair(src_port, dst_port)); + return (i != _connections.end()); +} + +uint32_t +PatchImpl::num_ports() const +{ + if (ThreadManager::thread_is(THREAD_PROCESS)) + return NodeImpl::num_ports(); + else + return _input_ports.size() + _output_ports.size(); +} + +/** Create a port. Not realtime safe. + */ +PortImpl* +PatchImpl::create_port(BufferFactory& bufs, const string& name, PortType type, size_t buffer_size, bool is_output, bool polyphonic) +{ + if (type == PortType::UNKNOWN) { + error << "[PatchImpl::create_port] Unknown port type " << type.uri() << endl; + return NULL; + } + + assert( !(type == PortType::UNKNOWN) ); + + return new DuplexPort(bufs, this, name, num_ports(), polyphonic, _polyphony, + type, Raul::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) +{ + ThreadManager::assert_thread(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; + break; + } + } + + 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; + break; + } + } + + if ( ! found) + error << "[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() +{ + ThreadManager::assert_thread(THREAD_PRE_PROCESS); + + _input_ports.clear(); + _output_ports.clear(); +} + +Raul::Array* +PatchImpl::build_ports_array() const +{ + ThreadManager::assert_thread(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 +{ + ThreadManager::assert_thread(THREAD_PRE_PROCESS); + + 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); + } + + // Add any queued connections that must be run after a cycle + for (Connections::const_iterator i = _connections.begin(); i != _connections.end(); ++i) { + SharedPtr c = PtrCast(i->second); + if (c->src_port()->context() == Context::AUDIO && + c->dst_port()->context() == Context::MESSAGE) { + compiled_patch->queued_connections.push_back(c.get()); + } + } + + assert(compiled_patch->size() == _nodes.size()); + + return compiled_patch; +} + +} // namespace Server +} // namespace Ingen diff --git a/src/server/PatchImpl.hpp b/src/server/PatchImpl.hpp new file mode 100644 index 00000000..20ff59a6 --- /dev/null +++ b/src/server/PatchImpl.hpp @@ -0,0 +1,165 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_PATCHIMPL_HPP +#define INGEN_ENGINE_PATCHIMPL_HPP + +#include +#include +#include "raul/List.hpp" +#include "ingen/PortType.hpp" +#include "ingen/Patch.hpp" +#include "NodeImpl.hpp" +#include "PluginImpl.hpp" +#include "CompiledPatch.hpp" + +namespace Ingen { + +class Connection; + +namespace Server { + +class CompiledPatch; +class ConnectionImpl; +class Context; +class Engine; +class ProcessContext; + +/** 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 NodeImpl, public Patch +{ +public: + PatchImpl(Engine& engine, + const Raul::Symbol& symbol, + uint32_t poly, + PatchImpl* parent, + SampleRate srate, + uint32_t local_poly); + + virtual ~PatchImpl(); + + void activate(BufferFactory& bufs); + void deactivate(); + + void process(ProcessContext& context); + + void set_buffer_size(Context& context, BufferFactory& bufs, PortType type, 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(BufferFactory& bufs, uint32_t poly); + + /** Apply a new (internal) polyphony value. + * + * Audio thread. + * + * \param context Process context + * \param bufs New set of buffers + * \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(ProcessContext& context, BufferFactory& bufs, Raul::Maid& maid, uint32_t poly); + + // Patch specific stuff not inherited from Node + + typedef Raul::List Nodes; + + void add_node(Nodes::Node* tn); + Nodes::Node* remove_node(const Raul::Symbol& symbol); + + 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(BufferFactory& bufs, const std::string& name, PortType type, size_t buffer_size, bool is_output, bool polyphonic); + void add_input(Raul::List::Node* port) { _input_ports.push_back(port); } ///< Preprocesser thread + void add_output(Raul::List::Node* port) { _output_ports.push_back(port); } ///< Preprocessor thread + Raul::List::Node* remove_port(const std::string& name); + void clear_ports(); + + void add_connection(SharedPtr c); + + SharedPtr 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_poly() 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 + Raul::List _input_ports; ///< Accessed in preprocessing thread only + Raul::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 (Raul::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 Server +} // namespace Ingen + +#endif // INGEN_ENGINE_PATCHIMPL_HPP diff --git a/src/server/PatchPlugin.hpp b/src/server/PatchPlugin.hpp new file mode 100644 index 00000000..72885c7d --- /dev/null +++ b/src/server/PatchPlugin.hpp @@ -0,0 +1,67 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_PATCHPLUGIN_HPP +#define INGEN_ENGINE_PATCHPLUGIN_HPP + +#include "ingen-config.h" + +#include +#include "PluginImpl.hpp" + +namespace Ingen { +namespace Server { + +class NodeImpl; + +/** Implementation of a Patch plugin. + * + * Patches don't actually work like this yet... + */ +class PatchPlugin : public PluginImpl +{ +public: + PatchPlugin( + Shared::LV2URIMap& uris, + const std::string& uri, + const std::string& symbol, + const std::string& name) + : PluginImpl(uris, Plugin::Patch, uri) + {} + + NodeImpl* instantiate(BufferFactory& bufs, + const std::string& name, + bool polyphonic, + PatchImpl* parent, + Engine& engine) + { + return NULL; + } + + const std::string symbol() const { return "patch"; } + const std::string name() const { return "Ingen Patch"; } + +private: + const std::string _symbol; + const std::string _name; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_PATCHPLUGIN_HPP + diff --git a/src/server/PluginImpl.cpp b/src/server/PluginImpl.cpp new file mode 100644 index 00000000..4eb2b078 --- /dev/null +++ b/src/server/PluginImpl.cpp @@ -0,0 +1,50 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "raul/log.hpp" +#include "PluginImpl.hpp" + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { + +void +PluginImpl::load() +{ + if (!_module) { + debug << "Loading plugin library " << _library_path << endl; + _module = new Glib::Module(_library_path, Glib::MODULE_BIND_LOCAL); + if (!(*_module)) + delete _module; + } +} + +void +PluginImpl::unload() +{ + if (_module) { + debug << "Unloading plugin library " << _library_path << endl; + delete _module; + _module = NULL; + } +} + +} // namespace Server +} // namespace Ingen + diff --git a/src/server/PluginImpl.hpp b/src/server/PluginImpl.hpp new file mode 100644 index 00000000..cea1145d --- /dev/null +++ b/src/server/PluginImpl.hpp @@ -0,0 +1,88 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_PLUGINIMPL_HPP +#define INGEN_ENGINE_PLUGINIMPL_HPP + +#include +#include + +#include +#include + +#include "ingen/Plugin.hpp" +#include "shared/ResourceImpl.hpp" + +namespace Ingen { + +namespace Shared { class LV2URIMap; } + +namespace Server { + +class PatchImpl; +class NodeImpl; +class Engine; +class BufferFactory; + +/** Implementation of a plugin (internal code, or a loaded shared library). + * + * Conceptually, a Node is an instance of this. + */ +class PluginImpl : public Plugin + , public Ingen::Shared::ResourceImpl + , public boost::noncopyable +{ +public: + PluginImpl(Ingen::Shared::LV2URIMap& uris, + Type type, + const std::string& uri, + const std::string library_path = "") + : ResourceImpl(uris, uri) + , _type(type) + , _library_path(library_path) + , _module(NULL) + {} + + virtual NodeImpl* instantiate(BufferFactory& bufs, + const std::string& name, + bool polyphonic, + PatchImpl* parent, + Engine& engine) = 0; + + virtual const std::string symbol() const = 0; + + virtual const std::string& library_path() const { return _library_path; } + + void load(); + void unload(); + + Plugin::Type type() const { return _type; } + void type(Plugin::Type t) { _type = t; } + Glib::Module* module() const { return _module; } + void module(Glib::Module* module) { _module = module; } + +protected: + Plugin::Type _type; + mutable std::string _library_path; + Glib::Module* _module; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_PLUGINIMPL_HPP + diff --git a/src/server/PortImpl.cpp b/src/server/PortImpl.cpp new file mode 100644 index 00000000..a448916e --- /dev/null +++ b/src/server/PortImpl.cpp @@ -0,0 +1,251 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "raul/Array.hpp" +#include "raul/Maid.hpp" +#include "shared/LV2URIMap.hpp" +#include "lv2/lv2plug.in/ns/ext/contexts/contexts.h" +#include "ingen/PortType.hpp" +#include "events/SendPortValue.hpp" +#include "events/SendPortActivity.hpp" +#include "AudioBuffer.hpp" +#include "BufferFactory.hpp" +#include "Engine.hpp" +#include "EventBuffer.hpp" +#include "LV2Atom.hpp" +#include "NodeImpl.hpp" +#include "ObjectBuffer.hpp" +#include "PortImpl.hpp" +#include "ThreadManager.hpp" + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { + +PortImpl::PortImpl(BufferFactory& bufs, + NodeImpl* const node, + const Raul::Symbol& name, + uint32_t index, + uint32_t poly, + PortType type, + const Atom& value, + size_t buffer_size) + : GraphObjectImpl(bufs.uris(), node, name) + , _bufs(bufs) + , _index(index) + , _poly(poly) + , _buffer_size(buffer_size) + , _buffer_type(type) + , _value(value) + , _broadcast(false) + , _set_by_user(false) + , _last_broadcasted_value(value) + , _context(Context::AUDIO) + , _buffers(new Array(static_cast(poly))) + , _prepared_buffers(NULL) +{ + _types.insert(type); + assert(node != NULL); + assert(_poly > 0); + + if (_buffer_size == 0) + _buffer_size = bufs.default_buffer_size(type); + + const Ingen::Shared::LV2URIMap& uris = bufs.uris(); + add_property(uris.rdf_type, type.uri()); + set_property(uris.lv2_index, Atom((int32_t)index)); + set_context(_context); + + if (type == PortType::EVENTS) + _broadcast = true; // send activity blips +} + +PortImpl::~PortImpl() +{ + delete _buffers; +} + +bool +PortImpl::supports(const Raul::URI& value_type) const +{ + return has_property(_bufs.uris().atom_supports, value_type); +} + +Raul::Array* +PortImpl::set_buffers(Raul::Array* buffers) +{ + ThreadManager::assert_thread(THREAD_PROCESS); + + Raul::Array* ret = NULL; + if (buffers != _buffers) { + ret = _buffers; + _buffers = buffers; + } + + connect_buffers(); + + return ret; +} + +bool +PortImpl::prepare_poly(BufferFactory& bufs, uint32_t poly) +{ + ThreadManager::assert_thread(THREAD_PRE_PROCESS); + if (buffer_type() != PortType::CONTROL && buffer_type() != PortType::AUDIO) + return false; + + if (_poly == poly) + return true; + + if (_prepared_buffers && _prepared_buffers->size() != poly) { + delete _prepared_buffers; + _prepared_buffers = NULL; + } + + if (!_prepared_buffers) + _prepared_buffers = new Array(poly, *_buffers, NULL); + + return true; +} + +void +PortImpl::prepare_poly_buffers(BufferFactory& bufs) +{ + if (_prepared_buffers) + get_buffers(bufs, _prepared_buffers, _prepared_buffers->size()); +} + +bool +PortImpl::apply_poly(Maid& maid, uint32_t poly) +{ + ThreadManager::assert_thread(THREAD_PROCESS); + if (buffer_type() != PortType::CONTROL && buffer_type() != PortType::AUDIO) + return false; + + if (!_prepared_buffers) + return true; + + assert(poly == _prepared_buffers->size()); + + _poly = poly; + + // Apply a new set of buffers from a preceding call to prepare_poly + maid.push(set_buffers(_prepared_buffers)); + assert(_buffers == _prepared_buffers); + _prepared_buffers = NULL; + + if (is_a(PortType::CONTROL)) + for (uint32_t v = 0; v < _poly; ++v) + if (_buffers->at(v)) + boost::static_pointer_cast(_buffers->at(v))->set_value( + _value.get_float(), 0, 0); + + assert(_buffers->size() >= poly); + assert(this->poly() == poly); + assert(!_prepared_buffers); + + return true; +} + +void +PortImpl::set_buffer_size(Context& context, BufferFactory& bufs, size_t size) +{ + _buffer_size = size; + + for (uint32_t v = 0; v < _poly; ++v) + _buffers->at(v)->resize(size); + + connect_buffers(); +} + +void +PortImpl::connect_buffers(SampleCount offset) +{ + for (uint32_t v = 0; v < _poly; ++v) + PortImpl::parent_node()->set_port_buffer(v, _index, buffer(v), offset); +} + +void +PortImpl::recycle_buffers() +{ + for (uint32_t v = 0; v < _poly; ++v) + _buffers->at(v) = NULL; +} + +void +PortImpl::clear_buffers() +{ + for (uint32_t v = 0; v < _poly; ++v) + buffer(v)->clear(); +} + +void +PortImpl::broadcast_value(Context& context, bool force) +{ + Raul::Atom val; + switch (buffer_type().symbol()) { + case PortType::UNKNOWN: + break; + case PortType::AUDIO: + case PortType::CONTROL: + val = ((AudioBuffer*)buffer(0).get())->value_at(0); + break; + case PortType::EVENTS: + if (((EventBuffer*)buffer(0).get())->event_count() > 0) { + const Events::SendPortActivity ev(context.engine(), context.start(), this); + context.event_sink().write(sizeof(ev), &ev); + } + break; + case PortType::VALUE: + case PortType::MESSAGE: + Ingen::Shared::LV2Atom::to_atom(_bufs.uris(), ((ObjectBuffer*)buffer(0).get())->atom(), val); + break; + } + + if (val.is_valid() && (force || val != _last_broadcasted_value)) { + _last_broadcasted_value = val; + const Events::SendPortValue ev(context.engine(), context.start(), this, true, 0, val); + context.event_sink().write(sizeof(ev), &ev); + } +} + +void +PortImpl::set_context(Context::ID c) +{ + const Ingen::Shared::LV2URIMap& uris = _bufs.uris(); + _context = c; + switch (c) { + case Context::AUDIO: + remove_property(uris.ctx_context, uris.wildcard); + break; + case Context::MESSAGE: + set_property(uris.ctx_context, uris.ctx_MessageContext); + break; + } +} + +PortType +PortImpl::buffer_type() const +{ + // TODO: multiple types + return *_types.begin(); +} + +} // namespace Server +} // namespace Ingen diff --git a/src/server/PortImpl.hpp b/src/server/PortImpl.hpp new file mode 100644 index 00000000..b3621f13 --- /dev/null +++ b/src/server/PortImpl.hpp @@ -0,0 +1,174 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_PORTIMPL_HPP +#define INGEN_ENGINE_PORTIMPL_HPP + +#include +#include +#include +#include "raul/Array.hpp" +#include "raul/Atom.hpp" +#include "ingen/Port.hpp" +#include "types.hpp" +#include "GraphObjectImpl.hpp" +#include "ingen/PortType.hpp" +#include "Buffer.hpp" +#include "Context.hpp" + +namespace Raul { class Maid; } + +namespace Ingen { +namespace Server { + +class NodeImpl; +class Buffer; +class BufferFactory; + +/** 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 Port +{ +public: + ~PortImpl(); + + /** A port's parent is always a node, so static cast should be safe */ + NodeImpl* parent_node() const { return (NodeImpl*)_parent; } + + /** Set the buffers array for this port. + * + * Audio thread. Returned value must be freed by caller. + * \a buffers must be poly() long + */ + Raul::Array* set_buffers(Raul::Array* buffers); + + /** Prepare for a new (external) polyphony value. + * + * Preprocessor thread, poly is actually applied by apply_poly. + */ + virtual bool prepare_poly(BufferFactory& bufs, uint32_t poly); + + virtual void prepare_poly_buffers(BufferFactory& bufs); + + /** Apply a new polyphony value. + * + * Audio thread. + * \a 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 BufferFactory::Ref buffer(uint32_t voice) const { + return _buffers->at((_poly == 1) ? 0 : voice); + } + inline BufferFactory::Ref prepared_buffer(uint32_t voice) const { + return _prepared_buffers->at(voice); + } + + /** Called once per process cycle */ + virtual void pre_process(Context& context) = 0; + virtual void post_process(Context& context) = 0; + + /** Empty buffer contents completely (ie silence) */ + virtual void clear_buffers(); + + virtual bool get_buffers(BufferFactory& bufs, + Raul::Array* buffers, + uint32_t poly) = 0; + + void setup_buffers(BufferFactory& bufs, uint32_t poly) { + get_buffers(bufs, _buffers, poly); + } + + virtual void connect_buffers(SampleCount offset=0); + virtual void recycle_buffers(); + + virtual bool is_input() const = 0; + virtual bool is_output() const = 0; + + uint32_t index() const { return _index; } + + const PortTypes& types() const { return _types; } + + PortType buffer_type() const; + + bool supports(const Raul::URI& value_type) const; + + size_t buffer_size() const { return _buffer_size; } + + uint32_t poly() const { + return _poly; + } + uint32_t prepared_poly() const { + return (_prepared_buffers) ? _prepared_buffers->size() : 1; + } + + void set_buffer_size(Context& context, BufferFactory& bufs, size_t size); + void set_buffer_type(PortType type); + + void broadcast(bool b) { _broadcast = b; } + bool broadcast() { return _broadcast; } + + void broadcast_value(Context& context, bool force=false); + + void raise_set_by_user_flag() { _set_by_user = true; } + + Context::ID context() const { return _context; } + void set_context(Context::ID c); + + BufferFactory& bufs() const { return _bufs; } + +protected: + PortImpl(BufferFactory& bufs, + NodeImpl* node, + const Raul::Symbol& name, + uint32_t index, + uint32_t poly, + PortType type, + const Raul::Atom& value, + size_t buffer_size); + + BufferFactory& _bufs; + uint32_t _index; + uint32_t _poly; + uint32_t _buffer_size; + PortType _buffer_type; + std::set _types; + Raul::Atom _value; + bool _broadcast; + bool _set_by_user; + Raul::Atom _last_broadcasted_value; + + Context::ID _context; + Raul::Array* _buffers; + + // Dynamic polyphony + Raul::Array* _prepared_buffers; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_PORTIMPL_HPP diff --git a/src/server/PostProcessor.cpp b/src/server/PostProcessor.cpp new file mode 100644 index 00000000..25252a16 --- /dev/null +++ b/src/server/PostProcessor.cpp @@ -0,0 +1,92 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "raul/log.hpp" +#include "raul/SRSWQueue.hpp" +#include "events/SendPortValue.hpp" +#include "Event.hpp" +#include "PostProcessor.hpp" +#include "Engine.hpp" +#include "Driver.hpp" +#include "ProcessContext.hpp" + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { + +PostProcessor::PostProcessor(Engine& engine, size_t queue_size) + : _engine(engine) + , _max_time(0) + , _events(queue_size) + , _event_buffer_size(sizeof(Events::SendPortValue)) // FIXME: make generic + , _event_buffer((uint8_t*)malloc(_event_buffer_size)) +{ +} + +PostProcessor::~PostProcessor() +{ + free(_event_buffer); +} + +void +PostProcessor::process() +{ + const FrameTime end_time = _max_time.get(); + + /* FIXME: The order here is a bit tricky: if the normal events are + * processed first, then a deleted port may still have a pending + * broadcast event which will segfault if executed afterwards. + * If it's the other way around, broadcasts will be sent for + * ports the client doesn't even know about yet... */ + + /* FIXME: process events from all threads if parallel */ + + /* Process audio thread generated events */ + while (true) { + Driver* driver = _engine.driver(); + if (driver && driver->context().event_sink().read(_event_buffer_size, _event_buffer)) { + if (((Event*)_event_buffer)->time() > end_time) { + warn << "Lost event with time " + << ((Event*)_event_buffer)->time() << " > " << end_time << endl; + break; + } + ((Event*)_event_buffer)->post_process(); + } else { + break; + } + } + + /* Process normal events */ + Raul::List::Node* n = _events.head(); + while (n) { + if (n->elem()->time() > end_time) + break; + Raul::List::Node* next = n->next(); + n->elem()->post_process(); + _events.erase(_events.begin()); + delete n->elem(); + delete n; + n = next; + } +} + +} // namespace Server +} // namespace Ingen diff --git a/src/server/PostProcessor.hpp b/src/server/PostProcessor.hpp new file mode 100644 index 00000000..a48c89f8 --- /dev/null +++ b/src/server/PostProcessor.hpp @@ -0,0 +1,68 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_POSTPROCESSOR_HPP +#define INGEN_ENGINE_POSTPROCESSOR_HPP + +#include +#include "raul/SRSWQueue.hpp" +#include "raul/List.hpp" + +namespace Ingen { +namespace Server { + +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: + PostProcessor(Engine& engine, size_t queue_size); + ~PostProcessor(); + + /** Push a list of events on to the process queue, realtime-safe, not thread-safe. */ + inline void append(Raul::List* l) { _events.append(*l); } + + /** 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::List _events; + uint32_t _event_buffer_size; + uint8_t* _event_buffer; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_POSTPROCESSOR_HPP diff --git a/src/server/ProcessContext.cpp b/src/server/ProcessContext.cpp new file mode 100644 index 00000000..4e098b8b --- /dev/null +++ b/src/server/ProcessContext.cpp @@ -0,0 +1,38 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "ProcessContext.hpp" +#include "ProcessSlave.hpp" + +namespace Ingen { +namespace Server { + +void +ProcessContext::activate(uint32_t parallelism, bool sched_rt) +{ + for (uint32_t i = 0; i < _slaves.size(); ++i) { + delete _slaves[i]; + } + _slaves.clear(); + _slaves.reserve(parallelism); + for (uint32_t i = 0; i < parallelism - 1; ++i) { + _slaves.push_back(new ProcessSlave(_engine, sched_rt)); + } +} + +} // namespace Server +} // namespace Ingen diff --git a/src/server/ProcessContext.hpp b/src/server/ProcessContext.hpp new file mode 100644 index 00000000..339b32f8 --- /dev/null +++ b/src/server/ProcessContext.hpp @@ -0,0 +1,54 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_PROCESSCONTEXT_HPP +#define INGEN_ENGINE_PROCESSCONTEXT_HPP + +#include + +#include "Context.hpp" +#include "EventSink.hpp" +#include "types.hpp" + +namespace Ingen { +namespace Server { + +class ProcessSlave; + +/** Context of a process() call (the audio context). + * \ingroup engine + */ +class ProcessContext : public Context +{ +public: + explicit ProcessContext(Engine& engine) : Context(engine, AUDIO) {} + + typedef std::vector Slaves; + + const Slaves& slaves() const { return _slaves; } + Slaves& slaves() { return _slaves; } + + void activate(uint32_t parallelism, bool sched_rt); + +private: + Slaves _slaves; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_PROCESSCONTEXT_HPP diff --git a/src/server/ProcessSlave.cpp b/src/server/ProcessSlave.cpp new file mode 100644 index 00000000..8d60dab3 --- /dev/null +++ b/src/server/ProcessSlave.cpp @@ -0,0 +1,73 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "ProcessSlave.hpp" +#include "NodeImpl.hpp" +#include "CompiledPatch.hpp" + +using namespace std; + +namespace Ingen { +namespace Server { + +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(*_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 Server +} // namespace Ingen diff --git a/src/server/ProcessSlave.hpp b/src/server/ProcessSlave.hpp new file mode 100644 index 00000000..4fb4b9d5 --- /dev/null +++ b/src/server/ProcessSlave.hpp @@ -0,0 +1,104 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_PROCESSSLAVE_HPP +#define INGEN_ENGINE_PROCESSSLAVE_HPP + +#include + +#include "raul/Array.hpp" +#include "raul/AtomicInt.hpp" +#include "raul/Slave.hpp" + +#include "Driver.hpp" +#include "Engine.hpp" +#include "ProcessContext.hpp" + +namespace Ingen { +namespace Server { + +class NodeImpl; +class CompiledPatch; + +class ProcessSlave : protected Raul::Slave { +public: + ProcessSlave(Engine& engine, bool realtime) + : _engine(engine) + , _id(_next_id++) + , _index(0) + , _state(STATE_FINISHED) + , _compiled_patch(NULL) + , _context(NULL) + { + 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; + _context = &context; + + 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 _engine.driver()->context(); } + inline ProcessContext& context() { return _engine.driver()->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; + + Engine& _engine; + uint32_t _id; + uint32_t _index; + Raul::AtomicInt _state; + CompiledPatch* _compiled_patch; + ProcessContext* _context; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_PROCESSSLAVE_HPP diff --git a/src/server/QueuedEngineInterface.cpp b/src/server/QueuedEngineInterface.cpp new file mode 100644 index 00000000..5ef9335f --- /dev/null +++ b/src/server/QueuedEngineInterface.cpp @@ -0,0 +1,223 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "raul/log.hpp" + +#include "Driver.hpp" +#include "Engine.hpp" +#include "EventSource.hpp" +#include "QueuedEngineInterface.hpp" +#include "events.hpp" + +#define LOG(s) s << "[QueuedEngineInterface] " + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { + +QueuedEngineInterface::QueuedEngineInterface(Engine& engine, size_t queue_size) + : EventSource(queue_size) + , _request(new Request(this, NULL, 0)) + , _engine(engine) + , _in_bundle(false) +{ + start(); +} + + +QueuedEngineInterface::~QueuedEngineInterface() +{ + stop(); +} + +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 + if (_engine.driver()) + return _engine.driver()->frame_time() + _engine.driver()->block_length(); + else + return 0; +} + +void +QueuedEngineInterface::set_next_response_id(int32_t id) +{ + if (_request) + _request->set_id(id); +} + +void +QueuedEngineInterface::disable_responses() +{ + _request->set_client(NULL); + _request->set_id(0); +} + +/* *** ServerInterface implementation below here *** */ + +void +QueuedEngineInterface::register_client(ClientInterface* client) +{ + push_queued(new Events::RegisterClient(_engine, _request, now(), client->uri(), client)); + if (!_request) { + _request = SharedPtr(new Request(this, client, 1)); + } else { + _request->set_id(1); + _request->set_client(client); + } +} + +void +QueuedEngineInterface::unregister_client(const URI& uri) +{ + push_queued(new Events::UnregisterClient(_engine, _request, now(), uri)); + if (_request && _request->client() && _request->client()->uri() == uri) { + _request->set_id(0); + _request->set_client(NULL); + } +} + +// Bundle commands + +void +QueuedEngineInterface::bundle_begin() +{ + _in_bundle = true; +} + +void +QueuedEngineInterface::bundle_end() +{ + _in_bundle = false; +} + +// Object commands + +void +QueuedEngineInterface::put(const URI& uri, + const Resource::Properties& properties, + const Resource::Graph ctx) +{ + push_queued(new Events::SetMetadata(_engine, _request, now(), true, ctx, uri, properties)); +} + +void +QueuedEngineInterface::delta(const URI& uri, + const Resource::Properties& remove, + const Resource::Properties& add) +{ + push_queued(new Events::SetMetadata(_engine, _request, now(), false, Resource::DEFAULT, uri, add, remove)); +} + +void +QueuedEngineInterface::move(const Path& old_path, + const Path& new_path) +{ + push_queued(new Events::Move(_engine, _request, now(), old_path, new_path)); +} + +void +QueuedEngineInterface::del(const URI& uri) +{ + if (uri == "ingen:engine") { + _request->respond_ok(); + _engine.quit(); + } else { + push_queued(new Events::Delete(_engine, _request, now(), uri)); + } +} + +void +QueuedEngineInterface::connect(const Path& src_port_path, + const Path& dst_port_path) +{ + push_queued(new Events::Connect(_engine, _request, now(), src_port_path, dst_port_path)); + +} + +void +QueuedEngineInterface::disconnect(const URI& src, + const URI& dst) +{ + if (!Path::is_path(src) && !Path::is_path(dst)) { + std::cerr << "Bad disconnect request " << src << " => " << dst << std::endl; + return; + } + + push_queued(new Events::Disconnect(_engine, _request, now(), + Path(src.str()), Path(dst.str()))); +} + +void +QueuedEngineInterface::disconnect_all(const Path& patch_path, + const Path& path) +{ + push_queued(new Events::DisconnectAll(_engine, _request, now(), patch_path, path)); +} + +void +QueuedEngineInterface::set_property(const URI& uri, + const URI& predicate, + const Atom& value) +{ + if (uri == "ingen:engine" && predicate == "ingen:enabled" + && value.type() == Atom::BOOL) { + if (value.get_bool()) { + _engine.activate(); + push_queued(new Events::Ping(_engine, _request, now())); + } else { + push_queued(new Events::Deactivate(_engine, _request, now())); + } + } else { + Resource::Properties remove; + remove.insert(make_pair(predicate, _engine.world()->uris()->wildcard)); + Resource::Properties add; + add.insert(make_pair(predicate, value)); + push_queued(new Events::SetMetadata( + _engine, _request, now(), false, Resource::DEFAULT, + uri, add, remove)); + } +} + +// Requests // + +void +QueuedEngineInterface::ping() +{ + push_queued(new Events::Ping(_engine, _request, now())); +} + +void +QueuedEngineInterface::get(const URI& uri) +{ + push_queued(new Events::Get(_engine, _request, now(), uri)); +} + +void +QueuedEngineInterface::request_property(const URI& uri, const URI& key) +{ + push_queued(new Events::RequestMetadata(_engine, _request, now(), Resource::DEFAULT, uri, key)); +} + +} // namespace Server +} // namespace Ingen diff --git a/src/server/QueuedEngineInterface.hpp b/src/server/QueuedEngineInterface.hpp new file mode 100644 index 00000000..3bd77013 --- /dev/null +++ b/src/server/QueuedEngineInterface.hpp @@ -0,0 +1,116 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_QUEUEDENGINEINTERFACE_HPP +#define INGEN_ENGINE_QUEUEDENGINEINTERFACE_HPP + +#include +#include +#include +#include "raul/SharedPtr.hpp" +#include "ingen/ClientInterface.hpp" +#include "ingen/ServerInterface.hpp" +#include "ingen/Resource.hpp" +#include "EventSource.hpp" +#include "Request.hpp" +#include "types.hpp" + +namespace Ingen { +namespace Server { + +class Engine; + +/** A queued (preprocessed) event source / interface. + * + * This is the bridge between the ServerInterface presented to the client, and + * the EventSource that needs to be presented to the Driver. + * + * Responses occur through the event mechanism (which notified clients in + * event post_process methods) and are related to an event by an integer ID. + * If you do not register a request, you have no way of knowing if your calls + * are successful. + */ +class QueuedEngineInterface : public EventSource, + public ServerInterface +{ +public: + QueuedEngineInterface(Engine& engine, size_t queue_size); + virtual ~QueuedEngineInterface(); + + Raul::URI uri() const { return "http://drobilla.net/ns/ingen#internal"; } + + void set_next_response_id(int32_t id); + + // Client registration + virtual void register_client(ClientInterface* client); + virtual void unregister_client(const Raul::URI& uri); + + // Bundles + virtual void bundle_begin(); + virtual void bundle_end(); + + // CommonInterface object commands + + virtual void put(const Raul::URI& path, + const Resource::Properties& properties, + const Resource::Graph g=Resource::DEFAULT); + + virtual void delta(const Raul::URI& path, + const Resource::Properties& remove, + const Resource::Properties& add); + + virtual void move(const Raul::Path& old_path, + const Raul::Path& new_path); + + virtual void connect(const Raul::Path& src_port_path, + const Raul::Path& dst_port_path); + + virtual void disconnect(const Raul::URI& src, + const Raul::URI& dst); + + virtual void set_property(const Raul::URI& subject_path, + const Raul::URI& predicate, + const Raul::Atom& value); + + virtual void del(const Raul::URI& uri); + + // ServerInterface object commands + + virtual void disconnect_all(const Raul::Path& parent_patch_path, + const Raul::Path& path); + + // Requests + virtual void ping(); + virtual void get(const Raul::URI& uri); + virtual void request_property(const Raul::URI& object_path, + const Raul::URI& key); + +protected: + virtual void disable_responses(); + + SharedPtr _request; ///< NULL if responding disabled + Engine& _engine; + bool _in_bundle; ///< True iff a bundle is currently being received + +private: + SampleCount now() const; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_QUEUEDENGINEINTERFACE_HPP diff --git a/src/server/QueuedEvent.cpp b/src/server/QueuedEvent.cpp new file mode 100644 index 00000000..ac3c60af --- /dev/null +++ b/src/server/QueuedEvent.cpp @@ -0,0 +1,49 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 { +namespace Server { + +void +QueuedEvent::pre_process() +{ + ThreadManager::assert_thread(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 Server +} // namespace Ingen + diff --git a/src/server/QueuedEvent.hpp b/src/server/QueuedEvent.hpp new file mode 100644 index 00000000..20229272 --- /dev/null +++ b/src/server/QueuedEvent.hpp @@ -0,0 +1,77 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_QUEUEDEVENT_HPP +#define INGEN_ENGINE_QUEUEDEVENT_HPP + +#include "Event.hpp" + +namespace Ingen { +namespace Server { + +/** 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); + + /** True iff this event blocks the prepare phase of other events. */ + bool is_blocking() { return _blocking; } + + bool is_prepared() { return _pre_processed; } + +protected: + QueuedEvent(Engine& engine, + SharedPtr request, + FrameTime time, + bool blocking=false) + : Event(engine, request, time) + , _pre_processed(false) + , _blocking(blocking) + {} + + // NULL event base (for internal events only!) + explicit QueuedEvent(Engine& engine) + : Event(engine, SharedPtr(), 0) + , _pre_processed(false) + , _blocking(false) + {} + + bool _pre_processed; + bool _blocking; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_QUEUEDEVENT_HPP diff --git a/src/server/Request.hpp b/src/server/Request.hpp new file mode 100644 index 00000000..5b91798d --- /dev/null +++ b/src/server/Request.hpp @@ -0,0 +1,81 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_REQUEST_HPP +#define INGEN_ENGINE_REQUEST_HPP + +#include +#include +#include "ingen/ClientInterface.hpp" +#include "EventSource.hpp" + +namespace Ingen { +namespace Server { + +/** Record of a request (used to respond to clients). + * + * This is a glorified std::pair for replying + * to numbered messages from a client. + * + * For responses that involve several 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 Request +{ +public: + Request(EventSource* source=0, + ClientInterface* client=0, + int32_t id=1) + : _source(source) + , _client(client) + , _id(id) + {} + + EventSource* source() { return _source; } + int32_t id() const { return _id; } + void set_id(int32_t id) { _id = id; } + + ClientInterface* client() const { return _client; } + void set_client(ClientInterface* client) { _client = client; } + + void unblock() { + if (_source) + _source->unblock(); + } + + void respond_ok() { + if (_client) + _client->response_ok(_id); + } + + void respond_error(const std::string& msg) { + if (_client) + _client->response_error(_id, msg); + } + +private: + EventSource* _source; + ClientInterface* _client; + int32_t _id; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_REQUEST_HPP + diff --git a/src/server/ThreadManager.hpp b/src/server/ThreadManager.hpp new file mode 100644 index 00000000..db705278 --- /dev/null +++ b/src/server/ThreadManager.hpp @@ -0,0 +1,56 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_THREADMANAGER_HPP +#define INGEN_ENGINE_THREADMANAGER_HPP + +#include +#include "raul/Thread.hpp" + +namespace Ingen { +namespace Server { + +enum ThreadID { + THREAD_PRE_PROCESS, + THREAD_PROCESS, + THREAD_MESSAGE, +}; + +class ThreadManager { +public: + inline static bool thread_is(ThreadID id) { + return Raul::Thread::get().is_context(id); + } + + inline static void assert_thread(ThreadID id) { + assert(single_threaded || Raul::Thread::get().is_context(id)); + } + + inline static void assert_not_thread(ThreadID id) { + assert(single_threaded || !Raul::Thread::get().is_context(id)); + } + + /** Set to true during initialisation so ensure_thread doesn't fail. + * Defined in Engine.cpp + */ + static bool single_threaded; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_THREADMANAGER_HPP diff --git a/src/server/events.hpp b/src/server/events.hpp new file mode 100644 index 00000000..7ddcfe52 --- /dev/null +++ b/src/server/events.hpp @@ -0,0 +1,41 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_EVENTS_HPP +#define INGEN_ENGINE_EVENTS_HPP + +#include "ingen-config.h" + +#include "events/Connect.hpp" +#include "events/CreateNode.hpp" +#include "events/CreatePatch.hpp" +#include "events/CreatePort.hpp" +#include "events/Deactivate.hpp" +#include "events/Delete.hpp" +#include "events/Disconnect.hpp" +#include "events/DisconnectAll.hpp" +#include "events/Get.hpp" +#include "events/Move.hpp" +#include "events/Ping.hpp" +#include "events/RegisterClient.hpp" +#include "events/RequestMetadata.hpp" +#include "events/SetMetadata.hpp" +#include "events/SetPortValue.hpp" +#include "events/UnregisterClient.hpp" + +#endif // INGEN_ENGINE_EVENTS_HPP + diff --git a/src/server/events/Connect.cpp b/src/server/events/Connect.cpp new file mode 100644 index 00000000..e0f09a3d --- /dev/null +++ b/src/server/events/Connect.cpp @@ -0,0 +1,204 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "raul/Maid.hpp" +#include "raul/Path.hpp" +#include "ClientBroadcaster.hpp" +#include "Connect.hpp" +#include "ConnectionImpl.hpp" +#include "DuplexPort.hpp" +#include "Engine.hpp" +#include "EngineStore.hpp" +#include "InputPort.hpp" +#include "OutputPort.hpp" +#include "PatchImpl.hpp" +#include "PortImpl.hpp" +#include "ProcessContext.hpp" +#include "Request.hpp" +#include "types.hpp" + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { +namespace Events { + +Connect::Connect(Engine& engine, SharedPtr request, SampleCount timestamp, const Path& src_port_path, const Path& dst_port_path) + : QueuedEvent(engine, request, timestamp) + , _src_port_path(src_port_path) + , _dst_port_path(dst_port_path) + , _patch(NULL) + , _src_output_port(NULL) + , _dst_input_port(NULL) + , _compiled_patch(NULL) + , _port_listnode(NULL) + , _buffers(NULL) +{ +} + +void +Connect::pre_process() +{ + PortImpl* src_port = _engine.engine_store()->find_port(_src_port_path); + PortImpl* dst_port = _engine.engine_store()->find_port(_dst_port_path); + if (!src_port || !dst_port) { + _error = PORT_NOT_FOUND; + 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(); + if (!src_node || !dst_node) { + _error = PARENTS_NOT_FOUND; + QueuedEvent::pre_process(); + return; + } + + if (src_node->parent() != dst_node->parent() + && src_node != dst_node->parent() + && src_node->parent() != dst_node) { + _error = PARENT_PATCH_DIFFERENT; + QueuedEvent::pre_process(); + return; + } + + if (!ConnectionImpl::can_connect(_src_output_port, _dst_input_port)) { + _error = TYPE_MISMATCH; + QueuedEvent::pre_process(); + return; + } + + // 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(); + } + + if (_patch->has_connection(_src_output_port, _dst_input_port)) { + _error = ALREADY_CONNECTED; + QueuedEvent::pre_process(); + return; + } + + _connection = SharedPtr( + new ConnectionImpl(*_engine.buffer_factory(), _src_output_port, _dst_input_port)); + + _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(_connection); + _dst_input_port->increment_num_connections(); + + /*if ((_dst_input_port->num_connections() == 1 + && (_connection->must_mix() || _connection->must_queue())) + || _dst_input_port->num_connections() == 2) {*/ + _buffers = new Raul::Array(_dst_input_port->poly()); + _dst_input_port->get_buffers(*_engine.buffer_factory(), + _buffers, _dst_input_port->poly()); + //} + + if (_patch->enabled()) + _compiled_patch = _patch->compile(); + + QueuedEvent::pre_process(); +} + +void +Connect::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); + assert(_buffers); + //if (_buffers) + _engine.maid()->push(_dst_input_port->set_buffers(_buffers)); + //else + // _dst_input_port->setup_buffers(*_engine.buffer_factory(), _dst_input_port->poly()); + _dst_input_port->connect_buffers(); + _engine.maid()->push(_patch->compiled_patch()); + _patch->compiled_patch(_compiled_patch); + } +} + +void +Connect::post_process() +{ + std::ostringstream ss; + if (_error == NO_ERROR) { + _request->respond_ok(); + _engine.broadcaster()->connect(_src_port_path, _dst_port_path); + return; + } + + ss << boost::format("Unable to make connection %1% -> %2% (") + % _src_port_path.chop_scheme() % _dst_port_path.chop_scheme(); + + 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 << ")"; + _request->respond_error(ss.str()); +} + +} // namespace Server +} // namespace Ingen +} // namespace Events + diff --git a/src/server/events/Connect.hpp b/src/server/events/Connect.hpp new file mode 100644 index 00000000..1cc98729 --- /dev/null +++ b/src/server/events/Connect.hpp @@ -0,0 +1,88 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_EVENTS_CONNECT_HPP +#define INGEN_EVENTS_CONNECT_HPP + +#include "raul/Path.hpp" +#include "QueuedEvent.hpp" +#include "PatchImpl.hpp" +#include "InputPort.hpp" +#include "types.hpp" + +namespace Raul { + template class ListNode; + template class Array; +} + +namespace Ingen { +namespace Server { + +class PatchImpl; +class NodeImpl; +class ConnectionImpl; +class PortImpl; +class InputPort; +class OutputPort; +class CompiledPatch; + +namespace Events { + +/** Make a Connection between two Ports. + * + * \ingroup engine + */ +class Connect : public QueuedEvent +{ +public: + Connect(Engine& engine, SharedPtr request, SampleCount timestamp, const Raul::Path& src_port_path, const Raul::Path& 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; + OutputPort* _src_output_port; + InputPort* _dst_input_port; + + CompiledPatch* _compiled_patch; ///< New process order for Patch + + SharedPtr _connection; + InputPort::Connections::Node* _port_listnode; + + Raul::Array* _buffers; +}; + +} // namespace Server +} // namespace Ingen +} // namespace Events + +#endif // INGEN_EVENTS_CONNECT_HPP diff --git a/src/server/events/CreateNode.cpp b/src/server/events/CreateNode.cpp new file mode 100644 index 00000000..01d4f285 --- /dev/null +++ b/src/server/events/CreateNode.cpp @@ -0,0 +1,146 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "raul/log.hpp" +#include "raul/Maid.hpp" +#include "raul/Path.hpp" +#include "sord/sordmm.hpp" +#include "shared/LV2URIMap.hpp" +#include "CreateNode.hpp" +#include "Request.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 "Driver.hpp" + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { +namespace Events { + +CreateNode::CreateNode( + Engine& engine, + SharedPtr request, + SampleCount timestamp, + const Path& path, + const URI& plugin_uri, + const Resource::Properties& properties) + : QueuedEvent(engine, request, timestamp) + , _path(path) + , _plugin_uri(plugin_uri) + , _patch(NULL) + , _plugin(NULL) + , _node(NULL) + , _compiled_patch(NULL) + , _node_already_exists(false) + , _polyphonic(false) + , _properties(properties) +{ + const Resource::Properties::const_iterator p = properties.find( + engine.world()->uris()->ingen_polyphonic); + if (p != properties.end() && p->second.type() == Raul::Atom::BOOL + && p->second.get_bool()) + _polyphonic = true; +} + +void +CreateNode::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()); + _plugin = _engine.node_factory()->plugin(_plugin_uri.str()); + + if (_patch && _plugin) { + + _node = _plugin->instantiate(*_engine.buffer_factory(), _path.symbol(), _polyphonic, _patch, _engine); + + if (_node != NULL) { + _node->properties().insert(_properties.begin(), _properties.end()); + _node->activate(*_engine.buffer_factory()); + + // 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)); + _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(); + } + } + + if (!_node) + _error = 1; + + QueuedEvent::pre_process(); +} + +void +CreateNode::execute(ProcessContext& context) +{ + QueuedEvent::execute(context); + + if (_node) { + _engine.maid()->push(_patch->compiled_patch()); + _patch->compiled_patch(_compiled_patch); + } +} + +void +CreateNode::post_process() +{ + if (!_request) + return; + + string msg; + if (_node_already_exists) { + msg = string("Could not create node - ").append(_path.str());// + " already exists."; + _request->respond_error(msg); + } else if (_patch == NULL) { + msg = "Could not find patch '" + _path.parent().str() +"' to add node."; + _request->respond_error(msg); + } else if (_plugin == NULL) { + msg = "Unable to load node "; + msg += _path.str() + " (you're missing the plugin " + _plugin_uri.str() + ")"; + _request->respond_error(msg); + } else if (_node == NULL) { + msg = "Failed to instantiate plugin " + _plugin_uri.str(); + _request->respond_error(msg); + } else { + _request->respond_ok(); + _engine.broadcaster()->send_object(_node, true); // yes, send ports + } +} + +} // namespace Server +} // namespace Ingen +} // namespace Events + diff --git a/src/server/events/CreateNode.hpp b/src/server/events/CreateNode.hpp new file mode 100644 index 00000000..bbaf830b --- /dev/null +++ b/src/server/events/CreateNode.hpp @@ -0,0 +1,71 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_EVENTS_CREATENODE_HPP +#define INGEN_EVENTS_CREATENODE_HPP + +#include +#include "QueuedEvent.hpp" +#include "ingen/Resource.hpp" + +namespace Ingen { +namespace Server { + +class PatchImpl; +class PluginImpl; +class NodeImpl; +class CompiledPatch; + +namespace Events { + +/** An event to load a Node and insert it into a Patch. + * + * \ingroup engine + */ +class CreateNode : public QueuedEvent +{ +public: + CreateNode( + Engine& engine, + SharedPtr request, + SampleCount timestamp, + const Raul::Path& node_path, + const Raul::URI& plugin_uri, + const Resource::Properties& properties); + + void pre_process(); + void execute(ProcessContext& context); + void post_process(); + +private: + Raul::Path _path; + Raul::URI _plugin_uri; + PatchImpl* _patch; + PluginImpl* _plugin; + NodeImpl* _node; + CompiledPatch* _compiled_patch; ///< Patch's new process order + bool _node_already_exists; + bool _polyphonic; + + Resource::Properties _properties; +}; + +} // namespace Server +} // namespace Ingen +} // namespace Events + +#endif // INGEN_EVENTS_CREATENODE_HPP diff --git a/src/server/events/CreatePatch.cpp b/src/server/events/CreatePatch.cpp new file mode 100644 index 00000000..a5c75adf --- /dev/null +++ b/src/server/events/CreatePatch.cpp @@ -0,0 +1,164 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "raul/Maid.hpp" +#include "raul/Path.hpp" +#include "shared/LV2URIMap.hpp" +#include "events/CreatePatch.hpp" +#include "Request.hpp" +#include "PatchImpl.hpp" +#include "NodeImpl.hpp" +#include "PluginImpl.hpp" +#include "Engine.hpp" +#include "ClientBroadcaster.hpp" +#include "Driver.hpp" +#include "EngineStore.hpp" + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { +namespace Events { + +CreatePatch::CreatePatch( + Engine& engine, + SharedPtr request, + SampleCount timestamp, + const Raul::Path& path, + int poly, + const Resource::Properties& properties) + : QueuedEvent(engine, request, timestamp) + , _path(path) + , _patch(NULL) + , _parent(NULL) + , _compiled_patch(NULL) + , _poly(poly) + , _properties(properties) +{ +} + +void +CreatePatch::pre_process() +{ + if (_path.is_root() || _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_poly())) + poly = _poly; + + const Ingen::Shared::LV2URIMap& uris = *_engine.world()->uris().get(); + + _patch = new PatchImpl(_engine, path.symbol(), poly, _parent, + _engine.driver()->sample_rate(), _poly); + _patch->properties().insert(_properties.begin(), _properties.end()); + _patch->add_property(uris.rdf_type, uris.ingen_Patch); + _patch->add_property(uris.rdf_type, + Resource::Property(uris.ingen_Node, Resource::EXTERNAL)); + + if (_parent != NULL) { + _parent->add_node(new PatchImpl::Nodes::Node(_patch)); + + if (_parent->enabled()) + _compiled_patch = _parent->compile(); + } + + _patch->activate(*_engine.buffer_factory()); + + // Insert into EngineStore + //_patch->add_to_store(_engine.engine_store()); + _engine.engine_store()->add(_patch); + + QueuedEvent::pre_process(); +} + +void +CreatePatch::execute(ProcessContext& context) +{ + QueuedEvent::execute(context); + + if (_patch) { + if (!_parent) { + assert(_path.is_root()); + assert(_patch->parent_patch() == NULL); + _engine.driver()->set_root_patch(_patch); + } else { + assert(_parent); + assert(!_path.is_root()); + _engine.maid()->push(_parent->compiled_patch()); + _parent->compiled_patch(_compiled_patch); + } + } +} + +void +CreatePatch::post_process() +{ + string msg; + if (_request) { + switch (_error) { + case NO_ERROR: + _request->respond_ok(); + // Don't send ports/nodes that have been added since prepare() + // (otherwise they would be sent twice) + _engine.broadcaster()->send_object(_patch, false); + break; + case OBJECT_EXISTS: + _request->respond_ok(); + /*string msg = "Unable to create patch: "; + msg.append(_path).append(" already exists."); + _request->respond_error(msg);*/ + break; + case PARENT_NOT_FOUND: + msg = "Unable to create patch: Parent "; + msg.append(Path(_path).parent().str()).append(" not found."); + _request->respond_error(msg); + break; + case INVALID_POLY: + msg = "Unable to create patch "; + msg.append(_path.str()).append(": ").append("Invalid polyphony requested."); + _request->respond_error(msg); + break; + default: + _request->respond_error("Unable to load patch."); + } + } +} + +} // namespace Server +} // namespace Ingen +} // namespace Events + diff --git a/src/server/events/CreatePatch.hpp b/src/server/events/CreatePatch.hpp new file mode 100644 index 00000000..e3afde5f --- /dev/null +++ b/src/server/events/CreatePatch.hpp @@ -0,0 +1,67 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_EVENTS_CREATEPATCH_HPP +#define INGEN_EVENTS_CREATEPATCH_HPP + +#include "QueuedEvent.hpp" +#include "ingen/Resource.hpp" + +namespace Ingen { +namespace Server { + +class PatchImpl; +class CompiledPatch; + +namespace Events { + +/** Creates a new Patch. + * + * \ingroup engine + */ +class CreatePatch : public QueuedEvent +{ +public: + CreatePatch( + Engine& engine, + SharedPtr request, + SampleCount timestamp, + const Raul::Path& path, + int poly, + const Resource::Properties& properties); + + void pre_process(); + void execute(ProcessContext& context); + void post_process(); + +private: + enum ErrorType { NO_ERROR, OBJECT_EXISTS, PARENT_NOT_FOUND, INVALID_POLY }; + + const Raul::Path _path; + PatchImpl* _patch; + PatchImpl* _parent; + CompiledPatch* _compiled_patch; + int _poly; + + Resource::Properties _properties; +}; + +} // namespace Server +} // namespace Ingen +} // namespace Events + +#endif // INGEN_EVENTS_CREATEPATCH_HPP diff --git a/src/server/events/CreatePort.cpp b/src/server/events/CreatePort.cpp new file mode 100644 index 00000000..ba46a35d --- /dev/null +++ b/src/server/events/CreatePort.cpp @@ -0,0 +1,192 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "raul/Array.hpp" +#include "raul/Atom.hpp" +#include "raul/List.hpp" +#include "raul/Maid.hpp" +#include "raul/Path.hpp" +#include "shared/LV2URIMap.hpp" +#include "ClientBroadcaster.hpp" +#include "ControlBindings.hpp" +#include "CreatePort.hpp" +#include "Driver.hpp" +#include "DuplexPort.hpp" +#include "Engine.hpp" +#include "EngineStore.hpp" +#include "PatchImpl.hpp" +#include "PatchImpl.hpp" +#include "PluginImpl.hpp" +#include "PortImpl.hpp" +#include "Request.hpp" + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { +namespace Events { + +CreatePort::CreatePort( + Engine& engine, + SharedPtr request, + SampleCount timestamp, + const Raul::Path& path, + const Raul::URI& type, + bool is_output, + const Resource::Properties& properties) + : QueuedEvent(engine, request, timestamp, bool(request)) + , _path(path) + , _type(type) + , _is_output(is_output) + , _data_type(type) + , _patch(NULL) + , _patch_port(NULL) + , _driver_port(NULL) + , _properties(properties) +{ + /* This is blocking because of the two different sets of Patch ports, the array used in the + * audio thread (inherited from NodeImpl), 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). + * + * TODO: fix this using RCU? + */ + + if (_data_type == PortType::UNKNOWN) + _error = UNKNOWN_TYPE; +} + +void +CreatePort::pre_process() +{ + if (_error == UNKNOWN_TYPE || _engine.engine_store()->find_object(_path)) { + QueuedEvent::pre_process(); + return; + } + + _patch = _engine.engine_store()->find_patch(_path.parent()); + + const Ingen::Shared::LV2URIMap& uris = *_engine.world()->uris().get(); + + if (_patch != NULL) { + assert(_patch->path() == _path.parent()); + + size_t buffer_size = _engine.buffer_factory()->default_buffer_size(_data_type); + + const uint32_t old_num_ports = (_patch->external_ports()) + ? _patch->external_ports()->size() + : 0; + + Resource::Properties::const_iterator index_i = _properties.find(uris.lv2_index); + if (index_i == _properties.end()) { + index_i = _properties.insert(make_pair(uris.lv2_index, (int)old_num_ports)); + } else if (index_i->second.type() != Atom::INT + || index_i->second.get_int32() != static_cast(old_num_ports)) { + QueuedEvent::pre_process(); + _error = BAD_INDEX; + return; + } + + Resource::Properties::const_iterator poly_i = _properties.find(uris.ingen_polyphonic); + bool polyphonic = (poly_i != _properties.end() && poly_i->second.type() == Atom::BOOL + && poly_i->second.get_bool()); + + _patch_port = _patch->create_port(*_engine.buffer_factory(), _path.symbol(), _data_type, buffer_size, _is_output, polyphonic); + + _patch_port->properties().insert(_properties.begin(), _properties.end()); + + assert(index_i->second == Atom((int)_patch_port->index())); + + 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(), NULL); + else + _ports_array = new Raul::Array(old_num_ports + 1, NULL); + + _ports_array->at(old_num_ports) = _patch_port; + _engine.engine_store()->add(_patch_port); + + if (!_patch->parent()) + _driver_port = _engine.driver()->create_port( + dynamic_cast(_patch_port)); + + assert(_ports_array->size() == _patch->num_ports()); + + } else { + _error = CREATION_FAILED; + } + } + QueuedEvent::pre_process(); +} + +void +CreatePort::execute(ProcessContext& context) +{ + QueuedEvent::execute(context); + + if (_patch_port) { + _engine.maid()->push(_patch->external_ports()); + _patch->external_ports(_ports_array); + _engine.control_bindings()->port_binding_changed(context, _patch_port); + } + + if (_driver_port) { + _engine.driver()->add_port(_driver_port); + } + + if (_request) + _request->unblock(); +} + +void +CreatePort::post_process() +{ + if (!_request) + return; + + string msg; + switch (_error) { + case NO_ERROR: + _request->respond_ok(); + _engine.broadcaster()->send_object(_patch_port, true); + break; + case BAD_INDEX: + msg = string("Could not create port ") + _path.str() + " (Illegal index given)"; + _request->respond_error(msg); + break; + case UNKNOWN_TYPE: + msg = string("Could not create port ") + _path.str() + " (Unknown type)"; + _request->respond_error(msg); + break; + case CREATION_FAILED: + msg = string("Could not create port ") + _path.str() + " (Creation failed)"; + _request->respond_error(msg); + break; + } +} + +} // namespace Server +} // namespace Ingen +} // namespace Events + diff --git a/src/server/events/CreatePort.hpp b/src/server/events/CreatePort.hpp new file mode 100644 index 00000000..ae44e2f1 --- /dev/null +++ b/src/server/events/CreatePort.hpp @@ -0,0 +1,81 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_EVENTS_CREATEPORT_HPP +#define INGEN_EVENTS_CREATEPORT_HPP + +#include "QueuedEvent.hpp" +#include "raul/Path.hpp" +#include "raul/Array.hpp" +#include "ingen/PortType.hpp" +#include "ingen/Resource.hpp" + +namespace Ingen { +namespace Server { + +class PatchImpl; +class PortImpl; +class DriverPort; + +namespace Events { + +/** An event to add a Port to a Patch. + * + * \ingroup engine + */ +class CreatePort : public QueuedEvent +{ +public: + CreatePort( + Engine& engine, + SharedPtr request, + SampleCount timestamp, + const Raul::Path& path, + const Raul::URI& type, + bool is_output, + const Resource::Properties& properties); + + void pre_process(); + void execute(ProcessContext& context); + void post_process(); + +private: + enum ErrorType { + NO_ERROR, + UNKNOWN_TYPE, + BAD_INDEX, + CREATION_FAILED + }; + + Raul::Path _path; + Raul::URI _type; + bool _is_output; + PortType _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; + + Resource::Properties _properties; +}; + +} // namespace Server +} // namespace Ingen +} // namespace Events + +#endif // INGEN_EVENTS_CREATEPORT_HPP diff --git a/src/server/events/Deactivate.hpp b/src/server/events/Deactivate.hpp new file mode 100644 index 00000000..779ba54c --- /dev/null +++ b/src/server/events/Deactivate.hpp @@ -0,0 +1,49 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_EVENTS_DEACTIVATE_HPP +#define INGEN_EVENTS_DEACTIVATE_HPP + +#include "QueuedEvent.hpp" +#include "Engine.hpp" + +namespace Ingen { +namespace Server { +namespace Events { + +/** Deactivates the engine. + * + * \ingroup engine + */ +class Deactivate : public QueuedEvent +{ +public: + Deactivate(Engine& engine, SharedPtr request, SampleCount timestamp) + : QueuedEvent(engine, request, timestamp) + {} + + void post_process() { + _request->respond_ok(); + _engine.deactivate(); + } +}; + +} // namespace Server +} // namespace Ingen +} // namespace Events + +#endif // INGEN_EVENTS_DEACTIVATE_HPP diff --git a/src/server/events/Delete.cpp b/src/server/events/Delete.cpp new file mode 100644 index 00000000..b1dc1558 --- /dev/null +++ b/src/server/events/Delete.cpp @@ -0,0 +1,213 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "raul/Maid.hpp" +#include "raul/Path.hpp" +#include "ClientBroadcaster.hpp" +#include "ControlBindings.hpp" +#include "Delete.hpp" +#include "DisconnectAll.hpp" +#include "Driver.hpp" +#include "Engine.hpp" +#include "EngineStore.hpp" +#include "NodeImpl.hpp" +#include "PatchImpl.hpp" +#include "PluginImpl.hpp" +#include "PortImpl.hpp" +#include "Request.hpp" + +using namespace std; + +namespace Ingen { +namespace Server { +namespace Events { + +Delete::Delete(Engine& engine, + SharedPtr request, + FrameTime time, + const Raul::URI& uri) + : QueuedEvent(engine, request, time, true) + , _uri(uri) + , _store_iterator(engine.engine_store()->end()) + , _garbage(NULL) + , _driver_port(NULL) + , _patch_node_listnode(NULL) + , _patch_port_listnode(NULL) + , _ports_array(NULL) + , _compiled_patch(NULL) + , _disconnect_event(NULL) +{ + assert(request); + assert(request->source()); + + if (Raul::Path::is_path(uri)) + _path = Raul::Path(uri.str()); +} + +Delete::~Delete() +{ + delete _disconnect_event; +} + +void +Delete::pre_process() +{ + if (_path.is_root() || _path == "path:/control_in" || _path == "path:/control_out") { + QueuedEvent::pre_process(); + return; + } + + _removed_bindings = _engine.control_bindings()->remove(_path); + + _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 && !_path.is_root()) { + assert(_node->parent_patch()); + _patch_node_listnode = _node->parent_patch()->remove_node(_path.symbol()); + if (_patch_node_listnode) { + assert(_patch_node_listnode->elem() == _node.get()); + + _disconnect_event = new DisconnectAll(_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 deleted + 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.symbol()); + if (_patch_port_listnode) { + assert(_patch_port_listnode->elem() == _port.get()); + + _disconnect_event = new DisconnectAll(_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 +Delete::execute(ProcessContext& context) +{ + QueuedEvent::execute(context); + + PatchImpl* parent_patch = NULL; + + if (_patch_node_listnode) { + assert(_node); + + if (_disconnect_event) + _disconnect_event->execute(context); + + parent_patch = _node->parent_patch(); + + } else if (_patch_port_listnode) { + assert(_port); + + if (_disconnect_event) + _disconnect_event->execute(context); + + parent_patch = _port->parent_patch(); + + _engine.maid()->push(_port->parent_patch()->external_ports()); + _port->parent_patch()->external_ports(_ports_array); + + if ( ! _port->parent_patch()->parent()) + _garbage = _engine.driver()->remove_port(_port->path(), &_driver_port); + } + + if (parent_patch) { + _engine.maid()->push(parent_patch->compiled_patch()); + parent_patch->compiled_patch(_compiled_patch); + } + + _request->unblock(); +} + +void +Delete::post_process() +{ + _removed_bindings.reset(); + + if (!Raul::Path::is_path(_uri) + || _path.is_root() || _path == "path:/control_in" || _path == "path:/control_out") { + // XXX: Just ignore? + //_request->respond_error(_path.chop_scheme() + " can not be deleted"); + } else if (!_node && !_port) { + string msg = string("Could not find object ") + _path.chop_scheme() + " to delete"; + _request->respond_error(msg); + } else if (_patch_node_listnode) { + assert(_node); + _node->deactivate(); + _request->respond_ok(); + _engine.broadcaster()->bundle_begin(); + if (_disconnect_event) + _disconnect_event->post_process(); + _engine.broadcaster()->del(_path); + _engine.broadcaster()->bundle_end(); + _engine.maid()->push(_patch_node_listnode); + } else if (_patch_port_listnode) { + assert(_port); + _request->respond_ok(); + _engine.broadcaster()->bundle_begin(); + if (_disconnect_event) + _disconnect_event->post_process(); + _engine.broadcaster()->del(_path); + _engine.broadcaster()->bundle_end(); + _engine.maid()->push(_patch_port_listnode); + } else { + _request->respond_error("Unable to delete object " + _path.chop_scheme()); + } + + if (_driver_port) + _driver_port->destroy(); + + _engine.maid()->push(_garbage); +} + +} // namespace Server +} // namespace Ingen +} // namespace Events diff --git a/src/server/events/Delete.hpp b/src/server/events/Delete.hpp new file mode 100644 index 00000000..ba256d87 --- /dev/null +++ b/src/server/events/Delete.hpp @@ -0,0 +1,95 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_EVENTS_DELETE_HPP +#define INGEN_EVENTS_DELETE_HPP + +#include "QueuedEvent.hpp" +#include "EngineStore.hpp" +#include "PatchImpl.hpp" +#include "ControlBindings.hpp" + +namespace Raul { + template class Array; + template class ListNode; +} + +namespace Ingen { +namespace Server { + +class GraphObjectImpl; +class NodeImpl; +class PortImpl; +class DriverPort; +class CompiledPatch; + +namespace Events { + +class DisconnectAll; + +/** \page methods + *

DELETE

+ * As per WebDAV (RFC4918 S9.6). + * + * Remove an object from the engine and destroy it. + * + * \li All properties of the object are lost + * \li All references to the object are lost (e.g. the parent's reference to + * this child is lost, any connections to the object are removed, etc.) + */ + +/** DELETE a graph object (see \ref methods). + * \ingroup engine + */ +class Delete : public QueuedEvent +{ +public: + Delete(Engine& engine, + SharedPtr request, + FrameTime timestamp, + const Raul::URI& uri); + + ~Delete(); + + void pre_process(); + void execute(ProcessContext& context); + void post_process(); + +private: + Raul::URI _uri; + Raul::Path _path; + EngineStore::iterator _store_iterator; + SharedPtr _node; ///< Non-NULL iff a node + SharedPtr _port; ///< Non-NULL iff a port + Raul::Deletable* _garbage; + 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 + DisconnectAll* _disconnect_event; + + SharedPtr _removed_bindings; + + SharedPtr< Raul::Table > > _removed_table; +}; + +} // namespace Server +} // namespace Ingen +} // namespace Events + +#endif // INGEN_EVENTS_DELETE_HPP diff --git a/src/server/events/Disconnect.cpp b/src/server/events/Disconnect.cpp new file mode 100644 index 00000000..e9374648 --- /dev/null +++ b/src/server/events/Disconnect.cpp @@ -0,0 +1,269 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "raul/log.hpp" +#include "raul/Maid.hpp" +#include "raul/Path.hpp" +#include "events/Disconnect.hpp" +#include "AudioBuffer.hpp" +#include "ClientBroadcaster.hpp" +#include "ConnectionImpl.hpp" +#include "DuplexPort.hpp" +#include "Engine.hpp" +#include "EngineStore.hpp" +#include "InputPort.hpp" +#include "OutputPort.hpp" +#include "PatchImpl.hpp" +#include "PortImpl.hpp" +#include "ProcessContext.hpp" +#include "Request.hpp" +#include "ThreadManager.hpp" + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { +namespace Events { + +Disconnect::Disconnect( + Engine& engine, + SharedPtr request, + SampleCount timestamp, + const Raul::Path& src_port_path, + const Raul::Path& dst_port_path) + : QueuedEvent(engine, request, timestamp) + , _src_port_path(src_port_path) + , _dst_port_path(dst_port_path) + , _patch(NULL) + , _src_port(NULL) + , _dst_port(NULL) + , _impl(NULL) + , _compiled_patch(NULL) +{ +} + +Disconnect::Impl::Impl(Engine& e, + PatchImpl* patch, + OutputPort* s, + InputPort* d) + : _engine(e) + , _src_output_port(s) + , _dst_input_port(d) + , _patch(patch) + , _connection(patch->remove_connection(_src_output_port, _dst_input_port)) + , _buffers(NULL) +{ + ThreadManager::assert_thread(THREAD_PRE_PROCESS); + + NodeImpl* const src_node = _src_output_port->parent_node(); + NodeImpl* const dst_node = _dst_input_port->parent_node(); + + 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; + } + } + + _dst_input_port->decrement_num_connections(); + + if (_dst_input_port->num_connections() == 0) { + _buffers = new Raul::Array(_dst_input_port->poly()); + _dst_input_port->get_buffers(*_engine.buffer_factory(), + _buffers, _dst_input_port->poly()); + + const bool is_control = _dst_input_port->is_a(PortType::CONTROL); + const float value = is_control ? _dst_input_port->value().get_float() : 0; + for (uint32_t i = 0; i < _buffers->size(); ++i) { + if (is_control) { + PtrCast(_buffers->at(i))->set_value(value, 0, 0); + } else { + _buffers->at(i)->clear(); + } + } + } + + _connection->pending_disconnection(true); +} + +void +Disconnect::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; + } + + 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 (!_patch->has_connection(_src_port, _dst_port)) { + _error = NOT_CONNECTED; + QueuedEvent::pre_process(); + return; + } + + if (src_node == NULL || dst_node == NULL) { + _error = PARENTS_NOT_FOUND; + QueuedEvent::pre_process(); + return; + } + + _impl = new Impl(_engine, + _patch, + dynamic_cast(_src_port), + dynamic_cast(_dst_port)); + + if (_patch->enabled()) + _compiled_patch = _patch->compile(); + + QueuedEvent::pre_process(); +} + +bool +Disconnect::Impl::execute(ProcessContext& context, bool set_dst_buffers) +{ + ThreadManager::assert_thread(THREAD_PROCESS); + + InputPort::Connections::Node* const port_connections_node + = _dst_input_port->remove_connection(context, _src_output_port); + if (!port_connections_node) { + return false; + } + + if (set_dst_buffers) { + if (_buffers) { + _engine.maid()->push(_dst_input_port->set_buffers(_buffers)); + } else { + _dst_input_port->setup_buffers(*_engine.buffer_factory(), + _dst_input_port->poly()); + } + _dst_input_port->connect_buffers(); + } else { + _dst_input_port->recycle_buffers(); + } + + assert(_connection); + assert(port_connections_node->elem() == _connection); + + _engine.maid()->push(port_connections_node); + return true; +} + +void +Disconnect::execute(ProcessContext& context) +{ + QueuedEvent::execute(context); + + if (_error == NO_ERROR) { + if (!_impl->execute(context, true)) { + _error = CONNECTION_NOT_FOUND; + return; + } + + _engine.maid()->push(_patch->compiled_patch()); + _patch->compiled_patch(_compiled_patch); + } +} + +void +Disconnect::post_process() +{ + if (_error == NO_ERROR) { + if (_request) + _request->respond_ok(); + _engine.broadcaster()->disconnect(_src_port->path(), _dst_port->path()); + } else { + string msg("Unable to disconnect "); + msg.append(_src_port_path.str() + " => " + _dst_port_path.str()); + msg.append(" ("); + switch (_error) { + case PARENT_PATCH_DIFFERENT: + msg.append("Ports exist in different patches"); + break; + case PORT_NOT_FOUND: + msg.append("Port not found"); + break; + case TYPE_MISMATCH: + msg.append("Ports have incompatible types"); + break; + case NOT_CONNECTED: + msg.append("Ports are not connected"); + break; + case PARENTS_NOT_FOUND: + msg.append("Parent node not found"); + break; + case CONNECTION_NOT_FOUND: + msg.append("Connection not found"); + break; + default: + break; + } + msg.append(")"); + if (_request) + _request->respond_error(msg); + } + + delete _impl; +} + +} // namespace Server +} // namespace Ingen +} // namespace Events + diff --git a/src/server/events/Disconnect.hpp b/src/server/events/Disconnect.hpp new file mode 100644 index 00000000..a553fe79 --- /dev/null +++ b/src/server/events/Disconnect.hpp @@ -0,0 +1,106 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_EVENTS_DISCONNECT_HPP +#define INGEN_EVENTS_DISCONNECT_HPP + +#include "raul/Path.hpp" +#include "QueuedEvent.hpp" +#include "types.hpp" +#include "PatchImpl.hpp" +#include "BufferFactory.hpp" + +namespace Raul { + template class ListNode; + template class Array; +} + +namespace Ingen { +namespace Server { + +class CompiledPatch; +class InputPort; +class OutputPort; +class PortImpl; + +namespace Events { + +/** Make a Connection between two Ports. + * + * \ingroup engine + */ +class Disconnect : public QueuedEvent +{ +public: + Disconnect( + Engine& engine, + SharedPtr request, + SampleCount timestamp, + const Raul::Path& src_port_path, + const Raul::Path& dst_port_path); + + void pre_process(); + void execute(ProcessContext& context); + void post_process(); + + class Impl { + public: + Impl(Engine& e, + PatchImpl* patch, + OutputPort* s, + InputPort* d); + + bool execute(ProcessContext& context, bool set_dst_buffers); + + InputPort* dst_port() { return _dst_input_port; } + + private: + Engine& _engine; + OutputPort* _src_output_port; + InputPort* _dst_input_port; + PatchImpl* _patch; + SharedPtr _connection; + Raul::Array* _buffers; + }; + +private: + enum ErrorType { + NO_ERROR, + PARENT_PATCH_DIFFERENT, + PORT_NOT_FOUND, + TYPE_MISMATCH, + NOT_CONNECTED, + PARENTS_NOT_FOUND, + CONNECTION_NOT_FOUND + }; + + const Raul::Path _src_port_path; + const Raul::Path _dst_port_path; + + PatchImpl* _patch; + PortImpl* _src_port; + PortImpl* _dst_port; + + Impl* _impl; + CompiledPatch* _compiled_patch; +}; + +} // namespace Events +} // namespace Server +} // namespace Ingen + +#endif // INGEN_EVENTS_DISCONNECT_HPP diff --git a/src/server/events/DisconnectAll.cpp b/src/server/events/DisconnectAll.cpp new file mode 100644 index 00000000..dd694810 --- /dev/null +++ b/src/server/events/DisconnectAll.cpp @@ -0,0 +1,189 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "raul/Array.hpp" +#include "raul/Maid.hpp" +#include "raul/Path.hpp" + +#include "ClientBroadcaster.hpp" +#include "ConnectionImpl.hpp" +#include "Engine.hpp" +#include "EngineStore.hpp" +#include "InputPort.hpp" +#include "NodeImpl.hpp" +#include "OutputPort.hpp" +#include "PatchImpl.hpp" +#include "PortImpl.hpp" +#include "Request.hpp" +#include "events/Disconnect.hpp" +#include "events/DisconnectAll.hpp" +#include "util.hpp" + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { +namespace Events { + +DisconnectAll::DisconnectAll(Engine& engine, SharedPtr request, SampleCount timestamp, const Path& parent_path, const Path& node_path) + : QueuedEvent(engine, request, timestamp) + , _parent_path(parent_path) + , _path(node_path) + , _parent(NULL) + , _node(NULL) + , _port(NULL) + , _compiled_patch(NULL) + , _deleting(false) +{ +} + +/** Internal version for use by other events. + */ +DisconnectAll::DisconnectAll(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)) + , _compiled_patch(NULL) + , _deleting(true) +{ +} + +DisconnectAll::~DisconnectAll() +{ + for (Impls::iterator i = _impls.begin(); i != _impls.end(); ++i) + delete (*i); +} + +void +DisconnectAll::maybe_remove_connection(ConnectionImpl* c) +{ + if (c->pending_disconnection()) + return; + + OutputPort* src = dynamic_cast(c->src_port()); + InputPort* dst = dynamic_cast(c->dst_port()); + + if (_node) { + if (src->parent_node() == _node || dst->parent_node() == _node) { + _impls.push_back(new Disconnect::Impl(_engine, _parent, src, dst)); + } + } else { + assert(_port); + if (src == _port || dst == _port) { + _impls.push_back(new Disconnect::Impl(_engine, _parent, src, dst)); + } + } +} + +void +DisconnectAll::pre_process() +{ + if (!_deleting) { + _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)); + } + + for (Patch::Connections::const_iterator i = _parent->connections().begin(); + i != _parent->connections().end(); ++i) { + maybe_remove_connection((ConnectionImpl*)i->second.get()); + } + + if (!_deleting && _parent->enabled()) + _compiled_patch = _parent->compile(); + + QueuedEvent::pre_process(); +} + +void +DisconnectAll::execute(ProcessContext& context) +{ + QueuedEvent::execute(context); + + if (_error == NO_ERROR) { + for (Impls::iterator i = _impls.begin(); i != _impls.end(); ++i) { + (*i)->execute(context, + !_deleting || ((*i)->dst_port()->parent_node() != _node)); + } + } + + _engine.maid()->push(_parent->compiled_patch()); + _parent->compiled_patch(_compiled_patch); +} + +void +DisconnectAll::post_process() +{ + if (_error == NO_ERROR) { + if (_request) + _request->respond_ok(); + _engine.broadcaster()->disconnect_all(_parent_path, _path); + } else { + if (_request) { + boost::format fmt("Unable to disconnect %1% (%2%)"); + fmt % _path; + switch (_error) { + case INVALID_PARENT_PATH: + fmt % string("Invalid parent path: ").append(_parent_path.str()); + break; + case PARENT_NOT_FOUND: + fmt % string("Unable to find parent: ").append(_parent_path.str()); + break; + case OBJECT_NOT_FOUND: + fmt % string("Unable to find object"); + default: + break; + } + _request->respond_error(fmt.str()); + } + } +} + +} // namespace Server +} // namespace Ingen +} // namespace Events + diff --git a/src/server/events/DisconnectAll.hpp b/src/server/events/DisconnectAll.hpp new file mode 100644 index 00000000..d53d1114 --- /dev/null +++ b/src/server/events/DisconnectAll.hpp @@ -0,0 +1,93 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_EVENTS_DISCONNECTALL_HPP +#define INGEN_EVENTS_DISCONNECTALL_HPP + +#include + +#include "raul/Path.hpp" + +#include "Disconnect.hpp" +#include "QueuedEvent.hpp" + +namespace Ingen { +namespace Server { + +class CompiledPatch; +class NodeImpl; +class PatchImpl; +class PortImpl; + +namespace Events { + +class Disconnect; + +/** An event to disconnect all connections to a Node. + * + * \ingroup engine + */ +class DisconnectAll : public QueuedEvent +{ +public: + DisconnectAll( + Engine& engine, + SharedPtr request, + SampleCount timestamp, + const Raul::Path& parent, + const Raul::Path& object); + + DisconnectAll( + Engine& engine, + PatchImpl* parent, + GraphObjectImpl* object); + + ~DisconnectAll(); + + void pre_process(); + void execute(ProcessContext& context); + void post_process(); + +private: + enum ErrorType { + NO_ERROR, + INVALID_PARENT_PATH, + PARENT_NOT_FOUND, + OBJECT_NOT_FOUND, + }; + + void maybe_remove_connection(ConnectionImpl* c); + + Raul::Path _parent_path; + Raul::Path _path; + PatchImpl* _parent; + NodeImpl* _node; + PortImpl* _port; + + typedef std::list Impls; + Impls _impls; + + CompiledPatch* _compiled_patch; ///< New process order for Patch + + bool _deleting; +}; + +} // namespace Server +} // namespace Ingen +} // namespace Events + +#endif // INGEN_EVENTS_DISCONNECTALL_HPP diff --git a/src/server/events/Get.cpp b/src/server/events/Get.cpp new file mode 100644 index 00000000..058f0b63 --- /dev/null +++ b/src/server/events/Get.cpp @@ -0,0 +1,84 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "Get.hpp" +#include "ingen/ClientInterface.hpp" +#include "Request.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 namespace Raul; + +namespace Ingen { +namespace Server { +namespace Events { + +Get::Get( + Engine& engine, + SharedPtr request, + SampleCount timestamp, + const URI& uri) + : QueuedEvent(engine, request, timestamp) + , _uri(uri) + , _object(NULL) + , _plugin(NULL) +{ +} + +void +Get::pre_process() +{ + if (_uri == "ingen:plugins") { + _plugins = _engine.node_factory()->plugins(); + } else if (Path::is_valid(_uri.str())) { + _object = _engine.engine_store()->find_object(Path(_uri.str())); + } else { + _plugin = _engine.node_factory()->plugin(_uri); + } + + QueuedEvent::pre_process(); +} + +void +Get::post_process() +{ + if (_uri == "ingen:plugins") { + _request->respond_ok(); + _engine.broadcaster()->send_plugins_to(_request->client(), _plugins); + } else if (!_object && !_plugin) { + _request->respond_error("Unable to find object requested."); + } else if (_request->client()) { + _request->respond_ok(); + if (_object) + ObjectSender::send_object(_request->client(), _object, true); + else if (_plugin) + _request->client()->put(_uri, _plugin->properties()); + } else { + _request->respond_error("Unable to find client to send object."); + } +} + +} // namespace Server +} // namespace Ingen +} // namespace Events + diff --git a/src/server/events/Get.hpp b/src/server/events/Get.hpp new file mode 100644 index 00000000..ed68e3c0 --- /dev/null +++ b/src/server/events/Get.hpp @@ -0,0 +1,60 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_EVENTS_GET_HPP +#define INGEN_EVENTS_GET_HPP + +#include "QueuedEvent.hpp" +#include "NodeFactory.hpp" +#include "types.hpp" + +namespace Ingen { +namespace Server { + +class GraphObjectImpl; +class PluginImpl; + +namespace Events { + +/** A request from a client to send an object. + * + * \ingroup engine + */ +class Get : public QueuedEvent +{ +public: + Get( + Engine& engine, + SharedPtr request, + SampleCount timestamp, + const Raul::URI& uri); + + void pre_process(); + void post_process(); + +private: + const Raul::URI _uri; + GraphObjectImpl* _object; + const PluginImpl* _plugin; + NodeFactory::Plugins _plugins; +}; + +} // namespace Server +} // namespace Ingen +} // namespace Events + +#endif // INGEN_EVENTS_GET_HPP diff --git a/src/server/events/Move.cpp b/src/server/events/Move.cpp new file mode 100644 index 00000000..2e006b8c --- /dev/null +++ b/src/server/events/Move.cpp @@ -0,0 +1,130 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "raul/Path.hpp" +#include "events/Move.hpp" +#include "ClientBroadcaster.hpp" +#include "Engine.hpp" +#include "NodeImpl.hpp" +#include "EngineStore.hpp" +#include "PatchImpl.hpp" +#include "Request.hpp" +#include "Driver.hpp" + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { +namespace Events { + +Move::Move(Engine& engine, SharedPtr request, SampleCount timestamp, const Path& path, const Path& new_path) + : QueuedEvent(engine, request, timestamp) + , _old_path(path) + , _new_path(new_path) + , _parent_patch(NULL) + , _store_iterator(engine.engine_store()->end()) +{ +} + +Move::~Move() +{ +} + +void +Move::pre_process() +{ + if (!_old_path.parent().is_parent_of(_new_path)) { + _error = PARENT_DIFFERS; + QueuedEvent::pre_process(); + return; + } + _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 = 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 +Move::execute(ProcessContext& context) +{ + QueuedEvent::execute(context); + + SharedPtr port = PtrCast(_store_iterator->second); + if (port && port->parent()->parent() == NULL) { + DriverPort* driver_port = _engine.driver()->driver_port(_new_path); + if (driver_port) + driver_port->move(_new_path); + } +} + +void +Move::post_process() +{ + string msg = "Unable to rename object - "; + + if (_error == NO_ERROR) { + _request->respond_ok(); + _engine.broadcaster()->move(_old_path, _new_path); + } else { + if (_error == OBJECT_EXISTS) + msg.append("Object already exists at ").append(_new_path.str()); + else if (_error == OBJECT_NOT_FOUND) + msg.append("Could not find object ").append(_old_path.str()); + else if (_error == OBJECT_NOT_RENAMABLE) + msg.append(_old_path.str()).append(" is not renamable"); + else if (_error == PARENT_DIFFERS) + msg.append(_new_path.str()).append(" is a child of a different patch"); + + _request->respond_error(msg); + } +} + +} // namespace Server +} // namespace Ingen +} // namespace Events diff --git a/src/server/events/Move.hpp b/src/server/events/Move.hpp new file mode 100644 index 00000000..4286f583 --- /dev/null +++ b/src/server/events/Move.hpp @@ -0,0 +1,79 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_EVENTS_MOVE_HPP +#define INGEN_EVENTS_MOVE_HPP + +#include "raul/Path.hpp" +#include "QueuedEvent.hpp" +#include "EngineStore.hpp" + +namespace Ingen { +namespace Server { + +class PatchImpl; + +namespace Events { + +/** \page methods + *

MOVE

+ * As per WebDAV (RFC4918 S9.9). + * + * Move an object from its current location and insert it at a new location + * in a single operation. + * + * MOVE to a path with a different parent is currently not supported. + */ + +/** MOVE a graph object to a new path (see \ref methods). + * \ingroup engine + */ +class Move : public QueuedEvent +{ +public: + Move( + Engine& engine, + SharedPtr request, + SampleCount timestamp, + const Raul::Path& old_path, + const Raul::Path& new_path); + ~Move(); + + void pre_process(); + void execute(ProcessContext& context); + void post_process(); + +private: + enum ErrorType { + NO_ERROR, + OBJECT_NOT_FOUND, + OBJECT_EXISTS, + OBJECT_NOT_RENAMABLE, + PARENT_DIFFERS + }; + + Raul::Path _old_path; + Raul::Path _new_path; + PatchImpl* _parent_patch; + EngineStore::iterator _store_iterator; +}; + +} // namespace Server +} // namespace Ingen +} // namespace Events + +#endif // INGEN_EVENTS_MOVE_HPP diff --git a/src/server/events/Ping.hpp b/src/server/events/Ping.hpp new file mode 100644 index 00000000..2353e496 --- /dev/null +++ b/src/server/events/Ping.hpp @@ -0,0 +1,51 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_EVENTS_PING_HPP +#define INGEN_EVENTS_PING_HPP + +#include "QueuedEvent.hpp" +#include "types.hpp" +#include "Request.hpp" + +namespace Ingen { +namespace Server { + +class PortImpl; + +namespace Events { + +/** A ping that travels through the pre-processed event queue before responding + * (useful for the order guarantee). + * + * \ingroup engine + */ +class Ping : public QueuedEvent +{ +public: + Ping(Engine& engine, SharedPtr request, SampleCount timestamp) + : QueuedEvent(engine, request, timestamp) + {} + + void post_process() { _request->respond_ok(); } +}; + +} // namespace Server +} // namespace Ingen +} // namespace Events + +#endif // INGEN_EVENTS_PING_HPP diff --git a/src/server/events/RegisterClient.cpp b/src/server/events/RegisterClient.cpp new file mode 100644 index 00000000..71ec26bc --- /dev/null +++ b/src/server/events/RegisterClient.cpp @@ -0,0 +1,57 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "Request.hpp" +#include "events/RegisterClient.hpp" +#include "Engine.hpp" +#include "ClientBroadcaster.hpp" + +using namespace Raul; + +namespace Ingen { +namespace Server { +namespace Events { + +RegisterClient::RegisterClient(Engine& engine, + SharedPtr request, + SampleCount timestamp, + const URI& uri, + ClientInterface* client) + : QueuedEvent(engine, request, timestamp) + , _uri(uri) + , _client(client) +{ +} + +void +RegisterClient::pre_process() +{ + _engine.broadcaster()->register_client(_uri, _client); + + QueuedEvent::pre_process(); +} + +void +RegisterClient::post_process() +{ + _request->respond_ok(); +} + +} // namespace Server +} // namespace Ingen +} // namespace Events + diff --git a/src/server/events/RegisterClient.hpp b/src/server/events/RegisterClient.hpp new file mode 100644 index 00000000..ec2d0809 --- /dev/null +++ b/src/server/events/RegisterClient.hpp @@ -0,0 +1,54 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_EVENTS_REGISTERCLIENT_HPP +#define INGEN_EVENTS_REGISTERCLIENT_HPP + +#include "raul/URI.hpp" +#include "ingen/ClientInterface.hpp" +#include "QueuedEvent.hpp" + +namespace Ingen { +namespace Server { +namespace Events { + +/** Registers a new client with the OSC system, so it can receive updates. + * + * \ingroup engine + */ +class RegisterClient : public QueuedEvent +{ +public: + RegisterClient(Engine& engine, + SharedPtr request, + SampleCount timestamp, + const Raul::URI& uri, + ClientInterface* client); + + void pre_process(); + void post_process(); + +private: + Raul::URI _uri; + ClientInterface* _client; +}; + +} // namespace Server +} // namespace Ingen +} // namespace Events + +#endif // INGEN_EVENTS_REGISTERCLIENT_HPP diff --git a/src/server/events/RequestMetadata.cpp b/src/server/events/RequestMetadata.cpp new file mode 100644 index 00000000..156fb51d --- /dev/null +++ b/src/server/events/RequestMetadata.cpp @@ -0,0 +1,137 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "raul/IntrusivePtr.hpp" +#include "ingen/ClientInterface.hpp" +#include "events/RequestMetadata.hpp" +#include "shared/LV2Atom.hpp" +#include "shared/LV2URIMap.hpp" +#include "AudioBuffer.hpp" +#include "ClientBroadcaster.hpp" +#include "Engine.hpp" +#include "EngineStore.hpp" +#include "GraphObjectImpl.hpp" +#include "ObjectBuffer.hpp" +#include "PluginImpl.hpp" +#include "PortImpl.hpp" +#include "ProcessContext.hpp" +#include "Request.hpp" + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { +namespace Events { + +RequestMetadata::RequestMetadata(Engine& engine, + SharedPtr request, + SampleCount timestamp, + Resource::Graph ctx, + const URI& subject, + const URI& key) + : QueuedEvent(engine, request, timestamp) + , _special_type(NONE) + , _uri(subject) + , _key(key) + , _resource(0) + , _context(ctx) +{ +} + +void +RequestMetadata::pre_process() +{ + const bool is_object = Path::is_path(_uri); + if (_request->client()) { + if (is_object) + _resource = _engine.engine_store()->find_object(Path(_uri.str())); + else + _resource = _engine.node_factory()->plugin(_uri); + + if (!_resource) { + QueuedEvent::pre_process(); + return; + } + } + + GraphObjectImpl* obj = dynamic_cast(_resource); + if (obj) { + if (_key == _engine.world()->uris()->ingen_value) + _special_type = PORT_VALUE; + else + _value = obj->get_property(_key); + } else { + _value = _resource->get_property(_key); + } + + QueuedEvent::pre_process(); +} + +void +RequestMetadata::execute(ProcessContext& context) +{ + QueuedEvent::execute(context); + if (_special_type == PORT_VALUE) { + PortImpl* port = dynamic_cast(_resource); + if (port) { + IntrusivePtr abuf = PtrCast(port->buffer(0)); + if (abuf) { + _value = abuf->value_at(0); + } else { + IntrusivePtr obuf = PtrCast(port->buffer(0)); + if (obuf) { + Ingen::Shared::LV2Atom::to_atom(*_engine.world()->uris().get(), + obuf->atom(), + _value); + } + } + } else { + _resource = 0; + } + } +} + +void +RequestMetadata::post_process() +{ + if (_request->client()) { + if (_special_type == PORT_VALUE) { + if (_resource) { + _request->respond_ok(); + _request->client()->set_property(_uri.str(), + _engine.world()->uris()->ingen_value, _value); + } else { + const string msg = "Get value for non-port " + _uri.str(); + _request->respond_error(msg); + } + } else if (!_resource) { + const string msg = "Unable to find subject " + _uri.str(); + _request->respond_error(msg); + } else { + _request->respond_ok(); + _request->client()->set_property(_uri, _key, _value); + } + } else { + _request->respond_error("Unknown client"); + } +} + +} // namespace Server +} // namespace Ingen +} // namespace Events + diff --git a/src/server/events/RequestMetadata.hpp b/src/server/events/RequestMetadata.hpp new file mode 100644 index 00000000..3f8311ef --- /dev/null +++ b/src/server/events/RequestMetadata.hpp @@ -0,0 +1,79 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_EVENTS_REQUESTMETADATA_HPP +#define INGEN_EVENTS_REQUESTMETADATA_HPP + +#include "raul/Atom.hpp" +#include "raul/URI.hpp" + +#include "QueuedEvent.hpp" + +namespace Ingen { + +namespace Shared { class ResourceImpl; } + +namespace Server { + +class GraphObjectImpl; + +namespace Events { + +/** \page methods + *

GET

+ * As per HTTP (RFC2616 S9.3). + * + * Get the description of a graph object. + */ + +/** GET an object (see \ref methods). + * + * \ingroup engine + */ +class RequestMetadata : public QueuedEvent +{ +public: + RequestMetadata(Engine& engine, + SharedPtr request, + SampleCount timestamp, + Resource::Graph context, + const Raul::URI& subject, + const Raul::URI& key); + + void pre_process(); + void execute(ProcessContext& context); + void post_process(); + +private: + enum ErrorType { NO_ERROR, NOT_FOUND }; + enum { + NONE, + PORT_VALUE + } _special_type; + + Raul::URI _uri; + Raul::URI _key; + Raul::Atom _value; + Ingen::Shared::ResourceImpl* _resource; + Resource::Graph _context; +}; + +} // namespace Server +} // namespace Ingen +} // namespace Events + +#endif // INGEN_EVENTS_REQUESTMETADATA_HPP diff --git a/src/server/events/SendBinding.cpp b/src/server/events/SendBinding.cpp new file mode 100644 index 00000000..ebfa21dd --- /dev/null +++ b/src/server/events/SendBinding.cpp @@ -0,0 +1,55 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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/SendBinding.hpp" +#include "shared/LV2URIMap.hpp" +#include "Engine.hpp" +#include "PortImpl.hpp" +#include "ClientBroadcaster.hpp" + +using namespace std; + +namespace Ingen { +namespace Server { +namespace Events { + +void +SendBinding::post_process() +{ + const Ingen::Shared::LV2URIMap& uris = *_engine.world()->uris().get(); + Raul::Atom::DictValue dict; + if (_type == ControlBindings::MIDI_CC) { + dict[uris.rdf_type] = uris.midi_Controller; + dict[uris.midi_controllerNumber] = _num; + } else if (_type == ControlBindings::MIDI_BENDER) { + dict[uris.rdf_type] = uris.midi_Bender; + } else if (_type == ControlBindings::MIDI_CHANNEL_PRESSURE) { + dict[uris.rdf_type] = uris.midi_ChannelPressure; + } else if (_type == ControlBindings::MIDI_NOTE) { + dict[uris.rdf_type] = uris.midi_Note; + dict[uris.midi_noteNumber] = _num; + } + // TODO: other event types + _port->set_property(uris.ingen_controlBinding, dict); // FIXME: thread unsafe + _engine.broadcaster()->set_property(_port->path(), uris.ingen_controlBinding, dict); +} + +} // namespace Server +} // namespace Ingen +} // namespace Events + diff --git a/src/server/events/SendBinding.hpp b/src/server/events/SendBinding.hpp new file mode 100644 index 00000000..f3727ece --- /dev/null +++ b/src/server/events/SendBinding.hpp @@ -0,0 +1,86 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_EVENTS_SENDBINDING_HPP +#define INGEN_EVENTS_SENDBINDING_HPP + +#include "server/Event.hpp" +#include "server/ControlBindings.hpp" +#include "server/types.hpp" + +namespace Ingen { +namespace Server { + +class PortImpl; + +namespace Events { + +/** A special event used internally to send control bindings from the audio thread. + * + * See SendPortValue documentation for details. + * + * \ingroup engine + */ +class SendBinding : public Event +{ +public: + inline SendBinding( + Engine& engine, + SampleCount timestamp, + PortImpl* port, + ControlBindings::Type type, + int16_t num) + : Event(engine, SharedPtr(), timestamp) + , _port(port) + , _type(type) + , _num(num) + { + assert(_port); + switch (type) { + case ControlBindings::MIDI_CC: + assert(num >= 0 && num < 128); + break; + case ControlBindings::MIDI_RPN: + assert(num >= 0 && num < 16384); + break; + case ControlBindings::MIDI_NRPN: + assert(num >= 0 && num < 16384); + default: + break; + } + } + + inline SendBinding& operator=(const SendBinding& ev) { + _port = ev._port; + _type = ev._type; + _num = ev._num; + return *this; + } + + void post_process(); + +private: + PortImpl* _port; + ControlBindings::Type _type; + int16_t _num; +}; + +} // namespace Server +} // namespace Ingen +} // namespace Events + +#endif // INGEN_EVENTS_SENDBINDING_HPP diff --git a/src/server/events/SendPortActivity.cpp b/src/server/events/SendPortActivity.cpp new file mode 100644 index 00000000..8ff960e2 --- /dev/null +++ b/src/server/events/SendPortActivity.cpp @@ -0,0 +1,36 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "events/SendPortActivity.hpp" +#include "Engine.hpp" +#include "PortImpl.hpp" +#include "ClientBroadcaster.hpp" + +namespace Ingen { +namespace Server { +namespace Events { + +void +SendPortActivity::post_process() +{ + _engine.broadcaster()->activity(_port->path()); +} + +} // namespace Server +} // namespace Ingen +} // namespace Events + diff --git a/src/server/events/SendPortActivity.hpp b/src/server/events/SendPortActivity.hpp new file mode 100644 index 00000000..dd74a68a --- /dev/null +++ b/src/server/events/SendPortActivity.hpp @@ -0,0 +1,69 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_EVENTS_SENDPORTACTIVITY_HPP +#define INGEN_EVENTS_SENDPORTACTIVITY_HPP + +#include "Event.hpp" +#include "types.hpp" + +namespace Ingen { +namespace Server { + +class PortImpl; + +namespace Events { + +/** 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 SendPortActivity : public Event +{ +public: + inline SendPortActivity(Engine& engine, + SampleCount timestamp, + PortImpl* port) + : Event(engine, SharedPtr(), timestamp) + , _port(port) + { + } + + inline SendPortActivity& operator=(const SendPortActivity& ev) { + _port = ev._port; + return *this; + } + + void post_process(); + +private: + PortImpl* _port; +}; + +} // namespace Server +} // namespace Ingen +} // namespace Events + +#endif // INGEN_EVENTS_SENDPORTACTIVITY_HPP diff --git a/src/server/events/SendPortValue.cpp b/src/server/events/SendPortValue.cpp new file mode 100644 index 00000000..1364b692 --- /dev/null +++ b/src/server/events/SendPortValue.cpp @@ -0,0 +1,42 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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/SendPortValue.hpp" +#include "shared/LV2URIMap.hpp" +#include "Engine.hpp" +#include "PortImpl.hpp" +#include "ClientBroadcaster.hpp" + +using namespace std; + +namespace Ingen { +namespace Server { +namespace Events { + +void +SendPortValue::post_process() +{ + _engine.broadcaster()->set_property( + _port->path(), + _engine.world()->uris()->ingen_value, _value); +} + +} // namespace Server +} // namespace Ingen +} // namespace Events + diff --git a/src/server/events/SendPortValue.hpp b/src/server/events/SendPortValue.hpp new file mode 100644 index 00000000..4465ef00 --- /dev/null +++ b/src/server/events/SendPortValue.hpp @@ -0,0 +1,82 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_EVENTS_SENDPORTVALUE_HPP +#define INGEN_EVENTS_SENDPORTVALUE_HPP + +#include "raul/Atom.hpp" +#include "server/Event.hpp" +#include "server/types.hpp" + +namespace Ingen { +namespace Server { + +class PortImpl; + +namespace Events { + +/** 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 SendPortValue : public Event +{ +public: + inline SendPortValue( + Engine& engine, + SampleCount timestamp, + PortImpl* port, + bool omni, + uint32_t voice_num, + const Raul::Atom& value) + : Event(engine, SharedPtr(), timestamp) + , _port(port) + , _omni(omni) + , _voice_num(voice_num) + , _value(value) + { + } + + inline SendPortValue& operator=(const SendPortValue& ev) { + _port = ev._port; + _omni = ev._omni; + _voice_num = ev._voice_num; + _value = ev._value; + return *this; + } + + void post_process(); + +private: + PortImpl* _port; + bool _omni; + uint32_t _voice_num; + Raul::Atom _value; +}; + +} // namespace Server +} // namespace Ingen +} // namespace Events + +#endif // INGEN_EVENTS_SENDPORTVALUE_HPP diff --git a/src/server/events/SetMetadata.cpp b/src/server/events/SetMetadata.cpp new file mode 100644 index 00000000..1812ad91 --- /dev/null +++ b/src/server/events/SetMetadata.cpp @@ -0,0 +1,380 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "raul/log.hpp" +#include "raul/Maid.hpp" +#include "ingen/PortType.hpp" +#include "shared/LV2URIMap.hpp" +#include "ClientBroadcaster.hpp" +#include "ControlBindings.hpp" +#include "CreateNode.hpp" +#include "CreatePatch.hpp" +#include "CreatePort.hpp" +#include "Driver.hpp" +#include "Engine.hpp" +#include "EngineStore.hpp" +#include "GraphObjectImpl.hpp" +#include "PatchImpl.hpp" +#include "PluginImpl.hpp" +#include "PortImpl.hpp" +#include "Request.hpp" +#include "SetMetadata.hpp" +#include "SetPortValue.hpp" + +#define LOG(s) s << "[SetMetadata] " + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { +namespace Events { + +typedef Resource::Properties Properties; + +SetMetadata::SetMetadata( + Engine& engine, + SharedPtr request, + SampleCount timestamp, + bool create, + Resource::Graph context, + const URI& subject, + const Properties& properties, + const Properties& remove) + : QueuedEvent(engine, request, timestamp, false) + , _create_event(NULL) + , _subject(subject) + , _properties(properties) + , _remove(remove) + , _object(NULL) + , _patch(NULL) + , _compiled_patch(NULL) + , _create(create) + , _context(context) +{ + if (context != Resource::DEFAULT) { + Resource::set_context(_properties, context); + } + + /* + LOG(info) << "Set " << subject << " : " << context << " {" << endl; + typedef Resource::Properties::const_iterator iterator; + for (iterator i = properties.begin(); i != properties.end(); ++i) + LOG(info) << " " << i->first << " = " << i->second << " :: " << i->second.type() << endl; + LOG(info) << "}" << endl; + + LOG(info) << "Unset " << subject << " {" << endl; + typedef Resource::Properties::const_iterator iterator; + for (iterator i = remove.begin(); i != remove.end(); ++i) + LOG(info) << " " << i->first << " = " << i->second << " :: " << i->second.type() << endl; + LOG(info) << "}" << endl; + */ +} + +SetMetadata::~SetMetadata() +{ + for (SetEvents::iterator i = _set_events.begin(); i != _set_events.end(); ++i) + delete *i; + + delete _create_event; +} + +void +SetMetadata::pre_process() +{ + typedef Properties::const_iterator iterator; + + const bool is_graph_object = Path::is_path(_subject); + + _object = is_graph_object + ? _engine.engine_store()->find_object(Path(_subject.str())) + : static_cast(_engine.node_factory()->plugin(_subject)); + + if (!_object && (!is_graph_object || !_create)) { + _error = NOT_FOUND; + QueuedEvent::pre_process(); + return; + } + + const Ingen::Shared::LV2URIMap& uris = *_engine.world()->uris().get(); + + if (is_graph_object && !_object) { + Path path(_subject.str()); + bool is_patch = false, is_node = false, is_port = false, is_output = false; + PortType data_type(PortType::UNKNOWN); + Shared::ResourceImpl::type(uris, _properties, is_patch, is_node, is_port, is_output, data_type); + + // Create a separate request without a source so EventSource isn't unblocked twice + SharedPtr sub_request(new Request(NULL, _request->client(), _request->id())); + + if (is_patch) { + uint32_t poly = 1; + iterator p = _properties.find(uris.ingen_polyphony); + if (p != _properties.end() && p->second.is_valid() && p->second.type() == Atom::INT) + poly = p->second.get_int32(); + _create_event = new CreatePatch(_engine, sub_request, _time, + path, poly, _properties); + } else if (is_node) { + const iterator p = _properties.find(uris.rdf_instanceOf); + _create_event = new CreateNode(_engine, sub_request, _time, + path, p->second.get_uri(), _properties); + } else if (is_port) { + _blocking = bool(_request); + _create_event = new CreatePort(_engine, sub_request, _time, + path, data_type.uri(), is_output, _properties); + } + if (_create_event) + _create_event->pre_process(); + else + _error = BAD_OBJECT_TYPE; + QueuedEvent::pre_process(); + return; + } + + _types.reserve(_properties.size()); + + GraphObjectImpl* obj = dynamic_cast(_object); + +#if 0 + // If we're replacing (i.e. this is a PUT, not a POST), first remove all properties + // with keys we will later set. This must be done first so a PUT with several properties + // of the same predicate (e.g. rdf:type) retains the multiple values. Only previously + // existing properties should be replaced + if (_replace) + for (Properties::iterator p = _properties.begin(); p != _properties.end(); ++p) + obj->properties().erase(p->first); +#endif + + for (Properties::const_iterator p = _remove.begin(); p != _remove.end(); ++p) { + const Raul::URI& key = p->first; + const Raul::Atom& value = p->second; + if (key == uris.ingen_controlBinding && value == uris.wildcard) { + PortImpl* port = dynamic_cast(_object); + if (port) + _old_bindings = _engine.control_bindings()->remove(port); + } + _object->remove_property(key, value); + } + + for (Properties::iterator p = _properties.begin(); p != _properties.end(); ++p) { + const Raul::URI& key = p->first; + const Raul::Atom& value = p->second; + SpecialType op = NONE; + if (obj) { + Resource& resource = *obj; + resource.add_property(key, value); + + PortImpl* port = dynamic_cast(_object); + if (port) { + if (key == uris.ingen_broadcast) { + if (value.type() == Atom::BOOL) { + op = ENABLE_BROADCAST; + } else { + _error = BAD_VALUE_TYPE; + } + } else if (key == uris.ingen_value) { + SetPortValue* ev = new SetPortValue(_engine, _request, _time, port, value); + ev->pre_process(); + _set_events.push_back(ev); + } else if (key == uris.ingen_controlBinding) { + if (port->is_a(PortType::CONTROL)) { + if (value == uris.wildcard) { + _engine.control_bindings()->learn(port); + } else if (value.type() == Atom::DICT) { + op = CONTROL_BINDING; + } else { + _error = BAD_VALUE_TYPE; + } + } else { + _error = BAD_OBJECT_TYPE; + } + } + } else if ((_patch = dynamic_cast(_object))) { + if (key == uris.ingen_enabled) { + if (value.type() == Atom::BOOL) { + op = ENABLE; + // FIXME: defer this until all other metadata has been processed + if (value.get_bool() && !_patch->enabled()) + _compiled_patch = _patch->compile(); + } else { + _error = BAD_VALUE_TYPE; + } + } else if (key == uris.ingen_polyphony) { + if (value.type() == Atom::INT) { + op = POLYPHONY; + _blocking = true; + _patch->prepare_internal_poly(*_engine.buffer_factory(), value.get_int32()); + } else { + _error = BAD_VALUE_TYPE; + } + } + } else if (key == uris.ingen_polyphonic) { + PatchImpl* parent = dynamic_cast(obj->parent()); + if (parent) { + if (value.type() == Atom::BOOL) { + op = POLYPHONIC; + _blocking = true; + obj->set_property(key, value.get_bool()); + NodeImpl* node = dynamic_cast(obj); + if (node) + node->set_polyphonic(value.get_bool()); + if (value.get_bool()) { + obj->prepare_poly(*_engine.buffer_factory(), parent->internal_poly()); + } else { + obj->prepare_poly(*_engine.buffer_factory(), 1); + } + } else { + _error = BAD_VALUE_TYPE; + } + } else { + _error = BAD_OBJECT_TYPE; + } + } + } + + if (_error != NO_ERROR) { + _error_predicate += key.str(); + break; + } + + _types.push_back(op); + } + + QueuedEvent::pre_process(); +} + +void +SetMetadata::execute(ProcessContext& context) +{ + if (_error != NO_ERROR) { + QueuedEvent::execute(context); + return; + } + + if (_create_event) { + QueuedEvent::execute(context); + _create_event->execute(context); + if (_blocking) + _request->unblock(); + return; + } + + for (SetEvents::iterator i = _set_events.begin(); i != _set_events.end(); ++i) + (*i)->execute(context); + + GraphObjectImpl* const object = dynamic_cast(_object); + NodeImpl* const node = dynamic_cast(_object); + PortImpl* const port = dynamic_cast(_object); + + std::vector::const_iterator t = _types.begin(); + for (Properties::const_iterator p = _properties.begin(); p != _properties.end(); ++p, ++t) { + const Raul::Atom& value = p->second; + switch (*t) { + case ENABLE_BROADCAST: + if (port) + port->broadcast(value.get_bool()); + break; + case ENABLE: + if (value.get_bool()) { + if (_compiled_patch) { + _engine.maid()->push(_patch->compiled_patch()); + _patch->compiled_patch(_compiled_patch); + } + _patch->enable(); + } else { + _patch->disable(); + } + break; + case POLYPHONIC: + { + PatchImpl* parent = reinterpret_cast(object->parent()); + if (value.get_bool()) + object->apply_poly(*_engine.maid(), parent->internal_poly()); + else + object->apply_poly(*_engine.maid(), 1); + } + break; + case POLYPHONY: + if (_patch->internal_poly() != static_cast(value.get_int32()) && + !_patch->apply_internal_poly(_engine.driver()->context(), + *_engine.buffer_factory(), + *_engine.maid(), value.get_int32())) { + _error = INTERNAL; + } + break; + case CONTROL_BINDING: + if (port) { + _engine.control_bindings()->port_binding_changed(context, port); + } else if (node) { + if (node->plugin_impl()->type() == Plugin::Internal) { + node->learn(); + } + } + break; + case NONE: + break; + } + } + + QueuedEvent::execute(context); + + if (_blocking) + _request->unblock(); +} + +void +SetMetadata::post_process() +{ + for (SetEvents::iterator i = _set_events.begin(); i != _set_events.end(); ++i) + (*i)->post_process(); + + switch (_error) { + case NO_ERROR: + if (_create_event) + _create_event->post_process(); + else + _request->respond_ok(); + if (_create) + _engine.broadcaster()->put(_subject, _properties, _context); + else + _engine.broadcaster()->delta(_subject, _remove, _properties); + break; + case NOT_FOUND: + _request->respond_error((boost::format( + "Unable to find object `%1%'") % _subject).str()); + break; + case INTERNAL: + _request->respond_error("Internal error"); + break; + case BAD_OBJECT_TYPE: + _request->respond_error((boost::format( + "Bad type for object `%1%'") % _subject).str()); + break; + case BAD_VALUE_TYPE: + _request->respond_error((boost::format( + "Bad metadata value type for subject `%1%' predicate `%2%'") + % _subject % _error_predicate).str()); + break; + } +} + +} // namespace Server +} // namespace Ingen +} // namespace Events + diff --git a/src/server/events/SetMetadata.hpp b/src/server/events/SetMetadata.hpp new file mode 100644 index 00000000..59856730 --- /dev/null +++ b/src/server/events/SetMetadata.hpp @@ -0,0 +1,124 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_EVENTS_SETMETADATA_HPP +#define INGEN_EVENTS_SETMETADATA_HPP + +#include +#include "raul/URI.hpp" +#include "shared/ResourceImpl.hpp" +#include "QueuedEvent.hpp" + +namespace Ingen { +namespace Server { + +class GraphObjectImpl; +class PatchImpl; +class CompiledPatch; + +namespace Events { + +/** \page methods + *

POST

+ * As per HTTP (RFC2616 S9.5). + * + * Append properties to a graph object. + * + * An object can have several properties with a single predicate. + * POST appends properties without modifying or removing existing properties. + */ + +/** \page methods + *

PUT

+ * As per HTTP (RFC2616 S9.6). + * + * Set properties of a graph object, or create an object. + * + * An object can have several properties with a single predicate. + * \li If the object does not yet exist, the message must contain sufficient + * information to create the object (e.g. known rdf:type properties, etc.) + * \li If the object does exist, a PUT removes all existing object properties + * with predicates that match any property in the message, then adds all + * properties from the message. + */ + +class SetPortValue; + +/** Set properties of a graph object. + * \ingroup engine + */ +class SetMetadata : public QueuedEvent +{ +public: + SetMetadata( + Engine& engine, + SharedPtr request, + SampleCount timestamp, + bool create, + Resource::Graph context, + const Raul::URI& subject, + const Resource::Properties& properties, + const Resource::Properties& remove = Resource::Properties()); + + ~SetMetadata(); + + void pre_process(); + void execute(ProcessContext& context); + void post_process(); + +private: + enum ErrorType { + NO_ERROR, + NOT_FOUND, + INTERNAL, + BAD_OBJECT_TYPE, + BAD_VALUE_TYPE + }; + + enum SpecialType { + NONE, + ENABLE, + ENABLE_BROADCAST, + POLYPHONY, + POLYPHONIC, + CONTROL_BINDING + }; + + typedef std::vector SetEvents; + + QueuedEvent* _create_event; + SetEvents _set_events; + std::vector _types; + std::vector _remove_types; + Raul::URI _subject; + Resource::Properties _properties; + Resource::Properties _remove; + Ingen::Shared::ResourceImpl* _object; + PatchImpl* _patch; + CompiledPatch* _compiled_patch; + std::string _error_predicate; + bool _create; + Resource::Graph _context; + + SharedPtr _old_bindings; +}; + +} // namespace Server +} // namespace Ingen +} // namespace Events + +#endif // INGEN_EVENTS_SETMETADATA_HPP diff --git a/src/server/events/SetPortValue.cpp b/src/server/events/SetPortValue.cpp new file mode 100644 index 00000000..4af14d44 --- /dev/null +++ b/src/server/events/SetPortValue.cpp @@ -0,0 +1,223 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "raul/log.hpp" +#include "lv2/lv2plug.in/ns/ext/event/event.h" +#include "shared/LV2URIMap.hpp" +#include "shared/LV2Features.hpp" +#include "shared/LV2Atom.hpp" +#include "shared/World.hpp" +#include "AudioBuffer.hpp" +#include "ClientBroadcaster.hpp" +#include "ControlBindings.hpp" +#include "Driver.hpp" +#include "Engine.hpp" +#include "EngineStore.hpp" +#include "EventBuffer.hpp" +#include "MessageContext.hpp" +#include "NodeImpl.hpp" +#include "ObjectBuffer.hpp" +#include "PortImpl.hpp" +#include "ProcessContext.hpp" +#include "ProcessContext.hpp" +#include "Request.hpp" +#include "SetPortValue.hpp" + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { +namespace Events { + +SetPortValue::SetPortValue(Engine& engine, + SharedPtr request, + bool queued, + SampleCount timestamp, + const Raul::Path& port_path, + const Raul::Atom& value) + : QueuedEvent(engine, request, timestamp) + , _queued(queued) + , _port_path(port_path) + , _value(value) + , _port(NULL) +{ +} + +/** Internal */ +SetPortValue::SetPortValue(Engine& engine, + SharedPtr request, + SampleCount timestamp, + PortImpl* port, + const Raul::Atom& value) + : QueuedEvent(engine, request, timestamp) + , _queued(false) + , _port_path(port->path()) + , _value(value) + , _port(port) +{ +} + +SetPortValue::~SetPortValue() +{ +} + +void +SetPortValue::pre_process() +{ + if (_queued) { + if (_port == NULL) + _port = _engine.engine_store()->find_port(_port_path); + if (_port == NULL) + _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) { + apply(*_engine.message_context()); + _port->parent_node()->set_port_valid(_port->index()); + _engine.message_context()->run(_port->parent_node(), + _engine.driver()->frame_time() + _engine.driver()->block_length()); + } + + if (_port) { + _port->set_value(_value); + _port->set_property(_engine.world()->uris()->ingen_value, _value); + } + + QueuedEvent::pre_process(); +} + +void +SetPortValue::execute(ProcessContext& context) +{ + Event::execute(context); + assert(_time >= context.start() && _time <= context.end()); + + if (_port && _port->context() == Context::MESSAGE) + return; + + apply(context); + _engine.control_bindings()->port_value_changed(context, _port); +} + +void +SetPortValue::apply(Context& context) +{ + uint32_t start = context.start(); + if (_error == NO_ERROR && !_port) + _port = _engine.engine_store()->find_port(_port_path); + + if (!_port) { + 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).get(); + AudioBuffer* const abuf = dynamic_cast(buf); + if (abuf) { + if (_value.type() != Atom::FLOAT) { + _error = TYPE_MISMATCH; + return; + } + + for (uint32_t v = 0; v < _port->poly(); ++v) { + ((AudioBuffer*)_port->buffer(v).get())->set_value( + _value.get_float(), start, _time); + } + return; + } + + Ingen::Shared::LV2URIMap& uris = *_engine.world()->uris().get(); + + EventBuffer* const ebuf = dynamic_cast(buf); + if (ebuf && _value.type() == Atom::BLOB) { + const uint32_t frames = std::max(uint32_t(_time - start), ebuf->latest_frames()); + + // Size 0 event, pass it along to the plugin as a typed but empty event + if (_value.data_size() == 0) { + const uint32_t type_id = uris.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(), + "http://lv2plug.in/ns/ext/midi#MidiEvent")) { + ebuf->prepare_write(context); + ebuf->append(frames, 0, + uris.global_to_event(uris.midi_MidiEvent.id).second, + _value.data_size(), + (const uint8_t*)_value.get_blob()); + _port->raise_set_by_user_flag(); + return; + } + } + + ObjectBuffer* const obuf = dynamic_cast(buf); + if (obuf) { + obuf->atom()->size = obuf->size() - sizeof(LV2_Atom); + if (Ingen::Shared::LV2Atom::from_atom(uris, _value, obuf->atom())) { + debug << "Converted atom " << _value << " :: " << obuf->atom()->type + << " * " << obuf->atom()->size << " @ " << obuf->atom() << endl; + return; + } else { + warn << "Failed to convert atom to LV2 object" << endl; + } + } + + warn << "Unknown value type " << (int)_value.type() << endl; + } +} + +void +SetPortValue::post_process() +{ + string msg; + std::ostringstream ss; + switch (_error) { + case NO_ERROR: + assert(_port != NULL); + _request->respond_ok(); + _engine.broadcaster()->set_property(_port_path, + _engine.world()->uris()->ingen_value, _value); + break; + case TYPE_MISMATCH: + ss << "Illegal value type " << _value.type() + << " for port " << _port_path << endl; + _request->respond_error(ss.str()); + break; + case PORT_NOT_FOUND: + msg = "Unable to find port "; + msg.append(_port_path.str()).append(" to set value"); + _request->respond_error(msg); + break; + case NO_SPACE: + ss << "Attempt to write " << _value.data_size() << " bytes to " + << _port_path.str() << ", with capacity " + << _port->buffer_size() << endl; + _request->respond_error(ss.str()); + break; + } +} + +} // namespace Server +} // namespace Ingen +} // namespace Events + diff --git a/src/server/events/SetPortValue.hpp b/src/server/events/SetPortValue.hpp new file mode 100644 index 00000000..6f3cde87 --- /dev/null +++ b/src/server/events/SetPortValue.hpp @@ -0,0 +1,83 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_EVENTS_SETPORTVALUE_HPP +#define INGEN_EVENTS_SETPORTVALUE_HPP + +#include "raul/Atom.hpp" +#include "QueuedEvent.hpp" +#include "types.hpp" + +namespace Ingen { +namespace Server { + +class PortImpl; + +namespace Events { + +/** 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 SetPortValue : public QueuedEvent +{ +public: + SetPortValue(Engine& engine, + SharedPtr request, + bool queued, + SampleCount timestamp, + const Raul::Path& port_path, + const Raul::Atom& value); + + SetPortValue(Engine& engine, + SharedPtr request, + SampleCount timestamp, + PortImpl* port, + const Raul::Atom& value); + + ~SetPortValue(); + + void pre_process(); + void execute(ProcessContext& context); + void post_process(); + +private: + enum ErrorType { + NO_ERROR, + PORT_NOT_FOUND, + NO_SPACE, + TYPE_MISMATCH + }; + + void apply(Context& context); + + bool _queued; + const Raul::Path _port_path; + const Raul::Atom _value; + PortImpl* _port; +}; + +} // namespace Server +} // namespace Ingen +} // namespace Events + +#endif // INGEN_EVENTS_SETPORTVALUE_HPP diff --git a/src/server/events/UnregisterClient.cpp b/src/server/events/UnregisterClient.cpp new file mode 100644 index 00000000..b87bfa81 --- /dev/null +++ b/src/server/events/UnregisterClient.cpp @@ -0,0 +1,48 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "ingen/ClientInterface.hpp" +#include "Request.hpp" +#include "UnregisterClient.hpp" +#include "Engine.hpp" +#include "ClientBroadcaster.hpp" + +using namespace Raul; + +namespace Ingen { +namespace Server { +namespace Events { + +UnregisterClient::UnregisterClient(Engine& engine, SharedPtr request, SampleCount timestamp, const URI& uri) + : QueuedEvent(engine, request, timestamp) + , _uri(uri) +{ +} + +void +UnregisterClient::post_process() +{ + if (_engine.broadcaster()->unregister_client(_uri)) + _request->respond_ok(); + else + _request->respond_error("Unable to unregister client"); +} + +} // namespace Server +} // namespace Ingen +} // namespace Events + diff --git a/src/server/events/UnregisterClient.hpp b/src/server/events/UnregisterClient.hpp new file mode 100644 index 00000000..eefc6318 --- /dev/null +++ b/src/server/events/UnregisterClient.hpp @@ -0,0 +1,50 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_EVENTS_UNREGISTERCLIENT_HPP +#define INGEN_EVENTS_UNREGISTERCLIENT_HPP + +#include "QueuedEvent.hpp" +#include "raul/URI.hpp" + +namespace Ingen { +namespace Server { +namespace Events { + +/** Unregisters an OSC client so it no longer receives notifications. + * + * \ingroup engine + */ +class UnregisterClient : public QueuedEvent +{ +public: + UnregisterClient(Engine& engine, + SharedPtr request, + SampleCount timestamp, + const Raul::URI& uri); + + void post_process(); + +private: + Raul::URI _uri; +}; + +} // namespace Server +} // namespace Ingen +} // namespace Events + +#endif // INGEN_EVENTS_UNREGISTERCLIENT_HPP diff --git a/src/server/ingen_engine.cpp b/src/server/ingen_engine.cpp new file mode 100644 index 00000000..568267cd --- /dev/null +++ b/src/server/ingen_engine.cpp @@ -0,0 +1,48 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "shared/Module.hpp" +#include "shared/World.hpp" +#include "Engine.hpp" +#include "QueuedEngineInterface.hpp" +#include "util.hpp" + +using namespace Ingen; + +struct IngenEngineModule : public Ingen::Shared::Module { + virtual void load(Ingen::Shared::World* world) { + Server::set_denormal_flags(); + SharedPtr engine(new Server::Engine(world)); + world->set_local_engine(engine); + SharedPtr interface( + new Server::QueuedEngineInterface(*engine.get(), + engine->event_queue_size())); + world->set_engine(interface); + engine->add_event_source(interface); + assert(world->local_engine() == engine); + } +}; + +extern "C" { + +Ingen::Shared::Module* +ingen_module_load() +{ + return new IngenEngineModule(); +} + +} // extern "C" diff --git a/src/server/ingen_http.cpp b/src/server/ingen_http.cpp new file mode 100644 index 00000000..d205b175 --- /dev/null +++ b/src/server/ingen_http.cpp @@ -0,0 +1,45 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "shared/Module.hpp" +#include "shared/World.hpp" +#include "HTTPEngineReceiver.hpp" +#include "Engine.hpp" + +using namespace std; +using namespace Ingen; + +struct IngenHTTPModule : public Ingen::Shared::Module { + void load(Ingen::Shared::World* world) { + Server::Engine* engine = (Server::Engine*)world->local_engine().get(); + SharedPtr interface( + new Server::HTTPEngineReceiver( + *engine, + world->conf()->option("engine-port").get_int32())); + engine->add_event_source(interface); + } +}; + +extern "C" { + +Ingen::Shared::Module* +ingen_module_load() +{ + return new IngenHTTPModule(); +} + +} // extern "C" diff --git a/src/server/ingen_jack.cpp b/src/server/ingen_jack.cpp new file mode 100644 index 00000000..ff760f1c --- /dev/null +++ b/src/server/ingen_jack.cpp @@ -0,0 +1,45 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "shared/Module.hpp" +#include "shared/World.hpp" +#include "JackDriver.hpp" +#include "Engine.hpp" + +using namespace std; +using namespace Ingen; + +struct IngenJackModule : public Ingen::Shared::Module { + void load(Ingen::Shared::World* world) { + Server::JackDriver* driver = new Server::JackDriver( + *(Server::Engine*)world->local_engine().get()); + driver->attach(world->conf()->option("jack-server").get_string(), + world->conf()->option("jack-client").get_string(), NULL); + ((Server::Engine*)world->local_engine().get())->set_driver( + SharedPtr(driver)); + } +}; + +extern "C" { + +Ingen::Shared::Module* +ingen_module_load() +{ + return new IngenJackModule(); +} + +} // extern "C" diff --git a/src/server/ingen_lv2.cpp b/src/server/ingen_lv2.cpp new file mode 100644 index 00000000..bd64cf34 --- /dev/null +++ b/src/server/ingen_lv2.cpp @@ -0,0 +1,422 @@ +/* Ingen.LV2 - A thin wrapper which allows Ingen to run as an LV2 plugin. + * Copyright 2008-2011 David Robillard + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the 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 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 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. + */ + +#include + +#include +#include + +#include +#include +#include + +#include "lv2/lv2plug.in/ns/lv2core/lv2.h" + +#include "raul/SharedPtr.hpp" +#include "raul/Thread.hpp" +#include "raul/log.hpp" + +#include "server/AudioBuffer.hpp" +#include "server/Driver.hpp" +#include "server/Engine.hpp" +#include "server/PatchImpl.hpp" +#include "server/PostProcessor.hpp" +#include "server/ProcessContext.hpp" +#include "server/QueuedEngineInterface.hpp" +#include "server/ThreadManager.hpp" +#include "ingen/ServerInterface.hpp" +#include "shared/World.hpp" +#include "serialisation/Parser.hpp" +#include "shared/Configuration.hpp" +#include "shared/runtime_paths.hpp" + +#include "ingen-config.h" + +/** Record of a patch in this Ingen LV2 bundle */ +struct LV2Patch { + LV2Patch(const std::string& u, const std::string& f); + + const std::string uri; + const std::string filename; + LV2_Descriptor descriptor; +}; + +class Lib { +public: + Lib(); + ~Lib(); + + typedef std::vector< SharedPtr > Patches; + + Patches patches; + Ingen::Shared::Configuration conf; + int argc; + char** argv; +}; + +/** Library state (constructed/destructed on library load/unload) */ +Lib lib; + +namespace Ingen { +namespace Server { + +class LV2Driver; + +class LV2Port : public DriverPort +{ +public: + LV2Port(LV2Driver* driver, DuplexPort* patch_port) + : DriverPort(patch_port) + , _driver(driver) + , _buffer(NULL) + {} + + // TODO: LV2 dynamic ports + void create() {} + void destroy() {} + void move(const Raul::Path& path) {} + + void pre_process(ProcessContext& context) { + if (!is_input() || !_buffer) + return; + + if (_patch_port->buffer_type() == PortType::AUDIO) { + AudioBuffer* patch_buf = (AudioBuffer*)_patch_port->buffer(0).get(); + patch_buf->copy((Sample*)_buffer, 0, context.nframes() - 1); + } else if (_patch_port->buffer_type() == PortType::EVENTS) { + //Raul::warn << "TODO: LV2 event I/O" << std::endl; + } + } + + void post_process(ProcessContext& context) { + if (is_input() || !_buffer) + return; + + if (_patch_port->buffer_type() == PortType::AUDIO) { + AudioBuffer* patch_buf = (AudioBuffer*)_patch_port->buffer(0).get(); + memcpy((Sample*)_buffer, patch_buf->data(), context.nframes() * sizeof(Sample)); + } else if (_patch_port->buffer_type() == PortType::EVENTS) { + //Raul::warn << "TODO: LV2 event I/O" << std::endl; + } + } + + void* buffer() const { return _buffer; } + void set_buffer(void* buf) { _buffer = buf; } + +private: + LV2Driver* _driver; + void* _buffer; +}; + +class LV2Driver : public Ingen::Server::Driver { +private: + typedef std::vector Ports; + +public: + LV2Driver(Engine& engine, SampleCount buffer_size, SampleCount sample_rate) + : _context(engine) + , _root_patch(NULL) + , _buffer_size(buffer_size) + , _sample_rate(sample_rate) + , _frame_time(0) + {} + + void run(uint32_t nframes) { + _context.locate(_frame_time, nframes, 0); + + for (Ports::iterator i = _ports.begin(); i != _ports.end(); ++i) + (*i)->pre_process(_context); + + _context.engine().process_events(_context); + + if (_root_patch) + _root_patch->process(_context); + + for (Ports::iterator i = _ports.begin(); i != _ports.end(); ++i) + (*i)->post_process(_context); + + _frame_time += nframes; + } + + virtual void set_root_patch(PatchImpl* patch) { _root_patch = patch; } + virtual PatchImpl* root_patch() { return _root_patch; } + + virtual void add_port(DriverPort* port) { + // Note this doesn't have to be realtime safe since there's no dynamic LV2 ports + ThreadManager::assert_thread(THREAD_PROCESS); + assert(dynamic_cast(port)); + assert(port->patch_port()->index() == _ports.size()); + _ports.push_back((LV2Port*)port); + } + + virtual Raul::Deletable* remove_port(const Raul::Path& path, + DriverPort** port=NULL) { + // Note this doesn't have to be realtime safe since there's no dynamic LV2 ports + ThreadManager::assert_thread(THREAD_PROCESS); + + for (Ports::iterator i = _ports.begin(); i != _ports.end(); ++i) { + if ((*i)->patch_port()->path() == path) { + _ports.erase(i); + return NULL; + } + } + + Raul::warn << "Unable to find port " << path << std::endl; + return NULL; + } + + virtual bool supports(PortType port_type, EventType event_type) { + return true; + } + + virtual DriverPort* create_port(DuplexPort* patch_port) { + return new LV2Port(this, patch_port); + } + + virtual DriverPort* driver_port(const Raul::Path& path) { + ThreadManager::assert_thread(THREAD_PROCESS); + + for (Ports::iterator i = _ports.begin(); i != _ports.end(); ++i) + if ((*i)->patch_port()->path() == path) + return (*i); + + return NULL; + } + + virtual SampleCount block_length() const { return _buffer_size; } + virtual SampleCount sample_rate() const { return _sample_rate; } + virtual SampleCount frame_time() const { return _frame_time;} + + virtual bool is_realtime() const { return true; } + virtual ProcessContext& context() { return _context; } + + Ports& ports() { return _ports; } + +private: + ProcessContext _context; + PatchImpl* _root_patch; + SampleCount _buffer_size; + SampleCount _sample_rate; + SampleCount _frame_time; + Ports _ports; +}; + +} // namespace Server +} // namespace Ingen + +extern "C" { + +using namespace Ingen; +using namespace Ingen::Server; + +/** LV2 plugin library entry point */ +LV2_SYMBOL_EXPORT +const LV2_Descriptor* +lv2_descriptor(uint32_t index) +{ + return index < lib.patches.size() ? &lib.patches[index]->descriptor : NULL; +} + +struct IngenPlugin { + Ingen::Shared::World* world; +}; + +static LV2_Handle +ingen_instantiate(const LV2_Descriptor* descriptor, + double rate, + const char* bundle_path, + const LV2_Feature*const* features) +{ + Shared::set_bundle_path(bundle_path); + + const LV2Patch* patch = NULL; + for (Lib::Patches::iterator i = lib.patches.begin(); i != lib.patches.end(); ++i) { + if (&(*i)->descriptor == descriptor) { + patch = (*i).get(); + break; + } + } + + if (!patch) { + Raul::error << "Could not find patch " << descriptor->URI << std::endl; + return NULL; + } + + IngenPlugin* plugin = (IngenPlugin*)malloc(sizeof(IngenPlugin)); + plugin->world = new Ingen::Shared::World(&lib.conf, lib.argc, lib.argv); + if (!plugin->world->load_module("serialisation")) { + delete plugin->world; + return NULL; + } + + SharedPtr engine(new Server::Engine(plugin->world)); + plugin->world->set_local_engine(engine); + + SharedPtr interface( + new Server::QueuedEngineInterface( + *engine.get(), + engine->event_queue_size())); + + plugin->world->set_engine(interface); + engine->add_event_source(interface); + + Raul::Thread::get().set_context(Server::THREAD_PRE_PROCESS); + Server::ThreadManager::single_threaded = true; + + // FIXME: fixed (or at least maximum) buffer size + LV2Driver* driver = new LV2Driver(*engine.get(), rate, 4096); + engine->set_driver(SharedPtr(driver)); + + engine->activate(); + Server::ThreadManager::single_threaded = true; + + ProcessContext context(*engine.get()); + context.locate(0, UINT_MAX, 0); + + engine->post_processor()->set_end_time(UINT_MAX); + + // TODO: Load only necessary plugins + plugin->world->engine()->get("ingen:plugins"); + interface->process(*engine->post_processor(), context, false); + engine->post_processor()->process(); + + plugin->world->parser()->parse_file(plugin->world, + plugin->world->engine().get(), + patch->filename); + + while (!interface->empty()) { + interface->process(*engine->post_processor(), context, false); + engine->post_processor()->process(); + } + + engine->deactivate(); + + return (LV2_Handle)plugin; +} + +static void +ingen_connect_port(LV2_Handle instance, uint32_t port, void* data) +{ + using namespace Ingen::Server; + + IngenPlugin* me = (IngenPlugin*)instance; + Server::Engine* engine = (Server::Engine*)me->world->local_engine().get(); + LV2Driver* driver = (LV2Driver*)engine->driver(); + if (port < driver->ports().size()) { + driver->ports().at(port)->set_buffer(data); + assert(driver->ports().at(port)->patch_port()->index() == port); + } else { + Raul::warn << "Connect to non-existent port " << port << std::endl; + } +} + +static void +ingen_activate(LV2_Handle instance) +{ + IngenPlugin* me = (IngenPlugin*)instance; + me->world->local_engine()->activate(); +} + +static void +ingen_run(LV2_Handle instance, uint32_t sample_count) +{ + IngenPlugin* me = (IngenPlugin*)instance; + Server::Engine* engine = (Server::Engine*)me->world->local_engine().get(); + // FIXME: don't do this every call + Raul::Thread::get().set_context(Ingen::Server::THREAD_PROCESS); + ((LV2Driver*)engine->driver())->run(sample_count); +} + +static void +ingen_deactivate(LV2_Handle instance) +{ + IngenPlugin* me = (IngenPlugin*)instance; + me->world->local_engine()->deactivate(); +} + +static void +ingen_cleanup(LV2_Handle instance) +{ + IngenPlugin* me = (IngenPlugin*)instance; + me->world->set_local_engine(SharedPtr()); + me->world->set_engine(SharedPtr()); + delete me->world; + free(instance); +} + +static const void* +ingen_extension_data(const char* uri) +{ + return NULL; +} + +LV2Patch::LV2Patch(const std::string& u, const std::string& f) + : uri(u) + , filename(f) +{ + descriptor.URI = uri.c_str(); + descriptor.instantiate = ingen_instantiate; + descriptor.connect_port = ingen_connect_port; + descriptor.activate = ingen_activate; + descriptor.run = ingen_run; + descriptor.deactivate = ingen_deactivate; + descriptor.cleanup = ingen_cleanup; + descriptor.extension_data = ingen_extension_data; +} + +/** Library constructor (called on shared library load) */ +Lib::Lib() + : argc(0) + , argv(NULL) +{ + if (!Glib::thread_supported()) + Glib::thread_init(); + + using namespace Ingen; + + Ingen::Shared::set_bundle_path_from_code((void*)&lv2_descriptor); + + Ingen::Shared::World* world = new Ingen::Shared::World(&conf, argc, argv); + if (!world->load_module("serialisation")) { + delete world; + return; + } + + assert(world->parser()); + + typedef Serialisation::Parser::PatchRecords Records; + + Records records(world->parser()->find_patches( + world, Glib::filename_to_uri( + Shared::bundle_file_path("manifest.ttl")))); + + for (Records::iterator i = records.begin(); i != records.end(); ++i) { + patches.push_back( + SharedPtr(new LV2Patch(i->patch_uri.str(), + i->file_uri))); + } + + delete world; +} + +/** Library destructor (called on shared library unload) */ +Lib::~Lib() +{ +} + +} // extern "C" diff --git a/src/server/ingen_osc.cpp b/src/server/ingen_osc.cpp new file mode 100644 index 00000000..bf0f09f8 --- /dev/null +++ b/src/server/ingen_osc.cpp @@ -0,0 +1,46 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "shared/Module.hpp" +#include "shared/World.hpp" +#include "OSCEngineReceiver.hpp" +#include "Engine.hpp" + +using namespace std; +using namespace Ingen; + +struct IngenOSCModule : public Ingen::Shared::Module { + void load(Ingen::Shared::World* world) { + Server::Engine* engine = (Server::Engine*)world->local_engine().get(); + SharedPtr interface( + new Server::OSCEngineReceiver( + *engine, + engine->event_queue_size(), + world->conf()->option("engine-port").get_int32())); + engine->add_event_source(interface); + } +}; + +extern "C" { + +Ingen::Shared::Module* +ingen_module_load() +{ + return new IngenOSCModule(); +} + +} // extern "C" diff --git a/src/server/internals/Controller.cpp b/src/server/internals/Controller.cpp new file mode 100644 index 00000000..3d477de6 --- /dev/null +++ b/src/server/internals/Controller.cpp @@ -0,0 +1,147 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "raul/midi_events.h" +#include "shared/LV2URIMap.hpp" +#include "internals/Controller.hpp" +#include "PostProcessor.hpp" +#include "events/SendPortValue.hpp" +#include "InputPort.hpp" +#include "OutputPort.hpp" +#include "InternalPlugin.hpp" +#include "AudioBuffer.hpp" +#include "ProcessContext.hpp" +#include "EventBuffer.hpp" +#include "util.hpp" + +using namespace std; + +namespace Ingen { +namespace Server { +namespace Internals { + +InternalPlugin* ControllerNode::internal_plugin(Shared::LV2URIMap& uris) { + return new InternalPlugin(uris, NS_INTERNALS "Controller", "controller"); +} + +ControllerNode::ControllerNode(InternalPlugin* plugin, + BufferFactory& bufs, + const string& path, + bool polyphonic, + PatchImpl* parent, + SampleRate srate) + : NodeImpl(plugin, path, false, parent, srate) + , _learning(false) +{ + const Ingen::Shared::LV2URIMap& uris = bufs.uris(); + _ports = new Raul::Array(6); + + _midi_in_port = new InputPort(bufs, this, "input", 0, 1, PortType::EVENTS, Raul::Atom()); + _midi_in_port->set_property(uris.lv2_name, "Input"); + _ports->at(0) = _midi_in_port; + + _param_port = new InputPort(bufs, this, "controller", 1, 1, PortType::CONTROL, 0.0f); + _param_port->set_property(uris.lv2_minimum, 0.0f); + _param_port->set_property(uris.lv2_maximum, 127.0f); + _param_port->set_property(uris.lv2_integer, true); + _param_port->set_property(uris.lv2_name, "Controller"); + _ports->at(1) = _param_port; + + _log_port = new InputPort(bufs, this, "logarithmic", 2, 1, PortType::CONTROL, 0.0f); + _log_port->set_property(uris.lv2_portProperty, uris.lv2_toggled); + _log_port->set_property(uris.lv2_name, "Logarithmic"); + _ports->at(2) = _log_port; + + _min_port = new InputPort(bufs, this, "minimum", 3, 1, PortType::CONTROL, 0.0f); + _min_port->set_property(uris.lv2_name, "Minimum"); + _ports->at(3) = _min_port; + + _max_port = new InputPort(bufs, this, "maximum", 4, 1, PortType::CONTROL, 1.0f); + _max_port->set_property(uris.lv2_name, "Maximum"); + _ports->at(4) = _max_port; + + _audio_port = new OutputPort(bufs, this, "ar_output", 5, 1, PortType::AUDIO, 0.0f); + _audio_port->set_property(uris.lv2_name, "Output"); + _ports->at(5) = _audio_port; +} + +void +ControllerNode::process(ProcessContext& context) +{ + NodeImpl::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).get(); + + midi_in->rewind(); + + 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(); + } + + NodeImpl::post_process(context); +} + +void +ControllerNode::control(ProcessContext& context, uint8_t control_num, uint8_t val, FrameTime time) +{ + Sample scaled_value; + + const Sample nval = (val / 127.0f); // normalized [0, 1] + + if (_learning) { + _param_port->set_value(control_num); + ((AudioBuffer*)_param_port->buffer(0).get())->set_value( + (float)control_num, context.start(), context.end()); + _param_port->broadcast_value(context, true); + _learning = false; + } + + const Sample min_port_val = ((AudioBuffer*)_min_port->buffer(0).get())->value_at(0); + const Sample max_port_val = ((AudioBuffer*)_max_port->buffer(0).get())->value_at(0); + const Sample log_port_val = ((AudioBuffer*)_log_port->buffer(0).get())->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).get())->value_at(0)) + ((AudioBuffer*)_audio_port->buffer(0).get())->set_value(scaled_value, context.start(), time); +} + +} // namespace Internals +} // namespace Server +} // namespace Ingen + diff --git a/src/server/internals/Controller.hpp b/src/server/internals/Controller.hpp new file mode 100644 index 00000000..30ef663f --- /dev/null +++ b/src/server/internals/Controller.hpp @@ -0,0 +1,74 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_INTERNALS_CONTROLLER_HPP +#define INGEN_INTERNALS_CONTROLLER_HPP + +#include +#include "NodeImpl.hpp" + +namespace Ingen { +namespace Server { + +class InputPort; +class OutputPort; +class InternalPlugin; + +namespace Internals { + +/** 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 ControllerNode : public NodeImpl +{ +public: + ControllerNode( + InternalPlugin* plugin, + BufferFactory& bufs, + const std::string& path, + bool polyphonic, + PatchImpl* parent, + SampleRate srate); + + void process(ProcessContext& context); + + void control(ProcessContext& context, uint8_t control_num, uint8_t val, FrameTime time); + + void learn() { _learning = true; } + + static InternalPlugin* internal_plugin(Shared::LV2URIMap& uris); + +private: + bool _learning; + + InputPort* _midi_in_port; + InputPort* _param_port; + InputPort* _log_port; + InputPort* _min_port; + InputPort* _max_port; + OutputPort* _audio_port; +}; + +} // namespace Server +} // namespace Ingen +} // namespace Internals + +#endif // INGEN_INTERNALS_CONTROLLER_HPP diff --git a/src/server/internals/Delay.cpp b/src/server/internals/Delay.cpp new file mode 100644 index 00000000..5c2758da --- /dev/null +++ b/src/server/internals/Delay.cpp @@ -0,0 +1,208 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "raul/log.hpp" +#include "raul/Array.hpp" +#include "raul/Maid.hpp" +#include "raul/midi_events.h" +#include "shared/LV2URIMap.hpp" +#include "internals/Delay.hpp" +#include "AudioBuffer.hpp" +#include "Driver.hpp" +#include "EventBuffer.hpp" +#include "InputPort.hpp" +#include "InternalPlugin.hpp" +#include "OutputPort.hpp" +#include "PatchImpl.hpp" +#include "ProcessContext.hpp" +#include "ingen-config.h" +#include "util.hpp" + +#define LOG(s) s << "[DelayNode] " + +#define CALC_DELAY(delaytime) \ + (f_clamp (delaytime * (float)sample_rate, 1.0f, (float)(buffer_mask + 1))) + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { +namespace Internals { + +static const float MAX_DELAY_SECONDS = 8.0f; + +InternalPlugin* DelayNode::internal_plugin(Shared::LV2URIMap& uris) { + return new InternalPlugin(uris, NS_INTERNALS "Delay", "delay"); +} + +DelayNode::DelayNode( + InternalPlugin* plugin, + BufferFactory& bufs, + const std::string& path, + bool polyphonic, + PatchImpl* parent, + SampleRate srate) + : NodeImpl(plugin, path, polyphonic, parent, srate) + , _buffer(0) + , _buffer_length(0) + , _buffer_mask(0) + , _write_phase(0) +{ + const Ingen::Shared::LV2URIMap& uris = bufs.uris(); + _ports = new Raul::Array(3); + + const float default_delay = 1.0f; + _last_delay_time = default_delay; + _delay_samples = default_delay; + + _delay_port = new InputPort(bufs, this, "delay", 1, _polyphony, PortType::CONTROL, default_delay); + _delay_port->set_property(uris.lv2_name, "Delay"); + _delay_port->set_property(uris.lv2_default, default_delay); + _delay_port->set_property(uris.lv2_minimum, (float)(1.0/(double)srate)); + _delay_port->set_property(uris.lv2_maximum, MAX_DELAY_SECONDS); + _ports->at(0) = _delay_port; + + _in_port = new InputPort(bufs, this, "in", 0, 1, PortType::AUDIO, 0.0f); + _in_port->set_property(uris.lv2_name, "Input"); + _ports->at(1) = _in_port; + + _out_port = new OutputPort(bufs, this, "out", 0, 1, PortType::AUDIO, 0.0f); + _out_port->set_property(uris.lv2_name, "Output"); + _ports->at(2) = _out_port; + + //_buffer = bufs.get(PortType::AUDIO, bufs.audio_buffer_size(buffer_length_frames), true); + +} + +DelayNode::~DelayNode() +{ + //_buffer.reset(); + free(_buffer); +} + +void +DelayNode::activate(BufferFactory& bufs) +{ + NodeImpl::activate(bufs); + const SampleCount min_size = MAX_DELAY_SECONDS * _srate; + + // Smallest power of two larger than min_size + SampleCount size = 1; + while (size < min_size) + size <<= 1; + + _buffer = (float*)calloc(size, sizeof(float)); + _buffer_mask = size - 1; + _buffer_length = size; + //_buffer->clear(); + _write_phase = 0; +} + +static inline float f_clamp(float x, float a, float b) +{ + const float x1 = fabs(x - a); + const float x2 = fabs(x - b); + + x = x1 + a + b; + x -= x2; + x *= 0.5; + + return x; +} + +static inline float cube_interp(const float fr, const float inm1, const float + in, const float inp1, const float inp2) +{ + return in + 0.5f * fr * (inp1 - inm1 + + fr * (4.0f * inp1 + 2.0f * inm1 - 5.0f * in - inp2 + + fr * (3.0f * (in - inp1) - inm1 + inp2))); +} + +void +DelayNode::process(ProcessContext& context) +{ + AudioBuffer* const delay_buf = (AudioBuffer*)_delay_port->buffer(0).get(); + AudioBuffer* const in_buf = (AudioBuffer*)_in_port->buffer(0).get(); + AudioBuffer* const out_buf = (AudioBuffer*)_out_port->buffer(0).get(); + + NodeImpl::pre_process(context); + + DelayNode* plugin_data = this; + + const float* const in = in_buf->data(); + float* const out = out_buf->data(); + const float delay_time = delay_buf->data()[0]; + const uint32_t buffer_mask = plugin_data->_buffer_mask; + const unsigned int sample_rate = plugin_data->_srate; + float delay_samples = plugin_data->_delay_samples; + long write_phase = plugin_data->_write_phase; + const uint32_t sample_count = context.nframes(); + + if (write_phase == 0) { + _last_delay_time = delay_time; + _delay_samples = delay_samples = CALC_DELAY(delay_time); + } + + if (delay_time == _last_delay_time) { + const long idelay_samples = (long)delay_samples; + const float frac = delay_samples - idelay_samples; + + for (uint32_t i = 0; i < sample_count; i++) { + long read_phase = write_phase - (long)delay_samples; + const float read = cube_interp(frac, + buffer_at(read_phase - 1), + buffer_at(read_phase), + buffer_at(read_phase + 1), + buffer_at(read_phase + 2)); + buffer_at(write_phase++) = in[i]; + out[i] = read; + } + } else { + const float next_delay_samples = CALC_DELAY(delay_time); + const float delay_samples_slope = (next_delay_samples - delay_samples) / sample_count; + + for (uint32_t i = 0; i < sample_count; i++) { + delay_samples += delay_samples_slope; + write_phase++; + const long read_phase = write_phase - (long)delay_samples; + const long idelay_samples = (long)delay_samples; + const float frac = delay_samples - idelay_samples; + const float read = cube_interp(frac, + buffer_at(read_phase - 1), + buffer_at(read_phase), + buffer_at(read_phase + 1), + buffer_at(read_phase + 2)); + buffer_at(write_phase) = in[i]; + out[i] = read; + } + + _last_delay_time = delay_time; + _delay_samples = delay_samples; + } + + _write_phase = write_phase; + + NodeImpl::post_process(context); +} + +} // namespace Internals +} // namespace Server +} // namespace Ingen + diff --git a/src/server/internals/Delay.hpp b/src/server/internals/Delay.hpp new file mode 100644 index 00000000..7456d7cc --- /dev/null +++ b/src/server/internals/Delay.hpp @@ -0,0 +1,78 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_INTERNALS_DELAY_HPP +#define INGEN_INTERNALS_DELAY_HPP + +#include +#include +#include "types.hpp" +#include "NodeImpl.hpp" + +namespace Ingen { +namespace Server { + +class InputPort; +class OutputPort; +class InternalPlugin; +class BufferFactory; + +namespace Internals { + +class DelayNode : public NodeImpl +{ +public: + DelayNode( + InternalPlugin* plugin, + BufferFactory& bufs, + const std::string& path, + bool polyphonic, + PatchImpl* parent, + SampleRate srate); + + ~DelayNode(); + + void activate(BufferFactory& bufs); + + void process(ProcessContext& context); + + static InternalPlugin* internal_plugin(Shared::LV2URIMap& uris); + + float delay_samples() const { return _delay_samples; } + +private: + inline float& buffer_at(long phase) const { return _buffer[phase & _buffer_mask]; } + + InputPort* _delay_port; + InputPort* _in_port; + OutputPort* _out_port; + + typedef long Phase; + + float* _buffer; + uint32_t _buffer_length; + uint32_t _buffer_mask; + Phase _write_phase; + float _last_delay_time; + float _delay_samples; +}; + +} // namespace Server +} // namespace Ingen +} // namespace Internals + +#endif // INGEN_INTERNALS_DELAY_HPP diff --git a/src/server/internals/Note.cpp b/src/server/internals/Note.cpp new file mode 100644 index 00000000..f28dacc1 --- /dev/null +++ b/src/server/internals/Note.cpp @@ -0,0 +1,416 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "raul/log.hpp" +#include "raul/Array.hpp" +#include "raul/Maid.hpp" +#include "raul/midi_events.h" +#include "shared/LV2URIMap.hpp" +#include "internals/Note.hpp" +#include "AudioBuffer.hpp" +#include "Driver.hpp" +#include "EventBuffer.hpp" +#include "InputPort.hpp" +#include "InternalPlugin.hpp" +#include "OutputPort.hpp" +#include "PatchImpl.hpp" +#include "ProcessContext.hpp" +#include "util.hpp" +#include "ingen-config.h" + +#define LOG(s) s << "[NoteNode] " + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { +namespace Internals { + +InternalPlugin* NoteNode::internal_plugin(Shared::LV2URIMap& uris) { + return new InternalPlugin(uris, NS_INTERNALS "Note", "note"); +} + +NoteNode::NoteNode( + InternalPlugin* plugin, + BufferFactory& bufs, + const std::string& path, + bool polyphonic, + PatchImpl* parent, + SampleRate srate) + : NodeImpl(plugin, path, polyphonic, parent, srate) + , _voices(new Raul::Array(_polyphony)) + , _prepared_voices(NULL) + , _sustain(false) +{ + const Ingen::Shared::LV2URIMap& uris = bufs.uris(); + _ports = new Raul::Array(5); + + _midi_in_port = new InputPort(bufs, this, "input", 0, 1, PortType::EVENTS, Raul::Atom()); + _midi_in_port->set_property(uris.lv2_name, "Input"); + _ports->at(0) = _midi_in_port; + + _freq_port = new OutputPort(bufs, this, "frequency", 1, _polyphony, PortType::AUDIO, 440.0f); + _freq_port->set_property(uris.lv2_name, "Frequency"); + _ports->at(1) = _freq_port; + + _vel_port = new OutputPort(bufs, this, "velocity", 2, _polyphony, PortType::AUDIO, 0.0f); + _vel_port->set_property(uris.lv2_minimum, 0.0f); + _vel_port->set_property(uris.lv2_maximum, 1.0f); + _vel_port->set_property(uris.lv2_name, "Velocity"); + _ports->at(2) = _vel_port; + + _gate_port = new OutputPort(bufs, this, "gate", 3, _polyphony, PortType::AUDIO, 0.0f); + _gate_port->set_property(uris.lv2_portProperty, uris.lv2_toggled); + _gate_port->set_property(uris.lv2_name, "Gate"); + _ports->at(3) = _gate_port; + + _trig_port = new OutputPort(bufs, this, "trigger", 4, _polyphony, PortType::AUDIO, 0.0f); + _trig_port->set_property(uris.lv2_portProperty, uris.lv2_toggled); + _trig_port->set_property(uris.lv2_name, "Trigger"); + _ports->at(4) = _trig_port; +} + +NoteNode::~NoteNode() +{ + delete _voices; +} + +bool +NoteNode::prepare_poly(BufferFactory& bufs, uint32_t poly) +{ + if (!_polyphonic) + return true; + + NodeImpl::prepare_poly(bufs, poly); + + if (_prepared_voices && poly <= _prepared_voices->size()) + return true; + + _prepared_voices = new Raul::Array(poly, *_voices, Voice()); + + return true; +} + +bool +NoteNode::apply_poly(Raul::Maid& maid, uint32_t poly) +{ + if (!NodeImpl::apply_poly(maid, poly)) + return false; + + if (_prepared_voices) { + assert(_polyphony <= _prepared_voices->size()); + maid.push(_voices); + _voices = _prepared_voices; + _prepared_voices = NULL; + } + assert(_polyphony <= _voices->size()); + + return true; +} + +void +NoteNode::process(ProcessContext& context) +{ + EventBuffer* const midi_in = (EventBuffer*)_midi_in_port->buffer(0).get(); + NodeImpl::pre_process(context); + + uint32_t frames = 0; + uint32_t subframes = 0; + uint16_t type = 0; + uint16_t size = 0; + uint8_t* buf = NULL; + + midi_in->rewind(); + + if (midi_in->event_count() > 0) + for (midi_in->rewind(); midi_in->get_event(&frames, &subframes, &type, &size, &buf); + midi_in->increment()) { + +#ifdef RAUL_LOG_DEBUG + LOG(debug) << "EVENT TYPE " << type << " @ " << frames << "." << subframes << ": "; + for (uint16_t i = 0; i < size; ++i) + debug << (int)((char)buf[i]) << " "; + debug << endl; +#endif + + if (frames < context.offset()) + continue; + if (frames > context.nframes()) + break; + + 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: + //warn << "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]); + } + } + + NodeImpl::post_process(context); +} + +void +NoteNode::note_on(ProcessContext& context, uint8_t note_num, uint8_t velocity, FrameTime time) +{ + assert(time >= context.start() && time <= context.end()); + assert(note_num <= 127); + + Key* key = &_keys[note_num]; + Voice* voice = NULL; + uint32_t voice_num = 0; + + if (key->state != Key::OFF) { +#ifdef RAUL_LOG_DEBUG + LOG(debug) << "Double midi note received" << endl; +#endif + 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]; + FrameTime 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]); + +#ifdef RAUL_LOG_DEBUG + LOG(debug) << "Note " << (int)note_num << " on @ " << time + << ". Voice " << voice_num << " / " << _polyphony << endl; +#endif + + // 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; +#ifdef RAUL_LOG_DEBUG + LOG(debug) << "Stole voice " << voice_num << endl; +#endif + } + + // 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).get())->set_value( + note_to_freq(note_num), context.start(), time); + ((AudioBuffer*)_vel_port->buffer(voice_num).get())->set_value( + velocity/127.0, context.start(), time); + ((AudioBuffer*)_gate_port->buffer(voice_num).get())->set_value( + 1.0f, context.start(), time); + + // trigger (one sample) + ((AudioBuffer*)_trig_port->buffer(voice_num).get())->set_value( + 1.0f, context.start(), time); + ((AudioBuffer*)_trig_port->buffer(voice_num).get())->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 +NoteNode::note_off(ProcessContext& context, uint8_t note_num, FrameTime time) +{ + assert(time >= context.start() && time <= context.end()); + + Key* key = &_keys[note_num]; + +#ifdef RAUL_LOG_DEBUG + debug << "Note " << (int)note_num << " off @ " << time << endl; +#endif + + 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) { +#ifdef RAUL_LOG_DEBUG + debug << "Free voice " << key->voice << endl; +#endif + free_voice(context, key->voice, time); + } else { +#ifdef RAUL_LOG_DEBUG + debug << "Hold voice " << key->voice << endl; +#endif + (*_voices)[key->voice].state = Voice::HOLDING; + } + + } else { +#ifdef RAUL_LOG_DEBUG + debug << "WARNING: Assigned key, but voice not active" << endl; +#endif + } + } + + key->state = Key::OFF; +} + +void +NoteNode::free_voice(ProcessContext& context, uint32_t voice, FrameTime time) +{ + assert(time >= context.start() && time <= context.end()); + + // Find a key to reassign to the freed voice (the newest, if there is one) + Key* replace_key = NULL; + uint8_t replace_key_num = 0; + + for (uint8_t 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).get())->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) +#ifdef RAUL_LOG_DEBUG + LOG(debug) << "Note off: key " << (*_voices)[voice].note << " voice " << voice << endl; +#endif + ((AudioBuffer*)_gate_port->buffer(voice).get())->set_value(0.0f, context.start(), time); + (*_voices)[voice].state = Voice::FREE; + } +} + +void +NoteNode::all_notes_off(ProcessContext& context, FrameTime time) +{ + assert(time >= context.start() && time <= context.end()); + +#ifdef RAUL_LOG_DEBUG + LOG(debug) << "All notes off @ " << time << endl; +#endif + + // FIXME: set all keys to Key::OFF? + + for (uint32_t i = 0; i < _polyphony; ++i) { + ((AudioBuffer*)_gate_port->buffer(i).get())->set_value(0.0f, context.start(), time); + (*_voices)[i].state = Voice::FREE; + } +} + +float +NoteNode::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; // Frequency of zero causes numerical problems... +} + +void +NoteNode::sustain_on(ProcessContext& context, FrameTime time) +{ + _sustain = true; +} + +void +NoteNode::sustain_off(ProcessContext& context, FrameTime time) +{ + assert(time >= context.start() && time <= context.end()); + + _sustain = false; + + for (uint32_t i=0; i < _polyphony; ++i) + if ((*_voices)[i].state == Voice::HOLDING) + free_voice(context, i, time); +} + +} // namespace Internals +} // namespace Server +} // namespace Ingen + diff --git a/src/server/internals/Note.hpp b/src/server/internals/Note.hpp new file mode 100644 index 00000000..d20baf13 --- /dev/null +++ b/src/server/internals/Note.hpp @@ -0,0 +1,101 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_INTERNALS_NOTE_HPP +#define INGEN_INTERNALS_NOTE_HPP + +#include +#include "types.hpp" +#include "NodeImpl.hpp" + +namespace Ingen { +namespace Server { + +class InputPort; +class OutputPort; +class InternalPlugin; + +namespace Internals { + +/** MIDI note input node. + * + * For pitched instruments like keyboard, etc. + * + * \ingroup engine + */ +class NoteNode : public NodeImpl +{ +public: + NoteNode( + InternalPlugin* plugin, + BufferFactory& bufs, + const std::string& path, + bool polyphonic, + PatchImpl* parent, + SampleRate srate); + + ~NoteNode(); + + bool prepare_poly(BufferFactory& bufs, uint32_t poly); + bool apply_poly(Raul::Maid& maid, uint32_t poly); + + void process(ProcessContext& context); + + void note_on(ProcessContext& context, uint8_t note_num, uint8_t velocity, FrameTime time); + void note_off(ProcessContext& context, uint8_t 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); + + static InternalPlugin* internal_plugin(Shared::LV2URIMap& uris); + +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), time(0) {} + State state; uint8_t 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 Server +} // namespace Ingen +} // namespace Internals + +#endif // INGEN_INTERNALS_NOTE_HPP diff --git a/src/server/internals/Trigger.cpp b/src/server/internals/Trigger.cpp new file mode 100644 index 00000000..d062fa13 --- /dev/null +++ b/src/server/internals/Trigger.cpp @@ -0,0 +1,168 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public 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 "raul/log.hpp" +#include "raul/midi_events.h" +#include "shared/LV2URIMap.hpp" +#include "internals/Trigger.hpp" +#include "AudioBuffer.hpp" +#include "EventBuffer.hpp" +#include "InputPort.hpp" +#include "InternalPlugin.hpp" +#include "OutputPort.hpp" +#include "ProcessContext.hpp" +#include "util.hpp" +#include "ingen-config.h" + +#define LOG(s) s << "[TriggerNode] " + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Server { +namespace Internals { + +InternalPlugin* TriggerNode::internal_plugin(Shared::LV2URIMap& uris) { + return new InternalPlugin(uris, NS_INTERNALS "Trigger", "trigger"); +} + +TriggerNode::TriggerNode( + InternalPlugin* plugin, + BufferFactory& bufs, + const std::string& path, + bool polyphonic, + PatchImpl* parent, + SampleRate srate) + : NodeImpl(plugin, path, false, parent, srate) + , _learning(false) +{ + const Ingen::Shared::LV2URIMap& uris = bufs.uris(); + _ports = new Raul::Array(5); + + _midi_in_port = new InputPort(bufs, this, "input", 0, 1, PortType::EVENTS, Raul::Atom()); + _midi_in_port->set_property(uris.lv2_name, "Input"); + _ports->at(0) = _midi_in_port; + + _note_port = new InputPort(bufs, this, "note", 1, 1, PortType::CONTROL, 60.0f); + _note_port->set_property(uris.lv2_minimum, 0.0f); + _note_port->set_property(uris.lv2_maximum, 127.0f); + _note_port->set_property(uris.lv2_integer, true); + _note_port->set_property(uris.lv2_name, "Note"); + _ports->at(1) = _note_port; + + _gate_port = new OutputPort(bufs, this, "gate", 2, 1, PortType::AUDIO, 0.0f); + _gate_port->set_property(uris.lv2_portProperty, uris.lv2_toggled); + _gate_port->set_property(uris.lv2_name, "Gate"); + _ports->at(2) = _gate_port; + + _trig_port = new OutputPort(bufs, this, "trigger", 3, 1, PortType::AUDIO, 0.0f); + _trig_port->set_property(uris.lv2_portProperty, uris.lv2_toggled); + _trig_port->set_property(uris.lv2_name, "Trigger"); + _ports->at(3) = _trig_port; + + _vel_port = new OutputPort(bufs, this, "velocity", 4, 1, PortType::AUDIO, 0.0f); + _vel_port->set_property(uris.lv2_minimum, 0.0f); + _vel_port->set_property(uris.lv2_maximum, 1.0f); + _vel_port->set_property(uris.lv2_name, "Velocity"); + _ports->at(4) = _vel_port; +} + +void +TriggerNode::process(ProcessContext& context) +{ + NodeImpl::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).get(); + + midi_in->rewind(); + + 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).get())->set_value(0.0f, context.start(), time); + default: + break; + } + } + + midi_in->increment(); + } + + NodeImpl::post_process(context); +} + +void +TriggerNode::note_on(ProcessContext& context, uint8_t note_num, uint8_t velocity, FrameTime time) +{ + assert(time >= context.start() && time <= context.end()); + + if (_learning) { + _note_port->set_value(note_num); + ((AudioBuffer*)_note_port->buffer(0).get())->set_value( + (float)note_num, context.start(), context.end()); + _note_port->broadcast_value(context, true); + _learning = false; + } + +#ifdef RAUL_LOG_DEBUG + LOG(debug) << path() << " note " << (int)note_num << " on @ " << time << endl; +#endif + + Sample filter_note = ((AudioBuffer*)_note_port->buffer(0).get())->value_at(0); + if (filter_note >= 0.0 && filter_note < 127.0 && (note_num == (uint8_t)filter_note)) { + ((AudioBuffer*)_gate_port->buffer(0).get())->set_value(1.0f, context.start(), time); + ((AudioBuffer*)_trig_port->buffer(0).get())->set_value(1.0f, context.start(), time); + ((AudioBuffer*)_trig_port->buffer(0).get())->set_value(0.0f, context.start(), time + 1); + ((AudioBuffer*)_vel_port->buffer(0).get())->set_value(velocity / 127.0f, context.start(), time); + assert(((AudioBuffer*)_trig_port->buffer(0).get())->data()[time - context.start()] == 1.0f); + } +} + +void +TriggerNode::note_off(ProcessContext& context, uint8_t note_num, FrameTime time) +{ + assert(time >= context.start() && time <= context.end()); + + if (note_num == lrintf(((AudioBuffer*)_note_port->buffer(0).get())->value_at(0))) + ((AudioBuffer*)_gate_port->buffer(0).get())->set_value(0.0f, context.start(), time); +} + +} // namespace Internals +} // namespace Server +} // namespace Ingen diff --git a/src/server/internals/Trigger.hpp b/src/server/internals/Trigger.hpp new file mode 100644 index 00000000..92b85345 --- /dev/null +++ b/src/server/internals/Trigger.hpp @@ -0,0 +1,77 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_INTERNALS_TRIGGER_HPP +#define INGEN_INTERNALS_TRIGGER_HPP + +#include +#include "NodeImpl.hpp" + +namespace Ingen { +namespace Server { + +class InputPort; +class OutputPort; +class InternalPlugin; + +namespace Internals { + +/** 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 TriggerNode : public NodeImpl +{ +public: + TriggerNode( + InternalPlugin* plugin, + BufferFactory& bufs, + const std::string& path, + bool polyphonic, + PatchImpl* parent, + SampleRate srate); + + void process(ProcessContext& context); + + void note_on(ProcessContext& context, uint8_t note_num, uint8_t velocity, FrameTime time); + void note_off(ProcessContext& context, uint8_t note_num, FrameTime time); + + void learn() { _learning = true; } + + static InternalPlugin* internal_plugin(Shared::LV2URIMap& uris); + +private: + bool _learning; + + InputPort* _midi_in_port; + InputPort* _note_port; + OutputPort* _gate_port; + OutputPort* _trig_port; + OutputPort* _vel_port; +}; + +} // namespace Server +} // namespace Ingen +} // namespace Internals + +#endif // INGEN_INTERNALS_TRIGGER_HPP diff --git a/src/server/mix.hpp b/src/server/mix.hpp new file mode 100644 index 00000000..4b062ed9 --- /dev/null +++ b/src/server/mix.hpp @@ -0,0 +1,91 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_MIX_HPP +#define INGEN_ENGINE_MIX_HPP + +#include "raul/log.hpp" +#include "ingen/PortType.hpp" +#include "Buffer.hpp" +#include "Context.hpp" + +using namespace Raul; + +namespace Ingen { +namespace Server { + +inline void +mix(Context& context, Buffer* dst, const IntrusivePtr* srcs, uint32_t num_srcs) +{ + using Ingen::PortType; + switch (dst->type().symbol()) { + case PortType::AUDIO: + case PortType::CONTROL: + // Copy the first source + dst->copy(context, srcs[0].get()); + + // Mix in the rest + for (uint32_t i = 1; i < num_srcs; ++i) { + assert(srcs[i]->type() == PortType::AUDIO || srcs[i]->type() == PortType::CONTROL); + ((AudioBuffer*)dst)->accumulate(context, (AudioBuffer*)srcs[i].get()); + } + + break; + + case PortType::EVENTS: + dst->clear(); + for (uint32_t i = 0; i < num_srcs; ++i) { + assert(srcs[i]->type() == PortType::EVENTS); + srcs[i]->rewind(); + } + + while (true) { + const EventBuffer* first = NULL; + for (uint32_t i = 0; i < num_srcs; ++i) { + const EventBuffer* const src = (const EventBuffer*)srcs[i].get(); + if (src->is_valid()) { + if (!first || src->get_event()->frames < first->get_event()->frames) + first = src; + } + } + if (first) { + const LV2_Event* const ev = first->get_event(); + ((EventBuffer*)dst)->append( + ev->frames, ev->subframes, ev->type, ev->size, + (const uint8_t*)ev + sizeof(LV2_Event)); + first->increment(); + } else { + break; + } + } + + dst->rewind(); + break; + + default: + if (num_srcs == 1) + dst->copy(context, srcs[0].get()); + else + error << "Mix of unsupported buffer types" << std::endl; + return; + } +} + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_MIX_HPP diff --git a/src/server/types.hpp b/src/server/types.hpp new file mode 100644 index 00000000..1c6b217e --- /dev/null +++ b/src/server/types.hpp @@ -0,0 +1,29 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_TYPES_HPP +#define INGEN_ENGINE_TYPES_HPP + +#include +#include + +typedef float Sample; +typedef uint32_t SampleCount; +typedef uint32_t SampleRate; +typedef uint32_t FrameTime; + +#endif // INGEN_ENGINE_TYPES_HPP diff --git a/src/server/util.hpp b/src/server/util.hpp new file mode 100644 index 00000000..9aab55ac --- /dev/null +++ b/src/server/util.hpp @@ -0,0 +1,90 @@ +/* This file is part of Ingen. + * Copyright 2007-2011 David Robillard + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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_UTIL_HPP +#define INGEN_ENGINE_UTIL_HPP + +#include +#include + +#include "raul/log.hpp" +#include "raul/Path.hpp" + +#include "ingen-config.h" + +#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 { +namespace Server { + +/** 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 + Raul::info << "Set SSE denormal fix flag" << endl; + } + } else { + Raul::warn << "This code has been built with SSE support, but your processor does" + << " not support the SSE instruction set, exiting." << std::endl; + exit(EXIT_FAILURE); + } +#endif +#endif +} + +static inline std::string +ingen_jack_port_name(const Raul::Path& path) +{ + return path.chop_start("/"); +} + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_UTIL_HPP diff --git a/src/server/wscript b/src/server/wscript new file mode 100644 index 00000000..708f38d2 --- /dev/null +++ b/src/server/wscript @@ -0,0 +1,128 @@ +#!/usr/bin/env python +from waflib.extras import autowaf as autowaf + +def build(bld): + # Headers + bld.install_files('${INCLUDEDIR}/ingen/server', bld.path.ant_glob('*.hpp')) + + core_source = ''' + AudioBuffer.cpp + BufferFactory.cpp + ClientBroadcaster.cpp + ConnectionImpl.cpp + ControlBindings.cpp + DuplexPort.cpp + Engine.cpp + EngineStore.cpp + Event.cpp + EventBuffer.cpp + EventSink.cpp + EventSource.cpp + GraphObjectImpl.cpp + InputPort.cpp + InternalPlugin.cpp + MessageContext.cpp + NodeFactory.cpp + NodeImpl.cpp + ObjectBuffer.cpp + ObjectSender.cpp + OutputPort.cpp + PatchImpl.cpp + PluginImpl.cpp + PortImpl.cpp + PostProcessor.cpp + ProcessContext.cpp + ProcessSlave.cpp + QueuedEngineInterface.cpp + QueuedEvent.cpp + events/Connect.cpp + events/CreateNode.cpp + events/CreatePatch.cpp + events/CreatePort.cpp + events/Delete.cpp + events/Disconnect.cpp + events/DisconnectAll.cpp + events/Get.cpp + events/Move.cpp + events/RegisterClient.cpp + events/RequestMetadata.cpp + events/SendBinding.cpp + events/SendPortActivity.cpp + events/SendPortValue.cpp + events/SetMetadata.cpp + events/SetPortValue.cpp + events/UnregisterClient.cpp + ingen_engine.cpp + internals/Controller.cpp + internals/Delay.cpp + internals/Note.cpp + internals/Trigger.cpp + ''' + + if bld.is_defined('HAVE_SLV2'): + core_source += ' LV2Info.cpp LV2Plugin.cpp LV2Node.cpp ' + + obj = bld(features = 'cxx cxxshlib') + obj.source = core_source + obj.export_includes = ['.'] + obj.includes = ['.', '..', '../..', '../../include'] + obj.name = 'libingen_server' + obj.target = 'ingen_server' + obj.install_path = '${LIBDIR}' + obj.use = 'libingen_shared' + core_libs = 'GLIBMM GTHREAD LV2CORE SLV2 RAUL SORD' + autowaf.use_lib(bld, obj, core_libs) + + if bld.is_defined('HAVE_SOUP'): + obj = bld(features = 'cxx cxxshlib') + obj.source = ''' + EventSource.cpp + QueuedEngineInterface.cpp + HTTPClientSender.cpp + HTTPEngineReceiver.cpp + ingen_http.cpp + ''' + obj.includes = ['.', '..', '../..', '../../include', '../server'] + obj.name = 'libingen_http' + obj.target = 'ingen_http' + obj.install_path = '${LIBDIR}' + autowaf.use_lib(bld, obj, core_libs + ' SOUP') + + if bld.is_defined('HAVE_LIBLO'): + obj = bld(features = 'cxx cxxshlib') + obj.source = ''' + EventSource.cpp + QueuedEngineInterface.cpp + OSCClientSender.cpp + OSCEngineReceiver.cpp + ingen_osc.cpp + ''' + obj.export_includes = ['.'] + obj.includes = ['.', '..', '../..', '../../include', '../server'] + obj.name = 'libingen_osc' + obj.target = 'ingen_osc' + obj.install_path = '${LIBDIR}' + autowaf.use_lib(bld, obj, core_libs + ' LIBLO') + + if bld.is_defined('HAVE_JACK'): + obj = bld(features = 'cxx cxxshlib') + obj.source = 'JackDriver.cpp ingen_jack.cpp' + obj.export_includes = ['.'] + obj.includes = ['.', '..', '../..', '../../include', '../server'] + obj.name = 'libingen_jack' + obj.target = 'ingen_jack' + obj.install_path = '${LIBDIR}' + obj.use = 'libingen_server' + autowaf.use_lib(bld, obj, core_libs + ' JACK') + + # Ingen LV2 wrapper + obj = bld(features = 'cxx cxxshlib') + obj.source = ' ingen_lv2.cpp ' + obj.export_includes = ['.'] + obj.includes = ['.', '..', '../..', '../../include'] + obj.name = 'libingen_lv2' + obj.target = 'ingen_lv2' + obj.install_path = '${LIBDIR}' + obj.use = 'libingen_shared' + obj.add_objects = 'libingen_server' + autowaf.use_lib(bld, obj, core_libs) diff --git a/src/shared/World.cpp b/src/shared/World.cpp index eea7bbd5..4c06a8f6 100644 --- a/src/shared/World.cpp +++ b/src/shared/World.cpp @@ -172,7 +172,7 @@ public: LV2Features* lv2_features; Sord::World* rdf_world; SharedPtr uris; - SharedPtr engine; + SharedPtr engine; SharedPtr local_engine; SharedPtr serialiser; SharedPtr parser; @@ -193,7 +193,7 @@ World::~World() } void World::set_local_engine(SharedPtr e) { _impl->local_engine = e; } -void World::set_engine(SharedPtr e) { _impl->engine = e; } +void World::set_engine(SharedPtr e) { _impl->engine = e; } void World::set_serialiser(SharedPtr s) { _impl->serialiser = s; } void World::set_parser(SharedPtr p) { _impl->parser = p; } void World::set_store(SharedPtr s) { _impl->store = s; } @@ -202,7 +202,7 @@ void World::set_conf(Raul::Configuration* c) { _impl->conf int& World::argc() { return _impl->argc; } char**& World::argv() { return _impl->argv; } SharedPtr World::local_engine() { return _impl->local_engine; } -SharedPtr World::engine() { return _impl->engine; } +SharedPtr World::engine() { return _impl->engine; } SharedPtr World::serialiser() { return _impl->serialiser; } SharedPtr World::parser() { return _impl->parser; } SharedPtr World::store() { return _impl->store; } @@ -245,14 +245,14 @@ World::unload_modules() /** Get an interface for a remote engine at @a url */ -SharedPtr +SharedPtr World::interface(const std::string& url) { const string scheme = url.substr(0, url.find(":")); const Pimpl::InterfaceFactories::const_iterator i = _impl->interface_factories.find(scheme); if (i == _impl->interface_factories.end()) { warn << "Unknown URI scheme `" << scheme << "'" << endl; - return SharedPtr(); + return SharedPtr(); } return i->second(this, url); diff --git a/src/shared/World.hpp b/src/shared/World.hpp index 7f100d8a..a0603bf0 100644 --- a/src/shared/World.hpp +++ b/src/shared/World.hpp @@ -36,7 +36,7 @@ namespace Sord { class World; } namespace Ingen { class EngineBase; -class EngineInterface; +class ServerInterface; namespace Serialisation { class Serialiser; class Parser; } @@ -63,27 +63,27 @@ public: virtual bool load_module(const char* name); virtual void unload_modules(); - typedef SharedPtr (*InterfaceFactory)( + typedef SharedPtr (*InterfaceFactory)( World* world, const std::string& engine_url); virtual void add_interface_factory(const std::string& scheme, InterfaceFactory factory); - virtual SharedPtr interface( + virtual SharedPtr interface( const std::string& engine_url); virtual bool run(const std::string& mime_type, const std::string& filename); virtual void set_local_engine(SharedPtr e); - virtual void set_engine(SharedPtr e); + virtual void set_engine(SharedPtr e); virtual void set_serialiser(SharedPtr s); virtual void set_parser(SharedPtr p); virtual void set_store(SharedPtr s); virtual SharedPtr local_engine(); - virtual SharedPtr engine(); + virtual SharedPtr engine(); virtual SharedPtr serialiser(); virtual SharedPtr parser(); virtual SharedPtr store(); -- cgit v1.2.1