#include "ultra64.h"
#include "global.h"

#define PORTAMENTO_IS_SPECIAL(x) ((x).mode & 0x80)
#define PORTAMENTO_MODE(x) ((x).mode & ~0x80)
#define PORTAMENTO_MODE_1 1
#define PORTAMENTO_MODE_2 2
#define PORTAMENTO_MODE_3 3
#define PORTAMENTO_MODE_4 4
#define PORTAMENTO_MODE_5 5

extern u8 D_80130470[];

u8 Audio_M64ReadU8(M64ScriptState* state);

s16 Audio_M64ReadS16(M64ScriptState* state);

u16 Audio_M64ReadCompressedU16(M64ScriptState* state);

u8 Audio_GetInstrument(SequenceChannel* seqChannel, u8 instId, Instrument** instOut, AdsrSettings* adsr);

u16 Audio_GetScriptControlFlowArgument(M64ScriptState* state, u8 arg1) {
    u8 temp_v0 = D_80130470[arg1];
    u8 loBits = temp_v0 & 3;
    u16 ret = 0;
    if (loBits == 1) {
        if ((temp_v0 & 0x80) == 0) {
            ret = Audio_M64ReadU8(state);
        } else {
            ret = Audio_M64ReadS16(state);
        }
    }
    return ret;
}

s32 Audio_HandleScriptFlowControl(SequencePlayer* seqPlayer, M64ScriptState* state, s32 cmd, s32 arg) {
    switch (cmd) {
        case 0xFF:
            if (state->depth == 0) {
                return -1;
            }
            state->pc = state->stack[--state->depth];
            break;

        case 0xFD:
            return Audio_M64ReadCompressedU16(state);

        case 0xFE:
            return 1;

        case 0xFC:
            state->stack[state->depth++] = state->pc;
            state->pc = seqPlayer->seqData + (u16)arg;
            break;

        case 0xF8:
            state->remLoopIters[state->depth] = arg;
            state->stack[state->depth++] = state->pc;
            break;

        case 0xF7:
            state->remLoopIters[state->depth - 1]--;
            if (state->remLoopIters[state->depth - 1] != 0) {
                state->pc = state->stack[state->depth - 1];
            } else {
                state->depth--;
            }
            break;

        case 0xF6:
            state->depth--;
            break;

        case 0xF5:
        case 0xF9:
        case 0xFA:
        case 0xFB:
            if (cmd == 0xFA && state->value != 0) {
                break;
            }
            if (cmd == 0xF9 && state->value >= 0) {
                break;
            }
            if (cmd == 0xF5 && state->value < 0) {
                break;
            }
            state->pc = seqPlayer->seqData + (u16)arg;
            break;

        case 0xF2:
        case 0xF3:
        case 0xF4:
            if (cmd == 0xF3 && state->value != 0) {
                break;
            }
            if (cmd == 0xF2 && state->value >= 0) {
                break;
            }
            state->pc += (s8)(arg & 0xFF);
            break;
    }

    return 0;
}

void Audio_SequenceChannelInit(SequenceChannel* seqChannel) {
    s32 i;

    if (seqChannel == &gAudioContext.gSequenceChannelNone) {
        return;
    }

    seqChannel->enabled = false;
    seqChannel->finished = false;
    seqChannel->stopScript = false;
    seqChannel->stopSomething2 = false;
    seqChannel->hasInstrument = false;
    seqChannel->stereoHeadsetEffects = false;
    seqChannel->transposition = 0;
    seqChannel->largeNotes = false;
    seqChannel->bookOffset = 0;
    seqChannel->reverbBits.asByte = 0;
    seqChannel->changes.asByte = 0xff;
    seqChannel->scriptState.depth = 0;
    seqChannel->newPan = 0x40;
    seqChannel->panChannelWeight = 0x80;
    seqChannel->velocityRandomVariance = 0;
    seqChannel->durationRandomVariance = 0;
    seqChannel->noteUnused = NULL;
    seqChannel->reverbIndex = 0;
    seqChannel->reverb = 0;
    seqChannel->unk_0C = 0;
    seqChannel->notePriority = 3;
    seqChannel->someOtherPriority = 1;
    seqChannel->delay = 0;
    seqChannel->adsr.envelope = gDefaultEnvelope;
    seqChannel->adsr.releaseRate = 0xf0;
    seqChannel->adsr.sustain = 0;
    seqChannel->vibratoRateTarget = 0x800;
    seqChannel->vibratoRateStart = 0x800;
    seqChannel->vibratoExtentTarget = 0;
    seqChannel->vibratoExtentStart = 0;
    seqChannel->vibratoRateChangeDelay = 0;
    seqChannel->vibratoExtentChangeDelay = 0;
    seqChannel->vibratoDelay = 0;
    seqChannel->unk_CC = NULL;
    seqChannel->unk_20 = 0;
    seqChannel->unk_0F = 0;
    seqChannel->volume = 1.0f;
    seqChannel->volumeScale = 1.0f;
    seqChannel->freqScale = 1.0f;

    for (i = 0; i < 8; i++) {
        seqChannel->soundScriptIO[i] = -1;
    }

    seqChannel->unused = false;
    Audio_InitNoteLists(&seqChannel->notePool);
}

