Got the audio flowing

git-svn-id: http://svn.drobilla.net/lad@70 a436a847-0d15-0410-975c-d299462d15a1
This commit is contained in:
dave 2006-06-21 03:27:16 +00:00
parent 1c22ed9ea1
commit 53e98ec221
21 changed files with 143 additions and 163 deletions

View File

@ -180,7 +180,7 @@ Buffer<T>::copy(const Buffer<T>* src, size_t start_sample, size_t end_sample)
assert(src->data() != NULL);
assert(m_data != NULL);
const T* const src_data = src->data();
register const T* const src_data = src->data();
for (size_t i=start_sample; i <= end_sample; ++i)
m_data[i] = src_data[i];
@ -205,7 +205,7 @@ Buffer<T>::accumulate(const Buffer<T>* const src, size_t start_sample, size_t en
assert(src->data() != NULL);
assert(m_data != NULL);
const T* const src_data = src->data();
register const T* const src_data = src->data();
for (size_t i=start_sample; i <= end_sample; ++i)
m_data[i] += src_data[i];
@ -267,8 +267,30 @@ Buffer<sample>::prepare(samplecount nframes)
}
////// DriverBuffer ////////
template<>
void
Buffer<MidiMessage>::prepare(samplecount nframes)
{
}
/** Set the buffer (data) used.
*
* This is only to be used by Drivers (to provide zero-copy processing).
*/
template<typename T>
void
Buffer<T>::set_data(T* data)
{
assert(!m_is_joined);
m_data = data;
}
template void Buffer<sample>::set_data(sample* data);
template void Buffer<MidiMessage>::set_data(MidiMessage* data);
////// DriverBuffer ////////
#if 0
template <typename T>
DriverBuffer<T>::DriverBuffer(size_t size)
: Buffer<T>(size)
@ -293,6 +315,6 @@ DriverBuffer<T>::set_data(T* data)
}
template void DriverBuffer<sample>::set_data(sample* data);
template void DriverBuffer<MidiMessage>::set_data(MidiMessage* data);
#endif
} // namespace Om

View File

@ -40,6 +40,9 @@ public:
void join(Buffer* buf);
void unjoin();
/** For driver use only!! */
void set_data(T* data);
inline T& value_at(size_t offset) { assert(offset < m_size); return m_data[offset]; }
void prepare(samplecount nframes);
@ -78,7 +81,7 @@ class DriverBuffer : public Buffer<T>
public:
DriverBuffer(size_t size);
void set_data(T* data);
private:
using Buffer<T>::m_data;

View File

@ -31,7 +31,6 @@ namespace Om {
Connection::Connection(Port* const src_port, Port* const dst_port)
: m_src_port(src_port),
m_dst_port(dst_port),
m_is_poly_to_mono( (src_port->parent_node()->poly() > dst_port->parent_node()->poly()) ),
m_pending_disconnection(false)
{
assert(src_port != NULL);

View File

@ -56,7 +56,6 @@ protected:
Port* const m_src_port;
Port* const m_dst_port;
bool m_is_poly_to_mono;
bool m_pending_disconnection;
};

View File

@ -1,84 +0,0 @@
/* This file is part of Om. Copyright (C) 2005 Dave Robillard.
*
* Om is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* Om is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef DSSIPLUGIN_H
#define DSSIPLUGIN_H
#include <dssi.h>
#include <lo/lo.h>
#include "LADSPAPlugin.h"
namespace Om {
class MidiMessage;
template <typename T> class InputPort;
/** An instance of a DSSI plugin.
*/
class DSSIPlugin : public LADSPAPlugin
{
public:
DSSIPlugin(const string& name, uint poly, Patch* parent, DSSI_Descriptor* descriptor, samplerate srate, size_t buffer_size);
~DSSIPlugin();
void activate();
void set_ui_url(const string& url);
void send_update();
void set_control(uint port_num, sample val);
void configure(const string& key, const string& val);
void program(int bank, int program);
void run(size_t nframes);
const Plugin* const plugin() const { return m_plugin; }
void plugin(const Plugin* const pi) { m_plugin = pi; }
private:
// Prevent copies
DSSIPlugin(const DSSIPlugin& copy) : LADSPAPlugin(copy) { exit(EXIT_FAILURE); }
DSSIPlugin& operator=(const DSSIPlugin& copy) { exit(EXIT_FAILURE); }
// DSSI GUI messages
void send_control(int port_num, float value);
void send_program(int bank, int value);
void send_configure(const string& key, const string& val);
void send_show();
void send_hide();
void send_quit();
string m_ui_url;
string m_ui_base_path;
lo_address m_ui_addr;
// Current values
int m_bank;
int m_program;
map<string, string> m_configures;
DSSI_Descriptor* m_dssi_descriptor;
InputPort<MidiMessage>* m_midi_in_port;
};
} // namespace Om
#endif // DSSIPLUGIN_H

