151 lines
3.8 KiB
C++
151 lines
3.8 KiB
C++
/*
|
|
* \brief Audio-out test implementation
|
|
* \author Sebastian Sumpf
|
|
* \author Christian Helmuth
|
|
* \date 2009-12-03
|
|
*
|
|
* The test program plays several tracks simultaneously to the Audio_out
|
|
* service. See README for the configuration.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2009-2017 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.
|
|
*/
|
|
|
|
#include <audio_out_session/connection.h>
|
|
#include <base/attached_rom_dataspace.h>
|
|
#include <base/component.h>
|
|
#include <base/heap.h>
|
|
#include <base/log.h>
|
|
#include <dataspace/client.h>
|
|
#include <input_session/connection.h>
|
|
#include <input/event.h>
|
|
|
|
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" };
|
|
|
|
|
|
class Click
|
|
{
|
|
private:
|
|
|
|
/*
|
|
* Noncopyable
|
|
*/
|
|
Click(Click const &);
|
|
Click &operator = (Click const &);
|
|
|
|
enum {
|
|
CHANNELS = 2, /* number of channels */
|
|
FRAME_SIZE = sizeof(float),
|
|
PERIOD_CSIZE = FRAME_SIZE * PERIOD, /* size of channel packet (bytes) */
|
|
PERIOD_FSIZE = CHANNELS * PERIOD_CSIZE, /* size of period in file (bytes) */
|
|
};
|
|
|
|
Env & _env;
|
|
Constructible<Audio_out::Connection> _audio_out[CHANNELS];
|
|
|
|
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:
|
|
|
|
Click(Env & env, Filename const & name)
|
|
: _env(env), _name(name)
|
|
{
|
|
for (int i = 0; i < CHANNELS; ++i) {
|
|
/* allocation signal for first channel only */
|
|
_audio_out[i].construct(env, channel_names[i], i == 0);
|
|
_audio_out[i]->start();
|
|
}
|
|
}
|
|
|
|
void play()
|
|
{
|
|
log("play click");
|
|
|
|
for (int i = 0; i < CHANNELS; i++)
|
|
_audio_out[i]->stream()->reset();
|
|
|
|
for (Genode::size_t offset = 0; offset < _size; offset += PERIOD_FSIZE) {
|
|
|
|
/*
|
|
* 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) / CHANNELS / FRAME_SIZE
|
|
: PERIOD;
|
|
|
|
Packet *p[CHANNELS];
|
|
|
|
while (true)
|
|
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]);
|
|
for (int chn = 1; chn < CHANNELS; ++chn)
|
|
p[chn] = _audio_out[chn]->stream()->get(pos);
|
|
|
|
/* copy channel contents into sessions */
|
|
float *content = (float *)(_base + offset);
|
|
for (unsigned c = 0; c < CHANNELS * chunk; c += CHANNELS)
|
|
for (int i = 0; i < CHANNELS; ++i)
|
|
p[i]->content()[c / 2] = content[c + i];
|
|
|
|
/* handle last packet gracefully */
|
|
if (chunk < PERIOD) {
|
|
for (int i = 0; i < CHANNELS; ++i)
|
|
memset(p[i]->content() + chunk,
|
|
0, PERIOD_CSIZE - FRAME_SIZE * chunk);
|
|
}
|
|
|
|
for (int i = 0; i < CHANNELS; i++)
|
|
_audio_out[i]->submit(p[i]);
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
struct Main
|
|
{
|
|
Env & _env;
|
|
Signal_handler<Main> _handler { _env.ep(), *this, &Main::_handle };
|
|
Input::Connection _input { _env };
|
|
Attached_dataspace _ev_ds { _env.rm(), _input.dataspace() };
|
|
Filename const _name { "click.raw" };
|
|
Click _click { _env, _name };
|
|
|
|
void _handle()
|
|
{
|
|
_input.for_each_event([&] (Input::Event const &ev) {
|
|
if (ev.press())
|
|
_click.play(); });
|
|
}
|
|
|
|
Main(Env & env) : _env(env)
|
|
{
|
|
log("--- Audio_out click test ---");
|
|
|
|
_input.sigh(_handler);
|
|
}
|
|
};
|
|
|
|
|
|
void Component::construct(Env & env) { static Main main(env); }
|