Reading of MIDI CC from MIDI regions (MidiModel). UI still needs work though..
Various fixes for linear/integer AutomationList interpolation (for CC). git-svn-id: svn://localhost/ardour2/trunk@2359 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
@@ -542,9 +542,9 @@ env = conf.Finish()
|
||||
|
||||
opt_flags = []
|
||||
if env['GPROFILE'] == 1:
|
||||
debug_flags = [ '-g', '-pg' ]
|
||||
debug_flags = [ '-O0', '-g', '-pg' ]
|
||||
else:
|
||||
debug_flags = [ '-g' ]
|
||||
debug_flags = [ '-O0', '-g' ]
|
||||
|
||||
# guess at the platform, used to define compiler flags
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ public:
|
||||
boost::shared_ptr<AutomationControl> control_factory(boost::shared_ptr<AutomationList> list);
|
||||
|
||||
typedef std::map<Parameter,boost::shared_ptr<AutomationControl> > Controls;
|
||||
Controls& controls() { return _controls; }
|
||||
Controls& controls() { return _controls; }
|
||||
const Controls& controls() const { return _controls; }
|
||||
|
||||
virtual void add_control(boost::shared_ptr<AutomationControl>);
|
||||
|
||||
@@ -234,7 +234,8 @@ class AutomationList : public PBD::StatefulDestructible
|
||||
*/
|
||||
double unlocked_eval (double x) const;
|
||||
|
||||
bool rt_safe_earliest_event (double start, double end, double& x, double& y) const;
|
||||
bool rt_safe_earliest_event (double start, double end, double& x, double& y, bool start_inclusive=false) const;
|
||||
bool rt_safe_earliest_event_unlocked (double start, double end, double& x, double& y, bool start_inclusive=false) const;
|
||||
|
||||
Curve& curve() { return *_curve; }
|
||||
const Curve& curve() const { return *_curve; }
|
||||
@@ -256,8 +257,8 @@ class AutomationList : public PBD::StatefulDestructible
|
||||
|
||||
void build_search_cache_if_necessary(double start, double end) const;
|
||||
|
||||
bool rt_safe_earliest_event_discrete (double start, double end, double& x, double& y) const;
|
||||
bool rt_safe_earliest_event_linear (double start, double end, double& x, double& y) const;
|
||||
bool rt_safe_earliest_event_discrete_unlocked (double start, double end, double& x, double& y, bool inclusive) const;
|
||||
bool rt_safe_earliest_event_linear_unlocked (double start, double end, double& x, double& y, bool inclusive) const;
|
||||
|
||||
AutomationList* cut_copy_clear (double, double, int op);
|
||||
|
||||
|
||||
@@ -96,16 +96,35 @@ struct MidiEvent {
|
||||
_size = copy._size;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline bool operator==(const MidiEvent& other) const {
|
||||
if (_time != other._time)
|
||||
return false;
|
||||
|
||||
if (_size != other._size)
|
||||
return false;
|
||||
|
||||
if (_buffer == other._buffer)
|
||||
return true;
|
||||
|
||||
for (size_t i=0; i < _size; ++i)
|
||||
if (_buffer[i] != other._buffer[i])
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool operator!=(const MidiEvent& other) const { return ! operator==(other); }
|
||||
|
||||
inline bool owns_buffer() const { return _owns_buffer; }
|
||||
|
||||
inline void set_buffer(Byte* buf) {
|
||||
inline void set_buffer(Byte* buf, bool own) {
|
||||
if (_owns_buffer) {
|
||||
free(_buffer);
|
||||
_buffer = NULL;
|
||||
}
|
||||
_buffer = buf;
|
||||
_owns_buffer = false;
|
||||
_owns_buffer = own;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
@@ -55,8 +55,8 @@ public:
|
||||
// This is crap.
|
||||
void write_lock() { _lock.writer_lock(); _automation_lock.lock(); }
|
||||
void write_unlock() { _lock.writer_unlock(); _automation_lock.unlock(); }
|
||||
void read_lock() const { _lock.reader_lock(); _automation_lock.lock(); }
|
||||
void read_unlock() const { _lock.reader_unlock(); _automation_lock.unlock(); }
|
||||
void read_lock() const { _lock.reader_lock(); /*_automation_lock.lock();*/ }
|
||||
void read_unlock() const { _lock.reader_unlock(); /*_automation_lock.unlock();*/ }
|
||||
|
||||
void clear() { _notes.clear(); }
|
||||
|
||||
@@ -140,33 +140,41 @@ public:
|
||||
/** Read iterator */
|
||||
class const_iterator {
|
||||
public:
|
||||
const_iterator(MidiModel& model, double t);
|
||||
const_iterator(const MidiModel& model, double t);
|
||||
~const_iterator();
|
||||
|
||||
const MidiEvent& operator*() const { return _event; }
|
||||
const MidiEvent& operator*() const { return _event; }
|
||||
const MidiEvent* operator->() const { return &_event; }
|
||||
|
||||
const const_iterator& operator++(); // prefix only
|
||||
bool operator==(const const_iterator& other) const;
|
||||
bool operator!=(const const_iterator& other) const { return ! operator==(other); }
|
||||
|
||||
private:
|
||||
const MidiModel& _model;
|
||||
const MidiModel* _model;
|
||||
MidiEvent _event;
|
||||
|
||||
typedef std::priority_queue<const Note*,std::vector<const Note*>, LaterNoteEndComparator>
|
||||
ActiveNotes;
|
||||
mutable ActiveNotes _active_notes;
|
||||
|
||||
Notes::iterator _note_iter;
|
||||
|
||||
std::vector<MidiControlIterator> _control_iters;
|
||||
bool _is_end;
|
||||
bool _locked;
|
||||
Notes::const_iterator _note_iter;
|
||||
std::vector<MidiControlIterator> _control_iters;
|
||||
std::vector<MidiControlIterator>::iterator _control_iter;
|
||||
};
|
||||
|
||||
const_iterator begin() const { return const_iterator(*this, 0); }
|
||||
const const_iterator& end() const { return _end_iter; }
|
||||
|
||||
private:
|
||||
friend class DeltaCommand;
|
||||
void add_note_unlocked(const Note& note);
|
||||
void remove_note_unlocked(const Note& note);
|
||||
|
||||
friend class const_iterator;
|
||||
bool control_to_midi_event(MidiEvent& ev, const MidiControlIterator& iter);
|
||||
bool control_to_midi_event(MidiEvent& ev, const MidiControlIterator& iter) const;
|
||||
|
||||
#ifndef NDEBUG
|
||||
bool is_sorted() const;
|
||||
@@ -185,14 +193,19 @@ private:
|
||||
WriteNotes _write_notes;
|
||||
bool _writing;
|
||||
bool _edited;
|
||||
|
||||
|
||||
const const_iterator _end_iter;
|
||||
|
||||
mutable nframes_t _next_read;
|
||||
mutable const_iterator _read_iter;
|
||||
|
||||
// note state for read():
|
||||
// (TODO: Remove and replace with iterator)
|
||||
|
||||
typedef std::priority_queue<const Note*,std::vector<const Note*>,
|
||||
LaterNoteEndComparator> ActiveNotes;
|
||||
|
||||
mutable ActiveNotes _active_notes;
|
||||
//mutable ActiveNotes _active_notes;
|
||||
};
|
||||
|
||||
} /* namespace ARDOUR */
|
||||
|
||||
@@ -342,7 +342,7 @@ MidiRingBuffer::read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t
|
||||
}
|
||||
|
||||
} else {
|
||||
printf("MRB - SKIPPING EVENT (with time %f)\n", ev.time());
|
||||
printf("MRB - SKIPPING EVENT AT TIME %f\n", ev.time());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1036,7 +1036,7 @@ AutomationList::build_search_cache_if_necessary(double start, double end) const
|
||||
const ControlEvent start_point (start, 0);
|
||||
const ControlEvent end_point (end, 0);
|
||||
|
||||
//cerr << "REBUILD: (" << _search_cache.left << ".." << _search_cache.right << ") -> ("
|
||||
//cerr << "REBUILD: (" << _search_cache.left << ".." << _search_cache.right << ") := ("
|
||||
// << start << ".." << end << ")" << endl;
|
||||
|
||||
_search_cache.range.first = lower_bound (_events.begin(), _events.end(), &start_point, time_comparator);
|
||||
@@ -1050,31 +1050,50 @@ AutomationList::build_search_cache_if_necessary(double start, double end) const
|
||||
/** Get the earliest event between \a start and \a end, using the current interpolation style.
|
||||
*
|
||||
* 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
|
||||
{
|
||||
if (_interpolation == Discrete)
|
||||
return rt_safe_earliest_event_discrete(start, end, x, y);
|
||||
else
|
||||
return rt_safe_earliest_event_linear(start, end, x, y);
|
||||
}
|
||||
|
||||
/** Get the earliest event between \a start and \a end (Discrete (lack of) interpolation)
|
||||
*
|
||||
* If an event is found, \a x and \a y are set to its coordinates.
|
||||
* \param inclusive Include events with timestamp exactly equal to \a start
|
||||
* \return true if event is found (and \a x and \a y are valid).
|
||||
*/
|
||||
bool
|
||||
AutomationList::rt_safe_earliest_event_discrete (double start, double end, double& x, double& y) const
|
||||
AutomationList::rt_safe_earliest_event(double start, double end, double& x, double& y, bool inclusive) const
|
||||
{
|
||||
// FIXME: It would be nice if this was unnecessary..
|
||||
Glib::Mutex::Lock lm(_lock, Glib::TRY_LOCK);
|
||||
if (!lm.locked()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return rt_safe_earliest_event_unlocked(start, end, x, y, inclusive);
|
||||
}
|
||||
|
||||
|
||||
/** Get the earliest event between \a start and \a end, using the current interpolation style.
|
||||
*
|
||||
* If an event is found, \a x and \a y are set to its coordinates.
|
||||
*
|
||||
* \param inclusive Include events with timestamp exactly equal to \a start
|
||||
* \return true if event is found (and \a x and \a y are valid).
|
||||
*/
|
||||
bool
|
||||
AutomationList::rt_safe_earliest_event_unlocked(double start, double end, double& x, double& y, bool inclusive) const
|
||||
{
|
||||
if (_interpolation == Discrete)
|
||||
return rt_safe_earliest_event_discrete_unlocked(start, end, x, y, inclusive);
|
||||
else
|
||||
return rt_safe_earliest_event_linear_unlocked(start, end, x, y, inclusive);
|
||||
}
|
||||
|
||||
|
||||
/** Get the earliest event between \a start and \a end (Discrete (lack of) interpolation)
|
||||
*
|
||||
* If an event is found, \a x and \a y are set to its coordinates.
|
||||
*
|
||||
* \param inclusive Include events with timestamp exactly equal to \a start
|
||||
* \return true if event is found (and \a x and \a y are valid).
|
||||
*/
|
||||
bool
|
||||
AutomationList::rt_safe_earliest_event_discrete_unlocked (double start, double end, double& x, double& y, bool inclusive) const
|
||||
{
|
||||
build_search_cache_if_necessary(start, end);
|
||||
|
||||
const pair<const_iterator,const_iterator>& range = _search_cache.range;
|
||||
@@ -1082,8 +1101,10 @@ AutomationList::rt_safe_earliest_event_discrete (double start, double end, doubl
|
||||
if (range.first != _events.end()) {
|
||||
const ControlEvent* const first = *range.first;
|
||||
|
||||
const bool past_start = (inclusive ? first->when >= start : first->when > start);
|
||||
|
||||
/* Earliest points is in range, return it */
|
||||
if (first->when >= start && first->when < end) {
|
||||
if (past_start >= start && first->when < end) {
|
||||
|
||||
x = first->when;
|
||||
y = first->value;
|
||||
@@ -1098,7 +1119,6 @@ AutomationList::rt_safe_earliest_event_discrete (double start, double end, doubl
|
||||
return true;
|
||||
|
||||
} else {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1111,20 +1131,19 @@ AutomationList::rt_safe_earliest_event_discrete (double start, double end, doubl
|
||||
/** Get the earliest time the line crosses an integer (Linear interpolation).
|
||||
*
|
||||
* If an event is found, \a x and \a y are set to its coordinates.
|
||||
*
|
||||
* \param inclusive Include events with timestamp exactly equal to \a start
|
||||
* \return true if event is found (and \a x and \a y are valid).
|
||||
*/
|
||||
bool
|
||||
AutomationList::rt_safe_earliest_event_linear (double start, double end, double& x, double& y) const
|
||||
AutomationList::rt_safe_earliest_event_linear_unlocked (double start, double end, double& x, double& y, bool inclusive) const
|
||||
{
|
||||
// FIXME: It would be nice if this was unnecessary..
|
||||
Glib::Mutex::Lock lm(_lock, Glib::TRY_LOCK);
|
||||
if (!lm.locked()) {
|
||||
return false;
|
||||
}
|
||||
//cerr << "earliest_event(" << start << ", " << end << ", " << x << ", " << y << ", " << inclusive << endl;
|
||||
|
||||
if (_events.size() < 2)
|
||||
return false;
|
||||
return rt_safe_earliest_event_discrete_unlocked(start, end, x, y, inclusive);
|
||||
|
||||
// Hack to avoid infinitely repeating the same event
|
||||
build_search_cache_if_necessary(start, end);
|
||||
|
||||
pair<const_iterator,const_iterator> range = _search_cache.range;
|
||||
@@ -1138,6 +1157,7 @@ AutomationList::rt_safe_earliest_event_linear (double start, double end, double&
|
||||
if (range.first == _events.begin() || (*range.first)->when == start) {
|
||||
first = *range.first;
|
||||
next = *(++range.first);
|
||||
++_search_cache.range.first;
|
||||
|
||||
/* Step is before first */
|
||||
} else {
|
||||
@@ -1147,48 +1167,70 @@ AutomationList::rt_safe_earliest_event_linear (double start, double end, double&
|
||||
next = *range.first;
|
||||
}
|
||||
|
||||
if (first->when == start) {
|
||||
x = start;
|
||||
if (inclusive && first->when == start) {
|
||||
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;
|
||||
//++_search_cache.range.first;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*cerr << first->value << " @ " << first->when << " ---> "
|
||||
<< next->value << " @ " << next->when << endl;*/
|
||||
|
||||
const double slope = (next->value - first->value) / (double)(next->when - first->when);
|
||||
|
||||
const double start_y = first->value + (slope * (start - first->when));
|
||||
//cerr << "start y: " << start_y << endl;
|
||||
|
||||
if (start_y >= first->value) {
|
||||
x = first->when + ((ceil(start_y) - first->value) / (double)slope);
|
||||
y = ceil(start_y);
|
||||
} else {
|
||||
x = first->when + ((floor(start_y) - first->value) / (double)slope);
|
||||
y = floor(start_y);
|
||||
|
||||
if (abs(first->value - next->value) <= 1) {
|
||||
if (next->when <= end && (!inclusive || next->when > start)) {
|
||||
x = next->when;
|
||||
y = next->value;
|
||||
/* Move left of cache to this point
|
||||
* (Optimize for immediate call this cycle within range) */
|
||||
_search_cache.left = x;
|
||||
//++_search_cache.range.first;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (x >= start && x < end) {
|
||||
//cerr << y << " @ " << x << endl;
|
||||
const double slope = (next->value - first->value) / (double)(next->when - first->when);
|
||||
//cerr << "start y: " << start_y << endl;
|
||||
|
||||
x = floor(x);
|
||||
//y = first->value + (slope * fabs(start - first->when));
|
||||
y = first->value;
|
||||
|
||||
if (first->value < next->value) // ramping up
|
||||
y = ceil(y);
|
||||
else // ramping down
|
||||
y = floor(y);
|
||||
|
||||
x = first->when + (y - first->value) / (double)slope;
|
||||
|
||||
while ((inclusive && x < start) || x <= start && y != next->value) {
|
||||
|
||||
if (first->value < next->value) // ramping up
|
||||
y += 1.0;
|
||||
else // ramping down
|
||||
y -= 1.0;
|
||||
|
||||
x = first->when + (y - first->value) / (double)slope;
|
||||
}
|
||||
|
||||
/*cerr << first->value << " @ " << first->when << " ... "
|
||||
<< next->value << " @ " << next->when
|
||||
<< " = " << y << " @ " << x << endl;*/
|
||||
|
||||
assert( (y >= first->value && y <= next->value)
|
||||
|| (y <= first->value && y >= next->value) );
|
||||
|
||||
|
||||
const bool past_start = (inclusive ? x >= start : x > start);
|
||||
if (past_start && x < end) {
|
||||
/* Move left of cache to this point
|
||||
* (Optimize for immediate call this cycle within range) */
|
||||
_search_cache.left = x;
|
||||
|
||||
if (x >= next->when)
|
||||
++_search_cache.range.first;
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
//cerr << "\tNo: " << start_y << ", " << x << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ MidiBuffer::push_back(const MidiEvent& ev)
|
||||
|
||||
memcpy(write_loc, ev.buffer(), ev.size());
|
||||
_events[_size] = ev;
|
||||
_events[_size].set_buffer(write_loc);
|
||||
_events[_size].set_buffer(write_loc, false);
|
||||
++_size;
|
||||
|
||||
//cerr << "MidiBuffer: pushed, size = " << _size << endl;
|
||||
@@ -148,7 +148,7 @@ MidiBuffer::push_back(const jack_midi_event_t& ev)
|
||||
memcpy(write_loc, ev.buffer, ev.size);
|
||||
_events[_size].time() = (double)ev.time;
|
||||
_events[_size].size() = ev.size;
|
||||
_events[_size].set_buffer(write_loc);
|
||||
_events[_size].set_buffer(write_loc, false);
|
||||
++_size;
|
||||
|
||||
//cerr << "MidiBuffer: pushed, size = " << _size << endl;
|
||||
@@ -178,7 +178,7 @@ MidiBuffer::reserve(double time, size_t size)
|
||||
|
||||
_events[_size].time() = time;
|
||||
_events[_size].size() = size;
|
||||
_events[_size].set_buffer(write_loc);
|
||||
_events[_size].set_buffer(write_loc, false);
|
||||
++_size;
|
||||
|
||||
//cerr << "MidiBuffer: reserved, size = " << _size << endl;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
#include <stdint.h>
|
||||
#include <pbd/enumwriter.h>
|
||||
#include <ardour/midi_model.h>
|
||||
@@ -36,50 +37,165 @@ using namespace ARDOUR;
|
||||
|
||||
// Read iterator (const_iterator)
|
||||
|
||||
MidiModel::const_iterator::const_iterator(MidiModel& model, double t)
|
||||
: _model(model)
|
||||
MidiModel::const_iterator::const_iterator(const MidiModel& model, double t)
|
||||
: _model(&model)
|
||||
, _is_end( (t == DBL_MAX) || model.empty())
|
||||
, _locked( ! _is_end)
|
||||
{
|
||||
model.read_lock();
|
||||
//cerr << "Created MIDI iterator @ " << t << "(is end: " << _is_end << ")" << endl;
|
||||
|
||||
if (_is_end)
|
||||
return;
|
||||
|
||||
model.read_lock();
|
||||
|
||||
_note_iter = model.notes().end();
|
||||
for (MidiModel::Notes::iterator i = model.notes().begin(); i != model.notes().end(); ++i) {
|
||||
|
||||
for (MidiModel::Notes::const_iterator i = model.notes().begin(); i != model.notes().end(); ++i) {
|
||||
if ((*i).time() >= t) {
|
||||
_note_iter = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MidiControlIterator earliest_control = make_pair(boost::shared_ptr<AutomationList>(), make_pair(DBL_MAX, 0.0));
|
||||
|
||||
MidiControlIterator earliest_control = make_pair(boost::shared_ptr<AutomationList>(),
|
||||
make_pair(DBL_MAX, 0.0));
|
||||
|
||||
_control_iters.reserve(model.controls().size());
|
||||
for (Automatable::Controls::const_iterator i = model.controls().begin();
|
||||
i != model.controls().end(); ++i) {
|
||||
|
||||
assert(i->first.type() == MidiCCAutomation);
|
||||
|
||||
double x, y;
|
||||
i->second->list()->rt_safe_earliest_event(t, DBL_MAX, x, y);
|
||||
bool ret = i->second->list()->rt_safe_earliest_event_unlocked(t, DBL_MAX, x, y);
|
||||
if (!ret) {
|
||||
cerr << "MIDI Iterator: CC " << i->first.id() << " (size " << i->second->list()->size()
|
||||
<< ") has no events past " << t << endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(x >= 0);
|
||||
assert(y >= 0);
|
||||
assert(y <= UINT8_MAX);
|
||||
|
||||
const MidiControlIterator new_iter = make_pair(i->second->list(), make_pair(x, y));
|
||||
|
||||
if (x < earliest_control.second.first)
|
||||
earliest_control = new_iter;
|
||||
|
||||
|
||||
//cerr << "MIDI Iterator: CC " << i->first.id() << " added (" << x << ", " << y << ")" << endl;
|
||||
_control_iters.push_back(new_iter);
|
||||
|
||||
if (x < earliest_control.second.first) {
|
||||
earliest_control = new_iter;
|
||||
_control_iter = _control_iters.end();
|
||||
--_control_iter;
|
||||
}
|
||||
}
|
||||
|
||||
if (_note_iter != model.notes().end())
|
||||
if (_note_iter != model.notes().end()) {
|
||||
_event = MidiEvent(_note_iter->on_event(), false);
|
||||
++_note_iter;
|
||||
}
|
||||
|
||||
if (earliest_control.first != 0 && earliest_control.second.first < _event.time())
|
||||
if (earliest_control.first && earliest_control.second.first < _event.time())
|
||||
model.control_to_midi_event(_event, earliest_control);
|
||||
else
|
||||
_control_iter = _control_iters.end();
|
||||
|
||||
if (_event.size() == 0) {
|
||||
//cerr << "Created MIDI iterator @ " << t << " is at end." << endl;
|
||||
_is_end = true;
|
||||
_model->read_unlock();
|
||||
_locked = false;
|
||||
//} else {
|
||||
// printf("MIDI Iterator = %X @ %lf\n", _event.type(), _event.time());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
MidiModel::const_iterator::~const_iterator()
|
||||
{
|
||||
_model.read_unlock();
|
||||
if (_locked)
|
||||
_model->read_unlock();
|
||||
}
|
||||
|
||||
|
||||
const MidiModel::const_iterator&
|
||||
MidiModel::const_iterator::operator++()
|
||||
{
|
||||
if (_is_end)
|
||||
throw std::logic_error("Attempt to iterate past end of MidiModel");
|
||||
|
||||
assert(_event.is_note() || _event.is_cc());
|
||||
|
||||
// Increment past current control event
|
||||
if (_control_iter->first && _event.is_cc()) {
|
||||
double x, y;
|
||||
const bool ret = _control_iter->first->rt_safe_earliest_event_unlocked(
|
||||
_control_iter->second.first, DBL_MAX, x, y, false);
|
||||
|
||||
if (ret) {
|
||||
//cerr << "Incremented " << _control_iter->first->parameter().id() << " to " << x << endl;
|
||||
_control_iter->second.first = x;
|
||||
_control_iter->second.second = y;
|
||||
} else {
|
||||
//cerr << "Hit end of " << _control_iter->first->parameter().id() << endl;
|
||||
_control_iter->first.reset();
|
||||
_control_iter->second.first = DBL_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
// Now find and point at the earliest event
|
||||
|
||||
_control_iter = _control_iters.begin();
|
||||
|
||||
for (std::vector<MidiControlIterator>::iterator i = _control_iters.begin();
|
||||
i != _control_iters.end(); ++i) {
|
||||
if (i->second.first < _control_iter->second.first) {
|
||||
_control_iter = i;
|
||||
}
|
||||
}
|
||||
|
||||
enum Type { NIL, NOTE, CC };
|
||||
Type type = NIL;
|
||||
|
||||
if (_note_iter != _model->notes().end())
|
||||
type = NOTE;
|
||||
|
||||
if (_control_iter != _control_iters.end() && _control_iter->second.first != DBL_MAX)
|
||||
if (_note_iter == _model->notes().end() || _control_iter->second.first < _note_iter->time())
|
||||
type = CC;
|
||||
|
||||
if (type == NOTE) {
|
||||
//cerr << "MIDI Iterator = note" << endl;
|
||||
_event = MidiEvent(_note_iter->on_event(), false);
|
||||
++_note_iter;
|
||||
} else if (type == CC) {
|
||||
//cerr << "MIDI Iterator = CC" << endl;
|
||||
_model->control_to_midi_event(_event, *_control_iter);
|
||||
} else {
|
||||
//cerr << "MIDI Iterator = NIL" << endl;
|
||||
_is_end = true;
|
||||
_model->read_unlock();
|
||||
_locked = false;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
MidiModel::const_iterator::operator==(const const_iterator& other) const
|
||||
{
|
||||
if (_is_end)
|
||||
if (other._is_end)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
else
|
||||
return (_event == other._event);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// MidiModel
|
||||
|
||||
MidiModel::MidiModel(Session& s, size_t size)
|
||||
@@ -88,7 +204,10 @@ MidiModel::MidiModel(Session& s, size_t size)
|
||||
, _note_mode(Sustained)
|
||||
, _writing(false)
|
||||
, _edited(false)
|
||||
, _active_notes(LaterNoteEndComparator())
|
||||
//, _active_notes(LaterNoteEndComparator())
|
||||
, _end_iter(*this, DBL_MAX)
|
||||
, _next_read(UINT32_MAX)
|
||||
, _read_iter(*this, DBL_MAX)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -98,10 +217,26 @@ MidiModel::MidiModel(Session& s, size_t size)
|
||||
* \return number of events written to \a dst
|
||||
*/
|
||||
size_t
|
||||
MidiModel::read (MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframes_t stamp_offset) const
|
||||
MidiModel::read(MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframes_t stamp_offset) const
|
||||
{
|
||||
size_t read_events = 0;
|
||||
|
||||
if (start != _next_read) {
|
||||
_read_iter = const_iterator(*this, (double)start);
|
||||
cerr << "Repositioning iterator from " << _next_read << " to " << start << endl;
|
||||
//} else {
|
||||
// cerr << "Using cached iterator at " << _next_read << endl;
|
||||
}
|
||||
|
||||
_next_read = start + nframes;
|
||||
|
||||
while (_read_iter != end() && _read_iter->time() < start + nframes) {
|
||||
dst.write(_read_iter->time() + stamp_offset, _read_iter->size(), _read_iter->buffer());
|
||||
++_read_iter;
|
||||
++read_events;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* FIXME: cache last lookup value to avoid O(n) search every time */
|
||||
|
||||
if (_note_mode == Sustained) {
|
||||
@@ -161,17 +296,17 @@ MidiModel::read (MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframe
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
return read_events;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
MidiModel::control_to_midi_event(MidiEvent& ev, const MidiControlIterator& iter)
|
||||
MidiModel::control_to_midi_event(MidiEvent& ev, const MidiControlIterator& iter) const
|
||||
{
|
||||
if (iter.first->parameter().type() == MidiCCAutomation) {
|
||||
if (!ev.owns_buffer() || ev.size() < 3)
|
||||
ev = MidiEvent(iter.second.first, 3, (Byte*)malloc(3), true);
|
||||
if (ev.size() < 3)
|
||||
ev.set_buffer((Byte*)malloc(3), true);
|
||||
|
||||
assert(iter.first);
|
||||
assert(iter.first->parameter().id() <= INT8_MAX);
|
||||
@@ -418,11 +553,10 @@ void
|
||||
MidiModel::append_cc_unlocked(double time, uint8_t number, uint8_t value)
|
||||
{
|
||||
Parameter param(MidiCCAutomation, number);
|
||||
|
||||
//cerr << "MidiModel " << this << " add CC " << (int)number << " = " << (int)value
|
||||
// << " @ " << time << endl;
|
||||
|
||||
boost::shared_ptr<AutomationControl> control = Automatable::control(param, true);
|
||||
//cerr << "MidiModel " << this << "(" << control.get() << ") add CC " << (int)number << " = " << (int)value
|
||||
// << " @ " << time << endl;
|
||||
control->list()->fast_simple_add(time, (double)value);
|
||||
}
|
||||
|
||||
|
||||
@@ -589,7 +589,10 @@ MidiTrack::write_controller_messages(MidiBuffer& output_buf, nframes_t start_fra
|
||||
buf[0] = MIDI_CMD_CONTROL;
|
||||
MidiEvent ev(0, 3, buf, false);
|
||||
|
||||
// Write controller automation
|
||||
// Write track controller automation
|
||||
#if 0
|
||||
// This now lives in MidiModel. Any need for track automation like this?
|
||||
// Relative Velocity?
|
||||
if (_session.transport_rolling()) {
|
||||
for (Controls::const_iterator i = _controls.begin(); i != _controls.end(); ++i) {
|
||||
const boost::shared_ptr<AutomationList> list = (*i).second->list();
|
||||
@@ -637,6 +640,7 @@ MidiTrack::write_controller_messages(MidiBuffer& output_buf, nframes_t start_fra
|
||||
output_buf.copy(cc_buf);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Append immediate events (UI controls)
|
||||
_immediate_events.read(output_buf, 0, 0, offset + nframes-1); // all stamps = 0
|
||||
|
||||
@@ -2151,7 +2151,7 @@ Session::update_route_solo_state ()
|
||||
|
||||
shared_ptr<RouteList> r = routes.reader ();
|
||||
|
||||
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
|
||||
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
|
||||
if ((*i)->soloed()) {
|
||||
mute = true;
|
||||
if (dynamic_cast<Track*>((*i).get())) {
|
||||
|
||||
Reference in New Issue
Block a user