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
This commit is contained in:
parent
71452634a7
commit
6e167cb1a8
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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 ();
|
||||
|
|
|
@ -129,8 +129,8 @@ namespace ARDOUR {
|
|||
};
|
||||
|
||||
enum NoteMode {
|
||||
Note,
|
||||
Percussion
|
||||
Sustained,
|
||||
Percussive
|
||||
};
|
||||
|
||||
struct BBT_Time {
|
||||
|
|
|
@ -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 ();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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?
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
void
|
||||
MidiTrack::set_note_mode (NoteMode m)
|
||||
{
|
||||
assert(_diskstream);
|
||||
|
||||
if (m != _mode) {
|
||||
|
||||
if (_diskstream->set_destructive (m == Destructive)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
_mode = m;
|
||||
|
||||
TrackModeChanged (); /* EMIT SIGNAL */
|
||||
}
|
||||
|
||||
return 0;
|
||||
_note_mode = m;
|
||||
midi_diskstream()->set_note_mode(m);
|
||||
}
|
||||
#endif
|
||||
|
||||
/** \return true on success, false on failure (no buffer space left)
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user