Files
PokemonEmeraldSafariFrontier/src/bard_music.c
GriffinR 2f67b17571 Document bard music (#2053)
* Missing bard song limit constants
* Index bard sound arrays with word constants
* Document bard music
2025-01-17 17:54:38 +00:00

248 lines
11 KiB
C

#include "global.h"
#include "bard_music.h"
#include "easy_chat.h"
#include "constants/songs.h"
// Indicates that the previous sound should be held.
#define PREV_BARD_SOUND { .songId = NUM_PHONEME_SONGS }
// Invalid sound, indicates the end of the sounds for the word.
#define NULL_BARD_SOUND { .songId = PHONEME_ID_NONE }
#include "data/bard_music/pokemon.h"
#include "data/bard_music/moves.h"
#include "data/bard_music/trainer.h"
#include "data/bard_music/status.h"
#include "data/bard_music/battle.h"
#include "data/bard_music/greetings.h"
#include "data/bard_music/people.h"
#include "data/bard_music/voices.h"
#include "data/bard_music/speech.h"
#include "data/bard_music/endings.h"
#include "data/bard_music/feelings.h"
#include "data/bard_music/conditions.h"
#include "data/bard_music/actions.h"
#include "data/bard_music/lifestyle.h"
#include "data/bard_music/hobbies.h"
#include "data/bard_music/time.h"
#include "data/bard_music/misc.h"
#include "data/bard_music/adjectives.h"
#include "data/bard_music/events.h"
#include "data/bard_music/trendysaying.h"
static const struct BardSoundTemplate (*const sBardSoundTemplatesTable[EC_NUM_GROUPS])[MAX_BARD_SOUNDS_PER_WORD] = {
[EC_GROUP_POKEMON] = NULL, // Handled by sBardSoundTemplates_Pokemon
[EC_GROUP_TRAINER] = sBardSoundTemplates_Trainer,
[EC_GROUP_STATUS] = sBardSoundTemplates_Status,
[EC_GROUP_BATTLE] = sBardSoundTemplates_Battle,
[EC_GROUP_GREETINGS] = sBardSoundTemplates_Greetings,
[EC_GROUP_PEOPLE] = sBardSoundTemplates_People,
[EC_GROUP_VOICES] = sBardSoundTemplates_Voices,
[EC_GROUP_SPEECH] = sBardSoundTemplates_Speech,
[EC_GROUP_ENDINGS] = sBardSoundTemplates_Endings,
[EC_GROUP_FEELINGS] = sBardSoundTemplates_Feelings,
[EC_GROUP_CONDITIONS] = sBardSoundTemplates_Conditions,
[EC_GROUP_ACTIONS] = sBardSoundTemplates_Actions,
[EC_GROUP_LIFESTYLE] = sBardSoundTemplates_Lifestyle,
[EC_GROUP_HOBBIES] = sBardSoundTemplates_Hobbies,
[EC_GROUP_TIME] = sBardSoundTemplates_Time,
[EC_GROUP_MISC] = sBardSoundTemplates_Misc,
[EC_GROUP_ADJECTIVES] = sBardSoundTemplates_Adjectives,
[EC_GROUP_EVENTS] = sBardSoundTemplates_Events,
[EC_GROUP_MOVE_1] = NULL, // Handled by sBardSoundTemplates_Moves
[EC_GROUP_MOVE_2] = NULL, // Handled by sBardSoundTemplates_Moves
[EC_GROUP_TRENDY_SAYING] = sBardSoundTemplates_TrendySaying,
[EC_GROUP_POKEMON_NATIONAL] = NULL, // Handled by sBardSoundTemplates_Pokemon
};
// The pitch tables below will be indexed using the number of BardSoundTemplates per word, so a table is selected
// that has at least MAX_BARD_SOUNDS_PER_WORD pitch values. Curiously they select pitch tables whose size is +1
// of the maximum number of sounds per word, so the final pitch value (before PITCH_END) isn't used.
// (i.e., 'NUM_BARD_PITCH_TABLES_PER_SIZE * (MAX_BARD_SOUNDS_PER_WORD-1)' would select a sufficiently large table).
#define BASE_PITCH_TABLE_INDEX (NUM_BARD_PITCH_TABLES_PER_SIZE * MAX_BARD_SOUNDS_PER_WORD)
#define PITCH_END 0x1800
static const s16 sPitch1_0[] = { -0x300, PITCH_END };
static const s16 sPitch1_1[] = { 0x0900, PITCH_END };
static const s16 sPitch1_2[] = { 0x0100, PITCH_END };
static const s16 sPitch1_3[] = { 0x0400, PITCH_END };
static const s16 sPitch1_4[] = { 0x0b00, PITCH_END };
static const s16 sPitch2_0[] = { -0x300, -0x100, PITCH_END };
static const s16 sPitch2_1[] = { -0x300, 0x0200, PITCH_END };
static const s16 sPitch2_2[] = { 0x0200, 0x0400, PITCH_END };
static const s16 sPitch2_3[] = { 0x0600, 0x0800, PITCH_END };
static const s16 sPitch2_4[] = { 0x0900, 0x0800, PITCH_END };
static const s16 sPitch3_0[] = { -0x300, -0x100, -0x300, PITCH_END };
static const s16 sPitch3_1[] = { 0x0400, -0x300, 0x0400, PITCH_END };
static const s16 sPitch3_2[] = { 0x0900, 0x0800, 0x0600, PITCH_END };
static const s16 sPitch3_3[] = { 0x0100, 0x0200, 0x0400, PITCH_END };
static const s16 sPitch3_4[] = { 0x0600, 0x1000, 0x0d00, PITCH_END };
static const s16 sPitch4_0[] = { 0x0400, 0x0900, 0x0400, 0x0900, PITCH_END };
static const s16 sPitch4_1[] = { 0x0900, 0x0400, 0x0d00, 0x0400, PITCH_END };
static const s16 sPitch4_2[] = { 0x0100, 0x0200, 0x0400, 0x0600, PITCH_END };
static const s16 sPitch4_3[] = { 0x0800, 0x0600, 0x0400, 0x0200, PITCH_END };
static const s16 sPitch4_4[] = { 0x0f00, 0x0d00, 0x0b00, 0x0a00, PITCH_END };
static const s16 sPitch5_0[] = { -0x300, -0x100, 0x0100, 0x0200, 0x0400, PITCH_END };
static const s16 sPitch5_1[] = { 0x0900, 0x0800, 0x0600, 0x0400, 0x0200, PITCH_END };
static const s16 sPitch5_2[] = { 0x0100, 0x0400, 0x0900, 0x0400, 0x0100, PITCH_END };
static const s16 sPitch5_3[] = { 0x0900, 0x0400, 0x0900, 0x0400, -0x300, PITCH_END };
static const s16 sPitch5_4[] = { 0x0b00, 0x0800, 0x0400, 0x0400, 0x0600, PITCH_END };
static const s16 sPitch6_0[] = { -0x300, -0x100, 0x0100, 0x0200, 0x0400, 0x0600, PITCH_END };
static const s16 sPitch6_1[] = { 0x0800, 0x0600, 0x0400, 0x0200, 0x0100, -0x100, PITCH_END };
static const s16 sPitch6_2[] = { 0x0100, 0x0200, 0x0400, 0x0100, 0x0200, 0x1000, PITCH_END };
static const s16 sPitch6_3[] = { 0x0400, -0x300, 0x0900, 0x0400, 0x0900, 0x0400, PITCH_END };
static const s16 sPitch6_4[] = { 0x0800, 0x0900, 0x0800, 0x0900, 0x0800, 0x0900, PITCH_END };
static const s16 sPitch7_0[] = { 0x0200, 0x0100, 0x0200, 0x0100, 0x0200, 0x0400, 0x0200, PITCH_END };
static const s16 sPitch7_1[] = { 0x0100, 0x0100, -0x100, -0x100, -0x300, 0x0400, -0x300, PITCH_END };
static const s16 sPitch7_2[] = { 0x0800, 0x0900, 0x0b00, 0x0d00, 0x0e00, 0x0d00, 0x0b00, PITCH_END };
static const s16 sPitch7_3[] = { 0x0800, 0x0600, 0x0400, 0x0200, 0x0d00, 0x0b00, 0x0900, PITCH_END };
static const s16 sPitch7_4[] = { 0x0300, 0x0400, 0x0600, 0x0800, 0x0700, 0x0800, 0x0400, PITCH_END };
// In practice only sPitch7_# below are used below.
// BASE_PITCH_TABLE_INDEX is 30 by default, and this table is always indexed with (x + 30), where x is some value 0 - 4
static const s16 *const sPitchTables[NUM_BARD_PITCH_TABLES_PER_SIZE * 7] = {
sPitch1_0, sPitch1_1, sPitch1_2, sPitch1_3, sPitch1_4,
sPitch2_0, sPitch2_1, sPitch2_2, sPitch2_3, sPitch2_4,
sPitch3_0, sPitch3_1, sPitch3_2, sPitch3_3, sPitch3_4,
sPitch4_0, sPitch4_1, sPitch4_2, sPitch4_3, sPitch4_4,
sPitch5_0, sPitch5_1, sPitch5_2, sPitch5_3, sPitch5_4,
sPitch6_0, sPitch6_1, sPitch6_2, sPitch6_3, sPitch6_4,
sPitch7_0, sPitch7_1, sPitch7_2, sPitch7_3, sPitch7_4
};
// If this fails, CalcWordSounds will likely read out of bounds for sPitchTables.
STATIC_ASSERT(BASE_PITCH_TABLE_INDEX + (NUM_BARD_PITCH_TABLES_PER_SIZE-1) < ARRAY_COUNT(sPitchTables), NotEnoughPitchTablesForBardSounds)
static const struct BardSoundTemplate sEmptyPhonemeTemplate[] = {
NULL_BARD_SOUND,
NULL_BARD_SOUND,
NULL_BARD_SOUND,
NULL_BARD_SOUND,
NULL_BARD_SOUND,
NULL_BARD_SOUND
};
static const int sPhonemeLengths[NUM_PHONEME_SONGS + 1] = {
[PHONEME_ID(PH_TRAP_BLEND)] = 9,
[PHONEME_ID(PH_TRAP_HELD)] = 22,
[PHONEME_ID(PH_TRAP_SOLO)] = 15,
[PHONEME_ID(PH_FACE_BLEND)] = 16,
[PHONEME_ID(PH_FACE_HELD)] = 39,
[PHONEME_ID(PH_FACE_SOLO)] = 21,
[PHONEME_ID(PH_CLOTH_BLEND)] = 9,
[PHONEME_ID(PH_CLOTH_HELD)] = 30,
[PHONEME_ID(PH_CLOTH_SOLO)] = 24,
[PHONEME_ID(PH_DRESS_BLEND)] = 15,
[PHONEME_ID(PH_DRESS_HELD)] = 25,
[PHONEME_ID(PH_DRESS_SOLO)] = 12,
[PHONEME_ID(PH_FLEECE_BLEND)] = 22,
[PHONEME_ID(PH_FLEECE_HELD)] = 45,
[PHONEME_ID(PH_FLEECE_SOLO)] = 24,
[PHONEME_ID(PH_KIT_BLEND)] = 15,
[PHONEME_ID(PH_KIT_HELD)] = 40,
[PHONEME_ID(PH_KIT_SOLO)] = 9,
[PHONEME_ID(PH_PRICE_BLEND)] = 21,
[PHONEME_ID(PH_PRICE_HELD)] = 42,
[PHONEME_ID(PH_PRICE_SOLO)] = 18,
[PHONEME_ID(PH_LOT_BLEND)] = 9,
[PHONEME_ID(PH_LOT_HELD)] = 22,
[PHONEME_ID(PH_LOT_SOLO)] = 15,
[PHONEME_ID(PH_GOAT_BLEND)] = 27,
[PHONEME_ID(PH_GOAT_HELD)] = 48,
[PHONEME_ID(PH_GOAT_SOLO)] = 18,
[PHONEME_ID(PH_THOUGHT_BLEND)] = 27,
[PHONEME_ID(PH_THOUGHT_HELD)] = 33,
[PHONEME_ID(PH_THOUGHT_SOLO)] = 24,
[PHONEME_ID(PH_CHOICE_BLEND)] = 25,
[PHONEME_ID(PH_CHOICE_HELD)] = 39,
[PHONEME_ID(PH_CHOICE_SOLO)] = 19,
[PHONEME_ID(PH_MOUTH_BLEND)] = 16,
[PHONEME_ID(PH_MOUTH_HELD)] = 54,
[PHONEME_ID(PH_MOUTH_SOLO)] = 18,
[PHONEME_ID(PH_FOOT_BLEND)] = 9,
[PHONEME_ID(PH_FOOT_HELD)] = 45,
[PHONEME_ID(PH_FOOT_SOLO)] = 15,
[PHONEME_ID(PH_GOOSE_BLEND)] = 12,
[PHONEME_ID(PH_GOOSE_HELD)] = 39,
[PHONEME_ID(PH_GOOSE_SOLO)] = 23,
[PHONEME_ID(PH_STRUT_BLEND)] = 5,
[PHONEME_ID(PH_STRUT_HELD)] = 45,
[PHONEME_ID(PH_STRUT_SOLO)] = 12,
[PHONEME_ID(PH_CURE_BLEND)] = 21,
[PHONEME_ID(PH_CURE_HELD)] = 48,
[PHONEME_ID(PH_CURE_SOLO)] = 12,
[PHONEME_ID(PH_NURSE_BLEND)] = 21,
[PHONEME_ID(PH_NURSE_HELD)] = 69,
[PHONEME_ID(PH_NURSE_SOLO)] = 18,
[NUM_PHONEME_SONGS] = 15, // This is the length that will be used by PREV_BARD_SOUND to hold the previous phoneme sound.
};
static s16 GetWordPitch(int tableIndex, int pitchIndex)
{
return sPitchTables[tableIndex][pitchIndex];
}
const struct BardSoundTemplate *GetWordSoundTemplates(u16 easyChatWord)
{
u32 category;
u32 subword;
const struct BardSoundTemplate (*ptr)[MAX_BARD_SOUNDS_PER_WORD];
if (IsBardWordInvalid(easyChatWord))
return sEmptyPhonemeTemplate;
category = EC_GROUP(easyChatWord);
subword = EC_INDEX(easyChatWord);
switch (category)
{
case EC_GROUP_POKEMON:
case EC_GROUP_POKEMON_NATIONAL:
ptr = sBardSoundTemplates_Pokemon;
break;
case EC_GROUP_MOVE_1:
case EC_GROUP_MOVE_2:
ptr = sBardSoundTemplates_Moves;
break;
default:
ptr = sBardSoundTemplatesTable[category];
break;
}
ptr += subword;
return *ptr;
}
// Assumes that 'soundTemplates' has already been loaded with the BardSoundTemplates for the easy chat word to calculate sounds for.
// 'pitchTableIndex' is chosen depending on the easy chat word, but is essentially an arbitrary value 0-4.
void CalcWordSounds(struct BardSong *song, u16 pitchTableIndex)
{
int i;
const struct BardSoundTemplate *template;
song->length = 0;
for (i = 0; i < MAX_BARD_SOUNDS_PER_WORD; i ++)
{
template = &song->soundTemplates[i];
if (template->songId != PHONEME_ID_NONE)
{
// Calculate the length and pitch of each phoneme in this word.
// A phoneme's length is always the same, and depends on the phoneme song and any adjustments in the template.
// Its pitch changes depending on the easy chat word and where in the list of templates the phoneme appears.
song->sounds[i].length = template->lengthAdjustment + sPhonemeLengths[template->songId];
song->sounds[i].pitch = GetWordPitch(pitchTableIndex + BASE_PITCH_TABLE_INDEX, i);
// Add this phoneme's length to the total sound length for this word.
song->length += song->sounds[i].length;
}
}
song->soundIndex = 0;
song->voiceInflection = 0;
}