# Playing Wav over modem.

• Hello I look for a way to cut audiofile (Wav) into a buffer and send it to open com.
As you can see with this example, i wonder how traduce this into Qt language?

First: open a stream from wav file

• Hi,

QSerialPort serialPort;
// open serialPort with checks
QFile waveFile("/path/to/wav/file.wav");
// open waveFile with checks


• EDIT Thanks @SGaist for the guidance

QSerialPort serial(QStringLiteral("Com2"));
serial.setBaudRate(115200);
serial.setParity(QSerialPort::NoParity);
serial.setDataBits(QSerialPort::Data8);
serial.setStopBits(QSerialPort::OneStop);
// open serialPort with checks
if(serial.open(QIODevice::WriteOnly)){
serial.write(QByteArrayLiteral("AT+VTX\r"));
QFile wavFile(R"**(C:\Users\Master\Desktop\Prmpt445.wav)**");
// open waveFile with checks
// for(qint64 fileSize = wavFile.size();fileSize>0;fileSize-=1024) serial.write(wavFile.read(1024)); //1024 chunks version
}
}
// you probably need just 1 (the first probably) connect in reality
serial.write(QByteArrayLiteral("AT+FCLASS=8\r"));
serial.write(QByteArrayLiteral("ATDT1231234\r"));
}


• sammachin said :
"You are correct in sending WAV (or RAW PCM Audio to be exact, WAV has a short header but that doesn't cause a problem) It should be 16bit 16Khz Mono with a 20ms frame size, at 16Hkz each 20ms frame will contain 320 samples of 16bits each so that give you the 640byte length."

In fact, if isend Wavefile without buffering, the "old" Modem can't play sound. First, i need to transform wav to 16bit 16Khz Mono.
I have to send Hayes Command AT+VSM=128, 8000 . I need perhaps to adapt the following code bellow.

 for(qint64 fileSize = wavFile.size();fileSize>0;fileSize-=1024)
sleep(20); }
]


It's not won yet.

• Hello, no way to play it.
Have you seen a paper which talk about the method to send data correctly?
I think i need to temporize the data with the number "frame". I don't find the command like sleep in Qt 's documentation.
do you have any idea?

• Hi
"this shouldn't be required any longer since an update to the interface means you can send your audio as fast as you like and we will buffer it our end."

So are you sure you need to send in slices ?

• My purpose is to make an answering machine with a 56k PCIe modem.
I don't think the caller has the capability to store the wav data.
So, perhaps the command AT+VTX or AT+VSM=16,8000 will be necessary.

Cmd-AT+VSM=?;
0,"Signed PCM",16,0,8000,(0,122-148),0
4,"G.711U",8,0,8000,(0,122-148),0
5,"G.711A",8,0,8000,(0,122-148),0
OK

Obviously, i need to send a 16 bits per sample with a 8000hz audio sampling rate.
The propriety of my wav is 128kb/s. In 16b, 128kb/s is equivalent 128k/16 = 8000Hz...

Wikipedia say
" The modem re-raises the signal in time for the computer to resume sending audio data before the playback buffer becomes completely empty."

I don't really understand what this really means.

• An idea, but Qfile as private gives some trouble to be declared.
Testing a QTextStream but no way at now.

private:
QFile *wavFile;
QTextStream *flux;
qint64 fileSize;
QTimer *timer;


the void :

    wavFile= new QFile ("msg2.wav");

timer = new QTimer(this);
fileSize = wavFile.size(); //usefull when try wavFile as pointor to the Slot
connect(timer, SIGNAL(timeout()), this, SLOT(sendWave()));
flux= new QTextStream (wavFile);
timer->start(200);            //1000 =>1s
qDebug() <<"Send Data";

} else { qDebug() << "no way to open wav file"; }


the slot:

void MainWindow::sendWave(){
if (!flux->atEnd()) {
} else {
qDebug() <<"End Send Data";
timer->stop();
}
//      }


This always loops...
flux->realNumberPrecision() give the same number before and after flux->readLine().

• void MainWindow::sendWave(){
if (!flux->atEnd()) {

m_serial->write(tmp);
m_serial->flush();

flux->seek(flux->pos()+1024);
} else {
qDebug() <<"End Send Data";
timer->stop();
}

}


Need to use pos and seek
But wav dont play sound

Perhaps the timer is so slow.

With a wav coding in 16 bit 8000hz , 1024b corresponds to 8ms.
My calcul seem to be true. My wave during 1m56s for 1814 loops. So, one loop is during 8.5ms.
In fact, the program don't send data during more the 1 minute...
So surprise.

Try with 5 and try to see it the modem <DTE> send full buffer ...

Not easy to go blind.
Need more help.

put this because compilator sends an error.
as we can see, it is not look like some hexadecimal blocks.
Different lengths and hexabits are traduced to arabic words..

1-P��(С�%WAVEfmt 
[.. and after..]
1-ض�(
[.. and after..]
1-ض�(ض�(


• void MainWindow::sendWave(){
if (!flux->atEnd()) {

            const char *tmp= flux->read(1024).toStdString().c_str();
m_serial->write(tmp);
m_serial->flush();

flux->seek(flux->pos()+1024);
} else {
qDebug() <<"End Send Data";
timer->stop();
}


}

This code looks odd. How about:

if (!flux->atEnd()) {
m_serial->write(data);
m_serial->flush();
}


Need to use pos and seek

If you read a file sequentially, no. Every read(1024) puts the read pointer 1024 byte further. You may need to check if you have read less then 1024 byte at the end. Note: readLine() does not work well for binary files like wave.

• @aha_1980 said in Playing Wav over modem.:

if (!flux->atEnd()) {
m_serial->write(data);
m_serial->flush();
}

mainwindow.cpp:396: erreur : conversion from 'QString' to non-scalar type 'const QByteArray' requested

• mainwindow.cpp:396: erreur : conversion from 'QString' to non-scalar type 'const QByteArray' requested

Ah, you read from a QTextStream. Citing the documentation: "The QTextStream class provides a convenient interface for reading and writing text." This class converts your file data to QString, which is not what you want!

Well, you don't want to write text! You want to write binary data. So either operate directly on QFile (as I would do) or dig into QDataStream.

Regards.

• Thanks for info.
So i try again.

• @An-other-french

Another tip: get yourself a serial port monitor to see what you actually write to the serial port. This makes also sure you get the timing right.

Good luck :)

• QByteArray data = wavFile->read(1024);


Ok for a truncate data of 1024b , qDebug show this. I put a newline after "

"\xB3\xDEV\xDD\xAA\xDB"
"c\xDA\xC6\xDA\t\xDD\xF4\xE0^\xE5\x89\xEB\xAC\xF0"
"7\xF6\x13\xFA\x9A\xFD\xCB\xFF"
"5\x02|\x04\xC3\x07\x84\x0B\x17\x12\xE0\x19\x90%\\0\xA7<>E!M\xF8Q7V|X\xC1YpY\xB4WBU&Q\xFAL.G\xBE"
"A7:R3\xA8*\\#\xAA\x1A\xCA\x13\xC5\x0B\xF6\x04\x8A\xFC)\xF5\xED\xEB\x96\xE2\n\xDB\x96\xD2"
"1\xCCS\xC5K\xC0\xF9\xBA\x1E\xB7P\xB3\x06\xB1v\xAF \xAF\xC8\xAFX\xB1\xD7\xB3"
"0\xB6\xEC\xB8\x96\xBBQ\xBF\xC8\xC2\x00\xC7j\xCA"
"2\xCE""3\xD1v\xD5\x9C\xD9\xC5\xDE\xC2\xE2\xDC\xE6\xF6\xE9t\xED\xD2\xF0"
"9\xF5\xD9\xF8~\xFC\xC0\xFE\x98\x00\x83\x01\xA8\x02$\x04\x94\x05g\x07\xC2\bf\n&\f\x98\x0E\x8C\x10p\x12\xB0\x13\xCB\x14\xC8\x15(\x17"  this part is strong : %\\0\xA7<>E!M\xF8Q7V|X\xC1YpY\xB4WBU&Q\xFAL.G\xBE  I except to read something like this: a9 de ce e4 86 e3 af e1 c8 de 64 e1 97 e0 ad df  Sure there is a conversion somewhere. the last 16b line: Qbebug \xE5\x06 \x05$\x06\xFF\x06\x13\x05*\xFF
wave file e5 06 20 05 24 06 ff 06 13 05 2a ff


I can anderstand /x but \$ or * is an other way.

Ps: My purpose is to play wave on the phone

• @An-other-french to output binary data for debugging, I recommend you to use QByteArray::toHex().

• No way to play any sound.
In the purpose to see modem response, i want to show Modem answer.
So, i need to connect the serial com to the text object

connect (m_serial, &QSerialPort::readyRead, this, &MainWindow::readData);

void MainWindow::readData(){
...
ui->textEdit->append("1-"+ data.left(data.size()));
...
}


My function don't make difference between input or output(response).

Do you have a idea to show them distinctly?

• Your connect and the readAll in the slot looks good so far.

data.left(data.size())

What do you expect from this construct? It should be the same as data alone.

As suggested earlier, use a serial port monitor to see which commands you actually send to the modem and wheter it answers.

• @aha_1980 said in Playing Wav over modem.:

data.left(data.size())

It's a tentative to cut the data. Just data is better.

As suggested earlier, use a serial port monitor to see which commands you actually send to the modem and wheter it answers.

I don t know how to distinct the write and the read. m_serial->readAll(); give all of us.

• I don t know how to distinct the write and the read. m_serial->readAll(); give all of us.

Sorry, what do you mean? serial->readAll() returns everything that the modem has send to your program since the last serial->readAll() call.

That's why I recommended to use a serial port monitor, which captures the data between your program and the modem so you can easier debug.

• To put header into the blocks send to the comserial, I need to size sample.
Show bellow a wave file header.

header  "RIFF>\xC1\x1B\x00WAVEfmt \x10\x00\x00\x00\x01\x00\x02\x00@\x1F\x00\x00\x80>\x00\x00\x02\x00\b\x00""data\b\xC0\x1B\x00\x7F\x7F\x7F\x7F"
header Hex data "52 49 46 46 3e c1 1b 00 57 41 56 45 66 6d 74 20 10 00 00 00 01 00 02 00 40 1f 00 00 80 3e 00 00 02 00 08 00 64 61 74 61 08 c0 1b 00 7f 7f 7f 7f"


I need to get the Bitspersample with an offet of 34 and 4 bits long.

          QByteArray header = wavFile->read(48);                  // header of wave file (.... 00 **08 00** 64...)

qint8 BitsPerSample =header.toHex().data()[34];       //And the [35]   after anderstand how to do

qDebug() << "Bits per sample" << BitsPerSample;     // print out 48


Why debug print 48? I want 08.
In reading, in base 10, 48 gives 2048.

Whynot BitsPerSample =qint8(header[34]);

• See here a methode
It uses a data stream in little Endian.
I really don't understand the sens of chunkDataSize and if this makes trouble along the sample data sending.

• Where i am now...

I put a timer to prevent the modem buffer from being saturated

           m_command = "AT+VTX;\r";
output = m_command.toStdString().c_str();
m_serial->write(output);
m_serial->flush();

timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(sendWave()));
timer->start(Latence);


The first bugg is the time it takes for the Hayes command to respond before sending a wave sample data to the modem.
I don't know how to wait the answer.
I think about a boolean to stop timer.
I will not certain where i need to put this wait because of use connect(m_serial, &QSerialPort::readyRead, this, &MainWindow::readData);