December 19, 2011

Anticross Anticross
Hobby Entomologist
248 posts

send raw data to QAudioOutput.

 

I’ve got QAudioOutput which initialized by next format:

  1. QAudioFormat format;
  2.                        // Set up the format, eg.
  3.                        format.setFrequency(44100);
  4.                        format.setChannels(2);
  5.                        format.setSampleSize(16);
  6.                        format.setCodec("audio/pcm");
  7.                        format.setByteOrder(QAudioFormat::LittleEndian);
  8.                        format.setSampleType(QAudioFormat::UnSignedInt);
  9.  
  10.                        QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
  11.                        if (!info.isFormatSupported(format)) {
  12.                            qWarning()<<"raw audio format not supported by backend, cannot play audio.";
  13.                            return;
  14.                        }

I’ve got a class subclassed from QIODevice to store incoming data which uses like a buffer:
  1. #ifndef AUDIOBUFFER_H
  2. #define AUDIOBUFFER_H
  3.  
  4. #include <QAudioFormat>
  5. #include <QIODevice>
  6. #include <QMutex>
  7.  
  8. class AudioBuffer : public QIODevice
  9. {
  10.     Q_OBJECT
  11. public:
  12.     struct block
  13.     {
  14.         char * ptr;
  15.         qint64 len;
  16.         int    index;
  17.     };
  18. //----------------------------------------------------------------------------------------------------------------------------------
  19.     AudioBuffer(/*const QAudioFormat &format,*/ QObject *parent = NULL) :   QIODevice(parent)
  20.     {
  21. //         const char * someData = "12MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWQuhaSSSSSSSSSSSSSSSSSSSSSSSSSSss";
  22. //         writeData(someData,sizeof(someData));
  23.     }
  24. //----------------------------------------------------------------------------------------------------------------------------------
  25.     ~AudioBuffer()
  26.     {
  27.         clear();
  28.     }
  29. //----------------------------------------------------------------------------------------------------------------------------------
  30.     void start()
  31.     {
  32.         if(!isOpen())
  33.             open(QIODevice::ReadWrite);
  34.     }
  35. //----------------------------------------------------------------------------------------------------------------------------------
  36.     void stop()
  37.     {
  38.         close();
  39.     }
  40. //----------------------------------------------------------------------------------------------------------------------------------
  41.     qint64 readData(char * data, qint64 maxlen)
  42.     {
  43.         QMutexLocker mutexLocker(&m_mutex);
  44.  
  45.         start();
  46.  
  47.         if(m_buffer.isEmpty())
  48.             return 0;
  49.  
  50.         int length = maxlen;
  51.  
  52.         int size = 0;
  53.  
  54.         while (m_buffer.isEmpty() == false) {
  55.             block bl = m_buffer.at(0);
  56.  
  57.             if (bl.len <= length) {
  58.                 memcpy(data+size, bl.ptr, bl.len);
  59.                 m_buffer.removeAt(0);
  60.                 delete [] bl.ptr;
  61.                 length -= bl.len;
  62.                 size += bl.len;
  63.                 continue;
  64.             }
  65.  
  66.             memcpy(data+size, bl.ptr, length);
  67.             bl.len -= length;
  68.             m_buffer[0] = bl;
  69.             memcpy(bl.ptr, bl.ptr+length, bl.len);
  70.             size += length;
  71.             length = 0;
  72.             break;
  73.         }
  74.         if (length > 0)
  75.             memset(data + (maxlen - length), 0xCC, length);
  76.  
  77.         return maxlen;
  78.     }
  79. //----------------------------------------------------------------------------------------------------------------------------------
  80.     qint64 writeData(const char *data, qint64 len)
  81.     {
  82.  
  83.         QMutexLocker mutexLocker(&m_mutex);
  84.  
  85.         start();
  86.  
  87.         char * ptr = new char[len];
  88.         memcpy(ptr,data,len);
  89.         block bl;
  90.         bl.ptr = ptr;
  91.         bl.len = len;
  92.         m_buffer.append(bl);
  93.  
  94.         return len;
  95.     }
  96. //----------------------------------------------------------------------------------------------------------------------------------
  97.     void clear(){
  98.         QMutexLocker mutexLocker(&m_mutex);
  99.  
  100.         for(int i = 0; i<m_buffer.count(); i++)
  101.         {
  102.             block bl = m_buffer.at(i);
  103.  
  104.             delete [] bl.ptr;
  105.         }
  106.  
  107.         m_buffer.clear();
  108.     }
  109. //----------------------------------------------------------------------------------------------------------------------------------
  110. private:
  111.     QList<block> m_buffer;
  112.     QMutex  m_mutex;
  113. };
  114.  
  115. #endif//AUDIOBUFFER_H

