audio: Begin implementing audio code
I have written play(), pause(), next() and seek_to() so far. Signed-off-by: Anna Schumaker <schumaker.anna@gmail.com>
This commit is contained in:
parent
b8b215be35
commit
edbdd1c66f
1
config
1
config
|
@ -34,6 +34,7 @@ class Config:
|
|||
if self.TEST: env.Append( CCFLAGS = [ "-DCONFIG_TEST" ])
|
||||
|
||||
def reset(self, TEST = False):
|
||||
self.AUDIO = False
|
||||
self.DATABASE = False
|
||||
self.DECK = False
|
||||
self.FILE = False
|
||||
|
|
|
@ -13,11 +13,12 @@ Audio: (lib/audio.cpp)
|
|||
namespace that will be easier to work with than using raw gstreamer
|
||||
functions.
|
||||
|
||||
The audio layer will also control the "pause after N tracks" feature
|
||||
so songs can be loaded without neeting to pass in a "begin playback"
|
||||
flag every time. The remaining tracks counter will only be decremented
|
||||
when a song finishes through the end-of-stream message passed by the
|
||||
gst pipeline.
|
||||
The audio layer is meant to be an interface used by the front end to
|
||||
control most features of the backend library. This means implementing
|
||||
next track, previous track, and so on here.
|
||||
|
||||
Gstreamer options passed to audio :: init() can be found by running
|
||||
`gst-inspect-1.0 --help-gst` on the command line.
|
||||
|
||||
- Internal:
|
||||
Set up a message bus to look for end-of-stream and error messages so
|
||||
|
@ -28,27 +29,30 @@ Audio: (lib/audio.cpp)
|
|||
- API:
|
||||
void audio :: init(argc, argv)
|
||||
Initialize the gstreamer layer and reload the track that was
|
||||
last loaded before shutdown. Please only pass --gst-* options
|
||||
for argv.
|
||||
last loaded before shutdown. Gstreamer is supposed to remove
|
||||
options from the argv array as they are processed, so pass
|
||||
pointers to argc and argv to this function.
|
||||
|
||||
void audio :: play()
|
||||
Begin or resume playback.
|
||||
bool audio :: play()
|
||||
bool audio :: pause()
|
||||
Change the gstreamer state to either GST_STATE_PLAYING or
|
||||
GST_STATE_PAUSED. Return true on success and false otherwise.
|
||||
|
||||
void audio :: pause()
|
||||
Pause playback.
|
||||
|
||||
void audio :: seek_to(int)
|
||||
Seek to a position X seconds into the track
|
||||
bool audio :: seek_to(int)
|
||||
Seek to a position X seconds into the track. Return true if
|
||||
a track is loaded and the seek isn't out of bounds. False
|
||||
otherwise.
|
||||
|
||||
void audio :: stop()
|
||||
pause()
|
||||
seek_to(0)
|
||||
|
||||
void audio :: next()
|
||||
Call the playlist :: next() function to get the next trackid,
|
||||
bool audio :: next()
|
||||
Call the deck :: next() function to get the next trackid,
|
||||
and load that file into the gstreamer pipeline. Do not change
|
||||
the state of the pipeline (if nothing is playing yet, don't
|
||||
call play()).
|
||||
call play()). Return true if a track has been loaded into the
|
||||
pipeline, false otherwise.
|
||||
|
||||
void audio :: previous()
|
||||
Call the playlist :: previous() function to iterate backwards
|
||||
|
@ -65,7 +69,9 @@ Audio: (lib/audio.cpp)
|
|||
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.
|
||||
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.
|
||||
|
||||
unsigned int audio :: pause_count()
|
||||
Return the number of tracks that will be played before
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*/
|
||||
#ifndef OCARINA_AUDIO_H
|
||||
#define OCARINA_AUDIO_H
|
||||
|
||||
extern "C" {
|
||||
#include <gst/gst.h>
|
||||
}
|
||||
|
||||
namespace audio
|
||||
{
|
||||
|
||||
void init(int *, char ***);
|
||||
|
||||
bool play();
|
||||
bool pause();
|
||||
bool seek_to(double);
|
||||
bool next();
|
||||
|
||||
};
|
||||
|
||||
#endif /* OCARINA_AUDIO_H */
|
|
@ -15,6 +15,7 @@ modules = {
|
|||
# #
|
||||
###########################
|
||||
|
||||
"AUDIO" : Module("audio.cpp", package = "gstreamer-1.0", depends = [ "DECK", "LIBRARY" ]),
|
||||
"DATABASE" : Module("database.cpp", depends = [ "FILE" ]),
|
||||
"DECK" : Module("deck.cpp", depends = [ "PLAYLIST" ]),
|
||||
"FILE" : Module("file.cpp", package = "glib-2.0"),
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <audio.h>
|
||||
#include <deck.h>
|
||||
#include <library.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static GstElement *ocarina_player;
|
||||
static bool track_loaded = false;
|
||||
|
||||
static gboolean on_message(GstBus *bus, GstMessage *message, gpointer data)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static bool change_state(GstState state)
|
||||
{
|
||||
GstStateChangeReturn ret;
|
||||
|
||||
ret = gst_element_set_state(GST_ELEMENT(ocarina_player), state);
|
||||
switch (ret) {
|
||||
case GST_STATE_CHANGE_SUCCESS:
|
||||
case GST_STATE_CHANGE_ASYNC:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool load_song(library :: Song &song)
|
||||
{
|
||||
GstState state;
|
||||
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);
|
||||
|
||||
escaped = gst_filename_to_uri(filepath.c_str(), NULL);
|
||||
g_object_set(G_OBJECT(ocarina_player), "uri", escaped, NULL);
|
||||
g_free(escaped);
|
||||
|
||||
return change_state(state);
|
||||
}
|
||||
|
||||
void audio :: init(int *argc, char ***argv)
|
||||
{
|
||||
GstBus *bus;
|
||||
|
||||
gst_init(argc, argv);
|
||||
|
||||
ocarina_player = gst_element_factory_make("playbin", "ocarina_player");
|
||||
bus = gst_pipeline_get_bus(GST_PIPELINE(ocarina_player));
|
||||
|
||||
gst_bus_add_watch(bus, on_message, NULL);
|
||||
}
|
||||
|
||||
bool audio :: play()
|
||||
{
|
||||
if (track_loaded == false)
|
||||
return false;
|
||||
return change_state(GST_STATE_PLAYING);
|
||||
}
|
||||
|
||||
bool audio :: pause()
|
||||
{
|
||||
if (track_loaded == false)
|
||||
return false;
|
||||
return change_state(GST_STATE_PAUSED);
|
||||
}
|
||||
|
||||
bool audio :: seek_to(double pos)
|
||||
{
|
||||
if (track_loaded == false)
|
||||
return false;
|
||||
return gst_element_seek_simple(GST_ELEMENT(ocarina_player),
|
||||
GST_FORMAT_TIME,
|
||||
GST_SEEK_FLAG_FLUSH,
|
||||
pos);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool audio :: next()
|
||||
{
|
||||
unsigned int id;
|
||||
library :: Song song;
|
||||
|
||||
try {
|
||||
id = deck :: next();
|
||||
if (library :: lookup(id, &song) == false)
|
||||
track_loaded = false;
|
||||
else
|
||||
track_loaded = load_song(song);
|
||||
} catch (int) {
|
||||
track_loaded = false;
|
||||
}
|
||||
return track_loaded;
|
||||
}
|
|
@ -57,8 +57,8 @@ rm_test_dir(xdg.BaseDirectory.xdg_data_home);
|
|||
#
|
||||
# Read SConscript files
|
||||
#
|
||||
scripts = [ "database", "deck", "file", "filter", "group", "idle", "index",
|
||||
"library", "playlist", "print" ]
|
||||
scripts = [ "audio", "database", "deck", "file", "filter", "group", "idle",
|
||||
"index", "library", "playlist", "print" ]
|
||||
for s in scripts:
|
||||
CONFIG.reset(TEST = True)
|
||||
SConscript("%s/Sconscript" % s)
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
#!/usr/bin/python
|
||||
Import("Test", "CONFIG")
|
||||
|
||||
CONFIG.AUDIO = True
|
||||
|
||||
Test("audio", "audio.cpp")
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <audio.h>
|
||||
#include <deck.h>
|
||||
#include <library.h>
|
||||
#include <print.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
|
||||
void gen_library()
|
||||
{
|
||||
system("tests/library/gen_library.sh");
|
||||
print("\n");
|
||||
}
|
||||
|
||||
void check_ret(const std :: string &test, bool ret, bool expected)
|
||||
{
|
||||
print("Test %s: ", test.c_str());
|
||||
if (ret == expected)
|
||||
print("Success!\n");
|
||||
else
|
||||
print("Failed.\n");
|
||||
}
|
||||
|
||||
/* Call various functions without a track loaded */
|
||||
void test_0()
|
||||
{
|
||||
check_ret("0a", audio :: play(), false);
|
||||
check_ret("0b", audio :: pause(), false);
|
||||
check_ret("0c", audio :: seek_to(10), false);
|
||||
check_ret("0d", audio :: next(), false);
|
||||
print("\n");
|
||||
}
|
||||
|
||||
void test_1()
|
||||
{
|
||||
check_ret("1a", audio :: next(), true);
|
||||
check_ret("1b", audio :: play(), true);
|
||||
check_ret("1c", audio :: pause(), true);
|
||||
check_ret("1c", audio :: seek_to(10), true);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
Playlist *plist;
|
||||
|
||||
gen_library();
|
||||
|
||||
/* Initialize before testing */
|
||||
audio :: init(&argc, &argv);
|
||||
test_0();
|
||||
|
||||
/* Read in library, set up a playlist */
|
||||
library :: add_path("/tmp/library/0");
|
||||
plist = deck :: create();
|
||||
for (unsigned int i = 0; i < 150; i++)
|
||||
plist->add(i);
|
||||
|
||||
test_1();
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue