201 lines
5.6 KiB
C
201 lines
5.6 KiB
C
#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;
|
|
}
|