How to implement simple audio loopback in QT - qt

I need to loopback audio recorded from the microphone straight to the speaker using QT (qt 5.7 on Windows...) - assume I can't use window's internal mic->speaker loopback (enable "listen to this device" on the microphone panel).
Any direction how to do that?

Based on the discussion on https://forum.qt.io/topic/19960/qaudioinput-redirect-to-qaudiooutput/3
#include <iostream>
#include <QCoreApplication>
#include <QAudioInput>
#include <QAudioOutput>
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
const auto decideAudioFormat = [](const QAudioDeviceInfo& devInfo)
{
QAudioFormat format;
format.setSampleRate(8000);
format.setChannelCount(1);
format.setSampleSize(16);
format.setCodec("audio/pcm");
format.setByteOrder(QAudioFormat::LittleEndian);
format.setSampleType(QAudioFormat::SignedInt);
if (devInfo.isFormatSupported(format))
{
return format;
}
else
{
std::cerr << "Raw audio format not supported by backend, cannot play audio.\n";
throw 0;
}
};
QAudioInput audioInput(decideAudioFormat(QAudioDeviceInfo::defaultInputDevice()));
QAudioOutput audioOutput(decideAudioFormat(QAudioDeviceInfo::defaultOutputDevice()));
audioOutput.start(audioInput.start());
return app.exec();
}
Adding a buffer in-between
I've encountered this issue before. If I'm not mistaken, the read() and write() functions share the same pos() tracker. This is what happens:
QBuffer buffer;
buffer.open(QBuffer::ReadWrite); // buffer.pos() == 0
buffer.write(someData, 5); // Writes in positions 0 -- 4, buffer.pos() == 5;
buffer.write(someData, 5); // Writes in positions 5 -- 9, buffer.pos() == 10;
buffer.read(otherBuffer.data(), 10); // Tries to read from position 10 onwards. No data found
2 solutions:
Implement your own, separate read-position and write-position trackers. Then, call seek() in readData() and writeData() to set pos() -- point to the end of the data before writing, but point to the middle of the data before reading
Use 2 separate buffers, and copy bytes from the input buffer into the output buffer
So, with separate buffers:
#include <iostream>
#include <cassert>
#include <QCoreApplication>
#include <QAudioInput>
#include <QAudioOutput>
#include <QBuffer>
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QBuffer rdBuff;
QBuffer wrBuff;
wrBuff.open(QBuffer::WriteOnly);
rdBuff.open(QBuffer::ReadOnly);
QObject::connect(&wrBuff, &QIODevice::bytesWritten, [&wrBuff, &rdBuff](qint64)
{
// remove all data that was already read
rdBuff.buffer().remove(0, rdBuff.pos());
// set pointer to the beginning of the unread data
const auto res = rdBuff.seek(0);
assert(res);
// write new data
rdBuff.buffer().append(wrBuff.buffer());
// remove all data that was already written
wrBuff.buffer().clear();
wrBuff.seek(0);
});
const auto decideAudioFormat = [](const QAudioDeviceInfo& devInfo)
{
QAudioFormat format;
format.setSampleRate(8000);
format.setChannelCount(1);
format.setSampleSize(16);
format.setCodec("audio/pcm");
format.setByteOrder(QAudioFormat::LittleEndian);
format.setSampleType(QAudioFormat::SignedInt);
if (devInfo.isFormatSupported(format))
{
return format;
}
else
{
std::cerr << "Raw audio format not supported by backend, cannot play audio.\n";
throw 0;
}
};
QAudioInput audioInput(decideAudioFormat(QAudioDeviceInfo::defaultInputDevice()));
QAudioOutput audioOutput(decideAudioFormat(QAudioDeviceInfo::defaultOutputDevice()));
audioInput.start(&wrBuff);
audioOutput.start(&rdBuff);
return app.exec();
}

Related

writeCharacters() of QXmlStreamWriter crashed when writting the content of a big file to QBuffer

