static const struct audio_format *
autoconvert_filter_open(struct filter *_filter,
			struct audio_format *in_audio_format,
			GError **error_r)
{
	struct autoconvert_filter *filter =
		(struct autoconvert_filter *)_filter;
	const struct audio_format *out_audio_format;

	assert(audio_format_valid(in_audio_format));

	/* open the "real" filter */

	filter->in_audio_format = *in_audio_format;

	out_audio_format = filter_open(filter->filter,
				       &filter->in_audio_format, error_r);
	if (out_audio_format == NULL)
		return NULL;

	/* need to convert? */

	if (!audio_format_equals(&filter->in_audio_format, in_audio_format)) {
		/* yes - create a convert_filter */
		struct audio_format audio_format2 = *in_audio_format;
		const struct audio_format *audio_format3;

		filter->convert = filter_new(&convert_filter_plugin, NULL,
					     error_r);
		if (filter->convert == NULL) {
			filter_close(filter->filter);
			return NULL;
		}

		audio_format3 = filter_open(filter->convert, &audio_format2,
					    error_r);
		if (audio_format3 == NULL) {
			filter_free(filter->convert);
			filter_close(filter->filter);
			return NULL;
		}

		assert(audio_format_equals(&audio_format2, in_audio_format));

		convert_filter_set(filter->convert, &filter->in_audio_format);
	} else
		/* no */
		filter->convert = NULL;

	return out_audio_format;
}
Example #2
0
void decoder_initialized(G_GNUC_UNUSED struct decoder * decoder,
			 const struct audio_format *audio_format,
			 bool seekable, float total_time)
{
	assert(dc.state == DECODE_STATE_START);
	assert(decoder != NULL);
	assert(decoder->stream_tag == NULL);
	assert(decoder->decoder_tag == NULL);
	assert(!decoder->seeking);
	assert(audio_format != NULL);
	assert(audio_format_defined(audio_format));
	assert(audio_format_valid(audio_format));

	dc.in_audio_format = *audio_format;
	getOutputAudioFormat(audio_format, &dc.out_audio_format);

	dc.seekable = seekable;
	dc.total_time = total_time;

	dc.state = DECODE_STATE_DECODE;
	notify_signal(&pc.notify);

	g_debug("audio_format=%u:%u:%u, seekable=%s",
		dc.in_audio_format.sample_rate, dc.in_audio_format.bits,
		dc.in_audio_format.channels,
		seekable ? "true" : "false");

	if (!audio_format_equals(&dc.in_audio_format, &dc.out_audio_format))
		g_debug("converting to %u:%u:%u",
			dc.out_audio_format.sample_rate,
			dc.out_audio_format.bits,
			dc.out_audio_format.channels);
}
static const struct audio_format *
chain_open_child(struct filter *filter,
		 const struct audio_format *prev_audio_format,
		 GError **error_r)
{
	struct audio_format conv_audio_format = *prev_audio_format;
	const struct audio_format *next_audio_format;

	next_audio_format = filter_open(filter, &conv_audio_format, error_r);
	if (next_audio_format == NULL)
		return NULL;

	if (!audio_format_equals(&conv_audio_format, prev_audio_format)) {
		struct audio_format_string s;

		filter_close(filter);
		g_set_error(error_r, filter_quark(), 0,
			    "Audio format not supported by filter '%s': %s",
			    filter->plugin->name,
			    audio_format_to_string(prev_audio_format, &s));
		return NULL;
	}

	return next_audio_format;
}
Example #4
0
unsigned cross_fade_calc(float duration, float total_time,
			 float mixramp_db, float mixramp_delay,
			 float replay_gain_db, float replay_gain_prev_db,
			 char *mixramp_start, char *mixramp_prev_end,
			 const struct audio_format *af,
			 const struct audio_format *old_format,
			 unsigned max_chunks)
{
	unsigned int chunks = 0;
	float chunks_f;
	float mixramp_overlap;

	if (duration < 0 || duration >= total_time ||
	    /* we can't crossfade when the audio formats are different */
	    !audio_format_equals(af, old_format))
		return 0;

	assert(duration >= 0);
	assert(audio_format_valid(af));

	chunks_f = (float)audio_format_time_to_size(af) / (float)CHUNK_SIZE;

	if (isnan(mixramp_delay) || !(mixramp_start) || !(mixramp_prev_end)) {
		chunks = (chunks_f * duration + 0.5);
	} else {
		/* Calculate mixramp overlap. */
		mixramp_overlap = mixramp_interpolate(mixramp_start, mixramp_db - replay_gain_db)
		  + mixramp_interpolate(mixramp_prev_end, mixramp_db - replay_gain_prev_db);
		if (!isnan(mixramp_overlap) && (mixramp_delay <= mixramp_overlap)) {
			chunks = (chunks_f * (mixramp_overlap - mixramp_delay));
			g_debug("will overlap %d chunks, %fs", chunks,
				mixramp_overlap - mixramp_delay);
		}
	}

	if (chunks > max_chunks) {
		chunks = max_chunks;
		g_warning("audio_buffer_size too small for computed MixRamp overlap");
	}

	return chunks;
}
Example #5
0
void
decoder_initialized(struct decoder *decoder,
		    const struct audio_format *audio_format,
		    bool seekable, float total_time)
{
	struct decoder_control *dc = decoder->dc;
	struct audio_format_string af_string;

	assert(dc->state == DECODE_STATE_START);
	assert(dc->pipe != NULL);
	assert(decoder != NULL);
	assert(decoder->stream_tag == NULL);
	assert(decoder->decoder_tag == NULL);
	assert(!decoder->seeking);
	assert(audio_format != NULL);
	assert(audio_format_defined(audio_format));
	assert(audio_format_valid(audio_format));

	dc->in_audio_format = *audio_format;
	getOutputAudioFormat(audio_format, &dc->out_audio_format);

	dc->seekable = seekable;
	dc->total_time = total_time;

	decoder_lock(dc);
	dc->state = DECODE_STATE_DECODE;
	decoder_unlock(dc);

	player_lock_signal();

	g_debug("audio_format=%s, seekable=%s",
		audio_format_to_string(&dc->in_audio_format, &af_string),
		seekable ? "true" : "false");

	if (!audio_format_equals(&dc->in_audio_format,
				 &dc->out_audio_format))
		g_debug("converting to %s",
			audio_format_to_string(&dc->out_audio_format,
					       &af_string));
}
Example #6
0
static void
ao_open(struct audio_output *ao)
{
    bool success;
    GError *error = NULL;
    const struct audio_format *filter_audio_format;
    struct audio_format_string af_string;

    assert(!ao->open);
    assert(ao->pipe != NULL);
    assert(ao->chunk == NULL);
    assert(audio_format_valid(&ao->in_audio_format));

    if (ao->fail_timer != NULL) {
        /* this can only happen when this
           output thread fails while
           audio_output_open() is run in the
           player thread */
        g_timer_destroy(ao->fail_timer);
        ao->fail_timer = NULL;
    }

    /* enable the device (just in case the last enable has failed) */

    if (!ao_enable(ao))
        /* still no luck */
        return;

    /* open the filter */

    filter_audio_format = ao_filter_open(ao, &ao->in_audio_format, &error);
    if (filter_audio_format == NULL) {
        g_warning("Failed to open filter for \"%s\" [%s]: %s",
                  ao->name, ao->plugin->name, error->message);
        g_error_free(error);

        ao->fail_timer = g_timer_new();
        return;
    }

    assert(audio_format_valid(filter_audio_format));

    ao->out_audio_format = *filter_audio_format;
    audio_format_mask_apply(&ao->out_audio_format,
                            &ao->config_audio_format);

    g_mutex_unlock(ao->mutex);
    success = ao_plugin_open(ao->plugin, ao->data,
                             &ao->out_audio_format,
                             &error);
    g_mutex_lock(ao->mutex);

    assert(!ao->open);

    if (!success) {
        g_warning("Failed to open \"%s\" [%s]: %s",
                  ao->name, ao->plugin->name, error->message);
        g_error_free(error);

        ao_filter_close(ao);
        ao->fail_timer = g_timer_new();
        return;
    }

    convert_filter_set(ao->convert_filter, &ao->out_audio_format);

    ao->open = true;

    g_debug("opened plugin=%s name=\"%s\" "
            "audio_format=%s",
            ao->plugin->name, ao->name,
            audio_format_to_string(&ao->out_audio_format, &af_string));

    if (!audio_format_equals(&ao->in_audio_format,
                             &ao->out_audio_format))
        g_debug("converting from %s",
                audio_format_to_string(&ao->in_audio_format,
                                       &af_string));
}
Example #7
0
enum decoder_command
decoder_data(struct decoder *decoder,
	     struct input_stream *is,
	     const void *_data, size_t length,
	     uint16_t kbit_rate)
{
	struct decoder_control *dc = decoder->dc;
	const char *data = _data;
	GError *error = NULL;
	enum decoder_command cmd;

	assert(dc->state == DECODE_STATE_DECODE);
	assert(dc->pipe != NULL);
	assert(length % audio_format_frame_size(&dc->in_audio_format) == 0);

	decoder_lock(dc);
	cmd = decoder_get_virtual_command(decoder);
	decoder_unlock(dc);

	if (cmd == DECODE_COMMAND_STOP || cmd == DECODE_COMMAND_SEEK ||
	    length == 0)
		return cmd;

	/* send stream tags */

	if (update_stream_tag(decoder, is)) {
		if (decoder->decoder_tag != NULL) {
			/* merge with tag from decoder plugin */
			struct tag *tag;

			tag = tag_merge(decoder->decoder_tag,
					decoder->stream_tag);
			cmd = do_send_tag(decoder, is, tag);
			tag_free(tag);
		} else
			/* send only the stream tag */
			cmd = do_send_tag(decoder, is, decoder->stream_tag);

		if (cmd != DECODE_COMMAND_NONE)
			return cmd;
	}

	if (!audio_format_equals(&dc->in_audio_format, &dc->out_audio_format)) {
		data = pcm_convert(&decoder->conv_state,
				   &dc->in_audio_format, data, length,
				   &dc->out_audio_format, &length,
				   &error);
		if (data == NULL) {
			/* the PCM conversion has failed - stop
			   playback, since we have no better way to
			   bail out */
			g_warning("%s", error->message);
			return DECODE_COMMAND_STOP;
		}
	}

	while (length > 0) {
		struct music_chunk *chunk;
		char *dest;
		size_t nbytes;
		bool full;

		chunk = decoder_get_chunk(decoder, is);
		if (chunk == NULL) {
			assert(dc->command != DECODE_COMMAND_NONE);
			return dc->command;
		}

		dest = music_chunk_write(chunk, &dc->out_audio_format,
					 decoder->timestamp -
					 dc->song->start_ms / 1000.0,
					 kbit_rate, &nbytes);
		if (dest == NULL) {
			/* the chunk is full, flush it */
			decoder_flush_chunk(decoder);
			player_lock_signal();
			continue;
		}

		assert(nbytes > 0);

		if (nbytes > length)
			nbytes = length;

		/* copy the buffer */

		memcpy(dest, data, nbytes);

		/* expand the music pipe chunk */

		full = music_chunk_expand(chunk, &dc->out_audio_format, nbytes);
		if (full) {
			/* the chunk is full, flush it */
			decoder_flush_chunk(decoder);
			player_lock_signal();
		}

		data += nbytes;
		length -= nbytes;

		decoder->timestamp += (double)nbytes /
			audio_format_time_to_size(&dc->out_audio_format);

		if (dc->end_ms > 0 &&
		    decoder->timestamp >= dc->end_ms / 1000.0)
			/* the end of this range has been reached:
			   stop decoding */
			return DECODE_COMMAND_STOP;
	}

	return DECODE_COMMAND_NONE;
}
Example #8
0
static bool
audio_output_open(struct audio_output *ao,
		  const struct audio_format *audio_format,
		  const struct music_pipe *mp)
{
	bool open;

	assert(mp != NULL);

	if (ao->fail_timer != NULL) {
		g_timer_destroy(ao->fail_timer);
		ao->fail_timer = NULL;
	}

	if (ao->open &&
	    audio_format_equals(audio_format, &ao->in_audio_format)) {
		assert(ao->pipe == mp ||
		       (ao->always_on && ao->pause));

		if (ao->pause) {
			ao->chunk = NULL;
			ao->pipe = mp;

			/* unpause with the CANCEL command; this is a
			   hack, but suits well for forcing the thread
			   to leave the ao_pause() thread, and we need
			   to flush the device buffer anyway */

			/* we're not using audio_output_cancel() here,
			   because that function is asynchronous */
			ao_command(ao, AO_COMMAND_CANCEL);

			/* the audio output is now waiting for a
			   signal; wake it up immediately */
			g_cond_signal(ao->cond);
		}

		return true;
	}

	ao->in_audio_format = *audio_format;
	ao->chunk = NULL;

	ao->pipe = mp;

	if (ao->thread == NULL)
		audio_output_thread_start(ao);

	ao_command(ao, ao->open ? AO_COMMAND_REOPEN : AO_COMMAND_OPEN);
	open = ao->open;

	if (open && ao->mixer != NULL) {
		GError *error = NULL;

		if (!mixer_open(ao->mixer, &error)) {
			g_warning("Failed to open mixer for '%s': %s",
				  ao->name, error->message);
			g_error_free(error);
		}
	}

	return open;
}
Example #9
0
enum decoder_command
decoder_data(struct decoder *decoder,
	     struct input_stream *is,
	     const void *_data, size_t length,
	     float data_time, uint16_t bitRate,
	     struct replay_gain_info *replay_gain_info)
{
	const char *data = _data;

