class RAudioProducer | .h |
constructor | RAudioProducer() |
destructor | ~RAudioProducer() |
SetSampleSpeed | void SetSampleSpeed(int n) |
SetCurrentSpeed | void SetCurrentSpeed(int n) Set speed; it will be played relative to the sample speed |
LoadSample | bool LoadSample(cstring fileName) |
SetBuffer | void SetBuffer(void *_buffer,int frames) Use audio buffer to pick sample frames from |
SetSample | void SetSample(QSample *smp) Use loaded 'smp' |
ProduceFrames | void ProduceFrames(void *dBuffer,int frames) |
SetVolume | void SetVolume(int n) |
class RAudio | .h |
constructor | RAudio() |
destructor | ~RAudio() |
Load | bool Load(cstring fileName) |
Create | bool Create() |
AddProducer | bool AddProducer(RAudioProducer *prod) |
RemoveProducer | bool RemoveProducer(RAudioProducer *prod) Removes a producer from the audio system Returns TRUE if producer was found & removed |
Run | void Run(float time) |
/*
* RAudio - audio handling for multiple objects/streams
* 17-08-00: Created!
* 21-02-01: Finally fixed audio distortion on SGI.
* (c) Ruud van Gaal
*/
#include <racer/racer.h>
#include <qlib/debug.h>
#pragma hdrstop
#include <qlib/info.h>
DEBUG_ENABLE
#define INI_NAME "audio.ini"
// Fixed volume for SGI (256 is max volume)
#define FIXEDVOL 40
/********************
* An Audio producer *
********************/
#undef DBG_CLASS
#define DBG_CLASS "RAudioProducer"
RAudioProducer::RAudioProducer()
{
flags=ENABLED;
smp=0;
sbuf=0;
speedSample=speedCurrent=1;
buffer=0;
curPos=0;
delta=0;
vol=256;
}
RAudioProducer::~RAudioProducer()
{
if(flags&PRIVATE_SMP)
{
delete smp;
}
if(sbuf)delete sbuf;
}
void RAudioProducer::SetSampleSpeed(int n)
{
speedSample=n;
}
void RAudioProducer::SetCurrentSpeed(int n)
// Set speed; it will be played relative to the sample speed
{
speedCurrent=n;
#ifdef WIN32
// Set frequency of playing sample
if(!sbuf)return;
// Original frequency
int orgFreq,curFreq;
orgFreq=22050;
// Current frequency
curFreq=22050*speedCurrent/speedSample;
if(curFreq<1000)curFreq=1000;
sbuf->SetFrequency(curFreq);
#endif
}
bool RAudioProducer::LoadSample(cstring fileName)
{
smp=new QSample(fileName);
if(!smp->IsOK())
{
qwarn("RAudioProducer: Can't load sample '%s'\n",fileName);
delete smp; smp=0;
return FALSE;
}
#ifdef WIN32
sbuf=new QDXSoundBuffer(smp,QDXSoundBuffer::IS3D,
QDXSoundBuffer::ALG_NO_VIRTUALIZATION);
if(!sbuf->IsCreated())
qwarn("RAudioProducer:LoadSample(); couldn't create QDXSoundBuffer");
sbuf->EnableLoop();
sbuf->Play();
#else
// SGI
SetSample(smp);
#endif
flags|=PRIVATE_SMP;
return TRUE;
}
void RAudioProducer::SetBuffer(void *_buffer,int frames)
// Use audio buffer to pick sample frames from
{
buffer=_buffer;
bufferSize=frames;
curPos=0;
}
void RAudioProducer::SetSample(QSample *smp)
// Use loaded 'smp'
{
int size;
size=smp->GetNoofFrames();
// Assuming 22050Hz, 8-bit, 1 channel
SetBuffer(smp->GetBuffer(),size);
//QQuickSave("sample.dmp",smp->GetBuffer(),size);
}
void RAudioProducer::ProduceFrames(void *dBuffer,int frames)
{
#ifdef WIN32
// DirectSound does producing in a separate (system) thread/process
#else
// SGI simple interpolation mechanism
int i;
unsigned char *s;
signed char *d;
int v,vd;
// Any sample present?
if(!smp)return;
d=(signed char*)dBuffer;
s=(unsigned char*)buffer;
for(i=0;i<frames;i++,d++)
{
//qdbg("i=%d, curPos=%d, d=%p, s=%p\n",i,curPos,d,s);
//*d=*d+((s[curPos])*vol/256)^0x80;
//v=(s[curPos]-128);
v=s[curPos];
//if(i<10)qdbg("vOrg=%d, vol=%d, v=%d\n",v,vol,v*vol/256);
//if(i<10)qdbg("vOrg=$%x\n",v);
v-=128;
v=v*vol/256;
// Bad sampling can be LOUD!
v=v*FIXEDVOL/256;
//v=((v-128)*vol/256)+128;
//if(i<10)qdbg("vol=%d, v=$%x\n",vol,v);
//if(vol<90)v=0;
//if(i<10)qdbg("*dOrg=$%x\n",*d);
// Cutoff adding to avoid distortion
vd=*d;
if(vd+v>127)*d=127;
else if(vd+v<-128)*d=-128;
else
{ // Normal add
*d=*d+v;
}
//if(i<10)qdbg("*d=$%x\n",*d);
delta+=speedCurrent;
while(delta>speedSample)
{ delta-=speedSample;
curPos++;
}
// Modulo (but faster)
while(curPos>=bufferSize)
curPos-=bufferSize;
}
#endif
}
void RAudioProducer::SetVolume(int n)
{
vol=n;
#ifdef WIN32
if(sbuf)sbuf->SetVolume(-10000+40*n);
#endif
}
/**************************
* The entire audio system *
**************************/
#undef DBG_CLASS
#define DBG_CLASS "RAudio"
RAudio::RAudio()
{
producers=0;
port=0;
frequency=0;
bits=0;
channels=0;
Load(INI_NAME);
}
RAudio::~RAudio()
{
}
bool RAudio::Load(cstring fileName)
{
QInfo info(RFindFile(fileName));
frequency=info.GetInt("output.frequency");
bits=info.GetInt("output.bits");
channels=info.GetInt("output.channels");
return Create();
}
bool RAudio::Create()
{
#ifdef WIN32
dxSound=new QDXSound();
if(!dxSound->Open())
{ qerr("Can't open DirectSound");
return FALSE;
}
return TRUE;
#else
// SGI
port=new QAudioPort();
port->SetWidth(bits/8);
port->SetChannels(channels);
port->SetQueueSize(100000);
if(!port->Open())
return FALSE;
return TRUE;
#endif
}
bool RAudio::AddProducer(RAudioProducer *prod)
{
if(producers==MAX_PRODUCER)
return FALSE;
producer[producers]=prod;
producers++;
return TRUE;
}
bool RAudio::RemoveProducer(RAudioProducer *prod)
// Removes a producer from the audio system
// Returns TRUE if producer was found & removed
{
int i;
for(i=0;i<producers;i++)
{
if(prod==producer[i])
{
// Shift array
for(;i<producers-1;i++)
{
producer[i]=producer[i+1];
}
producers--;
return TRUE;
}
}
// Producer was not found
return FALSE;
}
void RAudio::Run(float time)
{
#ifdef WIN32
// DirectSound is generating audio
#else
static void *bufAudio;
int i,nFrames;
int maxFrames=10000;
if(!bufAudio)
{ bufAudio=qcalloc(maxFrames);
}
// Let producers drop their audio in the buffer
nFrames=(int)(time*(float)frequency);
//nFrames*=2;
if(nFrames>maxFrames)
nFrames=maxFrames;
//qdbg("time=%f, freq=%d => frames=%d\n",time,frequency,nFrames);
memset(bufAudio,0,nFrames); // Channels, bits etc
for(i=0;i<producers;i++)
{
producer[i]->ProduceFrames(bufAudio,nFrames);
}
// Pass mixed audio onto OS audio system
// Note: Samps!=Frames (so this call is bad)
if(port->GetFilled()==0)
{ //qdbg("** Underflow in RAudio\n");
}
//qdbg("RAudio: filled=%d, fillable=%d\n",port->GetFilled(),port->GetFillable());
port->WriteSamps(bufAudio,nFrames);
// Assume a framerate of about 20fps
int minFrames=frequency/20;
if(port->GetFilled()<minFrames)
{ // Getting low on fill here; try not to get drops
// Don't fill in more than we've got
if(nFrames<minFrames)
{ minFrames=nFrames;
}
// Repeat some audio; it will glitch, but less than a drop
port->WriteSamps(bufAudio,minFrames);
}
#endif
}