From 98400327c3d2e497bba04f22dd096fcca4eeb255 Mon Sep 17 00:00:00 2001 From: Oystein Eftevaag Date: Thu, 26 Aug 2010 23:13:17 +0000 Subject: HUGO: Implemented basic MIDI support. svn-id: r52403 --- engines/hugo/engine.cpp | 4 +- engines/hugo/sound.cpp | 244 ++++++++++++++++++++++++++++++++++++++++-------- engines/hugo/sound.h | 5 +- engines/hugo/util.cpp | 9 -- 4 files changed, 212 insertions(+), 50 deletions(-) (limited to 'engines') diff --git a/engines/hugo/engine.cpp b/engines/hugo/engine.cpp index 967d75e663..1033acbc19 100644 --- a/engines/hugo/engine.cpp +++ b/engines/hugo/engine.cpp @@ -162,7 +162,7 @@ void HugoEngine::initConfig(inst_t action) { void HugoEngine::initialize() { debugC(1, kDebugEngine, "initialize"); - sound().initSound(INSTALL); + sound().initSound(); HugoEngine::get().scheduler().initEventQueue(); // Init scheduler stuff screen().initDisplay(); // Create Dibs and palette HugoEngine::get().file().openDatabaseFiles(); // Open database files @@ -195,8 +195,6 @@ void HugoEngine::initialize() { void HugoEngine::shutdown() { debugC(1, kDebugEngine, "shutdown"); - sound().initSound(RESTORE); - HugoEngine::get().file().closeDatabaseFiles(); if (_status.recordFl || _status.playbackFl) HugoEngine::get().file().closePlaybackFile(); diff --git a/engines/hugo/sound.cpp b/engines/hugo/sound.cpp index 3673bda7a6..249d9145ce 100644 --- a/engines/hugo/sound.cpp +++ b/engines/hugo/sound.cpp @@ -36,6 +36,8 @@ #include "sound/decoders/raw.h" #include "sound/audiostream.h" +#include "sound/midiparser.h" +#include "sound/mididrv.h" #include "hugo/hugo.h" #include "hugo/game.h" @@ -44,62 +46,236 @@ namespace Hugo { -uint16 SeqID; // Device id of (MIDI) sequencer -uint16 SeqVolID; // Low level id to set midi volume -uint16 WavID = 0; // Device id of waveaudio +class MidiPlayer : public MidiDriver { +public: + + enum { + NUM_CHANNELS = 16 + }; + + MidiPlayer(MidiDriver *driver); + ~MidiPlayer(); + + void play(uint8 *stream, uint16 size); + void stop(); + void pause(bool p); + void updateTimer(); + void adjustVolume(int diff); + void setVolume(int volume); + int getVolume() const { return _masterVolume; } + void setLooping(bool loop) { _isLooping = loop; } + + // MidiDriver interface + int open(); + void close(); + void send(uint32 b); + void metaEvent(byte type, byte *data, uint16 length); + void setTimerCallback(void *timerParam, void (*timerProc)(void *)) { } + uint32 getBaseTempo() { return _driver ? _driver->getBaseTempo() : 0; } + MidiChannel *allocateChannel() { return 0; } + MidiChannel *getPercussionChannel() { return 0; } + +private: + + static void timerCallback(void *p); + + MidiDriver *_driver; + MidiParser *_parser; + uint8 *_midiData; + bool _isLooping; + bool _isPlaying; + bool _paused; + int _masterVolume; + MidiChannel *_channelsTable[NUM_CHANNELS]; + uint8 _channelsVolume[NUM_CHANNELS]; + Common::Mutex _mutex; +}; + +MidiPlayer::MidiPlayer(MidiDriver *driver) + : _driver(driver), _parser(0), _midiData(0), _isLooping(false), _isPlaying(false), _paused(false), _masterVolume(0) { + assert(_driver); + memset(_channelsTable, 0, sizeof(_channelsTable)); + for (int i = 0; i < NUM_CHANNELS; i++) { + _channelsVolume[i] = 127; + } +} + +MidiPlayer::~MidiPlayer() { + close(); +} + +void MidiPlayer::play(uint8 *stream, uint16 size) { + if (!stream) { + stop(); + return; + } + + _midiData = (uint8 *)malloc(size); + if (_midiData) { + memcpy(_midiData, stream, size); + _mutex.lock(); + _parser->loadMusic(_midiData, size); + _parser->setTrack(0); + _isLooping = true; + _isPlaying = true; + _mutex.unlock(); + } +} + +void MidiPlayer::stop() { + _mutex.lock(); + if (_isPlaying) { + _isPlaying = false; + _parser->unloadMusic(); + free(_midiData); + _midiData = 0; + } + _mutex.unlock(); +} + +void MidiPlayer::pause(bool p) { + _paused = p; + + for (int i = 0; i < NUM_CHANNELS; ++i) { + if (_channelsTable[i]) { + _channelsTable[i]->volume(_paused ? 0 : _channelsVolume[i] * _masterVolume / 255); + } + } +} + +void MidiPlayer::updateTimer() { + if (_paused) { + return; + } + + _mutex.lock(); + if (_isPlaying) { + _parser->onTimer(); + } + _mutex.unlock(); +} + +void MidiPlayer::adjustVolume(int diff) { + setVolume(_masterVolume + diff); +} + +void MidiPlayer::setVolume(int volume) { + _masterVolume = CLIP(volume, 0, 255); + _mutex.lock(); + for (int i = 0; i < NUM_CHANNELS; ++i) { + if (_channelsTable[i]) { + _channelsTable[i]->volume(_channelsVolume[i] * _masterVolume / 255); + } + } + _mutex.unlock(); +} + +int MidiPlayer::open() { + _driver->open(); + + _parser = MidiParser::createParser_SMF(); + _parser->setMidiDriver(this); + _parser->setTimerRate(_driver->getBaseTempo()); + _driver->setTimerCallback(this, &timerCallback); + + return 0; +} + +void MidiPlayer::close() { + stop(); + _mutex.lock(); + _driver->setTimerCallback(NULL, NULL); + _driver->close(); + delete _driver; + _driver = 0; + _parser->setMidiDriver(NULL); + delete _parser; + _mutex.unlock(); +} + +void MidiPlayer::send(uint32 b) { + byte volume, ch = (byte)(b & 0xF); + switch (b & 0xFFF0) { + case 0x07B0: // volume change + volume = (byte)((b >> 16) & 0x7F); + _channelsVolume[ch] = volume; + volume = volume * _masterVolume / 255; + b = (b & 0xFF00FFFF) | (volume << 16); + break; + case 0x7BB0: // all notes off + if (!_channelsTable[ch]) { + // channel not yet allocated, no need to send the event + return; + } + break; + } + + if (!_channelsTable[ch]) { + _channelsTable[ch] = (ch == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel(); + } + if (_channelsTable[ch]) { + _channelsTable[ch]->send(b); + } +} -//HWAVEOUT hwav; // Handle of waveaudio -//LPWAVEHDR lphdr; // WaveOut structure ptr +void MidiPlayer::metaEvent(byte type, byte *data, uint16 length) { + switch (type) { + case 0x2F: // end of Track + if (_isLooping) { + _parser->jumpToTick(0); + } else { + stop(); + } + break; + default: +// warning("Unhandled meta event: %02x", type); + break; + } +} + +void MidiPlayer::timerCallback(void *p) { + MidiPlayer *player = (MidiPlayer *)p; + + player->updateTimer(); +} SoundHandler::SoundHandler(HugoEngine &vm) : _vm(vm) { + MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM); + MidiDriver *driver = MidiDriver::createMidi(dev); + + _midiPlayer = new MidiPlayer(driver); } void SoundHandler::setMusicVolume() { /* Set the FM music volume from config.mvolume (0..100%) */ - warning("STUB: setMusicVolume()"); - - // uint32 dwVolume; - // - // if (config.music) { - // dwVolume = config.mvolume * 0xffffL / 100; // Convert % to 0..0xffff - // dwVolume |= dwVolume << 16; // Set volume in both stereo words - // midiOutSetVolume(SeqVolID, dwVolume); - // } + + _midiPlayer->setVolume(_config.musicVolume * 255 / 100); } void SoundHandler::stopSound() { /* Stop any sound that might be playing */ - warning("STUB: stopSound()"); - - // waveOutReset(hwav); - // waveOutUnprepareHeader(hwav, lphdr, sizeof(WAVEHDR)); + _vm._mixer->stopAll(); } void SoundHandler::stopMusic() { /* Stop any tune that might be playing */ - warning("STUB: stopMusic()"); - //mciSendCommand(SeqID, MCI_CLOSE, MCI_WAIT, 0); + _midiPlayer->stop(); } void SoundHandler::toggleMusic() { // Turn music on and off - if (_config.musicFl) - stopMusic(); _config.musicFl = !_config.musicFl; - initSound(RESET); + + _midiPlayer->pause(_config.musicFl); } void SoundHandler::toggleSound() { // Turn digitized sound on and off _config.soundFl = !_config.soundFl; - initSound(RESET); } void SoundHandler::playMIDI(sound_pt seq_p, uint16 size) { -// Write supplied midi data to a temp file for MCI interface -// If seq_p is NULL, delete temp file - - warning("STUB: playMIDI()"); + _midiPlayer->play(seq_p, size); } @@ -146,19 +322,13 @@ void SoundHandler::playSound(int16 sound, stereo_t channel, byte priority) { } -void SoundHandler::initSound(inst_t action) { +void SoundHandler::initSound() { /* Initialize for MCI sound and midi */ - warning("STUB: initSound()"); + _midiPlayer->open(); } void SoundHandler::pauseSound(bool activeFl, int hTask) { -// Pause and restore music, sound on losing activity to hTask -// Don't stop music if we are parent of new task, i.e. WinHelp() -// or config.music_bkg is TRUE. - -//TODO: Is 'hTask' still useful ? - static bool firstFl = true; static bool musicFl, soundFl; @@ -183,7 +353,7 @@ void SoundHandler::pauseSound(bool activeFl, int hTask) { _config.soundFl = false; } } - initSound(RESET); + initSound(); } } // end of namespace Hugo diff --git a/engines/hugo/sound.h b/engines/hugo/sound.h index a9136b99e1..89fcf463da 100644 --- a/engines/hugo/sound.h +++ b/engines/hugo/sound.h @@ -37,6 +37,8 @@ namespace Hugo { +class MidiPlayer; + class SoundHandler { public: SoundHandler(HugoEngine &vm); @@ -46,11 +48,12 @@ public: void setMusicVolume(); void playMusic(short tune); void playSound(short sound, stereo_t channel, byte priority); - void initSound(inst_t action); + void initSound(); private: HugoEngine &_vm; Audio::SoundHandle _soundHandle; + MidiPlayer *_midiPlayer; void stopSound(); void stopMusic(); diff --git a/engines/hugo/util.cpp b/engines/hugo/util.cpp index da917a802e..fb3da9df88 100644 --- a/engines/hugo/util.cpp +++ b/engines/hugo/util.cpp @@ -114,12 +114,8 @@ void Utils::Warn(bool technote, const char *format, ...) { /* Arguments are same as printf */ /* technote TRUE if we are to refer user to technote file */ char buffer[WARNLEN]; - bool soundFl = _config.soundFl; va_list marker; - _config.soundFl = false; // Kill sound to allow beep sound - HugoEngine::get().sound().initSound(RESET); - va_start(marker, format); vsnprintf(buffer, WARNLEN, format, marker); va_end(marker); @@ -128,11 +124,6 @@ void Utils::Warn(bool technote, const char *format, ...) { //MessageBeep(MB_ICONEXCLAMATION); //MessageBox(hwnd, buffer, "HugoWin Warning", MB_OK | MB_ICONEXCLAMATION); warning("Hugo warning: %s", buffer); - - //sndPlaySound(NULL, 0); // Stop beep and restore sound - - _config.soundFl = soundFl; - HugoEngine::get().sound().initSound(RESET); } void Utils::Error(int error_type, const char *format, ...) { -- cgit v1.2.3