Played with pipewire a little (still have no idea how to remove latency). Added t_clonable trait to util templates

This commit is contained in:
Андреев Григорий 2025-08-24 17:29:45 +03:00
parent 44a9389bae
commit 555712a19d
12 changed files with 909 additions and 145 deletions

View File

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

View File

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

View File

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

View File

@ -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;
}

32
src/l1/system/pthread.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef PROTOTYPE1_SRC_L1_SYSTEM_PTHREAD_H
#define PROTOTYPE1_SRC_L1_SYSTEM_PTHREAD_H
#include "../core/util.h"
#include <pthread.h>
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

View File

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

29
src/l2/margaret/time.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef PROTOTYPE1_SRC_L2_MARGARET_TIME_H
#define PROTOTYPE1_SRC_L2_MARGARET_TIME_H
#include <time.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;
}
#endif

View File

@ -9,30 +9,8 @@
#include <vulkan/vulkan.h>
#include <vulkan/vulkan_xlib.h>
#include "../core/stringop.h"
#include <time.h>
#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;

View File

@ -1,4 +1,4 @@
#include "../../margaret/margaret.h"
#include "../../margaret/vulkan.h"
#include "../../../../gen/l2/geom.h"
#include <math.h>
#include "../../../l1/system/fileio.h"

View File

@ -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}};

331
src/l2/tests/r2/r2a.c Normal file
View File

@ -0,0 +1,331 @@
#include <pipewire/pipewire.h>
#include "../../../l1/core/util.h"
#include <spa/param/audio/format-utils.h>
#include <math.h>
#include <stdio.h>
#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, &registry_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;
}

331
src/l2/tests/r2/r2b.c Normal file
View File

@ -0,0 +1,331 @@
#include <pipewire/pipewire.h>
#include "../../../l1/core/util.h"
#include <spa/param/audio/format-utils.h>
#include <math.h>
#include <stdio.h>
#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<Trait> 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;
}