void
decoder_flush_chunk(struct decoder *decoder)
{
	struct decoder_control *dc = decoder->dc;

	assert(decoder != NULL);
	assert(decoder->chunk != NULL);

	if (music_chunk_is_empty(decoder->chunk))
		music_buffer_return(dc->buffer, decoder->chunk);
	else
		music_pipe_push(dc->pipe, decoder->chunk);

	decoder->chunk = NULL;
}
Beispiel #2
0
static const char *
ao_chunk_data(struct audio_output *ao, const struct music_chunk *chunk,
              struct filter *replay_gain_filter,
              unsigned *replay_gain_serial_p,
              size_t *length_r)
{
    assert(chunk != NULL);
    assert(!music_chunk_is_empty(chunk));
    assert(music_chunk_check_format(chunk, &ao->in_audio_format));

    const char *data = chunk->data;
    size_t length = chunk->length;

    (void)ao;

    assert(length % audio_format_frame_size(&ao->in_audio_format) == 0);

    if (length > 0 && replay_gain_filter != NULL) {
        if (chunk->replay_gain_serial != *replay_gain_serial_p) {
            replay_gain_filter_set_info(replay_gain_filter,
                                        chunk->replay_gain_serial != 0
                                        ? &chunk->replay_gain_info
                                        : NULL);
            *replay_gain_serial_p = chunk->replay_gain_serial;
        }

        GError *error = NULL;
        data = filter_filter(replay_gain_filter, data, length,
                             &length, &error);
        if (data == NULL) {
            g_warning("\"%s\" [%s] failed to filter: %s",
                      ao->name, ao->plugin->name, error->message);
            g_error_free(error);
            return NULL;
        }
    }

    *length_r = length;
    return data;
}
/**
 * Obtains the next chunk from the music pipe, optionally applies
 * cross-fading, and sends it to all audio outputs.
 *
 * @return true on success, false on error (playback will be stopped)
 */
