Ejemplo n.º 1
0
static const float *
pcm_convert_float(struct pcm_convert_state *state,
		  const struct audio_format *src_format,
		  const void *src_buffer, size_t src_size,
		  const struct audio_format *dest_format, size_t *dest_size_r,
		  GError **error_r)
{
	const float *buffer = src_buffer;
	size_t size = src_size;

	assert(dest_format->format == SAMPLE_FORMAT_FLOAT);

	if (src_format->reverse_endian || dest_format->reverse_endian) {
		g_set_error_literal(error_r, pcm_convert_quark(), 0,
				    "Reverse endian not supported");
		return NULL;
	}

	/* convert channels first, hoping the source format is
	   supported (float is not) */

	if (dest_format->channels != src_format->channels) {
		buffer = pcm_convert_channels(&state->channels_buffer,
					      src_format->format,
					      dest_format->channels,
					      src_format->channels,
					      buffer, size, &size, error_r);
		if (buffer == NULL)
			return NULL;
	}

	/* convert to float now */

	buffer = pcm_convert_to_float(&state->format_buffer,
				      src_format->format,
				      buffer, size, &size);
	if (buffer == NULL) {
		g_set_error(error_r, pcm_convert_quark(), 0,
			    "Conversion from %s to float is not implemented",
			    sample_format_to_string(src_format->format));
		return NULL;
	}

	/* resample with float, because this is the best format for
	   libsamplerate */

	if (src_format->sample_rate != dest_format->sample_rate) {
		buffer = pcm_resample_float(&state->resample,
					    dest_format->channels,
					    src_format->sample_rate,
					    buffer, size,
					    dest_format->sample_rate, &size,
					    error_r);
		if (buffer == NULL)
			return NULL;
	}

	*dest_size_r = size;
	return buffer;
}
Ejemplo n.º 2
0
static const int16_t *
pcm_convert_16(struct pcm_convert_state *state,
	       const struct audio_format *src_format,
	       const void *src_buffer, size_t src_size,
	       const struct audio_format *dest_format, size_t *dest_size_r,
	       GError **error_r)
{
	const int16_t *buf;
	size_t len;

	assert(dest_format->format == SAMPLE_FORMAT_S16);

	buf = pcm_convert_to_16(&state->format_buffer, &state->dither,
				src_format->format, src_buffer, src_size,
				&len);
	if (buf == NULL) {
		g_set_error(error_r, pcm_convert_quark(), 0,
			    "Conversion from %s to 16 bit is not implemented",
			    sample_format_to_string(src_format->format));
		return NULL;
	}

	if (src_format->channels != dest_format->channels) {
		buf = pcm_convert_channels_16(&state->channels_buffer,
					      dest_format->channels,
					      src_format->channels,
					      buf, len, &len);
		if (buf == NULL) {
			g_set_error(error_r, pcm_convert_quark(), 0,
				    "Conversion from %u to %u channels "
				    "is not implemented",
				    src_format->channels,
				    dest_format->channels);
			return NULL;
		}
	}

	if (src_format->sample_rate != dest_format->sample_rate) {
		buf = pcm_resample_16(&state->resample,
				      dest_format->channels,
				      src_format->sample_rate, buf, len,
				      dest_format->sample_rate, &len,
				      error_r);
		if (buf == NULL)
			return NULL;
	}

	if (dest_format->reverse_endian) {
		buf = pcm_byteswap_16(&state->byteswap_buffer, buf, len);
		assert(buf != NULL);
	}

	*dest_size_r = len;
	return buf;
}
Ejemplo n.º 3
0
const char *
audio_format_to_string(const struct audio_format *af,
                       struct audio_format_string *s)
{
    assert(af != NULL);
    assert(s != NULL);

    snprintf(s->buffer, sizeof(s->buffer), "%u:%s:%u",
             af->sample_rate, sample_format_to_string(af->format),
             af->channels);

    return s->buffer;
}
Ejemplo n.º 4
0
const char *
audio_format_to_string(const struct audio_format *af,
		       struct audio_format_string *s)
{
	assert(af != NULL);
	assert(s != NULL);

	snprintf(s->buffer, sizeof(s->buffer), "%u:%s%s:%u",
		 af->sample_rate, sample_format_to_string(af->format),
		 af->reverse_endian ? REVERSE_ENDIAN_SUFFIX : "",
		 af->channels);

