summaryrefslogtreecommitdiff
path: root/tools/zalsa/zita-a2j.cc
diff options
context:
space:
mode:
Diffstat (limited to 'tools/zalsa/zita-a2j.cc')
-rw-r--r--tools/zalsa/zita-a2j.cc329
1 files changed, 329 insertions, 0 deletions
diff --git a/tools/zalsa/zita-a2j.cc b/tools/zalsa/zita-a2j.cc
new file mode 100644
index 00000000..7fa56c39
--- /dev/null
+++ b/tools/zalsa/zita-a2j.cc
@@ -0,0 +1,329 @@
+// ----------------------------------------------------------------------------
+//
+// 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"
+
+static const char *clopt = "hvLSj:d:r:p:n:c:Q:I:";
+
+static void help (void)
+{
+ fprintf (stderr, "\n%s-%s\n", APPNAME, VERSION);
+ fprintf (stderr, "(C) 2012-2018 Fons Adriaensen <fons@linuxaudio.org>\n");
+ fprintf (stderr, "Use ALSA capture device as a Jack client.\n\n");
+ fprintf (stderr, "Usage: %s <options>\n", APPNAME);
+ fprintf (stderr, "Options:\n");
+ fprintf (stderr, " -h Display this text\n");
+ fprintf (stderr, " -j <jackname> Name as Jack client [%s]\n", APPNAME);
+ fprintf (stderr, " -d <device> ALSA capture device [none]\n");
+ fprintf (stderr, " -r <rate> Sample rate [48000]\n");
+ fprintf (stderr, " -p <period> Period size [256]\n");
+ fprintf (stderr, " -n <nfrags> Number of fragments [2]\n");
+ fprintf (stderr, " -c <nchannels> Number of channels [2]\n");
+ fprintf (stderr, " -S Word clock sync, no resampling\n");
+ fprintf (stderr, " -Q <quality> Resampling quality, 16..96 [auto]\n");
+ fprintf (stderr, " -I <samples> Latency adjustment [0]\n");
+ fprintf (stderr, " -L Force 16-bit and 2 channels [off]\n");
+ fprintf (stderr, " -v Print tracing information [off]\n");
+}
+
+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 == '-'))
+ {
+ fprintf (stderr, " Missing argument for '-%c' option.\n", k);
+ fprintf (stderr, " Use '-h' to see all options.\n");
+ 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))
+ {
+ fprintf (stderr, " Missing argument for '-%c' option.\n", optopt);
+ }
+ else if (isprint (optopt))
+ {
+ fprintf (stderr, " Unknown option '-%c'.\n", optopt);
+ }
+ else
+ {
+ fprintf (stderr, " Unknown option character '0x%02x'.\n", optopt & 255);
+ }
+ fprintf (stderr, " Use '-h' to see all options.\n");
+ 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)
+ {
+ printf ("Fatal error condition, terminating.\n");
+ stop = true;
+ return;
+ }
+ else if (J->_state == Jackclient::WAIT)
+ {
+ printf ("Detected excessive timing errors, waiting 10 seconds.\n");
+ n = 0;
+ }
+ else if (J->_state == Jackclient::SYNC0)
+ {
+ printf ("Starting synchronisation.\n");
+ }
+ else if (v_opt)
+ {
+ n++;
+ e += J->_error;
+ r += J->_ratio;
+ if (J->_bstat < k) k = J->_bstat;
+ }
+ infoq->rd_commit ();
+ }
+ if (n) printf ("%8.3lf %10.6lf %5d\n", 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)) {
+ fprintf (stderr, "parse options failed\n");
+ return 1;
+ }
+
+ if (device == 0)
+ {
+ help ();
+ return 1;
+ }
+ if (rqual < 16) rqual = 16;
+ if (rqual > 96) rqual = 96;
+ if ((fsamp < 8000) || (bsize < 16) || (nfrag < 2) || (nchan < 1))
+ {
+ fprintf (stderr, "Illegal parameter value(s).\n");
+ 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 ())
+ {
+ fprintf (stderr, "Can't open ALSA capture device '%s'.\n", device);
+ return 1;
+ }
+ if (v_opt) A->printinfo ();
+ if (nchan > A->ncapt ())
+ {
+ nchan = A->ncapt ();
+ fprintf (stderr, "Warning: only %d channels are available.\n", 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();
+ c->jack_initialize(client, load_init);
+ return 0;
+}
+
+void jack_finish (void* arg)
+{
+ Jackclient *J = (Jackclient *)arg;
+ zita_a2j *c = (zita_a2j *)J->getarg();
+ c->jack_finish(arg);
+ delete c;
+}
+
+} /* extern "C" */