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 | |||
* 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: {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. | |||
@@ -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 | |||
process ( jack_nframes_t nframes, void *arg ) | |||
{ | |||
@@ -246,29 +288,12 @@ process ( jack_nframes_t nframes, void *arg ) | |||
{ | |||
case PATTERN: | |||
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; | |||
} | |||
} | |||
switch ( song.play_mode ) | |||
{ | |||
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 ); | |||
break; | |||
case PATTERN: | |||
@@ -435,7 +460,7 @@ midi_init ( void ) | |||
//1 jack_set_buffer_size_callback( client, bufsize, 0 ); | |||
jack_set_process_callback( client, process, 0 ); | |||
jack_set_sync_callback( client, sync, 0 ); | |||
/* /\* initialize buffer size *\/ */ | |||
/* transport_poll(); */ | |||
@@ -300,8 +300,7 @@ pattern::draw_row_names ( Canvas *c ) const | |||
void | |||
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; | |||
_end = end; | |||
@@ -402,8 +401,6 @@ pattern::play ( tick_t start, tick_t end ) const | |||
if ( end > _end ) | |||
end = _end; | |||
_playing = true; | |||
// where we are in the absolute time | |||
tick_t tick = start - _start; | |||
int num_played = tick / d->length; | |||
@@ -415,13 +412,15 @@ pattern::play ( tick_t start, tick_t end ) const | |||
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; | |||
} | |||
_playing = true; | |||
if ( mode() == MUTE ) | |||
return; | |||
goto done; | |||
try_again: | |||
@@ -475,7 +474,15 @@ try_again: | |||
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 | |||
return; | |||
if ( start < _start ) | |||
start = _start; | |||
@@ -203,7 +202,7 @@ phrase::play ( tick_t start, tick_t end ) | |||
_index = tick % d->length; | |||
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: | |||
@@ -215,36 +214,27 @@ try_again: | |||
{ | |||
// 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 ) | |||
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. | |||
offset += d->length; | |||
goto try_again; | |||