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 <stdlib.h> 
#include <math.h> 
#include <QDateTime> 
#include <QDebug> 
#include <QPainter> 
#include <QVBoxLayout> 
#include <QAudioDeviceInfo> 
#include <QAudioInput> 
#include <qendian.h> 
#include "audioinput.h" 
#define PUSH_MODE_LABEL "Enable push mode" 
#define PULL_MODE_LABEL "Enable pull mode" 
#define SUSPEND_LABEL   "Suspend recording" 
#define RESUME_LABEL    "Resume recording" 
const  int  BufferSize =  4096 ;
AudioInfo:: AudioInfo(const  QAudioFormat & format,  QObject * parent)
    :   QIODevice ,    m_format(format)
    ,    m_maxAmplitude(0 )
    ,    m_level(0.0 )
{
    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 ;
    }
}
AudioInfo:: ~ AudioInfo()
{
}
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 );
    m_level =  0 ;
    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()
    :   m_canvas(0 )
    ,    m_modeButton(0 )
    ,    m_suspendResumeButton(0 )
    ,    m_deviceBox(0 )
    ,    m_device(QAudioDeviceInfo :: defaultInputDevice())
    ,    m_audioInfo(0 )
    ,    m_audioInput(0 )
    ,    m_input(0 )
    ,    m_pullMode(true )
    ,    m_buffer(BufferSize,  0 )
{
    initializeWindow();
    initializeAudio();
}
InputTest:: ~ InputTest() {}
void  InputTest:: initializeWindow()
{
    QScopedPointer < QWidget >  window(new  QWidget QScopedPointer < 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));
    foreach (const  QAudioDeviceInfo & deviceInfo,  QAudioDeviceInfo :: availableDevices(QAudio :: AudioInput)) {
        if  (deviceInfo ! =  defaultDeviceInfo)
            m_deviceBox- > addItem(deviceInfo. deviceName(),  qVariantFromValue (deviceInfo));
    }
    connect(m_deviceBox,  SIGNAL(activated(int )),  SLOT(deviceChanged(int )));
    layout- > addWidget(m_deviceBox);
    m_volumeSlider =  new  QSlider Qt :: Horizontal,  this );
    m_volumeSlider- > setRange(0 ,  100 );
    m_volumeSlider- > setValue(100 );
    connect(m_volumeSlider,  SIGNAL(valueChanged(int )),  SLOT(sliderChanged(int )));
    layout- > addWidget(m_volumeSlider);
    m_modeButton =  new  QPushButton this );
    m_modeButton- > setText(tr(PUSH_MODE_LABEL));
    connect(m_modeButton,  SIGNAL(clicked()),  SLOT(toggleMode()));
    layout- > addWidget(m_modeButton);
    m_suspendResumeButton =  new  QPushButton this );
    m_suspendResumeButton- > setText(tr(SUSPEND_LABEL));
    connect(m_suspendResumeButton,  SIGNAL(clicked()),  SLOT(toggleSuspend()));
    layout- > addWidget(m_suspendResumeButton);
    window- > setLayout(layout. data());
    layout. take(); // ownership transferred 
    setCentralWidget(window. data());
    QWidget * const  windowPtr =  window. take(); // ownership transferred 
    windowPtr- > show();
}
void  InputTest:: initializeAudio()
{
    m_format. setSampleRate(8000 );
    m_format. setChannelCount(1 );
    m_format. setSampleSize(16 );
    m_format. setSampleType(QAudioFormat :: SignedInt);
    m_format. setByteOrder(QAudioFormat :: LittleEndian);
    m_format. setCodec("audio/pcm" );
    QAudioDeviceInfo if  (! info. isFormatSupported(m_format)) {
        qWarning () < <  "Default format not supported - trying to use nearest" ;
        m_format =  info. nearestFormat(m_format);
    }
    if  (m_audioInfo)
        delete  m_audioInfo;
    m_audioInfo  =  new  AudioInfo(m_format,  this );
    connect(m_audioInfo,  SIGNAL(update()),  SLOT(refreshDisplay()));
    createAudioInput();
}
void  InputTest:: createAudioInput()
{
    m_audioInput =  new  QAudioInput ,  m_format,  this );
    qreal =  QAudio :: convertVolume(m_audioInput- > volume(), 
                                                QAudio :: LinearVolumeScale, 
                                                QAudio :: LogarithmicVolumeScale);
    m_volumeSlider- > setValue(qRound (initialVolume *  100 ));
    m_audioInfo- > start();
    m_audioInput- > start(m_audioInfo);
}
void  InputTest:: readMore()
{
    if  (! m_audioInput)
        return ;
    qint64 =  m_audioInput- > bytesReady();
    if  (len >  BufferSize)
        len =  BufferSize;
    qint64 =  m_input- > read(m_buffer. data(),  len);
    if  (l >  0 )
        m_audioInfo- > write(m_buffer. constData(),  l);
}
void  InputTest:: toggleMode()
{
    // Change bewteen pull and push modes 
    m_audioInput- > stop();
    if  (m_pullMode) {
        m_modeButton- > setText(tr(PULL_MODE_LABEL));
        m_input =  m_audioInput- > start();
        connect(m_input,  SIGNAL(readyRead()),  SLOT(readMore()));
        m_pullMode =  false ;
    } else  {
        m_modeButton- > setText(tr(PUSH_MODE_LABEL));
        m_pullMode =  true ;
        m_audioInput- > start(m_audioInfo);
    }
    m_suspendResumeButton- > setText(tr(SUSPEND_LABEL));
}
void  InputTest:: toggleSuspend()
{
    // toggle suspend/resume 
    if  (m_audioInput- > state() = =  QAudio :: SuspendedState) {
        m_audioInput- > resume();
        m_suspendResumeButton- > setText(tr(SUSPEND_LABEL));
    } else  if  (m_audioInput- > state() = =  QAudio :: ActiveState) {
        m_audioInput- > suspend();
        m_suspendResumeButton- > setText(tr(RESUME_LABEL));
    } else  if  (m_audioInput- > state() = =  QAudio :: StoppedState) {
        m_audioInput- > resume();
        m_suspendResumeButton- > setText(tr(SUSPEND_LABEL));
    } else  if  (m_audioInput- > state() = =  QAudio :: IdleState) {
        // no-op 
    }
}
void  InputTest:: refreshDisplay()
{
    m_canvas- > setLevel(m_audioInfo- > level());
}
void  InputTest:: deviceChanged(int  index)
{
    m_audioInfo- > stop();
    m_audioInput- > stop();
    m_audioInput- > disconnect(this );
    delete  m_audioInput;
    m_device =  m_deviceBox- > itemData(index). value< QAudioDeviceInfo > ();
    initializeAudio();
}
void  InputTest:: sliderChanged(int  value)
{
    if  (m_audioInput) {
        qreal =   QAudio :: convertVolume(value /  qreal 100 ), 
                                                    QAudio :: LogarithmicVolumeScale, 
                                                    QAudio :: LinearVolumeScale);
        m_audioInput- > setVolume(linearVolume);
    }
}