QAudiobuffer how to get left and right channel from Qbytearray for playback in QAudioOutput
-
Hi all, Currently I'm stuck and the Qt documentation isn't helping me at all.
I need a way to get and play the individual left and right audio channel of a QByteArray that contains PCM data.
So far it looks like QAudioBuffer is the class to use however how do I use it.It doesn't have any members that I can find that I can fill it with a QByteArray.
or maybe Im completely on the wrong track.I don't understand the QAudioBuffer class and how to use it.
#include "Sound.h" #include <QDebug> #include <QtCore> #include <QtMultimedia/QAudioOutput> #include <QAudioBuffer> int sampleRate = 44100; int channelCount = 2; int sampleSize = 16; const QString codec = "audio/pcm"; void SoundSystem::playSound(bool wait, qreal amplitude, float frequency, int msecs) { soundBuffer = new QByteArray(); format = new QAudioFormat(); format->setSampleRate(sampleRate); format->setChannelCount(channelCount); format->setSampleSize(sampleSize); format->setCodec(codec); format->setByteOrder(QAudioFormat::LittleEndian); format->setSampleType(QAudioFormat::UnSignedInt); output = new QAudioOutput(*format, this); outputBuffer = new QBuffer(soundBuffer); if (outputBuffer->open(QIODevice::ReadOnly) == false) { qCritical() << "Invalid operation while opening QBuffer. audio/pcm"; return; } msecs = (msecs < 50) ? 50 : msecs; qreal singleWaveTime = amplitude / frequency; qreal samplesPerWave = qCeil(format->sampleRate() * singleWaveTime); quint32 waveCount = qCeil(msecs / (singleWaveTime * 1000.0)); quint32 sampleSize = static_cast<quint32>(format->sampleSize() / 8.0); QByteArray data(waveCount * samplesPerWave * sampleSize * format->channelCount(), '\0'); unsigned char* dataPointer = reinterpret_cast<unsigned char*>(data.data()); for (quint32 currentWave = 0; currentWave < waveCount; currentWave++) { for (int currentSample = 0; currentSample < samplesPerWave; currentSample++) { double nextRadStep = (currentSample / static_cast<double>(samplesPerWave)) * (2 * M_PI); quint16 sampleValue = static_cast<quint16>((qSin(nextRadStep) > 0 ? 1 : -1) * 32767); for (int channel = 0; channel < format->channelCount(); channel++) { qToLittleEndian(sampleValue, dataPointer); dataPointer += sampleSize; } } } connect(output, SIGNAL(stateChanged(QAudio::State)), this, SLOT(handleAudioStateChanged(QAudio::State))); // Here I need to have Left, Right or Stereo channel data for playback. soundBuffer->append(data); output->start(outputBuffer); // Output is a QAudioOutput if(wait) { QEventLoop *loop = new QEventLoop(); QObject::connect(output, SIGNAL(stateChanged(QAudio::State)), loop, SLOT(quit())); do { loop->exec(QEventLoop::WaitForMoreEvents); if(output->state() == QAudio::StoppedState) { qDebug() << "Recieved Hard Stop"; output->stop(); soundBuffer->clear(); } } while(output->state() == QAudio::ActiveState || output->state() == QAudio::SuspendedState); // Susspend to make pause working delete (loop); } soundBuffer->clear(); // Flush Buffer to append new sequance. else it will cause std::bad_alloc for large wave data. }
How can I achieve this using Qt Libs only ? any help would be greatly appreciated.
Im using latest Qt 5.9.3 and Phonon isnt supported anymore -
Hi,
If you have a working solution that uses Phonon, then you might want to check the phonon4qt5 project.
-
Sadly no I don't have a working solution, Im completely stuck right now. Ive read that Phonon has or had the ability to get the channel data separately.
As Im using generated PCM data I just need a way to get the left and right channels data separately for playback. Either on left only or right only or both at the same time.
-
Since it's interleaved data, you can mask the left or right data before putting it into the QByteArray.
-
Could you maybe provide an example, I have not done masking yet.
one thing i tried was:
QByteArray leftSide, rightSide;
for(int i = 0; i < data.length() / 2; i+=2) { leftSide[i] = data[i]; rightSide[i+1] = data[i+1]; } soundBuffer->append(rightSide);
which didnt do much other then lower the volume but the sound still plays from both left and right channels.
-
quint16 input = 0xFFFF; quint16 leftOnly = input & 0x00FF; quint16 rightOnly = input & 0xFF00;
-
Still Lost. Ive tried a few things non of it seems to work for me. No audio on either channel. Im unclear on how
quint16 input = 0xFFFF;
Where does my QbyteArray data come into play if the quint16 is set ?
Also I appriciate your help in this so far. Its just im lost.QDataStream dataStream(data); quint16 input; dataStream >> input; quint16 leftOnly = input & 0x00FF; quint16 rightOnly = input & 0xFF00; QByteArray LeftSideData; QDataStream out(&LeftSideData, QIODevice::WriteOnly | QIODevice::Append); //out.setByteOrder(QDataStream::LittleEndian); out << leftOnly; soundBuffer->append(LeftSideData); // soundBuffer->append(RightSideData);
No audio when running Function.
-
If I look at the code, I assume you are masking 8 bits of data each time.
Is your audio really 8 bit or do you use 16 bit resolution? Then I think you need to take every interleaved 16 bit for left and right (but I haven't looked up the PCM audio specs, so I might be wrong).
Regards
-
Since you want to do some "mixing" with your audio channels. What about using the DSPFilters project ?
They provide a nice interface to do interleaving/de-interleaving and other manipulations that you might find useful.
-
When I create a test .Wav file by writing Wav header then this data then close and adjusting the header here is the data of the file. If that would help.
- Format : Wave
- File size : 30.5 MiB
- Duration : 3 min 1 s
- Overall bit rate mode : Constant
- Overall bit rate : 1 411 kb/s
- Format : PCM
- Format settings : Little / Signed
- Codec ID : 1
- Duration : 3 min 1 s
- Bit rate mode : Constant
- Bit rate : 1 411.2 kb/s
- Channel(s) : 2 channels
- Sampling rate : 44.1 kHz
- Bit depth : 16 bits
- Stream size : 30.5 MiB (100%)
The sampleSize = 16
-
Its not really mixing, It more just splitting the PCM to go either right or left. or play in stereo on the PC. Nothing more.
I'll have a look at the project. But Id really prefer to use Qt Libs only if at all possible. I didn't think this would turn out to be such a mission hehe.Can't the QAudioBuffer class do this. Ive look st the documentation and they refer to left and right but i don't understand the Library as the documentation provided is very limited. Also please note that I'm new to Audio programming and Im also not a C++ expert by far. Its like stumbling around in the dark at this stage for me.
-
Ok so I have figured out a solution that will suite my needs
After constructing the Wav and getting the PCM data into a QByteArray The format in the buffer for PCM 16 Bit will be as follows (L,L,R,R,L,L,R,R....) so every two bytes are sent to one channel (pcm 16) then I nullify the channel I want to cancel so If I want the the left channel only to work the buffer will be (L,L,0,0,L,L,0,0) or visa versa for right.So I don't need any external Libs and licenses for this project. Pure C++ and Qt Libs.
Thanks to @SGaist trying to help me. I did learn more about masking through this ordeal even tough I ended up not using masking it in the final solution.