|
|
| version 1.1, 2003/10/16 17:58:02 | version 1.21, 2005/04/05 20:37:07 |
|---|---|
| Line 1 | Line 1 |
| #include "compiler.h" | #include "compiler.h" |
| #include "wavefile.h" | |
| #include "dosio.h" | |
| #include "soundmng.h" | #include "soundmng.h" |
| #include "cpucore.h" | |
| #include "pccore.h" | #include "pccore.h" |
| #include "iocore.h" | #include "iocore.h" |
| #include "sound.h" | #include "sound.h" |
| #include "sndcsec.h" | #include "sndcsec.h" |
| #include "beep.h" | #include "beep.h" |
| #include "getsnd.h" | |
| UINT32 opna_rate = 22050; | SOUNDCFG soundcfg; |
| static int writebytes = 0; | |
| UINT32 ratebase200 = 110; | |
| UINT32 dsound_lastclock = 0; | |
| #define STREAM_CBMAX 16 | #define STREAM_CBMAX 16 |
| Line 27 typedef struct { | Line 25 typedef struct { |
| SINT32 *buffer; | SINT32 *buffer; |
| SINT32 *ptr; | SINT32 *ptr; |
| UINT samples; | UINT samples; |
| UINT reserve; | |
| UINT remain; | UINT remain; |
| #if defined(SUPPORT_WAVEREC) | |
| WAVEWR rec; | |
| #endif | |
| CBTBL *cbreg; | CBTBL *cbreg; |
| CBTBL cb[STREAM_CBMAX]; | CBTBL cb[STREAM_CBMAX]; |
| } SNDSTREAM; | } SNDSTREAM; |
| Line 37 static SNDSTREAM sndstream; | Line 39 static SNDSTREAM sndstream; |
| static void streamreset(void) { | static void streamreset(void) { |
| SNDCSEC_ENTER; | |
| sndstream.ptr = sndstream.buffer; | sndstream.ptr = sndstream.buffer; |
| sndstream.remain = sndstream.samples; | sndstream.remain = sndstream.samples + sndstream.reserve; |
| sndstream.cbreg = sndstream.cb; | sndstream.cbreg = sndstream.cb; |
| SNDCSEC_LEAVE; | |
| } | } |
| static void streamprepare(UINT samples) { | static void streamprepare(UINT samples) { |
| Line 61 static void streamprepare(UINT samples) | Line 65 static void streamprepare(UINT samples) |
| } | } |
| #if defined(SUPPORT_WAVEREC) | |
| // ---- wave rec | |
| BOOL 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]; | |
| UINT8 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 | |
| // ---- | // ---- |
| BOOL sound_create(UINT rate, UINT ms) { | BOOL sound_create(UINT rate, UINT ms) { |
| UINT samples; | UINT samples; |
| UINT reserve; | |
| ZeroMemory(&sndstream, sizeof(sndstream)); | ZeroMemory(&sndstream, sizeof(sndstream)); |
| if (rate == 0) { | |
| return(SUCCESS); | |
| } | |
| switch(rate) { | switch(rate) { |
| case 11025: | case 11025: |
| opna_rate = 11025; | |
| break; | |
| case 22050: | case 22050: |
| opna_rate = 22050; | |
| break; | |
| case 44100: | case 44100: |
| opna_rate = 44100; | |
| break; | break; |
| default: | default: |
| Line 93 BOOL sound_create(UINT rate, UINT ms) { | Line 188 BOOL sound_create(UINT rate, UINT ms) { |
| } | } |
| soundmng_reset(); | soundmng_reset(); |
| sndstream.buffer = (SINT32 *)_MALLOC(samples * 2 * sizeof(SINT32), | soundcfg.rate = rate; |
| "stream"); | 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) { | if (sndstream.buffer == NULL) { |
| goto scre_err2; | goto scre_err2; |
| } | } |
| sndstream.samples = samples; | sndstream.samples = samples; |
| streamreset(); | sndstream.reserve = reserve; |
| ratebase200 = (opna_rate + 199) / 200; | |
| SNDCSEC_INIT; | SNDCSEC_INIT; |
| streamreset(); | |
| return(SUCCESS); | return(SUCCESS); |
| scre_err2: | scre_err2: |
| Line 115 scre_err1: | Line 218 scre_err1: |
| void sound_destroy(void) { | void sound_destroy(void) { |
| if (sndstream.buffer) { | if (sndstream.buffer) { |
| SNDCSEC_TERM; | #if defined(SUPPORT_WAVEREC) |
| sound_recstop(); | |
| #endif | |
| soundmng_stop(); | soundmng_stop(); |
| streamreset(); | |
| soundmng_destroy(); | soundmng_destroy(); |
| SNDCSEC_TERM; | |
| _MFREE(sndstream.buffer); | _MFREE(sndstream.buffer); |
| sndstream.buffer = NULL; | sndstream.buffer = NULL; |
| } | } |
| Line 129 void sound_reset(void) { | Line 235 void sound_reset(void) { |
| if (sndstream.buffer) { | if (sndstream.buffer) { |
| soundmng_reset(); | soundmng_reset(); |
| streamreset(); | streamreset(); |
| dsound_lastclock = nevent.clock; | soundcfg.lastclock = CPU_CLOCK; |
| beep_eventreset(); | beep_eventreset(); |
| } | } |
| } | } |
| 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) { | void sound_streamregist(void *hdl, SOUNDCB cbfn) { |
| if (sndstream.buffer) { | if (sndstream.buffer) { |
| Line 151 void sound_streamregist(void *hdl, SOUND | Line 284 void sound_streamregist(void *hdl, SOUND |
| void sound_sync(void) { | void sound_sync(void) { |
| UINT length; | UINT32 length; |
| if (sndstream.buffer == NULL) { | if (sndstream.buffer == NULL) { |
| return; | return; |
| } | } |
| length = (nevent.clock + nevent.baseclock - nevent.remainclock | length = CPU_CLOCK + CPU_BASECLOCK - CPU_REMCLOCK - soundcfg.lastclock; |
| - dsound_lastclock) * ratebase200; | if (length < soundcfg.minclock) { |
| if (length < pc.dsoundclock2) { | |
| return; | return; |
| } | } |
| length /= pc.dsoundclock; | length = (length * soundcfg.hzbase) / soundcfg.clockbase; |
| if (length) { | if (length == 0) { |
| SNDCSEC_ENTER; | return; |
| streamprepare(length); | |
| SNDCSEC_LEAVE; | |
| writebytes += length; | |
| dsound_lastclock += (length * pc.dsoundclock / ratebase200); | |
| beep_eventreset(); | |
| } | } |
| SNDCSEC_ENTER; | |
| if (writebytes >= 100) { | #if defined(SUPPORT_WAVEREC) |
| writebytes = 0; | if (sndstream.rec) { |
| streamfilewrite(length); | |
| } | |
| else | |
| #endif | |
| 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(); | soundmng_sync(); |
| } | } |
| } | } |
| static volatile int locks = 0; | |
| const SINT32 *sound_pcmlock(void) { | const SINT32 *sound_pcmlock(void) { |
| const SINT32 *ret; | const SINT32 *ret; |
| if (locks) { | |
| TRACEOUT(("sound pcm lock: already locked")); | |
| return(NULL); | |
| } | |
| locks++; | |
| ret = sndstream.buffer; | ret = sndstream.buffer; |
| if (ret) { | if (ret) { |
| SNDCSEC_ENTER; | SNDCSEC_ENTER; |
| if (sndstream.remain) { | if (sndstream.remain > sndstream.reserve) |
| streamprepare(sndstream.remain); | #if defined(SUPPORT_WAVEREC) |
| dsound_lastclock = nevent.clock; | if (sndstream.rec) { |
| filltailsample(sndstream.remain - sndstream.reserve); | |
| } | |
| else | |
| #endif | |
| { | |
| streamprepare(sndstream.remain - sndstream.reserve); | |
| soundcfg.lastclock = CPU_CLOCK + CPU_BASECLOCK - CPU_REMCLOCK; | |
| beep_eventreset(); | beep_eventreset(); |
| } | } |
| } | } |
| else { | |
| locks--; | |
| } | |
| return(ret); | return(ret); |
| } | } |
| void sound_pcmunlock(const SINT32 *hdl) { | void sound_pcmunlock(const SINT32 *hdl) { |
| int leng; | |
| if (hdl) { | if (hdl) { |
| sndstream.ptr = sndstream.buffer; | leng = sndstream.reserve - sndstream.remain; |
| sndstream.remain = sndstream.samples; | 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; | SNDCSEC_LEAVE; |
| locks--; | |
| } | |
| } | |
| // ---- pcmmix | |
| BRESULT pcmmix_regist(PMIXDAT *dat, void *datptr, UINT datsize, UINT rate) { | |
| GETSND gs; | |
| UINT8 tmp[256]; | |
| UINT size; | |
| UINT r; | |
| SINT16 *buf; | |
| gs = getsnd_create(datptr, datsize); | |
| if (gs == NULL) { | |
| goto pmr_err1; | |
| } | |
| if (getsnd_setmixproc(gs, rate, 1) != SUCCESS) { | |
| goto pmr_err2; | |
| } | |
| size = 0; | |
| do { | |
| r = getsnd_getpcmbyleng(gs, tmp, sizeof(tmp)); | |
| size += r; | |
| } while(r); | |
| getsnd_destroy(gs); | |
| if (size == 0) { | |
| goto pmr_err1; | |
| } | |
| buf = (SINT16 *)_MALLOC(size, "PCM DATA"); | |
| if (buf == NULL) { | |
| goto pmr_err1; | |
| } | |
| gs = getsnd_create(datptr, datsize); | |
| if (gs == NULL) { | |
| goto pmr_err1; | |
| } | |
| if (getsnd_setmixproc(gs, rate, 1) != SUCCESS) { | |
| goto pmr_err2; | |
| } | |
| r = getsnd_getpcmbyleng(gs, buf, size); | |
| getsnd_destroy(gs); | |
| dat->sample = buf; | |
| dat->samples = r / 2; | |
| return(SUCCESS); | |
| pmr_err2: | |
| getsnd_destroy(gs); | |
| pmr_err1: | |
| return(FAILURE); | |
| } | |
| BRESULT pcmmix_regfile(PMIXDAT *dat, const OEMCHAR *fname, UINT rate) { | |
| FILEH fh; | |
| UINT size; | |
| UINT8 *ptr; | |
| BRESULT r; | |
| r = FAILURE; | |
| fh = file_open_rb(fname); | |
| if (fh == FILEH_INVALID) { | |
| goto pmrf_err1; | |
| } | |
| size = file_getsize(fh); | |
| if (size == 0) { | |
| goto pmrf_err2; | |
| } | |
| ptr = (UINT8 *)_MALLOC(size, fname); | |
| if (ptr == NULL) { | |
| goto pmrf_err2; | |
| } | |
| file_read(fh, ptr, size); | |
| file_close(fh); | |
| r = pcmmix_regist(dat, ptr, size, rate); | |
| _MFREE(ptr); | |
| return(r); | |
| pmrf_err2: | |
| file_close(fh); | |
| pmrf_err1: | |
| return(FAILURE); | |
| } | |
| void SOUNDCALL pcmmix_getpcm(PCMMIX hdl, SINT32 *pcm, UINT count) { | |
| UINT32 bitmap; | |
| PMIXTRK *t; | |
| const SINT16 *s; | |
| UINT srem; | |
| SINT32 *d; | |
| UINT drem; | |
| UINT r; | |
| UINT j; | |
| UINT flag; | |
| SINT32 vol; | |
| SINT32 samp; | |
| if ((hdl->hdr.playing == 0) || (count == 0)) { | |
| return; | |
| } | } |
| t = hdl->trk; | |
| bitmap = 1; | |
| do { | |
| if (hdl->hdr.playing & bitmap) { | |
| s = t->pcm; | |
| srem = t->remain; | |
| d = pcm; | |
| drem = count; | |
| flag = t->flag; | |
| vol = t->volume; | |
| do { | |
| r = min(srem, drem); | |
| switch(flag & (PMIXFLAG_L | PMIXFLAG_R)) { | |
| case PMIXFLAG_L: | |
| for (j=0; j<r; j++) { | |
| d[j*2+0] += (s[j] * vol) >> 12; | |
| } | |
| break; | |
| case PMIXFLAG_R: | |
| for (j=0; j<r; j++) { | |
| d[j*2+1] += (s[j] * vol) >> 12; | |
| } | |
| break; | |
| case PMIXFLAG_L | PMIXFLAG_R: | |
| for (j=0; j<r; j++) { | |
| samp = (s[j] * vol) >> 12; | |
| d[j*2+0] += samp; | |
| d[j*2+1] += samp; | |
| } | |
| break; | |
| } | |
| s += r; | |
| d += r*2; | |
| srem -= r; | |
| if (srem == 0) { | |
| if (flag & PMIXFLAG_LOOP) { | |
| s = t->data.sample; | |
| srem = t->data.samples; | |
| } | |
| else { | |
| hdl->hdr.playing &= ~bitmap; | |
| break; | |
| } | |
| } | |
| drem -= r; | |
| } while(drem); | |
| t->pcm = s; | |
| t->remain = srem; | |
| } | |
| t++; | |
| bitmap <<= 1; | |
| } while(bitmap < hdl->hdr.enable); | |
| } | } |