commit 3356dd454a8be73f353319b038e4e5f9f1df99ec
parent 48e5f70fd4904cb9b6c9b7b4251e304aa870cdc2
Author: MikoĊaj Lenczewski <mblenczewski@gmail.com>
Date: Thu, 9 May 2024 00:50:33 +0000
Implement prototype alsa support
Diffstat:
6 files changed, 443 insertions(+), 57 deletions(-)
diff --git a/build_linux.sh b/build_linux.sh
@@ -16,4 +16,4 @@ $CC -shared -o bin/starfield.so starfield/starfield.c \
# TODO: how to build a static binary that supports loading starfield.so?
$CC -o bin/linux_starfield platform/linux_starfield.c \
- $WARNINGS $CFLAGS $CPPFLAGS
+ $WARNINGS $CFLAGS $CPPFLAGS -lc -ldl
diff --git a/common/starfield_api.h b/common/starfield_api.h
@@ -39,6 +39,10 @@ struct str_t {
#define GiB (1024 * MiB)
#define TiB (1024 * GiB)
+#define MSECS (1000ULL)
+#define USECS (MSECS * 1000)
+#define NSECS (USECS * 1000)
+
#define ARRLEN(arr) (sizeof (arr) / sizeof (arr)[0])
#define MIN(a, b) ((a) < (b) ? (a) : (b))
@@ -105,6 +109,29 @@ struct starfield_arena {
u64 cap, len;
};
+inline void
+arena_reset(struct starfield_arena *arena)
+{
+ arena->len = 0;
+}
+
+inline void *
+arena_alloc(struct starfield_arena *arena, u64 size, u64 alignment)
+{
+ u64 aligned_len = ALIGN_NEXT(arena->len, alignment);
+
+ if (arena->cap < aligned_len + size)
+ return NULL;
+
+ void *ptr = (u8 *) arena->ptr + aligned_len;
+ arena->len = aligned_len + size;
+
+ return ptr;
+}
+
+#define PUSH_SIZED(arena, T) arena_alloc(arena, sizeof(T), alignof(T))
+#define PUSH_ARRAY(arena, T, n) arena_alloc(arena, sizeof(T) * (n), alignof(T))
+
struct starfield_memory {
struct starfield_arena permanent, temporary;
};
@@ -161,17 +188,30 @@ struct starfield_input {
struct starfield_keyboard_input keyboard;
};
+#define STARFIELD_VIDEO_HRES 1024 // 1920
+#define STARFIELD_VIDEO_VRES 576 // 1080
+
+#define STARFIELD_VIDEO_BYTES_PER_PIXEL 4
+
struct starfield_video_buffer {
void *buffer;
u32 width, height;
u32 pitch;
};
+#define STARFIELD_AUDIO_CHANNELS 2
+#define STARFIELD_AUDIO_SAMPLE_RATE 48000
+#define STARFIELD_AUDIO_SAMPLE_BITS 32
+
+#define STARFIELD_AUDIO_FRAME_BYTES \
+ (STARFIELD_AUDIO_CHANNELS * (STARFIELD_AUDIO_SAMPLE_BITS / 8))
+
struct starfield_audio_buffer {
void *buffer;
- u32 sample_rate;
u32 channels;
- u32 expected_samples;
+ u32 sample_rate;
+ u32 sample_bits;
+ u32 expected_frames;
};
#define STARFIELD_INIT(name) \
diff --git a/debug_linux.sh b/debug_linux.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+ROOT="$(dirname $0)"
+
+set -ex
+
+lldb -- $ROOT/bin/linux_starfield $@
diff --git a/platform/linux_starfield.c b/platform/linux_starfield.c
@@ -1,6 +1,6 @@
#include "linux_starfield.h"
-internal struct linux_starfield_state state;
+internal struct linux_state state;
int
main(int argc, char **argv, char **envp)
@@ -9,18 +9,6 @@ main(int argc, char **argv, char **envp)
(void) argc;
(void) envp;
- struct platform_api platform_api = load_platform_api();
-
- struct starfield_thread thread = {
- .id = 0,
- };
-
- struct starfield_memory memory;
- if (!init_starfield_memory(256 * MiB, 256 * MiB, &memory)) {
- fprintf(stderr, "Failed to allocate starfield memory\n");
- _exit(1);
- }
-
/* NOTE: we expect the library to be alongside our platform-specific
* executable. the unix loader does not search this path by default,
* so we build the absolute path to the library here as a workaround
@@ -29,49 +17,159 @@ main(int argc, char **argv, char **envp)
strcpy(starfield_library_path, argv[0]);
strcpy(strrchr(starfield_library_path, '/'), "/starfield.so");
- struct starfield_api starfield_api;
- if (!load_starfield_api(starfield_library_path, &starfield_api)) {
+ if (!load_starfield_api(starfield_library_path, &state.starfield_api)) {
fprintf(stderr, "Failed to load starfield library: %s\n", starfield_library_path);
_exit(1);
}
- starfield_api.init(&thread, &memory, &platform_api);
+ state.platform_api = load_platform_api();
+
+ struct starfield_thread thread = {
+ .id = 0,
+ };
+
+ if (!init_memory(&state.platform_memory, 64 * MiB, 64 * MiB)) {
+ fprintf(stderr, "Failed to initialise platform memory\n");
+ _exit(1);
+ }
+
+ if (!init_memory(&state.starfield_memory, 64 * MiB, 64 * MiB)) {
+ fprintf(stderr, "Failed to initialise starfield memory\n");
+ _exit(1);
+ }
+
+ f32 target_render_rate = 60.0, target_physics_rate = 60.0;
+
+ u32 target_us_per_frame = USECS / target_render_rate;
+ u32 target_us_per_tick = USECS / target_physics_rate;
+
+ (void) target_us_per_tick;
+
+ u32 period_us = target_us_per_frame, buffered_periods = 2;
+ if (!init_audio(&state.audio, period_us, buffered_periods)) {
+ fprintf(stderr, "Failed to initialise platform audio\n");
+ }
+
+ if (!init_video(&state.video)) {
+ fprintf(stderr, "Failed to initialise platform video\n");
+ _exit(1);
+ }
+
+ state.starfield_api.init(&thread, &state.starfield_memory, &state.platform_api);
state.running = 1;
+
+ f32 dt = 1.0f / target_render_rate;
while (state.running) {
- f32 dt = 0;
+ struct timespec start_nanos, end_nanos;
+ clock_gettime(CLOCK_MONOTONIC, &start_nanos);
struct starfield_input input = {
0,
};
- starfield_api.update(&thread, &memory, &platform_api, &input, dt);
+ state.starfield_api.update(&thread, &state.starfield_memory,
+ &state.platform_api, &input, dt);
struct starfield_video_buffer video = {
0,
};
- starfield_api.render(&thread, &memory, &platform_api, &video, dt);
+ state.starfield_api.render(&thread, &state.starfield_memory,
+ &state.platform_api, &video, dt);
- struct starfield_audio_buffer audio = {
- 0,
- };
+ if (state.audio.enabled) {
+ state.starfield_api.sample(&thread, &state.starfield_memory,
+ &state.platform_api, &state.audio.buffer, dt);
+
+#ifdef STARFIELD_DEBUG
+ /* TODO: temporary test tone */
+ s32 tone_hz = 256;
+ s32 tone_volume = INT32_MAX / 16;
+ func_local f32 tone_t = 0.0f;
+
+ const f32 PI = 3.1415926535f;
+ const u32 wave_period = state.audio.buffer.sample_rate / tone_hz;
+
+ s32 *sample = state.audio.buffer.buffer;
+ for (u32 i = 0; i < state.audio.buffer.expected_frames; i++) {
+ f32 v = sinf(tone_t);
+
+ int32_t sample_value = (int32_t) (v * tone_volume);
+ for (u32 j = 0; j < state.audio.buffer.channels; j++)
+ *sample++ = sample_value;
+
+ tone_t += 2 * PI / wave_period;
+ if (tone_t >= 2 * PI)
+ tone_t -= 2 * PI;
+ }
+#endif
+
+ play_audio(&state.audio);
+ }
+
+ clock_gettime(CLOCK_MONOTONIC, &end_nanos);
- starfield_api.sample(&thread, &memory, &platform_api, &audio, dt);
+ u64 elapsed_us = linux_elapsed_ns(&start_nanos, &end_nanos) / 1000;
+ if (elapsed_us < target_us_per_frame) {
+ struct timespec req = {
+ .tv_sec = 0,
+ .tv_nsec = (target_us_per_frame - elapsed_us) * 1000,
+ };
- sleep(1);
+ while (nanosleep(&req, &req) < 0);
+
+ clock_gettime(CLOCK_MONOTONIC, &end_nanos);
+ elapsed_us = linux_elapsed_ns(&start_nanos, &end_nanos) / 1000;
+ } else {
+ /* TODO: missed frame */
+ }
+
+ dt = (f32) elapsed_us / USECS;
}
- starfield_api.free(&thread, &memory, &platform_api);
+ state.starfield_api.free(&thread, &state.starfield_memory, &state.platform_api);
_exit(0);
}
-b32
-init_starfield_memory(u64 permanent_memory_cap, u64 temporary_memory_cap,
- struct starfield_memory *memory)
+internal u64
+linux_elapsed_ns(struct timespec *restrict start, struct timespec *restrict end)
+{
+ return ((end->tv_sec - start->tv_sec) * NSECS + (end->tv_nsec - start->tv_nsec));
+}
+
+#define LOAD_SYMBOL(loc, T, sym) \
+ ((loc) = (T *) dlsym(library, sym))
+
+internal b32
+load_starfield_api(char const *filename, struct starfield_api *api)
+{
+ void *library = dlopen(filename, RTLD_NOW);
+ if (!library) return false;
+
+ if (!LOAD_SYMBOL(api->init, starfield_api_init_t, "starfield_init"))
+ return false;
+
+ if (!LOAD_SYMBOL(api->update, starfield_api_update_t, "starfield_update"))
+ return false;
+
+ if (!LOAD_SYMBOL(api->render, starfield_api_render_t, "starfield_render"))
+ return false;
+
+ if (!LOAD_SYMBOL(api->sample, starfield_api_sample_t, "starfield_sample"))
+ return false;
+
+ if (!LOAD_SYMBOL(api->free, starfield_api_free_t, "starfield_free"))
+ return false;
+
+ return true;
+}
+
+internal b32
+init_memory(struct starfield_memory *memory, u64 permanent_memory_cap, u64 temporary_memory_cap)
{
-#if STARFIELD_DEBUG
+#ifdef STARFIELD_DEBUG
void *base_addr = (void *) (2 * TiB);
#else
void *base_addr = NULL;
@@ -79,13 +177,19 @@ init_starfield_memory(u64 permanent_memory_cap, u64 temporary_memory_cap,
u64 len = permanent_memory_cap + temporary_memory_cap;
- /* TODO: add hugetlb support? seems to require a kernel commandline arg */
- void *ptr = mmap(base_addr, len,
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
- -1, 0);
+ int prot = PROT_READ | PROT_WRITE;
+ int flags = MAP_PRIVATE | MAP_ANONYMOUS;
+
+#ifdef STARFIELD_DEBUG
+ flags |= MAP_FIXED;
+#endif
+
+ void *ptr = mmap(base_addr, len, prot, flags | MAP_HUGETLB | MAP_HUGE_2MB, -1, 0);
+ if (ptr == MAP_FAILED)
+ ptr = mmap(base_addr, len, prot, flags, -1, 0);
- if (ptr == MAP_FAILED) return false;
+ if (ptr == MAP_FAILED)
+ return false;
/* TODO: can we make this faster? can we avoid this entirely? */
memset(base_addr, 0, len);
@@ -101,27 +205,130 @@ init_starfield_memory(u64 permanent_memory_cap, u64 temporary_memory_cap,
return true;
}
-b32
-load_starfield_api(char const *filename, struct starfield_api *api)
+internal b32
+init_audio(struct linux_audio *audio, u32 period_us, u32 buffered_periods)
{
- void *library = dlopen(filename, RTLD_LAZY);
+ audio->enabled = false;
+
+ void *library = dlopen("libasound.so", RTLD_NOW);
if (!library) return false;
- if (!(api->init = (starfield_api_init_t *) dlsym(library, "starfield_init")))
+ if (!LOAD_SYMBOL(audio->api.snd_strerror, alsa_snd_strerror_t, "snd_strerror"))
+ return false;
+
+ if (!LOAD_SYMBOL(audio->api.snd_pcm_open, alsa_snd_pcm_open_t, "snd_pcm_open"))
+ return false;
+
+ if (!LOAD_SYMBOL(audio->api.snd_pcm_hw_params_sizeof,
+ alsa_snd_pcm_hw_params_sizeof_t,
+ "snd_pcm_hw_params_sizeof"))
+ return false;
+
+ if (!LOAD_SYMBOL(audio->api.snd_pcm_hw_params_any,
+ alsa_snd_pcm_hw_params_any_t,
+ "snd_pcm_hw_params_any"))
+ return false;
+
+ if (!LOAD_SYMBOL(audio->api.snd_pcm_hw_params_set_access,
+ alsa_snd_pcm_hw_params_set_access_t,
+ "snd_pcm_hw_params_set_access"))
+ return false;
+
+ if (!LOAD_SYMBOL(audio->api.snd_pcm_hw_params_set_format,
+ alsa_snd_pcm_hw_params_set_format_t,
+ "snd_pcm_hw_params_set_format"))
return false;
- if (!(api->update = (starfield_api_update_t *) dlsym(library, "starfield_update")))
+ if (!LOAD_SYMBOL(audio->api.snd_pcm_hw_params_set_channels,
+ alsa_snd_pcm_hw_params_set_channels_t,
+ "snd_pcm_hw_params_set_channels"))
return false;
- if (!(api->render = (starfield_api_render_t *) dlsym(library, "starfield_render")))
+ if (!LOAD_SYMBOL(audio->api.snd_pcm_hw_params_set_rate,
+ alsa_snd_pcm_hw_params_set_rate_t,
+ "snd_pcm_hw_params_set_rate"))
return false;
- if (!(api->sample = (starfield_api_sample_t *) dlsym(library, "starfield_sample")))
+ if (!LOAD_SYMBOL(audio->api.snd_pcm_hw_params_set_periods,
+ alsa_snd_pcm_hw_params_set_periods_t,
+ "snd_pcm_hw_params_set_periods"))
return false;
- if (!(api->free = (starfield_api_free_t *) dlsym(library, "starfield_free")))
+ if (!LOAD_SYMBOL(audio->api.snd_pcm_hw_params_set_period_time,
+ alsa_snd_pcm_hw_params_set_period_time_t,
+ "snd_pcm_hw_params_set_period_time"))
return false;
+ if (!LOAD_SYMBOL(audio->api.snd_pcm_hw_params,
+ alsa_snd_pcm_hw_params_t,
+ "snd_pcm_hw_params"))
+ return false;
+
+ if (!LOAD_SYMBOL(audio->api.snd_pcm_writei,
+ alsa_snd_pcm_writei_t,
+ "snd_pcm_writei"))
+ return false;
+
+ /* initialise audio */
+ int err;
+ if ((err = audio->api.snd_pcm_open(&audio->handle, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
+ fprintf(stderr, "snd_pcm_open: %s\n", audio->api.snd_strerror(err));
+ return false;
+ }
+
+ snd_pcm_hw_params_t *hw_params = alloca(audio->api.snd_pcm_hw_params_sizeof());
+ assert(hw_params);
+
+ audio->api.snd_pcm_hw_params_any(audio->handle, hw_params);
+ audio->api.snd_pcm_hw_params_set_access(audio->handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
+ audio->api.snd_pcm_hw_params_set_format(audio->handle, hw_params, SND_PCM_FORMAT_S32_LE);
+ audio->api.snd_pcm_hw_params_set_channels(audio->handle, hw_params, STARFIELD_AUDIO_CHANNELS);
+ audio->api.snd_pcm_hw_params_set_rate(audio->handle, hw_params, STARFIELD_AUDIO_SAMPLE_RATE, 0);
+ audio->api.snd_pcm_hw_params_set_periods(audio->handle, hw_params, buffered_periods, 0);
+ audio->api.snd_pcm_hw_params_set_period_time(audio->handle, hw_params, period_us, 0);
+
+ if ((err = audio->api.snd_pcm_hw_params(audio->handle, hw_params)) < 0) {
+ fprintf(stderr, "and_pcm_hw_params: %s\n", audio->api.snd_strerror(err));
+ return false;
+ }
+
+ u64 frames = (period_us * STARFIELD_AUDIO_SAMPLE_RATE * STARFIELD_AUDIO_FRAME_BYTES) / USECS;
+
+#ifdef STARFIELD_DEBUG
+ fprintf(stderr, "Audio buffer period (us): %" PRIu32 "\n", period_us);
+ fprintf(stderr, "Audio buffer frames: %" PRIu64 "\n", frames);
+#endif
+
+ audio->buffer.buffer = PUSH_ARRAY(&state.platform_memory.permanent, s32, frames);
+ assert(audio->buffer.buffer);
+
+ audio->buffer.channels = STARFIELD_AUDIO_CHANNELS;
+ audio->buffer.sample_rate = STARFIELD_AUDIO_SAMPLE_RATE;
+ audio->buffer.sample_bits = STARFIELD_AUDIO_SAMPLE_BITS;
+ audio->buffer.expected_frames = frames;
+
+ audio->enabled = true;
+
+ return true;
+}
+
+internal void
+play_audio(struct linux_audio *audio)
+{
+ void *buf = audio->buffer.buffer;
+ u32 frames = audio->buffer.expected_frames;
+
+ int err;
+ if ((err = audio->api.snd_pcm_writei(audio->handle, buf, frames)) < 0) {
+ fprintf(stderr, "snd_pcm_write: %s\n", audio->api.snd_strerror(err));
+ }
+}
+
+internal b32
+init_video(struct linux_video *video)
+{
+ (void) video;
+
return true;
}
@@ -154,7 +361,7 @@ DEBUG_PLATFORM_FREE_FILE_MEMORY(DEBUG_platform_free_file_memory)
#endif
-struct platform_api
+internal struct platform_api
load_platform_api(void)
{
return (struct platform_api) {
@@ -164,4 +371,9 @@ load_platform_api(void)
.DEBUG_free_file_memory = DEBUG_platform_free_file_memory,
#endif
};
-}
+
+/* utils
+ * ===========================================================================
+ */
+
+#include "starfield_api_inlines.c"
diff --git a/platform/linux_starfield.h b/platform/linux_starfield.h
@@ -2,8 +2,8 @@
#define LINUX_STARFIELD_H
#define _XOPEN_SOURCE 700
-
-#include "starfield_api.h"
+#define _GNU_SOURCE
+#define _DEFAULT_SOURCE
#include <stdio.h>
#include <string.h>
@@ -13,22 +13,128 @@
#include <dlfcn.h>
+#include <alsa/asoundlib.h>
+
+#include <wayland-client.h>
+
+#include "starfield_api.h"
+
struct starfield_thread {
u32 id;
};
-struct linux_starfield_state {
+/* alsa state */
+#define ALSA_SND_STRERROR(name) \
+ char const *name(int)
+
+#define ALSA_SND_PCM_OPEN(name) \
+ int name(snd_pcm_t **, char const *, snd_pcm_stream_t, int)
+
+#define ALSA_SND_PCM_HW_PARAMS_SIZEOF(name) \
+ size_t name(void)
+
+#define ALSA_SND_PCM_HW_PARAMS_ANY(name) \
+ int name(snd_pcm_t *, snd_pcm_hw_params_t *)
+
+#define ALSA_SND_PCM_HW_PARAMS_SET_ACCESS(name) \
+ int name(snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_access_t)
+
+#define ALSA_SND_PCM_HW_PARAMS_SET_FORMAT(name) \
+ int name(snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_format_t)
+
+#define ALSA_SND_PCM_HW_PARAMS_SET_CHANNELS(name) \
+ int name(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int)
+
+#define ALSA_SND_PCM_HW_PARAMS_SET_RATE(name) \
+ int name(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int, int)
+
+#define ALSA_SND_PCM_HW_PARAMS_SET_PERIODS(name) \
+ int name(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int, int)
+
+#define ALSA_SND_PCM_HW_PARAMS_SET_PERIOD_TIME(name) \
+ int name(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int, int)
+
+#define ALSA_SND_PCM_HW_PARAMS(name) \
+ int name(snd_pcm_t *, snd_pcm_hw_params_t *)
+
+#define ALSA_SND_PCM_WRITEI(name) \
+ int name(snd_pcm_t *, void const *, snd_pcm_uframes_t)
+
+typedef ALSA_SND_STRERROR(alsa_snd_strerror_t);
+typedef ALSA_SND_PCM_OPEN(alsa_snd_pcm_open_t);
+typedef ALSA_SND_PCM_HW_PARAMS_SIZEOF(alsa_snd_pcm_hw_params_sizeof_t);
+typedef ALSA_SND_PCM_HW_PARAMS_ANY(alsa_snd_pcm_hw_params_any_t);
+typedef ALSA_SND_PCM_HW_PARAMS_SET_ACCESS(alsa_snd_pcm_hw_params_set_access_t);
+typedef ALSA_SND_PCM_HW_PARAMS_SET_FORMAT(alsa_snd_pcm_hw_params_set_format_t);
+typedef ALSA_SND_PCM_HW_PARAMS_SET_CHANNELS(alsa_snd_pcm_hw_params_set_channels_t);
+typedef ALSA_SND_PCM_HW_PARAMS_SET_RATE(alsa_snd_pcm_hw_params_set_rate_t);
+typedef ALSA_SND_PCM_HW_PARAMS_SET_PERIODS(alsa_snd_pcm_hw_params_set_periods_t);
+typedef ALSA_SND_PCM_HW_PARAMS_SET_PERIOD_TIME(alsa_snd_pcm_hw_params_set_period_time_t);
+typedef ALSA_SND_PCM_HW_PARAMS(alsa_snd_pcm_hw_params_t);
+typedef ALSA_SND_PCM_WRITEI(alsa_snd_pcm_writei_t);
+
+struct linux_audio {
+ /* api reference:
+ * https://www.alsa-project.org/alsa-doc/alsa-lib/modules.html
+ */
+ struct {
+ alsa_snd_strerror_t *snd_strerror;
+ alsa_snd_pcm_open_t *snd_pcm_open;
+ alsa_snd_pcm_hw_params_sizeof_t *snd_pcm_hw_params_sizeof;
+ alsa_snd_pcm_hw_params_any_t *snd_pcm_hw_params_any;
+ alsa_snd_pcm_hw_params_set_access_t *snd_pcm_hw_params_set_access;
+ alsa_snd_pcm_hw_params_set_format_t *snd_pcm_hw_params_set_format;
+ alsa_snd_pcm_hw_params_set_channels_t *snd_pcm_hw_params_set_channels;
+ alsa_snd_pcm_hw_params_set_rate_t *snd_pcm_hw_params_set_rate;
+ alsa_snd_pcm_hw_params_set_periods_t *snd_pcm_hw_params_set_periods;
+ alsa_snd_pcm_hw_params_set_period_time_t *snd_pcm_hw_params_set_period_time;
+ alsa_snd_pcm_hw_params_t *snd_pcm_hw_params;
+ alsa_snd_pcm_writei_t *snd_pcm_writei;
+ } api;
+
+ snd_pcm_t *handle;
+
+ struct starfield_audio_buffer buffer;
+
+ b32 enabled;
+};
+
+/* wayland state */
+struct linux_video {
+ u8 pad;
+};
+
+struct linux_state {
+ struct starfield_memory platform_memory, starfield_memory;
+
+ struct linux_audio audio;
+ struct linux_video video;
+
+ struct platform_api platform_api;
+ struct starfield_api starfield_api;
+
b32 running;
};
-struct platform_api
+internal u64
+linux_elapsed_ns(struct timespec *restrict start, struct timespec *restrict end);
+
+internal b32
+load_starfield_api(char const *filename, struct starfield_api *api);
+
+internal struct platform_api
load_platform_api(void);
-b32
-init_starfield_memory(u64 permanent_memory_cap, u64 temporary_memory_cap,
- struct starfield_memory *memory);
+internal b32
+init_memory(struct starfield_memory *memory, u64 permanent_memory_cap, u64 temporary_memory_cap);
-b32
-load_starfield_api(char const *filename, struct starfield_api *api);
+internal b32
+init_audio(struct linux_audio *audio, u32 period_us, u32 buffered_periods);
+
+internal void
+play_audio(struct linux_audio *audio);
+
+internal b32
+init_video(struct linux_video *video);
#endif /* LINUX_STARFIELD_H */
diff --git a/starfield/starfield.c b/starfield/starfield.c
@@ -36,6 +36,27 @@ STARFIELD_SAMPLE(starfield_sample)
(void) platform;
(void) audio;
(void) dt;
+
+ /* TODO: temporary test tone */
+ s32 tone_hz = 256;
+ s32 tone_volume = INT32_MAX / 16;
+ func_local f32 tone_t = 0.0f;
+
+ const f32 PI = 3.1415926535f;
+ const u32 wave_period = audio->sample_rate / tone_hz;
+
+ s32 *sample = audio->buffer;
+ for (u32 i = 0; i < audio->expected_frames; i++) {
+ f32 v = 0.0; // sinf(tone_t);
+
+ int32_t sample_value = (int32_t) (v * tone_volume);
+ for (u32 j = 0; j < audio->channels; j++)
+ *sample++ = sample_value;
+
+ tone_t += 2 * PI / wave_period;
+ if (tone_t >= 2 * PI)
+ tone_t -= 2 * PI;
+ }
}
global