mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-18 12:46:32 +01:00
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
19 changed files with 304 additions and 91 deletions
|
|
@ -142,7 +142,7 @@ MidiRegionView::canvas_event(GdkEvent* ev)
|
||||||
model->add_note(new_note);
|
model->add_note(new_note);
|
||||||
model->finish_command();
|
model->finish_command();
|
||||||
|
|
||||||
view->update_bounds(new_note.note);
|
view->update_bounds(new_note.note());
|
||||||
|
|
||||||
add_note(new_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 footer_height = name_highlight->property_y2() - name_highlight->property_y1();
|
||||||
const double pixel_range = (trackview.height - footer_height - 5.0) / (double)note_range;
|
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) {
|
if ((ev.buffer[0] & 0xF0) == MIDI_CMD_NOTE_ON) {
|
||||||
const Byte& note = ev.buffer[1];
|
const Byte& note = ev.buffer[1];
|
||||||
const double y1 = trackview.height - (pixel_range * (note - view->lowest_note() + 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 Byte& note = ev.buffer[1];
|
||||||
const double x = trackview.editor.frame_to_pixel((nframes_t)ev.time);
|
const double x = trackview.editor.frame_to_pixel((nframes_t)ev.time);
|
||||||
const double y = trackview.height - (pixel_range * (note - view->lowest_note() + 1))
|
const double y = trackview.height - (pixel_range * (note - view->lowest_note() + 1))
|
||||||
|
|
@ -393,9 +393,9 @@ MidiRegionView::extend_active_notes()
|
||||||
void
|
void
|
||||||
MidiRegionView::add_note (const MidiModel::Note& note)
|
MidiRegionView::add_note (const MidiModel::Note& note)
|
||||||
{
|
{
|
||||||
assert(note.start >= 0);
|
assert(note.time() >= 0);
|
||||||
assert(note.start < _region->length());
|
assert(note.time() < _region->length());
|
||||||
//assert(note.start + note.duration < _region->length());
|
//assert(note.time() + note.duration < _region->length());
|
||||||
|
|
||||||
/*printf("Event, time = %f, size = %zu, data = ", ev.time, ev.size);
|
/*printf("Event, time = %f, size = %zu, data = ", ev.time, ev.size);
|
||||||
for (size_t i=0; i < ev.size; ++i) {
|
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 footer_height = name_highlight->property_y2() - name_highlight->property_y1();
|
||||||
const double pixel_range = (trackview.height - footer_height - 5.0) / (double)note_range;
|
const double pixel_range = (trackview.height - footer_height - 5.0) / (double)note_range;
|
||||||
|
|
||||||
if (mtv->note_mode() == Note) {
|
if (mtv->note_mode() == Sustained) {
|
||||||
const double y1 = trackview.height - (pixel_range * (note.note - view->lowest_note() + 1))
|
const double y1 = trackview.height - (pixel_range * (note.note() - view->lowest_note() + 1))
|
||||||
- footer_height - 3.0;
|
- footer_height - 3.0;
|
||||||
|
|
||||||
ArdourCanvas::SimpleRect * ev_rect = new Gnome::Canvas::SimpleRect(*group);
|
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_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_y2() = y1 + ceil(pixel_range);
|
||||||
|
|
||||||
ev_rect->property_fill_color_rgba() = 0xFFFFFF66;
|
ev_rect->property_fill_color_rgba() = 0xFFFFFF66;
|
||||||
|
|
@ -428,9 +428,9 @@ MidiRegionView::add_note (const MidiModel::Note& note)
|
||||||
ev_rect->show();
|
ev_rect->show();
|
||||||
_events.push_back(ev_rect);
|
_events.push_back(ev_rect);
|
||||||
|
|
||||||
} else if (mtv->note_mode() == Percussion) {
|
} else if (mtv->note_mode() == Percussive) {
|
||||||
const double x = trackview.editor.frame_to_pixel((nframes_t)note.start);
|
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))
|
const double y = trackview.height - (pixel_range * (note.note() - view->lowest_note() + 1))
|
||||||
- footer_height - 3.0;
|
- footer_height - 3.0;
|
||||||
|
|
||||||
Diamond* ev_diamond = new Diamond(*group, std::min(pixel_range, 5.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) {
|
for (size_t i=0; i < source->model()->n_notes(); ++i) {
|
||||||
const MidiModel::Note& note = source->model()->note_at(i);
|
const MidiModel::Note& note = source->model()->note_at(i);
|
||||||
|
|
||||||
update_bounds(note.note);
|
update_bounds(note.note());
|
||||||
|
|
||||||
if (redisplay_events)
|
if (redisplay_events)
|
||||||
region_view->add_note(note);
|
region_view->add_note(note);
|
||||||
|
|
|
||||||
|
|
@ -206,15 +206,15 @@ MidiTimeAxisView::build_mode_menu()
|
||||||
mode_menu->set_name ("ArdourContextMenu");
|
mode_menu->set_name ("ArdourContextMenu");
|
||||||
|
|
||||||
RadioMenuItem::Group mode_group;
|
RadioMenuItem::Group mode_group;
|
||||||
items.push_back (RadioMenuElem (mode_group, _("Note"),
|
items.push_back (RadioMenuElem (mode_group, _("Sustained"),
|
||||||
bind (mem_fun (*this, &MidiTimeAxisView::set_note_mode), Note)));
|
bind (mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained)));
|
||||||
_note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
|
_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"),
|
items.push_back (RadioMenuElem (mode_group, _("Percussive"),
|
||||||
bind (mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussion)));
|
bind (mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive)));
|
||||||
_percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
|
_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;
|
return mode_menu;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ private:
|
||||||
const MidiEvent& operator[](size_t i) const { assert(i < _size); return _events[i]; }
|
const MidiEvent& operator[](size_t i) const { assert(i < _size); return _events[i]; }
|
||||||
MidiEvent& operator[](size_t i) { 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
|
static const size_t MAX_EVENT_SIZE = 4; // bytes
|
||||||
|
|
||||||
/* We use _size as "number of events", so the size of _data is
|
/* We use _size as "number of events", so the size of _data is
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,8 @@ class MidiDiskstream : public Diskstream
|
||||||
|
|
||||||
int set_destructive (bool yn); // doom!
|
int set_destructive (bool yn); // doom!
|
||||||
|
|
||||||
|
void set_note_mode (NoteMode m);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class Session;
|
friend class Session;
|
||||||
|
|
||||||
|
|
@ -154,6 +156,7 @@ class MidiDiskstream : public Diskstream
|
||||||
boost::shared_ptr<SMFSource> _write_source;
|
boost::shared_ptr<SMFSource> _write_source;
|
||||||
RingBufferNPT<CaptureTransition>* _capture_transition_buf;
|
RingBufferNPT<CaptureTransition>* _capture_transition_buf;
|
||||||
nframes_t _last_flush_frame;
|
nframes_t _last_flush_frame;
|
||||||
|
NoteMode _note_mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
}; /* namespace ARDOUR */
|
}; /* namespace ARDOUR */
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@
|
||||||
#include <pbd/command.h>
|
#include <pbd/command.h>
|
||||||
#include <ardour/types.h>
|
#include <ardour/types.h>
|
||||||
#include <ardour/midi_buffer.h>
|
#include <ardour/midi_buffer.h>
|
||||||
|
#include <ardour/midi_ring_buffer.h>
|
||||||
|
|
||||||
namespace ARDOUR {
|
namespace ARDOUR {
|
||||||
|
|
||||||
|
|
@ -40,25 +41,46 @@ class Session;
|
||||||
class MidiModel : public boost::noncopyable {
|
class MidiModel : public boost::noncopyable {
|
||||||
public:
|
public:
|
||||||
struct Note {
|
struct Note {
|
||||||
Note(double s=0, double d=0, uint8_t n=0, uint8_t v=0)
|
Note(double time=0, double dur=0, uint8_t note=0, uint8_t vel=0x40);
|
||||||
: start(s), duration(d), note(n), velocity(v) {}
|
Note(const Note& copy);
|
||||||
|
|
||||||
inline bool operator==(const Note& other)
|
inline bool operator==(const Note& other)
|
||||||
{ return start == other.start && note == other.note; }
|
{ return time() == other.time() && note() == other.note(); }
|
||||||
|
|
||||||
double start;
|
inline double time() const { return _on_event.time; }
|
||||||
double duration;
|
inline double end_time() const { return _off_event.time; }
|
||||||
uint8_t note;
|
inline uint8_t note() const { return _on_event.note(); }
|
||||||
uint8_t velocity;
|
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);
|
MidiModel(Session& s, size_t size=0);
|
||||||
|
|
||||||
void clear() { _notes.clear(); }
|
void clear() { _notes.clear(); }
|
||||||
|
|
||||||
|
NoteMode note_mode() const { return _note_mode; }
|
||||||
|
void set_note_mode(NoteMode mode) { _note_mode = mode; }
|
||||||
|
|
||||||
void start_write();
|
void start_write();
|
||||||
|
bool currently_writing() const { return _writing; }
|
||||||
void end_write(bool delete_stuck=false);
|
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) */
|
/** Resizes vector if necessary (NOT realtime safe) */
|
||||||
void append(const MidiBuffer& data);
|
void append(const MidiBuffer& data);
|
||||||
|
|
||||||
|
|
@ -72,9 +94,16 @@ public:
|
||||||
typedef std::vector<Note> Notes;
|
typedef std::vector<Note> Notes;
|
||||||
|
|
||||||
inline static bool note_time_comparator (const Note& a, const Note& b) {
|
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 Notes& notes() { return _notes; }
|
||||||
inline const Notes& notes() const { return _notes; }
|
inline const Notes& notes() const { return _notes; }
|
||||||
|
|
||||||
|
|
@ -115,10 +144,12 @@ private:
|
||||||
|
|
||||||
Session& _session;
|
Session& _session;
|
||||||
|
|
||||||
Notes _notes;
|
Notes _notes;
|
||||||
|
NoteMode _note_mode;
|
||||||
|
|
||||||
typedef std::vector<size_t> WriteNotes;
|
typedef std::vector<size_t> WriteNotes;
|
||||||
WriteNotes _write_notes;
|
WriteNotes _write_notes;
|
||||||
|
bool _writing;
|
||||||
|
|
||||||
MidiEditCommand* _command; ///< In-progress command
|
MidiEditCommand* _command; ///< In-progress command
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
#ifndef __ardour_midi_ring_buffer_h__
|
#ifndef __ardour_midi_ring_buffer_h__
|
||||||
#define __ardour_midi_ring_buffer_h__
|
#define __ardour_midi_ring_buffer_h__
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <ardour/types.h>
|
#include <ardour/types.h>
|
||||||
#include <ardour/buffer.h>
|
#include <ardour/buffer.h>
|
||||||
|
|
@ -252,6 +253,9 @@ MidiRingBuffer::read(double* time, size_t* size, Byte* buf)
|
||||||
inline size_t
|
inline size_t
|
||||||
MidiRingBuffer::write(double time, size_t size, const Byte* buf)
|
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);
|
assert(size > 0);
|
||||||
|
|
||||||
if (write_space() < (sizeof(double) + sizeof(size_t) + size)) {
|
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;
|
size_t count = 0;
|
||||||
|
|
||||||
|
//printf("MRB - read %u .. %u + %u\n", start, end, offset);
|
||||||
|
|
||||||
while (read_space() > sizeof(double) + sizeof(size_t)) {
|
while (read_space() > sizeof(double) + sizeof(size_t)) {
|
||||||
|
|
||||||
full_peek(sizeof(double), (Byte*)&ev.time);
|
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);
|
success = MidiRingBufferBase<Byte>::full_read(sizeof(size_t), (Byte*)&ev.size);
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
cerr << "MRB: READ ERROR (time/size)" << endl;
|
std::cerr << "MRB: READ ERROR (time/size)" << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ev.time >= start) {
|
if (ev.time >= start) {
|
||||||
|
ev.time -= start;
|
||||||
Byte* write_loc = dst.reserve(ev.time, ev.size);
|
Byte* write_loc = dst.reserve(ev.time, ev.size);
|
||||||
success = MidiRingBufferBase<Byte>::full_read(ev.size, write_loc);
|
success = MidiRingBufferBase<Byte>::full_read(ev.size, write_loc);
|
||||||
|
|
||||||
if (!success)
|
if (success) {
|
||||||
cerr << "MRB: READ ERROR (data)" << endl;
|
++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 {
|
} else {
|
||||||
printf("MRB - SKIPPING EVENT (with time %f)\n", ev.time);
|
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());
|
//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 nframes_t write (MidiRingBuffer& src, nframes_t cnt);
|
||||||
|
|
||||||
virtual void mark_for_remove() = 0;
|
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; }
|
string captured_for() const { return _captured_for; }
|
||||||
void set_captured_for (string str) { _captured_for = str; }
|
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 load_model(bool lock=true, bool force_reload=false) = 0;
|
||||||
virtual void destroy_model() = 0;
|
virtual void destroy_model() = 0;
|
||||||
|
|
||||||
|
virtual bool model_loaded() const { return _model_loaded; }
|
||||||
|
|
||||||
MidiModel* model() { return _model; }
|
MidiModel* model() { return _model; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
NoteMode note_mode() const { return _note_mode; }
|
NoteMode note_mode() const { return _note_mode; }
|
||||||
void set_note_mode (NoteMode m) { _note_mode = m; }
|
void set_note_mode (NoteMode m);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@ class Source : public SessionObject
|
||||||
virtual nframes_t natural_position() const { return 0; }
|
virtual nframes_t natural_position() const { return 0; }
|
||||||
|
|
||||||
virtual void mark_for_remove() = 0;
|
virtual void mark_for_remove() = 0;
|
||||||
|
virtual void mark_streaming_write_started () {}
|
||||||
virtual void mark_streaming_write_completed () = 0;
|
virtual void mark_streaming_write_completed () = 0;
|
||||||
|
|
||||||
XMLNode& get_state ();
|
XMLNode& get_state ();
|
||||||
|
|
|
||||||
|
|
@ -129,8 +129,8 @@ namespace ARDOUR {
|
||||||
};
|
};
|
||||||
|
|
||||||
enum NoteMode {
|
enum NoteMode {
|
||||||
Note,
|
Sustained,
|
||||||
Percussion
|
Percussive
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BBT_Time {
|
struct BBT_Time {
|
||||||
|
|
|
||||||
|
|
@ -1794,11 +1794,13 @@ AudioDiskstream::engage_record_enable ()
|
||||||
(*chan)->source->ensure_monitor_input (!(Config->get_auto_input() && rolling));
|
(*chan)->source->ensure_monitor_input (!(Config->get_auto_input() && rolling));
|
||||||
}
|
}
|
||||||
capturing_sources.push_back ((*chan)->write_source);
|
capturing_sources.push_back ((*chan)->write_source);
|
||||||
|
(*chan)->write_source->mark_streaming_write_started ();
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
|
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
|
||||||
capturing_sources.push_back ((*chan)->write_source);
|
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_ENUM (Destructive);
|
||||||
REGISTER (_TrackMode);
|
REGISTER (_TrackMode);
|
||||||
|
|
||||||
REGISTER_ENUM (Note);
|
REGISTER_ENUM (Sustained);
|
||||||
REGISTER_ENUM (Percussion);
|
REGISTER_ENUM (Percussive);
|
||||||
REGISTER (_NoteMode);
|
REGISTER (_NoteMode);
|
||||||
|
|
||||||
REGISTER_ENUM (MeterFalloffOff);
|
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) {
|
for (size_t i=0; i < src.size(); ++i) {
|
||||||
const MidiEvent& ev = msrc[i];
|
const MidiEvent& ev = msrc[i];
|
||||||
if (ev.time >= offset && ev.time < offset+nframes) {
|
if (ev.time >= offset && ev.time < offset+nframes) {
|
||||||
|
//cerr << "MidiBuffer::read_from got event, " << ev.time << endl;
|
||||||
push_back(ev);
|
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*
|
Byte*
|
||||||
MidiBuffer::reserve(double time, size_t size)
|
MidiBuffer::reserve(double time, size_t size)
|
||||||
{
|
{
|
||||||
assert(size < MAX_EVENT_SIZE);
|
assert(size <= MAX_EVENT_SIZE);
|
||||||
|
|
||||||
if (_size == _capacity)
|
if (_size == _capacity)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,7 @@ MidiDiskstream::MidiDiskstream (Session &sess, const string &name, Diskstream::F
|
||||||
, _source_port(0)
|
, _source_port(0)
|
||||||
, _capture_transition_buf(0)
|
, _capture_transition_buf(0)
|
||||||
, _last_flush_frame(0)
|
, _last_flush_frame(0)
|
||||||
|
, _note_mode(Sustained)
|
||||||
{
|
{
|
||||||
/* prevent any write sources from being created */
|
/* prevent any write sources from being created */
|
||||||
|
|
||||||
|
|
@ -92,6 +93,7 @@ MidiDiskstream::MidiDiskstream (Session& sess, const XMLNode& node)
|
||||||
, _source_port(0)
|
, _source_port(0)
|
||||||
, _capture_transition_buf(0)
|
, _capture_transition_buf(0)
|
||||||
, _last_flush_frame(0)
|
, _last_flush_frame(0)
|
||||||
|
, _note_mode(Sustained)
|
||||||
{
|
{
|
||||||
in_set_state = true;
|
in_set_state = true;
|
||||||
init (Recordable);
|
init (Recordable);
|
||||||
|
|
@ -306,6 +308,14 @@ MidiDiskstream::set_destructive (bool yn)
|
||||||
return -1;
|
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
|
void
|
||||||
MidiDiskstream::check_record_status (nframes_t transport_frame, nframes_t nframes, bool can_record)
|
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));
|
_source_port->request_monitor_input (!(Config->get_auto_input() && rolling));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_write_source->mark_streaming_midi_write_started (_note_mode);
|
||||||
|
|
||||||
RecordEnableChanged (); /* EMIT SIGNAL */
|
RecordEnableChanged (); /* EMIT SIGNAL */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <queue>
|
||||||
|
#include <pbd/enumwriter.h>
|
||||||
#include <ardour/midi_model.h>
|
#include <ardour/midi_model.h>
|
||||||
#include <ardour/midi_events.h>
|
#include <ardour/midi_events.h>
|
||||||
#include <ardour/types.h>
|
#include <ardour/types.h>
|
||||||
|
|
@ -27,41 +29,155 @@
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace ARDOUR;
|
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)
|
MidiModel::MidiModel(Session& s, size_t size)
|
||||||
: _session(s)
|
: _session(s)
|
||||||
, _notes(size)
|
, _notes(size)
|
||||||
|
, _note_mode(Sustained)
|
||||||
|
, _writing(false)
|
||||||
, _command(NULL)
|
, _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.
|
/** Begin a write of events to the model.
|
||||||
*
|
*
|
||||||
* As note on and off events are written, complete notes with duration are
|
* If \a mode is Sustained, complete notes with duration are constructed as note
|
||||||
* constructed
|
* 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
|
void
|
||||||
MidiModel::start_write()
|
MidiModel::start_write()
|
||||||
{
|
{
|
||||||
|
//cerr << "MM START WRITE, MODE = " << enum_2_string(_note_mode) << endl;
|
||||||
_write_notes.clear();
|
_write_notes.clear();
|
||||||
|
_writing = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** Finish a write of events to the model.
|
/** Finish a write of events to the model.
|
||||||
*
|
*
|
||||||
* If \a delete_stuck is true, note on events that were never resolved with
|
* If \a delete_stuck is true and the current mode is Sustained, note on events
|
||||||
* a corresonding note off will be deleted. Otherwise they will remain as
|
* that were never resolved with a corresonding note off will be deleted.
|
||||||
* notes with duration 0.
|
* Otherwise they will remain as notes with duration 0.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
MidiModel::end_write(bool delete_stuck)
|
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() ; ) {
|
for (Notes::iterator n = _notes.begin(); n != _notes.end() ; ) {
|
||||||
if (n->duration == 0) {
|
if (n->duration() == 0) {
|
||||||
cerr << "WARNING: Stuck note lost: " << n->note << endl;
|
cerr << "WARNING: Stuck note lost: " << n->note() << endl;
|
||||||
n = _notes.erase(n);
|
n = _notes.erase(n);
|
||||||
} else {
|
} else {
|
||||||
++n;
|
++n;
|
||||||
|
|
@ -70,6 +186,7 @@ MidiModel::end_write(bool delete_stuck)
|
||||||
}
|
}
|
||||||
|
|
||||||
_write_notes.clear();
|
_write_notes.clear();
|
||||||
|
_writing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -84,10 +201,12 @@ MidiModel::end_write(bool delete_stuck)
|
||||||
void
|
void
|
||||||
MidiModel::append(const MidiBuffer& buf)
|
MidiModel::append(const MidiBuffer& buf)
|
||||||
{
|
{
|
||||||
|
assert(_writing);
|
||||||
|
|
||||||
for (MidiBuffer::const_iterator i = buf.begin(); i != buf.end(); ++i) {
|
for (MidiBuffer::const_iterator i = buf.begin(); i != buf.end(); ++i) {
|
||||||
const MidiEvent& ev = *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)
|
if (ev.type() == MIDI_CMD_NOTE_ON)
|
||||||
append_note_on(ev.time, ev.note(), ev.velocity());
|
append_note_on(ev.time, ev.note(), ev.velocity());
|
||||||
|
|
@ -106,7 +225,8 @@ MidiModel::append(const MidiBuffer& buf)
|
||||||
void
|
void
|
||||||
MidiModel::append(double time, size_t size, const Byte* buf)
|
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)
|
if ((buf[0] & 0xF0) == MIDI_CMD_NOTE_ON)
|
||||||
append_note_on(time, buf[1], buf[2]);
|
append_note_on(time, buf[1], buf[2]);
|
||||||
|
|
@ -118,28 +238,41 @@ MidiModel::append(double time, size_t size, const Byte* buf)
|
||||||
void
|
void
|
||||||
MidiModel::append_note_on(double time, uint8_t note_num, uint8_t velocity)
|
MidiModel::append_note_on(double time, uint8_t note_num, uint8_t velocity)
|
||||||
{
|
{
|
||||||
|
assert(_writing);
|
||||||
_notes.push_back(Note(time, 0, note_num, velocity));
|
_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
|
void
|
||||||
MidiModel::append_note_off(double time, uint8_t note_num)
|
MidiModel::append_note_off(double time, uint8_t note_num)
|
||||||
{
|
{
|
||||||
/* _write_notes (active notes) is presumably small enough for linear
|
assert(_writing);
|
||||||
* search to be a good idea. maybe not with instruments (percussion)
|
if (_note_mode == Percussive) {
|
||||||
* that don't send note off at all though.... FIXME? */
|
//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
|
/* FIXME: note off velocity for that one guy out there who actually has
|
||||||
* keys that send it */
|
* keys that send it */
|
||||||
|
|
||||||
for (WriteNotes::iterator n = _write_notes.begin(); n != _write_notes.end(); ++n) {
|
for (WriteNotes::iterator n = _write_notes.begin(); n != _write_notes.end(); ++n) {
|
||||||
Note& note = _notes[*n];
|
Note& note = _notes[*n];
|
||||||
if (note.note == note_num) {
|
//cerr << (unsigned)(uint8_t)note.note() << " ? " << (unsigned)note_num << endl;
|
||||||
assert(time > note.start);
|
if (note.note() == note_num) {
|
||||||
note.duration = time - note.start;
|
assert(time > note.time());
|
||||||
|
note.set_duration(time - note.time());
|
||||||
_write_notes.erase(n);
|
_write_notes.erase(n);
|
||||||
//cerr << "MidiModel resolved note, duration: " << note.duration << endl;
|
//cerr << "MidiModel resolved note, duration: " << note.duration() << endl;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -149,6 +282,7 @@ MidiModel::append_note_off(double time, uint8_t note_num)
|
||||||
void
|
void
|
||||||
MidiModel::add_note(const Note& note)
|
MidiModel::add_note(const Note& note)
|
||||||
{
|
{
|
||||||
|
// FIXME: take source lock
|
||||||
Notes::iterator i = upper_bound(_notes.begin(), _notes.end(), note, note_time_comparator);
|
Notes::iterator i = upper_bound(_notes.begin(), _notes.end(), note, note_time_comparator);
|
||||||
_notes.insert(i, note);
|
_notes.insert(i, note);
|
||||||
if (_command)
|
if (_command)
|
||||||
|
|
@ -159,6 +293,7 @@ MidiModel::add_note(const Note& note)
|
||||||
void
|
void
|
||||||
MidiModel::remove_note(const Note& note)
|
MidiModel::remove_note(const Note& note)
|
||||||
{
|
{
|
||||||
|
// FIXME: take source lock
|
||||||
Notes::iterator n = find(_notes.begin(), _notes.end(), note);
|
Notes::iterator n = find(_notes.begin(), _notes.end(), note);
|
||||||
if (n != _notes.end())
|
if (n != _notes.end())
|
||||||
_notes.erase(n);
|
_notes.erase(n);
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,13 @@ nframes_t
|
||||||
MidiSource::read (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, nframes_t stamp_offset) const
|
MidiSource::read (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, nframes_t stamp_offset) const
|
||||||
{
|
{
|
||||||
Glib::Mutex::Lock lm (_lock);
|
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
|
nframes_t
|
||||||
|
|
@ -119,3 +125,26 @@ MidiSource::file_changed (string path)
|
||||||
return ( !e1 );
|
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) {
|
if ((prop = node.property (X_("note-mode"))) != 0) {
|
||||||
_note_mode = NoteMode (string_2_enum (prop->value(), _note_mode));
|
_note_mode = NoteMode (string_2_enum (prop->value(), _note_mode));
|
||||||
} else {
|
} else {
|
||||||
_note_mode = Note;
|
_note_mode = Sustained;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((prop = node.property ("diskstream-id")) == 0) {
|
if ((prop = node.property ("diskstream-id")) == 0) {
|
||||||
|
|
@ -569,7 +569,6 @@ MidiTrack::process_output_buffers (BufferSet& bufs,
|
||||||
|
|
||||||
MidiBuffer& output_buf = bufs.get_midi(0);
|
MidiBuffer& output_buf = bufs.get_midi(0);
|
||||||
write_controller_messages(output_buf, start_frame, end_frame, nframes, offset);
|
write_controller_messages(output_buf, start_frame, end_frame, nframes, offset);
|
||||||
|
|
||||||
deliver_output(bufs, 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;
|
_freeze_record.state = UnFrozen;
|
||||||
FreezeChange (); /* EMIT SIGNAL */
|
FreezeChange (); /* EMIT SIGNAL */
|
||||||
}
|
}
|
||||||
#if 0
|
|
||||||
int
|
void
|
||||||
MidiTrack::set_mode (TrackMode m)
|
MidiTrack::set_note_mode (NoteMode m)
|
||||||
{
|
{
|
||||||
assert(_diskstream);
|
_note_mode = m;
|
||||||
|
midi_diskstream()->set_note_mode(m);
|
||||||
if (m != _mode) {
|
|
||||||
|
|
||||||
if (_diskstream->set_destructive (m == Destructive)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
_mode = m;
|
|
||||||
|
|
||||||
TrackModeChanged (); /* EMIT SIGNAL */
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
/** \return true on success, false on failure (no buffer space left)
|
/** \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;
|
const nframes_t oldlen = _length;
|
||||||
update_length(oldlen, cnt);
|
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 */
|
ViewDataRangeReady (buf_ptr, oldlen, cnt); /* EMIT SIGNAL */
|
||||||
|
|
||||||
|
|
@ -463,6 +468,8 @@ SMFSource::mark_for_remove ()
|
||||||
void
|
void
|
||||||
SMFSource::mark_streaming_write_completed ()
|
SMFSource::mark_streaming_write_completed ()
|
||||||
{
|
{
|
||||||
|
MidiSource::mark_streaming_write_completed();
|
||||||
|
|
||||||
if (!writable()) {
|
if (!writable()) {
|
||||||
return;
|
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
|
const double ev_time = (double)(time * frames_per_beat / (double)_ppqn); // in frames
|
||||||
|
|
||||||
if (ret > 0) { // didn't skip (meta) event
|
if (ret > 0) { // didn't skip (meta) event
|
||||||
//cerr << "ADDING EVENT TO MODEL: " << ev.time << endl;
|
|
||||||
_model->append(ev_time, ev.size, ev.buffer);
|
_model->append(ev_time, ev.size, ev.buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue