aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
authorOystein Eftevaag2010-08-26 23:13:17 +0000
committerOystein Eftevaag2010-08-26 23:13:17 +0000
commit98400327c3d2e497bba04f22dd096fcca4eeb255 (patch)
treed6eee60ec7ffa00b03322b1ff3b134fed59feeb0 /engines
parent09f42e24eb6b4d31f4f0e89dca815b19bafbbcb9 (diff)
downloadscummvm-rg350-98400327c3d2e497bba04f22dd096fcca4eeb255.tar.gz
scummvm-rg350-98400327c3d2e497bba04f22dd096fcca4eeb255.tar.bz2
scummvm-rg350-98400327c3d2e497bba04f22dd096fcca4eeb255.zip
HUGO: Implemented basic MIDI support.
svn-id: r52403
Diffstat (limited to 'engines')
-rw-r--r--engines/hugo/engine.cpp4
-rw-r--r--engines/hugo/sound.cpp244
-rw-r--r--engines/hugo/sound.h5
-rw-r--r--engines/hugo/util.cpp9
4 files changed, 212 insertions, 50 deletions
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, ...) {