ocarina/core/audio.c
Anna Schumaker c2178bc265 core: Cut back on hardcoded dbe_index uses
The dbe_index of a given database item might change in the future, so we
can't rely on it everywhere.  Let's just use it for saving and loading
files, with the expectation that changes will happen sometime after
startup.

Implements #69: Reduce use of dbe_index in Ocarina code
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2016-09-11 10:50:45 -04:00

267 lines
5.4 KiB
C

/*
* Copyright 2013 (c) Anna Schumaker.
*/
#include <core/audio.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", track_index(audio_track));
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)
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 */