Mercurial > otakunoraifu
view music2/wavfile.cc @ 51:cbb301016a4e
* oops... finishing what was started and not said with the other patch: fixing memory leaks
author | thib |
---|---|
date | Fri, 17 Apr 2009 18:40:39 +0000 |
parents | ed6c21dde840 |
children | 15a18fbe6f21 |
line wrap: on
line source
/* * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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, const 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, const 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; } delete[] tmpbuf; 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 %p\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 */