1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "bladerunner/audio_mixer.h"
24 
25 #include "bladerunner/bladerunner.h"
26 #include "bladerunner/music.h"
27 #include "bladerunner/time.h"
28 
29 #include "audio/audiostream.h"
30 #include "audio/mixer.h"
31 
32 #include "common/timer.h"
33 
34 namespace BladeRunner {
35 
AudioMixer(BladeRunnerEngine * vm)36 AudioMixer::AudioMixer(BladeRunnerEngine *vm) {
37 	_vm = vm;
38 	for (int i = 0; i < kChannels; ++i) {
39 		_channels[i].isPresent = false;
40 	}
41 #if !BLADERUNNER_ORIGINAL_BUGS
42 	for (int i = 0; i < kAudioMixerAppTimersNum; ++i) {
43 		_audioMixerAppTimers[i].started        = false;
44 		_audioMixerAppTimers[i].lastFired      = 0u;
45 		_audioMixerAppTimers[i].intervalMillis = 0u;
46 	}
47 #endif // BLADERUNNER_ORIGINAL_BUGS
48 	_vm->getTimerManager()->installTimerProc(timerCallback, (1000 / kUpdatesPerSecond) * 1000, this, "BladeRunnerAudioMixerTimer");
49 }
50 
~AudioMixer()51 AudioMixer::~AudioMixer() {
52 	for (int i = 0; i < kChannels; ++i) {
53 		stop(i, 0u);
54 	}
55 	_vm->getTimerManager()->removeTimerProc(timerCallback);
56 }
57 
play(Audio::Mixer::SoundType type,Audio::RewindableAudioStream * stream,int priority,bool loop,int volume,int pan,void (* endCallback)(int,void *),void * callbackData,uint32 trackDurationMs)58 int AudioMixer::play(Audio::Mixer::SoundType type, Audio::RewindableAudioStream *stream, int priority, bool loop, int volume, int pan, void (*endCallback)(int, void *), void *callbackData, uint32 trackDurationMs) {
59 	Common::StackLock lock(_mutex);
60 
61 	int channel = -1;
62 	int lowestPriority = 1000000;
63 	int lowestPriorityChannel = -1;
64 	for (int i = 0; i < kUsableChannels; ++i) {
65 		if (!_channels[i].isPresent) {
66 			channel = i;
67 			break;
68 		}
69 		if (_channels[i].priority < lowestPriority) {
70 			lowestPriority = _channels[i].priority;
71 			lowestPriorityChannel = i;
72 		}
73 	}
74 	if (channel == -1) {
75 		if (priority < lowestPriority) {
76 			//debug("No available audio channel found - giving up");
77 			return -1;
78 		}
79 		//debug("Stopping lowest priority channel %d with lower prio %d!", lowestPriorityChannel, lowestPriority);
80 		stop(lowestPriorityChannel, 0u);
81 		channel = lowestPriorityChannel;
82 	}
83 
84 	return playInChannel(channel, type, stream, priority, loop, volume, pan, endCallback, callbackData, trackDurationMs);
85 }
86 
playMusic(Audio::RewindableAudioStream * stream,int volume,void (* endCallback)(int,void *),void * callbackData,uint32 trackDurationMs)87 int AudioMixer::playMusic(Audio::RewindableAudioStream *stream, int volume, void(*endCallback)(int, void *), void *callbackData, uint32 trackDurationMs) {
88 	Common::StackLock lock(_mutex);
89 
90 	return playInChannel(kMusicChannel, Audio::Mixer::kMusicSoundType, stream, 100, false, volume, 0, endCallback, callbackData, trackDurationMs);
91 }
92 
93 // Note: time tends to be the requested time in seconds multiplied by 60u
stop(int channel,uint32 time)94 void AudioMixer::stop(int channel, uint32 time) {
95 	Common::StackLock lock(_mutex);
96 
97 	if (_channels[channel].isPresent) {
98 		if (time) {
99 			adjustVolume(channel, 0, time);
100 		} else {
101 			_channels[channel].isPresent = false;
102 			if (_channels[channel].sentToMixer) {
103 				_vm->_mixer->stopHandle(_channels[channel].handle);
104 			}
105 
106 			if (_channels[channel].endCallback != nullptr) {
107 				_channels[channel].endCallback(channel, _channels[channel].callbackData);
108 			}
109 		}
110 	}
111 }
112 
playInChannel(int channel,Audio::Mixer::SoundType type,Audio::RewindableAudioStream * stream,int priority,bool loop,int volume,int pan,void (* endCallback)(int,void *),void * callbackData,uint32 trackDurationMs)113 int AudioMixer::playInChannel(int channel, Audio::Mixer::SoundType type, Audio::RewindableAudioStream *stream, int priority, bool loop, int volume, int pan, void(*endCallback)(int, void *), void *callbackData, uint32 trackDurationMs) {
114 	_channels[channel].isPresent = true;
115 	_channels[channel].stream = stream;
116 	_channels[channel].priority = priority;
117 	_channels[channel].loop = loop;
118 	_channels[channel].volume = volume;
119 	_channels[channel].volumeTarget = 0;
120 	_channels[channel].volumeDelta = 0;
121 	_channels[channel].pan = pan;
122 	_channels[channel].panTarget = 0;
123 	_channels[channel].panDelta = 0;
124 	_channels[channel].endCallback = endCallback;
125 	_channels[channel].callbackData = callbackData;
126 	_channels[channel].timeStarted = _vm->_time->currentSystem();
127 	_channels[channel].trackDurationMs = trackDurationMs;
128 
129 	Audio::AudioStream *audioStream = stream;
130 
131 	if (loop) {
132 		audioStream = new Audio::LoopingAudioStream(stream, 0, DisposeAfterUse::YES);
133 	}
134 
135 	if (!_vm->_mixer->isReady()) {
136 		_channels[channel].sentToMixer = false;
137 		return channel;
138 	}
139 	_channels[channel].sentToMixer = true;
140 
141 	_vm->_mixer->playStream(
142 		type,
143 		&_channels[channel].handle,
144 		audioStream,
145 		-1,
146 		volume * 255 / 100,
147 		pan * 127 / 100);
148 
149 	return channel;
150 }
151 
isActive(int channel) const152 bool AudioMixer::isActive(int channel) const {
153 	Common::StackLock lock(_mutex);
154 
155 	return _channels[channel].isPresent
156 	       && ((_channels[channel].sentToMixer && _vm->_mixer->isSoundHandleActive(_channels[channel].handle))
157 	            || (!_channels[channel].sentToMixer && !_channels[channel].loop && (_vm->_time->currentSystem() - _channels[channel].timeStarted < _channels[channel].trackDurationMs)));
158 }
159 
timerCallback(void * self)160 void AudioMixer::timerCallback(void *self) {
161 	((AudioMixer *)self)->tick();
162 }
163 
164 // Note: time tends to be the requested time in seconds multiplied by 60u
adjustVolume(int channel,int newVolume,uint32 time)165 void AudioMixer::adjustVolume(int channel, int newVolume, uint32 time) {
166 	Common::StackLock lock(_mutex);
167 
168 	if (_channels[channel].isPresent) {
169 		_channels[channel].volumeTarget = newVolume;
170 		_channels[channel].volumeDelta = ((newVolume - _channels[channel].volume) / (time / 60.0f)) / (float)kUpdatesPerSecond;
171 	}
172 }
173 
174 // Note: time tends to be the requested time in seconds multiplied by 60u
adjustPan(int channel,int newPan,uint32 time)175 void AudioMixer::adjustPan(int channel, int newPan, uint32 time) {
176 	Common::StackLock lock(_mutex);
177 
178 	if (_channels[channel].isPresent) {
179 		newPan = CLIP(newPan, -100, 100);
180 		_channels[channel].panTarget = newPan;
181 		_channels[channel].panDelta = ((newPan - _channels[channel].pan) / (time / 60.0f)) / (float)kUpdatesPerSecond;
182 	}
183 }
184 
tick()185 void AudioMixer::tick() {
186 	Common::StackLock lock(_mutex);
187 
188 	for (int i = 0; i < kChannels; ++i) {
189 		Channel *channel = &_channels[i];
190 		if (!channel->isPresent) {
191 			continue;
192 		}
193 
194 		if (channel->volumeDelta != 0.0f) {
195 			// apply volumeDelta to volume (common use for adjustVolume or stop playing - ie mainly for fadeIn, fadeOut)
196 			channel->volume = CLIP(channel->volume + channel->volumeDelta, 0.0f, 100.0f);
197 
198 			if ((channel->volumeDelta < 0 && channel->volume <= channel->volumeTarget)
199 			    || (channel->volumeDelta > 0 && channel->volume >= channel->volumeTarget)) {
200 				channel->volumeDelta = 0.0f;
201 			}
202 
203 			if (channel->sentToMixer) {
204 				_vm->_mixer->setChannelVolume(channel->handle, (channel->volume * Audio::Mixer::kMaxChannelVolume) / 100); // map [0..100] to [0..kMaxChannelVolume]
205 			}
206 
207 			if (channel->volume <= 0.0f) {
208 				stop(i, 0u);
209 			}
210 		}
211 
212 		if (channel->panDelta != 0.0) {
213 			// apply panDelta to pan (common use for adjusting pan)
214 			channel->pan = CLIP(channel->pan + channel->panDelta, -100.0f, 100.0f);
215 
216 			if ((channel->panDelta < 0 && channel->pan <= channel->panTarget) || (channel->panDelta > 0 && channel->pan >= channel->panTarget)) {
217 				channel->panDelta = 0.0f;
218 			}
219 
220 			if (channel->sentToMixer) {
221 				_vm->_mixer->setChannelBalance(channel->handle, (channel->pan * 127) / 100); // map [-100..100] to [-127..127]
222 			}
223 		}
224 
225 		if ((channel->sentToMixer && !_vm->_mixer->isSoundHandleActive(channel->handle))
226 			|| channel->stream->endOfStream()
227 			|| (!channel->sentToMixer && !channel->loop && _vm->_time->currentSystem() - channel->timeStarted >= channel->trackDurationMs)
228 		) {
229 			stop(i, 0u);
230 		}
231 	}
232 
233 #if !BLADERUNNER_ORIGINAL_BUGS
234 	// piggyback the realtime triggered tick() actions, with a check for the virtual timers (app timers)
235 	for (int i = 0; i < kAudioMixerAppTimersNum; ++i) {
236 		if (_audioMixerAppTimers[i].started
237 		    && _vm->_time->currentSystem() - _audioMixerAppTimers[i].lastFired > _audioMixerAppTimers[i].intervalMillis) {
238 			// We actually need to have the _vm->_time->currentSystem() check in the if clause
239 			// and not use a var that stores the current time before we enter the loop
240 			// because the functions for these timers may affect the lastFired, by setting it to the a current system time
241 			// and then lastFired would have been greater than our stored system time here.
242 			_audioMixerAppTimers[i].lastFired = _vm->_time->currentSystem();
243 			switch (i) {
244 			case kAudioMixerAppTimerMusicNext:
245 				_vm->_music->next();
246 				break;
247 			case kAudioMixerAppTimerMusicFadeOut:
248 				_vm->_music->fadeOut();
249 				break;
250 			default:
251 				// error - but probably won't happen
252 				error("Unknown Audio Mixer App Timer Id");
253 				break;
254 			}
255 		}
256 	}
257 #endif // !BLADERUNNER_ORIGINAL_BUGS
258 }
259 
260 #if !BLADERUNNER_ORIGINAL_BUGS
startAppTimerProc(int audioMixAppTimerId,uint32 intervalMillis)261 void AudioMixer::startAppTimerProc(int audioMixAppTimerId, uint32 intervalMillis) {
262 	// Attempt to lock the mutex, since we reach here from another thread (main thread)
263 	Common::StackLock lock(_mutex);
264 	if (audioMixAppTimerId  < 0 || audioMixAppTimerId >= kAudioMixerAppTimersNum) {
265 		return;
266 	}
267 	_audioMixerAppTimers[audioMixAppTimerId].started        = true;
268 	_audioMixerAppTimers[audioMixAppTimerId].intervalMillis = intervalMillis;
269 	_audioMixerAppTimers[audioMixAppTimerId].lastFired      = _vm->_time->currentSystem();
270 }
271 
stopAppTimerProc(int audioMixAppTimerId)272 void AudioMixer::stopAppTimerProc(int audioMixAppTimerId) {
273 	// Attempt to lock the mutex, since we reach here from another thread (main thread)
274 	Common::StackLock lock(_mutex);
275 	if (audioMixAppTimerId  < 0 || audioMixAppTimerId >= kAudioMixerAppTimersNum) {
276 		return;
277 	}
278 	_audioMixerAppTimers[audioMixAppTimerId].started = false;
279 }
280 #endif // !BLADERUNNER_ORIGINAL_BUGS
281 
282 } // End of namespace BladeRunner
283