--- /dev/null
+/* Copyright (c) 2003-2021 James Daniels */
+/* Distributed under the MIT License */
+/* license terms: see LICENSE file in root or http://opensource.org/licenses/MIT */
+
+#include "AAS_Shared.h"
+#include "../../src/debug.h"
+
+extern const int AAS_data_v111;
+int AAS_lib_v111 AAS_IN_EWRAM;
+
+static AAS_BOOL AAS_da_active AAS_IN_EWRAM;
+static AAS_BOOL AAS_db_active AAS_IN_EWRAM;
+static AAS_BOOL AAS_dynamic_mix_rate AAS_IN_EWRAM;
+
+/* 0 = 0 Hz mix rate */
+/* 1 = 800 Hz mix rate, 16 byte mix buffer */
+/* 2 = 1600 Hz mix rate, 32 byte mix buffer */
+/* n = 800n Hz mix rate, 16n byte mix buffer */
+/* n <= 40 */
+/* REG_TM1D = 0x10000 - 16n; */
+/* REG_TM0D = AAS_tick_rate[n]; // 0x10000 - ((int)(16777216/800n)); */
+static AAS_u8 AAS_req_mix_rate AAS_IN_EWRAM;
+static AAS_u8 AAS_next_mix_rate AAS_IN_EWRAM;
+
+static AAS_u8 *AAS_next_mixing_buffer AAS_IN_EWRAM;
+
+static AAS_BOOL AAS_DSA_first AAS_IN_EWRAM = AAS_TRUE;
+
+static AAS_u8 AAS_MaxChans AAS_IN_EWRAM = 4;
+
+static AAS_BOOL AAS_Loud AAS_IN_EWRAM = AAS_FALSE;
+static AAS_u8 AAS_MixAudio_Mode AAS_IN_EWRAM = AAS_MIXAUDIO_MODE_NORMAL;
+
+
+void AAS_MixAudio_SetMode_Normal();
+void AAS_MixAudio_SetMode_Boost();
+void AAS_MixAudio_SetMode_BoostAndClip();
+void AAS_MOD_Interrupt();
+
+
+void AAS_MixAudio_SetMode(int mode)
+{
+ if(mode != AAS_MixAudio_Mode) {
+ switch (mode) {
+ case AAS_MIXAUDIO_MODE_NORMAL:
+ AAS_MixAudio_SetMode_Normal();
+ AAS_MixAudio_Mode = AAS_MIXAUDIO_MODE_NORMAL;
+ AAS_changed[0] = AAS_TRUE;
+ AAS_changed[1] = AAS_TRUE;
+ break;
+
+ case AAS_MIXAUDIO_MODE_BOOST:
+ AAS_MixAudio_SetMode_Boost();
+ AAS_MixAudio_Mode = AAS_MIXAUDIO_MODE_BOOST;
+ AAS_changed[0] = AAS_TRUE;
+ AAS_changed[1] = AAS_TRUE;
+ break;
+
+ case AAS_MIXAUDIO_MODE_BOOSTANDCLIP:
+ AAS_MixAudio_SetMode_BoostAndClip();
+ AAS_MixAudio_Mode = AAS_MIXAUDIO_MODE_BOOSTANDCLIP;
+ AAS_changed[0] = AAS_TRUE;
+ AAS_changed[1] = AAS_TRUE;
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+static void AAS_DoConfig(int mix_rate, int volscale, AAS_BOOL stereo, AAS_BOOL dynamic,
+ AAS_BOOL loud, int chans)
+{
+ struct AAS_Channel *ch;
+ int i;
+
+ AAS_MOD_Stop();
+
+ if(!AAS_initialised) {
+ REG_SOUNDCNT_X = 0x0080; /* turn sound chip on */
+
+ REG_DMA1SAD = (AAS_u32) AAS_mix_buffer; /*dma1 source */
+ REG_DMA1DAD = 0x040000a0; /*write to FIFO A address */
+ REG_DMA1CNT_H = 0xb600; /*dma control: DMA enabled+ start on FIFO+32bit+repeat+increment source&dest */
+
+ REG_DMA2SAD = (AAS_u32) (AAS_mix_buffer + 160); /*dma2 source */
+ REG_DMA2DAD = 0x040000a4; /*write to FIFO B address */
+ REG_DMA2CNT_H = 0xb600; /*dma control: DMA enabled+ start on FIFO+32bit+repeat+increment source&dest */
+
+ AAS_next_mixing_buffer = ((AAS_u8 *) AAS_mix_buffer) + 1280;
+
+ REG_IE |= 0x10; /* Enable irq for timer 1 */
+ REG_IME = 1; /* Enable all interrupts */
+
+ AAS_da_active = AAS_FALSE;
+ AAS_db_active = AAS_FALSE;
+ }
+
+ REG_TM0CNT = 0x0;
+ REG_TM1CNT = 0x0;
+
+ if(chans != AAS_MaxChans) {
+ switch (chans) {
+ case 8:
+ AAS_MixAudio_SetMaxChans_8();
+ break;
+
+ case 4:
+ AAS_MixAudio_SetMaxChans_4();
+ break;
+
+ default:
+ AAS_MixAudio_SetMaxChans_2();
+ break;
+ }
+ AAS_MaxChans = chans;
+ }
+
+ AAS_next_mix_rate = AAS_req_mix_rate = mix_rate;
+ AAS_mix_scale = ((AAS_DivTable[mix_rate] * 82) + 128) >> 6;
+ AAS_dynamic_mix_rate = dynamic;
+
+ if(stereo) {
+ const AAS_u8 chan_rearrange[AAS_MAX_CHANNELS] =
+ { 0, 8, 9, 1, 2, 10, 11, 3, 4, 12, 13, 5, 6, 14, 15, 7 };
+
+ for(i = 0; i < AAS_MAX_CHANNELS; ++i) {
+ AAS_chan_rearrange[i] = chan_rearrange[i];
+ }
+
+ REG_SOUNDCNT_H = 0x9a0d; /*enable DS A&B + fifo reset + use timer0 + 100% volume to L and R */
+ } else {
+ int a, b;
+
+ a = 0;
+ for(b = 0; b < chans; ++b) {
+ AAS_chan_rearrange[a] = b;
+ ++a;
+ }
+ for(b = 0; b < chans; ++b) {
+ AAS_chan_rearrange[a] = b + 8;
+ ++a;
+ }
+
+ REG_SOUNDCNT_H = 0xbb0d; /*enable DS A&B + fifo reset + use timer0 + 100% volume to L and R */
+ }
+
+ ch = &AAS_channels[0];
+ for(i = 16; i > 0; --i) {
+ ch->active = AAS_FALSE;
+ ch->loop_length = 0;
+ ch->pos = 0;
+ ch->end = 0;
+ ch->delta = 0;
+ ++ch;
+ }
+
+ AAS_volscale = volscale;
+
+ AAS_Loud = loud;
+ if(!loud)
+ AAS_MixAudio_SetMode(AAS_MIXAUDIO_MODE_NORMAL);
+
+ REG_TM0D = AAS_tick_rate[mix_rate];
+ REG_TM0CNT = 0x0080; /* Enable timer0 */
+
+ REG_TM1D = 0x10000 - (mix_rate << 4);
+ REG_TM1CNT = 0xC4; /*enable timer1 + irq and cascade from timer 0 */
+
+ AAS_lib_v111 = AAS_data_v111;
+}
+
+int AAS_SetConfig(int config_mix, int config_chans, int config_spatial, int config_dynamic)
+{
+ int i, chans, mix_rate, volscale, ret;
+ AAS_BOOL stereo;
+ AAS_BOOL dynamic;
+ AAS_BOOL loud;
+
+ ret = AAS_OK;
+
+ switch (config_mix) {
+ case AAS_CONFIG_MIX_32KHZ:
+ mix_rate = 40;
+ break;
+
+ case AAS_CONFIG_MIX_28KHZ:
+ mix_rate = 35;
+ break;
+
+ case AAS_CONFIG_MIX_24KHZ:
+ mix_rate = 30;
+ break;
+
+ case AAS_CONFIG_MIX_20KHZ:
+ mix_rate = 25;
+ break;
+
+ case AAS_CONFIG_MIX_16KHZ:
+ mix_rate = 20;
+ break;
+
+ case AAS_CONFIG_MIX_12KHZ:
+ mix_rate = 15;
+ break;
+
+ case AAS_CONFIG_MIX_8KHZ:
+ mix_rate = 10;
+ break;
+
+ default:
+ ret = AAS_ERROR_INVALID_CONFIG;
+ break;
+ }
+
+ switch (config_chans) {
+ case AAS_CONFIG_CHANS_16_LOUD:
+ volscale = 9;
+ loud = AAS_TRUE;
+ chans = 8;
+ break;
+
+ case AAS_CONFIG_CHANS_8_LOUD:
+ volscale = 8;
+ loud = AAS_TRUE;
+ chans = 4;
+ break;
+
+ case AAS_CONFIG_CHANS_4_LOUD:
+ volscale = 7;
+ loud = AAS_TRUE;
+ chans = 2;
+ break;
+
+ case AAS_CONFIG_CHANS_16:
+ volscale = 9;
+ loud = AAS_FALSE;
+ chans = 8;
+ break;
+
+ case AAS_CONFIG_CHANS_8:
+ volscale = 8;
+ loud = AAS_FALSE;
+ chans = 4;
+ break;
+
+ case AAS_CONFIG_CHANS_4:
+ volscale = 7;
+ loud = AAS_FALSE;
+ chans = 2;
+ break;
+
+ default:
+ ret = AAS_ERROR_INVALID_CONFIG;
+ break;
+ }
+
+ switch (config_spatial) {
+ case AAS_CONFIG_SPATIAL_MONO:
+ stereo = AAS_FALSE;
+ break;
+
+ case AAS_CONFIG_SPATIAL_STEREO:
+ stereo = AAS_TRUE;
+ break;
+
+ default:
+ ret = AAS_ERROR_INVALID_CONFIG;
+ break;
+ }
+
+ switch (config_dynamic) {
+ case AAS_CONFIG_DYNAMIC_ON:
+ dynamic = AAS_TRUE;
+ break;
+
+ case AAS_CONFIG_DYNAMIC_OFF:
+ dynamic = AAS_FALSE;
+ break;
+
+ default:
+ ret = AAS_ERROR_INVALID_CONFIG;
+ break;
+ }
+
+ if(ret == AAS_OK) {
+ AAS_DoConfig(mix_rate, volscale, stereo, dynamic, loud, chans);
+
+ AAS_initialised = AAS_TRUE;
+ }
+
+ return ret;
+}
+
+const AAS_s8 *AAS_GetOutputBufferAddress(int buffer)
+{
+ switch (buffer) {
+ case 0:
+ if(AAS_da_active)
+ return AAS_next_mixing_buffer;
+ else
+ return AAS_NULL;
+ break;
+
+ case 1:
+ if(AAS_db_active)
+ return AAS_next_mixing_buffer + 640;
+ else
+ return AAS_NULL;
+ break;
+
+ default:
+ return AAS_NULL;
+ break;
+ }
+}
+
+int AAS_GetOutputBufferLength()
+{
+ return AAS_next_mix_rate * 16;
+}
+
+const AAS_u32 AAS_zero_vols[160] = { 0 };
+
+static AAS_BOOL AAS_interrupt_occured AAS_IN_EWRAM = AAS_FALSE;
+
+void AAS_FastTimer1InterruptHandler()
+{
+ if(AAS_dynamic_mix_rate) {
+ REG_TM0CNT = 0x0;
+ REG_TM0D = AAS_tick_rate[AAS_next_mix_rate];
+ REG_TM0CNT = 0x0080; /* Enable timer0 */
+ REG_TM1CNT = 0x0;
+ REG_TM1D = 0x10000 - (AAS_next_mix_rate << 4);
+ REG_TM1CNT = 0xC4; /* Enable timer1 + irq and cascade from timer 0 */
+ }
+
+ REG_DMA1CNT = 0x84400004;
+ REG_DMA2CNT = 0x84400004;
+ REG_DMA1CNT_H = 0x0440;
+ REG_DMA2CNT_H = 0x0440;
+ if(AAS_da_active)
+ REG_DMA1SAD = (unsigned long)AAS_next_mixing_buffer; /* DMA1 source */
+ else
+ REG_DMA1SAD = (unsigned long)AAS_zero_vols;
+ REG_DMA1CNT_H = 0xb600; /* DMA control: DMA enabled+start on FIFO+32bit+repeat+increment source&dest */
+ /* Get click on hardware when switch off DMA on Direct Sound B, so have to do it this way instead */
+ if(AAS_db_active)
+ REG_DMA2SAD = (unsigned long)AAS_next_mixing_buffer + 640; /* DMA2 source */
+ else
+ REG_DMA2SAD = (unsigned long)AAS_zero_vols; /* DMA2 source */
+ REG_DMA2CNT_H = 0xb600; /* DMA control: DMA enabled+start on FIFO+32bit+repeat+increment source&dest */
+
+ AAS_interrupt_occured = AAS_TRUE;
+}
+
+#define READCH1 \
+ if ( ch->active ) \
+ { \
+ int vol = ch->volume; \
+ if ( vol == 0 ) \
+ { \
+ int delta = ch->delta; \
+ const AAS_s8* end_addr; \
+ addr = ch->pos + ((delta*curr_mix_rate)>>6); \
+ end_addr = (ch->end - (delta>>6)) - 1; \
+ if ( addr >= end_addr ) \
+ { \
+ int ll = ch->loop_length; \
+ if ( ll ) \
+ { \
+ while( addr >= end_addr ) \
+ { \
+ addr -= ll; \
+ } \
+ } \
+ else \
+ { \
+ ch->active = AAS_FALSE; \
+ } \
+ } \
+ ch->pos = addr; \
+ } \
+ else \
+ { \
+ tmp1 += vol; \
+ } \
+ ch->effective_volume = vol; \
+ } \
+ else \
+ { \
+ ch->effective_volume = 0; \
+ } \
+
+#define READCH2 \
+ if ( ch->active ) \
+ { \
+ int vol = ch->volume; \
+ if ( vol == 0 ) \
+ { \
+ int delta = ch->delta; \
+ const AAS_s8* end_addr; \
+ addr = ch->pos + ((delta*curr_mix_rate)>>6); \
+ end_addr = (ch->end - (delta>>6)) - 1; \
+ if ( addr >= end_addr ) \
+ { \
+ int ll = ch->loop_length; \
+ if ( ll ) \
+ { \
+ while( addr >= end_addr ) \
+ { \
+ addr -= ll; \
+ } \
+ } \
+ else \
+ { \
+ ch->active = AAS_FALSE; \
+ } \
+ } \
+ ch->pos = addr; \
+ } \
+ else \
+ { \
+ tmp2 += vol; \
+ } \
+ ch->effective_volume = vol; \
+ } \
+ else \
+ { \
+ ch->effective_volume = 0; \
+ } \
+
+void AAS_DoWork()
+{
+ if(AAS_interrupt_occured) {
+ AAS_interrupt_occured = AAS_FALSE;
+
+ if(AAS_next_mixing_buffer == (AAS_u8 *) AAS_mix_buffer)
+ AAS_next_mixing_buffer = ((AAS_u8 *) AAS_mix_buffer) + 1280;
+ else
+ AAS_next_mixing_buffer = ((AAS_u8 *) AAS_mix_buffer);
+
+ AAS_MOD_Interrupt();
+
+ {
+ int tmp1, tmp2, val, curr_mix_rate;
+ struct AAS_Channel *ch;
+ const AAS_s8 *addr;
+
+ curr_mix_rate = AAS_req_mix_rate;
+
+ if(AAS_dynamic_mix_rate) {
+ val = 0;
+ ch = &AAS_channels[0];
+ for(tmp2 = AAS_MaxChans; tmp2 > 0; --tmp2) {
+ if(ch->active && (ch->volume > 0)) {
+ tmp1 = ch->frequency;
+ if(tmp1 > val)
+ val = tmp1;
+ }
+ ++ch;
+ }
+
+ ch = &AAS_channels[8];
+ for(tmp2 = AAS_MaxChans; tmp2 > 0; --tmp2) {
+ if(ch->active && (ch->volume > 0)) {
+ tmp1 = ch->frequency;
+ if(tmp1 > val)
+ val = tmp1;
+ }
+ ++ch;
+ }
+
+ val = ((val * 82) >> 16) + 1;
+ if(val < curr_mix_rate)
+ curr_mix_rate = val;
+
+ if(AAS_next_mix_rate != curr_mix_rate) {
+ AAS_next_mix_rate = curr_mix_rate;
+ AAS_mix_scale = val = ((AAS_DivTable[curr_mix_rate] * 82) + 128) >> 6;
+ ch = &AAS_channels[0];
+ for(tmp2 = AAS_MaxChans; tmp2 > 0; --tmp2) {
+ if(ch->active)
+ ch->delta = AAS_Min(4095, ((ch->frequency * val) + 32768) >> 16);
+ ++ch;
+ }
+
+ ch = &AAS_channels[8];
+ for(tmp2 = AAS_MaxChans; tmp2 > 0; --tmp2) {
+ if(ch->active)
+ ch->delta = AAS_Min(4095, ((ch->frequency * val) + 32768) >> 16);
+ ++ch;
+ }
+
+ AAS_changed[0] = AAS_TRUE;
+ AAS_changed[1] = AAS_TRUE;
+ }
+ }
+
+ tmp1 = 0;
+ tmp2 = 0;
+
+ ch = &AAS_channels[0];
+ switch (AAS_MaxChans) {
+ case 8:
+ READCH1++ ch;
+ READCH1++ ch;
+ READCH1++ ch;
+ READCH1++ ch;
+ READCH1++ ch;
+ READCH1++ ch;
+ READCH1++ ch;
+ READCH1++ ch;
+ READCH2++ ch;
+ READCH2++ ch;
+ READCH2++ ch;
+ READCH2++ ch;
+ READCH2++ ch;
+ READCH2++ ch;
+ READCH2++ ch;
+ READCH2 break;
+
+ case 4:
+ READCH1++ ch;
+ READCH1++ ch;
+ READCH1++ ch;
+ READCH1 ch = &AAS_channels[8];
+ READCH2++ ch;
+ READCH2++ ch;
+ READCH2++ ch;
+ READCH2 break;
+
+ case 2:
+ default:
+ READCH1++ ch;
+ READCH1 ch = &AAS_channels[8];
+ READCH2++ ch;
+ READCH2 break;
+ }
+
+ if(AAS_Loud) {
+ if(AAS_DSA_first) {
+ /* Direct Sound A */
+ if(tmp1) {
+ if(tmp1 > 128) {
+ AAS_MixAudio_SetMode(AAS_MIXAUDIO_MODE_BOOSTANDCLIP);
+ } else {
+ AAS_MixAudio_SetMode(AAS_MIXAUDIO_MODE_BOOST);
+ }
+
+ if(AAS_changed[0]) {
+ AAS_INIT_BRANCH
+ AAS_BRANCH(AAS_MixAudio, AAS_next_mixing_buffer, &AAS_channels[0], curr_mix_rate);
+ } else {
+ AAS_INIT_BRANCH
+ AAS_BRANCH(AAS_MixAudio_NoChange, AAS_next_mixing_buffer, &AAS_channels[0], curr_mix_rate);
+ }
+ AAS_da_active = AAS_TRUE;
+ } else {
+ AAS_da_active = AAS_FALSE;
+ }
+
+ /* Direct Sound B */
+ if(tmp2) {
+ if(tmp2 > 128) {
+ AAS_MixAudio_SetMode(AAS_MIXAUDIO_MODE_BOOSTANDCLIP);
+ } else {
+ AAS_MixAudio_SetMode(AAS_MIXAUDIO_MODE_BOOST);
+ }
+
+ {
+ AAS_INIT_BRANCH
+ AAS_BRANCH(AAS_MixAudio, AAS_next_mixing_buffer + 640, &AAS_channels[8], curr_mix_rate);
+ }
+
+ AAS_db_active = AAS_TRUE;
+ } else {
+ AAS_db_active = AAS_FALSE;
+ }
+
+ AAS_DSA_first = AAS_FALSE;
+ } else {
+ /* Direct Sound B */
+ if(tmp2) {
+ if(tmp2 > 128) {
+ AAS_MixAudio_SetMode(AAS_MIXAUDIO_MODE_BOOSTANDCLIP);
+ } else {
+ AAS_MixAudio_SetMode(AAS_MIXAUDIO_MODE_BOOST);
+ }
+
+ if(AAS_changed[1]) {
+ AAS_INIT_BRANCH
+ AAS_BRANCH(AAS_MixAudio, AAS_next_mixing_buffer + 640, &AAS_channels[8], curr_mix_rate);
+ } else {
+ AAS_INIT_BRANCH
+ AAS_BRANCH(AAS_MixAudio_NoChange, AAS_next_mixing_buffer + 640, &AAS_channels[8], curr_mix_rate);
+ }
+ AAS_db_active = AAS_TRUE;
+ } else {
+ AAS_db_active = AAS_FALSE;
+ }
+
+ /* Direct Sound A */
+ if(tmp1) {
+ if(tmp1 > 128) {
+ AAS_MixAudio_SetMode(AAS_MIXAUDIO_MODE_BOOSTANDCLIP);
+ } else {
+ AAS_MixAudio_SetMode(AAS_MIXAUDIO_MODE_BOOST);
+ }
+
+ {
+ AAS_INIT_BRANCH
+ AAS_BRANCH(AAS_MixAudio, AAS_next_mixing_buffer, &AAS_channels[0], curr_mix_rate);
+ }
+
+ AAS_da_active = AAS_TRUE;
+ } else {
+ AAS_da_active = AAS_FALSE;
+ }
+
+ AAS_DSA_first = AAS_TRUE;
+ }
+ } else {
+ if(AAS_DSA_first) {
+ /* Direct Sound A */
+ if(tmp1) {
+ if(AAS_changed[0]) {
+ AAS_INIT_BRANCH
+ AAS_BRANCH(AAS_MixAudio, AAS_next_mixing_buffer, &AAS_channels[0], curr_mix_rate);
+ } else {
+ AAS_INIT_BRANCH
+ AAS_BRANCH(AAS_MixAudio_NoChange, AAS_next_mixing_buffer, &AAS_channels[0], curr_mix_rate);
+ }
+ AAS_da_active = AAS_TRUE;
+ } else {
+ AAS_da_active = AAS_FALSE;
+ }
+
+ /* Direct Sound B */
+ if(tmp2) {
+ {
+ AAS_INIT_BRANCH
+ AAS_BRANCH(AAS_MixAudio, AAS_next_mixing_buffer + 640, &AAS_channels[8], curr_mix_rate);
+ }
+ AAS_db_active = AAS_TRUE;
+ } else {
+ AAS_db_active = AAS_FALSE;
+ }
+
+ AAS_DSA_first = AAS_FALSE;
+ } else {
+ /* Direct Sound B */
+ if(tmp2) {
+ if(AAS_changed[1]) {
+ AAS_INIT_BRANCH
+ AAS_BRANCH(AAS_MixAudio, AAS_next_mixing_buffer + 640, &AAS_channels[8], curr_mix_rate);
+ } else {
+ AAS_INIT_BRANCH
+ AAS_BRANCH(AAS_MixAudio_NoChange, AAS_next_mixing_buffer + 640, &AAS_channels[8], curr_mix_rate);
+ }
+ AAS_db_active = AAS_TRUE;
+ } else {
+ AAS_db_active = AAS_FALSE;
+ }
+
+ /* Direct Sound A */
+ if(tmp1) {
+ {
+ AAS_INIT_BRANCH
+ AAS_BRANCH(AAS_MixAudio, AAS_next_mixing_buffer, &AAS_channels[0], curr_mix_rate);
+ }
+
+ AAS_da_active = AAS_TRUE;
+ } else {
+ AAS_da_active = AAS_FALSE;
+ }
+
+ AAS_DSA_first = AAS_TRUE;
+ }
+ }
+
+ AAS_changed[0] = AAS_FALSE;
+ AAS_changed[1] = AAS_FALSE;
+ }
+ }
+}
+
+void AAS_Timer1InterruptHandler()
+{
+ AAS_FastTimer1InterruptHandler();
+ AAS_DoWork();
+}
+
+int AAS_GetActualMixRate()
+{
+ return AAS_next_mix_rate * 800;
+}