You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
806 lines
18 KiB
806 lines
18 KiB
15 years ago
|
/*
|
||
|
|
||
|
Copyright (C) 2000 Stefan Westerfeld
|
||
|
stefan@space.twc.de
|
||
|
2001 Matthias Kretz
|
||
|
kretz@kde.org
|
||
|
|
||
|
This library is free software; you can redistribute it and/or
|
||
|
modify it under the terms of the GNU Library General Public
|
||
|
License as published by the Free Software Foundation; either
|
||
|
version 2 of the License, or (at your option) any later version.
|
||
|
|
||
|
This library is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
Library General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU Library General Public License
|
||
|
along with this library; see the file COPYING.LIB. If not, write to
|
||
|
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||
|
Boston, MA 02111-1307, USA.
|
||
|
|
||
|
*/
|
||
|
|
||
|
#include "artsc.h"
|
||
|
#include "soundserver.h"
|
||
|
#include "stdsynthmodule.h"
|
||
|
|
||
|
#include <iostream>
|
||
|
#include <algorithm>
|
||
|
#include <queue>
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <cstring>
|
||
|
#include <unistd.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <math.h>
|
||
|
#include <assert.h>
|
||
|
#include "arts_export.h"
|
||
|
|
||
|
#define arts_backend_debug(x) ;
|
||
|
|
||
|
using namespace std;
|
||
|
using namespace Arts;
|
||
|
|
||
|
/**
|
||
|
* Base class for streams
|
||
|
*/
|
||
|
class Stream
|
||
|
{
|
||
|
protected:
|
||
|
SoundServer server;
|
||
|
float serverBufferTime;
|
||
|
|
||
|
bool _finished, isAttached;
|
||
|
int _samplingRate, _bits, _channels, pos;
|
||
|
string _name;
|
||
|
queue< DataPacket<mcopbyte>* > streamqueue;
|
||
|
|
||
|
int packetCount, packetCapacity;
|
||
|
int blockingIO;
|
||
|
|
||
|
/**
|
||
|
* returns the amount of bytes that will be played in a given amount of
|
||
|
* time in milliseconds
|
||
|
*/
|
||
|
int timeToBytes(float time)
|
||
|
{
|
||
|
float playSpeed = _channels * _samplingRate * _bits / 8;
|
||
|
return (int)(playSpeed * (time / 1000.0));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* returns the time in milliseconds it takes with the current parameters
|
||
|
* to play a given amount of bytes
|
||
|
*/
|
||
|
float bytesToTime(int size)
|
||
|
{
|
||
|
float playSpeed = _channels * _samplingRate * _bits / 8;
|
||
|
return (1000.0 * ((float)size) / playSpeed);
|
||
|
}
|
||
|
|
||
|
int bufferSize() {
|
||
|
return packetCount * packetCapacity;
|
||
|
}
|
||
|
|
||
|
float bufferTime() {
|
||
|
return bytesToTime(bufferSize());
|
||
|
}
|
||
|
|
||
|
int bufferSpace() {
|
||
|
int space = 0;
|
||
|
|
||
|
attach();
|
||
|
|
||
|
/* make sure that our information is up-to-date */
|
||
|
Dispatcher::the()->ioManager()->processOneEvent(false);
|
||
|
|
||
|
if(!streamqueue.empty())
|
||
|
{
|
||
|
space += packetCapacity - pos; /* the first, half filled packet */
|
||
|
|
||
|
if(streamqueue.size() > 1) /* and the other, empty packets */
|
||
|
space += (streamqueue.size()-1)*packetCapacity;
|
||
|
}
|
||
|
return space;
|
||
|
}
|
||
|
|
||
|
int setBufferSize(int size)
|
||
|
{
|
||
|
/* don't change sizes when already streaming */
|
||
|
if(isAttached)
|
||
|
return ARTS_E_NOIMPL;
|
||
|
|
||
|
/*
|
||
|
* these parameters are usually a bad idea ;-) however we have to start
|
||
|
* somewhere, and maybe in two years, with a highly optimized kernel
|
||
|
* this is possible - for now, don't request the impossible or don't
|
||
|
* complain if it doesn't work
|
||
|
*/
|
||
|
packetCount = 3;
|
||
|
packetCapacity = 128;
|
||
|
|
||
|
/*
|
||
|
* - do not configure stream buffers smaller than the server
|
||
|
* recommended value
|
||
|
* - try to get more or less close to the value the application
|
||
|
* wants
|
||
|
*/
|
||
|
int needSize = max(size, timeToBytes(server.minStreamBufferTime()));
|
||
|
|
||
|
while(bufferSize() < needSize)
|
||
|
{
|
||
|
packetCount++;
|
||
|
if(packetCount == 8)
|
||
|
{
|
||
|
packetCount /= 2;
|
||
|
packetCapacity *= 2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return bufferSize();
|
||
|
}
|
||
|
|
||
|
int packetSettings()
|
||
|
{
|
||
|
int settings = 0;
|
||
|
|
||
|
int cap = packetCapacity;
|
||
|
while(cap > 1)
|
||
|
{
|
||
|
settings++;
|
||
|
cap /= 2;
|
||
|
}
|
||
|
|
||
|
settings |= packetCount << 16;
|
||
|
return settings;
|
||
|
}
|
||
|
|
||
|
int setPacketSettings(int settings)
|
||
|
{
|
||
|
/* don't change sizes when already streaming */
|
||
|
if(isAttached)
|
||
|
return ARTS_E_NOIMPL;
|
||
|
|
||
|
packetCount = settings >> 16;
|
||
|
|
||
|
packetCapacity = 1;
|
||
|
int c = settings & 0xffff;
|
||
|
while(c > 0) {
|
||
|
packetCapacity *= 2;
|
||
|
c--;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* - do not configure stream buffers smaller than the server
|
||
|
* recommended value
|
||
|
* - keep the packetSize the applications specified
|
||
|
*/
|
||
|
int needSize = timeToBytes(server.minStreamBufferTime());
|
||
|
|
||
|
while(bufferSize() < needSize)
|
||
|
packetCount++;
|
||
|
|
||
|
return packetSettings();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* the stream has to attach itself
|
||
|
*/
|
||
|
virtual void attach() = 0;
|
||
|
|
||
|
public:
|
||
|
Stream(SoundServer server, int rate, int bits, int channels,
|
||
|
string name) : server(server), _finished(false), isAttached(false),
|
||
|
_samplingRate(rate), _bits(bits), _channels(channels), pos(0),
|
||
|
_name(name)
|
||
|
{
|
||
|
serverBufferTime = server.serverBufferTime();
|
||
|
stream_set(ARTS_P_BUFFER_SIZE,64*1024);
|
||
|
stream_set(ARTS_P_BLOCKING,1);
|
||
|
}
|
||
|
virtual ~Stream()
|
||
|
{
|
||
|
//
|
||
|
}
|
||
|
|
||
|
virtual int stream_set(arts_parameter_t param, int value)
|
||
|
{
|
||
|
int result;
|
||
|
|
||
|
switch(param) {
|
||
|
case ARTS_P_BUFFER_SIZE:
|
||
|
return setBufferSize(value);
|
||
|
|
||
|
case ARTS_P_BUFFER_TIME:
|
||
|
result = setBufferSize(timeToBytes(value));
|
||
|
if(result < 0) return result;
|
||
|
return (int)bufferTime();
|
||
|
|
||
|
case ARTS_P_PACKET_SETTINGS:
|
||
|
return setPacketSettings(value);
|
||
|
|
||
|
case ARTS_P_BLOCKING:
|
||
|
if(value != 0 && value != 1) return ARTS_E_NOIMPL;
|
||
|
|
||
|
blockingIO = value;
|
||
|
return blockingIO;
|
||
|
/*
|
||
|
* maybe ARTS_P_TOTAL_LATENCY _could_ be made writeable, the
|
||
|
* others are of course useless
|
||
|
*/
|
||
|
case ARTS_P_BUFFER_SPACE:
|
||
|
case ARTS_P_SERVER_LATENCY:
|
||
|
case ARTS_P_TOTAL_LATENCY:
|
||
|
case ARTS_P_PACKET_SIZE:
|
||
|
case ARTS_P_PACKET_COUNT:
|
||
|
return ARTS_E_NOIMPL;
|
||
|
}
|
||
|
return ARTS_E_NOIMPL;
|
||
|
}
|
||
|
|
||
|
virtual int stream_get(arts_parameter_t param)
|
||
|
{
|
||
|
switch(param) {
|
||
|
case ARTS_P_BUFFER_SIZE:
|
||
|
return bufferSize();
|
||
|
|
||
|
case ARTS_P_BUFFER_TIME:
|
||
|
return (int)bufferTime();
|
||
|
|
||
|
case ARTS_P_BUFFER_SPACE:
|
||
|
return bufferSpace();
|
||
|
|
||
|
case ARTS_P_PACKET_SETTINGS:
|
||
|
return packetSettings();
|
||
|
|
||
|
case ARTS_P_SERVER_LATENCY:
|
||
|
return (int)serverBufferTime;
|
||
|
|
||
|
case ARTS_P_TOTAL_LATENCY:
|
||
|
return stream_get(ARTS_P_SERVER_LATENCY)
|
||
|
+ stream_get(ARTS_P_BUFFER_TIME);
|
||
|
|
||
|
case ARTS_P_BLOCKING:
|
||
|
return blockingIO;
|
||
|
|
||
|
case ARTS_P_PACKET_SIZE:
|
||
|
return packetCapacity;
|
||
|
|
||
|
case ARTS_P_PACKET_COUNT:
|
||
|
return packetCount;
|
||
|
}
|
||
|
return ARTS_E_NOIMPL;
|
||
|
}
|
||
|
|
||
|
virtual int write(const mcopbyte * /*data*/, int /*size*/)
|
||
|
{
|
||
|
return ARTS_E_NOIMPL;
|
||
|
}
|
||
|
|
||
|
virtual int read(mcopbyte * /*data*/, int /*size*/)
|
||
|
{
|
||
|
return ARTS_E_NOIMPL;
|
||
|
}
|
||
|
|
||
|
virtual void close() = 0;
|
||
|
|
||
|
int suspend()
|
||
|
{
|
||
|
if(isAttached)
|
||
|
{
|
||
|
return server.suspend();
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int suspended()
|
||
|
{
|
||
|
if(isAttached)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
return server.suspended();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
class Receiver : public ByteSoundReceiver_skel,
|
||
|
public StdSynthModule,
|
||
|
virtual public Stream
|
||
|
{
|
||
|
/*
|
||
|
* FIXME: bsWrapper is a more or less ugly trick to be able to use
|
||
|
* this object although not using smartwrappers to access it
|
||
|
*/
|
||
|
ByteSoundReceiver bsWrapper;
|
||
|
|
||
|
protected:
|
||
|
virtual void attach()
|
||
|
{
|
||
|
if(!isAttached)
|
||
|
{
|
||
|
isAttached = true;
|
||
|
|
||
|
server.attachRecorder(bsWrapper);
|
||
|
start();
|
||
|
|
||
|
/*
|
||
|
* TODO: this processOneEvent looks a bit strange here... it is
|
||
|
* there since StdIOManager does block 5 seconds on the first
|
||
|
* arts_write if it isn't - although notifications are pending
|
||
|
*
|
||
|
* Probably the real solution is to rewrite the
|
||
|
* StdIOManager::processOneEvent function. (And maybe drop the
|
||
|
* assumption that aRts will not block when an infinite amount
|
||
|
* of notifications is pending - I mean: will it ever happen?)
|
||
|
*/
|
||
|
Dispatcher::the()->ioManager()->processOneEvent(false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public:
|
||
|
Receiver(SoundServer server, int rate, int bits, int channels,
|
||
|
string name) : Stream( server, rate, bits, channels, name)
|
||
|
{
|
||
|
bsWrapper = ByteSoundReceiver::_from_base(this);
|
||
|
}
|
||
|
|
||
|
virtual ~Receiver() {
|
||
|
//
|
||
|
}
|
||
|
|
||
|
long samplingRate() { return _samplingRate; }
|
||
|
long channels() { return _channels; }
|
||
|
long bits() { return _bits; }
|
||
|
bool finished() { return _finished; }
|
||
|
string title() { return _name; }
|
||
|
|
||
|
void process_indata(DataPacket<mcopbyte> *packet)
|
||
|
{
|
||
|
streamqueue.push(packet);
|
||
|
}
|
||
|
|
||
|
void close()
|
||
|
{
|
||
|
if(isAttached)
|
||
|
{
|
||
|
/* remove all packets from the streamqueue */
|
||
|
while(!streamqueue.empty())
|
||
|
{
|
||
|
DataPacket<mcopbyte> *packet = streamqueue.front();
|
||
|
packet->processed();
|
||
|
streamqueue.pop();
|
||
|
}
|
||
|
|
||
|
server.detachRecorder(bsWrapper);
|
||
|
}
|
||
|
// similar effect like "delete this;"
|
||
|
bsWrapper = ByteSoundReceiver::null();
|
||
|
}
|
||
|
|
||
|
int read(mcopbyte *data, int size)
|
||
|
{
|
||
|
attach();
|
||
|
|
||
|
int remaining = size;
|
||
|
while(remaining)
|
||
|
{
|
||
|
if(blockingIO)
|
||
|
{
|
||
|
/* C API blocking style read */
|
||
|
while(streamqueue.empty())
|
||
|
Dispatcher::the()->ioManager()->processOneEvent(true);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* non blocking I/O */
|
||
|
if(streamqueue.empty())
|
||
|
Dispatcher::the()->ioManager()->processOneEvent(false);
|
||
|
|
||
|
/* still no more packets to read? */
|
||
|
if(streamqueue.empty())
|
||
|
return size - remaining;
|
||
|
}
|
||
|
|
||
|
/* get a packet */
|
||
|
DataPacket<mcopbyte> *packet = streamqueue.front();
|
||
|
|
||
|
/* copy some data from there */
|
||
|
int tocopy = min(remaining,packet->size-pos);
|
||
|
memcpy(data,&packet->contents[pos],tocopy);
|
||
|
pos += tocopy;
|
||
|
data += tocopy;
|
||
|
remaining -= tocopy;
|
||
|
|
||
|
/* have we read the whole packet? then get rid of it */
|
||
|
if(pos == packet->size)
|
||
|
{
|
||
|
packet->processed();
|
||
|
streamqueue.pop();
|
||
|
pos = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* no possible error conditions */
|
||
|
return size;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
class Sender : public ByteSoundProducerV2_skel,
|
||
|
public StdSynthModule,
|
||
|
virtual public Stream
|
||
|
{
|
||
|
/*
|
||
|
* FIXME: bsWrapper is a more or less ugly trick to be able to use
|
||
|
* this object although not using smartwrappers to access it
|
||
|
*/
|
||
|
ByteSoundProducerV2 bsWrapper;
|
||
|
|
||
|
protected:
|
||
|
virtual void attach()
|
||
|
{
|
||
|
if(!isAttached)
|
||
|
{
|
||
|
isAttached = true;
|
||
|
|
||
|
server.attach(bsWrapper);
|
||
|
start();
|
||
|
|
||
|
/*
|
||
|
* TODO: this processOneEvent looks a bit strange here... it is
|
||
|
* there since StdIOManager does block 5 seconds on the first
|
||
|
* arts_write if it isn't - although notifications are pending
|
||
|
*
|
||
|
* Probably the real solution is to rewrite the
|
||
|
* StdIOManager::processOneEvent function. (And maybe drop the
|
||
|
* assumption that aRts will not block when an infinite amount
|
||
|
* of notifications is pending - I mean: will it ever happen?)
|
||
|
*/
|
||
|
Dispatcher::the()->ioManager()->processOneEvent(false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public:
|
||
|
Sender(SoundServer server, int rate, int bits, int channels,
|
||
|
string name) : Stream( server, rate, bits, channels, name)
|
||
|
{
|
||
|
bsWrapper = ByteSoundProducerV2::_from_base(this);
|
||
|
}
|
||
|
|
||
|
virtual ~Sender() {
|
||
|
//
|
||
|
}
|
||
|
|
||
|
long samplingRate() { return _samplingRate; }
|
||
|
long channels() { return _channels; }
|
||
|
long bits() { return _bits; }
|
||
|
bool finished() { return _finished; }
|
||
|
string title() { return _name; }
|
||
|
|
||
|
void streamStart()
|
||
|
{
|
||
|
/*
|
||
|
* start streaming
|
||
|
*/
|
||
|
outdata.setPull(packetCount, packetCapacity);
|
||
|
}
|
||
|
|
||
|
void request_outdata(DataPacket<mcopbyte> *packet)
|
||
|
{
|
||
|
streamqueue.push(packet);
|
||
|
}
|
||
|
|
||
|
void close()
|
||
|
{
|
||
|
if(isAttached)
|
||
|
{
|
||
|
if(pos != 0)
|
||
|
{
|
||
|
/* send the last half-filled packet */
|
||
|
DataPacket<mcopbyte> *packet = streamqueue.front();
|
||
|
packet->size = pos;
|
||
|
packet->send();
|
||
|
streamqueue.pop();
|
||
|
}
|
||
|
outdata.endPull();
|
||
|
|
||
|
/* remove all packets from the streamqueue */
|
||
|
while(!streamqueue.empty())
|
||
|
{
|
||
|
DataPacket<mcopbyte> *packet = streamqueue.front();
|
||
|
packet->size = 0;
|
||
|
packet->send();
|
||
|
streamqueue.pop();
|
||
|
}
|
||
|
|
||
|
server.detach(bsWrapper);
|
||
|
}
|
||
|
// similar effect like "delete this;"
|
||
|
Arts::ByteSoundProducerV2_base* x = _copy();
|
||
|
bsWrapper = ByteSoundProducerV2::null();
|
||
|
x->_release();
|
||
|
}
|
||
|
|
||
|
int write(const mcopbyte *data, int size)
|
||
|
{
|
||
|
attach();
|
||
|
|
||
|
int remaining = size;
|
||
|
while(remaining)
|
||
|
{
|
||
|
if(blockingIO)
|
||
|
{
|
||
|
/* C API blocking style write */
|
||
|
/* we're not waiting for any data here, but rather for
|
||
|
* DataPackets that we can fill */
|
||
|
while(streamqueue.empty())
|
||
|
Dispatcher::the()->ioManager()->processOneEvent(true);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* non blocking I/O */
|
||
|
if(streamqueue.empty())
|
||
|
Dispatcher::the()->ioManager()->processOneEvent(false);
|
||
|
|
||
|
/* still no more space to write? */
|
||
|
if(streamqueue.empty())
|
||
|
return size - remaining;
|
||
|
}
|
||
|
|
||
|
/* get a packet */
|
||
|
DataPacket<mcopbyte> *packet = streamqueue.front();
|
||
|
|
||
|
/* copy some data there */
|
||
|
int tocopy = min(remaining,packetCapacity-pos);
|
||
|
memcpy(&packet->contents[pos],data,tocopy);
|
||
|
pos += tocopy;
|
||
|
data += tocopy;
|
||
|
remaining -= tocopy;
|
||
|
|
||
|
/* have we filled up the packet? then send it */
|
||
|
if(pos == packetCapacity)
|
||
|
{
|
||
|
packet->size = packetCapacity;
|
||
|
packet->send();
|
||
|
streamqueue.pop();
|
||
|
pos = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* no possible error conditions */
|
||
|
return size;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
class ArtsCApi {
|
||
|
protected:
|
||
|
static ArtsCApi *instance;
|
||
|
int refcnt;
|
||
|
|
||
|
Dispatcher dispatcher;
|
||
|
SoundServer server;
|
||
|
|
||
|
ArtsCApi() : refcnt(1), server(Reference("global:Arts_SoundServer"))
|
||
|
{
|
||
|
//
|
||
|
}
|
||
|
|
||
|
public:
|
||
|
// C Api commands
|
||
|
int init() {
|
||
|
if(server.isNull())
|
||
|
return ARTS_E_NOSERVER;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int suspend() {
|
||
|
if(!server.isNull())
|
||
|
return server.suspend()? 1:0;
|
||
|
return ARTS_E_NOSERVER;
|
||
|
}
|
||
|
|
||
|
int suspended() {
|
||
|
if(!server.isNull())
|
||
|
return server.suspended()? 1:0;
|
||
|
return ARTS_E_NOSERVER;
|
||
|
}
|
||
|
|
||
|
void free() {
|
||
|
// nothing to do
|
||
|
}
|
||
|
|
||
|
arts_stream_t play_stream(int rate, int bits, int channels, const char *name)
|
||
|
{
|
||
|
if(server.isNull())
|
||
|
return 0;
|
||
|
|
||
|
return (arts_stream_t)static_cast<Stream *>(new Sender(server,rate,bits,channels,name));
|
||
|
}
|
||
|
|
||
|
arts_stream_t record_stream(int rate, int bits, int channels, const char *name)
|
||
|
{
|
||
|
if(server.isNull())
|
||
|
return 0;
|
||
|
|
||
|
return (arts_stream_t)static_cast<Stream *>(new Receiver(server,rate,bits,channels,name));
|
||
|
}
|
||
|
|
||
|
void close_stream(arts_stream_t stream)
|
||
|
{
|
||
|
if(server.isNull())
|
||
|
return;
|
||
|
|
||
|
if(!stream)
|
||
|
return;
|
||
|
|
||
|
static_cast<Stream *>(stream)->close();
|
||
|
}
|
||
|
|
||
|
int write(arts_stream_t stream, const void *data, int size)
|
||
|
{
|
||
|
if(server.isNull())
|
||
|
return ARTS_E_NOSERVER;
|
||
|
|
||
|
if(!stream)
|
||
|
return ARTS_E_NOSTREAM;
|
||
|
|
||
|
return static_cast<Stream *>(stream)->write((const mcopbyte *)data,size);
|
||
|
}
|
||
|
|
||
|
int read(arts_stream_t stream, void *data, int size)
|
||
|
{
|
||
|
if(server.isNull())
|
||
|
return ARTS_E_NOSERVER;
|
||
|
|
||
|
if(!stream)
|
||
|
return ARTS_E_NOSTREAM;
|
||
|
|
||
|
return static_cast<Stream *>(stream)->read((mcopbyte *)data,size);
|
||
|
}
|
||
|
|
||
|
int stream_set(arts_stream_t stream, arts_parameter_t param, int value)
|
||
|
{
|
||
|
if(server.isNull())
|
||
|
return ARTS_E_NOSERVER;
|
||
|
|
||
|
if(!stream)
|
||
|
return ARTS_E_NOSTREAM;
|
||
|
|
||
|
return static_cast<Stream *>(stream)->stream_set(param,value);
|
||
|
}
|
||
|
|
||
|
int stream_get(arts_stream_t stream, arts_parameter_t param)
|
||
|
{
|
||
|
if(server.isNull())
|
||
|
return ARTS_E_NOSERVER;
|
||
|
|
||
|
if(!stream)
|
||
|
return ARTS_E_NOSTREAM;
|
||
|
|
||
|
return static_cast<Stream *>(stream)->stream_get(param);
|
||
|
}
|
||
|
|
||
|
// allocation and freeing of the class
|
||
|
static ArtsCApi *the() {
|
||
|
return instance;
|
||
|
}
|
||
|
static void ref() {
|
||
|
if(!instance)
|
||
|
instance = new ArtsCApi();
|
||
|
else
|
||
|
instance->refcnt++;
|
||
|
}
|
||
|
static void release() {
|
||
|
assert(instance);
|
||
|
assert(instance->refcnt > 0);
|
||
|
instance->refcnt--;
|
||
|
if(instance->refcnt == 0)
|
||
|
{
|
||
|
delete instance;
|
||
|
instance = 0;
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
//----------------------------- static members -------------------------------
|
||
|
|
||
|
ArtsCApi *ArtsCApi::instance = 0;
|
||
|
|
||
|
//------------------ wrappers from C to C++ class ----------------------------
|
||
|
|
||
|
extern "C" ARTSC_EXPORT int arts_backend_init()
|
||
|
{
|
||
|
arts_backend_debug("arts_backend_init");
|
||
|
ArtsCApi::ref();
|
||
|
|
||
|
// if init fails, don't expect free, and don't expect that the user
|
||
|
// continues using other API functions
|
||
|
int rc = ArtsCApi::the()->init();
|
||
|
if(rc < 0) ArtsCApi::release();
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
extern "C" ARTSC_EXPORT int arts_backend_suspend()
|
||
|
{
|
||
|
if(!ArtsCApi::the()) return ARTS_E_NOINIT;
|
||
|
arts_backend_debug("arts_backend_suspend");
|
||
|
return ArtsCApi::the()->suspend();
|
||
|
}
|
||
|
|
||
|
extern "C" ARTSC_EXPORT int arts_backend_suspended()
|
||
|
{
|
||
|
if(!ArtsCApi::the()) return ARTS_E_NOINIT;
|
||
|
arts_backend_debug("arts_backend_suspended");
|
||
|
return ArtsCApi::the()->suspended();
|
||
|
}
|
||
|
|
||
|
extern "C" ARTSC_EXPORT void arts_backend_free()
|
||
|
{
|
||
|
if(!ArtsCApi::the()) return;
|
||
|
|
||
|
arts_backend_debug("arts_backend_free");
|
||
|
ArtsCApi::the()->free();
|
||
|
ArtsCApi::release();
|
||
|
}
|
||
|
|
||
|
extern "C" ARTSC_EXPORT arts_stream_t arts_backend_play_stream(int rate, int bits, int channels, const char *name)
|
||
|
{
|
||
|
if(!ArtsCApi::the()) return 0;
|
||
|
|
||
|
arts_backend_debug("arts_backend_play_stream");
|
||
|
return ArtsCApi::the()->play_stream(rate,bits,channels,name);
|
||
|
}
|
||
|
|
||
|
extern "C" ARTSC_EXPORT arts_stream_t arts_backend_record_stream(int rate, int bits, int channels, const char *name)
|
||
|
{
|
||
|
if(!ArtsCApi::the()) return 0;
|
||
|
|
||
|
arts_backend_debug("arts_backend_record_stream");
|
||
|
return ArtsCApi::the()->record_stream(rate,bits,channels,name);
|
||
|
}
|
||
|
|
||
|
extern "C" ARTSC_EXPORT void arts_backend_close_stream(arts_stream_t stream)
|
||
|
{
|
||
|
if(!ArtsCApi::the()) return;
|
||
|
|
||
|
arts_backend_debug("arts_backend_close_stream");
|
||
|
ArtsCApi::the()->close_stream(stream);
|
||
|
}
|
||
|
|
||
|
extern "C" ARTSC_EXPORT int arts_backend_read(arts_stream_t stream, void *buffer, int count)
|
||
|
{
|
||
|
if(!ArtsCApi::the()) return ARTS_E_NOINIT;
|
||
|
|
||
|
arts_backend_debug("arts_backend_read");
|
||
|
return ArtsCApi::the()->read(stream,buffer,count);
|
||
|
}
|
||
|
|
||
|
extern "C" ARTSC_EXPORT int arts_backend_write(arts_stream_t stream, const void *buffer,
|
||
|
int count)
|
||
|
{
|
||
|
if(!ArtsCApi::the()) return ARTS_E_NOINIT;
|
||
|
|
||
|
arts_backend_debug("arts_backend_write");
|
||
|
return ArtsCApi::the()->write(stream,buffer,count);
|
||
|
}
|
||
|
|
||
|
extern "C" ARTSC_EXPORT int arts_backend_stream_set(arts_stream_t stream,
|
||
|
arts_parameter_t param, int value)
|
||
|
{
|
||
|
if(!ArtsCApi::the()) return ARTS_E_NOINIT;
|
||
|
|
||
|
arts_backend_debug("arts_stream_set");
|
||
|
return ArtsCApi::the()->stream_set(stream,param,value);
|
||
|
}
|
||
|
|
||
|
extern "C" ARTSC_EXPORT int arts_backend_stream_get(arts_stream_t stream,
|
||
|
arts_parameter_t param)
|
||
|
{
|
||
|
if(!ArtsCApi::the()) return ARTS_E_NOINIT;
|
||
|
|
||
|
arts_backend_debug("arts_stream_get");
|
||
|
return ArtsCApi::the()->stream_get(stream,param);
|
||
|
}
|