	return s->buffer;
}
Ejemplo n.º 5
0
static const void *
pcm_convert_channels(struct pcm_buffer *buffer, enum sample_format format,
		     uint8_t dest_channels,
		     uint8_t src_channels, const void *src,
		     size_t src_size, size_t *dest_size_r,
		     GError **error_r)
{
	const void *dest = NULL;

	switch (format) {
	case SAMPLE_FORMAT_UNDEFINED:
	case SAMPLE_FORMAT_S8:
	case SAMPLE_FORMAT_S24:
	case SAMPLE_FORMAT_FLOAT:
	case SAMPLE_FORMAT_DSD:
	case SAMPLE_FORMAT_DSD_LSBFIRST:
		g_set_error(error_r, pcm_convert_quark(), 0,
			    "Channel conversion not implemented for format '%s'",
			    sample_format_to_string(format));
		return NULL;

	case SAMPLE_FORMAT_S16:
		dest = pcm_convert_channels_16(buffer, dest_channels,
					       src_channels, src,
					       src_size, dest_size_r);
		break;

	case SAMPLE_FORMAT_S24_P32:
		dest = pcm_convert_channels_24(buffer, dest_channels,
					       src_channels, src,
					       src_size, dest_size_r);
		break;

	case SAMPLE_FORMAT_S32:
		dest = pcm_convert_channels_32(buffer, dest_channels,
					       src_channels, src,
					       src_size, dest_size_r);
		break;
	}

	if (dest == NULL) {
		g_set_error(error_r, pcm_convert_quark(), 0,
			    "Conversion from %u to %u channels "
			    "is not implemented",
			    src_channels, dest_channels);
		return NULL;
	}

	return dest;
}
Ejemplo n.º 6
0
const void *
pcm_convert(struct pcm_convert_state *state,
	    const struct audio_format *src_format,
	    const void *src, size_t src_size,
	    const struct audio_format *dest_format,
	    size_t *dest_size_r,
	    GError **error_r)
{
	switch (dest_format->format) {
	case SAMPLE_FORMAT_S16:
		return pcm_convert_16(state,
				      src_format, src, src_size,
				      dest_format, dest_size_r,
				      error_r);

	case SAMPLE_FORMAT_S24:
		return pcm_convert_24_packed(state,
					     src_format, src, src_size,
					     dest_format, dest_size_r,
					     error_r);

	case SAMPLE_FORMAT_S24_P32:
		return pcm_convert_24(state,
				      src_format, src, src_size,
				      dest_format, dest_size_r,
				      error_r);

	case SAMPLE_FORMAT_S32:
		return pcm_convert_32(state,
				      src_format, src, src_size,
				      dest_format, dest_size_r,
				      error_r);

	default:
		g_set_error(error_r, pcm_convert_quark(), 0,
			    "PCM conversion to %s is not implemented",
			    sample_format_to_string(dest_format->format));
		return NULL;
	}
}
Ejemplo n.º 7
0
/**
 * Set up the snd_pcm_t object which was opened by the caller.  Set up
 * the configured settings and the audio format.
 */
