2019-08-25 00:46:40 -04:00
|
|
|
#include <ultra64.h>
|
|
|
|
|
2020-03-01 22:42:52 -05:00
|
|
|
#include "heap.h"
|
2019-08-25 00:46:40 -04:00
|
|
|
#include "data.h"
|
|
|
|
#include "load.h"
|
|
|
|
#include "seqplayer.h"
|
|
|
|
#include "playback.h"
|
|
|
|
#include "synthesis.h"
|
|
|
|
#include "effects.h"
|
2020-02-03 00:51:26 -05:00
|
|
|
#include "external.h"
|
|
|
|
|
|
|
|
#ifdef VERSION_EU
|
|
|
|
void note_set_vel_pan_reverb(struct Note *note, f32 velocity, u8 pan, u8 reverb) {
|
|
|
|
struct NoteSubEu *sub = ¬e->noteSubEu;
|
|
|
|
f32 volRight, volLeft;
|
|
|
|
u16 unkMask = ~0x80;
|
|
|
|
|
|
|
|
pan &= unkMask;
|
|
|
|
if (sub->stereoHeadsetEffects && gSoundMode == SOUND_MODE_HEADSET) {
|
|
|
|
s32 smallPanIndex = pan >> 3;
|
|
|
|
if (smallPanIndex > 0xf) {
|
|
|
|
smallPanIndex = 0xf;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub->headsetPanLeft = gHeadsetPanQuantization[smallPanIndex];
|
|
|
|
sub->headsetPanRight = gHeadsetPanQuantization[15 - smallPanIndex];
|
|
|
|
sub->stereoStrongRight = FALSE;
|
|
|
|
sub->stereoStrongLeft = FALSE;
|
|
|
|
sub->usesHeadsetPanEffects = TRUE;
|
|
|
|
|
|
|
|
volLeft = gHeadsetPanVolume[pan];
|
|
|
|
volRight = gHeadsetPanVolume[127 - pan];
|
|
|
|
} else if (sub->stereoHeadsetEffects && gSoundMode == SOUND_MODE_STEREO) {
|
|
|
|
u8 strongLeft = FALSE;
|
|
|
|
u8 strongRight = FALSE;
|
|
|
|
sub->headsetPanLeft = 0;
|
|
|
|
sub->headsetPanRight = 0;
|
|
|
|
sub->usesHeadsetPanEffects = FALSE;
|
|
|
|
|
|
|
|
volLeft = gStereoPanVolume[pan];
|
|
|
|
volRight = gStereoPanVolume[127 - pan];
|
|
|
|
if (pan < 0x20) {
|
|
|
|
strongLeft = TRUE;
|
|
|
|
} else if (pan > 0x60) {
|
|
|
|
strongRight = TRUE;
|
|
|
|
}
|
2019-08-25 00:46:40 -04:00
|
|
|
|
2020-02-03 00:51:26 -05:00
|
|
|
sub->stereoStrongRight = strongRight;
|
|
|
|
sub->stereoStrongLeft = strongLeft;
|
|
|
|
} else if (gSoundMode == SOUND_MODE_MONO) {
|
|
|
|
volLeft = 0.707f;
|
|
|
|
volRight = 0.707f;
|
|
|
|
} else {
|
|
|
|
volLeft = gDefaultPanVolume[pan];
|
|
|
|
volRight = gDefaultPanVolume[127 - pan];
|
|
|
|
}
|
|
|
|
|
2020-09-20 11:15:47 -04:00
|
|
|
if (velocity < 0.0f) {
|
|
|
|
stubbed_printf("Audio: setvol: volume minus %f\n", velocity);
|
|
|
|
velocity = 0.0f;
|
|
|
|
}
|
|
|
|
if (velocity > 32767.f) {
|
|
|
|
stubbed_printf("Audio: setvol: volume overflow %f\n", velocity);
|
|
|
|
velocity = 32767.f;
|
|
|
|
}
|
2020-02-03 00:51:26 -05:00
|
|
|
|
|
|
|
sub->targetVolLeft = ((s32) (velocity * volLeft) & 0xffff) >> 5;
|
|
|
|
sub->targetVolRight = ((s32) (velocity * volRight) & 0xffff) >> 5;
|
|
|
|
|
|
|
|
if (sub->reverbVol != reverb) {
|
|
|
|
sub->reverbVol = reverb;
|
|
|
|
sub->envMixerNeedsInit = TRUE;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sub->needsInit) {
|
|
|
|
sub->envMixerNeedsInit = TRUE;
|
|
|
|
} else {
|
|
|
|
sub->envMixerNeedsInit = FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void note_set_resampling_rate(struct Note *note, f32 resamplingRateInput) {
|
|
|
|
f32 resamplingRate = 0.0f;
|
|
|
|
struct NoteSubEu *tempSub = ¬e->noteSubEu;
|
|
|
|
|
|
|
|
if (resamplingRateInput < 0.0f) {
|
2020-09-20 11:15:47 -04:00
|
|
|
stubbed_printf("Audio: setpitch: pitch minus %f\n", resamplingRateInput);
|
2020-02-03 00:51:26 -05:00
|
|
|
resamplingRateInput = 0.0f;
|
|
|
|
}
|
|
|
|
if (resamplingRateInput < 2.0f) {
|
|
|
|
tempSub->hasTwoAdpcmParts = 0;
|
|
|
|
|
|
|
|
if (1.99996f < resamplingRateInput) {
|
|
|
|
resamplingRate = 1.99996f;
|
|
|
|
} else {
|
|
|
|
resamplingRate = resamplingRateInput;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
tempSub->hasTwoAdpcmParts = 1;
|
|
|
|
if (3.99992f < resamplingRateInput) {
|
|
|
|
resamplingRate = 1.99996f;
|
|
|
|
} else {
|
|
|
|
resamplingRate = resamplingRateInput * 0.5f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
note->noteSubEu.resamplingRateFixedPoint = (s32) (resamplingRate * 32768.0f);
|
|
|
|
}
|
|
|
|
|
2020-03-01 22:42:52 -05:00
|
|
|
struct AudioBankSound *instrument_get_audio_bank_sound(struct Instrument *instrument, s32 semitone) {
|
2020-02-03 00:51:26 -05:00
|
|
|
struct AudioBankSound *sound;
|
|
|
|
if (semitone < instrument->normalRangeLo) {
|
|
|
|
sound = &instrument->lowNotesSound;
|
|
|
|
} else if (semitone <= instrument->normalRangeHi) {
|
|
|
|
sound = &instrument->normalNotesSound;
|
|
|
|
} else {
|
|
|
|
sound = &instrument->highNotesSound;
|
|
|
|
}
|
|
|
|
return sound;
|
|
|
|
}
|
|
|
|
|
2020-03-01 22:42:52 -05:00
|
|
|
struct Instrument *get_instrument_inner(s32 bankId, s32 instId) {
|
2020-02-03 00:51:26 -05:00
|
|
|
struct Instrument *inst;
|
|
|
|
|
|
|
|
if (IS_BANK_LOAD_COMPLETE(bankId) == FALSE) {
|
2020-09-20 11:15:47 -04:00
|
|
|
stubbed_printf("Audio: voiceman: No bank error %d\n", bankId);
|
2020-02-03 00:51:26 -05:00
|
|
|
gAudioErrorFlags = bankId + 0x10000000;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (instId >= gCtlEntries[bankId].numInstruments) {
|
2020-09-20 11:15:47 -04:00
|
|
|
stubbed_printf("Audio: voiceman: progNo. overflow %d,%d\n",
|
|
|
|
instId, gCtlEntries[bankId].numInstruments);
|
2020-02-03 00:51:26 -05:00
|
|
|
gAudioErrorFlags = ((bankId << 8) + instId) + 0x3000000;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
inst = gCtlEntries[bankId].instruments[instId];
|
|
|
|
if (inst == NULL) {
|
2020-09-20 11:15:47 -04:00
|
|
|
stubbed_printf("Audio: voiceman: progNo. undefined %d,%d\n", bankId, instId);
|
2020-02-03 00:51:26 -05:00
|
|
|
gAudioErrorFlags = ((bankId << 8) + instId) + 0x1000000;
|
|
|
|
return inst;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (((uintptr_t) gBankLoadedPool.persistent.pool.start <= (uintptr_t) inst
|
|
|
|
&& (uintptr_t) inst <= (uintptr_t)(gBankLoadedPool.persistent.pool.start
|
|
|
|
+ gBankLoadedPool.persistent.pool.size))
|
|
|
|
|| ((uintptr_t) gBankLoadedPool.temporary.pool.start <= (uintptr_t) inst
|
|
|
|
&& (uintptr_t) inst <= (uintptr_t)(gBankLoadedPool.temporary.pool.start
|
|
|
|
+ gBankLoadedPool.temporary.pool.size))) {
|
|
|
|
return inst;
|
|
|
|
}
|
|
|
|
|
2020-09-20 11:15:47 -04:00
|
|
|
stubbed_printf("Audio: voiceman: BAD Voicepointer %x,%d,%d\n", inst, bankId, instId);
|
2020-02-03 00:51:26 -05:00
|
|
|
gAudioErrorFlags = ((bankId << 8) + instId) + 0x2000000;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-03-01 22:42:52 -05:00
|
|
|
struct Drum *get_drum(s32 bankId, s32 drumId) {
|
2020-02-03 00:51:26 -05:00
|
|
|
struct Drum *drum;
|
|
|
|
if (drumId >= gCtlEntries[bankId].numDrums) {
|
2020-09-20 11:15:47 -04:00
|
|
|
stubbed_printf("Audio: voiceman: Percussion Overflow %d,%d\n",
|
|
|
|
drumId, gCtlEntries[bankId].numDrums);
|
2020-02-03 00:51:26 -05:00
|
|
|
gAudioErrorFlags = ((bankId << 8) + drumId) + 0x4000000;
|
2020-09-20 11:15:47 -04:00
|
|
|
return NULL;
|
2020-02-03 00:51:26 -05:00
|
|
|
}
|
2020-06-02 12:44:34 -04:00
|
|
|
#ifndef NO_SEGMENTED_MEMORY
|
2020-02-03 00:51:26 -05:00
|
|
|
if ((uintptr_t) gCtlEntries[bankId].drums < 0x80000000U) {
|
2020-09-20 11:15:47 -04:00
|
|
|
stubbed_printf("Percussion Pointer Error\n");
|
|
|
|
return NULL;
|
2020-02-03 00:51:26 -05:00
|
|
|
}
|
2020-06-02 12:44:34 -04:00
|
|
|
#endif
|
2020-02-03 00:51:26 -05:00
|
|
|
drum = gCtlEntries[bankId].drums[drumId];
|
|
|
|
if (drum == NULL) {
|
2020-09-20 11:15:47 -04:00
|
|
|
stubbed_printf("Audio: voiceman: Percpointer NULL %d,%d\n", bankId, drumId);
|
2020-02-03 00:51:26 -05:00
|
|
|
gAudioErrorFlags = ((bankId << 8) + drumId) + 0x5000000;
|
|
|
|
}
|
|
|
|
return drum;
|
|
|
|
}
|
|
|
|
#endif // VERSION_EU
|
|
|
|
|
|
|
|
#ifdef VERSION_EU
|
|
|
|
void note_init_for_layer(struct Note *note, struct SequenceChannelLayer *seqLayer);
|
|
|
|
#else
|
2019-08-25 00:46:40 -04:00
|
|
|
s32 note_init_for_layer(struct Note *note, struct SequenceChannelLayer *seqLayer);
|
2020-02-03 00:51:26 -05:00
|
|
|
#endif
|
2019-08-25 00:46:40 -04:00
|
|
|
|
2019-12-01 21:52:53 -05:00
|
|
|
void note_init(struct Note *note) {
|
2019-08-25 00:46:40 -04:00
|
|
|
if (note->parentLayer->adsr.releaseRate == 0) {
|
|
|
|
adsr_init(¬e->adsr, note->parentLayer->seqChannel->adsr.envelope, ¬e->adsrVolScale);
|
|
|
|
} else {
|
|
|
|
adsr_init(¬e->adsr, note->parentLayer->adsr.envelope, ¬e->adsrVolScale);
|
|
|
|
}
|
|
|
|
note->adsr.state = ADSR_STATE_INITIAL;
|
2020-02-03 00:51:26 -05:00
|
|
|
#ifdef VERSION_EU
|
|
|
|
note->noteSubEu = gDefaultNoteSub;
|
|
|
|
#else
|
2019-08-25 00:46:40 -04:00
|
|
|
note_init_volume(note);
|
|
|
|
note_enable(note);
|
2020-02-03 00:51:26 -05:00
|
|
|
#endif
|
2019-08-25 00:46:40 -04:00
|
|
|
}
|
|
|
|
|
2020-02-03 00:51:26 -05:00
|
|
|
#ifdef VERSION_EU
|
|
|
|
#define note_disable2 note_disable
|
|
|
|
void note_disable(struct Note *note) {
|
|
|
|
if (note->noteSubEu.needsInit == TRUE) {
|
|
|
|
note->noteSubEu.needsInit = FALSE;
|
|
|
|
} else {
|
|
|
|
note_set_vel_pan_reverb(note, 0, 0x40, 0);
|
|
|
|
}
|
|
|
|
note->priority = NOTE_PRIORITY_DISABLED;
|
|
|
|
note->parentLayer = NO_LAYER;
|
|
|
|
note->prevParentLayer = NO_LAYER;
|
|
|
|
note->noteSubEu.enabled = FALSE;
|
|
|
|
note->noteSubEu.finished = FALSE;
|
|
|
|
}
|
|
|
|
#else
|
2019-08-25 00:46:40 -04:00
|
|
|
void note_disable2(struct Note *note) {
|
|
|
|
note_disable(note);
|
|
|
|
}
|
2020-02-03 00:51:26 -05:00
|
|
|
#endif // VERSION_EU
|
2019-08-25 00:46:40 -04:00
|
|
|
|
2019-12-01 21:52:53 -05:00
|
|
|
void process_notes(void) {
|
2019-08-25 00:46:40 -04:00
|
|
|
f32 scale;
|
|
|
|
f32 frequency;
|
2020-02-03 00:51:26 -05:00
|
|
|
#ifndef VERSION_EU
|
2019-08-25 00:46:40 -04:00
|
|
|
u8 reverb;
|
2020-02-03 00:51:26 -05:00
|
|
|
#endif
|
2019-08-25 00:46:40 -04:00
|
|
|
f32 velocity;
|
2020-02-03 00:51:26 -05:00
|
|
|
#ifndef VERSION_EU
|
2019-08-25 00:46:40 -04:00
|
|
|
f32 pan;
|
|
|
|
f32 cap;
|
2020-02-03 00:51:26 -05:00
|
|
|
#endif
|
2019-08-25 00:46:40 -04:00
|
|
|
struct Note *note;
|
2020-02-03 00:51:26 -05:00
|
|
|
#ifdef VERSION_EU
|
|
|
|
struct NotePlaybackState *playbackState;
|
|
|
|
struct NoteSubEu *noteSubEu;
|
|
|
|
UNUSED u8 pad[12];
|
|
|
|
u8 reverb;
|
|
|
|
UNUSED u8 pad3;
|
|
|
|
u8 pan;
|
2020-03-01 22:42:52 -05:00
|
|
|
u8 bookOffset;
|
2020-02-03 00:51:26 -05:00
|
|
|
#endif
|
2019-08-25 00:46:40 -04:00
|
|
|
struct NoteAttributes *attributes;
|
2020-02-03 00:51:26 -05:00
|
|
|
#ifndef VERSION_EU
|
2019-08-25 00:46:40 -04:00
|
|
|
struct AudioListItem *it;
|
2020-02-03 00:51:26 -05:00
|
|
|
#endif
|
2019-08-25 00:46:40 -04:00
|
|
|
s32 i;
|
|
|
|
|
2020-09-20 11:15:47 -04:00
|
|
|
// Macro versions of audio_list_push_front and audio_list_remove.
|
|
|
|
// Should ideally be changed to use copt.
|
2019-08-25 00:46:40 -04:00
|
|
|
#define PREPEND(item, head_arg) \
|
|
|
|
((it = (item), it->prev != NULL) \
|
|
|
|
? it \
|
|
|
|
: (it->prev = (head_arg), it->next = (head_arg)->next, (head_arg)->next->prev = it, \
|
|
|
|
(head_arg)->next = it, (head_arg)->u.count++, it->pool = (head_arg)->pool, it))
|
|
|
|
#define POP(item) \
|
|
|
|
((it = (item), it->prev == NULL) \
|
|
|
|
? it \
|
|
|
|
: (it->prev->next = it->next, it->next->prev = it->prev, it->prev = NULL, it))
|
|
|
|
|
|
|
|
for (i = 0; i < gMaxSimultaneousNotes; i++) {
|
|
|
|
note = &gNotes[i];
|
2020-02-03 00:51:26 -05:00
|
|
|
#ifdef VERSION_EU
|
|
|
|
playbackState = (struct NotePlaybackState *) ¬e->priority;
|
|
|
|
if (note->parentLayer != NO_LAYER) {
|
2020-06-02 12:44:34 -04:00
|
|
|
#ifndef NO_SEGMENTED_MEMORY
|
2020-02-03 00:51:26 -05:00
|
|
|
if ((uintptr_t) playbackState->parentLayer < 0x7fffffffU) {
|
|
|
|
continue;
|
|
|
|
}
|
2020-06-02 12:44:34 -04:00
|
|
|
#endif
|
2020-02-03 00:51:26 -05:00
|
|
|
if (!playbackState->parentLayer->enabled && playbackState->priority >= NOTE_PRIORITY_MIN) {
|
|
|
|
goto c;
|
|
|
|
} else if (playbackState->parentLayer->seqChannel->seqPlayer == NULL) {
|
2020-09-20 11:15:47 -04:00
|
|
|
eu_stubbed_printf_0("CAUTION:SUB IS SEPARATED FROM GROUP");
|
2020-02-03 00:51:26 -05:00
|
|
|
sequence_channel_disable(playbackState->parentLayer->seqChannel);
|
|
|
|
playbackState->priority = NOTE_PRIORITY_STOPPING;
|
|
|
|
continue;
|
|
|
|
} else if (playbackState->parentLayer->seqChannel->seqPlayer->muted) {
|
|
|
|
if ((playbackState->parentLayer->seqChannel->muteBehavior
|
|
|
|
& (MUTE_BEHAVIOR_STOP_SCRIPT | MUTE_BEHAVIOR_STOP_NOTES))) {
|
|
|
|
goto c;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
goto d;
|
|
|
|
if (1) {
|
|
|
|
c:
|
|
|
|
seq_channel_layer_note_release(playbackState->parentLayer);
|
|
|
|
audio_list_remove(¬e->listItem);
|
|
|
|
audio_list_push_front(¬e->listItem.pool->decaying, ¬e->listItem);
|
|
|
|
playbackState->priority = NOTE_PRIORITY_STOPPING;
|
|
|
|
}
|
|
|
|
} else if (playbackState->priority >= NOTE_PRIORITY_MIN) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
d:
|
|
|
|
if (playbackState->priority != NOTE_PRIORITY_DISABLED) {
|
|
|
|
noteSubEu = ¬e->noteSubEu;
|
|
|
|
if (playbackState->priority == NOTE_PRIORITY_STOPPING || noteSubEu->finished) {
|
|
|
|
if (playbackState->adsr.state == ADSR_STATE_DISABLED || noteSubEu->finished) {
|
|
|
|
if (playbackState->wantedParentLayer != NO_LAYER) {
|
|
|
|
note_disable(note);
|
|
|
|
if (playbackState->wantedParentLayer->seqChannel != NULL) {
|
|
|
|
note_init_for_layer(note, playbackState->wantedParentLayer);
|
|
|
|
note_vibrato_init(note);
|
|
|
|
audio_list_remove(¬e->listItem);
|
|
|
|
audio_list_push_back(¬e->listItem.pool->active, ¬e->listItem);
|
|
|
|
playbackState->wantedParentLayer = NO_LAYER;
|
|
|
|
// don't skip
|
|
|
|
} else {
|
2020-09-20 11:15:47 -04:00
|
|
|
eu_stubbed_printf_0("Error:Wait Track disappear\n");
|
2020-02-03 00:51:26 -05:00
|
|
|
note_disable(note);
|
|
|
|
audio_list_remove(¬e->listItem);
|
|
|
|
audio_list_push_back(¬e->listItem.pool->disabled, ¬e->listItem);
|
|
|
|
playbackState->wantedParentLayer = NO_LAYER;
|
|
|
|
goto skip;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
note_disable(note);
|
|
|
|
audio_list_remove(¬e->listItem);
|
|
|
|
audio_list_push_back(¬e->listItem.pool->disabled, ¬e->listItem);
|
|
|
|
goto skip;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (1) {
|
|
|
|
}
|
|
|
|
} else if (playbackState->adsr.state == ADSR_STATE_DISABLED) {
|
|
|
|
note_disable(note);
|
|
|
|
audio_list_remove(¬e->listItem);
|
|
|
|
audio_list_push_back(¬e->listItem.pool->disabled, ¬e->listItem);
|
|
|
|
goto skip;
|
|
|
|
}
|
|
|
|
|
|
|
|
scale = adsr_update(&playbackState->adsr);
|
|
|
|
note_vibrato_update(note);
|
|
|
|
attributes = &playbackState->attributes;
|
|
|
|
if (playbackState->priority == NOTE_PRIORITY_STOPPING) {
|
|
|
|
frequency = attributes->freqScale;
|
|
|
|
velocity = attributes->velocity;
|
|
|
|
pan = attributes->pan;
|
|
|
|
reverb = attributes->reverb;
|
|
|
|
if (1) {
|
|
|
|
}
|
2020-03-01 22:42:52 -05:00
|
|
|
bookOffset = noteSubEu->bookOffset;
|
2020-02-03 00:51:26 -05:00
|
|
|
} else {
|
|
|
|
frequency = playbackState->parentLayer->noteFreqScale;
|
|
|
|
velocity = playbackState->parentLayer->noteVelocity;
|
|
|
|
pan = playbackState->parentLayer->notePan;
|
|
|
|
reverb = playbackState->parentLayer->seqChannel->reverb;
|
2020-03-01 22:42:52 -05:00
|
|
|
bookOffset = playbackState->parentLayer->seqChannel->bookOffset & 0x7;
|
2020-02-03 00:51:26 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
frequency *= playbackState->vibratoFreqScale * playbackState->portamentoFreqScale;
|
|
|
|
frequency *= gAudioBufferParameters.resampleRate;
|
|
|
|
velocity = velocity * scale * scale;
|
|
|
|
note_set_resampling_rate(note, frequency);
|
|
|
|
note_set_vel_pan_reverb(note, velocity, pan, reverb);
|
2020-03-01 22:42:52 -05:00
|
|
|
noteSubEu->bookOffset = bookOffset;
|
2020-02-03 00:51:26 -05:00
|
|
|
skip:;
|
|
|
|
}
|
|
|
|
#else
|
2019-08-25 00:46:40 -04:00
|
|
|
if (note->priority != NOTE_PRIORITY_DISABLED) {
|
2019-11-03 14:36:27 -05:00
|
|
|
if (note->priority == NOTE_PRIORITY_STOPPING || note->finished) {
|
|
|
|
if (note->adsrVolScale == 0 || note->finished) {
|
2019-08-25 00:46:40 -04:00
|
|
|
if (note->wantedParentLayer != NO_LAYER) {
|
|
|
|
note_disable2(note);
|
|
|
|
if (note->wantedParentLayer->seqChannel != NULL) {
|
|
|
|
if (note_init_for_layer(note, note->wantedParentLayer) == TRUE) {
|
|
|
|
note_disable2(note);
|
|
|
|
POP(¬e->listItem);
|
|
|
|
PREPEND(¬e->listItem, &gNoteFreeLists.disabled);
|
|
|
|
} else {
|
|
|
|
note_vibrato_init(note);
|
|
|
|
audio_list_push_back(¬e->listItem.pool->active,
|
|
|
|
POP(¬e->listItem));
|
|
|
|
note->wantedParentLayer = NO_LAYER;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
note_disable2(note);
|
|
|
|
audio_list_push_back(¬e->listItem.pool->disabled, POP(¬e->listItem));
|
|
|
|
note->wantedParentLayer = NO_LAYER;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
note_disable2(note);
|
|
|
|
audio_list_push_back(¬e->listItem.pool->disabled, POP(¬e->listItem));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (note->adsr.state == ADSR_STATE_DISABLED) {
|
|
|
|
note_disable2(note);
|
|
|
|
audio_list_push_back(¬e->listItem.pool->disabled, POP(¬e->listItem));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
adsr_update(¬e->adsr);
|
|
|
|
note_vibrato_update(note);
|
|
|
|
attributes = ¬e->attributes;
|
|
|
|
if (note->priority == NOTE_PRIORITY_STOPPING) {
|
|
|
|
frequency = attributes->freqScale;
|
|
|
|
velocity = attributes->velocity;
|
|
|
|
pan = attributes->pan;
|
|
|
|
reverb = attributes->reverb;
|
|
|
|
} else {
|
|
|
|
frequency = note->parentLayer->noteFreqScale;
|
|
|
|
velocity = note->parentLayer->noteVelocity;
|
|
|
|
pan = note->parentLayer->notePan;
|
|
|
|
reverb = note->parentLayer->seqChannel->reverb;
|
|
|
|
}
|
|
|
|
|
|
|
|
scale = note->adsrVolScale;
|
|
|
|
frequency *= note->vibratoFreqScale * note->portamentoFreqScale;
|
|
|
|
cap = 3.99992f;
|
|
|
|
if (gAiFrequency != 32006) {
|
|
|
|
frequency *= US_FLOAT(32000.0) / (f32) gAiFrequency;
|
|
|
|
}
|
|
|
|
frequency = (frequency < cap ? frequency : cap);
|
|
|
|
scale *= 4.3498e-5f; // ~1 / 23000
|
|
|
|
velocity = velocity * scale * scale;
|
|
|
|
note_set_frequency(note, frequency);
|
|
|
|
note_set_vel_pan_reverb(note, velocity, pan, reverb);
|
|
|
|
continue;
|
|
|
|
}
|
2020-02-03 00:51:26 -05:00
|
|
|
#endif
|
2019-08-25 00:46:40 -04:00
|
|
|
}
|
|
|
|
#undef PREPEND
|
|
|
|
#undef POP
|
|
|
|
}
|
|
|
|
|
|
|
|
void seq_channel_layer_decay_release_internal(struct SequenceChannelLayer *seqLayer, s32 target) {
|
|
|
|
struct Note *note;
|
|
|
|
struct NoteAttributes *attributes;
|
|
|
|
|
|
|
|
if (seqLayer == NO_LAYER || seqLayer->note == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
note = seqLayer->note;
|
|
|
|
attributes = ¬e->attributes;
|
|
|
|
|
2020-02-03 00:51:26 -05:00
|
|
|
#ifndef VERSION_EU
|
2019-08-25 00:46:40 -04:00
|
|
|
if (seqLayer->seqChannel != NULL && seqLayer->seqChannel->noteAllocPolicy == 0) {
|
|
|
|
seqLayer->note = NULL;
|
|
|
|
}
|
2020-02-03 00:51:26 -05:00
|
|
|
#endif
|
2019-08-25 00:46:40 -04:00
|
|
|
|
|
|
|
if (note->wantedParentLayer == seqLayer) {
|
|
|
|
note->wantedParentLayer = NO_LAYER;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (note->parentLayer != seqLayer) {
|
2020-02-03 00:51:26 -05:00
|
|
|
#ifdef VERSION_EU
|
2020-09-20 11:15:47 -04:00
|
|
|
if (note->parentLayer == NO_LAYER && note->wantedParentLayer == NO_LAYER &&
|
|
|
|
note->prevParentLayer == seqLayer && target != ADSR_STATE_DECAY) {
|
|
|
|
// Just guessing that this printf goes here... it's hard to parse.
|
|
|
|
eu_stubbed_printf_0("Slow Release Batting\n");
|
2020-02-03 00:51:26 -05:00
|
|
|
note->adsr.fadeOutVel = gAudioBufferParameters.updatesPerFrameInv;
|
|
|
|
note->adsr.action |= ADSR_ACTION_RELEASE;
|
|
|
|
}
|
|
|
|
#endif
|
2019-08-25 00:46:40 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-12-01 21:52:53 -05:00
|
|
|
seqLayer->status = SOUND_LOAD_STATUS_NOT_LOADED;
|
2019-08-25 00:46:40 -04:00
|
|
|
if (note->adsr.state != ADSR_STATE_DECAY) {
|
|
|
|
attributes->freqScale = seqLayer->noteFreqScale;
|
|
|
|
attributes->velocity = seqLayer->noteVelocity;
|
|
|
|
attributes->pan = seqLayer->notePan;
|
|
|
|
if (seqLayer->seqChannel != NULL) {
|
|
|
|
attributes->reverb = seqLayer->seqChannel->reverb;
|
|
|
|
}
|
|
|
|
note->priority = NOTE_PRIORITY_STOPPING;
|
|
|
|
note->prevParentLayer = note->parentLayer;
|
|
|
|
note->parentLayer = NO_LAYER;
|
|
|
|
if (target == ADSR_STATE_RELEASE) {
|
2020-02-03 00:51:26 -05:00
|
|
|
#ifdef VERSION_EU
|
|
|
|
note->adsr.fadeOutVel = gAudioBufferParameters.updatesPerFrameInv;
|
|
|
|
#else
|
2019-08-25 00:46:40 -04:00
|
|
|
note->adsr.fadeOutVel = 0x8000 / gAudioUpdatesPerFrame;
|
2020-02-03 00:51:26 -05:00
|
|
|
#endif
|
2019-08-25 00:46:40 -04:00
|
|
|
note->adsr.action |= ADSR_ACTION_RELEASE;
|
|
|
|
} else {
|
|
|
|
note->adsr.action |= ADSR_ACTION_DECAY;
|
2020-02-03 00:51:26 -05:00
|
|
|
#ifdef VERSION_EU
|
|
|
|
if (seqLayer->adsr.releaseRate == 0) {
|
|
|
|
note->adsr.fadeOutVel = seqLayer->seqChannel->adsr.releaseRate * gAudioBufferParameters.unkUpdatesPerFrameScaled;
|
|
|
|
} else {
|
|
|
|
note->adsr.fadeOutVel = seqLayer->adsr.releaseRate * gAudioBufferParameters.unkUpdatesPerFrameScaled;
|
|
|
|
}
|
|
|
|
note->adsr.sustain = (FLOAT_CAST(seqLayer->seqChannel->adsr.sustain) * note->adsr.current) / 256.0f;
|
|
|
|
#else
|
2019-08-25 00:46:40 -04:00
|
|
|
if (seqLayer->adsr.releaseRate == 0) {
|
|
|
|
note->adsr.fadeOutVel = seqLayer->seqChannel->adsr.releaseRate * 24;
|
|
|
|
} else {
|
|
|
|
note->adsr.fadeOutVel = seqLayer->adsr.releaseRate * 24;
|
|
|
|
}
|
|
|
|
note->adsr.sustain = (note->adsr.current * seqLayer->seqChannel->adsr.sustain) / 0x10000;
|
2020-02-03 00:51:26 -05:00
|
|
|
#endif
|
2019-08-25 00:46:40 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (target == ADSR_STATE_DECAY) {
|
|
|
|
audio_list_remove(¬e->listItem);
|
|
|
|
audio_list_push_front(¬e->listItem.pool->decaying, ¬e->listItem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void seq_channel_layer_note_decay(struct SequenceChannelLayer *seqLayer) {
|
|
|
|
seq_channel_layer_decay_release_internal(seqLayer, ADSR_STATE_DECAY);
|
|
|
|
}
|
|
|
|
|
|
|
|
void seq_channel_layer_note_release(struct SequenceChannelLayer *seqLayer) {
|
|
|
|
seq_channel_layer_decay_release_internal(seqLayer, ADSR_STATE_RELEASE);
|
|
|
|
}
|
|
|
|
|
2020-02-03 00:51:26 -05:00
|
|
|
|
|
|
|
#ifdef VERSION_EU
|
|
|
|
s32 build_synthetic_wave(struct Note *note, struct SequenceChannelLayer *seqLayer, s32 waveId) {
|
|
|
|
f32 freqScale;
|
|
|
|
f32 ratio;
|
|
|
|
u8 sampleCountIndex;
|
|
|
|
|
|
|
|
if (waveId < 128) {
|
2020-09-20 11:15:47 -04:00
|
|
|
stubbed_printf("Audio:Wavemem: Bad voiceno (%d)\n", waveId);
|
2020-02-03 00:51:26 -05:00
|
|
|
waveId = 128;
|
|
|
|
}
|
|
|
|
|
|
|
|
freqScale = seqLayer->freqScale;
|
|
|
|
if (seqLayer->portamento.mode != 0 && 0.0f < seqLayer->portamento.extent) {
|
|
|
|
freqScale *= (seqLayer->portamento.extent + 1.0f);
|
|
|
|
}
|
|
|
|
if (freqScale < 1.0f) {
|
|
|
|
sampleCountIndex = 0;
|
|
|
|
ratio = 1.0465f;
|
|
|
|
} else if (freqScale < 2.0f) {
|
|
|
|
sampleCountIndex = 1;
|
|
|
|
ratio = 0.52325f;
|
|
|
|
} else if (freqScale < 4.0f) {
|
|
|
|
sampleCountIndex = 2;
|
|
|
|
ratio = 0.26263f;
|
|
|
|
} else {
|
|
|
|
sampleCountIndex = 3;
|
|
|
|
ratio = 0.13081f;
|
|
|
|
}
|
|
|
|
seqLayer->freqScale *= ratio;
|
|
|
|
note->waveId = waveId;
|
|
|
|
note->sampleCountIndex = sampleCountIndex;
|
|
|
|
|
|
|
|
note->noteSubEu.sound.samples = &gWaveSamples[waveId - 128][sampleCountIndex * 64];
|
|
|
|
|
|
|
|
return sampleCountIndex;
|
|
|
|
}
|
2020-09-20 11:15:47 -04:00
|
|
|
|
2020-02-03 00:51:26 -05:00
|
|
|
#else
|
2019-12-01 21:52:53 -05:00
|
|
|
void build_synthetic_wave(struct Note *note, struct SequenceChannelLayer *seqLayer) {
|
2019-08-25 00:46:40 -04:00
|
|
|
s32 i;
|
|
|
|
s32 j;
|
|
|
|
s32 pos;
|
|
|
|
s32 stepSize;
|
|
|
|
s32 offset;
|
|
|
|
u8 lim;
|
|
|
|
u8 origSampleCount = note->sampleCount;
|
|
|
|
|
|
|
|
if (seqLayer->freqScale < US_FLOAT(1.0)) {
|
|
|
|
note->sampleCount = 64;
|
|
|
|
seqLayer->freqScale *= US_FLOAT(1.0465);
|
|
|
|
stepSize = 1;
|
|
|
|
} else if (seqLayer->freqScale < US_FLOAT(2.0)) {
|
|
|
|
note->sampleCount = 32;
|
|
|
|
seqLayer->freqScale *= US_FLOAT(0.52325);
|
|
|
|
stepSize = 2;
|
|
|
|
} else if (seqLayer->freqScale < US_FLOAT(4.0)) {
|
|
|
|
note->sampleCount = 16;
|
|
|
|
seqLayer->freqScale *= US_FLOAT(0.26263);
|
|
|
|
stepSize = 4;
|
|
|
|
} else {
|
|
|
|
note->sampleCount = 8;
|
|
|
|
seqLayer->freqScale *= US_FLOAT(0.13081);
|
|
|
|
stepSize = 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (note->sampleCount == origSampleCount && seqLayer->seqChannel->instOrWave == note->instOrWave) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load wave sample
|
|
|
|
note->instOrWave = (u8) seqLayer->seqChannel->instOrWave;
|
|
|
|
for (i = -1, pos = 0; pos < 0x40; pos += stepSize) {
|
|
|
|
i++;
|
2019-11-03 14:36:27 -05:00
|
|
|
note->synthesisBuffers->samples[i] = gWaveSamples[seqLayer->seqChannel->instOrWave - 0x80][pos];
|
2019-08-25 00:46:40 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Repeat sample
|
|
|
|
for (offset = note->sampleCount; offset < 0x40; offset += note->sampleCount) {
|
|
|
|
lim = note->sampleCount;
|
|
|
|
if (offset < 0 || offset > 0) {
|
|
|
|
for (j = 0; j < lim; j++) {
|
2019-11-03 14:36:27 -05:00
|
|
|
note->synthesisBuffers->samples[offset + j] = note->synthesisBuffers->samples[j];
|
2019-08-25 00:46:40 -04:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (j = 0; j < lim; j++) {
|
2019-11-03 14:36:27 -05:00
|
|
|
note->synthesisBuffers->samples[offset + j] = note->synthesisBuffers->samples[j];
|
2019-08-25 00:46:40 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-03 14:36:27 -05:00
|
|
|
osWritebackDCache(note->synthesisBuffers->samples, sizeof(note->synthesisBuffers->samples));
|
2019-08-25 00:46:40 -04:00
|
|
|
}
|
2020-02-03 00:51:26 -05:00
|
|
|
#endif
|
2019-08-25 00:46:40 -04:00
|
|
|
|
2019-12-01 21:52:53 -05:00
|
|
|
void init_synthetic_wave(struct Note *note, struct SequenceChannelLayer *seqLayer) {
|
2020-02-03 00:51:26 -05:00
|
|
|
#ifdef VERSION_EU
|
|
|
|
s32 sampleCountIndex;
|
|
|
|
s32 waveSampleCountIndex;
|
2020-03-01 22:42:52 -05:00
|
|
|
s32 waveId = seqLayer->instOrWave;
|
2020-02-03 00:51:26 -05:00
|
|
|
if (waveId == 0xff) {
|
|
|
|
waveId = seqLayer->seqChannel->instOrWave;
|
|
|
|
}
|
|
|
|
sampleCountIndex = note->sampleCountIndex;
|
|
|
|
waveSampleCountIndex = build_synthetic_wave(note, seqLayer, waveId);
|
|
|
|
note->synthesisState.samplePosInt = note->synthesisState.samplePosInt * euUnknownData_8030194c[waveSampleCountIndex] / euUnknownData_8030194c[sampleCountIndex];
|
|
|
|
#else
|
2019-08-25 00:46:40 -04:00
|
|
|
s32 sampleCount = note->sampleCount;
|
2019-12-01 21:52:53 -05:00
|
|
|
build_synthetic_wave(note, seqLayer);
|
2019-08-25 00:46:40 -04:00
|
|
|
if (sampleCount != 0) {
|
2019-11-03 14:36:27 -05:00
|
|
|
note->samplePosInt *= note->sampleCount / sampleCount;
|
2019-08-25 00:46:40 -04:00
|
|
|
} else {
|
2019-11-03 14:36:27 -05:00
|
|
|
note->samplePosInt = 0;
|
2019-08-25 00:46:40 -04:00
|
|
|
}
|
2020-02-03 00:51:26 -05:00
|
|
|
#endif
|
2019-08-25 00:46:40 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void init_note_list(struct AudioListItem *list) {
|
|
|
|
list->prev = list;
|
|
|
|
list->next = list;
|
|
|
|
list->u.count = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void init_note_lists(struct NotePool *pool) {
|
|
|
|
init_note_list(&pool->disabled);
|
|
|
|
init_note_list(&pool->decaying);
|
|
|
|
init_note_list(&pool->releasing);
|
|
|
|
init_note_list(&pool->active);
|
|
|
|
pool->disabled.pool = pool;
|
|
|
|
pool->decaying.pool = pool;
|
|
|
|
pool->releasing.pool = pool;
|
|
|
|
pool->active.pool = pool;
|
|
|
|
}
|
|
|
|
|
|
|
|
void init_note_free_list(void) {
|
|
|
|
s32 i;
|
|
|
|
|
|
|
|
init_note_lists(&gNoteFreeLists);
|
|
|
|
for (i = 0; i < gMaxSimultaneousNotes; i++) {
|
|
|
|
gNotes[i].listItem.u.value = &gNotes[i];
|
|
|
|
gNotes[i].listItem.prev = NULL;
|
|
|
|
audio_list_push_back(&gNoteFreeLists.disabled, &gNotes[i].listItem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void note_pool_clear(struct NotePool *pool) {
|
|
|
|
s32 i;
|
|
|
|
struct AudioListItem *source;
|
|
|
|
struct AudioListItem *cur;
|
|
|
|
struct AudioListItem *dest;
|
2020-02-03 00:51:26 -05:00
|
|
|
UNUSED s32 j; // unused in EU
|
2019-08-25 00:46:40 -04:00
|
|
|
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
|
|
switch (i) {
|
|
|
|
case 0:
|
|
|
|
source = &pool->disabled;
|
|
|
|
dest = &gNoteFreeLists.disabled;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
source = &pool->decaying;
|
|
|
|
dest = &gNoteFreeLists.decaying;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
source = &pool->releasing;
|
|
|
|
dest = &gNoteFreeLists.releasing;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
source = &pool->active;
|
|
|
|
dest = &gNoteFreeLists.active;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-02-03 00:51:26 -05:00
|
|
|
#ifdef VERSION_EU
|
|
|
|
for (;;) {
|
|
|
|
cur = source->next;
|
2020-09-20 11:15:47 -04:00
|
|
|
if (cur == source) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (cur == NULL) {
|
|
|
|
eu_stubbed_printf_0("Audio: C-Alloc : Dealloc voice is NULL\n");
|
2020-02-03 00:51:26 -05:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
audio_list_remove(cur);
|
|
|
|
audio_list_push_back(dest, cur);
|
|
|
|
}
|
|
|
|
#else
|
2019-08-25 00:46:40 -04:00
|
|
|
j = 0;
|
|
|
|
do {
|
|
|
|
cur = source->next;
|
2019-09-01 15:50:50 -04:00
|
|
|
if (cur == source) {
|
2019-08-25 00:46:40 -04:00
|
|
|
break;
|
2019-09-01 15:50:50 -04:00
|
|
|
}
|
2019-08-25 00:46:40 -04:00
|
|
|
audio_list_remove(cur);
|
|
|
|
audio_list_push_back(dest, cur);
|
|
|
|
j++;
|
|
|
|
} while (j <= gMaxSimultaneousNotes);
|
2020-02-03 00:51:26 -05:00
|
|
|
#endif
|
2019-08-25 00:46:40 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void note_pool_fill(struct NotePool *pool, s32 count) {
|
|
|
|
s32 i;
|
|
|
|
s32 j;
|
|
|
|
struct Note *note;
|
|
|
|
struct AudioListItem *source;
|
|
|
|
struct AudioListItem *dest;
|
|
|
|
|
|
|
|
note_pool_clear(pool);
|
|
|
|
|
|
|
|
for (i = 0, j = 0; j < count; i++) {
|
2019-09-01 15:50:50 -04:00
|
|
|
if (i == 4) {
|
2020-09-20 11:15:47 -04:00
|
|
|
eu_stubbed_printf_1("Alloc Error:Dim voice-Alloc %d", count);
|
2019-08-25 00:46:40 -04:00
|
|
|
return;
|
2019-09-01 15:50:50 -04:00
|
|
|
}
|
2019-08-25 00:46:40 -04:00
|
|
|
|
|
|
|
switch (i) {
|
|
|
|
case 0:
|
|
|
|
source = &gNoteFreeLists.disabled;
|
|
|
|
dest = &pool->disabled;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
source = &gNoteFreeLists.decaying;
|
|
|
|
dest = &pool->decaying;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
source = &gNoteFreeLists.releasing;
|
|
|
|
dest = &pool->releasing;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
source = &gNoteFreeLists.active;
|
|
|
|
dest = &pool->active;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (j < count) {
|
|
|
|
note = audio_list_pop_back(source);
|
2019-09-01 15:50:50 -04:00
|
|
|
if (note == NULL) {
|
2019-08-25 00:46:40 -04:00
|
|
|
break;
|
2019-09-01 15:50:50 -04:00
|
|
|
}
|
2019-08-25 00:46:40 -04:00
|
|
|
audio_list_push_back(dest, ¬e->listItem);
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void audio_list_push_front(struct AudioListItem *list, struct AudioListItem *item) {
|
|
|
|
// add 'item' to the front of the list given by 'list', if it's not in any list
|
2020-09-20 11:15:47 -04:00
|
|
|
if (item->prev != NULL) {
|
|
|
|
eu_stubbed_printf_0("Error:Same List Add\n");
|
|
|
|
} else {
|
2019-08-25 00:46:40 -04:00
|
|
|
item->prev = list;
|
|
|
|
item->next = list->next;
|
|
|
|
list->next->prev = item;
|
|
|
|
list->next = item;
|
|
|
|
list->u.count++;
|
|
|
|
item->pool = list->pool;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void audio_list_remove(struct AudioListItem *item) {
|
|
|
|
// remove 'item' from the list it's in, if any
|
2020-09-20 11:15:47 -04:00
|
|
|
if (item->prev == NULL) {
|
|
|
|
eu_stubbed_printf_0("Already Cut\n");
|
|
|
|
} else {
|
2019-08-25 00:46:40 -04:00
|
|
|
item->prev->next = item->next;
|
|
|
|
item->next->prev = item->prev;
|
|
|
|
item->prev = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-20 11:15:47 -04:00
|
|
|
struct Note *pop_node_with_lower_prio(struct AudioListItem *list, s32 limit) {
|
2019-08-25 00:46:40 -04:00
|
|
|
struct AudioListItem *cur = list->next;
|
|
|
|
struct AudioListItem *best;
|
|
|
|
|
|
|
|
if (cur == list) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-06-02 12:44:34 -04:00
|
|
|
for (best = cur; cur != list; cur = cur->next) {
|
2019-08-25 00:46:40 -04:00
|
|
|
if (((struct Note *) best->u.value)->priority >= ((struct Note *) cur->u.value)->priority) {
|
|
|
|
best = cur;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-03 00:51:26 -05:00
|
|
|
#ifdef VERSION_EU
|
|
|
|
if (best == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (limit <= ((struct Note *) best->u.value)->priority) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
#else
|
2019-08-25 00:46:40 -04:00
|
|
|
if (limit < ((struct Note *) best->u.value)->priority) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2020-02-03 00:51:26 -05:00
|
|
|
#endif
|
2019-08-25 00:46:40 -04:00
|
|
|
|
|
|
|
audio_list_remove(best);
|
|
|
|
return best->u.value;
|
|
|
|
}
|
|
|
|
|
2020-02-03 00:51:26 -05:00
|
|
|
#if defined(VERSION_EU)
|
|
|
|
void note_init_for_layer(struct Note *note, struct SequenceChannelLayer *seqLayer) {
|
|
|
|
UNUSED s32 pad[4];
|
|
|
|
s16 instId;
|
|
|
|
struct NoteSubEu *sub = ¬e->noteSubEu;
|
|
|
|
|
|
|
|
note->prevParentLayer = NO_LAYER;
|
|
|
|
note->parentLayer = seqLayer;
|
|
|
|
note->priority = seqLayer->seqChannel->notePriority;
|
2020-03-01 22:42:52 -05:00
|
|
|
seqLayer->notePropertiesNeedInit = TRUE;
|
2020-02-03 00:51:26 -05:00
|
|
|
seqLayer->status = SOUND_LOAD_STATUS_DISCARDABLE; // "loaded"
|
|
|
|
seqLayer->note = note;
|
|
|
|
seqLayer->seqChannel->noteUnused = note;
|
|
|
|
seqLayer->seqChannel->layerUnused = seqLayer;
|
|
|
|
seqLayer->noteVelocity = 0.0f;
|
|
|
|
note_init(note);
|
2020-03-01 22:42:52 -05:00
|
|
|
instId = seqLayer->instOrWave;
|
2020-02-03 00:51:26 -05:00
|
|
|
if (instId == 0xff) {
|
|
|
|
instId = seqLayer->seqChannel->instOrWave;
|
|
|
|
}
|
|
|
|
sub->sound.audioBankSound = seqLayer->sound;
|
|
|
|
|
|
|
|
if (instId >= 0x80) {
|
|
|
|
sub->isSyntheticWave = TRUE;
|
|
|
|
} else {
|
|
|
|
sub->isSyntheticWave = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sub->isSyntheticWave) {
|
|
|
|
build_synthetic_wave(note, seqLayer, instId);
|
|
|
|
}
|
|
|
|
sub->bankId = seqLayer->seqChannel->bankId;
|
|
|
|
sub->stereoHeadsetEffects = seqLayer->seqChannel->stereoHeadsetEffects;
|
2020-03-01 22:42:52 -05:00
|
|
|
sub->reverbIndex = seqLayer->seqChannel->reverbIndex & 3;
|
2020-02-03 00:51:26 -05:00
|
|
|
}
|
|
|
|
#else
|
2019-08-25 00:46:40 -04:00
|
|
|
s32 note_init_for_layer(struct Note *note, struct SequenceChannelLayer *seqLayer) {
|
|
|
|
note->prevParentLayer = NO_LAYER;
|
|
|
|
note->parentLayer = seqLayer;
|
|
|
|
note->priority = seqLayer->seqChannel->notePriority;
|
|
|
|
if (IS_BANK_LOAD_COMPLETE(seqLayer->seqChannel->bankId) == FALSE) {
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
note->bankId = seqLayer->seqChannel->bankId;
|
|
|
|
note->stereoHeadsetEffects = seqLayer->seqChannel->stereoHeadsetEffects;
|
|
|
|
note->sound = seqLayer->sound;
|
2019-12-01 21:52:53 -05:00
|
|
|
seqLayer->status = SOUND_LOAD_STATUS_DISCARDABLE; // "loaded"
|
2019-08-25 00:46:40 -04:00
|
|
|
seqLayer->note = note;
|
|
|
|
seqLayer->seqChannel->noteUnused = note;
|
|
|
|
seqLayer->seqChannel->layerUnused = seqLayer;
|
|
|
|
if (note->sound == NULL) {
|
2019-12-01 21:52:53 -05:00
|
|
|
build_synthetic_wave(note, seqLayer);
|
2019-08-25 00:46:40 -04:00
|
|
|
}
|
2019-12-01 21:52:53 -05:00
|
|
|
note_init(note);
|
2019-08-25 00:46:40 -04:00
|
|
|
return FALSE;
|
|
|
|
}
|
2020-02-03 00:51:26 -05:00
|
|
|
#endif
|
2019-08-25 00:46:40 -04:00
|
|
|
|
|
|
|
void func_80319728(struct Note *note, struct SequenceChannelLayer *seqLayer) {
|
|
|
|
seq_channel_layer_note_release(note->parentLayer);
|
|
|
|
note->wantedParentLayer = seqLayer;
|
|
|
|
}
|
|
|
|
|
|
|
|
void note_release_and_take_ownership(struct Note *note, struct SequenceChannelLayer *seqLayer) {
|
|
|
|
note->wantedParentLayer = seqLayer;
|
|
|
|
note->priority = NOTE_PRIORITY_STOPPING;
|
2020-02-03 00:51:26 -05:00
|
|
|
#ifdef VERSION_EU
|
|
|
|
note->adsr.fadeOutVel = gAudioBufferParameters.updatesPerFrameInv;
|
|
|
|
#else
|
2019-08-25 00:46:40 -04:00
|
|
|
note->adsr.fadeOutVel = 0x8000 / gAudioUpdatesPerFrame;
|
2020-02-03 00:51:26 -05:00
|
|
|
#endif
|
2019-08-25 00:46:40 -04:00
|
|
|
note->adsr.action |= ADSR_ACTION_RELEASE;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Note *alloc_note_from_disabled(struct NotePool *pool, struct SequenceChannelLayer *seqLayer) {
|
|
|
|
struct Note *note = audio_list_pop_back(&pool->disabled);
|
|
|
|
if (note != NULL) {
|
2020-02-03 00:51:26 -05:00
|
|
|
#ifdef VERSION_EU
|
|
|
|
note_init_for_layer(note, seqLayer);
|
|
|
|
#else
|
2019-08-25 00:46:40 -04:00
|
|
|
if (note_init_for_layer(note, seqLayer) == TRUE) {
|
|
|
|
audio_list_push_front(&gNoteFreeLists.disabled, ¬e->listItem);
|
|
|
|
return NULL;
|
|
|
|
}
|
2020-02-03 00:51:26 -05:00
|
|
|
#endif
|
2019-08-25 00:46:40 -04:00
|
|
|
audio_list_push_front(&pool->active, ¬e->listItem);
|
|
|
|
}
|
|
|
|
return note;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Note *alloc_note_from_decaying(struct NotePool *pool, struct SequenceChannelLayer *seqLayer) {
|
|
|
|
struct Note *note = audio_list_pop_back(&pool->decaying);
|
|
|
|
if (note != NULL) {
|
|
|
|
note_release_and_take_ownership(note, seqLayer);
|
|
|
|
audio_list_push_back(&pool->releasing, ¬e->listItem);
|
|
|
|
}
|
|
|
|
return note;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Note *alloc_note_from_active(struct NotePool *pool, struct SequenceChannelLayer *seqLayer) {
|
|
|
|
struct Note *note =
|
2020-09-20 11:15:47 -04:00
|
|
|
pop_node_with_lower_prio(&pool->active, seqLayer->seqChannel->notePriority);
|
|
|
|
if (note == NULL) {
|
|
|
|
eu_stubbed_printf_0("Audio: C-Alloc : lowerPrio is NULL\n");
|
|
|
|
} else {
|
2019-08-25 00:46:40 -04:00
|
|
|
func_80319728(note, seqLayer);
|
|
|
|
audio_list_push_back(&pool->releasing, ¬e->listItem);
|
|
|
|
}
|
|
|
|
return note;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Note *alloc_note(struct SequenceChannelLayer *seqLayer) {
|
|
|
|
struct Note *ret;
|
|
|
|
u32 policy = seqLayer->seqChannel->noteAllocPolicy;
|
|
|
|
|
|
|
|
if (policy & NOTE_ALLOC_LAYER) {
|
|
|
|
ret = seqLayer->note;
|
2020-02-03 00:51:26 -05:00
|
|
|
if (ret != NULL && ret->prevParentLayer == seqLayer
|
|
|
|
#ifdef VERSION_EU
|
|
|
|
&& ret->wantedParentLayer == NO_LAYER
|
|
|
|
#endif
|
|
|
|
) {
|
2019-08-25 00:46:40 -04:00
|
|
|
note_release_and_take_ownership(ret, seqLayer);
|
|
|
|
audio_list_remove(&ret->listItem);
|
2020-02-03 00:51:26 -05:00
|
|
|
#ifdef VERSION_EU
|
|
|
|
audio_list_push_back(&ret->listItem.pool->releasing, &ret->listItem);
|
|
|
|
#else
|
2019-08-25 00:46:40 -04:00
|
|
|
audio_list_push_back(&gNoteFreeLists.releasing, &ret->listItem);
|
2020-02-03 00:51:26 -05:00
|
|
|
#endif
|
2019-08-25 00:46:40 -04:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (policy & NOTE_ALLOC_CHANNEL) {
|
|
|
|
if (!(ret = alloc_note_from_disabled(&seqLayer->seqChannel->notePool, seqLayer))
|
|
|
|
&& !(ret = alloc_note_from_decaying(&seqLayer->seqChannel->notePool, seqLayer))
|
|
|
|
&& !(ret = alloc_note_from_active(&seqLayer->seqChannel->notePool, seqLayer))) {
|
2020-09-20 11:15:47 -04:00
|
|
|
eu_stubbed_printf_0("Sub Limited Warning: Drop Voice");
|
2019-12-01 21:52:53 -05:00
|
|
|
seqLayer->status = SOUND_LOAD_STATUS_NOT_LOADED;
|
2019-08-25 00:46:40 -04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (policy & NOTE_ALLOC_SEQ) {
|
|
|
|
if (!(ret = alloc_note_from_disabled(&seqLayer->seqChannel->notePool, seqLayer))
|
|
|
|
&& !(ret = alloc_note_from_disabled(&seqLayer->seqChannel->seqPlayer->notePool, seqLayer))
|
|
|
|
&& !(ret = alloc_note_from_decaying(&seqLayer->seqChannel->notePool, seqLayer))
|
|
|
|
&& !(ret = alloc_note_from_decaying(&seqLayer->seqChannel->seqPlayer->notePool, seqLayer))
|
|
|
|
&& !(ret = alloc_note_from_active(&seqLayer->seqChannel->notePool, seqLayer))
|
|
|
|
&& !(ret = alloc_note_from_active(&seqLayer->seqChannel->seqPlayer->notePool, seqLayer))) {
|
2020-09-20 11:15:47 -04:00
|
|
|
eu_stubbed_printf_0("Warning: Drop Voice");
|
2019-12-01 21:52:53 -05:00
|
|
|
seqLayer->status = SOUND_LOAD_STATUS_NOT_LOADED;
|
2019-08-25 00:46:40 -04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (policy & NOTE_ALLOC_GLOBAL_FREELIST) {
|
|
|
|
if (!(ret = alloc_note_from_disabled(&gNoteFreeLists, seqLayer))
|
|
|
|
&& !(ret = alloc_note_from_decaying(&gNoteFreeLists, seqLayer))
|
|
|
|
&& !(ret = alloc_note_from_active(&gNoteFreeLists, seqLayer))) {
|
2020-09-20 11:15:47 -04:00
|
|
|
eu_stubbed_printf_0("Warning: Drop Voice");
|
2019-12-01 21:52:53 -05:00
|
|
|
seqLayer->status = SOUND_LOAD_STATUS_NOT_LOADED;
|
2019-08-25 00:46:40 -04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(ret = alloc_note_from_disabled(&seqLayer->seqChannel->notePool, seqLayer))
|
|
|
|
&& !(ret = alloc_note_from_disabled(&seqLayer->seqChannel->seqPlayer->notePool, seqLayer))
|
|
|
|
&& !(ret = alloc_note_from_disabled(&gNoteFreeLists, seqLayer))
|
|
|
|
&& !(ret = alloc_note_from_decaying(&seqLayer->seqChannel->notePool, seqLayer))
|
|
|
|
&& !(ret = alloc_note_from_decaying(&seqLayer->seqChannel->seqPlayer->notePool, seqLayer))
|
|
|
|
&& !(ret = alloc_note_from_decaying(&gNoteFreeLists, seqLayer))
|
|
|
|
&& !(ret = alloc_note_from_active(&seqLayer->seqChannel->notePool, seqLayer))
|
|
|
|
&& !(ret = alloc_note_from_active(&seqLayer->seqChannel->seqPlayer->notePool, seqLayer))
|
|
|
|
&& !(ret = alloc_note_from_active(&gNoteFreeLists, seqLayer))) {
|
2020-09-20 11:15:47 -04:00
|
|
|
eu_stubbed_printf_0("Warning: Drop Voice");
|
2019-12-01 21:52:53 -05:00
|
|
|
seqLayer->status = SOUND_LOAD_STATUS_NOT_LOADED;
|
2019-08-25 00:46:40 -04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-02-03 00:51:26 -05:00
|
|
|
#ifndef VERSION_EU
|
2019-12-01 21:52:53 -05:00
|
|
|
void reclaim_notes(void) {
|
2019-08-25 00:46:40 -04:00
|
|
|
struct Note *note;
|
|
|
|
s32 i;
|
|
|
|
s32 cond;
|
|
|
|
|
|
|
|
for (i = 0; i < gMaxSimultaneousNotes; i++) {
|
|
|
|
note = &gNotes[i];
|
|
|
|
if (note->parentLayer != NO_LAYER) {
|
|
|
|
cond = FALSE;
|
|
|
|
if (!note->parentLayer->enabled && note->priority >= NOTE_PRIORITY_MIN) {
|
|
|
|
cond = TRUE;
|
|
|
|
} else if (note->parentLayer->seqChannel == NULL) {
|
|
|
|
audio_list_push_back(&gLayerFreeList, ¬e->parentLayer->listItem);
|
|
|
|
seq_channel_layer_disable(note->parentLayer);
|
|
|
|
note->priority = NOTE_PRIORITY_STOPPING;
|
|
|
|
} else if (note->parentLayer->seqChannel->seqPlayer == NULL) {
|
|
|
|
sequence_channel_disable(note->parentLayer->seqChannel);
|
|
|
|
note->priority = NOTE_PRIORITY_STOPPING;
|
|
|
|
} else if (note->parentLayer->seqChannel->seqPlayer->muted) {
|
|
|
|
if (note->parentLayer->seqChannel->muteBehavior
|
2019-12-01 21:52:53 -05:00
|
|
|
& (MUTE_BEHAVIOR_STOP_SCRIPT | MUTE_BEHAVIOR_STOP_NOTES)) {
|
2019-08-25 00:46:40 -04:00
|
|
|
cond = TRUE;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
cond = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cond) {
|
|
|
|
seq_channel_layer_note_release(note->parentLayer);
|
|
|
|
audio_list_remove(¬e->listItem);
|
|
|
|
audio_list_push_front(¬e->listItem.pool->disabled, ¬e->listItem);
|
|
|
|
note->priority = NOTE_PRIORITY_STOPPING;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-02-03 00:51:26 -05:00
|
|
|
#endif
|
|
|
|
|
2019-08-25 00:46:40 -04:00
|
|
|
void note_init_all(void) {
|
|
|
|
struct Note *note;
|
|
|
|
s32 i;
|
|
|
|
|
|
|
|
for (i = 0; i < gMaxSimultaneousNotes; i++) {
|
|
|
|
note = &gNotes[i];
|
2020-02-03 00:51:26 -05:00
|
|
|
#ifdef VERSION_EU
|
|
|
|
note->noteSubEu = gZeroNoteSub;
|
|
|
|
#else
|
2019-08-25 00:46:40 -04:00
|
|
|
note->enabled = FALSE;
|
|
|
|
note->stereoStrongRight = FALSE;
|
|
|
|
note->stereoStrongLeft = FALSE;
|
|
|
|
note->stereoHeadsetEffects = FALSE;
|
2020-02-03 00:51:26 -05:00
|
|
|
#endif
|
2019-08-25 00:46:40 -04:00
|
|
|
note->priority = NOTE_PRIORITY_DISABLED;
|
|
|
|
note->parentLayer = NO_LAYER;
|
|
|
|
note->wantedParentLayer = NO_LAYER;
|
|
|
|
note->prevParentLayer = NO_LAYER;
|
2020-02-03 00:51:26 -05:00
|
|
|
#ifdef VERSION_EU
|
|
|
|
note->waveId = 0;
|
|
|
|
#else
|
2019-08-25 00:46:40 -04:00
|
|
|
note->reverb = 0;
|
2019-11-03 14:36:27 -05:00
|
|
|
note->usesHeadsetPanEffects = FALSE;
|
2019-08-25 00:46:40 -04:00
|
|
|
note->sampleCount = 0;
|
|
|
|
note->instOrWave = 0;
|
|
|
|
note->targetVolLeft = 0;
|
|
|
|
note->targetVolRight = 0;
|
|
|
|
note->frequency = 0.0f;
|
|
|
|
note->unused1 = 0x3f;
|
2020-02-03 00:51:26 -05:00
|
|
|
#endif
|
2019-08-25 00:46:40 -04:00
|
|
|
note->attributes.velocity = 0.0f;
|
|
|
|
note->adsrVolScale = 0;
|
|
|
|
note->adsr.state = ADSR_STATE_DISABLED;
|
|
|
|
note->adsr.action = 0;
|
|
|
|
note->vibratoState.active = FALSE;
|
|
|
|
note->portamento.cur = 0.0f;
|
|
|
|
note->portamento.speed = 0.0f;
|
2020-02-03 00:51:26 -05:00
|
|
|
#ifdef VERSION_EU
|
|
|
|
note->synthesisState.synthesisBuffers = soundAlloc(&gNotesAndBuffersPool, sizeof(struct NoteSynthesisBuffers));
|
|
|
|
#else
|
2019-11-03 14:36:27 -05:00
|
|
|
note->synthesisBuffers = soundAlloc(&gNotesAndBuffersPool, sizeof(struct NoteSynthesisBuffers));
|
2020-02-03 00:51:26 -05:00
|
|
|
#endif
|
2019-08-25 00:46:40 -04:00
|
|
|
}
|
|
|
|
}
|