refs #372 Move voice channel sharing into CContextAudio

Before a very complex mechanism was implemented in order to share
a vatlib voice channel resource if more than one COM unit was connected
to the same channel. This is now moved into CContextAudio by mapping
a COM unit to a shared voice channel pointer.
This commit is contained in:
Roland Winklmeier
2015-02-03 21:22:11 +01:00
committed by Klaus Basan
parent 33dddf795e
commit 84ac5e9972
12 changed files with 354 additions and 530 deletions

View File

@@ -47,6 +47,14 @@ namespace BlackCore
IContextAudio(CRuntimeConfig::ContextMode mode, CRuntime *runtime) : CContext(mode, runtime) {}
public:
//! ComUnit number
enum ComUnit
{
Com1,
Com2
};
//! \brief Interface name
static const QString &InterfaceName()
{
@@ -101,19 +109,8 @@ namespace BlackCore
//! Get voice rooms for COM1, COM2, but without latest audio status
virtual BlackMisc::Audio::CVoiceRoomList getComVoiceRooms() const = 0;
/*!
* \brief COM 1 voice room
* \param withAudioStatus update audio status
* \return
*/
virtual BlackMisc::Audio::CVoiceRoom getCom1VoiceRoom(bool withAudioStatus) const = 0;
/*!
* \brief COM 2 voice room
* \param withAudioStatus update audio status
* \return
*/
virtual BlackMisc::Audio::CVoiceRoom getCom2VoiceRoom(bool withAudioStatus) const = 0;
//! Get voice room per com unit
virtual BlackMisc::Audio::CVoiceRoom getVoiceRoom(int comUnit, bool withAudioStatus) const = 0;
//! Set voice rooms
virtual void setComVoiceRooms(const BlackMisc::Audio::CVoiceRoomList &voiceRooms) = 0;
@@ -121,17 +118,11 @@ namespace BlackCore
//! Leave all voice rooms
virtual void leaveAllVoiceRooms() = 0;
//! COM1 room users callsigns
virtual BlackMisc::Aviation::CCallsignList getCom1RoomCallsigns() const = 0;
//! Room user callsigns
virtual BlackMisc::Aviation::CCallsignList getRoomCallsigns(int comUnit) const = 0;
//! COM2 room users callsigns
virtual BlackMisc::Aviation::CCallsignList getCom2RoomCallsigns() const = 0;
//! COM1 room users
virtual BlackMisc::Network::CUserList getCom1RoomUsers() const = 0;
//! COM2 room users
virtual BlackMisc::Network::CUserList getCom2RoomUsers() const = 0;
//! Room users
virtual BlackMisc::Network::CUserList getRoomUsers(int comUnit) const = 0;
//! Audio devices
virtual BlackMisc::Audio::CAudioDeviceInfoList getAudioDevices() const = 0;

View File

@@ -41,12 +41,12 @@ namespace BlackCore
m_inputManager = CInputManager::getInstance();
m_handlePtt = m_inputManager->registerHotkeyFunc(CHotkeyFunction::Ptt(), this, &CContextAudio::ps_setVoiceTransmission);
m_channelCom1 = m_voice->createVoiceChannel();
m_channelCom1->setMyAircraftCallsign(getIContextOwnAircraft()->getOwnAircraft().getCallsign());
connect(m_channelCom1.get(), &IVoiceChannel::connectionStatusChanged, this, &CContextAudio::ps_com1ConnectionStatusChanged);
m_channelCom2 = m_voice->createVoiceChannel();
m_channelCom2->setMyAircraftCallsign(getIContextOwnAircraft()->getOwnAircraft().getCallsign());
connect(m_channelCom2.get(), &IVoiceChannel::connectionStatusChanged, this, &CContextAudio::ps_com2ConnectionStatusChanged);
m_channel1 = m_voice->createVoiceChannel();
m_channel1->setMyAircraftCallsign(getIContextOwnAircraft()->getOwnAircraft().getCallsign());
connect(m_channel1.data(), &IVoiceChannel::connectionStatusChanged, this, &CContextAudio::ps_connectionStatusChanged);
m_channel2 = m_voice->createVoiceChannel();
m_channel2->setMyAircraftCallsign(getIContextOwnAircraft()->getOwnAircraft().getCallsign());
connect(m_channel2.data(), &IVoiceChannel::connectionStatusChanged, this, &CContextAudio::ps_connectionStatusChanged);
m_voiceInputDevice = m_voice->createInputDevice();
m_voiceOutputDevice = m_voice->createOutputDevice();
@@ -54,17 +54,20 @@ namespace BlackCore
m_audioMixer = m_voice->createAudioMixer();
m_voice->connectVoice(m_voiceInputDevice.get(), m_audioMixer.get(), IAudioMixer::InputMicrophone);
m_voice->connectVoice(m_channelCom1.get(), m_audioMixer.get(), IAudioMixer::InputVoiceChannel1);
m_voice->connectVoice(m_channelCom2.get(), m_audioMixer.get(), IAudioMixer::InputVoiceChannel2);
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_channelCom1.get());
m_voice->connectVoice(m_audioMixer.get(), IAudioMixer::OutputVoiceChannel2, m_channelCom2.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);
// 4. load sounds (init), not possible in own thread
QTimer::singleShot(10 * 1000, this, SLOT(ps_initNotificationSounds()));
m_unusedVoiceChannels.push_back(m_channel1);
m_unusedVoiceChannels.push_back(m_channel2);
}
/*
@@ -88,23 +91,17 @@ namespace BlackCore
/*
* Voice rooms for COM
*/
CVoiceRoom CContextAudio::getCom1VoiceRoom(bool withAudioStatus) const
CVoiceRoom CContextAudio::getVoiceRoom(int comUnitValue, bool withAudioStatus) const
{
Q_ASSERT(this->m_voice);
CLogMessage(this, CLogCategory::contextSlot()).debug() << Q_FUNC_INFO << withAudioStatus;
// We always have the audio status due to shared status
return m_channelCom1->getVoiceRoom();
}
/*
* Voice rooms for COM
*/
CVoiceRoom CContextAudio::getCom2VoiceRoom(bool withAudioStatus) const
{
Q_ASSERT(this->m_voice);
CLogMessage(this, CLogCategory::contextSlot()).debug() << Q_FUNC_INFO << withAudioStatus;
// We always have the audio status due to shared status
return m_channelCom2->getVoiceRoom();
auto voiceChannel = m_voiceChannelMapping.value(static_cast<ComUnit>(comUnitValue));
if (voiceChannel)
return voiceChannel->getVoiceRoom();
else
return CVoiceRoom();
}
/*
@@ -115,8 +112,29 @@ namespace BlackCore
Q_ASSERT(this->m_voice);
CLogMessage(this, CLogCategory::contextSlot()).debug() << Q_FUNC_INFO;
CVoiceRoomList voiceRoomList;
voiceRoomList.push_back(m_channelCom1->getVoiceRoom());
voiceRoomList.push_back(m_channelCom2->getVoiceRoom());
auto voiceChannelCom1 = m_voiceChannelMapping.value(Com1);
if (voiceChannelCom1)
{
CVoiceRoom room = voiceChannelCom1->getVoiceRoom();
voiceRoomList.push_back(room);
}
else
{
voiceRoomList.push_back(CVoiceRoom());
}
auto voiceChannelCom2 = m_voiceChannelMapping.value(Com2);
if (voiceChannelCom2)
{
CVoiceRoom room = voiceChannelCom2->getVoiceRoom();
voiceRoomList.push_back(room);
}
else
{
voiceRoomList.push_back(CVoiceRoom());
}
return voiceRoomList;
}
@@ -127,8 +145,11 @@ namespace BlackCore
{
Q_ASSERT(this->m_voice);
CLogMessage(this, CLogCategory::contextSlot()).debug() << Q_FUNC_INFO;
m_channelCom1->leaveVoiceRoom();
m_channelCom2->leaveVoiceRoom();
m_voiceChannelMapping.clear();
m_channel1->leaveVoiceRoom();
m_channel2->leaveVoiceRoom();
m_unusedVoiceChannels.push_back(m_channel1);
m_unusedVoiceChannels.push_back(m_channel2);
}
/*
@@ -204,9 +225,9 @@ namespace BlackCore
void CContextAudio::setVolumes(int com1Volume, int com2Volume)
{
//! \todo Fix when VATLIB 2.0 is available
int channelV1 = static_cast<int>(m_channelCom1->getVolume());
int channelV2 = static_cast<int>(m_channelCom2->getVolume());
//! \todo Will be removed as part of #371.
int channelV1 = static_cast<int>(m_channel1->getVolume());
int channelV2 = static_cast<int>(m_channel2->getVolume());
if (channelV1 == com1Volume && channelV2 == com2Volume) { return; }
bool enable1 = com1Volume > 0;
@@ -214,8 +235,8 @@ namespace BlackCore
bool muted = !enable1 && !enable2;
//! \todo m_channelCom1->setVolume here crashed, also what is correct setRoomOutputVolume or setVolume
m_channelCom1->setVolume(com1Volume);
m_channelCom2->setVolume(com2Volume);
m_channel1->setVolume(com1Volume);
m_channel2->setVolume(com2Volume);
this->setMute(muted);
emit changedAudioVolumes(com1Volume, com2Volume);
@@ -257,29 +278,86 @@ namespace BlackCore
CLogMessage(this, CLogCategory::contextSlot()).debug() << Q_FUNC_INFO << newRooms;
CVoiceRoomList currentRooms = getComVoiceRooms();
CVoiceRoom currentRoom1 = currentRooms[0];
CVoiceRoom currentRoom2 = currentRooms[1];
CVoiceRoom newRoom1 = newRooms[0];
CVoiceRoom newRoom2 = newRooms[1];
CVoiceRoom currentRoomCom1 = currentRooms[0];
CVoiceRoom currentRoomCom2 = currentRooms[1];
CVoiceRoom newRoomCom1 = newRooms[0];
CVoiceRoom newRoomCom2 = newRooms[1];
bool changed = false;
// changed rooms? But only compare on "URL", not status as connected etc.
if (currentRoom1.getVoiceRoomUrl() != newRoom1.getVoiceRoomUrl())
if (currentRoomCom1.getVoiceRoomUrl() != newRoomCom1.getVoiceRoomUrl())
{
m_channelCom1->leaveVoiceRoom();
if (newRoom1.isValid())
auto oldVoiceChannel = m_voiceChannelMapping.value(Com1);
if (oldVoiceChannel)
{
m_channelCom1->joinVoiceRoom(newRoom1);
m_voiceChannelMapping.remove(Com1);
// If the voice channel is not used by anybody else
if (!m_voiceChannelMapping.values().contains(oldVoiceChannel))
{
oldVoiceChannel->leaveVoiceRoom();
m_unusedVoiceChannels.push_back(oldVoiceChannel);
}
else
{
emit this->changedVoiceRooms(getComVoiceRooms(), false);
}
}
if (newRoomCom1.isValid())
{
auto newVoiceChannel = getVoiceChannelBy(newRoomCom1);
bool inUse = m_voiceChannelMapping.values().contains(newVoiceChannel);
m_voiceChannelMapping.insert(Com1, newVoiceChannel);
// If the voice channel is not used by anybody else
if (!inUse)
{
newVoiceChannel->joinVoiceRoom(newRoomCom1);
}
else
{
emit this->changedVoiceRooms(getComVoiceRooms(), true);
}
}
changed = true;
}
if (currentRoom2.getVoiceRoomUrl() != newRoom2.getVoiceRoomUrl())
// changed rooms? But only compare on "URL", not status as connected etc.
if (currentRoomCom2.getVoiceRoomUrl() != newRoomCom2.getVoiceRoomUrl())
{
m_channelCom2->leaveVoiceRoom();
if (newRoom2.isValid())
auto oldVoiceChannel = m_voiceChannelMapping.value(Com2);
if (oldVoiceChannel)
{
m_channelCom2->joinVoiceRoom(newRoom2);
m_voiceChannelMapping.remove(Com2);
// If the voice channel is not used by anybody else
if (!m_voiceChannelMapping.values().contains(oldVoiceChannel))
{
oldVoiceChannel->leaveVoiceRoom();
m_unusedVoiceChannels.push_back(oldVoiceChannel);
}
else
{
emit this->changedVoiceRooms(getComVoiceRooms(), false);
}
}
if (newRoomCom2.isValid())
{
auto newVoiceChannel = getVoiceChannelBy(newRoomCom2);
bool inUse = m_voiceChannelMapping.values().contains(newVoiceChannel);
m_voiceChannelMapping.insert(Com2, newVoiceChannel);
// If the voice channel is not used by anybody else
if (!inUse)
{
newVoiceChannel->joinVoiceRoom(newRoomCom2);
}
else
{
emit this->changedVoiceRooms(getComVoiceRooms(), true);
}
}
changed = true;
}
@@ -289,48 +367,27 @@ namespace BlackCore
Q_UNUSED(changed);
}
/*
* Room 1 callsigns
*/
CCallsignList CContextAudio::getCom1RoomCallsigns() const
CCallsignList CContextAudio::getRoomCallsigns(int comUnitValue) const
{
Q_ASSERT(this->m_voice);
CLogMessage(this, CLogCategory::contextSlot()).debug() << Q_FUNC_INFO;
return m_channelCom1->getVoiceRoomCallsigns();
auto voiceChannel = m_voiceChannelMapping.value(static_cast<ComUnit>(comUnitValue));
if (voiceChannel)
return voiceChannel->getVoiceRoomCallsigns();
else
return CCallsignList();
}
/*
* Room 2 callsigns
*/
CCallsignList CContextAudio::getCom2RoomCallsigns() const
{
Q_ASSERT(this->m_voice);
CLogMessage(this, CLogCategory::contextSlot()).debug() << Q_FUNC_INFO;
return m_channelCom2->getVoiceRoomCallsigns();
}
/*
* Room 1 users
*/
Network::CUserList CContextAudio::getCom1RoomUsers() const
Network::CUserList CContextAudio::getRoomUsers(int comUnitValue) const
{
Q_ASSERT(this->m_voice);
Q_ASSERT(this->getRuntime());
if (!this->getRuntime()->getIContextNetwork()) return Network::CUserList();
CLogMessage(this, CLogCategory::contextSlot()).debug() << Q_FUNC_INFO;
return this->getIContextNetwork()->getUsersForCallsigns(this->getCom1RoomCallsigns());
}
/*
* Room 2 users
*/
Network::CUserList CContextAudio::getCom2RoomUsers() const
{
Q_ASSERT(this->m_voice);
Q_ASSERT(this->getRuntime());
if (!this->getRuntime()->getIContextNetwork()) return Network::CUserList();
CLogMessage(this, CLogCategory::contextSlot()).debug() << Q_FUNC_INFO;
return this->getIContextNetwork()->getUsersForCallsigns(this->getCom2RoomCallsigns());
auto comUnit = static_cast<ComUnit>(comUnitValue);
return this->getIContextNetwork()->getUsersForCallsigns(this->getRoomCallsigns(comUnit));
}
/*
@@ -449,20 +506,7 @@ namespace BlackCore
qint32 v = parser.toInt(1);
if (v >= 0 && v <= 100)
{
qint32 v1 = this->m_channelCom1->getVolume();
qint32 v2 = this->m_channelCom2->getVolume();
if (parser.commandEndsWith("1"))
{
this->setVolumes(v, v2);
}
else if (parser.commandEndsWith("2"))
{
this->setVolumes(v1, v);
}
else
{
this->setVolumes(v, v);
}
// TODO: vatlib volume
}
}
return false;
@@ -478,7 +522,7 @@ namespace BlackCore
/*
* Connection status changed
*/
void CContextAudio::ps_com1ConnectionStatusChanged(IVoiceChannel::ConnectionStatus oldStatus, IVoiceChannel::ConnectionStatus newStatus)
void CContextAudio::ps_connectionStatusChanged(IVoiceChannel::ConnectionStatus oldStatus, IVoiceChannel::ConnectionStatus newStatus)
{
Q_UNUSED(oldStatus);
@@ -499,8 +543,8 @@ namespace BlackCore
if (this->getIContextOwnAircraft())
{
// good chance to update aircraft
m_channelCom1->setMyAircraftCallsign(this->getIContextOwnAircraft()->getOwnAircraft().getCallsign());
m_channelCom2->setMyAircraftCallsign(this->getIContextOwnAircraft()->getOwnAircraft().getCallsign());
m_channel1->setMyAircraftCallsign(this->getIContextOwnAircraft()->getOwnAircraft().getCallsign());
m_channel2->setMyAircraftCallsign(this->getIContextOwnAircraft()->getOwnAircraft().getCallsign());
}
emit this->changedVoiceRooms(getComVoiceRooms(), false);
break;
@@ -509,35 +553,22 @@ namespace BlackCore
}
}
void CContextAudio::ps_com2ConnectionStatusChanged(IVoiceChannel::ConnectionStatus oldStatus, IVoiceChannel::ConnectionStatus newStatus)
QSharedPointer<IVoiceChannel> CContextAudio::getVoiceChannelBy(const CVoiceRoom &voiceRoom)
{
Q_UNUSED(oldStatus);
switch (newStatus)
QSharedPointer<IVoiceChannel> voiceChannel;
for (const auto &channel : m_voiceChannelMapping.values())
{
case IVoiceChannel::Connected:
emit this->changedVoiceRooms(getComVoiceRooms(), true);
break;
case IVoiceChannel::Disconnecting:
break;
case IVoiceChannel::Connecting:
break;
case IVoiceChannel::ConnectingFailed:
case IVoiceChannel::DisconnectedError:
qWarning() << "Voice room COM2 error";
// intentional fall-through
case IVoiceChannel::Disconnected:
if (this->getIContextOwnAircraft())
{
// good chance to update aircraft
m_channelCom1->setMyAircraftCallsign(this->getIContextOwnAircraft()->getOwnAircraft().getCallsign());
m_channelCom2->setMyAircraftCallsign(this->getIContextOwnAircraft()->getOwnAircraft().getCallsign());
}
emit this->changedVoiceRooms(getComVoiceRooms(), false);
break;
default:
break;
if (channel->getVoiceRoom().getVoiceRoomUrl() == voiceRoom.getVoiceRoomUrl()) voiceChannel = channel;
}
// If we haven't found a valid voice channel pointer, get an unused one
if (!voiceChannel)
{
Q_ASSERT(!m_unusedVoiceChannels.isEmpty());
voiceChannel = m_unusedVoiceChannels.takeFirst();
}
return voiceChannel;
}
} // namespace

