Paper Mario DX
Paper Mario (N64) modding
 
Loading...
Searching...
No Matches
voice.c
Go to the documentation of this file.
1#include "audio.h"
2#include "audio/core.h"
3
5 s32 i;
6
7 for (i = 0; i < ARRAY_COUNT(globals->voices); i++) {
8 AuVoice* voice = &globals->voices[i];
9
10 if (voice->donePending) {
12 voice->donePending = FALSE;
13 voice->cmdPtr = NULL;
14 voice->priority = AU_PRIORITY_FREE;
15 }
16 }
17}
18
19void au_init_voices(AuGlobals* globals) {
20 s32 i;
21
22 for (i = 0; i < ARRAY_COUNT(globals->voices); i++) {
23 AuVoice* voice = &globals->voices[i];
24
25 voice->cmdPtr = NULL;
26 voice->unused_20 = 0;
27 voice->envDuration = 0;
28 voice->envTimeLeft = 0;
29 voice->envIntervalIndex = 0;
30 voice->unused_3C = 0;
31 voice->envelopeFlags = 0;
32 voice->isRelativeRelease = FALSE;
33 voice->envRelativeStart = ENV_VOL_MAX;
34 }
35}
36
39 s16 current;
40 s8 temp;
41 s32 i;
42
43 for (i = 0; i < ARRAY_COUNT(globals->voices); i++) {
44 voice = &globals->voices[i];
45
46 // skip inactive voices
47 if (voice->cmdPtr == NULL) {
48 continue;
49 }
50
51 if (voice->envelopeFlags & AU_VOICE_ENV_FLAG_HANDLED_VOL_CHANGE) {
52 // client volume changed on previous frame
54 continue;
55 }
56
57 if (voice->envelopeFlags & AU_VOICE_ENV_FLAG_KEY_RELEASED) {
58 // client released the key
59 voice->envelopeFlags &= ~AU_VOICE_ENV_FLAG_KEY_RELEASED;
60 voice->envelopeFlags |= AU_VOICE_ENV_FLAG_RELEASING;
61 voice->cmdPtr = (u8*)voice->envelope.cmdListRelease;
62
63 // the key can be released before the press envelope is complete
64 if (voice->envTimeLeft > AU_FRAME_USEC) {
65 // get interpolated "current" value
66 voice->envInitial += (s32) (voice->envDelta * (f32) (voice->envDuration - voice->envTimeLeft));
67 } else {
68 voice->envInitial = voice->envTarget;
69 }
70
71 // read the first interval of the release envelope
72 voice->envIntervalIndex = *voice->cmdPtr++;
73 temp = *voice->cmdPtr;
74 if (*(s8*)voice->cmdPtr++ < 0) {
75 // in this case release volumes are relative to last press volume
76 temp &= 0x7F;
77 voice->isRelativeRelease = TRUE;
78 voice->envRelativeStart = voice->envInitial;
79 }
80 voice->envTarget = temp;
81 voice->envTimeLeft = AuEnvelopeIntervals[voice->envIntervalIndex];
82 voice->envDuration = voice->envTimeLeft;
83
84 if (voice->envelopeFlags & AU_VOICE_ENV_FLAG_VOL_CHANGED) {
85 voice->envelopeFlags &= ~AU_VOICE_ENV_FLAG_VOL_CHANGED;
86 if (voice->envTimeLeft > AU_FRAME_USEC) {
87 voice->envTimeLeft -= AU_FRAME_USEC;
89 current = voice->envInitial + (s32) (voice->envDelta * (voice->envDuration - voice->envTimeLeft));
90 } else {
91 current = voice->envTarget;
92 }
93 voice->delta = AUDIO_SAMPLES;
94 } else {
95 voice->delta = au_voice_get_delta(voice->envDuration);
96 current = voice->envTarget;
97 }
98 voice->volume = VOL_MULT_4(current, voice->clientVolume, voice->envRelativeStart, voice->envScale);
99 voice->syncFlags |= AU_VOICE_SYNC_FLAG_PARAMS;
100 } else {
101 if (voice->envTimeLeft == -1) {
102 // keep current volume, this is 'sustain' phase
103 if (voice->envelopeFlags & AU_VOICE_ENV_FLAG_VOL_CHANGED) {
104 voice->envelopeFlags &= ~AU_VOICE_ENV_FLAG_VOL_CHANGED;
105 voice->volume = VOL_MULT_4(voice->envInitial, voice->clientVolume, voice->envRelativeStart, voice->envScale);
106 voice->syncFlags |= AU_VOICE_SYNC_FLAG_PARAMS;
107 }
108 } else {
109 voice->envTimeLeft -= AU_FRAME_USEC;
110 if (voice->envTimeLeft <= 0) {
111 if (*voice->cmdPtr == ENV_CMD_END) {
112 if (voice->envelopeFlags & AU_VOICE_ENV_FLAG_RELEASING) {
113 // if we reached the end after key release, stop the voice completely
114 voice->envelopeFlags = 0;
115 voice->cmdPtr = NULL;
116 voice->donePending = TRUE;
117 } else {
118 // we reached the end of press cmdlist, keep the last volume until the key is released
119 voice->envTimeLeft = -1;
120 voice->envDuration = -1;
121 voice->envIntervalIndex = ENV_TIME_300MS; // doesn't seem to affect anything
122 voice->delta = AUDIO_SAMPLES;
123 voice->envDelta = 0.0f;
124 voice->envInitial = voice->envTarget;
125 }
126 } else {
127 // get next envelope point
128 voice->envIntervalIndex = au_voice_step(voice);
129 voice->envInitial = voice->envTarget;
130 voice->envTarget = (*voice->cmdPtr++) & 0x7F;
131 voice->envTimeLeft = AuEnvelopeIntervals[voice->envIntervalIndex];
132 voice->envDuration = voice->envTimeLeft;
133 if (voice->envDuration != 0) {
134 voice->envDelta = ((f32) voice->envTarget - (f32) voice->envInitial) / (f32) voice->envDuration;
135 } else {
136 voice->envDelta = 0.0f;
137 }
138 if (voice->envelopeFlags & AU_VOICE_ENV_FLAG_VOL_CHANGED) {
139 voice->envelopeFlags &= ~AU_VOICE_ENV_FLAG_VOL_CHANGED;
140 if (voice->envTimeLeft > AU_FRAME_USEC) {
141 voice->envTimeLeft -= AU_FRAME_USEC;
143 current = voice->envInitial + (s32) (voice->envDelta * (voice->envDuration - voice->envTimeLeft));
144 } else {
145 current = voice->envTarget;
146 }
147 voice->delta = AUDIO_SAMPLES;
148 } else {
149 voice->delta = au_voice_get_delta(voice->envDuration);
150 current = voice->envTarget;
151 }
152 voice->volume = VOL_MULT_4(current, voice->clientVolume, voice->envRelativeStart, voice->envScale);
153 voice->syncFlags |= AU_VOICE_SYNC_FLAG_PARAMS;
154 }
155 } else {
156 // we are between two envelope points, do nothing, just handle client volume change
157 if (voice->envelopeFlags & AU_VOICE_ENV_FLAG_VOL_CHANGED) {
158 voice->envelopeFlags &= ~AU_VOICE_ENV_FLAG_VOL_CHANGED;
159 if (voice->envTimeLeft > AU_FRAME_USEC) {
160 voice->envTimeLeft -= AU_FRAME_USEC;
162 current = voice->envInitial + (s32) (voice->envDelta * (voice->envDuration - voice->envTimeLeft));
163 } else {
164 current = voice->envTarget;
165 }
166 voice->delta = AUDIO_SAMPLES;
167 voice->volume = VOL_MULT_4(current, voice->clientVolume, voice->envRelativeStart, voice->envScale);
168 voice->syncFlags |= AU_VOICE_SYNC_FLAG_PARAMS;
169 }
170 }
171 }
172 }
173 }
174}
175
177 voice->volume = VOL_MULT_4(voice->envTarget, voice->clientVolume, voice->envRelativeStart, voice->envScale);
178 voice->delta = au_voice_get_delta(voice->envTimeLeft);
180 voice->syncFlags |= AU_VOICE_SYNC_FLAG_PARAMS;
181}
182
186
189
190 voice->envelope.cmdListPress = envData->cmdListPress;
191 voice->cmdPtr = voice->envelope.cmdListPress;
192 voice->envelope.cmdListRelease = envData->cmdListRelease;
193 voice->envScale = ENV_VOL_MAX;
194 voice->loopStart = NULL;
195
197 voice->envelopeFlags = 0;
198 voice->envInitial = 0;
199 voice->envTarget = *voice->cmdPtr++;
200 voice->envIntervalIndex = intervalIndex;
202 voice->envTimeLeft = voice->envDuration;
203
204 voice->volume = VOL_MULT_3(voice->envTarget, voice->clientVolume, voice->envScale);
205 voice->delta = au_voice_get_delta(voice->envDuration);
206 if (voice->envDuration != 0) {
207 voice->envDelta = ((f32) voice->envTarget - (f32) voice->envInitial) / voice->envDuration;
208 } else {
209 voice->envDelta = 0.0f;
210 }
211 voice->isRelativeRelease = FALSE;
212 voice->envRelativeStart = ENV_VOL_MAX;
213}
214
216 u32 op;
217 u8 arg;
218
219 while (TRUE) {
220 if ((s8)(op = *voice->cmdPtr++) >= 0) {
221 break;
222 }
223 switch ((u8)op) {
225 arg = *voice->cmdPtr++;
226 if (arg > ENV_VOL_MAX) {
228 }
229 voice->envScale = arg;
230 break;
232 voice->envScale += (s8) *voice->cmdPtr++;
233 if (voice->envScale > ENV_VOL_MAX) {
234 voice->envScale = ENV_VOL_MAX;
235 } else if (voice->envScale < 0) {
236 voice->envScale = 0;
237 }
238 break;
240 voice->loopCounter = *voice->cmdPtr++; // 0 = infinite loop
241 voice->loopStart = voice->cmdPtr;
242 break;
243 case ENV_CMD_END_LOOP:
244 voice->cmdPtr++;
245 if (voice->loopCounter == 0 || --voice->loopCounter != 0) {
246 voice->cmdPtr = voice->loopStart;
247 }
248 break;
249 default:
250 // unknown command, skip argument
251 voice->cmdPtr++;
252 break;
253 }
254 }
255 return op;
256}
257
BSS s32 PopupMenu_SelectedIndex
AuVoice voices[24]
Definition audio.h:1075
#define VOL_MULT_4(a, b, c, d)
Definition audio.h:128
@ ENV_CMD_END
Definition audio.h:257
@ ENV_CMD_ADD_SCALE
Definition audio.h:255
@ ENV_CMD_SET_SCALE
Definition audio.h:256
@ ENV_CMD_START_LOOP
Definition audio.h:254
@ ENV_CMD_END_LOOP
Definition audio.h:253
s32 AuEnvelopeIntervals[]
Definition sfx_player.c:359
#define VOL_MULT_3(a, b, c)
Definition audio.h:125
#define AUDIO_SAMPLES
Definition audio.h:16
@ AU_VOICE_SYNC_FLAG_PARAMS
Definition audio.h:149
#define ENV_VOL_MAX
Definition audio.h:116
#define AU_FRAME_USEC
Definition audio.h:27
@ AU_PRIORITY_FREE
Definition audio.h:133
@ AU_VOICE_ENV_FLAG_KEY_RELEASED
Definition audio.h:143
@ AU_VOICE_ENV_FLAG_RELEASING
Definition audio.h:141
@ AU_VOICE_ENV_FLAG_VOL_CHANGED
Definition audio.h:144
@ AU_VOICE_ENV_FLAG_HANDLED_VOL_CHANGE
Definition audio.h:142
@ ENV_TIME_300MS
Definition audio.h:320
u8 * cmdPtr
Definition audio.h:846
void au_syn_stop_voice(u8 voiceIdx)
Definition syn_driver.c:323
#define ARRAY_COUNT(arr)
Definition macros.h:40
void au_init_voices(AuGlobals *globals)
Initializes all voices in the audio system.
Definition voice.c:19
void au_voice_after_volume_change(AuVoice *voice)
Applies volume update after a client-side volume change.
Definition voice.c:176
s32 au_voice_get_delta(s32 usecs)
Converts envelope step duration from microseconds to num samples delta.
Definition voice.c:183
void au_voice_set_vol_changed(AuVoice *voice)
(UNUSED) Force recalculation of voice envelope volume during next update.
Definition voice.c:258
void au_update_voices(AuGlobals *globals)
Main envelope system update, called once per frame.
Definition voice.c:37
void au_voice_start(AuVoice *voice, EnvelopeData *envData)
Starts a new voice with the given envelope data.
Definition voice.c:187
void au_flush_finished_voices(AuGlobals *globals)
(UNUSED) Immediately flush all voices which have finished playing.
Definition voice.c:4
u8 au_voice_step(AuVoice *voice)
Parses and executes envelope commands until a time interval is found.
Definition voice.c:215