/** * 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; }