#include "b3SoundEngine.h" #include "RtAudio.h" #include "b3AudioListener.h" #include "b3SoundSource.h" #include "Bullet3Common/b3AlignedObjectArray.h" #include "b3ReadWavFile.h" #include "../Utils/b3ResourcePath.h" #include "Bullet3Common/b3HashMap.h" // The default real-time audio input and output buffer size. If // clicks are occuring in the input and/or output sound stream, a // larger buffer size may help. Larger buffer sizes, however, produce // more latency. //const unsigned int RT_BUFFER_SIZE = 1024; const unsigned int RT_BUFFER_SIZE = 256; struct b3SoundEngineInternalData { b3AudioListener m_listener; RtAudio m_dac; bool m_useRealTimeDac; b3AlignedObjectArray m_soundSources; b3HashMap m_wavFiles; b3HashMap m_name2wav; int m_wavFileUidGenerator; b3SoundEngineInternalData() : m_useRealTimeDac(false), m_wavFileUidGenerator(123) { } }; b3SoundEngine::b3SoundEngine() { m_data = new b3SoundEngineInternalData(); } b3SoundEngine::~b3SoundEngine() { exit(); delete m_data; } void b3SoundEngine::init(int maxNumSoundSources, bool useRealTimeDac) { for (int i = 0; i < maxNumSoundSources; i++) { b3SoundSource* source = new b3SoundSource(); m_data->m_soundSources.push_back(source); m_data->m_listener.addSoundSource(source); } this->m_data->m_useRealTimeDac = useRealTimeDac; if (useRealTimeDac) { RtAudioFormat format = (sizeof(double) == 8) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32; RtAudio::StreamParameters parameters; parameters.deviceId = m_data->m_dac.getDefaultOutputDevice(); parameters.nChannels = 2; unsigned int bufferFrames = RT_BUFFER_SIZE; double sampleRate = m_data->m_listener.getSampleRate(); m_data->m_dac.openStream(¶meters, NULL, format, (unsigned int)sampleRate, &bufferFrames, &b3AudioListener::tick, (void*)m_data->m_listener.getTickData()); m_data->m_dac.startStream(); } } void b3SoundEngine::exit() { m_data->m_dac.closeStream(); m_data->m_useRealTimeDac = false; for (int i = 0; i < m_data->m_soundSources.size(); i++) { m_data->m_listener.removeSoundSource(m_data->m_soundSources[i]); m_data->m_soundSources[i]->stopSound(); delete m_data->m_soundSources[i]; } m_data->m_soundSources.clear(); for (int i = 0; i < m_data->m_wavFiles.size(); i++) { b3ReadWavFile** wavPtr = m_data->m_wavFiles.getAtIndex(i); if (wavPtr && *wavPtr) { b3ReadWavFile* wav = *wavPtr; delete wav; } } m_data->m_wavFiles.clear(); m_data->m_name2wav.clear(); } int b3SoundEngine::getAvailableSoundSource() { for (int i = 0; i < m_data->m_soundSources.size(); i++) { if (m_data->m_soundSources[i]->isAvailable()) { return i; } } return -1; } void b3SoundEngine::startSound(int soundSourceIndex, b3SoundMessage msg) { b3SoundSource* soundSource = m_data->m_soundSources[soundSourceIndex]; soundSource->setOscillatorAmplitude(0, msg.m_amplitude); soundSource->setOscillatorAmplitude(1, msg.m_amplitude); soundSource->setADSR(msg.m_attackRate, msg.m_decayRate, msg.m_sustainLevel, msg.m_releaseRate); switch (msg.m_type) { case B3_SOUND_SOURCE_SINE_OSCILLATOR: { soundSource->setOscillatorFrequency(0, msg.m_frequency); soundSource->setOscillatorFrequency(1, msg.m_frequency); soundSource->startSound(msg.m_autoKeyOff); break; } case B3_SOUND_SOURCE_WAV_FILE: { b3ReadWavFile** wavFilePtr = m_data->m_wavFiles[msg.m_wavId]; if (wavFilePtr) { b3ReadWavFile* wavFile = *wavFilePtr; soundSource->setWavFile(0, wavFile, getSampleRate()); soundSource->setWavFile(1, wavFile, getSampleRate()); soundSource->startSound(msg.m_autoKeyOff); } break; } default: { } } } void b3SoundEngine::releaseSound(int soundSourceIndex) { b3SoundSource* soundSource = m_data->m_soundSources[soundSourceIndex]; soundSource->stopSound(); } int b3SoundEngine::loadWavFile(const char* fileName) { int* wavUidPtr = m_data->m_name2wav[fileName]; if (wavUidPtr) { return *wavUidPtr; } char resourcePath[1024]; if (b3ResourcePath::findResourcePath(fileName, resourcePath, 1024, 0)) { b3ReadWavFile* wavFile = new b3ReadWavFile(); wavFile->getWavInfo(resourcePath); wavFile->resize(); wavFile->read(0, true); wavFile->normalize(1); int wavUID = m_data->m_wavFileUidGenerator++; m_data->m_wavFiles.insert(wavUID, wavFile); m_data->m_name2wav.insert(fileName, wavUID); return wavUID; } return 0; } double b3SoundEngine::getSampleRate() const { return m_data->m_listener.getSampleRate(); }