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 "common/config-manager.h"
25 #include "common/debug.h"
26 #include "common/file.h"
27 
28 #include "audio/audiostream.h"
29 
30 #include "dreamweb/dreamweb.h"
31 #include "dreamweb/sound.h"
32 
33 namespace DreamWeb {
34 
DreamWebSound(DreamWebEngine * vm)35 DreamWebSound::DreamWebSound(DreamWebEngine *vm) : _vm(vm) {
36 	_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume"));
37 	_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume"));
38 	_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, ConfMan.getInt("speech_volume"));
39 
40 	_currentSample = 0xff;
41 	_channel0Playing = 255;
42 	_channel0Repeat = 0;
43 	_channel0NewSound = false;
44 	_channel1Playing = 255;
45 	_channel1NewSound = false;
46 
47 	_volume = 0;
48 	_volumeTo = 0;
49 	_volumeDirection = 0;
50 	_volumeCount = 0;
51 }
52 
~DreamWebSound()53 DreamWebSound::~DreamWebSound() {
54 }
55 
loadSpeech(byte type1,int idx1,byte type2,int idx2)56 bool DreamWebSound::loadSpeech(byte type1, int idx1, byte type2, int idx2) {
57 	cancelCh1();
58 
59 	Common::String name = Common::String::format("%c%02d%c%04d.RAW", type1, idx1, type2, idx2);
60 	debug(2, "loadSpeech() name:%s", name.c_str());
61 	return loadSpeech(name);
62 }
63 
volumeChange(uint8 value,int8 direction)64 void DreamWebSound::volumeChange(uint8 value, int8 direction) {
65 	_volumeTo = value;
66 	_volumeDirection = direction;
67 }
68 
volumeAdjust()69 void DreamWebSound::volumeAdjust() {
70 	if (_volumeDirection == 0)
71 		return;
72 	if (_volume != _volumeTo) {
73 		_volumeCount += 64;
74 		// Only modify the volume every 256/64 = 4th time around
75 		if (_volumeCount == 0)
76 			_volume += _volumeDirection;
77 	} else {
78 		_volumeDirection = 0;
79 	}
80 }
81 
playChannel0(uint8 index,uint8 repeat)82 void DreamWebSound::playChannel0(uint8 index, uint8 repeat) {
83 	debug(1, "playChannel0(index:%d, repeat:%d)", index, repeat);
84 
85 	_channel0Playing = index;
86 	_channel0Repeat = repeat;
87 	_channel0NewSound = true;
88 }
89 
playChannel1(uint8 index)90 void DreamWebSound::playChannel1(uint8 index) {
91 	debug(1, "playChannel1(index:%d)", index);
92 	if (_channel1Playing == 7)
93 		return;
94 
95 	_channel1Playing = index;
96 	_channel1NewSound = true;
97 }
98 
cancelCh0()99 void DreamWebSound::cancelCh0() {
100 	debug(1, "cancelCh0()");
101 	_channel0Playing = 255;
102 	_channel0Repeat = 0;
103 	stopSound(0);
104 }
105 
cancelCh1()106 void DreamWebSound::cancelCh1() {
107 	debug(1, "cancelCh1()");
108 	_channel1Playing = 255;
109 	stopSound(1);
110 }
111 
loadRoomsSample(uint8 sample)112 void DreamWebSound::loadRoomsSample(uint8 sample) {
113 	debug(1, "loadRoomsSample(sample:%d)", sample);
114 
115 	if (sample == 255 || _currentSample == sample)
116 		return; // loaded already
117 
118 	assert(sample < 100);
119 	Common::String sampleSuffix = Common::String::format("V%02d", sample);
120 	_currentSample = sample;
121 
122 	uint8 ch0 = _channel0Playing;
123 	if (ch0 >= 12 && ch0 != 255)
124 		cancelCh0();
125 	uint8 ch1 = _channel1Playing;
126 	if (ch1 >= 12)
127 		cancelCh1();
128 	loadSounds(1, sampleSuffix.c_str());
129 }
130 
playSound(uint8 channel,uint8 id,uint8 loops)131 void DreamWebSound::playSound(uint8 channel, uint8 id, uint8 loops) {
132 	debug(1, "playSound(channel:%u, id:%u, loops:%u)", channel, id, loops);
133 
134 	int bank = 0;
135 	bool speech = false;
136 	Audio::Mixer::SoundType type = channel == 0?
137 		Audio::Mixer::kMusicSoundType: Audio::Mixer::kSFXSoundType;
138 
139 	if (id >= 12) {
140 		id -= 12;
141 		bank = 1;
142 		if (id == 50) {
143 			speech = true;
144 			type = Audio::Mixer::kSpeechSoundType;
145 		}
146 	}
147 	const SoundData &data = _soundData[bank];
148 
149 	Audio::SeekableAudioStream *raw;
150 	if (!speech) {
151 		if (id >= data.samples.size() || data.samples[id].size == 0) {
152 			warning("invalid sample #%u played", id);
153 			return;
154 		}
155 
156 		const Sample &sample = data.samples[id];
157 		uint8 *buffer = (uint8 *)malloc(sample.size);
158 		if (!buffer)
159 			error("out of memory: cannot allocate memory for sound(%u bytes)", sample.size);
160 		memcpy(buffer, data.data.begin() + sample.offset, sample.size);
161 
162 		raw = Audio::makeRawStream(buffer, sample.size, 22050, Audio::FLAG_UNSIGNED);
163 	} else {
164 		uint8 *buffer = (uint8 *)malloc(_speechData.size());
165 		if (!buffer)
166 			error("out of memory: cannot allocate memory for sound(%u bytes)", _speechData.size());
167 		memcpy(buffer, _speechData.begin(), _speechData.size());
168 		raw = Audio::makeRawStream(buffer, _speechData.size(), 22050, Audio::FLAG_UNSIGNED);
169 	}
170 
171 	Audio::AudioStream *stream;
172 	if (loops > 1) {
173 		stream = new Audio::LoopingAudioStream(raw, (loops < 255) ? loops : 0);
174 	} else
175 		stream = raw;
176 
177 	if (_vm->_mixer->isSoundHandleActive(_channelHandle[channel]))
178 		_vm->_mixer->stopHandle(_channelHandle[channel]);
179 	_vm->_mixer->playStream(type, &_channelHandle[channel], stream);
180 }
181 
stopSound(uint8 channel)182 void DreamWebSound::stopSound(uint8 channel) {
183 	debug(1, "stopSound(%u)", channel);
184 	assert(channel == 0 || channel == 1);
185 	_vm->_mixer->stopHandle(_channelHandle[channel]);
186 }
187 
loadSpeech(const Common::String & filename)188 bool DreamWebSound::loadSpeech(const Common::String &filename) {
189 	if (!_vm->hasSpeech())
190 		return false;
191 
192 	Common::File file;
193 	if (!file.open(_vm->getSpeechDirName() + "/" + filename))
194 		return false;
195 
196 	debug(1, "loadSpeech(%s)", filename.c_str());
197 
198 	uint size = file.size();
199 	_speechData.resize(size);
200 	file.read(_speechData.begin(), size);
201 	file.close();
202 	return true;
203 }
204 
soundHandler()205 void DreamWebSound::soundHandler() {
206 	_vm->_subtitles = ConfMan.getBool("subtitles");
207 	volumeAdjust();
208 
209 	uint volume = _volume;
210 	//.vol file loaded into soundbuf:0x4000
211 	//volume table at (volume * 0x100 + 0x3f00)
212 	//volume value could be from 1 to 7
213 	//1 - 0x10-0xff
214 	//2 - 0x1f-0xdf
215 	//3 - 0x2f-0xd0
216 	//4 - 0x3e-0xc1
217 	//5 - 0x4d-0xb2
218 	//6 - 0x5d-0xa2
219 	//7 - 0x6f-0x91
220 	if (volume >= 8)
221 		volume = 7;
222 	volume = (8 - volume) * Audio::Mixer::kMaxChannelVolume / 8;
223 	_vm->_mixer->setChannelVolume(_channelHandle[0], volume);
224 
225 	if (_channel0NewSound) {
226 		_channel0NewSound = false;
227 		if (_channel0Playing != 255) {
228 			playSound(0, _channel0Playing, _channel0Repeat);
229 		}
230 	}
231 	if (_channel1NewSound) {
232 		_channel1NewSound = false;
233 		if (_channel1Playing != 255) {
234 			playSound(1, _channel1Playing, 1);
235 		}
236 	}
237 
238 	if (!_vm->_mixer->isSoundHandleActive(_channelHandle[0])) {
239 		_channel0Playing = 255;
240 	}
241 	if (!_vm->_mixer->isSoundHandleActive(_channelHandle[1])) {
242 		_channel1Playing = 255;
243 	}
244 }
245 
loadSounds(uint bank,const Common::String & suffix)246 void DreamWebSound::loadSounds(uint bank, const Common::String &suffix) {
247 	Common::String filename = _vm->getDatafilePrefix() + suffix;
248 	debug(1, "loadSounds(%u, %s)", bank, filename.c_str());
249 	Common::File file;
250 	if (!file.open(filename)) {
251 		warning("cannot open %s", filename.c_str());
252 		return;
253 	}
254 
255 	uint8 header[96];
256 	file.read(header, sizeof(header));
257 	uint tablesize = READ_LE_UINT16(header + 50);
258 	debug(1, "table size = %u", tablesize);
259 
260 	SoundData &soundData = _soundData[bank];
261 	soundData.samples.resize(tablesize / 6);
262 	uint total = 0;
263 	for (uint i = 0; i < tablesize / 6; ++i) {
264 		uint8 entry[6];
265 		Sample &sample = soundData.samples[i];
266 		file.read(entry, sizeof(entry));
267 		sample.offset = entry[0] * 16384 + READ_LE_UINT16(entry + 1);
268 		sample.size = READ_LE_UINT16(entry + 3) * 2048;
269 		total += sample.size;
270 		debug(1, "offset: %08x, size: %u", sample.offset, sample.size);
271 	}
272 	soundData.data.resize(total);
273 	file.read(soundData.data.begin(), total);
274 	file.close();
275 }
276 
277 } // End of namespace DreamWeb
278