diff options
| author | Jonathan Bradley <jcb@pikum.xyz> | 2025-07-08 18:48:19 -0400 |
|---|---|---|
| committer | Jonathan Bradley <jcb@pikum.xyz> | 2025-07-08 18:48:19 -0400 |
| commit | e314577d9c775bd189e9494e7700e2e04cf79c21 (patch) | |
| tree | 6fae0c3164a3ed755c795d42add21e7ebf8ec74b /src | |
| parent | 512df40330e1409516b654857ca031fac19927b6 (diff) | |
pke: spatial audio first-pass
Diffstat (limited to 'src')
| -rw-r--r-- | src/audio-impl-pw.cpp | 73 |
1 files changed, 50 insertions, 23 deletions
diff --git a/src/audio-impl-pw.cpp b/src/audio-impl-pw.cpp index 53953ba..4f5f4cf 100644 --- a/src/audio-impl-pw.cpp +++ b/src/audio-impl-pw.cpp @@ -275,14 +275,13 @@ void on_pipewire_process(void *user_data) { spa_buffer *buf; int stride; uint8_t pc, pc2; - uint32_t i, c; + uint32_t i, ii, c; uint64_t n_frames, i_frame; float *dst; - float val, vol; + float val, vol, vol2; float *spatial_volumes = nullptr; glm::vec3 listener_origin; - glm::vec3 relative_audio_pos = glm::vec3(0); - glm::vec3 *spatial_sources = nullptr; + glm::vec3 audio_dir = glm::vec3(0); glm::vec3 *spatial_normals = nullptr; pk_mem_bucket_reset(pke_audio_mstr.bkt_transient); @@ -304,10 +303,9 @@ void on_pipewire_process(void *user_data) { } spatial_volumes = pk_new<float>(pke_audio_mstr.channel_count, pke_audio_mstr.bkt_transient); - spatial_sources = pk_new<glm::vec3>(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); - // calculate spatial_sources + // calculate spatial_normals // TODO maybe don't use UBO // TODO project-defined? // TODO instanced audio manager? (easier to handle explicit stereo sources?) @@ -315,12 +313,12 @@ void on_pipewire_process(void *user_data) { for (c = 0; c < pke_audio_mstr.channel_count; ++c) { switch (c) { case 0: - // left-facing-speaker + // left-speaker spatial_normals[c] = glm::normalize(glm::vec3(-1.f, 0.f, 1.f)); break; case 1: // right - spatial_normals[c] = glm::normalize(glm::vec3(-1.f, 0.f, 1.f)); + spatial_normals[c] = glm::normalize(glm::vec3( 1.f, 0.f, 1.f)); break; case 2: // center @@ -364,20 +362,36 @@ void on_pipewire_process(void *user_data) { 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) { - if (aobj->position_source[pc] != glm::vec3(0)) { - relative_audio_pos = glm::normalize(aobj->position_source[pc] - listener_origin); + 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; + } + } + if (audio_dir != glm::vec3(0)) { + float dot_dir = glm::dot(spatial_normals[c], audio_dir); + // fprintf(stderr, "[pw] dot: %f\n", dot_dir); + spatial_volumes[c] = glm::clamp(dot_dir, 0.f, 1.f); } - spatial_volumes[c] = glm::clamp(glm::dot(relative_audio_pos, spatial_sources[c]), 0.f, 1.f); - if (isnan(spatial_volumes[c])) { + if (isnan(spatial_volumes[c]) || spatial_volumes[c] == 0.0f) { /* - fprintf(stderr, "[pw] NaN: norm: %f,%f,%f, src: %f,%f,%f, origin: %f,%f,%f\n", - relative_audio_pos.x, relative_audio_pos.y, relative_audio_pos.z, + 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 ); @@ -387,39 +401,52 @@ void on_pipewire_process(void *user_data) { 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 * pke_audio_mstr.channel_count); // advance + 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)) { - vol *= spatial_volumes[c]; + vol2 *= (spatial_volumes[c] * distance_volume); } - if (vol <= 0.0) { + if (vol2 <= 0.0) { + // fprintf(stderr, "[pw] vol2 is <= 0.0\n"); dst += 1; continue; } val = ((float*)a->ptr)[aobj->play_heads[pc]]; - // vol = PK_CLAMP(vol, 0.0, 0.5); + // vol2 = PK_CLAMP(vol2, 0.0, 0.5); /* - if (isnan(vol)) { - fprintf(stderr, "[pw] vol is NaN\n"); + if (isnan(vol2)) { + fprintf(stderr, "[pw] vol2 is NaN\n"); } - if (vol == 0.0) { - fprintf(stderr, "[pw] vol is 0, %f, %f, %f\n", pke_audio_mstr.master_volume, pke_audio_mstr.source_volumes[c], spatial_volumes[c]); + 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]); } */ - *dst += val * vol; + *dst += val * vol2; /* if (isnan(*dst)) { fprintf(stderr, "[pw] *dst is NaN\n"); |