The following code use writeCharacters with small buffer size to writing the content of big file, but it seems it only works when writing the content to a xml file, but writeCharacters() will crash when writting the content of a big file to QBuffer, any solution? Thanks.
env: qt opensource 4.8.7; Visual Studio 2010; Windows 10;
big file: https://1drv.ms/u/s!Ap_EAuwC9QkXijbujBQcQk4Hat_O?e=KemgUY
#include <QtCore/QCoreApplication>
#include <QFile>
#include <QXmlStreamWriter>
#include <QBuffer>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QByteArray mContentBuffer;
QByteArray arr;
int pos, filesize;
QFile file("C:\\Work\\bigfile.xar"); //Szie: 300Mb
if(!file.open(QFile::ReadOnly))
{
return -1;
}
mContentBuffer = file.readAll();
file.close();
//QFile profile("C:\\Work\\profile.xml");
//if(!profile.open(QFile::WriteOnly|QFile::Truncate))
//{
// return -1;
//}
QBuffer buffer;
buffer.open(QBuffer::ReadWrite);
QXmlStreamWriter stream(&buffer);
stream.setAutoFormatting(true);
stream.writeStartDocument();
stream.writeStartElement("Profile");
//stream.writeTextElement("Content", mContentBuffer.toBase64());
stream.writeStartElement("Content");
pos = 0;
filesize = mContentBuffer.size();
while(pos<filesize){
arr = mContentBuffer.mid(pos, 2000000);
stream.writeCharacters(arr.toBase64());
pos+=arr.size();
}
stream.writeEndElement();
stream.writeEndElement(); // Profile
stream.writeEndDocument();
return 0;
}
Here is the goal for writing a big file into a QBuffer.
bool profileModified()
{
bool result = true;
QFile profile("C:\\Work\\profile.xml");
if(profile.exists())
{
QBuffer buffer;
buffer.open(QBuffer::ReadWrite);
QXmlStreamWriter stream(&buffer);
exportProfile(stream);
profile.open(QFile::ReadOnly);
QByteArray profileArr = profile.readAll();
buffer.seek(0);
QByteArray bufferArr = buffer.buffer();
result = (array_compare(profileArr, bufferArr) != 0);
profile.close();
}
return result;
}

Hod to do multiple instance of QTcpSocket objects for multiple connections to different servers?

