int main(int argc, char *argv[]) { pa_mempool *pool; pa_sample_spec a, b; pa_cvolume v; pa_log_set_level(PA_LOG_DEBUG); pa_assert_se(pool = pa_mempool_new(FALSE, 0)); a.channels = b.channels = 1; a.rate = b.rate = 44100; v.channels = a.channels; v.values[0] = pa_sw_volume_from_linear(0.5); for (a.format = 0; a.format < PA_SAMPLE_MAX; a.format ++) { for (b.format = 0; b.format < PA_SAMPLE_MAX; b.format ++) { pa_resampler *forth, *back; pa_memchunk i, j, k; printf("=== %s -> %s -> %s -> /2\n", pa_sample_format_to_string(a.format), pa_sample_format_to_string(b.format), pa_sample_format_to_string(a.format)); pa_assert_se(forth = pa_resampler_new(pool, &a, NULL, &b, NULL, PA_RESAMPLER_AUTO, 0)); pa_assert_se(back = pa_resampler_new(pool, &b, NULL, &a, NULL, PA_RESAMPLER_AUTO, 0)); i.memblock = generate_block(pool, &a); i.length = pa_memblock_get_length(i.memblock); i.index = 0; pa_resampler_run(forth, &i, &j); pa_resampler_run(back, &j, &k); printf("before: "); dump_block(&a, &i); printf("after : "); dump_block(&b, &j); printf("reverse: "); dump_block(&a, &k); pa_memblock_unref(j.memblock); pa_memblock_unref(k.memblock); pa_volume_memchunk(&i, &a, &v); printf("volume: "); dump_block(&a, &i); pa_memblock_unref(i.memblock); pa_resampler_free(forth); pa_resampler_free(back); } } pa_mempool_free(pool); return 0; }
/* Called from thread context */ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) { size_t length; size_t limit, mbs = 0; pa_source_output_assert_ref(o); pa_source_output_assert_io_context(o); pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state)); pa_assert(chunk); pa_assert(pa_frame_aligned(chunk->length, &o->source->sample_spec)); if (!o->push || o->thread_info.state == PA_SOURCE_OUTPUT_CORKED) return; pa_assert(o->thread_info.state == PA_SOURCE_OUTPUT_RUNNING); if (pa_memblockq_push(o->thread_info.delay_memblockq, chunk) < 0) { pa_log_debug("Delay queue overflow!"); pa_memblockq_seek(o->thread_info.delay_memblockq, (int64_t) chunk->length, PA_SEEK_RELATIVE, TRUE); } limit = o->process_rewind ? 0 : o->source->thread_info.max_rewind; if (limit > 0 && o->source->monitor_of) { pa_usec_t latency; size_t n; /* Hmm, check the latency for knowing how much of the buffered * data is actually still unplayed and might hence still * change. This is suboptimal. Ideally we'd have a call like * pa_sink_get_changeable_size() or so that tells us how much * of the queued data is actually still changeable. Hence * FIXME! */ latency = pa_sink_get_latency_within_thread(o->source->monitor_of); n = pa_usec_to_bytes(latency, &o->source->sample_spec); if (n < limit) limit = n; } /* Implement the delay queue */ while ((length = pa_memblockq_get_length(o->thread_info.delay_memblockq)) > limit) { pa_memchunk qchunk; length -= limit; pa_assert_se(pa_memblockq_peek(o->thread_info.delay_memblockq, &qchunk) >= 0); if (qchunk.length > length) qchunk.length = length; pa_assert(qchunk.length > 0); if (!o->thread_info.resampler) o->push(o, &qchunk); else { pa_memchunk rchunk; if (mbs == 0) mbs = pa_resampler_max_block_size(o->thread_info.resampler); if (qchunk.length > mbs) qchunk.length = mbs; pa_resampler_run(o->thread_info.resampler, &qchunk, &rchunk); if (rchunk.length > 0) o->push(o, &rchunk); if (rchunk.memblock) pa_memblock_unref(rchunk.memblock); } pa_memblock_unref(qchunk.memblock); pa_memblockq_drop(o->thread_info.delay_memblockq, qchunk.length); } }
int main(int argc, char *argv[]) { pa_mempool *pool = NULL; pa_sample_spec a, b; int ret = 1, c; bool all_formats = true; pa_resample_method_t method; int seconds; unsigned crossover_freq = 120; static const struct option long_options[] = { {"help", 0, NULL, 'h'}, {"verbose", 0, NULL, 'v'}, {"version", 0, NULL, ARG_VERSION}, {"from-rate", 1, NULL, ARG_FROM_SAMPLERATE}, {"from-format", 1, NULL, ARG_FROM_SAMPLEFORMAT}, {"from-channels", 1, NULL, ARG_FROM_CHANNELS}, {"to-rate", 1, NULL, ARG_TO_SAMPLERATE}, {"to-format", 1, NULL, ARG_TO_SAMPLEFORMAT}, {"to-channels", 1, NULL, ARG_TO_CHANNELS}, {"seconds", 1, NULL, ARG_SECONDS}, {"resample-method", 1, NULL, ARG_RESAMPLE_METHOD}, {"dump-resample-methods", 0, NULL, ARG_DUMP_RESAMPLE_METHODS}, {NULL, 0, NULL, 0} }; setlocale(LC_ALL, ""); #ifdef ENABLE_NLS bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR); #endif pa_log_set_level(PA_LOG_WARN); if (!getenv("MAKE_CHECK")) pa_log_set_level(PA_LOG_INFO); pa_assert_se(pool = pa_mempool_new(false, 0)); a.channels = b.channels = 1; a.rate = b.rate = 44100; a.format = b.format = PA_SAMPLE_S16LE; method = PA_RESAMPLER_AUTO; seconds = 60; while ((c = getopt_long(argc, argv, "hv", long_options, NULL)) != -1) { switch (c) { case 'h' : help(argv[0]); ret = 0; goto quit; case 'v': pa_log_set_level(PA_LOG_DEBUG); break; case ARG_VERSION: printf(_("%s %s\n"), argv[0], PACKAGE_VERSION); ret = 0; goto quit; case ARG_DUMP_RESAMPLE_METHODS: dump_resample_methods(); ret = 0; goto quit; case ARG_FROM_CHANNELS: a.channels = (uint8_t) atoi(optarg); break; case ARG_FROM_SAMPLEFORMAT: a.format = pa_parse_sample_format(optarg); all_formats = false; break; case ARG_FROM_SAMPLERATE: a.rate = (uint32_t) atoi(optarg); break; case ARG_TO_CHANNELS: b.channels = (uint8_t) atoi(optarg); break; case ARG_TO_SAMPLEFORMAT: b.format = pa_parse_sample_format(optarg); all_formats = false; break; case ARG_TO_SAMPLERATE: b.rate = (uint32_t) atoi(optarg); break; case ARG_SECONDS: seconds = atoi(optarg); break; case ARG_RESAMPLE_METHOD: if (*optarg == '\0' || pa_streq(optarg, "help")) { dump_resample_methods(); ret = 0; goto quit; } method = pa_parse_resample_method(optarg); break; default: goto quit; } } ret = 0; pa_assert_se(pool = pa_mempool_new(false, 0)); if (!all_formats) { pa_resampler *resampler; pa_memchunk i, j; pa_usec_t ts; pa_log_debug("Compilation CFLAGS: %s", PA_CFLAGS); pa_log_debug("=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)", seconds, a.rate, a.channels, pa_sample_format_to_string(a.format), b.rate, b.channels, pa_sample_format_to_string(b.format)); ts = pa_rtclock_now(); pa_assert_se(resampler = pa_resampler_new(pool, &a, NULL, &b, NULL, crossover_freq, method, 0)); pa_log_info("init: %llu", (long long unsigned)(pa_rtclock_now() - ts)); i.memblock = pa_memblock_new(pool, pa_usec_to_bytes(1*PA_USEC_PER_SEC, &a)); ts = pa_rtclock_now(); i.length = pa_memblock_get_length(i.memblock); i.index = 0; while (seconds--) { pa_resampler_run(resampler, &i, &j); if (j.memblock) pa_memblock_unref(j.memblock); } pa_log_info("resampling: %llu", (long long unsigned)(pa_rtclock_now() - ts)); pa_memblock_unref(i.memblock); pa_resampler_free(resampler); goto quit; } for (a.format = 0; a.format < PA_SAMPLE_MAX; a.format ++) { for (b.format = 0; b.format < PA_SAMPLE_MAX; b.format ++) { pa_resampler *forth, *back; pa_memchunk i, j, k; pa_log_debug("=== %s -> %s -> %s -> /2", pa_sample_format_to_string(a.format), pa_sample_format_to_string(b.format), pa_sample_format_to_string(a.format)); pa_assert_se(forth = pa_resampler_new(pool, &a, NULL, &b, NULL, crossover_freq, method, 0)); pa_assert_se(back = pa_resampler_new(pool, &b, NULL, &a, NULL, crossover_freq, method, 0)); i.memblock = generate_block(pool, &a); i.length = pa_memblock_get_length(i.memblock); i.index = 0; pa_resampler_run(forth, &i, &j); pa_resampler_run(back, &j, &k); dump_block("before", &a, &i); dump_block("after", &b, &j); dump_block("reverse", &a, &k); pa_memblock_unref(i.memblock); pa_memblock_unref(j.memblock); pa_memblock_unref(k.memblock); pa_resampler_free(forth); pa_resampler_free(back); } } quit: if (pool) pa_mempool_free(pool); return ret; }
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; }