view 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
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) == NULL) {
		fprintf(stderr, "Bad format: Cannot find WAVE file marker");
		return  WR_BADWAVE ;
	}

	ptr = findchunk(wave_buf, "fmt ", BUFFERSIZE) ;

	if (ptr == NULL) {
		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 == NULL) {
		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 NULL;
	}

	/*
	 * 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, SEEK_SET);
}

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;
}

WAVFILE_Stream::~WAVFILE_Stream() {
	if (data_orig) delete data_orig;
	if (stream) fclose(stream);
}

/************************************************************:
**
**	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 != NULL) {
		if (cvt->buf) delete[] cvt->buf;
		delete cvt;
		cvt = NULL;
	}
	delete[] tmpbuf;
	if (original) delete original;
	original = NULL;
}

int WAVFILE_Converter::Read(char* buf, int blksize, int blklen) {
	if (original == NULL || cvt == NULL) 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, 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 = NULL;
	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;
}

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 == NULL) { // 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 = NULL;
		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 = NULL;
}

int MP3FILE::Read(char* buf, int blksize, int blklen) {
	if (pimpl == NULL) 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, SEEK_SET);
	pimpl = new MP3FILE_impl(stream);
	if (pimpl->status == MP3FILE_impl::DONE) {
		delete pimpl;
		pimpl = NULL;
		fclose(stream);
		return;
	}
	int blksize = 1;
	blksize *= wavinfo.Channels * (wavinfo.DataBits/8);
	pimpl->write_data = NULL;
	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 != NULL && SMPEG_error(info) ) info = NULL;
	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 == NULL) {
		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 = NULL;
	}
}

int MP3FILE::Read(char* buf, int blksize, int blklen) {
	if (pimpl == NULL || pimpl->info == NULL) 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 == NULL || pimpl->info == NULL) 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 = NULL;}
MP3FILE::~MP3FILE(){}
void MP3FILE::Seek(int count){}
int MP3FILE::Read(char* buf, int blksize, int blklen){return -1;}
#endif /* SMPEG */