From de7ad4b0d6dc23533a3c790472121bcfd175ce6f Mon Sep 17 00:00:00 2001
From: Isaac0-dev <62234577+Isaac0-dev@users.noreply.github.com>
Date: Sun, 3 May 2026 07:52:42 +1000
Subject: [PATCH 1/2] memory.c cleanup and optimisation (#1157)
## Low hanging fruit optimisations for the memory data structures in `memory.c`
Mostly generic performance improvements:
- Swapping `calloc` calls for `malloc` calls where it's safe
- Combined allocations into one large allocation for `node + buffer`
- Using `realloc` instead of full `calloc` + `memcpy` + `free`.
---
src/game/memory.c | 53 +++++++++++++++++++++++++++++------------------
1 file changed, 33 insertions(+), 20 deletions(-)
diff --git a/src/game/memory.c b/src/game/memory.c
index eb02ac7ac..c4febcaed 100644
--- a/src/game/memory.c
+++ b/src/game/memory.c
@@ -5,7 +5,8 @@
#include "print.h"
#include "pc/debuglog.h"
-#define ALIGN16(val) (((val) + 0xF) & ~0xF)
+// Alignment to a data size
+#define ALIGN_UP(val, align) (((val) + ((align) - 1)) & ~((align) - 1))
//////////////////
// dynamic pool //
@@ -14,7 +15,8 @@
struct DynamicPool *gLevelPool = NULL;
struct DynamicPool* dynamic_pool_init(void) {
- struct DynamicPool* pool = calloc(1, sizeof(struct DynamicPool));
+ struct DynamicPool* pool = malloc(sizeof(struct DynamicPool));
+ if (!pool) { return NULL; }
pool->usedSpace = 0;
pool->tail = NULL;
pool->nextFree = NULL;
@@ -24,14 +26,17 @@ struct DynamicPool* dynamic_pool_init(void) {
void* dynamic_pool_alloc(struct DynamicPool *pool, u32 size) {
if (!pool) { return NULL; }
- struct DynamicPoolNode* node = calloc(1, sizeof(struct DynamicPoolNode));
- node->ptr = calloc(1, size);
+ size_t header = ALIGN_UP(sizeof(struct DynamicPoolNode), sizeof(void*));
+ struct DynamicPoolNode* node = malloc(header + size);
+ if (!node) { return NULL; }
+ node->ptr = (u8*)node + header;
node->prev = pool->tail;
node->size = size;
pool->tail = node;
pool->usedSpace += size;
+ memset(node->ptr, 0, size);
return node->ptr;
}
@@ -50,8 +55,7 @@ void dynamic_pool_free(struct DynamicPool *pool, void* ptr) {
next->prev = prev;
}
pool->usedSpace -= node->size;
- free(node->ptr);
- free(node);
+ free(node); // node->ptr is freed here too; it's part of the same allocation
return;
}
next = node;
@@ -81,8 +85,7 @@ void dynamic_pool_free_pool(struct DynamicPool *pool) {
struct DynamicPoolNode* node = pool->nextFree;
while (node) {
struct DynamicPoolNode* prev = node->prev;
- free(node->ptr);
- free(node);
+ free(node); // node->ptr is freed here too; it's part of the same allocation
node = prev;
}
@@ -107,7 +110,8 @@ struct GrowingPool* growing_pool_init(struct GrowingPool* pool, u32 nodeSize) {
pool->usedSpace = 0;
} else {
// allocate a new pool
- pool = calloc(1, sizeof(struct GrowingPool));
+ pool = malloc(sizeof(struct GrowingPool));
+ if (!pool) { return NULL; }
pool->usedSpace = 0;
pool->nodeSize = nodeSize;
pool->tail = NULL;
@@ -119,19 +123,22 @@ void* growing_pool_alloc(struct GrowingPool *pool, u32 size) {
if (!pool) { return NULL; }
// maintain alignment
- size = ALIGN16(size);
+ size = ALIGN_UP(size, sizeof(void*));
// check if it's too big for a node
if (size >= pool->nodeSize) {
// create a node just for this
- struct GrowingPoolNode* node = calloc(1, sizeof(struct GrowingPoolNode));
- node->ptr = calloc(1, size);
+ size_t header = ALIGN_UP(sizeof(struct GrowingPoolNode), sizeof(void*));
+ struct GrowingPoolNode* node = malloc(header + size);
+ if (!node) { return NULL; }
+ node->ptr = (u8*) node + header;
node->prev = pool->tail;
node->usedSpace = size;
pool->tail = node;
pool->usedSpace += size;
+ memset(node->ptr, 0, size);
return node->ptr;
}
@@ -143,7 +150,7 @@ void* growing_pool_alloc(struct GrowingPool *pool, u32 size) {
while (node && depth < 128) {
depth++;
s64 freeSpace = (s64)pool->nodeSize - (s64)node->usedSpace;
- if (freeSpace > size) { break; }
+ if (freeSpace >= (s64) size) { break; }
node = node->prev;
}
if (depth >= 128) {
@@ -153,9 +160,11 @@ void* growing_pool_alloc(struct GrowingPool *pool, u32 size) {
// allocate new node
if (!node) {
- node = calloc(1, sizeof(struct GrowingPoolNode));
+ size_t header = ALIGN_UP(sizeof(struct GrowingPoolNode), sizeof(void*));
+ node = malloc(header + pool->nodeSize);
+ if (!node) { return NULL; }
node->usedSpace = 0;
- node->ptr = calloc(1, pool->nodeSize);
+ node->ptr = (u8*) node + header;
node->prev = pool->tail;
pool->tail = node;
}
@@ -174,8 +183,7 @@ void growing_pool_free_pool(struct GrowingPool *pool) {
struct GrowingPoolNode* node = pool->tail;
while (node) {
struct GrowingPoolNode* prev = node->prev;
- free(node->ptr);
- free(node);
+ free(node); // node->ptr is freed here too; it's part of the same allocation
node = prev;
}
free(pool);
@@ -218,7 +226,12 @@ struct GrowingArray *growing_array_init(struct GrowingArray *array, u32 capacity
}
} else {
array = malloc(sizeof(struct GrowingArray));
+ if (!array) { return NULL; }
array->buffer = calloc(capacity, sizeof(void *));
+ if (!array->buffer) {
+ free(array);
+ return NULL;
+ }
}
array->capacity = capacity;
@@ -234,9 +247,9 @@ void *growing_array_alloc(struct GrowingArray *array, u32 size) {
// Increase capacity if needed
while (array->count >= array->capacity) {
u32 newCapacity = array->capacity * 2;
- void **newBuffer = calloc(newCapacity, sizeof(void *));
- memcpy(newBuffer, array->buffer, array->capacity * sizeof(void *));
- free(array->buffer);
+ void **newBuffer = realloc(array->buffer, newCapacity * sizeof(void *));
+ if (!newBuffer) { return NULL; }
+ memset(newBuffer + array->capacity, 0, (newCapacity - array->capacity) * sizeof(void *));
array->buffer = newBuffer;
array->capacity = newCapacity;
}
From 5dabcaa3130d77e5a4f8e3aa5705150ff42fc0b3 Mon Sep 17 00:00:00 2001
From: jayden <46307433+kermeow@users.noreply.github.com>
Date: Sun, 3 May 2026 08:59:11 +0100
Subject: [PATCH 2/2] Add `audio_stream_get/set_volume_channel` (#1205)
Allows modders to play audio streams on channels other than level background music.
4 constants have been added for this purpose:
- `MOD_AUDIO_CHANNEL_MASTER` - sound is only affected by master volume
- `MOD_AUDIO_CHANNEL_MUSIC` - sound is affected by music volume, same as previous behaviour
- `MOD_AUDIO_CHANNEL_SFX` - sound is affected by sfx volume, same as sample behaviour
- `MOD_AUDIO_CHANNEL_ENV` - sound is affected by env volume
This was done instead of using the existing `SEQ_PLAYER_*` constants to avoid confusion and because there isn't a `NONE`/`MASTER` option.
Additionally, sets the default to `MOD_AUDIO_CHANNEL_MUSIC` as to not break compatibility.
```lua
audio_stream_set_volume_channel(stream, MOD_AUDIO_CHANNEL_SFX) -- wow its just like a sample
audio_stream_get_volume_channel(stream) -- returns MOD_AUDIO_CHANNEL_SFX (its actually 2)
```
---
autogen/convert_constants.py | 2 ++
autogen/convert_structs.py | 2 +-
autogen/lua_definitions/constants.lua | 12 +++++++
autogen/lua_definitions/functions.lua | 14 ++++++++
autogen/lua_definitions/structs.lua | 1 +
docs/lua/constants.md | 11 +++++++
docs/lua/functions-6.md | 47 +++++++++++++++++++++++++++
docs/lua/functions.md | 2 ++
docs/lua/structs.md | 1 +
src/pc/lua/smlua_cobject_autogen.c | 23 ++++++-------
src/pc/lua/smlua_constants_autogen.c | 4 +++
src/pc/lua/smlua_functions_autogen.c | 38 ++++++++++++++++++++++
src/pc/lua/utils/smlua_audio_utils.c | 46 ++++++++++++++++++++++----
src/pc/lua/utils/smlua_audio_utils.h | 11 +++++++
14 files changed, 195 insertions(+), 19 deletions(-)
diff --git a/autogen/convert_constants.py b/autogen/convert_constants.py
index 1b1d3cb19..fd214b72a 100644
--- a/autogen/convert_constants.py
+++ b/autogen/convert_constants.py
@@ -56,6 +56,7 @@ in_files = [
"include/PR/gbi.h",
"include/PR/gbi_extension.h",
"src/engine/surface_load.h",
+ "src/pc/lua/utils/smlua_audio_utils.h",
]
exclude_constants = {
@@ -77,6 +78,7 @@ include_constants = {
"include/geo_commands.h": [ "BACKGROUND" ],
"include/level_commands.h": [ "WARP_CHECKPOINT", "WARP_NO_CHECKPOINT" ],
"src/audio/external.h": [ "SEQ_PLAYER", "DS_" ],
+ "src/pc/lua/utils/smlua_audio_utils.h": ["MOD_AUDIO_CHANNEL"],
"src/pc/mods/mod_storage.h": [ "MAX_KEYS", "MAX_KEY_VALUE_LENGTH" ],
"include/PR/gbi.h": [
"^G_NOOP$",
diff --git a/autogen/convert_structs.py b/autogen/convert_structs.py
index de75db5d8..22942ec37 100644
--- a/autogen/convert_structs.py
+++ b/autogen/convert_structs.py
@@ -97,7 +97,7 @@ override_field_invisible = {
"FnGraphNode": [ "luaTokenIndex" ],
"Object": [ "firstSurface" ],
"Animation": [ "unusedBoneCount" ],
- "ModAudio": [ "sound", "decoder", "buffer", "bufferSize", "sampleCopiesTail" ],
+ "ModAudio": [ "sound", "decoder", "buffer", "bufferSize", "sampleCopiesTail", "volChannel" ],
"Painting": [ "normalDisplayList", "textureMaps", "rippleDisplayList", "ripples" ],
"DialogEntry": [ "str" ],
"ModFsFile": [ "data", "capacity" ],
diff --git a/autogen/lua_definitions/constants.lua b/autogen/lua_definitions/constants.lua
index da3fa0573..0825c31b0 100644
--- a/autogen/lua_definitions/constants.lua
+++ b/autogen/lua_definitions/constants.lua
@@ -8171,6 +8171,18 @@ VALID_BUTTONS = (A_BUTTON | B_BUTTON | Z_TRIG | START_BUTTON | U_JPAD | D_JPAD |
--- @type integer
C_BUTTONS = (U_CBUTTONS | D_CBUTTONS | L_CBUTTONS | R_CBUTTONS )
+--- @type integer
+MOD_AUDIO_CHANNEL_MASTER = 0
+
+--- @type integer
+MOD_AUDIO_CHANNEL_MUSIC = 1
+
+--- @type integer
+MOD_AUDIO_CHANNEL_SFX = 2
+
+--- @type integer
+MOD_AUDIO_CHANNEL_ENV = 3
+
HOOK_UPDATE = 0 --- @type LuaHookedEventType
HOOK_MARIO_UPDATE = 1 --- @type LuaHookedEventType
HOOK_BEFORE_MARIO_UPDATE = 2 --- @type LuaHookedEventType
diff --git a/autogen/lua_definitions/functions.lua b/autogen/lua_definitions/functions.lua
index 398012557..37531004a 100644
--- a/autogen/lua_definitions/functions.lua
+++ b/autogen/lua_definitions/functions.lua
@@ -10334,6 +10334,20 @@ function audio_stream_set_volume(audio, volume)
-- ...
end
+--- @param audio ModAudio
+--- @return integer
+--- Gets the volume channel of an `audio` stream
+function audio_stream_get_volume_channel(audio)
+ -- ...
+end
+
+--- @param audio ModAudio
+--- @param channel integer
+--- Sets the volume channel of an `audio` stream
+function audio_stream_set_volume_channel(audio, channel)
+ -- ...
+end
+
--- @param filename string
--- @return ModAudio
--- Loads an `audio` sample
diff --git a/autogen/lua_definitions/structs.lua b/autogen/lua_definitions/structs.lua
index 23c52dca7..668e3a358 100644
--- a/autogen/lua_definitions/structs.lua
+++ b/autogen/lua_definitions/structs.lua
@@ -1212,6 +1212,7 @@
--- @field public looping boolean
--- @field public frequency number
--- @field public volume number
+--- @field public channel integer
--- @class ModFs
--- @field public mod Mod
diff --git a/docs/lua/constants.md b/docs/lua/constants.md
index df90c089e..78590ed1e 100644
--- a/docs/lua/constants.md
+++ b/docs/lua/constants.md
@@ -79,6 +79,7 @@
- [seq_ids.h](#seq_idsh)
- [enum SeqId](#enum-SeqId)
- [sm64.h](#sm64h)
+- [smlua_audio_utils.h](#smlua_audio_utilsh)
- [smlua_hooks.h](#smlua_hooksh)
- [enum LuaHookedEventType](#enum-LuaHookedEventType)
- [enum LuaHookedEventReturn](#enum-LuaHookedEventReturn)
@@ -3486,6 +3487,16 @@
+## [smlua_audio_utils.h](#smlua_audio_utils.h)
+- MOD_AUDIO_CHANNEL_MASTER
+- MOD_AUDIO_CHANNEL_MUSIC
+- MOD_AUDIO_CHANNEL_SFX
+- MOD_AUDIO_CHANNEL_ENV
+
+[:arrow_up_small:](#)
+
+
+
## [smlua_hooks.h](#smlua_hooks.h)
### [enum LuaHookedEventType](#LuaHookedEventType)
diff --git a/docs/lua/functions-6.md b/docs/lua/functions-6.md
index 30ecdc81f..8c21d8868 100644
--- a/docs/lua/functions-6.md
+++ b/docs/lua/functions-6.md
@@ -5947,6 +5947,53 @@ Sets the volume of an `audio` stream
+## [audio_stream_get_volume_channel](#audio_stream_get_volume_channel)
+
+### Description
+Gets the volume channel of an `audio` stream
+
+### Lua Example
+`local integerValue = audio_stream_get_volume_channel(audio)`
+
+### Parameters
+| Field | Type |
+| ----- | ---- |
+| audio | [ModAudio](structs.md#ModAudio) |
+
+### Returns
+- `integer`
+
+### C Prototype
+`u8 audio_stream_get_volume_channel(struct ModAudio *audio);`
+
+[:arrow_up_small:](#)
+
+
+
+## [audio_stream_set_volume_channel](#audio_stream_set_volume_channel)
+
+### Description
+Sets the volume channel of an `audio` stream
+
+### Lua Example
+`audio_stream_set_volume_channel(audio, channel)`
+
+### Parameters
+| Field | Type |
+| ----- | ---- |
+| audio | [ModAudio](structs.md#ModAudio) |
+| channel | `integer` |
+
+### Returns
+- None
+
+### C Prototype
+`void audio_stream_set_volume_channel(struct ModAudio *audio, u8 channel);`
+
+[:arrow_up_small:](#)
+
+
+
## [audio_sample_load](#audio_sample_load)
### Description
diff --git a/docs/lua/functions.md b/docs/lua/functions.md
index 242183052..fd2fb18b3 100644
--- a/docs/lua/functions.md
+++ b/docs/lua/functions.md
@@ -1849,6 +1849,8 @@
- [audio_stream_set_frequency](functions-6.md#audio_stream_set_frequency)
- [audio_stream_get_volume](functions-6.md#audio_stream_get_volume)
- [audio_stream_set_volume](functions-6.md#audio_stream_set_volume)
+ - [audio_stream_get_volume_channel](functions-6.md#audio_stream_get_volume_channel)
+ - [audio_stream_set_volume_channel](functions-6.md#audio_stream_set_volume_channel)
- [audio_sample_load](functions-6.md#audio_sample_load)
- [audio_sample_destroy](functions-6.md#audio_sample_destroy)
- [audio_sample_stop](functions-6.md#audio_sample_stop)
diff --git a/docs/lua/structs.md b/docs/lua/structs.md
index b14ade940..c7b911d43 100644
--- a/docs/lua/structs.md
+++ b/docs/lua/structs.md
@@ -1768,6 +1768,7 @@
| looping | `boolean` | |
| frequency | `number` | |
| volume | `number` | |
+| channel | `integer` | |
[:arrow_up_small:](#)
diff --git a/src/pc/lua/smlua_cobject_autogen.c b/src/pc/lua/smlua_cobject_autogen.c
index 5464829c4..303565be5 100644
--- a/src/pc/lua/smlua_cobject_autogen.c
+++ b/src/pc/lua/smlua_cobject_autogen.c
@@ -1494,18 +1494,19 @@ static struct LuaObjectField sModFields[LUA_MOD_FIELD_COUNT] = {
{ "size", LVT_U64, offsetof(struct Mod, size), true, LOT_NONE },
};
-#define LUA_MOD_AUDIO_FIELD_COUNT 10
+#define LUA_MOD_AUDIO_FIELD_COUNT 11
static struct LuaObjectField sModAudioFields[LUA_MOD_AUDIO_FIELD_COUNT] = {
- { "baseVolume", LVT_F32, offsetof(struct ModAudio, baseVolume), false, LOT_NONE },
- { "file", LVT_PROPERTY, .get = "return_self" },
- { "filepath", LVT_STRING_P, offsetof(struct ModAudio, filepath), true, LOT_NONE },
- { "frequency", LVT_PROPERTY, .get = "audio_stream_get_frequency", .set = "audio_stream_set_frequency" },
- { "isStream", LVT_BOOL, offsetof(struct ModAudio, isStream), true, LOT_NONE },
- { "loaded", LVT_BOOL, offsetof(struct ModAudio, loaded), true, LOT_NONE },
- { "looping", LVT_PROPERTY, .get = "audio_stream_get_looping", .set = "audio_stream_set_looping" },
- { "position", LVT_PROPERTY, .get = "audio_stream_get_position", .set = "audio_stream_set_position" },
- { "relativePath", LVT_STRING_P, offsetof(struct ModAudio, relativePath), true, LOT_NONE },
- { "volume", LVT_PROPERTY, .get = "audio_stream_get_volume", .set = "audio_stream_set_volume" },
+ { "baseVolume", LVT_F32, offsetof(struct ModAudio, baseVolume), false, LOT_NONE },
+ { "channel", LVT_PROPERTY, .get = "audio_stream_get_volume_channel", .set = "audio_stream_set_volume_channel" },
+ { "file", LVT_PROPERTY, .get = "return_self" },
+ { "filepath", LVT_STRING_P, offsetof(struct ModAudio, filepath), true, LOT_NONE },
+ { "frequency", LVT_PROPERTY, .get = "audio_stream_get_frequency", .set = "audio_stream_set_frequency" },
+ { "isStream", LVT_BOOL, offsetof(struct ModAudio, isStream), true, LOT_NONE },
+ { "loaded", LVT_BOOL, offsetof(struct ModAudio, loaded), true, LOT_NONE },
+ { "looping", LVT_PROPERTY, .get = "audio_stream_get_looping", .set = "audio_stream_set_looping" },
+ { "position", LVT_PROPERTY, .get = "audio_stream_get_position", .set = "audio_stream_set_position" },
+ { "relativePath", LVT_STRING_P, offsetof(struct ModAudio, relativePath), true, LOT_NONE },
+ { "volume", LVT_PROPERTY, .get = "audio_stream_get_volume", .set = "audio_stream_set_volume" },
};
#define LUA_MOD_FS_FIELD_COUNT 15
diff --git a/src/pc/lua/smlua_constants_autogen.c b/src/pc/lua/smlua_constants_autogen.c
index 9285bef76..80c6dcb85 100644
--- a/src/pc/lua/smlua_constants_autogen.c
+++ b/src/pc/lua/smlua_constants_autogen.c
@@ -3495,6 +3495,10 @@ char gSmluaConstants[] = ""
"ACT_RELEASING_BOWSER=0x00000392\n"
"VALID_BUTTONS=(A_BUTTON | B_BUTTON | Z_TRIG | START_BUTTON | U_JPAD | D_JPAD | L_JPAD | R_JPAD | L_TRIG | R_TRIG | X_BUTTON | Y_BUTTON | U_CBUTTONS | D_CBUTTONS | L_CBUTTONS | R_CBUTTONS )\n"
"C_BUTTONS=(U_CBUTTONS | D_CBUTTONS | L_CBUTTONS | R_CBUTTONS )\n"
+"MOD_AUDIO_CHANNEL_MASTER=0\n"
+"MOD_AUDIO_CHANNEL_MUSIC=1\n"
+"MOD_AUDIO_CHANNEL_SFX=2\n"
+"MOD_AUDIO_CHANNEL_ENV=3\n"
"HOOK_UPDATE=0\n"
"HOOK_MARIO_UPDATE=1\n"
"HOOK_BEFORE_MARIO_UPDATE=2\n"
diff --git a/src/pc/lua/smlua_functions_autogen.c b/src/pc/lua/smlua_functions_autogen.c
index 736f2dbb2..d5d8acca7 100644
--- a/src/pc/lua/smlua_functions_autogen.c
+++ b/src/pc/lua/smlua_functions_autogen.c
@@ -30774,6 +30774,42 @@ int smlua_func_audio_stream_set_volume(lua_State* L) {
return 1;
}
+int smlua_func_audio_stream_get_volume_channel(lua_State* L) {
+ if (L == NULL) { return 0; }
+
+ int top = lua_gettop(L);
+ if (top != 1) {
+ LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "audio_stream_get_volume_channel", 1, top);
+ return 0;
+ }
+
+ struct ModAudio* audio = (struct ModAudio*)smlua_to_cobject(L, 1, LOT_MODAUDIO);
+ if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "audio_stream_get_volume_channel"); return 0; }
+
+ lua_pushinteger(L, audio_stream_get_volume_channel(audio));
+
+ return 1;
+}
+
+int smlua_func_audio_stream_set_volume_channel(lua_State* L) {
+ if (L == NULL) { return 0; }
+
+ int top = lua_gettop(L);
+ if (top != 2) {
+ LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "audio_stream_set_volume_channel", 2, top);
+ return 0;
+ }
+
+ struct ModAudio* audio = (struct ModAudio*)smlua_to_cobject(L, 1, LOT_MODAUDIO);
+ if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "audio_stream_set_volume_channel"); return 0; }
+ u8 channel = smlua_to_integer(L, 2);
+ if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 2, "audio_stream_set_volume_channel"); return 0; }
+
+ audio_stream_set_volume_channel(audio, channel);
+
+ return 1;
+}
+
int smlua_func_audio_sample_load(lua_State* L) {
if (L == NULL) { return 0; }
@@ -38680,6 +38716,8 @@ void smlua_bind_functions_autogen(void) {
smlua_bind_function(L, "audio_stream_set_frequency", smlua_func_audio_stream_set_frequency);
smlua_bind_function(L, "audio_stream_get_volume", smlua_func_audio_stream_get_volume);
smlua_bind_function(L, "audio_stream_set_volume", smlua_func_audio_stream_set_volume);
+ smlua_bind_function(L, "audio_stream_get_volume_channel", smlua_func_audio_stream_get_volume_channel);
+ smlua_bind_function(L, "audio_stream_set_volume_channel", smlua_func_audio_stream_set_volume_channel);
smlua_bind_function(L, "audio_sample_load", smlua_func_audio_sample_load);
smlua_bind_function(L, "audio_sample_destroy", smlua_func_audio_sample_destroy);
smlua_bind_function(L, "audio_sample_stop", smlua_func_audio_sample_stop);
diff --git a/src/pc/lua/utils/smlua_audio_utils.c b/src/pc/lua/utils/smlua_audio_utils.c
index 09a81633f..7686f9094 100644
--- a/src/pc/lua/utils/smlua_audio_utils.c
+++ b/src/pc/lua/utils/smlua_audio_utils.c
@@ -363,10 +363,24 @@ struct ModAudio* audio_load_internal(const char* filename, bool isStream) {
audio->buffer = buffer;
audio->bufferSize = size;
audio->isStream = isStream;
+ audio->baseVolume = 1.0f;
+ audio->volChannel = MOD_AUDIO_CHANNEL_MUSIC;
audio->loaded = true;
return audio;
}
+static f32 get_audio_volume(struct ModAudio* audio) {
+ f32 volume = audio->baseVolume;
+ if (audio->volChannel == MOD_AUDIO_CHANNEL_MUSIC) {
+ volume *= (f32)configMusicVolume / 127.0f * (f32)gLuaVolumeLevel / 127.0f;
+ } else if (audio->volChannel == MOD_AUDIO_CHANNEL_SFX) {
+ volume *= (f32)configSfxVolume / 127.0f * (f32)gLuaVolumeSfx / 127.0f;
+ } else if (audio->volChannel == MOD_AUDIO_CHANNEL_ENV) {
+ volume *= (f32)configEnvVolume / 127.0f * (f32)gLuaVolumeEnv / 127.0f;
+ }
+ return gMasterVolume * volume;
+}
+
struct ModAudio* audio_stream_load(const char* filename) {
return audio_load_internal(filename, true);
}
@@ -388,6 +402,7 @@ void audio_stream_play(struct ModAudio* audio, bool restart, f32 volume) {
ma_sound_set_volume(&audio->sound, gMasterVolume * musicVolume * volume);
}
audio->baseVolume = volume;
+ ma_sound_set_volume(&audio->sound, get_audio_volume(audio));
if (restart || !ma_sound_is_playing(&audio->sound)) { ma_sound_seek_to_pcm_frame(&audio->sound, 0); }
ma_sound_start(&audio->sound);
}
@@ -473,14 +488,9 @@ f32 audio_stream_get_volume(struct ModAudio* audio) {
void audio_stream_set_volume(struct ModAudio* audio, f32 volume) {
if (!audio_sanity_check(audio, true, "set stream volume for")) { return; }
-
- if (configMuteFocusLoss && !WAPI.has_focus()) {
- ma_sound_set_volume(&audio->sound, 0);
- } else {
- f32 musicVolume = (f32)configMusicVolume / 127.0f * (f32)gLuaVolumeLevel / 127.0f;
- ma_sound_set_volume(&audio->sound, gMasterVolume * musicVolume * volume);
- }
+
audio->baseVolume = volume;
+ ma_sound_set_volume(&audio->sound, get_audio_volume(audio));
}
// void audio_stream_set_speed(struct ModAudio* audio, f32 initial_freq, f32 speed, bool pitch) {
@@ -489,6 +499,28 @@ void audio_stream_set_volume(struct ModAudio* audio, f32 volume) {
// bassh_set_speed(audio->handle, initial_freq, speed, pitch);
// }
+u8 audio_stream_get_volume_channel(struct ModAudio* audio) {
+ if (!audio_sanity_check(audio, true, "get stream volume channel from")) {
+ return 0;
+ }
+
+ return audio->volChannel;
+}
+
+void audio_stream_set_volume_channel(struct ModAudio* audio, u8 channel) {
+ if (!audio_sanity_check(audio, true, "set stream volume channel for")) {
+ return;
+ }
+
+ if (channel > MOD_AUDIO_CHANNEL_ENV) {
+ LOG_LUA_LINE("Tried to set volume channel to invalid value: %d", channel);
+ return;
+ }
+
+ audio->volChannel = channel;
+ ma_sound_set_volume(&audio->sound, get_audio_volume(audio));
+}
+
//////////////////////////////////////
// MA calls the end callback from its audio thread
diff --git a/src/pc/lua/utils/smlua_audio_utils.h b/src/pc/lua/utils/smlua_audio_utils.h
index 78a078410..880f80558 100644
--- a/src/pc/lua/utils/smlua_audio_utils.h
+++ b/src/pc/lua/utils/smlua_audio_utils.h
@@ -15,6 +15,11 @@ u8 smlua_audio_utils_allocate_sequence(void);
// mod sounds //
////////////////
+#define MOD_AUDIO_CHANNEL_MASTER 0
+#define MOD_AUDIO_CHANNEL_MUSIC 1
+#define MOD_AUDIO_CHANNEL_SFX 2
+#define MOD_AUDIO_CHANNEL_ENV 3
+
struct ModAudioSampleCopies {
ma_sound sound;
ma_decoder decoder;
@@ -35,12 +40,14 @@ struct ModAudio {
struct ModAudioSampleCopies* sampleCopiesTail;
bool isStream;
f32 baseVolume;
+ u8 volChannel;
bool loaded;
PROPERTY(position, audio_stream_get_position, audio_stream_set_position);
PROPERTY(looping, audio_stream_get_looping, audio_stream_set_looping);
PROPERTY(frequency, audio_stream_get_frequency, audio_stream_set_frequency);
PROPERTY(volume, audio_stream_get_volume, audio_stream_set_volume);
+ PROPERTY(channel, audio_stream_get_volume_channel, audio_stream_set_volume_channel);
PROPERTY(file, return_self, NULL); // compatibility band-aid
};
@@ -73,6 +80,10 @@ void audio_stream_set_frequency(struct ModAudio* audio, f32 freq);
f32 audio_stream_get_volume(struct ModAudio* audio);
/* |description|Sets the volume of an `audio` stream|descriptionEnd| */
void audio_stream_set_volume(struct ModAudio* audio, f32 volume);
+/* |description|Gets the volume channel of an `audio` stream|descriptionEnd| */
+u8 audio_stream_get_volume_channel(struct ModAudio *audio);
+/* |description|Sets the volume channel of an `audio` stream|descriptionEnd| */
+void audio_stream_set_volume_channel(struct ModAudio *audio, u8 channel);
void audio_sample_destroy_pending_copies(void);
/* |description|Loads an `audio` sample|descriptionEnd| */