summaryrefslogtreecommitdiff
path: root/src/audio-impl-pw.cpp
diff options
context:
space:
mode:
authorJonathan Bradley <jcb@pikum.xyz>2025-07-08 18:48:19 -0400
committerJonathan Bradley <jcb@pikum.xyz>2025-07-08 18:48:19 -0400
commite314577d9c775bd189e9494e7700e2e04cf79c21 (patch)
tree6fae0c3164a3ed755c795d42add21e7ebf8ec74b /src/audio-impl-pw.cpp
parent512df40330e1409516b654857ca031fac19927b6 (diff)
pke: spatial audio first-pass
Diffstat (limited to 'src/audio-impl-pw.cpp')
-rw-r--r--src/audio-impl-pw.cpp73
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");