MCPcopy Create free account
hub / github.com/aethersdr/AetherSDR / loadPipeSource

Method loadPipeSource

src/core/PipeWireAudioBridge.cpp:231–293  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

229}
230
231bool PipeWireAudioBridge::loadPipeSource(int index)
232{
233 // FIFO lives in the per-user XDG runtime directory with mode 0600.
234 // Pre-fix code used /tmp/aethersdr-dax-%1.pipe with mode 0666, which
235 // any local user could read (operator RX audio) or write (operator
236 // TX audio with the licensed callsign — an FCC liability).
237 // See GHSA-x8xf-4g5v-ppf9.
238 const QString fifoDir = daxFifoDir();
239 if (fifoDir.isEmpty()) return false;
240 auto pipePath = QStringLiteral("%1/dax-%2.pipe").arg(fifoDir).arg(index + 1);
241 auto sourceName = QStringLiteral("aethersdr-dax-%1").arg(index + 1);
242 auto sourceDesc = QStringLiteral("AetherSDR DAX %1").arg(index + 1);
243
244 if (!makeOwnedFifo(pipePath)) return false;
245
246 // Load PulseAudio pipe-source module.
247 // Format/rate match the PipeWire graph (48 kHz float32) so PipeWire does
248 // not insert a resampler on this node — that resampler is the dominant
249 // remaining latency source after pipe_size was reduced (see issue #1008).
250 // node.latency=256/48000 (~5.3 ms quantum) tells PipeWire to negotiate a
251 // small quantum for this node instead of falling back to clock.quantum-limit.
252 uint32_t modIdx = runPactl({
253 "load-module", "module-pipe-source",
254 QStringLiteral("file=%1").arg(pipePath),
255 QStringLiteral("source_name=%1").arg(sourceName),
256 // Single-quoted: the pipewire-pulse pulse-module argument parser splits
257 // the property value on unescaped whitespace, so unquoted the space in
258 // "AetherSDR DAX N" truncates device.description to "AetherSDR" and
259 // orphans node.latency. node.description is derived from
260 // device.description, so this also labels the node in qpwgraph / JACK.
261 QStringLiteral("source_properties='device.description=\"%1\" node.latency=256/48000'").arg(sourceDesc),
262 QStringLiteral("format=float32le"),
263 QStringLiteral("rate=%1").arg(PIPE_RATE),
264 QStringLiteral("channels=%1").arg(PIPE_CHANNELS),
265 });
266
267 if (modIdx == 0) {
268 ::unlink(pipePath.toUtf8().constData());
269 return false;
270 }
271
272 // Open the pipe for writing (non-blocking to avoid hanging if no reader)
273 int fd = ::open(pipePath.toUtf8().constData(), O_WRONLY | O_NONBLOCK);
274 if (fd < 0) {
275 qCWarning(lcDax) << "PipeWireAudioBridge: open pipe failed:" << strerror(errno);
276 runPactl({"unload-module", QString::number(modIdx)});
277 ::unlink(pipePath.toUtf8().constData());
278 return false;
279 }
280
281 // Cap kernel FIFO capacity so back-pressure surfaces quickly instead of
282 // accumulating ~1.3 s of audio in the default 64 KB pipe buffer.
283 if (::fcntl(fd, F_SETPIPE_SZ, PIPE_KERNEL_BUF) < 0) {
284 qCDebug(lcDax) << "PipeWireAudioBridge: F_SETPIPE_SZ failed (non-fatal):" << strerror(errno);
285 }
286
287 m_rx[index].fd = fd;
288 m_rx[index].moduleIndex = modIdx;

Callers

nothing calls this directly

Calls 4

daxFifoDirFunction · 0.85
runPactlFunction · 0.85
makeOwnedFifoFunction · 0.70
isEmptyMethod · 0.45

Tested by

no test coverage detected