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;
}
Exemple #3
0
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;
}
Exemple #5
0
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;
}
Exemple #6
0
/**
 * 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;
}
Exemple #7
0
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;
    }
  }
}
Exemple #8
0
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;
}