diff options
author | ShinyDragonHunter <32826900+ShinyDragonHunter@users.noreply.github.com> | 2018-11-05 21:37:14 +0000 |
---|---|---|
committer | ShinyDragonHunter <32826900+ShinyDragonHunter@users.noreply.github.com> | 2018-11-05 21:37:14 +0000 |
commit | aaea9de41982e3148d67aef1eb7399b22a578c32 (patch) | |
tree | 0e7e6c146d7b1f48ee040b6550b8c1860f98ed4a | |
parent | 0d7ea97dad2ebb04dafb066ba08b5d09061a727d (diff) |
Updated Reduce noise and improve sound quality of music (markdown)
-rw-r--r-- | Reduce-noise-and-improve-sound-quality-of-music.md | 2609 |
1 files changed, 2608 insertions, 1 deletions
diff --git a/Reduce-noise-and-improve-sound-quality-of-music.md b/Reduce-noise-and-improve-sound-quality-of-music.md index cc302e6..bb6bde7 100644 --- a/Reduce-noise-and-improve-sound-quality-of-music.md +++ b/Reduce-noise-and-improve-sound-quality-of-music.md @@ -1 +1,2608 @@ -Placeholder for now.
\ No newline at end of file +Many thanks to ipatix and Kurausukun. Without them, none of this would be possible. + +Anyway, these changes will allow the game to have reduced noise and output sound at a higher base frequency. + +In asm/m4a_1.s, everything in that file with: + +```asm + .include "asm/macros.inc" + .include "constants/gba_constants.inc" + .include "constants/m4a_constants.inc" + + .syntax unified + + .text + + thumb_func_start umul3232H32 +umul3232H32: + adr r2, __umul3232H32 + bx r2 + .arm +__umul3232H32: + umull r2, r3, r0, r1 + add r0, r3, 0 + bx lr + thumb_func_end umul3232H32 + + thumb_func_start SoundMain +SoundMain: + ldr r0, lt_SOUND_INFO_PTR + ldr r0, [r0] + ldr r2, lt_ID_NUMBER + ldr r3, [r0, o_SoundInfo_ident] + cmp r2, r3 + beq SoundMain_1 + bx lr @ Exit the function if ident doesn't match ID_NUMBER. +SoundMain_1: + adds r3, 1 + str r3, [r0, o_SoundInfo_ident] + push {r4-r7,lr} + mov r1, r8 + mov r2, r9 + mov r3, r10 + mov r4, r11 + push {r0-r4} + sub sp, 0x18 + ldrb r1, [r0, o_SoundInfo_maxLines] + cmp r1, 0 @ if maxLines is 0, there is no maximum + beq SoundMain_3 + ldr r2, lt_REG_VCOUNT + ldrb r2, [r2] + cmp r2, VCOUNT_VBLANK + bhs SoundMain_2 + adds r2, TOTAL_SCANLINES +SoundMain_2: + adds r1, r2 +SoundMain_3: + str r1, [sp, 0x14] + ldr r3, [r0, o_SoundInfo_func] + cmp r3, 0 + beq SoundMain_4 + ldr r0, [r0, o_SoundInfo_intp] + bl call_r3 + ldr r0, [sp, 0x18] +SoundMain_4: + ldr r3, [r0, o_SoundInfo_CgbSound] + bl call_r3 + ldr r0, [sp, 0x18] + ldr r3, [r0, o_SoundInfo_pcmSamplesPerVBlank] + mov r8, r3 + ldr r5, lt_o_SoundInfo_pcmBuffer + adds r5, r0 + ldrb r4, [r0, o_SoundInfo_pcmDmaCounter] + subs r7, r4, 1 + bls SoundMain_5 + ldrb r1, [r0, o_SoundInfo_pcmDmaPeriod] + subs r1, r7 + mov r2, r8 + muls r2, r1 + adds r5, r2 +SoundMain_5: + str r5, [sp, 0x8] + ldr r6, lt_PCM_DMA_BUF_SIZE + ldr r3, lt_SoundMainRAM_Buffer + bx r3 + + .align 2, 0 +lt_SOUND_INFO_PTR: .word SOUND_INFO_PTR +lt_ID_NUMBER: .word ID_NUMBER +lt_SoundMainRAM_Buffer: .word SoundMainRAM_Buffer + 1 +lt_REG_VCOUNT: .word REG_VCOUNT +lt_o_SoundInfo_pcmBuffer: .word o_SoundInfo_pcmBuffer +lt_PCM_DMA_BUF_SIZE: .word PCM_DMA_BUF_SIZE + thumb_func_end SoundMain + + .equ POKE_INIT, 1 + .equ DMA_FIX, 0 + .equ ENABLE_DECOMPRESSION, 1 + + /* stack variables */ + .equ ARG_FRAME_LENGTH, 0x0 @ TODO actually use this variable + .equ ARG_REMAIN_CHN, 0x4 @ This is the channel count variable + .equ ARG_BUFFER_POS, 0x8 @ stores the current output buffer pointer + .equ ARG_LOOP_START_POS, 0xC @ stores wave loop start position in channel loop + .equ ARG_LOOP_LENGTH, 0x10 @ '' '' '' end position + .equ ARG_BUFFER_POS_INDEX_HINT, 0x14 + .equ ARG_PCM_STRUCT, 0x18 @ pointer to engine the main work area + + /* channel struct */ + .equ CHN_STATUS, 0x0 @ [byte] channel status bitfield + .equ CHN_MODE, 0x1 @ [byte] channel mode bitfield + .equ CHN_VOL_1, 0x2 @ [byte] volume right + .equ CHN_VOL_2, 0x3 @ [byte] volume left + .equ CHN_ATTACK, 0x4 @ [byte] wave attack summand + .equ CHN_DECAY, 0x5 @ [byte] wave decay factor + .equ CHN_SUSTAIN, 0x6 @ [byte] wave sustain level + .equ CHN_RELEASE, 0x7 @ [byte] wave release factor + .equ CHN_ADSR_LEVEL, 0x9 @ [byte] current envelope level + .equ CHN_FINAL_VOL_1, 0xA @ [byte] not used anymore! + .equ CHN_FINAL_VOL_2, 0xB @ [byte] not used anymore! + .equ CHN_ECHO_VOL, 0xC @ [byte] pseudo echo volume + .equ CHN_ECHO_REMAIN, 0xD @ [byte] pseudo echo length + .equ CHN_SAMPLE_COUNTDOWN, 0x18 @ [word] sample countdown in mixing loop + .equ CHN_FINE_POSITION, 0x1C @ [word] inter sample position (23 bits) + .equ CHN_FREQUENCY, 0x20 @ [word] sample rate (in Hz) + .equ CHN_WAVE_OFFSET, 0x24 @ [word] wave header pointer + .equ CHN_POSITION_ABS, 0x28 @ [word] points to the current position in the wave data (relative offset for compressed samples) + .equ CHN_BLOCK_COUNT, 0x3C @ [word] only used for compressed samples: contains the value of the block that is currently decoded + + /* wave header struct */ + .equ WAVE_LOOP_FLAG, 0x3 @ [byte] 0x0 = oneshot; 0x40 = looped + .equ WAVE_FREQ, 0x4 @ [word] pitch adjustment value = mid-C samplerate * 1024 + .equ WAVE_LOOP_START, 0x8 @ [word] loop start position + .equ WAVE_LENGTH, 0xC @ [word] loop end / wave end position + .equ WAVE_DATA, 0x10 @ [byte array] actual wave data + + /* pulse wave synth configuration offset */ + .equ SYNTH_BASE_WAVE_DUTY, 0x1 @ [byte] + .equ SYNTH_WIDTH_CHANGE_1, 0x2 @ [byte] + .equ SYNTH_MOD_AMOUNT, 0x3 @ [byte] + .equ SYNTH_WIDTH_CHANGE_2, 0x4 @ [byte] + + /* CHN_STATUS flags - 0x0 = OFF */ + .equ FLAG_CHN_INIT, 0x80 @ [bit] write this value to init a channel + .equ FLAG_CHN_RELEASE, 0x40 @ [bit] write this value to release (fade out) the channel + .equ FLAG_CHN_COMP, 0x20 @ [bit] is wave being played compressed (yes/no) + .equ FLAG_CHN_LOOP, 0x10 @ [bit] loop (yes/no) + .equ FLAG_CHN_ECHO, 0x4 @ [bit] echo phase + .equ FLAG_CHN_ATTACK, 0x3 @ [bit] attack phase + .equ FLAG_CHN_DECAY, 0x2 @ [bit] decay phase + .equ FLAG_CHN_SUSTAIN, 0x1 @ [bit] sustain phase + + /* CHN_MODE flags */ + .equ MODE_FIXED_FREQ, 0x8 @ [bit] set to disable resampling (i.e. playback with output rate) + .equ MODE_REVERSE, 0x10 @ [bit] set to reverse sample playback + .equ MODE_COMP, 0x30 @ [bit] is wave being played compressed or reversed (TODO: rename flag) + .equ MODE_SYNTH, 0x40 @ [bit] READ ONLY, indicates synthzied output + + /* variables of the engine work area */ + .equ VAR_REVERB, 0x5 @ [byte] 0-127 = reverb level + .equ VAR_MAX_CHN, 0x6 @ [byte] maximum channels to process + .equ VAR_MASTER_VOL, 0x7 @ [byte] PCM master volume + .equ VAR_DEF_PITCH_FAC, 0x18 @ [word] this value gets multiplied with the sample rate for the inter sample distance + .equ VAR_FIRST_CHN, 0x50 @ [CHN struct] relative offset to channel array + + /* just some more defines */ + .equ REG_DMA3_SRC, 0x040000D4 + .equ ARM_OP_LEN, 0x4 + .syntax divided + + thumb_func_start SoundMainRAM +SoundMainRAM: +main_mixer: + /* load Reverb level and check if we need to apply it */ + STR R4, [SP, #ARG_BUFFER_POS_INDEX_HINT] + ADR R2, is_buffer_init + LDRB R0, [R2] + CMP R0, #0 + BNE C_setup_channel_state_loop + /* if buffer not initialized, clear first */ + LDR R3, hq_buffer + MOV R1, R8 + MOV R4, #0 + MOV R5, #0 + MOV R6, #0 + MOV R7, #0 + + LSR R1, #3 + BCC C_clear_buffer_align_8 + STMIA R3!, {R4, R5, R6, R7} + +C_clear_buffer_align_8: + LSR R1, #1 + BCC C_clear_buffer_align_16 + + STMIA R3!, {R4, R5, R6, R7} + STMIA R3!, {R4, R5, R6, R7} + +C_clear_buffer_align_16: + STMIA R3!, {R4, R5, R6, R7} + STMIA R3!, {R4, R5, R6, R7} + STMIA R3!, {R4, R5, R6, R7} + STMIA R3!, {R4, R5, R6, R7} + + SUB R1, #1 + BGT C_clear_buffer_align_16 + MOV R1, #1 + STRB R1, [R2] + B C_setup_channel_state_loop + + .align 2 +is_buffer_init: + .byte 0x0 + + .align 1 + +C_setup_channel_state_loop: + /* + * okay, before the actual mixing starts + * the volume and envelope calculation takes place + */ + MOV R4, R8 @ R4 = buffer length + /* + * this stroes the buffer length to a backup location + */ + STR R4, [SP, #ARG_FRAME_LENGTH] + /* init channel loop */ + LDR R4, [SP, #ARG_PCM_STRUCT] @ R4 = main work area pointer + LDR R0, [R4, #VAR_DEF_PITCH_FAC] @ R0 = samplingrate pitch factor + MOV R12, R0 + LDRB R0, [R4, #VAR_MAX_CHN] + ADD R4, #VAR_FIRST_CHN @ R4 = Base channel Offset (Channel #0) + +C_channel_state_loop: + /* this is the main channel processing loop */ + STR R0, [SP, #ARG_REMAIN_CHN] + LDR R3, [R4, #CHN_WAVE_OFFSET] + LDRB R6, [R4, #CHN_STATUS] @ R6 will hold the channel status + MOVS R0, #0xC7 @ check if any of the channel status flags is set + TST R0, R6 @ check if none of the flags is set + BEQ C_skip_channel + /* check channel flags */ + LSL R0, R6, #25 @ shift over the FLAG_CHN_INIT to CARRY + BCC C_adsr_echo_check @ continue with normal channel procedure + /* check leftmost bit */ + BMI C_stop_channel @ FLAG_CHN_INIT | FLAG_CHN_RELEASE -> stop directly + /* channel init procedure */ + MOVS R6, #FLAG_CHN_ATTACK + MOVS R0, R3 @ R0 = CHN_WAVE_OFFSET + ADD R0, #WAVE_DATA @ R0 = wave data offset + + /* Pokemon games seem to init channels differently than other m4a games */ + .if POKE_INIT==0 + STR R0, [R4, #CHN_POSITION_ABS] + LDR R0, [R3, #WAVE_LENGTH] + STR R0, [R4, #CHN_SAMPLE_COUNTDOWN] + .else + LDR R1, [R4, #CHN_SAMPLE_COUNTDOWN] + ADD R0, R0, R1 + STR R0, [R4, #CHN_POSITION_ABS] + LDR R0, [R3, #WAVE_LENGTH] + SUB R0, R0, R1 + STR R0, [R4, #CHN_SAMPLE_COUNTDOWN] + .endif + + MOVS R5, #0 @ initial envelope = #0 + STRB R5, [R4, #CHN_ADSR_LEVEL] + STR R5, [R4, #CHN_FINE_POSITION] + LDRB R2, [R3, #WAVE_LOOP_FLAG] + LSR R0, R2, #6 + BEQ C_adsr_attack + /* loop enabled here */ + MOVS R0, #FLAG_CHN_LOOP + ORR R6, R0 + B C_adsr_attack + +C_adsr_echo_check: + /* this is the normal ADSR procedure without init */ + LDRB R5, [R4, #CHN_ADSR_LEVEL] + LSL R0, R6, #29 @ FLAG_CHN_ECHO --> bit 31 (sign bit) + BPL C_adsr_release_check + /* pseudo echo handler */ + LDRB R0, [R4, #CHN_ECHO_REMAIN] + SUB R0, #1 + STRB R0, [R4, #CHN_ECHO_REMAIN] + BHI C_channel_vol_calc @ continue normal if channel is still on + +C_stop_channel: + MOVS R0, #0 + STRB R0, [R4, #CHN_STATUS] + +C_skip_channel: + /* go to end of the channel loop */ + B C_end_channel_state_loop + +C_adsr_release_check: + LSL R0, R6, #25 @ FLAG_CHN_RELEASE --> bit 31 (sign bit) + BPL C_adsr_decay_check + /* release handler */ + LDRB R0, [R4, #CHN_RELEASE] + @SUB R0, #0xFF @ linear decay; TODO make option for triggering it + @SUB R0, #1 + @ADD R5, R5, R0 + MUL R5, R5, R0 + LSR R5, #8 + BLE C_adsr_released + /* pseudo echo init handler */ + LDRB R0, [R4, #CHN_ECHO_VOL] + CMP R5, R0 + BHI C_channel_vol_calc + +C_adsr_released: + /* if volume released to #0 */ + LDRB R5, [R4, #CHN_ECHO_VOL] + CMP R5, #0 + BEQ C_stop_channel + /* pseudo echo volume handler */ + MOVS R0, #FLAG_CHN_ECHO + ORR R6, R0 @ set the echo flag + B C_adsr_save_and_finalize + +C_adsr_decay_check: + /* check if decay is active */ + MOVS R2, #(FLAG_CHN_DECAY+FLAG_CHN_SUSTAIN) + AND R2, R6 + CMP R2, #FLAG_CHN_DECAY + BNE C_adsr_attack_check @ decay not active yet + /* decay handler */ + LDRB R0, [R4, #CHN_DECAY] + MUL R5, R5, R0 + LSR R5, R5, #8 + LDRB R0, [R4, #CHN_SUSTAIN] + CMP R5, R0 + BHI C_channel_vol_calc @ sample didn't decay yet + /* sustain handler */ + MOVS R5, R0 @ current level = sustain level + BEQ C_adsr_released @ sustain level #0 --> branch + /* step to next phase otherweise */ + B C_adsr_next_state + +C_adsr_attack_check: + /* attack handler */ + CMP R2, #FLAG_CHN_ATTACK + BNE C_channel_vol_calc @ if it isn't in attack attack phase, it has to be in sustain (keep vol) --> branch + +C_adsr_attack: + /* apply attack summand */ + LDRB R0, [R4, #CHN_ATTACK] + ADD R5, R0 + CMP R5, #0xFF + BLO C_adsr_save_and_finalize + /* cap attack at 0xFF */ + MOVS R5, #0xFF + +C_adsr_next_state: + /* switch to next adsr phase */ + SUB R6, #1 + +C_adsr_save_and_finalize: + /* store channel status */ + STRB R6, [R4, #CHN_STATUS] + +C_channel_vol_calc: + /* store the calculated ADSR level */ + STRB R5, [R4, #CHN_ADSR_LEVEL] + /* apply master volume */ + LDR R0, [SP, #ARG_PCM_STRUCT] + LDRB R0, [R0, #VAR_MASTER_VOL] + ADD R0, #1 + MUL R5, R0 + /* left side volume */ + LDRB R0, [R4, #CHN_VOL_2] + MUL R0, R5 + LSR R0, #13 + MOV R10, R0 @ R10 = left volume + /* right side volume */ + LDRB R0, [R4, #CHN_VOL_1] + MUL R0, R5 + LSR R0, #13 + MOV R11, R0 @ R11 = right volume + /* + * Now we get closer to actual mixing: + * For looped samples some additional operations are required + */ + MOVS R0, #FLAG_CHN_LOOP + AND R0, R6 + BEQ C_skip_sample_loop_setup + /* loop setup handler */ + ADD R3, #WAVE_LOOP_START + LDMIA R3!, {R0, R1} @ R0 = loop start, R1 = loop end + ADD R3, R0 @ R3 = loop start position (absolute) + STR R3, [SP, #ARG_LOOP_START_POS] + SUB R0, R1, R0 + +C_skip_sample_loop_setup: + /* do the rest of the setup */ + STR R0, [SP, #ARG_LOOP_LENGTH] @ if loop is off --> R0 = 0x0 + LDR R5, hq_buffer + LDR R2, [R4, #CHN_SAMPLE_COUNTDOWN] + LDR R3, [R4, #CHN_POSITION_ABS] + LDRB R0, [R4, #CHN_MODE] + ADR R1, C_mixing_setup + BX R1 + + .align 2 +hq_buffer: + .word hq_buffer_ptr + + .arm + .align 2 + +C_mixing_setup: + /* frequency and mixing loading routine */ + LDR R8, [SP, #ARG_FRAME_LENGTH] + ORRS R11, R11, R10, LSL#16 @ R11 = 00LL00RR + BEQ C_mixing_epilogue @ volume #0 --> branch and skip channel processing + /* normal processing otherwise */ + TST R0, #MODE_FIXED_FREQ + BNE C_setup_fixed_freq_mixing + TST R0, #MODE_COMP + BNE C_setup_special_mixing @ compressed? --> branch + + STMFD SP!, {R4, R9, R12} + /* + * This mixer supports 4 different kind of synthesized sounds + * They are triggered if there is no samples to play + * This get's checked below + */ + MOVS R2, R2 + ORREQ R0, R0, #MODE_SYNTH + STREQB R0, [R4, #CHN_MODE] + ADD R4, R4, #CHN_FINE_POSITION + LDMIA R4, {R7, LR} @ R7 = Fine Position, LR = Frequency + MUL R4, LR, R12 @ R4 = inter sample steps = output rate factor * samplerate + /* now the first samples get loaded */ + LDRSB R6, [R3], #1 + LDRSB R12, [R3] + TST R0, #MODE_SYNTH + BNE C_setup_synth + /* incase no synth mode should be used, code contiues here */ + SUB R12, R12, R6 @ R12 = DELTA + /* + * Mixing goes with volume ranges 0-127 + * They come in 0-255 --> divide by 2 + */ + MOVS R11, R11, LSR#1 + ADC R11, R11, #0x8000 + BIC R11, R11, #0xFF00 + MOV R1, R7 @ R1 = inter sample position + /* + * There is 2 different mixing codepaths for uncompressed data + * path 1: fast mixing, but doesn't supports loop or stop + * path 2: not so fast but supports sample loops / stop + * This checks if there is enough samples aviable for path 1. + * important: R0 is expected to be #0 + */ + UMLAL R1, R0, R4, R8 + MOV R1, R1, LSR#23 + ORR R0, R1, R0, LSL#9 + CMP R2, R0 @ actual comparison + BLE C_setup_unbuffered_mixing @ if not enough samples are available for path 1 --> branch + /* + * This is the mixer path 1. + * The interesting thing here is that the code will + * buffer enough samples on stack if enough space + * on stack is available (or goes over the limit of 0x400 bytes) + */ + SUB R2, R2, R0 + LDR R10, upper_stack_bounds + ADD R10, R10, R0 + CMP R10, SP + ADD R10, R3, R0 + /* + * R2 = remaining samples after processing + * R10 = final sample position + * SP = original stack location + * These values will get reloaded after channel processing + * due to the lack of registers. + */ + STMFD SP!, {R2, R10} + CMPCC R0, #0x400 @ > 0x400 bytes --> read directly from ROM rather than buffered + MOV R10, SP + BCS C_select_highspeed_codepath + /* + * The code below inits the DMA to read word aligned + * samples from ROM to stack + */ + BIC R1, R3, #3 + MOV R9, #0x04000000 + ADD R9, R9, #0xD4 + ADD R0, R0, #7 + MOV R0, R0, LSR#2 + SUB SP, SP, R0, LSL#2 + AND R3, R3, #3 + ADD R3, R3, SP + ORR LR, R0, #0x84000000 + STMIA R9, {R1, SP, LR} @ actually starts the DMA + + /* Somehow is neccesary for some games not to break */ + .if DMA_FIX==1 + MOV R0, #0 + MOV R1, #0 + MOV R2, #0 + STMIA R9, {R0, R1, R2} + .endif + +C_select_highspeed_codepath: + STMFD SP!, {R10} @ save original SP for VLA + /* + * This code decides which piece of code to load + * depending on playback-rate / default-rate ratio. + * Modes > 1.0 run with different volume levels. + * R4 = inter sample step + */ + ADR R0, high_speed_code_resource @ loads the base pointer of the code + SUBS R4, R4, #0x800000 + MOVPL R11, R11, LSL#1 @ if >= 1.0* 0-127 --> 0-254 volume level + ADDPL R0, R0, #(ARM_OP_LEN*6) @ 6 instructions further + SUBPLS R4, R4, #0x800000 @ if >= 2.0* + ADDPL R0, R0, #(ARM_OP_LEN*6) + ADDPL R4, R4, #0x800000 + LDR R2, previous_fast_code + CMP R0, R2 @ code doesn't need to be reloaded if it's already in place + BEQ C_skip_fast_mixing_creation + /* This loads the needed code to RAM */ + STR R0, previous_fast_code + LDMIA R0, {R0-R2, R8-R10} @ load 6 opcodes + ADR LR, fast_mixing_instructions + +C_fast_mixing_creation_loop: + /* paste code to destination, see below for patterns */ + STMIA LR, {R0, R1} + ADD LR, LR, #(ARM_OP_LEN*38) + STMIA LR, {R0, R1} + SUB LR, LR, #(ARM_OP_LEN*35) + STMIA LR, {R2, R8-R10} + ADD LR, LR, #(ARM_OP_LEN*38) + STMIA LR, {R2, R8-R10} + SUB LR, LR, #(ARM_OP_LEN*32) + ADDS R5, R5, #0x40000000 @ do that for 4 blocks + BCC C_fast_mixing_creation_loop + + LDR R8, [SP] @ restore R8 with the frame length + LDR R8, [R8, #(ARG_FRAME_LENGTH + 0x8 + 0xC)] + +C_skip_fast_mixing_creation: + MOV R2, #0xFF000000 @ load the fine position overflow bitmask +C_fast_mixing_loop: + /* This is the actual processing and interpolation code loop; NOPs will be replaced by the code above */ + LDMIA R5, {R0, R1, R10, LR} @ load 4 stereo samples to Registers + MUL R9, R7, R12 +fast_mixing_instructions: + NOP @ Block #1 + NOP + MLANE R0, R11, R9, R0 + NOP + NOP + NOP + NOP + BIC R7, R7, R2, ASR#1 + MULNE R9, R7, R12 + NOP @ Block #2 + NOP + MLANE R1, R11, R9, R1 + NOP + NOP + NOP + NOP + BIC R7, R7, R2, ASR#1 + MULNE R9, R7, R12 + NOP @ Block #3 + NOP + MLANE R10, R11, R9, R10 + NOP + NOP + NOP + NOP + BIC R7, R7, R2, ASR#1 + MULNE R9, R7, R12 + NOP @ Block #4 + NOP + MLANE LR, R11, R9, LR + NOP + NOP + NOP + NOP + BIC R7, R7, R2, ASR#1 + STMIA R5!, {R0, R1, R10, LR} @ write 4 stereo samples + + LDMIA R5, {R0, R1, R10, LR} @ load the next 4 stereo samples + MULNE R9, R7, R12 + NOP @ Block #1 + NOP + MLANE R0, R11, R9, R0 + NOP + NOP + NOP + NOP + BIC R7, R7, R2, ASR#1 + MULNE R9, R7, R12 + NOP @ Block #2 + NOP + MLANE R1, R11, R9, R1 + NOP + NOP + NOP + NOP + BIC R7, R7, R2, ASR#1 + MULNE R9, R7, R12 + NOP @ Block #3 + NOP + MLANE R10, R11, R9, R10 + NOP + NOP + NOP + NOP + BIC R7, R7, R2, ASR#1 + MULNE R9, R7, R12 + NOP @ Block #4 + NOP + MLANE LR, R11, R9, LR + NOP + NOP + NOP + NOP + BIC R7, R7, R2, ASR#1 + STMIA R5!, {R0, R1, R10, LR} @ write 4 stereo samples + SUBS R8, R8, #8 + BGT C_fast_mixing_loop + /* restore previously saved values */ + LDMFD SP, {SP} @ reload original stack pointer from VLA + LDMFD SP!, {R2, R3} + B C_end_mixing + +/* Various variables for the cached mixer */ + + .align 2 +upper_stack_bounds: + .word 0x03007910 +previous_fast_code: + .word 0x0 /* mark as invalid initially */ + +/* Those instructions below are used by the high speed loop self modifying code */ +high_speed_code_resource: + /* Block for Mix Freq < 1.0 * Output Frequency */ + MOV R9, R9, ASR#22 + ADDS R9, R9, R6, LSL#1 + ADDS R7, R7, R4 + ADDPL R6, R12, R6 + LDRPLSB R12, [R3, #1]! + SUBPLS R12, R12, R6 + + /* Block for Mix Freq > 1.0 AND < 2.0 * Output Frequency */ + ADDS R9, R6, R9, ASR#23 + ADD R6, R12, R6 + ADDS R7, R7, R4 + LDRPLSB R6, [R3, #1]! + LDRSB R12, [R3, #1]! + SUBS R12, R12, R6 + + /* Block for Mix Freq > 2.0 * Output Frequency */ + ADDS R9, R6, R9, ASR#23 + ADD R7, R7, R4 + ADD R3, R3, R7, LSR#23 + LDRSB R6, [R3] + LDRSB R12, [R3, #1]! + SUBS R12, R12, R6 + +/* incase a loop or end occurs during mixing, this code is used */ +C_setup_unbuffered_mixing: + ADD R5, R5, R8, LSL#2 @ R5 = End of HQ buffer + +/* This below is the unbuffered mixing loop. R6 = base sample, R12 diff to next */ +C_unbuffered_mixing_loop: + + MUL R9, R7, R12 + MOV R9, R9, ASR#22 + ADDS R9, R9, R6, LSL#1 + LDRNE R0, [R5, -R8, LSL#2] + MLANE R0, R11, R9, R0 + STRNE R0, [R5, -R8, LSL#2] + ADD R7, R7, R4 + MOVS R9, R7, LSR#23 + BEQ C_unbuffered_mixing_skip_load @ skip the mixing load if it isn't required + + SUBS R2, R2, R7, LSR#23 + BLLE C_mixing_loop_or_end + SUBS R9, R9, #1 + ADDEQ R6, R12, R6 + @RETURN LOCATION FROM LOOP HANDLER + LDRNESB R6, [R3, R9]! + LDRSB R12, [R3, #1]! + SUB R12, R12, R6 + BIC R7, R7, #0x3F800000 + +C_unbuffered_mixing_skip_load: + SUBS R8, R8, #1 @ reduce the sample count for the buffer by #1 + BGT C_unbuffered_mixing_loop + +C_end_mixing: + SUB R3, R3, #1 @ because the mixer always needs 1 byte lookahead, this reverts it + LDMFD SP!, {R4, R9, R12} + STR R7, [R4, #CHN_FINE_POSITION] + B C_mixing_end_store + +C_mixing_loop_or_end: + /* This loads the loop information end loops incase it should */ + ADD R3, SP, #ARG_LOOP_START_POS+0xC + LDMIA R3, {R3, R6} @ R3 = Loop Start; R6 = Loop Length + CMP R6, #0 @ check if loop is enabled; if Loop is enabled R6 is != 0 + RSBNE R9, R2, #0 @ loop wraparound logic + ADDNE R2, R6, R2 + ADDNE PC, LR, #(ARM_OP_LEN*2) + LDMFD SP!, {R4, R9, R12} + B C_mixing_end_and_stop_channel @ R6 == 0 (always) + +C_fixed_mixing_loop_or_end: + LDR R2, [SP, #ARG_LOOP_LENGTH+0x8] + MOVS R6, R2 @ copy it to R6 and check whether loop is disabled + LDRNE R3, [SP, #ARG_LOOP_START_POS+0x8] + BXNE LR @ if it loops return to mixing function, if it doesn't go on end mixing + + LDMFD SP!, {R4, R9} + +C_mixing_end_and_stop_channel: + STRB R6, [R4] @ update channel flag with chn halt + B C_mixing_epilogue + +/* These are used for the fixed freq mixer */ +fixed_mixing_code_resource: + MOVS R6, R10, LSL#24 + MOVS R6, R6, ASR#24 + MOVS R6, R10, LSL#16 + MOVS R6, R6, ASR#24 + MOVS R6, R10, LSL#8 + MOVS R6, R6, ASR#24 + MOVS R6, R10, ASR#24 + LDMIA R3!, {R10} @ load chunk of samples + MOVS R6, R10, LSL#24 + MOVS R6, R6, ASR#24 + MOVS R6, R10, LSL#16 + MOVS R6, R6, ASR#24 + MOVS R6, R10, LSL#8 + MOVS R6, R6, ASR#24 + LDMFD SP!, {R4, R9, R12} + +C_setup_fixed_freq_mixing: + STMFD SP!, {R4, R9} + +C_fixed_mixing_length_check: + MOV LR, R2 @ sample countdown + CMP R2, R8 + MOVGT LR, R8 @ min(buffer_size, sample_countdown) + SUB LR, LR, #1 + MOVS LR, LR, LSR#2 + BEQ C_fixed_mixing_process_rest @ <= 3 samples to process + + SUB R8, R8, LR, LSL#2 @ subtract the amount of samples we need to process from the buffer length + SUB R2, R2, LR, LSL#2 @ subtract the amount of samples we need to process from the remaining samples + ADR R1, fixed_mixing_instructions + ADR R0, fixed_mixing_code_resource + MOV R9, R3, LSL#30 + ADD R0, R0, R9, LSR#27 @ alignment * 8 + resource offset = new resource offset + LDMIA R0!, {R6, R7, R9, R10} @ load and write instructions + STMIA R1, {R6, R7} + ADD R1, R1, #0xC + STMIA R1, {R9, R10} + ADD R1, R1, #0xC + LDMIA R0, {R6, R7, R9, R10} + STMIA R1, {R6, R7} + ADD R1, R1, #0xC + STMIA R1, {R9, R10} + LDMIA R3!, {R10} @ load 4 samples from ROM + +C_fixed_mixing_loop: + LDMIA R5, {R0, R1, R7, R9} @ load 4 samples from hq buffer + +fixed_mixing_instructions: + NOP + NOP + MLANE R0, R11, R6, R0 @ add new sample if neccessary + NOP + NOP + MLANE R1, R11, R6, R1 + NOP + NOP + MLANE R7, R11, R6, R7 + NOP + NOP + MLANE R9, R11, R6, R9 + STMIA R5!, {R0, R1, R7, R9} @ write samples to the mixing buffer + SUBS LR, LR, #1 + BNE C_fixed_mixing_loop + + SUB R3, R3, #4 @ we'll need to load this block again, so rewind a bit + +C_fixed_mixing_process_rest: + MOV R1, #4 @ repeat the loop #4 times to completley get rid of alignment errors + +C_fixed_mixing_unaligned_loop: + LDR R0, [R5] + LDRSB R6, [R3], #1 + MLA R0, R11, R6, R0 + STR R0, [R5], #4 + SUBS R2, R2, #1 + BLEQ C_fixed_mixing_loop_or_end + SUBS R1, R1, #1 + BGT C_fixed_mixing_unaligned_loop + + SUBS R8, R8, #4 + BGT C_fixed_mixing_length_check @ repeat the mixing procedure until the buffer is filled + + LDMFD SP!, {R4, R9} + +C_mixing_end_store: + STR R2, [R4, #CHN_SAMPLE_COUNTDOWN] + STR R3, [R4, #CHN_POSITION_ABS] + +C_mixing_epilogue: + ADR R0, (C_end_channel_state_loop+1) + BX R0 + + .thumb + +C_end_channel_state_loop: + LDR R0, [SP, #ARG_REMAIN_CHN] + SUB R0, #1 + BLE C_main_mixer_return + + ADD R4, #0x40 + B C_channel_state_loop + +C_main_mixer_return: + ADR R5, V_noise_shape + LDRB R4, [R5, #0] @ left noise shape + LSL R4, R4, #16 + LDRB R5, [R5, #1] @ right noise shape + LSL R5, R5, #16 + ADR R0, C_downsampler + BX R0 + + +V_noise_shape: + .byte 0, 0 + + .arm + .align 2 + +C_downsampler: + LDR R8, [SP, #ARG_FRAME_LENGTH] + LDR R9, [SP, #ARG_BUFFER_POS] + LDR R10, hq_buffer + MOV R11, #0xFF000000 + MOV LR, #0xC0000000 + +C_downsampler_loop: + LDMIA R10, {R0, R1, R2, R3} + ADD R12, R4, R0 @ left sample #1 + ADDS R4, R12, R12 + EORVS R12, LR, R4, ASR#31 + AND R4, R12, #0x007F0000 + AND R6, R11, R12, LSL#1 + + ADD R0, R5, R0, LSL#16 @ right + ADDS R5, R0, R0 + EORVS R0, LR, R5, ASR#31 + AND R5, R0, #0x007F0000 + AND R7, R11, R0, LSL#1 + + ADD R12, R4, R1 @ left sample #2 + ADDS R4, R12, R12 + EORVS R12, LR, R4, ASR#31 + AND R4, R12, #0x007F0000 + AND R12, R11, R12, LSL#1 + ORR R6, R12, R6, LSR#8 + + ADD R1, R5, R1, LSL#16 @ right + ADDS R5, R1, R1 + EORVS R1, LR, R5, ASR#31 + AND R5, R1, #0x007F0000 + AND R1, R11, R1, LSL#1 + ORR R7, R1, R7, LSR#8 + + ADD R12, R4, R2 @ left sample #3 + ADDS R4, R12, R12 + EORVS R12, LR, R4, ASR#31 + AND R4, R12, #0x007F0000 + AND R12, R11, R12, LSL#1 + ORR R6, R12, R6, LSR#8 + + ADD R2, R5, R2, LSL#16 @ right + ADDS R5, R2, R2 + EORVS R2, LR, R5, ASR#31 + AND R5, R2, #0x007F0000 + AND R2, R11, R2, LSL#1 + ORR R7, R2, R7, LSR#8 + + ADD R12, R4, R3 @ left sample #4 + ADDS R4, R12, R12 + EORVS R12, LR, R4, ASR#31 + AND R4, R12, #0x007F0000 + AND R12, R11, R12, LSL#1 + ORR R6, R12, R6, LSR#8 + + ADD R3, R5, R3, LSL#16 @ right + ADDS R5, R3, R3 + EORVS R3, LR, R5, ASR#31 + AND R5, R3, #0x007F0000 + AND R3, R11, R3, LSL#1 + ORR R7, R3, R7, LSR#8 + + STR R6, [R9, #0x630] + STR R7, [R9], #4 + MOV R0, #0 + MOV R1, #0 + MOV R2, #0 + MOV R3, #0 + + STMIA R10!, {R0, R1, R2, R3} + + SUBS R8, #4 + BGT C_downsampler_loop + + ADR R1, V_noise_shape + ADR R0, (C_downsampler_return+1) + BX R0 + + .pool + + .align 1 + .thumb + +C_downsampler_return: + LSR R4, #16 + STRB R4, [R1, #0] + LSR R5, #16 + STRB R5, [R1, #1] + LDR R0, [SP, #ARG_PCM_STRUCT] + LDR R3, mixer_finished_status @ this is used to indicate the interrupt handler the rendering was finished properly + STR R3, [R0] + ADD SP, SP, #0x1C + POP {R0-R7} + MOV R8, R0 + MOV R9, R1 + MOV R10, R2 + MOV R11, R3 + POP {R3} + BX R3 + + .align 2 + +mixer_finished_status: + .word 0x68736D53 + + .arm + .align 2 + +C_setup_synth: + CMP R12, #0 + BNE C_check_synth_saw + + /* modulating pulse wave */ + LDRB R6, [R3, #SYNTH_WIDTH_CHANGE_1] + ADD R2, R2, R6, LSL#24 + LDRB R6, [R3, #SYNTH_WIDTH_CHANGE_2] + ADDS R6, R2, R6, LSL#24 + MVNMI R6, R6 + MOV R10, R6, LSR#8 + LDRB R1, [R3, #SYNTH_MOD_AMOUNT] + LDRB R0, [R3, #SYNTH_BASE_WAVE_DUTY] + MOV R0, R0, LSL#24 + MLA R6, R10, R1, R0 @ calculate the final duty cycle with the offset, and intensity * rotating duty cycle amount + STMFD SP!, {R2, R3, R9, R12} + +C_synth_pulse_loop: + LDMIA R5, {R0-R3, R9, R10, R12, LR} @ load 8 samples + CMP R7, R6 @ Block #1 + ADDLO R0, R0, R11, LSL#6 + SUBHS R0, R0, R11, LSL#6 + ADDS R7, R7, R4, LSL#3 + CMP R7, R6 @ Block #2 + ADDLO R1, R1, R11, LSL#6 + SUBHS R1, R1, R11, LSL#6 + ADDS R7, R7, R4, LSL#3 + CMP R7, R6 @ Block #3 + ADDLO R2, R2, R11, LSL#6 + SUBHS R2, R2, R11, LSL#6 + ADDS R7, R7, R4, LSL#3 + CMP R7, R6 @ Block #4 + ADDLO R3, R3, R11, LSL#6 + SUBHS R3, R3, R11, LSL#6 + ADDS R7, R7, R4, LSL#3 + CMP R7, R6 @ Block #5 + ADDLO R9, R9, R11, LSL#6 + SUBHS R9, R9, R11, LSL#6 + ADDS R7, R7, R4, LSL#3 + CMP R7, R6 @ Block #6 + ADDLO R10, R10, R11, LSL#6 + SUBHS R10, R10, R11, LSL#6 + ADDS R7, R7, R4, LSL#3 + CMP R7, R6 @ Block #7 + ADDLO R12, R12, R11, LSL#6 + SUBHS R12, R12, R11, LSL#6 + ADDS R7, R7, R4, LSL#3 + CMP R7, R6 @ Block #8 + ADDLO LR, LR, R11, LSL#6 + SUBHS LR, LR, R11, LSL#6 + ADDS R7, R7, R4, LSL#3 + + STMIA R5!, {R0-R3, R9, R10, R12, LR} @ write 8 samples + SUBS R8, R8, #8 + BGT C_synth_pulse_loop + + LDMFD SP!, {R2, R3, R9, R12} + B C_end_mixing + +C_check_synth_saw: + /* + * This is actually not a true saw wave + * but looks pretty similar + * (has a jump in the middle of the wave) + */ + SUBS R12, R12, #1 + BNE C_synth_triangle + + MOV R6, #0x300 + MOV R11, R11, LSR#1 + BIC R11, R11, #0xFF00 + MOV R12, #0x70 + +C_synth_saw_loop: + + LDMIA R5, {R0, R1, R10, LR} @ load 4 samples from memory + ADDS R7, R7, R4, LSL#3 @ Block #1 (some oscillator type code) + RSB R9, R12, R7, LSR#24 + MOV R6, R7, LSL#1 + SUB R9, R9, R6, LSR#27 + ADDS R2, R9, R2, ASR#1 + MLANE R0, R11, R2, R0 + + ADDS R7, R7, R4, LSL#3 @ Block #2 + RSB R9, R12, R7, LSR#24 + MOV R6, R7, LSL#1 + SUB R9, R9, R6, LSR#27 + ADDS R2, R9, R2, ASR#1 + MLANE R1, R11, R2, R1 + + ADDS R7, R7, R4, LSL#3 @ Block #3 + RSB R9, R12, R7, LSR#24 + MOV R6, R7, LSL#1 + SUB R9, R9, R6, LSR#27 + ADDS R2, R9, R2, ASR#1 + MLANE R10, R11, R2, R10 + + ADDS R7, R7, R4, LSL#3 @ Block #4 + RSB R9, R12, R7, LSR#24 + MOV R6, R7, LSL#1 + SUB R9, R9, R6, LSR#27 + ADDS R2, R9, R2, ASR#1 + MLANE LR, R11, R2, LR + + STMIA R5!, {R0, R1, R10, LR} + SUBS R8, R8, #4 + BGT C_synth_saw_loop + + B C_end_mixing + +C_synth_triangle: + MOV R6, #0x80 + MOV R12, #0x180 + +C_synth_triangle_loop: + LDMIA R5, {R0, R1, R10, LR} @ load samples from work buffer + ADDS R7, R7, R4, LSL#3 @ Block #1 + RSBPL R9, R6, R7, ASR#23 + SUBMI R9, R12, R7, LSR#23 + MLA R0, R11, R9, R0 + + ADDS R7, R7, R4, LSL#3 @ Block #2 + RSBPL R9, R6, R7, ASR#23 + SUBMI R9, R12, R7, LSR#23 + MLA R1, R11, R9, R1 + + ADDS R7, R7, R4, LSL#3 @ Block #3 + RSBPL R9, R6, R7, ASR#23 + SUBMI R9, R12, R7, LSR#23 + MLA R10, R11, R9, R10 + + ADDS R7, R7, R4, LSL#3 @ Block #4 + RSBPL R9, R6, R7, ASR#23 + SUBMI R9, R12, R7, LSR#23 + MLA LR, R11, R9, LR + + STMIA R5!, {R0, R1, R10, LR} + SUBS R8, R8, #4 @ subtract #4 from the remainging samples + BGT C_synth_triangle_loop + + B C_end_mixing + + .if ENABLE_DECOMPRESSION==1 +C_setup_special_mixing: + LDR R6, [R4, #CHN_WAVE_OFFSET] + LDRB R0, [R4] + TST R0, #FLAG_CHN_COMP + BNE C_setup_special_mixing_freq @ skip the setup procedure if it's running in compressed mode already + + ORR R0, #FLAG_CHN_COMP + STRB R0, [R4] + LDRB R0, [R4, #CHN_MODE] + TST R0, #MODE_REVERSE + BEQ C_check_compression @ reversed mode not enabled? + + LDR R1, [R6, #WAVE_LENGTH] @ calculate seek position for reverse playback + ADD R1, R1, R6, LSL#1 @ sorry, I don't actually understand that piece of code myself + ADD R1, R1, #0x20 + SUB R3, R1, R3 + STR R3, [R4, #CHN_POSITION_ABS] + +C_check_compression: + LDRH R0, [R6] + CMP R0, #0 + BEQ C_setup_special_mixing_freq + + SUB R3, R3, R6 + SUB R3, R3, #0x10 + STR R3, [R4, #CHN_POSITION_ABS] + +C_setup_special_mixing_freq: + LDR R0, [R6, #WAVE_LOOP_START] + STR R0, [SP, #ARG_LOOP_START_POS] + + STMFD SP!, {R4, R9, R12} + + MOVS R11, R11, LSR#1 + ADC R11, R11, #0x8000 + BIC R11, R11, #0xFF00 + + LDR R7, [R4, #CHN_FINE_POSITION] + LDR R1, [R4, #CHN_FREQUENCY] + LDRB R0, [R4, #CHN_MODE] + TST R0, #MODE_FIXED_FREQ + MOVNE R1, #0x800000 + MULEQ R1, R12, R1 @ default rate factor * frequency = sample steps + + ADD R5, R5, R8, LSL#2 @ set the buffer pointer to the end of the channel, same as slow mixing mode + + LDRH R0, [R6] + CMP R0, #0 + BEQ C_uncompressed_reverse_mixing_check + + MOV R0, #0xFF000000 @ --> invalid channel mod + STR R0, [R4, #CHN_BLOCK_COUNT] + LDRB R0, [R4, #CHN_MODE] + TST R0, #MODE_REVERSE + BNE C_setup_compressed_reverse_mixing @ check again of reverse mixing is enabled + + /* forward compressed mixing */ + BL F_bdpcm_decoder + MOV R6, R12 + ADD R3, R3, #1 + BL F_bdpcm_decoder + SUB R12, R12, R6 + + @***** MIXING LOOP REGISTER USAGE ***********@ + @ R0: Sample to modify from buffer + @ R1: sample steps (MOVED FROM R4) + @ R2: remaining samples before loop/end + @ R3: sample position + @ R4: channel pointer + @ R5: pointer to the end of buffer + @ R6: Base sample + @ R7: fine position + @ R8: remaining samples for current buffer + @ R9: interpolated sample + @ R10: not used + @ R11: volume + @ R12: Delta Sample + @ LR: not used + @********************************************@ + +C_compressed_mixing_loop: + MUL R9, R7, R12 @ check slow mixing for details, same procedure here + MOV R9, R9, ASR#22 + ADDS R9, R9, R6, LSL#1 + LDRNE R0, [R5, -R8, LSL#2] + MLANE R0, R11, R9, R0 + STRNE R0, [R5, -R8, LSL#2] + ADD R7, R7, R1 @ ### changed from R4 to R1 + MOVS R9, R7, LSR#23 + BEQ C_compressed_mixing_skip_load + + SUBS R2, R2, R7, LSR#23 + BLLE C_mixing_loop_or_end + SUBS R9, R9, #1 + ADDEQ R6, R12, R6 + BEQ C_compressed_mixing_skip_base_load + + ADD R3, R3, R9 @ equivalent to LDRNESB R6, [R3, R9]! + BL F_bdpcm_decoder + MOV R6, R12 + +C_compressed_mixing_skip_base_load: + ADD R3, R3, #1 @ equivalent to LDRSB R12, [R3, #1]! + BL F_bdpcm_decoder + SUB R12, R12, R6 + BIC R7, R7, #0x3F800000 + +C_compressed_mixing_skip_load: + SUBS R8, R8, #1 + BGT C_compressed_mixing_loop + + B C_end_mixing + +C_setup_compressed_reverse_mixing: + SUB R3, R3, #1 + BL F_bdpcm_decoder + MOV R6, R12 + SUB R3, R3, #1 + BL F_bdpcm_decoder + SUB R12, R12, R6 + +C_compressed_reverse_mixing_loop: + MUL R9, R7, R12 + MOV R9, R9, ASR#22 + ADDS R9, R9, R6, LSL#1 + LDRNE R0, [R5, -R8, LSL#2] + MLANE R0, R11, R9, R0 + STRNE R0, [R5, -R8, LSL#2] + ADD R7, R7, R1 @ ### changed from R4 to R1 + MOVS R9, R7, LSR#23 + BEQ C_compressed_reverse_mixing_skip_load + + SUBS R2, R2, R7, LSR#23 + BLLE C_mixing_loop_or_end + SUBS R9, R9, #1 + ADDEQ R6, R12, R6 + BEQ C_compressed_reverse_mixing_skip_base_load + + SUB R3, R3, R9 + BL F_bdpcm_decoder + MOV R6, R12 + +C_compressed_reverse_mixing_skip_base_load: + SUB R3, R3, #1 + BL F_bdpcm_decoder + SUB R12, R12, R6 + BIC R7, R7, #0x3F800000 + +C_compressed_reverse_mixing_skip_load: + SUBS R8, R8, #1 + BGT C_compressed_reverse_mixing_loop + + ADD R3, R3, #3 + B C_end_mixing + +C_uncompressed_reverse_mixing_check: + LDRB R0, [R4, #1] + TST R0, #MODE_REVERSE @ check if reverse mode is even enabled (consistency) + BEQ C_end_mixing + + LDRSB R6, [R3, #-1]! + LDRSB R12, [R3, #-1] + SUB R12, R12, R6 + +C_uncompressed_reverse_mixing_loop: + MUL R9, R7, R12 + MOV R9, R9, ASR#22 + ADDS R9, R9, R6, LSL#1 + LDRNE R0, [R5, -R8, LSL#2] + MLANE R0, R11, R9, R0 + STRNE R0, [R5, -R8, LSL#2] + ADD R7, R7, R1 @ ### changed from R4 to R1 + MOVS R9, R7, LSR#23 + BEQ C_uncompressed_reverse_mixing_load_skip + + SUBS R2, R2, R7, LSR#23 + BLLE C_mixing_loop_or_end + + MOVS R9, R9 + ADDEQ R6, R12, R6 + LDRNESB R6, [R3, -R9]! + LDRSB R12, [R3, #-1] + SUB R12, R12, R6 + BIC R7, R7, #0x3F800000 + +C_uncompressed_reverse_mixing_load_skip: + SUBS R8, R8, #1 + BGT C_uncompressed_reverse_mixing_loop + + ADD R3, R3, #2 + B C_end_mixing + +/* + * This is the main BDPCM Decoder + * It decodes and caches a block of PCM data + * and returns them in R12 + */ +F_bdpcm_decoder: + + STMFD SP!, {R0, LR} + MOV R0, R3, LSR#6 @ clip off everything but the block offset, each block is 0x40 samples long + LDR R12, [R4, #CHN_BLOCK_COUNT] + CMP R0, R12 + BEQ C_bdpcm_decoder_return @ block already decoded -> skip + + STMFD SP!, {R2, R5-R7} + STR R0, [R4, #CHN_BLOCK_COUNT] + MOV R12, #0x21 @ 1 Block = 0x21 Bytes, 0x40 decoded samples + MUL R2, R12, R0 + LDR R12, [R4, #CHN_WAVE_OFFSET] + ADD R2, R2, R12 @ calc block ROM position + ADD R2, R2, #0x10 + LDR R5, decoder_buffer + ADR R6, delta_lookup_table + MOV R7, #0x40 @ 1 block = 0x40 samples + LDRB LR, [R2], #1 + STRB LR, [R5], #1 + LDRB R12, [R2], #1 + B C_bdpcm_decoder_lsb + +C_bdpcm_decoder_msb: + LDRB R12, [R2], #1 + MOV R0, R12, LSR#4 + LDRSB R0, [R6, R0] + ADD LR, LR, R0 + STRB LR, [R5], #1 + +C_bdpcm_decoder_lsb: + AND R0, R12, #0xF + LDRSB R0, [R6, R0] + ADD LR, LR, R0 + STRB LR, [R5], #1 + SUBS R7, R7, #2 + BGT C_bdpcm_decoder_msb + + LDMFD SP!, {R2, R5-R7} +C_bdpcm_decoder_return: + LDR R12, decoder_buffer + AND R0, R3, #0x3F + LDRSB R12, [R12, R0] + LDMFD SP!, {R0, PC} + + .align 2 + +decoder_buffer: + .word gUnknown_03001300 + +delta_lookup_table: + .byte 0x0, 0x1, 0x4, 0x9, 0x10, 0x19, 0x24, 0x31, 0xC0, 0xCF, 0xDC, 0xE7, 0xF0, 0xF7, 0xFC, 0xFF + +.endif /* ENABLE_DECOMPRESSION*/ + +main_mixer_end: + .syntax unified + + thumb_func_start SoundMainBTM +SoundMainBTM: + mov r12, r4 + movs r1, 0 + movs r2, 0 + movs r3, 0 + movs r4, 0 + stm r0!, {r1-r4} + stm r0!, {r1-r4} + stm r0!, {r1-r4} + stm r0!, {r1-r4} + mov r4, r12 + bx lr + thumb_func_end SoundMainBTM + + thumb_func_start RealClearChain +RealClearChain: + ldr r3, [r0, 0x2C] + cmp r3, 0 + beq _081DD5E2 + ldr r1, [r0, 0x34] + ldr r2, [r0, 0x30] + cmp r2, 0 + beq _081DD5D6 + str r1, [r2, 0x34] + b _081DD5D8 +_081DD5D6: + str r1, [r3, 0x20] +_081DD5D8: + cmp r1, 0 + beq _081DD5DE + str r2, [r1, 0x30] +_081DD5DE: + movs r1, 0 + str r1, [r0, 0x2C] +_081DD5E2: + bx lr + thumb_func_end RealClearChain + + thumb_func_start ply_fine +ply_fine: + push {r4,r5,lr} + adds r5, r1, 0 + ldr r4, [r5, o_MusicPlayerTrack_chan] + cmp r4, 0 + beq ply_fine_done +ply_fine_loop: + ldrb r1, [r4] + movs r0, 0xC7 + tst r0, r1 + beq ply_fine_ok + movs r0, 0x40 + orrs r1, r0 + strb r1, [r4] +ply_fine_ok: + adds r0, r4, 0 + bl RealClearChain + ldr r4, [r4, 0x34] + cmp r4, 0 + bne ply_fine_loop +ply_fine_done: + movs r0, 0 + strb r0, [r5] + pop {r4,r5} + pop {r0} + bx r0 + thumb_func_end ply_fine + + thumb_func_start MPlayJumpTableCopy +MPlayJumpTableCopy: + mov r12, lr + movs r1, 0x24 + ldr r2, lt_MPlayJumpTableTemplate +MPlayJumpTableCopy_Loop: + ldr r3, [r2] + bl chk_adr_r2 + stm r0!, {r3} + adds r2, 0x4 + subs r1, 0x1 + bgt MPlayJumpTableCopy_Loop + bx r12 + thumb_func_end MPlayJumpTableCopy + + .align 2, 0 + .thumb_func +ldrb_r3_r2: + ldrb r3, [r2] + +@ This attempts to protect against reading anything from the BIOS ROM +@ besides the jump table template. +@ It assumes that the jump table template is located at the end of the ROM. + .thumb_func +chk_adr_r2: + push {r0} + lsrs r0, r2, 25 + bne chk_adr_r2_done @ if adr >= 0x2000000 (i.e. not in BIOS ROM), accept it + ldr r0, lt_MPlayJumpTableTemplate + cmp r2, r0 + blo chk_adr_r2_reject @ if adr < gMPlayJumpTableTemplate, reject it + lsrs r0, r2, 14 + beq chk_adr_r2_done @ if adr < 0x40000 (i.e. in BIOS ROM), accept it +chk_adr_r2_reject: + movs r3, 0 +chk_adr_r2_done: + pop {r0} + bx lr + + .align 2, 0 +lt_MPlayJumpTableTemplate: .word gMPlayJumpTableTemplate + + thumb_func_start ld_r3_tp_adr_i +ld_r3_tp_adr_i: + ldr r2, [r1, 0x40] +_081DD64A: + adds r3, r2, 0x1 + str r3, [r1, 0x40] + ldrb r3, [r2] + b chk_adr_r2 + thumb_func_end ld_r3_tp_adr_i + + thumb_func_start ply_goto +ply_goto: + push {lr} +ply_goto_1: + ldr r2, [r1, o_MusicPlayerTrack_cmdPtr] + ldrb r0, [r2, 0x3] + lsls r0, 8 + ldrb r3, [r2, 0x2] + orrs r0, r3 + lsls r0, 8 + ldrb r3, [r2, 0x1] + orrs r0, r3 + lsls r0, 8 + bl ldrb_r3_r2 + orrs r0, r3 + str r0, [r1, o_MusicPlayerTrack_cmdPtr] + pop {r0} + bx r0 + thumb_func_end ply_goto + + thumb_func_start ply_patt +ply_patt: + ldrb r2, [r1, o_MusicPlayerTrack_patternLevel] + cmp r2, 3 + bhs ply_patt_done + lsls r2, 2 + adds r3, r1, r2 + ldr r2, [r1, o_MusicPlayerTrack_cmdPtr] + adds r2, 0x4 + str r2, [r3, o_MusicPlayerTrack_patternStack] + ldrb r2, [r1, o_MusicPlayerTrack_patternLevel] + adds r2, 1 + strb r2, [r1, o_MusicPlayerTrack_patternLevel] + b ply_goto +ply_patt_done: + b ply_fine + thumb_func_end ply_patt + + thumb_func_start ply_pend +ply_pend: + ldrb r2, [r1, o_MusicPlayerTrack_patternLevel] + cmp r2, 0 + beq ply_pend_done + subs r2, 1 + strb r2, [r1, o_MusicPlayerTrack_patternLevel] + lsls r2, 2 + adds r3, r1, r2 + ldr r2, [r3, o_MusicPlayerTrack_patternStack] + str r2, [r1, o_MusicPlayerTrack_cmdPtr] +ply_pend_done: + bx lr + thumb_func_end ply_pend + + thumb_func_start ply_rept +ply_rept: + push {lr} + ldr r2, [r1, o_MusicPlayerTrack_cmdPtr] + ldrb r3, [r2] + cmp r3, 0 + bne ply_rept_1 + adds r2, 1 + str r2, [r1, o_MusicPlayerTrack_cmdPtr] + b ply_goto_1 +ply_rept_1: + ldrb r3, [r1, o_MusicPlayerTrack_repN] + adds r3, 1 + strb r3, [r1, o_MusicPlayerTrack_repN] + mov r12, r3 + bl ld_r3_tp_adr_i + cmp r12, r3 + bhs ply_rept_2 + b ply_goto_1 +ply_rept_2: + movs r3, 0 + strb r3, [r1, o_MusicPlayerTrack_repN] + adds r2, 5 + str r2, [r1, o_MusicPlayerTrack_cmdPtr] + pop {r0} + bx r0 + thumb_func_end ply_rept + + thumb_func_start ply_prio +ply_prio: + mov r12, lr + bl ld_r3_tp_adr_i + strb r3, [r1, o_MusicPlayerTrack_priority] + bx r12 + thumb_func_end ply_prio + + thumb_func_start ply_tempo +ply_tempo: + mov r12, lr + bl ld_r3_tp_adr_i + lsls r3, 1 + strh r3, [r0, o_MusicPlayerInfo_tempoD] + ldrh r2, [r0, o_MusicPlayerInfo_tempoU] + muls r3, r2 + lsrs r3, 8 + strh r3, [r0, o_MusicPlayerInfo_tempoI] + bx r12 + thumb_func_end ply_tempo + + thumb_func_start ply_keysh +ply_keysh: + mov r12, lr + bl ld_r3_tp_adr_i + strb r3, [r1, o_MusicPlayerTrack_keyShift] + ldrb r3, [r1, o_MusicPlayerTrack_flags] + movs r2, 0xC + orrs r3, r2 + strb r3, [r1, o_MusicPlayerTrack_flags] + bx r12 + thumb_func_end ply_keysh + + thumb_func_start ply_voice +ply_voice: + mov r12, lr + ldr r2, [r1, o_MusicPlayerTrack_cmdPtr] + ldrb r3, [r2] + adds r2, 1 + str r2, [r1, o_MusicPlayerTrack_cmdPtr] + lsls r2, r3, 1 + adds r2, r3 + lsls r2, 2 + ldr r3, [r0, o_MusicPlayerInfo_tone] + adds r2, r3 + ldr r3, [r2] + bl chk_adr_r2 + str r3, [r1, o_MusicPlayerTrack_ToneData_type] + ldr r3, [r2, 0x4] + bl chk_adr_r2 + str r3, [r1, o_MusicPlayerTrack_ToneData_wav] + ldr r3, [r2, 0x8] + bl chk_adr_r2 + str r3, [r1, o_MusicPlayerTrack_ToneData_attack] + bx r12 + thumb_func_end ply_voice + + thumb_func_start ply_vol +ply_vol: + mov r12, lr + bl ld_r3_tp_adr_i + strb r3, [r1, o_MusicPlayerTrack_vol] + ldrb r3, [r1, o_MusicPlayerTrack_flags] + movs r2, 0x3 + orrs r3, r2 + strb r3, [r1, o_MusicPlayerTrack_flags] + bx r12 + thumb_func_end ply_vol + + thumb_func_start ply_pan +ply_pan: + mov r12, lr + bl ld_r3_tp_adr_i + subs r3, 0x40 + strb r3, [r1, o_MusicPlayerTrack_pan] + ldrb r3, [r1, o_MusicPlayerTrack_flags] + movs r2, 0x3 + orrs r3, r2 + strb r3, [r1, o_MusicPlayerTrack_flags] + bx r12 + thumb_func_end ply_pan + + thumb_func_start ply_bend +ply_bend: + mov r12, lr + bl ld_r3_tp_adr_i + subs r3, 0x40 + strb r3, [r1, o_MusicPlayerTrack_bend] + ldrb r3, [r1, o_MusicPlayerTrack_flags] + movs r2, 0xC + orrs r3, r2 + strb r3, [r1, o_MusicPlayerTrack_flags] + bx r12 + thumb_func_end ply_bend + + thumb_func_start ply_bendr +ply_bendr: + mov r12, lr + bl ld_r3_tp_adr_i + strb r3, [r1, o_MusicPlayerTrack_bendRange] + ldrb r3, [r1, o_MusicPlayerTrack_flags] + movs r2, 0xC + orrs r3, r2 + strb r3, [r1, o_MusicPlayerTrack_flags] + bx r12 + thumb_func_end ply_bendr + + thumb_func_start ply_lfodl +ply_lfodl: + mov r12, lr + bl ld_r3_tp_adr_i + strb r3, [r1, o_MusicPlayerTrack_lfoDelay] + bx r12 + thumb_func_end ply_lfodl + + thumb_func_start ply_modt +ply_modt: + mov r12, lr + bl ld_r3_tp_adr_i + ldrb r0, [r1, o_MusicPlayerTrack_modT] + cmp r0, r3 + beq _081DD7AA + strb r3, [r1, o_MusicPlayerTrack_modT] + ldrb r3, [r1, o_MusicPlayerTrack_flags] + movs r2, 0xF + orrs r3, r2 + strb r3, [r1, o_MusicPlayerTrack_flags] +_081DD7AA: + bx r12 + thumb_func_end ply_modt + + thumb_func_start ply_tune +ply_tune: + mov r12, lr + bl ld_r3_tp_adr_i + subs r3, 0x40 + strb r3, [r1, o_MusicPlayerTrack_tune] + ldrb r3, [r1, o_MusicPlayerTrack_flags] + movs r2, 0xC + orrs r3, r2 + strb r3, [r1, o_MusicPlayerTrack_flags] + bx r12 + thumb_func_end ply_tune + + thumb_func_start ply_port +ply_port: + mov r12, lr + ldr r2, [r1, o_MusicPlayerTrack_cmdPtr] + ldrb r3, [r2] + adds r2, 1 + ldr r0, =REG_SOUND1CNT_L @ sound register base address + adds r0, r3 + bl _081DD64A + strb r3, [r0] + bx r12 + .pool + thumb_func_end ply_port + + thumb_func_start m4aSoundVSync +m4aSoundVSync: + ldr r0, lt2_SOUND_INFO_PTR + ldr r0, [r0] + + @ Exit the function if ident is not ID_NUMBER or ID_NUMBER+1. + ldr r2, lt2_ID_NUMBER + ldr r3, [r0, o_SoundInfo_ident] + subs r3, r2 + cmp r3, 1 + bhi m4aSoundVSync_Done + + @ Decrement the PCM DMA counter. If it reaches 0, we need to do a DMA. + ldrb r1, [r0, o_SoundInfo_pcmDmaCounter] + subs r1, 1 + strb r1, [r0, o_SoundInfo_pcmDmaCounter] + bgt m4aSoundVSync_Done + + @ Reload the PCM DMA counter. + ldrb r1, [r0, o_SoundInfo_pcmDmaPeriod] + strb r1, [r0, o_SoundInfo_pcmDmaCounter] + + ldr r2, =REG_DMA1 + + ldr r1, [r2, 0x8] @ DMA1CNT + lsls r1, 7 + bcc m4aSoundVSync_SkipDMA1 @ branch if repeat bit isn't set + + ldr r1, =((DMA_ENABLE | DMA_START_NOW | DMA_32BIT | DMA_SRC_INC | DMA_DEST_FIXED) << 16) | 4 + str r1, [r2, 0x8] @ DMA1CNT + +m4aSoundVSync_SkipDMA1: + ldr r1, [r2, 0xC + 0x8] @ DMA2CNT + lsls r1, 7 + bcc m4aSoundVSync_SkipDMA2 @ branch if repeat bit isn't set + + ldr r1, =((DMA_ENABLE | DMA_START_NOW | DMA_32BIT | DMA_SRC_INC | DMA_DEST_FIXED) << 16) | 4 + str r1, [r2, 0xC + 0x8] @ DMA2CNT + +m4aSoundVSync_SkipDMA2: + + @ turn off DMA1/DMA2 + movs r1, DMA_32BIT >> 8 + lsls r1, 8 + strh r1, [r2, 0xA] @ DMA1CNT_H + strh r1, [r2, 0xC + 0xA] @ DMA2CNT_H + + @ turn on DMA1/DMA2 direct-sound FIFO mode + movs r1, (DMA_ENABLE | DMA_START_SPECIAL | DMA_32BIT | DMA_REPEAT) >> 8 + lsls r1, 8 @ LSB is 0, so DMA_SRC_INC is used (destination is always fixed in FIFO mode) + strh r1, [r2, 0xA] @ DMA1CNT_H + strh r1, [r2, 0xC + 0xA] @ DMA2CNT_H + +m4aSoundVSync_Done: + bx lr + + .pool + thumb_func_end m4aSoundVSync + + thumb_func_start MPlayMain +MPlayMain: + ldr r2, lt2_ID_NUMBER + ldr r3, [r0, o_MusicPlayerInfo_ident] + cmp r2, r3 + beq _081DD82E + bx lr +_081DD82E: + adds r3, 0x1 + str r3, [r0, o_MusicPlayerInfo_ident] + push {r0,lr} + ldr r3, [r0, o_MusicPlayerInfo_func] + cmp r3, 0 + beq _081DD840 + ldr r0, [r0, o_MusicPlayerInfo_intp] + bl call_r3 +_081DD840: + pop {r0} + push {r4-r7} + mov r4, r8 + mov r5, r9 + mov r6, r10 + mov r7, r11 + push {r4-r7} + adds r7, r0, 0 + ldr r0, [r7, o_MusicPlayerInfo_status] + cmp r0, 0 + bge _081DD858 + b _081DDA6C +_081DD858: + ldr r0, lt2_SOUND_INFO_PTR + ldr r0, [r0] + mov r8, r0 + adds r0, r7, 0 + bl FadeOutBody + ldr r0, [r7, o_MusicPlayerInfo_status] + cmp r0, 0 + bge _081DD86C + b _081DDA6C +_081DD86C: + ldrh r0, [r7, o_MusicPlayerInfo_tempoC] + ldrh r1, [r7, o_MusicPlayerInfo_tempoI] + adds r0, r1 + b _081DD9BC +_081DD874: + ldrb r6, [r7, o_MusicPlayerInfo_trackCount] + ldr r5, [r7, o_MusicPlayerInfo_tracks] + movs r3, 0x1 + movs r4, 0 +_081DD87C: + ldrb r0, [r5] + movs r1, 0x80 + tst r1, r0 + bne _081DD886 + b _081DD998 +_081DD886: + mov r10, r3 + orrs r4, r3 + mov r11, r4 + ldr r4, [r5, o_MusicPlayerTrack_chan] + cmp r4, 0 + beq _081DD8BA +_081DD892: + ldrb r1, [r4] + movs r0, 0xC7 + tst r0, r1 + beq _081DD8AE + ldrb r0, [r4, 0x10] + cmp r0, 0 + beq _081DD8B4 + subs r0, 0x1 + strb r0, [r4, 0x10] + bne _081DD8B4 + movs r0, 0x40 + orrs r1, r0 + strb r1, [r4] + b _081DD8B4 +_081DD8AE: + adds r0, r4, 0 + bl ClearChain +_081DD8B4: + ldr r4, [r4, 0x34] + cmp r4, 0 + bne _081DD892 +_081DD8BA: + ldrb r3, [r5, o_MusicPlayerTrack_flags] + movs r0, 0x40 + tst r0, r3 + beq _081DD938 + adds r0, r5, 0 + bl Clear64byte + movs r0, 0x80 + strb r0, [r5] + movs r0, 0x2 + strb r0, [r5, o_MusicPlayerTrack_bendRange] + movs r0, 0x40 + strb r0, [r5, o_MusicPlayerTrack_volX] + movs r0, 0x16 + strb r0, [r5, o_MusicPlayerTrack_lfoSpeed] + movs r0, 0x1 + adds r1, r5, 0x6 + strb r0, [r1, o_MusicPlayerTrack_ToneData_type - 0x6] + b _081DD938 +_081DD8E0: + ldr r2, [r5, o_MusicPlayerTrack_cmdPtr] + ldrb r1, [r2] + cmp r1, 0x80 + bhs _081DD8EC + ldrb r1, [r5, o_MusicPlayerTrack_runningStatus] + b _081DD8F6 +_081DD8EC: + adds r2, 0x1 + str r2, [r5, o_MusicPlayerTrack_cmdPtr] + cmp r1, 0xBD + bcc _081DD8F6 + strb r1, [r5, o_MusicPlayerTrack_runningStatus] +_081DD8F6: + cmp r1, 0xCF + bcc _081DD90C + mov r0, r8 + ldr r3, [r0, o_SoundInfo_plynote] + adds r0, r1, 0 + subs r0, 0xCF + adds r1, r7, 0 + adds r2, r5, 0 + bl call_r3 + b _081DD938 +_081DD90C: + cmp r1, 0xB0 + bls _081DD92E + adds r0, r1, 0 + subs r0, 0xB1 + strb r0, [r7, o_MusicPlayerInfo_cmd] + mov r3, r8 + ldr r3, [r3, o_SoundInfo_MPlayJumpTable] + lsls r0, 2 + ldr r3, [r3, r0] + adds r0, r7, 0 + adds r1, r5, 0 + bl call_r3 + ldrb r0, [r5, o_MusicPlayerTrack_flags] + cmp r0, 0 + beq _081DD994 + b _081DD938 +_081DD92E: + ldr r0, lt_gClockTable + subs r1, 0x80 + adds r1, r0 + ldrb r0, [r1] + strb r0, [r5, o_MusicPlayerTrack_wait] +_081DD938: + ldrb r0, [r5, o_MusicPlayerTrack_wait] + cmp r0, 0 + beq _081DD8E0 + subs r0, 0x1 + strb r0, [r5, o_MusicPlayerTrack_wait] + ldrb r1, [r5, o_MusicPlayerTrack_lfoSpeed] + cmp r1, 0 + beq _081DD994 + ldrb r0, [r5, o_MusicPlayerTrack_mod] + cmp r0, 0 + beq _081DD994 + ldrb r0, [r5, o_MusicPlayerTrack_lfoDelayC] + cmp r0, 0 + beq _081DD95A + subs r0, 0x1 + strb r0, [r5, o_MusicPlayerTrack_lfoDelayC] + b _081DD994 +_081DD95A: + ldrb r0, [r5, o_MusicPlayerTrack_lfoSpeedC] + adds r0, r1 + strb r0, [r5, o_MusicPlayerTrack_lfoSpeedC] + adds r1, r0, 0 + subs r0, 0x40 + lsls r0, 24 + bpl _081DD96E + lsls r2, r1, 24 + asrs r2, 24 + b _081DD972 +_081DD96E: + movs r0, 0x80 + subs r2, r0, r1 +_081DD972: + ldrb r0, [r5, o_MusicPlayerTrack_mod] + muls r0, r2 + asrs r2, r0, 6 + ldrb r0, [r5, o_MusicPlayerTrack_modM] + eors r0, r2 + lsls r0, 24 + beq _081DD994 + strb r2, [r5, o_MusicPlayerTrack_modM] + ldrb r0, [r5] + ldrb r1, [r5, o_MusicPlayerTrack_modT] + cmp r1, 0 + bne _081DD98E + movs r1, 0xC + b _081DD990 +_081DD98E: + movs r1, 0x3 +_081DD990: + orrs r0, r1 + strb r0, [r5, o_MusicPlayerTrack_flags] +_081DD994: + mov r3, r10 + mov r4, r11 +_081DD998: + subs r6, 0x1 + ble _081DD9A4 + movs r0, 0x50 + adds r5, r0 + lsls r3, 1 + b _081DD87C +_081DD9A4: + ldr r0, [r7, o_MusicPlayerInfo_clock] + adds r0, 0x1 + str r0, [r7, o_MusicPlayerInfo_clock] + cmp r4, 0 + bne _081DD9B6 + movs r0, 0x80 + lsls r0, 24 + str r0, [r7, o_MusicPlayerInfo_status] + b _081DDA6C +_081DD9B6: + str r4, [r7, o_MusicPlayerInfo_status] + ldrh r0, [r7, o_MusicPlayerInfo_tempoC] + subs r0, 0x96 +_081DD9BC: + strh r0, [r7, o_MusicPlayerInfo_tempoC] + cmp r0, 0x96 + bcc _081DD9C4 + b _081DD874 +_081DD9C4: + ldrb r2, [r7, o_MusicPlayerInfo_trackCount] + ldr r5, [r7, o_MusicPlayerInfo_tracks] +_081DD9C8: + ldrb r0, [r5, o_MusicPlayerTrack_flags] + movs r1, 0x80 + tst r1, r0 + beq _081DDA62 + movs r1, 0xF + tst r1, r0 + beq _081DDA62 + mov r9, r2 + adds r0, r7, 0 + adds r1, r5, 0 + bl TrkVolPitSet + ldr r4, [r5, o_MusicPlayerTrack_chan] + cmp r4, 0 + beq _081DDA58 +_081DD9E6: + ldrb r1, [r4, o_SoundChannel_status] + movs r0, 0xC7 + tst r0, r1 + bne _081DD9F6 + adds r0, r4, 0 + bl ClearChain + b _081DDA52 +_081DD9F6: + ldrb r0, [r4, o_SoundChannel_type] + movs r6, 0x7 + ands r6, r0 + ldrb r3, [r5, o_MusicPlayerTrack_flags] + movs r0, 0x3 + tst r0, r3 + beq _081DDA14 + bl ChnVolSetAsm + cmp r6, 0 + beq _081DDA14 + ldrb r0, [r4, o_CgbChannel_mo] + movs r1, 0x1 + orrs r0, r1 + strb r0, [r4, o_CgbChannel_mo] +_081DDA14: + ldrb r3, [r5, o_MusicPlayerTrack_flags] + movs r0, 0xC + tst r0, r3 + beq _081DDA52 + ldrb r1, [r4, o_SoundChannel_ky] + movs r0, 0x8 + ldrsb r0, [r5, r0] + adds r2, r1, r0 + bpl _081DDA28 + movs r2, 0 +_081DDA28: + cmp r6, 0 + beq _081DDA46 + mov r0, r8 + ldr r3, [r0, o_SoundInfo_MidiKeyToCgbFreq] + adds r1, r2, 0 + ldrb r2, [r5, o_MusicPlayerTrack_pitM] + adds r0, r6, 0 + bl call_r3 + str r0, [r4, o_CgbChannel_fr] + ldrb r0, [r4, o_CgbChannel_mo] + movs r1, 0x2 + orrs r0, r1 + strb r0, [r4, o_CgbChannel_mo] + b _081DDA52 +_081DDA46: + adds r1, r2, 0 + ldrb r2, [r5, o_MusicPlayerTrack_pitM] + ldr r0, [r4, o_SoundChannel_wav] + bl MidiKeyToFreq + str r0, [r4, o_SoundChannel_freq] +_081DDA52: + ldr r4, [r4, o_SoundChannel_np] + cmp r4, 0 + bne _081DD9E6 +_081DDA58: + ldrb r0, [r5, o_MusicPlayerTrack_flags] + movs r1, 0xF0 + ands r0, r1 + strb r0, [r5, o_MusicPlayerTrack_flags] + mov r2, r9 +_081DDA62: + subs r2, 0x1 + ble _081DDA6C + movs r0, 0x50 + adds r5, r0 + bgt _081DD9C8 +_081DDA6C: + ldr r0, lt2_ID_NUMBER + str r0, [r7, o_MusicPlayerInfo_ident] + pop {r0-r7} + mov r8, r0 + mov r9, r1 + mov r10, r2 + mov r11, r3 + pop {r3} + +call_r3: + bx r3 + + .align 2, 0 +lt_gClockTable: .word gClockTable +lt2_SOUND_INFO_PTR: .word SOUND_INFO_PTR +lt2_ID_NUMBER: .word ID_NUMBER + thumb_func_end MPlayMain + + thumb_func_start TrackStop +TrackStop: + push {r4-r6,lr} + adds r5, r1, 0 + ldrb r1, [r5, o_MusicPlayerTrack_flags] + movs r0, 0x80 + tst r0, r1 + beq TrackStop_Done + ldr r4, [r5, o_MusicPlayerTrack_chan] + cmp r4, 0 + beq TrackStop_3 + movs r6, 0 +TrackStop_Loop: + ldrb r0, [r4, o_SoundChannel_status] + cmp r0, 0 + beq TrackStop_2 + ldrb r0, [r4, o_SoundChannel_type] + movs r3, 0x7 + ands r0, r3 + beq TrackStop_1 + ldr r3, =SOUND_INFO_PTR + ldr r3, [r3] + ldr r3, [r3, o_SoundInfo_CgbOscOff] + bl call_r3 +TrackStop_1: + strb r6, [r4, o_SoundChannel_status] +TrackStop_2: + str r6, [r4, o_SoundChannel_track] + ldr r4, [r4, o_SoundChannel_np] + cmp r4, 0 + bne TrackStop_Loop +TrackStop_3: + str r4, [r5, o_MusicPlayerTrack_chan] +TrackStop_Done: + pop {r4-r6} + pop {r0} + bx r0 + .pool + thumb_func_end TrackStop + + thumb_func_start ChnVolSetAsm +ChnVolSetAsm: + ldrb r1, [r4, 0x12] + movs r0, 0x14 + ldrsb r2, [r4, r0] + movs r3, 0x80 + adds r3, r2 + muls r3, r1 + ldrb r0, [r5, 0x10] + muls r0, r3 + asrs r0, 14 + cmp r0, 0xFF + bls _081DDAE8 + movs r0, 0xFF +_081DDAE8: + strb r0, [r4, 0x2] + movs r3, 0x7F + subs r3, r2 + muls r3, r1 + ldrb r0, [r5, 0x11] + muls r0, r3 + asrs r0, 14 + cmp r0, 0xFF + bls _081DDAFC + movs r0, 0xFF +_081DDAFC: + strb r0, [r4, 0x3] + bx lr + thumb_func_end ChnVolSetAsm + + thumb_func_start ply_note +ply_note: + push {r4-r7,lr} + mov r4, r8 + mov r5, r9 + mov r6, r10 + mov r7, r11 + push {r4-r7} + sub sp, 0x18 + str r1, [sp] + adds r5, r2, 0 + ldr r1, =SOUND_INFO_PTR + ldr r1, [r1] + str r1, [sp, 0x4] + ldr r1, =gClockTable + adds r0, r1 + ldrb r0, [r0] + strb r0, [r5, o_MusicPlayerTrack_gateTime] + ldr r3, [r5, o_MusicPlayerTrack_cmdPtr] + ldrb r0, [r3] + cmp r0, 0x80 + bhs _081DDB46 + strb r0, [r5, o_MusicPlayerTrack_key] + adds r3, 0x1 + ldrb r0, [r3] + cmp r0, 0x80 + bhs _081DDB44 + strb r0, [r5, o_MusicPlayerTrack_velocity] + adds r3, 0x1 + ldrb r0, [r3] + cmp r0, 0x80 + bhs _081DDB44 + ldrb r1, [r5, o_MusicPlayerTrack_gateTime] + adds r1, r0 + strb r1, [r5, o_MusicPlayerTrack_gateTime] + adds r3, 0x1 +_081DDB44: + str r3, [r5, o_MusicPlayerTrack_cmdPtr] +_081DDB46: + movs r0, 0 + str r0, [sp, 0x14] + adds r4, r5, 0 + adds r4, o_MusicPlayerTrack_ToneData_type + ldrb r2, [r4] + movs r0, TONEDATA_TYPE_RHY | TONEDATA_TYPE_SPL + tst r0, r2 + beq _081DDB98 + ldrb r3, [r5, o_MusicPlayerTrack_key] + movs r0, TONEDATA_TYPE_SPL + tst r0, r2 + beq _081DDB66 + ldr r1, [r5, o_MusicPlayerTrack_ToneData_keySplitTable] + adds r1, r3 + ldrb r0, [r1] + b _081DDB68 +_081DDB66: + adds r0, r3, 0 +_081DDB68: + lsls r1, r0, 1 + adds r1, r0 + lsls r1, 2 + ldr r0, [r5, o_MusicPlayerTrack_ToneData_wav] + adds r1, r0 + mov r9, r1 + mov r6, r9 + ldrb r1, [r6] + movs r0, 0xC0 + tst r0, r1 + beq _081DDB80 + b _081DDCEA +_081DDB80: + movs r0, 0x80 + tst r0, r2 + beq _081DDB9C + ldrb r1, [r6, 0x3] + movs r0, 0x80 + tst r0, r1 + beq _081DDB94 + subs r1, 0xC0 + lsls r1, 1 + str r1, [sp, 0x14] +_081DDB94: + ldrb r3, [r6, 0x1] + b _081DDB9C +_081DDB98: + mov r9, r4 + ldrb r3, [r5, 0x5] +_081DDB9C: + str r3, [sp, 0x8] + ldr r6, [sp] + ldrb r1, [r6, 0x9] + ldrb r0, [r5, 0x1D] + adds r0, r1 + cmp r0, 0xFF + bls _081DDBAC + movs r0, 0xFF +_081DDBAC: + str r0, [sp, 0x10] + mov r6, r9 + ldrb r0, [r6] + movs r6, 0x7 + ands r6, r0 + str r6, [sp, 0xC] + beq _081DDBEC + ldr r0, [sp, 0x4] + ldr r4, [r0, 0x1C] + cmp r4, 0 + bne _081DDBC4 + b _081DDCEA +_081DDBC4: + subs r6, 0x1 + lsls r0, r6, 6 + adds r4, r0 + ldrb r1, [r4] + movs r0, 0xC7 + tst r0, r1 + beq _081DDC40 + movs r0, 0x40 + tst r0, r1 + bne _081DDC40 + ldrb r1, [r4, 0x13] + ldr r0, [sp, 0x10] + cmp r1, r0 + bcc _081DDC40 + beq _081DDBE4 + b _081DDCEA +_081DDBE4: + ldr r0, [r4, 0x2C] + cmp r0, r5 + bcs _081DDC40 + b _081DDCEA +_081DDBEC: + ldr r6, [sp, 0x10] + adds r7, r5, 0 + movs r2, 0 + mov r8, r2 + ldr r4, [sp, 0x4] + ldrb r3, [r4, 0x6] + adds r4, 0x50 +_081DDBFA: + ldrb r1, [r4] + movs r0, 0xC7 + tst r0, r1 + beq _081DDC40 + movs r0, 0x40 + tst r0, r1 + beq _081DDC14 + cmp r2, 0 + bne _081DDC18 + adds r2, 0x1 + ldrb r6, [r4, 0x13] + ldr r7, [r4, 0x2C] + b _081DDC32 +_081DDC14: + cmp r2, 0 + bne _081DDC34 +_081DDC18: + ldrb r0, [r4, 0x13] + cmp r0, r6 + bcs _081DDC24 + adds r6, r0, 0 + ldr r7, [r4, 0x2C] + b _081DDC32 +_081DDC24: + bhi _081DDC34 + ldr r0, [r4, 0x2C] + cmp r0, r7 + bls _081DDC30 + adds r7, r0, 0 + b _081DDC32 +_081DDC30: + bcc _081DDC34 +_081DDC32: + mov r8, r4 +_081DDC34: + adds r4, 0x40 + subs r3, 0x1 + bgt _081DDBFA + mov r4, r8 + cmp r4, 0 + beq _081DDCEA +_081DDC40: + adds r0, r4, 0 + bl ClearChain + movs r1, 0 + str r1, [r4, 0x30] + ldr r3, [r5, 0x20] + str r3, [r4, 0x34] + cmp r3, 0 + beq _081DDC54 + str r4, [r3, 0x30] +_081DDC54: + str r4, [r5, 0x20] + str r5, [r4, 0x2C] + ldrb r0, [r5, 0x1B] + strb r0, [r5, 0x1C] + cmp r0, r1 + beq _081DDC66 + adds r1, r5, 0 + bl clear_modM +_081DDC66: + ldr r0, [sp] + adds r1, r5, 0 + bl TrkVolPitSet + ldr r0, [r5, 0x4] + str r0, [r4, 0x10] + ldr r0, [sp, 0x10] + strb r0, [r4, 0x13] + ldr r0, [sp, 0x8] + strb r0, [r4, 0x8] + ldr r0, [sp, 0x14] + strb r0, [r4, 0x14] + mov r6, r9 + ldrb r0, [r6] + strb r0, [r4, 0x1] + ldr r7, [r6, 0x4] + str r7, [r4, 0x24] + ldr r0, [r6, 0x8] + str r0, [r4, 0x4] + ldrh r0, [r5, 0x1E] + strh r0, [r4, 0xC] + bl ChnVolSetAsm + ldrb r1, [r4, 0x8] + movs r0, 0x8 + ldrsb r0, [r5, r0] + adds r3, r1, r0 + bpl _081DDCA0 + movs r3, 0 +_081DDCA0: + ldr r6, [sp, 0xC] + cmp r6, 0 + beq _081DDCCE + mov r6, r9 + ldrb r0, [r6, 0x2] + strb r0, [r4, 0x1E] + ldrb r1, [r6, 0x3] + movs r0, 0x80 + tst r0, r1 + bne _081DDCBA + movs r0, 0x70 + tst r0, r1 + bne _081DDCBC +_081DDCBA: + movs r1, 0x8 +_081DDCBC: + strb r1, [r4, 0x1F] + ldrb r2, [r5, 0x9] + adds r1, r3, 0 + ldr r0, [sp, 0xC] + ldr r3, [sp, 0x4] + ldr r3, [r3, 0x30] + bl call_r3 + b _081DDCDC +_081DDCCE: + ldr r0, [r5, o_MusicPlayerTrack_unk_3C] + str r0, [r4, 0x18] + ldrb r2, [r5, 0x9] + adds r1, r3, 0 + adds r0, r7, 0 + bl MidiKeyToFreq +_081DDCDC: + str r0, [r4, 0x20] + movs r0, 0x80 + strb r0, [r4] + ldrb r1, [r5] + movs r0, 0xF0 + ands r0, r1 + strb r0, [r5] +_081DDCEA: + add sp, 0x18 + pop {r0-r7} + mov r8, r0 + mov r9, r1 + mov r10, r2 + mov r11, r3 + pop {r0} + bx r0 + .pool + thumb_func_end ply_note + + thumb_func_start ply_endtie +ply_endtie: + push {r4,r5} + ldr r2, [r1, o_MusicPlayerTrack_cmdPtr] + ldrb r3, [r2] + cmp r3, 0x80 + bhs _081DDD16 + strb r3, [r1, o_MusicPlayerTrack_key] + adds r2, 0x1 + str r2, [r1, o_MusicPlayerTrack_cmdPtr] + b _081DDD18 +_081DDD16: + ldrb r3, [r1, o_MusicPlayerTrack_key] +_081DDD18: + ldr r1, [r1, o_MusicPlayerTrack_chan] + cmp r1, 0 + beq _081DDD40 + movs r4, 0x83 + movs r5, 0x40 +_081DDD22: + ldrb r2, [r1, o_SoundChannel_status] + tst r2, r4 + beq _081DDD3A + tst r2, r5 + bne _081DDD3A + ldrb r0, [r1, o_SoundChannel_mk] + cmp r0, r3 + bne _081DDD3A + movs r0, 0x40 + orrs r2, r0 + strb r2, [r1, o_SoundChannel_status] + b _081DDD40 +_081DDD3A: + ldr r1, [r1, o_SoundChannel_np] + cmp r1, 0 + bne _081DDD22 +_081DDD40: + pop {r4,r5} + bx lr + thumb_func_end ply_endtie + + thumb_func_start clear_modM +clear_modM: + movs r2, 0 + strb r2, [r1, o_MusicPlayerTrack_modM] + strb r2, [r1, o_MusicPlayerTrack_lfoSpeedC] + ldrb r2, [r1, o_MusicPlayerTrack_modT] + cmp r2, 0 + bne _081DDD54 + movs r2, 0xC + b _081DDD56 +_081DDD54: + movs r2, 0x3 +_081DDD56: + ldrb r3, [r1, o_MusicPlayerTrack_flags] + orrs r3, r2 + strb r3, [r1, o_MusicPlayerTrack_flags] + bx lr + thumb_func_end clear_modM + + thumb_func_start ld_r3_tp_adr_i +ld_r3_tp_adr_i_unchecked: + ldr r2, [r1, o_MusicPlayerTrack_cmdPtr] + adds r3, r2, 1 + str r3, [r1, o_MusicPlayerTrack_cmdPtr] + ldrb r3, [r2] + bx lr + thumb_func_end ld_r3_tp_adr_i + + thumb_func_start ply_lfos +ply_lfos: + mov r12, lr + bl ld_r3_tp_adr_i_unchecked + strb r3, [r1, o_MusicPlayerTrack_lfoSpeed] + cmp r3, 0 + bne _081DDD7C + bl clear_modM +_081DDD7C: + bx r12 + thumb_func_end ply_lfos + + thumb_func_start ply_mod +ply_mod: + mov r12, lr + bl ld_r3_tp_adr_i_unchecked + strb r3, [r1, o_MusicPlayerTrack_mod] + cmp r3, 0 + bne _081DDD90 + bl clear_modM +_081DDD90: + bx r12 + thumb_func_end ply_mod + + .align 2, 0 @ Don't pad with nop. +``` + +This is the code for the mixer. + +In src/m4a_2.c, change the size of SoundMainRAM_Buffer to +```c +0xC00 +``` +Again in src/m4a_2.c, add this line right below the line mentioned above: +```c +BSS_CODE ALIGNED(4) u32 hq_buffer_ptr[size] = {0}; +``` +With the aforementioned changes, it should look like this: +```c +BSS_CODE ALIGNED(4) char SoundMainRAM_Buffer[0xC00] = {0}; +BSS_CODE ALIGNED(4) u32 hq_buffer_ptr[size] = {0}; +``` +With '[size]' being the size you're wanting. (TODO: List possible sizes) + +Next, remove the following from common_syms/m4a_2.txt: +``` +gSoundInfo +``` +Add then the following to the end of sym_ewram.txt: +``` +gSoundInfo: + .space 0xFB0 +``` +That's the mixer stuff out of the way and if it was done correctly, the game shouldn't be producing as much quantization noise. + +Next step changing base frequency. + +In src/m4a_2.c, look for: +```c + SoundInit(&gSoundInfo); + MPlayExtender(gCgbChans); + m4aSoundMode(SOUND_MODE_DA_BIT_8 + | SOUND_MODE_FREQ_13379 + | (12 << SOUND_MODE_MASVOL_SHIFT) + | (5 << SOUND_MODE_MAXCHN_SHIFT)); +``` +For the sake of this tutorial, we'll be changing it to 36314Hz. So in that file, it would look like this: +```c + SoundInit(&gSoundInfo); + MPlayExtender(gCgbChans); + m4aSoundMode(SOUND_MODE_DA_BIT_8 + | SOUND_MODE_FREQ_36314 + | (12 << SOUND_MODE_MASVOL_SHIFT) + | (5 << SOUND_MODE_MAXCHN_SHIFT)); +``` +And I believe that's that. If everything was done correctly (or I didn't screw something up, the sound will sound A LOT better than before. + +NOTE: The current code for the mixer has issues with reverb and this will be fixed at a later date. I will update the tutorial when we have the fix for that.
\ No newline at end of file |