24using namespace swift::misc::aviation;
25using namespace swift::misc::audio;
26using namespace swift::misc::network;
27using namespace swift::misc::physical_quantities;
28using namespace swift::misc::simulation;
29using namespace swift::sound;
30using namespace swift::core::afv::clients;
34namespace swift::core::context
41 void IContextAudio::onChangedLocalDevices(
const CAudioDeviceInfoList &devices) { this->registerDevices(devices); }
43 const QString &IContextAudio::InterfaceName()
49 const QString &IContextAudio::ObjectPath()
66 auto *context =
new CContextAudio(mode, runtime);
67 context->registerWithDBus(ObjectPath(), server);
73 SWIFT_VERIFY_X(
false, Q_FUNC_INFO,
"Empty context not supported for audio (since AFV)");
75 default:
SWIFT_VERIFY_X(
false, Q_FUNC_INFO,
"Unknown context mode");
return nullptr;
79 bool CContextAudioBase::parseCommandLine(
const QString &commandLine,
const CIdentifier &originator)
92 parser.
parse(commandLine);
93 if (!parser.isKnownCommand())
98 if (parser.matchesCommand(
".mute"))
100 this->setOutputMute(
true);
103 else if (parser.matchesCommand(
".unmute"))
105 this->setOutputMute(
false);
108 else if (parser.commandStartsWith(
"vol") && parser.countParts() > 1)
110 const int v = parser.toInt(1);
111 this->setMasterOutputVolume(v);
120 CContextAudioBase::registerHelp();
122 if (CContextAudioBase::isNoAudioSet())
128 this->initVoiceClient();
136 if (!myself || !sApp || sApp->isShuttingDown())
141 const CSettings as = m_audioSettings.getThreadLocal();
147 myself->changeDeviceSettings();
148 myself->onChangedAudioSettings();
149 myself->onChangedLocalDevices(m_activeLocalDevices);
153 CContextAudioBase::~CContextAudioBase() { this->gracefulShutdown(); }
155 void CContextAudioBase::initVoiceClient()
157 if (m_voiceClient || !sApp)
163 if (devices != m_activeLocalDevices)
165 m_activeLocalDevices = devices;
166 emit this->changedLocalAudioDevices(devices);
170 if (!m_winCoInitialized)
172 HRESULT hr = CoInitializeEx(
nullptr, COINIT_MULTITHREADED);
176 if (hr == RPC_E_CHANGED_MODE)
178 CLogMessage(
this).
debug(u
"CoInitializeEx was already called with a different mode. Trying again.");
179 hr = CoInitializeEx(
nullptr, COINIT_APARTMENTTHREADED);
185 if (hr == S_OK || hr == S_FALSE)
187 m_winCoInitialized =
true;
192 m_voiceClient =
new CAfvClient(sApp->getGlobalSetup().getAfvApiServerUrl().toQString(),
this);
194 Q_ASSERT_X(m_voiceClient->thread() == qApp->thread(), Q_FUNC_INFO,
"Should be in main thread");
195 m_voiceClient->start();
196 Q_ASSERT_X(m_voiceClient->owner() ==
this, Q_FUNC_INFO,
"Wrong owner");
197 Q_ASSERT_X(m_voiceClient->thread() != qApp->thread(), Q_FUNC_INFO,
"Must NOT be in main thread");
217 void CContextAudioBase::terminateVoiceClient()
221 m_voiceClient->gracefulShutdown();
223 m_voiceClient->deleteLater();
224 m_voiceClient =
nullptr;
226 if (m_winCoInitialized)
229 m_winCoInitialized =
false;
235 void CContextAudioBase::gracefulShutdown()
237 this->terminateVoiceClient();
240 m_selcalPlayer->gracefulShutdown();
241 m_selcalPlayer =
nullptr;
246 void CContextAudioBase::setRxTx(
bool rx1,
bool tx1,
bool rx2,
bool tx2)
250 m_voiceClient->setRxTx(rx1, tx1, rx2, tx2);
254 void CContextAudioBase::getRxTx(
bool &rx1,
bool &tx1,
bool &rx2,
bool &tx2)
const
258 m_voiceClient->setRxTx(rx1, tx1, rx2, tx2);
262 const CIdentifier &CContextAudioBase::audioRunsWhere()
const
264 static const CIdentifier i(
"CContextAudioBaseImpl");
274 return m_voiceClient->isEnabledComUnit(comUnit);
283 return m_voiceClient->isTransmittingComUnit(comUnit);
286 bool CContextAudioBase::connectAudioWithNetworkCredentials()
292 if (!sApp || sApp->isShuttingDown() || !sApp->getIContextNetwork())
297 const CEcosystem ecoSystem = this->getIContextNetwork()->getConnectedServer().getEcosystem();
304 const CUser connectedUser = this->getIContextNetwork()->getConnectedServer().getUser();
307 this->unRegisterAudioCallsign(cs, this->identifier());
308 if (this->hasRegisteredAudioCallsign(cs))
313 CLogMessage(
this).
info(u
"About to connect to voice as '%1' '%2'") << connectedUser.
getId() << cs;
315 this->registerAudioCallsign(cs, this->identifier());
319 bool CContextAudioBase::isAudioConnected()
const {
return m_voiceClient && m_voiceClient->isConnected(); }
321 bool CContextAudioBase::isAudioStarted()
const {
return m_voiceClient && m_voiceClient->isStarted(); }
323 bool CContextAudioBase::isComUnitIntegrated()
const
325 return m_voiceClient && m_voiceClient->isComUnitIntegrated();
331 { {
"n",
"noaudio" },
336 bool CContextAudioBase::isNoAudioSet()
342 return sApp->isParserOptionSet(
"noaudio");
345 QString CContextAudioBase::audioRunsWhereInfo()
const
347 const QString s = QStringLiteral(
"[%1] Audio on '%2', '%3'.")
349 audioRunsWhere().getProcessName());
367 const QString inputDeviceName = m_inputDeviceSetting.get();
368 const CAudioDeviceInfo inputDevice = this->getAudioInputDevices().findByNameOrDefault(
371 const QString outputDeviceName = m_outputDeviceSetting.get();
372 const CAudioDeviceInfo outputDevice = this->getAudioOutputDevices().findByNameOrDefault(
381 void CContextAudioBase::setCurrentAudioDevices(
const CAudioDeviceInfo &inputDevice,
395 Q_ASSERT_X(inputDevice.
isInputDevice(), Q_FUNC_INFO,
"Need input device");
401 Q_ASSERT_X(outputDevice.
isOutputDevice(), Q_FUNC_INFO,
"Need output device");
406 m_voiceClient->startAudio(inputDevice, outputDevice);
409 void CContextAudioBase::setMasterOutputVolume(
int volume)
416 const bool wasMuted = this->isOutputMuted();
419 const int currentVolume = m_voiceClient->getNormalizedMasterOutputVolume();
420 const bool changedVoiceOutput = (currentVolume != volume);
421 if (changedVoiceOutput)
424 m_voiceClient->setNormalizedMasterOutputVolume(volume);
425 m_outMasterVolumeBeforeMute = volume;
427 emit this->changedAudioVolume(volume);
428 if ((volume > 0 && wasMuted) || (volume < 1 && !wasMuted))
431 emit this->changedOutputMute(volume < 1);
435 CSettings as(m_audioSettings.getThreadLocal());
439 m_audioSettings.set(as);
445 if (comUnit != CComSystem::Com1 && comUnit != CComSystem::Com2)
456 const int currentVolume = m_voiceClient->getNormalizedComOutputVolume(comUnit);
457 const bool changedVoiceOutput = (currentVolume != volume);
458 if (changedVoiceOutput)
460 m_voiceClient->setNormalizedComOutputVolume(comUnit, volume);
461 emit this->changedAudioVolume(volume);
464 CSettings as(m_audioSettings.getThreadLocal());
468 m_audioSettings.set(as);
473 m_audioSettings.set(as);
477 int CContextAudioBase::getMasterOutputVolume()
const
483 return m_voiceClient->getNormalizedMasterOutputVolume();
492 return m_voiceClient->getNormalizedComOutputVolume(comUnit);
495 void CContextAudioBase::setOutputMute(
bool muted)
501 if (this->isOutputMuted() == muted)
508 m_outMasterVolumeBeforeMute = m_voiceClient->getNormalizedMasterOutputVolume();
511 m_voiceClient->setOutputMuted(muted);
514 m_voiceClient->setNormalizedMasterOutputVolume(m_outMasterVolumeBeforeMute);
518 bool CContextAudioBase::isOutputMuted()
const
524 return m_voiceClient->isOutputMuted();
527 void CContextAudioBase::playSelcalTone(
const CSelcal &selcal)
529 using namespace std::chrono_literals;
530 const std::chrono::milliseconds ms = m_selcalPlayer->play(90, selcal);
536 if (!sApp || sApp->isShuttingDown() || !myself)
540 this->playNotification(CNotificationSounds::NotificationTextMessageSupervisor,
true);
548 if (isDebugEnabled())
554 const bool play = !considerSettings ||
settings.isNotificationFlagSet(notification);
560 if (volume < 0 || volume > 100)
563 if (considerSettings)
565 volume = qMax(25,
settings.getNotificationVolume());
568 m_notificationPlayer.play(notification, volume);
571 void CContextAudioBase::enableAudioLoopback(
bool enable)
577 m_voiceClient->setLoopBack(enable);
580 bool CContextAudioBase::isAudioLoopbackEnabled()
const
586 return m_voiceClient->isLoopback();
589 void CContextAudioBase::setVoiceTransmission(
bool enable)
595 m_voiceClient->setPtt(enable);
598 void CContextAudioBase::changeDeviceSettings()
601 Q_ASSERT_X(devices.
size() == 2, Q_FUNC_INFO,
"Expect INPUT and OUTPUT device");
605 this->setCurrentAudioDevices(input, output);
608 void CContextAudioBase::onChangedAudioSettings()
610 const CSettings s = m_audioSettings.get();
612 m_notificationPlayer.updateDirectory(dir);
618 void CContextAudioBase::audioIncreaseVolume(
bool enabled)
624 const int v = qRound(this->getMasterOutputVolume() * 1.05);
625 this->setMasterOutputVolume(v);
628 void CContextAudioBase::audioDecreaseVolume(
bool enabled)
634 const int v = qRound(this->getMasterOutputVolume() / 1.05);
635 this->setMasterOutputVolume(v);
638 void CContextAudioBase::audioIncreaseVolumeCom1(
bool enabled)
644 if (isComUnitIntegrated())
648 const int v = qRound(this->getComOutputVolume(CComSystem::Com1) * 1.05);
649 this->setComOutputVolume(CComSystem::Com1, v);
652 void CContextAudioBase::audioDecreaseVolumeCom1(
bool enabled)
658 if (isComUnitIntegrated())
662 const int v = qRound(this->getComOutputVolume(CComSystem::Com1) / 1.05);
663 this->setComOutputVolume(CComSystem::Com1, v);
666 void CContextAudioBase::audioIncreaseVolumeCom2(
bool enabled)
672 if (isComUnitIntegrated())
676 const int v = qRound(this->getComOutputVolume(CComSystem::Com2) * 1.05);
677 this->setComOutputVolume(CComSystem::Com2, v);
680 void CContextAudioBase::audioDecreaseVolumeCom2(
bool enabled)
686 if (isComUnitIntegrated())
690 const int v = qRound(this->getComOutputVolume(CComSystem::Com2) / 1.05);
691 this->setComOutputVolume(CComSystem::Com2, v);
694 void CContextAudioBase::xCtxNetworkConnectionStatusChanged(
const CConnectionStatus &from,
703 SWIFT_VERIFY_X(this->getIContextNetwork(), Q_FUNC_INFO,
"Missing network context");
706 if (to.
isConnected() && this->getIContextNetwork())
708 const bool connected = this->connectAudioWithNetworkCredentials();
715 m_voiceClient->disconnectFrom();
719 void CContextAudioBase::onAfvConnectionStatusChanged(
int status)
726 const CCallsign cs = m_voiceClient->getCallsign();
731 case CAfvClient::Connected: this->registerAudioCallsign(cs, this->identifier());
break;
732 case CAfvClient::Disconnected: this->unRegisterAudioCallsign(cs, this->identifier());
break;
736 void CContextAudioBase::onAfvConnectionFailure(
const CStatusMessage &msg)
742 emit this->voiceClientFailure(msg);
745 bool CContextAudioBase::isRunningWithLocalCore() {
return sApp && sApp->isLocalContext(); }
static const QString & getShortVersionString()
Version as QVersionNumber.
ContextMode
How to handle a given context.
@ NotUsed
during shutdown or not used at all
@ Local
context runs in same process
@ Remote
context runs in a different process.
@ LocalInDBusServer
context runs in same process.
The class providing facades (the contexts) for all DBus relevant operations.
void ptt(bool active, const swift::misc::CIdentifier &identifier)
PTT status in this particular AFV client.
void changedOutputMute(bool muted)
Output mute changed.
ConnectionStatus
Connection status.
void afvConnectionFailure(const swift::misc::CStatusMessage &msg)
Authentication has failed with AFV server.
void stoppedAudio()
Audio has been stopped.
void startedAudio(const swift::misc::audio::CAudioDeviceInfo &inputDevice, const swift::misc::audio::CAudioDeviceInfo &outputDevice)
Started audio with devices.
void connectionStatusChanged(ConnectionStatus status)
Connection status has been changed.
IContextAudio(CCoreFacadeConfig::ContextMode mode, CCoreFacade *runtime)
Constructor.
Base for all context classes.
static const QString & coreServiceName()
Default service name.
Base class with a member CIdentifier to be inherited by a class which has an identity in the environm...
Value object encapsulating information identifying a component of a modular distributed swift process...
static const QString & contextSlot()
Context slots.
Class for emitting a log message.
static void preformatted(const CStatusMessage &statusMessage)
Sends a verbatim, preformatted message to the log.
Derived & debug()
Set the severity to debug.
Derived & info(const char16_t(&format)[N])
Set the severity to info, providing a format string.
size_type size() const
Returns number of elements in the sequence.
void push_back(const T &value)
Appends an element at the end of the sequence.
reference front()
Access the first element.
reference back()
Access the last element.
Utility methods for simple line parsing used with the command line.
void parse(const QString &commandLine)
Parse.
Streamable status message, e.g.
static bool isInThisThread(const QObject *toBeTested)
Is the current thread the object's thread?
Value object encapsulating information of a audio device.
static CAudioDeviceInfo getDefaultInputDevice()
Default input device.
bool isInputDevice() const
Input device.
const QString & getName() const
Get the device name.
bool isOutputDevice() const
Output device.
static CAudioDeviceInfo getDefaultOutputDevice()
Default output device.
Value object encapsulating a list of audio devices.
CAudioDeviceInfoList getOutputDevices() const
Get output devices in that list.
static CAudioDeviceInfoList allDevices()
Lists of all available devices.
CAudioDeviceInfoList getInputDevices() const
Get output devices in that list.
Value object encapsulating information of audio related settings.
int getOutVolumeCom2() const
Get volume for com2 (audio) 0..100.
const QString & getNotificationSoundDirectory() const
Notification directory.
void setOutVolume(int volume)
Set volume (audio) 0..100.
int getOutVolume() const
Get volume (audio) 0..100.
void setOutVolumeCom2(int volume)
Set volume for com2 (audio) 0..100.
int getOutVolumeCom1() const
Get volume for com1 (audio) 0..100.
void setOutVolumeCom1(int volume)
Set volume for com1 (audio) 0..100.
static int fixOutVolume(int v)
Make sure the volume is within the range.
Value object encapsulating information of a callsign.
const QString & asString() const
Get callsign (normalized).
QString toQString(bool i18n=false) const
Cast as QString.
Value object encapsulating information about a connection status.
bool isConnected() const
Query status.
bool isDisconnected() const
Query status.
Ecosystem of server belonging together.
static const CEcosystem & vatsim()
VATSIM eco system.
Value object encapsulating information of a user.
const QString & getId() const
Get id.
const QString & getPassword() const
Get password.
const aviation::CCallsign & getCallsign() const
Get associated callsign.
#define SWIFT_CORE_CONTEXTAUDIO_INTERFACENAME
DBus interface for context.
#define SWIFT_CORE_CONTEXTAUDIO_OBJECTPATH
DBus object path for context.
Classes related to configuration settings.
Free functions in swift::misc.
SWIFT_MISC_EXPORT const QString & boolToEnabledDisabled(bool v)
Bool to enabled/disabled.
QString translate(const char *context, const char *sourceText, const char *disambiguation, int n)
bool disconnect(const QMetaObject::Connection &connection)
QString arg(Args &&... args) const const
bool isEmpty() const const
QFuture< QtFuture::ArgsType< Signal > > connect(Sender *sender, Signal signal)
NotificationFlag
Play notification.
#define SWIFT_VERIFY_X(COND, WHERE, WHAT)
A weaker kind of assert.