summaryrefslogtreecommitdiff
path: root/arm7/lib/src/SND_channel.c
blob: 66acf606496d0d7c7e6e841483c63f49409f587d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#include "SND_channel.h"

#include "SND_lfo.h"

// TODO remove this extern once we actually know where this table is
extern u8 sSampleDataShiftTable[4];

u16 CalcDecayCoeff(int vol) {
    if (vol == 127)
        return 0xFFFF;
    else if (vol == 126)
        return 0x3C00;
    else if (vol < 50)
        return (u16)(vol * 2 + 1);
    else
        return (u16)(0x1E00 / (126 - vol));
}

void ExChannelSetup(struct SNDExChannel *chn, SNDExChannelCallback callback, void *callbackUserData, int priority) {
    chn->channelLLNext = NULL;
    chn->callback = callback;
    chn->callbackUserData = callbackUserData;
    chn->length = 0;
    chn->priority = (u8)priority;
    chn->volume = 127;
    chn->flags.start = FALSE;
    chn->flags.autoSweep = TRUE;
    chn->midiKey = 60;
    chn->rootMidiKey = 60;
    chn->velocity = 127;
    chn->initPan = 0;
    chn->userDecay = 0;
    chn->userDecay2 = 0;
    chn->userPitch = 0;
    chn->userPan = 0;
    chn->panRange = 127;
    chn->sweepPitch = 0;
    chn->sweepLength = 0;
    chn->sweepCounter = 0;

    SND_SetExChannelAttack(chn, 127);
    SND_SetExChannelDecay(chn, 127);
    SND_SetExChannelSustain(chn, 127);
    SND_SetExChannelRelease(chn, 127);
    SND_InitLfoParam(&chn->lfo.param);
}

void ExChannelStart(struct SNDExChannel *chn, int length) {
    chn->envAttenuation = -92544;
    chn->envStatus = 0;
    chn->length = length;
    SND_StartLfo(&chn->lfo);
    chn->flags.start = TRUE;
    chn->flags.active = TRUE;
}

int ExChannelVolumeCmp(struct SNDExChannel *chn_a, struct SNDExChannel *chn_b) {
    int vol_a = chn_a->volume & 0xFF;
    int vol_b = chn_b->volume & 0xFF;

    vol_a <<= 4;
    vol_b <<= 4;

    vol_a >>= sSampleDataShiftTable[chn_a->volume >> 8];
    vol_b >>= sSampleDataShiftTable[chn_b->volume >> 8];

    if (vol_a != vol_b) {
        if (vol_a < vol_b)
            return 1;
        else
            return -1;
    }
    return 0;
}

int ExChannelSweepUpdate(struct SNDExChannel *chn, BOOL step) {
    s64 result;

    if (chn->sweepPitch == 0) {
        result = 0;
    } else if (chn->sweepCounter >= chn->sweepLength) {
        result = 0;
    } else {
        result = (s64)chn->sweepPitch * (chn->sweepLength - chn->sweepCounter) / chn->sweepLength;

        if (step && chn->flags.autoSweep)
            chn->sweepCounter++;
    }

    return (int)result;
}

int ExChannelLfoUpdate(struct SNDExChannel *chn, BOOL step) {
    s64 result = SND_GetLfoValue(&chn->lfo);

    if (result != 0) {
        switch (chn->lfo.param.target) {
        case SND_LFO_VOLUME:
            result *= 60;
            break;
        case SND_LFO_PITCH:
            result <<= 6;
            break;
        case SND_LFO_PAN:
            result <<= 6;
            break;
        }
        result >>= 14;
    }

    if (step) {
        SND_UpdateLfo(&chn->lfo);
    }

    return (int)result;
}