1 /*
2 * Seven Kingdoms: Ancient Adversaries
3 *
4 * Copyright 2010 Unavowed <unavowed@vexillium.org>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20 #include <assert.h>
21 #include <limits.h>
22 #include <string.h>
23 #include <stdlib.h>
24
25 #include <ALL.h>
26 #include <dbglog.h>
27 #include <file_input_stream.h>
28 #include <file_util.h>
29 #include <wav_stream.h>
30
31 DBGLOG_DEFAULT_CHANNEL(Audio);
32
33 struct FormatHeader
34 {
35 enum { SIZE = 16 };
36
37 uint16_t audio_format;
38 uint16_t num_channels;
39 uint32_t frame_rate;
40 uint32_t byte_rate;
41 uint16_t block_align;
42 uint16_t bits_per_sample;
43 };
44
read_format_header(InputStream * in,FormatHeader * hdrp)45 static bool read_format_header(InputStream *in, FormatHeader *hdrp)
46 {
47 bool ok = true;
48
49 ok = ok && read_le<uint16_t>(in, &hdrp->audio_format);
50 ok = ok && read_le<uint16_t>(in, &hdrp->num_channels);
51 ok = ok && read_le<uint32_t>(in, &hdrp->frame_rate);
52 ok = ok && read_le<uint32_t>(in, &hdrp->byte_rate);
53 ok = ok && read_le<uint16_t>(in, &hdrp->block_align);
54 ok = ok && read_le<uint16_t>(in, &hdrp->bits_per_sample);
55
56 return ok;
57 }
58
59
WavStream()60 WavStream::WavStream()
61 {
62 this->clear();
63 }
64
~WavStream()65 WavStream::~WavStream()
66 {
67 this->close();
68 }
69
clear()70 void WavStream::clear()
71 {
72 this->in = NULL;
73 this->data_offset = 0;
74 this->data_length = 0;
75 this->data_left = 0;
76 this->fram_rate = 0;
77 this->chans = 0;
78 this->bytes = 0;
79 this->good = true;
80 }
81
advance_to_chunk(const char * name,uint32_t * sizep)82 bool WavStream::advance_to_chunk(const char *name, uint32_t *sizep)
83 {
84 char buffer[4];
85 uint32_t size;
86 bool ok = true;
87
88 for (;;)
89 {
90 ok = ok && in->read(buffer, 4);
91 ok = ok && read_le<uint32_t>(this->in, &size);
92
93 if (!ok)
94 return false;
95
96 if (memcmp(buffer, name, 4) == 0)
97 {
98 *sizep = size;
99 return true;
100 }
101
102 ok = ok && this->in->seek(size, SEEK_CUR);
103 }
104
105 return false;
106 }
107
open(const char * file_name)108 bool WavStream::open(const char *file_name)
109 {
110 FileInputStream *in;
111
112 this->close();
113
114 in = new FileInputStream;
115 if (!in->open(file_name) || !this->open(in))
116 {
117 delete in;
118 return false;
119 }
120
121 return true;
122 }
123
open(InputStream * in)124 bool WavStream::open(InputStream *in)
125 {
126 char name[4];
127 uint32_t size;
128 bool ok = true;
129 FormatHeader fmth;
130
131 this->close();
132 this->in = in;
133
134 ok = ok && this->in->seek(0, SEEK_SET);
135 ok = ok && in->read(name, 4);
136 ok = ok && (memcmp(name, "RIFF", 4) == 0);
137 ok = ok && this->in->seek(4, SEEK_CUR);
138 ok = ok && in->read(name, 4);
139 ok = ok && (memcmp(name, "WAVE", 4) == 0);
140
141 if (!ok)
142 {
143 ERR("[WavStream::open] Not a wave file\n");
144 goto err;
145 }
146
147 ok = ok && this->advance_to_chunk("fmt ", &size);
148 ok = ok && (size >= FormatHeader::SIZE);
149 ok = ok && read_format_header(this->in, &fmth);
150 ok = ok && this->in->seek(size - FormatHeader::SIZE, SEEK_CUR);
151 if (!ok || fmth.audio_format != 1
152 || (fmth.bits_per_sample != 8 && fmth.bits_per_sample != 16)
153 || (fmth.num_channels != 1 && fmth.num_channels != 2))
154 {
155 ERR("[WavStream::open] Unsupported format\n");
156 goto err;
157 }
158
159 this->bytes = fmth.bits_per_sample / 8;
160 this->chans = fmth.num_channels;
161 this->fram_rate = fmth.frame_rate;
162
163 ok = ok && this->advance_to_chunk("data", &size);
164 if (!ok)
165 {
166 ERR("[WavStream::open] Missing data chunk\n");
167 goto err;
168 }
169
170 this->data_offset = this->in->tell();
171 this->data_length = size / this->frame_size();
172 this->data_left = this->data_length;
173
174 return true;
175
176 err:
177 this->in = NULL; /* do not close in on error */
178 this->close();
179 return false;
180 }
181
seek(size_t frame_no)182 bool WavStream::seek(size_t frame_no)
183 {
184 if (!this->good)
185 return false;
186
187 if (frame_no >= this->data_length)
188 return false;
189
190 if (!this->in->seek(this->data_offset + this->frame_size() * frame_no,
191 SEEK_SET))
192 {
193 ERR("[WavStream::seek] Seek failed\n");
194 this->good = false;
195 return false;
196 }
197
198 this->data_left = this->data_length - frame_no;
199
200 return true;
201 }
202
close()203 void WavStream::close()
204 {
205 delete this->in;
206 this->clear();
207 }
208
read(void * buffer,size_t frame_count)209 long WavStream::read(void *buffer, size_t frame_count)
210 {
211 size_t read_size;
212
213 if (!this->good)
214 return -1;
215
216 read_size = MIN(frame_count, this->data_left);
217 if (read_size == 0)
218 return 0;
219
220 if (this->bytes == 2)
221 {
222 int16_t *buf = static_cast<int16_t *>(buffer);
223 int16_t *p;
224
225 for (size_t n = 0; n < read_size; n++)
226 {
227 for (int c = 0; c < this->chans; c++)
228 {
229 p = &buf[n * this->chans + c];
230
231 if (!read_le<int16_t>(this->in, p))
232 {
233 this->good = false;
234 return n;
235 }
236 }
237 }
238 }
239 else if (this->bytes == 1)
240 {
241 if (!this->in->read(buffer, this->chans * read_size))
242 {
243 this->good = false;
244 return 0;
245 }
246 }
247 else
248 abort();
249
250 this->data_left -= read_size;
251
252 return read_size;
253 }
254
frame_rate() const255 int32_t WavStream::frame_rate() const
256 {
257 return this->fram_rate;
258 }
259
channels() const260 int WavStream::channels() const
261 {
262 return this->chans;
263 }
264
sample_size() const265 int WavStream::sample_size() const
266 {
267 return this->bytes;
268 }
269