304 lines
6.9 KiB
C++
304 lines
6.9 KiB
C++
/*
|
|
* \brief Genode C API audio functions needed by OKLinux
|
|
* \author Stefan Kalkowski
|
|
* \date 2009-11-12
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2009-2013 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 <base/allocator_avl.h>
|
|
#include <base/printf.h>
|
|
#include <audio_out_session/connection.h>
|
|
|
|
extern "C" {
|
|
#include <genode/config.h>
|
|
}
|
|
|
|
static unsigned long stream_data = 0;
|
|
static void (*interrupt_handler)(unsigned long) = 0;
|
|
|
|
class Audio_cache
|
|
{
|
|
public:
|
|
|
|
typedef Audio_out::Session::Channel::Source Stream;
|
|
|
|
enum Channels { LEFT, RIGHT, CHANNEL };
|
|
|
|
enum {
|
|
FRAME_SIZE_OUT = Audio_out::FRAME_SIZE,
|
|
FRAME_SIZE_IN = sizeof(Genode::int16_t),
|
|
PACKET_SIZE_OUT = FRAME_SIZE_OUT * Audio_out::PERIOD,
|
|
PACKET_SIZE_IN = FRAME_SIZE_IN * Audio_out::PERIOD * CHANNEL,
|
|
PACKET_CNT_MAX = Audio_out::QUEUE_SIZE - 1,
|
|
BUF_SIZE = PACKET_CNT_MAX * PACKET_SIZE_OUT + 0x400
|
|
};
|
|
|
|
private:
|
|
|
|
class Entry
|
|
{
|
|
private:
|
|
|
|
Stream *_stream;
|
|
Packet_descriptor _descriptor; /* packet stream descriptor */
|
|
bool _ready; /* ready to submit */
|
|
bool _submitted; /* submitted to hardware */
|
|
|
|
public:
|
|
|
|
Entry(Stream *stream)
|
|
: _stream(stream), _descriptor(_stream->alloc_packet(PACKET_SIZE_OUT)),
|
|
_ready(false), _submitted(false) {}
|
|
|
|
Entry() : _ready(false), _submitted(false) { }
|
|
|
|
~Entry() {}
|
|
|
|
Packet_descriptor descriptor() { return _descriptor; }
|
|
|
|
float* content() {
|
|
return _stream->packet_content(_descriptor); }
|
|
|
|
void submit(bool delay=false)
|
|
{
|
|
_ready = true;
|
|
if(interrupt_handler && !_submitted && !delay) {
|
|
_submitted = true;
|
|
_stream->submit_packet(_descriptor);
|
|
}
|
|
}
|
|
|
|
bool submitted() { return _submitted; }
|
|
|
|
bool ready() { return _ready; }
|
|
|
|
void acknowledge()
|
|
{
|
|
_submitted = false;
|
|
_ready = false;
|
|
}
|
|
};
|
|
|
|
|
|
Genode::Allocator_avl _alloc_left;
|
|
Genode::Allocator_avl _alloc_right;
|
|
Audio_out::Connection _left;
|
|
Audio_out::Connection _right;
|
|
Entry _entries[CHANNEL][PACKET_CNT_MAX];
|
|
unsigned _idx[CHANNEL];
|
|
Genode::size_t _offset;
|
|
unsigned _hw_pointer[CHANNEL];
|
|
|
|
Stream *_stream(unsigned channel) {
|
|
return channel == LEFT ? _left.stream() : _right.stream(); }
|
|
|
|
public:
|
|
|
|
Audio_cache() : _alloc_left(Genode::env()->heap()),
|
|
_alloc_right(Genode::env()->heap()),
|
|
_left("front left", &_alloc_left, BUF_SIZE),
|
|
_right("front right", &_alloc_right, BUF_SIZE)
|
|
{
|
|
for (unsigned ch=LEFT; ch < CHANNEL; ch++) {
|
|
for (unsigned i = 0; i < PACKET_CNT_MAX; i++)
|
|
_entries[ch][i] = Entry(_stream(ch));
|
|
_idx[ch] = 0;
|
|
_hw_pointer[ch] = 0;
|
|
}
|
|
_offset = 0;
|
|
_right.sync_session(_left.session_capability());
|
|
}
|
|
|
|
~Audio_cache()
|
|
{
|
|
for (unsigned ch=LEFT; ch < CHANNEL; ch++)
|
|
for (unsigned i = 0; i < PACKET_CNT_MAX; i++)
|
|
_stream(ch)->release_packet(_entries[ch][i].descriptor());
|
|
}
|
|
|
|
void write (void *src, Genode::size_t size)
|
|
{
|
|
using namespace Genode;
|
|
|
|
/* The actual packet should not be in use */
|
|
for (unsigned ch=0; ch < CHANNEL; ch++)
|
|
if (_entries[ch][_idx[ch]].submitted()) {
|
|
PERR("Error: (un-)acknowledged packet chan=%d idx=%d",
|
|
ch, _idx[ch]);
|
|
}
|
|
|
|
short *src_ptr = (short*)src;
|
|
while (size) {
|
|
/* Use left space in packet or content-size */
|
|
size_t packet_space =
|
|
min<size_t>(size / CHANNEL / FRAME_SIZE_IN * FRAME_SIZE_OUT,
|
|
PACKET_SIZE_OUT - _offset);
|
|
|
|
for (unsigned chan=0; chan < CHANNEL; chan++) {
|
|
float *dest = _entries[chan][_idx[chan]].content()
|
|
+ (_offset / FRAME_SIZE_OUT);
|
|
for (unsigned frame = 0; frame < packet_space / FRAME_SIZE_OUT;
|
|
frame++)
|
|
dest[frame] = src_ptr[(frame * CHANNEL) + chan] / 32767.0;
|
|
}
|
|
|
|
/* If packet is full, submit it and switch to next one */
|
|
if ((_offset + packet_space) == PACKET_SIZE_OUT) {
|
|
for (unsigned chan=0; chan < CHANNEL; chan++) {
|
|
_entries[chan][_idx[chan]].submit(true);
|
|
_idx[chan] = (_idx[chan] + 1) % PACKET_CNT_MAX;
|
|
}
|
|
_offset = 0;
|
|
} else
|
|
_offset += packet_space;
|
|
src_ptr += (packet_space / FRAME_SIZE_OUT) * CHANNEL;
|
|
size -= packet_space / FRAME_SIZE_OUT * FRAME_SIZE_IN * CHANNEL;
|
|
}
|
|
}
|
|
|
|
void submit_all()
|
|
{
|
|
for (unsigned i = 0; i < PACKET_CNT_MAX; i++) {
|
|
for (unsigned ch = 0; ch < CHANNEL; ch++) {
|
|
if (_entries[ch][i].ready() && !_entries[ch][i].submitted())
|
|
_entries[ch][i].submit();
|
|
}
|
|
}
|
|
}
|
|
|
|
void flush() {
|
|
_left.flush();
|
|
_right.flush();
|
|
|
|
for (unsigned i = 0; i < PACKET_CNT_MAX; i++) {
|
|
for (unsigned ch = 0; ch < CHANNEL; ch++) {
|
|
if (_entries[ch][i].submitted())
|
|
_stream(ch)->get_acked_packet();
|
|
_entries[ch][i].acknowledge();
|
|
}
|
|
}
|
|
|
|
for (unsigned ch = 0; ch < CHANNEL; ch++) {
|
|
_idx[ch] = 0;
|
|
_hw_pointer[ch] = 0;
|
|
}
|
|
_offset = 0;
|
|
}
|
|
|
|
void acknowledge()
|
|
{
|
|
bool acked = false;
|
|
for (unsigned ch = 0; ch < CHANNEL; ch++) {
|
|
if (_stream(ch)->ack_avail()) {
|
|
_stream(ch)->get_acked_packet();
|
|
|
|
for (unsigned i = _idx[ch]; ;
|
|
i = (i + 1) % PACKET_CNT_MAX) {
|
|
if (_entries[ch][i].submitted()) {
|
|
_entries[ch][i].acknowledge();
|
|
break;
|
|
}
|
|
}
|
|
|
|
_hw_pointer[ch]++;
|
|
acked = true;
|
|
}
|
|
}
|
|
if (acked)
|
|
interrupt_handler(stream_data);
|
|
}
|
|
|
|
Genode::size_t pointer()
|
|
{
|
|
using namespace Genode;
|
|
Genode::size_t size = min<size_t>(_hw_pointer[LEFT], _hw_pointer[RIGHT]);
|
|
return size * Audio_cache::PACKET_SIZE_OUT * CHANNEL
|
|
/ FRAME_SIZE_OUT * FRAME_SIZE_IN ;
|
|
}
|
|
};
|
|
|
|
|
|
static Audio_cache *audio_cache(void)
|
|
{
|
|
try {
|
|
static Audio_cache _cache;
|
|
return &_cache;
|
|
} catch(...) {}
|
|
return 0;
|
|
}
|
|
|
|
static Genode::uint8_t zeroes[Audio_cache::PACKET_SIZE_IN];
|
|
|
|
|
|
extern "C" {
|
|
#include <genode/audio.h>
|
|
|
|
int genode_audio_ready()
|
|
{
|
|
return genode_config_audio() && audio_cache();
|
|
}
|
|
|
|
|
|
void genode_audio_collect_acks()
|
|
{
|
|
if (!interrupt_handler)
|
|
return;
|
|
audio_cache()->submit_all();
|
|
audio_cache()->acknowledge();
|
|
}
|
|
|
|
|
|
void genode_audio_prepare()
|
|
{
|
|
audio_cache()->flush();
|
|
}
|
|
|
|
|
|
void genode_audio_trigger_start(void (*func)(unsigned long), unsigned long data)
|
|
{
|
|
interrupt_handler = func;
|
|
stream_data = data;
|
|
}
|
|
|
|
|
|
void genode_audio_trigger_stop()
|
|
{
|
|
interrupt_handler = 0;
|
|
stream_data = 0;
|
|
audio_cache()->flush();
|
|
}
|
|
|
|
|
|
unsigned long genode_audio_position()
|
|
{
|
|
unsigned long ret = audio_cache()->pointer();
|
|
return ret;
|
|
}
|
|
|
|
|
|
void genode_audio_write(void* src, unsigned long sz)
|
|
{
|
|
audio_cache()->write(src, sz);
|
|
}
|
|
|
|
|
|
void genode_audio_fill_silence(unsigned long sz)
|
|
{
|
|
audio_cache()->write(&zeroes, sz);
|
|
}
|
|
|
|
|
|
unsigned int genode_audio_packet_size() { return Audio_cache::PACKET_SIZE_IN; }
|
|
|
|
|
|
unsigned int genode_audio_packet_count() { return Audio_cache::PACKET_CNT_MAX; }
|
|
|
|
} // extern "C"
|