1 /*
2 SOUNDFILE.CPP
3
4 Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
5 and the "Aleph One" developers.
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (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 This license is contained in the file "COPYING",
18 which is included with this source code; it is available online at
19 http://www.gnu.org/licenses/gpl.html
20
21 */
22
23 #include "SoundFile.h"
24 #include "Logging.h"
25 #include "csmisc.h"
26 #include "Decoder.h"
27
28 #include <assert.h>
29 #include <boost/make_shared.hpp>
30
31 #include "BStream.h"
32 #include <boost/iostreams/stream_buffer.hpp>
33 #include <utility>
34
35 namespace io = boost::iostreams;
36
SoundHeader()37 SoundHeader::SoundHeader() :
38 SoundInfo(),
39 data_offset(0)
40 {
41 }
42
UnpackStandardSystem7Header(BIStreamBE & header)43 bool SoundHeader::UnpackStandardSystem7Header(BIStreamBE &header)
44 {
45 try
46 {
47 bytes_per_frame = 1;
48 signed_8bit = false;
49 sixteen_bit = false;
50 stereo = false;
51 little_endian = false;
52 header.ignore(4); // sample pointer
53 header >> length;
54 header >> rate;
55 header >> loop_start;
56 header >> loop_end;
57
58 return true;
59 } catch (basic_bstream::failure e) {
60 return false;
61 }
62 }
63
UnpackExtendedSystem7Header(BIStreamBE & header)64 bool SoundHeader::UnpackExtendedSystem7Header(BIStreamBE &header)
65 {
66 try
67 {
68 signed_8bit = false;
69 header.ignore(4); // sample pointer
70 int32 num_channels;
71 header >> num_channels;
72 stereo = (num_channels == 2);
73 header >> rate;
74 header >> loop_start;
75 header >> loop_end;
76 uint8 header_type;
77 header >> header_type;
78 header.ignore(1); // baseFrequency
79 int32 num_frames;
80 header >> num_frames;
81
82 if (header_type == 0xfe)
83 {
84 header.ignore(10); // AIFF rate
85 header.ignore(4); // marker chunk
86 uint32 format;
87 header >> format;
88 header.ignore(4 * 3); // future use, ptr, ptr
89 int16 comp_id;
90 header >> comp_id;
91 if (format != FOUR_CHARS_TO_INT('t','w','o','s') || comp_id != -1) {
92 return false;
93 }
94 signed_8bit = true;
95 header.ignore(4);
96 } else {
97 header.ignore(22);
98 }
99
100 int16 sample_size;
101 header >> sample_size;
102
103 sixteen_bit = (sample_size == 16);
104 bytes_per_frame = (sixteen_bit ? 2 : 1) * (stereo ? 2 : 1);
105
106 length = num_frames * bytes_per_frame;
107 little_endian = false;
108
109 if ((loop_end - loop_start >= 4) && ((loop_start % bytes_per_frame) || (loop_end % bytes_per_frame)))
110 {
111 logWarning("loop_start=%i and loop_end=%i but bytes_per_frame=%i; interpreting as frame offsets", loop_start, loop_end, bytes_per_frame);
112 loop_start *= bytes_per_frame;
113 loop_end *= bytes_per_frame;
114 }
115
116 return true;
117 } catch (basic_bstream::failure e) {
118 return false;
119 }
120 }
121
Load(BIStreamBE & s)122 bool SoundHeader::Load(BIStreamBE& s)
123 {
124 Clear();
125
126 uint8 encoding;
127 s.rdbuf()->pubseekoff(20, std::ios_base::cur);
128 encoding = s.rdbuf()->sgetc();
129 s.rdbuf()->pubseekoff(-20, std::ios_base::cur);
130
131 switch (encoding)
132 {
133 case stdSH:
134 if (UnpackStandardSystem7Header(s))
135 {
136 data_offset = 22;
137 return true;
138 }
139 break;
140 case extSH:
141 case cmpSH:
142 if (UnpackExtendedSystem7Header(s))
143 {
144 data_offset = 64;
145 return true;
146 }
147 break;
148 }
149
150 return false;
151 }
152
LoadData(BIStreamBE & s)153 boost::shared_ptr<SoundData> SoundHeader::LoadData(BIStreamBE& s)
154 {
155 if (!data_offset)
156 {
157 return boost::shared_ptr<SoundData>();
158 }
159
160 s.ignore(data_offset);
161 boost::shared_ptr<SoundData> p = boost::make_shared<SoundData>(length);
162 try
163 {
164 s.read(reinterpret_cast<char*>(&(*p)[0]), length);
165 }
166 catch (basic_bstream::failure e)
167 {
168 p.reset();
169 }
170
171 return p;
172 }
173
Load(OpenedFile & SoundFile)174 bool SoundHeader::Load(OpenedFile &SoundFile)
175 {
176 io::stream_buffer<opened_file_device> sb(SoundFile);
177 BIStreamBE s(&sb);
178
179 return Load(s);
180 }
181
LoadData(OpenedFile & SoundFile)182 boost::shared_ptr<SoundData> SoundHeader::LoadData(OpenedFile& SoundFile)
183 {
184 io::stream_buffer<opened_file_device> sb(SoundFile);
185 BIStreamBE s(&sb);
186
187 return LoadData(s);
188 }
189
Load(LoadedResource & rsrc)190 bool SoundHeader::Load(LoadedResource& rsrc)
191 {
192 io::stream_buffer<io::array_source> sb(reinterpret_cast<char*>(rsrc.GetPointer()), rsrc.GetLength());
193 BIStreamBE s(&sb);
194
195 // Get resource format
196 uint16 format;
197 s >> format;
198 if (format != 1 && format != 2)
199 {
200 logWarning("Unknown sound resource format %d", format);
201 return false;
202 }
203
204 // Skip sound data types or reference count
205 if (format == 1)
206 {
207 uint16 num_data_formats;
208 s >> num_data_formats;
209 s.ignore(num_data_formats * 6);
210 }
211 else if (format == 2)
212 {
213 s.ignore(2);
214 }
215
216 // Scan sound commands for bufferCmd
217 uint16 num_cmds;
218 s >> num_cmds;
219 for (int i = 0; i < num_cmds; ++i)
220 {
221 uint16 cmd, param1;
222 uint32 param2;
223
224 s >> cmd
225 >> param1
226 >> param2;
227
228 if (cmd == bufferCmd)
229 {
230 s.rdbuf()->pubseekpos(param2);
231 if (Load(s))
232 {
233 data_offset += param2;
234 return true;
235 }
236 }
237 }
238
239 return false;
240 }
241
LoadData(LoadedResource & rsrc)242 boost::shared_ptr<SoundData> SoundHeader::LoadData(LoadedResource& rsrc)
243 {
244 io::stream_buffer<io::array_source> sb(reinterpret_cast<char*>(rsrc.GetPointer()), rsrc.GetLength());
245 BIStreamBE s(&sb);
246
247 return LoadData(s);
248 }
249
SoundDefinition()250 SoundDefinition::SoundDefinition() :
251 sound_code(0),
252 behavior_index(1),
253 flags(0),
254 chance(0),
255 low_pitch(0),
256 high_pitch(0),
257 permutations(1),
258 permutations_played(0),
259 group_offset(0), single_length(0), total_length(0),
260 last_played(0)
261 {
262 }
263
Unpack(OpenedFile & SoundFile)264 bool SoundDefinition::Unpack(OpenedFile &SoundFile)
265 {
266 if (!SoundFile.IsOpen()) return false;
267
268 vector<uint8> headerBuffer(HeaderSize());
269 if (!SoundFile.Read(headerBuffer.size(), &headerBuffer[0]))
270 return false;
271
272 AIStreamBE header(&headerBuffer[0], headerBuffer.size());
273
274 header >> sound_code;
275
276 header >> behavior_index;
277 header >> flags;
278
279 header >> chance;
280
281 header >> low_pitch;
282 header >> high_pitch;
283
284 header >> permutations;
285 header >> permutations_played;
286 header >> group_offset;
287 header >> single_length;
288 header >> total_length;
289
290 sound_offsets.resize(MAXIMUM_PERMUTATIONS_PER_SOUND);
291 for (int i = 0; i < sound_offsets.size(); i++)
292 {
293 header >> sound_offsets[i];
294 }
295
296 header >> last_played;
297
298 header.ignore(4 * 2);
299
300 return true;
301 }
302
Load(OpenedFile & SoundFile,bool LoadPermutations)303 bool SoundDefinition::Load(OpenedFile &SoundFile, bool LoadPermutations)
304 {
305 if (!SoundFile.IsOpen()) return false;
306
307 if (LoadPermutations)
308 sounds.resize(permutations);
309 else
310 sounds.resize(std::min(permutations, static_cast<int16>(1)));
311
312 for (int i = 0; i < sounds.size(); i++)
313 {
314 if (!SoundFile.SetPosition(group_offset + sound_offsets[i])
315 || !sounds[i].Load(SoundFile))
316 {
317 sounds.clear();
318 return false;
319 }
320 }
321
322 return true;
323 }
324
325
LoadData(OpenedFile & SoundFile,short permutation)326 boost::shared_ptr<SoundData> SoundDefinition::LoadData(OpenedFile& SoundFile, short permutation)
327 {
328 boost::shared_ptr<SoundData> p;
329 if (!SoundFile.IsOpen())
330 {
331 return p;
332 }
333
334 if (!SoundFile.SetPosition(group_offset + sound_offsets[permutation]))
335 {
336 return p;
337 }
338
339 return sounds[permutation].LoadData(SoundFile);
340 }
341
Open(FileSpecifier & SoundFileSpec)342 bool M2SoundFile::Open(FileSpecifier& SoundFileSpec)
343 {
344 Close();
345
346 std::unique_ptr<OpenedFile> sound_file(new OpenedFile);
347
348 if (!SoundFileSpec.Open(*sound_file, false)) return false;
349
350 std::vector<uint8> headerBuffer;
351 headerBuffer.resize(HeaderSize());
352
353 if (!sound_file->Read(headerBuffer.size(), &headerBuffer[0]))
354 return false;
355
356 AIStreamBE header(&headerBuffer[0], headerBuffer.size());
357 header >> version;
358 header >> tag;
359 header >> source_count;
360 header >> sound_count;
361 header.ignore(v1Unused * 2);
362
363 if ((version != 0 && version != 1) ||
364 tag != FOUR_CHARS_TO_INT('s','n','d','2') ||
365 sound_count < 0 ||
366 source_count < 0)
367 {
368 return false;
369 }
370
371 if (sound_count == 0)
372 {
373 sound_count = source_count;
374 source_count = 1;
375 }
376
377 // load the definitions
378 sound_definitions.resize(source_count);
379 for (int source = 0; source < source_count; source++)
380 {
381 sound_definitions[source].resize(sound_count);
382 for (int i = 0; i < sound_count; i++)
383 {
384 if (!sound_definitions[source][i].Unpack(*sound_file))
385 {
386 Close();
387 return false;
388 }
389 }
390 }
391
392 // load all the headers
393 for (int source = 0; source < source_count; ++source)
394 {
395 for (int i = 0; i < sound_count; ++i)
396 {
397 sound_definitions[source][i].Load(*sound_file, true);
398 }
399 }
400
401 // keep the sound file opened
402 opened_sound_file = std::move(sound_file);
403
404 return true;
405 }
406
Close()407 void M2SoundFile::Close()
408 {
409 sound_definitions.clear();
410 }
411
GetSoundDefinition(int source,int sound_index)412 SoundDefinition* M2SoundFile::GetSoundDefinition(int source, int sound_index)
413 {
414 if (source < sound_definitions.size() && sound_index < sound_definitions[source].size())
415 return &sound_definitions[source][sound_index];
416 else
417 return 0;
418 }
419
GetSoundData(SoundDefinition * definition,int permutation)420 boost::shared_ptr<SoundData> M2SoundFile::GetSoundData(SoundDefinition* definition, int permutation)
421 {
422 return definition->LoadData(*opened_sound_file, permutation);
423 }
424
Open(FileSpecifier & SoundFile)425 bool M1SoundFile::Open(FileSpecifier& SoundFile)
426 {
427 Close();
428 return SoundFile.Open(resource_file);
429 }
430
Close()431 void M1SoundFile::Close()
432 {
433 headers.clear();
434 definitions.clear();
435 cached_sound_code = -1;
436 cached_rsrc.Unload();
437 resource_file.Close();
438 }
439
GetSoundDefinition(int,int sound_index)440 SoundDefinition* M1SoundFile::GetSoundDefinition(int, int sound_index)
441 {
442 if (resource_file.Check('s', 'n', 'd', ' ', sound_index))
443 {
444 std::map<int16, SoundDefinition>::iterator it = definitions.find(sound_index);
445 if (it == definitions.end())
446 {
447 SoundDefinition definition;
448 definition.behavior_index = 2; // sound_is_loud
449 definition.sound_code = sound_index;
450 // look for permutations
451 definition.permutations = 1;
452 while (resource_file.Check('s', 'n', 'd', ' ', sound_index + definition.permutations) && definition.permutations < MAXIMUM_PERMUTATIONS_PER_SOUND)
453 {
454 ++definition.permutations;
455 }
456
457 it = definitions.insert(std::pair<int16, SoundDefinition>(sound_index, definition)).first;
458 }
459 return &(it->second);
460 }
461 else
462 {
463 return 0;
464 }
465 }
466
467 const int M1SoundFile::MAXIMUM_PERMUTATIONS_PER_SOUND = 5;
468
GetSoundHeader(SoundDefinition * definition,int permutation)469 SoundHeader M1SoundFile::GetSoundHeader(SoundDefinition* definition, int permutation)
470 {
471 int sound_index = definition->sound_code + std::min(permutation, MAXIMUM_PERMUTATIONS_PER_SOUND);
472
473 SoundHeader header;
474 std::map<int16, SoundHeader>::iterator it = headers.find(sound_index);
475 if (it == headers.end())
476 {
477 if (cached_sound_code != sound_index)
478 {
479 resource_file.Get('s', 'n', 'd', ' ', sound_index, cached_rsrc);
480 cached_sound_code = sound_index;
481 }
482
483 SoundHeader header;
484 header.Load(cached_rsrc);
485 it = headers.insert(std::pair<int16, SoundHeader>(sound_index, header)).first;
486 }
487
488 return it->second;
489 }
490
GetSoundData(SoundDefinition * definition,int permutation)491 boost::shared_ptr<SoundData> M1SoundFile::GetSoundData(SoundDefinition* definition, int permutation)
492 {
493 int sound_index = definition->sound_code + std::min(permutation, MAXIMUM_PERMUTATIONS_PER_SOUND);
494
495 if (cached_sound_code != sound_index)
496 {
497 resource_file.Get('s', 'n', 'd', ' ', sound_index, cached_rsrc);
498 cached_sound_code = sound_index;
499 }
500
501 return GetSoundHeader(definition, permutation).LoadData(cached_rsrc);
502 }
503