/* -*- mode: c; c-file-style: "linux"; -*- */ /* Copyright (C) 2003 Robert Ham 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 #include #include #include #include #include #include #include #include #include "internal.h" #include "engine.h" #include #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); } } #if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_CLOCK_NANOSLEEP) 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); }