I have a simple UDP application in Qt Framework where udpSender
sends same data every second (QTimer
). Meanwhile udpReceiver
only shows the incoming data with qDebug
, for now. I have looked at some examples but they don't satisfy what I want to achieve.
My main goal is to have an audio call between two users, say user1 has port 500 and user2 has port 550. When user1 calls user2 it takes the audio input from let's say QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice();
and rather saving as a file on the disk (all the examples were saving recorded audio as a file) I want to send audio data directly to user2's port. How do I send that data?
There are few other questions:
I guess I need to use some kind of buffer. Do I need a buffer like QAudioBuffer
? How I do that? When using buffer, will that be the data I'm going to send?
When sending datagrams -supposing each of them has a limited size- how can I check if one is filled with data and ready to send to receiver port?
udpSender looks like this:
udpSender::udpSender() : QObject()
{
udpSocket = new QUdpSocket();
timer = new QTimer();
startBroadcasting();
connect(timer, SIGNAL(timeout()), this, SLOT(broadcastDatagram()));
timer->start(15);
}
void udpSender::startBroadcasting()
{
timer->start(1000);
}
void udpSender::broadcastDatagram()
{
TODO: send Audio
udpSocket->writeDatagram("1", 1, QHostAddress::Broadcast, 44550);
}
and Receiver is like this:
udpReceiver::udpReceiver(QWidget *parent)
: QWidget(parent)
{
udpSocket = new QUdpSocket(this);
udpSocket->bind(44550, QUdpSocket::ShareAddress); //change port
connect(udpSocket, SIGNAL(readyRead()),
this, SLOT(processPendingDatagrams()));
}
udpReceiver::~udpReceiver()
{
delete udpSocket;
}
void udpReceiver::processPendingDatagrams()
{
QByteArray datagram;
while (udpSocket->hasPendingDatagrams()) {
datagram.resize(int(udpSocket->pendingDatagramSize()));
udpSocket->readDatagram(datagram.data(), datagram.size());
qDebug() << datagram.constData();
}
}
Also there is a AudioInput example from Qt where it has writeData method but never used. The answer I'm looking for is something like that? It starts with initialization of InputTest
which calls initializeAudio
but that's simply it.
AudioInfo::AudioInfo(const QAudioFormat &format)
: m_format(format)
{
switch (m_format.sampleSize()) {
// case 8:
case 16:
switch (m_format.sampleType()) {
case QAudioFormat::UnSignedInt:
m_maxAmplitude = 65535;
break;
case QAudioFormat::SignedInt:
m_maxAmplitude = 32767;
break;
default:
break;
}
break;
// case 32:
default:
break;
}
}
void AudioInfo::start()
{
open(QIODevice::WriteOnly);
}
void AudioInfo::stop()
{
close();
}
qint64 AudioInfo::readData(char *data, qint64 maxlen)
{
Q_UNUSED(data)
Q_UNUSED(maxlen)
return 0;
}
qint64 AudioInfo::writeData(const char *data, qint64 len) //never used!
{
if (m_maxAmplitude) {
Q_ASSERT(m_format.sampleSize() % 8 == 0);
const int channelBytes = m_format.sampleSize() / 8;
const int sampleBytes = m_format.channelCount() * channelBytes;
Q_ASSERT(len % sampleBytes == 0);
const int numSamples = len / sampleBytes;
quint32 maxValue = 0;
const unsigned char *ptr = reinterpret_cast<const unsigned char *>(data);
for (int i = 0; i < numSamples; ++i) {
for (int j = 0; j < m_format.channelCount(); ++j) {
quint32 value = 0;
if (m_format.sampleSize() == 8 && m_format.sampleType() == QAudioFormat::UnSignedInt) {
value = *reinterpret_cast<const quint8*>(ptr);
} else if (m_format.sampleSize() == 8 && m_format.sampleType() == QAudioFormat::SignedInt) {
value = qAbs(*reinterpret_cast<const qint8*>(ptr));
} else if (m_format.sampleSize() == 16 && m_format.sampleType() == QAudioFormat::UnSignedInt) {
if (m_format.byteOrder() == QAudioFormat::LittleEndian)
value = qFromLittleEndian<quint16>(ptr);
else
value = qFromBigEndian<quint16>(ptr);
} else if (m_format.sampleSize() == 16 && m_format.sampleType() == QAudioFormat::SignedInt) {
if (m_format.byteOrder() == QAudioFormat::LittleEndian)
value = qAbs(qFromLittleEndian<qint16>(ptr));
else
value = qAbs(qFromBigEndian<qint16>(ptr));
} else if (m_format.sampleSize() == 32 && m_format.sampleType() == QAudioFormat::UnSignedInt) {
if (m_format.byteOrder() == QAudioFormat::LittleEndian)
value = qFromLittleEndian<quint32>(ptr);
else
value = qFromBigEndian<quint32>(ptr);
} else if (m_format.sampleSize() == 32 && m_format.sampleType() == QAudioFormat::SignedInt) {
if (m_format.byteOrder() == QAudioFormat::LittleEndian)
value = qAbs(qFromLittleEndian<qint32>(ptr));
else
value = qAbs(qFromBigEndian<qint32>(ptr));
} else if (m_format.sampleSize() == 32 && m_format.sampleType() == QAudioFormat::Float) {
value = qAbs(*reinterpret_cast<const float*>(ptr) * 0x7fffffff); // assumes 0-1.0
}
maxValue = qMax(value, maxValue);
ptr += channelBytes;
}
}
maxValue = qMin(maxValue, m_maxAmplitude);
m_level = qreal(maxValue) / m_maxAmplitude;
}
emit update();
return len;
}
InputTest::InputTest()
{
initializeWindow();
initializeAudio(QAudioDeviceInfo::defaultInputDevice());
}
void InputTest::initializeAudio(const QAudioDeviceInfo &deviceInfo)
{
QAudioFormat format;
format.setSampleRate(8000);
format.setChannelCount(1);
format.setSampleSize(16);
format.setSampleType(QAudioFormat::SignedInt);
format.setByteOrder(QAudioFormat::LittleEndian);
format.setCodec("audio/pcm");
if (!deviceInfo.isFormatSupported(format)) {
qWarning() << "Default format not supported - trying to use nearest";
format = deviceInfo.nearestFormat(format);
}
m_audioInfo.reset(new AudioInfo(format));
connect(m_audioInfo.data(), &AudioInfo::update, [this]() {
m_canvas->setLevel(m_audioInfo->level());
});
m_audioInput.reset(new QAudioInput(deviceInfo, format));
qreal initialVolume = QAudio::convertVolume(m_audioInput->volume(),
QAudio::LinearVolumeScale,
QAudio::LogarithmicVolumeScale);
m_volumeSlider->setValue(qRound(initialVolume * 100));
m_audioInfo->start();
toggleMode();
}
void InputTest::toggleMode()
{
m_audioInput->stop();
toggleSuspend();
// Change bewteen pull and push modes
if (m_pullMode) {
m_modeButton->setText(tr("Enable push mode"));
m_audioInput->start(m_audioInfo.data());
} else {
m_modeButton->setText(tr("Enable pull mode"));
auto io = m_audioInput->start();
connect(io, &QIODevice::readyRead,
[&, io]() {
qint64 len = m_audioInput->bytesReady();
const int BufferSize = 4096;
if (len > BufferSize)
len = BufferSize;
QByteArray buffer(len, 0);
qint64 l = io->read(buffer.data(), len);
if (l > 0)
m_audioInfo->write(buffer.constData(), l);
});
}
m_pullMode = !m_pullMode;
}
As much of you can see from miles away, I'm not experienced in this and have limited knowledge. I would really appreciate any help!
User contributions licensed under CC BY-SA 3.0