Then I do the following thing:

  1. AudioBuffer * m_buf;
  2. QAudioOutput * m_output;
  3.  
  4. m_buf = new AudioBuffer();
  5. m_buf->start();
  6.  
  7. m_output = new QAudioOutput(format);
  8. m_output->start(m_buf);

And later add data in another place:
  1. m_buf->writeData((const char *)data,dataSize);

But after all this manipulations I hear no sound at output.

15 replies

December 19, 2011

Anticross Anticross
Hobby Entomologist
248 posts

I replace AudioBuffer By new variable QIODevice * m_dev; And do the following declarations

  1.  m_dev = m_output->start();

and replace
  1. m_buf->writeData((const char *)data,dataSize);

by
  1. m_dev->write((const char *)data,dataSize);

And now I get a beautiful noise in output but not that music which I put there. An this noise is interrupt periodically. But it finishes when data transmit finishes. Maybe something wrong with format?

As a source I am using wav file 44100 Hz 16 bit stereo, which i transmit over network an receive data by portions and write it actually here:

  1. m_buf->writeData((const char *)data,dataSize);

So if I use a file to receive this data, and after receiving I open it, it’s contains the music that I transmitt.

Edit: fixed code formatting and merged two posts; Andre

December 19, 2011

Anticross Anticross
Hobby Entomologist
248 posts

Now I’m using my buffer such as QIODevice

  1. m_buf = (AudioBuffer *)m_output->start();
and get the same. I change the file that I transmit to another one, and notice that I can hear music, but it’s very distorted, and interrupts as I wrote previously. But one more notice that the high freq sounds with low volume hears better than others.

December 19, 2011

Anticross Anticross
Hobby Entomologist
248 posts

It seems like data sending through RTP is converted to BigEndian format before reaching client. But I find no BigEndian support of playback, so maybe I need to do some fast conversation. Question: How to do that ?

December 19, 2011

Tobias Hunger Tobias Hunger
Mad Scientist
3132 posts

RTP defines all values to be transmitted in big-endian, so no surprise there;-)

http://developer.qt.nokia.com/doc/qt-4.8/qtendian.html to the rescue.

December 20, 2011

Anticross Anticross
Hobby Entomologist
248 posts

OK, thanks. Another question: I have

  1. unsigned char const* data
with size
  1. unsigned dataSize
, qFromBigEndian needs to chose type (qInt16,qInt32,qInt64), and returns value of it’s type, but how can I put this data back into unsigned char const* array with size of dataSize ?

December 20, 2011

Anticross Anticross
Hobby Entomologist
248 posts

This does not helped:

  1. char * dt = new char[dataSize];
  2. itoa(qFromBigEndian<qint16>(data),dt,2);
  3. m_buf->writeData((const char*)dt,dataSize);
  4. delete dt;

December 20, 2011

Anticross Anticross
Hobby Entomologist
248 posts

Edited to :

  1. qint16 val = qFromBigEndian<qint16>(data);
  2. uchar* dt = new uchar[dataSize];
  3. qToLittleEndian<qint16>(val,dt);
  4. m_buf->writeData((const char*)dt,dataSize);
  5. delete dt;

