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" ])
|
if self.TEST: env.Append( CCFLAGS = [ "-DCONFIG_TEST" ])
|
||||||
|
|
||||||
def reset(self, TEST = False):
|
def reset(self, TEST = False):
|
||||||
|
self.AUDIO = False
|
||||||
self.DATABASE = False
|
self.DATABASE = False
|
||||||
self.DECK = False
|
self.DECK = False
|
||||||
self.FILE = False
|
self.FILE = False
|
||||||
|
|
|
@ -13,11 +13,12 @@ Audio: (lib/audio.cpp)
|
||||||
namespace that will be easier to work with than using raw gstreamer
|
namespace that will be easier to work with than using raw gstreamer
|
||||||
functions.
|
functions.
|
||||||
|
|
||||||
The audio layer will also control the "pause after N tracks" feature
|
The audio layer is meant to be an interface used by the front end to
|
||||||
so songs can be loaded without neeting to pass in a "begin playback"
|
control most features of the backend library. This means implementing
|
||||||
flag every time. The remaining tracks counter will only be decremented
|
next track, previous track, and so on here.
|
||||||
when a song finishes through the end-of-stream message passed by the
|
|
||||||
gst pipeline.
|
Gstreamer options passed to audio :: init() can be found by running
|
||||||
|
`gst-inspect-1.0 --help-gst` on the command line.
|
||||||
|
|
||||||
- Internal:
|
- Internal:
|
||||||
Set up a message bus to look for end-of-stream and error messages so
|
Set up a message bus to look for end-of-stream and error messages so
|
||||||
|
@ -28,27 +29,30 @@ Audio: (lib/audio.cpp)
|
||||||
- API:
|
- API:
|
||||||
void audio :: init(argc, argv)
|
void audio :: init(argc, argv)
|
||||||
Initialize the gstreamer layer and reload the track that was
|
Initialize the gstreamer layer and reload the track that was
|
||||||
last loaded before shutdown. Please only pass --gst-* options
|
last loaded before shutdown. Gstreamer is supposed to remove
|
||||||
for argv.
|
options from the argv array as they are processed, so pass
|
||||||
|
pointers to argc and argv to this function.
|
||||||
|
|
||||||
void audio :: play()
|
bool audio :: play()
|
||||||
Begin or resume playback.
|
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()
|
bool audio :: seek_to(int)
|
||||||
Pause playback.
|
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
|
||||||
void audio :: seek_to(int)
|
otherwise.
|
||||||
Seek to a position X seconds into the track
|
|
||||||
|
|
||||||
void audio :: stop()
|
void audio :: stop()
|
||||||
pause()
|
pause()
|
||||||
seek_to(0)
|
seek_to(0)
|
||||||
|
|
||||||
void audio :: next()
|
bool audio :: next()
|
||||||
Call the playlist :: next() function to get the next trackid,
|
Call the deck :: next() function to get the next trackid,
|
||||||
and load that file into the gstreamer pipeline. Do not change
|
and load that file into the gstreamer pipeline. Do not change
|
||||||
the state of the pipeline (if nothing is playing yet, don't
|
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()
|
void audio :: previous()
|
||||||
Call the playlist :: previous() function to iterate backwards
|
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.
|
Return the duration of the current song in seconds.
|
||||||
|
|
||||||
void audio :: pause_after(unsigned int)
|
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()
|
unsigned int audio :: pause_count()
|
||||||
Return the number of tracks that will be played before
|
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" ]),
|
"DATABASE" : Module("database.cpp", depends = [ "FILE" ]),
|
||||||
"DECK" : Module("deck.cpp", depends = [ "PLAYLIST" ]),
|
"DECK" : Module("deck.cpp", depends = [ "PLAYLIST" ]),
|
||||||
"FILE" : Module("file.cpp", package = "glib-2.0"),
|
"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
|
# Read SConscript files
|
||||||
#
|
#
|
||||||
scripts = [ "database", "deck", "file", "filter", "group", "idle", "index",
|
scripts = [ "audio", "database", "deck", "file", "filter", "group", "idle",
|
||||||
"library", "playlist", "print" ]
|
"index", "library", "playlist", "print" ]
|
||||||
for s in scripts:
|
for s in scripts:
|
||||||
CONFIG.reset(TEST = True)
|
CONFIG.reset(TEST = True)
|
||||||
SConscript("%s/Sconscript" % s)
|
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