1 #include "b3SoundEngine.h"
2
3 #include "RtAudio.h"
4
5 #include "b3AudioListener.h"
6 #include "b3SoundSource.h"
7 #include "Bullet3Common/b3AlignedObjectArray.h"
8 #include "b3ReadWavFile.h"
9 #include "../Utils/b3ResourcePath.h"
10
11 #include "Bullet3Common/b3HashMap.h"
12
13 // The default real-time audio input and output buffer size. If
14 // clicks are occuring in the input and/or output sound stream, a
15 // larger buffer size may help. Larger buffer sizes, however, produce
16 // more latency.
17 //const unsigned int RT_BUFFER_SIZE = 1024;
18 const unsigned int RT_BUFFER_SIZE = 256;
19
20 struct b3SoundEngineInternalData
21 {
22 b3AudioListener m_listener;
23 RtAudio m_dac;
24 bool m_useRealTimeDac;
25
26 b3AlignedObjectArray<b3SoundSource*> m_soundSources;
27 b3HashMap<b3HashInt, b3ReadWavFile*> m_wavFiles;
28 b3HashMap<b3HashString, int> m_name2wav;
29
30 int m_wavFileUidGenerator;
31
b3SoundEngineInternalDatab3SoundEngineInternalData32 b3SoundEngineInternalData()
33 : m_useRealTimeDac(false),
34 m_wavFileUidGenerator(123)
35 {
36 }
37 };
38
b3SoundEngine()39 b3SoundEngine::b3SoundEngine()
40 {
41 m_data = new b3SoundEngineInternalData();
42 }
43
~b3SoundEngine()44 b3SoundEngine::~b3SoundEngine()
45 {
46 exit();
47 delete m_data;
48 }
49
init(int maxNumSoundSources,bool useRealTimeDac)50 void b3SoundEngine::init(int maxNumSoundSources, bool useRealTimeDac)
51 {
52 for (int i = 0; i < maxNumSoundSources; i++)
53 {
54 b3SoundSource* source = new b3SoundSource();
55 m_data->m_soundSources.push_back(source);
56 m_data->m_listener.addSoundSource(source);
57 }
58
59 this->m_data->m_useRealTimeDac = useRealTimeDac;
60
61 if (useRealTimeDac)
62 {
63 RtAudioFormat format = (sizeof(double) == 8) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32;
64 RtAudio::StreamParameters parameters;
65 parameters.deviceId = m_data->m_dac.getDefaultOutputDevice();
66 parameters.nChannels = 2;
67
68 unsigned int bufferFrames = RT_BUFFER_SIZE;
69 double sampleRate = m_data->m_listener.getSampleRate();
70
71 m_data->m_dac.openStream(¶meters, NULL, format, (unsigned int)sampleRate, &bufferFrames, &b3AudioListener::tick,
72 (void*)m_data->m_listener.getTickData());
73
74 m_data->m_dac.startStream();
75 }
76 }
77
exit()78 void b3SoundEngine::exit()
79 {
80 m_data->m_dac.closeStream();
81 m_data->m_useRealTimeDac = false;
82
83 for (int i = 0; i < m_data->m_soundSources.size(); i++)
84 {
85 m_data->m_listener.removeSoundSource(m_data->m_soundSources[i]);
86 m_data->m_soundSources[i]->stopSound();
87 delete m_data->m_soundSources[i];
88 }
89 m_data->m_soundSources.clear();
90
91 for (int i = 0; i < m_data->m_wavFiles.size(); i++)
92 {
93 b3ReadWavFile** wavPtr = m_data->m_wavFiles.getAtIndex(i);
94 if (wavPtr && *wavPtr)
95 {
96 b3ReadWavFile* wav = *wavPtr;
97 delete wav;
98 }
99 }
100 m_data->m_wavFiles.clear();
101 m_data->m_name2wav.clear();
102 }
103
getAvailableSoundSource()104 int b3SoundEngine::getAvailableSoundSource()
105 {
106 for (int i = 0; i < m_data->m_soundSources.size(); i++)
107 {
108 if (m_data->m_soundSources[i]->isAvailable())
109 {
110 return i;
111 }
112 }
113 return -1;
114 }
115
startSound(int soundSourceIndex,b3SoundMessage msg)116 void b3SoundEngine::startSound(int soundSourceIndex, b3SoundMessage msg)
117 {
118 b3SoundSource* soundSource = m_data->m_soundSources[soundSourceIndex];
119 soundSource->setOscillatorAmplitude(0, msg.m_amplitude);
120 soundSource->setOscillatorAmplitude(1, msg.m_amplitude);
121
122 soundSource->setADSR(msg.m_attackRate, msg.m_decayRate, msg.m_sustainLevel, msg.m_releaseRate);
123
124 switch (msg.m_type)
125 {
126 case B3_SOUND_SOURCE_SINE_OSCILLATOR:
127 {
128 soundSource->setOscillatorFrequency(0, msg.m_frequency);
129 soundSource->setOscillatorFrequency(1, msg.m_frequency);
130
131 soundSource->startSound(msg.m_autoKeyOff);
132 break;
133 }
134 case B3_SOUND_SOURCE_WAV_FILE:
135 {
136 b3ReadWavFile** wavFilePtr = m_data->m_wavFiles[msg.m_wavId];
137 if (wavFilePtr)
138 {
139 b3ReadWavFile* wavFile = *wavFilePtr;
140 soundSource->setWavFile(0, wavFile, getSampleRate());
141 soundSource->setWavFile(1, wavFile, getSampleRate());
142 soundSource->startSound(msg.m_autoKeyOff);
143 }
144 break;
145 }
146 default:
147 {
148 }
149 }
150 }
151
releaseSound(int soundSourceIndex)152 void b3SoundEngine::releaseSound(int soundSourceIndex)
153 {
154 b3SoundSource* soundSource = m_data->m_soundSources[soundSourceIndex];
155 soundSource->stopSound();
156 }
157
loadWavFile(const char * fileName)158 int b3SoundEngine::loadWavFile(const char* fileName)
159 {
160 int* wavUidPtr = m_data->m_name2wav[fileName];
161 if (wavUidPtr)
162 {
163 return *wavUidPtr;
164 }
165 char resourcePath[1024];
166
167 if (b3ResourcePath::findResourcePath(fileName, resourcePath, 1024, 0))
168 {
169 b3ReadWavFile* wavFile = new b3ReadWavFile();
170 wavFile->getWavInfo(resourcePath);
171 wavFile->resize();
172 wavFile->read(0, true);
173 wavFile->normalize(1);
174 int wavUID = m_data->m_wavFileUidGenerator++;
175 m_data->m_wavFiles.insert(wavUID, wavFile);
176 m_data->m_name2wav.insert(fileName, wavUID);
177 return wavUID;
178 }
179 return 0;
180 }
181
getSampleRate() const182 double b3SoundEngine::getSampleRate() const
183 {
184 return m_data->m_listener.getSampleRate();
185 }
186