diff --git a/src/blackcore/audiodevice.h b/src/blackcore/audiodevice.h index cb17c2057..04b3d5697 100644 --- a/src/blackcore/audiodevice.h +++ b/src/blackcore/audiodevice.h @@ -43,6 +43,30 @@ namespace BlackCore virtual void setInputDevice(const BlackMisc::Audio::CAudioDeviceInfo &device) = 0; }; + class BLACKCORE_EXPORT CAudioInputDeviceDummy : public IAudioInputDevice + { + Q_OBJECT + public: + //! Constructor + CAudioInputDeviceDummy(QObject *parent = nullptr) : IAudioInputDevice(parent) {} + + //! Destructor + virtual ~CAudioInputDeviceDummy() override = default; + + //! \copydoc IAudioInputDevice::getInputDevices + virtual const BlackMisc::Audio::CAudioDeviceInfoList &getInputDevices() const override { return m_devices; } + + //! \copydoc IAudioInputDevice::getCurrentInputDevice + virtual const BlackMisc::Audio::CAudioDeviceInfo &getCurrentInputDevice() const override { return m_currentDevice; } + + //! \copydoc IAudioInputDevice::setInputDevice + virtual void setInputDevice(const BlackMisc::Audio::CAudioDeviceInfo &device) override { m_currentDevice = device; } + + private: + BlackMisc::Audio::CAudioDeviceInfoList m_devices; /*!< in and output devices */ + BlackMisc::Audio::CAudioDeviceInfo m_currentDevice; + }; + //! Audio Output Device class IAudioOutputDevice : public QObject { diff --git a/src/blackcore/context/contextaudioimpl.cpp b/src/blackcore/context/contextaudioimpl.cpp index 875b85d06..cde8098da 100644 --- a/src/blackcore/context/contextaudioimpl.cpp +++ b/src/blackcore/context/contextaudioimpl.cpp @@ -54,38 +54,13 @@ namespace BlackCore IContextAudio(mode, runtime), m_voice(new CVoiceVatlib()) { - //! \todo KB 2018-11 those are supposed to be Qt::QueuedConnection, but not yet changed (risk to break something) - m_channel1 = m_voice->createVoiceChannel(); - connect(m_channel1.data(), &IVoiceChannel::connectionStatusChanged, this, &CContextAudio::onConnectionStatusChanged); - connect(m_channel1.data(), &IVoiceChannel::userJoinedRoom, this, &CContextAudio::onUserJoinedRoom); - connect(m_channel1.data(), &IVoiceChannel::userLeftRoom, this, &CContextAudio::onUserLeftRoom); - m_channel2 = m_voice->createVoiceChannel(); - connect(m_channel2.data(), &IVoiceChannel::connectionStatusChanged, this, &CContextAudio::onConnectionStatusChanged); - connect(m_channel2.data(), &IVoiceChannel::userJoinedRoom, this, &CContextAudio::onUserJoinedRoom); - connect(m_channel2.data(), &IVoiceChannel::userLeftRoom, this, &CContextAudio::onUserLeftRoom); + initVoiceChannels(); + initInputDevice(); + initOutputDevice(); + initAudioMixer(); - m_voiceInputDevice = m_voice->createInputDevice(); - m_voiceOutputDevice = m_voice->createOutputDevice(); - - m_audioMixer = m_voice->createAudioMixer(); - - m_voice->connectVoice(m_voiceInputDevice.get(), m_audioMixer.get(), IAudioMixer::InputMicrophone); - m_voice->connectVoice(m_channel1.data(), m_audioMixer.get(), IAudioMixer::InputVoiceChannel1); - m_voice->connectVoice(m_channel2.data(), m_audioMixer.get(), IAudioMixer::InputVoiceChannel2); - m_voice->connectVoice(m_audioMixer.get(), IAudioMixer::OutputOutputDevice1, m_voiceOutputDevice.get()); - m_voice->connectVoice(m_audioMixer.get(), IAudioMixer::OutputVoiceChannel1, m_channel1.data()); - m_voice->connectVoice(m_audioMixer.get(), IAudioMixer::OutputVoiceChannel2, m_channel2.data()); - - m_audioMixer->makeMixerConnection(IAudioMixer::InputVoiceChannel1, IAudioMixer::OutputOutputDevice1); - m_audioMixer->makeMixerConnection(IAudioMixer::InputVoiceChannel2, IAudioMixer::OutputOutputDevice1); this->setVoiceOutputVolume(90); - m_unusedVoiceChannels.push_back(m_channel1); - m_unusedVoiceChannels.push_back(m_channel2); - - m_voiceChannelOutputPortMapping[m_channel1] = IAudioMixer::OutputVoiceChannel1; - m_voiceChannelOutputPortMapping[m_channel2] = IAudioMixer::OutputVoiceChannel2; - m_selcalPlayer = new CSelcalPlayer(QAudioDeviceInfo::defaultOutputDevice(), this); this->changeDeviceSettings(); @@ -105,6 +80,69 @@ namespace BlackCore return this; } + void CContextAudio::initVoiceChannels() + { + //! \todo KB 2018-11 those are supposed to be Qt::QueuedConnection, but not yet changed (risk to break something) + m_channel1 = m_voice->createVoiceChannel(); + connect(m_channel1.data(), &IVoiceChannel::connectionStatusChanged, this, &CContextAudio::onConnectionStatusChanged); + connect(m_channel1.data(), &IVoiceChannel::userJoinedRoom, this, &CContextAudio::onUserJoinedRoom); + connect(m_channel1.data(), &IVoiceChannel::userLeftRoom, this, &CContextAudio::onUserLeftRoom); + m_channel2 = m_voice->createVoiceChannel(); + connect(m_channel2.data(), &IVoiceChannel::connectionStatusChanged, this, &CContextAudio::onConnectionStatusChanged); + connect(m_channel2.data(), &IVoiceChannel::userJoinedRoom, this, &CContextAudio::onUserJoinedRoom); + connect(m_channel2.data(), &IVoiceChannel::userLeftRoom, this, &CContextAudio::onUserLeftRoom); + + m_unusedVoiceChannels.push_back(m_channel1); + m_unusedVoiceChannels.push_back(m_channel2); + + m_voiceChannelOutputPortMapping[m_channel1] = IAudioMixer::OutputVoiceChannel1; + m_voiceChannelOutputPortMapping[m_channel2] = IAudioMixer::OutputVoiceChannel2; + } + + void CContextAudio::initInputDevice() + { + #ifdef Q_OS_MAC + CMacOSMicrophoneAccess::AuthorizationStatus status = m_micAccess.getAuthorizationStatus(); + if (status == CMacOSMicrophoneAccess::Authorized) + { + m_voiceInputDevice = m_voice->createInputDevice(); + } + else if (status == CMacOSMicrophoneAccess::NotDetermined) + { + m_voiceInputDevice.reset(new CAudioInputDeviceDummy(this)); + connect(&m_micAccess, &CMacOSMicrophoneAccess::permissionRequestAnswered, this, &CContextAudio::delayedInitMicrophone); + m_micAccess.requestAccess(); + } + else + { + m_voiceInputDevice.reset(new CAudioInputDeviceDummy(this)); + CLogMessage(this).error(u"Microphone access not granted. Voice input will not work."); + } + #else + m_voiceInputDevice = m_voice->createInputDevice(); + #endif + } + + void CContextAudio::initOutputDevice() + { + m_voiceOutputDevice = m_voice->createOutputDevice(); + } + + void CContextAudio::initAudioMixer() + { + m_audioMixer = m_voice->createAudioMixer(); + + m_voice->connectVoice(m_voiceInputDevice.get(), m_audioMixer.get(), IAudioMixer::InputMicrophone); + m_voice->connectVoice(m_channel1.data(), m_audioMixer.get(), IAudioMixer::InputVoiceChannel1); + m_voice->connectVoice(m_channel2.data(), m_audioMixer.get(), IAudioMixer::InputVoiceChannel2); + m_voice->connectVoice(m_audioMixer.get(), IAudioMixer::OutputOutputDevice1, m_voiceOutputDevice.get()); + m_voice->connectVoice(m_audioMixer.get(), IAudioMixer::OutputVoiceChannel1, m_channel1.data()); + m_voice->connectVoice(m_audioMixer.get(), IAudioMixer::OutputVoiceChannel2, m_channel2.data()); + + m_audioMixer->makeMixerConnection(IAudioMixer::InputVoiceChannel1, IAudioMixer::OutputOutputDevice1); + m_audioMixer->makeMixerConnection(IAudioMixer::InputVoiceChannel2, IAudioMixer::OutputOutputDevice1); + } + CContextAudio::~CContextAudio() { this->leaveAllVoiceRooms(); @@ -643,5 +681,14 @@ namespace BlackCore return voiceChannel; } + + + #ifdef Q_OS_MAC + void CContextAudio::delayedInitMicrophone() + { + m_voiceInputDevice = m_voice->createInputDevice(); + m_voice->connectVoice(m_voiceInputDevice.get(), m_audioMixer.get(), IAudioMixer::InputMicrophone); + } + #endif } // namespace } // namespace diff --git a/src/blackcore/context/contextaudioimpl.h b/src/blackcore/context/contextaudioimpl.h index 71aaad82f..8a1b9b3e0 100644 --- a/src/blackcore/context/contextaudioimpl.h +++ b/src/blackcore/context/contextaudioimpl.h @@ -30,6 +30,7 @@ #include "blackmisc/network/userlist.h" #include "blackmisc/settingscache.h" #include "blackmisc/icons.h" +#include "blackmisc/macos/microphoneaccess.h" #include "blacksound/selcalplayer.h" #include "blacksound/notificationplayer.h" @@ -120,6 +121,12 @@ namespace BlackCore CContextAudio *registerWithDBus(BlackMisc::CDBusServer *server); private: + void initVoiceChannels(); + void initInputDevice(); + void initOutputDevice(); + void initAudioMixer(); + void initVoiceVatlib(bool allocateInput = true); + //! \copydoc IVoice::connectionStatusChanged //! \sa IContextAudio::changedVoiceRooms void onConnectionStatusChanged(IVoiceChannel::ConnectionStatus oldStatus, IVoiceChannel::ConnectionStatus newStatus); @@ -152,6 +159,11 @@ namespace BlackCore const int MinUnmuteVolume = 20; //!< minimum volume when unmuted int m_outVolumeBeforeMute = 90; + #ifdef Q_OS_MAC + BlackMisc::CMacOSMicrophoneAccess m_micAccess; + void delayedInitMicrophone(); + #endif + // For easy access. QSharedPointer m_channel1; QSharedPointer m_channel2; diff --git a/src/blackmisc/blackmisc.pro b/src/blackmisc/blackmisc.pro index b2267df81..14b69c689 100644 --- a/src/blackmisc/blackmisc.pro +++ b/src/blackmisc/blackmisc.pro @@ -70,6 +70,10 @@ SOURCES += *.cpp \ $$PWD/test/*.cpp \ $$PWD/weather/*.cpp +macx { + HEADERS += $$PWD/macos/microphoneaccess.h + OBJECTIVE_SOURCES += $$PWD/macos/microphoneaccess.mm +} INCLUDEPATH *= $$EXTERNALSROOT/common/include/crashpad INCLUDEPATH *= $$EXTERNALSROOT/common/include/crashpad/mini_chromium @@ -87,7 +91,7 @@ msvc { CONFIG(debug, debug|release): LIBS *= -lclientd -lutild -lbased -lRpcrt4 -lAdvapi32 CONFIG(release, debug|release): LIBS *= -lclient -lutil -lbase -lRpcrt4 -lAdvapi32 } -macx: LIBS += -lclient -lutil -lbase -lbsm -framework Security -framework CoreFoundation -framework ApplicationServices -framework Foundation +macx: LIBS += -lclient -lutil -lbase -lbsm -framework AVFoundation -framework Security -framework CoreFoundation -framework ApplicationServices -framework Foundation unix:!macx: LIBS *= -lclient -lutil -lbase DESTDIR = $$DestRoot/lib diff --git a/src/blackmisc/macos/microphoneaccess.h b/src/blackmisc/macos/microphoneaccess.h new file mode 100644 index 000000000..c29fa0939 --- /dev/null +++ b/src/blackmisc/macos/microphoneaccess.h @@ -0,0 +1,48 @@ +/* Copyright (C) 2019 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution. No part of swift project, including this file, may be copied, modified, propagated, + * or distributed except according to the terms contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKMISC_AUDIOACCESSREQUEST_H +#define BLACKMISC_AUDIOACCESSREQUEST_H + +#include "blackmisc/blackmiscexport.h" +#include +#include + +namespace BlackMisc +{ + //! Wrapper around MacOS 10.14 AVCaptureDevice AVCaptureDevice authorization + class BLACKMISC_EXPORT CMacOSMicrophoneAccess : public QObject + { + Q_OBJECT + public: + //! Authorization status + enum AuthorizationStatus + { + Authorized, + Denied, + NotDetermined + }; + + //! Constructor + CMacOSMicrophoneAccess(QObject *parent = nullptr); + + //! Request access + void requestAccess(); + + //! Get current authorization status + AuthorizationStatus getAuthorizationStatus(); + + signals: + //! User has answered the permission request popup + void permissionRequestAnswered(bool granted); + }; +} + +#endif diff --git a/src/blackmisc/macos/microphoneaccess.mm b/src/blackmisc/macos/microphoneaccess.mm new file mode 100644 index 000000000..b4b5586b4 --- /dev/null +++ b/src/blackmisc/macos/microphoneaccess.mm @@ -0,0 +1,62 @@ +/* Copyright (C) 2019 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution. No part of swift project, including this file, may be copied, modified, propagated, + * or distributed except according to the terms contained in the LICENSE file. + */ + +#include "microphoneaccess.h" +#import + +namespace BlackMisc +{ +//#ifdef Q_OS_MAC + + BlackMisc::CMacOSMicrophoneAccess::CMacOSMicrophoneAccess(QObject *parent) : + QObject(parent) + { } + + void CMacOSMicrophoneAccess::requestAccess() + { + if (@available(macOS 10.14, *)) + { + NSString *mediaType = AVMediaTypeAudio; + [AVCaptureDevice requestAccessForMediaType:mediaType completionHandler:^(BOOL granted) + { + emit permissionRequestAnswered(granted); + }]; + } + else + { + emit permissionRequestAnswered(true); + } + + } + + CMacOSMicrophoneAccess::AuthorizationStatus CMacOSMicrophoneAccess::getAuthorizationStatus() + { + if (@available(macOS 10.14, *)) + { + NSString *mediaType = AVMediaTypeAudio; + AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:mediaType]; + if(authStatus == AVAuthorizationStatusAuthorized) + { + return AuthorizationStatus::Authorized; + } + else if(authStatus == AVAuthorizationStatusNotDetermined) + { + return AuthorizationStatus::NotDetermined; + } + return AuthorizationStatus::Denied; + } + else + { + return AuthorizationStatus::Authorized; + } + + } + +// #endif +} + diff --git a/src/swiftcore/Info.plist b/src/swiftcore/Info.plist new file mode 100644 index 000000000..c4c48045e --- /dev/null +++ b/src/swiftcore/Info.plist @@ -0,0 +1,30 @@ + + + + + CFBundleName + swift core + CFBundleDisplayName + swift core + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleGetInfoString + swift project - free and open source pilot client + CFBundleIconFile + ${ASSETCATALOG_COMPILER_APPICON_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundlePackageType + APPL + CFBundleSignature + ${QMAKE_PKGINFO_TYPEINFO} + LSMinimumSystemVersion + ${MACOSX_DEPLOYMENT_TARGET} + NSPrincipalClass + NSApplication + NSSupportsAutomaticGraphicsSwitching + + NSMicrophoneUsageDescription + This application needs access to your Microphone for Vatsim Voice. + + diff --git a/src/swiftcore/swiftcore.pro b/src/swiftcore/swiftcore.pro index 3811bbc18..437e5dae1 100644 --- a/src/swiftcore/swiftcore.pro +++ b/src/swiftcore/swiftcore.pro @@ -28,6 +28,9 @@ target.path = $$PREFIX/bin INSTALLS += target macx { + QMAKE_TARGET_BUNDLE_PREFIX = "org.swift-project" + QMAKE_INFO_PLIST = Info.plist + # Modifies plugin path qtconf.path = $$PREFIX/bin/swiftcore.app/Contents/Resources qtconf.files = qt.conf diff --git a/src/swiftdata/Info.plist b/src/swiftdata/Info.plist new file mode 100644 index 000000000..ec28bef06 --- /dev/null +++ b/src/swiftdata/Info.plist @@ -0,0 +1,28 @@ + + + + + CFBundleName + swift data + CFBundleDisplayName + swift data + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleGetInfoString + swift project - free and open source pilot client + CFBundleIconFile + ${ASSETCATALOG_COMPILER_APPICON_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundlePackageType + APPL + CFBundleSignature + ${QMAKE_PKGINFO_TYPEINFO} + LSMinimumSystemVersion + ${MACOSX_DEPLOYMENT_TARGET} + NSPrincipalClass + NSApplication + NSSupportsAutomaticGraphicsSwitching + + + diff --git a/src/swiftdata/swiftdata.pro b/src/swiftdata/swiftdata.pro index 7ad073234..d9ae482ab 100644 --- a/src/swiftdata/swiftdata.pro +++ b/src/swiftdata/swiftdata.pro @@ -29,6 +29,9 @@ target.path = $$PREFIX/bin INSTALLS += target macx { + QMAKE_TARGET_BUNDLE_PREFIX = "org.swift-project" + QMAKE_INFO_PLIST = Info.plist + # Modifies plugin path qtconf.path = $$PREFIX/bin/swiftdata.app/Contents/Resources qtconf.files = qt.conf diff --git a/src/swiftguistandard/Info.plist b/src/swiftguistandard/Info.plist new file mode 100644 index 000000000..276c58b37 --- /dev/null +++ b/src/swiftguistandard/Info.plist @@ -0,0 +1,30 @@ + + + + + CFBundleName + swift gui + CFBundleDisplayName + swift gui + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleGetInfoString + swift project - free and open source pilot client + CFBundleIconFile + ${ASSETCATALOG_COMPILER_APPICON_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundlePackageType + APPL + CFBundleSignature + ${QMAKE_PKGINFO_TYPEINFO} + LSMinimumSystemVersion + ${MACOSX_DEPLOYMENT_TARGET} + NSPrincipalClass + NSApplication + NSSupportsAutomaticGraphicsSwitching + + NSMicrophoneUsageDescription + This application needs access to your Microphone for Vatsim Voice. + + diff --git a/src/swiftguistandard/swiftguistandard.pro b/src/swiftguistandard/swiftguistandard.pro index aa6e3c065..5271b843a 100644 --- a/src/swiftguistandard/swiftguistandard.pro +++ b/src/swiftguistandard/swiftguistandard.pro @@ -29,6 +29,9 @@ target.path = $$PREFIX/bin INSTALLS += target macx { + QMAKE_TARGET_BUNDLE_PREFIX = "org.swift-project" + QMAKE_INFO_PLIST = Info.plist + # Modifies plugin path qtconf.path = $$PREFIX/bin/swiftguistd.app/Contents/Resources qtconf.files = qt.conf diff --git a/src/swiftlauncher/Info.plist b/src/swiftlauncher/Info.plist new file mode 100644 index 000000000..bad414ecf --- /dev/null +++ b/src/swiftlauncher/Info.plist @@ -0,0 +1,28 @@ + + + + + CFBundleName + swift launcher + CFBundleDisplayName + swift launcher + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleGetInfoString + swift project - free and open source pilot client + CFBundleIconFile + ${ASSETCATALOG_COMPILER_APPICON_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundlePackageType + APPL + CFBundleSignature + ${QMAKE_PKGINFO_TYPEINFO} + LSMinimumSystemVersion + ${MACOSX_DEPLOYMENT_TARGET} + NSPrincipalClass + NSApplication + NSSupportsAutomaticGraphicsSwitching + + + diff --git a/src/swiftlauncher/swiftlauncher.pro b/src/swiftlauncher/swiftlauncher.pro index bb5155813..a658421fd 100644 --- a/src/swiftlauncher/swiftlauncher.pro +++ b/src/swiftlauncher/swiftlauncher.pro @@ -30,6 +30,9 @@ target.path = $$PREFIX/bin INSTALLS += target macx { + QMAKE_TARGET_BUNDLE_PREFIX = "org.swift-project" + QMAKE_INFO_PLIST = Info.plist + # Modifies plugin path qtconf.path = $$PREFIX/bin/swiftlauncher.app/Contents/Resources qtconf.files = qt.conf