add sound
This commit is contained in:
parent
99d9c43ea3
commit
01d6e09a91
22 changed files with 1825 additions and 39 deletions
|
|
@ -22,6 +22,7 @@ struct pxl8_script {
|
|||
lua_State* L;
|
||||
pxl8_gfx* gfx;
|
||||
pxl8_input_state* input;
|
||||
pxl8_sfx_mixer* mixer;
|
||||
char last_error[PXL8_MAX_ERROR_SIZE];
|
||||
char main_path[PXL8_MAX_PATH];
|
||||
char watch_dir[PXL8_MAX_PATH];
|
||||
|
|
@ -420,7 +421,53 @@ static const char* pxl8_ffi_cdefs =
|
|||
"void pxl8_save_free(u8* data);\n"
|
||||
"bool pxl8_save_exists(pxl8_save* save, u8 slot);\n"
|
||||
"i32 pxl8_save_delete(pxl8_save* save, u8 slot);\n"
|
||||
"const char* pxl8_save_get_directory(pxl8_save* save);\n";
|
||||
"const char* pxl8_save_get_directory(pxl8_save* save);\n"
|
||||
"\n"
|
||||
"typedef struct pxl8_sfx_context pxl8_sfx_context;\n"
|
||||
"typedef struct pxl8_sfx_mixer pxl8_sfx_mixer;\n"
|
||||
"typedef struct pxl8_sfx_node pxl8_sfx_node;\n"
|
||||
"typedef enum pxl8_sfx_filter_type { PXL8_SFX_FILTER_BANDPASS = 0, PXL8_SFX_FILTER_HIGHPASS, PXL8_SFX_FILTER_LOWPASS, PXL8_SFX_FILTER_NONE } pxl8_sfx_filter_type;\n"
|
||||
"typedef enum pxl8_sfx_lfo_target { PXL8_SFX_LFO_AMPLITUDE = 0, PXL8_SFX_LFO_FILTER, PXL8_SFX_LFO_PITCH } pxl8_sfx_lfo_target;\n"
|
||||
"typedef enum pxl8_sfx_node_type { PXL8_SFX_NODE_COMPRESSOR, PXL8_SFX_NODE_DELAY, PXL8_SFX_NODE_REVERB } pxl8_sfx_node_type;\n"
|
||||
"typedef enum pxl8_sfx_waveform { PXL8_SFX_WAVE_NOISE = 0, PXL8_SFX_WAVE_PULSE, PXL8_SFX_WAVE_SAW, PXL8_SFX_WAVE_SINE, PXL8_SFX_WAVE_SQUARE, PXL8_SFX_WAVE_TRIANGLE } pxl8_sfx_waveform;\n"
|
||||
"typedef struct pxl8_sfx_adsr { f32 attack; f32 decay; f32 sustain; f32 release; } pxl8_sfx_adsr;\n"
|
||||
"typedef struct pxl8_sfx_compressor_config { f32 attack; f32 ratio; f32 release; f32 threshold; } pxl8_sfx_compressor_config;\n"
|
||||
"typedef struct pxl8_sfx_delay_config { f32 feedback; f32 mix; u32 time_l; u32 time_r; } pxl8_sfx_delay_config;\n"
|
||||
"typedef struct pxl8_sfx_reverb_config { f32 damping; f32 mix; f32 room; } pxl8_sfx_reverb_config;\n"
|
||||
"typedef struct pxl8_sfx_voice_params { pxl8_sfx_adsr amp_env; pxl8_sfx_adsr filter_env; pxl8_sfx_filter_type filter_type; pxl8_sfx_lfo_target lfo_target; pxl8_sfx_waveform lfo_waveform; pxl8_sfx_waveform waveform; f32 filter_cutoff; f32 filter_env_depth; f32 filter_resonance; f32 fx_send; f32 lfo_depth; f32 lfo_rate; f32 pulse_width; } pxl8_sfx_voice_params;\n"
|
||||
"pxl8_sfx_node* pxl8_sfx_compressor_create(pxl8_sfx_compressor_config cfg);\n"
|
||||
"void pxl8_sfx_compressor_set_attack(pxl8_sfx_node* node, f32 attack);\n"
|
||||
"void pxl8_sfx_compressor_set_ratio(pxl8_sfx_node* node, f32 ratio);\n"
|
||||
"void pxl8_sfx_compressor_set_release(pxl8_sfx_node* node, f32 release);\n"
|
||||
"void pxl8_sfx_compressor_set_threshold(pxl8_sfx_node* node, f32 threshold);\n"
|
||||
"void pxl8_sfx_context_append_node(pxl8_sfx_context* ctx, pxl8_sfx_node* node);\n"
|
||||
"pxl8_sfx_context* pxl8_sfx_context_create(void);\n"
|
||||
"void pxl8_sfx_context_destroy(pxl8_sfx_context* ctx);\n"
|
||||
"pxl8_sfx_node* pxl8_sfx_context_get_head(pxl8_sfx_context* ctx);\n"
|
||||
"f32 pxl8_sfx_context_get_volume(const pxl8_sfx_context* ctx);\n"
|
||||
"void pxl8_sfx_context_insert_node(pxl8_sfx_context* ctx, pxl8_sfx_node* after, pxl8_sfx_node* node);\n"
|
||||
"void pxl8_sfx_context_remove_node(pxl8_sfx_context* ctx, pxl8_sfx_node* node);\n"
|
||||
"void pxl8_sfx_context_set_volume(pxl8_sfx_context* ctx, f32 volume);\n"
|
||||
"pxl8_sfx_node* pxl8_sfx_delay_create(pxl8_sfx_delay_config cfg);\n"
|
||||
"void pxl8_sfx_delay_set_feedback(pxl8_sfx_node* node, f32 feedback);\n"
|
||||
"void pxl8_sfx_delay_set_mix(pxl8_sfx_node* node, f32 mix);\n"
|
||||
"void pxl8_sfx_delay_set_time(pxl8_sfx_node* node, u32 time_l, u32 time_r);\n"
|
||||
"void pxl8_sfx_mixer_attach(pxl8_sfx_mixer* mixer, pxl8_sfx_context* ctx);\n"
|
||||
"pxl8_sfx_mixer* pxl8_sfx_mixer_create(void);\n"
|
||||
"void pxl8_sfx_mixer_destroy(pxl8_sfx_mixer* mixer);\n"
|
||||
"void pxl8_sfx_mixer_detach(pxl8_sfx_mixer* mixer, pxl8_sfx_context* ctx);\n"
|
||||
"f32 pxl8_sfx_mixer_get_master_volume(const pxl8_sfx_mixer* mixer);\n"
|
||||
"void pxl8_sfx_mixer_set_master_volume(pxl8_sfx_mixer* mixer, f32 volume);\n"
|
||||
"void pxl8_sfx_node_destroy(pxl8_sfx_node* node);\n"
|
||||
"f32 pxl8_sfx_note_to_freq(u8 note);\n"
|
||||
"u16 pxl8_sfx_play_note(pxl8_sfx_context* ctx, u8 note, const pxl8_sfx_voice_params* params, f32 volume);\n"
|
||||
"void pxl8_sfx_release_voice(pxl8_sfx_context* ctx, u16 voice_id);\n"
|
||||
"pxl8_sfx_node* pxl8_sfx_reverb_create(pxl8_sfx_reverb_config cfg);\n"
|
||||
"void pxl8_sfx_reverb_set_damping(pxl8_sfx_node* node, f32 damping);\n"
|
||||
"void pxl8_sfx_reverb_set_mix(pxl8_sfx_node* node, f32 mix);\n"
|
||||
"void pxl8_sfx_reverb_set_room(pxl8_sfx_node* node, f32 room);\n"
|
||||
"void pxl8_sfx_stop_all(pxl8_sfx_context* ctx);\n"
|
||||
"void pxl8_sfx_stop_voice(pxl8_sfx_context* ctx, u16 voice_id);\n";
|
||||
|
||||
void pxl8_lua_log(int level, const char* file, int line, const char* msg) {
|
||||
if (file && (file[0] == '?' || file[0] == '\0')) file = NULL;
|
||||
|
|
@ -637,6 +684,14 @@ void pxl8_script_set_input(pxl8_script* script, pxl8_input_state* input) {
|
|||
}
|
||||
}
|
||||
|
||||
void pxl8_script_set_sfx(pxl8_script* script, pxl8_sfx_mixer* mixer) {
|
||||
if (!script) return;
|
||||
script->mixer = mixer;
|
||||
if (script->L && mixer) {
|
||||
lua_pushlightuserdata(script->L, mixer);
|
||||
lua_setglobal(script->L, "_pxl8_sfx_mixer");
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_script_set_sys(pxl8_script* script, void* sys) {
|
||||
if (!script) return;
|
||||
|
|
@ -920,6 +975,90 @@ pxl8_result pxl8_script_call_function_f32(pxl8_script* script, const char* name,
|
|||
return PXL8_OK;
|
||||
}
|
||||
|
||||
static bool pxl8_script_is_builtin_global(const char* name) {
|
||||
static const char* builtins[] = {
|
||||
"_G", "_VERSION", "arg", "assert", "bit", "collectgarbage",
|
||||
"coroutine", "debug", "dofile", "error", "fennel", "ffi",
|
||||
"gcinfo", "getfenv", "getmetatable", "init", "io", "ipairs",
|
||||
"jit", "load", "loadfile", "loadstring", "math", "module",
|
||||
"newproxy", "next", "os", "package", "pairs", "pcall",
|
||||
"print", "pxl8", "rawequal", "rawget", "rawset", "require",
|
||||
"select", "setfenv", "setmetatable", "string", "table",
|
||||
"tonumber", "tostring", "type", "unpack", "update", "frame",
|
||||
"xpcall", "_pxl8_gfx", "_pxl8_input", "_pxl8_sfx_mixer", "_pxl8_sys",
|
||||
NULL
|
||||
};
|
||||
for (int i = 0; builtins[i]; i++) {
|
||||
if (strcmp(name, builtins[i]) == 0) return true;
|
||||
}
|
||||
return name[0] == '_' && name[1] == '_';
|
||||
}
|
||||
|
||||
static void pxl8_script_cleanup(pxl8_script* script) {
|
||||
if (script->gfx) {
|
||||
pxl8_gfx_clear_textures(script->gfx);
|
||||
}
|
||||
|
||||
if (script->mixer) {
|
||||
pxl8_sfx_mixer_clear(script->mixer);
|
||||
}
|
||||
}
|
||||
|
||||
static void pxl8_script_save_globals(pxl8_script* script) {
|
||||
lua_State* L = script->L;
|
||||
|
||||
lua_newtable(L);
|
||||
int saved_table = lua_gettop(L);
|
||||
|
||||
lua_pushvalue(L, LUA_GLOBALSINDEX);
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, -2) != 0) {
|
||||
if (lua_type(L, -2) == LUA_TSTRING) {
|
||||
const char* name = lua_tostring(L, -2);
|
||||
int vtype = lua_type(L, -1);
|
||||
|
||||
if (!pxl8_script_is_builtin_global(name) &&
|
||||
vtype != LUA_TFUNCTION &&
|
||||
vtype != LUA_TUSERDATA &&
|
||||
vtype != LUA_TLIGHTUSERDATA &&
|
||||
vtype != LUA_TTHREAD &&
|
||||
vtype != 10) {
|
||||
lua_pushvalue(L, -2);
|
||||
lua_pushvalue(L, -2);
|
||||
lua_settable(L, saved_table);
|
||||
}
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, "_pxl8_hotreload_state");
|
||||
pxl8_debug("Hot reload state saved");
|
||||
}
|
||||
|
||||
static void pxl8_script_restore_globals(pxl8_script* script) {
|
||||
lua_State* L = script->L;
|
||||
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, "_pxl8_hotreload_state");
|
||||
if (lua_isnil(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, -2) != 0) {
|
||||
lua_pushvalue(L, -2);
|
||||
lua_pushvalue(L, -2);
|
||||
lua_settable(L, LUA_GLOBALSINDEX);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_pushnil(L);
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, "_pxl8_hotreload_state");
|
||||
pxl8_debug("Hot reload state restored");
|
||||
}
|
||||
|
||||
static time_t get_file_mod_time(const char* path) {
|
||||
struct stat file_stat;
|
||||
if (stat(path, &file_stat) == 0) {
|
||||
|
|
@ -972,15 +1111,8 @@ pxl8_result pxl8_script_load_main(pxl8_script* script, const char* path) {
|
|||
|
||||
pxl8_strncpy(script->main_path, path, sizeof(script->main_path));
|
||||
|
||||
char* last_slash = strrchr(script->main_path, '/');
|
||||
if (last_slash) {
|
||||
size_t dir_len = last_slash - script->main_path;
|
||||
strncpy(script->watch_dir, script->main_path, dir_len);
|
||||
script->watch_dir[dir_len] = '\0';
|
||||
} else {
|
||||
script->watch_dir[0] = '.';
|
||||
script->watch_dir[1] = '\0';
|
||||
}
|
||||
script->watch_dir[0] = '.';
|
||||
script->watch_dir[1] = '\0';
|
||||
|
||||
script->latest_mod_time = get_latest_script_mod_time(script->watch_dir);
|
||||
|
||||
|
|
@ -1005,26 +1137,72 @@ pxl8_result pxl8_script_load_main(pxl8_script* script, const char* path) {
|
|||
return result;
|
||||
}
|
||||
|
||||
static void pxl8_script_clear_cart_modules(pxl8_script* script) {
|
||||
lua_State* L = script->L;
|
||||
|
||||
lua_getglobal(L, "package");
|
||||
lua_getfield(L, -1, "loaded");
|
||||
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, -2) != 0) {
|
||||
lua_pop(L, 1);
|
||||
const char* name = lua_tostring(L, -1);
|
||||
if (name && strncmp(name, "pxl8", 4) != 0 &&
|
||||
strcmp(name, "fennel") != 0 &&
|
||||
strcmp(name, "ffi") != 0 &&
|
||||
strcmp(name, "bit") != 0 &&
|
||||
strcmp(name, "jit") != 0 &&
|
||||
strcmp(name, "string") != 0 &&
|
||||
strcmp(name, "table") != 0 &&
|
||||
strcmp(name, "math") != 0 &&
|
||||
strcmp(name, "io") != 0 &&
|
||||
strcmp(name, "os") != 0 &&
|
||||
strcmp(name, "debug") != 0 &&
|
||||
strcmp(name, "coroutine") != 0 &&
|
||||
strcmp(name, "package") != 0) {
|
||||
lua_pushvalue(L, -1);
|
||||
lua_pushnil(L);
|
||||
lua_settable(L, -4);
|
||||
}
|
||||
}
|
||||
|
||||
lua_pop(L, 2);
|
||||
}
|
||||
|
||||
bool pxl8_script_check_reload(pxl8_script* script) {
|
||||
if (!script || script->main_path[0] == '\0') return false;
|
||||
if (!script || script->main_path[0] == '\0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
time_t current_mod_time = get_latest_script_mod_time(script->watch_dir);
|
||||
if (current_mod_time > script->latest_mod_time && current_mod_time != 0) {
|
||||
pxl8_info("Script files modified, reloading: %s", script->main_path);
|
||||
script->latest_mod_time = current_mod_time;
|
||||
|
||||
pxl8_script_cleanup(script);
|
||||
pxl8_script_save_globals(script);
|
||||
pxl8_script_clear_cart_modules(script);
|
||||
|
||||
const char* ext = strrchr(script->main_path, '.');
|
||||
bool reloaded = false;
|
||||
|
||||
if (ext && strcmp(ext, ".fnl") == 0) {
|
||||
if (pxl8_script_run_fennel_file(script, script->main_path) == PXL8_OK) {
|
||||
pxl8_script_call_function(script, "init");
|
||||
return true;
|
||||
reloaded = true;
|
||||
}
|
||||
} else if (ext && strcmp(ext, ".lua") == 0) {
|
||||
if (pxl8_script_run_file(script, script->main_path) == PXL8_OK) {
|
||||
pxl8_script_call_function(script, "init");
|
||||
return true;
|
||||
reloaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (reloaded) {
|
||||
pxl8_script_restore_globals(script);
|
||||
}
|
||||
|
||||
return reloaded;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue