Пример #1
0
static pa_sample_spec *parse_sdp_sample_spec(pa_sample_spec *ss, char *c) {
    unsigned rate, channels;
    pa_assert(ss);
    pa_assert(c);

    if (pa_startswith(c, "L16/")) {
        ss->format = PA_SAMPLE_S16BE;
        c += 4;
    } else if (pa_startswith(c, "L8/")) {
        ss->format = PA_SAMPLE_U8;
        c += 3;
    } else if (pa_startswith(c, "PCMA/")) {
        ss->format = PA_SAMPLE_ALAW;
        c += 5;
    } else if (pa_startswith(c, "PCMU/")) {
        ss->format = PA_SAMPLE_ULAW;
        c += 5;
    } else
        return NULL;

    if (sscanf(c, "%u/%u", &rate, &channels) == 2) {
        ss->rate = (uint32_t) rate;
        ss->channels = (uint8_t) channels;
    } else if (sscanf(c, "%u", &rate) == 2) {
        ss->rate = (uint32_t) rate;
        ss->channels = 1;
    } else
        return NULL;

    if (!pa_sample_spec_valid(ss))
        return NULL;

    return ss;
}
Пример #2
0
char *pa_sample_spec_to_mime_type(const pa_sample_spec *ss, const pa_channel_map *cm) {
    pa_assert(pa_channel_map_compatible(cm, ss));
    pa_assert(pa_sample_spec_valid(ss));

    if (!pa_sample_spec_is_mime(ss, cm))
        return NULL;

    switch (ss->format) {

        case PA_SAMPLE_S16BE:
        case PA_SAMPLE_S24BE:
        case PA_SAMPLE_U8:
            /* Stupid UPnP implementations (PS3...) choke on spaces in
             * the mime type, that's why we write only ';' here,
             * instead of '; '. */
            return pa_sprintf_malloc("audio/%s;rate=%u;channels=%u",
                                     ss->format == PA_SAMPLE_S16BE ? "L16" :
                                     (ss->format == PA_SAMPLE_S24BE ? "L24" : "L8"),
                                     ss->rate, ss->channels);

        case PA_SAMPLE_ULAW:
            return pa_xstrdup("audio/basic");

        default:
            pa_assert_not_reached();
    }
}
Пример #3
0
/**
 * Start recording
 *
 * We request the default format used by pulse here because the data will be
 * converted and possibly re-sampled by obs anyway.
 *
 * For now we request a buffer length of 25ms although pulse seems to ignore
 * this setting for monitor streams. For "real" input streams this should work
 * fine though.
 */
static int_fast32_t pulse_start_recording(struct pulse_data *data)
{
	if (pulse_get_server_info(pulse_server_info, (void *) data) < 0) {
		blog(LOG_ERROR, "Unable to get server info !");
		return -1;
	}

	if (pulse_get_source_info(pulse_source_info, data->device,
			(void *) data) < 0) {
		blog(LOG_ERROR, "Unable to get source info !");
		return -1;
	}

	pa_sample_spec spec;
	spec.format   = data->format;
	spec.rate     = data->samples_per_sec;
	spec.channels = data->channels;

	if (!pa_sample_spec_valid(&spec)) {
		blog(LOG_ERROR, "Sample spec is not valid");
		return -1;
	}

	data->speakers = pulse_channels_to_obs_speakers(spec.channels);
	data->bytes_per_frame = pa_frame_size(&spec);

	data->stream = pulse_stream_new(obs_source_get_name(data->source),
		&spec, NULL);
	if (!data->stream) {
		blog(LOG_ERROR, "Unable to create stream");
		return -1;
	}

	pulse_lock();
	pa_stream_set_read_callback(data->stream, pulse_stream_read,
		(void *) data);
	pulse_unlock();

	pa_buffer_attr attr;
	attr.fragsize  = pa_usec_to_bytes(25000, &spec);
	attr.maxlength = (uint32_t) -1;
	attr.minreq    = (uint32_t) -1;
	attr.prebuf    = (uint32_t) -1;
	attr.tlength   = (uint32_t) -1;

	pa_stream_flags_t flags = PA_STREAM_ADJUST_LATENCY;

	pulse_lock();
	int_fast32_t ret = pa_stream_connect_record(data->stream, data->device,
		&attr, flags);
	pulse_unlock();
	if (ret < 0) {
		pulse_stop_recording(data);
		blog(LOG_ERROR, "Unable to connect to stream");
		return -1;
	}

	blog(LOG_INFO, "Started recording from '%s'", data->device);
	return 0;
}
Пример #4
0
int pa_scache_add_item(
        pa_core *c,
        const char *name,
        const pa_sample_spec *ss,
        const pa_channel_map *map,
        const pa_memchunk *chunk,
        pa_proplist *p,
        uint32_t *idx) {

    pa_scache_entry *e;
    char st[PA_SAMPLE_SPEC_SNPRINT_MAX];
    pa_channel_map tmap;

    pa_assert(c);
    pa_assert(name);
    pa_assert(!ss || pa_sample_spec_valid(ss));
    pa_assert(!map || (pa_channel_map_valid(map) && ss && pa_channel_map_compatible(map, ss)));

    if (ss && !map) {
        pa_channel_map_init_extend(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT);
        map = &tmap;
    }

    if (chunk && chunk->length > PA_SCACHE_ENTRY_SIZE_MAX)
        return -1;

    if (!(e = scache_add_item(c, name)))
        return -1;

    pa_sample_spec_init(&e->sample_spec);
    pa_channel_map_init(&e->channel_map);
    pa_cvolume_init(&e->volume);
    e->volume_is_set = FALSE;

    if (ss) {
        e->sample_spec = *ss;
        pa_cvolume_reset(&e->volume, ss->channels);
    }

    if (map)
        e->channel_map = *map;

    if (chunk) {
        e->memchunk = *chunk;
        pa_memblock_ref(e->memchunk.memblock);
    }

    if (p)
        pa_proplist_update(e->proplist, PA_UPDATE_REPLACE, p);

    if (idx)
        *idx = e->index;

    pa_log_debug("Created sample \"%s\" (#%d), %lu bytes with sample spec %s",
                 name, e->index, (unsigned long) e->memchunk.length,
                 pa_sample_spec_snprint(st, sizeof(st), &e->sample_spec));

    return 0;
}
Пример #5
0
pa_memchunk* pa_silence_memchunk_get(pa_silence_cache *cache, pa_mempool *pool, pa_memchunk* ret, const pa_sample_spec *spec, size_t length) {
    pa_memblock *b;
    size_t l;

    pa_assert(cache);
    pa_assert(pa_sample_spec_valid(spec));

    if (!(b = cache->blocks[spec->format]))

        switch (spec->format) {
            case PA_SAMPLE_U8:
                cache->blocks[PA_SAMPLE_U8] = b = silence_memblock_new(pool, 0x80);
                break;
            case PA_SAMPLE_S16LE:
            case PA_SAMPLE_S16BE:
            case PA_SAMPLE_S32LE:
            case PA_SAMPLE_S32BE:
            case PA_SAMPLE_S24LE:
            case PA_SAMPLE_S24BE:
            case PA_SAMPLE_S24_32LE:
            case PA_SAMPLE_S24_32BE:
            case PA_SAMPLE_FLOAT32LE:
            case PA_SAMPLE_FLOAT32BE:
                cache->blocks[PA_SAMPLE_S16LE] = b = silence_memblock_new(pool, 0);
                cache->blocks[PA_SAMPLE_S16BE] = pa_memblock_ref(b);
                cache->blocks[PA_SAMPLE_S32LE] = pa_memblock_ref(b);
                cache->blocks[PA_SAMPLE_S32BE] = pa_memblock_ref(b);
                cache->blocks[PA_SAMPLE_S24LE] = pa_memblock_ref(b);
                cache->blocks[PA_SAMPLE_S24BE] = pa_memblock_ref(b);
                cache->blocks[PA_SAMPLE_S24_32LE] = pa_memblock_ref(b);
                cache->blocks[PA_SAMPLE_S24_32BE] = pa_memblock_ref(b);
                cache->blocks[PA_SAMPLE_FLOAT32LE] = pa_memblock_ref(b);
                cache->blocks[PA_SAMPLE_FLOAT32BE] = pa_memblock_ref(b);
                break;
            case PA_SAMPLE_ALAW:
                cache->blocks[PA_SAMPLE_ALAW] = b = silence_memblock_new(pool, 0xd5);
                break;
            case PA_SAMPLE_ULAW:
                cache->blocks[PA_SAMPLE_ULAW] = b = silence_memblock_new(pool, 0xff);
                break;
            default:
                pa_assert_not_reached();
    }

    pa_assert(b);

    ret->memblock = pa_memblock_ref(b);

    l = pa_memblock_get_length(b);
    if (length > l || length == 0)
        length = l;

    ret->length = pa_frame_align(length, spec);
    ret->index = 0;

    return ret;
}
Пример #6
0
/*
 * start recording
 */
