Files
scummvm-cursorfix/engines/kyra/sound/sound_intern.h
2026-02-02 04:50:13 +01:00

632 lines
16 KiB
C++

/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef KYRA_SOUND_INTERN_H
#define KYRA_SOUND_INTERN_H
#include "kyra/sound/sound.h"
#include "kyra/sound/sound_pc_v1.h"
#include "audio/midiparser.h"
#include "audio/miles.h"
#include "audio/softsynth/emumidi.h"
#include "audio/softsynth/fmtowns_pc98/towns_audio.h"
#include "common/mutex.h"
class EuphonyPlayer;
class TownsPC98_AudioDriver;
namespace Audio {
class PCSpeakerStream;
class MaxTrax;
} // End of namespace Audio
namespace Common {
class MacResManager;
} // End of namespace Common
namespace Kyra {
class MidiOutput;
/**
* MIDI output device.
*
* This device supports both MT-32 MIDI, as used in
* Kyrandia 1 and 2, and GM MIDI, as used in Kyrandia 2.
*/
class SoundMidiPC : public Sound {
public:
SoundMidiPC(KyraEngine_v1 *vm, Audio::Mixer *mixer, MidiDriver *driver, kType type);
~SoundMidiPC() override;
kType getMusicType() const override { return _type; }
bool init() override;
void updateVolumeSettings() override;
void initAudioResourceInfo(int set, void *info) override;
void selectAudioResourceSet(int set) override;
bool hasSoundFile(uint file) const override;
void loadSoundFile(uint file) override;
void loadSoundFile(const Common::Path &file) override;
void loadSfxFile(const Common::Path &file) override;
void playTrack(uint8 track) override;
void haltTrack() override;
bool isPlaying() const override;
void playSoundEffect(uint16 track, uint8 volume = 0xFF) override;
void stopAllSoundEffects() override;
void beginFadeOut() override;
void pause(bool paused) override;
private:
static void onTimer(void *data);
// Our channel handling
int _musicVolume, _sfxVolume;
uint32 _fadeStartTime;
bool _fadeMusicOut;
// Midi file related
Common::Path _mFileName, _sFileName;
byte *_musicFile, *_sfxFile;
MidiParser *_music;
MidiParser *_sfx[3];
const SoundResourceInfo_PC *res() const {return _resInfo[_currentResourceSet]; }
SoundResourceInfo_PC *_resInfo[3];
int _currentResourceSet;
// misc
kType _type;
Common::Path getFileName(const Common::Path &str);
bool _nativeMT32;
MidiDriver *_driver;
Audio::MidiDriver_Miles_Midi *_output;
Common::Mutex _mutex;
};
class SoundTowns_LoK : public Sound {
public:
SoundTowns_LoK(KyraEngine_v1 *vm, Audio::Mixer *mixer);
~SoundTowns_LoK() override;
kType getMusicType() const override { return kTowns; }
bool init() override;
void process() override;
void initAudioResourceInfo(int set, void *info) override;
void selectAudioResourceSet(int set) override;
bool hasSoundFile(uint file) const override;
void loadSoundFile(uint file) override;
void loadSoundFile(const Common::Path &) override {}
void playTrack(uint8 track) override;
void haltTrack() override;
void playSoundEffect(uint16 track, uint8 volume = 0xFF) override;
void stopAllSoundEffects() override;
void beginFadeOut() override;
void updateVolumeSettings() override;
void enableMusic(int enable) override;
private:
bool loadInstruments();
void playEuphonyTrack(uint32 offset, int loop);
void fadeOutSoundEffects();
int _lastTrack;
Audio::SoundHandle _sfxHandle;
uint8 *_musicTrackData;
uint _sfxFileIndex;
uint8 *_sfxFileData;
uint8 _sfxChannel;
EuphonyPlayer *_player;
bool _cdaPlaying;
const SoundResourceInfo_Towns *res() const {return _resInfo[_currentResourceSet]; }
SoundResourceInfo_Towns *_resInfo[3];
int _currentResourceSet;
const uint8 *_musicFadeTable;
const uint8 *_sfxBTTable;
const uint8 *_sfxWDTable;
};
class SoundPC98_LoK : public Sound {
public:
SoundPC98_LoK(KyraEngine_v1 *vm, Audio::Mixer *mixer);
~SoundPC98_LoK() override;
kType getMusicType() const override { return kPC98; }
bool init() override;
void initAudioResourceInfo(int set, void *info) override;
void selectAudioResourceSet(int set) override;
bool hasSoundFile(uint file) const override;
void loadSoundFile(uint file) override;
void loadSoundFile(const Common::Path &file) override;
void playTrack(uint8 track) override;
void haltTrack() override;
void beginFadeOut() override;
int32 voicePlay(const char *file, Audio::SoundHandle *handle, uint8 volume, uint8 priority, bool isSfx) override { return -1; }
void playSoundEffect(uint16 track, uint8 volume = 0xFF) override;
void updateVolumeSettings() override;
private:
int _lastTrack;
uint8 *_musicTrackData;
uint8 *_sfxTrackData;
TownsPC98_AudioDriver *_driver;
const char *resPattern() {return _resInfo[_currentResourceSet]->c_str(); }
Common::String *_resInfo[3];
int _currentResourceSet;
};
class SoundTownsPC98_v2 : public Sound {
public:
SoundTownsPC98_v2(KyraEngine_v1 *vm, Audio::Mixer *mixer);
~SoundTownsPC98_v2() override;
kType getMusicType() const override { return _vm->gameFlags().platform == Common::kPlatformFMTowns ? kTowns : kPC98; }
bool init() override;
void process() override;
void initAudioResourceInfo(int set, void *info) override;
void selectAudioResourceSet(int set) override;
bool hasSoundFile(uint file) const override;
void loadSoundFile(uint file) override {}
void loadSoundFile(const Common::Path &file) override;
void playTrack(uint8 track) override;
void haltTrack() override;
void beginFadeOut() override;
int32 voicePlay(const char *file, Audio::SoundHandle *handle, uint8 volume = 255, uint8 priority = 255, bool isSfx = true) override;
void playSoundEffect(uint16 track, uint8 volume = 0xFF) override;
void updateVolumeSettings() override;
private:
Audio::AudioStream *_currentSFX;
int _lastTrack;
bool _useFmSfx;
uint8 *_musicTrackData;
uint8 *_sfxTrackData;
TownsPC98_AudioDriver *_driver;
const SoundResourceInfo_TownsPC98V2 *res() const { return _resInfo[_currentResourceSet]; }
SoundResourceInfo_TownsPC98V2 *_resInfo[3];
int _currentResourceSet;
};
// PC Speaker MIDI driver
class MidiDriver_PCSpeaker : public MidiDriver_Emulated {
public:
MidiDriver_PCSpeaker(Audio::Mixer *mixer);
~MidiDriver_PCSpeaker() override;
// MidiDriver interface
void close() override {}
void send(uint32 data) override;
MidiChannel *allocateChannel() override { return 0; }
MidiChannel *getPercussionChannel() override { return 0; }
// MidiDriver_Emulated interface
void generateSamples(int16 *buffer, int numSamples) override;
// AudioStream interface
bool isStereo() const override { return false; }
int getRate() const override { return _rate; }
private:
Common::Mutex _mutex;
Audio::PCSpeakerStream *_speaker;
int _rate;
struct Channel {
uint8 pitchBendLow, pitchBendHigh;
uint8 hold;
uint8 modulation;
uint8 voiceProtect;
uint8 noteCount;
} _channel[2];
void resetController(int channel);
struct Note {
bool enabled;
uint8 hardwareChannel;
uint8 midiChannel;
uint8 note;
bool processHold;
uint8 flags;
uint8 hardwareFlags;
uint16 priority;
int16 modulation;
uint16 precedence;
} _note[2];
void noteOn(int channel, int note);
void noteOff(int channel, int note);
void turnNoteOn(int note);
void overwriteNote(int note);
void turnNoteOff(int note);
void setupTone(int note);
uint16 _countdown;
uint8 _hardwareChannel[1];
bool _modulationFlag;
uint8 _timerValue;
void onTimer() override;
static const uint8 _noteTable1[];
static const uint8 _noteTable2[];
};
// for StaticResource (maybe we can find a nicer way to handle it)
struct AmigaSfxTable {
uint8 note;
uint8 patch;
uint16 duration;
uint8 volume;
uint8 pan;
};
class SoundAmiga_LoK : public Sound {
public:
SoundAmiga_LoK(KyraEngine_v1 *vm, Audio::Mixer *mixer);
~SoundAmiga_LoK() override;
kType getMusicType() const override { return kAmiga; } //FIXME
bool init() override;
void initAudioResourceInfo(int set, void *info) override;
void selectAudioResourceSet(int set) override;
bool hasSoundFile(uint file) const override;
void loadSoundFile(uint file) override;
void loadSoundFile(const Common::Path &) override {}
void playTrack(uint8 track) override;
void haltTrack() override;
void beginFadeOut() override;
int32 voicePlay(const char *file, Audio::SoundHandle *handle, uint8 volume, uint8 priority, bool isSfx) override { return -1; }
void playSoundEffect(uint16 track, uint8 volume = 0xFF) override;
protected:
Audio::MaxTrax *_driver;
Audio::SoundHandle _musicHandle;
enum FileType { kFileNone = -1, kFileIntro = 0, kFileGame = 1, kFileFinal = 2 } _fileLoaded;
const AmigaSfxTable *_tableSfxIntro;
int _tableSfxIntro_Size;
const AmigaSfxTable *_tableSfxGame;
int _tableSfxGame_Size;
};
class SoundMacRes;
class HalestormDriver;
class SoundMac : public Sound {
public:
SoundMac(KyraEngine_v1 *vm, Audio::Mixer *mixer);
~SoundMac() override;
kType getMusicType() const override;
bool init() override { return init(musicEnabled() == 1); }
bool init(bool hiQuality);
void initAudioResourceInfo(int, void*) override {}
void selectAudioResourceSet(int set) override;
bool hasSoundFile(uint) const override { return true; }
void loadSoundFile(uint) override {}
void loadSoundFile(const Common::Path &) override {}
void playTrack(uint8 track) override;
void haltTrack() override;
void playSoundEffect(uint16 track, uint8) override;
bool isPlaying() const override;
void beginFadeOut() override;
void updateVolumeSettings() override;
void enableMusic(int enable) override;
private:
void setQuality(bool hi);
SoundMacRes *_res;
HalestormDriver *_driver;
const int _talkieFlag;
bool _ready;
const uint16 *_resIDMusic;
int _currentResourceSet;
static const uint16 _resIDMusicIntro[4];
static const uint16 _resIDMusicIngame[35];
static const uint8 _musicLoopTable[35];
static const uint16 _resIDSfxIntro[2][39];
static const uint16 _resIDSfxIngame[2][39];
struct SoundEffectDef {
uint8 note;
uint8 number;
uint16 rate;
uint8 unk;
};
static const SoundEffectDef _soundEffectDefsIntro[16];
static const SoundEffectDef _soundEffectDefsIngame[120];
};
#ifdef ENABLE_EOB
class SoundTowns_Darkmoon : public Sound, public TownsAudioInterfacePluginDriver {
public:
SoundTowns_Darkmoon(KyraEngine_v1 *vm, Audio::Mixer *mixer);
~SoundTowns_Darkmoon() override;
kType getMusicType() const override { return kTowns; }
bool init() override;
void timerCallback(int timerId) override;
void initAudioResourceInfo(int set, void *info) override;
void selectAudioResourceSet(int set) override;
bool hasSoundFile(uint file) const override;
void loadSoundFile(uint file) override;
void loadSoundFile(const Common::Path &name) override;
void playTrack(uint8 track) override;
void haltTrack() override;
bool isPlaying() const override;
void playSoundEffect(uint16 track, uint8 volume = 0xFF) override;
void stopAllSoundEffects() override;
void beginFadeOut() override;
void updateVolumeSettings() override;
int checkTrigger() override;
void resetTrigger() override;
private:
struct SoundTableEntry {
int8 type;
int32 para1;
int16 para2;
} _soundTable[120];
const char *const *_fileList;
uint _fileListLen;
uint8 _lastSfxChan;
uint8 _lastEnvChan;
uint8 *_pcmData;
uint32 _pcmDataSize;
uint8 _pcmVol;
int _timer;
int _timerSwitch;
SoundResourceInfo_TownsEoB *_resource[3];
TownsAudioInterface *_intf;
};
class AudioMaster2;
class SoundAmiga_EoB: public Sound {
public:
SoundAmiga_EoB(KyraEngine_v1 *vm, Audio::Mixer *mixer);
~SoundAmiga_EoB() override;
kType getMusicType() const override;
bool init() override;
void initAudioResourceInfo(int set, void *info) override;
void selectAudioResourceSet(int set) override;
bool hasSoundFile(uint file) const override { return false; }
void loadSoundFile(uint) override {}
void loadSoundFile(const Common::Path &file) override;
void unloadSoundFile(const Common::String &file) override;
void playTrack(uint8 track) override;
void haltTrack() override;
void playSoundEffect(uint16 track, uint8 volume = 0xFF) override;
void beginFadeOut() override { beginFadeOut(160); }
void beginFadeOut(int delay) override;
void updateVolumeSettings() override;
int checkTrigger() override;
private:
uint8 *_fileBuffer;
KyraEngine_v1 *_vm;
AudioMaster2 *_driver;
SoundResourceInfo_AmigaEoB *_resInfo[3];
Common::String _lastSound;
int _currentResourceSet;
bool _ready;
};
class MLALF98;
class SoundPC98_EoB : public Sound {
public:
SoundPC98_EoB(KyraEngine_v1 *vm, Audio::Mixer *mixer);
~SoundPC98_EoB() override;
kType getMusicType() const override;
bool init() override;
void initAudioResourceInfo(int set, void *info) override;
void selectAudioResourceSet(int set) override;
bool hasSoundFile(uint file) const override { return false; }
void loadSoundFile(uint file) override;
void loadSoundFile(const Common::Path &file) override {}
void loadSfxFile(const Common::Path &file) override;
void playTrack(uint8 track) override;
void haltTrack() override;
void playSoundEffect(uint16 track, uint8) override;
void beginFadeOut() override {}
void updateVolumeSettings() override;
private:
KyraEngine_v1 *_vm;
MLALF98 *_driver;
SoundResourceInfo_PC *_resInfo[3];
int _currentResourceSet;
uint32 _sfxDelay;
bool _ready;
};
class CapcomPC98AudioDriver;
class SoundPC98_Darkmoon : public Sound {
public:
SoundPC98_Darkmoon(KyraEngine_v1 *vm, MidiDriver::DeviceHandle dev, Audio::Mixer *mixer);
~SoundPC98_Darkmoon() override;
kType getMusicType() const override;
kType getSfxType() const override;
bool init() override;
void initAudioResourceInfo(int set, void *info) override;
void selectAudioResourceSet(int set) override;
bool hasSoundFile(uint file) const override { return true; }
void loadSoundFile(uint file) override;
void loadSoundFile(const Common::Path &name) override;
void playTrack(uint8 track) override;
void haltTrack() override;
bool isPlaying() const override;
void playSoundEffect(uint16 track, uint8 volume = 0xFF) override;
void stopAllSoundEffects() override;
void beginFadeOut() override;
void pause(bool paused) override;
void updateVolumeSettings() override;
int checkTrigger() override;
void resetTrigger() override {} // This sound class is for EOB II only, this method is not needed there.
private:
void restartBackgroundMusic();
const uint8 *getData(uint16 track) const;
KyraEngine_v1 *_vm;
CapcomPC98AudioDriver *_driver;
uint8 *_soundData, *_fileBuffer;
int _lastTrack;
const SoundResourceInfo_PC *res() const {return _resInfo[_currentResourceSet]; }
SoundResourceInfo_PC *_resInfo[3];
int _currentResourceSet;
Common::Path _soundFileLoaded;
MidiDriver::DeviceHandle _dev;
kType _drvType;
bool _ready;
};
class SegaAudioDriver;
class SoundSegaCD_EoB : public Sound {
public:
SoundSegaCD_EoB(KyraEngine_v1 *vm, Audio::Mixer *mixer);
~SoundSegaCD_EoB() override;
kType getMusicType() const override;
bool init() override;
void initAudioResourceInfo(int, void*) override {}
void selectAudioResourceSet(int) override {}
bool hasSoundFile(uint file) const override { return false; }
void loadSoundFile(uint file) override {}
void loadSoundFile(const Common::Path &file) override {}
void playTrack(uint8 track) override;
void haltTrack() override;
void playSoundEffect(uint16 track, uint8 volume) override;
bool isPlaying() const override;
void beginFadeOut() override {}
void updateVolumeSettings() override;
private:
void loadPCMData();
void loadFMData();
KyraEngine_v1 *_vm;
SegaAudioDriver *_driver;
uint8 _pcmOffsets[8];
uint16 _fmOffsets[140];
const uint8 *_fmData;
int _lastSoundEffect;
bool _ready;
static const uint8 _fmTrackMap[140];
};
#endif
} // End of namespace Kyra
#endif