File:  [RetroPC.NET] / xmil / sound / sound.c
Revision 1.6: download - view: text, annotated - select for diffs
Tue Jun 3 05:07:32 2008 JST (17 years, 4 months ago) by yui
Branches: MAIN
CVS tags: HEAD
change to c style comment

#include	"compiler.h"

#if !defined(DISABLE_SOUND)

#include	"wavefile.h"
#include	"dosio.h"
#include	"soundmng.h"
#include	"z80core.h"
#include	"pccore.h"
#include	"sound.h"
#include	"sndcsec.h"
/* #include	"getsnd.h" */


	SOUNDCFG	soundcfg;


#define	STREAM_CBMAX	4

typedef struct {
	void	*hdl;
	SOUNDCB	cbfn;
} CBTBL;

typedef struct {
	SINT32	*buffer;
	SINT32	*ptr;
	UINT	samples;
	UINT	reserve;
	UINT	remain;
#if defined(SUPPORT_WAVEREC)
	WAVEWR	rec;
#endif
	CBTBL	*cbreg;
	CBTBL	cb[STREAM_CBMAX];
} SNDSTREAM;

static	SNDSTREAM	sndstream;


static void streamreset(void) {

	SNDCSEC_ENTER;
	sndstream.ptr = sndstream.buffer;
	sndstream.remain = sndstream.samples + sndstream.reserve;
	sndstream.cbreg = sndstream.cb;
	SNDCSEC_LEAVE;
}

static void streamprepare(UINT samples) {

	CBTBL	*cb;
	UINT	count;

	count = min(sndstream.remain, samples);
	if (count) {
		ZeroMemory(sndstream.ptr, count * 2 * sizeof(SINT32));
		cb = sndstream.cb;
		while(cb < sndstream.cbreg) {
			cb->cbfn(cb->hdl, sndstream.ptr, count);
			cb++;
		}
		sndstream.ptr += count * 2;
		sndstream.remain -= count;
	}
}


#if defined(SUPPORT_WAVEREC)
/* wave rec */

BRESULT sound_recstart(const OEMCHAR *filename) {

	WAVEWR	rec;

	sound_recstop();
	if (sndstream.buffer == NULL) {
		return(FAILURE);
	}
	rec = wavewr_open(filename, soundcfg.rate, 16, 2);
	sndstream.rec = rec;
	if (rec) {
		return(SUCCESS);
	}
	return(FAILURE);
}

void sound_recstop(void) {

	WAVEWR	rec;

	rec = sndstream.rec;
	sndstream.rec = NULL;
	wavewr_close(rec);
}

static void streamfilewrite(UINT samples) {

	CBTBL	*cb;
	UINT	count;
	SINT32	buf32[2*512];
	BYTE	buf[2*2*512];
	UINT	r;
	UINT	i;
	SINT32	samp;

	while(samples) {
		count = min(samples, 512);
		ZeroMemory(buf32, count * 2 * sizeof(SINT32));
		cb = sndstream.cb;
		while(cb < sndstream.cbreg) {
			cb->cbfn(cb->hdl, buf32, count);
			cb++;
		}
		r = min(sndstream.remain, count);
		if (r) {
			CopyMemory(sndstream.ptr, buf32, r * 2 * sizeof(SINT32));
			sndstream.ptr += r * 2;
			sndstream.remain -= r;
		}
		for (i=0; i<count*2; i++) {
			samp = buf32[i];
			if (samp > 32767) {
				samp = 32767;
			}
			else if (samp < -32768) {
				samp = -32768;
			}
			/* little endianなので satuation_s16は使えない */
			buf[i*2+0] = (UINT8)samp;
			buf[i*2+1] = (UINT8)(samp >> 8);
		}
		wavewr_write(sndstream.rec, buf, count * 4);
		samples -= count;
	}
}

static void filltailsample(UINT count) {

	SINT32	*ptr;
	UINT	orgsize;
	SINT32	sampl;
	SINT32	sampr;

	count = min(sndstream.remain, count);
	if (count) {
		ptr = sndstream.ptr;
		orgsize = (ptr - sndstream.buffer) / 2;
		if (orgsize == 0) {
			sampl = 0;
			sampr = 0;
		}
		else {
			sampl = *(ptr - 2);
			sampr = *(ptr - 1);
		}
		sndstream.ptr += count * 2;
		sndstream.remain -= count;
		do {
			ptr[0] = sampl;
			ptr[1] = sampr;
			ptr += 2;
		} while(--count);
	}
}
#endif


/* ---- */

