ardour/libs/ardour/ardour/step_sequencer.h

356 lines
9.5 KiB
C++

/*
* Copyright (C) 2018 Paul Davis <paul@linuxaudiosystems.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __libardour_step_sequencer_h__
#define __libardour_step_sequencer_h__
#include <vector>
#include <unistd.h>
#include <boost/atomic.hpp>
#include <boost/rational.hpp>
#include <boost/intrusive/list.hpp>
#include <glibmm/threads.h>
#include "pbd/pool.h"
#include "pbd/ringbuffer.h"
#include "pbd/stateful.h"
#include "evoral/Event.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 {
class MidiBuffer;
class MidiNoteTracker;
class StepSequencer;
class StepSequence;
class TempoMap;
class SMFSource;
typedef std::pair<Temporal::Beats,samplepos_t> BeatPosition;
typedef std::vector<BeatPosition> BeatPositions;
typedef Evoral::Event<Temporal::Beats> MusicTimeEvent;
typedef std::vector<MusicTimeEvent*> MusicTimeEvents;
class Step : public PBD::Stateful {
public:
enum Mode {
AbsolutePitch,
RelativePitch
};
typedef boost::rational<int> DurationRatio;
Step (StepSequence&, size_t n, Temporal::Beats const & beat, int notenum);
~Step ();
size_t index() const { return _index; }
void set_note (double note, double velocity = 0.5, int n = 0);
void set_chord (size_t note_cnt, double* notes);
void set_parameter (int number, double value, int n = 0);
void adjust_velocity (int amt);
void adjust_pitch (int amt);
void adjust_duration (DurationRatio const & amt);
void adjust_octave (int amt);
void adjust_offset (double fraction);
Mode mode() const { return _mode; }
void set_mode (Mode m);
double note (size_t n = 0) const { return _notes[n].number; }
double velocity (size_t n = 0) const { return _notes[n].velocity; }
void set_velocity (double, size_t n = 0);
DurationRatio duration () const { return _duration; }
void set_duration (DurationRatio const &);
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; }
void set_enabled (bool);
bool enabled() const { return _enabled; }
void set_repeat (size_t r);
size_t repeat() const { return _repeat; }
void set_beat (Temporal::Beats const & beat);
Temporal::Beats beat () const { return _nominal_beat; }
bool run (MidiBuffer& buf, bool running, samplepos_t, samplepos_t, MidiNoteTracker&);
bool skipped() const { return _skipped; }
void set_skipped (bool);
void reschedule (Temporal::Beats const &, Temporal::Beats const &);
int octave_shift() const { return _octave_shift; }
void set_octave_shift (int);
XMLNode& get_state() const;
int set_state (XMLNode const &, int);
void dump (MusicTimeEvents&, Temporal::Beats const&) const;
static const int _notes_per_step = 5;
static const int _parameters_per_step = 5;
private:
friend class StepSequence; /* HACK */
StepSequence& _sequence;
size_t _index;
bool _enabled;
Temporal::Beats _nominal_beat;
Temporal::Beats _scheduled_beat;
bool _skipped;
Mode _mode;
int _octave_shift;
DurationRatio _duration;
struct ParameterValue {
int parameter;
double value;
};
struct Note {
union {
double number; /* typically MIDI note number */
double interval; /* semitones */
};
double velocity;
Temporal::Beats offset;
MIDI::byte off_msg[3];
Note () : number (-1), velocity (0.0) {}
Note (double n, double v,Temporal::Beats const & o) : number (n), velocity (v), offset (o) {}
};
Note _notes[_notes_per_step];
ParameterValue _parameters[_parameters_per_step];
size_t _repeat;
void check_note (size_t n, MidiBuffer& buf, bool, samplepos_t, samplepos_t, MidiNoteTracker&);
void check_parameter (size_t n, MidiBuffer& buf, bool, samplepos_t, samplepos_t);
void dump_note (MusicTimeEvents&, size_t n, Temporal::Beats const &) const;
void dump_parameter (MusicTimeEvents&, size_t n, Temporal::Beats const &) const;
StepSequencer& sequencer() const;
};
class StepSequence : public PBD::Stateful
{
public:
enum Direction {
forwards = 0,
backwards = 1,
end_to_end = 2,
rd_random = 3
};
StepSequence (StepSequencer &myseq, size_t index, size_t nsteps, Temporal::Beats const & step_size, Temporal::Beats const & bar_size, int notenum);
~StepSequence ();
size_t index() const { return _index; }
size_t nsteps() const { return _steps.size(); }
Step& step (size_t n) const;
void startup (Temporal::Beats const & start, Temporal::Beats const & offset);
int root() const { return _root; }
void set_root (int 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);
void shift_left (size_t n = 1);
void shift_right (size_t n = 1);
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, MidiNoteTracker&);
StepSequencer& sequencer() const { return _sequencer; }
XMLNode& get_state() const;
int set_state (XMLNode const &, int);
void dump (MusicTimeEvents&, Temporal::Beats const &) const;
private:
StepSequencer& _sequencer;
int _index;
mutable Glib::Threads::Mutex _step_lock;
typedef std::vector<Step*> Steps;
Steps _steps;
int _channel; /* MIDI channel */
int _root;
MusicalMode _mode;
};
class StepSequencer : public PBD::Stateful
{
public:
StepSequencer (TempoMap&, size_t nseqs, size_t nsteps, Temporal::Beats const & step_size, Temporal::Beats const & bar_size, int notenum);
~StepSequencer ();
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;
StepSequence& sequence (size_t n) const;
Temporal::Beats duration() const;
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);
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, samplepos_t, samplepos_t, double, pframes_t, bool);
TempoMap& tempo_map() const { return _tempo_map; }
XMLNode& get_state() const;
int set_state (XMLNode const &, int);
void queue_note_off (Temporal::Beats const &, uint8_t note, uint8_t velocity, uint8_t channel);
std::shared_ptr<Source> write_to_source (Session& s, std::string p = std::string()) const;
private:
mutable Glib::Threads::Mutex _sequence_lock;
TempoMap& _tempo_map;
typedef std::vector<StepSequence*> StepSequences;
StepSequences _sequences;
Temporal::Beats _last_startup; /* last time we started running */
size_t _last_step; /* last step that we executed */
Temporal::Beats _step_size;
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::MidiNoteTracker 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);
struct NoteOffBlob : public boost::intrusive::list_base_hook<> {
NoteOffBlob (Temporal::Beats const & w, uint8_t n, uint8_t v, uint8_t c)
: when (w) { buf[0] = 0x80|c; buf[1] = n; buf[2] = v; }
Temporal::Beats when;
uint8_t buf[3];
static Pool pool;
void *operator new (size_t) {
return pool.alloc ();
}
void operator delete (void* ptr, size_t /* size */) {
pool.release (ptr);
}
bool operator< (NoteOffBlob const & other) const {
return when < other.when;
}
};
typedef boost::intrusive::list<NoteOffBlob> NoteOffList;
NoteOffList note_offs;
void check_note_offs (ARDOUR::MidiBuffer&, samplepos_t start_sample, samplepos_t last_sample);
void clear_note_offs ();
bool fill_midi_source (std::shared_ptr<SMFSource> src) const;
};
} /* namespace */
#endif /* __libardour_step_sequencer_h__ */