static int_fast32_t pulse_start_recording(struct pulse_data *data)
{
	if (pulse_get_server_info(pulse_server_info, (void *) data) < 0) {
		blog(LOG_ERROR, "pulse-input: Unable to get server info !");
		return -1;
	}

	pa_sample_spec spec;
	spec.format   = data->format;
	spec.rate     = data->samples_per_sec;
	spec.channels = data->channels;

	if (!pa_sample_spec_valid(&spec)) {
		blog(LOG_ERROR, "pulse-input: Sample spec is not valid");
		return -1;
	}

	data->bytes_per_frame = pa_frame_size(&spec);
	blog(LOG_DEBUG, "pulse-input: %u bytes per frame",
	     (unsigned int) data->bytes_per_frame);

	data->stream = pulse_stream_new(obs_source_getname(data->source),
		&spec, NULL);
	if (!data->stream) {
		blog(LOG_ERROR, "pulse-input: Unable to create stream");
		return -1;
	}

	pulse_lock();
	pa_stream_set_read_callback(data->stream, pulse_stream_read,
		(void *) data);
	pulse_unlock();

	pa_buffer_attr attr;
	attr.fragsize  = get_buffer_size(data, 250);
	attr.maxlength = (uint32_t) -1;
	attr.minreq    = (uint32_t) -1;
	attr.prebuf    = (uint32_t) -1;
	attr.tlength   = (uint32_t) -1;

	pa_stream_flags_t flags =
		PA_STREAM_INTERPOLATE_TIMING
		| PA_STREAM_AUTO_TIMING_UPDATE
		| PA_STREAM_ADJUST_LATENCY;

	pulse_lock();
	int_fast32_t ret = pa_stream_connect_record(data->stream, data->device,
		&attr, flags);
	pulse_unlock();
	if (ret < 0) {
		blog(LOG_ERROR, "pulse-input: Unable to connect to stream");
		return -1;
	}

	blog(LOG_DEBUG, "pulse-input: Recording started");
	return 0;
}
Пример #7
0
int pa_channel_map_compatible(const pa_channel_map *map, const pa_sample_spec *ss) {
    pa_assert(map);
    pa_assert(ss);

    pa_return_val_if_fail(pa_channel_map_valid(map), 0);
    pa_return_val_if_fail(pa_sample_spec_valid(ss), 0);

    return map->channels == ss->channels;
}
Пример #8
0
void
eventd_sound_pulseaudio_play_data(EventdSoundPulseaudioContext *context, gpointer data, gsize length, gint format, guint32 rate, guint8 channels)
{
    pa_sample_spec sample_spec;
    pa_stream *stream;
    EventdSoundPulseaudioEventData *event_data;

    if ( data == NULL )
        return;

    if ( ( context == NULL ) || ( pa_context_get_state(context->context) != PA_CONTEXT_READY ) )
    {
        g_free(data);
        return;
    }

    switch ( format )
    {
    case SF_FORMAT_PCM_16:
    case SF_FORMAT_PCM_U8:
    case SF_FORMAT_PCM_S8:
        sample_spec.format = PA_SAMPLE_S16NE;
    break;
    case SF_FORMAT_PCM_24:
        sample_spec.format = PA_SAMPLE_S24NE;
    break;
    case SF_FORMAT_PCM_32:
        sample_spec.format = PA_SAMPLE_S32NE;
    break;
    case SF_FORMAT_FLOAT:
    case SF_FORMAT_DOUBLE:
        sample_spec.format = PA_SAMPLE_FLOAT32NE;
    break;
    default:
        g_warning("Unsupported format");
        return;
    }

    sample_spec.rate = rate;
    sample_spec.channels = channels;

    if ( ! pa_sample_spec_valid(&sample_spec) )
    {
        g_warning("Invalid spec");
        return;
    }

    stream = pa_stream_new(context->context, "sndfile plugin playback", &sample_spec, NULL);

    event_data = g_new0(EventdSoundPulseaudioEventData, 1);
    event_data->data = data;
    event_data->length = length;

    pa_stream_set_state_callback(stream, _eventd_sound_pulseaudio_stream_state_callback, event_data);
    pa_stream_connect_playback(stream, NULL, NULL, 0, NULL, NULL);
}
Пример #9
0
int pa_cvolume_compatible(const pa_cvolume *v, const pa_sample_spec *ss) {

    pa_assert(v);
    pa_assert(ss);

    pa_return_val_if_fail(pa_cvolume_valid(v), 0);
    pa_return_val_if_fail(pa_sample_spec_valid(ss), 0);

    return v->channels == ss->channels;
}
Пример #10
0
/** Pretty print a sample type specification to a string */
char* pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec) {
 if ( s == NULL || l == 0 || spec == NULL )
  return NULL;

 if ( pa_sample_spec_valid(spec) ) {
  snprintf(s, l, "%s %uch %uHz", pa_sample_format_to_string(spec->format), spec->channels, spec->rate);
 } else {
  snprintf(s, l, "Invalid");
 }

 return s;
}
Пример #11
0
/*
 * Create a new pulse audio stream and connect to it
 *
 * Return a negative value on error
 */
static int pulse_connect_stream(struct pulse_data *data)
{
	pa_sample_spec spec;
	spec.format = data->format;
	spec.rate = data->samples_per_sec;
	spec.channels = get_audio_channels(data->speakers);

	if (!pa_sample_spec_valid(&spec)) {
		blog(LOG_ERROR, "pulse-input: Sample spec is not valid");
		return -1;
	}

	data->bytes_per_frame = pa_frame_size(&spec);
	blog(LOG_DEBUG, "pulse-input: %u bytes per frame",
	     (unsigned int) data->bytes_per_frame);

	pa_buffer_attr attr;
	attr.fragsize = get_buffer_size(data, 250);
	attr.maxlength = (uint32_t) -1;
	attr.minreq = (uint32_t) -1;
	attr.prebuf = (uint32_t) -1;
	attr.tlength = (uint32_t) -1;

	data->stream = pa_stream_new_with_proplist(data->context,
		obs_source_getname(data->source), &spec, NULL, data->props);
	if (!data->stream) {
		blog(LOG_ERROR, "pulse-input: Unable to create stream");
		return -1;
	}
	pa_stream_flags_t flags =
		PA_STREAM_INTERPOLATE_TIMING
		| PA_STREAM_AUTO_TIMING_UPDATE
		| PA_STREAM_ADJUST_LATENCY;
	if (pa_stream_connect_record(data->stream, NULL, &attr, flags) < 0) {
		blog(LOG_ERROR, "pulse-input: Unable to connect to stream");
		return -1;
	}

	for (;;) {
		pulse_iterate(data);
		pa_stream_state_t state = pa_stream_get_state(data->stream);
		if (state == PA_STREAM_READY) {
			blog(LOG_DEBUG, "pulse-input: Stream ready");
			break;
		}
		if (!PA_STREAM_IS_GOOD(state)) {
			blog(LOG_ERROR, "pulse-input: Stream connect failed");
			return -1;
		}
	}

	return 0;
}
Пример #12
0
int pa_sndfile_read_sample_spec(SNDFILE *sf, pa_sample_spec *ss) {
    SF_INFO sfi;
    int sf_errno;

    pa_assert(sf);
    pa_assert(ss);

    pa_zero(sfi);
    if ((sf_errno = sf_command(sf, SFC_GET_CURRENT_SF_INFO, &sfi, sizeof(sfi)))) {
        pa_log_error("sndfile: %s", sf_error_number(sf_errno));
        return -1;
    }

    switch (sfi.format & SF_FORMAT_SUBMASK) {

        case SF_FORMAT_PCM_16:
        case SF_FORMAT_PCM_U8:
        case SF_FORMAT_PCM_S8:
            ss->format = PA_SAMPLE_S16NE;
            break;

        case SF_FORMAT_PCM_24:
            ss->format = PA_SAMPLE_S24NE;
            break;

        case SF_FORMAT_PCM_32:
            ss->format = PA_SAMPLE_S32NE;
            break;

        case SF_FORMAT_ULAW:
            ss->format = PA_SAMPLE_ULAW;
            break;

        case SF_FORMAT_ALAW:
            ss->format = PA_SAMPLE_ALAW;
            break;

        case SF_FORMAT_FLOAT:
        case SF_FORMAT_DOUBLE:
        default:
            ss->format = PA_SAMPLE_FLOAT32NE;
            break;
    }

    ss->rate = (uint32_t) sfi.samplerate;
    ss->channels = (uint8_t) sfi.channels;

    if (!pa_sample_spec_valid(ss))
        return -1;

    return 0;
}
Пример #13
0
size_t pa_context_get_tile_size(pa_context *c, const pa_sample_spec *ss) {
    size_t fs, mbs;

    pa_assert(c);
    pa_assert(PA_REFCNT_VALUE(c) >= 1);

    PA_CHECK_VALIDITY_RETURN_ANY(c, !pa_detect_fork(), PA_ERR_FORKED, (size_t) -1);
    PA_CHECK_VALIDITY_RETURN_ANY(c, !ss || pa_sample_spec_valid(ss), PA_ERR_INVALID, (size_t) -1);

    fs = ss ? pa_frame_size(ss) : 1;
    mbs = PA_ROUND_DOWN(pa_mempool_block_size_max(c->mempool), fs);
    return PA_MAX(mbs, fs);
}
Пример #14
0
APULSE_EXPORT
char *
pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec)
{
    trace_info_f("F %s s=%p, l=%u, spec=%p\n", __func__, s, (unsigned)l, spec);

    if (spec && pa_sample_spec_valid(spec)) {
        snprintf(s, l, "%s %uch %uHz", pa_sample_format_to_string(spec->format), spec->channels,
                 spec->rate);
    } else {
        snprintf(s, l, "invalid");
    }

    return s;
}
Пример #15
0
/**
 * Pulsaudio init
 */
