How to use QtMultimedia to play a wav file? - c++

My current code is:
void Sound::run() {
QFile audio_file(mResourcePath);
if(audio_file.open(QIODevice::ReadOnly)) {
audio_file.seek(44); // skip wav header
QByteArray audio_data = audio_file.readAll();
audio_file.close();
QBuffer* audio_buffer = new QBuffer(&audio_data);
qDebug() << audio_buffer->size();
QAudioFormat format;
format.setSampleSize(16);
format.setSampleRate(44100);
format.setChannelCount(2);
format.setCodec("audio/pcm");
format.setByteOrder(QAudioFormat::LittleEndian);
format.setSampleType(QAudioFormat::UnSignedInt);
QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
if (!info.isFormatSupported(format)) {
qWarning()<<"raw audio format not supported by backend, cannot play audio.";
return;
}
qDebug() << info.deviceName();
QAudioOutput* output = new QAudioOutput(info, format);
output->start(audio_buffer);
}
}
This whole thing is started as a QRunnable in a QThreadPool and that part works fine. Problem is I never get any audio. My sound device is operational, the buffer is filled. I don't know what's wrong. I use app.exec(). Help appreciated.

The device (QBuffer) has to be open:
QBuffer audio_buffer(&audio_data);
audio_buffer.open(QIODevice::ReadOnly);
QAudioOutput needs an event loop to play anything, and that loop has to be running in the thread it belongs to. Which is the thread it was created in when you don't explicitly move it to another thread:
// Create the device and start playing...
QAudioOutput output(info, format);
output.start(&audio_buffer);
// ...then wait for the sound to finish
QEventLoop loop;
QObject::connect(&output, SIGNAL(stateChanged(QAudio::State)), &loop, SLOT(quit()));
do {
loop.exec();
} while(output.state() == QAudio::ActiveState);
Everything you allocate should be deallocated when the sound has finished playing, or you would have a memory leak, and the event loop will now run inside the function, so you can allocate everything locally.

Related

Connect signal of superclass of object

I am working with a QProcess and have connected QProcess's signal readyReadStandardOutput().
That process normally spits out data to the console regularly, but the readyReadStandardOutput() seems to batch results and only emit every half a minute or so (with all accumulated data).
I want to access the "live feed" of the QProcess so I thought maybe QProcess's superclass QIODevice has some other signals.
Other solutions instead of using bytesWritten are also welcome
Now I'm trying to connect bytesWritten, but it doesn't let me.
Code:
void MainWindow::on_Program_clicked() {
program= new QProcess(this);
QString file = "../folder/program/program.exe";
QString directory = "../folder/program/";
//qint64 pid;
program->setWorkingDirectory(directory);
program->start(file, {""});
program->waitForStarted();
connect(program, SIGNAL(readyReadStandardOutput()), this, SLOT(readOutput()));
//ERROR: "QObject::connect: No such signal QProcess::bytesWritten() in ..\---\mainwindow.cpp:45
connect(program, SIGNAL(bytesWritten()), this, SLOT(myBytesWritten()));
}
void MainWindow::myBytesWritten() {
QProcess *program = dynamic_cast<QProcess *>(sender());
QByteArray outData = program->readAll();
qDebug() << "DEBUG: " + outData;
}
//Works, but only emits a signal every so often, and not every time a new line is written to the console as when I launch the exe normally
void MainWindow::readOutput(){
QProcess *program = dynamic_cast<QProcess *>(sender());
QByteArray outData = program->readAll();
qDebug() << "DEBUG: " + outData;
}
If you want to use the old, deprecated way of connecting signals to slots, you need to also include the parameter list:
connect(program, SIGNAL(bytesWritten(int)), this, SLOT(myBytesWritten()));
Or you can just use the modern, compile-time Qt5 way:
connect(program, &QProcess::bytesWritten, this, &MainWindow::myBytesWritten);
Also note that bytesWritten fires when a write command from YOUR end has succeeded. You don't seem to be sending any input so this will never fire.

QMediaPlayer duration() returns 0 always

I use Qt 5.7
I'm writing Music Player, and have one problem. Method duration() of QMediaPlayer always returns 0. How can I fix it?
Example of code:
QMediaPlayer *player = new QMediaPlayer;
player->setMedia(QMediaContent(QUrl(path)));
qDebug() << player->duration(); // returns 0
player->play(); // it works
You cannot do a player->duration() right after the player->setMedia(QMediaContent(QUrl(path)));.
Indeed, the QMediaPlayer::setMedia is asynchronous so if you call the duration right after it, the media won't be set yet and then the duration will be wrong.
From Qt documentation on setMedia:
Note: This function returns immediately after recording the specified source of the media. It does not wait for the media to finish loading and does not check for errors.
When the duration is updated, QMediaPlayer send a signal named durationChanged(qint64 duration). So that you need to do is to connect this signal with a lambda or a slot.
For example,
QMediaPlayer *player = new QMediaPlayer(this);
connect(player, &QMediaPlayer::durationChanged, this, [&](qint64 dur) {
qDebug() << "duration = " << dur;
});
QUrl file = QUrl::fromLocalFile(QFileDialog::getOpenFileName(this, tr("Open Music"), "", tr("")));
if (file.url() == "")
return ;
player->setMedia(file);
qDebug() << player->duration();
player->setVolume(50);
player->play();
the first qDebug will write 0 as your but the second in the lambda will write the new duration of the QMediaPlayer.

QAudioInput::notify() isn't executed while capturing sound from mic

I want to use QAudioInput to capture sound from mic, process it and then play. As I understood, I need to connect to notify signal and inside handler for it user readAll() function to get raw data. But the problem is, this handler function is never executed. Here is my code:
void MainWindow::on_pushButton_clicked()
{
QList<QAudioDeviceInfo> list = QAudioDeviceInfo::availableDevices(QAudio::AudioInput);
if(list.isEmpty())
{
qDebug() << "No audio input device";
return;
}
QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice();
QAudioFormat format;
// Set up the desired format, for example:
format.setSampleRate(44100);
format.setChannelCount(1);
format.setSampleSize(32);
format.setCodec("audio/pcm");
format.setByteOrder(QAudioFormat::LittleEndian);
format.setSampleType(QAudioFormat::UnSignedInt);
if (!info.isFormatSupported(format)) {
qWarning() << "Default format not supported, trying to use the nearest.";
format = info.nearestFormat(format);
}
qDebug() << "input device :" << info.deviceName();
audio = new QAudioInput(info, format);
qDebug("created()");
connect(audio, SIGNAL(notify()), this, SLOT(onNotify()));
audio->setNotifyInterval(10);
bufferInput = audio->start();
}
void MainWindow::onNotify()
{
qDebug() << "onNotify()";
//QByteArray array = bufferInput->readAll();
//bufferOutput->write(array);
}
(audio is of type QAudioInput* and bufferInput is of type QIODevice*)
and when I click the button, "input device = " and "created()" messages are displayed, but "onNotify()" isn't shown.
What am I doing wrong?
QAudioInput seems quite broken. Or I'm utterly misunderstanding something.
The only thing that worked reliably for me was to use the readyRead() signal of the QIODevice buffer returned by start(). Unfortunately, that isn't fired very often on my system (around every 40 to 60ms on my system).
What I've found is that notify() starts firing when I either call resume() on the QAudioInput (because it's in idle state after calling start()) or do a readAll() on the QIODevice (!). But at least with PyQt this leads to a stack overflow after a minute or so.
I would suspect the platform matters as well, as the actual QAudioInput implementation depends on the platform and audio system used (PulseAudio on Fedora 32 in my case).
Connecting a slot to readyRead signal of QAudioInput and reading from QIODevice with readAll is the correct solution. Why do you think 40 or 60 ms interval is inappropriate? QAudioInput object must capture some audio during this interval and send signal after it.

How to read HID devices (/dev/hidrawX) with Qt on Linux?

