#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, DebugAlias(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\"", FmtString(path)); i64 start_ns = P_TimeNs(); String error_msg = Lit("Unknown error"); Assert(StringEndsWith(path, Lit(".mp3"))); /* Decode */ MP3_Result decoded = ZI; { R_Resource sound_rs = R_OpenResource(path); if (R_ResourceExists(&sound_rs)) { u64 decode_flags = 0; if (flags & SOUND_FLAG_STEREO) { decode_flags |= MP3_DecodeFlag_Stereo; } decoded = MP3_Decode(scratch.arena, R_GetResourceData(&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"); } R_CloseResource(&sound_rs); } if (decoded.success) { /* Store */ SND_Sound *sound = 0; u64 samples_count = decoded.samples_count; i16 *samples = 0; { AC_Store store = AC_OpenStore(); sound = PushStructNoZero(store.arena, SND_Sound); samples = PushStructsNoZero(store.arena, i16, samples_count); AC_CloseStore(&store); } /* Initialize */ ZeroStruct(sound); sound->flags = flags; sound->samples_count = samples_count; sound->samples = samples; CopyBytes(sound->samples, decoded.samples, decoded.samples_count * sizeof(*decoded.samples)); P_LogSuccessF("Loaded sound \"%F\" in %F seconds", FmtString(path), FmtFloat(SecondsFromNs(P_TimeNs() - start_ns))); AC_MarkReady(asset, sound); } else { P_LogErrorF("Error loading sound \"%F\": %F", FmtString(path), FmtString(error_msg)); /* Store */ SND_Sound *sound = 0; { AC_Store store = AC_OpenStore(); sound = PushStructNoZero(store.arena, SND_Sound); AC_CloseStore(&store); } *sound = (SND_Sound) { 0 }; AC_MarkReady(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 = StringFormat(scratch.arena, Lit("%F%F_sound"), FmtString(path), FmtUint((u64)flags)); u64 hash = AC_HashFromKey(key); b32 is_first_touch; AC_Asset *asset = AC_TouchCache(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(StringFormat(scratch.arena, Lit("Sound path \"%F\" too long!"), FmtString(path))); } CstrBuffFromStringToBuff(StringFromArray(params->path_cstr), path); params->path_len = path.len; params->asset = asset; params->flags = flags; /* PushStruct task */ AC_MarkLoading(asset); P_Run(1, sound_load_asset_job, params, P_Pool_Background, P_Priority_Low, &asset->counter); if (wait) { AC_WaitOnAssetReady(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 *)AC_DataFromStore(asset); return sound; } SND_Sound *sound_load(String path, u32 flags) { __prof; AC_Asset *asset = sound_load_asset(path, flags, 1); AC_WaitOnAssetReady(asset); SND_Sound *sound = (SND_Sound *)AC_DataFromStore(asset); return sound; }