From a709333bb82b222e25648f26eb7b76722c4acfcc Mon Sep 17 00:00:00 2001 From: John Tsiombikas Date: Wed, 7 Apr 2021 08:18:59 +0300 Subject: [PATCH] added apex audio system --- .gitignore | 2 + Makefile | 32 +- libs/aas/AAS.h | 121 +++++ libs/aas/AAS_ASM.s | 299 +++++++++++ libs/aas/AAS_MOD.c | 932 +++++++++++++++++++++++++++++++++ libs/aas/AAS_Main.c | 698 +++++++++++++++++++++++++ libs/aas/AAS_Mixer.h | 62 +++ libs/aas/AAS_Mixer.s | 632 +++++++++++++++++++++++ libs/aas/AAS_SFX.c | 217 ++++++++ libs/aas/AAS_Shared.c | 737 +++++++++++++++++++++++++++ libs/aas/AAS_Shared.h | 83 +++ libs/aas/Makefile | 21 + src/data.h | 6 + src/intr.c | 4 +- src/intr.h | 4 +- src/main.c | 11 + tools/conv2aas/Makefile | 11 + tools/conv2aas/conv2aas.c | 1247 +++++++++++++++++++++++++++++++++++++++++++++ 18 files changed, 5111 insertions(+), 8 deletions(-) create mode 100644 libs/aas/AAS.h create mode 100644 libs/aas/AAS_ASM.s create mode 100644 libs/aas/AAS_MOD.c create mode 100644 libs/aas/AAS_Main.c create mode 100644 libs/aas/AAS_Mixer.h create mode 100644 libs/aas/AAS_Mixer.s create mode 100644 libs/aas/AAS_SFX.c create mode 100644 libs/aas/AAS_Shared.c create mode 100644 libs/aas/AAS_Shared.h create mode 100644 libs/aas/Makefile create mode 100644 src/data.h create mode 100644 tools/conv2aas/Makefile create mode 100644 tools/conv2aas/conv2aas.c diff --git a/.gitignore b/.gitignore index b6fae84..d7a3666 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,6 @@ cfg.mk *.gba *.elf +*.a pngdump +conv2aas diff --git a/Makefile b/Makefile index a312a03..3d0b9d4 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,16 @@ src = $(wildcard src/*.c) ssrc = $(wildcard src/*.s) -obj = $(src:.c=.o) $(ssrc:.s=.o) +obj = $(src:.c=.o) $(ssrc:.s=.o) $(dataobj) dep = $(src:.c=.d) name = gbajam21 elf = $(name).elf bin = $(name).gba +audata = data/audio/popcorn.mod +dataobj = data/aas_data.o + +libs = libs/aas/libaas.a + TCPREFIX = arm-none-eabi- CPP = $(TCPREFIX)cpp @@ -17,10 +22,11 @@ EMU = vbam opt = -O3 -fomit-frame-pointer -mcpu=arm7tdmi -mtune=arm7tdmi -mthumb -mthumb-interwork #dbg = -g +inc = -I. -Ilibs/aas -CFLAGS = $(opt) $(dbg) -pedantic -Wall -MMD $(def) +CFLAGS = $(opt) $(dbg) -pedantic -Wall -MMD $(def) $(inc) ASFLAGS = -mthumb-interwork -LDFLAGS = -mthumb -mthumb-interwork +LDFLAGS = -mthumb -mthumb-interwork $(libs) EMUFLAGS = -T 100 -f 1 --agb-print -include cfg.mk @@ -32,7 +38,7 @@ $(bin): $(elf) $(OBJCOPY) -O binary $(elf) $(bin) gbafix -r0 $(bin) -$(elf): $(obj) +$(elf): $(obj) $(libs) $(CC) -o $(elf) $(obj) -specs=gba.specs $(LDFLAGS) -include $(dep) @@ -40,6 +46,9 @@ $(elf): $(obj) tools/pngdump/pngdump: $(MAKE) -C tools/pngdump +tools/conv2aas/conv2aas: + $(MAKE) -C tools/conv2aas + #data/sprites.raw: data/sprites1.png data/sprites2.png data/sprites3.png data/sprites4.png data/sprites5.png data/sprites6.png # tools/pngdump/pngdump -o $@ -n $^ @@ -49,6 +58,11 @@ tools/pngdump/pngdump: %.pal: %.png tools/pngdump/pngdump tools/pngdump/pngdump -o $@ -c $< +data/aas_data.h: data/aas_data.s + +data/aas_data.s: $(audata) tools/conv2aas/conv2aas + tools/conv2aas/conv2aas data/audio + .PHONY: clean clean: rm -f $(obj) $(bin) $(bin_mb) $(elf) $(elf_mb) @@ -57,6 +71,10 @@ clean: cleandep: rm -f $(dep) +.PHONY: cleanlibs +cleanlibs: + $(MAKE) -C libs/aas clean + .PHONY: install install: $(bin) if2a -n -f -W $< @@ -72,3 +90,9 @@ simrun: $(bin) .PHONY: disasm disasm: $(elf) $(OBJDUMP) -d $< >$@ + +.PHONY: libs +libs: $(libs) + +libs/aas/libaas.a: + $(MAKE) -C libs/aas diff --git a/libs/aas/AAS.h b/libs/aas/AAS.h new file mode 100644 index 0000000..e92d549 --- /dev/null +++ b/libs/aas/AAS.h @@ -0,0 +1,121 @@ +/* Copyright (c) 2003-2021 James Daniels */ +/* Distributed under the MIT License */ +/* license terms: see LICENSE file in root or http://opensource.org/licenses/MIT */ + +/* Main AAS include */ +/* */ +/* See API documentation for more information. */ + +#ifndef __AAS__ +#define __AAS__ + +#ifdef __cplusplus +#define AAS_BEGIN_DECLS extern "C" { +#define AAS_END_DECLS } +#else +#define AAS_BEGIN_DECLS +#define AAS_END_DECLS +#endif + +AAS_BEGIN_DECLS + +#define AAS_VERSION 0x111 /* v1.11 */ + +/* Types */ +#define AAS_u32 unsigned int +#define AAS_s32 signed int +#define AAS_u16 unsigned short +#define AAS_s16 signed short +#define AAS_u8 unsigned char +#define AAS_s8 signed char +#define AAS_BOOL unsigned char +#define AAS_TRUE 1 +#define AAS_FALSE 0 +#define AAS_NULL 0 + +/* Return values */ +#define AAS_OK 0 +#define AAS_ERROR_VOLUME_OUT_OF_RANGE -1 +#define AAS_ERROR_CHANNEL_NOT_AVAILABLE -2 +#define AAS_ERROR_FREQUENCY_OUT_OF_RANGE -3 +#define AAS_ERROR_MOD_DOES_NOT_EXIST -4 +#define AAS_ERROR_CALL_SET_CONFIG_FIRST -5 +#define AAS_ERROR_INVALID_CONFIG -6 +#define AAS_ERROR_INVALID_SAMPLE_ADDRESS -7 +#define AAS_ERROR_NO_MOD_PLAYING -8 +#define AAS_ERROR_NOT_ENOUGH_CHANNELS -9 +#define AAS_ERROR_CHANNEL_ACTIVE -10 +#define AAS_ERROR_CHANNEL_UNRESUMEABLE -11 +#define AAS_ERROR_INVALID_SONG_POS -12 + +/* AAS_SetConfig() mix settings */ +#define AAS_CONFIG_MIX_32KHZ 1 +#define AAS_CONFIG_MIX_28KHZ 2 +#define AAS_CONFIG_MIX_24KHZ 3 +#define AAS_CONFIG_MIX_20KHZ 4 +#define AAS_CONFIG_MIX_16KHZ 5 +#define AAS_CONFIG_MIX_12KHZ 6 +#define AAS_CONFIG_MIX_8KHZ 7 + +/* AAS_SetConfig() channel settings */ +#define AAS_CONFIG_CHANS_16_LOUD 6 +#define AAS_CONFIG_CHANS_8_LOUD 5 +#define AAS_CONFIG_CHANS_4_LOUD 4 +#define AAS_CONFIG_CHANS_16 3 +#define AAS_CONFIG_CHANS_8 2 +#define AAS_CONFIG_CHANS_4 1 + +/* AAS_SetConfig() spatial settings */ +#define AAS_CONFIG_SPATIAL_STEREO 2 +#define AAS_CONFIG_SPATIAL_MONO 1 + +/* AAS_SetConfig() dynamic mixing settings */ +#define AAS_CONFIG_DYNAMIC_OFF 0 +#define AAS_CONFIG_DYNAMIC_ON 1 + +/* General commands */ +int AAS_SetConfig( int config_mix, int config_chans, int config_spatial, int config_dynamic ); /* Must call at least once before doing anything else */ +void AAS_DoDMA3( void* source, void* dest, AAS_u32 flags_and_length ); +void AAS_ShowLogo(); + +/* Interrupt handling commands */ +void AAS_Timer1InterruptHandler(); /* Use when there are no other CPU-intensive interrupts */ +void AAS_FastTimer1InterruptHandler(); /* Use when there are other CPU-intensive interrupts */ +void AAS_DoWork(); /* Must be called at least 50 times/sec if using AAS_FastTimer1InterruptHandler() */ + +/* Sample playing commands */ +int AAS_SFX_Play( int channel, int sample_volume, int sample_frequency, const AAS_s8* sample_start, const AAS_s8* sample_end, const AAS_s8* sample_restart ); +AAS_BOOL AAS_SFX_ChannelExists( int channel ); /* returns AAS_TRUE only if AAS_SFX_Play will succeed for this channel */ +AAS_BOOL AAS_SFX_IsActive( int channel ); /* returns AAS_TRUE if channel is valid and active, AAS_FALSE otherwise */ +int AAS_SFX_EndLoop( int channel ); /* If sample was looping, will stop at end of current iteration */ +int AAS_SFX_SetFrequency( int channel, int sample_frequency ); +int AAS_SFX_SetVolume( int channel, int sample_volume ); +int AAS_SFX_Stop( int channel ); +int AAS_SFX_Resume( int channel ); +int AAS_SFX_GetNumChannels(); /* returns number of SFX channels */ + +/* MOD commands */ +int AAS_MOD_Play( int song_num ); /* loops by default */ +int AAS_MOD_SetLoop( AAS_BOOL loop ); /* specify whether current song will loop */ +void AAS_MOD_Stop(); +AAS_BOOL AAS_MOD_IsPlaying(); /* is a song playing? */ +AAS_BOOL AAS_MOD_HasLooped(); /* has the current song looped? */ +int AAS_MOD_GetVolume(); /* 0: silent, 256 = max */ +int AAS_MOD_SetVolume( int vol ); /* 0: silent, 256 = max (do not set above 256 or below 0!) */ +int AAS_MOD_GetSongPos(); +int AAS_MOD_SetSongPos( int song_pos ); /* Immediately jumps to the specified song position. */ +int AAS_MOD_QueueSongPos( int song_pos ); /* Jumps to the specified song position when the current pattern finishes. */ +int AAS_MOD_GetLineNum(); +int AAS_MOD_GetLastFilterValue(); /* Returns the value specified by the most recent "E0: Set Filter" effect */ +void AAS_MOD_Pause(); /* Stops the MOD in a way that allows it to be safely resumed */ +void AAS_MOD_Resume(); /* Should only be used after AAS_MOD_Pause() */ +int AAS_MOD_GetNumChannels(); /* Returns number of channels currently being reserved by the MOD */ + +/* Misc commands */ +const AAS_s8* AAS_GetOutputBufferAddress( int buffer ); /* buffer should be 0 or 1, otherwise will return AAS_NULL */ +int AAS_GetOutputBufferLength(); +int AAS_GetActualMixRate(); + +AAS_END_DECLS + +#endif diff --git a/libs/aas/AAS_ASM.s b/libs/aas/AAS_ASM.s new file mode 100644 index 0000000..bf99d4a --- /dev/null +++ b/libs/aas/AAS_ASM.s @@ -0,0 +1,299 @@ +@ Copyright (c) 2003-2021 James Daniels +@ Distributed under the MIT License +@ license terms: see LICENSE file in root or http://opensource.org/licenses/MIT + +.TEXT +.SECTION .text,"ax",%progbits +.ALIGN +.ARM + +.GLOBAL AAS_DoDMA3 + +.GLOBAL AAS_MixAudio_SetMode_Normal +.GLOBAL AAS_MixAudio_SetMode_Boost +.GLOBAL AAS_MixAudio_SetMode_BoostAndClip + +.GLOBAL AAS_MixAudio_SetMaxChans_2 +.GLOBAL AAS_MixAudio_SetMaxChans_4 +.GLOBAL AAS_MixAudio_SetMaxChans_8 + +.GLOBAL _AAS_vol_lookup + + +@ Volume lookup table. -1 means use multiply, 0 to 7 means use bit shift. + +_AAS_vol_lookup: + .byte 0, 1, -1, 2, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1, 4 + .byte -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 5 + .byte -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + .byte -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 6 + .byte -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + .byte -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + .byte -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + .byte -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 7 + +_ma_mul_r5_r0_r3: + .word 0x00000010 + mul r5,r0,r3 +_ma_mov_r5_r0_lsl_0: + .word 0x00000001 + mov r5,r0,lsl #0 +_ma_mlane_r5_r0_r3_r5: + .word 0x00000011 + mlane r5,r0,r3,r5 +_ma_add_r5_r5_r0_lsl_0: + .word 0x00000011 + add r5,r5,r0,lsl #0 + + +_ma_merge_noclip_start: + @ ma_again: + + @ Merge + ldr r14,[sp,#24] + and r5,r14,r5,lsr #8 + and r6,r14,r6,lsr #8 + and r7,r14,r7,lsr #8 + and r8,r14,r8,lsr #8 + and r9,r14,r9,lsr #8 + and r10,r14,r10,lsr #8 + and r11,r14,r11,lsr #8 + and r12,r14,r12,lsr #8 + add r5,r5,r6,lsl #8 + add r6,r7,r8,lsl #8 + add r7,r9,r10,lsl #8 + add r8,r11,r12,lsl #8 + + @ Store + stmia r4!,{r5-r8} + + @ Loop + subs r14,r14,#0x2000000 + .word 0xdaffffa5 @ ble ma_end + + @ ma_start: + str r14,[sp,#24] +_ma_merge_noclip_end: + + +_ma_merge_boostnoclip_start: + @ ma_again: + + @ Merge + ldr r14,[sp,#24] + and r5,r14,r5,lsr #7 + and r6,r14,r6,lsr #7 + and r7,r14,r7,lsr #7 + and r8,r14,r8,lsr #7 + and r9,r14,r9,lsr #7 + and r10,r14,r10,lsr #7 + and r11,r14,r11,lsr #7 + and r12,r14,r12,lsr #7 + add r5,r5,r6,lsl #8 + add r6,r7,r8,lsl #8 + add r7,r9,r10,lsl #8 + add r8,r11,r12,lsl #8 + + @ Store + stmia r4!,{r5-r8} + + @ Loop + subs r14,r14,#0x2000000 + .word 0xdaffffa5 @ ble ma_end + + @ ma_start: + str r14,[sp,#24] +_ma_merge_boostnoclip_end: + + +_ma_merge_clip_start: + _ma_mask_0x80808080: .word 0x80808080 + + .word 0,0 @ padding + + @ ma_again: + @ Merge + ldr r14,[sp,#24] + .word 0xe51f2018 @ ldr r2,_ma_mask_0x80808080 + + and r0,r14,r5,lsr #8 + and r1,r14,r6,lsr #8 + add r0,r0,r1,lsl #8 + and r5,r14,r5,lsr #7 + and r6,r14,r6,lsr #7 + add r5,r5,r6,lsl #8 + eor r0,r0,r5 + ands r0,r2,r0 + beq no_clip1 @ perhaps not worthwhile? + and r1,r2,r5 + sub r1,r2,r1,lsr #7 + sub r0,r0,r0,lsr #7 + orr r0,r0,r0,lsl #1 + bic r5,r5,r0 + and r1,r1,r0 + orr r5,r5,r1 +no_clip1: + + and r0,r14,r7,lsr #8 + and r1,r14,r8,lsr #8 + add r0,r0,r1,lsl #8 + and r7,r14,r7,lsr #7 + and r8,r14,r8,lsr #7 + add r6,r7,r8,lsl #8 + eor r0,r0,r6 + ands r0,r2,r0 + beq no_clip2 @ perhaps not worthwhile? + and r1,r2,r6 + sub r1,r2,r1,lsr #7 + sub r0,r0,r0,lsr #7 + orr r0,r0,r0,lsl #1 + bic r6,r6,r0 + and r1,r1,r0 + orr r6,r6,r1 +no_clip2: + + and r0,r14,r9,lsr #8 + and r1,r14,r10,lsr #8 + add r0,r0,r1,lsl #8 + and r9,r14,r9,lsr #7 + and r10,r14,r10,lsr #7 + add r7,r9,r10,lsl #8 + eor r0,r0,r7 + ands r0,r2,r0 + beq no_clip3 @ perhaps not worthwhile? + and r1,r2,r7 + sub r1,r2,r1,lsr #7 + sub r0,r0,r0,lsr #7 + orr r0,r0,r0,lsl #1 + bic r7,r7,r0 + and r1,r1,r0 + orr r7,r7,r1 +no_clip3: + + and r0,r14,r11,lsr #8 + and r1,r14,r12,lsr #8 + add r0,r0,r1,lsl #8 + and r11,r14,r11,lsr #7 + and r12,r14,r12,lsr #7 + add r8,r11,r12,lsl #8 + eor r0,r0,r8 + ands r0,r2,r0 + beq no_clip4 @ perhaps not worthwhile? + and r1,r2,r8 + sub r1,r2,r1,lsr #7 + sub r0,r0,r0,lsr #7 + orr r0,r0,r0,lsl #1 + bic r8,r8,r0 + and r1,r1,r0 + orr r8,r8,r1 +no_clip4: + + @ Store + stmia r4!,{r5-r8} + + @ Loop + subs r14,r14,#0x2000000 + .word 0xdaffff6d @ ble ma_end + + @ ma_start: + str r14,[sp,#24] +_ma_merge_clip_end: + +_ma_clip: +add r12,pc,#0x540 @ adr r12,ma_buffer_start +.word 0xea000091 @ b ma_start +add r0,pc,#0x248 @ adr r0,ma_again + +_ma_noclip: +add r12,pc,#0x460 @ adr r12,ma_buffer_start +.word 0xea000059 @ b ma_start +add r0,pc,#0x23c @ adr r0,ma_again + + +AAS_MixAudio_SetMode_BoostAndClip: + adr r12,_ma_clip + adr r0,_ma_merge_clip_start + mov r2,#((_ma_merge_clip_end-_ma_merge_clip_start)/4) + +do_mods: + ldr r1,=_AAS_MixAudio_mod4 + add r2,r2,#0x84000000 + mov r3,#0x04000000 + add r3,r3,#0xd4 + stmia r3,{r0-r2} + ldmia r12,{r1-r3} + ldr r0,=_AAS_MixAudio_mod1 + str r1,[r0] + ldr r0,=_AAS_MixAudio_mod2 + str r2,[r0] + ldr r0,=_AAS_MixAudio_mod3 + str r3,[r0] + bx lr + + +AAS_MixAudio_SetMode_Normal: + adr r12,_ma_noclip + adr r0,_ma_merge_noclip_start + mov r2,#((_ma_merge_noclip_end-_ma_merge_noclip_start)/4) + b do_mods + + +AAS_MixAudio_SetMode_Boost: + adr r12,_ma_noclip + adr r0,_ma_merge_boostnoclip_start + mov r2,#((_ma_merge_boostnoclip_end-_ma_merge_boostnoclip_start)/4) + b do_mods + + +_ma_2ch: +mov r6,#0x10000000 +mov r10,#2 +mov r14,#2 +sub r1,r1,#(20*2) + +_ma_4ch: +mov r6,#0x30000000 +mov r10,#4 +mov r14,#4 +sub r1,r1,#(20*4) + +_ma_8ch: +mov r6,#0x70000000 +mov r10,#8 +mov r14,#8 +sub r1,r1,#(20*8) + + +AAS_MixAudio_SetMaxChans_4: + adr r12,_ma_4ch + +do_mods2: + ldmia r12,{r0-r3} + ldr r12,=_AAS_MixAudio_mod5 + str r0,[r12] + ldr r12,=_AAS_MixAudio_mod6 + str r1,[r12] + ldr r12,=_AAS_MixAudio_mod7 + str r2,[r12] + ldr r12,=_AAS_MixAudio_mod8 + str r3,[r12] + bx lr + + +AAS_MixAudio_SetMaxChans_8: + adr r12,_ma_8ch + b do_mods2 + + +AAS_MixAudio_SetMaxChans_2: + adr r12,_ma_2ch + b do_mods2 + +.pool + + +AAS_DoDMA3: + mov r3,#0x04000000 + add r3,r3,#0xd4 + stmia r3,{r0-r2} + bx lr diff --git a/libs/aas/AAS_MOD.c b/libs/aas/AAS_MOD.c new file mode 100644 index 0000000..a6a2e2b --- /dev/null +++ b/libs/aas/AAS_MOD.c @@ -0,0 +1,932 @@ +/* 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" + +AAS_s16 AAS_mod_num AAS_IN_EWRAM = -1; +AAS_u8 AAS_mod_num_chans AAS_IN_EWRAM = 0; + +static AAS_s16 AAS_mod_num_store AAS_IN_EWRAM = -2; +static AAS_u16 AAS_mod_song_pos AAS_IN_EWRAM; +static AAS_u16 AAS_mod_line_num AAS_IN_EWRAM; + +struct AAS_MOD_Channel { + AAS_u32 *pattern; + const AAS_u8 *samp_start; + AAS_u16 effect; + AAS_u16 period; + AAS_u16 slide_target_period; + AAS_u8 samp_num; + AAS_u8 slide_rate; + AAS_u8 volume; + AAS_u8 vibrato_rate; + AAS_u8 vibrato_depth; + AAS_u8 vibrato_pos; + AAS_u8 tremolo_rate; + AAS_u8 tremolo_depth; + AAS_u8 tremolo_pos; + AAS_s8 trigger; + AAS_u8 arpeggio_pos; + AAS_u8 note; +}; /* 28 bytes */ + +static struct AAS_MOD_Channel AAS_mod_chan[AAS_MAX_CHANNELS] AAS_IN_EWRAM; + +static AAS_s32 AAS_mod_timer AAS_IN_EWRAM; +static AAS_u32 AAS_mod_tempo AAS_IN_EWRAM; +static AAS_u8 AAS_mod_bpm AAS_IN_EWRAM; +static AAS_u8 AAS_mod_speed AAS_IN_EWRAM; +static AAS_BOOL AAS_mod_looped AAS_IN_EWRAM = AAS_FALSE; +static AAS_s16 AAS_mod_overall_volume AAS_IN_EWRAM = 256; +static AAS_u8 AAS_mod_loop_start AAS_IN_EWRAM = 0; +static AAS_s8 AAS_mod_loop_counter AAS_IN_EWRAM = 0; +static AAS_BOOL AAS_mod_loop AAS_IN_EWRAM = AAS_TRUE; +static AAS_u8 AAS_mod_last_filter_value AAS_IN_EWRAM = 0; +static AAS_BOOL AAS_mod_active_effects AAS_IN_EWRAM = AAS_FALSE; +static AAS_s8 AAS_mod_next_song_pos AAS_IN_EWRAM = -1; + +int AAS_MOD_GetNumChannels() +{ + return AAS_mod_num_chans; +} + +int AAS_MOD_GetLastFilterValue() +{ + return AAS_mod_last_filter_value; +} + +int AAS_MOD_QueueSongPos(int song_pos) +{ + if(AAS_mod_num >= 0) { + if((song_pos >= 0) && (song_pos < 128)) { + if(AAS_Sequence[AAS_mod_num][song_pos][0] == -1) { + return AAS_ERROR_INVALID_SONG_POS; + } else { + AAS_mod_next_song_pos = song_pos; + + return AAS_OK; + } + } else { + return AAS_ERROR_INVALID_SONG_POS; + } + } else { + return AAS_ERROR_NO_MOD_PLAYING; + } +} + +int AAS_MOD_SetSongPos(int song_pos) +{ + if(AAS_mod_num >= 0) { + if((song_pos >= 0) && (song_pos < 128)) { + if(AAS_Sequence[AAS_mod_num][song_pos][0] == -1) { + return AAS_ERROR_INVALID_SONG_POS; + } else { + struct AAS_MOD_Channel *mod_chan = AAS_mod_chan; + int chan; + + for(chan = 0; chan < AAS_mod_num_chans; ++chan) { + mod_chan->pattern = + (AAS_u32 *) (AAS_PatternData + + (((int) + AAS_Sequence[AAS_mod_num][song_pos] + [chan]) << 8)); + ++mod_chan; + } + AAS_mod_line_num = 0; + AAS_mod_song_pos = song_pos; + + return AAS_OK; + } + } else { + return AAS_ERROR_INVALID_SONG_POS; + } + } else { + return AAS_ERROR_NO_MOD_PLAYING; + } +} + +int AAS_MOD_GetSongPos() +{ + if(AAS_mod_num >= 0) + return AAS_mod_song_pos; + else + return AAS_ERROR_NO_MOD_PLAYING; +} + +int AAS_MOD_GetLineNum() +{ + if(AAS_mod_num >= 0) + return AAS_mod_line_num; + else + return AAS_ERROR_NO_MOD_PLAYING; +} + +int AAS_MOD_GetVolume() +{ + return AAS_mod_overall_volume; +} + +int AAS_MOD_SetVolume(int vol) +{ + if((vol >= 0) && (vol <= 256)) { + int i; + + for(i = AAS_mod_num_chans - 1; i >= 0; --i) + AAS_channels[AAS_chan_rearrange[i]].volume = (AAS_mod_chan[i].volume * vol) >> AAS_volscale; + + AAS_mod_overall_volume = vol; + + return AAS_OK; + } else { + return AAS_ERROR_VOLUME_OUT_OF_RANGE; + } +} + +void AAS_MOD_Pause() +{ + int i; + + AAS_mod_num_store = AAS_mod_num; + AAS_mod_num = -1; + + for(i = 0; i < AAS_mod_num_chans; ++i) { + AAS_channels[AAS_chan_rearrange[i]].active = AAS_FALSE; + } +} + +void AAS_MOD_Resume() +{ + if(AAS_mod_num_store != -2) { + struct AAS_Channel *ch; + int i; + + for(i = 0; i < AAS_mod_num_chans; ++i) { + ch = &AAS_channels[AAS_chan_rearrange[i]]; + + if(!ch->active) { + if(ch->loop_length) { + ch->active = AAS_TRUE; + } else { + if(ch->pos < ((ch->end - (ch->delta >> 6)) - 1)) { + ch->active = AAS_TRUE; + } + } + } + } + + AAS_mod_num = AAS_mod_num_store; + } +} + +void AAS_MOD_Stop() +{ + int i; + struct AAS_Channel *ch; + + AAS_mod_num_store = -2; + AAS_mod_num = -1; + AAS_mod_next_song_pos = -1; + + for(i = 0; i < AAS_mod_num_chans; ++i) { + ch = &AAS_channels[AAS_chan_rearrange[i]]; + ch->active = AAS_FALSE; + ch->loop_length = 0; + ch->pos = 0; + ch->end = 0; + ch->delta = 0; + AAS_mod_chan[i].tremolo_pos = 0; + AAS_mod_chan[i].vibrato_pos = 0; + AAS_mod_chan[i].effect = 0; + AAS_mod_chan[i].volume = 0; + AAS_mod_chan[i].trigger = -1; + AAS_mod_chan[i].note = 0; + AAS_mod_chan[i].slide_target_period = 0; + } + AAS_mod_active_effects = AAS_FALSE; + AAS_mod_looped = AAS_FALSE; + AAS_mod_loop_counter = 0; + AAS_mod_loop_start = 0; + AAS_mod_last_filter_value = 0; + AAS_mod_num_chans = 0; +} + +AAS_BOOL AAS_MOD_HasLooped() +{ + return AAS_mod_looped; +} + +AAS_BOOL AAS_MOD_IsPlaying() +{ + return (AAS_mod_num >= 0); +} + +int AAS_MOD_Play(int song_num) +{ + AAS_MOD_Stop(); + + if(AAS_initialised) { + if((song_num >= 0) && (song_num < AAS_DATA_NUM_MODS) && AAS_NumChans[song_num]) { + int i; + + if(AAS_volscale == 9) + i = 16; + else if(AAS_volscale == 8) + i = 8; + else + i = 4; + + if(AAS_NumChans[song_num] > i) { + return AAS_ERROR_NOT_ENOUGH_CHANNELS; + } else { + /*AAS_mod_num = 0; */ + AAS_mod_loop = AAS_TRUE; + AAS_mod_num_store = -2; + AAS_mod_song_pos = 0; + AAS_mod_num_chans = AAS_NumChans[song_num]; + + for(i = 0; i < AAS_mod_num_chans; ++i) + AAS_mod_chan[i].pattern = (AAS_u32 *) (AAS_PatternData + (((int)AAS_Sequence[song_num][0][i]) << 8)); + + AAS_mod_line_num = 0; + AAS_mod_speed = 6; + AAS_mod_bpm = 125; + AAS_mod_tempo = AAS_DivTable[AAS_mod_speed] * AAS_mod_bpm; + AAS_mod_timer = 0x7d0000 - AAS_mod_tempo; + AAS_mod_num = song_num; + return AAS_OK; + } + } else { + return AAS_ERROR_MOD_DOES_NOT_EXIST; + } + } else { + return AAS_ERROR_CALL_SET_CONFIG_FIRST; + } +} + +int AAS_MOD_SetLoop(AAS_BOOL loop) +{ + if(AAS_initialised) { + if(AAS_mod_num >= 0) { + AAS_mod_loop = loop; + return AAS_OK; + } else { + return AAS_ERROR_NO_MOD_PLAYING; + } + } else { + return AAS_ERROR_CALL_SET_CONFIG_FIRST; + } +} + +void AAS_MOD_Interrupt() +{ + if(AAS_mod_num >= 0) { + AAS_mod_timer += AAS_mod_tempo; + + if(AAS_mod_timer < 0x7d0000) { + if(AAS_mod_active_effects) { + const AAS_u8 *chan_rearrange = AAS_chan_rearrange; + struct AAS_MOD_Channel *mod_chan = AAS_mod_chan; + int chan, num_chans; + + num_chans = AAS_mod_num_chans; + + for(chan = 0; chan < num_chans; ++chan) { + int effect; + + effect = mod_chan->effect; + + if(effect) { + int val, tmp, output; + struct AAS_Channel *out_chan; + + tmp = chan_rearrange[chan]; + out_chan = &AAS_channels[tmp]; + output = tmp >> 3; + + switch (effect >> 8) { + case 0xe: /* extended effects */ + switch (effect & 0xf0) { + case 0xc0: /* note cut */ + val = effect & 0xf; + --val; + if(val <= 0) { + mod_chan->effect = 0; + out_chan->active = AAS_FALSE; + + AAS_changed[output] = AAS_TRUE; + } else { + mod_chan->effect = (effect & 0xff0) + val; + } + break; + + case 0x90: /* retrigger sample */ + case 0xd0: /* delay sample */ + if(mod_chan->trigger >= 0) { + --mod_chan->trigger; + if(mod_chan->trigger < 0) { + const struct AAS_ModSample *samp = &AAS_ModSamples[AAS_mod_num][mod_chan->samp_num]; + int repeat = samp->repeat; + int length = samp->length; + const AAS_s8 *data = AAS_SampleData + samp->data; + + mod_chan->samp_start = data; + out_chan->pos = data; + out_chan-> pos_fraction = 0; + if(repeat == 65535) + out_chan->loop_length = 0; + else + out_chan->loop_length = ((AAS_u32) (length - repeat)) << 1; + out_chan->end = data + (length << 1); + out_chan->frequency = AAS_MOD_period_conv_table[mod_chan->period]; + out_chan->delta = AAS_Min(4095, ((out_chan->frequency * AAS_mix_scale) + 32768) >> 16); + out_chan->active = AAS_TRUE; + + AAS_changed[output] = AAS_TRUE; + } + } + break; + + default: + break; + } + break; + + case 0x6: /* vibrato + volume slide */ + val = mod_chan->vibrato_pos; + val += mod_chan->vibrato_rate; + if(val >= 64) + val -= 64; + mod_chan->vibrato_pos = val; + tmp = mod_chan->period + (((AAS_sin[val] * mod_chan->vibrato_depth) + 32) >> 6); + if(tmp < 113) + tmp = 113; + if(tmp > 856) + tmp = 856; + out_chan->frequency = AAS_MOD_period_conv_table[tmp]; + out_chan->delta = AAS_Min(4095, ((out_chan->frequency * AAS_mix_scale) + 32768) >> 16); + /* Intentionally no "break;" */ + + case 0xa: /* volume slide */ + val = effect & 0xf0; + if(val) + val >>= 4; + else + val = -(effect & 0xf); + tmp = mod_chan->volume; + tmp += val; + if(tmp > 64) + tmp = 64; + else if(tmp < 0) + tmp = 0; + mod_chan->volume = tmp; + out_chan->volume = (tmp * AAS_mod_overall_volume) >> AAS_volscale; + + AAS_changed[output] = AAS_TRUE; + break; + + case 0x1: /* slide up */ + val = effect & 0xff; + tmp = mod_chan->period - val; + if(tmp < 113) + tmp = 113; + mod_chan->period = tmp; + out_chan->frequency = AAS_MOD_period_conv_table[tmp]; + out_chan->delta = AAS_Min(4095, ((out_chan->frequency * AAS_mix_scale) + 32768) >> 16); + + AAS_changed[output] = AAS_TRUE; + break; + + case 0x2: /* slide down */ + val = effect & 0xff; + tmp = mod_chan->period + val; + if(tmp > 856) + tmp = 856; + mod_chan->period = tmp; + out_chan->frequency = AAS_MOD_period_conv_table[tmp]; + out_chan->delta = AAS_Min(4095, ((out_chan->frequency * AAS_mix_scale) + 32768) >> 16); + + AAS_changed[output] = AAS_TRUE; + break; + + case 0x3: /* tone portamento */ + val = mod_chan->period; + tmp = mod_chan->slide_target_period; + if(val < tmp) { + val += mod_chan->slide_rate; + if(val >= tmp) { + mod_chan->effect = 0; + val = tmp; + } + } else if(val > tmp) { + val -= mod_chan->slide_rate; + if(val <= tmp) { + mod_chan->effect = 0; + val = tmp; + } + } else if(val == tmp) { + mod_chan->effect = 0; + } + mod_chan->period = val; + out_chan->frequency = AAS_MOD_period_conv_table[val]; + out_chan->delta = AAS_Min(4095, ((out_chan->frequency * AAS_mix_scale) + 32768) >> 16); + + AAS_changed[output] = AAS_TRUE; + break; + + case 0x5: /* tone portamento + volume slide */ + val = mod_chan->period; + tmp = mod_chan->slide_target_period; + if(val < tmp) { + val += mod_chan->slide_rate; + if(val >= tmp) + val = tmp; + } else if(val > tmp) { + val -= mod_chan->slide_rate; + if(val <= tmp) + val = tmp; + } + mod_chan->period = val; + out_chan->frequency = AAS_MOD_period_conv_table[val]; + out_chan->delta = AAS_Min(4095, ((out_chan->frequency * AAS_mix_scale) + 32768) >> 16); + + val = effect & 0xf0; + if(val) + val >>= 4; + else + val = -(effect & 0xf); + tmp = mod_chan->volume; + tmp += val; + if(tmp > 64) + tmp = 64; + else if(tmp < 0) + tmp = 0; + mod_chan->volume = tmp; + out_chan->volume = (tmp * AAS_mod_overall_volume) >> AAS_volscale; + + AAS_changed[output] = AAS_TRUE; + break; + + case 0x4: /* vibrato */ + val = mod_chan->vibrato_pos; + val += mod_chan->vibrato_rate; + if(val >= 64) + val -= 64; + mod_chan->vibrato_pos = val; + tmp = mod_chan->period + (((AAS_sin[val] * mod_chan->vibrato_depth) + 32) >> 6); + if(tmp < 113) + tmp = 113; + else if(tmp > 856) + tmp = 856; + out_chan->frequency = AAS_MOD_period_conv_table[tmp]; + out_chan->delta = AAS_Min(4095, ((out_chan->frequency * AAS_mix_scale) + 32768) >> 16); + + AAS_changed[output] = AAS_TRUE; + break; + + case 0x7: /* tremolo */ + val = mod_chan->tremolo_pos; + val += mod_chan->tremolo_rate; + if(val >= 64) + val -= 64; + mod_chan->tremolo_pos = val; + tmp = mod_chan->volume + (((AAS_sin[val] * mod_chan->tremolo_depth) + 32) >> 6); + if(tmp < 0) + tmp = 0; + else if(tmp > 64) + tmp = 64; + out_chan->volume = (tmp * AAS_mod_overall_volume) >> AAS_volscale; + + AAS_changed[output] = AAS_TRUE; + break; + + case 0x0: /* possible arpeggio */ + tmp = effect & 0xff; + if(tmp) /* definite arpeggio */ + { + ++mod_chan->arpeggio_pos; + val = mod_chan->note; + switch (mod_chan->arpeggio_pos) { + case 0: + break; + + case 1: + val += tmp >> 4; + break; + + case 2: + val += tmp & 0xf; + /* Intentionally no "break;" to allow AAS_mod_arpeggio_pos[chan] to be restarted */ + + default: + mod_chan->arpeggio_pos = 0; + break; + } + + if(val) { + out_chan->frequency = AAS_MOD_period_conv_table[AAS_period_table[AAS_ModSamples[AAS_mod_num][mod_chan->samp_num].finetune][val - 1]]; + out_chan->delta = AAS_Min(4095, ((out_chan-> frequency * AAS_mix_scale) + 32768) >> 16); + + AAS_changed[output] = AAS_TRUE; + } + } + break; + + default: + break; + } + } + + ++mod_chan; + } + } + } else { + const AAS_u8 *chan_rearrange = AAS_chan_rearrange; + struct AAS_MOD_Channel *mod_chan = AAS_mod_chan; + int chan, num_chans; + int samp_num, effect, period, speed, tmp; + int jump_ahead = -1; + int jump_song_pos = -1; + AAS_BOOL active_effects = AAS_FALSE; + const struct AAS_ModSample *mod_samp = AAS_ModSamples[AAS_mod_num]; + + num_chans = AAS_mod_num_chans; + + AAS_mod_timer -= 0x7d0000; + + for(chan = 0; chan < num_chans; ++chan) { + int output; + struct AAS_Channel *out_chan; + AAS_u32 dat; + + tmp = chan_rearrange[chan]; + out_chan = &AAS_channels[tmp]; + output = tmp >> 3; + + /* Tidy up after arpeggio */ + effect = mod_chan->effect; + if(effect) { + if(effect < 0x100) { + tmp = mod_chan->note; + if(tmp) { + out_chan->frequency = AAS_MOD_period_conv_table[AAS_period_table[mod_samp[mod_chan->samp_num].finetune][tmp - 1]]; + out_chan->delta = AAS_Min(4095, ((out_chan->frequency * AAS_mix_scale) + 32768) >> 16); + + AAS_changed[output] = AAS_TRUE; + } + } + } + + dat = *mod_chan->pattern++; + samp_num = (dat >> 24) - 1; + period = (dat >> 12) & 0xfff; + effect = dat & 0xfff; + + if(samp_num >= 0) { + mod_chan->samp_num = samp_num; + mod_chan->volume = tmp = mod_samp[samp_num].volume; + out_chan->volume = (tmp * AAS_mod_overall_volume) >> AAS_volscale; + + AAS_changed[output] = AAS_TRUE; + } else { + samp_num = mod_chan->samp_num; + } + + if(period) { + if(samp_num >= 0) { + const struct AAS_ModSample *samp = &mod_samp[samp_num]; + + mod_chan->note = period; + period = AAS_period_table[samp->finetune][period - 1]; + + if((effect > 0xed0) && (effect < 0xee0)) /* delay sample */ + { + mod_chan->period = period; + mod_chan->trigger = (effect & 0xf) - 1; + } else { + tmp = effect >> 8; + + if((tmp != 0x3) && (tmp != 0x5)) { + int repeat = samp->repeat; + int length = samp->length; + const AAS_s8 *data = AAS_SampleData + samp->data; + + mod_chan->samp_start = data; + out_chan->pos = data; + out_chan->pos_fraction = 0; + mod_chan->period = period; + if(repeat == 65535) + out_chan->loop_length = 0; + else + out_chan->loop_length = ((AAS_u32) (length - repeat)) << 1; + out_chan->end = data + (length << 1); + out_chan->frequency = AAS_MOD_period_conv_table[period]; + out_chan->delta = AAS_Min(4095, ((out_chan->frequency * AAS_mix_scale) + 32768) >> 16); + out_chan->active = AAS_TRUE; + + AAS_changed[output] = AAS_TRUE; + } + } + } + } + + if(effect) { + switch (effect >> 8) { + case 0xf: /* set speed */ + speed = effect & 0xff; + if(speed > 0) { + if(speed > 31) + AAS_mod_bpm = speed; + else + AAS_mod_speed = speed; + + /*AAS_mod_tempo = (((1<<15)*AAS_mod_bpm*24)/(3000*AAS_mod_speed))<<13; // use LUT */ + + /* approximately: */ + /*AAS_mod_tempo = AAS_DivTable[AAS_mod_speed]*AAS_mod_bpm*33; */ + AAS_mod_tempo = AAS_DivTable[AAS_mod_speed] * AAS_mod_bpm; + } + effect = 0; /* No need to process this effect again */ + break; + + case 0xe: /* extended effects */ + switch (effect & 0xf0) { + case 0x00: /* set filter (used to send messages to code instead) */ + AAS_mod_last_filter_value = effect & 0xf; + effect = 0; /* No need to process this effect again */ + break; + + case 0x10: /* fine slide up */ + tmp = mod_chan->period - (effect & 0xf); + if(tmp < 113) + tmp = 113; + mod_chan->period = tmp; + out_chan->frequency = AAS_MOD_period_conv_table[tmp]; + out_chan->delta = AAS_Min(4095, ((out_chan->frequency * AAS_mix_scale) + 32768) >> 16); + effect = 0; /* No need to process this effect again */ + + AAS_changed[output] = AAS_TRUE; + break; + + case 0x20: /* fine slide down */ + tmp = mod_chan->period + (effect & 0xf); + if(tmp > 856) + tmp = 856; + mod_chan->period = tmp; + out_chan->frequency = AAS_MOD_period_conv_table[tmp]; + out_chan->delta = AAS_Min(4095, ((out_chan->frequency * AAS_mix_scale) + 32768) >> 16); + effect = 0; /* No need to process this effect again */ + + AAS_changed[output] = AAS_TRUE; + break; + + case 0x60: /* set/jump to loop */ + tmp = effect & 0xf; + if(tmp) { + if(AAS_mod_loop_counter) + --AAS_mod_loop_counter; + else + AAS_mod_loop_counter = tmp; + + if(AAS_mod_loop_counter) { + int i; + struct AAS_MOD_Channel + *mod_chan2 = AAS_mod_chan; + + for(i = 0; i < num_chans; ++i) { + mod_chan2->pattern = (AAS_u32 *)(AAS_PatternData + (((int)AAS_Sequence[AAS_mod_num][AAS_mod_song_pos][i]) << 8)); + mod_chan2-> pattern += AAS_mod_loop_start; + ++mod_chan2; + } + AAS_mod_line_num = AAS_mod_loop_start; + } + } else { + AAS_mod_loop_start = AAS_mod_line_num; + } + effect = 0; /* No need to process this effect again */ + break; + + case 0x90: /* retrigger sample */ + mod_chan->trigger = (effect & 0xf) - 1; + break; + + case 0xa0: /* fine volume slide up */ + tmp = mod_chan->volume; + tmp += effect & 0xf; + if(tmp > 64) + tmp = 64; + mod_chan->volume = tmp; + out_chan->volume = (tmp * AAS_mod_overall_volume) >> AAS_volscale; + effect = 0; /* No need to process this effect again */ + + AAS_changed[output] = AAS_TRUE; + break; + + case 0xb0: /* fine volume slide down */ + tmp = mod_chan->volume; + tmp -= effect & 0xf; + if(tmp < 0) + tmp = 0; + mod_chan->volume = tmp; + out_chan->volume = (tmp * AAS_mod_overall_volume) >> AAS_volscale; + effect = 0; /* No need to process this effect again */ + + AAS_changed[output] = AAS_TRUE; + break; + + case 0xc0: /* note cut */ + if((effect & 0xf) == 0) { + effect = 0; + out_chan->active = AAS_FALSE; + AAS_changed[output] = AAS_TRUE; + } + break; + + case 0xe0: /* pattern delay */ + AAS_mod_timer -= (effect & 0xf) * 0x7d0000; + effect = 0; /* No need to process this effect again */ + break; + + default: + break; + } + break; + + case 0xd: /* pattern break */ + /*jump_ahead = (((effect>>4)&0xf)*10)+(effect&0xf); */ + jump_ahead = effect & 0xff; + effect = 0; /* No need to process this effect again */ + break; + + case 0xc: /* set volume */ + mod_chan->volume = tmp = effect & 0xff; + out_chan->volume = (tmp * AAS_mod_overall_volume) >> AAS_volscale; + effect = 0; /* No need to process this effect again */ + + AAS_changed[output] = AAS_TRUE; + break; + + case 0xb: /* position jump */ + jump_song_pos = effect & 0xff; + effect = 0; /* No need to process this effect again */ + break; + + case 0x9: /* set sample offset */ + tmp = effect & 0xff; + if(tmp) { + const AAS_s8 *new_pos = mod_chan->samp_start + (tmp << 8); + + if(new_pos >= out_chan->end) { + out_chan->active = AAS_FALSE; + + AAS_changed[output] = AAS_TRUE; + } else + out_chan->pos = new_pos; + } + effect = 0; /* No need to process this effect again */ + break; + + case 0x7: /* tremolo */ + if(effect & 0xf0) + mod_chan->tremolo_rate = (effect & 0xf0) >> 4; + if(effect & 0xf) + mod_chan->tremolo_depth = effect & 0xf; + if(effect & 0xff) + mod_chan->tremolo_pos = 0; + break; + + case 0x4: /* vibrato */ + if(effect & 0xf0) + mod_chan->vibrato_rate = (effect & 0xf0) >> 4; + if(effect & 0xf) + mod_chan->vibrato_depth = effect & 0xf; + if(effect & 0xff) + mod_chan->vibrato_pos = 0; + break; + + case 0x3: /* tone portamento */ + tmp = effect & 0xff; + if(tmp) + mod_chan->slide_rate = tmp; + + if(period) + mod_chan->slide_target_period = period; + else if(mod_chan->slide_target_period == 0) + effect = 0; + break; + + case 0x0: /* possible arpeggio */ + tmp = effect & 0xff; + if(tmp) /* definite arpeggio */ + { + tmp = mod_chan->note; + mod_chan->arpeggio_pos = 0; + if(tmp) { + out_chan->frequency = AAS_MOD_period_conv_table[AAS_period_table[mod_samp[mod_chan->samp_num].finetune][tmp - 1]]; + out_chan->delta = AAS_Min(4095, ((out_chan->frequency * AAS_mix_scale) + 32768) >> 16); + + AAS_changed[output] = AAS_TRUE; + } + } + break; + + default: + /*printf_special( "effect:%p period:%d samp:%d\n", effect, period, samp_num ); */ + break; + } + } + + mod_chan->effect = effect; + if(effect) + active_effects = AAS_TRUE; + ++mod_chan; + } + + AAS_mod_active_effects = active_effects; + + if(jump_ahead >= 0) { + AAS_mod_loop_start = 0; + ++AAS_mod_song_pos; + if(AAS_Sequence[AAS_mod_num][AAS_mod_song_pos][0] == -1) { + AAS_mod_looped = AAS_TRUE; + AAS_mod_song_pos = 0; + mod_chan = AAS_mod_chan; + for(chan = num_chans; chan > 0; --chan) { + mod_chan->slide_target_period = 0; + ++mod_chan; + } + } + + mod_chan = AAS_mod_chan; + for(chan = 0; chan < num_chans; ++chan) { + mod_chan->pattern = (AAS_u32 *) (AAS_PatternData + (((int)AAS_Sequence[AAS_mod_num][AAS_mod_song_pos][chan]) << 8)); + mod_chan->pattern += jump_ahead; + ++mod_chan; + } + AAS_mod_line_num = jump_ahead; + } else if(jump_song_pos >= 0) { + AAS_mod_loop_start = 0; + if(jump_song_pos < 128) { + AAS_mod_song_pos = jump_song_pos; + if(AAS_Sequence[AAS_mod_num][AAS_mod_song_pos][0] == -1) { + AAS_mod_looped = AAS_TRUE; + mod_chan = AAS_mod_chan; + for(chan = num_chans; chan > 0; --chan) { + mod_chan->slide_target_period = 0; + ++mod_chan; + } + AAS_mod_song_pos = 0; + } + } else { + AAS_mod_looped = AAS_TRUE; + mod_chan = AAS_mod_chan; + for(chan = num_chans; chan > 0; --chan) { + mod_chan->slide_target_period = 0; + ++mod_chan; + } + AAS_mod_song_pos = 0; + } + + mod_chan = AAS_mod_chan; + for(chan = 0; chan < num_chans; ++chan) { + mod_chan->pattern = (AAS_u32*)(AAS_PatternData + (((int)AAS_Sequence[AAS_mod_num][AAS_mod_song_pos][chan]) << 8)); + ++mod_chan; + } + AAS_mod_line_num = 0; + } else { + ++AAS_mod_line_num; + if(AAS_mod_line_num > 63) { + AAS_mod_line_num = 0; + AAS_mod_loop_start = 0; + + if(AAS_mod_next_song_pos == -1) { + ++AAS_mod_song_pos; + } else { + AAS_mod_song_pos = AAS_mod_next_song_pos; + AAS_mod_next_song_pos = -1; + } + + if(AAS_Sequence[AAS_mod_num][AAS_mod_song_pos][0] == -1) { + AAS_mod_looped = AAS_TRUE; + mod_chan = AAS_mod_chan; + for(chan = num_chans; chan > 0; --chan) { + mod_chan->slide_target_period = 0; + ++mod_chan; + } + AAS_mod_song_pos = AAS_RestartPos[AAS_mod_num]; + } + + mod_chan = AAS_mod_chan; + for(chan = 0; chan < num_chans; ++chan) { + mod_chan->pattern = (AAS_u32*)(AAS_PatternData + (((int)AAS_Sequence[AAS_mod_num][AAS_mod_song_pos][chan]) << 8)); + ++mod_chan; + } + } + } + + if(AAS_mod_looped) + if(!AAS_mod_loop) + AAS_MOD_Stop(); + } + } +} diff --git a/libs/aas/AAS_Main.c b/libs/aas/AAS_Main.c new file mode 100644 index 0000000..dae0c73 --- /dev/null +++ b/libs/aas/AAS_Main.c @@ -0,0 +1,698 @@ +/* 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; +} diff --git a/libs/aas/AAS_Mixer.h b/libs/aas/AAS_Mixer.h new file mode 100644 index 0000000..646d56a --- /dev/null +++ b/libs/aas/AAS_Mixer.h @@ -0,0 +1,62 @@ +/* Copyright (c) 2003-2021 James Daniels */ +/* Distributed under the MIT License */ +/* license terms: see LICENSE file in root or http://opensource.org/licenses/MIT */ + +/* Mixer-specific AAS include */ +/* */ +/* The functions and variables declared here should only be used if you need */ +/* to directly access AAS_MixAudio(). This is not recommended and doing so */ +/* will make it unsafe to use any other AAS functions except AAS_ShowLogo() */ +/* and AAS_DoDMA3(). See the "Mixer" section of the documentation for more */ +/* information. */ + +#ifndef __AAS_MIXER__ +#define __AAS_MIXER__ + +#include "AAS.h" + +AAS_BEGIN_DECLS + +#define AAS_IN_IWRAM __attribute__ ((section (".iwram"))) +#define AAS_IN_EWRAM __attribute__ ((section (".ewram"))) + +struct AAS_Channel +{ + AAS_u8 effective_volume; /* 0 : effective_volume = (active&volume)?volume:0 */ + AAS_BOOL active; /* 1 : 0 = Channel inactive, 1 = channel active */ + AAS_u8 volume; /* 2 : Total vol of chan set must be <= 256, each vol usually in range 0-64 */ + AAS_u8 pos_fraction; /* 3 : Fraction component of pos */ + AAS_u16 frequency; /* 4 : Frequency (Hz) */ + AAS_u16 delta; /* 6 : Delta */ + const AAS_s8* pos; /* 8 : Current sample address, fraction component in pos_fraction */ + const AAS_s8* end; /* 12 : Address of end of sample */ + AAS_u32 loop_length; /* 16 : 0 = No repeat, Other value = Bytes from end back to restart point */ +}; /* Length = 20 bytes */ + +extern struct AAS_Channel AAS_channels[16] AAS_IN_EWRAM; +extern AAS_u32 AAS_mix_buffer[640] AAS_IN_EWRAM; + +#define AAS_INIT_BRANCH void (*ptr2Function)(); +#define AAS_BRANCH(a,b...) ({ ptr2Function = a; ptr2Function(b); }) + +/* AAS_MixAudio() is in IWRAM, call from ROM as follows: (same for AAS_MixAudio_NoChange()) */ +/* AAS_INIT_BRANCH */ +/* AAS_BRANCH( AAS_MixAudio, mix_buffer, chans, iterations ); */ + +void AAS_MixAudio( AAS_s8* mix_buffer, struct AAS_Channel chans[], int iterations ); +void AAS_MixAudio_NoChange( AAS_s8* mix_buffer, struct AAS_Channel chans[], int iterations ); /* Only call if no changes to chans[] since previous call to AAS_MixAudio(). Do not call twice in a row. */ + +#define AAS_MIXAUDIO_MODE_NORMAL 0 /* Total vol must be <= 256, normal vol - default */ +#define AAS_MIXAUDIO_MODE_BOOST 1 /* Total vol must be <= 128, double vol */ +#define AAS_MIXAUDIO_MODE_BOOSTANDCLIP 2 /* Total vol must be <= 256, double vol */ + +void AAS_MixAudio_SetMode( int mode ); /* Set mixer mode, only call if 100% sure AAS_MixAudio won't interrupt */ + +/* Set maximum number of channels in set (lower=faster), only call if 100% sure AAS_MixAudio won't interrupt */ +void AAS_MixAudio_SetMaxChans_2(); +void AAS_MixAudio_SetMaxChans_4(); /* Default */ +void AAS_MixAudio_SetMaxChans_8(); + +AAS_END_DECLS + +#endif diff --git a/libs/aas/AAS_Mixer.s b/libs/aas/AAS_Mixer.s new file mode 100644 index 0000000..7f1e20f --- /dev/null +++ b/libs/aas/AAS_Mixer.s @@ -0,0 +1,632 @@ +@ Copyright (c) 2003-2021 James Daniels +@ Distributed under the MIT License +@ license terms: see LICENSE file in root or http://opensource.org/licenses/MIT + +.TEXT +.SECTION .iwram,"ax",%progbits +.ALIGN +.ARM + +.GLOBAL AAS_MixAudio +.GLOBAL AAS_MixAudio_NoChange +.EXTERN AAS_DivTable + +.GLOBAL _AAS_MixAudio_mod1 +.GLOBAL _AAS_MixAudio_mod2 +.GLOBAL _AAS_MixAudio_mod3 +.GLOBAL _AAS_MixAudio_mod4 +.GLOBAL _AAS_MixAudio_mod5 +.GLOBAL _AAS_MixAudio_mod6 +.GLOBAL _AAS_MixAudio_mod7 +.GLOBAL _AAS_MixAudio_mod8 + +.pool + +_ma_mov_r3_0: mov r3,#0 +_ma_add_r0_r0_0: add r0,r0,#0 + +_ma_ldr_pc_0: .word 0xe51f0000+8 @ sort of equivalent to opcode(ldr r0,[pc,#-0]) +_ma_mov_r14_r0_lsr_6: mov r14,r0,lsr #6 +_ma_ldrsb_r0_r14_shifted: .word 0x3e1fe00d @ (opcode("ldrsb r0,[r14,#+0]!")>>4) + (3<<28) +_ma_add_r0_r0_r0_lsl_16: adds r0,r0,r0,lsl #16 @ change regs as appropriate +_ma_vol_lookup_addr: .word _AAS_vol_lookup-1 +_ma_total_iterations: .word 0x0 +_ma_total_delta: .word 0x0 +_ma_bytes_available: .word 0x0 +_ma_no_skip: .word 0x0 + + + @ AAS_CODE_IN_IWRAM void AAS_MixAudio_NoChange( AAS_s8* mix_buffer, struct AAS_Channel chans[], int iterations ); + +AAS_MixAudio_NoChange: + stmfd sp!,{r4-r11,r14} + sub sp,sp,#16 + stmfd sp!,{r0-r2} + + mov r9,#0 + str r9,_ma_no_skip + ldr r10,_ma_bytes_available + ldr r9,_ma_total_iterations + cmp r9,r2 + movhi r9,r2 + mov r11,r9 + ldr r2,_ma_total_delta + + b ma_quickstart + + + @ AAS_CODE_IN_IWRAM void AAS_MixAudio( AAS_s8* mix_buffer, struct AAS_Channel chans[], int iterations ); + +AAS_MixAudio: + stmfd sp!,{r4-r11,r14} + sub sp,sp,#16 + stmfd sp!,{r0-r2} + + @ [sp] = _ma_mix_buffer + @ [sp,#4] = _ma_chans + @ [sp,#8] = _ma_to_go + @ [sp,#12] = _ma_iterations_loop + @ [sp,#16] = _ma_iterations_buffer + @ [sp,#20] = _ma_iterations_scale_buffer + @ [sp,#24] = _ma_loop_counter + + @ r0 = temp + @ r1 = chans + @ r2 = iterations in main loop + @ r3 = _ma_add_r0_r0_r0_lsl_16 + @ r4 = temp + @ r5 = temp + @ r6 = outer loop counter/active channels found/total delta>>2 + @ r7 = temp + @ r8 = temp + @ r9 = temp + @ r10 = temp + @ r11 = temp + @ r12 = dest address + @ r14 = temp + +ma_do_setup: +_AAS_MixAudio_mod1: + adr r12,ma_buffer_start + ldr r3,_ma_add_r0_r0_r0_lsl_16 +_AAS_MixAudio_mod5: + mov r6,#0x70000000 @ was #0x30000000 + mov r2,#256 + + +ma_setup_loop: + ldrb r14,[r1],#20 @ effective_volume + + cmp r14,#0 + beq ma_skip @ skip if effective_volume == 0 + + + @ Setup volume registers: + @ r11 = increment for r4 + @ r14 = "mul r5,r0,r3"/"mov r5,r0,lsl #0"/"mlane r5,r0,r3,r5"/"add r5,r5,r0,lsl #0" + @adr r10,_ma_vol_lookup-1 + ldr r10,_ma_vol_lookup_addr + add r4,r10,#129 @ 129 = 1+_ma_mul_r5_r0_r3-_ma_vol_lookup + ldrsb r10,[r10,r14] + ands r5,r6,#0x0f000000 @ test if this is first active channel + addne r4,r4,#16 @ 16 = _ma_mlane_r5_r0_r3_r5-_ma_mul_r5_r0_r3 + @adreq r4,_ma_mul_r5_r0_r3 @ use mul/mov if this is first non-zero chan + @adrne r4,_ma_mlane_r5_r0_r3_r5 @ use mlane/add if this is first non-zero chan + cmp r10,#0 + ldrlt r11,_ma_mov_r3_0 @ read "mov r3,#vol" if vol not power of 2 + addlt r11,r11,r14 @ set #vol in "mov r3,#vol" if vol not power of 2 + strlt r11,[r12],#4 @ write "mov r3,#vol" if vol not power of 2 + addge r4,r4,#8 @ increment if vol is power of 2 + ldmia r4,{r11,r14} @ read mul/mlane/mov/add and increment + addge r14,r14,r10,lsl #7 @ set lsl #val for mov/add if vol power of 2 + + + @ r0,r4,r5,r7,r8,r9,r10 available + @ r3 = _ma_add_r0_r0_0 + @ r5 = delta/increment for r14 + @ r8 = _ma_divide_table + @ r11 = temp (was increment for r14) + @ r12 = dest address + @ r14 = "mul r5,r0,r3"/"mov r5,r0,lsl #0"/"mlane r5,r0,r3,r5"/"add r5,r5,r0,lsl #0" + + @ Setup delta registers, write delta increment instructions: + @ r5 = delta/increment for r14 + adr r7,_ma_chan_cache @ could remove + add r7,r7,r5,lsr #22 @ could remove + ldr r10,_ma_ldr_pc_0 @ could pre-subtract _ma_chan_cache from _ma_ldr_pc_0 (would need to set at runtime) + sub r10,r10,r7 @ could change to sub r10,r10,r5,lsr #22 + add r0,r10,r12 + eor r10,r10,#0x00100000 @ switch to str + ldr r7,_ma_mov_r14_r0_lsr_6 + ldrh r5,[r1,#6-20] @ delta + and r9,r5,#0xff + ldr r4,_ma_add_r0_r0_0 + add r9,r4,r9 + stmia r12!,{r0,r7,r9} + add r7,r4,#0xc00 + add r7,r7,r5,lsr #8 + tst r7,#0xff + strne r7,[r12],#4 + add r6,r6,r5,lsr #2 + add r7,r10,r12 + str r7,[r12],#4 + add r5,r11,r5,lsl #20 + + + @ Final setup: + @ r0 = delta_pos + @ r8 = x + @ r9 = x_history + @ r10 = local outer loop counter/_ma_ldrsb_r0_r14_shifted + mov r0,#0x200 @ delta_pos = 0.5 (was 0) + ldr r10,_ma_ldrsb_r0_r14_shifted + mov r8,#0 + mov r9,#0 + + + @ r0 = delta_pos + @ r1 = chans + @ r2 = iterations in main loop + @ r3 = _ma_add_r0_r0_r0_lsl_16 + @ r4 = temp + @ r5 = delta/increment for r14 + @ r6 = outer loop counter/total delta<<1 + @ r7 = temp + @ r8 = x + @ r9 = x_history + @ r10 = local outer loop counter/_ma_ldrsb_r0_r14_shifted + @ r11 = temp (was increment for r14) + @ r12 = dest address + @ r14 = "mul r5,r0,r3"/"mov r5,r0,lsl #0"/"mlane r5,r0,r3,r5"/"add r5,r5,r0,lsl #0" + + @ Write instructions: + mov r7,r10,lsl #4 + str r7,[r12],#4 + b ma_setup_inner_loop_first + .word 0,0,0 @ padding +ma_setup_outer_loop: + + @ Write delta: + add r0,r0,r5,lsr #20 + movs r4,r0,lsr #10 + beq ma_setup_inner_loop_skip1 + sub r0,r0,r4,lsl #10 + add r4,r4,r10,lsl #4 + mov r8,r7 + add r4,r4,r8,lsl #4 + str r4,[r12],#4 +ma_setup_inner_loop_skip1: + add r9,r8,r9,lsl #8 + +ma_setup_inner_loop_first: + + @ Write delta: + add r0,r0,r5,lsr #20 + movs r4,r0,lsr #10 + beq ma_setup_inner_loop_skip2 + sub r0,r0,r4,lsl #10 + add r4,r4,r10,lsl #4 + subs r8,r8,#0x100 + movlt r8,#0x200 + add r4,r4,r8,lsl #4 + str r4,[r12],#4 +ma_setup_inner_loop_skip2: + add r9,r8,r9,lsl #8 + + @ Write delta: + add r0,r0,r5,lsr #20 + movs r4,r0,lsr #10 + beq ma_setup_inner_loop_skip3 + sub r0,r0,r4,lsl #10 + add r4,r4,r10,lsl #4 + subs r8,r8,#0x100 + movlt r8,#0x200 + add r4,r4,r8,lsl #4 + str r4,[r12],#4 +ma_setup_inner_loop_skip3: + add r9,r8,r9,lsl #8 + + @ Write merge and mul/mla/mov/add: + subs r11,r8,#0x100 + movlt r11,#0x200 + add r7,r14,r11,lsr #8 + add r4,r3,r11,lsl #4 + bic r11,r9,#0x00ff0000 + add r4,r4,r11,lsr #8 + stmia r12!,{r4,r7} + add r14,r14,r5,lsl #12 + + @ Write delta: + add r0,r0,r5,lsr #20 + movs r4,r0,lsr #10 + beq ma_setup_inner_loop_skip4 + sub r0,r0,r4,lsl #10 + add r4,r4,r10,lsl #4 + subs r8,r8,#0x100 + movlt r8,#0x200 + add r4,r4,r8,lsl #4 + str r4,[r12],#4 +ma_setup_inner_loop_skip4: + add r9,r8,r9,lsl #8 + + @ Write merge: (skips if unnecessary) + subs r7,r8,#0x100 + movlt r7,#0x200 + bic r4,r9,#0x00ff0000 + cmp r4,r11 + addne r11,r3,r4,lsr #8 + addne r11,r11,r7,lsl #4 + strne r11,[r12],#4 + + @ Write mul/mla/mov/add: + add r4,r14,r7,lsr #8 + str r4,[r12],#4 + add r14,r14,r5,lsl #12 + + subs r10,r10,#0x10000000 + bge ma_setup_outer_loop + + + @ Calculate iterations until end of sample: + ldr r10,[r1,#8-20] @ pos + ldr r0,[r1,#12-20] @ end + sub r0,r0,r10 + mov r5,r5,lsr #19 + ldr r8,_ma_divide_table + ldrh r5,[r8,r5] + mul r0,r5,r0 + cmp r2,r0,lsr #10 + movhi r2,r0,lsr #10 + + add r6,r6,#0x01000000 @ increment active channels found + +ma_skip: + subs r6,r6,#0x10000000 + bge ma_setup_loop + + @ Write "b ma_again" +_AAS_MixAudio_mod3: + adr r0,ma_again + sub r0,r0,r12 + mov r0,r0,asr #2 + sub r0,r0,#2 + @bic r0,r0,#0xff000000 + @add r0,r0,#0xea000000 + adr r4,ma_buffer_end + bic r0,r0,#0x15000000 @ offset always negative, so this is equivalent to above + str r0,[r12],#4 + + sub r10,r4,r12 + and r4,r6,#0x0f000000 + sub r10,r10,r4,lsr #21 @ r10 -= 8*used_channels + @str r10,_ma_iterations_scale_buffer + @str r10,[sp,#20] + str r10,_ma_bytes_available + + ldr r11,[sp,#8] + subs r9,r2,r11 + str r9,_ma_total_iterations + @cmp r2,r11 + mov r9,#1 + movhi r9,#0 + str r9,_ma_no_skip + movhi r2,r11 + + mov r11,r2 + mov r9,r2 + bic r2,r6,#0xff000000 + str r2,_ma_total_delta + + @ r2 = total_delta>>2 + @ r9 = r11 = total_iterations - i.e. iterations until loop - argh! need to recalculate each call! + @ r10 = ((bytes_available-(8*channels_used))<<4) + + @ Could remove need to recalc total_iterations each call by not doing "total_iterations = min( iterations, total_iterations )" - do min below instead and sub "iterations" from "total_iterations" afterwards. Problem: Need to accurately calculate "total_iterations" even when it is large. (Although only need to cope with total_iterations being twice as large as it is now because only ever re-use config once.) + +ma_quickstart: + @ldr r4,_ma_mix_buffer + ldr r4,[sp] + + + @ r1 = &_ma_mix_buffer + @ r2 = total_delta>>2 + @ r3 = refill iterations<<3 + @ r4 = _ma_mix_buffer + @ r5 = temp + @ r6 = temp + @ r7 = temp + @ r8 = #0x04000000 + @ r9 = total iterations + @ r10 = ((bytes_available-(8*channels_used))<<4) + @ r11 = total iterations + @ r12 = temp + @ r14 = dest + + @ Calc iterations + @ iterations = ((bytes_available-(8*channels_used))<<4)/(total_delta>>2) + ldr r3,_ma_divide_table +ma_begin: @ called from process loop + mov r2,r2,lsl #1 + ldrh r3,[r3,r2] + mul r3,r10,r3 + @str r3,_ma_iterations_buffer + str r3,[sp,#16] +ma_begin2: + cmp r9,r3,lsr #12 @ was asr #3 + movgt r9,r3,lsr #12 @ was asr #3 + + sub r3,r11,r9 + @str r3,_ma_iterations_loop + str r3,[sp,#12] + + cmp r9,#0 + ble ma_end + + + @ r0 = temp + @ r1 = temp + @ r2 = 0xc0 + @ r3 = chans + @ r4 = _ma_mix_buffer + @ r5 = DMA address + @ r6 = 2 + @ r7 = temp + @ r8 = &_ma_chan_cache + @ r9 = iterations + @ r10 = loop counter + @ r11 = temp + @ r12 = buffer address + @ r14 = temp + + @ Fill buffer + adr r8,_ma_chan_cache + mov r5,#0x04000000 + add r5,r5,#0xd4 + mov r6,#2 +_AAS_MixAudio_mod6: + mov r10,#8 @ was #4 - could perhaps set according to max # of channels / 2? + mov r2,#0xc0 + adr r12,ma_buffer_end + @ldr r3,_ma_chans + ldr r3,[sp,#4] + +ma_fill_buffer_loop: + ldrb r14,[r3],#20 @ effective_volume + cmp r14,#0 + beq ma_fill_buffer_skip + ldr r7,[r3,#8-20] @ pos + bic r0,r7,#0x3 + ldrb r11,[r3,#3-20] @ pos_fraction + and r1,r2,r7,lsl #6 + add r1,r1,r11,lsr #2 + ldrh r14,[r3,#6-20] @ delta + mul r14,r9,r14 + add r11,r11,r14,lsl #2 + strb r11,[r3,#3-20] @ pos_fraction + add r7,r7,r11,lsr #8 + str r7,[r3,#8-20] @ pos + add r7,r6,r14,lsr #8 @ words to copy + sub r12,r12,r7,lsl #2 + add r1,r1,r12,lsl #6 + str r1,[r8],#4 @ IWRAM pos + add r14,r7,#0x84000000 + stmia r5,{r0,r12,r14} +ma_fill_buffer_skip: + subs r10,r10,#1 + bgt ma_fill_buffer_loop + + @ Setup registers for main loop + @ldr r3,_ma_to_go + ldr r3,[sp,#8] + sub r3,r3,r9 + @str r3,_ma_to_go + str r3,[sp,#8] + + @ Setup registers for main loop + @mvn r14,#0xff00 + @add r14,r14,r9,lsl #24 + mvn r14,#0xff00 + bic r14,r14,#0x1000000 + add r14,r14,r9,lsl #25 +_AAS_MixAudio_mod2: + b ma_start + +ma_end: + @ldr r9,_ma_iterations_loop + ldr r9,[sp,#12] + movs r11,r9 + @ldrne r3,_ma_iterations_buffer + ldrne r3,[sp,#16] + bne ma_begin2 + + @ldr r1,_ma_chans + ldr r1,[sp,#4] + @ should be after ble below? + + @ldr r9,_ma_to_go + ldr r9,[sp,#8] + cmp r9,#0 + ldrle r3,_ma_no_skip + cmple r3,#0 + ble ma_chan_finished + + @ change so only branch if done all iterations and no samples have ended + + @ r0 = redo setup + @ r1 = chans[] (was loop) + @ r2 = total_delta>>2 + @ r3 = divide_table + @ r4 = _ma_mix_buffer + @ r5 = temp + @ r6 = delta + @ r7 = end + @ r8 = unused + @ r9 = loops to go + @ r10 = _ma_specific_first + @ r11 = &ma_chan0_start + @ r12 = temp + @ r13 = unused + @ r14 = loop (was chans[]) + + ldr r3,_ma_divide_table +_AAS_MixAudio_mod7: + mov r14,#8 @ was #4 + mov r2,#0 + mov r0,#0 + +ma_check_chan_loop: + ldrb r7,[r1],#20 + cmp r7,#0 + beq ma_chan_done + + ldrh r7,[r1,#6-20] @ delta + ldr r12,[r1,#8-20] @ pos + ldr r6,[r1,#12-20] @ end + sub r5,r6,r7,lsr #6 + sub r5,r5,#1 + cmp r5,r12 + ble ma_chan_do_loop + +ma_chan_ok: + add r2,r2,r7,lsr #2 + sub r5,r6,r12 + mov r7,r7,lsl #1 + ldrh r7,[r3,r7] + mul r5,r7,r5 + cmp r9,r5,lsr #10 + movhi r9,r5,lsr #10 + +ma_chan_done: + subs r14,r14,#1 + bgt ma_check_chan_loop + + cmp r0,#0 + bne ma_chan_has_finished + + movs r11,r9 + @ldrgt r10,_ma_iterations_scale_buffer + @ldrgt r10,[sp,#20] + ldrgt r10,_ma_bytes_available + bgt ma_begin + +ma_chan_finished: + add sp,sp,#28 + ldmfd sp!, {r4-r11, r14} + bx lr @ Thumb interwork friendly. + +ma_chan_has_finished: + cmp r2,#0 + beq ma_chan_all_zeroes + @ldr r2,_ma_to_go + @ldr r2,[sp,#8] +_AAS_MixAudio_mod8: + sub r1,r1,#(20*8) @ was #(20*4) + @str r4,_ma_mix_buffer + str r4,[sp] + b ma_do_setup + +ma_chan_all_zeroes: @ very rare - only happens if last active channel finished during this period + @ldr r5,_ma_to_go + ldr r5,[sp,#8] @ can be 0 sometimes + movs r5,r5,lsl #2 + add r5,r5,#0x85000000 + adr r2,_ma_empty + mov r6,#0x04000000 + add r6,r6,#0xd4 + stmneia r6,{r2,r4,r5} @ r5(_ma_to_go) can be 0 sometimes + b ma_chan_finished + + + +ma_chan_do_loop: + ldr r5,[r1,#16-20] @ loop_length + cmp r5,#0 + + mov r0,#1 @ redo setup !!moved!! + + @ Loop sample + subne r12,r12,r5 + strne r12,[r1,#8-20] @ pos + bne ma_chan_ok + + @ Set inactive + strh r5,[r1,#0-20] @ effective_volume + active + @mov r0,#1 @ redo setup !!was here!! + b ma_chan_done + + +_ma_chan_cache: + .word 0,0,0,0,0,0,0,0 @ IWRAM pos, chan: 0,1,2,3,4,5,6,7 + +_ma_divide_table: .word AAS_DivTable + +_ma_empty: .word 0 +.word 0,0 @ padding + +_AAS_MixAudio_mod4: + + @ r0-r2 : temp + @ r3 : volume + @ r4 : output address + @ r5-r12 : output buffer + @ r14 : sample address/loop/mask + +ma_again: + + @ r0 = temp + @ r1 = temp2 + @ r2 = mask_0x80808080 + @@ r3 free + + @ Make sure algo is as efficient as possible + + @ Merge + @ldr r14,_ma_loop_counter + ldr r14,[sp,#24] + and r5,r14,r5,lsr #8 + and r6,r14,r6,lsr #8 + and r7,r14,r7,lsr #8 + and r8,r14,r8,lsr #8 + and r9,r14,r9,lsr #8 + and r10,r14,r10,lsr #8 + and r11,r14,r11,lsr #8 + and r12,r14,r12,lsr #8 + add r5,r5,r6,lsl #8 + add r6,r7,r8,lsl #8 + add r7,r9,r10,lsl #8 + add r8,r11,r12,lsl #8 + + @ Store + stmia r4!,{r5-r8} + + @ Loop + subs r14,r14,#0x2000000 + ble ma_end + +ma_start: + @str r14,_ma_loop_counter + str r14,[sp,#24] + +ma_buffer_start: @ 2048 bytes (1152 bytes would be equivalent to previous cache size) + + .word 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 @ 128 bytes + .word 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 @ 128 bytes + .word 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 @ 128 bytes + .word 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 @ 128 bytes + + .word 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 @ 128 bytes + .word 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 @ 128 bytes + .word 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 @ 128 bytes + .word 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 @ 128 bytes + + .word 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 @ 128 bytes + .word 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 @ 128 bytes + .word 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 @ 128 bytes + .word 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 @ 128 bytes + + .word 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 @ 128 bytes + .word 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 @ 128 bytes + .word 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 @ 128 bytes + .word 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 @ 128 bytes + +ma_buffer_end: diff --git a/libs/aas/AAS_SFX.c b/libs/aas/AAS_SFX.c new file mode 100644 index 0000000..f8f4c41 --- /dev/null +++ b/libs/aas/AAS_SFX.c @@ -0,0 +1,217 @@ +/* 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" + +int AAS_SFX_GetNumChannels() +{ + if(AAS_initialised) { + if(AAS_volscale == 9) + return 16 - AAS_MOD_GetNumChannels(); + else if(AAS_volscale == 8) + return 8 - AAS_MOD_GetNumChannels(); + else + return 4 - AAS_MOD_GetNumChannels(); + } else { + return 0; + } +} + +struct AAS_Channel *AAS_SFX_GetChannel(int channel) +{ + if(AAS_volscale == 9) + return &AAS_channels[AAS_chan_rearrange[15 - channel]]; + else if(AAS_volscale == 8) + return &AAS_channels[AAS_chan_rearrange[7 - channel]]; + else + return &AAS_channels[AAS_chan_rearrange[3 - channel]]; +} + +int AAS_SFX_GetOutput(int channel) +{ + if(AAS_volscale == 9) + return AAS_chan_rearrange[15 - channel] >> 3; + else if(AAS_volscale == 8) + return AAS_chan_rearrange[7 - channel] >> 3; + else + return AAS_chan_rearrange[3 - channel] >> 3; +} + +AAS_BOOL AAS_SFX_ChannelExists(int channel) +{ + if((channel >= 0) && (channel < AAS_SFX_GetNumChannels())) { + return AAS_TRUE; + } else { + return AAS_FALSE; + } +} + +int AAS_SFX_Play(int channel, int sample_volume, int sample_frequency, const AAS_s8 * sample_start, + const AAS_s8 * sample_end, const AAS_s8 * sample_restart) +{ + if(AAS_initialised) { + if(AAS_SFX_ChannelExists(channel)) { + if((sample_frequency >= 1) && (sample_frequency <= 65535)) { + if((sample_volume >= 0) && (sample_volume <= 64)) { + if(sample_start && (sample_end > sample_start) && + (sample_restart < sample_end)) { + struct AAS_Channel *ch; + + ch = AAS_SFX_GetChannel(channel); + ch->active = AAS_FALSE; + ch->volume = (sample_volume << 8) >> AAS_volscale; + ch->frequency = sample_frequency; + ch->delta = AAS_Min(4095, ((sample_frequency * AAS_mix_scale) + 32768) >> 16); + ch->pos = sample_start; + ch->pos_fraction = 0; + if(sample_restart) + ch->loop_length = sample_end - sample_restart; + else + ch->loop_length = 0; + ch->end = sample_end; + ch->active = AAS_TRUE; + + AAS_changed[AAS_SFX_GetOutput(channel)] = AAS_TRUE; + + return AAS_OK; + } else { + return AAS_ERROR_INVALID_SAMPLE_ADDRESS; + } + } else { + return AAS_ERROR_VOLUME_OUT_OF_RANGE; + } + } else { + return AAS_ERROR_FREQUENCY_OUT_OF_RANGE; + } + } else { + return AAS_ERROR_CHANNEL_NOT_AVAILABLE; + } + } else { + return AAS_ERROR_CALL_SET_CONFIG_FIRST; + } +} + +AAS_BOOL AAS_SFX_IsActive(int channel) +{ + if(AAS_SFX_ChannelExists(channel)) { + return AAS_SFX_GetChannel(channel)->active; + } else { + return AAS_FALSE; + } +} + +int AAS_SFX_EndLoop(int channel) +{ + if(AAS_initialised) { + if(AAS_SFX_ChannelExists(channel)) { + AAS_SFX_GetChannel(channel)->loop_length = 0; + + return AAS_OK; + } else { + return AAS_ERROR_CHANNEL_NOT_AVAILABLE; + } + } else { + return AAS_ERROR_CALL_SET_CONFIG_FIRST; + } +} + +int AAS_SFX_SetFrequency(int channel, int sample_frequency) +{ + if(AAS_initialised) { + if(AAS_SFX_ChannelExists(channel)) { + if((sample_frequency >= 1) && (sample_frequency <= 65535)) { + struct AAS_Channel *ch; + + ch = AAS_SFX_GetChannel(channel); + ch->frequency = sample_frequency; + ch->delta = AAS_Min(4095, ((sample_frequency * AAS_mix_scale) + 32768) >> 16); + + AAS_changed[AAS_SFX_GetOutput(channel)] = AAS_TRUE; + + return AAS_OK; + } else { + return AAS_ERROR_FREQUENCY_OUT_OF_RANGE; + } + } else { + return AAS_ERROR_CHANNEL_NOT_AVAILABLE; + } + } else { + return AAS_ERROR_CALL_SET_CONFIG_FIRST; + } +} + +int AAS_SFX_SetVolume(int channel, int sample_volume) +{ + if(AAS_initialised) { + if(AAS_SFX_ChannelExists(channel)) { + if((sample_volume >= 0) && (sample_volume <= 64)) { + AAS_SFX_GetChannel(channel)->volume = (sample_volume << 8) >> AAS_volscale; + + AAS_changed[AAS_SFX_GetOutput(channel)] = AAS_TRUE; + + return AAS_OK; + } else { + return AAS_ERROR_VOLUME_OUT_OF_RANGE; + } + } else { + return AAS_ERROR_CHANNEL_NOT_AVAILABLE; + } + } else { + return AAS_ERROR_CALL_SET_CONFIG_FIRST; + } +} + +int AAS_SFX_Stop(int channel) +{ + if(AAS_initialised) { + if(AAS_SFX_ChannelExists(channel)) { + AAS_SFX_GetChannel(channel)->active = AAS_FALSE; + + AAS_changed[AAS_SFX_GetOutput(channel)] = AAS_TRUE; + + return AAS_OK; + } else { + return AAS_ERROR_CHANNEL_NOT_AVAILABLE; + } + } else { + return AAS_ERROR_CALL_SET_CONFIG_FIRST; + } +} + +int AAS_SFX_Resume(int channel) +{ + if(AAS_initialised) { + if(AAS_SFX_ChannelExists(channel)) { + struct AAS_Channel *ch; + + ch = AAS_SFX_GetChannel(channel); + + if(!ch->active) { + if(ch->loop_length) { + ch->active = AAS_TRUE; + + AAS_changed[AAS_SFX_GetOutput(channel)] = AAS_TRUE; + + return AAS_OK; + } else { + if(ch->pos < ((ch->end - (ch->delta >> 6)) - 1)) { + ch->active = AAS_TRUE; + + AAS_changed[AAS_SFX_GetOutput(channel)] = AAS_TRUE; + + return AAS_OK; + } else { + return AAS_ERROR_CHANNEL_UNRESUMEABLE; + } + } + } else { + return AAS_ERROR_CHANNEL_ACTIVE; + } + } else { + return AAS_ERROR_CHANNEL_NOT_AVAILABLE; + } + } else { + return AAS_ERROR_CALL_SET_CONFIG_FIRST; + } +} diff --git a/libs/aas/AAS_Shared.c b/libs/aas/AAS_Shared.c new file mode 100644 index 0000000..b6f11d3 --- /dev/null +++ b/libs/aas/AAS_Shared.c @@ -0,0 +1,737 @@ +/* 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" + +const AAS_u16 AAS_DivTable[8192] = { + 65535, 65535, 32768, 21845, 16384, 13107, 10922, 9362, 8192, 7281, 6553, + 5957, 5461, 5041, 4681, 4369, 4096, 3855, 3640, 3449, 3276, 3120, 2978, + 2849, 2730, 2621, 2520, 2427, 2340, 2259, 2184, 2114, 2048, 1985, 1927, + 1872, 1820, 1771, 1724, 1680, 1638, 1598, 1560, 1524, 1489, 1456, 1424, + 1394, 1365, 1337, 1310, 1285, 1260, 1236, 1213, 1191, 1170, 1149, 1129, + 1110, 1092, 1074, 1057, 1040, 1024, 1008, 992, 978, 963, 949, 936, 923, + 910, 897, 885, 873, 862, 851, 840, 829, 819, 809, 799, 789, 780, 771, 762, + 753, 744, 736, 728, 720, 712, 704, 697, 689, 682, 675, 668, 661, 655, 648, + 642, 636, 630, 624, 618, 612, 606, 601, 595, 590, 585, 579, 574, 569, 564, + 560, 555, 550, 546, 541, 537, 532, 528, 524, 520, 516, 512, 508, 504, 500, + 496, 492, 489, 485, 481, 478, 474, 471, 468, 464, 461, 458, 455, 451, 448, + 445, 442, 439, 436, 434, 431, 428, 425, 422, 420, 417, 414, 412, 409, 407, + 404, 402, 399, 397, 394, 392, 390, 387, 385, 383, 381, 378, 376, 374, 372, + 370, 368, 366, 364, 362, 360, 358, 356, 354, 352, 350, 348, 346, 344, 343, + 341, 339, 337, 336, 334, 332, 330, 329, 327, 326, 324, 322, 321, 319, 318, + 316, 315, 313, 312, 310, 309, 307, 306, 304, 303, 302, 300, 299, 297, 296, + 295, 293, 292, 291, 289, 288, 287, 286, 284, 283, 282, 281, 280, 278, 277, + 276, 275, 274, 273, 271, 270, 269, 268, 267, 266, 265, 264, 263, 262, 261, + 260, 259, 258, 257, 256, 255, 254, 253, 252, 251, 250, 249, 248, 247, 246, + 245, 244, 243, 242, 241, 240, 240, 239, 238, 237, 236, 235, 234, 234, 233, + 232, 231, 230, 229, 229, 228, 227, 226, 225, 225, 224, 223, 222, 222, 221, + 220, 219, 219, 218, 217, 217, 216, 215, 214, 214, 213, 212, 212, 211, 210, + 210, 209, 208, 208, 207, 206, 206, 205, 204, 204, 203, 202, 202, 201, 201, + 200, 199, 199, 198, 197, 197, 196, 196, 195, 195, 194, 193, 193, 192, 192, + 191, 191, 190, 189, 189, 188, 188, 187, 187, 186, 186, 185, 185, 184, 184, + 183, 183, 182, 182, 181, 181, 180, 180, 179, 179, 178, 178, 177, 177, 176, + 176, 175, 175, 174, 174, 173, 173, 172, 172, 172, 171, 171, 170, 170, 169, + 169, 168, 168, 168, 167, 167, 166, 166, 165, 165, 165, 164, 164, 163, 163, + 163, 162, 162, 161, 161, 161, 160, 160, 159, 159, 159, 158, 158, 157, 157, + 157, 156, 156, 156, 155, 155, 154, 154, 154, 153, 153, 153, 152, 152, 152, + 151, 151, 151, 150, 150, 149, 149, 149, 148, 148, 148, 147, 147, 147, 146, + 146, 146, 145, 145, 145, 144, 144, 144, 144, 143, 143, 143, 142, 142, 142, + 141, 141, 141, 140, 140, 140, 140, 139, 139, 139, 138, 138, 138, 137, 137, + 137, 137, 136, 136, 136, 135, 135, 135, 135, 134, 134, 134, 134, 133, 133, + 133, 132, 132, 132, 132, 131, 131, 131, 131, 130, 130, 130, 130, 129, 129, + 129, 129, 128, 128, 128, 128, 127, 127, 127, 127, 126, 126, 126, 126, 125, + 125, 125, 125, 124, 124, 124, 124, 123, 123, 123, 123, 122, 122, 122, 122, + 122, 121, 121, 121, 121, 120, 120, 120, 120, 120, 119, 119, 119, 119, 118, + 118, 118, 118, 118, 117, 117, 117, 117, 117, 116, 116, 116, 116, 115, 115, + 115, 115, 115, 114, 114, 114, 114, 114, 113, 113, 113, 113, 113, 112, 112, + 112, 112, 112, 112, 111, 111, 111, 111, 111, 110, 110, 110, 110, 110, 109, + 109, 109, 109, 109, 109, 108, 108, 108, 108, 108, 107, 107, 107, 107, 107, + 107, 106, 106, 106, 106, 106, 106, 105, 105, 105, 105, 105, 105, 104, 104, + 104, 104, 104, 104, 103, 103, 103, 103, 103, 103, 102, 102, 102, 102, 102, + 102, 101, 101, 101, 101, 101, 101, 100, 100, 100, 100, 100, 100, 100, 99, + 99, 99, 99, 99, 99, 98, 98, 98, 98, 98, 98, 98, 97, 97, 97, 97, 97, 97, 97, + 96, 96, 96, 96, 96, 96, 96, 95, 95, 95, 95, 95, 95, 95, 94, 94, 94, 94, 94, + 94, 94, 94, 93, 93, 93, 93, 93, 93, 93, 92, 92, 92, 92, 92, 92, 92, 92, 91, + 91, 91, 91, 91, 91, 91, 91, 90, 90, 90, 90, 90, 90, 90, 90, 89, 89, 89, 89, + 89, 89, 89, 89, 88, 88, 88, 88, 88, 88, 88, 88, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 86, 86, 86, 86, 86, 86, 86, 86, 86, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 84, 84, 84, 84, 84, 84, 84, 84, 84, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 72, 72, 72, 72, 72, 72, + 72, 72, 72, 72, 72, 72, 72, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, + 71, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 66, + 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 65, 65, 65, 65, 65, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 54, 54, 54, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 +}; + +const AAS_u16 AAS_MOD_period_conv_table[2048] = { + 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, + 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, + 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, + 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, + 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, + 65535, 65535, 65535, 65535, 65535, 64488, 63337, 62226, 61153, 60116, + 59114, 58145, 57207, 56299, 55420, 54567, 53740, 52938, 52160, 51404, + 50669, 49956, 49262, 48587, 47931, 47291, 46669, 46063, 45473, 44897, + 44336, 43788, 43254, 42733, 42224, 41728, 41242, 40768, 40305, 39852, + 39409, 38976, 38553, 38138, 37732, 37335, 36946, 36565, 36192, 35827, + 35468, 35117, 34773, 34435, 34104, 33779, 33461, 33148, 32841, 32540, + 32244, 31954, 31668, 31388, 31113, 30842, 30576, 30315, 30058, 29805, + 29557, 29313, 29072, 28836, 28603, 28375, 28149, 27928, 27710, 27495, + 27283, 27075, 26870, 26668, 26469, 26273, 26080, 25889, 25702, 25517, + 25334, 25155, 24978, 24803, 24631, 24461, 24293, 24128, 23965, 23804, + 23645, 23489, 23334, 23182, 23031, 22883, 22736, 22591, 22448, 22307, + 22168, 22030, 21894, 21760, 21627, 21496, 21366, 21238, 21112, 20987, + 20864, 20742, 20621, 20502, 20384, 20267, 20152, 20038, 19926, 19815, + 19704, 19596, 19488, 19381, 19276, 19172, 19069, 18967, 18866, 18766, + 18667, 18570, 18473, 18377, 18282, 18189, 18096, 18004, 17913, 17823, + 17734, 17646, 17558, 17472, 17386, 17301, 17217, 17134, 17052, 16970, + 16889, 16809, 16730, 16652, 16574, 16497, 16420, 16345, 16270, 16195, + 16122, 16049, 15977, 15905, 15834, 15763, 15694, 15625, 15556, 15488, + 15421, 15354, 15288, 15222, 15157, 15093, 15029, 14965, 14902, 14840, + 14778, 14717, 14656, 14596, 14536, 14477, 14418, 14359, 14301, 14244, + 14187, 14131, 14074, 14019, 13964, 13909, 13855, 13801, 13747, 13694, + 13641, 13589, 13537, 13486, 13435, 13384, 13334, 13284, 13234, 13185, + 13136, 13088, 13040, 12992, 12944, 12897, 12851, 12804, 12758, 12712, + 12667, 12622, 12577, 12533, 12489, 12445, 12401, 12358, 12315, 12272, + 12230, 12188, 12146, 12105, 12064, 12023, 11982, 11942, 11902, 11862, + 11822, 11783, 11744, 11705, 11667, 11629, 11591, 11553, 11515, 11478, + 11441, 11404, 11368, 11331, 11295, 11259, 11224, 11188, 11153, 11118, + 11084, 11049, 11015, 10981, 10947, 10913, 10880, 10846, 10813, 10780, + 10748, 10715, 10683, 10651, 10619, 10587, 10556, 10524, 10493, 10462, + 10432, 10401, 10371, 10340, 10310, 10280, 10251, 10221, 10192, 10163, + 10133, 10105, 10076, 10047, 10019, 9991, 9963, 9935, 9907, 9879, 9852, + 9825, 9798, 9771, 9744, 9717, 9690, 9664, 9638, 9612, 9586, 9560, 9534, + 9509, 9483, 9458, 9433, 9408, 9383, 9358, 9333, 9309, 9285, 9260, 9236, + 9212, 9188, 9165, 9141, 9117, 9094, 9071, 9048, 9025, 9002, 8979, 8956, + 8934, 8911, 8889, 8867, 8845, 8823, 8801, 8779, 8757, 8736, 8714, 8693, + 8672, 8650, 8629, 8608, 8588, 8567, 8546, 8526, 8505, 8485, 8465, 8444, + 8424, 8404, 8385, 8365, 8345, 8326, 8306, 8287, 8267, 8248, 8229, 8210, + 8191, 8172, 8153, 8135, 8116, 8097, 8079, 8061, 8042, 8024, 8006, 7988, + 7970, 7952, 7934, 7917, 7899, 7881, 7864, 7847, 7829, 7812, 7795, 7778, + 7761, 7744, 7727, 7710, 7693, 7677, 7660, 7644, 7627, 7611, 7595, 7578, + 7562, 7546, 7530, 7514, 7498, 7482, 7467, 7451, 7435, 7420, 7404, 7389, + 7374, 7358, 7343, 7328, 7313, 7298, 7283, 7268, 7253, 7238, 7223, 7209, + 7194, 7179, 7165, 7150, 7136, 7122, 7108, 7093, 7079, 7065, 7051, 7037, + 7023, 7009, 6995, 6982, 6968, 6954, 6941, 6927, 6914, 6900, 6887, 6873, + 6860, 6847, 6834, 6820, 6807, 6794, 6781, 6768, 6755, 6743, 6730, 6717, + 6704, 6692, 6679, 6667, 6654, 6642, 6629, 6617, 6605, 6592, 6580, 6568, + 6556, 6544, 6532, 6520, 6508, 6496, 6484, 6472, 6460, 6448, 6437, 6425, + 6413, 6402, 6390, 6379, 6367, 6356, 6345, 6333, 6322, 6311, 6299, 6288, + 6277, 6266, 6255, 6244, 6233, 6222, 6211, 6200, 6190, 6179, 6168, 6157, + 6147, 6136, 6125, 6115, 6104, 6094, 6083, 6073, 6063, 6052, 6042, 6032, + 6021, 6011, 6001, 5991, 5981, 5971, 5961, 5951, 5941, 5931, 5921, 5911, + 5901, 5891, 5882, 5872, 5862, 5852, 5843, 5833, 5824, 5814, 5805, 5795, + 5786, 5776, 5767, 5757, 5748, 5739, 5730, 5720, 5711, 5702, 5693, 5684, + 5675, 5665, 5656, 5647, 5638, 5629, 5621, 5612, 5603, 5594, 5585, 5576, + 5568, 5559, 5550, 5542, 5533, 5524, 5516, 5507, 5499, 5490, 5482, 5473, + 5465, 5456, 5448, 5440, 5431, 5423, 5415, 5406, 5398, 5390, 5382, 5374, + 5365, 5357, 5349, 5341, 5333, 5325, 5317, 5309, 5301, 5293, 5285, 5278, + 5270, 5262, 5254, 5246, 5239, 5231, 5223, 5216, 5208, 5200, 5193, 5185, + 5177, 5170, 5162, 5155, 5147, 5140, 5132, 5125, 5118, 5110, 5103, 5096, + 5088, 5081, 5074, 5066, 5059, 5052, 5045, 5038, 5031, 5023, 5016, 5009, + 5002, 4995, 4988, 4981, 4974, 4967, 4960, 4953, 4946, 4939, 4933, 4926, + 4919, 4912, 4905, 4899, 4892, 4885, 4878, 4872, 4865, 4858, 4852, 4845, + 4838, 4832, 4825, 4819, 4812, 4806, 4799, 4793, 4786, 4780, 4773, 4767, + 4760, 4754, 4748, 4741, 4735, 4729, 4722, 4716, 4710, 4704, 4697, 4691, + 4685, 4679, 4673, 4666, 4660, 4654, 4648, 4642, 4636, 4630, 4624, 4618, + 4612, 4606, 4600, 4594, 4588, 4582, 4576, 4570, 4564, 4558, 4553, 4547, + 4541, 4535, 4529, 4524, 4518, 4512, 4506, 4501, 4495, 4489, 4484, 4478, + 4472, 4467, 4461, 4455, 4450, 4444, 4439, 4433, 4428, 4422, 4417, 4411, + 4406, 4400, 4395, 4389, 4384, 4378, 4373, 4368, 4362, 4357, 4352, 4346, + 4341, 4336, 4330, 4325, 4320, 4314, 4309, 4304, 4299, 4294, 4288, 4283, + 4278, 4273, 4268, 4263, 4257, 4252, 4247, 4242, 4237, 4232, 4227, 4222, + 4217, 4212, 4207, 4202, 4197, 4192, 4187, 4182, 4177, 4172, 4167, 4163, + 4158, 4153, 4148, 4143, 4138, 4133, 4129, 4124, 4119, 4114, 4109, 4105, + 4100, 4095, 4090, 4086, 4081, 4076, 4072, 4067, 4062, 4058, 4053, 4048, + 4044, 4039, 4035, 4030, 4025, 4021, 4016, 4012, 4007, 4003, 3998, 3994, + 3989, 3985, 3980, 3976, 3971, 3967, 3963, 3958, 3954, 3949, 3945, 3940, + 3936, 3932, 3927, 3923, 3919, 3914, 3910, 3906, 3901, 3897, 3893, 3889, + 3884, 3880, 3876, 3872, 3867, 3863, 3859, 3855, 3851, 3846, 3842, 3838, + 3834, 3830, 3826, 3822, 3817, 3813, 3809, 3805, 3801, 3797, 3793, 3789, + 3785, 3781, 3777, 3773, 3769, 3765, 3761, 3757, 3753, 3749, 3745, 3741, + 3737, 3733, 3729, 3725, 3721, 3717, 3714, 3710, 3706, 3702, 3698, 3694, + 3690, 3687, 3683, 3679, 3675, 3671, 3667, 3664, 3660, 3656, 3652, 3649, + 3645, 3641, 3637, 3634, 3630, 3626, 3622, 3619, 3615, 3611, 3608, 3604, + 3600, 3597, 3593, 3589, 3586, 3582, 3579, 3575, 3571, 3568, 3564, 3561, + 3557, 3554, 3550, 3546, 3543, 3539, 3536, 3532, 3529, 3525, 3522, 3518, + 3515, 3511, 3508, 3504, 3501, 3497, 3494, 3491, 3487, 3484, 3480, 3477, + 3473, 3470, 3467, 3463, 3460, 3457, 3453, 3450, 3446, 3443, 3440, 3436, + 3433, 3430, 3426, 3423, 3420, 3417, 3413, 3410, 3407, 3403, 3400, 3397, + 3394, 3390, 3387, 3384, 3381, 3377, 3374, 3371, 3368, 3365, 3361, 3358, + 3355, 3352, 3349, 3346, 3342, 3339, 3336, 3333, 3330, 3327, 3324, 3321, + 3317, 3314, 3311, 3308, 3305, 3302, 3299, 3296, 3293, 3290, 3287, 3284, + 3281, 3278, 3275, 3272, 3269, 3266, 3263, 3260, 3257, 3254, 3251, 3248, + 3245, 3242, 3239, 3236, 3233, 3230, 3227, 3224, 3221, 3218, 3215, 3212, + 3209, 3206, 3204, 3201, 3198, 3195, 3192, 3189, 3186, 3183, 3181, 3178, + 3175, 3172, 3169, 3166, 3164, 3161, 3158, 3155, 3152, 3149, 3147, 3144, + 3141, 3138, 3136, 3133, 3130, 3127, 3125, 3122, 3119, 3116, 3114, 3111, + 3108, 3105, 3103, 3100, 3097, 3095, 3092, 3089, 3086, 3084, 3081, 3078, + 3076, 3073, 3070, 3068, 3065, 3062, 3060, 3057, 3055, 3052, 3049, 3047, + 3044, 3041, 3039, 3036, 3034, 3031, 3028, 3026, 3023, 3021, 3018, 3016, + 3013, 3010, 3008, 3005, 3003, 3000, 2998, 2995, 2993, 2990, 2988, 2985, + 2983, 2980, 2978, 2975, 2973, 2970, 2968, 2965, 2963, 2960, 2958, 2955, + 2953, 2950, 2948, 2945, 2943, 2941, 2938, 2936, 2933, 2931, 2928, 2926, + 2924, 2921, 2919, 2916, 2914, 2912, 2909, 2907, 2904, 2902, 2900, 2897, + 2895, 2893, 2890, 2888, 2886, 2883, 2881, 2878, 2876, 2874, 2871, 2869, + 2867, 2865, 2862, 2860, 2858, 2855, 2853, 2851, 2848, 2846, 2844, 2842, + 2839, 2837, 2835, 2832, 2830, 2828, 2826, 2823, 2821, 2819, 2817, 2814, + 2812, 2810, 2808, 2806, 2803, 2801, 2799, 2797, 2795, 2792, 2790, 2788, + 2786, 2784, 2781, 2779, 2777, 2775, 2773, 2771, 2768, 2766, 2764, 2762, + 2760, 2758, 2755, 2753, 2751, 2749, 2747, 2745, 2743, 2741, 2738, 2736, + 2734, 2732, 2730, 2728, 2726, 2724, 2722, 2720, 2717, 2715, 2713, 2711, + 2709, 2707, 2705, 2703, 2701, 2699, 2697, 2695, 2693, 2691, 2689, 2687, + 2685, 2682, 2680, 2678, 2676, 2674, 2672, 2670, 2668, 2666, 2664, 2662, + 2660, 2658, 2656, 2654, 2652, 2650, 2648, 2646, 2644, 2642, 2641, 2639, + 2637, 2635, 2633, 2631, 2629, 2627, 2625, 2623, 2621, 2619, 2617, 2615, + 2613, 2611, 2609, 2608, 2606, 2604, 2602, 2600, 2598, 2596, 2594, 2592, + 2590, 2588, 2587, 2585, 2583, 2581, 2579, 2577, 2575, 2573, 2572, 2570, + 2568, 2566, 2564, 2562, 2560, 2559, 2557, 2555, 2553, 2551, 2549, 2548, + 2546, 2544, 2542, 2540, 2538, 2537, 2535, 2533, 2531, 2529, 2528, 2526, + 2524, 2522, 2520, 2519, 2517, 2515, 2513, 2511, 2510, 2508, 2506, 2504, + 2503, 2501, 2499, 2497, 2496, 2494, 2492, 2490, 2489, 2487, 2485, 2483, + 2482, 2480, 2478, 2476, 2475, 2473, 2471, 2469, 2468, 2466, 2464, 2463, + 2461, 2459, 2458, 2456, 2454, 2452, 2451, 2449, 2447, 2446, 2444, 2442, + 2441, 2439, 2437, 2436, 2434, 2432, 2431, 2429, 2427, 2426, 2424, 2422, + 2421, 2419, 2417, 2416, 2414, 2412, 2411, 2409, 2407, 2406, 2404, 2403, + 2401, 2399, 2398, 2396, 2394, 2393, 2391, 2390, 2388, 2386, 2385, 2383, + 2382, 2380, 2378, 2377, 2375, 2374, 2372, 2370, 2369, 2367, 2366, 2364, + 2363, 2361, 2359, 2358, 2356, 2355, 2353, 2352, 2350, 2348, 2347, 2345, + 2344, 2342, 2341, 2339, 2338, 2336, 2335, 2333, 2331, 2330, 2328, 2327, + 2325, 2324, 2322, 2321, 2319, 2318, 2316, 2315, 2313, 2312, 2310, 2309, + 2307, 2306, 2304, 2303, 2301, 2300, 2298, 2297, 2295, 2294, 2292, 2291, + 2289, 2288, 2286, 2285, 2283, 2282, 2280, 2279, 2278, 2276, 2275, 2273, + 2272, 2270, 2269, 2267, 2266, 2264, 2263, 2262, 2260, 2259, 2257, 2256, + 2254, 2253, 2251, 2250, 2249, 2247, 2246, 2244, 2243, 2242, 2240, 2239, + 2237, 2236, 2234, 2233, 2232, 2230, 2229, 2227, 2226, 2225, 2223, 2222, + 2220, 2219, 2218, 2216, 2215, 2214, 2212, 2211, 2209, 2208, 2207, 2205, + 2204, 2203, 2201, 2200, 2198, 2197, 2196, 2194, 2193, 2192, 2190, 2189, + 2188, 2186, 2185, 2184, 2182, 2181, 2180, 2178, 2177, 2176, 2174, 2173, + 2172, 2170, 2169, 2168, 2166, 2165, 2164, 2162, 2161, 2160, 2158, 2157, + 2156, 2154, 2153, 2152, 2150, 2149, 2148, 2147, 2145, 2144, 2143, 2141, + 2140, 2139, 2137, 2136, 2135, 2134, 2132, 2131, 2130, 2128, 2127, 2126, + 2125, 2123, 2122, 2121, 2120, 2118, 2117, 2116, 2115, 2113, 2112, 2111, + 2109, 2108, 2107, 2106, 2104, 2103, 2102, 2101, 2099, 2098, 2097, 2096, + 2095, 2093, 2092, 2091, 2090, 2088, 2087, 2086, 2085, 2083, 2082, 2081, + 2080, 2079, 2077, 2076, 2075, 2074, 2072, 2071, 2070, 2069, 2068, 2066, + 2065, 2064, 2063, 2062, 2060, 2059, 2058, 2057, 2056, 2054, 2053, 2052, + 2051, 2050, 2049, 2047, 2046, 2045, 2044, 2043, 2041, 2040, 2039, 2038, + 2037, 2036, 2034, 2033, 2032, 2031, 2030, 2029, 2027, 2026, 2025, 2024, + 2023, 2022, 2021, 2019, 2018, 2017, 2016, 2015, 2014, 2012, 2011, 2010, + 2009, 2008, 2007, 2006, 2005, 2003, 2002, 2001, 2000, 1999, 1998, 1997, + 1996, 1994, 1993, 1992, 1991, 1990, 1989, 1988, 1987, 1985, 1984, 1983, + 1982, 1981, 1980, 1979, 1978, 1977, 1975, 1974, 1973, 1972, 1971, 1970, + 1969, 1968, 1967, 1966, 1965, 1963, 1962, 1961, 1960, 1959, 1958, 1957, + 1956, 1955, 1954, 1953, 1952, 1950, 1949, 1948, 1947, 1946, 1945, 1944, + 1943, 1942, 1941, 1940, 1939, 1938, 1937, 1936, 1935, 1933, 1932, 1931, + 1930, 1929, 1928, 1927, 1926, 1925, 1924, 1923, 1922, 1921, 1920, 1919, + 1918, 1917, 1916, 1915, 1914, 1913, 1912, 1911, 1910, 1908, 1907, 1906, + 1905, 1904, 1903, 1902, 1901, 1900, 1899, 1898, 1897, 1896, 1895, 1894, + 1893, 1892, 1891, 1890, 1889, 1888, 1887, 1886, 1885, 1884, 1883, 1882, + 1881, 1880, 1879, 1878, 1877, 1876, 1875, 1874, 1873, 1872, 1871, 1870, + 1869, 1868, 1867, 1866, 1865, 1864, 1863, 1862, 1861, 1860, 1859, 1858, + 1857, 1857, 1856, 1855, 1854, 1853, 1852, 1851, 1850, 1849, 1848, 1847, + 1846, 1845, 1844, 1843, 1842, 1841, 1840, 1839, 1838, 1837, 1836, 1835, + 1834, 1833, 1833, 1832, 1831, 1830, 1829, 1828, 1827, 1826, 1825, 1824, + 1823, 1822, 1821, 1820, 1819, 1818, 1817, 1817, 1816, 1815, 1814, 1813, + 1812, 1811, 1810, 1809, 1808, 1807, 1806, 1805, 1805, 1804, 1803, 1802, + 1801, 1800, 1799, 1798, 1797, 1796, 1795, 1794, 1794, 1793, 1792, 1791, + 1790, 1789, 1788, 1787, 1786, 1785, 1785, 1784, 1783, 1782, 1781, 1780, + 1779, 1778, 1777, 1777, 1776, 1775, 1774, 1773, 1772, 1771, 1770, 1769, + 1769, 1768, 1767, 1766, 1765, 1764, 1763, 1762, 1761, 1761, 1760, 1759, + 1758, 1757, 1756, 1755, 1755, 1754, 1753, 1752, 1751, 1750, 1749, 1748, + 1748, 1747, 1746, 1745, 1744, 1743, 1742, 1742, 1741, 1740, 1739, 1738, + 1737, 1736, 1736, 1735, 1734, 1733, 1732 +}; + +const AAS_u16 AAS_period_table[16][60] = +{ + { + 1712, 1616, 1524, 1440, 1356, 1280, 1208, 1140, 1076, 1016, 960 , 906, + 856 , 808 , 762 , 720 , 678 , 640 , 604 , 570 , 538 , 508 , 480 , 453, + 428 , 404 , 381 , 360 , 339 , 320 , 302 , 285 , 269 , 254 , 240 , 226, + 214 , 202 , 190 , 180 , 170 , 160 , 151 , 143 , 135 , 127 , 120 , 113, + 107 , 101 , 95 , 90 , 85 , 80 , 75 , 71 , 67 , 63 , 60 , 56 + },{ + 1700, 1604, 1514, 1430, 1348, 1274, 1202, 1134, 1070, 1010, 954 , 900, + 850 , 802 , 757 , 715 , 674 , 637 , 601 , 567 , 535 , 505 , 477 , 450, + 425 , 401 , 379 , 357 , 337 , 318 , 300 , 284 , 268 , 253 , 239 , 225, + 213 , 201 , 189 , 179 , 169 , 159 , 150 , 142 , 134 , 126 , 119 , 113, + 106 , 100 , 94 , 89 , 84 , 79 , 75 , 71 , 67 , 63 , 59 , 56 + },{ + 1688, 1592, 1504, 1418, 1340, 1264, 1194, 1126, 1064, 1004, 948 , 894, + 844 , 796 , 752 , 709 , 670 , 632 , 597 , 563 , 532 , 502 , 474 , 447, + 422 , 398 , 376 , 355 , 335 , 316 , 298 , 282 , 266 , 251 , 237 , 224, + 211 , 199 , 188 , 177 , 167 , 158 , 149 , 141 , 133 , 125 , 118 , 112, + 105 , 99 , 94 , 88 , 83 , 79 , 74 , 70 , 66 , 62 , 59 , 56 + },{ + 1676, 1582, 1492, 1408, 1330, 1256, 1184, 1118, 1056, 996 , 940 , 888, + 838 , 791 , 746 , 704 , 665 , 628 , 592 , 559 , 528 , 498 , 470 , 444, + 419 , 395 , 373 , 352 , 332 , 314 , 296 , 280 , 264 , 249 , 235 , 222, + 209 , 198 , 187 , 176 , 166 , 157 , 148 , 140 , 132 , 125 , 118 , 111, + 104 , 99 , 93 , 88 , 83 , 78 , 74 , 70 , 66 , 62 , 59 , 55 + },{ + 1664, 1570, 1482, 1398, 1320, 1246, 1176, 1110, 1048, 990 , 934 , 882, + 832 , 785 , 741 , 699 , 660 , 623 , 588 , 555 , 524 , 495 , 467 , 441, + 416 , 392 , 370 , 350 , 330 , 312 , 294 , 278 , 262 , 247 , 233 , 220, + 208 , 196 , 185 , 175 , 165 , 156 , 147 , 139 , 131 , 124 , 117 , 110, + 104 , 98 , 92 , 87 , 82 , 78 , 73 , 69 , 65 , 62 , 58 , 55 + },{ + 1652, 1558, 1472, 1388, 1310, 1238, 1168, 1102, 1040, 982 , 926 , 874, + 826 , 779 , 736 , 694 , 655 , 619 , 584 , 551 , 520 , 491 , 463 , 437, + 413 , 390 , 368 , 347 , 328 , 309 , 292 , 276 , 260 , 245 , 232 , 219, + 206 , 195 , 184 , 174 , 164 , 155 , 146 , 138 , 130 , 123 , 116 , 109, + 103 , 97 , 92 , 87 , 82 , 77 , 73 , 69 , 65 , 61 , 58 , 54 + },{ + 1640, 1548, 1460, 1378, 1302, 1228, 1160, 1094, 1032, 974 , 920 , 868, + 820 , 774 , 730 , 689 , 651 , 614 , 580 , 547 , 516 , 487 , 460 , 434, + 410 , 387 , 365 , 345 , 325 , 307 , 290 , 274 , 258 , 244 , 230 , 217, + 205 , 193 , 183 , 172 , 163 , 154 , 145 , 137 , 129 , 122 , 115 , 109, + 102 , 96 , 91 , 86 , 81 , 77 , 72 , 68 , 64 , 61 , 57 , 54 + },{ + 1628, 1536, 1450, 1368, 1292, 1220, 1150, 1086, 1026, 968 , 914 , 862, + 814 , 768 , 725 , 684 , 646 , 610 , 575 , 543 , 513 , 484 , 457 , 431, + 407 , 384 , 363 , 342 , 323 , 305 , 288 , 272 , 256 , 242 , 228 , 216, + 204 , 192 , 181 , 171 , 161 , 152 , 144 , 136 , 128 , 121 , 114 , 108, + 102 , 96 , 90 , 85 , 80 , 76 , 72 , 68 , 64 , 60 , 57 , 54 + },{ + 1814, 1712, 1616, 1524, 1440, 1356, 1280, 1208, 1140, 1076, 1016, 960, + 907 , 856 , 808 , 762 , 720 , 678 , 640 , 604 , 570 , 538 , 508 , 480, + 453 , 428 , 404 , 381 , 360 , 339 , 320 , 302 , 285 , 269 , 254 , 240, + 226 , 214 , 202 , 190 , 180 , 170 , 160 , 151 , 143 , 135 , 127 , 120, + 113 , 107 , 101 , 95 , 90 , 85 , 80 , 75 , 71 , 67 , 63 , 60 + },{ + 1800, 1700, 1604, 1514, 1430, 1350, 1272, 1202, 1134, 1070, 1010, 954, + 900 , 850 , 802 , 757 , 715 , 675 , 636 , 601 , 567 , 535 , 505 , 477, + 450 , 425 , 401 , 379 , 357 , 337 , 318 , 300 , 284 , 268 , 253 , 238, + 225 , 212 , 200 , 189 , 179 , 169 , 159 , 150 , 142 , 134 , 126 , 119, + 112 , 106 , 100 , 94 , 89 , 84 , 79 , 75 , 71 , 67 , 63 , 59 + },{ + 1788, 1688, 1592, 1504, 1418, 1340, 1264, 1194, 1126, 1064, 1004, 948, + 894 , 844 , 796 , 752 , 709 , 670 , 632 , 597 , 563 , 532 , 502 , 474, + 447 , 422 , 398 , 376 , 355 , 335 , 316 , 298 , 282 , 266 , 251 , 237, + 223 , 211 , 199 , 188 , 177 , 167 , 158 , 149 , 141 , 133 , 125 , 118, + 111 , 105 , 99 , 94 , 88 , 83 , 79 , 74 , 70 , 66 , 62 , 59 + },{ + 1774, 1676, 1582, 1492, 1408, 1330, 1256, 1184, 1118, 1056, 996 , 940, + 887 , 838 , 791 , 746 , 704 , 665 , 628 , 592 , 559 , 528 , 498 , 470, + 444 , 419 , 395 , 373 , 352 , 332 , 314 , 296 , 280 , 264 , 249 , 235, + 222 , 209 , 198 , 187 , 176 , 166 , 157 , 148 , 140 , 132 , 125 , 118, + 111 , 104 , 99 , 93 , 88 , 83 , 78 , 74 , 70 , 66 , 62 , 59 + },{ + 1762, 1664, 1570, 1482, 1398, 1320, 1246, 1176, 1110, 1048, 988 , 934, + 881 , 832 , 785 , 741 , 699 , 660 , 623 , 588 , 555 , 524 , 494 , 467, + 441 , 416 , 392 , 370 , 350 , 330 , 312 , 294 , 278 , 262 , 247 , 233, + 220 , 208 , 196 , 185 , 175 , 165 , 156 , 147 , 139 , 131 , 123 , 117, + 110 , 104 , 98 , 92 , 87 , 82 , 78 , 73 , 69 , 65 , 61 , 58 + },{ + 1750, 1652, 1558, 1472, 1388, 1310, 1238, 1168, 1102, 1040, 982 , 926, + 875 , 826 , 779 , 736 , 694 , 655 , 619 , 584 , 551 , 520 , 491 , 463, + 437 , 413 , 390 , 368 , 347 , 328 , 309 , 292 , 276 , 260 , 245 , 232, + 219 , 206 , 195 , 184 , 174 , 164 , 155 , 146 , 138 , 130 , 123 , 116, + 109 , 103 , 97 , 92 , 87 , 82 , 77 , 73 , 69 , 65 , 61 , 58 + },{ + 1736, 1640, 1548, 1460, 1378, 1302, 1228, 1160, 1094, 1032, 974 , 920, + 868 , 820 , 774 , 730 , 689 , 651 , 614 , 580 , 547 , 516 , 487 , 460, + 434 , 410 , 387 , 365 , 345 , 325 , 307 , 290 , 274 , 258 , 244 , 230, + 217 , 205 , 193 , 183 , 172 , 163 , 154 , 145 , 137 , 129 , 122 , 115, + 108 , 102 , 96 , 91 , 86 , 81 , 77 , 72 , 68 , 64 , 61 , 57 + },{ + 1724, 1628, 1536, 1450, 1368, 1292, 1220, 1150, 1086, 1026, 968 , 914, + 862 , 814 , 768 , 725 , 684 , 646 , 610 , 575 , 543 , 513 , 484 , 457, + 431 , 407 , 384 , 363 , 342 , 323 , 305 , 288 , 272 , 256 , 242 , 228, + 216 , 203 , 192 , 181 , 171 , 161 , 152 , 144 , 136 , 128 , 121 , 114, + 108 , 101 , 96 , 90 , 85 , 80 , 76 , 72 , 68 , 64 , 60 , 57 + } +}; + +const AAS_u16 AAS_tick_rate[41] = { + 65535, 44565, 55051, 58546, 60294, 61342, 62041, 62541, 62915, 63206, + 63439, 63630, 63789, 63923, 64039, 64138, 64226, 64303, 64371, 64433, + 64488, 64538, 64583, 64625, 64663, 64698, 64730, 64760, 64788, 64813, + 64837, 64860, 64881, 64901, 64920, 64937, 64954, 64970, 64985, 64999, 65012 +}; + +const AAS_s8 AAS_sin[64] = { + 0, -12, -24, -37, -48, -60, -71, -81, -90, -98, -106, -112, -118, -122, + -125, -127, -127, -127, -125, -122, -118, -112, -106, -99, -90, -81, -71, + -60, -49, -37, -25, -12, 0, 12, 24, 36, 48, 60, 70, 81, 90, 98, 106, 112, + 118, 122, 125, 127, 127, 127, 125, 122, 118, 113, 106, 99, 90, 81, 71, 60, + 49, 37, 25, 12 +}; + +AAS_BOOL AAS_initialised AAS_IN_EWRAM = AAS_FALSE; +AAS_u8 AAS_volscale AAS_IN_EWRAM; +AAS_u16 AAS_mix_scale AAS_IN_EWRAM; +AAS_BOOL AAS_changed[2] AAS_IN_IWRAM; + +AAS_u8 AAS_chan_rearrange[AAS_MAX_CHANNELS] AAS_IN_EWRAM; + +struct AAS_Channel AAS_channels[16] AAS_IN_EWRAM; + +/* 2560 bytes, must be aligned to 4 byte boundary, could be just 640 bytes if mix rate was 8KHz */ +AAS_u32 AAS_mix_buffer[640] AAS_IN_EWRAM; diff --git a/libs/aas/AAS_Shared.h b/libs/aas/AAS_Shared.h new file mode 100644 index 0000000..877c0d2 --- /dev/null +++ b/libs/aas/AAS_Shared.h @@ -0,0 +1,83 @@ +/* Copyright (c) 2003-2021 James Daniels */ +/* Distributed under the MIT License */ +/* license terms: see LICENSE file in root or http://opensource.org/licenses/MIT */ + +#ifndef __AAS_SHARED__ +#define __AAS_SHARED__ + +#include "AAS.h" +#include "AAS_Mixer.h" + +#define AAS_MAX_CHANNELS 16 + +#define REG_SOUNDCNT_L (*(volatile AAS_u16 *)0x4000080) +#define REG_SOUNDCNT_H (*(volatile AAS_u16 *)0x4000082) +#define REG_SOUNDCNT_X (*(volatile AAS_u16 *)0x4000084) + +#define REG_DMA1SAD (*(volatile AAS_u32 *)0x40000BC) +#define REG_DMA1DAD (*(volatile AAS_u32 *)0x40000C0) +#define REG_DMA1CNT_H (*(volatile AAS_u16 *)0x40000C6) +#define REG_DMA1CNT (*(volatile AAS_u32 *)0x40000C4) + +#define REG_DMA2SAD (*(volatile AAS_u32 *)0x40000C8) +#define REG_DMA2DAD (*(volatile AAS_u32 *)0x40000CC) +#define REG_DMA2CNT_H (*(volatile AAS_u16 *)0x40000D2) +#define REG_DMA2CNT (*(volatile AAS_u32 *)0x40000d0) + +#define REG_TM0CNT (*(volatile AAS_u16 *)0x4000102) +#define REG_TM0D (*(volatile AAS_u16 *)0x4000100) + +#define REG_TM1CNT (*(volatile AAS_u16 *)0x4000106) +#define REG_TM1D (*(volatile AAS_u16 *)0x4000104) + +#define REG_IE (*(volatile AAS_u16 *)0x4000200) +#define REG_IME (*(volatile AAS_u16 *)0x4000208) +#define REG_IF (*(volatile AAS_u16 *)0x4000202) + +#define DISPCNT (*(volatile AAS_u16 *)0x4000000) +#define REG_VCOUNT (*(volatile AAS_u16 *)0x4000006) + +struct AAS_ModSample +{ + AAS_u32 data; /* offset in bytes */ + AAS_u16 repeat; /* in halfwords */ + AAS_u16 length; /* in halfwords */ + AAS_u8 finetune; + AAS_u8 volume; + AAS_u16 padding; +}; + +extern const AAS_u16 AAS_DATA_NUM_MODS; +extern const struct AAS_ModSample AAS_ModSamples[][31]; +extern const AAS_s16 AAS_Sequence[][128][16]; +extern const AAS_u8 AAS_NumChans[]; +extern const AAS_u8 AAS_RestartPos[]; +extern const AAS_u8 AAS_PatternData[]; +extern const AAS_s8 AAS_SampleData[]; + +extern const AAS_u16 AAS_DivTable[8192]; +extern const AAS_u16 AAS_MOD_period_conv_table[2048]; +extern const AAS_u16 AAS_period_table[16][60]; + +extern const AAS_u16 AAS_tick_rate[41]; + +extern const AAS_s8 AAS_sin[64]; + +extern AAS_s16 AAS_mod_num AAS_IN_EWRAM; +extern AAS_BOOL AAS_initialised AAS_IN_EWRAM; +extern AAS_u8 AAS_volscale AAS_IN_EWRAM; +extern AAS_u8 AAS_mod_num_chans AAS_IN_EWRAM; +extern AAS_u16 AAS_mix_scale AAS_IN_EWRAM; +extern AAS_BOOL AAS_changed[2] AAS_IN_IWRAM; + +extern AAS_u8 AAS_chan_rearrange[AAS_MAX_CHANNELS] AAS_IN_EWRAM; + +__inline static int AAS_Min( int a, int b ) +{ + if ( a < b ) + return a; + else + return b; +} + +#endif diff --git a/libs/aas/Makefile b/libs/aas/Makefile new file mode 100644 index 0000000..2c84074 --- /dev/null +++ b/libs/aas/Makefile @@ -0,0 +1,21 @@ +obj = AAS_ASM.o AAS_Main.o AAS_MOD.o AAS_Shared.o AAS_SFX.o AAS_Mixer.o +lib_a = libaas.a + +TCPREFIX = arm-none-eabi- +CC = $(TCPREFIX)gcc +AS = $(TCPREFIX)as +AR = $(TCPREFIX)ar + +CFLAGS = -mthumb -O3 -funroll-loops -mcpu=arm7tdmi -ffast-math \ + -fno-exceptions -mthumb-interwork -fomit-frame-pointer +ASFLAGS = -mthumb-interwork + +$(lib_a): $(obj) + $(AR) rcs $@ $(obj) + +%.o: %.s + $(AS) $(ASFLAGS) -o $@ $< + +.PHONY: clean +clean: + rm -f $(obj) $(lib_a) diff --git a/src/data.h b/src/data.h new file mode 100644 index 0000000..3f92927 --- /dev/null +++ b/src/data.h @@ -0,0 +1,6 @@ +#ifndef DATA_H_ +#define DATA_H_ + +#include "data/aas_data.h" + +#endif /* DATA_H_ */ diff --git a/src/intr.c b/src/intr.c index 80ee066..7d8bdf2 100644 --- a/src/intr.c +++ b/src/intr.c @@ -9,7 +9,7 @@ static void intr_handler(void) int i; uint16_t iflags; - clr_intr(); + intr_disable(); iflags = REG_IF & 0x3fff; @@ -20,7 +20,7 @@ static void intr_handler(void) } REG_IF = iflags; /* ack intr */ - set_intr(); + intr_enable(); } void intr_init(void) diff --git a/src/intr.h b/src/intr.h index d37e012..859b9a7 100644 --- a/src/intr.h +++ b/src/intr.h @@ -24,9 +24,9 @@ enum { void intr_init(void); /* set/clear interrupts */ -#define set_intr() \ +#define intr_enable() \ do { REG_IME |= 0x0001; } while(0) -#define clr_intr() \ +#define intr_disable() \ do { REG_IME &= 0xfffe; } while(0) /* set an interrupt handler */ diff --git a/src/main.c b/src/main.c index d7a3308..03930b1 100644 --- a/src/main.c +++ b/src/main.c @@ -3,6 +3,9 @@ #include "intr.h" #include "debug.h" +#include "AAS.h" +#include "data.h" + #define RGB15(r, g, b) \ (((uint16_t)(r) & 0x1f) | \ (((uint16_t)(g) & 0x1f) << 5) | \ @@ -17,6 +20,12 @@ int main(void) intr_init(); + interrupt(INTR_TIMER1, AAS_Timer1InterruptHandler); + AAS_SetConfig(AAS_CONFIG_MIX_24KHZ, AAS_CONFIG_CHANS_8, AAS_CONFIG_SPATIAL_STEREO, AAS_CONFIG_DYNAMIC_ON); + unmask(INTR_TIMER1); + intr_enable(); + + REG_DISPCNT = 3 | DISPCNT_BG2; vptr = (uint16_t*)VRAM_START_ADDR; @@ -31,6 +40,8 @@ int main(void) } } + AAS_MOD_Play(AAS_DATA_MOD_popcorn); + for(;;); return 0; } diff --git a/tools/conv2aas/Makefile b/tools/conv2aas/Makefile new file mode 100644 index 0000000..af55b98 --- /dev/null +++ b/tools/conv2aas/Makefile @@ -0,0 +1,11 @@ +obj = conv2aas.o +bin = conv2aas + +CFLAGS = -pedantic -Wall -g + +$(bin): $(obj) + $(CC) -o $@ $(obj) $(LDFLAGS) + +.PHONY: clean +clean: + rm -f $(obj) $(bin) diff --git a/tools/conv2aas/conv2aas.c b/tools/conv2aas/conv2aas.c new file mode 100644 index 0000000..679f15d --- /dev/null +++ b/tools/conv2aas/conv2aas.c @@ -0,0 +1,1247 @@ +/* Copyright (c) 2003-2021 James Daniels */ +/* Distributed under the MIT License */ +/* license terms: see LICENSE file in root or http://opensource.org/licenses/MIT */ + +/* Notes: */ +/* + Max number of MODs = 256 */ +/* + Max total size of all samples = 33,554,432 bytes */ +/* + Max total size of all patterns = 33,554,432 bytes */ +/* + All functions declared static to work around problem with GCC */ + +#include +#include +#include +#include +#include + +#define BYTE signed char +#define UBYTE unsigned char +#define BOOL unsigned char +#define UWORD unsigned short +#define ULONG unsigned int +#define WORD signed short +#define TRUE 1 +#define FALSE 0 + +__inline static int Min(int a, int b) +{ + if (a < b) + return a; + else + return b; +} + +__inline static UWORD SwapUWORD( UWORD a ) +{ + return (a>>8)+((a&0xff)<<8); +} + +static int memcmp_new( UBYTE* a, UBYTE* b, int length ) +{ + for(; length > 0; --length ) + if ( *a++ != *b++ ) + return 1; + + return 0; +} + +static void memcpy_new( UBYTE* dest, UBYTE* source, int length ) +{ + for(; length > 0; --length ) + *dest++ = *source++; +} + +#define MAX_DATA_LENGTH 33554432 +typedef struct data_pack +{ + const char* chunk_name; + int chunk_data_length; + int chunk_unique; + int chunk_actual; + unsigned char chunk_data[MAX_DATA_LENGTH]; +} DATAPACK; + +static DATAPACK* DATA_Create( const char* name ) +{ + DATAPACK* new_data = (DATAPACK*)malloc(sizeof(DATAPACK)); + + if ( new_data ) + { + /*printf( "DATA_Create( %s ): addr:%p\n", name, new_data ); */ + + new_data->chunk_name = name; + new_data->chunk_data_length = 0; + new_data->chunk_unique = 0; + new_data->chunk_actual = 0; + } + else + { + printf( "DATA_Create( %s ): Failed (address:%p length:%d)...\n", name, (void*)new_data, (int)sizeof(DATAPACK) ); + } + + return new_data; +} + +static int DATA_AppendAligned( DATAPACK* curr_data, unsigned char* data, int length, int block_length ) +{ + ++curr_data->chunk_actual; + + if ( (block_length*((int)(length/block_length))) != length ) + { + printf( "DATA_AppendAligned( %p, %p, %d, %d ): Warning length not exactly divisible by block_length!\n", + (void*)curr_data, data, length, block_length ); + return 0; + } + else if ( length == 0 ) + { + return 0; + } + else + { + int offset = 0; + + while ( (offset + block_length) <= curr_data->chunk_data_length ) + { + if ( memcmp( data, curr_data->chunk_data + offset, length ) == 0 ) + { + return offset/block_length; + } + + offset += block_length; + }; + + if ( curr_data->chunk_data_length <= (MAX_DATA_LENGTH-length) ) + { + memcpy( curr_data->chunk_data + offset, data, length ); + curr_data->chunk_data_length += length; + ++curr_data->chunk_unique; + return offset/block_length; + } + else + { + --curr_data->chunk_actual; + printf( "DATA_AppendAligned( %p, %p, %d, %d ): Data >%d bytes!\n", + (void*)curr_data, data, length, block_length, MAX_DATA_LENGTH ); + return 0; + } + } +} + +static int DATA_Append( DATAPACK* curr_data, unsigned char* data, int length ) +{ + ++curr_data->chunk_actual; + + if ( length == 0 ) + { + return 0; + } + else + { + int offset = 0; + + while ( (offset + length) <= curr_data->chunk_data_length ) + { + if ( memcmp_new( data, curr_data->chunk_data + offset, length ) == 0 ) + return offset; + ++offset; + }; + + if ( curr_data->chunk_data_length <= (MAX_DATA_LENGTH-length) ) + { + offset = curr_data->chunk_data_length; + memcpy_new( curr_data->chunk_data + offset, data, length ); + curr_data->chunk_data_length += length; + ++curr_data->chunk_unique; + return offset; + } + else + { + --curr_data->chunk_actual; + printf( "DATA_Append( %p, %d ): Data >%d bytes!\n", data, length, MAX_DATA_LENGTH ); + return 0; + } + } +} + +static void DATA_Write( DATAPACK* curr_data, FILE* out_file ) +{ + int i, len; + UBYTE val; + + len = curr_data->chunk_data_length; + if ( len & 0x3 ) + { + len &= ~0x3; + len += 0x4; + } + + printf( "Writing %s (Unique:%d Actual:%d Length:%d)...", curr_data->chunk_name, curr_data->chunk_unique, curr_data->chunk_actual, len ); + + fprintf( out_file, "\n.ALIGN\n.GLOBAL %s\n%s:\n.byte", curr_data->chunk_name, curr_data->chunk_name ); + + if ( len > 0 ) + { + for( i = 0; i < len; ++i ) + { + if ( i >= curr_data->chunk_data_length ) + val = 0; + else + val = *(curr_data->chunk_data + i); + if ( i == 0 ) + fprintf( out_file, " %d", val ); + else + fprintf( out_file, ", %d", val ); + } + fprintf( out_file, "\n" ); + } + else + { + fprintf( out_file, " 0\n" ); + } + + printf( "Done!\n" ); + + free( curr_data ); +} + +struct ModSampleTemp +{ + UWORD repeat; /* in halfwords */ + UWORD length; /* in halfwords */ + UWORD truelen; /* in halfwords */ + UBYTE volume; + UBYTE finetune; +}; + +struct ModSample +{ + ULONG data; /* offset in bytes */ + UWORD repeat; /* in halfwords */ + UWORD length; /* in halfwords */ + UBYTE finetune; + UBYTE volume; +}; + +static void MISC_ProcessSound( BYTE* data, int length ) +{ + for(; length > 0; --length ) + { + int val = *data; + + if ( val == -128 ) + val = -127; + + *data++ = val; + } +} + +static void MISC_ConvSoundToSigned( UBYTE* data, int length ) +{ + BYTE* bdata = (BYTE*)data; + for(; length > 0; --length ) + { + int val = *data; + val -= 128; + *bdata++ = val; + } +} + +static void MOD_LoadSound( struct ModSample* smp, FILE* in_file, DATAPACK* samples, int length, int truelen, int repeat ) +{ + BYTE* samp = malloc( (length<<1)+16 ); + int data; + + fread( samp+16, (length<<1), 1, in_file ); + + if ( truelen > 0 ) + { + memset( samp, 0, 16 ); + if ( (repeat < 65535) && (truelen > repeat) ) + memcpy( samp+(repeat<<1), samp+(truelen<<1), 16 ); /* should really merge, also wasteful of memory */ + MISC_ProcessSound( samp, (truelen<<1)+16 ); + data = DATA_Append( samples, (UBYTE*)samp, (truelen<<1)+16 ); + smp->data = data+16; + } + else + { + smp->data = 0; + } + smp->length = truelen; + + free( samp ); +} + +#define MAX_MODS 256 + +static struct ModSample samps[MAX_MODS][31]; +static WORD patts[MAX_MODS][128][16]; /* [mod][pattern][channel] */ +static UBYTE mod_num_chans[MAX_MODS]; +static UBYTE mod_song_restart_pos[MAX_MODS]; +static int max_song_length = 0; + +static const char* effect_name[32] = { "*0: Arpeggio", "*1: Slide Up", "*2: Slide Down", "*3: Tone Portamento", "*4: Vibrato", "*5: Tone Portamento + Volume Slide", "*6: Vibrato + Volume Slide", "*7: Tremolo", "8: Set Panning Position", "*9: Set Sample Offset", "*A: Volume Slide", "*B: Position Jump", "*C: Set Volume", "*D: Pattern Break", "*F: Set Speed (BPM)", "*F: Set Speed (Ticks)", "*E0: Set Filter", "*E1: Fine Slide Up", "*E2: Fine Slide Down", "E3: Glissando Control", "E4: Set Vibrato Waveform", "E5: Set FineTune", "*E6: Set/Jump to Loop", "E7: Set Tremolo Waveform", "E8: Illegal", "*E9: Retrigger Note", "*EA: Fine Volume Slide Up", "*EB: Fine Volume Slide Down", "*EC: Note Cut", "*ED: Note Delay", "*EE: Pattern Delay", "EF: Invert Loop" }; + +static int MOD_FindNote( int period ) +{ + const int period_table[61] = + { + 0, + 1712, 1616, 1524, 1440, 1356, 1280, 1208, 1140, 1076, 1016, 960 , 906, + 856, 808 , 762 , 720 , 678 , 640 , 604 , 570 , 538 , 508 , 480 , 453, + 428, 404 , 381 , 360 , 339 , 320 , 302 , 285 , 269 , 254 , 240 , 226, + 214, 202 , 190 , 180 , 170 , 160 , 151 , 143 , 135 , 127 , 120 , 113, + 107, 101 , 95 , 90 , 85 , 80 , 75 , 71 , 67 , 63 , 60 , 56 + }; + int i, d, r; + int closest = 1000000000; + + r = -1; + + for( i = 0; i < 61; ++i ) + { + d = abs( period_table[i] - period ); + if ( d < closest ) + { + r = i; + closest = d; + } + } + + if ( r == -1 ) + { + printf( "Can't find note with period:%d...\n", period ); + } + + return r; +} + +#define FOUR_CHAR_CODE( ch0, ch1, ch2, ch3 ) \ + ( (long)(unsigned char)(ch0) | ( (long)(unsigned char)(ch1) << 8 ) | \ + ( (long)(unsigned char)(ch2) << 16 ) | ( (long)(unsigned char)(ch3) << 24 ) ) + +static int MOD_GetNumChans( ULONG file_format ) +{ + switch( file_format ) + { + case FOUR_CHAR_CODE( 'T', 'D', 'Z', '1'): + return 1; + break; + + case FOUR_CHAR_CODE( '2', 'C', 'H', 'N'): + case FOUR_CHAR_CODE( 'T', 'D', 'Z', '2'): + return 2; + break; + + case FOUR_CHAR_CODE( 'T', 'D', 'Z', '3'): + return 3; + break; + + case FOUR_CHAR_CODE( 'M', '.', 'K', '.'): + case FOUR_CHAR_CODE( 'F', 'L', 'T', '4'): + case FOUR_CHAR_CODE( 'M', '!', 'K', '!'): + return 4; + break; + + case FOUR_CHAR_CODE( '5', 'C', 'H', 'N'): + return 5; + break; + + case FOUR_CHAR_CODE( '6', 'C', 'H', 'N'): + return 6; + break; + + case FOUR_CHAR_CODE( '7', 'C', 'H', 'N'): + return 7; + break; + + case FOUR_CHAR_CODE( '8', 'C', 'H', 'N'): + case FOUR_CHAR_CODE( 'O', 'C', 'T', 'A'): + case FOUR_CHAR_CODE( 'C', 'D', '8', '1'): + return 8; + break; + + case FOUR_CHAR_CODE( 'F', 'L', 'T', '8'): + return -2; /* Unsupported MOD type */ + break; + + case FOUR_CHAR_CODE( '9', 'C', 'H', 'N'): + return 9; + break; + + case FOUR_CHAR_CODE( '1', '0', 'C', 'H'): + return 10; + break; + + case FOUR_CHAR_CODE( '1', '1', 'C', 'H'): + return 11; + break; + + case FOUR_CHAR_CODE( '1', '2', 'C', 'H'): + return 12; + break; + + case FOUR_CHAR_CODE( '1', '3', 'C', 'H'): + return 13; + break; + + case FOUR_CHAR_CODE( '1', '4', 'C', 'H'): + return 14; + break; + + case FOUR_CHAR_CODE( '1', '5', 'C', 'H'): + return 15; + break; + + case FOUR_CHAR_CODE( '1', '6', 'C', 'H'): + return 16; + break; + + case FOUR_CHAR_CODE( '1', '7', 'C', 'H'): + case FOUR_CHAR_CODE( '1', '8', 'C', 'H'): + case FOUR_CHAR_CODE( '1', '9', 'C', 'H'): + case FOUR_CHAR_CODE( '2', '0', 'C', 'H'): + case FOUR_CHAR_CODE( '2', '1', 'C', 'H'): + case FOUR_CHAR_CODE( '2', '2', 'C', 'H'): + case FOUR_CHAR_CODE( '2', '3', 'C', 'H'): + case FOUR_CHAR_CODE( '2', '4', 'C', 'H'): + case FOUR_CHAR_CODE( '2', '5', 'C', 'H'): + case FOUR_CHAR_CODE( '2', '6', 'C', 'H'): + case FOUR_CHAR_CODE( '2', '7', 'C', 'H'): + case FOUR_CHAR_CODE( '2', '8', 'C', 'H'): + case FOUR_CHAR_CODE( '2', '9', 'C', 'H'): + case FOUR_CHAR_CODE( '3', '0', 'C', 'H'): + case FOUR_CHAR_CODE( '3', '1', 'C', 'H'): + case FOUR_CHAR_CODE( '3', '2', 'C', 'H'): + case FOUR_CHAR_CODE( '3', '3', 'C', 'H'): + case FOUR_CHAR_CODE( '3', '4', 'C', 'H'): + case FOUR_CHAR_CODE( '3', '5', 'C', 'H'): + case FOUR_CHAR_CODE( '3', '6', 'C', 'H'): + case FOUR_CHAR_CODE( '3', '7', 'C', 'H'): + case FOUR_CHAR_CODE( '3', '8', 'C', 'H'): + case FOUR_CHAR_CODE( '3', '9', 'C', 'H'): + case FOUR_CHAR_CODE( '4', '0', 'C', 'H'): + case FOUR_CHAR_CODE( '4', '1', 'C', 'H'): + case FOUR_CHAR_CODE( '4', '2', 'C', 'H'): + case FOUR_CHAR_CODE( '4', '3', 'C', 'H'): + case FOUR_CHAR_CODE( '4', '4', 'C', 'H'): + case FOUR_CHAR_CODE( '4', '5', 'C', 'H'): + case FOUR_CHAR_CODE( '4', '6', 'C', 'H'): + case FOUR_CHAR_CODE( '4', '7', 'C', 'H'): + case FOUR_CHAR_CODE( '4', '8', 'C', 'H'): + case FOUR_CHAR_CODE( '4', '9', 'C', 'H'): + case FOUR_CHAR_CODE( '5', '0', 'C', 'H'): + case FOUR_CHAR_CODE( '5', '1', 'C', 'H'): + case FOUR_CHAR_CODE( '5', '2', 'C', 'H'): + case FOUR_CHAR_CODE( '5', '3', 'C', 'H'): + case FOUR_CHAR_CODE( '5', '4', 'C', 'H'): + case FOUR_CHAR_CODE( '5', '5', 'C', 'H'): + case FOUR_CHAR_CODE( '5', '6', 'C', 'H'): + case FOUR_CHAR_CODE( '5', '7', 'C', 'H'): + case FOUR_CHAR_CODE( '5', '8', 'C', 'H'): + case FOUR_CHAR_CODE( '5', '9', 'C', 'H'): + case FOUR_CHAR_CODE( '6', '0', 'C', 'H'): + case FOUR_CHAR_CODE( '6', '1', 'C', 'H'): + case FOUR_CHAR_CODE( '6', '2', 'C', 'H'): + case FOUR_CHAR_CODE( '6', '3', 'C', 'H'): + case FOUR_CHAR_CODE( '6', '4', 'C', 'H'): + return -3; /* Too many channels */ + break; + + default: + return -1; /* Unrecognised MOD type */ + break; + } + + /* 'FLT4', 'FLT8': Startrekker 4/8 channel file. ('FLT6' doesn't exist) */ + /* 'CD81' : Falcon 8 channel MODs */ + /* '2CHN' : FastTracker 2 Channel MODs */ + /* 'yyCH' where yy can be 10, 12, .. 30, 32: FastTracker yy Channel MODs */ + /* 'yyCH' where yy can be 11, 13, 15: TakeTracker 11, 13, 15 channel MODs */ + /* 'TDZx' where x can be 1, 2 or 3: TakeTracker 1, 2, 3 channel MODs */ + /* ModPlug Tracker saves 33-64 channel MODs as '33CH' to '64CH' */ +} + +static void MOD_ConvMod( FILE* out_file, DATAPACK* samples, DATAPACK* patterns, const char* filename, int mod_num ) +{ + FILE* in_file; + + mod_num_chans[mod_num] = 0; + + in_file = fopen( filename, "rb" ); + if ( in_file ) + { + struct ModSampleTemp samps_temp[32]; + char temp[23]; + int i, tmp, line, chan; + UBYTE song_length, last_pattern; + UBYTE song_data[128]; + int pat[64][16]; + UWORD tmp_uword; + UBYTE tmp_ubyte; + ULONG notes_temp[16][64]; + int effect_count[32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + BOOL first; + ULONG new_temp; + int num_chans; + UBYTE song_restart_pos; + + /*printf( "\nMOD_ConvMod( %s ): Reading...\n", filename ); */ + + fread( temp, 20, 1, in_file ); /* mod name */ + + for( i = 1; i <= 31; ++i ) + { + fread( temp, 22, 1, in_file ); /* sample name */ + + fread( &tmp_uword, 2, 1, in_file ); /* sample length */ + samps_temp[i].length = SwapUWORD( tmp_uword ); + + fread( &tmp_ubyte, 1, 1, in_file ); /* finetune */ + /*if ( tmp_ubyte & 8 ) */ + /* tmp_ubyte -= 8; */ + /*else */ + /* tmp_ubyte += 8; */ + samps_temp[i].finetune = tmp_ubyte; + if ( tmp_ubyte > 15 ) + printf( "MOD_ConvMod( %s ): Error: finetune > 15...\n", filename ); + + fread( &tmp_ubyte, 1, 1, in_file ); /* volume */ + samps_temp[i].volume = tmp_ubyte; + + if ( tmp_ubyte > 64 ) + { + printf( "Failed! Reason: Sample vol > 64\n" ); + return; + } + + fread( &tmp_uword, 2, 1, in_file ); + samps_temp[i].repeat = SwapUWORD( tmp_uword ); + + fread( &tmp_uword, 2, 1, in_file ); + tmp = SwapUWORD( tmp_uword ); + + /*samps_temp[i].truelen = samps_temp[i].length; */ + + if ( tmp <= 1 ) + { + samps_temp[i].repeat = 65535; + samps_temp[i].truelen = samps_temp[i].length; + } + else + { + samps_temp[i].truelen = Min( samps_temp[i].repeat + tmp, samps_temp[i].length ); + } + + /*printf( "i:%d len:%d vol:%d rep:%d fine:%d\n", i, samps_temp[i].truelen, samps_temp[i].volume, samps_temp[i].repeat, samps_temp[i].finetune ); */ + } + + fread( &song_length, 1, 1, in_file ); + fread( &song_restart_pos, 1, 1, in_file ); + + if ( (song_length < 1) || (song_length > 128) ) + printf( "MOD_ConvMod( %s ): song_length:%d\n", filename, song_length ); + + last_pattern = 0; + for( i = 0; i <= 127; ++i ) + { + fread( &tmp_ubyte, 1, 1, in_file ); + song_data[i] = tmp_ubyte; + if ( tmp_ubyte > last_pattern ) + last_pattern = tmp_ubyte; + } + + if ( last_pattern > 63 ) + printf( "MOD_ConvMod( %s ): last_pattern:%d\n", filename, last_pattern ); + + fread( &new_temp, 4, 1, in_file ); + num_chans = MOD_GetNumChans( new_temp ); + if ( num_chans <= 0 ) + { + switch( num_chans ) + { + case -2: /* Unsupported MOD type */ + printf( "Failed! Reason: Unsupported MOD type (%x)\n", new_temp ); + break; + + case -3: /* Too many channels */ + printf( "Failed! Reason: Too many channels\n" ); + break; + + default: /* Unrecognised MOD type */ + printf( "Failed! Reason: Unrecognised MOD type (%x)\n", new_temp ); + break; + } + return; + } + else + { + if ( num_chans == 1 ) + printf( "%d channel...", num_chans ); + else + printf( "%d channels...", num_chans ); + + mod_num_chans[mod_num] = num_chans; + } + + for( i = 0; i <= last_pattern; ++i ) + { + /*curr_note = ¬es_temp[0][0]; */ + for( line = 0; line < 64; ++line ) + { + for( chan = 0; chan < num_chans; ++chan ) + { + UBYTE byte1, byte2, byte3, byte4; + int samp_num, period, effect; + + fread( &byte1, 1, 1, in_file ); + fread( &byte2, 1, 1, in_file ); + fread( &byte3, 1, 1, in_file ); + fread( &byte4, 1, 1, in_file ); + + samp_num = (byte1&0xf0)+(byte3>>4); + period = (((int)(byte1&0xf))<<8) + ((int)byte2); + effect = (((int)(byte3&0xf))<<8) + byte4; + if ( effect != 0 ) + { + if ( (effect>>8) == 0xe ) + ++effect_count[((effect&0xf0)>>4)+16]; + else + { + if ( (effect>>8) == 0xf ) + { + if ( (effect&0xff) > 31 ) + ++effect_count[14]; + else + ++effect_count[15]; + } + else + { + if ( (effect>>8) == 0xc ) + if ( (effect&0xff) > 64 ) + effect = 0xc40; + if ( (effect>>8) == 0xd ) /* pattern break */ + effect = (effect&0xf00) + (((effect>>4)&0xf)*10)+(effect&0xf); + ++effect_count[effect>>8]; + } + } + } + notes_temp[chan][line] = (samp_num<<24) + (MOD_FindNote(period)<<12) + effect; + } + } + + for( chan = 0; chan < num_chans; ++chan ) + { + pat[i][chan] = DATA_AppendAligned( patterns, (UBYTE*)(¬es_temp[chan][0]), 256, 256 ); + } + } + + for( i = 0; i <= 127; ++i ) + { + if ( i >= song_length ) + { + for( chan = 0; chan < num_chans; ++chan ) + patts[mod_num][i][chan] = -1; + } + else + { + for( chan = 0; chan < num_chans; ++chan ) + patts[mod_num][i][chan] = pat[song_data[i]][chan]; + } + } + + if ( song_length > max_song_length ) + max_song_length = song_length; + + if ( song_restart_pos < 127 ) + { + if ( patts[mod_num][song_restart_pos][0] == -1 ) + song_restart_pos = 0; + } + else + { + song_restart_pos = 0; + } + + mod_song_restart_pos[mod_num] = song_restart_pos; + + for( i = 1; i <= 31; ++i ) + { + if ( samps_temp[i].length > 0 ) + { + MOD_LoadSound( &samps[mod_num][i-1], in_file, samples, samps_temp[i].length, samps_temp[i].truelen, samps_temp[i].repeat ); + samps[mod_num][i-1].volume = samps_temp[i].volume; + samps[mod_num][i-1].finetune = samps_temp[i].finetune; + samps[mod_num][i-1].repeat = samps_temp[i].repeat; + } + } + + printf( "Done!\n" ); + + first = TRUE; + for( i = 0; i <= 31; ++i ) + if ( effect_count[i] > 0 ) + if ( *(effect_name[i]) != '*' ) + { + if ( first ) + { + printf( "Unsupported effects:\n" ); + first = FALSE; + } + printf( "%s (%d)\n", effect_name[i], effect_count[i] ); + } + + printf( "song_length:%d last_pattern:%d\n\n", song_length, last_pattern ); + + fclose( in_file ); + } + else + { + printf( "MOD_ConvMod( %s ): Unable to open file for reading...\n", filename ); + } +} + +static void MOD_WriteSamples( FILE* out_file, int num_mods ) +{ + int i, s; + + fprintf( out_file, "\n.ALIGN\n.GLOBAL AAS_ModSamples\nAAS_ModSamples:" ); + + for( i = 0; i < num_mods; ++i ) + { + fprintf( out_file, "\n.word" ); + for( s = 0; s < 31; ++s ) + { + if ( s == 0 ) + fprintf( out_file, " %u, %u, %u", samps[i][s].data, (samps[i][s].length<<16)+samps[i][s].repeat, ((samps[i][s].volume&0xff)<<8)+(samps[i][s].finetune&0xff) ); + else + fprintf( out_file, ", %u, %u, %u", samps[i][s].data, (samps[i][s].length<<16)+samps[i][s].repeat, ((samps[i][s].volume&0xff)<<8)+(samps[i][s].finetune&0xff) ); + } + } + + fprintf( out_file, "\n" ); +} + +static void MOD_WritePatterns( FILE* out_file, int num_mods ) +{ + int mod_num, pattern_num, channel_num; + BOOL first; + + fprintf( out_file, "\n.ALIGN\n.GLOBAL AAS_Sequence\nAAS_Sequence:" ); + + for( mod_num = 0; mod_num < num_mods; ++mod_num ) + { + fprintf( out_file, "\n.short" ); + first = TRUE; + for( pattern_num = 0; pattern_num < 128; ++pattern_num ) + { + for( channel_num = 0; channel_num < 16; ++channel_num ) + { + if ( first ) + { + first = FALSE; + fprintf( out_file, " %d", patts[mod_num][pattern_num][channel_num] ); + } + else + { + fprintf( out_file, ", %d", patts[mod_num][pattern_num][channel_num] ); + } + } + } + } + + fprintf( out_file, "\n" ); +} + +static void MOD_WriteNumChans( FILE* out_file, int num_mods ) +{ + int mod_num; + + fprintf( out_file, "\n.ALIGN\n.GLOBAL AAS_NumChans\nAAS_NumChans:\n.byte" ); + + for( mod_num = 0; mod_num < num_mods; ++mod_num ) + { + if ( mod_num ) + fprintf( out_file, ", %d", mod_num_chans[mod_num] ); + else + fprintf( out_file, " %d", mod_num_chans[mod_num] ); + } + + fprintf( out_file, "\n" ); +} + +static void MOD_WriteSongRestartPos( FILE* out_file, int num_mods ) +{ + int mod_num; + + fprintf( out_file, "\n.ALIGN\n.GLOBAL AAS_RestartPos\nAAS_RestartPos:\n.byte" ); + + for( mod_num = 0; mod_num < num_mods; ++mod_num ) + { + if ( mod_num ) + fprintf( out_file, ", %d", mod_song_restart_pos[mod_num] ); + else + fprintf( out_file, " %d", mod_song_restart_pos[mod_num] ); + } + + fprintf( out_file, "\n" ); +} + +/* + static void MOD_WritePeriodConvTable( FILE* out_file, int mix_rate ) + { + int i, freq; + + fprintf( out_file, "\nconst UWORD AAS_MOD_period_conv_table[2048] = { 65535" ); + + for( i = 1; i < 2048; ++i ) + { + freq = (int)((((double)7093789.2)/((double)(2*i)))); + if ( freq > 65535 ) + freq = 65535; + fprintf( out_file, ", %d", freq ); + } + + fprintf( out_file, " };\n" ); + } + */ + +static int last_samp_length = 0; + +static int RAW_LoadSound( const char* filename, DATAPACK* sfx ) +{ + int data = -1; + struct stat file_info; + + if ( stat( filename, &file_info ) == 0 ) + { + FILE* in_file; + int length; + length = file_info.st_size; + + in_file = fopen( filename, "rb" ); + if ( in_file ) + { + BYTE* samp = (BYTE*)malloc( length+16 ); + + /*printf( "\nRAW_LoadSound( %s ): Reading...\n", filename ); */ + + last_samp_length = length; + + fread( samp+16, 1, length, in_file ); + fclose( in_file ); + + memcpy( samp, samp+length, 16 ); + + MISC_ProcessSound( samp, length+16 ); + + data = DATA_Append( sfx, (UBYTE*)samp, length+16 ) + 16; + + free( samp ); + + printf( "Done!\n\n" ); + } + else + { + printf( "\nRAW_LoadSound( %s ): Unable to open...\n", filename ); + } + } + else + { + printf( "\nRAW_LoadSound( %s ): Unable to open...\n", filename ); + } + + return data; +} + +static int WAV_LoadSound( const char* filename, DATAPACK* sfx ) +{ + int data = -1; + FILE* in_file; + + last_samp_length = 0; + + in_file = fopen( filename, "rb" ); + if ( in_file ) + { + ULONG tmp; + + fread( &tmp, 4, 1, in_file ); + + if ( tmp == 0x46464952 ) /* "RIFF" */ + { + fread( &tmp, 4, 1, in_file ); + fread( &tmp, 4, 1, in_file ); + + if ( tmp == 0x45564157 ) /* "WAVE" */ + { + fread( &tmp, 4, 1, in_file ); + + if ( tmp == 0x20746d66 ) /* "fmt " */ + { + unsigned short format; + + fread( &tmp, 4, 1, in_file ); + fread( &format, 2, 1, in_file ); + + if ( format == 1 ) /* PCM format */ + { + unsigned short channels; + + fread( &channels, 2, 1, in_file ); + + if ( channels == 1 ) /* mono */ + { + unsigned short bits_per_sample; + + fread( &tmp, 4, 1, in_file ); /* sample rate in hz */ + fread( &tmp, 4, 1, in_file ); + fread( &tmp, 2, 1, in_file ); + fread( &bits_per_sample, 2, 1, in_file ); + + if ( bits_per_sample == 8 ) + { + int ret, length; + BOOL done; + + done = FALSE; + length = 0; + + do + { + ret = fread( &tmp, 4, 1, in_file ); + + if ( ret == 1 ) + { + if ( tmp == 0x61746164 ) /* "data" */ + { + ret = fread( &length, 4, 1, in_file ); + + if ( ret == 1 ) + { + if ( length > 0 ) + { + done = TRUE; + } + } + else + { + done = TRUE; + } + } + else + { + int size; + + ret = fread( &size, 4, 1, in_file ); + + if ( ret == 1 ) + { + fseek( in_file, size, SEEK_CUR ); + } + else + { + done = TRUE; + } + } + } + else + { + done = TRUE; + } + } + while ( !done ); + + if ( length > 0 ) + { + BYTE* samp = (BYTE*)malloc( length+16 ); + + last_samp_length = length; + + fread( samp+16, 1, length, in_file ); + + memcpy( samp, samp+length, 16 ); + + MISC_ConvSoundToSigned( (UBYTE*)samp, length+16 ); + + MISC_ProcessSound( samp, length+16 ); + + data = DATA_Append( sfx, (UBYTE*)samp, length+16 ) + 16; + + free( samp ); + + printf( "Done!\n\n" ); + } + else + { + printf( "Failed: Unable to find valid data subchunk...\n" ); + } + } + else + { + printf( "\nWAV_LoadSound( %s ): Failed: Not 8 bit...\n", filename ); + } + } + else + { + printf( "\nWAV_LoadSound( %s ): Failed: Not mono...\n", filename ); + } + } + else + { + printf( "\nWAV_LoadSound( %s ): Failed: Not in PCM format...\n", filename ); + } + } + else + { + printf( "\nWAV_LoadSound( %s ): Failed: Missing fmt subchunk...\n", filename ); + } + } + else + { + printf( "\nWAV_LoadSound( %s ): Failed: Not a WAVE file...\n", filename ); + } + } + else + { + printf( "\nWAV_LoadSound( %s ): Failed: RIFF header missing...\n", filename ); + } + + fclose( in_file ); + } + else + { + printf( "\nWAV_LoadSound( %s ): Failed: Unable to open...\n", filename ); + } + + return data; +} + +__inline static char String_GetChar( const char* txt, int offset ) +{ + return *(txt+offset); +} + +static BOOL String_EndsWithMOD( const char* txt ) +{ + int txt_len = strlen( txt ); + + if ( txt_len > 4 ) + { + if ( (String_GetChar( txt, txt_len-4 ) == '.') && + ((String_GetChar( txt, txt_len-3 ) == 'm') || (String_GetChar( txt, txt_len-3 ) == 'M')) && + ((String_GetChar( txt, txt_len-2 ) == 'o') || (String_GetChar( txt, txt_len-2 ) == 'O')) && + ((String_GetChar( txt, txt_len-1 ) == 'd') || (String_GetChar( txt, txt_len-1 ) == 'D')) ) + { + return TRUE; + } + } + + return FALSE; +} + +static BOOL String_EndsWithRAW( const char* txt ) +{ + int txt_len = strlen( txt ); + + if ( txt_len > 4 ) + { + if ( (String_GetChar( txt, txt_len-4 ) == '.') && + ((String_GetChar( txt, txt_len-3 ) == 'r') || (String_GetChar( txt, txt_len-3 ) == 'R')) && + ((String_GetChar( txt, txt_len-2 ) == 'a') || (String_GetChar( txt, txt_len-2 ) == 'A')) && + ((String_GetChar( txt, txt_len-1 ) == 'w') || (String_GetChar( txt, txt_len-1 ) == 'W')) ) + { + return TRUE; + } + } + + return FALSE; +} + +static BOOL String_EndsWithWAV( const char* txt ) +{ + int txt_len = strlen( txt ); + + if ( txt_len > 4 ) + { + if ( (String_GetChar( txt, txt_len-4 ) == '.') && + ((String_GetChar( txt, txt_len-3 ) == 'w') || (String_GetChar( txt, txt_len-3 ) == 'W')) && + ((String_GetChar( txt, txt_len-2 ) == 'a') || (String_GetChar( txt, txt_len-2 ) == 'A')) && + ((String_GetChar( txt, txt_len-1 ) == 'v') || (String_GetChar( txt, txt_len-1 ) == 'V')) ) + { + return TRUE; + } + } + + return FALSE; +} + +static void String_MakeSafe( char* temp ) +{ + char c; + + while( *temp != (char)0 ) + { + c = *temp; + if ( !(((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) || ((c >= '0') && (c <= '9'))) ) + *temp = '_'; + ++temp; + } +} + +static void ShowHelp() +{ + printf( " Usage: Conv2AAS input_dir\n\n" ); + printf( " input_dir Directory containing all Protracker\n" ); + printf( " MODs & sample data\n\n" ); + printf( " Output goes to files AAS_Data.s & AAS_Data.h\n" ); +} + +int main( int argc, char *argv[] ) +{ + setbuf( stdout, 0 ); + + printf( "\n" ); + printf( "/---------------------------------------------\\\n" ); + printf( "| Conv2AAS v1.11 WAV, RAW & MOD -> AAS |\n" ); + printf( "| Copyright (c) 2005, Apex Designs |\n" ); + printf( "\\---------------------------------------------/\n\n" ); + + if ( argc < 2 ) + { + ShowHelp(); + } + else if ( (argc == 2) && ((strcmp( argv[1], "-help" ) == 0) || (strcmp( argv[1], "-?" ) == 0)) ) + { + ShowHelp(); + } + else + { + FILE* out_file_s; + + out_file_s = fopen( "data/aas_data.s", "w" ); + if ( out_file_s ) + { + FILE* out_file_h; + + out_file_h = fopen( "data/aas_data.h", "w" ); + if ( out_file_h ) + { + DIR* dir_info; + + dir_info = opendir( argv[1] ); + if ( dir_info ) + { + int files_converted = 0; + int mods_found = 0; + DATAPACK* samples; + DATAPACK* patterns; + struct dirent* file_info; + + samples = DATA_Create( "AAS_SampleData" ); + patterns = DATA_Create( "AAS_PatternData" ); + + fprintf( out_file_h, "#ifndef __AAS_DATA__\n#define __AAS_DATA__\n\n#include \"AAS.h\"\n\n#if AAS_VERSION != 0x111\n#error AAS version does not match Conv2AAS version\n#endif\n\nAAS_BEGIN_DECLS\n" ); + + fprintf( out_file_s, ".TEXT\n.SECTION .rodata\n.ALIGN\n.ARM\n\n.ALIGN\n.EXTERN AAS_lib_v111\n.GLOBAL AAS_data_v111\nAAS_data_v111:\n.word AAS_lib_v111\n" ); + + do + { + file_info = readdir( dir_info ); + if ( file_info ) + { + char temp[512]; + + strcpy( temp, argv[1] ); + strcat( temp, "/" ); + strcat( temp, file_info->d_name ); + + if ( String_EndsWithMOD( file_info->d_name ) ) + { + ++files_converted; + printf( "Adding MOD %s...", file_info->d_name ); + MOD_ConvMod( out_file_h, samples, patterns, temp, mods_found ); + /*printf( "Done!\n" ); */ + strcpy( temp, file_info->d_name ); + *(temp + strlen(temp) - 4) = 0; + String_MakeSafe( temp ); + fprintf( out_file_h, "\nextern const AAS_u8 AAS_DATA_MOD_%s;\n", temp ); + fprintf( out_file_s, "\n.ALIGN\n.GLOBAL AAS_DATA_MOD_%s\nAAS_DATA_MOD_%s:\n.byte %d\n", temp, temp, mods_found ); + ++mods_found; + } + else if ( String_EndsWithRAW( file_info->d_name ) ) + { + int val; + ++files_converted; + printf( "Adding RAW %s...", file_info->d_name ); + val = RAW_LoadSound( temp, samples ); + if ( val >= 0 ) + { + strcpy( temp, file_info->d_name ); + *(temp + strlen(temp) - 4) = 0; + String_MakeSafe( temp ); + fprintf( out_file_h, "\nextern const AAS_s8* const AAS_DATA_SFX_START_%s;\n", temp ); + fprintf( out_file_s, "\n.ALIGN\n.GLOBAL AAS_DATA_SFX_START_%s\nAAS_DATA_SFX_START_%s:\n.word AAS_SampleData + %d\n", temp, temp, val ); + fprintf( out_file_h, "\nextern const AAS_s8* const AAS_DATA_SFX_END_%s;\n", temp ); + fprintf( out_file_s, "\n.ALIGN\n.GLOBAL AAS_DATA_SFX_END_%s\nAAS_DATA_SFX_END_%s:\n.word AAS_SampleData + %d\n", temp, temp, val+last_samp_length ); + } + } + else if ( String_EndsWithWAV( file_info->d_name ) ) + { + int val; + ++files_converted; + printf( "Adding WAV %s...", file_info->d_name ); + val = WAV_LoadSound( temp, samples ); + if ( val >= 0 ) + { + strcpy( temp, file_info->d_name ); + *(temp + strlen(temp) - 4) = 0; + String_MakeSafe( temp ); + fprintf( out_file_h, "\nextern const AAS_s8* const AAS_DATA_SFX_START_%s;\n", temp ); + fprintf( out_file_s, "\n.ALIGN\n.GLOBAL AAS_DATA_SFX_START_%s\nAAS_DATA_SFX_START_%s:\n.word AAS_SampleData + %d\n", temp, temp, val ); + fprintf( out_file_h, "\nextern const AAS_s8* const AAS_DATA_SFX_END_%s;\n", temp ); + fprintf( out_file_s, "\n.ALIGN\n.GLOBAL AAS_DATA_SFX_END_%s\nAAS_DATA_SFX_END_%s:\n.word AAS_SampleData + %d\n", temp, temp, val+last_samp_length ); + } + } + } + } while ( file_info ); + + closedir( dir_info ); + + fprintf( out_file_s, "\n.ALIGN\n.GLOBAL AAS_DATA_NUM_MODS\nAAS_DATA_NUM_MODS:\n.short %d\n", mods_found ); + + MOD_WriteSamples( out_file_s, mods_found ); + MOD_WritePatterns( out_file_s, mods_found ); + MOD_WriteNumChans( out_file_s, mods_found ); + MOD_WriteSongRestartPos( out_file_s, mods_found ); + + /*MOD_WritePeriodConvTable( out_file_h, 24002 ); */ + + DATA_Write( samples, out_file_s ); + DATA_Write( patterns, out_file_s ); + + printf( "\n" ); + + fprintf( out_file_h, "\nAAS_END_DECLS\n\n#endif\n" ); + } + else + { + printf( "Unable to open directory %s...\n", argv[1] ); + } + + fclose( out_file_h ); + } + else + { + printf( "Unable to open AASData.h for writing...\n" ); + } + + fclose( out_file_s ); + } + else + { + printf( "Unable to open AASData.s for writing...\n" ); + } + } + + return 0; +} -- 1.7.10.4