#define SOUND_SAMPLE_RATE 48000 struct sound_task_params { struct sound_task_params *next_free; u32 flags; AC_Asset *asset; u64 path_len; char path_cstr[1024]; }; struct sound_task_params_store { struct sound_task_params *head_free; Arena *arena; P_Mutex mutex; }; /* ========================== * * Global state * ========================== */ GLOBAL struct { struct sound_task_params_store params; } G = ZI, DEBUG_ALIAS(G, G_sound); /* ========================== * * Startup * ========================== */ SND_StartupReceipt sound_startup(AC_StartupReceipt *asset_cache_sr) { __prof; (UNUSED)asset_cache_sr; G.params.arena = AllocArena(GIBI(64)); return (SND_StartupReceipt) { 0 }; } /* ========================== * * Load task param store * ========================== */ INTERNAL struct sound_task_params *sound_task_params_alloc(void) { struct sound_task_params *p = 0; { P_Lock lock = P_LockE(&G.params.mutex); if (G.params.head_free) { p = G.params.head_free; G.params.head_free = p->next_free; } else { p = PushStruct(G.params.arena, struct sound_task_params); } P_Unlock(&lock); } return p; } INTERNAL void sound_task_params_release(struct sound_task_params *p) { P_Lock lock = P_LockE(&G.params.mutex); p->next_free = G.params.head_free; G.params.head_free = p; P_Unlock(&lock); } /* ========================== * * Load * ========================== */ INTERNAL P_JobDef(sound_load_asset_job, job) { __prof; struct sound_task_params *params = job.sig; TempArena scratch = BeginScratchNoConflict(); String path = STRING(params->path_len, (u8 *)params->path_cstr); AC_Asset *asset = params->asset; u32 flags = params->flags; P_LogInfoF("Loading sound \"%F\"", FMT_STR(path)); i64 start_ns = P_TimeNs(); String error_msg = LIT("Unknown error"); ASSERT(string_ends_with(path, LIT(".mp3"))); /* Decode */ MP3_Result decoded = ZI; { R_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), SOUND_SAMPLE_RATE, 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 */ SND_Sound *sound = 0; i16 *samples = 0; { AC_Store store = asset_cache_store_open(); sound = PushStructNoZero(store.arena, SND_Sound); samples = PushArrayNoZero(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)); P_LogSuccessF("Loaded sound \"%F\" in %F seconds", FMT_STR(path), FMT_FLOAT(SECONDS_FROM_NS(P_TimeNs() - start_ns))); asset_cache_mark_ready(asset, sound); } else { P_LogErrorF("Error loading sound \"%F\": %F", FMT_STR(path), FMT_STR(error_msg)); /* Store */ SND_Sound *sound = 0; { AC_Store store = asset_cache_store_open(); sound = PushStructNoZero(store.arena, SND_Sound); asset_cache_store_close(&store); } *sound = (SND_Sound) { 0 }; asset_cache_mark_ready(asset, sound); } sound_task_params_release(params); EndScratch(scratch); } AC_Asset *sound_load_asset(String path, u32 flags, b32 wait) { __prof; TempArena scratch = BeginScratchNoConflict(); /* Generate and append sound flags to path key */ 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; AC_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)) { P_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; /* PushStruct task */ asset_cache_mark_loading(asset); P_Run(1, sound_load_asset_job, params, P_Pool_Background, P_Priority_Low, &asset->counter); if (wait) { asset_cache_wait(asset); } } EndScratch(scratch); return asset; } SND_Sound *sound_load_async(String path, u32 flags) { __prof; AC_Asset *asset = sound_load_asset(path, flags, 0); SND_Sound *sound = (SND_Sound *)asset_cache_get_store_data(asset); return sound; } SND_Sound *sound_load(String path, u32 flags) { __prof; AC_Asset *asset = sound_load_asset(path, flags, 1); asset_cache_wait(asset); SND_Sound *sound = (SND_Sound *)asset_cache_get_store_data(asset); return sound; }