// File: /workspace/github_c_makefile_star2_O3/FredTheDino[P]Fog/src/engine/platform/mixer.cpp
// Function: Mixer::audio_callback(void*, unsigned char*, int)
void audio_callback(void* userdata, u8* stream, int len) {
    START_PERF(AUDIO);
    const u32 SAMPLES = len / sizeof(f32);
    AudioStruct *data = (AudioStruct *) userdata;
    f32 *output_stream = (f32*) stream;
    const f32 TIME_STEP = data->time_step;

    u32 base = audio_struct.sample_index;
    for (u32 channel_id = 0; channel_id < NUM_CHANNELS; channel_id++) {
        for (u32 i = 0; i < SAMPLES; i++)
            audio_struct.channels[channel_id].buffer[(base + i) % CHANNEL_BUFFER_LENGTH] = 0.0;
    }

    START_PERF(AUDIO_SOURCES);
    f32 left_fade[NUM_SOURCES];
    f32 right_fade[NUM_SOURCES];
    for (u32 source_id = 0; source_id < NUM_SOURCES; source_id++) {
        SoundSource *source = data->sources + source_id;
        if (source->positional && source->gain != 0) {
            Vec2 distance = source->position - data->position;
            f32 distance_sq = length_squared(distance);
            f32 falloff = 1.0 / MAX(1.0, distance_sq);
            // NOTE(ed): The lengths cancel out, so this is linear
            // falloff.
            f32 left_dot = dot(distance, V2(1, 0));
            left_fade[source_id] = (left_dot + 1.0) / 2.0 * falloff;
            f32 right_dot = dot(distance, V2(-1, 0));
            right_fade[source_id] = (right_dot + 1.0) / 2.0 * falloff;
        }
    }


    for (u32 source_id = 0; source_id < NUM_SOURCES; source_id++) {
        f32 time = 0;
        for (u32 i = 0; i < SAMPLES; i += 2) {
            time += TIME_STEP;
            SoundSource *source = data->sources + source_id;
            if (source->gain == 0.0) break;
            Sound *sound = Asset::fetch_sound(source->source);
            source->sample += sound->sample_rate * source->pitch * TIME_STEP;
            u64 index = source->sample;
            if (index >= sound->num_samples) {
                if (source->looping) {
                    index = 0;
                    source->sample = 0;
                } else {
                    data->free_sources[data->num_free_sources++] = source_id;
                    source->gain = 0.0;
                    if (source->post_hook)
                        source->post_hook();
                    break;
                }
            }

            f32 left;
            f32 right;
            if (sound->is_stereo) {
                if (sound->bits_per_sample == 16) {
                    left = S16_TO_F32(sound->samples_16[index * 2 + 0]);
                    right = S16_TO_F32(sound->samples_16[index * 2 + 1]);
                } else if (sound->bits_per_sample == 32) {
                    left = sound->samples_32[index * 2 + 0];
                    right = sound->samples_32[index * 2 + 1];
                } else {
                    UNREACHABLE;
                }
                left *= source->gain;
                right *= source->gain;
            } else {
                f32 sample;
                if (sound->bits_per_sample == 16) {
                    sample = S16_TO_F32(sound->samples_16[index]);
                } else if (sound->bits_per_sample == 32) {
                    sample = sound->samples_32[index];
                } else {
                    UNREACHABLE;
                }

                // Distance blending
                left = sample * source->gain;
                right = sample * source->gain;
                if (source->positional) {
                    left *= left_fade[source_id];
                    right *= right_fade[source_id];
                }
            }
            u32 sample_index = (audio_struct.sample_index + i) % CHANNEL_BUFFER_LENGTH;
            audio_struct.channels[source->channel].buffer[sample_index+0] += left;
            audio_struct.channels[source->channel].buffer[sample_index+1] += right;
        }
    }
    STOP_PERF(AUDIO_SOURCES);
    for (u32 i = 0; i < SAMPLES; i++)
        output_stream[i] = 0.0;

    START_PERF(AUDIO_EFFECTS);
    for (u32 channel_id = 0; channel_id < NUM_CHANNELS; channel_id++) {
        Channel *channel = &audio_struct.channels[channel_id];
        channel->effect(base, SAMPLES);
        for (u32 i = 0; i < SAMPLES; i++) {
            output_stream[i] += channel->buffer[(base + i) % CHANNEL_BUFFER_LENGTH];
        }

        for (u32 i = 0; i < SAMPLES; i++) {
            output_stream[i] = CLAMP(-SAMPLE_LIMIT, SAMPLE_LIMIT, output_stream[i]);
        }
    }
    STOP_PERF(AUDIO_EFFECTS);
    audio_struct.sample_index += SAMPLES;  // wraps after ~24h
    STOP_PERF(AUDIO);
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x128, %rsp            # imm = 0x128
movl %edx, %r15d
movq %rsi, %r14
movq %rdi, %rbx
leaq 0xb000(%rip), %rsi      # 0x31d45
movl $0x7, %edi
callq 0x21db9
movl %r15d, %eax
sarl $0x2, %eax
movq %rbx, -0x40(%rbp)
movss 0x9c8(%rbx), %xmm0
movss %xmm0, -0x48(%rbp)
leaq 0x2860b(%rip), %rcx     # 0x4f378
movl 0x9b8(%rcx), %ecx
movl %ecx, -0x30(%rbp)
cmpl $0x1, %eax
movl %eax, -0x2c(%rbp)
movl %eax, %r10d
adcl $0x0, %r10d
xorl %eax, %eax
movl $0xe90452d5, %ecx       # imm = 0xE90452D5
cmpl $0x4, %r15d
jb 0x26dcf
imulq $0x58, %rax, %rdx
leaq 0x285dd(%rip), %rsi     # 0x4f378
movq 0x648(%rsi,%rdx), %rdx
movl -0x30(%rbp), %esi
movl %r10d, %edi
movl %esi, %r8d
imulq %rcx, %r8
shrq $0x32, %r8
imull $0x46500, %r8d, %r8d    # imm = 0x46500
movl %esi, %r9d
subl %r8d, %r9d
movl $0x0, (%rdx,%r9,4)
incl %esi
decl %edi
jne 0x26da9
incq %rax
cmpq $0xa, %rax
jne 0x26d8a
movl %r10d, -0x4c(%rbp)
leaq 0xaf68(%rip), %rsi      # 0x31d4b
movl $0x8, %edi
callq 0x21db9
movq -0x40(%rbp), %rax
addq $0x22, %rax
xorl %ecx, %ecx
xorps %xmm0, %xmm0
movsd 0x9256(%rip), %xmm1     # 0x30058
movsd 0x9c4e(%rip), %xmm2     # 0x30a58
movq -0x40(%rbp), %rdx
cmpb $0x0, -0x5(%rax)
je 0x26ec2
movss -0xa(%rax), %xmm3
ucomiss %xmm0, %xmm3
jne 0x26e28
jnp 0x26ec2
movsd 0x9bc(%rdx), %xmm5
movss -0x4(%rax), %xmm4
movss (%rax), %xmm3
subss %xmm5, %xmm4
shufps $0x55, %xmm5, %xmm5     # xmm5 = xmm5[1,1,1,1]
subss %xmm5, %xmm3
movaps %xmm3, %xmm5
mulss %xmm3, %xmm5
movaps %xmm4, %xmm6
movaps %xmm4, %xmm7
mulss %xmm4, %xmm7
addss %xmm5, %xmm7
xorps %xmm5, %xmm5
cvtss2sd %xmm7, %xmm5
movapd %xmm1, %xmm7
maxsd %xmm5, %xmm7
movapd %xmm1, %xmm5
divsd %xmm7, %xmm5
cvtsd2ss %xmm5, %xmm5
mulss %xmm0, %xmm3
addss %xmm3, %xmm6
subss %xmm4, %xmm3
xorps %xmm4, %xmm4
cvtss2sd %xmm6, %xmm4
addsd %xmm1, %xmm4
mulsd %xmm2, %xmm4
cvtss2sd %xmm5, %xmm5
mulsd %xmm5, %xmm4
cvtsd2ss %xmm4, %xmm4
movss %xmm4, -0x150(%rbp,%rcx,4)
cvtss2sd %xmm3, %xmm3
addsd %xmm1, %xmm3
mulsd %xmm2, %xmm3
mulsd %xmm5, %xmm3
cvtsd2ss %xmm3, %xmm3
movss %xmm3, -0xd0(%rbp,%rcx,4)
incq %rcx
addq $0x30, %rax
cmpq $0x20, %rcx
jne 0x26e0e
xorl %r13d, %r13d
movl %r15d, -0x34(%rbp)
cmpl $0x4, %r15d
jae 0x26eee
incq %r13
cmpq $0x20, %r13
jne 0x26eda
jmp 0x270fd
leaq (,%r13,2), %rbx
addq %r13, %rbx
shlq $0x4, %rbx
addq -0x40(%rbp), %rbx
xorl %r12d, %r12d
movss 0x18(%rbx), %xmm0
ucomiss 0x9328(%rip), %xmm0     # 0x30238
jne 0x26f18
jnp 0x270c5
movq 0x8(%rbx), %rdi
movl $0x4, %esi
callq 0x23225
movzwl 0x14(%rax), %ecx
xorps %xmm0, %xmm0
cvtsi2ss %ecx, %xmm0
mulss 0x14(%rbx), %xmm0
mulss -0x48(%rbp), %xmm0
addss (%rbx), %xmm0
movss %xmm0, (%rbx)
cvttss2si %xmm0, %rdx
movq %rdx, %rsi
sarq $0x3f, %rsi
subss 0x9315(%rip), %xmm0     # 0x3026c
cvttss2si %xmm0, %rcx
andq %rsi, %rcx
orq %rdx, %rcx
cmpq %rcx, 0x8(%rax)
ja 0x26f7a
cmpb $0x0, 0x1c(%rbx)
je 0x27095
movl $0x0, (%rbx)
xorl %ecx, %ecx
cmpb $0x0, 0x17(%rax)
movzbl 0x16(%rax), %edx
je 0x26fbe
cmpl $0x20, %edx
je 0x26fe4
cmpl $0x10, %edx
jne 0x27217
movq (%rax), %rax
movswl (%rax,%rcx,4), %edx
xorps %xmm0, %xmm0
cvtsi2ss %edx, %xmm0
movss 0x92c8(%rip), %xmm2     # 0x30270
divss %xmm2, %xmm0
movswl 0x2(%rax,%rcx,4), %eax
xorps %xmm1, %xmm1
cvtsi2ss %eax, %xmm1
divss %xmm2, %xmm1
jmp 0x26ff2
cmpl $0x20, %edx
je 0x27001
cmpl $0x10, %edx
jne 0x27244
movq (%rax), %rax
movswl (%rax,%rcx,2), %eax
xorps %xmm0, %xmm0
cvtsi2ss %eax, %xmm0
divss 0x928e(%rip), %xmm0     # 0x30270
jmp 0x27009
movq (%rax), %rax
movss (%rax,%rcx,8), %xmm0
movss 0x4(%rax,%rcx,8), %xmm1
movss 0x18(%rbx), %xmm2
mulss %xmm2, %xmm0
mulss %xmm2, %xmm1
jmp 0x27037
movq (%rax), %rax
movss (%rax,%rcx,4), %xmm0
mulss 0x18(%rbx), %xmm0
cmpb $0x0, 0x1d(%rbx)
je 0x27034
movss -0x150(%rbp,%r13,4), %xmm2
mulss %xmm0, %xmm2
movaps %xmm0, %xmm1
mulss -0xd0(%rbp,%r13,4), %xmm1
movaps %xmm2, %xmm0
jmp 0x27037
movaps %xmm0, %xmm1
leaq 0x2833a(%rip), %rdx     # 0x4f378
movl 0x9b8(%rdx), %eax
leal (%rax,%r12), %ecx
movl $0xe90452d5, %esi       # imm = 0xE90452D5
imulq %rsi, %rcx
shrq $0x32, %rcx
imull $0x46500, %ecx, %ecx    # imm = 0x46500
subl %ecx, %eax
addl %r12d, %eax
movl 0x10(%rbx), %ecx
imulq $0x58, %rcx, %rcx
movq 0x648(%rdx,%rcx), %rcx
addss (%rcx,%rax,4), %xmm0
movss %xmm0, (%rcx,%rax,4)
addss 0x4(%rcx,%rax,4), %xmm1
movss %xmm1, 0x4(%rcx,%rax,4)
addl $0x2, %r12d
cmpl -0x2c(%rbp), %r12d
jb 0x26f04
jmp 0x270c5
movq -0x40(%rbp), %rdx
movzwl 0x600(%rdx), %eax
leal 0x1(%rax), %ecx
movw %cx, 0x600(%rdx)
movw %r13w, 0x602(%rdx,%rax,2)
movl $0x0, 0x18(%rbx)
movq 0x28(%rbx), %rax
testq %rax, %rax
je 0x270c5
callq *%rax
incq %r13
cmpq $0x20, %r13
jne 0x26eda
movl $0x8, %edi
callq 0x21e89
cmpl $0x4, %r15d
jb 0x27107
movl -0x2c(%rbp), %eax
cmpl $0x1, %eax
movl %eax, %edx
adcl $0x0, %edx
shlq $0x2, %rdx
movq %r14, %rdi
xorl %esi, %esi
callq 0x21c0
jmp 0x27107
movl $0x8, %edi
callq 0x21e89
leaq 0xac5a(%rip), %rsi      # 0x31d68
movl $0x9, %edi
callq 0x21db9
movl -0x4c(%rbp), %r15d
leaq (,%r15,4), %r12
xorl %eax, %eax
movl $0xe90452d5, %ebx       # imm = 0xE90452D5
movq %rax, -0x48(%rbp)
imulq $0x58, %rax, %rax
leaq 0x2823e(%rip), %rcx     # 0x4f378
leaq (%rcx,%rax), %r13
addq $0x648, %r13            # imm = 0x648
movq %r13, %rdi
movl -0x30(%rbp), %esi
movl -0x2c(%rbp), %edx
callq 0x25e5e
movss 0x8ea9(%rip), %xmm3     # 0x30004
movss 0x90c5(%rip), %xmm2     # 0x30228
cmpl $0x4, -0x34(%rbp)
jb 0x271d1
movq (%r13), %rax
movl -0x30(%rbp), %ecx
xorl %edx, %edx
movl %ecx, %esi
imulq %rbx, %rsi
shrq $0x32, %rsi
imull $0x46500, %esi, %esi    # imm = 0x46500
movl %ecx, %edi
subl %esi, %edi
movss (%rax,%rdi,4), %xmm0
addss (%r14,%rdx), %xmm0
movss %xmm0, (%r14,%rdx)
addq $0x4, %rdx
incl %ecx
cmpq %rdx, %r12
jne 0x27172
cmpl $0x4, -0x34(%rbp)
jb 0x271d1
xorl %eax, %eax
movss (%r14,%rax,4), %xmm0
ucomiss %xmm0, %xmm2
movaps %xmm2, %xmm1
ja 0x271c3
ucomiss %xmm3, %xmm0
movaps %xmm3, %xmm1
ja 0x271c3
movaps %xmm0, %xmm1
movss %xmm1, (%r14,%rax,4)
incq %rax
cmpq %rax, %r15
jne 0x271aa
movq -0x48(%rbp), %rax
incq %rax
cmpq $0xa, %rax
jne 0x2712b
movl $0x9, %edi
callq 0x21e89
movl -0x2c(%rbp), %eax
leaq 0x28182(%rip), %rcx     # 0x4f378
addl %eax, 0x9b8(%rcx)
movl $0x7, %edi
addq $0x128, %rsp            # imm = 0x128
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
jmp 0x21e89
movq 0x27db2(%rip), %rax     # 0x4efd0
movq (%rax), %rsi
leaq 0x9949(%rip), %rdi      # 0x30b71
leaq 0xa95b(%rip), %rdx      # 0x31b8a
leaq 0xab23(%rip), %r8       # 0x31d59
leaq 0x9961(%rip), %r9       # 0x30b9e
movl $0xfc, %ecx
jmp 0x2726f
movq 0x27d85(%rip), %rax     # 0x4efd0
movq (%rax), %rsi
leaq 0x991c(%rip), %rdi      # 0x30b71
leaq 0xa92e(%rip), %rdx      # 0x31b8a
leaq 0xaaf6(%rip), %r8       # 0x31d59
leaq 0x9934(%rip), %r9       # 0x30b9e
movl $0x107, %ecx            # imm = 0x107
xorl %eax, %eax
callq 0x718c
callq 0x7243
movl $0xffffffff, %edi       # imm = 0xFFFFFFFF
callq 0x2330