Rev 460 | Rev 466 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
/*
* Copyright (C) 2024 by Andreas Theofilu <andreas@theosys.at>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <cstring>
#include <QUdpSocket>
#include <QAudioSink>
#include <QAudioSource>
#include <QMediaDevices>
#include "tqintercom.h"
#include "tconfig.h"
#include "terror.h"
#define PACKETSIZE 16384
using std::min;
TQIntercom::TQIntercom(QObject *parent)
: QObject(parent)
{
DECL_TRACER("TQIntercom::TQIntercom()");
mSpkLevel = TConfig::getSystemVolume();
mMicLevel = TConfig::getSystemGain();
}
TQIntercom::TQIntercom(QObject *parent, INTERCOM_t ic)
: QObject(parent)
{
DECL_TRACER("TQIntercom::TQIntercom(INTERCOM_t ic)");
mSpkLevel = TConfig::getSystemVolume();
mMicLevel = TConfig::getSystemGain();
setIntercom(ic);
}
TQIntercom::~TQIntercom()
{
DECL_TRACER("TQIntercom::~TQIntercom()");
if (mRemote)
delete mRemote;
if (mMicrophone)
delete mMicrophone;
if (mUdpTalker)
{
if (mUdpTalker->isOpen())
mUdpTalker->close();
delete mUdpTalker;
}
if (mUdpListener)
{
if (mUdpListener->isOpen())
mUdpListener->close();
delete mUdpListener;
}
}
void TQIntercom::setIntercom(INTERCOM_t ic)
{
if (ic.ip.empty())
{
MSG_ERROR("No valid IP address!");
return;
}
if (ic.rxPort < 0 || ic.rxPort > 0x0ffff)
{
MSG_ERROR("Receiver port is invalid! (" << ic.rxPort << ")");
return;
}
if (ic.txPort < 0 || ic.txPort > 0x0ffff)
{
MSG_ERROR("Transmit port is invalid! (" << ic.txPort << ")");
return;
}
if (ic.rxPort == 0 && ic.txPort == 0)
{
MSG_ERROR("No transmit and no receive port!");
return;
}
if (ic.mode < 0 || ic.mode > 2)
{
MSG_ERROR("Invalid mode " << ic.mode << "!");
return;
}
if (ic.mode == 0 && ic.rxPort == 0) // listen / receive
{
MSG_ERROR("No network port for listening!");
return;
}
if (ic.mode == 1 && ic.txPort == 0) // talk / send
{
MSG_ERROR("No network port for talking!");
return;
}
mIntercom = ic;
mAudioFormat.setSampleRate(8000);
mAudioFormat.setSampleFormat(QAudioFormat::UInt8);
mAudioFormat.setChannelCount(1);
mAudioFormat.setChannelConfig(QAudioFormat::ChannelConfigMono);
mInitialized = true;
}
void TQIntercom::setSpeakerLevel(int level)
{
DECL_TRACER("TQIntercom::setSpeakerLevel(int level)");
if (level < 0 || level > 100)
return;
mSpkLevel = level;
if (mIntercom.mode == 0 || mIntercom.mode == 2)
{
if (mRemote)
mRemote->setVolume(level);
}
}
void TQIntercom::setMicrophoneLevel(int level)
{
DECL_TRACER("TQIntercom::setMicrophoneLevel(int level)");
if (level < 0 || level > 100)
return;
if (mIntercom.mode == 1 || mIntercom.mode == 2)
{
if (mMicrophone)
mMicrophone->setVolume(level);
}
}
void TQIntercom::start()
{
DECL_TRACER("TQIntercom::start()");
if ((mIntercom.mode == 0 || mIntercom.mode == 2) && mIntercom.rxPort) // listen
{
if (!spawnServer())
return;
if (mRemote)
delete mRemote;
mRemote = new QAudioSink(mAudioFormat, this);
mRemote->setVolume(mSpkLevel);
mRemote->start(mUdpListener);
}
if ((mIntercom.mode == 1 || mIntercom.mode == 2) && mIntercom.txPort) // talk
{
if (!connectTalker())
return;
if (mMicrophone)
delete mMicrophone;
mMicrophone = new QAudioSource(mAudioFormat, this);
mMicrophone->setVolume(mMicLevel);
mMicrophone->start(mUdpTalker);
}
}
void TQIntercom::stop()
{
DECL_TRACER("TQIntercom::stop()");
if ((mIntercom.mode == 0 || mIntercom.mode == 2) && mIntercom.rxPort) // listen
{
if (mRemote)
mRemote->stop();
if (mUdpListener)
mUdpListener->close();
}
if ((mIntercom.mode == 1 || mIntercom.mode == 2) && mIntercom.txPort) // talk
{
if (mMicrophone)
mMicrophone->stop();
if (mUdpTalker)
mUdpTalker->close();
}
}
void TQIntercom::setMute(bool mute)
{
DECL_TRACER("TQIntercom::setMute(bool mute)");
if (mIntercom.mode == 1 || mIntercom.mode == 2)
{
if (mMicrophone)
mMicrophone->setVolume(mute ? 0 : TConfig::getSystemGain());
}
}
bool TQIntercom::connectTalker()
{
DECL_TRACER("TQIntercom::connectTalker()");
// First we initialize the socket
QHostAddress hostAddress(QString(mIntercom.ip.c_str()));
if (mUdpTalker)
{
mUdpTalker->close();
delete mUdpTalker;
}
mUdpTalker = new QUdpSocket(this);
if (!mUdpTalker->bind(hostAddress, mIntercom.txPort))
{
delete mUdpTalker;
mUdpTalker = nullptr;
MSG_WARNING("Couldn't bind to host \"" << hostAddress.toString().toStdString() << "\" at port " << mIntercom.txPort << "!");
return false;
}
return true;
}
bool TQIntercom::spawnServer()
{
DECL_TRACER("TQIntercom::spawnServer()");
if (mUdpListener)
delete mUdpListener;
mUdpListener = new QUdpSocket(this);
if (!mUdpListener->bind(QHostAddress::Any, mIntercom.rxPort))
{
delete mUdpListener;
mUdpListener = nullptr;
return false;
}
connect(mUdpListener, &QUdpSocket::stateChanged, this, &TQIntercom::onStateChanged);
return true;
}
void TQIntercom::onStateChanged(QAbstractSocket::SocketState socketState)
{
DECL_TRACER("TQIntercom::onStateChanged(QAbstractSocket::SocketState socketState)");
if (socketState == QAbstractSocket::ConnectedState)
{
// connect socket to speaker
QAudioDevice info(QMediaDevices::defaultAudioInput());
if (!info.isFormatSupported(mAudioFormat))
{
MSG_WARNING("Raw audio device can not be captured!");
return;
}
}
}