| 885 | |
| 886 | #if defined(Q_OS_MAC) || defined(HAVE_PIPEWIRE) |
| 887 | bool MainWindow::startDax() |
| 888 | { |
| 889 | if (m_daxBridge) return true; |
| 890 | |
| 891 | #ifdef Q_OS_MAC |
| 892 | // Only start if the macOS HAL driver bundle is installed. |
| 893 | if (!macDaxDriverInstalled()) { |
| 894 | qWarning() << "MainWindow: DAX HAL plugin not installed"; |
| 895 | QMessageBox::warning(this, "DAX Audio Driver Missing", |
| 896 | "The AetherSDR DAX audio driver is not installed on this Mac.\n\n" |
| 897 | "Install the DAX Virtual Audio Driver from the AetherSDR DMG package, " |
| 898 | "then enable DAX again."); |
| 899 | return false; |
| 900 | } |
| 901 | #endif |
| 902 | |
| 903 | m_daxBridge = new DaxBridge(this); |
| 904 | if (!m_daxBridge->open()) { |
| 905 | qWarning() << "MainWindow: failed to open DAX audio bridge"; |
| 906 | QMessageBox::warning(this, "DAX Audio Bridge Error", |
| 907 | "AetherSDR could not open the DAX audio bridge.\n\n" |
| 908 | "If the DAX driver was just installed, quit and relaunch AetherSDR and try again."); |
| 909 | delete m_daxBridge; |
| 910 | m_daxBridge = nullptr; |
| 911 | return false; |
| 912 | } |
| 913 | |
| 914 | // Listen for DAX stream status messages to register them in PanadapterStream. |
| 915 | // The radio sends "stream 0xNNNNNNNN type=dax_rx dax_channel=N" status lines |
| 916 | // in response to our stream create commands. |
| 917 | connect(&m_radioModel, &RadioModel::statusReceived, |
| 918 | m_daxBridge, [this](const QString& obj, const QMap<QString,QString>& kvs) { |
| 919 | if (!obj.startsWith("stream ")) return; |
| 920 | const QStringList parts = obj.split(QLatin1Char(' '), Qt::SkipEmptyParts); |
| 921 | if (parts.size() < 2) return; |
| 922 | bool ok = false; |
| 923 | quint32 streamId = parts[1].toUInt(&ok, 0); |
| 924 | if (!ok) return; |
| 925 | const bool removed = parts.contains(QStringLiteral("removed")) || kvs.contains(QStringLiteral("removed")); |
| 926 | if (removed) { |
| 927 | m_radioModel.panStream()->unregisterDaxStream(streamId); |
| 928 | qCDebug(lcDax) << "MainWindow: unregistered removed DAX RX stream" |
| 929 | << "0x" + QString::number(streamId, 16); |
| 930 | return; |
| 931 | } |
| 932 | QString type = kvs.value("type"); |
| 933 | if (type == "dax_rx") { |
| 934 | if (!streamStatusBelongsToUs(kvs, m_radioModel.ourClientHandle())) { |
| 935 | qCDebug(lcDax) << "MainWindow: ignoring DAX RX stream for another client" |
| 936 | << "stream=0x" + QString::number(streamId, 16) |
| 937 | << "owner=" << kvs.value("client_handle"); |
| 938 | return; |
| 939 | } |
| 940 | int ch = kvs.value("dax_channel").toInt(); |
| 941 | if (streamId && ch >= 1 && ch <= 4) { |
| 942 | m_radioModel.panStream()->registerDaxStream(streamId, ch); |
| 943 | qCDebug(lcDax) << "MainWindow: registered DAX RX ch" << ch |
| 944 | << "stream=0x" + QString::number(streamId, 16); |
nothing calls this directly
no test coverage detected