/** * 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(struct player *player) { player->xfade = XFADE_UNKNOWN; char *uri = song_get_uri(player->song); g_message("played \"%s\"", uri); g_free(uri); music_pipe_free(player->pipe); player->pipe = player->dc->pipe; audio_output_all_song_border(); if (!player_wait_for_decoder(player)) return false; struct player_control *const pc = player->pc; player_lock(pc); if (pc->border_pause) { player->paused = true; pc->state = PLAYER_STATE_PAUSE; } player_unlock(pc); return true; }
/** * 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(struct player *player) { struct player_control *pc = player->pc; struct decoder_control *dc = player->dc; assert(player->decoder_starting); decoder_lock(dc); GError *error = dc_get_error(dc); if (error != NULL) { /* the decoder failed */ decoder_unlock(dc); player_lock(pc); pc_set_error(pc, PLAYER_ERROR_DECODER, error); player_unlock(pc); return false; } else if (!decoder_is_starting(dc)) { /* the decoder is ready and ok */ decoder_unlock(dc); if (player->output_open && !audio_output_all_wait(pc, 1)) /* the output devices havn't finished playing all chunks yet - wait for that */ return true; player_lock(pc); pc->total_time = real_song_duration(dc->song, dc->total_time); pc->audio_format = dc->in_audio_format; player_unlock(pc); player->play_audio_format = dc->out_audio_format; player->decoder_starting = false; if (!player->paused && !player_open_output(player)) { char *uri = song_get_uri(dc->song); g_warning("problems opening audio device " "while playing \"%s\"", uri); g_free(uri); return true; } return true; } else { /* the decoder is not yet ready; wait some more */ player_wait_decoder(pc, dc); decoder_unlock(dc); return true; } }
GPtrArray * spl_load(const char *utf8path) { FILE *file; GPtrArray *list; char buffer[MPD_PATH_MAX]; char *path_fs; if (!spl_valid_name(utf8path)) return NULL; path_fs = map_spl_utf8_to_fs(utf8path); if (path_fs == NULL) return NULL; while (!(file = fopen(path_fs, "r")) && errno == EINTR); g_free(path_fs); if (file == NULL) return NULL; list = g_ptr_array_new(); while (fgets(buffer, sizeof(buffer), file)) { char *s = buffer; if (*s == PLAYLIST_COMMENT) continue; g_strchomp(buffer); if (!uri_has_scheme(s)) { char *path_utf8; struct song *song; path_utf8 = map_fs_to_utf8(s); if (path_utf8 == NULL) continue; song = db_get_song(path_utf8); g_free(path_utf8); if (song == NULL) continue; s = song_get_uri(song); } else s = g_strdup(s); g_ptr_array_add(list, s); if (list->len >= playlist_max_length) break; } while (fclose(file) && errno == EINTR); return list; }
char * sticker_song_get_value(const struct song *song, const char *name) { char *uri, *value; assert(song != NULL); assert(song_in_database(song)); uri = song_get_uri(song); value = sticker_load_value("song", uri, name); g_free(uri); return value; }
struct sticker * sticker_song_get(const struct song *song) { char *uri; struct sticker *sticker; assert(song != NULL); assert(song_in_database(song)); uri = song_get_uri(song); sticker = sticker_load("song", uri); g_free(uri); return sticker; }
bool sticker_song_delete_value(const struct song *song, const char *name) { char *uri; bool success; assert(song != NULL); assert(song_in_database(song)); uri = song_get_uri(song); success = sticker_delete_value("song", uri, name); g_free(uri); return success; }
bool sticker_song_delete(const struct song *song) { char *uri; bool ret; assert(song != NULL); assert(song_in_database(song)); uri = song_get_uri(song); ret = sticker_delete("song", uri); g_free(uri); return ret; }
bool sticker_song_set_value(const struct song *song, const char *name, const char *value) { char *uri; bool ret; assert(song != NULL); assert(song_in_database(song)); uri = song_get_uri(song); ret = sticker_store_value("song", uri, name, value); g_free(uri); return ret; }
static bool locate_tag_search(const struct song *song, enum tag_type type, const char *str) { bool ret = false; if (type == LOCATE_TAG_FILE_TYPE || (int)type == LOCATE_TAG_ANY_TYPE) { char *uri = song_get_uri(song); char *p = g_utf8_casefold(uri, -1); g_free(uri); if (strstr(p, str)) ret = true; g_free(p); if (ret == 1 || type == LOCATE_TAG_FILE_TYPE) return ret; } if (!song->tag) return false; bool visited_types[TAG_NUM_OF_ITEM_TYPES]; memset(visited_types, 0, sizeof(visited_types)); for (unsigned i = 0; i < song->tag->num_items && !ret; i++) { visited_types[song->tag->items[i]->type] = true; if ((int)type != LOCATE_TAG_ANY_TYPE && song->tag->items[i]->type != type) { continue; } char *duplicate = g_utf8_casefold(song->tag->items[i]->value, -1); if (*str && strstr(duplicate, str)) ret = true; g_free(duplicate); } /** If the search critieron was not visited during the sweep * through the song's tag, it means this field is absent from * the tag or empty. Thus, if the searched string is also * empty (first char is a \0), then it's a match as well and * we should return true. */ if (!*str && !visited_types[type]) return true; return ret; }
/** * Queue a song, addressed by its order number. */ static void playlist_queue_song_order(struct playlist *playlist, unsigned order) { struct song *song; char *uri; assert(queue_valid_order(&playlist->queue, order)); playlist->queued = order; song = queue_get_order(&playlist->queue, order); uri = song_get_uri(song); g_debug("queue song %i:\"%s\"", playlist->queued, uri); g_free(uri); pc_enqueue_song(song); }
void playlist_play_order(struct playlist *playlist, int orderNum) { struct song *song; char *uri; playlist->playing = true; playlist->queued = -1; song = queue_get_order(&playlist->queue, orderNum); uri = song_get_uri(song); g_debug("play %i:\"%s\"", orderNum, uri); g_free(uri); pc_play(song); playlist->current = orderNum; }
/** * 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(struct player *player) { player->xfade = XFADE_UNKNOWN; char *uri = song_get_uri(player->song); g_message("played \"%s\"", uri); g_free(uri); music_pipe_free(player->pipe); player->pipe = player->dc->pipe; audio_output_all_song_border(); if (!player_wait_for_decoder(player)) return false; return true; }
void playlist_play_order(struct playlist *playlist, struct player_control *pc, int orderNum) { char *uri; playlist->playing = true; playlist->queued = -1; struct song *song = song_dup_detached(queue_get_order(&playlist->queue, orderNum)); uri = song_get_uri(song); g_debug("play %i:\"%s\"", orderNum, uri); g_free(uri); pc_play(pc, song); playlist->current = orderNum; }
void playlist_print_song(FILE *file, const struct song *song) { if (playlist_saveAbsolutePaths && song_in_database(song)) { char *path = map_song_fs(song); if (path != NULL) { fprintf(file, "%s\n", path); g_free(path); } } else { char *uri = song_get_uri(song), *uri_fs; uri_fs = utf8_to_fs_charset(uri); g_free(uri); fprintf(file, "%s\n", uri_fs); g_free(uri_fs); } }
static bool locate_tag_match(const struct song *song, enum tag_type type, const char *str) { if (type == LOCATE_TAG_FILE_TYPE || (int)type == LOCATE_TAG_ANY_TYPE) { char *uri = song_get_uri(song); bool matches = strcmp(str, uri) == 0; g_free(uri); if (matches) return true; if (type == LOCATE_TAG_FILE_TYPE) return false; } if (!song->tag) return false; bool visited_types[TAG_NUM_OF_ITEM_TYPES]; memset(visited_types, 0, sizeof(visited_types)); for (unsigned i = 0; i < song->tag->num_items; i++) { visited_types[song->tag->items[i]->type] = true; if ((int)type != LOCATE_TAG_ANY_TYPE && song->tag->items[i]->type != type) { continue; } if (0 == strcmp(str, song->tag->items[i]->value)) return true; } /** If the search critieron was not visited during the sweep * through the song's tag, it means this field is absent from * the tag or empty. Thus, if the searched string is also * empty (first char is a \0), then it's a match as well and * we should return true. */ if (!*str && !visited_types[type]) return true; return false; }
/** * Queue a song, addressed by its order number. */ static void playlist_queue_song_order(struct playlist *playlist, struct player_control *pc, unsigned order) { char *uri; assert(queue_valid_order(&playlist->queue, order)); playlist->queued = order; struct song *song = song_dup_detached(queue_get_order(&playlist->queue, order)); uri = song_get_uri(song); g_debug("queue song %i:\"%s\"", playlist->queued, uri); g_free(uri); pc_enqueue_song(pc, song); }
static void decoder_run(struct decoder_control *dc) { const struct song *song = dc->song; char *uri; assert(song != NULL); if (song_is_file(song)) uri = map_song_fs(song); else uri = song_get_uri(song); if (uri == NULL) { dc->state = DECODE_STATE_ERROR; decoder_command_finished_locked(dc); return; } decoder_run_song(dc, song, uri); g_free(uri); }
static void decoder_run_song(struct decoder_control *dc, const struct song *song, const char *uri) { struct decoder decoder = { .dc = dc, .initial_seek_pending = dc->start_ms > 0, .initial_seek_running = false, }; int ret; decoder.timestamp = 0.0; decoder.seeking = false; decoder.song_tag = song->tag != NULL && song_is_file(song) ? tag_dup(song->tag) : NULL; decoder.stream_tag = NULL; decoder.decoder_tag = NULL; decoder.chunk = NULL; dc->state = DECODE_STATE_START; decoder_command_finished_locked(dc); pcm_convert_init(&decoder.conv_state); ret = song_is_file(song) ? decoder_run_file(&decoder, uri) : decoder_run_stream(&decoder, uri); decoder_unlock(dc); pcm_convert_deinit(&decoder.conv_state); /* flush the last chunk */ if (decoder.chunk != NULL) decoder_flush_chunk(&decoder); if (decoder.song_tag != NULL) tag_free(decoder.song_tag); if (decoder.stream_tag != NULL) tag_free(decoder.stream_tag); if (decoder.decoder_tag != NULL) tag_free(decoder.decoder_tag); decoder_lock(dc); dc->state = ret ? DECODE_STATE_STOP : DECODE_STATE_ERROR; } static void decoder_run(struct decoder_control *dc) { const struct song *song = dc->song; char *uri; assert(song != NULL); if (song_is_file(song)) uri = map_song_fs(song); else uri = song_get_uri(song); if (uri == NULL) { dc->state = DECODE_STATE_ERROR; decoder_command_finished_locked(dc); return; } decoder_run_song(dc, song, uri); g_free(uri); } static gpointer decoder_task(gpointer arg) { struct decoder_control *dc = arg; decoder_lock(dc); do { assert(dc->state == DECODE_STATE_STOP || dc->state == DECODE_STATE_ERROR); switch (dc->command) { case DECODE_COMMAND_START: dc_mixramp_start(dc, NULL); dc_mixramp_prev_end(dc, dc->mixramp_end); dc->mixramp_end = NULL; /* Don't free, it's copied above. */ dc->replay_gain_prev_db = dc->replay_gain_db; dc->replay_gain_db = 0; /* fall through */ case DECODE_COMMAND_SEEK: decoder_run(dc); break; case DECODE_COMMAND_STOP: decoder_command_finished_locked(dc); break; case DECODE_COMMAND_NONE: decoder_wait(dc); break; } } while (dc->command != DECODE_COMMAND_NONE || !dc->quit); decoder_unlock(dc); return NULL; } void decoder_thread_start(struct decoder_control *dc) { GError *e = NULL; assert(dc->thread == NULL); dc->quit = false; dc->thread = g_thread_create(decoder_task, dc, true, &e); if (dc->thread == NULL) MPD_ERROR("Failed to spawn decoder task: %s", e->message); }
static void decoder_run_song(struct decoder_control *dc, const struct song *song, const char *uri) { struct decoder decoder = { .dc = dc, .initial_seek_pending = dc->start_ms > 0, .initial_seek_running = false, }; int ret; decoder.timestamp = 0.0; decoder.seeking = false; decoder.song_tag = song->tag != NULL && song_is_file(song) ? tag_dup(song->tag) : NULL; decoder.stream_tag = NULL; decoder.decoder_tag = NULL; decoder.chunk = NULL; dc->state = DECODE_STATE_START; decoder_command_finished_locked(dc); pcm_convert_init(&decoder.conv_state); ret = song_is_file(song) ? decoder_run_file(&decoder, uri) : decoder_run_stream(&decoder, uri); decoder_unlock(dc); pcm_convert_deinit(&decoder.conv_state); /* flush the last chunk */ if (decoder.chunk != NULL) decoder_flush_chunk(&decoder); if (decoder.song_tag != NULL) tag_free(decoder.song_tag); if (decoder.stream_tag != NULL) tag_free(decoder.stream_tag); if (decoder.decoder_tag != NULL) tag_free(decoder.decoder_tag); decoder_lock(dc); if (ret) dc->state = DECODE_STATE_STOP; else { dc->state = DECODE_STATE_ERROR; const char *error_uri = song->uri; char *allocated = uri_remove_auth(error_uri); if (allocated != NULL) error_uri = allocated; dc->error = g_error_new(decoder_quark(), 0, "Failed to decode %s", error_uri); g_free(allocated); } } static void decoder_run(struct decoder_control *dc) { dc_clear_error(dc); const struct song *song = dc->song; char *uri; assert(song != NULL); if (song_is_file(song)) uri = map_song_fs(song); else uri = song_get_uri(song); if (uri == NULL) { dc->state = DECODE_STATE_ERROR; dc->error = g_error_new(decoder_quark(), 0, "Failed to map song"); decoder_command_finished_locked(dc); return; } decoder_run_song(dc, song, uri); g_free(uri); }
static char * pc_errored_song_uri(void) { return song_get_uri(pc.errored_song); }
char *decoder_get_uri(G_GNUC_UNUSED struct decoder *decoder) { return song_get_uri(dc.current_song); }
/** * 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(struct player *player) { struct player_control *pc = player->pc; struct decoder_control *dc = player->dc; assert(player->decoder_starting); decoder_lock(dc); if (decoder_has_failed(dc)) { /* the decoder failed */ decoder_unlock(dc); player_lock(pc); pc->errored_song = dc->song; pc->error = PLAYER_ERROR_FILE; player_unlock(pc); return false; } else if (!decoder_is_starting(dc)) { /* the decoder is ready and ok */ decoder_unlock(dc); if (audio_format_defined(&player->play_audio_format) && !audio_output_all_wait(pc, 1)) /* the output devices havn't finished playing all chunks yet - wait for that */ return true; player_lock(pc); pc->total_time = real_song_duration(dc->song, dc->total_time); pc->audio_format = dc->in_audio_format; player_unlock(pc); player->play_audio_format = dc->out_audio_format; player->decoder_starting = false; if (!player->paused && !audio_output_all_open(&dc->out_audio_format, player_buffer)) { char *uri = song_get_uri(dc->song); g_warning("problems opening audio device " "while playing \"%s\"", uri); g_free(uri); 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_unlock(pc); player->paused = true; return true; } return true; } else { /* the decoder is not yet ready; wait some more */ player_wait_decoder(pc, dc); decoder_unlock(dc); return true; } }