Mercurial > otakunoraifu
diff music2/wavfile.cc @ 0:223b71206888
Initial import
author | thib |
---|---|
date | Fri, 01 Aug 2008 16:32:45 +0000 |
parents | |
children | fa8511a21d05 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/music2/wavfile.cc @@ -0,0 +1,808 @@ +/* + * wavfile.c WAV file check + * + * Copyright: wavfile.c (c) Erik de Castro Lopo erikd@zip.com.au + * + * Modified : 1997-1998 Masaki Chikama (Wren) <chikama@kasumi.ipl.mech.nagoya-u.ac.jp> + * 1998- <masaki-c@is.aist-nara.ac.jp> + * 2000- Kazunori Ueno(JAGARL) <jagarl@createor.club.ne.jp> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * +*/ + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/types.h> +#include <unistd.h> +#include <string.h> +#include "wavfile.h" +#include "system/file.h" +#include "music.h" + +#define BUFFERSIZE 1024 +#define PCM_WAVE_FORMAT 1 + +/******************************************************* +** +** WAVE Header +*/ + +inline int LittleEndian_getDW(const char *b,int index) { + int c0, c1, c2, c3; + int d0, d1; + c0 = *(const unsigned char*)(b + index + 0); + c1 = *(const unsigned char*)(b + index + 1); + c2 = *(const unsigned char*)(b + index + 2); + c3 = *(const unsigned char*)(b + index + 3); + d0 = c0 + (c1 << 8); + d1 = c2 + (c3 << 8); + return d0 + (d1 << 16); +} + +inline int LittleEndian_get3B(const char *b,int index) { + int c0, c1, c2; + c0 = *(const unsigned char*)(b + index + 0); + c1 = *(const unsigned char*)(b + index + 1); + c2 = *(const unsigned char*)(b + index + 2); + return c0 + (c1 << 8) + (c2 << 16); +} + +inline int LittleEndian_getW(const char *b,int index) { + int c0, c1; + c0 = *(const unsigned char*)(b + index + 0); + c1 = *(const unsigned char*)(b + index + 1); + return c0 + (c1 << 8); +} + +inline void LittleEndian_putW(int num, char *b, int index) { + int c0, c1; + num %= 65536; + c0 = num % 256; + c1 = num / 256; + b[index] = c0; b[index+1] = c1; +} + +typedef struct +{ u_long dwSize ; + u_short wFormatTag ; + u_short wChannels ; + u_long dwSamplesPerSec ; + u_long dwAvgBytesPerSec ; + u_short wBlockAlign ; + u_short wBitsPerSample ; +} WAVEFORMAT ; + +typedef struct +{ char RiffID [4] ; + u_long RiffSize ; + char WaveID [4] ; + char FmtID [4] ; + u_long FmtSize ; + u_short wFormatTag ; + u_short nChannels ; + u_long nSamplesPerSec ; + u_long nAvgBytesPerSec ; + u_short nBlockAlign ; + u_short wBitsPerSample ; + char DataID [4] ; + u_long nDataBytes ; +} WAVE_HEADER ; + + +static void waveFormatCopy( WAVEFORMAT* wav, char *ptr ); +static char* findchunk (char* s1, char* s2, size_t n) ; + +static int WaveHeaderCheck (char *wave_buf,int* channels, u_long* samplerate, int* samplebits, u_long* samples,u_long* datastart) +{ + static WAVEFORMAT waveformat ; + char* ptr ; + u_long databytes ; + + if (findchunk (wave_buf, "RIFF", BUFFERSIZE) != wave_buf) { + fprintf(stderr, "Bad format: Cannot find RIFF file marker"); + return WR_BADRIFF ; + } + + if (! findchunk (wave_buf, "WAVE", BUFFERSIZE)) { + fprintf(stderr, "Bad format: Cannot find WAVE file marker"); + return WR_BADWAVE ; + } + + ptr = findchunk (wave_buf, "fmt ", BUFFERSIZE) ; + + if (! ptr) { + fprintf(stderr, "Bad format: Cannot find 'fmt' file marker"); + return WR_BADFORMAT ; + } + + ptr += 4 ; /* Move past "fmt ".*/ + waveFormatCopy( &waveformat, ptr ); + + if (waveformat.dwSize != (sizeof (WAVEFORMAT) - sizeof (u_long))) { + /* fprintf(stderr, "Bad format: Bad fmt size"); */ + /* return WR_BADFORMATSIZE ; */ + } + + if (waveformat.wFormatTag != PCM_WAVE_FORMAT) { + fprintf(stderr, "Only supports PCM wave format"); + return WR_NOTPCMFORMAT ; + } + + ptr = findchunk (wave_buf, "data", BUFFERSIZE) ; + + if (! ptr) { + fprintf(stderr,"Bad format: unable to find 'data' file marker"); + return WR_NODATACHUNK ; + } + + ptr += 4 ; /* Move past "data".*/ + databytes = LittleEndian_getDW(ptr, 0); + + /* Everything is now cool, so fill in output data.*/ + + *channels = waveformat.wChannels; + *samplerate = waveformat.dwSamplesPerSec ; + *samplebits = waveformat.wBitsPerSample ; + *samples = databytes / waveformat.wBlockAlign ; + + *datastart = (u_long)(ptr) + 4; + + if (waveformat.dwSamplesPerSec != waveformat.dwAvgBytesPerSec / waveformat.wBlockAlign) { + fprintf(stderr, "Bad file format"); + return WR_BADFORMATDATA ; + } + + if (waveformat.dwSamplesPerSec != waveformat.dwAvgBytesPerSec / waveformat.wChannels / ((waveformat.wBitsPerSample == 16) ? 2 : 1)) { + fprintf(stderr, "Bad file format"); + return WR_BADFORMATDATA ; + } + + return 0 ; +} ; /* WaveHeaderCheck*/ + + +static char* findchunk (char* pstart, char* fourcc, size_t n) +{ char *pend ; + int k, test ; + + pend = pstart + n ; + + while (pstart < pend) + { + if (*pstart == *fourcc) /* found match for first char*/ + { test = 1 ; + for (k = 1 ; fourcc [k] != 0 ; k++) + test = (test ? ( pstart [k] == fourcc [k] ) : 0) ; + if (test) + return pstart ; + } ; /* if*/ + pstart ++ ; + } ; /* while lpstart*/ + + return NULL ; +} ; /* findchuck*/ + +static void waveFormatCopy( WAVEFORMAT* wav, char *ptr ) { + wav->dwSize = LittleEndian_getDW( ptr, 0 ); + wav->wFormatTag = LittleEndian_getW( ptr, 4 ); + wav->wChannels = LittleEndian_getW( ptr, 6 ); + wav->dwSamplesPerSec = LittleEndian_getDW( ptr, 8 ); + wav->dwAvgBytesPerSec = LittleEndian_getDW( ptr, 12 ); + wav->wBlockAlign = LittleEndian_getW( ptr, 16 ); + wav->wBitsPerSample = LittleEndian_getW( ptr, 18 ); +} + +static char* WavGetInfo(WAVFILE* wfile, char *data) { + int e; /* Saved errno value */ + int channels; /* Channels recorded in this wav file */ + u_long samplerate; /* Sampling rate */ + int sample_bits; /* data bit size (8/12/16) */ + u_long samples; /* The number of samples in this file */ + u_long datastart; /* The offset to the wav data */ + + if ( (e = WaveHeaderCheck(data, + &channels,&samplerate, + &sample_bits,&samples,&datastart) != 0 )) { + fprintf(stderr,"WavGetInfo(): Reading WAV header\n"); + return 0; + } + + /* + * Copy WAV data over to WAVFILE struct: + */ + wfile->wavinfo.Channels = channels; + + wfile->wavinfo.SamplingRate = (unsigned int) samplerate; + wfile->wavinfo.DataBits = (unsigned short) sample_bits; + + return (char *) datastart; +} + +/************************************************************: +** +** WAVFILE stream reader +*/ + +#include<SDL_mixer.h> +WAVFILE::WAVFILE(void) { + wavinfo.SamplingRate=0; + wavinfo.Channels=1; + wavinfo.DataBits=0; +} + +int WAVFILE_Stream::Read(char* in_buf, int blksize, int length) { + /* ファイルの読み込み */ + if (data_length == 0 && stream_length == 0) return -1; + /* wf->data にデータの残りがあればそれも読み込む */ + if (data_length > blksize*length) { + memcpy(in_buf, data, blksize*length); + data += blksize * length; + data_length -= blksize * length; + return length; + } + memcpy(in_buf, data, data_length); + if (stream_length != -1 && stream_length < blksize*length-data_length) { + length = (stream_length+data_length+blksize-1)/blksize; + } + int read_len = 0; + if (blksize*length-data_length > 0) { + read_len = fread(in_buf+data_length, 1, blksize*length-data_length, stream); + if (stream_length != -1 && stream_length > read_len) stream_length -= read_len; + if (feof(stream)) stream_length = 0; // end of file + } else { + stream_length = 0; // all data were read + } + int blklen = (read_len + data_length) / blksize; + data_length = 0; + return blklen; +} +void WAVFILE_Stream::Seek(int count) { + int blksize = 1; + /* block size の設定 */ + blksize *= wavinfo.Channels * (wavinfo.DataBits/8); + data_length = 0; + stream_length = stream_length_orig - stream_top - count*blksize; + fseek(stream, count*blksize+stream_top, 0); +} +WAVFILE_Stream::WAVFILE_Stream(FILE* _stream, int _length) { + stream = _stream; + stream_length = _length; + stream_length_orig = _length; + data_orig = new char[1024]; + data = data_orig; + data_length = 1024; + if (stream_length != -1 && stream_length < data_length) { + data_length = stream_length; + } + fread(data, data_length, 1, stream); + if (stream_length != -1) + stream_length -= data_length; + data = WavGetInfo(this, data); + if (data == 0) { + stream_length = 0; + data_length = 0; + return; + } + stream_top = data - data_orig; + data_length -= data - data_orig; + return; +} +WAVFILE_Stream::~WAVFILE_Stream() { + if (data_orig) delete data_orig; + if (stream) fclose(stream); + return; +} +/************************************************************: +** +** WAVE format converter with SDL_audio +*/ +WAVFILE* WAVFILE::MakeConverter(WAVFILE* new_reader) { + bool need = false; + if (new_reader->wavinfo.SamplingRate != freq) need = true; + if (new_reader->wavinfo.Channels != channels) need = true; + if (format == AUDIO_S8) { + if (new_reader->wavinfo.DataBits != 8) need = true; + } else if (format == AUDIO_S16) { + if (new_reader->wavinfo.DataBits != 16) need = true; + } else { + need = true; + } + if (!need) return new_reader; + /* 変換もとのフォーマットを得る */ + int from_format; + if (new_reader->wavinfo.DataBits == 8) from_format = AUDIO_S8; + else from_format = AUDIO_S16; + SDL_AudioCVT* cvt = new SDL_AudioCVT; + int ret = SDL_BuildAudioCVT(cvt, from_format, new_reader->wavinfo.Channels, freq, + format, 2, freq); + if (ret == -1) { + delete cvt; + fprintf(stderr,"Cannot make wave file converter!!!\n"); + return new_reader; + } + WAVFILE_Converter* conv = new WAVFILE_Converter(new_reader, cvt); + return conv; +} +WAVFILE_Converter::WAVFILE_Converter(WAVFILE* _orig, SDL_AudioCVT* _cvt) { + original = _orig; + cvt = _cvt; + //datasize = 4096*4; + datasize = 48000; + cvt->buf = new Uint8[datasize*cvt->len_mult]; + cvt->len = 0; + tmpbuf = new char[datasize*cvt->len_mult + 1024]; + memset(tmpbuf, 0, datasize*cvt->len_mult+1024); +}; + +static int conv_wave_rate(short* in_buf, int length, int in_rate, int out_rate, char* tmpbuf); +WAVFILE_Converter::~WAVFILE_Converter() { + if (cvt) { + if (cvt->buf) delete cvt->buf; + delete cvt; + cvt = 0; + } + if (original) delete original; + original = 0; +} +int WAVFILE_Converter::Read(char* buf, int blksize, int blklen) { + if (original == 0 || cvt == 0) return -1; + int copied_length = 0; + if (cvt->len < blksize*blklen) { + memcpy(buf, cvt->buf, cvt->len); + copied_length += cvt->len; + do { + int cnt = original->Read((char*)cvt->buf, 1, datasize); + if (cnt <= 0) { + cvt->len = 0; + break; + } + cvt->len = cnt; + SDL_ConvertAudio(cvt); + if (freq < original->wavinfo.SamplingRate) { // rate conversion は SDL_ConvertAudio ではうまく行かない + // 48000Hz -> 44100Hz or 22050Hz などを想定 + // 長さは短くなるはずなので、特に処理はなし + cvt->len = conv_wave_rate( (short*)(cvt->buf), cvt->len_cvt/4, original->wavinfo.SamplingRate, freq, tmpbuf); + cvt->len *= 4; + } else { + cvt->len = cvt->len_cvt; + } + if (cvt->len+copied_length > blksize*blklen) break; + memcpy(buf+copied_length, cvt->buf, cvt->len); + copied_length += cvt->len; + } while(1); + } + if (cvt->len == 0 && copied_length == 0) return -1; + else if (cvt->len > 0) { + int len = blksize * blklen - copied_length; + memcpy(buf+copied_length, cvt->buf, len); + memmove(cvt->buf, cvt->buf+len, cvt->len-len); + copied_length += len; + cvt->len -= len; + } + return copied_length / blksize; +} +/* format は signed, 16bit, little endian, stereo と決めうち +** 場合によっていは big endian になることもあるかも。 +*/ +static int conv_wave_rate(short* in_buf, int length, int in_rate, int out_rate, char* tmpbuf) { + int input_rate = in_rate; + int output_rate = out_rate; + double input_rate_d = input_rate, output_rate_d = output_rate; + double dtime; int outlen; short* out, * out_orig; int next_sample1, next_sample2; + short* in_buf_orig = in_buf; + int i; int time; + + if (input_rate == output_rate) return length; + if (length <= 0) return 0; + /* 一般の周波数変換:線型補完 */ + int& first_flag = *(int*)(tmpbuf); + int& prev_time = *(int*)(tmpbuf+4); + int& prev_sample1 = *(int*)(tmpbuf+8); + int& prev_sample2 = *(int*)(tmpbuf+12); + out = (short*)(tmpbuf+16); + /* 初めてならデータを初期化 */ + if (first_flag == 0) { + first_flag = 1; + prev_time = 0; + prev_sample1 = short(read_little_endian_short((char*)(in_buf++))); + prev_sample2 = short(read_little_endian_short((char*)(in_buf++))); + length--; + } + /* 今回作成するデータ量を得る */ + dtime = prev_time + length * output_rate_d; + outlen = (int)(dtime / input_rate_d); + out_orig = out; + if (first_flag == 1) { + write_little_endian_short((char*)out, prev_sample1); + out++; + write_little_endian_short((char*)out, prev_sample2); + out++; + } + dtime -= input_rate_d*outlen; /* 次の prev_time */ + + time=0; + next_sample1 = short(read_little_endian_short((char*)(in_buf++))); + next_sample2 = short(read_little_endian_short((char*)(in_buf++))); + for (i=0; i<outlen; i++) { + /* double で計算してみたけどそう簡単には高速化は無理らしい */ + /* なお、変換は 1分のデータに1秒程度かかる(Celeron 700MHz) */ + time += input_rate; + while(time-prev_time>output_rate) { + prev_sample1 = next_sample1; + next_sample1 = short(read_little_endian_short((char*)(in_buf++))); + prev_sample2 = next_sample2; + next_sample2 = short(read_little_endian_short((char*)(in_buf++))); + prev_time += output_rate; + } + write_little_endian_short((char*)out, + ((time-prev_time)*next_sample1 + + (input_rate-time+prev_time)*prev_sample1) / input_rate); + out++; + write_little_endian_short((char*)out, + ((time-prev_time)*next_sample2 + + (input_rate-time+prev_time)*prev_sample2) / input_rate); + *out++; + } + prev_time += output_rate; prev_time -= input_rate * outlen; + prev_sample1 = next_sample1; prev_sample2 = next_sample2; + if (first_flag == 1) { + outlen++; first_flag = 2; + } + memcpy(in_buf_orig, out_orig, outlen*2*sizeof(short)); + return outlen; +} + + +/************************************************************: +** +** MP3FILE stream reader +*/ + +int WAVFILE::freq = 48000; +int WAVFILE::channels = 2; +int WAVFILE::format = MIX_DEFAULT_FORMAT; + +#if HAVE_LIBMAD + +#include<mad.h> +#define MPEG_BUFSZ 40000 /* 2.5 s at 128 kbps; 1 s at 320 kbps */ +struct MP3FILE_impl { + enum { PREPARE, RUN, WRITE, DONE} status; + struct mad_decoder decoder; + char* data; + int data_len; + char* write_data; + unsigned int write_data_len; + unsigned int write_pointer; + unsigned int src_pointer; + FILE* stream; + MP3FILE_impl(FILE*); + ~MP3FILE_impl(); + static enum mad_flow callback_read(void *data, struct mad_stream *stream); + static enum mad_flow callback_error(void *data, struct mad_stream *stream, struct mad_frame *frame); + static enum mad_flow callback_write(void *data, struct mad_header const *header, struct mad_pcm *pcm); + enum mad_flow callback_write_impl(struct mad_pcm *pcm); + void run(void); +}; + +MP3FILE_impl::MP3FILE_impl(FILE* _stream) { + stream = _stream; + data = new char[MPEG_BUFSZ]; + data_len = 0; + src_pointer = 0; + write_data = 0; + write_data_len = 0; + write_pointer = 0; + + /* initialize decoder */ + mad_decoder_init(&decoder, (void*)this, callback_read, 0 /* header */, 0 /* filter */, callback_write, + callback_error, 0 /* message */); + /* prepare stream */ + status = PREPARE; + *(void**)(&decoder.sync) = malloc(sizeof(*decoder.sync)); + + mad_stream_init(&decoder.sync->stream); + mad_frame_init(&decoder.sync->frame); + mad_synth_init(&decoder.sync->synth); + + mad_stream_options(&decoder.sync->stream, decoder.options); + + while(status != WRITE && status != DONE) run(); +} +MP3FILE_impl::~MP3FILE_impl() { + free(decoder.sync); + mad_decoder_finish(&decoder); + delete[] data; + return; +} + +void MP3FILE_impl::run(void) { + if (status == DONE) return; + struct mad_stream *stream = &decoder.sync->stream; + struct mad_frame *frame = &decoder.sync->frame; + struct mad_synth *synth = &decoder.sync->synth; + if (status == PREPARE) { + switch (decoder.input_func(decoder.cb_data, stream)) { + case MAD_FLOW_STOP: + case MAD_FLOW_BREAK: + goto done; + case MAD_FLOW_CONTINUE: + status = RUN; + case MAD_FLOW_IGNORE: + break; + } + return; + } + if (status == RUN) { + if (mad_frame_decode(frame, stream) == -1) { + if (!MAD_RECOVERABLE(stream->error)) { + status = PREPARE; + return; + } + switch (decoder.error_func((void*)this, stream, frame)) { + case MAD_FLOW_STOP: + case MAD_FLOW_BREAK: + goto done; + case MAD_FLOW_IGNORE: + status = PREPARE; + return; + case MAD_FLOW_CONTINUE: + default: + return; + } + } + + mad_synth_frame(synth, frame); + src_pointer = 0; + status = WRITE; + return; + } + if (status == WRITE) { + switch (decoder.output_func(decoder.cb_data, &frame->header, &synth->pcm)) { + case MAD_FLOW_STOP: + case MAD_FLOW_BREAK: + goto done; + case MAD_FLOW_IGNORE: + return; + case MAD_FLOW_CONTINUE: + status = RUN; + break; + } + if (stream->error == MAD_ERROR_BUFLEN) { + stream->error = MAD_ERROR_NONE; + status = PREPARE; + } + return; + } +done: + status = DONE; + mad_synth_finish(&decoder.sync->synth); + mad_frame_finish(&decoder.sync->frame); + mad_stream_finish(&decoder.sync->stream); + return; +} + +enum mad_flow MP3FILE_impl::callback_read(void *data, struct mad_stream *stream) +{ + MP3FILE_impl* impl = (MP3FILE_impl*)data; + if (stream->next_frame) { + impl->data_len -= (char*)stream->next_frame - impl->data; + memmove(impl->data, (char*)stream->next_frame, impl->data_len); + } else { + impl->data_len = 0; + } + int count; + if (feof(impl->stream)) { + if (stream->next_frame && (char*)stream->next_frame - impl->data > 0) { + // There is under processing data + count = 0; + } else { + // all data were processed + return MAD_FLOW_STOP; + } + } else { + count = fread(impl->data + impl->data_len, 1, MPEG_BUFSZ-impl->data_len, impl->stream); + if (count <= 0) { + return MAD_FLOW_BREAK; + } + } + impl->data_len += count; + if (impl->data_len < MPEG_BUFSZ) { + memset(impl->data + impl->data_len, 0, MPEG_BUFSZ-impl->data_len); + } + mad_stream_buffer(stream, (unsigned char*)impl->data, impl->data_len); + return MAD_FLOW_CONTINUE; +} + +enum mad_flow MP3FILE_impl::callback_error(void *data, struct mad_stream *stream, struct mad_frame *frame) +{ + MP3FILE_impl* impl = (MP3FILE_impl*)data; + fprintf(stdout, "decoding error 0x%04x (%s) at byte offset %u\n", + stream->error, mad_stream_errorstr(stream), + ftell(impl->stream) - ((impl->data+impl->data_len)-(char*)stream->this_frame)); + /* return MAD_FLOW_BREAK here to stop decoding (and propagate an error) */ + return MAD_FLOW_CONTINUE; +} +signed int scale(mad_fixed_t sample) +{ + /* round */ + sample += (1L << (MAD_F_FRACBITS - 16)); + + /* clip */ + if (sample >= MAD_F_ONE) + sample = MAD_F_ONE - 1; + else if (sample < -MAD_F_ONE) + sample = -MAD_F_ONE; + + /* quantize */ + return sample >> (MAD_F_FRACBITS + 1 - 16); +} +enum mad_flow MP3FILE_impl::callback_write(void *data, struct mad_header const *header, struct mad_pcm *pcm) +{ + MP3FILE_impl* pimpl = (MP3FILE_impl*)data; + return pimpl->callback_write_impl(pcm); +} +enum mad_flow MP3FILE_impl::callback_write_impl(struct mad_pcm *pcm) +{ + if (write_data_len == 0) return MAD_FLOW_IGNORE; + mad_fixed_t const *left_ch = pcm->samples[0] + src_pointer; + mad_fixed_t const *right_ch = pcm->samples[1] + src_pointer; + + unsigned int nchannels = pcm->channels; + unsigned int nsamples = pcm->length - src_pointer; + if (write_pointer + nsamples * nchannels * 2 > write_data_len) { + nsamples = (write_data_len - write_pointer) / nchannels / 2; + } + write_data_len &= ~(nchannels*2-1); /* write_data_len はあらかじめ丸めておく */ + src_pointer += nsamples; + if (write_data == 0) { // skip data write + write_pointer += nsamples*2*2; + } else while(nsamples--) { + signed int sample = scale(*left_ch++); + write_data[write_pointer++] = sample & 0xff; + write_data[write_pointer++] = (sample>>8) & 0xff; + if (nchannels == 2) { + sample = scale(*right_ch++); + } + write_data[write_pointer++] = sample & 0xff; + write_data[write_pointer++] = (sample>>8) & 0xff; + } + if (write_pointer >= write_data_len) return MAD_FLOW_IGNORE; + else return MAD_FLOW_CONTINUE; +} + +MP3FILE::MP3FILE(FILE* stream, int len) { + pimpl = new MP3FILE_impl(stream); + if (pimpl->status == MP3FILE_impl::DONE) { + delete pimpl; + pimpl = 0; + fclose(stream); + return; + } + wavinfo.SamplingRate = pimpl->decoder.sync->synth.pcm.samplerate; + wavinfo.Channels = 2; + wavinfo.DataBits = 16; +} +MP3FILE::~MP3FILE() { + if (pimpl) { + FILE* s = pimpl->stream; + delete pimpl; + fclose(s); + } + pimpl = 0; +} +int MP3FILE::Read(char* buf, int blksize, int blklen) { + if (pimpl == 0) return -1; + pimpl->write_data = buf; + pimpl->write_data_len = blksize*blklen; + pimpl->write_pointer = 0; + do { + pimpl->run(); + } while(pimpl->status != MP3FILE_impl::DONE && pimpl->write_pointer < pimpl->write_data_len); + return pimpl->write_pointer / blksize; +} +void MP3FILE::Seek(int count) { + FILE* stream = pimpl->stream; + delete pimpl; + fseek(stream,0,0); + pimpl = new MP3FILE_impl(stream); + if (pimpl->status == MP3FILE_impl::DONE) { + delete pimpl; + pimpl = 0; + fclose(stream); + return; + } + int blksize = 1; + blksize *= wavinfo.Channels * (wavinfo.DataBits/8); + pimpl->write_data = 0; + pimpl->write_data_len = count * blksize; + pimpl->write_pointer = 0; + do { + pimpl->run(); + } while(pimpl->status != MP3FILE_impl::DONE && pimpl->write_pointer < pimpl->write_data_len); + return; +} +#elif USE_SMPEG +#include<smpeg/smpeg.h> + +struct MP3FILE_impl { + SMPEG* info; + FILE* stream; + MP3FILE_impl(FILE*); +}; + +MP3FILE_impl::MP3FILE_impl(FILE* _stream) { + stream = _stream; + info = SMPEG_new_descr(fileno(stream), NULL, 0); +fprintf(stderr,"mp3 %x\n",info); + if (info && SMPEG_error(info) ) info = 0; + SMPEG_enableaudio(info, 0); + SMPEG_enableaudio(info, 1); + SMPEG_play(info); +} + +MP3FILE::MP3FILE(FILE* stream, int len) { + pimpl = new MP3FILE_impl(stream); + if (pimpl->info == 0) { + delete pimpl; + fclose(stream); + return; + } + SDL_AudioSpec fmt; + SMPEG_wantedSpec(pimpl->info, &fmt); + wavinfo.SamplingRate = fmt.freq; + wavinfo.Channels = fmt.channels; + wavinfo.DataBits = (fmt.format == AUDIO_S8) ? 8:16; +} +MP3FILE::~MP3FILE() { + if (pimpl && pimpl->info) { + if (SMPEG_status(pimpl->info) == SMPEG_PLAYING) SMPEG_stop(pimpl->info); + SMPEG_delete(pimpl->info); + } + if (pimpl) { + fclose(pimpl->stream); + delete pimpl; + pimpl = 0; + } +} +int MP3FILE::Read(char* buf, int blksize, int blklen) { + if (pimpl == 0 || pimpl->info == 0) return -1; + int r = SMPEG_playAudio(pimpl->info, (Uint8*)buf, blksize*blklen); + if (r <= 0) { // end of file + return -1; + } + return r / blksize; +} +void MP3FILE::Seek(int count) { + if (pimpl == 0 || pimpl->info == 0) return; + SMPEG_stop(pimpl->info); + SMPEG_rewind(pimpl->info); + SMPEG_play(pimpl->info); + count /= 4; + count *= 4; // reduce noise; possibly SMPEG error + char* d = new char[count*channels*2]; + Read(d,count,channels*2); + delete[] d; + return; +} +#else /* SMPEG */ +MP3FILE::MP3FILE(FILE* stream, int len) {pimpl = 0;} +MP3FILE::~MP3FILE(){} +void MP3FILE::Seek(int count){} +int MP3FILE::Read(char* buf, int blksize, int blklen){return -1;} +#endif /* SMPEG */