| @@ -25,8 +25,9 @@ | |||||
| #include "alsathread.h" | #include "alsathread.h" | ||||
| Jackclient::Jackclient (jack_client_t* cl, const char*jserv, int mode, int nchan) : | |||||
| Jackclient::Jackclient (jack_client_t* cl, const char*jserv, int mode, int nchan, void *arg) : | |||||
| _client (cl), | _client (cl), | ||||
| _arg (arg), | |||||
| _mode (mode), | _mode (mode), | ||||
| _nchan (nchan), | _nchan (nchan), | ||||
| _state (INIT), | _state (INIT), | ||||
| @@ -31,7 +31,7 @@ class Jackclient | |||||
| { | { | ||||
| public: | public: | ||||
| Jackclient (jack_client_t*, const char *jserv, int mode, int nchan); | |||||
| Jackclient (jack_client_t*, const char *jserv, int mode, int nchan, void *arg); | |||||
| virtual ~Jackclient (void); | virtual ~Jackclient (void); | ||||
| enum { PLAY, CAPT }; | enum { PLAY, CAPT }; | ||||
| @@ -51,6 +51,7 @@ public: | |||||
| int bsize (void) const { return _bsize; } | int bsize (void) const { return _bsize; } | ||||
| int rprio (void) const { return _rprio; } | int rprio (void) const { return _rprio; } | ||||
| void register_ports (int nports); | void register_ports (int nports); | ||||
| void *getarg(void) const { return _arg; } | |||||
| private: | private: | ||||
| @@ -80,6 +81,7 @@ private: | |||||
| jack_client_t *_client; | jack_client_t *_client; | ||||
| jack_port_t *_ports [256]; | jack_port_t *_ports [256]; | ||||
| void *_arg; | |||||
| const char *_jname; | const char *_jname; | ||||
| int _mode; | int _mode; | ||||
| int _nchan; | int _nchan; | ||||
| @@ -27,25 +27,7 @@ | |||||
| #include "jackclient.h" | #include "jackclient.h" | ||||
| #include "lfqueue.h" | #include "lfqueue.h" | ||||
| static Lfq_int32 commq (16); | |||||
| static Lfq_adata alsaq (256); | |||||
| static Lfq_jdata infoq (256); | |||||
| static Lfq_audio *audioq = 0; | |||||
| static bool stop = false; | |||||
| static const char *clopt = "hvLj:d:r:p:n:c:Q:I:"; | |||||
| static bool v_opt = false; | |||||
| static bool L_opt = false; | |||||
| static const char *jname = APPNAME; | |||||
| static const char *device = 0; | |||||
| static int fsamp = 0; // try to get from server if unspecified | |||||
| static int bsize = 0; // try to get from server if unspecified | |||||
| static int nfrag = 2; | |||||
| static int nchan = 2; | |||||
| static int rqual = 48; | |||||
| static int ltcor = 0; | |||||
| static const char *clopt = "hvLj:d:r:p:n:c:Q:I:"; | |||||
| static void help (void) | static void help (void) | ||||
| { | { | ||||
| @@ -68,57 +50,101 @@ static void help (void) | |||||
| exit (1); | exit (1); | ||||
| } | } | ||||
| static int procoptions (int ac, const char *av []) | |||||
| class zita_a2j | |||||
| { | { | ||||
| int k; | |||||
| optind = 1; | |||||
| opterr = 0; | |||||
| while ((k = getopt (ac, (char **) av, (char *) clopt)) != -1) | |||||
| Lfq_int32 *commq; | |||||
| Lfq_adata *alsaq; | |||||
| Lfq_jdata *infoq; | |||||
| Lfq_audio *audioq; | |||||
| bool stop; | |||||
| bool v_opt; | |||||
| bool L_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; | |||||
| jname = strdup(APPNAME); | |||||
| device = 0; | |||||
| fsamp = 0; | |||||
| bsize = 0; | |||||
| nfrag = 2; | |||||
| nchan = 2; | |||||
| rqual = 48; | |||||
| ltcor = 0; | |||||
| A = 0; | |||||
| C = 0; | |||||
| J = 0; | |||||
| } | |||||
| private: | |||||
| int procoptions (int ac, const char *av []) | |||||
| { | { | ||||
| if (optarg && (*optarg == '-')) | |||||
| int k; | |||||
| optind = 1; | |||||
| opterr = 0; | |||||
| while ((k = getopt (ac, (char **) av, (char *) clopt)) != -1) | |||||
| { | { | ||||
| fprintf (stderr, " Missing argument for '-%c' option.\n", k); | |||||
| fprintf (stderr, " Use '-h' to see all options.\n"); | |||||
| exit (1); | |||||
| if (optarg && (*optarg == '-')) | |||||
| { | |||||
| fprintf (stderr, " Missing argument for '-%c' option.\n", k); | |||||
| fprintf (stderr, " Use '-h' to see all options.\n"); | |||||
| exit (1); | |||||
| } | |||||
| switch (k) | |||||
| { | |||||
| case 'h' : help (); exit (0); | |||||
| case 'v' : v_opt = true; break; | |||||
| case 'L' : L_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; | |||||
| } | |||||
| } | } | ||||
| switch (k) | |||||
| { | |||||
| case 'h' : help (); exit (0); | |||||
| case 'v' : v_opt = true; break; | |||||
| case 'L' : L_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; | |||||
| } | } | ||||
| return 0; | |||||
| } | |||||
| static int parse_options (const char* load_init) | |||||
| { | |||||
| int parse_options (const char* load_init) | |||||
| { | |||||
| int argsz; | int argsz; | ||||
| int argc = 0; | int argc = 0; | ||||
| const char** argv; | const char** argv; | ||||
| @@ -127,8 +153,8 @@ static int parse_options (const char* load_init) | |||||
| char* ptr = args; | char* ptr = args; | ||||
| char* savep; | char* savep; | ||||
| if (!load_init) { | |||||
| return 0; | |||||
| if (!load_init) { | |||||
| return 0; | |||||
| } | } | ||||
| argsz = 8; /* random guess at "maxargs" */ | argsz = 8; /* random guess at "maxargs" */ | ||||
| @@ -137,149 +163,166 @@ static int parse_options (const char* load_init) | |||||
| argv[argc++] = APPNAME; | argv[argc++] = APPNAME; | ||||
| while (1) { | 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; | |||||
| 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); | return procoptions (argc, argv); | ||||
| } | |||||
| static void printinfo (void) | |||||
| { | |||||
| int n; | |||||
| double e, r; | |||||
| Jdata *J; | |||||
| } | |||||
| n = 0; | |||||
| e = r = 0; | |||||
| while (infoq.rd_avail ()) | |||||
| void printinfo (void) | |||||
| { | { | ||||
| 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 15 seconds.\n"); | |||||
| printf ("This may happen with current Jack1 after freewheeling.\n"); | |||||
| n = 0; | |||||
| } | |||||
| else if (J->_state == Jackclient::SYNC0) | |||||
| { | |||||
| printf ("Starting synchronisation.\n"); | |||||
| } | |||||
| else if (v_opt) | |||||
| { | |||||
| n++; | |||||
| e += J->_error; | |||||
| r += J->_ratio; | |||||
| } | |||||
| infoq.rd_commit (); | |||||
| } | |||||
| if (n) printf ("%8.3lf %10.6lf\n", e / n, r / n); | |||||
| } | |||||
| int n; | |||||
| double e, r; | |||||
| Jdata *J; | |||||
| n = 0; | |||||
| 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 15 seconds.\n"); | |||||
| printf ("This may happen with current Jack1 after freewheeling.\n"); | |||||
| n = 0; | |||||
| } | |||||
| else if (J->_state == Jackclient::SYNC0) | |||||
| { | |||||
| printf ("Starting synchronisation.\n"); | |||||
| } | |||||
| else if (v_opt) | |||||
| { | |||||
| n++; | |||||
| e += J->_error; | |||||
| r += J->_ratio; | |||||
| } | |||||
| infoq->rd_commit (); | |||||
| } | |||||
| if (n) printf ("%8.3lf %10.6lf\n", e / n, r / n); | |||||
| } | |||||
| static Alsa_pcmi *A = 0; | |||||
| static Alsathread *C = 0; | |||||
| static Jackclient *J = 0; | |||||
| extern "C" { | |||||
| Alsa_pcmi *A; | |||||
| Alsathread *C; | |||||
| Jackclient *J; | |||||
| 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; | |||||
| } | |||||
| public: | |||||
| if (device == 0) help (); | |||||
| if (rqual < 16) rqual = 16; | |||||
| if (rqual > 96) rqual = 96; | |||||
| if ((fsamp && fsamp < 8000) || (bsize && bsize < 16) || (nfrag < 2) || (nchan < 1)) | |||||
| int | |||||
| jack_initialize (jack_client_t* client, const char* load_init) | |||||
| { | { | ||||
| fprintf (stderr, "Illegal parameter value(s).\n"); | |||||
| return 1; | |||||
| } | |||||
| 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; | |||||
| } | |||||
| J = new Jackclient (client, 0, Jackclient::CAPT, 0); | |||||
| usleep (100000); | |||||
| if (device == 0) help (); | |||||
| if (rqual < 16) rqual = 16; | |||||
| if (rqual > 96) rqual = 96; | |||||
| if ((fsamp && fsamp < 8000) || (bsize && bsize < 16) || (nfrag < 2) || (nchan < 1)) | |||||
| { | |||||
| fprintf (stderr, "Illegal parameter value(s).\n"); | |||||
| return 1; | |||||
| } | |||||
| /* if SR and/or bufsize are unspecified, use the same values | |||||
| as the JACK server. | |||||
| */ | |||||
| if (fsamp == 0) | |||||
| { | |||||
| fsamp = J->fsamp(); | |||||
| } | |||||
| J = new Jackclient (client, 0, Jackclient::CAPT, 0, this); | |||||
| usleep (100000); | |||||
| if (bsize == 0) | |||||
| { | |||||
| bsize = J->bsize(); | |||||
| } | |||||
| /* if SR and/or bufsize are unspecified, use the same values | |||||
| as the JACK server. | |||||
| */ | |||||
| if (fsamp == 0) | |||||
| { | |||||
| fsamp = J->fsamp(); | |||||
| } | |||||
| 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 (bsize == 0) | |||||
| { | |||||
| bsize = J->bsize(); | |||||
| } | |||||
| 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->register_ports (nchan); | |||||
| t_alsa = (double) bsize / fsamp; | |||||
| if (t_alsa < 1e-3) t_alsa = 1e-3; | |||||
| t_jack = (double) J->bsize () / J->fsamp (); | |||||
| t_del = 1.5 * t_alsa + t_jack; | |||||
| k_del = (int)(t_del * fsamp); | |||||
| for (k = 256; k < k_del + J->bsize (); k *= 2); | |||||
| audioq = new Lfq_audio (k, nchan); | |||||
| C->start (audioq, commq, alsaq, J->rprio () + 10); | |||||
| J->start (audioq, commq, alsaq, infoq, J->fsamp () / (double) fsamp, k_del, ltcor, rqual); | |||||
| return 0; | |||||
| } | } | ||||
| if (v_opt) A->printinfo (); | |||||
| if (nchan > A->ncapt ()) | |||||
| void jack_finish (void* arg) | |||||
| { | { | ||||
| nchan = A->ncapt (); | |||||
| fprintf (stderr, "Warning: only %d channels are available.\n", nchan); | |||||
| commq->wr_int32 (Alsathread::TERM); | |||||
| usleep (100000); | |||||
| delete C; | |||||
| delete A; | |||||
| delete J; | |||||
| delete audioq; | |||||
| } | } | ||||
| C = new Alsathread (A, Alsathread::CAPT); | |||||
| J->register_ports (nchan); | |||||
| t_alsa = (double) bsize / fsamp; | |||||
| if (t_alsa < 1e-3) t_alsa = 1e-3; | |||||
| t_jack = (double) J->bsize () / J->fsamp (); | |||||
| t_del = 1.5 * t_alsa + t_jack; | |||||
| k_del = (int)(t_del * fsamp); | |||||
| for (k = 256; k < k_del + J->bsize (); k *= 2); | |||||
| audioq = new Lfq_audio (k, nchan); | |||||
| C->start (audioq, &commq, &alsaq, J->rprio () + 10); | |||||
| J->start (audioq, &commq, &alsaq, &infoq, J->fsamp () / (double) fsamp, k_del, ltcor, rqual); | |||||
| return 0; | |||||
| }; | |||||
| 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); | |||||
| } | } | ||||
| void jack_finish (void* arg) | void jack_finish (void* arg) | ||||
| { | { | ||||
| commq.wr_int32 (Alsathread::TERM); | |||||
| usleep (100000); | |||||
| delete C; | |||||
| delete A; | |||||
| delete J; | |||||
| delete audioq; | |||||
| Jackclient *J = (Jackclient *)arg; | |||||
| zita_a2j *c = (zita_a2j *)J->getarg(); | |||||
| c->jack_finish(arg); | |||||
| delete c; | |||||
| } | } | ||||
| } /* extern "C" */ | } /* extern "C" */ | ||||
| @@ -27,25 +27,7 @@ | |||||
| #include "jackclient.h" | #include "jackclient.h" | ||||
| #include "lfqueue.h" | #include "lfqueue.h" | ||||
| static Lfq_int32 commq (16); | |||||
| static Lfq_adata alsaq (256); | |||||
| static Lfq_jdata infoq (256); | |||||
| static Lfq_audio *audioq = 0; | |||||
| static bool stop = false; | |||||
| static const char *clopt = "hvLj:d:r:p:n:c:Q:O:"; | |||||
| static bool v_opt = false; | |||||
| static bool L_opt = false; | |||||
| static const char *jname = APPNAME; | |||||
| static const char *device = 0; | |||||
| static int fsamp = 0; | |||||
| static int bsize = 0; | |||||
| static int nfrag = 2; | |||||
| static int nchan = 2; | |||||
| static int rqual = 48; | |||||
| static int ltcor = 0; | |||||
| static const char *clopt = "hvLj:d:r:p:n:c:Q:I:"; | |||||
| static void help (void) | static void help (void) | ||||
| { | { | ||||
| @@ -68,58 +50,102 @@ static void help (void) | |||||
| exit (1); | exit (1); | ||||
| } | } | ||||
| static int procoptions (int ac, const char *av []) | |||||
| class zita_j2a | |||||
| { | { | ||||
| int k; | |||||
| optind = 1; | |||||
| opterr = 0; | |||||
| while ((k = getopt (ac, (char **) av, (char *) clopt)) != -1) | |||||
| Lfq_int32 *commq; | |||||
| Lfq_adata *alsaq; | |||||
| Lfq_jdata *infoq; | |||||
| Lfq_audio *audioq; | |||||
| bool stop; | |||||
| bool v_opt; | |||||
| bool L_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; | |||||
| jname = strdup(APPNAME); | |||||
| device = 0; | |||||
| fsamp = 0; | |||||
| bsize = 0; | |||||
| nfrag = 2; | |||||
| nchan = 2; | |||||
| rqual = 48; | |||||
| ltcor = 0; | |||||
| A = 0; | |||||
| P = 0; | |||||
| J = 0; | |||||
| } | |||||
| private: | |||||
| int procoptions (int ac, const char *av []) | |||||
| { | { | ||||
| if (optarg && (*optarg == '-')) | |||||
| int k; | |||||
| optind = 1; | |||||
| opterr = 0; | |||||
| while ((k = getopt (ac, (char **) av, (char *) clopt)) != -1) | |||||
| { | { | ||||
| fprintf (stderr, " Missing argument for '-%c' option.\n", k); | |||||
| fprintf (stderr, " Use '-h' to see all options.\n"); | |||||
| exit (1); | |||||
| if (optarg && (*optarg == '-')) | |||||
| { | |||||
| fprintf (stderr, " Missing argument for '-%c' option.\n", k); | |||||
| fprintf (stderr, " Use '-h' to see all options.\n"); | |||||
| exit (1); | |||||
| } | |||||
| switch (k) | |||||
| { | |||||
| case 'h' : help (); exit (0); | |||||
| case 'v' : v_opt = true; break; | |||||
| case 'L' : L_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)) | |||||
| { | |||||
| 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; | |||||
| } | |||||
| } | } | ||||
| switch (k) | |||||
| { | |||||
| case 'h' : help (); exit (0); | |||||
| case 'v' : v_opt = true; break; | |||||
| case 'L' : L_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)) | |||||
| { | |||||
| 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; | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| static int parse_options (const char* load_init) | |||||
| { | |||||
| int parse_options (const char* load_init) | |||||
| { | |||||
| int argsz; | int argsz; | ||||
| int argc = 0; | int argc = 0; | ||||
| const char** argv; | const char** argv; | ||||
| @@ -128,8 +154,8 @@ static int parse_options (const char* load_init) | |||||
| char* ptr = args; | char* ptr = args; | ||||
| char* savep; | char* savep; | ||||
| if (!load_init) { | |||||
| return 0; | |||||
| if (!load_init) { | |||||
| return 0; | |||||
| } | } | ||||
| argsz = 8; /* random guess at "maxargs" */ | argsz = 8; /* random guess at "maxargs" */ | ||||
| @@ -138,144 +164,162 @@ static int parse_options (const char* load_init) | |||||
| argv[argc++] = APPNAME; | argv[argc++] = APPNAME; | ||||
| while (1) { | 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; | |||||
| 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); | return procoptions (argc, argv); | ||||
| } | |||||
| static void printinfo (void) | |||||
| { | |||||
| int n; | |||||
| double e, r; | |||||
| Jdata *J; | |||||
| } | |||||
| n = 0; | |||||
| e = r = 0; | |||||
| while (infoq.rd_avail ()) | |||||
| void printinfo (void) | |||||
| { | { | ||||
| 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 15 seconds.\n"); | |||||
| printf ("This may happen with current Jack1 after freewheeling.\n"); | |||||
| n = 0; | |||||
| } | |||||
| else if (J->_state == Jackclient::SYNC0) | |||||
| { | |||||
| printf ("Starting synchronisation.\n"); | |||||
| } | |||||
| else if (v_opt) | |||||
| { | |||||
| n++; | |||||
| e += J->_error; | |||||
| r += J->_ratio; | |||||
| } | |||||
| infoq.rd_commit (); | |||||
| int n; | |||||
| double e, r; | |||||
| Jdata *J; | |||||
| n = 0; | |||||
| 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 15 seconds.\n"); | |||||
| printf ("This may happen with current Jack1 after freewheeling.\n"); | |||||
| n = 0; | |||||
| } | |||||
| else if (J->_state == Jackclient::SYNC0) | |||||
| { | |||||
| printf ("Starting synchronisation.\n"); | |||||
| } | |||||
| else if (v_opt) | |||||
| { | |||||
| n++; | |||||
| e += J->_error; | |||||
| r += J->_ratio; | |||||
| } | |||||
| infoq->rd_commit (); | |||||
| } | |||||
| if (n) printf ("%8.3lf %10.6lf\n", e / n, r / n); | |||||
| } | } | ||||
| if (n) printf ("%8.3lf %10.6lf\n", e / n, r / n); | |||||
| } | |||||
| Alsa_pcmi *A = 0; | |||||
| Alsathread *P = 0; | |||||
| Jackclient *J = 0; | |||||
| Alsa_pcmi *A; | |||||
| Alsathread *P; | |||||
| Jackclient *J; | |||||
| public: | |||||
| extern "C" { | |||||
| 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; | |||||
| 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)) { | |||||
| return 1; | |||||
| } | |||||
| if (parse_options (load_init)) { | |||||
| if (device == 0) help (); | |||||
| if (rqual < 16) rqual = 16; | |||||
| if (rqual > 96) rqual = 96; | |||||
| if ((fsamp && fsamp < 8000) || (bsize && bsize < 16) || (nfrag < 2) || (nchan < 1)) | |||||
| { | |||||
| fprintf (stderr, "Illegal parameter value(s).\n"); | |||||
| return 1; | return 1; | ||||
| } | |||||
| } | |||||
| if (device == 0) help (); | |||||
| if (rqual < 16) rqual = 16; | |||||
| if (rqual > 96) rqual = 96; | |||||
| if ((fsamp && fsamp < 8000) || (bsize && bsize < 16) || (nfrag < 2) || (nchan < 1)) | |||||
| { | |||||
| fprintf (stderr, "Illegal parameter value(s).\n"); | |||||
| return 1; | |||||
| } | |||||
| J = new Jackclient (client, 0, Jackclient::PLAY, 0, this); | |||||
| usleep (100000); | |||||
| J = new Jackclient (client, 0, Jackclient::PLAY, 0); | |||||
| usleep (100000); | |||||
| /* if SR and/or bufsize are unspecified, use the same values | |||||
| as the JACK server. | |||||
| */ | |||||
| /* if SR and/or bufsize are unspecified, use the same values | |||||
| as the JACK server. | |||||
| */ | |||||
| if (fsamp == 0) | |||||
| { | |||||
| fsamp = J->fsamp(); | |||||
| } | |||||
| if (fsamp == 0) | |||||
| { | |||||
| fsamp = J->fsamp(); | |||||
| } | |||||
| if (bsize == 0) | |||||
| { | |||||
| bsize = J->bsize(); | |||||
| } | |||||
| if (bsize == 0) | |||||
| { | |||||
| bsize = J->bsize(); | |||||
| } | |||||
| 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 ()) | |||||
| { | |||||
| fprintf (stderr, "Can't open ALSA playback device '%s'.\n", device); | |||||
| 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 ()) | |||||
| { | |||||
| fprintf (stderr, "Can't open ALSA playback device '%s'.\n", device); | |||||
| return 1; | |||||
| } | |||||
| if (v_opt) A->printinfo (); | |||||
| if (nchan > A->nplay ()) | |||||
| { | |||||
| nchan = A->nplay (); | |||||
| fprintf (stderr, "Warning: only %d channels are available.\n", nchan); | |||||
| } | |||||
| P = new Alsathread (A, Alsathread::PLAY); | |||||
| J->register_ports (nchan); | |||||
| t_alsa = (double) bsize / fsamp; | |||||
| if (t_alsa < 1e-3) t_alsa = 1e-3; | |||||
| t_jack = (double) J->bsize () / J->fsamp (); | |||||
| t_del = 1.5 * t_alsa + t_jack; | |||||
| k_del = (int)(t_del * fsamp); | |||||
| for (k = 256; k < k_del + J->bsize (); k *= 2); | |||||
| audioq = new Lfq_audio (k, nchan); | |||||
| P->start (audioq, commq, alsaq, J->rprio () + 10); | |||||
| J->start (audioq, commq, alsaq, infoq, (double) fsamp / J->fsamp (), k_del, ltcor, rqual); | |||||
| return 0; | |||||
| } | } | ||||
| if (v_opt) A->printinfo (); | |||||
| if (nchan > A->nplay ()) | |||||
| void jack_finish (void* arg) | |||||
| { | { | ||||
| nchan = A->nplay (); | |||||
| fprintf (stderr, "Warning: only %d channels are available.\n", nchan); | |||||
| commq->wr_int32 (Alsathread::TERM); | |||||
| usleep (100000); | |||||
| delete P; | |||||
| delete A; | |||||
| delete J; | |||||
| delete audioq; | |||||
| } | } | ||||
| P = new Alsathread (A, Alsathread::PLAY); | |||||
| J->register_ports (nchan); | |||||
| t_alsa = (double) bsize / fsamp; | |||||
| if (t_alsa < 1e-3) t_alsa = 1e-3; | |||||
| t_jack = (double) J->bsize () / J->fsamp (); | |||||
| t_del = 1.5 * t_alsa + t_jack; | |||||
| k_del = (int)(t_del * fsamp); | |||||
| for (k = 256; k < k_del + J->bsize (); k *= 2); | |||||
| audioq = new Lfq_audio (k, nchan); | |||||
| }; | |||||
| P->start (audioq, &commq, &alsaq, J->rprio () + 10); | |||||
| J->start (audioq, &commq, &alsaq, &infoq, (double) fsamp / J->fsamp (), k_del, ltcor, rqual); | |||||
| extern "C" { | |||||
| return 0; | |||||
| int | |||||
| jack_initialize (jack_client_t* client, const char* load_init) | |||||
| { | |||||
| zita_j2a *c = new zita_j2a(); | |||||
| c->jack_initialize(client, load_init); | |||||
| } | } | ||||
| void jack_finish (void* arg) | void jack_finish (void* arg) | ||||
| { | { | ||||
| commq.wr_int32 (Alsathread::TERM); | |||||
| usleep (100000); | |||||
| delete P; | |||||
| delete A; | |||||
| delete J; | |||||
| delete audioq; | |||||
| Jackclient *J = (Jackclient *)arg; | |||||
| zita_j2a *c = (zita_j2a *)J->getarg(); | |||||
| c->jack_finish(arg); | |||||
| delete c; | |||||
| } | } | ||||
| } /* extern "C" */ | } /* extern "C" */ | ||||