track notes at the region level in MidiPlaylist; resolve them (deliver note offs) if a note spans the end of the region
git-svn-id: svn://localhost/ardour2/branches/3.0@5804 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
parent
a86b994c68
commit
539a692b0e
|
@ -25,6 +25,7 @@
|
|||
|
||||
#include "ardour/ardour.h"
|
||||
#include "ardour/playlist.h"
|
||||
#include "ardour/midi_state_tracker.h"
|
||||
#include "evoral/Parameter.hpp"
|
||||
|
||||
namespace ARDOUR
|
||||
|
@ -74,6 +75,10 @@ private:
|
|||
bool region_changed (Change, boost::shared_ptr<Region>);
|
||||
|
||||
NoteMode _note_mode;
|
||||
|
||||
typedef std::map<Region*,MidiStateTracker*> NoteTrackers;
|
||||
NoteTrackers _note_trackers;
|
||||
|
||||
};
|
||||
|
||||
} /* namespace ARDOUR */
|
||||
|
|
|
@ -42,6 +42,7 @@ class Playlist;
|
|||
class Session;
|
||||
class MidiFilter;
|
||||
class MidiSource;
|
||||
class MidiStateTracker;
|
||||
template<typename T> class MidiRingBuffer;
|
||||
|
||||
class MidiRegion : public Region
|
||||
|
@ -56,10 +57,11 @@ class MidiRegion : public Region
|
|||
virtual sframes_t readable_length() const { return length(); }
|
||||
|
||||
nframes_t read_at (MidiRingBuffer<nframes_t>& dst,
|
||||
sframes_t position,
|
||||
nframes_t dur,
|
||||
uint32_t chan_n = 0,
|
||||
NoteMode mode = Sustained) const;
|
||||
sframes_t position,
|
||||
nframes_t dur,
|
||||
uint32_t chan_n = 0,
|
||||
NoteMode mode = Sustained,
|
||||
MidiStateTracker* tracker = 0) const;
|
||||
|
||||
nframes_t master_read_at (MidiRingBuffer<nframes_t>& dst,
|
||||
sframes_t position,
|
||||
|
@ -104,10 +106,11 @@ class MidiRegion : public Region
|
|||
|
||||
private:
|
||||
nframes_t _read_at (const SourceList&, MidiRingBuffer<nframes_t>& dst,
|
||||
nframes_t position,
|
||||
nframes_t dur,
|
||||
uint32_t chan_n = 0,
|
||||
NoteMode mode = Sustained) const;
|
||||
sframes_t position,
|
||||
nframes_t dur,
|
||||
uint32_t chan_n = 0,
|
||||
NoteMode mode = Sustained,
|
||||
MidiStateTracker* tracker = 0) const;
|
||||
|
||||
void recompute_at_start ();
|
||||
void recompute_at_end ();
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
|
||||
namespace ARDOUR {
|
||||
|
||||
class MidiStateTracker;
|
||||
class MidiModel;
|
||||
template<typename T> class MidiRingBuffer;
|
||||
|
||||
|
@ -55,11 +56,12 @@ class MidiSource : virtual public Source
|
|||
* \param cnt Length of range to be read (in audio frames)
|
||||
* \param stamp_offset Offset to add to event times written to dst
|
||||
* \param negative_stamp_offset Offset to subtract from event times written to dst
|
||||
* \param tracker an optional pointer to MidiStateTracker object, for note on/off tracking
|
||||
*/
|
||||
virtual nframes_t midi_read (MidiRingBuffer<nframes_t>& dst,
|
||||
sframes_t source_start,
|
||||
sframes_t start, nframes_t cnt,
|
||||
sframes_t stamp_offset, sframes_t negative_stamp_offset) const;
|
||||
sframes_t source_start,
|
||||
sframes_t start, nframes_t cnt,
|
||||
sframes_t stamp_offset, sframes_t negative_stamp_offset, MidiStateTracker*) const;
|
||||
|
||||
virtual nframes_t midi_write (MidiRingBuffer<nframes_t>& src,
|
||||
sframes_t source_start,
|
||||
|
@ -113,9 +115,10 @@ class MidiSource : virtual public Source
|
|||
virtual void flush_midi() = 0;
|
||||
|
||||
virtual nframes_t read_unlocked (MidiRingBuffer<nframes_t>& dst,
|
||||
sframes_t position,
|
||||
sframes_t start, nframes_t cnt,
|
||||
sframes_t stamp_offset, sframes_t negative_stamp_offset) const = 0;
|
||||
sframes_t position,
|
||||
sframes_t start, nframes_t cnt,
|
||||
sframes_t stamp_offset, sframes_t negative_stamp_offset,
|
||||
MidiStateTracker* tracker) const = 0;
|
||||
|
||||
virtual nframes_t write_unlocked (MidiRingBuffer<nframes_t>& dst,
|
||||
sframes_t position,
|
||||
|
|
|
@ -21,11 +21,11 @@
|
|||
#define __ardour_midi_state_tracker_h__
|
||||
|
||||
#include <bitset>
|
||||
|
||||
#include "ardour/midi_buffer.h"
|
||||
|
||||
namespace ARDOUR {
|
||||
|
||||
template <typename T> class MidiRingBuffer;
|
||||
|
||||
/** Tracks played notes, so they can be resolved in potential stuck note
|
||||
* situations (e.g. looping, transport stop, etc).
|
||||
|
@ -36,7 +36,10 @@ public:
|
|||
MidiStateTracker();
|
||||
|
||||
void track (const MidiBuffer::iterator& from, const MidiBuffer::iterator& to, bool& looped);
|
||||
void add (uint8_t note, uint8_t chn);
|
||||
void remove (uint8_t note, uint8_t chn);
|
||||
void resolve_notes (MidiBuffer& buffer, nframes64_t time);
|
||||
void resolve_notes (MidiRingBuffer<nframes_t>& buffer, nframes64_t time);
|
||||
void dump (std::ostream&);
|
||||
void reset ();
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include "ardour/track.h"
|
||||
#include "ardour/midi_ring_buffer.h"
|
||||
#include "ardour/midi_state_tracker.h"
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
@ -96,8 +97,7 @@ protected:
|
|||
int _set_state (const XMLNode&, int, bool call_base);
|
||||
|
||||
private:
|
||||
void write_out_of_band_data (
|
||||
BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes);
|
||||
void write_out_of_band_data (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes);
|
||||
|
||||
int set_diskstream (boost::shared_ptr<MidiDiskstream> ds);
|
||||
void use_new_diskstream ();
|
||||
|
@ -111,7 +111,6 @@ private:
|
|||
uint8_t _default_channel;
|
||||
bool _midi_thru;
|
||||
|
||||
|
||||
int no_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame,
|
||||
bool state_changing, bool can_record, bool rec_monitors_input);
|
||||
void push_midi_input_to_step_edit_ringbuffer (nframes_t nframes);
|
||||
|
|
|
@ -71,12 +71,13 @@ public:
|
|||
|
||||
private:
|
||||
nframes_t read_unlocked (MidiRingBuffer<nframes_t>& dst,
|
||||
sframes_t position,
|
||||
sframes_t start,
|
||||
nframes_t cnt,
|
||||
sframes_t stamp_offset,
|
||||
sframes_t negative_stamp_offset) const;
|
||||
|
||||
sframes_t position,
|
||||
sframes_t start,
|
||||
nframes_t cnt,
|
||||
sframes_t stamp_offset,
|
||||
sframes_t negative_stamp_offset,
|
||||
MidiStateTracker* tracker) const;
|
||||
|
||||
nframes_t write_unlocked (MidiRingBuffer<nframes_t>& src,
|
||||
sframes_t position,
|
||||
nframes_t cnt);
|
||||
|
|
|
@ -142,6 +142,16 @@ MidiPlaylist::read (MidiRingBuffer<nframes_t>& dst, nframes_t start, nframes_t d
|
|||
for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
|
||||
if ((*i)->coverage (start, end) != OverlapNone) {
|
||||
regs.push_back(*i);
|
||||
} else {
|
||||
/* region does not cover the current read boundaries, so make
|
||||
sure that we silence any notes that it had turned on
|
||||
*/
|
||||
NoteTrackers::iterator t = _note_trackers.find ((*i).get());
|
||||
if (t != _note_trackers.end()) {
|
||||
t->second->resolve_notes (dst, (*i)->last_frame());
|
||||
delete t->second;
|
||||
_note_trackers.erase (t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,7 +161,20 @@ MidiPlaylist::read (MidiRingBuffer<nframes_t>& dst, nframes_t start, nframes_t d
|
|||
for (vector<boost::shared_ptr<Region> >::iterator i = regs.begin(); i != regs.end(); ++i) {
|
||||
boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(*i);
|
||||
if (mr) {
|
||||
mr->read_at (dst, start, dur, chan_n, _note_mode);
|
||||
|
||||
NoteTrackers::iterator t = _note_trackers.find ((*i).get());
|
||||
MidiStateTracker* tracker;
|
||||
|
||||
if (t == _note_trackers.end()) {
|
||||
pair<Region*,MidiStateTracker*> newpair;
|
||||
newpair.first = (*i).get();
|
||||
tracker = newpair.second = new MidiStateTracker;
|
||||
_note_trackers.insert (newpair);
|
||||
} else {
|
||||
tracker = t->second;
|
||||
}
|
||||
|
||||
mr->read_at (dst, start, dur, chan_n, _note_mode, tracker);
|
||||
_read_data_count += mr->read_data_count();
|
||||
}
|
||||
}
|
||||
|
@ -159,11 +182,18 @@ MidiPlaylist::read (MidiRingBuffer<nframes_t>& dst, nframes_t start, nframes_t d
|
|||
return dur;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MidiPlaylist::remove_dependents (boost::shared_ptr<Region> /*region*/)
|
||||
MidiPlaylist::remove_dependents (boost::shared_ptr<Region> region)
|
||||
{
|
||||
/* MIDI regions have no dependents (crossfades) */
|
||||
/* MIDI regions have no dependents (crossfades) but we might be tracking notes */
|
||||
NoteTrackers::iterator t = _note_trackers.find (region.get());
|
||||
|
||||
/* GACK! THREAD SAFETY! */
|
||||
|
||||
if (t != _note_trackers.end()) {
|
||||
delete t->second;
|
||||
_note_trackers.erase (t);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -130,19 +130,20 @@ MidiRegion::set_position_internal (nframes_t pos, bool allow_bbt_recompute)
|
|||
}
|
||||
|
||||
nframes_t
|
||||
MidiRegion::read_at (MidiRingBuffer<nframes_t>& out, sframes_t position, nframes_t dur, uint32_t chan_n, NoteMode mode) const
|
||||
MidiRegion::read_at (MidiRingBuffer<nframes_t>& out, sframes_t position, nframes_t dur, uint32_t chan_n, NoteMode mode, MidiStateTracker* tracker) const
|
||||
{
|
||||
return _read_at (_sources, out, position, dur, chan_n, mode);
|
||||
return _read_at (_sources, out, position, dur, chan_n, mode, tracker);
|
||||
}
|
||||
|
||||
nframes_t
|
||||
MidiRegion::master_read_at (MidiRingBuffer<nframes_t>& out, sframes_t position, nframes_t dur, uint32_t chan_n, NoteMode mode) const
|
||||
{
|
||||
return _read_at (_master_sources, out, position, dur, chan_n, mode);
|
||||
return _read_at (_master_sources, out, position, dur, chan_n, mode); /* no tracker */
|
||||
}
|
||||
|
||||
nframes_t
|
||||
MidiRegion::_read_at (const SourceList& /*srcs*/, MidiRingBuffer<nframes_t>& dst, nframes_t position, nframes_t dur, uint32_t chan_n, NoteMode mode) const
|
||||
MidiRegion::_read_at (const SourceList& /*srcs*/, MidiRingBuffer<nframes_t>& dst, sframes_t position, nframes_t dur, uint32_t chan_n,
|
||||
NoteMode mode, MidiStateTracker* tracker) const
|
||||
{
|
||||
nframes_t internal_offset = 0;
|
||||
nframes_t src_offset = 0;
|
||||
|
@ -207,8 +208,9 @@ MidiRegion::_read_at (const SourceList& /*srcs*/, MidiRingBuffer<nframes_t>& dst
|
|||
_start + internal_offset, // where to start reading in the source
|
||||
to_read, // read duration in frames
|
||||
output_buffer_position, // the offset in the output buffer
|
||||
negative_output_buffer_position // amount to substract from note times
|
||||
) != to_read) {
|
||||
negative_output_buffer_position, // amount to substract from note times
|
||||
tracker
|
||||
) != to_read) {
|
||||
return 0; /* "read nothing" */
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "ardour/audioengine.h"
|
||||
#include "ardour/midi_model.h"
|
||||
#include "ardour/midi_ring_buffer.h"
|
||||
#include "ardour/midi_state_tracker.h"
|
||||
#include "ardour/midi_source.h"
|
||||
#include "ardour/session.h"
|
||||
#include "ardour/session_directory.h"
|
||||
|
@ -126,8 +127,9 @@ MidiSource::invalidate ()
|
|||
|
||||
nframes_t
|
||||
MidiSource::midi_read (MidiRingBuffer<nframes_t>& dst, sframes_t source_start,
|
||||
sframes_t start, nframes_t cnt,
|
||||
sframes_t stamp_offset, sframes_t negative_stamp_offset) const
|
||||
sframes_t start, nframes_t cnt,
|
||||
sframes_t stamp_offset, sframes_t negative_stamp_offset,
|
||||
MidiStateTracker* tracker) const
|
||||
{
|
||||
Glib::Mutex::Lock lm (_lock);
|
||||
|
||||
|
@ -152,13 +154,21 @@ MidiSource::midi_read (MidiRingBuffer<nframes_t>& dst, sframes_t source_start,
|
|||
const sframes_t time_frames = BEATS_TO_FRAMES(i->time());
|
||||
if (time_frames < source_start + start + cnt) {
|
||||
dst.write(time_frames, i->event_type(), i->size(), i->buffer());
|
||||
if (tracker) {
|
||||
Evoral::MIDIEvent<Evoral::MusicalTime>& ev (*(Evoral::MIDIEvent<Evoral::MusicalTime>*) (&(*i)));
|
||||
if (ev.is_note_on()) {
|
||||
tracker->add (ev.note(), ev.channel());
|
||||
} else if (ev.is_note_off()) {
|
||||
tracker->remove (ev.note(), ev.channel());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return cnt;
|
||||
} else {
|
||||
return read_unlocked (dst, source_start, start, cnt, stamp_offset, negative_stamp_offset);
|
||||
return read_unlocked (dst, source_start, start, cnt, stamp_offset, negative_stamp_offset, tracker);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include <iostream>
|
||||
#include "ardour/event_type_map.h"
|
||||
#include "ardour/midi_ring_buffer.h"
|
||||
#include "ardour/midi_state_tracker.h"
|
||||
|
||||
using namespace std;
|
||||
|
@ -47,6 +48,21 @@ MidiStateTracker::track_note_onoffs (const Evoral::MIDIEvent<MidiBuffer::TimeTyp
|
|||
}
|
||||
}
|
||||
}
|
||||
void
|
||||
MidiStateTracker::add (uint8_t note, uint8_t chn)
|
||||
{
|
||||
cerr << "Added note " << note << " chan " << chn << endl;
|
||||
_active_notes[note + 128 * chn]++;
|
||||
}
|
||||
|
||||
void
|
||||
MidiStateTracker::remove (uint8_t note, uint8_t chn)
|
||||
{
|
||||
if (_active_notes[note + 128 * chn]) {
|
||||
cerr << "Removed note " << note << " chan " << chn << endl;
|
||||
_active_notes[note + 128 * chn]--;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MidiStateTracker::track (const MidiBuffer::iterator &from, const MidiBuffer::iterator &to, bool& looped)
|
||||
|
@ -81,6 +97,23 @@ MidiStateTracker::resolve_notes (MidiBuffer &dst, nframes64_t time)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
MidiStateTracker::resolve_notes (MidiRingBuffer<nframes_t> &dst, nframes64_t time)
|
||||
{
|
||||
uint8_t buf[3];
|
||||
for (int channel = 0; channel < 16; ++channel) {
|
||||
for (int note = 0; note < 128; ++note) {
|
||||
while (_active_notes[channel * 128 + note]) {
|
||||
buf[0] = MIDI_CMD_NOTE_OFF|channel;
|
||||
buf[1] = note;
|
||||
buf[2] = 0;
|
||||
dst.write (time, EventTypeMap::instance().midi_event_type (buf[0]), 3, buf);
|
||||
_active_notes[channel * 128 + note]--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MidiStateTracker::dump (ostream& o)
|
||||
{
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "ardour/event_type_map.h"
|
||||
#include "ardour/midi_model.h"
|
||||
#include "ardour/midi_ring_buffer.h"
|
||||
#include "ardour/midi_state_tracker.h"
|
||||
#include "ardour/session.h"
|
||||
#include "ardour/smf_source.h"
|
||||
|
||||
|
@ -98,8 +99,9 @@ SMFSource::~SMFSource ()
|
|||
/** All stamps in audio frames */
|
||||
nframes_t
|
||||
SMFSource::read_unlocked (MidiRingBuffer<nframes_t>& destination, sframes_t source_start,
|
||||
sframes_t start, nframes_t duration,
|
||||
sframes_t stamp_offset, sframes_t negative_stamp_offset) const
|
||||
sframes_t start, nframes_t duration,
|
||||
sframes_t stamp_offset, sframes_t negative_stamp_offset,
|
||||
MidiStateTracker* tracker) const
|
||||
{
|
||||
int ret = 0;
|
||||
uint64_t time = 0; // in SMF ticks, 1 tick per _ppqn
|
||||
|
@ -166,11 +168,18 @@ SMFSource::read_unlocked (MidiRingBuffer<nframes_t>& destination, sframes_t sour
|
|||
|
||||
if (ev_frame_time < start + duration) {
|
||||
destination.write(ev_frame_time - negative_stamp_offset, ev_type, ev_size, ev_buffer);
|
||||
|
||||
if (tracker) {
|
||||
if (ev_buffer[0] & MIDI_CMD_NOTE_ON) {
|
||||
tracker->add (ev_buffer[1], ev_buffer[0] & 0xf);
|
||||
} else if (ev_buffer[0] & MIDI_CMD_NOTE_OFF) {
|
||||
tracker->remove (ev_buffer[1], ev_buffer[0] & 0xf);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
_read_data_count += ev_size;
|
||||
|
||||
if (ev_size > scratch_size) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user