	assert(dc.state == DECODE_STATE_DECODE);
	assert(length % audio_format_frame_size(&dc.in_audio_format) == 0);

	if (dc.command == DECODE_COMMAND_STOP ||
	    dc.command == DECODE_COMMAND_SEEK ||
	    length == 0)
		return dc.command;

	/* send stream tags */

	if (update_stream_tag(decoder, is)) {
		enum decoder_command cmd;

		if (decoder->decoder_tag != NULL) {
			/* merge with tag from decoder plugin */
			struct tag *tag;

			tag = tag_merge(decoder->stream_tag,
					decoder->decoder_tag);
			cmd = do_send_tag(decoder, is, tag);
			tag_free(tag);
		} else
			/* send only the stream tag */
			cmd = do_send_tag(decoder, is, decoder->stream_tag);

		if (cmd != DECODE_COMMAND_NONE)
			return cmd;
	}

	if (!audio_format_equals(&dc.in_audio_format, &dc.out_audio_format)) {
		data = pcm_convert(&decoder->conv_state,
				   &dc.in_audio_format, data, length,
				   &dc.out_audio_format, &length);

		/* under certain circumstances, pcm_convert() may
		   return an empty buffer - this condition should be
		   investigated further, but for now, do this check as
		   a workaround: */
		if (data == NULL)
			return DECODE_COMMAND_NONE;
	}

