#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>