Mercurial > otakunoraifu
annotate music2/wavfile.cc @ 67:419761c8d9b9
Add configure option to build scn2kdump.
author | Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> |
---|---|
date | Wed, 23 Feb 2011 01:48:51 +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 */ |