static void
pa_init ()
{
  int r;
  int i;

  if (!pa_sample_spec_valid (&sample_spec))
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
		_("Wrong Spec\n"));
  }
  /* set up main record loop */
  if (!(m = pa_mainloop_new ()))
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
		_("pa_mainloop_new() failed.\n"));
  }
  mainloop_api = pa_mainloop_get_api (m);

  /* listen to signals */
  r = pa_signal_init (mainloop_api);
  GNUNET_assert (r == 0);
  pa_signal_new (SIGINT, &exit_signal_callback, NULL);
  pa_signal_new (SIGTERM, &exit_signal_callback, NULL);

  /* connect to the main pulseaudio context */

  if (!(context = pa_context_new (mainloop_api, "GNUNET VoIP")))
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
		_("pa_context_new() failed.\n"));
  }
  pa_context_set_state_callback (context, &context_state_callback, NULL);
  if (pa_context_connect (context, NULL, 0, NULL) < 0)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
		_("pa_context_connect() failed: %s\n"),
		pa_strerror (pa_context_errno (context)));
  }
  if (pa_mainloop_run (m, &i) < 0)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
		_("pa_mainloop_run() failed.\n"));
  }
}
Пример #16
0
AudioStream::AudioStream(pa_context *c, pa_threaded_mainloop *m, const char *desc, int type, int smplrate, std::string& deviceName)
    : audiostream_(0), mainloop_(m)
{
    static const pa_channel_map channel_map = {
        1,
        { PA_CHANNEL_POSITION_MONO },
    };

    pa_sample_spec sample_spec = {
        PA_SAMPLE_S16LE, // PA_SAMPLE_FLOAT32LE,
        smplrate,
        1
    };

    assert(pa_sample_spec_valid(&sample_spec));
    assert(pa_channel_map_valid(&channel_map));

    audiostream_ = pa_stream_new(c, desc, &sample_spec, &channel_map);

    if (!audiostream_) {
        ERROR("%s: pa_stream_new() failed : %s" , desc, pa_strerror(pa_context_errno(c)));
        throw std::runtime_error("Could not create stream\n");
    }

    pa_buffer_attr attributes;
    attributes.maxlength = pa_usec_to_bytes(160 * PA_USEC_PER_MSEC, &sample_spec);
    attributes.tlength = pa_usec_to_bytes(80 * PA_USEC_PER_MSEC, &sample_spec);
    attributes.prebuf = 0;
    attributes.fragsize = pa_usec_to_bytes(80 * PA_USEC_PER_MSEC, &sample_spec);
    attributes.minreq = (uint32_t) -1;

    pa_threaded_mainloop_lock(mainloop_);

    if (type == PLAYBACK_STREAM || type == RINGTONE_STREAM)
        pa_stream_connect_playback(audiostream_, deviceName == "" ? NULL : deviceName.c_str(), &attributes,
		(pa_stream_flags_t)(PA_STREAM_ADJUST_LATENCY|PA_STREAM_AUTO_TIMING_UPDATE), NULL, NULL);
    else if (type == CAPTURE_STREAM)
        pa_stream_connect_record(audiostream_, deviceName == "" ? NULL : deviceName.c_str(), &attributes,
		(pa_stream_flags_t)(PA_STREAM_ADJUST_LATENCY|PA_STREAM_AUTO_TIMING_UPDATE));

    pa_threaded_mainloop_unlock(mainloop_);

    pa_stream_set_state_callback(audiostream_, stream_state_callback, NULL);
}
Пример #17
0
/* Initialize a new _mbx_out. This is a helper function for _mbx_out_new() */
static void malloc_and_init(_mbx_out *out_p,
        const char *name, const char *dev_name, _mbx_out_cb cb,
        void *output_cb_userdata)
{
    *out_p = _mbx_xmalloc(sizeof(struct _mbx_out));
    bzero(*out_p, sizeof(struct _mbx_out));
    (*out_p)->state = _MBX_OUT_INITIALIZING;
    (*out_p)->output_cb_userdata = output_cb_userdata;
    (*out_p)->name = _mbx_xstrdup(name);
    (*out_p)->dev_name = _mbx_xstrdup(dev_name);
    /* TODO: The sample spec is hard coded. It should be determined depending
     * on the capabilities of the sound card. */
    (*out_p)->sample_spec.rate = MBX_SAMPLE_RATE;
    (*out_p)->sample_spec.channels = 2; /* stereo */
    (*out_p)->sample_spec.format = PA_SAMPLE_S16LE;
    /* an invalid sample spec would be a programming error */
    assert(pa_sample_spec_valid(&(*out_p)->sample_spec));
    (*out_p)->cb = cb;
}
Пример #18
0
gboolean
gst_pulse_fill_sample_spec (GstRingBufferSpec * spec, pa_sample_spec * ss)
{

  if (spec->format == GST_MU_LAW && spec->width == 8)
    ss->format = PA_SAMPLE_ULAW;
  else if (spec->format == GST_A_LAW && spec->width == 8)
    ss->format = PA_SAMPLE_ALAW;
  else if (spec->format == GST_U8 && spec->width == 8)
    ss->format = PA_SAMPLE_U8;
  else if (spec->format == GST_S16_LE && spec->width == 16)
    ss->format = PA_SAMPLE_S16LE;
  else if (spec->format == GST_S16_BE && spec->width == 16)
    ss->format = PA_SAMPLE_S16BE;
  else if (spec->format == GST_FLOAT32_LE && spec->width == 32)
    ss->format = PA_SAMPLE_FLOAT32LE;
  else if (spec->format == GST_FLOAT32_BE && spec->width == 32)
    ss->format = PA_SAMPLE_FLOAT32BE;
  else if (spec->format == GST_S32_LE && spec->width == 32)
    ss->format = PA_SAMPLE_S32LE;
  else if (spec->format == GST_S32_BE && spec->width == 32)
    ss->format = PA_SAMPLE_S32BE;
#ifdef HAVE_PULSE_0_9_15
  else if (spec->format == GST_S24_3LE && spec->width == 24)
    ss->format = PA_SAMPLE_S24LE;
  else if (spec->format == GST_S24_3BE && spec->width == 24)
    ss->format = PA_SAMPLE_S24BE;
  else if (spec->format == GST_S24_LE && spec->width == 32)
    ss->format = PA_SAMPLE_S24_32LE;
  else if (spec->format == GST_S24_BE && spec->width == 32)
    ss->format = PA_SAMPLE_S24_32BE;
#endif
  else
    return FALSE;

  ss->channels = spec->channels;
  ss->rate = spec->rate;

  if (!pa_sample_spec_valid (ss))
    return FALSE;

  return TRUE;
}
Пример #19
0
pa_format_info* pa_format_info_from_sample_spec(pa_sample_spec *ss, pa_channel_map *map) {
    char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
    pa_format_info *f;

    pa_assert(ss && pa_sample_spec_valid(ss));
    pa_assert(!map || pa_channel_map_valid(map));

    f = pa_format_info_new();
    f->encoding = PA_ENCODING_PCM;

    pa_format_info_set_sample_format(f, ss->format);
    pa_format_info_set_rate(f, ss->rate);
    pa_format_info_set_channels(f, ss->channels);

    if (map) {
        pa_channel_map_snprint(cm, sizeof(cm), map);
        pa_format_info_set_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, cm);
    }

    return f;
}
Пример #20
0
gboolean
gst_pulse_fill_sample_spec (GstAudioRingBufferSpec * spec, pa_sample_spec * ss)
{
  if (spec->type == GST_AUDIO_RING_BUFFER_FORMAT_TYPE_RAW) {
    if (!gstaudioformat_to_pasampleformat (GST_AUDIO_INFO_FORMAT (&spec->info),
            &ss->format))
      return FALSE;
  } else if (spec->type == GST_AUDIO_RING_BUFFER_FORMAT_TYPE_MU_LAW) {
    ss->format = PA_SAMPLE_ULAW;
  } else if (spec->type == GST_AUDIO_RING_BUFFER_FORMAT_TYPE_A_LAW) {
    ss->format = PA_SAMPLE_ALAW;
  } else
    return FALSE;

  ss->channels = GST_AUDIO_INFO_CHANNELS (&spec->info);
  ss->rate = GST_AUDIO_INFO_RATE (&spec->info);

  if (!pa_sample_spec_valid (ss))
    return FALSE;

  return TRUE;
}
Пример #21
0
AudioStream::AudioStream(pa_context *c,
                         pa_threaded_mainloop *m,
                         const char *desc,
                         int type,
                         unsigned samplrate,
                         const PaDeviceInfos* infos,
                         bool ec)
    : audiostream_(0), mainloop_(m)
{
    const pa_channel_map channel_map = infos->channel_map;

    pa_sample_spec sample_spec = {
        PA_SAMPLE_S16LE, // PA_SAMPLE_FLOAT32LE,
        samplrate,
        channel_map.channels
    };

    RING_DBG("%s: trying to create stream with device %s (%dHz, %d channels)", desc, infos->name.c_str(), samplrate, channel_map.channels);

    assert(pa_sample_spec_valid(&sample_spec));
    assert(pa_channel_map_valid(&channel_map));

    std::unique_ptr<pa_proplist, decltype(pa_proplist_free)&> pl (pa_proplist_new(), pa_proplist_free);
    pa_proplist_sets(pl.get(), PA_PROP_FILTER_WANT, "echo-cancel");

    audiostream_ = pa_stream_new_with_proplist(c, desc, &sample_spec, &channel_map, ec ? pl.get() : nullptr);
    if (!audiostream_) {
        RING_ERR("%s: pa_stream_new() failed : %s" , desc, pa_strerror(pa_context_errno(c)));
        throw std::runtime_error("Could not create stream\n");
    }

    pa_buffer_attr attributes;
    attributes.maxlength = pa_usec_to_bytes(160 * PA_USEC_PER_MSEC, &sample_spec);
    attributes.tlength = pa_usec_to_bytes(80 * PA_USEC_PER_MSEC, &sample_spec);
    attributes.prebuf = 0;
    attributes.fragsize = pa_usec_to_bytes(80 * PA_USEC_PER_MSEC, &sample_spec);
    attributes.minreq = (uint32_t) -1;

    {
        PulseMainLoopLock lock(mainloop_);
        const pa_stream_flags_t flags = static_cast<pa_stream_flags_t>(PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE);

        if (type == PLAYBACK_STREAM || type == RINGTONE_STREAM) {
            pa_stream_connect_playback(audiostream_,
                    infos->name.empty() ? NULL : infos->name.c_str(),
                    &attributes,
                    flags,
                    NULL, NULL);
        } else if (type == CAPTURE_STREAM) {
            pa_stream_connect_record(audiostream_,
                    infos->name.empty() ? NULL : infos->name.c_str(),
                    &attributes,
                    flags);
        }
    }

    pa_stream_set_state_callback(audiostream_, [](pa_stream* s, void* user_data){
        static_cast<AudioStream*>(user_data)->stateChanged(s);
    }, this);
    pa_stream_set_moved_callback(audiostream_, [](pa_stream* s, void* user_data){
        static_cast<AudioStream*>(user_data)->moved(s);
    }, this);
}
int pa__init(pa_module*m) {
    struct userdata *u = NULL;
    pa_sample_spec ss;
    pa_channel_map map;
    pa_modargs *ma = NULL;
    jack_status_t status;
    const char *server_name, *client_name;
    uint32_t channels = 0;
    pa_bool_t do_connect = TRUE;
    unsigned i;
    const char **ports = NULL, **p;
    pa_sink_new_data data;

    pa_assert(m);

    jack_set_error_function(jack_error_func);

    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
        pa_log("Failed to parse module arguments.");
        goto fail;
    }

    if (pa_modargs_get_value_boolean(ma, "connect", &do_connect) < 0) {
        pa_log("Failed to parse connect= argument.");
        goto fail;
    }

    server_name = pa_modargs_get_value(ma, "server_name", NULL);
    client_name = pa_modargs_get_value(ma, "client_name", "PulseAudio JACK Sink");

    m->userdata = u = pa_xnew0(struct userdata, 1);
    u->core = m->core;
    u->module = m;
    u->saved_frame_time_valid = FALSE;
    u->rtpoll = pa_rtpoll_new();
    pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);

    /* The queue linking the JACK thread and our RT thread */
    u->jack_msgq = pa_asyncmsgq_new(0);

    /* The msgq from the JACK RT thread should have an even higher
     * priority than the normal message queues, to match the guarantee
     * all other drivers make: supplying the audio device with data is
     * the top priority -- and as long as that is possible we don't do
     * anything else */
    u->rtpoll_item = pa_rtpoll_item_new_asyncmsgq_read(u->rtpoll, PA_RTPOLL_EARLY-1, u->jack_msgq);

    if (!(u->client = jack_client_open(client_name, server_name ? JackServerName : JackNullOption, &status, server_name))) {
        pa_log("jack_client_open() failed.");
        goto fail;
    }

    ports = jack_get_ports(u->client, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortIsPhysical|JackPortIsInput);

    channels = 0;
    for (p = ports; *p; p++)
        channels++;

    if (!channels)
        channels = m->core->default_sample_spec.channels;

    if (pa_modargs_get_value_u32(ma, "channels", &channels) < 0 ||
        channels <= 0 ||
        channels > PA_CHANNELS_MAX) {
        pa_log("Failed to parse channels= argument.");
        goto fail;
    }

    if (channels == m->core->default_channel_map.channels)
        map = m->core->default_channel_map;
    else
        pa_channel_map_init_extend(&map, channels, PA_CHANNEL_MAP_ALSA);

    if (pa_modargs_get_channel_map(ma, NULL, &map) < 0 || map.channels != channels) {
        pa_log("Failed to parse channel_map= argument.");
        goto fail;
    }

    pa_log_info("Successfully connected as '%s'", jack_get_client_name(u->client));

    u->channels = ss.channels = (uint8_t) channels;
    ss.rate = jack_get_sample_rate(u->client);
    ss.format = PA_SAMPLE_FLOAT32NE;

    pa_assert(pa_sample_spec_valid(&ss));

    for (i = 0; i < ss.channels; i++) {
        if (!(u->port[i] = jack_port_register(u->client, pa_channel_position_to_string(map.map[i]), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput|JackPortIsTerminal, 0))) {
            pa_log("jack_port_register() failed.");
            goto fail;
        }
    }

    pa_sink_new_data_init(&data);
    data.driver = __FILE__;
    data.module = m;
    pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
    pa_sink_new_data_set_sample_spec(&data, &ss);
    pa_sink_new_data_set_channel_map(&data, &map);
    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "jack");
    if (server_name)
        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server_name);
    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Jack sink (%s)", jack_get_client_name(u->client));
    pa_proplist_sets(data.proplist, "jack.client_name", jack_get_client_name(u->client));

    if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
        pa_log("Invalid properties");
        pa_sink_new_data_done(&data);
        goto fail;
    }

    u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY);
    pa_sink_new_data_done(&data);

    if (!u->sink) {
        pa_log("Failed to create sink.");
        goto fail;
    }

    u->sink->parent.process_msg = sink_process_msg;
    u->sink->userdata = u;

    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
    pa_sink_set_rtpoll(u->sink, u->rtpoll);
    pa_sink_set_max_request(u->sink, jack_get_buffer_size(u->client) * pa_frame_size(&u->sink->sample_spec));

    jack_set_process_callback(u->client, jack_process, u);
    jack_on_shutdown(u->client, jack_shutdown, u);
    jack_set_thread_init_callback(u->client, jack_init, u);
    jack_set_buffer_size_callback(u->client, jack_buffer_size, u);

    if (!(u->thread = pa_thread_new(thread_func, u))) {
        pa_log("Failed to create thread.");
        goto fail;
    }

    if (jack_activate(u->client)) {
        pa_log("jack_activate() failed");
        goto fail;
    }

    if (do_connect) {
        for (i = 0, p = ports; i < ss.channels; i++, p++) {

            if (!*p) {
                pa_log("Not enough physical output ports, leaving unconnected.");
                break;
            }

            pa_log_info("Connecting %s to %s", jack_port_name(u->port[i]), *p);

            if (jack_connect(u->client, jack_port_name(u->port[i]), *p)) {
                pa_log("Failed to connect %s to %s, leaving unconnected.", jack_port_name(u->port[i]), *p);
                break;
            }
        }
    }

    pa_sink_put(u->sink);

    free(ports);
    pa_modargs_free(ma);

    return 0;

