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">
|
||||
<resource name="RAM" quantum="2M"/>
|
||||
<provides><service name="Audio_out"/></provides>
|
||||
<config>
|
||||
<default out_volume="75" volume="25" muted="0"/>
|
||||
</config>
|
||||
<route>
|
||||
<service name="Audio_out"> <child name="audio_drv"/> </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
|
||||
# length of the used sample file.
|
||||
#
|
||||
run_genode_until {.*played.*1 time\(s\)} 60
|
||||
run_genode_until {.*played.*1 time\(s\)} 300
|
||||
|
|
81
repos/os/include/audio/source.h
Normal file
81
repos/os/include/audio/source.h
Normal file
|
@ -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.
|
||||
*/
|
||||
|
||||
#include <audio_out_session/connection.h>
|
||||
#include <audio/source.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <base/component.h>
|
||||
#include <base/heap.h>
|
||||
|
@ -27,111 +27,81 @@ using Filename = Genode::String<64>;
|
|||
using namespace Genode;
|
||||
using namespace Audio_out;
|
||||
|
||||
static constexpr bool const verbose = false;
|
||||
static constexpr char const * channel_names[2] = { "front left", "front right" };
|
||||
static constexpr bool const verbose = true;
|
||||
|
||||
|
||||
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 &);
|
||||
Track &operator = (Track const &);
|
||||
size_t chunk = (_offset + PERIOD_FSIZE > _size)
|
||||
? (_size - _offset) / CHN_CNT / FRAME_SIZE
|
||||
: PERIOD;
|
||||
|
||||
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;
|
||||
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();
|
||||
/* copy channel contents into sessions */
|
||||
float *content = (float *)(_base + _offset);
|
||||
for (unsigned c = 0; c < CHN_CNT * chunk; c += CHN_CNT) {
|
||||
left[c / 2] = content[c + 0];
|
||||
right[c / 2] = content[c + 1];
|
||||
}
|
||||
|
||||
void entry() override
|
||||
{
|
||||
if (verbose)
|
||||
log(_name, " size is ", _size, " bytes "
|
||||
"(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)");
|
||||
}
|
||||
/* handle last packet gracefully */
|
||||
if (chunk < PERIOD) {
|
||||
memset( left + chunk, 0, PERIOD_CSIZE - FRAME_SIZE * chunk);
|
||||
memset(right + chunk, 0, PERIOD_CSIZE - FRAME_SIZE * chunk);
|
||||
}
|
||||
|
||||
_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
Block a user