334 lines
8.1 KiB
C++
334 lines
8.1 KiB
C++
/*
|
|
* \brief Audio_in session interface
|
|
* \author Josef Soentgen
|
|
* \date 2015-05-08
|
|
*
|
|
* An Audio_in session corresponds to one input channel, which can be used to
|
|
* receive audio frames. Each session consists of an 'Audio_in::Stream' object
|
|
* that resides in shared memory between the client and the server. The
|
|
* 'Audio_in::Stream' in turn consists of 'Audio_in::Packet's that contain
|
|
* the actual frames. Each packet within a stream is freely accessible. When
|
|
* recording the source will allocate a new packet and override already
|
|
* recorded ones if the queue is already full. In contrast to the
|
|
* 'Audio_out::Stream' the current position pointer is updated by the client.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2015-2017 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_IN_SESSION__AUDIO_IN_SESSION_H_
|
|
#define _INCLUDE__AUDIO_IN_SESSION__AUDIO_IN_SESSION_H_
|
|
|
|
#include <base/allocator.h>
|
|
#include <dataspace/capability.h>
|
|
#include <base/rpc.h>
|
|
#include <session/session.h>
|
|
|
|
|
|
namespace Audio_in {
|
|
class Packet;
|
|
class Stream;
|
|
class Session;
|
|
|
|
enum {
|
|
QUEUE_SIZE = 431, /* buffer queue size (~5s) */
|
|
PERIOD = 512, /* samples per periode (~11.6ms) */
|
|
SAMPLE_RATE = 44100,
|
|
SAMPLE_SIZE = sizeof(float),
|
|
};
|
|
}
|
|
|
|
|
|
/**
|
|
* Audio_in packet containing frames
|
|
*/
|
|
class Audio_in::Packet
|
|
{
|
|
private:
|
|
|
|
friend class Session_client;
|
|
friend class Stream;
|
|
|
|
bool _valid;
|
|
bool _wait_for_record;
|
|
float _data[PERIOD];
|
|
|
|
void _submit() { _valid = true; _wait_for_record = true; }
|
|
void _alloc() { _wait_for_record = false; _valid = false; }
|
|
|
|
public:
|
|
|
|
Packet() : _valid(false), _wait_for_record(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)
|
|
{
|
|
Genode::memcpy(_data, data, (samples > PERIOD ? PERIOD : samples) * SAMPLE_SIZE);
|
|
|
|
if (samples < PERIOD)
|
|
Genode::memset(data + samples, 0, (PERIOD - samples) * SAMPLE_SIZE);
|
|
}
|
|
|
|
/**
|
|
* Get content
|
|
*
|
|
* \return pointer to frame data
|
|
*/
|
|
float *content() { return _data; }
|
|
|
|
/**
|
|
* Record state
|
|
*
|
|
* \return true if the packet has been recorded; false otherwise
|
|
*/
|
|
bool recorded() const { return !_wait_for_record; }
|
|
|
|
/**
|
|
* Valid state
|
|
*
|
|
* 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
|
|
*/
|
|
bool valid() const { return _valid; }
|
|
|
|
Genode::size_t size() const { return sizeof(_data); }
|
|
|
|
|
|
/**********************************************
|
|
** Intended to be called by the server side **
|
|
**********************************************/
|
|
|
|
/**
|
|
* Invalidate packet, thus marking it as processed
|
|
*/
|
|
void invalidate() { _valid = false; }
|
|
|
|
/**
|
|
* Mark a packet as recorded
|
|
*/
|
|
void mark_as_recorded() { _wait_for_record = 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_in::Stream
|
|
{
|
|
private:
|
|
|
|
unsigned _pos { 0 }; /* current record position */
|
|
unsigned _tail { 0 }; /* tail pointer used for allocations */
|
|
Packet _buf[QUEUE_SIZE]; /* packet queue */
|
|
|
|
public:
|
|
|
|
/**
|
|
* Current audio record position
|
|
*
|
|
* \return position
|
|
*/
|
|
unsigned pos() const { return _pos; }
|
|
|
|
/**
|
|
* Current tail position
|
|
*
|
|
* \return tail position
|
|
*/
|
|
unsigned tail() const { return _tail; }
|
|
|
|
/**
|
|
* 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 empty
|
|
*/
|
|
bool empty() const
|
|
{
|
|
bool valid = false;
|
|
for (int i = 0; i < QUEUE_SIZE; i++)
|
|
valid |= _buf[i].valid();
|
|
|
|
return !valid;
|
|
}
|
|
|
|
/**
|
|
* Retrieve an packet at given position
|
|
*
|
|
* \param pos position in stream
|
|
*
|
|
* \return Audio_in packet
|
|
*/
|
|
Packet *get(unsigned pos) { return &_buf[pos % QUEUE_SIZE]; }
|
|
|
|
/**
|
|
* Allocate a packet in stream
|
|
*
|
|
* \return Packet
|
|
*/
|
|
Packet *alloc()
|
|
{
|
|
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 **
|
|
**********************************************/
|
|
|
|
/**
|
|
* Submit a packet to the packet queue
|
|
*/
|
|
void submit(Packet *p) { p->_submit(); }
|
|
|
|
/**
|
|
* Check if stream queue has overrun
|
|
*/
|
|
bool overrun() const { return (_tail + 1) % QUEUE_SIZE == _pos; }
|
|
|
|
|
|
/**********************************************
|
|
** Intended to be called by the client 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_in session base
|
|
*/
|
|
class Audio_in::Session : public Genode::Session
|
|
{
|
|
protected:
|
|
|
|
Stream *_stream;
|
|
|
|
public:
|
|
|
|
/**
|
|
* \noapi
|
|
*/
|
|
static const char *service_name() { return "Audio_in"; }
|
|
|
|
enum { CAP_QUOTA = 4 };
|
|
|
|
/**
|
|
* Return stream of this session, see 'Stream' above
|
|
*/
|
|
Stream *stream() const { return _stream; }
|
|
|
|
/**
|
|
* Start recording (alloc and submit packets after calling 'start')
|
|
*/
|
|
virtual void start() = 0;
|
|
|
|
/**
|
|
* Stop recording
|
|
*/
|
|
virtual void stop() = 0;
|
|
|
|
|
|
/*************
|
|
** Signals **
|
|
*************/
|
|
|
|
/**
|
|
* The 'progress' signal is sent from the server to the client if a
|
|
* packet has been recorded.
|
|
*
|
|
* See: client.h, connection.h
|
|
*/
|
|
virtual void progress_sigh(Genode::Signal_context_capability sigh) = 0;
|
|
|
|
/**
|
|
* The 'overrun' signal is sent from the server to the client if an
|
|
* overrun has occured.
|
|
*
|
|
* See: client.h, connection.h
|
|
*/
|
|
virtual void overrun_sigh(Genode::Signal_context_capability sigh) = 0;
|
|
|
|
/**
|
|
* The 'data_avail' signal is sent from the server to the client 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_overrun_sigh, void, overrun_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_overrun_sigh,
|
|
Rpc_data_avail_sigh);
|
|
};
|
|
|
|
#endif /* _INCLUDE__AUDIO_IN_SESSION__AUDIO_IN_SESSION_H_ */
|