mirror of
https://github.com/Ardour/ardour.git
synced 2026-01-26 23:18:20 +01:00
various changes, improvements and fixes for Beatbox API and implementation
This commit is contained in:
parent
592175ab80
commit
6bbfc5a460
2 changed files with 120 additions and 41 deletions
|
|
@ -59,8 +59,8 @@ class BeatBox : public ARDOUR::Processor {
|
|||
|
||||
Timecode::BBT_Time get_last_time () const;
|
||||
|
||||
void inject_note (int number, int velocity);
|
||||
void inject_note (int number, int velocity, Timecode::BBT_Time at);
|
||||
void add_note (int number, int velocity, Timecode::BBT_Time at);
|
||||
void remove_note (int number, Timecode::BBT_Time at);
|
||||
|
||||
void set_measure_count (int measures);
|
||||
void set_meter (int beats, int beat_type);
|
||||
|
|
@ -97,14 +97,15 @@ class BeatBox : public ARDOUR::Processor {
|
|||
ARDOUR::MidiStateTracker outbound_tracker;
|
||||
|
||||
struct Event {
|
||||
superclock_t time;
|
||||
superclock_t whole_note_superclocks;
|
||||
size_t size;
|
||||
unsigned char buf[24];
|
||||
superclock_t time;
|
||||
superclock_t whole_note_superclocks;
|
||||
size_t size;
|
||||
unsigned char buf[24];
|
||||
int once;
|
||||
|
||||
Event () : time (0), size (0) {}
|
||||
Event (superclock_t t, size_t sz, unsigned char* b) : time (t), size (sz) { memcpy (buf, b, std::min (sizeof (buf), sz)); }
|
||||
Event (Event const & other) : time (other.time), size (other.size) { memcpy (buf, other.buf, other.size); }
|
||||
Event () : time (0), size (0), once (0) {}
|
||||
Event (superclock_t t, size_t sz, unsigned char* b) : time (t), size (sz), once (0) { memcpy (buf, b, std::min (sizeof (buf), sz)); }
|
||||
Event (Event const & other) : time (other.time), size (other.size), once (0) { memcpy (buf, other.buf, other.size); }
|
||||
|
||||
static MultiAllocSingleReleasePool pool;
|
||||
|
||||
|
|
@ -129,8 +130,8 @@ class BeatBox : public ARDOUR::Processor {
|
|||
|
||||
void compute_tempo_clocks ();
|
||||
|
||||
RingBuffer<Event*> injection_queue;
|
||||
void queue_event (Event*);
|
||||
RingBuffer<Event*> add_queue;
|
||||
RingBuffer<Event*> remove_queue;
|
||||
};
|
||||
|
||||
} /* namespace */
|
||||
|
|
|
|||
|
|
@ -56,7 +56,8 @@ BeatBox::BeatBox (Session& s)
|
|||
, measure_superclocks (0)
|
||||
, _quantize_divisor (4)
|
||||
, clear_pending (false)
|
||||
, injection_queue (256)
|
||||
, add_queue (64)
|
||||
, remove_queue (64)
|
||||
{
|
||||
_display_to_user = true;
|
||||
}
|
||||
|
|
@ -119,6 +120,7 @@ BeatBox::run (BufferSet& bufs, framepos_t /*start_frame*/, framepos_t /*end_fram
|
|||
} else {
|
||||
if (!_start_requested) {
|
||||
_running = false;
|
||||
outbound_tracker.resolve_notes (bufs.get_midi (0), 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -145,12 +147,6 @@ BeatBox::run (BufferSet& bufs, framepos_t /*start_frame*/, framepos_t /*end_fram
|
|||
return;
|
||||
}
|
||||
|
||||
Event* injected_event;
|
||||
|
||||
while (injection_queue.read (&injected_event, 1)) {
|
||||
_current_events.insert (injected_event);
|
||||
}
|
||||
|
||||
superclock_t process_start = superclock_cnt - last_start;
|
||||
superclock_t process_end = process_start + superclocks;
|
||||
const superclock_t loop_length = _measures * measure_superclocks;
|
||||
|
|
@ -208,7 +204,7 @@ BeatBox::run (BufferSet& bufs, framepos_t /*start_frame*/, framepos_t /*end_fram
|
|||
if (_quantize_divisor != 0) {
|
||||
const superclock_t time_per_grid_unit = whole_note_superclocks / _quantize_divisor;
|
||||
|
||||
if ((in_event.buffer()[0] & 0xf) == MIDI_CMD_NOTE_OFF) {
|
||||
if ((in_event.buffer()[0] & 0xf0) == MIDI_CMD_NOTE_OFF) {
|
||||
|
||||
/* note off is special - it must be quantized
|
||||
* to at least 1 quantization "spacing" after
|
||||
|
|
@ -218,18 +214,20 @@ BeatBox::run (BufferSet& bufs, framepos_t /*start_frame*/, framepos_t /*end_fram
|
|||
/* look for the note on */
|
||||
|
||||
IncompleteNotes::iterator ee;
|
||||
bool found = false;
|
||||
|
||||
for (ee = _incomplete_notes.begin(); ee != _incomplete_notes.end(); ++ee) {
|
||||
/* check for same note and channel */
|
||||
if (((*ee)->buf[1] == in_event.buffer()[1]) && ((*ee)->buf[0] & 0xf) == (in_event.buffer()[0] & 0xf)) {
|
||||
quantized_time = (*ee)->time + time_per_grid_unit;
|
||||
_incomplete_notes.erase (ee);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ee == _incomplete_notes.end()) {
|
||||
cerr << "Note off for " << (int) (*ee)->buf[1] << " seen without corresponding note on among " << _incomplete_notes.size() << endl;
|
||||
if (!found) {
|
||||
cerr << "Note off for " << (int) in_event.buffer()[1] << " seen without corresponding note on among " << _incomplete_notes.size() << endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -241,6 +239,12 @@ BeatBox::run (BufferSet& bufs, framepos_t /*start_frame*/, framepos_t /*end_fram
|
|||
quantized_time = elapsed_time;
|
||||
}
|
||||
|
||||
/* if computed quantized time is past the end of the loop, wrap
|
||||
it back around.
|
||||
*/
|
||||
|
||||
quantized_time %= loop_length;
|
||||
|
||||
if (in_event.size() > 24) {
|
||||
cerr << "Ignored large MIDI event\n";
|
||||
continue;
|
||||
|
|
@ -262,25 +266,86 @@ BeatBox::run (BufferSet& bufs, framepos_t /*start_frame*/, framepos_t /*end_fram
|
|||
|
||||
_current_events.insert (new_event);
|
||||
|
||||
if ((new_event->buf[0] & 0xf) == MIDI_CMD_NOTE_ON) {
|
||||
if ((new_event->buf[0] & 0xf0) == MIDI_CMD_NOTE_ON) {
|
||||
_incomplete_notes.push_back (new_event);
|
||||
}
|
||||
}
|
||||
|
||||
/* Notes added from other threads */
|
||||
|
||||
Event* added_event;
|
||||
|
||||
if (offset == 0) {
|
||||
/* during first pass only */
|
||||
|
||||
while (add_queue.read (&added_event, 1)) {
|
||||
_current_events.insert (added_event);
|
||||
|
||||
if (((added_event->buf[0] & 0xf0) == MIDI_CMD_NOTE_ON) &&
|
||||
(added_event->time >= process_end || added_event->time < process_start)) {
|
||||
|
||||
/* won't hear it this time, so do immediate play. Off will follow in time */
|
||||
|
||||
/* e->buf is guaranteed to live through this process cycle, so do not alloc for a copy*/
|
||||
const Evoral::Event<MidiBuffer::TimeType> eev (Evoral::MIDI_EVENT, 0, added_event->size, added_event->buf, false);
|
||||
|
||||
if (buf.insert_event (eev)) {
|
||||
outbound_tracker.track (added_event->buf);
|
||||
}
|
||||
|
||||
/* insert a 1-time only note off to turn off this immediate note on */
|
||||
|
||||
Event* matching_note_off = new Event;
|
||||
matching_note_off->once = 1;
|
||||
matching_note_off->size = 3;
|
||||
|
||||
if (_quantize_divisor) {
|
||||
matching_note_off->time = process_start + (beat_superclocks / _quantize_divisor);
|
||||
} else {
|
||||
matching_note_off->time = process_start + beat_superclocks;
|
||||
}
|
||||
matching_note_off->time %= loop_length;
|
||||
|
||||
matching_note_off->buf[0] = MIDI_CMD_NOTE_OFF | (added_event->buf[0] & 0xf);
|
||||
matching_note_off->buf[1] = added_event->buf[1];
|
||||
matching_note_off->buf[2] = 0;
|
||||
|
||||
_current_events.insert (matching_note_off);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Output */
|
||||
|
||||
for (Events::iterator ee = _current_events.begin(); ee != _current_events.end(); ++ee) {
|
||||
for (Events::iterator ee = _current_events.begin(); ee != _current_events.end(); ) {
|
||||
Event* e = (*ee);
|
||||
if (e->size && (e->time >= process_start && e->time < process_end)) {
|
||||
if ((e->once <= 1) && e->size && (e->time >= process_start && e->time < process_end)) {
|
||||
const framepos_t sample_offset_in_buffer = superclock_to_samples (offset + e->time - process_start, _session.frame_rate());
|
||||
/* e->buf is guaranteed to live through this process * * cycle, so do not alloc for a copy*/
|
||||
/* e->buf is guaranteed to live through this process cycle, so do not alloc for a copy*/
|
||||
const Evoral::Event<MidiBuffer::TimeType> eev (Evoral::MIDI_EVENT, sample_offset_in_buffer, e->size, e->buf, false);
|
||||
buf.insert_event (eev);
|
||||
if (buf.insert_event (eev)) {
|
||||
outbound_tracker.track (e->buf);
|
||||
}
|
||||
}
|
||||
|
||||
if (e->time >= process_end) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (e->once) {
|
||||
case 0:
|
||||
/* normal event, do nothing */
|
||||
++ee;
|
||||
break;
|
||||
case 1:
|
||||
/* delete it next process cycle */
|
||||
e->once++;
|
||||
++ee;
|
||||
break;
|
||||
default:
|
||||
delete e;
|
||||
ee = _current_events.erase (ee);
|
||||
}
|
||||
}
|
||||
|
||||
superclock_cnt += superclocks;
|
||||
|
|
@ -350,17 +415,17 @@ BeatBox::get_last_time() const
|
|||
}
|
||||
|
||||
void
|
||||
BeatBox::inject_note (int note, int velocity)
|
||||
BeatBox::remove_note (int note, Timecode::BBT_Time at)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
BeatBox::inject_note (int note, int velocity, Timecode::BBT_Time at)
|
||||
BeatBox::add_note (int note, int velocity, Timecode::BBT_Time at)
|
||||
{
|
||||
Event* e = new Event; // pool allocated, thread safe
|
||||
Event* on = new Event; // pool allocated, thread safe
|
||||
|
||||
if (!e) {
|
||||
if (!on) {
|
||||
cerr << "No more events for injection, grow pool\n";
|
||||
return;
|
||||
}
|
||||
|
|
@ -372,17 +437,30 @@ BeatBox::inject_note (int note, int velocity, Timecode::BBT_Time at)
|
|||
at.bars %= _measures;
|
||||
at.beats %= _meter_beats;
|
||||
|
||||
e->time = (measure_superclocks * at.bars) + (beat_superclocks * at.beats);
|
||||
e->size = 3;
|
||||
e->buf[0] = MIDI_CMD_NOTE_ON | (0 & 0xf);
|
||||
e->buf[1] = note;
|
||||
e->buf[2] = velocity;
|
||||
on->time = (measure_superclocks * at.bars) + (beat_superclocks * at.beats);
|
||||
on->size = 3;
|
||||
on->buf[0] = MIDI_CMD_NOTE_ON | (0 & 0xf);
|
||||
on->buf[1] = note;
|
||||
on->buf[2] = velocity;
|
||||
|
||||
queue_event (e);
|
||||
}
|
||||
Event* off = new Event; // pool allocated, thread safe
|
||||
|
||||
void
|
||||
BeatBox::queue_event (Event* e)
|
||||
{
|
||||
injection_queue.write (&e, 1);
|
||||
if (!off) {
|
||||
cerr << "No more events for injection, grow pool\n";
|
||||
return;
|
||||
}
|
||||
|
||||
if (_quantize_divisor != 0) {
|
||||
off->time = on->time + (beat_superclocks / _quantize_divisor);
|
||||
} else {
|
||||
/* 1/4 second note .. totally arbitrary */
|
||||
off->time = on->time + (_session.frame_rate() / 4);
|
||||
}
|
||||
off->size = 3;
|
||||
off->buf[0] = MIDI_CMD_NOTE_OFF | (0 & 0xf);
|
||||
off->buf[1] = note;
|
||||
off->buf[2] = 0;
|
||||
|
||||
add_queue.write (&on, 1);
|
||||
add_queue.write (&off, 1);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue