2011-12-22 15:19:25 +00:00
|
|
|
/*
|
2013-01-10 11:10:41 +00:00
|
|
|
* \brief Audio_out session interface
|
2011-12-22 15:19:25 +00:00
|
|
|
* \author Sebastian Sumpf
|
2013-01-10 11:10:41 +00:00
|
|
|
* \date 2012-12-20
|
2011-12-22 15:19:25 +00:00
|
|
|
*
|
2013-01-10 11:10:41 +00:00
|
|
|
* An audio session corresponds to one output channel, which can be used to
|
|
|
|
* send audio frames. Each session consists of an 'Audio_out::Stream' object
|
|
|
|
* that resides in shared memory between the client and the server. The
|
|
|
|
* 'Audio_out::Stream' in turn consists of 'Audio_out::Packet's that contain
|
|
|
|
* the actual frames. Each packet within a stream is freely accessible or may
|
|
|
|
* be allocated successively. Also there is a current position pointer for each
|
|
|
|
* stream that is updated by the server. This way, it is possible to send
|
|
|
|
* sporadic events that need immediate processing as well as streams that rely
|
|
|
|
* on buffering.
|
2011-12-22 15:19:25 +00:00
|
|
|
*
|
2013-01-10 11:10:41 +00:00
|
|
|
* Audio_out channel identifiers (loosely related to WAV channels) are:
|
2011-12-22 15:19:25 +00:00
|
|
|
*
|
2013-01-10 11:10:41 +00:00
|
|
|
* * front left (or left), front right (or right), front center
|
|
|
|
* * lfe (low frequency effects, subwoofer)
|
|
|
|
* * rear left, rear right, rear center
|
2011-12-22 15:19:25 +00:00
|
|
|
*
|
|
|
|
* For example, consumer-oriented 6-channel (5.1) audio uses front
|
2013-01-10 11:10:41 +00:00
|
|
|
* left/right/center, rear left/right and lfe.
|
|
|
|
*
|
|
|
|
* Note: That most components right now only support: "(front) left" and
|
|
|
|
* "(front) right".
|
2011-12-22 15:19:25 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2013-01-10 11:10:41 +00:00
|
|
|
* Copyright (C) 2012-2013 Genode Labs GmbH
|
2011-12-22 15:19:25 +00:00
|
|
|
*
|
|
|
|
* This file is part of the Genode OS framework, which is distributed
|
|
|
|
* under the terms of the GNU General Public License version 2.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef _INCLUDE__AUDIO_OUT_SESSION__AUDIO_OUT_SESSION_H_
|
|
|
|
#define _INCLUDE__AUDIO_OUT_SESSION__AUDIO_OUT_SESSION_H_
|
|
|
|
|
2013-01-10 11:10:41 +00:00
|
|
|
#include <base/allocator.h>
|
2011-12-22 15:19:25 +00:00
|
|
|
#include <dataspace/capability.h>
|
2013-01-10 11:10:41 +00:00
|
|
|
#include <base/rpc.h>
|
2011-12-22 15:19:25 +00:00
|
|
|
#include <session/session.h>
|
|
|
|
|
2013-01-10 11:10:41 +00:00
|
|
|
|
2011-12-22 15:19:25 +00:00
|
|
|
namespace Audio_out {
|
2013-01-10 11:10:41 +00:00
|
|
|
class Packet;
|
|
|
|
class Stream;
|
|
|
|
class Session;
|
2011-12-22 15:19:25 +00:00
|
|
|
|
|
|
|
enum {
|
2013-01-10 11:10:41 +00:00
|
|
|
QUEUE_SIZE = 16, /* buffer queue size */
|
|
|
|
PERIOD = 2048, /* samples per period */
|
|
|
|
SAMPLE_RATE = 44100,
|
|
|
|
SAMPLE_SIZE = sizeof(float),
|
2011-12-22 15:19:25 +00:00
|
|
|
};
|
2013-01-10 11:10:41 +00:00
|
|
|
}
|
2011-12-22 15:19:25 +00:00
|
|
|
|
|
|
|
|
2013-01-10 11:10:41 +00:00
|
|
|
/**
|
|
|
|
* Audio_out packet containing frames
|
|
|
|
*/
|
|
|
|
class Audio_out::Packet
|
|
|
|
{
|
|
|
|
private:
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2013-01-10 11:10:41 +00:00
|
|
|
friend class Session_client;
|
|
|
|
friend class Stream;
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2013-01-10 11:10:41 +00:00
|
|
|
bool _valid;
|
|
|
|
bool _wait_for_play;
|
|
|
|
float _data[PERIOD];
|
|
|
|
|
|
|
|
void _submit() { _valid = true; _wait_for_play = true; }
|
|
|
|
void _alloc() { _wait_for_play = false; _valid = false; }
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
Packet() : _valid(false), _wait_for_play(false) { }
|
2011-12-22 15:19:25 +00:00
|
|
|
|
|
|
|
/**
|
2013-01-10 11:10:41 +00:00
|
|
|
* Copy data into packet, if there are less frames given than 'PERIOD',
|
|
|
|
* the remainder is filled with zeros
|
|
|
|
*
|
|
|
|
* \param data frames to copy in
|
|
|
|
* \param size number of frames to copy
|
2011-12-22 15:19:25 +00:00
|
|
|
*/
|
2013-01-10 11:10:41 +00:00
|
|
|
void content(float *data, Genode::size_t samples)
|
|
|
|
{
|
|
|
|
Genode::memcpy(_data, data, (samples > PERIOD ? PERIOD : samples) * SAMPLE_SIZE);
|
|
|
|
|
|
|
|
if (samples < PERIOD)
|
|
|
|
Genode::memset(data + samples, 0, (PERIOD - samples) * SAMPLE_SIZE);
|
|
|
|
}
|
2011-12-22 15:19:25 +00:00
|
|
|
|
|
|
|
/**
|
2013-01-10 11:10:41 +00:00
|
|
|
* Get content
|
|
|
|
*
|
|
|
|
* \return pointer to frame data
|
2011-12-22 15:19:25 +00:00
|
|
|
*/
|
2013-01-10 11:10:41 +00:00
|
|
|
float *content() { return _data; }
|
2011-12-22 15:19:25 +00:00
|
|
|
|
|
|
|
/**
|
2013-01-10 11:10:41 +00:00
|
|
|
* Play state
|
2011-12-22 15:19:25 +00:00
|
|
|
*
|
2013-01-10 11:10:41 +00:00
|
|
|
* \return true if the packet has been played back; false otherwise
|
|
|
|
*/
|
|
|
|
bool played() const { return !_wait_for_play; }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Valid state
|
2011-12-22 15:19:25 +00:00
|
|
|
*
|
2013-01-10 11:10:41 +00:00
|
|
|
* The valid state of a packet describes that the packet has been
|
|
|
|
* processed by the server even though it may not have been played back
|
|
|
|
* if the packet is invalid. For example, if a server is a filter, the
|
|
|
|
* audio may not have been processed by the output driver.
|
|
|
|
*
|
|
|
|
* \return true if packet has *not* been processed yet; false otherwise
|
2011-12-22 15:19:25 +00:00
|
|
|
*/
|
2013-01-10 11:10:41 +00:00
|
|
|
bool valid() const { return _valid; }
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2013-01-10 11:10:41 +00:00
|
|
|
Genode::size_t size() const { return sizeof(_data); }
|
2011-12-22 15:19:25 +00:00
|
|
|
|
|
|
|
|
2013-01-10 11:10:41 +00:00
|
|
|
/**********************************************
|
|
|
|
** Intended to be called by the server side **
|
|
|
|
**********************************************/
|
2011-12-22 15:19:25 +00:00
|
|
|
|
2013-01-10 11:10:41 +00:00
|
|
|
/**
|
|
|
|
* Invalidate packet, thus marking it as processed
|
|
|
|
*/
|
|
|
|
void invalidate() { _valid = false; }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Mark a packet as played
|
|
|
|
*/
|
|
|
|
void mark_as_played() { _wait_for_play = false; }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The audio-stream object containing packets
|
|
|
|
*
|
|
|
|
* The stream object is created upon session creation. The server will allocate
|
|
|
|
* a dataspace on the client's account. The client session will then request
|
|
|
|
* this dataspace and both client and server will attach it in their respective
|
|
|
|
* protection domain. After that, the stream pointer within a session will be
|
|
|
|
* pointed to the attached dataspace on both sides. Because the 'Stream' object
|
|
|
|
* is backed by shared memory, its constructor is never supposed to be called.
|
|
|
|
*/
|
|
|
|
class Audio_out::Stream
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
|
|
|
|
unsigned _pos; /* current playback position */
|
|
|
|
unsigned _tail; /* tail pointer used for allocations */
|
|
|
|
Packet _buf[QUEUE_SIZE]; /* packet queue */
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Exceptions
|
|
|
|
*/
|
|
|
|
class Alloc_failed { };
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Current audio playback position
|
|
|
|
*
|
|
|
|
* \return position
|
|
|
|
*/
|
|
|
|
unsigned pos() const { return _pos; }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve next packet for given packet
|
|
|
|
*
|
|
|
|
* \param packet preceding packet
|
|
|
|
*
|
|
|
|
* \return Successor of packet or successor of current position if
|
|
|
|
* 'packet' is zero
|
|
|
|
*/
|
|
|
|
Packet *next(Packet *packet = 0)
|
|
|
|
{
|
|
|
|
return packet ? get(packet_position(packet) + 1) : get(pos() + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieves the position of a given packet in the stream queue
|
|
|
|
*
|
|
|
|
* \param packet a packet
|
|
|
|
*
|
|
|
|
* \return position in stream queue
|
|
|
|
*/
|
|
|
|
unsigned packet_position(Packet *packet) { return packet - &_buf[0]; }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if stream queue is full/empty
|
|
|
|
*/
|
|
|
|
bool empty() const
|
|
|
|
{
|
|
|
|
bool valid = false;
|
|
|
|
for (int i = 0; i < QUEUE_SIZE; i++)
|
|
|
|
valid |= _buf[i].valid();
|
|
|
|
|
|
|
|
return !valid;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool full() const { return (_tail + 1) % QUEUE_SIZE == _pos; }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve an audio at given position
|
|
|
|
*
|
|
|
|
* \param pos position in stream
|
|
|
|
*
|
|
|
|
* \return Audio_out packet
|
|
|
|
*/
|
|
|
|
Packet *get(unsigned pos) { return &_buf[pos % QUEUE_SIZE]; }
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Allocate a packet in stream
|
|
|
|
*
|
|
|
|
* \return Packet
|
|
|
|
* \throw Alloc_failed when stream queue is full
|
|
|
|
*/
|
|
|
|
Packet *alloc()
|
|
|
|
{
|
|
|
|
if (full())
|
|
|
|
throw Alloc_failed();
|
|
|
|
|
|
|
|
unsigned pos = _tail;
|
|
|
|
_tail = (_tail + 1) % QUEUE_SIZE;
|
|
|
|
|
|
|
|
Packet *p = get(pos);
|
|
|
|
p->_alloc();
|
|
|
|
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reset stream queue
|
|
|
|
*
|
|
|
|
* This means that allocation will start at current queue position.
|
|
|
|
*/
|
|
|
|
void reset() { _tail = _pos; }
|
|
|
|
|
|
|
|
|
|
|
|
/**********************************************
|
|
|
|
** Intended to be called by the server side **
|
|
|
|
***********************************************/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set current stream position
|
|
|
|
*
|
|
|
|
* \param pos current position
|
|
|
|
*/
|
|
|
|
void pos(unsigned p) { _pos = p; }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Increment current stream position by one
|
|
|
|
*/
|
|
|
|
void increment_position() { _pos = (_pos + 1) % QUEUE_SIZE; }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Audio_out session base
|
|
|
|
*/
|
|
|
|
class Audio_out::Session : public Genode::Session
|
|
|
|
{
|
|
|
|
protected:
|
|
|
|
|
|
|
|
Stream *_stream;
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
static const char *service_name() { return "Audio_out"; }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return stream of this session, see 'Stream' above
|
|
|
|
*/
|
|
|
|
Stream *stream() const { return _stream; }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Start playback (alloc and submit packets after calling 'start')
|
|
|
|
*/
|
|
|
|
virtual void start() = 0;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Stop playback
|
|
|
|
*/
|
|
|
|
virtual void stop() = 0;
|
|
|
|
|
|
|
|
|
|
|
|
/*************
|
|
|
|
** Signals **
|
|
|
|
*************/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The 'progress' signal is sent from the server to the client if a
|
|
|
|
* packet has been played.
|
|
|
|
*
|
|
|
|
* See: client.h, connection.h
|
|
|
|
*/
|
|
|
|
virtual void progress_sigh(Genode::Signal_context_capability sigh) = 0;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The 'alloc' signal is sent from the server to the client when the
|
|
|
|
* stream queue leaves the 'full' state.
|
|
|
|
*
|
|
|
|
* See: client.h, connection.h
|
|
|
|
*/
|
|
|
|
virtual void alloc_sigh(Genode::Signal_context_capability sigh) = 0;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The 'data_avail' signal is sent from the client to the server if the
|
|
|
|
* stream queue leaves the 'empty' state.
|
|
|
|
*/
|
|
|
|
virtual Genode::Signal_context_capability data_avail_sigh() = 0;
|
|
|
|
|
|
|
|
GENODE_RPC(Rpc_start, void, start);
|
|
|
|
GENODE_RPC(Rpc_stop, void, stop);
|
|
|
|
GENODE_RPC(Rpc_dataspace, Genode::Dataspace_capability, dataspace);
|
|
|
|
GENODE_RPC(Rpc_progress_sigh, void, progress_sigh, Genode::Signal_context_capability);
|
|
|
|
GENODE_RPC(Rpc_alloc_sigh, void, alloc_sigh, Genode::Signal_context_capability);
|
|
|
|
GENODE_RPC(Rpc_data_avail_sigh, Genode::Signal_context_capability, data_avail_sigh);
|
|
|
|
|
|
|
|
GENODE_RPC_INTERFACE(Rpc_start, Rpc_stop, Rpc_dataspace, Rpc_progress_sigh,
|
|
|
|
Rpc_alloc_sigh, Rpc_data_avail_sigh);
|
|
|
|
};
|
2011-12-22 15:19:25 +00:00
|
|
|
|
|
|
|
#endif /* _INCLUDE__AUDIO_OUT_SESSION__AUDIO_OUT_SESSION_H_ */
|