Mercurial > audlegacy-plugins
view src/xsf/desmume/SPU.c @ 3047:4434b4c4a715
ported EsounD plugin
author | Andrew O. Shadoura <bugzilla@tut.by> |
---|---|
date | Fri, 17 Apr 2009 16:17:10 +0300 |
parents | 70b0973e7b70 |
children |
line wrap: on
line source
/* Copyright (C) 2006 Theo Berkau Ideas borrowed from Stephane Dallongeville's SCSP core This file is part of DeSmuME DeSmuME 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. DeSmuME 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 DeSmuME; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <stdlib.h> #include <string.h> #include "ARM9.h" #include "MMU.h" #include "SPU.h" #include "mem.h" #include "armcpu.h" enum { FORMAT_PCM8 = 0, FORMAT_PCM16 = 1, FORMAT_ADPCM = 2, FORMAT_PSG = 3 }; #define VOL_SHIFT 10 typedef struct { int id; int status; int format; u8 *buf8; s16 *buf16; double pos, inc; int loopend, looppos; int loop, length; s32 adpcm; int adpcm_pos, adpcm_index; s32 adpcm_loop; int adpcm_loop_pos, adpcm_loop_index; int psg_duty; int timer; int volume; int pan; int shift; int repeat, hold; u32 addr; s32 volumel; s32 volumer; s16 output; } SChannel; typedef struct { s32 *pmixbuf; s16 *pclipingbuf; u32 buflen; SChannel ch[16]; } SPU_struct; static SPU_struct spu = { 0, 0, 0 }; static SoundInterface_struct *SNDCore=NULL; extern SoundInterface_struct *SNDCoreList[]; int SPU_ChangeSoundCore(int coreid, int buffersize) { int i; SPU_DeInit(); // Allocate memory for sound buffer spu.buflen = buffersize * 2; /* stereo */ spu.pmixbuf = malloc(spu.buflen * sizeof(s32)); if (!spu.pmixbuf) { SPU_DeInit(); return -1; } spu.pclipingbuf = malloc(spu.buflen * sizeof(s16)); if (!spu.pclipingbuf) { SPU_DeInit(); return -1; } // So which core do we want? if (coreid == SNDCORE_DEFAULT) coreid = 0; // Assume we want the first one // Go through core list and find the id for (i = 0; SNDCoreList[i] != NULL; i++) { if (SNDCoreList[i]->id == coreid) { // Set to current core SNDCore = SNDCoreList[i]; break; } } if (SNDCore == NULL) { SPU_DeInit(); return -1; } if (SNDCore->Init(spu.buflen) == -1) { // Since it failed, instead of it being fatal, we'll just use the dummy // core instead SNDCore = &SNDDummy; } return 0; } int SPU_Init(int coreid, int buffersize) { SPU_DeInit(); SPU_Reset(); return SPU_ChangeSoundCore(coreid, buffersize); } void SPU_Pause(int pause) { if(pause) SNDCore->MuteAudio(); else SNDCore->UnMuteAudio(); } void SPU_SetVolume(int volume) { if (SNDCore) SNDCore->SetVolume(volume); } void SPU_DeInit(void) { spu.buflen = 0; if (spu.pmixbuf) { free(spu.pmixbuf); spu.pmixbuf = 0; } if (spu.pclipingbuf) { free(spu.pclipingbuf); spu.pclipingbuf = 0; } if (SNDCore) { SNDCore->DeInit(); } SNDCore = &SNDDummy; } static const short g_adpcm_index[16] = { -1, -1, -1, -1, -1, -1, -1, -1, 2, 2, 4, 4, 6, 6, 8, 8 }; static const int g_adpcm_mult[89] = { 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x0010, 0x0011, 0x0013, 0x0015, 0x0017, 0x0019, 0x001C, 0x001F, 0x0022, 0x0025, 0x0029, 0x002D, 0x0032, 0x0037, 0x003C, 0x0042, 0x0049, 0x0050, 0x0058, 0x0061, 0x006B, 0x0076, 0x0082, 0x008F, 0x009D, 0x00AD, 0x00BE, 0x00D1, 0x00E6, 0x00FD, 0x0117, 0x0133, 0x0151, 0x0173, 0x0198, 0x01C1, 0x01EE, 0x0220, 0x0256, 0x0292, 0x02D4, 0x031C, 0x036C, 0x03C3, 0x0424, 0x048E, 0x0502, 0x0583, 0x0610, 0x06AB, 0x0756, 0x0812, 0x08E0, 0x09C3, 0x0ABD, 0x0BD0, 0x0CFF, 0x0E4C, 0x0FBA, 0x114C, 0x1307, 0x14EE, 0x1706, 0x1954, 0x1BDC, 0x1EA5, 0x21B6, 0x2515, 0x28CA, 0x2CDF, 0x315B, 0x364B, 0x3BB9, 0x41B2, 0x4844, 0x4F7E, 0x5771, 0x602F, 0x69CE, 0x7462, 0x7FFF, }; static const s16 g_psg_duty[8][8] = { { -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, +0x7FFF }, { -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, +0x7FFF, +0x7FFF }, { -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, +0x7FFF, +0x7FFF, +0x7FFF }, { -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, +0x7FFF, +0x7FFF, +0x7FFF, +0x7FFF }, { -0x7FFF, -0x7FFF, -0x7FFF, +0x7FFF, +0x7FFF, +0x7FFF, +0x7FFF, +0x7FFF }, { -0x7FFF, -0x7FFF, +0x7FFF, +0x7FFF, +0x7FFF, +0x7FFF, +0x7FFF, +0x7FFF }, { -0x7FFF, +0x7FFF, +0x7FFF, +0x7FFF, +0x7FFF, +0x7FFF, +0x7FFF, +0x7FFF }, { -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF }, }; static void reset_channel(SChannel *ch, int id) { ch->status = 0; ch->id = id; } void SPU_Reset(void) { int i; for (i = 0;i < 16; i++) reset_channel(&spu.ch[i], i); for (i = 0x400; i < 0x51D; i++) T1WriteByte(MMU.ARM7_REG, i, 0); } void SPU_KeyOn(int channel) { } static INLINE void adjust_channel_timer(SChannel *ch) { ch->inc = (((double)33512000) / (44100 * 2)) / (double)(0x10000 - ch->timer); } static int check_valid(u32 addr, u32 size) { u32 t1, t2; if(size > MMU.MMU_MASK[1][(addr >> 20) & 0xff]) return 0; t1 = addr; t2 = (addr + size); t1 &= MMU.MMU_MASK[1][(addr >> 20) & 0xff]; t2 &= MMU.MMU_MASK[1][(addr >> 20) & 0xff]; if(t2 < t1) return 0; return 1; } static void start_channel(SChannel *ch) { switch(ch->format) { case FORMAT_PCM8: { u8 *p = MMU.MMU_MEM[1][(ch->addr >> 20) & 0xff]; u32 ofs = MMU.MMU_MASK[1][(ch->addr >> 20) & 0xff] & ch->addr; u32 size = ((ch->length + ch->loop) << 2); if((p != NULL) && check_valid(ch->addr, size)) { ch->buf8 = p + ofs; ch->looppos = ch->loop << 2; ch->loopend = size; ch->pos = 0; ch->status = 1; } } break; case FORMAT_PCM16: { u8 *p = MMU.MMU_MEM[1][(ch->addr >> 20) & 0xff]; u32 ofs = MMU.MMU_MASK[1][(ch->addr >> 20) & 0xff] & ch->addr; u32 size = ((ch->length + ch->loop) << 1); if((p != NULL) && check_valid(ch->addr, size << 1)) { ch->buf16 = (s16 *)(p + ofs - (ofs & 1)); ch->looppos = ch->loop << 1; ch->loopend = size; ch->pos = 0; ch->status = 1; } } break; case FORMAT_ADPCM: { u8 *p = MMU.MMU_MEM[1][(ch->addr >> 20) & 0xff]; u32 ofs = MMU.MMU_MASK[1][(ch->addr >> 20) & 0xff] & ch->addr; u32 size = ((ch->length + ch->loop) << 3); if((p != NULL) && check_valid(ch->addr, size >> 1)) { ch->buf8 = p + ofs; #ifdef WORDS_BIGENDIAN ch->adpcm = ((s32)(s16)T1ReadWord((u8 *)ch->buf8, 0)) << 3; #else ch->adpcm = ((s32)*(s16 *)ch->buf8) << 3; #endif ch->adpcm_index = (ch->buf8[2] & 0x7F); ch->adpcm_pos = 8; ch->pos = 9; ch->looppos = ch->loop << 3; ch->loopend = size; ch->adpcm_loop_index = -1; ch->status = 1; } } break; case FORMAT_PSG: ch->status = 1; if(ch->id < 14) { ch->pos = 0; } else { ch->pos = 0x7FFF; } break; } } static void stop_channel(SChannel *ch) { u32 addr = 0x400 + (ch->id << 4) + 3; ch->status = 0; T1WriteByte(MMU.ARM7_REG, addr, (u8)(T1ReadByte(MMU.ARM7_REG, addr) & ~0x80)); } static void set_channel_volume(SChannel *ch) { s32 vol1 = (T1ReadByte(MMU.ARM7_REG, 0x500) & 0x7F) * ch->volume; s32 vol2; vol2 = vol1 * ch->pan; vol1 = vol1 * (127-ch->pan); ch->volumel = vol1 >> (21 - VOL_SHIFT + ch->shift); ch->volumer = vol2 >> (21 - VOL_SHIFT + ch->shift); } void SPU_WriteByte(u32 addr, u8 x) { addr &= 0x00000FFF; T1WriteByte(MMU.ARM7_REG, addr, x); if(addr < 0x500) { SChannel *ch; switch(addr & 0x0F) { case 0x0: ch = spu.ch + (addr >> 4 & 0xF); ch->volume = (x & 0x7F); set_channel_volume(ch); break; case 0x1: ch = spu.ch + (addr >> 4 & 0xF); ch->shift = (x & 0x03); ch->hold = (x >> 7 & 0x01); set_channel_volume(ch); break; case 0x2: ch = spu.ch + (addr >> 4 & 0xF); ch->pan = (x & 0x7F); set_channel_volume(ch); break; case 0x3: ch = spu.ch + (addr >> 4 & 0xF); ch->psg_duty = (x & 0x07); ch->repeat = (x >> 3 & 0x03); ch->format = (x >> 5 & 0x03); if(x & 0x80) start_channel(ch); else stop_channel(ch); break; #if !DISABLE_XSF_TESTS case 0x04: case 0x05: case 0x06: case 0x07: ch = spu.ch + (addr >> 4 & 0xF); ch->addr = (T1ReadLong(MMU.ARM7_REG, addr & ~3) & 0x07FFFFFF); break; case 0x08: case 0x09: ch = spu.ch + (addr >> 4 & 0xF); ch->timer = T1ReadWord(MMU.ARM7_REG, addr & ~1); adjust_channel_timer(ch); break; case 0x0a: case 0x0b: ch = spu.ch + (addr >> 4 & 0xF); ch->loop = T1ReadWord(MMU.ARM7_REG, addr & ~1); break; case 0x0c: case 0x0e: case 0x0d: case 0x0f: ch = spu.ch + (addr >> 4 & 0xF); ch->length = (T1ReadLong(MMU.ARM7_REG, addr & ~3) & 0x003FFFFF); break; #endif } } } void SPU_WriteWord(u32 addr, u16 x) { addr &= 0x00000FFF; T1WriteWord(MMU.ARM7_REG, addr, x); if(addr < 0x500) { SChannel *ch; switch(addr & 0x00F) { case 0x0: ch = spu.ch + (addr >> 4 & 0xF); ch->volume = (x & 0x007F); ch->shift = (x >> 8 & 0x0003); ch->hold = (x >> 15 & 0x0001); set_channel_volume(ch); break; case 0x2: ch = spu.ch + (addr >> 4 & 0xF); ch->pan = (x & 0x007F); ch->psg_duty = (x >> 8 & 0x0007); ch->repeat = (x >> 11 & 0x0003); ch->format = (x >> 13 & 0x0003); set_channel_volume(ch); if(x & 0x8000) start_channel(ch); else stop_channel(ch); break; case 0x08: ch = spu.ch + (addr >> 4 & 0xF); ch->timer = x; adjust_channel_timer(ch); break; case 0x0a: ch = spu.ch + (addr >> 4 & 0xF); ch->loop = x; break; #if !DISABLE_XSF_TESTS case 0x04: case 0x06: ch = spu.ch + (addr >> 4 & 0xF); ch->addr = (T1ReadLong(MMU.ARM7_REG, addr & ~3) & 0x07FFFFFF); break; case 0x0c: case 0x0e: ch = spu.ch + (addr >> 4 & 0xF); ch->length = (T1ReadLong(MMU.ARM7_REG, addr & ~3) & 0x003FFFFF); break; #endif } } } void SPU_WriteLong(u32 addr, u32 x) { addr &= 0x00000FFF; T1WriteLong(MMU.ARM7_REG, addr, x); if(addr < 0x500) { SChannel *ch; switch(addr & 0x00F) { case 0x0: ch = spu.ch + (addr >> 4 & 0xF); ch->volume = (x & 0x7F); ch->shift = (x >> 8 & 0x00000003); ch->hold = (x >> 15 & 0x00000001); ch->pan = (x >> 16 & 0x0000007F); ch->psg_duty = (x >> 24 & 0x00000007); ch->repeat = (x >> 27 & 0x00000003); ch->format = (x >> 29 & 0x00000003); set_channel_volume(ch); if(x & 0x80000000) start_channel(ch); else stop_channel(ch); break; case 0x04: ch = spu.ch + (addr >> 4 & 0xF); ch->addr = (x & 0x07FFFFFF); break; case 0x08: ch = spu.ch + (addr >> 4 & 0xF); ch->timer = (x & 0x0000FFFF); ch->loop = (x >> 16 & 0x0000FFFF); adjust_channel_timer(ch); break; case 0x0C: ch = spu.ch + (addr >> 4 & 0xF); ch->length = (x & 0x003FFFFF); break; } } } ////////////////////////////////////////////////////////////////////////////// u32 SPU_ReadLong(u32 addr) { addr &= 0xFFF; return T1ReadLong(MMU.ARM7_REG, addr); } static INLINE s32 clipping(s32 x, s32 min, s32 max) { #if 1 || defined(SIGNED_IS_NOT_2S_COMPLEMENT) if (x < min) { return (min); } else if (x > max) { return (max); } return (x); #else return x ^ ((-(x < min)) & (x ^ min)) ^ ((-(x > max)) & (x ^ max)); #endif } extern unsigned long dwChannelMute; static void decode_pcm8(SChannel *ch, s32 *out, int length) { int oi; double pos, inc, len; if (!ch->buf8) return; pos = ch->pos; inc = ch->inc; len = ch->loopend; for(oi = 0; oi < length; oi++) { ch->output = ((s16)(s8)ch->buf8[(int)pos]) << 8; #if 0 if (dwChannelMute & (1 << ch->id)) { out++; out++; } else #endif { *(out++) += (ch->output * ch->volumel) >> VOL_SHIFT; *(out++) += (ch->output * ch->volumer) >> VOL_SHIFT; } pos += inc; if(pos >= len) { switch(ch->repeat) { #if !DISABLE_XSF_TESTS case 0: #endif case 1: pos += ch->looppos - len; break; default: stop_channel(ch); oi = length; break; } } } ch->pos = pos; return; } static void decode_pcm16(SChannel *ch, s32 *out, int length) { int oi; double pos, inc, len; if (!ch->buf16) return; pos = ch->pos; inc = ch->inc; len = ch->loopend; for(oi = 0; oi < length; oi++) { #ifdef WORDS_BIGENDIAN ch->output = (s16)T1ReadWord((u8 *)ch->buf16, pos << 1); #else ch->output = (s16)ch->buf16[(int)pos]; #endif #if 0 if (dwChannelMute & (1 << ch->id)) { out++; out++; } else #endif { *(out++) += (ch->output * ch->volumel) >> VOL_SHIFT; *(out++) += (ch->output * ch->volumer) >> VOL_SHIFT; } pos += inc; if(pos >= len) { switch(ch->repeat) { #if !DISABLE_XSF_TESTS case 0: #endif case 1: pos += ch->looppos - len; break; default: stop_channel(ch); oi = length; break; } } } ch->pos = pos; } static INLINE void decode_adpcmone_P4(SChannel *ch, int m) { int i, ci0; u8 *p; s32 s; int N; i = ch->adpcm_pos; p = (ch->buf8 + (i >> 1)); ci0 = ch->adpcm_index; s = ch->adpcm; if (ch->adpcm_loop_index < 0 && m >= ch->looppos) { ch->adpcm_loop_index = ci0; ch->adpcm_loop = s; ch->adpcm_loop_pos = i; } if(i++ & 1) { s32 x1, d1; x1 = ((*(p++) >> 3) & 0x1F | 1); d1 = ((x1 & 0xF) * g_adpcm_mult[ci0] & ~7); ci0 = clipping((ci0 + g_adpcm_index[x1 & 0xE]), 0, 88); #if 1 || defined(SIGNED_IS_NOT_2S_COMPLEMENT) if(x1 & 0x10) d1 = -d1; #else d1 -= (d1 + d1) & (-(((x1 >> 4) & 1))); #endif s = clipping((s + d1), (-32768 << 3), (32767 << 3)); } N = (((m & ~1) - (i & ~1)) >> 1); for(i = 0; i < N; i++) { s32 x0, d0; s32 x1, d1; int ci1; x0 = ((*p << 1) & 0x1F | 1); x1 = ((*p >> 3) & 0x1F | 1); ci1 = clipping((ci0 + g_adpcm_index[x0 & 0xE]), 0, 88); d0 = ((x0 & 0xF) * g_adpcm_mult[ci0] & ~7); ci0 = clipping((ci1 + g_adpcm_index[x1 & 0xE]), 0, 88); d1 = ((x1 & 0xF) * g_adpcm_mult[ci1] & ~7); #if 1 || defined(SIGNED_IS_NOT_2S_COMPLEMENT) if(x0 & 0x10) d0 = -d0; if(x1 & 0x10) d1 = -d1; #else d0 -= (d0 + d0) & (-(((x0 >> 4) & 1))); d1 -= (d1 + d1) & (-(((x1 >> 4) & 1))); #endif s = clipping((s + d0), (-32768 << 3), (32767 << 3)); s = clipping((s + d1), (-32768 << 3), (32767 << 3)); p++; } if(m & 1) { s32 x0, d0; x0 = ((*p << 1) & 0x1F | 1); d0 = ((x0 & 0xF) * g_adpcm_mult[ci0] & ~7); ci0 = clipping((ci0 + g_adpcm_index[x0 & 0xE]), 0, 88); #if 1 || defined(SIGNED_IS_NOT_2S_COMPLEMENT) if(x0 & 0x10) d0 = -d0; #else d0 -= (d0 + d0) & (-(((x0 >> 4) & 1))); #endif s = clipping((s + d0), (-32768 << 3), (32767 << 3)); } ch->output = (s16)(s >> 3); ch->adpcm = s; ch->adpcm_index = ci0; ch->adpcm_pos = m; } static INLINE void decode_adpcmone_XX(SChannel *ch, int m) { int i, ci0; u8 *p; s32 s; i = ch->adpcm_pos; p = (ch->buf8 + (i >> 1)); ci0 = ch->adpcm_index; s = ch->adpcm; if (ch->adpcm_loop_index < 0 && m >= ch->looppos) { ch->adpcm_loop_index = ci0; ch->adpcm_loop = s; ch->adpcm_loop_pos = i; } while (i < m) { s32 x1, d1; x1 = ((s32)*p) >> ((i & 1) << 2) & 0xf; x1 = x1 + x1 + 1; d1 = ((x1 & 0xF) * g_adpcm_mult[ci0] & ~7); ci0 = clipping((ci0 + g_adpcm_index[x1 & 0xE]), 0, 88); #if 1 || defined(SIGNED_IS_NOT_2S_COMPLEMENT) if(x1 & 0x10) d1 = -d1; #else d1 -= (d1 + d1) & (-(((x1 >> 4) & 1))); #endif s = clipping((s + d1), (-32768 << 3), (32767 << 3)); p += (i & 1); i++; } ch->output = (s16)(s >> 3); ch->adpcm = s; ch->adpcm_index = ci0; ch->adpcm_pos = m; } #define decode_adpcmone decode_adpcmone_P4 static void decode_adpcm(SChannel *ch, s32 *out, int length) { int oi; double pos, inc, len; if (!ch->buf8) return; pos = ch->pos; inc = ch->inc; len = ch->loopend; for(oi = 0; oi < length; oi++) { int m = (int)pos; int i = ch->adpcm_pos; if(i < m) decode_adpcmone(ch, m); #if 0 if (dwChannelMute & (1 << ch->id)) { out++; out++; } else #endif { *(out++) += (ch->output * ch->volumel) >> VOL_SHIFT; *(out++) += (ch->output * ch->volumer) >> VOL_SHIFT; } pos += inc; if(pos >= len) { switch(ch->repeat) { case 1: if (ch->adpcm_loop_index >= 0) { pos += ch->looppos - len; ch->adpcm_pos = ch->adpcm_loop_pos; ch->adpcm_index = ch->adpcm_loop_index; ch->adpcm = ch->adpcm_loop; break; } #if !DISABLE_XSF_TESTS case 0: pos = 9 - len; #ifdef WORDS_BIGENDIAN ch->adpcm = ((s32)(s16)T1ReadWord((u8 *)ch->buf8, 0)) << 3; #else ch->adpcm = ((s32)*(s16 *)ch->buf8) << 3; #endif ch->adpcm_index = (ch->buf8[2] & 0x7F); ch->adpcm_pos = 8; break; #endif default: stop_channel(ch); oi = length; break; } } } ch->pos = pos; } static void decode_psg(SChannel *ch, s32 *out, int length) { int oi; if(ch->id < 14) { // NOTE: square wave. double pos, inc, len; pos = ch->pos; inc = ch->inc; len = ch->length; for(oi = 0; oi < length; oi++) { ch->output = (s16)g_psg_duty[ch->psg_duty][(int)pos & 0x00000007]; #if 0 if (dwChannelMute & (1 << ch->id)) { out++; out++; } else #endif { *(out++) += (ch->output * ch->volumel) >> VOL_SHIFT; *(out++) += (ch->output * ch->volumer) >> VOL_SHIFT; } pos += inc; } ch->pos = pos; } else { // NOTE: noise. u16 X; X = (u16)ch->pos; for(oi = 0; oi < length; oi++) { if(X & 1) { X >>= 1; X ^= 0x6000; ch->output = -0x8000; } else { X >>= 1; ch->output = +0x7FFF; } } #if 0 if (dwChannelMute & (1 << ch->id)) { out++; out++; } else #endif { *(out++) += (ch->output * ch->volumel) >> VOL_SHIFT; *(out++) += (ch->output * ch->volumer) >> VOL_SHIFT; } ch->pos = X; } } void SPU_EmulateSamples(u32 numsamples) { u32 sizesmp = numsamples; u32 sizebyte = sizesmp << 2; if (sizebyte > spu.buflen * sizeof(s16)) sizebyte = spu.buflen * sizeof(s16); sizesmp = sizebyte >> 2; sizebyte = sizesmp << 2; if (sizesmp > 0) { unsigned i; SChannel *ch = spu.ch; memset(spu.pmixbuf, 0, spu.buflen * sizeof(s32)); for (i = 0; i < 16; i++) { if (ch->status) { switch (ch->format) { case 0: decode_pcm8(ch, spu.pmixbuf, sizesmp); break; case 1: decode_pcm16(ch, spu.pmixbuf, sizesmp); break; case 2: decode_adpcm(ch, spu.pmixbuf, sizesmp); break; case 3: decode_psg(ch, spu.pmixbuf, sizesmp); break; } } ch++; } for (i = 0; i < sizesmp * 2; i++) spu.pclipingbuf[i] = (s16)clipping(spu.pmixbuf[i], -0x8000, 0x7fff); SNDCore->UpdateAudio(spu.pclipingbuf, sizesmp); } } void SPU_Emulate(void) { SPU_EmulateSamples(SNDCore->GetAudioSpace()); } ////////////////////////////////////////////////////////////////////////////// // Dummy Sound Interface ////////////////////////////////////////////////////////////////////////////// static int SNDDummyInit(int buffersize) { return 0; } ////////////////////////////////////////////////////////////////////////////// static void SNDDummyDeInit() { } ////////////////////////////////////////////////////////////////////////////// static void SNDDummyUpdateAudio(s16 *buffer, u32 num_samples) { } ////////////////////////////////////////////////////////////////////////////// static u32 SNDDummyGetAudioSpace() { return 735; } ////////////////////////////////////////////////////////////////////////////// static void SNDDummyMuteAudio() { } ////////////////////////////////////////////////////////////////////////////// static void SNDDummyUnMuteAudio() { } ////////////////////////////////////////////////////////////////////////////// static void SNDDummySetVolume(int volume) { } ////////////////////////////////////////////////////////////////////////////// SoundInterface_struct SNDDummy = { SNDCORE_DUMMY, "Dummy Sound Interface", SNDDummyInit, SNDDummyDeInit, SNDDummyUpdateAudio, SNDDummyGetAudioSpace, SNDDummyMuteAudio, SNDDummyUnMuteAudio, SNDDummySetVolume };