diff --git a/core/audio.cpp b/core/audio.cpp index f633b221..1ea72b14 100644 --- a/core/audio.cpp +++ b/core/audio.cpp @@ -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) diff --git a/core/core.cpp b/core/core.cpp index 8dc74954..7fa7d303 100644 --- a/core/core.cpp +++ b/core/core.cpp @@ -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() diff --git a/gui/gst.cpp b/gui/gst.cpp index 98b06f62..b75c192b 100644 --- a/gui/gst.cpp +++ b/gui/gst.cpp @@ -53,38 +53,29 @@ static void set_markup(Gtk::Label *label, const std::string &size, Glib::Markup::escape_text(text) + ""); } +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) { diff --git a/gui/ocarina.cpp b/gui/ocarina.cpp index a2742b97..9b3eb8e7 100644 --- a/gui/ocarina.cpp +++ b/gui/ocarina.cpp @@ -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(); diff --git a/gui/queue/model.cpp b/gui/queue/model.cpp index 529f729e..ccb23504 100644 --- a/gui/queue/model.cpp +++ b/gui/queue/model.cpp @@ -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(); } diff --git a/include/core/audio.h b/include/core/audio.h index 5c806822..a3229e75 100644 --- a/include/core/audio.h +++ b/include/core/audio.h @@ -12,6 +12,12 @@ extern "C" { #include +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 */ diff --git a/include/core/core.h b/include/core/core.h index 0319937c..d5d027c1 100644 --- a/include/core/core.h +++ b/include/core/core.h @@ -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; }; /** diff --git a/include/gui/ocarina.h b/include/gui/ocarina.h index 0fce6061..058613ea 100644 --- a/include/gui/ocarina.h +++ b/include/gui/ocarina.h @@ -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(); diff --git a/tests/core/audio.cpp b/tests/core/audio.cpp index fe81027b..098ff614 100644 --- a/tests/core/audio.cpp +++ b/tests/core/audio.cpp @@ -4,11 +4,14 @@ #include extern "C" { #include +#include +#include } #include #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),