diff options
author | uruk | 2014-05-01 22:52:20 +0200 |
---|---|---|
committer | uruk | 2014-05-01 22:52:20 +0200 |
commit | 8e3bbbea7c97ac222a7f061b68fe161b455bf0f8 (patch) | |
tree | 2782f4b3daab8ec0acd851e351fc7228c94faca8 /engines | |
parent | 5b105566a5637edcf73fd6cbc690c1b3b958ff2a (diff) | |
download | scummvm-rg350-8e3bbbea7c97ac222a7f061b68fe161b455bf0f8.tar.gz scummvm-rg350-8e3bbbea7c97ac222a7f061b68fe161b455bf0f8.tar.bz2 scummvm-rg350-8e3bbbea7c97ac222a7f061b68fe161b455bf0f8.zip |
CGE2: Add sound code with working MIDI parts.
Diffstat (limited to 'engines')
-rw-r--r-- | engines/cge2/cge2.cpp | 20 | ||||
-rw-r--r-- | engines/cge2/cge2.h | 7 | ||||
-rw-r--r-- | engines/cge2/module.mk | 3 | ||||
-rw-r--r-- | engines/cge2/sound.cpp | 290 | ||||
-rw-r--r-- | engines/cge2/sound.h | 135 |
5 files changed, 453 insertions, 2 deletions
diff --git a/engines/cge2/cge2.cpp b/engines/cge2/cge2.cpp index 955c5eb0ea..370846faf7 100644 --- a/engines/cge2/cge2.cpp +++ b/engines/cge2/cge2.cpp @@ -30,6 +30,7 @@ #include "cge2/cge2.h" #include "cge2/bitmap.h" #include "cge2/vga13h.h" +#include "cge2/sound.h" namespace CGE2 { @@ -38,21 +39,31 @@ CGE2Engine::CGE2Engine(OSystem *syst, const ADGameDescription *gameDescription) _resman = nullptr; _vga = nullptr; _sprite = nullptr; + _midiPlayer = nullptr; + _fx = nullptr; + _sound = nullptr; _quitFlag = false; _bitmapPalette = nullptr; _mode = 0; + _music = true; } void CGE2Engine::init() { _resman = new ResourceManager(); _vga = new Vga(this); + _fx = new Fx(this, 16); + _sound = new Sound(this); + _midiPlayer = new MusicPlayer(this); } void CGE2Engine::deinit() { delete _resman; delete _vga; delete _sprite; + delete _fx; + delete _sound; + delete _midiPlayer; } bool CGE2Engine::hasFeature(EngineFeature f) const { @@ -77,11 +88,18 @@ Common::Error CGE2Engine::saveGameState(int slot, const Common::String &desc) { } Common::Error CGE2Engine::run() { + warning("STUB: CGE2Engine::run()"); + initGraphics(kScrWidth, kScrHeight, false); init(); - warning("STUB: CGE2Engine::run()"); + showTitle("WELCOME"); + + // Temporary code to test the midi player. + _midiPlayer->loadMidi(1); + g_system->delayMillis(100000); + deinit(); return Common::kNoError; } diff --git a/engines/cge2/cge2.h b/engines/cge2/cge2.h index 814c607a93..9f7fcf6244 100644 --- a/engines/cge2/cge2.h +++ b/engines/cge2/cge2.h @@ -38,6 +38,9 @@ namespace CGE2 { class Vga; class Sprite; +class MusicPlayer; +class Fx; +class Sound; #define kScrWidth 320 #define kScrHeight 240 @@ -61,10 +64,14 @@ public: bool _quitFlag; Dac *_bitmapPalette; int _mode; + bool _music; ResourceManager *_resman; Vga *_vga; Sprite *_sprite; + MusicPlayer *_midiPlayer; + Fx *_fx; + Sound *_sound; private: void init(); void deinit(); diff --git a/engines/cge2/module.mk b/engines/cge2/module.mk index 493488f745..f885933e9d 100644 --- a/engines/cge2/module.mk +++ b/engines/cge2/module.mk @@ -5,7 +5,8 @@ MODULE_OBJS = \ detection.o \ fileio.o \ vga13h.o \ - bitmap.o + bitmap.o \ + sound.o # This module can be built as a plugin ifeq ($(ENABLE_CGE2), DYNAMIC_PLUGIN) diff --git a/engines/cge2/sound.cpp b/engines/cge2/sound.cpp new file mode 100644 index 0000000000..a3e866f05f --- /dev/null +++ b/engines/cge2/sound.cpp @@ -0,0 +1,290 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#include "cge2/sound.h" +//#include "cge/text.h" +//#include "cge/cge_main.h" +#include "common/config-manager.h" +#include "common/memstream.h" +#include "audio/decoders/raw.h" +#include "audio/audiostream.h" +#include "cge2/cge2.h" + +namespace CGE2 { + +DataCk::DataCk(byte *buf, int bufSize) { + _buf = buf; + _ckSize = bufSize; +} + +DataCk::~DataCk() { + free(_buf); +} + +Sound::Sound(CGE2Engine *vm) : _vm(vm) { + _audioStream = NULL; + _soundRepeatCount = 1; + open(); +} + +Sound::~Sound() { + close(); +} + +void Sound::close() { + _vm->_midiPlayer->killMidi(); +} + +void Sound::open() { + setRepeat(1); + play((*_vm->_fx)[30000], 8); +} + +void Sound::setRepeat(int16 count) { + _soundRepeatCount = count; +} + +int16 Sound::getRepeat() { + return _soundRepeatCount; +} + +void Sound::play(DataCk *wav, int pan) { + if (wav) { + stop(); + _smpinf._saddr = &*(wav->addr()); + _smpinf._slen = (uint16)wav->size(); + _smpinf._span = pan; + _smpinf._counter = getRepeat(); + sndDigiStart(&_smpinf); + } +} + +void Sound::sndDigiStart(SmpInfo *PSmpInfo) { + // Create an audio stream wrapper for sound + Common::MemoryReadStream *stream = new Common::MemoryReadStream(PSmpInfo->_saddr, + PSmpInfo->_slen, DisposeAfterUse::NO); + _audioStream = Audio::makeWAVStream(stream, DisposeAfterUse::YES); + + // Start the new sound + _vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle, + Audio::makeLoopingAudioStream(_audioStream, (uint)PSmpInfo->_counter)); + + // CGE pan: + // 8 = Center + // Less = Left + // More = Right + _vm->_mixer->setChannelBalance(_soundHandle, (int8)CLIP(((PSmpInfo->_span - 8) * 16), -127, 127)); +} + +void Sound::stop() { + sndDigiStop(&_smpinf); +} + +void Sound::sndDigiStop(SmpInfo *PSmpInfo) { + if (_vm->_mixer->isSoundHandleActive(_soundHandle)) + _vm->_mixer->stopHandle(_soundHandle); + _audioStream = NULL; +} + +Fx::Fx(CGE2Engine *vm, int size) : _current(NULL), _vm(vm) { + _cache = new Handler[size]; + for (_size = 0; _size < size; _size++) { + _cache[_size]._ref = 0; + _cache[_size]._wav = NULL; + } +} + +Fx::~Fx() { + clear(); + delete[] _cache; +} + +void Fx::clear() { + for (Handler *p = _cache, *q = p + _size; p < q; p++) { + if (p->_ref) { + p->_ref = 0; + delete p->_wav; + p->_wav = NULL; + } + } + _current = NULL; +} + +int Fx::find(int ref) { + int i = 0; + for (Handler *p = _cache, *q = p + _size; p < q; p++) { + if (p->_ref == ref) + break; + else + ++i; + } + return i; +} + +void Fx::name(int ref, int sub) { + warning("STUB: Fx::name()"); +} + +DataCk *Fx::load(int idx, int ref) { + char filename[12]; + warning("TODO: Implement Fx::load() with the use of Fx::name() properly in paralell with Snail! It just won't work this way."); + sprintf(filename, "FX%05d.WAV", ref); + + EncryptedStream file(_vm, filename); + DataCk *wav = loadWave(&file); + if (wav) { + Handler *p = &_cache[idx]; + delete p->_wav; + p->_wav = wav; + p->_ref = ref; + } else { + warning("Unable to load %s", filename); + } + return wav; +} + +DataCk *Fx::loadWave(EncryptedStream *file) { + byte *data = (byte *)malloc(file->size()); + + if (!data) + return 0; + + file->read(data, file->size()); + + return new DataCk(data, file->size()); +} + +DataCk *Fx::operator[](int ref) { + int i; + if ((i = find(ref)) < _size) + _current = _cache[i]._wav; + else { + if ((i = find(0)) >= _size) { + clear(); + i = 0; + } + _current = load(i, ref); + } + return _current; +} + +MusicPlayer::MusicPlayer(CGE2Engine *vm) : _vm(vm) { + _data = NULL; + _isGM = false; + + MidiPlayer::createDriver(); + + int ret = _driver->open(); + if (ret == 0) { + if (_nativeMT32) + _driver->sendMT32Reset(); + else + _driver->sendGMReset(); + + // TODO: Load cmf.ins with the instrument table. It seems that an + // interface for such an operation is supported for AdLib. Maybe for + // this card, setting instruments is necessary. + + _driver->setTimerCallback(this, &timerCallback); + } + _dataSize = -1; +} + +MusicPlayer::~MusicPlayer() { + killMidi(); +} + +void MusicPlayer::killMidi() { + Audio::MidiPlayer::stop(); + + free(_data); + _data = NULL; +} + +void MusicPlayer::loadMidi(int ref) { + warning("STUB: MusicPlayer::loadMidi()"); // if (MidiNotify) MidiNotify(); + + // Work out the filename and check the given MIDI file exists + Common::String filename = Common::String::format("%.2dSG%.2d.MID", ref >> 8, ref & 0xFF); + if (!_vm->_resman->exist(filename.c_str())) + return; + + // Stop any currently playing MIDI file + killMidi(); + + // Read in the data for the file + EncryptedStream mid(_vm, filename.c_str()); + _dataSize = mid.size(); + _data = (byte *)malloc(_dataSize); + mid.read(_data, _dataSize); + + // Start playing the music + sndMidiStart(); +} + +void MusicPlayer::sndMidiStart() { + _isGM = true; + + MidiParser *parser = MidiParser::createParser_SMF(); + if (parser->loadMusic(_data, _dataSize)) { + parser->setTrack(0); + parser->setMidiDriver(this); + parser->setTimerRate(_driver->getBaseTempo()); + parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1); + + _parser = parser; + + syncVolume(); + + // Al the tracks are supposed to loop + _isLooping = true; + _isPlaying = true; + } +} + +void MusicPlayer::send(uint32 b) { + if ((b & 0xF0) == 0xC0 && !_isGM && !_nativeMT32) { + b = (b & 0xFFFF00FF) | MidiDriver::_mt32ToGm[(b >> 8) & 0xFF] << 8; + } + + Audio::MidiPlayer::send(b); +} + +void MusicPlayer::sendToChannel(byte channel, uint32 b) { + if (!_channelsTable[channel]) { + _channelsTable[channel] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel(); + // If a new channel is allocated during the playback, make sure + // its volume is correctly initialized. + if (_channelsTable[channel]) + _channelsTable[channel]->volume(_channelsVolume[channel] * _masterVolume / 255); + } + + if (_channelsTable[channel]) + _channelsTable[channel]->send(b); +} + +} // End of namespace CGE2 diff --git a/engines/cge2/sound.h b/engines/cge2/sound.h new file mode 100644 index 0000000000..c488c3fbba --- /dev/null +++ b/engines/cge2/sound.h @@ -0,0 +1,135 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on original Sfinx source code + * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon + */ + +#ifndef CGE2_SOUND_H +#define CGE2_SOUND_H + +#include "cge2/fileio.h" +#include "audio/audiostream.h" +#include "audio/decoders/wave.h" +#include "audio/fmopl.h" +#include "audio/mididrv.h" +#include "audio/midiparser.h" +#include "audio/midiplayer.h" +#include "audio/mixer.h" +#include "common/memstream.h" + +namespace CGE2 { + +class CGE2Engine; + +// sample info +struct SmpInfo { + const uint8 *_saddr; // address + uint16 _slen; // length + uint16 _span; // left/right pan (0-15) + int _counter; // number of time the sample should be played +}; + +class DataCk { + byte *_buf; + int _ckSize; +public: + DataCk(byte *buf, int bufSize); + ~DataCk(); + inline const byte *addr() { + return _buf; + } + inline int size() { + return _ckSize; + } +}; + +class Sound { +public: + SmpInfo _smpinf; + + Sound(CGE2Engine *vm); + ~Sound(); + void open(); + void close(); + void play(DataCk *wav, int pan); + int16 getRepeat(); + void setRepeat(int16 count); + void stop(); +private: + int _soundRepeatCount; + CGE2Engine *_vm; + Audio::SoundHandle _soundHandle; + Audio::RewindableAudioStream *_audioStream; + + void sndDigiStart(SmpInfo *PSmpInfo); + void sndDigiStop(SmpInfo *PSmpInfo); +}; + +class Fx { + CGE2Engine *_vm; + struct Handler { + int _ref; + DataCk *_wav; + } *_cache; + int _size; + + DataCk *load(int idx, int ref); + DataCk *loadWave(EncryptedStream *file); + int find(int ref); +public: + DataCk *_current; + + Fx(CGE2Engine *vm, int size); + ~Fx(); + void clear(); + void name(int ref, int sub); + DataCk *operator[](int ref); +}; + +class MusicPlayer: public Audio::MidiPlayer { +private: + CGE2Engine *_vm; + byte *_data; + int _dataSize; + bool _isGM; + + // Start MIDI File + void sndMidiStart(); + + // Stop MIDI File + void sndMidiStop(); +public: + MusicPlayer(CGE2Engine *vm); + ~MusicPlayer(); + + void loadMidi(int ref); + void killMidi(); + + virtual void send(uint32 b); + virtual void sendToChannel(byte channel, uint32 b); +}; + +} // End of namespace CGE2 + +#endif // CGE2_SOUND_H |