| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** 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. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| // |
| // W A R N I N G |
| // ------------- |
| // |
| // This file is not part of the Qt API. It exists for the convenience |
| // of other Qt classes. This header file may change from version to |
| // version without notice, or even be removed. |
| // |
| // INTERNAL USE ONLY: Do NOT use for any other purpose. |
| // |
| |
| |
| #include "qwindowsaudioinput.h" |
| |
| #include <QtCore/QDataStream> |
| #include <QtCore/qtimer.h> |
| |
| QT_BEGIN_NAMESPACE |
| |
| //#define DEBUG_AUDIO 1 |
| |
| QWindowsAudioInput::QWindowsAudioInput(const QByteArray &device) |
| { |
| bytesAvailable = 0; |
| buffer_size = 0; |
| period_size = 0; |
| m_device = device; |
| totalTimeValue = 0; |
| intervalTime = 1000; |
| errorState = QAudio::NoError; |
| deviceState = QAudio::StoppedState; |
| audioSource = 0; |
| pullMode = true; |
| resuming = false; |
| finished = false; |
| waveBlockOffset = 0; |
| |
| mixerID = 0; |
| cachedVolume = -1.0f; |
| memset(&mixerLineControls, 0, sizeof(mixerLineControls)); |
| } |
| |
| QWindowsAudioInput::~QWindowsAudioInput() |
| { |
| stop(); |
| } |
| |
| void QT_WIN_CALLBACK QWindowsAudioInput::waveInProc( HWAVEIN hWaveIn, UINT uMsg, |
| DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2 ) |
| { |
| Q_UNUSED(dwParam1) |
| Q_UNUSED(dwParam2) |
| Q_UNUSED(hWaveIn) |
| |
| QWindowsAudioInput* qAudio; |
| qAudio = (QWindowsAudioInput*)(dwInstance); |
| if(!qAudio) |
| return; |
| |
| QMutexLocker locker(&qAudio->mutex); |
| |
| switch(uMsg) { |
| case WIM_OPEN: |
| break; |
| case WIM_DATA: |
| if(qAudio->waveFreeBlockCount > 0) |
| qAudio->waveFreeBlockCount--; |
| qAudio->feedback(); |
| break; |
| case WIM_CLOSE: |
| qAudio->finished = true; |
| break; |
| default: |
| return; |
| } |
| } |
| |
| WAVEHDR* QWindowsAudioInput::allocateBlocks(int size, int count) |
| { |
| int i; |
| unsigned char* buffer; |
| WAVEHDR* blocks; |
| DWORD totalBufferSize = (size + sizeof(WAVEHDR))*count; |
| |
| if((buffer=(unsigned char*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY, |
| totalBufferSize)) == 0) { |
| qWarning("QAudioInput: Memory allocation error"); |
| return 0; |
| } |
| blocks = (WAVEHDR*)buffer; |
| buffer += sizeof(WAVEHDR)*count; |
| for(i = 0; i < count; i++) { |
| blocks[i].dwBufferLength = size; |
| blocks[i].lpData = (LPSTR)buffer; |
| blocks[i].dwBytesRecorded=0; |
| blocks[i].dwUser = 0L; |
| blocks[i].dwFlags = 0L; |
| blocks[i].dwLoops = 0L; |
| result = waveInPrepareHeader(hWaveIn,&blocks[i], sizeof(WAVEHDR)); |
| if(result != MMSYSERR_NOERROR) { |
| qWarning("QAudioInput: Can't prepare block %d",i); |
| return 0; |
| } |
| buffer += size; |
| } |
| return blocks; |
| } |
| |
| void QWindowsAudioInput::freeBlocks(WAVEHDR* blockArray) |
| { |
| WAVEHDR* blocks = blockArray; |
| |
| int count = buffer_size/period_size; |
| |
| for(int i = 0; i < count; i++) { |
| waveInUnprepareHeader(hWaveIn,blocks, sizeof(WAVEHDR)); |
| blocks++; |
| } |
| HeapFree(GetProcessHeap(), 0, blockArray); |
| } |
| |
| QAudio::Error QWindowsAudioInput::error() const |
| { |
| return errorState; |
| } |
| |
| QAudio::State QWindowsAudioInput::state() const |
| { |
| return deviceState; |
| } |
| |
| #ifndef DRVM_MAPPER_CONSOLEVOICECOM_GET |
| #ifndef DRVM_MAPPER |
| #define DRVM_MAPPER 0x2000 |
| #endif |
| #ifndef DRVM_MAPPER_STATUS |
| #define DRVM_MAPPER_STATUS (DRVM_MAPPER+0) |
| #endif |
| #define DRVM_USER 0x4000 |
| #define DRVM_MAPPER_RECONFIGURE (DRVM_MAPPER+1) |
| #define DRVM_MAPPER_PREFERRED_GET (DRVM_MAPPER+21) |
| #define DRVM_MAPPER_CONSOLEVOICECOM_GET (DRVM_MAPPER+23) |
| #endif |
| |
| void QWindowsAudioInput::setVolume(qreal volume) |
| { |
| cachedVolume = volume; |
| for (DWORD i = 0; i < mixerLineControls.cControls; i++) { |
| |
| MIXERCONTROLDETAILS controlDetails; |
| controlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS); |
| controlDetails.dwControlID = mixerLineControls.pamxctrl[i].dwControlID; |
| controlDetails.cChannels = 1; |
| |
| if ((mixerLineControls.pamxctrl[i].dwControlType == MIXERCONTROL_CONTROLTYPE_FADER) || |
| (mixerLineControls.pamxctrl[i].dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)) { |
| MIXERCONTROLDETAILS_UNSIGNED controlDetailsUnsigned; |
| controlDetailsUnsigned.dwValue = qBound(DWORD(0), DWORD(65535.0 * volume + 0.5), DWORD(65535)); |
| controlDetails.cMultipleItems = 0; |
| controlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED); |
| controlDetails.paDetails = &controlDetailsUnsigned; |
| mixerSetControlDetails(mixerID, &controlDetails, MIXER_SETCONTROLDETAILSF_VALUE); |
| } |
| } |
| } |
| |
| qreal QWindowsAudioInput::volume() const |
| { |
| for (DWORD i = 0; i < mixerLineControls.cControls; i++) { |
| if ((mixerLineControls.pamxctrl[i].dwControlType != MIXERCONTROL_CONTROLTYPE_FADER) && |
| (mixerLineControls.pamxctrl[i].dwControlType != MIXERCONTROL_CONTROLTYPE_VOLUME)) { |
| continue; |
| } |
| |
| MIXERCONTROLDETAILS controlDetails; |
| controlDetails.cbStruct = sizeof(controlDetails); |
| controlDetails.dwControlID = mixerLineControls.pamxctrl[i].dwControlID; |
| controlDetails.cChannels = 1; |
| controlDetails.cMultipleItems = 0; |
| controlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED); |
| MIXERCONTROLDETAILS_UNSIGNED detailsUnsigned; |
| controlDetails.paDetails = &detailsUnsigned; |
| memset(controlDetails.paDetails, 0, controlDetails.cbDetails); |
| |
| MMRESULT result = mixerGetControlDetails(mixerID, &controlDetails, MIXER_GETCONTROLDETAILSF_VALUE); |
| if (result != MMSYSERR_NOERROR) |
| continue; |
| if (controlDetails.cbDetails < sizeof(MIXERCONTROLDETAILS_UNSIGNED)) |
| continue; |
| return detailsUnsigned.dwValue / 65535.0; |
| } |
| |
| return qFuzzyCompare(cachedVolume, qreal(-1.0f)) ? 1.0f : cachedVolume; |
| } |
| |
| void QWindowsAudioInput::setFormat(const QAudioFormat& fmt) |
| { |
| if (deviceState == QAudio::StoppedState) |
| settings = fmt; |
| } |
| |
| QAudioFormat QWindowsAudioInput::format() const |
| { |
| return settings; |
| } |
| |
| void QWindowsAudioInput::start(QIODevice* device) |
| { |
| if(deviceState != QAudio::StoppedState) |
| close(); |
| |
| if(!pullMode && audioSource) |
| delete audioSource; |
| |
| pullMode = true; |
| audioSource = device; |
| |
| deviceState = QAudio::ActiveState; |
| |
| if(!open()) |
| return; |
| |
| emit stateChanged(deviceState); |
| } |
| |
| QIODevice* QWindowsAudioInput::start() |
| { |
| if(deviceState != QAudio::StoppedState) |
| close(); |
| |
| if(!pullMode && audioSource) |
| delete audioSource; |
| |
| pullMode = false; |
| audioSource = new InputPrivate(this); |
| audioSource->open(QIODevice::ReadOnly | QIODevice::Unbuffered); |
| |
| deviceState = QAudio::IdleState; |
| |
| if(!open()) |
| return 0; |
| |
| emit stateChanged(deviceState); |
| |
| return audioSource; |
| } |
| |
| void QWindowsAudioInput::stop() |
| { |
| if(deviceState == QAudio::StoppedState) |
| return; |
| |
| close(); |
| emit stateChanged(deviceState); |
| } |
| |
| bool QWindowsAudioInput::open() |
| { |
| #ifdef DEBUG_AUDIO |
| QTime now(QTime::currentTime()); |
| qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()"; |
| #endif |
| header = 0; |
| |
| period_size = 0; |
| |
| if (!qt_convertFormat(settings, &wfx)) { |
| qWarning("QAudioInput: open error, invalid format."); |
| } else if (buffer_size == 0) { |
| buffer_size |
| = (settings.sampleRate() |
| * settings.channelCount() |
| * settings.sampleSize() |
| + 39) / 40; |
| period_size = buffer_size / 5; |
| } else { |
| period_size = buffer_size / 5; |
| } |
| |
| if (period_size == 0) { |
| errorState = QAudio::OpenError; |
| deviceState = QAudio::StoppedState; |
| emit stateChanged(deviceState); |
| return false; |
| } |
| |
| timeStamp.restart(); |
| elapsedTimeOffset = 0; |
| |
| QDataStream ds(&m_device, QIODevice::ReadOnly); |
| quint32 deviceId; |
| ds >> deviceId; |
| |
| if (waveInOpen(&hWaveIn, UINT_PTR(deviceId), &wfx.Format, |
| (DWORD_PTR)&waveInProc, |
| (DWORD_PTR) this, |
| CALLBACK_FUNCTION) != MMSYSERR_NOERROR) { |
| errorState = QAudio::OpenError; |
| deviceState = QAudio::StoppedState; |
| emit stateChanged(deviceState); |
| qWarning("QAudioInput: failed to open audio device"); |
| return false; |
| } |
| waveBlocks = allocateBlocks(period_size, buffer_size/period_size); |
| waveBlockOffset = 0; |
| |
| if(waveBlocks == 0) { |
| errorState = QAudio::OpenError; |
| deviceState = QAudio::StoppedState; |
| emit stateChanged(deviceState); |
| qWarning("QAudioInput: failed to allocate blocks. open failed"); |
| return false; |
| } |
| |
| mutex.lock(); |
| waveFreeBlockCount = buffer_size/period_size; |
| mutex.unlock(); |
| |
| for(int i=0; i<buffer_size/period_size; i++) { |
| result = waveInAddBuffer(hWaveIn, &waveBlocks[i], sizeof(WAVEHDR)); |
| if(result != MMSYSERR_NOERROR) { |
| qWarning("QAudioInput: failed to setup block %d,err=%d",i,result); |
| errorState = QAudio::OpenError; |
| deviceState = QAudio::StoppedState; |
| emit stateChanged(deviceState); |
| return false; |
| } |
| } |
| result = waveInStart(hWaveIn); |
| if(result) { |
| qWarning("QAudioInput: failed to start audio input"); |
| errorState = QAudio::OpenError; |
| deviceState = QAudio::StoppedState; |
| emit stateChanged(deviceState); |
| return false; |
| } |
| timeStampOpened.restart(); |
| elapsedTimeOffset = 0; |
| totalTimeValue = 0; |
| errorState = QAudio::NoError; |
| initMixer(); |
| return true; |
| } |
| |
| void QWindowsAudioInput::close() |
| { |
| if(deviceState == QAudio::StoppedState) |
| return; |
| |
| deviceState = QAudio::StoppedState; |
| waveInReset(hWaveIn); |
| |
| mutex.lock(); |
| for (int i=0; i<waveFreeBlockCount; i++) |
| waveInUnprepareHeader(hWaveIn,&waveBlocks[i],sizeof(WAVEHDR)); |
| freeBlocks(waveBlocks); |
| mutex.unlock(); |
| |
| waveInClose(hWaveIn); |
| closeMixer(); |
| |
| int count = 0; |
| while(!finished && count < 500) { |
| count++; |
| Sleep(10); |
| } |
| } |
| |
| void QWindowsAudioInput::initMixer() |
| { |
| // Get the Mixer ID from the Sound Device ID |
| UINT mixerIntID = 0; |
| if (mixerGetID(reinterpret_cast<HMIXEROBJ>(hWaveIn), |
| &mixerIntID, MIXER_OBJECTF_HWAVEIN) != MMSYSERR_NOERROR) |
| return; |
| mixerID = reinterpret_cast<HMIXEROBJ>(quintptr(mixerIntID)); |
| |
| // Get the Destination (Recording) Line Information |
| MIXERLINE mixerLine; |
| mixerLine.cbStruct = sizeof(MIXERLINE); |
| mixerLine.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN; |
| if (mixerGetLineInfo(mixerID, &mixerLine, MIXER_GETLINEINFOF_COMPONENTTYPE) != MMSYSERR_NOERROR) |
| return; |
| |
| // Set all the Destination (Recording) Line Controls |
| if (mixerLine.cControls > 0) { |
| mixerLineControls.cbStruct = sizeof(MIXERLINECONTROLS); |
| mixerLineControls.dwLineID = mixerLine.dwLineID; |
| mixerLineControls.cControls = mixerLine.cControls; |
| mixerLineControls.cbmxctrl = sizeof(MIXERCONTROL); |
| mixerLineControls.pamxctrl = new MIXERCONTROL[mixerLineControls.cControls]; |
| if (mixerGetLineControls(mixerID, &mixerLineControls, MIXER_GETLINECONTROLSF_ALL) != MMSYSERR_NOERROR) |
| closeMixer(); |
| else if (!qFuzzyCompare(cachedVolume, qreal(-1.0f))) |
| setVolume(cachedVolume); |
| } |
| } |
| |
| void QWindowsAudioInput::closeMixer() |
| { |
| delete[] mixerLineControls.pamxctrl; |
| memset(&mixerLineControls, 0, sizeof(mixerLineControls)); |
| } |
| |
| int QWindowsAudioInput::bytesReady() const |
| { |
| if(period_size == 0 || buffer_size == 0) |
| return 0; |
| |
| int buf = ((buffer_size/period_size)-waveFreeBlockCount)*period_size; |
| if(buf < 0) |
| buf = 0; |
| return buf; |
| } |
| |
| qint64 QWindowsAudioInput::read(char* data, qint64 len) |
| { |
| bool done = false; |
| |
| char* p = data; |
| qint64 l = 0; |
| qint64 written = 0; |
| while(!done) { |
| // Read in some audio data |
| if(waveBlocks[header].dwBytesRecorded > 0 && waveBlocks[header].dwFlags & WHDR_DONE) { |
| if(pullMode) { |
| l = audioSource->write(waveBlocks[header].lpData + waveBlockOffset, |
| waveBlocks[header].dwBytesRecorded - waveBlockOffset); |
| #ifdef DEBUG_AUDIO |
| qDebug()<<"IN: "<<waveBlocks[header].dwBytesRecorded<<", OUT: "<<l; |
| #endif |
| if(l < 0) { |
| // error |
| qWarning("QAudioInput: IOError"); |
| errorState = QAudio::IOError; |
| |
| } else if(l == 0) { |
| // cant write to IODevice |
| qWarning("QAudioInput: IOError, can't write to QIODevice"); |
| errorState = QAudio::IOError; |
| |
| } else { |
| totalTimeValue += l; |
| errorState = QAudio::NoError; |
| if (deviceState != QAudio::ActiveState) { |
| deviceState = QAudio::ActiveState; |
| emit stateChanged(deviceState); |
| } |
| resuming = false; |
| } |
| } else { |
| l = qMin<qint64>(len, waveBlocks[header].dwBytesRecorded - waveBlockOffset); |
| // push mode |
| memcpy(p, waveBlocks[header].lpData + waveBlockOffset, l); |
| |
| len -= l; |
| |
| #ifdef DEBUG_AUDIO |
| qDebug()<<"IN: "<<waveBlocks[header].dwBytesRecorded<<", OUT: "<<l; |
| #endif |
| totalTimeValue += l; |
| errorState = QAudio::NoError; |
| if (deviceState != QAudio::ActiveState) { |
| deviceState = QAudio::ActiveState; |
| emit stateChanged(deviceState); |
| } |
| resuming = false; |
| } |
| } else { |
| //no data, not ready yet, next time |
| break; |
| } |
| |
| if (l < waveBlocks[header].dwBytesRecorded - waveBlockOffset) { |
| waveBlockOffset += l; |
| done = true; |
| } else { |
| waveBlockOffset = 0; |
| |
| waveInUnprepareHeader(hWaveIn,&waveBlocks[header], sizeof(WAVEHDR)); |
| |
| mutex.lock(); |
| waveFreeBlockCount++; |
| mutex.unlock(); |
| |
| waveBlocks[header].dwBytesRecorded=0; |
| waveBlocks[header].dwFlags = 0L; |
| result = waveInPrepareHeader(hWaveIn,&waveBlocks[header], sizeof(WAVEHDR)); |
| if(result != MMSYSERR_NOERROR) { |
| result = waveInPrepareHeader(hWaveIn,&waveBlocks[header], sizeof(WAVEHDR)); |
| qWarning("QAudioInput: failed to prepare block %d,err=%d",header,result); |
| errorState = QAudio::IOError; |
| |
| mutex.lock(); |
| waveFreeBlockCount--; |
| mutex.unlock(); |
| |
| return 0; |
| } |
| result = waveInAddBuffer(hWaveIn, &waveBlocks[header], sizeof(WAVEHDR)); |
| if(result != MMSYSERR_NOERROR) { |
| qWarning("QAudioInput: failed to setup block %d,err=%d",header,result); |
| errorState = QAudio::IOError; |
| |
| mutex.lock(); |
| waveFreeBlockCount--; |
| mutex.unlock(); |
| |
| return 0; |
| } |
| header++; |
| if(header >= buffer_size/period_size) |
| header = 0; |
| p+=l; |
| |
| mutex.lock(); |
| if(!pullMode) { |
| if(len < period_size || waveFreeBlockCount == buffer_size/period_size) |
| done = true; |
| } else { |
| if(waveFreeBlockCount == buffer_size/period_size) |
| done = true; |
| } |
| mutex.unlock(); |
| } |
| |
| written+=l; |
| } |
| #ifdef DEBUG_AUDIO |
| qDebug()<<"read in len="<<written; |
| #endif |
| return written; |
| } |
| |
| void QWindowsAudioInput::resume() |
| { |
| if(deviceState == QAudio::SuspendedState) { |
| deviceState = QAudio::ActiveState; |
| for(int i=0; i<buffer_size/period_size; i++) { |
| result = waveInAddBuffer(hWaveIn, &waveBlocks[i], sizeof(WAVEHDR)); |
| if(result != MMSYSERR_NOERROR) { |
| qWarning("QAudioInput: failed to setup block %d,err=%d",i,result); |
| errorState = QAudio::OpenError; |
| deviceState = QAudio::StoppedState; |
| emit stateChanged(deviceState); |
| return; |
| } |
| } |
| |
| mutex.lock(); |
| waveFreeBlockCount = buffer_size/period_size; |
| mutex.unlock(); |
| |
| header = 0; |
| resuming = true; |
| waveBlockOffset = 0; |
| waveInStart(hWaveIn); |
| QTimer::singleShot(20,this,SLOT(feedback())); |
| emit stateChanged(deviceState); |
| } |
| } |
| |
| void QWindowsAudioInput::setBufferSize(int value) |
| { |
| buffer_size = value; |
| } |
| |
| int QWindowsAudioInput::bufferSize() const |
| { |
| return buffer_size; |
| } |
| |
| int QWindowsAudioInput::periodSize() const |
| { |
| return period_size; |
| } |
| |
| void QWindowsAudioInput::setNotifyInterval(int ms) |
| { |
| intervalTime = qMax(0, ms); |
| } |
| |
| int QWindowsAudioInput::notifyInterval() const |
| { |
| return intervalTime; |
| } |
| |
| qint64 QWindowsAudioInput::processedUSecs() const |
| { |
| if (deviceState == QAudio::StoppedState) |
| return 0; |
| qint64 result = qint64(1000000) * totalTimeValue / |
| (settings.channelCount()*(settings.sampleSize()/8)) / |
| settings.sampleRate(); |
| |
| return result; |
| } |
| |
| void QWindowsAudioInput::suspend() |
| { |
| if(deviceState == QAudio::ActiveState) { |
| waveInReset(hWaveIn); |
| deviceState = QAudio::SuspendedState; |
| emit stateChanged(deviceState); |
| } |
| } |
| |
| void QWindowsAudioInput::feedback() |
| { |
| #ifdef DEBUG_AUDIO |
| QTime now(QTime::currentTime()); |
| qDebug()<<now.second()<<"s "<<now.msec()<<"ms :feedback() INPUT "<<this; |
| #endif |
| if(!(deviceState==QAudio::StoppedState||deviceState==QAudio::SuspendedState)) |
| QMetaObject::invokeMethod(this, "deviceReady", Qt::QueuedConnection); |
| } |
| |
| bool QWindowsAudioInput::deviceReady() |
| { |
| bytesAvailable = bytesReady(); |
| #ifdef DEBUG_AUDIO |
| QTime now(QTime::currentTime()); |
| qDebug()<<now.second()<<"s "<<now.msec()<<"ms :deviceReady() INPUT"; |
| #endif |
| if(deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState) |
| return true; |
| |
| if(pullMode) { |
| // reads some audio data and writes it to QIODevice |
| read(0, buffer_size); |
| } else { |
| // emits readyRead() so user will call read() on QIODevice to get some audio data |
| InputPrivate* a = qobject_cast<InputPrivate*>(audioSource); |
| a->trigger(); |
| } |
| |
| if(intervalTime && (timeStamp.elapsed() + elapsedTimeOffset) > intervalTime) { |
| emit notify(); |
| elapsedTimeOffset = timeStamp.elapsed() + elapsedTimeOffset - intervalTime; |
| timeStamp.restart(); |
| } |
| return true; |
| } |
| |
| qint64 QWindowsAudioInput::elapsedUSecs() const |
| { |
| if (deviceState == QAudio::StoppedState) |
| return 0; |
| |
| return timeStampOpened.elapsed() * qint64(1000); |
| } |
| |
| void QWindowsAudioInput::reset() |
| { |
| stop(); |
| if (period_size > 0) |
| waveFreeBlockCount = buffer_size / period_size; |
| } |
| |
| InputPrivate::InputPrivate(QWindowsAudioInput* audio) |
| { |
| audioDevice = qobject_cast<QWindowsAudioInput*>(audio); |
| } |
| |
| InputPrivate::~InputPrivate() {} |
| |
| qint64 InputPrivate::readData( char* data, qint64 len) |
| { |
| // push mode, user read() called |
| if(audioDevice->deviceState != QAudio::ActiveState && |
| audioDevice->deviceState != QAudio::IdleState) |
| return 0; |
| // Read in some audio data |
| return audioDevice->read(data,len); |
| } |
| |
| qint64 InputPrivate::writeData(const char* data, qint64 len) |
| { |
| Q_UNUSED(data) |
| Q_UNUSED(len) |
| |
| emit readyRead(); |
| return 0; |
| } |
| |
| void InputPrivate::trigger() |
| { |
| emit readyRead(); |
| } |
| |
| QT_END_NAMESPACE |
| |
| #include "moc_qwindowsaudioinput.cpp" |