From 15cdf454ea5a1460b41cb462c35f1ffb853a0764 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Fri, 10 Oct 2008 00:39:29 +0000 Subject: Apply MIDI looping patch from torbenh, with minor changes. General idea: use internal events to mark loop boundaries in MIDI buffers so readers can make sense of timestamps. git-svn-id: svn://localhost/ardour2/branches/3.0@3905 d708f5d6-7413-0410-9779-e7cbd77b26cf --- libs/ardour/SConscript | 1 + libs/ardour/ardour/event_type_map.h | 4 ++++ libs/ardour/ardour/midi_diskstream.h | 8 ++++++-- libs/ardour/ardour/midi_ring_buffer.h | 14 ++++++++++++++ libs/ardour/jack_midi_port.cc | 6 ++++-- libs/ardour/midi_buffer.cc | 10 +++++++--- libs/ardour/midi_diskstream.cc | 29 ++++++++++++++++++++++------- libs/ardour/midi_track.cc | 2 +- libs/ardour/panner.cc | 6 ------ 9 files changed, 59 insertions(+), 21 deletions(-) (limited to 'libs') diff --git a/libs/ardour/SConscript b/libs/ardour/SConscript index fb1d052418..2abf18cd55 100644 --- a/libs/ardour/SConscript +++ b/libs/ardour/SConscript @@ -110,6 +110,7 @@ midi_playlist.cc midi_port.cc midi_region.cc midi_source.cc +midi_state_tracker.cc midi_stretch.cc midi_track.cc mix.cc diff --git a/libs/ardour/ardour/event_type_map.h b/libs/ardour/ardour/event_type_map.h index 99911121c5..dfa11d6408 100644 --- a/libs/ardour/ardour/event_type_map.h +++ b/libs/ardour/ardour/event_type_map.h @@ -46,6 +46,10 @@ private: static EventTypeMap event_type_map; }; +enum InternalEventType { + LoopEventType = 1000 +}; + } // namespace ARDOUR #endif /* __ardour_event_type_map_h__ */ diff --git a/libs/ardour/ardour/midi_diskstream.h b/libs/ardour/ardour/midi_diskstream.h index 28e80816a8..a3d4d6d0a4 100644 --- a/libs/ardour/ardour/midi_diskstream.h +++ b/libs/ardour/ardour/midi_diskstream.h @@ -46,6 +46,7 @@ #include #include #include +#include struct tm; @@ -68,7 +69,7 @@ class MidiDiskstream : public Diskstream float playback_buffer_load() const; float capture_buffer_load() const; - void get_playback(MidiBuffer& dst, nframes_t start, nframes_t end); + void get_playback(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t offset); void set_record_enabled (bool yn); @@ -170,13 +171,16 @@ class MidiDiskstream : public Diskstream void engage_record_enable (); void disengage_record_enable (); - + void check_note_onoffs(Evoral::MIDIEvent &event); + void emit_pending_note_offs(MidiBuffer &dst, nframes_t time); + MidiRingBuffer* _playback_buf; MidiRingBuffer* _capture_buf; MidiPort* _source_port; boost::shared_ptr _write_source; nframes_t _last_flush_frame; NoteMode _note_mode; + MidiStateTracker _midistate_tracker; }; }; /* namespace ARDOUR */ diff --git a/libs/ardour/ardour/midi_ring_buffer.h b/libs/ardour/ardour/midi_ring_buffer.h index d1acb9b235..6e827d2852 100644 --- a/libs/ardour/ardour/midi_ring_buffer.h +++ b/libs/ardour/ardour/midi_ring_buffer.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -148,6 +149,18 @@ MidiRingBuffer::read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t continue; } + // This event marks a loop happening. this means that + // the next events timestamp will be non-monotonic. + if (ev_type == LoopEventType) { + ev_time -= start; + Evoral::MIDIEvent loopevent(LoopEventType, ev_time); + dst.push_back(loopevent); + + // We can safely return, without reading the data, because + // a LoopEvent does not have data. + return count + 1; + } + uint8_t status; success = full_peek(sizeof(uint8_t), &status); assert(success); // If this failed, buffer is corrupt, all hope is lost @@ -168,6 +181,7 @@ MidiRingBuffer::read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t assert(ev_time >= start); ev_time -= start; + ev_time += offset; uint8_t* write_loc = dst.reserve(ev_time, ev_size); if (write_loc == NULL) { diff --git a/libs/ardour/jack_midi_port.cc b/libs/ardour/jack_midi_port.cc index 3c3f50c9bb..4b7b66c659 100644 --- a/libs/ardour/jack_midi_port.cc +++ b/libs/ardour/jack_midi_port.cc @@ -110,6 +110,7 @@ JackMidiPort::cycle_end (nframes_t nframes, nframes_t offset) for (MidiBuffer::iterator i = _buffer->begin(); i != _buffer->end(); ++i) { const Evoral::Event& ev = *i; + // event times should be frames, relative to cycle start assert(ev.time() >= 0); assert(ev.time() < nframes); @@ -133,7 +134,8 @@ JackMidiPort::flush_buffers (nframes_t nframes, nframes_t offset) const Evoral::Event& ev = *i; // event times should be frames, relative to cycle start assert(ev.time() >= 0); - assert(ev.time() < nframes); - jack_midi_event_write (jack_buffer, (jack_nframes_t) ev.time(), ev.buffer(), ev.size()); + assert(ev.time() < (nframes+offset)); + if (ev.time() >= offset) + jack_midi_event_write (jack_buffer, (jack_nframes_t) ev.time(), ev.buffer(), ev.size()); } } diff --git a/libs/ardour/midi_buffer.cc b/libs/ardour/midi_buffer.cc index 6f0620cae1..ae370cdef7 100644 --- a/libs/ardour/midi_buffer.cc +++ b/libs/ardour/midi_buffer.cc @@ -110,13 +110,17 @@ MidiBuffer::read_from(const Buffer& src, nframes_t nframes, nframes_t offset) assert(_capacity >= msrc.size()); - clear(); - assert(_size == 0); + if (offset == 0) { + clear(); + assert(_size == 0); + } // FIXME: slow for (size_t i=0; i < msrc.size(); ++i) { const Evoral::MIDIEvent& ev = msrc[i]; - if (ev.time() >= offset && ev.time() < offset+nframes) { + if (ev.time() < offset) + continue; + if (ev.time() < (nframes + offset)) { //cout << "MidiBuffer::read_from got event, " << int(ev.type()) << " time: " << ev.time() << " buffer size: " << _size << endl; push_back(ev); } else { diff --git a/libs/ardour/midi_diskstream.cc b/libs/ardour/midi_diskstream.cc index 0b79213a23..2372d91c5d 100644 --- a/libs/ardour/midi_diskstream.cc +++ b/libs/ardour/midi_diskstream.cc @@ -728,7 +728,7 @@ MidiDiskstream::read (nframes_t& start, nframes_t dur, bool reversed) position within the loop. */ - if (loc && start >= loop_end) { + if (loc && (start >= loop_end)) { //cerr << "start adjusted from " << start; start = loop_start + ((start - loop_start) % loop_length); //cerr << "to " << start << endl; @@ -774,6 +774,9 @@ MidiDiskstream::read (nframes_t& start, nframes_t dur, bool reversed) /* if we read to the end of the loop, go back to the beginning */ if (reloop) { + // Synthesize LoopEvent here, because the next events + // written will have non-monotonic timestamps. + _playback_buf->write(loop_end - 1, LoopEventType, 0, 0); start = loop_start; } else { start += this_read; @@ -1435,7 +1438,6 @@ MidiDiskstream::capture_buffer_load () const (double) _capture_buf->capacity()); } - int MidiDiskstream::use_pending_capture_data (XMLNode& node) { @@ -1446,16 +1448,29 @@ MidiDiskstream::use_pending_capture_data (XMLNode& node) * so that an event at \a start has time = 0 */ void -MidiDiskstream::get_playback(MidiBuffer& dst, nframes_t start, nframes_t end) +MidiDiskstream::get_playback(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t offset) { - dst.clear(); - assert(dst.size() == 0); + if (offset == 0) { + dst.clear(); + assert(dst.size() == 0); + } // Reverse. ... We just don't do reverse, ok? Back off. if (end <= start) { return; } - // Translates stamps to be relative to start - _playback_buf->read(dst, start, end); + // Check only events added this offset cycle + MidiBuffer::iterator this_cycle_start = dst.end(); + + // Translates stamps to be relative to start, but add offset. + _playback_buf->read(dst, start, end, offset); + + + // Now feed the data through the MidiStateTracker. + // In case it detects a LoopEvent it will add necessary note + // offs. + + if (_midistate_tracker.track(this_cycle_start, dst.end())) + _midistate_tracker.resolve_notes(dst, end-start - 1 + offset); } diff --git a/libs/ardour/midi_track.cc b/libs/ardour/midi_track.cc index 662d19df3c..0a253f3963 100644 --- a/libs/ardour/midi_track.cc +++ b/libs/ardour/midi_track.cc @@ -510,7 +510,7 @@ MidiTrack::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, //const size_t limit = n_process_buffers().n_audio(); BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers()); - diskstream->get_playback(bufs.get_midi(0), start_frame, end_frame); + diskstream->get_playback(bufs.get_midi(0), start_frame, end_frame, offset); process_output_buffers (bufs, start_frame, end_frame, nframes, offset, (!_session.get_record_enabled() || !Config->get_do_not_record_plugins()), declick, (_meter_point != MeterInput)); diff --git a/libs/ardour/panner.cc b/libs/ardour/panner.cc index 6a6a840407..fe0a792b6b 100644 --- a/libs/ardour/panner.cc +++ b/libs/ardour/panner.cc @@ -970,12 +970,6 @@ Panner::state (bool full) node.add_property (X_("link_direction"), enum_2_string (_link_direction)); node.add_property (X_("bypassed"), (bypassed() ? "yes" : "no")); - snprintf (buf, sizeof (buf), "%d", _streampanners.size()); - node.add_property (X_("ins"), buf); - snprintf (buf, sizeof (buf), "%d", outputs.size()); - node.add_property (X_("outs"), buf); - /* add each output */ - for (vector::iterator o = outputs.begin(); o != outputs.end(); ++o) { XMLNode* onode = new XMLNode (X_("Output")); snprintf (buf, sizeof (buf), "%.12g", (*o).x); -- cgit v1.2.3