I have a QByteArray, inside the QByteArray are multiple values of diferent datatypes which I want to extract. The difficulty by that is, that the values are in an defined length of x-bits, and they have a defined start position (also in bits).
eg: an int8 is stored inside the QByteArray from bit nr. 4 (of the first byte) to bit nr. 12 (inside the second byte).
On the Qt-wiki side i found a method to disassemble a byteArray into a bit array: https://wiki.qt.io/Working_with_Raw_Data
So i'm cutting my bits out of the byte array like that:
QByteArray MyCutter::cutMessage(qint32 bitStart, qint32 bitLength)
{
qDebug() << mBuffer;
QBitArray bits(mBufferLen * 8);
for(quint32 i=0; i<mBufferLen; ++i)
{
for(quint32 b=0; b<8; b++)
{
bits.setBit(i*8 +b, mBuffer.at(i) & (1<<(7-b)));
}
}
qDebug() << bits;
QByteArray bytes;
//round up to the next n*8 length of a byte: (length + y) = x*8
qint32 bitLengthWithZeros = (bitLength + (8 - 1)) & ~(8 - 1);
bytes.resize(bitLengthWithZeros/8);
bytes.fill(0);
for(qint32 b=bitStart ,c=0; b<(bitStart + bitLength); b++, c++)
{
bytes[c/8] = (bytes.at(c/8) | ((bits.testBit(b)?1:0)<<(7-b%8)));
}
qDebug() << bytes;
return bytes.data();
}
This is working fine so far - I can cut my ByteArray into any other.
The Problem is to convert the values into int/float/doubles, and to be more specific into signed values.
To convert i've tried two things:
QByteArray::toHex().toLong(nullptr, 16) ... toLong/toLongLong etc. This is working, but only returns me the UNSIGNED value of the QByteArray. If I'm cutting the mBuffer with the function MyCutter::cutMessage, like the excample above, from the 4. bit to the 12. (which is also 0xFF) im getting 255 as signed int! And that's wrong?
On the other side I've tried to convert it with QStremData:
QDataStream stream(mBuffer);
stream.setByteOrder(QDataStream::LittleEndian);
qint64 result;
stream >> result;
qDebug() << QString::number(result,16);
qDebug() << QString::number(result);
mBuffer are the raw data. If im putting "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" insde mBuffer, the printed value is -1 which is correct.
QByteArray h = cutMessage(0,8);
qDebug() << h.toHex().toLongLong(nullptr, 16);
QDataStream stream2(h);
stream2.setByteOrder(QDataStream::LittleEndian);
qint64 result2;
stream2 >> result2;
qDebug() << QString::number(result2,16);
qDebug() << QString::number(result2);
Converting the cut-Message with the code block above is always returning me "0".
So without cutting, the interpretation of the whole QByteArray is correct, but if I'am cutting something off it's returning me always the unsigned value, or "0".
Somehow I'am loosing some information while the transformation into QBitArray and vice versa.
Hopefully my explanations are understandable ;)
Related
I have a WAV file that I decode with the QAudioDecoder. As a result I have a QAudioBuffer object. I want to store the data stored in QAudioBuffer in a QByteArray for my QIODevice derived class. I want to use this data in the ReadData method of my derived class for audio output. I now have 2 questions:
How to get a QByteArray from a QAuddioBuffer?
I used the following code, but unfortunately this is not correct. The data in QAudioBuffer is coded to 2Bytes, but each element in a QByteArray is coded to 1Byte (right?). Don't we have a loss of information there? To test if QByteArray contains the original data from the WAV file, I save it to a TXT file.
is this approach appropriate? I actually want to apply some operations on the data stored in QAudioBuffer (e.g. filters) and listen to the result in real time.
Thanks in advance.
Here is the code
QAudioFormat *format_decoder;
format_decoder = new QAudioFormat;
format_decoder->setSampleRate(44100);
format_decoder->setChannelCount(1);
format_decoder->setSampleFormat(QAudioFormat::Int16);
QAudioDecoder decoder;
decoder.setSource(filenameSource);
decoder.setAudioFormat(*format_decoder);
decoder.start();
QObject::connect(&decoder, &QAudioDecoder::bufferReady, this, &MainWindow::slot_bufReady);
and the slot
void MainWindow::slot_bufReady(){
QAudioBuffer buffer = m_audioDecoder->read();
QByteArray buffer_ByteArray(buffer.constData<char>(), buffer.byteCount());
QFile file(filenameTest1);
if(!file.open(QIODevice::WriteOnly|QIODevice::Append)) {
qDebug() << "ERRO "; }
QTextStream strem(&file);
for(auto const dat: buffer_ByteArray) {
strem<< qreal(dat)/128.0<< "\r\n";
}
file.cloe();
This looks suspicious:
for(auto const dat: buffer_ByteArray) {
strem<< qreal(dat)/128.0<< "\r\n";
}
Your audio format is 16-bit mono. Reading it byte by byte is a non-starter. Read it sample by sample. That is, read two bytes at a time and convert. More likely this:
int16_t* data = (int16_t*)(buffer.data());
int samples = buffer.sampleCount();
for (int i = 0; i < samples; i++)
{
strem << data[i] << "\r\n";
}
The above will save your samples into a text file. You could plot it with Excel. But as others have said, that's not as useful as saving in as binary. You could prepend a WAV file header such that it can be played and analyzed with other tools.
Update
If your intent is to transcode from 16-bit to 8-bit, this is how you would likely do it:
int16_t* data = (int16_t*)(buffer.data());
QByteArray buffer_ByteArray(buffer.sampleCount(), '\0');
for (size_t i = 0; i < samples; i++) {
buffer_ByteArray[i] = (char)(data[i] / 256); // 16-bit to 8-bit
}
Note: some audio platforms use unsigned integers for 8-bit audio. That is the zero amplitude sample is 0x80. This is the case for 8-bit WAV files. If that's in play, then change this line:
buffer_ByteArray[i] = (char)(data[i] / 256); // 16-bit to 8-bit
To this:
char c = (char)(data[i] / 256); // 16-bit to 8-bit signed
const unsigned char mask = 0x80;
buffer_ByteArray[i] = (char)(mask ^ c);
QString samp_buff[100];
QByteArray data;
uint8_t speed;
samp_buff[3] = data.toHex(); //I converted the QByteArray into a string
qDebug() << "read_every_data_"<< samp_buff[3];
speed = samp_buff[3].toUInt(); //Trying to convert the string to uint8_t
qDebug() << "Converted to UINT8" << speed;
Hi! I successfully got the Qbytearray value (data) stored in as a QString in the samp_buff array of strings, and also during the conversion of QString to uint8_t in the form of hex.
Data: "\x07" //QByteArray
read_every_data_ "07" //QString
Converted to UINT8 7 //Uint8_t
Its working fine for this but the problem arises when this happens.
Data: "\x0B" //QByteArray
read_every_data_ "0b" //QString
Converted to UINT8 0 //Uint8_t
Whenever the hex string has alphabets in it, the result of the conversion becomes zero.
As the documentation of QString::toUint suggests, the signature of the function looks like this.
uint QString::toUInt(bool *ok = nullptr, int base = 10) const
The second argument base is for specifying the base. To convert from hex strings, supply 16 to it.
speed = samp_buff[3].toUInt(nullptr, 16);
This have been troubled me for days and I really have no clue so to trouble Y'all.
I have an input in Qt to get any number from the user input.
After the user input the number, I get the number and convert it into hexadecimal (base 16) after I convert it, if the hexadecimal is more than 1 bytes, I will split it into 1 bytes size. I have done all the conversion and splitting.
Now my problem is , after I convert and split, the hexadecimal stays in the data type of QString, but in order to sent inside the QBtyeArray, I need convert back to int.
Can you guys tell me is there any convenient way to convert QString back to the int? I have tried a lot of ways but all of the conversion give me the value of base 10, I want the value to be in base 16 but in int.
Example : The user input 10800 (base 10) in the lineEdit , I retrieve the 10800 from lineEdit and after that I convert it to a base 16, so the hexadecimal of 10800 is 2A30 , after I do the conversion , the value 2A30 is in string , may I know how to convert the type into int but the value still stays as 2A30 but not convert back to 10800 (base 10).
The closest answer I get is through this method
unsigned int value = QString("0x2A").toUInt(&ok, 16);
int abcde = sprintf(abc, "%x", value);
qDebug()<<QString::number(value);
QByteArray test_a = abc;
but either it returns me QByteArray or char, I want it in int, because I need to specify each bytes I send in writeDatagram() functions like this.
QByteArray datagram(4, '\x000');
datagram[0] = 0x02;
datagram[1] = 0x10;
datagram[2] = 0x00;
datagram[3] = 0x00;
Please tell me if my question is not clear enough. I'm using Qt 5.2.1
Thanks !!!
Why do you do the conversion etc.? I presume that you want to send an arbitrarily long integers in, effectively, base 256, i.e. one byte and at a time, instead of, say one decimal digit at a time. I also assume you want to represent them big endian, i.e. the most significant base-256 digit comes first. Say if you had to send 12384928, it'd be sent as bytes 188, 250, 160 (0xbc, 0xfa, 0xa0).
That's pretty easy to do:
QByteArray numberToBytes(const QString &number) {
QByteArray result;
bool ok = false;
auto value = number.toLongLong(&ok);
if (ok) {
int n = sizeof(value);
while (value && n--) {
result.append(quint8(value & 0xFF));
value = value >> 8;
}
std::reverse(result.begin(), result.end());
}
return result;
}
QString bytesToNumber(const QByteArray &bytes) {
qlonglong value = 0;
for (auto b : bytes)
value = (value << 8) | quint8(b);
return QString::number(value);
}
void test() {
Q_ASSERT(sizeof(qlonglong) == 8);
Q_ASSERT(numberToBytes("256") == QByteArray::fromRawData("\x01\x00", 2));
Q_ASSERT(numberToBytes("2134789") == QByteArray::fromRawData("\x20\x93 \x05", 3));
Q_ASSERT(numberToBytes("-58931") == QByteArray::fromRawData("\xFF\xFF\xFF\xFF\xFF\xFF\x19\xCD");
}
You might also consider numbers too long to fit in 8 bytes. Those require a slightly more involved radix change operation - after all, you don't really want to be doing repetitive long divisions. See this page for details.
But it really looks as if you want to simply send strings in datagrams. If you wish to append a checksum (here: CCITT CRC-16) to the data, that's not hard either, because Qt does it for us:
QByteArray serialize(const QString &str, bool withCRC = false) {
QByteArray result;
QDataStream ds(&result, QIODevice::WriteOnly);
ds << str;
if (withCRC) ds << qChecksum(result.constData(), result.size());
return result;
}
QString deserialize(const QByteArray &packet, bool withCRC = false) {
QString result;
QDataStream ds(packet);
ds >> result;
if (withCRC) {
quint16 crc;
ds >> crc;
crc ^= qChecksum(packet.data(), packet.size() - 2);
if (crc) return {};
}
return result;
}
The format of the datagram is as follows: length of the string (4 bytes), followed by each character in the string (2 bytes each - it's a QChar). The optional CRC is another 2 bytes. That's all there's to that.
If the string has only ASCII characters, then sending the UTF-8 representation will take half the space if the string is long:
QByteArray serialize(const QString &str, bool withCRC = false) {
QByteArray result;
QDataStream ds(&result, QIODevice::WriteOnly);
ds << str.toUtf8();
if (withCRC) ds << qChecksum(result.constData(), result.size());
return result;
}
QString deserialize(const QByteArray &packet, bool withCRC = false) {
QByteArray result;
QDataStream ds(packet);
ds >> result;
if (withCRC) {
quint16 crc;
ds >> crc;
crc ^= qChecksum(result.constData(), result.size());
if (crc) return {};
}
return QString::fromUtf8(result);
}
Perhaps you tried to make the string smaller by knowing ahead of time that it is a number, and thus representing it optimally. How long do you expect those strings to be, and what is your limit on the datagram size?
This might be a stupid question but I can't seem to find how to display a byte from a QByteArray as "01011000" for example.
That's because the function is not related to the scope of QByteArray, which is a simple byte container. Instead, you need to get the specific byte (as char) to print and show singles bits from it. For instance, try this (magic):
char myByte = myByteArray.at(0);
for (int i = 7; i >= 0; --i) {
std::cout << ((myByte >> i) & 1);
}
Assuming that your machine has 8-bit bytes (which is not as a bold statement as it would have been 20 years ago).
I have an application which consists of two primary modules. One is written in C, uses standard C runtime library and one written in Qt C++. They communicate with each other with IPC. C module creates a char array, fills it with data and sends to the module written in Qt. I want to deserialize received data using QDataStream, but my efforts didn't yield any result yet. Here's a simple example what I'm trying to achieve:
unsigned int pointer = 0;
const int IPC_MSG_LEN = 500;
const int IPC_MSG_HEADER = 200;
const int SOMETHING = 1443;
char api = 55;
char msg[IPC_MSG_LEN] = {0};
memcpy_s(msg, IPC_MSG_LEN, &IPC_MSG_HEADER, sizeof(int));
pointer = sizeof(unsigned int);
memcpy_s(&msg[pointer], IPC_MSG_LEN - pointer, &api, sizeof(char));
++pointer;
memcpy_s(&msg[pointer], IPC_MSG_LEN - pointer, &SOMETHING, sizeof(int));
QByteArray arr(msg, IPC_MSG_LEN);
QDataStream ds(&arr, QIODevice::ReadOnly);
qint32 header = 0, aa = 0;
qint8 t_api = 0;
ds >> header; //Doesn't work
ds >> t_api; //Works
ds >> aa; //Doesn't work
As you can see, the code is pretty simple, but header and aa variables are deserialized to a random number. However t_api (one byte variable) has correct value assigned.
So what's the problem with this code? Does QDataStream uses a private data format which is not compatible with the one I'm using? Should I write my own QIODevice implementation or there is a quick fix I'm not aware of? :)
Thanks, I appreciate your help.
UPDATE
Thank you very much guys, your solution worked perfectly with those primitive data types, but the problem is that I also need to be able to serialize/deserialize char* strings too.
wchar_t* name1 = L"something";
memcpy_s(&msg[pointer], IPC_MSG_LEN - pointer, name1, (wcslen(name1) + 1) * 2);
char* ai = new char[500];
ds >> ai; //ai becomes NULL here :|
Is there a way to achieve that? Thanks again
QDataStream::setByteOrder(QDataStream::LittleEndian);
#include <QDebug>
#include <QByteArray>
#include <QDataStream>
#include <QString>
#include <vector>
template<typename T> void writePtr(char*&dst, T data){
*reinterpret_cast<T*>(dst) = data;
dst += sizeof(T);
}
int main(int argc, char** argv){
const size_t ipcSize = 512;
std::vector<char> buffer(ipcSize, 0);
quint32 sendVal1 = 0x12345678, recvVal1 = 0;
quint8 sendVal2 = 0xee, recvVal2 = 0;
quint32 sendVal3 = 0x9999abcd, recvVal3 = 0;
char* dst = &buffer[0];
writePtr(dst, sendVal1);
writePtr(dst, sendVal2);
writePtr(dst, sendVal3);
QByteArray byteArray(&buffer[0]);
QDataStream stream(&byteArray, QIODevice::ReadOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream >> recvVal1 >> recvVal2 >> recvVal3;
qDebug() << QString(QObject::tr("sent: %1, received: %2")).arg(sendVal1, 8, 16).arg (recvVal1, 8, 16);
qDebug() << QString(QObject::tr("sent: %1, received: %2")).arg(sendVal2, 2, 16).arg(recvVal2, 2, 16);
qDebug() << QString(QObject::tr("sent: %1, received: %2")).arg(sendVal3, 8, 16).arg(recvVal3, 8, 16);
return 0;
}
but the problem is that I also need to be able to serialize/deserialize char* strings too.
Qt data serialization format is explained (in detail) here. You MUST read that document if you want to use QDataStream for IPC. Qt has nice documentation, so use it.
Also this is not a char* string:
wchar_t* name1 = L"something";
It is a wchar_t* string.
wchar_t has different size depending on compiler - either 4 or 2 bytes per wchar_t. Which means problem for IPC. unlike wchar_t, char is guaranteed to be 1 byte big.
So either encode entire string to UTF8 (or use 8bit-encoded string with known codepage/encoding) and write it as raw data in QByteArray-compatible format:
void writeDataPtr(char*& ptr, const char* data, quint32 size){
if (!data){
size = 0xffffffff;
writePtr(ptr, size);
return;
}
memcpy(ptr, data, size);
ptr += size;
}
Then use QString::fromUtf8 to decode it (or QTextCodec - if you decided to use other 8bit encoding instead of utf8). OR if you can ensure that your wchar_t* string is UTF16-compliant and sizeof(wchar_t) == 2, dump it in QString-compatible format.
By the way - If I were you, I'd avoid memcpy_s. It is not part of C++ standard, which is a very good reason to avoid it.
I want is to read wchar_t*/char* from QDataStream until stream position gets to null terminating character.
If this is homework, tag your post accordingly.
One of those should work:
QString readWcharString(QDataStream& stream){
QVector<ushort> rawData;
ushort tmp;
do{
stream >> tmp;
rawData.push_back(tmp)
}while(tmp);
return QString::fromUtf16(rawData.data());
}
or
QString readWcharString(QDataStream& stream){
QVector<wchar_t> rawData;
ushort tmp;
do{
stream >> tmp;
rawData.push_back(tmp)
}while(tmp);
return QString::fromWCharArray(rawData.data());
}
QDataStream stores the numbers in the big endian format by default.
You can change that with:
ds.setByteOrder(QDataStream::ByteOrder(QSysInfo::ByteOrder));
which will use the detected host endianness instead.