fail:
    if (ma)
        pa_modargs_free(ma);

    free(ports);

    pa__done(m);

    return -1;
}
Пример #23
0
int pa_sndfile_write_sample_spec(SF_INFO *sfi, pa_sample_spec *ss) {
    pa_assert(sfi);
    pa_assert(ss);

    sfi->samplerate = (int) ss->rate;
    sfi->channels = (int) ss->channels;

    if (pa_sample_format_is_le(ss->format) > 0)
        sfi->format = SF_ENDIAN_LITTLE;
    else if (pa_sample_format_is_be(ss->format) > 0)
        sfi->format = SF_ENDIAN_BIG;

    switch (ss->format) {

        case PA_SAMPLE_U8:
            ss->format = PA_SAMPLE_S16NE;
            sfi->format = SF_FORMAT_PCM_U8;
            break;

        case PA_SAMPLE_S16LE:
        case PA_SAMPLE_S16BE:
            ss->format = PA_SAMPLE_S16NE;
            sfi->format |= SF_FORMAT_PCM_16;
            break;

        case PA_SAMPLE_S24LE:
        case PA_SAMPLE_S24BE:
            ss->format = PA_SAMPLE_S24NE;
            sfi->format |= SF_FORMAT_PCM_24;
            break;

        case PA_SAMPLE_S24_32LE:
        case PA_SAMPLE_S24_32BE:
            ss->format = PA_SAMPLE_S24_32NE;
            sfi->format |= SF_FORMAT_PCM_32;
            break;

        case PA_SAMPLE_S32LE:
        case PA_SAMPLE_S32BE:
            ss->format = PA_SAMPLE_S32NE;
            sfi->format |= SF_FORMAT_PCM_32;
            break;

        case PA_SAMPLE_ULAW:
            sfi->format = SF_FORMAT_ULAW;
            break;

        case PA_SAMPLE_ALAW:
            sfi->format = SF_FORMAT_ALAW;
            break;

        case PA_SAMPLE_FLOAT32LE:
        case PA_SAMPLE_FLOAT32BE:
        default:
            ss->format = PA_SAMPLE_FLOAT32NE;
            sfi->format |= SF_FORMAT_FLOAT;
            break;
    }

    if (!pa_sample_spec_valid(ss))
        return -1;

    return 0;
}
Пример #24
0
int main(int argc, char *argv[]) {
    pa_mainloop* m = NULL;
    int ret = 1, c;
    char *bn, *server = NULL;
    pa_time_event *time_event = NULL;
    const char *filename = NULL;

    static const struct option long_options[] = {
        {"record",       0, NULL, 'r'},
        {"playback",     0, NULL, 'p'},
        {"device",       1, NULL, 'd'},
        {"server",       1, NULL, 's'},
        {"client-name",  1, NULL, 'n'},
        {"stream-name",  1, NULL, ARG_STREAM_NAME},
        {"version",      0, NULL, ARG_VERSION},
        {"help",         0, NULL, 'h'},
        {"verbose",      0, NULL, 'v'},
        {"volume",       1, NULL, ARG_VOLUME},
        {"rate",         1, NULL, ARG_SAMPLERATE},
        {"format",       1, NULL, ARG_SAMPLEFORMAT},
        {"channels",     1, NULL, ARG_CHANNELS},
        {"channel-map",  1, NULL, ARG_CHANNELMAP},
        {"fix-format",   0, NULL, ARG_FIX_FORMAT},
        {"fix-rate",     0, NULL, ARG_FIX_RATE},
        {"fix-channels", 0, NULL, ARG_FIX_CHANNELS},
        {"no-remap",     0, NULL, ARG_NO_REMAP},
        {"no-remix",     0, NULL, ARG_NO_REMIX},
        {"latency",      1, NULL, ARG_LATENCY},
        {"process-time", 1, NULL, ARG_PROCESS_TIME},
        {"property",     1, NULL, ARG_PROPERTY},
        {"raw",          0, NULL, ARG_RAW},
        {"file-format",  2, NULL, ARG_FILE_FORMAT},
        {"list-file-formats", 0, NULL, ARG_LIST_FILE_FORMATS},
        {"latency-msec", 1, NULL, ARG_LATENCY_MSEC},
        {"process-time-msec", 1, NULL, ARG_PROCESS_TIME_MSEC},
        {NULL,           0, NULL, 0}
    };

    setlocale(LC_ALL, "");
    bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR);

    bn = pa_path_get_filename(argv[0]);

    if (strstr(bn, "play")) {
        mode = PLAYBACK;
        raw = FALSE;
    } else if (strstr(bn, "record")) {
        mode = RECORD;
        raw = FALSE;
    } else if (strstr(bn, "cat")) {
        mode = PLAYBACK;
        raw = TRUE;
    } if (strstr(bn, "rec") || strstr(bn, "mon")) {
        mode = RECORD;
        raw = TRUE;
    }

    proplist = pa_proplist_new();

    while ((c = getopt_long(argc, argv, "rpd:s:n:hv", long_options, NULL)) != -1) {

        switch (c) {
            case 'h' :
                help(bn);
                ret = 0;
                goto quit;

            case ARG_VERSION:
                printf(_("pacat %s\n"
                         "Compiled with libpulse %s\n"
                         "Linked with libpulse %s\n"),
                       PACKAGE_VERSION,
                       pa_get_headers_version(),
                       pa_get_library_version());
                ret = 0;
                goto quit;

            case 'r':
                mode = RECORD;
                break;

            case 'p':
                mode = PLAYBACK;
                break;

            case 'd':
                pa_xfree(device);
                device = pa_xstrdup(optarg);
                break;

            case 's':
                pa_xfree(server);
                server = pa_xstrdup(optarg);
                break;

            case 'n': {
                char *t;

                if (!(t = pa_locale_to_utf8(optarg)) ||
                    pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, t) < 0) {

                    pa_log(_("Invalid client name '%s'"), t ? t : optarg);
                    pa_xfree(t);
                    goto quit;
                }

                pa_xfree(t);
                break;
            }

            case ARG_STREAM_NAME: {
                char *t;

                if (!(t = pa_locale_to_utf8(optarg)) ||
                    pa_proplist_sets(proplist, PA_PROP_MEDIA_NAME, t) < 0) {

                    pa_log(_("Invalid stream name '%s'"), t ? t : optarg);
                    pa_xfree(t);
                    goto quit;
                }

                pa_xfree(t);
                break;
            }

            case 'v':
                verbose = 1;
                break;

            case ARG_VOLUME: {
                int v = atoi(optarg);
                volume = v < 0 ? 0U : (pa_volume_t) v;
                volume_is_set = TRUE;
                break;
            }

            case ARG_CHANNELS:
                sample_spec.channels = (uint8_t) atoi(optarg);
                sample_spec_set = TRUE;
                break;

            case ARG_SAMPLEFORMAT:
                sample_spec.format = pa_parse_sample_format(optarg);
                sample_spec_set = TRUE;
                break;

            case ARG_SAMPLERATE:
                sample_spec.rate = (uint32_t) atoi(optarg);
                sample_spec_set = TRUE;
                break;

            case ARG_CHANNELMAP:
                if (!pa_channel_map_parse(&channel_map, optarg)) {
                    pa_log(_("Invalid channel map '%s'"), optarg);
                    goto quit;
                }

                channel_map_set = TRUE;
                break;

            case ARG_FIX_CHANNELS:
                flags |= PA_STREAM_FIX_CHANNELS;
                break;

            case ARG_FIX_RATE:
                flags |= PA_STREAM_FIX_RATE;
                break;

            case ARG_FIX_FORMAT:
                flags |= PA_STREAM_FIX_FORMAT;
                break;

            case ARG_NO_REMIX:
                flags |= PA_STREAM_NO_REMIX_CHANNELS;
                break;

            case ARG_NO_REMAP:
                flags |= PA_STREAM_NO_REMAP_CHANNELS;
                break;

            case ARG_LATENCY:
                if (((latency = (size_t) atoi(optarg))) <= 0) {
                    pa_log(_("Invalid latency specification '%s'"), optarg);
                    goto quit;
                }
                break;

            case ARG_PROCESS_TIME:
                if (((process_time = (size_t) atoi(optarg))) <= 0) {
                    pa_log(_("Invalid process time specification '%s'"), optarg);
                    goto quit;
                }
                break;

            case ARG_LATENCY_MSEC:
                if (((latency_msec = (int32_t) atoi(optarg))) <= 0) {
                    pa_log(_("Invalid latency specification '%s'"), optarg);
                    goto quit;
                }
                break;

            case ARG_PROCESS_TIME_MSEC:
                if (((process_time_msec = (int32_t) atoi(optarg))) <= 0) {
                    pa_log(_("Invalid process time specification '%s'"), optarg);
                    goto quit;
                }
                break;

            case ARG_PROPERTY: {
                char *t;

                if (!(t = pa_locale_to_utf8(optarg)) ||
                    pa_proplist_setp(proplist, t) < 0) {

                    pa_xfree(t);
                    pa_log(_("Invalid property '%s'"), optarg);
                    goto quit;
                }

                pa_xfree(t);
                break;
            }

            case ARG_RAW:
                raw = TRUE;
                break;

            case ARG_FILE_FORMAT:
                raw = FALSE;

                if (optarg) {
                    if ((file_format = pa_sndfile_format_from_string(optarg)) < 0) {
                        pa_log(_("Unknown file format %s."), optarg);
                        goto quit;
                    }
                }

                raw = FALSE;
                break;

            case ARG_LIST_FILE_FORMATS:
                pa_sndfile_dump_formats();
                ret = 0;
                goto quit;

            default:
                goto quit;
        }
    }

    if (!pa_sample_spec_valid(&sample_spec)) {
        pa_log(_("Invalid sample specification"));
        goto quit;
    }

    if (optind+1 == argc) {
        int fd;

        filename = argv[optind];

        if ((fd = open(argv[optind], mode == PLAYBACK ? O_RDONLY : O_WRONLY|O_TRUNC|O_CREAT, 0666)) < 0) {
            pa_log(_("open(): %s"), strerror(errno));
            goto quit;
        }

        if (dup2(fd, mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO) < 0) {
            pa_log(_("dup2(): %s"), strerror(errno));
            goto quit;
        }

        pa_close(fd);

    } else if (optind+1 <= argc) {
        pa_log(_("Too many arguments."));
        goto quit;
    }

    if (!raw) {
        SF_INFO sfi;
        pa_zero(sfi);

        if (mode == RECORD) {
            /* This might patch up the sample spec */
            if (pa_sndfile_write_sample_spec(&sfi, &sample_spec) < 0) {
                pa_log(_("Failed to generate sample specification for file."));
                goto quit;
            }

            /* Transparently upgrade classic .wav to wavex for multichannel audio */
            if (file_format <= 0) {
                if ((sample_spec.channels == 2 && (!channel_map_set || (channel_map.map[0] == PA_CHANNEL_POSITION_LEFT &&
                                                                        channel_map.map[1] == PA_CHANNEL_POSITION_RIGHT))) ||
                    (sample_spec.channels == 1 && (!channel_map_set || (channel_map.map[0] == PA_CHANNEL_POSITION_MONO))))
                    file_format = SF_FORMAT_WAV;
                else
                    file_format = SF_FORMAT_WAVEX;
            }

            sfi.format |= file_format;
        }

        if (!(sndfile = sf_open_fd(mode == RECORD ? STDOUT_FILENO : STDIN_FILENO,
                                   mode == RECORD ? SFM_WRITE : SFM_READ,
                                   &sfi, 0))) {
            pa_log(_("Failed to open audio file."));
            goto quit;
        }

        if (mode == PLAYBACK) {
            if (sample_spec_set)
                pa_log(_("Warning: specified sample specification will be overwritten with specification from file."));

            if (pa_sndfile_read_sample_spec(sndfile, &sample_spec) < 0) {
                pa_log(_("Failed to determine sample specification from file."));
                goto quit;
            }
            sample_spec_set = TRUE;

            if (!channel_map_set) {
                /* Allow the user to overwrite the channel map on the command line */
                if (pa_sndfile_read_channel_map(sndfile, &channel_map) < 0) {
                    if (sample_spec.channels > 2)
                        pa_log(_("Warning: Failed to determine channel map from file."));
                } else
                    channel_map_set = TRUE;
            }
        }
    }

    if (!channel_map_set)
        pa_channel_map_init_extend(&channel_map, sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);

    if (!pa_channel_map_compatible(&channel_map, &sample_spec)) {
        pa_log(_("Channel map doesn't match sample specification"));
        goto quit;
    }

    if (!raw) {
        pa_proplist *sfp;

        if (mode == PLAYBACK)
            readf_function = pa_sndfile_readf_function(&sample_spec);
        else {
            if (pa_sndfile_write_channel_map(sndfile, &channel_map) < 0)
                pa_log(_("Warning: failed to write channel map to file."));

            writef_function = pa_sndfile_writef_function(&sample_spec);
        }

        /* Fill in libsndfile prop list data */
        sfp = pa_proplist_new();
        pa_sndfile_init_proplist(sndfile, sfp);
        pa_proplist_update(proplist, PA_UPDATE_MERGE, sfp);
        pa_proplist_free(sfp);
    }

    if (verbose) {
        char tss[PA_SAMPLE_SPEC_SNPRINT_MAX], tcm[PA_CHANNEL_MAP_SNPRINT_MAX];

        pa_log(_("Opening a %s stream with sample specification '%s' and channel map '%s'."),
                mode == RECORD ? _("recording") : _("playback"),
                pa_sample_spec_snprint(tss, sizeof(tss), &sample_spec),
                pa_channel_map_snprint(tcm, sizeof(tcm), &channel_map));
    }

    /* Fill in client name if none was set */
    if (!pa_proplist_contains(proplist, PA_PROP_APPLICATION_NAME)) {
        char *t;

        if ((t = pa_locale_to_utf8(bn))) {
            pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, t);
            pa_xfree(t);
        }
    }

    /* Fill in media name if none was set */
    if (!pa_proplist_contains(proplist, PA_PROP_MEDIA_NAME)) {
        const char *t;

        if ((t = filename) ||
            (t = pa_proplist_gets(proplist, PA_PROP_APPLICATION_NAME)))
            pa_proplist_sets(proplist, PA_PROP_MEDIA_NAME, t);
    }

    /* Set up a new main loop */
    if (!(m = pa_mainloop_new())) {
        pa_log(_("pa_mainloop_new() failed."));
        goto quit;
    }

    mainloop_api = pa_mainloop_get_api(m);

    pa_assert_se(pa_signal_init(mainloop_api) == 0);
    pa_signal_new(SIGINT, exit_signal_callback, NULL);
    pa_signal_new(SIGTERM, exit_signal_callback, NULL);
#ifdef SIGUSR1
    pa_signal_new(SIGUSR1, sigusr1_signal_callback, NULL);
#endif
    pa_disable_sigpipe();

    if (raw) {
        if (!(stdio_event = mainloop_api->io_new(mainloop_api,
                                                 mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO,
                                                 mode == PLAYBACK ? PA_IO_EVENT_INPUT : PA_IO_EVENT_OUTPUT,
                                                 mode == PLAYBACK ? stdin_callback : stdout_callback, NULL))) {
            pa_log(_("io_new() failed."));
            goto quit;
        }
    }

    /* Create a new connection context */
    if (!(context = pa_context_new_with_proplist(mainloop_api, NULL, proplist))) {
        pa_log(_("pa_context_new() failed."));
        goto quit;
    }

    pa_context_set_state_callback(context, context_state_callback, NULL);

    /* Connect the context */
    if (pa_context_connect(context, server, 0, NULL) < 0) {
        pa_log(_("pa_context_connect() failed: %s"), pa_strerror(pa_context_errno(context)));
        goto quit;
    }

    if (verbose) {
        if (!(time_event = pa_context_rttime_new(context, pa_rtclock_now() + TIME_EVENT_USEC, time_event_callback, NULL))) {
            pa_log(_("pa_context_rttime_new() failed."));
            goto quit;
        }
    }

    /* Run the main loop */
    if (pa_mainloop_run(m, &ret) < 0) {
        pa_log(_("pa_mainloop_run() failed."));
        goto quit;
    }

