summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2007-07-06 00:09:53 +0000
committerDavid Robillard <d@drobilla.net>2007-07-06 00:09:53 +0000
commit19273e824d40534a4e31259fb8b83122b24aa4e9 (patch)
tree1ee44d60b2776dcd05b3eede0d9f8b5c21ac89ca /libs
parent40f353f1519e334a15721d7bc64fd5e7ef9e5437 (diff)
Midi CC automation sending (send points only, no linear interpolation yet).
Split buffer.cc into buffer.cc audio_buffer.cc midi_buffer.cc. Renamed 'send_buffers' to 'mix_buffers'. This is the first revision of Ardour where clicking around and drawing things can send MIDI and thus generate wonderful world-changing music. Break out the champagne. git-svn-id: svn://localhost/ardour2/trunk@2115 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs')
-rw-r--r--libs/ardour/SConscript2
-rw-r--r--libs/ardour/ardour/automation_event.h16
-rw-r--r--libs/ardour/ardour/midi_buffer.h5
-rw-r--r--libs/ardour/ardour/midi_track.h4
-rw-r--r--libs/ardour/ardour/session.h4
-rw-r--r--libs/ardour/audio_buffer.cc56
-rw-r--r--libs/ardour/automation_event.cc117
-rw-r--r--libs/ardour/buffer.cc187
-rw-r--r--libs/ardour/midi_buffer.cc255
-rw-r--r--libs/ardour/midi_track.cc80
-rw-r--r--libs/ardour/send.cc2
-rw-r--r--libs/ardour/session.cc22
12 files changed, 519 insertions, 231 deletions
diff --git a/libs/ardour/SConscript b/libs/ardour/SConscript
index f93c19649f..5d56e198fb 100644
--- a/libs/ardour/SConscript
+++ b/libs/ardour/SConscript
@@ -46,6 +46,8 @@ audio_port.cc
midi_port.cc
port_set.cc
buffer.cc
+audio_buffer.cc
+midi_buffer.cc
buffer_set.cc
meter.cc
amp.cc
diff --git a/libs/ardour/ardour/automation_event.h b/libs/ardour/ardour/automation_event.h
index fb4aa52ec2..8a9b110714 100644
--- a/libs/ardour/ardour/automation_event.h
+++ b/libs/ardour/ardour/automation_event.h
@@ -191,10 +191,20 @@ class AutomationList : public PBD::StatefulDestructible
}
};
+ /** Lookup cache for eval functions, range contains equivalent values */
struct LookupCache {
+ LookupCache() : left(-1) {}
double left; /* leftmost x coordinate used when finding "range" */
std::pair<AutomationList::const_iterator,AutomationList::const_iterator> range;
};
+
+ /** Lookup cache for point finding, range contains points between left and right */
+ struct SearchCache {
+ SearchCache() : left(-1), right(-1) {}
+ double left; /* leftmost x coordinate used when finding "range" */
+ double right; /* rightmost x coordinate used when finding "range" */
+ std::pair<AutomationList::const_iterator,AutomationList::const_iterator> range;
+ };
static sigc::signal<void, AutomationList*> AutomationListCreated;
@@ -205,6 +215,7 @@ class AutomationList : public PBD::StatefulDestructible
mutable sigc::signal<void> Dirty;
Glib::Mutex& lock() const { return _lock; }
LookupCache& lookup_cache() const { return _lookup_cache; }
+ SearchCache& search_cache() const { return _search_cache; }
/** Called by locked entry point and various private
* locations where we already hold the lock.
@@ -212,6 +223,8 @@ class AutomationList : public PBD::StatefulDestructible
* FIXME: Should this be private? Curve needs it..
*/
double unlocked_eval (double x) const;
+
+ bool rt_safe_earliest_event (double start, double end, double& x, double& y) const;
Curve& curve() { return *_curve; }
const Curve& curve() const { return *_curve; }
@@ -220,7 +233,7 @@ class AutomationList : public PBD::StatefulDestructible
/** Called by unlocked_eval() to handle cases of 3 or more control points.
*/
- virtual double multipoint_eval (double x) const;
+ double multipoint_eval (double x) const;
AutomationList* cut_copy_clear (double, double, int op);
@@ -231,6 +244,7 @@ class AutomationList : public PBD::StatefulDestructible
void _x_scale (double factor);
mutable LookupCache _lookup_cache;
+ mutable SearchCache _search_cache;
Parameter _parameter;
EventList _events;
diff --git a/libs/ardour/ardour/midi_buffer.h b/libs/ardour/ardour/midi_buffer.h
index f7223fcecd..cf2f92ff79 100644
--- a/libs/ardour/ardour/midi_buffer.h
+++ b/libs/ardour/ardour/midi_buffer.h
@@ -30,12 +30,13 @@ class MidiBuffer : public Buffer
{
public:
MidiBuffer(size_t capacity);
-
~MidiBuffer();
void silence(nframes_t dur, nframes_t offset=0);
void read_from(const Buffer& src, nframes_t nframes, nframes_t offset);
+
+ void copy(const MidiBuffer& copy);
bool push_back(const ARDOUR::MidiEvent& event);
bool push_back(const jack_midi_event_t& event);
@@ -46,6 +47,8 @@ public:
static size_t max_event_size() { return MAX_EVENT_SIZE; }
+ bool merge(const MidiBuffer& a, const MidiBuffer& b);
+
private:
// FIXME: Jack needs to tell us this
static const size_t MAX_EVENT_SIZE = 4; // bytes
diff --git a/libs/ardour/ardour/midi_track.h b/libs/ardour/ardour/midi_track.h
index 8b4706f7b3..a6e28405f8 100644
--- a/libs/ardour/ardour/midi_track.h
+++ b/libs/ardour/ardour/midi_track.h
@@ -91,6 +91,10 @@ protected:
int _set_state (const XMLNode&, bool call_base);
private:
+
+ void write_controller_messages(MidiBuffer& buf,
+ nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset);
+
int set_diskstream (boost::shared_ptr<MidiDiskstream> ds);
void set_state_part_two ();
void set_state_part_three ();
diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h
index 2c4c60a911..8a51653968 100644
--- a/libs/ardour/ardour/session.h
+++ b/libs/ardour/ardour/session.h
@@ -272,7 +272,7 @@ class Session : public PBD::StatefulDestructible
BufferSet& get_silent_buffers (ChanCount count = ChanCount::ZERO);
BufferSet& get_scratch_buffers (ChanCount count = ChanCount::ZERO);
- BufferSet& get_send_buffers (ChanCount count = ChanCount::ZERO);
+ BufferSet& get_mix_buffers (ChanCount count = ChanCount::ZERO);
void add_diskstream (boost::shared_ptr<Diskstream>);
boost::shared_ptr<Diskstream> diskstream_by_id (const PBD::ID& id);
@@ -986,7 +986,7 @@ class Session : public PBD::StatefulDestructible
nframes_t last_stop_frame;
BufferSet* _scratch_buffers;
BufferSet* _silent_buffers;
- BufferSet* _send_buffers;
+ BufferSet* _mix_buffers;
nframes_t current_block_size;
nframes_t _worst_output_latency;
nframes_t _worst_input_latency;
diff --git a/libs/ardour/audio_buffer.cc b/libs/ardour/audio_buffer.cc
new file mode 100644
index 0000000000..059b61ed2f
--- /dev/null
+++ b/libs/ardour/audio_buffer.cc
@@ -0,0 +1,56 @@
+/*
+ Copyright (C) 2006-2007 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <ardour/audio_buffer.h>
+
+#ifdef __x86_64__
+static const int CPU_CACHE_ALIGN = 64;
+#else
+static const int CPU_CACHE_ALIGN = 16; /* arguably 32 on most arches, but it matters less */
+#endif
+
+namespace ARDOUR {
+
+
+AudioBuffer::AudioBuffer(size_t capacity)
+ : Buffer(DataType::AUDIO, capacity)
+ , _owns_data(false)
+ , _data(NULL)
+{
+ _size = capacity; // For audio buffers, size = capacity (always)
+ if (capacity > 0) {
+#ifdef NO_POSIX_MEMALIGN
+ _data = (Sample *) malloc(sizeof(Sample) * capacity);
+#else
+ posix_memalign((void**)&_data, CPU_CACHE_ALIGN, sizeof(Sample) * capacity);
+#endif
+ assert(_data);
+ _owns_data = true;
+ clear();
+ }
+}
+
+AudioBuffer::~AudioBuffer()
+{
+ if (_owns_data)
+ free(_data);
+}
+
+
+} // namespace ARDOUR
+
diff --git a/libs/ardour/automation_event.cc b/libs/ardour/automation_event.cc
index 09e4a926e8..2bb987ad24 100644
--- a/libs/ardour/automation_event.cc
+++ b/libs/ardour/automation_event.cc
@@ -71,6 +71,8 @@ AutomationList::AutomationList (Parameter id, double min_val, double max_val, do
_rt_insertion_point = _events.end();
_lookup_cache.left = -1;
_lookup_cache.range.first = _events.end();
+ _search_cache.left = -1;
+ _search_cache.range.first = _events.end();
_sort_pending = false;
assert(_parameter.type() != NullAutomation);
@@ -91,8 +93,8 @@ AutomationList::AutomationList (const AutomationList& other)
_state = other._state;
_touching = other._touching;
_rt_insertion_point = _events.end();
- _lookup_cache.left = -1;
_lookup_cache.range.first = _events.end();
+ _search_cache.range.first = _events.end();
_sort_pending = false;
for (const_iterator i = other._events.begin(); i != other._events.end(); ++i) {
@@ -118,8 +120,8 @@ AutomationList::AutomationList (const AutomationList& other, double start, doubl
_state = other._state;
_touching = other._touching;
_rt_insertion_point = _events.end();
- _lookup_cache.left = -1;
_lookup_cache.range.first = _events.end();
+ _search_cache.range.first = _events.end();
_sort_pending = false;
/* now grab the relevant points, and shift them back if necessary */
@@ -127,7 +129,7 @@ AutomationList::AutomationList (const AutomationList& other, double start, doubl
AutomationList* section = const_cast<AutomationList*>(&other)->copy (start, end);
if (!section->empty()) {
- for (AutomationList::iterator i = section->begin(); i != section->end(); ++i) {
+ for (iterator i = section->begin(); i != section->end(); ++i) {
_events.push_back (new ControlEvent ((*i)->when, (*i)->value));
}
}
@@ -155,8 +157,8 @@ AutomationList::AutomationList (const XMLNode& node, Parameter id)
_state = Off;
_style = Absolute;
_rt_insertion_point = _events.end();
- _lookup_cache.left = -1;
_lookup_cache.range.first = _events.end();
+ _search_cache.range.first = _events.end();
_sort_pending = false;
set_state (node);
@@ -283,7 +285,7 @@ AutomationList::extend_to (double when)
void AutomationList::_x_scale (double factor)
{
- for (AutomationList::iterator i = _events.begin(); i != _events.end(); ++i) {
+ for (iterator i = _events.begin(); i != _events.end(); ++i) {
(*i)->when = floor ((*i)->when * factor);
}
@@ -334,17 +336,17 @@ AutomationList::rt_add (double when, double value)
++far;
}
- if(_new_touch) {
- where = far;
- _rt_insertion_point = where;
-
- if((*where)->when == when) {
- (*where)->value = value;
- done = true;
- }
- } else {
- where = _events.erase (after, far);
- }
+ if (_new_touch) {
+ where = far;
+ _rt_insertion_point = where;
+
+ if ((*where)->when == when) {
+ (*where)->value = value;
+ done = true;
+ }
+ } else {
+ where = _events.erase (after, far);
+ }
} else {
@@ -432,7 +434,7 @@ AutomationList::add (double when, double value)
}
void
-AutomationList::erase (AutomationList::iterator i)
+AutomationList::erase (iterator i)
{
{
Glib::Mutex::Lock lm (_lock);
@@ -444,7 +446,7 @@ AutomationList::erase (AutomationList::iterator i)
}
void
-AutomationList::erase (AutomationList::iterator start, AutomationList::iterator end)
+AutomationList::erase (iterator start, iterator end)
{
{
Glib::Mutex::Lock lm (_lock);
@@ -673,6 +675,7 @@ void
AutomationList::mark_dirty ()
{
_lookup_cache.left = -1;
+ _search_cache.left = -1;
Dirty (); /* EMIT SIGNAL */
}
@@ -782,7 +785,7 @@ AutomationList::truncate_start (double overall_length)
{
{
Glib::Mutex::Lock lm (_lock);
- AutomationList::iterator i;
+ iterator i;
double first_legal_value;
double first_legal_coordinate;
@@ -947,27 +950,24 @@ AutomationList::unlocked_eval (double x) const
double
AutomationList::multipoint_eval (double x) const
{
- pair<AutomationList::const_iterator,AutomationList::const_iterator> range;
double upos, lpos;
double uval, lval;
double fraction;
- /* only do the range lookup if x is in a different range than last time
- this was called (or if the lookup cache has been marked "dirty" (left<0)
- */
-
+ /* Only do the range lookup if x is in a different range than last time
+ * this was called (or if the lookup cache has been marked "dirty" (left<0) */
if ((_lookup_cache.left < 0) ||
((_lookup_cache.left > x) ||
(_lookup_cache.range.first == _events.end()) ||
((*_lookup_cache.range.second)->when < x))) {
- ControlEvent cp (x, 0);
+ const ControlEvent cp (x, 0);
TimeComparator cmp;
_lookup_cache.range = equal_range (_events.begin(), _events.end(), &cp, cmp);
}
- range = _lookup_cache.range;
+ pair<const_iterator,const_iterator> range = _lookup_cache.range;
if (range.first == range.second) {
@@ -1007,6 +1007,71 @@ AutomationList::multipoint_eval (double x) const
return (*range.first)->value;
}
+/** Get the earliest event between \a start and \a end.
+ *
+ * If an event is found, \a x and \a y are set to its coordinates.
+ * \return true if event is found (and \a x and \a y are valid).
+ */
+bool
+AutomationList::rt_safe_earliest_event (double start, double end, double& x, double& y) const
+{
+ // FIXME: It would be nice if this was unnecessary..
+ Glib::Mutex::Lock lm(_lock, Glib::TRY_LOCK);
+ if (!lm.locked()) {
+ return false;
+ }
+
+ /* Only do the range lookup if x is in a different range than last time
+ * this was called (or if the search cache has been marked "dirty" (left<0) */
+ if ((_search_cache.left < 0) ||
+ ((_search_cache.left > start) ||
+ (_search_cache.right < end))) {
+
+ const ControlEvent start_point (start, 0);
+ const ControlEvent end_point (end, 0);
+ TimeComparator cmp;
+
+ //cerr << "REBUILD: (" << _search_cache.left << ".." << _search_cache.right << ") -> ("
+ // << start << ".." << end << ")" << endl;
+
+ _search_cache.range.first = lower_bound (_events.begin(), _events.end(), &start_point, cmp);
+ _search_cache.range.second = upper_bound (_events.begin(), _events.end(), &end_point, cmp);
+
+ _search_cache.left = start;
+ _search_cache.right = end;
+ }
+
+ pair<const_iterator,const_iterator> range = _search_cache.range;
+
+ if (range.first != _events.end()) {
+ const ControlEvent* const first = *range.first;
+
+ /* Earliest points is in range, return it */
+ if (first->when >= start && first->when < end) {
+
+ x = first->when;
+ y = first->value;
+
+ /* Move left of cache to this point
+ * (Optimize for immediate call this cycle within range) */
+ _search_cache.left = x;
+ ++_search_cache.range.first;
+
+ assert(x >= start);
+ assert(x < end);
+ return true;
+
+ } else {
+
+ return false;
+ }
+
+ /* No points in range */
+ } else {
+ return false;
+ }
+}
+
AutomationList*
AutomationList::cut (iterator start, iterator end)
{
diff --git a/libs/ardour/buffer.cc b/libs/ardour/buffer.cc
index c91d09d1a8..8abe238a47 100644
--- a/libs/ardour/buffer.cc
+++ b/libs/ardour/buffer.cc
@@ -16,10 +16,6 @@
675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <algorithm>
-#include <iostream>
-using std::cerr; using std::endl;
-
#include <ardour/buffer.h>
#include <ardour/audio_buffer.h>
#include <ardour/midi_buffer.h>
@@ -45,188 +41,5 @@ Buffer::create(DataType type, size_t capacity)
}
-AudioBuffer::AudioBuffer(size_t capacity)
- : Buffer(DataType::AUDIO, capacity)
- , _owns_data(false)
- , _data(NULL)
-{
- _size = capacity; // For audio buffers, size = capacity (always)
- if (capacity > 0) {
-#ifdef NO_POSIX_MEMALIGN
- _data = (Sample *) malloc(sizeof(Sample) * capacity);
-#else
- posix_memalign((void**)&_data, CPU_CACHE_ALIGN, sizeof(Sample) * capacity);
-#endif
- assert(_data);
- _owns_data = true;
- clear();
- }
-}
-
-AudioBuffer::~AudioBuffer()
-{
- if (_owns_data)
- free(_data);
-}
-
-// FIXME: mirroring for MIDI buffers?
-MidiBuffer::MidiBuffer(size_t capacity)
- : Buffer(DataType::MIDI, capacity)
-// , _owns_data(true)
- , _events(NULL)
- , _data(NULL)
-{
- assert(capacity > 0);
-
- _size = 0;
-
-#ifdef NO_POSIX_MEMALIGN
- _events = (MidiEvent *) malloc(sizeof(MidiEvent) * capacity);
- _data = (Byte *) malloc(sizeof(Byte) * capacity * MAX_EVENT_SIZE);
-#else
- posix_memalign((void**)&_events, CPU_CACHE_ALIGN, sizeof(MidiEvent) * capacity);
- posix_memalign((void**)&_data, CPU_CACHE_ALIGN, sizeof(Byte) * capacity * MAX_EVENT_SIZE);
-#endif
- assert(_data);
- assert(_events);
- silence(_capacity);
-}
-
-MidiBuffer::~MidiBuffer()
-{
- free(_events);
- free(_data);
-}
-
-
-/** Read events from @a src starting at time @a offset into the START of this buffer, for
- * time direction @a nframes. Relative time, where 0 = start of buffer.
- *
- * Note that offset and nframes refer to sample time, NOT buffer offsets or event counts.
- */
-void
-MidiBuffer::read_from(const Buffer& src, nframes_t nframes, nframes_t offset)
-{
- assert(src.type() == DataType::MIDI);
- const MidiBuffer& msrc = (MidiBuffer&)src;
-
- assert(_capacity >= src.size());
-
- clear();
- assert(_size == 0);
-
- // FIXME: slow
- for (size_t i=0; i < src.size(); ++i) {
- const MidiEvent& ev = msrc[i];
- if (ev.time >= offset && ev.time < offset+nframes) {
- push_back(ev);
- }
- }
-
- _silent = src.silent();
-}
-
-
-/** Push an event into the buffer.
- *
- * Note that the raw MIDI pointed to by ev will be COPIED and unmodified.
- * That is, the caller still owns it, if it needs freeing it's Not My Problem(TM).
- * Realtime safe.
- * @return false if operation failed (not enough room)
- */
-bool
-MidiBuffer::push_back(const MidiEvent& ev)
-{
- if (_size == _capacity)
- return false;
-
- Byte* const write_loc = _data + (_size * MAX_EVENT_SIZE);
-
- memcpy(write_loc, ev.buffer, ev.size);
- _events[_size] = ev;
- _events[_size].buffer = write_loc;
- ++_size;
-
- //cerr << "MidiBuffer: pushed, size = " << _size << endl;
-
- _silent = false;
-
- return true;
-}
-
-
-/** Push an event into the buffer.
- *
- * Note that the raw MIDI pointed to by ev will be COPIED and unmodified.
- * That is, the caller still owns it, if it needs freeing it's Not My Problem(TM).
- * Realtime safe.
- * @return false if operation failed (not enough room)
- */
-bool
-MidiBuffer::push_back(const jack_midi_event_t& ev)
-{
- if (_size == _capacity)
- return false;
-
- Byte* const write_loc = _data + (_size * MAX_EVENT_SIZE);
-
- memcpy(write_loc, ev.buffer, ev.size);
- _events[_size].time = (double)ev.time;
- _events[_size].size = ev.size;
- _events[_size].buffer = write_loc;
- ++_size;
-
- //cerr << "MidiBuffer: pushed, size = " << _size << endl;
-
- _silent = false;
-
- return true;
-}
-
-
-/** Reserve space for a new event in the buffer.
- *
- * This call is for copying MIDI directly into the buffer, the data location
- * (of sufficient size to write \a size bytes) is returned, or NULL on failure.
- * This call MUST be immediately followed by a write to the returned data
- * location, or the buffer will be corrupted and very nasty things will happen.
- */
-Byte*
-MidiBuffer::reserve(double time, size_t size)
-{
- assert(size < MAX_EVENT_SIZE);
-
- if (_size == _capacity)
- return NULL;
-
- Byte* const write_loc = _data + (_size * MAX_EVENT_SIZE);
-
- _events[_size].time = time;
- _events[_size].size = size;
- _events[_size].buffer = write_loc;
- ++_size;
-
- //cerr << "MidiBuffer: reserved, size = " << _size << endl;
-
- _silent = false;
-
- return write_loc;
-}
-
-
-void
-MidiBuffer::silence(nframes_t dur, nframes_t offset)
-{
- // FIXME use parameters
- assert(offset == 0);
- //assert(dur == _capacity);
-
- memset(_events, 0, sizeof(MidiEvent) * _capacity);
- memset(_data, 0, sizeof(Byte) * _capacity * MAX_EVENT_SIZE);
- _size = 0;
- _silent = true;
-}
-
-
} // namespace ARDOUR
diff --git a/libs/ardour/midi_buffer.cc b/libs/ardour/midi_buffer.cc
new file mode 100644
index 0000000000..5376886a5d
--- /dev/null
+++ b/libs/ardour/midi_buffer.cc
@@ -0,0 +1,255 @@
+/*
+ Copyright (C) 2006-2007 Paul Davis
+ Author: Dave Robillard
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <iostream>
+#include <ardour/midi_buffer.h>
+
+#ifdef __x86_64__
+static const int CPU_CACHE_ALIGN = 64;
+#else
+static const int CPU_CACHE_ALIGN = 16; /* arguably 32 on most arches, but it matters less */
+#endif
+
+using namespace std;
+
+namespace ARDOUR {
+
+
+// FIXME: mirroring for MIDI buffers?
+MidiBuffer::MidiBuffer(size_t capacity)
+ : Buffer(DataType::MIDI, capacity)
+// , _owns_data(true)
+ , _events(NULL)
+ , _data(NULL)
+{
+ assert(capacity > 0);
+
+ _size = 0;
+
+#ifdef NO_POSIX_MEMALIGN
+ _events = (MidiEvent *) malloc(sizeof(MidiEvent) * capacity);
+ _data = (Byte *) malloc(sizeof(Byte) * capacity * MAX_EVENT_SIZE);
+#else
+ posix_memalign((void**)&_events, CPU_CACHE_ALIGN, sizeof(MidiEvent) * capacity);
+ posix_memalign((void**)&_data, CPU_CACHE_ALIGN, sizeof(Byte) * capacity * MAX_EVENT_SIZE);
+#endif
+ assert(_data);
+ assert(_events);
+ silence(_capacity);
+}
+
+void
+MidiBuffer::copy(const MidiBuffer& copy)
+{
+ assert(_capacity >= copy._capacity);
+ _size = 0;
+
+ for (size_t i=0; i < copy.size(); ++i)
+ push_back(copy[i]);
+}
+
+MidiBuffer::~MidiBuffer()
+{
+ free(_events);
+ free(_data);
+}
+
+
+/** Read events from @a src starting at time @a offset into the START of this buffer, for
+ * time direction @a nframes. Relative time, where 0 = start of buffer.
+ *
+ * Note that offset and nframes refer to sample time, NOT buffer offsets or event counts.
+ */
+void
+MidiBuffer::read_from(const Buffer& src, nframes_t nframes, nframes_t offset)
+{
+ assert(src.type() == DataType::MIDI);
+ const MidiBuffer& msrc = (MidiBuffer&)src;
+
+ assert(_capacity >= src.size());
+
+ clear();
+ assert(_size == 0);
+
+ // FIXME: slow
+ for (size_t i=0; i < src.size(); ++i) {
+ const MidiEvent& ev = msrc[i];
+ if (ev.time >= offset && ev.time < offset+nframes) {
+ push_back(ev);
+ }
+ }
+
+ _silent = src.silent();
+}
+
+
+/** Push an event into the buffer.
+ *
+ * Note that the raw MIDI pointed to by ev will be COPIED and unmodified.
+ * That is, the caller still owns it, if it needs freeing it's Not My Problem(TM).
+ * Realtime safe.
+ * @return false if operation failed (not enough room)
+ */
+bool
+MidiBuffer::push_back(const MidiEvent& ev)
+{
+ if (_size == _capacity)
+ return false;
+
+ Byte* const write_loc = _data + (_size * MAX_EVENT_SIZE);
+
+ memcpy(write_loc, ev.buffer, ev.size);
+ _events[_size] = ev;
+ _events[_size].buffer = write_loc;
+ ++_size;
+
+ //cerr << "MidiBuffer: pushed, size = " << _size << endl;
+
+ _silent = false;
+
+ return true;
+}
+
+
+/** Push an event into the buffer.
+ *
+ * Note that the raw MIDI pointed to by ev will be COPIED and unmodified.
+ * That is, the caller still owns it, if it needs freeing it's Not My Problem(TM).
+ * Realtime safe.
+ * @return false if operation failed (not enough room)
+ */
+bool
+MidiBuffer::push_back(const jack_midi_event_t& ev)
+{
+ if (_size == _capacity)
+ return false;
+
+ Byte* const write_loc = _data + (_size * MAX_EVENT_SIZE);
+
+ memcpy(write_loc, ev.buffer, ev.size);
+ _events[_size].time = (double)ev.time;
+ _events[_size].size = ev.size;
+ _events[_size].buffer = write_loc;
+ ++_size;
+
+ //cerr << "MidiBuffer: pushed, size = " << _size << endl;
+
+ _silent = false;
+
+ return true;
+}
+
+
+/** Reserve space for a new event in the buffer.
+ *
+ * This call is for copying MIDI directly into the buffer, the data location
+ * (of sufficient size to write \a size bytes) is returned, or NULL on failure.
+ * This call MUST be immediately followed by a write to the returned data
+ * location, or the buffer will be corrupted and very nasty things will happen.
+ */
+Byte*
+MidiBuffer::reserve(double time, size_t size)
+{
+ assert(size < MAX_EVENT_SIZE);
+
+ if (_size == _capacity)
+ return NULL;
+
+ Byte* const write_loc = _data + (_size * MAX_EVENT_SIZE);
+
+ _events[_size].time = time;
+ _events[_size].size = size;
+ _events[_size].buffer = write_loc;
+ ++_size;
+
+ //cerr << "MidiBuffer: reserved, size = " << _size << endl;
+
+ _silent = false;
+
+ return write_loc;
+}
+
+
+void
+MidiBuffer::silence(nframes_t dur, nframes_t offset)
+{
+ // FIXME use parameters
+ assert(offset == 0);
+ //assert(dur == _capacity);
+
+ memset(_events, 0, sizeof(MidiEvent) * _capacity);
+ memset(_data, 0, sizeof(Byte) * _capacity * MAX_EVENT_SIZE);
+ _size = 0;
+ _silent = true;
+}
+
+
+/** Clear, and merge \a a and \a b into this buffer.
+ *
+ * FIXME: This is slow.
+ *
+ * \return true if complete merge was successful
+ */
+bool
+MidiBuffer::merge(const MidiBuffer& a, const MidiBuffer& b)
+{
+ _size = 0;
+
+ // Die if a merge isn't necessary as it's expensive
+ assert(a.size() > 0 && b.size() > 0);
+
+ size_t a_index = 0;
+ size_t b_index = 0;
+ size_t count = a.size() + b.size();
+
+ while (count > 0 && a_index < a.size() && b_index < b.size()) {
+
+ if (size() == capacity()) {
+ cerr << "WARNING: MIDI buffer overrun, events lost!" << endl;
+ return false;
+ }
+
+ if (a_index == a.size()) {
+ push_back(a[a_index]);
+ ++a_index;
+ } else if (b_index == b.size()) {
+ push_back(b[b_index]);
+ ++b_index;
+ } else {
+ const MidiEvent& a_ev = a[a_index];
+ const MidiEvent& b_ev = b[b_index];
+
+ if (a_ev.time <= b_ev.time) {
+ push_back(a_ev);
+ ++a_index;
+ } else {
+ push_back(b_ev);
+ ++b_index;
+ }
+ }
+
+ --count;
+ }
+
+ return true;
+}
+
+
+} // namespace ARDOUR
+
diff --git a/libs/ardour/midi_track.cc b/libs/ardour/midi_track.cc
index 00b76eb08a..aa23e3d92b 100644
--- a/libs/ardour/midi_track.cc
+++ b/libs/ardour/midi_track.cc
@@ -72,6 +72,8 @@ MidiTrack::MidiTrack (Session& sess, string name, Route::Flag flag, TrackMode mo
set_input_maximum(ChanCount(DataType::MIDI, 1));
set_output_minimum(ChanCount(DataType::MIDI, 1));
set_output_maximum(ChanCount(DataType::MIDI, 1));
+
+ MoreChannels(ChanCount(DataType::MIDI, 2)); /* EMIT SIGNAL */
}
MidiTrack::MidiTrack (Session& sess, const XMLNode& node)
@@ -84,6 +86,8 @@ MidiTrack::MidiTrack (Session& sess, const XMLNode& node)
set_input_maximum(ChanCount(DataType::MIDI, 1));
set_output_minimum(ChanCount(DataType::MIDI, 1));
set_output_maximum(ChanCount(DataType::MIDI, 1));
+
+ MoreChannels(ChanCount(DataType::MIDI, 2)); /* EMIT SIGNAL */
}
MidiTrack::~MidiTrack ()
@@ -559,12 +563,84 @@ MidiTrack::process_output_buffers (BufferSet& bufs,
if (muted()) {
IO::silence(nframes, offset);
} else {
- MidiBuffer& out_buf = bufs.get_midi(0);
- _immediate_events.read(out_buf, 0, 0, offset + nframes-1); // all stamps = 0
+
+ MidiBuffer& output_buf = bufs.get_midi(0);
+ write_controller_messages(output_buf, start_frame, end_frame, nframes, offset);
+
deliver_output(bufs, start_frame, end_frame, nframes, offset);
}
}
+void
+MidiTrack::write_controller_messages(MidiBuffer& output_buf, nframes_t start_frame, nframes_t end_frame,
+ nframes_t nframes, nframes_t offset)
+{
+ BufferSet& mix_buffers = _session.get_mix_buffers(ChanCount(DataType::MIDI, 2));
+
+ /* FIXME: this could be more realtimey */
+
+ // Write immediate events (UI controls)
+ MidiBuffer& cc_buf = mix_buffers.get_midi(0);
+ cc_buf.silence(nframes, offset);
+ MidiEvent ev;
+ ev.size = 3; // CC = 3 bytes
+ Byte buf[ev.size];
+ buf[0] = MIDI_CMD_CONTROL;
+ ev.buffer = buf;
+
+ // Write controller automation
+ if (_session.transport_rolling()) {
+ for (Controls::const_iterator i = _controls.begin(); i != _controls.end(); ++i) {
+ const boost::shared_ptr<AutomationList> list = (*i).second->list();
+
+ if ( (!list->automation_playback())
+ || (list->parameter().type() != MidiCCAutomation))
+ continue;
+
+ double start = start_frame;
+ double x, y;
+ while ((*i).second->list()->rt_safe_earliest_event(start, end_frame, x, y)) {
+ assert(x >= start_frame);
+ assert(x <= end_frame);
+
+ const nframes_t stamp = (nframes_t)floor(x - start_frame);
+ assert(stamp < nframes);
+
+ assert(y >= 0.0);
+ assert(y <= 127.0);
+
+ ev.time = stamp;
+ ev.buffer[1] = (Byte)list->parameter().id();
+ ev.buffer[2] = (Byte)y;
+
+ cc_buf.push_back(ev);
+
+ start = x;
+ }
+ }
+ }
+
+ /* FIXME: too much copying! */
+
+ // Merge cc buf into output
+ if (cc_buf.size() > 0) {
+
+ // Both CC and route, must merge
+ if (output_buf.size() > 0) {
+
+ MidiBuffer& mix_buf = mix_buffers.get_midi(1);
+ mix_buf.merge(output_buf, cc_buf);
+ output_buf.copy(mix_buf);
+
+ } else {
+ output_buf.copy(cc_buf);
+ }
+ }
+
+ // Append immediate events (UI controls)
+ _immediate_events.read(output_buf, 0, 0, offset + nframes-1); // all stamps = 0
+}
+
int
MidiTrack::export_stuff (BufferSet& bufs, nframes_t nframes, nframes_t end_frame)
{
diff --git a/libs/ardour/send.cc b/libs/ardour/send.cc
index 5567649c9a..929b1fc9a8 100644
--- a/libs/ardour/send.cc
+++ b/libs/ardour/send.cc
@@ -120,7 +120,7 @@ Send::run_in_place (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame,
// we have to copy the input, because IO::deliver_output may alter the buffers
// in-place, which a send must never do.
- BufferSet& sendbufs = _session.get_send_buffers(bufs.count());
+ BufferSet& sendbufs = _session.get_mix_buffers(bufs.count());
sendbufs.read_from(bufs, nframes);
assert(sendbufs.count() == bufs.count());
diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc
index a374e10a4f..e1997df980 100644
--- a/libs/ardour/session.cc
+++ b/libs/ardour/session.cc
@@ -110,7 +110,7 @@ Session::Session (AudioEngine &eng,
: _engine (eng),
_scratch_buffers(new BufferSet()),
_silent_buffers(new BufferSet()),
- _send_buffers(new BufferSet()),
+ _mix_buffers(new BufferSet()),
_mmc_port (default_mmc_port),
_mtc_port (default_mtc_port),
_midi_port (default_midi_port),
@@ -212,7 +212,7 @@ Session::Session (AudioEngine &eng,
: _engine (eng),
_scratch_buffers(new BufferSet()),
_silent_buffers(new BufferSet()),
- _send_buffers(new BufferSet()),
+ _mix_buffers(new BufferSet()),
_mmc_port (default_mmc_port),
_mtc_port (default_mtc_port),
_midi_port (default_midi_port),
@@ -350,7 +350,7 @@ Session::destroy ()
delete _scratch_buffers;
delete _silent_buffers;
- delete _send_buffers;
+ delete _mix_buffers;
AudioDiskstream::free_working_buffers();
@@ -3681,14 +3681,14 @@ Session::ensure_buffers (ChanCount howmany)
if (current_block_size == 0)
return; // too early? (is this ok?)
- // We need at least 1 MIDI scratch buffer to mix/merge
- if (howmany.n_midi() < 1)
- howmany.set_midi(1);
+ // We need at least 2 MIDI scratch buffers to mix/merge
+ if (howmany.n_midi() < 2)
+ howmany.set_midi(2);
// FIXME: JACK needs to tell us maximum MIDI buffer size
// Using nasty assumption (max # events == nframes) for now
_scratch_buffers->ensure_buffers(howmany, current_block_size);
- _send_buffers->ensure_buffers(howmany, current_block_size);
+ _mix_buffers->ensure_buffers(howmany, current_block_size);
_silent_buffers->ensure_buffers(howmany, current_block_size);
allocate_pan_automation_buffers (current_block_size, howmany.n_audio(), false);
@@ -4064,11 +4064,11 @@ Session::get_scratch_buffers (ChanCount count)
}
BufferSet&
-Session::get_send_buffers (ChanCount count)
+Session::get_mix_buffers (ChanCount count)
{
- assert(_send_buffers->available() >= count);
- _send_buffers->set_count(count);
- return *_send_buffers;
+ assert(_mix_buffers->available() >= count);
+ _mix_buffers->set_count(count);
+ return *_mix_buffers;
}
uint32_t