add sound
This commit is contained in:
parent
99d9c43ea3
commit
01d6e09a91
22 changed files with 1825 additions and 39 deletions
|
|
@ -1,5 +1,6 @@
|
|||
(local pxl8 (require :pxl8))
|
||||
(local menu (require :mod.menu))
|
||||
(local music (require :mod.music))
|
||||
(local worldgen (require :mod.worldgen))
|
||||
|
||||
(var time 0)
|
||||
|
|
@ -29,6 +30,8 @@
|
|||
(pxl8.load_palette "res/sprites/pxl8_logo.ase")
|
||||
(set logo-sprite (pxl8.load_sprite "res/sprites/pxl8_logo.ase"))
|
||||
(set particles (pxl8.particles_new 1000))
|
||||
(music.init)
|
||||
(music.start)
|
||||
(worldgen.init)))
|
||||
|
||||
(global update (fn [dt]
|
||||
|
|
@ -79,6 +82,8 @@
|
|||
(set logo-dy (- logo-dy))))
|
||||
:worldgen (worldgen.update dt))
|
||||
|
||||
(music.update dt)
|
||||
|
||||
(when particles
|
||||
(pxl8.particles_update particles dt)))
|
||||
|
||||
|
|
|
|||
68
demo/mod/music.fnl
Normal file
68
demo/mod/music.fnl
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
(local pxl8 (require :pxl8))
|
||||
|
||||
(var time 0)
|
||||
(var step 0)
|
||||
(var ctx nil)
|
||||
(var params nil)
|
||||
(var bass-params nil)
|
||||
(var playing false)
|
||||
|
||||
(local melody [60 64 67 72 67 64 60 64 67 72 76 72 67 64 62 66 69 74 69 66 62 66 69 74 78 74 69 66])
|
||||
(local bass [36 50 40 36 38 38 50 38])
|
||||
(local step-duration 0.15)
|
||||
|
||||
(fn init []
|
||||
(set ctx (pxl8.sfx_context_create))
|
||||
|
||||
(local delay (pxl8.sfx_delay_create {:time_l 350 :time_r 500 :feedback 0.4 :mix 0.25}))
|
||||
(local reverb (pxl8.sfx_reverb_create {:room 0.5 :damping 0.5 :mix 0.3}))
|
||||
(local compressor (pxl8.sfx_compressor_create {:threshold -12 :ratio 4 :attack 10 :release 100}))
|
||||
|
||||
(pxl8.sfx_context_append_node ctx delay)
|
||||
(pxl8.sfx_context_append_node ctx reverb)
|
||||
(pxl8.sfx_context_append_node ctx compressor)
|
||||
|
||||
(pxl8.sfx_mixer_attach ctx)
|
||||
|
||||
(set params (pxl8.sfx_voice_params
|
||||
{:waveform pxl8.SFX_WAVE_TRIANGLE
|
||||
:attack 0.005 :decay 0.08 :sustain 0.0 :release 0.05
|
||||
:filter_type pxl8.SFX_FILTER_LOWPASS
|
||||
:filter_cutoff 2500 :filter_resonance 0.15
|
||||
:filter_attack 0.005 :filter_decay 0.1 :filter_sustain 0.1 :filter_release 0.05
|
||||
:filter_env_depth 1500
|
||||
:fx_send 0.3}))
|
||||
|
||||
(set bass-params (pxl8.sfx_voice_params
|
||||
{:waveform pxl8.SFX_WAVE_SQUARE
|
||||
:attack 0.005 :decay 0.1 :sustain 0.0 :release 0.05
|
||||
:filter_type pxl8.SFX_FILTER_LOWPASS
|
||||
:filter_cutoff 600 :filter_resonance 0.2
|
||||
:fx_send 0.2})))
|
||||
|
||||
(fn start []
|
||||
(set playing true)
|
||||
(set time 0)
|
||||
(set step 0))
|
||||
|
||||
(fn stop []
|
||||
(set playing false)
|
||||
(pxl8.sfx_stop_all ctx))
|
||||
|
||||
(fn update [dt]
|
||||
(when playing
|
||||
(set time (+ time dt))
|
||||
(when (>= time step-duration)
|
||||
(set time (- time step-duration))
|
||||
(local note (. melody (+ 1 (% step (length melody)))))
|
||||
(pxl8.sfx_play_note ctx note params 0.4)
|
||||
(when (= (% step 4) 0)
|
||||
(local bass-note (. bass (+ 1 (% (math.floor (/ step 4)) (length bass)))))
|
||||
(pxl8.sfx_play_note ctx bass-note bass-params 0.35))
|
||||
(set step (+ step 1)))))
|
||||
|
||||
{:init init
|
||||
:start start
|
||||
:stop stop
|
||||
:update update
|
||||
:is-playing (fn [] playing)}
|
||||
BIN
demo/res/tiles/tilesheet-dungeon.ase
Normal file
BIN
demo/res/tiles/tilesheet-dungeon.ase
Normal file
Binary file not shown.
BIN
demo/res/tiles/tilesheet-world.ase
Normal file
BIN
demo/res/tiles/tilesheet-world.ase
Normal file
Binary file not shown.
1
pxl8.sh
1
pxl8.sh
|
|
@ -347,6 +347,7 @@ case "$COMMAND" in
|
|||
src/pxl8_save.c
|
||||
src/pxl8_script.c
|
||||
src/pxl8_sdl3.c
|
||||
src/pxl8_sfx.c
|
||||
src/pxl8_tilemap.c
|
||||
src/pxl8_tilesheet.c
|
||||
src/pxl8_transition.c
|
||||
|
|
|
|||
|
|
@ -12,8 +12,9 @@ local gui = require("pxl8.gui")
|
|||
local world = require("pxl8.world")
|
||||
local transition = require("pxl8.transition")
|
||||
local anim = require("pxl8.anim")
|
||||
local sfx = require("pxl8.sfx")
|
||||
|
||||
core.init(_pxl8_gfx, _pxl8_input, _pxl8_sys)
|
||||
core.init(_pxl8_gfx, _pxl8_input, _pxl8_sfx_mixer, _pxl8_sys)
|
||||
|
||||
local pxl8 = {}
|
||||
|
||||
|
|
@ -189,4 +190,57 @@ pxl8.anim_set_state = anim.set_state
|
|||
pxl8.anim_stop = anim.stop
|
||||
pxl8.anim_update = anim.update
|
||||
|
||||
pxl8.sfx_compressor_create = sfx.compressor_create
|
||||
pxl8.sfx_compressor_set_attack = sfx.compressor_set_attack
|
||||
pxl8.sfx_compressor_set_ratio = sfx.compressor_set_ratio
|
||||
pxl8.sfx_compressor_set_release = sfx.compressor_set_release
|
||||
pxl8.sfx_compressor_set_threshold = sfx.compressor_set_threshold
|
||||
pxl8.sfx_context_append_node = sfx.context_append_node
|
||||
pxl8.sfx_context_create = sfx.context_create
|
||||
pxl8.sfx_context_destroy = sfx.context_destroy
|
||||
pxl8.sfx_context_get_head = sfx.context_get_head
|
||||
pxl8.sfx_context_get_volume = sfx.context_get_volume
|
||||
pxl8.sfx_context_insert_node = sfx.context_insert_node
|
||||
pxl8.sfx_context_remove_node = sfx.context_remove_node
|
||||
pxl8.sfx_context_set_volume = sfx.context_set_volume
|
||||
pxl8.sfx_delay_create = sfx.delay_create
|
||||
pxl8.sfx_delay_set_feedback = sfx.delay_set_feedback
|
||||
pxl8.sfx_delay_set_mix = sfx.delay_set_mix
|
||||
pxl8.sfx_delay_set_time = sfx.delay_set_time
|
||||
pxl8.sfx_get_master_volume = sfx.get_master_volume
|
||||
pxl8.sfx_mixer_attach = sfx.mixer_attach
|
||||
pxl8.sfx_mixer_detach = sfx.mixer_detach
|
||||
pxl8.sfx_node_destroy = sfx.node_destroy
|
||||
pxl8.sfx_note_to_freq = sfx.note_to_freq
|
||||
pxl8.sfx_play_note = sfx.play_note
|
||||
pxl8.sfx_release_voice = sfx.release_voice
|
||||
pxl8.sfx_reverb_create = sfx.reverb_create
|
||||
pxl8.sfx_reverb_set_damping = sfx.reverb_set_damping
|
||||
pxl8.sfx_reverb_set_mix = sfx.reverb_set_mix
|
||||
pxl8.sfx_reverb_set_room = sfx.reverb_set_room
|
||||
pxl8.sfx_set_master_volume = sfx.set_master_volume
|
||||
pxl8.sfx_stop_all = sfx.stop_all
|
||||
pxl8.sfx_stop_voice = sfx.stop_voice
|
||||
pxl8.sfx_voice_params = sfx.voice_params
|
||||
|
||||
pxl8.SFX_FILTER_BANDPASS = sfx.FILTER_BANDPASS
|
||||
pxl8.SFX_FILTER_HIGHPASS = sfx.FILTER_HIGHPASS
|
||||
pxl8.SFX_FILTER_LOWPASS = sfx.FILTER_LOWPASS
|
||||
pxl8.SFX_FILTER_NONE = sfx.FILTER_NONE
|
||||
|
||||
pxl8.SFX_LFO_AMPLITUDE = sfx.LFO_AMPLITUDE
|
||||
pxl8.SFX_LFO_FILTER = sfx.LFO_FILTER
|
||||
pxl8.SFX_LFO_PITCH = sfx.LFO_PITCH
|
||||
|
||||
pxl8.SFX_NODE_COMPRESSOR = sfx.NODE_COMPRESSOR
|
||||
pxl8.SFX_NODE_DELAY = sfx.NODE_DELAY
|
||||
pxl8.SFX_NODE_REVERB = sfx.NODE_REVERB
|
||||
|
||||
pxl8.SFX_WAVE_NOISE = sfx.WAVE_NOISE
|
||||
pxl8.SFX_WAVE_PULSE = sfx.WAVE_PULSE
|
||||
pxl8.SFX_WAVE_SAW = sfx.WAVE_SAW
|
||||
pxl8.SFX_WAVE_SINE = sfx.WAVE_SINE
|
||||
pxl8.SFX_WAVE_SQUARE = sfx.WAVE_SQUARE
|
||||
pxl8.SFX_WAVE_TRIANGLE = sfx.WAVE_TRIANGLE
|
||||
|
||||
return pxl8
|
||||
|
|
|
|||
|
|
@ -3,10 +3,11 @@ local C = ffi.C
|
|||
|
||||
local core = {}
|
||||
|
||||
function core.init(gfx_ptr, input_ptr, sys_ptr)
|
||||
core.gfx = gfx_ptr
|
||||
core.input = input_ptr
|
||||
core.sys = sys_ptr
|
||||
function core.init(gfx, input, sfx_mixer, sys)
|
||||
core.gfx = gfx
|
||||
core.input = input
|
||||
core.sfx_mixer = sfx_mixer
|
||||
core.sys = sys
|
||||
end
|
||||
|
||||
function core.get_fps()
|
||||
|
|
|
|||
198
src/lua/pxl8/sfx.lua
Normal file
198
src/lua/pxl8/sfx.lua
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
local ffi = require("ffi")
|
||||
local C = ffi.C
|
||||
local core = require("pxl8.core")
|
||||
|
||||
local sfx = {}
|
||||
|
||||
sfx.FILTER_BANDPASS = C.PXL8_SFX_FILTER_BANDPASS
|
||||
sfx.FILTER_HIGHPASS = C.PXL8_SFX_FILTER_HIGHPASS
|
||||
sfx.FILTER_LOWPASS = C.PXL8_SFX_FILTER_LOWPASS
|
||||
sfx.FILTER_NONE = C.PXL8_SFX_FILTER_NONE
|
||||
|
||||
sfx.LFO_AMPLITUDE = C.PXL8_SFX_LFO_AMPLITUDE
|
||||
sfx.LFO_FILTER = C.PXL8_SFX_LFO_FILTER
|
||||
sfx.LFO_PITCH = C.PXL8_SFX_LFO_PITCH
|
||||
|
||||
sfx.NODE_COMPRESSOR = C.PXL8_SFX_NODE_COMPRESSOR
|
||||
sfx.NODE_DELAY = C.PXL8_SFX_NODE_DELAY
|
||||
sfx.NODE_REVERB = C.PXL8_SFX_NODE_REVERB
|
||||
|
||||
sfx.WAVE_NOISE = C.PXL8_SFX_WAVE_NOISE
|
||||
sfx.WAVE_PULSE = C.PXL8_SFX_WAVE_PULSE
|
||||
sfx.WAVE_SAW = C.PXL8_SFX_WAVE_SAW
|
||||
sfx.WAVE_SINE = C.PXL8_SFX_WAVE_SINE
|
||||
sfx.WAVE_SQUARE = C.PXL8_SFX_WAVE_SQUARE
|
||||
sfx.WAVE_TRIANGLE = C.PXL8_SFX_WAVE_TRIANGLE
|
||||
|
||||
function sfx.compressor_create(opts)
|
||||
opts = opts or {}
|
||||
local cfg = ffi.new("pxl8_sfx_compressor_config")
|
||||
cfg.attack = opts.attack or 10
|
||||
cfg.ratio = opts.ratio or 4
|
||||
cfg.release = opts.release or 100
|
||||
cfg.threshold = opts.threshold or -12
|
||||
return C.pxl8_sfx_compressor_create(cfg)
|
||||
end
|
||||
|
||||
function sfx.compressor_set_attack(node, attack)
|
||||
C.pxl8_sfx_compressor_set_attack(node, attack)
|
||||
end
|
||||
|
||||
function sfx.compressor_set_ratio(node, ratio)
|
||||
C.pxl8_sfx_compressor_set_ratio(node, ratio)
|
||||
end
|
||||
|
||||
function sfx.compressor_set_release(node, release)
|
||||
C.pxl8_sfx_compressor_set_release(node, release)
|
||||
end
|
||||
|
||||
function sfx.compressor_set_threshold(node, threshold)
|
||||
C.pxl8_sfx_compressor_set_threshold(node, threshold)
|
||||
end
|
||||
|
||||
function sfx.context_append_node(ctx, node)
|
||||
C.pxl8_sfx_context_append_node(ctx, node)
|
||||
end
|
||||
|
||||
function sfx.context_create()
|
||||
return C.pxl8_sfx_context_create()
|
||||
end
|
||||
|
||||
function sfx.context_destroy(ctx)
|
||||
C.pxl8_sfx_context_destroy(ctx)
|
||||
end
|
||||
|
||||
function sfx.context_get_head(ctx)
|
||||
return C.pxl8_sfx_context_get_head(ctx)
|
||||
end
|
||||
|
||||
function sfx.context_get_volume(ctx)
|
||||
return C.pxl8_sfx_context_get_volume(ctx)
|
||||
end
|
||||
|
||||
function sfx.context_insert_node(ctx, after, node)
|
||||
C.pxl8_sfx_context_insert_node(ctx, after, node)
|
||||
end
|
||||
|
||||
function sfx.context_remove_node(ctx, node)
|
||||
C.pxl8_sfx_context_remove_node(ctx, node)
|
||||
end
|
||||
|
||||
function sfx.context_set_volume(ctx, volume)
|
||||
C.pxl8_sfx_context_set_volume(ctx, volume)
|
||||
end
|
||||
|
||||
function sfx.delay_create(opts)
|
||||
opts = opts or {}
|
||||
local cfg = ffi.new("pxl8_sfx_delay_config")
|
||||
cfg.feedback = opts.feedback or 0.4
|
||||
cfg.mix = opts.mix or 0.25
|
||||
cfg.time_l = opts.time_l or 350
|
||||
cfg.time_r = opts.time_r or 500
|
||||
return C.pxl8_sfx_delay_create(cfg)
|
||||
end
|
||||
|
||||
function sfx.delay_set_feedback(node, feedback)
|
||||
C.pxl8_sfx_delay_set_feedback(node, feedback)
|
||||
end
|
||||
|
||||
function sfx.delay_set_mix(node, mix)
|
||||
C.pxl8_sfx_delay_set_mix(node, mix)
|
||||
end
|
||||
|
||||
function sfx.delay_set_time(node, time_l, time_r)
|
||||
C.pxl8_sfx_delay_set_time(node, time_l, time_r)
|
||||
end
|
||||
|
||||
function sfx.get_master_volume()
|
||||
return C.pxl8_sfx_mixer_get_master_volume(core.sfx_mixer)
|
||||
end
|
||||
|
||||
function sfx.mixer_attach(ctx)
|
||||
C.pxl8_sfx_mixer_attach(core.sfx_mixer, ctx)
|
||||
end
|
||||
|
||||
function sfx.mixer_detach(ctx)
|
||||
C.pxl8_sfx_mixer_detach(core.sfx_mixer, ctx)
|
||||
end
|
||||
|
||||
function sfx.node_destroy(node)
|
||||
C.pxl8_sfx_node_destroy(node)
|
||||
end
|
||||
|
||||
function sfx.note_to_freq(note)
|
||||
return C.pxl8_sfx_note_to_freq(note)
|
||||
end
|
||||
|
||||
function sfx.play_note(ctx, note, params, volume)
|
||||
return C.pxl8_sfx_play_note(ctx, note, params, volume or 0.8)
|
||||
end
|
||||
|
||||
function sfx.release_voice(ctx, voice_id)
|
||||
C.pxl8_sfx_release_voice(ctx, voice_id)
|
||||
end
|
||||
|
||||
function sfx.reverb_create(opts)
|
||||
opts = opts or {}
|
||||
local cfg = ffi.new("pxl8_sfx_reverb_config")
|
||||
cfg.damping = opts.damping or 0.5
|
||||
cfg.mix = opts.mix or 0.3
|
||||
cfg.room = opts.room or 0.5
|
||||
return C.pxl8_sfx_reverb_create(cfg)
|
||||
end
|
||||
|
||||
function sfx.reverb_set_damping(node, damping)
|
||||
C.pxl8_sfx_reverb_set_damping(node, damping)
|
||||
end
|
||||
|
||||
function sfx.reverb_set_mix(node, mix)
|
||||
C.pxl8_sfx_reverb_set_mix(node, mix)
|
||||
end
|
||||
|
||||
function sfx.reverb_set_room(node, room)
|
||||
C.pxl8_sfx_reverb_set_room(node, room)
|
||||
end
|
||||
|
||||
function sfx.set_master_volume(volume)
|
||||
C.pxl8_sfx_mixer_set_master_volume(core.sfx_mixer, volume)
|
||||
end
|
||||
|
||||
function sfx.stop_all(ctx)
|
||||
C.pxl8_sfx_stop_all(ctx)
|
||||
end
|
||||
|
||||
function sfx.stop_voice(ctx, voice_id)
|
||||
C.pxl8_sfx_stop_voice(ctx, voice_id)
|
||||
end
|
||||
|
||||
function sfx.voice_params(opts)
|
||||
opts = opts or {}
|
||||
local params = ffi.new("pxl8_sfx_voice_params")
|
||||
|
||||
params.amp_env.attack = opts.attack or 0.01
|
||||
params.amp_env.decay = opts.decay or 0.1
|
||||
params.amp_env.sustain = opts.sustain or 0.5
|
||||
params.amp_env.release = opts.release or 0.2
|
||||
|
||||
params.filter_env.attack = opts.filter_attack or 0.01
|
||||
params.filter_env.decay = opts.filter_decay or 0.1
|
||||
params.filter_env.sustain = opts.filter_sustain or 0.3
|
||||
params.filter_env.release = opts.filter_release or 0.1
|
||||
|
||||
params.filter_type = opts.filter_type or C.PXL8_SFX_FILTER_NONE
|
||||
params.lfo_target = opts.lfo_target or C.PXL8_SFX_LFO_PITCH
|
||||
params.lfo_waveform = opts.lfo_waveform or C.PXL8_SFX_WAVE_SINE
|
||||
params.waveform = opts.waveform or C.PXL8_SFX_WAVE_SINE
|
||||
|
||||
params.filter_cutoff = opts.filter_cutoff or 4000
|
||||
params.filter_env_depth = opts.filter_env_depth or 0
|
||||
params.filter_resonance = opts.filter_resonance or 0
|
||||
params.fx_send = opts.fx_send or 0
|
||||
params.lfo_depth = opts.lfo_depth or 0
|
||||
params.lfo_rate = opts.lfo_rate or 0
|
||||
params.pulse_width = opts.pulse_width or 0.5
|
||||
|
||||
return params
|
||||
end
|
||||
|
||||
return sfx
|
||||
12
src/pxl8.c
12
src/pxl8.c
|
|
@ -218,12 +218,19 @@ pxl8_result pxl8_init(pxl8* sys, i32 argc, char* argv[]) {
|
|||
return PXL8_ERROR_INITIALIZATION_FAILED;
|
||||
}
|
||||
|
||||
game->mixer = pxl8_sfx_mixer_create();
|
||||
if (!game->mixer) {
|
||||
pxl8_error("failed to create audio mixer");
|
||||
return PXL8_ERROR_INITIALIZATION_FAILED;
|
||||
}
|
||||
|
||||
if (game->repl_mode) {
|
||||
pxl8_info("starting in REPL mode with script: %s", game->script_path);
|
||||
}
|
||||
|
||||
pxl8_script_set_gfx(game->script, game->gfx);
|
||||
pxl8_script_set_input(game->script, &game->input);
|
||||
pxl8_script_set_sfx(game->script, game->mixer);
|
||||
pxl8_script_set_sys(game->script, sys);
|
||||
|
||||
if (game->script_path[0] != '\0') {
|
||||
|
|
@ -364,6 +371,7 @@ void pxl8_quit(pxl8* sys) {
|
|||
pxl8_cart_unmount(sys->cart);
|
||||
}
|
||||
|
||||
pxl8_sfx_mixer_destroy(game->mixer);
|
||||
pxl8_gfx_destroy(game->gfx);
|
||||
pxl8_script_destroy(game->script);
|
||||
}
|
||||
|
|
@ -394,6 +402,10 @@ pxl8_input_state* pxl8_get_input(const pxl8* sys) {
|
|||
return (sys && sys->game) ? &sys->game->input : NULL;
|
||||
}
|
||||
|
||||
pxl8_sfx_mixer* pxl8_get_sfx_mixer(const pxl8* sys) {
|
||||
return (sys && sys->game) ? sys->game->mixer : NULL;
|
||||
}
|
||||
|
||||
void pxl8_center_cursor(pxl8* sys) {
|
||||
if (!sys || !sys->hal || !sys->hal->center_cursor) return;
|
||||
sys->hal->center_cursor(sys->platform_data);
|
||||
|
|
|
|||
|
|
@ -199,6 +199,22 @@ void pxl8_atlas_destroy(pxl8_atlas* atlas) {
|
|||
free(atlas);
|
||||
}
|
||||
|
||||
void pxl8_atlas_clear(pxl8_atlas* atlas, u32 preserve_count) {
|
||||
if (!atlas) return;
|
||||
|
||||
for (u32 i = preserve_count; i < atlas->entry_count; i++) {
|
||||
atlas->entries[i].active = false;
|
||||
}
|
||||
|
||||
atlas->entry_count = preserve_count;
|
||||
atlas->free_count = 0;
|
||||
|
||||
atlas->skyline.nodes[0] = (pxl8_skyline_node){0, 0, (i32)atlas->width};
|
||||
atlas->skyline.count = 1;
|
||||
|
||||
atlas->dirty = true;
|
||||
}
|
||||
|
||||
bool pxl8_atlas_expand(pxl8_atlas* atlas, pxl8_pixel_mode pixel_mode) {
|
||||
if (!atlas || atlas->width >= 4096) return false;
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ bool pxl8_atlas_is_dirty(const pxl8_atlas* atlas);
|
|||
void pxl8_atlas_mark_clean(pxl8_atlas* atlas);
|
||||
|
||||
u32 pxl8_atlas_add_texture(pxl8_atlas* atlas, const u8* pixels, u32 w, u32 h, pxl8_pixel_mode pixel_mode);
|
||||
void pxl8_atlas_clear(pxl8_atlas* atlas, u32 preserve_count);
|
||||
bool pxl8_atlas_expand(pxl8_atlas* atlas, pxl8_pixel_mode pixel_mode);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
|||
|
|
@ -43,6 +43,10 @@ static const char embed_pxl8_particles[] = {
|
|||
#embed "src/lua/pxl8/particles.lua"
|
||||
, 0 };
|
||||
|
||||
static const char embed_pxl8_sfx[] = {
|
||||
#embed "src/lua/pxl8/sfx.lua"
|
||||
, 0 };
|
||||
|
||||
static const char embed_pxl8_tilemap[] = {
|
||||
#embed "src/lua/pxl8/tilemap.lua"
|
||||
, 0 };
|
||||
|
|
@ -74,6 +78,7 @@ static const pxl8_embed pxl8_embeds[] = {
|
|||
PXL8_EMBED_ENTRY(embed_pxl8_input, "pxl8.input"),
|
||||
PXL8_EMBED_ENTRY(embed_pxl8_math, "pxl8.math"),
|
||||
PXL8_EMBED_ENTRY(embed_pxl8_particles, "pxl8.particles"),
|
||||
PXL8_EMBED_ENTRY(embed_pxl8_sfx, "pxl8.sfx"),
|
||||
PXL8_EMBED_ENTRY(embed_pxl8_tilemap, "pxl8.tilemap"),
|
||||
PXL8_EMBED_ENTRY(embed_pxl8_transition, "pxl8.transition"),
|
||||
PXL8_EMBED_ENTRY(embed_pxl8_vfx, "pxl8.vfx"),
|
||||
|
|
|
|||
|
|
@ -2,11 +2,13 @@
|
|||
|
||||
#include "pxl8_gfx.h"
|
||||
#include "pxl8_script.h"
|
||||
#include "pxl8_sfx.h"
|
||||
#include "pxl8_types.h"
|
||||
|
||||
typedef struct pxl8_game {
|
||||
pxl8_gfx* gfx;
|
||||
pxl8_script* script;
|
||||
pxl8_sfx_mixer* mixer;
|
||||
|
||||
i32 frame_count;
|
||||
u64 last_time;
|
||||
|
|
|
|||
|
|
@ -197,6 +197,18 @@ static pxl8_result pxl8_gfx_ensure_atlas(pxl8_gfx* gfx) {
|
|||
return gfx->atlas ? PXL8_OK : PXL8_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
void pxl8_gfx_clear_textures(pxl8_gfx* gfx) {
|
||||
if (!gfx) return;
|
||||
|
||||
if (gfx->atlas) {
|
||||
pxl8_atlas_clear(gfx->atlas, 0);
|
||||
}
|
||||
|
||||
if (gfx->sprite_cache) {
|
||||
gfx->sprite_cache_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pxl8_result pxl8_gfx_create_texture(pxl8_gfx* gfx, const u8* pixels, u32 width, u32 height) {
|
||||
if (!gfx || !gfx->initialized || !pixels) return PXL8_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
|
|
|
|||
|
|
@ -25,36 +25,37 @@ extern "C" {
|
|||
pxl8_gfx* pxl8_gfx_create(const pxl8_hal* hal, void* platform_data, pxl8_pixel_mode mode, pxl8_resolution resolution);
|
||||
void pxl8_gfx_destroy(pxl8_gfx* gfx);
|
||||
|
||||
void pxl8_gfx_present(pxl8_gfx* gfx);
|
||||
void pxl8_gfx_update(pxl8_gfx* gfx, f32 dt);
|
||||
void pxl8_gfx_upload_framebuffer(pxl8_gfx* gfx);
|
||||
|
||||
pxl8_bounds pxl8_gfx_get_bounds(pxl8_gfx* gfx);
|
||||
pxl8_pixel_mode pxl8_gfx_get_pixel_mode(pxl8_gfx* gfx);
|
||||
u8* pxl8_gfx_get_framebuffer_indexed(pxl8_gfx* gfx);
|
||||
u16* pxl8_gfx_get_framebuffer_hicolor(pxl8_gfx* gfx);
|
||||
i32 pxl8_gfx_get_height(const pxl8_gfx* gfx);
|
||||
u32 pxl8_gfx_get_palette_size(const pxl8_gfx* gfx);
|
||||
pxl8_pixel_mode pxl8_gfx_get_pixel_mode(pxl8_gfx* gfx);
|
||||
i32 pxl8_gfx_get_width(const pxl8_gfx* gfx);
|
||||
|
||||
void pxl8_gfx_project(pxl8_gfx* gfx, f32 left, f32 right, f32 top, f32 bottom);
|
||||
void pxl8_gfx_set_viewport(pxl8_gfx* gfx, pxl8_viewport vp);
|
||||
pxl8_viewport pxl8_gfx_viewport(pxl8_bounds bounds, i32 width, i32 height);
|
||||
|
||||
void pxl8_gfx_clear_textures(pxl8_gfx* gfx);
|
||||
pxl8_result pxl8_gfx_create_texture(pxl8_gfx* gfx, const u8* pixels, u32 width, u32 height);
|
||||
pxl8_result pxl8_gfx_load_font_atlas(pxl8_gfx* gfx);
|
||||
pxl8_result pxl8_gfx_load_palette(pxl8_gfx* gfx, const char* path);
|
||||
pxl8_result pxl8_gfx_load_sprite(pxl8_gfx* gfx, const char* path);
|
||||
void pxl8_gfx_present(pxl8_gfx* gfx);
|
||||
void pxl8_gfx_project(pxl8_gfx* gfx, f32 left, f32 right, f32 top, f32 bottom);
|
||||
void pxl8_gfx_upload_framebuffer(pxl8_gfx* gfx);
|
||||
pxl8_viewport pxl8_gfx_viewport(pxl8_bounds bounds, i32 width, i32 height);
|
||||
|
||||
i32 pxl8_gfx_add_palette_cycle(pxl8_gfx* gfx, u8 start_index, u8 end_index, f32 speed);
|
||||
void pxl8_gfx_clear_palette_cycles(pxl8_gfx* gfx);
|
||||
void pxl8_gfx_color_ramp(pxl8_gfx* gfx, u8 start, u8 count, u32 from_color, u32 to_color);
|
||||
void pxl8_gfx_cycle_palette(pxl8_gfx* gfx, u8 start, u8 count, i32 step);
|
||||
void pxl8_gfx_fade_palette(pxl8_gfx* gfx, u8 start, u8 count, f32 amount, u32 target_color);
|
||||
void pxl8_gfx_interpolate_palettes(pxl8_gfx* gfx, u32* palette1, u32* palette2, u8 start, u8 count, f32 t);
|
||||
void pxl8_gfx_swap_palette(pxl8_gfx* gfx, u8 start, u8 count, u32* new_colors);
|
||||
|
||||
i32 pxl8_gfx_add_palette_cycle(pxl8_gfx* gfx, u8 start_index, u8 end_index, f32 speed);
|
||||
void pxl8_gfx_remove_palette_cycle(pxl8_gfx* gfx, i32 cycle_id);
|
||||
void pxl8_gfx_set_palette_cycle_speed(pxl8_gfx* gfx, i32 cycle_id, f32 speed);
|
||||
void pxl8_gfx_clear_palette_cycles(pxl8_gfx* gfx);
|
||||
void pxl8_gfx_update(pxl8_gfx* gfx, f32 dt);
|
||||
void pxl8_gfx_swap_palette(pxl8_gfx* gfx, u8 start, u8 count, u32* new_colors);
|
||||
|
||||
void pxl8_circle(pxl8_gfx* gfx, i32 cx, i32 cy, i32 radius, u32 color);
|
||||
void pxl8_circle_fill(pxl8_gfx* gfx, i32 cx, i32 cy, i32 radius, u32 color);
|
||||
|
|
@ -68,6 +69,10 @@ void pxl8_sprite(pxl8_gfx* gfx, u32 sprite_id, i32 x, i32 y, i32 w, i32 h);
|
|||
void pxl8_text(pxl8_gfx* gfx, const char* text, i32 x, i32 y, u32 color);
|
||||
|
||||
void pxl8_3d_clear_zbuffer(pxl8_gfx* gfx);
|
||||
void pxl8_3d_draw_line_3d(pxl8_gfx* gfx, pxl8_vec3 p0, pxl8_vec3 p1, u32 color);
|
||||
void pxl8_3d_draw_triangle(pxl8_gfx* gfx, pxl8_triangle tri);
|
||||
void pxl8_3d_draw_triangle_raw(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, pxl8_vec3 v2, u32 color);
|
||||
void pxl8_3d_draw_triangle_textured(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, pxl8_vec3 v2, f32 u0, f32 v0f, f32 u1, f32 v1f, f32 u2, f32 v2f, u32 texture_id);
|
||||
const pxl8_frustum* pxl8_3d_get_frustum(pxl8_gfx* gfx);
|
||||
void pxl8_3d_set_affine_textures(pxl8_gfx* gfx, bool affine);
|
||||
void pxl8_3d_set_backface_culling(pxl8_gfx* gfx, bool culling);
|
||||
|
|
@ -75,10 +80,6 @@ void pxl8_3d_set_model(pxl8_gfx* gfx, pxl8_mat4 mat);
|
|||
void pxl8_3d_set_projection(pxl8_gfx* gfx, pxl8_mat4 mat);
|
||||
void pxl8_3d_set_view(pxl8_gfx* gfx, pxl8_mat4 mat);
|
||||
void pxl8_3d_set_wireframe(pxl8_gfx* gfx, bool wireframe);
|
||||
void pxl8_3d_draw_line_3d(pxl8_gfx* gfx, pxl8_vec3 p0, pxl8_vec3 p1, u32 color);
|
||||
void pxl8_3d_draw_triangle(pxl8_gfx* gfx, pxl8_triangle tri);
|
||||
void pxl8_3d_draw_triangle_raw(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, pxl8_vec3 v2, u32 color);
|
||||
void pxl8_3d_draw_triangle_textured(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, pxl8_vec3 v2, f32 u0, f32 v0f, f32 u1, f32 v1f, f32 u2, f32 v2f, u32 texture_id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@
|
|||
|
||||
#include "pxl8_types.h"
|
||||
|
||||
#define PXL8_PI 3.14159265358979323846f
|
||||
#define PXL8_TAU (PXL8_PI * 2.0f)
|
||||
|
||||
typedef struct pxl8_vec2 {
|
||||
f32 x, y;
|
||||
} pxl8_vec2;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "pxl8_cart.h"
|
||||
#include "pxl8_gfx.h"
|
||||
#include "pxl8_sfx.h"
|
||||
#include "pxl8_types.h"
|
||||
|
||||
typedef struct pxl8_script pxl8_script;
|
||||
|
|
@ -19,6 +20,7 @@ bool pxl8_script_is_incomplete_input(pxl8_script* script);
|
|||
void pxl8_script_set_cart_path(pxl8_script* script, const char* cart_path, const char* original_cwd);
|
||||
void pxl8_script_set_gfx(pxl8_script* script, pxl8_gfx* gfx);
|
||||
void pxl8_script_set_input(pxl8_script* script, pxl8_input_state* input);
|
||||
void pxl8_script_set_sfx(pxl8_script* script, pxl8_sfx_mixer* mixer);
|
||||
void pxl8_script_set_sys(pxl8_script* script, void* sys);
|
||||
|
||||
pxl8_result pxl8_script_call_function(pxl8_script* script, const char* name);
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ static void sdl3_upload_texture(void* platform_data, const u8* pixels, u32 w, u3
|
|||
}
|
||||
|
||||
SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) {
|
||||
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS)) {
|
||||
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_AUDIO)) {
|
||||
pxl8_error("SDL_Init failed: %s", SDL_GetError());
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
|
|
|||
1092
src/pxl8_sfx.c
Normal file
1092
src/pxl8_sfx.c
Normal file
File diff suppressed because it is too large
Load diff
133
src/pxl8_sfx.h
Normal file
133
src/pxl8_sfx.h
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
#pragma once
|
||||
|
||||
#include "pxl8_types.h"
|
||||
|
||||
#define PXL8_SFX_BUFFER_SIZE 1024
|
||||
#define PXL8_SFX_MAX_CONTEXTS 8
|
||||
#define PXL8_SFX_MAX_DELAY_SAMPLES 44100
|
||||
#define PXL8_SFX_MAX_VOICES 16
|
||||
#define PXL8_SFX_SAMPLE_RATE 44100
|
||||
|
||||
typedef struct pxl8_sfx_context pxl8_sfx_context;
|
||||
typedef struct pxl8_sfx_mixer pxl8_sfx_mixer;
|
||||
typedef struct pxl8_sfx_node pxl8_sfx_node;
|
||||
|
||||
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;
|
||||
|
||||
typedef enum pxl8_sfx_lfo_target {
|
||||
PXL8_SFX_LFO_AMPLITUDE = 0,
|
||||
PXL8_SFX_LFO_FILTER,
|
||||
PXL8_SFX_LFO_PITCH
|
||||
} pxl8_sfx_lfo_target;
|
||||
|
||||
typedef enum pxl8_sfx_node_type {
|
||||
PXL8_SFX_NODE_COMPRESSOR,
|
||||
PXL8_SFX_NODE_DELAY,
|
||||
PXL8_SFX_NODE_REVERB
|
||||
} pxl8_sfx_node_type;
|
||||
|
||||
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;
|
||||
|
||||
typedef struct pxl8_sfx_adsr {
|
||||
f32 attack;
|
||||
f32 decay;
|
||||
f32 sustain;
|
||||
f32 release;
|
||||
} pxl8_sfx_adsr;
|
||||
|
||||
typedef struct pxl8_sfx_compressor_config {
|
||||
f32 attack;
|
||||
f32 ratio;
|
||||
f32 release;
|
||||
f32 threshold;
|
||||
} pxl8_sfx_compressor_config;
|
||||
|
||||
typedef struct pxl8_sfx_delay_config {
|
||||
f32 feedback;
|
||||
f32 mix;
|
||||
u32 time_l;
|
||||
u32 time_r;
|
||||
} pxl8_sfx_delay_config;
|
||||
|
||||
typedef struct pxl8_sfx_reverb_config {
|
||||
f32 damping;
|
||||
f32 mix;
|
||||
f32 room;
|
||||
} pxl8_sfx_reverb_config;
|
||||
|
||||
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;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
pxl8_sfx_node* pxl8_sfx_compressor_create(pxl8_sfx_compressor_config cfg);
|
||||
void pxl8_sfx_compressor_set_attack(pxl8_sfx_node* node, f32 attack);
|
||||
void pxl8_sfx_compressor_set_ratio(pxl8_sfx_node* node, f32 ratio);
|
||||
void pxl8_sfx_compressor_set_release(pxl8_sfx_node* node, f32 release);
|
||||
void pxl8_sfx_compressor_set_threshold(pxl8_sfx_node* node, f32 threshold);
|
||||
|
||||
void pxl8_sfx_context_append_node(pxl8_sfx_context* ctx, pxl8_sfx_node* node);
|
||||
pxl8_sfx_context* pxl8_sfx_context_create(void);
|
||||
void pxl8_sfx_context_destroy(pxl8_sfx_context* ctx);
|
||||
pxl8_sfx_node* pxl8_sfx_context_get_head(pxl8_sfx_context* ctx);
|
||||
f32 pxl8_sfx_context_get_volume(const pxl8_sfx_context* ctx);
|
||||
void pxl8_sfx_context_insert_node(pxl8_sfx_context* ctx, pxl8_sfx_node* after, pxl8_sfx_node* node);
|
||||
void pxl8_sfx_context_remove_node(pxl8_sfx_context* ctx, pxl8_sfx_node* node);
|
||||
void pxl8_sfx_context_set_volume(pxl8_sfx_context* ctx, f32 volume);
|
||||
|
||||
pxl8_sfx_node* pxl8_sfx_delay_create(pxl8_sfx_delay_config cfg);
|
||||
void pxl8_sfx_delay_set_feedback(pxl8_sfx_node* node, f32 feedback);
|
||||
void pxl8_sfx_delay_set_mix(pxl8_sfx_node* node, f32 mix);
|
||||
void pxl8_sfx_delay_set_time(pxl8_sfx_node* node, u32 time_l, u32 time_r);
|
||||
|
||||
void pxl8_sfx_mixer_attach(pxl8_sfx_mixer* mixer, pxl8_sfx_context* ctx);
|
||||
void pxl8_sfx_mixer_clear(pxl8_sfx_mixer* mixer);
|
||||
pxl8_sfx_mixer* pxl8_sfx_mixer_create(void);
|
||||
void pxl8_sfx_mixer_destroy(pxl8_sfx_mixer* mixer);
|
||||
void pxl8_sfx_mixer_detach(pxl8_sfx_mixer* mixer, pxl8_sfx_context* ctx);
|
||||
f32 pxl8_sfx_mixer_get_master_volume(const pxl8_sfx_mixer* mixer);
|
||||
void pxl8_sfx_mixer_set_master_volume(pxl8_sfx_mixer* mixer, f32 volume);
|
||||
|
||||
void pxl8_sfx_node_destroy(pxl8_sfx_node* node);
|
||||
f32 pxl8_sfx_note_to_freq(u8 note);
|
||||
u16 pxl8_sfx_play_note(pxl8_sfx_context* ctx, u8 note, const pxl8_sfx_voice_params* params, f32 volume);
|
||||
void pxl8_sfx_release_voice(pxl8_sfx_context* ctx, u16 voice_id);
|
||||
|
||||
pxl8_sfx_node* pxl8_sfx_reverb_create(pxl8_sfx_reverb_config cfg);
|
||||
void pxl8_sfx_reverb_set_damping(pxl8_sfx_node* node, f32 damping);
|
||||
void pxl8_sfx_reverb_set_mix(pxl8_sfx_node* node, f32 mix);
|
||||
void pxl8_sfx_reverb_set_room(pxl8_sfx_node* node, f32 room);
|
||||
|
||||
void pxl8_sfx_stop_all(pxl8_sfx_context* ctx);
|
||||
void pxl8_sfx_stop_voice(pxl8_sfx_context* ctx, u16 voice_id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
#include "pxl8_gfx.h"
|
||||
#include "pxl8_hal.h"
|
||||
#include "pxl8_io.h"
|
||||
#include "pxl8_sfx.h"
|
||||
#include "pxl8_types.h"
|
||||
|
||||
typedef struct pxl8 pxl8;
|
||||
|
|
@ -14,22 +15,23 @@ extern "C" {
|
|||
pxl8* pxl8_create(const pxl8_hal* hal);
|
||||
void pxl8_destroy(pxl8* sys);
|
||||
|
||||
pxl8_result pxl8_init(pxl8* sys, i32 argc, char* argv[]);
|
||||
pxl8_result pxl8_update(pxl8* sys);
|
||||
pxl8_result pxl8_frame(pxl8* sys);
|
||||
void pxl8_quit(pxl8* sys);
|
||||
|
||||
f32 pxl8_get_fps(const pxl8* sys);
|
||||
pxl8_gfx* pxl8_get_gfx(const pxl8* sys);
|
||||
pxl8_input_state* pxl8_get_input(const pxl8* sys);
|
||||
pxl8_size pxl8_get_resolution_dimensions(pxl8_resolution resolution);
|
||||
pxl8_sfx_mixer* pxl8_get_sfx_mixer(const pxl8* sys);
|
||||
bool pxl8_is_running(const pxl8* sys);
|
||||
|
||||
void pxl8_center_cursor(pxl8* sys);
|
||||
void pxl8_set_cursor(pxl8* sys, pxl8_cursor cursor);
|
||||
void pxl8_set_relative_mouse_mode(pxl8* sys, bool enabled);
|
||||
void pxl8_set_running(pxl8* sys, bool running);
|
||||
|
||||
void pxl8_center_cursor(pxl8* sys);
|
||||
pxl8_result pxl8_frame(pxl8* sys);
|
||||
pxl8_result pxl8_init(pxl8* sys, i32 argc, char* argv[]);
|
||||
void pxl8_quit(pxl8* sys);
|
||||
pxl8_result pxl8_update(pxl8* sys);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue