summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorfalkTX <falktx@falktx.com>2021-04-14 19:35:02 +0100
committerfalkTX <falktx@falktx.com>2021-04-14 19:35:02 +0100
commit5041ab0fe7fb719efa5122386d355397289a91f0 (patch)
tree0fd2e614c2741e65d15902ec6b4c48d8e5afa807
parente984aeb2b4163b45f095515610432d9f59db243e (diff)
parent852ba2b8ebc944865e75d8eb5d4550d408772f3b (diff)
downloadjack2-1.9.18.tar.gz
Merge branch 'develop'v1.9.18
-rw-r--r--ChangeLog.rst8
-rw-r--r--common/JackClient.cpp3
-rw-r--r--common/JackConstants.h2
-rw-r--r--common/JackMetadata.cpp6
-rw-r--r--linux/JackLinuxFutex.cpp17
-rw-r--r--tools/midi_dump.c5
-rw-r--r--tools/wscript28
-rw-r--r--tools/zalsa/alsathread.cc218
-rw-r--r--tools/zalsa/alsathread.h68
-rw-r--r--tools/zalsa/jackclient.cc549
-rw-r--r--tools/zalsa/jackclient.h120
-rw-r--r--tools/zalsa/lfqueue.cc89
-rw-r--r--tools/zalsa/lfqueue.h182
-rw-r--r--tools/zalsa/pxthread.cc78
-rw-r--r--tools/zalsa/pxthread.h52
-rw-r--r--tools/zalsa/timers.h53
-rw-r--r--tools/zalsa/zita-a2j.cc333
-rw-r--r--tools/zalsa/zita-j2a.cc331
-rw-r--r--wscript7
19 files changed, 2136 insertions, 13 deletions
diff --git a/ChangeLog.rst b/ChangeLog.rst
index 1f95a75c..569024cb 100644
--- a/ChangeLog.rst
+++ b/ChangeLog.rst
@@ -1,6 +1,14 @@
ChangeLog
#########
+* 1.9.18 (2021-04-15)
+
+ * Add zalsa_in/out as internal client (based on zita-a2j/j2a and jack1 code)
+ * Fix jack_midi_dump deadlock on close after the jack server is restarted
+ * Fix interrupt signal for linux futex waits
+ * Fix usage of meta-data in official macOS builds (private DB errors)
+ * Log error message when cleaning previous DB (macOS and Windows)
+
* 1.9.17 (2021-01-15)
* Fix jack_control stopping after first command iteration
diff --git a/common/JackClient.cpp b/common/JackClient.cpp
index b03dbc25..43de6b3e 100644
--- a/common/JackClient.cpp
+++ b/common/JackClient.cpp
@@ -31,6 +31,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include <math.h>
#include <string>
#include <algorithm>
+#include <climits>
using namespace std;
@@ -636,7 +637,7 @@ inline int JackClient::CallProcessCallback()
inline bool JackClient::WaitSync()
{
// Suspend itself: wait on the input synchro
- if (GetGraphManager()->SuspendRefNum(GetClientControl(), fSynchroTable, 0x7FFFFFFF) < 0) {
+ if (GetGraphManager()->SuspendRefNum(GetClientControl(), fSynchroTable, LONG_MAX) < 0) {
jack_error("SuspendRefNum error");
return false;
} else {
diff --git a/common/JackConstants.h b/common/JackConstants.h
index 07f2ca4c..2c4ccf98 100644
--- a/common/JackConstants.h
+++ b/common/JackConstants.h
@@ -24,7 +24,7 @@
#include "config.h"
#endif
-#define VERSION "1.9.17"
+#define VERSION "1.9.18"
#define BUFFER_SIZE_MAX 8192
diff --git a/common/JackMetadata.cpp b/common/JackMetadata.cpp
index 8cf00f32..ad2bdc93 100644
--- a/common/JackMetadata.cpp
+++ b/common/JackMetadata.cpp
@@ -123,9 +123,11 @@ int JackMetadata::PropertyInit()
#endif
if ((ret = fDBenv->open (fDBenv, dbpath, DB_CREATE | DB_INIT_LOCK | DB_INIT_MPOOL | DB_THREAD, 0)) != 0) {
-#ifdef WIN32
+#if defined(WIN32) || defined(__APPLE__)
// new versions of jack2 are built with HAVE_MIXED_SIZE_ADDRESSING, which induces this error, this is expected
if (ret == DB_VERSION_MISMATCH) {
+ jack_error ("Failed to open previous DB environment, trying again clean...");
+
// cleanup old stuff
snprintf (dbpath, sizeof(dbpath), "%s/jack_db/metadata.db", fDBFilesDir);
remove (dbpath);
@@ -145,7 +147,7 @@ int JackMetadata::PropertyInit()
if (ret != 0)
#endif
{
- jack_error ("cannot open DB environment: %s", db_strerror (ret));
+ jack_error ("Cannot open DB environment: %s", db_strerror (ret));
fDBenv = NULL;
return -1;
}
diff --git a/linux/JackLinuxFutex.cpp b/linux/JackLinuxFutex.cpp
index aef99cd2..29b13901 100644
--- a/linux/JackLinuxFutex.cpp
+++ b/linux/JackLinuxFutex.cpp
@@ -23,6 +23,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "JackConstants.h"
#include "JackError.h"
#include "promiscuous.h"
+#include <climits>
#include <fcntl.h>
#include <stdio.h>
#include <sys/mman.h>
@@ -93,18 +94,24 @@ bool JackLinuxFutex::Wait()
fFutex->internal = !fFutex->internal;
}
+ const int wait_mode = fFutex->internal ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT;
+
for (;;)
{
if (__sync_bool_compare_and_swap(&fFutex->futex, 1, 0))
return true;
- if (::syscall(SYS_futex, fFutex, fFutex->internal ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, 0, NULL, NULL, 0) != 0 && errno != EWOULDBLOCK)
- return false;
+ if (::syscall(SYS_futex, fFutex, wait_mode, 0, NULL, NULL, 0) != 0)
+ if (errno != EAGAIN && errno != EINTR)
+ return false;
}
}
bool JackLinuxFutex::TimedWait(long usec)
{
+ if (usec == LONG_MAX)
+ return Wait();
+
if (!fFutex) {
jack_error("JackLinuxFutex::TimedWait name = %s already deallocated!!", fName);
return false;
@@ -120,14 +127,16 @@ bool JackLinuxFutex::TimedWait(long usec)
const int nsecs = (usec % 1000000) * 1000;
const timespec timeout = { static_cast<time_t>(secs), nsecs };
+ const int wait_mode = fFutex->internal ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT;
for (;;)
{
if (__sync_bool_compare_and_swap(&fFutex->futex, 1, 0))
return true;
- if (::syscall(SYS_futex, fFutex, fFutex->internal ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, 0, &timeout, NULL, 0) != 0 && errno != EWOULDBLOCK)
- return false;
+ if (::syscall(SYS_futex, fFutex, wait_mode, 0, &timeout, NULL, 0) != 0)
+ if (errno != EAGAIN && errno != EINTR)
+ return false;
}
}
diff --git a/tools/midi_dump.c b/tools/midi_dump.c
index 7a7ec2b3..ef9148b3 100644
--- a/tools/midi_dump.c
+++ b/tools/midi_dump.c
@@ -112,6 +112,11 @@ process (jack_nframes_t frames, void* arg)
static void wearedone(int sig) {
fprintf(stderr, "Shutting down\n");
keeprunning = 0;
+ /* main loop might be blocked by data_ready when jack server dies. */
+ if (pthread_mutex_trylock (&msg_thread_lock) == 0) {
+ pthread_cond_signal (&data_ready);
+ pthread_mutex_unlock (&msg_thread_lock);
+ }
}
static void usage (int status) {
diff --git a/tools/wscript b/tools/wscript
index 0fc90289..a6a60f5c 100644
--- a/tools/wscript
+++ b/tools/wscript
@@ -19,8 +19,9 @@ example_tools = {
}
def configure(conf):
- conf.env['BUILD_EXAMPLE_ALSA_IO'] = conf.env['SAMPLERATE'] and conf.env['BUILD_DRIVER_ALSA']
- conf.env['BUILD_EXAMPLE_CLIENT_TRANSPORT'] = conf.env['READLINE']
+ conf.env['BUILD_TOOL_ALSA_IO'] = conf.env['SAMPLERATE'] and conf.env['BUILD_DRIVER_ALSA']
+ conf.env['BUILD_TOOL_CLIENT_TRANSPORT'] = conf.env['READLINE']
+ conf.env['BUILD_TOOL_ZALSA'] = conf.env['ZALSA']
def build(bld):
if bld.env['IS_LINUX']:
@@ -50,7 +51,7 @@ def build(bld):
prog.target = example_tool
- if bld.env['BUILD_EXAMPLE_CLIENT_TRANSPORT']:
+ if bld.env['BUILD_TOOL_CLIENT_TRANSPORT']:
prog = bld(features = 'c cprogram')
prog.includes = os_incdir + ['../common/jack', '../common']
prog.source = 'transport.c'
@@ -74,7 +75,7 @@ def build(bld):
prog.target = 'jack_netsource'
prog.defines = ['HAVE_CONFIG_H']
- if bld.env['IS_LINUX'] and bld.env['BUILD_EXAMPLE_ALSA_IO']:
+ if bld.env['IS_LINUX'] and bld.env['BUILD_TOOL_ALSA_IO']:
prog = bld(features = 'c cprogram')
prog.includes = os_incdir + ['../common/jack', '../common']
prog.source = ['alsa_in.c', '../common/memops.c']
@@ -89,6 +90,25 @@ def build(bld):
prog.use = ['clientlib', 'ALSA', 'SAMPLERATE', 'M']
prog.target = 'alsa_out'
+ if bld.env['IS_LINUX'] and bld.env['BUILD_TOOL_ZALSA']:
+ prog = bld(features = ['cxx', 'cxxshlib'])
+ prog.defines = ['HAVE_CONFIG_H','SERVER_SIDE','APPNAME="zalsa_in"','VERSION="0.4.0"']
+ prog.install_path = '${ADDON_DIR}/'
+ prog.includes = os_incdir + ['../common/jack', '../common', 'zalsa']
+ prog.source = ['zalsa/zita-a2j.cc', 'zalsa/alsathread.cc', 'zalsa/jackclient.cc', 'zalsa/pxthread.cc', 'zalsa/lfqueue.cc']
+ prog.target = 'zalsa_in'
+ prog.use = ['ZITA-ALSA-PCMI', 'ZITA-RESAMPLER', 'ALSA', 'M', 'RT', 'serverlib']
+ prog.env['cxxshlib_PATTERN'] = '%s.so'
+
+ prog = bld(features = ['cxx', 'cxxshlib'])
+ prog.defines = ['HAVE_CONFIG_H','SERVER_SIDE','APPNAME="zalsa_out"','VERSION="0.4.0"']
+ prog.install_path = '${ADDON_DIR}/'
+ prog.includes = os_incdir + ['../common/jack', '../common', 'zalsa']
+ prog.source = ['zalsa/zita-j2a.cc', 'zalsa/alsathread.cc', 'zalsa/jackclient.cc', 'zalsa/pxthread.cc', 'zalsa/lfqueue.cc']
+ prog.target = 'zalsa_out'
+ prog.use = ['ZITA-ALSA-PCMI', 'ZITA-RESAMPLER', 'ALSA', 'M', 'RT', 'serverlib']
+ prog.env['cxxshlib_PATTERN'] = '%s.so'
+
if not bld.env['IS_WINDOWS']:
bld.symlink_as('${PREFIX}/bin/jack_disconnect', 'jack_connect')
bld.install_files('${PREFIX}/bin', 'jack_control', chmod=0o755)
diff --git a/tools/zalsa/alsathread.cc b/tools/zalsa/alsathread.cc
new file mode 100644
index 00000000..1ce5dd93
--- /dev/null
+++ b/tools/zalsa/alsathread.cc
@@ -0,0 +1,218 @@
+// ----------------------------------------------------------------------------
+//
+// Copyright (C) 2012-2018 Fons Adriaensen <fons@linuxaudio.org>
+//
+// 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 <http://www.gnu.org/licenses/>.
+//
+// ----------------------------------------------------------------------------
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include "alsathread.h"
+#include "timers.h"
+
+
+Alsathread::Alsathread (Alsa_pcmi *alsadev, int mode) :
+ _alsadev (alsadev ),
+ _mode (mode),
+ _state (INIT),
+ _fsize (alsadev->fsize ()),
+ _audioq (0),
+ _commq (0),
+ _alsaq (0)
+{
+ // Compute DLL filter coefficients.
+ _dt = (double) _fsize / _alsadev->fsamp ();
+ _w1 = 2 * M_PI * 0.1 * _dt;
+ _w2 = _w1 * _w1;
+ _w1 *= 1.6;
+}
+
+
+Alsathread::~Alsathread (void)
+{
+ _alsadev->pcm_stop ();
+}
+
+
+int Alsathread::start (Lfq_audio *audioq, Lfq_int32 *commq, Lfq_adata *alsaq, int rtprio)
+{
+ // Start the ALSA thread.
+ _audioq = audioq;
+ _commq = commq;
+ _alsaq = alsaq;
+ _state = WAIT;
+ if (thr_start (SCHED_FIFO, rtprio, 0x10000)) return 1;
+ return 0;
+}
+
+
+void Alsathread::send (int k, double t)
+{
+ Adata *D;
+
+ // Send (state, frame count, timestamp) to Jack thread.
+ if (_alsaq->wr_avail ())
+ {
+ D = _alsaq->wr_datap ();
+ D->_state = _state;
+ D->_nsamp = k;
+ D->_timer = t;
+ _alsaq->wr_commit ();
+ }
+}
+
+
+// The following two functions transfer data between the audio queue
+// and the ALSA device. Note that we do *not* check the queue's fill
+// state, and it may overrun or underrun. It actually will in the first
+// few iterations and in error conditions. This is entirely intentional.
+// The queue keeps correct read and write counters even in that case,
+// and the main control loop and error recovery depend on it working
+// and being used in this way.
+
+int Alsathread::capture (void)
+{
+ int c, n, k;
+ float *p;
+
+ // Start reading from ALSA device.
+ _alsadev->capt_init (_fsize);
+ if (_state == PROC)
+ {
+ // Input frames from the ALSA device to the audio queue.
+ // The outer loop takes care of wraparound.
+ for (n = _fsize; n; n -= k)
+ {
+ p = _audioq->wr_datap (); // Audio queue write pointer.
+ k = _audioq->wr_linav (); // Number of frames that can be
+ if (k > n) k = n; // written without wraparound.
+ for (c = 0; c < _audioq->nchan (); c++)
+ {
+ // Copy and interleave one channel.
+ _alsadev->capt_chan (c, p + c, k, _audioq->nchan ());
+ }
+ _audioq->wr_commit (k); // Update audio queue state.
+ }
+ }
+ // Finish reading from ALSA device.
+ _alsadev->capt_done (_fsize);
+ return _fsize;
+}
+
+
+int Alsathread::playback (void)
+{
+ int c, n, k;
+ float *p;
+
+ // Start writing to ALSA device.
+ _alsadev->play_init (_fsize);
+ c = 0;
+ if (_state == PROC)
+ {
+ // Output frames from the audio queue to the ALSA device.
+ // The outer loop takes care of wraparound.
+ for (n = _fsize; n; n -= k)
+ {
+ p = _audioq->rd_datap (); // Audio queue read pointer.
+ k = _audioq->rd_linav (); // Number of frames that can
+ if (k > n) k = n; // be read without wraparound.
+ for (c = 0; c < _audioq->nchan (); c++)
+ {
+ // De-interleave and copy one channel.
+ _alsadev->play_chan (c, p + c, k, _audioq->nchan ());
+ }
+ _audioq->rd_commit (k); // Update audio queue state.
+ }
+ }
+ // Clear all or remaining channels.
+ while (c < _alsadev->nplay ()) _alsadev->clear_chan (c++, _fsize);
+ // Finish writing to ALSA device.
+ _alsadev->play_done (_fsize);
+ return _fsize;
+}
+
+
+void Alsathread::thr_main (void)
+{
+ int na, nu;
+ double tw, er;
+
+ _alsadev->pcm_start ();
+ while (_state != TERM)
+ {
+ // Wait for next cycle, then take timestamp.
+ na = _alsadev->pcm_wait ();
+
+ tw = tjack (jack_get_time ());
+ // Check for errors - requires restart.
+ if (_alsadev->state () && (na == 0))
+ {
+ _state = WAIT;
+ send (0, 0);
+ usleep (10000);
+ continue;
+ }
+
+ // Check for commands from the Jack thread.
+ if (_commq->rd_avail ())
+ {
+ _state = _commq->rd_int32 ();
+ if (_state == PROC) _first = true;
+ if (_state == TERM) send (0, 0);
+ }
+
+ // We could have more than one period.
+ nu = 0;
+ while (na >= _fsize)
+ {
+ // Transfer frames.
+ if (_mode == PLAY) nu += playback ();
+ else nu += capture ();
+ // Update loop condition.
+ na -= _fsize;
+ // Run the DLL if in PROC state.
+ if (_state == PROC)
+ {
+ if (_first)
+ {
+ // Init DLL in first iteration.
+ _first = false;
+ _dt = (double) _fsize / _alsadev->fsamp ();
+ _t0 = tw;
+ _t1 = tw + _dt;
+ }
+ else
+ {
+ // Update the DLL.
+ // If we have more than one period, use
+ // the time error only for the last one.
+ if (na >= _fsize) er = 0;
+ else er = tjack_diff (tw, _t1);
+ _t0 = _t1;
+ _t1 = tjack_diff (_t1 + _dt + _w1 * er, 0.0);
+ _dt += _w2 * er;
+ }
+ }
+ }
+
+ // Send number of frames used and timestamp to Jack thread.
+ if (_state == PROC) send (nu, _t1);
+ }
+ _alsadev->pcm_stop ();
+}
diff --git a/tools/zalsa/alsathread.h b/tools/zalsa/alsathread.h
new file mode 100644
index 00000000..f9cb761c
--- /dev/null
+++ b/tools/zalsa/alsathread.h
@@ -0,0 +1,68 @@
+// ----------------------------------------------------------------------------
+//
+// Copyright (C) 2012-2018 Fons Adriaensen <fons@linuxaudio.org>
+//
+// 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 <http://www.gnu.org/licenses/>.
+//
+// ----------------------------------------------------------------------------
+
+
+#ifndef __ALSATHREAD_H
+#define __ALSATHREAD_H
+
+
+#include <zita-alsa-pcmi.h>
+#include "jack/jack.h"
+#include "pxthread.h"
+#include "lfqueue.h"
+
+
+class Alsathread : public Pxthread
+{
+public:
+
+ enum { INIT, WAIT, PROC, TERM };
+ enum { PLAY, CAPT };
+
+ Alsathread (Alsa_pcmi *alsadev, int mode);
+ virtual ~Alsathread (void);
+ virtual void thr_main (void);
+
+ int start (Lfq_audio *audioq, Lfq_int32 *commq, Lfq_adata *alsaq, int rtprio);
+
+private:
+
+ void send (int k, double t);
+ int capture (void);
+ int playback (void);
+
+ Alsa_pcmi *_alsadev;
+ int _mode;
+ int _state;
+ int _nfail;
+ int _fsize;
+ Lfq_audio *_audioq;
+ Lfq_int32 *_commq;
+ Lfq_adata *_alsaq;
+ bool _first;
+// double _jtmod;
+ double _t0;
+ double _t1;
+ double _dt;
+ double _w1;
+ double _w2;
+};
+
+
+#endif
diff --git a/tools/zalsa/jackclient.cc b/tools/zalsa/jackclient.cc
new file mode 100644
index 00000000..11f93c16
--- /dev/null
+++ b/tools/zalsa/jackclient.cc
@@ -0,0 +1,549 @@
+// ----------------------------------------------------------------------------
+//
+// Copyright (C) 2012-2018 Fons Adriaensen <fons@linuxaudio.org>
+//
+// 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 <http://www.gnu.org/licenses/>.
+//
+// ----------------------------------------------------------------------------
+
+
+#include <stdio.h>
+#include <math.h>
+#include "jackclient.h"
+#include "alsathread.h"
+#include "timers.h"
+
+
+Jackclient::Jackclient (jack_client_t* cl, const char *jserv, int mode, int nchan, bool sync, void *arg) :
+ _client (cl),
+ _arg (arg),
+ _mode (mode),
+ _nchan (nchan),
+ _state (INIT),
+ _freew (false),
+ _resamp (0)
+{
+ init (jserv);
+ if (!sync) _resamp = new VResampler ();
+}
+
+
+Jackclient::~Jackclient (void)
+{
+ fini ();
+}
+
+
+bool Jackclient::init (const char *jserv)
+{
+ int i, spol, flags;
+ char s [64];
+ struct sched_param spar;
+
+ if (_client == 0)
+ {
+ fprintf (stderr, "Can't connect to Jack, is the server running ?\n");
+ return false;
+ }
+ 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;
+ if (jack_activate (_client))
+ {
+ fprintf(stderr, "Can't activate Jack");
+ return false;
+ }
+ _jname = jack_get_client_name (_client);
+ _bsize = jack_get_buffer_size (_client);
+ _fsamp = jack_get_sample_rate (_client);
+
+ flags = JackPortIsTerminal | JackPortIsPhysical;
+ for (i = 0; i < _nchan; 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);
+ }
+ }
+ pthread_getschedparam (jack_client_thread_id (_client), &spol, &spar);
+ _rprio = spar.sched_priority - sched_get_priority_max (spol);
+ _buff = new float [_bsize * _nchan];
+ return true;
+}
+
+
+void Jackclient::fini (void)
+{
+ delete[] _buff;
+ delete _resamp;
+}
+
+
+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;
+ _ratio = ratio;
+ _delay = delay;
+ _rcorr = 1.0;
+ if (_resamp)
+ {
+ _resamp->setup (_ratio, _nchan, rqual);
+ _resamp->set_rrfilt (100);
+ d = _resamp->inpsize () / 2.0;
+ if (_mode == PLAY) d *= _ratio;
+ _delay += d;
+ }
+ _ltcor = ltcor;
+ _ppsec = (_fsamp + _bsize / 2) / _bsize;
+ 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 ();
+ if (_resamp)
+ {
+ // Reset and prefill the resampler.
+ _resamp->reset ();
+ _resamp->inp_count = _resamp->inpsize () / 2 - 1;
+ _resamp->out_count = 99999;
+ _resamp->process ();
+ }
+ // Initiliase state variables.
+ _t_a0 = _t_a1 = 0;
+ _k_a0 = _k_a1 = 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.28 * bw * _bsize / _fsamp;
+ _w0 = 1.0 - exp (-20.0 * w);
+ _w1 = w * 2 / _bsize;
+ _w2 = w / 2;
+ if (_mode == PLAY) _w1 /= _ratio;
+ else _w1 *= _ratio;
+}
+
+
+void Jackclient::playback (int nframes)
+{
+ int i, j, n;
+ float *p, *q;
+ float *inp [MAXCHAN];
+
+ _bstat = _audioq->rd_avail ();
+ for (i = 0; i < _nchan; i++)
+ {
+ inp [i] = (float *)(jack_port_get_buffer (_ports [i], nframes));
+ }
+ if (_resamp)
+ {
+ // Interleave inputs into _buff.
+ for (i = 0; i < _nchan; i++)
+ {
+ p = inp [i];
+ 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);
+ }
+ }
+ else
+ {
+ // Interleave inputs into audio queue.
+ // The while loop takes care of wraparound.
+ while (nframes)
+ {
+ q = _audioq->wr_datap ();
+ n = _audioq->wr_linav ();
+ if (n > nframes) n = nframes;
+ for (i = 0; i < _nchan; i++)
+ {
+ p = inp [i];
+ for (j = 0; j < n; j++) q [j * _nchan] = p [j];
+ inp [i] += n;
+ q += 1;
+ }
+ _audioq->wr_commit (n);
+ nframes -= n;
+ }
+ }
+}
+
+
+void Jackclient::capture (int nframes)
+{
+ int i, j, n;
+ float *p, *q;
+ float *out [MAXCHAN];
+
+ for (i = 0; i < _nchan; i++)
+ {
+ out [i] = (float *)(jack_port_get_buffer (_ports [i], nframes));
+ }
+ if (_resamp)
+ {
+ // 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);
+ }
+ // Deinterleave _buff to outputs.
+ for (i = 0; i < _nchan; i++)
+ {
+ p = _buff + i;
+ q = out [i];
+ for (j = 0; j < _bsize; j++) q [j] = p [j * _nchan];
+ }
+ }
+ else
+ {
+ // Deinterleave audio queue to outputs.
+ // The while loop takes care of wraparound.
+ while (nframes)
+ {
+ p = _audioq->rd_datap ();
+ n = _audioq->rd_linav ();
+ if (n > nframes) n = nframes;
+ for (i = 0; i < _nchan; i++)
+ {
+ q = out [i];
+ for (j = 0; j < n; j++) q [j] = p [j * _nchan];
+ out [i] += n;
+ p += 1;
+ }
+ _audioq->rd_commit (n);
+ nframes -= n;
+ }
+ }
+ _bstat = _audioq->rd_avail ();
+}
+
+
+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;
+ J->_bstat = _bstat;
+ _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_time_t t0, t1;
+ jack_nframes_t ft;
+ float us;
+ double tj, err, d1, d2, rd;
+
+ // 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;
+ }
+
+ // Get the start time of the current cycle.
+ jack_get_cycle_times (_client, &ft, &t0, &t1, &us);
+ tj = tjack (t0);
+
+ // Check for any skipped cycles.
+ if (_state >= SYNC1)
+ {
+ dk = ft - _ft - _bsize;
+ if (_mode == PLAY)
+ {
+ dk = (int)(dk * _ratio + 0.5);
+ _audioq->wr_commit (dk);
+ }
+ else
+ {
+ dk = (int)(dk / _ratio + 0.5);
+ _audioq->rd_commit (dk);
+ }
+ }
+ _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 = tjack_diff (tj, _t_a0);
+ d2 = tjack_diff (_t_a1, _t_a0);
+ rd = _resamp ? _resamp->inpdist () : 0.0;
+
+ if (_mode == PLAY)
+ {
+ n = _audioq->nwr () - _k_a0; // Must be done as integer as both terms will overflow.
+ err = n - (_k_a1 - _k_a0) * d1 / d2 + rd * _ratio - _delay;
+ }
+ else
+ {
+ n = _k_a0 - _audioq->nrd (); // Must be done as integer as both terms will overflow.
+ err = n + (_k_a1 - _k_a0) * d1 / d2 + rd - _delay ;
+ }
+ n = (int)(floor (err + 0.5));
+ if (_state == SYNC2)
+ {
+ // We have the first delay error value. Adjust the audio
+ // queue to obtain the actually wanted delay, and start
+ // tracking.
+ if (_mode == PLAY) _audioq->wr_commit (-n);
+ else _audioq->rd_commit (n);
+ err -= n;
+ setloop (1.0);
+ _state = PROC1;
+ }
+ }
+
+ // Switch to lower bandwidth after 4 seconds.
+ if ((_state == PROC1) && (++_count == 4 * _ppsec))
+ {
+ _state = PROC2;
+ setloop (0.05);
+ }
+
+ if (_state >= PROC1)
+ {
+ _z1 += _w0 * (_w1 * err - _z1);
+ _z2 += _w0 * (_z1 - _z2);
+ _z3 += _w2 * _z2;
+ // Check error conditions.
+ if (fabs (_z3) > 0.05)
+ {
+ // Something is really wrong, wait 10 seconds then restart.
+ initwait (10 * _ppsec);
+ return 0;
+ }
+ // Run loop filter and set resample ratio.
+ if (_resamp)
+ {
+ _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;
+}
diff --git a/tools/zalsa/jackclient.h b/tools/zalsa/jackclient.h
new file mode 100644
index 00000000..52f3ddfd
--- /dev/null
+++ b/tools/zalsa/jackclient.h
@@ -0,0 +1,120 @@
+// ----------------------------------------------------------------------------
+//
+// Copyright (C) 2012-2018 Fons Adriaensen <fons@linuxaudio.org>
+//
+// 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 <http://www.gnu.org/licenses/>.
+//
+// ----------------------------------------------------------------------------
+
+
+#ifndef __JACKCLIENT_H
+#define __JACKCLIENT_H
+
+
+#include <zita-resampler/vresampler.h>
+#include "jack/jack.h"
+#include "lfqueue.h"
+
+
+class Jackclient
+{
+public:
+
+ Jackclient (jack_client_t*, const char *jserv, int mode, int nchan, bool sync, void *arg);
+ virtual ~Jackclient (void);
+
+ enum { PLAY, CAPT, MAXCHAN = 64 };
+ enum { INIT, TERM, WAIT, SYNC0, SYNC1, SYNC2, PROC1, PROC2 };
+
+ void start (Lfq_audio *audioq,
+ Lfq_int32 *commq,
+ Lfq_adata *alsaq,
+ Lfq_jdata *infoq,
+ double ratio,
+ int delay,
+ int ltcor,
+ int rqual);
+
+ const char *jname (void) const { return _jname; }
+ int fsamp (void) const { return _fsamp; }
+ int bsize (void) const { return _bsize; }
+ int rprio (void) const { return _rprio; }
+ void *getarg(void) const { return _arg; }
+
+private:
+
+ bool init (const char *jserv);
+ void fini (void);
+ void initwait (int nwait);
+ void initsync (void);
+ void setloop (double bw);
+ void silence (int nframes);
+ void playback (int nframes);
+ void capture (int nframes);
+ void sendinfo (int state, double error, double ratio);
+
+ virtual void thr_main (void) {}
+
+ void jack_freewheel (int state);
+ void jack_latency (jack_latency_callback_mode_t jlcm);
+ int jack_process (int nframes);
+
+ jack_client_t *_client;
+ jack_port_t *_ports [MAXCHAN];
+ void *_arg;
+ const char *_jname;
+ int _mode;
+ int _nchan;
+ int _state;
+ int _count;
+ int _fsamp;
+ int _bsize;
+ int _rprio;
+ bool _freew;
+ float *_buff;
+
+ Lfq_audio *_audioq;
+ Lfq_int32 *_commq;
+ Lfq_adata *_alsaq;
+ Lfq_jdata *_infoq;
+ double _ratio;
+ int _ppsec;
+ int _bstat;
+
+ jack_nframes_t _ft;
+ double _t_a0;
+ double _t_a1;
+ int _k_a0;
+ int _k_a1;
+ double _delay;
+ int _ltcor;
+
+ double _w0;
+ double _w1;
+ double _w2;
+ double _z1;
+ double _z2;
+ double _z3;
+ double _rcorr;
+ VResampler *_resamp;
+
+ static void jack_static_shutdown (void *arg);
+ static int jack_static_buffsize (jack_nframes_t nframes, void *arg);
+ static void jack_static_freewheel (int state, void *arg);
+ static void jack_static_latency (jack_latency_callback_mode_t jlcm, void *arg);
+ static int jack_static_process (jack_nframes_t nframes, void *arg);
+};
+
+
+#endif
diff --git a/tools/zalsa/lfqueue.cc b/tools/zalsa/lfqueue.cc
new file mode 100644
index 00000000..f66d07e3
--- /dev/null
+++ b/tools/zalsa/lfqueue.cc
@@ -0,0 +1,89 @@
+// ----------------------------------------------------------------------------
+//
+// Copyright (C) 2012 Fons Adriaensen <fons@linuxaudio.org>
+//
+// 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 <http://www.gnu.org/licenses/>.
+//
+// ----------------------------------------------------------------------------
+
+
+#include <assert.h>
+#include "lfqueue.h"
+
+
+Lfq_adata::Lfq_adata (int size) :
+ _size (size),
+ _mask (size - 1),
+ _nwr (0),
+ _nrd (0)
+{
+ assert (!(_size & _mask));
+ _data = new Adata [_size];
+}
+
+Lfq_adata::~Lfq_adata (void)
+{
+ delete[] _data;
+}
+
+
+Lfq_jdata::Lfq_jdata (int size) :
+ _size (size),
+ _mask (size - 1),
+ _nwr (0),
+ _nrd (0)
+{
+ assert (!(_size & _mask));
+ _data = new Jdata [_size];
+}
+
+Lfq_jdata::~Lfq_jdata (void)
+{
+ delete[] _data;
+}
+
+
+Lfq_int32::Lfq_int32 (int size) :
+ _size (size),
+ _mask (size - 1),
+ _nwr (0),
+ _nrd (0)
+{
+ assert (!(_size & _mask));
+ _data = new int32_t [_size];
+}
+
+Lfq_int32::~Lfq_int32 (void)
+{
+ delete[] _data;
+}
+
+
+Lfq_audio::Lfq_audio (int nsamp, int nchan) :
+ _size (nsamp),
+ _mask (nsamp - 1),
+ _nch (nchan),
+ _nwr (0),
+ _nrd (0)
+{
+ assert (!(_size & _mask));
+ _data = new float [_nch * _size];
+}
+
+Lfq_audio::~Lfq_audio (void)
+{
+ delete[] _data;
+}
+
+
diff --git a/tools/zalsa/lfqueue.h b/tools/zalsa/lfqueue.h
new file mode 100644
index 00000000..3943d401
--- /dev/null
+++ b/tools/zalsa/lfqueue.h
@@ -0,0 +1,182 @@
+// ----------------------------------------------------------------------------
+//
+// Copyright (C) 2012 Fons Adriaensen <fons@linuxaudio.org>
+//
+// 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 <http://www.gnu.org/licenses/>.
+//
+// ----------------------------------------------------------------------------
+
+
+#ifndef __LFQUEUE_H
+#define __LFQUEUE_H
+
+
+#include <stdint.h>
+#include <string.h>
+
+
+class Adata
+{
+public:
+
+ int32_t _state;
+ int32_t _nsamp;
+ double _timer;
+};
+
+
+class Lfq_adata
+{
+public:
+
+ Lfq_adata (int size);
+ ~Lfq_adata (void);
+
+ void reset (void) { _nwr = _nrd = 0; }
+ int size (void) const { return _size; }
+
+ int wr_avail (void) const { return _size - _nwr + _nrd; }
+ Adata *wr_datap (void) { return _data + (_nwr & _mask); }
+ void wr_commit (void) { _nwr++; }
+
+ int rd_avail (void) const { return _nwr - _nrd; }
+ Adata *rd_datap (void) { return _data + (_nrd & _mask); }
+ void rd_commit (void) { _nrd++; }
+
+private:
+
+ Adata *_data;
+ int _size;
+ int _mask;
+ int _nwr;
+ int _nrd;
+};
+
+
+class Jdata
+{
+public:
+
+ int32_t _state;
+ double _error;
+ double _ratio;
+ int _bstat;
+};
+
+
+class Lfq_jdata
+{
+public:
+
+ Lfq_jdata (int size);
+ ~Lfq_jdata (void);
+
+ void reset (void) { _nwr = _nrd = 0; }
+ int size (void) const { return _size; }
+
+ int wr_avail (void) const { return _size - _nwr + _nrd; }
+ Jdata *wr_datap (void) { return _data + (_nwr & _mask); }
+ void wr_commit (void) { _nwr++; }
+
+ int rd_avail (void) const { return _nwr - _nrd; }
+ Jdata *rd_datap (void) { return _data + (_nrd & _mask); }
+ void rd_commit (void) { _nrd++; }
+
+private:
+
+ Jdata *_data;
+ int _size;
+ int _mask;
+ int _nwr;
+ int _nrd;
+};
+
+
+class Lfq_int32
+{
+public:
+
+ Lfq_int32 (int size);
+ ~Lfq_int32 (void);
+
+ int size (void) const { return _size; }
+ void reset (void) { _nwr = _nrd = 0; }
+
+ int wr_avail (void) const { return _size - _nwr + _nrd; }
+ int32_t *wr_datap (void) { return _data + (_nwr & _mask); }
+ void wr_commit (void) { _nwr++; }
+
+ int rd_avail (void) const { return _nwr - _nrd; }
+ int32_t *rd_datap (void) { return _data + (_nrd & _mask); }
+ void rd_commit (void) { _nrd++; }
+
+ void wr_int32 (int32_t v) { _data [_nwr++ & _mask] = v; }
+ void wr_uint32 (uint32_t v) { _data [_nwr++ & _mask] = v; }
+ void wr_float (float v) { *(float *)(_data + (_nwr++ & _mask)) = v; }
+
+ int32_t rd_int32 (void) { return _data [_nrd++ & _mask]; }
+ int32_t rd_uint32 (void) { return _data [_nrd++ & _mask]; }
+ float rd_float (void) { return *(float *)(_data + (_nrd++ & _mask)); }
+
+private:
+
+ int32_t *_data;
+ int _size;
+ int _mask;
+ int _nwr;
+ int _nrd;
+};
+
+
+class Lfq_audio
+{
+public:
+
+ Lfq_audio (int nsamp, int nchan);
+ ~Lfq_audio (void);
+
+ int size (void) const { return _size; }
+ void reset (void)
+ {
+ _nwr = _nrd = 0;
+ memset (_data, 0, _size * _nch * sizeof (float));
+ }
+
+ int nchan (void) const { return _nch; }
+ int nwr (void) const { return _nwr; };
+ int nrd (void) const { return _nrd; };
+
+ int wr_avail (void) const { return _size - _nwr + _nrd; }
+ int wr_linav (void) const { return _size - (_nwr & _mask); }
+ float *wr_datap (void) { return _data + _nch * (_nwr & _mask); }
+ void wr_commit (int k) { _nwr += k; }
+
+ int rd_avail (void) const { return _nwr - _nrd; }
+ int rd_linav (void) const { return _size - (_nrd & _mask); }
+ float *rd_datap (void) { return _data + _nch * (_nrd & _mask); }
+ void rd_commit (int k) { _nrd += k; }
+
+private:
+
+ float *_data;
+ int _size;
+ int _mask;
+ int _nch;
+ int _nwr;
+ int _nrd;
+};
+
+
+#endif
+
diff --git a/tools/zalsa/pxthread.cc b/tools/zalsa/pxthread.cc
new file mode 100644
index 00000000..e3e88499
--- /dev/null
+++ b/tools/zalsa/pxthread.cc
@@ -0,0 +1,78 @@
+// ----------------------------------------------------------------------------
+//
+// Copyright (C) 2012 Fons Adriaensen <fons@linuxaudio.org>
+//
+// 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 <http://www.gnu.org/licenses/>.
+//
+// ----------------------------------------------------------------------------
+
+
+#include "pxthread.h"
+
+
+Pxthread::Pxthread (void) : _thrid (0)
+{
+}
+
+
+Pxthread::~Pxthread (void)
+{
+}
+
+
+extern "C" void *Pxthread_entry_point (void *arg)
+{
+ Pxthread *T = (Pxthread *) arg;
+ T->thr_main ();
+ return NULL;
+}
+
+
+int Pxthread::thr_start (int policy, int priority, size_t stacksize)
+{
+ int min, max, rc;
+ pthread_attr_t attr;
+ struct sched_param parm;
+
+ min = sched_get_priority_min (policy);
+ max = sched_get_priority_max (policy);
+ priority += max;
+ if (priority > max) priority = max;
+ if (priority < min) priority = min;
+ parm.sched_priority = priority;
+
+ pthread_attr_init (&attr);
+ pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
+ pthread_attr_setschedpolicy (&attr, policy);
+ pthread_attr_setschedparam (&attr, &parm);
+ pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM);
+ pthread_attr_setinheritsched (&attr, PTHREAD_EXPLICIT_SCHED);
+ pthread_attr_setstacksize (&attr, stacksize);
+
+ _thrid = 0;
+ rc = pthread_create (&_thrid,
+ &attr,
+ Pxthread_entry_point,
+ this);
+
+ pthread_attr_destroy (&attr);
+
+ return rc;
+}
+
+
+void Pxthread::thr_main (void)
+{
+}
+
diff --git a/tools/zalsa/pxthread.h b/tools/zalsa/pxthread.h
new file mode 100644
index 00000000..32d115fb
--- /dev/null
+++ b/tools/zalsa/pxthread.h
@@ -0,0 +1,52 @@
+// ----------------------------------------------------------------------------
+//
+// Copyright (C) 2012 Fons Adriaensen <fons@linuxaudio.org>
+//
+// 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 <http://www.gnu.org/licenses/>.
+//
+// ----------------------------------------------------------------------------
+
+
+#ifndef __PXTHREAD_H
+#define __PXTHREAD_H
+
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <errno.h>
+#include <pthread.h>
+
+
+class Pxthread
+{
+public:
+
+ Pxthread (void);
+ virtual ~Pxthread (void);
+ Pxthread (const Pxthread&);
+ Pxthread& operator=(const Pxthread&);
+
+ virtual void thr_main (void) = 0;
+ virtual int thr_start (int policy, int priority, size_t stacksize = 0);
+
+private:
+
+ pthread_t _thrid;
+};
+
+
+#endif
diff --git a/tools/zalsa/timers.h b/tools/zalsa/timers.h
new file mode 100644
index 00000000..267afa61
--- /dev/null
+++ b/tools/zalsa/timers.h
@@ -0,0 +1,53 @@
+// ----------------------------------------------------------------------------
+//
+// Copyright (C) 2012-2018 Fons Adriaensen <fons@linuxaudio.org>
+//
+// 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 <http://www.gnu.org/licenses/>.
+//
+// ----------------------------------------------------------------------------
+
+
+#ifndef __TIMERS_H
+#define __TIMERS_H
+
+
+#include <math.h>
+#include <sys/time.h>
+#include <jack/jack.h>
+
+
+#define tjack_mod ldexp (1e-6f, 32)
+
+
+
+inline double tjack_diff (double a, double b)
+{
+ double d, m;
+
+ d = a - b;
+ m = tjack_mod;
+ while (d < -m / 2) d += m;
+ while (d >= m / 2) d -= m;
+ return d;
+}
+
+
+inline double tjack (jack_time_t t, double dt = 0)
+{
+ int32_t u = (int32_t)(t & 0xFFFFFFFFLL);
+ return 1e-6 * u;
+}
+
+
+#endif
diff --git a/tools/zalsa/zita-a2j.cc b/tools/zalsa/zita-a2j.cc
new file mode 100644
index 00000000..fb6bae0e
--- /dev/null
+++ b/tools/zalsa/zita-a2j.cc
@@ -0,0 +1,333 @@
+// ----------------------------------------------------------------------------
+//
+// Copyright (C) 2012 Fons Adriaensen <fons@linuxaudio.org>
+//
+// 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 <http://www.gnu.org/licenses/>.
+//
+// ----------------------------------------------------------------------------
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <signal.h>
+#include "alsathread.h"
+#include "jackclient.h"
+#include "lfqueue.h"
+#include "jack/control.h"
+
+static const char *clopt = "hvLSj:d:r:p:n:c:Q:I:";
+
+static void help (void)
+{
+ jack_info ("%s-%s", APPNAME, VERSION);
+ jack_info ("(C) 2012-2018 Fons Adriaensen <fons@linuxaudio.org>");
+ jack_info ("Use ALSA capture device as a Jack client.");
+ jack_info ("Options:");
+ jack_info (" -h Display this text");
+ jack_info (" -j <jackname> Name as Jack client [%s]", APPNAME);
+ jack_info (" -d <device> ALSA capture device [none]");
+ jack_info (" -r <rate> Sample rate [48000]");
+ jack_info (" -p <period> Period size [256]");
+ jack_info (" -n <nfrags> Number of fragments [2]");
+ jack_info (" -c <nchannels> Number of channels [2]");
+ jack_info (" -S Word clock sync, no resampling");
+ jack_info (" -Q <quality> Resampling quality, 16..96 [auto]");
+ jack_info (" -I <samples> Latency adjustment [0]");
+ jack_info (" -L Force 16-bit and 2 channels [off]");
+ jack_info (" -v Print tracing information [off]");
+}
+
+class zita_a2j
+{
+ Lfq_int32 *commq;
+ Lfq_adata *alsaq;
+ Lfq_jdata *infoq;
+ Lfq_audio *audioq;
+ bool stop;
+ bool v_opt;
+ bool L_opt;
+ bool S_opt;
+ char *jname;
+ char *device;
+ int fsamp;
+ int bsize;
+ int nfrag;
+ int nchan;
+ int rqual;
+ int ltcor;
+
+public:
+
+ zita_a2j()
+ {
+ commq = new Lfq_int32(16);
+ alsaq = new Lfq_adata(256);
+ infoq = new Lfq_jdata(256);
+ audioq = 0;
+ stop = false;
+ v_opt = false;
+ L_opt = false;
+ S_opt = false;
+ jname = strdup(APPNAME);
+ device = 0;
+ fsamp = 48000;
+ bsize = 128;
+ nfrag = 2;
+ nchan = 2;
+ rqual = 0;
+ ltcor = 0;
+ A = 0;
+ C = 0;
+ J = 0;
+ }
+
+private:
+
+ int procoptions (int ac, const char *av [])
+ {
+ int k;
+
+ optind = 1;
+ opterr = 0;
+ while ((k = getopt (ac, (char **) av, (char *) clopt)) != -1)
+ {
+ if (optarg && (*optarg == '-'))
+ {
+ jack_error (APPNAME ": Missing argument for '-%c' option.", k);
+ jack_error (APPNAME ": Use '-h' to see all options.");
+ return 1;
+ }
+ switch (k)
+ {
+ case 'h' : help (); return 1;
+ case 'v' : v_opt = true; break;
+ case 'L' : L_opt = true; break;
+ case 'S' : S_opt = true; break;
+ case 'j' : jname = optarg; break;
+ case 'd' : device = optarg; break;
+ case 'r' : fsamp = atoi (optarg); break;
+ case 'p' : bsize = atoi (optarg); break;
+ case 'n' : nfrag = atoi (optarg); break;
+ case 'c' : nchan = atoi (optarg); break;
+ case 'Q' : rqual = atoi (optarg); break;
+ case 'I' : ltcor = atoi (optarg); break;
+ case '?':
+ if (optopt != ':' && strchr (clopt, optopt))
+ {
+ jack_error (APPNAME ": Missing argument for '-%c' option.", optopt);
+ }
+ else if (isprint (optopt))
+ {
+ jack_error (APPNAME ": Unknown option '-%c'.", optopt);
+ }
+ else
+ {
+ jack_error (APPNAME ": Unknown option character '0x%02x'.", optopt & 255);
+ }
+ jack_error (APPNAME ": Use '-h' to see all options.");
+ return 1;
+ default:
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ int parse_options (const char* load_init)
+ {
+ int argsz;
+ int argc = 0;
+ const char** argv;
+ char* args = strdup (load_init);
+ char* token;
+ char* ptr = args;
+ char* savep;
+
+ if (!load_init) {
+ return 0;
+ }
+
+ argsz = 8; /* random guess at "maxargs" */
+ argv = (const char **) malloc (sizeof (char *) * argsz);
+
+ argv[argc++] = APPNAME;
+
+ while (1) {
+
+ if ((token = strtok_r (ptr, " ", &savep)) == NULL) {
+ break;
+ }
+
+ if (argc == argsz) {
+ argsz *= 2;
+ argv = (const char **) realloc (argv, sizeof (char *) * argsz);
+ }
+
+ argv[argc++] = token;
+ ptr = NULL;
+ }
+
+ return procoptions (argc, argv);
+ }
+
+ void printinfo (void)
+ {
+ int n, k;
+ double e, r;
+ Jdata *J;
+
+ n = 0;
+ k = 99999;
+ e = r = 0;
+ while (infoq->rd_avail ())
+ {
+ J = infoq->rd_datap ();
+ if (J->_state == Jackclient::TERM)
+ {
+ jack_error (APPNAME ": Fatal error condition, terminating.");
+ stop = true;
+ return;
+ }
+ else if (J->_state == Jackclient::WAIT)
+ {
+ jack_info (APPNAME ": Detected excessive timing errors, waiting 10 seconds.");
+ n = 0;
+ }
+ else if (J->_state == Jackclient::SYNC0)
+ {
+ jack_info (APPNAME ": Starting synchronisation.");
+ }
+ else if (v_opt)
+ {
+ n++;
+ e += J->_error;
+ r += J->_ratio;
+ if (J->_bstat < k) k = J->_bstat;
+ }
+ infoq->rd_commit ();
+ }
+ if (n) jack_info (APPNAME ": %8.3lf %10.6lf %5d", e / n, r / n, k);
+ }
+
+
+ Alsa_pcmi *A;
+ Alsathread *C;
+ Jackclient *J;
+
+public:
+
+ int
+ jack_initialize (jack_client_t* client, const char* load_init)
+ {
+ int k, k_del, opts;
+ double t_jack;
+ double t_alsa;
+ double t_del;
+
+ if (parse_options (load_init)) {
+ jack_error (APPNAME ": parse options failed");
+ delete this;
+ return 1;
+ }
+
+ if (device == 0)
+ {
+ help ();
+ delete this;
+ return 1;
+ }
+ if (rqual < 16) rqual = 16;
+ if (rqual > 96) rqual = 96;
+ if ((fsamp < 8000) || (bsize < 16) || (nfrag < 2) || (nchan < 1))
+ {
+ jack_error (APPNAME ": Illegal parameter value(s).");
+ delete this;
+ return 1;
+ }
+
+ opts = 0;
+ if (v_opt) opts |= Alsa_pcmi::DEBUG_ALL;
+ if (L_opt) opts |= Alsa_pcmi::FORCE_16B | Alsa_pcmi::FORCE_2CH;
+ A = new Alsa_pcmi (0, device, 0, fsamp, bsize, nfrag, opts);
+ if (A->state ())
+ {
+ jack_error (APPNAME ": Can't open ALSA capture device '%s'.", device);
+ delete this;
+ return 1;
+ }
+ if (v_opt) A->printinfo ();
+ if (nchan > A->ncapt ())
+ {
+ nchan = A->ncapt ();
+ jack_error (APPNAME ": Warning: only %d channels are available.", nchan);
+ }
+ C = new Alsathread (A, Alsathread::CAPT);
+ J = new Jackclient (client, 0, Jackclient::CAPT, nchan, S_opt, this);
+ usleep (100000);
+
+ t_alsa = (double) bsize / fsamp;
+ if (t_alsa < 1e-3) t_alsa = 1e-3;
+ t_jack = (double) J->bsize () / J->fsamp ();
+ t_del = t_alsa + t_jack;
+ k_del = (int)(t_del * fsamp);
+ for (k = 256; k < 2 * k_del; k *= 2);
+ audioq = new Lfq_audio (k, nchan);
+
+ if (rqual == 0)
+ {
+ k = (fsamp < J->fsamp ()) ? fsamp : J->fsamp ();
+ if (k < 44100) k = 44100;
+ rqual = (int)((6.7 * k) / (k - 38000));
+ }
+ if (rqual < 16) rqual = 16;
+ if (rqual > 96) rqual = 96;
+
+ C->start (audioq, commq, alsaq, J->rprio () + 10);
+ J->start (audioq, commq, alsaq, infoq, J->fsamp () / (double) fsamp, k_del, ltcor, rqual);
+
+ return 0;
+ }
+
+ void jack_finish (void* arg)
+ {
+ commq->wr_int32 (Alsathread::TERM);
+ usleep (100000);
+ delete C;
+ delete A;
+ delete J;
+ delete audioq;
+ }
+};
+
+extern "C" {
+
+int
+jack_initialize (jack_client_t* client, const char* load_init)
+{
+ zita_a2j *c = new zita_a2j();
+ return c->jack_initialize(client, load_init);
+}
+
+void jack_finish (void* arg)
+{
+ if (!arg) return;
+ Jackclient *J = (Jackclient *)arg;
+ zita_a2j *c = (zita_a2j *)J->getarg();
+ c->jack_finish(arg);
+ delete c;
+}
+
+} /* extern "C" */
diff --git a/tools/zalsa/zita-j2a.cc b/tools/zalsa/zita-j2a.cc
new file mode 100644
index 00000000..3f20247f
--- /dev/null
+++ b/tools/zalsa/zita-j2a.cc
@@ -0,0 +1,331 @@
+// ----------------------------------------------------------------------------
+//
+// Copyright (C) 2012 Fons Adriaensen <fons@linuxaudio.org>
+//
+// 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 <http://www.gnu.org/licenses/>.
+//
+// ----------------------------------------------------------------------------
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <signal.h>
+#include "alsathread.h"
+#include "jackclient.h"
+#include "lfqueue.h"
+#include "jack/control.h"
+
+static const char *clopt = "hvLSj:d:r:p:n:c:Q:O:";
+
+static void help (void)
+{
+ jack_info ("%s-%s", APPNAME, VERSION);
+ jack_info ("(C) 2012-2018 Fons Adriaensen <fons@linuxaudio.org>");
+ jack_info ("Use ALSA playback device as a Jack client.");
+ jack_info ("Options:");
+ jack_info (" -h Display this text");
+ jack_info (" -j <jackname> Name as Jack client [%s]", APPNAME);
+ jack_info (" -d <device> ALSA playback device [none]");
+ jack_info (" -r <rate> Sample rate [48000]");
+ jack_info (" -p <period> Period size [256]");
+ jack_info (" -n <nfrags> Number of fragments [2]");
+ jack_info (" -c <nchannels> Number of channels [2]");
+ jack_info (" -S Word clock sync, no resampling");
+ jack_info (" -Q <quality> Resampling quality, 16..96 [auto]");
+ jack_info (" -O <samples> Latency adjustment [0]");
+ jack_info (" -L Force 16-bit and 2 channels [off]");
+ jack_info (" -v Print tracing information [off]");
+}
+
+class zita_j2a
+{
+ Lfq_int32 *commq;
+ Lfq_adata *alsaq;
+ Lfq_jdata *infoq;
+ Lfq_audio *audioq;
+ bool stop;
+ bool v_opt;
+ bool L_opt;
+ bool S_opt;
+ char *jname;
+ char *device;
+ int fsamp;
+ int bsize;
+ int nfrag;
+ int nchan;
+ int rqual;
+ int ltcor;
+
+public:
+
+ zita_j2a()
+ {
+ commq = new Lfq_int32(16);
+ alsaq = new Lfq_adata(256);
+ infoq = new Lfq_jdata(256);
+ audioq = 0;
+ stop = false;
+ v_opt = false;
+ L_opt = false;
+ S_opt = false;
+ jname = strdup(APPNAME);
+ device = 0;
+ fsamp = 48000;
+ bsize = 128;
+ nfrag = 2;
+ nchan = 2;
+ rqual = 0;
+ ltcor = 0;
+ A = 0;
+ P = 0;
+ J = 0;
+ }
+
+private:
+
+ int procoptions (int ac, const char *av [])
+ {
+ int k;
+
+ optind = 1;
+ opterr = 0;
+ while ((k = getopt (ac, (char **) av, (char *) clopt)) != -1)
+ {
+ if (optarg && (*optarg == '-'))
+ {
+ jack_error (APPNAME ": Missing argument for '-%c' option.", k);
+ jack_error (APPNAME ": Use '-h' to see all options.");
+ return 1;
+ }
+ switch (k)
+ {
+ case 'h' : help (); return 1;
+ case 'v' : v_opt = true; break;
+ case 'L' : L_opt = true; break;
+ case 'S' : S_opt = true; break;
+ case 'j' : jname = optarg; break;
+ case 'd' : device = optarg; break;
+ case 'r' : fsamp = atoi (optarg); break;
+ case 'p' : bsize = atoi (optarg); break;
+ case 'n' : nfrag = atoi (optarg); break;
+ case 'c' : nchan = atoi (optarg); break;
+ case 'Q' : rqual = atoi (optarg); break;
+ case 'O' : ltcor = atoi (optarg); break;
+ case '?':
+ if (optopt != ':' && strchr (clopt, optopt))
+ {
+ jack_error (APPNAME ": Missing argument for '-%c' option.", optopt);
+ }
+ else if (isprint (optopt))
+ {
+ jack_error (APPNAME ": Unknown option '-%c'.", optopt);
+ }
+ else
+ {
+ jack_error (APPNAME ": Unknown option character '0x%02x'.", optopt & 255);
+ }
+ jack_error (APPNAME ": Use '-h' to see all options.");
+ return 1;
+ default:
+ return 1;
+ }
+ }
+
+ return 0;
+ }
+
+ int parse_options (const char* load_init)
+ {
+ int argsz;
+ int argc = 0;
+ const char** argv;
+ char* args = strdup (load_init);
+ char* token;
+ char* ptr = args;
+ char* savep;
+
+ if (!load_init) {
+ return 0;
+ }
+
+ argsz = 8; /* random guess at "maxargs" */
+ argv = (const char **) malloc (sizeof (char *) * argsz);
+
+ argv[argc++] = APPNAME;
+
+ while (1) {
+
+ if ((token = strtok_r (ptr, " ", &savep)) == NULL) {
+ break;
+ }
+
+ if (argc == argsz) {
+ argsz *= 2;
+ argv = (const char **) realloc (argv, sizeof (char *) * argsz);
+ }
+
+ argv[argc++] = token;
+ ptr = NULL;
+ }
+
+ return procoptions (argc, argv);
+ }
+
+ void printinfo (void)
+ {
+ int n, k;
+ double e, r;
+ Jdata *J;
+
+ n = 0;
+ k = 99999;
+ e = r = 0;
+ while (infoq->rd_avail ())
+ {
+ J = infoq->rd_datap ();
+ if (J->_state == Jackclient::TERM)
+ {
+ jack_info (APPNAME ": Fatal error condition, terminating.");
+ stop = true;
+ return;
+ }
+ else if (J->_state == Jackclient::WAIT)
+ {
+ jack_info (APPNAME ": Detected excessive timing errors, waiting 10 seconds.");
+ n = 0;
+ }
+ else if (J->_state == Jackclient::SYNC0)
+ {
+ jack_info (APPNAME ": Starting synchronisation.");
+ }
+ else if (v_opt)
+ {
+ n++;
+ e += J->_error;
+ r += J->_ratio;
+ if (J->_bstat < k) k = J->_bstat;
+ }
+ infoq->rd_commit ();
+ }
+ if (n) jack_info ("%8.3lf %10.6lf %5d", e / n, r / n, k);
+ }
+
+ Alsa_pcmi *A;
+ Alsathread *P;
+ Jackclient *J;
+
+public:
+
+ int jack_initialize (jack_client_t* client, const char* load_init)
+ {
+ int k, k_del, opts;
+ double t_jack;
+ double t_alsa;
+ double t_del;
+
+ if (parse_options (load_init)) {
+ delete this;
+ return 1;
+ }
+
+ if (device == 0)
+ {
+ help ();
+ delete this;
+ return 1;
+ }
+ if (rqual < 16) rqual = 16;
+ if (rqual > 96) rqual = 96;
+ if ((fsamp < 8000) || (bsize < 16) || (nfrag < 2) || (nchan < 1))
+ {
+ jack_error (APPNAME ": Illegal parameter value(s).");
+ delete this;
+ return 1;
+ }
+
+ opts = 0;
+ if (v_opt) opts |= Alsa_pcmi::DEBUG_ALL;
+ if (L_opt) opts |= Alsa_pcmi::FORCE_16B | Alsa_pcmi::FORCE_2CH;
+ A = new Alsa_pcmi (device, 0, 0, fsamp, bsize, nfrag, opts);
+ if (A->state ())
+ {
+ jack_error (APPNAME ": Can't open ALSA playback device '%s'.", device);
+ delete this;
+ return 1;
+ }
+ if (v_opt) A->printinfo ();
+ if (nchan > A->nplay ())
+ {
+ nchan = A->nplay ();
+ jack_error (APPNAME ": Warning: only %d channels are available.", nchan);
+ }
+ P = new Alsathread (A, Alsathread::PLAY);
+ J = new Jackclient (client, 0, Jackclient::PLAY, nchan, S_opt, this);
+ usleep (100000);
+
+ t_alsa = (double) bsize / fsamp;
+ if (t_alsa < 1e-3) t_alsa = 1e-3;
+ t_jack = (double) J->bsize () / J->fsamp ();
+ t_del = t_alsa + t_jack;
+ k_del = (int)(t_del * fsamp);
+ for (k = 256; k < 2 * k_del; k *= 2);
+ audioq = new Lfq_audio (k, nchan);
+
+ if (rqual == 0)
+ {
+ k = (fsamp < J->fsamp ()) ? fsamp : J->fsamp ();
+ if (k < 44100) k = 44100;
+ rqual = (int)((6.7 * k) / (k - 38000));
+ }
+ if (rqual < 16) rqual = 16;
+ if (rqual > 96) rqual = 96;
+
+ P->start (audioq, commq, alsaq, J->rprio () + 10);
+ J->start (audioq, commq, alsaq, infoq, (double) fsamp / J->fsamp (), k_del, ltcor, rqual);
+
+ return 0;
+ }
+
+ void jack_finish (void* arg)
+ {
+ commq->wr_int32 (Alsathread::TERM);
+ usleep (100000);
+ delete P;
+ delete A;
+ delete J;
+ delete audioq;
+ }
+};
+
+extern "C" {
+
+int
+jack_initialize (jack_client_t* client, const char* load_init)
+{
+ zita_j2a *c = new zita_j2a();
+ return c->jack_initialize(client, load_init);
+}
+
+void jack_finish (void* arg)
+{
+ if (!arg) return;
+ Jackclient *J = (Jackclient *)arg;
+ zita_j2a *c = (zita_j2a *)J->getarg();
+ c->jack_finish(arg);
+ delete c;
+}
+
+} /* extern "C" */
diff --git a/wscript b/wscript
index 3d56751c..86a42302 100644
--- a/wscript
+++ b/wscript
@@ -11,7 +11,7 @@ import sys
from waflib import Logs, Options, Task, Utils
from waflib.Build import BuildContext, CleanContext, InstallContext, UninstallContext
-VERSION='1.9.17'
+VERSION='1.9.18'
APPNAME='jack'
JACK_API_VERSION = '0.1.0'
@@ -174,6 +174,11 @@ def options(opt):
help='Use Berkeley DB (metadata)')
db.check(header_name='db.h')
db.check(lib='db')
+ zalsa = opt.add_auto_option(
+ 'zalsa',
+ help='Build internal zita-a2j/j2a client')
+ zalsa.check(lib='zita-alsa-pcmi')
+ zalsa.check(lib='zita-resampler')
# dbus options
opt.recurse('dbus')