i'm quite starting OOP so i can make incredible stupid things.
Anyway.
I've got 2 cameras (or 4) witch own their tcp server for providing MJPEG video stream.
on one base computer i need to handle 2 (or 4) tcp client connections to these units.
if i just have one instance, or one camera to handle, it works. but with multiple, it fail in bad tcp connections, I my mind, the issue should be with the instanciation of 2 QTcpsocket objects.
I tried with thread, with slots, still the same, when i instanciate more than one object of my client class, the connection read nothing or auto close. I have different TCP port for different units before you tell that.
have a look on my main code sirs:
MAIN.CPP
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QDebug>
#include <QImage>
#include "streamerclient.h"
#include "camimageprovider.h"
#include "streamerthread.h"
#include "streamerthread2.h"
#include<unistd.h>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
StreamerThread clientSat1(1238,"SAT1-SNTL");clientSat1.start();
CamImageProvider *camImageProviderSat1(new CamImageProvider());
CamImageProvider *camImageProviderSat2(new CamImageProvider());
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("camImageProviderSat1",camImageProviderSat1);
engine.addImageProvider("camSat1", camImageProviderSat1);
engine.rootContext()->setContextProperty("camImageProviderSat2",camImageProviderSat2);
engine.addImageProvider("camSat2", camImageProviderSat2);
const QUrl url(u"qrc:/baseApp001/main.qml"_qs);
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);//const QImage &)) ,Qt::DirectConnection
QObject::connect(&clientSat1, SIGNAL(newImage(QImage)),camImageProviderSat1, SLOT(updateImage(QImage)));
QObject::connect(&clientSat1, SIGNAL(newImage(QImage)),camImageProviderSat2, SLOT(updateImage(QImage)));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
CLIENT OBJECT DECLARATION
#ifndef STREAMERTHREAD_H
#define STREAMERTHREAD_H
#include <QThread>
#include <QTcpSocket>
#include<QDebug>
#include <QObject>
#include <QTimer>
#include <QVector>
#include <QVariant>
#include <QImage>
#include <QFuture>
class StreamerThread : public QThread
{
Q_OBJECT
Q_PROPERTY(NOTIFY newImage)
public:
StreamerThread(int port,char *satName,QObject *parent=0);
qint64 newTcpDataRead();
void run();
QImage img(){return m_Image;}
private:
QTcpSocket *socket;
int socketDescriptor;
QImage m_Image;
QByteArray m_baImage; // Variable contenant l'image reçue.
bool m_quit; // Variable permettant de savoir que l'application est en cours de fermeture.
char m_satName[16];
private slots:
void slotQuit(); // Slot appelé lors de la fermeture de l'application.
signals:
void newImage(const QImage &);
};
#endif // STREAMERTHREAD_H
CLIENT OBJECT DESCRIPTION
#include "streamerthread.h"
#include<iostream>
#include<QImage>
#include<QDebug>
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/highgui.hpp>
#include<opencv2/imgproc.hpp>
char datout=0;
int m_port=0;
StreamerThread::StreamerThread(int port,char *satName,QObject *parent)
{
m_quit = 0;
m_port=port;
strcpy(m_satName,satName);
this->socketDescriptor=port;
//m_Image = QImage(640,480, QImage::Format_RGB888);//RGB32);
m_Image = QImage(1280,720, QImage::Format_RGB888);//RGB32);
}
void StreamerThread::run()
{
qInfo()<<this<<" construit pour "<<m_satName <<"d: "<<this->socketDescriptor;
//socket = new QTcpSocket();
socket = new QTcpSocket();
socket->setSocketDescriptor(this->socketDescriptor);
// socket->connectToHost("127.0.0.1", 1234);
socket->connectToHost(m_satName, m_port);
if(!socket->waitForConnected(4000))
{
qDebug()<<"error "<<socket->errorString();
}
// socket->write(new char[4]{1,2,3,4});
// QByteArray buffer;
socket->flush();
std::cout<<"HostOk"<<std::endl;
m_quit=0;
while(m_quit == 0)
{
/* if(socket->state()==QTcpSocket::UnconnectedState)
{
qDebug()<<"deconnecte";
socket->close();
//delete socket;
socket->deleteLater();
sleep(2);
delete socket;
qDebug()<<"essai de reconnecter";
socket = new QTcpSocket();
socket->connectToHost(m_satName, m_port);
// socket->connectToHost("192.168.0.20", 1234);
if(!socket->waitForConnected(3000))
{
qDebug()<<"error "<<socket->errorString();
}
// socket->write(new char[4]{1,2,3,4});
//QByteArray buffer;
socket->flush();
std::cout<<"Host-Ok"<<std::endl;
}
*/
this->newTcpDataRead();
}
}
QImage Mat2QImage(cv::Mat const& src)
{
cv::Mat temp; //make the same cv::Mat
cvtColor(src, temp,cv::COLOR_BGR2RGB); //cvtColor Makes a copt, that what i need
QImage dest((const uchar *) temp.data, temp.cols, temp.rows, temp.step, QImage::Format_RGB888);
dest.bits(); //enforce deep copy, see documentation
return dest;
}
cv::Mat QImage2Mat(QImage const& src)
{
cv::Mat tmp(src.height(),src.width(),CV_8UC3,(uchar*)src.bits(),src.bytesPerLine());
cv::Mat result; //deep copy just in case (my lack of knowledge with open cv)
cvtColor(tmp, result,cv::COLOR_BGR2RGB);
return result;
}
qint64 StreamerThread::newTcpDataRead()
{
QDataStream in(socket);
in.setVersion(QDataStream::Qt_5_11);
// initialize data
QImage image;
QByteArray data;
static qint64 imageSize = 0;
QString currentSatName="";
QByteArray currentSatData;
socket->waitForReadyRead();
usleep(1000);
//le client envoie un QByteArrayavec la taille du jpeg en premier puis les datas
if ( 0 == imageSize ) {
if ( socket->bytesAvailable() < (int)sizeof(qint64) )
{
std::cout<<"error "<<std::endl;
return-1;
}
in >> imageSize;
in>>currentSatName;
in>>currentSatData;
// qInfo() << imageSize;
//std::cout<<currentSatName.toStdString()<<std::endl;
}
//le client envoie un QByteArrayavec les datas, une chaine jpeg a recoder avec cv::decode
if (socket->bytesAvailable() < imageSize ) {;}//return -2;
else
{
in>>data;
//Vers cv::Mat:
std::vector<uchar> vData(data.begin(), data.end());
cv::Mat matImg;
if(imageSize!=-1&&imageSize!=0&&data.size()>0)
{
matImg = cv::imdecode(cv::Mat(1,
imageSize,//vData.size(),
CV_8UC1,
&vData[0]), cv:: IMREAD_COLOR);
QImage image= Mat2QImage(matImg);
emit newImage(image);
}
else
{
qDebug()<<"matrice a -1";
qInfo()<<this<< " :erreur decodage tcp";
QImage image = QImage(640,480, QImage::Format_RGB888);
image.fill(QColor("red"));
socket->flush();
}
imageSize = 0;
//DATA OUT
QByteArray block;
QByteArray satData;
if(datout>=10)
{
QString satName="RATS";
satData.reserve(8);
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_5_11);
// initialize data
// serialize
out << qint64(0) <<satName<<satData;
out.device()->seek(0);
out << (qint64)(block.size() - sizeof(qint64)); // the size of the block to be sent
// send over TCP
qint64 written = socket->write(block);
socket->waitForBytesWritten();
datout=0;
}
datout++;
return image.sizeInBytes();
}
}
void StreamerThread::slotQuit()
{
m_quit = 1;
}
I really don't understand why it fail when i instanciate more than one client object
when it fail, it's loop here:
if ( 0 == imageSize ) {
if ( socket->bytesAvailable() < (int)sizeof(qint64) )
{
std::cout<<"error "<<std::endl;
return-1;
}
And it is the last instancied object that is running, the first created is keeping saying "error".
I really suppose it is related to socket, maybe instanciation is not done correctly.
i see that both are created with good TCP port.but, to validate my guess, i copied the
streamerthread.cpp
streamerthread.h
to a new file, a different object so:
streamerthread2.cpp
streamerthread2.h
so for the test i declare like this:
StreamerThread clientSat1(1238,"SAT1-SNTL");clientSat1.start();
StreamerThread2 clientSat2(1234,"SAT2-SNTL");clientSat2.start();
so i've one instance of 2 differents object....and it work here! stupid way of doing but it work....
I really don't want to do like this, i really want to us objects and their power.
please say me im stupid and my error is ->there<- .
many thanks all!
in Instantiation of my streamerSlot classe, i uses a static variable.
since you just have one ovbject instantiated, it seems to works.
BUT if you instantiate more than one object, the static declaration, is declared just one time...one time for every instantiations, this is why my instantiations was bad.
Hope this will help .
Regards

How to write data to a given serial port with qt?

#include <QSerialPort>
#include <QSerialPortInfo>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// Example use QSerialPortInfo
foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
// Example use QSerialPort
QSerialPort serial;
serial.setPort(info);
if (serial.open(QIODevice::ReadWrite))
//I try to send a string of hexadecimal numbers,seems not right
//serial.write(QByteArray("0xFF010100FFFFFF"));
serial.close();
}
return a.exec();
}
The example above shows how to open all available serial ports and then close them. But I want to open a given serial port, such as COM6, set its BaudRate,DataBits,Parity,StopBits ,FlowControl and then send a string of hexadecimal numbers.
This video will definitely help you: https://www.youtube.com/watch?v=UD78xyKbrfk
You can also find similar code here: https://cboard.cprogramming.com/cplusplus-programming/169624-read-write-serial-port.html
Example code:
#include <QSerialPort>
MySerialPort::MySerialPort()
{
serial = new QSerialPort(this);
openSerialPort();
}
void MySerialPort::openSerialPort()
{
serial->setPortName("COM3");
serial->setBaudRate(QSerialPort::Baud9600);
serial->setDataBits(QSerialPort::Data8);
serial->setParity(QSerialPort::NoParity);
serial->setStopBits(QSerialPort::OneStop);
serial->setFlowControl(QSerialPort::NoFlowControl);
if (serial->open(QIODevice::ReadWrite))
{
//Connected
}
else
{
//Open error
}
}
void MySerialPort::writeData(const QByteArray &data)
{
serial->write(data);
}