I'm working on a 'RepRap calibration tool' which would use a mouse attached to the printing platform to measure the movement of the platform.
Right now I'm stuck trying to read raw mouse data from /dev/hidrawX, but I'm unable to read any data.
So far I've tried:
First attempt:
QFile f("/dev/hidraw0");
f.readAll();
Reads nothing.
Second attempt:
m_file = new QFile("/dev/hidraw0");
m_sn= new QSocketNotifier(m_file->handle(), QSocketNotifier::Read);
m_sn->setEnabled(true);
connect(m_sn, SIGNAL(activated(int)), this, SLOT(readyRead()));
then on the readyRead SLOT:
qDebug()<<"Ready Read!!"<<m_file.bytesAvailable();
QTextStream d(&m_file);
qDebug()<< d.read(64);
This code fires the readyRead slot once but it gets stuck on the read(64) call, if I comment the read(64) the slot will be fired each time the mouse its moved.
m_file.bytesAvailable() always reports 0.
Which is the right way to read these devices with Qt?
Solution:
I reworked the code like:
bool rcMouseHandler::openHidraw(QString device)
{
int fd =open(device.toLocal8Bit(),O_NONBLOCK);
if(fd <=0)
{
qDebug()<<"[WARN]rcMouseHandler::open-> Cant open!";
return false;
}
m_sn= new QSocketNotifier(fd, QSocketNotifier::Read);
m_sn->setEnabled(true);
connect(m_sn, SIGNAL(activated(int)), this, SLOT(readyRead()));
return true;
}
void rcMouseHandler::readyRead()
{
qDebug()<<"Ready Read!!";
char buffer[4] = {0,0,0,0};
read(m_sn->socket(),&buffer,4);
qDebug()<<(quint8)buffer[0]<<(quint8)buffer[1]<<(quint8)buffer[2]<<(quint8)buffer[3];
}
The right way I suppose here to not use Qt. Why you need portable wrapper above POSIX open and read, when this part of your code is not portable (part that work with /dev/*). Open device with "open" "man 2 open" in O_NONBLOCK and call "read" (man 2 read) to get data from it. And you can still use QSocketNotifier with handle that return "open".

How to capture video from camera via ethernet socket

I need to capture video from a live camera plugged to my PC ethernet socket. I used Phonon to capture video from a file in my system first. It works fine. Then, I created a socket to read the video. Here, I do not know how to get the buffered data and set it as a source to my Phonon video! I would thank if anyone could help me with this.
Here's the code to read a video :
void PlayVideo::rollOn()
{
media = new Phonon::MediaObject(movieLabel);
media->setCurrentSource(Phonon::MediaSource(QString("/home/saman/4.7/phonon_test/sample.mp4")));
videoPlayer = new Phonon::VideoPlayer(Phonon::VideoCategory, movieLabel);
videoPlayer->setFixedSize(QSize(400, 300));
videoPlayer->show();
connect(videoPlayer, SIGNAL(finished()), videoPlayer, SLOT(deleteLater()));
videoPlayer->play(media->currentSource());
}
and this is how I added sockets to the code:
void PlayVideo::rollOn()
{
udpSocketin = new QUdpSocket(this);
udpSocketin->bind(localPort);
connect(udpSocketin, SIGNAL(readyRead()),this, SLOT(readDatagrams()));
QDataStream out(&datagramout, QIODevice::WriteOnly);
out.setVersion (QDataStream::Qt_4_7);
timer2 = new QTimer(this);
connect(timer2, SIGNAL(timeout()), this, SLOT(playbuff()));
media = new Phonon::MediaObject(movieLabel);
media->setCurrentSource(Phonon::MediaSource(QString("/home/saman/4.7/phonon_test/sample.mp4")));
//media->setCurrentSource (Phonon::MediaSource());
videoPlayer = new Phonon::VideoPlayer(Phonon::VideoCategory, movieLabel);
videoPlayer->setFixedSize(QSize(400, 300));
videoPlayer->show();
connect(videoPlayer, SIGNAL(finished()), videoPlayer, SLOT(deleteLater()));
videoPlayer->play(media->currentSource());
}
void PlayVideo::readDatagrams()
{
if(udpSocketin->hasPendingDatagrams ())
{
datagramin.resize (udpSocketin->pendingDatagramSize ());
qint64 receiveBytes = udpSocketin->readDatagram (datagramin.data (), datagramin.size ));
if(receivedBytes <= 0)
{
qDebug("receivedBytes <= 0");
}
}
}
You can put your data into a QBuffer, which is a subclass of QIODevice. Then, there's a media source constructor that accepts a QIODevice.