From 9241285f749ec4a55ab7ba2538bcdfeda9cdad94 Mon Sep 17 00:00:00 2001 From: Alex Baines Date: Tue, 24 Nov 2020 20:11:47 +0000 Subject: [PATCH] linux audio --- custom/4coder_audio.cpp | 5 + custom/4coder_dynamic_bindings.cpp | 6 +- custom/4coder_search_list.h | 4 + platform_linux/alsa_funcs.txt | 14 +++ platform_linux/linux_4ed.cpp | 35 +++++-- platform_linux/linux_4ed_audio.cpp | 132 +++++++++++++++++++++++++ platform_linux/linux_4ed_functions.cpp | 16 +++ 7 files changed, 201 insertions(+), 11 deletions(-) create mode 100644 platform_linux/alsa_funcs.txt create mode 100644 platform_linux/linux_4ed_audio.cpp diff --git a/custom/4coder_audio.cpp b/custom/4coder_audio.cpp index a7a6e6bf..c1b406b5 100644 --- a/custom/4coder_audio.cpp +++ b/custom/4coder_audio.cpp @@ -2,7 +2,12 @@ // NOTE(allen): Default Mixer Helpers // TODO(allen): intrinsics wrappers +#if OS_LINUX +#include +#define _InterlockedExchangeAdd __sync_fetch_and_add +#else #include +#endif function u32 AtomicAddU32AndReturnOriginal(u32 volatile *Value, u32 Addend) diff --git a/custom/4coder_dynamic_bindings.cpp b/custom/4coder_dynamic_bindings.cpp index 5f351f6b..bb9e3668 100644 --- a/custom/4coder_dynamic_bindings.cpp +++ b/custom/4coder_dynamic_bindings.cpp @@ -102,9 +102,9 @@ dynamic_binding_load_from_file(Application_Links *app, Mapping *mapping, String_ } else{ config_add_error(scratch, parsed, node->result.pos, - (keycode != 0) ? "Invalid command" : - (command != 0) ? "Invalid key": - "Invalid command and key"); + (keycode != 0) ? (char*)"Invalid command" : + (command != 0) ? (char*)"Invalid key": + (char*)"Invalid command and key"); } } diff --git a/custom/4coder_search_list.h b/custom/4coder_search_list.h index ad2194c7..307726c5 100644 --- a/custom/4coder_search_list.h +++ b/custom/4coder_search_list.h @@ -27,6 +27,10 @@ function void def_search_normal_load_list(Arena *arena, List_String_Const_u8 *li function String_Const_u8 def_search_get_full_path(Arena *arena, List_String_Const_u8 *list, String_Const_u8 file_name); +#if OS_LINUX +#include +#endif + function FILE *def_search_fopen(Arena *arena, List_String_Const_u8 *list, char *file_name, char *opt); function FILE *def_search_normal_fopen(Arena *arena, char *file_name, char *opt); diff --git a/platform_linux/alsa_funcs.txt b/platform_linux/alsa_funcs.txt new file mode 100644 index 00000000..6c9fbd57 --- /dev/null +++ b/platform_linux/alsa_funcs.txt @@ -0,0 +1,14 @@ +ALSA_FN(snd_pcm_sframes_t, writei , (snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)) +ALSA_FN(int , recover , (snd_pcm_t *pcm, int err, int silent)) +ALSA_FN(int , open , (snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode)) +ALSA_FN(int , hw_params_malloc , (snd_pcm_hw_params_t **ptr)) +ALSA_FN(int , hw_params_any , (snd_pcm_t *pcm, snd_pcm_hw_params_t *params)) +ALSA_FN(int , hw_params_set_access , (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t _access)) +ALSA_FN(int , hw_params_set_format , (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val)) +ALSA_FN(int , hw_params_set_channels , (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val)) +ALSA_FN(int , hw_params_set_rate , (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val, int dir)) +ALSA_FN(int , hw_params_set_buffer_size, (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t val)) +ALSA_FN(int , hw_params , (snd_pcm_t *pcm, snd_pcm_hw_params_t *params)) +ALSA_FN(void , hw_params_free , (snd_pcm_hw_params_t *obj)) +ALSA_FN(int , poll_descriptors_count , (snd_pcm_t *pcm)) +ALSA_FN(int , poll_descriptors , (snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space)) diff --git a/platform_linux/linux_4ed.cpp b/platform_linux/linux_4ed.cpp index 626869c1..50efe1cc 100644 --- a/platform_linux/linux_4ed.cpp +++ b/platform_linux/linux_4ed.cpp @@ -191,7 +191,14 @@ struct Linux_Vars { String_Const_u8 clipboard_contents; b32 received_new_clipboard; b32 clipboard_catch_all; - + + pthread_mutex_t audio_mutex; + pthread_cond_t audio_cond; + void* audio_ctx; + Audio_Mix_Sources_Function* audio_src_func; + Audio_Mix_Destination_Function* audio_dst_func; + System_Thread audio_thread; + Atom atom_TARGETS; Atom atom_CLIPBOARD; Atom atom_UTF8_STRING; @@ -266,6 +273,11 @@ handle_to_object(Plat_Handle ph){ return *(Linux_Object**)&ph; } +Plat_Handle +object_to_handle(Linux_Object* obj) { + return *(Plat_Handle*)&obj; +} + internal Linux_Object* linux_alloc_object(Linux_Object_Kind kind){ Linux_Object* result = NULL; @@ -546,6 +558,7 @@ os_popup_error(char *title, char *message){ //////////////////////////// #include "linux_4ed_functions.cpp" +#include "linux_4ed_audio.cpp" //////////////////////////// @@ -1727,6 +1740,9 @@ main(int argc, char **argv){ pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&linuxvars.memory_tracker_mutex, &attr); + + pthread_mutex_init(&linuxvars.audio_mutex, &attr); + pthread_cond_init(&linuxvars.audio_cond, NULL); // NOTE(allen): context setup { @@ -1767,10 +1783,10 @@ main(int argc, char **argv){ { App_Get_Functions *get_funcs = 0; Scratch_Block scratch(&linuxvars.tctx); - Path_Search_List search_list = {}; - search_list_add_system_path(scratch, &search_list, SystemPath_Binary); + List_String_Const_u8 search_list = {}; + def_search_list_add_system_path(scratch, &search_list, SystemPath_Binary); - String_Const_u8 core_path = get_full_path(scratch, &search_list, SCu8("4ed_app.so")); + String_Const_u8 core_path = def_get_full_path(scratch, &search_list, SCu8("4ed_app.so")); if (system_load_library(scratch, core_path, &core_library)){ get_funcs = (App_Get_Functions*)system_get_proc(core_library, "app_get_functions"); if (get_funcs != 0){ @@ -1828,9 +1844,9 @@ main(int argc, char **argv){ Scratch_Block scratch(&linuxvars.tctx); String_Const_u8 default_file_name = string_u8_litexpr("custom_4coder.so"); - Path_Search_List search_list = {}; - search_list_add_system_path(scratch, &search_list, SystemPath_CurrentDirectory); - search_list_add_system_path(scratch, &search_list, SystemPath_Binary); + List_String_Const_u8 search_list = {}; + def_search_list_add_system_path(scratch, &search_list, SystemPath_CurrentDirectory); + def_search_list_add_system_path(scratch, &search_list, SystemPath_Binary); String_Const_u8 custom_file_names[2] = {}; i32 custom_file_count = 1; if (plat_settings.custom_dll != 0){ @@ -1845,7 +1861,7 @@ main(int argc, char **argv){ } String_Const_u8 custom_file_name = {}; for (i32 i = 0; i < custom_file_count; i += 1){ - custom_file_name = get_full_path(scratch, &search_list, custom_file_names[i]); + custom_file_name = def_get_full_path(scratch, &search_list, custom_file_names[i]); if (custom_file_name.size > 0){ break; } @@ -1876,6 +1892,9 @@ main(int argc, char **argv){ linux_x11_init(argc, argv, &plat_settings); linux_keycode_init(linuxvars.dpy); linux_epoll_init(); + + linuxvars.audio_thread = system_thread_launch(&linux_audio_main, NULL); + // app init { diff --git a/platform_linux/linux_4ed_audio.cpp b/platform_linux/linux_4ed_audio.cpp new file mode 100644 index 00000000..54585ad6 --- /dev/null +++ b/platform_linux/linux_4ed_audio.cpp @@ -0,0 +1,132 @@ +#define ___fred_function function +#undef function +#include +#include +#define function ___fred_function + +internal void +linux_default_mix_sources(void *ctx, f32 *mix_buffer, u32 sample_count) +{ + +} + +internal void +linux_default_mix_destination(i16 *dst, f32 *src, u32 sample_count) +{ + u32 opl = sample_count*2; + for(u32 i = 0; i < sample_count; i += 1){ + dst[i] = (i16)src[i]; + } +} + +internal struct alsa_funcs { +#define ALSA_FN(r,n,a) r (*n) a; +#include "alsa_funcs.txt" +#undef ALSA_FN +} snd_pcm; + +internal void +linux_submit_audio(snd_pcm_t* pcm, i16* samples, u32 sample_count, f32* mix_buffer) +{ + Audio_Mix_Sources_Function *audio_mix_src; + Audio_Mix_Destination_Function *audio_mix_dst; + + pthread_mutex_lock(&linuxvars.audio_mutex); + audio_mix_src = linuxvars.audio_src_func; + audio_mix_dst = linuxvars.audio_dst_func; + void* audio_ctx = linuxvars.audio_ctx; + pthread_mutex_unlock(&linuxvars.audio_mutex); + + if(!audio_mix_src) { + audio_mix_src = linux_default_mix_sources; + } + + if(!audio_mix_dst) { + audio_mix_dst = linux_default_mix_destination; + } + + audio_mix_src(audio_ctx, mix_buffer, sample_count); + audio_mix_dst(samples, mix_buffer, sample_count); + + int err = snd_pcm.writei(pcm, samples, sample_count); + if(err < 0){ + snd_pcm.recover(pcm, err, 1); + } +} + +#define chk(x) ({\ + int err = (x);\ + if(err < 0){\ + fprintf(stderr, "ALSA ERR: %s: [%d]\n", #x, err);\ + }\ +}) + +internal void +linux_audio_main(void* _unused) +{ + const u32 SamplesPerSecond = 48000; + const u32 SamplesPerBuffer = 16*SamplesPerSecond/1000; + const u32 ChannelCount = 2; + const u32 BytesPerSample = 2; // S16LE + const u32 BufferSize = SamplesPerBuffer * BytesPerSample; + const u32 BufferCount = 3; + const u32 MixBufferSize = (SamplesPerBuffer * ChannelCount * sizeof(f32)); + const u32 SampleBufferSize = (SamplesPerBuffer * ChannelCount * sizeof(i16)); + + void* lib = dlopen("libasound.so.2", RTLD_LOCAL | RTLD_LAZY); + if(!lib) { + fprintf(stderr, "failed to load libasound.so.2: %s", dlerror());\ + return; + } + +#define ALSA_FN(r,n,a)\ + *((void**)&snd_pcm.n) = (void*)dlsym(lib, stringify(snd_pcm_##n));\ + if(!snd_pcm.n){\ + fprintf(stderr, "failed to load alsa func: %s", #n);\ + return;\ + } +#include "alsa_funcs.txt" +#undef ALSA_FN + + snd_pcm_t* pcm; + + chk( snd_pcm.open(&pcm, "default", SND_PCM_STREAM_PLAYBACK, 0)); + + snd_pcm_hw_params_t* hw; + chk( snd_pcm.hw_params_malloc (&hw)); + chk( snd_pcm.hw_params_any (pcm, hw)); + chk( snd_pcm.hw_params_set_access (pcm, hw, SND_PCM_ACCESS_RW_INTERLEAVED)); + chk( snd_pcm.hw_params_set_format (pcm, hw, SND_PCM_FORMAT_S16_LE)); + chk( snd_pcm.hw_params_set_channels (pcm, hw, ChannelCount)); + chk( snd_pcm.hw_params_set_rate (pcm, hw, SamplesPerSecond, 0)); + chk( snd_pcm.hw_params_set_buffer_size (pcm, hw, BufferSize * BufferCount)); + chk( snd_pcm.hw_params (pcm, hw)); + snd_pcm.hw_params_free (hw); + + int fd_count = snd_pcm.poll_descriptors_count(pcm); + struct pollfd* fds = (struct pollfd*)calloc(fd_count, sizeof(struct pollfd)); + snd_pcm.poll_descriptors(pcm, fds, fd_count); + + for(;;) { + int n = poll(fds, fd_count, -1); + if(n == -1) { + perror("poll"); + continue; + } + + f32* MixBuffer = (f32*)calloc(1, MixBufferSize); + i16* SampleBuffer = (i16*)calloc(1, SampleBufferSize); + + if(!MixBuffer || !SampleBuffer) { + perror("calloc"); + continue; + } + + linux_submit_audio(pcm, SampleBuffer, SamplesPerBuffer, MixBuffer); + + free(MixBuffer); + free(SampleBuffer); + } +} + +#undef chk diff --git a/platform_linux/linux_4ed_functions.cpp b/platform_linux/linux_4ed_functions.cpp index d63b71d1..5044b8b7 100644 --- a/platform_linux/linux_4ed_functions.cpp +++ b/platform_linux/linux_4ed_functions.cpp @@ -334,6 +334,7 @@ system_wake_up_timer_create(void){ // NOTE(inso): timers created on-demand to avoid file-descriptor exhaustion. object->timer.fd = -1; + return object_to_handle(object); } internal void @@ -803,5 +804,20 @@ system_set_key_mode_sig(){ linuxvars.key_mode = mode; } +internal void +system_set_source_mixer(void* ctx, Audio_Mix_Sources_Function* mix_func){ + pthread_mutex_lock(&linuxvars.audio_mutex); + linuxvars.audio_ctx = ctx; + linuxvars.audio_src_func = mix_func; + pthread_mutex_unlock(&linuxvars.audio_mutex); +} + +internal void +system_set_destination_mixer(Audio_Mix_Destination_Function* mix_func){ + pthread_mutex_lock(&linuxvars.audio_mutex); + linuxvars.audio_dst_func = mix_func; + pthread_mutex_unlock(&linuxvars.audio_mutex); +} + // NOTE(inso): to prevent me continuously messing up indentation // vim: et:ts=4:sts=4:sw=4