BRESULT sound_create(UINT rate, UINT ms) {

	UINT	samples;
	UINT	reserve;

	ZeroMemory(&sndstream, sizeof(sndstream));
	samples = soundmng_create(rate, ms);
	if (samples == 0) {
		goto scre_err1;
	}
	soundmng_reset();

	soundcfg.rate = rate;

#if defined(SOUNDRESERVE)
	reserve = rate * SOUNDRESERVE / 1000;
#else
	reserve = 0;
#endif
	sndstream.buffer = (SINT32 *)_MALLOC((samples + reserve) * 2 
												* sizeof(SINT32), "stream");
	if (sndstream.buffer == NULL) {
		goto scre_err2;
	}
	sndstream.samples = samples;
	sndstream.reserve = reserve;

	SNDCSEC_INIT;
	streamreset();
	return(SUCCESS);

scre_err2:
	soundmng_destroy();

scre_err1:
	return(FAILURE);
}

void sound_destroy(void) {

	if (sndstream.buffer) {
#if defined(SUPPORT_WAVEREC)
		sound_recstop();
#endif
		soundmng_stop();
		streamreset();
		soundmng_destroy();
		SNDCSEC_TERM;
		_MFREE(sndstream.buffer);
		sndstream.buffer = NULL;
	}
}

void sound_reset(void) {

	if (sndstream.buffer) {
		soundmng_reset();
		streamreset();
		soundcfg.lastclock = CPU_CLOCK;
	}
}

void sound_changeclock(void) {

	UINT32	clock;
	UINT	hz;
	UINT	hzmax;

	if (sndstream.buffer == NULL) {
		return;
	}

	/* とりあえず 25で割り切れる。 */
	clock = pccore.realclock / 25;
	hz = soundcfg.rate / 25;

	/* で、クロック数に合せて調整。(64bit演算しろよな的) */
	hzmax = (1 << (32 - 8)) / (clock >> 8);
	while(hzmax < hz) {
		clock = (clock + 1) >> 1;
		hz = (hz + 1) >> 1;
	}
	TRACEOUT(("hzbase/clockbase = %d/%d", hz, clock));
	soundcfg.hzbase = hz;
	soundcfg.clockbase = clock;
	soundcfg.minclock = 2 * clock / hz;
	soundcfg.lastclock = CPU_CLOCK;
}

void sound_streamregist(void *hdl, SOUNDCB cbfn) {

	if (sndstream.buffer) {
		if ((cbfn) &&
			(sndstream.cbreg < (sndstream.cb + STREAM_CBMAX))) {
			sndstream.cbreg->hdl = hdl;
			sndstream.cbreg->cbfn = cbfn;
			sndstream.cbreg++;
		}
	}
}


/* ---- */

void sound_sync(void) {

	UINT32	length;

	if (sndstream.buffer == NULL) {
		return;
	}
	length = CPU_CLOCK + CPU_BASECLOCK - CPU_REMCLOCK - soundcfg.lastclock;
	if (length < soundcfg.minclock) {
		return;
	}
	length = (length * soundcfg.hzbase) / soundcfg.clockbase;
	if (length == 0) {
		return;
	}
	SNDCSEC_ENTER;
#if defined(SUPPORT_WAVEREC)
	if (sndstream.rec) {
		streamfilewrite(length);
	}
	else
#endif
	streamprepare(length);
	soundcfg.lastclock += length * soundcfg.clockbase / soundcfg.hzbase;
	SNDCSEC_LEAVE;

	soundcfg.writecount += length;
	if (soundcfg.writecount >= 100) {
		soundcfg.writecount = 0;
		soundmng_sync();
	}
}


static volatile int locks = 0;

const SINT32 *sound_pcmlock(void) {

const SINT32 *ret;

	if (locks) {
		/* TRACEOUT(("sound pcm lock: already locked")); */
		return(NULL);
	}
	locks++;
	ret = sndstream.buffer;
	if (ret) {
		SNDCSEC_ENTER;
		if (sndstream.remain > sndstream.reserve)
#if defined(SUPPORT_WAVEREC)
			if (sndstream.rec) {
				filltailsample(sndstream.remain - sndstream.reserve);
			}
			else
#endif
		{
			streamprepare(sndstream.remain - sndstream.reserve);
			soundcfg.lastclock = CPU_CLOCK + CPU_BASECLOCK - CPU_REMCLOCK;
		}
	}
	else {
		locks--;
	}
	return(ret);
}

void sound_pcmunlock(const SINT32 *hdl) {

	int		leng;

	if (hdl) {
		leng = sndstream.reserve - sndstream.remain;
		if (leng > 0) {
			CopyMemory(sndstream.buffer,
						sndstream.buffer + (sndstream.samples * 2),
												leng * 2 * sizeof(SINT32));
		}
		sndstream.ptr = sndstream.buffer + (leng * 2);
		sndstream.remain = sndstream.samples + sndstream.reserve - leng;
		/* sndstream.remain += sndstream.samples; */
		SNDCSEC_LEAVE;
		locks--;
	}
}

#endif


RetroPC.NET-CVS <cvs@retropc.net>