View File

@@ -54,26 +54,17 @@ namespace BlackCore
//! \copydoc IContextAudio::getComVoiceRoomsWithAudioStatus()
virtual BlackMisc::Audio::CVoiceRoomList getComVoiceRoomsWithAudioStatus() const override;
//! \copydoc IContextAudio::getCom1VoiceRoom
virtual BlackMisc::Audio::CVoiceRoom getCom1VoiceRoom(bool withAudioStatus) const override;
//! \copydoc IContextAudio::getCom2VoiceRoom
virtual BlackMisc::Audio::CVoiceRoom getCom2VoiceRoom(bool withAudioStatus) const override;
//! \copydoc IContextAudio::getVoiceRoom
virtual BlackMisc::Audio::CVoiceRoom getVoiceRoom(int comUnitValue, bool withAudioStatus) const override;
//! \copydoc IContextAudio::setComVoiceRooms
virtual void setComVoiceRooms(const BlackMisc::Audio::CVoiceRoomList &newRooms) override;
//! \copydoc IContextAudio::getCom1RoomCallsigns()
virtual BlackMisc::Aviation::CCallsignList getCom1RoomCallsigns() const override;
//! \copydoc IContextAudio::getRoomCallsigns()
virtual BlackMisc::Aviation::CCallsignList getRoomCallsigns(int comUnitValue) const override;
//! \copydoc IContextAudio::getCom2RoomCallsigns()
virtual BlackMisc::Aviation::CCallsignList getCom2RoomCallsigns() const override;
//! \copydoc IContextAudio::getCom1RoomUsers()
virtual BlackMisc::Network::CUserList getCom1RoomUsers() const override;
//! \copydoc IContextAudio::getCom2RoomUsers()
virtual BlackMisc::Network::CUserList getCom2RoomUsers() const override;
//! \copydoc IContextAudio::getRoomUsers()
virtual BlackMisc::Network::CUserList getRoomUsers(int comUnitValue) const override;
//! \copydoc IContextAudio::leaveAllVoiceRooms
virtual void leaveAllVoiceRooms() override;
@@ -149,11 +140,7 @@ namespace BlackCore
//! \copydoc IVoice::connectionStatusChanged
//! \sa IContextAudio::changedVoiceRooms
void ps_com1ConnectionStatusChanged(IVoiceChannel::ConnectionStatus oldStatus, IVoiceChannel::ConnectionStatus newStatus);
//! \copydoc IVoice::connectionStatusChanged
//! \sa IContextAudio::changedVoiceRooms
void ps_com2ConnectionStatusChanged(IVoiceChannel::ConnectionStatus oldStatus, IVoiceChannel::ConnectionStatus newStatus);
void ps_connectionStatusChanged(IVoiceChannel::ConnectionStatus oldStatus, IVoiceChannel::ConnectionStatus newStatus);
//! Init notification sounds
void ps_initNotificationSounds();
@@ -161,12 +148,15 @@ namespace BlackCore
void ps_setVoiceTransmission(bool enable);
private:
const int MinUnmuteVolume = 20; //!< minimum volume when unmuted
const int VoiceRoomEnabledVolume = 95; //!< voice room volume when enabled
//! Connection in transition
bool inTransitionState() const;
QSharedPointer<IVoiceChannel> getVoiceChannelBy(const BlackMisc::Audio::CVoiceRoom &voiceRoom);
CInputManager *m_inputManager = nullptr;
CInputManager::RegistrationHandle m_handlePtt;
@@ -174,12 +164,15 @@ namespace BlackCore
std::unique_ptr<IAudioMixer> m_audioMixer;
int m_outDeviceVolume = 100;
std::unique_ptr<IVoiceChannel> m_channelCom1;
std::unique_ptr<IVoiceChannel> m_channelCom2;
// For easy access.
QSharedPointer<IVoiceChannel> m_channel1;
QSharedPointer<IVoiceChannel> m_channel2;
std::unique_ptr<IAudioOutputDevice> m_voiceOutputDevice;
std::unique_ptr<IAudioInputDevice> m_voiceInputDevice;
QList<QSharedPointer<IVoiceChannel>> m_unusedVoiceChannels;
QHash<ComUnit, QSharedPointer<IVoiceChannel>> m_voiceChannelMapping;
};
} // namespace

