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