But the quality of audio is worse then without conversation.

December 20, 2011

Anticross Anticross
Hobby Entomologist
248 posts

I write function:

  1. bool SpeakerSink::toLittleEndian( const char * src, int size, int nBits)
  2. {
  3.     if(src == NULL)
  4.         return false;
  5.  
  6.     for (int i = 0; i < size; i+=nBits) {
  7.         qint16 val = qFromBigEndian<qint16>((const uchar*)(src+i));
  8.         qMemCopy((void*)(src+i),&val,nBits);
  9.     }
  10.  
  11.     return true;
  12. }

It solve task of converting from big-endian to little-endian and in headphones I get the same result(I mean sound is recognizable but bad quality) as if my server will transmit data without conversation to big-endian. But sound still distorted and interrupts.

December 20, 2011

Anticross Anticross
Hobby Entomologist
248 posts

I open VLC player and receive this stream normally, then I look at codec details and see some PCM S16BE. So what is it and can I transform data to play normal PCM on QAudioOutput ? So is it 16-bit Big-endian pcm or something else? What means S ?

December 20, 2011

Anticross Anticross
Hobby Entomologist
248 posts

I record output of my sound card when receiving stream with sine of 1000 Hz. After opening it in Sound Forge I see pauses in this sine with delays from 5 ms to 25 ms but sine has the same shape(I mean no distortions).

December 20, 2011

Tobias Hunger Tobias Hunger
Mad Scientist
3132 posts

Sorry, no idea what kind of data you send around your network;-)

Why do you have nBits in the toLittleEndian method shown above? Anything but nBits == 2 breaks the method, doesn’t it? The name is rather confusing, too… shouldn’t it at least be nBytes?

December 21, 2011

Anticross Anticross
Hobby Entomologist
248 posts

You’re right the name must be at least nBytes :) Maybe QAudioOutput reads the buffer faster than it writes by receiving data from network, and I just need to wait some time before playing.

December 21, 2011

Anticross Anticross
Hobby Entomologist
248 posts

