mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-09-01 08:22:58 -04:00
Audio res documentation, API cleanup, bug fixes
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
flowchart TD
|
||||
Start[Start Sound Effect]
|
||||
subgraph BST
|
||||
params[Look up sound parameters]
|
||||
end
|
||||
Start-->|Sound ID|BST
|
||||
|
||||
subgraph BSC
|
||||
bms_start[Look up BMS start]
|
||||
end
|
||||
Start-->|Sound ID|BSC
|
||||
|
||||
BSC-->|BMS data| bms[BMS interpreter]
|
||||
|
||||
subgraph IBNK
|
||||
bnk[Look up instrument parameters]
|
||||
end
|
||||
|
||||
bms-->|Plays notes| IBNK
|
||||
|
||||
subgraph WSYS
|
||||
wsys[Look up wave sample]
|
||||
end
|
||||
|
||||
IBNK-->|Wave ID|WSYS
|
||||
|
||||
|
||||
flowchart TD
|
||||
Start[Start Sound Effect]
|
||||
subgraph BST
|
||||
params[Look up sound parameters]
|
||||
end
|
||||
Start-->|Sound ID|BST
|
||||
|
||||
BST-->|File path| ast[Stream /AudioRes/Stream/xxx.ast from disc]
|
||||
|
||||
|
||||
flowchart TD
|
||||
Start[Start Sound Effect]
|
||||
subgraph BST
|
||||
params[Look up sound parameters]
|
||||
end
|
||||
Start-->|Sound ID|BST
|
||||
|
||||
BST-->|Resource ID| arc[Z2SoundSeqs.arc]
|
||||
arc-->|BMS file| bms[BMS interpreter]
|
||||
|
||||
subgraph IBNK
|
||||
bnk[Look up instrument parameters]
|
||||
end
|
||||
|
||||
bms-->|Plays notes| IBNK
|
||||
|
||||
subgraph WSYS
|
||||
wsys[Look up wave sample]
|
||||
end
|
||||
|
||||
IBNK-->|Wave ID|WSYS
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 22 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 21 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 15 KiB |
+2
-2
@@ -26,7 +26,7 @@ All audio data is stored in `/Audiores` on the disc. Files are as follows:
|
||||
|
||||
### `/Audiores/Seqs/Z2SoundSeqs.arc`
|
||||
|
||||
Contains the BMS instructions for all BMS-based music and sound effects. Not all data is kept in memory at once.
|
||||
Contains the BMS instructions for all BMS-based music. Not all data is kept in memory at once.
|
||||
|
||||
### `/Audiores/Stream/*.ast`
|
||||
|
||||
@@ -230,7 +230,7 @@ on scene changes in TP's game code.
|
||||
```
|
||||
u8 mPriority;
|
||||
u8 mVolume; // Converted to float: mVolume * (1.0/127.0)
|
||||
u16 mStreamPanParameters; // Bitpacked, two bits per channel determining whether a channel is center (00), left (01), or right (10).
|
||||
u16 mStreamPanParameters; // Bitpacked, two bits per channel determining whether a channel is center (01), left (10), or right (11).
|
||||
char* mStreamFilePath[] : u32; // File path to the .ast on disc.
|
||||
```
|
||||
|
||||
|
||||
@@ -13,14 +13,14 @@ MOD_EXPORT ModResult mod_initialize(ModError*) {
|
||||
AudioWaveHandle handle;
|
||||
svc_audio_res->replace_wave(
|
||||
mod_ctx,
|
||||
SoundEffects,
|
||||
AUDIO_WAVE_BANK_SOUND_EFFECTS,
|
||||
4238,
|
||||
"res/go.opus",
|
||||
nullptr,
|
||||
&handle);
|
||||
svc_audio_res->replace_wave(
|
||||
mod_ctx,
|
||||
SoundEffects,
|
||||
AUDIO_WAVE_BANK_SOUND_EFFECTS,
|
||||
4237,
|
||||
"res/go.opus",
|
||||
nullptr,
|
||||
|
||||
@@ -6,43 +6,117 @@
|
||||
#define AUDIO_RES_SERVICE_MAJOR 1u
|
||||
#define AUDIO_RES_SERVICE_MINOR 0u
|
||||
|
||||
/*
|
||||
* Defines APIs for replacing and adding audio resources.
|
||||
*
|
||||
* TP's audio engine (JAudioV2) is extremely complex. For more of an overview through its
|
||||
* functionality, please see docs/jaudio.md in the repo.
|
||||
*/
|
||||
|
||||
/*
|
||||
* WSYS structs
|
||||
*/
|
||||
|
||||
#define AUDIO_RES_DEFAULT_KEY 0x3C;
|
||||
|
||||
typedef enum AudioWaveBank : uint8_t {
|
||||
SoundEffects = 0,
|
||||
MusicSamples = 1,
|
||||
} AudioWaveBank;
|
||||
|
||||
typedef enum AudioWaveFormat : uint8_t {
|
||||
Adpcm4 = 0,
|
||||
Adpcm2 = 1,
|
||||
Pcm8 = 2,
|
||||
Pcm16 = 3,
|
||||
} AudioWaveFormat;
|
||||
|
||||
typedef uint64_t AudioWaveHandle;
|
||||
typedef uint64_t AudioSoundTableHandle;
|
||||
/**
|
||||
* "Default" key for wave definitions. Key like musical key, not map keys or whatever.
|
||||
*/
|
||||
#define AUDIO_RES_DEFAULT_KEY 0x3C
|
||||
|
||||
/**
|
||||
* Defines TP's raw wave banks, which can be modified.
|
||||
*/
|
||||
typedef enum AudioWaveBank : uint8_t {
|
||||
AUDIO_WAVE_BANK_SOUND_EFFECTS = 0,
|
||||
AUDIO_WAVE_BANK_MUSIC_SAMPLES = 1,
|
||||
} AudioWaveBank;
|
||||
|
||||
/**
|
||||
* Defines formats for raw sample data the DSP can play back.
|
||||
*/
|
||||
typedef enum AudioWaveFormat : uint8_t {
|
||||
/**
|
||||
* 16 samples per 9 bytes custom Nintendo ADPCM.
|
||||
*/
|
||||
AUDIO_WAVE_FORMAT_ADPCM4 = 0,
|
||||
|
||||
/**
|
||||
* 16 samples per 5 bytes custom Nintendo ADPCM.
|
||||
*/
|
||||
AUDIO_WAVE_FORMAT_ADPCM2 = 1,
|
||||
|
||||
/**
|
||||
* 1-byte-per-sample simple PCM.
|
||||
*/
|
||||
AUDIO_WAVE_FORMAT_PCM8 = 2,
|
||||
|
||||
/**
|
||||
* 2-byte-per-sample simple PCM.
|
||||
*/
|
||||
AUDIO_WAVE_FORMAT_PCM16 = 3,
|
||||
} AudioWaveFormat;
|
||||
|
||||
/**
|
||||
* Handle to reference wave replacements/additions by mods.
|
||||
*/
|
||||
typedef uint64_t AudioWaveHandle;
|
||||
|
||||
/**
|
||||
* Data needed to load a raw audio data file.
|
||||
*
|
||||
* @see AudioWaveInfo
|
||||
*/
|
||||
typedef struct AudioRawWave {
|
||||
/**
|
||||
* Format of the raw sample data.
|
||||
*/
|
||||
AudioWaveFormat format;
|
||||
|
||||
/**
|
||||
* Sample rate, in Hertz.
|
||||
*/
|
||||
float sample_rate;
|
||||
|
||||
/**
|
||||
* Last audio sample before ADPCM loop start point. (unused if not looping)
|
||||
*/
|
||||
int16_t sample_value_last;
|
||||
|
||||
/**
|
||||
* Second-to-last audio sample before ADPCM loop start point. (unused if not looping)
|
||||
*/
|
||||
int16_t sample_value_penult;
|
||||
} AudioRawWave;
|
||||
|
||||
/**
|
||||
* Metadata needed to play a wave.
|
||||
*/
|
||||
typedef struct AudioWaveInfo {
|
||||
/**
|
||||
* Base key (musical key). Set to AUDIO_RES_DEFAULT_KEY if you don't care.
|
||||
*/
|
||||
uint8_t base_key;
|
||||
|
||||
/**
|
||||
* If true, the sample loops.
|
||||
*/
|
||||
bool loop;
|
||||
|
||||
/**
|
||||
* Sample number where loop resumes when end is reached.
|
||||
* Does nothing if not looping.
|
||||
*/
|
||||
uint32_t loop_start_sample;
|
||||
|
||||
/**
|
||||
* End of the wave. Stops playback if not looping.
|
||||
* Automatically clamped to the sample count from the file, if greater.
|
||||
*/
|
||||
uint32_t loop_end_sample;
|
||||
|
||||
/**
|
||||
* If provided, specifies that the passed file is "raw" and only contains sample data.
|
||||
* This must be used if you want to ship ADPCM samples, as other containers do not support that.
|
||||
*/
|
||||
AudioRawWave const* raw_wave;
|
||||
} AudioWaveInfo;
|
||||
|
||||
@@ -50,6 +124,9 @@ typedef struct AudioWaveInfo {
|
||||
* BST structs
|
||||
*/
|
||||
|
||||
/**
|
||||
* Game defined categories (groups) for sound effects.
|
||||
*/
|
||||
typedef enum SoundEffectCategory : uint8_t {
|
||||
SE_CATEGORY_SYSTEM_SE,
|
||||
SE_CATEGORY_PLAYER_VOICE,
|
||||
@@ -63,44 +140,131 @@ typedef enum SoundEffectCategory : uint8_t {
|
||||
SE_CATEGORY_ENV_SE
|
||||
} SoundEffectCategory;
|
||||
|
||||
/**
|
||||
* Data that can be defined for a sound effect in the sound table.
|
||||
*/
|
||||
typedef struct AudioSoundTableEffectInfo {
|
||||
/**
|
||||
* Priority relative to other sound effects. Can affect culling and such if
|
||||
* many effects are playing.
|
||||
*/
|
||||
uint8_t priority;
|
||||
|
||||
/**
|
||||
* Volume multiplier for this sound effect. Clamped to range 0-2.
|
||||
*/
|
||||
float volume;
|
||||
|
||||
/**
|
||||
* Pitch multiplier for this sound effect.
|
||||
*/
|
||||
float pitch;
|
||||
|
||||
/**
|
||||
* If true, the effect is always treated as max priority, regardless of factors like distance.
|
||||
*/
|
||||
bool always_max_priority;
|
||||
|
||||
/**
|
||||
* Don't calculate volume changes by distance.
|
||||
*/
|
||||
bool ignore_distance_volume;
|
||||
|
||||
/**
|
||||
* Don't FX Mix (reverb) changes by distance.
|
||||
*/
|
||||
bool ignore_distance_fx_mix;
|
||||
|
||||
/**
|
||||
* Don't calculate panning (left/right balance).
|
||||
*/
|
||||
bool ignore_pan;
|
||||
|
||||
/**
|
||||
* Don't calculate dolby (front/back balance).
|
||||
*/
|
||||
bool ignore_dolby;
|
||||
|
||||
/**
|
||||
* 0-15 value controlling volume randomization strength.
|
||||
*/
|
||||
uint8_t random_volume;
|
||||
|
||||
/**
|
||||
* 0-15 value controlling pitch randomization strength.
|
||||
*/
|
||||
uint8_t random_pitch;
|
||||
|
||||
/**
|
||||
* 0-15 value controlling Doppler effect strength.
|
||||
*/
|
||||
uint8_t doppler_power;
|
||||
|
||||
/**
|
||||
* Value from 0-15 selecting "volume distance" class. This effectively selects a fixed curve for
|
||||
* parameters like volume by distance.
|
||||
*/
|
||||
uint8_t volume_dist_class;
|
||||
|
||||
/**
|
||||
* Limit minimum volume of sound (after distance drop-off) to 0.2.
|
||||
*/
|
||||
bool clamp_min_volume;
|
||||
|
||||
/**
|
||||
* Treat this sound as "far away" or "culled" at "max distance."
|
||||
* Affects things like automatic stopping.
|
||||
*/
|
||||
bool cull_at_max_distance;
|
||||
} AudioSoundTableEffectInfo;
|
||||
|
||||
/**
|
||||
* Stereo panning parameters for stream channels.
|
||||
*/
|
||||
typedef enum StreamPan : uint8_t {
|
||||
STREAM_PAN_CENTER,
|
||||
STREAM_PAN_LEFT,
|
||||
STREAM_PAN_RIGHT,
|
||||
} StreamPan;
|
||||
|
||||
/**
|
||||
* Maximum amount of channels a streamed track can have.
|
||||
*/
|
||||
#define STREAM_MAX_CHILDREN 6
|
||||
|
||||
/**
|
||||
* Data that can be defined for a stream in the sound table.
|
||||
*/
|
||||
typedef struct AudioSoundTableStreamInfo {
|
||||
/**
|
||||
* Unsure if used.
|
||||
*/
|
||||
uint8_t priority;
|
||||
|
||||
/**
|
||||
* Volume multiplier for this stream. Clamped to range 0-2.
|
||||
*/
|
||||
float volume;
|
||||
|
||||
/**
|
||||
* For each channel in the loaded .ast file, specifies the panning position for said channel.
|
||||
*/
|
||||
StreamPan pan_parameters[STREAM_MAX_CHILDREN];
|
||||
char const* file_path;
|
||||
|
||||
/**
|
||||
* Whether this stream automatically stops on scene change.
|
||||
*/
|
||||
bool stop_on_scene_change;
|
||||
} AudioSoundTableStreamInfo;
|
||||
|
||||
/**
|
||||
* Handle to reference sound table replacements/additions by mods.
|
||||
*/
|
||||
typedef uint64_t AudioSoundTableHandle;
|
||||
|
||||
/**
|
||||
* Defines APIs for replacing and adding audio resources.
|
||||
*/
|
||||
typedef struct AudioResService {
|
||||
ServiceHeader header;
|
||||
|
||||
@@ -108,6 +272,32 @@ typedef struct AudioResService {
|
||||
* WSYS API
|
||||
*/
|
||||
|
||||
/**
|
||||
* Default wave info if none is provided: default key, no loop, not raw audio.
|
||||
*/
|
||||
AudioWaveInfo const* default_wave_info;
|
||||
|
||||
/**
|
||||
* Replace an existing audio wave in the game. This replacement will follow mod order
|
||||
* prioritization.
|
||||
*
|
||||
* The sound effect will remain permanently resident in memory.
|
||||
*
|
||||
* Sound effects can be provided as raw samples (if raw_wave data is provided in wave_info),
|
||||
* WAVE (.wav) file, or OGG Opus (.opus, if Dusklight is compiled with support). Container
|
||||
* formats are detected based on header, not based on file name.
|
||||
*
|
||||
* @param ctx Pointer to your mod's context.
|
||||
* @param bank Which wave bank to replace a sound effect in.
|
||||
* @param wave_id ID of the wave to replace. This does *not* directly correlate to sound table
|
||||
* entries or JAISound values in any way!
|
||||
* @param file_name Path of the audio file to load in the mod's data, e.g. res/foo.opus. This
|
||||
* does *not* need to be an overlay file!
|
||||
* @param wave_info Optional: metadata for the new wave. If not provided will use reasonable
|
||||
* defaults (@ref default_wave_info).
|
||||
* @param out_handle Optional: pointer receives the handle for the replacement. This can be used
|
||||
* with @ref remove_wave.
|
||||
*/
|
||||
ModResult (*replace_wave)(
|
||||
ModContext* ctx,
|
||||
AudioWaveBank bank,
|
||||
@@ -116,6 +306,22 @@ typedef struct AudioResService {
|
||||
AudioWaveInfo const* wave_info,
|
||||
AudioWaveHandle* out_handle);
|
||||
|
||||
/**
|
||||
* Add a new audio wave to the game. The service allocates the placed ID for you.
|
||||
*
|
||||
* The sound effect will remain permanently resident in memory.
|
||||
*
|
||||
* Sound effects can be provided as raw samples (if raw_wave data is provided in wave_info),
|
||||
* WAVE (.wav) file, or OGG Opus (.opus, if Dusklight is compiled with support). Container
|
||||
* formats are detected based on header, not based on file name.
|
||||
*
|
||||
* @param ctx Pointer to your mod's context.
|
||||
* @param bank Which wave bank to replace a sound effect in.
|
||||
* @param file_name Path of the audio file to load in the mod's data, e.g. res/foo.opus. This does *not* need to be an overlay file!
|
||||
* @param wave_info Optional: metadata for the new wave. If not provided will use reasonable defaults (@ref default_wave_info).
|
||||
* @param out_handle Optional: pointer receives the handle for the addition. This can be used with @ref remove_wave.
|
||||
* @param out_wave_id Receives the allocated ID in the wave bank.
|
||||
*/
|
||||
ModResult (*add_wave)(
|
||||
ModContext* ctx,
|
||||
AudioWaveBank bank,
|
||||
@@ -124,12 +330,33 @@ typedef struct AudioResService {
|
||||
AudioWaveHandle* out_handle,
|
||||
uint16_t* out_wave_id);
|
||||
|
||||
/**
|
||||
* Remove a wave addition/replacement previously created by this mod.
|
||||
*
|
||||
* @param ctx Pointer to your mod's context.
|
||||
* @param handle The handle identifying which wave to remove.
|
||||
*/
|
||||
ModResult (*remove_wave)(ModContext* ctx, AudioWaveHandle handle);
|
||||
|
||||
/*
|
||||
* BST API
|
||||
*/
|
||||
|
||||
/**
|
||||
* Default sound effect info if none is provided: default volume, pitch, medium priority.
|
||||
*/
|
||||
AudioSoundTableEffectInfo const* default_effect_info;
|
||||
|
||||
/**
|
||||
* Replace a sound effect's parameters in the sound table. This replacement will follow mod order
|
||||
* prioritization.
|
||||
*
|
||||
* @param ctx Pointer to your mod's context.
|
||||
* @param category_id Category of the sound effect.
|
||||
* @param effect_id ID of the effect.
|
||||
* @param info Optional: new parameters for the sound. Falls back to @ref default_effect_info if not provided.
|
||||
* @param out_handle Optional: pointer receives the handle for the replacement. This can be used with @ref remove_sound_table.
|
||||
*/
|
||||
ModResult (*replace_sound_table_effect)(
|
||||
ModContext* ctx,
|
||||
SoundEffectCategory category_id,
|
||||
@@ -137,6 +364,15 @@ typedef struct AudioResService {
|
||||
AudioSoundTableEffectInfo const* info,
|
||||
AudioSoundTableHandle* out_handle);
|
||||
|
||||
/**
|
||||
* Add a sound effect to the sound table. The service allocates the placed ID for you.
|
||||
*
|
||||
* @param ctx Pointer to your mod's context.
|
||||
* @param category_id Category of the sound effect.
|
||||
* @param info Optional: new parameters for the sound. Falls back to @ref default_effect_info if not provided.
|
||||
* @param out_handle Optional: pointer receives the handle for the replacement. This can be used with @ref remove_sound_table.
|
||||
* @param out_effect_id Receives the allocated ID of the sound effect.
|
||||
*/
|
||||
ModResult (*add_sound_table_effect)(
|
||||
ModContext* ctx,
|
||||
SoundEffectCategory category_id,
|
||||
@@ -144,18 +380,50 @@ typedef struct AudioResService {
|
||||
AudioSoundTableHandle* out_handle,
|
||||
uint16_t* out_effect_id);
|
||||
|
||||
/**
|
||||
* Default sound effect info if none is provided: default volume, medium priority, stereo channels, *no* stop on scene change.
|
||||
*/
|
||||
AudioSoundTableStreamInfo const* default_stream_info;
|
||||
|
||||
/**
|
||||
* Replace a sound effect's parameters in the sound table. This replacement will follow mod order
|
||||
* prioritization.
|
||||
*
|
||||
* @param ctx Pointer to your mod's context.
|
||||
* @param stream_id ID of the stream.
|
||||
* @param file_path Path of the .ast on disc. If you're providing one yourself, use an overlay!
|
||||
* @param info Optional: new parameters for the stream. Falls back to @ref default_stream_info if not provided.
|
||||
* @param out_handle Optional: pointer receives the handle for the replacement. This can be used with @ref remove_sound_table.
|
||||
*/
|
||||
ModResult (*replace_sound_table_stream)(
|
||||
ModContext* ctx,
|
||||
uint16_t stream_id,
|
||||
char const* file_path,
|
||||
AudioSoundTableStreamInfo const* info,
|
||||
AudioSoundTableHandle* out_handle);
|
||||
|
||||
/**
|
||||
* Add a sound effect to the sound table. The service allocates the placed ID for you.
|
||||
*
|
||||
* @param ctx Pointer to your mod's context.
|
||||
* @param file_path Path of the .ast on disc. If you're providing one yourself, use an overlay!
|
||||
* @param info Optional: new parameters for the stream. Falls back to @ref default_stream_info if not provided.
|
||||
* @param out_handle Optional: pointer receives the handle for the replacement. This can be used with @ref remove_sound_table.
|
||||
* @param out_stream_id Receives the allocated ID of the stream.
|
||||
*/
|
||||
ModResult (*add_sound_table_stream)(
|
||||
ModContext* ctx,
|
||||
char const* file_path,
|
||||
AudioSoundTableStreamInfo const* info,
|
||||
AudioSoundTableHandle* out_handle,
|
||||
uint16_t* out_stream_id);
|
||||
|
||||
/**
|
||||
* Remove a sound table addition/replacement previously created by this mod.
|
||||
*
|
||||
* @param ctx Pointer to your mod's context.
|
||||
* @param handle The handle identifying which wave to remove.
|
||||
*/
|
||||
ModResult (*remove_sound_table)(ModContext* ctx, AudioSoundTableHandle handle);
|
||||
} AudioResService;
|
||||
|
||||
|
||||
@@ -16,11 +16,14 @@ using namespace dusk::helpers::cast;
|
||||
|
||||
constexpr AudioResService s_audioResService{
|
||||
.header = SERVICE_HEADER(AudioResService, AUDIO_RES_SERVICE_MAJOR, AUDIO_RES_SERVICE_MINOR),
|
||||
.default_wave_info = &wsys::default_wave_info,
|
||||
.replace_wave = &wsys::insert_replace_wave,
|
||||
.add_wave = &wsys::insert_add_wave,
|
||||
.remove_wave = &wsys::remove_wave,
|
||||
.default_effect_info = &bst::default_effect_info,
|
||||
.replace_sound_table_effect = &bst::replace_sound_table_effect,
|
||||
.add_sound_table_effect = &bst::add_sound_table_effect,
|
||||
.default_stream_info = &bst::default_stream_info,
|
||||
.replace_sound_table_stream = &bst::replace_sound_table_stream,
|
||||
.add_sound_table_stream = &bst::add_sound_table_stream,
|
||||
.remove_sound_table = &bst::remove_sound_table,
|
||||
|
||||
@@ -35,6 +35,7 @@ bool validate_category(SoundEffectCategory const category) {
|
||||
}
|
||||
|
||||
uint8_t volume_to_item(float volume) {
|
||||
volume = std::clamp(volume, 0.0f, 2.0f);
|
||||
return static_cast<uint8_t>(volume * 127);
|
||||
}
|
||||
|
||||
@@ -90,8 +91,8 @@ u8 SoundEffectReplacementSlot::get_type_id() const {
|
||||
}
|
||||
|
||||
StreamReplacementSlot::StreamReplacementSlot(
|
||||
bool mod_defined, u16 id, const AudioSoundTableStreamInfo& info)
|
||||
: SoundTableReplacementSlot(mod_defined, id), file_path(info.file_path) {
|
||||
bool mod_defined, u16 id, char const* file_path, const AudioSoundTableStreamInfo& info)
|
||||
: SoundTableReplacementSlot(mod_defined, id), file_path(file_path) {
|
||||
stop_on_scene_change = info.stop_on_scene_change;
|
||||
item.mPriority = info.priority;
|
||||
item.mVolume = volume_to_item(info.volume);
|
||||
@@ -216,10 +217,14 @@ static ModResult insert_sound_table_effect_core(ModContext* ctx, SoundEffectCate
|
||||
}
|
||||
|
||||
auto mod = mod_from_context(ctx);
|
||||
if (mod == nullptr || info == nullptr || !validate_category(category_id)) {
|
||||
if (mod == nullptr || !validate_category(category_id)) {
|
||||
return MOD_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (info == nullptr) {
|
||||
info = &default_effect_info;
|
||||
}
|
||||
|
||||
auto slot = std::make_shared<SoundEffectReplacementSlot>(mod_defined, effect_id, category_id, *info);
|
||||
sound_replacements_dirty = true;
|
||||
|
||||
@@ -236,6 +241,8 @@ ModResult replace_sound_table_effect(ModContext* ctx, SoundEffectCategory catego
|
||||
return insert_sound_table_effect_core(ctx, category_id, effect_id, false, info, out_handle);
|
||||
}
|
||||
|
||||
AudioSoundTableEffectInfo const default_effect_info(128, 1, 1);
|
||||
|
||||
ModResult add_sound_table_effect(ModContext* ctx, SoundEffectCategory category_id,
|
||||
AudioSoundTableEffectInfo const* info, AudioSoundTableHandle* out_handle,
|
||||
uint16_t* out_effect_id) {
|
||||
@@ -263,6 +270,7 @@ static ModResult insert_sound_table_stream_core(
|
||||
ModContext* ctx,
|
||||
uint16_t stream_id,
|
||||
bool mod_defined,
|
||||
char const* file_path,
|
||||
AudioSoundTableStreamInfo const* info,
|
||||
AudioSoundTableHandle* out_handle) {
|
||||
if (out_handle != nullptr) {
|
||||
@@ -270,11 +278,15 @@ static ModResult insert_sound_table_stream_core(
|
||||
}
|
||||
|
||||
auto mod = mod_from_context(ctx);
|
||||
if (mod == nullptr || info == nullptr) {
|
||||
if (mod == nullptr || file_path == nullptr) {
|
||||
return MOD_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
auto slot = std::make_shared<StreamReplacementSlot>(mod_defined, stream_id, *info);
|
||||
if (info == nullptr) {
|
||||
info = &default_stream_info;
|
||||
}
|
||||
|
||||
auto slot = std::make_shared<StreamReplacementSlot>(mod_defined, stream_id, file_path, *info);
|
||||
sound_replacements_dirty = true;
|
||||
|
||||
auto const handle = sound_replacements.emplace(*mod, std::move(slot));
|
||||
@@ -285,12 +297,22 @@ static ModResult insert_sound_table_stream_core(
|
||||
return MOD_OK;
|
||||
}
|
||||
|
||||
AudioSoundTableStreamInfo const default_stream_info(
|
||||
128,
|
||||
1,
|
||||
{
|
||||
STREAM_PAN_LEFT,
|
||||
STREAM_PAN_RIGHT
|
||||
});
|
||||
|
||||
ModResult replace_sound_table_stream(ModContext* ctx, uint16_t stream_id,
|
||||
char const* file_path,
|
||||
AudioSoundTableStreamInfo const* info, AudioSoundTableHandle* out_handle) {
|
||||
return insert_sound_table_stream_core(ctx, stream_id, false, info, out_handle);
|
||||
return insert_sound_table_stream_core(ctx, stream_id, false, file_path, info, out_handle);
|
||||
}
|
||||
|
||||
ModResult add_sound_table_stream(ModContext* ctx, AudioSoundTableStreamInfo const* info,
|
||||
ModResult add_sound_table_stream(ModContext* ctx,
|
||||
char const* file_path, AudioSoundTableStreamInfo const* info,
|
||||
AudioSoundTableHandle* out_handle, uint16_t* out_stream_id) {
|
||||
if (out_stream_id == nullptr) {
|
||||
return MOD_INVALID_ARGUMENT;
|
||||
@@ -298,7 +320,7 @@ ModResult add_sound_table_stream(ModContext* ctx, AudioSoundTableStreamInfo cons
|
||||
|
||||
auto const stream_id = stream_id_allocator.alloc();
|
||||
|
||||
auto const result = insert_sound_table_stream_core(ctx, stream_id, true, info, out_handle);
|
||||
auto const result = insert_sound_table_stream_core(ctx, stream_id, true, file_path, info, out_handle);
|
||||
if (result != MOD_OK) {
|
||||
stream_id_allocator.free(stream_id);
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ struct StreamReplacementSlot final : SoundTableReplacementSlot {
|
||||
std::string file_path;
|
||||
bool stop_on_scene_change;
|
||||
|
||||
StreamReplacementSlot(bool mod_defined, u16 id, const AudioSoundTableStreamInfo& info);
|
||||
StreamReplacementSlot(bool mod_defined, u16 id, char const* file_path, const AudioSoundTableStreamInfo& info);
|
||||
|
||||
~StreamReplacementSlot() override = default;
|
||||
|
||||
@@ -68,6 +68,8 @@ void frame_end();
|
||||
void remove_mod(LoadedMod const& mod);
|
||||
void sync_audio_replacements();
|
||||
|
||||
extern AudioSoundTableEffectInfo const default_effect_info;
|
||||
|
||||
ModResult replace_sound_table_effect(
|
||||
ModContext* ctx,
|
||||
SoundEffectCategory category_id,
|
||||
@@ -82,14 +84,18 @@ ModResult add_sound_table_effect(
|
||||
AudioSoundTableHandle* out_handle,
|
||||
uint16_t* out_effect_id);
|
||||
|
||||
extern AudioSoundTableStreamInfo const default_stream_info;
|
||||
|
||||
ModResult replace_sound_table_stream(
|
||||
ModContext* ctx,
|
||||
uint16_t stream_id,
|
||||
char const* file_path,
|
||||
AudioSoundTableStreamInfo const* info,
|
||||
AudioSoundTableHandle* out_handle);
|
||||
|
||||
ModResult add_sound_table_stream(
|
||||
ModContext* ctx,
|
||||
char const* file_path,
|
||||
AudioSoundTableStreamInfo const* info,
|
||||
AudioSoundTableHandle* out_handle,
|
||||
uint16_t* out_stream_id);
|
||||
|
||||
@@ -84,7 +84,7 @@ ModResult load_opus(LoadedMod const& mod, RuntimeWaveReplacementSlot& slot, std:
|
||||
slot.data = std::make_shared<SampleDataPcm16>(std::move(pcm_buffer));
|
||||
slot.sample_rate = 48000; // Opus always decodes at 48 kHz.
|
||||
slot.sample_count = length;
|
||||
slot.format = Pcm16;
|
||||
slot.format = AUDIO_WAVE_FORMAT_PCM16;
|
||||
|
||||
return MOD_OK;
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ ModResult load_wav(LoadedMod const& mod, RuntimeWaveReplacementSlot& slot, std::
|
||||
|
||||
pcmData.be_swap();
|
||||
|
||||
slot.format = Pcm16;
|
||||
slot.format = AUDIO_WAVE_FORMAT_PCM16;
|
||||
slot.sample_count = pcmData.size() / sizeof(u16);
|
||||
slot.data = std::make_unique<SampleDataPcm16>(std::move(pcmData));
|
||||
|
||||
|
||||
@@ -29,18 +29,18 @@ PlainIdAllocator<u16> sound_effect_id_allocator(5'000);
|
||||
PlainIdAllocator<u16> music_sample_id_allocator(1'000);
|
||||
|
||||
PlainIdAllocator<u16>& id_allocator_for_bank(AudioWaveBank const bank) {
|
||||
return bank == SoundEffects ? sound_effect_id_allocator : music_sample_id_allocator;
|
||||
return bank == AUDIO_WAVE_BANK_SOUND_EFFECTS ? sound_effect_id_allocator : music_sample_id_allocator;
|
||||
}
|
||||
|
||||
bool validate_raw_size(LoadedMod const& mod, std::string const& path, uintptr_t actual_size, AudioRawWave const& raw, u32& sample_count) {
|
||||
u32 samples_per_block;
|
||||
u32 bytes_per_block;
|
||||
switch (raw.format) {
|
||||
case Adpcm4:
|
||||
case AUDIO_WAVE_FORMAT_ADPCM4:
|
||||
samples_per_block = 16;
|
||||
bytes_per_block = 9;
|
||||
break;
|
||||
case Pcm16:
|
||||
case AUDIO_WAVE_FORMAT_PCM16:
|
||||
samples_per_block = 1;
|
||||
bytes_per_block = 2;
|
||||
break;
|
||||
@@ -71,7 +71,7 @@ ModResult load_raw(
|
||||
return MOD_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (slot.format == Pcm16) {
|
||||
if (slot.format == AUDIO_WAVE_FORMAT_PCM16) {
|
||||
SampleDataPcm16 pcm16;
|
||||
pcm16.data.resize(sample_count);
|
||||
memcpy(pcm16.data.data(), file_contents.data(), file_contents.size());
|
||||
@@ -179,6 +179,15 @@ ModResult insert_replace_wave_core(
|
||||
slot.loop = wave_info->loop;
|
||||
slot.loop_start_sample = wave_info->loop_start_sample;
|
||||
slot.loop_end_sample = wave_info->loop_end_sample;
|
||||
|
||||
if (slot.loop_end_sample > slot.sample_count) {
|
||||
slot.loop_end_sample = slot.sample_count;
|
||||
}
|
||||
|
||||
if (slot.loop_start_sample >= slot.loop_end_sample) {
|
||||
Log.error("[{}] wave has start >= end", mod->metadata.id);
|
||||
return MOD_INVALID_ARGUMENT;
|
||||
}
|
||||
} else {
|
||||
slot.base_key = AUDIO_RES_DEFAULT_KEY;
|
||||
slot.loop = false;
|
||||
@@ -202,6 +211,8 @@ ModResult insert_replace_wave_core(
|
||||
absl::flat_hash_map<AudioWaveKey, AudioWaveReplacementValue> s_replacements;
|
||||
std::mutex s_replacements_mutex;
|
||||
|
||||
AudioWaveInfo const default_wave_info(AUDIO_RES_DEFAULT_KEY, false, 0, std::numeric_limits<u32>::max(), nullptr);
|
||||
|
||||
ModResult remove_wave(ModContext* ctx, AudioWaveHandle handle) {
|
||||
auto* mod = mod_from_context(ctx);
|
||||
if (mod == nullptr || handle == 0) {
|
||||
@@ -239,7 +250,7 @@ ModResult insert_add_wave(
|
||||
|
||||
*out_wave_id = 0;
|
||||
|
||||
if (bank != SoundEffects && bank != MusicSamples) {
|
||||
if (bank != AUDIO_WAVE_BANK_SOUND_EFFECTS && bank != AUDIO_WAVE_BANK_MUSIC_SAMPLES) {
|
||||
return MOD_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
|
||||
@@ -108,6 +108,8 @@ void frame_end();
|
||||
void remove_mod(LoadedMod& mod);
|
||||
void sync_audio_replacements();
|
||||
|
||||
extern AudioWaveInfo const default_wave_info;
|
||||
|
||||
ModResult insert_replace_wave(
|
||||
ModContext* ctx,
|
||||
AudioWaveBank bank,
|
||||
|
||||
Reference in New Issue
Block a user