diff options
| author | Jonathan Bradley <jcb@pikum.xyz> | 2025-07-14 16:35:32 -0400 |
|---|---|---|
| committer | Jonathan Bradley <jcb@pikum.xyz> | 2025-07-14 16:35:32 -0400 |
| commit | 7f0b0a59425321dcc880ddc0f4b479bce85e0bb0 (patch) | |
| tree | c5c10d187faf26a248d859deac68ba83bbe26e97 /src/audio-impl-pw.cpp | |
| parent | f88ca0bc946bae086e02eacdc6c129f00e2e07e3 (diff) | |
pke: audio: process each tick, impl buffer copy
Diffstat (limited to 'src/audio-impl-pw.cpp')
| -rw-r--r-- | src/audio-impl-pw.cpp | 238 |
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], ¶ms_low_pass_filter[pc]); - val += pke_audio_fx_reverb((float*)a->ptr, a->size / sizeof(float), (uint32_t)aobj->play_heads[pc], ¶ms_reverb[pc]); - val += pke_audio_fx_delay((float*)a->ptr, a->size / sizeof(float), (uint32_t)aobj->play_heads[pc], ¶ms_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: |
