|
- /* -*- mode: c; c-file-style: "linux"; -*- */
- /*
- Copyright (C) 2003 Robert Ham <rah@bash.sh>
- Copyright (C) 2001 Paul Davis
-
- 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 2 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, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
- */
-
- #include <math.h>
- #include <stdio.h>
- #include <memory.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <errno.h>
- #include <stdarg.h>
- #include <sys/mman.h>
-
- #include <jack/types.h>
- #include "internal.h"
- #include "engine.h"
- #include <sysdeps/time.h>
-
- #include "dummy_driver.h"
-
- #undef DEBUG_WAKEUP
-
- /* this is used for calculate what counts as an xrun */
- #define PRETEND_BUFFER_SIZE 4096
-
- void
- FakeVideoSync( dummy_driver_t *driver )
- {
- #define VIDEO_SYNC_PERIOD (48000 / 30)
- static int vidCounter = VIDEO_SYNC_PERIOD;
-
- int period = driver->period_size;
- jack_position_t *position = &driver->engine->control->current_time;
-
- if ( period >= VIDEO_SYNC_PERIOD ) {
- jack_error("JACK driver period size too large for simple video sync emulation. Halting.");
- exit(0);
- }
-
- //enable video sync, whether it occurs in this period or not
- position->audio_frames_per_video_frame = VIDEO_SYNC_PERIOD;
- position->valid = (jack_position_bits_t) (position->valid | JackAudioVideoRatio);
-
- //no video pulse found in this period, just decrement the counter
- if ( vidCounter > period ) {
- vidCounter -= period;
- }
-
- //video pulse occurs in this period
- if ( vidCounter <= period ) {
- int remainder = period - vidCounter;
- vidCounter = VIDEO_SYNC_PERIOD - remainder;
-
- position->video_offset = vidCounter;
- position->valid = (jack_position_bits_t) (position->valid | JackVideoFrameOffset);
- }
- }
-
- #ifdef HAVE_CLOCK_GETTIME
- static inline unsigned long long ts_to_nsec(struct timespec ts)
- {
- return ts.tv_sec * 1000000000LL + ts.tv_nsec;
- }
-
- static inline struct timespec nsec_to_ts(unsigned long long nsecs)
- {
- struct timespec ts;
- ts.tv_sec = nsecs / (1000000000LL);
- ts.tv_nsec = nsecs % (1000000000LL);
- return ts;
- }
-
- static inline struct timespec add_ts(struct timespec ts, unsigned int usecs)
- {
- unsigned long long nsecs = ts_to_nsec(ts);
- nsecs += usecs * 1000LL;
- return nsec_to_ts(nsecs);
- }
-
- static inline int cmp_lt_ts(struct timespec ts1, struct timespec ts2)
- {
- if(ts1.tv_sec < ts2.tv_sec) {
- return 1;
- } else if (ts1.tv_sec == ts2.tv_sec && ts1.tv_nsec < ts2.tv_nsec) {
- return 1;
- } else return 0;
- }
-
- static jack_nframes_t
- dummy_driver_wait (dummy_driver_t *driver, int extra_fd, int *status,
- float *delayed_usecs)
- {
- jack_nframes_t nframes = driver->period_size;
- struct timespec now;
-
- *status = 0;
- /* this driver doesn't work so well if we report a delay */
- *delayed_usecs = 0; /* lie about it */
-
- clock_gettime(CLOCK_REALTIME, &now);
-
- if (cmp_lt_ts(driver->next_wakeup, now)) {
- if (driver->next_wakeup.tv_sec == 0) {
- /* first time through */
- clock_gettime(CLOCK_REALTIME, &driver->next_wakeup);
- } else if ((ts_to_nsec(now) - ts_to_nsec(driver->next_wakeup))/1000LL
- > (PRETEND_BUFFER_SIZE * 1000000LL
- / driver->sample_rate)) {
- /* xrun */
- jack_error("**** dummy: xrun of %ju usec",
- (uintmax_t)(ts_to_nsec(now) - ts_to_nsec(driver->next_wakeup))/1000LL);
- nframes = 0;
- driver->next_wakeup.tv_sec = 0;
- } else {
- /* late, but handled by our "buffer"; try to
- * get back on track */
- }
- driver->next_wakeup = add_ts(driver->next_wakeup, driver->wait_time);
- } else {
- if(clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &driver->next_wakeup, NULL)) {
- jack_error("error while sleeping");
- *status = -1;
- } else {
- clock_gettime(CLOCK_REALTIME, &now);
- // guaranteed to sleep long enough for this to be correct
- *delayed_usecs = (ts_to_nsec(now) - ts_to_nsec(driver->next_wakeup));
- *delayed_usecs /= 1000.0;
- }
- driver->next_wakeup = add_ts(driver->next_wakeup, driver->wait_time);
- }
-
- driver->last_wait_ust = driver->engine->get_microseconds ();
- driver->engine->transport_cycle_start (driver->engine,
- driver->last_wait_ust);
-
- return nframes;
- }
-
- static int dummy_driver_nt_start (dummy_driver_t *drv)
- {
- drv->next_wakeup.tv_sec = 0;
- return 0;
- }
-
- #else
-
- static jack_nframes_t
- dummy_driver_wait (dummy_driver_t *driver, int extra_fd, int *status,
- float *delayed_usecs)
- {
- jack_time_t now = driver->engine->get_microseconds();
-
- if (driver->next_time < now) {
- if (driver->next_time == 0) {
- /* first time through */
- driver->next_time = now + driver->wait_time;
- } else if (now - driver->next_time
- > (PRETEND_BUFFER_SIZE * 1000000LL
- / driver->sample_rate)) {
- /* xrun */
- jack_error("**** dummy: xrun of %ju usec",
- (uintmax_t)now - driver->next_time);
- driver->next_time = now + driver->wait_time;
- } else {
- /* late, but handled by our "buffer"; try to
- * get back on track */
- driver->next_time += driver->wait_time;
- }
- } else {
- jack_time_t wait = driver->next_time - now;
- struct timespec ts = { .tv_sec = wait / 1000000,
- .tv_nsec = (wait % 1000000) * 1000 };
- nanosleep(&ts,NULL);
- driver->next_time += driver->wait_time;
- }
-
- driver->last_wait_ust = driver->engine->get_microseconds ();
- driver->engine->transport_cycle_start (driver->engine,
- driver->last_wait_ust);
-
- /* this driver doesn't work so well if we report a delay */
- *delayed_usecs = 0; /* lie about it */
- *status = 0;
- return driver->period_size;
- }
-
- static int dummy_driver_nt_start (dummy_driver_t *drv)
- {
- drv->next_time = 0;
- return 0;
- }
- #endif
-
- static inline int
- dummy_driver_run_cycle (dummy_driver_t *driver)
- {
- jack_engine_t *engine = driver->engine;
- int wait_status;
- float delayed_usecs;
-
- jack_nframes_t nframes = dummy_driver_wait (driver, -1, &wait_status,
- &delayed_usecs);
- if (nframes == 0) {
- /* we detected an xrun and restarted: notify
- * clients about the delay. */
- engine->delay (engine, delayed_usecs);
- return 0;
- }
-
- // FakeVideoSync (driver);
-
- if (wait_status == 0)
- return engine->run_cycle (engine, nframes, delayed_usecs);
-
- if (wait_status < 0)
- return -1;
- else
- return 0;
- }
-
- static int
- dummy_driver_null_cycle (dummy_driver_t* driver, jack_nframes_t nframes)
- {
- return 0;
- }
-
- static int
- dummy_driver_bufsize (dummy_driver_t* driver, jack_nframes_t nframes)
- {
- driver->period_size = nframes;
- driver->period_usecs = driver->wait_time =
- (jack_time_t) floor ((((float) nframes) / driver->sample_rate)
- * 1000000.0f);
-
- /* tell the engine to change its buffer size */
- if (driver->engine->set_buffer_size (driver->engine, nframes)) {
- jack_error ("dummy: cannot set engine buffer size to %d (check MIDI)", nframes);
- return -1;
- }
-
- return 0;
- }
-
- static int
- dummy_driver_write (dummy_driver_t* driver, jack_nframes_t nframes)
- {
- return 0;
- }
-
-
- static int
- dummy_driver_attach (dummy_driver_t *driver)
- {
- jack_port_t * port;
- char buf[32];
- unsigned int chn;
- int port_flags;
-
- if (driver->engine->set_buffer_size (driver->engine, driver->period_size)) {
- jack_error ("dummy: cannot set engine buffer size to %d (check MIDI)", driver->period_size);
- return -1;
- }
- driver->engine->set_sample_rate (driver->engine, driver->sample_rate);
-
- port_flags = JackPortIsOutput|JackPortIsPhysical|JackPortIsTerminal;
-
- for (chn = 0; chn < driver->capture_channels; chn++)
- {
- snprintf (buf, sizeof(buf) - 1, "capture_%u", chn+1);
-
- port = jack_port_register (driver->client, buf,
- JACK_DEFAULT_AUDIO_TYPE,
- port_flags, 0);
- if (!port)
- {
- jack_error ("DUMMY: cannot register port for %s", buf);
- break;
- }
-
- driver->capture_ports =
- jack_slist_append (driver->capture_ports, port);
- }
-
- port_flags = JackPortIsInput|JackPortIsPhysical|JackPortIsTerminal;
-
- for (chn = 0; chn < driver->playback_channels; chn++)
- {
- snprintf (buf, sizeof(buf) - 1, "playback_%u", chn+1);
-
- port = jack_port_register (driver->client, buf,
- JACK_DEFAULT_AUDIO_TYPE,
- port_flags, 0);
-
- if (!port)
- {
- jack_error ("DUMMY: cannot register port for %s", buf);
- break;
- }
-
- driver->playback_ports =
- jack_slist_append (driver->playback_ports, port);
- }
-
- jack_activate (driver->client);
-
- return 0;
- }
-
- static int
- dummy_driver_detach (dummy_driver_t *driver)
- {
- JSList * node;
-
- if (driver->engine == 0)
- return 0;
-
- for (node = driver->capture_ports; node; node = jack_slist_next (node))
- jack_port_unregister (driver->client,
- ((jack_port_t *) node->data));
-
- jack_slist_free (driver->capture_ports);
- driver->capture_ports = NULL;
-
-
- for (node = driver->playback_ports; node; node = jack_slist_next (node))
- jack_port_unregister (driver->client,
- ((jack_port_t *) node->data));
-
- jack_slist_free (driver->playback_ports);
- driver->playback_ports = NULL;
-
- return 0;
- }
-
-
- static void
- dummy_driver_delete (dummy_driver_t *driver)
- {
- jack_driver_nt_finish ((jack_driver_nt_t *) driver);
- free (driver);
- }
-
- static jack_driver_t *
- dummy_driver_new (jack_client_t * client,
- char *name,
- unsigned int capture_ports,
- unsigned int playback_ports,
- jack_nframes_t sample_rate,
- jack_nframes_t period_size,
- unsigned long wait_time)
- {
- dummy_driver_t * driver;
-
- jack_info ("creating dummy driver ... %s|%" PRIu32 "|%" PRIu32
- "|%lu|%u|%u", name, sample_rate, period_size, wait_time,
- capture_ports, playback_ports);
-
- driver = (dummy_driver_t *) calloc (1, sizeof (dummy_driver_t));
-
- jack_driver_nt_init ((jack_driver_nt_t *) driver);
-
- driver->write = (JackDriverReadFunction) dummy_driver_write;
- driver->null_cycle = (JackDriverNullCycleFunction) dummy_driver_null_cycle;
- driver->nt_attach = (JackDriverNTAttachFunction) dummy_driver_attach;
- driver->nt_start = (JackDriverNTStartFunction) dummy_driver_nt_start;
- driver->nt_detach = (JackDriverNTDetachFunction) dummy_driver_detach;
- driver->nt_bufsize = (JackDriverNTBufSizeFunction) dummy_driver_bufsize;
- driver->nt_run_cycle = (JackDriverNTRunCycleFunction) dummy_driver_run_cycle;
-
- driver->period_usecs =
- (jack_time_t) floor ((((float) period_size) / sample_rate)
- * 1000000.0f);
- driver->sample_rate = sample_rate;
- driver->period_size = period_size;
- driver->wait_time = wait_time;
- //driver->next_time = 0; // not needed since calloc clears the memory
- driver->last_wait_ust = 0;
-
- driver->capture_channels = capture_ports;
- driver->capture_ports = NULL;
- driver->playback_channels = playback_ports;
- driver->playback_ports = NULL;
-
- driver->client = client;
- driver->engine = NULL;
-
- return (jack_driver_t *) driver;
- }
-
-
- /* DRIVER "PLUGIN" INTERFACE */
-
- jack_driver_desc_t *
- driver_get_descriptor ()
- {
- jack_driver_desc_t * desc;
- jack_driver_param_desc_t * params;
- unsigned int i;
-
- desc = calloc (1, sizeof (jack_driver_desc_t));
- strcpy (desc->name, "dummy");
- desc->nparams = 5;
-
- params = calloc (desc->nparams, sizeof (jack_driver_param_desc_t));
-
- i = 0;
- strcpy (params[i].name, "capture");
- params[i].character = 'C';
- params[i].type = JackDriverParamUInt;
- params[i].value.ui = 2U;
- strcpy (params[i].short_desc, "Number of capture ports");
- strcpy (params[i].long_desc, params[i].short_desc);
-
- i++;
- strcpy (params[i].name, "playback");
- params[i].character = 'P';
- params[i].type = JackDriverParamUInt;
- params[1].value.ui = 2U;
- strcpy (params[i].short_desc, "Number of playback ports");
- strcpy (params[i].long_desc, params[i].short_desc);
-
- i++;
- strcpy (params[i].name, "rate");
- params[i].character = 'r';
- params[i].type = JackDriverParamUInt;
- params[i].value.ui = 48000U;
- strcpy (params[i].short_desc, "Sample rate");
- strcpy (params[i].long_desc, params[i].short_desc);
-
- i++;
- strcpy (params[i].name, "period");
- params[i].character = 'p';
- params[i].type = JackDriverParamUInt;
- params[i].value.ui = 1024U;
- strcpy (params[i].short_desc, "Frames per period");
- strcpy (params[i].long_desc, params[i].short_desc);
-
- i++;
- strcpy (params[i].name, "wait");
- params[i].character = 'w';
- params[i].type = JackDriverParamUInt;
- params[i].value.ui = 21333U;
- strcpy (params[i].short_desc,
- "Number of usecs to wait between engine processes");
- strcpy (params[i].long_desc, params[i].short_desc);
-
- desc->params = params;
-
- return desc;
- }
-
- const char driver_client_name[] = "dummy_pcm";
-
- jack_driver_t *
- driver_initialize (jack_client_t *client, const JSList * params)
- {
- jack_nframes_t sample_rate = 48000;
- jack_nframes_t period_size = 1024;
- unsigned int capture_ports = 2;
- unsigned int playback_ports = 2;
- int wait_time_set = 0;
- unsigned long wait_time = 0;
- const JSList * node;
- const jack_driver_param_t * param;
-
- for (node = params; node; node = jack_slist_next (node)) {
- param = (const jack_driver_param_t *) node->data;
-
- switch (param->character) {
-
- case 'C':
- capture_ports = param->value.ui;
- break;
-
- case 'P':
- playback_ports = param->value.ui;
- break;
-
- case 'r':
- sample_rate = param->value.ui;
- break;
-
- case 'p':
- period_size = param->value.ui;
- break;
-
- case 'w':
- wait_time = param->value.ui;
- wait_time_set = 1;
- break;
-
- }
- }
-
- if (!wait_time_set)
- wait_time = (((float)period_size) / ((float)sample_rate)) * 1000000.0;
-
- return dummy_driver_new (client, "dummy_pcm", capture_ports,
- playback_ports, sample_rate, period_size,
- wait_time);
- }
-
- void
- driver_finish (jack_driver_t *driver)
- {
- dummy_driver_delete ((dummy_driver_t *) driver);
- }
|