1 /* ResidualVM - A 3D game interpreter
2 *
3 * ResidualVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the AUTHORS
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 "engines/stark/resources/sound.h"
24
25 #include "audio/decoders/vorbis.h"
26
27 #include "common/system.h"
28
29 #include "engines/stark/formats/iss.h"
30 #include "engines/stark/formats/xrc.h"
31 #include "engines/stark/services/archiveloader.h"
32 #include "engines/stark/services/global.h"
33 #include "engines/stark/services/services.h"
34 #include "engines/stark/services/stateprovider.h"
35
36 namespace Stark {
37 namespace Resources {
38
~Sound()39 Sound::~Sound() {
40 }
41
Sound(Object * parent,byte subType,uint16 index,const Common::String & name)42 Sound::Sound(Object *parent, byte subType, uint16 index, const Common::String &name) :
43 Object(parent, subType, index, name),
44 _enabled(0),
45 _looping(false),
46 _field_64(0),
47 _loopIndefinitely(false),
48 _loadFromFile(true),
49 _maxDuration(0),
50 _stockSoundType(0),
51 _field_6C(0),
52 _soundType(0),
53 _pan(0),
54 _volume(0),
55 _fadeDurationRemaining(0),
56 _fadeTargetVolume(0.0),
57 _fadeTargetPan(0.0) {
58 _type = TYPE;
59 }
60
makeAudioStream()61 Audio::RewindableAudioStream *Sound::makeAudioStream() {
62 Common::SeekableReadStream *stream = nullptr;
63 Audio::RewindableAudioStream *audioStream = nullptr;
64
65 // First try the .iss / isn files
66 if (_loadFromFile) {
67 stream = StarkArchiveLoader->getExternalFile(_filename, _archiveName);
68 } else {
69 stream = StarkArchiveLoader->getFile(_filename, _archiveName);
70 }
71
72 if (stream) {
73 audioStream = Formats::makeISSStream(stream, DisposeAfterUse::YES);
74 }
75
76 if (!audioStream) {
77 // The 2 CD version uses Ogg Vorbis
78 Common::String filename = _filename;
79 if (_filename.hasSuffix(".iss") || _filename.hasSuffix(".isn")) {
80 filename = Common::String(_filename.c_str(), _filename.size() - 4) + ".ovs";
81 }
82
83 stream = StarkArchiveLoader->getExternalFile(filename, _archiveName);
84 if (stream) {
85 #ifdef USE_VORBIS
86 audioStream = Audio::makeVorbisStream(stream, DisposeAfterUse::YES);
87 #else
88 warning("Cannot decode sound '%s', Vorbis support is not compiled in", filename.c_str());
89 delete stream;
90 #endif
91 }
92 }
93
94 if (!audioStream) {
95 warning("Unable to load sound '%s'", _filename.c_str());
96 }
97
98 return audioStream;
99 }
100
getMixerSoundType()101 Audio::Mixer::SoundType Sound::getMixerSoundType() {
102 switch (_soundType) {
103 case kSoundTypeVoice:
104 return Audio::Mixer::kSpeechSoundType;
105 case kSoundTypeEffect:
106 return Audio::Mixer::kSFXSoundType;
107 case kSoundTypeMusic:
108 return Audio::Mixer::kMusicSoundType;
109 default:
110 error("Unknown sound type '%d'", _soundType);
111 }
112 }
113
play()114 void Sound::play() {
115 Audio::RewindableAudioStream *rewindableStream = makeAudioStream();
116
117 if (!rewindableStream) {
118 return;
119 }
120
121 Audio::AudioStream *playStream;
122 if (_looping) {
123 playStream = Audio::makeLoopingAudioStream(rewindableStream, 0);
124 } else {
125 playStream = rewindableStream;
126 }
127
128 g_system->getMixer()->playStream(getMixerSoundType(), &_handle, playStream, -1,
129 _volume * Audio::Mixer::kMaxChannelVolume, _pan * 127);
130 }
131
isPlaying()132 bool Sound::isPlaying() {
133 return g_system->getMixer()->isSoundHandleActive(_handle);
134 }
135
stop()136 void Sound::stop() {
137 g_system->getMixer()->stopHandle(_handle);
138 _handle = Audio::SoundHandle();
139 }
140
onPreDestroy()141 void Sound::onPreDestroy() {
142 Object::onPreDestroy();
143 stop();
144 }
145
readData(Formats::XRCReadStream * stream)146 void Sound::readData(Formats::XRCReadStream *stream) {
147 _filename = stream->readString();
148 _enabled = stream->readUint32LE();
149 _looping = stream->readBool();
150 _field_64 = stream->readUint32LE();
151 _loopIndefinitely = stream->readBool();
152 _maxDuration = stream->readUint32LE();
153 _loadFromFile = stream->readBool(); // Used only in the 4CD version
154 _stockSoundType = stream->readUint32LE();
155 _soundName = stream->readString();
156 _field_6C = stream->readUint32LE();
157 _soundType = stream->readUint32LE();
158 _pan = stream->readFloat();
159 _volume = stream->readFloat();
160 _archiveName = stream->getArchiveName();
161 }
162
printData()163 void Sound::printData() {
164 debug("filename: %s", _filename.c_str());
165 debug("enabled: %d", _enabled);
166 debug("looping: %d", _looping);
167 debug("field_64: %d", _field_64);
168 debug("loopIndefinitely: %d", _loopIndefinitely);
169 debug("maxDuration: %d", _maxDuration);
170 debug("loadFromFile: %d", _loadFromFile);
171 debug("stockSoundType: %d", _stockSoundType);
172 debug("soundName: %s", _soundName.c_str());
173 debug("field_6C: %d", _field_6C);
174 debug("soundType: %d", _soundType);
175 debug("pan: %f", _pan);
176 debug("volume: %f", _volume);
177 }
178
onGameLoop()179 void Sound::onGameLoop() {
180 Object::onGameLoop();
181
182 if (_looping && !_loopIndefinitely) {
183 // Automatically stop after the maximum run time has been reached
184 uint32 elapsedTime = g_system->getMixer()->getSoundElapsedTime(_handle);
185 if (elapsedTime > _maxDuration) {
186 stop();
187 }
188 }
189
190 if (_fadeDurationRemaining > 0 && isPlaying()) {
191 _volume += (_fadeTargetVolume - _volume) * StarkGlobal->getMillisecondsPerGameloop() / (float) _fadeDurationRemaining;
192 _pan += (_fadeTargetPan - _pan) * StarkGlobal->getMillisecondsPerGameloop() / (float) _fadeDurationRemaining;
193
194 _fadeDurationRemaining -= StarkGlobal->getMillisecondsPerGameloop();
195
196 if (_fadeDurationRemaining <= 0) {
197 _fadeDurationRemaining = 0;
198
199 _volume = _fadeTargetVolume;
200 _pan = _fadeTargetPan;
201 }
202
203 g_system->getMixer()->setChannelVolume(_handle, _volume * Audio::Mixer::kMaxChannelVolume);
204 g_system->getMixer()->setChannelBalance(_handle, _pan * 127);
205 }
206 }
207
getStockSoundType() const208 uint32 Sound::getStockSoundType() const {
209 return _stockSoundType;
210 }
211
changeVolumePan(int32 volume,int32 pan,int32 duration)212 void Sound::changeVolumePan(int32 volume, int32 pan, int32 duration) {
213 if (isPlaying()) {
214 _fadeDurationRemaining = duration;
215
216 if (_fadeDurationRemaining > 0) {
217 _fadeTargetVolume = volume / 100.0f;
218 _fadeTargetPan = pan / 100.0f;
219 } else {
220 _volume = volume / 100.0f;
221 _pan = pan / 100.0f;
222
223 g_system->getMixer()->setChannelVolume(_handle, _volume * Audio::Mixer::kMaxChannelVolume);
224 g_system->getMixer()->setChannelBalance(_handle, _pan * 127);
225 }
226 } else {
227 if (_fadeDurationRemaining == 0) {
228 _volume = volume / 100.0f;
229 _pan = pan / 100.0f;
230 }
231 }
232 }
233
saveLoadCurrent(ResourceSerializer * serializer)234 void Sound::saveLoadCurrent(ResourceSerializer *serializer) {
235 bool playing = isPlaying();
236 serializer->syncAsUint32LE(playing);
237
238 if (_subType != kSoundSub3 && playing) {
239 uint32 elapsed = g_system->getMixer()->getSoundElapsedTime(_handle);
240 serializer->syncAsUint32LE(elapsed);
241 serializer->syncAsFloat(_volume);
242 serializer->syncAsFloat(_pan);
243 serializer->syncAsUint32LE(_fadeDurationRemaining);
244 serializer->syncAsFloat(_fadeTargetVolume);
245 serializer->syncAsFloat(_fadeTargetPan);
246
247 if (serializer->isLoading()) {
248 play();
249 // TODO: Seek to the "elapsed" position
250 }
251 }
252 }
253
254 } // End of namespace Resources
255 } // End of namespace Stark
256