cfeca9ae4b
I'm going to use this to distinguish between various playlist types that are about to be added. Let's update the playlist functions first, and then add more types in a future patch. Signed-off-by: Anna Schumaker <Anna@OcarinaProject.net>
270 lines
5.5 KiB
C
270 lines
5.5 KiB
C
/*
|
|
* Copyright 2013 (c) Anna Schumaker.
|
|
*/
|
|
#include <core/audio.h>
|
|
#include <core/collection.h>
|
|
#include <core/idle.h>
|
|
#include <core/playlist.h>
|
|
#include <core/tempq.h>
|
|
|
|
static struct file audio_file = FILE_INIT("cur_track", 0, 0);
|
|
static struct track *audio_track = NULL;
|
|
static GstElement *audio_player = NULL;
|
|
static struct audio_ops *audio_ops = NULL;
|
|
static int audio_pause_count = -1;
|
|
static guint audio_bus = 0;
|
|
|
|
|
|
static void __audio_save()
|
|
{
|
|
file_open(&audio_file, OPEN_WRITE);
|
|
file_writef(&audio_file, "%u\n", audio_track->tr_dbe.dbe_index);
|
|
file_close(&audio_file);
|
|
}
|
|
|
|
static bool __audio_change_state(GstState state)
|
|
{
|
|
GstStateChangeReturn ret = GST_STATE_CHANGE_FAILURE;
|
|
|
|
if (audio_cur_state() != state)
|
|
ret = gst_element_set_state(audio_player, state);
|
|
if (ret == GST_STATE_CHANGE_FAILURE)
|
|
return false;
|
|
|
|
audio_ops->on_state_change(state);
|
|
return true;
|
|
}
|
|
|
|
/* Load a track, but don't add it to the history. */
|
|
static struct track *__audio_load_basic(struct track *track, GstState state)
|
|
{
|
|
struct track *prev = audio_track;
|
|
gchar *path, *uri;
|
|
|
|
if (!track)
|
|
return NULL;
|
|
|
|
path = track_path(track);
|
|
uri = gst_filename_to_uri(path, NULL);
|
|
audio_track = track;
|
|
|
|
gst_element_set_state(audio_player, GST_STATE_NULL);
|
|
g_object_set(G_OBJECT(audio_player), "uri", uri, NULL);
|
|
audio_ops->on_load(track);
|
|
__audio_change_state(state);
|
|
|
|
tempq_updated(prev);
|
|
queue_updated(playlist_get_queue(PL_SYSTEM, "Collection"), prev);
|
|
queue_updated(playlist_get_queue(PL_SYSTEM, "Collection"), audio_track);
|
|
|
|
__audio_save();
|
|
g_free(uri);
|
|
g_free(path);
|
|
return track;
|
|
}
|
|
|
|
static struct track *__audio_load(struct track *track, GstState state)
|
|
{
|
|
if (__audio_load_basic(track, state))
|
|
playlist_add(PL_SYSTEM, "History", track);
|
|
return track;
|
|
}
|
|
|
|
static struct track *__audio_next(GstState state)
|
|
{
|
|
struct track *track = tempq_next();
|
|
if (!track)
|
|
track = playlist_next();
|
|
return __audio_load(track, state);
|
|
}
|
|
|
|
static gboolean __audio_message(GstBus *bus, GstMessage *message, gpointer data)
|
|
{
|
|
switch (GST_MESSAGE_TYPE(message)) {
|
|
case GST_MESSAGE_ERROR:
|
|
audio_error(message);
|
|
break;
|
|
case GST_MESSAGE_EOS:
|
|
audio_eos();
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool __audio_init_idle(void *data)
|
|
{
|
|
unsigned int track;
|
|
|
|
if (file_open(&audio_file, OPEN_READ)) {
|
|
file_readf(&audio_file, "%u", &track);
|
|
file_close(&audio_file);
|
|
__audio_load(track_get(track), GST_STATE_PAUSED);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
void audio_init(int *argc, char ***argv, struct audio_ops *ops)
|
|
{
|
|
GstBus *bus;
|
|
|
|
gst_init(argc, argv);
|
|
audio_player = gst_element_factory_make("playbin", "ocarina_player");
|
|
audio_ops = ops;
|
|
|
|
bus = gst_pipeline_get_bus(GST_PIPELINE(audio_player));
|
|
audio_bus = gst_bus_add_watch(bus, __audio_message, NULL);
|
|
gst_object_unref(bus);
|
|
|
|
idle_schedule(IDLE_SYNC, __audio_init_idle, NULL);
|
|
}
|
|
|
|
void audio_deinit()
|
|
{
|
|
gst_element_set_state(audio_player, GST_STATE_NULL);
|
|
gst_object_unref(GST_ELEMENT(audio_player));
|
|
g_source_remove(audio_bus);
|
|
|
|
audio_player = NULL;
|
|
audio_track = NULL;
|
|
|
|
gst_deinit();
|
|
}
|
|
|
|
bool audio_load(struct track *track)
|
|
{
|
|
if (track == audio_track)
|
|
return false;
|
|
return __audio_load(track, GST_STATE_PLAYING);
|
|
}
|
|
|
|
struct track *audio_cur_track()
|
|
{
|
|
return audio_track;
|
|
}
|
|
|
|
GstState audio_cur_state()
|
|
{
|
|
GstState cur = GST_STATE_NULL;
|
|
if (audio_player)
|
|
gst_element_get_state(audio_player,
|
|
&cur, NULL,
|
|
GST_CLOCK_TIME_NONE);
|
|
return cur;
|
|
}
|
|
|
|
bool audio_play()
|
|
{
|
|
if (!audio_track)
|
|
return false;
|
|
return __audio_change_state(GST_STATE_PLAYING);
|
|
}
|
|
|
|
bool audio_pause()
|
|
{
|
|
if (!audio_track)
|
|
return false;
|
|
return __audio_change_state(GST_STATE_PAUSED);
|
|
}
|
|
|
|
bool audio_seek(gint64 offset)
|
|
{
|
|
if (!audio_track)
|
|
return false;
|
|
return gst_element_seek_simple(audio_player,
|
|
GST_FORMAT_TIME,
|
|
GST_SEEK_FLAG_FLUSH,
|
|
offset);
|
|
}
|
|
|
|
gint64 audio_position()
|
|
{
|
|
gint64 position;
|
|
if (gst_element_query_position(audio_player,
|
|
GST_FORMAT_TIME,
|
|
&position))
|
|
return position;
|
|
return 0;
|
|
}
|
|
|
|
int64_t audio_duration()
|
|
{
|
|
gint64 duration;
|
|
if (gst_element_query_duration(audio_player,
|
|
GST_FORMAT_TIME,
|
|
&duration))
|
|
return duration;
|
|
if (audio_track)
|
|
return audio_track->tr_length * GST_SECOND;
|
|
return 0;
|
|
}
|
|
|
|
struct track *audio_next()
|
|
{
|
|
return __audio_next(GST_STATE_PLAYING);
|
|
}
|
|
|
|
struct track *audio_prev()
|
|
{
|
|
return __audio_load_basic(playlist_prev(), GST_STATE_PLAYING);
|
|
}
|
|
|
|
struct track *audio_eos()
|
|
{
|
|
/* Mark current track as played */
|
|
if (audio_track) {
|
|
track_played(audio_track);
|
|
playlist_update(PL_SYSTEM, "Unplayed");
|
|
playlist_update(PL_SYSTEM, "Most Played");
|
|
playlist_update(PL_SYSTEM, "Least Played");
|
|
}
|
|
|
|
/* Check pause count and pick the next track */
|
|
if (audio_pause_count >= 0) {
|
|
audio_pause_after(audio_pause_count - 1);
|
|
if (audio_pause_count == -1)
|
|
return __audio_next(GST_STATE_PAUSED);
|
|
}
|
|
return __audio_next(GST_STATE_PLAYING);
|
|
}
|
|
|
|
void audio_error(GstMessage *error)
|
|
{
|
|
gchar *path = NULL, *debug = NULL;
|
|
GError *err = NULL;
|
|
|
|
if (audio_track) {
|
|
collection_check_library(audio_track->tr_library);
|
|
path = track_path(audio_track);
|
|
}
|
|
if (error)
|
|
gst_message_parse_error(error, &err, &debug);
|
|
|
|
g_print("Error: %s (%s)\n", err->message, path);
|
|
if (debug)
|
|
g_print("Debug details: %s\n", debug);
|
|
|
|
__audio_next(audio_cur_state());
|
|
|
|
g_error_free(err);
|
|
g_free(debug);
|
|
g_free(path);
|
|
}
|
|
|
|
void audio_pause_after(int n)
|
|
{
|
|
if (n != audio_pause_count) {
|
|
audio_pause_count = n;
|
|
audio_ops->on_config_pause(audio_pause_count);
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_TESTING
|
|
GstElement *test_audio_player()
|
|
{
|
|
return audio_player;
|
|
}
|
|
#endif /* CONFIG_TESTING */
|