linux audio

master
Alex Baines 2020-11-24 20:11:47 +00:00
parent cd6e11f6ee
commit 9241285f74
7 changed files with 201 additions and 11 deletions

View File

@ -2,7 +2,12 @@
// NOTE(allen): Default Mixer Helpers // NOTE(allen): Default Mixer Helpers
// TODO(allen): intrinsics wrappers // TODO(allen): intrinsics wrappers
#if OS_LINUX
#include <immintrin.h>
#define _InterlockedExchangeAdd __sync_fetch_and_add
#else
#include <intrin.h> #include <intrin.h>
#endif
function u32 function u32
AtomicAddU32AndReturnOriginal(u32 volatile *Value, u32 Addend) AtomicAddU32AndReturnOriginal(u32 volatile *Value, u32 Addend)

View File

@ -102,9 +102,9 @@ dynamic_binding_load_from_file(Application_Links *app, Mapping *mapping, String_
} }
else{ else{
config_add_error(scratch, parsed, node->result.pos, config_add_error(scratch, parsed, node->result.pos,
(keycode != 0) ? "Invalid command" : (keycode != 0) ? (char*)"Invalid command" :
(command != 0) ? "Invalid key": (command != 0) ? (char*)"Invalid key":
"Invalid command and key"); (char*)"Invalid command and key");
} }
} }

View File

@ -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); 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 <stdio.h>
#endif
function FILE *def_search_fopen(Arena *arena, List_String_Const_u8 *list, char *file_name, char *opt); 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); function FILE *def_search_normal_fopen(Arena *arena, char *file_name, char *opt);

View File

@ -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))

View File

@ -192,6 +192,13 @@ struct Linux_Vars {
b32 received_new_clipboard; b32 received_new_clipboard;
b32 clipboard_catch_all; 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_TARGETS;
Atom atom_CLIPBOARD; Atom atom_CLIPBOARD;
Atom atom_UTF8_STRING; Atom atom_UTF8_STRING;
@ -266,6 +273,11 @@ handle_to_object(Plat_Handle ph){
return *(Linux_Object**)&ph; return *(Linux_Object**)&ph;
} }
Plat_Handle
object_to_handle(Linux_Object* obj) {
return *(Plat_Handle*)&obj;
}
internal Linux_Object* internal Linux_Object*
linux_alloc_object(Linux_Object_Kind kind){ linux_alloc_object(Linux_Object_Kind kind){
Linux_Object* result = NULL; Linux_Object* result = NULL;
@ -546,6 +558,7 @@ os_popup_error(char *title, char *message){
//////////////////////////// ////////////////////////////
#include "linux_4ed_functions.cpp" #include "linux_4ed_functions.cpp"
#include "linux_4ed_audio.cpp"
//////////////////////////// ////////////////////////////
@ -1728,6 +1741,9 @@ main(int argc, char **argv){
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&linuxvars.memory_tracker_mutex, &attr); 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 // NOTE(allen): context setup
{ {
Base_Allocator* alloc = get_base_allocator_system(); Base_Allocator* alloc = get_base_allocator_system();
@ -1767,10 +1783,10 @@ main(int argc, char **argv){
{ {
App_Get_Functions *get_funcs = 0; App_Get_Functions *get_funcs = 0;
Scratch_Block scratch(&linuxvars.tctx); Scratch_Block scratch(&linuxvars.tctx);
Path_Search_List search_list = {}; List_String_Const_u8 search_list = {};
search_list_add_system_path(scratch, &search_list, SystemPath_Binary); 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)){ if (system_load_library(scratch, core_path, &core_library)){
get_funcs = (App_Get_Functions*)system_get_proc(core_library, "app_get_functions"); get_funcs = (App_Get_Functions*)system_get_proc(core_library, "app_get_functions");
if (get_funcs != 0){ if (get_funcs != 0){
@ -1828,9 +1844,9 @@ main(int argc, char **argv){
Scratch_Block scratch(&linuxvars.tctx); Scratch_Block scratch(&linuxvars.tctx);
String_Const_u8 default_file_name = string_u8_litexpr("custom_4coder.so"); String_Const_u8 default_file_name = string_u8_litexpr("custom_4coder.so");
Path_Search_List search_list = {}; List_String_Const_u8 search_list = {};
search_list_add_system_path(scratch, &search_list, SystemPath_CurrentDirectory); def_search_list_add_system_path(scratch, &search_list, SystemPath_CurrentDirectory);
search_list_add_system_path(scratch, &search_list, SystemPath_Binary); def_search_list_add_system_path(scratch, &search_list, SystemPath_Binary);
String_Const_u8 custom_file_names[2] = {}; String_Const_u8 custom_file_names[2] = {};
i32 custom_file_count = 1; i32 custom_file_count = 1;
if (plat_settings.custom_dll != 0){ if (plat_settings.custom_dll != 0){
@ -1845,7 +1861,7 @@ main(int argc, char **argv){
} }
String_Const_u8 custom_file_name = {}; String_Const_u8 custom_file_name = {};
for (i32 i = 0; i < custom_file_count; i += 1){ 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){ if (custom_file_name.size > 0){
break; break;
} }
@ -1877,6 +1893,9 @@ main(int argc, char **argv){
linux_keycode_init(linuxvars.dpy); linux_keycode_init(linuxvars.dpy);
linux_epoll_init(); linux_epoll_init();
linuxvars.audio_thread = system_thread_launch(&linux_audio_main, NULL);
// app init // app init
{ {
Scratch_Block scratch(&linuxvars.tctx); Scratch_Block scratch(&linuxvars.tctx);

View File

@ -0,0 +1,132 @@
#define ___fred_function function
#undef function
#include <alsa/asoundlib.h>
#include <poll.h>
#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

View File

@ -334,6 +334,7 @@ system_wake_up_timer_create(void){
// NOTE(inso): timers created on-demand to avoid file-descriptor exhaustion. // NOTE(inso): timers created on-demand to avoid file-descriptor exhaustion.
object->timer.fd = -1; object->timer.fd = -1;
return object_to_handle(object);
} }
internal void internal void
@ -803,5 +804,20 @@ system_set_key_mode_sig(){
linuxvars.key_mode = mode; 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 // NOTE(inso): to prevent me continuously messing up indentation
// vim: et:ts=4:sts=4:sw=4 // vim: et:ts=4:sts=4:sw=4