View File

@@ -64,33 +64,17 @@ namespace BlackCore
/*
* COM1 callsigns
*/
BlackMisc::Aviation::CCallsignList CContextAudioProxy::getCom1RoomCallsigns() const
BlackMisc::Aviation::CCallsignList CContextAudioProxy::getRoomCallsigns(int comUnitValue) const
{
return this->m_dBusInterface->callDBusRet<BlackMisc::Aviation::CCallsignList>(QLatin1Literal("getCom1RoomCallsigns"));
}
/*
* COM2 callsigns
*/
BlackMisc::Aviation::CCallsignList CContextAudioProxy::getCom2RoomCallsigns() const
{
return this->m_dBusInterface->callDBusRet<BlackMisc::Aviation::CCallsignList>(QLatin1Literal("getCom2RoomCallsigns"));
return this->m_dBusInterface->callDBusRet<BlackMisc::Aviation::CCallsignList>(QLatin1Literal("getRoomCallsigns"), comUnitValue);
}
/*
* COM1 users
*/
BlackMisc::Network::CUserList CContextAudioProxy::getCom1RoomUsers() const
BlackMisc::Network::CUserList CContextAudioProxy::getRoomUsers(int comUnitValue) const
{
return this->m_dBusInterface->callDBusRet<BlackMisc::Network::CUserList>(QLatin1Literal("getCom1RoomUsers"));
}
/*
* COM2 users
*/
BlackMisc::Network::CUserList CContextAudioProxy::getCom2RoomUsers() const
{
return this->m_dBusInterface->callDBusRet<BlackMisc::Network::CUserList>(QLatin1Literal("getCom2RoomUsers"));
return this->m_dBusInterface->callDBusRet<BlackMisc::Network::CUserList>(QLatin1Literal("getRoomUsers"), comUnitValue);
}
/*
@@ -136,17 +120,9 @@ namespace BlackCore
/*
* Voice room
*/
CVoiceRoom CContextAudioProxy::getCom1VoiceRoom(bool withAudioStatus) const
CVoiceRoom CContextAudioProxy::getVoiceRoom(int comUnitValue, bool withAudioStatus) const
{
return this->m_dBusInterface->callDBusRet<CVoiceRoom>(QLatin1Literal("getCom1VoiceRoom"), withAudioStatus);
}
/*
* Voice room
*/
CVoiceRoom CContextAudioProxy::getCom2VoiceRoom(bool withAudioStatus) const
{
return this->m_dBusInterface->callDBusRet<CVoiceRoom>(QLatin1Literal("getCom2VoiceRoom"), withAudioStatus);
return this->m_dBusInterface->callDBusRet<CVoiceRoom>(QLatin1Literal("getVoiceRoom"), comUnitValue, withAudioStatus);
}
/*

View File

@@ -57,26 +57,17 @@ namespace BlackCore
//! \copydoc IContextAudio::getComVoiceRoomsWithAudioStatus()
virtual BlackMisc::Audio::CVoiceRoomList getComVoiceRoomsWithAudioStatus() const override;
//! \copydoc IContextAudio::getCom1VoiceRoom
virtual BlackMisc::Audio::CVoiceRoom getCom1VoiceRoom(bool withAudioStatus) const override;
//! \copydoc IContextAudio::getCom2VoiceRoom
virtual BlackMisc::Audio::CVoiceRoom getCom2VoiceRoom(bool withAudioStatus) const override;
//! \copydoc IContextAudio::getVoiceRoom
virtual BlackMisc::Audio::CVoiceRoom getVoiceRoom(int comUnitValue, bool withAudioStatus) const override;
//! \copydoc IContextAudio::setComVoiceRooms()
virtual void setComVoiceRooms(const BlackMisc::Audio::CVoiceRoomList &voiceRooms) override;
//! \copydoc IContextAudio::getCom1RoomCallsigns()
virtual BlackMisc::Aviation::CCallsignList getCom1RoomCallsigns() const override;
//! \copydoc IContextAudio::getRoomCallsigns()
virtual BlackMisc::Aviation::CCallsignList getRoomCallsigns(int comUnitValue) const override;
//! \copydoc IContextAudio::getCom2RoomCallsigns()
virtual BlackMisc::Aviation::CCallsignList getCom2RoomCallsigns() const override;
//! \copydoc IContextAudio::getCom1RoomUsers()
virtual BlackMisc::Network::CUserList getCom1RoomUsers() const override;
//! \copydoc IContextAudio::getCom2RoomUsers()
virtual BlackMisc::Network::CUserList getCom2RoomUsers() const override;
//! \copydoc IContextAudio::getRoomUsers()
virtual BlackMisc::Network::CUserList getRoomUsers(int comUnitValue) const override;
//! \copydoc IContextAudio::leaveAllVoiceRooms
virtual void leaveAllVoiceRooms() override;

View File

@@ -18,6 +18,7 @@
#include "../blackmisc/statusmessage.h"
#include <QObject>
#include <QSharedPointer>
#include <memory>
@@ -40,7 +41,7 @@ namespace BlackCore
virtual ~IVoice() {}
//! Create voice channel object
virtual std::unique_ptr<IVoiceChannel> createVoiceChannel() = 0;
virtual QSharedPointer<IVoiceChannel> createVoiceChannel() = 0;
//! Create input device object
virtual std::unique_ptr<IAudioInputDevice> createInputDevice() = 0;

View File

@@ -8,7 +8,6 @@
*/
#include "voice_channel_vatlib.h"
#include "voice_channel_vatlib_p.h"
#include "blackmisc/logmessage.h"
#include <mutex>
@@ -19,42 +18,128 @@ using namespace BlackMisc::Aviation;
namespace BlackCore
{
// Room data hash shared between all CVoiceChannel objects
QHash<CVoiceChannelVatlib * const, QSharedPointer<CVoiceChannelVatlibPrivate>> CVoiceChannelVatlibPrivate::m_sharedRoomData;
// Constructor
// Don't set the QObject parent. It will conflict with @QSharedPointer@ memory management
CVoiceChannelVatlibPrivate::CVoiceChannelVatlibPrivate(VatAudioService audioService, VatUDPAudioPort udpPort, CVoiceChannelVatlib *parent)
: m_audioService(audioService),
m_udpPort(udpPort),
q_ptr(parent)
CVoiceChannelVatlib::CVoiceChannelVatlib(VatAudioService audioService, VatUDPAudioPort udpPort, QObject *parent)
: IVoiceChannel(parent),
m_audioService(audioService),
m_udpPort(udpPort)
{
Q_ASSERT(m_audioService);
Q_ASSERT(m_udpPort);
m_connectionRefCount = 0;
m_roomStatus = IVoiceChannel::Disconnected;
m_voiceChannels.push_back(parent);
m_voiceChannel.reset(Vat_CreateVoiceChannel(m_audioService, "", 3782, "", "", m_udpPort));
Vat_SetConnectionChangedHandler(m_voiceChannel.data(), onRoomStatusUpdate, this);
Vat_SetConnectionChangedHandler(m_voiceChannel.data(), roomStatusUpdate, this);
Vat_SetClientJoinedHandler(m_voiceChannel.data(), processUserJoined, this);
Vat_SetClientLeftHandler(m_voiceChannel.data(), processUserLeft, this);
Vat_SetVoiceTransmissionChangedHandler(m_voiceChannel.data(), processTransmissionChange, this);
}
CVoiceChannelVatlibPrivate::~CVoiceChannelVatlibPrivate()
CVoiceChannelVatlib::~CVoiceChannelVatlib()
{
Q_ASSERT(m_roomStatus == IVoiceChannel::Disconnected);
}
void CVoiceChannelVatlibPrivate::setRoomOutputVolume(int volume)
void CVoiceChannelVatlib::joinVoiceRoom(const CVoiceRoom &voiceRoom)
{
// FIXME
Q_UNUSED(volume)
if (m_roomStatus == IVoiceChannel::Connecting || m_roomStatus == IVoiceChannel::Connected) return;
// No one else is using this voice room, so prepare to join
m_voiceRoom = voiceRoom;
Vat_SetRoomInfo(m_voiceChannel.data(), qPrintable(voiceRoom.getHostname()), 3782,
qPrintable(voiceRoom.getChannel()),
qPrintable(m_callsign.toQString()));
CLogMessage(this).debug() << "Joining voice room " << m_voiceRoom.getVoiceRoomUrl();
Vat_JoinRoom(m_voiceChannel.data());
}
void CVoiceChannelVatlibPrivate::updateRoomStatus(VatVoiceChannel /* channel */, VatConnectionStatus /** oldVatStatus **/, VatConnectionStatus newVatStatus)
// Leave room
void CVoiceChannelVatlib::leaveVoiceRoom()
{
// If this room is not connected, there is nothing to do
if (m_roomStatus == IVoiceChannel::Disconnecting || m_roomStatus == IVoiceChannel::Disconnected) return;
CLogMessage(this).debug() << "Leaving voice room " << m_voiceRoom.getVoiceRoomUrl();
Vat_DisconnectFromRoom(m_voiceChannel.data());
m_voiceRoom = {};
m_listCallsigns = {};
}
CCallsignList CVoiceChannelVatlib::getVoiceRoomCallsigns() const
{
return m_listCallsigns;
}
void CVoiceChannelVatlib::setMyAircraftCallsign(const CCallsign &callsign)
{
m_callsign = callsign;
}
BlackMisc::Audio::CVoiceRoom CVoiceChannelVatlib::getVoiceRoom() const
{
return m_voiceRoom;
}
bool CVoiceChannelVatlib::isMuted() const
{
// Remove
return false;
}
void CVoiceChannelVatlib::setVolume(int /* volume */)
{
// Remove
}
int CVoiceChannelVatlib::getVolume() const
{
// Remove
return 100;
}
VatVoiceChannel CVoiceChannelVatlib::getVoiceChannel() const
{
return m_voiceChannel.data();
}
CCallsign CVoiceChannelVatlib::extractCallsign(const QString &name)
{
CCallsign callsign;
if (name.isEmpty()) return callsign;
// callsign might contain: VATSIM id, user name
if (name.contains(" "))
{
QStringList parts = name.split(" ");
callsign = CCallsign(parts[0]);
// I throw away VATSIM id here, maybe we could use it
}
else
{
callsign = CCallsign(name);
}
return callsign;
}
void CVoiceChannelVatlib::userJoinedVoiceRoom(VatVoiceChannel, int /** id **/, const char *name)
{
CCallsign callsign(extractCallsign(name));
m_listCallsigns.push_back(callsign);
emit userJoinedRoom(callsign);
}
void CVoiceChannelVatlib::userLeftVoiceRoom(VatVoiceChannel, int /** id **/, const char *name)
{
CCallsign callsign(extractCallsign(name));
m_listCallsigns.remove(callsign);
emit userLeftRoom(callsign);
}
void CVoiceChannelVatlib::transmissionChanged(VatVoiceChannel, VatVoiceTransmissionStatus status)
{
if (status == vatVoiceStarted) emit audioStarted();
else emit audioStopped();
}
void CVoiceChannelVatlib::updateRoomStatus(VatVoiceChannel /* channel */, VatConnectionStatus /** oldVatStatus **/, VatConnectionStatus newVatStatus)
{
IVoiceChannel::ConnectionStatus oldStatus = m_roomStatus;
switch (newVatStatus)
@@ -86,219 +171,35 @@ namespace BlackCore
default:
break;
}
emit q_ptr->connectionStatusChanged(oldStatus, m_roomStatus);
emit connectionStatusChanged(oldStatus, m_roomStatus);
}
void CVoiceChannelVatlibPrivate::userJoinedVoiceRoom(VatVoiceChannel, int /** id **/, const char *name)
CVoiceChannelVatlib *cbvar_cast_voiceChannel(void *cbvar)
{
CCallsign callsign(extractCallsign(name));
m_listCallsigns.push_back(callsign);
for (const auto &e : m_voiceChannels)
emit e->userJoinedRoom(callsign);
return static_cast<CVoiceChannelVatlib *>(cbvar);
}
void CVoiceChannelVatlibPrivate::userLeftVoiceRoom(VatVoiceChannel, int /** id **/, const char *name)
void CVoiceChannelVatlib::processUserJoined(VatVoiceChannel channel, int id, const char *name, void *cbVar)
{
CCallsign callsign(extractCallsign(name));
m_listCallsigns.remove(callsign);
for (const auto &e : m_voiceChannels)
emit e->userLeftRoom(callsign);
auto obj = cbvar_cast_voiceChannel(cbVar);
obj->userJoinedVoiceRoom(channel, id, name);
}
void CVoiceChannelVatlibPrivate::transmissionChanged(VatVoiceChannel, VatVoiceTransmissionStatus status)
void CVoiceChannelVatlib::processUserLeft(VatVoiceChannel channel, int id, const char *name, void *cbVar)
{
if (status == vatVoiceStarted)
emit q_ptr->audioStarted();
else
emit q_ptr->audioStopped();
auto obj = cbvar_cast_voiceChannel(cbVar);
obj->userLeftVoiceRoom(channel, id, name);
}
CVoiceChannelVatlibPrivate *cbvar_cast_voiceChannelPrivate(void *cbvar)
void CVoiceChannelVatlib::processTransmissionChange(VatVoiceChannel channel, VatVoiceTransmissionStatus status, void *cbVar)
{
return static_cast<CVoiceChannelVatlibPrivate *>(cbvar);
auto obj = cbvar_cast_voiceChannel(cbVar);
obj->transmissionChanged(channel, status);
}
CCallsign CVoiceChannelVatlibPrivate::extractCallsign(const QString &name)
void CVoiceChannelVatlib::roomStatusUpdate(VatVoiceChannel channel, VatConnectionStatus oldStatus, VatConnectionStatus newStatus, void *cbVar)
{
CCallsign callsign;
if (name.isEmpty()) return callsign;
// callsign might contain: VATSIM id, user name
if (name.contains(" "))
{
QStringList parts = name.split(" ");
callsign = CCallsign(parts[0]);
// I throw away VATSIM id here, maybe we could use it
}
else
{
callsign = CCallsign(name);
}
return callsign;
}
void CVoiceChannelVatlibPrivate::processUserJoined(VatVoiceChannel channel, int id, const char *name, void *cbVar)
{
CVoiceChannelVatlibPrivate *voiceChannelPrivate = cbvar_cast_voiceChannelPrivate(cbVar);
voiceChannelPrivate->userJoinedVoiceRoom(channel, id, name);
}
void CVoiceChannelVatlibPrivate::processUserLeft(VatVoiceChannel channel, int id, const char *name, void *cbVar)
{
CVoiceChannelVatlibPrivate *voiceChannelPrivate = cbvar_cast_voiceChannelPrivate(cbVar);
voiceChannelPrivate->userLeftVoiceRoom(channel, id, name);
}
void CVoiceChannelVatlibPrivate::processTransmissionChange(VatVoiceChannel channel, VatVoiceTransmissionStatus status, void *cbVar)
{
CVoiceChannelVatlibPrivate *voiceChannelPrivate = cbvar_cast_voiceChannelPrivate(cbVar);
voiceChannelPrivate->transmissionChanged(channel, status);
}
/*
* Room status update
*/
void CVoiceChannelVatlibPrivate::onRoomStatusUpdate(VatVoiceChannel channel, VatConnectionStatus oldStatus, VatConnectionStatus newStatus, void *cbVar)
{
auto obj = cbvar_cast_voiceChannelPrivate(cbVar);
auto obj = cbvar_cast_voiceChannel(cbVar);
obj->updateRoomStatus(channel, oldStatus, newStatus);
}
// Get shared room data
QHash<CVoiceChannelVatlib * const, QSharedPointer<CVoiceChannelVatlibPrivate>> &CVoiceChannelVatlibPrivate::getSharedRoomData()
{
return m_sharedRoomData;
}
// Constructor
CVoiceChannelVatlib::CVoiceChannelVatlib(VatAudioService audioService, VatUDPAudioPort udpPort, QObject *parent)
: IVoiceChannel(parent),
d_ptr(new CVoiceChannelVatlibPrivate(audioService, udpPort, this))
{
}
// Destructor
CVoiceChannelVatlib::~CVoiceChannelVatlib()
{
d_ptr->m_voiceChannels.removeAll(this);
}
// Join room
void CVoiceChannelVatlib::joinVoiceRoom(const CVoiceRoom &voiceRoom)
{
// Find if a different channel is connected already to this voice room
auto roomDataList = CVoiceChannelVatlibPrivate::getSharedRoomData().values();
auto iterator = std::find_if(roomDataList.begin(), roomDataList.end(), [&](const QSharedPointer<CVoiceChannelVatlibPrivate> roomData)
{
return roomData->m_voiceRoom.getVoiceRoomUrl() == voiceRoom.getVoiceRoomUrl();
});
// If we found another channel
if (iterator != roomDataList.end())
{
// Increase the connection reference counter
(*iterator)->m_connectionRefCount++;
d_ptr = (*iterator);
// Assign shared room data to this channel index
CVoiceChannelVatlibPrivate::getSharedRoomData().insert(this, *iterator);
// Add ourselfes as listener
d_ptr->m_voiceChannels.push_back(this);
// Since the room is used already, we have to simulate the state changes
emit connectionStatusChanged(IVoiceChannel::Disconnected, IVoiceChannel::Connecting);
emit connectionStatusChanged(IVoiceChannel::Connecting, IVoiceChannel::Connected);
}
else
{
// No one else is using this voice room, so prepare to join
d_ptr->m_voiceRoom = voiceRoom;
Vat_SetRoomInfo(d_ptr->m_voiceChannel.data(), qPrintable(voiceRoom.getHostname()), 3782,
qPrintable(voiceRoom.getChannel()),
qPrintable(d_ptr->m_callsign.toQString()));
d_ptr->m_roomStatus = IVoiceChannel::Disconnected;
Vat_JoinRoom(d_ptr->m_voiceChannel.data());
CVoiceChannelVatlibPrivate::m_sharedRoomData.insert(this, d_ptr);
++d_ptr->m_connectionRefCount;
}
}
// Leave room
void CVoiceChannelVatlib::leaveVoiceRoom()
{
// If this room is not connected, there is nothing to do
if (d_ptr->m_roomStatus == IVoiceChannel::Disconnecting || d_ptr->m_roomStatus == IVoiceChannel::Disconnected) return;
// Decrease the connection reference counter
--d_ptr->m_connectionRefCount;
// If this was the last channel, connected to the room, leave it.
if (d_ptr->m_connectionRefCount == 0)
{
qDebug() << "Leaving voice room!";
Vat_DisconnectFromRoom(d_ptr->m_voiceChannel.data());
}
else
{
d_ptr->m_voiceChannels.removeAll(this);
// We need to assign a private class
// This automatically clears callsign list etc.
VatAudioService audioService = d_ptr->m_audioService;
VatUDPAudioPort udpPort = d_ptr->m_udpPort;
d_ptr.reset(new CVoiceChannelVatlibPrivate(audioService, udpPort, this));
CVoiceChannelVatlibPrivate::getSharedRoomData().insert(this, d_ptr);
// Simulate the state change
emit connectionStatusChanged(IVoiceChannel::Connected, IVoiceChannel::Disconnecting);
emit connectionStatusChanged(IVoiceChannel::Disconnecting, IVoiceChannel::Disconnected);
}
}
CCallsignList CVoiceChannelVatlib::getVoiceRoomCallsigns() const
{
return d_ptr->m_listCallsigns;
}
void CVoiceChannelVatlib::setMyAircraftCallsign(const CCallsign &callsign)
{
d_ptr->m_callsign = callsign;
}
BlackMisc::Audio::CVoiceRoom CVoiceChannelVatlib::getVoiceRoom() const
{
return d_ptr->m_voiceRoom;
}
bool CVoiceChannelVatlib::isMuted() const
{
return !d_ptr->m_outputEnabled;
}
void CVoiceChannelVatlib::setVolume(int volume)
{
d_ptr->setRoomOutputVolume(volume);
}
int CVoiceChannelVatlib::getVolume() const
{
// FIXME
return 100;
}
VatVoiceChannel CVoiceChannelVatlib::getVoiceChannel() const
{
return d_ptr->m_voiceChannel.data();
}
}

