1 /*
2 Copyright (C) 2005 The Pentagram team
3 Copyright (C) 2010-2013 The Exult Team
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19 #include "pent_include.h"
20 #include "AudioMixer.h"
21 #include "Configuration.h"
22
23 #include "AudioChannel.h"
24
25 #include "MidiDriver.h"
26
27 #include <SDL.h>
28
29 #include <algorithm>
30 #include <iostream>
31 #include <limits>
32 #include <mutex>
33
34 #include "Midi.h"
35
36 namespace Pentagram {
37 class SDLAudioDevice {
38 public:
SDLAudioDevice(SDL_AudioDeviceID dev_)39 SDLAudioDevice(SDL_AudioDeviceID dev_) : dev(dev_) {}
~SDLAudioDevice()40 ~SDLAudioDevice() {
41 SDL_CloseAudioDevice(dev);
42 }
43
pause()44 void pause() {
45 SDL_PauseAudioDevice(dev, 1);
46 }
47
unpause()48 void unpause() {
49 SDL_PauseAudioDevice(dev, 0);
50 }
51
lock()52 void lock() {
53 SDL_LockAudioDevice(dev);
54 }
55
unlock()56 void unlock() {
57 SDL_UnlockAudioDevice(dev);
58 }
59
60 private:
61 SDL_AudioDeviceID dev;
62 };
63 }
64
65 using namespace Pentagram;
66
67 AudioMixer *AudioMixer::the_audio_mixer = nullptr;
68
AudioMixer(int sample_rate_,bool stereo_,int num_channels_)69 AudioMixer::AudioMixer(int sample_rate_, bool stereo_, int num_channels_) :
70 audio_ok(false),
71 sample_rate(sample_rate_), stereo(stereo_),
72 midi(nullptr), midi_volume(255),
73 id_counter(0), device(nullptr)
74 {
75 the_audio_mixer = this;
76
77 std::cout << "Creating AudioMixer..." << std::endl;
78
79 SDL_AudioSpec desired{};
80 SDL_AudioSpec obtained;
81
82 desired.format = AUDIO_S16SYS;
83 desired.freq = sample_rate_;
84 desired.channels = stereo_?2:1;
85 desired.callback = sdlAudioCallback;
86 desired.userdata = this;
87
88 // Set update rate to 20 Hz, or there abouts. This should be more then adequate for everyone
89 desired.samples=1;
90 while(desired.samples<=desired.freq/30) desired.samples<<=1;
91
92 // Open SDL Audio (even though we may not need it)
93 SDL_InitSubSystem(SDL_INIT_AUDIO);
94 SDL_AudioDeviceID dev = SDL_OpenAudioDevice(nullptr, 0, &desired, &obtained, SDL_AUDIO_ALLOW_ANY_CHANGE);
95 audio_ok = (dev != 0);
96
97 if (audio_ok) {
98 pout << "Audio opened using format: " << obtained.freq << " Hz "
99 << static_cast<int>(obtained.channels) << " Channels" << std::endl;
100 device = new SDLAudioDevice(dev);
101 {
102 std::lock_guard<SDLAudioDevice> lock(*device);
103 sample_rate = obtained.freq;
104 stereo = obtained.channels == 2;
105
106 internal_buffer.resize((obtained.size + 1) / 2);
107 for (int i = 0; i < num_channels_; i++)
108 channels.emplace_back(sample_rate, stereo);
109 }
110
111 // GO GO GO!
112 device->unpause();
113 }
114 }
115
~AudioMixer()116 AudioMixer::~AudioMixer()
117 {
118 std::cout << "Destroying AudioMixer..." << std::endl;
119
120 closeMidiOutput();
121
122 delete device;
123
124 the_audio_mixer = nullptr;
125 }
126
reset()127 void AudioMixer::reset()
128 {
129 if (!audio_ok) return;
130
131 std::cout << "Resetting AudioMixer..." << std::endl;
132
133 midi->stop_music();
134
135 std::lock_guard<SDLAudioDevice> lock(*device);
136 for (auto& channel : channels) channel.stop();
137 }
138
playSample(AudioSample * sample,int loop,int priority,bool paused,uint32 pitch_shift_,int lvol,int rvol)139 sint32 AudioMixer::playSample(AudioSample *sample, int loop, int priority, bool paused, uint32 pitch_shift_, int lvol, int rvol)
140 {
141 if (!audio_ok || channels.empty()) return -1;
142
143 std::lock_guard<SDLAudioDevice> lock(*device);
144 auto it = std::find_if(channels.begin(), channels.end(), [](auto& channel) {
145 return !channel.isPlaying();
146 });
147 if (it == channels.end()) {
148 it = std::min_element(channels.begin(), channels.end(), [](auto& c1, auto& c2) {
149 return c1.getPriority() < c2.getPriority();
150 });
151 }
152 if (it != channels.end() && (!it->isPlaying() || it->getPriority() < priority)) {
153 if (id_counter == std::numeric_limits<decltype(id_counter)>::max()) {
154 id_counter = 0;
155 } else {
156 ++id_counter;
157 }
158 it->playSample(sample, loop, priority, paused, pitch_shift_, lvol, rvol, id_counter);
159 return id_counter;
160 }
161 return -1;
162 }
163
isPlaying(sint32 instance_id) const164 bool AudioMixer::isPlaying(sint32 instance_id) const
165 {
166 if (instance_id < 0 || channels.empty() || !audio_ok) return false;
167
168 std::lock_guard<SDLAudioDevice> lock(*device);
169 auto it = std::find_if(channels.cbegin(), channels.cend(), [instance_id](auto& channel) {
170 return channel.getInstanceId() == instance_id;
171 });
172 return it != channels.end() && it->isPlaying();
173 }
174
isPlaying(AudioSample * sample) const175 bool AudioMixer::isPlaying(AudioSample *sample) const
176 {
177 if (!sample || channels.empty() || !audio_ok) return false;
178
179 std::lock_guard<SDLAudioDevice> lock(*device);
180 auto it = std::find_if(channels.cbegin(), channels.cend(), [sample](auto& channel) {
181 return channel.getSample() == sample;
182 });
183 return it != channels.end();
184 }
185
isPlayingVoice() const186 bool AudioMixer::isPlayingVoice() const
187 {
188 if (channels.empty() || !audio_ok) return false;
189
190 std::lock_guard<SDLAudioDevice> lock(*device);
191 auto it = std::find_if(channels.cbegin(), channels.cend(), [](auto& channel) {
192 return channel.isPlaying() && channel.getSample()->isVocSample();
193 });
194 return it != channels.end();
195 }
196
stopSample(sint32 instance_id)197 void AudioMixer::stopSample(sint32 instance_id)
198 {
199 if (instance_id < 0 || channels.empty() || !audio_ok) return;
200
201 std::lock_guard<SDLAudioDevice> lock(*device);
202 auto it = std::find_if(channels.begin(), channels.end(), [instance_id](auto& channel) {
203 return channel.getInstanceId() == instance_id;
204 });
205 if (it != channels.end()) {
206 it->stop();
207 }
208 }
209
stopSample(AudioSample * sample)210 void AudioMixer::stopSample(AudioSample *sample)
211 {
212 if (!sample || channels.empty() || !audio_ok) return;
213
214 std::lock_guard<SDLAudioDevice> lock(*device);
215 for (auto& channel : channels)
216 {
217 if (channel.getSample() == sample)
218 channel.stop();
219 }
220 }
221
setPaused(sint32 instance_id,bool paused)222 void AudioMixer::setPaused(sint32 instance_id, bool paused)
223 {
224 if (instance_id < 0 || channels.empty() || !audio_ok) return;
225
226 std::lock_guard<SDLAudioDevice> lock(*device);
227 auto it = std::find_if(channels.begin(), channels.end(), [instance_id](auto& channel) {
228 return channel.getInstanceId() == instance_id;
229 });
230 if (it != channels.end()) {
231 it->setPaused(paused);
232 }
233 }
234
isPaused(sint32 instance_id) const235 bool AudioMixer::isPaused(sint32 instance_id) const
236 {
237 if (instance_id < 0 || channels.empty() || !audio_ok) return false;
238
239 std::lock_guard<SDLAudioDevice> lock(*device);
240 auto it = std::find_if(channels.cbegin(), channels.cend(), [instance_id](auto& channel) {
241 return channel.getInstanceId() == instance_id;
242 });
243 return it != channels.end() && it->isPaused();
244 }
245
setPausedAll(bool paused)246 void AudioMixer::setPausedAll(bool paused)
247 {
248 if (channels.empty() || !audio_ok) return;
249
250 std::lock_guard<SDLAudioDevice> lock(*device);
251 for (auto& channel : channels)
252 channel.setPaused(paused);
253 }
254
setVolume(sint32 instance_id,int lvol,int rvol)255 void AudioMixer::setVolume(sint32 instance_id, int lvol, int rvol)
256 {
257 if (instance_id < 0 || channels.empty() || !audio_ok) return;
258
259 std::lock_guard<SDLAudioDevice> lock(*device);
260 auto it = std::find_if(channels.begin(), channels.end(), [instance_id](auto& channel) {
261 return channel.getInstanceId() == instance_id;
262 });
263 if (it != channels.end()) {
264 it->setVolume(lvol, rvol);
265 }
266 }
267
getVolume(sint32 instance_id,int & lvol,int & rvol) const268 void AudioMixer::getVolume(sint32 instance_id, int &lvol, int &rvol) const
269 {
270 if (instance_id < 0 || channels.empty() || !audio_ok) return;
271
272 std::lock_guard<SDLAudioDevice> lock(*device);
273 auto it = std::find_if(channels.cbegin(), channels.cend(), [instance_id](auto& channel) {
274 return channel.getInstanceId() == instance_id;
275 });
276 if (it != channels.end()) {
277 it->getVolume(lvol, rvol);
278 }
279 }
280
281
set2DPosition(sint32 instance_id,int distance,int angle)282 bool AudioMixer::set2DPosition(sint32 instance_id, int distance, int angle)
283 {
284 if (instance_id < 0 || channels.empty() || !audio_ok) return false;
285
286 std::lock_guard<SDLAudioDevice> lock(*device);
287 auto it = std::find_if(channels.begin(), channels.end(), [instance_id](auto& channel) {
288 return channel.getInstanceId() == instance_id;
289 });
290 if (it != channels.end()) {
291 it->set2DPosition(distance, angle);
292 return it->isPlaying();
293 }
294 return false;
295 }
296
get2DPosition(sint32 instance_id,int & distance,int & angle) const297 void AudioMixer::get2DPosition(sint32 instance_id, int &distance, int &angle) const
298 {
299 if (instance_id < 0 || channels.empty() || !audio_ok) return;
300
301 std::lock_guard<SDLAudioDevice> lock(*device);
302 auto it = std::find_if(channels.cbegin(), channels.cend(), [instance_id](auto& channel) {
303 return channel.getInstanceId() == instance_id;
304 });
305 if (it != channels.end()) {
306 it->get2DPosition(distance, angle);
307 }
308 }
309
sdlAudioCallback(void * userdata,Uint8 * stream,int len)310 void AudioMixer::sdlAudioCallback(void *userdata, Uint8 *stream, int len)
311 {
312 auto *mixer = static_cast<AudioMixer *>(userdata);
313 // Unfortunately, SDL does not guarantee that stream will be aligned to
314 // the correct alignment for sint16.
315 // There is no real solution except using an aligned buffer and copying.
316 size_t newlen = size_t(len + 1) / 2;
317 // This should never be needed, as we set the vector length
318 // based on information provided by SDL. Lets leave it in anyway
319 // just in case...
320 if (newlen > mixer->internal_buffer.size()) {
321 mixer->internal_buffer.resize(newlen);
322 }
323 mixer->MixAudio(mixer->internal_buffer.data(), len);
324 std::memcpy(stream, mixer->internal_buffer.data(), len);
325 }
326
MixAudio(sint16 * stream,uint32 bytes)327 void AudioMixer::MixAudio(sint16 *stream, uint32 bytes)
328 {
329 if (!audio_ok) return;
330 std::memset(stream,0,bytes);
331 if (midi) midi->produceSamples(stream, bytes);
332 for (auto& channel : channels)
333 if (channel.isPlaying()) channel.resampleAndMix(stream,bytes);
334 }
335
openMidiOutput()336 void AudioMixer::openMidiOutput()
337 {
338 if (midi) return;
339 if (!audio_ok) return;
340
341 auto * new_driver = new MyMidiPlayer();
342
343 {
344 std::lock_guard<SDLAudioDevice> lock(*device);
345 midi = new_driver;
346 }
347 midi->load_timbres();
348 //midi_driver->setGlobalVolume(midi_volume);
349 }
350
closeMidiOutput()351 void AudioMixer::closeMidiOutput()
352 {
353 if (!midi) return;
354 std::cout << "Destroying MidiDriver..." << std::endl;
355
356 midi->stop_music(true);
357 midi->destroyMidiDriver();
358
359 std::lock_guard<SDLAudioDevice> lock(*device);
360 delete midi;
361 midi = nullptr;
362 }
363