From c65c8b06f22d03827a4d7297e053ace3fbe4801c Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Wed, 25 Dec 2013 14:35:33 -0500 Subject: [PATCH] audio: Implement pause after N tracks feature This patch implements the pause-after-N-tracks feature. I also included various improvements to the audio code. Signed-off-by: Anna Schumaker --- design/audio.txt | 22 +++++++--- include/audio.h | 5 +++ lib/audio.cpp | 69 +++++++++++++++++++++++++++++++- tests/audio/audio.cpp | 91 +++++++++++++++++++++++++++++++++++++++--- tests/audio/audio.good | 46 +++++++++++++++++++++ 5 files changed, 220 insertions(+), 13 deletions(-) create mode 100644 tests/audio/audio.good diff --git a/design/audio.txt b/design/audio.txt index f4f64610..834f7b81 100644 --- a/design/audio.txt +++ b/design/audio.txt @@ -33,6 +33,9 @@ Audio: (lib/audio.cpp) options from the argv array as they are processed, so pass pointers to argc and argv to this function. + void audio :: quit() + Clean up memory allocated by gstreamer. + bool audio :: play() bool audio :: pause() Change the gstreamer state to either GST_STATE_PLAYING or @@ -71,12 +74,19 @@ Audio: (lib/audio.cpp) unsigned int audio :: duration() Return the duration of the current song in seconds. - void audio :: pause_after(unsigned int) - Pause after N tracks, pass a negative number to disable. The - count will only be decremented when an end-of-stream message - is received by the gstreamer pipeline. + void audio :: pause_after(bool enabled, unsigned int N) + Pause after N tracks. The first parameter is a bool + representing if this feature is enabled or not (true == enabled, + false == disabled). + + The count will only be decremented when an end-of-stream message + is received by the gstreamer pipeline, and not when calling + next(). + + bool audio :: pause_enabled() + Return true if pausing is enabled, and false if pausing is + disabled. unsigned int audio :: pause_count() Return the number of tracks that will be played before - playback pauses. Throw -1 if the user does not want us to - pause. + playback pauses. diff --git a/include/audio.h b/include/audio.h index 3cc357cb..604445a7 100644 --- a/include/audio.h +++ b/include/audio.h @@ -12,6 +12,7 @@ namespace audio { void init(int *, char ***); + void quit(); bool play(); bool pause(); @@ -22,6 +23,10 @@ namespace audio bool seek_to(long); long position(); long duration(); + + void pause_after(bool, unsigned int); + bool pause_enabled(); + unsigned int pause_count(); }; #endif /* OCARINA_AUDIO_H */ diff --git a/lib/audio.cpp b/lib/audio.cpp index 2bcf43c7..a7092ad5 100644 --- a/lib/audio.cpp +++ b/lib/audio.cpp @@ -8,11 +8,49 @@ #include static GstElement *ocarina_player; + static bool track_loaded = false; static unsigned int cur_trackid = 0; +static bool o_pause_enabled = false; +static unsigned int o_pause_count = 0; +static bool o_should_pause = false; + +static void parse_error(GstMessage *error) +{ + GError *err; + gchar *debug; + + gst_message_parse_error(error, &err, &debug); + g_print("Error: %s\n", err->message); + g_error_free(err); + g_free(debug); +} + +static void handle_pause_count() +{ + if (o_pause_enabled == false) + return; + else if (o_pause_count == 0) { + o_should_pause = true; + o_pause_enabled = false; + } else + o_pause_count--; +} + static gboolean on_message(GstBus *bus, GstMessage *message, gpointer data) { + switch (GST_MESSAGE_TYPE(message)) { + case GST_MESSAGE_ERROR: + parse_error(message); + audio :: next(); + break; + case GST_MESSAGE_EOS: + handle_pause_count(); + audio :: next(); + default: + break; + } return TRUE; } @@ -36,9 +74,15 @@ static bool load_song(library :: Song &song) gchar *escaped; std::string filepath = song.library->root_path + "/" + song.track->filepath; - gst_element_get_state(GST_ELEMENT(ocarina_player), &state, - NULL, GST_CLOCK_TIME_NONE); + if (o_should_pause == true) { + state = GST_STATE_PAUSED; + o_should_pause = false; + } else { + gst_element_get_state(GST_ELEMENT(ocarina_player), &state, + NULL, GST_CLOCK_TIME_NONE); + } + change_state(GST_STATE_NULL); escaped = gst_filename_to_uri(filepath.c_str(), NULL); g_object_set(G_OBJECT(ocarina_player), "uri", escaped, NULL); g_free(escaped); @@ -58,6 +102,11 @@ void audio :: init(int *argc, char ***argv) gst_bus_add_watch(bus, on_message, NULL); } +void audio :: quit() +{ + gst_deinit(); +} + bool audio :: play() { if (track_loaded == false) @@ -142,3 +191,19 @@ long audio :: duration() return 0; return duration; } + +void audio :: pause_after(bool enabled, unsigned int n) +{ + o_pause_enabled = enabled; + o_pause_count = n; +} + +bool audio :: pause_enabled() +{ + return o_pause_enabled; +} + +unsigned int audio :: pause_count() +{ + return o_pause_count; +} diff --git a/tests/audio/audio.cpp b/tests/audio/audio.cpp index 467239c2..f4dca2d9 100644 --- a/tests/audio/audio.cpp +++ b/tests/audio/audio.cpp @@ -63,11 +63,90 @@ void test_1() check_ret("1b", audio :: play(), true); check_ret("1c", audio :: pause(), true); check_ret("1d", audio :: seek_to(1 * GST_SECOND), true); - check_ret("1e", audio :: position(), 1 * GST_SECOND); - check_ret("1f", audio :: stop(), true); - check_ret("1g", audio :: current_trackid() == 0, true); - check_ret("1h", audio :: position(), 0); - check_ret("1i", audio :: duration(), song.track->length); + check_ret("1e", audio :: stop(), true); + check_ret("1f", audio :: current_trackid() == 0, true); + check_ret("1g", audio :: position(), 0); + print("\n"); +} + +/* Test pause_after() */ +unsigned int test_2_count = 0; +int test_2_cb(gpointer data) +{ + long seek_pos, pos, max; + library :: Song song; + GMainLoop *loop = (GMainLoop *)data; + + library :: lookup(audio :: current_trackid(), &song); + pos = audio :: position(); + + switch (test_2_count) { + case 0: + break; + case 1: + check_ret("2g", audio :: duration(), song.track->length * GST_SECOND); + seek_pos = (song.track->length * GST_SECOND) - GST_SECOND; + check_ret("2h", audio :: seek_to(seek_pos), true); + break; + case 2: + max = (song.track->length * GST_SECOND) - GST_SECOND + (501 * GST_MSECOND); + + check_ret("2i", pos <= max, true); + check_ret("2j", audio :: stop(), true); + break; + case 3: + check_ret("2k", pos, 0); + check_ret("2l", audio :: play(), true); + check_ret("2m", audio :: seek_to(audio :: duration() - 1), true); + break; + case 4: + check_ret("2n", audio :: pause_count(), (long)2); + check_ret("2o", audio :: seek_to(audio :: duration() - 1), true); + break; + case 5: + check_ret("2p", audio :: pause_count(), (long)1); + check_ret("2q", audio :: seek_to(audio :: duration() - 1), true); + break; + case 6: + check_ret("2r", audio :: pause_count(), (long)0); + check_ret("2s", audio :: seek_to(audio :: duration() - 1), true); + break; + case 7: + check_ret("2t", audio :: pause_enabled(), false); + check_ret("2u", audio :: pause_count(), (long)0); + break; + case 8: + pos = audio :: position(); + check_ret("2v", (0 <= pos) && (pos <= GST_MSECOND), true); + break; + case 9: + pos = audio :: position(); + check_ret("2w", (0 <= pos) && (pos <= GST_MSECOND), true); + default: + g_main_quit(loop); + } + + test_2_count++; + return true; +} + +void test_2() +{ + GMainLoop *loop; + + check_ret("2a", audio :: pause_enabled(), false); + check_ret("2b", audio :: pause_count(), (long)0); + audio :: pause_after(true, 3); + check_ret("2c", audio :: pause_enabled(), true); + check_ret("2d", audio :: pause_count(), (long)3); + audio :: next(); + check_ret("2e", audio :: pause_enabled(), true); + check_ret("2f", audio :: pause_count(), (long)3); + + audio :: play(); + loop = g_main_loop_new(NULL, FALSE); + g_timeout_add(500, test_2_cb, loop); + g_main_loop_run(loop); } int main(int argc, char **argv) @@ -87,6 +166,8 @@ int main(int argc, char **argv) plist->add(i); test_1(); + test_2(); + //audio :: quit(); return 0; } diff --git a/tests/audio/audio.good b/tests/audio/audio.good new file mode 100644 index 00000000..80e7c0a1 --- /dev/null +++ b/tests/audio/audio.good @@ -0,0 +1,46 @@ +Generating library: 0 +Generating library: 1 +Generating library: 2 +Generating library: 3 +Generating library: 4 + +Test 0a: Success! +Test 0b: Success! +Test 0c: Success! +Test 0d: Success! +Test 0e: Success! +Test 0f: Success! +Test 0g: Success! +Test 0h: Success! + +Test 1a: Success! +Test 1b: Success! +Test 1c: Success! +Test 1d: Success! +Test 1e: Success! +Test 1f: Success! +Test 1g: Success! + +Test 2a: Success! +Test 2b: Success! +Test 2c: Success! +Test 2d: Success! +Test 2e: Success! +Test 2f: Success! +Test 2g: Success! +Test 2h: Success! +Test 2i: Success! +Test 2j: Success! +Test 2k: Success! +Test 2l: Success! +Test 2m: Success! +Test 2n: Success! +Test 2o: Success! +Test 2p: Success! +Test 2q: Success! +Test 2r: Success! +Test 2s: Success! +Test 2t: Success! +Test 2u: Success! +Test 2v: Success! +Test 2w: Success!