diff --git a/jack.C b/jack.C index 2f325f9..234ec64 100644 --- a/jack.C +++ b/jack.C @@ -48,7 +48,7 @@ int sample_rate; const int MAX_PORT = 16; -const int subticks_per_tick = 2048; +const int subticks_per_tick = 4096; /* timers for notes on all channels and ports. When a note is played, * the respective value in this array is set to the note duraction in @@ -209,16 +209,16 @@ process ( jack_nframes_t nframes, void *arg ) static tick_t onph = 0; static int old_play_mode = PATTERN; - ::nframes = nframes; + static int not_dropped = 0; - // init all port buffers (maybe we should only do this as needed) - /* loop over stuff */ + ::nframes = nframes; transport.nframes = nframes; transport.poll(); - tick_t ph = trunc( transport.ticks ); - tick_t nph = trunc( transport.ticks + transport.ticks_per_period ); + /* ph-nph is exclusive. It is important that in normal continuous playback each tick is covered exactly once! */ + const tick_t ph = transport.ticks; + const tick_t nph = trunc( transport.ticks + transport.ticks_per_period ); if ( ! transport.valid ) goto schedule; @@ -227,12 +227,20 @@ process ( jack_nframes_t nframes, void *arg ) goto schedule; if ( ph != onph ) - DWARNING( "dropped ticks" ); + { + if ( onph > ph ) + DWARNING( "duplicated %lu ticks (out of %d)", onph - ph, (int)(not_dropped * transport.ticks_per_period) ); + else + DWARNING( "dropped %lu ticks (out of %d)", ph - onph, (int)(not_dropped * transport.ticks_per_period) ); + + not_dropped = 0; + } + + ++not_dropped; onph = nph; -// MESSAGE( "tpp %f %f-%f", transport.ticks_per_period, ph, nph ); -// MESSAGE( "tpp %f %lu-%lu", transport.ticks_per_period, ph, nph ); +// DMESSAGE( "tpp %f %lu-%lu", transport.ticks_per_period, ph, nph ); switch ( old_play_mode ) { diff --git a/transport.C b/transport.C index adad049..44dd59c 100644 --- a/transport.C +++ b/transport.C @@ -37,20 +37,26 @@ static volatile bool _done; /** callback for when we're Timebase Master, mostly taken from * transport.c in Jack's example clients. */ +/* FIXME: there is a subtle interaction here between the tempo and + * JACK's buffer size. Inflating ticks_per_beat (as jack_transport + * does) diminishes the effect of this correlation, but does not + * eliminate it... This is caused by the accumulation of a precision + * error, and all timebase master routines I've examined appear to + * suffer from this same tempo distortion (and all use the magic + * number of 1920 ticks_per_beat in an attempt to reduce the magnitude + * of the error. Currently, we keep this behaviour. */ void Transport::timebase ( jack_transport_state_t state, jack_nframes_t nframes, jack_position_t *pos, int new_pos, void *arg ) { - pos->valid = JackPositionBBT; - pos->beats_per_bar = transport._master_beats_per_bar; - pos->ticks_per_beat = PPQN; - - /* FIXME: WTF is this? Quarter note? */ - pos->beat_type = transport._master_beat_type; - - pos->beats_per_minute = transport._master_beats_per_minute; if ( new_pos || ! _done ) { + pos->valid = JackPositionBBT; + pos->beats_per_bar = transport._master_beats_per_bar; + pos->ticks_per_beat = 1920.0; /* magic number means what? */ + pos->beat_type = transport._master_beat_type; + pos->beats_per_minute = transport._master_beats_per_minute; + double wallclock = (double)pos->frame / (pos->frame_rate * 60); unsigned long abs_tick = wallclock * pos->beats_per_minute * pos->ticks_per_beat; @@ -66,17 +72,19 @@ Transport::timebase ( jack_transport_state_t state, jack_nframes_t nframes, jack } else { - // FIXME: use ticks_per_period here? pos->tick += nframes * pos->ticks_per_beat * pos->beats_per_minute / (pos->frame_rate * 60); - while (pos->tick >= pos->ticks_per_beat) { + while ( pos->tick >= pos->ticks_per_beat ) + { pos->tick -= pos->ticks_per_beat; - if (++pos->beat > pos->beats_per_bar) { + + if ( ++pos->beat > pos->beats_per_bar ) + { pos->beat = 1; + ++pos->bar; - pos->bar_start_tick += - pos->beats_per_bar - * pos->ticks_per_beat; + + pos->bar_start_tick += pos->beats_per_bar * pos->ticks_per_beat; } } } @@ -132,17 +140,22 @@ Transport::poll ( void ) /* FIXME: this only needs to be calculated if bpm or framerate changes */ { - double frames_per_beat = frame_rate * 60 / beats_per_minute; + const double frames_per_beat = frame_rate * 60 / beats_per_minute; frames_per_tick = frames_per_beat / (double)PPQN; ticks_per_period = nframes / frames_per_tick; } tick_t abs_tick = (pos.bar * pos.beats_per_bar + pos.beat) * pos.ticks_per_beat + pos.tick; - ticks = abs_tick * (PPQN / pos.ticks_per_beat); -// ticks = abs_tick / (pos.ticks_per_beat / PPQN); +// tick_t abs_tick = pos.bar_start_tick + (pos.beat * pos.ticks_per_beat) + pos.tick; + + /* scale Jack's ticks to our ticks */ + + const double pulses_per_tick = PPQN / pos.ticks_per_beat; + + ticks = abs_tick * pulses_per_tick; + tick = tick * pulses_per_tick; - tick = tick * (PPQN / pos.ticks_per_beat); ticks_per_beat = PPQN; } diff --git a/transport.H b/transport.H index 5b06ead..86d3c7e 100644 --- a/transport.H +++ b/transport.H @@ -53,8 +53,8 @@ public: playhead_t ticks; unsigned beats_per_bar; - unsigned ticks_per_beat; unsigned beat_type; + double ticks_per_beat; double beats_per_minute; double ticks_per_period;