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

Method startPlayback

src/core/ClientPuduMonitor.cpp:181–354  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

179}
180
181void ClientPuduMonitor::startPlayback()
182{
183 // Emit on every failure path so the RX mute installed at record-start
184 // (or a prior manual-playback attempt) is always lifted. The m_playing
185 // guard below is the one exception — if we are already playing the mute
186 // is legitimately held and must not be dropped.
187 auto bail = [this]() {
188 emit muteRxRequested(false);
189 emit playbackStopped();
190 };
191
192 if (m_playing) return;
193 if (m_recordedBytes <= 0) { bail(); return; }
194
195 // ── Pick an output device + format ─────────────────────────────
196 // int16 stereo at the sink's native rate. Try 24 kHz first (zero-
197 // resample fast path — typical on Linux); fall back to 48 kHz
198 // (typical on macOS and Windows) with a one-shot r8brain upsample
199 // of the whole captured buffer up-front.
200 //
201 // Prefer the device the user picked in Radio Settings > Audio
202 // (m_outputDevice, seeded by MainWindow from AudioEngine). Only
203 // accept it if it is still present in the live audioOutputs() list
204 // — a hotplug/unplug between selection and now would otherwise
205 // strand us on a stale handle. Mirrors AudioEngine::startSidetoneStream().
206 QAudioDevice dev = QMediaDevices::defaultAudioOutput();
207 if (!m_outputDevice.isNull()) {
208 const auto outputs = QMediaDevices::audioOutputs();
209 for (const auto& d : outputs) {
210 if (d.id() == m_outputDevice.id()) { dev = d; break; }
211 }
212 }
213 if (dev.isNull()) {
214 AudioSummaryLogger::OpenFailureSummary failure;
215 failure.path = QStringLiteral("Aetherial monitor playback");
216 failure.backend = QStringLiteral("QAudioSink");
217 failure.deviceDescription = QStringLiteral("Unavailable");
218 failure.attemptedFormats = QStringLiteral("system default output");
219 failure.failureReason = QStringLiteral("no audio output device");
220 AudioSummaryLogger::logOpenFailure(failure);
221 bail(); return;
222 }
223
224 // Negotiate the playback format via the shared factory (#3306, Phase 6b).
225 // The monitor holds recorded Int16, so prefer Int16 (no conversion on a
226 // normal device) and fall back to Float for Float-only WASAPI mixers — the
227 // Int16->Float conversion below handles that case (#3231). The factory
228 // supplies, in one place, the per-OS preferred rate (Win/Mac 48k to dodge
229 // the WASAPI 24k resampler artifacts #2120 — same policy as the RX sink;
230 // Linux native 24k) plus the 44.1k and preferredFormat fallbacks the old
231 // hand-rolled ladder enumerated by hand. We walk it with isFormatSupported
232 // (trusted here — that is exactly how #3231's Float-only devices are
233 // detected, by Int16 being correctly rejected).
234 QAudioFormat fmt;
235 int sinkRate = kSampleRate;
236 bool fallbackOccurred = false;
237 QStringList fallbackReasons;
238 QStringList attemptedFormats;

Callers

nothing calls this directly

Calls 15

logOpenFailureFunction · 0.85
formatLadderFunction · 0.85
hostTargetOsFunction · 0.85
moveFunction · 0.85
logAuxiliarySinkFunction · 0.85
isNullMethod · 0.80
idMethod · 0.80
sampleFormatNameFunction · 0.70
sampleRateMethod · 0.45
sizeMethod · 0.45
dataMethod · 0.45
closeMethod · 0.45

Tested by

no test coverage detected