33
/* sound.c - sound effects and music support */
33
// sound.c - sound effects and music support
35
#include "common/debug.h"
35
36
#include "common/system.h"
37
#include "common/textconsole.h"
38
#include "common/config-manager.h"
37
#include "sound/decoders/raw.h"
38
#include "sound/audiostream.h"
39
#include "sound/midiparser.h"
40
#include "sound/mididrv.h"
40
#include "audio/decoders/raw.h"
41
#include "audio/audiostream.h"
42
#include "audio/midiparser.h"
42
44
#include "hugo/hugo.h"
43
45
#include "hugo/game.h"
44
46
#include "hugo/file.h"
45
47
#include "hugo/sound.h"
48
#include "hugo/text.h"
49
class MidiPlayer : public MidiDriver {
56
MidiPlayer(MidiDriver *driver);
59
void play(uint8 *stream, uint16 size);
63
void adjustVolume(int diff);
64
void setVolume(int volume);
65
int getVolume() const { return _masterVolume; }
66
void setLooping(bool loop) { _isLooping = loop; }
68
// MidiDriver interface
72
void metaEvent(byte type, byte *data, uint16 length);
73
void setTimerCallback(void *timerParam, void (*timerProc)(void *)) { }
74
uint32 getBaseTempo() { return _driver ? _driver->getBaseTempo() : 0; }
75
MidiChannel *allocateChannel() { return 0; }
76
MidiChannel *getPercussionChannel() { return 0; }
80
static void timerCallback(void *p);
89
MidiChannel *_channelsTable[NUM_CHANNELS];
90
uint8 _channelsVolume[NUM_CHANNELS];
94
MidiPlayer::MidiPlayer(MidiDriver *driver)
95
: _driver(driver), _parser(0), _midiData(0), _isLooping(false), _isPlaying(false), _paused(false), _masterVolume(0) {
52
MidiPlayer::MidiPlayer() {
53
MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
54
_driver = MidiDriver::createMidi(dev);
97
memset(_channelsTable, 0, sizeof(_channelsTable));
98
for (int i = 0; i < NUM_CHANNELS; i++) {
99
_channelsVolume[i] = 127;
59
int ret = _driver->open();
61
_driver->sendGMReset();
63
_driver->setTimerCallback(this, &timerCallback);
103
MidiPlayer::~MidiPlayer() {
107
67
void MidiPlayer::play(uint8 *stream, uint16 size) {
68
debugC(3, kDebugMusic, "MidiPlayer::play");
70
Common::StackLock lock(_mutex);
113
76
_midiData = (uint8 *)malloc(size);
115
78
memcpy(_midiData, stream, size);
80
syncVolume(); // FIXME: syncVolume calls setVolume which in turn also locks the mutex! ugh
82
_parser = MidiParser::createParser_SMF();
117
83
_parser->loadMusic(_midiData, size);
118
84
_parser->setTrack(0);
85
_parser->setMidiDriver(this);
86
_parser->setTimerRate(_driver->getBaseTempo());
120
88
_isPlaying = true;
125
void MidiPlayer::stop() {
129
_parser->unloadMusic();
136
92
void MidiPlayer::pause(bool p) {
139
for (int i = 0; i < NUM_CHANNELS; ++i) {
95
for (int i = 0; i < kNumChannels; ++i) {
140
96
if (_channelsTable[i]) {
141
97
_channelsTable[i]->volume(_paused ? 0 : _channelsVolume[i] * _masterVolume / 255);
146
void MidiPlayer::updateTimer() {
102
void MidiPlayer::onTimer() {
103
Common::StackLock lock(_mutex);
105
if (!_paused && _isPlaying && _parser) {
153
106
_parser->onTimer();
158
void MidiPlayer::adjustVolume(int diff) {
159
setVolume(_masterVolume + diff);
162
void MidiPlayer::setVolume(int volume) {
163
_masterVolume = CLIP(volume, 0, 255);
165
for (int i = 0; i < NUM_CHANNELS; ++i) {
166
if (_channelsTable[i]) {
167
_channelsTable[i]->volume(_channelsVolume[i] * _masterVolume / 255);
173
int MidiPlayer::open() {
176
_parser = MidiParser::createParser_SMF();
177
_parser->setMidiDriver(this);
178
_parser->setTimerRate(_driver->getBaseTempo());
179
_driver->setTimerCallback(this, &timerCallback);
184
void MidiPlayer::close() {
187
_driver->setTimerCallback(NULL, NULL);
191
_parser->setMidiDriver(NULL);
196
void MidiPlayer::send(uint32 b) {
197
byte volume, ch = (byte)(b & 0xF);
198
switch (b & 0xFFF0) {
199
case 0x07B0: // volume change
200
volume = (byte)((b >> 16) & 0x7F);
201
_channelsVolume[ch] = volume;
202
volume = volume * _masterVolume / 255;
203
b = (b & 0xFF00FFFF) | (volume << 16);
205
case 0x7BB0: // all notes off
206
if (!_channelsTable[ch]) {
207
// channel not yet allocated, no need to send the event
213
if (!_channelsTable[ch]) {
214
_channelsTable[ch] = (ch == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel();
216
if (_channelsTable[ch]) {
217
_channelsTable[ch]->send(b);
221
void MidiPlayer::metaEvent(byte type, byte *data, uint16 length) {
223
case 0x2F: // end of Track
225
_parser->jumpToTick(0);
231
// warning("Unhandled meta event: %02x", type);
236
void MidiPlayer::timerCallback(void *p) {
237
MidiPlayer *player = (MidiPlayer *)p;
239
player->updateTimer();
242
SoundHandler::SoundHandler(HugoEngine &vm) : _vm(vm) {
243
MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
244
MidiDriver *driver = MidiDriver::createMidi(dev);
246
_midiPlayer = new MidiPlayer(driver);
110
void MidiPlayer::sendToChannel(byte channel, uint32 b) {
111
if (!_channelsTable[channel]) {
112
_channelsTable[channel] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel();
113
// If a new channel is allocated during the playback, make sure
114
// its volume is correctly initialized.
115
if (_channelsTable[channel])
116
_channelsTable[channel]->volume(_channelsVolume[channel] * _masterVolume / 255);
119
if (_channelsTable[channel])
120
_channelsTable[channel]->send(b);
124
SoundHandler::SoundHandler(HugoEngine *vm) : _vm(vm) {
125
_midiPlayer = new MidiPlayer();
126
_speakerStream = new Audio::PCSpeaker(_vm->_mixer->getOutputRate());
127
_vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_speakerHandle,
128
_speakerStream, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
133
pcspkrNoteDuration = 2;
136
SoundHandler::~SoundHandler() {
137
_vm->getTimerManager()->removeTimerProc(&loopPlayer);
138
_vm->_mixer->stopHandle(_speakerHandle);
139
delete _speakerStream;
144
* Set the FM music volume from config.mvolume (0..100%)
249
146
void SoundHandler::setMusicVolume() {
250
/* Set the FM music volume from config.mvolume (0..100%) */
252
_midiPlayer->setVolume(_config.musicVolume * 255 / 100);
147
_midiPlayer->syncVolume();
151
* Stop any sound that might be playing
255
153
void SoundHandler::stopSound() {
256
/* Stop any sound that might be playing */
257
_vm._mixer->stopAll();
154
_vm->_mixer->stopAll();
158
* Stop any tune that might be playing
260
160
void SoundHandler::stopMusic() {
261
/* Stop any tune that might be playing */
262
161
_midiPlayer->stop();
165
* Turn music on and off
265
167
void SoundHandler::toggleMusic() {
266
// Turn music on and off
267
_config.musicFl = !_config.musicFl;
269
_midiPlayer->pause(_config.musicFl);
168
_vm->_config.musicFl = !_vm->_config.musicFl;
170
_midiPlayer->pause(!_vm->_config.musicFl);
174
* Turn digitized sound on and off
272
176
void SoundHandler::toggleSound() {
273
// Turn digitized sound on and off
274
_config.soundFl = !_config.soundFl;
177
_vm->_config.soundFl = !_vm->_config.soundFl;
277
180
void SoundHandler::playMIDI(sound_pt seq_p, uint16 size) {
278
181
_midiPlayer->play(seq_p, size);
185
* Read a tune sequence from the sound database and start playing it
282
187
void SoundHandler::playMusic(int16 tune) {
283
/* Read a tune sequence from the sound database and start playing it */
284
188
sound_pt seqPtr; // Sequence data from file
285
189
uint16 size; // Size of sequence data
287
if (_config.musicFl) {
288
_vm.getGameStatus().song = tune;
289
seqPtr = _vm.file().getSound(tune, &size);
191
if (_vm->_config.musicFl) {
192
_vm->getGameStatus().song = tune;
193
seqPtr = _vm->_file->getSound(tune, &size);
290
194
playMIDI(seqPtr, size);
295
void SoundHandler::playSound(int16 sound, stereo_t channel, byte priority) {
296
/* Produce various sound effects on supplied stereo channel(s) */
297
/* Override currently playing sound only if lower or same priority */
200
* Produce various sound effects on supplied stereo channel(s)
201
* Override currently playing sound only if lower or same priority
203
void SoundHandler::playSound(int16 sound, const byte priority) {
299
204
// uint32 dwVolume; // Left, right volume of sound
300
205
sound_pt sound_p; // Sound data
301
206
uint16 size; // Size of data
302
static byte curPriority = 0; // Priority of currently playing sound
305
if (!_config.soundFl || !_vm._mixer->isReady())
209
if (!_vm->_config.soundFl || !_vm->_mixer->isReady())
308
// // See if last wave still playing - if so, check priority
309
// if (waveOutUnprepareHeader(hwav, lphdr, sizeof(WAVEHDR)) == WAVERR_STILLPLAYING)
310
// if (priority < curPriority) // Don't override unless priority >= current
314
213
curPriority = priority;
317
if ((sound_p = _vm.file().getSound(sound, &size)) == NULL)
216
if ((sound_p = _vm->_file->getSound(sound, &size)) == 0)
320
219
Audio::AudioStream *stream = Audio::makeRawStream(sound_p, size, 11025, Audio::FLAG_UNSIGNED);
321
_vm._mixer->playStream(Audio::Mixer::kSpeechSoundType, &_soundHandle, stream);
220
_vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle, stream);
224
* Initialize for MCI sound and midi
325
226
void SoundHandler::initSound() {
326
/* Initialize for MCI sound and midi */
227
//_midiPlayer->open();
230
void SoundHandler::syncVolume() {
233
if (ConfMan.getBool("sfx_mute") || ConfMan.getBool("mute"))
236
soundVolume = MIN(255, ConfMan.getInt("sfx_volume"));
238
_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, soundVolume);
239
_midiPlayer->syncVolume();
243
* Check if music is still playing.
244
* If not, select the next track in the playlist and play it
246
void SoundHandler::checkMusic() {
247
if (_midiPlayer->isPlaying())
250
for (int i = 0; _vm->_defltTunes[i] != -1; i++) {
251
if (_vm->_defltTunes[i] == _vm->getGameStatus().song) {
252
if (_vm->_defltTunes[i + 1] != -1)
253
playMusic(_vm->_defltTunes[i + 1]);
255
playMusic(_vm->_defltTunes[0]);
261
void SoundHandler::loopPlayer(void *refCon) {
262
((SoundHandler*)refCon)->pcspkr_player();
266
* Decrement last note's timer and see if time to play next note yet.
267
* If so, interpret next note in string and play it. Update ptr to string
268
* Timer: >0 - song still going, 0 - Stop note, -1 - Set next note
270
void SoundHandler::pcspkr_player() {
271
static const uint16 pcspkrNotes[8] = {1352, 1205, 2274, 2026, 1805, 1704, 1518}; // The 3rd octave note counts A..G
272
static const uint16 pcspkrSharps[8] = {1279, 1171, 2150, 1916, 1755, 1611, 1435}; // The sharps, A# to B#
273
static const uint16 pcspkrFlats[8] = {1435, 1279, 2342, 2150, 1916, 1755, 1611}; // The flats, Ab to Bb
275
_vm->getTimerManager()->removeTimerProc(&loopPlayer);
276
_vm->getTimerManager()->installTimerProc(&loopPlayer, 1000000 / 9, this);
278
uint16 count; // Value to set timer chip to for note
281
if (!_vm->_config.soundFl || !_vm->_mixer->isReady())
282
return; // Poo! User doesn't want sound!
287
if (!*DOSSongPtr) // Song has finished
290
if (!--pcspkrTimer) { // timer zero, stop note
291
_speakerStream->stop();
293
} else if (pcspkrTimer >= 0) { // Note still going
297
// Time to play next note
300
switch (*DOSSongPtr) {
301
case 'O': // Switch to new octave 1..7
303
pcspkrOctave = *DOSSongPtr - '0';
304
if ((pcspkrOctave < 0) || (pcspkrOctave > 7))
305
error("pcspkr_player() - Bad octave");
308
case 'L': // Switch to new duration (in ticks)
310
pcspkrNoteDuration = *DOSSongPtr - '0';
311
if (pcspkrNoteDuration < 0)
312
error("pcspkr_player() - Bad duration");
313
pcspkrNoteDuration--;
317
case '^': // Move up an octave
322
case 'v': // Move down an octave
332
switch (*DOSSongPtr) {
333
case 'A': // The notes.
340
count = pcspkrNotes[*DOSSongPtr - 'A'];
341
switch (DOSSongPtr[1]) { // Check for sharp or flat (#, -)
343
count = pcspkrSharps[*DOSSongPtr++ - 'A'];
346
count = pcspkrFlats[*DOSSongPtr++ - 'A'];
351
if (pcspkrOctave > 3) // Adjust for octave
352
count /= (1 << (pcspkrOctave - 3));
353
else if (pcspkrOctave < 3)
354
count *= (1 << (3 - pcspkrOctave));
355
_speakerStream->play(Audio::PCSpeaker::kWaveFormSaw, kHugoCNT / count, (int32) ((1 + pcspkrNoteDuration) * _vm->_normalTPS) * 8);
356
pcspkrTimer = pcspkrNoteDuration;
359
case '.': // A rest note
360
_speakerStream->stop();
361
pcspkrTimer = pcspkrNoteDuration;
365
warning("pcspkr_player() - Unhandled note");
369
void SoundHandler::loadIntroSong(Common::ReadStream &in) {
370
for (int varnt = 0; varnt < _vm->_numVariant; varnt++) {
371
uint16 numBuf = in.readUint16BE();
372
if (varnt == _vm->_gameVariant)
373
DOSIntroSong = _vm->_text->getTextData(numBuf);
377
void SoundHandler::initPcspkrPlayer() {
378
_vm->getTimerManager()->installTimerProc(&loopPlayer, 1000000 / 9, this);
331
381
} // End of namespace Hugo