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 "common/memstream.h"
24 #include "audio/mixer.h"
25 #include "neverhood/sound.h"
26 #include "neverhood/resource.h"
27 #include "neverhood/resourceman.h"
28 
29 // Convert volume from percent to 0..255
30 #define VOLUME(volume) (Audio::Mixer::kMaxChannelVolume / 100 * (volume))
31 
32 // Convert panning from percent (50% equals center) to -127..0..+127
33 #define PANNING(panning) (254 / 100 * (panning) - 127)
34 
35 namespace Neverhood {
36 
SoundResource(NeverhoodEngine * vm)37 SoundResource::SoundResource(NeverhoodEngine *vm)
38 	: _vm(vm), _soundIndex(-1) {
39 }
40 
~SoundResource()41 SoundResource::~SoundResource() {
42 	unload();
43 }
44 
isPlaying()45 bool SoundResource::isPlaying() {
46 	AudioResourceManSoundItem *soundItem = getSoundItem();
47 	return soundItem ? soundItem->isPlaying() : false;
48 }
49 
load(uint32 fileHash)50 void SoundResource::load(uint32 fileHash) {
51 	unload();
52 	_soundIndex = _vm->_audioResourceMan->addSound(fileHash);
53 	AudioResourceManSoundItem *soundItem = getSoundItem();
54 	if (soundItem)
55 		soundItem->loadSound();
56 }
57 
unload()58 void SoundResource::unload() {
59 	if (_soundIndex >= 0) {
60 		_vm->_audioResourceMan->removeSound(_soundIndex);
61 		_soundIndex = -1;
62 	}
63 }
64 
play(uint32 fileHash)65 void SoundResource::play(uint32 fileHash) {
66 	load(fileHash);
67 	play();
68 }
69 
play()70 void SoundResource::play() {
71 	AudioResourceManSoundItem *soundItem = getSoundItem();
72 	if (soundItem)
73 		soundItem->playSound(false);
74 }
75 
playLooping()76 void SoundResource::playLooping() {
77 	AudioResourceManSoundItem *soundItem = getSoundItem();
78 	if (soundItem)
79 		soundItem->playSound(true);
80 }
81 
stop()82 void SoundResource::stop() {
83 	AudioResourceManSoundItem *soundItem = getSoundItem();
84 	if (soundItem)
85 		soundItem->stopSound();
86 }
87 
setVolume(int16 volume)88 void SoundResource::setVolume(int16 volume) {
89 	AudioResourceManSoundItem *soundItem = getSoundItem();
90 	if (soundItem)
91 		soundItem->setVolume(volume);
92 }
93 
setPan(int16 pan)94 void SoundResource::setPan(int16 pan) {
95 	AudioResourceManSoundItem *soundItem = getSoundItem();
96 	if (soundItem)
97 		soundItem->setPan(pan);
98 }
99 
getSoundItem()100 AudioResourceManSoundItem *SoundResource::getSoundItem() {
101 	return _vm->_audioResourceMan->getSoundItem(_soundIndex);
102 }
103 
MusicResource(NeverhoodEngine * vm)104 MusicResource::MusicResource(NeverhoodEngine *vm)
105 	: _vm(vm), _musicIndex(-1) {
106 }
107 
isPlaying()108 bool MusicResource::isPlaying() {
109 	AudioResourceManMusicItem *musicItem = getMusicItem();
110 	return musicItem && musicItem->isPlaying();
111 }
112 
load(uint32 fileHash)113 void MusicResource::load(uint32 fileHash) {
114 	unload();
115 	_musicIndex = _vm->_audioResourceMan->loadMusic(fileHash);
116 }
117 
unload()118 void MusicResource::unload() {
119 	AudioResourceManMusicItem *musicItem = getMusicItem();
120 	if (musicItem) {
121 		musicItem->unloadMusic();
122 		_musicIndex = -1;
123 	}
124 }
125 
play(int16 fadeVolumeStep)126 void MusicResource::play(int16 fadeVolumeStep) {
127 	AudioResourceManMusicItem *musicItem = getMusicItem();
128 	if (musicItem)
129 		musicItem->playMusic(fadeVolumeStep);
130 }
131 
stop(int16 fadeVolumeStep)132 void MusicResource::stop(int16 fadeVolumeStep) {
133 	AudioResourceManMusicItem *musicItem = getMusicItem();
134 	if (musicItem)
135 		musicItem->stopMusic(fadeVolumeStep);
136 }
137 
setVolume(int16 volume)138 void MusicResource::setVolume(int16 volume) {
139 	AudioResourceManMusicItem *musicItem = getMusicItem();
140 	if (musicItem)
141 		musicItem->setVolume(volume);
142 }
143 
getMusicItem()144 AudioResourceManMusicItem *MusicResource::getMusicItem() {
145 	return _vm->_audioResourceMan->getMusicItem(_musicIndex);
146 }
147 
MusicItem(NeverhoodEngine * vm,uint32 groupNameHash,uint32 musicFileHash)148 MusicItem::MusicItem(NeverhoodEngine *vm, uint32 groupNameHash, uint32 musicFileHash)
149 	: _vm(vm), _musicResource(NULL) {
150 
151 	_groupNameHash = groupNameHash;
152 	_fileHash = musicFileHash;
153 	_play = false;
154 	_stop = false;
155 	_fadeVolumeStep = 0;
156 	_countdown = 24;
157 	_musicResource = new MusicResource(_vm);
158 	_musicResource->load(musicFileHash);
159 }
160 
~MusicItem()161 MusicItem::~MusicItem() {
162 	if (_musicResource)
163 		_musicResource->unload();
164 	delete _musicResource;
165 }
166 
startMusic(int16 countdown,int16 fadeVolumeStep)167 void MusicItem::startMusic(int16 countdown, int16 fadeVolumeStep) {
168 	_play = true;
169 	_stop = false;
170 	_countdown = countdown;
171 	_fadeVolumeStep = fadeVolumeStep;
172 }
173 
stopMusic(int16 countdown,int16 fadeVolumeStep)174 void MusicItem::stopMusic(int16 countdown, int16 fadeVolumeStep) {
175 	_play = false;
176 	_stop = true;
177 	_countdown = countdown;
178 	_fadeVolumeStep = fadeVolumeStep;
179 }
180 
update()181 void MusicItem::update() {
182 	if (_countdown) {
183 		--_countdown;
184 	} else if (_play && !_musicResource->isPlaying()) {
185 		debug(1, "MusicItem: play music %08X (fade %d)", _fileHash, _fadeVolumeStep);
186 		_musicResource->play(_fadeVolumeStep);
187 		_fadeVolumeStep = 0;
188 	} else if (_stop) {
189 		debug(1, "MusicItem: stop music %08X (fade %d)", _fileHash, _fadeVolumeStep);
190 		_musicResource->stop(_fadeVolumeStep);
191 		_fadeVolumeStep = 0;
192 		_stop = false;
193 	}
194 }
195 
SoundItem(NeverhoodEngine * vm,uint32 groupNameHash,uint32 soundFileHash,bool playOnceAfterRandomCountdown,int16 minCountdown,int16 maxCountdown,bool playOnceAfterCountdown,int16 initialCountdown,bool playLooping,int16 currCountdown)196 SoundItem::SoundItem(NeverhoodEngine *vm, uint32 groupNameHash, uint32 soundFileHash,
197 	bool playOnceAfterRandomCountdown, int16 minCountdown, int16 maxCountdown,
198 	bool playOnceAfterCountdown, int16 initialCountdown, bool playLooping, int16 currCountdown)
199 	: _vm(vm), _soundResource(NULL), _groupNameHash(groupNameHash), _fileHash(soundFileHash),
200 	_playOnceAfterRandomCountdown(false), _minCountdown(0), _maxCountdown(0),
201 	_playOnceAfterCountdown(playOnceAfterCountdown), _initialCountdown(initialCountdown),
202 	_playLooping(false), _currCountdown(currCountdown) {
203 
204 	_soundResource = new SoundResource(vm);
205 	_soundResource->load(soundFileHash);
206 }
207 
~SoundItem()208 SoundItem::~SoundItem() {
209 	if (_soundResource)
210 		_soundResource->unload();
211 	delete _soundResource;
212 }
213 
setSoundParams(bool playOnceAfterRandomCountdown,int16 minCountdown,int16 maxCountdown,int16 firstMinCountdown,int16 firstMaxCountdown)214 void SoundItem::setSoundParams(bool playOnceAfterRandomCountdown, int16 minCountdown, int16 maxCountdown,
215 	int16 firstMinCountdown, int16 firstMaxCountdown) {
216 
217 	_playOnceAfterCountdown = false;
218 	_playLooping = false;
219 	_playOnceAfterRandomCountdown = playOnceAfterRandomCountdown;
220 	if (minCountdown > 0)
221 		_minCountdown = minCountdown;
222 	if (maxCountdown > 0)
223 		_maxCountdown = maxCountdown;
224 	if (firstMinCountdown > firstMaxCountdown)
225 		_currCountdown = firstMinCountdown;
226 	else if (firstMinCountdown > 0 && firstMaxCountdown > 0 && firstMinCountdown < firstMaxCountdown)
227 		_currCountdown = _vm->_rnd->getRandomNumberRng(firstMinCountdown, firstMaxCountdown);
228 }
229 
playSoundLooping()230 void SoundItem::playSoundLooping() {
231 	_playOnceAfterRandomCountdown = false;
232 	_playOnceAfterCountdown = false;
233 	_playLooping = true;
234 }
235 
stopSound()236 void SoundItem::stopSound() {
237 	_playOnceAfterRandomCountdown = false;
238 	_playOnceAfterCountdown = false;
239 	_playLooping = false;
240 	_soundResource->stop();
241 }
242 
setVolume(int volume)243 void SoundItem::setVolume(int volume) {
244 	_soundResource->setVolume(volume);
245 }
246 
update()247 void SoundItem::update() {
248 	if (_playOnceAfterCountdown) {
249 		if (_currCountdown == 0)
250 			_currCountdown = _initialCountdown;
251 		else if (--_currCountdown <= 0)
252 			_soundResource->play();
253 	} else if (_playOnceAfterRandomCountdown) {
254 		if (_currCountdown == 0) {
255 			if (_minCountdown > 0 && _maxCountdown > 0 && _minCountdown < _maxCountdown)
256 				_currCountdown = _vm->_rnd->getRandomNumberRng(_minCountdown, _maxCountdown);
257 		} else if (--_currCountdown <= 0)
258 			_soundResource->play();
259 	} else if (_playLooping && !_soundResource->isPlaying())
260 		_soundResource->playLooping();
261 }
262 
263 // SoundMan
264 
SoundMan(NeverhoodEngine * vm)265 SoundMan::SoundMan(NeverhoodEngine *vm)
266 	: _vm(vm), _soundIndex1(-1), _soundIndex2(-1), _soundIndex3(-1),
267 	  _initialCountdown(15), _playOnceAfterCountdown(false),
268 	  _initialCountdown3(9), _playOnceAfterCountdown3(false) {
269 }
270 
~SoundMan()271 SoundMan::~SoundMan() {
272 	stopAllMusic();
273 	stopAllSounds();
274 }
275 
stopAllMusic()276 void SoundMan::stopAllMusic() {
277 	for (uint i = 0; i < _musicItems.size(); ++i) {
278 		if (_musicItems[i]) {
279 			_musicItems[i]->stopMusic(0, 0);
280 			delete _musicItems[i];
281 			_musicItems[i] = NULL;
282 		}
283 	}
284 }
285 
stopAllSounds()286 void SoundMan::stopAllSounds() {
287 	for (uint i = 0; i < _soundItems.size(); ++i) {
288 		if (_soundItems[i]) {
289 			_soundItems[i]->stopSound();
290 			delete _soundItems[i];
291 			_soundItems[i] = NULL;
292 		}
293 	}
294 
295 	_soundIndex1 = _soundIndex2 = _soundIndex3 = -1;
296 }
297 
addMusic(uint32 groupNameHash,uint32 musicFileHash)298 void SoundMan::addMusic(uint32 groupNameHash, uint32 musicFileHash) {
299 	addMusicItem(new MusicItem(_vm, groupNameHash, musicFileHash));
300 }
301 
deleteMusic(uint32 musicFileHash)302 void SoundMan::deleteMusic(uint32 musicFileHash) {
303 	MusicItem *musicItem = getMusicItemByHash(musicFileHash);
304 	if (musicItem) {
305 		delete musicItem;
306 		for (uint i = 0; i < _musicItems.size(); ++i)
307 			if (_musicItems[i] == musicItem) {
308 				_musicItems[i] = NULL;
309 				break;
310 			}
311 	}
312 }
313 
startMusic(uint32 musicFileHash,int16 countdown,int16 fadeVolumeStep)314 void SoundMan::startMusic(uint32 musicFileHash, int16 countdown, int16 fadeVolumeStep) {
315 	MusicItem *musicItem = getMusicItemByHash(musicFileHash);
316 	if (musicItem)
317 		musicItem->startMusic(countdown, fadeVolumeStep);
318 }
319 
stopMusic(uint32 musicFileHash,int16 countdown,int16 fadeVolumeStep)320 void SoundMan::stopMusic(uint32 musicFileHash, int16 countdown, int16 fadeVolumeStep) {
321 	MusicItem *musicItem = getMusicItemByHash(musicFileHash);
322 	if (musicItem)
323 		musicItem->stopMusic(countdown, fadeVolumeStep);
324 }
325 
addSound(uint32 groupNameHash,uint32 soundFileHash)326 void SoundMan::addSound(uint32 groupNameHash, uint32 soundFileHash) {
327 	addSoundItem(new SoundItem(_vm, groupNameHash, soundFileHash, false, 50, 600, false, 0, false, 0));
328 }
329 
addSoundList(uint32 groupNameHash,const uint32 * soundFileHashList)330 void SoundMan::addSoundList(uint32 groupNameHash, const uint32 *soundFileHashList) {
331 	while (*soundFileHashList)
332 		addSound(groupNameHash, *soundFileHashList++);
333 }
334 
deleteSound(uint32 soundFileHash)335 void SoundMan::deleteSound(uint32 soundFileHash) {
336 	SoundItem *soundItem = getSoundItemByHash(soundFileHash);
337 	if (soundItem) {
338 		delete soundItem;
339 		for (uint i = 0; i < _soundItems.size(); ++i)
340 			if (_soundItems[i] == soundItem) {
341 				_soundItems[i] = NULL;
342 				break;
343 			}
344 	}
345 }
346 
setSoundParams(uint32 soundFileHash,bool playOnceAfterRandomCountdown,int16 minCountdown,int16 maxCountdown,int16 firstMinCountdown,int16 firstMaxCountdown)347 void SoundMan::setSoundParams(uint32 soundFileHash, bool playOnceAfterRandomCountdown,
348 	int16 minCountdown, int16 maxCountdown, int16 firstMinCountdown, int16 firstMaxCountdown) {
349 
350 	SoundItem *soundItem = getSoundItemByHash(soundFileHash);
351 	if (soundItem)
352 		soundItem->setSoundParams(playOnceAfterRandomCountdown, minCountdown, maxCountdown,
353 			firstMinCountdown, firstMaxCountdown);
354 }
355 
setSoundListParams(const uint32 * soundFileHashList,bool playOnceAfterRandomCountdown,int16 minCountdown,int16 maxCountdown,int16 firstMinCountdown,int16 firstMaxCountdown)356 void SoundMan::setSoundListParams(const uint32 *soundFileHashList, bool playOnceAfterRandomCountdown,
357 	int16 minCountdown, int16 maxCountdown, int16 firstMinCountdown, int16 firstMaxCountdown) {
358 
359 	while (*soundFileHashList)
360 		setSoundParams(*soundFileHashList++, playOnceAfterRandomCountdown,
361 			minCountdown, maxCountdown, firstMinCountdown, firstMaxCountdown);
362 }
363 
playSoundLooping(uint32 soundFileHash)364 void SoundMan::playSoundLooping(uint32 soundFileHash) {
365 	SoundItem *soundItem = getSoundItemByHash(soundFileHash);
366 	if (soundItem)
367 		soundItem->playSoundLooping();
368 }
369 
stopSound(uint32 soundFileHash)370 void SoundMan::stopSound(uint32 soundFileHash) {
371 	SoundItem *soundItem = getSoundItemByHash(soundFileHash);
372 	if (soundItem)
373 		soundItem->stopSound();
374 }
375 
setSoundVolume(uint32 soundFileHash,int volume)376 void SoundMan::setSoundVolume(uint32 soundFileHash, int volume) {
377 	SoundItem *soundItem = getSoundItemByHash(soundFileHash);
378 	if (soundItem)
379 		soundItem->setVolume(volume);
380 }
381 
update()382 void SoundMan::update() {
383 
384 	for (uint i = 0; i < _soundItems.size(); ++i) {
385 		SoundItem *soundItem = _soundItems[i];
386 		if (soundItem)
387 			soundItem->update();
388 	}
389 	for (uint i = 0; i < _musicItems.size(); ++i) {
390 		MusicItem *musicItem = _musicItems[i];
391 		if (musicItem)
392 			musicItem->update();
393 	}
394 
395 }
396 
deleteGroup(uint32 groupNameHash)397 void SoundMan::deleteGroup(uint32 groupNameHash) {
398 	deleteMusicGroup(groupNameHash);
399 	deleteSoundGroup(groupNameHash);
400 }
401 
deleteMusicGroup(uint32 groupNameHash)402 void SoundMan::deleteMusicGroup(uint32 groupNameHash) {
403 	for (uint index = 0; index < _musicItems.size(); ++index) {
404 		MusicItem *musicItem = _musicItems[index];
405 		if (musicItem && musicItem->getGroupNameHash() == groupNameHash) {
406 			delete musicItem;
407 			_musicItems[index] = NULL;
408 		}
409 	}
410 }
411 
deleteSoundGroup(uint32 groupNameHash)412 void SoundMan::deleteSoundGroup(uint32 groupNameHash) {
413 
414 	if (_soundIndex1 != -1 && _soundItems[_soundIndex1]->getGroupNameHash() == groupNameHash) {
415 		deleteSoundByIndex(_soundIndex1);
416 		_soundIndex1 = -1;
417 	}
418 
419 	if (_soundIndex2 != -1 && _soundItems[_soundIndex2]->getGroupNameHash() == groupNameHash) {
420 		deleteSoundByIndex(_soundIndex2);
421 		_soundIndex2 = -1;
422 	}
423 
424 	if (_soundIndex3 != -1 && _soundItems[_soundIndex3]->getGroupNameHash() == groupNameHash) {
425 		deleteSoundByIndex(_soundIndex3);
426 		_soundIndex3 = -1;
427 	}
428 
429 	for (uint index = 0; index < _soundItems.size(); ++index)
430 		if (_soundItems[index] && _soundItems[index]->getGroupNameHash() == groupNameHash)
431 			deleteSoundByIndex(index);
432 
433 }
434 
playTwoSounds(uint32 groupNameHash,uint32 soundFileHash1,uint32 soundFileHash2,int16 initialCountdown)435 void SoundMan::playTwoSounds(uint32 groupNameHash, uint32 soundFileHash1, uint32 soundFileHash2, int16 initialCountdown) {
436 
437 	int16 currCountdown1 = _initialCountdown;
438 	int16 currCountdown2 = _initialCountdown / 2;
439 
440 	if (_soundIndex1 != -1) {
441 		currCountdown1 = _soundItems[_soundIndex1]->getCurrCountdown();
442 		deleteSoundByIndex(_soundIndex1);
443 		_soundIndex1 = -1;
444 	}
445 
446 	if (_soundIndex2 != -1) {
447 		currCountdown2 = _soundItems[_soundIndex2]->getCurrCountdown();
448 		deleteSoundByIndex(_soundIndex2);
449 		_soundIndex2 = -1;
450 	}
451 
452 	if (initialCountdown > 0)
453 		_initialCountdown = initialCountdown;
454 
455 	if (soundFileHash1 != 0) {
456 		SoundItem *soundItem = new SoundItem(_vm, groupNameHash, soundFileHash1, false, 0, 0,
457 			_playOnceAfterCountdown, _initialCountdown, false, currCountdown1);
458 		soundItem->setVolume(80);
459 		_soundIndex1 = addSoundItem(soundItem);
460 	}
461 
462 	if (soundFileHash2 != 0) {
463 		SoundItem *soundItem = new SoundItem(_vm, groupNameHash, soundFileHash2, false, 0, 0,
464 			_playOnceAfterCountdown, _initialCountdown, false, currCountdown2);
465 		soundItem->setVolume(80);
466 		_soundIndex2 = addSoundItem(soundItem);
467 	}
468 
469 }
470 
playSoundThree(uint32 groupNameHash,uint32 soundFileHash)471 void SoundMan::playSoundThree(uint32 groupNameHash, uint32 soundFileHash) {
472 
473 	if (_soundIndex3 != -1) {
474 		deleteSoundByIndex(_soundIndex3);
475 		_soundIndex3 = -1;
476 	}
477 
478 	if (soundFileHash != 0) {
479 		SoundItem *soundItem = new SoundItem(_vm, groupNameHash, soundFileHash, false, 0, 0, false, _initialCountdown3, false, 0);
480 		_soundIndex3 = addSoundItem(soundItem);
481 	}
482 
483 }
484 
setTwoSoundsPlayFlag(bool playOnceAfterCountdown)485 void SoundMan::setTwoSoundsPlayFlag(bool playOnceAfterCountdown) {
486 	if (_soundIndex1 != -1)
487 		_soundItems[_soundIndex1]->setPlayOnceAfterCountdown(playOnceAfterCountdown);
488 	if (_soundIndex2 != -1)
489 		_soundItems[_soundIndex2]->setPlayOnceAfterCountdown(playOnceAfterCountdown);
490 	_playOnceAfterCountdown = playOnceAfterCountdown;
491 }
492 
setSoundThreePlayFlag(bool playOnceAfterCountdown)493 void SoundMan::setSoundThreePlayFlag(bool playOnceAfterCountdown) {
494 	if (_soundIndex3 != -1)
495 		_soundItems[_soundIndex3]->setPlayOnceAfterCountdown(playOnceAfterCountdown);
496 	_playOnceAfterCountdown3 = playOnceAfterCountdown;
497 }
498 
getMusicItemByHash(uint32 musicFileHash)499 MusicItem *SoundMan::getMusicItemByHash(uint32 musicFileHash) {
500 	for (uint i = 0; i < _musicItems.size(); ++i)
501 		if (_musicItems[i] && _musicItems[i]->getFileHash() == musicFileHash)
502 			return _musicItems[i];
503 	return NULL;
504 }
505 
getSoundItemByHash(uint32 soundFileHash)506 SoundItem *SoundMan::getSoundItemByHash(uint32 soundFileHash) {
507 	for (uint i = 0; i < _soundItems.size(); ++i)
508 		if (_soundItems[i] && _soundItems[i]->getFileHash() == soundFileHash)
509 			return _soundItems[i];
510 	return NULL;
511 }
512 
addMusicItem(MusicItem * musicItem)513 int16 SoundMan::addMusicItem(MusicItem *musicItem) {
514 	for (uint i = 0; i < _musicItems.size(); ++i)
515 		if (!_musicItems[i]) {
516 			_musicItems[i] = musicItem;
517 			return i;
518 		}
519 	int16 musicIndex = _musicItems.size();
520 	_musicItems.push_back(musicItem);
521 	return musicIndex;
522 }
523 
addSoundItem(SoundItem * soundItem)524 int16 SoundMan::addSoundItem(SoundItem *soundItem) {
525 	for (uint i = 0; i < _soundItems.size(); ++i)
526 		if (!_soundItems[i]) {
527 			_soundItems[i] = soundItem;
528 			return i;
529 		}
530 	int16 soundIndex = _soundItems.size();
531 	_soundItems.push_back(soundItem);
532 	return soundIndex;
533 }
534 
deleteSoundByIndex(int index)535 void SoundMan::deleteSoundByIndex(int index) {
536 	delete _soundItems[index];
537 	_soundItems[index] = NULL;
538 }
539 
540 // NeverhoodAudioStream
541 
NeverhoodAudioStream(int rate,byte shiftValue,bool isLooping,DisposeAfterUse::Flag disposeStream,Common::SeekableReadStream * stream)542 NeverhoodAudioStream::NeverhoodAudioStream(int rate, byte shiftValue, bool isLooping, DisposeAfterUse::Flag disposeStream, Common::SeekableReadStream *stream)
543 	: _rate(rate), _shiftValue(shiftValue), _isLooping(isLooping), _isStereo(false), _stream(stream, disposeStream), _endOfData(false), _buffer(0),
544 	_isCompressed(_shiftValue != 0xFF), _prevValue(0) {
545 	// Setup our buffer for readBuffer
546 	_buffer = new byte[kSampleBufferLength * (_isCompressed ? 1 : 2)];
547 	assert(_buffer);
548 }
549 
~NeverhoodAudioStream()550 NeverhoodAudioStream::~NeverhoodAudioStream() {
551 	delete[] _buffer;
552 }
553 
readBuffer(int16 * buffer,const int numSamples)554 int NeverhoodAudioStream::readBuffer(int16 *buffer, const int numSamples) {
555 	int samplesLeft = numSamples;
556 
557 	while (samplesLeft > 0 && !_endOfData) {
558 
559 		const int maxSamples = MIN<int>(kSampleBufferLength, samplesLeft);
560 		const int bytesToRead = maxSamples * (_isCompressed ? 1 : 2);
561 		int bytesRead = _stream->read(_buffer, bytesToRead);
562 		int samplesRead = bytesRead / (_isCompressed ? 1 : 2);
563 
564 		samplesLeft -= samplesRead;
565 
566 		const byte *src = _buffer;
567 		if (_isCompressed) {
568 			while (samplesRead--) {
569 				_prevValue += (int8)(*src++);
570 				*buffer++ = _prevValue << _shiftValue;
571 			}
572 		} else {
573 			while (samplesRead--) {
574 				*buffer++ = READ_LE_UINT16(src);
575 				src += 2;
576 			}
577 		}
578 
579 		if (bytesRead < bytesToRead || _stream->pos() >= _stream->size() || _stream->err() || _stream->eos()) {
580 			if (_isLooping) {
581 				_stream->seek(0);
582 				_prevValue = 0;
583 			} else {
584 				_endOfData = true;
585 			}
586 		}
587 
588 	}
589 
590 	return numSamples - samplesLeft;
591 }
592 
AudioResourceManSoundItem(NeverhoodEngine * vm,uint32 fileHash)593 AudioResourceManSoundItem::AudioResourceManSoundItem(NeverhoodEngine *vm, uint32 fileHash)
594 	: _vm(vm), _fileHash(fileHash), _data(NULL), _isLoaded(false), _isPlaying(false),
595 	_volume(100), _panning(50) {
596 
597 	_vm->_res->queryResource(_fileHash, _resourceHandle);
598 	_soundHandle = new Audio::SoundHandle();
599 }
600 
~AudioResourceManSoundItem()601 AudioResourceManSoundItem::~AudioResourceManSoundItem() {
602 	delete _soundHandle;
603 }
604 
loadSound()605 void AudioResourceManSoundItem::loadSound() {
606 	if (!_data && _resourceHandle.isValid() &&
607 		(_resourceHandle.type() == kResTypeSound || _resourceHandle.type() == kResTypeMusic)) {
608 		_vm->_res->loadResource(_resourceHandle, _vm->applyResourceFixes());
609 		_data = _resourceHandle.data();
610 	}
611 }
612 
unloadSound()613 void AudioResourceManSoundItem::unloadSound() {
614 	if (_vm->_mixer->isSoundHandleActive(*_soundHandle))
615 		_vm->_mixer->stopHandle(*_soundHandle);
616 	_vm->_res->unloadResource(_resourceHandle);
617 	_data = NULL;
618 }
619 
setVolume(int16 volume)620 void AudioResourceManSoundItem::setVolume(int16 volume) {
621 	_volume = MIN<int16>(volume, 100);
622 	if (_isPlaying && _vm->_mixer->isSoundHandleActive(*_soundHandle))
623 		_vm->_mixer->setChannelVolume(*_soundHandle, VOLUME(_volume));
624 }
625 
setPan(int16 pan)626 void AudioResourceManSoundItem::setPan(int16 pan) {
627 	_panning = MIN<int16>(pan, 100);
628 	if (_isPlaying && _vm->_mixer->isSoundHandleActive(*_soundHandle))
629 		_vm->_mixer->setChannelVolume(*_soundHandle, PANNING(_panning));
630 }
631 
playSound(bool looping)632 void AudioResourceManSoundItem::playSound(bool looping) {
633 	if (!_data)
634 		loadSound();
635 	if (_data) {
636 		const byte *shiftValue = _resourceHandle.extData();
637 		Common::MemoryReadStream *stream = new Common::MemoryReadStream(_data, _resourceHandle.size(), DisposeAfterUse::NO);
638 		NeverhoodAudioStream *audioStream = new NeverhoodAudioStream(22050, *shiftValue, looping, DisposeAfterUse::YES, stream);
639 		_vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, _soundHandle,
640 			audioStream, -1, VOLUME(_volume), PANNING(_panning));
641 		debug(1, "playing sound %08X", _fileHash);
642 		_isPlaying = true;
643 	}
644 }
645 
stopSound()646 void AudioResourceManSoundItem::stopSound() {
647 	if (_vm->_mixer->isSoundHandleActive(*_soundHandle))
648 		_vm->_mixer->stopHandle(*_soundHandle);
649 	_isPlaying = false;
650 }
651 
isPlaying()652 bool AudioResourceManSoundItem::isPlaying() {
653 	return _vm->_mixer->isSoundHandleActive(*_soundHandle);
654 }
655 
AudioResourceManMusicItem(NeverhoodEngine * vm,uint32 fileHash)656 AudioResourceManMusicItem::AudioResourceManMusicItem(NeverhoodEngine *vm, uint32 fileHash)
657 	: _vm(vm), _fileHash(fileHash), _terminate(false), _canRestart(false),
658 	_volume(100), _panning(50),	_start(false), _isFadingIn(false), _isFadingOut(false), _isPlaying(false),
659 	_fadeVolume(0), _fadeVolumeStep(0) {
660 
661 	_soundHandle = new Audio::SoundHandle();
662 }
663 
~AudioResourceManMusicItem()664 AudioResourceManMusicItem::~AudioResourceManMusicItem() {
665 	delete _soundHandle;
666 }
667 
playMusic(int16 fadeVolumeStep)668 void AudioResourceManMusicItem::playMusic(int16 fadeVolumeStep) {
669 	if (!_isPlaying) {
670 		_isFadingIn = false;
671 		_isFadingOut = false;
672 		if (fadeVolumeStep != 0) {
673 			_isFadingIn = true;
674 			_fadeVolume = 0;
675 			_fadeVolumeStep = fadeVolumeStep;
676 		}
677 		_start = true;
678 		_terminate = false;
679 	}
680 }
681 
stopMusic(int16 fadeVolumeStep)682 void AudioResourceManMusicItem::stopMusic(int16 fadeVolumeStep) {
683 	if (_vm->_mixer->isSoundHandleActive(*_soundHandle)) {
684 		if (fadeVolumeStep != 0) {
685 			if (_isFadingIn)
686 				_isFadingIn = false;
687 			else
688 				_fadeVolume = _volume;
689 			_isFadingOut = true;
690 			_fadeVolumeStep = fadeVolumeStep;
691 		} else {
692 			_vm->_mixer->stopHandle(*_soundHandle);
693 		}
694 		_isPlaying = false;
695 	}
696 }
697 
unloadMusic()698 void AudioResourceManMusicItem::unloadMusic() {
699 	if (_isFadingOut) {
700 		_canRestart = true;
701 	} else {
702 		if (_vm->_mixer->isSoundHandleActive(*_soundHandle))
703 			_vm->_mixer->stopHandle(*_soundHandle);
704 		_isPlaying = false;
705 		_terminate = true;
706 	}
707 }
708 
setVolume(int16 volume)709 void AudioResourceManMusicItem::setVolume(int16 volume) {
710 	_volume = MIN<int16>(volume, 100);
711 	if (_isPlaying && _vm->_mixer->isSoundHandleActive(*_soundHandle))
712 		_vm->_mixer->setChannelVolume(*_soundHandle, VOLUME(_volume));
713 }
714 
restart()715 void AudioResourceManMusicItem::restart() {
716 	_canRestart = false;
717 	_isFadingOut = false;
718 	_isFadingIn = true;
719 }
720 
update()721 void AudioResourceManMusicItem::update() {
722 
723 	if (_start && !_vm->_mixer->isSoundHandleActive(*_soundHandle)) {
724 		ResourceHandle resourceHandle;
725 		_vm->_res->queryResource(_fileHash, resourceHandle);
726 		Common::SeekableReadStream *stream = _vm->_res->createStream(_fileHash);
727 		const byte *shiftValue = resourceHandle.extData();
728 		NeverhoodAudioStream *audioStream = new NeverhoodAudioStream(22050, *shiftValue, true, DisposeAfterUse::YES, stream);
729 		_vm->_mixer->playStream(Audio::Mixer::kMusicSoundType, _soundHandle,
730 			audioStream, -1, VOLUME(_isFadingIn ? _fadeVolume : _volume),
731 			PANNING(_panning));
732 		_start = false;
733 		_isPlaying = true;
734 	}
735 
736 	if (_vm->_mixer->isSoundHandleActive(*_soundHandle)) {
737 		if (_isFadingIn) {
738 			_fadeVolume += _fadeVolumeStep;
739 			if (_fadeVolume >= _volume) {
740 				_fadeVolume = _volume;
741 				_isFadingIn = false;
742 			}
743 			_vm->_mixer->setChannelVolume(*_soundHandle, VOLUME(_fadeVolume));
744 		}
745 		if (_isFadingOut) {
746 			_fadeVolume -= _fadeVolumeStep;
747 			if (_fadeVolume < 0)
748 				_fadeVolume = 0;
749 			_vm->_mixer->setChannelVolume(*_soundHandle, VOLUME(_fadeVolume));
750 			if (_fadeVolume == 0) {
751 				_isFadingOut = false;
752 				stopMusic(0);
753 				if (_canRestart)
754 					unloadMusic();
755 			}
756 		}
757 	}
758 
759 }
760 
AudioResourceMan(NeverhoodEngine * vm)761 AudioResourceMan::AudioResourceMan(NeverhoodEngine *vm)
762 	: _vm(vm) {
763 }
764 
stopAllMusic()765 void AudioResourceMan::stopAllMusic() {
766 	for (uint i = 0; i < _musicItems.size(); ++i) {
767 		if (_musicItems[i]) {
768 			_musicItems[i]->stopMusic(0);
769 			delete _musicItems[i];
770 			_musicItems[i] = NULL;
771 		}
772 	}
773 }
774 
stopAllSounds()775 void AudioResourceMan::stopAllSounds() {
776 	for (uint i = 0; i < _soundItems.size(); ++i) {
777 		if (_soundItems[i]) {
778 			_soundItems[i]->stopSound();
779 			delete _soundItems[i];
780 			_soundItems[i] = NULL;
781 		}
782 	}
783 }
784 
~AudioResourceMan()785 AudioResourceMan::~AudioResourceMan() {
786 	stopAllMusic();
787 	stopAllSounds();
788 }
789 
addSound(uint32 fileHash)790 int16 AudioResourceMan::addSound(uint32 fileHash) {
791 	AudioResourceManSoundItem *soundItem = new AudioResourceManSoundItem(_vm, fileHash);
792 
793 	for (uint i = 0; i < _soundItems.size(); ++i)
794 		if (!_soundItems[i]) {
795 			_soundItems[i] = soundItem;
796 			return i;
797 		}
798 
799 	int16 soundIndex = (int16)_soundItems.size();
800 	_soundItems.push_back(soundItem);
801 	return soundIndex;
802 }
803 
removeSound(int16 soundIndex)804 void AudioResourceMan::removeSound(int16 soundIndex) {
805 	AudioResourceManSoundItem *soundItem = getSoundItem(soundIndex);
806 	if (soundItem) {
807 		soundItem->unloadSound();
808 		delete soundItem;
809 		_soundItems[soundIndex] = NULL;
810 	}
811 }
812 
loadMusic(uint32 fileHash)813 int16 AudioResourceMan::loadMusic(uint32 fileHash) {
814 	AudioResourceManMusicItem *musicItem;
815 
816 	for (uint i = 0; i < _musicItems.size(); ++i) {
817 		musicItem = _musicItems[i];
818 		if (musicItem && musicItem->getFileHash() == fileHash && musicItem->canRestart()) {
819 			musicItem->restart();
820 			return i;
821 		}
822 	}
823 
824 	musicItem = new AudioResourceManMusicItem(_vm, fileHash);
825 
826 	for (uint i = 0; i < _musicItems.size(); ++i) {
827 		if (!_musicItems[i]) {
828 			_musicItems[i] = musicItem;
829 			return i;
830 		}
831 	}
832 
833 	int16 musicIndex = _musicItems.size();
834 	_musicItems.push_back(musicItem);
835 	return musicIndex;
836 
837 }
838 
updateMusic()839 void AudioResourceMan::updateMusic() {
840 	for (uint musicIndex = 0; musicIndex < _musicItems.size(); ++musicIndex) {
841 		AudioResourceManMusicItem *musicItem = _musicItems[musicIndex];
842 		if (musicItem) {
843 			musicItem->update();
844 			if (musicItem->isTerminated()) {
845 				delete musicItem;
846 				_musicItems[musicIndex] = NULL;
847 			}
848 		}
849 	}
850 }
851 
getSoundItem(int16 index)852 AudioResourceManSoundItem *AudioResourceMan::getSoundItem(int16 index) {
853 	return (index >= 0 && index < (int16)_soundItems.size()) ? _soundItems[index] : NULL;
854 }
855 
getMusicItem(int16 index)856 AudioResourceManMusicItem *AudioResourceMan::getMusicItem(int16 index) {
857 	return (index >= 0 && index < (int16)_musicItems.size()) ? _musicItems[index] : NULL;
858 }
859 
860 } // End of namespace Neverhood
861