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