diff options
-rw-r--r-- | src/PlayerThread.cxx | 546 |
1 files changed, 277 insertions, 269 deletions
diff --git a/src/PlayerThread.cxx b/src/PlayerThread.cxx index 250790958..543c1fcae 100644 --- a/src/PlayerThread.cxx +++ b/src/PlayerThread.cxx @@ -142,6 +142,132 @@ struct player { cross_fade_chunks(0), cross_fade_tag(NULL), elapsed_time(0.0) {} + + void ClearAndDeletePipe() { + pipe->Clear(buffer); + delete pipe; + } + + void ClearAndReplacePipe(MusicPipe *_pipe) { + ClearAndDeletePipe(); + pipe = _pipe; + } + + void ReplacePipe(MusicPipe *_pipe) { + delete pipe; + pipe = _pipe; + } + + /** + * Start the decoder. + * + * Player lock is not held. + */ + void StartDecoder(MusicPipe &pipe); + + /** + * The decoder has acknowledged the "START" command (see + * player::WaitForDecoder()). This function checks if the decoder + * initialization has completed yet. + * + * The player lock is not held. + */ + bool CheckDecoderStartup(); + + /** + * Stop the decoder and clears (and frees) its music pipe. + * + * Player lock is not held. + */ + void StopDecoder(); + + /** + * Is the decoder still busy on the same song as the player? + * + * Note: this function does not check if the decoder is already + * finished. + */ + gcc_pure + bool IsDecoderAtCurrentSong() const { + assert(pipe != nullptr); + + return dc.pipe == pipe; + } + + /** + * Returns true if the decoder is decoding the next song (or has begun + * decoding it, or has finished doing it), and the player hasn't + * switched to that song yet. + */ + gcc_pure + bool IsDecoderAtNextSong() const { + return dc.pipe != nullptr && !IsDecoderAtCurrentSong(); + } + + /** + * This is the handler for the #PLAYER_COMMAND_SEEK command. + * + * The player lock is not held. + */ + bool SeekDecoder(); + + /** + * After the decoder has been started asynchronously, wait for + * the "START" command to finish. The decoder may not be + * initialized yet, i.e. there is no audio_format information + * yet. + * + * The player lock is not held. + */ + bool WaitForDecoder(); + + /** + * Wrapper for audio_output_all_open(). Upon failure, it pauses the + * player. + * + * @return true on success + */ + bool OpenOutput(); + + /** + * 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) + */ + bool PlayNextChunk(); + + /** + * Sends a chunk of silence to the audio outputs. This is + * called when there is not enough decoded data in the pipe + * yet, to prevent underruns in the hardware buffers. + * + * The player lock is not held. + */ + bool SendSilence(); + + /** + * Player lock must be held before calling. + */ + void ProcessCommand(); + + /** + * This is called at the border between two songs: the audio output + * has consumed all chunks of the current song, and we should start + * sending chunks from the next one. + * + * The player lock is not held. + * + * @return true on success, false on error (playback will be stopped) + */ + bool SongBorder(); + + /* + * The main loop of the player thread, during playback. This + * is basically a state machine, which multiplexes data + * between the decoder thread and the output threads. + */ + void Run(); }; static void @@ -161,18 +287,10 @@ player_command_finished(player_control &pc) pc.Unlock(); } -/** - * Start the decoder. - * - * Player lock is not held. - */ -static void -player_dc_start(player &player, MusicPipe &pipe) +void +player::StartDecoder(MusicPipe &_pipe) { - player_control &pc = player.pc; - decoder_control &dc = player.dc; - - assert(player.queued || pc.command == PLAYER_COMMAND_SEEK); + assert(queued || pc.command == PLAYER_COMMAND_SEEK); assert(pc.next_song != NULL); unsigned start_ms = pc.next_song->start_ms; @@ -181,75 +299,33 @@ player_dc_start(player &player, MusicPipe &pipe) dc.Start(pc.next_song->DupDetached(), start_ms, pc.next_song->end_ms, - player.buffer, pipe); -} - -/** - * Is the decoder still busy on the same song as the player? - * - * Note: this function does not check if the decoder is already - * finished. - */ -static bool -player_dc_at_current_song(const player &player) -{ - assert(player.pipe != NULL); - - return player.dc.pipe == player.pipe; + buffer, _pipe); } -/** - * Returns true if the decoder is decoding the next song (or has begun - * decoding it, or has finished doing it), and the player hasn't - * switched to that song yet. - */ -static bool -player_dc_at_next_song(const player &player) -{ - return player.dc.pipe != NULL && !player_dc_at_current_song(player); -} - -/** - * Stop the decoder and clears (and frees) its music pipe. - * - * Player lock is not held. - */ -static void -player_dc_stop(player &player) +void +player::StopDecoder() { - decoder_control &dc = player.dc; - dc.Stop(); if (dc.pipe != NULL) { /* clear and free the decoder pipe */ - dc.pipe->Clear(player.buffer); + dc.pipe->Clear(buffer); - if (dc.pipe != player.pipe) + if (dc.pipe != pipe) delete dc.pipe; dc.pipe = NULL; } } -/** - * After the decoder has been started asynchronously, wait for the - * "START" command to finish. The decoder may not be initialized yet, - * i.e. there is no audio_format information yet. - * - * The player lock is not held. - */ -static bool -player_wait_for_decoder(player &player) +bool +player::WaitForDecoder() { - player_control &pc = player.pc; - decoder_control &dc = player.dc; - - assert(player.queued || pc.command == PLAYER_COMMAND_SEEK); + assert(queued || pc.command == PLAYER_COMMAND_SEEK); assert(pc.next_song != NULL); - player.queued = false; + queued = false; Error error = dc.LockGetError(); if (error.IsDefined()) { @@ -264,15 +340,15 @@ player_wait_for_decoder(player &player) return false; } - if (player.song != NULL) - player.song->Free(); + if (song != nullptr) + song->Free(); - player.song = pc.next_song; - player.elapsed_time = 0.0; + song = pc.next_song; + elapsed_time = 0.0; /* set the "starting" flag, which will be cleared by player_check_decoder_startup() */ - player.decoder_starting = true; + decoder_starting = true; pc.Lock(); @@ -312,26 +388,17 @@ real_song_duration(const Song *song, double decoder_duration) return decoder_duration - song->start_ms / 1000.0; } -/** - * Wrapper for audio_output_all_open(). Upon failure, it pauses the - * player. - * - * @return true on success - */ -static bool -player_open_output(player &player) +bool +player::OpenOutput() { - player_control &pc = player.pc; - - assert(player.play_audio_format.IsDefined()); + assert(play_audio_format.IsDefined()); assert(pc.state == PLAYER_STATE_PLAY || pc.state == PLAYER_STATE_PAUSE); Error error; - if (audio_output_all_open(player.play_audio_format, player.buffer, - error)) { - player.output_open = true; - player.paused = false; + if (audio_output_all_open(play_audio_format, buffer, error)) { + output_open = true; + paused = false; pc.Lock(); pc.state = PLAYER_STATE_PLAY; @@ -343,11 +410,11 @@ player_open_output(player &player) } else { g_warning("%s", error.GetMessage()); - player.output_open = false; + output_open = false; /* pause: the user may resume playback as soon as an audio output becomes available */ - player.paused = true; + paused = true; pc.Lock(); pc.SetError(PLAYER_ERROR_OUTPUT, std::move(error)); @@ -360,20 +427,10 @@ player_open_output(player &player) } } -/** - * The decoder has acknowledged the "START" command (see - * player_wait_for_decoder()). This function checks if the decoder - * initialization has completed yet. - * - * The player lock is not held. - */ -static bool -player_check_decoder_startup(player &player) +bool +player::CheckDecoderStartup() { - player_control &pc = player.pc; - decoder_control &dc = player.dc; - - assert(player.decoder_starting); + assert(decoder_starting); dc.Lock(); @@ -392,7 +449,7 @@ player_check_decoder_startup(player &player) dc.Unlock(); - if (player.output_open && + if (output_open && !audio_output_all_wait(&pc, 1)) /* the output devices havn't finished playing all chunks yet - wait for that */ @@ -405,10 +462,10 @@ player_check_decoder_startup(player &player) idle_add(IDLE_PLAYER); - player.play_audio_format = dc.out_audio_format; - player.decoder_starting = false; + play_audio_format = dc.out_audio_format; + decoder_starting = false; - if (!player.paused && !player_open_output(player)) { + if (!paused && !OpenOutput()) { char *uri = dc.song->GetURI(); g_warning("problems opening audio device " "while playing \"%s\"", uri); @@ -428,30 +485,23 @@ player_check_decoder_startup(player &player) } } -/** - * Sends a chunk of silence to the audio outputs. This is called when - * there is not enough decoded data in the pipe yet, to prevent - * underruns in the hardware buffers. - * - * The player lock is not held. - */ -static bool -player_send_silence(player &player) +bool +player::SendSilence() { - assert(player.output_open); - assert(player.play_audio_format.IsDefined()); + assert(output_open); + assert(play_audio_format.IsDefined()); - struct music_chunk *chunk = player.buffer.Allocate(); + struct music_chunk *chunk = buffer.Allocate(); if (chunk == NULL) { g_warning("Failed to allocate silence buffer"); return false; } #ifndef NDEBUG - chunk->audio_format = player.play_audio_format; + chunk->audio_format = play_audio_format; #endif - const size_t frame_size = player.play_audio_format.GetFrameSize(); + const size_t frame_size = play_audio_format.GetFrameSize(); /* this formula ensures that we don't send partial frames */ unsigned num_frames = sizeof(chunk->data) / frame_size; @@ -463,63 +513,53 @@ player_send_silence(player &player) Error error; if (!audio_output_all_play(chunk, error)) { g_warning("%s", error.GetMessage()); - player.buffer.Return(chunk); + buffer.Return(chunk); return false; } return true; } -/** - * This is the handler for the #PLAYER_COMMAND_SEEK command. - * - * The player lock is not held. - */ -static bool player_seek_decoder(player &player) +inline bool +player::SeekDecoder() { - player_control &pc = player.pc; - Song *song = pc.next_song; - decoder_control &dc = player.dc; - assert(pc.next_song != NULL); - const unsigned start_ms = song->start_ms; + const unsigned start_ms = pc.next_song->start_ms; - if (!dc.LockIsCurrentSong(song)) { + if (!dc.LockIsCurrentSong(pc.next_song)) { /* the decoder is already decoding the "next" song - stop it and start the previous song again */ - player_dc_stop(player); + StopDecoder(); /* clear music chunks which might still reside in the pipe */ - player.pipe->Clear(player.buffer); + pipe->Clear(buffer); /* re-start the decoder */ - player_dc_start(player, *player.pipe); - if (!player_wait_for_decoder(player)) { + StartDecoder(*pipe); + if (!WaitForDecoder()) { /* decoder failure */ player_command_finished(pc); return false; } } else { - if (!player_dc_at_current_song(player)) { + if (!IsDecoderAtCurrentSong()) { /* the decoder is already decoding the "next" song, but it is the same song file; exchange the pipe */ - player.pipe->Clear(player.buffer); - delete player.pipe; - player.pipe = dc.pipe; + ClearAndReplacePipe(dc.pipe); } pc.next_song->Free(); pc.next_song = NULL; - player.queued = false; + queued = false; } /* wait for the decoder to complete initialization */ - while (player.decoder_starting) { - if (!player_check_decoder_startup(player)) { + while (decoder_starting) { + if (!CheckDecoderStartup()) { /* decoder failure */ player_command_finished(pc); return false; @@ -540,28 +580,23 @@ static bool player_seek_decoder(player &player) return false; } - player.elapsed_time = where; + elapsed_time = where; player_command_finished(pc); - player.xfade = XFADE_UNKNOWN; + xfade = XFADE_UNKNOWN; /* re-fill the buffer after seeking */ - player.buffering = true; + buffering = true; audio_output_all_cancel(); return true; } -/** - * Player lock must be held before calling. - */ -static void player_process_command(player &player) +inline void +player::ProcessCommand() { - player_control &pc = player.pc; - gcc_unused decoder_control &dc = player.dc; - switch (pc.command) { case PLAYER_COMMAND_NONE: case PLAYER_COMMAND_STOP: @@ -578,30 +613,30 @@ static void player_process_command(player &player) case PLAYER_COMMAND_QUEUE: assert(pc.next_song != NULL); - assert(!player.queued); - assert(!player_dc_at_next_song(player)); + assert(!queued); + assert(!IsDecoderAtNextSong()); - player.queued = true; + queued = true; player_command_finished_locked(pc); break; case PLAYER_COMMAND_PAUSE: pc.Unlock(); - player.paused = !player.paused; - if (player.paused) { + paused = !paused; + if (paused) { audio_output_all_pause(); pc.Lock(); pc.state = PLAYER_STATE_PAUSE; - } else if (!player.play_audio_format.IsDefined()) { + } else if (!play_audio_format.IsDefined()) { /* the decoder hasn't provided an audio format yet - don't open the audio device yet */ pc.Lock(); pc.state = PLAYER_STATE_PLAY; } else { - player_open_output(player); + OpenOutput(); pc.Lock(); } @@ -611,7 +646,7 @@ static void player_process_command(player &player) case PLAYER_COMMAND_SEEK: pc.Unlock(); - player_seek_decoder(player); + SeekDecoder(); pc.Lock(); break; @@ -624,22 +659,22 @@ static void player_process_command(player &player) return; } - if (player_dc_at_next_song(player)) { + if (IsDecoderAtNextSong()) { /* the decoder is already decoding the song - stop it and reset the position */ pc.Unlock(); - player_dc_stop(player); + StopDecoder(); pc.Lock(); } pc.next_song->Free(); pc.next_song = NULL; - player.queued = false; + queued = false; player_command_finished_locked(pc); break; case PLAYER_COMMAND_REFRESH: - if (player.output_open && !player.paused) { + if (output_open && !paused) { pc.Unlock(); audio_output_all_check(); pc.Lock(); @@ -647,7 +682,7 @@ static void player_process_command(player &player) pc.elapsed_time = audio_output_all_get_elapsed_time(); if (pc.elapsed_time < 0.0) - pc.elapsed_time = player.elapsed_time; + pc.elapsed_time = elapsed_time; player_command_finished_locked(pc); break; @@ -714,18 +749,9 @@ play_chunk(player_control &pc, return true; } -/** - * 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(player &player) +inline bool +player::PlayNextChunk() { - player_control &pc = player.pc; - decoder_control &dc = player.dc; - if (!audio_output_all_wait(&pc, 64)) /* the output pipe is still large enough, don't send another chunk */ @@ -733,38 +759,36 @@ play_next_chunk(player &player) unsigned cross_fade_position; struct music_chunk *chunk = NULL; - if (player.xfade == XFADE_ENABLED && - player_dc_at_next_song(player) && - (cross_fade_position = player.pipe->GetSize()) - <= player.cross_fade_chunks) { + if (xfade == XFADE_ENABLED && IsDecoderAtNextSong() && + (cross_fade_position = pipe->GetSize()) <= cross_fade_chunks) { /* perform cross fade */ music_chunk *other_chunk = dc.pipe->Shift(); - if (!player.cross_fading) { + if (!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; + cross_fade_chunks = cross_fade_position; + cross_fading = true; } if (other_chunk != NULL) { - chunk = player.pipe->Shift(); + chunk = pipe->Shift(); 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::MergeReplace(player.cross_fade_tag, + cross_fade_tag = + Tag::MergeReplace(cross_fade_tag, other_chunk->tag); other_chunk->tag = NULL; if (std::isnan(pc.mixramp_delay_seconds)) { chunk->mix_ratio = ((float)cross_fade_position) - / player.cross_fade_chunks; + / cross_fade_chunks; } else { chunk->mix_ratio = nan(""); } @@ -777,7 +801,7 @@ play_next_chunk(player &player) beginning of the new song, we can easily recover by throwing it away now */ - player.buffer.Return(other_chunk); + buffer.Return(other_chunk); other_chunk = NULL; } @@ -792,7 +816,7 @@ play_next_chunk(player &player) cross fading */ dc.Unlock(); - player.xfade = XFADE_DISABLED; + xfade = XFADE_DISABLED; } else { /* wait for the decoder */ dc.Signal(); @@ -805,26 +829,24 @@ play_next_chunk(player &player) } if (chunk == NULL) - chunk = player.pipe->Shift(); + chunk = pipe->Shift(); 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::MergeReplace(chunk->tag, - player.cross_fade_tag); - player.cross_fade_tag = NULL; + if (xfade != XFADE_ENABLED && cross_fade_tag != nullptr) { + chunk->tag = Tag::MergeReplace(chunk->tag, cross_fade_tag); + cross_fade_tag = nullptr; } /* play the current chunk */ Error error; - if (!play_chunk(player.pc, player.song, chunk, player.buffer, - player.play_audio_format, error)) { + if (!play_chunk(pc, song, chunk, buffer, play_audio_format, error)) { g_warning("%s", error.GetMessage()); - player.buffer.Return(chunk); + buffer.Return(chunk); pc.Lock(); @@ -833,7 +855,7 @@ play_next_chunk(player &player) /* pause: the user may resume playback as soon as an audio output becomes available */ pc.state = PLAYER_STATE_PAUSE; - player.paused = true; + paused = true; pc.Unlock(); @@ -848,45 +870,34 @@ play_next_chunk(player &player) dc.Lock(); if (!dc.IsIdle() && dc.pipe->GetSize() <= (pc.buffered_before_play + - player.buffer.GetSize() * 3) / 4) + buffer.GetSize() * 3) / 4) dc.Signal(); dc.Unlock(); return true; } -/** - * This is called at the border between two songs: the audio output - * has consumed all chunks of the current song, and we should start - * sending chunks from the next one. - * - * The player lock is not held. - * - * @return true on success, false on error (playback will be stopped) - */ -static bool -player_song_border(player &player) +inline bool +player::SongBorder() { - player.xfade = XFADE_UNKNOWN; + xfade = XFADE_UNKNOWN; - char *uri = player.song->GetURI(); + char *uri = song->GetURI(); g_message("played \"%s\"", uri); g_free(uri); - delete player.pipe; - player.pipe = player.dc.pipe; + ReplacePipe(dc.pipe); audio_output_all_song_border(); - if (!player_wait_for_decoder(player)) + if (!WaitForDecoder()) return false; - player_control &pc = player.pc; pc.Lock(); const bool border_pause = pc.border_pause; if (border_pause) { - player.paused = true; + paused = true; pc.state = PLAYER_STATE_PAUSE; } @@ -898,28 +909,20 @@ player_song_border(player &player) return true; } -/* - * The main loop of the player thread, during playback. This is - * basically a state machine, which multiplexes data between the - * decoder thread and the output threads. - */ -static void -do_play(player_control &pc, decoder_control &dc, - MusicBuffer &buffer) +inline void +player::Run() { - player player(pc, dc, buffer); - pc.Unlock(); - player.pipe = new MusicPipe(); + pipe = new MusicPipe(); - player_dc_start(player, *player.pipe); - if (!player_wait_for_decoder(player)) { - assert(player.song == NULL); + StartDecoder(*pipe); + if (!WaitForDecoder()) { + assert(song == NULL); - player_dc_stop(player); + StopDecoder(); player_command_finished(pc); - delete player.pipe; + delete pipe; GlobalEvents::Emit(GlobalEvents::PLAYLIST); pc.Lock(); return; @@ -929,12 +932,12 @@ do_play(player_control &pc, decoder_control &dc, pc.state = PLAYER_STATE_PLAY; if (pc.command == PLAYER_COMMAND_SEEK) - player.elapsed_time = pc.seek_where; + elapsed_time = pc.seek_where; player_command_finished_locked(pc); while (true) { - player_process_command(player); + ProcessCommand(); if (pc.command == PLAYER_COMMAND_STOP || pc.command == PLAYER_COMMAND_EXIT || pc.command == PLAYER_COMMAND_CLOSE_AUDIO) { @@ -945,19 +948,18 @@ do_play(player_control &pc, decoder_control &dc, pc.Unlock(); - if (player.buffering) { + if (buffering) { /* buffering at the start of the song - wait until the buffer is large enough, to prevent stuttering on slow machines */ - if (player.pipe->GetSize() < pc.buffered_before_play && + if (pipe->GetSize() < pc.buffered_before_play && !dc.LockIsIdle()) { /* not enough decoded buffer space yet */ - if (!player.paused && - player.output_open && + if (!paused && output_open && audio_output_all_check() < 4 && - !player_send_silence(player)) + !SendSilence()) break; dc.Lock(); @@ -968,14 +970,14 @@ do_play(player_control &pc, decoder_control &dc, continue; } else { /* buffering is complete */ - player.buffering = false; + buffering = false; } } - if (player.decoder_starting) { + if (decoder_starting) { /* wait until the decoder is initialized completely */ - if (!player_check_decoder_startup(player)) + if (!CheckDecoderStartup()) break; pc.Lock(); @@ -985,31 +987,30 @@ do_play(player_control &pc, decoder_control &dc, #ifndef NDEBUG /* music_pipe_check_format(&play_audio_format, - player.next_song_chunk, + next_song_chunk, &dc.out_audio_format); */ #endif - if (dc.LockIsIdle() && player.queued && - dc.pipe == player.pipe) { + if (dc.LockIsIdle() && queued && dc.pipe == pipe) { /* the decoder has finished the current song; make it decode the next song */ - assert(dc.pipe == NULL || dc.pipe == player.pipe); + assert(dc.pipe == NULL || dc.pipe == pipe); - player_dc_start(player, *new MusicPipe()); + StartDecoder(*new MusicPipe()); } if (/* no cross-fading if MPD is going to pause at the end of the current song */ !pc.border_pause && - player_dc_at_next_song(player) && - player.xfade == XFADE_UNKNOWN && + IsDecoderAtNextSong() && + xfade == XFADE_UNKNOWN && !dc.LockIsStarting()) { /* enable cross fading in this song? if yes, calculate how many chunks will be required for it */ - player.cross_fade_chunks = + cross_fade_chunks = cross_fade_calc(pc.cross_fade_seconds, dc.total_time, pc.mixramp_db, pc.mixramp_delay_seconds, @@ -1018,29 +1019,29 @@ do_play(player_control &pc, decoder_control &dc, dc.mixramp_start, dc.mixramp_prev_end, dc.out_audio_format, - player.play_audio_format, - player.buffer.GetSize() - + play_audio_format, + buffer.GetSize() - pc.buffered_before_play); - if (player.cross_fade_chunks > 0) { - player.xfade = XFADE_ENABLED; - player.cross_fading = false; + if (cross_fade_chunks > 0) { + xfade = XFADE_ENABLED; + cross_fading = false; } else /* cross fading is disabled or the next song is too short */ - player.xfade = XFADE_DISABLED; + xfade = XFADE_DISABLED; } - if (player.paused) { + if (paused) { pc.Lock(); if (pc.command == PLAYER_COMMAND_NONE) pc.Wait(); continue; - } else if (!player.pipe->IsEmpty()) { + } else if (!pipe->IsEmpty()) { /* at least one music chunk is ready - send it to the audio output */ - play_next_chunk(player); + PlayNextChunk(); } else if (audio_output_all_check() > 0) { /* not enough data from decoder, but the output thread is still busy, so it's @@ -1048,45 +1049,44 @@ do_play(player_control &pc, decoder_control &dc, /* XXX synchronize in a better way */ g_usleep(10000); - } else if (player_dc_at_next_song(player)) { + } else if (IsDecoderAtNextSong()) { /* at the beginning of a new song */ - if (!player_song_border(player)) + if (!SongBorder()) break; } else if (dc.LockIsIdle()) { /* check the size of the pipe again, because the decoder thread may have added something since we last checked */ - if (player.pipe->IsEmpty()) { + if (pipe->IsEmpty()) { /* wait for the hardware to finish playback */ audio_output_all_drain(); break; } - } else if (player.output_open) { + } else if (output_open) { /* the decoder is too busy and hasn't provided new PCM data in time: send silence (if the output pipe is empty) */ - if (!player_send_silence(player)) + if (!SendSilence()) break; } pc.Lock(); } - player_dc_stop(player); + StopDecoder(); - player.pipe->Clear(player.buffer); - delete player.pipe; + ClearAndDeletePipe(); - delete player.cross_fade_tag; + delete cross_fade_tag; - if (player.song != NULL) - player.song->Free(); + if (song != nullptr) + song->Free(); pc.Lock(); - if (player.queued) { + if (queued) { assert(pc.next_song != NULL); pc.next_song->Free(); pc.next_song = NULL; @@ -1101,6 +1101,14 @@ do_play(player_control &pc, decoder_control &dc, pc.Lock(); } +static void +do_play(player_control &pc, decoder_control &dc, + MusicBuffer &buffer) +{ + player player(pc, dc, buffer); + player.Run(); +} + static gpointer player_task(gpointer arg) { |