core/audio: Move audio_load() out of the audio namespace

And add the on_load() audio operation.

Signed-off-by: Anna Schumaker <Anna@OcarinaProject.net>
This commit is contained in:
Anna Schumaker 2015-12-15 10:23:15 -05:00
parent e0c89556ed
commit e5c3d8f02e
9 changed files with 138 additions and 98 deletions

View File

@ -11,43 +11,52 @@ extern "C" {
static struct file audio_file;
static GstElement *audio_player = NULL;
static struct track *audio_track = NULL;
static GstElement *audio_player = NULL;
static struct audio_ops *audio_ops = NULL;
static bool _pause_enabled = false;
static unsigned int _pause_count = 0;
static struct track *cur_track = NULL;
static AudioDriver *cur_driver = NULL;
static void save_state()
static void __audio_save()
{
file_open(&audio_file, OPEN_WRITE);
file_writef(&audio_file, "%u\n", cur_track->tr_dbe.dbe_index);
file_writef(&audio_file, "%u\n", audio_track->tr_dbe.dbe_index);
file_close(&audio_file);
}
static void _load_track(struct track *track, bool start_playback)
static bool __audio_load(struct track *track, GstState state)
{
cur_track = track;
if (!track)
return;
gchar *path, *uri;
cur_driver->load(track);
if (start_playback)
audio :: play();
else
audio :: pause();
save_state();
if (!track)
return false;
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);
gst_element_set_state(audio_player, state);
audio_ops->on_load(track, state);
__audio_save();
g_free(uri);
g_free(path);
return true;
}
static bool continue_playback()
static GstState continue_playback()
{
bool ret = true;
GstState ret = GST_STATE_PLAYING;
if (_pause_enabled) {
if (_pause_count == 0) {
ret = false;
ret = GST_STATE_PAUSED;
_pause_enabled = false;
} else
_pause_count--;
@ -72,31 +81,32 @@ void AudioDriver :: eos()
{
struct track *track;
if (cur_track) {
track_played(cur_track);
queue_updated(collection_get_queue(), cur_track);
if (audio_track) {
track_played(audio_track);
queue_updated(collection_get_queue(), audio_track);
}
track = tempq_next();
if (!track)
track = queue_next(collection_get_queue());
_load_track(track, continue_playback());
__audio_load(track, continue_playback());
}
void audio_init(int *argc, char ***argv)
void audio_init(int *argc, char ***argv, struct audio_ops *ops)
{
unsigned int track;
gst_init(argc, argv);
audio_player = gst_element_factory_make("playbin", "ocarina_player");
audio_ops = ops;
file_init(&audio_file, "cur_track", 0);
if (file_open(&audio_file, OPEN_READ)) {
file_readf(&audio_file, "%u", &track);
file_close(&audio_file);
audio :: load_track(track_get(track));
audio_load(track_get(track));
}
}
@ -104,26 +114,40 @@ void audio_deinit()
{
gst_element_set_state(audio_player, GST_STATE_NULL);
gst_object_unref(audio_player);
audio_player = NULL;
audio_track = NULL;
gst_deinit();
}
bool audio_load(struct track *track)
{
if (track == audio_track)
return false;
if (__audio_load(track, cur_driver->is_playing() ? GST_STATE_PLAYING :
GST_STATE_PAUSED)) {
history_add(audio_track);
return true;
}
return false;
}
void audio :: play()
{
if (cur_track)
if (audio_track)
cur_driver->play();
}
void audio :: pause()
{
if (cur_track)
if (audio_track)
cur_driver->pause();
}
void audio :: seek_to(int64_t pos)
{
if (cur_track)
if (audio_track)
cur_driver->seek_to(pos);
}
@ -135,14 +159,14 @@ void audio :: stop()
int64_t audio :: position()
{
if (cur_track)
if (audio_track)
return cur_driver->position();
return 0;
}
int64_t audio :: duration()
{
if (cur_track)
if (audio_track)
return cur_driver->duration();
return 0;
}
@ -152,28 +176,20 @@ void audio :: next()
struct track *track = tempq_next();
if (!track)
track = queue_next(collection_get_queue());
_load_track(track, cur_driver->is_playing());
if (cur_track)
history_add(cur_track);
if (__audio_load(track, cur_driver->is_playing() ? GST_STATE_PLAYING :
GST_STATE_PAUSED))
history_add(audio_track);
}
void audio :: prev()
{
_load_track(history_prev(), cur_driver->is_playing());
}
void audio :: load_track(struct track *track)
{
if (!track || track == cur_track)
return;
_load_track(track, cur_driver->is_playing());
history_add(cur_track);
__audio_load(history_prev(), cur_driver->is_playing() ?
GST_STATE_PLAYING : GST_STATE_PAUSED);
}
struct track *audio :: current_track()
{
return cur_track;
return audio_track;
}
void audio :: pause_after(bool enabled, unsigned int n)

View File

@ -21,7 +21,7 @@ void core :: init(int *argc, char ***argv, struct core_init_data *init)
collection_init(init->collection_ops);
history_init(init->history_ops);
tempq_init(init->tempq_ops);
audio_init(argc, argv);
audio_init(argc, argv, init->audio_ops);
}
void core :: deinit()

View File

@ -53,38 +53,29 @@ static void set_markup(Gtk::Label *label, const std::string &size,
Glib::Markup::escape_text(text) + "</span>");
}
static void on_load(struct track *track, GstState state)
{
gchar *str = g_strdup_printf("From: %s", track->tr_album->al_name);
set_markup(o_album, "x-large", str);
g_free(str);
str = g_strdup_printf("By: %s", track->tr_artist->ar_name);
set_markup(o_artist, "x-large", str);
g_free(str);
set_markup(o_title, "xx-large", track->tr_title);
str = string_sec2str(track->tr_length);
o_duration->set_text(str);
g_free(str);
plist :: track_loaded(track);
}
class GSTDriver : public AudioDriver
{
public:
void load(struct track *track)
{
gchar *path = track_path(track);
gchar *uri = gst_filename_to_uri(path, NULL);
gchar *len = string_sec2str(track->tr_length);
gchar *str;
gst_change_state(GST_STATE_NULL);
g_object_set(G_OBJECT(audio_get_player()), "uri", uri, NULL);
g_free(uri);
str = g_strdup_printf("From: %s", track->tr_album->al_name);
set_markup(o_album, "x-large", str);
g_free(str);
str = g_strdup_printf("By: %s", track->tr_artist->ar_name);
set_markup(o_artist, "x-large", str);
g_free(str);
set_markup(o_title, "xx-large", track->tr_title);
o_duration->set_text(len);
g_free(path);
g_free(len);
plist :: track_loaded(track);
}
void play()
{
if (gst_change_state(GST_STATE_PLAYING)) {
@ -142,6 +133,10 @@ public:
static GSTDriver *gst_driver;
struct audio_ops audio_ops = {
on_load,
};
static int parse_gst_error(GstMessage *error)
{

View File

@ -18,6 +18,7 @@ struct core_init_data init_data = {
&history_ops,
&playlist_ops,
&tempq_ops,
&audio_ops,
};
namespace gui
@ -64,6 +65,7 @@ int main(int argc, char **argv)
gst :: init_pre();
core :: init(&argc, &argv, &init_data);
gst :: init();
plist :: init();
manager :: init();
init_tabs();

View File

@ -56,7 +56,7 @@ void QueueModel::on_row_changed(unsigned int row)
void QueueModel::on_path_selected(const Gtk::TreePath &path)
{
audio :: load_track(track_get(path_to_id(path)));
audio_load(track_get(path_to_id(path)));
queue_selected(_queue, path[0]);
audio :: play();
}

View File

@ -12,6 +12,12 @@ extern "C" {
#include <stdint.h>
struct audio_ops {
/* Called when a track is loaded. */
void (*on_load)(struct track *, GstState);
};
/**
* The audio driver class gives us an interface for using multiple
* audio frameworks for audio playback.
@ -25,13 +31,6 @@ public:
virtual ~AudioDriver(); /**< AudioDriver destructor. */
/**
* Loads an audio file for playback.
*
* @param track The Track to load.
*/
virtual void load(struct track *track) = 0;
/**
* Called to begin playback on the currently loaded track.
*/
@ -112,13 +111,6 @@ namespace audio
void next(); /**< Find and load the next track that should be played. */
void prev(); /**< Call the deck :: previous() function and load the result. */
/**
* Load a specific track for playback.
*
* @param track The track that should be loaded.
*/
void load_track(struct track *track);
/**
* @return A pointer to the currently playing track object.
*/
@ -156,11 +148,14 @@ namespace audio
/* Called to initialize the audio manager. */
void audio_init(int *, char ***);
void audio_init(int *, char ***, struct audio_ops *);
/* Called to deinitialize the audio manager. */
void audio_deinit();
/* Called to load a track for playback. */
bool audio_load(struct track *);
GstElement *audio_get_player();
#endif /* OCARINA_CORE_AUDIO_H */

View File

@ -12,6 +12,7 @@ struct core_init_data {
struct queue_ops *history_ops;
struct queue_ops *playlist_ops;
struct queue_ops *tempq_ops;
struct audio_ops *audio_ops;
};
/**

View File

@ -34,6 +34,7 @@ extern struct queue_ops collection_ops;
extern struct queue_ops history_ops;
extern struct queue_ops playlist_ops;
extern struct queue_ops tempq_ops;
extern struct audio_ops audio_ops;
void on_pq_created(queue *, unsigned int);
void post_init_queue_tabs();

View File

@ -4,11 +4,14 @@
#include <core/audio.h>
extern "C" {
#include <core/collection.h>
#include <core/history.h>
#include <core/idle.h>
}
#include <core/core.h>
#include "test.h"
struct track *TRACK_NULL = NULL;
static unsigned int load_count = 0;
class TestDriver : public AudioDriver
@ -22,13 +25,6 @@ public:
TestDriver() : playing(false), cur_pos(0), cur_duration(0) {}
~TestDriver() {};
void load(struct track *track)
{
cur_file = track_path(track);
playing = false;
cur_pos = 0;
}
void play() { playing = true; }
void pause() { playing = false; }
@ -39,26 +35,59 @@ public:
int64_t duration() { return cur_duration; }
} driver;
static void test_audio_load(struct track *track, GstState state)
{
load_count++;
}
static struct audio_ops test_audio_ops = {
test_audio_load,
};
static struct core_init_data test_init_data = {
NULL,
NULL,
NULL,
NULL,
&test_audio_ops,
};
static void test_init()
{
test_equal(audio_get_player(), NULL);
test_equal(audio :: current_track(), NULL);
test_equal(audio_get_player(), NULL);
core :: init(NULL, NULL, &test_init_data);
test_equal(audio_load(NULL), false);
test_equal(audio :: current_track(), NULL);
collection_add("tests/Music/Hyrule Symphony");
while (idle_run_task()) {};
test_equal(audio :: current_track(), NULL);
test_not_equal(audio_get_player(), NULL);
}
static void test_playback()
{
test_equal(audio_load(track_get(0)), true);
test_equal(load_count, 1);
test_equal(audio :: current_track(), track_get(0));
test_equal(queue_size(history_get_queue()), 1);
test_equal(audio_load(NULL), false);
test_equal(load_count, 1);
test_equal(audio :: current_track(), track_get(0));
test_equal(queue_size(history_get_queue()), 1);
test_equal(audio_load(track_get(0)), false);
test_equal(load_count, 1);
test_equal(queue_size(history_get_queue()), 1);
}
static void test_deinit()
{
core :: deinit();
@ -107,7 +136,7 @@ void test_init2()
struct track *track;
test_cp_data_dir();
audio_init(NULL, NULL);
audio_init(NULL, NULL, &test_audio_ops);
track = audio :: current_track();
test_equal(track, TRACK_NULL);
@ -158,17 +187,17 @@ void test_track_controls()
audio :: next();
test_equal(driver->is_playing(), true);
audio :: load_track(track);
audio_load(track);
test_not_equal(audio :: current_track(), track);
track = track_get(0);
audio :: seek_to(4242);
audio :: load_track(track);
audio_load(track);
test_equal(driver->is_playing(), true);
test_equal(audio :: position(), (long)0);
audio :: seek_to(4242);
audio :: load_track(track);
audio_load(track);
test_equal(driver->is_playing(), true);
test_equal(audio :: position(), (long)4242);
@ -212,6 +241,7 @@ void test_autopause()
DECLARE_UNIT_TESTS(
UNIT_TEST("Audio Initialization", test_init),
UNIT_TEST("Audio Playback", test_playback),
UNIT_TEST("Audio Deinitialization", test_deinit),
UNIT_TEST("Test Audio Pre-Init", test_pre_init),
UNIT_TEST("Test Audio Init 2", test_init2),