char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map) { unsigned channel; pa_bool_t first = TRUE; char *e; pa_assert(s); pa_assert(l > 0); pa_assert(map); pa_init_i18n(); if (!pa_channel_map_valid(map)) { pa_snprintf(s, l, _("(invalid)")); return s; } *(e = s) = 0; for (channel = 0; channel < map->channels && l > 1; channel++) { l -= pa_snprintf(e, l, "%s%s", first ? "" : ",", pa_channel_position_to_string(map->map[channel])); e = strchr(e, 0); first = FALSE; } return s; }
char *pa_cvolume_snprint_verbose(char *s, size_t l, const pa_cvolume *c, const pa_channel_map *map, int print_dB) { char *current = s; bool first = true; pa_assert(s); pa_assert(l > 0); pa_assert(c); pa_init_i18n(); if (!pa_cvolume_valid(c)) { pa_snprintf(s, l, _("(invalid)")); return s; } pa_assert(!map || (map->channels == c->channels)); pa_assert(!map || pa_channel_map_valid(map)); current[0] = 0; for (unsigned channel = 0; channel < c->channels && l > 1; channel++) { char channel_position[32]; size_t bytes_printed; char buf[PA_VOLUME_SNPRINT_VERBOSE_MAX]; if (map) pa_snprintf(channel_position, sizeof(channel_position), "%s", pa_channel_position_to_string(map->map[channel])); else pa_snprintf(channel_position, sizeof(channel_position), "%u", channel); bytes_printed = pa_snprintf(current, l, "%s%s: %s", first ? "" : ", ", channel_position, pa_volume_snprint_verbose(buf, sizeof(buf), c->values[channel], print_dB)); l -= bytes_printed; current += bytes_printed; first = false; } return s; }
APULSE_EXPORT char * pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map) { trace_info("F %s s=%p, l=%d, map=%p\n", __func__, s, (int)l, map); char *ptr = s; if (!pa_channel_map_valid(map)) { snprintf(s, l, "(invalid)"); return s; } for (int c = 0; c < map->channels && l > 1; c ++) { int adv = snprintf(ptr, l, "%s%s", (c == 0) ? "" : ",", pa_channel_position_to_string(map->map[c])); ptr += adv; l -= adv; } return s; }
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; pa_bool_t do_connect = TRUE; unsigned i; const char **ports = NULL, **p; pa_sink_new_data data; 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; 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 || channels <= 0 || channels > PA_CHANNELS_MAX) { 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(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) { 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; } } } pa_sink_put(u->sink); free(ports); pa_modargs_free(ma); return 0; fail: if (ma) pa_modargs_free(ma); free(ports); pa__done(m); return -1; }
int pa__init(pa_module*m) { struct userdata *u; pa_sample_spec ss, sink_input_ss; pa_channel_map map, sink_input_map; pa_modargs *ma; pa_sink *master=NULL; pa_sink_input_new_data sink_input_data; pa_sink_new_data sink_data; bool use_volume_sharing = true; bool force_flat_volume = false; pa_memchunk silence; const char *hrir_file; unsigned i, j, found_channel_left, found_channel_right; float *hrir_data; pa_sample_spec hrir_ss; pa_channel_map hrir_map; pa_sample_spec hrir_temp_ss; pa_memchunk hrir_temp_chunk, hrir_temp_chunk_resampled; pa_resampler *resampler; size_t hrir_copied_length, hrir_total_length; hrir_temp_chunk.memblock = NULL; hrir_temp_chunk_resampled.memblock = NULL; pa_assert(m); if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { pa_log("Failed to parse module arguments."); goto fail; } if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SINK))) { pa_log("Master sink not found"); goto fail; } pa_assert(master); u = pa_xnew0(struct userdata, 1); u->module = m; m->userdata = u; /* Initialize hrir and input buffer */ /* this is the hrir file for the left ear! */ if (!(hrir_file = pa_modargs_get_value(ma, "hrir", NULL))) { pa_log("The mandatory 'hrir' module argument is missing."); goto fail; } if (pa_sound_file_load(master->core->mempool, hrir_file, &hrir_temp_ss, &hrir_map, &hrir_temp_chunk, NULL) < 0) { pa_log("Cannot load hrir file."); goto fail; } /* sample spec / map of hrir */ hrir_ss.format = PA_SAMPLE_FLOAT32; hrir_ss.rate = master->sample_spec.rate; hrir_ss.channels = hrir_temp_ss.channels; /* sample spec of sink */ ss = hrir_ss; map = hrir_map; if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) { pa_log("Invalid sample format specification or channel map"); goto fail; } ss.format = PA_SAMPLE_FLOAT32; hrir_ss.rate = ss.rate; u->channels = ss.channels; if (pa_modargs_get_value_boolean(ma, "use_volume_sharing", &use_volume_sharing) < 0) { pa_log("use_volume_sharing= expects a boolean argument"); goto fail; } if (pa_modargs_get_value_boolean(ma, "force_flat_volume", &force_flat_volume) < 0) { pa_log("force_flat_volume= expects a boolean argument"); goto fail; } if (use_volume_sharing && force_flat_volume) { pa_log("Flat volume can't be forced when using volume sharing."); goto fail; } /* sample spec / map of sink input */ pa_channel_map_init_stereo(&sink_input_map); sink_input_ss.channels = 2; sink_input_ss.format = PA_SAMPLE_FLOAT32; sink_input_ss.rate = ss.rate; u->sink_fs = pa_frame_size(&ss); u->fs = pa_frame_size(&sink_input_ss); /* Create sink */ pa_sink_new_data_init(&sink_data); sink_data.driver = __FILE__; sink_data.module = m; if (!(sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL)))) sink_data.name = pa_sprintf_malloc("%s.vsurroundsink", master->name); pa_sink_new_data_set_sample_spec(&sink_data, &ss); pa_sink_new_data_set_channel_map(&sink_data, &map); pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name); pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter"); pa_proplist_sets(sink_data.proplist, "device.vsurroundsink.name", sink_data.name); if (pa_modargs_get_proplist(ma, "sink_properties", sink_data.proplist, PA_UPDATE_REPLACE) < 0) { pa_log("Invalid properties"); pa_sink_new_data_done(&sink_data); goto fail; } if ((u->auto_desc = !pa_proplist_contains(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION))) { const char *z; z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION); pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Virtual Surround Sink %s on %s", sink_data.name, z ? z : master->name); } u->sink = pa_sink_new(m->core, &sink_data, (master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY)) | (use_volume_sharing ? PA_SINK_SHARE_VOLUME_WITH_MASTER : 0)); pa_sink_new_data_done(&sink_data); if (!u->sink) { pa_log("Failed to create sink."); goto fail; } u->sink->parent.process_msg = sink_process_msg_cb; u->sink->set_state = sink_set_state_cb; u->sink->update_requested_latency = sink_update_requested_latency_cb; u->sink->request_rewind = sink_request_rewind_cb; pa_sink_set_set_mute_callback(u->sink, sink_set_mute_cb); if (!use_volume_sharing) { pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb); pa_sink_enable_decibel_volume(u->sink, true); } /* Normally this flag would be enabled automatically be we can force it. */ if (force_flat_volume) u->sink->flags |= PA_SINK_FLAT_VOLUME; u->sink->userdata = u; pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq); /* Create sink input */ pa_sink_input_new_data_init(&sink_input_data); sink_input_data.driver = __FILE__; sink_input_data.module = m; pa_sink_input_new_data_set_sink(&sink_input_data, master, false); sink_input_data.origin_sink = u->sink; pa_proplist_setf(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Virtual Surround Sink Stream from %s", pa_proplist_gets(u->sink->proplist, PA_PROP_DEVICE_DESCRIPTION)); pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter"); pa_sink_input_new_data_set_sample_spec(&sink_input_data, &sink_input_ss); pa_sink_input_new_data_set_channel_map(&sink_input_data, &sink_input_map); pa_sink_input_new(&u->sink_input, m->core, &sink_input_data); pa_sink_input_new_data_done(&sink_input_data); if (!u->sink_input) goto fail; u->sink_input->pop = sink_input_pop_cb; u->sink_input->process_rewind = sink_input_process_rewind_cb; u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; u->sink_input->update_max_request = sink_input_update_max_request_cb; u->sink_input->update_sink_latency_range = sink_input_update_sink_latency_range_cb; u->sink_input->update_sink_fixed_latency = sink_input_update_sink_fixed_latency_cb; u->sink_input->kill = sink_input_kill_cb; u->sink_input->attach = sink_input_attach_cb; u->sink_input->detach = sink_input_detach_cb; u->sink_input->state_change = sink_input_state_change_cb; u->sink_input->moving = sink_input_moving_cb; u->sink_input->volume_changed = use_volume_sharing ? NULL : sink_input_volume_changed_cb; u->sink_input->mute_changed = sink_input_mute_changed_cb; u->sink_input->userdata = u; u->sink->input_to_master = u->sink_input; pa_sink_input_get_silence(u->sink_input, &silence); u->memblockq = pa_memblockq_new("module-virtual-surround-sink memblockq", 0, MEMBLOCKQ_MAXLENGTH, 0, &sink_input_ss, 1, 1, 0, &silence); pa_memblock_unref(silence.memblock); /* resample hrir */ resampler = pa_resampler_new(u->sink->core->mempool, &hrir_temp_ss, &hrir_map, &hrir_ss, &hrir_map, PA_RESAMPLER_SRC_SINC_BEST_QUALITY, PA_RESAMPLER_NO_REMAP); u->hrir_samples = hrir_temp_chunk.length / pa_frame_size(&hrir_temp_ss) * hrir_ss.rate / hrir_temp_ss.rate; if (u->hrir_samples > 64) { u->hrir_samples = 64; pa_log("The (resampled) hrir contains more than 64 samples. Only the first 64 samples will be used to limit processor usage."); } hrir_total_length = u->hrir_samples * pa_frame_size(&hrir_ss); u->hrir_channels = hrir_ss.channels; u->hrir_data = (float *) pa_xmalloc(hrir_total_length); hrir_copied_length = 0; /* add silence to the hrir until we get enough samples out of the resampler */ while (hrir_copied_length < hrir_total_length) { pa_resampler_run(resampler, &hrir_temp_chunk, &hrir_temp_chunk_resampled); if (hrir_temp_chunk.memblock != hrir_temp_chunk_resampled.memblock) { /* Silence input block */ pa_silence_memblock(hrir_temp_chunk.memblock, &hrir_temp_ss); } if (hrir_temp_chunk_resampled.memblock) { /* Copy hrir data */ hrir_data = (float *) pa_memblock_acquire(hrir_temp_chunk_resampled.memblock); if (hrir_total_length - hrir_copied_length >= hrir_temp_chunk_resampled.length) { memcpy(u->hrir_data + hrir_copied_length, hrir_data, hrir_temp_chunk_resampled.length); hrir_copied_length += hrir_temp_chunk_resampled.length; } else { memcpy(u->hrir_data + hrir_copied_length, hrir_data, hrir_total_length - hrir_copied_length); hrir_copied_length = hrir_total_length; } pa_memblock_release(hrir_temp_chunk_resampled.memblock); pa_memblock_unref(hrir_temp_chunk_resampled.memblock); hrir_temp_chunk_resampled.memblock = NULL; } } pa_resampler_free(resampler); pa_memblock_unref(hrir_temp_chunk.memblock); hrir_temp_chunk.memblock = NULL; if (hrir_map.channels < map.channels) { pa_log("hrir file does not have enough channels!"); goto fail; } normalize_hrir(u); /* create mapping between hrir and input */ u->mapping_left = (unsigned *) pa_xnew0(unsigned, u->channels); u->mapping_right = (unsigned *) pa_xnew0(unsigned, u->channels); for (i = 0; i < map.channels; i++) { found_channel_left = 0; found_channel_right = 0; for (j = 0; j < hrir_map.channels; j++) { if (hrir_map.map[j] == map.map[i]) { u->mapping_left[i] = j; found_channel_left = 1; } if (hrir_map.map[j] == mirror_channel(map.map[i])) { u->mapping_right[i] = j; found_channel_right = 1; } } if (!found_channel_left) { pa_log("Cannot find mapping for channel %s", pa_channel_position_to_string(map.map[i])); goto fail; } if (!found_channel_right) { pa_log("Cannot find mapping for channel %s", pa_channel_position_to_string(mirror_channel(map.map[i]))); goto fail; } } u->input_buffer = pa_xmalloc0(u->hrir_samples * u->sink_fs); u->input_buffer_offset = 0; pa_sink_put(u->sink); pa_sink_input_put(u->sink_input); pa_modargs_free(ma); return 0; fail: if (hrir_temp_chunk.memblock) pa_memblock_unref(hrir_temp_chunk.memblock); if (hrir_temp_chunk_resampled.memblock) pa_memblock_unref(hrir_temp_chunk_resampled.memblock); if (ma) pa_modargs_free(ma); pa__done(m); return -1; }