audioinput.cpp Example File
					 
					
						multimedia/audioinput/audioinput.cpp
					 
					
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
**   * Redistributions of source code must retain the above copyright
**     notice, this list of conditions and the following disclaimer.
**   * Redistributions in binary form must reproduce the above copyright
**     notice, this list of conditions and the following disclaimer in
**     the documentation and/or other materials provided with the
**     distribution.
**   * Neither the name of The Qt Company Ltd nor the names of its
**     contributors may be used to endorse or promote products derived
**     from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/ 
#include "audioinput.h" 
#include <stdlib.h> 
#include <math.h> 
#include <QDateTime> 
#include <QDebug> 
#include <QPainter> 
#include <QVBoxLayout> 
#include <QAudioDeviceInfo> 
#include <QAudioInput> 
#include <qendian.h> 
AudioInfo:: AudioInfo(const  QAudioFormat & format)
    : m_format(format)
{
    switch  (m_format. sampleSize()) {
    case  8 :
        switch  (m_format. sampleType()) {
        case  QAudioFormat :: UnSignedInt:
            m_maxAmplitude =  255 ;
            break ;
        case  QAudioFormat :: SignedInt:
            m_maxAmplitude =  127 ;
            break ;
        default :
            break ;
        }
        break ;
    case  16 :
        switch  (m_format. sampleType()) {
        case  QAudioFormat :: UnSignedInt:
            m_maxAmplitude =  65535 ;
            break ;
        case  QAudioFormat :: SignedInt:
            m_maxAmplitude =  32767 ;
            break ;
        default :
            break ;
        }
        break ;
    case  32 :
        switch  (m_format. sampleType()) {
        case  QAudioFormat :: UnSignedInt:
            m_maxAmplitude =  0xffffffff ;
            break ;
        case  QAudioFormat :: SignedInt:
            m_maxAmplitude =  0x7fffffff ;
            break ;
        case  QAudioFormat :: Float:
            m_maxAmplitude =  0x7fffffff ; // Kind of 
        default :
            break ;
        }
        break ;
    default :
        break ;
    }
}
void  AudioInfo:: start()
{
    open(QIODevice :: WriteOnly);
}
void  AudioInfo:: stop()
{
    close();
}
qint64 :: readData(char  * data,  qint64 return  0 ;
}
qint64 :: writeData(const  char  * data,  qint64 if  (m_maxAmplitude) {
        Q_ASSERT(m_format. sampleSize() %  8  = =  0 );
        const  int  channelBytes =  m_format. sampleSize() /  8 ;
        const  int  sampleBytes =  m_format. channelCount() *  channelBytes;
        Q_ASSERT(len %  sampleBytes = =  0 );
        const  int  numSamples =  len /  sampleBytes;
        quint32 =  0 ;
        const  unsigned  char  * ptr =  reinterpret_cast < const  unsigned  char  * > (data);
        for  (int  i =  0 ; i <  numSamples; + + i) {
            for  (int  j =  0 ; j <  m_format. channelCount(); + + j) {
                quint32 =  0 ;
                if  (m_format. sampleSize() = =  8  & &  m_format. sampleType() = =  QAudioFormat :: UnSignedInt) {
                    value =  * reinterpret_cast < const  quint8 * > (ptr);
                } else  if  (m_format. sampleSize() = =  8  & &  m_format. sampleType() = =  QAudioFormat :: SignedInt) {
                    value =  qAbs (* reinterpret_cast < const  qint8 * > (ptr));
                } else  if  (m_format. sampleSize() = =  16  & &  m_format. sampleType() = =  QAudioFormat :: UnSignedInt) {
                    if  (m_format. byteOrder() = =  QAudioFormat :: LittleEndian)
                        value =  qFromLittleEndian < quint16 > (ptr);
                    else 
                        value =  qFromBigEndian < quint16 > (ptr);
                } else  if  (m_format. sampleSize() = =  16  & &  m_format. sampleType() = =  QAudioFormat :: SignedInt) {
                    if  (m_format. byteOrder() = =  QAudioFormat :: LittleEndian)
                        value =  qAbs (qFromLittleEndian < qint16 > (ptr));
                    else 
                        value =  qAbs (qFromBigEndian < qint16 > (ptr));
                } else  if  (m_format. sampleSize() = =  32  & &  m_format. sampleType() = =  QAudioFormat :: UnSignedInt) {
                    if  (m_format. byteOrder() = =  QAudioFormat :: LittleEndian)
                        value =  qFromLittleEndian < quint32 > (ptr);
                    else 
                        value =  qFromBigEndian < quint32 > (ptr);
                } else  if  (m_format. sampleSize() = =  32  & &  m_format. sampleType() = =  QAudioFormat :: SignedInt) {
                    if  (m_format. byteOrder() = =  QAudioFormat :: LittleEndian)
                        value =  qAbs (qFromLittleEndian < qint32 > (ptr));
                    else 
                        value =  qAbs (qFromBigEndian < qint32 > (ptr));
                } else  if  (m_format. sampleSize() = =  32  & &  m_format. sampleType() = =  QAudioFormat :: Float) {
                    value =  qAbs (* reinterpret_cast < const  float * > (ptr) *  0x7fffffff ); // assumes 0-1.0 
                }
                maxValue =  qMax (value,  maxValue);
                ptr + =  channelBytes;
            }
        }
        maxValue =  qMin (maxValue,  m_maxAmplitude);
        m_level =  qreal /  m_maxAmplitude;
    }
    emit  update();
    return  len;
}
RenderArea:: RenderArea(QWidget * parent)
    : QWidget QPalette :: Base);
    setAutoFillBackground(true );
    setMinimumHeight(30 );
    setMinimumWidth(200 );
}
void  RenderArea:: paintEvent(QPaintEvent *  /* event */ )
{
    QPainter this );
    painter. setPen(Qt :: black);
    painter. drawRect(QRect . viewport(). left()+ 10 , 
                           painter. viewport(). top()+ 10 , 
                           painter. viewport(). right()- 20 , 
                           painter. viewport(). bottom()- 20 ));
    if  (m_level = =  0.0 )
        return ;
    int  pos =  ((painter. viewport(). right()- 20 )- (painter. viewport(). left()+ 11 ))* m_level;
    painter. fillRect(painter. viewport(). left()+ 11 , 
                     painter. viewport(). top()+ 10 , 
                     pos, 
                     painter. viewport(). height()- 21 , 
                     Qt :: red);
}
void  RenderArea:: setLevel(qreal =  value;
    update();
}
InputTest:: InputTest()
{
    initializeWindow();
    initializeAudio(QAudioDeviceInfo :: defaultInputDevice());
}
void  InputTest:: initializeWindow()
{
    QWidget * window =  new  QWidget QVBoxLayout * layout =  new  QVBoxLayout =  new  RenderArea(this );
    layout- > addWidget(m_canvas);
    m_deviceBox =  new  QComboBox this );
    const  QAudioDeviceInfo & defaultDeviceInfo =  QAudioDeviceInfo :: defaultInputDevice();
    m_deviceBox- > addItem(defaultDeviceInfo. deviceName(),  qVariantFromValue (defaultDeviceInfo));
    for  (auto  & deviceInfo: QAudioDeviceInfo :: availableDevices(QAudio :: AudioInput)) {
        if  (deviceInfo ! =  defaultDeviceInfo)
            m_deviceBox- > addItem(deviceInfo. deviceName(),  qVariantFromValue (deviceInfo));
    }
    connect(m_deviceBox,  QOverload < int > :: of(& QComboBox :: activated),  this ,  & InputTest:: deviceChanged);
    layout- > addWidget(m_deviceBox);
    m_volumeSlider =  new  QSlider Qt :: Horizontal,  this );
    m_volumeSlider- > setRange(0 ,  100 );
    m_volumeSlider- > setValue(100 );
    connect(m_volumeSlider,  & QSlider :: valueChanged,  this ,  & InputTest:: sliderChanged);
    layout- > addWidget(m_volumeSlider);
    m_modeButton =  new  QPushButton this );
    connect(m_modeButton,  & QPushButton :: clicked,  this ,  & InputTest:: toggleMode);
    layout- > addWidget(m_modeButton);
    m_suspendResumeButton =  new  QPushButton this );
    connect(m_suspendResumeButton,  & QPushButton :: clicked,  this ,  & InputTest:: toggleSuspend);
    layout- > addWidget(m_suspendResumeButton);
    window- > setLayout(layout);
    setCentralWidget(window);
    window- > show();
}
void  InputTest:: initializeAudio(const  QAudioDeviceInfo & deviceInfo)
{
    QAudioFormat . setSampleRate(8000 );
    format. setChannelCount(1 );
    format. setSampleSize(16 );
    format. setSampleType(QAudioFormat :: SignedInt);
    format. setByteOrder(QAudioFormat :: LittleEndian);
    format. setCodec("audio/pcm" );
    if  (! deviceInfo. isFormatSupported(format)) {
        qWarning () < <  "Default format not supported - trying to use nearest" ;
        format =  deviceInfo. nearestFormat(format);
    }
    m_audioInfo. reset(new  AudioInfo(format));
    connect(m_audioInfo. data(),  & AudioInfo:: update,  [ this ] () {
        m_canvas- > setLevel(m_audioInfo- > level());
    });
    m_audioInput. reset(new  QAudioInput ,  format));
    qreal =  QAudio :: convertVolume(m_audioInput- > volume(), 
                                                QAudio :: LinearVolumeScale, 
                                                QAudio :: LogarithmicVolumeScale);
    m_volumeSlider- > setValue(qRound (initialVolume *  100 ));
    m_audioInfo- > start();
    toggleMode();
}
void  InputTest:: toggleMode()
{
    m_audioInput- > stop();
    toggleSuspend();
    // Change bewteen pull and push modes 
    if  (m_pullMode) {
        m_modeButton- > setText(tr("Enable push mode" ));
        m_audioInput- > start(m_audioInfo. data());
    } else  {
        m_modeButton- > setText(tr("Enable pull mode" ));
        auto  io =  m_audioInput- > start();
        connect(io,  & QIODevice :: readyRead, 
            [ & ,  io] () {
                qint64 =  m_audioInput- > bytesReady();
                const  int  BufferSize =  4096 ;
                if  (len >  BufferSize)
                    len =  BufferSize;
                QByteArray ,  0 );
                qint64 =  io- > read(buffer. data(),  len);
                if  (l >  0 )
                    m_audioInfo- > write(buffer. constData(),  l);
            });
    }
    m_pullMode =  ! m_pullMode;
}
void  InputTest:: toggleSuspend()
{
    // toggle suspend/resume 
    if  (m_audioInput- > state() = =  QAudio :: SuspendedState | |  m_audioInput- > state() = =  QAudio :: StoppedState) {
        m_audioInput- > resume();
        m_suspendResumeButton- > setText(tr("Suspend recording" ));
    } else  if  (m_audioInput- > state() = =  QAudio :: ActiveState) {
        m_audioInput- > suspend();
        m_suspendResumeButton- > setText(tr("Resume recording" ));
    } else  if  (m_audioInput- > state() = =  QAudio :: IdleState) {
        // no-op 
    }
}
void  InputTest:: deviceChanged(int  index)
{
    m_audioInfo- > stop();
    m_audioInput- > stop();
    m_audioInput- > disconnect(this );
    initializeAudio(m_deviceBox- > itemData(index). value< QAudioDeviceInfo > ());
}
void  InputTest:: sliderChanged(int  value)
{
    qreal =  QAudio :: convertVolume(value /  qreal 100 ), 
                                               QAudio :: LogarithmicVolumeScale, 
                                               QAudio :: LinearVolumeScale);
    m_audioInput- > setVolume(linearVolume);
}