Send raw data to QAudioOutput.



  • I've got QAudioOutput which initialized by next format:
    @QAudioFormat format;
    // Set up the format, eg.
    format.setFrequency(44100);
    format.setChannels(2);
    format.setSampleSize(16);
    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;
                       }@
    

    I've got a class subclassed from QIODevice to store incoming data which uses like a buffer:
    @#ifndef AUDIOBUFFER_H
    #define AUDIOBUFFER_H

    #include <QAudioFormat>
    #include <QIODevice>
    #include <QMutex>

    class AudioBuffer : public QIODevice
    {
    Q_OBJECT
    public:
    struct block
    {
    char * ptr;
    qint64 len;
    int index;
    };
    //----------------------------------------------------------------------------------------------------------------------------------
    AudioBuffer(/const QAudioFormat &format,/ QObject *parent = NULL) : QIODevice(parent)
    {
    // const char * someData = "12MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWQuhaSSSSSSSSSSSSSSSSSSSSSSSSSSss";
    // writeData(someData,sizeof(someData));
    }
    //----------------------------------------------------------------------------------------------------------------------------------
    ~AudioBuffer()
    {
    clear();
    }
    //----------------------------------------------------------------------------------------------------------------------------------
    void start()
    {
    if(!isOpen())
    open(QIODevice::ReadWrite);
    }
    //----------------------------------------------------------------------------------------------------------------------------------
    void stop()
    {
    close();
    }
    //----------------------------------------------------------------------------------------------------------------------------------
    qint64 readData(char * data, qint64 maxlen)
    {
    QMutexLocker mutexLocker(&m_mutex);

        start();
    
        if(m_buffer.isEmpty())
            return 0;
    
        int length = maxlen;
    
        int size = 0;
    
        while (m_buffer.isEmpty() == false) {
            block bl = m_buffer.at(0);
    
            if (bl.len <= length) {
                memcpy(data+size, bl.ptr, bl.len);
                m_buffer.removeAt(0);
                delete [] bl.ptr;
                length -= bl.len;
                size += bl.len;
                continue;
            }
    
            memcpy(data+size, bl.ptr, length);
            bl.len -= length;
            m_buffer[0] = bl;
            memcpy(bl.ptr, bl.ptr+length, bl.len);
            size += length;
            length = 0;
            break;
        }
        if (length > 0)
            memset(data + (maxlen - length), 0xCC, length);
    
        return maxlen;
    }
    

    //----------------------------------------------------------------------------------------------------------------------------------
    qint64 writeData(const char *data, qint64 len)
    {

        QMutexLocker mutexLocker(&m_mutex);
    
        start();
    
        char * ptr = new char[len];
        memcpy(ptr,data,len);
        block bl;
        bl.ptr = ptr;
        bl.len = len;
        m_buffer.append(bl);
    
        return len;
    }
    

    //----------------------------------------------------------------------------------------------------------------------------------
    void clear(){
    QMutexLocker mutexLocker(&m_mutex);

        for(int i = 0; i<m_buffer.count(); i++)
        {
            block bl = m_buffer.at(i);
    
            delete [] bl.ptr;
        }
    
        m_buffer.clear();
    }
    

    //----------------------------------------------------------------------------------------------------------------------------------
    private:
    QList<block> m_buffer;
    QMutex m_mutex;
    };

    #endif//AUDIOBUFFER_H@

    Then I do the following thing:
    @
    AudioBuffer * m_buf;
    QAudioOutput * m_output;

    m_buf = new AudioBuffer();
    m_buf->start();

    m_output = new QAudioOutput(format);
    m_output->start(m_buf);
    @
    And later add data in another place:
    @m_buf->writeData((const char *)data,dataSize);@

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



  • I replace AudioBuffer By new variable <code>QIODevice * m_dev;</code> And do the following declarations
    @
    m_dev = m_output->start();
    @
    and replace
    @m_buf->writeData((const char *)data,dataSize);@
    by
    @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:
    @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



  • Now I'm using my buffer such as QIODevice @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.



  • 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 ?


  • Moderators

    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.



  • OK, thanks. Another question: I have @unsigned char const* data@ with size @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 ?



  • This does not helped:
    @
    char * dt = new char[dataSize];
    itoa(qFromBigEndian<qint16>(data),dt,2);
    m_buf->writeData((const char*)dt,dataSize);
    delete dt;
    @



  • Edited to :
    @qint16 val = qFromBigEndian<qint16>(data);
    uchar* dt = new uchar[dataSize];
    qToLittleEndian<qint16>(val,dt);
    m_buf->writeData((const char*)dt,dataSize);
    delete dt;@
    But the quality of audio is worse then without conversation.



  • I write function:
    @bool SpeakerSink::toLittleEndian( const char * src, int size, int nBits)
    {
    if(src == NULL)
    return false;

    for (int i = 0; i < size; i+=nBits) {
        qint16 val = qFromBigEndian<qint16>((const uchar*)(src+i));
        qMemCopy((void*)(src+i),&val,nBits);
    }
    
    return true;
    

    }@
    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.



  • I open VLC player and receive this stream normally, then I look at codec details and see some PCM S16BE(twos). 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 ?



  • 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).


  • Moderators

    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?



  • 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.



  • I rewrite my audio buffer class. Now it looks like this:
    @#ifndef AUDIOBUFFER_H
    #define AUDIOBUFFER_H

    #include <QAudioFormat>
    #include <QIODevice>
    #include <QMutex>
    #include <QDebug>

    class AudioBuffer : public QIODevice
    {
    Q_OBJECT

    public:
    //----------------------------------------------------------------------------------------------------------------------------------
    AudioBuffer(/const QAudioFormat &format,/ QObject parent = NULL, int bufSize = 1400) : QIODevice(parent)
    {
    m_bufSize = bufSize
    4;

        m_buffer1 = new const char[m_bufSize];
        m_buffer2 = new const char[m_bufSize];
    
        m_rightOffset = 0;
        m_leftOffset  = 0;
        m_cursor      = 0;
    
        qMemSet((void *)m_buffer1,0,m_bufSize);
    
    }
    

    //----------------------------------------------------------------------------------------------------------------------------------
    ~AudioBuffer()
    {
    delete m_buffer1;
    delete m_buffer2;
    }
    //----------------------------------------------------------------------------------------------------------------------------------
    void start()
    {
    if(!isOpen())
    open(QIODevice::ReadWrite);
    }
    //----------------------------------------------------------------------------------------------------------------------------------
    void stop()
    {
    close();
    }
    //----------------------------------------------------------------------------------------------------------------------------------
    qint64 readData(char * data, qint64 len)
    {
    int free2end = m_bufSize-m_cursor;

        if(len <= free2end){
            qMemCopy(data,m_buffer1+m_cursor,len);
    
            m_rightOffset += len;
            m_cursor += len;
    
            return len;
        }
        else
        {
            qMemCopy(data,m_buffer1+m_cursor,free2end);
    
            m_cursor = 0;
    
            qMemCopy(data+free2end,m_buffer1,len-free2end);
    
            m_rightOffset = len-free2end;
            m_leftOffset = 0;
            m_cursor += m_rightOffset;
    
            return len;
        }
    
        return 0;
    }
    

    //----------------------------------------------------------------------------------------------------------------------------------
    qint64 writeData(const char *data, qint64 len)
    {
    int free2end = m_bufSize-m_rightOffset;

        if(len <= free2end) // if we don't reach end of buffer
        {
            qMemCopy((void*)(m_buffer1+m_rightOffset),data,len);
            m_rightOffset += len;
    
            qDebug() << m_buffer1;
    
            return len;
        }
        else if(len <= (m_leftOffset+free2end))
        {
            qMemCopy((void*)(m_buffer1+m_rightOffset),data,free2end); //write to end
            m_leftOffset = len-free2end; // need to write more from source and set new left offset
            qMemCopy((void*)m_buffer1,data+free2end,m_leftOffset); //write lefted part 
            m_rightOffset = m_leftOffset; // set new right offset value
            m_leftOffset  = 0; // set new left offset value
    
            qDebug() << m_buffer1;
    
            return len;
        }
        else
        {
            qMemCopy((void*)(m_buffer1+m_rightOffset),data,free2end); //write to end
            m_rightOffset += len-free2end; // need to write more from source
    
            len -= free2end;
    
            qMemCopy((void*)m_buffer1,data+free2end,m_leftOffset); //write lefted part 
            m_rightOffset = 0; // set new right offset value
    
            len -= m_leftOffset;
    
            m_leftOffset = 0;
    
            qDebug() << m_buffer1;
    
            return len;
        }
    
        return 0;
    }
    

    //----------------------------------------------------------------------------------------------------------------------------------
    private:
    const char * m_buffer1;
    const char * m_buffer2;
    int m_rightOffset;
    int m_leftOffset;
    int m_cursor;
    int m_bufSize;
    };

    #endif//AUDIOBUFFER_H@
    And now I'm using it like this:
    @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 ?



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



  • 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.



  • this is an interesting thread. a QAudioOuput from a buffer example should be added to the document.

    too bad the document only has an example of playing a file.



  • has this issue been resolved yet? I also need to achieve the same thing?



  • has this issue been resolved yet? I also need to achieve the same thing?



  • I had a similar problem using a ringbuffer as a QIODevice backend for which I spent a whole day. The solution was to override the following method:

    @bool isSequential() const
    {
    return true;
    }@



  • I had a similar problem using a ringbuffer as a QIODevice backend for which I spent a whole day. The solution was to override the following method:

    @bool isSequential() const
    {
    return true;
    }@


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.