View File

@ -49,7 +49,8 @@ public:
: _symbol(symbol)
{}
const char* const uri() const { return type_uris[_symbol]; }
const char* const uri() const { return type_uris[_symbol]; }
const Symbol& symbol() const { return _symbol; }
inline bool operator==(const Symbol& symbol) const { return (_symbol == symbol); }
inline bool operator!=(const Symbol& symbol) const { return (_symbol != symbol); }

View File

@ -37,6 +37,7 @@ DuplexPort<T>::DuplexPort(Node* parent, const string& name, size_t index, size_t
, OutputPort<T>(parent, name, index, poly, type, buffer_size)
, _is_output(is_output)
{
assert(TypedPort<T>::_parent == parent);
}
template DuplexPort<sample>::DuplexPort(Node* parent, const string& name, size_t index, size_t poly, DataType type, size_t buffer_size, bool is_output);
template DuplexPort<MidiMessage>::DuplexPort(Node* parent, const string& name, size_t index, size_t poly, DataType type, size_t buffer_size, bool is_output);

View File

@ -48,8 +48,8 @@ public:
: _parent(parent), _name(name)
{
assert(parent == NULL || _name.length() > 0);
assert(parent == NULL || _name.find("/") == string::npos);
assert(((string)path()).find("//") == string::npos);
assert(_name.find("/") == string::npos);
assert(path().find("//") == string::npos);
}
virtual ~GraphObject() {}

View File

@ -235,12 +235,14 @@ InputPort<sample>::process(samplecount nframes)
m_buffers.at(0)->join((*m_connections.begin())->buffer(0));
do_mixdown = false;
}
update_buffers();
} else {
do_mixdown = false;
}
update_buffers();
}
//cerr << path() << " mixing: " << do_mixdown << endl;
if (!do_mixdown) {
assert(m_buffers.at(0)->data() == (*m_connections.begin())->buffer(0)->data());
return;
@ -250,13 +252,14 @@ InputPort<sample>::process(samplecount nframes)
assert(!m_is_tied || m_buffers.at(0)->data() == m_tied_port->buffer(0)->data());*/
for (size_t voice=0; voice < _poly; ++voice) {
// Copy first connection
m_buffers.at(voice)->copy((*m_connections.begin())->buffer(voice), 0, _buffer_size-1);
// Accumulate the rest
if (m_connections.size() > 1) {
// Copy first connection
TypedConnectionListIterator c = m_connections.begin();
// Add all other connections
for (++c; c != m_connections.end(); ++c)
m_buffers.at(voice)->accumulate((*c)->buffer(voice), 0, _buffer_size-1);
}
@ -271,7 +274,7 @@ InputPort<sample>::process(samplecount nframes)
template <>
void
InputPort<MidiMessage>::process(samplecount nframes)
{
{
//assert(!m_is_tied || m_tied_port != NULL);
const size_t num_ins = m_connections.size();

View File

@ -51,6 +51,7 @@ JackAudioPort::JackAudioPort(JackAudioDriver* driver, DuplexPort<sample>* patch_
m_driver(driver),
m_jack_port(NULL),
m_jack_buffer(NULL),
//m_jack_buffer(NULL),
m_patch_port(patch_port)
{
//assert(patch_port->tied_port() != NULL);
@ -61,7 +62,7 @@ JackAudioPort::JackAudioPort(JackAudioDriver* driver, DuplexPort<sample>* patch_
(patch_port->is_input()) ? JackPortIsInput : JackPortIsOutput,
0);
m_jack_buffer = new DriverBuffer<jack_sample_t>(driver->buffer_size());
//m_jack_buffer = new DriverBuffer<jack_sample_t>(driver->buffer_size());
patch_port->fixed_buffers(true);
}
@ -89,22 +90,32 @@ JackAudioPort::remove_from_driver()
void
JackAudioPort::prepare_buffer(jack_nframes_t nframes)
{
// Technically this doesn't need to be done every time for output ports
m_jack_buffer->set_data((jack_default_audio_sample_t*)
// FIXME: Technically this doesn't need to be done every time for output ports
/*m_jack_buffer->set_data((jack_default_audio_sample_t*)
jack_port_get_buffer(m_jack_port, nframes));
m_patch_port->buffer(0)->join(m_jack_buffer);
*/
jack_sample_t* jack_buf = (jack_sample_t*)jack_port_get_buffer(m_jack_port, nframes);
if (jack_buf != m_jack_buffer) {
//cerr << "Jack buffer: " << jack_buf << endl;
m_patch_port->buffer(0)->set_data(jack_buf);
m_jack_buffer = jack_buf;
}
//assert(m_patch_port->tied_port() != NULL);
// FIXME: fixed_buffers switch on/off thing can be removed once this shit
// FIXME: fixed_buffers switch on/off thing can be removed once this
// gets figured out and assertions can go away
m_patch_port->fixed_buffers(false);
m_patch_port->buffer(0)->join(m_jack_buffer);
//m_patch_port->fixed_buffers(false);
//m_patch_port->buffer(0)->join(m_jack_buffer);
//m_patch_port->tied_port()->buffer(0)->join(m_jack_buffer);
m_patch_port->fixed_buffers(true);
//m_patch_port->fixed_buffers(true);
//assert(m_patch_port->buffer(0)->data() == m_patch_port->tied_port()->buffer(0)->data());
assert(m_patch_port->buffer(0)->data() == m_jack_buffer->data());
assert(m_patch_port->buffer(0)->data() == jack_buf);
}
@ -329,7 +340,7 @@ JackAudioDriver::m_process_cb(jack_nframes_t nframes)
// Run root patch
assert(m_root_patch != NULL);
m_root_patch->process(nframes);
return 0;
}

View File

@ -49,8 +49,8 @@ public:
void prepare_buffer(jack_nframes_t nframes);
jack_port_t* jack_port() const { return m_jack_port; }
DriverBuffer<sample>* buffer() const { return m_jack_buffer; }
void jack_buffer(jack_sample_t* s) { m_jack_buffer->set_data(s); }
//DriverBuffer<sample>* buffer() const { return m_jack_buffer; }
//void jack_buffer(jack_sample_t* s) { m_jack_buffer->set_data(s); }
DuplexPort<sample>* patch_port() const { return m_patch_port; }
private:
@ -60,7 +60,8 @@ private:
JackAudioDriver* m_driver;
jack_port_t* m_jack_port;
DriverBuffer<sample>* m_jack_buffer;
jack_sample_t* m_jack_buffer; ///< Cached for output ports
//DriverBuffer<sample>* m_jack_buffer;
DuplexPort<sample>* m_patch_port;
};

View File

@ -185,9 +185,8 @@ LADSPANode::set_port_buffer(size_t voice, size_t port_num, void* buf)
assert(voice < _poly);
// Could be a MIDI port after this
if (port_num < _descriptor->PortCount) {
if (port_num < _descriptor->PortCount)
_descriptor->connect_port(_instances[voice], port_num, (sample*)buf);
}
}
#if 0

View File

@ -136,10 +136,10 @@ private:
List(const List& copy);
List& operator=(const List& copy);
ListNode<T>* m_head;
ListNode<T>* m_tail;
ListNode<T>* m_head;
ListNode<T>* m_tail;
size_t m_size;
iterator m_end_iter;
iterator m_end_iter;
const_iterator m_const_end_iter;
};

View File

@ -348,14 +348,14 @@ Patch::remove_port(const Port* port)
Array<Node*>*
Patch::build_process_order() const
{
Node* node = NULL;
Array<Node*>* const process_order = new Array<Node*>(_nodes.size());
// FIXME: tweak algorithm so it just ends up like this and save the iteration?
for (List<Node*>::const_iterator i = _nodes.begin(); i != _nodes.end(); ++i)
(*i)->traversed(false);
// Traverse backwards starting at outputs
for (List<Port*>::const_iterator p = _output_ports.begin(); p != _output_ports.end(); ++p) {
//for (List<Port*>::const_iterator p = _output_ports.begin(); p != _output_ports.end(); ++p) {
/*const Port* const port = (*p);
for (List<Connection*>::const_iterator c = port->connections().begin();
@ -366,17 +366,32 @@ Patch::build_process_order() const
assert(connection->src_port()->parent_node());
build_process_order_recursive(connection->src_port()->parent_node(), process_order);
}*/
}
//}
// Add any (disjoint) nodes that weren't hit by the traversal
for (List<Node*>::const_iterator i = _nodes.begin(); i != _nodes.end(); ++i) {
/*for (List<Node*>::const_iterator i = _nodes.begin(); i != _nodes.end(); ++i) {
node = (*i);
if ( ! node->traversed()) {
process_order->push_back(*i);
node->traversed(true);
}
}*/
for (List<Node*>::const_iterator i = _nodes.begin(); i != _nodes.end(); ++i) {
Node* const node = (*i);
// Either a sink or connected to our output ports:
if ( (!node->traversed()) && node->dependants()->size() == 0)
build_process_order_recursive(node, process_order);
}
cerr << "----------------------------------------\n";
for (size_t i=0; i < process_order->size(); ++i) {
assert(process_order->at(i));
cerr << process_order->at(i)->path() << endl;
}
cerr << "----------------------------------------\n";
assert(process_order->size() == _nodes.size());
return process_order;

View File

@ -34,15 +34,20 @@ template <typename T>
TypedConnection<T>::TypedConnection(OutputPort<T>* const src_port, InputPort<T>* const dst_port)
: Connection(src_port, dst_port),
m_local_buffer(NULL),
m_is_poly_to_mono( (src_port->parent_node()->poly() > dst_port->parent_node()->poly()) ),
m_must_mix(true),
m_buffer_size(src_port->buffer_size()),
m_pending_disconnection(false)
{
assert((src_port->parent_node()->poly() == dst_port->parent_node()->poly())
|| (src_port->parent_node()->poly() == 1 || dst_port->parent_node()->poly() == 1));
if (m_is_poly_to_mono) // Poly -> Mono connection, need a buffer to mix in to
if (src_port->poly() == dst_port->poly())
m_must_mix = false;
else // Poly -> Mono connection, need a buffer to mix into
m_local_buffer = new Buffer<T>(m_buffer_size);
//cerr << "Connection " << src_port->path() << " -> " << dst_port->path()
// << "\t mixing: " << m_must_mix << endl;
}
template TypedConnection<sample>::TypedConnection(OutputPort<sample>* const src_port, InputPort<sample>* const dst_port);
template TypedConnection<MidiMessage>::TypedConnection(OutputPort<MidiMessage>* const src_port, InputPort<MidiMessage>* const dst_port);
@ -71,7 +76,10 @@ TypedConnection<sample>::process(samplecount nframes)
* would avoid having to mix multiple times. Probably not a very common
* case, but it would be faster anyway. */
if (m_is_poly_to_mono) {
if (m_must_mix) {
//cerr << "Mixing " << src_port()->buffer(0)->data()
// << " -> " << m_local_buffer->data() << endl;
m_local_buffer->copy(src_port()->buffer(0), 0, m_buffer_size-1);
// Mix all the source's voices down into local buffer starting at the second

View File

@ -61,7 +61,7 @@ private:
TypedConnection& operator=(const TypedConnection&);
Buffer<T>* m_local_buffer; ///< Only used for poly->mono connections
bool m_is_poly_to_mono;
bool m_must_mix;
size_t m_buffer_size;
bool m_pending_disconnection;
};
@ -73,7 +73,7 @@ TypedConnection<sample>::buffer(size_t voice) const
{
TypedPort<sample>* const src_port = (TypedPort<sample>*)m_src_port;
if (m_is_poly_to_mono) {
if (m_must_mix) {
return m_local_buffer;
} else {
if (src_port->poly() == 1)

View File

@ -98,21 +98,14 @@ template void TypedPort<sample>::allocate_buffers();
template void TypedPort<MidiMessage>::allocate_buffers();
/*
template<>
template <typename T>
void
TypedPort<sample>::process(samplecount nframes)
TypedPort<T>::process(samplecount nframes)
{
for (size_t i=0; i < _poly; ++i)
m_buffers.at(i)->prepare(nframes);
}
template<>
void
TypedPort<MidiMessage>::process(samplecount nframes)
{
}
*/
template<typename T>
void

View File

@ -49,7 +49,7 @@ public:
Buffer<T>* buffer(size_t voice) const { return m_buffers.at(voice); }
virtual void process(samplecount nframes) {}
virtual void process(samplecount nframes);
virtual void clear_buffers();
//TypedPort* tied_port() const { return m_tied_port; }

View File

@ -29,7 +29,10 @@
#include "util/Path.h"
#include "Port.h"
#include "AudioDriver.h"
#include "MidiDriver.h"
#include "List.h"
#include "Driver.h"
#include "DuplexPort.h"
namespace Om {
@ -41,7 +44,8 @@ AddPortEvent::AddPortEvent(CountedPtr<Responder> responder, const string& path,
_is_output(is_output),
_data_type(DataType::UNKNOWN),
_patch(NULL),
_port(NULL),
_patch_port(NULL),
_driver_port(NULL),
_succeeded(true)
{
string type_str;
@ -52,11 +56,6 @@ AddPortEvent::AddPortEvent(CountedPtr<Responder> responder, const string& path,
}
AddPortEvent::~AddPortEvent()
{
}
void
AddPortEvent::pre_process()
{
@ -65,6 +64,8 @@ AddPortEvent::pre_process()
return;
}
// FIXME: this is just a mess :/
_patch = om->object_store()->find_patch(_path.parent());
if (_patch != NULL) {
@ -74,15 +75,24 @@ AddPortEvent::pre_process()
if (_type == "AUDIO" || _type == "MIDI")
buffer_size = om->audio_driver()->buffer_size();
_port = _patch->create_port(_path.name(), _data_type, buffer_size, _is_output);
if (_port) {
_patch_port = _patch->create_port(_path.name(), _data_type, buffer_size, _is_output);
if (_patch_port) {
if (_is_output)
_patch->add_output(new ListNode<Port*>(_port));
_patch->add_output(new ListNode<Port*>(_patch_port));
else
_patch->add_input(new ListNode<Port*>(_port));
_patch->add_input(new ListNode<Port*>(_patch_port));
_ports_array = new Array<Port*>(_patch->num_ports() + 1, _patch->external_ports());
_ports_array->at(_patch->num_ports()) = _port;
om->object_store()->add(_port);
_ports_array->at(_patch->num_ports()) = _patch_port;
om->object_store()->add(_patch_port);
if (!_patch->parent()) {
if (_type == "AUDIO")
_driver_port = om->audio_driver()->create_port(
dynamic_cast<DuplexPort<sample>*>(_patch_port));
else if (_type == "MIDI")
_driver_port = om->midi_driver()->create_port(
dynamic_cast<DuplexPort<MidiMessage>*>(_patch_port));
}
}
}
QueuedEvent::pre_process();
@ -94,11 +104,14 @@ AddPortEvent::execute(samplecount offset)
{
QueuedEvent::execute(offset);
if (_port) {
if (_patch_port) {
om->maid()->push(_patch->external_ports());
//_patch->add_port(_port);
_patch->external_ports(_ports_array);
}
if (_driver_port)
_driver_port->add_to_driver();
}
@ -110,7 +123,7 @@ AddPortEvent::post_process()
m_responder->respond_error(msg);
} else {
m_responder->respond_ok();
om->client_broadcaster()->send_port(_port);
om->client_broadcaster()->send_port(_patch_port);
}
}

View File

@ -31,6 +31,7 @@ namespace Om {
class Patch;
class Port;
class Plugin;
class DriverPort;
/** An event to add a Port to a Patch.
@ -41,7 +42,6 @@ class AddPortEvent : public QueuedEvent
{
public:
AddPortEvent(CountedPtr<Responder> responder, const string& path, const string& type, bool is_output);
~AddPortEvent();
void pre_process();
void execute(samplecount offset);
@ -53,18 +53,10 @@ private:
bool _is_output;
DataType _data_type;
Patch* _patch;
Port* _port;
Array<Port*>* _ports_array; // New (external) ports array for Patch
Port* _patch_port;
Array<Port*>* _ports_array; ///< New (external) ports array for Patch
DriverPort* _driver_port; ///< Driver (eg Jack) port if this is a toplevel port
bool _succeeded;
/*
string m_patch_name;
Path m_path;
Plugin* m_plugin;
bool m_poly;
Patch* m_patch;
Node* m_node;
Array<Node*>* m_process_order; // Patch's new process order
bool m_node_already_exists;*/
};

View File

@ -212,8 +212,12 @@ TypedConnectionEvent<T>::pre_process()
m_port_listnode = new ListNode<TypedConnection<T>*>(m_connection);
m_patch_listnode = new ListNode<Connection*>(m_connection);
dst_node->providers()->push_back(new ListNode<Node*>(src_node));
src_node->dependants()->push_back(new ListNode<Node*>(dst_node));
// Need to be careful about patch port connections here and adding a node's
// parent as a dependant/provider...
if (src_node->parent() == dst_node->parent()) {
dst_node->providers()->push_back(new ListNode<Node*>(src_node));
src_node->dependants()->push_back(new ListNode<Node*>(dst_node));
}
if (m_patch->process())
m_process_order = m_patch->build_process_order();