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/file.h"
24 #include "common/stream.h"
25 #include "common/textconsole.h"
26 
27 #include "audio/mixer.h"
28 #include "audio/midiparser.h"
29 #include "audio/midiplayer.h"
30 #include "audio/mods/protracker.h"
31 #include "audio/decoders/raw.h"
32 
33 #include "parallaction/sound.h"
34 #include "parallaction/parallaction.h"
35 
36 
37 namespace Parallaction {
38 
39 class MidiPlayer : public Audio::MidiPlayer {
40 public:
41 
42 	MidiPlayer();
43 
44 	void play(Common::SeekableReadStream *stream);
45 	void pause(bool p);
pause()46 	void pause() override { assert(0); } // overridden
47 	void onTimer() override;
48 
49 private:
50 	bool _paused;
51 };
52 
MidiPlayer()53 MidiPlayer::MidiPlayer()
54 	: _paused(false) {
55 
56 	MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
57 	_driver = MidiDriver::createMidi(dev);
58 	assert(_driver);
59 
60 	int ret = _driver->open();
61 	if (ret == 0) {
62 		_driver->setTimerCallback(this, &timerCallback);
63 	}
64 }
65 
play(Common::SeekableReadStream * stream)66 void MidiPlayer::play(Common::SeekableReadStream *stream) {
67 	Common::StackLock lock(_mutex);
68 
69 	stop();
70 	if (!stream)
71 		return;
72 
73 	int size = stream->size();
74 	_midiData = (uint8 *)malloc(size);
75 	if (_midiData) {
76 		stream->read(_midiData, size);
77 		delete stream;
78 
79 		_parser = MidiParser::createParser_SMF();
80 		_parser->loadMusic(_midiData, size);
81 		_parser->setTrack(0);
82 		_parser->setMidiDriver(this);
83 		_parser->setTimerRate(_driver->getBaseTempo());
84 		_isLooping = true;
85 		_isPlaying = true;
86 	}
87 }
88 
pause(bool p)89 void MidiPlayer::pause(bool p) {
90 	_paused = p;
91 
92 	for (int i = 0; i < kNumChannels; ++i) {
93 		if (_channelsTable[i]) {
94 			_channelsTable[i]->volume(_paused ? 0 : _channelsVolume[i] * _masterVolume / 255);
95 		}
96 	}
97 }
98 
onTimer()99 void MidiPlayer::onTimer() {
100 	Common::StackLock lock(_mutex);
101 
102 	if (!_paused && _isPlaying && _parser) {
103 		_parser->onTimer();
104 	}
105 }
106 
DosSoundMan_ns(Parallaction_ns * vm)107 DosSoundMan_ns::DosSoundMan_ns(Parallaction_ns *vm) : SoundMan_ns(vm), _playing(false) {
108 	_midiPlayer = new MidiPlayer();
109 }
110 
~DosSoundMan_ns()111 DosSoundMan_ns::~DosSoundMan_ns() {
112 	debugC(1, kDebugAudio, "DosSoundMan_ns_ns::playMusic()");
113 
114 	delete _midiPlayer;
115 }
116 
isLocationSilent(const char * locationName)117 bool DosSoundMan_ns::isLocationSilent(const char *locationName) {
118 
119 	// these are the prefixes for location names with no background midi music
120 	const char *noMusicPrefix[] = { "museo", "intgrottadopo", "caveau", "estgrotta", "plaza1", "endtgz", "common", 0 };
121 	Common::String s(locationName);
122 
123 	for (int i = 0; noMusicPrefix[i]; i++) {
124 		if (s.hasPrefix(noMusicPrefix[i])) {
125 			return true;
126 		}
127 	}
128 
129 	return false;
130 }
131 
playMusic()132 void DosSoundMan_ns::playMusic() {
133 	debugC(1, kDebugAudio, "DosSoundMan_ns_ns::playMusic()");
134 
135 	if (isLocationSilent(_vm->_location._name)) {
136 		// just stop the music if this location is silent
137 		_midiPlayer->stop();
138 		return;
139 	}
140 
141 	Common::SeekableReadStream *stream = _vm->_disk->loadMusic(_musicFile);
142 	_midiPlayer->play(stream);
143 	_midiPlayer->setVolume(255);
144 
145 	_playing = true;
146 }
147 
stopMusic()148 void DosSoundMan_ns::stopMusic() {
149 	_musicFile[0] = 0;
150 	_midiPlayer->stop();
151 
152 	_playing = false;
153 }
154 
pause(bool p)155 void DosSoundMan_ns::pause(bool p) {
156 	SoundMan_ns::pause(p);
157 	_midiPlayer->pause(p);
158 }
159 
locationHasOwnSoftMusic(const char * locationName)160 bool DosSoundMan_ns::locationHasOwnSoftMusic(const char *locationName) {
161 	return !scumm_stricmp(locationName, "night") || !scumm_stricmp(locationName, "intsushi");
162 }
163 
playCharacterMusic(const char * character)164 void DosSoundMan_ns::playCharacterMusic(const char *character) {
165 	if (!character || locationHasOwnSoftMusic(_vm->_location._name)) {
166 		return;
167 	}
168 
169 	char *name = const_cast<char *>(character);
170 	const char *newMusicFile = 0;
171 
172 	if (!scumm_stricmp(name, g_dinoName)) {
173 		newMusicFile = "dino";
174 	} else
175 	if (!scumm_stricmp(name, g_donnaName)) {
176 		newMusicFile = "donna";
177 	} else
178 	if (!scumm_stricmp(name, g_doughName)) {
179 		newMusicFile = "nuts";
180 	} else {
181 		warning("unknown character '%s' in DosSoundMan_ns_ns::playCharacterMusic", character);
182 		return;
183 	}
184 
185 	if (!_playing || (newMusicFile && scumm_stricmp(newMusicFile, _musicFile))) {
186 		// avoid restarting the same piece
187 		setMusicFile(newMusicFile);
188 		playMusic();
189 		debugC(2, kDebugExec, "changeLocation: started character specific music (%s)", newMusicFile);
190 	}
191 }
192 
playLocationMusic(const char * location)193 void DosSoundMan_ns::playLocationMusic(const char *location) {
194 	if (locationHasOwnSoftMusic(location)) {
195 		setMusicFile("soft");
196 		playMusic();
197 		debugC(2, kDebugExec, "changeLocation: started music 'soft'");
198 	} else
199 	if (isLocationSilent(location)) {
200 		stopMusic();
201 		debugC(2, kDebugExec, "changeLocation: music stopped");
202 	} else {
203 		playCharacterMusic(_vm->_char.getBaseName());
204 	}
205 }
206 
207 
208 
209 
210 #pragma mark Amiga sound manager code
211 
212 
213 #define AMIGABEEP_SIZE	16
214 #define NUM_REPEATS		60
215 
216 static int8 res_amigaBeep[AMIGABEEP_SIZE] = {
217 	0, 20, 40, 60, 80, 60, 40, 20, 0, -20, -40, -60, -80, -60, -40, -20
218 };
219 
AmigaSoundMan_ns(Parallaction_ns * vm)220 AmigaSoundMan_ns::AmigaSoundMan_ns(Parallaction_ns *vm) : SoundMan_ns(vm) {
221 	_musicStream = 0;
222 
223 	// initialize the waveform for the 'beep' sound
224 	beepSoundBufferSize = AMIGABEEP_SIZE * NUM_REPEATS;
225 	beepSoundBuffer = new int8[beepSoundBufferSize];
226 	int8 *odata = beepSoundBuffer;
227 	for (int i = 0; i < NUM_REPEATS; i++) {
228 		memcpy(odata, res_amigaBeep, AMIGABEEP_SIZE);
229 		odata += AMIGABEEP_SIZE;
230 	}
231 
232 }
233 
~AmigaSoundMan_ns()234 AmigaSoundMan_ns::~AmigaSoundMan_ns() {
235 	stopMusic();
236 	stopSfx(0);
237 	stopSfx(1);
238 	stopSfx(2);
239 	stopSfx(3);
240 
241 	delete[] beepSoundBuffer;
242 }
243 
loadChannelData(const char * filename,Channel * ch,bool looping)244 Audio::AudioStream *AmigaSoundMan_ns::loadChannelData(const char *filename, Channel *ch, bool looping) {
245 	Audio::AudioStream *input = 0;
246 
247 	if (!scumm_stricmp("beep", filename)) {
248 		int rate = 11934;
249 		ch->volume = 160;
250 		input = Audio::makeRawStream((byte *)beepSoundBuffer, beepSoundBufferSize, rate, 0, DisposeAfterUse::NO);
251 	} else {
252 		Common::SeekableReadStream *stream = _vm->_disk->loadSound(filename);
253 		input = Audio::make8SVXStream(*stream, looping);
254 		delete stream;
255 	}
256 
257 	ch->stream = input;
258 
259 	return input;
260 }
261 
playSfx(const char * filename,uint channel,bool looping,int volume)262 void AmigaSoundMan_ns::playSfx(const char *filename, uint channel, bool looping, int volume) {
263 	if (channel >= NUM_SFX_CHANNELS) {
264 		warning("unknown sfx channel");
265 		return;
266 	}
267 
268 	stopSfx(channel);
269 
270 	debugC(1, kDebugAudio, "AmigaSoundMan_ns::playSfx(%s, %i)", filename, channel);
271 
272 	Channel *ch = &_channels[channel];
273 	Audio::AudioStream *input = loadChannelData(filename, ch, looping);
274 
275 	if (volume == -1) {
276 		volume = ch->volume;
277 	}
278 
279 	_mixer->playStream(Audio::Mixer::kSFXSoundType, &ch->handle, input, -1, volume);
280 }
281 
stopSfx(uint channel)282 void AmigaSoundMan_ns::stopSfx(uint channel) {
283 	if (channel >= NUM_SFX_CHANNELS) {
284 		warning("unknown sfx channel");
285 		return;
286 	}
287 
288 	debugC(1, kDebugAudio, "AmigaSoundMan_ns::stopSfx(%i)", channel);
289 	_mixer->stopHandle(_channels[channel].handle);
290 	_channels[channel].stream = 0;
291 }
292 
playMusic()293 void AmigaSoundMan_ns::playMusic() {
294 	stopMusic();
295 
296 	debugC(1, kDebugAudio, "AmigaSoundMan_ns::playMusic()");
297 
298 	Common::SeekableReadStream *stream = _vm->_disk->loadMusic(_musicFile);
299 	_musicStream = Audio::makeProtrackerStream(stream);
300 	delete stream;
301 
302 	debugC(3, kDebugAudio, "AmigaSoundMan_ns::playMusic(): created new music stream");
303 
304 	_mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, _musicStream, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, false);
305 }
306 
stopMusic()307 void AmigaSoundMan_ns::stopMusic() {
308 	debugC(1, kDebugAudio, "AmigaSoundMan_ns::stopMusic()");
309 
310 	if (_mixer->isSoundHandleActive(_musicHandle)) {
311 		_mixer->stopHandle(_musicHandle);
312 		delete _musicStream;
313 		_musicStream = 0;
314 	}
315 }
316 
playCharacterMusic(const char * character)317 void AmigaSoundMan_ns::playCharacterMusic(const char *character) {
318 }
319 
playLocationMusic(const char * location)320 void AmigaSoundMan_ns::playLocationMusic(const char *location) {
321 }
322 
323 
SoundMan_ns(Parallaction_ns * vm)324 SoundMan_ns::SoundMan_ns(Parallaction_ns *vm) : _vm(vm) {
325 	_mixer = _vm->_mixer;
326 	_sfxLooping = false;
327 	_sfxVolume = 0;
328 	_sfxRate = 0;
329 	_sfxChannel = 0;
330 	_musicType = 0;
331 }
332 
setMusicVolume(int value)333 void SoundMan_ns::setMusicVolume(int value) {
334 	_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, value);
335 }
336 
setMusicFile(const char * filename)337 void SoundMan_ns::setMusicFile(const char *filename) {
338 	Common::strlcpy(_musicFile, filename, PATH_LEN);
339 }
340 
execute(int command,const char * parm=0)341 void SoundMan_ns::execute(int command, const char *parm = 0) {
342 	uint32 n = strtoul(parm, 0, 10);
343 	bool b = (n == 1) ? true : false;
344 
345 	switch (command) {
346 	case SC_PLAYMUSIC:
347 		if (_musicType == MUSIC_CHARACTER) playCharacterMusic(parm);
348 		else if (_musicType == MUSIC_LOCATION) playLocationMusic(parm);
349 		else playMusic();
350 		break;
351 	case SC_STOPMUSIC:
352 		stopMusic();
353 		break;
354 	case SC_SETMUSICTYPE:
355 		_musicType = n;
356 		break;
357 	case SC_SETMUSICFILE:
358 		setMusicFile(parm);
359 		break;
360 
361 	case SC_PLAYSFX:
362 		playSfx(parm, _sfxChannel, _sfxLooping, _sfxVolume);
363 		break;
364 	case SC_STOPSFX:
365 		stopSfx(n);
366 		break;
367 
368 	case SC_SETSFXCHANNEL:
369 		_sfxChannel = n;
370 		break;
371 	case SC_SETSFXLOOPING:
372 		_sfxLooping = b;
373 		break;
374 	case SC_SETSFXVOLUME:
375 		_sfxVolume = n;
376 		break;
377 
378 	case SC_PAUSE:
379 		pause(b);
380 		break;
381 
382 	default:
383 		break;
384 	}
385 }
386 
387 } // namespace Parallaction
388