genode-ehmry/repos/os/include/audio/stream.h
Emery Hemingway 5e8bc9ef51 WiP! Audio session redesign
Define a unified packet-buffer format for both Audio_out and Audio_in
sessions. This allows an Audio_out and an Audio_in client to exchange
packets with a common buffer and lexicon.

The sessions have also been stripped down to a minimum. The
'progress' and 'data_avail' signal handlers have been replaced with a
single 'progress' signal sent by Audio_in and received by Audio_in.

The Audio_in session has a reset signal delivered to the client to
indicate that the progress signal handler has been replaced (dubious).

These changes reflect a different relationship between the two sessions.
Drivers that play audio are now Audio_in clients and drive the rate that
packets can be consumed by sending progress signals.

Ref #2858
2019-06-28 16:17:03 +02:00

253 lines
5.3 KiB
C++

/*
* \brief Common definitions for Audio_* sessions
* \author Sebastian Sumpf
* \author Josef Soentgen
* \author Emery Hemingway
* \date 2018-06-05
*
* The Audio_out and Audio_in session use a common layout for
* shared-memory packet queues. This file defines that layout
* and convenience classes for each side of the audio pipeline.
*/
/*
* 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.
*/
#ifndef _INCLUDE__AUDIO__STREAM_H_
#define _INCLUDE__AUDIO__STREAM_H_
#include <audio/parameters.h>
#include <util/string.h>
namespace Audio {
class Packet;
class Stream;
struct Stream_source;
struct Stream_sink;
}
/**
* Audio packet containing frames
*/
class Audio::Packet
{
friend class Stream;
friend class Stream_sink;
friend class Stream_source;
protected:
float _data[PERIOD];
bool _active = false;
public:
Packet() { }
void recorded() { _active = true; }
void played() { _active = false; }
/**
* 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
*/
void content(float *data, Genode::size_t samples)
{
using namespace Genode;
memcpy(_data, data, (samples > PERIOD ? (size_t)PERIOD : samples) * SAMPLE_SIZE);
if (samples < PERIOD)
memset(_data + samples, 0, (PERIOD - samples) * SAMPLE_SIZE);
}
/**
* Get content
*
* \return pointer to frame data
*/
float *content() { return _data; }
Genode::size_t size() const { return sizeof(_data); }
};
/**
* 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::Stream
{
friend class Stream_source;
friend class Stream_sink;
private:
Genode::uint32_t _record_pos { 0 }; /* current record (server) position */
Genode::uint32_t _play_pos { 0 }; /* current playback (client) position */
Packet _buf[QUEUE_SIZE]; /* packet queue */
public:
/**
* Current record position
*
* \return record position
*/
unsigned record_pos() const { return _record_pos; }
/**
* Current playback position
*
* \return playback position
*/
unsigned play_pos() const { return _play_pos; }
/**
* Reset stream queue
*
* This means that plaback will start at current record position.
*/
void reset() { _play_pos = _record_pos; }
/**
* 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) const { return packet - &_buf[0]; }
/**
* Retrieve an packet at given position
*
* \param pos position in stream
*
* \return Audio_in packet
*/
Packet *get(unsigned pos) { return &_buf[pos % QUEUE_SIZE]; }
/**
* Check if stream queue is empty
*/
bool empty() const {
return (_play_pos == _record_pos); }
/**
* Check if stream queue is full
*/
bool full() const {
return ((_record_pos+1) % QUEUE_SIZE) == _play_pos; }
int queued() const
{
int gap = _record_pos < _play_pos
? _record_pos+QUEUE_SIZE-_play_pos
: _record_pos-_play_pos;
return gap;
}
};
/**
* Stream object for retreiving audio packets
*/
struct Audio::Stream_source : Stream
{
template <typename PROC>
void play(int pos, PROC const &proc)
{
Packet &p = *get(pos);
if (p._active) {
proc(p);
p._active = false;
_play_pos = (pos+1) % QUEUE_SIZE;
}
}
/**
* Set current stream position
*
* \param pos current position
*/
void pos(unsigned p) { _play_pos = p; }
/**
* Increment current stream position by one
*/
void increment_position() {
_play_pos = (_play_pos + 1) % QUEUE_SIZE; }
/* position the play position on the first unplayed packet */
void seek()
{
unsigned pos = _record_pos;
for (unsigned i = 0; i < QUEUE_SIZE; ++i) {
pos = (pos+1) % QUEUE_SIZE;
if (_buf[pos]._active)
break;
}
}
};
/**
* Stream object for submitting audio packets
*/
struct Audio::Stream_sink : Stream
{
friend class Stream;
template <typename PROC>
void record(int pos, PROC const &proc)
{
Packet &p = *get(pos);
if (!p._active) {
proc(p);
p._active = true;
_record_pos = (pos+1) % QUEUE_SIZE;
}
}
/**
* Set current stream position
*
* \param pos current position
*/
void pos(unsigned p) { _record_pos = p; }
/**
* Increment current stream position by one
*/
void increment_position() {
_record_pos = (_record_pos + 1) % QUEUE_SIZE; }
/* position the play position on the first unrecorded packet */
void seek()
{
unsigned pos = _play_pos;
for (unsigned i = 0; i < QUEUE_SIZE; ++i) {
pos = (pos+1) % QUEUE_SIZE;
if (!_buf[pos]._active)
break;
}
}
};
#endif /* _INCLUDE__AUDIO__STREAM_H_ */