Hack in new audio system contributed by casey; win32 only for now

master
Allen Webster 2020-11-23 13:46:01 -08:00
parent 45ab2afe61
commit a9a1931e34
13 changed files with 399 additions and 12 deletions

View File

@ -303,6 +303,12 @@ define_api(Arena *arena){
api_param(arena, call, "Key_Mode", "mode");
}
{
API_Call *call = api_call(arena, api, "play_clip", "void");
api_param(arena, call, "Audio_Clip", "clip");
api_param(arena, call, "Audio_Control*", "control");
}
return(api);
}

104
custom/4coder_audio.cpp Normal file
View File

@ -0,0 +1,104 @@
////////////////////////////////
// NOTE(allen): Load Clip
#if !defined(FCODER_SKIP_WAV)
#define FCODER_SKIP_WAV
#pragma pack(push, 1)
struct wave_fmt_data
{
u16 wFormatTag;
u16 wChannels;
u32 dwSamplesPerSec;
u32 dwAvgBytesPerSec;
u16 wBlockAlign;
u16 wBitsPerSample;
};
struct riff_header
{
u32 ID;
u32 DataSize;
};
#pragma pack(pop)
#endif
function Audio_Clip
audio_clip_from_wav_data(String_Const_u8 data){
Audio_Clip Result = {};
if (data.size >= 4 && *(u32 *)data.str == *(u32 *)"RIFF"){
// NOTE(casey): This ROM is in WAV format
riff_header *RootHeader = (riff_header *)data.str;
wave_fmt_data *Format = 0;
u32 SampleDataSize = 0;
i16 *Samples = 0;
u32 At = sizeof(riff_header);
u32 LastAt = At + ((RootHeader->DataSize + 1) & ~1);
if ((*(u32 *)(data.str + At) == *(u32 *)"WAVE") &&
(LastAt <= data.size)){
At += sizeof(u32);
while (At < LastAt){
riff_header *Header = (riff_header *)(data.str + At);
u32 DataAt = At + sizeof(riff_header);
u32 EndAt = DataAt + ((Header->DataSize + 1) & ~1);
if(EndAt <= data.size)
{
void *Data = (data.str + DataAt);
if(Header->ID == *(u32 *)"fmt ")
{
Format = (wave_fmt_data *)Data;
}
else if(Header->ID == *(u32 *)"data")
{
SampleDataSize = Header->DataSize;
Samples = (i16 *)Data;
}
}
At = EndAt;
}
}
if (Format &&
Samples &&
(Format->wFormatTag == 1) &&
(Format->wChannels == 2) &&
(Format->wBitsPerSample == 16) &&
(Format->dwSamplesPerSec == 48000)){
Result.sample_count = SampleDataSize / (Format->wChannels*Format->wBitsPerSample/8);
Result.samples = (i16 *)Samples;
}
else{
// TODO(casey): This is where you would output an error - to 4coder somehow?
}
}
else{
// TODO(casey): This is where you would output an error - to 4coder somehow?
}
return(Result);
}
#include <stdlib.h>
function Audio_Clip
audio_clip_from_wav_file_name(char *file_name){
String_Const_u8 data = {};
FILE *file = fopen(file_name, "rb");
if (file != 0){
fseek(file, 0, SEEK_END);
data.size = ftell(file);
data.str = (u8*)malloc(data.size);
if (data.str != 0 && data.size > 0){
fseek(file, 0, SEEK_SET);
fread(data.str, data.size, 1, file);
}
fclose(file);
}
Audio_Clip result = audio_clip_from_wav_data(data);
return(result);
}

11
custom/4coder_audio.h Normal file
View File

@ -0,0 +1,11 @@
/* date = November 23rd 2020 1:18 pm */
#ifndef FCODER_AUDIO_H
#define FCODER_AUDIO_H
////////////////////////////////
// NOTE(allen): Load Clip
function Audio_Clip audio_clip_from_wav_data(String_Const_u8 data);
#endif //4CODER_AUDIO_H

View File

@ -18,6 +18,15 @@ CUSTOM_DOC("Default command for responding to a startup event")
load_project(app);
}
}
{
Audio_Clip test_clip = audio_clip_from_wav_file_name("W:\\4ed\\audio_test\\raygun_zap.wav");
local_persist Audio_Control test_control = {};
test_control.left_volume_knob = AUDIO_PRODUCER_KNOB_ONE;
test_control.right_volume_knob = AUDIO_PRODUCER_KNOB_ONE;
system_play_clip(test_clip, &test_control);
}
}
CUSTOM_COMMAND_SIG(default_try_exit)

