Add Audio::Stereo_out frontend to Audio_out service
Add a frontend for the Audio_out service and convert the audio_out test to use this utility. Abstracting the service and packet streams allows the service to be refactored without adjusting native application code. Fix #3435
This commit is contained in:
parent
11d2a2957a
commit
ac853252c3
|
@ -62,6 +62,9 @@ append_if $use_mixer config {
|
||||||
<start name="mixer">
|
<start name="mixer">
|
||||||
<resource name="RAM" quantum="2M"/>
|
<resource name="RAM" quantum="2M"/>
|
||||||
<provides><service name="Audio_out"/></provides>
|
<provides><service name="Audio_out"/></provides>
|
||||||
|
<config>
|
||||||
|
<default out_volume="75" volume="25" muted="0"/>
|
||||||
|
</config>
|
||||||
<route>
|
<route>
|
||||||
<service name="Audio_out"> <child name="audio_drv"/> </service>
|
<service name="Audio_out"> <child name="audio_drv"/> </service>
|
||||||
<any-service> <parent/> <any-child/> </any-service>
|
<any-service> <parent/> <any-child/> </any-service>
|
||||||
|
@ -130,4 +133,4 @@ append qemu_args " -nographic -soundhw es1370 "
|
||||||
# For obvious reasons the timeout depends on the total
|
# For obvious reasons the timeout depends on the total
|
||||||
# length of the used sample file.
|
# length of the used sample file.
|
||||||
#
|
#
|
||||||
run_genode_until {.*played.*1 time\(s\)} 60
|
run_genode_until {.*played.*1 time\(s\)} 300
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
* \brief Audio session producer class
|
||||||
|
* \author Emery Hemingway
|
||||||
|
* \date 2019-06-13
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2019 Genode Labs GmbH
|
||||||
|
*
|
||||||
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
|
* under the terms of the GNU Affero General Public License version 3.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <audio_out_session/connection.h>
|
||||||
|
|
||||||
|
namespace Audio
|
||||||
|
{
|
||||||
|
struct Source;
|
||||||
|
class Stereo_out;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct Audio::Source
|
||||||
|
{
|
||||||
|
virtual ~Source() { }
|
||||||
|
virtual bool fill(float *left, float *right, Genode::size_t samples) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Audio::Stereo_out
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
Audio::Source &_source;
|
||||||
|
|
||||||
|
Audio_out::Connection _left, _right;
|
||||||
|
|
||||||
|
Genode::Io_signal_handler<Stereo_out> _progress_handler;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Stereo_out(Genode::Env &env, Audio::Source &source)
|
||||||
|
: _source(source)
|
||||||
|
, _left (env, "left", false, false)
|
||||||
|
, _right(env, "right", false, false)
|
||||||
|
, _progress_handler(env.ep(), *this, &Stereo_out::progress)
|
||||||
|
{
|
||||||
|
_left.progress_sigh(_progress_handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void start()
|
||||||
|
{
|
||||||
|
_left.start();
|
||||||
|
_right.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop()
|
||||||
|
{
|
||||||
|
_left.stop();
|
||||||
|
_right.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void progress()
|
||||||
|
{
|
||||||
|
using namespace Audio_out;
|
||||||
|
|
||||||
|
while (!_left.stream()->full()) {
|
||||||
|
Packet *left = _left.stream()->alloc();
|
||||||
|
unsigned pos = _left.stream()->packet_position(left);
|
||||||
|
Packet *right = _right.stream()->get(pos);
|
||||||
|
|
||||||
|
if (!_source.fill(left->content(), right->content(), PERIOD))
|
||||||
|
break;
|
||||||
|
|
||||||
|
_left.submit(left);
|
||||||
|
_right.submit(right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
|
@ -15,7 +15,7 @@
|
||||||
* under the terms of the GNU Affero General Public License version 3.
|
* under the terms of the GNU Affero General Public License version 3.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <audio_out_session/connection.h>
|
#include <audio/source.h>
|
||||||
#include <base/attached_rom_dataspace.h>
|
#include <base/attached_rom_dataspace.h>
|
||||||
#include <base/component.h>
|
#include <base/component.h>
|
||||||
#include <base/heap.h>
|
#include <base/heap.h>
|
||||||
|
@ -27,111 +27,81 @@ using Filename = Genode::String<64>;
|
||||||
using namespace Genode;
|
using namespace Genode;
|
||||||
using namespace Audio_out;
|
using namespace Audio_out;
|
||||||
|
|
||||||
static constexpr bool const verbose = false;
|
static constexpr bool const verbose = true;
|
||||||
static constexpr char const * channel_names[2] = { "front left", "front right" };
|
|
||||||
|
|
||||||
|
|
||||||
class Track : public Thread
|
struct Track final : Audio::Source
|
||||||
{
|
{
|
||||||
private:
|
/*
|
||||||
|
* Noncopyable
|
||||||
|
*/
|
||||||
|
Track(Track const &);
|
||||||
|
Track &operator = (Track const &);
|
||||||
|
|
||||||
|
enum {
|
||||||
|
CHN_CNT = 2, /* number of channels */
|
||||||
|
FRAME_SIZE = sizeof(float),
|
||||||
|
PERIOD_CSIZE = FRAME_SIZE * PERIOD, /* size of channel packet (bytes) */
|
||||||
|
PERIOD_FSIZE = CHN_CNT * PERIOD_CSIZE, /* size of period in file (bytes) */
|
||||||
|
};
|
||||||
|
|
||||||
|
Env & _env;
|
||||||
|
|
||||||
|
Filename const & _name;
|
||||||
|
|
||||||
|
Attached_rom_dataspace _sample_ds { _env, _name.string() };
|
||||||
|
|
||||||
|
char const * const _base { _sample_ds.local_addr<char const>() };
|
||||||
|
size_t const _size { _sample_ds.size() };
|
||||||
|
size_t _offset { 0 };
|
||||||
|
|
||||||
|
Audio::Stereo_out _stereo_out { _env, *this };
|
||||||
|
|
||||||
|
bool fill(float *left, float *right, Genode::size_t /*samples*/) override
|
||||||
|
{
|
||||||
|
if (_size <= _offset) {
|
||||||
|
_stereo_out.stop();
|
||||||
|
log("played '", _name, "' 1 time(s)");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Noncopyable
|
* The current chunk (in number of frames of one channel)
|
||||||
|
* is the size of the period except at the end of the
|
||||||
|
* file.
|
||||||
*/
|
*/
|
||||||
Track(Track const &);
|
size_t chunk = (_offset + PERIOD_FSIZE > _size)
|
||||||
Track &operator = (Track const &);
|
? (_size - _offset) / CHN_CNT / FRAME_SIZE
|
||||||
|
: PERIOD;
|
||||||
|
|
||||||
enum {
|
/* copy channel contents into sessions */
|
||||||
CHN_CNT = 2, /* number of channels */
|
float *content = (float *)(_base + _offset);
|
||||||
FRAME_SIZE = sizeof(float),
|
for (unsigned c = 0; c < CHN_CNT * chunk; c += CHN_CNT) {
|
||||||
PERIOD_CSIZE = FRAME_SIZE * PERIOD, /* size of channel packet (bytes) */
|
left[c / 2] = content[c + 0];
|
||||||
PERIOD_FSIZE = CHN_CNT * PERIOD_CSIZE, /* size of period in file (bytes) */
|
right[c / 2] = content[c + 1];
|
||||||
};
|
|
||||||
|
|
||||||
Env & _env;
|
|
||||||
Constructible<Audio_out::Connection> _audio_out[CHN_CNT];
|
|
||||||
|
|
||||||
Filename const & _name;
|
|
||||||
|
|
||||||
Attached_rom_dataspace _sample_ds { _env, _name.string() };
|
|
||||||
char const * const _base = _sample_ds.local_addr<char const>();
|
|
||||||
size_t const _size = _sample_ds.size();
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Track(Env & env, Filename const & name)
|
|
||||||
: Thread(env, "track", sizeof(size_t)*2048), _env(env), _name(name)
|
|
||||||
{
|
|
||||||
/* allocation signal for first channel only */
|
|
||||||
for (int i = 0; i < CHN_CNT; ++i)
|
|
||||||
_audio_out[i].construct(env, channel_names[i], i == 0);
|
|
||||||
|
|
||||||
start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void entry() override
|
/* handle last packet gracefully */
|
||||||
{
|
if (chunk < PERIOD) {
|
||||||
if (verbose)
|
memset( left + chunk, 0, PERIOD_CSIZE - FRAME_SIZE * chunk);
|
||||||
log(_name, " size is ", _size, " bytes "
|
memset(right + chunk, 0, PERIOD_CSIZE - FRAME_SIZE * chunk);
|
||||||
"(attached to ", (void *)_base, ")");
|
|
||||||
|
|
||||||
for (int i = 0; i < CHN_CNT; ++i)
|
|
||||||
_audio_out[i]->start();
|
|
||||||
|
|
||||||
unsigned cnt = 0;
|
|
||||||
while (1) {
|
|
||||||
|
|
||||||
for (size_t offset = 0, cnt = 1;
|
|
||||||
offset < _size;
|
|
||||||
offset += PERIOD_FSIZE, ++cnt) {
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The current chunk (in number of frames of one channel)
|
|
||||||
* is the size of the period except at the end of the
|
|
||||||
* file.
|
|
||||||
*/
|
|
||||||
size_t chunk = (offset + PERIOD_FSIZE > _size)
|
|
||||||
? (_size - offset) / CHN_CNT / FRAME_SIZE
|
|
||||||
: PERIOD;
|
|
||||||
|
|
||||||
Packet *p[CHN_CNT];
|
|
||||||
while (1)
|
|
||||||
try {
|
|
||||||
p[0] = _audio_out[0]->stream()->alloc();
|
|
||||||
break;
|
|
||||||
} catch (Audio_out::Stream::Alloc_failed) {
|
|
||||||
_audio_out[0]->wait_for_alloc();
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned pos = _audio_out[0]->stream()->packet_position(p[0]);
|
|
||||||
/* sync other channels with first one */
|
|
||||||
for (int chn = 1; chn < CHN_CNT; ++chn)
|
|
||||||
p[chn] = _audio_out[chn]->stream()->get(pos);
|
|
||||||
|
|
||||||
/* copy channel contents into sessions */
|
|
||||||
float *content = (float *)(_base + offset);
|
|
||||||
for (unsigned c = 0; c < CHN_CNT * chunk; c += CHN_CNT)
|
|
||||||
for (int i = 0; i < CHN_CNT; ++i)
|
|
||||||
p[i]->content()[c / 2] = content[c + i];
|
|
||||||
|
|
||||||
/* handle last packet gracefully */
|
|
||||||
if (chunk < PERIOD) {
|
|
||||||
for (int i = 0; i < CHN_CNT; ++i)
|
|
||||||
memset(p[i]->content() + chunk,
|
|
||||||
0, PERIOD_CSIZE - FRAME_SIZE * chunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (verbose)
|
|
||||||
log(_name, " submit packet ",
|
|
||||||
_audio_out[0]->stream()->packet_position((p[0])));
|
|
||||||
|
|
||||||
for (int i = 0; i < CHN_CNT; i++)
|
|
||||||
_audio_out[i]->submit(p[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
log("played '", _name, "' ", ++cnt, " time(s)");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_offset += PERIOD_FSIZE;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Track(Env & env, Filename const & name)
|
||||||
|
: _env(env), _name(name)
|
||||||
|
{
|
||||||
|
if (verbose)
|
||||||
|
log(_name, " size is ", _size, " bytes "
|
||||||
|
"(attached to ", (void *)_base, ")");
|
||||||
|
|
||||||
|
_stereo_out.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
~Track() { }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue