diff --git a/CMakeLists.txt b/CMakeLists.txt index 0dde950..ae1700b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,23 @@ project(prototype1 C) #include_directories(${CMAKE_SOURCE_DIR}) set(CMAKE_C_FLAGS "-Wall -Wextra -Werror=implicit-function-declaration -Werror=return-type --std=c99 -g -ggdb -O0") +execute_process( + COMMAND pkg-config --cflags libpipewire-0.3 + OUTPUT_VARIABLE LIBPIPEWIRE_CFLAGS + OUTPUT_STRIP_TRAILING_WHITESPACE +) +execute_process( + COMMAND pkg-config --libs libpipewire-0.3 + OUTPUT_VARIABLE LIBPIPEWIRE_LIBS + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +#add_compile_options("-I/nix/store/2hm4rjvywd00p417y43i9rzx8v793qi0-pipewire-1.4.5-dev/include/pipewire-0.3 -I/nix/store/2hm4rjvywd00p417y43i9rzx8v793qi0-pipewire-1.4.5-dev/include/spa-0.2") + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} \ + -I /nix/store/2hm4rjvywd00p417y43i9rzx8v793qi0-pipewire-1.4.5-dev/include/pipewire-0.3 \ + -I /nix/store/2hm4rjvywd00p417y43i9rzx8v793qi0-pipewire-1.4.5-dev/include/spa-0.2 ") + add_compile_definitions(_POSIX_C_SOURCE=200112L) add_compile_definitions(_GNU_SOURCE) add_compile_options(-fno-trapping-math) @@ -26,6 +43,13 @@ target_link_libraries(0_render_test_tex_init_prep -lm) add_executable(1_render_test src/l2/tests/r1/r1.c gen/l_wl_protocols/xdg-shell-private.c) target_link_libraries(1_render_test -lwayland-client -lrt -lm -lxkbcommon) +add_executable(2a_render_test src/l2/tests/r2/r2a.c) +target_link_libraries(2a_render_test ${LIBPIPEWIRE_LIBS} -lm) + +add_executable(2b_render_test src/l2/tests/r2/r2b.c) +target_link_libraries(2b_render_test ${LIBPIPEWIRE_LIBS} -lm) + + #add_executable(0_play_test src/l3/tests/p0.c) #target_link_libraries(0_play_test -lncurses) # diff --git a/src/l1/codegen/codegen.c b/src/l1/codegen/codegen.c index 63e5927..64b0935 100644 --- a/src/l1/codegen/codegen.c +++ b/src/l1/codegen/codegen.c @@ -33,7 +33,7 @@ int main() { SpanU8 T[4] = {cstr("VecU8"), cstr("VecU16"), cstr("VecU32"), cstr("VecU64")}; for (size_t i = 0; i < ARRAY_SIZE(T); i++) { VecU8_append_vec(&head, generate_util_templates_instantiation(T[i], (util_templates_instantiation_options){ - .vec = true, .vec_extended = true, .span = true, .collab_vec_span = true, .vec_equal = true, + .t_clonable = true, .vec = true, .vec_extended = true, .span = true, .collab_vec_span = true, .vec_equal = true, })); } VecU8_append_vec(&head, generate_VecT_new_of_size_method(cstr("VecU8"))); diff --git a/src/l1/codegen/util_template_inst.h b/src/l1/codegen/util_template_inst.h index 37e0e79..b7ce591 100644 --- a/src/l1/codegen/util_template_inst.h +++ b/src/l1/codegen/util_template_inst.h @@ -3,8 +3,10 @@ #include "codegen.h" -/* if !primitive, requires methods T_clone, T_drop */ -NODISCARD VecU8 generate_VecT_struct_and_base_methods(SpanU8 T, bool primitive) { +// todo: add macro that iterates over vector + +/* if !primitive, requires methods T_drop, and, if also clonable, requires method T_clone */ +NODISCARD VecU8 generate_VecT_struct_and_base_methods(SpanU8 T, bool primitive, bool clonable) { VecU8 g_VecT = VecU8_fmt("Vec%s", T); SpanU8 VecT = VecU8_to_span(&g_VecT); VecU8 res = VecU8_fmt( @@ -14,7 +16,7 @@ NODISCARD VecU8 generate_VecT_struct_and_base_methods(SpanU8 T, bool primitive) SPACE4 "size_t capacity;\n" "} %s;\n\n", T, VecT); - VecU8_append_vec(&res, VecU8_fmt("#define %s_new() (%s){ 0 }\n\n", VecT, VecT)); + VecU8_append_vec(&res, VecU8_fmt("#define %s_new() ((%s){ 0 })\n\n", VecT, VecT)); VecU8_append_vec(&res, VecU8_fmt("void %s_drop(%s self) {\n", VecT, VecT)); if (!primitive) { @@ -55,19 +57,20 @@ NODISCARD VecU8 generate_VecT_struct_and_base_methods(SpanU8 T, bool primitive) SPACE4 "return &self->buf[i];\n" "}\n\n", T, VecT, VecT)); - VecU8_append_vec(&res, VecU8_fmt( - "NODISCARD %s %s_clone(const %s* self) {\n" - SPACE4 "%s res = (%s){.buf = safe_calloc(self->len, sizeof(%s)), .len = self->len, .capacity = self->len};\n", - VecT, VecT, VecT, VecT, VecT, T)); - if (primitive) { + if (clonable) { VecU8_append_vec(&res, VecU8_fmt( - SPACE4 "memcpy(res.buf, self->buf, self->len * sizeof(%s));\n", T)); - } else { - VecU8_append_vec(&res, VecU8_fmt( - SPACE4 "for (size_t i = 0; i < self->len; i++)\n" - SPACE4 SPACE4 "res.buf[i] = %s_clone(&self->buf[i]);\n", T)); + "NODISCARD %s %s_clone(const %s* self) {\n" + SPACE4 "%s res = (%s){.buf = safe_calloc(self->len, sizeof(%s)), .len = self->len, .capacity = self->len};\n", + VecT, VecT, VecT, VecT, VecT, T)); + if (primitive) { + VecU8_append_vec(&res, VecU8_fmt(SPACE4 "memcpy(res.buf, self->buf, self->len * sizeof(%s));\n", T)); + } else { + VecU8_append_vec(&res, VecU8_fmt( + SPACE4 "for (size_t i = 0; i < self->len; i++)\n" + SPACE4 SPACE4 "res.buf[i] = %s_clone(&self->buf[i]);\n", T)); + } + VecU8_append_span(&res, cstr(SPACE4 "return res;\n}\n\n")); } - VecU8_append_span(&res, cstr(SPACE4 "return res;\n}\n\n")); VecU8_append_vec(&res, VecU8_fmt( "void %s_append_vec(%s* self, %s b) {\n" @@ -96,7 +99,7 @@ NODISCARD VecU8 generate_VecT_struct_and_base_methods(SpanU8 T, bool primitive) } /* if !primitive, requires methods T_clone, T_drop */ -NODISCARD VecU8 generate_VecT_trivmove_extended_methods(SpanU8 T, bool primitive) { +NODISCARD VecU8 generate_VecT_trivmove_extended_methods(SpanU8 T, bool primitive, bool clonable) { VecU8 g_VecT = VecU8_fmt("Vec%s", T); SpanU8 VecT = VecU8_to_span(&g_VecT); VecU8 res = VecU8_new(); @@ -111,8 +114,29 @@ NODISCARD VecU8 generate_VecT_trivmove_extended_methods(SpanU8 T, bool primitive if (!primitive) { VecU8_append_vec(&res, VecU8_fmt( "void %s_pop_and_drop(%s* self) {\n" - SPACE4 "%s_drop(%s_pop(self));\n" - "}\n\n", VecT, VecT, T, VecT)); + SPACE4 "assert(self->len > 0);\n" + SPACE4 "%s_drop(self->buf[self->len - 1]);\n" + SPACE4 "self->len--;\n" + "}\n\n", VecT, VecT, T)); + } + + VecU8_append_vec(&res, VecU8_fmt( + "%s%s %s_unordered_pop(%s* self, size_t ind) {\n" + SPACE4 "assert(ind < self->len);\n" + SPACE4 "%s res = self->buf[ind];\n" + SPACE4 "self->buf[ind] = self->buf[self->len - 1];\n" + SPACE4 "self->len--;\n" + SPACE4 "return res;\n" + "}\n\n", primitive ? cstr("") : cstr("NODISCARD "), T, VecT, VecT, T)); + + if (!primitive) { + VecU8_append_vec(&res, VecU8_fmt( + "void %s_unordered_pop_and_drop(%s* self, size_t ind) {\n" + SPACE4 "assert(ind < self->len);\n" + SPACE4 "%s_drop(self->buf[ind]);\n" + SPACE4 "self->buf[ind] = self->buf[self->len - 1];\n" + SPACE4 "self->len--;\n" + "}\n\n", VecT, VecT, T)); } VecU8_append_vec(&res, VecU8_fmt( @@ -130,7 +154,7 @@ NODISCARD VecU8 generate_VecT_trivmove_extended_methods(SpanU8 T, bool primitive SPACE4 SPACE4 "res.buf[i] = el;\n" SPACE4 "return res;\n" "}\n\n", VecT, VecT, T, VecT, VecT, T)); - } else { + } else if (clonable) { VecU8_append_vec(&res, VecU8_fmt( "NODISCARD %s %s_new_filled(size_t len, const %s* el) {\n" SPACE4 "%s res = (%s){.buf = safe_calloc(len, sizeof(%s)), .len = len, .capacity = len};\n" @@ -283,8 +307,8 @@ NODISCARD VecU8 generate_SpanT_struct_and_methods( // void codegen_append_vec_some_span_method(VecU8* res, SpanU8 mod, SpanU8 ) -/* T must be trivially movable. If !primitive, requires methods T_drop (implicitly), T_clone */ -NODISCARD VecU8 generate_SpanT_VecT_trivmove_collab(SpanU8 T, bool primitive, bool add_mutable, bool add_extended) { +/* T must be trivially movable. If !primitive, requires methods T_drop (implicitly) and, if clonable, requires T_clone */ +NODISCARD VecU8 generate_SpanT_VecT_trivmove_collab(SpanU8 T, bool primitive, bool clonable, bool add_mutable, bool add_extended) { VecU8 g_SpanT = VecU8_fmt("Span%s", T); VecU8 g_MutSpanT = VecU8_fmt("MutSpan%s", T); VecU8 g_VecT = VecU8_fmt("Vec%s", T); @@ -293,20 +317,22 @@ NODISCARD VecU8 generate_SpanT_VecT_trivmove_collab(SpanU8 T, bool primitive, bo SpanU8 VecT = VecU8_to_span(&g_VecT); VecU8 res = VecU8_new(); - VecU8_append_vec(&res, VecU8_fmt( - "NODISCARD %s %s_from_span(%s src){\n" - SPACE4 "%s res = (%s){ .buf = safe_calloc(src.len, sizeof(%s)), .len = src.len, .capacity = src.len };\n", - VecT, VecT, SpanT, VecT, VecT, T)); - if (primitive) { + if (clonable) { VecU8_append_vec(&res, VecU8_fmt( - SPACE4 "memcpy(res.buf, src.data, src.len * sizeof(%s));\n", T)); - } else { - VecU8_append_vec(&res, VecU8_fmt( - SPACE4 "for (size_t i = 0; i < src.len; i++)\n" - SPACE8 "res.buf[i] = %s_clone(&src.data[i]);\n", T)); - } - VecU8_append_span(&res, cstr(SPACE4 "return res;\n}\n\n")); + "NODISCARD %s %s_from_span(%s src){\n" + SPACE4 "%s res = (%s){ .buf = safe_calloc(src.len, sizeof(%s)), .len = src.len, .capacity = src.len };\n", + VecT, VecT, SpanT, VecT, VecT, T)); + if (primitive) { + VecU8_append_vec(&res, VecU8_fmt( + SPACE4 "memcpy(res.buf, src.data, src.len * sizeof(%s));\n", T)); + } else { + VecU8_append_vec(&res, VecU8_fmt( + SPACE4 "for (size_t i = 0; i < src.len; i++)\n" + SPACE8 "res.buf[i] = %s_clone(&src.data[i]);\n", T)); + } + VecU8_append_span(&res, cstr(SPACE4 "return res;\n}\n\n")); + } VecU8_append_vec(&res, VecU8_fmt( "%s %s_to_span(const %s* vec){\n" SPACE4 "return (%s){vec->buf, vec->len};\n" @@ -318,25 +344,27 @@ NODISCARD VecU8 generate_SpanT_VecT_trivmove_collab(SpanU8 T, bool primitive, bo "}\n\n", MutSpanT, VecT, VecT, MutSpanT)); } - VecU8_append_vec(&res, VecU8_fmt( - "void %s_append_span(%s* self, %s b) {\n" - SPACE4 "size_t new_length = self->len + b.len;\n" - SPACE4 "if (new_length > self->capacity) {\n" - SPACE4 SPACE4 "size_t new_capacity = Vec_get_new_capacity(self->capacity, new_length);\n" - SPACE4 SPACE4 "self->buf = safe_realloc(self->buf, new_capacity * sizeof(%s));\n" - SPACE4 SPACE4 "self->capacity = new_capacity;\n" - SPACE4 "}\n", VecT, VecT, SpanT, T)); - if (primitive) { + if (clonable) { VecU8_append_vec(&res, VecU8_fmt( - SPACE4 "memcpy(self->buf + self->len, b.data, b.len * sizeof(%s));\n", T)); - } else { - VecU8_append_vec(&res, VecU8_fmt( - SPACE4 "for (size_t i = 0; i < b.len; i++)\n" - SPACE4 SPACE4 "self->buf[self->len + i] = %s_clone(&b.data[i]);\n", T)); + "void %s_append_span(%s* self, %s b) {\n" + SPACE4 "size_t new_length = self->len + b.len;\n" + SPACE4 "if (new_length > self->capacity) {\n" + SPACE4 SPACE4 "size_t new_capacity = Vec_get_new_capacity(self->capacity, new_length);\n" + SPACE4 SPACE4 "self->buf = safe_realloc(self->buf, new_capacity * sizeof(%s));\n" + SPACE4 SPACE4 "self->capacity = new_capacity;\n" + SPACE4 "}\n", VecT, VecT, SpanT, T)); + if (primitive) { + VecU8_append_vec(&res, VecU8_fmt( + SPACE4 "memcpy(self->buf + self->len, b.data, b.len * sizeof(%s));\n", T)); + } else { + VecU8_append_vec(&res, VecU8_fmt( + SPACE4 "for (size_t i = 0; i < b.len; i++)\n" + SPACE4 SPACE4 "self->buf[self->len + i] = %s_clone(&b.data[i]);\n", T)); + } + VecU8_append_span(&res, cstr( + SPACE4 "self->len = new_length;\n" + "}\n\n")); } - VecU8_append_span(&res, cstr( - SPACE4 "self->len = new_length;\n" - "}\n\n")); if (add_extended) { VecU8_append_vec(&res, VecU8_fmt( @@ -359,7 +387,7 @@ NODISCARD VecU8 generate_SpanT_VecT_trivmove_collab(SpanU8 T, bool primitive, bo return res; } -NODISCARD VecU8 generate_OptionT_struct_and_methods(SpanU8 T, bool primitive) { +NODISCARD VecU8 generate_OptionT_struct_and_methods(SpanU8 T, bool primitive, bool clonable) { VecU8 g_OptionT = VecU8_fmt("Option%s", T); SpanU8 OptionT = VecU8_to_span(&g_OptionT); @@ -398,12 +426,14 @@ NODISCARD VecU8 generate_OptionT_struct_and_methods(SpanU8 T, bool primitive) { SPACE4 "if (self.variant == Option_None)\n" SPACE4 SPACE4 "%s_drop(self.some);\n" "}\n\n", OptionT, OptionT, T)); - VecU8_append_vec(&res, VecU8_fmt( - "NODISCARD %s %s_clone(const %s* self) {\n" - SPACE4 "if (self->variant == Option_None)\n" - SPACE4 SPACE4 "return (%s) { .variant = Option_None };\n" - SPACE4 "return (%s){ .variant = Option_Some, .some = %s_clone(&self->some) };\n" - "}\n\n", OptionT, OptionT, OptionT, OptionT, OptionT, T)); + if (clonable) { + VecU8_append_vec(&res, VecU8_fmt( + "NODISCARD %s %s_clone(const %s* self) {\n" + SPACE4 "if (self->variant == Option_None)\n" + SPACE4 SPACE4 "return (%s) { .variant = Option_None };\n" + SPACE4 "return (%s){ .variant = Option_Some, .some = %s_clone(&self->some) };\n" + "}\n\n", OptionT, OptionT, OptionT, OptionT, OptionT, T)); + } } VecU8_drop(g_OptionT); @@ -418,6 +448,7 @@ NODISCARD VecU8 generate_OptionT_struct_and_methods(SpanU8 T, bool primitive) { typedef struct { bool t_integer; bool t_primitive; + bool t_clonable; bool vec; bool vec_extended; bool vec_equal; @@ -433,39 +464,10 @@ typedef struct { NODISCARD VecU8 generate_util_templates_instantiation(SpanU8 T, util_templates_instantiation_options op) { VecU8 res = VecU8_new(); - assert(op.t_primitive || !op.t_integer); - if (op.vec) { - VecU8_append_vec(&res, generate_VecT_struct_and_base_methods(T, op.t_primitive)); - } - if (op.vec_extended) { - assert(op.vec); - VecU8_append_vec(&res, generate_VecT_trivmove_extended_methods(T, op.t_primitive)); - } - if (op.vec_equal) { - assert(op.vec); - VecU8_append_vec(&res, generate_VecT_equal_method(T, op.t_integer)); - } - if (op.vec_new_of_size) { - assert(op.vec); - VecU8_append_vec(&res, generate_VecT_new_of_size_method(T)); - } - if (op.span) { - VecU8_append_vec(&res, generate_SpanT_struct_and_methods(T, op.t_integer, op.mut_span, false, op.span_extended, op.span_sort)); - } - if (op.collab_vec_span) { - assert(op.vec && op.span); - VecU8_append_vec(&res, generate_SpanT_VecT_trivmove_collab(T, op.t_primitive, op.mut_span, op.collab_vec_span_extended)); - } - if (op.option) { - VecU8_append_vec(&res, generate_OptionT_struct_and_methods(T, op.t_primitive)); - } - return res; -} - -/* You want bonus_ns to be cstr("").*/ -void generate_eve_header(SpanU8 layer, SpanU8 bonus_ns, SpanU8 T, util_templates_instantiation_options op) { if (op.t_integer) op.t_primitive = true; + if (op.t_primitive) + op.t_clonable = true; if (op.vec_extended) op.vec = true; if (op.vec_equal) @@ -483,6 +485,38 @@ void generate_eve_header(SpanU8 layer, SpanU8 bonus_ns, SpanU8 T, util_templates op.vec = true; } assert(op.vec || op.span || op.option); + assert(op.t_primitive || !op.t_integer); + assert(!op.t_primitive || op.t_clonable); + if (op.vec) { + VecU8_append_vec(&res, generate_VecT_struct_and_base_methods(T, op.t_primitive, op.t_clonable)); + } + if (op.vec_extended) { + assert(op.vec); + VecU8_append_vec(&res, generate_VecT_trivmove_extended_methods(T, op.t_primitive, op.t_clonable)); + } + if (op.vec_equal) { + assert(op.vec); + VecU8_append_vec(&res, generate_VecT_equal_method(T, op.t_integer)); + } + if (op.vec_new_of_size) { + assert(op.vec); + VecU8_append_vec(&res, generate_VecT_new_of_size_method(T)); + } + if (op.span) { + VecU8_append_vec(&res, generate_SpanT_struct_and_methods(T, op.t_integer, op.mut_span, false, op.span_extended, op.span_sort)); + } + if (op.collab_vec_span) { + assert(op.vec && op.span); + VecU8_append_vec(&res, generate_SpanT_VecT_trivmove_collab(T, op.t_primitive, op.t_clonable, op.mut_span, op.collab_vec_span_extended)); + } + if (op.option) { + VecU8_append_vec(&res, generate_OptionT_struct_and_methods(T, op.t_primitive, op.t_clonable)); + } + return res; +} + +/* You want bonus_ns to be cstr("").*/ +void generate_eve_header(SpanU8 layer, SpanU8 bonus_ns, SpanU8 T, util_templates_instantiation_options op) { VecU8 text = vcstr("/*Automatically generated file. Do not edit it */\n\n"); VecU8_append_vec(&text, generate_util_templates_instantiation(T, op)); VecU8 filename = VecU8_fmt("%s/eve/%s/""%s%s%s%s%s""%s%s.h", layer, bonus_ns, @@ -501,7 +535,13 @@ void generate_eve_span_garden_for_primitive(SpanU8 layer, SpanU8 ns, SpanU8 T, b }); } -void generate_eve_span_garden_for_non_primitive(SpanU8 layer, SpanU8 ns, SpanU8 T, bool with_vector, bool with_span) { +void generate_eve_span_garden_for_non_primitive_clonable(SpanU8 layer, SpanU8 ns, SpanU8 T, bool with_vector, bool with_span) { + generate_eve_header(layer, ns, T, (util_templates_instantiation_options){ + .t_clonable = true, .vec = with_vector, .span = with_span, .collab_vec_span = with_vector && with_span + }); +} + +void generate_eve_span_garden_for_non_primitive_non_clonable(SpanU8 layer, SpanU8 ns, SpanU8 T, bool with_vector, bool with_span) { generate_eve_header(layer, ns, T, (util_templates_instantiation_options){ .vec = with_vector, .span = with_span, .collab_vec_span = with_vector && with_span }); diff --git a/src/l1/core/util.h b/src/l1/core/util.h index ada33f3..00186b1 100644 --- a/src/l1/core/util.h +++ b/src/l1/core/util.h @@ -74,13 +74,6 @@ size_t Vec_get_new_capacity(size_t old_capacity, size_t new_length) { return old_capacity; } -// todo: rename span to MutSpan and ConstSpan to Span - -// #define SpanT_VecT_trivmove_COMPLETE_Definition(T) \ -// VecT_trivmove_struct_Definition(T) VecT_trivmove_method_Definition(T) \ -// SpanT_struct_Definition(T) SpanT_method_Definition(T) SpanT_VecT_method_Definition(T) \ -// VecT_trivmove_equal_method_Definition(T) - float pow2f(float x) { return x * x; } diff --git a/src/l1/system/pthread.h b/src/l1/system/pthread.h new file mode 100644 index 0000000..a17742b --- /dev/null +++ b/src/l1/system/pthread.h @@ -0,0 +1,32 @@ +#ifndef PROTOTYPE1_SRC_L1_SYSTEM_PTHREAD_H +#define PROTOTYPE1_SRC_L1_SYSTEM_PTHREAD_H + +#include "../core/util.h" +#include + +void safe_pthread_mutex_lock(pthread_mutex_t* mut) { + if (pthread_mutex_lock(mut)) + abortf("O_o\n"); +} + +void safe_pthread_mutex_unlock(pthread_mutex_t *mut) { + if (pthread_mutex_unlock(mut)) + abortf("O_o\n"); +} + +void safe_pthread_mutex_init(pthread_mutex_t* place, const pthread_mutexattr_t* attr) { + if (pthread_mutex_init(place, attr)) + abortf("pthread_mutex_init\n"); +} + +void safe_pthread_mutex_destroy(pthread_mutex_t* mut) { + if (pthread_mutex_destroy(mut)) + abortf("O_O\n"); +} + +void safe_pthread_join(pthread_t thread) { + pthread_join(thread, NULL); +} + + +#endif \ No newline at end of file diff --git a/src/l2/codegen/codegen.c b/src/l2/codegen/codegen.c index bf92f58..6fa3705 100644 --- a/src/l2/codegen/codegen.c +++ b/src/l2/codegen/codegen.c @@ -11,7 +11,7 @@ void eve_of_l2() { make_dir_nofail("l2/eve/r0"); /* Needed in r0_assets.h */ generate_eve_span_garden_for_primitive(cstr("l2"), cstr("r0"), cstr("GenericMeshVertex"), true, false); - generate_eve_span_garden_for_non_primitive(cstr("l2"), cstr("r0"), cstr("ModelInSceneTemplate"), true, false); + generate_eve_span_garden_for_non_primitive_clonable(cstr("l2"), cstr("r0"), cstr("ModelInSceneTemplate"), true, false); generate_eve_span_garden_for_primitive(cstr("l2"), cstr("r0"), cstr("GenericMeshInstance"), true, false); generate_eve_span_garden_for_primitive(cstr("l2"), cstr("r0"), cstr("ShinyMeshVertex"), true, false); generate_eve_span_garden_for_primitive(cstr("l2"), cstr("r0"), cstr("ShinyMeshInstance"), true, false); @@ -55,6 +55,12 @@ void eve_of_l2() { generate_eve_span_garden_for_primitive(cstr("l2"), cstr(""), cstr("VkBufferCopy"), true, false); // todo: move vectors of vulkan handlers to some other place, away from margaret header /* Needed in r0.c */ + make_dir_nofail("l2/eve/r2"); + /* Needed in r2 */ + generate_eve_header(cstr("l2"), cstr("r2"), cstr("BoxLizaSound"), + (util_templates_instantiation_options){ .vec = true}); + generate_eve_header(cstr("l2"), cstr("r2"), cstr("PlayingSound"), + (util_templates_instantiation_options){.vec_extended = true}); } void generate_Vec_cvec_header() { diff --git a/src/l2/margaret/time.h b/src/l2/margaret/time.h new file mode 100644 index 0000000..88cc900 --- /dev/null +++ b/src/l2/margaret/time.h @@ -0,0 +1,29 @@ +#ifndef PROTOTYPE1_SRC_L2_MARGARET_TIME_H +#define PROTOTYPE1_SRC_L2_MARGARET_TIME_H + +#include + +typedef struct timespec margaret_ns_time; + +margaret_ns_time margaret_clock_gettime_monotonic_raw() { + margaret_ns_time res; + int ret = clock_gettime(CLOCK_MONOTONIC_RAW, &res); + if (ret < 0) + abortf("clock_gettime"); + return res; +} + +int64_t margaret_ns_time_ns_diff(margaret_ns_time from, margaret_ns_time to) { + return (to.tv_sec - from.tv_sec) * 1000000000 + (to.tv_nsec - from.tv_nsec); +} + +float margaret_ns_time_sec_diff(margaret_ns_time from, margaret_ns_time to) { + return (float)margaret_ns_time_ns_diff(from, to) / 1e9f; +} + +float margaret_clock_monotonic_raw_diff(margaret_ns_time start) { + return (float)margaret_ns_time_ns_diff(start, margaret_clock_gettime_monotonic_raw()) / 1e9f; +} + + +#endif \ No newline at end of file diff --git a/src/l2/margaret/margaret.h b/src/l2/margaret/vulkan.h similarity index 98% rename from src/l2/margaret/margaret.h rename to src/l2/margaret/vulkan.h index 9b2b9f3..7a49d9b 100644 --- a/src/l2/margaret/margaret.h +++ b/src/l2/margaret/vulkan.h @@ -9,30 +9,8 @@ #include #include #include "../core/stringop.h" -#include #include "../../l1/system/fileio.h" - -typedef struct timespec margaret_ns_time; - -margaret_ns_time margaret_clock_gettime_monotonic_raw() { - margaret_ns_time res; - int ret = clock_gettime(CLOCK_MONOTONIC_RAW, &res); - if (ret < 0) - abortf("clock_gettime"); - return res; -} - -int64_t margaret_ns_time_ns_diff(margaret_ns_time from, margaret_ns_time to) { - return (to.tv_sec - from.tv_sec) * 1000000000 + (to.tv_nsec - from.tv_nsec); -} - -float margaret_ns_time_sec_diff(margaret_ns_time from, margaret_ns_time to) { - return (float)margaret_ns_time_ns_diff(from, to) / 1e9f; -} - -float margaret_clock_monotonic_raw_diff(margaret_ns_time start) { - return (float)margaret_ns_time_ns_diff(start, margaret_clock_gettime_monotonic_raw()) / 1e9f; -} +#include "time.h" // todo: rewrite margaret to use libwayland-client, and get rid of this fucking crap typedef XEvent Xlib_Event; diff --git a/src/l2/tests/r0/r0.c b/src/l2/tests/r0/r0.c index 490baa8..8362275 100644 --- a/src/l2/tests/r0/r0.c +++ b/src/l2/tests/r0/r0.c @@ -1,4 +1,4 @@ -#include "../../margaret/margaret.h" +#include "../../margaret/vulkan.h" #include "../../../../gen/l2/geom.h" #include #include "../../../l1/system/fileio.h" diff --git a/src/l2/tests/r1/r1.c b/src/l2/tests/r1/r1.c index c470bb3..6d9c0a5 100644 --- a/src/l2/tests/r1/r1.c +++ b/src/l2/tests/r1/r1.c @@ -104,7 +104,7 @@ typedef struct { int32_t width; int32_t height; bool closed; -} my_state; +} audio_thread_state_t; typedef struct { uint32_t* data; @@ -123,7 +123,7 @@ void draw_frame_h_rasterizer_cb(void* ug, S32 x, S32 y, vec4 attr) { g->data[y * g->width + x] = color_vec4_to_color_u32(attr); } -void draw_frame(my_state* state, uint32_t* data, int32_t width, int32_t height) { +void draw_frame(audio_thread_state_t* state, uint32_t* data, int32_t width, int32_t height) { for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { data[y * width + x] = 0xFF00FFFF; @@ -170,7 +170,7 @@ void durackaya_tochka_update(durackaya_tochka* dot, float dur, float width, floa } } -void update_state(my_state* state, uint32_t fl) { +void update_state(audio_thread_state_t* state, uint32_t fl) { const float width = (float)(state->width < MAX_BUFFER_WIDTH ? state->width : MAX_BUFFER_WIDTH); const float height = (float)(state->height < MAX_BUFFER_HEIGHT ? state->height : MAX_BUFFER_HEIGHT); float dur = (float)fl / 1000; @@ -180,7 +180,7 @@ void update_state(my_state* state, uint32_t fl) { } static void main_h_wl_buffer_release(void *data, struct wl_buffer *buffer) { - my_state* state = data; + audio_thread_state_t* state = data; for (int ij = 0; ij < SWAPCHAIN_SLOTS; ij++) { if (state->swapchain.used_buffers[ij] == buffer) { // printf("Buffer %p was released and destroyed\n", buffer); @@ -196,7 +196,7 @@ static const struct wl_buffer_listener main_h_wl_buffer_listener = { .release = main_h_wl_buffer_release, }; -int swapchain_take_slot(my_state *state) { +int swapchain_take_slot(audio_thread_state_t *state) { for (int ij = 0; ij < SWAPCHAIN_SLOTS; ij++) { if (!state->swapchain.used_buffers[ij]) { const int32_t width = state->width < MAX_BUFFER_WIDTH ? state->width : MAX_BUFFER_WIDTH; @@ -215,7 +215,7 @@ int swapchain_take_slot(my_state *state) { return -1; } -void try_drawing_frame(my_state *state){ +void try_drawing_frame(audio_thread_state_t *state){ if (!state->swapchain.want_to_draw) return; int ij = swapchain_take_slot(state); @@ -236,7 +236,7 @@ void try_drawing_frame(my_state *state){ } static void main_h_xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial){ - my_state *state = data; + audio_thread_state_t *state = data; printf("XDG surface configured!\n"); xdg_surface_ack_configure(xdg_surface, serial); // todo: synchronize with frame event @@ -247,7 +247,7 @@ static void main_h_xdg_surface_configure(void *data, struct xdg_surface *xdg_sur static void main_h_xdg_toplevel_configure( void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, struct wl_array *states ){ - my_state *state = data; + audio_thread_state_t *state = data; printf("XDG toplevel configured to (%d %d)\n", width, height); if (width <= 0 || height < 0) return; @@ -256,7 +256,7 @@ static void main_h_xdg_toplevel_configure( } static void main_h_xdg_toplevel_close(void *data, struct xdg_toplevel *toplevel){ - my_state *state = data; + audio_thread_state_t *state = data; state->closed = true; } @@ -283,7 +283,7 @@ static const struct xdg_wm_base_listener main_h_xdg_wm_base_listener = { static void main_h_wl_keyboard_keymap( void *data, struct wl_keyboard *wl_keyboard, uint32_t format, int32_t fd, uint32_t size ) { - my_state* state = data; + audio_thread_state_t* state = data; // todo: replace asserts with abortf assert(format == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1); char *map_shm = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); @@ -303,7 +303,7 @@ static void main_h_wl_keyboard_keymap( static void main_h_wl_keyboard_enter( void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys ) { - my_state* state = data; + audio_thread_state_t* state = data; printf("keyboard enter; keys pressed are:\n"); uint32_t *key; wl_array_for_each(key, keys) { @@ -322,14 +322,14 @@ static void main_h_wl_keyboard_enter( static void main_h_wl_keyboard_leave( void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface ) { - my_state* state = data; + audio_thread_state_t* state = data; memset(&state->first_0x80_keys, 0, sizeof(state->first_0x80_keys)); } static void main_h_wl_keyboard_key( void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t key_action ) { - my_state* state = data; + audio_thread_state_t* state = data; char buf1[128]; char buf2[128]; uint32_t actual_key = key + 8; @@ -350,7 +350,7 @@ static void main_h_wl_keyboard_modifiers( void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group ) { - my_state* state = data; + audio_thread_state_t* state = data; xkb_state_update_mask(state->xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group); } @@ -372,27 +372,27 @@ static void main_h_wl_pointer_enter( void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y ) { - my_state *state = data; + audio_thread_state_t *state = data; state->pointer_pos = (vec2){(float)surface_x / 256.f, (float)surface_y / 256.f}; } static void main_h_wl_pointer_leave( void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface ) { - my_state *state = data; + audio_thread_state_t *state = data; } static void main_h_wl_pointer_motion( void *data,struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y ) { - my_state *state = data; + audio_thread_state_t *state = data; state->pointer_pos = (vec2){(float)surface_x / 256.f, (float)surface_y / 256.f}; } static void main_h_wl_pointer_button( void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t btn_action ) { - my_state *state = data; + audio_thread_state_t *state = data; if (btn_action == WL_POINTER_BUTTON_STATE_PRESSED) { printf("button = %u\n", button); if (button == 0x110) { @@ -404,11 +404,11 @@ static void main_h_wl_pointer_button( static void main_h_wl_pointer_axis( void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value ) { - my_state *state = data; + audio_thread_state_t *state = data; } static void main_h_wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) { - my_state *state = data; + audio_thread_state_t *state = data; } const struct wl_pointer_listener main_h_wl_pointer_listener = { @@ -421,7 +421,7 @@ const struct wl_pointer_listener main_h_wl_pointer_listener = { }; static void main_h_wl_seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities) { - my_state* state = data; + audio_thread_state_t* state = data; if (capabilities & WL_SEAT_CAPABILITY_POINTER) { state->pointer = wl_seat_get_pointer(wl_seat); if (!state->pointer) @@ -448,7 +448,7 @@ static const struct wl_seat_listener main_h_wl_seat_listener = { static void main_h_wl_registry_global( void *data, struct wl_registry *wl_registry, uint32_t name, const char *interface, uint32_t version ) { - my_state *state = data; + audio_thread_state_t *state = data; if (strcmp(interface, wl_shm_interface.name) == 0) { state->wl_shm = wl_registry_bind(wl_registry, name, &wl_shm_interface, 1); if (!state->wl_shm) @@ -486,7 +486,7 @@ static const struct wl_registry_listener main_h_wl_registry_listener = { static const struct wl_callback_listener main_h_wl_surface_frame_listener; static void main_h_wl_surface_frame_done(void *data, struct wl_callback *cb, uint32_t time){ - my_state *state = data; + audio_thread_state_t *state = data; wl_callback_destroy(cb); // todo: when I add multiple surfaces, gonna need to think of something smarter state->wl_callback = wl_surface_frame(state->wl_surface); @@ -510,7 +510,7 @@ static const struct wl_callback_listener main_h_wl_surface_frame_listener = { int main() { - my_state state = { .width = 800, .height = 480 }; + audio_thread_state_t state = { .width = 800, .height = 480 }; state.v0 = (durackaya_tochka){.pos = {10, 10}, .speed = {100, 100}}; state.v1 = (durackaya_tochka){.pos = {100, 10}, .speed = {100, -50}}; state.v2 = (durackaya_tochka){.pos = {5, 330}, .speed = {-20, 170}}; diff --git a/src/l2/tests/r2/r2a.c b/src/l2/tests/r2/r2a.c new file mode 100644 index 0000000..80ea979 --- /dev/null +++ b/src/l2/tests/r2/r2a.c @@ -0,0 +1,331 @@ +#include +#include "../../../l1/core/util.h" +#include +#include +#include +#include "../../../l1/system/pthread.h" + +#define DEFAULT_RATE 44100 +#define DEFAULT_CHANNELS 2 +#define DEFAULT_VOLUME 0.1 + +/* [roundtrip] */ +typedef struct { + int pending; + struct pw_main_loop *loop; +} Roundtrip_H_GuestData; + +static void roundtrip_h_pw_core_done(void *data, uint32_t id, int seq) { + Roundtrip_H_GuestData *g = data; + + if (id == PW_ID_CORE && seq == g->pending) + pw_main_loop_quit(g->loop); +} + +static void roundtrip(struct pw_core *core, struct pw_main_loop *loop) { + int err; + static const struct pw_core_events core_events = { + PW_VERSION_CORE_EVENTS, + .done = roundtrip_h_pw_core_done, + }; + + Roundtrip_H_GuestData aboba = {.pending = pw_core_sync(core, PW_ID_CORE, 0), .loop = loop }; + struct spa_hook core_listener; + + pw_core_add_listener(core, &core_listener, &core_events, &aboba); + + if ((err = pw_main_loop_run(loop)) < 0) + printf("main_loop_run error:%d!\n", err); + + spa_hook_remove(&core_listener); +} +/* [roundtrip] */ + +static void main_h_pw_registry_global( + void *data, uint32_t id, uint32_t permissions, const char *type, uint32_t version, const struct spa_dict *props + ) { + printf("object: id:%u type:%s/%d\n", id, type, version); +} + +static const struct pw_registry_events main_h_pw_registry_listener = { + PW_VERSION_REGISTRY_EVENTS, + .global = main_h_pw_registry_global, +}; + +typedef struct { + double* buffer; + int buffer_size; + int i; + double decay; +} KaplusStrong; + +KaplusStrong KaplusStrong_new(double frequency) { + int buffer_size = (int)(DEFAULT_RATE / frequency); + double* buffer = safe_calloc(buffer_size, sizeof(double)); + return (KaplusStrong){buffer, buffer_size, 0, 0.999}; +} + +void KaplusStrong_ding(KaplusStrong* self) { + for (int i = 0; i < self->buffer_size; i++) { + /* random noise in [-1, 1] */ + self->buffer[i] = ((double)rand() / RAND_MAX) * 2.0 - 1.0; + } +} + +double KaplusStrong_next(KaplusStrong* self) { + int j = (self->i + 1) % self->buffer_size; + double A = self->buffer[self->i]; + double B = self->buffer[j]; + self->buffer[self->i] = self->decay * 0.5 * (A + B); + self->i = j; + return A; +} + +void KaplusStrong_drop(KaplusStrong self) { + free(self.buffer); +} + +typedef struct { + double freq; + int tones; + bool raise_first; + double* phase; + double* tone_c; + double c; +} Multitone; + +Multitone Multitone_new(double frequency) { + int tones = MAX_S32(1, (int)floor(DEFAULT_RATE / frequency) - 1); + double* phase = safe_calloc(tones, sizeof(double)); + double* tone_c = safe_calloc(tones, sizeof(double)); + double nc = 0; + for (int i = 0; i < tones; i++) + nc += exp(-(double)i / 16); + for (int i = 0; i < tones; i++) { + tone_c[i] = exp(-(double)i / 16) / nc; + } + // for (int i = 0; i < tones; i++) { + // tone_c[i] = 1. / (double)(i + 1); + // } + return (Multitone){.freq = frequency, .tones = tones, .phase = phase, .tone_c = tone_c}; +} + +void Multitone_ding(Multitone* self) { + self->raise_first = true; +} + +double Multitone_next(Multitone* self) { + if (self->raise_first) { + self->c += 0.01; + if (self->c >= 1) { + self->c = 1; + self->raise_first = false; + } + } else { + self->c *= 0.9999; + } + + double a = 0; + for (int t = 0; t < self->tones; t++) { + a += sin(self->phase[t]) * self->tone_c[t]; + self->phase[t] += M_PI * 2 * self->freq * (float)(t + 1) / DEFAULT_RATE; + if (self->phase[t] >= M_PI * 2) + self->phase[t] -= M_PI * 2; + } + return a * self->c; +} + +void Multitone_drop(Multitone self) { + free(self.phase); + free(self.tone_c); +} + +typedef struct { + bool stop; + bool note_ks; + bool note_mt; +} AudioThreadCommands; + +/* Can't be copied, can't be cloned (contains mutex) */ +typedef struct { + pthread_mutex_t mut; + AudioThreadCommands cmd; +} AudioThreadBridge; + +typedef struct { + struct pw_main_loop *pw_main_loop; + struct pw_context *pw_context; + struct pw_core *pw_core; + struct pw_registry *pw_registry; + struct spa_hook registry_listener_hook; + struct pw_stream *pw_stream; + struct spa_hook stream_listener_hook; + AudioThreadBridge* bridge; + + KaplusStrong ks; + Multitone mt; + double time_written; +} audio_thread_state_t; + +static void main_h_pw_stream_process(void *ug){ + audio_thread_state_t* state = ug; + safe_pthread_mutex_lock(&state->bridge->mut); + AudioThreadCommands cmd = state->bridge->cmd; + state->bridge->cmd = (AudioThreadCommands){ 0}; + safe_pthread_mutex_unlock(&state->bridge->mut); + if (cmd.stop) { + pw_main_loop_quit(state->pw_main_loop); + return; + } + if (cmd.note_ks) + KaplusStrong_ding(&state->ks); + if (cmd.note_mt) + Multitone_ding(&state->mt); + + struct pw_buffer *b = pw_stream_dequeue_buffer(state->pw_stream); + if (!b) { + pw_log_warn("out of buffers: %m"); + return; + } + + struct spa_buffer *buf = b->buffer; + int16_t *dst = buf->datas[0].data; + if (dst == NULL) + return; + + uint32_t stride = sizeof(int16_t) * DEFAULT_CHANNELS; + uint32_t n_frames = buf->datas[0].maxsize / stride; + if (b->requested) + n_frames = SPA_MIN(b->requested, n_frames); + + for (uint32_t i = 0; i < n_frames; i++) { + double a = 0; + a += KaplusStrong_next(&state->ks); + a += Multitone_next(&state->mt); + state->time_written += 1. / DEFAULT_RATE; + int16_t val = (int16_t)round(a * DEFAULT_VOLUME * 32767.0); + for (int c = 0; c < DEFAULT_CHANNELS; c++) + *dst++ = val; + } + + buf->datas[0].chunk->offset = 0; + buf->datas[0].chunk->stride = (int32_t)stride; + buf->datas[0].chunk->size = n_frames * stride; + + pw_stream_queue_buffer(state->pw_stream, b); + printf("Time written = %lf\n", state->time_written); +} + +const static struct pw_stream_events main_h_pw_stream_listener = { + PW_VERSION_STREAM_EVENTS, + .process = main_h_pw_stream_process +}; + +void AudioThreadBridge_init(AudioThreadBridge* place) { + safe_pthread_mutex_init(&place->mut, NULL); + memset(&place->cmd, 0, sizeof(AudioThreadCommands)); +} + +void AudioThreadBridge_destroy(AudioThreadBridge* resid) { + pthread_mutex_destroy(&resid->mut); +} + +/* Full of audio */ +void* audio_thread(void* ug) { + audio_thread_state_t state = { .ks = KaplusStrong_new(440), .mt = Multitone_new(440), .bridge = ug }; + + state.pw_main_loop = pw_main_loop_new(NULL /* properties */); + if (!state.pw_main_loop) + abortf("pw_main_loop_new"); + state.pw_context = pw_context_new(pw_main_loop_get_loop(state.pw_main_loop), NULL, 0); + if (!state.pw_context) + abortf("pw_context_new\n"); + state.pw_core = pw_context_connect(state.pw_context, NULL /* properties */, 0 /* user_data size */); + if (!state.pw_core) + abortf("pw_context_connect\n"); + state.pw_registry = pw_core_get_registry(state.pw_core, PW_VERSION_REGISTRY, 0 /* user_data size */); + if (!state.pw_registry) + abortf("pw_core_get_registry\n"); + + struct spa_hook registry_listener_hook; + pw_registry_add_listener(state.pw_registry, ®istry_listener_hook, &main_h_pw_registry_listener, NULL); + + roundtrip(state.pw_core, state.pw_main_loop); + + state.pw_stream = pw_stream_new(state.pw_core, "audio-src", pw_properties_new( + PW_KEY_MEDIA_TYPE, "Audio", PW_KEY_MEDIA_CATEGORY, "Playback", PW_KEY_MEDIA_ROLE, "Music", NULL)); + + struct spa_hook stream_listener_hook; + pw_stream_add_listener(state.pw_stream, &stream_listener_hook, &main_h_pw_stream_listener, &state); + + const struct spa_pod *stream_params[1]; + uint8_t pod_buffer[1024]; + struct spa_pod_builder stream_params_b = SPA_POD_BUILDER_INIT(pod_buffer, sizeof(pod_buffer)); + stream_params[0] = spa_format_audio_raw_build(&stream_params_b, SPA_PARAM_EnumFormat, + &SPA_AUDIO_INFO_RAW_INIT( + .format = SPA_AUDIO_FORMAT_S16, + .channels = DEFAULT_CHANNELS, + .rate = DEFAULT_RATE )); + + pw_stream_connect(state.pw_stream, + PW_DIRECTION_OUTPUT, + PW_ID_ANY, + PW_STREAM_FLAG_AUTOCONNECT | + PW_STREAM_FLAG_MAP_BUFFERS | + PW_STREAM_FLAG_RT_PROCESS, + stream_params, 1); + + pw_main_loop_run(state.pw_main_loop); + printf("Exited mainloop\n"); + + pw_stream_destroy(state.pw_stream); + KaplusStrong_drop(state.ks); + Multitone_drop(state.mt); + pw_proxy_destroy((struct pw_proxy*)state.pw_registry); + pw_core_disconnect(state.pw_core); + pw_context_destroy(state.pw_context); + pw_main_loop_destroy(state.pw_main_loop); + printf("Thread finished\n"); + return NULL; +} + +int main(int argc, char *argv[]) { + pw_init(NULL, NULL); + + AudioThreadBridge bridge; + AudioThreadBridge_init(&bridge); + pthread_t th; + if (pthread_create(&th, NULL, audio_thread, &bridge)) + abortf("pthread_create\n"); + + while (true) { + char line[50]; + if (!fgets(line, sizeof(line), stdin)) + break; + line[strcspn(line, "\n")] = '\0'; + if (strcmp(line, "1") == 0) { + safe_pthread_mutex_lock(&bridge.mut); + bridge.cmd.note_ks = true; + safe_pthread_mutex_unlock(&bridge.mut); + } else if (strcmp(line, "2") == 0) { + safe_pthread_mutex_lock(&bridge.mut); + bridge.cmd.note_mt = true; + safe_pthread_mutex_unlock(&bridge.mut); + } else if (strcmp(line, "q") == 0) { + safe_pthread_mutex_lock(&bridge.mut); + bridge.cmd.stop = true; + safe_pthread_mutex_unlock(&bridge.mut); + break; + } else { + printf("?\n"); + } + + } + + safe_pthread_join(th); + printf("The End!\n"); + AudioThreadBridge_destroy(&bridge); + + pw_deinit(); + return 0; +} diff --git a/src/l2/tests/r2/r2b.c b/src/l2/tests/r2/r2b.c new file mode 100644 index 0000000..b45a1f3 --- /dev/null +++ b/src/l2/tests/r2/r2b.c @@ -0,0 +1,331 @@ +#include +#include "../../../l1/core/util.h" +#include +#include +#include +#include "../../../l1/system/pthread.h" +#include "../../margaret/time.h" + +#define DEFAULT_RATE 44100 +#define DEFAULT_CHANNELS 2 +#define DEFAULT_VOLUME 0.1 + +typedef struct { + /* self (takes ownership) */ + void (*drop)(void*); + /* self, returns: pcm value */ + double (*next)(void*); + /* self, returns: duration in frames */ + size_t (*get_duration)(const void*); +} LizaSound_Table; + +typedef struct { + const void* r; + const LizaSound_Table* t; +} RefLizaSound; + +typedef struct { + void* r; + const LizaSound_Table* t; +} MutRefLizaSound; + +/* Existence of Box type implies that _drop method is virtual according to this trait */ +typedef struct { + void* m; /* Owns memory block r and object in it */ + const LizaSound_Table* t; +} BoxLizaSound; + + +typedef struct { + double* buffer; + int buffer_size; + int i; + double decay; + double planned_time; +} StringTwitchSound; + +StringTwitchSound StringTwitchSound_new(double frequency, double decay_time) { + int buffer_size = (int)(DEFAULT_RATE / frequency); + double* buffer = safe_calloc(buffer_size, sizeof(double)); + for (int i = 0; i < buffer_size; i++) { + /* random noise in [-1, 1] */ + buffer[i] = ((double)rand() / RAND_MAX) * 2.0 - 1.0; + } + double decay_factor = pow(0.01, 1 / (decay_time * frequency)); + return (StringTwitchSound){.buffer = buffer, .buffer_size = buffer_size, .i = 0, + .decay = decay_factor, .planned_time = decay_time}; +} + +void StringTwitchSound_drop(StringTwitchSound self) { + free(self.buffer); +} + +double StringTwitchSound_next(StringTwitchSound* self) { + int j = (self->i + 1) % self->buffer_size; + double A = self->buffer[self->i]; + double B = self->buffer[j]; + self->buffer[self->i] = self->decay * 0.5 * (A + B); + self->i = j; + return A; +} + +size_t StringTwitchSound_get_duration(const StringTwitchSound* self) { + return (size_t)ceil(self->planned_time * DEFAULT_RATE); +} + +void LizaSound_Table_StringTwitchSound_drop(void* g) { + StringTwitchSound_drop(*(StringTwitchSound*)g); +} +double LizaSound_Table_StringTwitchSound_next(void* g) { + return StringTwitchSound_next(g); +} +size_t LizaSound_Table_StringTwitchSound_get_duration(const void* g) { + return StringTwitchSound_get_duration(g); +} +const LizaSound_Table LizaSound_Table_StringTwitchSound = { + .drop = LizaSound_Table_StringTwitchSound_drop, + .next = LizaSound_Table_StringTwitchSound_next, + .get_duration = LizaSound_Table_StringTwitchSound_get_duration +}; + +BoxLizaSound BoxLizaSound_from_StringTwitchSound(StringTwitchSound obj) { + void* mem = safe_malloc(sizeof(StringTwitchSound)); + memcpy(mem, &obj, sizeof(StringTwitchSound)); + return (BoxLizaSound){.m = mem, .t = &LizaSound_Table_StringTwitchSound}; +} + +void BoxLizaSound_drop(BoxLizaSound self) { + self.t->drop(self.m); + free(self.m); +} + +#include "../../../../gen/l2/eve/r2/VecBoxLizaSound.h" + +typedef struct { + bool stop; + VecBoxLizaSound new_sounds; +} AudioThreadCommands; + +void AudioThreadCommands_drop(AudioThreadCommands self) { + VecBoxLizaSound_drop(self.new_sounds); +} + +/* Not movable and even so more it isn't trivially movable */ +typedef struct { + pthread_mutex_t mut; + AudioThreadCommands cmd; +} AudioThreadBridge; + +/* non-copyable trivially movable (because of BoxLizaSound field) */ +typedef struct { + BoxLizaSound gen; + size_t remaining_frames; +} PlayingSound; + +void PlayingSound_drop(PlayingSound self) { + BoxLizaSound_drop(self.gen); +} + +#include "../../../../gen/l2/eve/r2/PlayingSound.h" + +double fix_loud_sound(double a) { + const double L = 0.5; + const double b = (1 - L) / exp(-L / (1 - L)); + const double d = 1 / (1 - L); + if (a < -L) { + return - (1 - d * exp(b * a)); + } else if (a < L) { + return a; + } else { + return 1 - d * exp(-b * a); + } +} + +typedef struct { + struct pw_main_loop *pw_main_loop; + struct pw_context *pw_context; + struct pw_core *pw_core; + struct pw_registry *pw_registry; + struct spa_hook registry_listener_hook; + struct pw_stream *pw_stream; + struct spa_hook stream_listener_hook; + AudioThreadBridge* bridge; + + VecPlayingSound playing_sounds; + double time_written; +} audio_thread_state_t; + +static void main_h_pw_stream_process(void *ug){ + audio_thread_state_t* state = ug; + safe_pthread_mutex_lock(&state->bridge->mut); + AudioThreadCommands* incoming_cmd = &state->bridge->cmd; + if (incoming_cmd->stop) { + pw_main_loop_quit(state->pw_main_loop); + safe_pthread_mutex_unlock(&state->bridge->mut); + return; + } + for (size_t i = 0; i < incoming_cmd->new_sounds.len; i++) { + /* effectively moved out (will resize incoming_cmd->new_sounds to 0 soon) */ + BoxLizaSound snd = *VecBoxLizaSound_at(&incoming_cmd->new_sounds, i); + VecPlayingSound_append(&state->playing_sounds, (PlayingSound){ + .gen = snd, .remaining_frames = snd.t->get_duration(snd.m) + }); + printf("Appended note when wrote: %lf\n", state->time_written); + } + incoming_cmd->new_sounds.len = 0; /* We moved everything out */ + safe_pthread_mutex_unlock(&state->bridge->mut); + + struct pw_buffer *b = pw_stream_dequeue_buffer(state->pw_stream); + if (!b) { + pw_log_warn("out of buffers: %m"); + return; + } + struct spa_buffer *buf = b->buffer; + uint32_t stride = sizeof(int16_t) * DEFAULT_CHANNELS; + uint32_t n_frames = buf->datas[0].maxsize / stride; + if (b->requested) + n_frames = SPA_MIN(b->requested, n_frames); + + int16_t *dst = buf->datas[0].data; + if (dst == NULL) + return; + + for (uint32_t f = 0; f < n_frames; f++) { + double a = 0; + for (size_t i = 0; i < state->playing_sounds.len;) { + PlayingSound* snd = VecPlayingSound_mat(&state->playing_sounds, i); + if (snd->remaining_frames == 0) { + /* snd invalidated, but iteration continues */ + VecPlayingSound_unordered_pop_and_drop(&state->playing_sounds, i); + continue; + } + a += snd->gen.t->next(snd->gen.m); + snd->remaining_frames--; + i++; + } + int16_t val = (int16_t)round(fix_loud_sound(a) * DEFAULT_VOLUME * 32767.0); + for (int c = 0; c < DEFAULT_CHANNELS; c++) + *dst++ = val; + } + state->time_written += (double)n_frames / DEFAULT_RATE; + + + buf->datas[0].chunk->offset = 0; + buf->datas[0].chunk->stride = (int32_t)stride; + buf->datas[0].chunk->size = n_frames * stride; + + pw_stream_queue_buffer(state->pw_stream, b); + // printf("Time written = %lf\n", state->time_written); +} + +const static struct pw_stream_events main_h_pw_stream_listener = { + PW_VERSION_STREAM_EVENTS, + .process = main_h_pw_stream_process +}; + +void AudioThreadBridge_init(AudioThreadBridge* place) { + safe_pthread_mutex_init(&place->mut, NULL); + memset(&place->cmd, 0, sizeof(AudioThreadCommands)); +} + +void AudioThreadBridge_destroy(AudioThreadBridge* resid) { + AudioThreadCommands_drop(resid->cmd); + pthread_mutex_destroy(&resid->mut); +} + +/* Full of audio */ +void* audio_thread(void* ug) { + audio_thread_state_t state = { .playing_sounds = VecPlayingSound_new(), .bridge = ug }; + + state.pw_main_loop = pw_main_loop_new(NULL /* properties */); + if (!state.pw_main_loop) + abortf("pw_main_loop_new"); + state.pw_context = pw_context_new(pw_main_loop_get_loop(state.pw_main_loop), NULL, 0); + if (!state.pw_context) + abortf("pw_context_new\n"); + state.pw_core = pw_context_connect(state.pw_context, NULL /* properties */, 0 /* user_data size */); + if (!state.pw_core) + abortf("pw_context_connect\n"); + state.pw_registry = pw_core_get_registry(state.pw_core, PW_VERSION_REGISTRY, 0 /* user_data size */); + if (!state.pw_registry) + abortf("pw_core_get_registry\n"); + + state.pw_stream = pw_stream_new(state.pw_core, "audio-src", pw_properties_new( + PW_KEY_MEDIA_TYPE, "Audio", PW_KEY_MEDIA_CATEGORY, "Playback", PW_KEY_MEDIA_ROLE, "Music", NULL)); + + struct spa_hook stream_listener_hook; + pw_stream_add_listener(state.pw_stream, &stream_listener_hook, &main_h_pw_stream_listener, &state); + + const struct spa_pod *stream_params[1]; + uint8_t pod_buffer[1024]; + struct spa_pod_builder stream_params_b = SPA_POD_BUILDER_INIT(pod_buffer, sizeof(pod_buffer)); + stream_params[0] = spa_format_audio_raw_build(&stream_params_b, SPA_PARAM_EnumFormat, + &SPA_AUDIO_INFO_RAW_INIT( + .format = SPA_AUDIO_FORMAT_S16, + .channels = DEFAULT_CHANNELS, + .rate = DEFAULT_RATE )); + + pw_stream_connect(state.pw_stream, + PW_DIRECTION_OUTPUT, + PW_ID_ANY, + PW_STREAM_FLAG_AUTOCONNECT | + PW_STREAM_FLAG_MAP_BUFFERS | + PW_STREAM_FLAG_RT_PROCESS, + stream_params, 1); + + pw_main_loop_run(state.pw_main_loop); + printf("Exited mainloop\n"); + + pw_stream_destroy(state.pw_stream); + pw_proxy_destroy((struct pw_proxy*)state.pw_registry); + pw_core_disconnect(state.pw_core); + pw_context_destroy(state.pw_context); + pw_main_loop_destroy(state.pw_main_loop); + + VecPlayingSound_drop(state.playing_sounds); + return NULL; +} + +int main(int argc, char *argv[]) { + int ret; + pw_init(NULL, NULL); + + AudioThreadBridge bridge; + AudioThreadBridge_init(&bridge); + pthread_t th; + margaret_ns_time start = margaret_clock_gettime_monotonic_raw(); + if (pthread_create(&th, NULL, audio_thread, &bridge)) + abortf("pthread_create\n"); + printf("Press random key to get help\n"); + while (true) { + char line[50]; + if (!fgets(line, sizeof(line), stdin)) + break; + line[strcspn(line, "\n")] = '\0'; + if (strcmp(line, "1") == 0) { + safe_pthread_mutex_lock(&bridge.mut); + VecBoxLizaSound_append(&bridge.cmd.new_sounds, BoxLizaSound_from_StringTwitchSound(StringTwitchSound_new(440, 0.9))); + printf("Pressed 1 at %f\n", margaret_ns_time_sec_diff(start, margaret_clock_gettime_monotonic_raw())); + safe_pthread_mutex_unlock(&bridge.mut); + } else if (strcmp(line, "2") == 0) { + safe_pthread_mutex_lock(&bridge.mut); + VecBoxLizaSound_append(&bridge.cmd.new_sounds, BoxLizaSound_from_StringTwitchSound(StringTwitchSound_new(440, 9))); + safe_pthread_mutex_unlock(&bridge.mut); + } else if (strcmp(line, "q") == 0) { + safe_pthread_mutex_lock(&bridge.mut); + bridge.cmd.stop = true; + safe_pthread_mutex_unlock(&bridge.mut); + break; + } else { + printf("?\n"); + } + + } + + safe_pthread_join(th); + printf("The End!\n"); + AudioThreadBridge_destroy(&bridge); + + pw_deinit(); + return 0; +}