static bool
alsa_setup(struct alsa_data *ad, struct audio_format *audio_format,
	   GError **error)
{
	snd_pcm_hw_params_t *hwparams;
	snd_pcm_sw_params_t *swparams;
	unsigned int sample_rate = audio_format->sample_rate;
	unsigned int channels = audio_format->channels;
	snd_pcm_uframes_t alsa_buffer_size;
	snd_pcm_uframes_t alsa_period_size;
	int err;
	const char *cmd = NULL;
	int retry = MPD_ALSA_RETRY_NR;
	unsigned int period_time, period_time_ro;
	unsigned int buffer_time;

	period_time_ro = period_time = ad->period_time;
configure_hw:
	/* configure HW params */
	snd_pcm_hw_params_alloca(&hwparams);
	cmd = "snd_pcm_hw_params_any";
	err = snd_pcm_hw_params_any(ad->pcm, hwparams);
	if (err < 0)
		goto error;

	if (ad->use_mmap) {
		err = snd_pcm_hw_params_set_access(ad->pcm, hwparams,
						   SND_PCM_ACCESS_MMAP_INTERLEAVED);
		if (err < 0) {
			g_warning("Cannot set mmap'ed mode on ALSA device \"%s\":  %s\n",
				  alsa_device(ad), snd_strerror(-err));
			g_warning("Falling back to direct write mode\n");
			ad->use_mmap = false;
		} else
			ad->writei = snd_pcm_mmap_writei;
	}

	if (!ad->use_mmap) {
		cmd = "snd_pcm_hw_params_set_access";
		err = snd_pcm_hw_params_set_access(ad->pcm, hwparams,
						   SND_PCM_ACCESS_RW_INTERLEAVED);
		if (err < 0)
			goto error;
		ad->writei = snd_pcm_writei;
	}

	err = alsa_output_setup_format(ad->pcm, hwparams, audio_format);
	if (err < 0) {
		g_set_error(error, alsa_output_quark(), err,
			    "ALSA device \"%s\" does not support format %s: %s",
			    alsa_device(ad),
			    sample_format_to_string(audio_format->format),
			    snd_strerror(-err));
		return false;
	}

	err = snd_pcm_hw_params_set_channels_near(ad->pcm, hwparams,
						  &channels);
	if (err < 0) {
		g_set_error(error, alsa_output_quark(), err,
			    "ALSA device \"%s\" does not support %i channels: %s",
			    alsa_device(ad), (int)audio_format->channels,
			    snd_strerror(-err));
		return false;
	}
	audio_format->channels = (int8_t)channels;

	err = snd_pcm_hw_params_set_rate_near(ad->pcm, hwparams,
					      &sample_rate, NULL);
	if (err < 0 || sample_rate == 0) {
		g_set_error(error, alsa_output_quark(), err,
			    "ALSA device \"%s\" does not support %u Hz audio",
			    alsa_device(ad), audio_format->sample_rate);
		return false;
	}
	audio_format->sample_rate = sample_rate;

	snd_pcm_uframes_t buffer_size_min, buffer_size_max;
	snd_pcm_hw_params_get_buffer_size_min(hwparams, &buffer_size_min);
	snd_pcm_hw_params_get_buffer_size_max(hwparams, &buffer_size_max);
	unsigned buffer_time_min, buffer_time_max;
	snd_pcm_hw_params_get_buffer_time_min(hwparams, &buffer_time_min, 0);
	snd_pcm_hw_params_get_buffer_time_max(hwparams, &buffer_time_max, 0);
	g_debug("buffer: size=%u..%u time=%u..%u",
		(unsigned)buffer_size_min, (unsigned)buffer_size_max,
		buffer_time_min, buffer_time_max);

	snd_pcm_uframes_t period_size_min, period_size_max;
	snd_pcm_hw_params_get_period_size_min(hwparams, &period_size_min, 0);
	snd_pcm_hw_params_get_period_size_max(hwparams, &period_size_max, 0);
	unsigned period_time_min, period_time_max;
	snd_pcm_hw_params_get_period_time_min(hwparams, &period_time_min, 0);
	snd_pcm_hw_params_get_period_time_max(hwparams, &period_time_max, 0);
	g_debug("period: size=%u..%u time=%u..%u",
		(unsigned)period_size_min, (unsigned)period_size_max,
		period_time_min, period_time_max);

	if (ad->buffer_time > 0) {
		buffer_time = ad->buffer_time;
		cmd = "snd_pcm_hw_params_set_buffer_time_near";
		err = snd_pcm_hw_params_set_buffer_time_near(ad->pcm, hwparams,
							     &buffer_time, NULL);
		if (err < 0)
			goto error;
	} else {
		err = snd_pcm_hw_params_get_buffer_time(hwparams, &buffer_time,
							NULL);
		if (err < 0)
			buffer_time = 0;
	}

	if (period_time_ro == 0 && buffer_time >= 10000) {
		period_time_ro = period_time = buffer_time / 4;

		g_debug("default period_time = buffer_time/4 = %u/4 = %u",
			buffer_time, period_time);
	}

	if (period_time_ro > 0) {
		period_time = period_time_ro;
		cmd = "snd_pcm_hw_params_set_period_time_near";
		err = snd_pcm_hw_params_set_period_time_near(ad->pcm, hwparams,
							     &period_time, NULL);
		if (err < 0)
			goto error;
	}

	cmd = "snd_pcm_hw_params";
	err = snd_pcm_hw_params(ad->pcm, hwparams);
	if (err == -EPIPE && --retry > 0 && period_time_ro > 0) {
		period_time_ro = period_time_ro >> 1;
		goto configure_hw;
	} else if (err < 0)
Ejemplo n.º 8
0
static bool
mvp_set_pcm_params(struct mvp_data *md, struct audio_format *audio_format,
		   GError **error)
{
	unsigned mix[5];

	switch (audio_format->channels) {
	case 1:
		mix[0] = 1;
		break;

	case 2:
		mix[0] = 0;
		break;

	default:
		g_debug("unsupported channel count %u - falling back to stereo",
			audio_format->channels);
		audio_format->channels = 2;
		mix[0] = 0;
		break;
	}

	/* 0,1=24bit(24) , 2,3=16bit */
	switch (audio_format->format) {
	case SAMPLE_FORMAT_S16:
		mix[1] = 2;
		break;

	case SAMPLE_FORMAT_S24_P32:
		mix[1] = 0;
		break;

	default:
		g_debug("unsupported sample format %s - falling back to 16 bit",
			sample_format_to_string(audio_format->format));
		audio_format->format = SAMPLE_FORMAT_S16;
		mix[1] = 2;
		break;
	}

	mix[3] = 0;	/* stream type? */
	mix[4] = G_BYTE_ORDER == G_LITTLE_ENDIAN;

	/*
	 * if there is an exact match for the frequency, use it.
	 */
	mix[2] = mvp_find_sample_rate(audio_format->sample_rate);
	if (mix[2] == (unsigned)-1) {
		g_set_error(error, mvp_output_quark(), 0,
			    "Can not find suitable output frequency for %u",
			    audio_format->sample_rate);
		return false;
	}

	if (ioctl(md->fd, MVP_SET_AUD_FORMAT, &mix) < 0) {
		g_set_error(error, mvp_output_quark(), errno,
			    "Can not set audio format");
		return false;
	}

	if (ioctl(md->fd, MVP_SET_AUD_SYNC, 2) != 0) {
		g_set_error(error, mvp_output_quark(), errno,
			    "Can not set audio sync");
		return false;
	}

	if (ioctl(md->fd, MVP_SET_AUD_PLAY, 0) < 0) {
		g_set_error(error, mvp_output_quark(), errno,
			    "Can not set audio play mode");
		return false;
	}

	return true;
}
Ejemplo n.º 9
0
const void *
pcm_convert(struct pcm_convert_state *state,
	    const struct audio_format *src_format,
	    const void *src, size_t src_size,
	    const struct audio_format *dest_format,
	    size_t *dest_size_r,
	    GError **error_r)
{
	if (src_format->reverse_endian) {
		/* convert to host byte order, because all of our
		   conversion libraries assume host byte order */

		src = pcm_byteswap(&state->byteswap_buffer, src_format->format,
				   src, src_size);
		if (src == NULL) {
			g_set_error(error_r, pcm_convert_quark(), 0,
				    "PCM byte order change of format '%s' is not implemented",
				    sample_format_to_string(src_format->format));
			return NULL;
		}
	}

	struct audio_format float_format;
	if (src_format->format == SAMPLE_FORMAT_DSD ||
	    src_format->format == SAMPLE_FORMAT_DSD_LSBFIRST) {
		size_t f_size;
		const bool lsbfirst =
			src_format->format == SAMPLE_FORMAT_DSD_LSBFIRST;
		const float *f = pcm_dsd_to_float(&state->dsd,
						  src_format->channels,
						  lsbfirst, src, src_size,
						  &f_size);
		if (f == NULL) {
			g_set_error_literal(error_r, pcm_convert_quark(), 0,
					    "DSD to PCM conversion failed");
			return NULL;
		}

		float_format = *src_format;
		float_format.format = SAMPLE_FORMAT_FLOAT;

		src_format = &float_format;
		src = f;
		src_size = f_size;
	}

	switch (dest_format->format) {
	case SAMPLE_FORMAT_S16:
		return pcm_convert_16(state,
				      src_format, src, src_size,
				      dest_format, dest_size_r,
				      error_r);

	case SAMPLE_FORMAT_S24:
		return pcm_convert_24_packed(state,
					     src_format, src, src_size,
					     dest_format, dest_size_r,
					     error_r);

	case SAMPLE_FORMAT_S24_P32:
		return pcm_convert_24(state,
				      src_format, src, src_size,
				      dest_format, dest_size_r,
				      error_r);

	case SAMPLE_FORMAT_S32:
		return pcm_convert_32(state,
				      src_format, src, src_size,
				      dest_format, dest_size_r,
				      error_r);

	case SAMPLE_FORMAT_FLOAT:
		return pcm_convert_float(state,
					 src_format, src, src_size,
					 dest_format, dest_size_r,
					 error_r);

	default:
		g_set_error(error_r, pcm_convert_quark(), 0,
			    "PCM conversion to %s is not implemented",
			    sample_format_to_string(dest_format->format));
		return NULL;
	}
}