View File

@@ -65,7 +65,37 @@ namespace BlackCore
VatVoiceChannel getVoiceChannel() const;
private:
QSharedPointer<CVoiceChannelVatlibPrivate> d_ptr;
struct VatVoiceChannelDeleter
{
static inline void cleanup(VatProducerConsumer_tag *obj)
{
if (obj) Vat_DestroyVoiceChannel(obj);
}
};
BlackMisc::Aviation::CCallsign extractCallsign(const QString &name);
void userJoinedVoiceRoom(VatVoiceChannel, int id, const char *name);
void userLeftVoiceRoom(VatVoiceChannel, int id, const char *name);
void transmissionChanged(VatVoiceChannel, VatVoiceTransmissionStatus status);
void updateRoomStatus(VatVoiceChannel channel, VatConnectionStatus /** oldStatus **/, VatConnectionStatus newStatus);
static void processUserJoined(VatVoiceChannel channel, int id, const char *name, void *cbVar);
static void processUserLeft(VatVoiceChannel channel, int id, const char *name, void *cbVar);
static void processTransmissionChange(VatVoiceChannel channel, VatVoiceTransmissionStatus status, void *cbVar);
static void roomStatusUpdate(VatVoiceChannel channel, VatConnectionStatus oldStatus, VatConnectionStatus newStatus, void *cbVar);
BlackMisc::Aviation::CCallsign m_callsign; // Own callsign
BlackMisc::Audio::CVoiceRoom m_voiceRoom; // Voice Room
BlackMisc::Aviation::CCallsignList m_listCallsigns; // Callsigns connected to room
IVoiceChannel::ConnectionStatus m_roomStatus = IVoiceChannel::Disconnected; // Room connection status
VatAudioService m_audioService;
VatUDPAudioPort m_udpPort;
QScopedPointer<VatProducerConsumer_tag, VatVoiceChannelDeleter> m_voiceChannel;
};
}

