summaryrefslogtreecommitdiff
path: root/src/audio-impl-pw.cpp
diff options
context:
space:
mode:
authorJonathan Bradley <jcb@pikum.xyz>2025-07-14 16:35:32 -0400
committerJonathan Bradley <jcb@pikum.xyz>2025-07-14 16:35:32 -0400
commit7f0b0a59425321dcc880ddc0f4b479bce85e0bb0 (patch)
treec5c10d187faf26a248d859deac68ba83bbe26e97 /src/audio-impl-pw.cpp
parentf88ca0bc946bae086e02eacdc6c129f00e2e07e3 (diff)
pke: audio: process each tick, impl buffer copy
Diffstat (limited to 'src/audio-impl-pw.cpp')
-rw-r--r--src/audio-impl-pw.cpp238
1 files changed, 26 insertions, 212 deletions
diff --git a/src/audio-impl-pw.cpp b/src/audio-impl-pw.cpp
index 92acd8d..1bc2525 100644
--- a/src/audio-impl-pw.cpp
+++ b/src/audio-impl-pw.cpp
@@ -1,15 +1,10 @@
#include "audio-impl-pw.hpp"
-#include "audio-impl-shared.hpp"
-#include "asset-manager.hpp"
#include "audio-types.hpp"
#include "game-settings.hpp"
-#include "math-helpers.hpp"
#include "pipewire/keys.h"
#include "pipewire/thread-loop.h"
#include "pk.h"
-#include "vendor-glm-include.hpp"
-#include "window.hpp"
// see header file
#pragma GCC diagnostic push
@@ -114,7 +109,7 @@ void pke_audio_pw_init() {
spa_audio_info_raw spa_audio_info_raw_init{};
spa_audio_info_raw_init.flags = 0;
spa_audio_info_raw_init.format = SPA_AUDIO_FORMAT_F32;
- spa_audio_info_raw_init.rate = 48000;
+ spa_audio_info_raw_init.rate = PKE_AUDIO_BITRATE;
spa_audio_info_raw_init.channels = pke_audio_mstr.channel_count;
spa_audio_info_raw_init.position[0] = SPA_AUDIO_CHANNEL_FL;
spa_audio_info_raw_init.position[1] = SPA_AUDIO_CHANNEL_FR;
@@ -200,6 +195,10 @@ void pke_audio_pw_remap_outputs() {
const char *str;
uint32_t *out_ports = pk_new<uint32_t>(pke_audio_mstr.channel_count, pkeSettings.mem_bkt.game_transient);
uint32_t *in_ports = pk_new<uint32_t>(pke_audio_mstr.channel_count, pkeSettings.mem_bkt.game_transient);
+ for (i = 0; i < pke_audio_mstr.channel_count; ++i) {
+ out_ports[i] = 0;
+ in_ports[i] = 0;
+ }
pke_audio_mstr.mtx_buffer.lock();
pw_thread_loop_lock(pke_audio_pw.loop);
@@ -276,19 +275,8 @@ void on_pipewire_process(void *user_data) {
pw_buffer *b;
spa_buffer *buf;
int stride;
- uint8_t pc, pc2;
- uint32_t i, ii, c;
- int64_t n_frames, i_frame;
+ int64_t n_frames;
float *dst;
- float val, vol, vol2;
- float *spatial_volumes = nullptr;
- glm::vec3 listener_origin;
- glm::vec3 audio_dir = glm::vec3(0);
- glm::vec3 *spatial_normals = nullptr;
- pke_audio_fx_params_reverb *params_reverb;
- pke_audio_fx_params_delay *params_delay;
- pke_audio_fx_params_low_pass_filter *params_low_pass_filter;
- pk_mem_bucket_reset(pke_audio_mstr.bkt_transient);
if ((b = pw_stream_dequeue_buffer(pke_audio_pw.stream)) == NULL) {
fprintf(stderr, "[" __FILE__ "][on_pipewire_process] out of buffers");
@@ -301,64 +289,6 @@ void on_pipewire_process(void *user_data) {
return;
}
- spatial_volumes = pk_new<float>(pke_audio_mstr.channel_count, pke_audio_mstr.bkt_transient);
- spatial_normals = pk_new<glm::vec3>(pke_audio_mstr.channel_count, pke_audio_mstr.bkt_transient);
- params_reverb = pk_new<pke_audio_fx_params_reverb>(pke_audio_mstr.channel_count, pke_audio_mstr.bkt_transient);
- params_delay = pk_new<pke_audio_fx_params_delay>(pke_audio_mstr.channel_count, pke_audio_mstr.bkt_transient);
- params_low_pass_filter = pk_new<pke_audio_fx_params_low_pass_filter>(pke_audio_mstr.channel_count, pke_audio_mstr.bkt_transient);
-
- // calculate spatial_normals
- // TODO maybe don't use UBO
- // TODO project-defined?
- // TODO instanced audio manager? (easier to handle explicit stereo sources?)
- listener_origin = glm::inverse(UBO.model) * glm::vec4(0, 0, 0, 1);
- for (c = 0; c < pke_audio_mstr.channel_count; ++c) {
- switch (c) {
- case 0:
- // left-speaker
- if (pke_audio_mstr.channel_count > 2) {
- spatial_normals[c] = glm::normalize(glm::vec3(-1.f, 0.f, 1.f));
- } else {
- spatial_normals[c] = glm::normalize(glm::vec3(-1.f, 0.f, 0.f));
- }
- break;
- case 1:
- // right
- if (pke_audio_mstr.channel_count > 2) {
- spatial_normals[c] = glm::normalize(glm::vec3( 1.f, 0.f, 1.f));
- } else {
- spatial_normals[c] = glm::normalize(glm::vec3( 1.f, 0.f, 0.f));
- }
- break;
- case 2:
- // center
- spatial_normals[c] = glm::normalize(glm::vec3( 0.f, 0.f, 1.f));
- break;
- case 3:
- // subwoofer
- spatial_normals[c] = glm::normalize(glm::vec3( 0.f, 0.f, 0.f));
- break;
- case 4:
- // left surround
- spatial_normals[c] = glm::normalize(glm::vec3(-1.f, 0.f, 0.f));
- break;
- case 5:
- // right surround
- spatial_normals[c] = glm::normalize(glm::vec3( 1.f, 0.f, 0.f));
- break;
- case 6:
- // rear left surround
- spatial_normals[c] = glm::normalize(glm::vec3(-1.f, 0.f, -1.f));
- break;
- case 7:
- // rear right surround
- spatial_normals[c] = glm::normalize(glm::vec3( 1.f, 0.f, -1.f));
- break;
- default:
- break;
- }
- }
-
pke_audio_mstr.mtx_buffer.lock();
stride = sizeof(float) * pke_audio_mstr.channel_count;
@@ -367,7 +297,8 @@ void on_pipewire_process(void *user_data) {
if (b->requested) {
n_frames = PK_MIN((int64_t)b->requested, n_frames);
}
- // fprintf(stdout, "[pw] frame count: %li, requested: %lu\n", n_frames, b->requested);
+ // fprintf(stdout, "[pw] frame count available: %li, requested: %lu, ready: %li\n", n_frames, b->requested, pke_audio_mstr.buffer_frames);
+ n_frames = PK_MIN(n_frames, pke_audio_mstr.buffer_frames);
buf->datas[0].chunk->offset = 0;
buf->datas[0].chunk->stride = stride;
@@ -377,141 +308,24 @@ void on_pipewire_process(void *user_data) {
goto audio_done;
}
- for (i = 0; i < pke_audio_mstr.playing_objects.next; ++i) {
- pke_audio_obj *aobj = &pke_audio_mstr.playing_objects[i];
- const Asset *a = AM_Get(aobj->handle);
- vol = 1.0;
- vol *= pke_audio_mstr.master_volume;
- vol *= pke_audio_mstr.source_volumes[static_cast<pke_audio_source_T>(aobj->source)];
-
- for (pc = 0; pc < aobj->play_count; ++pc) {
- // TODO configurable
- // >= 5.0 meters is 100% volume
- // <=50.0 meters is 0% volume
- float distance_volume = glm::distance(aobj->position_source[pc], listener_origin);
- distance_volume = 1.0 - ((distance_volume - 5.0) / 50 - 5);
- distance_volume = glm::clamp(distance_volume, 0.f, 1.f);
-
- // calculate panning
- // TODO handle subwoofer explicitly
- float sum = 0.0;
- for (c = 0; c < pke_audio_mstr.channel_count; ++c) {
- audio_dir = glm::normalize(aobj->position_source[pc] - listener_origin);
- for (ii = 0; ii < 3; ++ii) {
- if (glm::isnan(audio_dir[ii])) {
- audio_dir = glm::vec3(0);
- spatial_volumes[c] = 1.f;
- break;
- }
- }
- float dot_dir = glm::dot(spatial_normals[c], audio_dir);
- if (!glm::isnan(dot_dir) && dot_dir < 0.0) {
- // fprintf(stderr, "[pw] dot: %f\n", dot_dir);
-
- // 20k max, 500 min
- params_low_pass_filter[c].cutoff_freq = log_interp(500.f, 20000.f, abs(dot_dir));
- } else {
- params_low_pass_filter[c].cutoff_freq = 0.f;
- }
- params_reverb[c].reverb_strength = 0.f; // TODO
- params_delay[c].delay_frames = 0; // TODO
- // spatial_volumes[c] = glm::clamp(dot_dir, 0.f, 1.f);
- spatial_volumes[c] = lerp(0.f, 1.f, dot_dir + 0.85f);
-
- if (isnan(spatial_volumes[c]) || spatial_volumes[c] == 0.0f) {
- /*
- fprintf(stderr, "[pw] NaN or 0: chan: %i, norm: %f,%f,%f, src: %f,%f,%f, origin: %f,%f,%f\n",
- c,
- audio_dir.x, audio_dir.y, audio_dir.z,
- aobj->position_source[pc].x, aobj->position_source[pc].y, aobj->position_source[pc].z,
- listener_origin.x, listener_origin.y, listener_origin.z
- );
- */
- spatial_volumes[c] = 1.0;
- }
-
- sum += spatial_volumes[c];
- }
- // normalize (TODO do we want this?)
- /*
- if (sum > 1.f) {
- for (c = 0; c < pke_audio_mstr.channel_count; ++c) {
- spatial_volumes[c] /= sum;
- }
- }
- */
-
- /*
- for (c = 0; c < pke_audio_mstr.channel_count; ++c) {
- fprintf(stderr, "[pw] obj: %i, chan: %i vol: %f, spatial: %f, dist: %f\n",
- i, c, vol, spatial_volumes[c], distance_volume
- );
- }
- */
-
- for (i_frame = 0; i_frame < n_frames; ++i_frame) {
- dst = (float*)buf->datas[0].data;
- dst += (i_frame * (uint64_t)pke_audio_mstr.channel_count); // advance
-
- for (c = 0; c < pke_audio_mstr.channel_count; ++c) {
- if (i == 0 && pc == 0) { *dst = 0.f; } // clear buffer as we go
-
- vol2 = vol;
- if (PK_HAS_FLAG(aobj->flags[pc], pke_audio_flag_pos_spatial)) {
- vol2 *= (spatial_volumes[c] * distance_volume);
- }
- if (vol2 <= 0.0) {
- // fprintf(stderr, "[pw] chan: %i vol2 is <= 0.0\n", c);
- dst += 1;
- continue;
- }
-
- /*
- if (isnan(vol2)) {
- fprintf(stderr, "[pw] vol2 is NaN\n");
- }
- if (vol2 == 0.0) {
- fprintf(stderr, "[pw] vol2 is 0, %f, %f, %f\n", pke_audio_mstr.master_volume, pke_audio_mstr.source_volumes[c], spatial_volumes[c]);
- }
- */
-
- // val = ((float*)a->ptr)[aobj->play_heads[pc]]; // val is read inside fx_low_pass_filter
- val = pke_audio_fx_low_pass_filter((float*)a->ptr, a->size / sizeof(float), (uint32_t)aobj->play_heads[pc], &params_low_pass_filter[pc]);
- val += pke_audio_fx_reverb((float*)a->ptr, a->size / sizeof(float), (uint32_t)aobj->play_heads[pc], &params_reverb[pc]);
- val += pke_audio_fx_delay((float*)a->ptr, a->size / sizeof(float), (uint32_t)aobj->play_heads[pc], &params_delay[pc]);
- *dst += val * vol2;
- /*
- if (isnan(*dst)) {
- fprintf(stderr, "[pw] *dst is NaN\n");
- }
- */
- dst += 1;
-
- }
-
- // TODO type-specific attributes for assets so we can pre-calculate this or just KNOW the frame length ahead of time
- aobj->play_heads[pc] += 1;
- if (aobj->play_heads[pc] >= a->size / sizeof(float)) {
- for (pc2 = 0; pc2 < aobj->play_count-1; ++pc2) {
- aobj->flags[pc2] = aobj->flags[pc2+1];
- aobj->play_heads[pc2] = aobj->play_heads[pc2+1];
- aobj->position_source[pc2] = aobj->position_source[pc2+1];
- }
- pc -= 1;
- aobj->play_count -= 1;
- break;
- }
- }
- }
- AM_Release(aobj->handle);
- if (aobj->play_count == 0) {
- pk_arr_remove_at(&pke_audio_mstr.playing_objects, i);
- i -= 1;
- }
- }
-
- if (pke_audio_mstr.playing_objects.next == 0) {
- memset(dst, 0, sizeof(float) * n_frames * pke_audio_mstr.channel_count);
+ memcpy(dst, pke_audio_mstr.buffer, buf->datas[0].chunk->size);
+
+ if (n_frames > 0 && n_frames < pke_audio_mstr.buffer_frames) {
+ // TODO PERF I think if I always had two buffers allocated
+ // I could alternate between them for better perf.
+ float* new_buffer = pk_new<float>(PKE_AUDIO_BUFFER_FRAMES * pke_audio_mstr.channel_count * sizeof(float), pke_audio_mstr.bkt);
+ memcpy(
+ new_buffer,
+ pke_audio_mstr.buffer + (pke_audio_mstr.channel_count * n_frames),
+ stride * (pke_audio_mstr.buffer_frames - n_frames)
+ );
+ pk_delete<float>(pke_audio_mstr.buffer, PKE_AUDIO_BUFFER_FRAMES * pke_audio_mstr.channel_count * sizeof(float), pke_audio_mstr.bkt);
+ pke_audio_mstr.buffer = new_buffer;
+
+ // fprintf(stdout, "[pw] shift buffer. buffer_frames before: %li, processed_frames: %li, after: %li\n", pke_audio_mstr.buffer_frames, n_frames, pke_audio_mstr.buffer_frames - n_frames);
+ pke_audio_mstr.buffer_frames -= n_frames;
+ } else {
+ pke_audio_mstr.buffer_frames = 0;
}
audio_done: