Example #1
0
/**
 * The main thread of the music player. Handles events and coverts
 * samples to play a song.
 */
static void *music_player_main(struct music_player_t *self_p)
{
    uint32_t mask;
    struct time_t timeout;

    thrd_set_name("music_player");

    /* Start the periodic fill timer. */
    timeout.seconds = 0;
    timeout.nanoseconds = 10000000;
    timer_init(&self_p->timer,
               &timeout,
               (void (*)(void *))fill_timer_cb,
               self_p,
               TIMER_PERIODIC);
    timer_start(&self_p->timer);

    /* Start the main loop of the music player. */
    while (1) {
        mask = (EVENT_PLAY
                | EVENT_PAUSE
                | EVENT_STOP
                | EVENT_TIMEOUT);
        event_read(&self_p->event, &mask, sizeof(mask));

        if (mask & EVENT_STOP) {
            handle_event_stop(self_p);
        }

        if (mask & EVENT_PAUSE) {
            handle_event_pause(self_p);
        }

        if (mask & EVENT_PLAY) {
            handle_event_play(self_p);
        }

        /* Play if the state in playing, eyy! */
        if (self_p->state == STATE_PLAYING) {
            play_chunk(self_p);
        }
    }

    return (NULL);
}
/**
 * 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;
}