Now patterns should be triggered properly even if we come in in the middle of a phrase, as happens when randomly relocating the transport.tags/non-sequencer-v1.9.4
@@ -5,7 +5,6 @@ | |||||
; General | ; General | ||||
* TODO: {3} fix transport seeking to the middle of a phrase/pattern in Sequence Mode. | |||||
* TODO: {3} deal with dropped ticks from timebase master better when running as slave. | * TODO: {3} deal with dropped ticks from timebase master better when running as slave. | ||||
* TODO: {2} move keybindings into menus for discoverability (like in Non-DAW) | * TODO: {2} move keybindings into menus for discoverability (like in Non-DAW) | ||||
* TODO: {2} improve or get rid of the transport.valid test at initialization time. | * TODO: {2} improve or get rid of the transport.valid test at initialization time. | ||||
@@ -202,6 +202,48 @@ midi_all_sound_off ( void ) | |||||
} | } | ||||
} | } | ||||
static void | |||||
stop_all_patterns ( void ) | |||||
{ | |||||
for ( uint i = pattern::patterns(); i--; ) | |||||
{ | |||||
pattern *p = pattern::pattern_by_number( i + 1 ); | |||||
p->stop(); | |||||
} | |||||
} | |||||
static int | |||||
sync ( jack_transport_state_t state, jack_position_t *pos, void * ) | |||||
{ | |||||
static bool seeking = false; | |||||
switch ( state ) | |||||
{ | |||||
case JackTransportStopped: /* new position requested */ | |||||
/* JACK docs lie. This is only called when the transport | |||||
is *really* stopped, not when starting a slow-sync | |||||
cycle */ | |||||
stop_all_patterns(); | |||||
return 1; | |||||
case JackTransportStarting: /* this means JACK is polling slow-sync clients */ | |||||
{ | |||||
stop_all_patterns(); | |||||
return 1; | |||||
} | |||||
case JackTransportRolling: /* JACK's timeout has expired */ | |||||
/* FIXME: what's the right thing to do here? */ | |||||
// request_locate( pos->frame ); | |||||
return 1; | |||||
break; | |||||
default: | |||||
WARNING( "unknown transport state" ); | |||||
} | |||||
return 0; | |||||
} | |||||
static int | static int | ||||
process ( jack_nframes_t nframes, void *arg ) | process ( jack_nframes_t nframes, void *arg ) | ||||
{ | { | ||||
@@ -246,29 +288,12 @@ process ( jack_nframes_t nframes, void *arg ) | |||||
{ | { | ||||
case PATTERN: | case PATTERN: | ||||
case TRIGGER: | case TRIGGER: | ||||
{ | |||||
// stop all patterns. | |||||
for ( uint i = pattern::patterns(); i--; ) | |||||
{ | |||||
pattern *p = pattern::pattern_by_number( i + 1 ); | |||||
p->stop(); | |||||
} | |||||
stop_all_patterns(); | |||||
break; | break; | ||||
} | |||||
} | } | ||||
switch ( song.play_mode ) | switch ( song.play_mode ) | ||||
{ | { | ||||
case SEQUENCE: | case SEQUENCE: | ||||
// first handle patterns already playing | |||||
for ( uint i = pattern::patterns(); i--; ) | |||||
{ | |||||
pattern *p = pattern::pattern_by_number( i + 1 ); | |||||
if ( p && p->playing() ) | |||||
p->play( ph, nph ); | |||||
} | |||||
playlist->play( ph, nph ); | playlist->play( ph, nph ); | ||||
break; | break; | ||||
case PATTERN: | case PATTERN: | ||||
@@ -435,7 +460,7 @@ midi_init ( void ) | |||||
//1 jack_set_buffer_size_callback( client, bufsize, 0 ); | //1 jack_set_buffer_size_callback( client, bufsize, 0 ); | ||||
jack_set_process_callback( client, process, 0 ); | jack_set_process_callback( client, process, 0 ); | ||||
jack_set_sync_callback( client, sync, 0 ); | |||||
/* /\* initialize buffer size *\/ */ | /* /\* initialize buffer size *\/ */ | ||||
/* transport_poll(); */ | /* transport_poll(); */ | ||||
@@ -300,8 +300,7 @@ pattern::draw_row_names ( Canvas *c ) const | |||||
void | void | ||||
pattern::trigger ( tick_t start, tick_t end ) | pattern::trigger ( tick_t start, tick_t end ) | ||||
{ | { | ||||
if ( start > end ) | |||||
ASSERTION( "programming error: invalid loop trigger! (%lu-%lu)", start, end ); | |||||
ASSERT( start <= end, "programming error: invalid loop trigger! (%lu-%lu)", start, end ); | |||||
_start = start; | _start = start; | ||||
_end = end; | _end = end; | ||||
@@ -402,8 +401,6 @@ pattern::play ( tick_t start, tick_t end ) const | |||||
if ( end > _end ) | if ( end > _end ) | ||||
end = _end; | end = _end; | ||||
_playing = true; | |||||
// where we are in the absolute time | // where we are in the absolute time | ||||
tick_t tick = start - _start; | tick_t tick = start - _start; | ||||
int num_played = tick / d->length; | int num_played = tick / d->length; | ||||
@@ -415,13 +412,15 @@ pattern::play ( tick_t start, tick_t end ) const | |||||
if ( _index < end - start ) | if ( _index < end - start ) | ||||
{ | { | ||||
DMESSAGE( "Triggered pattern %d at tick %lu (ls: %lu, le: %lu, o: %lu)", 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; | ||||
} | } | ||||
_playing = true; | |||||
if ( mode() == MUTE ) | if ( mode() == MUTE ) | ||||
return; | |||||
goto done; | |||||
try_again: | try_again: | ||||
@@ -475,7 +474,15 @@ try_again: | |||||
DMESSAGE( "out of events, resetting to satisfy loop" ); | DMESSAGE( "out of events, resetting to satisfy loop" ); | ||||
done: ; | |||||
done: | |||||
if ( _end == end ) | |||||
{ | |||||
/* we're doing playing this trigger */ | |||||
DMESSAGE( "Pattern %d ended at tick %lu (ls: %lu, le: %lu, o: %lu)", number(), end, _start, _end, offset ); | |||||
stop(); | |||||
} | |||||
} | } | ||||
@@ -186,7 +186,6 @@ phrase::play ( tick_t start, tick_t end ) | |||||
// not ready yet | // not ready yet | ||||
return; | return; | ||||
if ( start < _start ) | if ( start < _start ) | ||||
start = _start; | start = _start; | ||||
@@ -203,7 +202,7 @@ phrase::play ( tick_t start, tick_t end ) | |||||
_index = tick % d->length; | _index = tick % d->length; | ||||
if ( _index < end - start ) | if ( _index < end - start ) | ||||
MESSAGE( "Triggered phrase %d at tick %lu (ls: %lu, le: %lu, o: %lu)", number(), start, _start, _end, offset ); | |||||
DMESSAGE( "Triggered phrase %d at tick %lu (ls: %lu, le: %lu, o: %lu)", number(), start, _start, _end, offset ); | |||||
try_again: | try_again: | ||||
@@ -215,36 +214,27 @@ try_again: | |||||
{ | { | ||||
// MESSAGE( "s[%ld] -> t[%ld] : %ld, len %ld", start, end, e->timestamp(), _length ); // (*e).print(); | // MESSAGE( "s[%ld] -> t[%ld] : %ld, len %ld", start, end, e->timestamp(), _length ); // (*e).print(); | ||||
tick_t ts = e->timestamp() + offset; | |||||
const tick_t ts = e->timestamp() + offset; | |||||
if ( ts >= end ) | if ( ts >= end ) | ||||
goto done; | goto done; | ||||
if ( ts >= start ) | |||||
if ( e->is_note_on() ) | |||||
{ | { | ||||
event ne = *e; | |||||
if ( ne.is_note_on() || ne.is_note_off() ) | |||||
const tick_t tse = offset + e->link()->timestamp(); | |||||
if ( tse > start ) | |||||
{ | { | ||||
int ev_note = e->note(); | |||||
// d->mapping.translate( &ne ); | |||||
pattern *p = pattern::pattern_by_number( 1 + note_to_y( ev_note ) ); | |||||
if ( p ) | |||||
{ | |||||
if ( e->is_note_on() ) | |||||
{ | |||||
p->trigger( ts, offset + e->link()->timestamp() ); | |||||
p->play( ts, end ); | |||||
} | |||||
else | |||||
if ( e->is_note_off() ) | |||||
p->stop(); | |||||
} | |||||
pattern *p = pattern::pattern_by_number( 1 + note_to_y( e->note() ) ); | |||||
if ( ! p->playing() ) | |||||
p->trigger( ts, offset + e->link()->timestamp() ); | |||||
p->play( start, end ); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
// ran out of events, but there's still some loop left to play. | // ran out of events, but there's still some loop left to play. | ||||
offset += d->length; | offset += d->length; | ||||
goto try_again; | goto try_again; | ||||