I rewrite my audio buffer class. Now it looks like this:

  1. #ifndef AUDIOBUFFER_H
  2. #define AUDIOBUFFER_H
  3.  
  4. #include <QAudioFormat>
  5. #include <QIODevice>
  6. #include <QMutex>
  7. #include <QDebug>
  8.  
  9. class AudioBuffer : public QIODevice
  10. {
  11.     Q_OBJECT
  12.  
  13. public:
  14. //----------------------------------------------------------------------------------------------------------------------------------
  15.     AudioBuffer(/*const QAudioFormat &format,*/ QObject *parent = NULL, int bufSize = 1400) :   QIODevice(parent)
  16.     {
  17.         m_bufSize = bufSize*4;
  18.  
  19.         m_buffer1 = new const char[m_bufSize];
  20.         m_buffer2 = new const char[m_bufSize];
  21.  
  22.         m_rightOffset = 0;
  23.         m_leftOffset  = 0;
  24.         m_cursor      = 0;
  25.  
  26.         qMemSet((void *)m_buffer1,0,m_bufSize);
  27.  
  28.     }
  29. //----------------------------------------------------------------------------------------------------------------------------------
  30.     ~AudioBuffer()
  31.     {
  32.         delete m_buffer1;
  33.         delete m_buffer2;
  34.     }
  35. //----------------------------------------------------------------------------------------------------------------------------------
  36.     void start()
  37.     {
  38.         if(!isOpen())
  39.             open(QIODevice::ReadWrite);
  40.     }
  41. //----------------------------------------------------------------------------------------------------------------------------------
  42.     void stop()
  43.     {
  44.         close();
  45.     }
  46. //----------------------------------------------------------------------------------------------------------------------------------
  47.     qint64 readData(char * data, qint64 len)
  48.     {
  49.         int free2end = m_bufSize-m_cursor;
  50.  
  51.         if(len <= free2end){
  52.             qMemCopy(data,m_buffer1+m_cursor,len);
  53.  
  54.             m_rightOffset += len;
  55.             m_cursor += len;
  56.  
  57.             return len;
  58.         }
  59.         else
  60.         {
  61.             qMemCopy(data,m_buffer1+m_cursor,free2end);
  62.  
  63.             m_cursor = 0;
  64.  
  65.             qMemCopy(data+free2end,m_buffer1,len-free2end);
  66.  
  67.             m_rightOffset = len-free2end;
  68.             m_leftOffset = 0;
  69.             m_cursor += m_rightOffset;
  70.  
  71.             return len;
  72.         }
  73.  
  74.         return 0;
  75.     }
  76. //----------------------------------------------------------------------------------------------------------------------------------
  77.     qint64 writeData(const char *data, qint64 len)
  78.     {
  79.         int free2end   = m_bufSize-m_rightOffset;
  80.  
  81.         if(len <= free2end) // if we don't reach end of buffer
  82.         {
  83.             qMemCopy((void*)(m_buffer1+m_rightOffset),data,len);
  84.             m_rightOffset += len;
  85.  
  86.             qDebug() << m_buffer1;
  87.  
  88.             return len;
  89.         }
  90.         else if(len <= (m_leftOffset+free2end))
  91.         {
  92.             qMemCopy((void*)(m_buffer1+m_rightOffset),data,free2end); //write to end
  93.             m_leftOffset = len-free2end; // need to write more from source and set new left offset
  94.             qMemCopy((void*)m_buffer1,data+free2end,m_leftOffset); //write lefted part
  95.             m_rightOffset = m_leftOffset; // set new right offset value
  96.             m_leftOffset  = 0; // set new left offset value
  97.  
  98.             qDebug() << m_buffer1;
  99.  
  100.             return len;
  101.         }
  102.         else
  103.         {
  104.             qMemCopy((void*)(m_buffer1+m_rightOffset),data,free2end); //write to end
  105.             m_rightOffset += len-free2end; // need to write more from source
  106.  
  107.             len -= free2end;
  108.  
  109.             qMemCopy((void*)m_buffer1,data+free2end,m_leftOffset); //write lefted part
  110.             m_rightOffset = 0; // set new right offset value
  111.  
  112.             len -= m_leftOffset;
  113.  
  114.             m_leftOffset = 0;
  115.  
  116.             qDebug() << m_buffer1;
  117.  
  118.             return len;
  119.         }
  120.  
  121.         return 0;
  122.     }
  123. //----------------------------------------------------------------------------------------------------------------------------------
  124. private:
  125.     const char * m_buffer1;
  126.     const char * m_buffer2;
  127.     int          m_rightOffset;
  128.     int          m_leftOffset;
  129.     int          m_cursor;
  130.     int          m_bufSize;
  131. };
  132.  
  133. #endif//AUDIOBUFFER_H

And now I’m using it like this:
  1. m_output->start(m_buf);

and it enters write data functions, but doesn’t reads data. So question: How QAudioOutput asks for the data to play from QIODevice ? Is it call readData function or not ? And one more question: Is it possible to play rtsp stream using Phonon ?

December 21, 2011

Anticross Anticross
Hobby Entomologist
248 posts

One more question whats the main difference between push and pull mode of playing ?

December 29, 2011

Anticross Anticross
Hobby Entomologist
248 posts

Interesting thing: when I play 22050 Hz 16 bit 2 channels audio stream – it plays normally without interruptions, but when playing 44100 Hz 16 bit stereo – not. If I open same stream(44 kHz) in VLC player or any other it plays normally without interruptions.

 
  ‹‹ What may be the cause of following error?      QT 4.7 cannot receive multi-touch events. ››

You must log in to post a reply. Not a member yet? Register here!