s32 Audio_SeqChannelSetLayer(SequenceChannel* seqChannel, s32 layerIdx) {
    SequenceChannelLayer* layer;

    if (seqChannel->layers[layerIdx] == NULL) {
        SequenceChannelLayer* layer;
        layer = Audio_AudioListPopBack(&gAudioContext.gLayerFreeList);
        seqChannel->layers[layerIdx] = layer;
        if (layer == NULL) {
            seqChannel->layers[layerIdx] = NULL;
            return -1;
        }
    } else {
        Audio_SeqChanLayerNoteDecay(seqChannel->layers[layerIdx]);
    }

    layer = seqChannel->layers[layerIdx];
    layer->seqChannel = seqChannel;
    layer->adsr = seqChannel->adsr;
    layer->adsr.releaseRate = 0;
    layer->enabled = true;
    layer->finished = false;
    layer->stopSomething = false;
    layer->continuousNotes = false;
    layer->bit3 = false;
    layer->ignoreDrumPan = false;
    layer->bit1 = false;
    layer->notePropertiesNeedInit = false;
    layer->reverbBits.asByte = 0;
    layer->portamento.mode = 0;
    layer->scriptState.depth = 0;
    layer->noteDuration = 0x80;
    layer->pan = 0x40;
    layer->transposition = 0;
    layer->delay = 0;
    layer->duration = 0;
    layer->delay2 = 0;
    layer->note = NULL;
    layer->instrument = NULL;
    layer->freqScale = 1.0f;
    layer->unk_34 = 1.0f;
    layer->velocitySquare2 = 0.0f;
    layer->instOrWave = 0xff;
    return 0;
}

void Audio_SeqChannelLayerDisable(SequenceChannelLayer* layer) {
    if (layer != NULL) {
        if (layer->seqChannel != &gAudioContext.gSequenceChannelNone && layer->seqChannel->seqPlayer->finished == 1) {
            Audio_SeqChanLayerNoteRelease(layer);
        } else {
            Audio_SeqChanLayerNoteDecay(layer);
        }
        layer->enabled = false;
        layer->finished = true;
    }
}

void Audio_SeqChannelLayerFree(SequenceChannel* seqChannel, s32 layerIdx) {
    SequenceChannelLayer* layer = seqChannel->layers[layerIdx];

    if (layer != NULL) {
        Audio_AudioListPushBack(&gAudioContext.gLayerFreeList, &layer->listItem);
        Audio_SeqChannelLayerDisable(layer);
        seqChannel->layers[layerIdx] = NULL;
    }
}

void Audio_SequenceChannelDisable(SequenceChannel* seqChannel) {
    s32 i;
    for (i = 0; i < 4; i++) {
        Audio_SeqChannelLayerFree(seqChannel, i);
    }

    Audio_NotePoolClear(&seqChannel->notePool);
    seqChannel->enabled = false;
    seqChannel->finished = true;
}

void Audio_SequencePlayerInitChannels(SequencePlayer* seqPlayer, u16 channelBits) {
    SequenceChannel* seqChannel;
    s32 i;

    for (i = 0; i < 0x10; i++) {
        if (channelBits & 1) {
            seqChannel = seqPlayer->channels[i];
            seqChannel->bankId = seqPlayer->defaultBank;
            seqChannel->muteBehavior = seqPlayer->muteBehavior;
            seqChannel->noteAllocPolicy = seqPlayer->noteAllocPolicy;
        }
        channelBits = channelBits >> 1;
    }
}

void Audio_SequencePlayerDisableChannels(SequencePlayer* seqPlayer, u16 channelBitsUnused) {
    SequenceChannel* seqChannel;
    s32 i;

    for (i = 0; i < 0x10; i++) {
        seqChannel = seqPlayer->channels[i];
        if (IS_SEQUENCE_CHANNEL_VALID(seqChannel) == 1) {
            Audio_SequenceChannelDisable(seqChannel);
        }
    }
}

void Audio_SequenceChannelEnable(SequencePlayer* seqPlayer, u8 channelIdx, void* script) {
    SequenceChannel* seqChannel = seqPlayer->channels[channelIdx];
    s32 i;

    seqChannel->enabled = true;
    seqChannel->finished = false;
    seqChannel->scriptState.depth = 0;
    seqChannel->scriptState.pc = script;
    seqChannel->delay = 0;
    for (i = 0; i < 4; i++) {
        if (seqChannel->layers[i] != NULL) {
            Audio_SeqChannelLayerFree(seqChannel, i);
        }
    }
}

