diff options
Diffstat (limited to 'src/libs')
45 files changed, 575 insertions, 100 deletions
diff --git a/src/libs/engine/CompiledPatch.hpp b/src/libs/engine/CompiledPatch.hpp index f5de52e1..953d94d6 100644 --- a/src/libs/engine/CompiledPatch.hpp +++ b/src/libs/engine/CompiledPatch.hpp @@ -39,7 +39,8 @@ struct CompiledNode { { // Copy to a vector for maximum iteration speed and cache optimization // (Need to take a copy anyway) - + + _dependants.reserve(d->size()); for (List<Node*>::iterator i = d->begin(); i != d->end(); ++i) _dependants.push_back(*i); } diff --git a/src/libs/engine/DSSINode.cpp b/src/libs/engine/DSSINode.cpp index c2d05d36..af9be208 100644 --- a/src/libs/engine/DSSINode.cpp +++ b/src/libs/engine/DSSINode.cpp @@ -172,7 +172,7 @@ DSSINode::has_midi_input() const void -DSSINode::process(SampleCount nframes, FrameTime start, FrameTime end) +DSSINode::process(ProcessContext& context, SampleCount nframes, FrameTime start, FrameTime end) { NodeBase::pre_process(nframes, start, end); @@ -188,7 +188,7 @@ DSSINode::process(SampleCount nframes, FrameTime start, FrameTime end) _dssi_descriptor->run_multiple_synths(1, _instances, nframes, events, events_sizes); } else { - LADSPANode::process(nframes, start, end); + LADSPANode::process(context, nframes, start, end); } NodeBase::post_process(nframes, start, end); diff --git a/src/libs/engine/DSSINode.hpp b/src/libs/engine/DSSINode.hpp index 2c97fdc3..1e24836d 100644 --- a/src/libs/engine/DSSINode.hpp +++ b/src/libs/engine/DSSINode.hpp @@ -54,7 +54,7 @@ public: void configure(const string& key, const string& val); void program(int bank, int program); - void process(SampleCount nframes, FrameTime start, FrameTime end); + void process(ProcessContext& events, SampleCount nframes, FrameTime start, FrameTime end); void set_port_buffer(uint32_t voice, uint32_t port_num, Buffer* buf); diff --git a/src/libs/engine/DuplexPort.cpp b/src/libs/engine/DuplexPort.cpp index a0d370db..f0b138ed 100644 --- a/src/libs/engine/DuplexPort.cpp +++ b/src/libs/engine/DuplexPort.cpp @@ -30,10 +30,10 @@ namespace Ingen { DuplexPort::DuplexPort(Node* parent, const string& name, uint32_t index, uint32_t poly, DataType type, size_t buffer_size, bool is_output) -: Port(parent, name, index, poly, type, buffer_size) -, InputPort(parent, name, index, poly, type, buffer_size) -, OutputPort(parent, name, index, poly, type, buffer_size) -, _is_output(is_output) + : Port(parent, name, index, poly, type, buffer_size) + , InputPort(parent, name, index, poly, type, buffer_size) + , OutputPort(parent, name, index, poly, type, buffer_size) + , _is_output(is_output) { assert(Port::_parent == parent); } @@ -43,11 +43,19 @@ void DuplexPort::pre_process(SampleCount nframes, FrameTime start, FrameTime end) { // Think about it... - - if (_is_output) + +// if (_is_output) { InputPort::pre_process(nframes, start, end); - else - OutputPort::pre_process(nframes, start, end); +// } else { + //for (uint32_t i=0; i < _poly; ++i) + // _buffers->at(i)->rewind(); +// OutputPort::pre_process(nframes, start, end); +// } +} + +void +DuplexPort::process(ProcessContext& events, SampleCount nframes, FrameTime start, FrameTime end) +{ } diff --git a/src/libs/engine/DuplexPort.hpp b/src/libs/engine/DuplexPort.hpp index b6d2578c..4c98ea88 100644 --- a/src/libs/engine/DuplexPort.hpp +++ b/src/libs/engine/DuplexPort.hpp @@ -45,6 +45,7 @@ public: virtual ~DuplexPort() {} void pre_process(SampleCount nframes, FrameTime start, FrameTime end); + void process(ProcessContext& events, SampleCount nframes, FrameTime start, FrameTime end); void post_process(SampleCount nframes, FrameTime start, FrameTime end); bool is_input() const { return !_is_output; } diff --git a/src/libs/engine/EventSink.hpp b/src/libs/engine/EventSink.hpp new file mode 100644 index 00000000..27407684 --- /dev/null +++ b/src/libs/engine/EventSink.hpp @@ -0,0 +1,59 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef EVENTSINK_H +#define EVENTSINK_H + +#include <list> +#include <utility> +#include <raul/DoubleBuffer.hpp> +#include "types.hpp" + +namespace Ingen { + +class Port; + + +/** 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(size_t capacity) : _capacity(capacity) {} + + /* FIXME: Figure out variable sized event queues and make this a generic + * interface (ie don't add a method for every event type, crap..) */ + + void control_change(Port* port, float val); + +private: + size_t _capacity; + //Raul::List<std::pair<Port*, Raul::DoubleBuffer<float> > > _ports; +}; + + + +} // namespace Ingen + +#endif // EVENTSINK_H + diff --git a/src/libs/engine/EventSource.hpp b/src/libs/engine/EventSource.hpp index 2bf40527..803fa8c9 100644 --- a/src/libs/engine/EventSource.hpp +++ b/src/libs/engine/EventSource.hpp @@ -42,7 +42,6 @@ class PostProcessor; class EventSource { public: - virtual ~EventSource() {} virtual void activate() = 0; @@ -54,7 +53,7 @@ public: FrameTime cycle_end) = 0; protected: - EventSource() {} + size_t _capacity; }; diff --git a/src/libs/engine/GraphObject.hpp b/src/libs/engine/GraphObject.hpp index a2cfd5f0..3da9c64f 100644 --- a/src/libs/engine/GraphObject.hpp +++ b/src/libs/engine/GraphObject.hpp @@ -27,16 +27,16 @@ #include <raul/Atom.hpp> #include "types.hpp" -using std::string; using Raul::Atom; using Raul::Path; - namespace Raul { class Maid; } namespace Ingen { class Patch; +class ProcessContext; + /** An object on the audio graph - Patch, Node, Port, etc. * @@ -67,7 +67,7 @@ public: inline GraphObject* parent() const { return _parent; } inline const string& name() const { return _name; } - virtual void process(SampleCount nframes, FrameTime start, FrameTime end) = 0; + virtual void process(ProcessContext& events, SampleCount nframes, FrameTime start, FrameTime end) = 0; /** Rename */ virtual void set_path(const Path& new_path) { @@ -102,7 +102,7 @@ public: protected: GraphObject* _parent; - string _name; + std::string _name; bool _polyphonic; private: diff --git a/src/libs/engine/InputPort.cpp b/src/libs/engine/InputPort.cpp index 1adb53a0..4f2147c4 100644 --- a/src/libs/engine/InputPort.cpp +++ b/src/libs/engine/InputPort.cpp @@ -23,17 +23,30 @@ #include "Connection.hpp" #include "OutputPort.hpp" #include "Node.hpp" +#include "ProcessContext.hpp" #include "util.hpp" -using std::cerr; using std::cout; using std::endl; - +using namespace std; namespace Ingen { InputPort::InputPort(Node* parent, const string& name, uint32_t index, uint32_t poly, DataType type, size_t buffer_size) -: Port(parent, name, index, poly, type, buffer_size) + : Port(parent, name, index, poly, type, buffer_size) + , _last_reported_value(0.0f) // default? +{ +} + + +void +InputPort::set_buffer_size(size_t size) { + Port::set_buffer_size(size); + assert(_buffer_size = size); + + for (Raul::List<Connection*>::iterator c = _connections.begin(); c != _connections.end(); ++c) + (*c)->set_buffer_size(size); + } @@ -64,6 +77,10 @@ InputPort::add_connection(Raul::ListNode<Connection*>* const c) } Port::connect_buffers(); } + + // Automatically monitor connected control inputs + if (_type == DataType::FLOAT && _buffer_size == 1) + _monitor = true; } @@ -104,6 +121,10 @@ InputPort::remove_connection(const OutputPort* src_port) if (modify_buffers) Port::connect_buffers(); + + // Turn off monitoring if we're not connected any more (FIXME: not quite right..) + if (_type == DataType::FLOAT && _buffer_size == 1 && _connections.size() == 0) + _monitor = false; return connection; } @@ -195,16 +216,18 @@ InputPort::pre_process(SampleCount nframes, FrameTime start, FrameTime end) void -InputPort::set_buffer_size(size_t size) +InputPort::process(ProcessContext& context, SampleCount nframes, FrameTime start, FrameTime end) { - Port::set_buffer_size(size); - assert(_buffer_size = size); - - for (Raul::List<Connection*>::iterator c = _connections.begin(); c != _connections.end(); ++c) - (*c)->set_buffer_size(size); - + if (_monitor && _type == DataType::FLOAT && _buffer_size == 1) { + const Sample value = ((AudioBuffer*)(*_buffers)[0])->value_at(0); + if (value != _last_reported_value) { + context.event_sink().control_change(this, ((AudioBuffer*)(*_buffers)[0])->value_at(0)); + _last_reported_value = value; + } + } } + void InputPort::post_process(SampleCount nframes, FrameTime start, FrameTime end) { diff --git a/src/libs/engine/InputPort.hpp b/src/libs/engine/InputPort.hpp index a7da6250..6aa86b9c 100644 --- a/src/libs/engine/InputPort.hpp +++ b/src/libs/engine/InputPort.hpp @@ -57,6 +57,7 @@ public: const Connections& connections() { return _connections; } void pre_process(SampleCount nframes, FrameTime start, FrameTime end); + void process(ProcessContext& events, SampleCount nframes, FrameTime start, FrameTime end); void post_process(SampleCount nframes, FrameTime start, FrameTime end); bool is_connected() const { return (_connections.size() > 0); } @@ -69,6 +70,7 @@ public: private: Connections _connections; + Sample _last_reported_value; }; diff --git a/src/libs/engine/JackAudioDriver.cpp b/src/libs/engine/JackAudioDriver.cpp index b9cbdb60..4f32beb4 100644 --- a/src/libs/engine/JackAudioDriver.cpp +++ b/src/libs/engine/JackAudioDriver.cpp @@ -299,8 +299,9 @@ JackAudioDriver::_process_cb(jack_nframes_t nframes) // Run root patch if (_root_patch) - _root_patch->process(nframes, start_of_current_cycle, start_of_current_cycle + nframes); - + _root_patch->process(_process_context, nframes, start_of_current_cycle, + start_of_current_cycle + nframes); + return 0; } diff --git a/src/libs/engine/JackAudioDriver.hpp b/src/libs/engine/JackAudioDriver.hpp index 9b195b13..28007d77 100644 --- a/src/libs/engine/JackAudioDriver.hpp +++ b/src/libs/engine/JackAudioDriver.hpp @@ -25,6 +25,7 @@ #include <raul/List.hpp> #include "AudioDriver.hpp" #include "Buffer.hpp" +#include "ProcessContext.hpp" namespace Ingen { @@ -136,6 +137,7 @@ private: jack_transport_state_t _transport_state; Raul::List<JackAudioPort*> _ports; + ProcessContext _process_context; Patch* _root_patch; }; diff --git a/src/libs/engine/LADSPANode.cpp b/src/libs/engine/LADSPANode.cpp index ad1761e8..b2725131 100644 --- a/src/libs/engine/LADSPANode.cpp +++ b/src/libs/engine/LADSPANode.cpp @@ -188,7 +188,7 @@ LADSPANode::deactivate() void -LADSPANode::process(SampleCount nframes, FrameTime start, FrameTime end) +LADSPANode::process(ProcessContext& context, SampleCount nframes, FrameTime start, FrameTime end) { NodeBase::pre_process(nframes, start, end); diff --git a/src/libs/engine/LADSPANode.hpp b/src/libs/engine/LADSPANode.hpp index 3c673f0b..a4ea1031 100644 --- a/src/libs/engine/LADSPANode.hpp +++ b/src/libs/engine/LADSPANode.hpp @@ -42,7 +42,7 @@ public: void activate(); void deactivate(); - void process(SampleCount nframes, FrameTime start, FrameTime end); + void process(ProcessContext& events, SampleCount nframes, FrameTime start, FrameTime end); void set_port_buffer(uint32_t voice, uint32_t port_num, Buffer* buf); diff --git a/src/libs/engine/LV2Node.cpp b/src/libs/engine/LV2Node.cpp index f1b61997..88c002e5 100644 --- a/src/libs/engine/LV2Node.cpp +++ b/src/libs/engine/LV2Node.cpp @@ -241,7 +241,7 @@ LV2Node::deactivate() void -LV2Node::process(SampleCount nframes, FrameTime start, FrameTime end) +LV2Node::process(ProcessContext& context, SampleCount nframes, FrameTime start, FrameTime end) { NodeBase::pre_process(nframes, start, end); diff --git a/src/libs/engine/LV2Node.hpp b/src/libs/engine/LV2Node.hpp index c566b138..d0941b93 100644 --- a/src/libs/engine/LV2Node.hpp +++ b/src/libs/engine/LV2Node.hpp @@ -51,7 +51,7 @@ public: void activate(); void deactivate(); - void process(SampleCount nframes, FrameTime start, FrameTime end); + void process(ProcessContext& events, SampleCount nframes, FrameTime start, FrameTime end); void set_port_buffer(uint32_t voice, uint32_t port_num, Buffer* buf); diff --git a/src/libs/engine/Makefile.am b/src/libs/engine/Makefile.am index 44d3825b..5a5879d5 100644 --- a/src/libs/engine/Makefile.am +++ b/src/libs/engine/Makefile.am @@ -83,6 +83,8 @@ libingen_engine_la_SOURCES = \ Port.hpp \ PostProcessor.cpp \ PostProcessor.hpp \ + ProcessContext.hpp \ + ProcessContext.cpp \ ProcessSlave.hpp \ ProcessSlave.cpp \ QueuedEngineInterface.cpp \ diff --git a/src/libs/engine/MidiControlNode.cpp b/src/libs/engine/MidiControlNode.cpp index 3d4924b6..a2db6696 100644 --- a/src/libs/engine/MidiControlNode.cpp +++ b/src/libs/engine/MidiControlNode.cpp @@ -71,7 +71,7 @@ MidiControlNode::MidiControlNode(const string& path, bool polyphonic, Patch* par void -MidiControlNode::process(SampleCount nframes, FrameTime start, FrameTime end) +MidiControlNode::process(ProcessContext& context, SampleCount nframes, FrameTime start, FrameTime end) { NodeBase::pre_process(nframes, start, end); diff --git a/src/libs/engine/MidiControlNode.hpp b/src/libs/engine/MidiControlNode.hpp index ee97fa8a..d06e2288 100644 --- a/src/libs/engine/MidiControlNode.hpp +++ b/src/libs/engine/MidiControlNode.hpp @@ -41,7 +41,7 @@ class MidiControlNode : public NodeBase public: MidiControlNode(const std::string& path, bool polyphonic, Patch* parent, SampleRate srate, size_t buffer_size); - void process(SampleCount nframes, FrameTime start, FrameTime end); + void process(ProcessContext& events, SampleCount nframes, FrameTime start, FrameTime end); void control(uchar control_num, uchar val, SampleCount offset); diff --git a/src/libs/engine/MidiNoteNode.cpp b/src/libs/engine/MidiNoteNode.cpp index 42d96dfa..bfab7ed0 100644 --- a/src/libs/engine/MidiNoteNode.cpp +++ b/src/libs/engine/MidiNoteNode.cpp @@ -119,7 +119,7 @@ MidiNoteNode::apply_poly(Raul::Maid& maid, uint32_t poly) void -MidiNoteNode::process(SampleCount nframes, FrameTime start, FrameTime end) +MidiNoteNode::process(ProcessContext& context, SampleCount nframes, FrameTime start, FrameTime end) { NodeBase::pre_process(nframes, start, end); diff --git a/src/libs/engine/MidiNoteNode.hpp b/src/libs/engine/MidiNoteNode.hpp index f2fbbf5d..e86102db 100644 --- a/src/libs/engine/MidiNoteNode.hpp +++ b/src/libs/engine/MidiNoteNode.hpp @@ -45,7 +45,7 @@ public: bool prepare_poly(uint32_t poly); bool apply_poly(Raul::Maid& maid, uint32_t poly); - void process(SampleCount nframes, FrameTime start, FrameTime end); + void process(ProcessContext& events, SampleCount nframes, FrameTime start, FrameTime end); void note_on(uchar note_num, uchar velocity, FrameTime time, SampleCount nframes, FrameTime start, FrameTime end); void note_off(uchar note_num, FrameTime time, SampleCount nframes, FrameTime start, FrameTime end); diff --git a/src/libs/engine/MidiTriggerNode.cpp b/src/libs/engine/MidiTriggerNode.cpp index 7d8fa952..7a889d43 100644 --- a/src/libs/engine/MidiTriggerNode.cpp +++ b/src/libs/engine/MidiTriggerNode.cpp @@ -59,7 +59,7 @@ MidiTriggerNode::MidiTriggerNode(const string& path, bool polyphonic, Patch* par void -MidiTriggerNode::process(SampleCount nframes, FrameTime start, FrameTime end) +MidiTriggerNode::process(ProcessContext& context, SampleCount nframes, FrameTime start, FrameTime end) { NodeBase::pre_process(nframes, start, end); diff --git a/src/libs/engine/MidiTriggerNode.hpp b/src/libs/engine/MidiTriggerNode.hpp index 83dbe65f..389768f8 100644 --- a/src/libs/engine/MidiTriggerNode.hpp +++ b/src/libs/engine/MidiTriggerNode.hpp @@ -43,7 +43,7 @@ class MidiTriggerNode : public NodeBase public: MidiTriggerNode(const std::string& path, bool polyphonic, Patch* parent, SampleRate srate, size_t buffer_size); - void process(SampleCount nframes, FrameTime start, FrameTime end); + void process(ProcessContext& events, SampleCount nframes, FrameTime start, FrameTime end); void note_on(uchar note_num, uchar velocity, FrameTime time, SampleCount nframes, FrameTime start, FrameTime end); void note_off(uchar note_num, FrameTime time, SampleCount nframes, FrameTime start, FrameTime end); diff --git a/src/libs/engine/Node.hpp b/src/libs/engine/Node.hpp index 83a31c67..8db11ad8 100644 --- a/src/libs/engine/Node.hpp +++ b/src/libs/engine/Node.hpp @@ -117,7 +117,7 @@ public: * @a start and @a end are transport times: end is not redundant in the case * of varispeed, where end-start != nframes. */ - virtual void process(SampleCount nframes, FrameTime start, FrameTime end) = 0; + virtual void process(ProcessContext& events, SampleCount nframes, FrameTime start, FrameTime end) = 0; virtual void set_port_buffer(uint32_t voice, uint32_t port_num, Buffer* buf) = 0; diff --git a/src/libs/engine/NodeBase.cpp b/src/libs/engine/NodeBase.cpp index 4f3d37b2..5e6176f0 100644 --- a/src/libs/engine/NodeBase.cpp +++ b/src/libs/engine/NodeBase.cpp @@ -175,10 +175,10 @@ NodeBase::signal_input_ready() void NodeBase::pre_process(SampleCount nframes, FrameTime start, FrameTime end) { - assert(_activated); // Mix down any ports with multiple inputs - for (size_t i=0; i < _ports->size(); ++i) - _ports->at(i)->pre_process(nframes, start, end); + if (_ports) + for (size_t i=0; i < _ports->size(); ++i) + _ports->at(i)->pre_process(nframes, start, end); } @@ -187,11 +187,10 @@ NodeBase::pre_process(SampleCount nframes, FrameTime start, FrameTime end) void NodeBase::post_process(SampleCount nframes, FrameTime start, FrameTime end) { - assert(_activated); - /* Write output ports */ - for (size_t i=0; i < _ports->size(); ++i) - _ports->at(i)->post_process(nframes, start, end); + if (_ports) + for (size_t i=0; i < _ports->size(); ++i) + _ports->at(i)->post_process(nframes, start, end); } diff --git a/src/libs/engine/NodeBase.hpp b/src/libs/engine/NodeBase.hpp index 9f12c95f..d43a84a6 100644 --- a/src/libs/engine/NodeBase.hpp +++ b/src/libs/engine/NodeBase.hpp @@ -70,7 +70,7 @@ public: virtual unsigned n_inputs_ready() const { return _n_inputs_ready.get(); } virtual void pre_process(SampleCount nframes, FrameTime start, FrameTime end); - virtual void process(SampleCount nframes, FrameTime start, FrameTime end) = 0; + virtual void process(ProcessContext& events, SampleCount nframes, FrameTime start, FrameTime end) = 0; virtual void post_process(SampleCount nframes, FrameTime start, FrameTime end); virtual void set_port_buffer(uint32_t voice, uint32_t port_num, Buffer* buf) {} diff --git a/src/libs/engine/OutputPort.cpp b/src/libs/engine/OutputPort.cpp index 9724c4c7..b7da8956 100644 --- a/src/libs/engine/OutputPort.cpp +++ b/src/libs/engine/OutputPort.cpp @@ -16,7 +16,8 @@ */ #include "OutputPort.hpp" -#include "Buffer.hpp" +#include "AudioBuffer.hpp" +#include "ProcessContext.hpp" namespace Ingen { @@ -30,6 +31,15 @@ OutputPort::pre_process(SampleCount nframes, FrameTime start, FrameTime end) void +OutputPort::process(ProcessContext& context, SampleCount nframes, FrameTime start, FrameTime end) +{ + if (_monitor && _type == DataType::FLOAT && _buffer_size == 1) { + context.event_sink().control_change(this, ((AudioBuffer*)(*_buffers)[0])->value_at(0)); + } +} + + +void OutputPort::post_process(SampleCount nframes, FrameTime start, FrameTime end) { for (uint32_t i=0; i < _poly; ++i) diff --git a/src/libs/engine/OutputPort.hpp b/src/libs/engine/OutputPort.hpp index 0c2e0443..8f12d252 100644 --- a/src/libs/engine/OutputPort.hpp +++ b/src/libs/engine/OutputPort.hpp @@ -50,6 +50,7 @@ public: {} void pre_process(SampleCount nframes, FrameTime start, FrameTime end); + void process(ProcessContext& events, SampleCount nframes, FrameTime start, FrameTime end); void post_process(SampleCount nframes, FrameTime start, FrameTime end); virtual ~OutputPort() {} diff --git a/src/libs/engine/Patch.cpp b/src/libs/engine/Patch.cpp index 2705db36..80d59768 100644 --- a/src/libs/engine/Patch.cpp +++ b/src/libs/engine/Patch.cpp @@ -147,33 +147,25 @@ Patch::apply_internal_poly(Raul::Maid& maid, uint32_t poly) * Calls all Nodes in (roughly, if parallel) the order _compiled_patch specifies. */ void -Patch::process(SampleCount nframes, FrameTime start, FrameTime end) +Patch::process(ProcessContext& context, SampleCount nframes, FrameTime start, FrameTime end) { if (_compiled_patch == NULL || _compiled_patch->size() == 0 || !_process) return; - /* Prepare input ports */ - /* FIXME: Pre-processing input ports breaks MIDI somehow? */ - if (_ports) - for (size_t i=0; i < _ports->size(); ++i) - if (_ports->at(i)->is_output()) - _ports->at(i)->pre_process(nframes, start, end); + NodeBase::pre_process(nframes, start, end); /* Run */ if (_engine.process_slaves().size() > 0) - process_parallel(nframes, start, end); + process_parallel(context, nframes, start, end); else - process_single(nframes, start, end); + process_single(context, nframes, start, end); - /* Write output ports */ - if (_ports) - for (size_t i=0; i < _ports->size(); ++i) - _ports->at(i)->post_process(nframes, start, end); + NodeBase::post_process(nframes, start, end); } void -Patch::process_parallel(SampleCount nframes, FrameTime start, FrameTime end) +Patch::process_parallel(ProcessContext& context, SampleCount nframes, FrameTime start, FrameTime end) { size_t n_slaves = _engine.process_slaves().size(); @@ -209,7 +201,7 @@ Patch::process_parallel(SampleCount nframes, FrameTime start, FrameTime end) if (n.node()->process_lock()) { if (n.node()->n_inputs_ready() == n.n_providers()) { - n.node()->process(nframes, start, end); + n.node()->process(context, nframes, start, end); /* Signal dependants their input is ready */ for (size_t i=0; i < n.dependants().size(); ++i) @@ -232,7 +224,8 @@ Patch::process_parallel(SampleCount nframes, FrameTime start, FrameTime end) /* 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. + * FIXME: This probably breaks (race) at extremely small nframes where + * ingen is the majority of the DSP load. */ for (size_t i=0; i < n_slaves; ++i) _engine.process_slaves()[i]->finish(); @@ -240,12 +233,12 @@ Patch::process_parallel(SampleCount nframes, FrameTime start, FrameTime end) void -Patch::process_single(SampleCount nframes, FrameTime start, FrameTime end) +Patch::process_single(ProcessContext& context, SampleCount nframes, FrameTime start, FrameTime end) { CompiledPatch* const cp = _compiled_patch; for (size_t i=0; i < cp->size(); ++i) - (*cp)[i].node()->process(nframes, start, end); + (*cp)[i].node()->process(context, nframes, start, end); } diff --git a/src/libs/engine/Patch.hpp b/src/libs/engine/Patch.hpp index 32a02f76..75de93cb 100644 --- a/src/libs/engine/Patch.hpp +++ b/src/libs/engine/Patch.hpp @@ -54,7 +54,7 @@ public: void activate(); void deactivate(); - void process(SampleCount nframes, FrameTime start, FrameTime end); + void process(ProcessContext& events, SampleCount nframes, FrameTime start, FrameTime end); void set_buffer_size(size_t size); @@ -113,8 +113,8 @@ public: private: inline void compile_recursive(Node* n, CompiledPatch* output) const; - void process_parallel(SampleCount nframes, FrameTime start, FrameTime end); - void process_single(SampleCount nframes, FrameTime start, FrameTime end); + void process_parallel(ProcessContext& context, SampleCount nframes, FrameTime start, FrameTime end); + void process_single(ProcessContext& context, SampleCount nframes, FrameTime start, FrameTime end); Engine& _engine; uint32_t _internal_poly; diff --git a/src/libs/engine/Port.cpp b/src/libs/engine/Port.cpp index 8613581d..d1813f3f 100644 --- a/src/libs/engine/Port.cpp +++ b/src/libs/engine/Port.cpp @@ -20,8 +20,9 @@ #include "Port.hpp" #include "Node.hpp" #include "DataType.hpp" -#include "Buffer.hpp" +#include "AudioBuffer.hpp" #include "BufferFactory.hpp" +#include "ProcessContext.hpp" namespace Ingen { @@ -34,9 +35,10 @@ Port::Port(Node* const node, const string& name, uint32_t index, uint32_t poly, : GraphObject(node, name, true) , _index(index) , _poly(poly) - , _type(type) , _buffer_size(buffer_size) + , _type(type) , _fixed_buffers(false) + , _monitor(false) , _buffers(new Raul::Array<Buffer*>(poly)) { assert(node != NULL); diff --git a/src/libs/engine/Port.hpp b/src/libs/engine/Port.hpp index f0b7ec25..f02d6ba4 100644 --- a/src/libs/engine/Port.hpp +++ b/src/libs/engine/Port.hpp @@ -31,6 +31,7 @@ namespace Ingen { class Node; class Buffer; +class ProcessContext; /** A port on a Node. @@ -67,7 +68,6 @@ public: /** Called once per process cycle */ virtual void pre_process(SampleCount nframes, FrameTime start, FrameTime end) = 0; - virtual void process(SampleCount nframes, FrameTime start, FrameTime end) {} virtual void post_process(SampleCount nframes, FrameTime start, FrameTime end) {}; /** Empty buffer contents completely (ie silence) */ @@ -85,6 +85,9 @@ public: void fixed_buffers(bool b) { _fixed_buffers = b; } bool fixed_buffers() { return _fixed_buffers; } + + void monitor(bool b) { _monitor = b; } + bool monitor() { return _monitor; } protected: Port(Node* const node, const std::string& name, uint32_t index, uint32_t poly, DataType type, size_t buffer_size); @@ -92,11 +95,12 @@ protected: virtual void allocate_buffers(); virtual void connect_buffers(); - uint32_t _index; - uint32_t _poly; - DataType _type; - size_t _buffer_size; - bool _fixed_buffers; + uint32_t _index; + uint32_t _poly; + uint32_t _buffer_size; + DataType _type; + bool _fixed_buffers; + bool _monitor; Raul::Array<Buffer*>* _buffers; diff --git a/src/libs/engine/ProcessContext.cpp b/src/libs/engine/ProcessContext.cpp new file mode 100644 index 00000000..306f3dba --- /dev/null +++ b/src/libs/engine/ProcessContext.cpp @@ -0,0 +1,25 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License 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" + +using namespace std; + +namespace Ingen { + + +} // namespace Ingen diff --git a/src/libs/engine/ProcessContext.hpp b/src/libs/engine/ProcessContext.hpp new file mode 100644 index 00000000..4d5f733f --- /dev/null +++ b/src/libs/engine/ProcessContext.hpp @@ -0,0 +1,50 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PROCESSCONTEXT_H +#define PROCESSCONTEXT_H + +#include "EventSink.hpp" + +namespace Ingen { + + +/** Context of a process() call. + * + * This is used to pass whatever information a GraphObject might need to + * process in the audio thread, e.g. the available thread pool, sink for + * events (generated in the audio thread, not user initiated events), etc. + * + * \ingroup engine + */ +class ProcessContext +{ +public: + ProcessContext() : _event_sink(1024) {} // FIXME: size? + + EventSink& event_sink() { return _event_sink; } + +private: + EventSink _event_sink; +}; + + + +} // namespace Ingen + +#endif // PROCESSCONTEXT_H + diff --git a/src/libs/engine/ProcessSlave.cpp b/src/libs/engine/ProcessSlave.cpp index 749f6a5c..d8f2e575 100644 --- a/src/libs/engine/ProcessSlave.cpp +++ b/src/libs/engine/ProcessSlave.cpp @@ -49,7 +49,7 @@ ProcessSlave::_whipped() n.node()->wait_for_input(n.n_providers()); - n.node()->process(_nframes, _start, _end); + n.node()->process(_process_context, _nframes, _start, _end); /* Signal dependants their input is ready */ for (size_t i=0; i < n.dependants().size(); ++i) diff --git a/src/libs/engine/ProcessSlave.hpp b/src/libs/engine/ProcessSlave.hpp index a5d2d9cc..c341fe64 100644 --- a/src/libs/engine/ProcessSlave.hpp +++ b/src/libs/engine/ProcessSlave.hpp @@ -24,6 +24,7 @@ #include <raul/Slave.hpp> #include <raul/Array.hpp> #include <raul/AtomicInt.hpp> +#include "ProcessContext.hpp" #include "types.hpp" namespace Ingen { @@ -35,7 +36,7 @@ class CompiledPatch; class ProcessSlave : protected Raul::Slave { public: ProcessSlave(bool realtime) - : _id(_next_id++), _state(STATE_FINISHED), _index(0), _compiled_patch(NULL) + : _id(_next_id++), _index(0), _state(STATE_FINISHED), _compiled_patch(NULL) { std::stringstream ss; ss << "Process Slave "; @@ -55,12 +56,13 @@ public: inline void whip(CompiledPatch* compiled_patch, size_t start_index, SampleCount nframes, FrameTime start, FrameTime end) { assert(_state == STATE_FINISHED); + _index = start_index; + _state = STATE_RUNNING; _nframes = nframes; _start = start; _end = end; - _index = start_index; _compiled_patch = compiled_patch; - _state = STATE_RUNNING; + Raul::Slave::whip(); } @@ -82,12 +84,13 @@ private: static const int STATE_FINISHED = 2; size_t _id; + size_t _index; Raul::AtomicInt _state; SampleCount _nframes; FrameTime _start; FrameTime _end; - size_t _index; CompiledPatch* _compiled_patch; + ProcessContext _process_context; }; diff --git a/src/libs/engine/TransportNode.cpp b/src/libs/engine/TransportNode.cpp index afa4cf21..6ee9835d 100644 --- a/src/libs/engine/TransportNode.cpp +++ b/src/libs/engine/TransportNode.cpp @@ -82,7 +82,7 @@ TransportNode::TransportNode(const string& path, bool polyphonic, Patch* parent, void -TransportNode::process(SampleCount nframes, FrameTime start, FrameTime end) +TransportNode::process(ProcessContext& context, SampleCount nframes, FrameTime start, FrameTime end) { NodeBase::pre_process(nframes, start, end); #if 0 diff --git a/src/libs/engine/TransportNode.hpp b/src/libs/engine/TransportNode.hpp index 2d90d501..a12a8cfe 100644 --- a/src/libs/engine/TransportNode.hpp +++ b/src/libs/engine/TransportNode.hpp @@ -36,7 +36,7 @@ class TransportNode : public NodeBase public: TransportNode(const std::string& path, bool polyphonic, Patch* parent, SampleRate srate, size_t buffer_size); - virtual void process(SampleCount nframes, FrameTime start, FrameTime end); + virtual void process(ProcessContext& events, SampleCount nframes, FrameTime start, FrameTime end); }; diff --git a/src/libs/engine/events/EnablePortNotificationEvent.cpp b/src/libs/engine/events/EnablePortNotificationEvent.cpp new file mode 100644 index 00000000..992c1615 --- /dev/null +++ b/src/libs/engine/events/EnablePortNotificationEvent.cpp @@ -0,0 +1,80 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "RequestPortValueEvent.hpp" +#include <string> +#include "interface/ClientInterface.hpp" +#include "Responder.hpp" +#include "Engine.hpp" +#include "Port.hpp" +#include "ObjectStore.hpp" +#include "ClientBroadcaster.hpp" +#include "AudioBuffer.hpp" + +using std::string; + +namespace Ingen { + + +RequestPortValueEvent::RequestPortValueEvent(Engine& engine, SharedPtr<Responder> responder, SampleCount timestamp, const string& port_path) +: QueuedEvent(engine, responder, timestamp), + _port_path(port_path), + _port(NULL), + _value(0.0) +{ +} + + +void +RequestPortValueEvent::pre_process() +{ + _port = _engine.object_store()->find_port(_port_path); + + QueuedEvent::pre_process(); +} + + +void +RequestPortValueEvent::execute(SampleCount nframes, FrameTime start, FrameTime end) +{ + QueuedEvent::execute(nframes, start, end); + assert(_time >= start && _time <= end); + + if (_port != NULL && _port->type() == DataType::FLOAT) + _value = ((AudioBuffer*)_port->buffer(0))->value_at(0/*_time - start*/); + else + _port = NULL; // triggers error response +} + + +void +RequestPortValueEvent::post_process() +{ + string msg; + if (!_port) { + _responder->respond_error("Unable to find port for get_value responder."); + } else if (_responder->client()) { + _responder->respond_ok(); + _responder->client()->control_change(_port_path, _value); + } else { + _responder->respond_error("Unable to find client to send port value"); + } +} + + +} // namespace Ingen + diff --git a/src/libs/engine/events/EnablePortNotificationEvent.hpp b/src/libs/engine/events/EnablePortNotificationEvent.hpp new file mode 100644 index 00000000..522af143 --- /dev/null +++ b/src/libs/engine/events/EnablePortNotificationEvent.hpp @@ -0,0 +1,58 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef ENABLEPORTNOTIFICATIONEVENT_H +#define ENABLEPORTNOTIFICATIONEVENT_H + +#include <string> +#include "QueuedEvent.hpp" +#include "types.hpp" + +using std::string; + +namespace Ingen { + +class Port; +namespace Shared { class ClientInterface; } +using Shared::ClientInterface; + + +/** Enable sending of dynamic value change notifications for a port. + * + * \ingroup engine + */ +class EnablePortNotificationEvent : public QueuedEvent +{ +public: + EnablePortNotificationEvent(Engine& engine, + SharedPtr<Responder> responder, + SampleCount timestamp, + const std::string& port_path); + + void pre_process(); + void execute(SampleCount nframes, FrameTime start, FrameTime end); + void post_process(); + +private: + const std::string _port_path; + Port* _port; +}; + + +} // namespace Ingen + +#endif // ENABLEPORTNOTIFICATIONEVENT_H diff --git a/src/libs/engine/events/Makefile.am b/src/libs/engine/events/Makefile.am index f3190756..a97c3bba 100644 --- a/src/libs/engine/events/Makefile.am +++ b/src/libs/engine/events/Makefile.am @@ -33,6 +33,8 @@ EXTRA_DIST = \ DisconnectionEvent.hpp \ EnablePatchEvent.cpp \ EnablePatchEvent.hpp \ + EnablePortNotificationEvent.cpp \ + EnablePortNotificationEvent.hpp \ LoadPluginsEvent.cpp \ LoadPluginsEvent.hpp \ MidiLearnEvent.cpp \ @@ -58,6 +60,8 @@ EXTRA_DIST = \ RequestPluginsEvent.hpp \ RequestPortValueEvent.cpp \ RequestPortValueEvent.hpp \ + SendPortValueEvent.cpp \ + SendPortValueEvent.hpp \ SetMetadataEvent.cpp \ SetMetadataEvent.hpp \ SetPolyphonyEvent.hpp \ diff --git a/src/libs/engine/events/RequestPortValueEvent.cpp b/src/libs/engine/events/RequestPortValueEvent.cpp index 992c1615..34545670 100644 --- a/src/libs/engine/events/RequestPortValueEvent.cpp +++ b/src/libs/engine/events/RequestPortValueEvent.cpp @@ -15,9 +15,9 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "RequestPortValueEvent.hpp" #include <string> #include "interface/ClientInterface.hpp" +#include "events/EnablePortNotificationEvent.hpp" #include "Responder.hpp" #include "Engine.hpp" #include "Port.hpp" @@ -30,17 +30,19 @@ using std::string; namespace Ingen { -RequestPortValueEvent::RequestPortValueEvent(Engine& engine, SharedPtr<Responder> responder, SampleCount timestamp, const string& port_path) +EnablePortNotificationEvent::EnablePortNotificationEvent(Engine& engine, + SharedPtr<Responder> responder, + SampleCount timestamp, + const std::string& port_path) : QueuedEvent(engine, responder, timestamp), _port_path(port_path), - _port(NULL), - _value(0.0) + _port(NULL) { } void -RequestPortValueEvent::pre_process() +EnablePortNotificationEvent::pre_process() { _port = _engine.object_store()->find_port(_port_path); @@ -49,21 +51,25 @@ RequestPortValueEvent::pre_process() void -RequestPortValueEvent::execute(SampleCount nframes, FrameTime start, FrameTime end) +EnablePortNotificationEvent::execute(SampleCount nframes, FrameTime start, FrameTime end) { QueuedEvent::execute(nframes, start, end); + +#if 0 assert(_time >= start && _time <= end); if (_port != NULL && _port->type() == DataType::FLOAT) _value = ((AudioBuffer*)_port->buffer(0))->value_at(0/*_time - start*/); else _port = NULL; // triggers error response +#endif } void -RequestPortValueEvent::post_process() +EnablePortNotificationEvent::post_process() { +#if 0 string msg; if (!_port) { _responder->respond_error("Unable to find port for get_value responder."); @@ -73,6 +79,7 @@ RequestPortValueEvent::post_process() } else { _responder->respond_error("Unable to find client to send port value"); } +#endif } diff --git a/src/libs/engine/events/SendPortValueEvent.cpp b/src/libs/engine/events/SendPortValueEvent.cpp new file mode 100644 index 00000000..d537011d --- /dev/null +++ b/src/libs/engine/events/SendPortValueEvent.cpp @@ -0,0 +1,61 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <sstream> +#include "Responder.hpp" +#include "SendPortValueEvent.hpp" +#include "Engine.hpp" +#include "Port.hpp" +#include "ClientBroadcaster.hpp" +#include "Node.hpp" +#include "ObjectStore.hpp" +#include "AudioBuffer.hpp" +#include "MidiBuffer.hpp" + +using namespace std; + +namespace Ingen { + + +SendPortValueEvent(Engine& engine, + SampleCount timestamp, + Port* port, + bool omni, + uint32_t voice_num, + float value) + : _port(port) + , _omni(omni) + , _voice_num(voice_num) + , _value(value) +{ +} + + +void +SendPortValueEvent::post_process() +{ + if (_omni) { + _engine.broadcaster()->send_control_change(_port->path(), _value); + } else { + cerr << "NON-OMNI CONTROL CHANGE WHAT?" << endl; + _engine.broadcaster()->send_control_change(_port->path(), _value); + } +} + + +} // namespace Ingen + diff --git a/src/libs/engine/events/SendPortValueEvent.hpp b/src/libs/engine/events/SendPortValueEvent.hpp new file mode 100644 index 00000000..d63d43b0 --- /dev/null +++ b/src/libs/engine/events/SendPortValueEvent.hpp @@ -0,0 +1,80 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SENDPORTVALUEEVENT_H +#define SENDPORTVALUEEVENT_H + +#include <string> +#include "Event.hpp" +#include "types.hpp" +using std::string; + +namespace Ingen { + +class Port; + + +/** 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. Variable size data is harder. + * Need some clever variable sized event RT allocation in flat buffer thingie.. + * + * \ingroup engine + */ +class SendPortValueEvent : public Event +{ +public: + SendPortValueEvent() {} + + inline SendPortValueEvent(Engine& engine, + SampleCount timestamp, + Port* port, + bool omni, + uint32_t voice_num, + Sample value) + : _port(port) + , _omni(omni) + , _voice_num(voice_num) + , _value(value) + { + } + + inline void operator=(const SendPortValueEvent& ev) { + _port = ev._port; + _omni = ev._omni; + _voice_num = ev._voice_num; + _value = ev._value; + } + + void post_process(); + +private: + Port* _port; + bool _omni; + uint32_t _voice_num; + Sample _value; +}; + + +} // namespace Ingen + +#endif // SENDPORTVALUEEVENT_H diff --git a/src/libs/engine/events/SetPortValueEvent.cpp b/src/libs/engine/events/SetPortValueEvent.cpp index 9d1ad0db..c1f3c890 100644 --- a/src/libs/engine/events/SetPortValueEvent.cpp +++ b/src/libs/engine/events/SetPortValueEvent.cpp @@ -32,7 +32,7 @@ namespace Ingen { /** Omni (all voices) control setting */ -SetPortValueEvent::SetPortValueEvent(Engine& engine, +SetPortValueEvent::SetPortValueEvent(Engine& engine, SharedPtr<Responder> responder, SampleCount timestamp, const string& port_path, @@ -53,12 +53,12 @@ SetPortValueEvent::SetPortValueEvent(Engine& engine, /** Voice-specific control setting */ SetPortValueEvent::SetPortValueEvent(Engine& engine, - SharedPtr<Responder> responder, - SampleCount timestamp, - uint32_t voice_num, - const string& port_path, - uint32_t data_size, - const void* data) + SharedPtr<Responder> responder, + SampleCount timestamp, + uint32_t voice_num, + const string& port_path, + uint32_t data_size, + const void* data) : Event(engine, responder, timestamp), _omni(false), _voice_num(voice_num), |