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

Method startPlayback

src/core/QsoRecorder.cpp:389–487  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

387}
388
389void QsoRecorder::startPlayback()
390{
391 if (m_playing || m_lastRecordingPath.isEmpty()) return;
392
393 // Prefer the device the user picked in Radio Settings > Audio
394 // (m_outputDevice, seeded by MainWindow from AudioEngine). Only
395 // accept it if it is still present in the live audioOutputs() list
396 // — a hotplug/unplug between selection and now would otherwise
397 // strand us on a stale handle. Mirrors ClientPuduMonitor and
398 // AudioEngine::startSidetoneStream() (#3361).
399 QAudioDevice dev = QMediaDevices::defaultAudioOutput();
400 if (!m_outputDevice.isNull()) {
401 const auto outputs = QMediaDevices::audioOutputs();
402 for (const auto& d : outputs) {
403 if (d.id() == m_outputDevice.id()) { dev = d; break; }
404 }
405 }
406 if (dev.isNull()) return;
407
408 // Negotiate the playback format via the shared factory (#3306, Phase 6b).
409 // The recording is Int16, so prefer Int16 (no conversion on a normal device)
410 // and fall back to Float for Float-only WASAPI mixers — the Int16->Float
411 // conversion below handles that (#3231). The factory supplies the per-OS
412 // preferred rate (Win/Mac 48k to dodge the WASAPI 24k resampler artifacts
413 // #2120; Linux native 24k) plus the 44.1k and preferredFormat fallbacks.
414 // Previously QSO playback bailed on a Float-only device; now it works.
415 // Walk with isFormatSupported (trusted), mirroring ClientPuduMonitor.
416 QAudioFormat fmt;
417 int sinkRate = SAMPLE_RATE;
418 bool haveFormat = false;
419 const QList<QAudioFormat> ladder = AudioDeviceNegotiator::formatLadder(
420 dev, AudioFormatNegotiator::Direction::Output,
421 AudioFormatNegotiator::ResamplerPolicy::PreservePan,
422 AudioFormatNegotiator::hostTargetOs(),
423 AudioFormatNegotiator::kInternalRate,
424 /*bluetoothHfp=*/false, /*preferredRateOverride=*/0,
425 AudioFormatNegotiator::FormatPreference::Int16First);
426 for (const QAudioFormat& cand : ladder) {
427 QAudioFormat c = cand;
428 c.setChannelCount(NUM_CHANNELS);
429 if (dev.isFormatSupported(c)) {
430 fmt = c;
431 sinkRate = c.sampleRate();
432 haveFormat = true;
433 break;
434 }
435 }
436 if (!haveFormat) return;
437
438 if (!preparePlaybackPcm(sinkRate)) return;
439
440 // Float-only WASAPI mixers reject Int16 — convert the Int16 playback PCM to
441 // Float32 to match the negotiated sink format (#3231 / Phase 6b), mirroring
442 // ClientPuduMonitor. Only Float32 is handled (the only non-Int16 format
443 // preferredFormat() returns in practice on WASAPI/CoreAudio).
444 if (fmt.sampleFormat() == QAudioFormat::Float) {
445 const int samples = m_playPcm.size() / static_cast<int>(sizeof(int16_t));
446 QByteArray floatPcm(samples * static_cast<int>(sizeof(float)), '\0');

Callers 4

registerMidiParamsMethod · 0.45
toggleAetherialStripMethod · 0.45
wireVfoWidgetMethod · 0.45
wireDspAppletsMethod · 0.45

Calls 15

formatLadderFunction · 0.85
hostTargetOsFunction · 0.85
moveFunction · 0.85
isNullMethod · 0.80
idMethod · 0.80
isEmptyMethod · 0.45
sampleRateMethod · 0.45
sizeMethod · 0.45
dataMethod · 0.45
closeMethod · 0.45
openMethod · 0.45
startMethod · 0.45

Tested by

no test coverage detected