Speech bubbles can point down right.
[scummvm-innocent.git] / sound / voc.cpp
blob3e8d8c2ed3b75adf455f6c98c45a89ebb8d89d6f
1 /* ScummVM - Graphic Adventure Engine
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.
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.
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.
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.
21 * $URL$
22 * $Id$
26 #include "common/debug.h"
27 #include "common/endian.h"
28 #include "common/util.h"
29 #include "common/stream.h"
31 #include "sound/audiostream.h"
32 #include "sound/mixer.h"
33 #include "sound/voc.h"
36 namespace Audio {
38 int getSampleRateFromVOCRate(int vocSR) {
39 if (vocSR == 0xa5 || vocSR == 0xa6) {
40 return 11025;
41 } else if (vocSR == 0xd2 || vocSR == 0xd3) {
42 return 22050;
43 } else {
44 int sr = 1000000L / (256L - vocSR);
45 // inexact sampling rates occur e.g. in the kitchen in Monkey Island,
46 // very easy to reach right from the start of the game.
47 //warning("inexact sample rate used: %i (0x%x)", sr, vocSR);
48 return sr;
52 static byte *loadVOCFromStream(Common::ReadStream &stream, int &size, int &rate, int &loops, int &begin_loop, int &end_loop) {
53 VocFileHeader fileHeader;
55 if (stream.read(&fileHeader, 8) != 8)
56 goto invalid;
58 if (!memcmp(&fileHeader, "VTLK", 4)) {
59 if (stream.read(&fileHeader, sizeof(VocFileHeader)) != sizeof(VocFileHeader))
60 goto invalid;
61 } else if (!memcmp(&fileHeader, "Creative", 8)) {
62 if (stream.read(((byte *)&fileHeader) + 8, sizeof(VocFileHeader) - 8) != sizeof(VocFileHeader) - 8)
63 goto invalid;
64 } else {
65 invalid:;
66 warning("loadVOCFromStream: Invalid header");
67 return NULL;
70 if (memcmp(fileHeader.desc, "Creative Voice File", 19) != 0)
71 error("loadVOCFromStream: Invalid header");
72 if (fileHeader.desc[19] != 0x1A)
73 debug(3, "loadVOCFromStream: Partially invalid header");
75 int32 offset = FROM_LE_16(fileHeader.datablock_offset);
76 int16 version = FROM_LE_16(fileHeader.version);
77 int16 code = FROM_LE_16(fileHeader.id);
78 assert(offset == sizeof(VocFileHeader));
79 // 0x100 is an invalid VOC version used by German version of DOTT (Disk) and
80 // French version of Simon the Sorcerer 2 (CD)
81 assert(version == 0x010A || version == 0x0114 || version == 0x0100);
82 assert(code == ~version + 0x1234);
84 int len;
85 byte *ret_sound = 0;
86 size = 0;
87 begin_loop = 0;
88 end_loop = 0;
90 while ((code = stream.readByte())) {
91 len = stream.readByte();
92 len |= stream.readByte() << 8;
93 len |= stream.readByte() << 16;
95 switch (code) {
96 case 1:
97 case 9: {
98 int packing;
99 if (code == 1) {
100 int time_constant = stream.readByte();
101 packing = stream.readByte();
102 len -= 2;
103 rate = getSampleRateFromVOCRate(time_constant);
104 } else {
105 rate = stream.readUint32LE();
106 int bits = stream.readByte();
107 int channels = stream.readByte();
108 if (bits != 8 || channels != 1) {
109 warning("Unsupported VOC file format (%d bits per sample, %d channels)", bits, channels);
110 break;
112 packing = stream.readUint16LE();
113 stream.readUint32LE();
114 len -= 12;
116 debug(9, "VOC Data Block: %d, %d, %d", rate, packing, len);
117 if (packing == 0) {
118 if (size) {
119 ret_sound = (byte *)realloc(ret_sound, size + len);
120 } else {
121 ret_sound = (byte *)malloc(len);
123 stream.read(ret_sound + size, len);
124 size += len;
125 begin_loop = size;
126 end_loop = size;
127 } else {
128 warning("VOC file packing %d unsupported", packing);
130 } break;
131 case 3: // silence
132 // occur with a few Igor sounds, voc file starts with a silence block with a
133 // frequency different from the data block. Just ignore fow now (implementing
134 // it wouldn't make a big difference anyway...)
135 assert(len == 3);
136 stream.readUint16LE();
137 stream.readByte();
138 break;
139 case 6: // begin of loop
140 assert(len == 2);
141 loops = stream.readUint16LE();
142 break;
143 case 7: // end of loop
144 assert(len == 0);
145 break;
146 case 8: // "Extended"
147 // This occures in the LoL Intro demo. This block can usually be used to create stereo
148 // sound, but the LoL intro has only an empty block, thus this dummy implementation will
149 // work.
150 assert(len == 4);
151 stream.readUint16LE();
152 stream.readByte();
153 stream.readByte();
154 break;
155 default:
156 warning("Unhandled code in VOC file : %d", code);
157 return ret_sound;
160 debug(4, "VOC Data Size : %d", size);
161 return ret_sound;
164 byte *loadVOCFromStream(Common::ReadStream &stream, int &size, int &rate) {
165 int loops, begin_loop, end_loop;
166 return loadVOCFromStream(stream, size, rate, loops, begin_loop, end_loop);
169 AudioStream *makeVOCStream(Common::ReadStream &stream, byte flags, uint loopStart, uint loopEnd) {
170 int size, rate;
172 byte *data = loadVOCFromStream(stream, size, rate);
173 if (!data)
174 return 0;
176 return makeLinearInputStream(data, size, rate, flags | Audio::Mixer::FLAG_AUTOFREE, loopStart, loopEnd);
180 } // End of namespace Audio