power_play/src/sound.c

209 lines
6.1 KiB
C

#include "sound.h"
#include "arena.h"
#include "log.h"
#include "sys.h"
#include "resource.h"
#include "asset_cache.h"
#include "mp3.h"
struct sound_task_params {
struct sound_task_params *next_free;
u32 flags;
struct asset *asset;
u64 path_len;
char path_cstr[1024];
};
struct sound_task_params_store {
struct sound_task_params *head_free;
struct arena *arena;
struct snc_mutex mutex;
};
/* ========================== *
* Global state
* ========================== */
GLOBAL struct {
struct sound_task_params_store params;
} G = ZI, DEBUG_ALIAS(G, G_sound);
/* ========================== *
* Startup
* ========================== */
struct sound_startup_receipt sound_startup(struct asset_cache_startup_receipt *asset_cache_sr,
struct resource_startup_receipt *resource_sr)
{
(UNUSED)asset_cache_sr;
(UNUSED)resource_sr;
G.params.arena = arena_alloc(GIBI(64));
return (struct sound_startup_receipt) { 0 };
}
/* ========================== *
* Load task param store
* ========================== */
INTERNAL struct sound_task_params *sound_task_params_alloc(void)
{
struct sound_task_params *p = 0;
{
struct snc_lock lock = snc_lock_e(&G.params.mutex);
if (G.params.head_free) {
p = G.params.head_free;
G.params.head_free = p->next_free;
} else {
p = arena_push(G.params.arena, struct sound_task_params);
}
snc_unlock(&lock);
}
return p;
}
INTERNAL void sound_task_params_release(struct sound_task_params *p)
{
struct snc_lock lock = snc_lock_e(&G.params.mutex);
p->next_free = G.params.head_free;
G.params.head_free = p;
snc_unlock(&lock);
}
/* ========================== *
* Load
* ========================== */
INTERNAL SYS_JOB_DEF(sound_load_asset_job, job)
{
__prof;
struct sound_task_params *params = job.sig;
struct arena_temp scratch = scratch_begin_no_conflict();
struct string path = STRING(params->path_len, (u8 *)params->path_cstr);
struct asset *asset = params->asset;
u32 flags = params->flags;
logf_info("Loading sound \"%F\"", FMT_STR(path));
i64 start_ns = sys_time_ns();
struct string error_msg = LIT("Unknown error");
ASSERT(string_ends_with(path, LIT(".mp3")));
/* Decode */
struct mp3_decode_result decoded = ZI;
{
struct resource sound_rs = resource_open(path);
if (resource_exists(&sound_rs)) {
u64 decode_flags = 0;
if (flags & SOUND_FLAG_STEREO) {
decode_flags |= MP3_DECODE_FLAG_STEREO;
}
decoded = mp3_decode(scratch.arena, resource_get_data(&sound_rs), decode_flags);
if (!decoded.success) {
error_msg = LIT("Failed to decode sound file");
}
} else {
error_msg = LIT("Resource not found");
}
resource_close(&sound_rs);
}
if (decoded.success) {
/* Store */
struct sound *sound = 0;
i16 *samples = 0;
{
struct asset_cache_store store = asset_cache_store_open();
sound = arena_push_no_zero(store.arena, struct sound);
samples = arena_push_array_no_zero(store.arena, i16, decoded.pcm.count);
asset_cache_store_close(&store);
}
/* Initialize */
MEMZERO_STRUCT(sound);
sound->flags = flags;
sound->pcm.count = decoded.pcm.count;
sound->pcm.samples = samples;
MEMCPY(sound->pcm.samples, decoded.pcm.samples, decoded.pcm.count * sizeof(*decoded.pcm.samples));
logf_success("Loaded sound \"%F\" in %F seconds", FMT_STR(path), FMT_FLOAT(SECONDS_FROM_NS(sys_time_ns() - start_ns)));
asset_cache_mark_ready(asset, sound);
} else {
logf_error("Error loading sound \"%F\": %F", FMT_STR(path), FMT_STR(error_msg));
/* Store */
struct sound *sound = 0;
{
struct asset_cache_store store = asset_cache_store_open();
sound = arena_push_no_zero(store.arena, struct sound);
asset_cache_store_close(&store);
}
*sound = (struct sound) { 0 };
asset_cache_mark_ready(asset, sound);
}
sound_task_params_release(params);
scratch_end(scratch);
}
struct asset *sound_load_asset(struct string path, u32 flags, b32 wait)
{
__prof;
struct arena_temp scratch = scratch_begin_no_conflict();
/* Generate and append sound flags to path key */
struct string key = string_format(scratch.arena,
LIT("%F%F_sound"),
FMT_STR(path),
FMT_UINT((u64)flags));
u64 hash = asset_cache_hash(key);
b32 is_first_touch;
struct asset *asset = asset_cache_touch(key, hash, &is_first_touch);
if (is_first_touch) {
/* Assemble task params */
struct sound_task_params *params = sound_task_params_alloc();
if (path.len > (sizeof(params->path_cstr) - 1)) {
sys_panic(string_format(scratch.arena,
LIT("Sound path \"%F\" too long!"),
FMT_STR(path)));
}
cstr_buff_from_string(STRING_FROM_ARRAY(params->path_cstr), path);
params->path_len = path.len;
params->asset = asset;
params->flags = flags;
/* Push task */
asset_cache_mark_loading(asset);
sys_run(1, sound_load_asset_job, params, SYS_PRIORITY_BACKGROUND, &asset->counter);
if (wait) {
asset_cache_wait(asset);
}
}
scratch_end(scratch);
return asset;
}
struct sound *sound_load_async(struct string path, u32 flags)
{
__prof;
struct asset *asset = sound_load_asset(path, flags, 0);
struct sound *sound = (struct sound *)asset_cache_get_store_data(asset);
return sound;
}
struct sound *sound_load(struct string path, u32 flags)
{
__prof;
struct asset *asset = sound_load_asset(path, flags, 1);
asset_cache_wait(asset);
struct sound *sound = (struct sound *)asset_cache_get_store_data(asset);
return sound;
}