static size_t find_buffersize(jack_t *jd, int latency) { jack_latency_range_t range; int i, buffer_frames, min_buffer_frames, jack_latency = 0; settings_t *settings = config_get_ptr(); int frames = latency * settings->audio.out_rate / 1000; for (i = 0; i < 2; i++) { jack_port_get_latency_range(jd->ports[i], JackPlaybackLatency, &range); if ((int)range.max > jack_latency) jack_latency = range.max; } RARCH_LOG("JACK: Jack latency is %d frames.\n", jack_latency); buffer_frames = frames - jack_latency; min_buffer_frames = jack_get_buffer_size(jd->client) * 2; RARCH_LOG("JACK: Minimum buffer size is %d frames.\n", min_buffer_frames); if (buffer_frames < min_buffer_frames) buffer_frames = min_buffer_frames; return buffer_frames * sizeof(jack_default_audio_sample_t); }
static guint gst_jack_ring_buffer_delay (GstRingBuffer * buf) { GstJackAudioSrc *src; guint i, res = 0; #ifdef HAVE_JACK_0_120_2 jack_latency_range_t range; #else guint latency; #endif jack_client_t *client; src = GST_JACK_AUDIO_SRC (GST_OBJECT_PARENT (buf)); client = gst_jack_audio_client_get_client (src->client); for (i = 0; i < src->port_count; i++) { #ifdef HAVE_JACK_0_120_2 jack_port_get_latency_range (src->ports[i], JackCaptureLatency, &range); if (range.max > res) res = range.max; #else latency = jack_port_get_total_latency (client, src->ports[i]); if (latency > res) res = latency; #endif } GST_DEBUG_OBJECT (src, "delay %u", res); return res; }
void latency_cb (jack_latency_callback_mode_t mode, void *arg) { jack_latency_range_t range; if (mode == JackCaptureLatency) { jack_port_get_latency_range (input_port, mode, &range); range.min += latency; range.max += latency; jack_port_set_latency_range (output_port, mode, &range); } else { jack_port_get_latency_range (output_port, mode, &range); range.min += latency; range.max += latency; jack_port_set_latency_range (input_port, mode, &range); } }
static int LatencyRange(lua_State *L) { jack_latency_range_t range; pud_t *pud = pud_check(L, 1); jack_latency_callback_mode_t mode = CheckMode(L, 2); jack_port_get_latency_range(pud->port, mode, &range); lua_pushinteger(L, range.min); lua_pushinteger(L, range.max); return 2; }
/** returns the sum of latency of all ports between this one and a terminal port. */ nframes_t Port::total_latency ( void ) const { #ifdef HAVE_JACK_PORT_GET_LATENCY_RANGE jack_latency_range_t range; jack_port_get_latency_range( _port, _direction == Input ? JackPlaybackLatency : JackCaptureLatency, &range ); return range.max; #else return jack_port_get_total_latency( _client->jack_client() , _port ); #endif }
/** returns the number of frames of latency assigned to this port */ void Port::get_latency ( direction_e dir, nframes_t *min, nframes_t *max ) const { #ifdef HAVE_JACK_PORT_GET_LATENCY_RANGE jack_latency_range_t range; jack_port_get_latency_range( _port, dir == Output ? JackPlaybackLatency : JackCaptureLatency, &range ); *min = range.min; *max = range.max; #else *min = *max = jack_port_get_latency( _port ); #endif }
void latency_cb (jack_latency_callback_mode_t mode, void *arg) { jack_latency_range_t range; range.min = range.max = 0; if (mode == JackCaptureLatency) { jack_port_set_latency_range (jack_play, mode, &range); jack_port_get_latency_range (jack_capt, mode, &range); if ((range.min != capture_latency.min) || (range.max != capture_latency.max)) { capture_latency = range; printf ("new capture latency: [%d, %d]\n", range.min, range.max); } } else { jack_port_set_latency_range (jack_capt, mode, &range); jack_port_get_latency_range (jack_play, mode, &range); if ((range.min != playback_latency.min) || (range.max != playback_latency.max)) { playback_latency = range; printf ("new playback latency: [%d, %d]\n", range.min, range.max); } } }
static guint gst_jack_ring_buffer_delay (GstRingBuffer * buf) { GstJackAudioSrc *src; guint i, res = 0; jack_latency_range_t range; src = GST_JACK_AUDIO_SRC (GST_OBJECT_PARENT (buf)); for (i = 0; i < src->port_count; i++) { jack_port_get_latency_range (src->ports[i], JackCaptureLatency, &range); if (range.max > res) res = range.max; } GST_DEBUG_OBJECT (src, "delay %u", res); return res; }
static guint gst_jack_ring_buffer_delay (GstRingBuffer * buf) { GstJackAudioSink *sink; guint i, res = 0; jack_latency_range_t range; sink = GST_JACK_AUDIO_SINK (GST_OBJECT_PARENT (buf)); for (i = 0; i < sink->port_count; i++) { jack_port_get_latency_range (sink->ports[i], JackPlaybackLatency, &range); if (range.max > res) res = range.max; } GST_LOG_OBJECT (sink, "delay %u", res); return res; }
bool SC_JackDriver::GraphOrderChanged() { SC_JackPortList* outputs = mOutputList; jack_nframes_t lat = 0; for (int i=0; i < outputs->mSize; ++i) { jack_latency_range_t range; jack_port_get_latency_range( outputs->mPorts[i], JackPlaybackLatency, &range ); jack_nframes_t portLat = range.max; if (portLat > lat) lat = portLat; } double maxLat = (double)lat / mSampleRate; if (maxLat != mMaxOutputLatency) { mMaxOutputLatency = maxLat; scprintf("%s: max output latency %.1f ms\n", kJackDriverIdent, maxLat * 1e3); } return true; }
static int init(struct ao *ao) { struct priv *p = ao->priv; const char **matching_ports = NULL; char *port_name = p->cfg_port && p->cfg_port[0] ? p->cfg_port : NULL; jack_options_t open_options = JackNullOption; int port_flags = JackPortIsInput; int i; struct mp_chmap_sel sel = {0}; if (p->stdlayout == 0) { mp_chmap_sel_add_waveext(&sel); } else if (p->stdlayout == 1) { mp_chmap_sel_add_alsa_def(&sel); } else { mp_chmap_sel_add_any(&sel); } if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) goto err_out; if (!p->autostart) open_options |= JackNoStartServer; p->client = jack_client_open(p->cfg_client_name, open_options, NULL); if (!p->client) { MP_FATAL(ao, "cannot open server\n"); goto err_out; } jack_set_process_callback(p->client, outputaudio, ao); // list matching ports if connections should be made if (p->connect) { if (!port_name) port_flags |= JackPortIsPhysical; matching_ports = jack_get_ports(p->client, port_name, NULL, port_flags); if (!matching_ports || !matching_ports[0]) { MP_FATAL(ao, "no physical ports available\n"); goto err_out; } i = 1; p->num_ports = ao->channels.num; while (matching_ports[i]) i++; if (p->num_ports > i) p->num_ports = i; } // create out output ports for (i = 0; i < p->num_ports; i++) { char pname[30]; snprintf(pname, 30, "out_%d", i); p->ports[i] = jack_port_register(p->client, pname, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); if (!p->ports[i]) { MP_FATAL(ao, "not enough ports available\n"); goto err_out; } } if (jack_activate(p->client)) { MP_FATAL(ao, "activate failed\n"); goto err_out; } for (i = 0; i < p->num_ports; i++) { if (jack_connect(p->client, jack_port_name(p->ports[i]), matching_ports[i])) { MP_FATAL(ao, "connecting failed\n"); goto err_out; } } ao->samplerate = jack_get_sample_rate(p->client); jack_latency_range_t jack_latency_range; jack_port_get_latency_range(p->ports[0], JackPlaybackLatency, &jack_latency_range); p->jack_latency = (float)(jack_latency_range.max + jack_get_buffer_size(p->client)) / (float)ao->samplerate; p->callback_interval = 0; if (!ao_chmap_sel_get_def(ao, &sel, &ao->channels, p->num_ports)) goto err_out; ao->format = AF_FORMAT_FLOAT_NE; int unitsize = ao->channels.num * sizeof(float); p->outburst = (CHUNK_SIZE + unitsize - 1) / unitsize * unitsize; p->ring = mp_ring_new(p, NUM_CHUNKS * p->outburst); free(matching_ports); return 0; err_out: free(matching_ports); if (p->client) jack_client_close(p->client); return -1; }
static int init(struct ao *ao) { struct priv *p = ao->priv; struct mp_chmap_sel sel = {0}; jack_options_t open_options; ao->format = AF_FORMAT_FLOATP; switch (p->stdlayout) { case 0: mp_chmap_sel_add_waveext(&sel); break; case 1: mp_chmap_sel_add_alsa_def(&sel); break; default: mp_chmap_sel_add_any(&sel); } if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) goto err_chmap; open_options = JackNullOption; if (!p->autostart) open_options |= JackNoStartServer; p->client = jack_client_open(p->cfg_client_name, open_options, NULL); if (!p->client) { MP_FATAL(ao, "cannot open server\n"); goto err_client_open; } if (create_ports(ao, ao->channels.num)) goto err_create_ports; jack_set_process_callback(p->client, process, ao); if (jack_activate(p->client)) { MP_FATAL(ao, "activate failed\n"); goto err_activate; } ao->samplerate = jack_get_sample_rate(p->client); if (p->connect) if (connect_to_outports(ao)) goto err_connect; jack_latency_range_t jack_latency_range; jack_port_get_latency_range(p->ports[0], JackPlaybackLatency, &jack_latency_range); p->jack_latency = (float)(jack_latency_range.max + jack_get_buffer_size(p->client)) / (float)ao->samplerate; if (!ao_chmap_sel_get_def(ao, &sel, &ao->channels, p->num_ports)) goto err_chmap_sel_get_def; return 0; err_chmap_sel_get_def: err_connect: jack_deactivate(p->client); err_activate: err_create_ports: jack_client_close(p->client); err_client_open: err_chmap: return -1; }
static int refresh_devices_bare(SoundIoPrivate *si) { SoundIo *soundio = &si->pub; SoundIoJack *sij = &si->backend_data.jack; if (sij->is_shutdown) return SoundIoErrorBackendDisconnected; SoundIoDevicesInfo *devices_info = allocate<SoundIoDevicesInfo>(1); if (!devices_info) return SoundIoErrorNoMem; devices_info->default_output_index = -1; devices_info->default_input_index = -1; const char **port_names = jack_get_ports(sij->client, nullptr, nullptr, 0); if (!port_names) { soundio_destroy_devices_info(devices_info); return SoundIoErrorNoMem; } SoundIoList<SoundIoJackClient> clients = {0}; const char **port_name_ptr = port_names; for (; *port_name_ptr; port_name_ptr += 1) { const char *client_and_port_name = *port_name_ptr; int client_and_port_name_len = strlen(client_and_port_name); jack_port_t *jport = jack_port_by_name(sij->client, client_and_port_name); if (!jport) { // This refresh devices scan is already outdated. Just give up and // let refresh_devices be called again. jack_free(port_names); soundio_destroy_devices_info(devices_info); return SoundIoErrorInterrupted; } int flags = jack_port_flags(jport); const char *port_type = jack_port_type(jport); if (strcmp(port_type, JACK_DEFAULT_AUDIO_TYPE) != 0) { // we don't know how to support such a port continue; } SoundIoDeviceAim aim = (flags & JackPortIsInput) ? SoundIoDeviceAimOutput : SoundIoDeviceAimInput; bool is_physical = flags & JackPortIsPhysical; const char *client_name = nullptr; const char *port_name = nullptr; int client_name_len; int port_name_len; split_str(client_and_port_name, client_and_port_name_len, ':', &client_name, &client_name_len, &port_name, &port_name_len); if (!client_name || !port_name) { // device does not have colon, skip it continue; } SoundIoJackClient *client = find_or_create_client(&clients, aim, is_physical, client_name, client_name_len); if (!client) { jack_free(port_names); soundio_destroy_devices_info(devices_info); return SoundIoErrorNoMem; } if (client->port_count >= SOUNDIO_MAX_CHANNELS) { // we hit the channel limit, skip the leftovers continue; } SoundIoJackPort *port = &client->ports[client->port_count++]; port->full_name = client_and_port_name; port->full_name_len = client_and_port_name_len; port->name = port_name; port->name_len = port_name_len; port->channel_id = soundio_parse_channel_id(port_name, port_name_len); jack_latency_callback_mode_t latency_mode = (aim == SoundIoDeviceAimOutput) ? JackPlaybackLatency : JackCaptureLatency; jack_port_get_latency_range(jport, latency_mode, &port->latency_range); } for (int i = 0; i < clients.length; i += 1) { SoundIoJackClient *client = &clients.at(i); if (client->port_count <= 0) continue; SoundIoDevicePrivate *dev = allocate<SoundIoDevicePrivate>(1); if (!dev) { jack_free(port_names); soundio_destroy_devices_info(devices_info); return SoundIoErrorNoMem; } SoundIoDevice *device = &dev->pub; SoundIoDeviceJack *dj = &dev->backend_data.jack; int description_len = client->name_len + 3 + 2 * client->port_count; for (int port_index = 0; port_index < client->port_count; port_index += 1) { SoundIoJackPort *port = &client->ports[port_index]; description_len += port->name_len; } dev->destruct = destruct_device; device->ref_count = 1; device->soundio = soundio; device->is_raw = false; device->aim = client->aim; device->id = soundio_str_dupe(client->name, client->name_len); device->name = allocate<char>(description_len); device->current_format = SoundIoFormatFloat32NE; device->sample_rate_count = 1; device->sample_rates = &dev->prealloc_sample_rate_range; device->sample_rates[0].min = sij->sample_rate; device->sample_rates[0].max = sij->sample_rate; device->sample_rate_current = sij->sample_rate; device->software_latency_current = sij->period_size / (double) sij->sample_rate; device->software_latency_min = sij->period_size / (double) sij->sample_rate; device->software_latency_max = sij->period_size / (double) sij->sample_rate; dj->port_count = client->port_count; dj->ports = allocate<SoundIoDeviceJackPort>(dj->port_count); if (!device->id || !device->name || !dj->ports) { jack_free(port_names); soundio_device_unref(device); soundio_destroy_devices_info(devices_info); return SoundIoErrorNoMem; } for (int port_index = 0; port_index < client->port_count; port_index += 1) { SoundIoJackPort *port = &client->ports[port_index]; SoundIoDeviceJackPort *djp = &dj->ports[port_index]; djp->full_name = soundio_str_dupe(port->full_name, port->full_name_len); djp->full_name_len = port->full_name_len; djp->channel_id = port->channel_id; djp->latency_range = port->latency_range; if (!djp->full_name) { jack_free(port_names); soundio_device_unref(device); soundio_destroy_devices_info(devices_info); return SoundIoErrorNoMem; } } memcpy(device->name, client->name, client->name_len); memcpy(&device->name[client->name_len], ": ", 2); int index = client->name_len + 2; for (int port_index = 0; port_index < client->port_count; port_index += 1) { SoundIoJackPort *port = &client->ports[port_index]; memcpy(&device->name[index], port->name, port->name_len); index += port->name_len; if (port_index + 1 < client->port_count) { memcpy(&device->name[index], ", ", 2); index += 2; } } device->current_layout.channel_count = client->port_count; bool any_invalid = false; for (int port_index = 0; port_index < client->port_count; port_index += 1) { SoundIoJackPort *port = &client->ports[port_index]; device->current_layout.channels[port_index] = port->channel_id; any_invalid = any_invalid || (port->channel_id == SoundIoChannelIdInvalid); } if (any_invalid) { const struct SoundIoChannelLayout *layout = soundio_channel_layout_get_default(client->port_count); if (layout) device->current_layout = *layout; } else { soundio_channel_layout_detect_builtin(&device->current_layout); } device->layout_count = 1; device->layouts = &device->current_layout; device->format_count = 1; device->formats = &dev->prealloc_format; device->formats[0] = device->current_format; SoundIoList<SoundIoDevice *> *device_list; if (device->aim == SoundIoDeviceAimOutput) { device_list = &devices_info->output_devices; if (devices_info->default_output_index < 0 && client->is_physical) devices_info->default_output_index = device_list->length; } else { assert(device->aim == SoundIoDeviceAimInput); device_list = &devices_info->input_devices; if (devices_info->default_input_index < 0 && client->is_physical) devices_info->default_input_index = device_list->length; } if (device_list->append(device)) { soundio_device_unref(device); soundio_destroy_devices_info(devices_info); return SoundIoErrorNoMem; } } jack_free(port_names); soundio_destroy_devices_info(si->safe_devices_info); si->safe_devices_info = devices_info; return 0; }
static int handle_process(jack_nframes_t frames, void *arg) { jack_midi_data_t *buffer; jack_midi_event_t event; jack_nframes_t event_count; jack_nframes_t event_time; jack_nframes_t frame; size_t i; jack_nframes_t last_frame_time; jack_midi_data_t *message; jack_time_t microseconds; void *port_buffer; jack_time_t time; jack_midi_clear_buffer(jack_port_get_buffer(out_port, frames)); switch (process_state) { case 0: /* State: initializing */ switch (wait_semaphore(init_semaphore, 0)) { case -1: set_process_error(SOURCE_WAIT_SEMAPHORE, get_semaphore_error()); /* Fallthrough on purpose */ case 0: return 0; } highest_latency = 0; lowest_latency = 0; messages_received = 0; messages_sent = 0; process_state = 1; total_latency = 0; total_latency_time = 0; unexpected_messages = 0; xrun_count = 0; jack_port_get_latency_range(remote_in_port, JackCaptureLatency, &in_latency_range); jack_port_get_latency_range(remote_out_port, JackPlaybackLatency, &out_latency_range); goto send_message; case 1: /* State: processing */ port_buffer = jack_port_get_buffer(in_port, frames); event_count = jack_midi_get_event_count(port_buffer); last_frame_time = jack_last_frame_time(client); for (i = 0; i < event_count; i++) { jack_midi_event_get(&event, port_buffer, i); message = (messages_received % 2) ? message_2 : message_1; if ((event.size == message_size) && (! memcmp(message, event.buffer, message_size * sizeof(jack_midi_data_t)))) { goto found_message; } unexpected_messages++; } microseconds = jack_frames_to_time(client, last_frame_time) - last_activity_time; if ((microseconds / 1000000) >= timeout) { set_process_error(SOURCE_PROCESS, ERROR_MSG_TIMEOUT); } break; found_message: event_time = last_frame_time + event.time; frame = event_time - last_activity; time = jack_frames_to_time(client, event_time) - last_activity_time; if ((! highest_latency) || (frame > highest_latency)) { highest_latency = frame; highest_latency_time = time; } if ((! lowest_latency) || (frame < lowest_latency)) { lowest_latency = frame; lowest_latency_time = time; } latency_time_values[messages_received] = time; latency_values[messages_received] = frame; total_latency += frame; total_latency_time += time; messages_received++; if (messages_received == samples) { process_state = 2; if (! signal_semaphore(process_semaphore)) { /* Sigh ... */ die(SOURCE_SIGNAL_SEMAPHORE, get_semaphore_error()); } break; } send_message: frame = (jack_nframes_t) ((((double) rand()) / RAND_MAX) * frames); if (frame >= frames) { frame = frames - 1; } port_buffer = jack_port_get_buffer(out_port, frames); buffer = jack_midi_event_reserve(port_buffer, frame, message_size); if (buffer == NULL) { set_process_error(SOURCE_EVENT_RESERVE, ERROR_RESERVE); break; } message = (messages_sent % 2) ? message_2 : message_1; memcpy(buffer, message, message_size * sizeof(jack_midi_data_t)); last_activity = jack_last_frame_time(client) + frame; last_activity_time = jack_frames_to_time(client, last_activity); messages_sent++; case 2: /* State: finished - do nothing */ case -1: /* State: error - do nothing */ case -2: /* State: signalled - do nothing */ ; } return 0; }
int main (int argc, char *argv[]) { jack_client_t *client; jack_status_t status; jack_options_t options = JackNoStartServer; const char **ports, **connections; unsigned int i, j, k; int skip_port; int show_aliases = 0; int show_con = 0; int show_port_latency = 0; int show_total_latency = 0; int show_properties = 0; int show_type = 0; int c; int option_index; char* aliases[2]; char *server_name = NULL; jack_port_t *port; struct option long_options[] = { { "server", 1, 0, 's' }, { "aliases", 0, 0, 'A' }, { "connections", 0, 0, 'c' }, { "port-latency", 0, 0, 'l' }, { "total-latency", 0, 0, 'L' }, { "properties", 0, 0, 'p' }, { "type", 0, 0, 't' }, { "help", 0, 0, 'h' }, { "version", 0, 0, 'v' }, { 0, 0, 0, 0 } }; my_name = strrchr(argv[0], '/'); if (my_name == 0) { my_name = argv[0]; } else { my_name ++; } while ((c = getopt_long (argc, argv, "s:AclLphvt", long_options, &option_index)) >= 0) { switch (c) { case 's': server_name = (char *) malloc (sizeof (char) * strlen(optarg)); strcpy (server_name, optarg); options |= JackServerName; break; case 'A': aliases[0] = (char *) malloc (jack_port_name_size()); aliases[1] = (char *) malloc (jack_port_name_size()); show_aliases = 1; break; case 'c': show_con = 1; break; case 'l': show_port_latency = 1; break; case 'L': show_total_latency = 1; break; case 'p': show_properties = 1; break; case 't': show_type = 1; break; case 'h': show_usage (); return 1; break; case 'v': show_version (); return 1; break; default: show_usage (); return 1; break; } } /* Open a client connection to the JACK server. Starting a * new server only to list its ports seems pointless, so we * specify JackNoStartServer. */ //JOQ: need a new server name option client = jack_client_open ("lsp", options, &status, server_name); if (client == NULL) { if (status & JackServerFailed) { fprintf (stderr, "JACK server not running\n"); } else { fprintf (stderr, "jack_client_open() failed, " "status = 0x%2.0x\n", status); } return 1; } ports = jack_get_ports (client, NULL, NULL, 0); if (!ports) goto error; for (i = 0; ports && ports[i]; ++i) { // skip over any that don't match ALL of the strings presented at command line skip_port = 0; for(k = optind; k < argc; k++){ if(strstr(ports[i], argv[k]) == NULL ){ skip_port = 1; } } if (skip_port) continue; printf ("%s\n", ports[i]); port = jack_port_by_name (client, ports[i]); if (show_aliases) { int cnt; int i; cnt = jack_port_get_aliases (port, aliases); for (i = 0; i < cnt; ++i) { printf (" %s\n", aliases[i]); } } if (show_con) { if ((connections = jack_port_get_all_connections (client, jack_port_by_name(client, ports[i]))) != 0) { for (j = 0; connections[j]; j++) { printf (" %s\n", connections[j]); } free (connections); } } if (show_port_latency) { if (port) { jack_latency_range_t range; printf (" port latency = %" PRIu32 " frames\n", jack_port_get_latency (port)); jack_port_get_latency_range (port, JackPlaybackLatency, &range); printf (" port playback latency = [ %" PRIu32 " %" PRIu32 " ] frames\n", range.min, range.max); jack_port_get_latency_range (port, JackCaptureLatency, &range); printf (" port capture latency = [ %" PRIu32 " %" PRIu32 " ] frames\n", range.min, range.max); } } if (show_total_latency) { if (port) { printf (" total latency = %d frames\n", jack_port_get_total_latency (client, port)); } } if (show_properties) { if (port) { int flags = jack_port_flags (port); printf (" properties: "); if (flags & JackPortIsInput) { fputs ("input,", stdout); } if (flags & JackPortIsOutput) { fputs ("output,", stdout); } if (flags & JackPortCanMonitor) { fputs ("can-monitor,", stdout); } if (flags & JackPortIsPhysical) { fputs ("physical,", stdout); } if (flags & JackPortIsTerminal) { fputs ("terminal,", stdout); } putc ('\n', stdout); } } if (show_type) { if (port) { putc ('\t', stdout); fputs (jack_port_type (port), stdout); putc ('\n', stdout); } } } error: if (show_aliases) { free(aliases[0]); free(aliases[1]); } if (ports) jack_free (ports); jack_client_close (client); exit (0); }
static int init(int rate, const struct mp_chmap *channels, int format, int flags) { const char **matching_ports = NULL; char *port_name = NULL; char *client_name = NULL; int autostart = 0; int connect = 1; const opt_t subopts[] = { {"port", OPT_ARG_MSTRZ, &port_name, NULL}, {"name", OPT_ARG_MSTRZ, &client_name, NULL}, {"estimate", OPT_ARG_BOOL, &estimate, NULL}, {"autostart", OPT_ARG_BOOL, &autostart, NULL}, {"connect", OPT_ARG_BOOL, &connect, NULL}, {NULL} }; jack_options_t open_options = JackUseExactName; int port_flags = JackPortIsInput; int i; estimate = 1; if (subopt_parse(ao_subdevice, subopts) != 0) { print_help(); return 0; } struct mp_chmap_sel sel = {0}; mp_chmap_sel_add_waveext(&sel); if (!ao_chmap_sel_adjust(&ao_data, &sel, &ao_data.channels)) goto err_out; if (!client_name) { client_name = malloc(40); sprintf(client_name, "mpv [%d]", getpid()); } if (!autostart) open_options |= JackNoStartServer; client = jack_client_open(client_name, open_options, NULL); if (!client) { mp_msg(MSGT_AO, MSGL_FATAL, "[JACK] cannot open server\n"); goto err_out; } buffer = av_fifo_alloc(BUFFSIZE); jack_set_process_callback(client, outputaudio, 0); // list matching ports if connections should be made if (connect) { if (!port_name) port_flags |= JackPortIsPhysical; matching_ports = jack_get_ports(client, port_name, NULL, port_flags); if (!matching_ports || !matching_ports[0]) { mp_msg(MSGT_AO, MSGL_FATAL, "[JACK] no physical ports available\n"); goto err_out; } i = 1; num_ports = ao_data.channels.num; while (matching_ports[i]) i++; if (num_ports > i) num_ports = i; } // create out output ports for (i = 0; i < num_ports; i++) { char pname[30]; snprintf(pname, 30, "out_%d", i); ports[i] = jack_port_register(client, pname, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); if (!ports[i]) { mp_msg(MSGT_AO, MSGL_FATAL, "[JACK] not enough ports available\n"); goto err_out; } } if (jack_activate(client)) { mp_msg(MSGT_AO, MSGL_FATAL, "[JACK] activate failed\n"); goto err_out; } for (i = 0; i < num_ports; i++) { if (jack_connect(client, jack_port_name(ports[i]), matching_ports[i])) { mp_msg(MSGT_AO, MSGL_FATAL, "[JACK] connecting failed\n"); goto err_out; } } rate = jack_get_sample_rate(client); jack_latency_range_t jack_latency_range; jack_port_get_latency_range(ports[0], JackPlaybackLatency, &jack_latency_range); jack_latency = (float)(jack_latency_range.max + jack_get_buffer_size(client)) / (float)rate; callback_interval = 0; if (!ao_chmap_sel_get_def(&ao_data, &sel, &ao_data.channels, num_ports)) goto err_out; ao_data.samplerate = rate; ao_data.format = AF_FORMAT_FLOAT_NE; ao_data.bps = ao_data.channels.num * rate * sizeof(float); ao_data.buffersize = CHUNK_SIZE * NUM_CHUNKS; ao_data.outburst = CHUNK_SIZE; free(matching_ports); free(port_name); free(client_name); return 1; err_out: free(matching_ports); free(port_name); free(client_name); if (client) jack_client_close(client); av_fifo_free(buffer); buffer = NULL; return 0; }
int pa__init(pa_module*m) { struct userdata *u = NULL; pa_sample_spec ss; pa_channel_map map; pa_modargs *ma = NULL; jack_status_t status; const char *server_name, *client_name; uint32_t channels = 0; bool do_connect = true; unsigned i; const char **ports = NULL, **p; pa_sink_new_data data; jack_latency_range_t r; size_t n; pa_assert(m); jack_set_error_function(jack_error_func); if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { pa_log("Failed to parse module arguments."); goto fail; } if (pa_modargs_get_value_boolean(ma, "connect", &do_connect) < 0) { pa_log("Failed to parse connect= argument."); goto fail; } server_name = pa_modargs_get_value(ma, "server_name", NULL); client_name = pa_modargs_get_value(ma, "client_name", "PulseAudio JACK Sink"); m->userdata = u = pa_xnew0(struct userdata, 1); u->core = m->core; u->module = m; u->saved_frame_time_valid = false; u->rtpoll = pa_rtpoll_new(); pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); /* The queue linking the JACK thread and our RT thread */ u->jack_msgq = pa_asyncmsgq_new(0); /* The msgq from the JACK RT thread should have an even higher * priority than the normal message queues, to match the guarantee * all other drivers make: supplying the audio device with data is * the top priority -- and as long as that is possible we don't do * anything else */ u->rtpoll_item = pa_rtpoll_item_new_asyncmsgq_read(u->rtpoll, PA_RTPOLL_EARLY-1, u->jack_msgq); if (!(u->client = jack_client_open(client_name, server_name ? JackServerName : JackNullOption, &status, server_name))) { pa_log("jack_client_open() failed."); goto fail; } ports = jack_get_ports(u->client, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortIsPhysical|JackPortIsInput); channels = 0; if (ports) for (p = ports; *p; p++) channels++; if (!channels) channels = m->core->default_sample_spec.channels; if (pa_modargs_get_value_u32(ma, "channels", &channels) < 0 || !pa_channels_valid(channels)) { pa_log("Failed to parse channels= argument."); goto fail; } if (channels == m->core->default_channel_map.channels) map = m->core->default_channel_map; else pa_channel_map_init_extend(&map, channels, PA_CHANNEL_MAP_ALSA); if (pa_modargs_get_channel_map(ma, NULL, &map) < 0 || map.channels != channels) { pa_log("Failed to parse channel_map= argument."); goto fail; } pa_log_info("Successfully connected as '%s'", jack_get_client_name(u->client)); u->channels = ss.channels = (uint8_t) channels; ss.rate = jack_get_sample_rate(u->client); ss.format = PA_SAMPLE_FLOAT32NE; pa_assert(pa_sample_spec_valid(&ss)); for (i = 0; i < ss.channels; i++) { if (!(u->port[i] = jack_port_register(u->client, pa_channel_position_to_string(map.map[i]), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput|JackPortIsTerminal, 0))) { pa_log("jack_port_register() failed."); goto fail; } } pa_sink_new_data_init(&data); data.driver = __FILE__; data.module = m; pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME)); pa_sink_new_data_set_sample_spec(&data, &ss); pa_sink_new_data_set_channel_map(&data, &map); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "jack"); if (server_name) pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server_name); pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Jack sink (%s)", jack_get_client_name(u->client)); pa_proplist_sets(data.proplist, "jack.client_name", jack_get_client_name(u->client)); if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) { pa_log("Invalid properties"); pa_sink_new_data_done(&data); goto fail; } u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY); pa_sink_new_data_done(&data); if (!u->sink) { pa_log("Failed to create sink."); goto fail; } u->sink->parent.process_msg = sink_process_msg; u->sink->userdata = u; pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); pa_sink_set_rtpoll(u->sink, u->rtpoll); pa_sink_set_max_request(u->sink, jack_get_buffer_size(u->client) * pa_frame_size(&u->sink->sample_spec)); jack_set_process_callback(u->client, jack_process, u); jack_on_shutdown(u->client, jack_shutdown, u); jack_set_thread_init_callback(u->client, jack_init, u); jack_set_buffer_size_callback(u->client, jack_buffer_size, u); if (!(u->thread = pa_thread_new("jack-sink", thread_func, u))) { pa_log("Failed to create thread."); goto fail; } if (jack_activate(u->client)) { pa_log("jack_activate() failed"); goto fail; } if (do_connect) { for (i = 0, p = ports; i < ss.channels; i++, p++) { if (!p || !*p) { pa_log("Not enough physical output ports, leaving unconnected."); break; } pa_log_info("Connecting %s to %s", jack_port_name(u->port[i]), *p); if (jack_connect(u->client, jack_port_name(u->port[i]), *p)) { pa_log("Failed to connect %s to %s, leaving unconnected.", jack_port_name(u->port[i]), *p); break; } } } jack_port_get_latency_range(u->port[0], JackPlaybackLatency, &r); n = r.max * pa_frame_size(&u->sink->sample_spec); pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(n, &u->sink->sample_spec)); pa_sink_put(u->sink); if (ports) jack_free(ports); pa_modargs_free(ma); return 0; fail: if (ma) pa_modargs_free(ma); if (ports) jack_free(ports); pa__done(m); return -1; }
static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *memchunk) { struct userdata *u = PA_SINK(o)->userdata; switch (code) { case SINK_MESSAGE_RENDER: /* Handle the request from the JACK thread */ if (u->sink->thread_info.state == PA_SINK_RUNNING) { pa_memchunk chunk; size_t nbytes; void *p; pa_assert(offset > 0); nbytes = (size_t) offset * pa_frame_size(&u->sink->sample_spec); pa_sink_render_full(u->sink, nbytes, &chunk); p = pa_memblock_acquire_chunk(&chunk); pa_deinterleave(p, u->buffer, u->channels, sizeof(float), (unsigned) offset); pa_memblock_release(chunk.memblock); pa_memblock_unref(chunk.memblock); } else { unsigned c; pa_sample_spec ss; /* Humm, we're not RUNNING, hence let's write some silence */ /* This can happen if we're paused, or during shutdown (when we're unlinked but jack is still running). */ ss = u->sink->sample_spec; ss.channels = 1; for (c = 0; c < u->channels; c++) pa_silence_memory(u->buffer[c], (size_t) offset * pa_sample_size(&ss), &ss); } u->frames_in_buffer = (jack_nframes_t) offset; u->saved_frame_time = * (jack_nframes_t*) data; u->saved_frame_time_valid = true; return 0; case SINK_MESSAGE_BUFFER_SIZE: pa_sink_set_max_request_within_thread(u->sink, (size_t) offset * pa_frame_size(&u->sink->sample_spec)); return 0; case SINK_MESSAGE_ON_SHUTDOWN: pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); return 0; case PA_SINK_MESSAGE_GET_LATENCY: { jack_nframes_t l, ft, d; jack_latency_range_t r; size_t n; /* This is the "worst-case" latency */ jack_port_get_latency_range(u->port[0], JackPlaybackLatency, &r); l = r.max + u->frames_in_buffer; if (u->saved_frame_time_valid) { /* Adjust the worst case latency by the time that * passed since we last handed data to JACK */ ft = jack_frame_time(u->client); d = ft > u->saved_frame_time ? ft - u->saved_frame_time : 0; l = l > d ? l - d : 0; } /* Convert it to usec */ n = l * pa_frame_size(&u->sink->sample_spec); *((pa_usec_t*) data) = pa_bytes_to_usec(n, &u->sink->sample_spec); return 0; } } return pa_sink_process_msg(o, code, data, offset, memchunk); }