diff options
| author | Jonathan Bradley <jcb@pikum.xyz> | 2025-09-18 12:18:00 -0400 |
|---|---|---|
| committer | Jonathan Bradley <jcb@pikum.xyz> | 2025-09-18 12:18:00 -0400 |
| commit | a8e6384c89a204518a210e3e38d6af089fe57f84 (patch) | |
| tree | 3217451bdb07eb03e895aea5dd40a71384e27297 | |
| parent | f5328ba8b42dacd3e47223481dc4f1ea58d9bbd3 (diff) | |
pke-at: first-pass metronome
| -rw-r--r-- | Makefile | 1 | ||||
| -rw-r--r-- | src/level-main.cpp | 29 | ||||
| -rw-r--r-- | src/main.cpp | 10 | ||||
| -rw-r--r-- | src/pke-at-common.cpp | 55 | ||||
| -rw-r--r-- | src/pke-at-common.hpp | 12 | ||||
| -rw-r--r-- | src/pke-at-settings.cpp | 2 | ||||
| -rw-r--r-- | src/pke-at-settings.hpp | 20 | ||||
| -rw-r--r-- | src/pke-at.cpp | 4 |
8 files changed, 122 insertions, 11 deletions
@@ -43,6 +43,7 @@ obj/%.o : src/%.cpp | prepare bin/pke-at: ## Builds the pke-at executable bin/pke-at: obj/main.o bin/pke-at: obj/pke-at-settings.o +bin/pke-at: obj/pke-at-common.o bin/pke-at: obj/pke-at.o bin/pke-at: obj/level-main.o $(CXX) -v -std=c++23 $(BUILD_MODE_FLAGS) $(INCS) $^ $(LDFLAGS) $(CXXFLAGS) -o $@ diff --git a/src/level-main.cpp b/src/level-main.cpp index d0cfcba..26618e4 100644 --- a/src/level-main.cpp +++ b/src/level-main.cpp @@ -1,6 +1,10 @@ #include "level-main.hpp" +#include "pke-at-common.hpp" +#include "pke-at-settings.hpp" +#include "pke/audio-types.hpp" +#include "pke/audio.hpp" #include "pke/level.hpp" struct pke_level_main_master { @@ -22,10 +26,35 @@ void pke_at_level_main_init() { hello_world->min_size = glm::vec2(0.9); hello_world->max_size = glm::vec2(0.9); pke_level_register_root_ui_box(main_mstr.level, hello_world); + pke_at_bpm_reset(120); + g_at.mtrnm.beep = pke_at_audio_get_or_generate_sawtooth(440.f, 0.30); + g_at.mtrnm.beep_accent = pke_at_audio_get_or_generate_sawtooth(440.f * (3/2.f), 0.30); + pke_audio_set_volume(pke_audio_source_sfx, 0.25); } void pke_at_level_main_tick(double delta) { (void)delta; + g_at.bpm.delta_since_last_beat += delta; + if (g_at.bpm.lerp_delta <= g_at.bpm.lerp_delta_duration) { + g_at.bpm.lerp_delta += delta; + g_at.bpm.current = std::lerp(g_at.bpm.last, g_at.bpm.target, g_at.bpm.lerp_delta / g_at.bpm.lerp_delta_duration); + } else { + g_at.bpm.current = g_at.bpm.target; + } + g_at.bpm.delta_per_beat = 60.f / g_at.bpm.current; + g_at.bpm.delta_since_last_beat += delta; + if (g_at.bpm.delta_since_last_beat >= g_at.bpm.delta_per_beat) { + if (g_at.mtrnm.beat == 0) { + pke_audio_play(g_at.mtrnm.beep_accent, pke_audio_source_sfx, pke_audio_flag_none); + fprintf(stdout, "beat accent %f\n", delta); + } else { + pke_audio_play(g_at.mtrnm.beep, pke_audio_source_sfx, pke_audio_flag_none); + fprintf(stdout, "beat %f\n", delta); + } + g_at.bpm.delta_since_last_beat = std::fmod(g_at.bpm.delta_since_last_beat, g_at.bpm.delta_per_beat); + g_at.mtrnm.beat += 1; + g_at.mtrnm.beat %= g_at.mtrnm.beats_per_bar; + } } void pke_at_level_main_teardown() { diff --git a/src/main.cpp b/src/main.cpp index d1e16cd..7da12ee 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -24,15 +24,15 @@ int main(int argc, char *argv[]) { signal(SIGTERM, signal_handler); fprintf(stdout, "PKE_AT ENTERING\n"); { - pke_at_settings.mem.bkt = pk_mem_bucket_create("pke-at main", PK_MEM_DEFAULT_BUCKET_SIZE, PK_MEMBUCKET_FLAG_NONE); - pke_at_settings.mem.bkt_transient = pk_mem_bucket_create("pke-at transient", PK_MEM_DEFAULT_BUCKET_SIZE, PK_MEMBUCKET_FLAG_TRANSIENT); - pk_mem_bucket_set_client_mem_bucket(pke_at_settings.mem.bkt); + g_at.mem.bkt = pk_mem_bucket_create("pke-at main", PK_MEM_DEFAULT_BUCKET_SIZE, PK_MEMBUCKET_FLAG_NONE); + g_at.mem.bkt_transient = pk_mem_bucket_create("pke-at transient", PK_MEM_DEFAULT_BUCKET_SIZE, PK_MEMBUCKET_FLAG_TRANSIENT); + pk_mem_bucket_set_client_mem_bucket(g_at.mem.bkt); pk_arr_append_t(&LoadedPkePlugins, pke_at_plugin); } PkeArgs_Parse(argc, argv); Game_Main({}, argv[0]); - pk_mem_bucket_destroy(pke_at_settings.mem.bkt_transient); - pk_mem_bucket_destroy(pke_at_settings.mem.bkt); + pk_mem_bucket_destroy(g_at.mem.bkt_transient); + pk_mem_bucket_destroy(g_at.mem.bkt); fprintf(stdout, "PKE_AT EXITING\n"); return 0; } diff --git a/src/pke-at-common.cpp b/src/pke-at-common.cpp new file mode 100644 index 0000000..a2a290f --- /dev/null +++ b/src/pke-at-common.cpp @@ -0,0 +1,55 @@ + +#include "pke-at-common.hpp" + +#include "pke-at-settings.hpp" + +#include "pke/audio-types.hpp" + +void pke_at_bpm_reset(uint8_t bpm) { + g_at.bpm.last = bpm; + g_at.bpm.target = bpm; + g_at.bpm.current = bpm; + g_at.bpm.lerp_delta = 0.f; + g_at.bpm.lerp_delta_duration = 0.f; + g_at.bpm.delta_per_beat = 60.f / (double)bpm; + g_at.bpm.delta_since_last_beat = 0.f; + g_at.mtrnm.beat = 0; + g_at.mtrnm.beats_per_bar = 4; +} + +AssetHandle pke_at_audio_get_or_generate_sawtooth(double pitch_freq, double duration) { + uint32_t u; + uint32_t len; + float *bytes; + float phase; + float phase_increment; + pke_asset_details details{}; + AssetHandle handle; + AssetKey key; + + snprintf(key, AssetKeyLength, "saw%.5u;%.2f", (uint32_t)pitch_freq, duration); + fprintf(stdout, "sawtooth: %s%c\n", key, '\0'); + + handle = AM_GetHandle(key); + if (handle != AssetHandle_MAX) { + return handle; + } + + len = std::ceil(PKE_AUDIO_BITRATE * duration); + bytes = pk_new_arr<float>(len, g_at.mem.bkt_transient); + if (bytes == nullptr) { + throw "[pke_at_audio_get_or_generate_sawtooth] failed to alloc"; + } + phase = 0.0f; + phase_increment = pitch_freq / float(PKE_AUDIO_BITRATE); + for (u = 0; u < len; ++u) { + bytes[u] = 2.f * (phase - floor(phase + 0.5f)); + phase += phase_increment; + if (phase >= 1.f) phase -= 1.f; + } + + // TODO + // details.audio. + handle = AM_Register(key, PKE_ASSET_TYPE_AUDIO, bytes, len, 64, &details); + return handle; +} diff --git a/src/pke-at-common.hpp b/src/pke-at-common.hpp new file mode 100644 index 0000000..a7b9a69 --- /dev/null +++ b/src/pke-at-common.hpp @@ -0,0 +1,12 @@ +#ifndef PKE_AT_PKE_AT_COMMON_HPP +#define PKE_AT_PKE_AT_COMMON_HPP + +#include "pke/asset-manager.hpp" + +#include <cstdint> + +void pke_at_bpm_reset(uint8_t bpm); + +AssetHandle pke_at_audio_get_or_generate_sawtooth(double pitch_freq, double duration); + +#endif /* PKE_AT_PKE_AT_COMMON_HPP */ diff --git a/src/pke-at-settings.cpp b/src/pke-at-settings.cpp index 3f5c6c0..0f25589 100644 --- a/src/pke-at-settings.cpp +++ b/src/pke-at-settings.cpp @@ -1,4 +1,4 @@ #include "pke-at-settings.hpp" -struct pke_at_global_settings pke_at_settings{}; +struct pke_at_global_settings g_at{}; diff --git a/src/pke-at-settings.hpp b/src/pke-at-settings.hpp index 1eca096..7d89e51 100644 --- a/src/pke-at-settings.hpp +++ b/src/pke-at-settings.hpp @@ -1,6 +1,7 @@ #ifndef PKE_AT_PKE_AT_SETTINGS_HPP #define PKE_AT_PKE_AT_SETTINGS_HPP +#include "pke/asset-manager.hpp" #include "pke/pk.h" struct pke_at_global_settings { @@ -8,8 +9,25 @@ struct pke_at_global_settings { pk_membucket *bkt; pk_membucket *bkt_transient; } mem; + struct pke_at_global_settings_bpm { + float last; + float target; + float current; + double lerp_delta; + double lerp_delta_duration; + double delta_per_beat; + double delta_since_last_beat; + } bpm; + struct pke_at_global_settings_metronome { + uint8_t beat; + uint8_t beats_per_bar; + AssetHandle beep; + AssetHandle beep_accent; + } mtrnm; + struct pke_at_global_settings_rt { + } rt; }; -extern struct pke_at_global_settings pke_at_settings; +extern struct pke_at_global_settings g_at; #endif /* PKE_AT_PKE_AT_SETTINGS_HPP */ diff --git a/src/pke-at.cpp b/src/pke-at.cpp index f256a10..f4c7462 100644 --- a/src/pke-at.cpp +++ b/src/pke-at.cpp @@ -2,7 +2,6 @@ #include <pke/pke.hpp> #include "pke-at.hpp" -#include "pke/level.hpp" #include "level-main.hpp" struct pke_at_master { @@ -19,7 +18,4 @@ void pke_at_init() { } void pke_at_teardown() { - if (pkeSettings.rt.activeLevel != nullptr) { - pke_level_teardown(pkeSettings.rt.activeLevel); - } } |
