From c0edd61d75c3961157db4aaad18ecd014642322f Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Fri, 9 Nov 2018 21:27:42 -0500 Subject: [PATCH] more redesign of beatbox fundamentals, adding back RT-safe modifications to sequencer --- libs/ardour/ardour/beatbox.h | 73 --------- libs/ardour/ardour/step_sequencer.h | 90 +++++++---- libs/ardour/beatbox.cc | 167 +------------------- libs/ardour/step_sequencer.cc | 237 +++++++++++++++++++++++----- 4 files changed, 257 insertions(+), 310 deletions(-) diff --git a/libs/ardour/ardour/beatbox.h b/libs/ardour/ardour/beatbox.h index e514363a08..3860e0d749 100644 --- a/libs/ardour/ardour/beatbox.h +++ b/libs/ardour/ardour/beatbox.h @@ -58,25 +58,6 @@ class BeatBox : public ARDOUR::Processor { void silence (samplecnt_t nframes, samplepos_t start_frame); bool can_support_io_configuration (const ChanCount& in, ChanCount& out); - bool running() const { return _running || _start_requested; } - void start (); - void stop (); - void clear (); - - void add_note (int number, int velocity, Timecode::BBT_Time at); - void remove_note (int number, Timecode::BBT_Time at); - void edit_note_number (int old_number, int new_number); - - void set_measure_count (int measures); - void set_meter (int beats, int beat_type); - void set_tempo (float bpm); - - void set_quantize (int divisor); - - float tempo() const { return _tempo; } - int meter_beats() const { return _meter_beats; } - int meter_beat_type() const { return _meter_beat_type; } - XMLNode& state(); XMLNode& get_state(void); @@ -84,62 +65,8 @@ class BeatBox : public ARDOUR::Processor { private: StepSequencer* _sequencer; - bool _start_requested; - bool _running; - int _measures; - float _tempo; - float _tempo_request; - int _meter_beats; - int _meter_beat_type; - samplepos_t last_start; - samplepos_t last_end; - int _sample_rate; - superclock_t whole_note_superclocks; - superclock_t tick_superclocks; - superclock_t beat_superclocks; - superclock_t measure_superclocks; - int _quantize_divisor; - bool clear_pending; ARDOUR::MidiStateTracker inbound_tracker; - ARDOUR::MidiStateTracker outbound_tracker; - - struct Event { - superclock_t time; - superclock_t whole_note_superclocks; - size_t size; - unsigned char buf[24]; - int once; - - 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; - - void *operator new (size_t) { - return pool.alloc (); - } - - void operator delete (void* ptr, size_t /* size */) { - pool.release (ptr); - } - }; - - struct EventComparator { - bool operator () (Event const * a, Event const * b) const; - }; - - typedef std::vector IncompleteNotes; - IncompleteNotes _incomplete_notes; - - typedef std::set Events; - Events _current_events; - - void compute_tempo_clocks (); - - PBD::RingBuffer add_queue; - PBD::RingBuffer remove_queue; bool fill_midi_source (boost::shared_ptr); diff --git a/libs/ardour/ardour/step_sequencer.h b/libs/ardour/ardour/step_sequencer.h index 03bcc1d8fc..15885a977c 100644 --- a/libs/ardour/ardour/step_sequencer.h +++ b/libs/ardour/ardour/step_sequencer.h @@ -22,16 +22,20 @@ #include #include +#include #include #include +#include "pbd/pool.h" +#include "pbd/ringbuffer.h" #include "pbd/stateful.h" #include "temporal/types.h" #include "temporal/beats.h" #include "ardour/mode.h" +#include "ardour/midi_state_tracker.h" #include "ardour/types.h" namespace ARDOUR { @@ -54,7 +58,7 @@ class Step : public PBD::Stateful { typedef boost::rational DurationRatio; - Step (StepSequence&, Temporal::Beats const & beat, int notenum); + Step (StepSequence&, size_t n, Temporal::Beats const & beat, int notenum); ~Step (); void set_note (double note, double velocity = 0.5, int n = 0); @@ -108,6 +112,7 @@ class Step : public PBD::Stateful { friend class StepSequence; /* HACK */ StepSequence& _sequence; + size_t index; bool _enabled; Temporal::Beats _nominal_beat; Temporal::Beats _scheduled_beat; @@ -168,8 +173,6 @@ class StepSequence : public PBD::Stateful 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); @@ -184,17 +187,9 @@ class StepSequence : public PBD::Stateful void shift_left (size_t n = 1); void shift_right (size_t n = 1); - size_t start_step() const { return _start; } - size_t end_step() const { return _end; } - - void set_start_step (size_t); - void set_end_step (size_t); - void set_start_and_end_step (size_t, size_t); - - void set_step_size (Temporal::Beats const &); - Temporal::Beats step_size () const { return _step_size; } - void reset (); + void reschedule (Temporal::Beats const &, Temporal::Beats const &); + void schedule (Temporal::Beats const &); bool run (MidiBuffer& buf, bool running, samplepos_t, samplepos_t, MidiStateTracker&); @@ -209,14 +204,7 @@ class StepSequence : public PBD::Stateful typedef std::vector Steps; Steps _steps; - 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; }; @@ -227,7 +215,8 @@ class StepSequencer : public PBD::Stateful StepSequencer (TempoMap&, size_t nseqs, size_t nsteps, Temporal::Beats const & step_size, Temporal::Beats const & bar_size, int notenum); ~StepSequencer (); - size_t nsteps() const { return _sequences.front()->nsteps(); } + size_t step_capacity() const { return _step_capacity; } + size_t nsteps() const { return _end_step - _start_step; } size_t nsequences() const { return _sequences.size(); } int last_step() const; @@ -236,19 +225,19 @@ class StepSequencer : public PBD::Stateful 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); + + size_t start_step() const { return _start_step; } + size_t end_step() const { return _end_step; } void sync (); /* return all rows to start step */ void reset (); /* return entire state to default */ - bool run (MidiBuffer& buf, bool running, samplepos_t, samplepos_t, MidiStateTracker&); + bool run (MidiBuffer& buf, samplepos_t, samplepos_t, double, pframes_t, bool); TempoMap& tempo_map() const { return _tempo_map; } @@ -257,17 +246,56 @@ class StepSequencer : public PBD::Stateful private: mutable Glib::Threads::Mutex _sequence_lock; + TempoMap& _tempo_map; typedef std::vector StepSequences; - StepSequences _sequences; - TempoMap& _tempo_map; + Temporal::Beats _last_startup; /* last time we started running */ + size_t _last_step; /* last step that we executed */ Temporal::Beats _step_size; - int32_t _start; - int32_t _end; - Temporal::Beats _last_start; - int _last_step; + size_t _start_step; + size_t _end_step; + samplepos_t last_start; + samplepos_t last_end; /* end sample time of last run() call */ + bool _running; + size_t _step_capacity; + + ARDOUR::MidiStateTracker outbound_tracker; + + struct Request { + + /* bitwise types, so we can combine multiple in one + */ + + enum Type { + SetStartStep = 0x1, + SetEndStep = 0x2, + SetNSequences = 0x4, + SetStepSize = 0x8, + }; + + Type type; + + Temporal::Beats step_size; + size_t nsequences; + size_t start_step; + size_t end_step; + + static MultiAllocSingleReleasePool pool; + + void *operator new (size_t) { + return pool.alloc (); + } + + void operator delete (void* ptr, size_t /* size */) { + pool.release (ptr); + } + }; + + PBD::RingBuffer requests; + bool check_requests (); + Temporal::Beats reschedule (samplepos_t); }; } /* namespace */ diff --git a/libs/ardour/beatbox.cc b/libs/ardour/beatbox.cc index b44829a8f5..8cde28481c 100644 --- a/libs/ardour/beatbox.cc +++ b/libs/ardour/beatbox.cc @@ -42,26 +42,9 @@ using std::endl; using namespace ARDOUR; -MultiAllocSingleReleasePool BeatBox::Event::pool (X_("beatbox events"), sizeof (Event), 2048); - BeatBox::BeatBox (Session& s) : Processor (s, _("BeatBox")) , _sequencer (0) - , _start_requested (false) - , _running (false) - , _measures (2) - , _tempo (-1.0) - , _meter_beats (-1) - , _meter_beat_type (-1) - , last_start (0) - , whole_note_superclocks (0) - , tick_superclocks (0) - , beat_superclocks (0) - , measure_superclocks (0) - , _quantize_divisor (4) - , clear_pending (false) - , add_queue (64) - , remove_queue (64) { _display_to_user = true; _sequencer = new StepSequencer (s.tempo_map(), 12, 32, Temporal::Beats (0, Temporal::Beats::PPQN/8), Temporal::Beats (4, 0), 40); @@ -72,20 +55,6 @@ BeatBox::~BeatBox () delete _sequencer; } -void -BeatBox::start () -{ - /* we can start */ - - _start_requested = true; -} - -void -BeatBox::stop () -{ - _start_requested = false; -} - void BeatBox::silence (samplecnt_t, samplepos_t) { @@ -93,80 +62,13 @@ BeatBox::silence (samplecnt_t, samplepos_t) } void -BeatBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_sample, 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; } - bool resolve = false; - - if (speed == 0) { - if (_running) { - resolve = true; - _running = false; - } - } - - if (speed != 0) { - - if (!_running || (last_end != start_sample)) { - - if (last_end != start_sample) { - resolve = true; - } - - /* compute the beat position of this first "while-moving - * run() call as an offset into the sequencer's current loop - * length. - */ - - TempoMap& tmap (_session.tempo_map()); - - const Temporal::Beats start_beat (tmap.beat_at_sample (start_sample)); - const int32_t tick_duration = _sequencer->duration().to_ticks(); - - - - 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; - - } - } - - if (resolve) { - outbound_tracker.resolve_notes (bufs.get_midi(0), 0); - } - - _sequencer->run (bufs.get_midi (0), _running, start_sample, end_sample, outbound_tracker); - last_end = end_sample; -} - -void -BeatBox::set_quantize (int divisor) -{ - _quantize_divisor = divisor; -} - -void -BeatBox::clear () -{ - clear_pending = true; -} - -bool -BeatBox::EventComparator::operator() (Event const * a, Event const *b) const -{ - if (a->time == b->time) { - if (a->buf[0] == b->buf[0]) { - return a < b; - } - return !ARDOUR::MidiBuffer::second_simultaneous_midi_byte_is_first (a->buf[0], b->buf[0]); - } - return a->time < b->time; + _sequencer->run (bufs.get_midi (0), start_sample, end_sample, speed, nsamples, result_required); } bool @@ -190,68 +92,6 @@ BeatBox::state() return node; } -void -BeatBox::edit_note_number (int old_number, int new_number) -{ - for (Events::iterator e = _current_events.begin(); e != _current_events.end(); ++e) { - if (((*e)->buf[0] & 0xf0) == MIDI_CMD_NOTE_OFF || ((*e)->buf[0] & 0xf0) == MIDI_CMD_NOTE_ON) { - if ((*e)->buf[1] == old_number) { - (*e)->buf[1] = new_number; - } - } - } -} - -void -BeatBox::remove_note (int note, Timecode::BBT_Time at) -{ -} - -void -BeatBox::add_note (int note, int velocity, Timecode::BBT_Time at) -{ - Event* on = new Event; // pool allocated, thread safe - - if (!on) { - cerr << "No more events for injection, grow pool\n"; - return; - } - /* convert to zero-base */ - at.bars--; - at.beats--; - - /* clamp to current loop configuration */ - at.bars %= _measures; - at.beats %= _meter_beats; - - 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; - - Event* off = new Event; // pool allocated, thread safe - - 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.sample_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); -} - bool BeatBox::fill_source (boost::shared_ptr src) { @@ -267,6 +107,7 @@ BeatBox::fill_source (boost::shared_ptr src) bool BeatBox::fill_midi_source (boost::shared_ptr src) { +#if 0 Temporal::Beats smf_beats; if (_current_events.empty()) { @@ -295,6 +136,6 @@ BeatBox::fill_midi_source (boost::shared_ptr src) } catch (...) { cerr << "Exception during beatbox write to SMF... " << endl; } - +#endif return false; } diff --git a/libs/ardour/step_sequencer.cc b/libs/ardour/step_sequencer.cc index 87a75b8550..4f4ccc672f 100644 --- a/libs/ardour/step_sequencer.cc +++ b/libs/ardour/step_sequencer.cc @@ -30,8 +30,9 @@ using namespace PBD; using namespace ARDOUR; using namespace std; -Step::Step (StepSequence &s, Temporal::Beats const & b, int base_note) +Step::Step (StepSequence &s, size_t n, Temporal::Beats const & b, int base_note) : _sequence (s) + , index (n) , _enabled (true) , _nominal_beat (b) , _skipped (false) @@ -336,9 +337,9 @@ Step::check_note (size_t n, MidiBuffer& buf, bool running, samplepos_t start_sam * just to get non-simultaneous on/off events at * step boundaries. */ - note.off_at += Temporal::Beats (0, _sequence.step_size().to_ticks() - 1); + note.off_at += Temporal::Beats (0, sequencer().step_size().to_ticks() - 1); } else { - note.off_at += Temporal::Beats (0, (_sequence.step_size().to_ticks() * _duration.numerator()) / _duration.denominator()); + note.off_at += Temporal::Beats (0, (sequencer().step_size().to_ticks() * _duration.numerator()) / _duration.denominator()); } } } @@ -401,22 +402,19 @@ Step::set_state (XMLNode const &, int) StepSequence::StepSequence (StepSequencer& s, size_t nsteps, Temporal::Beats const & step_size, Temporal::Beats const & bar_size, int r) : _sequencer (s) - , _start (0) - , _end (nsteps - 1) , _channel (0) - , _step_size (step_size) - , _bar_size (bar_size) , _root (r) , _mode (MusicalMode::IonianMajor) { Temporal::Beats beats; for (size_t s = 0; s < nsteps; ++s) { - _steps.push_back (new Step (*this, beats, _root)); - beats += step_size; + /* beats is wrong but we will correct in ::schedule */ + _steps.push_back (new Step (*this, s, beats, _root)); } - end_beat = beats; + /* schedule them all from zero for now */ + schedule (beats); } StepSequence::~StepSequence () @@ -427,16 +425,29 @@ StepSequence::~StepSequence () } void -StepSequence::startup (Temporal::Beats const & start, Temporal::Beats const & offset) +StepSequence::schedule (Temporal::Beats const & start) { - for (Steps::iterator i = _steps.begin(); i != _steps.end(); ++i) { - (*i)->reschedule (start, offset); + Temporal::Beats beats (start); + const size_t s = _sequencer.start_step(); + const size_t e = _sequencer.end_step(); + + for (size_t n = s; n < e; ++n) { + _steps[n]->set_beat (beats); + cerr << "beat " << n << " @ " << beats << ' '; + beats += _sequencer.step_size(); } + cerr << endl; } void -StepSequence::reset () +StepSequence::reschedule (Temporal::Beats const & start, Temporal::Beats const & offset) { + const size_t s = _sequencer.start_step(); + const size_t e = _sequencer.end_step(); + + for (size_t n = s; n < e; ++n) { + _steps[n]->reschedule (start, offset); + } } void @@ -445,23 +456,32 @@ StepSequence::set_channel (int c) _channel = c; } -Temporal::Beats -StepSequence::wrap (Temporal::Beats const & b) const -{ - if (b < end_beat) { - return b; - } - - return b - end_beat; -} - - bool StepSequence::run (MidiBuffer& buf, bool running, samplepos_t start_sample, samplepos_t end_sample, MidiStateTracker& tracker) { - for (Steps::iterator s = _steps.begin(); s != _steps.end(); ++s) { - (*s)->run (buf, running, start_sample, end_sample, tracker); + const size_t s = _sequencer.start_step(); + const size_t e = _sequencer.end_step(); + const size_t t = _steps.size(); + size_t n = 0; + + /* steps before the start step .. may still have pending off's or ... */ + + while (n < s) { + _steps[n++]->run (buf, false, start_sample, end_sample, tracker); } + + /* currently in use steps */ + + while (n < e) { + _steps[n++]->run (buf, running, start_sample, end_sample, tracker); + } + + /* steps after the end step .. may still have pending off's or ... */ + + while (n < t) { + _steps[n++]->run (buf, false, start_sample, end_sample, tracker); + } + return true; } @@ -487,12 +507,19 @@ StepSequence::set_state (XMLNode const &, int) /**/ +MultiAllocSingleReleasePool StepSequencer::Request::pool (X_("step sequencer requests"), sizeof (StepSequencer::Request), 64); + StepSequencer::StepSequencer (TempoMap& tmap, size_t nseqs, size_t nsteps, Temporal::Beats const & step_size, Temporal::Beats const & bar_size, int notenum) : _tempo_map (tmap) - , _step_size (step_size) - , _start (0) - , _end (nsteps) , _last_step (0) + , _step_size (step_size) + , _start_step (0) + , _end_step (nsteps) + , last_start (0) + , last_end (0) + , _running (false) + , _step_capacity (nsteps) + , requests (64) { for (size_t n = 0; n < nseqs; ++n) { _sequences.push_back (new StepSequence (*this, nsteps, step_size, bar_size, notenum)); @@ -507,19 +534,82 @@ StepSequencer::~StepSequencer () } } +Temporal::Beats +StepSequencer::reschedule (samplepos_t start_sample) +{ + cerr << "SEQ reschedule\n"; + + /* compute the beat position of this first "while-moving + * run() call as an offset into the sequencer's current loop + * length. + */ + + const Temporal::Beats start_beat (_tempo_map.beat_at_sample (start_sample)); + const int32_t tick_duration = duration().to_ticks(); + + const Temporal::Beats closest_previous_loop_start = Temporal::Beats::ticks ((start_beat.to_ticks() / tick_duration) * tick_duration); + const Temporal::Beats offset = Temporal::Beats::ticks ((start_beat.to_ticks() % tick_duration)); + + for (StepSequences::iterator s = _sequences.begin(); s != _sequences.end(); ++s) { + (*s)->reschedule (closest_previous_loop_start, offset); + } + + return closest_previous_loop_start; +} + bool -StepSequencer::run (MidiBuffer& buf, bool running, samplepos_t start_sample, samplepos_t end_sample, MidiStateTracker& tracker) +StepSequencer::run (MidiBuffer& buf, samplepos_t start_sample, samplepos_t end_sample, double speed, pframes_t, bool) { Glib::Threads::Mutex::Lock lm (_sequence_lock); + bool resolve = false; + bool need_reschedule = check_requests (); + + if (speed == 0) { + if (_running) { + resolve = true; + _running = false; + } + } + + if (speed != 0) { + + if (!_running || (last_end != start_sample)) { + + if (last_end != start_sample) { + /* non-linear motion, need to resolve notes */ + resolve = true; + } + + _last_startup = reschedule (start_sample); + last_start = start_sample; + need_reschedule = false; + _running = true; + } + } + + + if (need_reschedule) { + reschedule (start_sample); + need_reschedule = false; + } + for (StepSequences::iterator s = _sequences.begin(); s != _sequences.end(); ++s) { - (*s)->run (buf, running, start_sample, end_sample, tracker); + (*s)->run (buf, _running, start_sample, end_sample, outbound_tracker); } const Temporal::Beats terminal_beat = Temporal::Beats (_tempo_map.beat_at_sample (end_sample - 1)); const size_t dur_ticks = duration().to_ticks(); const size_t step_ticks = _step_size.to_ticks(); - _last_step = ((terminal_beat - _last_start).to_ticks() % dur_ticks) / step_ticks; + + _last_step = _start_step + (((terminal_beat - _last_startup).to_ticks() % dur_ticks) / step_ticks); + + if (resolve) { + outbound_tracker.resolve_notes (buf, 0); + } + + last_start = start_sample; + last_end = end_sample; return true; } @@ -549,20 +639,12 @@ StepSequencer::reset () Temporal::Beats StepSequencer::duration() const { - return _step_size * (_end - _start) ; + return _step_size * (_end_step - _start_step) ; } void -StepSequencer::startup (Temporal::Beats const & start, Temporal::Beats const & offset) +StepSequence::reset () { - _last_start = start; - { - Glib::Threads::Mutex::Lock lm1 (_sequence_lock); - for (StepSequences::iterator s = _sequences.begin(); s != _sequences.end(); ++s) { - (*s)->startup (start, offset); - } - } - } StepSequence& @@ -572,6 +654,24 @@ StepSequencer::sequence (size_t n) const return *_sequences[n]; } +void +StepSequencer::set_start_step (size_t n) +{ + Request* r = new Request; + r->type = Request::SetStartStep; + r->start_step = n; + requests.write (&r, 1); +} + +void +StepSequencer::set_end_step (size_t n) +{ + Request* r = new Request; + r->type = Request::SetEndStep; + r->end_step = n; + requests.write (&r, 1); +} + XMLNode& StepSequencer::get_state() { @@ -583,3 +683,54 @@ StepSequencer::set_state (XMLNode const &, int) { return 0; } + +bool +StepSequencer::check_requests () +{ + Request* req; + bool changed = false; + bool reschedule = false; + + while (requests.read (&req, 1)) { + + if (req->type & Request::SetStartStep) { + if (req->start_step != _start_step) { + if (req->start_step < _end_step) { + _start_step = req->start_step; + reschedule = true; + changed = true; + } + } + } + + if (req->type & Request::SetEndStep) { + if (req->end_step != _end_step) { + if (req->end_step > _start_step) { + _end_step = req->end_step; + reschedule = true; + changed = true; + } + } + } + + if (req->type & Request::SetNSequences) { + // XXXX + } + if (req->type & Request::SetStepSize) { + if (_step_size != req->step_size) { + _step_size = req->step_size; + reschedule = true; + changed = true; + } + } + } + + delete req; + + if (changed) { + PropertyChange pc; + PropertyChanged (pc); + } + + return reschedule; +}