Emery Hemingway
5e8bc9ef51
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
253 lines
5.3 KiB
C++
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_ */ |