linux audio
parent
cd6e11f6ee
commit
9241285f74
|
@ -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)
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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))
|
|
@ -191,7 +191,14 @@ struct Linux_Vars {
|
||||||
String_Const_u8 clipboard_contents;
|
String_Const_u8 clipboard_contents;
|
||||||
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"
|
||||||
|
|
||||||
////////////////////////////
|
////////////////////////////
|
||||||
|
|
||||||
|
@ -1727,6 +1740,9 @@ main(int argc, char **argv){
|
||||||
pthread_mutexattr_init(&attr);
|
pthread_mutexattr_init(&attr);
|
||||||
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
|
||||||
{
|
{
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -1876,6 +1892,9 @@ main(int argc, char **argv){
|
||||||
linux_x11_init(argc, argv, &plat_settings);
|
linux_x11_init(argc, argv, &plat_settings);
|
||||||
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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue