more redesign of beatbox fundamentals, adding back RT-safe modifications to sequencer
This commit is contained in:
@@ -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<Event*> IncompleteNotes;
|
||||
IncompleteNotes _incomplete_notes;
|
||||
|
||||
typedef std::set<Event*,EventComparator> Events;
|
||||
Events _current_events;
|
||||
|
||||
void compute_tempo_clocks ();
|
||||
|
||||
PBD::RingBuffer<Event*> add_queue;
|
||||
PBD::RingBuffer<Event*> remove_queue;
|
||||
|
||||
bool fill_midi_source (boost::shared_ptr<SMFSource>);
|
||||
|
||||
|
||||
@@ -22,16 +22,20 @@
|
||||
#include <vector>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <boost/atomic.hpp>
|
||||
#include <boost/rational.hpp>
|
||||
|
||||
#include <glibmm/threads.h>
|
||||
|
||||
#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<int> 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<Step*> 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<StepSequence*> 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<Request*> requests;
|
||||
bool check_requests ();
|
||||
Temporal::Beats reschedule (samplepos_t);
|
||||
};
|
||||
|
||||
} /* namespace */
|
||||
|
||||
@@ -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<Source> src)
|
||||
{
|
||||
@@ -267,6 +107,7 @@ BeatBox::fill_source (boost::shared_ptr<Source> src)
|
||||
bool
|
||||
BeatBox::fill_midi_source (boost::shared_ptr<SMFSource> src)
|
||||
{
|
||||
#if 0
|
||||
Temporal::Beats smf_beats;
|
||||
|
||||
if (_current_events.empty()) {
|
||||
@@ -295,6 +136,6 @@ BeatBox::fill_midi_source (boost::shared_ptr<SMFSource> src)
|
||||
} catch (...) {
|
||||
cerr << "Exception during beatbox write to SMF... " << endl;
|
||||
}
|
||||
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user