void AudioDriverPulseAudio::detect_channels(bool capture) { pa_channel_map_init_stereo(capture ? &pa_rec_map : &pa_map); String device = capture ? capture_device_name : device_name; if (device == "Default") { // Get the default output device name pa_status = 0; pa_operation *pa_op = pa_context_get_server_info(pa_ctx, &AudioDriverPulseAudio::pa_server_info_cb, (void *)this); if (pa_op) { while (pa_status == 0) { int ret = pa_mainloop_iterate(pa_ml, 1, NULL); if (ret < 0) { ERR_PRINT("pa_mainloop_iterate error"); } } pa_operation_unref(pa_op); } else { ERR_PRINT("pa_context_get_server_info error"); } } char dev[1024]; if (device == "Default") { strcpy(dev, capture ? capture_default_device.utf8().get_data() : default_device.utf8().get_data()); } else { strcpy(dev, device.utf8().get_data()); } // Now using the device name get the amount of channels pa_status = 0; pa_operation *pa_op; if (capture) { pa_op = pa_context_get_source_info_by_name(pa_ctx, dev, &AudioDriverPulseAudio::pa_source_info_cb, (void *)this); } else { pa_op = pa_context_get_sink_info_by_name(pa_ctx, dev, &AudioDriverPulseAudio::pa_sink_info_cb, (void *)this); } if (pa_op) { while (pa_status == 0) { int ret = pa_mainloop_iterate(pa_ml, 1, NULL); if (ret < 0) { ERR_PRINT("pa_mainloop_iterate error"); } } pa_operation_unref(pa_op); } else { if (capture) { ERR_PRINT("pa_context_get_source_info_by_name error"); } else { ERR_PRINT("pa_context_get_sink_info_by_name error"); } } }
void RageSoundDriver_PulseAudio::m_InitStream(void) { int error; pa_sample_spec ss; pa_channel_map map; /* init sample spec */ ss.format = PA_SAMPLE_S16LE; ss.channels = 2; ss.rate = PREFSMAN->m_iSoundPreferredSampleRate; if(ss.rate == 0) { ss.rate = 44100; } /* init channel map */ pa_channel_map_init_stereo(&map); /* check sample spec */ if(!pa_sample_spec_valid(&ss)) { if(asprintf(&m_Error, "invalid sample spec!") == -1) { m_Error = nullptr; } m_Sem.Post(); return; } /* log the used sample spec */ char specstring[PA_SAMPLE_SPEC_SNPRINT_MAX]; pa_sample_spec_snprint(specstring, sizeof(specstring), &ss); LOG->Trace("Pulse: using sample spec: %s", specstring); /* create the stream */ LOG->Trace("Pulse: pa_stream_new()..."); m_PulseStream = pa_stream_new(m_PulseCtx, "Stepmania Audio", &ss, &map); if(m_PulseStream == nullptr) { if(asprintf(&m_Error, "pa_stream_new(): %s", pa_strerror(pa_context_errno(m_PulseCtx))) == -1) { m_Error = nullptr; } m_Sem.Post(); return; } /* set the write callback, it will be called when the sound server * needs data */ pa_stream_set_write_callback(m_PulseStream, StaticStreamWriteCb, this); /* set the state callback, it will be called the the stream state will * change */ pa_stream_set_state_callback(m_PulseStream, StaticStreamStateCb, this); /* configure attributes of the stream */ pa_buffer_attr attr; memset(&attr, 0x00, sizeof(attr)); /* tlength: Target length of the buffer. * * "The server tries to assure that at least tlength bytes are always * available in the per-stream server-side playback buffer. It is * recommended to set this to (uint32_t) -1, which will initialize * this to a value that is deemed sensible by the server. However, * this value will default to something like 2s, i.e. for applications * that have specific latency requirements this value should be set to * the maximum latency that the application can deal with." * * We don't want the default here, we want a small latency. * We use pa_usec_to_bytes() to convert a latency to a buffer size. */ attr.tlength = pa_usec_to_bytes(20*PA_USEC_PER_MSEC, &ss); /* maxlength: Maximum length of the buffer * * "Setting this to (uint32_t) -1 will initialize this to the maximum * value supported by server, which is recommended." * * (uint32_t)-1 is NOT working here, setting it to tlength*2, like * openal-soft-pulseaudio does. */ attr.maxlength = attr.tlength*2; /* minreq: Minimum request * * "The server does not request less than minreq bytes from the client, * instead waits until the buffer is free enough to request more bytes * at once. It is recommended to set this to (uint32_t) -1, which will * initialize this to a value that is deemed sensible by the server." * * (uint32_t)-1 is NOT working here, setting it to 0, like * openal-soft-pulseaudio does. */ attr.minreq = 0; /* prebuf: Pre-buffering * * "The server does not start with playback before at least prebuf * bytes are available in the buffer. It is recommended to set this * to (uint32_t) -1, which will initialize this to the same value as * tlength" */ attr.prebuf = (uint32_t)-1; /* log the used target buffer length */ LOG->Trace("Pulse: using target buffer length of %i bytes", attr.tlength); /* connect the stream for playback */ LOG->Trace("Pulse: pa_stream_connect_playback()..."); error = pa_stream_connect_playback(m_PulseStream, nullptr, &attr, PA_STREAM_AUTO_TIMING_UPDATE, nullptr, nullptr); if(error < 0) { if(asprintf(&m_Error, "pa_stream_connect_playback(): %s", pa_strerror(pa_context_errno(m_PulseCtx))) == -1) { m_Error = nullptr; } m_Sem.Post(); return; } m_SampleRate = ss.rate; }
int pa__init(pa_module* m) { pa_assert(m); /* this example uses fixed sample spec and channel map * * however, usually we to the following instead: * - set sample spec and chennel map to default values: * m->core->default_sample_spec * m->core->default_channel_map * * - overwrite them with pa_modargs_get_sample_spec_and_channel_map() * if module was loaded with corresponding arguments * * - finally adjust values to nearest supported form */ pa_sample_spec sample_spec; sample_spec.format = PA_SAMPLE_FLOAT32LE; sample_spec.rate = 44100; sample_spec.channels = 2; pa_channel_map channel_map; pa_channel_map_init_stereo(&channel_map); /* get module arguments (key-value list passed to load-module) */ pa_modargs *args; if (!(args = pa_modargs_new(m->argument, example_sink_modargs))) { pa_log("[example sink] failed to parse module arguments"); goto error; } /* create and initialize module-specific data */ struct example_sink_userdata *u = pa_xnew0(struct example_sink_userdata, 1); pa_assert(u); m->userdata = u; u->module = m; u->rtpoll = pa_rtpoll_new(); pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); u->output_file = pa_modargs_get_value(args, "output_file", "/dev/null"); u->output_fd = open(u->output_file, O_WRONLY | O_CREAT | O_TRUNC); if (u->output_fd == -1) { pa_log("[example sink] can't open output file %s", u->output_file); goto error; } /* create and initialize sink */ pa_sink_new_data data; pa_sink_new_data_init(&data); data.driver = "example_sink"; data.module = m; pa_sink_new_data_set_name( &data, pa_modargs_get_value(args, "sink_name", "example_sink")); pa_sink_new_data_set_sample_spec(&data, &sample_spec); pa_sink_new_data_set_channel_map(&data, &channel_map); if (pa_modargs_get_proplist( args, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) { pa_log("[example sink] invalid sink properties"); pa_sink_new_data_done(&data); goto error; } u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY); pa_sink_new_data_done(&data); if (!u->sink) { pa_log("[example sink] failed to create sink"); goto error; } /* setup sink callbacks */ u->sink->parent.process_msg = process_message; u->sink->userdata = u; /* setup sink event loop */ pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); pa_sink_set_rtpoll(u->sink, u->rtpoll); /* start thread for sink event loop and sample reader */ if (!(u->thread = pa_thread_new("example_sink", thread_loop, u))) { pa_log("[example sink] failed to create thread"); goto error; } pa_sink_put(u->sink); pa_modargs_free(args); return 0; error: if (args) { pa_modargs_free(args); } pa__done(m); return -1; }
int pa__init(pa_module *m) { pa_modargs *ma; const char *master_sink_name; const char *master_source_name; const char *max_hw_frag_size_str; const char *aep_runtime; pa_source *master_source; struct userdata *u; pa_proplist *p; pa_sink *master_sink; const char *raw_sink_name; const char *raw_source_name; const char *voice_sink_name; const char *voice_source_name; const char *dbus_type; int max_hw_frag_size = 3840; pa_assert(m); if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { pa_log_error("Failed to parse module arguments"); goto fail; } voice_turn_sidetone_down(); master_sink_name = pa_modargs_get_value(ma, "master_sink", NULL); master_source_name = pa_modargs_get_value(ma, "master_source", NULL); raw_sink_name = pa_modargs_get_value(ma, "raw_sink_name", "sink.voice.raw"); raw_source_name = pa_modargs_get_value(ma, "raw_source_name", "source.voice.raw"); voice_sink_name = pa_modargs_get_value(ma, "voice_sink_name", "sink.voice"); voice_source_name = pa_modargs_get_value(ma, "voice_source_name", "source.voice"); dbus_type = pa_modargs_get_value(ma, "dbus_type", "session"); max_hw_frag_size_str = pa_modargs_get_value(ma, "max_hw_frag_size", "3840"); aep_runtime = pa_modargs_get_value(ma, "aep_runtime", "bbaid1n-wr0-h9a22b--dbxpb--"); voice_set_aep_runtime_switch(aep_runtime); pa_log_debug("Got arguments: master_sink=\"%s\" master_source=\"%s\" raw_sink_name=\"%s\" raw_source_name=\"%s\" dbus_type=\"%s\" max_hw_frag_size=\"%s\". ", master_sink_name, master_source_name, raw_sink_name, raw_source_name, dbus_type, max_hw_frag_size_str); if (!(master_sink = pa_namereg_get(m->core, master_sink_name, PA_NAMEREG_SINK))) { pa_log("Master sink \"%s\" not found", master_sink_name); goto fail; } if (!(master_source = pa_namereg_get(m->core, master_source_name, PA_NAMEREG_SOURCE))) { pa_log( "Master source \"%s\" not found", master_source_name); goto fail; } if (master_sink->sample_spec.format != master_source->sample_spec.format && master_sink->sample_spec.rate != master_source->sample_spec.rate && master_sink->sample_spec.channels != master_source->sample_spec.channels) { pa_log("Master source and sink must have same sample spec"); goto fail; } if (pa_atoi(max_hw_frag_size_str, &max_hw_frag_size) < 0 || max_hw_frag_size < 960 || max_hw_frag_size > 128*960) { pa_log("Bad value for max_hw_frag_size: %s", max_hw_frag_size_str); goto fail; } m->userdata = u = pa_xnew0(struct userdata, 1); u->core = m->core; u->module = m; u->modargs = ma; u->master_sink = master_sink; u->master_source = master_source; u->mainloop_handler = voice_mainloop_handler_new(u);; u->ul_timing_advance = 500; // = 500 micro seconds, seems to be a good default value pa_channel_map_init_mono(&u->mono_map); pa_channel_map_init_stereo(&u->stereo_map); u->hw_sample_spec.format = PA_SAMPLE_S16NE; u->hw_sample_spec.rate = SAMPLE_RATE_HW_HZ; u->hw_sample_spec.channels = 2; u->hw_mono_sample_spec.format = PA_SAMPLE_S16NE; u->hw_mono_sample_spec.rate = SAMPLE_RATE_HW_HZ; u->hw_mono_sample_spec.channels = 1; u->aep_sample_spec.format = PA_SAMPLE_S16NE; u->aep_sample_spec.rate = SAMPLE_RATE_AEP_HZ; u->aep_sample_spec.channels = 1; pa_channel_map_init_mono(&u->aep_channel_map); // The result is rounded down incorrectly thus +1 u->aep_fragment_size = pa_usec_to_bytes(PERIOD_AEP_USECS+1, &u->aep_sample_spec); u->aep_hw_fragment_size = pa_usec_to_bytes(PERIOD_AEP_USECS+1, &u->hw_sample_spec); u->hw_fragment_size = pa_usec_to_bytes(PERIOD_MASTER_USECS+1, &u->hw_sample_spec); u->hw_fragment_size_max = max_hw_frag_size; if (0 != (u->hw_fragment_size_max % u->hw_fragment_size)) u->hw_fragment_size_max += u->hw_fragment_size - (u->hw_fragment_size_max % u->hw_fragment_size); u->aep_hw_mono_fragment_size = pa_usec_to_bytes(PERIOD_AEP_USECS+1, &u->hw_mono_sample_spec); u->hw_mono_fragment_size = pa_usec_to_bytes(PERIOD_MASTER_USECS+1, &u->hw_mono_sample_spec); u->voice_ul_fragment_size = pa_usec_to_bytes(PERIOD_CMT_USECS+1, &u->aep_sample_spec); pa_silence_memchunk_get(&u->core->silence_cache, u->core->mempool, &u->aep_silence_memchunk, &u->aep_sample_spec, u->aep_fragment_size); voice_memchunk_pool_load(u); if (voice_init_raw_sink(u, raw_sink_name)) goto fail; pa_sink_put(u->raw_sink); if (voice_init_voip_sink(u, voice_sink_name)) goto fail; pa_sink_put(u->voip_sink); if (voice_init_aep_sink_input(u)) goto fail; pa_atomic_store(&u->mixer_state, PROP_MIXER_TUNING_PRI); u->alt_mixer_compensation = PA_VOLUME_NORM; if (voice_init_hw_sink_input(u)) goto fail; u->sink_temp_buff = pa_xmalloc(2 * u->hw_fragment_size_max); u->sink_temp_buff_len = 2 * u->hw_fragment_size_max; u->dl_memblockq = pa_memblockq_new(0, 2 * u->voice_ul_fragment_size, 0, pa_frame_size(&u->aep_sample_spec), 0, 0, 0, NULL); if (voice_init_raw_source(u, raw_source_name)) goto fail; pa_source_put(u->raw_source); if (voice_init_voip_source(u, voice_source_name)) goto fail; pa_source_put(u->voip_source); if (voice_init_hw_source_output(u)) goto fail; u->hw_source_memblockq = pa_memblockq_new(0, 2 * u->hw_fragment_size_max, 0, pa_frame_size(&u->hw_sample_spec), 0, 0, 0, NULL); u->ul_memblockq = pa_memblockq_new(0, 2 * u->voice_ul_fragment_size, 0, pa_frame_size(&u->aep_sample_spec), 0, 0, 0, NULL); u->cs_call_sink_input = 0; u->dl_sideinfo_queue = pa_queue_new(); u->linear_q15_master_volume_L = INT16_MAX; u->linear_q15_master_volume_R = INT16_MAX; u->field_2CC = 0; voice_aep_ear_ref_init(u); if (voice_convert_init(u)) goto fail; if (voice_init_event_forwarder(u, dbus_type) || voice_init_cmtspeech(u)) goto fail; if (!(u->wb_mic_iir_eq = iir_eq_new(u->hw_fragment_size / 2, master_source->sample_spec.channels))) goto fail; if (!(u->nb_mic_iir_eq = iir_eq_new( u->aep_fragment_size / 2, 1))) goto fail; if (!(u->wb_ear_iir_eq = fir_eq_new(master_sink->sample_spec.rate, master_sink->sample_spec.channels))) goto fail; if (!(u->nb_ear_iir_eq = iir_eq_new(u->aep_fragment_size / 2, 1))) goto fail; u->input_task_active = FALSE; u->xprot_watchdog = TRUE; u->ambient_temp = 30; if (!(u->xprot = xprot_new())) goto fail; u->aep_enable = FALSE; u->wb_meq_enable = FALSE; u->wb_eeq_enable = FALSE; u->nb_meq_enable = FALSE; u->nb_eeq_enable = FALSE; u->xprot_enable = FALSE; u->updating_parameters = FALSE; u->sink_proplist_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], 0, (pa_hook_cb_t)sink_proplist_changed_cb, u);; u->source_proplist_changed_slot = pa_hook_connect( &m->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], 0, (pa_hook_cb_t)source_proplist_changed_cb, u); u->mode_accessory_hwid_hash = 0; p = pa_proplist_new(); pa_proplist_sets(p, PA_NOKIA_PROP_AUDIO_MODE, "ihf"); pa_proplist_sets(p, PA_NOKIA_PROP_AUDIO_ACCESSORY_HWID, ""); pa_sink_update_proplist( master_sink, PA_UPDATE_REPLACE, p); pa_proplist_free(p); pa_source_output_put(u->hw_source_output); pa_sink_input_put(u->hw_sink_input); pa_sink_input_put(u->aep_sink_input); u->sink_subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK, sink_subscribe_cb, u); return 0; fail: if (ma) pa_modargs_free(ma); pa__done(m); return -1; }
static gboolean gst_pulsesrc_create_stream (GstPulseSrc * pulsesrc, GstCaps * caps) { pa_channel_map channel_map; GstStructure *s; gboolean need_channel_layout = FALSE; GstRingBufferSpec spec; const gchar *name; memset (&spec, 0, sizeof (GstRingBufferSpec)); spec.latency_time = GST_SECOND; if (!gst_ring_buffer_parse_caps (&spec, caps)) { GST_ELEMENT_ERROR (pulsesrc, RESOURCE, SETTINGS, ("Can't parse caps."), (NULL)); goto fail; } /* Keep the refcount of the caps at 1 to make them writable */ gst_caps_unref (spec.caps); if (!gst_pulse_fill_sample_spec (&spec, &pulsesrc->sample_spec)) { GST_ELEMENT_ERROR (pulsesrc, RESOURCE, SETTINGS, ("Invalid sample specification."), (NULL)); goto fail; } pa_threaded_mainloop_lock (pulsesrc->mainloop); if (!pulsesrc->context) { GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED, ("Bad context"), (NULL)); goto unlock_and_fail; } s = gst_caps_get_structure (caps, 0); if (!gst_structure_has_field (s, "channel-layout") || !gst_pulse_gst_to_channel_map (&channel_map, &spec)) { if (spec.channels == 1) pa_channel_map_init_mono (&channel_map); else if (spec.channels == 2) pa_channel_map_init_stereo (&channel_map); else need_channel_layout = TRUE; } name = "Record Stream"; if (pulsesrc->proplist) { if (!(pulsesrc->stream = pa_stream_new_with_proplist (pulsesrc->context, name, &pulsesrc->sample_spec, (need_channel_layout) ? NULL : &channel_map, pulsesrc->proplist))) { GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED, ("Failed to create stream: %s", pa_strerror (pa_context_errno (pulsesrc->context))), (NULL)); goto unlock_and_fail; } } else if (!(pulsesrc->stream = pa_stream_new (pulsesrc->context, name, &pulsesrc->sample_spec, (need_channel_layout) ? NULL : &channel_map))) { GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED, ("Failed to create stream: %s", pa_strerror (pa_context_errno (pulsesrc->context))), (NULL)); goto unlock_and_fail; } if (need_channel_layout) { const pa_channel_map *m = pa_stream_get_channel_map (pulsesrc->stream); gst_pulse_channel_map_to_gst (m, &spec); caps = spec.caps; } GST_DEBUG_OBJECT (pulsesrc, "Caps are %" GST_PTR_FORMAT, caps); pa_stream_set_state_callback (pulsesrc->stream, gst_pulsesrc_stream_state_cb, pulsesrc); pa_stream_set_read_callback (pulsesrc->stream, gst_pulsesrc_stream_request_cb, pulsesrc); pa_stream_set_underflow_callback (pulsesrc->stream, gst_pulsesrc_stream_underflow_cb, pulsesrc); pa_stream_set_overflow_callback (pulsesrc->stream, gst_pulsesrc_stream_overflow_cb, pulsesrc); pa_stream_set_latency_update_callback (pulsesrc->stream, gst_pulsesrc_stream_latency_update_cb, pulsesrc); pa_threaded_mainloop_unlock (pulsesrc->mainloop); return TRUE; unlock_and_fail: gst_pulsesrc_destroy_stream (pulsesrc); pa_threaded_mainloop_unlock (pulsesrc->mainloop); fail: return FALSE; }
/** * Setup a new stream based on the properties of the given audio_buf */ static void stream_setup(pa_audio_mode_t *pam, audio_buf_t *ab) { pa_stream *s; char buf[100]; int flags = 0; #if PA_API_VERSION >= 12 pa_proplist *pl; media_pipe_t *mp = ab->ab_mp; #endif pa_channel_map map; pa_cvolume cv; memset(&pam->ss, 0, sizeof(pa_sample_spec)); pam->ss.format = ab->ab_isfloat ? PA_SAMPLE_FLOAT32NE : PA_SAMPLE_S16NE; pam->ss.rate = ab->ab_samplerate; switch(ab->ab_format) { case AM_FORMAT_PCM_STEREO: pam->ss.channels = 2; pa_channel_map_init_stereo(&map); break; case AM_FORMAT_PCM_5DOT0: pam->ss.channels = 5; pa_channel_map_init(&map); map.channels = 5; map.map[0] = PA_CHANNEL_POSITION_LEFT; map.map[1] = PA_CHANNEL_POSITION_RIGHT; map.map[2] = PA_CHANNEL_POSITION_CENTER; map.map[3] = PA_CHANNEL_POSITION_SIDE_LEFT; map.map[4] = PA_CHANNEL_POSITION_SIDE_RIGHT; break; case AM_FORMAT_PCM_5DOT1: pam->ss.channels = 6; pa_channel_map_init(&map); map.channels = 6; map.map[0] = PA_CHANNEL_POSITION_LEFT; map.map[1] = PA_CHANNEL_POSITION_RIGHT; map.map[2] = PA_CHANNEL_POSITION_CENTER; map.map[3] = PA_CHANNEL_POSITION_LFE; map.map[4] = PA_CHANNEL_POSITION_SIDE_LEFT; map.map[5] = PA_CHANNEL_POSITION_SIDE_RIGHT; break; case AM_FORMAT_PCM_7DOT1: pam->ss.channels = 8; pa_channel_map_init(&map); map.channels = 8; map.map[0] = PA_CHANNEL_POSITION_LEFT; map.map[1] = PA_CHANNEL_POSITION_RIGHT; map.map[2] = PA_CHANNEL_POSITION_CENTER; map.map[3] = PA_CHANNEL_POSITION_LFE; map.map[4] = PA_CHANNEL_POSITION_SIDE_LEFT; map.map[5] = PA_CHANNEL_POSITION_SIDE_RIGHT; map.map[6] = PA_CHANNEL_POSITION_REAR_LEFT; map.map[7] = PA_CHANNEL_POSITION_REAR_RIGHT; break; case AM_FORMAT_PCM_6DOT1: pam->ss.channels = 7; pa_channel_map_init(&map); map.channels = 7; map.map[0] = PA_CHANNEL_POSITION_LEFT; map.map[1] = PA_CHANNEL_POSITION_RIGHT; map.map[2] = PA_CHANNEL_POSITION_CENTER; map.map[3] = PA_CHANNEL_POSITION_LFE; map.map[4] = PA_CHANNEL_POSITION_SIDE_LEFT; map.map[5] = PA_CHANNEL_POSITION_SIDE_RIGHT; map.map[6] = PA_CHANNEL_POSITION_REAR_CENTER; break; default: abort(); } TRACE(TRACE_DEBUG, "PA", "Created stream %s", pa_sample_spec_snprint(buf, sizeof(buf), &pam->ss)); #if PA_API_VERSION >= 12 pl = pa_proplist_new(); if(mp->mp_flags & MP_VIDEO) pa_proplist_sets(pl, PA_PROP_MEDIA_ROLE, "video"); else pa_proplist_sets(pl, PA_PROP_MEDIA_ROLE, "music"); s = pa_stream_new_with_proplist(pam->context, "Showtime playback", &pam->ss, &map, pl); pa_proplist_free(pl); #else s = pa_stream_new(pam->context, "Showtime playback", &pam->ss, &map); #endif pa_stream_set_state_callback(s, stream_state_callback, pam); pa_stream_set_write_callback(s, stream_write_callback, pam); flags |= PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING; memset(&cv, 0, sizeof(cv)); pa_cvolume_set(&cv, pam->ss.channels, pam->mastervol); #if 1 pa_buffer_attr pba = {0}; pba.fragsize = (uint32_t)-1; pba.maxlength = 16 * 1024; pba.minreq = 3 * 1024; pba.prebuf = 8 * 1024; pba.tlength = 12 * 1024; #endif pa_stream_connect_playback(s, NULL, &pba, flags, &cv, NULL); pam->stream = s; pam->cur_rate = ab->ab_samplerate; pam->cur_format = ab->ab_format; pam->cur_isfloat = ab->ab_isfloat; }
static int pulseaudio_audio_reconfig(audio_decoder_t *ad) { decoder_t *d = (decoder_t *)ad; int i; pa_threaded_mainloop_lock(mainloop); if(pulseaudio_make_context_ready()) { pa_threaded_mainloop_unlock(mainloop); return -1; } if(d->s) { pa_stream_disconnect(d->s); pa_stream_unref(d->s); } pa_channel_map map; ad->ad_out_sample_rate = ad->ad_in_sample_rate; d->ss.rate = ad->ad_in_sample_rate; switch(ad->ad_in_sample_format) { case AV_SAMPLE_FMT_S32: case AV_SAMPLE_FMT_S32P: ad->ad_out_sample_format = AV_SAMPLE_FMT_S32; d->ss.format = PA_SAMPLE_S32NE; d->framesize = sizeof(int32_t); break; case AV_SAMPLE_FMT_S16: case AV_SAMPLE_FMT_S16P: ad->ad_out_sample_format = AV_SAMPLE_FMT_S16; d->ss.format = PA_SAMPLE_S16NE; d->framesize = sizeof(int16_t); break; default: ad->ad_out_sample_format = AV_SAMPLE_FMT_FLT; d->ss.format = PA_SAMPLE_FLOAT32NE; d->framesize = sizeof(float); break; } switch(ad->ad_in_channel_layout) { case AV_CH_LAYOUT_MONO: d->ss.channels = 1; ad->ad_out_channel_layout = AV_CH_LAYOUT_MONO; pa_channel_map_init_mono(&map); break; case AV_CH_LAYOUT_STEREO: d->ss.channels = 2; ad->ad_out_channel_layout = AV_CH_LAYOUT_STEREO; pa_channel_map_init_stereo(&map); default: pa_channel_map_init(&map); for(i = 0; i < sizeof(av2pa_map) / sizeof(av2pa_map[0]); i++) { if(ad->ad_in_channel_layout & av2pa_map[i].avmask) { ad->ad_out_channel_layout |= av2pa_map[i].avmask; map.map[map.channels++] = av2pa_map[i].papos; } } d->ss.channels = map.channels; break; } d->framesize *= d->ss.channels; ad->ad_tile_size = pa_context_get_tile_size(ctx, &d->ss) / d->framesize; char buf[100]; char buf2[PA_CHANNEL_MAP_SNPRINT_MAX]; TRACE(TRACE_DEBUG, "PA", "Created stream %s [%s] (tilesize=%d)", pa_sample_spec_snprint(buf, sizeof(buf), &d->ss), pa_channel_map_snprint(buf2, sizeof(buf2), &map), ad->ad_tile_size); #if PA_API_VERSION >= 12 pa_proplist *pl = pa_proplist_new(); media_pipe_t *mp = ad->ad_mp; if(mp->mp_flags & MP_VIDEO) pa_proplist_sets(pl, PA_PROP_MEDIA_ROLE, "video"); else pa_proplist_sets(pl, PA_PROP_MEDIA_ROLE, "music"); d->s = pa_stream_new_with_proplist(ctx, "Showtime playback", &d->ss, &map, pl); pa_proplist_free(pl); #else d->s = pa_stream_new(ctx, "Showtime playback", &ss, &map); #endif int flags = 0; pa_stream_set_state_callback(d->s, stream_state_callback, d); pa_stream_set_write_callback(d->s, stream_write_callback, d); flags |= PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING; pa_stream_connect_playback(d->s, NULL, NULL, flags, NULL, NULL); while(1) { switch(pa_stream_get_state(d->s)) { case PA_STREAM_UNCONNECTED: case PA_STREAM_CREATING: pa_threaded_mainloop_wait(mainloop); continue; case PA_STREAM_READY: pa_threaded_mainloop_unlock(mainloop); return 0; case PA_STREAM_TERMINATED: case PA_STREAM_FAILED: pa_threaded_mainloop_unlock(mainloop); return 1; } } }
static gboolean gst_pulsesrc_create_stream (GstPulseSrc * pulsesrc, GstCaps ** caps) { pa_channel_map channel_map; const pa_channel_map *m; GstStructure *s; gboolean need_channel_layout = FALSE; GstAudioRingBufferSpec spec; const gchar *name; s = gst_caps_get_structure (*caps, 0); gst_structure_get_int (s, "channels", &spec.info.channels); if (!gst_structure_has_field (s, "channel-mask")) { if (spec.info.channels == 1) { pa_channel_map_init_mono (&channel_map); } else if (spec.info.channels == 2) { gst_structure_set (s, "channel-mask", GST_TYPE_BITMASK, GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_LEFT) | GST_AUDIO_CHANNEL_POSITION_MASK (FRONT_RIGHT), NULL); pa_channel_map_init_stereo (&channel_map); } else { need_channel_layout = TRUE; gst_structure_set (s, "channel-mask", GST_TYPE_BITMASK, G_GUINT64_CONSTANT (0), NULL); } } memset (&spec, 0, sizeof (GstAudioRingBufferSpec)); spec.latency_time = GST_SECOND; if (!gst_audio_ring_buffer_parse_caps (&spec, *caps)) goto invalid_caps; /* Keep the refcount of the caps at 1 to make them writable */ gst_caps_unref (spec.caps); if (!need_channel_layout && !gst_pulse_gst_to_channel_map (&channel_map, &spec)) { need_channel_layout = TRUE; gst_structure_set (s, "channel-mask", GST_TYPE_BITMASK, G_GUINT64_CONSTANT (0), NULL); memset (spec.info.position, 0xff, sizeof (spec.info.position)); } if (!gst_pulse_fill_sample_spec (&spec, &pulsesrc->sample_spec)) goto invalid_spec; pa_threaded_mainloop_lock (pulsesrc->mainloop); if (!pulsesrc->context) goto bad_context; name = "Record Stream"; if (pulsesrc->proplist) { if (!(pulsesrc->stream = pa_stream_new_with_proplist (pulsesrc->context, name, &pulsesrc->sample_spec, (need_channel_layout) ? NULL : &channel_map, pulsesrc->proplist))) goto create_failed; } else if (!(pulsesrc->stream = pa_stream_new (pulsesrc->context, name, &pulsesrc->sample_spec, (need_channel_layout) ? NULL : &channel_map))) goto create_failed; m = pa_stream_get_channel_map (pulsesrc->stream); gst_pulse_channel_map_to_gst (m, &spec); gst_audio_channel_positions_to_valid_order (spec.info.position, spec.info.channels); gst_caps_unref (*caps); *caps = gst_audio_info_to_caps (&spec.info); GST_DEBUG_OBJECT (pulsesrc, "Caps are %" GST_PTR_FORMAT, *caps); pa_stream_set_state_callback (pulsesrc->stream, gst_pulsesrc_stream_state_cb, pulsesrc); pa_stream_set_read_callback (pulsesrc->stream, gst_pulsesrc_stream_request_cb, pulsesrc); pa_stream_set_underflow_callback (pulsesrc->stream, gst_pulsesrc_stream_underflow_cb, pulsesrc); pa_stream_set_overflow_callback (pulsesrc->stream, gst_pulsesrc_stream_overflow_cb, pulsesrc); pa_stream_set_latency_update_callback (pulsesrc->stream, gst_pulsesrc_stream_latency_update_cb, pulsesrc); pa_threaded_mainloop_unlock (pulsesrc->mainloop); return TRUE; /* ERRORS */ invalid_caps: { GST_ELEMENT_ERROR (pulsesrc, RESOURCE, SETTINGS, ("Can't parse caps."), (NULL)); goto fail; } invalid_spec: { GST_ELEMENT_ERROR (pulsesrc, RESOURCE, SETTINGS, ("Invalid sample specification."), (NULL)); goto fail; } bad_context: { GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED, ("Bad context"), (NULL)); goto unlock_and_fail; } create_failed: { GST_ELEMENT_ERROR (pulsesrc, RESOURCE, FAILED, ("Failed to create stream: %s", pa_strerror (pa_context_errno (pulsesrc->context))), (NULL)); goto unlock_and_fail; } unlock_and_fail: { gst_pulsesrc_destroy_stream (pulsesrc); pa_threaded_mainloop_unlock (pulsesrc->mainloop); fail: return FALSE; } }
int pa__init(pa_module*m) { pa_modargs *ma = NULL; struct userdata *u; const char *master_sink_name; const char *master_source_name; const char *raw_sink_name; const char *raw_source_name; const char *voice_sink_name; const char *voice_source_name; const char *max_hw_frag_size_str; int max_hw_frag_size = 3840; pa_sink *master_sink; pa_source *master_source; pa_assert(m); if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { pa_log("Failed to parse module arguments"); goto fail; } master_sink_name = pa_modargs_get_value(ma, "master_sink", NULL); master_source_name = pa_modargs_get_value(ma, "master_source", NULL); raw_sink_name = pa_modargs_get_value(ma, "raw_sink_name", "sink.voice.raw"); raw_source_name = pa_modargs_get_value(ma, "raw_source_name", "source.voice.raw"); voice_sink_name = pa_modargs_get_value(ma, "voice_sink_name", "sink.voice"); voice_source_name = pa_modargs_get_value(ma, "voice_source_name", "source.voice"); max_hw_frag_size_str = pa_modargs_get_value(ma, "max_hw_frag_size", "3840"); pa_log_debug("Got arguments: master_sink=\"%s\" master_source=\"%s\" " "raw_sink_name=\"%s\" raw_source_name=\"%s\" max_hw_frag_size=\"%s\".", master_sink_name, master_source_name, raw_sink_name, raw_source_name, max_hw_frag_size_str); if (!(master_sink = pa_namereg_get(m->core, master_sink_name, PA_NAMEREG_SINK))) { pa_log("Master sink \"%s\" not found", master_sink_name); goto fail; } if (!(master_source = pa_namereg_get(m->core, master_source_name, PA_NAMEREG_SOURCE))) { pa_log("Master source \"%s\" not found", master_source_name); goto fail; } if (master_sink->sample_spec.format != master_source->sample_spec.format && master_sink->sample_spec.rate != master_source->sample_spec.rate && master_sink->sample_spec.channels != master_source->sample_spec.channels) { pa_log("Master source and sink must have same sample spec"); goto fail; } if (pa_atoi(max_hw_frag_size_str, &max_hw_frag_size) < 0 || max_hw_frag_size < 960 || max_hw_frag_size > 128*960) { pa_log("Bad value for max_hw_frag_size: %s", max_hw_frag_size_str); goto fail; } m->userdata = u = pa_xnew0(struct userdata, 1); u->modargs = ma; u->core = m->core; u->module = m; u->master_sink = master_sink; u->master_source = master_source; set_hooks(u); u->mainloop_handler = voice_mainloop_handler_new(u); u->ul_timing_advance = 500; // = 500 micro seconds, seems to be a good default value pa_channel_map_init_mono(&u->mono_map); pa_channel_map_init_stereo(&u->stereo_map); u->hw_sample_spec.format = PA_SAMPLE_S16NE; u->hw_sample_spec.rate = VOICE_SAMPLE_RATE_HW_HZ; u->hw_sample_spec.channels = 2; u->hw_mono_sample_spec.format = PA_SAMPLE_S16NE; u->hw_mono_sample_spec.rate = VOICE_SAMPLE_RATE_HW_HZ; u->hw_mono_sample_spec.channels = 1; u->aep_sample_spec.format = PA_SAMPLE_S16NE; u->aep_sample_spec.rate = VOICE_SAMPLE_RATE_AEP_HZ; u->aep_sample_spec.channels = 1; pa_channel_map_init_mono(&u->aep_channel_map); // The result is rounded down incorrectly thus +1 u->aep_fragment_size = pa_usec_to_bytes(VOICE_PERIOD_AEP_USECS+1, &u->aep_sample_spec); u->aep_hw_fragment_size = pa_usec_to_bytes(VOICE_PERIOD_AEP_USECS+1, &u->hw_sample_spec); u->hw_fragment_size = pa_usec_to_bytes(VOICE_PERIOD_MASTER_USECS+1, &u->hw_sample_spec); u->hw_fragment_size_max = max_hw_frag_size; if (0 != (u->hw_fragment_size_max % u->hw_fragment_size)) u->hw_fragment_size_max += u->hw_fragment_size - (u->hw_fragment_size_max % u->hw_fragment_size); u->aep_hw_mono_fragment_size = pa_usec_to_bytes(VOICE_PERIOD_AEP_USECS+1, &u->hw_mono_sample_spec); u->hw_mono_fragment_size = pa_usec_to_bytes(VOICE_PERIOD_MASTER_USECS+1, &u->hw_mono_sample_spec); u->voice_ul_fragment_size = pa_usec_to_bytes(VOICE_PERIOD_CMT_USECS+1, &u->aep_sample_spec); pa_silence_memchunk_get(&u->core->silence_cache, u->core->mempool, &u->aep_silence_memchunk, & u->aep_sample_spec, u->aep_fragment_size); voice_memchunk_pool_load(u); if (voice_init_raw_sink(u, raw_sink_name)) goto fail; u->call_state_tracker = pa_call_state_tracker_get(m->core); pa_atomic_store(&u->mixer_state, PROP_MIXER_TUNING_PRI); pa_call_state_tracker_set_active(u->call_state_tracker, FALSE); u->alt_mixer_compensation = PA_VOLUME_NORM; if (voice_init_hw_sink_input(u)) goto fail; /* This must be set before calling pa_sink_put(), because pa_sink_put() has * assertion * "!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER) || s->flat_sink_input". */ u->raw_sink->flat_sink_input = u->hw_sink_input; /* This must be called before calling voice_init_voip_sink(), because * pa_sink_input_new() has assertion * "PA_SINK_IS_LINKED(pa_sink_get_state(data->sink))". */ pa_sink_put(u->raw_sink); /* This must be called before calling voice_init_aep_sink_input(), because * the flat volume logic will otherwise mess up the aep sink input's volume * when pa_sink_input_put(u->hw_sink_input) is called. */ pa_sink_input_put(u->hw_sink_input); if (voice_init_voip_sink(u, voice_sink_name)) goto fail; if (voice_init_aep_sink_input(u)) goto fail; u->sink_temp_buff = pa_xmalloc(2*u->hw_fragment_size_max); u->sink_temp_buff_len = 2*u->hw_fragment_size_max; if (voice_init_raw_source(u, raw_source_name)) goto fail; pa_source_put(u->raw_source); if (voice_init_voip_source(u, voice_source_name)) goto fail; pa_source_put(u->voip_source); if (voice_init_hw_source_output(u)) goto fail; /* TODO: Guess we should use max_hw_frag_size here */ u->hw_source_memblockq = // 8 * 5ms = 40ms pa_memblockq_new(0, 2*u->hw_fragment_size_max, 0, pa_frame_size(&u->hw_sample_spec), 0, 0, 0, NULL); u->ul_memblockq = pa_memblockq_new(0, 2*u->voice_ul_fragment_size, 0, pa_frame_size(&u->aep_sample_spec), 0, 0, 0, NULL); u->dl_sideinfo_queue = pa_queue_new(); u->ul_deadline = 0; u->linear_q15_master_volume_L = INT16_MAX; u->linear_q15_master_volume_R = INT16_MAX; voice_aep_ear_ref_init(u); if (voice_convert_init(u)) goto fail; /* IHF mode is the default and this initialization is consistent with it. */ u->active_mic_channel = MIC_CH0; meego_parameter_request_updates("voice", (pa_hook_cb_t)voice_parameter_cb, PA_HOOK_NORMAL, FALSE, u); meego_parameter_request_updates("alsa", (pa_hook_cb_t)alsa_parameter_cb, PA_HOOK_NORMAL, FALSE, u); meego_parameter_request_updates("aep", (pa_hook_cb_t)aep_parameter_cb, PA_HOOK_LATE, FALSE, u); /* aep-s-i */ /* voip-sink ---\ hw-sink-input */ /* > optimized mix -------------> master-sink */ /* | */ /* raw-sink */ /* */ /* voip-src <--- hw-source-output */ /* < mux <------------- master-src */ /* raw-src <--- */ u->voip_sink->flat_sink_input = u->aep_sink_input; pa_sink_put(u->voip_sink); pa_source_output_put(u->hw_source_output); pa_sink_input_put(u->aep_sink_input); u->sink_subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SINK_INPUT, master_sink_volume_subscribe_cb, u); u->previous_master_source_state = pa_source_get_state(u->master_source); u->source_change_subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SOURCE, master_source_state_subscribe_cb, u); return 0; fail: 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; }
Error AudioDriverPulseAudio::init_device() { // If there is a specified device check that it is really present if (device_name != "Default") { Array list = get_device_list(); if (list.find(device_name) == -1) { device_name = "Default"; new_device = "Default"; } } // Detect the amount of channels PulseAudio is using // Note: If using an even amount of channels (2, 4, etc) channels and pa_map.channels will be equal, // if not then pa_map.channels will have the real amount of channels PulseAudio is using and channels // will have the amount of channels Godot is using (in this case it's pa_map.channels + 1) detect_channels(); switch (pa_map.channels) { case 1: // Mono case 3: // Surround 2.1 case 5: // Surround 5.0 case 7: // Surround 7.0 channels = pa_map.channels + 1; break; case 2: // Stereo case 4: // Surround 4.0 case 6: // Surround 5.1 case 8: // Surround 7.1 channels = pa_map.channels; break; default: WARN_PRINTS("PulseAudio: Unsupported number of channels: " + itos(pa_map.channels)); pa_channel_map_init_stereo(&pa_map); channels = 2; break; } int latency = GLOBAL_DEF_RST("audio/output_latency", DEFAULT_OUTPUT_LATENCY); buffer_frames = closest_power_of_2(latency * mix_rate / 1000); pa_buffer_size = buffer_frames * pa_map.channels; print_verbose("PulseAudio: detected " + itos(pa_map.channels) + " channels"); print_verbose("PulseAudio: audio buffer frames: " + itos(buffer_frames) + " calculated latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms"); pa_sample_spec spec; spec.format = PA_SAMPLE_S16LE; spec.channels = pa_map.channels; spec.rate = mix_rate; pa_str = pa_stream_new(pa_ctx, "Sound", &spec, &pa_map); if (pa_str == NULL) { ERR_PRINTS("PulseAudio: pa_stream_new error: " + String(pa_strerror(pa_context_errno(pa_ctx)))); ERR_FAIL_V(ERR_CANT_OPEN); } pa_buffer_attr attr; // set to appropriate buffer length (in bytes) from global settings // Note: PulseAudio defaults to 4 fragments, which means that the actual // latency is tlength / fragments. It seems that the PulseAudio has no way // to get the fragments number so we're hardcoding this to the default of 4 const int fragments = 4; attr.tlength = pa_buffer_size * sizeof(int16_t) * fragments; // set them to be automatically chosen attr.prebuf = (uint32_t)-1; attr.maxlength = (uint32_t)-1; attr.minreq = (uint32_t)-1; const char *dev = device_name == "Default" ? NULL : device_name.utf8().get_data(); pa_stream_flags flags = pa_stream_flags(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE); int error_code = pa_stream_connect_playback(pa_str, dev, &attr, flags, NULL, NULL); ERR_FAIL_COND_V(error_code < 0, ERR_CANT_OPEN); samples_in.resize(buffer_frames * channels); samples_out.resize(pa_buffer_size); // Reset audio input to keep synchronisation. input_position = 0; input_size = 0; return OK; }