How can I use Qt to encrypt/decrypte/play a video?

I am looking for a way to encrypt an video file and then use decrypt it to ram and play it directly.
setMedia takes a QIOdevice as second argument:
#include <QMediaPlayer>
#include <QApplication>
#include <QFile>
int main(int argc, char ** argv)
{
QApplication app(argc,argv);
QString fileName = app.arguments().at(1);
QFile io(fileName);
io.open(QFile::ReadOnly);
QMediaPlayer player;
player.setMedia(QUrl("test.mp3"), &io);
player.play();
return app.exec();
}
but just in case you really mean QDataStream:
QDataStream is buffered io, QIODevice is direct io, they're not compatible, so you have to double buffer like this:
#include <QMediaPlayer>
#include <QApplication>
#include <QFile>
#include <QBuffer>
#include <QDataStream>
int main(int argc, char ** argv)
{
QApplication app(argc,argv);
QString fileName = app.arguments().at(1);
// our double buffer
QByteArray bufferSpace;
// our stream on which we can put "media" data
QDataStream stream(&bufferSpace, QIODevice::WriteOnly);
// this is just demo data
QFile io(fileName);
io.open(QFile::ReadOnly);
stream << io.readAll();
// open an IO Device on our buffer
QBuffer buffer(&bufferSpace);
buffer.open(QBuffer::ReadOnly);
// give the IO to the media player
QMediaPlayer player;
player.setMedia(QUrl("test.mp3"), &buffer);
player.play();
return app.exec();
}
edit
here's a faster version of that "crypto" code you posted without using a buffer for the entire file:
#include <QMediaPlayer>
#include <QApplication>
#include <QFile>
#include <QBuffer>
#include <QDataStream>
static const unsigned char key = 0xAB;
class MyFunnyCrypto : public QFile /*or subclass any other IO*/
{
protected:
virtual qint64 readData(char *data, qint64 maxSize)
{
qint64 r = QFile::readData(data, maxSize);
if (r > 0) {
for (qint64 i = 0; i < r; i++) {
data[i] = data[i]^key;
}
}
return r;
}
};
int main(int argc, char ** argv)
{
QApplication app(argc,argv);
QString fileName = app.arguments().at(1);
MyFunnyCrypto io;
io.setFileName(fileName);
io.open(QFile::ReadOnly);
// give the IO to the media player
QMediaPlayer player;
player.setMedia(QUrl("test.mp3"), &io);
player.play();
return app.exec();
}
Thanks to #aep .
It doesn't have anything to do with QDataStream, and it's quite simple.
Encryption:
unsigned char key = 0xAB;
QFile file("test.mp3");
file.open(QIODevice::ReadOnly);
QByteArray byteArray = file.readAll();
for(int i=1024;i<byteArray.length();i++){
byteArray[i] = byteArray[i]^key;
}
file.close();
QFile encrypted("encrypted.mp3");
encrypted.open(QIODevice::WriteOnly);
encrypted.write(byteArray);
encrypted.close();
And decryption:
unsigned char key = 0xAB;
QFile file("encrypted.mp3");
file.open(QIODevice::ReadOnly);
auto byteArray = file.readAll();
for(int i=1024;i<byteArray.length();i++){
byteArray[i] = byteArray[i]^key;
}
QBuffer buffer(&byteArray);
buffer.open(QBuffer::ReadOnly);
// give the IO to the media player
QMediaPlayer player;
player.setMedia(QUrl(), &buffer);
player.play();
In the for-loop, I want to declare i as qint64 rather than int. But the compiler shows QByteArray::​operator[](int i).