static bool
play_next_chunk(struct player *player)
{
	struct player_control *pc = player->pc;
	struct decoder_control *dc = player->dc;

	if (!audio_output_all_wait(pc, 64))
		/* the output pipe is still large enough, don't send
		   another chunk */
		return true;

	unsigned cross_fade_position;
	struct music_chunk *chunk = NULL;
	if (player->xfade == XFADE_ENABLED &&
	    player_dc_at_next_song(player) &&
	    (cross_fade_position = music_pipe_size(player->pipe))
	    <= player->cross_fade_chunks) {
		/* perform cross fade */
		struct music_chunk *other_chunk =
			music_pipe_shift(dc->pipe);

		if (!player->cross_fading) {
			/* beginning of the cross fade - adjust
			   crossFadeChunks which might be bigger than
			   the remaining number of chunks in the old
			   song */
			player->cross_fade_chunks = cross_fade_position;
			player->cross_fading = true;
		}

		if (other_chunk != NULL) {
			chunk = music_pipe_shift(player->pipe);
			assert(chunk != NULL);
			assert(chunk->other == NULL);

			/* don't send the tags of the new song (which
			   is being faded in) yet; postpone it until
			   the current song is faded out */
			player->cross_fade_tag =
				tag_merge_replace(player->cross_fade_tag,
						  other_chunk->tag);
			other_chunk->tag = NULL;

			if (isnan(pc->mixramp_delay_seconds)) {
				chunk->mix_ratio = ((float)cross_fade_position)
					     / player->cross_fade_chunks;
			} else {
				chunk->mix_ratio = nan("");
			}

			if (music_chunk_is_empty(other_chunk)) {
				/* the "other" chunk was a music_chunk
				   which had only a tag, but no music
				   data - we cannot cross-fade that;
				   but since this happens only at the
				   beginning of the new song, we can
				   easily recover by throwing it away
				   now */
				music_buffer_return(player_buffer,
						    other_chunk);
				other_chunk = NULL;
			}

			chunk->other = other_chunk;
		} else {
			/* there are not enough decoded chunks yet */

			decoder_lock(dc);

			if (decoder_is_idle(dc)) {
				/* the decoder isn't running, abort
				   cross fading */
				decoder_unlock(dc);

				player->xfade = XFADE_DISABLED;
			} else {
				/* wait for the decoder */
				decoder_signal(dc);
				player_wait_decoder(pc, dc);
				decoder_unlock(dc);

				return true;
			}
		}
	}

	if (chunk == NULL)
		chunk = music_pipe_shift(player->pipe);

	assert(chunk != NULL);

	/* insert the postponed tag if cross-fading is finished */

	if (player->xfade != XFADE_ENABLED && player->cross_fade_tag != NULL) {
		chunk->tag = tag_merge_replace(chunk->tag,
					       player->cross_fade_tag);
		player->cross_fade_tag = NULL;
	}

	/* play the current chunk */

	if (!play_chunk(player->pc, player->song, chunk,
			&player->play_audio_format)) {
		music_buffer_return(player_buffer, chunk);

		player_lock(pc);

		pc->error = PLAYER_ERROR_AUDIO;

		/* pause: the user may resume playback as soon as an
		   audio output becomes available */
		pc->state = PLAYER_STATE_PAUSE;
		player->paused = true;

		player_unlock(pc);

		return false;
	}

	/* this formula should prevent that the decoder gets woken up
	   with each chunk; it is more efficient to make it decode a
	   larger block at a time */
	decoder_lock(dc);
	if (!decoder_is_idle(dc) &&
	    music_pipe_size(dc->pipe) <= (pc->buffered_before_play +
					 music_buffer_size(player_buffer) * 3) / 4)
		decoder_signal(dc);
	decoder_unlock(dc);

	return true;
}
static bool
ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk)
{
	const char *data = chunk->data;
	size_t size = chunk->length;
	GError *error = NULL;

	assert(ao != NULL);
	assert(ao->filter != NULL);
	assert(!music_chunk_is_empty(chunk));
	assert(music_chunk_check_format(chunk, &ao->in_audio_format));
	assert(size % audio_format_frame_size(&ao->in_audio_format) == 0);

	if (chunk->tag != NULL) {
		g_mutex_unlock(ao->mutex);
		ao_plugin_send_tag(ao->plugin, ao->data, chunk->tag);
		g_mutex_lock(ao->mutex);
	}

	/* update replay gain */

	if (ao->replay_gain_filter != NULL &&
	    chunk->replay_gain_serial != ao->replay_gain_serial) {
		replay_gain_filter_set_info(ao->replay_gain_filter,
					    chunk->replay_gain_serial != 0
					    ? &chunk->replay_gain_info
					    : NULL);
		ao->replay_gain_serial = chunk->replay_gain_serial;
	}

	if (size == 0)
		return true;

	data = filter_filter(ao->filter, data, size, &size, &error);
	if (data == NULL) {
		g_warning("\"%s\" [%s] failed to filter: %s",
			  ao->name, ao->plugin->name, error->message);
		g_error_free(error);

		ao_close(ao, false);

		/* don't automatically reopen this device for 10
		   seconds */
		ao->fail_timer = g_timer_new();
		return false;
	}

	while (size > 0 && ao->command == AO_COMMAND_NONE) {
		size_t nbytes;

		g_mutex_unlock(ao->mutex);
		nbytes = ao_plugin_play(ao->plugin, ao->data, data, size,
					&error);
		g_mutex_lock(ao->mutex);
		if (nbytes == 0) {
			/* play()==0 means failure */
			g_warning("\"%s\" [%s] failed to play: %s",
				  ao->name, ao->plugin->name, error->message);
			g_error_free(error);

			ao_close(ao, false);

			/* don't automatically reopen this device for
			   10 seconds */
			ao->fail_timer = g_timer_new();
			return false;
		}

		assert(nbytes <= size);
		assert(nbytes % audio_format_frame_size(&ao->out_audio_format) == 0);

		data += nbytes;
		size -= nbytes;
	}

	return true;
}