aboutsummaryrefslogtreecommitdiff
path: root/src/Playlist.cxx
diff options
context:
space:
mode:
authorMax Kellermann <max@duempel.org>2013-01-04 20:50:00 +0100
committerMax Kellermann <max@duempel.org>2013-01-04 22:12:33 +0100
commit05c91082e3de0b0078c26ddb9da68fd00da8c90e (patch)
tree7f1ef3988b787ab3e1e07e0147fabf94963495a5 /src/Playlist.cxx
parent7267558ba1cba9338c78b41d11e2eadef6bb515b (diff)
playlist: convert to C++
Diffstat (limited to 'src/Playlist.cxx')
-rw-r--r--src/Playlist.cxx452
1 files changed, 452 insertions, 0 deletions
diff --git a/src/Playlist.cxx b/src/Playlist.cxx
new file mode 100644
index 00000000..de2daf14
--- /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);
+}