1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "audio/decoders/raw.h"
24 #include "audio/decoders/voc.h"
25 #include "backends/audiocd/audiocd.h"
26 #include "common/config-manager.h"
27 #include "xeen/sound.h"
28 #include "xeen/sound_driver_adlib.h"
29 #include "xeen/xeen.h"
30
31 namespace Xeen {
32
Sound(Audio::Mixer * mixer)33 Sound::Sound(Audio::Mixer *mixer) : _mixer(mixer), _fxOn(true), _musicOn(true), _subtitles(false),
34 _songData(nullptr), _effectsData(nullptr), _musicSide(0), _musicPercent(100),
35 _musicVolume(0), _sfxVolume(0) {
36 _SoundDriver = new SoundDriverAdlib();
37 if (g_vm->getIsCD())
38 g_system->getAudioCDManager()->open();
39 }
40
~Sound()41 Sound::~Sound() {
42 stopAllAudio();
43 if (g_vm->getIsCD())
44 g_system->getAudioCDManager()->close();
45
46 delete _SoundDriver;
47 delete[] _effectsData;
48 delete[] _songData;
49 }
50
playSound(Common::SeekableReadStream & s,int unused)51 void Sound::playSound(Common::SeekableReadStream &s, int unused) {
52 stopSound();
53 if (!_fxOn)
54 return;
55
56 s.seek(0);
57 Common::SeekableReadStream *srcStream = s.readStream(s.size());
58 Audio::SeekableAudioStream *stream = Audio::makeVOCStream(srcStream,
59 Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
60 _mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle, stream);
61 }
62
playSound(const Common::String & name,int unused)63 void Sound::playSound(const Common::String &name, int unused) {
64 File f;
65 if (!f.open(name))
66 error("Could not open sound - %s", name.c_str());
67
68 playSound(f);
69 }
70
playSound(const Common::String & name,int ccNum,int unused)71 void Sound::playSound(const Common::String &name, int ccNum, int unused) {
72 File f;
73 if (!f.open(name, ccNum))
74 error("Could not open sound - %s", name.c_str());
75
76 playSound(f);
77 }
78
playVoice(const Common::String & name,int ccMode)79 void Sound::playVoice(const Common::String &name, int ccMode) {
80 File f;
81 bool result = (ccMode == -1) ? f.open(name) : f.open(name, ccMode);
82 if (!result)
83 error("Could not open sound - %s", name.c_str());
84
85 stopSound();
86
87 Common::SeekableReadStream *srcStream = f.readStream(f.size());
88 Audio::SeekableAudioStream *stream = Audio::makeVOCStream(srcStream,
89 Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
90 _mixer->playStream(Audio::Mixer::kSpeechSoundType, &_soundHandle, stream);
91 }
92
stopSound()93 void Sound::stopSound() {
94 _mixer->stopHandle(_soundHandle);
95 }
96
isSoundPlaying() const97 bool Sound::isSoundPlaying() const {
98 return _mixer->isSoundHandleActive(_soundHandle);
99 }
100
stopAllAudio()101 void Sound::stopAllAudio() {
102 stopSong();
103 stopFX();
104 stopSound();
105 setMusicPercent(100);
106 }
107
setFxOn(bool isOn)108 void Sound::setFxOn(bool isOn) {
109 ConfMan.setBool("sfx_mute", !isOn);
110 if (isOn)
111 ConfMan.setBool("mute", false);
112
113 g_vm->syncSoundSettings();
114 }
115
loadEffectsData()116 void Sound::loadEffectsData() {
117 // Stop any prior FX
118 stopFX();
119
120 if (!_effectsData) {
121 // Load in an entire driver so we have quick access to the effects data that's hardcoded within it
122 File file("blastmus");
123 byte *effectsData = new byte[file.size()];
124 file.seek(0);
125 file.read(effectsData, file.size());
126 file.close();
127 _effectsData = effectsData;
128
129 // Locate the playFX routine
130 const byte *fx = effectsData + READ_LE_UINT16(effectsData + 10) + 12;
131 assert(READ_BE_UINT16(fx + 28) == 0x81FB);
132 uint numEffects = READ_LE_UINT16(fx + 30);
133
134 assert(READ_BE_UINT16(fx + 36) == 0x8B87);
135 const byte *table = effectsData + READ_LE_UINT16(fx + 38);
136
137 // Extract the effects offsets
138 _effectsOffsets.resize(numEffects);
139 for (uint idx = 0; idx < numEffects; ++idx)
140 _effectsOffsets[idx] = READ_LE_UINT16(&table[idx * 2]);
141 }
142 }
143
playFX(uint effectId)144 void Sound::playFX(uint effectId) {
145 stopFX();
146 loadEffectsData();
147
148 if (effectId < _effectsOffsets.size()) {
149 const byte *dataP = &_effectsData[_effectsOffsets[effectId]];
150 _SoundDriver->playFX(effectId, dataP);
151 }
152 }
153
stopFX()154 void Sound::stopFX() {
155 _SoundDriver->stopFX();
156 }
157
songCommand(uint commandId,byte musicVolume,byte sfxVolume)158 int Sound::songCommand(uint commandId, byte musicVolume, byte sfxVolume) {
159 int result = _SoundDriver->songCommand(commandId, musicVolume, sfxVolume);
160 if (commandId == STOP_SONG) {
161 delete[] _songData;
162 _songData = nullptr;
163 }
164
165 return result;
166 }
167
playSong(Common::SeekableReadStream & stream)168 void Sound::playSong(Common::SeekableReadStream &stream) {
169 stopSong();
170 if (!_musicOn)
171 return;
172
173 byte *songData = new byte[stream.size()];
174 stream.seek(0);
175 stream.read(songData, stream.size());
176 _songData = songData;
177
178 _SoundDriver->playSong(_songData);
179 }
180
playSong(const Common::String & name,int param)181 void Sound::playSong(const Common::String &name, int param) {
182 _priorMusic = _currentMusic;
183 _currentMusic = name;
184
185 Common::File mf;
186 if (mf.open(name)) {
187 playSong(mf);
188 } else {
189 File f(name, _musicSide);
190 playSong(f);
191 }
192 }
193
setMusicOn(bool isOn)194 void Sound::setMusicOn(bool isOn) {
195 ConfMan.setBool("music_mute", !isOn);
196 if (isOn)
197 ConfMan.setBool("mute", false);
198
199 g_vm->syncSoundSettings();
200 }
201
isMusicPlaying() const202 bool Sound::isMusicPlaying() const {
203 return _SoundDriver->isPlaying();
204 }
205
setMusicPercent(byte percent)206 void Sound::setMusicPercent(byte percent) {
207 assert(percent <= 100);
208 _musicPercent = percent;
209
210 updateVolume();
211 }
212
updateSoundSettings()213 void Sound::updateSoundSettings() {
214 _fxOn = !ConfMan.getBool("sfx_mute");
215 if (!_fxOn)
216 stopFX();
217
218 _musicOn = !ConfMan.getBool("music_mute");
219 if (!_musicOn)
220 stopSong();
221
222 _subtitles = ConfMan.hasKey("subtitles") ? ConfMan.getBool("subtitles") : true;
223 _musicVolume = CLIP(ConfMan.getInt("music_volume"), 0, 255);
224 _sfxVolume = CLIP(ConfMan.getInt("sfx_volume"), 0, 255);
225 updateVolume();
226 }
227
updateVolume()228 void Sound::updateVolume() {
229 songCommand(SET_VOLUME, _musicPercent * _musicVolume / 100, _sfxVolume);
230 }
231
232 } // End of namespace Xeen
233