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:
+20
-17
@@ -1,4 +1,3 @@
|
|||||||
#include <algorithm>
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -75,7 +74,8 @@ class NDIInNode : public dmf::NodeBase {
|
|||||||
log("audio mxlCreateFlowWriter failed (status=%d) — continuing without audio", ast);
|
log("audio mxlCreateFlowWriter failed (status=%d) — continuing without audio", ast);
|
||||||
has_audio = false;
|
has_audio = false;
|
||||||
} else {
|
} 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) {
|
if (kind == dmf::NDIReceiver::FrameKind::Video) {
|
||||||
have_video = true;
|
have_video = true;
|
||||||
} else if (kind == dmf::NDIReceiver::FrameKind::Audio && has_audio) {
|
} else if (kind == dmf::NDIReceiver::FrameKind::Audio && has_audio) {
|
||||||
mxlGrainInfo grain{};
|
mxlMutableWrappedMultiBufferSlice slice{};
|
||||||
uint8_t* buf = nullptr;
|
if (mxlFlowWriterOpenSamples(audio_writer, audio_index,
|
||||||
if (mxlFlowWriterOpenGrain(audio_writer, audio_index, &grain, &buf) == MXL_STATUS_OK) {
|
static_cast<size_t>(audio_info.samples), &slice) == MXL_STATUS_OK) {
|
||||||
// Convert float32 planar → int32 interleaved PCM
|
// MXL audio is float32 planar: each channel occupies its own ring buffer
|
||||||
const int audio_grain_size = audio_cfg.discrete.sliceSizes[0];
|
// region, accessed at base + c * stride. Fragments handle ring wraparound.
|
||||||
const int capacity = audio_grain_size / (channels * static_cast<int>(sizeof(int32_t)));
|
const size_t frag0 = slice.base.fragments[0].size / sizeof(float);
|
||||||
const int to_write = std::min(audio_info.samples, capacity);
|
const size_t frag1 = slice.base.fragments[1].size / sizeof(float);
|
||||||
auto* out = reinterpret_cast<int32_t*>(buf);
|
for (int c = 0; c < audio_info.channels; ++c) {
|
||||||
for (int s = 0; s < to_write; ++s)
|
const float* src = &audio_buf[c * audio_info.channel_stride];
|
||||||
for (int c = 0; c < audio_info.channels; ++c) {
|
auto* dst0 = reinterpret_cast<float*>(
|
||||||
float f = audio_buf[c * audio_info.channel_stride + s];
|
static_cast<uint8_t*>(slice.base.fragments[0].pointer) + c * slice.stride);
|
||||||
f = std::max(-1.0f, std::min(1.0f, f));
|
std::memcpy(dst0, src, frag0 * sizeof(float));
|
||||||
*out++ = static_cast<int32_t>(f * 2147483647.0f);
|
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;
|
audio_index += audio_info.samples;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user