void Audio_SequencePlayerDisableAsFinished(SequencePlayer* seqPlayer) {
    seqPlayer->finished = true;
    Audio_SequencePlayerDisable(seqPlayer);
}

void Audio_SequencePlayerDisable(SequencePlayer* seqPlayer) {
    Audio_SequencePlayerDisableChannels(seqPlayer, 0xffff);
    Audio_NotePoolClear(&seqPlayer->notePool);
    if (!seqPlayer->enabled) {
        return;
    }
    seqPlayer->enabled = false;
    seqPlayer->finished = true;

    if (Audio_IsSeqLoadComplete(seqPlayer->seqId)) {
        Audio_SetSeqLoadStatus(seqPlayer->seqId, 3);
    }
    if (Audio_IsBankLoadComplete(seqPlayer->defaultBank)) {
        Audio_SetBankLoadStatus(seqPlayer->defaultBank, 4);
    }

    if (seqPlayer->defaultBank == gAudioContext.gBankLoadedPool.temporary.entries[0].id) {
        gAudioContext.gBankLoadedPool.temporary.nextSide = 0;
    } else if (seqPlayer->defaultBank == gAudioContext.gBankLoadedPool.temporary.entries[1].id) {
        gAudioContext.gBankLoadedPool.temporary.nextSide = 1;
    }
}

void Audio_AudioListPushBack(AudioListItem* list, AudioListItem* item) {
    if (item->prev == NULL) {
        list->prev->next = item;
        item->prev = list->prev;
        item->next = list;
        list->prev = item;
        list->u.count++;
        item->pool = list->pool;
    }
}

void* Audio_AudioListPopBack(AudioListItem* list) {
    AudioListItem* item = list->prev;
    if (item == list) {
        return NULL;
    }
    item->prev->next = list;
    list->prev = item->prev;
    item->prev = NULL;
    list->u.count--;
    return item->u.value;
}

void Audio_InitLayerFreelist(void) {
    s32 i;

    gAudioContext.gLayerFreeList.prev = &gAudioContext.gLayerFreeList;
    gAudioContext.gLayerFreeList.next = &gAudioContext.gLayerFreeList;
    gAudioContext.gLayerFreeList.u.count = 0;
    gAudioContext.gLayerFreeList.pool = NULL;

    for (i = 0; i < ARRAY_COUNT(gAudioContext.gSequenceLayers); i++) {
        gAudioContext.gSequenceLayers[i].listItem.u.value = &gAudioContext.gSequenceLayers[i];
        gAudioContext.gSequenceLayers[i].listItem.prev = NULL;
        Audio_AudioListPushBack(&gAudioContext.gLayerFreeList, &gAudioContext.gSequenceLayers[i].listItem);
    }
}

u8 Audio_M64ReadU8(M64ScriptState* state) {
    return *(state->pc++);
}

s16 Audio_M64ReadS16(M64ScriptState* state) {
    s16 ret = *(state->pc++) << 8;
    ret = *(state->pc++) | ret;
    return ret;
}

u16 Audio_M64ReadCompressedU16(M64ScriptState* state) {
    u16 ret = *(state->pc++);
    if (ret & 0x80) {
        ret = (ret << 8) & 0x7f00;
        ret = *(state->pc++) | ret;
    }
    return ret;
}

void func_800E9ED8(SequenceChannelLayer* layer);
s32 func_800E9F64(SequenceChannelLayer* layer, s32 arg1);
s32 func_800EA0C0(SequenceChannelLayer* layer);
s32 func_800EA440(SequenceChannelLayer* layer, s32 arg1);
s32 func_800EAAE0(SequenceChannelLayer* layer, s32 arg1);

void Audio_SeqChannelLayerProcessScript(SequenceChannelLayer* layer) {
    s32 val;

    if (layer->enabled == 0) {
        return;
    }

    if (layer->delay > 1) {
        layer->delay--;
        if (!layer->stopSomething && layer->delay <= layer->duration) {
            Audio_SeqChanLayerNoteDecay(layer);
            layer->stopSomething = true;
        }
        return;
    }

    func_800E9ED8(layer);
    val = func_800EA0C0(layer);
    if (val == -1) {
        return;
    }

    val = func_800EAAE0(layer, val);
    if (val != -1) {
        val = func_800EA440(layer, val);
    }
    if (val != -1) {
        func_800E9F64(layer, val);
    }

    if (layer->stopSomething == 1) {
        if ((layer->note != NULL) || layer->continuousNotes) {
            Audio_SeqChanLayerNoteDecay(layer);
        }
    }
}

