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.
arts/flow/synth_play_impl.cc

286 lines
6.9 KiB

/*
Copyright (C) 2000 Stefan Westerfeld
stefan@space.twc.de
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., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "artsflow.h"
#include "debug.h"
#include "convert.h"
#include "objectmanager.h"
#include "audiosubsys.h"
#include "dispatcher.h"
#include "iomanager.h"
#include "flowsystem.h"
#include "stdsynthmodule.h"
#include <stdio.h>
#include <iostream>
using namespace std;
using namespace Arts;
namespace Arts {
class Synth_PLAY_impl : virtual public Synth_PLAY_skel,
virtual public ASProducer,
virtual public StdSynthModule,
virtual public IONotify,
virtual public TimeNotify
{
protected:
AudioSubSystem *as;
bool haveSubSys;
/*
* these are to prevent the following situation
* 1) audio subsystem needs more data
* 2) calculation is started
* 3) somehow, some module makes a synchronous invocation to the outside
* world and waits for the result
* 4) since the audio subsystem still needs data, and since we are in an
* idle state now, another calculation will be started, which will of
* course fail due to reentrancy
* 5) repeat 4) until result is there => lots of wasted CPU cycles (when
* running with realtime priority: system freeze)
*/
bool inProgress; // we are just doing some calculations
bool restartIOHandling; // I/O handlers removed upon reaching 4: restart
int audioReadFD;
int audioWriteFD;
bool audioOpen;
typedef unsigned char uchar;
unsigned char *outblock;
unsigned long maxsamples;
unsigned long channels;
int format;
int bits;
bool retryOpen;
public:
/*
* functions from the SynthModule interface (which is inherited by
* SynthPlay)
*/
void streamInit() {
as = AudioSubSystem::the();
maxsamples = 0;
outblock = 0;
retryOpen = false;
audioOpen = false;
inProgress = false;
haveSubSys = as->attachProducer(this);
if(!haveSubSys)
{
arts_info("Synth_PLAY: audio subsystem is already used");
return;
}
audioOpen = as->open();
if(!audioOpen)
{
if(Dispatcher::the()->flowSystem()->suspended())
{
arts_info("/dev/dsp currently unavailable (retrying)");
Dispatcher::the()->ioManager()->addTimer(1000, this);
retryOpen = true;
}
else
{
arts_info("Synth_PLAY: audio subsystem init failed");
arts_info("ASError = %s",as->error());
}
audioReadFD = audioWriteFD = -1;
}
else
{
audioReadFD = as->selectReadFD();
audioWriteFD = as->selectWriteFD();
}
channels = as->channels();
format = as->format();
bits = as->bits();
arts_debug("audio format is %d Hz, %d bits, %d channels",
as->samplingRate(), bits, channels);
}
void notifyTime() {
assert(retryOpen);
audioOpen = as->open();
if(audioOpen)
{
audioReadFD = as->selectReadFD();
audioWriteFD = as->selectWriteFD();
streamStart();
arts_info("/dev/dsp ok");
Dispatcher::the()->ioManager()->removeTimer(this);
retryOpen = false;
}
}
void streamStart() {
IOManager *iom = Dispatcher::the()->ioManager();
if(audioReadFD >= 0)
iom->watchFD(audioReadFD, IOType::read|IOType::except, this);
if(audioWriteFD >= 0)
iom->watchFD(audioWriteFD, IOType::write|IOType::except, this);
}
void streamEnd() {
if(retryOpen)
Dispatcher::the()->ioManager()->removeTimer(this);
arts_debug("Synth_PLAY: closing audio fd");
if(audioReadFD >= 0 || audioWriteFD >= 0)
{
IOManager *iom = Dispatcher::the()->ioManager();
iom->remove(this,IOType::all);
audioReadFD = audioWriteFD = -1;
}
AudioSubSystem::the()->detachProducer();
if(outblock)
{
delete[] outblock;
outblock = 0;
}
}
AutoSuspendState autoSuspend()
{
return static_cast<AutoSuspendState>(asSuspendStop|asConsumer);
}
void calculateBlock(unsigned long samples)
{
// no audio subsystem, no play
if(!as->running() || !haveSubSys) return;
if(samples > maxsamples)
{
maxsamples = samples;
if(outblock) delete[] outblock;
outblock = new uchar[maxsamples * channels * ( format & ( 8 | 16 | 32 ) ) / 8];
}
assert(channels);
arts_assert(format == 8 || format == 16 || format == 17 || format == 32 );
if(channels == 1)
{
if(format == 8)
convert_mono_float_8(samples,invalue_left,outblock);
else if(format == 16)
convert_mono_float_16le(samples,invalue_left,outblock);
else if(format == 17)
convert_mono_float_16be(samples,invalue_left,outblock);
else if(format == 32)
{
as->write( invalue_left, samples );
return;
}
}
else if(channels == 2)
{
if(format == 8)
convert_stereo_2float_i8(samples,invalue_left,invalue_right,
outblock);
else if(format == 16)
convert_stereo_2float_i16le(samples,invalue_left,invalue_right,
outblock);
else if(format == 17)
convert_stereo_2float_i16be(samples,invalue_left,invalue_right,
outblock);
else if(format == 32)
{
float * buffer = ( float* )outblock;
float * end = invalue_left + samples;
while( invalue_left < end )
{
*buffer++ = *invalue_left++;
*buffer++ = *invalue_right++;
}
as->write( outblock, 2 * samples * sizeof( float ) );
return;
}
}
else arts_warning("channels != 1 && channels != 2?");
as->write(outblock,channels * (bits / 8) * samples);
}
/**
* notifyIO from the IONotify interface (IOManager)
*/
void notifyIO(int fd, int type)
{
arts_return_if_fail(as->running());
assert(fd == audioReadFD || fd == audioWriteFD);
if(inProgress)
{
if(!restartIOHandling)
{
// prevent lots of retries - we just can't do calculations
// now, so we need to wait until the situation has resolved
Dispatcher::the()->ioManager()->remove(this,IOType::all);
restartIOHandling = true;
}
return;
}
// convert iomanager notification types to audiosubsys notification
int asType = 0;
if(type & IOType::read) asType |= AudioSubSystem::ioRead;
if(type & IOType::write) asType |= AudioSubSystem::ioWrite;
assert(asType != 0);
restartIOHandling = false;
inProgress = true;
as->handleIO(asType);
inProgress = false;
if(restartIOHandling) streamStart();
}
/**
* needmore from the ASProducer interface (AudioSubSystem)
*/
void needMore()
{
_node()->requireFlow();
}
};
REGISTER_IMPLEMENTATION(Synth_PLAY_impl);
}
// vim: sw=4 ts=4 noet