fix: use continuous sample API for audio flow in ndiin

Audio flows in MXL use mxlFlowWriterOpenSamples/CommitSamples, not
the grain API (mxlFlowWriterOpenGrain is for discrete video flows).
Using the grain API silently did nothing — hence "last published index: 0".

MXL audio is float32 planar: each channel occupies its own ring buffer
region accessed at base.fragments[*].pointer + c * slice.stride.
Two fragments handle ring buffer wraparound. No int32 conversion needed.

Also log continuous.channelCount/bufferLength instead of the discrete
sliceSizes field (wrong union member for audio).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
JohannesItten
2026-07-01 12:56:55 +03:00
parent 72a554a172
commit aa39a821db
+20 -17
View File
@@ -1,4 +1,3 @@
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
@@ -75,7 +74,8 @@ class NDIInNode : public dmf::NodeBase {
log("audio mxlCreateFlowWriter failed (status=%d) — continuing without audio", ast);
has_audio = false;
} else {
log("audio grain=%u B", audio_cfg.discrete.sliceSizes[0]);
log("audio channels=%u buffer=%u samples",
audio_cfg.continuous.channelCount, audio_cfg.continuous.bufferLength);
}
}
@@ -104,22 +104,25 @@ class NDIInNode : public dmf::NodeBase {
if (kind == dmf::NDIReceiver::FrameKind::Video) {
have_video = true;
} else if (kind == dmf::NDIReceiver::FrameKind::Audio && has_audio) {
mxlGrainInfo grain{};
uint8_t* buf = nullptr;
if (mxlFlowWriterOpenGrain(audio_writer, audio_index, &grain, &buf) == MXL_STATUS_OK) {
// Convert float32 planar → int32 interleaved PCM
const int audio_grain_size = audio_cfg.discrete.sliceSizes[0];
const int capacity = audio_grain_size / (channels * static_cast<int>(sizeof(int32_t)));
const int to_write = std::min(audio_info.samples, capacity);
auto* out = reinterpret_cast<int32_t*>(buf);
for (int s = 0; s < to_write; ++s)
for (int c = 0; c < audio_info.channels; ++c) {
float f = audio_buf[c * audio_info.channel_stride + s];
f = std::max(-1.0f, std::min(1.0f, f));
*out++ = static_cast<int32_t>(f * 2147483647.0f);
mxlMutableWrappedMultiBufferSlice slice{};
if (mxlFlowWriterOpenSamples(audio_writer, audio_index,
static_cast<size_t>(audio_info.samples), &slice) == MXL_STATUS_OK) {
// MXL audio is float32 planar: each channel occupies its own ring buffer
// region, accessed at base + c * stride. Fragments handle ring wraparound.
const size_t frag0 = slice.base.fragments[0].size / sizeof(float);
const size_t frag1 = slice.base.fragments[1].size / sizeof(float);
for (int c = 0; c < audio_info.channels; ++c) {
const float* src = &audio_buf[c * audio_info.channel_stride];
auto* dst0 = reinterpret_cast<float*>(
static_cast<uint8_t*>(slice.base.fragments[0].pointer) + c * slice.stride);
std::memcpy(dst0, src, frag0 * sizeof(float));
if (frag1 > 0) {
auto* dst1 = reinterpret_cast<float*>(
static_cast<uint8_t*>(slice.base.fragments[1].pointer) + c * slice.stride);
std::memcpy(dst1, src + frag0, frag1 * sizeof(float));
}
grain.validSlices = grain.totalSlices;
mxlFlowWriterCommitGrain(audio_writer, &grain);
}
mxlFlowWriterCommitSamples(audio_writer);
}
audio_index += audio_info.samples;
}