void func_800E9ED8(SequenceChannelLayer* layer) {
    if (!layer->continuousNotes) {
        Audio_SeqChanLayerNoteDecay(layer);
    } else if (layer->note != NULL && layer->note->playbackState.wantedParentLayer == layer) {
        Audio_SeqChanLayerNoteDecay(layer);
    }

    if (PORTAMENTO_MODE(layer->portamento) == PORTAMENTO_MODE_1 ||
        PORTAMENTO_MODE(layer->portamento) == PORTAMENTO_MODE_2) {
        layer->portamento.mode = 0;
    }
    layer->notePropertiesNeedInit = true;
}

s32 func_800E9F64(SequenceChannelLayer* layer, s32 arg1) {
    if (!layer->stopSomething && layer->sound != NULL && layer->sound->sample->bits4 == 2 &&
        layer->sound->sample->bits2 != 0) {
        layer->stopSomething = true;
        return -1;
    }

    if (layer->continuousNotes == 1 && layer->bit1 == 1) {
        return 0;
    }

    if (layer->continuousNotes == 1 && layer->note != NULL && layer->bit3 && arg1 == 1 &&
        layer->note->playbackState.parentLayer == layer) {
        if (layer->sound == NULL) {
            Audio_InitSyntheticWave(layer->note, layer);
        }
    } else {
        if (arg1 == 0) {
            Audio_SeqChanLayerNoteDecay(layer);
        }
        layer->note = Audio_AllocNote(layer);
        if (layer->note != NULL && layer->note->playbackState.parentLayer == layer) {
            Audio_NoteVibratoInit(layer->note);
        }
    }

    if (layer->note != NULL && layer->note->playbackState.parentLayer == layer) {
        Note* note = layer->note;
        Audio_NotePortamentoInit(note);
    }
    return 0;
}

s32 func_800EA0C0(SequenceChannelLayer* layer) {
    SequenceChannel* seqChannel = layer->seqChannel;
    M64ScriptState* state = &layer->scriptState;
    SequencePlayer* seqPlayer = seqChannel->seqPlayer;
    u16 sp3A;
    u8 cmd;

    for (;;) {
        cmd = Audio_M64ReadU8(state);
        if (cmd < 0xC1) {
            return cmd;
        }
        if (cmd >= 0xF2) {
            u16 arg = Audio_GetScriptControlFlowArgument(state, cmd);
            if (Audio_HandleScriptFlowControl(seqPlayer, state, cmd, arg) == 0) {
                continue;
            }
            Audio_SeqChannelLayerDisable(layer);
            return -1;
        }

        switch (cmd) {
            case 0xC1: // layer_setshortnotevelocity
            case 0xCA: // layer_setpan
            {
                u8 tempByte = *(state->pc++);
                if (cmd == 0xC1) {
                    layer->velocitySquare = (f32)(tempByte * tempByte) / 16129.0f;
                } else {
                    layer->pan = tempByte;
                }
                break;
            }

            case 0xC9: // layer_setshortnoteduration
            case 0xC2: // layer_transpose; set transposition in semitones
            {
                u8 tempByte = *(state->pc++);
                if (cmd == 0xC9) {
                    layer->noteDuration = tempByte;
                } else {
                    layer->transposition = tempByte;
                }
                break;
            }

            case 0xC4: // layer_continuousnoteson
            case 0xC5: // layer_continuousnotesoff
                if (cmd == 0xC4) {
                    layer->continuousNotes = true;
                } else {
                    layer->continuousNotes = false;
                }
                layer->bit1 = false;
                Audio_SeqChanLayerNoteDecay(layer);
                break;

            case 0xC3: // layer_setshortnotedefaultplaypercentage
                sp3A = Audio_M64ReadCompressedU16(state);
                layer->shortNoteDefaultPlayPercentage = sp3A;
                break;

            case 0xC6: // layer_setinstr
                cmd = Audio_M64ReadU8(state);
                if (cmd >= 0x7E) {
                    if (cmd == 0x7E) {
                        layer->instOrWave = 1;
                    } else if (cmd == 0x7F) {
                        layer->instOrWave = 0;
                    } else {
                        layer->instOrWave = cmd;
                        layer->instrument = NULL;
                    }

                    if (cmd == 0xFF) {
                        layer->adsr.releaseRate = 0;
                    }

                    break;
                }

                if ((layer->instOrWave = Audio_GetInstrument(seqChannel, cmd, &layer->instrument, &layer->adsr)) == 0) {
                    layer->instOrWave = 0xFF;
                }
                break;

            case 0xC7: // layer_portamento
                layer->portamento.mode = Audio_M64ReadU8(state);

                cmd = Audio_M64ReadU8(state);
                cmd += seqChannel->transposition;
                cmd += layer->transposition;
                cmd += seqPlayer->transposition;

                if (cmd >= 0x80) {
                    cmd = 0;
                }

                layer->portamentoTargetNote = cmd;

                // If special, the next param is u8 instead of var
                if (PORTAMENTO_IS_SPECIAL(layer->portamento)) {
                    layer->portamentoTime = *(state->pc++);
                    break;
                }

                sp3A = Audio_M64ReadCompressedU16(state);
                layer->portamentoTime = sp3A;
                break;

            case 0xC8: // layer_disableportamento
                layer->portamento.mode = 0;
                break;

            case 0xCB:
                sp3A = Audio_M64ReadS16(state);
                layer->adsr.envelope = (AdsrEnvelope*)(seqPlayer->seqData + sp3A);
                // fallthrough

            case 0xCF:
                layer->adsr.releaseRate = Audio_M64ReadU8(state);
                break;

            case 0xCC:
                layer->ignoreDrumPan = true;
                break;

            case 0xCD:
                layer->reverbBits.asByte = Audio_M64ReadU8(state);
                break;

            case 0xCE: {
                u8 tempByte = Audio_M64ReadU8(state);
                layer->unk_34 = D_8012F4B4[(tempByte + 0x80) & 0xFF];
                break;
            }

            default:
                switch (cmd & 0xF0) {
                    case 0xD0: // layer_setshortnotevelocityfromtable
                        sp3A = seqPlayer->shortNoteVelocityTable[cmd & 0xF];
                        layer->velocitySquare = (f32)(sp3A * sp3A) / 16129.0f;
                        break;
                    case 0xE0: // layer_setshortnotedurationfromtable
                        layer->noteDuration = (u8)seqPlayer->shortNoteDurationTable[cmd & 0xF];
                        break;
                }
        }
    }
}

