244 lines
9.0 KiB
C++
244 lines
9.0 KiB
C++
#include "proAudioRt.h"
|
|
#include <cmath>
|
|
#include <cstdio>
|
|
#include <climits>
|
|
#include <cstring>
|
|
#include <cstdlib>
|
|
|
|
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;
|
|
};
|
|
|
|
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(), ma_sound(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;
|
|
|
|
try {
|
|
m_dac.openStream( &oParams, NULL, RTAUDIO_SINT16, frequency, &chunkSize, &cbMix, (void *)this );
|
|
m_dac.startStream();
|
|
}
|
|
catch ( RtError& e ) {
|
|
fprintf(stderr,"%s\n", e.getMessage().c_str());
|
|
if(m_dac.isStreamOpen()) m_dac.closeStream();
|
|
return;
|
|
}
|
|
|
|
// initialize tracks:
|
|
m_nSound=nTracks;
|
|
ma_sound=new _AudioTrack[m_nSound];
|
|
memset(ma_sound,0,m_nSound*sizeof(_AudioTrack));
|
|
m_freqOut = frequency;
|
|
}
|
|
|
|
DeviceAudioRt::~DeviceAudioRt() {
|
|
if(m_dac.isStreamOpen()) m_dac.closeStream();
|
|
delete [] ma_sound;
|
|
for( map<unsigned int,AudioSample*>::iterator it=mm_sample.begin(); it!=mm_sample.end(); ++it)
|
|
delete it->second;
|
|
mm_sample.clear();
|
|
}
|
|
|
|
unsigned int 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_sampleCounter,pSample));
|
|
return m_sampleCounter;
|
|
}
|
|
|
|
bool DeviceAudioRt::sampleDestroy(unsigned int sample) {
|
|
// look for sample:
|
|
map<unsigned int,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;
|
|
if(iter->first==m_sampleCounter) --m_sampleCounter;
|
|
mm_sample.erase(iter);
|
|
return true;
|
|
}
|
|
|
|
const AudioSample* DeviceAudioRt::sample(unsigned int handle) const {
|
|
map<unsigned int,AudioSample*>::const_iterator it=mm_sample.find(handle);
|
|
if( it == mm_sample.end() ) return 0;
|
|
return it->second;
|
|
}
|
|
|
|
|
|
unsigned int DeviceAudioRt::soundPlay(unsigned int sample, float volumeL, float volumeR, float disparity, float pitch ) {
|
|
// look for sample:
|
|
map<unsigned int,AudioSample*>::iterator iter=mm_sample.find(sample);
|
|
if( iter == mm_sample.end() ) return 0; // no sample found
|
|
// look for an empty (or finished) sound track
|
|
unsigned int i;
|
|
for ( i=0; i<m_nSound; ++i )
|
|
if (!ma_sound[i].isPlaying) break;
|
|
if ( i == m_nSound ) return 0; // no empty slot found
|
|
|
|
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;
|
|
return i+1;
|
|
}
|
|
|
|
unsigned int DeviceAudioRt::soundLoop(unsigned int sample, float volumeL, float volumeR, float disparity, float pitch ) {
|
|
unsigned int ret=soundPlay(sample,volumeL,volumeR,disparity, pitch);
|
|
if(ret) ma_sound[ret-1].isLoop=true;
|
|
return ret;
|
|
}
|
|
|
|
bool DeviceAudioRt::soundUpdate(unsigned int sound, float volumeL, float volumeR, float disparity, float pitch ) {
|
|
if(!sound || (sound>m_nSound) || !ma_sound[sound-1].isPlaying) return false;
|
|
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(unsigned int sound) {
|
|
if(!sound||(sound>m_nSound)||!ma_sound[sound-1].isPlaying) return false;
|
|
ma_sound[sound-1].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;
|
|
}
|
|
|
|
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) {
|
|
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].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;
|
|
}
|