diff options
author | David Robillard <d@drobilla.net> | 2007-07-28 07:09:21 +0000 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2007-07-28 07:09:21 +0000 |
commit | 6e167cb1a835cb0b44990cc4c2b2a47db9dd2b9e (patch) | |
tree | e31d6aac9c2ca21a46f18a24822b47c2003aee4c | |
parent | 71452634a711cfeae4cf7eb9fbe7667210a071f1 (diff) |
Playback from MIDI model, playback of clicked-in events.
Note the diskstream chunk size affects reading of clicked-in, so you may need to seek away and back again to have new events read (this will be fixed).
git-svn-id: svn://localhost/ardour2/trunk@2183 d708f5d6-7413-0410-9779-e7cbd77b26cf
-rw-r--r-- | gtk2_ardour/midi_region_view.cc | 26 | ||||
-rw-r--r-- | gtk2_ardour/midi_streamview.cc | 2 | ||||
-rw-r--r-- | gtk2_ardour/midi_time_axis.cc | 12 | ||||
-rw-r--r-- | libs/ardour/ardour/midi_buffer.h | 2 | ||||
-rw-r--r-- | libs/ardour/ardour/midi_diskstream.h | 3 | ||||
-rw-r--r-- | libs/ardour/ardour/midi_model.h | 49 | ||||
-rw-r--r-- | libs/ardour/ardour/midi_ring_buffer.h | 27 | ||||
-rw-r--r-- | libs/ardour/ardour/midi_source.h | 6 | ||||
-rw-r--r-- | libs/ardour/ardour/midi_track.h | 2 | ||||
-rw-r--r-- | libs/ardour/ardour/source.h | 1 | ||||
-rw-r--r-- | libs/ardour/ardour/types.h | 4 | ||||
-rw-r--r-- | libs/ardour/audio_diskstream.cc | 2 | ||||
-rw-r--r-- | libs/ardour/enums.cc | 4 | ||||
-rw-r--r-- | libs/ardour/midi_buffer.cc | 5 | ||||
-rw-r--r-- | libs/ardour/midi_diskstream.cc | 12 | ||||
-rw-r--r-- | libs/ardour/midi_model.cc | 171 | ||||
-rw-r--r-- | libs/ardour/midi_source.cc | 31 | ||||
-rw-r--r-- | libs/ardour/midi_track.cc | 26 | ||||
-rw-r--r-- | libs/ardour/smf_source.cc | 10 |
19 files changed, 304 insertions, 91 deletions
diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index 2f6444e799..0c2efec35a 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -142,7 +142,7 @@ MidiRegionView::canvas_event(GdkEvent* ev) model->add_note(new_note); model->finish_command(); - view->update_bounds(new_note.note); + view->update_bounds(new_note.note()); add_note(new_note); } @@ -317,7 +317,7 @@ MidiRegionView::add_event (const MidiEvent& ev) const double footer_height = name_highlight->property_y2() - name_highlight->property_y1(); const double pixel_range = (trackview.height - footer_height - 5.0) / (double)note_range; - if (mtv->note_mode() == Note) { + if (mtv->note_mode() == Sustained) { if ((ev.buffer[0] & 0xF0) == MIDI_CMD_NOTE_ON) { const Byte& note = ev.buffer[1]; const double y1 = trackview.height - (pixel_range * (note - view->lowest_note() + 1)) @@ -352,7 +352,7 @@ MidiRegionView::add_event (const MidiEvent& ev) } } - } else if (mtv->note_mode() == Percussion) { + } else if (mtv->note_mode() == Percussive) { const Byte& note = ev.buffer[1]; const double x = trackview.editor.frame_to_pixel((nframes_t)ev.time); const double y = trackview.height - (pixel_range * (note - view->lowest_note() + 1)) @@ -393,9 +393,9 @@ MidiRegionView::extend_active_notes() void MidiRegionView::add_note (const MidiModel::Note& note) { - assert(note.start >= 0); - assert(note.start < _region->length()); - //assert(note.start + note.duration < _region->length()); + assert(note.time() >= 0); + assert(note.time() < _region->length()); + //assert(note.time() + note.duration < _region->length()); /*printf("Event, time = %f, size = %zu, data = ", ev.time, ev.size); for (size_t i=0; i < ev.size; ++i) { @@ -411,14 +411,14 @@ MidiRegionView::add_note (const MidiModel::Note& note) const double footer_height = name_highlight->property_y2() - name_highlight->property_y1(); const double pixel_range = (trackview.height - footer_height - 5.0) / (double)note_range; - if (mtv->note_mode() == Note) { - const double y1 = trackview.height - (pixel_range * (note.note - view->lowest_note() + 1)) + if (mtv->note_mode() == Sustained) { + const double y1 = trackview.height - (pixel_range * (note.note() - view->lowest_note() + 1)) - footer_height - 3.0; ArdourCanvas::SimpleRect * ev_rect = new Gnome::Canvas::SimpleRect(*group); - ev_rect->property_x1() = trackview.editor.frame_to_pixel((nframes_t)note.start); + ev_rect->property_x1() = trackview.editor.frame_to_pixel((nframes_t)note.time()); ev_rect->property_y1() = y1; - ev_rect->property_x2() = trackview.editor.frame_to_pixel((nframes_t)(note.start + note.duration)); + ev_rect->property_x2() = trackview.editor.frame_to_pixel((nframes_t)(note.time() + note.duration())); ev_rect->property_y2() = y1 + ceil(pixel_range); ev_rect->property_fill_color_rgba() = 0xFFFFFF66; @@ -428,9 +428,9 @@ MidiRegionView::add_note (const MidiModel::Note& note) ev_rect->show(); _events.push_back(ev_rect); - } else if (mtv->note_mode() == Percussion) { - const double x = trackview.editor.frame_to_pixel((nframes_t)note.start); - const double y = trackview.height - (pixel_range * (note.note - view->lowest_note() + 1)) + } else if (mtv->note_mode() == Percussive) { + const double x = trackview.editor.frame_to_pixel((nframes_t)note.time()); + const double y = trackview.height - (pixel_range * (note.note() - view->lowest_note() + 1)) - footer_height - 3.0; Diamond* ev_diamond = new Diamond(*group, std::min(pixel_range, 5.0)); diff --git a/gtk2_ardour/midi_streamview.cc b/gtk2_ardour/midi_streamview.cc index d6361905b5..e602c3a880 100644 --- a/gtk2_ardour/midi_streamview.cc +++ b/gtk2_ardour/midi_streamview.cc @@ -140,7 +140,7 @@ MidiStreamView::display_region(MidiRegionView* region_view, bool redisplay_event for (size_t i=0; i < source->model()->n_notes(); ++i) { const MidiModel::Note& note = source->model()->note_at(i); - update_bounds(note.note); + update_bounds(note.note()); if (redisplay_events) region_view->add_note(note); diff --git a/gtk2_ardour/midi_time_axis.cc b/gtk2_ardour/midi_time_axis.cc index fb3d67913d..9c2808e07c 100644 --- a/gtk2_ardour/midi_time_axis.cc +++ b/gtk2_ardour/midi_time_axis.cc @@ -206,15 +206,15 @@ MidiTimeAxisView::build_mode_menu() mode_menu->set_name ("ArdourContextMenu"); RadioMenuItem::Group mode_group; - items.push_back (RadioMenuElem (mode_group, _("Note"), - bind (mem_fun (*this, &MidiTimeAxisView::set_note_mode), Note))); + items.push_back (RadioMenuElem (mode_group, _("Sustained"), + bind (mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained))); _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back()); - _note_mode_item->set_active(_note_mode == Note); + _note_mode_item->set_active(_note_mode == Sustained); - items.push_back (RadioMenuElem (mode_group, _("Percussion"), - bind (mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussion))); + items.push_back (RadioMenuElem (mode_group, _("Percussive"), + bind (mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive))); _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back()); - _percussion_mode_item->set_active(_note_mode == Percussion); + _percussion_mode_item->set_active(_note_mode == Percussive); return mode_menu; } diff --git a/libs/ardour/ardour/midi_buffer.h b/libs/ardour/ardour/midi_buffer.h index 17feea6ff1..2ba6a0990b 100644 --- a/libs/ardour/ardour/midi_buffer.h +++ b/libs/ardour/ardour/midi_buffer.h @@ -81,7 +81,7 @@ private: const MidiEvent& operator[](size_t i) const { assert(i < _size); return _events[i]; } MidiEvent& operator[](size_t i) { assert(i < _size); return _events[i]; } - // FIXME: Jack needs to tell us this + // FIXME: Eliminate this static const size_t MAX_EVENT_SIZE = 4; // bytes /* We use _size as "number of events", so the size of _data is diff --git a/libs/ardour/ardour/midi_diskstream.h b/libs/ardour/ardour/midi_diskstream.h index be329b4704..53c7a7e22c 100644 --- a/libs/ardour/ardour/midi_diskstream.h +++ b/libs/ardour/ardour/midi_diskstream.h @@ -88,6 +88,8 @@ class MidiDiskstream : public Diskstream boost::shared_ptr<SMFSource> write_source () { return _write_source; } int set_destructive (bool yn); // doom! + + void set_note_mode (NoteMode m); protected: friend class Session; @@ -154,6 +156,7 @@ class MidiDiskstream : public Diskstream boost::shared_ptr<SMFSource> _write_source; RingBufferNPT<CaptureTransition>* _capture_transition_buf; nframes_t _last_flush_frame; + NoteMode _note_mode; }; }; /* namespace ARDOUR */ diff --git a/libs/ardour/ardour/midi_model.h b/libs/ardour/ardour/midi_model.h index 49ef21b26f..a2e2275157 100644 --- a/libs/ardour/ardour/midi_model.h +++ b/libs/ardour/ardour/midi_model.h @@ -25,6 +25,7 @@ #include <pbd/command.h> #include <ardour/types.h> #include <ardour/midi_buffer.h> +#include <ardour/midi_ring_buffer.h> namespace ARDOUR { @@ -40,25 +41,46 @@ class Session; class MidiModel : public boost::noncopyable { public: struct Note { - Note(double s=0, double d=0, uint8_t n=0, uint8_t v=0) - : start(s), duration(d), note(n), velocity(v) {} + Note(double time=0, double dur=0, uint8_t note=0, uint8_t vel=0x40); + Note(const Note& copy); inline bool operator==(const Note& other) - { return start == other.start && note == other.note; } + { return time() == other.time() && note() == other.note(); } - double start; - double duration; - uint8_t note; - uint8_t velocity; + inline double time() const { return _on_event.time; } + inline double end_time() const { return _off_event.time; } + inline uint8_t note() const { return _on_event.note(); } + inline uint8_t velocity() const { return _on_event.velocity(); } + inline double duration() const { return _off_event.time - _on_event.time; } + + inline void set_duration(double d) { _off_event.time = _on_event.time + d; } + + inline MidiEvent& on_event() { return _on_event; } + inline MidiEvent& off_event() { return _off_event; } + + inline const MidiEvent& on_event() const { return _on_event; } + inline const MidiEvent& off_event() const { return _off_event; } + + private: + MidiEvent _on_event; + MidiEvent _off_event; + Byte _on_event_buffer[3]; + Byte _off_event_buffer[3]; }; MidiModel(Session& s, size_t size=0); void clear() { _notes.clear(); } + NoteMode note_mode() const { return _note_mode; } + void set_note_mode(NoteMode mode) { _note_mode = mode; } + void start_write(); + bool currently_writing() const { return _writing; } void end_write(bool delete_stuck=false); + size_t read (MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframes_t stamp_offset) const; + /** Resizes vector if necessary (NOT realtime safe) */ void append(const MidiBuffer& data); @@ -72,9 +94,16 @@ public: typedef std::vector<Note> Notes; inline static bool note_time_comparator (const Note& a, const Note& b) { - return a.start < b.start; + return a.time() < b.time(); } + struct LaterNoteEndComparator { + typedef const Note* value_type; + inline bool operator()(const Note* const a, const Note* const b) { + return a->end_time() > b->end_time(); + } + }; + inline Notes& notes() { return _notes; } inline const Notes& notes() const { return _notes; } @@ -115,10 +144,12 @@ private: Session& _session; - Notes _notes; + Notes _notes; + NoteMode _note_mode; typedef std::vector<size_t> WriteNotes; WriteNotes _write_notes; + bool _writing; MidiEditCommand* _command; ///< In-progress command }; diff --git a/libs/ardour/ardour/midi_ring_buffer.h b/libs/ardour/ardour/midi_ring_buffer.h index 3effb96664..6c57c78c3c 100644 --- a/libs/ardour/ardour/midi_ring_buffer.h +++ b/libs/ardour/ardour/midi_ring_buffer.h @@ -19,6 +19,7 @@ #ifndef __ardour_midi_ring_buffer_h__ #define __ardour_midi_ring_buffer_h__ +#include <iostream> #include <algorithm> #include <ardour/types.h> #include <ardour/buffer.h> @@ -252,6 +253,9 @@ MidiRingBuffer::read(double* time, size_t* size, Byte* buf) inline size_t MidiRingBuffer::write(double time, size_t size, const Byte* buf) { + //printf("MRB - write %#X %d %d with time %lf\n", + // buf[0], buf[1], buf[2], time); + assert(size > 0); if (write_space() < (sizeof(double) + sizeof(size_t) + size)) { @@ -280,6 +284,8 @@ MidiRingBuffer::read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t size_t count = 0; + //printf("MRB - read %u .. %u + %u\n", start, end, offset); + while (read_space() > sizeof(double) + sizeof(size_t)) { full_peek(sizeof(double), (Byte*)&ev.time); @@ -292,30 +298,25 @@ MidiRingBuffer::read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t success = MidiRingBufferBase<Byte>::full_read(sizeof(size_t), (Byte*)&ev.size); if (!success) { - cerr << "MRB: READ ERROR (time/size)" << endl; + std::cerr << "MRB: READ ERROR (time/size)" << std::endl; continue; } if (ev.time >= start) { + ev.time -= start; Byte* write_loc = dst.reserve(ev.time, ev.size); success = MidiRingBufferBase<Byte>::full_read(ev.size, write_loc); - if (!success) - cerr << "MRB: READ ERROR (data)" << endl; + if (success) { + ++count; + //printf("MRB - read event at time %lf\n", ev.time); + } else { + std::cerr << "MRB: READ ERROR (data)" << std::endl; + } - //printf("MRB - read %#X %d %d with time %u at index %zu\n", - // ev.buffer[0], ev.buffer[1], ev.buffer[2], ev.time, - // priv_read_ptr); - // } else { printf("MRB - SKIPPING EVENT (with time %f)\n", ev.time); - break; } - - ++count; - - assert(ev.time <= end); - ev.time -= start; } //printf("(R) read space: %zu\n", read_space()); diff --git a/libs/ardour/ardour/midi_source.h b/libs/ardour/ardour/midi_source.h index d95d0fd75a..5393f07157 100644 --- a/libs/ardour/ardour/midi_source.h +++ b/libs/ardour/ardour/midi_source.h @@ -53,7 +53,9 @@ class MidiSource : public Source virtual nframes_t write (MidiRingBuffer& src, nframes_t cnt); virtual void mark_for_remove() = 0; - virtual void mark_streaming_write_completed () {} + virtual void mark_streaming_midi_write_started (NoteMode mode); + virtual void mark_streaming_write_started (); + virtual void mark_streaming_write_completed (); string captured_for() const { return _captured_for; } void set_captured_for (string str) { _captured_for = str; } @@ -72,6 +74,8 @@ class MidiSource : public Source virtual void load_model(bool lock=true, bool force_reload=false) = 0; virtual void destroy_model() = 0; + virtual bool model_loaded() const { return _model_loaded; } + MidiModel* model() { return _model; } protected: diff --git a/libs/ardour/ardour/midi_track.h b/libs/ardour/ardour/midi_track.h index bb55454d3b..c9ead65d49 100644 --- a/libs/ardour/ardour/midi_track.h +++ b/libs/ardour/ardour/midi_track.h @@ -84,7 +84,7 @@ public: }; NoteMode note_mode() const { return _note_mode; } - void set_note_mode (NoteMode m) { _note_mode = m; } + void set_note_mode (NoteMode m); protected: diff --git a/libs/ardour/ardour/source.h b/libs/ardour/ardour/source.h index 029469f491..e1843df75c 100644 --- a/libs/ardour/ardour/source.h +++ b/libs/ardour/ardour/source.h @@ -55,6 +55,7 @@ class Source : public SessionObject virtual nframes_t natural_position() const { return 0; } virtual void mark_for_remove() = 0; + virtual void mark_streaming_write_started () {} virtual void mark_streaming_write_completed () = 0; XMLNode& get_state (); diff --git a/libs/ardour/ardour/types.h b/libs/ardour/ardour/types.h index 9edeffb120..e13bd09d83 100644 --- a/libs/ardour/ardour/types.h +++ b/libs/ardour/ardour/types.h @@ -129,8 +129,8 @@ namespace ARDOUR { }; enum NoteMode { - Note, - Percussion + Sustained, + Percussive }; struct BBT_Time { diff --git a/libs/ardour/audio_diskstream.cc b/libs/ardour/audio_diskstream.cc index 14eef2961b..135aa4747f 100644 --- a/libs/ardour/audio_diskstream.cc +++ b/libs/ardour/audio_diskstream.cc @@ -1794,11 +1794,13 @@ AudioDiskstream::engage_record_enable () (*chan)->source->ensure_monitor_input (!(Config->get_auto_input() && rolling)); } capturing_sources.push_back ((*chan)->write_source); + (*chan)->write_source->mark_streaming_write_started (); } } else { for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { capturing_sources.push_back ((*chan)->write_source); + (*chan)->write_source->mark_streaming_write_started (); } } diff --git a/libs/ardour/enums.cc b/libs/ardour/enums.cc index 91fbda89c5..f2d2e92169 100644 --- a/libs/ardour/enums.cc +++ b/libs/ardour/enums.cc @@ -136,8 +136,8 @@ setup_enum_writer () REGISTER_ENUM (Destructive); REGISTER (_TrackMode); - REGISTER_ENUM (Note); - REGISTER_ENUM (Percussion); + REGISTER_ENUM (Sustained); + REGISTER_ENUM (Percussive); REGISTER (_NoteMode); REGISTER_ENUM (MeterFalloffOff); diff --git a/libs/ardour/midi_buffer.cc b/libs/ardour/midi_buffer.cc index 5376886a5d..5b7c789ba0 100644 --- a/libs/ardour/midi_buffer.cc +++ b/libs/ardour/midi_buffer.cc @@ -91,7 +91,10 @@ MidiBuffer::read_from(const Buffer& src, nframes_t nframes, nframes_t offset) for (size_t i=0; i < src.size(); ++i) { const MidiEvent& ev = msrc[i]; if (ev.time >= offset && ev.time < offset+nframes) { + //cerr << "MidiBuffer::read_from got event, " << ev.time << endl; push_back(ev); + } else { + //cerr << "MidiBuffer event out of range, " << ev.time << endl; } } @@ -166,7 +169,7 @@ MidiBuffer::push_back(const jack_midi_event_t& ev) Byte* MidiBuffer::reserve(double time, size_t size) { - assert(size < MAX_EVENT_SIZE); + assert(size <= MAX_EVENT_SIZE); if (_size == _capacity) return NULL; diff --git a/libs/ardour/midi_diskstream.cc b/libs/ardour/midi_diskstream.cc index 691335de93..c5711a8295 100644 --- a/libs/ardour/midi_diskstream.cc +++ b/libs/ardour/midi_diskstream.cc @@ -68,6 +68,7 @@ MidiDiskstream::MidiDiskstream (Session &sess, const string &name, Diskstream::F , _source_port(0) , _capture_transition_buf(0) , _last_flush_frame(0) + , _note_mode(Sustained) { /* prevent any write sources from being created */ @@ -92,6 +93,7 @@ MidiDiskstream::MidiDiskstream (Session& sess, const XMLNode& node) , _source_port(0) , _capture_transition_buf(0) , _last_flush_frame(0) + , _note_mode(Sustained) { in_set_state = true; init (Recordable); @@ -305,6 +307,14 @@ MidiDiskstream::set_destructive (bool yn) assert( ! yn); return -1; } + +void +MidiDiskstream::set_note_mode (NoteMode m) +{ + _note_mode = m; + if (_write_source && _write_source->model()) + _write_source->model()->set_note_mode(m); +} void MidiDiskstream::check_record_status (nframes_t transport_frame, nframes_t nframes, bool can_record) @@ -1212,6 +1222,8 @@ MidiDiskstream::engage_record_enable () _source_port->request_monitor_input (!(Config->get_auto_input() && rolling)); } + _write_source->mark_streaming_midi_write_started (_note_mode); + RecordEnableChanged (); /* EMIT SIGNAL */ } diff --git a/libs/ardour/midi_model.cc b/libs/ardour/midi_model.cc index 1c6990e84d..b1a87551b1 100644 --- a/libs/ardour/midi_model.cc +++ b/libs/ardour/midi_model.cc @@ -19,6 +19,8 @@ */ #include <iostream> +#include <queue> +#include <pbd/enumwriter.h> #include <ardour/midi_model.h> #include <ardour/midi_events.h> #include <ardour/types.h> @@ -27,41 +29,155 @@ using namespace std; using namespace ARDOUR; +// Note + +MidiModel::Note::Note(double t, double d, uint8_t n, uint8_t v) +{ + _on_event.time = t; + _on_event.buffer = _on_event_buffer; + _on_event.size = 3; + _on_event.buffer[0] = MIDI_CMD_NOTE_ON; + _on_event.buffer[1] = n; + _on_event.buffer[2] = v; + + _off_event.time = t + d; + _off_event.buffer = _off_event_buffer; + _off_event.size = 3; + _off_event.buffer[0] = MIDI_CMD_NOTE_OFF; + _off_event.buffer[1] = n; + _off_event.buffer[2] = 0x40; + + assert(time() == t); + assert(duration() == d); + assert(note() == n); + assert(velocity() == v); +} + + +MidiModel::Note::Note(const Note& copy) + : _on_event(copy._on_event) + , _off_event(copy._off_event) +{ + memcpy(_on_event_buffer, copy._on_event_buffer, 3); + memcpy(_off_event_buffer, copy._off_event_buffer, 3); + _on_event.buffer = _on_event_buffer; + _off_event.buffer = _off_event_buffer; +} + + +// MidiModel MidiModel::MidiModel(Session& s, size_t size) : _session(s) , _notes(size) + , _note_mode(Sustained) + , _writing(false) , _command(NULL) { } +/** Read events in frame range \a start .. \a start+cnt into \a dst, + * adding \a stamp_offset to each event's timestamp. + * \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 +{ + size_t read_events = 0; + + //cerr << "MM READ " << start << " .. " << nframes << endl; + + /* FIXME: cache last lookup value to avoid the search */ + + if (_note_mode == Sustained) { + LaterNoteEndComparator cmp; + priority_queue<const Note*,vector<const Note*>,LaterNoteEndComparator> active_notes(cmp); + + /* FIXME: cache last lookup value to avoid the search */ + for (Notes::const_iterator n = _notes.begin(); n != _notes.end(); ++n) { + + //cerr << "MM ON " << n->time() << endl; + + if (n->time() >= start + nframes) + break; + + while ( ! active_notes.empty() ) { + const Note* const earliest_off = active_notes.top(); + const MidiEvent& ev = earliest_off->off_event(); + if (ev.time < start + nframes && ev.time <= n->time()) { + dst.write(ev.time + stamp_offset, ev.size, ev.buffer); + active_notes.pop(); + ++read_events; + } else { + break; + } + } + + // Note on + if (n->time() >= start) { + const MidiEvent& ev = n->on_event(); + dst.write(ev.time + stamp_offset, ev.size, ev.buffer); + active_notes.push(&(*n)); + ++read_events; + } + + } + + // 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; +} + + /** Begin a write of events to the model. * - * As note on and off events are written, complete notes with duration are - * constructed + * If \a mode is Sustained, complete notes with duration are constructed as note + * on/off events are received. Otherwise (Percussive), only note on events are + * stored; note off events are discarded entirely and all contained notes will + * have duration 0. */ void MidiModel::start_write() { + //cerr << "MM START WRITE, MODE = " << enum_2_string(_note_mode) << endl; _write_notes.clear(); + _writing = true; } /** Finish a write of events to the model. * - * If \a delete_stuck is true, note on events that were never resolved with - * a corresonding note off will be deleted. Otherwise they will remain as - * notes with duration 0. + * If \a delete_stuck is true and the current mode is Sustained, note on events + * that were never resolved with a corresonding note off will be deleted. + * Otherwise they will remain as notes with duration 0. */ void MidiModel::end_write(bool delete_stuck) { - if (delete_stuck) { + assert(_writing); + + //cerr << "MM END WRITE\n"; + + 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; @@ -70,6 +186,7 @@ MidiModel::end_write(bool delete_stuck) } _write_notes.clear(); + _writing = false; } @@ -84,10 +201,12 @@ MidiModel::end_write(bool delete_stuck) void MidiModel::append(const MidiBuffer& buf) { + assert(_writing); + for (MidiBuffer::const_iterator i = buf.begin(); i != buf.end(); ++i) { const MidiEvent& ev = *i; - assert(_notes.empty() || ev.time >= _notes.back().start); + assert(_notes.empty() || ev.time >= _notes.back().time()); if (ev.type() == MIDI_CMD_NOTE_ON) append_note_on(ev.time, ev.note(), ev.velocity()); @@ -106,7 +225,8 @@ MidiModel::append(const MidiBuffer& buf) void MidiModel::append(double time, size_t size, const Byte* buf) { - assert(_notes.empty() || time >= _notes.back().start); + assert(_notes.empty() || time >= _notes.back().time()); + assert(_writing); if ((buf[0] & 0xF0) == MIDI_CMD_NOTE_ON) append_note_on(time, buf[1], buf[2]); @@ -118,28 +238,41 @@ MidiModel::append(double time, size_t size, const Byte* buf) void MidiModel::append_note_on(double time, uint8_t note_num, uint8_t velocity) { + assert(_writing); _notes.push_back(Note(time, 0, note_num, velocity)); - _write_notes.push_back(_notes.size() - 1); + if (_note_mode == Sustained) { + //cerr << "MM Appending note on " << (unsigned)(uint8_t)note_num << endl; + _write_notes.push_back(_notes.size() - 1); + } else { + //cerr << "MM NOT appending note on" << endl; + } } void MidiModel::append_note_off(double time, uint8_t note_num) { - /* _write_notes (active notes) is presumably small enough for linear - * search to be a good idea. maybe not with instruments (percussion) - * that don't send note off at all though.... FIXME? */ + assert(_writing); + if (_note_mode == Percussive) { + //cerr << "MM Ignoring note off (percussive mode)" << endl; + return; + } else { + //cerr << "MM Attempting to resolve note off " << (unsigned)(uint8_t)note_num << endl; + } + /* FIXME: make _write_notes fixed size (127 noted) for speed */ + /* FIXME: note off velocity for that one guy out there who actually has * keys that send it */ for (WriteNotes::iterator n = _write_notes.begin(); n != _write_notes.end(); ++n) { Note& note = _notes[*n]; - if (note.note == note_num) { - assert(time > note.start); - note.duration = time - note.start; + //cerr << (unsigned)(uint8_t)note.note() << " ? " << (unsigned)note_num << endl; + if (note.note() == note_num) { + assert(time > note.time()); + note.set_duration(time - note.time()); _write_notes.erase(n); - //cerr << "MidiModel resolved note, duration: " << note.duration << endl; + //cerr << "MidiModel resolved note, duration: " << note.duration() << endl; break; } } @@ -149,6 +282,7 @@ MidiModel::append_note_off(double time, uint8_t note_num) void MidiModel::add_note(const Note& note) { + // FIXME: take source lock Notes::iterator i = upper_bound(_notes.begin(), _notes.end(), note, note_time_comparator); _notes.insert(i, note); if (_command) @@ -159,6 +293,7 @@ MidiModel::add_note(const Note& note) void MidiModel::remove_note(const Note& note) { + // FIXME: take source lock Notes::iterator n = find(_notes.begin(), _notes.end(), note); if (n != _notes.end()) _notes.erase(n); diff --git a/libs/ardour/midi_source.cc b/libs/ardour/midi_source.cc index f2a3121be2..238bf4f481 100644 --- a/libs/ardour/midi_source.cc +++ b/libs/ardour/midi_source.cc @@ -99,7 +99,13 @@ nframes_t MidiSource::read (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, nframes_t stamp_offset) const { Glib::Mutex::Lock lm (_lock); - return read_unlocked (dst, start, cnt, stamp_offset); + if (_model_loaded && _model) { + /*const size_t n_events = */_model->read(dst, start, cnt, stamp_offset); + //cout << "Read " << n_events << " events from model." << endl; + return cnt; + } else { + return read_unlocked (dst, start, cnt, stamp_offset); + } } nframes_t @@ -119,3 +125,26 @@ MidiSource::file_changed (string path) return ( !e1 ); } +void +MidiSource::mark_streaming_midi_write_started (NoteMode mode) +{ + if (_model) { + _model->set_note_mode(mode); + _model->start_write(); + } +} + +void +MidiSource::mark_streaming_write_started () +{ + if (_model) + _model->start_write(); +} + +void +MidiSource::mark_streaming_write_completed () +{ + if (_model) + _model->end_write(false); // FIXME: param? +} + diff --git a/libs/ardour/midi_track.cc b/libs/ardour/midi_track.cc index 3d7c429075..55d195fab0 100644 --- a/libs/ardour/midi_track.cc +++ b/libs/ardour/midi_track.cc @@ -167,7 +167,7 @@ MidiTrack::_set_state (const XMLNode& node, bool call_base) if ((prop = node.property (X_("note-mode"))) != 0) { _note_mode = NoteMode (string_2_enum (prop->value(), _note_mode)); } else { - _note_mode = Note; + _note_mode = Sustained; } if ((prop = node.property ("diskstream-id")) == 0) { @@ -569,7 +569,6 @@ MidiTrack::process_output_buffers (BufferSet& bufs, 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); } } @@ -685,26 +684,13 @@ MidiTrack::unfreeze () _freeze_record.state = UnFrozen; FreezeChange (); /* EMIT SIGNAL */ } -#if 0 -int -MidiTrack::set_mode (TrackMode m) -{ - assert(_diskstream); - - if (m != _mode) { - - if (_diskstream->set_destructive (m == Destructive)) { - return -1; - } - _mode = m; - - TrackModeChanged (); /* EMIT SIGNAL */ - } - - return 0; +void +MidiTrack::set_note_mode (NoteMode m) +{ + _note_mode = m; + midi_diskstream()->set_note_mode(m); } -#endif /** \return true on success, false on failure (no buffer space left) */ diff --git a/libs/ardour/smf_source.cc b/libs/ardour/smf_source.cc index 08ec1d87a8..eac782853f 100644 --- a/libs/ardour/smf_source.cc +++ b/libs/ardour/smf_source.cc @@ -408,7 +408,12 @@ SMFSource::write_unlocked (MidiRingBuffer& src, nframes_t cnt) const nframes_t oldlen = _length; update_length(oldlen, cnt); - _model->append(buf); + if (_model) { + if ( ! _model->currently_writing()) { + _model->start_write(); + } + _model->append(buf); + } ViewDataRangeReady (buf_ptr, oldlen, cnt); /* EMIT SIGNAL */ @@ -463,6 +468,8 @@ SMFSource::mark_for_remove () void SMFSource::mark_streaming_write_completed () { + MidiSource::mark_streaming_write_completed(); + if (!writable()) { return; } @@ -828,7 +835,6 @@ SMFSource::load_model(bool lock, bool force_reload) const double ev_time = (double)(time * frames_per_beat / (double)_ppqn); // in frames if (ret > 0) { // didn't skip (meta) event - //cerr << "ADDING EVENT TO MODEL: " << ev.time << endl; _model->append(ev_time, ev.size, ev.buffer); } |