step sequencer now follows tempo map precisely; beatbox loses some functionality (for now)

This commit is contained in:
Paul Davis 2018-11-02 09:59:35 -04:00
parent be831c6870
commit d682e61b99
3 changed files with 94 additions and 328 deletions

View file

@ -95,6 +95,7 @@ class BeatBox : public ARDOUR::Processor {
int _meter_beat_type;
superclock_t superclock_cnt;
superclock_t last_start;
superclock_t last_end;
Timecode::BBT_Time last_time;
int _sample_rate;

View file

@ -25,7 +25,7 @@
#include <glibmm/threads.h>
#include "temporal/types.h"
#include "temporal/bbt_time.h"
#include "temporal/beats.h"
#include "ardour/mode.h"
#include "ardour/types.h"
@ -33,9 +33,13 @@
namespace ARDOUR {
class MidiBuffer;
class MidiStateTracker;
class StepSequencer;
class StepSequence;
class TempoMap;
typedef std::pair<Temporal::Beats,samplepos_t> BeatPosition;
typedef std::vector<BeatPosition> BeatPositions;
class Step {
public:
@ -44,10 +48,10 @@ class Step {
RelativePitch
};
Step (StepSequence&, Timecode::BBT_Time const & nominal_on);
Step (StepSequence&, Temporal::Beats const & beat);
~Step ();
void set_note (double note, double velocity = 0.5, double duration = 0.9, int n = 0);
void set_note (double note, double velocity = 0.5, int32_t duration = 1, int n = 0);
void set_chord (size_t note_cnt, double* notes);
void set_parameter (int number, double value, int n = 0);
@ -56,11 +60,10 @@ class Step {
double note (size_t n = 0) const { return _notes[n].number; }
double velocity (size_t n = 0) const { return _notes[n].velocity; }
Timecode::BBT_Time beat_duration (size_t n = 0) const;
double duration (size_t n = 0) const { return _notes[n].duration; }
int32_t duration (size_t n = 0) const { return _notes[n].duration; }
void set_offset (Timecode::BBT_Time const &, size_t n = 0);
Timecode::BBT_Time offset (size_t n = 0) const { return _notes[n].offset; }
void set_offset (Temporal::Beats const &, size_t n = 0);
Temporal::Beats offset (size_t n = 0) const { return _notes[n].offset; }
int parameter (size_t n = 0) const { return _parameters[n].parameter; }
int parameter_value (size_t n = 0) const { return _parameters[n].value; }
@ -71,16 +74,22 @@ class Step {
void set_repeat (size_t r);
size_t repeat() const { return _repeat; }
void set_nominal_on (Timecode::BBT_Time const &);
bool run (MidiBuffer& buf, Timecode::BBT_Time const & start, Timecode::BBT_Time const & end, samplecnt_t beat_samples);
void set_beat (Temporal::Beats const & beat);
Temporal::Beats beat () const { return _nominal_beat; }
bool run (MidiBuffer& buf, bool running, samplepos_t, samplepos_t, MidiStateTracker&);
bool skipped() const { return _skipped; }
void set_skipped (bool);
void set_timeline_offset (Temporal::Beats const &, Temporal::Beats const &);
private:
StepSequence& _sequence;
bool _enabled;
Timecode::BBT_Time _nominal_on;
Temporal::Beats timeline_offset;
Temporal::Beats _nominal_beat;
Temporal::Beats _scheduled_beat;
bool _skipped;
Mode _mode;
@ -95,14 +104,13 @@ class Step {
double interval; /* semitones */
};
double velocity;
double duration;
Timecode::BBT_Time offset;
int32_t duration;
Temporal::Beats offset;
bool on;
Timecode::BBT_Time off_at;
Temporal::Beats off_at;
Note () : number (-1), on (false) {}
Note (double n, double v, double d, Timecode::BBT_Time o)
: number (n), velocity (v), duration (d), offset (o), on (false) {}
Note () : number (-1), velocity (0.5), duration (1), on (false) {}
Note (double n, double v, double d, Temporal::Beats const & o) : number (n), velocity (v), duration (d), offset (o), on (false) {}
};
static const int _notes_per_step = 5;
@ -112,9 +120,11 @@ class Step {
ParameterValue _parameters[_parameters_per_step];
size_t _repeat;
void check_note (size_t n, MidiBuffer& buf, Timecode::BBT_Time const & start, Timecode::BBT_Time const & end, ARDOUR::samplecnt_t beat_samples);
void check_parameter (size_t n, MidiBuffer& buf, Timecode::BBT_Time const & start, Timecode::BBT_Time const & end, ARDOUR::samplecnt_t beat_samples);
void check_note (size_t n, MidiBuffer& buf, bool, samplepos_t, samplepos_t, MidiStateTracker&);
void check_parameter (size_t n, MidiBuffer& buf, bool, samplepos_t, samplepos_t);
StepSequencer& sequencer() const;
};
class StepSequence
@ -127,15 +137,21 @@ class StepSequence
rd_random = 3
};
StepSequence (size_t numsteps, StepSequencer &myseq);
StepSequence (StepSequencer &myseq, size_t nsteps, Temporal::Beats const & step_size, Temporal::Beats const & bar_size);
~StepSequence ();
void startup (Temporal::Beats const & start, Temporal::Beats const & offset);
Temporal::Beats bar_size() const { return _bar_size; }
double root() const { return _root; }
void set_root (double n);
int channel() const { return _channel; }
void set_channel (int);
Temporal::Beats wrap (Temporal::Beats const &) const;
MusicalMode mode() const { return _mode; }
void set_mode (MusicalMode m);
@ -146,13 +162,14 @@ class StepSequence
void set_end_step (size_t);
void set_start_and_end_step (size_t, size_t);
void set_beat_divisor (size_t);
size_t beat_divisor () const { return _beat_divisor; }
void set_step_size (Temporal::Beats const &);
Temporal::Beats step_size () const { return _step_size; }
void reset ();
void set_tempo (double quarters_per_minute, int sr);
bool run (MidiBuffer& buf, Timecode::BBT_Time const & start, Timecode::BBT_Time const & end);
bool run (MidiBuffer& buf, bool running, samplepos_t, samplepos_t, MidiStateTracker&);
StepSequencer& sequencer() const { return _sequencer; }
private:
StepSequencer& _sequencer;
@ -160,63 +177,52 @@ class StepSequence
typedef std::vector<Step*> Steps;
Steps _steps;
size_t _start;
size_t _end;
size_t _start; /* step count */
size_t _end; /* step count */
int _channel; /* MIDI channel */
Temporal::Beats _step_size;
Temporal::Beats _bar_size;
Temporal::Beats end_beat;
double _root;
MusicalMode _mode;
size_t _beat_divisor;
int _channel;
samplecnt_t _beat_samples;
};
class StepSequencer {
public:
enum State {
Running,
Halted,
Paused,
};
StepSequencer (size_t nseqs, size_t nsteps);
StepSequencer (TempoMap&, size_t nseqs, size_t nsteps, Temporal::Beats const & step_size, Temporal::Beats const & bar_size);
~StepSequencer ();
Temporal::Beats duration() const;
void startup (Temporal::Beats const & start, Temporal::Beats const & offset);
Temporal::Beats step_size () const { return _step_size; }
void set_step_size (Temporal::Beats const &);
void set_start_step (size_t);
void set_end_step (size_t);
void set_start_and_end_step (size_t, size_t);
bool running() const { return _state == Running; }
bool halted() const { return _state == Halted; }
bool paused() const { return _state == Paused; }
void start ();
void halt (); /* stop everything, reset */
void play ();
void pause ();
void toggle_pause ();
void sync (); /* return all rows to start step */
void reset (); /* return entire state to default */
double tempo() const; /* quarters per minute, not beats per minute */
void set_tempo (double, int sr);
bool run (MidiBuffer& buf, bool running, samplepos_t, samplepos_t, MidiStateTracker&);
bool run (MidiBuffer& buf, Timecode::BBT_Time const & start, Timecode::BBT_Time const & end);
TempoMap& tempo_map() const { return _tempo_map; }
private:
Glib::Threads::Mutex _sequence_lock;
Glib::Threads::Mutex _state_lock;
typedef std::vector<StepSequence*> StepSequences;
StepSequences _sequences;
State _state;
Timecode::BBT_Time _target_start;
Timecode::BBT_Time _target_end;
double _target_tempo;
Timecode::BBT_Time _start;
Timecode::BBT_Time _end;
double _tempo;
TempoMap& _tempo_map;
Temporal::Beats _step_size;
int32_t _start;
int32_t _end;
};
} /* namespace */

View file

@ -35,6 +35,7 @@
#include "ardour/session.h"
#include "ardour/smf_source.h"
#include "ardour/step_sequencer.h"
#include "ardour/tempo.h"
using std::cerr;
using std::endl;
@ -49,10 +50,9 @@ BeatBox::BeatBox (Session& s)
, _start_requested (false)
, _running (false)
, _measures (2)
, _tempo (120)
, _tempo_request (0)
, _meter_beats (4)
, _meter_beat_type (4)
, _tempo (-1.0)
, _meter_beats (-1)
, _meter_beat_type (-1)
, superclock_cnt (0)
, last_start (0)
, whole_note_superclocks (0)
@ -65,7 +65,7 @@ BeatBox::BeatBox (Session& s)
, remove_queue (64)
{
_display_to_user = true;
_sequencer = new StepSequencer (1, 32);
_sequencer = new StepSequencer (s.tempo_map(), 1, 8, Temporal::Beats (1, 0), Temporal::Beats (4, 0));
}
BeatBox::~BeatBox ()
@ -73,24 +73,9 @@ BeatBox::~BeatBox ()
delete _sequencer;
}
void
BeatBox::compute_tempo_clocks ()
{
whole_note_superclocks = (superclock_ticks_per_second * 60) / (_tempo / _meter_beat_type);
beat_superclocks = whole_note_superclocks / _meter_beat_type;
tick_superclocks = beat_superclocks / Timecode::BBT_Time::ticks_per_beat;
measure_superclocks = beat_superclocks * _meter_beats;
_sequencer->set_tempo (_tempo, AudioEngine::instance()->sample_rate());
}
void
BeatBox::start ()
{
/* compute tempo, beat steps etc. */
compute_tempo_clocks ();
/* we can start */
_start_requested = true;
@ -102,12 +87,6 @@ BeatBox::stop ()
_start_requested = false;
}
void
BeatBox::set_tempo (float bpm)
{
_tempo_request = bpm;
}
void
BeatBox::silence (samplecnt_t, samplepos_t)
{
@ -115,276 +94,56 @@ BeatBox::silence (samplecnt_t, samplepos_t)
}
void
BeatBox::run (BufferSet& bufs, samplepos_t /*start_frame*/, samplepos_t /*end_frame*/, double speed, pframes_t nsamples, bool /*result_required*/)
BeatBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_sample, double speed, pframes_t nsamples, bool /*result_required*/)
{
if (bufs.count().n_midi() == 0) {
return;
}
if (!_running) {
if (_start_requested) {
_running = true;
last_start = superclock_cnt;
}
bool resolve = false;
} else {
if (!_start_requested) {
if (speed == 0) {
if (_running) {
resolve = true;
_running = false;
outbound_tracker.resolve_notes (bufs.get_midi (0), 0);
}
}
superclock_t superclocks = samples_to_superclock (nsamples, _session.sample_rate());
if (speed != 0) {
if (_tempo_request) {
double ratio = _tempo / _tempo_request;
_tempo = _tempo_request;
_tempo_request = 0;
if (!_running || (last_end != start_sample)) {
compute_tempo_clocks ();
/* recompute all the event times based on the ratio between the
* new and old tempo.
*/
for (Events::iterator ee = _current_events.begin(); ee != _current_events.end(); ++ee) {
(*ee)->time = llrintf ((*ee)->time * ratio);
}
}
if (!_running) {
superclock_cnt += superclocks;
return;
}
superclock_t process_start = superclock_cnt - last_start;
superclock_t process_end = process_start + superclocks;
const superclock_t loop_length = _measures * measure_superclocks;
const superclock_t orig_superclocks = superclocks;
process_start %= loop_length;
process_end %= loop_length;
Timecode::BBT_Time start = last_time;
last_time.bars = (process_end / measure_superclocks);
last_time.beats = ((process_end - (last_time.bars * measure_superclocks)) / beat_superclocks);
last_time.ticks = (process_end - (last_time.bars * measure_superclocks) - (last_time.beats * beat_superclocks)) / tick_superclocks;
/* change to 1-base */
last_time.bars++;
last_time.beats++;
std::cerr << "run " << process_start << " .. " << process_end << " => " << start << " .. " << last_time << endl;
bool two_pass_required;
superclock_t offset = 0;
if (process_end < process_start) {
two_pass_required = true;
process_end = loop_length;
superclocks = process_end - process_start;
} else {
two_pass_required = false;
}
Evoral::Event<MidiBuffer::TimeType> in_event;
/* do this on the first pass only */
MidiBuffer& buf = bufs.get_midi (0);
if (clear_pending) {
for (Events::iterator ee = _current_events.begin(); ee != _current_events.end(); ++ee) {
delete *ee;
}
_current_events.clear ();
_incomplete_notes.clear ();
clear_pending = false;
}
second_pass:
/* input */
for (MidiBuffer::iterator e = buf.begin(); e != buf.end(); ++e) {
const Evoral::Event<MidiBuffer::TimeType>& in_event = *e;
superclock_t event_time = superclock_cnt + samples_to_superclock (in_event.time(), _session.sample_rate());
superclock_t elapsed_time = event_time - last_start;
superclock_t in_loop_time = elapsed_time % loop_length;
superclock_t quantized_time;
if (_quantize_divisor != 0) {
const superclock_t time_per_grid_unit = whole_note_superclocks / _quantize_divisor;
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
* the corresponding note on.
*/
/* 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 (!found) {
cerr << "Note off for " << (int) in_event.buffer()[1] << " seen without corresponding note on among " << _incomplete_notes.size() << endl;
continue;
}
} else {
quantized_time = (in_loop_time / time_per_grid_unit) * time_per_grid_unit;
if (last_end != start_sample) {
resolve = true;
}
} else {
quantized_time = elapsed_time;
}
/* compute the beat position of this first "while-moving
* run() call as an offset into the sequencer's current loop
* length.
*/
/* if computed quantized time is past the end of the loop, wrap
it back around.
*/
TempoMap& tmap (_session.tempo_map());
quantized_time %= loop_length;
const Temporal::Beats start_beat (tmap.beat_at_sample (start_sample));
const int32_t tick_duration = _sequencer->duration().to_ticks();
if (in_event.size() > 24) {
cerr << "Ignored large MIDI event\n";
continue;
}
Event* new_event = new Event; // pool alloc, thread safe
if (!new_event) {
cerr << "No more events, grow pool\n";
continue;
}
Temporal::Beats closest_previous_loop_start = Temporal::Beats::ticks ((start_beat.to_ticks() / tick_duration) * tick_duration);
Temporal::Beats offset = Temporal::Beats::ticks ((start_beat.to_ticks() % tick_duration));
_sequencer->startup (closest_previous_loop_start, offset);
last_start = start_sample;
_running = true;
new_event->time = quantized_time;
new_event->whole_note_superclocks = whole_note_superclocks;
new_event->size = in_event.size();
memcpy (new_event->buf, in_event.buffer(), new_event->size);
inbound_tracker.track (new_event->buf);
_current_events.insert (new_event);
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);
}
}
if (resolve) {
outbound_tracker.resolve_notes (bufs.get_midi(0), 0);
}
/* Output */
#if 0
for (Events::iterator ee = _current_events.begin(); ee != _current_events.end(); ) {
Event* e = (*ee);
if ((e->once <= 1) && e->size && (e->time >= process_start && e->time < process_end)) {
const samplepos_t sample_offset_in_buffer = superclock_to_samples (offset + e->time - process_start, _session.sample_rate());
/* 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);
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;
/* old versions of libstc++ don't return an iterator
from set<T>::erase (iterator)
*/
Events::iterator n = ee;
++n;
_current_events.erase (ee);
ee = n;
}
}
#endif
_sequencer->run (buf, start, last_time);
superclock_cnt += superclocks;
if (two_pass_required) {
offset = superclocks;
superclocks = orig_superclocks - superclocks;
process_start = 0;
process_end = superclocks;
two_pass_required = false;
goto second_pass;
}
return;
_sequencer->run (bufs.get_midi (0), _running, start_sample, end_sample, outbound_tracker);
last_end = end_sample;
}
void