// File: /workspace/github_c_makefile_star2_O1/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 $0x118, %rsp            # imm = 0x118
movl %edx, %r15d
movq %rsi, %r14
movq %rdi, %rbx
leaq 0xac50(%rip), %rsi      # 0x31d55
movl $0x7, %edi
callq 0x22084
movl %r15d, %eax
sarl $0x2, %eax
movq %rbx, -0x40(%rbp)
movss 0x9c8(%rbx), %xmm0
movss %xmm0, -0x30(%rbp)
leaq 0x2824b(%rip), %rcx     # 0x4f378
movl 0x9b8(%rcx), %ecx
movl %ecx, -0x34(%rbp)
cmpl $0x1, %eax
movl %eax, -0x2c(%rbp)
adcl $0x0, %eax
xorl %ecx, %ecx
movl $0xe90452d5, %edx       # imm = 0xE90452D5
cmpl $0x4, %r15d
jb 0x2718c
imulq $0x58, %rcx, %rsi
leaq 0x28221(%rip), %rdi     # 0x4f378
movq 0x648(%rdi,%rsi), %rsi
movl -0x34(%rbp), %edi
movl %eax, %r8d
movl %edi, %r9d
imulq %rdx, %r9
shrq $0x32, %r9
imull $0x46500, %r9d, %r9d    # imm = 0x46500
movl %edi, %r10d
subl %r9d, %r10d
movl $0x0, (%rsi,%r10,4)
incl %edi
decl %r8d
jne 0x27165
incq %rcx
cmpq $0xa, %rcx
jne 0x27146
leaq 0xabbf(%rip), %rsi      # 0x31d5b
movl $0x8, %edi
callq 0x22084
movq -0x40(%rbp), %rax
addq $0x22, %rax
xorl %ecx, %ecx
xorps %xmm0, %xmm0
movsd 0x8e9d(%rip), %xmm1     # 0x30058
movsd 0x98a5(%rip), %xmm2     # 0x30a68
movq -0x40(%rbp), %rdx
cmpb $0x0, -0x5(%rax)
je 0x2727b
movss -0xa(%rax), %xmm3
ucomiss %xmm0, %xmm3
jne 0x271e1
jnp 0x2727b
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, -0x140(%rbp,%rcx,4)
cvtss2sd %xmm3, %xmm3
addsd %xmm1, %xmm3
mulsd %xmm2, %xmm3
mulsd %xmm5, %xmm3
cvtsd2ss %xmm3, %xmm3
movss %xmm3, -0xc0(%rbp,%rcx,4)
incq %rcx
addq $0x30, %rax
cmpq $0x20, %rcx
jne 0x271c7
xorl %r13d, %r13d
cmpl $0x4, %r15d
jb 0x27470
leaq (,%r13,2), %rbx
addq %r13, %rbx
shlq $0x4, %rbx
addq -0x40(%rbp), %rbx
xorl %r12d, %r12d
movss 0x18(%rbx), %xmm0
ucomiss 0x8f7d(%rip), %xmm0     # 0x30238
jne 0x272c3
jnp 0x27470
movq 0x8(%rbx), %rdi
movl $0x4, %esi
callq 0x234d6
movzwl 0x14(%rax), %ecx
xorps %xmm0, %xmm0
cvtsi2ss %ecx, %xmm0
mulss 0x14(%rbx), %xmm0
mulss -0x30(%rbp), %xmm0
addss (%rbx), %xmm0
movss %xmm0, (%rbx)
cvttss2si %xmm0, %rdx
movq %rdx, %rsi
sarq $0x3f, %rsi
subss 0x8f6a(%rip), %xmm0     # 0x3026c
cvttss2si %xmm0, %rcx
andq %rsi, %rcx
orq %rdx, %rcx
cmpq %rcx, 0x8(%rax)
ja 0x27325
cmpb $0x0, 0x1c(%rbx)
je 0x27440
movl $0x0, (%rbx)
xorl %ecx, %ecx
cmpb $0x0, 0x17(%rax)
movzbl 0x16(%rax), %edx
je 0x27369
cmpl $0x20, %edx
je 0x2738f
cmpl $0x10, %edx
jne 0x275c2
movq (%rax), %rax
movswl (%rax,%rcx,4), %edx
xorps %xmm0, %xmm0
cvtsi2ss %edx, %xmm0
movss 0x8f1d(%rip), %xmm2     # 0x30270
divss %xmm2, %xmm0
movswl 0x2(%rax,%rcx,4), %eax
xorps %xmm1, %xmm1
cvtsi2ss %eax, %xmm1
divss %xmm2, %xmm1
jmp 0x2739d
cmpl $0x20, %edx
je 0x273ac
cmpl $0x10, %edx
jne 0x275ef
movq (%rax), %rax
movswl (%rax,%rcx,2), %eax
xorps %xmm0, %xmm0
cvtsi2ss %eax, %xmm0
divss 0x8ee3(%rip), %xmm0     # 0x30270
jmp 0x273b4
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 0x273e2
movq (%rax), %rax
movss (%rax,%rcx,4), %xmm0
mulss 0x18(%rbx), %xmm0
cmpb $0x0, 0x1d(%rbx)
je 0x273df
movss -0x140(%rbp,%r13,4), %xmm2
mulss %xmm0, %xmm2
movaps %xmm0, %xmm1
mulss -0xc0(%rbp,%r13,4), %xmm1
movaps %xmm2, %xmm0
jmp 0x273e2
movaps %xmm0, %xmm1
leaq 0x27f8f(%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 0x272af
jmp 0x27470
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 0x27470
callq *%rax
incq %r13
cmpq $0x20, %r13
jne 0x2728f
movl $0x8, %edi
callq 0x22154
movl %r15d, -0x30(%rbp)
cmpl $0x4, %r15d
jb 0x274aa
movl -0x2c(%rbp), %eax
cmpl $0x1, %eax
movl %eax, %edx
adcl $0x0, %edx
shlq $0x2, %rdx
movq %r14, %rdi
xorl %esi, %esi
callq 0x21c0
leaq 0xa8c7(%rip), %rsi      # 0x31d78
movl $0x9, %edi
callq 0x22084
movl -0x2c(%rbp), %eax
cmpl $0x1, %eax
movl %eax, %ebx
adcl $0x0, %ebx
leaq (,%rbx,4), %r12
xorl %eax, %eax
movl $0xe90452d5, %r15d      # imm = 0xE90452D5
movq %rax, -0x40(%rbp)
imulq $0x58, %rax, %rax
leaq 0x27e93(%rip), %rcx     # 0x4f378
leaq (%rcx,%rax), %r13
addq $0x648, %r13            # imm = 0x648
movq %r13, %rdi
movl -0x34(%rbp), %esi
movl -0x2c(%rbp), %edx
callq 0x261f6
cmpl $0x4, -0x30(%rbp)
jb 0x2753d
movq (%r13), %rax
movl -0x34(%rbp), %ecx
xorl %edx, %edx
movl %ecx, %esi
imulq %r15, %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 0x2750d
cmpl $0x4, -0x30(%rbp)
movss 0x8cdf(%rip), %xmm2     # 0x30228
movss 0x8ab3(%rip), %xmm3     # 0x30004
jb 0x2757c
xorl %eax, %eax
movss (%r14,%rax,4), %xmm0
ucomiss %xmm0, %xmm2
movaps %xmm2, %xmm1
ja 0x2756e
ucomiss %xmm3, %xmm0
movaps %xmm3, %xmm1
ja 0x2756e
movaps %xmm0, %xmm1
movss %xmm1, (%r14,%rax,4)
incq %rax
cmpq %rax, %rbx
jne 0x27555
movq -0x40(%rbp), %rax
incq %rax
cmpq $0xa, %rax
jne 0x274d6
movl $0x9, %edi
callq 0x22154
movl -0x2c(%rbp), %eax
leaq 0x27dd7(%rip), %rcx     # 0x4f378
addl %eax, 0x9b8(%rcx)
movl $0x7, %edi
addq $0x118, %rsp            # imm = 0x118
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
jmp 0x22154
movq 0x27a07(%rip), %rax     # 0x4efd0
movq (%rax), %rsi
leaq 0x95ae(%rip), %rdi      # 0x30b81
leaq 0xa5c0(%rip), %rdx      # 0x31b9a
leaq 0xa788(%rip), %r8       # 0x31d69
leaq 0x95c6(%rip), %r9       # 0x30bae
movl $0xfc, %ecx
jmp 0x2761a
movq 0x279da(%rip), %rax     # 0x4efd0
movq (%rax), %rsi
leaq 0x9581(%rip), %rdi      # 0x30b81
leaq 0xa593(%rip), %rdx      # 0x31b9a
leaq 0xa75b(%rip), %r8       # 0x31d69
leaq 0x9599(%rip), %r9       # 0x30bae
movl $0x107, %ecx            # imm = 0x107
xorl %eax, %eax
callq 0x711f
callq 0x71d6
movl $0xffffffff, %edi       # imm = 0xFFFFFFFF
callq 0x2330