/*
* \brief Audio player
* \author Josef Soentgen
* \date 2015-11-19
*/
/*
* Copyright (C) 2015-2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include
#include
#include
#include
#include
#include
#include
#include
/* local includes */
#include
#include
extern "C" {
/*
* UINT64_C is needed by libav headers
*
* Use the compiler's definition as fallback because the UINT64_C macro is only
* defined in when used with C.
*/
#ifndef UINT64_C
#define UINT64_C(c) __UINT64_C(c)
#endif
#include
#include
#include
#include
#include
#include
#include
#include
}; /* extern "C" */
namespace Audio_player {
class Output;
class Playlist;
class Decoder;
struct Main;
typedef Util::Ring_buffer<64 * 1024> Frame_data;
typedef Genode::String<1024> Path;
enum { LEFT, RIGHT, NUM_CHANNELS };
enum { AUDIO_OUT_PACKET_SIZE = Audio_out::PERIOD * NUM_CHANNELS * sizeof(float) };
enum { QUEUED_PACKET_THRESHOLD = 10 };
}
struct Audio_player::Output
{
Audio_out::Connection _left;
Audio_out::Connection _right;
Audio_out::Connection *_out[NUM_CHANNELS];
Audio_out::Packet *_alloc_position;
unsigned _packets_submitted = 0;
template
static void for_each_channel(FUNC const &func) {
for (int i = 0; i < Audio_player::NUM_CHANNELS; i++) func(i); }
/**
* Constructor
*
* \param sigh progress signal handler
*/
Output(Genode::Env &env,
Genode::Signal_context_capability sigh)
: _left(env, "left", true, true), _right(env, "right", false, false)
{
/*
* We only care about the left (first) channel and sync all other
* channels with it when needed
*/
_left.progress_sigh(sigh);
_out[LEFT] = &_left;
_out[RIGHT] = &_right;
}
void start()
{
for_each_channel([&] (int const i) { _out[i]->start(); });
}
void stop()
{
for_each_channel([&] (int const i) { _out[i]->stop(); });
_alloc_position = nullptr;
}
/**
* Return rounded estimate of packets per secound
*/
unsigned packets_per_sec() const { return Audio_out::SAMPLE_RATE / Audio_out::PERIOD; }
/**
* Return the size of one output frame
*/
size_t frame_size() const { return AUDIO_OUT_PACKET_SIZE; }
/**
* Return number of currently queued packets in Audio_out stream
*/
unsigned queued()
{
if (_alloc_position == nullptr) _alloc_position = _out[LEFT]->stream()->next();
unsigned const packet_pos = _out[LEFT]->stream()->packet_position(_alloc_position);
unsigned const play_pos = _out[LEFT]->stream()->pos();
unsigned const queued = packet_pos < play_pos
? ((Audio_out::QUEUE_SIZE + packet_pos) - play_pos)
: packet_pos - play_pos;
return queued;
}
/**
* Fetch decoded frames from frame data buffer and fill Audio_out packets
*/
void drain_buffer(Frame_data &frame_data)
{
if (_alloc_position == nullptr) _alloc_position = _out[LEFT]->stream()->next();
while (frame_data.read_avail() > (AUDIO_OUT_PACKET_SIZE)) {
Audio_out::Packet *p[NUM_CHANNELS];
p[LEFT] = _out[LEFT]->stream()->next(_alloc_position);
unsigned const ppos = _out[LEFT]->stream()->packet_position(p[LEFT]);
p[RIGHT] = _out[RIGHT]->stream()->get(ppos);
float tmp[Audio_out::PERIOD * NUM_CHANNELS];
size_t const n = frame_data.read(tmp, sizeof(tmp));
if (n != sizeof(tmp)) {
Genode::warning("less frame data read than expected");
}
float *left_content = p[LEFT]->content();
float *right_content = p[RIGHT]->content();
for (unsigned i = 0; i < Audio_out::PERIOD; i++) {
left_content[i] = tmp[i * NUM_CHANNELS + LEFT];
right_content[i] = tmp[i * NUM_CHANNELS + RIGHT];
}
for_each_channel([&] (int const i) { _out[i]->submit(p[i]); });
_alloc_position = p[LEFT];
_packets_submitted++;
}
}
/**
* Return number of already submitted Audio_out packets
*/
unsigned packets_submitted() { return _packets_submitted; }
};
class Audio_player::Playlist
{
public:
struct Track : public Util::List