quit:
    if (stream)
        pa_stream_unref(stream);

    if (context)
        pa_context_unref(context);

    if (stdio_event) {
        pa_assert(mainloop_api);
        mainloop_api->io_free(stdio_event);
    }

    if (time_event) {
        pa_assert(mainloop_api);
        mainloop_api->time_free(time_event);
    }

    if (m) {
        pa_signal_done();
        pa_mainloop_free(m);
    }

    pa_xfree(buffer);

    pa_xfree(server);
    pa_xfree(device);

    if (sndfile)
        sf_close(sndfile);

    if (proplist)
        pa_proplist_free(proplist);

    return ret;
}
Пример #25
0
static int init(struct ao *ao, char *params)
{
    struct pa_sample_spec ss;
    struct pa_channel_map map;
    char *devarg = NULL;
    char *host = NULL;
    char *sink = NULL;
    const char *version = pa_get_library_version();

    struct priv *priv = talloc_zero(ao, struct priv);
    ao->priv = priv;

    if (params) {
        devarg = strdup(params);
        sink = strchr(devarg, ':');
        if (sink)
            *sink++ = 0;
        if (devarg[0])
            host = devarg;
    }

    priv->broken_pause = false;
    /* not sure which versions are affected, assume 0.9.11* to 0.9.14*
     * known bad: 0.9.14, 0.9.13
     * known good: 0.9.9, 0.9.10, 0.9.15
     * To test: pause, wait ca. 5 seconds, framestep and see if MPlayer
     * hangs somewhen. */
    if (strncmp(version, "0.9.1", 5) == 0 && version[5] >= '1'
        && version[5] <= '4') {
        mp_msg(MSGT_AO, MSGL_WARN,
               "[pulse] working around probably broken pause functionality,\n"
               "        see http://www.pulseaudio.org/ticket/440\n");
        priv->broken_pause = true;
    }

    ss.channels = ao->channels;
    ss.rate = ao->samplerate;

    const struct format_map *fmt_map = format_maps;
    while (fmt_map->mp_format != ao->format) {
        if (fmt_map->mp_format == AF_FORMAT_UNKNOWN) {
            mp_msg(MSGT_AO, MSGL_V,
                   "AO: [pulse] Unsupported format, using default\n");
            fmt_map = format_maps;
            break;
        }
        fmt_map++;
    }
    ao->format = fmt_map->mp_format;
    ss.format = fmt_map->pa_format;

    if (!pa_sample_spec_valid(&ss)) {
        mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Invalid sample spec\n");
        goto fail;
    }

    pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_ALSA);
    ao->bps = pa_bytes_per_second(&ss);

    if (!(priv->mainloop = pa_threaded_mainloop_new())) {
        mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Failed to allocate main loop\n");
        goto fail;
    }

    if (!(priv->context = pa_context_new(pa_threaded_mainloop_get_api(
                                 priv->mainloop), PULSE_CLIENT_NAME))) {
        mp_msg(MSGT_AO, MSGL_ERR, "AO: [pulse] Failed to allocate context\n");
        goto fail;
    }

    pa_context_set_state_callback(priv->context, context_state_cb, ao);

    if (pa_context_connect(priv->context, host, 0, NULL) < 0)
        goto fail;

    pa_threaded_mainloop_lock(priv->mainloop);

    if (pa_threaded_mainloop_start(priv->mainloop) < 0)
        goto unlock_and_fail;

    /* Wait until the context is ready */
    pa_threaded_mainloop_wait(priv->mainloop);

    if (pa_context_get_state(priv->context) != PA_CONTEXT_READY)
        goto unlock_and_fail;

    if (!(priv->stream = pa_stream_new(priv->context, "audio stream", &ss,
                                       &map)))
        goto unlock_and_fail;

    pa_stream_set_state_callback(priv->stream, stream_state_cb, ao);
    pa_stream_set_write_callback(priv->stream, stream_request_cb, ao);
    pa_stream_set_latency_update_callback(priv->stream,
                                          stream_latency_update_cb, ao);
    pa_buffer_attr bufattr = {
        .maxlength = -1,
        .tlength = pa_usec_to_bytes(1000000, &ss),
        .prebuf = -1,
        .minreq = -1,
        .fragsize = -1,
    };
    if (pa_stream_connect_playback(priv->stream, sink, &bufattr,
                                   PA_STREAM_INTERPOLATE_TIMING
                                   | PA_STREAM_AUTO_TIMING_UPDATE, NULL,
                                   NULL) < 0)
        goto unlock_and_fail;

    /* Wait until the stream is ready */
    pa_threaded_mainloop_wait(priv->mainloop);

    if (pa_stream_get_state(priv->stream) != PA_STREAM_READY)
        goto unlock_and_fail;

    pa_threaded_mainloop_unlock(priv->mainloop);

    free(devarg);
    return 0;

unlock_and_fail:

    if (priv->mainloop)
        pa_threaded_mainloop_unlock(priv->mainloop);

fail:
    if (priv->context)
        GENERIC_ERR_MSG(priv->context, "Init failed");
    free(devarg);
    uninit(ao, true);
    return -1;
}

static void cork(struct ao *ao, bool pause)
{
    struct priv *priv = ao->priv;
    pa_threaded_mainloop_lock(priv->mainloop);
    priv->retval = 0;
    if (!waitop(priv, pa_stream_cork(priv->stream, pause, success_cb, ao)) ||
        !priv->retval)
        GENERIC_ERR_MSG(priv->context, "pa_stream_cork() failed");
}

// Play the specified data to the pulseaudio server
static int play(struct ao *ao, void *data, int len, int flags)
{
    struct priv *priv = ao->priv;
    /* For some reason Pulseaudio behaves worse if this is done after
     * the write - rapidly repeated seeks result in bogus increasing
     * reported latency. */
    if (priv->did_reset)
        cork(ao, false);
    pa_threaded_mainloop_lock(priv->mainloop);
    if (pa_stream_write(priv->stream, data, len, NULL, 0,
                        PA_SEEK_RELATIVE) < 0) {
        GENERIC_ERR_MSG(priv->context, "pa_stream_write() failed");
        len = -1;
    }
    if (priv->did_reset) {
        priv->did_reset = false;
        if (!waitop(priv, pa_stream_update_timing_info(priv->stream,
                                                       success_cb, ao))
            || !priv->retval)
            GENERIC_ERR_MSG(priv->context, "pa_stream_UPP() failed");
    } else
        pa_threaded_mainloop_unlock(priv->mainloop);
    return len;
}

// Reset the audio stream, i.e. flush the playback buffer on the server side
static void reset(struct ao *ao)
{
    // pa_stream_flush() works badly if not corked
    cork(ao, true);
    struct priv *priv = ao->priv;
    pa_threaded_mainloop_lock(priv->mainloop);
    priv->retval = 0;
    if (!waitop(priv, pa_stream_flush(priv->stream, success_cb, ao)) ||
        !priv->retval)
        GENERIC_ERR_MSG(priv->context, "pa_stream_flush() failed");
    priv->did_reset = true;
}
Пример #26
0
bool CPulseAESound::Initialize()
{
  /* we dont re-init the wav loader in PA as PA handles the samplerate */
  if (!m_wavLoader.IsValid())
    return false;

  m_sampleSpec.format   = PA_SAMPLE_FLOAT32NE;
  m_sampleSpec.rate     = m_wavLoader.GetSampleRate();
  m_sampleSpec.channels = m_wavLoader.GetChannelLayout().Count();

  if (!pa_sample_spec_valid(&m_sampleSpec))
  {
    CLog::Log(LOGERROR, "CPulseAESound::Initialize - Invalid sample spec");
    return false;
  }

  struct pa_channel_map map;
  map.channels = m_sampleSpec.channels;
  switch (map.channels)
  {
    case 1:
      map.map[0] = PA_CHANNEL_POSITION_MONO;
      break;

    case 2:
      map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
      map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
      break;

    default:
      CLog::Log(LOGERROR, "CPulseAESound::Initialize - We do not yet support multichannel sounds");
      return false;
  }

  m_maxVolume     = CAEFactory::GetEngine()->GetVolume();
  m_volume        = 1.0f;
  pa_volume_t paVolume = pa_sw_volume_from_linear((double)(m_volume * m_maxVolume));
  pa_cvolume_set(&m_chVolume, m_sampleSpec.channels, paVolume);

  pa_threaded_mainloop_lock(m_mainLoop);
  if ((m_stream = pa_stream_new(m_context, m_pulseName.c_str(), &m_sampleSpec, &map)) == NULL)
  {
    CLog::Log(LOGERROR, "CPulseAESound::Initialize - Could not create a stream");
    pa_threaded_mainloop_unlock(m_mainLoop);
    return false;
  }

  pa_stream_set_state_callback(m_stream, CPulseAESound::StreamStateCallback, this);
  pa_stream_set_write_callback(m_stream, CPulseAESound::StreamWriteCallback, this);

  if (pa_stream_connect_upload(m_stream, m_wavLoader.GetFrameCount() * pa_frame_size(&m_sampleSpec)) != 0)
  {
    CLog::Log(LOGERROR, "CPulseAESound::Initialize - Could not initialize the stream");
    pa_stream_disconnect(m_stream);
    m_stream = NULL;
    pa_threaded_mainloop_unlock(m_mainLoop);
    return false;
  }

  /* check if the stream failed */
  if (pa_stream_get_state(m_stream) == PA_STREAM_FAILED)
  {
    CLog::Log(LOGERROR, "CPulseAESound::Initialize - Waited for the stream but it failed");
    pa_stream_disconnect(m_stream);
    m_stream = NULL;
    pa_threaded_mainloop_unlock(m_mainLoop);
    return false;
  }

  pa_threaded_mainloop_unlock(m_mainLoop);
  return true;
}
Пример #27
0
int PulseAudioDriver::setup(bool capture, bool playback, const QString& )
{
	PENTER;
	
	sample_spec.rate = frame_rate;
	sample_spec.channels = 2;
	sample_spec.format = PA_SAMPLE_FLOAT32NE;
	
	assert(pa_sample_spec_valid(&sample_spec));
	
	if (channel_map_set && channel_map.channels != sample_spec.channels) {
		fprintf(stderr, "Channel map doesn't match file.\n");
		return -1;
	}
	
	/* Set up a new main loop */
	if (!(mainloop = pa_mainloop_new())) {
		fprintf(stderr, "pa_mainloop_new() failed.\n");
		return -1;
	}

	mainloop_api = pa_mainloop_get_api(mainloop);

	int r = pa_signal_init(mainloop_api);
	assert(r == 0);

	/* Create a new connection context */
	if (!(context = pa_context_new(mainloop_api, "Traverso"))) {
		fprintf(stderr, "pa_context_new() failed.\n");
		return -1;
	}

	pa_context_set_state_callback(context, context_state_callback, this);

	/* Connect the context */
	pa_context_connect(context, "", (pa_context_flags_t)0, NULL);

	int ret;
	/* Run the main loop */
// 	if (pa_mainloop_run(mainloop, &ret) < 0) {
// 		fprintf(stderr, "pa_mainloop_run() failed.\n");
// 		return -1;
// 	}


	AudioChannel* audiochannel;
	int port_flags;
	char buf[32];
	
	// TODO use the found maxchannel count for the playback stream, instead of assuming 2 !!
	for (int chn = 0; chn < 2; chn++) {

		snprintf (buf, sizeof(buf) - 1, "playback_%d", chn+1);

		audiochannel = device->register_playback_channel(buf, "32 bit float audio", port_flags, frames_per_cycle, chn);
		audiochannel->set_latency( frames_per_cycle + capture_frame_latency );
		playbackChannels.append(audiochannel);
	}

	// TODO use the found maxchannel count for the capture stream, instead of assuming 0 !!
	for (int chn = 0; chn < 2; chn++) {

		snprintf (buf, sizeof(buf) - 1, "capture_%d", chn+1);

		audiochannel = device->register_capture_channel(buf, "32 bit float audio", port_flags, frames_per_cycle, chn);
		audiochannel->set_latency( frames_per_cycle + capture_frame_latency );
		captureChannels.append(audiochannel);
	}

	return 1;
}
Пример #28
0
static int pulse_open()
{
  ENTER(__FUNCTION__);
    pa_sample_spec ss;
    pa_operation *o = NULL;
    int success;
    int ret = PULSE_ERROR;

    assert(!mainloop);
    assert(!context);
    assert(!stream);
    assert(!connected);

    pthread_mutex_init( &pulse_mutex, (const pthread_mutexattr_t *)NULL);

    ss.format = ESPEAK_FORMAT;
    ss.rate = wave_samplerate;
    ss.channels = ESPEAK_CHANNEL;

    if (!pa_sample_spec_valid(&ss))
      return false;

    SHOW_TIME("pa_threaded_mainloop_new (call)");
    if (!(mainloop = pa_threaded_mainloop_new())) {
      SHOW("Failed to allocate main loop\n","");
        goto fail;
    }

    pa_threaded_mainloop_lock(mainloop);

    SHOW_TIME("pa_context_new (call)");
    if (!(context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), "eSpeak"))) {
      SHOW("Failed to allocate context\n","");
      goto unlock_and_fail;
    }

    pa_context_set_state_callback(context, context_state_cb, NULL);
    pa_context_set_subscribe_callback(context, subscribe_cb, NULL);

    SHOW_TIME("pa_context_connect (call)");
    if (pa_context_connect(context, NULL, (pa_context_flags_t)0, NULL) < 0) {
        SHOW("Failed to connect to server: %s", pa_strerror(pa_context_errno(context)));
	ret = PULSE_NO_CONNECTION;
        goto unlock_and_fail;
    }

    SHOW_TIME("pa_threaded_mainloop_start (call)");
    if (pa_threaded_mainloop_start(mainloop) < 0) {
      SHOW("Failed to start main loop","");
        goto unlock_and_fail;
    }

    /* Wait until the context is ready */
    SHOW_TIME("pa_threaded_mainloop_wait");
    pa_threaded_mainloop_wait(mainloop);

    if (pa_context_get_state(context) != PA_CONTEXT_READY) {
        SHOW("Failed to connect to server: %s", pa_strerror(pa_context_errno(context)));
	ret = PULSE_NO_CONNECTION;
 	if (mainloop)
 	  pa_threaded_mainloop_stop(mainloop);
        goto unlock_and_fail;
    }

    SHOW_TIME("pa_stream_new");
    if (!(stream = pa_stream_new(context, "unknown", &ss, NULL))) {
        SHOW("Failed to create stream: %s", pa_strerror(pa_context_errno(context)));
        goto unlock_and_fail;
    }

    pa_stream_set_state_callback(stream, stream_state_cb, NULL);
    pa_stream_set_write_callback(stream, stream_request_cb, NULL);
    pa_stream_set_latency_update_callback(stream, stream_latency_update_cb, NULL);

    pa_buffer_attr a_attr;

    a_attr.maxlength = MAXLENGTH;
    a_attr.tlength = TLENGTH;
    a_attr.prebuf = PREBUF;
    a_attr.minreq = MINREQ;
    a_attr.fragsize = 0;

    SHOW_TIME("pa_connect_playback");
    if (pa_stream_connect_playback(stream, NULL, &a_attr, (pa_stream_flags_t)(PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE), NULL, NULL) < 0) {
        SHOW("Failed to connect stream: %s", pa_strerror(pa_context_errno(context)));
        goto unlock_and_fail;
    }

    /* Wait until the stream is ready */
    SHOW_TIME("pa_threaded_mainloop_wait");
    pa_threaded_mainloop_wait(mainloop);

    if (pa_stream_get_state(stream) != PA_STREAM_READY) {
        SHOW("Failed to connect stream: %s", pa_strerror(pa_context_errno(context)));
        goto unlock_and_fail;
    }

    /* Now subscribe to events */
    SHOW_TIME("pa_context_subscribe");
    if (!(o = pa_context_subscribe(context, PA_SUBSCRIPTION_MASK_SINK_INPUT, context_success_cb, &success))) {
        SHOW("pa_context_subscribe() failed: %s", pa_strerror(pa_context_errno(context)));
        goto unlock_and_fail;
    }
    
    success = 0;
    SHOW_TIME("pa_threaded_mainloop_wait");
    while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
        CHECK_DEAD_GOTO(fail, 1);
        pa_threaded_mainloop_wait(mainloop);
    }

    pa_operation_unref(o);

    if (!success) {
        SHOW("pa_context_subscribe() failed: %s", pa_strerror(pa_context_errno(context)));
        goto unlock_and_fail;
    }

    do_trigger = 0;
    written = 0;
    time_offset_msec = 0;
    just_flushed = 0;
    connected = 1;
    
    pa_threaded_mainloop_unlock(mainloop);
    SHOW_TIME("pulse_open (ret true)");
   
    return PULSE_OK;

