ocarina/core/audio.c
Anna Schumaker cfeca9ae4b core/playlist: Add playlist_type enum
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>
2016-08-13 08:31:30 -04:00

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