/** * Plays a #music_chunk object (after applying software volume). If * it contains a (stream) tag, copy it to the current song, so MPD's * playlist reflects the new stream tag. * * Player lock is not held. */ static bool play_chunk(struct player_control *pc, struct song *song, struct music_chunk *chunk, const struct audio_format *format) { assert(music_chunk_check_format(chunk, format)); if (chunk->tag != NULL) update_song_tag(song, chunk->tag); if (chunk->length == 0) { music_buffer_return(player_buffer, chunk); return true; } player_lock(pc); pc->bit_rate = chunk->bit_rate; player_unlock(pc); /* send the chunk to the audio outputs */ if (!audio_output_all_play(chunk)) return false; pc->total_play_time += (double)chunk->length / audio_format_time_to_size(format); return true; }
/** * 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(struct player *player) { assert(audio_format_defined(&player->play_audio_format)); struct music_chunk *chunk = music_buffer_allocate(player_buffer); if (chunk == NULL) { g_warning("Failed to allocate silence buffer"); return false; } #ifndef NDEBUG chunk->audio_format = player->play_audio_format; #endif size_t frame_size = audio_format_frame_size(&player->play_audio_format); /* this formula ensures that we don't send partial frames */ unsigned num_frames = sizeof(chunk->data) / frame_size; chunk->times = -1.0; /* undefined time stamp */ chunk->length = num_frames * frame_size; memset(chunk->data, 0, chunk->length); if (!audio_output_all_play(chunk)) { music_buffer_return(player_buffer, chunk); return false; } return true; }
void decoder_command_finished(struct decoder *decoder) { struct decoder_control *dc = decoder->dc; decoder_lock(dc); assert(dc->command != DECODE_COMMAND_NONE); assert(dc->command != DECODE_COMMAND_SEEK || dc->seek_error || decoder->seeking); assert(dc->pipe != NULL); if (decoder->seeking) { decoder->seeking = false; /* delete frames from the old song position */ if (decoder->chunk != NULL) { music_buffer_return(dc->buffer, decoder->chunk); decoder->chunk = NULL; } music_pipe_clear(dc->pipe, dc->buffer); decoder->timestamp = dc->seek_where; } dc->command = DECODE_COMMAND_NONE; decoder_unlock(dc); player_lock_signal(); }
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; }
void decoder_command_finished(G_GNUC_UNUSED struct decoder * decoder) { assert(dc.command != DECODE_COMMAND_NONE); assert(dc.command != DECODE_COMMAND_SEEK || dc.seek_error || decoder->seeking); if (dc.command == DECODE_COMMAND_SEEK) { /* delete frames from the old song position */ if (decoder->chunk != NULL) { music_buffer_return(dc.buffer, decoder->chunk); decoder->chunk = NULL; } music_pipe_clear(dc.pipe, dc.buffer); } dc.command = DECODE_COMMAND_NONE; notify_signal(&pc.notify); }
/** * 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; }