Module: raudio.cpp

class RAudioProducer
.h

constructorRAudioProducer()
destructor~RAudioProducer()
SetSampleSpeedvoid SetSampleSpeed(int n)
SetCurrentSpeedvoid SetCurrentSpeed(int n)

Set speed; it will be played relative to the sample speed

LoadSamplebool LoadSample(cstring fileName)
SetBuffervoid SetBuffer(void *_buffer,int frames)

Use audio buffer to pick sample frames from

SetSamplevoid SetSample(QSample *smp)

Use loaded 'smp'

ProduceFramesvoid ProduceFrames(void *dBuffer,int frames)
SetVolumevoid SetVolume(int n)

class RAudio
.h

constructorRAudio()
destructor~RAudio()
Loadbool Load(cstring fileName)
Createbool Create()
AddProducerbool AddProducer(RAudioProducer *prod)
RemoveProducerbool RemoveProducer(RAudioProducer *prod)

Removes a producer from the audio system
Returns TRUE if producer was found & removed

Runvoid 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
}