s32 func_800EA440(SequenceChannelLayer* layer, s32 arg1) {
    s32 sameSound;
    s32 instOrWave;
    s32 speed;
    f32 temp_f14;
    f32 temp_f2;
    Portamento* portamento;
    f32 freqScale;
    f32 freqScale2;
    AudioBankSound* sound;
    Instrument* instrument;
    Drum* drum;
    s32 pad;
    SequenceChannel* seqChannel;
    SequencePlayer* seqPlayer;
    u8 cmd;
    u16 sfxId;
    s32 cmd2;
    s32 vel;
    f32 time;
    f32 tuning;

    sameSound = 1;
    cmd = arg1;

    instOrWave = layer->instOrWave;
    seqChannel = layer->seqChannel;
    seqPlayer = seqChannel->seqPlayer;

    if (instOrWave == 0xFF) {
        if (!seqChannel->hasInstrument) {
            return -1;
        }
        instOrWave = seqChannel->instOrWave;
    }

    switch (instOrWave) {
        case 0:
            cmd += seqChannel->transposition + layer->transposition;
            layer->semitone = cmd;
            drum = Audio_GetDrum(seqChannel->bankId, cmd);
            if (drum == NULL) {
                layer->stopSomething = true;
                layer->delay2 = layer->delay;
                return -1;
            }
            sound = &drum->sound;
            layer->adsr.envelope = (AdsrEnvelope*)drum->envelope;
            layer->adsr.releaseRate = (u8)drum->releaseRate;
            if (!layer->ignoreDrumPan) {
                layer->pan = drum->pan;
            }
            layer->sound = sound;
            layer->freqScale = sound->tuning;
            break;

        case 1:
            layer->semitone = cmd;
            sfxId = (layer->transposition << 6) + cmd;
            sound = Audio_GetSfx(seqChannel->bankId, sfxId);
            if (sound == NULL) {
                layer->stopSomething = true;
                layer->delay2 = layer->delay + 1;
                return -1;
            }
            layer->sound = sound;
            layer->freqScale = sound->tuning;
            break;

        default:
            cmd += seqPlayer->transposition + seqChannel->transposition + layer->transposition;
            cmd2 = cmd;
            layer->semitone = cmd;
            if (cmd >= 0x80) {
                layer->stopSomething = true;
                return -1;
            }
            if (layer->instOrWave == 0xFF) {
                instrument = seqChannel->instrument;
            } else {
                instrument = layer->instrument;
            }

            if (layer->portamento.mode != 0) {
                portamento = &layer->portamento;
                vel = (cmd > layer->portamentoTargetNote) ? cmd : layer->portamentoTargetNote;

                if (instrument != NULL) {
                    sound = Audio_InstrumentGetAudioBankSound(instrument, vel);
                    sameSound = (layer->sound == sound);
                    layer->sound = sound;
                    tuning = sound->tuning;
                } else {
                    layer->sound = NULL;
                    tuning = 1.0f;
                    if (instOrWave >= 0xC0) {
                        layer->sound = &gAudioContext.gSynthesisReverbs[instOrWave - 0xC0].sound;
                    }
                }

                temp_f2 = gNoteFrequencies[cmd2] * tuning;
                temp_f14 = gNoteFrequencies[layer->portamentoTargetNote] * tuning;

                switch (PORTAMENTO_MODE(*portamento)) {
                    case PORTAMENTO_MODE_1:
                    case PORTAMENTO_MODE_3:
                    case PORTAMENTO_MODE_5:
                        freqScale2 = temp_f2;
                        freqScale = temp_f14;
                        break;
                    case PORTAMENTO_MODE_2:
                    case PORTAMENTO_MODE_4:
                        freqScale = temp_f2;
                        freqScale2 = temp_f14;
                        break;
                    default:
                        freqScale = temp_f2;
                        freqScale2 = temp_f2;
                        break;
                }

                portamento->extent = (freqScale2 / freqScale) - 1.0f;

                if (PORTAMENTO_IS_SPECIAL(*portamento)) {
                    speed = seqPlayer->tempo * 0x8000 / gAudioContext.gTempoInternalToExternal;
                    if (layer->delay != 0) {
                        speed = speed * 0x100 / (layer->delay * layer->portamentoTime);
                    }
                } else {
                    speed = 0x20000 / (layer->portamentoTime * gAudioContext.gAudioBufferParameters.updatesPerFrame);
                }

                if (speed >= 0x7FFF) {
                    speed = 0x7FFF;
                } else if (speed < 1) {
                    speed = 1;
                }

                portamento->speed = speed;
                portamento->cur = 0;
                layer->freqScale = freqScale;
                if (PORTAMENTO_MODE(*portamento) == PORTAMENTO_MODE_5) {
                    layer->portamentoTargetNote = cmd;
                }
                break;
            }

            if (instrument != NULL) {
                sound = Audio_InstrumentGetAudioBankSound(instrument, cmd);
                sameSound = (sound == layer->sound);
                layer->sound = sound;
                layer->freqScale = gNoteFrequencies[cmd2] * sound->tuning;
            } else {
                layer->sound = NULL;
                layer->freqScale = gNoteFrequencies[cmd2];
                if (instOrWave >= 0xC0) {
                    layer->sound = &gAudioContext.gSynthesisReverbs[instOrWave - 0xC0].sound;
                }
            }
            break;
    }

    layer->delay2 = layer->delay;
    layer->freqScale *= layer->unk_34;
    if (layer->delay == 0) {
        if (layer->sound != NULL) {
            time = (f32)layer->sound->sample->loop->end;
        } else {
            time = 0.0f;
        }
        time *= seqPlayer->tempo;
        time *= gAudioContext.unk_2870;
        time /= layer->freqScale;
        if (time > 32766.0f) {
            time = 32766.0f;
        }
        layer->duration = 0;
        layer->delay = (u16)(s32)time + 1;
        if (layer->portamento.mode != 0) {
            // (It's a bit unclear if 'portamento' has actually always been
            // set when this is reached...)
            if (PORTAMENTO_IS_SPECIAL(*portamento)) {
                s32 speed2;
                speed2 = seqPlayer->tempo * 0x8000 / gAudioContext.gTempoInternalToExternal;
                speed2 = speed2 * 0x100 / (layer->delay * layer->portamentoTime);
                if (speed2 >= 0x7FFF) {
                    speed2 = 0x7FFF;
                } else if (speed2 < 1) {
                    speed2 = 1;
                }
                portamento->speed = speed2;
            }
        }
    }
    return sameSound;
}