	while (length > 0) {
		struct music_chunk *chunk;
		char *dest;
		size_t nbytes;
		bool full;

		chunk = decoder_get_chunk(decoder, is);
		if (chunk == NULL) {
			assert(dc.command != DECODE_COMMAND_NONE);
			return dc.command;
		}

		dest = music_chunk_write(chunk, &dc.out_audio_format,
					 data_time, bitRate, &nbytes);
		if (dest == NULL) {
			/* the chunk is full, flush it */
			decoder_flush_chunk(decoder);
			notify_signal(&pc.notify);
			continue;
		}

		assert(nbytes > 0);

		if (nbytes > length)
			nbytes = length;

		/* copy the buffer */

		memcpy(dest, data, nbytes);

		/* apply replay gain or normalization */

		if (replay_gain_info != NULL &&
		    replay_gain_mode != REPLAY_GAIN_OFF)
			replay_gain_apply(replay_gain_info, dest, nbytes,
					  &dc.out_audio_format);
		else if (normalizationEnabled)
			normalizeData(dest, nbytes, &dc.out_audio_format);

		/* expand the music pipe chunk */

		full = music_chunk_expand(chunk, &dc.out_audio_format, nbytes);
		if (full) {
			/* the chunk is full, flush it */
			decoder_flush_chunk(decoder);
			notify_signal(&pc.notify);
		}

		data += nbytes;
		length -= nbytes;
	}

	return DECODE_COMMAND_NONE;
}