unlock_and_fail:

    if (o)
        pa_operation_unref(o);
    
    pa_threaded_mainloop_unlock(mainloop);
    
fail:

    //    pulse_close();

  if (ret == PULSE_NO_CONNECTION) {
    if (context) {
      SHOW_TIME("pa_context_disconnect (call)");
      pa_context_disconnect(context);
      pa_context_unref(context);
      context = NULL;
    }
  
    if (mainloop) {
      SHOW_TIME("pa_threaded_mainloop_free (call)");
      pa_threaded_mainloop_free(mainloop);
      mainloop = NULL;
    }  
  } 
  else {
    pulse_close();
  }

  SHOW_TIME("pulse_open (ret false)");
  
  return ret;

}
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;
}
Пример #30
0
static void resolver_cb(
        AvahiServiceResolver *r,
        AvahiIfIndex interface, AvahiProtocol protocol,
        AvahiResolverEvent event,
        const char *name, const char *type, const char *domain,
        const char *host_name, const AvahiAddress *a, uint16_t port,
        AvahiStringList *txt,
        AvahiLookupResultFlags flags,
        void *userdata) {

    struct userdata *u = userdata;
    struct tunnel *tnl;

    pa_assert(u);

    tnl = tunnel_new(interface, protocol, name, type, domain);

    if (event != AVAHI_RESOLVER_FOUND)
        pa_log("Resolving of '%s' failed: %s", name, avahi_strerror(avahi_client_errno(u->client)));
    else {
        char *device = NULL, *dname, *module_name, *args;
        const char *t;
        char at[AVAHI_ADDRESS_STR_MAX], cmt[PA_CHANNEL_MAP_SNPRINT_MAX];
        pa_sample_spec ss;
        pa_channel_map cm;
        AvahiStringList *l;
        pa_bool_t channel_map_set = FALSE;
        pa_module *m;

        ss = u->core->default_sample_spec;
        cm = u->core->default_channel_map;

        for (l = txt; l; l = l->next) {
            char *key, *value;
            pa_assert_se(avahi_string_list_get_pair(l, &key, &value, NULL) == 0);

            if (pa_streq(key, "device")) {
                pa_xfree(device);
                device = value;
                value = NULL;
            } else if (pa_streq(key, "rate"))
                ss.rate = (uint32_t) atoi(value);
            else if (pa_streq(key, "channels"))
                ss.channels = (uint8_t) atoi(value);
            else if (pa_streq(key, "format"))
                ss.format = pa_parse_sample_format(value);
            else if (pa_streq(key, "channel_map")) {
                pa_channel_map_parse(&cm, value);
                channel_map_set = TRUE;
            }

            avahi_free(key);
            avahi_free(value);
        }

        if (!channel_map_set && cm.channels != ss.channels)
            pa_channel_map_init_extend(&cm, ss.channels, PA_CHANNEL_MAP_DEFAULT);

        if (!pa_sample_spec_valid(&ss)) {
            pa_log("Service '%s' contains an invalid sample specification.", name);
            avahi_free(device);
            goto finish;
        }

        if (!pa_channel_map_valid(&cm) || cm.channels != ss.channels) {
            pa_log("Service '%s' contains an invalid channel map.", name);
            avahi_free(device);
            goto finish;
        }

        if (device)
            dname = pa_sprintf_malloc("tunnel.%s.%s", host_name, device);
        else
            dname = pa_sprintf_malloc("tunnel.%s", host_name);

        if (!pa_namereg_is_valid_name(dname)) {
            pa_log("Cannot construct valid device name from credentials of service '%s'.", dname);
            avahi_free(device);
            pa_xfree(dname);
            goto finish;
        }

        t = strstr(type, "sink") ? "sink" : "source";

        module_name = pa_sprintf_malloc("module-tunnel-%s", t);
        args = pa_sprintf_malloc("server=[%s]:%u "
                                 "%s=%s "
                                 "format=%s "
                                 "channels=%u "
                                 "rate=%u "
                                 "%s_name=%s "
                                 "channel_map=%s",
                                 avahi_address_snprint(at, sizeof(at), a), port,
                                 t, device,
                                 pa_sample_format_to_string(ss.format),
                                 ss.channels,
                                 ss.rate,
                                 t, dname,
                                 pa_channel_map_snprint(cmt, sizeof(cmt), &cm));

        pa_log_debug("Loading %s with arguments '%s'", module_name, args);

        if ((m = pa_module_load(u->core, module_name, args))) {
            tnl->module_index = m->index;
            pa_hashmap_put(u->tunnels, tnl, tnl);
            tnl = NULL;
        }

        pa_xfree(module_name);
        pa_xfree(dname);
        pa_xfree(args);
        avahi_free(device);
    }

finish:

    avahi_service_resolver_free(r);

    if (tnl)
        tunnel_free(tnl);
}