View File

@@ -1,91 +0,0 @@
/* Copyright (C) 2014
* 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 and at http://www.swift-project.org/license.html. 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.
*/
#ifndef BLACKCORE_VOICE_CHANNEL_P_H
#define BLACKCORE_VOICE_CHANNEL_P_H
#include "voice_channel_vatlib.h"
#include "blackmisc/voiceroom.h"
#include "blackmisc/avcallsign.h"
#include <QObject>
#include <QMutex>
#include <atomic>
//! \file
namespace BlackCore
{
// Inhibit doxygen warnings about missing documentation
//! \cond PRIVATE
// Private Implementation of CVoiceChannelVatlib
class CVoiceChannelVatlibPrivate : public QObject
{
Q_OBJECT
public:
struct VatVoiceChannelDeleter
{
static inline void cleanup(VatProducerConsumer_tag *obj)
{
if (obj) Vat_DestroyVoiceChannel(obj);
}
};
// Default constructor
CVoiceChannelVatlibPrivate(VatAudioService audioService, VatUDPAudioPort udpPort, CVoiceChannelVatlib *parent);
// Destructor
~CVoiceChannelVatlibPrivate();
// Set room output volume
void setRoomOutputVolume(int volume);
void updateRoomStatus(VatVoiceChannel channel, VatConnectionStatus /** oldStatus **/, VatConnectionStatus newStatus);
BlackMisc::Aviation::CCallsign extractCallsign(const QString &name);
static void processUserJoined(VatVoiceChannel channel, int id, const char *name, void *cbVar);
static void processUserLeft(VatVoiceChannel channel, int id, const char *name, void *cbVar);
static void processTransmissionChange(VatVoiceChannel channel, VatVoiceTransmissionStatus status, void *cbVar);
static void onRoomStatusUpdate(VatVoiceChannel channel, VatConnectionStatus oldStatus, VatConnectionStatus newStatus, void *cbVar);
// Get shared room data for this channel
static QHash<CVoiceChannelVatlib * const, QSharedPointer<CVoiceChannelVatlibPrivate>> &getSharedRoomData();
BlackMisc::Aviation::CCallsign m_callsign; // Own callsign
BlackMisc::Audio::CVoiceRoom m_voiceRoom; // Voice Room
std::atomic<quint16> m_connectionRefCount; // Connection reference couting
std::atomic<bool> m_outputEnabled; // Is room output enabled?
BlackMisc::Aviation::CCallsignList m_listCallsigns; // Callsigns connected to room
std::atomic<IVoiceChannel::ConnectionStatus> m_roomStatus; // Room connection status
QList<CVoiceChannelVatlib *> m_voiceChannels;
VatAudioService m_audioService;
VatUDPAudioPort m_udpPort;
QScopedPointer<VatProducerConsumer_tag, VatVoiceChannelDeleter> m_voiceChannel;
static QHash<CVoiceChannelVatlib * const, QSharedPointer<CVoiceChannelVatlibPrivate>> m_sharedRoomData;
const static qint32 InvalidRoomIndex = -1; // Invalid room index
CVoiceChannelVatlib * const q_ptr = nullptr;
Q_DECLARE_PUBLIC(CVoiceChannelVatlib)
private:
void userJoinedVoiceRoom(VatVoiceChannel, int id, const char *name);
void userLeftVoiceRoom(VatVoiceChannel, int id, const char *name);
void transmissionChanged(VatVoiceChannel, VatVoiceTransmissionStatus status);
};
//! \endcond
}
#endif // guard

