summaryrefslogtreecommitdiff
path: root/libs/ardour
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2007-10-14 05:45:31 +0000
committerDavid Robillard <d@drobilla.net>2007-10-14 05:45:31 +0000
commit14dcc3f0170f8c723bcef0a59562adce0e8e7596 (patch)
treeca173319a68bd575a0721236df90d14a57cbda86 /libs/ardour
parent15af67c179ce336867c8f783388384f3ce214a99 (diff)
Fix displaying of notes in auto-created MIDI region when it's the first region in the track.
Fix crash after recording long phrases of MIDI. Fix MIDI looping (kinda). Add note-off exposure to MidiModel::iterator. Fix first-note-is-stuck-note problem. Fix resolving long notes while recording. Fix several other things I forget now. MIDI works pretty well..... git-svn-id: svn://localhost/ardour2/trunk@2555 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs/ardour')
-rw-r--r--libs/ardour/ardour/midi_event.h19
-rw-r--r--libs/ardour/ardour/midi_model.h57
-rw-r--r--libs/ardour/midi_buffer.cc4
-rw-r--r--libs/ardour/midi_diskstream.cc33
-rw-r--r--libs/ardour/midi_model.cc314
-rw-r--r--libs/ardour/midi_track.cc4
-rw-r--r--libs/ardour/quantize.cc10
-rw-r--r--libs/ardour/session.cc120
-rw-r--r--libs/ardour/session_events.cc2
-rw-r--r--libs/ardour/smf_source.cc2
-rw-r--r--libs/ardour/source_factory.cc6
11 files changed, 308 insertions, 263 deletions
diff --git a/libs/ardour/ardour/midi_event.h b/libs/ardour/ardour/midi_event.h
index c430c708b7..ee6ea7b749 100644
--- a/libs/ardour/ardour/midi_event.h
+++ b/libs/ardour/ardour/midi_event.h
@@ -83,16 +83,19 @@ struct MidiEvent {
inline const MidiEvent& operator=(const MidiEvent& copy) {
_time = copy._time;
- if (!_owns_buffer) {
- _buffer = copy._buffer;
- } else if (copy._buffer) {
- if (!_buffer || _size < copy._size)
- _buffer = (Byte*)realloc(_buffer, copy._size);
- memcpy(_buffer, copy._buffer, copy._size);
+ if (_owns_buffer) {
+ if (copy._buffer) {
+ if (!_buffer || _size < copy._size)
+ _buffer = (Byte*)realloc(_buffer, copy._size);
+ memcpy(_buffer, copy._buffer, copy._size);
+ } else {
+ free(_buffer);
+ _buffer = NULL;
+ }
} else {
- free(_buffer);
- _buffer = NULL;
+ _buffer = copy._buffer;
}
+
_size = copy._size;
return *this;
}
diff --git a/libs/ardour/ardour/midi_model.h b/libs/ardour/ardour/midi_model.h
index 28a747683c..8e7d786722 100644
--- a/libs/ardour/ardour/midi_model.h
+++ b/libs/ardour/ardour/midi_model.h
@@ -22,6 +22,7 @@
#define __ardour_midi_model_h__
#include <queue>
+#include <deque>
#include <utility>
#include <boost/utility.hpp>
#include <glibmm/thread.h>
@@ -72,20 +73,24 @@ public:
/** Resizes vector if necessary (NOT realtime safe) */
void append(const MidiEvent& ev);
- inline const Note& note_at(unsigned i) const { return _notes[i]; }
+ inline const boost::shared_ptr<const Note> note_at(unsigned i) const { return _notes[i]; }
+ inline const boost::shared_ptr<Note> note_at(unsigned i) { return _notes[i]; }
inline size_t n_notes() const { return _notes.size(); }
inline bool empty() const { return _notes.size() == 0 && _controls.size() == 0; }
- typedef std::vector<Note> Notes;
+ /* FIXME: use better data structure */
+ typedef std::vector< boost::shared_ptr<Note> > Notes;
- inline static bool note_time_comparator (const Note& a, const Note& b) {
- return a.time() < b.time();
+ inline static bool note_time_comparator (const boost::shared_ptr<const Note> a,
+ const boost::shared_ptr<const Note> b) {
+ return a->time() < b->time();
}
struct LaterNoteEndComparator {
typedef const Note* value_type;
- inline bool operator()(const Note* const a, const Note* const b) {
+ inline bool operator()(const boost::shared_ptr<const Note> a,
+ const boost::shared_ptr<const Note> b) const {
return a->end_time() > b->end_time();
}
};
@@ -111,14 +116,14 @@ public:
/*int set_state (const XMLNode&);
XMLNode& get_state ();*/
- void add(const Note& note);
- void remove(const Note& note);
+ void add(const boost::shared_ptr<Note> note);
+ void remove(const boost::shared_ptr<Note> note);
private:
- MidiModel& _model;
- std::string _name;
- std::list<Note> _added_notes;
- std::list<Note> _removed_notes;
+ MidiModel& _model;
+ const std::string _name;
+ std::list< boost::shared_ptr<Note> > _added_notes;
+ std::list< boost::shared_ptr<Note> > _removed_notes;
};
MidiModel::DeltaCommand* new_delta_command(const std::string name="midi edit");
@@ -140,19 +145,28 @@ public:
const_iterator(const MidiModel& model, double t);
~const_iterator();
+ inline bool locked() const { return _locked; }
+
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); }
+
+ const_iterator& operator=(const const_iterator& other);
private:
+ friend class MidiModel;
+
const MidiModel* _model;
MidiEvent _event;
- typedef std::priority_queue<const Note*,std::vector<const Note*>, LaterNoteEndComparator>
- ActiveNotes;
+ typedef std::priority_queue<
+ boost::shared_ptr<Note>, std::deque< boost::shared_ptr<Note> >,
+ LaterNoteEndComparator>
+ ActiveNotes;
+
mutable ActiveNotes _active_notes;
bool _is_end;
@@ -167,8 +181,8 @@ public:
private:
friend class DeltaCommand;
- void add_note_unlocked(const Note& note);
- void remove_note_unlocked(const Note& note);
+ void add_note_unlocked(const boost::shared_ptr<Note> note);
+ void remove_note_unlocked(const boost::shared_ptr<const Note> note);
friend class const_iterator;
bool control_to_midi_event(MidiEvent& ev, const MidiControlIterator& iter) const;
@@ -193,16 +207,13 @@ private:
const const_iterator _end_iter;
- mutable nframes_t _next_read;
+ 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;
+ typedef std::priority_queue<
+ boost::shared_ptr<Note>, std::deque< boost::shared_ptr<Note> >,
+ LaterNoteEndComparator>
+ ActiveNotes;
};
} /* namespace ARDOUR */
diff --git a/libs/ardour/midi_buffer.cc b/libs/ardour/midi_buffer.cc
index 2b47689911..e971e9ae8f 100644
--- a/libs/ardour/midi_buffer.cc
+++ b/libs/ardour/midi_buffer.cc
@@ -193,8 +193,8 @@ void
MidiBuffer::silence(nframes_t dur, nframes_t offset)
{
// FIXME use parameters
- assert(offset == 0);
- //assert(dur == _capacity);
+ if (offset != 0)
+ cerr << "WARNING: MidiBuffer::silence w/ offset != 0 (not implemented)" << endl;
memset(_events, 0, sizeof(MidiEvent) * _capacity);
memset(_data, 0, sizeof(Byte) * _capacity * MAX_EVENT_SIZE);
diff --git a/libs/ardour/midi_diskstream.cc b/libs/ardour/midi_diskstream.cc
index 2b8f70a307..3a2842be7a 100644
--- a/libs/ardour/midi_diskstream.cc
+++ b/libs/ardour/midi_diskstream.cc
@@ -141,7 +141,10 @@ MidiDiskstream::~MidiDiskstream ()
void
MidiDiskstream::non_realtime_locate (nframes_t position)
{
+ //cerr << "MDS: non_realtime_locate: " << position << endl;
+ assert(_write_source);
_write_source->set_timeline_position (position);
+ seek(position, true); // correct?
}
@@ -204,25 +207,7 @@ MidiDiskstream::get_input_sources ()
_source_port = _io->midi_input(0);
- /* I don't get it....
- const char **connections = _io->input(0)->get_connections ();
-
- if (connections == 0 || connections[0] == 0) {
-
- if (_source_port) {
- // _source_port->disable_metering ();
- }
-
- _source_port = 0;
-
- } else {
- _source_port = dynamic_cast<MidiPort*>(
- _session.engine().get_port_by_name (connections[0]) );
- }
-
- if (connections) {
- free (connections);
- }*/
+ // do... stuff?
}
int
@@ -685,6 +670,7 @@ MidiDiskstream::set_pending_overwrite (bool yn)
int
MidiDiskstream::overwrite_existing_buffers ()
{
+ cerr << "MDS: overwrite_existing_buffers() (does nothing)" << endl;
return 0;
}
@@ -693,6 +679,8 @@ MidiDiskstream::seek (nframes_t frame, bool complete_refill)
{
Glib::Mutex::Lock lm (state_lock);
int ret = -1;
+
+ //cerr << "MDS: seek: " << frame << endl;
_playback_buf->reset();
_capture_buf->reset();
@@ -722,6 +710,8 @@ MidiDiskstream::can_internal_playback_seek (nframes_t distance)
int
MidiDiskstream::internal_playback_seek (nframes_t distance)
{
+ cerr << "MDS: internal_playback_seek " << distance << endl;
+
first_recordable_frame += distance;
playback_sample += distance;
@@ -1203,6 +1193,10 @@ MidiDiskstream::engage_record_enable ()
_source_port->request_monitor_input (!(Config->get_auto_input() && rolling));
}
+ // FIXME: Why is this necessary? Isn't needed for AudioDiskstream...
+ if (!_write_source)
+ use_new_write_source();
+
_write_source->mark_streaming_midi_write_started (_note_mode, _session.transport_frame());
RecordEnableChanged (); /* EMIT SIGNAL */
@@ -1405,6 +1399,7 @@ MidiDiskstream::reset_write_sources (bool mark_write_complete, bool force)
if (_write_source && mark_write_complete) {
_write_source->mark_streaming_write_completed ();
}
+
use_new_write_source (0);
if (record_enabled()) {
diff --git a/libs/ardour/midi_model.cc b/libs/ardour/midi_model.cc
index d7167ed2ec..9a87e89ab8 100644
--- a/libs/ardour/midi_model.cc
+++ b/libs/ardour/midi_model.cc
@@ -42,7 +42,7 @@ MidiModel::const_iterator::const_iterator(const MidiModel& model, double t)
, _is_end( (t == DBL_MAX) || model.empty())
, _locked( ! _is_end)
{
- //cerr << "Created MIDI iterator @ " << t << "(is end: " << _is_end << ")" << endl;
+ //cerr << "Created MIDI iterator @ " << t << " (is end: " << _is_end << ")" << endl;
if (_is_end)
return;
@@ -52,7 +52,7 @@ MidiModel::const_iterator::const_iterator(const MidiModel& model, double t)
_note_iter = model.notes().end();
for (MidiModel::Notes::const_iterator i = model.notes().begin(); i != model.notes().end(); ++i) {
- if ((*i).time() >= t) {
+ if ((*i)->time() >= t) {
_note_iter = i;
break;
}
@@ -70,8 +70,8 @@ MidiModel::const_iterator::const_iterator(const MidiModel& model, double t)
double 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;
+ /*cerr << "MIDI Iterator: CC " << i->first.id() << " (size " << i->second->list()->size()
+ << ") has no events past " << t << endl;*/
continue;
}
@@ -92,7 +92,8 @@ MidiModel::const_iterator::const_iterator(const MidiModel& model, double t)
}
if (_note_iter != model.notes().end()) {
- _event = MidiEvent(_note_iter->on_event(), false);
+ _event = MidiEvent((*_note_iter)->on_event(), false);
+ _active_notes.push(*_note_iter);
++_note_iter;
}
@@ -106,9 +107,9 @@ MidiModel::const_iterator::const_iterator(const MidiModel& model, double t)
_is_end = true;
_model->read_unlock();
_locked = false;
- //} else {
- // printf("MIDI Iterator = %X @ %lf\n", _event.type(), _event.time());
- }
+ } /*else {
+ printf("MIDI Iterator = %X @ %lf\n", _event.type(), _event.time());
+ }*/
}
@@ -155,30 +156,51 @@ MidiModel::const_iterator::operator++()
}
}
- enum Type { NIL, NOTE, CC };
- Type type = NIL;
+ enum Type { NIL, NOTE_ON, NOTE_OFF, CC };
+
+ Type type = NIL;
+ double t = 0;
- if (_note_iter != _model->notes().end())
- type = NOTE;
+ // Next earliest note on
+ if (_note_iter != _model->notes().end()) {
+ type = NOTE_ON;
+ t = (*_note_iter)->time();
+ }
+ // Use the next earliest note off iff it's earlier than the note on
+ if (_model->note_mode() == Sustained && (! _active_notes.empty())) {
+ if (type == NIL || _active_notes.top()->end_time() <= (*_note_iter)->time()) {
+ type = NOTE_OFF;
+ t = _active_notes.top()->end_time();
+ }
+ }
+
+ // Use the next earliest controller iff it's earlier than the note event
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())
+ if (type == NIL || _control_iter->second.first < t)
type = CC;
- if (type == NOTE) {
- //cerr << "MIDI Iterator = note" << endl;
- _event = MidiEvent(_note_iter->on_event(), false);
+ if (type == NOTE_ON) {
+ //cerr << "********** MIDI Iterator = note on" << endl;
+ _event = MidiEvent((*_note_iter)->on_event(), false);
+ _active_notes.push(*_note_iter);
++_note_iter;
+ } else if (type == NOTE_OFF) {
+ //cerr << "********** MIDI Iterator = note off" << endl;
+ _event = MidiEvent(_active_notes.top()->off_event(), false);
+ _active_notes.pop();
} else if (type == CC) {
- //cerr << "MIDI Iterator = CC" << endl;
+ //cerr << "********** MIDI Iterator = CC" << endl;
_model->control_to_midi_event(_event, *_control_iter);
} else {
- //cerr << "MIDI Iterator = NIL" << endl;
+ //cerr << "********** MIDI Iterator = END" << endl;
_is_end = true;
_model->read_unlock();
_locked = false;
}
+ assert(_is_end || _event.size() > 0);
+
return *this;
}
@@ -186,14 +208,36 @@ MidiModel::const_iterator::operator++()
bool
MidiModel::const_iterator::operator==(const const_iterator& other) const
{
- if (_is_end)
- if (other._is_end)
- return true;
- else
- return false;
+ if (_is_end || other._is_end)
+ return (_is_end == other._is_end);
else
return (_event == other._event);
}
+
+
+MidiModel::const_iterator&
+MidiModel::const_iterator::operator=(const const_iterator& other)
+{
+ if (_locked && _model != other._model)
+ _model->read_unlock();
+
+ assert( ! other._event.owns_buffer());
+
+ _model = other._model;
+ _event = other._event;
+ _is_end = other._is_end;
+ _locked = other._locked;
+ _note_iter = other._note_iter;
+ _control_iters = other._control_iters;
+ _control_iter = other._control_iter;
+
+ assert( ! _event.owns_buffer());
+
+ if (_locked)
+ _model->read_lock();
+
+ return *this;
+}
// MidiModel
@@ -204,11 +248,12 @@ MidiModel::MidiModel(Session& s, size_t size)
, _note_mode(Sustained)
, _writing(false)
, _edited(false)
- //, _active_notes(LaterNoteEndComparator())
, _end_iter(*this, DBL_MAX)
, _next_read(UINT32_MAX)
, _read_iter(*this, DBL_MAX)
{
+ assert(_end_iter._is_end);
+ assert( ! _end_iter._locked);
}
@@ -219,84 +264,34 @@ MidiModel::MidiModel(Session& s, size_t size)
size_t
MidiModel::read(MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframes_t stamp_offset) const
{
+ //cerr << this << " MM::read @ " << start << " * " << nframes << " + " << stamp_offset << endl;
+ //cerr << this << " MM # notes: " << n_notes() << endl;
+
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;
+ //cerr << "Repositioning iterator from " << _next_read << " to " << start << endl;
+ } else {
+ //cerr << "Using cached iterator at " << _next_read << endl;
+ }
+
+ if (_read_iter == end()) {
+ //cerr << this << " MM::read: at end @ " << _read_iter->time() << endl;
+ } else {
+ //cerr << this << " MM::read: at " << _read_iter->time() << endl;
}
_next_read = start + nframes;
while (_read_iter != end() && _read_iter->time() < start + nframes) {
+ assert(_read_iter->size() > 0);
dst.write(_read_iter->time() + stamp_offset, _read_iter->size(), _read_iter->buffer());
+ //cerr << this << " MM::read event @ " << _read_iter->time() << endl;
++_read_iter;
++read_events;
}
-#if 0
- /* FIXME: cache last lookup value to avoid O(n) search every time */
-
- if (_note_mode == Sustained) {
-
- for (Notes::const_iterator n = _notes.begin(); n != _notes.end(); ++n) {
-
- while ( ! _active_notes.empty() ) {
- const Note* const earliest_off = _active_notes.top();
- const MidiEvent& off_ev = earliest_off->off_event();
- if (off_ev.time() < start + nframes && off_ev.time() <= n->time()) {
- dst.write(off_ev.time() + stamp_offset, off_ev.size(), off_ev.buffer());
- _active_notes.pop();
- ++read_events;
- } else {
- break;
- }
- }
-
- if (n->time() >= start + nframes)
- break;
-
- // Note on
- if (n->time() >= start) {
- const MidiEvent& on_ev = n->on_event();
- dst.write(on_ev.time() + stamp_offset, on_ev.size(), on_ev.buffer());
- _active_notes.push(&(*n));
- ++read_events;
- }
-
- }
-
- // Write any trailing note offs
- while ( ! _active_notes.empty() ) {
- const Note* const earliest_off = _active_notes.top();
- const MidiEvent& off_ev = earliest_off->off_event();
- if (off_ev.time() < start + nframes) {
- dst.write(off_ev.time() + stamp_offset, off_ev.size(), off_ev.buffer());
- _active_notes.pop();
- ++read_events;
- } else {
- break;
- }
- }
-
- // Percussive
- } else {
- for (Notes::const_iterator n = _notes.begin(); n != _notes.end(); ++n) {
- // Note on
- if (n->time() >= start) {
- if (n->time() < start + nframes) {
- const MidiEvent& ev = n->on_event();
- dst.write(ev.time() + stamp_offset, ev.size(), ev.buffer());
- ++read_events;
- } else {
- break;
- }
- }
- }
- }
-#endif
return read_events;
}
@@ -323,83 +318,6 @@ MidiModel::control_to_midi_event(MidiEvent& ev, const MidiControlIterator& iter)
}
-/** Return the earliest MIDI event in the given range.
- *
- * \return true if \a output has been set to the earliest event in the given range.
- */
-#if 0
-bool
-MidiModel::earliest_note_event(MidiEvent& output, nframes_t start, nframes_t nframes) const
-{
- /* FIXME: cache last lookup value to avoid O(n) search every time */
-
- const Note* const earliest_on = NULL;
- const Note* const earliest_off = NULL;
- const MidiEvent* const earliest_cc = NULL;
-
- /* Notes */
-
- if (_note_mode == Sustained) {
-
- for (Notes::const_iterator n = _notes.begin(); n != _notes.end(); ++n) {
-
- if ( ! _active_notes.empty() ) {
- const Note* const earliest_off = _active_notes.top();
- const MidiEvent& off_ev = earliest_off->off_event();
- if (off_ev.time() < start + nframes && off_ev.time() <= n->time()) {
- output = off_ev;
- //dst.write(off_ev.time() + stamp_offset, off_ev.size(), off_ev.buffer());
- _active_notes.pop();
- return true;
- }
- }
-
- if (n->time() >= start + nframes)
- break;
-
- // Note on
- if (n->time() >= start) {
- earliest_on = &n->on_event();
- //dst.write(on_ev.time() + stamp_offset, on_ev.size(), on_ev.buffer());
- _active_notes.push(&(*n));
- return true;
- }
-
- }
-
- // Write any trailing note offs
- while ( ! _active_notes.empty() ) {
- const Note* const earliest_off = _active_notes.top();
- const MidiEvent& off_ev = earliest_off->off_event();
- if (off_ev.time() < start + nframes) {
- dst.write(off_ev.time() + stamp_offset, off_ev.size(), off_ev.buffer());
- _active_notes.pop();
- ++read_events;
- } else {
- break;
- }
- }
-
- // Percussive
- } else {
- for (Notes::const_iterator n = _notes.begin(); n != _notes.end(); ++n) {
- // Note on
- if (n->time() >= start) {
- if (n->time() < start + nframes) {
- const MidiEvent& ev = n->on_event();
- dst.write(ev.time() + stamp_offset, ev.size(), ev.buffer());
- ++read_events;
- } else {
- break;
- }
- }
- }
- }
-
- return read_events;
-}
-#endif
-
/** Begin a write of events to the model.
*
* If \a mode is Sustained, complete notes with duration are constructed as note
@@ -435,8 +353,8 @@ MidiModel::end_write(bool delete_stuck)
if (_note_mode == Sustained && delete_stuck) {
for (Notes::iterator n = _notes.begin(); n != _notes.end() ; ) {
- if (n->duration() == 0) {
- cerr << "WARNING: Stuck note lost: " << n->note() << endl;
+ if ((*n)->duration() == 0) {
+ cerr << "WARNING: Stuck note lost: " << (*n)->note() << endl;
n = _notes.erase(n);
} else {
++n;
@@ -461,7 +379,7 @@ MidiModel::append(const MidiEvent& ev)
{
write_lock();
- assert(_notes.empty() || ev.time() >= _notes.back().time());
+ assert(_notes.empty() || ev.time() >= _notes.back()->time());
assert(_writing);
if (ev.is_note_on())
@@ -483,7 +401,7 @@ MidiModel::append_note_on_unlocked(double time, uint8_t note_num, uint8_t veloci
//cerr << "MidiModel " << this << " note " << (int)note_num << " on @ " << time << endl;
assert(_writing);
- _notes.push_back(Note(time, 0, note_num, velocity));
+ _notes.push_back(boost::shared_ptr<Note>(new Note(time, 0, note_num, velocity)));
if (_note_mode == Sustained) {
//cerr << "MM Sustained: Appending active note on " << (unsigned)(uint8_t)note_num << endl;
_write_notes.push_back(_notes.size() - 1);
@@ -512,7 +430,7 @@ MidiModel::append_note_off_unlocked(double time, uint8_t note_num)
* keys that send it */
for (WriteNotes::iterator n = _write_notes.begin(); n != _write_notes.end(); ++n) {
- Note& note = _notes[*n];
+ Note& note = *_notes[*n].get();
//cerr << (unsigned)(uint8_t)note.note() << " ? " << (unsigned)note_num << endl;
if (note.note() == note_num) {
assert(time > note.time());
@@ -538,7 +456,7 @@ MidiModel::append_cc_unlocked(double time, uint8_t number, uint8_t value)
void
-MidiModel::add_note_unlocked(const Note& note)
+MidiModel::add_note_unlocked(const boost::shared_ptr<Note> note)
{
//cerr << "MidiModel " << this << " add note " << (int)note.note() << " @ " << note.time() << endl;
Notes::iterator i = upper_bound(_notes.begin(), _notes.end(), note, note_time_comparator);
@@ -547,7 +465,7 @@ MidiModel::add_note_unlocked(const Note& note)
void
-MidiModel::remove_note_unlocked(const Note& note)
+MidiModel::remove_note_unlocked(const boost::shared_ptr<const Note> note)
{
//cerr << "MidiModel " << this << " remove note " << (int)note.note() << " @ " << note.time() << endl;
Notes::iterator n = find(_notes.begin(), _notes.end(), note);
@@ -562,10 +480,10 @@ MidiModel::is_sorted() const
{
bool t = 0;
for (Notes::const_iterator n = _notes.begin(); n != _notes.end(); ++n)
- if (n->time() < t)
+ if ((*n)->time() < t)
return false;
else
- t = n->time();
+ t = (*n)->time();
return true;
}
@@ -605,7 +523,7 @@ MidiModel::apply_command(Command* cmd)
void
-MidiModel::DeltaCommand::add(const Note& note)
+MidiModel::DeltaCommand::add(const boost::shared_ptr<Note> note)
{
//cerr << "MEC: apply" << endl;
@@ -615,7 +533,7 @@ MidiModel::DeltaCommand::add(const Note& note)
void
-MidiModel::DeltaCommand::remove(const Note& note)
+MidiModel::DeltaCommand::remove(const boost::shared_ptr<Note> note)
{
//cerr << "MEC: remove" << endl;
@@ -630,15 +548,27 @@ MidiModel::DeltaCommand::operator()()
// This could be made much faster by using a priority_queue for added and
// removed notes (or sort here), and doing a single iteration over _model
+ // Need to reset iterator to drop the read lock it holds, or we'll deadlock
+ const bool reset_iter = (_model._read_iter.locked());
+ const double iter_time = _model._read_iter->time();
+
+ if (reset_iter)
+ _model._read_iter = _model.end(); // drop read lock
+
+ assert( ! _model._read_iter.locked());
+
_model.write_lock();
- for (std::list<Note>::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i)
+ for (std::list< boost::shared_ptr<Note> >::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i)
_model.add_note_unlocked(*i);
- for (std::list<Note>::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i)
+ for (std::list< boost::shared_ptr<Note> >::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i)
_model.remove_note_unlocked(*i);
_model.write_unlock();
+
+ if (reset_iter)
+ _model._read_iter = const_iterator(_model, iter_time);
_model.ContentsChanged(); /* EMIT SIGNAL */
}
@@ -650,16 +580,28 @@ MidiModel::DeltaCommand::undo()
// This could be made much faster by using a priority_queue for added and
// removed notes (or sort here), and doing a single iteration over _model
+ // Need to reset iterator to drop the read lock it holds, or we'll deadlock
+ const bool reset_iter = (_model._read_iter.locked());
+ const double iter_time = _model._read_iter->time();
+
+ if (reset_iter)
+ _model._read_iter = _model.end(); // drop read lock
+
+ assert( ! _model._read_iter.locked());
+
_model.write_lock();
- for (std::list<Note>::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i)
+ for (std::list< boost::shared_ptr<Note> >::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i)
_model.remove_note_unlocked(*i);
- for (std::list<Note>::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i)
+ for (std::list< boost::shared_ptr<Note> >::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i)
_model.add_note_unlocked(*i);
_model.write_unlock();
+ if (reset_iter)
+ _model._read_iter = const_iterator(_model, iter_time);
+
_model.ContentsChanged(); /* EMIT SIGNAL */
}
@@ -697,9 +639,9 @@ MidiModel::write_to(boost::shared_ptr<MidiSource> source)
// Write any pending note offs earlier than this note on
while ( ! active_notes.empty() ) {
- const Note* const earliest_off = active_notes.top();
- const MidiEvent& off_ev = earliest_off->off_event();
- if (off_ev.time() <= n->time()) {
+ const boost::shared_ptr<const Note> earliest_off = active_notes.top();
+ const MidiEvent& off_ev = earliest_off->off_event();
+ if (off_ev.time() <= (*n)->time()) {
source->append_event_unlocked(off_ev);
active_notes.pop();
} else {
@@ -708,9 +650,9 @@ MidiModel::write_to(boost::shared_ptr<MidiSource> source)
}
// Write this note on
- source->append_event_unlocked(n->on_event());
- if (n->duration() > 0)
- active_notes.push(&(*n));
+ source->append_event_unlocked((*n)->on_event());
+ if ((*n)->duration() > 0)
+ active_notes.push(*n);
}
// Write any trailing note offs
diff --git a/libs/ardour/midi_track.cc b/libs/ardour/midi_track.cc
index 3b62fb0f84..d81f1f2cef 100644
--- a/libs/ardour/midi_track.cc
+++ b/libs/ardour/midi_track.cc
@@ -354,13 +354,11 @@ MidiTrack::no_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_fram
bool session_state_changing, bool can_record, bool rec_monitors_input)
{
if (n_outputs().n_midi() == 0) {
- //return 0;
- throw; // FIXME
+ return 0;
}
if (!_active) {
silence (nframes, offset);
- //return 0; // FIXME
}
if (session_state_changing) {
diff --git a/libs/ardour/quantize.cc b/libs/ardour/quantize.cc
index 5da30e8466..c29144996c 100644
--- a/libs/ardour/quantize.cc
+++ b/libs/ardour/quantize.cc
@@ -70,13 +70,13 @@ Quantize::run (boost::shared_ptr<Region> r)
double q_frames = _q * (m.frames_per_bar(t, session.frame_rate()) / (double)m.beats_per_bar());
for (MidiModel::Notes::iterator i = model->notes().begin(); i != model->notes().end(); ++i) {
- const double new_time = lrint(i->time() / q_frames) * q_frames;
- const double new_dur = ((i->time() != 0 && new_dur < (q_frames * 1.5))
+ const double new_time = lrint((*i)->time() / q_frames) * q_frames;
+ const double new_dur = (((*i)->time() != 0 && new_dur < (q_frames * 1.5))
? q_frames
- : lrint(i->duration() / q_frames) * q_frames);
+ : lrint((*i)->duration() / q_frames) * q_frames);
- i->set_time(new_time);
- i->set_duration(new_dur);
+ (*i)->set_time(new_time);
+ (*i)->set_duration(new_dur);
}
model->set_edited(true);
diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc
index 2473490f5b..abd8a0abba 100644
--- a/libs/ardour/session.cc
+++ b/libs/ardour/session.cc
@@ -1482,11 +1482,13 @@ Session::new_midi_track (TrackMode mode, uint32_t how_many)
char track_name[32];
uint32_t track_id = 0;
uint32_t n = 0;
- uint32_t channels_used = 0;
string port;
RouteList new_routes;
list<boost::shared_ptr<MidiTrack> > ret;
+ //uint32_t control_id;
+ // FIXME: need physical I/O and autoconnect stuff for MIDI
+
/* count existing midi tracks */
{
@@ -1496,18 +1498,29 @@ Session::new_midi_track (TrackMode mode, uint32_t how_many)
if (dynamic_cast<MidiTrack*>((*i).get()) != 0) {
if (!(*i)->is_hidden()) {
n++;
- channels_used += (*i)->n_inputs().n_midi();
+ //channels_used += (*i)->n_inputs().n_midi();
}
}
}
}
+ /*
+ vector<string> physinputs;
+ vector<string> physoutputs;
+ uint32_t nphysical_in;
+ uint32_t nphysical_out;
+
+ _engine.get_physical_outputs (physoutputs);
+ _engine.get_physical_inputs (physinputs);
+ control_id = ntracks() + nbusses() + 1;
+ */
+
while (how_many) {
/* check for duplicate route names, since we might have pre-existing
- routes with this name (e.g. create Midi1, Midi2, delete Midi1,
+ routes with this name (e.g. create Audio1, Audio2, delete Audio1,
save, close,restart,add new route - first named route is now
- Midi2)
+ Audio2)
*/
@@ -1522,17 +1535,71 @@ Session::new_midi_track (TrackMode mode, uint32_t how_many)
} while (track_id < (UINT_MAX-1));
+ /*
+ if (Config->get_input_auto_connect() & AutoConnectPhysical) {
+ nphysical_in = min (n_physical_inputs, (uint32_t) physinputs.size());
+ } else {
+ nphysical_in = 0;
+ }
+
+ if (Config->get_output_auto_connect() & AutoConnectPhysical) {
+ nphysical_out = min (n_physical_outputs, (uint32_t) physinputs.size());
+ } else {
+ nphysical_out = 0;
+ }
+ */
+
+ shared_ptr<MidiTrack> track;
+
try {
- shared_ptr<MidiTrack> track (new MidiTrack (*this, track_name, Route::Flag (0), mode));
+ track = boost::shared_ptr<MidiTrack>((new MidiTrack (*this, track_name, Route::Flag (0), mode)));
- if (track->ensure_io (ChanCount(DataType::MIDI, 1), ChanCount(DataType::MIDI, 1), false, this)) {
+ if (track->ensure_io (ChanCount(DataType::MIDI, 1), ChanCount(DataType::AUDIO, 1), false, this)) {
error << "cannot configure 1 in/1 out configuration for new midi track" << endmsg;
+ goto failed;
+ }
+
+ /*
+ if (nphysical_in) {
+ for (uint32_t x = 0; x < track->n_inputs().n_midi() && x < nphysical_in; ++x) {
+
+ port = "";
+
+ if (Config->get_input_auto_connect() & AutoConnectPhysical) {
+ port = physinputs[(channels_used+x)%nphysical_in];
+ }
+
+ if (port.length() && track->connect_input (track->input (x), port, this)) {
+ break;
+ }
+ }
+ }
+
+ for (uint32_t x = 0; x < track->n_outputs().n_midi(); ++x) {
+
+ port = "";
+
+ if (nphysical_out && (Config->get_output_auto_connect() & AutoConnectPhysical)) {
+ port = physoutputs[(channels_used+x)%nphysical_out];
+ } else if (Config->get_output_auto_connect() & AutoConnectMaster) {
+ if (_master_out) {
+ port = _master_out->input (x%_master_out->n_inputs().n_midi())->name();
+ }
+ }
+
+ if (port.length() && track->connect_output (track->output (x), port, this)) {
+ break;
+ }
}
channels_used += track->n_inputs ().n_midi();
+ */
+
+ track->midi_diskstream()->non_realtime_input_change();
+
track->DiskstreamChanged.connect (mem_fun (this, &Session::resort_routes));
- track->set_remote_control_id (ntracks());
+ //track->set_remote_control_id (control_id);
new_routes.push_back (track);
ret.push_back (track);
@@ -1540,14 +1607,43 @@ Session::new_midi_track (TrackMode mode, uint32_t how_many)
catch (failed_constructor &err) {
error << _("Session: could not create new midi track.") << endmsg;
- // XXX should we delete the tracks already created?
- ret.clear ();
- return ret;
+
+ if (track) {
+ /* we need to get rid of this, since the track failed to be created */
+ /* XXX arguably, AudioTrack::AudioTrack should not do the Session::add_diskstream() */
+
+ {
+ RCUWriter<DiskstreamList> writer (diskstreams);
+ boost::shared_ptr<DiskstreamList> ds = writer.get_copy();
+ ds->remove (track->midi_diskstream());
+ }
+ }
+
+ goto failed;
}
-
+
+ catch (AudioEngine::PortRegistrationFailure& pfe) {
+
+ error << _("No more JACK ports are available. You will need to stop Ardour and restart JACK with ports if you need this many tracks.") << endmsg;
+
+ if (track) {
+ /* we need to get rid of this, since the track failed to be created */
+ /* XXX arguably, MidiTrack::MidiTrack should not do the Session::add_diskstream() */
+
+ {
+ RCUWriter<DiskstreamList> writer (diskstreams);
+ boost::shared_ptr<DiskstreamList> ds = writer.get_copy();
+ ds->remove (track->midi_diskstream());
+ }
+ }
+
+ goto failed;
+ }
+
--how_many;
}
+ failed:
if (!new_routes.empty()) {
add_routes (new_routes, false);
save_state (_current_snapshot_name);
@@ -2732,8 +2828,6 @@ Session::source_by_id (const PBD::ID& id)
source = i->second;
}
- /* XXX search MIDI or other searches here */
-
return source;
}
diff --git a/libs/ardour/session_events.cc b/libs/ardour/session_events.cc
index aa5a1b87d4..bf1d9f1a09 100644
--- a/libs/ardour/session_events.cc
+++ b/libs/ardour/session_events.cc
@@ -397,7 +397,7 @@ Session::process_event (Event* ev)
break;
case Event::Overwrite:
- overwrite_some_buffers (static_cast<AudioDiskstream*>(ev->ptr));
+ overwrite_some_buffers (static_cast<Diskstream*>(ev->ptr));
break;
case Event::SetDiskstreamSpeed:
diff --git a/libs/ardour/smf_source.cc b/libs/ardour/smf_source.cc
index 3bc53cb538..b8d82bdb9d 100644
--- a/libs/ardour/smf_source.cc
+++ b/libs/ardour/smf_source.cc
@@ -326,7 +326,7 @@ SMFSource::read_event(uint32_t* delta_t, uint32_t* size, Byte** buf) const
nframes_t
SMFSource::read_unlocked (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, nframes_t stamp_offset) const
{
- //cerr << "SMF - read " << start << ", count=" << cnt << ", offset=" << stamp_offset << endl;
+ //cerr << "SMF " << name() << " read " << start << ", count=" << cnt << ", offset=" << stamp_offset << endl;
// 64 bits ought to be enough for anybody
uint64_t time = 0; // in SMF ticks, 1 tick per _ppqn
diff --git a/libs/ardour/source_factory.cc b/libs/ardour/source_factory.cc
index d07b9d6d69..3a657bc0e2 100644
--- a/libs/ardour/source_factory.cc
+++ b/libs/ardour/source_factory.cc
@@ -169,8 +169,10 @@ SourceFactory::create (Session& s, const XMLNode& node, bool defer_peaks)
#endif
} else if (type == DataType::MIDI) {
-
- boost::shared_ptr<Source> ret (new SMFSource (s, node));
+ boost::shared_ptr<Source> ret (new SMFSource (s, node));
+
+ SourceCreated (ret);
+ return ret;
}
return boost::shared_ptr<Source>();