proteaaudio-0.10.0: cbits/proAudioRt.cpp
#include "proAudioRt.h"
#include <cmath>
#include <cstdio>
#include <climits>
#include <cstring>
#include <cstdlib>
#ifdef __RTAUDIO_DUMMY__
#error "No suitable audio backend has found for RtAudio! Install the dev package with headers for the system's audio library and make sure it is in the include path!"
#endif
using namespace std;
struct _AudioTrack {
/// sample
AudioSample * sample;
/// position in sample in frames
unsigned int dpos;
/// length of sample in frames
unsigned int dlen;
/// disparity in seconds between left and right, normally 0.0f
float disparity;
/// left volume
float volL;
/// right volume
float volR;
/// pitch factor, normally 1.0f
float pitch;
/// stores whether sample has to be looped
bool isLoop;
/// stores whether sample is currently playing
bool isPlaying;
/// stores whether sample is currently paused
bool isPaused;
/// unique id for this sound
uint64_t uniqueId;
};
DeviceAudio* DeviceAudioRt::create(unsigned int nTracks, unsigned int frequency, unsigned int chunkSize) {
if(!s_instance) {
DeviceAudioRt* pAudio = new DeviceAudioRt(nTracks,frequency,chunkSize);
if(!pAudio->m_freqOut) delete pAudio;
else s_instance = pAudio;
}
return s_instance;
}
DeviceAudioRt::DeviceAudioRt(unsigned int nTracks, unsigned int frequency, unsigned int chunkSize) : DeviceAudio(), m_uniqueCounter(0) {
if ( m_dac.getDeviceCount() < 1 ) {
fprintf(stderr,"DeviceAudioRt ERROR: No audio devices found!\n");
return;
}
// Set our stream parameters for output only.
RtAudio::StreamParameters oParams;
oParams.deviceId = m_dac.getDefaultOutputDevice(); // default device
oParams.nChannels = 2; // stereo
oParams.firstChannel = 0;
// initialize tracks:
m_nSound=nTracks;
ma_sound=new _AudioTrack[m_nSound];
memset(ma_sound,0,m_nSound*sizeof(_AudioTrack));
m_freqOut = frequency;
try {
m_dac.openStream( &oParams, NULL, RTAUDIO_SINT16, frequency, &chunkSize, &cbMix, (void *)this );
m_dac.startStream();
}
catch ( RtAudioError& e ) {
fprintf(stderr,"%s\n", e.getMessage().c_str());
if(m_dac.isStreamOpen()) m_dac.closeStream();
return;
}
}
DeviceAudioRt::~DeviceAudioRt() {
if(m_dac.isStreamOpen()) m_dac.closeStream();
delete [] ma_sound;
for( map<uint64_t,AudioSample*>::iterator it=mm_sample.begin(); it!=mm_sample.end(); ++it)
delete it->second;
mm_sample.clear();
}
uint64_t DeviceAudioRt::sampleFromMemory(const AudioSample & sample, float volume) {
AudioSample * pSample = new AudioSample(sample);
if(volume!=1.0f) pSample->volume(volume);
pSample->bitsPerSample(16);
mm_sample.insert(make_pair(++m_uniqueCounter,pSample));
return m_uniqueCounter;
}
bool DeviceAudioRt::sampleDestroy(uint64_t sample) {
// look for sample:
map<uint64_t,AudioSample*>::iterator iter=mm_sample.find(sample);
if( iter == mm_sample.end() ) return false;
// stop currently playing sounds referring to this sample:
for (unsigned int i=0; i<m_nSound; ++i ) if(ma_sound[i].sample == iter->second)
ma_sound[i].isPlaying=false;
// cleanup:
delete iter->second;
mm_sample.erase(iter);
return true;
}
const AudioSample* DeviceAudioRt::sample(uint64_t handle) const {
map<uint64_t,AudioSample*>::const_iterator it=mm_sample.find(handle);
if( it == mm_sample.end() ) return 0;
return it->second;
}
uint64_t DeviceAudioRt::soundPlay(uint64_t sample, float volumeL, float volumeR, float disparity, float pitch) {
// look for an empty (or finished) sound track
unsigned int i;
for ( i=0; i<m_nSound; ++i )
if (!ma_sound[i].isPlaying) break;
return soundPlayOn(i,sample,volumeL,volumeR,disparity,pitch);
}
uint64_t DeviceAudioRt::soundPlayOn(unsigned int i, uint64_t sample, float volumeL, float volumeR, float disparity, float pitch) {
if ( i >= m_nSound ) return 0; // invalid, or no empty slot found
// look for sample:
map<uint64_t,AudioSample*>::iterator iter=mm_sample.find(sample);
if( iter == mm_sample.end() ) return 0; // no sample found
uint64_t uniqueHandle = UH_PACK_UNIQUE_HANDLE(++m_uniqueCounter, i);
unsigned int sampleRate = iter->second->sampleRate();
if(sampleRate!=m_freqOut) pitch*=(float)sampleRate/(float)m_freqOut;
// put the sample data in the slot and play it
ma_sound[i].sample = iter->second;
ma_sound[i].dlen = iter->second->frames();
ma_sound[i].dpos = 0;
ma_sound[i].volL=volumeL;
ma_sound[i].volR=volumeR;
ma_sound[i].disparity=disparity;
ma_sound[i].pitch=fabs(pitch);
ma_sound[i].isLoop=false;
ma_sound[i].isPlaying=true;
ma_sound[i].isPaused=false;
ma_sound[i].uniqueId=UH_UNPACK_UNIQUE_ID(uniqueHandle);
return uniqueHandle;
}
uint64_t DeviceAudioRt::soundLoop(uint64_t sample, float volumeL, float volumeR, float disparity, float pitch ) {
uint64_t uniqueHandle=soundPlay(sample,volumeL,volumeR,disparity, pitch);
if(uniqueHandle) ma_sound[UH_UNPACK_PAYLOAD(uniqueHandle)].isLoop=true;
return uniqueHandle;
}
uint64_t DeviceAudioRt::soundLoopOn(unsigned int i, uint64_t sample, float volumeL, float volumeR, float disparity, float pitch) {
uint64_t uniqueHandle=soundPlayOn(i,sample,volumeL,volumeR,disparity, pitch);
if(uniqueHandle) ma_sound[UH_UNPACK_PAYLOAD(uniqueHandle)].isLoop=true;
return uniqueHandle;
}
bool DeviceAudioRt::soundUpdate(uint64_t uniqueHandle, bool pause, float volumeL, float volumeR, float disparity, float pitch ) {
unsigned int sound = UH_UNPACK_PAYLOAD(uniqueHandle);
if((sound>=m_nSound) || !ma_sound[sound].isPlaying || ma_sound[sound].uniqueId!=UH_UNPACK_UNIQUE_ID(uniqueHandle)) return false;
ma_sound[sound].isPaused=pause;
ma_sound[sound].volL=volumeL;
ma_sound[sound].volR=volumeR;
ma_sound[sound].disparity=disparity;
unsigned int sampleRate = ma_sound[sound].sample->sampleRate();
if(sampleRate!=m_freqOut) pitch*=(float)sampleRate/(float)m_freqOut;
ma_sound[sound].pitch=fabs(pitch);
return true;
}
bool DeviceAudioRt::soundStop(uint64_t uniqueHandle) {
unsigned int sound = UH_UNPACK_PAYLOAD(uniqueHandle);
if((sound>=m_nSound) || !ma_sound[sound].isPlaying || ma_sound[sound].uniqueId!=UH_UNPACK_UNIQUE_ID(uniqueHandle)) return false;
ma_sound[sound].isPlaying=false;
return true;
}
void DeviceAudioRt::soundStop() {
for (unsigned int i=0; i<m_nSound; ++i )
ma_sound[i].isPlaying=false;
}
unsigned int DeviceAudioRt::soundActive() const {
if(!const_cast<RtAudio*>(&m_dac)->isStreamRunning() ) return 0;
unsigned int ret = 0, i;
for ( i=0; i<m_nSound; ++i )
if (ma_sound[i].isPlaying) ++ret;
return ret;
}
bool DeviceAudioRt::soundActive(uint64_t uniqueHandle) {
unsigned int sound = UH_UNPACK_PAYLOAD(uniqueHandle);
if((sound>=m_nSound) || ma_sound[sound].uniqueId!=UH_UNPACK_UNIQUE_ID(uniqueHandle)) return false;
return ma_sound[sound].isPlaying;
}
int DeviceAudioRt::mixOutputFloat(signed short *outputBuffer, unsigned int nFrames) {
for(unsigned int j=0; j<nFrames; ++j) {
float left=0.0f;
float right=0.0f;
for (unsigned int i=0; i<m_nSound; ++i ) if(ma_sound[i].isPlaying && !ma_sound[i].isPaused) {
unsigned int nChannels = ma_sound[i].sample->channels();
if((ma_sound[i].pitch==1.0f)&&!ma_sound[i].disparity) { // use optimized default mixing:
unsigned int currPos=ma_sound[i].dpos+j;
if(ma_sound[i].isLoop) currPos%=ma_sound[i].dlen;
else if(currPos >= ma_sound[i].dlen) continue;
currPos*=ma_sound[i].sample->sizeFrame();
float dataL = (float)(*((signed short *)(&ma_sound[i].sample->data()[currPos])));
left += dataL * m_volL*ma_sound[i].volL;
float dataR = (nChannels>1) ? (float)(*((signed short *)(&ma_sound[i].sample->data()[currPos+2]))) : dataL;
right+= dataR * m_volR*ma_sound[i].volR;
}
else { // use nearest sample and disparity:
double fract=ma_sound[i].dpos+j*ma_sound[i].pitch;
unsigned int currPos=(unsigned int)fract;
fract = fmod(fract,1.0);
int currPosL= (ma_sound[i].disparity<0.0f) ? currPos+int(m_freqOut*ma_sound[i].disparity) : currPos;
int currPosR= (ma_sound[i].disparity>0.0f) ? currPos-int(m_freqOut*ma_sound[i].disparity) : currPos;
if(nChannels>1) currPosR+=sizeof(signed short); // use second channel
if(ma_sound[i].isLoop) {
currPosL+=ma_sound[i].dlen;
currPosL%=ma_sound[i].dlen;
currPosR+=ma_sound[i].dlen;
currPosR%=ma_sound[i].dlen;
}
if(currPosL<0) {
// do nothing
}
else if((unsigned int)currPosL+1 < ma_sound[i].dlen) {
currPosL*=ma_sound[i].sample->sizeFrame();
float dataL = (1.0f-(float)fract)*(float)(*((signed short *)(&ma_sound[i].sample->data()[currPosL])))
+ (float)fract*(float)(*((signed short *)(&ma_sound[i].sample->data()[currPosL+ma_sound[i].sample->sizeFrame()])));
left += dataL * m_volL*ma_sound[i].volL;
}
else if((unsigned int)currPosL+1 == ma_sound[i].dlen) {
currPosL*=ma_sound[i].sample->sizeFrame();
float dataL = (float)(*((signed short *)(&ma_sound[i].sample->data()[currPosL])));
left += dataL * m_volL*ma_sound[i].volL;
}
if(currPosR<0) {
// do nothing
}
else if((unsigned int)currPosR+1 < ma_sound[i].dlen) {
currPosR*=ma_sound[i].sample->sizeFrame();
float dataR = (1.0f-(float)fract)*(float)(*((signed short *)(&ma_sound[i].sample->data()[currPosR])))
+ (float)fract*(float)(*((signed short *)(&ma_sound[i].sample->data()[currPosR+ma_sound[i].sample->sizeFrame()])));
right += dataR * m_volR*ma_sound[i].volR;
}
else if((unsigned int)currPosR+1 == ma_sound[i].dlen) {
currPosR*=ma_sound[i].sample->sizeFrame();
float dataR = (float)(*((signed short *)(&ma_sound[i].sample->data()[currPosR])));
right += dataR * m_volR*ma_sound[i].volR;
}
}
}
// clamp and set output:
outputBuffer[2*j] = left>SHRT_MAX ? SHRT_MAX : left<SHRT_MIN ? SHRT_MIN : (signed short)left;
outputBuffer[2*j+1] = right>SHRT_MAX ? SHRT_MAX : right<SHRT_MIN ? SHRT_MIN : (signed short)right;
}
// calculate new pos:
for (unsigned int i=0; i<m_nSound; ++i ) {
if (ma_sound[i].isPaused) continue;
if(ma_sound[i].pitch==1.0f) ma_sound[i].dpos += nFrames;
else ma_sound[i].dpos += (unsigned int)(nFrames*ma_sound[i].pitch);
if(ma_sound[i].isLoop) ma_sound[i].dpos%=ma_sound[i].dlen;
else if(ma_sound[i].dpos>ma_sound[i].dlen+2*abs(int(m_freqOut*-ma_sound[i].disparity)))
ma_sound[i].isPlaying=false;
}
return 0;
}