View File

@@ -39,9 +39,9 @@ namespace BlackCore
*/
CVoiceVatlib::~CVoiceVatlib() {}
std::unique_ptr<IVoiceChannel> CVoiceVatlib::createVoiceChannel()
QSharedPointer<IVoiceChannel> CVoiceVatlib::createVoiceChannel()
{
return make_unique<CVoiceChannelVatlib>(m_audioService.data(), m_udpPort.data(), this);
return QSharedPointer<IVoiceChannel>(new CVoiceChannelVatlib(m_audioService.data(), m_udpPort.data(), this));
}
std::unique_ptr<IAudioInputDevice> CVoiceVatlib::createInputDevice()

View File

@@ -11,6 +11,7 @@
#include <QString>
#include <QScopedPointer>
#include <QSharedPointer>
#ifdef Q_OS_WIN
#ifndef NOMINMAX
@@ -37,7 +38,7 @@ namespace BlackCore
virtual ~CVoiceVatlib();
//! \copydoc IVoice::createVoiceChannel()
virtual std::unique_ptr<IVoiceChannel> createVoiceChannel() override;
virtual QSharedPointer<IVoiceChannel> createVoiceChannel() override;
//! \copydoc IVoice::createInputDevice()
virtual std::unique_ptr<IAudioInputDevice> createInputDevice() override;