s32 func_800EAAE0(SequenceChannelLayer* layer, s32 arg1) {
    M64ScriptState* state;
    u16 playPercentage;
    s32 velocity;
    SequenceChannel* seqChannel;
    SequencePlayer* seqPlayer;
    s32 intDelta;
    f32 floatDelta;

    state = &layer->scriptState;
    seqChannel = layer->seqChannel;
    seqPlayer = seqChannel->seqPlayer;

    if (arg1 == 0xC0) {
        layer->delay = Audio_M64ReadCompressedU16(state);
        layer->stopSomething = true;
        layer->bit1 = false;
        return -1;
    }

    layer->stopSomething = false;
    if (seqChannel->largeNotes == 1) {
        switch (arg1 & 0xC0) {
            case 0:
                playPercentage = Audio_M64ReadCompressedU16(state);
                velocity = *(state->pc++);
                layer->noteDuration = *(state->pc++);
                layer->playPercentage = playPercentage;
                break;

            case 0x40:
                playPercentage = Audio_M64ReadCompressedU16(state);
                velocity = *(state->pc++);
                layer->noteDuration = 0;
                layer->playPercentage = playPercentage;
                break;

            case 0x80:
                playPercentage = layer->playPercentage;
                velocity = *(state->pc++);
                layer->noteDuration = *(state->pc++);
                break;
        }

        if (velocity > 0x7F || velocity < 0) {
            velocity = 0x7F;
        }
        layer->velocitySquare = (f32)velocity * (f32)velocity / 16129.0f;
        arg1 -= (arg1 & 0xC0);
    } else {
        switch (arg1 & 0xC0) {
            case 0:
                playPercentage = Audio_M64ReadCompressedU16(state);
                layer->playPercentage = playPercentage;
                break;

            case 0x40:
                playPercentage = layer->shortNoteDefaultPlayPercentage;
                break;

            case 0x80:
                playPercentage = layer->playPercentage;
                break;
        }
        arg1 -= (arg1 & 0xC0);
    }

    if (seqChannel->velocityRandomVariance != 0) {
        floatDelta =
            layer->velocitySquare * (f32)(gAudioContext.gAudioRandom % seqChannel->velocityRandomVariance) / 100.0f;
        if ((gAudioContext.gAudioRandom & 0x8000) != 0) {
            floatDelta = -floatDelta;
        }
        layer->velocitySquare2 = layer->velocitySquare + floatDelta;
        if (layer->velocitySquare2 < 0.0f) {
            layer->velocitySquare2 = 0.0f;
        } else if (layer->velocitySquare2 > 1.0f) {
            layer->velocitySquare2 = 1.0f;
        }
    } else {
        layer->velocitySquare2 = layer->velocitySquare;
    }

    layer->delay = playPercentage;
    layer->duration = (layer->noteDuration * playPercentage) >> 8;
    if (seqChannel->durationRandomVariance != 0) {
        // @bug should probably be durationRandomVariance
        intDelta = (layer->duration * (gAudioContext.gAudioRandom % seqChannel->velocityRandomVariance)) / 100;
        if ((gAudioContext.gAudioRandom & 0x4000) != 0) {
            intDelta = -intDelta;
        }
        layer->duration += intDelta;
        if (layer->duration < 0) {
            layer->duration = 0;
        } else if (layer->duration > layer->delay) {
            layer->duration = layer->delay;
        }
    }

    if ((seqPlayer->muted && (seqChannel->muteBehavior & (0x40 | 0x10)) != 0) || seqChannel->stopSomething2) {
        layer->stopSomething = true;
        return -1;
    }
    if (seqPlayer->unk_DC != 0) {
        layer->stopSomething = true;
        return -1;
    }
    return arg1;
}

