Paper Mario DX
Paper Mario (N64) modding
 
Loading...
Searching...
No Matches
voice.c File Reference

Go to the source code of this file.

Functions

void au_flush_finished_voices (AuGlobals *globals)
 (UNUSED) Immediately flush all voices which have finished playing.
 
void au_init_voices (AuGlobals *globals)
 Initializes all voices in the audio system.
 
void au_update_voices (AuGlobals *globals)
 Main envelope system update, called once per frame.
 
void au_voice_after_volume_change (AuVoice *voice)
 Applies volume update after a client-side volume change.
 
s32 au_voice_get_delta (s32 usecs)
 Converts envelope step duration from microseconds to num samples delta.
 
void au_voice_start (AuVoice *voice, EnvelopeData *envData)
 Starts a new voice with the given envelope data.
 
u8 au_voice_step (AuVoice *voice)
 Parses and executes envelope commands until a time interval is found.
 
void au_voice_set_vol_changed (AuVoice *voice)
 (UNUSED) Force recalculation of voice envelope volume during next update.
 

Function Documentation

◆ au_flush_finished_voices()

void au_flush_finished_voices ( AuGlobals * globals)

(UNUSED) Immediately flush all voices which have finished playing.

These are normally released automatically during the start of each audio frame.

Definition at line 4 of file voice.c.

4 {
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}
BSS s32 PopupMenu_SelectedIndex
AuVoice voices[24]
Definition audio.h:1075
@ AU_PRIORITY_FREE
Definition audio.h:133
void au_syn_stop_voice(u8 voiceIdx)
Definition syn_driver.c:323
#define ARRAY_COUNT(arr)
Definition macros.h:40

◆ au_init_voices()

void au_init_voices ( AuGlobals * globals)

Initializes all voices in the audio system.

Sets default values and clears previous envelope state.

Definition at line 19 of file voice.c.

19 {
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}
#define ENV_VOL_MAX
Definition audio.h:116
u8 * cmdPtr
Definition audio.h:846

Referenced by au_engine_init().

◆ au_update_voices()

void au_update_voices ( AuGlobals * globals)

Main envelope system update, called once per frame.

Progresses envelope state for all active voices.

Definition at line 37 of file voice.c.

37 {
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}
#define VOL_MULT_4(a, b, c, d)
Definition audio.h:128
@ ENV_CMD_END
Definition audio.h:257
s32 AuEnvelopeIntervals[]
Definition sfx_player.c:359
#define AUDIO_SAMPLES
Definition audio.h:16
@ AU_VOICE_SYNC_FLAG_PARAMS
Definition audio.h:149
#define AU_FRAME_USEC
Definition audio.h:27
@ 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
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
u8 au_voice_step(AuVoice *voice)
Parses and executes envelope commands until a time interval is found.
Definition voice.c:215

Referenced by au_update_clients_for_audio_frame().

◆ au_voice_after_volume_change()

void au_voice_after_volume_change ( AuVoice * voice)

Applies volume update after a client-side volume change.

This is deferred to avoid modifying envelope state mid-step.

Definition at line 176 of file voice.c.

176 {
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}

Referenced by au_update_voices().

◆ au_voice_get_delta()

s32 au_voice_get_delta ( s32 usecs)

Converts envelope step duration from microseconds to num samples delta.

Uses AU_FRAME_USEC as the base time slice, returning the number of audio samples corresponding to the provided duration.

Parameters
msecsTime duration in microseconds.
Returns
Number of samples that should pass in this interval.

Definition at line 183 of file voice.c.

183 {
184 return (usecs / AU_FRAME_USEC) * AUDIO_SAMPLES;
185}

Referenced by au_update_voices(), au_voice_after_volume_change(), and au_voice_start().

◆ au_voice_start()

void au_voice_start ( AuVoice * voice,
EnvelopeData * envData )

Starts a new voice with the given envelope data.

Initializes envelope state and prepares the press phase for playback.

Parameters
voicePointer to the voice being started.
envDataEnvelope command lists (press and release) to use.

Definition at line 187 of file voice.c.

187 {
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}
#define VOL_MULT_3(a, b, c)
Definition audio.h:125

Referenced by au_syn_begin_audio_frame().

◆ au_voice_step()

u8 au_voice_step ( AuVoice * voice)

Parses and executes envelope commands until a time interval is found.

Commands include setting multipliers, loop control, etc.

Definition at line 215 of file voice.c.

215 {
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}
@ 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

Referenced by au_update_voices(), and au_voice_start().

◆ au_voice_set_vol_changed()

void au_voice_set_vol_changed ( AuVoice * voice)

(UNUSED) Force recalculation of voice envelope volume during next update.

Definition at line 258 of file voice.c.

258 {
259 voice->envelopeFlags |= AU_VOICE_ENV_FLAG_VOL_CHANGED;
260}