Mercurial > otakunoraifu
annotate music2/wavfile.cc @ 66:d112357a0ec1
Fix a bug with savegames introduced with changeset c7bcc0ec2267.
Warning: savegames created since c7bcc0ec2267 are probably corrupted,
you may have to start the game over.
If you chose not to do so, you should replace all occurrences of 'TextWindow' by 'TextImplWindow',
and 'Text Window' by 'TextImpl Window' in your save files.
author | Thibaut Girka <thib@sitedethib.com> |
---|---|
date | Sat, 11 Dec 2010 18:36:20 +0100 |
parents | 4416cfac86ae |
children |
rev | line source |
---|---|
0 | 1 /* |
2 * wavfile.c WAV file check | |
3 * | |
4 * Copyright: wavfile.c (c) Erik de Castro Lopo erikd@zip.com.au | |
5 * | |
6 * Modified : 1997-1998 Masaki Chikama (Wren) <chikama@kasumi.ipl.mech.nagoya-u.ac.jp> | |
7 * 1998- <masaki-c@is.aist-nara.ac.jp> | |
8 * 2000- Kazunori Ueno(JAGARL) <jagarl@createor.club.ne.jp> | |
9 * | |
10 * This program is free software; you can redistribute it and/or modify | |
11 * it under the terms of the GNU General Public License as published by | |
12 * the Free Software Foundation; either version 2 of the License, or | |
13 * (at your option) any later version. | |
14 * | |
15 * This program is distributed in the hope that it will be useful, | |
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 * GNU General Public License for more details. | |
19 * | |
27 | 20 * You should have received a copy of the GNU General Public License along |
21 * with this program; if not, write to the Free Software Foundation, Inc., | |
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
0 | 23 * |
24 */ | |
25 | |
52 | 26 #include <stdarg.h> |
27 #include <stdio.h> | |
28 #include <stdlib.h> | |
29 #include <errno.h> | |
30 #include <sys/types.h> | |
31 #include <unistd.h> | |
32 #include <string.h> | |
33 #include "wavfile.h" | |
34 #include "system/file.h" | |
35 #include "music.h" | |
0 | 36 |
52 | 37 #define BUFFERSIZE 1024 |
38 #define PCM_WAVE_FORMAT 1 | |
0 | 39 |
40 /******************************************************* | |
41 ** | |
42 ** WAVE Header | |
43 */ | |
44 | |
45 inline int LittleEndian_getDW(const char *b,int index) { | |
46 int c0, c1, c2, c3; | |
47 int d0, d1; | |
48 c0 = *(const unsigned char*)(b + index + 0); | |
49 c1 = *(const unsigned char*)(b + index + 1); | |
50 c2 = *(const unsigned char*)(b + index + 2); | |
51 c3 = *(const unsigned char*)(b + index + 3); | |
52 d0 = c0 + (c1 << 8); | |
53 d1 = c2 + (c3 << 8); | |
54 return d0 + (d1 << 16); | |
55 } | |
56 | |
57 inline int LittleEndian_get3B(const char *b,int index) { | |
58 int c0, c1, c2; | |
59 c0 = *(const unsigned char*)(b + index + 0); | |
60 c1 = *(const unsigned char*)(b + index + 1); | |
61 c2 = *(const unsigned char*)(b + index + 2); | |
62 return c0 + (c1 << 8) + (c2 << 16); | |
63 } | |
64 | |
65 inline int LittleEndian_getW(const char *b,int index) { | |
66 int c0, c1; | |
67 c0 = *(const unsigned char*)(b + index + 0); | |
68 c1 = *(const unsigned char*)(b + index + 1); | |
69 return c0 + (c1 << 8); | |
70 } | |
71 | |
72 inline void LittleEndian_putW(int num, char *b, int index) { | |
73 int c0, c1; | |
74 num %= 65536; | |
75 c0 = num % 256; | |
76 c1 = num / 256; | |
52 | 77 b[index] = c0; |
78 b[index+1] = c1; | |
0 | 79 } |
80 | |
81 typedef struct | |
82 { u_long dwSize ; | |
83 u_short wFormatTag ; | |
84 u_short wChannels ; | |
85 u_long dwSamplesPerSec ; | |
86 u_long dwAvgBytesPerSec ; | |
87 u_short wBlockAlign ; | |
88 u_short wBitsPerSample ; | |
52 | 89 } WAVEFORMAT; |
0 | 90 |
91 typedef struct | |
92 { char RiffID [4] ; | |
93 u_long RiffSize ; | |
94 char WaveID [4] ; | |
95 char FmtID [4] ; | |
96 u_long FmtSize ; | |
97 u_short wFormatTag ; | |
98 u_short nChannels ; | |
99 u_long nSamplesPerSec ; | |
100 u_long nAvgBytesPerSec ; | |
101 u_short nBlockAlign ; | |
102 u_short wBitsPerSample ; | |
103 char DataID [4] ; | |
104 u_long nDataBytes ; | |
52 | 105 } WAVE_HEADER; |
0 | 106 |
107 | |
52 | 108 static void waveFormatCopy(WAVEFORMAT* wav, char *ptr ); |
109 static char* findchunk(char* s1, const char* s2, size_t n) ; | |
0 | 110 |
52 | 111 static int WaveHeaderCheck(char *wave_buf,int* channels, u_long* samplerate, int* samplebits, u_long* samples,u_long* datastart) |
112 { | |
113 static WAVEFORMAT waveformat; | |
114 char* ptr; | |
115 u_long databytes; | |
0 | 116 |
52 | 117 if (findchunk(wave_buf, "RIFF", BUFFERSIZE) != wave_buf) { |
0 | 118 fprintf(stderr, "Bad format: Cannot find RIFF file marker"); |
119 return WR_BADRIFF ; | |
120 } | |
121 | |
52 | 122 if (findchunk(wave_buf, "WAVE", BUFFERSIZE) == NULL) { |
0 | 123 fprintf(stderr, "Bad format: Cannot find WAVE file marker"); |
124 return WR_BADWAVE ; | |
125 } | |
126 | |
52 | 127 ptr = findchunk(wave_buf, "fmt ", BUFFERSIZE) ; |
0 | 128 |
52 | 129 if (ptr == NULL) { |
0 | 130 fprintf(stderr, "Bad format: Cannot find 'fmt' file marker"); |
131 return WR_BADFORMAT ; | |
132 } | |
133 | |
134 ptr += 4 ; /* Move past "fmt ".*/ | |
135 waveFormatCopy( &waveformat, ptr ); | |
136 | |
137 if (waveformat.dwSize != (sizeof (WAVEFORMAT) - sizeof (u_long))) { | |
138 /* fprintf(stderr, "Bad format: Bad fmt size"); */ | |
139 /* return WR_BADFORMATSIZE ; */ | |
140 } | |
141 | |
142 if (waveformat.wFormatTag != PCM_WAVE_FORMAT) { | |
143 fprintf(stderr, "Only supports PCM wave format"); | |
144 return WR_NOTPCMFORMAT ; | |
145 } | |
146 | |
52 | 147 ptr = findchunk(wave_buf, "data", BUFFERSIZE) ; |
0 | 148 |
52 | 149 if (ptr == NULL) { |
0 | 150 fprintf(stderr,"Bad format: unable to find 'data' file marker"); |
151 return WR_NODATACHUNK ; | |
152 } | |
153 | |
154 ptr += 4 ; /* Move past "data".*/ | |
155 databytes = LittleEndian_getDW(ptr, 0); | |
156 | |
157 /* Everything is now cool, so fill in output data.*/ | |
158 | |
159 *channels = waveformat.wChannels; | |
160 *samplerate = waveformat.dwSamplesPerSec ; | |
161 *samplebits = waveformat.wBitsPerSample ; | |
162 *samples = databytes / waveformat.wBlockAlign ; | |
163 | |
164 *datastart = (u_long)(ptr) + 4; | |
165 | |
166 if (waveformat.dwSamplesPerSec != waveformat.dwAvgBytesPerSec / waveformat.wBlockAlign) { | |
167 fprintf(stderr, "Bad file format"); | |
168 return WR_BADFORMATDATA ; | |
169 } | |
170 | |
171 if (waveformat.dwSamplesPerSec != waveformat.dwAvgBytesPerSec / waveformat.wChannels / ((waveformat.wBitsPerSample == 16) ? 2 : 1)) { | |
172 fprintf(stderr, "Bad file format"); | |
173 return WR_BADFORMATDATA ; | |
174 } | |
175 | |
52 | 176 return 0; |
177 } /* WaveHeaderCheck*/ | |
0 | 178 |
179 | |
52 | 180 static char* findchunk(char* pstart, const char* fourcc, size_t n) { |
181 char *pend; | |
182 int k, test; | |
0 | 183 |
52 | 184 pend = pstart + n; |
0 | 185 |
186 while (pstart < pend) | |
187 { | |
52 | 188 if (*pstart == *fourcc) { /* found match for first char*/ |
189 test = 1 ; | |
190 for (k = 1 ; fourcc[k] != 0 ; k++) | |
191 test = (test ? ( pstart[k] == fourcc[k] ) : 0) ; | |
0 | 192 if (test) |
193 return pstart ; | |
52 | 194 } ; /* if*/ |
195 pstart++; | |
196 } /* while lpstart*/ | |
0 | 197 |
52 | 198 return NULL; |
199 } /* findchuck*/ | |
0 | 200 |
201 static void waveFormatCopy( WAVEFORMAT* wav, char *ptr ) { | |
202 wav->dwSize = LittleEndian_getDW( ptr, 0 ); | |
203 wav->wFormatTag = LittleEndian_getW( ptr, 4 ); | |
204 wav->wChannels = LittleEndian_getW( ptr, 6 ); | |
205 wav->dwSamplesPerSec = LittleEndian_getDW( ptr, 8 ); | |
206 wav->dwAvgBytesPerSec = LittleEndian_getDW( ptr, 12 ); | |
207 wav->wBlockAlign = LittleEndian_getW( ptr, 16 ); | |
208 wav->wBitsPerSample = LittleEndian_getW( ptr, 18 ); | |
209 } | |
210 | |
211 static char* WavGetInfo(WAVFILE* wfile, char *data) { | |
212 int e; /* Saved errno value */ | |
213 int channels; /* Channels recorded in this wav file */ | |
214 u_long samplerate; /* Sampling rate */ | |
215 int sample_bits; /* data bit size (8/12/16) */ | |
216 u_long samples; /* The number of samples in this file */ | |
217 u_long datastart; /* The offset to the wav data */ | |
218 | |
219 if ( (e = WaveHeaderCheck(data, | |
220 &channels,&samplerate, | |
221 &sample_bits,&samples,&datastart) != 0 )) { | |
222 fprintf(stderr,"WavGetInfo(): Reading WAV header\n"); | |
52 | 223 return NULL; |
0 | 224 } |
52 | 225 |
0 | 226 /* |
227 * Copy WAV data over to WAVFILE struct: | |
228 */ | |
229 wfile->wavinfo.Channels = channels; | |
230 | |
231 wfile->wavinfo.SamplingRate = (unsigned int) samplerate; | |
232 wfile->wavinfo.DataBits = (unsigned short) sample_bits; | |
233 | |
234 return (char *) datastart; | |
235 } | |
236 | |
237 /************************************************************: | |
238 ** | |
239 ** WAVFILE stream reader | |
240 */ | |
241 | |
52 | 242 #include <SDL_mixer.h> |
0 | 243 WAVFILE::WAVFILE(void) { |
52 | 244 wavinfo.SamplingRate = 0; |
245 wavinfo.Channels = 1; | |
246 wavinfo.DataBits = 0; | |
0 | 247 } |
248 | |
249 int WAVFILE_Stream::Read(char* in_buf, int blksize, int length) { | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
250 /* ファイルの読み込み */ |
0 | 251 if (data_length == 0 && stream_length == 0) return -1; |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
252 /* wf->data にデータの残りがあればそれも読み込む */ |
0 | 253 if (data_length > blksize*length) { |
254 memcpy(in_buf, data, blksize*length); | |
255 data += blksize * length; | |
256 data_length -= blksize * length; | |
257 return length; | |
258 } | |
259 memcpy(in_buf, data, data_length); | |
260 if (stream_length != -1 && stream_length < blksize*length-data_length) { | |
261 length = (stream_length+data_length+blksize-1)/blksize; | |
262 } | |
263 int read_len = 0; | |
264 if (blksize*length-data_length > 0) { | |
265 read_len = fread(in_buf+data_length, 1, blksize*length-data_length, stream); | |
266 if (stream_length != -1 && stream_length > read_len) stream_length -= read_len; | |
267 if (feof(stream)) stream_length = 0; // end of file | |
268 } else { | |
269 stream_length = 0; // all data were read | |
270 } | |
271 int blklen = (read_len + data_length) / blksize; | |
272 data_length = 0; | |
273 return blklen; | |
274 } | |
52 | 275 |
0 | 276 void WAVFILE_Stream::Seek(int count) { |
277 int blksize = 1; | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
278 /* block size の設定 */ |
0 | 279 blksize *= wavinfo.Channels * (wavinfo.DataBits/8); |
280 data_length = 0; | |
281 stream_length = stream_length_orig - stream_top - count*blksize; | |
52 | 282 fseek(stream, count*blksize+stream_top, SEEK_SET); |
0 | 283 } |
52 | 284 |
0 | 285 WAVFILE_Stream::WAVFILE_Stream(FILE* _stream, int _length) { |
286 stream = _stream; | |
287 stream_length = _length; | |
288 stream_length_orig = _length; | |
289 data_orig = new char[1024]; | |
290 data = data_orig; | |
291 data_length = 1024; | |
292 if (stream_length != -1 && stream_length < data_length) { | |
293 data_length = stream_length; | |
294 } | |
295 fread(data, data_length, 1, stream); | |
296 if (stream_length != -1) | |
297 stream_length -= data_length; | |
298 data = WavGetInfo(this, data); | |
299 if (data == 0) { | |
300 stream_length = 0; | |
301 data_length = 0; | |
302 return; | |
303 } | |
304 stream_top = data - data_orig; | |
305 data_length -= data - data_orig; | |
306 } | |
52 | 307 |
0 | 308 WAVFILE_Stream::~WAVFILE_Stream() { |
309 if (data_orig) delete data_orig; | |
310 if (stream) fclose(stream); | |
311 } | |
52 | 312 |
0 | 313 /************************************************************: |
314 ** | |
315 ** WAVE format converter with SDL_audio | |
316 */ | |
317 WAVFILE* WAVFILE::MakeConverter(WAVFILE* new_reader) { | |
318 bool need = false; | |
319 if (new_reader->wavinfo.SamplingRate != freq) need = true; | |
320 if (new_reader->wavinfo.Channels != channels) need = true; | |
321 if (format == AUDIO_S8) { | |
322 if (new_reader->wavinfo.DataBits != 8) need = true; | |
323 } else if (format == AUDIO_S16) { | |
324 if (new_reader->wavinfo.DataBits != 16) need = true; | |
325 } else { | |
326 need = true; | |
327 } | |
328 if (!need) return new_reader; | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
329 /* 変換もとのフォーマットを得る */ |
0 | 330 int from_format; |
331 if (new_reader->wavinfo.DataBits == 8) from_format = AUDIO_S8; | |
332 else from_format = AUDIO_S16; | |
333 SDL_AudioCVT* cvt = new SDL_AudioCVT; | |
334 int ret = SDL_BuildAudioCVT(cvt, from_format, new_reader->wavinfo.Channels, freq, | |
335 format, 2, freq); | |
336 if (ret == -1) { | |
337 delete cvt; | |
338 fprintf(stderr,"Cannot make wave file converter!!!\n"); | |
339 return new_reader; | |
340 } | |
341 WAVFILE_Converter* conv = new WAVFILE_Converter(new_reader, cvt); | |
342 return conv; | |
343 } | |
52 | 344 |
0 | 345 WAVFILE_Converter::WAVFILE_Converter(WAVFILE* _orig, SDL_AudioCVT* _cvt) { |
346 original = _orig; | |
347 cvt = _cvt; | |
348 //datasize = 4096*4; | |
349 datasize = 48000; | |
350 cvt->buf = new Uint8[datasize*cvt->len_mult]; | |
351 cvt->len = 0; | |
352 tmpbuf = new char[datasize*cvt->len_mult + 1024]; | |
353 memset(tmpbuf, 0, datasize*cvt->len_mult+1024); | |
52 | 354 } |
0 | 355 |
356 static int conv_wave_rate(short* in_buf, int length, int in_rate, int out_rate, char* tmpbuf); | |
357 WAVFILE_Converter::~WAVFILE_Converter() { | |
52 | 358 if (cvt != NULL) { |
7 | 359 if (cvt->buf) delete[] cvt->buf; |
0 | 360 delete cvt; |
52 | 361 cvt = NULL; |
0 | 362 } |
8 | 363 delete[] tmpbuf; |
0 | 364 if (original) delete original; |
52 | 365 original = NULL; |
0 | 366 } |
52 | 367 |
0 | 368 int WAVFILE_Converter::Read(char* buf, int blksize, int blklen) { |
52 | 369 if (original == NULL || cvt == NULL) return -1; |
0 | 370 int copied_length = 0; |
371 if (cvt->len < blksize*blklen) { | |
372 memcpy(buf, cvt->buf, cvt->len); | |
373 copied_length += cvt->len; | |
374 do { | |
375 int cnt = original->Read((char*)cvt->buf, 1, datasize); | |
376 if (cnt <= 0) { | |
377 cvt->len = 0; | |
378 break; | |
379 } | |
380 cvt->len = cnt; | |
381 SDL_ConvertAudio(cvt); | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
382 if (freq < original->wavinfo.SamplingRate) { // rate conversion は SDL_ConvertAudio ではうまく行かない |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
383 // 48000Hz -> 44100Hz or 22050Hz などを想定 |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
384 // 長さは短くなるはずなので、特に処理はなし |
0 | 385 cvt->len = conv_wave_rate( (short*)(cvt->buf), cvt->len_cvt/4, original->wavinfo.SamplingRate, freq, tmpbuf); |
386 cvt->len *= 4; | |
387 } else { | |
388 cvt->len = cvt->len_cvt; | |
389 } | |
390 if (cvt->len+copied_length > blksize*blklen) break; | |
391 memcpy(buf+copied_length, cvt->buf, cvt->len); | |
392 copied_length += cvt->len; | |
393 } while(1); | |
394 } | |
395 if (cvt->len == 0 && copied_length == 0) return -1; | |
396 else if (cvt->len > 0) { | |
397 int len = blksize * blklen - copied_length; | |
398 memcpy(buf+copied_length, cvt->buf, len); | |
399 memmove(cvt->buf, cvt->buf+len, cvt->len-len); | |
400 copied_length += len; | |
401 cvt->len -= len; | |
402 } | |
403 return copied_length / blksize; | |
404 } | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
405 /* format は signed, 16bit, little endian, stereo と決めうち |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
406 ** 場合によっていは big endian になることもあるかも。 |
0 | 407 */ |
408 static int conv_wave_rate(short* in_buf, int length, int in_rate, int out_rate, char* tmpbuf) { | |
409 int input_rate = in_rate; | |
410 int output_rate = out_rate; | |
411 double input_rate_d = input_rate, output_rate_d = output_rate; | |
412 double dtime; int outlen; short* out, * out_orig; int next_sample1, next_sample2; | |
413 short* in_buf_orig = in_buf; | |
52 | 414 int i, time; |
0 | 415 |
416 if (input_rate == output_rate) return length; | |
417 if (length <= 0) return 0; | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
418 /* 一般の周波数変換:線型補完 */ |
0 | 419 int& first_flag = *(int*)(tmpbuf); |
420 int& prev_time = *(int*)(tmpbuf+4); | |
421 int& prev_sample1 = *(int*)(tmpbuf+8); | |
422 int& prev_sample2 = *(int*)(tmpbuf+12); | |
423 out = (short*)(tmpbuf+16); | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
424 /* 初めてならデータを初期化 */ |
0 | 425 if (first_flag == 0) { |
426 first_flag = 1; | |
427 prev_time = 0; | |
428 prev_sample1 = short(read_little_endian_short((char*)(in_buf++))); | |
429 prev_sample2 = short(read_little_endian_short((char*)(in_buf++))); | |
430 length--; | |
431 } | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
432 /* 今回作成するデータ量を得る */ |
0 | 433 dtime = prev_time + length * output_rate_d; |
434 outlen = (int)(dtime / input_rate_d); | |
435 out_orig = out; | |
436 if (first_flag == 1) { | |
437 write_little_endian_short((char*)out, prev_sample1); | |
438 out++; | |
439 write_little_endian_short((char*)out, prev_sample2); | |
440 out++; | |
441 } | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
442 dtime -= input_rate_d*outlen; /* 次の prev_time */ |
0 | 443 |
52 | 444 time = 0; |
0 | 445 next_sample1 = short(read_little_endian_short((char*)(in_buf++))); |
446 next_sample2 = short(read_little_endian_short((char*)(in_buf++))); | |
52 | 447 for (i=0; i < outlen; i++) { |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
448 /* double で計算してみたけどそう簡単には高速化は無理らしい */ |
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
449 /* なお、変換は 1分のデータに1秒程度かかる(Celeron 700MHz) */ |
0 | 450 time += input_rate; |
451 while(time-prev_time>output_rate) { | |
452 prev_sample1 = next_sample1; | |
453 next_sample1 = short(read_little_endian_short((char*)(in_buf++))); | |
454 prev_sample2 = next_sample2; | |
455 next_sample2 = short(read_little_endian_short((char*)(in_buf++))); | |
456 prev_time += output_rate; | |
457 } | |
458 write_little_endian_short((char*)out, | |
459 ((time-prev_time)*next_sample1 + | |
460 (input_rate-time+prev_time)*prev_sample1) / input_rate); | |
461 out++; | |
462 write_little_endian_short((char*)out, | |
463 ((time-prev_time)*next_sample2 + | |
464 (input_rate-time+prev_time)*prev_sample2) / input_rate); | |
465 *out++; | |
466 } | |
467 prev_time += output_rate; prev_time -= input_rate * outlen; | |
468 prev_sample1 = next_sample1; prev_sample2 = next_sample2; | |
469 if (first_flag == 1) { | |
470 outlen++; first_flag = 2; | |
471 } | |
472 memcpy(in_buf_orig, out_orig, outlen*2*sizeof(short)); | |
473 return outlen; | |
474 } | |
475 | |
476 | |
477 /************************************************************: | |
478 ** | |
479 ** MP3FILE stream reader | |
480 */ | |
481 | |
482 int WAVFILE::freq = 48000; | |
483 int WAVFILE::channels = 2; | |
484 int WAVFILE::format = MIX_DEFAULT_FORMAT; | |
485 | |
486 #if HAVE_LIBMAD | |
487 | |
52 | 488 #include <mad.h> |
0 | 489 #define MPEG_BUFSZ 40000 /* 2.5 s at 128 kbps; 1 s at 320 kbps */ |
490 struct MP3FILE_impl { | |
491 enum { PREPARE, RUN, WRITE, DONE} status; | |
492 struct mad_decoder decoder; | |
493 char* data; | |
494 int data_len; | |
495 char* write_data; | |
496 unsigned int write_data_len; | |
497 unsigned int write_pointer; | |
498 unsigned int src_pointer; | |
499 FILE* stream; | |
500 MP3FILE_impl(FILE*); | |
501 ~MP3FILE_impl(); | |
502 static enum mad_flow callback_read(void *data, struct mad_stream *stream); | |
503 static enum mad_flow callback_error(void *data, struct mad_stream *stream, struct mad_frame *frame); | |
504 static enum mad_flow callback_write(void *data, struct mad_header const *header, struct mad_pcm *pcm); | |
505 enum mad_flow callback_write_impl(struct mad_pcm *pcm); | |
506 void run(void); | |
507 }; | |
508 | |
509 MP3FILE_impl::MP3FILE_impl(FILE* _stream) { | |
510 stream = _stream; | |
511 data = new char[MPEG_BUFSZ]; | |
512 data_len = 0; | |
513 src_pointer = 0; | |
52 | 514 write_data = NULL; |
0 | 515 write_data_len = 0; |
516 write_pointer = 0; | |
517 | |
518 /* initialize decoder */ | |
519 mad_decoder_init(&decoder, (void*)this, callback_read, 0 /* header */, 0 /* filter */, callback_write, | |
520 callback_error, 0 /* message */); | |
521 /* prepare stream */ | |
522 status = PREPARE; | |
523 *(void**)(&decoder.sync) = malloc(sizeof(*decoder.sync)); | |
524 | |
525 mad_stream_init(&decoder.sync->stream); | |
526 mad_frame_init(&decoder.sync->frame); | |
527 mad_synth_init(&decoder.sync->synth); | |
528 | |
529 mad_stream_options(&decoder.sync->stream, decoder.options); | |
530 | |
531 while(status != WRITE && status != DONE) run(); | |
532 } | |
533 MP3FILE_impl::~MP3FILE_impl() { | |
534 free(decoder.sync); | |
535 mad_decoder_finish(&decoder); | |
536 delete[] data; | |
537 } | |
538 | |
539 void MP3FILE_impl::run(void) { | |
540 if (status == DONE) return; | |
541 struct mad_stream *stream = &decoder.sync->stream; | |
542 struct mad_frame *frame = &decoder.sync->frame; | |
543 struct mad_synth *synth = &decoder.sync->synth; | |
544 if (status == PREPARE) { | |
545 switch (decoder.input_func(decoder.cb_data, stream)) { | |
546 case MAD_FLOW_STOP: | |
547 case MAD_FLOW_BREAK: | |
548 goto done; | |
549 case MAD_FLOW_CONTINUE: | |
550 status = RUN; | |
551 case MAD_FLOW_IGNORE: | |
552 break; | |
553 } | |
554 return; | |
555 } | |
556 if (status == RUN) { | |
557 if (mad_frame_decode(frame, stream) == -1) { | |
558 if (!MAD_RECOVERABLE(stream->error)) { | |
559 status = PREPARE; | |
560 return; | |
561 } | |
562 switch (decoder.error_func((void*)this, stream, frame)) { | |
563 case MAD_FLOW_STOP: | |
564 case MAD_FLOW_BREAK: | |
565 goto done; | |
566 case MAD_FLOW_IGNORE: | |
567 status = PREPARE; | |
568 return; | |
569 case MAD_FLOW_CONTINUE: | |
570 default: | |
571 return; | |
572 } | |
573 } | |
574 | |
575 mad_synth_frame(synth, frame); | |
576 src_pointer = 0; | |
577 status = WRITE; | |
578 return; | |
579 } | |
580 if (status == WRITE) { | |
581 switch (decoder.output_func(decoder.cb_data, &frame->header, &synth->pcm)) { | |
582 case MAD_FLOW_STOP: | |
583 case MAD_FLOW_BREAK: | |
584 goto done; | |
585 case MAD_FLOW_IGNORE: | |
586 return; | |
587 case MAD_FLOW_CONTINUE: | |
588 status = RUN; | |
589 break; | |
590 } | |
591 if (stream->error == MAD_ERROR_BUFLEN) { | |
592 stream->error = MAD_ERROR_NONE; | |
593 status = PREPARE; | |
594 } | |
595 return; | |
596 } | |
597 done: | |
598 status = DONE; | |
599 mad_synth_finish(&decoder.sync->synth); | |
600 mad_frame_finish(&decoder.sync->frame); | |
601 mad_stream_finish(&decoder.sync->stream); | |
602 return; | |
603 } | |
604 | |
605 enum mad_flow MP3FILE_impl::callback_read(void *data, struct mad_stream *stream) | |
606 { | |
607 MP3FILE_impl* impl = (MP3FILE_impl*)data; | |
608 if (stream->next_frame) { | |
609 impl->data_len -= (char*)stream->next_frame - impl->data; | |
610 memmove(impl->data, (char*)stream->next_frame, impl->data_len); | |
611 } else { | |
612 impl->data_len = 0; | |
613 } | |
614 int count; | |
615 if (feof(impl->stream)) { | |
616 if (stream->next_frame && (char*)stream->next_frame - impl->data > 0) { | |
617 // There is under processing data | |
618 count = 0; | |
619 } else { | |
620 // all data were processed | |
621 return MAD_FLOW_STOP; | |
622 } | |
623 } else { | |
624 count = fread(impl->data + impl->data_len, 1, MPEG_BUFSZ-impl->data_len, impl->stream); | |
625 if (count <= 0) { | |
626 return MAD_FLOW_BREAK; | |
627 } | |
628 } | |
629 impl->data_len += count; | |
630 if (impl->data_len < MPEG_BUFSZ) { | |
631 memset(impl->data + impl->data_len, 0, MPEG_BUFSZ-impl->data_len); | |
632 } | |
633 mad_stream_buffer(stream, (unsigned char*)impl->data, impl->data_len); | |
634 return MAD_FLOW_CONTINUE; | |
635 } | |
636 | |
637 enum mad_flow MP3FILE_impl::callback_error(void *data, struct mad_stream *stream, struct mad_frame *frame) | |
638 { | |
639 MP3FILE_impl* impl = (MP3FILE_impl*)data; | |
640 fprintf(stdout, "decoding error 0x%04x (%s) at byte offset %u\n", | |
641 stream->error, mad_stream_errorstr(stream), | |
642 ftell(impl->stream) - ((impl->data+impl->data_len)-(char*)stream->this_frame)); | |
643 /* return MAD_FLOW_BREAK here to stop decoding (and propagate an error) */ | |
644 return MAD_FLOW_CONTINUE; | |
645 } | |
52 | 646 |
0 | 647 signed int scale(mad_fixed_t sample) |
648 { | |
649 /* round */ | |
650 sample += (1L << (MAD_F_FRACBITS - 16)); | |
651 | |
652 /* clip */ | |
653 if (sample >= MAD_F_ONE) | |
654 sample = MAD_F_ONE - 1; | |
655 else if (sample < -MAD_F_ONE) | |
656 sample = -MAD_F_ONE; | |
657 | |
658 /* quantize */ | |
659 return sample >> (MAD_F_FRACBITS + 1 - 16); | |
660 } | |
52 | 661 |
0 | 662 enum mad_flow MP3FILE_impl::callback_write(void *data, struct mad_header const *header, struct mad_pcm *pcm) |
663 { | |
664 MP3FILE_impl* pimpl = (MP3FILE_impl*)data; | |
665 return pimpl->callback_write_impl(pcm); | |
666 } | |
52 | 667 |
0 | 668 enum mad_flow MP3FILE_impl::callback_write_impl(struct mad_pcm *pcm) |
669 { | |
670 if (write_data_len == 0) return MAD_FLOW_IGNORE; | |
671 mad_fixed_t const *left_ch = pcm->samples[0] + src_pointer; | |
672 mad_fixed_t const *right_ch = pcm->samples[1] + src_pointer; | |
673 | |
674 unsigned int nchannels = pcm->channels; | |
675 unsigned int nsamples = pcm->length - src_pointer; | |
676 if (write_pointer + nsamples * nchannels * 2 > write_data_len) { | |
677 nsamples = (write_data_len - write_pointer) / nchannels / 2; | |
678 } | |
65
4416cfac86ae
Convert EUC-JP files to UTF8
Thibaut Girka <thib@sitedethib.com>
parents:
52
diff
changeset
|
679 write_data_len &= ~(nchannels*2-1); /* write_data_len はあらかじめ丸めておく */ |
0 | 680 src_pointer += nsamples; |
52 | 681 if (write_data == NULL) { // skip data write |
0 | 682 write_pointer += nsamples*2*2; |
683 } else while(nsamples--) { | |
684 signed int sample = scale(*left_ch++); | |
685 write_data[write_pointer++] = sample & 0xff; | |
686 write_data[write_pointer++] = (sample>>8) & 0xff; | |
687 if (nchannels == 2) { | |
688 sample = scale(*right_ch++); | |
689 } | |
690 write_data[write_pointer++] = sample & 0xff; | |
691 write_data[write_pointer++] = (sample>>8) & 0xff; | |
692 } | |
693 if (write_pointer >= write_data_len) return MAD_FLOW_IGNORE; | |
694 else return MAD_FLOW_CONTINUE; | |
695 } | |
696 | |
697 MP3FILE::MP3FILE(FILE* stream, int len) { | |
698 pimpl = new MP3FILE_impl(stream); | |
699 if (pimpl->status == MP3FILE_impl::DONE) { | |
700 delete pimpl; | |
52 | 701 pimpl = NULL; |
0 | 702 fclose(stream); |
703 return; | |
704 } | |
705 wavinfo.SamplingRate = pimpl->decoder.sync->synth.pcm.samplerate; | |
706 wavinfo.Channels = 2; | |
707 wavinfo.DataBits = 16; | |
708 } | |
52 | 709 |
0 | 710 MP3FILE::~MP3FILE() { |
711 if (pimpl) { | |
712 FILE* s = pimpl->stream; | |
713 delete pimpl; | |
714 fclose(s); | |
715 } | |
52 | 716 pimpl = NULL; |
0 | 717 } |
52 | 718 |
0 | 719 int MP3FILE::Read(char* buf, int blksize, int blklen) { |
52 | 720 if (pimpl == NULL) return -1; |
0 | 721 pimpl->write_data = buf; |
722 pimpl->write_data_len = blksize*blklen; | |
723 pimpl->write_pointer = 0; | |
724 do { | |
725 pimpl->run(); | |
726 } while(pimpl->status != MP3FILE_impl::DONE && pimpl->write_pointer < pimpl->write_data_len); | |
727 return pimpl->write_pointer / blksize; | |
728 } | |
729 void MP3FILE::Seek(int count) { | |
730 FILE* stream = pimpl->stream; | |
731 delete pimpl; | |
52 | 732 fseek(stream, 0, SEEK_SET); |
0 | 733 pimpl = new MP3FILE_impl(stream); |
734 if (pimpl->status == MP3FILE_impl::DONE) { | |
735 delete pimpl; | |
52 | 736 pimpl = NULL; |
0 | 737 fclose(stream); |
738 return; | |
739 } | |
740 int blksize = 1; | |
741 blksize *= wavinfo.Channels * (wavinfo.DataBits/8); | |
52 | 742 pimpl->write_data = NULL; |
0 | 743 pimpl->write_data_len = count * blksize; |
744 pimpl->write_pointer = 0; | |
745 do { | |
746 pimpl->run(); | |
747 } while(pimpl->status != MP3FILE_impl::DONE && pimpl->write_pointer < pimpl->write_data_len); | |
748 return; | |
749 } | |
52 | 750 |
0 | 751 #elif USE_SMPEG |
52 | 752 #include <smpeg/smpeg.h> |
0 | 753 |
754 struct MP3FILE_impl { | |
755 SMPEG* info; | |
756 FILE* stream; | |
757 MP3FILE_impl(FILE*); | |
758 }; | |
759 | |
760 MP3FILE_impl::MP3FILE_impl(FILE* _stream) { | |
761 stream = _stream; | |
762 info = SMPEG_new_descr(fileno(stream), NULL, 0); | |
48 | 763 fprintf(stderr,"mp3 %p\n",info); |
52 | 764 if (info != NULL && SMPEG_error(info) ) info = NULL; |
0 | 765 SMPEG_enableaudio(info, 0); |
766 SMPEG_enableaudio(info, 1); | |
767 SMPEG_play(info); | |
768 } | |
769 | |
770 MP3FILE::MP3FILE(FILE* stream, int len) { | |
771 pimpl = new MP3FILE_impl(stream); | |
52 | 772 if (pimpl->info == NULL) { |
0 | 773 delete pimpl; |
774 fclose(stream); | |
775 return; | |
776 } | |
777 SDL_AudioSpec fmt; | |
778 SMPEG_wantedSpec(pimpl->info, &fmt); | |
779 wavinfo.SamplingRate = fmt.freq; | |
780 wavinfo.Channels = fmt.channels; | |
781 wavinfo.DataBits = (fmt.format == AUDIO_S8) ? 8:16; | |
782 } | |
52 | 783 |
0 | 784 MP3FILE::~MP3FILE() { |
785 if (pimpl && pimpl->info) { | |
786 if (SMPEG_status(pimpl->info) == SMPEG_PLAYING) SMPEG_stop(pimpl->info); | |
787 SMPEG_delete(pimpl->info); | |
788 } | |
789 if (pimpl) { | |
790 fclose(pimpl->stream); | |
791 delete pimpl; | |
52 | 792 pimpl = NULL; |
0 | 793 } |
794 } | |
52 | 795 |
0 | 796 int MP3FILE::Read(char* buf, int blksize, int blklen) { |
52 | 797 if (pimpl == NULL || pimpl->info == NULL) return -1; |
0 | 798 int r = SMPEG_playAudio(pimpl->info, (Uint8*)buf, blksize*blklen); |
799 if (r <= 0) { // end of file | |
800 return -1; | |
801 } | |
802 return r / blksize; | |
803 } | |
52 | 804 |
0 | 805 void MP3FILE::Seek(int count) { |
52 | 806 if (pimpl == NULL || pimpl->info == NULL) return; |
0 | 807 SMPEG_stop(pimpl->info); |
808 SMPEG_rewind(pimpl->info); | |
809 SMPEG_play(pimpl->info); | |
810 count /= 4; | |
811 count *= 4; // reduce noise; possibly SMPEG error | |
812 char* d = new char[count*channels*2]; | |
813 Read(d,count,channels*2); | |
814 delete[] d; | |
815 return; | |
816 } | |
52 | 817 |
0 | 818 #else /* SMPEG */ |
52 | 819 MP3FILE::MP3FILE(FILE* stream, int len) {pimpl = NULL;} |
0 | 820 MP3FILE::~MP3FILE(){} |
821 void MP3FILE::Seek(int count){} | |
822 int MP3FILE::Read(char* buf, int blksize, int blklen){return -1;} | |
823 #endif /* SMPEG */ |