void func_800EAEF4(SequenceChannel* seqChannel, u8 arg1) {
    if ((arg1 & 0xF) != 0) {
        seqChannel->notePriority = arg1 & 0xF;
    }
    arg1 = arg1 >> 4;
    if (arg1 != 0) {
        seqChannel->someOtherPriority = arg1;
    }
}

u8 Audio_GetInstrument(SequenceChannel* seqChannel, u8 instId, Instrument** instOut, AdsrSettings* adsr) {
    Instrument* inst = Audio_GetInstrumentInner(seqChannel->bankId, instId);
    if (inst == NULL) {
        *instOut = NULL;
        return 0;
    }
    adsr->envelope = inst->envelope;
    adsr->releaseRate = inst->releaseRate;
    *instOut = inst;
    instId += 2;
    return instId;
}

void Audio_SetInstrument(SequenceChannel* seqChannel, u8 instId) {
    if (instId >= 0x80) {
        seqChannel->instOrWave = instId;
        seqChannel->instrument = NULL;
    } else if (instId == 0x7F) {
        seqChannel->instOrWave = 0;
        seqChannel->instrument = (Instrument*)1;
    } else if (instId == 0x7E) {
        seqChannel->instOrWave = 1;
        seqChannel->instrument = (Instrument*)2;
    } else if ((seqChannel->instOrWave =
                    Audio_GetInstrument(seqChannel, instId, &seqChannel->instrument, &seqChannel->adsr)) == 0) {
        seqChannel->hasInstrument = false;
        return;
    }
    seqChannel->hasInstrument = true;
}

void Audio_SequenceChannelSetVolume(SequenceChannel* seqChannel, u8 volume) {
    seqChannel->volume = (f32)(s32)volume / 127.0f;
}

#pragma GLOBAL_ASM("asm/non_matchings/code/audio_seqplayer/Audio_SequenceChannelProcessScript.s")

void Audio_SequenceChannelProcessScript(SequenceChannel* seqChannel);

#pragma GLOBAL_ASM("asm/non_matchings/code/audio_seqplayer/Audio_SequencePlayerProcessSequence.s")

