diff options
author | Max Kellermann <max@duempel.org> | 2013-01-04 20:50:00 +0100 |
---|---|---|
committer | Max Kellermann <max@duempel.org> | 2013-01-04 22:12:33 +0100 |
commit | 05c91082e3de0b0078c26ddb9da68fd00da8c90e (patch) | |
tree | 7f1ef3988b787ab3e1e07e0147fabf94963495a5 /src/Playlist.cxx | |
parent | 7267558ba1cba9338c78b41d11e2eadef6bb515b (diff) | |
download | mpd-05c91082e3de0b0078c26ddb9da68fd00da8c90e.tar.gz mpd-05c91082e3de0b0078c26ddb9da68fd00da8c90e.tar.xz mpd-05c91082e3de0b0078c26ddb9da68fd00da8c90e.zip |
playlist: convert to C++
Diffstat (limited to 'src/Playlist.cxx')
-rw-r--r-- | src/Playlist.cxx | 452 |
1 files changed, 452 insertions, 0 deletions
diff --git a/src/Playlist.cxx b/src/Playlist.cxx new file mode 100644 index 000000000..de2daf144 --- /dev/null +++ b/src/Playlist.cxx @@ -0,0 +1,452 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "PlaylistInternal.hxx" +#include "song.h" + +extern "C" { +#include "player_control.h" +#include "conf.h" +#include "idle.h" +} + +#include <glib.h> + +#include <assert.h> + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "playlist" + +void +playlist_increment_version_all(struct playlist *playlist) +{ + queue_modify_all(&playlist->queue); + idle_add(IDLE_PLAYLIST); +} + +void +playlist_tag_changed(struct playlist *playlist) +{ + if (!playlist->playing) + return; + + assert(playlist->current >= 0); + + queue_modify(&playlist->queue, playlist->current); + idle_add(IDLE_PLAYLIST); +} + +void +playlist_init(struct playlist *playlist) +{ + queue_init(&playlist->queue, + config_get_positive(CONF_MAX_PLAYLIST_LENGTH, + DEFAULT_PLAYLIST_MAX_LENGTH)); + + playlist->queued = -1; + playlist->current = -1; +} + +void +playlist_finish(struct playlist *playlist) +{ + queue_finish(&playlist->queue); +} + +/** + * 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); +} + +/** + * Called if the player thread has started playing the "queued" song. + */ +static void +playlist_song_started(struct playlist *playlist, struct player_control *pc) +{ + assert(pc->next_song == NULL); + assert(playlist->queued >= -1); + + /* queued song has started: copy queued to current, + and notify the clients */ + + int current = playlist->current; + playlist->current = playlist->queued; + playlist->queued = -1; + + if(playlist->queue.consume) + playlist_delete(playlist, pc, + queue_order_to_position(&playlist->queue, + current)); + + idle_add(IDLE_PLAYER); +} + +const struct song * +playlist_get_queued_song(struct playlist *playlist) +{ + if (!playlist->playing || playlist->queued < 0) + return NULL; + + return queue_get_order(&playlist->queue, playlist->queued); +} + +void +playlist_update_queued_song(struct playlist *playlist, + struct player_control *pc, + const struct song *prev) +{ + int next_order; + const struct song *next_song; + + if (!playlist->playing) + return; + + assert(!queue_is_empty(&playlist->queue)); + assert((playlist->queued < 0) == (prev == NULL)); + + next_order = playlist->current >= 0 + ? queue_next_order(&playlist->queue, playlist->current) + : 0; + + if (next_order == 0 && playlist->queue.random && + !playlist->queue.single) { + /* shuffle the song order again, so we get a different + order each time the playlist is played + completely */ + unsigned current_position = + queue_order_to_position(&playlist->queue, + playlist->current); + + queue_shuffle_order(&playlist->queue); + + /* make sure that the playlist->current still points to + the current song, after the song order has been + shuffled */ + playlist->current = + queue_position_to_order(&playlist->queue, + current_position); + } + + if (next_order >= 0) + next_song = queue_get_order(&playlist->queue, next_order); + else + next_song = NULL; + + if (prev != NULL && next_song != prev) { + /* clear the currently queued song */ + pc_cancel(pc); + playlist->queued = -1; + } + + if (next_order >= 0) { + if (next_song != prev) + playlist_queue_song_order(playlist, pc, next_order); + else + playlist->queued = next_order; + } +} + +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; +} + +static void +playlist_resume_playback(struct playlist *playlist, struct player_control *pc); + +/** + * This is the "PLAYLIST" event handler. It is invoked by the player + * thread whenever it requests a new queued song, or when it exits. + */ +void +playlist_sync(struct playlist *playlist, struct player_control *pc) +{ + if (!playlist->playing) + /* this event has reached us out of sync: we aren't + playing anymore; ignore the event */ + return; + + player_lock(pc); + enum player_state pc_state = pc_get_state(pc); + const struct song *pc_next_song = pc->next_song; + player_unlock(pc); + + if (pc_state == PLAYER_STATE_STOP) + /* the player thread has stopped: check if playback + should be restarted with the next song. That can + happen if the playlist isn't filling the queue fast + enough */ + playlist_resume_playback(playlist, pc); + else { + /* check if the player thread has already started + playing the queued song */ + if (pc_next_song == NULL && playlist->queued != -1) + playlist_song_started(playlist, pc); + + player_lock(pc); + pc_next_song = pc->next_song; + player_unlock(pc); + + /* make sure the queued song is always set (if + possible) */ + if (pc_next_song == NULL && playlist->queued < 0) + playlist_update_queued_song(playlist, pc, NULL); + } +} + +/** + * The player has stopped for some reason. Check the error, and + * decide whether to re-start playback + */ +static void +playlist_resume_playback(struct playlist *playlist, struct player_control *pc) +{ + enum player_error error; + + assert(playlist->playing); + assert(pc_get_state(pc) == PLAYER_STATE_STOP); + + error = pc_get_error_type(pc); + if (error == PLAYER_ERROR_NONE) + playlist->error_count = 0; + else + ++playlist->error_count; + + if ((playlist->stop_on_error && error != PLAYER_ERROR_NONE) || + error == PLAYER_ERROR_OUTPUT || + playlist->error_count >= queue_length(&playlist->queue)) + /* too many errors, or critical error: stop + playback */ + playlist_stop(playlist, pc); + else + /* continue playback at the next song */ + playlist_next(playlist, pc); +} + +bool +playlist_get_repeat(const struct playlist *playlist) +{ + return playlist->queue.repeat; +} + +bool +playlist_get_random(const struct playlist *playlist) +{ + return playlist->queue.random; +} + +bool +playlist_get_single(const struct playlist *playlist) +{ + return playlist->queue.single; +} + +bool +playlist_get_consume(const struct playlist *playlist) +{ + return playlist->queue.consume; +} + +void +playlist_set_repeat(struct playlist *playlist, struct player_control *pc, + bool status) +{ + if (status == playlist->queue.repeat) + return; + + struct queue *queue = &playlist->queue; + + queue->repeat = status; + + pc_set_border_pause(pc, queue->single && !queue->repeat); + + /* if the last song is currently being played, the "next song" + might change when repeat mode is toggled */ + playlist_update_queued_song(playlist, pc, + playlist_get_queued_song(playlist)); + + idle_add(IDLE_OPTIONS); +} + +static void +playlist_order(struct playlist *playlist) +{ + if (playlist->current >= 0) + /* update playlist.current, order==position now */ + playlist->current = queue_order_to_position(&playlist->queue, + playlist->current); + + queue_restore_order(&playlist->queue); +} + +void +playlist_set_single(struct playlist *playlist, struct player_control *pc, + bool status) +{ + if (status == playlist->queue.single) + return; + + struct queue *queue = &playlist->queue; + + queue->single = status; + + pc_set_border_pause(pc, queue->single && !queue->repeat); + + /* if the last song is currently being played, the "next song" + might change when single mode is toggled */ + playlist_update_queued_song(playlist, pc, + playlist_get_queued_song(playlist)); + + idle_add(IDLE_OPTIONS); +} + +void +playlist_set_consume(struct playlist *playlist, bool status) +{ + if (status == playlist->queue.consume) + return; + + playlist->queue.consume = status; + idle_add(IDLE_OPTIONS); +} + +void +playlist_set_random(struct playlist *playlist, struct player_control *pc, + bool status) +{ + const struct song *queued; + + if (status == playlist->queue.random) + return; + + queued = playlist_get_queued_song(playlist); + + playlist->queue.random = status; + + if (playlist->queue.random) { + /* shuffle the queue order, but preserve + playlist->current */ + + int current_position = + playlist->playing && playlist->current >= 0 + ? (int)queue_order_to_position(&playlist->queue, + playlist->current) + : -1; + + queue_shuffle_order(&playlist->queue); + + if (current_position >= 0) { + /* make sure the current song is the first in + the order list, so the whole rest of the + playlist is played after that */ + unsigned current_order = + queue_position_to_order(&playlist->queue, + current_position); + queue_swap_order(&playlist->queue, 0, current_order); + playlist->current = 0; + } else + playlist->current = -1; + } else + playlist_order(playlist); + + playlist_update_queued_song(playlist, pc, queued); + + idle_add(IDLE_OPTIONS); +} + +int +playlist_get_current_song(const struct playlist *playlist) +{ + if (playlist->current >= 0) + return queue_order_to_position(&playlist->queue, + playlist->current); + + return -1; +} + +int +playlist_get_next_song(const struct playlist *playlist) +{ + if (playlist->current >= 0) + { + if (playlist->queue.single == 1 && playlist->queue.repeat == 1) + return queue_order_to_position(&playlist->queue, + playlist->current); + else if (playlist->current + 1 < (int)queue_length(&playlist->queue)) + return queue_order_to_position(&playlist->queue, + playlist->current + 1); + else if (playlist->queue.repeat == 1) + return queue_order_to_position(&playlist->queue, 0); + } + + return -1; +} + +unsigned long +playlist_get_version(const struct playlist *playlist) +{ + return playlist->queue.version; +} + +int +playlist_get_length(const struct playlist *playlist) +{ + return queue_length(&playlist->queue); +} + +unsigned +playlist_get_song_id(const struct playlist *playlist, unsigned song) +{ + return queue_position_to_id(&playlist->queue, song); +} |