View File

@ -28,6 +28,7 @@
#endif
#include "4coder_variables.h"
#include "4coder_audio.h"
#include "4coder_profile.h"
#include "4coder_async_tasks.h"
#include "4coder_token.h"
@ -129,6 +130,7 @@
#include "4coder_doc_commands.cpp"
#include "4coder_docs.cpp"
#include "4coder_variables.cpp"
#include "4coder_audio.cpp"
#include "4coder_examples.cpp"

View File

@ -787,4 +787,39 @@ struct Process_State{
i64 return_code;
};
////////////////////////////////
api(custom)
struct Audio_Control{
#define AUDIO_PRODUCER_KNOB_ONE 256
volatile u32 left_volume_knob;
volatile u32 right_volume_knob;
volatile u32 generation;
volatile u32 last_played_sample_index;;
};
api(custom)
struct Audio_Clip{
i16 *samples;
Audio_Control *control;
u32 sample_count;
u32 at_sample_index;
};
api(custom)
struct Audio_System{
volatile u32 quit;
volatile u32 ticket;
volatile u32 serving;
volatile u32 generation;
u32 next_playing_clip_index;
Audio_Clip playing_clips[64];
// NOTE(casey): Requests to play sounds are written to a pending array to avoid long locking
volatile u32 pending_clip_count;
Audio_Clip pending_clips[64];
};
#endif

View File