void Audio_SequencePlayerProcessSequence(SequencePlayer* seqPlayer);

void Audio_ProcessSequences(s32 arg0) {
    SequencePlayer* seqPlayer;
    u32 i;

    gAudioContext.gNoteSubEuOffset =
        (gAudioContext.gAudioBufferParameters.updatesPerFrame - arg0 - 1) * gAudioContext.gMaxSimultaneousNotes;
    for (i = 0; i < (u32)gAudioContext.gAudioBufferParameters.numSequencePlayers; i++) {
        seqPlayer = &gAudioContext.gSequencePlayers[i];
        if (seqPlayer->enabled == 1) {
            Audio_SequencePlayerProcessSequence(seqPlayer);
            Audio_SequencePlayerProcessSound(seqPlayer);
        }
    }
    Audio_ProcessNotes();
}

void Audio_ProcessSequence(SequencePlayer* seqPlayer) {
    while (seqPlayer->unk_DC > 0) {
        Audio_SequencePlayerProcessSequence(seqPlayer);
        Audio_SequencePlayerProcessSound(seqPlayer);
        seqPlayer->unk_DC--;
    }
}

void Audio_ResetSequencePlayer(SequencePlayer* seqPlayer) {
    s32 i;

    Audio_SequencePlayerDisable(seqPlayer);
    seqPlayer->unk_0b2 = false;
    seqPlayer->delay = 0;
    seqPlayer->state = 1;
    seqPlayer->fadeTimer = 0;
    seqPlayer->fadeTimerUnkEu = 0;
    seqPlayer->tempoAcc = 0;
    seqPlayer->tempo = 120 * TATUMS_PER_BEAT; // 120 BPM
    seqPlayer->unk_0C = 0;
    seqPlayer->transposition = 0;
    seqPlayer->noteAllocPolicy = 0;
    seqPlayer->shortNoteVelocityTable = gDefaultShortNoteVelocityTable;
    seqPlayer->shortNoteDurationTable = gDefaultShortNoteDurationTable;
    seqPlayer->unk_E0 = 0;
    seqPlayer->fadeVolume = 1.0f;
    seqPlayer->fadeVelocity = 0.0f;
    seqPlayer->volume = 0.0f;
    seqPlayer->muteVolumeScale = 0.5f;

    for (i = 0; i < 0x10; i++) {
        Audio_SequenceChannelInit(seqPlayer->channels[i]);
    }
}

void func_800EC734(s32 seqPlayerIdx) {
    SequenceChannel* seqChannel;
    SequencePlayer* seqPlayer;
    s32 i, j;

    seqPlayer = &gAudioContext.gSequencePlayers[seqPlayerIdx];
    for (i = 0; i < 0x10; i++) {
        seqPlayer->channels[i] = Audio_AllocZeroed(&gAudioContext.gNotesAndBuffersPool, sizeof(SequenceChannel));
        if (seqPlayer->channels[i] == NULL) {
            seqPlayer->channels[i] = &gAudioContext.gSequenceChannelNone;
        } else {
            seqChannel = seqPlayer->channels[i];
            seqChannel->seqPlayer = seqPlayer;
            seqChannel->enabled = false;
            for (j = 0; j < 4; j++) {
                seqChannel->layers[j] = NULL;
            }
        }
        Audio_SequenceChannelInit(seqPlayer->channels[i]);
    }
}

void Audio_InitSequencePlayer(SequencePlayer* seqPlayer) {
    s32 i, j;

    for (i = 0; i < 0x10; i++) {
        seqPlayer->channels[i] = &gAudioContext.gSequenceChannelNone;
    }

    seqPlayer->enabled = false;
    seqPlayer->muted = false;
    seqPlayer->bankDmaInProgress = false;
    seqPlayer->seqDmaInProgress = false;
    seqPlayer->unk_0b1 = false;

    for (j = 0; j < 8; j++) {
        seqPlayer->unk_158[j] = -1;
    }
    seqPlayer->muteBehavior = 0x40 | 0x20;
    seqPlayer->fadeVolumeScale = 1.0f;
    seqPlayer->unk_34 = 1.0f;
    Audio_InitNoteLists(&seqPlayer->notePool);
    Audio_ResetSequencePlayer(seqPlayer);
}

void Audio_InitSequencePlayers(void) {
    s32 i;

    Audio_InitLayerFreelist();
    for (i = 0; i < 64; i++) {
        gAudioContext.gSequenceLayers[i].seqChannel = NULL;
        gAudioContext.gSequenceLayers[i].enabled = false;
    }

    for (i = 0; i < 4; i++) {
        Audio_InitSequencePlayer(&gAudioContext.gSequencePlayers[i]);
    }
}
