// ---------------------------------------------------------------------------- // // Copyright (C) 2012 Fons Adriaensen // // 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 3 of the License, 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, see . // // ---------------------------------------------------------------------------- #include #include #include #include #include "jackclient.h" #include "alsathread.h" Jackclient::Jackclient (jack_client_t* cl, const char*jserv, int mode, int nchan, void *arg) : _client (cl), _arg (arg), _mode (mode), _nchan (nchan), _state (INIT), _freew (false) { init (jserv); } Jackclient::~Jackclient (void) { fini (); } void Jackclient::init (const char *jserv) { int spol; jack_status_t stat; struct sched_param spar; if (_client == 0) { return; } jack_set_process_callback (_client, jack_static_process, (void *) this); jack_set_latency_callback (_client, jack_static_latency, (void *) this); jack_set_freewheel_callback (_client, jack_static_freewheel, (void *) this); jack_set_buffer_size_callback (_client, jack_static_buffsize, (void *) this); jack_on_shutdown (_client, jack_static_shutdown, (void *) this); _bsize = 0; _fsamp = 0; _jname = jack_get_client_name (_client); _bsize = jack_get_buffer_size (_client); _fsamp = jack_get_sample_rate (_client); if (_nchan) { register_ports (_nchan); } _rprio = jack_client_real_time_priority (_client); } void Jackclient::register_ports (int n) { int i, flags; char s [64]; if (n > sizeof (_ports) / sizeof (_ports[0])) { return; } flags = JackPortIsTerminal | JackPortIsPhysical; for (i = 0; i < n; i++) { if (_mode == PLAY) { sprintf (s, "playback_%d", i + 1); _ports [i] = jack_port_register (_client, s, JACK_DEFAULT_AUDIO_TYPE, flags | JackPortIsInput, 0); } else { sprintf (s, "capture_%d", i + 1); _ports [i] = jack_port_register (_client, s, JACK_DEFAULT_AUDIO_TYPE, flags | JackPortIsOutput, 0); } } _nchan = n; _buff = new float [_bsize * _nchan]; } void Jackclient::fini (void) { delete[] _buff; } void Jackclient::jack_static_shutdown (void *arg) { ((Jackclient *) arg)->sendinfo (TERM, 0, 0); } int Jackclient::jack_static_buffsize (jack_nframes_t nframes, void *arg) { Jackclient *J = (Jackclient *) arg; if (J->_bsize == 0) J->_bsize = nframes; else if (J->_bsize != (int) nframes) J->_state = Jackclient::TERM; return 0; } void Jackclient::jack_static_freewheel (int state, void *arg) { ((Jackclient *) arg)->jack_freewheel (state); } void Jackclient::jack_static_latency (jack_latency_callback_mode_t jlcm, void *arg) { ((Jackclient *) arg)->jack_latency (jlcm); } int Jackclient::jack_static_process (jack_nframes_t nframes, void *arg) { return ((Jackclient *) arg)->jack_process (nframes); } void Jackclient::start (Lfq_audio *audioq, Lfq_int32 *commq, Lfq_adata *alsaq, Lfq_jdata *infoq, double ratio, int delay, int ltcor, int rqual) { double d; _audioq = audioq; _commq = commq; _alsaq = alsaq; _infoq = infoq; _quant = ldexp (1e-6f, 28); _ratio = ratio; _rcorr = 1.0; _resamp.setup (_ratio, _nchan, rqual); _resamp.set_rrfilt (100); d = _resamp.inpsize () / 2.0; if (_mode == PLAY) d *= _ratio; _delay = delay + d; _ltcor = ltcor; _ppsec = (_fsamp + _bsize / 2) / _bsize; if (jack_activate (_client)) { fprintf(stderr, "Can't activate Jack"); return; } initwait (_ppsec / 2); jack_recompute_total_latencies (_client); } void Jackclient::initwait (int nwait) { _count = -nwait; _commq->wr_int32 (Alsathread::WAIT); _state = WAIT; if (nwait > _ppsec) sendinfo (WAIT, 0, 0); } void Jackclient::initsync (void) { // Reset all lock-free queues. _commq->reset (); _alsaq->reset (); _audioq->reset (); // Reset and prefill the resampler. _resamp.reset (); _resamp.inp_count = _resamp.inpsize () / 2 - 1; _resamp.out_count = 10000; _resamp.process (); // Initiliase state variables. _t_a0 = _t_a1 = 0; _k_a0 = _k_a1 = 0; _k_j0 = 0; // Initialise loop filter state. _z1 = _z2 = _z3 = 0; // Activate the ALSA thread, _commq->wr_int32 (Alsathread::PROC); _state = SYNC0; sendinfo (SYNC0, 0, 0); } void Jackclient::setloop (double bw) { double w; // Set the loop bandwidth to bw Hz. w = 6.28f * 20 * bw * _bsize / _fsamp; _w0 = 1.0 - exp (-w); w = 6.28f * bw * _ratio / _fsamp; _w1 = w * 1.6; _w2 = w * _bsize / 1.6; } void Jackclient::playback (int nframes) { int i, j, n; float *p, *q; // Interleave inputs into _buff. for (i = 0; i < _nchan; i++) { p = (float *)(jack_port_get_buffer (_ports [i], nframes)); q = _buff + i; for (j = 0; j < _bsize; j++) q [j * _nchan] = p [j]; } // Resample _buff and write to audio queue. // The while loop takes care of wraparound. _resamp.inp_count = _bsize; _resamp.inp_data = _buff; while (_resamp.inp_count) { _resamp.out_count = _audioq->wr_linav (); _resamp.out_data = _audioq->wr_datap (); n = _resamp.out_count; _resamp.process (); n -= _resamp.out_count; _audioq->wr_commit (n); // Adjust state by the number of frames used. _k_j0 += n; } } void Jackclient::capture (int nframes) { int i, j, n; float *p, *q; // Read from audio queue and resample. // The while loop takes care of wraparound. _resamp.out_count = _bsize; _resamp.out_data = _buff; while (_resamp.out_count) { _resamp.inp_count = _audioq->rd_linav (); _resamp.inp_data = _audioq->rd_datap (); n = _resamp.inp_count; _resamp.process (); n -= _resamp.inp_count; _audioq->rd_commit (n); // Adjust state by the number of frames used. _k_j0 += n; } // Deinterleave _buff to outputs. for (i = 0; i < _nchan; i++) { p = _buff + i; q = (float *)(jack_port_get_buffer (_ports [i], nframes)); for (j = 0; j < _bsize; j++) q [j] = p [j * _nchan]; } } void Jackclient::silence (int nframes) { int i; float *q; // Write silence to all jack ports. for (i = 0; i < _nchan; i++) { q = (float *)(jack_port_get_buffer (_ports [i], nframes)); memset (q, 0, nframes * sizeof (float)); } } void Jackclient::sendinfo (int state, double error, double ratio) { Jdata *J; if (_infoq->wr_avail ()) { J = _infoq->wr_datap (); J->_state = state; J->_error = error; J->_ratio = ratio; _infoq->wr_commit (); } } void Jackclient::jack_freewheel (int state) { _freew = state ? true : false; if (_freew) initwait (_ppsec / 4); } void Jackclient::jack_latency (jack_latency_callback_mode_t jlcm) { jack_latency_range_t R; int i; if (_state < WAIT) return; if (_mode == PLAY) { if (jlcm != JackPlaybackLatency) return; R.min = R.max = (int)(_delay / _ratio) + _ltcor; } else { if (jlcm != JackCaptureLatency) return; R.min = R.max = (int)(_delay * _ratio) + _ltcor; } for (i = 0; i < _nchan; i++) { jack_port_set_latency_range (_ports [i], jlcm, &R); } } int Jackclient::jack_process (int nframes) { int dk, n; Adata *D; jack_nframes_t ft; double tj, err, d1, d2; // Buffer size change or other evil. if (_state == TERM) { sendinfo (TERM, 0, 0); return 0; } // Skip cylce if ports may not yet exist. if (_state < WAIT) return 0; // Start synchronisation 1/2 second after entering // the WAIT state. This delay allows the ALSA thread // to restart cleanly if necessary. Disabled while // freewheeling. if (_state == WAIT) { if (_freew) return 0; if (_mode == CAPT) silence (nframes); if (++_count == 0) initsync (); else return 0; } // Compute the start time of the current cycle. // Jack should really provide the usecs directly. ft = jack_last_frame_time (_client); tj = 1e-6 * (jack_frames_to_time (_client, ft) & 0x0FFFFFFF); // Check for any skipped cycles. This is invalid // the first time, but won't be used then anyway. dk = ft - _ft - _bsize; _ft = ft; // Check if we have timing data from the ALSA thread. n = _alsaq->rd_avail (); // If the data queue is full restart synchronisation. // This can happen e.g. on a jack engine timeout, or // when too many cycles have been skipped. if (n == _alsaq->size ()) { initwait (_ppsec / 2); return 0; } if (n) { // Else move interval end to start, and update the // interval end keeping only the most recent data. if (_state < SYNC2) _state++; _t_a0 = _t_a1; _k_a0 = _k_a1; while (_alsaq->rd_avail ()) { D = _alsaq->rd_datap (); // Restart synchronisation in case of // an error in the ALSA interface. if (D->_state == Alsathread::WAIT) { initwait (_ppsec / 2); return 0; } _t_a1 = D->_timer; _k_a1 += D->_nsamp; _alsaq->rd_commit (); } } err = 0; if (_state >= SYNC2) { // Compute the delay error. d1 = modtime (tj - _t_a0); d2 = modtime (_t_a1 - _t_a0); if (_mode == PLAY) { n = _k_j0 - _k_a0; // Must be done as integer as both terms will overflow. err = n - (_k_a1 - _k_a0) * d1 / d2 + _resamp.inpdist () * _ratio - _delay; } else { n = _k_a0 - _k_j0; err = n + (_k_a1 - _k_a0) * d1 / d2 + _resamp.inpdist () - _delay ; } n = (int)(floor (err + 0.5)); if (_state == SYNC2) { // We have the first delay error value. Adjust both the state // variables and the audio queue by the same number of frames // to obtain the actually wanted delay, and start tracking. if (_mode == PLAY) { _audioq->wr_commit (-n); _k_j0 -= n; } else { _audioq->rd_commit (n); _k_j0 += n; } err -= n; setloop (1.0); _state = PROC1; } else if (_state >= PROC1) { // Check error conditions. if (dk) { // Jack skipped some cycles. Adjust both the state // and the audio queue so we can just continue. // The loop will correct the remaining fraction // of a frame. if (_mode == PLAY) { dk = (int)(dk * _ratio + 0.5); if (abs (dk + n) < _bsize / 4) { _audioq->wr_commit (dk); _k_j0 += dk; err += dk; n = 0; } } else { dk = (int)(dk / _ratio + 0.5); if (abs (dk - n) < _bsize / 4) { _audioq->rd_commit (dk); _k_j0 += dk; err -= dk; n = 0; } } } if (fabs (err) >= 50) { // Something is really wrong, wait 15 seconds then restart. initwait (15 * _ppsec); return 0; } } } // Switch to lower bandwidth after 4 seconds. if ((_state == PROC1) && (++_count == 4 * _ppsec)) { _state = PROC2; setloop (0.05); } if (_state >= PROC1) { // Run loop filter and set resample ratio. _z1 += _w0 * (_w1 * err - _z1); _z2 += _w0 * (_z1 - _z2); _z3 += _w2 * _z2; _rcorr = 1 - _z2 - _z3; if (_rcorr > 1.05) _rcorr = 1.05; if (_rcorr < 0.95) _rcorr = 0.95; _resamp.set_rratio (_rcorr); sendinfo (_state, err, _rcorr); // Resample and transfer between audio // queue and jack ports. if (_mode == PLAY) playback (nframes); else capture (nframes); } else if (_mode == CAPT) silence (nframes); return 0; }