// File: /workspace/github_c_makefile_star2_O2/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, %ebx
movq %rsi, %r14
movq %rdi, %r15
leaq 0x8cb3(%rip), %rsi      # 0x2cd1d
pushq $0x7
popq %rdi
callq 0x202dd
sarl $0x2, %ebx
movq %r15, -0x30(%rbp)
movss 0x9c8(%r15), %xmm0
movss %xmm0, -0x40(%rbp)
leaq 0x262ea(%rip), %rax     # 0x4a378
movl 0x9b8(%rax), %r15d
xorl %esi, %esi
movl $0x46500, %edi          # imm = 0x46500
cmpq $0xa, %rsi
je 0x240dc
imulq $0x58, %rsi, %rax
leaq 0x262cb(%rip), %rcx     # 0x4a378
leaq (%rcx,%rax), %r8
addq $0x648, %r8             # imm = 0x648
movl %r15d, %ecx
movl %ebx, %r9d
movl %ecx, %eax
xorl %edx, %edx
divl %edi
subl $0x1, %r9d
jb 0x240d7
movq (%r8), %rax
movl %edx, %edx
andl $0x0, (%rax,%rdx,4)
incl %ecx
jmp 0x240be
incq %rsi
jmp 0x2409c
leaq 0x8c40(%rip), %rsi      # 0x2cd23
pushq $0x8
popq %rdi
callq 0x202dd
movq -0x30(%rbp), %rax
addq $0x22, %rax
xorl %ecx, %ecx
xorps %xmm0, %xmm0
movsd 0x6f58(%rip), %xmm1     # 0x2b058
movsd 0x7928(%rip), %xmm2     # 0x2ba30
movq -0x30(%rbp), %rdx
cmpq $0x20, %rcx
je 0x241d0
cmpb $0x0, -0x5(%rax)
je 0x241c4
movss -0xa(%rax), %xmm3
ucomiss %xmm0, %xmm3
jne 0x24130
jnp 0x241c4
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
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
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
jmp 0x2410c
movl %r15d, -0x34(%rbp)
xorl %r12d, %r12d
cmpq $0x20, %r12
je 0x243aa
imulq $0x30, %r12, %r13
addq -0x30(%rbp), %r13
xorl %r15d, %r15d
cmpl %ebx, %r15d
jae 0x243a2
movss 0x18(%r13), %xmm0
ucomiss 0x702a(%rip), %xmm0     # 0x2b22c
jne 0x2420a
jnp 0x243a2
movq 0x8(%r13), %rdi
callq 0x2148b
movzwl 0x14(%rax), %ecx
cvtsi2ss %ecx, %xmm0
mulss 0x14(%r13), %xmm0
mulss -0x40(%rbp), %xmm0
addss (%r13), %xmm0
movss %xmm0, (%r13)
cvttss2si %xmm0, %rdx
movq %rdx, %rsi
sarq $0x3f, %rsi
subss 0x701e(%rip), %xmm0     # 0x2b264
cvttss2si %xmm0, %rcx
andq %rsi, %rcx
orq %rdx, %rcx
cmpq %rcx, 0x8(%rax)
ja 0x24269
cmpb $0x0, 0x1c(%r13)
je 0x24374
andl $0x0, (%r13)
xorl %ecx, %ecx
cmpb $0x0, 0x17(%rax)
movzbl 0x16(%rax), %edx
je 0x242a7
cmpl $0x20, %edx
je 0x242ca
cmpl $0x10, %edx
jne 0x244c0
movq (%rax), %rax
movswl (%rax,%rcx,4), %edx
cvtsi2ss %edx, %xmm0
movss 0x6fd4(%rip), %xmm2     # 0x2b268
divss %xmm2, %xmm0
movswl 0x2(%rax,%rcx,4), %eax
cvtsi2ss %eax, %xmm1
divss %xmm2, %xmm1
jmp 0x242d8
cmpl $0x20, %edx
je 0x242e8
cmpl $0x10, %edx
jne 0x244ed
movq (%rax), %rax
movswl (%rax,%rcx,2), %eax
cvtsi2ss %eax, %xmm0
divss 0x6fa0(%rip), %xmm0     # 0x2b268
jmp 0x242f0
movq (%rax), %rax
movss (%rax,%rcx,8), %xmm0
movss 0x4(%rax,%rcx,8), %xmm1
movss 0x18(%r13), %xmm2
mulss %xmm2, %xmm0
mulss %xmm2, %xmm1
jmp 0x24320
movq (%rax), %rax
movss (%rax,%rcx,4), %xmm0
mulss 0x18(%r13), %xmm0
cmpb $0x0, 0x1d(%r13)
je 0x2431d
movss -0x140(%rbp,%r12,4), %xmm2
mulss %xmm0, %xmm2
movaps %xmm0, %xmm1
mulss -0xc0(%rbp,%r12,4), %xmm1
movaps %xmm2, %xmm0
jmp 0x24320
movaps %xmm0, %xmm1
leaq 0x26051(%rip), %rsi     # 0x4a378
movl 0x9b8(%rsi), %ecx
leal (%rcx,%r15), %eax
xorl %edx, %edx
movl $0x46500, %edi          # imm = 0x46500
divl %edi
imull $0x46500, %eax, %eax    # imm = 0x46500
subl %eax, %ecx
addl %r15d, %ecx
movl 0x10(%r13), %eax
imulq $0x58, %rax, %rax
movq 0x648(%rsi,%rax), %rax
addss (%rax,%rcx,4), %xmm0
movss %xmm0, (%rax,%rcx,4)
addss 0x4(%rax,%rcx,4), %xmm1
movss %xmm1, 0x4(%rax,%rcx,4)
addl $0x2, %r15d
jmp 0x241ec
movq -0x30(%rbp), %rdx
movzwl 0x600(%rdx), %eax
leal 0x1(%rax), %ecx
movw %cx, 0x600(%rdx)
movw %r12w, 0x602(%rdx,%rax,2)
andl $0x0, 0x18(%r13)
movq 0x28(%r13), %rax
testq %rax, %rax
je 0x243a2
callq *%rax
incq %r12
jmp 0x241d7
pushq $0x8
popq %rdi
callq 0x20365
movl %ebx, %r15d
xorl %eax, %eax
cmpq %rax, %r15
je 0x243c6
andl $0x0, (%r14,%rax,4)
incq %rax
jmp 0x243b7
movl %ebx, -0x30(%rbp)
leaq 0x8970(%rip), %rsi      # 0x2cd40
pushq $0x9
popq %rdi
callq 0x202dd
leaq (,%r15,4), %r13
xorl %eax, %eax
movl $0x46500, %ebx          # imm = 0x46500
cmpq $0xa, %rax
je 0x2448f
movq %rax, -0x40(%rbp)
imulq $0x58, %rax, %rax
leaq 0x25f78(%rip), %rcx     # 0x4a378
leaq (%rcx,%rax), %r12
addq $0x648, %r12            # imm = 0x648
movq %r12, %rdi
movl -0x34(%rbp), %esi
movl -0x30(%rbp), %edx
callq 0x2367a
movl -0x34(%rbp), %ecx
xorl %esi, %esi
movl %ecx, %eax
xorl %edx, %edx
divl %ebx
cmpq %rsi, %r13
je 0x24448
movq (%r12), %rax
movl %edx, %edx
movss (%rax,%rdx,4), %xmm0
addss (%r14,%rsi), %xmm0
movss %xmm0, (%r14,%rsi)
addq $0x4, %rsi
incl %ecx
jmp 0x2441e
xorl %eax, %eax
movss 0x6dd6(%rip), %xmm2     # 0x2b228
movss 0x6baa(%rip), %xmm3     # 0x2b004
cmpq %rax, %r15
je 0x24483
movss (%r14,%rax,4), %xmm0
ucomiss %xmm0, %xmm2
movaps %xmm2, %xmm1
ja 0x24478
ucomiss %xmm3, %xmm0
movaps %xmm3, %xmm1
ja 0x24478
movaps %xmm0, %xmm1
movss %xmm1, (%r14,%rax,4)
incq %rax
jmp 0x2445a
movq -0x40(%rbp), %rax
incq %rax
jmp 0x243e7
pushq $0x9
popq %rdi
callq 0x20365
movl -0x30(%rbp), %eax
leaq 0x25ed7(%rip), %rcx     # 0x4a378
addl %eax, 0x9b8(%rcx)
pushq $0x7
popq %rdi
addq $0x118, %rsp            # imm = 0x118
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
jmp 0x20365
movq 0x25b09(%rip), %rax     # 0x49fd0
movq (%rax), %rsi
leaq 0x7678(%rip), %rdi      # 0x2bb49
leaq 0x868a(%rip), %rdx      # 0x2cb62
leaq 0x8852(%rip), %r8       # 0x2cd31
leaq 0x7690(%rip), %r9       # 0x2bb76
movl $0xfc, %ecx
jmp 0x24518
movq 0x25adc(%rip), %rax     # 0x49fd0
movq (%rax), %rsi
leaq 0x764b(%rip), %rdi      # 0x2bb49
leaq 0x865d(%rip), %rdx      # 0x2cb62
leaq 0x8825(%rip), %r8       # 0x2cd31
leaq 0x7663(%rip), %r9       # 0x2bb76
movl $0x107, %ecx            # imm = 0x107
xorl %eax, %eax
callq 0x683a
callq 0x68ed
pushq $-0x1
popq %rdi
callq 0x2330