@ -307,8 +307,8 @@ static Command_Metadata fcoder_metacmd_table[248] = {
{ PROC_LINKS(decrease_face_size, 0), false, "decrease_face_size", 18, "Decrease the size of the face used by the current buffer.", 57, "W:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 757 },
{ PROC_LINKS(default_file_externally_modified, 0), false, "default_file_externally_modified", 32, "Notes the external modification of attached files by printing a message.", 72, "W:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 2062 },
{ PROC_LINKS(default_startup, 0), false, "default_startup", 15, "Default command for responding to a startup event", 49, "W:\\4ed\\code\\custom\\4coder_default_hooks.cpp", 43, 7 },
{ PROC_LINKS(default_try_exit, 0), false, "default_try_exit", 16, "Default command for responding to a try-exit event", 50, "W:\\4ed\\code\\custom\\4coder_default_hooks.cpp", 43, 23 },
{ PROC_LINKS(default_view_input_handler, 0), false, "default_view_input_handler", 26, "Input consumption loop for default view behavior", 48, "W:\\4ed\\code\\custom\\4coder_default_hooks.cpp", 43, 67 },
{ PROC_LINKS(default_try_exit, 0), false, "default_try_exit", 16, "Default command for responding to a try-exit event", 50, "W:\\4ed\\code\\custom\\4coder_default_hooks.cpp", 43, 32 },
{ PROC_LINKS(default_view_input_handler, 0), false, "default_view_input_handler", 26, "Input consumption loop for default view behavior", 48, "W:\\4ed\\code\\custom\\4coder_default_hooks.cpp", 43, 76 },
{ PROC_LINKS(delete_alpha_numeric_boundary, 0), false, "delete_alpha_numeric_boundary", 29, "Delete characters between the cursor position and the first alphanumeric boundary to the right.", 95, "W:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 161 },
{ PROC_LINKS(delete_char, 0), false, "delete_char", 11, "Deletes the character to the right of the cursor.", 49, "W:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 79 },
{ PROC_LINKS(delete_current_scope, 0), false, "delete_current_scope", 20, "Deletes the braces surrounding the currently selected scope. Leaves the contents within the scope.", 99, "W:\\4ed\\code\\custom\\4coder_scope_commands.cpp", 44, 112 },

View File

@ -54,6 +54,7 @@ vtable->set_fullscreen = system_set_fullscreen;
vtable->is_fullscreen = system_is_fullscreen;
vtable->get_keyboard_modifiers = system_get_keyboard_modifiers;
vtable->set_key_mode = system_set_key_mode;
vtable->play_clip = system_play_clip;
}
#if defined(DYNAMIC_LINK_API)
function void
@ -112,6 +113,7 @@ system_set_fullscreen = vtable->set_fullscreen;
system_is_fullscreen = vtable->is_fullscreen;
system_get_keyboard_modifiers = vtable->get_keyboard_modifiers;
system_set_key_mode = vtable->set_key_mode;
system_play_clip = vtable->play_clip;
}
#undef DYNAMIC_LINK_API
#endif

View File

@ -52,6 +52,7 @@
#define system_is_fullscreen_sig() b32 system_is_fullscreen(void)
#define system_get_keyboard_modifiers_sig() Input_Modifier_Set system_get_keyboard_modifiers(Arena* arena)
#define system_set_key_mode_sig() void system_set_key_mode(Key_Mode mode)
#define system_play_clip_sig() void system_play_clip(Audio_Clip clip, Audio_Control* control)
typedef String_Const_u8 system_get_path_type(Arena* arena, System_Path_Code path_code);
typedef String_Const_u8 system_get_canonical_type(Arena* arena, String_Const_u8 name);
typedef File_List system_get_file_list_type(Arena* arena, String_Const_u8 directory);
@ -106,6 +107,7 @@ typedef b32 system_set_fullscreen_type(b32 full_screen);
typedef b32 system_is_fullscreen_type(void);
typedef Input_Modifier_Set system_get_keyboard_modifiers_type(Arena* arena);
typedef void system_set_key_mode_type(Key_Mode mode);
typedef void system_play_clip_type(Audio_Clip clip, Audio_Control* control);
struct API_VTable_system{
system_get_path_type *get_path;
system_get_canonical_type *get_canonical;
@ -161,6 +163,7 @@ system_set_fullscreen_type *set_fullscreen;
system_is_fullscreen_type *is_fullscreen;
system_get_keyboard_modifiers_type *get_keyboard_modifiers;
system_set_key_mode_type *set_key_mode;
system_play_clip_type *play_clip;
};
#if defined(STATIC_LINK_API)
internal String_Const_u8 system_get_path(Arena* arena, System_Path_Code path_code);
@ -217,6 +220,7 @@ internal b32 system_set_fullscreen(b32 full_screen);
internal b32 system_is_fullscreen(void);
internal Input_Modifier_Set system_get_keyboard_modifiers(Arena* arena);
internal void system_set_key_mode(Key_Mode mode);
internal void system_play_clip(Audio_Clip clip, Audio_Control* control);
#undef STATIC_LINK_API
#elif defined(DYNAMIC_LINK_API)
global system_get_path_type *system_get_path = 0;
@ -273,5 +277,6 @@ global system_set_fullscreen_type *system_set_fullscreen = 0;
global system_is_fullscreen_type *system_is_fullscreen = 0;
global system_get_keyboard_modifiers_type *system_get_keyboard_modifiers = 0;
global system_set_key_mode_type *system_set_key_mode = 0;
global system_play_clip_type *system_play_clip = 0;
#undef DYNAMIC_LINK_API
#endif

View File

@ -245,5 +245,10 @@ api_param(arena, call, "Arena*", "arena");
API_Call *call = api_call_with_location(arena, result, string_u8_litexpr("set_key_mode"), string_u8_litexpr("void"), string_u8_litexpr(""));
api_param(arena, call, "Key_Mode", "mode");
}
{
API_Call *call = api_call_with_location(arena, result, string_u8_litexpr("play_clip"), string_u8_litexpr("void"), string_u8_litexpr(""));
api_param(arena, call, "Audio_Clip", "clip");
api_param(arena, call, "Audio_Control*", "control");
}
return(result);
}

View File

@ -52,3 +52,4 @@ api(system) function b32 set_fullscreen(b32 full_screen);
api(system) function b32 is_fullscreen(void);
api(system) function Input_Modifier_Set get_keyboard_modifiers(Arena* arena);
api(system) function void set_key_mode(Key_Mode mode);
api(system) function void play_clip(Audio_Clip clip, Audio_Control* control);

View File

@ -179,6 +179,9 @@ struct Win32_Vars{
HWND window_handle;
f32 screen_scale_factor;
Audio_System audio_system;
DWORD audio_thread_id;
f64 count_per_usecond;
b32 first;
i32 running_cli;
@ -259,6 +262,7 @@ handle_type_ptr(void *ptr){
////////////////////////////////
#include "win32_4ed_functions.cpp"
#include "win32_audio.cpp"
////////////////////////////////
@ -355,9 +359,16 @@ system_set_key_mode_sig(){
win32vars.key_mode = mode;
}
//
// Clipboard
//
////////////////////////////////
// NOTE(allen): Audio
internal
system_play_clip_sig(){
win32_play_clip(clip, control);
}
////////////////////////////////
// NOTE(allen): Clipboard
internal String_Const_u8
win32_read_clipboard_contents(Thread_Context *tctx, Arena *arena){
@ -1783,10 +1794,7 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdS
}
}
//
// Window and GL Initialization
//
// NOTE(allen): Window Init
RECT window_rect = {};
if (plat_settings.set_window_size){
window_rect.right = plat_settings.window_w;
@ -1811,10 +1819,10 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdS
GetClientRect(win32vars.window_handle, &window_rect);
win32_resize(window_rect.right - window_rect.left, window_rect.bottom - window_rect.top);
//
// Misc System Initializations
//
// NOTE(allen): Audio Init
win32vars.audio_thread_id = win32_audio_init(&win32vars.audio_system);
// NOTE(allen): Misc Init
if (!AddClipboardFormatListener(win32vars.window_handle)){
Scratch_Block scratch(win32vars.tctx);
win32_output_error_string(scratch, ErrorString_UseLog);

View File

@ -0,0 +1,199 @@
////////////////////////////////
// NOTE(allen): Win32 Audio Functions
function u32
AtomicAddU32AndReturnOriginal(u32 volatile *Value, u32 Addend)
{
// NOTE(casey): Returns the original value _prior_ to adding
u32 Result = _InterlockedExchangeAdd(Value, Addend);
return(Result);
}
function void
win32_begin_ticket_mutex(Audio_System *Crunky)
{
u32 Ticket = AtomicAddU32AndReturnOriginal(&Crunky->ticket, 1);
while(Ticket != Crunky->serving) {_mm_pause();}
}
function void
win32_end_ticket_mutex(Audio_System *Crunky)
{
AtomicAddU32AndReturnOriginal(&Crunky->serving, 1);
}
function void
win32_play_clip(Audio_Clip clip, Audio_Control *control){
clip.control = control;
Audio_System *Crunky = &win32vars.audio_system;
win32_begin_ticket_mutex(Crunky);
if (Crunky->pending_clip_count < ArrayCount(Crunky->pending_clips))
{
Crunky->pending_clips[Crunky->pending_clip_count++] = clip;
}
win32_end_ticket_mutex(Crunky);
}
function void
win32_mix_audio(Audio_System *Crunky, u32 SampleCount, i16 *Dest){
// NOTE(casey): Move pending sounds into the playing list
win32_begin_ticket_mutex(Crunky);
++Crunky->generation;
for(u32 PendIndex = 0;
PendIndex < Crunky->pending_clip_count;
++PendIndex)
{
u32 DestIndex = Crunky->next_playing_clip_index++ % ArrayCount(Crunky->playing_clips);
Crunky->playing_clips[DestIndex] = Crunky->pending_clips[PendIndex];
}
Crunky->pending_clip_count = 0;
win32_end_ticket_mutex(Crunky);
memset(Dest, 0, SampleCount*4);
for(u32 SoundIndex = 0;
SoundIndex < ArrayCount(Crunky->playing_clips);
++SoundIndex)
{
Audio_Clip Sound = Crunky->playing_clips[SoundIndex];
u32 SamplesToMix = Min((Sound.sample_count - Sound.at_sample_index), SampleCount);
Crunky->playing_clips[SoundIndex].at_sample_index += SamplesToMix;
i32 LeftVol = AUDIO_PRODUCER_KNOB_ONE;
i32 RightVol = AUDIO_PRODUCER_KNOB_ONE;
if(SamplesToMix && Sound.control)
{
LeftVol = Sound.control->left_volume_knob;
RightVol = Sound.control->right_volume_knob;
Sound.control->generation = Crunky->generation;
Sound.control->last_played_sample_index = Crunky->playing_clips[SoundIndex].at_sample_index;
}
for(u32 SampleIndex = 0;
SampleIndex < SamplesToMix;
++SampleIndex)
{
u32 src_base_indx = 2*(Sound.at_sample_index + SampleIndex);
u32 dst_base_indx = 2*SampleIndex;
Dest[dst_base_indx + 0] += (i16)(LeftVol*(i32)Sound.samples[src_base_indx + 0]/AUDIO_PRODUCER_KNOB_ONE);
Dest[dst_base_indx + 1] += (i16)(RightVol*(i32)Sound.samples[src_base_indx + 1]/AUDIO_PRODUCER_KNOB_ONE);
}
}
}
function b32
win32_submit_audio(Audio_System *Crunky, HWAVEOUT WaveOut, WAVEHDR *Header){
b32 Result = false;
u32 SampleCount = Header->dwBufferLength/4;
i16 *Samples = (i16 *)Header->lpData;
win32_mix_audio(Crunky, SampleCount, Samples);
DWORD Error = waveOutPrepareHeader(WaveOut, Header, sizeof(*Header));
if(Error == MMSYSERR_NOERROR)
{
Error = waveOutWrite(WaveOut, Header, sizeof(*Header));
if(Error == MMSYSERR_NOERROR)
{
// NOTE(casey): Success
Result = true;
}
}
return(Result);
}
function DWORD WINAPI
win32_audio_loop(void *Passthrough){
Audio_System *Crunky = (Audio_System *)Passthrough;
//
// NOTE(casey): Set up our audio output buffer
//
u32 SamplesPerSecond = 48000;
u32 SamplesPerBuffer = 16*SamplesPerSecond/1000;
u32 ChannelCount = 2;
u32 BytesPerChannelValue = 2;
u32 BytesPerSample = ChannelCount*BytesPerChannelValue;
u32 BufferCount = 3;
u32 BufferSize = SamplesPerBuffer*BytesPerSample;
u32 HeaderSize = sizeof(WAVEHDR);
u32 TotalBufferSize = (BufferSize+HeaderSize);
u32 TotalAudioMemorySize = BufferCount*TotalBufferSize;
//
// NOTE(casey): Initialize audio out
//
HWAVEOUT WaveOut = {};
WAVEFORMATEX Format = {};
Format.wFormatTag = WAVE_FORMAT_PCM;
Format.nChannels = (WORD)ChannelCount;
Format.wBitsPerSample = (WORD)(8*BytesPerChannelValue);
Format.nSamplesPerSec = SamplesPerSecond;
Format.nBlockAlign = (Format.nChannels*Format.wBitsPerSample)/8;
Format.nAvgBytesPerSec = Format.nBlockAlign * Format.nSamplesPerSec;
if(waveOutOpen(&WaveOut, WAVE_MAPPER, &Format, GetCurrentThreadId(), 0, CALLBACK_THREAD) == MMSYSERR_NOERROR)
{
void *AudioBufferMemory = VirtualAlloc(0, TotalAudioMemorySize, MEM_COMMIT, PAGE_READWRITE);
if(AudioBufferMemory)
{
u8 *At = (u8 *)AudioBufferMemory;
for(u32 BufferIndex = 0;
BufferIndex < BufferCount;
++BufferIndex)
{
WAVEHDR *Header = (WAVEHDR *)At;
At += sizeof(WAVEHDR);
Header->lpData = (char *)At;
Header->dwBufferLength = TotalBufferSize;
At += TotalBufferSize;
win32_submit_audio(Crunky, WaveOut, Header);
}
}
else
{
Crunky->quit = true;
}
}
else
{
Crunky->quit = true;
}
//
// NOTE(casey): Serve audio forever (until we are told to stop)
//
// SetTimer(0, 0, 100, 0);
while(!Crunky->quit)
{
MSG Message = {};
GetMessage(&Message, 0, 0, 0);
if(Message.message == MM_WOM_DONE)
{
WAVEHDR *Header = (WAVEHDR *)Message.lParam;
if(Header->dwFlags & WHDR_DONE)
{
Header->dwFlags &= ~WHDR_DONE;
}
waveOutUnprepareHeader(WaveOut, Header, sizeof(*Header));
win32_submit_audio(Crunky, WaveOut, Header);
}
}
return(0);
}
function DWORD
win32_audio_init(Audio_System *audio_system){
DWORD thread_id = 0;
HANDLE handle = CreateThread(0, 0, win32_audio_loop, audio_system, 0, &thread_id);
CloseHandle(handle);
return(thread_id);
}