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:
David Robillard 2007-07-28 07:09:21 +00:00
parent 71452634a7
commit 6e167cb1a8
19 changed files with 304 additions and 91 deletions

View File

@ -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));

View File

@ -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);

View File

@ -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;
}

View File

@ -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

View File

@ -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 */

View File

@ -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
};

View File

@ -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());

View File

@ -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:

View File

@ -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:

View File

@ -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 ();

View File

@ -129,8 +129,8 @@ namespace ARDOUR {
};
enum NoteMode {
Note,
Percussion
Sustained,
Percussive
};
struct BBT_Time {

View File

@ -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 ();
}
}

View File

@ -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);

View File

@ -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;

View File

@ -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 */
}

View File

@ -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);

View File

@ -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?
}

View File

@ -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)
*/

View File

@ -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);
}