Audio: Adjust components to new audio interface
OSS driver, ALSA driver, audio-out test, avplay, and mixer Issue #602
This commit is contained in:
parent
238d6a29c5
commit
47bb48bdd6
|
@ -22,6 +22,6 @@ int audio_init();
|
||||||
/**
|
/**
|
||||||
* Play data of size
|
* Play data of size
|
||||||
*/
|
*/
|
||||||
int audio_play(short *data, int size);
|
int audio_play(short *data, unsigned size);
|
||||||
|
|
||||||
#endif /* _INCLUDE__AUDIO_H_ */
|
#endif /* _INCLUDE__AUDIO_H_ */
|
||||||
|
|
|
@ -196,7 +196,7 @@ enum {
|
||||||
#define GET_PROCESS_PID(p) -1
|
#define GET_PROCESS_PID(p) -1
|
||||||
|
|
||||||
#define KERNEL_MALLOC(size) (dde_kit_large_malloc(size))
|
#define KERNEL_MALLOC(size) (dde_kit_large_malloc(size))
|
||||||
#define KERNEL_FREE(ptr) (dde_kit_large_free(ptr))
|
#define KERNEL_FREE(ptr) (dde_kit_large_free(ptr))
|
||||||
|
|
||||||
void * dma_alloc(oss_native_word *phys, size_t size);
|
void * dma_alloc(oss_native_word *phys, size_t size);
|
||||||
#define CONTIG_MALLOC(osdev, sz, memlimit, phaddr, handle) dma_alloc(phaddr, sz)
|
#define CONTIG_MALLOC(osdev, sz, memlimit, phaddr, handle) dma_alloc(phaddr, sz)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* \brief Audio-session-entry point
|
* \brief Audio-out session entry point
|
||||||
* \author Sebastian Sumpf
|
* \author Sebastian Sumpf
|
||||||
* \date 2012-11-20
|
* \date 2012-11-20
|
||||||
*/
|
*/
|
||||||
|
@ -19,7 +19,7 @@ extern "C" {
|
||||||
#include <base/sleep.h>
|
#include <base/sleep.h>
|
||||||
#include <root/component.h>
|
#include <root/component.h>
|
||||||
#include <cap_session/connection.h>
|
#include <cap_session/connection.h>
|
||||||
#include <audio_out_session/rpc_object.h>
|
#include <audio_session/rpc_object.h>
|
||||||
#include <util/misc_math.h>
|
#include <util/misc_math.h>
|
||||||
|
|
||||||
#include <audio.h>
|
#include <audio.h>
|
||||||
|
@ -27,202 +27,253 @@ extern "C" {
|
||||||
|
|
||||||
using namespace Genode;
|
using namespace Genode;
|
||||||
|
|
||||||
|
|
||||||
static const bool verbose = false;
|
static const bool verbose = false;
|
||||||
static bool audio_out_active = false;
|
static bool audio_out_active = false;
|
||||||
|
|
||||||
|
enum Channel_number { LEFT, RIGHT, MAX_CHANNELS, INVALID = MAX_CHANNELS };
|
||||||
|
|
||||||
|
|
||||||
namespace Audio_out {
|
namespace Audio_out {
|
||||||
|
class Session_component;
|
||||||
class Session_component;
|
class Out;
|
||||||
|
class Root;
|
||||||
enum Channel_number { LEFT, RIGHT, MAX_CHANNELS, INVALID = MAX_CHANNELS };
|
struct Root_policy;
|
||||||
|
|
||||||
static Session_component *channel_acquired[MAX_CHANNELS];
|
static Session_component *channel_acquired[MAX_CHANNELS];
|
||||||
|
}
|
||||||
|
|
||||||
class Session_component : public Session_rpc_object
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
Ram_dataspace_capability _ds;
|
class Audio_out::Session_component : public Audio_out::Session_rpc_object
|
||||||
Channel_number _channel;
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
Signal_dispatcher<Session_component> _process_packet_dispatcher;
|
Channel_number _channel;
|
||||||
|
Signal_context_capability _ctx_cap;
|
||||||
|
Signal_transmitter _signal;
|
||||||
|
|
||||||
Ram_dataspace_capability _alloc_dataspace(size_t size)
|
public:
|
||||||
{
|
|
||||||
_ds = env()->ram_session()->alloc(size);
|
|
||||||
return _ds;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _process_packets(unsigned)
|
Session_component(Channel_number channel, Signal_context_capability ctx_cap)
|
||||||
{
|
: Session_rpc_object(ctx_cap), _channel(channel), _ctx_cap(ctx_cap)
|
||||||
/* handle audio-out packets */
|
{
|
||||||
Session_component *left = channel_acquired[LEFT],
|
Audio_out::channel_acquired[_channel] = this;
|
||||||
*right = channel_acquired[RIGHT];
|
_signal.context(ctx_cap);
|
||||||
while (left->channel()->packet_avail() &&
|
}
|
||||||
right->channel()->packet_avail() &&
|
|
||||||
left->channel()->ready_to_ack() &&
|
|
||||||
right->channel()->ready_to_ack()) {
|
|
||||||
|
|
||||||
/* get packets for channels */
|
~Session_component()
|
||||||
Packet_descriptor p[MAX_CHANNELS];
|
{
|
||||||
static short data[2 * PERIOD];
|
Audio_out::channel_acquired[_channel] = 0;
|
||||||
p[LEFT] = left->channel()->get_packet();
|
}
|
||||||
p[RIGHT] = right->channel()->get_packet();
|
|
||||||
|
|
||||||
/* convert float to S16LE */
|
void start()
|
||||||
for (int i = 0; i < 2 * PERIOD; i += 2) {
|
{
|
||||||
data[i] = left->channel()->packet_content(p[LEFT])[i / 2] * 32767;
|
Session_rpc_object::start();
|
||||||
data[i + 1] = right->channel()->packet_content(p[RIGHT])[i / 2] * 32767;
|
/* this will trigger Audio_out::Out::handle */
|
||||||
}
|
_signal.submit();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (verbose)
|
|
||||||
PDBG("play packet");
|
|
||||||
|
|
||||||
/* send to driver */
|
class Audio_out::Out : public Driver_context
|
||||||
int err;
|
{
|
||||||
if (audio_out_active)
|
private:
|
||||||
if ((err = audio_play(data, 4 * PERIOD)))
|
|
||||||
PWRN("Error %d during playback", err);
|
|
||||||
|
|
||||||
/* acknowledge packet to the client */
|
bool _active() {
|
||||||
if (p[LEFT].valid())
|
return channel_acquired[LEFT] && channel_acquired[RIGHT] &&
|
||||||
left->channel()->acknowledge_packet(p[LEFT]);
|
channel_acquired[LEFT]->active() && channel_acquired[RIGHT]->active();
|
||||||
|
}
|
||||||
|
|
||||||
if (p[RIGHT].valid())
|
Stream *left() { return channel_acquired[LEFT]->stream(); }
|
||||||
right->channel()->acknowledge_packet(p[RIGHT]);
|
Stream *right() { return channel_acquired[RIGHT]->stream(); }
|
||||||
|
|
||||||
|
void _advance_position(Packet *l, Packet *r)
|
||||||
|
{
|
||||||
|
bool full_left = left()->full();
|
||||||
|
bool full_right = right()->full();
|
||||||
|
|
||||||
|
left()->pos(left()->packet_position(l));
|
||||||
|
right()->pos(right()->packet_position(r));
|
||||||
|
|
||||||
|
left()->increment_position();
|
||||||
|
right()->increment_position();
|
||||||
|
|
||||||
|
Session_component *channel_left = channel_acquired[LEFT];
|
||||||
|
Session_component *channel_right = channel_acquired[RIGHT];
|
||||||
|
|
||||||
|
if (full_left)
|
||||||
|
channel_left->alloc_submit();
|
||||||
|
|
||||||
|
if (full_right)
|
||||||
|
channel_right->alloc_submit();
|
||||||
|
|
||||||
|
channel_left->progress_submit();
|
||||||
|
channel_right->progress_submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _play_packet()
|
||||||
|
{
|
||||||
|
Packet *p_left = left()->get(left()->pos());
|
||||||
|
Packet *p_right = right()->get(right()->pos());
|
||||||
|
|
||||||
|
bool found = false;
|
||||||
|
for (int i = 0; i < QUEUE_SIZE; i++) {
|
||||||
|
if (p_left->valid() && p_right->valid()) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p_left = left()->next(p_left);
|
||||||
|
p_right = right()->next(p_right);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
if (!found)
|
||||||
|
return false;
|
||||||
|
|
||||||
Session_component(Channel_number channel, size_t buffer_size,
|
/* convert float to S16LE */
|
||||||
Rpc_entrypoint &ep, Signal_receiver &sig_rec)
|
static short data[2 * PERIOD];
|
||||||
: Session_rpc_object(_alloc_dataspace(buffer_size), ep), _channel(channel),
|
for (int i = 0; i < 2 * PERIOD; i += 2) {
|
||||||
_process_packet_dispatcher(sig_rec, *this,
|
data[i] = p_left->content()[i / 2] * 32767;
|
||||||
&Session_component::_process_packets)
|
data[i + 1] = p_right->content()[i / 2] * 32767;
|
||||||
{
|
|
||||||
Audio_out::channel_acquired[_channel] = this;
|
|
||||||
Session_rpc_object::_channel.sigh_packet_avail(_process_packet_dispatcher);
|
|
||||||
Session_rpc_object::_channel.sigh_ready_to_ack(_process_packet_dispatcher);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~Session_component()
|
p_left->invalidate();
|
||||||
{
|
p_right->invalidate();
|
||||||
Audio_out::channel_acquired[_channel] = 0;
|
|
||||||
|
|
||||||
env()->ram_session()->free(_ds);
|
if (verbose)
|
||||||
|
PDBG("play packet");
|
||||||
|
|
||||||
|
/* send to driver */
|
||||||
|
int err;
|
||||||
|
if (audio_out_active)
|
||||||
|
if ((err = audio_play(data, 4 * PERIOD)))
|
||||||
|
PWRN("Error %d during playback", err);
|
||||||
|
|
||||||
|
p_left->mark_as_played();
|
||||||
|
p_right->mark_as_played();
|
||||||
|
|
||||||
|
_advance_position(p_left, p_right);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
void handle()
|
||||||
|
{
|
||||||
|
while (true) {
|
||||||
|
if (!_active()) {
|
||||||
|
//TODO: may stop device
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* play one packet */
|
||||||
|
if (!_play_packet())
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* give others a try */
|
||||||
|
Service_handler::s()->check_signal(false);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*********************************
|
const char *debug() { return "Audio out"; }
|
||||||
** Audio-out-session interface **
|
};
|
||||||
*********************************/
|
|
||||||
|
|
||||||
void flush()
|
|
||||||
{
|
|
||||||
while (channel()->packet_avail())
|
|
||||||
channel()->acknowledge_packet(channel()->get_packet());
|
|
||||||
}
|
|
||||||
|
|
||||||
void sync_session(Session_capability audio_out_session) { }
|
static bool channel_number_from_string(const char *name,
|
||||||
|
Channel_number *out_number)
|
||||||
|
{
|
||||||
|
static struct Names {
|
||||||
|
const char *name;
|
||||||
|
Channel_number number;
|
||||||
|
} names[] = {
|
||||||
|
{ "left", LEFT }, { "front left", LEFT },
|
||||||
|
{ "right", RIGHT }, { "front right", RIGHT },
|
||||||
|
{ 0, INVALID }
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool channel_number_from_string(const char *name,
|
for (Names *n = names; n->name; ++n)
|
||||||
Channel_number *out_number)
|
if (!Genode::strcmp(name, n->name)) {
|
||||||
|
*out_number = n->number;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Session creation policy for our service
|
||||||
|
*/
|
||||||
|
struct Audio_out::Root_policy
|
||||||
|
{
|
||||||
|
void aquire(const char *args)
|
||||||
{
|
{
|
||||||
static struct Names {
|
size_t ram_quota =
|
||||||
const char *name;
|
Arg_string::find_arg(args, "ram_quota" ).ulong_value(0);
|
||||||
Channel_number number;
|
size_t session_size =
|
||||||
} names[] = {
|
align_addr(sizeof(Audio_out::Session_component), 12);
|
||||||
{ "left", LEFT }, { "front left", LEFT },
|
|
||||||
{ "right", RIGHT }, { "front right", RIGHT },
|
|
||||||
{ 0, INVALID }
|
|
||||||
};
|
|
||||||
|
|
||||||
for (Names *n = names; n->name; ++n)
|
if ((ram_quota < session_size) ||
|
||||||
if (!Genode::strcmp(name, n->name)) {
|
(sizeof(Stream) > ram_quota - session_size)) {
|
||||||
*out_number = n->number;
|
PERR("insufficient 'ram_quota', got %zd, need %zd",
|
||||||
return true;
|
ram_quota, sizeof(Stream) + session_size);
|
||||||
}
|
throw ::Root::Quota_exceeded();
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
char channel_name[16];
|
||||||
|
Channel_number channel_number;
|
||||||
|
Arg_string::find_arg(args, "channel").string(channel_name,
|
||||||
|
sizeof(channel_name),
|
||||||
|
"left");
|
||||||
|
if (!channel_number_from_string(channel_name, &channel_number))
|
||||||
|
throw ::Root::Invalid_args();
|
||||||
|
if (Audio_out::channel_acquired[channel_number])
|
||||||
|
throw ::Root::Unavailable();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void release() { }
|
||||||
* Session creation policy for our service
|
};
|
||||||
*/
|
|
||||||
struct Root_policy
|
|
||||||
{
|
|
||||||
void aquire(const char *args)
|
|
||||||
{
|
|
||||||
size_t ram_quota =
|
|
||||||
Arg_string::find_arg(args, "ram_quota" ).ulong_value(0);
|
|
||||||
size_t buffer_size =
|
|
||||||
Arg_string::find_arg(args, "buffer_size").ulong_value(0);
|
|
||||||
size_t session_size =
|
|
||||||
align_addr(sizeof(Session_component), 12);
|
|
||||||
|
|
||||||
if ((ram_quota < session_size) ||
|
|
||||||
(buffer_size > ram_quota - session_size)) {
|
namespace Audio_out {
|
||||||
PERR("insufficient 'ram_quota', got %zd, need %zd",
|
typedef Root_component<Session_component, Root_policy> Root_component;
|
||||||
ram_quota, buffer_size + session_size);
|
}
|
||||||
throw Root::Quota_exceeded();
|
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* Root component, handling new session requests.
|
||||||
|
*/
|
||||||
|
class Audio_out::Root : public Audio_out::Root_component
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
Signal_context_capability _ctx_cap;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
Session_component *_create_session(const char *args)
|
||||||
|
{
|
||||||
|
if (!audio_out_active)
|
||||||
|
throw Root::Unavailable();
|
||||||
|
|
||||||
char channel_name[16];
|
char channel_name[16];
|
||||||
Channel_number channel_number;
|
Channel_number channel_number = INVALID;
|
||||||
Arg_string::find_arg(args, "channel").string(channel_name,
|
Arg_string::find_arg(args, "channel").string(channel_name,
|
||||||
sizeof(channel_name),
|
sizeof(channel_name),
|
||||||
"left");
|
"left");
|
||||||
if (!channel_number_from_string(channel_name, &channel_number))
|
channel_number_from_string(channel_name, &channel_number);
|
||||||
throw Root::Invalid_args();
|
|
||||||
if (channel_acquired[channel_number])
|
return new (md_alloc())
|
||||||
throw Root::Unavailable();
|
Session_component(channel_number, _ctx_cap);
|
||||||
}
|
}
|
||||||
|
|
||||||
void release() { }
|
public:
|
||||||
};
|
|
||||||
|
|
||||||
typedef Root_component<Session_component, Root_policy> Root_component;
|
Root(Rpc_entrypoint *session_ep, Allocator *md_alloc, Signal_context_capability ctx_cap)
|
||||||
|
: Root_component(session_ep, md_alloc), _ctx_cap(ctx_cap)
|
||||||
/*
|
{ }
|
||||||
* Root component, handling new session requests.
|
};
|
||||||
*/
|
|
||||||
class Root : public Root_component
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
Rpc_entrypoint &_channel_ep;
|
|
||||||
Signal_receiver &_sig_rec;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
Session_component *_create_session(const char *args)
|
|
||||||
{
|
|
||||||
size_t buffer_size =
|
|
||||||
Arg_string::find_arg(args, "buffer_size").ulong_value(0);
|
|
||||||
|
|
||||||
if (!audio_out_active)
|
|
||||||
throw Root::Unavailable();
|
|
||||||
|
|
||||||
char channel_name[16];
|
|
||||||
Channel_number channel_number = INVALID;
|
|
||||||
Arg_string::find_arg(args, "channel").string(channel_name,
|
|
||||||
sizeof(channel_name),
|
|
||||||
"left");
|
|
||||||
channel_number_from_string(channel_name, &channel_number);
|
|
||||||
|
|
||||||
return new (md_alloc())
|
|
||||||
Session_component(channel_number, buffer_size, _channel_ep, _sig_rec);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Root(Rpc_entrypoint &session_ep, Allocator *md_alloc, Signal_receiver &sig_rec)
|
|
||||||
: Root_component(&session_ep, md_alloc), _channel_ep(session_ep), _sig_rec(sig_rec)
|
|
||||||
{ }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
|
@ -242,7 +293,8 @@ int main()
|
||||||
audio_out_active = audio_init() ? false : true;
|
audio_out_active = audio_init() ? false : true;
|
||||||
|
|
||||||
if (audio_out_active) {
|
if (audio_out_active) {
|
||||||
static Audio_out::Root audio_root(ep, env()->heap(), recv);
|
static Audio_out::Out out;
|
||||||
|
static Audio_out::Root audio_root(&ep, env()->heap(), recv.manage(&out));
|
||||||
env()->parent()->announce(ep.manage(&audio_root));
|
env()->parent()->announce(ep.manage(&audio_root));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -350,7 +350,7 @@ int audio_init()
|
||||||
}
|
}
|
||||||
|
|
||||||
/* set fragment policy (TODO: make configurable) */
|
/* set fragment policy (TODO: make configurable) */
|
||||||
int policy = 8;
|
int policy = 1;
|
||||||
if (ioctl_dsp(SNDCTL_DSP_POLICY, &policy) == -1)
|
if (ioctl_dsp(SNDCTL_DSP_POLICY, &policy) == -1)
|
||||||
PERR("Error setting policy");
|
PERR("Error setting policy");
|
||||||
|
|
||||||
|
@ -379,12 +379,12 @@ int audio_init()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int audio_play(short *data, int size)
|
int audio_play(short *data, unsigned size)
|
||||||
{
|
{
|
||||||
uio_t io = { (char *)data, size, UIO_WRITE };
|
uio_t io = { (char *)data, size, UIO_WRITE };
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if ((ret = dsp_drv->write(0, 0, &io, size)) != size)
|
if ((ret = dsp_drv->write(0, 0, &io, size)) != (int)size)
|
||||||
PERR("Error writing data s: %d r: %d func %p", size, ret, dsp_drv->write);
|
PERR("Error writing data s: %d r: %d func %p", size, ret, dsp_drv->write);
|
||||||
|
|
||||||
Irq::check_irq();
|
Irq::check_irq();
|
||||||
|
|
|
@ -1,10 +1,29 @@
|
||||||
|
#
|
||||||
|
# Build
|
||||||
|
#
|
||||||
|
|
||||||
build {
|
build {
|
||||||
core init
|
core init
|
||||||
drivers/timer
|
drivers/timer
|
||||||
drivers/framebuffer drivers/pci drivers/input drivers/audio_out
|
drivers/framebuffer drivers/pci drivers/input drivers/oss
|
||||||
app/avplay
|
server/mixer
|
||||||
|
app/avplay drivers/acpi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Download media file
|
||||||
|
#
|
||||||
|
|
||||||
|
set media_url "ftp://ftp.untergrund.net/users/ae/dhstv/escape-chotro.mp4"
|
||||||
|
if {![file exists bin/mediafile]} {
|
||||||
|
puts "downloading media file from $media_url"
|
||||||
|
catch { exec wget -O bin/mediafile $media_url }
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Generate config
|
||||||
|
#
|
||||||
|
|
||||||
create_boot_directory
|
create_boot_directory
|
||||||
|
|
||||||
set config {
|
set config {
|
||||||
|
@ -36,11 +55,28 @@ append_if [have_spec sdl] config {
|
||||||
</provides>
|
</provides>
|
||||||
</start>}
|
</start>}
|
||||||
|
|
||||||
append_if [have_spec pci] config {
|
append_if [have_spec acpi] config {
|
||||||
|
<start name="acpi">
|
||||||
|
<resource name="RAM" quantum="2M"/>
|
||||||
|
<binary name="acpi_drv"/>
|
||||||
|
<provides>
|
||||||
|
<service name="PCI"/>
|
||||||
|
<service name="IRQ" />
|
||||||
|
</provides>
|
||||||
|
<route>
|
||||||
|
<any-service> <parent/> <any-child /> </any-service>
|
||||||
|
</route>
|
||||||
|
</start>
|
||||||
|
}
|
||||||
|
|
||||||
|
append_if [expr [have_spec pci] && ![have_spec acpi]] config {
|
||||||
<start name="pci_drv">
|
<start name="pci_drv">
|
||||||
<resource name="RAM" quantum="1M"/>
|
<resource name="RAM" quantum="2M"/>
|
||||||
<provides><service name="PCI"/></provides>
|
<provides>
|
||||||
</start>}
|
<service name="PCI"/>
|
||||||
|
</provides>
|
||||||
|
</start>
|
||||||
|
}
|
||||||
|
|
||||||
append_if [have_spec vesa] config {
|
append_if [have_spec vesa] config {
|
||||||
<start name="vesa_drv">
|
<start name="vesa_drv">
|
||||||
|
@ -59,10 +95,23 @@ append config {
|
||||||
<resource name="RAM" quantum="1M"/>
|
<resource name="RAM" quantum="1M"/>
|
||||||
<provides><service name="Timer"/></provides>
|
<provides><service name="Timer"/></provides>
|
||||||
</start>
|
</start>
|
||||||
<start name="audio_out_drv">
|
<start name="oss_drv">
|
||||||
<resource name="RAM" quantum="2M"/>
|
<resource name="RAM" quantum="6M"/>
|
||||||
<provides><service name="Audio_out"/></provides>
|
<route>
|
||||||
</start>
|
<any-service> <any-child /> <parent /></any-service>
|
||||||
|
</route>
|
||||||
|
<provides>
|
||||||
|
<service name="Audio_out"/>
|
||||||
|
</provides>
|
||||||
|
</start>
|
||||||
|
<start name="mixer">
|
||||||
|
<resource name="RAM" quantum="1M"/>
|
||||||
|
<provides><service name="Audio_out"/></provides>
|
||||||
|
<route>
|
||||||
|
<service name="Audio_out"> <child name="oss_drv"/> </service>
|
||||||
|
<any-service> <parent/> <any-child/> </any-service>
|
||||||
|
</route>
|
||||||
|
</start>
|
||||||
<start name="avplay">
|
<start name="avplay">
|
||||||
<resource name="RAM" quantum="64M"/>
|
<resource name="RAM" quantum="64M"/>
|
||||||
<config>
|
<config>
|
||||||
|
@ -70,14 +119,23 @@ append config {
|
||||||
<arg value="mediafile"/>
|
<arg value="mediafile"/>
|
||||||
<sdl_audio_volume value="100"/>
|
<sdl_audio_volume value="100"/>
|
||||||
</config>
|
</config>
|
||||||
|
<route>
|
||||||
|
<service name="PCI"> <any-child /> </service>
|
||||||
|
<service name="Audio_out"> <child name="mixer"/> </service>
|
||||||
|
<any-service> <parent/> <any-child /> </any-service>
|
||||||
|
</route>
|
||||||
</start>
|
</start>
|
||||||
</config>
|
</config>
|
||||||
}
|
}
|
||||||
|
|
||||||
install_config $config
|
install_config $config
|
||||||
|
|
||||||
|
#
|
||||||
|
# Boot modules
|
||||||
|
#
|
||||||
|
|
||||||
set boot_modules {
|
set boot_modules {
|
||||||
core init timer audio_out_drv avplay
|
core init timer oss_drv mixer avplay
|
||||||
ld.lib.so libc.lib.so libc_log.lib.so libc_rom.lib.so libm.lib.so pthread.lib.so zlib.lib.so sdl.lib.so
|
ld.lib.so libc.lib.so libc_log.lib.so libc_rom.lib.so libm.lib.so pthread.lib.so zlib.lib.so sdl.lib.so
|
||||||
avfilter.lib.so avutil.lib.so avcodec.lib.so avformat.lib.so swscale.lib.so
|
avfilter.lib.so avutil.lib.so avcodec.lib.so avformat.lib.so swscale.lib.so
|
||||||
mediafile
|
mediafile
|
||||||
|
@ -85,6 +143,7 @@ set boot_modules {
|
||||||
|
|
||||||
lappend_if [have_spec linux] boot_modules fb_sdl
|
lappend_if [have_spec linux] boot_modules fb_sdl
|
||||||
lappend_if [have_spec pci] boot_modules pci_drv
|
lappend_if [have_spec pci] boot_modules pci_drv
|
||||||
|
lappend_if [have_spec pci] boot_modules acpi_drv
|
||||||
lappend_if [have_spec vesa] boot_modules vesa_drv
|
lappend_if [have_spec vesa] boot_modules vesa_drv
|
||||||
lappend_if [have_spec ps2] boot_modules ps2_drv
|
lappend_if [have_spec ps2] boot_modules ps2_drv
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* \brief Genode-specific audio backend
|
* \brief Genode-specific audio backend
|
||||||
* \author Christian Prochaska
|
* \author Christian Prochaska
|
||||||
|
* \author Sebastian Sumpf
|
||||||
* \date 2012-03-13
|
* \date 2012-03-13
|
||||||
*
|
*
|
||||||
* based on the dummy SDL audio driver
|
* based on the dummy SDL audio driver
|
||||||
|
@ -16,14 +17,12 @@
|
||||||
#include <base/allocator_avl.h>
|
#include <base/allocator_avl.h>
|
||||||
#include <base/printf.h>
|
#include <base/printf.h>
|
||||||
#include <base/thread.h>
|
#include <base/thread.h>
|
||||||
#include <audio_out_session/connection.h>
|
#include <audio_session/connection.h>
|
||||||
#include <os/config.h>
|
#include <os/config.h>
|
||||||
|
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
AUDIO_OUT_SAMPLE_SIZE = sizeof(float),
|
AUDIO_CHANNELS = 2,
|
||||||
AUDIO_OUT_CHANNELS = 2,
|
|
||||||
AUDIO_OUT_FREQ = 44100,
|
|
||||||
AUDIO_OUT_SAMPLES = 1024,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
using Genode::env;
|
using Genode::env;
|
||||||
|
@ -52,15 +51,11 @@ extern "C" {
|
||||||
/* The tag name used by Genode audio */
|
/* The tag name used by Genode audio */
|
||||||
#define GENODEAUD_DRIVER_NAME "genode"
|
#define GENODEAUD_DRIVER_NAME "genode"
|
||||||
|
|
||||||
typedef Audio_out::Session::Channel::Source Stream;
|
|
||||||
|
|
||||||
|
|
||||||
struct SDL_PrivateAudioData {
|
struct SDL_PrivateAudioData {
|
||||||
Uint8 *mixbuf;
|
Uint8 *mixbuf;
|
||||||
Uint32 mixlen;
|
Uint32 mixlen;
|
||||||
Genode::Allocator_avl *block_alloc[AUDIO_OUT_CHANNELS];
|
Audio_out::Connection *audio[AUDIO_CHANNELS];
|
||||||
Audio_out::Connection *audio_out[AUDIO_OUT_CHANNELS];
|
|
||||||
Stream *stream[AUDIO_OUT_CHANNELS];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -82,7 +77,10 @@ static void read_config()
|
||||||
/* read volume from config file */
|
/* read volume from config file */
|
||||||
try {
|
try {
|
||||||
unsigned int config_volume;
|
unsigned int config_volume;
|
||||||
Genode::config()->xml_node().sub_node("sdl_audio_volume").attribute("value").value(&config_volume);
|
|
||||||
|
Genode::config()->xml_node().sub_node("sdl_audio_volume")
|
||||||
|
.attribute("value").value(&config_volume);
|
||||||
|
|
||||||
volume = (float)config_volume / 100;
|
volume = (float)config_volume / 100;
|
||||||
}
|
}
|
||||||
catch (Genode::Config::Invalid) { }
|
catch (Genode::Config::Invalid) { }
|
||||||
|
@ -108,10 +106,8 @@ static int GENODEAUD_Available(void)
|
||||||
|
|
||||||
static void GENODEAUD_DeleteDevice(SDL_AudioDevice *device)
|
static void GENODEAUD_DeleteDevice(SDL_AudioDevice *device)
|
||||||
{
|
{
|
||||||
for (int channel = 0; channel < AUDIO_OUT_CHANNELS; channel++) {
|
for (int channel = 0; channel < AUDIO_CHANNELS; channel++)
|
||||||
destroy(env()->heap(), device->hidden->audio_out[channel]);
|
destroy(env()->heap(), device->hidden->audio[channel]);
|
||||||
destroy(env()->heap(), device->hidden->block_alloc[channel]);
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_free(device->hidden);
|
SDL_free(device->hidden);
|
||||||
SDL_free(device);
|
SDL_free(device);
|
||||||
|
@ -148,23 +144,19 @@ static SDL_AudioDevice *GENODEAUD_CreateDevice(int devindex)
|
||||||
_this->free = GENODEAUD_DeleteDevice;
|
_this->free = GENODEAUD_DeleteDevice;
|
||||||
|
|
||||||
/* connect to 'Audio_out' service */
|
/* connect to 'Audio_out' service */
|
||||||
for (int channel = 0; channel < AUDIO_OUT_CHANNELS; channel++) {
|
for (int channel = 0; channel < AUDIO_CHANNELS; channel++) {
|
||||||
_this->hidden->block_alloc[channel] = new (env()->heap()) Allocator_avl(env()->heap());
|
|
||||||
try {
|
try {
|
||||||
_this->hidden->audio_out[channel] = new (env()->heap())
|
_this->hidden->audio[channel] = new (env()->heap())
|
||||||
Audio_out::Connection(channel_names[channel], _this->hidden->block_alloc[channel]);
|
Audio_out::Connection(channel_names[channel]);
|
||||||
_this->hidden->stream[channel] = _this->hidden->audio_out[channel]->stream();
|
_this->hidden->audio[channel]->start();
|
||||||
} catch(Genode::Parent::Service_denied) {
|
} catch(Genode::Parent::Service_denied) {
|
||||||
PERR("Could not connect to 'Audio_out' service.");
|
PERR("Could not connect to 'Audio_out' service.");
|
||||||
destroy(env()->heap(), _this->hidden->block_alloc[channel]);
|
|
||||||
while(--channel > 0) {
|
while(--channel > 0)
|
||||||
destroy(env()->heap(), _this->hidden->audio_out[channel]);
|
destroy(env()->heap(), _this->hidden->audio[channel]);
|
||||||
destroy(env()->heap(), _this->hidden->block_alloc[channel]);
|
|
||||||
}
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (channel > 0)
|
|
||||||
_this->hidden->audio_out[channel]->sync_session(_this->hidden->audio_out[channel-1]->session_capability());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Genode::config()->sigh(signal_receiver()->manage(&config_signal_context));
|
Genode::config()->sigh(signal_receiver()->manage(&config_signal_context));
|
||||||
|
@ -183,27 +175,32 @@ AudioBootStrap GENODEAUD_bootstrap = {
|
||||||
/* This function waits until it is possible to write a full sound buffer */
|
/* This function waits until it is possible to write a full sound buffer */
|
||||||
static void GENODEAUD_WaitAudio(_THIS)
|
static void GENODEAUD_WaitAudio(_THIS)
|
||||||
{
|
{
|
||||||
for (int channel = 0; channel < AUDIO_OUT_CHANNELS; channel++)
|
for (int channel = 0; channel < AUDIO_CHANNELS; channel++) {
|
||||||
while (_this->hidden->stream[channel]->ack_avail() ||
|
Audio_out::Connection *connection = _this->hidden->audio[channel];
|
||||||
!_this->hidden->stream[channel]->ready_to_submit()) {
|
|
||||||
_this->hidden->stream[channel]->release_packet(_this->hidden->stream[channel]->get_acked_packet());
|
/* wait until allocation is possible */
|
||||||
}
|
if (connection->stream()->full())
|
||||||
|
connection->wait_for_alloc();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void GENODEAUD_PlayAudio(_THIS)
|
static void GENODEAUD_PlayAudio(_THIS)
|
||||||
{
|
{
|
||||||
Packet_descriptor p[AUDIO_OUT_CHANNELS];
|
Audio_out::Packet *p[AUDIO_CHANNELS];
|
||||||
|
|
||||||
|
for (int channel = 0; channel < AUDIO_CHANNELS; channel++)
|
||||||
|
while (1) {
|
||||||
|
Audio_out::Connection *connection = _this->hidden->audio[channel];
|
||||||
|
|
||||||
for (int channel = 0; channel < AUDIO_OUT_CHANNELS; channel++)
|
|
||||||
while (1)
|
|
||||||
try {
|
try {
|
||||||
p[channel] = _this->hidden->stream[channel]->alloc_packet(AUDIO_OUT_SAMPLE_SIZE * AUDIO_OUT_SAMPLES);
|
p[channel] = connection->stream()->alloc();
|
||||||
break;
|
break;
|
||||||
} catch (Stream::Packet_alloc_failed) {
|
} catch (Audio_out::Stream::Alloc_failed) {
|
||||||
/* wait for next finished packet */
|
/* wait for next finished packet */
|
||||||
_this->hidden->stream[channel]->release_packet(_this->hidden->stream[channel]->get_acked_packet());
|
connection->wait_for_alloc();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (signal_receiver()->pending()) {
|
if (signal_receiver()->pending()) {
|
||||||
signal_receiver()->wait_for_signal();
|
signal_receiver()->wait_for_signal();
|
||||||
|
@ -211,13 +208,13 @@ static void GENODEAUD_PlayAudio(_THIS)
|
||||||
read_config();
|
read_config();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int sample = 0; sample < AUDIO_OUT_SAMPLES; sample++)
|
for (int sample = 0; sample < Audio_out::PERIOD; sample++)
|
||||||
for (int channel = 0; channel < AUDIO_OUT_CHANNELS; channel++)
|
for (int channel = 0; channel < AUDIO_CHANNELS; channel++)
|
||||||
_this->hidden->stream[channel]->packet_content(p[channel])[sample] =
|
p[channel]->content()[sample] =
|
||||||
volume * (float)(((int16_t*)_this->hidden->mixbuf)[sample * AUDIO_OUT_CHANNELS + channel]) / 32768;
|
volume * (float)(((int16_t*)_this->hidden->mixbuf)[sample * AUDIO_CHANNELS + channel]) / 32768;
|
||||||
|
|
||||||
for (int channel = 0; channel < AUDIO_OUT_CHANNELS; channel++)
|
for (int channel = 0; channel < AUDIO_CHANNELS; channel++)
|
||||||
_this->hidden->stream[channel]->submit_packet(p[channel]);
|
_this->hidden->audio[channel]->submit(p[channel]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -243,10 +240,10 @@ static int GENODEAUD_OpenAudio(_THIS, SDL_AudioSpec *spec)
|
||||||
PDBG("requested samples = %u", spec->samples);
|
PDBG("requested samples = %u", spec->samples);
|
||||||
PDBG("requested size = %u", spec->size);
|
PDBG("requested size = %u", spec->size);
|
||||||
|
|
||||||
spec->channels = AUDIO_OUT_CHANNELS;
|
spec->channels = AUDIO_CHANNELS;
|
||||||
spec->format = AUDIO_S16LSB;
|
spec->format = AUDIO_S16LSB;
|
||||||
spec->freq = AUDIO_OUT_FREQ;
|
spec->freq = Audio_out::SAMPLE_RATE;
|
||||||
spec->samples = AUDIO_OUT_SAMPLES;
|
spec->samples = Audio_out::PERIOD;
|
||||||
SDL_CalculateAudioSpec(spec);
|
SDL_CalculateAudioSpec(spec);
|
||||||
|
|
||||||
/* Allocate mixing buffer */
|
/* Allocate mixing buffer */
|
||||||
|
|
|
@ -0,0 +1,144 @@
|
||||||
|
#
|
||||||
|
# Build
|
||||||
|
#
|
||||||
|
|
||||||
|
# generic components
|
||||||
|
set build_components {
|
||||||
|
core init
|
||||||
|
drivers/timer
|
||||||
|
server/mixer
|
||||||
|
test/audio_out
|
||||||
|
}
|
||||||
|
|
||||||
|
# platform-specific components
|
||||||
|
if {[have_spec linux]} {
|
||||||
|
lappend build_components drivers/audio_out
|
||||||
|
} else {
|
||||||
|
lappend build_components drivers/pci
|
||||||
|
lappend build_components drivers/acpi
|
||||||
|
lappend build_components drivers/oss
|
||||||
|
}
|
||||||
|
|
||||||
|
build $build_components
|
||||||
|
create_boot_directory
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Config
|
||||||
|
#
|
||||||
|
|
||||||
|
set config {
|
||||||
|
<config>
|
||||||
|
<parent-provides>
|
||||||
|
<service name="ROM"/>
|
||||||
|
<service name="RAM"/>
|
||||||
|
<service name="IRQ"/>
|
||||||
|
<service name="IO_MEM"/>
|
||||||
|
<service name="IO_PORT"/>
|
||||||
|
<service name="CAP"/>
|
||||||
|
<service name="PD"/>
|
||||||
|
<service name="RM"/>
|
||||||
|
<service name="CPU"/>
|
||||||
|
<service name="LOG"/>
|
||||||
|
<service name="SIGNAL"/>
|
||||||
|
</parent-provides>
|
||||||
|
<default-route>
|
||||||
|
<service name="Audio_out"> <child name="mixer"/> </service>
|
||||||
|
<any-service> <parent/> <any-child/> </any-service>
|
||||||
|
</default-route>
|
||||||
|
}
|
||||||
|
|
||||||
|
if {![have_spec linux]} {
|
||||||
|
append config {
|
||||||
|
<start name="acpi">
|
||||||
|
<resource name="RAM" quantum="2M"/>
|
||||||
|
<binary name="acpi_drv"/>
|
||||||
|
<provides>
|
||||||
|
<service name="PCI"/>
|
||||||
|
<service name="IRQ" />
|
||||||
|
</provides>
|
||||||
|
<route>
|
||||||
|
<service name="PCI"> <any-child /> </service>
|
||||||
|
<any-service> <parent/> <any-child /> </any-service>
|
||||||
|
</route>
|
||||||
|
</start>
|
||||||
|
<start name="audio_out_drv">
|
||||||
|
<binary name="oss_drv" />
|
||||||
|
<resource name="RAM" quantum="6M"/>
|
||||||
|
<route>
|
||||||
|
<service name="IRQ"><child name="acpi" /></service>
|
||||||
|
<any-service> <parent /> <any-child /></any-service>
|
||||||
|
</route>
|
||||||
|
<provides>
|
||||||
|
<service name="Audio_out"/>
|
||||||
|
</provides>
|
||||||
|
</start>
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
append config {
|
||||||
|
<start name="audio_out_drv">
|
||||||
|
<resource name="RAM" quantum="1M" />
|
||||||
|
<provides>
|
||||||
|
<service name="Audio_out" />
|
||||||
|
</provides>
|
||||||
|
</start>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
append config {
|
||||||
|
<start name="timer">
|
||||||
|
<resource name="RAM" quantum="1M"/>
|
||||||
|
<provides><service name="Timer"/></provides>
|
||||||
|
</start>
|
||||||
|
<start name="mixer">
|
||||||
|
<resource name="RAM" quantum="1M"/>
|
||||||
|
<provides><service name="Audio_out"/></provides>
|
||||||
|
<route>
|
||||||
|
<service name="Audio_out"> <child name="audio_out_drv"/> </service>
|
||||||
|
<any-service> <parent/> <any-child/> </any-service>
|
||||||
|
</route>
|
||||||
|
</start>
|
||||||
|
<start name="audio0">
|
||||||
|
<binary name="test-audio_out"/>
|
||||||
|
<resource name="RAM" quantum="8M"/>
|
||||||
|
<config>
|
||||||
|
<filename>sample.raw</filename>
|
||||||
|
<filename>vogel.f32</filename>
|
||||||
|
</config>
|
||||||
|
<route>
|
||||||
|
<service name="Audio_out"> <child name="mixer"/> </service>
|
||||||
|
<any-service> <parent/> <any-child/> </any-service>
|
||||||
|
</route>
|
||||||
|
</start>
|
||||||
|
</config>
|
||||||
|
}
|
||||||
|
|
||||||
|
install_config $config
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Boot modules
|
||||||
|
#
|
||||||
|
|
||||||
|
# generic modules
|
||||||
|
set boot_modules {
|
||||||
|
core init
|
||||||
|
timer
|
||||||
|
test-audio_out
|
||||||
|
sample.raw
|
||||||
|
vogel.f32
|
||||||
|
mixer
|
||||||
|
}
|
||||||
|
|
||||||
|
# platform-specific components
|
||||||
|
if {[have_spec linux]} {
|
||||||
|
lappend boot_modules audio_out_drv
|
||||||
|
} else {
|
||||||
|
lappend boot_modules oss_drv
|
||||||
|
lappend boot_modules acpi_drv
|
||||||
|
lappend boot_modules pci_drv
|
||||||
|
}
|
||||||
|
|
||||||
|
build_boot_image $boot_modules
|
||||||
|
append qemu_args "-m 256 -soundhw es1370 -nographic"
|
||||||
|
run_genode_until forever
|
|
@ -39,13 +39,13 @@ int audio_drv_init(void)
|
||||||
if ((err = snd_pcm_hw_params_set_format(playback_handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0)
|
if ((err = snd_pcm_hw_params_set_format(playback_handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0)
|
||||||
return -5;
|
return -5;
|
||||||
|
|
||||||
if ((err = snd_pcm_hw_params_set_rate_near(playback_handle, hw_params, &rate, 0)) < 0)
|
if ((err = snd_pcm_hw_params_set_rate(playback_handle, hw_params, rate, 0)) < 0)
|
||||||
return -6;
|
return -6;
|
||||||
|
|
||||||
if ((err = snd_pcm_hw_params_set_channels(playback_handle, hw_params, 2)) < 0)
|
if ((err = snd_pcm_hw_params_set_channels(playback_handle, hw_params, 2)) < 0)
|
||||||
return -7;
|
return -7;
|
||||||
|
|
||||||
if ((err = snd_pcm_hw_params_set_period_size(playback_handle, hw_params, 4096, 0)) < 0)
|
if ((err = snd_pcm_hw_params_set_period_size(playback_handle, hw_params, 2048, 0)) < 0)
|
||||||
return -8;
|
return -8;
|
||||||
|
|
||||||
if ((err = snd_pcm_hw_params_set_periods(playback_handle, hw_params, 4, 0)) < 0)
|
if ((err = snd_pcm_hw_params_set_periods(playback_handle, hw_params, 4, 0)) < 0)
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
/*
|
/*
|
||||||
* \brief Audio-out driver for Linux
|
* \brief Audio_out-out driver for Linux
|
||||||
* \author Christian Helmuth
|
* \author Christian Helmuth
|
||||||
|
* \author Sebastian Sumpf
|
||||||
* \date 2010-05-11
|
* \date 2010-05-11
|
||||||
*
|
*
|
||||||
* FIXME session and driver shutdown not implemented (audio_drv_stop)
|
* FIXME session and driver shutdown not implemented (audio_drv_stop)
|
||||||
|
*
|
||||||
|
* 2013-01-09: Adapted to news audio interface
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -17,7 +20,7 @@
|
||||||
#include <base/sleep.h>
|
#include <base/sleep.h>
|
||||||
#include <root/component.h>
|
#include <root/component.h>
|
||||||
#include <cap_session/connection.h>
|
#include <cap_session/connection.h>
|
||||||
#include <audio_out_session/rpc_object.h>
|
#include <audio_session/rpc_object.h>
|
||||||
#include <util/misc_math.h>
|
#include <util/misc_math.h>
|
||||||
|
|
||||||
#include "alsa.h"
|
#include "alsa.h"
|
||||||
|
@ -26,222 +29,242 @@ using namespace Genode;
|
||||||
|
|
||||||
static const bool verbose = false;
|
static const bool verbose = false;
|
||||||
|
|
||||||
namespace Audio_out {
|
enum Channel_number { LEFT, RIGHT, MAX_CHANNELS, INVALID = MAX_CHANNELS };
|
||||||
|
|
||||||
class Session_component;
|
|
||||||
|
|
||||||
enum Channel_number { LEFT, RIGHT, MAX_CHANNELS, INVALID = MAX_CHANNELS };
|
|
||||||
|
|
||||||
|
namespace Audio_out
|
||||||
|
{
|
||||||
|
class Session_component;
|
||||||
|
class Out;
|
||||||
|
class Root;
|
||||||
|
struct Root_policy;
|
||||||
static Session_component *channel_acquired[MAX_CHANNELS];
|
static Session_component *channel_acquired[MAX_CHANNELS];
|
||||||
static Semaphore channel_sema;
|
};
|
||||||
|
|
||||||
|
|
||||||
class Session_component : public Session_rpc_object
|
class Audio_out::Session_component : public Audio_out::Session_rpc_object
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
||||||
Ram_dataspace_capability _ds;
|
Channel_number _channel;
|
||||||
Channel_number _channel;
|
|
||||||
|
|
||||||
Ram_dataspace_capability _alloc_dataspace(size_t size)
|
public:
|
||||||
{
|
|
||||||
_ds = env()->ram_session()->alloc(size);
|
|
||||||
return _ds;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
Session_component(Channel_number channel, Signal_context_capability data_cap)
|
||||||
|
:
|
||||||
Session_component(Channel_number channel, size_t buffer_size,
|
Session_rpc_object(data_cap),
|
||||||
Rpc_entrypoint &ep)
|
_channel(channel)
|
||||||
:
|
|
||||||
Session_rpc_object(_alloc_dataspace(buffer_size), ep),
|
|
||||||
_channel(channel)
|
|
||||||
{
|
|
||||||
Audio_out::channel_acquired[_channel] = this;
|
|
||||||
channel_sema.up();
|
|
||||||
}
|
|
||||||
|
|
||||||
~Session_component()
|
|
||||||
{
|
|
||||||
channel_sema.down();
|
|
||||||
Audio_out::channel_acquired[_channel] = 0;
|
|
||||||
|
|
||||||
env()->ram_session()->free(_ds);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*********************************
|
|
||||||
** Audio-out session interface **
|
|
||||||
*********************************/
|
|
||||||
|
|
||||||
void flush()
|
|
||||||
{
|
|
||||||
while (channel()->packet_avail())
|
|
||||||
channel()->acknowledge_packet(channel()->get_packet());
|
|
||||||
}
|
|
||||||
|
|
||||||
void sync_session(Session_capability audio_out_session) { }
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool channel_number_from_string(const char *name,
|
|
||||||
Channel_number *out_number)
|
|
||||||
{
|
|
||||||
static struct Names {
|
|
||||||
const char *name;
|
|
||||||
Channel_number number;
|
|
||||||
} names[] = {
|
|
||||||
{ "left", LEFT }, { "front left", LEFT },
|
|
||||||
{ "right", RIGHT }, { "front right", RIGHT },
|
|
||||||
{ 0, INVALID }
|
|
||||||
};
|
|
||||||
|
|
||||||
for (Names *n = names; n->name; ++n)
|
|
||||||
if (!strcmp(name, n->name)) {
|
|
||||||
*out_number = n->number;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Session creation policy for our service
|
|
||||||
*/
|
|
||||||
struct Root_policy
|
|
||||||
{
|
|
||||||
void aquire(const char *args)
|
|
||||||
{
|
{
|
||||||
size_t ram_quota =
|
Audio_out::channel_acquired[_channel] = this;
|
||||||
Arg_string::find_arg(args, "ram_quota" ).ulong_value(0);
|
|
||||||
size_t buffer_size =
|
|
||||||
Arg_string::find_arg(args, "buffer_size").ulong_value(0);
|
|
||||||
size_t session_size =
|
|
||||||
align_addr(sizeof(Session_component), 12);
|
|
||||||
|
|
||||||
if ((ram_quota < session_size) ||
|
|
||||||
(buffer_size > ram_quota - session_size)) {
|
|
||||||
PERR("insufficient 'ram_quota', got %zd, need %zd",
|
|
||||||
ram_quota, buffer_size + session_size);
|
|
||||||
throw Root::Quota_exceeded();
|
|
||||||
}
|
|
||||||
|
|
||||||
char channel_name[16];
|
|
||||||
Channel_number channel_number;
|
|
||||||
Arg_string::find_arg(args, "channel").string(channel_name,
|
|
||||||
sizeof(channel_name),
|
|
||||||
"left");
|
|
||||||
if (!channel_number_from_string(channel_name, &channel_number))
|
|
||||||
throw Root::Invalid_args();
|
|
||||||
if (channel_acquired[channel_number])
|
|
||||||
throw Root::Unavailable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void release() { }
|
~Session_component()
|
||||||
|
{
|
||||||
|
Audio_out::channel_acquired[_channel] = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static bool channel_number_from_string(const char *name,
|
||||||
|
Channel_number *out_number)
|
||||||
|
{
|
||||||
|
static struct Names {
|
||||||
|
const char *name;
|
||||||
|
Channel_number number;
|
||||||
|
} names[] = {
|
||||||
|
{ "left", LEFT }, { "front left", LEFT },
|
||||||
|
{ "right", RIGHT }, { "front right", RIGHT },
|
||||||
|
{ 0, INVALID }
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef Root_component<Session_component, Root_policy> Root_component;
|
for (Names *n = names; n->name; ++n)
|
||||||
|
if (!strcmp(name, n->name)) {
|
||||||
|
*out_number = n->number;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
return false;
|
||||||
* Root component, handling new session requests.
|
|
||||||
*/
|
|
||||||
class Root : public Root_component,
|
|
||||||
private Thread<0x2000>
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
Semaphore _startup_sema; /* thread startup sync */
|
|
||||||
Rpc_entrypoint &_channel_ep;
|
|
||||||
|
|
||||||
bool _packets_available(Session_component *channel)
|
|
||||||
{
|
|
||||||
if (channel && channel->channel()->packet_avail())
|
|
||||||
return true;
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void entry()
|
|
||||||
{
|
|
||||||
audio_drv_adopt_myself();
|
|
||||||
|
|
||||||
/* indicate thread-startup completion */
|
|
||||||
_startup_sema.up();
|
|
||||||
|
|
||||||
/* handle audio-out packets */
|
|
||||||
while (true) {
|
|
||||||
for (int i = 0; i < MAX_CHANNELS; ++i)
|
|
||||||
channel_sema.down();
|
|
||||||
|
|
||||||
Session_component *left = channel_acquired[LEFT],
|
|
||||||
*right = channel_acquired[RIGHT];
|
|
||||||
|
|
||||||
/* get packets for channels */
|
|
||||||
Packet_descriptor p[MAX_CHANNELS];
|
|
||||||
static short data[2 * PERIOD];
|
|
||||||
|
|
||||||
p[LEFT] = left->channel()->get_packet();
|
|
||||||
for (int i = 0; i < 2 * PERIOD; i += 2)
|
|
||||||
data[i] = left->channel()->packet_content(p[LEFT])[i / 2] * 32767;
|
|
||||||
p[RIGHT] = right->channel()->get_packet();
|
|
||||||
for (int i = 1; i < 2 * PERIOD; i += 2)
|
|
||||||
data[i] = right->channel()->packet_content(p[RIGHT])[i / 2] * 32767;
|
|
||||||
|
|
||||||
if (verbose)
|
|
||||||
PDBG("play packet");
|
|
||||||
|
|
||||||
/* blocking-write packet to ALSA */
|
|
||||||
while (int err = audio_drv_play(data, PERIOD)) {
|
|
||||||
if (verbose) PERR("Error %d during playback", err);
|
|
||||||
audio_drv_stop();
|
|
||||||
audio_drv_start();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* acknowledge packet to the client */
|
|
||||||
if (p[LEFT].valid())
|
|
||||||
left->channel()->acknowledge_packet(p[LEFT]);
|
|
||||||
if (p[RIGHT].valid())
|
|
||||||
right->channel()->acknowledge_packet(p[RIGHT]);
|
|
||||||
|
|
||||||
for (int i = 0; i < MAX_CHANNELS; ++i)
|
|
||||||
channel_sema.up();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
Session_component *_create_session(const char *args)
|
|
||||||
{
|
|
||||||
size_t buffer_size =
|
|
||||||
Arg_string::find_arg(args, "buffer_size").ulong_value(0);
|
|
||||||
|
|
||||||
char channel_name[16];
|
|
||||||
Channel_number channel_number = INVALID;
|
|
||||||
Arg_string::find_arg(args, "channel").string(channel_name,
|
|
||||||
sizeof(channel_name),
|
|
||||||
"left");
|
|
||||||
channel_number_from_string(channel_name, &channel_number);
|
|
||||||
|
|
||||||
return new (md_alloc())
|
|
||||||
Session_component(channel_number, buffer_size, _channel_ep);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Root(Rpc_entrypoint *session_ep, Allocator *md_alloc)
|
|
||||||
: Root_component(session_ep, md_alloc), _channel_ep(*session_ep)
|
|
||||||
{
|
|
||||||
/* synchronize with root thread startup */
|
|
||||||
start();
|
|
||||||
_startup_sema.down();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Manually initialize the 'lx_environ' pointer, needed because 'nic_drv' is not
|
* Root component, handling new session requests.
|
||||||
* using the normal Genode startup code.
|
*/
|
||||||
|
class Audio_out::Out : public Thread<1024 * sizeof(addr_t)>
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
Signal_receiver *_data_recv; /* data is available */
|
||||||
|
|
||||||
|
bool _active() {
|
||||||
|
return channel_acquired[LEFT] && channel_acquired[RIGHT] &&
|
||||||
|
channel_acquired[LEFT]->active() && channel_acquired[RIGHT]->active();
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream *left() { return channel_acquired[LEFT]->stream(); }
|
||||||
|
Stream *right() { return channel_acquired[RIGHT]->stream(); }
|
||||||
|
|
||||||
|
void _advance_position(Packet *l, Packet *r)
|
||||||
|
{
|
||||||
|
bool full_left = left()->full();
|
||||||
|
bool full_right = right()->full();
|
||||||
|
|
||||||
|
left()->pos(left()->packet_position(l));
|
||||||
|
right()->pos(right()->packet_position(l));
|
||||||
|
|
||||||
|
left()->increment_position();
|
||||||
|
right()->increment_position();
|
||||||
|
|
||||||
|
Session_component *channel_left = channel_acquired[LEFT];
|
||||||
|
Session_component *channel_right = channel_acquired[RIGHT];
|
||||||
|
|
||||||
|
if (full_left)
|
||||||
|
channel_left->alloc_submit();
|
||||||
|
|
||||||
|
if (full_right)
|
||||||
|
channel_right->alloc_submit();
|
||||||
|
|
||||||
|
channel_left->progress_submit();
|
||||||
|
channel_right->progress_submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _play_packet()
|
||||||
|
{
|
||||||
|
Packet *p_left = left()->get(left()->pos());
|
||||||
|
Packet *p_right = right()->get(left()->pos());
|
||||||
|
|
||||||
|
bool found = false;
|
||||||
|
for (int i = 0; i < QUEUE_SIZE; i++) {
|
||||||
|
if (p_left->valid() && p_right->valid()) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
p_left = left()->next(p_left);
|
||||||
|
p_right = right()->next(p_right);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* convert float to S16LE */
|
||||||
|
static short data[2 * PERIOD];
|
||||||
|
|
||||||
|
for (int i = 0; i < 2 * PERIOD; i += 2) {
|
||||||
|
data[i] = p_left->content()[i / 2] * 32767;
|
||||||
|
data[i + 1] = p_right->content()[i / 2] * 32767;
|
||||||
|
}
|
||||||
|
|
||||||
|
p_left->invalidate();
|
||||||
|
p_right->invalidate();
|
||||||
|
|
||||||
|
if (verbose)
|
||||||
|
PDBG("play packet");
|
||||||
|
|
||||||
|
/* blocking-write packet to ALSA */
|
||||||
|
while (int err = audio_drv_play(data, PERIOD)) {
|
||||||
|
if (verbose) PERR("Error %d during playback", err);
|
||||||
|
audio_drv_stop();
|
||||||
|
audio_drv_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
p_left->mark_as_played();
|
||||||
|
p_right->mark_as_played();
|
||||||
|
|
||||||
|
_advance_position(p_left, p_right);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Out(Signal_receiver *data_recv)
|
||||||
|
: Thread("audio_out"), _data_recv(data_recv) { }
|
||||||
|
|
||||||
|
void entry()
|
||||||
|
{
|
||||||
|
audio_drv_adopt_myself();
|
||||||
|
|
||||||
|
/* handle audio out */
|
||||||
|
while (true)
|
||||||
|
if (!_active() || !_play_packet())
|
||||||
|
_data_recv->wait_for_signal();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Session creation policy for our service
|
||||||
|
*/
|
||||||
|
struct Audio_out::Root_policy
|
||||||
|
{
|
||||||
|
void aquire(const char *args)
|
||||||
|
{
|
||||||
|
size_t ram_quota =
|
||||||
|
Arg_string::find_arg(args, "ram_quota" ).ulong_value(0);
|
||||||
|
size_t session_size =
|
||||||
|
align_addr(sizeof(Audio_out::Session_component), 12);
|
||||||
|
|
||||||
|
if ((ram_quota < session_size) ||
|
||||||
|
(sizeof(Stream) > ram_quota - session_size)) {
|
||||||
|
PERR("insufficient 'ram_quota', got %zd, need %zd",
|
||||||
|
ram_quota, sizeof(Stream) + session_size);
|
||||||
|
throw ::Root::Quota_exceeded();
|
||||||
|
}
|
||||||
|
|
||||||
|
char channel_name[16];
|
||||||
|
Channel_number channel_number;
|
||||||
|
Arg_string::find_arg(args, "channel").string(channel_name,
|
||||||
|
sizeof(channel_name),
|
||||||
|
"left");
|
||||||
|
if (!channel_number_from_string(channel_name, &channel_number))
|
||||||
|
throw ::Root::Invalid_args();
|
||||||
|
if (Audio_out::channel_acquired[channel_number])
|
||||||
|
throw ::Root::Unavailable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void release() { }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
namespace Audio_out {
|
||||||
|
typedef Root_component<Session_component, Root_policy> Root_component;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Audio_out::Root : public Audio_out::Root_component
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
Signal_context_capability _data_cap;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
Session_component *_create_session(const char *args)
|
||||||
|
{
|
||||||
|
char channel_name[16];
|
||||||
|
Channel_number channel_number = INVALID;
|
||||||
|
Arg_string::find_arg(args, "channel").string(channel_name,
|
||||||
|
sizeof(channel_name),
|
||||||
|
"left");
|
||||||
|
channel_number_from_string(channel_name, &channel_number);
|
||||||
|
|
||||||
|
return new (md_alloc())
|
||||||
|
Session_component(channel_number, _data_cap);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Root(Rpc_entrypoint *session_ep, Allocator *md_alloc, Signal_context_capability data_cap)
|
||||||
|
: Root_component(session_ep, md_alloc), _data_cap(data_cap)
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Manually initialize the 'lx_environ' pointer, needed because 'nic_drv' is
|
||||||
|
* not using the normal Genode startup code.
|
||||||
*/
|
*/
|
||||||
extern char **environ;
|
extern char **environ;
|
||||||
char **lx_environ = environ;
|
char **lx_environ = environ;
|
||||||
|
@ -249,9 +272,10 @@ char **lx_environ = environ;
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
enum { STACK_SIZE = 4096 };
|
/* setup data available signal */
|
||||||
static Cap_connection cap;
|
static Signal_receiver data_recv;
|
||||||
static Rpc_entrypoint ep(&cap, STACK_SIZE, "audio_ep");
|
static Signal_context data_context;
|
||||||
|
static Signal_context_capability data_cap(data_recv.manage(&data_context));
|
||||||
|
|
||||||
/* init ALSA */
|
/* init ALSA */
|
||||||
int err = audio_drv_init();
|
int err = audio_drv_init();
|
||||||
|
@ -261,9 +285,18 @@ int main(int argc, char **argv)
|
||||||
}
|
}
|
||||||
audio_drv_start();
|
audio_drv_start();
|
||||||
|
|
||||||
|
/* start output thread */
|
||||||
|
Audio_out::Out *out = new(env()->heap()) Audio_out::Out(&data_recv);
|
||||||
|
out->start();
|
||||||
|
|
||||||
|
enum { STACK_SIZE = 1024 * sizeof(addr_t) };
|
||||||
|
static Cap_connection cap;
|
||||||
|
static Rpc_entrypoint ep(&cap, STACK_SIZE, "audio_ep");
|
||||||
|
|
||||||
/* setup service */
|
/* setup service */
|
||||||
static Audio_out::Root audio_root(&ep, env()->heap());
|
static Audio_out::Root audio_root(&ep, env()->heap(), data_cap);
|
||||||
env()->parent()->announce(ep.manage(&audio_root));
|
env()->parent()->announce(ep.manage(&audio_root));
|
||||||
|
|
||||||
sleep_forever();
|
sleep_forever();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -15,13 +15,17 @@
|
||||||
* under the terms of the GNU General Public License version 2.
|
* under the terms of the GNU General Public License version 2.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <base/allocator_avl.h>
|
#include <audio_session/connection.h>
|
||||||
#include <base/printf.h>
|
#include <base/printf.h>
|
||||||
#include <base/sleep.h>
|
#include <base/sleep.h>
|
||||||
#include <base/thread.h>
|
#include <rom_session/connection.h>
|
||||||
#include <audio_out_session/connection.h>
|
#include <dataspace/client.h>
|
||||||
#include <os/config.h>
|
#include <os/config.h>
|
||||||
|
|
||||||
|
|
||||||
|
using namespace Genode;
|
||||||
|
|
||||||
|
|
||||||
using namespace Genode;
|
using namespace Genode;
|
||||||
using namespace Audio_out;
|
using namespace Audio_out;
|
||||||
|
|
||||||
|
@ -29,11 +33,12 @@ static const bool verbose = false;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
CHN_CNT = 2, /* number of channels */
|
CHN_CNT = 2, /* number of channels */
|
||||||
|
FRAME_SIZE = sizeof(float),
|
||||||
PERIOD_CSIZE = FRAME_SIZE * PERIOD, /* size of channel packet (bytes) */
|
PERIOD_CSIZE = FRAME_SIZE * PERIOD, /* size of channel packet (bytes) */
|
||||||
PERIOD_FSIZE = CHN_CNT * PERIOD_CSIZE, /* size of period in file (bytes) */
|
PERIOD_FSIZE = CHN_CNT * PERIOD_CSIZE, /* size of period in file (bytes) */
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static const char *channel_names[] = { "front left", "front right" };
|
static const char *channel_names[] = { "front left", "front right" };
|
||||||
|
|
||||||
|
|
||||||
|
@ -41,22 +46,18 @@ class Track : Thread<8192>
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
||||||
typedef Audio_out::Session::Channel::Source Stream;
|
|
||||||
|
|
||||||
const char *_file;
|
const char *_file;
|
||||||
Audio_out::Connection *_audio_out[CHN_CNT];
|
Audio_out::Connection *_audio_out[CHN_CNT];
|
||||||
Stream *_stream[CHN_CNT];
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Track(const char *file, Allocator_avl *block_alloc[CHN_CNT]) : _file(file)
|
Track(const char *file) : Thread("track"), _file(file)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < CHN_CNT; ++i) {
|
for (int i = 0; i < CHN_CNT; ++i) {
|
||||||
|
/* allocation signal for first channel only */
|
||||||
_audio_out[i] = new (env()->heap())
|
_audio_out[i] = new (env()->heap())
|
||||||
Audio_out::Connection(channel_names[i], block_alloc[i]);
|
Audio_out::Connection(channel_names[i],
|
||||||
_stream[i] = _audio_out[i]->stream();
|
i == 0 ? true : false);
|
||||||
if (i > 0)
|
|
||||||
_audio_out[i]->sync_session(_audio_out[i-1]->session_capability());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,8 +83,9 @@ class Track : Thread<8192>
|
||||||
PDBG("%s size is %zu Bytes (attached to %p)",
|
PDBG("%s size is %zu Bytes (attached to %p)",
|
||||||
_file, ds_client.size(), base);
|
_file, ds_client.size(), base);
|
||||||
|
|
||||||
Packet_descriptor p[CHN_CNT];
|
size_t file_size = ds_client.size();
|
||||||
size_t file_size = ds_client.size();
|
for (int i = 0; i < CHN_CNT; ++i)
|
||||||
|
_audio_out[i]->start();
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
|
|
||||||
|
@ -100,46 +102,40 @@ class Track : Thread<8192>
|
||||||
? (file_size - offset) / CHN_CNT / FRAME_SIZE
|
? (file_size - offset) / CHN_CNT / FRAME_SIZE
|
||||||
: PERIOD;
|
: PERIOD;
|
||||||
|
|
||||||
for (int chn = 0; chn < CHN_CNT; ++chn)
|
Packet *p[CHN_CNT];
|
||||||
while (1)
|
while (1)
|
||||||
try {
|
try {
|
||||||
p[chn] = _stream[chn]->alloc_packet(PERIOD_CSIZE);
|
p[0] = _audio_out[0]->stream()->alloc();
|
||||||
break;
|
break;
|
||||||
} catch (Stream::Packet_alloc_failed) {
|
} catch (Audio_out::Stream::Alloc_failed) {
|
||||||
/* wait for next finished packet */
|
_audio_out[0]->wait_for_alloc();
|
||||||
_stream[chn]->release_packet(_stream[chn]->get_acked_packet());
|
}
|
||||||
}
|
|
||||||
|
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 */
|
/* copy channel contents into sessions */
|
||||||
float *content = (float *)(base + offset);
|
float *content = (float *)(base + offset);
|
||||||
for (unsigned c = 0; c < CHN_CNT * chunk; c += CHN_CNT)
|
for (unsigned c = 0; c < CHN_CNT * chunk; c += CHN_CNT)
|
||||||
for (int i = 0; i < CHN_CNT; ++i)
|
for (int i = 0; i < CHN_CNT; ++i)
|
||||||
_stream[i]->packet_content(p[i])[c / 2] = content[c + i];
|
p[i]->content()[c / 2] = content[c + i];
|
||||||
|
|
||||||
/* handle last packet gracefully */
|
/* handle last packet gracefully */
|
||||||
if (chunk < PERIOD) {
|
if (chunk < PERIOD) {
|
||||||
for (int i = 0; i < CHN_CNT; ++i)
|
for (int i = 0; i < CHN_CNT; ++i)
|
||||||
memset((_stream[i]->packet_content(p[i]) + chunk),
|
memset(p[i]->content() + chunk,
|
||||||
0, PERIOD_CSIZE - FRAME_SIZE * chunk);
|
0, PERIOD_CSIZE - FRAME_SIZE * chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (verbose)
|
if (verbose)
|
||||||
PDBG("%s submit packet %zu", _file, cnt);
|
PDBG("%s submit packet %zu", _file,
|
||||||
|
_audio_out[0]->stream()->packet_position((p[0])));
|
||||||
|
|
||||||
for (int i = 0; i < CHN_CNT; ++i)
|
for (int i = 0; i < CHN_CNT; i++)
|
||||||
_stream[i]->submit_packet(p[i]);
|
_audio_out[i]->submit(p[i]);
|
||||||
|
|
||||||
for (int i = 0; i < CHN_CNT; ++i)
|
|
||||||
while (_stream[i]->ack_avail() ||
|
|
||||||
!_stream[i]->ready_to_submit()) {
|
|
||||||
_stream[i]->release_packet(_stream[i]->get_acked_packet());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ack remaining packets */
|
|
||||||
for (int i = 0; i < CHN_CNT; ++i)
|
|
||||||
while (_stream[i]->ack_avail())
|
|
||||||
_stream[i]->release_packet(_stream[i]->get_acked_packet());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,7 +185,7 @@ static int process_config(const char ***files)
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
PDBG("--- Audio-out test ---\n");
|
PDBG("--- Audio_out test ---\n");
|
||||||
|
|
||||||
const char *defaults[] = { "1.raw", "2.raw" };
|
const char *defaults[] = { "1.raw", "2.raw" };
|
||||||
const char **files = defaults;
|
const char **files = defaults;
|
||||||
|
@ -203,11 +199,9 @@ int main(int argc, char **argv)
|
||||||
}
|
}
|
||||||
|
|
||||||
Track *track[cnt];
|
Track *track[cnt];
|
||||||
Allocator_avl *block_alloc[cnt][CHN_CNT];
|
|
||||||
for (int i = 0; i < cnt; ++i) {
|
for (int i = 0; i < cnt; ++i) {
|
||||||
for (int j = 0; j < CHN_CNT; ++j)
|
track[i] = new (env()->heap()) Track(files[i]);
|
||||||
block_alloc[i][j] = new (env()->heap()) Allocator_avl(env()->heap());
|
|
||||||
track[i] = new (env()->heap()) Track(files[i], block_alloc[i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* start playback after constrution of all tracks */
|
/* start playback after constrution of all tracks */
|
||||||
|
@ -217,3 +211,5 @@ int main(int argc, char **argv)
|
||||||
sleep_forever();
|
sleep_forever();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue