mirror of
https://github.com/libretro/NP2kai.git
synced 2024-10-07 14:53:33 +00:00
418 lines
7.7 KiB
C
418 lines
7.7 KiB
C
/**
|
||
* @file sound.c
|
||
* @brief Implementation of the sound
|
||
*/
|
||
|
||
#include "compiler.h"
|
||
#include "sound.h"
|
||
#include "cpucore.h"
|
||
#include "pccore.h"
|
||
#include "iocore.h"
|
||
#include "sndcsec.h"
|
||
#include "beep.h"
|
||
#include "soundmng.h"
|
||
#if defined(SUPPORT_WAVEREC)
|
||
#include "common/wavefile.h"
|
||
#endif /* defined(SUPPORT_WAVEREC) */
|
||
|
||
SOUNDCFG soundcfg;
|
||
|
||
|
||
#define STREAM_CBMAX 16
|
||
|
||
typedef struct {
|
||
void *hdl;
|
||
SOUNDCB cbfn;
|
||
} CBTBL;
|
||
|
||
typedef struct {
|
||
SINT32 *buffer;
|
||
SINT32 *ptr;
|
||
UINT samples;
|
||
UINT reserve;
|
||
UINT remain;
|
||
#if defined(SUPPORT_WAVEREC)
|
||
WAVEFILEH rec;
|
||
#endif /* defined(SUPPORT_WAVEREC) */
|
||
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 = np2min(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
|
||
|
||
/**
|
||
* Starts recording
|
||
* @param[in] lpFilename The filename
|
||
* @retval SUCCESS If succeeded
|
||
* @retval FAILURE If failed
|
||
*/
|
||
BRESULT sound_recstart(const OEMCHAR *lpFilename)
|
||
{
|
||
WAVEFILEH rec;
|
||
|
||
sound_recstop();
|
||
if (sndstream.buffer == NULL)
|
||
{
|
||
return FAILURE;
|
||
}
|
||
rec = wavefile_create(lpFilename, soundcfg.rate, 16, 2);
|
||
sndstream.rec = rec;
|
||
if (rec)
|
||
{
|
||
return SUCCESS;
|
||
}
|
||
return FAILURE;
|
||
}
|
||
|
||
/**
|
||
* Stops recording
|
||
*/
|
||
void sound_recstop(void)
|
||
{
|
||
WAVEFILEH rec;
|
||
|
||
rec = sndstream.rec;
|
||
sndstream.rec = NULL;
|
||
wavefile_close(rec);
|
||
}
|
||
|
||
/**
|
||
* is recording?
|
||
* @retval TRUE Yes
|
||
*/
|
||
BOOL sound_isrecording(void)
|
||
{
|
||
return (sndstream.rec != NULL) ? TRUE : FALSE;
|
||
}
|
||
|
||
/**
|
||
* write
|
||
* @param[in] samples The count of samples
|
||
*/
|
||
static void streamfilewrite(UINT nSamples)
|
||
{
|
||
UINT nCount;
|
||
SINT32 buf32[2 * 512];
|
||
CBTBL *cb;
|
||
UINT8 buf[2 * 512][2];
|
||
UINT r;
|
||
UINT i;
|
||
SINT32 nSample;
|
||
|
||
while (nSamples)
|
||
{
|
||
nCount = np2min(nSamples, 512);
|
||
memset(buf32, 0, nCount * 2 * sizeof(buf32[0]));
|
||
cb = sndstream.cb;
|
||
while (cb < sndstream.cbreg)
|
||
{
|
||
cb->cbfn(cb->hdl, buf32, nCount);
|
||
cb++;
|
||
}
|
||
r = np2min(sndstream.remain, nCount);
|
||
if (r)
|
||
{
|
||
memcpy(sndstream.ptr, buf32, r * 2 * sizeof(buf32[0]));
|
||
sndstream.ptr += r * 2;
|
||
sndstream.remain -= r;
|
||
}
|
||
for (i = 0; i < nCount * 2; i++)
|
||
{
|
||
nSample = buf32[i];
|
||
if (nSample > 32767)
|
||
{
|
||
nSample = 32767;
|
||
}
|
||
else if (nSample < -32768)
|
||
{
|
||
nSample = -32768;
|
||
}
|
||
// little endian<61>Ȃ̂<C882> satuation_s16<31>͎g<CD8E><67><EFBFBD>Ȃ<EFBFBD>
|
||
buf[i][0] = (UINT8)nSample;
|
||
buf[i][1] = (UINT8)(nSample >> 8);
|
||
}
|
||
wavefile_write(sndstream.rec, buf, nCount * 2 * sizeof(buf[0]));
|
||
nSamples -= nCount;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* fill
|
||
* @param[in] samples The count of samples
|
||
*/
|
||
static void filltailsample(UINT nCount)
|
||
{
|
||
SINT32 *ptr;
|
||
UINT nOrgSize;
|
||
SINT32 nSampleL;
|
||
SINT32 nSampleR;
|
||
|
||
nCount = np2min(sndstream.remain, nCount);
|
||
if (nCount)
|
||
{
|
||
ptr = sndstream.ptr;
|
||
nOrgSize = (UINT)((ptr - sndstream.buffer) / 2);
|
||
if (nOrgSize == 0)
|
||
{
|
||
nSampleL = 0;
|
||
nSampleR = 0;
|
||
}
|
||
else
|
||
{
|
||
nSampleL = *(ptr - 2);
|
||
nSampleR = *(ptr - 1);
|
||
}
|
||
sndstream.ptr += nCount * 2;
|
||
sndstream.remain -= nCount;
|
||
do
|
||
{
|
||
ptr[0] = nSampleL;
|
||
ptr[1] = nSampleR;
|
||
ptr += 2;
|
||
} while (--nCount);
|
||
}
|
||
}
|
||
#endif /* defined(SUPPORT_WAVEREC) */
|
||
|
||
|
||
// ----
|
||
|
||
BRESULT sound_create(UINT rate, UINT ms) {
|
||
|
||
UINT samples;
|
||
UINT reserve;
|
||
|
||
ZeroMemory(&sndstream, sizeof(sndstream));
|
||
switch(rate) {
|
||
case 11025:
|
||
case 22050:
|
||
case 44100:
|
||
case 48000:
|
||
case 88200:
|
||
case 96000:
|
||
case 176400:
|
||
case 192000:
|
||
break;
|
||
|
||
default:
|
||
return(FAILURE);
|
||
}
|
||
samples = soundmng_create(rate, ms);
|
||
if (samples == 0) {
|
||
goto scre_err1;
|
||
}
|
||
soundmng_reset();
|
||
|
||
soundcfg.rate = rate;
|
||
sound_changeclock();
|
||
|
||
#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 /* defined(SUPPORT_WAVEREC) */
|
||
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;
|
||
beep_eventreset();
|
||
}
|
||
}
|
||
|
||
void sound_changeclock(void) {
|
||
|
||
UINT32 clk;
|
||
UINT hz;
|
||
UINT hzmax;
|
||
|
||
if (sndstream.buffer == NULL) {
|
||
return;
|
||
}
|
||
|
||
// <20>Ƃ肠<C682><E882A0><EFBFBD><EFBFBD> 25<32>Ŋ<EFBFBD><C58A><EFBFBD><EFBFBD><EFBFBD><D882><EFBFBD><EFBFBD>B
|
||
clk = pccore.realclock / 25;
|
||
hz = soundcfg.rate / 25;
|
||
|
||
// <20>ŁA<C581>N<EFBFBD><4E><EFBFBD>b<EFBFBD>N<EFBFBD><4E><EFBFBD>ɍ<EFBFBD><C98D><EFBFBD><EFBFBD>Ē<EFBFBD><C492><EFBFBD><EFBFBD>B(64bit<69><74><EFBFBD>Z<EFBFBD><5A><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȓI)
|
||
hzmax = (1 << (32 - 8)) / (clk >> 8);
|
||
while(hzmax < hz) {
|
||
clk = (clk + 1) >> 1;
|
||
hz = (hz + 1) >> 1;
|
||
}
|
||
TRACEOUT(("hzbase/clockbase = %d/%d", hz, clk));
|
||
soundcfg.hzbase = hz;
|
||
soundcfg.clockbase = clk;
|
||
soundcfg.minclock = 2 * clk / 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 /* defined(SUPPORT_WAVEREC) */
|
||
streamprepare(length);
|
||
soundcfg.lastclock += length * soundcfg.clockbase / soundcfg.hzbase;
|
||
beep_eventreset();
|
||
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 /* defined(SUPPORT_WAVEREC) */
|
||
{
|
||
streamprepare(sndstream.remain - sndstream.reserve);
|
||
soundcfg.lastclock = CPU_CLOCK + CPU_BASECLOCK - CPU_REMCLOCK;
|
||
beep_eventreset();
|
||
}
|
||
}
|
||
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--;
|
||
}
|
||
}
|