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(&parameters, 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