Files
Microtransactions64/src/audio/internal.h
Gregory Heskett 8f6fde697f Disable Stereo Headset Effects and stub out all known code references via define (#633)
* Disable stereoheadseteffects and stub out all known code references via define

By effect this also completely removes Headset sound options from the game config, as it serves no purpose whatsoever compared to stereo.
Because it's stubbed out everywhere rather than only the necessary locations to prevent it, this should boost audio runtime slightly for everything, not just the stuff that actively used it.

* Rename DISABLE_HEADSET_STEREO_EFFECTS to ENABLE_HEADSET_STEREO_EFFECTS

* Reorganize and reduce audio Note struct

* Add performance to define description

* Rename ENABLE_HEADSET_STEREO_EFFECTS to ENABLE_STEREO_HEADSET_EFFECTS
2023-06-07 13:58:59 -04:00

897 lines
28 KiB
C

#ifndef AUDIO_INTERNAL_H
#define AUDIO_INTERNAL_H
#include <ultra64.h>
#include "types.h"
#if defined(VERSION_EU) || defined(VERSION_SH)
#define SEQUENCE_PLAYERS 4
#else
#define SEQUENCE_PLAYERS 3
#endif
#define LAYERS_MAX 4
#define CHANNELS_MAX 16
#ifdef EXPAND_AUDIO_HEAP // Not technically on the heap but it's memory nonetheless...
#define SEQUENCE_CHANNELS (SEQUENCE_PLAYERS * CHANNELS_MAX)
#define SEQUENCE_LAYERS ((SEQUENCE_CHANNELS * LAYERS_MAX) / 2) // This should be more than plenty in nearly all circumstances.
#else // EXPAND_AUDIO_HEAP
#if defined(VERSION_EU) || defined(VERSION_SH)
#define SEQUENCE_CHANNELS 48
#define SEQUENCE_LAYERS 64
#else
#define SEQUENCE_CHANNELS 32
#define SEQUENCE_LAYERS 52
#endif
#endif // EXPAND_AUDIO_HEAP
#define VIBRATO_DISABLED_VALUE (0xFF * 8)
#define NO_LAYER ((struct SequenceChannelLayer *)(-1))
enum MuteBehaviors {
MUTE_BEHAVIOR_STOP_SCRIPT = (1 << 7), // 0x80 // stop processing sequence/channel scripts
MUTE_BEHAVIOR_STOP_NOTES = (1 << 6), // 0x40 // prevent further notes from playing
MUTE_BEHAVIOR_SOFTEN = (1 << 5), // 0x20 // lower volume, by default to half
};
enum SequencePlayerStates {
SEQUENCE_PLAYER_STATE_0,
SEQUENCE_PLAYER_STATE_FADE_OUT,
SEQUENCE_PLAYER_STATE_2,
SEQUENCE_PLAYER_STATE_3,
SEQUENCE_PLAYER_STATE_4
};
enum NotePriority {
NOTE_PRIORITY_DISABLED,
NOTE_PRIORITY_STOPPING,
NOTE_PRIORITY_MIN,
NOTE_PRIORITY_DEFAULT
};
#define TATUMS_PER_BEAT 48
// abi.h contains more details about the ADPCM and S8 codecs, "skip" skips codec processing
enum Codecs {
CODEC_ADPCM,
CODEC_S8,
CODEC_SKIP
};
#define TEMPO_SCALE TATUMS_PER_BEAT
// Convert u8 or u16 to f32. On JP, this uses a u32->f32 conversion,
// resulting in more bloated codegen, while on US it goes through s32.
// Since u8 and u16 fit losslessly in both, behavior is the same.
#define FLOAT_CAST(x) (f32) (s32) (x)
#if defined(ISVPRINT) || defined(UNF)
#define stubbed_printf osSyncPrintf
#else
// No-op printf macro which leaves string literals in rodata in IDO. IDO
// doesn't support variadic macros, so instead we let the parameter list
// expand to a no-op comma expression. Another possibility is that it might
// have expanded to something with "if (0)". See also goddard/gd_main.h.
// On US/JP, -sopt optimizes away these except for external.c.
#ifdef __sgi
#define stubbed_printf
#else
#define stubbed_printf(...)
#endif
#endif
#include "game/puppyprint.h"
#ifdef VERSION_EU
/*#ifdef PUPPYPRINT_DEBUG
#define eu_stubbed_printf_0(msg) append_puppyprint_log(msg)
#define eu_stubbed_printf_1(msg, a) append_puppyprint_log(msg, a)
#define eu_stubbed_printf_2(msg, a, b) append_puppyprint_log(msg, a, b)
#define eu_stubbed_printf_3(msg, a, b, c) append_puppyprint_log(msg, a, b, c)
#else*/
#define eu_stubbed_printf_0(msg) stubbed_printf(msg)
#define eu_stubbed_printf_1(msg, a) stubbed_printf(msg, a)
#define eu_stubbed_printf_2(msg, a, b) stubbed_printf(msg, a, b)
#define eu_stubbed_printf_3(msg, a, b, c) stubbed_printf(msg, a, b, c)
//#endif
#else
#define eu_stubbed_printf_0(msg)
#define eu_stubbed_printf_1(msg, a)
#define eu_stubbed_printf_2(msg, a, b)
#define eu_stubbed_printf_3(msg, a, b, c)
#endif
struct NotePool;
struct AudioListItem {
// A node in a circularly linked list. Each node is either a head or an item:
// - Items can be either detached (prev = NULL), or attached to a list.
// 'value' points to something of interest.
// - List heads are always attached; if a list is empty, its head points
// to itself. 'count' contains the size of the list.
// If the list holds notes, 'pool' points back to the pool where it lives.
// Otherwise, that member is NULL.
struct AudioListItem *prev;
struct AudioListItem *next;
union {
void *value; // either Note* or SequenceChannelLayer*
s32 count;
} u;
struct NotePool *pool;
}; // size = 0x10
struct NotePool {
struct AudioListItem disabled;
struct AudioListItem decaying;
struct AudioListItem releasing;
struct AudioListItem active;
};
struct VibratoState {
/*0x00, 0x00*/ struct SequenceChannel *seqChannel;
/*0x04, 0x04*/ u32 time;
#if defined(VERSION_EU) || defined(VERSION_SH)
/* , 0x08*/ s16 *curve;
/* , 0x0C*/ f32 extent;
/* , 0x10*/ f32 rate;
/* , 0x14*/ u8 active;
#else
/*0x08, */ s8 *curve;
/*0x0C, */ u8 activeFlags;
/*0x0E, */ u16 rate;
/*0x10, */ u16 extent;
#endif
/*0x12, 0x16*/ u16 rateChangeTimer;
/*0x14, 0x18*/ u16 extentChangeTimer;
/*0x16, 0x1A*/ u16 delay;
}; // size = 0x18, 0x1C on EU
// Pitch sliding by up to one octave in the positive direction. Negative
// direction is "supported" by setting extent to be negative. The code
// extrapolates exponentially in the wrong direction in that case, but that
// doesn't prevent seqplayer from doing it, AFAICT.
struct Portamento {
u8 mode; // bit 0x80 denotes something; the rest are an index 0-5
f32 cur;
f32 speed;
f32 extent;
}; // size = 0x10
struct AdsrEnvelope {
s16 delay;
s16 arg;
}; // size = 0x4
struct AdpcmLoop {
u32 start;
u32 end;
u32 count;
u32 pad;
s16 state[16]; // only exists if count != 0. 8-byte aligned
};
struct AdpcmBook {
s32 order;
s32 npredictors;
s16 book[1]; // size 8 * order * npredictors. 8-byte aligned
};
struct AudioBankSample {
#ifdef VERSION_SH
/* 0x00 */ u32 codec : 4;
/* 0x00 */ u32 medium : 2;
/* 0x00 */ u32 bit1 : 1;
/* 0x00 */ u32 isPatched : 1;
/* 0x01 */ u32 size : 24;
#else
u8 unused;
u8 loaded;
#endif
u8 *sampleAddr;
struct AdpcmLoop *loop;
struct AdpcmBook *book;
#ifndef VERSION_SH
u32 sampleSize; // never read. either 0 or 1 mod 9, depending on padding
#endif
};
struct AudioBankSound {
struct AudioBankSample *sample;
f32 tuning; // frequency scale factor
}; // size = 0x8
struct Instrument {
/*0x00*/ u8 loaded;
/*0x01*/ u8 normalRangeLo;
/*0x02*/ u8 normalRangeHi;
/*0x03*/ u8 releaseRate;
/*0x04*/ struct AdsrEnvelope *envelope;
/*0x08*/ struct AudioBankSound lowNotesSound;
/*0x10*/ struct AudioBankSound normalNotesSound;
/*0x18*/ struct AudioBankSound highNotesSound;
}; // size = 0x20
struct Drum {
u8 releaseRate;
u8 pan;
u8 loaded;
struct AudioBankSound sound;
struct AdsrEnvelope *envelope;
};
struct AudioBank {
struct Drum **drums;
struct Instrument *instruments[1];
}; // dynamic size
struct CtlEntry {
#ifndef VERSION_SH
u8 unused;
#endif
u8 numInstruments;
u8 numDrums;
#ifdef VERSION_SH
u8 bankId1;
u8 bankId2;
#endif
struct Instrument **instruments;
struct Drum **drums;
}; // size = 0xC
struct M64ScriptState {
u8 *pc;
u8 *stack[4];
u8 remLoopIters[4];
u8 depth;
}; // size = 0x1C
// Also known as a Group, according to debug strings.
struct SequencePlayer {
/*US/JP, EU, SH */
#if defined(VERSION_EU) || defined(VERSION_SH)
/*0x000, 0x000, 0x000*/ u8 enabled : 1;
#else
/*0x000, 0x000*/ volatile u8 enabled : 1;
#endif
/*0x000, 0x000*/ u8 finished : 1; // never read
/*0x000, 0x000*/ u8 muted : 1;
/*0x000, 0x000*/ u8 seqDmaInProgress : 1;
/*0x000, 0x000*/ u8 bankDmaInProgress : 1;
#if defined(VERSION_EU) || defined(VERSION_SH)
/* 0x000*/ u8 recalculateVolume : 1;
#endif
#ifdef VERSION_SH
/* 0x000*/ u8 unkSh: 1;
#endif
#if defined(VERSION_JP) || defined(VERSION_US)
/*0x001 */ s8 seqVariation;
#endif
/*0x002, 0x001, 0x001*/ u8 state;
/*0x003, 0x002*/ u8 noteAllocPolicy;
/*0x004, 0x003*/ u8 muteBehavior;
/*0x005, 0x004*/ u8 seqId;
/*0x006, 0x005*/ u8 defaultBank[1]; // must be an array to get a comparison
// to match; other u8's might also be part of that array
/*0x007, 0x006*/ u8 loadingBankId;
#if defined(VERSION_JP) || defined(VERSION_US)
/*0x008, ?????*/ u8 loadingBankNumInstruments;
/*0x009, ?????*/ u8 loadingBankNumDrums;
#endif
#if defined(VERSION_EU) || defined(VERSION_SH)
/* , 0x007, 0x007*/ s8 seqVariationEu[1];
#endif
/*0x00A, 0x008*/ u16 tempo; // beats per minute in JP, tatums per minute in US/EU
/*0x00C, 0x00A*/ u16 tempoAcc;
#if defined(VERSION_JP) || defined(VERSION_US)
/*0x00E, 0x010*/ u16 fadeRemainingFrames;
#endif
#ifdef VERSION_SH
/* 0x00C*/ s16 tempoAdd;
#endif
/*0x010, 0x00C, 0x00E*/ s16 transposition;
/*0x012, 0x00E, 0x010*/ u16 delay;
#if defined(VERSION_EU) || defined(VERSION_SH)
/*0x00E, 0x010, 0x012*/ u16 fadeRemainingFrames;
/* , 0x012, 0x014*/ u16 fadeTimerUnkEu;
#endif
/*0x014, 0x014*/ u8 *seqData; // buffer of some sort
/*0x018, 0x018, 0x1C*/ f32 fadeVolume; // set to 1.0f
/*0x01C, 0x01C*/ f32 fadeVelocity; // set to 0.0f
/*0x020, 0x020, 0x024*/ f32 volume; // set to 0.0f
/*0x024, 0x024*/ f32 muteVolumeScale; // set to 0.5f
#if defined(VERSION_EU) || defined(VERSION_SH)
/* , 0x028, 0x02C*/ f32 fadeVolumeScale;
/* , 0x02C*/ f32 appliedFadeVolume;
#else
/*0x028, */ f32 volumeDefault;
#endif
/*0x02C, 0x030, 0x034*/ struct SequenceChannel *channels[CHANNELS_MAX];
/*0x06C, 0x070*/ struct M64ScriptState scriptState;
/*0x088, 0x08C*/ u8 *shortNoteVelocityTable;
/*0x08C, 0x090*/ u8 *shortNoteDurationTable;
/*0x090, 0x094*/ struct NotePool notePool;
/*0x0D0, 0x0D4*/ OSMesgQueue seqDmaMesgQueue;
/*0x0E8, 0x0EC*/ OSMesg seqDmaMesg;
/*0x0EC, 0x0F0*/ OSIoMesg seqDmaIoMesg;
/*0x100, 0x108*/ OSMesgQueue bankDmaMesgQueue;
/*0x118, 0x120*/ OSMesg bankDmaMesg;
/*0x11C, 0x124*/ OSIoMesg bankDmaIoMesg;
/*0x130, 0x13C*/ u8 *bankDmaCurrMemAddr;
#if defined(VERSION_JP) || defined(VERSION_US)
/*0x134, ?????*/ struct AudioBank *loadingBank;
#endif
/*0x138, 0x140*/ uintptr_t bankDmaCurrDevAddr;
/*0x13C, 0x144*/ ssize_t bankDmaRemaining;
}; // size = 0x140, 0x148 on EU, 0x14C on SH
struct AdsrSettings {
u8 releaseRate;
#if defined(VERSION_EU) || defined(VERSION_SH)
u8 sustain;
#else
u16 sustain; // sustain level, 2^16 = max
#endif
struct AdsrEnvelope *envelope;
}; // size = 0x8
struct AdsrState {
/*0x00, 0x00*/ u8 action;
/*0x01, 0x01*/ u8 state;
#if defined(VERSION_JP) || defined(VERSION_US)
/*0x02, */ s16 initial; // always 0
/*0x04, */ s16 target;
/*0x06, */ s16 current;
#endif
/*0x08, 0x02*/ s16 envIndex;
/*0x0A, 0x04*/ s16 delay;
#if defined(VERSION_EU) || defined(VERSION_SH)
/* , 0x08*/ f32 sustain;
/* , 0x0C*/ f32 velocity;
/* , 0x10*/ f32 fadeOutVel;
/* , 0x14*/ f32 current;
/* , 0x18*/ f32 target;
s32 pad1C;
#else
/*0x0C, */ s16 sustain;
/*0x0E, */ s16 fadeOutVel;
/*0x10, */ s32 velocity;
/*0x14, */ s32 currentHiRes;
/*0x18, */ s16 *volOut;
#endif
/*0x1C, 0x20*/ struct AdsrEnvelope *envelope;
}; // size = 0x20, 0x24 in EU
struct ReverbBitsData {
/* 0x00 */ u8 bit0 : 1;
/* 0x00 */ u8 bit1 : 1;
/* 0x00 */ u8 bit2 : 1;
/* 0x00 */ u8 usesHeadsetPanEffects : 1;
/* 0x00 */ u8 stereoHeadsetEffects : 2;
/* 0x00 */ u8 strongRight : 1;
/* 0x00 */ u8 strongLeft : 1;
};
union ReverbBits {
/* 0x00 */ struct ReverbBitsData s;
/* 0x00 */ u8 asByte;
};
struct ReverbInfo {
u8 reverbVol;
u8 synthesisVolume; // UQ4.4, although 0 <= x < 1 is rounded up to 1
u8 pan;
union ReverbBits reverbBits;
f32 freqScale;
f32 velocity;
s32 unused;
s16 *filter;
};
struct NoteAttributes {
u8 reverbVol;
#ifdef VERSION_SH
u8 synthesisVolume; // UQ4.4, although 0 <= x < 1 is rounded up to 1
#endif
#if defined(VERSION_EU) || defined(VERSION_SH)
u8 pan;
#endif
#ifdef VERSION_SH
union ReverbBits reverbBits;
#endif
f32 freqScale;
f32 velocity;
#if defined(VERSION_JP) || defined(VERSION_US)
f32 pan;
#endif
#ifdef VERSION_SH
s16 *filter;
#endif
}; // size = 0x10
// Also known as a SubTrack, according to debug strings.
// Confusingly, a SubTrack is a container of Tracks.
struct SequenceChannel {
/* U/J, EU, SH */
/*0x00, 0x00*/ u8 enabled : 1;
/*0x00, 0x00*/ u8 finished : 1;
/*0x00, 0x00*/ u8 stopScript : 1;
/*0x00, 0x00*/ u8 stopSomething2 : 1; // sets SequenceChannelLayer.stopSomething
/*0x00, 0x00*/ u8 hasInstrument : 1;
#ifdef ENABLE_STEREO_HEADSET_EFFECTS
/*0x00, 0x00*/ u8 stereoHeadsetEffects : 1;
#else
/*0x00, 0x00*/ u8 paddingBit : 1;
#endif
/*0x00, ????*/ u8 largeNotes : 1; // notes specify duration and velocity
/*0x00, ????*/ u8 unused : 1; // never read, set to 0
#if defined(VERSION_EU) || defined(VERSION_SH)
/* , 0x01*/ union {
struct {
u8 freqScale : 1;
u8 volume : 1;
u8 pan : 1;
} as_bitfields;
u8 as_u8;
} changes;
#endif
/*0x01, 0x02*/ u8 noteAllocPolicy;
/*0x02, 0x03, 0x03*/ u8 muteBehavior;
/*0x03, 0x04, 0x04*/ u8 reverbVol; // until EU: Q1.7, after EU: UQ0.8
/*0x04, ????*/ u8 notePriority; // 0-3
#ifdef VERSION_SH
u8 unkSH06; // some priority
#endif
/*0x05, 0x06*/ u8 bankId;
#if defined(VERSION_EU) || defined(VERSION_SH)
/* , 0x07*/ u8 reverbIndex;
/* , 0x08, 0x09*/ u8 bookOffset;
/* , 0x09*/ u8 newPan;
/* , 0x0A*/ u8 panChannelWeight; // proportion of pan that comes from the channel (0..128)
#else
/*0x06, */ u8 updatesPerFrameUnused;
#endif
#ifdef VERSION_SH
/* 0x0C*/ u8 synthesisVolume; // UQ4.4, although 0 <= x < 1 is rounded up to 1
#endif
/*0x08, 0x0C, 0x0E*/ u16 vibratoRateStart; // initially 0x800
/*0x0A, 0x0E, 0x10*/ u16 vibratoExtentStart;
/*0x0C, 0x10, 0x12*/ u16 vibratoRateTarget; // initially 0x800
/*0x0E, 0x12, 0x14*/ u16 vibratoExtentTarget;
/*0x10, 0x14, 0x16*/ u16 vibratoRateChangeDelay;
/*0x12, 0x16, 0x18*/ u16 vibratoExtentChangeDelay;
/*0x14, 0x18, 0x1A*/ u16 vibratoDelay;
/*0x16, 0x1A, 0x1C*/ u16 delay;
/*0x18, 0x1C, 0x1E*/ s16 instOrWave; // either 0 (none), instrument index + 1, or
// 0x80..0x83 for sawtooth/triangle/sine/square waves.
/*0x1A, 0x1E, 0x20*/ s16 transposition;
/*0x1C, 0x20, 0x24*/ f32 volumeScale;
/*0x20, 0x24, 0x28*/ f32 volume;
#if defined(VERSION_JP) || defined(VERSION_US)
/*0x24, */ f32 pan;
/*0x28, */ f32 panChannelWeight; // proportion of pan that comes from the channel (0..1)
#else
/* , 0x28*/ s32 pan;
/* , 0x2C*/ f32 appliedVolume;
#endif
/*0x2C, 0x30*/ f32 freqScale;
/*0x30, 0x34*/ u8 (*dynTable)[][2];
/*0x34, ????*/ struct Note *noteUnused; // never read
/*0x38, ????*/ struct SequenceChannelLayer *layerUnused; // never read
/*0x3C, 0x40*/ struct Instrument *instrument;
/*0x40, 0x44*/ struct SequencePlayer *seqPlayer;
/*0x44, 0x48*/ struct SequenceChannelLayer *layers[LAYERS_MAX];
#ifndef VERSION_SH
/*0x54, 0x58 */ s8 soundScriptIO[8]; // bridge between sound script and audio lib. For player 2,
// [0] contains enabled, [4] contains sound ID, [5] contains reverb adjustment
#endif
/*0x5C, 0x60*/ struct M64ScriptState scriptState;
/*0x78, 0x7C*/ struct AdsrSettings adsr;
/*0x80, 0x84*/ struct NotePool notePool;
#ifdef VERSION_SH
/* 0xC0*/ s8 soundScriptIO[8]; // bridge between sound script and audio lib. For player 2,
// [0] contains enabled, [4] contains sound ID, [5] contains reverb adjustment
/* 0xC8*/ u16 unkC8;
/* 0xCC*/ s16 *filter;
#endif
}; // size = 0xC0, 0xC4 in EU, 0xD0 in SH
// Also known as a Track, according to debug strings.
struct SequenceChannelLayer {
/* U/J, EU, SH */
/*0x00, 0x00*/ u8 enabled : 1;
/*0x00, 0x00*/ u8 finished : 1;
/*0x00, 0x00*/ u8 stopSomething : 1; // ?
/*0x00, 0x00*/ u8 continuousNotes : 1; // keep the same note for consecutive notes with the same sound
#if defined(VERSION_EU) || defined(VERSION_SH)
/* , 0x00*/ u8 unusedEu0b8 : 1;
/* , 0x00*/ u8 notePropertiesNeedInit : 1;
/* , 0x00*/ u8 ignoreDrumPan : 1;
#ifdef VERSION_SH
/* , , 0x01 */ union ReverbBits reverbBits;
#endif
/* , 0x01, 0x02*/ u8 instOrWave;
#endif
/*0x01, 0x02, 0x03*/ u8 status; // 0x03 in SH
/*0x02, 0x03*/ u8 noteDuration; // set to 0x80
/*0x03, 0x04*/ u8 portamentoTargetNote;
#if defined(VERSION_EU) || defined(VERSION_SH)
/* , 0x05*/ u8 pan; // 0..128
/* , 0x06, 0x07*/ u8 notePan;
#endif
/*0x04, 0x08*/ struct Portamento portamento;
/*0x14, 0x18*/ struct AdsrSettings adsr;
/*0x1C, 0x20*/ u16 portamentoTime;
/*0x1E, 0x22*/ s16 transposition; // #semitones added to play commands
// (m64 instruction encoding only allows referring to the limited range
// 0..0x3f; this makes 0x40..0x7f accessible as well)
/*0x20, 0x24, 0x24*/ f32 freqScale;
#ifdef VERSION_SH
/* 0x28*/ f32 freqScaleMultiplier;
#endif
/*0x24, 0x28, 0x2C*/ f32 velocitySquare;
#if defined(VERSION_JP) || defined(VERSION_US)
/*0x28, */ f32 pan; // 0..1
#endif
/*0x2C, 0x2C, 0x30*/ f32 noteVelocity;
#if defined(VERSION_JP) || defined(VERSION_US)
/*0x30*/ f32 notePan;
#endif
/*0x34, 0x30, 0x34*/ f32 noteFreqScale;
/*0x38, 0x34*/ s16 shortNoteDefaultPlayPercentage;
/*0x3A, 0x36*/ s16 playPercentage; // it's not really a percentage...
/*0x3C, 0x38*/ s16 delay;
/*0x3E, 0x3A*/ s16 duration;
/*0x40, 0x3C*/ s16 delayUnused; // set to 'delay', never read
/*0x44, 0x40, 0x44*/ struct Note *note;
/*0x48, 0x44*/ struct Instrument *instrument;
/*0x4C, 0x48*/ struct AudioBankSound *sound;
/*0x50, 0x4C, 0x50*/ struct SequenceChannel *seqChannel;
/*0x54, 0x50*/ struct M64ScriptState scriptState;
/*0x70, 0x6C*/ struct AudioListItem listItem;
#if defined(VERSION_EU)
u8 pad2[4];
#endif
}; // size = 0x80
#if defined(VERSION_EU) || defined(VERSION_SH)
struct NoteSynthesisState {
/*0x00*/ u8 restart;
/*0x01*/ u8 sampleDmaIndex;
/*0x02*/ u8 prevHeadsetPanRight;
/*0x03*/ u8 prevHeadsetPanLeft;
#ifdef VERSION_SH
/* 0x04*/ u8 reverbVol;
/* 0x05*/ u8 unk5;
#endif
/*0x04, 0x06*/ u16 samplePosFrac;
/*0x08*/ s32 samplePosInt;
/*0x0C*/ struct NoteSynthesisBuffers *synthesisBuffers;
/*0x10*/ s16 curVolLeft; // UQ0.16 (EU Q1.15)
/*0x12*/ s16 curVolRight; // UQ0.16 (EU Q1.15)
};
struct NotePlaybackState {
/* U/J, EU, SH */
/*0x04, 0x00, 0x00*/ u8 priority;
/* 0x01, 0x01*/ u8 waveId;
/* 0x02, 0x02*/ u8 sampleCountIndex;
#ifdef VERSION_SH
/* 0x03*/ u8 bankId;
/* 0x04*/ u8 unkSH34;
#endif
/*0x08, 0x04, 0x06*/ s16 adsrVolScale;
/*0x18, 0x08, 0x08*/ f32 portamentoFreqScale;
/*0x1C, 0x0C, 0x0C*/ f32 vibratoFreqScale;
/*0x28, 0x10, */ struct SequenceChannelLayer *prevParentLayer;
/*0x2C, 0x14, 0x14*/ struct SequenceChannelLayer *parentLayer;
/*0x30, 0x18, 0x18*/ struct SequenceChannelLayer *wantedParentLayer;
/* , 0x1C, 0x1C*/ struct NoteAttributes attributes;
/*0x54, 0x28, 0x2C*/ struct AdsrState adsr;
/*0x74, 0x4C, */ struct Portamento portamento;
/*0x84, 0x5C, */ struct VibratoState vibratoState;
};
struct NoteSubEu {
/*0x00*/ volatile u8 enabled : 1;
/*0x00*/ u8 needsInit : 1;
/*0x00*/ u8 finished : 1;
/*0x00*/ u8 envMixerNeedsInit : 1;
/*0x00*/ u8 stereoStrongRight : 1;
/*0x00*/ u8 stereoStrongLeft : 1;
/*0x00*/ u8 stereoHeadsetEffects : 1;
/*0x00*/ u8 usesHeadsetPanEffects : 1;
/*0x01*/ u8 reverbIndex : 3;
/*0x01*/ u8 bookOffset : 3;
/*0x01*/ u8 isSyntheticWave : 1;
/*0x01*/ u8 hasTwoAdpcmParts : 1;
#ifdef VERSION_EU
/*0x02*/ u8 bankId;
#else
/*0x02*/ u8 synthesisVolume; // UQ4.4, although 0 <= x < 1 is rounded up to 1
#endif
/*0x03*/ u8 headsetPanRight;
/*0x04*/ u8 headsetPanLeft;
/*0x05*/ u8 reverbVol; // UQ0.7 (EU Q1.7)
/*0x06*/ u16 targetVolLeft; // UQ0.12 (EU UQ0.10)
/*0x08*/ u16 targetVolRight; // UQ0.12 (EU UQ0.10)
/*0x0A*/ u16 resamplingRateFixedPoint; // stored as signed but loaded as u16
/*0x0C*/ union {
s16 *samples;
struct AudioBankSound *audioBankSound;
} sound;
#ifdef VERSION_SH
/*0x10*/ s16 *filter;
#endif
};
struct Note {
/* U/J, EU, SH */
/*0xA4, 0x00, 0x00*/ struct AudioListItem listItem;
/* 0x10, 0x10*/ struct NoteSynthesisState synthesisState;
// The next members are actually part of a struct (NotePlaybackState), but
// that results in messy US/EU ifdefs. Instead we cast to a struct pointer
// when needed... This breaks alignment on non-N64 platforms, which we hack
// around by skipping the padding in that case.
// TODO: use macros or something instead.
#ifdef TARGET_N64
u8 pad0[12];
#endif
/*0x04, 0x30, 0x30*/ u8 priority;
/* 0x31, 0x31*/ u8 waveId;
/* 0x32, 0x32*/ u8 sampleCountIndex;
#ifdef VERSION_SH
/* 0x33*/ u8 bankId;
/* 0x34*/ u8 unkSH34;
#endif
/*0x08, 0x34, 0x36*/ s16 adsrVolScale;
/*0x18, 0x38, */ f32 portamentoFreqScale;
/*0x1C, 0x3C, */ f32 vibratoFreqScale;
/*0x28, 0x40, */ struct SequenceChannelLayer *prevParentLayer;
/*0x2C, 0x44, 0x44*/ struct SequenceChannelLayer *parentLayer;
/*0x30, 0x48, 0x48*/ struct SequenceChannelLayer *wantedParentLayer;
/* , 0x4C, 0x4C*/ struct NoteAttributes attributes;
/*0x54, 0x58, 0x5C*/ struct AdsrState adsr;
/*0x74, 0x7C*/ struct Portamento portamento;
/*0x84, 0x8C*/ struct VibratoState vibratoState;
u8 pad3[8];
/* , 0xB0, 0xB4*/ struct NoteSubEu noteSubEu;
}; // size = 0xC0, known to be 0xC8 on SH
#else
// volatile Note, needed in synthesis_process_notes
struct vNote {
/* U/J, EU */
/*0x00*/ volatile u8 enabled : 1;
long long int force_structure_alignment;
}; // size = 0xC0
struct Note {
/* U/J, EU */
/*0x00*/ u8 enabled : 1;
/*0x00*/ u8 needsInit : 1;
/*0x00*/ u8 restart : 1;
/*0x00*/ u8 finished : 1;
/*0x00*/ u8 envMixerNeedsInit : 1;
/*0x00*/ u8 initFullVelocity : 1;
#ifdef ENABLE_STEREO_HEADSET_EFFECTS
/*0x00*/ u8 stereoHeadsetEffects : 1;
/*0x00*/ u8 usesHeadsetPanEffects : 1;
/*0x01*/ u8 stereoStrongRight : 1;
/*0x01*/ u8 stereoStrongLeft : 1;
#else
/* */ u8 pad0[0x01];
#endif
/*0x02*/ u8 sampleDmaIndex;
/*0x03*/ u8 priority;
/*0x04*/ u8 sampleCount; // 0, 8, 16, 32 or 64
/*0x05*/ u8 instOrWave;
/*0x06*/ u8 bankId; // in NoteSubEu on EU
/*0x07*/ u8 reverbVol; // Q1.7
/*0x08*/ s16 adsrVolScale;
/*0x0A*/ u16 samplePosFrac;
/*0x0C*/ s32 samplePosInt;
/*0x10*/ f32 portamentoFreqScale;
/*0x14*/ f32 vibratoFreqScale;
/*0x18*/ struct AudioBankSound *sound;
/*0x1C*/ struct SequenceChannelLayer *prevParentLayer;
/*0x20*/ struct SequenceChannelLayer *parentLayer;
/*0x24*/ struct SequenceChannelLayer *wantedParentLayer;
/*0x28*/ struct NoteSynthesisBuffers *synthesisBuffers;
/*0x2C*/ f32 frequency;
/*0x30*/ u16 targetVolLeft; // Q1.15, but will always be non-negative
/*0x32*/ u16 targetVolRight; // Q1.15, but will always be non-negative
/*0x34*/ struct NoteAttributes attributes;
/*0x44*/ struct AdsrState adsr;
/*0x64*/ struct Portamento portamento;
/*0x74*/ struct VibratoState vibratoState;
/*0x8C*/ struct AudioListItem listItem;
/*0x9C*/ s16 curVolLeft; // Q1.15, but will always be non-negative
/*0x9E*/ s16 curVolRight; // Q1.15, but will always be non-negative
/*0xA0*/ s16 reverbVolShifted; // Q1.15
#ifdef ENABLE_STEREO_HEADSET_EFFECTS
/*0xA2*/ u16 headsetPanRight;
/*0xA4*/ u16 headsetPanLeft;
/*0xA6*/ u16 prevHeadsetPanRight;
/*0xA8*/ u16 prevHeadsetPanLeft;
/* */ u8 align16Padding[0x06];
#else
/* */ u8 align16Padding[0x0E];
#endif
}; // size = 0xB0
#endif
struct NoteSynthesisBuffers {
s16 adpcmdecState[0x10];
s16 finalResampleState[0x10];
#ifdef VERSION_SH
s16 unk[0x10];
s16 filterBuffer[0x20];
s16 panSamplesBuffer[0x20];
#else
s16 mixEnvelopeState[0x28];
s16 panResampleState[0x10];
s16 panSamplesBuffer[0x20];
s16 dummyResampleState[0x10];
#if defined(VERSION_JP) || defined(VERSION_US)
s16 samples[0x40];
#endif
#endif
};
#ifdef BETTER_REVERB
struct BetterReverbSettings {
u8 useLightweightSettings;
s8 downsampleRate;
u8 isMono;
u8 filterCount;
s16 windowSize;
s16 gain;
u8 gainIndex;
u8 reverbIndex;
u32 *delaysL;
u32 *delaysR;
u8 *reverbMultsL;
u8 *reverbMultsR;
};
#endif
#ifdef VERSION_EU
struct ReverbSettingsEU {
u8 downsampleRate;
u8 windowSize; // To be multiplied by 64
u16 gain;
};
#else
struct ReverbSettingsUS {
u8 downsampleRate;
u16 windowSize;
u16 gain;
};
struct ReverbSettingsEU {
u8 downsampleRate; // always 1
u8 windowSize; // To be multiplied by 16
u16 gain;
u16 unk4; // always zero
u16 unk6; // always zero
s8 unk8; // always -1
u16 unkA; // always 0x3000
s16 unkC; // always zero
s16 unkE; // always zero
};
#endif
struct AudioSessionSettingsEU {
/* 0x00 */ u32 frequency;
/* 0x04 */ u8 unk1; // always 1
/* 0x05 */ u8 maxSimultaneousNotes;
/* 0x06 */ u8 numReverbs; // always 1
/* 0x07 */ u8 unk2; // always 0
/* 0x08 */ struct ReverbSettingsEU *reverbSettings;
/* 0x0C */ u16 volume;
/* 0x0E */ u16 unk3; // always 0
/* 0x10 */ u32 persistentSeqMem;
/* 0x14 */ u32 persistentBankMem;
#ifdef VERSION_SH
/* 0x18 */ u32 unk18; // always 0
#endif
/* 0x18, 0x1C */ u32 temporarySeqMem;
/* 0x1C, 0x20 */ u32 temporaryBankMem;
#ifdef VERSION_SH
/* 0x24 */ u32 unk24; // always 0
/* 0x28 */ u32 unkMem28; // always 0
/* 0x2C */ u32 unkMem2C; // always 0
#endif
}; // 0x30 on shindou
struct AudioSessionSettings {
/*0x00*/ u32 frequency;
/*0x04*/ u8 maxSimultaneousNotes;
/*0x06*/ u16 volume;
/*0x08*/ u32 persistentSeqMem;
/*0x0C*/ u32 persistentBankMem;
/*0x10*/ u32 temporarySeqMem;
/*0x14*/ u32 temporaryBankMem;
}; // size = 0x18
struct AudioBufferParametersEU {
/*0x00*/ s16 presetUnk4; // audio frames per vsync?
/*0x02*/ u16 frequency;
/*0x04*/ u16 aiFrequency; // ?16
/*0x06*/ s16 samplesPerFrameTarget;
/*0x08*/ s16 maxAiBufferLength;
/*0x0A*/ s16 minAiBufferLength;
/*0x0C*/ s16 updatesPerFrame;
/*0x0E*/ s16 samplesPerUpdate;
/*0x10*/ s16 samplesPerUpdateMax;
/*0x12*/ s16 samplesPerUpdateMin;
/*0x14*/ f32 resampleRate; // contains 32000.0f / frequency
/*0x18*/ f32 updatesPerFrameInv; // 1.0f / updatesPerFrame
/*0x1C*/ f32 unkUpdatesPerFrameScaled; // 3.0f / (1280.0f * updatesPerFrame)
};
struct EuAudioCmd {
union {
#if IS_BIG_ENDIAN
struct {
u8 op;
u8 arg1;
u8 arg2;
u8 arg3;
} s;
#else
struct {
u8 arg3;
u8 arg2;
u8 arg1;
u8 op;
} s;
#endif
s32 first;
} u;
union {
s32 as_s32;
u32 as_u32;
f32 as_f32;
#if IS_BIG_ENDIAN
u8 as_u8;
s8 as_s8;
#else
struct {
u8 pad0[3];
u8 as_u8;
};
struct {
u8 pad1[3];
s8 as_s8;
};
#endif
} u2;
};
#ifdef VERSION_SH
struct PendingDmaSample {
u8 medium;
u8 bankId;
u8 idx;
uintptr_t devAddr;
void *vAddr;
u8 *resultSampleAddr;
s32 state;
s32 remaining;
s8 *io;
/*0x1C*/ struct AudioBankSample sample;
/*0x2C*/ OSMesgQueue queue;
/*0x44*/ OSMesg mesgs[1];
/*0x48*/ OSIoMesg ioMesg;
};
struct UnkStruct80343D00 {
u32 someIndex; // array into one of the two slots below
struct PendingDmaSample arr[2];
};
// in external.c
extern struct UnkStruct80343D00 D_SH_80343D00;
#endif
#endif // AUDIO_INTERNAL_H