#if OS_WINDOWS #include "impl/windows/core.c" #elif OS_LINUX #include "impl/linux/core.c" #endif Str8 FS_ReadEntireFile(M_Arena *arena, Str8 path) { Str8 result = { 0 }; OS_Handle file = FS_FileOpen(path, FS_ACCESS_READ); if (file.v[0]) { result.count = FS_FileSize(file); result.data = M_ArenaPush(arena, U8, .count = result.count); FS_FileRead(file, result.data, result.count, 0); FS_FileClose(file); } return result; } internal void SDL_SubmitAudio(void *user, SDL_AudioStream *stream, int needed, int total) { Audio_Context *audio = cast(Audio_Context *) user; (void) total; M_TempScope(0, 0) { U32 prev = 0; U32 it = audio->head; U32 n_samples = needed >> 1; U32 n_frames = n_samples >> 1; F32 *left_f32 = M_ArenaPush(temp.arena, F32, .count = n_frames); F32 *right_f32 = M_ArenaPush(temp.arena, F32, .count = n_frames); while (it != 0) { Audio_Track *track = &audio->tracks[it]; U32 next = track->next; F32 *l0 = left_f32; F32 *r0 = right_f32; Assert(track->playing); U32 remain = track->data->n_frames - track->n_played; U32 play = Min(n_frames, remain); U32 off = track->n_played << 1; // played n frames, thus double to n samples for (U32 f = 0; f < play; ++f) { *l0++ += cast(F32) (track->data->samples[off + (2 * f) + 0]) * track->volume * audio->volume; *r0++ += cast(F32) (track->data->samples[off + (2 * f) + 1]) * track->volume * audio->volume; } track->n_played += play; if (track->n_played == track->data->n_frames) { if (prev == 0) { // Head has finished playing // audio->head = track->next; track->next = audio->free; audio->free = it; } else { // We're in the middle somewhere so prev->next == it->next // audio->tracks[prev].next = track->next; track->next = audio->free; audio->free = it; } track->playing = false; } it = next; } F32 *l0 = left_f32; F32 *r0 = right_f32; S16 *interleaved = M_ArenaPush(temp.arena, S16, .count = n_samples); S16 *i0 = interleaved; for (U32 n = 0; n < n_frames; ++n) { *i0++ = cast(S16) (l0[n] + 0.5f); *i0++ = cast(S16) (r0[n] + 0.5f); } SDL_PutAudioStreamData(stream, interleaved, needed); } } void Audio_Init(M_Arena *arena, Audio_Context *audio, F32 volume) { SDL_AudioSpec spec = { 0 }; spec.format = SDL_AUDIO_S16LE; spec.channels = 2; spec.freq = 44100; audio->stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec, SDL_SubmitAudio, audio); if (audio->stream) { M_TempScope(0, 0) { Str8 exec = FS_SystemPath(temp.arena, FS_SYSTEM_PATH_EXE); Str8 path = Sf(temp.arena, "%.*s/assets", Sv(exec)); FS_List files = FS_PathList(temp.arena, path); U32 n_audio = 0; for (FS_Entry *it = files.first; it != 0; it = it->next) { if (Str8_EndsWith(it->basename, S("wav"))) { n_audio += 1; } } audio->n_sounds = 0; audio->sounds = M_ArenaPush(arena, Audio_Data, .count = n_audio); for (FS_Entry *it = files.first; it != 0; it = it->next) { if (Str8_EndsWith(it->basename, S("wav"))) { Audio_Data *sound = &audio->sounds[audio->n_sounds]; U8 *data; U32 count; SDL_AudioSpec wav; SDL_LoadWAV((const char *) it->path.data, &wav, &data, &count); Assert(wav.freq == 44100); sound->samples = (S16 *) data; sound->n_frames = (count >> 2); audio->n_sounds += 1; } } audio->n_tracks = 16; for (U32 it = 1; it < audio->n_tracks; ++it) { audio->tracks[it].next = it + 1; } audio->tracks[audio->n_tracks - 1].next = 0; audio->volume = volume; audio->head = 0; audio->free = 1; printf("--- Loaded %d sounds ---\n", audio->n_sounds); } } } U32 Audio_Play(Audio_Context *audio, U32 index) { U32 result = 0; if (audio->free != 0 && SDL_LockAudioStream(audio->stream)) { result = audio->free; Audio_Track *track = &audio->tracks[result]; audio->free = track->next; track->playing = true; track->data = &audio->sounds[index]; track->n_played = 0; track->volume = 1.0f; track->next = audio->head; audio->head = result; SDL_UnlockAudioStream(audio->stream); } return result; } void Audio_ChangeVolume(Audio_Context *audio, U32 handle, F32 volume) { if (SDL_LockAudioStream(audio->stream)) { audio->tracks[handle].volume = volume; SDL_UnlockAudioStream(audio->stream); } }