Save QList<int> to QSettings

I want to save a QList<int> to my QSettings without looping through it.
I know that I could use writeArray() and a loop to save all items or to write the QList to a QByteArray and save this but then it is not human readable in my INI file..
Currently I am using the following to transform my QList<int> to QList<QVariant>:
QList<QVariant> variantList;
//Temp is the QList<int>
for (int i = 0; i < temp.size(); i++)
variantList.append(temp.at(i));
And to save this QList<Variant> to my Settings I use the following code:
QVariant list;
list.setValue(variantList);
//saveSession is my QSettings object
saveSession.setValue("MyList", list);
The QList is correctly saved to my INI file as I can see (comma seperated list of my ints)
But the function crashes on exit.
I already tried to use a pointer to my QSettings object instead but then it crashes on deleting the pointer ..
QSettings::setValue() needs QVariant as a second parameter. To pass QList as QVariant, you have to declare it as a Qt meta type. Here's the code snippet that demonstrates how to register a type as meta type:
#include <QCoreApplication>
#include <QDebug>
#include <QMetaType>
#include <QSettings>
#include <QVariant>
Q_DECLARE_METATYPE(QList<int>)
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qRegisterMetaTypeStreamOperators<QList<int> >("QList<int>");
QList<int> myList;
myList.append(1);
myList.append(2);
myList.append(3);
QSettings settings("Moose Soft", "Facturo-Pro");
settings.setValue("foo", QVariant::fromValue(myList));
QList<int> myList2 = settings.value("foo").value<QList<int> >();
qDebug() << myList2;
return 0;
}
You might have to register QList as a meta-type of its own for it to work. This is a good starting point to read up on meta-types in Qt: http://qt.nokia.com/doc/4.6/qmetatype.html#details .
I was also struggling with this ... and I believe I now have a decent solution.
I hope this saves someone the trouble, it caused me.
#include <QCoreApplication>
#include <QSettings>
#include <QList>
#include <QDataStream>
#include <QVariant>
#include <QVariantList>
#include <QDebug>
#include <deque>
template <class T> static QVariant toVariant(const QList<T> &list)
{
QVariantList variantList;
variantList.reserve(list.size());
for (const auto& v : list)
{
variantList.append(v);
}
return variantList;
}
template <class T> static QList<T> toList(const QVariant &qv)
{
QList <T> dataList;
foreach(QVariant v, qv.value<QVariantList>()) {
dataList << v.value<T>();
}
return dataList;
}
void Gen()
{
QList<QString> data {"hello", "world","how", "are", "you"};
QList<int> ages {10,20,30,40};
QSettings setup("stuff.ini", QSettings::IniFormat);
setup.beginWriteArray("rules");
for (int groups=0;groups<3;groups++)
{
setup.setArrayIndex(groups);
setup.setValue("rule",QString("Rule-%1").arg(groups));
setup.setValue("data", toVariant (data));
setup.setValue("ages", toVariant (ages));
}
setup.endArray();
setup.sync();
}
void Read()
{
QSettings setupR("stuff.ini", QSettings::IniFormat);
int rule_count = setupR.beginReadArray("rules");
for (int groups=0;groups<rule_count;groups++)
{
setupR.setArrayIndex(groups);
QString nameRead = setupR.value("rule").toString();
QList<QString> dataRead = toList<QString>(setupR.value("data"));
qDebug() << "Rule " << groups;
qDebug() << "Rule Read" << nameRead;
qDebug() << "Data Read2" << dataRead;
}
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// Write
Gen();
// Read
Read();
exit(0);
//return a.exec();
}
You should end up with an INI file which looks like this ...
[rules]
1\ages=10, 20, 30, 40
1\data=hello, world, how, are, you
1\rule=Rule-0
2\ages=10, 20, 30, 40
2\data=hello, world, how, are, you
2\rule=Rule-1
3\ages=10, 20, 30, 40
3\data=hello, world, how, are, you
3\rule=Rule-2
size=3
The nice thing here is you can edit this outside of QT (Just be careful) ...

Resources