Pattern muteing/unmuteing (via GUI or MIDI) takes effect the next time the pattern loops.tags/non-sequencer-v1.9.4
@@ -822,7 +822,8 @@ Grid::mode ( int m ) | |||||
{ | { | ||||
_mode = m; | _mode = m; | ||||
signal_settings_change(); | |||||
/* can't do this in RT thread, sorry. */ | |||||
/// signal_settings_change(); | |||||
} | } | ||||
int | int | ||||
@@ -122,7 +122,7 @@ protected: | |||||
volatile mutable tick_t _index; /* playhead, relative to start -- primarily used to draw the playhead */ | volatile mutable tick_t _index; /* playhead, relative to start -- primarily used to draw the playhead */ | ||||
volatile mutable bool _playing; /* true if currently playing */ | volatile mutable bool _playing; /* true if currently playing */ | ||||
volatile int _mode; /* mute, solo */ | |||||
mutable volatile int _mode; /* mute, solo */ | |||||
// FIXME: shouldn't this be "volatile"? | // FIXME: shouldn't this be "volatile"? | ||||
// const volatile data *_rd; /* read only data used by RT thread */ | // const volatile data *_rd; /* read only data used by RT thread */ | ||||
@@ -185,7 +185,7 @@ if ( Fl::event() == FL_SHORTCUT && Fl::event_key() == FL_Escape ) | |||||
if ( maybe_save_song() ) | if ( maybe_save_song() ) | ||||
quit();} open | quit();} open | ||||
xywh {209 100 865 800} type Double box PLASTIC_UP_BOX color 37 resizable xclass non size_range {600 420 0 0} visible | |||||
xywh {856 305 865 800} type Double box PLASTIC_UP_BOX color 37 resizable xclass non size_range {600 420 0 0} visible | |||||
} { | } { | ||||
Fl_Menu_Bar menu_bar {open | Fl_Menu_Bar menu_bar {open | ||||
xywh {0 0 869 30} color 37 | xywh {0 0 869 30} color 37 | ||||
@@ -983,7 +983,7 @@ else | |||||
} { | } { | ||||
MenuItem {} { | MenuItem {} { | ||||
label Pattern | label Pattern | ||||
callback {song.play_mode = PATTERN;} | |||||
callback {song.play_mode = PATTERN;} selected | |||||
xywh {5 5 40 25} | xywh {5 5 40 25} | ||||
} | } | ||||
MenuItem {} { | MenuItem {} { | ||||
@@ -996,6 +996,11 @@ else | |||||
callback {song.play_mode = TRIGGER;} | callback {song.play_mode = TRIGGER;} | ||||
xywh {25 25 40 25} | xywh {25 25 40 25} | ||||
} | } | ||||
MenuItem {} { | |||||
label Queue | |||||
callback {song.play_mode = QUEUE;} | |||||
xywh {0 0 40 24} | |||||
} | |||||
} | } | ||||
} | } | ||||
Fl_Group {} {open | Fl_Group {} {open | ||||
@@ -307,6 +307,7 @@ process ( jack_nframes_t nframes, void *arg ) | |||||
{ | { | ||||
case PATTERN: | case PATTERN: | ||||
case TRIGGER: | case TRIGGER: | ||||
case QUEUE: | |||||
DMESSAGE( "Stopping all patterns" ); | DMESSAGE( "Stopping all patterns" ); | ||||
stop_all_patterns(); | stop_all_patterns(); | ||||
break; | break; | ||||
@@ -343,41 +344,55 @@ process ( jack_nframes_t nframes, void *arg ) | |||||
/* no need to pass it to the GUI, we can trigger patterns here */ | /* no need to pass it to the GUI, we can trigger patterns here */ | ||||
if ( e.channel() == 0 && e.is_note_on() ) | if ( e.channel() == 0 && e.is_note_on() ) | ||||
{ | { | ||||
if ( e.note() < pattern::patterns() ) | if ( e.note() < pattern::patterns() ) | ||||
{ | { | ||||
pattern *p = pattern::pattern_by_number( e.note() + 1 ); | pattern *p = pattern::pattern_by_number( e.note() + 1 ); | ||||
if ( p->playing() ) | |||||
if ( TRIGGER == song.play_mode ) | |||||
{ | { | ||||
DMESSAGE( "Untriggering pattern %i", e.note() ); | |||||
if ( e.note() < pattern::patterns() ) | |||||
if ( p->playing() ) | |||||
{ | { | ||||
pattern *p = pattern::pattern_by_number( e.note() + 1 ); | |||||
DMESSAGE( "Untriggering pattern %i ph=%lu, ts=%lu", e.note(), ph, e.timestamp() ); | DMESSAGE( "Untriggering pattern %i ph=%lu, ts=%lu", e.note(), ph, e.timestamp() ); | ||||
p->trigger( ph, e.timestamp() ); | p->trigger( ph, e.timestamp() ); | ||||
} | } | ||||
else | |||||
{ | |||||
DMESSAGE( "Triggering pattern %i ph=%lu, ts=%lu", e.note(), ph, e.timestamp() ); | |||||
p->trigger( e.timestamp(), -1 ); | |||||
} | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
DMESSAGE( "Triggering pattern %i ph=%lu, ts=%lu", e.note(), ph, e.timestamp() ); | |||||
p->trigger( e.timestamp(), -1 ); | |||||
if ( p->mode() == PLAY ) | |||||
{ | |||||
DMESSAGE( "Dequeuing pattern %i ph=%lu, ts=%lu", e.note(), ph, e.timestamp() ); | |||||
p->mode( MUTE ); | |||||
} | |||||
else | |||||
{ | |||||
DMESSAGE( "Queuing pattern %i ph=%lu, ts=%lu", e.note(), ph, e.timestamp() ); | |||||
p->mode( PLAY ); | |||||
} | |||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
switch ( song.play_mode ) | switch ( song.play_mode ) | ||||
{ | { | ||||
case SEQUENCE: | case SEQUENCE: | ||||
playlist->play( ph, nph ); | playlist->play( ph, nph ); | ||||
break; | break; | ||||
case QUEUE: | |||||
case PATTERN: | case PATTERN: | ||||
{ | { | ||||
for ( uint i = pattern::patterns(); i--; ) | for ( uint i = pattern::patterns(); i--; ) | ||||
@@ -51,7 +51,8 @@ bool save_song ( const char *name ); | |||||
enum play_mode_e { | enum play_mode_e { | ||||
PATTERN, | PATTERN, | ||||
SEQUENCE, | SEQUENCE, | ||||
TRIGGER | |||||
TRIGGER, | |||||
QUEUE | |||||
// PHRASE, | // PHRASE, | ||||
}; | }; | ||||
@@ -43,6 +43,9 @@ pattern::pattern ( void ) | |||||
_ppqn = 4; | _ppqn = 4; | ||||
_bpb = 4; | _bpb = 4; | ||||
_note = 8; | _note = 8; | ||||
_queued = -1; | |||||
int _bars = 2; | int _bars = 2; | ||||
// we need to reinitalize this. | // we need to reinitalize this. | ||||
@@ -325,6 +328,12 @@ pattern::stop ( void ) const | |||||
void | void | ||||
pattern::mode ( int n ) | pattern::mode ( int n ) | ||||
{ | { | ||||
if ( QUEUE == song.play_mode ) | |||||
{ | |||||
queue( n ); | |||||
return; | |||||
} | |||||
if ( n == SOLO ) | if ( n == SOLO ) | ||||
{ | { | ||||
if ( pattern::_solo ) | if ( pattern::_solo ) | ||||
@@ -355,6 +364,19 @@ pattern::mode ( void ) const | |||||
return Grid::mode(); | return Grid::mode(); | ||||
} | } | ||||
/* queue a mode change for the next loop */ | |||||
void | |||||
pattern::queue ( int m ) | |||||
{ | |||||
_queued = m; | |||||
} | |||||
int | |||||
pattern::queue ( void ) const | |||||
{ | |||||
return _queued; | |||||
} | |||||
/* WARNING: runs in the RT thread! */ | /* WARNING: runs in the RT thread! */ | ||||
// output notes from /start/ to /end/ (absolute) | // output notes from /start/ to /end/ (absolute) | ||||
void | void | ||||
@@ -389,11 +411,33 @@ pattern::play ( tick_t start, tick_t end ) const | |||||
_index = tick % d->length; | _index = tick % d->length; | ||||
bool reset_queued = false; | |||||
if ( _index < end - start ) | if ( _index < end - start ) | ||||
{ | { | ||||
/* period covers the beginning of the loop */ | |||||
DMESSAGE( "%s pattern %d at tick %lu (ls: %lu, le: %lu, o: %lu)", _playing ? "Looped" : "Triggered", number(), start, _start, _end, offset ); | DMESSAGE( "%s pattern %d at tick %lu (ls: %lu, le: %lu, o: %lu)", _playing ? "Looped" : "Triggered", number(), start, _start, _end, offset ); | ||||
_cleared = false; | _cleared = false; | ||||
if ( PLAY == _queued ) | |||||
{ | |||||
/* set the start point to loop boundary */ | |||||
start = start - _index; | |||||
_mode = PLAY; | |||||
reset_queued = true; | |||||
} | |||||
} | |||||
else if ( _index >= d->length - ( end - start ) ) | |||||
{ | |||||
if ( MUTE == _queued ) | |||||
{ | |||||
/* set the end point to loop boundary */ | |||||
end = end - _index; | |||||
reset_queued = true; | |||||
} | |||||
} | } | ||||
_playing = true; | _playing = true; | ||||
@@ -455,6 +499,12 @@ try_again: | |||||
done: | done: | ||||
if ( _queued >= 0 && reset_queued ) | |||||
{ | |||||
_mode = _queued; | |||||
_queued = -1; | |||||
} | |||||
if ( _end == end ) | if ( _end == end ) | ||||
{ | { | ||||
/* we're done playing this trigger */ | /* we're done playing this trigger */ | ||||
@@ -43,13 +43,12 @@ class pattern : public Grid | |||||
bool _recording; | bool _recording; | ||||
mutable volatile bool _cleared; | mutable volatile bool _cleared; | ||||
volatile bool _triggered; | |||||
mutable volatile int _queued; | |||||
// int _key; | // int _key; | ||||
int _note; | int _note; | ||||
void _add ( void ); | void _add ( void ); | ||||
@@ -88,8 +87,8 @@ public: | |||||
void record( int mode ); | void record( int mode ); | ||||
void record_stop ( void ); | void record_stop ( void ); | ||||
void toggle_trigger ( void ); | |||||
bool triggered ( void ) const; | |||||
void queue ( int mode ); | |||||
int queue ( void ) const; | |||||
void randomize_row ( int y, int feel, float probability ); | void randomize_row ( int y, int feel, float probability ); | ||||