Initial import from the monolithic Qt.
This is the beginning of revision history for this module. If you want to look at revision history older than this, please refer to the Qt Git wiki for how to use Git history grafting. At the time of writing, this wiki is located here: http://qt.gitorious.org/qt/pages/GitIntroductionWithQt If you have already performed the grafting and you don't see any history beyond this commit, try running "git log" with the "--follow" argument. Branched from the monolithic repo, Qt master branch, at commit 896db169ea224deb96c59ce8af800d019de63f12
This commit is contained in:
73
src/multimedia/audio/audio.pri
Normal file
73
src/multimedia/audio/audio.pri
Normal file
@@ -0,0 +1,73 @@
|
||||
HEADERS += $$PWD/qaudio.h \
|
||||
$$PWD/qaudioformat.h \
|
||||
$$PWD/qaudioinput.h \
|
||||
$$PWD/qaudiooutput.h \
|
||||
$$PWD/qaudiodeviceinfo.h \
|
||||
$$PWD/qaudioengineplugin.h \
|
||||
$$PWD/qaudioengine.h \
|
||||
$$PWD/qaudiodevicefactory_p.h
|
||||
|
||||
|
||||
SOURCES += $$PWD/qaudio.cpp \
|
||||
$$PWD/qaudioformat.cpp \
|
||||
$$PWD/qaudiodeviceinfo.cpp \
|
||||
$$PWD/qaudiooutput.cpp \
|
||||
$$PWD/qaudioinput.cpp \
|
||||
$$PWD/qaudioengineplugin.cpp \
|
||||
$$PWD/qaudioengine.cpp \
|
||||
$$PWD/qaudiodevicefactory.cpp
|
||||
|
||||
contains(QT_CONFIG, audio-backend) {
|
||||
|
||||
mac {
|
||||
HEADERS += $$PWD/qaudioinput_mac_p.h \
|
||||
$$PWD/qaudiooutput_mac_p.h \
|
||||
$$PWD/qaudiodeviceinfo_mac_p.h \
|
||||
$$PWD/qaudio_mac_p.h
|
||||
|
||||
SOURCES += $$PWD/qaudiodeviceinfo_mac_p.cpp \
|
||||
$$PWD/qaudiooutput_mac_p.cpp \
|
||||
$$PWD/qaudioinput_mac_p.cpp \
|
||||
$$PWD/qaudio_mac.cpp
|
||||
|
||||
LIBS += -framework ApplicationServices -framework CoreAudio -framework AudioUnit -framework AudioToolbox
|
||||
|
||||
} else:win32 {
|
||||
|
||||
HEADERS += $$PWD/qaudioinput_win32_p.h $$PWD/qaudiooutput_win32_p.h $$PWD/qaudiodeviceinfo_win32_p.h
|
||||
SOURCES += $$PWD/qaudiodeviceinfo_win32_p.cpp \
|
||||
$$PWD/qaudiooutput_win32_p.cpp \
|
||||
$$PWD/qaudioinput_win32_p.cpp
|
||||
!wince*:LIBS += -lwinmm
|
||||
wince*:LIBS += -lcoredll
|
||||
|
||||
} else:symbian {
|
||||
INCLUDEPATH += $${EPOCROOT}epoc32/include/mmf/common
|
||||
INCLUDEPATH += $${EPOCROOT}epoc32/include/mmf/server
|
||||
|
||||
HEADERS += $$PWD/qaudio_symbian_p.h \
|
||||
$$PWD/qaudiodeviceinfo_symbian_p.h \
|
||||
$$PWD/qaudioinput_symbian_p.h \
|
||||
$$PWD/qaudiooutput_symbian_p.h
|
||||
|
||||
SOURCES += $$PWD/qaudio_symbian_p.cpp \
|
||||
$$PWD/qaudiodeviceinfo_symbian_p.cpp \
|
||||
$$PWD/qaudioinput_symbian_p.cpp \
|
||||
$$PWD/qaudiooutput_symbian_p.cpp
|
||||
|
||||
LIBS += -lmmfdevsound
|
||||
} else:unix {
|
||||
unix:contains(QT_CONFIG, alsa) {
|
||||
linux-*|freebsd-*|openbsd-*:{
|
||||
DEFINES += HAS_ALSA
|
||||
HEADERS += $$PWD/qaudiooutput_alsa_p.h $$PWD/qaudioinput_alsa_p.h $$PWD/qaudiodeviceinfo_alsa_p.h
|
||||
SOURCES += $$PWD/qaudiodeviceinfo_alsa_p.cpp \
|
||||
$$PWD/qaudiooutput_alsa_p.cpp \
|
||||
$$PWD/qaudioinput_alsa_p.cpp
|
||||
LIBS_PRIVATE += -lasound
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DEFINES += QT_NO_AUDIO_BACKEND
|
||||
}
|
||||
104
src/multimedia/audio/qaudio.cpp
Normal file
104
src/multimedia/audio/qaudio.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#include <QtMultimedia/qaudio.h>
|
||||
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace QAudio
|
||||
{
|
||||
|
||||
class RegisterMetaTypes
|
||||
{
|
||||
public:
|
||||
RegisterMetaTypes()
|
||||
{
|
||||
qRegisterMetaType<QAudio::Error>();
|
||||
qRegisterMetaType<QAudio::State>();
|
||||
qRegisterMetaType<QAudio::Mode>();
|
||||
}
|
||||
|
||||
} _register;
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
\namespace QAudio
|
||||
\brief The QAudio namespace contains enums used by the audio classes.
|
||||
\inmodule QtMultimedia
|
||||
\ingroup multimedia
|
||||
\since 4.6
|
||||
*/
|
||||
|
||||
/*!
|
||||
\enum QAudio::Error
|
||||
|
||||
\value NoError No errors have occurred
|
||||
\value OpenError An error opening the audio device
|
||||
\value IOError An error occurred during read/write of audio device
|
||||
\value UnderrunError Audio data is not being fed to the audio device at a fast enough rate
|
||||
\value FatalError A non-recoverable error has occurred, the audio device is not usable at this time.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\enum QAudio::State
|
||||
|
||||
\value ActiveState Audio data is being processed, this state is set after start() is called
|
||||
and while audio data is available to be processed.
|
||||
\value SuspendedState The audio device is in a suspended state, this state will only be entered
|
||||
after suspend() is called.
|
||||
\value StoppedState The audio device is closed, not processing any audio data
|
||||
\value IdleState The QIODevice passed in has no data and audio system's buffer is empty, this state
|
||||
is set after start() is called and while no audio data is available to be processed.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\enum QAudio::Mode
|
||||
|
||||
\value AudioOutput audio output device
|
||||
\value AudioInput audio input device
|
||||
*/
|
||||
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
71
src/multimedia/audio/qaudio.h
Normal file
71
src/multimedia/audio/qaudio.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#ifndef QAUDIO_H
|
||||
#define QAUDIO_H
|
||||
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
#include <QtCore/qmetatype.h>
|
||||
|
||||
QT_BEGIN_HEADER
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QT_MODULE(Multimedia)
|
||||
|
||||
namespace QAudio
|
||||
{
|
||||
enum Error { NoError, OpenError, IOError, UnderrunError, FatalError };
|
||||
enum State { ActiveState, SuspendedState, StoppedState, IdleState };
|
||||
enum Mode { AudioInput, AudioOutput };
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
QT_END_HEADER
|
||||
|
||||
Q_DECLARE_METATYPE(QAudio::Error)
|
||||
Q_DECLARE_METATYPE(QAudio::State)
|
||||
Q_DECLARE_METATYPE(QAudio::Mode)
|
||||
|
||||
#endif // QAUDIO_H
|
||||
145
src/multimedia/audio/qaudio_mac.cpp
Normal file
145
src/multimedia/audio/qaudio_mac.cpp
Normal file
@@ -0,0 +1,145 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#include "qaudio_mac_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
// Debugging
|
||||
QDebug operator<<(QDebug dbg, const QAudioFormat& audioFormat)
|
||||
{
|
||||
dbg.nospace() << "QAudioFormat(" <<
|
||||
audioFormat.frequency() << "," <<
|
||||
audioFormat.channels() << "," <<
|
||||
audioFormat.sampleSize()<< "," <<
|
||||
audioFormat.codec() << "," <<
|
||||
audioFormat.byteOrder() << "," <<
|
||||
audioFormat.sampleType() << ")";
|
||||
|
||||
return dbg.space();
|
||||
}
|
||||
|
||||
|
||||
// Conversion
|
||||
QAudioFormat toQAudioFormat(AudioStreamBasicDescription const& sf)
|
||||
{
|
||||
QAudioFormat audioFormat;
|
||||
|
||||
audioFormat.setFrequency(sf.mSampleRate);
|
||||
audioFormat.setChannels(sf.mChannelsPerFrame);
|
||||
audioFormat.setSampleSize(sf.mBitsPerChannel);
|
||||
audioFormat.setCodec(QString::fromLatin1("audio/pcm"));
|
||||
audioFormat.setByteOrder((sf.mFormatFlags & kAudioFormatFlagIsBigEndian) != 0 ? QAudioFormat::BigEndian : QAudioFormat::LittleEndian);
|
||||
QAudioFormat::SampleType type = QAudioFormat::UnSignedInt;
|
||||
if ((sf.mFormatFlags & kAudioFormatFlagIsSignedInteger) != 0)
|
||||
type = QAudioFormat::SignedInt;
|
||||
else if ((sf.mFormatFlags & kAudioFormatFlagIsFloat) != 0)
|
||||
type = QAudioFormat::Float;
|
||||
audioFormat.setSampleType(type);
|
||||
|
||||
return audioFormat;
|
||||
}
|
||||
|
||||
AudioStreamBasicDescription toAudioStreamBasicDescription(QAudioFormat const& audioFormat)
|
||||
{
|
||||
AudioStreamBasicDescription sf;
|
||||
|
||||
sf.mFormatFlags = kAudioFormatFlagIsPacked;
|
||||
sf.mSampleRate = audioFormat.frequency();
|
||||
sf.mFramesPerPacket = 1;
|
||||
sf.mChannelsPerFrame = audioFormat.channels();
|
||||
sf.mBitsPerChannel = audioFormat.sampleSize();
|
||||
sf.mBytesPerFrame = sf.mChannelsPerFrame * (sf.mBitsPerChannel / 8);
|
||||
sf.mBytesPerPacket = sf.mFramesPerPacket * sf.mBytesPerFrame;
|
||||
sf.mFormatID = kAudioFormatLinearPCM;
|
||||
|
||||
switch (audioFormat.sampleType()) {
|
||||
case QAudioFormat::SignedInt: sf.mFormatFlags |= kAudioFormatFlagIsSignedInteger; break;
|
||||
case QAudioFormat::UnSignedInt: /* default */ break;
|
||||
case QAudioFormat::Float: sf.mFormatFlags |= kAudioFormatFlagIsFloat; break;
|
||||
case QAudioFormat::Unknown: default: break;
|
||||
}
|
||||
|
||||
if (audioFormat.byteOrder() == QAudioFormat::BigEndian)
|
||||
sf.mFormatFlags |= kAudioFormatFlagIsBigEndian;
|
||||
|
||||
return sf;
|
||||
}
|
||||
|
||||
// QAudioRingBuffer
|
||||
QAudioRingBuffer::QAudioRingBuffer(int bufferSize):
|
||||
m_bufferSize(bufferSize)
|
||||
{
|
||||
m_buffer = new char[m_bufferSize];
|
||||
reset();
|
||||
}
|
||||
|
||||
QAudioRingBuffer::~QAudioRingBuffer()
|
||||
{
|
||||
delete m_buffer;
|
||||
}
|
||||
|
||||
int QAudioRingBuffer::used() const
|
||||
{
|
||||
return m_bufferUsed;
|
||||
}
|
||||
|
||||
int QAudioRingBuffer::free() const
|
||||
{
|
||||
return m_bufferSize - m_bufferUsed;
|
||||
}
|
||||
|
||||
int QAudioRingBuffer::size() const
|
||||
{
|
||||
return m_bufferSize;
|
||||
}
|
||||
|
||||
void QAudioRingBuffer::reset()
|
||||
{
|
||||
m_readPos = 0;
|
||||
m_writePos = 0;
|
||||
m_bufferUsed = 0;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
||||
144
src/multimedia/audio/qaudio_mac_p.h
Normal file
144
src/multimedia/audio/qaudio_mac_p.h
Normal file
@@ -0,0 +1,144 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $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.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
|
||||
#ifndef QAUDIO_MAC_P_H
|
||||
#define QAUDIO_MAC_P_H
|
||||
|
||||
#include <CoreAudio/CoreAudio.h>
|
||||
|
||||
#include <QtCore/qdebug.h>
|
||||
#include <QtCore/qatomic.h>
|
||||
|
||||
#include <QtMultimedia/qaudioformat.h>
|
||||
|
||||
QT_BEGIN_HEADER
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QT_MODULE(Multimedia)
|
||||
|
||||
|
||||
extern QDebug operator<<(QDebug dbg, const QAudioFormat& audioFormat);
|
||||
|
||||
extern QAudioFormat toQAudioFormat(const AudioStreamBasicDescription& streamFormat);
|
||||
extern AudioStreamBasicDescription toAudioStreamBasicDescription(QAudioFormat const& audioFormat);
|
||||
|
||||
class QAudioRingBuffer
|
||||
{
|
||||
public:
|
||||
typedef QPair<char*, int> Region;
|
||||
|
||||
QAudioRingBuffer(int bufferSize);
|
||||
~QAudioRingBuffer();
|
||||
|
||||
Region acquireReadRegion(int size)
|
||||
{
|
||||
const int used = m_bufferUsed.fetchAndAddAcquire(0);
|
||||
|
||||
if (used > 0) {
|
||||
const int readSize = qMin(size, qMin(m_bufferSize - m_readPos, used));
|
||||
|
||||
return readSize > 0 ? Region(m_buffer + m_readPos, readSize) : Region(0, 0);
|
||||
}
|
||||
|
||||
return Region(0, 0);
|
||||
}
|
||||
|
||||
void releaseReadRegion(Region const& region)
|
||||
{
|
||||
m_readPos = (m_readPos + region.second) % m_bufferSize;
|
||||
|
||||
m_bufferUsed.fetchAndAddRelease(-region.second);
|
||||
}
|
||||
|
||||
Region acquireWriteRegion(int size)
|
||||
{
|
||||
const int free = m_bufferSize - m_bufferUsed.fetchAndAddAcquire(0);
|
||||
|
||||
if (free > 0) {
|
||||
const int writeSize = qMin(size, qMin(m_bufferSize - m_writePos, free));
|
||||
|
||||
return writeSize > 0 ? Region(m_buffer + m_writePos, writeSize) : Region(0, 0);
|
||||
}
|
||||
|
||||
return Region(0, 0);
|
||||
}
|
||||
|
||||
void releaseWriteRegion(Region const& region)
|
||||
{
|
||||
m_writePos = (m_writePos + region.second) % m_bufferSize;
|
||||
|
||||
m_bufferUsed.fetchAndAddRelease(region.second);
|
||||
}
|
||||
|
||||
int used() const;
|
||||
int free() const;
|
||||
int size() const;
|
||||
|
||||
void reset();
|
||||
|
||||
private:
|
||||
int m_bufferSize;
|
||||
int m_readPos;
|
||||
int m_writePos;
|
||||
char* m_buffer;
|
||||
QAtomicInt m_bufferUsed;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
QT_END_HEADER
|
||||
|
||||
#endif // QAUDIO_MAC_P_H
|
||||
|
||||
|
||||
644
src/multimedia/audio/qaudio_symbian_p.cpp
Normal file
644
src/multimedia/audio/qaudio_symbian_p.cpp
Normal file
@@ -0,0 +1,644 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qaudio_symbian_p.h"
|
||||
#include <mmffourcc.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace SymbianAudio {
|
||||
namespace Utils {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Static data
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Sample rate / frequency
|
||||
|
||||
typedef TMMFSampleRate SampleRateNative;
|
||||
typedef int SampleRateQt;
|
||||
|
||||
const int SampleRateCount = 12;
|
||||
|
||||
const SampleRateNative SampleRateListNative[SampleRateCount] = {
|
||||
EMMFSampleRate8000Hz
|
||||
, EMMFSampleRate11025Hz
|
||||
, EMMFSampleRate12000Hz
|
||||
, EMMFSampleRate16000Hz
|
||||
, EMMFSampleRate22050Hz
|
||||
, EMMFSampleRate24000Hz
|
||||
, EMMFSampleRate32000Hz
|
||||
, EMMFSampleRate44100Hz
|
||||
, EMMFSampleRate48000Hz
|
||||
, EMMFSampleRate64000Hz
|
||||
, EMMFSampleRate88200Hz
|
||||
, EMMFSampleRate96000Hz
|
||||
};
|
||||
|
||||
const SampleRateQt SampleRateListQt[SampleRateCount] = {
|
||||
8000
|
||||
, 11025
|
||||
, 12000
|
||||
, 16000
|
||||
, 22050
|
||||
, 24000
|
||||
, 32000
|
||||
, 44100
|
||||
, 48000
|
||||
, 64000
|
||||
, 88200
|
||||
, 96000
|
||||
};
|
||||
|
||||
// Channels
|
||||
|
||||
typedef TMMFMonoStereo ChannelsNative;
|
||||
typedef int ChannelsQt;
|
||||
|
||||
const int ChannelsCount = 2;
|
||||
|
||||
const ChannelsNative ChannelsListNative[ChannelsCount] = {
|
||||
EMMFMono
|
||||
, EMMFStereo
|
||||
};
|
||||
|
||||
const ChannelsQt ChannelsListQt[ChannelsCount] = {
|
||||
1
|
||||
, 2
|
||||
};
|
||||
|
||||
// Encoding
|
||||
|
||||
const int EncodingCount = 6;
|
||||
|
||||
const TUint32 EncodingFourCC[EncodingCount] = {
|
||||
KMMFFourCCCodePCM8 // 0
|
||||
, KMMFFourCCCodePCMU8 // 1
|
||||
, KMMFFourCCCodePCM16 // 2
|
||||
, KMMFFourCCCodePCMU16 // 3
|
||||
, KMMFFourCCCodePCM16B // 4
|
||||
, KMMFFourCCCodePCMU16B // 5
|
||||
};
|
||||
|
||||
// The characterised DevSound API specification states that the iEncoding
|
||||
// field in TMMFCapabilities is ignored, and that the FourCC should be used
|
||||
// to specify the PCM encoding.
|
||||
// See "SGL.GT0287.102 Multimedia DevSound Baseline Compatibility.doc" in the
|
||||
// mm_info/mm_docs repository.
|
||||
const TMMFSoundEncoding EncodingNative[EncodingCount] = {
|
||||
EMMFSoundEncoding16BitPCM // 0
|
||||
, EMMFSoundEncoding16BitPCM // 1
|
||||
, EMMFSoundEncoding16BitPCM // 2
|
||||
, EMMFSoundEncoding16BitPCM // 3
|
||||
, EMMFSoundEncoding16BitPCM // 4
|
||||
, EMMFSoundEncoding16BitPCM // 5
|
||||
};
|
||||
|
||||
|
||||
const int EncodingSampleSize[EncodingCount] = {
|
||||
8 // 0
|
||||
, 8 // 1
|
||||
, 16 // 2
|
||||
, 16 // 3
|
||||
, 16 // 4
|
||||
, 16 // 5
|
||||
};
|
||||
|
||||
const QAudioFormat::Endian EncodingByteOrder[EncodingCount] = {
|
||||
QAudioFormat::LittleEndian // 0
|
||||
, QAudioFormat::LittleEndian // 1
|
||||
, QAudioFormat::LittleEndian // 2
|
||||
, QAudioFormat::LittleEndian // 3
|
||||
, QAudioFormat::BigEndian // 4
|
||||
, QAudioFormat::BigEndian // 5
|
||||
};
|
||||
|
||||
const QAudioFormat::SampleType EncodingSampleType[EncodingCount] = {
|
||||
QAudioFormat::SignedInt // 0
|
||||
, QAudioFormat::UnSignedInt // 1
|
||||
, QAudioFormat::SignedInt // 2
|
||||
, QAudioFormat::UnSignedInt // 3
|
||||
, QAudioFormat::SignedInt // 4
|
||||
, QAudioFormat::UnSignedInt // 5
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Private functions
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Helper functions for implementing parameter conversions
|
||||
|
||||
template<typename Input>
|
||||
bool findValue(const Input *inputArray, int length, Input input, int &index) {
|
||||
bool result = false;
|
||||
for (int i=0; !result && i<length; ++i)
|
||||
if (inputArray[i] == input) {
|
||||
index = i;
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename Input, typename Output>
|
||||
bool convertValue(const Input *inputArray, const Output *outputArray,
|
||||
int length, Input input, Output &output) {
|
||||
int index;
|
||||
const bool result = findValue<Input>(inputArray, length, input, index);
|
||||
if (result)
|
||||
output = outputArray[index];
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Macro which is used to generate the implementation of the conversion
|
||||
* functions. The implementation is just a wrapper around the templated
|
||||
* convertValue function, e.g.
|
||||
*
|
||||
* CONVERSION_FUNCTION_IMPL(SampleRate, Qt, Native)
|
||||
*
|
||||
* expands to
|
||||
*
|
||||
* bool SampleRateQtToNative(int input, TMMFSampleRate &output) {
|
||||
* return convertValue<SampleRateQt, SampleRateNative>
|
||||
* (SampleRateListQt, SampleRateListNative, SampleRateCount,
|
||||
* input, output);
|
||||
* }
|
||||
*/
|
||||
#define CONVERSION_FUNCTION_IMPL(FieldLc, Field, Input, Output) \
|
||||
bool FieldLc##Input##To##Output(Field##Input input, Field##Output &output) { \
|
||||
return convertValue<Field##Input, Field##Output>(Field##List##Input, \
|
||||
Field##List##Output, Field##Count, input, output); \
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local helper functions
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
CONVERSION_FUNCTION_IMPL(sampleRate, SampleRate, Qt, Native)
|
||||
CONVERSION_FUNCTION_IMPL(sampleRate, SampleRate, Native, Qt)
|
||||
CONVERSION_FUNCTION_IMPL(channels, Channels, Qt, Native)
|
||||
CONVERSION_FUNCTION_IMPL(channels, Channels, Native, Qt)
|
||||
|
||||
bool sampleInfoQtToNative(int inputSampleSize,
|
||||
QAudioFormat::Endian inputByteOrder,
|
||||
QAudioFormat::SampleType inputSampleType,
|
||||
TUint32 &outputFourCC,
|
||||
TMMFSoundEncoding &outputEncoding) {
|
||||
|
||||
bool found = false;
|
||||
|
||||
for (int i=0; i<EncodingCount && !found; ++i) {
|
||||
if ( EncodingSampleSize[i] == inputSampleSize
|
||||
&& EncodingByteOrder[i] == inputByteOrder
|
||||
&& EncodingSampleType[i] == inputSampleType) {
|
||||
outputFourCC = EncodingFourCC[i];
|
||||
outputEncoding = EncodingNative[i]; // EMMFSoundEncoding16BitPCM
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
void capabilitiesNativeToQt(const TMMFCapabilities &caps,
|
||||
const TFourCC &fourcc,
|
||||
QList<int> &frequencies,
|
||||
QList<int> &channels,
|
||||
QList<int> &sampleSizes,
|
||||
QList<QAudioFormat::Endian> &byteOrders,
|
||||
QList<QAudioFormat::SampleType> &sampleTypes) {
|
||||
|
||||
frequencies.clear();
|
||||
sampleSizes.clear();
|
||||
byteOrders.clear();
|
||||
sampleTypes.clear();
|
||||
channels.clear();
|
||||
|
||||
for (int i=0; i<SampleRateCount; ++i)
|
||||
if (caps.iRate & SampleRateListNative[i])
|
||||
frequencies += SampleRateListQt[i];
|
||||
|
||||
for (int i=0; i<ChannelsCount; ++i)
|
||||
if (caps.iChannels & ChannelsListNative[i])
|
||||
channels += ChannelsListQt[i];
|
||||
|
||||
for (int i=0; i<EncodingCount; ++i) {
|
||||
if (fourcc == EncodingFourCC[i]) {
|
||||
sampleSizes += EncodingSampleSize[i];
|
||||
byteOrders += EncodingByteOrder[i];
|
||||
sampleTypes += EncodingSampleType[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool formatQtToNative(const QAudioFormat &inputFormat,
|
||||
TUint32 &outputFourCC,
|
||||
TMMFCapabilities &outputFormat) {
|
||||
|
||||
bool result = false;
|
||||
|
||||
// Need to use temporary variables because TMMFCapabilities fields are all
|
||||
// TInt, rather than MMF enumerated types.
|
||||
TMMFSampleRate outputSampleRate;
|
||||
TMMFMonoStereo outputChannels;
|
||||
TMMFSoundEncoding outputEncoding;
|
||||
|
||||
if (inputFormat.codec() == QLatin1String("audio/pcm")) {
|
||||
result =
|
||||
sampleRateQtToNative(inputFormat.frequency(), outputSampleRate)
|
||||
&& channelsQtToNative(inputFormat.channels(), outputChannels)
|
||||
&& sampleInfoQtToNative(inputFormat.sampleSize(),
|
||||
inputFormat.byteOrder(),
|
||||
inputFormat.sampleType(),
|
||||
outputFourCC,
|
||||
outputEncoding);
|
||||
}
|
||||
|
||||
if (result) {
|
||||
outputFormat.iRate = outputSampleRate;
|
||||
outputFormat.iChannels = outputChannels;
|
||||
outputFormat.iEncoding = outputEncoding;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QAudio::State stateNativeToQt(State nativeState)
|
||||
{
|
||||
switch (nativeState) {
|
||||
case ClosedState:
|
||||
return QAudio::StoppedState;
|
||||
case InitializingState:
|
||||
return QAudio::StoppedState;
|
||||
case ActiveState:
|
||||
return QAudio::ActiveState;
|
||||
case IdleState:
|
||||
return QAudio::IdleState;
|
||||
case SuspendedPausedState:
|
||||
case SuspendedStoppedState:
|
||||
return QAudio::SuspendedState;
|
||||
default:
|
||||
Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid state");
|
||||
return QAudio::StoppedState; // suppress compiler warning
|
||||
}
|
||||
}
|
||||
|
||||
qint64 bytesToSamples(const QAudioFormat &format, qint64 length)
|
||||
{
|
||||
return length / ((format.sampleSize() / 8) * format.channels());
|
||||
}
|
||||
|
||||
qint64 samplesToBytes(const QAudioFormat &format, qint64 samples)
|
||||
{
|
||||
return samples * (format.sampleSize() / 8) * format.channels();
|
||||
}
|
||||
|
||||
} // namespace Utils
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// DevSoundWrapper
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DevSoundWrapper::DevSoundWrapper(QAudio::Mode mode, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_mode(mode)
|
||||
, m_state(StateIdle)
|
||||
, m_devsound(0)
|
||||
, m_fourcc(0)
|
||||
{
|
||||
QT_TRAP_THROWING(m_devsound = CMMFDevSound::NewL());
|
||||
|
||||
switch (mode) {
|
||||
case QAudio::AudioOutput:
|
||||
m_nativeMode = EMMFStatePlaying;
|
||||
break;
|
||||
|
||||
case QAudio::AudioInput:
|
||||
m_nativeMode = EMMFStateRecording;
|
||||
break;
|
||||
|
||||
default:
|
||||
Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid mode");
|
||||
}
|
||||
|
||||
getSupportedCodecs();
|
||||
}
|
||||
|
||||
DevSoundWrapper::~DevSoundWrapper()
|
||||
{
|
||||
delete m_devsound;
|
||||
}
|
||||
|
||||
const QList<QString>& DevSoundWrapper::supportedCodecs() const
|
||||
{
|
||||
return m_supportedCodecs;
|
||||
}
|
||||
|
||||
void DevSoundWrapper::initialize(const QString& codec)
|
||||
{
|
||||
Q_ASSERT(StateInitializing != m_state);
|
||||
m_state = StateInitializing;
|
||||
if (QLatin1String("audio/pcm") == codec) {
|
||||
m_fourcc = KMMFFourCCCodePCM16;
|
||||
TRAPD(err, m_devsound->InitializeL(*this, m_fourcc, m_nativeMode));
|
||||
if (KErrNone != err) {
|
||||
m_state = StateIdle;
|
||||
emit initializeComplete(err);
|
||||
}
|
||||
} else {
|
||||
emit initializeComplete(KErrNotSupported);
|
||||
}
|
||||
}
|
||||
|
||||
const QList<int>& DevSoundWrapper::supportedFrequencies() const
|
||||
{
|
||||
Q_ASSERT(StateInitialized == m_state);
|
||||
return m_supportedFrequencies;
|
||||
}
|
||||
|
||||
const QList<int>& DevSoundWrapper::supportedChannels() const
|
||||
{
|
||||
Q_ASSERT(StateInitialized == m_state);
|
||||
return m_supportedChannels;
|
||||
}
|
||||
|
||||
const QList<int>& DevSoundWrapper::supportedSampleSizes() const
|
||||
{
|
||||
Q_ASSERT(StateInitialized == m_state);
|
||||
return m_supportedSampleSizes;
|
||||
}
|
||||
|
||||
const QList<QAudioFormat::Endian>& DevSoundWrapper::supportedByteOrders() const
|
||||
{
|
||||
Q_ASSERT(StateInitialized == m_state);
|
||||
return m_supportedByteOrders;
|
||||
}
|
||||
|
||||
const QList<QAudioFormat::SampleType>& DevSoundWrapper::supportedSampleTypes() const
|
||||
{
|
||||
Q_ASSERT(StateInitialized == m_state);
|
||||
return m_supportedSampleTypes;
|
||||
}
|
||||
|
||||
bool DevSoundWrapper::isFormatSupported(const QAudioFormat &format) const
|
||||
{
|
||||
Q_ASSERT(StateInitialized == m_state);
|
||||
return m_supportedCodecs.contains(format.codec())
|
||||
&& m_supportedFrequencies.contains(format.frequency())
|
||||
&& m_supportedChannels.contains(format.channels())
|
||||
&& m_supportedSampleSizes.contains(format.sampleSize())
|
||||
&& m_supportedSampleTypes.contains(format.sampleType())
|
||||
&& m_supportedByteOrders.contains(format.byteOrder());
|
||||
}
|
||||
|
||||
int DevSoundWrapper::samplesProcessed() const
|
||||
{
|
||||
int result = 0;
|
||||
if (StateInitialized == m_state) {
|
||||
switch (m_mode) {
|
||||
case QAudio::AudioInput:
|
||||
result = m_devsound->SamplesRecorded();
|
||||
break;
|
||||
case QAudio::AudioOutput:
|
||||
result = m_devsound->SamplesPlayed();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool DevSoundWrapper::setFormat(const QAudioFormat &format)
|
||||
{
|
||||
Q_ASSERT(StateInitialized == m_state);
|
||||
bool result = false;
|
||||
TUint32 fourcc;
|
||||
TMMFCapabilities nativeFormat;
|
||||
if (Utils::formatQtToNative(format, fourcc, nativeFormat)) {
|
||||
TMMFCapabilities currentNativeFormat = m_devsound->Config();
|
||||
nativeFormat.iBufferSize = currentNativeFormat.iBufferSize;
|
||||
TRAPD(err, m_devsound->SetConfigL(nativeFormat));
|
||||
result = (KErrNone == err);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool DevSoundWrapper::start()
|
||||
{
|
||||
Q_ASSERT(StateInitialized == m_state);
|
||||
int err = KErrArgument;
|
||||
switch (m_mode) {
|
||||
case QAudio::AudioInput:
|
||||
TRAP(err, m_devsound->RecordInitL());
|
||||
break;
|
||||
case QAudio::AudioOutput:
|
||||
TRAP(err, m_devsound->PlayInitL());
|
||||
break;
|
||||
}
|
||||
return (KErrNone == err);
|
||||
}
|
||||
|
||||
bool DevSoundWrapper::pause()
|
||||
{
|
||||
Q_ASSERT(StateInitialized == m_state);
|
||||
const bool canPause = isResumeSupported();
|
||||
if (canPause)
|
||||
m_devsound->Pause();
|
||||
else
|
||||
stop();
|
||||
return canPause;
|
||||
}
|
||||
|
||||
void DevSoundWrapper::resume()
|
||||
{
|
||||
Q_ASSERT(StateInitialized == m_state);
|
||||
Q_ASSERT(isResumeSupported());
|
||||
// TODO: QTBUG-13625
|
||||
}
|
||||
|
||||
void DevSoundWrapper::stop()
|
||||
{
|
||||
m_devsound->Stop();
|
||||
}
|
||||
|
||||
void DevSoundWrapper::bufferProcessed()
|
||||
{
|
||||
Q_ASSERT(StateInitialized == m_state);
|
||||
switch (m_mode) {
|
||||
case QAudio::AudioInput:
|
||||
m_devsound->RecordData();
|
||||
break;
|
||||
case QAudio::AudioOutput:
|
||||
m_devsound->PlayData();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DevSoundWrapper::getSupportedCodecs()
|
||||
{
|
||||
/*
|
||||
* TODO: once we support formats other than PCM, this function should
|
||||
* convert the array of FourCC codes into MIME types for each codec.
|
||||
*
|
||||
RArray<TFourCC> fourcc;
|
||||
QT_TRAP_THROWING(CleanupClosePushL(&fourcc));
|
||||
|
||||
TMMFPrioritySettings settings;
|
||||
switch (mode) {
|
||||
case QAudio::AudioOutput:
|
||||
settings.iState = EMMFStatePlaying;
|
||||
m_devsound->GetSupportedInputDataTypesL(fourcc, settings);
|
||||
break;
|
||||
|
||||
case QAudio::AudioInput:
|
||||
settings.iState = EMMFStateRecording;
|
||||
m_devsound->GetSupportedInputDataTypesL(fourcc, settings);
|
||||
break;
|
||||
|
||||
default:
|
||||
Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid mode");
|
||||
}
|
||||
|
||||
CleanupStack::PopAndDestroy(); // fourcc
|
||||
*/
|
||||
|
||||
m_supportedCodecs.append(QLatin1String("audio/pcm"));
|
||||
}
|
||||
|
||||
void DevSoundWrapper::populateCapabilities()
|
||||
{
|
||||
m_supportedFrequencies.clear();
|
||||
m_supportedChannels.clear();
|
||||
m_supportedSampleSizes.clear();
|
||||
m_supportedByteOrders.clear();
|
||||
m_supportedSampleTypes.clear();
|
||||
|
||||
const TMMFCapabilities caps = m_devsound->Capabilities();
|
||||
|
||||
for (int i=0; i<Utils::SampleRateCount; ++i)
|
||||
if (caps.iRate & Utils::SampleRateListNative[i])
|
||||
m_supportedFrequencies += Utils::SampleRateListQt[i];
|
||||
|
||||
for (int i=0; i<Utils::ChannelsCount; ++i)
|
||||
if (caps.iChannels & Utils::ChannelsListNative[i])
|
||||
m_supportedChannels += Utils::ChannelsListQt[i];
|
||||
|
||||
for (int i=0; i<Utils::EncodingCount; ++i) {
|
||||
if (m_fourcc == Utils::EncodingFourCC[i]) {
|
||||
m_supportedSampleSizes += Utils::EncodingSampleSize[i];
|
||||
m_supportedByteOrders += Utils::EncodingByteOrder[i];
|
||||
m_supportedSampleTypes += Utils::EncodingSampleType[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool DevSoundWrapper::isResumeSupported() const
|
||||
{
|
||||
// TODO: QTBUG-13625
|
||||
return false;
|
||||
}
|
||||
|
||||
void DevSoundWrapper::InitializeComplete(TInt aError)
|
||||
{
|
||||
Q_ASSERT(StateInitializing == m_state);
|
||||
if (KErrNone == aError) {
|
||||
m_state = StateInitialized;
|
||||
populateCapabilities();
|
||||
} else {
|
||||
m_state = StateIdle;
|
||||
}
|
||||
emit initializeComplete(aError);
|
||||
}
|
||||
|
||||
void DevSoundWrapper::ToneFinished(TInt aError)
|
||||
{
|
||||
Q_UNUSED(aError)
|
||||
// This class doesn't use DevSound's tone playback functions, so should
|
||||
// never receive this callback.
|
||||
Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected callback");
|
||||
}
|
||||
|
||||
void DevSoundWrapper::BufferToBeFilled(CMMFBuffer *aBuffer)
|
||||
{
|
||||
Q_ASSERT(QAudio::AudioOutput == m_mode);
|
||||
emit bufferToBeProcessed(aBuffer);
|
||||
}
|
||||
|
||||
void DevSoundWrapper::PlayError(TInt aError)
|
||||
{
|
||||
Q_ASSERT(QAudio::AudioOutput == m_mode);
|
||||
emit processingError(aError);
|
||||
}
|
||||
|
||||
void DevSoundWrapper::BufferToBeEmptied(CMMFBuffer *aBuffer)
|
||||
{
|
||||
Q_ASSERT(QAudio::AudioInput == m_mode);
|
||||
emit bufferToBeProcessed(aBuffer);
|
||||
}
|
||||
|
||||
void DevSoundWrapper::RecordError(TInt aError)
|
||||
{
|
||||
Q_ASSERT(QAudio::AudioInput == m_mode);
|
||||
emit processingError(aError);
|
||||
}
|
||||
|
||||
void DevSoundWrapper::ConvertError(TInt aError)
|
||||
{
|
||||
Q_UNUSED(aError)
|
||||
// This class doesn't use DevSound's format conversion functions, so
|
||||
// should never receive this callback.
|
||||
Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected callback");
|
||||
}
|
||||
|
||||
void DevSoundWrapper::DeviceMessage(TUid aMessageType, const TDesC8 &aMsg)
|
||||
{
|
||||
Q_UNUSED(aMessageType)
|
||||
Q_UNUSED(aMsg)
|
||||
// Ignore this callback.
|
||||
}
|
||||
|
||||
|
||||
} // namespace SymbianAudio
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
||||
200
src/multimedia/audio/qaudio_symbian_p.h
Normal file
200
src/multimedia/audio/qaudio_symbian_p.h
Normal file
@@ -0,0 +1,200 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $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.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#ifndef QAUDIO_SYMBIAN_P_H
|
||||
#define QAUDIO_SYMBIAN_P_H
|
||||
|
||||
#include <QtCore/QList>
|
||||
#include <QtCore/QString>
|
||||
#include <QtMultimedia/qaudioformat.h>
|
||||
#include <QtMultimedia/qaudio.h>
|
||||
#include <sounddevice.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace SymbianAudio {
|
||||
|
||||
/**
|
||||
* Default values used by audio input and output classes, when underlying
|
||||
* DevSound instance has not yet been created.
|
||||
*/
|
||||
|
||||
const int DefaultBufferSize = 4096; // bytes
|
||||
const int DefaultNotifyInterval = 1000; // ms
|
||||
|
||||
/**
|
||||
* Enumeration used to track state of internal DevSound instances.
|
||||
* Values are translated to the corresponding QAudio::State values by
|
||||
* SymbianAudio::Utils::stateNativeToQt.
|
||||
*/
|
||||
enum State {
|
||||
ClosedState
|
||||
, InitializingState
|
||||
, ActiveState
|
||||
, IdleState
|
||||
// QAudio is suspended; DevSound is paused
|
||||
, SuspendedPausedState
|
||||
// QAudio is suspended; DevSound is stopped
|
||||
, SuspendedStoppedState
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrapper around DevSound instance
|
||||
*/
|
||||
class DevSoundWrapper
|
||||
: public QObject
|
||||
, public MDevSoundObserver
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DevSoundWrapper(QAudio::Mode mode, QObject *parent = 0);
|
||||
~DevSoundWrapper();
|
||||
|
||||
public:
|
||||
// List of supported codecs; can be called once object is constructed
|
||||
const QList<QString>& supportedCodecs() const;
|
||||
|
||||
// Asynchronous initialization function; emits devsoundInitializeComplete
|
||||
void initialize(const QString& codec);
|
||||
|
||||
// Capabilities, for selected codec. Can be called once initialize has returned
|
||||
// successfully.
|
||||
const QList<int>& supportedFrequencies() const;
|
||||
const QList<int>& supportedChannels() const;
|
||||
const QList<int>& supportedSampleSizes() const;
|
||||
const QList<QAudioFormat::Endian>& supportedByteOrders() const;
|
||||
const QList<QAudioFormat::SampleType>& supportedSampleTypes() const;
|
||||
|
||||
bool isFormatSupported(const QAudioFormat &format) const;
|
||||
|
||||
int samplesProcessed() const;
|
||||
bool setFormat(const QAudioFormat &format);
|
||||
bool start();
|
||||
|
||||
// If DevSound implementation supports pause, calls pause and returns true.
|
||||
// Otherwise calls stop and returns false. In this case, all DevSound buffers
|
||||
// currently held by the backend must be discarded.
|
||||
bool pause();
|
||||
|
||||
void resume();
|
||||
|
||||
void stop();
|
||||
void bufferProcessed();
|
||||
|
||||
public:
|
||||
// MDevSoundObserver
|
||||
void InitializeComplete(TInt aError);
|
||||
void ToneFinished(TInt aError);
|
||||
void BufferToBeFilled(CMMFBuffer *aBuffer);
|
||||
void PlayError(TInt aError);
|
||||
void BufferToBeEmptied(CMMFBuffer *aBuffer);
|
||||
void RecordError(TInt aError);
|
||||
void ConvertError(TInt aError);
|
||||
void DeviceMessage(TUid aMessageType, const TDesC8 &aMsg);
|
||||
|
||||
signals:
|
||||
void initializeComplete(int error);
|
||||
void bufferToBeProcessed(CMMFBuffer *buffer);
|
||||
void processingError(int error);
|
||||
|
||||
private:
|
||||
void getSupportedCodecs();
|
||||
void populateCapabilities();
|
||||
bool isResumeSupported() const;
|
||||
|
||||
private:
|
||||
const QAudio::Mode m_mode;
|
||||
TMMFState m_nativeMode;
|
||||
|
||||
enum State {
|
||||
StateIdle,
|
||||
StateInitializing,
|
||||
StateInitialized
|
||||
} m_state;
|
||||
|
||||
CMMFDevSound* m_devsound;
|
||||
TFourCC m_fourcc;
|
||||
|
||||
QList<QString> m_supportedCodecs;
|
||||
QList<int> m_supportedFrequencies;
|
||||
QList<int> m_supportedChannels;
|
||||
QList<int> m_supportedSampleSizes;
|
||||
QList<QAudioFormat::Endian> m_supportedByteOrders;
|
||||
QList<QAudioFormat::SampleType> m_supportedSampleTypes;
|
||||
|
||||
};
|
||||
|
||||
|
||||
namespace Utils {
|
||||
|
||||
/**
|
||||
* Convert internal states to QAudio states.
|
||||
*/
|
||||
QAudio::State stateNativeToQt(State nativeState);
|
||||
|
||||
/**
|
||||
* Convert data length to number of samples.
|
||||
*/
|
||||
qint64 bytesToSamples(const QAudioFormat &format, qint64 length);
|
||||
|
||||
/**
|
||||
* Convert number of samples to data length.
|
||||
*/
|
||||
qint64 samplesToBytes(const QAudioFormat &format, qint64 samples);
|
||||
|
||||
} // namespace Utils
|
||||
} // namespace SymbianAudio
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
269
src/multimedia/audio/qaudiodevicefactory.cpp
Normal file
269
src/multimedia/audio/qaudiodevicefactory.cpp
Normal file
@@ -0,0 +1,269 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QtCore/qdebug.h>
|
||||
#include <QtMultimedia/qaudioengine.h>
|
||||
#include <QtMultimedia/qaudioengineplugin.h>
|
||||
#include <private/qfactoryloader_p.h>
|
||||
#include "qaudiodevicefactory_p.h"
|
||||
|
||||
#ifndef QT_NO_AUDIO_BACKEND
|
||||
#if defined(Q_OS_WIN)
|
||||
#include "qaudiodeviceinfo_win32_p.h"
|
||||
#include "qaudiooutput_win32_p.h"
|
||||
#include "qaudioinput_win32_p.h"
|
||||
#elif defined(Q_OS_MAC)
|
||||
#include "qaudiodeviceinfo_mac_p.h"
|
||||
#include "qaudiooutput_mac_p.h"
|
||||
#include "qaudioinput_mac_p.h"
|
||||
#elif defined(HAS_ALSA)
|
||||
#include "qaudiodeviceinfo_alsa_p.h"
|
||||
#include "qaudiooutput_alsa_p.h"
|
||||
#include "qaudioinput_alsa_p.h"
|
||||
#elif defined(Q_OS_SYMBIAN)
|
||||
#include "qaudiodeviceinfo_symbian_p.h"
|
||||
#include "qaudiooutput_symbian_p.h"
|
||||
#include "qaudioinput_symbian_p.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
||||
#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
|
||||
Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
|
||||
(QAudioEngineFactoryInterface_iid, QLatin1String("/audio"), Qt::CaseInsensitive))
|
||||
#endif
|
||||
|
||||
class QNullDeviceInfo : public QAbstractAudioDeviceInfo
|
||||
{
|
||||
public:
|
||||
QAudioFormat preferredFormat() const { qWarning()<<"using null deviceinfo, none available"; return QAudioFormat(); }
|
||||
bool isFormatSupported(const QAudioFormat& ) const { return false; }
|
||||
QAudioFormat nearestFormat(const QAudioFormat& ) const { return QAudioFormat(); }
|
||||
QString deviceName() const { return QString(); }
|
||||
QStringList codecList() { return QStringList(); }
|
||||
QList<int> frequencyList() { return QList<int>(); }
|
||||
QList<int> channelsList() { return QList<int>(); }
|
||||
QList<int> sampleSizeList() { return QList<int>(); }
|
||||
QList<QAudioFormat::Endian> byteOrderList() { return QList<QAudioFormat::Endian>(); }
|
||||
QList<QAudioFormat::SampleType> sampleTypeList() { return QList<QAudioFormat::SampleType>(); }
|
||||
};
|
||||
|
||||
class QNullInputDevice : public QAbstractAudioInput
|
||||
{
|
||||
public:
|
||||
QIODevice* start(QIODevice* ) { qWarning()<<"using null input device, none available"; return 0; }
|
||||
void stop() {}
|
||||
void reset() {}
|
||||
void suspend() {}
|
||||
void resume() {}
|
||||
int bytesReady() const { return 0; }
|
||||
int periodSize() const { return 0; }
|
||||
void setBufferSize(int ) {}
|
||||
int bufferSize() const { return 0; }
|
||||
void setNotifyInterval(int ) {}
|
||||
int notifyInterval() const { return 0; }
|
||||
qint64 processedUSecs() const { return 0; }
|
||||
qint64 elapsedUSecs() const { return 0; }
|
||||
QAudio::Error error() const { return QAudio::OpenError; }
|
||||
QAudio::State state() const { return QAudio::StoppedState; }
|
||||
QAudioFormat format() const { return QAudioFormat(); }
|
||||
};
|
||||
|
||||
class QNullOutputDevice : public QAbstractAudioOutput
|
||||
{
|
||||
public:
|
||||
QIODevice* start(QIODevice* ) { qWarning()<<"using null output device, none available"; return 0; }
|
||||
void stop() {}
|
||||
void reset() {}
|
||||
void suspend() {}
|
||||
void resume() {}
|
||||
int bytesFree() const { return 0; }
|
||||
int periodSize() const { return 0; }
|
||||
void setBufferSize(int ) {}
|
||||
int bufferSize() const { return 0; }
|
||||
void setNotifyInterval(int ) {}
|
||||
int notifyInterval() const { return 0; }
|
||||
qint64 processedUSecs() const { return 0; }
|
||||
qint64 elapsedUSecs() const { return 0; }
|
||||
QAudio::Error error() const { return QAudio::OpenError; }
|
||||
QAudio::State state() const { return QAudio::StoppedState; }
|
||||
QAudioFormat format() const { return QAudioFormat(); }
|
||||
};
|
||||
|
||||
QList<QAudioDeviceInfo> QAudioDeviceFactory::availableDevices(QAudio::Mode mode)
|
||||
{
|
||||
QList<QAudioDeviceInfo> devices;
|
||||
#ifndef QT_NO_AUDIO_BACKEND
|
||||
#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA) || defined(Q_OS_SYMBIAN))
|
||||
foreach (const QByteArray &handle, QAudioDeviceInfoInternal::availableDevices(mode))
|
||||
devices << QAudioDeviceInfo(QLatin1String("builtin"), handle, mode);
|
||||
#endif
|
||||
#endif
|
||||
#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
|
||||
QFactoryLoader* l = loader();
|
||||
|
||||
foreach (QString const& key, l->keys()) {
|
||||
QAudioEngineFactoryInterface* plugin = qobject_cast<QAudioEngineFactoryInterface*>(l->instance(key));
|
||||
if (plugin) {
|
||||
foreach (QByteArray const& handle, plugin->availableDevices(mode))
|
||||
devices << QAudioDeviceInfo(key, handle, mode);
|
||||
}
|
||||
|
||||
delete plugin;
|
||||
}
|
||||
#endif
|
||||
return devices;
|
||||
}
|
||||
|
||||
QAudioDeviceInfo QAudioDeviceFactory::defaultInputDevice()
|
||||
{
|
||||
#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
|
||||
QAudioEngineFactoryInterface* plugin = qobject_cast<QAudioEngineFactoryInterface*>(loader()->instance(QLatin1String("default")));
|
||||
|
||||
if (plugin) {
|
||||
QList<QByteArray> list = plugin->availableDevices(QAudio::AudioInput);
|
||||
if (list.size() > 0)
|
||||
return QAudioDeviceInfo(QLatin1String("default"), list.at(0), QAudio::AudioInput);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef QT_NO_AUDIO_BACKEND
|
||||
#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA) || defined(Q_OS_SYMBIAN))
|
||||
return QAudioDeviceInfo(QLatin1String("builtin"), QAudioDeviceInfoInternal::defaultInputDevice(), QAudio::AudioInput);
|
||||
#endif
|
||||
#endif
|
||||
return QAudioDeviceInfo();
|
||||
}
|
||||
|
||||
QAudioDeviceInfo QAudioDeviceFactory::defaultOutputDevice()
|
||||
{
|
||||
#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
|
||||
QAudioEngineFactoryInterface* plugin = qobject_cast<QAudioEngineFactoryInterface*>(loader()->instance(QLatin1String("default")));
|
||||
|
||||
if (plugin) {
|
||||
QList<QByteArray> list = plugin->availableDevices(QAudio::AudioOutput);
|
||||
if (list.size() > 0)
|
||||
return QAudioDeviceInfo(QLatin1String("default"), list.at(0), QAudio::AudioOutput);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef QT_NO_AUDIO_BACKEND
|
||||
#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA) || defined(Q_OS_SYMBIAN))
|
||||
return QAudioDeviceInfo(QLatin1String("builtin"), QAudioDeviceInfoInternal::defaultOutputDevice(), QAudio::AudioOutput);
|
||||
#endif
|
||||
#endif
|
||||
return QAudioDeviceInfo();
|
||||
}
|
||||
|
||||
QAbstractAudioDeviceInfo* QAudioDeviceFactory::audioDeviceInfo(const QString &realm, const QByteArray &handle, QAudio::Mode mode)
|
||||
{
|
||||
QAbstractAudioDeviceInfo *rc = 0;
|
||||
|
||||
#ifndef QT_NO_AUDIO_BACKEND
|
||||
#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA) || defined(Q_OS_SYMBIAN))
|
||||
if (realm == QLatin1String("builtin"))
|
||||
return new QAudioDeviceInfoInternal(handle, mode);
|
||||
#endif
|
||||
#endif
|
||||
#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
|
||||
QAudioEngineFactoryInterface* plugin =
|
||||
qobject_cast<QAudioEngineFactoryInterface*>(loader()->instance(realm));
|
||||
|
||||
if (plugin)
|
||||
rc = plugin->createDeviceInfo(handle, mode);
|
||||
#endif
|
||||
return rc == 0 ? new QNullDeviceInfo() : rc;
|
||||
}
|
||||
|
||||
QAbstractAudioInput* QAudioDeviceFactory::createDefaultInputDevice(QAudioFormat const &format)
|
||||
{
|
||||
return createInputDevice(defaultInputDevice(), format);
|
||||
}
|
||||
|
||||
QAbstractAudioOutput* QAudioDeviceFactory::createDefaultOutputDevice(QAudioFormat const &format)
|
||||
{
|
||||
return createOutputDevice(defaultOutputDevice(), format);
|
||||
}
|
||||
|
||||
QAbstractAudioInput* QAudioDeviceFactory::createInputDevice(QAudioDeviceInfo const& deviceInfo, QAudioFormat const &format)
|
||||
{
|
||||
if (deviceInfo.isNull())
|
||||
return new QNullInputDevice();
|
||||
#ifndef QT_NO_AUDIO_BACKEND
|
||||
#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA) || defined(Q_OS_SYMBIAN))
|
||||
if (deviceInfo.realm() == QLatin1String("builtin"))
|
||||
return new QAudioInputPrivate(deviceInfo.handle(), format);
|
||||
#endif
|
||||
#endif
|
||||
#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
|
||||
QAudioEngineFactoryInterface* plugin =
|
||||
qobject_cast<QAudioEngineFactoryInterface*>(loader()->instance(deviceInfo.realm()));
|
||||
|
||||
if (plugin)
|
||||
return plugin->createInput(deviceInfo.handle(), format);
|
||||
#endif
|
||||
return new QNullInputDevice();
|
||||
}
|
||||
|
||||
QAbstractAudioOutput* QAudioDeviceFactory::createOutputDevice(QAudioDeviceInfo const& deviceInfo, QAudioFormat const &format)
|
||||
{
|
||||
if (deviceInfo.isNull())
|
||||
return new QNullOutputDevice();
|
||||
#ifndef QT_NO_AUDIO_BACKEND
|
||||
#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA) || defined(Q_OS_SYMBIAN))
|
||||
if (deviceInfo.realm() == QLatin1String("builtin"))
|
||||
return new QAudioOutputPrivate(deviceInfo.handle(), format);
|
||||
#endif
|
||||
#endif
|
||||
#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
|
||||
QAudioEngineFactoryInterface* plugin =
|
||||
qobject_cast<QAudioEngineFactoryInterface*>(loader()->instance(deviceInfo.realm()));
|
||||
|
||||
if (plugin)
|
||||
return plugin->createOutput(deviceInfo.handle(), format);
|
||||
#endif
|
||||
return new QNullOutputDevice();
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
97
src/multimedia/audio/qaudiodevicefactory_p.h
Normal file
97
src/multimedia/audio/qaudiodevicefactory_p.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $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.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#ifndef QAUDIODEVICEFACTORY_P_H
|
||||
#define QAUDIODEVICEFACTORY_P_H
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
#include <QtCore/qbytearray.h>
|
||||
#include <QtCore/qlist.h>
|
||||
|
||||
#include <QtMultimedia/qaudiodeviceinfo.h>
|
||||
|
||||
QT_BEGIN_HEADER
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QT_MODULE(Multimedia)
|
||||
|
||||
class QAbstractAudioInput;
|
||||
class QAbstractAudioOutput;
|
||||
class QAbstractAudioDeviceInfo;
|
||||
|
||||
class QAudioDeviceFactory
|
||||
{
|
||||
public:
|
||||
static QList<QAudioDeviceInfo> availableDevices(QAudio::Mode mode);
|
||||
|
||||
static QAudioDeviceInfo defaultInputDevice();
|
||||
static QAudioDeviceInfo defaultOutputDevice();
|
||||
|
||||
static QAbstractAudioDeviceInfo* audioDeviceInfo(const QString &realm, const QByteArray &handle, QAudio::Mode mode);
|
||||
|
||||
static QAbstractAudioInput* createDefaultInputDevice(QAudioFormat const &format);
|
||||
static QAbstractAudioOutput* createDefaultOutputDevice(QAudioFormat const &format);
|
||||
|
||||
static QAbstractAudioInput* createInputDevice(QAudioDeviceInfo const &device, QAudioFormat const &format);
|
||||
static QAbstractAudioOutput* createOutputDevice(QAudioDeviceInfo const &device, QAudioFormat const &format);
|
||||
|
||||
static QAbstractAudioInput* createNullInput();
|
||||
static QAbstractAudioOutput* createNullOutput();
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
QT_END_HEADER
|
||||
|
||||
#endif // QAUDIODEVICEFACTORY_P_H
|
||||
|
||||
460
src/multimedia/audio/qaudiodeviceinfo.cpp
Normal file
460
src/multimedia/audio/qaudiodeviceinfo.cpp
Normal file
@@ -0,0 +1,460 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qaudiodevicefactory_p.h"
|
||||
#include <QtMultimedia/qaudioengine.h>
|
||||
#include <QtMultimedia/qaudiodeviceinfo.h>
|
||||
|
||||
#include <QtCore/qmap.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QAudioDeviceInfoPrivate : public QSharedData
|
||||
{
|
||||
public:
|
||||
QAudioDeviceInfoPrivate():info(0) {}
|
||||
QAudioDeviceInfoPrivate(const QString &r, const QByteArray &h, QAudio::Mode m):
|
||||
realm(r), handle(h), mode(m)
|
||||
{
|
||||
info = QAudioDeviceFactory::audioDeviceInfo(realm, handle, mode);
|
||||
}
|
||||
|
||||
QAudioDeviceInfoPrivate(const QAudioDeviceInfoPrivate &other):
|
||||
QSharedData(other),
|
||||
realm(other.realm), handle(other.handle), mode(other.mode)
|
||||
{
|
||||
info = QAudioDeviceFactory::audioDeviceInfo(realm, handle, mode);
|
||||
}
|
||||
|
||||
QAudioDeviceInfoPrivate& operator=(const QAudioDeviceInfoPrivate &other)
|
||||
{
|
||||
delete info;
|
||||
|
||||
realm = other.realm;
|
||||
handle = other.handle;
|
||||
mode = other.mode;
|
||||
info = QAudioDeviceFactory::audioDeviceInfo(realm, handle, mode);
|
||||
return *this;
|
||||
}
|
||||
|
||||
~QAudioDeviceInfoPrivate()
|
||||
{
|
||||
delete info;
|
||||
}
|
||||
|
||||
QString realm;
|
||||
QByteArray handle;
|
||||
QAudio::Mode mode;
|
||||
QAbstractAudioDeviceInfo* info;
|
||||
};
|
||||
|
||||
|
||||
/*!
|
||||
\class QAudioDeviceInfo
|
||||
\brief The QAudioDeviceInfo class provides an interface to query audio devices and their functionality.
|
||||
\inmodule QtMultimedia
|
||||
\ingroup multimedia
|
||||
|
||||
\since 4.6
|
||||
|
||||
QAudioDeviceInfo lets you query for audio devices--such as sound
|
||||
cards and USB headsets--that are currently available on the system.
|
||||
The audio devices available are dependent on the platform or audio plugins installed.
|
||||
|
||||
You can also query each device for the formats it supports. A
|
||||
format in this context is a set consisting of a specific byte
|
||||
order, channel, codec, frequency, sample rate, and sample type. A
|
||||
format is represented by the QAudioFormat class.
|
||||
|
||||
The values supported by the the device for each of these
|
||||
parameters can be fetched with
|
||||
supportedByteOrders(), supportedChannelCounts(), supportedCodecs(),
|
||||
supportedSampleRates(), supportedSampleSizes(), and
|
||||
supportedSampleTypes(). The combinations supported are dependent on the platform,
|
||||
audio plugins installed and the audio device capabilities. If you need a specific format, you can check if
|
||||
the device supports it with isFormatSupported(), or fetch a
|
||||
supported format that is as close as possible to the format with
|
||||
nearestFormat(). For instance:
|
||||
|
||||
\snippet doc/src/snippets/audio/main.cpp 1
|
||||
\dots 8
|
||||
\snippet doc/src/snippets/audio/main.cpp 2
|
||||
|
||||
A QAudioDeviceInfo is used by Qt to construct
|
||||
classes that communicate with the device--such as
|
||||
QAudioInput, and QAudioOutput. The static
|
||||
functions defaultInputDevice(), defaultOutputDevice(), and
|
||||
availableDevices() let you get a list of all available
|
||||
devices. Devices are fetch according to the value of mode
|
||||
this is specified by the QAudio::Mode enum.
|
||||
The QAudioDeviceInfo returned are only valid for the QAudio::Mode.
|
||||
|
||||
For instance:
|
||||
|
||||
\code
|
||||
foreach(const QAudioDeviceInfo &deviceInfo, QAudioDeviceInfo::availableDevices(QAudio::AudioOutput))
|
||||
qDebug() << "Device name: " << deviceInfo.deviceName();
|
||||
\endcode
|
||||
|
||||
In this code sample, we loop through all devices that are able to output
|
||||
sound, i.e., play an audio stream in a supported format. For each device we
|
||||
find, we simply print the deviceName().
|
||||
|
||||
\sa QAudioOutput, QAudioInput
|
||||
*/
|
||||
|
||||
/*!
|
||||
Constructs an empty QAudioDeviceInfo object.
|
||||
*/
|
||||
|
||||
QAudioDeviceInfo::QAudioDeviceInfo():
|
||||
d(new QAudioDeviceInfoPrivate)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Constructs a copy of \a other.
|
||||
*/
|
||||
|
||||
QAudioDeviceInfo::QAudioDeviceInfo(const QAudioDeviceInfo& other):
|
||||
d(other.d)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Destroy this audio device info.
|
||||
*/
|
||||
|
||||
QAudioDeviceInfo::~QAudioDeviceInfo()
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the QAudioDeviceInfo object to be equal to \a other.
|
||||
*/
|
||||
|
||||
QAudioDeviceInfo& QAudioDeviceInfo::operator=(const QAudioDeviceInfo &other)
|
||||
{
|
||||
d = other.d;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns whether this QAudioDeviceInfo object holds a device definition.
|
||||
*/
|
||||
|
||||
bool QAudioDeviceInfo::isNull() const
|
||||
{
|
||||
return d->info == 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns human readable name of audio device.
|
||||
|
||||
Device names vary depending on platform/audio plugin being used.
|
||||
|
||||
They are a unique string identifiers for the audio device.
|
||||
|
||||
eg. default, Intel, U0x46d0x9a4
|
||||
*/
|
||||
|
||||
QString QAudioDeviceInfo::deviceName() const
|
||||
{
|
||||
return isNull() ? QString() : d->info->deviceName();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns true if \a settings are supported by the audio device of this QAudioDeviceInfo.
|
||||
*/
|
||||
|
||||
bool QAudioDeviceInfo::isFormatSupported(const QAudioFormat &settings) const
|
||||
{
|
||||
return isNull() ? false : d->info->isFormatSupported(settings);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns QAudioFormat of default settings.
|
||||
|
||||
These settings are provided by the platform/audio plugin being used.
|
||||
|
||||
They also are dependent on the QAudio::Mode being used.
|
||||
|
||||
A typical audio system would provide something like:
|
||||
\list
|
||||
\o Input settings: 8000Hz mono 8 bit.
|
||||
\o Output settings: 44100Hz stereo 16 bit little endian.
|
||||
\endlist
|
||||
*/
|
||||
|
||||
QAudioFormat QAudioDeviceInfo::preferredFormat() const
|
||||
{
|
||||
return isNull() ? QAudioFormat() : d->info->preferredFormat();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns closest QAudioFormat to \a settings that system audio supports.
|
||||
|
||||
These settings are provided by the platform/audio plugin being used.
|
||||
|
||||
They also are dependent on the QAudio::Mode being used.
|
||||
*/
|
||||
|
||||
QAudioFormat QAudioDeviceInfo::nearestFormat(const QAudioFormat &settings) const
|
||||
{
|
||||
if (isFormatSupported(settings))
|
||||
return settings;
|
||||
|
||||
QAudioFormat nearest = settings;
|
||||
|
||||
nearest.setCodec(QLatin1String("audio/pcm"));
|
||||
|
||||
if (nearest.sampleType() == QAudioFormat::Unknown) {
|
||||
QAudioFormat preferred = preferredFormat();
|
||||
nearest.setSampleType(preferred.sampleType());
|
||||
}
|
||||
|
||||
QMap<int,int> testFrequencies;
|
||||
QList<int> frequenciesAvailable = supportedFrequencies();
|
||||
QMap<int,int> testSampleSizes;
|
||||
QList<int> sampleSizesAvailable = supportedSampleSizes();
|
||||
|
||||
// Get sorted sampleSizes (equal to and ascending values only)
|
||||
if (sampleSizesAvailable.contains(settings.sampleSize()))
|
||||
testSampleSizes.insert(0,settings.sampleSize());
|
||||
sampleSizesAvailable.removeAll(settings.sampleSize());
|
||||
foreach (int size, sampleSizesAvailable) {
|
||||
int larger = (size > settings.sampleSize()) ? size : settings.sampleSize();
|
||||
int smaller = (size > settings.sampleSize()) ? settings.sampleSize() : size;
|
||||
if (size >= settings.sampleSize()) {
|
||||
int diff = larger - smaller;
|
||||
testSampleSizes.insert(diff, size);
|
||||
}
|
||||
}
|
||||
|
||||
// Get sorted frequencies (equal to and ascending values only)
|
||||
if (frequenciesAvailable.contains(settings.frequency()))
|
||||
testFrequencies.insert(0,settings.frequency());
|
||||
frequenciesAvailable.removeAll(settings.frequency());
|
||||
foreach (int frequency, frequenciesAvailable) {
|
||||
int larger = (frequency > settings.frequency()) ? frequency : settings.frequency();
|
||||
int smaller = (frequency > settings.frequency()) ? settings.frequency() : frequency;
|
||||
if (frequency >= settings.frequency()) {
|
||||
int diff = larger - smaller;
|
||||
testFrequencies.insert(diff, frequency);
|
||||
}
|
||||
}
|
||||
|
||||
// Try to find nearest
|
||||
// Check ascending frequencies, ascending sampleSizes
|
||||
QMapIterator<int, int> sz(testSampleSizes);
|
||||
while (sz.hasNext()) {
|
||||
sz.next();
|
||||
nearest.setSampleSize(sz.value());
|
||||
QMapIterator<int, int> i(testFrequencies);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
nearest.setFrequency(i.value());
|
||||
if (isFormatSupported(nearest))
|
||||
return nearest;
|
||||
}
|
||||
}
|
||||
|
||||
//Fallback
|
||||
return preferredFormat();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a list of supported codecs.
|
||||
|
||||
All platform and plugin implementations should provide support for:
|
||||
|
||||
"audio/pcm" - Linear PCM
|
||||
|
||||
For writing plugins to support additional codecs refer to:
|
||||
|
||||
http://www.iana.org/assignments/media-types/audio/
|
||||
*/
|
||||
|
||||
QStringList QAudioDeviceInfo::supportedCodecs() const
|
||||
{
|
||||
return isNull() ? QStringList() : d->info->codecList();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a list of supported sample rates.
|
||||
|
||||
\since 4.7
|
||||
*/
|
||||
|
||||
QList<int> QAudioDeviceInfo::supportedSampleRates() const
|
||||
{
|
||||
return supportedFrequencies();
|
||||
}
|
||||
|
||||
/*!
|
||||
\obsolete
|
||||
|
||||
Use supportedSampleRates() instead.
|
||||
*/
|
||||
|
||||
QList<int> QAudioDeviceInfo::supportedFrequencies() const
|
||||
{
|
||||
return isNull() ? QList<int>() : d->info->frequencyList();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a list of supported channel counts.
|
||||
|
||||
\since 4.7
|
||||
*/
|
||||
|
||||
QList<int> QAudioDeviceInfo::supportedChannelCounts() const
|
||||
{
|
||||
return supportedChannels();
|
||||
}
|
||||
|
||||
/*!
|
||||
\obsolete
|
||||
|
||||
Use supportedChannelCount() instead.
|
||||
*/
|
||||
|
||||
QList<int> QAudioDeviceInfo::supportedChannels() const
|
||||
{
|
||||
return isNull() ? QList<int>() : d->info->channelsList();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a list of supported sample sizes.
|
||||
*/
|
||||
|
||||
QList<int> QAudioDeviceInfo::supportedSampleSizes() const
|
||||
{
|
||||
return isNull() ? QList<int>() : d->info->sampleSizeList();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a list of supported byte orders.
|
||||
*/
|
||||
|
||||
QList<QAudioFormat::Endian> QAudioDeviceInfo::supportedByteOrders() const
|
||||
{
|
||||
return isNull() ? QList<QAudioFormat::Endian>() : d->info->byteOrderList();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a list of supported sample types.
|
||||
*/
|
||||
|
||||
QList<QAudioFormat::SampleType> QAudioDeviceInfo::supportedSampleTypes() const
|
||||
{
|
||||
return isNull() ? QList<QAudioFormat::SampleType>() : d->info->sampleTypeList();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the name of the default input audio device.
|
||||
All platform and audio plugin implementations provide a default audio device to use.
|
||||
*/
|
||||
|
||||
QAudioDeviceInfo QAudioDeviceInfo::defaultInputDevice()
|
||||
{
|
||||
return QAudioDeviceFactory::defaultInputDevice();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the name of the default output audio device.
|
||||
All platform and audio plugin implementations provide a default audio device to use.
|
||||
*/
|
||||
|
||||
QAudioDeviceInfo QAudioDeviceInfo::defaultOutputDevice()
|
||||
{
|
||||
return QAudioDeviceFactory::defaultOutputDevice();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a list of audio devices that support \a mode.
|
||||
*/
|
||||
|
||||
QList<QAudioDeviceInfo> QAudioDeviceInfo::availableDevices(QAudio::Mode mode)
|
||||
{
|
||||
return QAudioDeviceFactory::availableDevices(mode);
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
|
||||
QAudioDeviceInfo::QAudioDeviceInfo(const QString &realm, const QByteArray &handle, QAudio::Mode mode):
|
||||
d(new QAudioDeviceInfoPrivate(realm, handle, mode))
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
|
||||
QString QAudioDeviceInfo::realm() const
|
||||
{
|
||||
return d->realm;
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
|
||||
QByteArray QAudioDeviceInfo::handle() const
|
||||
{
|
||||
return d->handle;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
|
||||
QAudio::Mode QAudioDeviceInfo::mode() const
|
||||
{
|
||||
return d->mode;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
114
src/multimedia/audio/qaudiodeviceinfo.h
Normal file
114
src/multimedia/audio/qaudiodeviceinfo.h
Normal file
@@ -0,0 +1,114 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#ifndef QAUDIODEVICEINFO_H
|
||||
#define QAUDIODEVICEINFO_H
|
||||
|
||||
#include <QtCore/qobject.h>
|
||||
#include <QtCore/qglobal.h>
|
||||
#include <QtCore/qbytearray.h>
|
||||
#include <QtCore/qstring.h>
|
||||
#include <QtCore/qstringlist.h>
|
||||
#include <QtCore/qlist.h>
|
||||
|
||||
#include <QtMultimedia/qaudio.h>
|
||||
#include <QtMultimedia/qaudioformat.h>
|
||||
|
||||
|
||||
QT_BEGIN_HEADER
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QT_MODULE(Multimedia)
|
||||
|
||||
class QAudioDeviceFactory;
|
||||
|
||||
class QAudioDeviceInfoPrivate;
|
||||
class Q_MULTIMEDIA_EXPORT QAudioDeviceInfo
|
||||
{
|
||||
friend class QAudioDeviceFactory;
|
||||
|
||||
public:
|
||||
QAudioDeviceInfo();
|
||||
QAudioDeviceInfo(const QAudioDeviceInfo& other);
|
||||
~QAudioDeviceInfo();
|
||||
|
||||
QAudioDeviceInfo& operator=(const QAudioDeviceInfo& other);
|
||||
|
||||
bool isNull() const;
|
||||
|
||||
QString deviceName() const;
|
||||
|
||||
bool isFormatSupported(const QAudioFormat &format) const;
|
||||
QAudioFormat preferredFormat() const;
|
||||
QAudioFormat nearestFormat(const QAudioFormat &format) const;
|
||||
|
||||
QStringList supportedCodecs() const;
|
||||
QList<int> supportedFrequencies() const;
|
||||
QList<int> supportedSampleRates() const;
|
||||
QList<int> supportedChannels() const;
|
||||
QList<int> supportedChannelCounts() const;
|
||||
QList<int> supportedSampleSizes() const;
|
||||
QList<QAudioFormat::Endian> supportedByteOrders() const;
|
||||
QList<QAudioFormat::SampleType> supportedSampleTypes() const;
|
||||
|
||||
static QAudioDeviceInfo defaultInputDevice();
|
||||
static QAudioDeviceInfo defaultOutputDevice();
|
||||
|
||||
static QList<QAudioDeviceInfo> availableDevices(QAudio::Mode mode);
|
||||
|
||||
private:
|
||||
QAudioDeviceInfo(const QString &realm, const QByteArray &handle, QAudio::Mode mode);
|
||||
QString realm() const;
|
||||
QByteArray handle() const;
|
||||
QAudio::Mode mode() const;
|
||||
|
||||
QSharedDataPointer<QAudioDeviceInfoPrivate> d;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
QT_END_HEADER
|
||||
|
||||
Q_DECLARE_METATYPE(QAudioDeviceInfo)
|
||||
|
||||
#endif // QAUDIODEVICEINFO_H
|
||||
544
src/multimedia/audio/qaudiodeviceinfo_alsa_p.cpp
Normal file
544
src/multimedia/audio/qaudiodeviceinfo_alsa_p.cpp
Normal file
@@ -0,0 +1,544 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $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.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include "qaudiodeviceinfo_alsa_p.h"
|
||||
|
||||
#include <alsa/version.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QAudioDeviceInfoInternal::QAudioDeviceInfoInternal(QByteArray dev, QAudio::Mode mode)
|
||||
{
|
||||
handle = 0;
|
||||
|
||||
device = QLatin1String(dev);
|
||||
this->mode = mode;
|
||||
|
||||
#if (SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
|
||||
checkSurround();
|
||||
#endif
|
||||
}
|
||||
|
||||
QAudioDeviceInfoInternal::~QAudioDeviceInfoInternal()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
bool QAudioDeviceInfoInternal::isFormatSupported(const QAudioFormat& format) const
|
||||
{
|
||||
return testSettings(format);
|
||||
}
|
||||
|
||||
QAudioFormat QAudioDeviceInfoInternal::preferredFormat() const
|
||||
{
|
||||
QAudioFormat nearest;
|
||||
if(mode == QAudio::AudioOutput) {
|
||||
nearest.setFrequency(44100);
|
||||
nearest.setChannels(2);
|
||||
nearest.setByteOrder(QAudioFormat::LittleEndian);
|
||||
nearest.setSampleType(QAudioFormat::SignedInt);
|
||||
nearest.setSampleSize(16);
|
||||
nearest.setCodec(QLatin1String("audio/pcm"));
|
||||
} else {
|
||||
nearest.setFrequency(8000);
|
||||
nearest.setChannels(1);
|
||||
nearest.setSampleType(QAudioFormat::UnSignedInt);
|
||||
nearest.setSampleSize(8);
|
||||
nearest.setCodec(QLatin1String("audio/pcm"));
|
||||
if(!testSettings(nearest)) {
|
||||
nearest.setChannels(2);
|
||||
nearest.setSampleSize(16);
|
||||
nearest.setSampleType(QAudioFormat::SignedInt);
|
||||
}
|
||||
}
|
||||
return nearest;
|
||||
}
|
||||
|
||||
QAudioFormat QAudioDeviceInfoInternal::nearestFormat(const QAudioFormat& format) const
|
||||
{
|
||||
if(testSettings(format))
|
||||
return format;
|
||||
else
|
||||
return preferredFormat();
|
||||
}
|
||||
|
||||
QString QAudioDeviceInfoInternal::deviceName() const
|
||||
{
|
||||
return device;
|
||||
}
|
||||
|
||||
QStringList QAudioDeviceInfoInternal::codecList()
|
||||
{
|
||||
updateLists();
|
||||
return codecz;
|
||||
}
|
||||
|
||||
QList<int> QAudioDeviceInfoInternal::frequencyList()
|
||||
{
|
||||
updateLists();
|
||||
return freqz;
|
||||
}
|
||||
|
||||
QList<int> QAudioDeviceInfoInternal::channelsList()
|
||||
{
|
||||
updateLists();
|
||||
return channelz;
|
||||
}
|
||||
|
||||
QList<int> QAudioDeviceInfoInternal::sampleSizeList()
|
||||
{
|
||||
updateLists();
|
||||
return sizez;
|
||||
}
|
||||
|
||||
QList<QAudioFormat::Endian> QAudioDeviceInfoInternal::byteOrderList()
|
||||
{
|
||||
updateLists();
|
||||
return byteOrderz;
|
||||
}
|
||||
|
||||
QList<QAudioFormat::SampleType> QAudioDeviceInfoInternal::sampleTypeList()
|
||||
{
|
||||
updateLists();
|
||||
return typez;
|
||||
}
|
||||
|
||||
bool QAudioDeviceInfoInternal::open()
|
||||
{
|
||||
int err = 0;
|
||||
QString dev = device;
|
||||
QList<QByteArray> devices = availableDevices(mode);
|
||||
|
||||
if(dev.compare(QLatin1String("default")) == 0) {
|
||||
#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
|
||||
dev = QLatin1String(devices.first().constData());
|
||||
#else
|
||||
dev = QLatin1String("hw:0,0");
|
||||
#endif
|
||||
} else {
|
||||
#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
|
||||
dev = device;
|
||||
#else
|
||||
int idx = 0;
|
||||
char *name;
|
||||
|
||||
QString shortName = device.mid(device.indexOf(QLatin1String("="),0)+1);
|
||||
|
||||
while(snd_card_get_name(idx,&name) == 0) {
|
||||
if(dev.contains(QLatin1String(name)))
|
||||
break;
|
||||
idx++;
|
||||
}
|
||||
dev = QString(QLatin1String("hw:%1,0")).arg(idx);
|
||||
#endif
|
||||
}
|
||||
if(mode == QAudio::AudioOutput) {
|
||||
err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_PLAYBACK,0);
|
||||
} else {
|
||||
err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_CAPTURE,0);
|
||||
}
|
||||
if(err < 0) {
|
||||
handle = 0;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void QAudioDeviceInfoInternal::close()
|
||||
{
|
||||
if(handle)
|
||||
snd_pcm_close(handle);
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
bool QAudioDeviceInfoInternal::testSettings(const QAudioFormat& format) const
|
||||
{
|
||||
// Set nearest to closest settings that do work.
|
||||
// See if what is in settings will work (return value).
|
||||
int err = 0;
|
||||
snd_pcm_t* handle;
|
||||
snd_pcm_hw_params_t *params;
|
||||
QString dev = device;
|
||||
|
||||
QList<QByteArray> devices = QAudioDeviceInfoInternal::availableDevices(QAudio::AudioOutput);
|
||||
|
||||
if(dev.compare(QLatin1String("default")) == 0) {
|
||||
#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
|
||||
dev = QLatin1String(devices.first().constData());
|
||||
#else
|
||||
dev = QLatin1String("hw:0,0");
|
||||
#endif
|
||||
} else {
|
||||
#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
|
||||
dev = device;
|
||||
#else
|
||||
int idx = 0;
|
||||
char *name;
|
||||
|
||||
QString shortName = device.mid(device.indexOf(QLatin1String("="),0)+1);
|
||||
|
||||
while(snd_card_get_name(idx,&name) == 0) {
|
||||
if(shortName.compare(QLatin1String(name)) == 0)
|
||||
break;
|
||||
idx++;
|
||||
}
|
||||
dev = QString(QLatin1String("hw:%1,0")).arg(idx);
|
||||
#endif
|
||||
}
|
||||
if(mode == QAudio::AudioOutput) {
|
||||
err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_PLAYBACK,0);
|
||||
} else {
|
||||
err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_CAPTURE,0);
|
||||
}
|
||||
if(err < 0) {
|
||||
handle = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool testChannel = false;
|
||||
bool testCodec = false;
|
||||
bool testFreq = false;
|
||||
bool testType = false;
|
||||
bool testSize = false;
|
||||
|
||||
int dir = 0;
|
||||
|
||||
snd_pcm_nonblock( handle, 0 );
|
||||
snd_pcm_hw_params_alloca( ¶ms );
|
||||
snd_pcm_hw_params_any( handle, params );
|
||||
|
||||
// set the values!
|
||||
snd_pcm_hw_params_set_channels(handle,params,format.channels());
|
||||
snd_pcm_hw_params_set_rate(handle,params,format.frequency(),dir);
|
||||
|
||||
err = -1;
|
||||
|
||||
switch(format.sampleSize()) {
|
||||
case 8:
|
||||
if(format.sampleType() == QAudioFormat::SignedInt)
|
||||
err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S8);
|
||||
else if(format.sampleType() == QAudioFormat::UnSignedInt)
|
||||
err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U8);
|
||||
break;
|
||||
case 16:
|
||||
if(format.sampleType() == QAudioFormat::SignedInt) {
|
||||
if(format.byteOrder() == QAudioFormat::LittleEndian)
|
||||
err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_LE);
|
||||
else if(format.byteOrder() == QAudioFormat::BigEndian)
|
||||
err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_BE);
|
||||
} else if(format.sampleType() == QAudioFormat::UnSignedInt) {
|
||||
if(format.byteOrder() == QAudioFormat::LittleEndian)
|
||||
err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U16_LE);
|
||||
else if(format.byteOrder() == QAudioFormat::BigEndian)
|
||||
err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U16_BE);
|
||||
}
|
||||
break;
|
||||
case 32:
|
||||
if(format.sampleType() == QAudioFormat::SignedInt) {
|
||||
if(format.byteOrder() == QAudioFormat::LittleEndian)
|
||||
err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S32_LE);
|
||||
else if(format.byteOrder() == QAudioFormat::BigEndian)
|
||||
err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S32_BE);
|
||||
} else if(format.sampleType() == QAudioFormat::UnSignedInt) {
|
||||
if(format.byteOrder() == QAudioFormat::LittleEndian)
|
||||
err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U32_LE);
|
||||
else if(format.byteOrder() == QAudioFormat::BigEndian)
|
||||
err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U32_BE);
|
||||
}
|
||||
}
|
||||
|
||||
// For now, just accept only audio/pcm codec
|
||||
if(!format.codec().startsWith(QLatin1String("audio/pcm"))) {
|
||||
err=-1;
|
||||
} else
|
||||
testCodec = true;
|
||||
|
||||
if(err>=0 && format.channels() != -1) {
|
||||
err = snd_pcm_hw_params_test_channels(handle,params,format.channels());
|
||||
if(err>=0)
|
||||
err = snd_pcm_hw_params_set_channels(handle,params,format.channels());
|
||||
if(err>=0)
|
||||
testChannel = true;
|
||||
}
|
||||
|
||||
if(err>=0 && format.frequency() != -1) {
|
||||
err = snd_pcm_hw_params_test_rate(handle,params,format.frequency(),0);
|
||||
if(err>=0)
|
||||
err = snd_pcm_hw_params_set_rate(handle,params,format.frequency(),dir);
|
||||
if(err>=0)
|
||||
testFreq = true;
|
||||
}
|
||||
|
||||
if((err>=0 && format.sampleSize() != -1) &&
|
||||
(format.sampleType() != QAudioFormat::Unknown)) {
|
||||
switch(format.sampleSize()) {
|
||||
case 8:
|
||||
if(format.sampleType() == QAudioFormat::SignedInt)
|
||||
err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S8);
|
||||
else if(format.sampleType() == QAudioFormat::UnSignedInt)
|
||||
err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U8);
|
||||
break;
|
||||
case 16:
|
||||
if(format.sampleType() == QAudioFormat::SignedInt) {
|
||||
if(format.byteOrder() == QAudioFormat::LittleEndian)
|
||||
err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_LE);
|
||||
else if(format.byteOrder() == QAudioFormat::BigEndian)
|
||||
err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_BE);
|
||||
} else if(format.sampleType() == QAudioFormat::UnSignedInt) {
|
||||
if(format.byteOrder() == QAudioFormat::LittleEndian)
|
||||
err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U16_LE);
|
||||
else if(format.byteOrder() == QAudioFormat::BigEndian)
|
||||
err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U16_BE);
|
||||
}
|
||||
break;
|
||||
case 32:
|
||||
if(format.sampleType() == QAudioFormat::SignedInt) {
|
||||
if(format.byteOrder() == QAudioFormat::LittleEndian)
|
||||
err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S32_LE);
|
||||
else if(format.byteOrder() == QAudioFormat::BigEndian)
|
||||
err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S32_BE);
|
||||
} else if(format.sampleType() == QAudioFormat::UnSignedInt) {
|
||||
if(format.byteOrder() == QAudioFormat::LittleEndian)
|
||||
err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U32_LE);
|
||||
else if(format.byteOrder() == QAudioFormat::BigEndian)
|
||||
err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U32_BE);
|
||||
}
|
||||
}
|
||||
if(err>=0) {
|
||||
testSize = true;
|
||||
testType = true;
|
||||
}
|
||||
}
|
||||
if(err>=0)
|
||||
err = snd_pcm_hw_params(handle, params);
|
||||
|
||||
if(err == 0) {
|
||||
// settings work
|
||||
// close()
|
||||
if(handle)
|
||||
snd_pcm_close(handle);
|
||||
return true;
|
||||
}
|
||||
if(handle)
|
||||
snd_pcm_close(handle);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void QAudioDeviceInfoInternal::updateLists()
|
||||
{
|
||||
// redo all lists based on current settings
|
||||
freqz.clear();
|
||||
channelz.clear();
|
||||
sizez.clear();
|
||||
byteOrderz.clear();
|
||||
typez.clear();
|
||||
codecz.clear();
|
||||
|
||||
if(!handle)
|
||||
open();
|
||||
|
||||
if(!handle)
|
||||
return;
|
||||
|
||||
for(int i=0; i<(int)MAX_SAMPLE_RATES; i++) {
|
||||
//if(snd_pcm_hw_params_test_rate(handle, params, SAMPLE_RATES[i], dir) == 0)
|
||||
freqz.append(SAMPLE_RATES[i]);
|
||||
}
|
||||
channelz.append(1);
|
||||
channelz.append(2);
|
||||
#if (SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
|
||||
if (surround40) channelz.append(4);
|
||||
if (surround51) channelz.append(6);
|
||||
if (surround71) channelz.append(8);
|
||||
#endif
|
||||
sizez.append(8);
|
||||
sizez.append(16);
|
||||
sizez.append(32);
|
||||
byteOrderz.append(QAudioFormat::LittleEndian);
|
||||
byteOrderz.append(QAudioFormat::BigEndian);
|
||||
typez.append(QAudioFormat::SignedInt);
|
||||
typez.append(QAudioFormat::UnSignedInt);
|
||||
typez.append(QAudioFormat::Float);
|
||||
codecz.append(QLatin1String("audio/pcm"));
|
||||
close();
|
||||
}
|
||||
|
||||
QList<QByteArray> QAudioDeviceInfoInternal::availableDevices(QAudio::Mode mode)
|
||||
{
|
||||
QList<QByteArray> allDevices;
|
||||
QList<QByteArray> devices;
|
||||
QByteArray filter;
|
||||
|
||||
#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
|
||||
// Create a list of all current audio devices that support mode
|
||||
void **hints, **n;
|
||||
char *name, *descr, *io;
|
||||
|
||||
if(snd_device_name_hint(-1, "pcm", &hints) < 0) {
|
||||
qWarning() << "no alsa devices available";
|
||||
return devices;
|
||||
}
|
||||
n = hints;
|
||||
|
||||
if(mode == QAudio::AudioInput) {
|
||||
filter = "Input";
|
||||
} else {
|
||||
filter = "Output";
|
||||
}
|
||||
|
||||
while (*n != NULL) {
|
||||
name = snd_device_name_get_hint(*n, "NAME");
|
||||
if (name != 0 && qstrcmp(name, "null") != 0) {
|
||||
descr = snd_device_name_get_hint(*n, "DESC");
|
||||
io = snd_device_name_get_hint(*n, "IOID");
|
||||
|
||||
if ((descr != NULL) && ((io == NULL) || (io == filter))) {
|
||||
QString deviceName = QLatin1String(name);
|
||||
QString deviceDescription = QLatin1String(descr);
|
||||
allDevices.append(deviceName.toLocal8Bit().constData());
|
||||
if (deviceDescription.contains(QLatin1String("Default Audio Device")))
|
||||
devices.append(deviceName.toLocal8Bit().constData());
|
||||
}
|
||||
|
||||
free(name);
|
||||
if (descr != NULL)
|
||||
free(descr);
|
||||
if (io != NULL)
|
||||
free(io);
|
||||
}
|
||||
++n;
|
||||
}
|
||||
snd_device_name_free_hint(hints);
|
||||
|
||||
if(devices.size() > 0) {
|
||||
devices.append("default");
|
||||
}
|
||||
#else
|
||||
int idx = 0;
|
||||
char* name;
|
||||
|
||||
while(snd_card_get_name(idx,&name) == 0) {
|
||||
devices.append(name);
|
||||
idx++;
|
||||
}
|
||||
if (idx > 0)
|
||||
devices.append("default");
|
||||
#endif
|
||||
if (devices.size() == 0 && allDevices.size() > 0)
|
||||
return allDevices;
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
QByteArray QAudioDeviceInfoInternal::defaultInputDevice()
|
||||
{
|
||||
QList<QByteArray> devices = availableDevices(QAudio::AudioInput);
|
||||
if(devices.size() == 0)
|
||||
return QByteArray();
|
||||
|
||||
return devices.first();
|
||||
}
|
||||
|
||||
QByteArray QAudioDeviceInfoInternal::defaultOutputDevice()
|
||||
{
|
||||
QList<QByteArray> devices = availableDevices(QAudio::AudioOutput);
|
||||
if(devices.size() == 0)
|
||||
return QByteArray();
|
||||
|
||||
return devices.first();
|
||||
}
|
||||
|
||||
#if (SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
|
||||
void QAudioDeviceInfoInternal::checkSurround()
|
||||
{
|
||||
QList<QByteArray> devices;
|
||||
surround40 = false;
|
||||
surround51 = false;
|
||||
surround71 = false;
|
||||
|
||||
void **hints, **n;
|
||||
char *name, *descr, *io;
|
||||
|
||||
if(snd_device_name_hint(-1, "pcm", &hints) < 0)
|
||||
return;
|
||||
|
||||
n = hints;
|
||||
|
||||
while (*n != NULL) {
|
||||
name = snd_device_name_get_hint(*n, "NAME");
|
||||
descr = snd_device_name_get_hint(*n, "DESC");
|
||||
io = snd_device_name_get_hint(*n, "IOID");
|
||||
if((name != NULL) && (descr != NULL)) {
|
||||
QString deviceName = QLatin1String(name);
|
||||
if (mode == QAudio::AudioOutput) {
|
||||
if(deviceName.contains(QLatin1String("surround40")))
|
||||
surround40 = true;
|
||||
if(deviceName.contains(QLatin1String("surround51")))
|
||||
surround51 = true;
|
||||
if(deviceName.contains(QLatin1String("surround71")))
|
||||
surround71 = true;
|
||||
}
|
||||
}
|
||||
if(name != NULL)
|
||||
free(name);
|
||||
if(descr != NULL)
|
||||
free(descr);
|
||||
if(io != NULL)
|
||||
free(io);
|
||||
++n;
|
||||
}
|
||||
snd_device_name_free_hint(hints);
|
||||
}
|
||||
#endif
|
||||
|
||||
QT_END_NAMESPACE
|
||||
124
src/multimedia/audio/qaudiodeviceinfo_alsa_p.h
Normal file
124
src/multimedia/audio/qaudiodeviceinfo_alsa_p.h
Normal file
@@ -0,0 +1,124 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $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.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
|
||||
#ifndef QAUDIODEVICEINFOALSA_H
|
||||
#define QAUDIODEVICEINFOALSA_H
|
||||
|
||||
#include <alsa/asoundlib.h>
|
||||
|
||||
#include <QtCore/qbytearray.h>
|
||||
#include <QtCore/qstringlist.h>
|
||||
#include <QtCore/qlist.h>
|
||||
#include <QtCore/qdebug.h>
|
||||
|
||||
#include <QtMultimedia/qaudio.h>
|
||||
#include <QtMultimedia/qaudiodeviceinfo.h>
|
||||
#include <QtMultimedia/qaudioengine.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
const unsigned int MAX_SAMPLE_RATES = 5;
|
||||
const unsigned int SAMPLE_RATES[] =
|
||||
{ 8000, 11025, 22050, 44100, 48000 };
|
||||
|
||||
class QAudioDeviceInfoInternal : public QAbstractAudioDeviceInfo
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QAudioDeviceInfoInternal(QByteArray dev,QAudio::Mode mode);
|
||||
~QAudioDeviceInfoInternal();
|
||||
|
||||
bool testSettings(const QAudioFormat& format) const;
|
||||
void updateLists();
|
||||
QAudioFormat preferredFormat() const;
|
||||
bool isFormatSupported(const QAudioFormat& format) const;
|
||||
QAudioFormat nearestFormat(const QAudioFormat& format) const;
|
||||
QString deviceName() const;
|
||||
QStringList codecList();
|
||||
QList<int> frequencyList();
|
||||
QList<int> channelsList();
|
||||
QList<int> sampleSizeList();
|
||||
QList<QAudioFormat::Endian> byteOrderList();
|
||||
QList<QAudioFormat::SampleType> sampleTypeList();
|
||||
static QByteArray defaultInputDevice();
|
||||
static QByteArray defaultOutputDevice();
|
||||
static QList<QByteArray> availableDevices(QAudio::Mode);
|
||||
|
||||
private:
|
||||
bool open();
|
||||
void close();
|
||||
|
||||
#if (SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
|
||||
void checkSurround();
|
||||
bool surround40;
|
||||
bool surround51;
|
||||
bool surround71;
|
||||
#endif
|
||||
|
||||
QString device;
|
||||
QAudio::Mode mode;
|
||||
QAudioFormat nearest;
|
||||
QList<int> freqz;
|
||||
QList<int> channelz;
|
||||
QList<int> sizez;
|
||||
QList<QAudioFormat::Endian> byteOrderz;
|
||||
QStringList codecz;
|
||||
QList<QAudioFormat::SampleType> typez;
|
||||
snd_pcm_t* handle;
|
||||
snd_pcm_hw_params_t *params;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
|
||||
363
src/multimedia/audio/qaudiodeviceinfo_mac_p.cpp
Normal file
363
src/multimedia/audio/qaudiodeviceinfo_mac_p.cpp
Normal file
@@ -0,0 +1,363 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $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.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include <QtCore/qstringlist.h>
|
||||
#include <QtCore/qlist.h>
|
||||
#include <QtCore/qbytearray.h>
|
||||
#include <QtCore/qdatastream.h>
|
||||
#include <QtCore/qdebug.h>
|
||||
#include <private/qcore_mac_p.h>
|
||||
|
||||
#include <QtMultimedia/qaudiodeviceinfo.h>
|
||||
#include "qaudio_mac_p.h"
|
||||
#include "qaudiodeviceinfo_mac_p.h"
|
||||
|
||||
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
||||
QAudioDeviceInfoInternal::QAudioDeviceInfoInternal(QByteArray const& handle, QAudio::Mode)
|
||||
{
|
||||
QDataStream ds(handle);
|
||||
quint32 did, tm;
|
||||
|
||||
ds >> did >> tm >> name;
|
||||
deviceId = AudioDeviceID(did);
|
||||
mode = QAudio::Mode(tm);
|
||||
}
|
||||
|
||||
bool QAudioDeviceInfoInternal::isFormatSupported(const QAudioFormat& format) const
|
||||
{
|
||||
QAudioDeviceInfoInternal *self = const_cast<QAudioDeviceInfoInternal*>(this);
|
||||
|
||||
return format.isValid()
|
||||
&& format.codec() == QString::fromLatin1("audio/pcm")
|
||||
&& self->frequencyList().contains(format.frequency())
|
||||
&& self->channelsList().contains(format.channels())
|
||||
&& self->sampleSizeList().contains(format.sampleSize());
|
||||
}
|
||||
|
||||
QAudioFormat QAudioDeviceInfoInternal::preferredFormat() const
|
||||
{
|
||||
QAudioFormat rc;
|
||||
|
||||
UInt32 propSize = 0;
|
||||
|
||||
if (AudioDeviceGetPropertyInfo(deviceId,
|
||||
0,
|
||||
mode == QAudio::AudioInput,
|
||||
kAudioDevicePropertyStreams,
|
||||
&propSize,
|
||||
0) == noErr) {
|
||||
|
||||
const int sc = propSize / sizeof(AudioStreamID);
|
||||
|
||||
if (sc > 0) {
|
||||
AudioStreamID* streams = new AudioStreamID[sc];
|
||||
|
||||
if (AudioDeviceGetProperty(deviceId,
|
||||
0,
|
||||
mode == QAudio::AudioInput,
|
||||
kAudioDevicePropertyStreams,
|
||||
&propSize,
|
||||
streams) == noErr) {
|
||||
|
||||
for (int i = 0; i < sc; ++i) {
|
||||
if (AudioStreamGetPropertyInfo(streams[i],
|
||||
0,
|
||||
kAudioStreamPropertyPhysicalFormat,
|
||||
&propSize,
|
||||
0) == noErr) {
|
||||
|
||||
AudioStreamBasicDescription sf;
|
||||
|
||||
if (AudioStreamGetProperty(streams[i],
|
||||
0,
|
||||
kAudioStreamPropertyPhysicalFormat,
|
||||
&propSize,
|
||||
&sf) == noErr) {
|
||||
rc = toQAudioFormat(sf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete[] streams;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
QAudioFormat QAudioDeviceInfoInternal::nearestFormat(const QAudioFormat& format) const
|
||||
{
|
||||
QAudioFormat rc(format);
|
||||
QAudioFormat target = preferredFormat();
|
||||
|
||||
if (!format.codec().isEmpty() && format.codec() != QString::fromLatin1("audio/pcm"))
|
||||
return QAudioFormat();
|
||||
|
||||
rc.setCodec(QString::fromLatin1("audio/pcm"));
|
||||
|
||||
if (rc.frequency() != target.frequency())
|
||||
rc.setFrequency(target.frequency());
|
||||
if (rc.channels() != target.channels())
|
||||
rc.setChannels(target.channels());
|
||||
if (rc.sampleSize() != target.sampleSize())
|
||||
rc.setSampleSize(target.sampleSize());
|
||||
if (rc.byteOrder() != target.byteOrder())
|
||||
rc.setByteOrder(target.byteOrder());
|
||||
if (rc.sampleType() != target.sampleType())
|
||||
rc.setSampleType(target.sampleType());
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
QString QAudioDeviceInfoInternal::deviceName() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
QStringList QAudioDeviceInfoInternal::codecList()
|
||||
{
|
||||
return QStringList() << QString::fromLatin1("audio/pcm");
|
||||
}
|
||||
|
||||
QList<int> QAudioDeviceInfoInternal::frequencyList()
|
||||
{
|
||||
QSet<int> rc;
|
||||
|
||||
// Add some common frequencies
|
||||
rc << 8000 << 11025 << 22050 << 44100;
|
||||
|
||||
//
|
||||
UInt32 propSize = 0;
|
||||
|
||||
if (AudioDeviceGetPropertyInfo(deviceId,
|
||||
0,
|
||||
mode == QAudio::AudioInput,
|
||||
kAudioDevicePropertyAvailableNominalSampleRates,
|
||||
&propSize,
|
||||
0) == noErr) {
|
||||
|
||||
const int pc = propSize / sizeof(AudioValueRange);
|
||||
|
||||
if (pc > 0) {
|
||||
AudioValueRange* vr = new AudioValueRange[pc];
|
||||
|
||||
if (AudioDeviceGetProperty(deviceId,
|
||||
0,
|
||||
mode == QAudio::AudioInput,
|
||||
kAudioDevicePropertyAvailableNominalSampleRates,
|
||||
&propSize,
|
||||
vr) == noErr) {
|
||||
|
||||
for (int i = 0; i < pc; ++i)
|
||||
rc << vr[i].mMaximum;
|
||||
}
|
||||
|
||||
delete[] vr;
|
||||
}
|
||||
}
|
||||
|
||||
return rc.toList();
|
||||
}
|
||||
|
||||
QList<int> QAudioDeviceInfoInternal::channelsList()
|
||||
{
|
||||
QList<int> rc;
|
||||
|
||||
// Can mix down to 1 channel
|
||||
rc << 1;
|
||||
|
||||
UInt32 propSize = 0;
|
||||
int channels = 0;
|
||||
|
||||
if (AudioDeviceGetPropertyInfo(deviceId,
|
||||
0,
|
||||
mode == QAudio::AudioInput,
|
||||
kAudioDevicePropertyStreamConfiguration,
|
||||
&propSize,
|
||||
0) == noErr) {
|
||||
|
||||
AudioBufferList* audioBufferList = static_cast<AudioBufferList*>(qMalloc(propSize));
|
||||
|
||||
if (audioBufferList != 0) {
|
||||
if (AudioDeviceGetProperty(deviceId,
|
||||
0,
|
||||
mode == QAudio::AudioInput,
|
||||
kAudioDevicePropertyStreamConfiguration,
|
||||
&propSize,
|
||||
audioBufferList) == noErr) {
|
||||
|
||||
for (int i = 0; i < int(audioBufferList->mNumberBuffers); ++i) {
|
||||
channels += audioBufferList->mBuffers[i].mNumberChannels;
|
||||
rc << channels;
|
||||
}
|
||||
}
|
||||
|
||||
qFree(audioBufferList);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
QList<int> QAudioDeviceInfoInternal::sampleSizeList()
|
||||
{
|
||||
return QList<int>() << 8 << 16 << 24 << 32 << 64;
|
||||
}
|
||||
|
||||
QList<QAudioFormat::Endian> QAudioDeviceInfoInternal::byteOrderList()
|
||||
{
|
||||
return QList<QAudioFormat::Endian>() << QAudioFormat::LittleEndian << QAudioFormat::BigEndian;
|
||||
}
|
||||
|
||||
QList<QAudioFormat::SampleType> QAudioDeviceInfoInternal::sampleTypeList()
|
||||
{
|
||||
return QList<QAudioFormat::SampleType>() << QAudioFormat::SignedInt << QAudioFormat::UnSignedInt << QAudioFormat::Float;
|
||||
}
|
||||
|
||||
static QByteArray get_device_info(AudioDeviceID audioDevice, QAudio::Mode mode)
|
||||
{
|
||||
UInt32 size;
|
||||
QByteArray device;
|
||||
QDataStream ds(&device, QIODevice::WriteOnly);
|
||||
AudioStreamBasicDescription sf;
|
||||
CFStringRef name;
|
||||
Boolean isInput = mode == QAudio::AudioInput;
|
||||
|
||||
// Id
|
||||
ds << quint32(audioDevice);
|
||||
|
||||
// Mode
|
||||
size = sizeof(AudioStreamBasicDescription);
|
||||
if (AudioDeviceGetProperty(audioDevice, 0, isInput, kAudioDevicePropertyStreamFormat,
|
||||
&size, &sf) != noErr) {
|
||||
return QByteArray();
|
||||
}
|
||||
ds << quint32(mode);
|
||||
|
||||
// Name
|
||||
size = sizeof(CFStringRef);
|
||||
if (AudioDeviceGetProperty(audioDevice, 0, isInput, kAudioObjectPropertyName,
|
||||
&size, &name) != noErr) {
|
||||
qWarning() << "QAudioDeviceInfo: Unable to find device name";
|
||||
}
|
||||
ds << QCFString::toQString(name);
|
||||
|
||||
CFRelease(name);
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
QByteArray QAudioDeviceInfoInternal::defaultInputDevice()
|
||||
{
|
||||
AudioDeviceID audioDevice;
|
||||
UInt32 size = sizeof(audioDevice);
|
||||
|
||||
if (AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &size,
|
||||
&audioDevice) != noErr) {
|
||||
qWarning() << "QAudioDeviceInfo: Unable to find default input device";
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
return get_device_info(audioDevice, QAudio::AudioInput);
|
||||
}
|
||||
|
||||
QByteArray QAudioDeviceInfoInternal::defaultOutputDevice()
|
||||
{
|
||||
AudioDeviceID audioDevice;
|
||||
UInt32 size = sizeof(audioDevice);
|
||||
|
||||
if (AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size,
|
||||
&audioDevice) != noErr) {
|
||||
qWarning() << "QAudioDeviceInfo: Unable to find default output device";
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
return get_device_info(audioDevice, QAudio::AudioOutput);
|
||||
}
|
||||
|
||||
QList<QByteArray> QAudioDeviceInfoInternal::availableDevices(QAudio::Mode mode)
|
||||
{
|
||||
QList<QByteArray> devices;
|
||||
|
||||
UInt32 propSize = 0;
|
||||
|
||||
if (AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &propSize, 0) == noErr) {
|
||||
|
||||
const int dc = propSize / sizeof(AudioDeviceID);
|
||||
|
||||
if (dc > 0) {
|
||||
AudioDeviceID* audioDevices = new AudioDeviceID[dc];
|
||||
|
||||
if (AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &propSize, audioDevices) == noErr) {
|
||||
for (int i = 0; i < dc; ++i) {
|
||||
QByteArray info = get_device_info(audioDevices[i], mode);
|
||||
if (!info.isNull())
|
||||
devices << info;
|
||||
}
|
||||
}
|
||||
|
||||
delete[] audioDevices;
|
||||
}
|
||||
}
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
97
src/multimedia/audio/qaudiodeviceinfo_mac_p.h
Normal file
97
src/multimedia/audio/qaudiodeviceinfo_mac_p.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $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.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
|
||||
#ifndef QDEVICEINFO_MAC_P_H
|
||||
#define QDEVICEINFO_MAC_P_H
|
||||
|
||||
#include <CoreAudio/CoreAudio.h>
|
||||
|
||||
#include <QtMultimedia/qaudioengine.h>
|
||||
|
||||
QT_BEGIN_HEADER
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QAudioDeviceInfoInternal : public QAbstractAudioDeviceInfo
|
||||
{
|
||||
public:
|
||||
AudioDeviceID deviceId;
|
||||
QString name;
|
||||
QAudio::Mode mode;
|
||||
|
||||
QAudioDeviceInfoInternal(QByteArray const& handle, QAudio::Mode mode);
|
||||
|
||||
bool isFormatSupported(const QAudioFormat& format) const;
|
||||
QAudioFormat preferredFormat() const;
|
||||
QAudioFormat nearestFormat(const QAudioFormat& format) const;
|
||||
|
||||
QString deviceName() const;
|
||||
|
||||
QStringList codecList();
|
||||
QList<int> frequencyList();
|
||||
QList<int> channelsList();
|
||||
QList<int> sampleSizeList();
|
||||
QList<QAudioFormat::Endian> byteOrderList();
|
||||
QList<QAudioFormat::SampleType> sampleTypeList();
|
||||
|
||||
static QByteArray defaultInputDevice();
|
||||
static QByteArray defaultOutputDevice();
|
||||
|
||||
static QList<QByteArray> availableDevices(QAudio::Mode mode);
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
QT_END_HEADER
|
||||
|
||||
#endif // QDEVICEINFO_MAC_P_H
|
||||
240
src/multimedia/audio/qaudiodeviceinfo_symbian_p.cpp
Normal file
240
src/multimedia/audio/qaudiodeviceinfo_symbian_p.cpp
Normal file
@@ -0,0 +1,240 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include "qaudiodeviceinfo_symbian_p.h"
|
||||
#include "qaudio_symbian_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QAudioDeviceInfoInternal::QAudioDeviceInfoInternal(QByteArray device,
|
||||
QAudio::Mode mode)
|
||||
: m_deviceName(QLatin1String(device))
|
||||
, m_mode(mode)
|
||||
, m_updated(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QAudioDeviceInfoInternal::~QAudioDeviceInfoInternal()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QAudioFormat QAudioDeviceInfoInternal::preferredFormat() const
|
||||
{
|
||||
QAudioFormat format;
|
||||
switch (m_mode) {
|
||||
case QAudio::AudioOutput:
|
||||
format.setFrequency(44100);
|
||||
format.setChannels(2);
|
||||
format.setSampleSize(16);
|
||||
format.setByteOrder(QAudioFormat::LittleEndian);
|
||||
format.setSampleType(QAudioFormat::SignedInt);
|
||||
format.setCodec(QLatin1String("audio/pcm"));
|
||||
break;
|
||||
|
||||
case QAudio::AudioInput:
|
||||
format.setFrequency(8000);
|
||||
format.setChannels(1);
|
||||
format.setSampleSize(16);
|
||||
format.setByteOrder(QAudioFormat::LittleEndian);
|
||||
format.setSampleType(QAudioFormat::SignedInt);
|
||||
format.setCodec(QLatin1String("audio/pcm"));
|
||||
break;
|
||||
|
||||
default:
|
||||
Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid mode");
|
||||
}
|
||||
|
||||
if (!isFormatSupported(format)) {
|
||||
format = QAudioFormat();
|
||||
format.setCodec(QLatin1String("audio/pcm"));
|
||||
if (m_capabilities.contains(format.codec())) {
|
||||
const Capabilities &codecCaps = m_capabilities[format.codec()];
|
||||
if (codecCaps.m_frequencies.size())
|
||||
format.setFrequency(codecCaps.m_frequencies[0]);
|
||||
if (codecCaps.m_channels.size())
|
||||
format.setChannels(codecCaps.m_channels[0]);
|
||||
if (codecCaps.m_sampleSizes.size())
|
||||
format.setSampleSize(codecCaps.m_sampleSizes[0]);
|
||||
if (codecCaps.m_byteOrders.size())
|
||||
format.setByteOrder(codecCaps.m_byteOrders[0]);
|
||||
if (codecCaps.m_sampleTypes.size())
|
||||
format.setSampleType(codecCaps.m_sampleTypes[0]);
|
||||
}
|
||||
}
|
||||
|
||||
return format;
|
||||
}
|
||||
|
||||
bool QAudioDeviceInfoInternal::isFormatSupported(
|
||||
const QAudioFormat &format) const
|
||||
{
|
||||
getSupportedFormats();
|
||||
bool supported = false;
|
||||
if (m_capabilities.contains(format.codec())) {
|
||||
const Capabilities &codecCaps = m_capabilities[format.codec()];
|
||||
supported = codecCaps.m_frequencies.contains(format.frequency())
|
||||
&& codecCaps.m_channels.contains(format.channels())
|
||||
&& codecCaps.m_sampleSizes.contains(format.sampleSize())
|
||||
&& codecCaps.m_byteOrders.contains(format.byteOrder())
|
||||
&& codecCaps.m_sampleTypes.contains(format.sampleType());
|
||||
}
|
||||
return supported;
|
||||
}
|
||||
|
||||
QAudioFormat QAudioDeviceInfoInternal::nearestFormat(const QAudioFormat &format) const
|
||||
{
|
||||
if (isFormatSupported(format))
|
||||
return format;
|
||||
else
|
||||
return preferredFormat();
|
||||
}
|
||||
|
||||
QString QAudioDeviceInfoInternal::deviceName() const
|
||||
{
|
||||
return m_deviceName;
|
||||
}
|
||||
|
||||
QStringList QAudioDeviceInfoInternal::codecList()
|
||||
{
|
||||
getSupportedFormats();
|
||||
return m_capabilities.keys();
|
||||
}
|
||||
|
||||
QList<int> QAudioDeviceInfoInternal::frequencyList()
|
||||
{
|
||||
getSupportedFormats();
|
||||
return m_unionCapabilities.m_frequencies;
|
||||
}
|
||||
|
||||
QList<int> QAudioDeviceInfoInternal::channelsList()
|
||||
{
|
||||
getSupportedFormats();
|
||||
return m_unionCapabilities.m_channels;
|
||||
}
|
||||
|
||||
QList<int> QAudioDeviceInfoInternal::sampleSizeList()
|
||||
{
|
||||
getSupportedFormats();
|
||||
return m_unionCapabilities.m_sampleSizes;
|
||||
}
|
||||
|
||||
QList<QAudioFormat::Endian> QAudioDeviceInfoInternal::byteOrderList()
|
||||
{
|
||||
getSupportedFormats();
|
||||
return m_unionCapabilities.m_byteOrders;
|
||||
}
|
||||
|
||||
QList<QAudioFormat::SampleType> QAudioDeviceInfoInternal::sampleTypeList()
|
||||
{
|
||||
getSupportedFormats();
|
||||
return m_unionCapabilities.m_sampleTypes;
|
||||
}
|
||||
|
||||
QByteArray QAudioDeviceInfoInternal::defaultInputDevice()
|
||||
{
|
||||
return QByteArray("default");
|
||||
}
|
||||
|
||||
QByteArray QAudioDeviceInfoInternal::defaultOutputDevice()
|
||||
{
|
||||
return QByteArray("default");
|
||||
}
|
||||
|
||||
QList<QByteArray> QAudioDeviceInfoInternal::availableDevices(QAudio::Mode)
|
||||
{
|
||||
QList<QByteArray> result;
|
||||
result += QByteArray("default");
|
||||
return result;
|
||||
}
|
||||
|
||||
void QAudioDeviceInfoInternal::devsoundInitializeComplete(int err)
|
||||
{
|
||||
m_intializationResult = err;
|
||||
m_initializing = false;
|
||||
}
|
||||
|
||||
// Helper function
|
||||
template<typename T>
|
||||
void appendUnique(QList<T> &left, const QList<T> &right)
|
||||
{
|
||||
foreach (const T &value, right)
|
||||
if (!left.contains(value))
|
||||
left += value;
|
||||
}
|
||||
|
||||
void QAudioDeviceInfoInternal::getSupportedFormats() const
|
||||
{
|
||||
if (!m_updated) {
|
||||
QScopedPointer<SymbianAudio::DevSoundWrapper> devsound(new SymbianAudio::DevSoundWrapper(m_mode));
|
||||
connect(devsound.data(), SIGNAL(initializeComplete(int)),
|
||||
this, SLOT(devsoundInitializeComplete(int)));
|
||||
|
||||
foreach (const QString& codec, devsound->supportedCodecs()) {
|
||||
m_initializing = true;
|
||||
devsound->initialize(codec);
|
||||
while (m_initializing)
|
||||
QCoreApplication::instance()->processEvents(QEventLoop::WaitForMoreEvents);
|
||||
if (KErrNone == m_intializationResult) {
|
||||
m_capabilities[codec].m_frequencies = devsound->supportedFrequencies();
|
||||
appendUnique(m_unionCapabilities.m_frequencies, devsound->supportedFrequencies());
|
||||
|
||||
m_capabilities[codec].m_channels = devsound->supportedChannels();
|
||||
appendUnique(m_unionCapabilities.m_channels, devsound->supportedChannels());
|
||||
|
||||
m_capabilities[codec].m_sampleSizes = devsound->supportedSampleSizes();
|
||||
appendUnique(m_unionCapabilities.m_sampleSizes, devsound->supportedSampleSizes());
|
||||
|
||||
m_capabilities[codec].m_byteOrders = devsound->supportedByteOrders();
|
||||
appendUnique(m_unionCapabilities.m_byteOrders, devsound->supportedByteOrders());
|
||||
|
||||
m_capabilities[codec].m_sampleTypes = devsound->supportedSampleTypes();
|
||||
appendUnique(m_unionCapabilities.m_sampleTypes, devsound->supportedSampleTypes());
|
||||
}
|
||||
}
|
||||
|
||||
m_updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
121
src/multimedia/audio/qaudiodeviceinfo_symbian_p.h
Normal file
121
src/multimedia/audio/qaudiodeviceinfo_symbian_p.h
Normal file
@@ -0,0 +1,121 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $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.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#ifndef QAUDIODEVICEINFO_SYMBIAN_P_H
|
||||
#define QAUDIODEVICEINFO_SYMBIAN_P_H
|
||||
|
||||
#include <QtCore/QMap>
|
||||
#include <QtMultimedia/qaudioengine.h>
|
||||
#include <sounddevice.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace SymbianAudio {
|
||||
class DevSoundWrapper;
|
||||
}
|
||||
|
||||
class QAudioDeviceInfoInternal
|
||||
: public QAbstractAudioDeviceInfo
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QAudioDeviceInfoInternal(QByteArray device, QAudio::Mode mode);
|
||||
~QAudioDeviceInfoInternal();
|
||||
|
||||
// QAbstractAudioDeviceInfo
|
||||
QAudioFormat preferredFormat() const;
|
||||
bool isFormatSupported(const QAudioFormat &format) const;
|
||||
QAudioFormat nearestFormat(const QAudioFormat &format) const;
|
||||
QString deviceName() const;
|
||||
QStringList codecList();
|
||||
QList<int> frequencyList();
|
||||
QList<int> channelsList();
|
||||
QList<int> sampleSizeList();
|
||||
QList<QAudioFormat::Endian> byteOrderList();
|
||||
QList<QAudioFormat::SampleType> sampleTypeList();
|
||||
static QByteArray defaultInputDevice();
|
||||
static QByteArray defaultOutputDevice();
|
||||
static QList<QByteArray> availableDevices(QAudio::Mode);
|
||||
|
||||
private slots:
|
||||
void devsoundInitializeComplete(int err);
|
||||
|
||||
private:
|
||||
void getSupportedFormats() const;
|
||||
|
||||
private:
|
||||
mutable bool m_initializing;
|
||||
int m_intializationResult;
|
||||
|
||||
QString m_deviceName;
|
||||
QAudio::Mode m_mode;
|
||||
|
||||
struct Capabilities
|
||||
{
|
||||
QList<int> m_frequencies;
|
||||
QList<int> m_channels;
|
||||
QList<int> m_sampleSizes;
|
||||
QList<QAudioFormat::Endian> m_byteOrders;
|
||||
QList<QAudioFormat::SampleType> m_sampleTypes;
|
||||
};
|
||||
|
||||
// Mutable to allow lazy initialization when called from const-qualified
|
||||
// public functions (isFormatSupported, nearestFormat)
|
||||
mutable bool m_updated;
|
||||
mutable QMap<QString, Capabilities> m_capabilities;
|
||||
mutable Capabilities m_unionCapabilities;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
447
src/multimedia/audio/qaudiodeviceinfo_win32_p.cpp
Normal file
447
src/multimedia/audio/qaudiodeviceinfo_win32_p.cpp
Normal file
@@ -0,0 +1,447 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $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.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
#include "qaudiodeviceinfo_win32_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
// For mingw toolchain mmsystem.h only defines half the defines, so add if needed.
|
||||
#ifndef WAVE_FORMAT_44M08
|
||||
#define WAVE_FORMAT_44M08 0x00000100
|
||||
#define WAVE_FORMAT_44S08 0x00000200
|
||||
#define WAVE_FORMAT_44M16 0x00000400
|
||||
#define WAVE_FORMAT_44S16 0x00000800
|
||||
#define WAVE_FORMAT_48M08 0x00001000
|
||||
#define WAVE_FORMAT_48S08 0x00002000
|
||||
#define WAVE_FORMAT_48M16 0x00004000
|
||||
#define WAVE_FORMAT_48S16 0x00008000
|
||||
#define WAVE_FORMAT_96M08 0x00010000
|
||||
#define WAVE_FORMAT_96S08 0x00020000
|
||||
#define WAVE_FORMAT_96M16 0x00040000
|
||||
#define WAVE_FORMAT_96S16 0x00080000
|
||||
#endif
|
||||
|
||||
|
||||
QAudioDeviceInfoInternal::QAudioDeviceInfoInternal(QByteArray dev, QAudio::Mode mode)
|
||||
{
|
||||
device = QLatin1String(dev);
|
||||
this->mode = mode;
|
||||
|
||||
updateLists();
|
||||
}
|
||||
|
||||
QAudioDeviceInfoInternal::~QAudioDeviceInfoInternal()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
bool QAudioDeviceInfoInternal::isFormatSupported(const QAudioFormat& format) const
|
||||
{
|
||||
return testSettings(format);
|
||||
}
|
||||
|
||||
QAudioFormat QAudioDeviceInfoInternal::preferredFormat() const
|
||||
{
|
||||
QAudioFormat nearest;
|
||||
if(mode == QAudio::AudioOutput) {
|
||||
nearest.setFrequency(44100);
|
||||
nearest.setChannelCount(2);
|
||||
nearest.setByteOrder(QAudioFormat::LittleEndian);
|
||||
nearest.setSampleType(QAudioFormat::SignedInt);
|
||||
nearest.setSampleSize(16);
|
||||
nearest.setCodec(QLatin1String("audio/pcm"));
|
||||
} else {
|
||||
nearest.setFrequency(11025);
|
||||
nearest.setChannelCount(1);
|
||||
nearest.setByteOrder(QAudioFormat::LittleEndian);
|
||||
nearest.setSampleType(QAudioFormat::SignedInt);
|
||||
nearest.setSampleSize(8);
|
||||
nearest.setCodec(QLatin1String("audio/pcm"));
|
||||
}
|
||||
return nearest;
|
||||
}
|
||||
|
||||
QAudioFormat QAudioDeviceInfoInternal::nearestFormat(const QAudioFormat& format) const
|
||||
{
|
||||
if(testSettings(format))
|
||||
return format;
|
||||
else
|
||||
return preferredFormat();
|
||||
}
|
||||
|
||||
QString QAudioDeviceInfoInternal::deviceName() const
|
||||
{
|
||||
return device;
|
||||
}
|
||||
|
||||
QStringList QAudioDeviceInfoInternal::codecList()
|
||||
{
|
||||
updateLists();
|
||||
return codecz;
|
||||
}
|
||||
|
||||
QList<int> QAudioDeviceInfoInternal::frequencyList()
|
||||
{
|
||||
updateLists();
|
||||
return freqz;
|
||||
}
|
||||
|
||||
QList<int> QAudioDeviceInfoInternal::channelsList()
|
||||
{
|
||||
updateLists();
|
||||
return channelz;
|
||||
}
|
||||
|
||||
QList<int> QAudioDeviceInfoInternal::sampleSizeList()
|
||||
{
|
||||
updateLists();
|
||||
return sizez;
|
||||
}
|
||||
|
||||
QList<QAudioFormat::Endian> QAudioDeviceInfoInternal::byteOrderList()
|
||||
{
|
||||
updateLists();
|
||||
return byteOrderz;
|
||||
}
|
||||
|
||||
QList<QAudioFormat::SampleType> QAudioDeviceInfoInternal::sampleTypeList()
|
||||
{
|
||||
updateLists();
|
||||
return typez;
|
||||
}
|
||||
|
||||
|
||||
bool QAudioDeviceInfoInternal::open()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void QAudioDeviceInfoInternal::close()
|
||||
{
|
||||
}
|
||||
|
||||
bool QAudioDeviceInfoInternal::testSettings(const QAudioFormat& format) const
|
||||
{
|
||||
// Set nearest to closest settings that do work.
|
||||
// See if what is in settings will work (return value).
|
||||
|
||||
bool failed = false;
|
||||
bool match = false;
|
||||
|
||||
// check codec
|
||||
for( int i = 0; i < codecz.count(); i++) {
|
||||
if (format.codec() == codecz.at(i))
|
||||
match = true;
|
||||
}
|
||||
if (!match) failed = true;
|
||||
|
||||
// check channel
|
||||
match = false;
|
||||
if (!failed) {
|
||||
for( int i = 0; i < channelz.count(); i++) {
|
||||
if (format.channels() == channelz.at(i)) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!match)
|
||||
failed = true;
|
||||
}
|
||||
|
||||
// check frequency
|
||||
match = false;
|
||||
if (!failed) {
|
||||
for( int i = 0; i < freqz.count(); i++) {
|
||||
if (format.frequency() == freqz.at(i)) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!match)
|
||||
failed = true;
|
||||
}
|
||||
|
||||
// check sample size
|
||||
match = false;
|
||||
if (!failed) {
|
||||
for( int i = 0; i < sizez.count(); i++) {
|
||||
if (format.sampleSize() == sizez.at(i)) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!match)
|
||||
failed = true;
|
||||
}
|
||||
|
||||
// check byte order
|
||||
match = false;
|
||||
if (!failed) {
|
||||
for( int i = 0; i < byteOrderz.count(); i++) {
|
||||
if (format.byteOrder() == byteOrderz.at(i)) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!match)
|
||||
failed = true;
|
||||
}
|
||||
|
||||
// check sample type
|
||||
match = false;
|
||||
if (!failed) {
|
||||
for( int i = 0; i < typez.count(); i++) {
|
||||
if (format.sampleType() == typez.at(i)) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!match)
|
||||
failed = true;
|
||||
}
|
||||
|
||||
if(!failed) {
|
||||
// settings work
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void QAudioDeviceInfoInternal::updateLists()
|
||||
{
|
||||
// redo all lists based on current settings
|
||||
bool base = false;
|
||||
bool match = false;
|
||||
DWORD fmt = NULL;
|
||||
QString tmp;
|
||||
|
||||
if(device.compare(QLatin1String("default")) == 0)
|
||||
base = true;
|
||||
|
||||
if(mode == QAudio::AudioOutput) {
|
||||
WAVEOUTCAPS woc;
|
||||
unsigned long iNumDevs,i;
|
||||
iNumDevs = waveOutGetNumDevs();
|
||||
for(i=0;i<iNumDevs;i++) {
|
||||
if(waveOutGetDevCaps(i, &woc, sizeof(WAVEOUTCAPS))
|
||||
== MMSYSERR_NOERROR) {
|
||||
tmp = QString((const QChar *)woc.szPname);
|
||||
if(tmp.compare(device) == 0) {
|
||||
match = true;
|
||||
fmt = woc.dwFormats;
|
||||
break;
|
||||
}
|
||||
if(base) {
|
||||
match = true;
|
||||
fmt = woc.dwFormats;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
WAVEINCAPS woc;
|
||||
unsigned long iNumDevs,i;
|
||||
iNumDevs = waveInGetNumDevs();
|
||||
for(i=0;i<iNumDevs;i++) {
|
||||
if(waveInGetDevCaps(i, &woc, sizeof(WAVEINCAPS))
|
||||
== MMSYSERR_NOERROR) {
|
||||
tmp = QString((const QChar *)woc.szPname);
|
||||
if(tmp.compare(device) == 0) {
|
||||
match = true;
|
||||
fmt = woc.dwFormats;
|
||||
break;
|
||||
}
|
||||
if(base) {
|
||||
match = true;
|
||||
fmt = woc.dwFormats;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sizez.clear();
|
||||
freqz.clear();
|
||||
channelz.clear();
|
||||
byteOrderz.clear();
|
||||
typez.clear();
|
||||
codecz.clear();
|
||||
|
||||
if(match) {
|
||||
if((fmt && WAVE_FORMAT_1M08)
|
||||
|| (fmt && WAVE_FORMAT_1S08)
|
||||
|| (fmt && WAVE_FORMAT_2M08)
|
||||
|| (fmt && WAVE_FORMAT_2S08)
|
||||
|| (fmt && WAVE_FORMAT_4M08)
|
||||
|| (fmt && WAVE_FORMAT_4S08)
|
||||
#ifndef Q_OS_WINCE
|
||||
|| (fmt && WAVE_FORMAT_48M08)
|
||||
|| (fmt && WAVE_FORMAT_48S08)
|
||||
|| (fmt && WAVE_FORMAT_96M08)
|
||||
|| (fmt && WAVE_FORMAT_96S08)
|
||||
#endif
|
||||
) {
|
||||
sizez.append(8);
|
||||
}
|
||||
if((fmt && WAVE_FORMAT_1M16)
|
||||
|| (fmt && WAVE_FORMAT_1S16)
|
||||
|| (fmt && WAVE_FORMAT_2M16)
|
||||
|| (fmt && WAVE_FORMAT_2S16)
|
||||
|| (fmt && WAVE_FORMAT_4M16)
|
||||
|| (fmt && WAVE_FORMAT_4S16)
|
||||
#ifndef Q_OS_WINCE
|
||||
|| (fmt && WAVE_FORMAT_48M16)
|
||||
|| (fmt && WAVE_FORMAT_48S16)
|
||||
|| (fmt && WAVE_FORMAT_96M16)
|
||||
|| (fmt && WAVE_FORMAT_96S16)
|
||||
#endif
|
||||
) {
|
||||
sizez.append(16);
|
||||
}
|
||||
if((fmt && WAVE_FORMAT_1M08)
|
||||
|| (fmt && WAVE_FORMAT_1S08)
|
||||
|| (fmt && WAVE_FORMAT_1M16)
|
||||
|| (fmt && WAVE_FORMAT_1S16)) {
|
||||
freqz.append(11025);
|
||||
}
|
||||
if((fmt && WAVE_FORMAT_2M08)
|
||||
|| (fmt && WAVE_FORMAT_2S08)
|
||||
|| (fmt && WAVE_FORMAT_2M16)
|
||||
|| (fmt && WAVE_FORMAT_2S16)) {
|
||||
freqz.append(22050);
|
||||
}
|
||||
if((fmt && WAVE_FORMAT_4M08)
|
||||
|| (fmt && WAVE_FORMAT_4S08)
|
||||
|| (fmt && WAVE_FORMAT_4M16)
|
||||
|| (fmt && WAVE_FORMAT_4S16)) {
|
||||
freqz.append(44100);
|
||||
}
|
||||
#ifndef Q_OS_WINCE
|
||||
if((fmt && WAVE_FORMAT_48M08)
|
||||
|| (fmt && WAVE_FORMAT_48S08)
|
||||
|| (fmt && WAVE_FORMAT_48M16)
|
||||
|| (fmt && WAVE_FORMAT_48S16)) {
|
||||
freqz.append(48000);
|
||||
}
|
||||
if((fmt && WAVE_FORMAT_96M08)
|
||||
|| (fmt && WAVE_FORMAT_96S08)
|
||||
|| (fmt && WAVE_FORMAT_96M16)
|
||||
|| (fmt && WAVE_FORMAT_96S16)) {
|
||||
freqz.append(96000);
|
||||
}
|
||||
#endif
|
||||
channelz.append(1);
|
||||
channelz.append(2);
|
||||
if (mode == QAudio::AudioOutput) {
|
||||
channelz.append(4);
|
||||
channelz.append(6);
|
||||
channelz.append(8);
|
||||
}
|
||||
|
||||
byteOrderz.append(QAudioFormat::LittleEndian);
|
||||
|
||||
typez.append(QAudioFormat::SignedInt);
|
||||
typez.append(QAudioFormat::UnSignedInt);
|
||||
|
||||
codecz.append(QLatin1String("audio/pcm"));
|
||||
}
|
||||
if (freqz.count() > 0)
|
||||
freqz.prepend(8000);
|
||||
}
|
||||
|
||||
QList<QByteArray> QAudioDeviceInfoInternal::availableDevices(QAudio::Mode mode)
|
||||
{
|
||||
Q_UNUSED(mode)
|
||||
|
||||
QList<QByteArray> devices;
|
||||
|
||||
if(mode == QAudio::AudioOutput) {
|
||||
WAVEOUTCAPS woc;
|
||||
unsigned long iNumDevs,i;
|
||||
iNumDevs = waveOutGetNumDevs();
|
||||
for(i=0;i<iNumDevs;i++) {
|
||||
if(waveOutGetDevCaps(i, &woc, sizeof(WAVEOUTCAPS))
|
||||
== MMSYSERR_NOERROR) {
|
||||
devices.append(QString((const QChar *)woc.szPname).toLocal8Bit().constData());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
WAVEINCAPS woc;
|
||||
unsigned long iNumDevs,i;
|
||||
iNumDevs = waveInGetNumDevs();
|
||||
for(i=0;i<iNumDevs;i++) {
|
||||
if(waveInGetDevCaps(i, &woc, sizeof(WAVEINCAPS))
|
||||
== MMSYSERR_NOERROR) {
|
||||
devices.append(QString((const QChar *)woc.szPname).toLocal8Bit().constData());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if(devices.count() > 0)
|
||||
devices.append("default");
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
QByteArray QAudioDeviceInfoInternal::defaultOutputDevice()
|
||||
{
|
||||
return QByteArray("default");
|
||||
}
|
||||
|
||||
QByteArray QAudioDeviceInfoInternal::defaultInputDevice()
|
||||
{
|
||||
return QByteArray("default");
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
112
src/multimedia/audio/qaudiodeviceinfo_win32_p.h
Normal file
112
src/multimedia/audio/qaudiodeviceinfo_win32_p.h
Normal file
@@ -0,0 +1,112 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $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.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
|
||||
#ifndef QAUDIODEVICEINFOWIN_H
|
||||
#define QAUDIODEVICEINFOWIN_H
|
||||
|
||||
#include <QtCore/qbytearray.h>
|
||||
#include <QtCore/qstringlist.h>
|
||||
#include <QtCore/qlist.h>
|
||||
#include <QtCore/qdebug.h>
|
||||
|
||||
#include <QtMultimedia/qaudiodeviceinfo.h>
|
||||
#include <QtMultimedia/qaudioengine.h>
|
||||
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
const unsigned int MAX_SAMPLE_RATES = 5;
|
||||
const unsigned int SAMPLE_RATES[] = { 8000, 11025, 22050, 44100, 48000 };
|
||||
|
||||
class QAudioDeviceInfoInternal : public QAbstractAudioDeviceInfo
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QAudioDeviceInfoInternal(QByteArray dev,QAudio::Mode mode);
|
||||
~QAudioDeviceInfoInternal();
|
||||
|
||||
bool open();
|
||||
void close();
|
||||
|
||||
bool testSettings(const QAudioFormat& format) const;
|
||||
void updateLists();
|
||||
QAudioFormat preferredFormat() const;
|
||||
bool isFormatSupported(const QAudioFormat& format) const;
|
||||
QAudioFormat nearestFormat(const QAudioFormat& format) const;
|
||||
QString deviceName() const;
|
||||
QStringList codecList();
|
||||
QList<int> frequencyList();
|
||||
QList<int> channelsList();
|
||||
QList<int> sampleSizeList();
|
||||
QList<QAudioFormat::Endian> byteOrderList();
|
||||
QList<QAudioFormat::SampleType> sampleTypeList();
|
||||
static QByteArray defaultInputDevice();
|
||||
static QByteArray defaultOutputDevice();
|
||||
static QList<QByteArray> availableDevices(QAudio::Mode);
|
||||
|
||||
private:
|
||||
QAudio::Mode mode;
|
||||
QString device;
|
||||
QAudioFormat nearest;
|
||||
QList<int> freqz;
|
||||
QList<int> channelz;
|
||||
QList<int> sizez;
|
||||
QList<QAudioFormat::Endian> byteOrderz;
|
||||
QStringList codecz;
|
||||
QList<QAudioFormat::SampleType> typez;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
343
src/multimedia/audio/qaudioengine.cpp
Normal file
343
src/multimedia/audio/qaudioengine.cpp
Normal file
@@ -0,0 +1,343 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QtMultimedia/qaudioengine.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
/*!
|
||||
\class QAbstractAudioDeviceInfo
|
||||
\brief The QAbstractAudioDeviceInfo class provides access for QAudioDeviceInfo to access the audio
|
||||
device provided by the plugin.
|
||||
\internal
|
||||
|
||||
\ingroup multimedia
|
||||
|
||||
This class implements the audio functionality for
|
||||
QAudioDeviceInfo, i.e., QAudioDeviceInfo keeps a
|
||||
QAbstractAudioDeviceInfo and routes function calls to it. For a
|
||||
description of the functionality that QAbstractAudioDeviceInfo
|
||||
implements, you can read the class and functions documentation of
|
||||
QAudioDeviceInfo.
|
||||
|
||||
\sa QAudioDeviceInfo
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual QAudioFormat QAbstractAudioDeviceInfo::preferredFormat() const
|
||||
Returns the nearest settings.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual bool QAbstractAudioDeviceInfo::isFormatSupported(const QAudioFormat& format) const
|
||||
Returns true if \a format is available from audio device.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual QAudioFormat QAbstractAudioDeviceInfo::nearestFormat(const QAudioFormat& format) const
|
||||
Returns the nearest settings \a format.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual QString QAbstractAudioDeviceInfo::deviceName() const
|
||||
Returns the audio device name.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual QStringList QAbstractAudioDeviceInfo::codecList()
|
||||
Returns the list of currently available codecs.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual QList<int> QAbstractAudioDeviceInfo::frequencyList()
|
||||
Returns the list of currently available frequencies.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual QList<int> QAbstractAudioDeviceInfo::channelsList()
|
||||
Returns the list of currently available channels.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual QList<int> QAbstractAudioDeviceInfo::sampleSizeList()
|
||||
Returns the list of currently available sample sizes.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual QList<QAudioFormat::Endian> QAbstractAudioDeviceInfo::byteOrderList()
|
||||
Returns the list of currently available byte orders.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual QList<QAudioFormat::SampleType> QAbstractAudioDeviceInfo::sampleTypeList()
|
||||
Returns the list of currently available sample types.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\class QAbstractAudioOutput
|
||||
\brief The QAbstractAudioOutput class provides access for QAudioOutput to access the audio
|
||||
device provided by the plugin.
|
||||
\internal
|
||||
|
||||
\ingroup multimedia
|
||||
|
||||
QAbstractAudioOutput implements audio functionality for
|
||||
QAudioOutput, i.e., QAudioOutput routes function calls to
|
||||
QAbstractAudioOutput. For a description of the functionality that
|
||||
is implemented, see the QAudioOutput class and function
|
||||
descriptions.
|
||||
|
||||
\sa QAudioOutput
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual QIODevice* QAbstractAudioOutput::start(QIODevice* device)
|
||||
Uses the \a device as the QIODevice to transfer data. If \a device is null then the class
|
||||
creates an internal QIODevice. Returns a pointer to the QIODevice being used to handle
|
||||
the data transfer. This QIODevice can be used to write() audio data directly. Passing a
|
||||
QIODevice allows the data to be transferred without any extra code.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual void QAbstractAudioOutput::stop()
|
||||
Stops the audio output.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual void QAbstractAudioOutput::reset()
|
||||
Drops all audio data in the buffers, resets buffers to zero.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual void QAbstractAudioOutput::suspend()
|
||||
Stops processing audio data, preserving buffered audio data.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual void QAbstractAudioOutput::resume()
|
||||
Resumes processing audio data after a suspend()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual int QAbstractAudioOutput::bytesFree() const
|
||||
Returns the free space available in bytes in the audio buffer.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual int QAbstractAudioOutput::periodSize() const
|
||||
Returns the period size in bytes.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual void QAbstractAudioOutput::setBufferSize(int value)
|
||||
Sets the audio buffer size to \a value in bytes.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual int QAbstractAudioOutput::bufferSize() const
|
||||
Returns the audio buffer size in bytes.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual void QAbstractAudioOutput::setNotifyInterval(int ms)
|
||||
Sets the interval for notify() signal to be emitted. This is based on the \a ms
|
||||
of audio data processed not on actual real-time. The resolution of the timer
|
||||
is platform specific.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual int QAbstractAudioOutput::notifyInterval() const
|
||||
Returns the notify interval in milliseconds.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual qint64 QAbstractAudioOutput::processedUSecs() const
|
||||
Returns the amount of audio data processed since start() was called in milliseconds.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual qint64 QAbstractAudioOutput::elapsedUSecs() const
|
||||
Returns the milliseconds since start() was called, including time in Idle and suspend states.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual QAudio::Error QAbstractAudioOutput::error() const
|
||||
Returns the error state.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual QAudio::State QAbstractAudioOutput::state() const
|
||||
Returns the state of audio processing.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual QAudioFormat QAbstractAudioOutput::format() const
|
||||
Returns the QAudioFormat being used.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QAbstractAudioOutput::stateChanged(QAudio::State state)
|
||||
This signal is emitted when the device \a state has changed.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QAbstractAudioOutput::notify()
|
||||
This signal is emitted when x ms of audio data has been processed
|
||||
the interval set by setNotifyInterval(x).
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
\class QAbstractAudioInput
|
||||
\brief The QAbstractAudioInput class provides access for QAudioInput to access the audio
|
||||
device provided by the plugin.
|
||||
\internal
|
||||
|
||||
\ingroup multimedia
|
||||
|
||||
QAudioDeviceInput keeps an instance of QAbstractAudioInput and
|
||||
routes calls to functions of the same name to QAbstractAudioInput.
|
||||
This means that it is QAbstractAudioInput that implements the
|
||||
audio functionality. For a description of the functionality, see
|
||||
the QAudioInput class description.
|
||||
|
||||
\sa QAudioInput
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual QIODevice* QAbstractAudioInput::start(QIODevice* device)
|
||||
Uses the \a device as the QIODevice to transfer data. If \a device is null
|
||||
then the class creates an internal QIODevice. Returns a pointer to the
|
||||
QIODevice being used to handle the data transfer. This QIODevice can be used to
|
||||
read() audio data directly. Passing a QIODevice allows the data to be transferred
|
||||
without any extra code.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual void QAbstractAudioInput::stop()
|
||||
Stops the audio input.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual void QAbstractAudioInput::reset()
|
||||
Drops all audio data in the buffers, resets buffers to zero.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual void QAbstractAudioInput::suspend()
|
||||
Stops processing audio data, preserving buffered audio data.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual void QAbstractAudioInput::resume()
|
||||
Resumes processing audio data after a suspend().
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual int QAbstractAudioInput::bytesReady() const
|
||||
Returns the amount of audio data available to read in bytes.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual int QAbstractAudioInput::periodSize() const
|
||||
Returns the period size in bytes.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual void QAbstractAudioInput::setBufferSize(int value)
|
||||
Sets the audio buffer size to \a value in milliseconds.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual int QAbstractAudioInput::bufferSize() const
|
||||
Returns the audio buffer size in milliseconds.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual void QAbstractAudioInput::setNotifyInterval(int ms)
|
||||
Sets the interval for notify() signal to be emitted. This is based
|
||||
on the \a ms of audio data processed not on actual real-time.
|
||||
The resolution of the timer is platform specific.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual int QAbstractAudioInput::notifyInterval() const
|
||||
Returns the notify interval in milliseconds.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual qint64 QAbstractAudioInput::processedUSecs() const
|
||||
Returns the amount of audio data processed since start() was called in milliseconds.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual qint64 QAbstractAudioInput::elapsedUSecs() const
|
||||
Returns the milliseconds since start() was called, including time in Idle and suspend states.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual QAudio::Error QAbstractAudioInput::error() const
|
||||
Returns the error state.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual QAudio::State QAbstractAudioInput::state() const
|
||||
Returns the state of audio processing.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual QAudioFormat QAbstractAudioInput::format() const
|
||||
Returns the QAudioFormat being used
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QAbstractAudioInput::stateChanged(QAudio::State state)
|
||||
This signal is emitted when the device \a state has changed.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QAbstractAudioInput::notify()
|
||||
This signal is emitted when x ms of audio data has been processed
|
||||
the interval set by setNotifyInterval(x).
|
||||
*/
|
||||
|
||||
|
||||
QT_END_NAMESPACE
|
||||
131
src/multimedia/audio/qaudioengine.h
Normal file
131
src/multimedia/audio/qaudioengine.h
Normal file
@@ -0,0 +1,131 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QAUDIOENGINE_H
|
||||
#define QAUDIOENGINE_H
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
#include <QtMultimedia/qaudio.h>
|
||||
#include <QtMultimedia/qaudioformat.h>
|
||||
#include <QtMultimedia/qaudiodeviceinfo.h>
|
||||
|
||||
QT_BEGIN_HEADER
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QT_MODULE(Multimedia)
|
||||
|
||||
class Q_MULTIMEDIA_EXPORT QAbstractAudioDeviceInfo : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
virtual QAudioFormat preferredFormat() const = 0;
|
||||
virtual bool isFormatSupported(const QAudioFormat &format) const = 0;
|
||||
virtual QAudioFormat nearestFormat(const QAudioFormat &format) const = 0;
|
||||
virtual QString deviceName() const = 0;
|
||||
virtual QStringList codecList() = 0;
|
||||
virtual QList<int> frequencyList() = 0;
|
||||
virtual QList<int> channelsList() = 0;
|
||||
virtual QList<int> sampleSizeList() = 0;
|
||||
virtual QList<QAudioFormat::Endian> byteOrderList() = 0;
|
||||
virtual QList<QAudioFormat::SampleType> sampleTypeList() = 0;
|
||||
};
|
||||
|
||||
class Q_MULTIMEDIA_EXPORT QAbstractAudioOutput : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
virtual QIODevice* start(QIODevice* device) = 0;
|
||||
virtual void stop() = 0;
|
||||
virtual void reset() = 0;
|
||||
virtual void suspend() = 0;
|
||||
virtual void resume() = 0;
|
||||
virtual int bytesFree() const = 0;
|
||||
virtual int periodSize() const = 0;
|
||||
virtual void setBufferSize(int value) = 0;
|
||||
virtual int bufferSize() const = 0;
|
||||
virtual void setNotifyInterval(int milliSeconds) = 0;
|
||||
virtual int notifyInterval() const = 0;
|
||||
virtual qint64 processedUSecs() const = 0;
|
||||
virtual qint64 elapsedUSecs() const = 0;
|
||||
virtual QAudio::Error error() const = 0;
|
||||
virtual QAudio::State state() const = 0;
|
||||
virtual QAudioFormat format() const = 0;
|
||||
|
||||
Q_SIGNALS:
|
||||
void stateChanged(QAudio::State);
|
||||
void notify();
|
||||
};
|
||||
|
||||
class Q_MULTIMEDIA_EXPORT QAbstractAudioInput : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
virtual QIODevice* start(QIODevice* device) = 0;
|
||||
virtual void stop() = 0;
|
||||
virtual void reset() = 0;
|
||||
virtual void suspend() = 0;
|
||||
virtual void resume() = 0;
|
||||
virtual int bytesReady() const = 0;
|
||||
virtual int periodSize() const = 0;
|
||||
virtual void setBufferSize(int value) = 0;
|
||||
virtual int bufferSize() const = 0;
|
||||
virtual void setNotifyInterval(int milliSeconds) = 0;
|
||||
virtual int notifyInterval() const = 0;
|
||||
virtual qint64 processedUSecs() const = 0;
|
||||
virtual qint64 elapsedUSecs() const = 0;
|
||||
virtual QAudio::Error error() const = 0;
|
||||
virtual QAudio::State state() const = 0;
|
||||
virtual QAudioFormat format() const = 0;
|
||||
|
||||
Q_SIGNALS:
|
||||
void stateChanged(QAudio::State);
|
||||
void notify();
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
QT_END_HEADER
|
||||
|
||||
#endif // QAUDIOENGINE_H
|
||||
54
src/multimedia/audio/qaudioengineplugin.cpp
Normal file
54
src/multimedia/audio/qaudioengineplugin.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#include <QtMultimedia/qaudioengineplugin.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QAudioEnginePlugin::QAudioEnginePlugin(QObject* parent) :
|
||||
QObject(parent)
|
||||
{}
|
||||
|
||||
QAudioEnginePlugin::~QAudioEnginePlugin()
|
||||
{}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
93
src/multimedia/audio/qaudioengineplugin.h
Normal file
93
src/multimedia/audio/qaudioengineplugin.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#ifndef QAUDIOENGINEPLUGIN_H
|
||||
#define QAUDIOENGINEPLUGIN_H
|
||||
|
||||
#include <QtCore/qstring.h>
|
||||
#include <QtCore/qplugin.h>
|
||||
#include <QtCore/qfactoryinterface.h>
|
||||
|
||||
#include <QtMultimedia/qaudioformat.h>
|
||||
#include <QtMultimedia/qaudiodeviceinfo.h>
|
||||
#include <QtMultimedia/qaudioengine.h>
|
||||
|
||||
|
||||
QT_BEGIN_HEADER
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QT_MODULE(Multimedia)
|
||||
|
||||
struct Q_MULTIMEDIA_EXPORT QAudioEngineFactoryInterface : public QFactoryInterface
|
||||
{
|
||||
virtual QList<QByteArray> availableDevices(QAudio::Mode) const = 0;
|
||||
virtual QAbstractAudioInput* createInput(const QByteArray& device, const QAudioFormat& format = QAudioFormat()) = 0;
|
||||
virtual QAbstractAudioOutput* createOutput(const QByteArray& device, const QAudioFormat& format = QAudioFormat()) = 0;
|
||||
virtual QAbstractAudioDeviceInfo* createDeviceInfo(const QByteArray& device, QAudio::Mode mode) = 0;
|
||||
};
|
||||
|
||||
#define QAudioEngineFactoryInterface_iid \
|
||||
"com.nokia.qt.QAudioEngineFactoryInterface"
|
||||
Q_DECLARE_INTERFACE(QAudioEngineFactoryInterface, QAudioEngineFactoryInterface_iid)
|
||||
|
||||
class Q_MULTIMEDIA_EXPORT QAudioEnginePlugin : public QObject, public QAudioEngineFactoryInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(QAudioEngineFactoryInterface:QFactoryInterface)
|
||||
|
||||
public:
|
||||
QAudioEnginePlugin(QObject *parent = 0);
|
||||
~QAudioEnginePlugin();
|
||||
|
||||
virtual QStringList keys() const = 0;
|
||||
virtual QList<QByteArray> availableDevices(QAudio::Mode) const = 0;
|
||||
virtual QAbstractAudioInput* createInput(const QByteArray& device, const QAudioFormat& format = QAudioFormat()) = 0;
|
||||
virtual QAbstractAudioOutput* createOutput(const QByteArray& device, const QAudioFormat& format = QAudioFormat()) = 0;
|
||||
virtual QAbstractAudioDeviceInfo* createDeviceInfo(const QByteArray& device, QAudio::Mode mode) = 0;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
QT_END_HEADER
|
||||
|
||||
#endif // QAUDIOENGINEPLUGIN_H
|
||||
407
src/multimedia/audio/qaudioformat.cpp
Normal file
407
src/multimedia/audio/qaudioformat.cpp
Normal file
@@ -0,0 +1,407 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
#include <QDebug>
|
||||
#include <QtMultimedia/qaudioformat.h>
|
||||
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
||||
class QAudioFormatPrivate : public QSharedData
|
||||
{
|
||||
public:
|
||||
QAudioFormatPrivate()
|
||||
{
|
||||
frequency = -1;
|
||||
channels = -1;
|
||||
sampleSize = -1;
|
||||
byteOrder = QAudioFormat::Endian(QSysInfo::ByteOrder);
|
||||
sampleType = QAudioFormat::Unknown;
|
||||
}
|
||||
|
||||
QAudioFormatPrivate(const QAudioFormatPrivate &other):
|
||||
QSharedData(other),
|
||||
codec(other.codec),
|
||||
byteOrder(other.byteOrder),
|
||||
sampleType(other.sampleType),
|
||||
frequency(other.frequency),
|
||||
channels(other.channels),
|
||||
sampleSize(other.sampleSize)
|
||||
{
|
||||
}
|
||||
|
||||
QAudioFormatPrivate& operator=(const QAudioFormatPrivate &other)
|
||||
{
|
||||
codec = other.codec;
|
||||
byteOrder = other.byteOrder;
|
||||
sampleType = other.sampleType;
|
||||
frequency = other.frequency;
|
||||
channels = other.channels;
|
||||
sampleSize = other.sampleSize;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
QString codec;
|
||||
QAudioFormat::Endian byteOrder;
|
||||
QAudioFormat::SampleType sampleType;
|
||||
int frequency;
|
||||
int channels;
|
||||
int sampleSize;
|
||||
};
|
||||
|
||||
/*!
|
||||
\class QAudioFormat
|
||||
\brief The QAudioFormat class stores audio parameter information.
|
||||
|
||||
\inmodule QtMultimedia
|
||||
\ingroup multimedia
|
||||
\since 4.6
|
||||
|
||||
An audio format specifies how data in an audio stream is arranged,
|
||||
i.e, how the stream is to be interpreted. The encoding itself is
|
||||
specified by the codec() used for the stream.
|
||||
|
||||
In addition to the encoding, QAudioFormat contains other
|
||||
parameters that further specify how the audio data is arranged.
|
||||
These are the frequency, the number of channels, the sample size,
|
||||
the sample type, and the byte order. The following table describes
|
||||
these in more detail.
|
||||
|
||||
\table
|
||||
\header
|
||||
\o Parameter
|
||||
\o Description
|
||||
\row
|
||||
\o Sample Rate
|
||||
\o Samples per second of audio data in Hertz.
|
||||
\row
|
||||
\o Number of channels
|
||||
\o The number of audio channels (typically one for mono
|
||||
or two for stereo)
|
||||
\row
|
||||
\o Sample size
|
||||
\o How much data is stored in each sample (typically 8
|
||||
or 16 bits)
|
||||
\row
|
||||
\o Sample type
|
||||
\o Numerical representation of sample (typically signed integer,
|
||||
unsigned integer or float)
|
||||
\row
|
||||
\o Byte order
|
||||
\o Byte ordering of sample (typically little endian, big endian)
|
||||
\endtable
|
||||
|
||||
You can obtain audio formats compatible with the audio device used
|
||||
through functions in QAudioDeviceInfo. This class also lets you
|
||||
query available parameter values for a device, so that you can set
|
||||
the parameters yourself. See the QAudioDeviceInfo class
|
||||
description for details. You need to know the format of the audio
|
||||
streams you wish to play. Qt does not set up formats for you.
|
||||
*/
|
||||
|
||||
/*!
|
||||
Construct a new audio format.
|
||||
|
||||
Values are initialized as follows:
|
||||
\list
|
||||
\o sampleRate() = -1
|
||||
\o channelCount() = -1
|
||||
\o sampleSize() = -1
|
||||
\o byteOrder() = QAudioFormat::Endian(QSysInfo::ByteOrder)
|
||||
\o sampleType() = QAudioFormat::Unknown
|
||||
\c codec() = ""
|
||||
\endlist
|
||||
*/
|
||||
|
||||
QAudioFormat::QAudioFormat():
|
||||
d(new QAudioFormatPrivate)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Construct a new audio format using \a other.
|
||||
*/
|
||||
|
||||
QAudioFormat::QAudioFormat(const QAudioFormat &other):
|
||||
d(other.d)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Destroy this audio format.
|
||||
*/
|
||||
|
||||
QAudioFormat::~QAudioFormat()
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Assigns \a other to this QAudioFormat implementation.
|
||||
*/
|
||||
|
||||
QAudioFormat& QAudioFormat::operator=(const QAudioFormat &other)
|
||||
{
|
||||
d = other.d;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns true if this QAudioFormat is equal to the \a other
|
||||
QAudioFormat; otherwise returns false.
|
||||
|
||||
All elements of QAudioFormat are used for the comparison.
|
||||
*/
|
||||
|
||||
bool QAudioFormat::operator==(const QAudioFormat &other) const
|
||||
{
|
||||
return d->frequency == other.d->frequency &&
|
||||
d->channels == other.d->channels &&
|
||||
d->sampleSize == other.d->sampleSize &&
|
||||
d->byteOrder == other.d->byteOrder &&
|
||||
d->codec == other.d->codec &&
|
||||
d->sampleType == other.d->sampleType;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns true if this QAudioFormat is not equal to the \a other
|
||||
QAudioFormat; otherwise returns false.
|
||||
|
||||
All elements of QAudioFormat are used for the comparison.
|
||||
*/
|
||||
|
||||
bool QAudioFormat::operator!=(const QAudioFormat& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns true if all of the parameters are valid.
|
||||
*/
|
||||
|
||||
bool QAudioFormat::isValid() const
|
||||
{
|
||||
return d->frequency != -1 && d->channels != -1 && d->sampleSize != -1 &&
|
||||
d->sampleType != QAudioFormat::Unknown && !d->codec.isEmpty();
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the sample rate to \a samplerate Hertz.
|
||||
|
||||
\since 4.7
|
||||
*/
|
||||
|
||||
void QAudioFormat::setSampleRate(int samplerate)
|
||||
{
|
||||
d->frequency = samplerate;
|
||||
}
|
||||
|
||||
/*!
|
||||
\obsolete
|
||||
|
||||
Use setSampleRate() instead.
|
||||
*/
|
||||
|
||||
void QAudioFormat::setFrequency(int frequency)
|
||||
{
|
||||
d->frequency = frequency;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the current sample rate in Hertz.
|
||||
|
||||
\since 4.7
|
||||
*/
|
||||
|
||||
int QAudioFormat::sampleRate() const
|
||||
{
|
||||
return d->frequency;
|
||||
}
|
||||
|
||||
/*!
|
||||
\obsolete
|
||||
|
||||
Use sampleRate() instead.
|
||||
*/
|
||||
|
||||
int QAudioFormat::frequency() const
|
||||
{
|
||||
return d->frequency;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the channel count to \a channels.
|
||||
|
||||
\since 4.7
|
||||
*/
|
||||
|
||||
void QAudioFormat::setChannelCount(int channels)
|
||||
{
|
||||
d->channels = channels;
|
||||
}
|
||||
|
||||
/*!
|
||||
\obsolete
|
||||
|
||||
Use setChannelCount() instead.
|
||||
*/
|
||||
|
||||
void QAudioFormat::setChannels(int channels)
|
||||
{
|
||||
d->channels = channels;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the current channel count value.
|
||||
|
||||
\since 4.7
|
||||
*/
|
||||
|
||||
int QAudioFormat::channelCount() const
|
||||
{
|
||||
return d->channels;
|
||||
}
|
||||
|
||||
/*!
|
||||
\obsolete
|
||||
|
||||
Use channelCount() instead.
|
||||
*/
|
||||
|
||||
int QAudioFormat::channels() const
|
||||
{
|
||||
return d->channels;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the sample size to the \a sampleSize specified.
|
||||
*/
|
||||
|
||||
void QAudioFormat::setSampleSize(int sampleSize)
|
||||
{
|
||||
d->sampleSize = sampleSize;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the current sample size value.
|
||||
*/
|
||||
|
||||
int QAudioFormat::sampleSize() const
|
||||
{
|
||||
return d->sampleSize;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the codec to \a codec.
|
||||
|
||||
\sa QAudioDeviceInfo::supportedCodecs()
|
||||
*/
|
||||
|
||||
void QAudioFormat::setCodec(const QString &codec)
|
||||
{
|
||||
d->codec = codec;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the current codec value.
|
||||
|
||||
\sa QAudioDeviceInfo::supportedCodecs()
|
||||
*/
|
||||
|
||||
QString QAudioFormat::codec() const
|
||||
{
|
||||
return d->codec;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the byteOrder to \a byteOrder.
|
||||
*/
|
||||
|
||||
void QAudioFormat::setByteOrder(QAudioFormat::Endian byteOrder)
|
||||
{
|
||||
d->byteOrder = byteOrder;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the current byteOrder value.
|
||||
*/
|
||||
|
||||
QAudioFormat::Endian QAudioFormat::byteOrder() const
|
||||
{
|
||||
return d->byteOrder;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the sampleType to \a sampleType.
|
||||
*/
|
||||
|
||||
void QAudioFormat::setSampleType(QAudioFormat::SampleType sampleType)
|
||||
{
|
||||
d->sampleType = sampleType;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the current SampleType value.
|
||||
*/
|
||||
|
||||
QAudioFormat::SampleType QAudioFormat::sampleType() const
|
||||
{
|
||||
return d->sampleType;
|
||||
}
|
||||
|
||||
/*!
|
||||
\enum QAudioFormat::SampleType
|
||||
|
||||
\value Unknown Not Set
|
||||
\value SignedInt samples are signed integers
|
||||
\value UnSignedInt samples are unsigned intergers
|
||||
\value Float samples are floats
|
||||
*/
|
||||
|
||||
/*!
|
||||
\enum QAudioFormat::Endian
|
||||
|
||||
\value BigEndian samples are big endian byte order
|
||||
\value LittleEndian samples are little endian byte order
|
||||
*/
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
107
src/multimedia/audio/qaudioformat.h
Normal file
107
src/multimedia/audio/qaudioformat.h
Normal file
@@ -0,0 +1,107 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#ifndef QAUDIOFORMAT_H
|
||||
#define QAUDIOFORMAT_H
|
||||
|
||||
#include <QtCore/qobject.h>
|
||||
#include <QtCore/qglobal.h>
|
||||
#include <QtCore/qshareddata.h>
|
||||
|
||||
|
||||
QT_BEGIN_HEADER
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QT_MODULE(Multimedia)
|
||||
|
||||
|
||||
class QAudioFormatPrivate;
|
||||
|
||||
class Q_MULTIMEDIA_EXPORT QAudioFormat
|
||||
{
|
||||
public:
|
||||
enum SampleType { Unknown, SignedInt, UnSignedInt, Float };
|
||||
enum Endian { BigEndian = QSysInfo::BigEndian, LittleEndian = QSysInfo::LittleEndian };
|
||||
|
||||
QAudioFormat();
|
||||
QAudioFormat(const QAudioFormat &other);
|
||||
~QAudioFormat();
|
||||
|
||||
QAudioFormat& operator=(const QAudioFormat &other);
|
||||
bool operator==(const QAudioFormat &other) const;
|
||||
bool operator!=(const QAudioFormat &other) const;
|
||||
|
||||
bool isValid() const;
|
||||
|
||||
void setFrequency(int frequency);
|
||||
int frequency() const;
|
||||
void setSampleRate(int sampleRate);
|
||||
int sampleRate() const;
|
||||
|
||||
void setChannels(int channels);
|
||||
int channels() const;
|
||||
void setChannelCount(int channelCount);
|
||||
int channelCount() const;
|
||||
|
||||
void setSampleSize(int sampleSize);
|
||||
int sampleSize() const;
|
||||
|
||||
void setCodec(const QString &codec);
|
||||
QString codec() const;
|
||||
|
||||
void setByteOrder(QAudioFormat::Endian byteOrder);
|
||||
QAudioFormat::Endian byteOrder() const;
|
||||
|
||||
void setSampleType(QAudioFormat::SampleType sampleType);
|
||||
QAudioFormat::SampleType sampleType() const;
|
||||
|
||||
private:
|
||||
QSharedDataPointer<QAudioFormatPrivate> d;
|
||||
};
|
||||
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
QT_END_HEADER
|
||||
|
||||
#endif // QAUDIOFORMAT_H
|
||||
441
src/multimedia/audio/qaudioinput.cpp
Normal file
441
src/multimedia/audio/qaudioinput.cpp
Normal file
@@ -0,0 +1,441 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#include <QtMultimedia/qaudio.h>
|
||||
#include <QtMultimedia/qaudiodeviceinfo.h>
|
||||
#include <QtMultimedia/qaudioengine.h>
|
||||
#include <QtMultimedia/qaudioinput.h>
|
||||
|
||||
#include "qaudiodevicefactory_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
/*!
|
||||
\class QAudioInput
|
||||
\brief The QAudioInput class provides an interface for receiving audio data from an audio input device.
|
||||
|
||||
\inmodule QtMultimedia
|
||||
\ingroup multimedia
|
||||
\since 4.6
|
||||
|
||||
You can construct an audio input with the system's
|
||||
\l{QAudioDeviceInfo::defaultInputDevice()}{default audio input
|
||||
device}. It is also possible to create QAudioInput with a
|
||||
specific QAudioDeviceInfo. When you create the audio input, you
|
||||
should also send in the QAudioFormat to be used for the recording
|
||||
(see the QAudioFormat class description for details).
|
||||
|
||||
To record to a file:
|
||||
|
||||
QAudioInput lets you record audio with an audio input device. The
|
||||
default constructor of this class will use the systems default
|
||||
audio device, but you can also specify a QAudioDeviceInfo for a
|
||||
specific device. You also need to pass in the QAudioFormat in
|
||||
which you wish to record.
|
||||
|
||||
Starting up the QAudioInput is simply a matter of calling start()
|
||||
with a QIODevice opened for writing. For instance, to record to a
|
||||
file, you can:
|
||||
|
||||
\code
|
||||
QFile outputFile; // class member.
|
||||
QAudioInput* audio; // class member.
|
||||
\endcode
|
||||
|
||||
\code
|
||||
{
|
||||
outputFile.setFileName("/tmp/test.raw");
|
||||
outputFile.open( QIODevice::WriteOnly | QIODevice::Truncate );
|
||||
|
||||
QAudioFormat format;
|
||||
// set up the format you want, eg.
|
||||
format.setFrequency(8000);
|
||||
format.setChannels(1);
|
||||
format.setSampleSize(8);
|
||||
format.setCodec("audio/pcm");
|
||||
format.setByteOrder(QAudioFormat::LittleEndian);
|
||||
format.setSampleType(QAudioFormat::UnSignedInt);
|
||||
|
||||
QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice();
|
||||
if (!info.isFormatSupported(format)) {
|
||||
qWarning()<<"default format not supported try to use nearest";
|
||||
format = info.nearestFormat(format);
|
||||
}
|
||||
|
||||
audio = new QAudioInput(format, this);
|
||||
QTimer::singleShot(3000, this, SLOT(stopRecording()));
|
||||
audio->start(&outputFile);
|
||||
// Records audio for 3000ms
|
||||
}
|
||||
\endcode
|
||||
|
||||
This will start recording if the format specified is supported by
|
||||
the input device (you can check this with
|
||||
QAudioDeviceInfo::isFormatSupported(). In case there are any
|
||||
snags, use the error() function to check what went wrong. We stop
|
||||
recording in the \c stopRecording() slot.
|
||||
|
||||
\code
|
||||
void stopRecording()
|
||||
{
|
||||
audio->stop();
|
||||
outputFile->close();
|
||||
delete audio;
|
||||
}
|
||||
\endcode
|
||||
|
||||
At any point in time, QAudioInput will be in one of four states:
|
||||
active, suspended, stopped, or idle. These states are specified by
|
||||
the QAudio::State enum. You can request a state change directly through
|
||||
suspend(), resume(), stop(), reset(), and start(). The current
|
||||
state is reported by state(). QAudioOutput will also signal you
|
||||
when the state changes (stateChanged()).
|
||||
|
||||
QAudioInput provides several ways of measuring the time that has
|
||||
passed since the start() of the recording. The \c processedUSecs()
|
||||
function returns the length of the stream in microseconds written,
|
||||
i.e., it leaves out the times the audio input was suspended or idle.
|
||||
The elapsedUSecs() function returns the time elapsed since start() was called regardless of
|
||||
which states the QAudioInput has been in.
|
||||
|
||||
If an error should occur, you can fetch its reason with error().
|
||||
The possible error reasons are described by the QAudio::Error
|
||||
enum. The QAudioInput will enter the \l{QAudio::}{StoppedState} when
|
||||
an error is encountered. Connect to the stateChanged() signal to
|
||||
handle the error:
|
||||
|
||||
\snippet doc/src/snippets/audio/main.cpp 0
|
||||
|
||||
\sa QAudioOutput, QAudioDeviceInfo
|
||||
|
||||
\section1 Symbian Platform Security Requirements
|
||||
|
||||
On Symbian, processes which use this class must have the
|
||||
\c UserEnvironment platform security capability. If the client
|
||||
process lacks this capability, calls to either overload of start()
|
||||
will fail.
|
||||
This failure is indicated by the QAudioInput object setting
|
||||
its error() value to \l{QAudio::OpenError} and then emitting a
|
||||
\l{stateChanged()}{stateChanged}(\l{QAudio::StoppedState}) signal.
|
||||
|
||||
Platform security capabilities are added via the
|
||||
\l{qmake-variable-reference.html#target-capability}{TARGET.CAPABILITY}
|
||||
qmake variable.
|
||||
*/
|
||||
|
||||
/*!
|
||||
Construct a new audio input and attach it to \a parent.
|
||||
The default audio input device is used with the output
|
||||
\a format parameters.
|
||||
*/
|
||||
|
||||
QAudioInput::QAudioInput(const QAudioFormat &format, QObject *parent):
|
||||
QObject(parent)
|
||||
{
|
||||
d = QAudioDeviceFactory::createDefaultInputDevice(format);
|
||||
connect(d, SIGNAL(notify()), SIGNAL(notify()));
|
||||
connect(d, SIGNAL(stateChanged(QAudio::State)), SIGNAL(stateChanged(QAudio::State)));
|
||||
}
|
||||
|
||||
/*!
|
||||
Construct a new audio input and attach it to \a parent.
|
||||
The device referenced by \a audioDevice is used with the input
|
||||
\a format parameters.
|
||||
*/
|
||||
|
||||
QAudioInput::QAudioInput(const QAudioDeviceInfo &audioDevice, const QAudioFormat &format, QObject *parent):
|
||||
QObject(parent)
|
||||
{
|
||||
d = QAudioDeviceFactory::createInputDevice(audioDevice, format);
|
||||
connect(d, SIGNAL(notify()), SIGNAL(notify()));
|
||||
connect(d, SIGNAL(stateChanged(QAudio::State)), SIGNAL(stateChanged(QAudio::State)));
|
||||
}
|
||||
|
||||
/*!
|
||||
Destroy this audio input.
|
||||
*/
|
||||
|
||||
QAudioInput::~QAudioInput()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
/*!
|
||||
Uses the \a device as the QIODevice to transfer data.
|
||||
Passing a QIODevice allows the data to be transferred without any extra code.
|
||||
All that is required is to open the QIODevice. QAudioInput does not take
|
||||
ownership of \a device.
|
||||
|
||||
The QAudioInput will write to the device when new data is available. You can
|
||||
subclass QIODevice and reimplement \l{QIODevice::}{writeData()} if you wish to
|
||||
access the data. If you simply want to save data to a file, you can pass a
|
||||
QFile to this function.
|
||||
|
||||
If able to successfully get audio data from the systems audio device the
|
||||
state() is set to either QAudio::ActiveState or QAudio::IdleState,
|
||||
error() is set to QAudio::NoError and the stateChanged() signal is emitted.
|
||||
|
||||
If a problem occurs during this process the error() is set to QAudio::OpenError,
|
||||
state() is set to QAudio::StoppedState and stateChanged() signal is emitted.
|
||||
|
||||
\l{QAudioInput#Symbian Platform Security Requirements}
|
||||
|
||||
\sa QIODevice
|
||||
*/
|
||||
|
||||
void QAudioInput::start(QIODevice* device)
|
||||
{
|
||||
d->start(device);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
Returns a pointer to a new QIODevice that will be used to handle the data transfer.
|
||||
This QIODevice can be used to \l{QIODevice::}{read()} audio data directly.
|
||||
You will typically connect to the \l{QIODevice::}{readyRead()} signal, and
|
||||
read from the device in the slot you connect to. QAudioInput keeps ownership
|
||||
of the device.
|
||||
|
||||
If able to access the systems audio device the state() is set to
|
||||
QAudio::IdleState, error() is set to QAudio::NoError
|
||||
and the stateChanged() signal is emitted.
|
||||
|
||||
If a problem occurs during this process the error() is set to QAudio::OpenError,
|
||||
state() is set to QAudio::StoppedState and stateChanged() signal is emitted.
|
||||
|
||||
\l{QAudioInput#Symbian Platform Security Requirements}
|
||||
|
||||
\sa QIODevice
|
||||
*/
|
||||
|
||||
QIODevice* QAudioInput::start()
|
||||
{
|
||||
return d->start(0);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the QAudioFormat being used.
|
||||
*/
|
||||
|
||||
QAudioFormat QAudioInput::format() const
|
||||
{
|
||||
return d->format();
|
||||
}
|
||||
|
||||
/*!
|
||||
Stops the audio input, detaching from the system resource.
|
||||
|
||||
Sets error() to QAudio::NoError, state() to QAudio::StoppedState and
|
||||
emit stateChanged() signal.
|
||||
*/
|
||||
|
||||
void QAudioInput::stop()
|
||||
{
|
||||
d->stop();
|
||||
}
|
||||
|
||||
/*!
|
||||
Drops all audio data in the buffers, resets buffers to zero.
|
||||
*/
|
||||
|
||||
void QAudioInput::reset()
|
||||
{
|
||||
d->reset();
|
||||
}
|
||||
|
||||
/*!
|
||||
Stops processing audio data, preserving buffered audio data.
|
||||
|
||||
Sets error() to QAudio::NoError, state() to QAudio::SuspendedState and
|
||||
emit stateChanged() signal.
|
||||
*/
|
||||
|
||||
void QAudioInput::suspend()
|
||||
{
|
||||
d->suspend();
|
||||
}
|
||||
|
||||
/*!
|
||||
Resumes processing audio data after a suspend().
|
||||
|
||||
Sets error() to QAudio::NoError.
|
||||
Sets state() to QAudio::ActiveState if you previously called start(QIODevice*).
|
||||
Sets state() to QAudio::IdleState if you previously called start().
|
||||
emits stateChanged() signal.
|
||||
*/
|
||||
|
||||
void QAudioInput::resume()
|
||||
{
|
||||
d->resume();
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the audio buffer size to \a value milliseconds.
|
||||
|
||||
Note: This function can be called anytime before start(), calls to this
|
||||
are ignored after start(). It should not be assumed that the buffer size
|
||||
set is the actual buffer size used, calling bufferSize() anytime after start()
|
||||
will return the actual buffer size being used.
|
||||
|
||||
*/
|
||||
|
||||
void QAudioInput::setBufferSize(int value)
|
||||
{
|
||||
d->setBufferSize(value);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the audio buffer size in milliseconds.
|
||||
|
||||
If called before start(), returns platform default value.
|
||||
If called before start() but setBufferSize() was called prior, returns value set by setBufferSize().
|
||||
If called after start(), returns the actual buffer size being used. This may not be what was set previously
|
||||
by setBufferSize().
|
||||
|
||||
*/
|
||||
|
||||
int QAudioInput::bufferSize() const
|
||||
{
|
||||
return d->bufferSize();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the amount of audio data available to read in bytes.
|
||||
|
||||
NOTE: returned value is only valid while in QAudio::ActiveState or QAudio::IdleState
|
||||
state, otherwise returns zero.
|
||||
*/
|
||||
|
||||
int QAudioInput::bytesReady() const
|
||||
{
|
||||
/*
|
||||
-If not ActiveState|IdleState, return 0
|
||||
-return amount of audio data available to read
|
||||
*/
|
||||
return d->bytesReady();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the period size in bytes.
|
||||
|
||||
Note: This is the recommended read size in bytes.
|
||||
*/
|
||||
|
||||
int QAudioInput::periodSize() const
|
||||
{
|
||||
return d->periodSize();
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the interval for notify() signal to be emitted.
|
||||
This is based on the \a ms of audio data processed
|
||||
not on actual real-time.
|
||||
The minimum resolution of the timer is platform specific and values
|
||||
should be checked with notifyInterval() to confirm actual value
|
||||
being used.
|
||||
*/
|
||||
|
||||
void QAudioInput::setNotifyInterval(int ms)
|
||||
{
|
||||
d->setNotifyInterval(ms);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the notify interval in milliseconds.
|
||||
*/
|
||||
|
||||
int QAudioInput::notifyInterval() const
|
||||
{
|
||||
return d->notifyInterval();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the amount of audio data processed since start()
|
||||
was called in microseconds.
|
||||
*/
|
||||
|
||||
qint64 QAudioInput::processedUSecs() const
|
||||
{
|
||||
return d->processedUSecs();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the microseconds since start() was called, including time in Idle and
|
||||
Suspend states.
|
||||
*/
|
||||
|
||||
qint64 QAudioInput::elapsedUSecs() const
|
||||
{
|
||||
return d->elapsedUSecs();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the error state.
|
||||
*/
|
||||
|
||||
QAudio::Error QAudioInput::error() const
|
||||
{
|
||||
return d->error();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the state of audio processing.
|
||||
*/
|
||||
|
||||
QAudio::State QAudioInput::state() const
|
||||
{
|
||||
return d->state();
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QAudioInput::stateChanged(QAudio::State state)
|
||||
This signal is emitted when the device \a state has changed.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QAudioInput::notify()
|
||||
This signal is emitted when x ms of audio data has been processed
|
||||
the interval set by setNotifyInterval(x).
|
||||
*/
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
111
src/multimedia/audio/qaudioinput.h
Normal file
111
src/multimedia/audio/qaudioinput.h
Normal file
@@ -0,0 +1,111 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#ifndef QAUDIOINPUT_H
|
||||
#define QAUDIOINPUT_H
|
||||
|
||||
#include <QtCore/qiodevice.h>
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
#include <QtMultimedia/qaudio.h>
|
||||
#include <QtMultimedia/qaudioformat.h>
|
||||
#include <QtMultimedia/qaudiodeviceinfo.h>
|
||||
|
||||
|
||||
QT_BEGIN_HEADER
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QT_MODULE(Multimedia)
|
||||
|
||||
|
||||
class QAbstractAudioInput;
|
||||
|
||||
class Q_MULTIMEDIA_EXPORT QAudioInput : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QAudioInput(const QAudioFormat &format = QAudioFormat(), QObject *parent = 0);
|
||||
explicit QAudioInput(const QAudioDeviceInfo &audioDeviceInfo, const QAudioFormat &format = QAudioFormat(), QObject *parent = 0);
|
||||
~QAudioInput();
|
||||
|
||||
QAudioFormat format() const;
|
||||
|
||||
void start(QIODevice *device);
|
||||
QIODevice* start();
|
||||
|
||||
void stop();
|
||||
void reset();
|
||||
void suspend();
|
||||
void resume();
|
||||
|
||||
void setBufferSize(int bytes);
|
||||
int bufferSize() const;
|
||||
|
||||
int bytesReady() const;
|
||||
int periodSize() const;
|
||||
|
||||
void setNotifyInterval(int milliSeconds);
|
||||
int notifyInterval() const;
|
||||
|
||||
qint64 processedUSecs() const;
|
||||
qint64 elapsedUSecs() const;
|
||||
|
||||
QAudio::Error error() const;
|
||||
QAudio::State state() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void stateChanged(QAudio::State);
|
||||
void notify();
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(QAudioInput)
|
||||
|
||||
QAbstractAudioInput* d;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
QT_END_HEADER
|
||||
|
||||
#endif // QAUDIOINPUT_H
|
||||
746
src/multimedia/audio/qaudioinput_alsa_p.cpp
Normal file
746
src/multimedia/audio/qaudioinput_alsa_p.cpp
Normal file
@@ -0,0 +1,746 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $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.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include <QtCore/qcoreapplication.h>
|
||||
#include "qaudioinput_alsa_p.h"
|
||||
#include "qaudiodeviceinfo_alsa_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
//#define DEBUG_AUDIO 1
|
||||
|
||||
QAudioInputPrivate::QAudioInputPrivate(const QByteArray &device, const QAudioFormat& audioFormat):
|
||||
settings(audioFormat)
|
||||
{
|
||||
bytesAvailable = 0;
|
||||
handle = 0;
|
||||
ahandler = 0;
|
||||
access = SND_PCM_ACCESS_RW_INTERLEAVED;
|
||||
pcmformat = SND_PCM_FORMAT_S16;
|
||||
buffer_size = 0;
|
||||
period_size = 0;
|
||||
buffer_time = 100000;
|
||||
period_time = 20000;
|
||||
totalTimeValue = 0;
|
||||
intervalTime = 1000;
|
||||
audioBuffer = 0;
|
||||
errorState = QAudio::NoError;
|
||||
deviceState = QAudio::StoppedState;
|
||||
audioSource = 0;
|
||||
pullMode = true;
|
||||
resuming = false;
|
||||
|
||||
m_device = device;
|
||||
|
||||
timer = new QTimer(this);
|
||||
connect(timer,SIGNAL(timeout()),SLOT(userFeed()));
|
||||
}
|
||||
|
||||
QAudioInputPrivate::~QAudioInputPrivate()
|
||||
{
|
||||
close();
|
||||
disconnect(timer, SIGNAL(timeout()));
|
||||
QCoreApplication::processEvents();
|
||||
delete timer;
|
||||
}
|
||||
|
||||
QAudio::Error QAudioInputPrivate::error() const
|
||||
{
|
||||
return errorState;
|
||||
}
|
||||
|
||||
QAudio::State QAudioInputPrivate::state() const
|
||||
{
|
||||
return deviceState;
|
||||
}
|
||||
|
||||
|
||||
QAudioFormat QAudioInputPrivate::format() const
|
||||
{
|
||||
return settings;
|
||||
}
|
||||
|
||||
int QAudioInputPrivate::xrun_recovery(int err)
|
||||
{
|
||||
int count = 0;
|
||||
bool reset = false;
|
||||
|
||||
if(err == -EPIPE) {
|
||||
errorState = QAudio::UnderrunError;
|
||||
err = snd_pcm_prepare(handle);
|
||||
if(err < 0)
|
||||
reset = true;
|
||||
else {
|
||||
bytesAvailable = checkBytesReady();
|
||||
if (bytesAvailable <= 0)
|
||||
reset = true;
|
||||
}
|
||||
|
||||
} else if((err == -ESTRPIPE)||(err == -EIO)) {
|
||||
errorState = QAudio::IOError;
|
||||
while((err = snd_pcm_resume(handle)) == -EAGAIN){
|
||||
usleep(100);
|
||||
count++;
|
||||
if(count > 5) {
|
||||
reset = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(err < 0) {
|
||||
err = snd_pcm_prepare(handle);
|
||||
if(err < 0)
|
||||
reset = true;
|
||||
}
|
||||
}
|
||||
if(reset) {
|
||||
close();
|
||||
open();
|
||||
snd_pcm_prepare(handle);
|
||||
return 0;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int QAudioInputPrivate::setFormat()
|
||||
{
|
||||
snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN;
|
||||
|
||||
if(settings.sampleSize() == 8) {
|
||||
format = SND_PCM_FORMAT_U8;
|
||||
} else if(settings.sampleSize() == 16) {
|
||||
if(settings.sampleType() == QAudioFormat::SignedInt) {
|
||||
if(settings.byteOrder() == QAudioFormat::LittleEndian)
|
||||
format = SND_PCM_FORMAT_S16_LE;
|
||||
else
|
||||
format = SND_PCM_FORMAT_S16_BE;
|
||||
} else if(settings.sampleType() == QAudioFormat::UnSignedInt) {
|
||||
if(settings.byteOrder() == QAudioFormat::LittleEndian)
|
||||
format = SND_PCM_FORMAT_U16_LE;
|
||||
else
|
||||
format = SND_PCM_FORMAT_U16_BE;
|
||||
}
|
||||
} else if(settings.sampleSize() == 24) {
|
||||
if(settings.sampleType() == QAudioFormat::SignedInt) {
|
||||
if(settings.byteOrder() == QAudioFormat::LittleEndian)
|
||||
format = SND_PCM_FORMAT_S24_LE;
|
||||
else
|
||||
format = SND_PCM_FORMAT_S24_BE;
|
||||
} else if(settings.sampleType() == QAudioFormat::UnSignedInt) {
|
||||
if(settings.byteOrder() == QAudioFormat::LittleEndian)
|
||||
format = SND_PCM_FORMAT_U24_LE;
|
||||
else
|
||||
format = SND_PCM_FORMAT_U24_BE;
|
||||
}
|
||||
} else if(settings.sampleSize() == 32) {
|
||||
if(settings.sampleType() == QAudioFormat::SignedInt) {
|
||||
if(settings.byteOrder() == QAudioFormat::LittleEndian)
|
||||
format = SND_PCM_FORMAT_S32_LE;
|
||||
else
|
||||
format = SND_PCM_FORMAT_S32_BE;
|
||||
} else if(settings.sampleType() == QAudioFormat::UnSignedInt) {
|
||||
if(settings.byteOrder() == QAudioFormat::LittleEndian)
|
||||
format = SND_PCM_FORMAT_U32_LE;
|
||||
else
|
||||
format = SND_PCM_FORMAT_U32_BE;
|
||||
} else if(settings.sampleType() == QAudioFormat::Float) {
|
||||
if(settings.byteOrder() == QAudioFormat::LittleEndian)
|
||||
format = SND_PCM_FORMAT_FLOAT_LE;
|
||||
else
|
||||
format = SND_PCM_FORMAT_FLOAT_BE;
|
||||
}
|
||||
} else if(settings.sampleSize() == 64) {
|
||||
if(settings.byteOrder() == QAudioFormat::LittleEndian)
|
||||
format = SND_PCM_FORMAT_FLOAT64_LE;
|
||||
else
|
||||
format = SND_PCM_FORMAT_FLOAT64_BE;
|
||||
}
|
||||
|
||||
return format != SND_PCM_FORMAT_UNKNOWN
|
||||
? snd_pcm_hw_params_set_format( handle, hwparams, format)
|
||||
: -1;
|
||||
}
|
||||
|
||||
QIODevice* QAudioInputPrivate::start(QIODevice* device)
|
||||
{
|
||||
if(deviceState != QAudio::StoppedState)
|
||||
close();
|
||||
|
||||
if(!pullMode && audioSource) {
|
||||
delete audioSource;
|
||||
}
|
||||
|
||||
if(device) {
|
||||
//set to pull mode
|
||||
pullMode = true;
|
||||
audioSource = device;
|
||||
deviceState = QAudio::ActiveState;
|
||||
} else {
|
||||
//set to push mode
|
||||
pullMode = false;
|
||||
deviceState = QAudio::IdleState;
|
||||
audioSource = new InputPrivate(this);
|
||||
audioSource->open(QIODevice::ReadOnly | QIODevice::Unbuffered);
|
||||
}
|
||||
|
||||
if( !open() )
|
||||
return 0;
|
||||
|
||||
emit stateChanged(deviceState);
|
||||
|
||||
return audioSource;
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::stop()
|
||||
{
|
||||
if(deviceState == QAudio::StoppedState)
|
||||
return;
|
||||
|
||||
deviceState = QAudio::StoppedState;
|
||||
|
||||
close();
|
||||
emit stateChanged(deviceState);
|
||||
}
|
||||
|
||||
bool QAudioInputPrivate::open()
|
||||
{
|
||||
#ifdef DEBUG_AUDIO
|
||||
QTime now(QTime::currentTime());
|
||||
qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()";
|
||||
#endif
|
||||
clockStamp.restart();
|
||||
timeStamp.restart();
|
||||
elapsedTimeOffset = 0;
|
||||
|
||||
int dir;
|
||||
int err = 0;
|
||||
int count=0;
|
||||
unsigned int freakuency=settings.frequency();
|
||||
|
||||
if (!settings.isValid()) {
|
||||
qWarning("QAudioOutput: open error, invalid format.");
|
||||
} else if (settings.frequency() <= 0) {
|
||||
qWarning("QAudioOutput: open error, invalid sample rate (%d).",
|
||||
settings.frequency());
|
||||
} else {
|
||||
err = -1;
|
||||
}
|
||||
|
||||
if (err == 0) {
|
||||
errorState = QAudio::OpenError;
|
||||
deviceState = QAudio::StoppedState;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
QString dev = QString(QLatin1String(m_device.constData()));
|
||||
QList<QByteArray> devices = QAudioDeviceInfoInternal::availableDevices(QAudio::AudioInput);
|
||||
if(dev.compare(QLatin1String("default")) == 0) {
|
||||
#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
|
||||
dev = QLatin1String(devices.first());
|
||||
#else
|
||||
dev = QLatin1String("hw:0,0");
|
||||
#endif
|
||||
} else {
|
||||
#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
|
||||
dev = QLatin1String(m_device);
|
||||
#else
|
||||
int idx = 0;
|
||||
char *name;
|
||||
|
||||
QString shortName = QLatin1String(m_device.mid(m_device.indexOf('=',0)+1).constData());
|
||||
|
||||
while(snd_card_get_name(idx,&name) == 0) {
|
||||
if(qstrncmp(shortName.toLocal8Bit().constData(),name,shortName.length()) == 0)
|
||||
break;
|
||||
idx++;
|
||||
}
|
||||
dev = QString(QLatin1String("hw:%1,0")).arg(idx);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Step 1: try and open the device
|
||||
while((count < 5) && (err < 0)) {
|
||||
err=snd_pcm_open(&handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_CAPTURE,0);
|
||||
if(err < 0)
|
||||
count++;
|
||||
}
|
||||
if (( err < 0)||(handle == 0)) {
|
||||
errorState = QAudio::OpenError;
|
||||
deviceState = QAudio::StoppedState;
|
||||
emit stateChanged(deviceState);
|
||||
return false;
|
||||
}
|
||||
snd_pcm_nonblock( handle, 0 );
|
||||
|
||||
// Step 2: Set the desired HW parameters.
|
||||
snd_pcm_hw_params_alloca( &hwparams );
|
||||
|
||||
bool fatal = false;
|
||||
QString errMessage;
|
||||
unsigned int chunks = 8;
|
||||
|
||||
err = snd_pcm_hw_params_any( handle, hwparams );
|
||||
if ( err < 0 ) {
|
||||
fatal = true;
|
||||
errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_any: err = %1").arg(err);
|
||||
}
|
||||
if ( !fatal ) {
|
||||
err = snd_pcm_hw_params_set_rate_resample( handle, hwparams, 1 );
|
||||
if ( err < 0 ) {
|
||||
fatal = true;
|
||||
errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_rate_resample: err = %1").arg(err);
|
||||
}
|
||||
}
|
||||
if ( !fatal ) {
|
||||
err = snd_pcm_hw_params_set_access( handle, hwparams, access );
|
||||
if ( err < 0 ) {
|
||||
fatal = true;
|
||||
errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_access: err = %1").arg(err);
|
||||
}
|
||||
}
|
||||
if ( !fatal ) {
|
||||
err = setFormat();
|
||||
if ( err < 0 ) {
|
||||
fatal = true;
|
||||
errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_format: err = %1").arg(err);
|
||||
}
|
||||
}
|
||||
if ( !fatal ) {
|
||||
err = snd_pcm_hw_params_set_channels( handle, hwparams, (unsigned int)settings.channels() );
|
||||
if ( err < 0 ) {
|
||||
fatal = true;
|
||||
errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_channels: err = %1").arg(err);
|
||||
}
|
||||
}
|
||||
if ( !fatal ) {
|
||||
err = snd_pcm_hw_params_set_rate_near( handle, hwparams, &freakuency, 0 );
|
||||
if ( err < 0 ) {
|
||||
fatal = true;
|
||||
errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_rate_near: err = %1").arg(err);
|
||||
}
|
||||
}
|
||||
if ( !fatal ) {
|
||||
err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, &dir);
|
||||
if ( err < 0 ) {
|
||||
fatal = true;
|
||||
errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_buffer_time_near: err = %1").arg(err);
|
||||
}
|
||||
}
|
||||
if ( !fatal ) {
|
||||
err = snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, &dir);
|
||||
if ( err < 0 ) {
|
||||
fatal = true;
|
||||
errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_period_time_near: err = %1").arg(err);
|
||||
}
|
||||
}
|
||||
if ( !fatal ) {
|
||||
err = snd_pcm_hw_params_set_periods_near(handle, hwparams, &chunks, &dir);
|
||||
if ( err < 0 ) {
|
||||
fatal = true;
|
||||
errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_periods_near: err = %1").arg(err);
|
||||
}
|
||||
}
|
||||
if ( !fatal ) {
|
||||
err = snd_pcm_hw_params(handle, hwparams);
|
||||
if ( err < 0 ) {
|
||||
fatal = true;
|
||||
errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params: err = %1").arg(err);
|
||||
}
|
||||
}
|
||||
if( err < 0) {
|
||||
qWarning()<<errMessage;
|
||||
errorState = QAudio::OpenError;
|
||||
deviceState = QAudio::StoppedState;
|
||||
emit stateChanged(deviceState);
|
||||
return false;
|
||||
}
|
||||
snd_pcm_hw_params_get_buffer_size(hwparams,&buffer_frames);
|
||||
buffer_size = snd_pcm_frames_to_bytes(handle,buffer_frames);
|
||||
snd_pcm_hw_params_get_period_size(hwparams,&period_frames, &dir);
|
||||
period_size = snd_pcm_frames_to_bytes(handle,period_frames);
|
||||
snd_pcm_hw_params_get_buffer_time(hwparams,&buffer_time, &dir);
|
||||
snd_pcm_hw_params_get_period_time(hwparams,&period_time, &dir);
|
||||
|
||||
// Step 3: Set the desired SW parameters.
|
||||
snd_pcm_sw_params_t *swparams;
|
||||
snd_pcm_sw_params_alloca(&swparams);
|
||||
snd_pcm_sw_params_current(handle, swparams);
|
||||
snd_pcm_sw_params_set_start_threshold(handle,swparams,period_frames);
|
||||
snd_pcm_sw_params_set_stop_threshold(handle,swparams,buffer_frames);
|
||||
snd_pcm_sw_params_set_avail_min(handle, swparams,period_frames);
|
||||
snd_pcm_sw_params(handle, swparams);
|
||||
|
||||
// Step 4: Prepare audio
|
||||
if(audioBuffer == 0)
|
||||
audioBuffer = new char[buffer_size];
|
||||
snd_pcm_prepare( handle );
|
||||
snd_pcm_start(handle);
|
||||
|
||||
// Step 5: Setup timer
|
||||
bytesAvailable = checkBytesReady();
|
||||
|
||||
if(pullMode)
|
||||
connect(audioSource,SIGNAL(readyRead()),this,SLOT(userFeed()));
|
||||
|
||||
// Step 6: Start audio processing
|
||||
chunks = buffer_size/period_size;
|
||||
timer->start(period_time*chunks/2000);
|
||||
|
||||
errorState = QAudio::NoError;
|
||||
|
||||
totalTimeValue = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::close()
|
||||
{
|
||||
timer->stop();
|
||||
|
||||
if ( handle ) {
|
||||
snd_pcm_drop( handle );
|
||||
snd_pcm_close( handle );
|
||||
handle = 0;
|
||||
delete [] audioBuffer;
|
||||
audioBuffer=0;
|
||||
}
|
||||
}
|
||||
|
||||
int QAudioInputPrivate::checkBytesReady()
|
||||
{
|
||||
if(resuming)
|
||||
bytesAvailable = period_size;
|
||||
else if(deviceState != QAudio::ActiveState
|
||||
&& deviceState != QAudio::IdleState)
|
||||
bytesAvailable = 0;
|
||||
else {
|
||||
int frames = snd_pcm_avail_update(handle);
|
||||
if (frames < 0) {
|
||||
bytesAvailable = frames;
|
||||
} else {
|
||||
if((int)frames > (int)buffer_frames)
|
||||
frames = buffer_frames;
|
||||
bytesAvailable = snd_pcm_frames_to_bytes(handle, frames);
|
||||
}
|
||||
}
|
||||
return bytesAvailable;
|
||||
}
|
||||
|
||||
int QAudioInputPrivate::bytesReady() const
|
||||
{
|
||||
return qMax(bytesAvailable, 0);
|
||||
}
|
||||
|
||||
qint64 QAudioInputPrivate::read(char* data, qint64 len)
|
||||
{
|
||||
// Read in some audio data and write it to QIODevice, pull mode
|
||||
if ( !handle )
|
||||
return 0;
|
||||
|
||||
// bytesAvaiable is saved as a side effect of checkBytesReady().
|
||||
int bytesToRead = checkBytesReady();
|
||||
|
||||
if (bytesToRead < 0) {
|
||||
// bytesAvailable as negative is error code, try to recover from it.
|
||||
xrun_recovery(bytesToRead);
|
||||
bytesToRead = checkBytesReady();
|
||||
if (bytesToRead < 0) {
|
||||
// recovery failed must stop and set error.
|
||||
close();
|
||||
errorState = QAudio::IOError;
|
||||
deviceState = QAudio::StoppedState;
|
||||
emit stateChanged(deviceState);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bytesToRead = qMin<qint64>(len, bytesToRead);
|
||||
bytesToRead -= bytesToRead % period_size;
|
||||
int count=0, err = 0;
|
||||
while(count < 5) {
|
||||
int chunks = bytesToRead/period_size;
|
||||
int frames = chunks*period_frames;
|
||||
if(frames > (int)buffer_frames)
|
||||
frames = buffer_frames;
|
||||
int readFrames = snd_pcm_readi(handle, audioBuffer, frames);
|
||||
if (readFrames >= 0) {
|
||||
err = snd_pcm_frames_to_bytes(handle, readFrames);
|
||||
#ifdef DEBUG_AUDIO
|
||||
qDebug()<<QString::fromLatin1("read in bytes = %1 (frames=%2)").arg(err).arg(readFrames).toLatin1().constData();
|
||||
#endif
|
||||
break;
|
||||
} else if((readFrames == -EAGAIN) || (readFrames == -EINTR)) {
|
||||
errorState = QAudio::IOError;
|
||||
err = 0;
|
||||
break;
|
||||
} else {
|
||||
if(readFrames == -EPIPE) {
|
||||
errorState = QAudio::UnderrunError;
|
||||
err = snd_pcm_prepare(handle);
|
||||
} else if(readFrames == -ESTRPIPE) {
|
||||
err = snd_pcm_prepare(handle);
|
||||
}
|
||||
if(err != 0) break;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
if(err > 0) {
|
||||
// got some send it onward
|
||||
#ifdef DEBUG_AUDIO
|
||||
qDebug()<<"frames to write to QIODevice = "<<
|
||||
snd_pcm_bytes_to_frames( handle, (int)err )<<" ("<<err<<") bytes";
|
||||
#endif
|
||||
if(deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState)
|
||||
return 0;
|
||||
if (pullMode) {
|
||||
qint64 l = audioSource->write(audioBuffer,err);
|
||||
if(l < 0) {
|
||||
close();
|
||||
errorState = QAudio::IOError;
|
||||
deviceState = QAudio::StoppedState;
|
||||
emit stateChanged(deviceState);
|
||||
} else if(l == 0) {
|
||||
if (deviceState != QAudio::IdleState) {
|
||||
errorState = QAudio::NoError;
|
||||
deviceState = QAudio::IdleState;
|
||||
emit stateChanged(deviceState);
|
||||
}
|
||||
} else {
|
||||
bytesAvailable -= err;
|
||||
totalTimeValue += err;
|
||||
resuming = false;
|
||||
if (deviceState != QAudio::ActiveState) {
|
||||
errorState = QAudio::NoError;
|
||||
deviceState = QAudio::ActiveState;
|
||||
emit stateChanged(deviceState);
|
||||
}
|
||||
}
|
||||
return l;
|
||||
|
||||
} else {
|
||||
memcpy(data,audioBuffer,err);
|
||||
bytesAvailable -= err;
|
||||
totalTimeValue += err;
|
||||
resuming = false;
|
||||
if (deviceState != QAudio::ActiveState) {
|
||||
errorState = QAudio::NoError;
|
||||
deviceState = QAudio::ActiveState;
|
||||
emit stateChanged(deviceState);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::resume()
|
||||
{
|
||||
if(deviceState == QAudio::SuspendedState) {
|
||||
int err = 0;
|
||||
|
||||
if(handle) {
|
||||
err = snd_pcm_prepare( handle );
|
||||
if(err < 0)
|
||||
xrun_recovery(err);
|
||||
|
||||
err = snd_pcm_start(handle);
|
||||
if(err < 0)
|
||||
xrun_recovery(err);
|
||||
|
||||
bytesAvailable = buffer_size;
|
||||
}
|
||||
resuming = true;
|
||||
deviceState = QAudio::ActiveState;
|
||||
int chunks = buffer_size/period_size;
|
||||
timer->start(period_time*chunks/2000);
|
||||
emit stateChanged(deviceState);
|
||||
}
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::setBufferSize(int value)
|
||||
{
|
||||
buffer_size = value;
|
||||
}
|
||||
|
||||
int QAudioInputPrivate::bufferSize() const
|
||||
{
|
||||
return buffer_size;
|
||||
}
|
||||
|
||||
int QAudioInputPrivate::periodSize() const
|
||||
{
|
||||
return period_size;
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::setNotifyInterval(int ms)
|
||||
{
|
||||
intervalTime = qMax(0, ms);
|
||||
}
|
||||
|
||||
int QAudioInputPrivate::notifyInterval() const
|
||||
{
|
||||
return intervalTime;
|
||||
}
|
||||
|
||||
qint64 QAudioInputPrivate::processedUSecs() const
|
||||
{
|
||||
qint64 result = qint64(1000000) * totalTimeValue /
|
||||
(settings.channels()*(settings.sampleSize()/8)) /
|
||||
settings.frequency();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::suspend()
|
||||
{
|
||||
if(deviceState == QAudio::ActiveState||resuming) {
|
||||
timer->stop();
|
||||
deviceState = QAudio::SuspendedState;
|
||||
emit stateChanged(deviceState);
|
||||
}
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::userFeed()
|
||||
{
|
||||
if(deviceState == QAudio::StoppedState || deviceState == QAudio::SuspendedState)
|
||||
return;
|
||||
#ifdef DEBUG_AUDIO
|
||||
QTime now(QTime::currentTime());
|
||||
qDebug()<<now.second()<<"s "<<now.msec()<<"ms :userFeed() IN";
|
||||
#endif
|
||||
deviceReady();
|
||||
}
|
||||
|
||||
bool QAudioInputPrivate::deviceReady()
|
||||
{
|
||||
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();
|
||||
}
|
||||
bytesAvailable = checkBytesReady();
|
||||
|
||||
if(deviceState != QAudio::ActiveState)
|
||||
return true;
|
||||
|
||||
if (bytesAvailable < 0) {
|
||||
// bytesAvailable as negative is error code, try to recover from it.
|
||||
xrun_recovery(bytesAvailable);
|
||||
bytesAvailable = checkBytesReady();
|
||||
if (bytesAvailable < 0) {
|
||||
// recovery failed must stop and set error.
|
||||
close();
|
||||
errorState = QAudio::IOError;
|
||||
deviceState = QAudio::StoppedState;
|
||||
emit stateChanged(deviceState);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(intervalTime && (timeStamp.elapsed() + elapsedTimeOffset) > intervalTime) {
|
||||
emit notify();
|
||||
elapsedTimeOffset = timeStamp.elapsed() + elapsedTimeOffset - intervalTime;
|
||||
timeStamp.restart();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
qint64 QAudioInputPrivate::elapsedUSecs() const
|
||||
{
|
||||
if (deviceState == QAudio::StoppedState)
|
||||
return 0;
|
||||
|
||||
return clockStamp.elapsed()*1000;
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::reset()
|
||||
{
|
||||
if(handle)
|
||||
snd_pcm_reset(handle);
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::drain()
|
||||
{
|
||||
if(handle)
|
||||
snd_pcm_drain(handle);
|
||||
}
|
||||
|
||||
InputPrivate::InputPrivate(QAudioInputPrivate* audio)
|
||||
{
|
||||
audioDevice = qobject_cast<QAudioInputPrivate*>(audio);
|
||||
}
|
||||
|
||||
InputPrivate::~InputPrivate()
|
||||
{
|
||||
}
|
||||
|
||||
qint64 InputPrivate::readData( char* data, qint64 len)
|
||||
{
|
||||
return audioDevice->read(data,len);
|
||||
}
|
||||
|
||||
qint64 InputPrivate::writeData(const char* data, qint64 len)
|
||||
{
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void InputPrivate::trigger()
|
||||
{
|
||||
emit readyRead();
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
159
src/multimedia/audio/qaudioinput_alsa_p.h
Normal file
159
src/multimedia/audio/qaudioinput_alsa_p.h
Normal file
@@ -0,0 +1,159 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $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.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
|
||||
#ifndef QAUDIOINPUTALSA_H
|
||||
#define QAUDIOINPUTALSA_H
|
||||
|
||||
#include <alsa/asoundlib.h>
|
||||
|
||||
#include <QtCore/qfile.h>
|
||||
#include <QtCore/qdebug.h>
|
||||
#include <QtCore/qtimer.h>
|
||||
#include <QtCore/qstring.h>
|
||||
#include <QtCore/qstringlist.h>
|
||||
#include <QtCore/qelapsedtimer.h>
|
||||
#include <QtCore/qdatetime.h>
|
||||
|
||||
#include <QtMultimedia/qaudio.h>
|
||||
#include <QtMultimedia/qaudiodeviceinfo.h>
|
||||
#include <QtMultimedia/qaudioengine.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class InputPrivate;
|
||||
|
||||
class QAudioInputPrivate : public QAbstractAudioInput
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QAudioInputPrivate(const QByteArray &device, const QAudioFormat& audioFormat);
|
||||
~QAudioInputPrivate();
|
||||
|
||||
qint64 read(char* data, qint64 len);
|
||||
|
||||
QIODevice* start(QIODevice* device = 0);
|
||||
void stop();
|
||||
void reset();
|
||||
void suspend();
|
||||
void resume();
|
||||
int bytesReady() const;
|
||||
int periodSize() const;
|
||||
void setBufferSize(int value);
|
||||
int bufferSize() const;
|
||||
void setNotifyInterval(int milliSeconds);
|
||||
int notifyInterval() const;
|
||||
qint64 processedUSecs() const;
|
||||
qint64 elapsedUSecs() const;
|
||||
QAudio::Error error() const;
|
||||
QAudio::State state() const;
|
||||
QAudioFormat format() const;
|
||||
bool resuming;
|
||||
snd_pcm_t* handle;
|
||||
qint64 totalTimeValue;
|
||||
QIODevice* audioSource;
|
||||
QAudioFormat settings;
|
||||
QAudio::Error errorState;
|
||||
QAudio::State deviceState;
|
||||
|
||||
private slots:
|
||||
void userFeed();
|
||||
bool deviceReady();
|
||||
|
||||
private:
|
||||
int checkBytesReady();
|
||||
int xrun_recovery(int err);
|
||||
int setFormat();
|
||||
bool open();
|
||||
void close();
|
||||
void drain();
|
||||
|
||||
QTimer* timer;
|
||||
QElapsedTimer timeStamp;
|
||||
QElapsedTimer clockStamp;
|
||||
qint64 elapsedTimeOffset;
|
||||
int intervalTime;
|
||||
char* audioBuffer;
|
||||
int bytesAvailable;
|
||||
QByteArray m_device;
|
||||
bool pullMode;
|
||||
int buffer_size;
|
||||
int period_size;
|
||||
unsigned int buffer_time;
|
||||
unsigned int period_time;
|
||||
snd_pcm_uframes_t buffer_frames;
|
||||
snd_pcm_uframes_t period_frames;
|
||||
snd_async_handler_t* ahandler;
|
||||
snd_pcm_access_t access;
|
||||
snd_pcm_format_t pcmformat;
|
||||
snd_timestamp_t* timestamp;
|
||||
snd_pcm_hw_params_t *hwparams;
|
||||
};
|
||||
|
||||
class InputPrivate : public QIODevice
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
InputPrivate(QAudioInputPrivate* audio);
|
||||
~InputPrivate();
|
||||
|
||||
qint64 readData( char* data, qint64 len);
|
||||
qint64 writeData(const char* data, qint64 len);
|
||||
|
||||
void trigger();
|
||||
private:
|
||||
QAudioInputPrivate *audioDevice;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
957
src/multimedia/audio/qaudioinput_mac_p.cpp
Normal file
957
src/multimedia/audio/qaudioinput_mac_p.cpp
Normal file
@@ -0,0 +1,957 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $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.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include <QtCore/qendian.h>
|
||||
#include <QtCore/qtimer.h>
|
||||
#include <QtCore/qdebug.h>
|
||||
|
||||
#include <QtMultimedia/qaudioinput.h>
|
||||
|
||||
#include "qaudio_mac_p.h"
|
||||
#include "qaudioinput_mac_p.h"
|
||||
#include "qaudiodeviceinfo_mac_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
||||
namespace QtMultimediaInternal
|
||||
{
|
||||
|
||||
static const int default_buffer_size = 4 * 1024;
|
||||
|
||||
class QAudioBufferList
|
||||
{
|
||||
public:
|
||||
QAudioBufferList(AudioStreamBasicDescription const& streamFormat):
|
||||
owner(false),
|
||||
sf(streamFormat)
|
||||
{
|
||||
const bool isInterleaved = (sf.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0;
|
||||
const int numberOfBuffers = isInterleaved ? 1 : sf.mChannelsPerFrame;
|
||||
|
||||
dataSize = 0;
|
||||
|
||||
bfs = reinterpret_cast<AudioBufferList*>(qMalloc(sizeof(AudioBufferList) +
|
||||
(sizeof(AudioBuffer) * numberOfBuffers)));
|
||||
|
||||
bfs->mNumberBuffers = numberOfBuffers;
|
||||
for (int i = 0; i < numberOfBuffers; ++i) {
|
||||
bfs->mBuffers[i].mNumberChannels = isInterleaved ? numberOfBuffers : 1;
|
||||
bfs->mBuffers[i].mDataByteSize = 0;
|
||||
bfs->mBuffers[i].mData = 0;
|
||||
}
|
||||
}
|
||||
|
||||
QAudioBufferList(AudioStreamBasicDescription const& streamFormat, char* buffer, int bufferSize):
|
||||
owner(false),
|
||||
sf(streamFormat),
|
||||
bfs(0)
|
||||
{
|
||||
dataSize = bufferSize;
|
||||
|
||||
bfs = reinterpret_cast<AudioBufferList*>(qMalloc(sizeof(AudioBufferList) + sizeof(AudioBuffer)));
|
||||
|
||||
bfs->mNumberBuffers = 1;
|
||||
bfs->mBuffers[0].mNumberChannels = 1;
|
||||
bfs->mBuffers[0].mDataByteSize = dataSize;
|
||||
bfs->mBuffers[0].mData = buffer;
|
||||
}
|
||||
|
||||
QAudioBufferList(AudioStreamBasicDescription const& streamFormat, int framesToBuffer):
|
||||
owner(true),
|
||||
sf(streamFormat),
|
||||
bfs(0)
|
||||
{
|
||||
const bool isInterleaved = (sf.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0;
|
||||
const int numberOfBuffers = isInterleaved ? 1 : sf.mChannelsPerFrame;
|
||||
|
||||
dataSize = framesToBuffer * sf.mBytesPerFrame;
|
||||
|
||||
bfs = reinterpret_cast<AudioBufferList*>(qMalloc(sizeof(AudioBufferList) +
|
||||
(sizeof(AudioBuffer) * numberOfBuffers)));
|
||||
bfs->mNumberBuffers = numberOfBuffers;
|
||||
for (int i = 0; i < numberOfBuffers; ++i) {
|
||||
bfs->mBuffers[i].mNumberChannels = isInterleaved ? numberOfBuffers : 1;
|
||||
bfs->mBuffers[i].mDataByteSize = dataSize;
|
||||
bfs->mBuffers[i].mData = qMalloc(dataSize);
|
||||
}
|
||||
}
|
||||
|
||||
~QAudioBufferList()
|
||||
{
|
||||
if (owner) {
|
||||
for (UInt32 i = 0; i < bfs->mNumberBuffers; ++i)
|
||||
qFree(bfs->mBuffers[i].mData);
|
||||
}
|
||||
|
||||
qFree(bfs);
|
||||
}
|
||||
|
||||
AudioBufferList* audioBufferList() const
|
||||
{
|
||||
return bfs;
|
||||
}
|
||||
|
||||
char* data(int buffer = 0) const
|
||||
{
|
||||
return static_cast<char*>(bfs->mBuffers[buffer].mData);
|
||||
}
|
||||
|
||||
qint64 bufferSize(int buffer = 0) const
|
||||
{
|
||||
return bfs->mBuffers[buffer].mDataByteSize;
|
||||
}
|
||||
|
||||
int frameCount(int buffer = 0) const
|
||||
{
|
||||
return bfs->mBuffers[buffer].mDataByteSize / sf.mBytesPerFrame;
|
||||
}
|
||||
|
||||
int packetCount(int buffer = 0) const
|
||||
{
|
||||
return bfs->mBuffers[buffer].mDataByteSize / sf.mBytesPerPacket;
|
||||
}
|
||||
|
||||
int packetSize() const
|
||||
{
|
||||
return sf.mBytesPerPacket;
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
for (UInt32 i = 0; i < bfs->mNumberBuffers; ++i) {
|
||||
bfs->mBuffers[i].mDataByteSize = dataSize;
|
||||
bfs->mBuffers[i].mData = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool owner;
|
||||
int dataSize;
|
||||
AudioStreamBasicDescription sf;
|
||||
AudioBufferList* bfs;
|
||||
};
|
||||
|
||||
class QAudioPacketFeeder
|
||||
{
|
||||
public:
|
||||
QAudioPacketFeeder(QAudioBufferList* abl):
|
||||
audioBufferList(abl)
|
||||
{
|
||||
totalPackets = audioBufferList->packetCount();
|
||||
position = 0;
|
||||
}
|
||||
|
||||
bool feed(AudioBufferList& dst, UInt32& packetCount)
|
||||
{
|
||||
if (position == totalPackets) {
|
||||
dst.mBuffers[0].mDataByteSize = 0;
|
||||
packetCount = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (totalPackets - position < packetCount)
|
||||
packetCount = totalPackets - position;
|
||||
|
||||
dst.mBuffers[0].mDataByteSize = packetCount * audioBufferList->packetSize();
|
||||
dst.mBuffers[0].mData = audioBufferList->data() + (position * audioBufferList->packetSize());
|
||||
|
||||
position += packetCount;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return position == totalPackets;
|
||||
}
|
||||
|
||||
private:
|
||||
UInt32 totalPackets;
|
||||
UInt32 position;
|
||||
QAudioBufferList* audioBufferList;
|
||||
};
|
||||
|
||||
class QAudioInputBuffer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QAudioInputBuffer(int bufferSize,
|
||||
int maxPeriodSize,
|
||||
AudioStreamBasicDescription const& inputFormat,
|
||||
AudioStreamBasicDescription const& outputFormat,
|
||||
QObject* parent):
|
||||
QObject(parent),
|
||||
m_deviceError(false),
|
||||
m_audioConverter(0),
|
||||
m_inputFormat(inputFormat),
|
||||
m_outputFormat(outputFormat)
|
||||
{
|
||||
m_maxPeriodSize = maxPeriodSize;
|
||||
m_periodTime = m_maxPeriodSize / m_outputFormat.mBytesPerFrame * 1000 / m_outputFormat.mSampleRate;
|
||||
m_buffer = new QAudioRingBuffer(bufferSize + (bufferSize % maxPeriodSize == 0 ? 0 : maxPeriodSize - (bufferSize % maxPeriodSize)));
|
||||
m_inputBufferList = new QAudioBufferList(m_inputFormat);
|
||||
|
||||
m_flushTimer = new QTimer(this);
|
||||
connect(m_flushTimer, SIGNAL(timeout()), SLOT(flushBuffer()));
|
||||
|
||||
if (toQAudioFormat(inputFormat) != toQAudioFormat(outputFormat)) {
|
||||
if (AudioConverterNew(&m_inputFormat, &m_outputFormat, &m_audioConverter) != noErr) {
|
||||
qWarning() << "QAudioInput: Unable to create an Audio Converter";
|
||||
m_audioConverter = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~QAudioInputBuffer()
|
||||
{
|
||||
delete m_buffer;
|
||||
}
|
||||
|
||||
qint64 renderFromDevice(AudioUnit audioUnit,
|
||||
AudioUnitRenderActionFlags* ioActionFlags,
|
||||
const AudioTimeStamp* inTimeStamp,
|
||||
UInt32 inBusNumber,
|
||||
UInt32 inNumberFrames)
|
||||
{
|
||||
const bool pullMode = m_device == 0;
|
||||
|
||||
OSStatus err;
|
||||
qint64 framesRendered = 0;
|
||||
|
||||
m_inputBufferList->reset();
|
||||
err = AudioUnitRender(audioUnit,
|
||||
ioActionFlags,
|
||||
inTimeStamp,
|
||||
inBusNumber,
|
||||
inNumberFrames,
|
||||
m_inputBufferList->audioBufferList());
|
||||
|
||||
if (m_audioConverter != 0) {
|
||||
QAudioPacketFeeder feeder(m_inputBufferList);
|
||||
|
||||
int copied = 0;
|
||||
const int available = m_buffer->free();
|
||||
|
||||
while (err == noErr && !feeder.empty()) {
|
||||
QAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(available);
|
||||
|
||||
if (region.second == 0)
|
||||
break;
|
||||
|
||||
AudioBufferList output;
|
||||
output.mNumberBuffers = 1;
|
||||
output.mBuffers[0].mNumberChannels = 1;
|
||||
output.mBuffers[0].mDataByteSize = region.second;
|
||||
output.mBuffers[0].mData = region.first;
|
||||
|
||||
UInt32 packetSize = region.second / m_outputFormat.mBytesPerPacket;
|
||||
err = AudioConverterFillComplexBuffer(m_audioConverter,
|
||||
converterCallback,
|
||||
&feeder,
|
||||
&packetSize,
|
||||
&output,
|
||||
0);
|
||||
region.second = output.mBuffers[0].mDataByteSize;
|
||||
copied += region.second;
|
||||
|
||||
m_buffer->releaseWriteRegion(region);
|
||||
}
|
||||
|
||||
framesRendered += copied / m_outputFormat.mBytesPerFrame;
|
||||
}
|
||||
else {
|
||||
const int available = m_inputBufferList->bufferSize();
|
||||
bool wecan = true;
|
||||
int copied = 0;
|
||||
|
||||
while (wecan && copied < available) {
|
||||
QAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(available - copied);
|
||||
|
||||
if (region.second > 0) {
|
||||
memcpy(region.first, m_inputBufferList->data() + copied, region.second);
|
||||
copied += region.second;
|
||||
}
|
||||
else
|
||||
wecan = false;
|
||||
|
||||
m_buffer->releaseWriteRegion(region);
|
||||
}
|
||||
|
||||
framesRendered = copied / m_outputFormat.mBytesPerFrame;
|
||||
}
|
||||
|
||||
if (pullMode && framesRendered > 0)
|
||||
emit readyRead();
|
||||
|
||||
return framesRendered;
|
||||
}
|
||||
|
||||
qint64 readBytes(char* data, qint64 len)
|
||||
{
|
||||
bool wecan = true;
|
||||
qint64 bytesCopied = 0;
|
||||
|
||||
len -= len % m_maxPeriodSize;
|
||||
while (wecan && bytesCopied < len) {
|
||||
QAudioRingBuffer::Region region = m_buffer->acquireReadRegion(len - bytesCopied);
|
||||
|
||||
if (region.second > 0) {
|
||||
memcpy(data + bytesCopied, region.first, region.second);
|
||||
bytesCopied += region.second;
|
||||
}
|
||||
else
|
||||
wecan = false;
|
||||
|
||||
m_buffer->releaseReadRegion(region);
|
||||
}
|
||||
|
||||
return bytesCopied;
|
||||
}
|
||||
|
||||
void setFlushDevice(QIODevice* device)
|
||||
{
|
||||
if (m_device != device)
|
||||
m_device = device;
|
||||
}
|
||||
|
||||
void startFlushTimer()
|
||||
{
|
||||
if (m_device != 0) {
|
||||
m_flushTimer->start((m_buffer->size() - (m_maxPeriodSize * 2)) / m_maxPeriodSize * m_periodTime);
|
||||
}
|
||||
}
|
||||
|
||||
void stopFlushTimer()
|
||||
{
|
||||
m_flushTimer->stop();
|
||||
}
|
||||
|
||||
void flush(bool all = false)
|
||||
{
|
||||
if (m_device == 0)
|
||||
return;
|
||||
|
||||
const int used = m_buffer->used();
|
||||
const int readSize = all ? used : used - (used % m_maxPeriodSize);
|
||||
|
||||
if (readSize > 0) {
|
||||
bool wecan = true;
|
||||
int flushed = 0;
|
||||
|
||||
while (!m_deviceError && wecan && flushed < readSize) {
|
||||
QAudioRingBuffer::Region region = m_buffer->acquireReadRegion(readSize - flushed);
|
||||
|
||||
if (region.second > 0) {
|
||||
int bytesWritten = m_device->write(region.first, region.second);
|
||||
if (bytesWritten < 0) {
|
||||
stopFlushTimer();
|
||||
m_deviceError = true;
|
||||
}
|
||||
else {
|
||||
region.second = bytesWritten;
|
||||
flushed += bytesWritten;
|
||||
wecan = bytesWritten != 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
wecan = false;
|
||||
|
||||
m_buffer->releaseReadRegion(region);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
m_buffer->reset();
|
||||
m_deviceError = false;
|
||||
}
|
||||
|
||||
int available() const
|
||||
{
|
||||
return m_buffer->free();
|
||||
}
|
||||
|
||||
int used() const
|
||||
{
|
||||
return m_buffer->used();
|
||||
}
|
||||
|
||||
signals:
|
||||
void readyRead();
|
||||
|
||||
private slots:
|
||||
void flushBuffer()
|
||||
{
|
||||
flush();
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_deviceError;
|
||||
int m_maxPeriodSize;
|
||||
int m_periodTime;
|
||||
QIODevice* m_device;
|
||||
QTimer* m_flushTimer;
|
||||
QAudioRingBuffer* m_buffer;
|
||||
QAudioBufferList* m_inputBufferList;
|
||||
AudioConverterRef m_audioConverter;
|
||||
AudioStreamBasicDescription m_inputFormat;
|
||||
AudioStreamBasicDescription m_outputFormat;
|
||||
|
||||
const static OSStatus as_empty = 'qtem';
|
||||
|
||||
// Converter callback
|
||||
static OSStatus converterCallback(AudioConverterRef inAudioConverter,
|
||||
UInt32* ioNumberDataPackets,
|
||||
AudioBufferList* ioData,
|
||||
AudioStreamPacketDescription** outDataPacketDescription,
|
||||
void* inUserData)
|
||||
{
|
||||
Q_UNUSED(inAudioConverter);
|
||||
Q_UNUSED(outDataPacketDescription);
|
||||
|
||||
QAudioPacketFeeder* feeder = static_cast<QAudioPacketFeeder*>(inUserData);
|
||||
|
||||
if (!feeder->feed(*ioData, *ioNumberDataPackets))
|
||||
return as_empty;
|
||||
|
||||
return noErr;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class MacInputDevice : public QIODevice
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacInputDevice(QAudioInputBuffer* audioBuffer, QObject* parent):
|
||||
QIODevice(parent),
|
||||
m_audioBuffer(audioBuffer)
|
||||
{
|
||||
open(QIODevice::ReadOnly | QIODevice::Unbuffered);
|
||||
connect(m_audioBuffer, SIGNAL(readyRead()), SIGNAL(readyRead()));
|
||||
}
|
||||
|
||||
qint64 readData(char* data, qint64 len)
|
||||
{
|
||||
return m_audioBuffer->readBytes(data, len);
|
||||
}
|
||||
|
||||
qint64 writeData(const char* data, qint64 len)
|
||||
{
|
||||
Q_UNUSED(data);
|
||||
Q_UNUSED(len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool isSequential() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
QAudioInputBuffer* m_audioBuffer;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
QAudioInputPrivate::QAudioInputPrivate(const QByteArray& device, QAudioFormat const& format):
|
||||
audioFormat(format)
|
||||
{
|
||||
QDataStream ds(device);
|
||||
quint32 did, mode;
|
||||
|
||||
ds >> did >> mode;
|
||||
|
||||
if (QAudio::Mode(mode) == QAudio::AudioOutput)
|
||||
errorCode = QAudio::OpenError;
|
||||
else {
|
||||
audioDeviceInfo = new QAudioDeviceInfoInternal(device, QAudio::AudioInput);
|
||||
isOpen = false;
|
||||
audioDeviceId = AudioDeviceID(did);
|
||||
audioUnit = 0;
|
||||
startTime = 0;
|
||||
totalFrames = 0;
|
||||
audioBuffer = 0;
|
||||
internalBufferSize = QtMultimediaInternal::default_buffer_size;
|
||||
clockFrequency = AudioGetHostClockFrequency() / 1000;
|
||||
errorCode = QAudio::NoError;
|
||||
stateCode = QAudio::StoppedState;
|
||||
|
||||
intervalTimer = new QTimer(this);
|
||||
intervalTimer->setInterval(1000);
|
||||
connect(intervalTimer, SIGNAL(timeout()), SIGNAL(notify()));
|
||||
}
|
||||
}
|
||||
|
||||
QAudioInputPrivate::~QAudioInputPrivate()
|
||||
{
|
||||
close();
|
||||
delete audioDeviceInfo;
|
||||
}
|
||||
|
||||
bool QAudioInputPrivate::open()
|
||||
{
|
||||
UInt32 size = 0;
|
||||
|
||||
if (isOpen)
|
||||
return true;
|
||||
|
||||
ComponentDescription cd;
|
||||
cd.componentType = kAudioUnitType_Output;
|
||||
cd.componentSubType = kAudioUnitSubType_HALOutput;
|
||||
cd.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
cd.componentFlags = 0;
|
||||
cd.componentFlagsMask = 0;
|
||||
|
||||
// Open
|
||||
Component cp = FindNextComponent(NULL, &cd);
|
||||
if (cp == 0) {
|
||||
qWarning() << "QAudioInput: Failed to find HAL Output component";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (OpenAComponent(cp, &audioUnit) != noErr) {
|
||||
qWarning() << "QAudioInput: Unable to Open Output Component";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set mode
|
||||
// switch to input mode
|
||||
UInt32 enable = 1;
|
||||
if (AudioUnitSetProperty(audioUnit,
|
||||
kAudioOutputUnitProperty_EnableIO,
|
||||
kAudioUnitScope_Input,
|
||||
1,
|
||||
&enable,
|
||||
sizeof(enable)) != noErr) {
|
||||
qWarning() << "QAudioInput: Unable to switch to input mode (Enable Input)";
|
||||
return false;
|
||||
}
|
||||
|
||||
enable = 0;
|
||||
if (AudioUnitSetProperty(audioUnit,
|
||||
kAudioOutputUnitProperty_EnableIO,
|
||||
kAudioUnitScope_Output,
|
||||
0,
|
||||
&enable,
|
||||
sizeof(enable)) != noErr) {
|
||||
qWarning() << "QAudioInput: Unable to switch to input mode (Disable output)";
|
||||
return false;
|
||||
}
|
||||
|
||||
// register callback
|
||||
AURenderCallbackStruct cb;
|
||||
cb.inputProc = inputCallback;
|
||||
cb.inputProcRefCon = this;
|
||||
|
||||
if (AudioUnitSetProperty(audioUnit,
|
||||
kAudioOutputUnitProperty_SetInputCallback,
|
||||
kAudioUnitScope_Global,
|
||||
0,
|
||||
&cb,
|
||||
sizeof(cb)) != noErr) {
|
||||
qWarning() << "QAudioInput: Failed to set AudioUnit callback";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set Audio Device
|
||||
if (AudioUnitSetProperty(audioUnit,
|
||||
kAudioOutputUnitProperty_CurrentDevice,
|
||||
kAudioUnitScope_Global,
|
||||
0,
|
||||
&audioDeviceId,
|
||||
sizeof(audioDeviceId)) != noErr) {
|
||||
qWarning() << "QAudioInput: Unable to use configured device";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set format
|
||||
// Wanted
|
||||
streamFormat = toAudioStreamBasicDescription(audioFormat);
|
||||
|
||||
// Required on unit
|
||||
if (audioFormat == audioDeviceInfo->preferredFormat()) {
|
||||
deviceFormat = streamFormat;
|
||||
AudioUnitSetProperty(audioUnit,
|
||||
kAudioUnitProperty_StreamFormat,
|
||||
kAudioUnitScope_Output,
|
||||
1,
|
||||
&deviceFormat,
|
||||
sizeof(deviceFormat));
|
||||
}
|
||||
else {
|
||||
size = sizeof(deviceFormat);
|
||||
if (AudioUnitGetProperty(audioUnit,
|
||||
kAudioUnitProperty_StreamFormat,
|
||||
kAudioUnitScope_Input,
|
||||
1,
|
||||
&deviceFormat,
|
||||
&size) != noErr) {
|
||||
qWarning() << "QAudioInput: Unable to retrieve device format";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (AudioUnitSetProperty(audioUnit,
|
||||
kAudioUnitProperty_StreamFormat,
|
||||
kAudioUnitScope_Output,
|
||||
1,
|
||||
&deviceFormat,
|
||||
sizeof(deviceFormat)) != noErr) {
|
||||
qWarning() << "QAudioInput: Unable to set device format";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Setup buffers
|
||||
UInt32 numberOfFrames;
|
||||
size = sizeof(UInt32);
|
||||
if (AudioUnitGetProperty(audioUnit,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
kAudioUnitScope_Global,
|
||||
0,
|
||||
&numberOfFrames,
|
||||
&size) != noErr) {
|
||||
qWarning() << "QAudioInput: Failed to get audio period size";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allocate buffer
|
||||
periodSizeBytes = numberOfFrames * streamFormat.mBytesPerFrame;
|
||||
|
||||
if (internalBufferSize < periodSizeBytes * 2)
|
||||
internalBufferSize = periodSizeBytes * 2;
|
||||
else
|
||||
internalBufferSize -= internalBufferSize % streamFormat.mBytesPerFrame;
|
||||
|
||||
audioBuffer = new QtMultimediaInternal::QAudioInputBuffer(internalBufferSize,
|
||||
periodSizeBytes,
|
||||
deviceFormat,
|
||||
streamFormat,
|
||||
this);
|
||||
|
||||
audioIO = new QtMultimediaInternal::MacInputDevice(audioBuffer, this);
|
||||
|
||||
// Init
|
||||
if (AudioUnitInitialize(audioUnit) != noErr) {
|
||||
qWarning() << "QAudioInput: Failed to initialize AudioUnit";
|
||||
return false;
|
||||
}
|
||||
|
||||
isOpen = true;
|
||||
|
||||
return isOpen;
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::close()
|
||||
{
|
||||
if (audioUnit != 0) {
|
||||
AudioOutputUnitStop(audioUnit);
|
||||
AudioUnitUninitialize(audioUnit);
|
||||
CloseComponent(audioUnit);
|
||||
}
|
||||
|
||||
delete audioBuffer;
|
||||
}
|
||||
|
||||
QAudioFormat QAudioInputPrivate::format() const
|
||||
{
|
||||
return audioFormat;
|
||||
}
|
||||
|
||||
QIODevice* QAudioInputPrivate::start(QIODevice* device)
|
||||
{
|
||||
QIODevice* op = device;
|
||||
|
||||
if (!audioDeviceInfo->isFormatSupported(audioFormat) || !open()) {
|
||||
stateCode = QAudio::StoppedState;
|
||||
errorCode = QAudio::OpenError;
|
||||
return audioIO;
|
||||
}
|
||||
|
||||
reset();
|
||||
audioBuffer->reset();
|
||||
audioBuffer->setFlushDevice(op);
|
||||
|
||||
if (op == 0)
|
||||
op = audioIO;
|
||||
|
||||
// Start
|
||||
startTime = AudioGetCurrentHostTime();
|
||||
totalFrames = 0;
|
||||
|
||||
audioThreadStart();
|
||||
|
||||
stateCode = QAudio::ActiveState;
|
||||
errorCode = QAudio::NoError;
|
||||
emit stateChanged(stateCode);
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::stop()
|
||||
{
|
||||
QMutexLocker lock(&mutex);
|
||||
if (stateCode != QAudio::StoppedState) {
|
||||
audioThreadStop();
|
||||
audioBuffer->flush(true);
|
||||
|
||||
errorCode = QAudio::NoError;
|
||||
stateCode = QAudio::StoppedState;
|
||||
QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode));
|
||||
}
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::reset()
|
||||
{
|
||||
QMutexLocker lock(&mutex);
|
||||
if (stateCode != QAudio::StoppedState) {
|
||||
audioThreadStop();
|
||||
|
||||
errorCode = QAudio::NoError;
|
||||
stateCode = QAudio::StoppedState;
|
||||
QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode));
|
||||
}
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::suspend()
|
||||
{
|
||||
QMutexLocker lock(&mutex);
|
||||
if (stateCode == QAudio::ActiveState || stateCode == QAudio::IdleState) {
|
||||
audioThreadStop();
|
||||
|
||||
errorCode = QAudio::NoError;
|
||||
stateCode = QAudio::SuspendedState;
|
||||
QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode));
|
||||
}
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::resume()
|
||||
{
|
||||
QMutexLocker lock(&mutex);
|
||||
if (stateCode == QAudio::SuspendedState) {
|
||||
audioThreadStart();
|
||||
|
||||
errorCode = QAudio::NoError;
|
||||
stateCode = QAudio::ActiveState;
|
||||
QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode));
|
||||
}
|
||||
}
|
||||
|
||||
int QAudioInputPrivate::bytesReady() const
|
||||
{
|
||||
return audioBuffer->used();
|
||||
}
|
||||
|
||||
int QAudioInputPrivate::periodSize() const
|
||||
{
|
||||
return periodSizeBytes;
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::setBufferSize(int bs)
|
||||
{
|
||||
internalBufferSize = bs;
|
||||
}
|
||||
|
||||
int QAudioInputPrivate::bufferSize() const
|
||||
{
|
||||
return internalBufferSize;
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::setNotifyInterval(int milliSeconds)
|
||||
{
|
||||
if (intervalTimer->interval() == milliSeconds)
|
||||
return;
|
||||
|
||||
if (milliSeconds <= 0)
|
||||
milliSeconds = 0;
|
||||
|
||||
intervalTimer->setInterval(milliSeconds);
|
||||
}
|
||||
|
||||
int QAudioInputPrivate::notifyInterval() const
|
||||
{
|
||||
return intervalTimer->interval();
|
||||
}
|
||||
|
||||
qint64 QAudioInputPrivate::processedUSecs() const
|
||||
{
|
||||
return totalFrames * 1000000 / audioFormat.frequency();
|
||||
}
|
||||
|
||||
qint64 QAudioInputPrivate::elapsedUSecs() const
|
||||
{
|
||||
if (stateCode == QAudio::StoppedState)
|
||||
return 0;
|
||||
|
||||
return (AudioGetCurrentHostTime() - startTime) / (clockFrequency / 1000);
|
||||
}
|
||||
|
||||
QAudio::Error QAudioInputPrivate::error() const
|
||||
{
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
QAudio::State QAudioInputPrivate::state() const
|
||||
{
|
||||
return stateCode;
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::audioThreadStop()
|
||||
{
|
||||
stopTimers();
|
||||
if (audioThreadState.testAndSetAcquire(Running, Stopped))
|
||||
threadFinished.wait(&mutex);
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::audioThreadStart()
|
||||
{
|
||||
startTimers();
|
||||
audioThreadState = Running;
|
||||
AudioOutputUnitStart(audioUnit);
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::audioDeviceStop()
|
||||
{
|
||||
AudioOutputUnitStop(audioUnit);
|
||||
audioThreadState = Stopped;
|
||||
threadFinished.wakeOne();
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::audioDeviceFull()
|
||||
{
|
||||
QMutexLocker lock(&mutex);
|
||||
if (stateCode == QAudio::ActiveState) {
|
||||
audioDeviceStop();
|
||||
|
||||
errorCode = QAudio::UnderrunError;
|
||||
stateCode = QAudio::IdleState;
|
||||
QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::audioDeviceError()
|
||||
{
|
||||
QMutexLocker lock(&mutex);
|
||||
if (stateCode == QAudio::ActiveState) {
|
||||
audioDeviceStop();
|
||||
|
||||
errorCode = QAudio::IOError;
|
||||
stateCode = QAudio::StoppedState;
|
||||
QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::startTimers()
|
||||
{
|
||||
audioBuffer->startFlushTimer();
|
||||
if (intervalTimer->interval() > 0)
|
||||
intervalTimer->start();
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::stopTimers()
|
||||
{
|
||||
audioBuffer->stopFlushTimer();
|
||||
intervalTimer->stop();
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::deviceStopped()
|
||||
{
|
||||
stopTimers();
|
||||
emit stateChanged(stateCode);
|
||||
}
|
||||
|
||||
// Input callback
|
||||
OSStatus QAudioInputPrivate::inputCallback(void* inRefCon,
|
||||
AudioUnitRenderActionFlags* ioActionFlags,
|
||||
const AudioTimeStamp* inTimeStamp,
|
||||
UInt32 inBusNumber,
|
||||
UInt32 inNumberFrames,
|
||||
AudioBufferList* ioData)
|
||||
{
|
||||
Q_UNUSED(ioData);
|
||||
|
||||
QAudioInputPrivate* d = static_cast<QAudioInputPrivate*>(inRefCon);
|
||||
|
||||
const int threadState = d->audioThreadState.fetchAndAddAcquire(0);
|
||||
if (threadState == Stopped)
|
||||
d->audioDeviceStop();
|
||||
else {
|
||||
qint64 framesWritten;
|
||||
|
||||
framesWritten = d->audioBuffer->renderFromDevice(d->audioUnit,
|
||||
ioActionFlags,
|
||||
inTimeStamp,
|
||||
inBusNumber,
|
||||
inNumberFrames);
|
||||
|
||||
if (framesWritten > 0)
|
||||
d->totalFrames += framesWritten;
|
||||
else if (framesWritten == 0)
|
||||
d->audioDeviceFull();
|
||||
else if (framesWritten < 0)
|
||||
d->audioDeviceError();
|
||||
}
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#include "qaudioinput_mac_p.moc"
|
||||
|
||||
169
src/multimedia/audio/qaudioinput_mac_p.h
Normal file
169
src/multimedia/audio/qaudioinput_mac_p.h
Normal file
@@ -0,0 +1,169 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $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.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
|
||||
#ifndef QAUDIOINPUT_MAC_P_H
|
||||
#define QAUDIOINPUT_MAC_P_H
|
||||
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#include <CoreAudio/CoreAudio.h>
|
||||
#include <AudioUnit/AudioUnit.h>
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
|
||||
#include <QtCore/qobject.h>
|
||||
#include <QtCore/qmutex.h>
|
||||
#include <QtCore/qwaitcondition.h>
|
||||
#include <QtCore/qatomic.h>
|
||||
|
||||
#include <QtMultimedia/qaudio.h>
|
||||
#include <QtMultimedia/qaudioformat.h>
|
||||
#include <QtMultimedia/qaudioengine.h>
|
||||
|
||||
QT_BEGIN_HEADER
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QTimer;
|
||||
class QIODevice;
|
||||
class QAbstractAudioDeviceInfo;
|
||||
|
||||
namespace QtMultimediaInternal
|
||||
{
|
||||
class QAudioInputBuffer;
|
||||
}
|
||||
|
||||
class QAudioInputPrivate : public QAbstractAudioInput
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
bool isOpen;
|
||||
int periodSizeBytes;
|
||||
int internalBufferSize;
|
||||
qint64 totalFrames;
|
||||
QAudioFormat audioFormat;
|
||||
QIODevice* audioIO;
|
||||
AudioUnit audioUnit;
|
||||
AudioDeviceID audioDeviceId;
|
||||
Float64 clockFrequency;
|
||||
UInt64 startTime;
|
||||
QAudio::Error errorCode;
|
||||
QAudio::State stateCode;
|
||||
QtMultimediaInternal::QAudioInputBuffer* audioBuffer;
|
||||
QMutex mutex;
|
||||
QWaitCondition threadFinished;
|
||||
QAtomicInt audioThreadState;
|
||||
QTimer* intervalTimer;
|
||||
AudioStreamBasicDescription streamFormat;
|
||||
AudioStreamBasicDescription deviceFormat;
|
||||
QAbstractAudioDeviceInfo *audioDeviceInfo;
|
||||
|
||||
QAudioInputPrivate(const QByteArray& device, QAudioFormat const& format);
|
||||
~QAudioInputPrivate();
|
||||
|
||||
bool open();
|
||||
void close();
|
||||
|
||||
QAudioFormat format() const;
|
||||
|
||||
QIODevice* start(QIODevice* device);
|
||||
void stop();
|
||||
void reset();
|
||||
void suspend();
|
||||
void resume();
|
||||
void idle();
|
||||
|
||||
int bytesReady() const;
|
||||
int periodSize() const;
|
||||
|
||||
void setBufferSize(int value);
|
||||
int bufferSize() const;
|
||||
|
||||
void setNotifyInterval(int milliSeconds);
|
||||
int notifyInterval() const;
|
||||
|
||||
qint64 processedUSecs() const;
|
||||
qint64 elapsedUSecs() const;
|
||||
|
||||
QAudio::Error error() const;
|
||||
QAudio::State state() const;
|
||||
|
||||
void audioThreadStart();
|
||||
void audioThreadStop();
|
||||
|
||||
void audioDeviceStop();
|
||||
void audioDeviceFull();
|
||||
void audioDeviceError();
|
||||
|
||||
void startTimers();
|
||||
void stopTimers();
|
||||
|
||||
private slots:
|
||||
void deviceStopped();
|
||||
|
||||
private:
|
||||
enum { Running, Stopped };
|
||||
|
||||
// Input callback
|
||||
static OSStatus inputCallback(void* inRefCon,
|
||||
AudioUnitRenderActionFlags* ioActionFlags,
|
||||
const AudioTimeStamp* inTimeStamp,
|
||||
UInt32 inBusNumber,
|
||||
UInt32 inNumberFrames,
|
||||
AudioBufferList* ioData);
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
QT_END_HEADER
|
||||
|
||||
#endif // QAUDIOINPUT_MAC_P_H
|
||||
560
src/multimedia/audio/qaudioinput_symbian_p.cpp
Normal file
560
src/multimedia/audio/qaudioinput_symbian_p.cpp
Normal file
@@ -0,0 +1,560 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qaudioinput_symbian_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Constants
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const int PushInterval = 50; // ms
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Private class
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SymbianAudioInputPrivate::SymbianAudioInputPrivate(
|
||||
QAudioInputPrivate *audioDevice)
|
||||
: m_audioDevice(audioDevice)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
SymbianAudioInputPrivate::~SymbianAudioInputPrivate()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
qint64 SymbianAudioInputPrivate::readData(char *data, qint64 len)
|
||||
{
|
||||
qint64 totalRead = 0;
|
||||
|
||||
if (m_audioDevice->state() == QAudio::ActiveState ||
|
||||
m_audioDevice->state() == QAudio::IdleState) {
|
||||
|
||||
while (totalRead < len) {
|
||||
const qint64 read = m_audioDevice->read(data + totalRead,
|
||||
len - totalRead);
|
||||
if (read > 0)
|
||||
totalRead += read;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return totalRead;
|
||||
}
|
||||
|
||||
qint64 SymbianAudioInputPrivate::writeData(const char *data, qint64 len)
|
||||
{
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SymbianAudioInputPrivate::dataReady()
|
||||
{
|
||||
emit readyRead();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
QAudioInputPrivate::QAudioInputPrivate(const QByteArray &device,
|
||||
const QAudioFormat &format)
|
||||
: m_device(device)
|
||||
, m_format(format)
|
||||
, m_clientBufferSize(SymbianAudio::DefaultBufferSize)
|
||||
, m_notifyInterval(SymbianAudio::DefaultNotifyInterval)
|
||||
, m_notifyTimer(new QTimer(this))
|
||||
, m_error(QAudio::NoError)
|
||||
, m_internalState(SymbianAudio::ClosedState)
|
||||
, m_externalState(QAudio::StoppedState)
|
||||
, m_pullMode(false)
|
||||
, m_sink(0)
|
||||
, m_pullTimer(new QTimer(this))
|
||||
, m_devSound(0)
|
||||
, m_devSoundBuffer(0)
|
||||
, m_devSoundBufferSize(0)
|
||||
, m_totalBytesReady(0)
|
||||
, m_devSoundBufferPos(0)
|
||||
, m_totalSamplesRecorded(0)
|
||||
{
|
||||
qRegisterMetaType<CMMFBuffer *>("CMMFBuffer *");
|
||||
|
||||
connect(m_notifyTimer.data(), SIGNAL(timeout()), this, SIGNAL(notify()));
|
||||
|
||||
m_pullTimer->setInterval(PushInterval);
|
||||
connect(m_pullTimer.data(), SIGNAL(timeout()), this, SLOT(pullData()));
|
||||
}
|
||||
|
||||
QAudioInputPrivate::~QAudioInputPrivate()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
QIODevice* QAudioInputPrivate::start(QIODevice *device)
|
||||
{
|
||||
stop();
|
||||
|
||||
open();
|
||||
if (SymbianAudio::ClosedState != m_internalState) {
|
||||
if (device) {
|
||||
m_pullMode = true;
|
||||
m_sink = device;
|
||||
} else {
|
||||
m_sink = new SymbianAudioInputPrivate(this);
|
||||
m_sink->open(QIODevice::ReadOnly | QIODevice::Unbuffered);
|
||||
}
|
||||
|
||||
m_elapsed.restart();
|
||||
}
|
||||
|
||||
return m_sink;
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::stop()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::reset()
|
||||
{
|
||||
m_totalSamplesRecorded += getSamplesRecorded();
|
||||
m_devSound->stop();
|
||||
startRecording();
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::suspend()
|
||||
{
|
||||
if (SymbianAudio::ActiveState == m_internalState
|
||||
|| SymbianAudio::IdleState == m_internalState) {
|
||||
m_notifyTimer->stop();
|
||||
m_pullTimer->stop();
|
||||
const qint64 samplesRecorded = getSamplesRecorded();
|
||||
m_totalSamplesRecorded += samplesRecorded;
|
||||
|
||||
const bool paused = m_devSound->pause();
|
||||
if (paused) {
|
||||
if (m_devSoundBuffer)
|
||||
m_devSoundBufferQ.append(m_devSoundBuffer);
|
||||
m_devSoundBuffer = 0;
|
||||
setState(SymbianAudio::SuspendedPausedState);
|
||||
} else {
|
||||
m_devSoundBuffer = 0;
|
||||
m_devSoundBufferQ.clear();
|
||||
m_devSoundBufferPos = 0;
|
||||
setState(SymbianAudio::SuspendedStoppedState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::resume()
|
||||
{
|
||||
if (QAudio::SuspendedState == m_externalState) {
|
||||
if (SymbianAudio::SuspendedPausedState == m_internalState)
|
||||
m_devSound->resume();
|
||||
else
|
||||
m_devSound->start();
|
||||
startDataTransfer();
|
||||
}
|
||||
}
|
||||
|
||||
int QAudioInputPrivate::bytesReady() const
|
||||
{
|
||||
Q_ASSERT(m_devSoundBufferPos <= m_totalBytesReady);
|
||||
return m_totalBytesReady - m_devSoundBufferPos;
|
||||
}
|
||||
|
||||
int QAudioInputPrivate::periodSize() const
|
||||
{
|
||||
return bufferSize();
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::setBufferSize(int value)
|
||||
{
|
||||
// Note that DevSound does not allow its client to specify the buffer size.
|
||||
// This functionality is available via custom interfaces, but since these
|
||||
// cannot be guaranteed to work across all DevSound implementations, we
|
||||
// do not use them here.
|
||||
// In order to comply with the expected bevahiour of QAudioInput, we store
|
||||
// the value and return it from bufferSize(), but the underlying DevSound
|
||||
// buffer size remains unchanged.
|
||||
if (value > 0)
|
||||
m_clientBufferSize = value;
|
||||
}
|
||||
|
||||
int QAudioInputPrivate::bufferSize() const
|
||||
{
|
||||
return m_devSoundBufferSize ? m_devSoundBufferSize : m_clientBufferSize;
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::setNotifyInterval(int ms)
|
||||
{
|
||||
if (ms >= 0) {
|
||||
const int oldNotifyInterval = m_notifyInterval;
|
||||
m_notifyInterval = ms;
|
||||
if (m_notifyInterval && (SymbianAudio::ActiveState == m_internalState ||
|
||||
SymbianAudio::IdleState == m_internalState))
|
||||
m_notifyTimer->start(m_notifyInterval);
|
||||
else
|
||||
m_notifyTimer->stop();
|
||||
}
|
||||
}
|
||||
|
||||
int QAudioInputPrivate::notifyInterval() const
|
||||
{
|
||||
return m_notifyInterval;
|
||||
}
|
||||
|
||||
qint64 QAudioInputPrivate::processedUSecs() const
|
||||
{
|
||||
int samplesPlayed = 0;
|
||||
if (m_devSound && QAudio::SuspendedState != m_externalState)
|
||||
samplesPlayed = getSamplesRecorded();
|
||||
|
||||
// Protect against division by zero
|
||||
Q_ASSERT_X(m_format.frequency() > 0, Q_FUNC_INFO, "Invalid frequency");
|
||||
|
||||
const qint64 result = qint64(1000000) *
|
||||
(samplesPlayed + m_totalSamplesRecorded)
|
||||
/ m_format.frequency();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
qint64 QAudioInputPrivate::elapsedUSecs() const
|
||||
{
|
||||
const qint64 result = (QAudio::StoppedState == state()) ?
|
||||
0 : m_elapsed.elapsed() * 1000;
|
||||
return result;
|
||||
}
|
||||
|
||||
QAudio::Error QAudioInputPrivate::error() const
|
||||
{
|
||||
return m_error;
|
||||
}
|
||||
|
||||
QAudio::State QAudioInputPrivate::state() const
|
||||
{
|
||||
return m_externalState;
|
||||
}
|
||||
|
||||
QAudioFormat QAudioInputPrivate::format() const
|
||||
{
|
||||
return m_format;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Private functions
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void QAudioInputPrivate::open()
|
||||
{
|
||||
Q_ASSERT_X(SymbianAudio::ClosedState == m_internalState,
|
||||
Q_FUNC_INFO, "DevSound already opened");
|
||||
|
||||
Q_ASSERT(!m_devSound);
|
||||
m_devSound = new SymbianAudio::DevSoundWrapper(QAudio::AudioInput, this);
|
||||
|
||||
connect(m_devSound, SIGNAL(initializeComplete(int)),
|
||||
this, SLOT(devsoundInitializeComplete(int)));
|
||||
connect(m_devSound, SIGNAL(bufferToBeProcessed(CMMFBuffer *)),
|
||||
this, SLOT(devsoundBufferToBeEmptied(CMMFBuffer *)));
|
||||
connect(m_devSound, SIGNAL(processingError(int)),
|
||||
this, SLOT(devsoundRecordError(int)));
|
||||
|
||||
setState(SymbianAudio::InitializingState);
|
||||
m_devSound->initialize(m_format.codec());
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::startRecording()
|
||||
{
|
||||
const int samplesRecorded = m_devSound->samplesProcessed();
|
||||
Q_ASSERT(samplesRecorded == 0);
|
||||
|
||||
bool ok = m_devSound->setFormat(m_format);
|
||||
if (ok)
|
||||
ok = m_devSound->start();
|
||||
|
||||
if (ok) {
|
||||
startDataTransfer();
|
||||
} else {
|
||||
setError(QAudio::OpenError);
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::startDataTransfer()
|
||||
{
|
||||
if (m_notifyInterval)
|
||||
m_notifyTimer->start(m_notifyInterval);
|
||||
|
||||
if (m_pullMode)
|
||||
m_pullTimer->start();
|
||||
|
||||
if (bytesReady()) {
|
||||
setState(SymbianAudio::ActiveState);
|
||||
if (!m_pullMode)
|
||||
pushData();
|
||||
} else {
|
||||
if (QAudio::SuspendedState == m_externalState)
|
||||
setState(SymbianAudio::ActiveState);
|
||||
else
|
||||
setState(SymbianAudio::IdleState);
|
||||
}
|
||||
}
|
||||
|
||||
CMMFDataBuffer* QAudioInputPrivate::currentBuffer() const
|
||||
{
|
||||
CMMFDataBuffer *result = m_devSoundBuffer;
|
||||
if (!result && !m_devSoundBufferQ.empty())
|
||||
result = m_devSoundBufferQ.front();
|
||||
return result;
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::pushData()
|
||||
{
|
||||
Q_ASSERT_X(bytesReady(), Q_FUNC_INFO, "No data available");
|
||||
Q_ASSERT_X(!m_pullMode, Q_FUNC_INFO, "pushData called when in pull mode");
|
||||
qobject_cast<SymbianAudioInputPrivate *>(m_sink)->dataReady();
|
||||
}
|
||||
|
||||
qint64 QAudioInputPrivate::read(char *data, qint64 len)
|
||||
{
|
||||
// SymbianAudioInputPrivate is ready to read data
|
||||
|
||||
Q_ASSERT_X(!m_pullMode, Q_FUNC_INFO,
|
||||
"read called when in pull mode");
|
||||
|
||||
qint64 bytesRead = 0;
|
||||
|
||||
CMMFDataBuffer *buffer = 0;
|
||||
while ((buffer = currentBuffer()) && (bytesRead < len)) {
|
||||
if (SymbianAudio::IdleState == m_internalState)
|
||||
setState(SymbianAudio::ActiveState);
|
||||
|
||||
TDesC8 &inputBuffer = buffer->Data();
|
||||
|
||||
Q_ASSERT(inputBuffer.Length() >= m_devSoundBufferPos);
|
||||
const qint64 inputBytes = inputBuffer.Length() - m_devSoundBufferPos;
|
||||
const qint64 outputBytes = len - bytesRead;
|
||||
const qint64 copyBytes = outputBytes < inputBytes ?
|
||||
outputBytes : inputBytes;
|
||||
|
||||
memcpy(data, inputBuffer.Ptr() + m_devSoundBufferPos, copyBytes);
|
||||
|
||||
m_devSoundBufferPos += copyBytes;
|
||||
data += copyBytes;
|
||||
bytesRead += copyBytes;
|
||||
|
||||
if (inputBytes == copyBytes)
|
||||
bufferEmptied();
|
||||
}
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::pullData()
|
||||
{
|
||||
Q_ASSERT_X(m_pullMode, Q_FUNC_INFO,
|
||||
"pullData called when in push mode");
|
||||
|
||||
CMMFDataBuffer *buffer = 0;
|
||||
while (buffer = currentBuffer()) {
|
||||
if (SymbianAudio::IdleState == m_internalState)
|
||||
setState(SymbianAudio::ActiveState);
|
||||
|
||||
TDesC8 &inputBuffer = buffer->Data();
|
||||
|
||||
Q_ASSERT(inputBuffer.Length() >= m_devSoundBufferPos);
|
||||
const qint64 inputBytes = inputBuffer.Length() - m_devSoundBufferPos;
|
||||
const qint64 bytesPushed = m_sink->write(
|
||||
(char*)inputBuffer.Ptr() + m_devSoundBufferPos, inputBytes);
|
||||
|
||||
m_devSoundBufferPos += bytesPushed;
|
||||
|
||||
if (inputBytes == bytesPushed)
|
||||
bufferEmptied();
|
||||
|
||||
if (!bytesPushed)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::devsoundInitializeComplete(int err)
|
||||
{
|
||||
Q_ASSERT_X(SymbianAudio::InitializingState == m_internalState,
|
||||
Q_FUNC_INFO, "Invalid state");
|
||||
|
||||
if (!err && m_devSound->isFormatSupported(m_format))
|
||||
startRecording();
|
||||
else
|
||||
setError(QAudio::OpenError);
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::devsoundBufferToBeEmptied(CMMFBuffer *baseBuffer)
|
||||
{
|
||||
// Following receipt of this signal, DevSound should not provide another
|
||||
// buffer until we have returned the current one.
|
||||
Q_ASSERT_X(!m_devSoundBuffer, Q_FUNC_INFO, "Buffer already held");
|
||||
|
||||
CMMFDataBuffer *const buffer = static_cast<CMMFDataBuffer*>(baseBuffer);
|
||||
|
||||
if (!m_devSoundBufferSize)
|
||||
m_devSoundBufferSize = buffer->Data().MaxLength();
|
||||
|
||||
m_totalBytesReady += buffer->Data().Length();
|
||||
|
||||
if (SymbianAudio::SuspendedPausedState == m_internalState) {
|
||||
m_devSoundBufferQ.append(buffer);
|
||||
} else {
|
||||
// Will be returned to DevSoundWrapper by bufferProcessed().
|
||||
m_devSoundBuffer = buffer;
|
||||
m_devSoundBufferPos = 0;
|
||||
|
||||
if (bytesReady() && !m_pullMode)
|
||||
pushData();
|
||||
}
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::devsoundRecordError(int err)
|
||||
{
|
||||
Q_UNUSED(err)
|
||||
setError(QAudio::IOError);
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::bufferEmptied()
|
||||
{
|
||||
m_devSoundBufferPos = 0;
|
||||
|
||||
if (m_devSoundBuffer) {
|
||||
m_totalBytesReady -= m_devSoundBuffer->Data().Length();
|
||||
m_devSoundBuffer = 0;
|
||||
m_devSound->bufferProcessed();
|
||||
} else {
|
||||
Q_ASSERT(!m_devSoundBufferQ.empty());
|
||||
m_totalBytesReady -= m_devSoundBufferQ.front()->Data().Length();
|
||||
m_devSoundBufferQ.erase(m_devSoundBufferQ.begin());
|
||||
|
||||
// If the queue has been emptied, resume transfer from the hardware
|
||||
if (m_devSoundBufferQ.empty())
|
||||
if (!m_devSound->start())
|
||||
setError(QAudio::IOError);
|
||||
}
|
||||
|
||||
Q_ASSERT(m_totalBytesReady >= 0);
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::close()
|
||||
{
|
||||
m_notifyTimer->stop();
|
||||
m_pullTimer->stop();
|
||||
|
||||
m_error = QAudio::NoError;
|
||||
|
||||
if (m_devSound)
|
||||
m_devSound->stop();
|
||||
delete m_devSound;
|
||||
m_devSound = 0;
|
||||
|
||||
m_devSoundBuffer = 0;
|
||||
m_devSoundBufferSize = 0;
|
||||
m_totalBytesReady = 0;
|
||||
|
||||
if (!m_pullMode) // m_sink is owned
|
||||
delete m_sink;
|
||||
m_pullMode = false;
|
||||
m_sink = 0;
|
||||
|
||||
m_devSoundBufferQ.clear();
|
||||
m_devSoundBufferPos = 0;
|
||||
m_totalSamplesRecorded = 0;
|
||||
|
||||
setState(SymbianAudio::ClosedState);
|
||||
}
|
||||
|
||||
qint64 QAudioInputPrivate::getSamplesRecorded() const
|
||||
{
|
||||
qint64 result = 0;
|
||||
if (m_devSound)
|
||||
result = qint64(m_devSound->samplesProcessed());
|
||||
return result;
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::setError(QAudio::Error error)
|
||||
{
|
||||
m_error = error;
|
||||
|
||||
// Although no state transition actually occurs here, a stateChanged event
|
||||
// must be emitted to inform the client that the call to start() was
|
||||
// unsuccessful.
|
||||
if (QAudio::OpenError == error) {
|
||||
emit stateChanged(QAudio::StoppedState);
|
||||
} else {
|
||||
if (QAudio::UnderrunError == error)
|
||||
setState(SymbianAudio::IdleState);
|
||||
else
|
||||
// Close the DevSound instance. This causes a transition to
|
||||
// StoppedState. This must be done asynchronously in case the
|
||||
// current function was called from a DevSound event handler, in which
|
||||
// case deleting the DevSound instance may cause an exception.
|
||||
QMetaObject::invokeMethod(this, "close", Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::setState(SymbianAudio::State newInternalState)
|
||||
{
|
||||
const QAudio::State oldExternalState = m_externalState;
|
||||
m_internalState = newInternalState;
|
||||
m_externalState = SymbianAudio::Utils::stateNativeToQt(m_internalState);
|
||||
|
||||
if (m_externalState != oldExternalState)
|
||||
emit stateChanged(m_externalState);
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
174
src/multimedia/audio/qaudioinput_symbian_p.h
Normal file
174
src/multimedia/audio/qaudioinput_symbian_p.h
Normal file
@@ -0,0 +1,174 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $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.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#ifndef QAUDIOINPUT_SYMBIAN_P_H
|
||||
#define QAUDIOINPUT_SYMBIAN_P_H
|
||||
|
||||
#include <QtMultimedia/qaudioengine.h>
|
||||
#include <QTime>
|
||||
#include <QTimer>
|
||||
#include "qaudio_symbian_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QAudioInputPrivate;
|
||||
|
||||
class SymbianAudioInputPrivate : public QIODevice
|
||||
{
|
||||
friend class QAudioInputPrivate;
|
||||
Q_OBJECT
|
||||
public:
|
||||
SymbianAudioInputPrivate(QAudioInputPrivate *audio);
|
||||
~SymbianAudioInputPrivate();
|
||||
|
||||
qint64 readData(char *data, qint64 len);
|
||||
qint64 writeData(const char *data, qint64 len);
|
||||
|
||||
void dataReady();
|
||||
|
||||
private:
|
||||
QAudioInputPrivate *const m_audioDevice;
|
||||
};
|
||||
|
||||
class QAudioInputPrivate
|
||||
: public QAbstractAudioInput
|
||||
{
|
||||
friend class SymbianAudioInputPrivate;
|
||||
Q_OBJECT
|
||||
public:
|
||||
QAudioInputPrivate(const QByteArray &device,
|
||||
const QAudioFormat &audioFormat);
|
||||
~QAudioInputPrivate();
|
||||
|
||||
// QAbstractAudioInput
|
||||
QIODevice* start(QIODevice *device = 0);
|
||||
void stop();
|
||||
void reset();
|
||||
void suspend();
|
||||
void resume();
|
||||
int bytesReady() const;
|
||||
int periodSize() const;
|
||||
void setBufferSize(int value);
|
||||
int bufferSize() const;
|
||||
void setNotifyInterval(int milliSeconds);
|
||||
int notifyInterval() const;
|
||||
qint64 processedUSecs() const;
|
||||
qint64 elapsedUSecs() const;
|
||||
QAudio::Error error() const;
|
||||
QAudio::State state() const;
|
||||
QAudioFormat format() const;
|
||||
|
||||
private slots:
|
||||
void pullData();
|
||||
void devsoundInitializeComplete(int err);
|
||||
void devsoundBufferToBeEmptied(CMMFBuffer *);
|
||||
void devsoundRecordError(int err);
|
||||
|
||||
private:
|
||||
void open();
|
||||
void startRecording();
|
||||
void startDataTransfer();
|
||||
CMMFDataBuffer* currentBuffer() const;
|
||||
void pushData();
|
||||
qint64 read(char *data, qint64 len);
|
||||
void bufferEmptied();
|
||||
Q_INVOKABLE void close();
|
||||
|
||||
qint64 getSamplesRecorded() const;
|
||||
|
||||
void setError(QAudio::Error error);
|
||||
void setState(SymbianAudio::State state);
|
||||
|
||||
private:
|
||||
const QByteArray m_device;
|
||||
const QAudioFormat m_format;
|
||||
|
||||
int m_clientBufferSize;
|
||||
int m_notifyInterval;
|
||||
QScopedPointer<QTimer> m_notifyTimer;
|
||||
QTime m_elapsed;
|
||||
QAudio::Error m_error;
|
||||
|
||||
SymbianAudio::State m_internalState;
|
||||
QAudio::State m_externalState;
|
||||
|
||||
bool m_pullMode;
|
||||
QIODevice *m_sink;
|
||||
|
||||
QScopedPointer<QTimer> m_pullTimer;
|
||||
|
||||
SymbianAudio::DevSoundWrapper* m_devSound;
|
||||
|
||||
// Latest buffer provided by DevSound, to be empied of data.
|
||||
CMMFDataBuffer *m_devSoundBuffer;
|
||||
|
||||
int m_devSoundBufferSize;
|
||||
|
||||
// Total amount of data in buffers provided by DevSound
|
||||
int m_totalBytesReady;
|
||||
|
||||
// Queue of buffers returned after call to CMMFDevSound::Pause().
|
||||
QList<CMMFDataBuffer *> m_devSoundBufferQ;
|
||||
|
||||
// Current read position within m_devSoundBuffer
|
||||
qint64 m_devSoundBufferPos;
|
||||
|
||||
// Samples recorded up to the last call to suspend(). It is necessary
|
||||
// to cache this because suspend() is implemented using
|
||||
// CMMFDevSound::Stop(), which resets DevSound's SamplesRecorded() counter.
|
||||
quint32 m_totalSamplesRecorded;
|
||||
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
633
src/multimedia/audio/qaudioinput_win32_p.cpp
Normal file
633
src/multimedia/audio/qaudioinput_win32_p.cpp
Normal file
@@ -0,0 +1,633 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $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.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
|
||||
#include "qaudioinput_win32_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
//#define DEBUG_AUDIO 1
|
||||
|
||||
QAudioInputPrivate::QAudioInputPrivate(const QByteArray &device, const QAudioFormat& audioFormat):
|
||||
settings(audioFormat)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
QAudioInputPrivate::~QAudioInputPrivate()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void QT_WIN_CALLBACK QAudioInputPrivate::waveInProc( HWAVEIN hWaveIn, UINT uMsg,
|
||||
DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 )
|
||||
{
|
||||
Q_UNUSED(dwParam1)
|
||||
Q_UNUSED(dwParam2)
|
||||
Q_UNUSED(hWaveIn)
|
||||
|
||||
QAudioInputPrivate* qAudio;
|
||||
qAudio = (QAudioInputPrivate*)(dwInstance);
|
||||
if(!qAudio)
|
||||
return;
|
||||
|
||||
QMutexLocker(&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* QAudioInputPrivate::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 QAudioInputPrivate::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 QAudioInputPrivate::error() const
|
||||
{
|
||||
return errorState;
|
||||
}
|
||||
|
||||
QAudio::State QAudioInputPrivate::state() const
|
||||
{
|
||||
return deviceState;
|
||||
}
|
||||
|
||||
QAudioFormat QAudioInputPrivate::format() const
|
||||
{
|
||||
return settings;
|
||||
}
|
||||
|
||||
QIODevice* QAudioInputPrivate::start(QIODevice* device)
|
||||
{
|
||||
if(deviceState != QAudio::StoppedState)
|
||||
close();
|
||||
|
||||
if(!pullMode && audioSource) {
|
||||
delete audioSource;
|
||||
}
|
||||
|
||||
if(device) {
|
||||
//set to pull mode
|
||||
pullMode = true;
|
||||
audioSource = device;
|
||||
deviceState = QAudio::ActiveState;
|
||||
} else {
|
||||
//set to push mode
|
||||
pullMode = false;
|
||||
deviceState = QAudio::IdleState;
|
||||
audioSource = new InputPrivate(this);
|
||||
audioSource->open(QIODevice::ReadOnly | QIODevice::Unbuffered);
|
||||
}
|
||||
|
||||
if( !open() )
|
||||
return 0;
|
||||
|
||||
emit stateChanged(deviceState);
|
||||
|
||||
return audioSource;
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::stop()
|
||||
{
|
||||
if(deviceState == QAudio::StoppedState)
|
||||
return;
|
||||
|
||||
close();
|
||||
emit stateChanged(deviceState);
|
||||
}
|
||||
|
||||
bool QAudioInputPrivate::open()
|
||||
{
|
||||
#ifdef DEBUG_AUDIO
|
||||
QTime now(QTime::currentTime());
|
||||
qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()";
|
||||
#endif
|
||||
header = 0;
|
||||
period_size = 0;
|
||||
|
||||
if (!settings.isValid()) {
|
||||
qWarning("QAudioInput: open error, invalid format.");
|
||||
} else if (settings.channels() <= 0) {
|
||||
qWarning("QAudioInput: open error, invalid number of channels (%d).",
|
||||
settings.channels());
|
||||
} else if (settings.sampleSize() <= 0) {
|
||||
qWarning("QAudioInput: open error, invalid sample size (%d).",
|
||||
settings.sampleSize());
|
||||
} else if (settings.frequency() < 8000 || settings.frequency() > 48000) {
|
||||
qWarning("QAudioInput: open error, frequency out of range (%d).", settings.frequency());
|
||||
} else if (buffer_size == 0) {
|
||||
|
||||
buffer_size
|
||||
= (settings.frequency()
|
||||
* settings.channels()
|
||||
* settings.sampleSize()
|
||||
#ifndef Q_OS_WINCE // Default buffer size, 200ms, default period size is 40ms
|
||||
+ 39) / 40;
|
||||
period_size = buffer_size / 5;
|
||||
} else {
|
||||
period_size = buffer_size / 5;
|
||||
#else // For wince reduce size to 40ms for buffer size and 20ms period
|
||||
+ 199) / 200;
|
||||
period_size = buffer_size / 2;
|
||||
} else {
|
||||
period_size = buffer_size / 2;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (period_size == 0) {
|
||||
errorState = QAudio::OpenError;
|
||||
deviceState = QAudio::StoppedState;
|
||||
emit stateChanged(deviceState);
|
||||
return false;
|
||||
}
|
||||
|
||||
timeStamp.restart();
|
||||
elapsedTimeOffset = 0;
|
||||
wfx.nSamplesPerSec = settings.frequency();
|
||||
wfx.wBitsPerSample = settings.sampleSize();
|
||||
wfx.nChannels = settings.channels();
|
||||
wfx.cbSize = 0;
|
||||
|
||||
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
||||
wfx.nBlockAlign = (wfx.wBitsPerSample >> 3) * wfx.nChannels;
|
||||
wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec;
|
||||
|
||||
UINT_PTR devId = WAVE_MAPPER;
|
||||
|
||||
WAVEINCAPS wic;
|
||||
unsigned long iNumDevs,ii;
|
||||
iNumDevs = waveInGetNumDevs();
|
||||
for(ii=0;ii<iNumDevs;ii++) {
|
||||
if(waveInGetDevCaps(ii, &wic, sizeof(WAVEINCAPS))
|
||||
== MMSYSERR_NOERROR) {
|
||||
QString tmp;
|
||||
tmp = QString((const QChar *)wic.szPname);
|
||||
if(tmp.compare(QLatin1String(m_device)) == 0) {
|
||||
devId = ii;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(waveInOpen(&hWaveIn, devId, &wfx,
|
||||
(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);
|
||||
|
||||
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();
|
||||
|
||||
waveCurrentBlock = 0;
|
||||
|
||||
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;
|
||||
return true;
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::close()
|
||||
{
|
||||
if(deviceState == QAudio::StoppedState)
|
||||
return;
|
||||
|
||||
deviceState = QAudio::StoppedState;
|
||||
waveInReset(hWaveIn);
|
||||
waveInClose(hWaveIn);
|
||||
|
||||
int count = 0;
|
||||
while(!finished && count < 500) {
|
||||
count++;
|
||||
Sleep(10);
|
||||
}
|
||||
|
||||
mutex.lock();
|
||||
for(int i=0; i<waveFreeBlockCount; i++)
|
||||
waveInUnprepareHeader(hWaveIn,&waveBlocks[i],sizeof(WAVEHDR));
|
||||
freeBlocks(waveBlocks);
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
int QAudioInputPrivate::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 QAudioInputPrivate::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,
|
||||
waveBlocks[header].dwBytesRecorded);
|
||||
#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 += waveBlocks[header].dwBytesRecorded;
|
||||
errorState = QAudio::NoError;
|
||||
if (deviceState != QAudio::ActiveState) {
|
||||
deviceState = QAudio::ActiveState;
|
||||
emit stateChanged(deviceState);
|
||||
}
|
||||
resuming = false;
|
||||
}
|
||||
} else {
|
||||
l = qMin<qint64>(len, waveBlocks[header].dwBytesRecorded);
|
||||
// push mode
|
||||
memcpy(p, waveBlocks[header].lpData, l);
|
||||
|
||||
len -= l;
|
||||
|
||||
#ifdef DEBUG_AUDIO
|
||||
qDebug()<<"IN: "<<waveBlocks[header].dwBytesRecorded<<", OUT: "<<l;
|
||||
#endif
|
||||
totalTimeValue += waveBlocks[header].dwBytesRecorded;
|
||||
errorState = QAudio::NoError;
|
||||
if (deviceState != QAudio::ActiveState) {
|
||||
deviceState = QAudio::ActiveState;
|
||||
emit stateChanged(deviceState);
|
||||
}
|
||||
resuming = false;
|
||||
}
|
||||
} else {
|
||||
//no data, not ready yet, next time
|
||||
break;
|
||||
}
|
||||
|
||||
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 QAudioInputPrivate::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();
|
||||
|
||||
waveCurrentBlock = 0;
|
||||
header = 0;
|
||||
resuming = true;
|
||||
waveInStart(hWaveIn);
|
||||
QTimer::singleShot(20,this,SLOT(feedback()));
|
||||
emit stateChanged(deviceState);
|
||||
}
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::setBufferSize(int value)
|
||||
{
|
||||
buffer_size = value;
|
||||
}
|
||||
|
||||
int QAudioInputPrivate::bufferSize() const
|
||||
{
|
||||
return buffer_size;
|
||||
}
|
||||
|
||||
int QAudioInputPrivate::periodSize() const
|
||||
{
|
||||
return period_size;
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::setNotifyInterval(int ms)
|
||||
{
|
||||
intervalTime = qMax(0, ms);
|
||||
}
|
||||
|
||||
int QAudioInputPrivate::notifyInterval() const
|
||||
{
|
||||
return intervalTime;
|
||||
}
|
||||
|
||||
qint64 QAudioInputPrivate::processedUSecs() const
|
||||
{
|
||||
if (deviceState == QAudio::StoppedState)
|
||||
return 0;
|
||||
qint64 result = qint64(1000000) * totalTimeValue /
|
||||
(settings.channels()*(settings.sampleSize()/8)) /
|
||||
settings.frequency();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::suspend()
|
||||
{
|
||||
if(deviceState == QAudio::ActiveState) {
|
||||
waveInReset(hWaveIn);
|
||||
deviceState = QAudio::SuspendedState;
|
||||
emit stateChanged(deviceState);
|
||||
}
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::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 QAudioInputPrivate::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 QAudioInputPrivate::elapsedUSecs() const
|
||||
{
|
||||
if (deviceState == QAudio::StoppedState)
|
||||
return 0;
|
||||
|
||||
return timeStampOpened.elapsed()*1000;
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::reset()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
InputPrivate::InputPrivate(QAudioInputPrivate* audio)
|
||||
{
|
||||
audioDevice = qobject_cast<QAudioInputPrivate*>(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
|
||||
160
src/multimedia/audio/qaudioinput_win32_p.h
Normal file
160
src/multimedia/audio/qaudioinput_win32_p.h
Normal file
@@ -0,0 +1,160 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $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.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#ifndef QAUDIOINPUTWIN_H
|
||||
#define QAUDIOINPUTWIN_H
|
||||
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
|
||||
#include <QtCore/qfile.h>
|
||||
#include <QtCore/qdebug.h>
|
||||
#include <QtCore/qtimer.h>
|
||||
#include <QtCore/qstring.h>
|
||||
#include <QtCore/qstringlist.h>
|
||||
#include <QtCore/qdatetime.h>
|
||||
#include <QtCore/qmutex.h>
|
||||
|
||||
#include <QtMultimedia/qaudio.h>
|
||||
#include <QtMultimedia/qaudiodeviceinfo.h>
|
||||
#include <QtMultimedia/qaudioengine.h>
|
||||
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QAudioInputPrivate : public QAbstractAudioInput
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QAudioInputPrivate(const QByteArray &device, const QAudioFormat& audioFormat);
|
||||
~QAudioInputPrivate();
|
||||
|
||||
qint64 read(char* data, qint64 len);
|
||||
|
||||
QAudioFormat format() const;
|
||||
QIODevice* start(QIODevice* device = 0);
|
||||
void stop();
|
||||
void reset();
|
||||
void suspend();
|
||||
void resume();
|
||||
int bytesReady() const;
|
||||
int periodSize() const;
|
||||
void setBufferSize(int value);
|
||||
int bufferSize() const;
|
||||
void setNotifyInterval(int milliSeconds);
|
||||
int notifyInterval() const;
|
||||
qint64 processedUSecs() const;
|
||||
qint64 elapsedUSecs() const;
|
||||
QAudio::Error error() const;
|
||||
QAudio::State state() const;
|
||||
|
||||
QIODevice* audioSource;
|
||||
QAudioFormat settings;
|
||||
QAudio::Error errorState;
|
||||
QAudio::State deviceState;
|
||||
|
||||
private:
|
||||
qint32 buffer_size;
|
||||
qint32 period_size;
|
||||
qint32 header;
|
||||
QByteArray m_device;
|
||||
int bytesAvailable;
|
||||
int intervalTime;
|
||||
QTime timeStamp;
|
||||
qint64 elapsedTimeOffset;
|
||||
QTime timeStampOpened;
|
||||
qint64 totalTimeValue;
|
||||
bool pullMode;
|
||||
bool resuming;
|
||||
WAVEFORMATEX wfx;
|
||||
HWAVEIN hWaveIn;
|
||||
MMRESULT result;
|
||||
WAVEHDR* waveBlocks;
|
||||
volatile bool finished;
|
||||
volatile int waveFreeBlockCount;
|
||||
int waveCurrentBlock;
|
||||
|
||||
QMutex mutex;
|
||||
static void QT_WIN_CALLBACK waveInProc( HWAVEIN hWaveIn, UINT uMsg,
|
||||
DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 );
|
||||
|
||||
WAVEHDR* allocateBlocks(int size, int count);
|
||||
void freeBlocks(WAVEHDR* blockArray);
|
||||
bool open();
|
||||
void close();
|
||||
|
||||
private slots:
|
||||
void feedback();
|
||||
bool deviceReady();
|
||||
|
||||
signals:
|
||||
void processMore();
|
||||
};
|
||||
|
||||
class InputPrivate : public QIODevice
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
InputPrivate(QAudioInputPrivate* audio);
|
||||
~InputPrivate();
|
||||
|
||||
qint64 readData( char* data, qint64 len);
|
||||
qint64 writeData(const char* data, qint64 len);
|
||||
|
||||
void trigger();
|
||||
private:
|
||||
QAudioInputPrivate *audioDevice;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
430
src/multimedia/audio/qaudiooutput.cpp
Normal file
430
src/multimedia/audio/qaudiooutput.cpp
Normal file
@@ -0,0 +1,430 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#include <QtMultimedia/qaudio.h>
|
||||
#include <QtMultimedia/qaudiodeviceinfo.h>
|
||||
#include <QtMultimedia/qaudioengine.h>
|
||||
#include <QtMultimedia/qaudiooutput.h>
|
||||
|
||||
#include "qaudiodevicefactory_p.h"
|
||||
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
/*!
|
||||
\class QAudioOutput
|
||||
\brief The QAudioOutput class provides an interface for sending audio data to an audio output device.
|
||||
|
||||
\inmodule QtMultimedia
|
||||
\ingroup multimedia
|
||||
\since 4.6
|
||||
|
||||
You can construct an audio output with the system's
|
||||
\l{QAudioDeviceInfo::defaultOutputDevice()}{default audio output
|
||||
device}. It is also possible to create QAudioOutput with a
|
||||
specific QAudioDeviceInfo. When you create the audio output, you
|
||||
should also send in the QAudioFormat to be used for the playback
|
||||
(see the QAudioFormat class description for details).
|
||||
|
||||
To play a file:
|
||||
|
||||
Starting to play an audio stream is simply a matter of calling
|
||||
start() with a QIODevice. QAudioOutput will then fetch the data it
|
||||
needs from the io device. So playing back an audio file is as
|
||||
simple as:
|
||||
|
||||
\code
|
||||
QFile inputFile; // class member.
|
||||
QAudioOutput* audio; // class member.
|
||||
\endcode
|
||||
|
||||
\code
|
||||
inputFile.setFileName("/tmp/test.raw");
|
||||
inputFile.open(QIODevice::ReadOnly);
|
||||
|
||||
QAudioFormat format;
|
||||
// Set up the format, eg.
|
||||
format.setFrequency(8000);
|
||||
format.setChannels(1);
|
||||
format.setSampleSize(8);
|
||||
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;
|
||||
}
|
||||
|
||||
audio = new QAudioOutput(format, this);
|
||||
connect(audio,SIGNAL(stateChanged(QAudio::State)),SLOT(finishedPlaying(QAudio::State)));
|
||||
audio->start(&inputFile);
|
||||
|
||||
\endcode
|
||||
|
||||
The file will start playing assuming that the audio system and
|
||||
output device support it. If you run out of luck, check what's
|
||||
up with the error() function.
|
||||
|
||||
After the file has finished playing, we need to stop the device:
|
||||
|
||||
\code
|
||||
void finishedPlaying(QAudio::State state)
|
||||
{
|
||||
if(state == QAudio::IdleState) {
|
||||
audio->stop();
|
||||
inputFile.close();
|
||||
delete audio;
|
||||
}
|
||||
}
|
||||
\endcode
|
||||
|
||||
At any given time, the QAudioOutput will be in one of four states:
|
||||
active, suspended, stopped, or idle. These states are described
|
||||
by the QAudio::State enum.
|
||||
State changes are reported through the stateChanged() signal. You
|
||||
can use this signal to, for instance, update the GUI of the
|
||||
application; the mundane example here being changing the state of
|
||||
a \c { play/pause } button. You request a state change directly
|
||||
with suspend(), stop(), reset(), resume(), and start().
|
||||
|
||||
While the stream is playing, you can set a notify interval in
|
||||
milliseconds with setNotifyInterval(). This interval specifies the
|
||||
time between two emissions of the notify() signal. This is
|
||||
relative to the position in the stream, i.e., if the QAudioOutput
|
||||
is in the SuspendedState or the IdleState, the notify() signal is
|
||||
not emitted. A typical use-case would be to update a
|
||||
\l{QSlider}{slider} that allows seeking in the stream.
|
||||
If you want the time since playback started regardless of which
|
||||
states the audio output has been in, elapsedUSecs() is the function for you.
|
||||
|
||||
If an error occurs, you can fetch the \l{QAudio::Error}{error
|
||||
type} with the error() function. Please see the QAudio::Error enum
|
||||
for a description of the possible errors that are reported. When
|
||||
an error is encountered, the state changes to QAudio::StoppedState.
|
||||
You can check for errors by connecting to the stateChanged()
|
||||
signal:
|
||||
|
||||
\snippet doc/src/snippets/audio/main.cpp 3
|
||||
|
||||
\sa QAudioInput, QAudioDeviceInfo
|
||||
*/
|
||||
|
||||
/*!
|
||||
Construct a new audio output and attach it to \a parent.
|
||||
The default audio output device is used with the output
|
||||
\a format parameters.
|
||||
*/
|
||||
|
||||
QAudioOutput::QAudioOutput(const QAudioFormat &format, QObject *parent):
|
||||
QObject(parent)
|
||||
{
|
||||
d = QAudioDeviceFactory::createDefaultOutputDevice(format);
|
||||
connect(d, SIGNAL(notify()), SIGNAL(notify()));
|
||||
connect(d, SIGNAL(stateChanged(QAudio::State)), SIGNAL(stateChanged(QAudio::State)));
|
||||
}
|
||||
|
||||
/*!
|
||||
Construct a new audio output and attach it to \a parent.
|
||||
The device referenced by \a audioDevice is used with the output
|
||||
\a format parameters.
|
||||
*/
|
||||
|
||||
QAudioOutput::QAudioOutput(const QAudioDeviceInfo &audioDevice, const QAudioFormat &format, QObject *parent):
|
||||
QObject(parent)
|
||||
{
|
||||
d = QAudioDeviceFactory::createOutputDevice(audioDevice, format);
|
||||
connect(d, SIGNAL(notify()), SIGNAL(notify()));
|
||||
connect(d, SIGNAL(stateChanged(QAudio::State)), SIGNAL(stateChanged(QAudio::State)));
|
||||
}
|
||||
|
||||
/*!
|
||||
Destroys this audio output.
|
||||
*/
|
||||
|
||||
QAudioOutput::~QAudioOutput()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the QAudioFormat being used.
|
||||
|
||||
*/
|
||||
|
||||
QAudioFormat QAudioOutput::format() const
|
||||
{
|
||||
return d->format();
|
||||
}
|
||||
|
||||
/*!
|
||||
Uses the \a device as the QIODevice to transfer data.
|
||||
Passing a QIODevice allows the data to be transferred without any extra code.
|
||||
All that is required is to open the QIODevice.
|
||||
|
||||
If able to successfully output audio data to the systems audio device the
|
||||
state() is set to QAudio::ActiveState, error() is set to QAudio::NoError
|
||||
and the stateChanged() signal is emitted.
|
||||
|
||||
If a problem occurs during this process the error() is set to QAudio::OpenError,
|
||||
state() is set to QAudio::StoppedState and stateChanged() signal is emitted.
|
||||
|
||||
In either case, the stateChanged() signal may be emitted either synchronously
|
||||
during execution of the start() function or asynchronously after start() has
|
||||
returned to the caller.
|
||||
|
||||
\sa QIODevice
|
||||
*/
|
||||
|
||||
void QAudioOutput::start(QIODevice* device)
|
||||
{
|
||||
d->start(device);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a pointer to the QIODevice being used to handle the data
|
||||
transfer. This QIODevice can be used to write() audio data directly.
|
||||
|
||||
If able to access the systems audio device the state() is set to
|
||||
QAudio::IdleState, error() is set to QAudio::NoError
|
||||
and the stateChanged() signal is emitted.
|
||||
|
||||
If a problem occurs during this process the error() is set to QAudio::OpenError,
|
||||
state() is set to QAudio::StoppedState and stateChanged() signal is emitted.
|
||||
|
||||
In either case, the stateChanged() signal may be emitted either synchronously
|
||||
during execution of the start() function or asynchronously after start() has
|
||||
returned to the caller.
|
||||
|
||||
\sa QIODevice
|
||||
*/
|
||||
|
||||
QIODevice* QAudioOutput::start()
|
||||
{
|
||||
return d->start(0);
|
||||
}
|
||||
|
||||
/*!
|
||||
Stops the audio output, detaching from the system resource.
|
||||
|
||||
Sets error() to QAudio::NoError, state() to QAudio::StoppedState and
|
||||
emit stateChanged() signal.
|
||||
*/
|
||||
|
||||
void QAudioOutput::stop()
|
||||
{
|
||||
d->stop();
|
||||
}
|
||||
|
||||
/*!
|
||||
Drops all audio data in the buffers, resets buffers to zero.
|
||||
*/
|
||||
|
||||
void QAudioOutput::reset()
|
||||
{
|
||||
d->reset();
|
||||
}
|
||||
|
||||
/*!
|
||||
Stops processing audio data, preserving buffered audio data.
|
||||
|
||||
Sets error() to QAudio::NoError, state() to QAudio::SuspendedState and
|
||||
emit stateChanged() signal.
|
||||
*/
|
||||
|
||||
void QAudioOutput::suspend()
|
||||
{
|
||||
d->suspend();
|
||||
}
|
||||
|
||||
/*!
|
||||
Resumes processing audio data after a suspend().
|
||||
|
||||
Sets error() to QAudio::NoError.
|
||||
Sets state() to QAudio::ActiveState if you previously called start(QIODevice*).
|
||||
Sets state() to QAudio::IdleState if you previously called start().
|
||||
emits stateChanged() signal.
|
||||
|
||||
Note: signal will always be emitted during execution of the resume() function.
|
||||
*/
|
||||
|
||||
void QAudioOutput::resume()
|
||||
{
|
||||
d->resume();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the free space available in bytes in the audio buffer.
|
||||
|
||||
NOTE: returned value is only valid while in QAudio::ActiveState or QAudio::IdleState
|
||||
state, otherwise returns zero.
|
||||
*/
|
||||
|
||||
int QAudioOutput::bytesFree() const
|
||||
{
|
||||
return d->bytesFree();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the period size in bytes.
|
||||
|
||||
Note: This is the recommended write size in bytes.
|
||||
*/
|
||||
|
||||
int QAudioOutput::periodSize() const
|
||||
{
|
||||
return d->periodSize();
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the audio buffer size to \a value in bytes.
|
||||
|
||||
Note: This function can be called anytime before start(), calls to this
|
||||
are ignored after start(). It should not be assumed that the buffer size
|
||||
set is the actual buffer size used, calling bufferSize() anytime after start()
|
||||
will return the actual buffer size being used.
|
||||
*/
|
||||
|
||||
void QAudioOutput::setBufferSize(int value)
|
||||
{
|
||||
d->setBufferSize(value);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the audio buffer size in bytes.
|
||||
|
||||
If called before start(), returns platform default value.
|
||||
If called before start() but setBufferSize() was called prior, returns value set by setBufferSize().
|
||||
If called after start(), returns the actual buffer size being used. This may not be what was set previously
|
||||
by setBufferSize().
|
||||
|
||||
*/
|
||||
|
||||
int QAudioOutput::bufferSize() const
|
||||
{
|
||||
return d->bufferSize();
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the interval for notify() signal to be emitted.
|
||||
This is based on the \a ms of audio data processed
|
||||
not on actual real-time.
|
||||
The minimum resolution of the timer is platform specific and values
|
||||
should be checked with notifyInterval() to confirm actual value
|
||||
being used.
|
||||
*/
|
||||
|
||||
void QAudioOutput::setNotifyInterval(int ms)
|
||||
{
|
||||
d->setNotifyInterval(ms);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the notify interval in milliseconds.
|
||||
*/
|
||||
|
||||
int QAudioOutput::notifyInterval() const
|
||||
{
|
||||
return d->notifyInterval();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the amount of audio data processed by the class since start()
|
||||
was called in microseconds.
|
||||
|
||||
Note: The amount of audio data played can be determined by subtracting
|
||||
the microseconds of audio data still in the systems audio buffer.
|
||||
|
||||
\code
|
||||
qint64 bytesInBuffer = bufferSize() - bytesFree();
|
||||
qint64 usInBuffer = (qint64)(1000000) * bytesInBuffer / ( channels() * sampleSize() / 8 ) / frequency();
|
||||
qint64 usPlayed = processedUSecs() - usInBuffer;
|
||||
\endcode
|
||||
*/
|
||||
|
||||
qint64 QAudioOutput::processedUSecs() const
|
||||
{
|
||||
return d->processedUSecs();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the microseconds since start() was called, including time in Idle and
|
||||
Suspend states.
|
||||
*/
|
||||
|
||||
qint64 QAudioOutput::elapsedUSecs() const
|
||||
{
|
||||
return d->elapsedUSecs();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the error state.
|
||||
*/
|
||||
|
||||
QAudio::Error QAudioOutput::error() const
|
||||
{
|
||||
return d->error();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the state of audio processing.
|
||||
*/
|
||||
|
||||
QAudio::State QAudioOutput::state() const
|
||||
{
|
||||
return d->state();
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QAudioOutput::stateChanged(QAudio::State state)
|
||||
This signal is emitted when the device \a state has changed.
|
||||
This is the current state of the audio output.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QAudioOutput::notify()
|
||||
This signal is emitted when x ms of audio data has been processed
|
||||
the interval set by setNotifyInterval(x).
|
||||
*/
|
||||
|
||||
QT_END_NAMESPACE
|
||||
111
src/multimedia/audio/qaudiooutput.h
Normal file
111
src/multimedia/audio/qaudiooutput.h
Normal file
@@ -0,0 +1,111 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#ifndef QAUDIOOUTPUT_H
|
||||
#define QAUDIOOUTPUT_H
|
||||
|
||||
#include <QtCore/qiodevice.h>
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
#include <QtMultimedia/qaudio.h>
|
||||
#include <QtMultimedia/qaudioformat.h>
|
||||
#include <QtMultimedia/qaudiodeviceinfo.h>
|
||||
|
||||
|
||||
QT_BEGIN_HEADER
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QT_MODULE(Multimedia)
|
||||
|
||||
|
||||
class QAbstractAudioOutput;
|
||||
|
||||
class Q_MULTIMEDIA_EXPORT QAudioOutput : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QAudioOutput(const QAudioFormat &format = QAudioFormat(), QObject *parent = 0);
|
||||
explicit QAudioOutput(const QAudioDeviceInfo &audioDeviceInfo, const QAudioFormat &format = QAudioFormat(), QObject *parent = 0);
|
||||
~QAudioOutput();
|
||||
|
||||
QAudioFormat format() const;
|
||||
|
||||
void start(QIODevice *device);
|
||||
QIODevice* start();
|
||||
|
||||
void stop();
|
||||
void reset();
|
||||
void suspend();
|
||||
void resume();
|
||||
|
||||
void setBufferSize(int bytes);
|
||||
int bufferSize() const;
|
||||
|
||||
int bytesFree() const;
|
||||
int periodSize() const;
|
||||
|
||||
void setNotifyInterval(int milliSeconds);
|
||||
int notifyInterval() const;
|
||||
|
||||
qint64 processedUSecs() const;
|
||||
qint64 elapsedUSecs() const;
|
||||
|
||||
QAudio::Error error() const;
|
||||
QAudio::State state() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void stateChanged(QAudio::State);
|
||||
void notify();
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(QAudioOutput)
|
||||
|
||||
QAbstractAudioOutput* d;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
QT_END_HEADER
|
||||
|
||||
#endif // QAUDIOOUTPUT_H
|
||||
791
src/multimedia/audio/qaudiooutput_alsa_p.cpp
Normal file
791
src/multimedia/audio/qaudiooutput_alsa_p.cpp
Normal file
@@ -0,0 +1,791 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $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.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include <QtCore/qcoreapplication.h>
|
||||
#include "qaudiooutput_alsa_p.h"
|
||||
#include "qaudiodeviceinfo_alsa_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
//#define DEBUG_AUDIO 1
|
||||
|
||||
QAudioOutputPrivate::QAudioOutputPrivate(const QByteArray &device, const QAudioFormat& audioFormat):
|
||||
settings(audioFormat)
|
||||
{
|
||||
bytesAvailable = 0;
|
||||
handle = 0;
|
||||
ahandler = 0;
|
||||
access = SND_PCM_ACCESS_RW_INTERLEAVED;
|
||||
pcmformat = SND_PCM_FORMAT_S16;
|
||||
buffer_frames = 0;
|
||||
period_frames = 0;
|
||||
buffer_size = 0;
|
||||
period_size = 0;
|
||||
buffer_time = 100000;
|
||||
period_time = 20000;
|
||||
totalTimeValue = 0;
|
||||
intervalTime = 1000;
|
||||
audioBuffer = 0;
|
||||
errorState = QAudio::NoError;
|
||||
deviceState = QAudio::StoppedState;
|
||||
audioSource = 0;
|
||||
pullMode = true;
|
||||
resuming = false;
|
||||
opened = false;
|
||||
|
||||
m_device = device;
|
||||
|
||||
timer = new QTimer(this);
|
||||
connect(timer,SIGNAL(timeout()),SLOT(userFeed()));
|
||||
}
|
||||
|
||||
QAudioOutputPrivate::~QAudioOutputPrivate()
|
||||
{
|
||||
close();
|
||||
disconnect(timer, SIGNAL(timeout()));
|
||||
QCoreApplication::processEvents();
|
||||
delete timer;
|
||||
}
|
||||
|
||||
QAudio::Error QAudioOutputPrivate::error() const
|
||||
{
|
||||
return errorState;
|
||||
}
|
||||
|
||||
QAudio::State QAudioOutputPrivate::state() const
|
||||
{
|
||||
return deviceState;
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::async_callback(snd_async_handler_t *ahandler)
|
||||
{
|
||||
QAudioOutputPrivate* audioOut;
|
||||
|
||||
audioOut = static_cast<QAudioOutputPrivate*>
|
||||
(snd_async_handler_get_callback_private(ahandler));
|
||||
|
||||
if((audioOut->deviceState==QAudio::ActiveState)||(audioOut->resuming))
|
||||
audioOut->feedback();
|
||||
}
|
||||
|
||||
int QAudioOutputPrivate::xrun_recovery(int err)
|
||||
{
|
||||
int count = 0;
|
||||
bool reset = false;
|
||||
|
||||
if(err == -EPIPE) {
|
||||
errorState = QAudio::UnderrunError;
|
||||
err = snd_pcm_prepare(handle);
|
||||
if(err < 0)
|
||||
reset = true;
|
||||
|
||||
} else if((err == -ESTRPIPE)||(err == -EIO)) {
|
||||
errorState = QAudio::IOError;
|
||||
while((err = snd_pcm_resume(handle)) == -EAGAIN){
|
||||
usleep(100);
|
||||
count++;
|
||||
if(count > 5) {
|
||||
reset = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(err < 0) {
|
||||
err = snd_pcm_prepare(handle);
|
||||
if(err < 0)
|
||||
reset = true;
|
||||
}
|
||||
}
|
||||
if(reset) {
|
||||
close();
|
||||
open();
|
||||
snd_pcm_prepare(handle);
|
||||
return 0;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int QAudioOutputPrivate::setFormat()
|
||||
{
|
||||
snd_pcm_format_t pcmformat = SND_PCM_FORMAT_UNKNOWN;
|
||||
|
||||
if(settings.sampleSize() == 8) {
|
||||
pcmformat = SND_PCM_FORMAT_U8;
|
||||
|
||||
} else if(settings.sampleSize() == 16) {
|
||||
if(settings.sampleType() == QAudioFormat::SignedInt) {
|
||||
if(settings.byteOrder() == QAudioFormat::LittleEndian)
|
||||
pcmformat = SND_PCM_FORMAT_S16_LE;
|
||||
else
|
||||
pcmformat = SND_PCM_FORMAT_S16_BE;
|
||||
} else if(settings.sampleType() == QAudioFormat::UnSignedInt) {
|
||||
if(settings.byteOrder() == QAudioFormat::LittleEndian)
|
||||
pcmformat = SND_PCM_FORMAT_U16_LE;
|
||||
else
|
||||
pcmformat = SND_PCM_FORMAT_U16_BE;
|
||||
}
|
||||
} else if(settings.sampleSize() == 24) {
|
||||
if(settings.sampleType() == QAudioFormat::SignedInt) {
|
||||
if(settings.byteOrder() == QAudioFormat::LittleEndian)
|
||||
pcmformat = SND_PCM_FORMAT_S24_LE;
|
||||
else
|
||||
pcmformat = SND_PCM_FORMAT_S24_BE;
|
||||
} else if(settings.sampleType() == QAudioFormat::UnSignedInt) {
|
||||
if(settings.byteOrder() == QAudioFormat::LittleEndian)
|
||||
pcmformat = SND_PCM_FORMAT_U24_LE;
|
||||
else
|
||||
pcmformat = SND_PCM_FORMAT_U24_BE;
|
||||
}
|
||||
} else if(settings.sampleSize() == 32) {
|
||||
if(settings.sampleType() == QAudioFormat::SignedInt) {
|
||||
if(settings.byteOrder() == QAudioFormat::LittleEndian)
|
||||
pcmformat = SND_PCM_FORMAT_S32_LE;
|
||||
else
|
||||
pcmformat = SND_PCM_FORMAT_S32_BE;
|
||||
} else if(settings.sampleType() == QAudioFormat::UnSignedInt) {
|
||||
if(settings.byteOrder() == QAudioFormat::LittleEndian)
|
||||
pcmformat = SND_PCM_FORMAT_U32_LE;
|
||||
else
|
||||
pcmformat = SND_PCM_FORMAT_U32_BE;
|
||||
} else if(settings.sampleType() == QAudioFormat::Float) {
|
||||
if(settings.byteOrder() == QAudioFormat::LittleEndian)
|
||||
pcmformat = SND_PCM_FORMAT_FLOAT_LE;
|
||||
else
|
||||
pcmformat = SND_PCM_FORMAT_FLOAT_BE;
|
||||
}
|
||||
} else if(settings.sampleSize() == 64) {
|
||||
if(settings.byteOrder() == QAudioFormat::LittleEndian)
|
||||
pcmformat = SND_PCM_FORMAT_FLOAT64_LE;
|
||||
else
|
||||
pcmformat = SND_PCM_FORMAT_FLOAT64_BE;
|
||||
}
|
||||
|
||||
return pcmformat != SND_PCM_FORMAT_UNKNOWN
|
||||
? snd_pcm_hw_params_set_format( handle, hwparams, pcmformat)
|
||||
: -1;
|
||||
}
|
||||
|
||||
QIODevice* QAudioOutputPrivate::start(QIODevice* device)
|
||||
{
|
||||
if(deviceState != QAudio::StoppedState)
|
||||
deviceState = QAudio::StoppedState;
|
||||
|
||||
errorState = QAudio::NoError;
|
||||
|
||||
// Handle change of mode
|
||||
if(audioSource && pullMode && !device) {
|
||||
// pull -> push
|
||||
close();
|
||||
audioSource = 0;
|
||||
} else if(audioSource && !pullMode && device) {
|
||||
// push -> pull
|
||||
close();
|
||||
delete audioSource;
|
||||
audioSource = 0;
|
||||
}
|
||||
|
||||
if(device) {
|
||||
//set to pull mode
|
||||
pullMode = true;
|
||||
audioSource = device;
|
||||
deviceState = QAudio::ActiveState;
|
||||
} else {
|
||||
//set to push mode
|
||||
if(!audioSource) {
|
||||
audioSource = new OutputPrivate(this);
|
||||
audioSource->open(QIODevice::WriteOnly|QIODevice::Unbuffered);
|
||||
}
|
||||
pullMode = false;
|
||||
deviceState = QAudio::IdleState;
|
||||
}
|
||||
|
||||
open();
|
||||
|
||||
emit stateChanged(deviceState);
|
||||
|
||||
return audioSource;
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::stop()
|
||||
{
|
||||
if(deviceState == QAudio::StoppedState)
|
||||
return;
|
||||
errorState = QAudio::NoError;
|
||||
deviceState = QAudio::StoppedState;
|
||||
close();
|
||||
emit stateChanged(deviceState);
|
||||
}
|
||||
|
||||
bool QAudioOutputPrivate::open()
|
||||
{
|
||||
if(opened)
|
||||
return true;
|
||||
|
||||
#ifdef DEBUG_AUDIO
|
||||
QTime now(QTime::currentTime());
|
||||
qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()";
|
||||
#endif
|
||||
timeStamp.restart();
|
||||
elapsedTimeOffset = 0;
|
||||
|
||||
int dir;
|
||||
int err = 0;
|
||||
int count=0;
|
||||
unsigned int freakuency=settings.frequency();
|
||||
|
||||
if (!settings.isValid()) {
|
||||
qWarning("QAudioOutput: open error, invalid format.");
|
||||
} else if (settings.frequency() <= 0) {
|
||||
qWarning("QAudioOutput: open error, invalid sample rate (%d).",
|
||||
settings.frequency());
|
||||
} else {
|
||||
err = -1;
|
||||
}
|
||||
|
||||
if (err == 0) {
|
||||
errorState = QAudio::OpenError;
|
||||
deviceState = QAudio::StoppedState;
|
||||
return false;
|
||||
}
|
||||
|
||||
QString dev = QString(QLatin1String(m_device.constData()));
|
||||
QList<QByteArray> devices = QAudioDeviceInfoInternal::availableDevices(QAudio::AudioOutput);
|
||||
if(dev.compare(QLatin1String("default")) == 0) {
|
||||
#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
|
||||
dev = QLatin1String(devices.first());
|
||||
#else
|
||||
dev = QLatin1String("hw:0,0");
|
||||
#endif
|
||||
} else {
|
||||
#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
|
||||
dev = QLatin1String(m_device);
|
||||
#else
|
||||
int idx = 0;
|
||||
char *name;
|
||||
|
||||
QString shortName = QLatin1String(m_device.mid(m_device.indexOf('=',0)+1).constData());
|
||||
|
||||
while(snd_card_get_name(idx,&name) == 0) {
|
||||
if(qstrncmp(shortName.toLocal8Bit().constData(),name,shortName.length()) == 0)
|
||||
break;
|
||||
idx++;
|
||||
}
|
||||
dev = QString(QLatin1String("hw:%1,0")).arg(idx);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Step 1: try and open the device
|
||||
while((count < 5) && (err < 0)) {
|
||||
err=snd_pcm_open(&handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_PLAYBACK,0);
|
||||
if(err < 0)
|
||||
count++;
|
||||
}
|
||||
if (( err < 0)||(handle == 0)) {
|
||||
errorState = QAudio::OpenError;
|
||||
deviceState = QAudio::StoppedState;
|
||||
return false;
|
||||
}
|
||||
snd_pcm_nonblock( handle, 0 );
|
||||
|
||||
// Step 2: Set the desired HW parameters.
|
||||
snd_pcm_hw_params_alloca( &hwparams );
|
||||
|
||||
bool fatal = false;
|
||||
QString errMessage;
|
||||
unsigned int chunks = 8;
|
||||
|
||||
err = snd_pcm_hw_params_any( handle, hwparams );
|
||||
if ( err < 0 ) {
|
||||
fatal = true;
|
||||
errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_any: err = %1").arg(err);
|
||||
}
|
||||
if ( !fatal ) {
|
||||
err = snd_pcm_hw_params_set_rate_resample( handle, hwparams, 1 );
|
||||
if ( err < 0 ) {
|
||||
fatal = true;
|
||||
errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_rate_resample: err = %1").arg(err);
|
||||
}
|
||||
}
|
||||
if ( !fatal ) {
|
||||
err = snd_pcm_hw_params_set_access( handle, hwparams, access );
|
||||
if ( err < 0 ) {
|
||||
fatal = true;
|
||||
errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_access: err = %1").arg(err);
|
||||
}
|
||||
}
|
||||
if ( !fatal ) {
|
||||
err = setFormat();
|
||||
if ( err < 0 ) {
|
||||
fatal = true;
|
||||
errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_format: err = %1").arg(err);
|
||||
}
|
||||
}
|
||||
if ( !fatal ) {
|
||||
err = snd_pcm_hw_params_set_channels( handle, hwparams, (unsigned int)settings.channels() );
|
||||
if ( err < 0 ) {
|
||||
fatal = true;
|
||||
errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_channels: err = %1").arg(err);
|
||||
}
|
||||
}
|
||||
if ( !fatal ) {
|
||||
err = snd_pcm_hw_params_set_rate_near( handle, hwparams, &freakuency, 0 );
|
||||
if ( err < 0 ) {
|
||||
fatal = true;
|
||||
errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_rate_near: err = %1").arg(err);
|
||||
}
|
||||
}
|
||||
if ( !fatal ) {
|
||||
unsigned int maxBufferTime = 0;
|
||||
unsigned int minBufferTime = 0;
|
||||
unsigned int maxPeriodTime = 0;
|
||||
unsigned int minPeriodTime = 0;
|
||||
|
||||
err = snd_pcm_hw_params_get_buffer_time_max(hwparams, &maxBufferTime, &dir);
|
||||
if ( err >= 0)
|
||||
err = snd_pcm_hw_params_get_buffer_time_min(hwparams, &minBufferTime, &dir);
|
||||
if ( err >= 0)
|
||||
err = snd_pcm_hw_params_get_period_time_max(hwparams, &maxPeriodTime, &dir);
|
||||
if ( err >= 0)
|
||||
err = snd_pcm_hw_params_get_period_time_min(hwparams, &minPeriodTime, &dir);
|
||||
|
||||
if ( err < 0 ) {
|
||||
fatal = true;
|
||||
errMessage = QString::fromLatin1("QAudioOutput: buffer/period min and max: err = %1").arg(err);
|
||||
} else {
|
||||
if (maxBufferTime < buffer_time || buffer_time < minBufferTime || maxPeriodTime < period_time || minPeriodTime > period_time) {
|
||||
#ifdef DEBUG_AUDIO
|
||||
qDebug()<<"defaults out of range";
|
||||
qDebug()<<"pmin="<<minPeriodTime<<", pmax="<<maxPeriodTime<<", bmin="<<minBufferTime<<", bmax="<<maxBufferTime;
|
||||
#endif
|
||||
period_time = minPeriodTime;
|
||||
if (period_time*4 <= maxBufferTime) {
|
||||
// Use 4 periods if possible
|
||||
buffer_time = period_time*4;
|
||||
chunks = 4;
|
||||
} else if (period_time*2 <= maxBufferTime) {
|
||||
// Use 2 periods if possible
|
||||
buffer_time = period_time*2;
|
||||
chunks = 2;
|
||||
} else {
|
||||
qWarning()<<"QAudioOutput: alsa only supports single period!";
|
||||
fatal = true;
|
||||
}
|
||||
#ifdef DEBUG_AUDIO
|
||||
qDebug()<<"used: buffer_time="<<buffer_time<<", period_time="<<period_time;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( !fatal ) {
|
||||
err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, &dir);
|
||||
if ( err < 0 ) {
|
||||
fatal = true;
|
||||
errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_buffer_time_near: err = %1").arg(err);
|
||||
}
|
||||
}
|
||||
if ( !fatal ) {
|
||||
err = snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, &dir);
|
||||
if ( err < 0 ) {
|
||||
fatal = true;
|
||||
errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_period_time_near: err = %1").arg(err);
|
||||
}
|
||||
}
|
||||
if ( !fatal ) {
|
||||
err = snd_pcm_hw_params_set_periods_near(handle, hwparams, &chunks, &dir);
|
||||
if ( err < 0 ) {
|
||||
fatal = true;
|
||||
errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_periods_near: err = %1").arg(err);
|
||||
}
|
||||
}
|
||||
if ( !fatal ) {
|
||||
err = snd_pcm_hw_params(handle, hwparams);
|
||||
if ( err < 0 ) {
|
||||
fatal = true;
|
||||
errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params: err = %1").arg(err);
|
||||
}
|
||||
}
|
||||
if( err < 0) {
|
||||
qWarning()<<errMessage;
|
||||
errorState = QAudio::OpenError;
|
||||
deviceState = QAudio::StoppedState;
|
||||
return false;
|
||||
}
|
||||
snd_pcm_hw_params_get_buffer_size(hwparams,&buffer_frames);
|
||||
buffer_size = snd_pcm_frames_to_bytes(handle,buffer_frames);
|
||||
snd_pcm_hw_params_get_period_size(hwparams,&period_frames, &dir);
|
||||
period_size = snd_pcm_frames_to_bytes(handle,period_frames);
|
||||
snd_pcm_hw_params_get_buffer_time(hwparams,&buffer_time, &dir);
|
||||
snd_pcm_hw_params_get_period_time(hwparams,&period_time, &dir);
|
||||
|
||||
// Step 3: Set the desired SW parameters.
|
||||
snd_pcm_sw_params_t *swparams;
|
||||
snd_pcm_sw_params_alloca(&swparams);
|
||||
snd_pcm_sw_params_current(handle, swparams);
|
||||
snd_pcm_sw_params_set_start_threshold(handle,swparams,period_frames);
|
||||
snd_pcm_sw_params_set_stop_threshold(handle,swparams,buffer_frames);
|
||||
snd_pcm_sw_params_set_avail_min(handle, swparams,period_frames);
|
||||
snd_pcm_sw_params(handle, swparams);
|
||||
|
||||
// Step 4: Prepare audio
|
||||
if(audioBuffer == 0)
|
||||
audioBuffer = new char[snd_pcm_frames_to_bytes(handle,buffer_frames)];
|
||||
snd_pcm_prepare( handle );
|
||||
snd_pcm_start(handle);
|
||||
|
||||
// Step 5: Setup callback and timer fallback
|
||||
snd_async_add_pcm_handler(&ahandler, handle, async_callback, this);
|
||||
bytesAvailable = bytesFree();
|
||||
|
||||
// Step 6: Start audio processing
|
||||
timer->start(period_time/1000);
|
||||
|
||||
clockStamp.restart();
|
||||
timeStamp.restart();
|
||||
elapsedTimeOffset = 0;
|
||||
errorState = QAudio::NoError;
|
||||
totalTimeValue = 0;
|
||||
opened = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::close()
|
||||
{
|
||||
timer->stop();
|
||||
|
||||
if ( handle ) {
|
||||
snd_pcm_drain( handle );
|
||||
snd_pcm_close( handle );
|
||||
handle = 0;
|
||||
delete [] audioBuffer;
|
||||
audioBuffer=0;
|
||||
}
|
||||
if(!pullMode && audioSource) {
|
||||
delete audioSource;
|
||||
audioSource = 0;
|
||||
}
|
||||
opened = false;
|
||||
}
|
||||
|
||||
int QAudioOutputPrivate::bytesFree() const
|
||||
{
|
||||
if(resuming)
|
||||
return period_size;
|
||||
|
||||
if(deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState)
|
||||
return 0;
|
||||
int frames = snd_pcm_avail_update(handle);
|
||||
if((int)frames > (int)buffer_frames)
|
||||
frames = buffer_frames;
|
||||
|
||||
return snd_pcm_frames_to_bytes(handle, frames);
|
||||
}
|
||||
|
||||
qint64 QAudioOutputPrivate::write( const char *data, qint64 len )
|
||||
{
|
||||
// Write out some audio data
|
||||
if ( !handle )
|
||||
return 0;
|
||||
#ifdef DEBUG_AUDIO
|
||||
qDebug()<<"frames to write out = "<<
|
||||
snd_pcm_bytes_to_frames( handle, (int)len )<<" ("<<len<<") bytes";
|
||||
#endif
|
||||
int frames, err;
|
||||
int space = bytesFree();
|
||||
if(len < space) {
|
||||
// Just write it
|
||||
frames = snd_pcm_bytes_to_frames( handle, (int)len );
|
||||
err = snd_pcm_writei( handle, data, frames );
|
||||
} else {
|
||||
// Only write space worth
|
||||
frames = snd_pcm_bytes_to_frames( handle, (int)space );
|
||||
err = snd_pcm_writei( handle, data, frames );
|
||||
}
|
||||
if(err > 0) {
|
||||
totalTimeValue += err;
|
||||
resuming = false;
|
||||
errorState = QAudio::NoError;
|
||||
if (deviceState != QAudio::ActiveState) {
|
||||
deviceState = QAudio::ActiveState;
|
||||
emit stateChanged(deviceState);
|
||||
}
|
||||
return snd_pcm_frames_to_bytes( handle, err );
|
||||
} else
|
||||
err = xrun_recovery(err);
|
||||
|
||||
if(err < 0) {
|
||||
close();
|
||||
errorState = QAudio::FatalError;
|
||||
deviceState = QAudio::StoppedState;
|
||||
emit stateChanged(deviceState);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QAudioOutputPrivate::periodSize() const
|
||||
{
|
||||
return period_size;
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::setBufferSize(int value)
|
||||
{
|
||||
if(deviceState == QAudio::StoppedState)
|
||||
buffer_size = value;
|
||||
}
|
||||
|
||||
int QAudioOutputPrivate::bufferSize() const
|
||||
{
|
||||
return buffer_size;
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::setNotifyInterval(int ms)
|
||||
{
|
||||
intervalTime = qMax(0, ms);
|
||||
}
|
||||
|
||||
int QAudioOutputPrivate::notifyInterval() const
|
||||
{
|
||||
return intervalTime;
|
||||
}
|
||||
|
||||
qint64 QAudioOutputPrivate::processedUSecs() const
|
||||
{
|
||||
return qint64(1000000) * totalTimeValue / settings.frequency();
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::resume()
|
||||
{
|
||||
if(deviceState == QAudio::SuspendedState) {
|
||||
int err = 0;
|
||||
|
||||
if(handle) {
|
||||
err = snd_pcm_prepare( handle );
|
||||
if(err < 0)
|
||||
xrun_recovery(err);
|
||||
|
||||
err = snd_pcm_start(handle);
|
||||
if(err < 0)
|
||||
xrun_recovery(err);
|
||||
|
||||
bytesAvailable = (int)snd_pcm_frames_to_bytes(handle, buffer_frames);
|
||||
}
|
||||
resuming = true;
|
||||
|
||||
deviceState = QAudio::ActiveState;
|
||||
|
||||
errorState = QAudio::NoError;
|
||||
timer->start(period_time/1000);
|
||||
emit stateChanged(deviceState);
|
||||
}
|
||||
}
|
||||
|
||||
QAudioFormat QAudioOutputPrivate::format() const
|
||||
{
|
||||
return settings;
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::suspend()
|
||||
{
|
||||
if(deviceState == QAudio::ActiveState || deviceState == QAudio::IdleState || resuming) {
|
||||
timer->stop();
|
||||
deviceState = QAudio::SuspendedState;
|
||||
errorState = QAudio::NoError;
|
||||
emit stateChanged(deviceState);
|
||||
}
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::userFeed()
|
||||
{
|
||||
if(deviceState == QAudio::StoppedState || deviceState == QAudio::SuspendedState)
|
||||
return;
|
||||
#ifdef DEBUG_AUDIO
|
||||
QTime now(QTime::currentTime());
|
||||
qDebug()<<now.second()<<"s "<<now.msec()<<"ms :userFeed() OUT";
|
||||
#endif
|
||||
if(deviceState == QAudio::IdleState)
|
||||
bytesAvailable = bytesFree();
|
||||
|
||||
deviceReady();
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::feedback()
|
||||
{
|
||||
updateAvailable();
|
||||
}
|
||||
|
||||
|
||||
void QAudioOutputPrivate::updateAvailable()
|
||||
{
|
||||
#ifdef DEBUG_AUDIO
|
||||
QTime now(QTime::currentTime());
|
||||
qDebug()<<now.second()<<"s "<<now.msec()<<"ms :updateAvailable()";
|
||||
#endif
|
||||
bytesAvailable = bytesFree();
|
||||
}
|
||||
|
||||
bool QAudioOutputPrivate::deviceReady()
|
||||
{
|
||||
if(pullMode) {
|
||||
int l = 0;
|
||||
int chunks = bytesAvailable/period_size;
|
||||
if(chunks==0) {
|
||||
bytesAvailable = bytesFree();
|
||||
return false;
|
||||
}
|
||||
#ifdef DEBUG_AUDIO
|
||||
qDebug()<<"deviceReady() avail="<<bytesAvailable<<" bytes, period size="<<period_size<<" bytes";
|
||||
qDebug()<<"deviceReady() no. of chunks that can fit ="<<chunks<<", chunks in bytes ="<<period_size*chunks;
|
||||
#endif
|
||||
int input = period_frames*chunks;
|
||||
if(input > (int)buffer_frames)
|
||||
input = buffer_frames;
|
||||
l = audioSource->read(audioBuffer,snd_pcm_frames_to_bytes(handle, input));
|
||||
if(l > 0) {
|
||||
// Got some data to output
|
||||
if(deviceState != QAudio::ActiveState)
|
||||
return true;
|
||||
qint64 bytesWritten = write(audioBuffer,l);
|
||||
if (bytesWritten != l)
|
||||
audioSource->seek(audioSource->pos()-(l-bytesWritten));
|
||||
bytesAvailable = bytesFree();
|
||||
|
||||
} else if(l == 0) {
|
||||
// Did not get any data to output
|
||||
bytesAvailable = bytesFree();
|
||||
if(bytesAvailable > snd_pcm_frames_to_bytes(handle, buffer_frames-period_frames)) {
|
||||
// Underrun
|
||||
if (deviceState != QAudio::IdleState) {
|
||||
errorState = QAudio::UnderrunError;
|
||||
deviceState = QAudio::IdleState;
|
||||
emit stateChanged(deviceState);
|
||||
}
|
||||
}
|
||||
|
||||
} else if(l < 0) {
|
||||
close();
|
||||
deviceState = QAudio::StoppedState;
|
||||
errorState = QAudio::IOError;
|
||||
emit stateChanged(deviceState);
|
||||
}
|
||||
} else {
|
||||
bytesAvailable = bytesFree();
|
||||
if(bytesAvailable > snd_pcm_frames_to_bytes(handle, buffer_frames-period_frames)) {
|
||||
// Underrun
|
||||
if (deviceState != QAudio::IdleState) {
|
||||
errorState = QAudio::UnderrunError;
|
||||
deviceState = QAudio::IdleState;
|
||||
emit stateChanged(deviceState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(deviceState != QAudio::ActiveState)
|
||||
return true;
|
||||
|
||||
if(intervalTime && (timeStamp.elapsed() + elapsedTimeOffset) > intervalTime) {
|
||||
emit notify();
|
||||
elapsedTimeOffset = timeStamp.elapsed() + elapsedTimeOffset - intervalTime;
|
||||
timeStamp.restart();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
qint64 QAudioOutputPrivate::elapsedUSecs() const
|
||||
{
|
||||
if (deviceState == QAudio::StoppedState)
|
||||
return 0;
|
||||
|
||||
return clockStamp.elapsed()*1000;
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::reset()
|
||||
{
|
||||
if(handle)
|
||||
snd_pcm_reset(handle);
|
||||
|
||||
stop();
|
||||
}
|
||||
|
||||
OutputPrivate::OutputPrivate(QAudioOutputPrivate* audio)
|
||||
{
|
||||
audioDevice = qobject_cast<QAudioOutputPrivate*>(audio);
|
||||
}
|
||||
|
||||
OutputPrivate::~OutputPrivate() {}
|
||||
|
||||
qint64 OutputPrivate::readData( char* data, qint64 len)
|
||||
{
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(len)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
qint64 OutputPrivate::writeData(const char* data, qint64 len)
|
||||
{
|
||||
int retry = 0;
|
||||
qint64 written = 0;
|
||||
if((audioDevice->deviceState == QAudio::ActiveState)
|
||||
||(audioDevice->deviceState == QAudio::IdleState)) {
|
||||
while(written < len) {
|
||||
int chunk = audioDevice->write(data+written,(len-written));
|
||||
if(chunk <= 0)
|
||||
retry++;
|
||||
written+=chunk;
|
||||
if(retry > 10)
|
||||
return written;
|
||||
}
|
||||
}
|
||||
return written;
|
||||
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
166
src/multimedia/audio/qaudiooutput_alsa_p.h
Normal file
166
src/multimedia/audio/qaudiooutput_alsa_p.h
Normal file
@@ -0,0 +1,166 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $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.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#ifndef QAUDIOOUTPUTALSA_H
|
||||
#define QAUDIOOUTPUTALSA_H
|
||||
|
||||
#include <alsa/asoundlib.h>
|
||||
|
||||
#include <QtCore/qfile.h>
|
||||
#include <QtCore/qdebug.h>
|
||||
#include <QtCore/qtimer.h>
|
||||
#include <QtCore/qstring.h>
|
||||
#include <QtCore/qstringlist.h>
|
||||
#include <QtCore/qelapsedtimer.h>
|
||||
#include <QtCore/qdatetime.h>
|
||||
|
||||
#include <QtMultimedia/qaudio.h>
|
||||
#include <QtMultimedia/qaudiodeviceinfo.h>
|
||||
#include <QtMultimedia/qaudioengine.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class OutputPrivate;
|
||||
|
||||
class QAudioOutputPrivate : public QAbstractAudioOutput
|
||||
{
|
||||
friend class OutputPrivate;
|
||||
Q_OBJECT
|
||||
public:
|
||||
QAudioOutputPrivate(const QByteArray &device, const QAudioFormat& audioFormat);
|
||||
~QAudioOutputPrivate();
|
||||
|
||||
qint64 write( const char *data, qint64 len );
|
||||
|
||||
QIODevice* start(QIODevice* device = 0);
|
||||
void stop();
|
||||
void reset();
|
||||
void suspend();
|
||||
void resume();
|
||||
int bytesFree() const;
|
||||
int periodSize() const;
|
||||
void setBufferSize(int value);
|
||||
int bufferSize() const;
|
||||
void setNotifyInterval(int milliSeconds);
|
||||
int notifyInterval() const;
|
||||
qint64 processedUSecs() const;
|
||||
qint64 elapsedUSecs() const;
|
||||
QAudio::Error error() const;
|
||||
QAudio::State state() const;
|
||||
QAudioFormat format() const;
|
||||
|
||||
QIODevice* audioSource;
|
||||
QAudioFormat settings;
|
||||
QAudio::Error errorState;
|
||||
QAudio::State deviceState;
|
||||
|
||||
private slots:
|
||||
void userFeed();
|
||||
void feedback();
|
||||
void updateAvailable();
|
||||
bool deviceReady();
|
||||
|
||||
signals:
|
||||
void processMore();
|
||||
|
||||
private:
|
||||
bool opened;
|
||||
bool pullMode;
|
||||
bool resuming;
|
||||
int buffer_size;
|
||||
int period_size;
|
||||
int intervalTime;
|
||||
qint64 totalTimeValue;
|
||||
unsigned int buffer_time;
|
||||
unsigned int period_time;
|
||||
snd_pcm_uframes_t buffer_frames;
|
||||
snd_pcm_uframes_t period_frames;
|
||||
static void async_callback(snd_async_handler_t *ahandler);
|
||||
int xrun_recovery(int err);
|
||||
|
||||
int setFormat();
|
||||
bool open();
|
||||
void close();
|
||||
|
||||
QTimer* timer;
|
||||
QByteArray m_device;
|
||||
int bytesAvailable;
|
||||
QElapsedTimer timeStamp;
|
||||
QElapsedTimer clockStamp;
|
||||
qint64 elapsedTimeOffset;
|
||||
char* audioBuffer;
|
||||
snd_pcm_t* handle;
|
||||
snd_async_handler_t* ahandler;
|
||||
snd_pcm_access_t access;
|
||||
snd_pcm_format_t pcmformat;
|
||||
snd_timestamp_t* timestamp;
|
||||
snd_pcm_hw_params_t *hwparams;
|
||||
};
|
||||
|
||||
class OutputPrivate : public QIODevice
|
||||
{
|
||||
friend class QAudioOutputPrivate;
|
||||
Q_OBJECT
|
||||
public:
|
||||
OutputPrivate(QAudioOutputPrivate* audio);
|
||||
~OutputPrivate();
|
||||
|
||||
qint64 readData( char* data, qint64 len);
|
||||
qint64 writeData(const char* data, qint64 len);
|
||||
|
||||
private:
|
||||
QAudioOutputPrivate *audioDevice;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
703
src/multimedia/audio/qaudiooutput_mac_p.cpp
Normal file
703
src/multimedia/audio/qaudiooutput_mac_p.cpp
Normal file
@@ -0,0 +1,703 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $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.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#include <CoreAudio/CoreAudio.h>
|
||||
#include <AudioUnit/AudioUnit.h>
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
|
||||
#include <QtCore/qendian.h>
|
||||
#include <QtCore/qbuffer.h>
|
||||
#include <QtCore/qtimer.h>
|
||||
#include <QtCore/qdebug.h>
|
||||
|
||||
#include <QtMultimedia/qaudiooutput.h>
|
||||
|
||||
#include "qaudio_mac_p.h"
|
||||
#include "qaudiooutput_mac_p.h"
|
||||
#include "qaudiodeviceinfo_mac_p.h"
|
||||
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
||||
namespace QtMultimediaInternal
|
||||
{
|
||||
|
||||
static const int default_buffer_size = 8 * 1024;
|
||||
|
||||
|
||||
class QAudioOutputBuffer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QAudioOutputBuffer(int bufferSize, int maxPeriodSize, QAudioFormat const& audioFormat):
|
||||
m_deviceError(false),
|
||||
m_maxPeriodSize(maxPeriodSize),
|
||||
m_device(0)
|
||||
{
|
||||
m_buffer = new QAudioRingBuffer(bufferSize + (bufferSize % maxPeriodSize == 0 ? 0 : maxPeriodSize - (bufferSize % maxPeriodSize)));
|
||||
m_bytesPerFrame = (audioFormat.sampleSize() / 8) * audioFormat.channels();
|
||||
m_periodTime = m_maxPeriodSize / m_bytesPerFrame * 1000 / audioFormat.frequency();
|
||||
|
||||
m_fillTimer = new QTimer(this);
|
||||
connect(m_fillTimer, SIGNAL(timeout()), SLOT(fillBuffer()));
|
||||
}
|
||||
|
||||
~QAudioOutputBuffer()
|
||||
{
|
||||
delete m_buffer;
|
||||
}
|
||||
|
||||
qint64 readFrames(char* data, qint64 maxFrames)
|
||||
{
|
||||
bool wecan = true;
|
||||
qint64 framesRead = 0;
|
||||
|
||||
while (wecan && framesRead < maxFrames) {
|
||||
QAudioRingBuffer::Region region = m_buffer->acquireReadRegion((maxFrames - framesRead) * m_bytesPerFrame);
|
||||
|
||||
if (region.second > 0) {
|
||||
region.second -= region.second % m_bytesPerFrame;
|
||||
memcpy(data + (framesRead * m_bytesPerFrame), region.first, region.second);
|
||||
framesRead += region.second / m_bytesPerFrame;
|
||||
}
|
||||
else
|
||||
wecan = false;
|
||||
|
||||
m_buffer->releaseReadRegion(region);
|
||||
}
|
||||
|
||||
if (framesRead == 0 && m_deviceError)
|
||||
framesRead = -1;
|
||||
|
||||
return framesRead;
|
||||
}
|
||||
|
||||
qint64 writeBytes(const char* data, qint64 maxSize)
|
||||
{
|
||||
bool wecan = true;
|
||||
qint64 bytesWritten = 0;
|
||||
|
||||
maxSize -= maxSize % m_bytesPerFrame;
|
||||
while (wecan && bytesWritten < maxSize) {
|
||||
QAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(maxSize - bytesWritten);
|
||||
|
||||
if (region.second > 0) {
|
||||
memcpy(region.first, data + bytesWritten, region.second);
|
||||
bytesWritten += region.second;
|
||||
}
|
||||
else
|
||||
wecan = false;
|
||||
|
||||
m_buffer->releaseWriteRegion(region);
|
||||
}
|
||||
|
||||
if (bytesWritten > 0)
|
||||
emit readyRead();
|
||||
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
int available() const
|
||||
{
|
||||
return m_buffer->free();
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
m_buffer->reset();
|
||||
m_deviceError = false;
|
||||
}
|
||||
|
||||
void setPrefetchDevice(QIODevice* device)
|
||||
{
|
||||
if (m_device != device) {
|
||||
m_device = device;
|
||||
if (m_device != 0)
|
||||
fillBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
void startFillTimer()
|
||||
{
|
||||
if (m_device != 0)
|
||||
m_fillTimer->start(m_buffer->size() / 2 / m_maxPeriodSize * m_periodTime);
|
||||
}
|
||||
|
||||
void stopFillTimer()
|
||||
{
|
||||
m_fillTimer->stop();
|
||||
}
|
||||
|
||||
signals:
|
||||
void readyRead();
|
||||
|
||||
private slots:
|
||||
void fillBuffer()
|
||||
{
|
||||
const int free = m_buffer->free();
|
||||
const int writeSize = free - (free % m_maxPeriodSize);
|
||||
|
||||
if (writeSize > 0) {
|
||||
bool wecan = true;
|
||||
int filled = 0;
|
||||
|
||||
while (!m_deviceError && wecan && filled < writeSize) {
|
||||
QAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(writeSize - filled);
|
||||
|
||||
if (region.second > 0) {
|
||||
region.second = m_device->read(region.first, region.second);
|
||||
if (region.second > 0)
|
||||
filled += region.second;
|
||||
else if (region.second == 0)
|
||||
wecan = false;
|
||||
else if (region.second < 0) {
|
||||
m_fillTimer->stop();
|
||||
region.second = 0;
|
||||
m_deviceError = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
wecan = false;
|
||||
|
||||
m_buffer->releaseWriteRegion(region);
|
||||
}
|
||||
|
||||
if (filled > 0)
|
||||
emit readyRead();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_deviceError;
|
||||
int m_maxPeriodSize;
|
||||
int m_bytesPerFrame;
|
||||
int m_periodTime;
|
||||
QIODevice* m_device;
|
||||
QTimer* m_fillTimer;
|
||||
QAudioRingBuffer* m_buffer;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
class MacOutputDevice : public QIODevice
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacOutputDevice(QtMultimediaInternal::QAudioOutputBuffer* audioBuffer, QObject* parent):
|
||||
QIODevice(parent),
|
||||
m_audioBuffer(audioBuffer)
|
||||
{
|
||||
open(QIODevice::WriteOnly | QIODevice::Unbuffered);
|
||||
}
|
||||
|
||||
qint64 readData(char* data, qint64 len)
|
||||
{
|
||||
Q_UNUSED(data);
|
||||
Q_UNUSED(len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
qint64 writeData(const char* data, qint64 len)
|
||||
{
|
||||
return m_audioBuffer->writeBytes(data, len);
|
||||
}
|
||||
|
||||
bool isSequential() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
QtMultimediaInternal::QAudioOutputBuffer* m_audioBuffer;
|
||||
};
|
||||
|
||||
|
||||
QAudioOutputPrivate::QAudioOutputPrivate(const QByteArray& device, const QAudioFormat& format):
|
||||
audioFormat(format)
|
||||
{
|
||||
QDataStream ds(device);
|
||||
quint32 did, mode;
|
||||
|
||||
ds >> did >> mode;
|
||||
|
||||
if (QAudio::Mode(mode) == QAudio::AudioInput)
|
||||
errorCode = QAudio::OpenError;
|
||||
else {
|
||||
audioDeviceInfo = new QAudioDeviceInfoInternal(device, QAudio::AudioOutput);
|
||||
isOpen = false;
|
||||
audioDeviceId = AudioDeviceID(did);
|
||||
audioUnit = 0;
|
||||
audioIO = 0;
|
||||
startTime = 0;
|
||||
totalFrames = 0;
|
||||
audioBuffer = 0;
|
||||
internalBufferSize = QtMultimediaInternal::default_buffer_size;
|
||||
clockFrequency = AudioGetHostClockFrequency() / 1000;
|
||||
errorCode = QAudio::NoError;
|
||||
stateCode = QAudio::StoppedState;
|
||||
audioThreadState = Stopped;
|
||||
|
||||
intervalTimer = new QTimer(this);
|
||||
intervalTimer->setInterval(1000);
|
||||
connect(intervalTimer, SIGNAL(timeout()), SIGNAL(notify()));
|
||||
}
|
||||
}
|
||||
|
||||
QAudioOutputPrivate::~QAudioOutputPrivate()
|
||||
{
|
||||
delete audioDeviceInfo;
|
||||
close();
|
||||
}
|
||||
|
||||
bool QAudioOutputPrivate::open()
|
||||
{
|
||||
if (errorCode != QAudio::NoError)
|
||||
return false;
|
||||
|
||||
if (isOpen)
|
||||
return true;
|
||||
|
||||
ComponentDescription cd;
|
||||
cd.componentType = kAudioUnitType_Output;
|
||||
cd.componentSubType = kAudioUnitSubType_HALOutput;
|
||||
cd.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
cd.componentFlags = 0;
|
||||
cd.componentFlagsMask = 0;
|
||||
|
||||
// Open
|
||||
Component cp = FindNextComponent(NULL, &cd);
|
||||
if (cp == 0) {
|
||||
qWarning() << "QAudioOutput: Failed to find HAL Output component";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (OpenAComponent(cp, &audioUnit) != noErr) {
|
||||
qWarning() << "QAudioOutput: Unable to Open Output Component";
|
||||
return false;
|
||||
}
|
||||
|
||||
// register callback
|
||||
AURenderCallbackStruct cb;
|
||||
cb.inputProc = renderCallback;
|
||||
cb.inputProcRefCon = this;
|
||||
|
||||
if (AudioUnitSetProperty(audioUnit,
|
||||
kAudioUnitProperty_SetRenderCallback,
|
||||
kAudioUnitScope_Global,
|
||||
0,
|
||||
&cb,
|
||||
sizeof(cb)) != noErr) {
|
||||
qWarning() << "QAudioOutput: Failed to set AudioUnit callback";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set Audio Device
|
||||
if (AudioUnitSetProperty(audioUnit,
|
||||
kAudioOutputUnitProperty_CurrentDevice,
|
||||
kAudioUnitScope_Global,
|
||||
0,
|
||||
&audioDeviceId,
|
||||
sizeof(audioDeviceId)) != noErr) {
|
||||
qWarning() << "QAudioOutput: Unable to use configured device";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set stream format
|
||||
streamFormat = toAudioStreamBasicDescription(audioFormat);
|
||||
|
||||
UInt32 size = sizeof(streamFormat);
|
||||
if (AudioUnitSetProperty(audioUnit,
|
||||
kAudioUnitProperty_StreamFormat,
|
||||
kAudioUnitScope_Input,
|
||||
0,
|
||||
&streamFormat,
|
||||
sizeof(streamFormat)) != noErr) {
|
||||
qWarning() << "QAudioOutput: Unable to Set Stream information";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allocate buffer
|
||||
UInt32 numberOfFrames = 0;
|
||||
size = sizeof(UInt32);
|
||||
if (AudioUnitGetProperty(audioUnit,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
kAudioUnitScope_Global,
|
||||
0,
|
||||
&numberOfFrames,
|
||||
&size) != noErr) {
|
||||
qWarning() << "QAudioInput: Failed to get audio period size";
|
||||
return false;
|
||||
}
|
||||
|
||||
periodSizeBytes = numberOfFrames * streamFormat.mBytesPerFrame;
|
||||
if (internalBufferSize < periodSizeBytes * 2)
|
||||
internalBufferSize = periodSizeBytes * 2;
|
||||
else
|
||||
internalBufferSize -= internalBufferSize % streamFormat.mBytesPerFrame;
|
||||
|
||||
audioBuffer = new QtMultimediaInternal::QAudioOutputBuffer(internalBufferSize, periodSizeBytes, audioFormat);
|
||||
connect(audioBuffer, SIGNAL(readyRead()), SLOT(inputReady())); // Pull
|
||||
|
||||
audioIO = new MacOutputDevice(audioBuffer, this);
|
||||
|
||||
// Init
|
||||
if (AudioUnitInitialize(audioUnit)) {
|
||||
qWarning() << "QAudioOutput: Failed to initialize AudioUnit";
|
||||
return false;
|
||||
}
|
||||
|
||||
isOpen = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::close()
|
||||
{
|
||||
if (audioUnit != 0) {
|
||||
AudioOutputUnitStop(audioUnit);
|
||||
AudioUnitUninitialize(audioUnit);
|
||||
CloseComponent(audioUnit);
|
||||
}
|
||||
|
||||
delete audioBuffer;
|
||||
}
|
||||
|
||||
QAudioFormat QAudioOutputPrivate::format() const
|
||||
{
|
||||
return audioFormat;
|
||||
}
|
||||
|
||||
QIODevice* QAudioOutputPrivate::start(QIODevice* device)
|
||||
{
|
||||
QIODevice* op = device;
|
||||
|
||||
if (!audioDeviceInfo->isFormatSupported(audioFormat) || !open()) {
|
||||
stateCode = QAudio::StoppedState;
|
||||
errorCode = QAudio::OpenError;
|
||||
return audioIO;
|
||||
}
|
||||
|
||||
reset();
|
||||
audioBuffer->reset();
|
||||
audioBuffer->setPrefetchDevice(op);
|
||||
|
||||
if (op == 0) {
|
||||
op = audioIO;
|
||||
stateCode = QAudio::IdleState;
|
||||
}
|
||||
else
|
||||
stateCode = QAudio::ActiveState;
|
||||
|
||||
// Start
|
||||
errorCode = QAudio::NoError;
|
||||
totalFrames = 0;
|
||||
startTime = AudioGetCurrentHostTime();
|
||||
|
||||
if (stateCode == QAudio::ActiveState)
|
||||
audioThreadStart();
|
||||
|
||||
emit stateChanged(stateCode);
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::stop()
|
||||
{
|
||||
QMutexLocker lock(&mutex);
|
||||
if (stateCode != QAudio::StoppedState) {
|
||||
audioThreadDrain();
|
||||
|
||||
stateCode = QAudio::StoppedState;
|
||||
errorCode = QAudio::NoError;
|
||||
QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode));
|
||||
}
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::reset()
|
||||
{
|
||||
QMutexLocker lock(&mutex);
|
||||
if (stateCode != QAudio::StoppedState) {
|
||||
audioThreadStop();
|
||||
|
||||
stateCode = QAudio::StoppedState;
|
||||
errorCode = QAudio::NoError;
|
||||
QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode));
|
||||
}
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::suspend()
|
||||
{
|
||||
QMutexLocker lock(&mutex);
|
||||
if (stateCode == QAudio::ActiveState || stateCode == QAudio::IdleState) {
|
||||
audioThreadStop();
|
||||
|
||||
stateCode = QAudio::SuspendedState;
|
||||
errorCode = QAudio::NoError;
|
||||
QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode));
|
||||
}
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::resume()
|
||||
{
|
||||
QMutexLocker lock(&mutex);
|
||||
if (stateCode == QAudio::SuspendedState) {
|
||||
audioThreadStart();
|
||||
|
||||
stateCode = QAudio::ActiveState;
|
||||
errorCode = QAudio::NoError;
|
||||
QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode));
|
||||
}
|
||||
}
|
||||
|
||||
int QAudioOutputPrivate::bytesFree() const
|
||||
{
|
||||
return audioBuffer->available();
|
||||
}
|
||||
|
||||
int QAudioOutputPrivate::periodSize() const
|
||||
{
|
||||
return periodSizeBytes;
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::setBufferSize(int bs)
|
||||
{
|
||||
if (stateCode == QAudio::StoppedState)
|
||||
internalBufferSize = bs;
|
||||
}
|
||||
|
||||
int QAudioOutputPrivate::bufferSize() const
|
||||
{
|
||||
return internalBufferSize;
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::setNotifyInterval(int milliSeconds)
|
||||
{
|
||||
if (intervalTimer->interval() == milliSeconds)
|
||||
return;
|
||||
|
||||
if (milliSeconds <= 0)
|
||||
milliSeconds = 0;
|
||||
|
||||
intervalTimer->setInterval(milliSeconds);
|
||||
}
|
||||
|
||||
int QAudioOutputPrivate::notifyInterval() const
|
||||
{
|
||||
return intervalTimer->interval();
|
||||
}
|
||||
|
||||
qint64 QAudioOutputPrivate::processedUSecs() const
|
||||
{
|
||||
return totalFrames * 1000000 / audioFormat.frequency();
|
||||
}
|
||||
|
||||
qint64 QAudioOutputPrivate::elapsedUSecs() const
|
||||
{
|
||||
if (stateCode == QAudio::StoppedState)
|
||||
return 0;
|
||||
|
||||
return (AudioGetCurrentHostTime() - startTime) / (clockFrequency / 1000);
|
||||
}
|
||||
|
||||
QAudio::Error QAudioOutputPrivate::error() const
|
||||
{
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
QAudio::State QAudioOutputPrivate::state() const
|
||||
{
|
||||
return stateCode;
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::audioThreadStart()
|
||||
{
|
||||
startTimers();
|
||||
audioThreadState = Running;
|
||||
AudioOutputUnitStart(audioUnit);
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::audioThreadStop()
|
||||
{
|
||||
stopTimers();
|
||||
if (audioThreadState.testAndSetAcquire(Running, Stopped))
|
||||
threadFinished.wait(&mutex);
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::audioThreadDrain()
|
||||
{
|
||||
stopTimers();
|
||||
if (audioThreadState.testAndSetAcquire(Running, Draining))
|
||||
threadFinished.wait(&mutex);
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::audioDeviceStop()
|
||||
{
|
||||
AudioOutputUnitStop(audioUnit);
|
||||
audioThreadState = Stopped;
|
||||
threadFinished.wakeOne();
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::audioDeviceIdle()
|
||||
{
|
||||
QMutexLocker lock(&mutex);
|
||||
if (stateCode == QAudio::ActiveState) {
|
||||
audioDeviceStop();
|
||||
|
||||
errorCode = QAudio::UnderrunError;
|
||||
stateCode = QAudio::IdleState;
|
||||
QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::audioDeviceError()
|
||||
{
|
||||
QMutexLocker lock(&mutex);
|
||||
if (stateCode == QAudio::ActiveState) {
|
||||
audioDeviceStop();
|
||||
|
||||
errorCode = QAudio::IOError;
|
||||
stateCode = QAudio::StoppedState;
|
||||
QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::startTimers()
|
||||
{
|
||||
audioBuffer->startFillTimer();
|
||||
if (intervalTimer->interval() > 0)
|
||||
intervalTimer->start();
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::stopTimers()
|
||||
{
|
||||
audioBuffer->stopFillTimer();
|
||||
intervalTimer->stop();
|
||||
}
|
||||
|
||||
|
||||
void QAudioOutputPrivate::deviceStopped()
|
||||
{
|
||||
intervalTimer->stop();
|
||||
emit stateChanged(stateCode);
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::inputReady()
|
||||
{
|
||||
QMutexLocker lock(&mutex);
|
||||
if (stateCode == QAudio::IdleState) {
|
||||
audioThreadStart();
|
||||
|
||||
stateCode = QAudio::ActiveState;
|
||||
errorCode = QAudio::NoError;
|
||||
|
||||
QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
OSStatus QAudioOutputPrivate::renderCallback(void* inRefCon,
|
||||
AudioUnitRenderActionFlags* ioActionFlags,
|
||||
const AudioTimeStamp* inTimeStamp,
|
||||
UInt32 inBusNumber,
|
||||
UInt32 inNumberFrames,
|
||||
AudioBufferList* ioData)
|
||||
{
|
||||
Q_UNUSED(ioActionFlags)
|
||||
Q_UNUSED(inTimeStamp)
|
||||
Q_UNUSED(inBusNumber)
|
||||
Q_UNUSED(inNumberFrames)
|
||||
|
||||
QAudioOutputPrivate* d = static_cast<QAudioOutputPrivate*>(inRefCon);
|
||||
|
||||
const int threadState = d->audioThreadState.fetchAndAddAcquire(0);
|
||||
if (threadState == Stopped) {
|
||||
ioData->mBuffers[0].mDataByteSize = 0;
|
||||
d->audioDeviceStop();
|
||||
}
|
||||
else {
|
||||
const UInt32 bytesPerFrame = d->streamFormat.mBytesPerFrame;
|
||||
qint64 framesRead;
|
||||
|
||||
framesRead = d->audioBuffer->readFrames((char*)ioData->mBuffers[0].mData,
|
||||
ioData->mBuffers[0].mDataByteSize / bytesPerFrame);
|
||||
|
||||
if (framesRead > 0) {
|
||||
ioData->mBuffers[0].mDataByteSize = framesRead * bytesPerFrame;
|
||||
d->totalFrames += framesRead;
|
||||
}
|
||||
else {
|
||||
ioData->mBuffers[0].mDataByteSize = 0;
|
||||
if (framesRead == 0) {
|
||||
if (threadState == Draining)
|
||||
d->audioDeviceStop();
|
||||
else
|
||||
d->audioDeviceIdle();
|
||||
}
|
||||
else
|
||||
d->audioDeviceError();
|
||||
}
|
||||
}
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#include "qaudiooutput_mac_p.moc"
|
||||
|
||||
169
src/multimedia/audio/qaudiooutput_mac_p.h
Normal file
169
src/multimedia/audio/qaudiooutput_mac_p.h
Normal file
@@ -0,0 +1,169 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $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.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#ifndef QAUDIOOUTPUT_MAC_P_H
|
||||
#define QAUDIOOUTPUT_MAC_P_H
|
||||
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#include <CoreAudio/CoreAudio.h>
|
||||
#include <AudioUnit/AudioUnit.h>
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
|
||||
#include <QtCore/qobject.h>
|
||||
#include <QtCore/qmutex.h>
|
||||
#include <QtCore/qwaitcondition.h>
|
||||
#include <QtCore/qtimer.h>
|
||||
#include <QtCore/qatomic.h>
|
||||
|
||||
#include <QtMultimedia/qaudio.h>
|
||||
#include <QtMultimedia/qaudioformat.h>
|
||||
#include <QtMultimedia/qaudioengine.h>
|
||||
|
||||
QT_BEGIN_HEADER
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QIODevice;
|
||||
class QAbstractAudioDeviceInfo;
|
||||
|
||||
namespace QtMultimediaInternal
|
||||
{
|
||||
class QAudioOutputBuffer;
|
||||
}
|
||||
|
||||
class QAudioOutputPrivate : public QAbstractAudioOutput
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
bool isOpen;
|
||||
int internalBufferSize;
|
||||
int periodSizeBytes;
|
||||
qint64 totalFrames;
|
||||
QAudioFormat audioFormat;
|
||||
QIODevice* audioIO;
|
||||
AudioDeviceID audioDeviceId;
|
||||
AudioUnit audioUnit;
|
||||
Float64 clockFrequency;
|
||||
UInt64 startTime;
|
||||
AudioStreamBasicDescription deviceFormat;
|
||||
AudioStreamBasicDescription streamFormat;
|
||||
QtMultimediaInternal::QAudioOutputBuffer* audioBuffer;
|
||||
QAtomicInt audioThreadState;
|
||||
QWaitCondition threadFinished;
|
||||
QMutex mutex;
|
||||
QTimer* intervalTimer;
|
||||
QAbstractAudioDeviceInfo *audioDeviceInfo;
|
||||
|
||||
QAudio::Error errorCode;
|
||||
QAudio::State stateCode;
|
||||
|
||||
QAudioOutputPrivate(const QByteArray& device, const QAudioFormat& format);
|
||||
~QAudioOutputPrivate();
|
||||
|
||||
bool open();
|
||||
void close();
|
||||
|
||||
QAudioFormat format() const;
|
||||
|
||||
QIODevice* start(QIODevice* device);
|
||||
void stop();
|
||||
void reset();
|
||||
void suspend();
|
||||
void resume();
|
||||
|
||||
int bytesFree() const;
|
||||
int periodSize() const;
|
||||
|
||||
void setBufferSize(int value);
|
||||
int bufferSize() const;
|
||||
|
||||
void setNotifyInterval(int milliSeconds);
|
||||
int notifyInterval() const;
|
||||
|
||||
qint64 processedUSecs() const;
|
||||
qint64 elapsedUSecs() const;
|
||||
|
||||
QAudio::Error error() const;
|
||||
QAudio::State state() const;
|
||||
|
||||
void audioThreadStart();
|
||||
void audioThreadStop();
|
||||
void audioThreadDrain();
|
||||
|
||||
void audioDeviceStop();
|
||||
void audioDeviceIdle();
|
||||
void audioDeviceError();
|
||||
|
||||
void startTimers();
|
||||
void stopTimers();
|
||||
|
||||
private slots:
|
||||
void deviceStopped();
|
||||
void inputReady();
|
||||
|
||||
private:
|
||||
enum { Running, Draining, Stopped };
|
||||
|
||||
static OSStatus renderCallback(void* inRefCon,
|
||||
AudioUnitRenderActionFlags* ioActionFlags,
|
||||
const AudioTimeStamp* inTimeStamp,
|
||||
UInt32 inBusNumber,
|
||||
UInt32 inNumberFrames,
|
||||
AudioBufferList* ioData);
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
QT_END_HEADER
|
||||
|
||||
#endif
|
||||
643
src/multimedia/audio/qaudiooutput_symbian_p.cpp
Normal file
643
src/multimedia/audio/qaudiooutput_symbian_p.cpp
Normal file
@@ -0,0 +1,643 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qaudiooutput_symbian_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Constants
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const int UnderflowTimerInterval = 50; // ms
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Private class
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SymbianAudioOutputPrivate::SymbianAudioOutputPrivate(
|
||||
QAudioOutputPrivate *audioDevice)
|
||||
: m_audioDevice(audioDevice)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
SymbianAudioOutputPrivate::~SymbianAudioOutputPrivate()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
qint64 SymbianAudioOutputPrivate::readData(char *data, qint64 len)
|
||||
{
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
qint64 SymbianAudioOutputPrivate::writeData(const char *data, qint64 len)
|
||||
{
|
||||
qint64 totalWritten = 0;
|
||||
|
||||
if (m_audioDevice->state() == QAudio::ActiveState ||
|
||||
m_audioDevice->state() == QAudio::IdleState) {
|
||||
|
||||
while (totalWritten < len) {
|
||||
const qint64 written = m_audioDevice->pushData(data + totalWritten,
|
||||
len - totalWritten);
|
||||
if (written > 0)
|
||||
totalWritten += written;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return totalWritten;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public functions
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
QAudioOutputPrivate::QAudioOutputPrivate(const QByteArray &device,
|
||||
const QAudioFormat &format)
|
||||
: m_device(device)
|
||||
, m_format(format)
|
||||
, m_clientBufferSize(SymbianAudio::DefaultBufferSize)
|
||||
, m_notifyInterval(SymbianAudio::DefaultNotifyInterval)
|
||||
, m_notifyTimer(new QTimer(this))
|
||||
, m_error(QAudio::NoError)
|
||||
, m_internalState(SymbianAudio::ClosedState)
|
||||
, m_externalState(QAudio::StoppedState)
|
||||
, m_pullMode(false)
|
||||
, m_source(0)
|
||||
, m_devSound(0)
|
||||
, m_devSoundBuffer(0)
|
||||
, m_devSoundBufferSize(0)
|
||||
, m_bytesWritten(0)
|
||||
, m_pushDataReady(false)
|
||||
, m_bytesPadding(0)
|
||||
, m_underflow(false)
|
||||
, m_lastBuffer(false)
|
||||
, m_underflowTimer(new QTimer(this))
|
||||
, m_samplesPlayed(0)
|
||||
, m_totalSamplesPlayed(0)
|
||||
{
|
||||
qRegisterMetaType<CMMFBuffer *>("CMMFBuffer *");
|
||||
|
||||
connect(m_notifyTimer.data(), SIGNAL(timeout()), this, SIGNAL(notify()));
|
||||
|
||||
m_underflowTimer->setInterval(UnderflowTimerInterval);
|
||||
connect(m_underflowTimer.data(), SIGNAL(timeout()), this,
|
||||
SLOT(underflowTimerExpired()));
|
||||
}
|
||||
|
||||
QAudioOutputPrivate::~QAudioOutputPrivate()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
QIODevice* QAudioOutputPrivate::start(QIODevice *device)
|
||||
{
|
||||
stop();
|
||||
|
||||
if (device) {
|
||||
m_pullMode = true;
|
||||
m_source = device;
|
||||
}
|
||||
|
||||
open();
|
||||
|
||||
if (SymbianAudio::ClosedState != m_internalState) {
|
||||
if (device) {
|
||||
connect(m_source, SIGNAL(readyRead()), this, SLOT(dataReady()));
|
||||
} else {
|
||||
m_source = new SymbianAudioOutputPrivate(this);
|
||||
m_source->open(QIODevice::WriteOnly | QIODevice::Unbuffered);
|
||||
}
|
||||
|
||||
m_elapsed.restart();
|
||||
}
|
||||
|
||||
return m_source;
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::stop()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::reset()
|
||||
{
|
||||
m_totalSamplesPlayed += getSamplesPlayed();
|
||||
m_devSound->stop();
|
||||
m_bytesPadding = 0;
|
||||
startPlayback();
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::suspend()
|
||||
{
|
||||
if (SymbianAudio::ActiveState == m_internalState
|
||||
|| SymbianAudio::IdleState == m_internalState) {
|
||||
m_notifyTimer->stop();
|
||||
m_underflowTimer->stop();
|
||||
const qint64 samplesWritten = SymbianAudio::Utils::bytesToSamples(
|
||||
m_format, m_bytesWritten);
|
||||
const qint64 samplesPlayed = getSamplesPlayed();
|
||||
m_totalSamplesPlayed += samplesPlayed;
|
||||
m_bytesWritten = 0;
|
||||
const bool paused = m_devSound->pause();
|
||||
if (paused) {
|
||||
setState(SymbianAudio::SuspendedPausedState);
|
||||
} else {
|
||||
m_devSoundBuffer = 0;
|
||||
// Calculate the amount of data dropped
|
||||
const qint64 paddingSamples = samplesWritten - samplesPlayed;
|
||||
Q_ASSERT(paddingSamples >= 0);
|
||||
m_bytesPadding = SymbianAudio::Utils::samplesToBytes(m_format,
|
||||
paddingSamples);
|
||||
setState(SymbianAudio::SuspendedStoppedState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::resume()
|
||||
{
|
||||
if (QAudio::SuspendedState == m_externalState) {
|
||||
if (SymbianAudio::SuspendedPausedState == m_internalState)
|
||||
m_devSound->resume();
|
||||
else
|
||||
startPlayback();
|
||||
}
|
||||
}
|
||||
|
||||
int QAudioOutputPrivate::bytesFree() const
|
||||
{
|
||||
int result = 0;
|
||||
if (m_devSoundBuffer) {
|
||||
const TDes8 &outputBuffer = m_devSoundBuffer->Data();
|
||||
result = outputBuffer.MaxLength() - outputBuffer.Length();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int QAudioOutputPrivate::periodSize() const
|
||||
{
|
||||
return bufferSize();
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::setBufferSize(int value)
|
||||
{
|
||||
// Note that DevSound does not allow its client to specify the buffer size.
|
||||
// This functionality is available via custom interfaces, but since these
|
||||
// cannot be guaranteed to work across all DevSound implementations, we
|
||||
// do not use them here.
|
||||
// In order to comply with the expected bevahiour of QAudioOutput, we store
|
||||
// the value and return it from bufferSize(), but the underlying DevSound
|
||||
// buffer size remains unchanged.
|
||||
if (value > 0)
|
||||
m_clientBufferSize = value;
|
||||
}
|
||||
|
||||
int QAudioOutputPrivate::bufferSize() const
|
||||
{
|
||||
return m_devSoundBufferSize ? m_devSoundBufferSize : m_clientBufferSize;
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::setNotifyInterval(int ms)
|
||||
{
|
||||
if (ms >= 0) {
|
||||
const int oldNotifyInterval = m_notifyInterval;
|
||||
m_notifyInterval = ms;
|
||||
if (m_notifyInterval && (SymbianAudio::ActiveState == m_internalState ||
|
||||
SymbianAudio::IdleState == m_internalState))
|
||||
m_notifyTimer->start(m_notifyInterval);
|
||||
else
|
||||
m_notifyTimer->stop();
|
||||
}
|
||||
}
|
||||
|
||||
int QAudioOutputPrivate::notifyInterval() const
|
||||
{
|
||||
return m_notifyInterval;
|
||||
}
|
||||
|
||||
qint64 QAudioOutputPrivate::processedUSecs() const
|
||||
{
|
||||
int samplesPlayed = 0;
|
||||
if (m_devSound && QAudio::SuspendedState != m_externalState)
|
||||
samplesPlayed = getSamplesPlayed();
|
||||
|
||||
// Protect against division by zero
|
||||
Q_ASSERT_X(m_format.frequency() > 0, Q_FUNC_INFO, "Invalid frequency");
|
||||
|
||||
const qint64 result = qint64(1000000) *
|
||||
(samplesPlayed + m_totalSamplesPlayed)
|
||||
/ m_format.frequency();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
qint64 QAudioOutputPrivate::elapsedUSecs() const
|
||||
{
|
||||
const qint64 result = (QAudio::StoppedState == state()) ?
|
||||
0 : m_elapsed.elapsed() * 1000;
|
||||
return result;
|
||||
}
|
||||
|
||||
QAudio::Error QAudioOutputPrivate::error() const
|
||||
{
|
||||
return m_error;
|
||||
}
|
||||
|
||||
QAudio::State QAudioOutputPrivate::state() const
|
||||
{
|
||||
return m_externalState;
|
||||
}
|
||||
|
||||
QAudioFormat QAudioOutputPrivate::format() const
|
||||
{
|
||||
return m_format;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Private functions
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void QAudioOutputPrivate::dataReady()
|
||||
{
|
||||
// Client-provided QIODevice has data ready to read.
|
||||
|
||||
Q_ASSERT_X(m_source->bytesAvailable(), Q_FUNC_INFO,
|
||||
"readyRead signal received, but no data available");
|
||||
|
||||
if (!m_bytesPadding)
|
||||
pullData();
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::underflowTimerExpired()
|
||||
{
|
||||
const TInt samplesPlayed = getSamplesPlayed();
|
||||
if (m_samplesPlayed && (samplesPlayed == m_samplesPlayed)) {
|
||||
setError(QAudio::UnderrunError);
|
||||
} else {
|
||||
m_samplesPlayed = samplesPlayed;
|
||||
m_underflowTimer->start();
|
||||
}
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::devsoundInitializeComplete(int err)
|
||||
{
|
||||
Q_ASSERT_X(SymbianAudio::InitializingState == m_internalState,
|
||||
Q_FUNC_INFO, "Invalid state");
|
||||
|
||||
if (!err && m_devSound->isFormatSupported(m_format))
|
||||
startPlayback();
|
||||
else
|
||||
setError(QAudio::OpenError);
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::devsoundBufferToBeFilled(CMMFBuffer *bufferBase)
|
||||
{
|
||||
// Following receipt of this signal, DevSound should not provide another
|
||||
// buffer until we have returned the current one.
|
||||
Q_ASSERT_X(!m_devSoundBuffer, Q_FUNC_INFO, "Buffer already held");
|
||||
|
||||
// Will be returned to DevSoundWrapper by bufferProcessed().
|
||||
m_devSoundBuffer = static_cast<CMMFDataBuffer*>(bufferBase);
|
||||
|
||||
if (!m_devSoundBufferSize)
|
||||
m_devSoundBufferSize = m_devSoundBuffer->Data().MaxLength();
|
||||
|
||||
writePaddingData();
|
||||
|
||||
if (m_pullMode && isDataReady() && !m_bytesPadding)
|
||||
pullData();
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::devsoundPlayError(int err)
|
||||
{
|
||||
switch (err) {
|
||||
case KErrUnderflow:
|
||||
m_underflow = true;
|
||||
if (m_pullMode && !m_lastBuffer)
|
||||
setError(QAudio::UnderrunError);
|
||||
else
|
||||
setState(SymbianAudio::IdleState);
|
||||
break;
|
||||
case KErrOverflow:
|
||||
// Silently consume this error when in playback mode
|
||||
break;
|
||||
default:
|
||||
setError(QAudio::IOError);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::open()
|
||||
{
|
||||
Q_ASSERT_X(SymbianAudio::ClosedState == m_internalState,
|
||||
Q_FUNC_INFO, "DevSound already opened");
|
||||
|
||||
Q_ASSERT(!m_devSound);
|
||||
m_devSound = new SymbianAudio::DevSoundWrapper(QAudio::AudioOutput, this);
|
||||
|
||||
connect(m_devSound, SIGNAL(initializeComplete(int)),
|
||||
this, SLOT(devsoundInitializeComplete(int)));
|
||||
connect(m_devSound, SIGNAL(bufferToBeProcessed(CMMFBuffer *)),
|
||||
this, SLOT(devsoundBufferToBeFilled(CMMFBuffer *)));
|
||||
connect(m_devSound, SIGNAL(processingError(int)),
|
||||
this, SLOT(devsoundPlayError(int)));
|
||||
|
||||
setState(SymbianAudio::InitializingState);
|
||||
m_devSound->initialize(m_format.codec());
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::startPlayback()
|
||||
{
|
||||
bool ok = m_devSound->setFormat(m_format);
|
||||
if (ok)
|
||||
ok = m_devSound->start();
|
||||
|
||||
if (ok) {
|
||||
if (isDataReady())
|
||||
setState(SymbianAudio::ActiveState);
|
||||
else
|
||||
setState(SymbianAudio::IdleState);
|
||||
|
||||
if (m_notifyInterval)
|
||||
m_notifyTimer->start(m_notifyInterval);
|
||||
m_underflow = false;
|
||||
|
||||
Q_ASSERT(m_devSound->samplesProcessed() == 0);
|
||||
|
||||
writePaddingData();
|
||||
|
||||
if (m_pullMode && m_source->bytesAvailable() && !m_bytesPadding)
|
||||
dataReady();
|
||||
} else {
|
||||
setError(QAudio::OpenError);
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::writePaddingData()
|
||||
{
|
||||
// See comments in suspend()
|
||||
|
||||
while (m_devSoundBuffer && m_bytesPadding) {
|
||||
if (SymbianAudio::IdleState == m_internalState)
|
||||
setState(SymbianAudio::ActiveState);
|
||||
|
||||
TDes8 &outputBuffer = m_devSoundBuffer->Data();
|
||||
const qint64 outputBytes = bytesFree();
|
||||
const qint64 paddingBytes = outputBytes < m_bytesPadding ?
|
||||
outputBytes : m_bytesPadding;
|
||||
unsigned char *ptr = const_cast<unsigned char*>(outputBuffer.Ptr());
|
||||
Mem::FillZ(ptr, paddingBytes);
|
||||
outputBuffer.SetLength(outputBuffer.Length() + paddingBytes);
|
||||
Q_ASSERT(m_bytesPadding >= paddingBytes);
|
||||
m_bytesPadding -= paddingBytes;
|
||||
|
||||
if (m_pullMode && m_source->atEnd())
|
||||
lastBufferFilled();
|
||||
if ((paddingBytes == outputBytes) || !m_bytesPadding)
|
||||
bufferFilled();
|
||||
}
|
||||
}
|
||||
|
||||
qint64 QAudioOutputPrivate::pushData(const char *data, qint64 len)
|
||||
{
|
||||
// Data has been written to SymbianAudioOutputPrivate
|
||||
|
||||
Q_ASSERT_X(!m_pullMode, Q_FUNC_INFO,
|
||||
"pushData called when in pull mode");
|
||||
|
||||
const unsigned char *const inputPtr =
|
||||
reinterpret_cast<const unsigned char*>(data);
|
||||
qint64 bytesWritten = 0;
|
||||
|
||||
if (SymbianAudio::IdleState == m_internalState)
|
||||
setState(SymbianAudio::ActiveState);
|
||||
|
||||
while (m_devSoundBuffer && (bytesWritten < len)) {
|
||||
// writePaddingData() is called from BufferToBeFilled(), so we should
|
||||
// never have any padding data left at this point.
|
||||
Q_ASSERT_X(0 == m_bytesPadding, Q_FUNC_INFO,
|
||||
"Padding bytes remaining in pushData");
|
||||
|
||||
TDes8 &outputBuffer = m_devSoundBuffer->Data();
|
||||
|
||||
const qint64 outputBytes = bytesFree();
|
||||
const qint64 inputBytes = len - bytesWritten;
|
||||
const qint64 copyBytes = outputBytes < inputBytes ?
|
||||
outputBytes : inputBytes;
|
||||
|
||||
outputBuffer.Append(inputPtr + bytesWritten, copyBytes);
|
||||
bytesWritten += copyBytes;
|
||||
|
||||
bufferFilled();
|
||||
}
|
||||
|
||||
m_pushDataReady = (bytesWritten < len);
|
||||
|
||||
// If DevSound is still initializing (m_internalState == InitializingState),
|
||||
// we cannot transition m_internalState to ActiveState, but we must emit
|
||||
// an (external) state change from IdleState to ActiveState. The following
|
||||
// call triggers this signal.
|
||||
setState(m_internalState);
|
||||
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::pullData()
|
||||
{
|
||||
Q_ASSERT_X(m_pullMode, Q_FUNC_INFO,
|
||||
"pullData called when in push mode");
|
||||
|
||||
// writePaddingData() is called by BufferToBeFilled() before pullData(),
|
||||
// so we should never have any padding data left at this point.
|
||||
Q_ASSERT_X(0 == m_bytesPadding, Q_FUNC_INFO,
|
||||
"Padding bytes remaining in pullData");
|
||||
|
||||
qint64 inputBytes = m_source->bytesAvailable();
|
||||
while (m_devSoundBuffer && inputBytes) {
|
||||
if (SymbianAudio::IdleState == m_internalState)
|
||||
setState(SymbianAudio::ActiveState);
|
||||
|
||||
TDes8 &outputBuffer = m_devSoundBuffer->Data();
|
||||
|
||||
const qint64 outputBytes = bytesFree();
|
||||
const qint64 copyBytes = outputBytes < inputBytes ?
|
||||
outputBytes : inputBytes;
|
||||
|
||||
char *outputPtr = (char*)(outputBuffer.Ptr() + outputBuffer.Length());
|
||||
const qint64 bytesCopied = m_source->read(outputPtr, copyBytes);
|
||||
Q_ASSERT(bytesCopied == copyBytes);
|
||||
outputBuffer.SetLength(outputBuffer.Length() + bytesCopied);
|
||||
inputBytes -= bytesCopied;
|
||||
|
||||
if (m_source->atEnd())
|
||||
lastBufferFilled();
|
||||
else if (copyBytes == outputBytes)
|
||||
bufferFilled();
|
||||
}
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::bufferFilled()
|
||||
{
|
||||
Q_ASSERT_X(m_devSoundBuffer, Q_FUNC_INFO, "No buffer to return");
|
||||
|
||||
const TDes8 &outputBuffer = m_devSoundBuffer->Data();
|
||||
m_bytesWritten += outputBuffer.Length();
|
||||
|
||||
m_devSoundBuffer = 0;
|
||||
|
||||
m_samplesPlayed = getSamplesPlayed();
|
||||
m_underflowTimer->start();
|
||||
|
||||
if (QAudio::UnderrunError == m_error)
|
||||
m_error = QAudio::NoError;
|
||||
|
||||
m_devSound->bufferProcessed();
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::lastBufferFilled()
|
||||
{
|
||||
Q_ASSERT_X(m_devSoundBuffer, Q_FUNC_INFO, "No buffer to fill");
|
||||
Q_ASSERT_X(!m_lastBuffer, Q_FUNC_INFO, "Last buffer already sent");
|
||||
m_lastBuffer = true;
|
||||
m_devSoundBuffer->SetLastBuffer(ETrue);
|
||||
bufferFilled();
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::close()
|
||||
{
|
||||
m_notifyTimer->stop();
|
||||
m_underflowTimer->stop();
|
||||
|
||||
m_error = QAudio::NoError;
|
||||
|
||||
if (m_devSound)
|
||||
m_devSound->stop();
|
||||
delete m_devSound;
|
||||
m_devSound = 0;
|
||||
|
||||
m_devSoundBuffer = 0;
|
||||
m_devSoundBufferSize = 0;
|
||||
|
||||
if (!m_pullMode) // m_source is owned
|
||||
delete m_source;
|
||||
m_pullMode = false;
|
||||
m_source = 0;
|
||||
|
||||
m_bytesWritten = 0;
|
||||
m_pushDataReady = false;
|
||||
m_bytesPadding = 0;
|
||||
m_underflow = false;
|
||||
m_lastBuffer = false;
|
||||
m_samplesPlayed = 0;
|
||||
m_totalSamplesPlayed = 0;
|
||||
|
||||
setState(SymbianAudio::ClosedState);
|
||||
}
|
||||
|
||||
qint64 QAudioOutputPrivate::getSamplesPlayed() const
|
||||
{
|
||||
qint64 result = 0;
|
||||
if (m_devSound) {
|
||||
const qint64 samplesWritten = SymbianAudio::Utils::bytesToSamples(
|
||||
m_format, m_bytesWritten);
|
||||
|
||||
if (m_underflow) {
|
||||
result = samplesWritten;
|
||||
} else {
|
||||
// This is necessary because some DevSound implementations report
|
||||
// that they have played more data than has actually been provided to them
|
||||
// by the client.
|
||||
const qint64 devSoundSamplesPlayed(m_devSound->samplesProcessed());
|
||||
result = qMin(devSoundSamplesPlayed, samplesWritten);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::setError(QAudio::Error error)
|
||||
{
|
||||
m_error = error;
|
||||
|
||||
// Although no state transition actually occurs here, a stateChanged event
|
||||
// must be emitted to inform the client that the call to start() was
|
||||
// unsuccessful.
|
||||
if (QAudio::OpenError == error) {
|
||||
emit stateChanged(QAudio::StoppedState);
|
||||
} else {
|
||||
if (QAudio::UnderrunError == error)
|
||||
setState(SymbianAudio::IdleState);
|
||||
else
|
||||
// Close the DevSound instance. This causes a transition to
|
||||
// StoppedState. This must be done asynchronously in case the
|
||||
// current function was called from a DevSound event handler, in which
|
||||
// case deleting the DevSound instance may cause an exception.
|
||||
QMetaObject::invokeMethod(this, "close", Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::setState(SymbianAudio::State newInternalState)
|
||||
{
|
||||
const QAudio::State oldExternalState = m_externalState;
|
||||
m_internalState = newInternalState;
|
||||
m_externalState = SymbianAudio::Utils::stateNativeToQt(m_internalState);
|
||||
|
||||
if (m_externalState != oldExternalState)
|
||||
emit stateChanged(m_externalState);
|
||||
}
|
||||
|
||||
bool QAudioOutputPrivate::isDataReady() const
|
||||
{
|
||||
return (m_source && m_source->bytesAvailable())
|
||||
|| m_bytesPadding
|
||||
|| m_pushDataReady;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
198
src/multimedia/audio/qaudiooutput_symbian_p.h
Normal file
198
src/multimedia/audio/qaudiooutput_symbian_p.h
Normal file
@@ -0,0 +1,198 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $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.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#ifndef QAUDIOOUTPUT_SYMBIAN_P_H
|
||||
#define QAUDIOOUTPUT_SYMBIAN_P_H
|
||||
|
||||
#include <QtMultimedia/qaudioengine.h>
|
||||
#include <QTime>
|
||||
#include <QTimer>
|
||||
#include <sounddevice.h>
|
||||
#include "qaudio_symbian_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QAudioOutputPrivate;
|
||||
|
||||
class SymbianAudioOutputPrivate : public QIODevice
|
||||
{
|
||||
friend class QAudioOutputPrivate;
|
||||
Q_OBJECT
|
||||
public:
|
||||
SymbianAudioOutputPrivate(QAudioOutputPrivate *audio);
|
||||
~SymbianAudioOutputPrivate();
|
||||
|
||||
qint64 readData(char *data, qint64 len);
|
||||
qint64 writeData(const char *data, qint64 len);
|
||||
|
||||
private:
|
||||
QAudioOutputPrivate *const m_audioDevice;
|
||||
};
|
||||
|
||||
class QAudioOutputPrivate
|
||||
: public QAbstractAudioOutput
|
||||
{
|
||||
friend class SymbianAudioOutputPrivate;
|
||||
Q_OBJECT
|
||||
public:
|
||||
QAudioOutputPrivate(const QByteArray &device,
|
||||
const QAudioFormat &audioFormat);
|
||||
~QAudioOutputPrivate();
|
||||
|
||||
// QAbstractAudioOutput
|
||||
QIODevice* start(QIODevice *device = 0);
|
||||
void stop();
|
||||
void reset();
|
||||
void suspend();
|
||||
void resume();
|
||||
int bytesFree() const;
|
||||
int periodSize() const;
|
||||
void setBufferSize(int value);
|
||||
int bufferSize() const;
|
||||
void setNotifyInterval(int milliSeconds);
|
||||
int notifyInterval() const;
|
||||
qint64 processedUSecs() const;
|
||||
qint64 elapsedUSecs() const;
|
||||
QAudio::Error error() const;
|
||||
QAudio::State state() const;
|
||||
QAudioFormat format() const;
|
||||
|
||||
private slots:
|
||||
void dataReady();
|
||||
void underflowTimerExpired();
|
||||
void devsoundInitializeComplete(int err);
|
||||
void devsoundBufferToBeFilled(CMMFBuffer *);
|
||||
void devsoundPlayError(int err);
|
||||
|
||||
private:
|
||||
void open();
|
||||
void startPlayback();
|
||||
void writePaddingData();
|
||||
qint64 pushData(const char *data, qint64 len);
|
||||
void pullData();
|
||||
void bufferFilled();
|
||||
void lastBufferFilled();
|
||||
Q_INVOKABLE void close();
|
||||
|
||||
qint64 getSamplesPlayed() const;
|
||||
|
||||
void setError(QAudio::Error error);
|
||||
void setState(SymbianAudio::State state);
|
||||
|
||||
bool isDataReady() const;
|
||||
|
||||
private:
|
||||
const QByteArray m_device;
|
||||
const QAudioFormat m_format;
|
||||
|
||||
int m_clientBufferSize;
|
||||
int m_notifyInterval;
|
||||
QScopedPointer<QTimer> m_notifyTimer;
|
||||
QTime m_elapsed;
|
||||
QAudio::Error m_error;
|
||||
|
||||
SymbianAudio::State m_internalState;
|
||||
QAudio::State m_externalState;
|
||||
|
||||
bool m_pullMode;
|
||||
QIODevice *m_source;
|
||||
|
||||
SymbianAudio::DevSoundWrapper* m_devSound;
|
||||
|
||||
// Buffer provided by DevSound, to be filled with data.
|
||||
CMMFDataBuffer *m_devSoundBuffer;
|
||||
|
||||
int m_devSoundBufferSize;
|
||||
|
||||
// Number of bytes transferred from QIODevice to QAudioOutput. It is
|
||||
// necessary to count this because data is dropped when suspend() is
|
||||
// called. The difference between the position reported by DevSound and
|
||||
// this value allows us to calculate m_bytesPadding;
|
||||
quint32 m_bytesWritten;
|
||||
|
||||
// True if client has provided data while the audio subsystem was not
|
||||
// ready to consume it.
|
||||
bool m_pushDataReady;
|
||||
|
||||
// Number of zero bytes which will be written when client calls resume().
|
||||
quint32 m_bytesPadding;
|
||||
|
||||
// True if PlayError(KErrUnderflow) has been called.
|
||||
bool m_underflow;
|
||||
|
||||
// True if a buffer marked with the "last buffer" flag has been provided
|
||||
// to DevSound.
|
||||
bool m_lastBuffer;
|
||||
|
||||
// Some DevSound implementations ignore all underflow errors raised by the
|
||||
// audio driver, unless the last buffer flag has been set by the client.
|
||||
// In push-mode playback, this flag will never be set, so the underflow
|
||||
// error will never be reported. In order to work around this, a timer
|
||||
// is used, which gets reset every time the client provides more data. If
|
||||
// the timer expires, an underflow error is raised by this object.
|
||||
QScopedPointer<QTimer> m_underflowTimer;
|
||||
|
||||
// Result of previous call to CMMFDevSound::SamplesPlayed(). This value is
|
||||
// used to determine whether, when m_underflowTimer expires, an
|
||||
// underflow error has actually occurred.
|
||||
quint32 m_samplesPlayed;
|
||||
|
||||
// Samples played up to the last call to suspend(). It is necessary
|
||||
// to cache this because suspend() is implemented using
|
||||
// CMMFDevSound::Stop(), which resets DevSound's SamplesPlayed() counter.
|
||||
quint32 m_totalSamplesPlayed;
|
||||
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
703
src/multimedia/audio/qaudiooutput_win32_p.cpp
Normal file
703
src/multimedia/audio/qaudiooutput_win32_p.cpp
Normal file
@@ -0,0 +1,703 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $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.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include "qaudiooutput_win32_p.h"
|
||||
|
||||
#ifndef SPEAKER_FRONT_LEFT
|
||||
#define SPEAKER_FRONT_LEFT 0x00000001
|
||||
#define SPEAKER_FRONT_RIGHT 0x00000002
|
||||
#define SPEAKER_FRONT_CENTER 0x00000004
|
||||
#define SPEAKER_LOW_FREQUENCY 0x00000008
|
||||
#define SPEAKER_BACK_LEFT 0x00000010
|
||||
#define SPEAKER_BACK_RIGHT 0x00000020
|
||||
#define SPEAKER_FRONT_LEFT_OF_CENTER 0x00000040
|
||||
#define SPEAKER_FRONT_RIGHT_OF_CENTER 0x00000080
|
||||
#define SPEAKER_BACK_CENTER 0x00000100
|
||||
#define SPEAKER_SIDE_LEFT 0x00000200
|
||||
#define SPEAKER_SIDE_RIGHT 0x00000400
|
||||
#define SPEAKER_TOP_CENTER 0x00000800
|
||||
#define SPEAKER_TOP_FRONT_LEFT 0x00001000
|
||||
#define SPEAKER_TOP_FRONT_CENTER 0x00002000
|
||||
#define SPEAKER_TOP_FRONT_RIGHT 0x00004000
|
||||
#define SPEAKER_TOP_BACK_LEFT 0x00008000
|
||||
#define SPEAKER_TOP_BACK_CENTER 0x00010000
|
||||
#define SPEAKER_TOP_BACK_RIGHT 0x00020000
|
||||
#define SPEAKER_RESERVED 0x7FFC0000
|
||||
#define SPEAKER_ALL 0x80000000
|
||||
#endif
|
||||
|
||||
#ifndef _WAVEFORMATEXTENSIBLE_
|
||||
|
||||
#define _WAVEFORMATEXTENSIBLE_
|
||||
typedef struct
|
||||
{
|
||||
WAVEFORMATEX Format; // Base WAVEFORMATEX data
|
||||
union
|
||||
{
|
||||
WORD wValidBitsPerSample; // Valid bits in each sample container
|
||||
WORD wSamplesPerBlock; // Samples per block of audio data; valid
|
||||
// if wBitsPerSample=0 (but rarely used).
|
||||
WORD wReserved; // Zero if neither case above applies.
|
||||
} Samples;
|
||||
DWORD dwChannelMask; // Positions of the audio channels
|
||||
GUID SubFormat; // Format identifier GUID
|
||||
} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE, *LPPWAVEFORMATEXTENSIBLE;
|
||||
typedef const WAVEFORMATEXTENSIBLE* LPCWAVEFORMATEXTENSIBLE;
|
||||
|
||||
#endif
|
||||
|
||||
#if !defined(WAVE_FORMAT_EXTENSIBLE)
|
||||
#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
|
||||
#endif
|
||||
|
||||
//#define DEBUG_AUDIO 1
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QAudioOutputPrivate::QAudioOutputPrivate(const QByteArray &device, const QAudioFormat& audioFormat):
|
||||
settings(audioFormat)
|
||||
{
|
||||
bytesAvailable = 0;
|
||||
buffer_size = 0;
|
||||
period_size = 0;
|
||||
m_device = device;
|
||||
totalTimeValue = 0;
|
||||
intervalTime = 1000;
|
||||
audioBuffer = 0;
|
||||
errorState = QAudio::NoError;
|
||||
deviceState = QAudio::StoppedState;
|
||||
audioSource = 0;
|
||||
pullMode = true;
|
||||
finished = false;
|
||||
}
|
||||
|
||||
QAudioOutputPrivate::~QAudioOutputPrivate()
|
||||
{
|
||||
mutex.lock();
|
||||
finished = true;
|
||||
mutex.unlock();
|
||||
|
||||
close();
|
||||
}
|
||||
|
||||
void CALLBACK QAudioOutputPrivate::waveOutProc( HWAVEOUT hWaveOut, UINT uMsg,
|
||||
DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 )
|
||||
{
|
||||
Q_UNUSED(dwParam1)
|
||||
Q_UNUSED(dwParam2)
|
||||
Q_UNUSED(hWaveOut)
|
||||
|
||||
QAudioOutputPrivate* qAudio;
|
||||
qAudio = (QAudioOutputPrivate*)(dwInstance);
|
||||
if(!qAudio)
|
||||
return;
|
||||
|
||||
QMutexLocker(&qAudio->mutex);
|
||||
|
||||
switch(uMsg) {
|
||||
case WOM_OPEN:
|
||||
qAudio->feedback();
|
||||
break;
|
||||
case WOM_CLOSE:
|
||||
return;
|
||||
case WOM_DONE:
|
||||
if(qAudio->finished || qAudio->buffer_size == 0 || qAudio->period_size == 0) {
|
||||
return;
|
||||
}
|
||||
qAudio->waveFreeBlockCount++;
|
||||
if(qAudio->waveFreeBlockCount >= qAudio->buffer_size/qAudio->period_size)
|
||||
qAudio->waveFreeBlockCount = qAudio->buffer_size/qAudio->period_size;
|
||||
qAudio->feedback();
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
WAVEHDR* QAudioOutputPrivate::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("QAudioOutput: 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;
|
||||
buffer += size;
|
||||
}
|
||||
return blocks;
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::freeBlocks(WAVEHDR* blockArray)
|
||||
{
|
||||
WAVEHDR* blocks = blockArray;
|
||||
|
||||
int count = buffer_size/period_size;
|
||||
|
||||
for(int i = 0; i < count; i++) {
|
||||
waveOutUnprepareHeader(hWaveOut,blocks, sizeof(WAVEHDR));
|
||||
blocks++;
|
||||
}
|
||||
HeapFree(GetProcessHeap(), 0, blockArray);
|
||||
}
|
||||
|
||||
QAudioFormat QAudioOutputPrivate::format() const
|
||||
{
|
||||
return settings;
|
||||
}
|
||||
|
||||
QIODevice* QAudioOutputPrivate::start(QIODevice* device)
|
||||
{
|
||||
if(deviceState != QAudio::StoppedState)
|
||||
close();
|
||||
|
||||
if(!pullMode && audioSource) {
|
||||
delete audioSource;
|
||||
}
|
||||
|
||||
if(device) {
|
||||
//set to pull mode
|
||||
pullMode = true;
|
||||
audioSource = device;
|
||||
deviceState = QAudio::ActiveState;
|
||||
} else {
|
||||
//set to push mode
|
||||
pullMode = false;
|
||||
audioSource = new OutputPrivate(this);
|
||||
audioSource->open(QIODevice::WriteOnly|QIODevice::Unbuffered);
|
||||
deviceState = QAudio::IdleState;
|
||||
}
|
||||
|
||||
if( !open() )
|
||||
return 0;
|
||||
|
||||
emit stateChanged(deviceState);
|
||||
|
||||
return audioSource;
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::stop()
|
||||
{
|
||||
if(deviceState == QAudio::StoppedState)
|
||||
return;
|
||||
close();
|
||||
if(!pullMode && audioSource) {
|
||||
delete audioSource;
|
||||
audioSource = 0;
|
||||
}
|
||||
emit stateChanged(deviceState);
|
||||
}
|
||||
|
||||
bool QAudioOutputPrivate::open()
|
||||
{
|
||||
#ifdef DEBUG_AUDIO
|
||||
QTime now(QTime::currentTime());
|
||||
qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()";
|
||||
#endif
|
||||
|
||||
period_size = 0;
|
||||
|
||||
if (!settings.isValid()) {
|
||||
qWarning("QAudioOutput: open error, invalid format.");
|
||||
} else if (settings.channels() <= 0) {
|
||||
qWarning("QAudioOutput: open error, invalid number of channels (%d).",
|
||||
settings.channels());
|
||||
} else if (settings.sampleSize() <= 0) {
|
||||
qWarning("QAudioOutput: open error, invalid sample size (%d).",
|
||||
settings.sampleSize());
|
||||
} else if (settings.frequency() < 8000 || settings.frequency() > 48000) {
|
||||
qWarning("QAudioOutput: open error, frequency out of range (%d).", settings.frequency());
|
||||
} else if (buffer_size == 0) {
|
||||
// Default buffer size, 200ms, default period size is 40ms
|
||||
buffer_size
|
||||
= (settings.frequency()
|
||||
* settings.channels()
|
||||
* 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;
|
||||
}
|
||||
|
||||
waveBlocks = allocateBlocks(period_size, buffer_size/period_size);
|
||||
|
||||
mutex.lock();
|
||||
waveFreeBlockCount = buffer_size/period_size;
|
||||
mutex.unlock();
|
||||
|
||||
waveCurrentBlock = 0;
|
||||
|
||||
if(audioBuffer == 0)
|
||||
audioBuffer = new char[buffer_size];
|
||||
|
||||
timeStamp.restart();
|
||||
elapsedTimeOffset = 0;
|
||||
|
||||
wfx.nSamplesPerSec = settings.frequency();
|
||||
wfx.wBitsPerSample = settings.sampleSize();
|
||||
wfx.nChannels = settings.channels();
|
||||
wfx.cbSize = 0;
|
||||
|
||||
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
||||
wfx.nBlockAlign = (wfx.wBitsPerSample >> 3) * wfx.nChannels;
|
||||
wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec;
|
||||
|
||||
UINT_PTR devId = WAVE_MAPPER;
|
||||
|
||||
WAVEOUTCAPS woc;
|
||||
unsigned long iNumDevs,ii;
|
||||
iNumDevs = waveOutGetNumDevs();
|
||||
for(ii=0;ii<iNumDevs;ii++) {
|
||||
if(waveOutGetDevCaps(ii, &woc, sizeof(WAVEOUTCAPS))
|
||||
== MMSYSERR_NOERROR) {
|
||||
QString tmp;
|
||||
tmp = QString((const QChar *)woc.szPname);
|
||||
if(tmp.compare(QLatin1String(m_device)) == 0) {
|
||||
devId = ii;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( settings.channels() <= 2) {
|
||||
if(waveOutOpen(&hWaveOut, devId, &wfx,
|
||||
(DWORD_PTR)&waveOutProc,
|
||||
(DWORD_PTR) this,
|
||||
CALLBACK_FUNCTION) != MMSYSERR_NOERROR) {
|
||||
errorState = QAudio::OpenError;
|
||||
deviceState = QAudio::StoppedState;
|
||||
emit stateChanged(deviceState);
|
||||
qWarning("QAudioOutput: open error");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
WAVEFORMATEXTENSIBLE wfex;
|
||||
wfex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
wfex.Format.nChannels = settings.channels();
|
||||
wfex.Format.wBitsPerSample = settings.sampleSize();
|
||||
wfex.Format.nSamplesPerSec = settings.frequency();
|
||||
wfex.Format.nBlockAlign = wfex.Format.nChannels*wfex.Format.wBitsPerSample/8;
|
||||
wfex.Format.nAvgBytesPerSec=wfex.Format.nSamplesPerSec*wfex.Format.nBlockAlign;
|
||||
wfex.Samples.wValidBitsPerSample=wfex.Format.wBitsPerSample;
|
||||
static const GUID _KSDATAFORMAT_SUBTYPE_PCM = {
|
||||
0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
|
||||
wfex.SubFormat=_KSDATAFORMAT_SUBTYPE_PCM;
|
||||
wfex.Format.cbSize=22;
|
||||
|
||||
wfex.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
|
||||
if (settings.channels() >= 4)
|
||||
wfex.dwChannelMask |= SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
|
||||
if (settings.channels() >= 6)
|
||||
wfex.dwChannelMask |= SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY;
|
||||
if (settings.channels() == 8)
|
||||
wfex.dwChannelMask |= SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT;
|
||||
|
||||
if(waveOutOpen(&hWaveOut, devId, &wfex.Format,
|
||||
(DWORD_PTR)&waveOutProc,
|
||||
(DWORD_PTR) this,
|
||||
CALLBACK_FUNCTION) != MMSYSERR_NOERROR) {
|
||||
errorState = QAudio::OpenError;
|
||||
deviceState = QAudio::StoppedState;
|
||||
emit stateChanged(deviceState);
|
||||
qWarning("QAudioOutput: open error");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
totalTimeValue = 0;
|
||||
timeStampOpened.restart();
|
||||
elapsedTimeOffset = 0;
|
||||
|
||||
errorState = QAudio::NoError;
|
||||
if(pullMode) {
|
||||
deviceState = QAudio::ActiveState;
|
||||
QTimer::singleShot(10, this, SLOT(feedback()));
|
||||
} else
|
||||
deviceState = QAudio::IdleState;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::close()
|
||||
{
|
||||
if(deviceState == QAudio::StoppedState)
|
||||
return;
|
||||
|
||||
deviceState = QAudio::StoppedState;
|
||||
errorState = QAudio::NoError;
|
||||
int delay = (buffer_size-bytesFree())*1000/(settings.frequency()
|
||||
*settings.channels()*(settings.sampleSize()/8));
|
||||
waveOutReset(hWaveOut);
|
||||
Sleep(delay+10);
|
||||
|
||||
freeBlocks(waveBlocks);
|
||||
waveOutClose(hWaveOut);
|
||||
delete [] audioBuffer;
|
||||
audioBuffer = 0;
|
||||
buffer_size = 0;
|
||||
}
|
||||
|
||||
int QAudioOutputPrivate::bytesFree() const
|
||||
{
|
||||
int buf;
|
||||
buf = waveFreeBlockCount*period_size;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
int QAudioOutputPrivate::periodSize() const
|
||||
{
|
||||
return period_size;
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::setBufferSize(int value)
|
||||
{
|
||||
if(deviceState == QAudio::StoppedState)
|
||||
buffer_size = value;
|
||||
}
|
||||
|
||||
int QAudioOutputPrivate::bufferSize() const
|
||||
{
|
||||
return buffer_size;
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::setNotifyInterval(int ms)
|
||||
{
|
||||
intervalTime = qMax(0, ms);
|
||||
}
|
||||
|
||||
int QAudioOutputPrivate::notifyInterval() const
|
||||
{
|
||||
return intervalTime;
|
||||
}
|
||||
|
||||
qint64 QAudioOutputPrivate::processedUSecs() const
|
||||
{
|
||||
if (deviceState == QAudio::StoppedState)
|
||||
return 0;
|
||||
qint64 result = qint64(1000000) * totalTimeValue /
|
||||
(settings.channels()*(settings.sampleSize()/8)) /
|
||||
settings.frequency();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
qint64 QAudioOutputPrivate::write( const char *data, qint64 len )
|
||||
{
|
||||
// Write out some audio data
|
||||
if (deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState)
|
||||
return 0;
|
||||
|
||||
char* p = (char*)data;
|
||||
int l = (int)len;
|
||||
|
||||
WAVEHDR* current;
|
||||
int remain;
|
||||
current = &waveBlocks[waveCurrentBlock];
|
||||
while(l > 0) {
|
||||
mutex.lock();
|
||||
if(waveFreeBlockCount==0) {
|
||||
mutex.unlock();
|
||||
break;
|
||||
}
|
||||
mutex.unlock();
|
||||
|
||||
if(current->dwFlags & WHDR_PREPARED)
|
||||
waveOutUnprepareHeader(hWaveOut, current, sizeof(WAVEHDR));
|
||||
|
||||
if(l < period_size)
|
||||
remain = l;
|
||||
else
|
||||
remain = period_size;
|
||||
memcpy(current->lpData, p, remain);
|
||||
|
||||
l -= remain;
|
||||
p += remain;
|
||||
current->dwBufferLength = remain;
|
||||
waveOutPrepareHeader(hWaveOut, current, sizeof(WAVEHDR));
|
||||
waveOutWrite(hWaveOut, current, sizeof(WAVEHDR));
|
||||
|
||||
mutex.lock();
|
||||
waveFreeBlockCount--;
|
||||
#ifdef DEBUG_AUDIO
|
||||
qDebug("write out l=%d, waveFreeBlockCount=%d",
|
||||
current->dwBufferLength,waveFreeBlockCount);
|
||||
#endif
|
||||
mutex.unlock();
|
||||
totalTimeValue += current->dwBufferLength;
|
||||
waveCurrentBlock++;
|
||||
waveCurrentBlock %= buffer_size/period_size;
|
||||
current = &waveBlocks[waveCurrentBlock];
|
||||
current->dwUser = 0;
|
||||
errorState = QAudio::NoError;
|
||||
if (deviceState != QAudio::ActiveState) {
|
||||
deviceState = QAudio::ActiveState;
|
||||
emit stateChanged(deviceState);
|
||||
}
|
||||
}
|
||||
return (len-l);
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::resume()
|
||||
{
|
||||
if(deviceState == QAudio::SuspendedState) {
|
||||
deviceState = QAudio::ActiveState;
|
||||
errorState = QAudio::NoError;
|
||||
waveOutRestart(hWaveOut);
|
||||
QTimer::singleShot(10, this, SLOT(feedback()));
|
||||
emit stateChanged(deviceState);
|
||||
}
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::suspend()
|
||||
{
|
||||
if(deviceState == QAudio::ActiveState || deviceState == QAudio::IdleState) {
|
||||
int delay = (buffer_size-bytesFree())*1000/(settings.frequency()
|
||||
*settings.channels()*(settings.sampleSize()/8));
|
||||
waveOutPause(hWaveOut);
|
||||
Sleep(delay+10);
|
||||
deviceState = QAudio::SuspendedState;
|
||||
errorState = QAudio::NoError;
|
||||
emit stateChanged(deviceState);
|
||||
}
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::feedback()
|
||||
{
|
||||
#ifdef DEBUG_AUDIO
|
||||
QTime now(QTime::currentTime());
|
||||
qDebug()<<now.second()<<"s "<<now.msec()<<"ms :feedback()";
|
||||
#endif
|
||||
bytesAvailable = bytesFree();
|
||||
|
||||
if(!(deviceState==QAudio::StoppedState||deviceState==QAudio::SuspendedState)) {
|
||||
if(bytesAvailable >= period_size)
|
||||
QMetaObject::invokeMethod(this, "deviceReady", Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
bool QAudioOutputPrivate::deviceReady()
|
||||
{
|
||||
if(deviceState == QAudio::StoppedState || deviceState == QAudio::SuspendedState)
|
||||
return false;
|
||||
|
||||
if(pullMode) {
|
||||
int chunks = bytesAvailable/period_size;
|
||||
#ifdef DEBUG_AUDIO
|
||||
qDebug()<<"deviceReady() avail="<<bytesAvailable<<" bytes, period size="<<period_size<<" bytes";
|
||||
qDebug()<<"deviceReady() no. of chunks that can fit ="<<chunks<<", chunks in bytes ="<<chunks*period_size;
|
||||
#endif
|
||||
bool startup = false;
|
||||
if(totalTimeValue == 0)
|
||||
startup = true;
|
||||
|
||||
bool full=false;
|
||||
|
||||
mutex.lock();
|
||||
if(waveFreeBlockCount==0) full = true;
|
||||
mutex.unlock();
|
||||
|
||||
if (full){
|
||||
#ifdef DEBUG_AUDIO
|
||||
qDebug() << "Skipping data as unable to write";
|
||||
#endif
|
||||
if(intervalTime && (timeStamp.elapsed() + elapsedTimeOffset) > intervalTime ) {
|
||||
emit notify();
|
||||
elapsedTimeOffset = timeStamp.elapsed() + elapsedTimeOffset - intervalTime;
|
||||
timeStamp.restart();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if(startup)
|
||||
waveOutPause(hWaveOut);
|
||||
int input = period_size*chunks;
|
||||
int l = audioSource->read(audioBuffer,input);
|
||||
if(l > 0) {
|
||||
int out= write(audioBuffer,l);
|
||||
if(out > 0) {
|
||||
if (deviceState != QAudio::ActiveState) {
|
||||
deviceState = QAudio::ActiveState;
|
||||
emit stateChanged(deviceState);
|
||||
}
|
||||
}
|
||||
if ( out < l) {
|
||||
// Didn't write all data
|
||||
audioSource->seek(audioSource->pos()-(l-out));
|
||||
}
|
||||
if(startup)
|
||||
waveOutRestart(hWaveOut);
|
||||
} else if(l == 0) {
|
||||
bytesAvailable = bytesFree();
|
||||
|
||||
int check = 0;
|
||||
|
||||
mutex.lock();
|
||||
check = waveFreeBlockCount;
|
||||
mutex.unlock();
|
||||
|
||||
if(check == buffer_size/period_size) {
|
||||
if (deviceState != QAudio::IdleState) {
|
||||
errorState = QAudio::UnderrunError;
|
||||
deviceState = QAudio::IdleState;
|
||||
emit stateChanged(deviceState);
|
||||
}
|
||||
}
|
||||
|
||||
} else if(l < 0) {
|
||||
bytesAvailable = bytesFree();
|
||||
errorState = QAudio::IOError;
|
||||
}
|
||||
} else {
|
||||
int buffered;
|
||||
|
||||
mutex.lock();
|
||||
buffered = waveFreeBlockCount;
|
||||
mutex.unlock();
|
||||
|
||||
if (buffered >= buffer_size/period_size && deviceState == QAudio::ActiveState) {
|
||||
if (deviceState != QAudio::IdleState) {
|
||||
errorState = QAudio::UnderrunError;
|
||||
deviceState = QAudio::IdleState;
|
||||
emit stateChanged(deviceState);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState)
|
||||
return true;
|
||||
|
||||
if(intervalTime && (timeStamp.elapsed() + elapsedTimeOffset) > intervalTime) {
|
||||
emit notify();
|
||||
elapsedTimeOffset = timeStamp.elapsed() + elapsedTimeOffset - intervalTime;
|
||||
timeStamp.restart();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
qint64 QAudioOutputPrivate::elapsedUSecs() const
|
||||
{
|
||||
if (deviceState == QAudio::StoppedState)
|
||||
return 0;
|
||||
|
||||
return timeStampOpened.elapsed()*1000;
|
||||
}
|
||||
|
||||
QAudio::Error QAudioOutputPrivate::error() const
|
||||
{
|
||||
return errorState;
|
||||
}
|
||||
|
||||
QAudio::State QAudioOutputPrivate::state() const
|
||||
{
|
||||
return deviceState;
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::reset()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
OutputPrivate::OutputPrivate(QAudioOutputPrivate* audio)
|
||||
{
|
||||
audioDevice = qobject_cast<QAudioOutputPrivate*>(audio);
|
||||
}
|
||||
|
||||
OutputPrivate::~OutputPrivate() {}
|
||||
|
||||
qint64 OutputPrivate::readData( char* data, qint64 len)
|
||||
{
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(len)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
qint64 OutputPrivate::writeData(const char* data, qint64 len)
|
||||
{
|
||||
int retry = 0;
|
||||
qint64 written = 0;
|
||||
|
||||
if((audioDevice->deviceState == QAudio::ActiveState)
|
||||
||(audioDevice->deviceState == QAudio::IdleState)) {
|
||||
qint64 l = len;
|
||||
while(written < l) {
|
||||
int chunk = audioDevice->write(data+written,(l-written));
|
||||
if(chunk <= 0)
|
||||
retry++;
|
||||
else
|
||||
written+=chunk;
|
||||
|
||||
if(retry > 10)
|
||||
return written;
|
||||
}
|
||||
audioDevice->deviceState = QAudio::ActiveState;
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
157
src/multimedia/audio/qaudiooutput_win32_p.h
Normal file
157
src/multimedia/audio/qaudiooutput_win32_p.h
Normal file
@@ -0,0 +1,157 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $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.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#ifndef QAUDIOOUTPUTWIN_H
|
||||
#define QAUDIOOUTPUTWIN_H
|
||||
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
|
||||
#include <QtCore/qdebug.h>
|
||||
#include <QtCore/qtimer.h>
|
||||
#include <QtCore/qstring.h>
|
||||
#include <QtCore/qstringlist.h>
|
||||
#include <QtCore/qdatetime.h>
|
||||
#include <QtCore/qmutex.h>
|
||||
|
||||
#include <QtMultimedia/qaudio.h>
|
||||
#include <QtMultimedia/qaudiodeviceinfo.h>
|
||||
#include <QtMultimedia/qaudioengine.h>
|
||||
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QAudioOutputPrivate : public QAbstractAudioOutput
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QAudioOutputPrivate(const QByteArray &device, const QAudioFormat& audioFormat);
|
||||
~QAudioOutputPrivate();
|
||||
|
||||
qint64 write( const char *data, qint64 len );
|
||||
|
||||
QAudioFormat format() const;
|
||||
QIODevice* start(QIODevice* device = 0);
|
||||
void stop();
|
||||
void reset();
|
||||
void suspend();
|
||||
void resume();
|
||||
int bytesFree() const;
|
||||
int periodSize() const;
|
||||
void setBufferSize(int value);
|
||||
int bufferSize() const;
|
||||
void setNotifyInterval(int milliSeconds);
|
||||
int notifyInterval() const;
|
||||
qint64 processedUSecs() const;
|
||||
qint64 elapsedUSecs() const;
|
||||
QAudio::Error error() const;
|
||||
QAudio::State state() const;
|
||||
|
||||
QIODevice* audioSource;
|
||||
QAudioFormat settings;
|
||||
QAudio::Error errorState;
|
||||
QAudio::State deviceState;
|
||||
|
||||
private slots:
|
||||
void feedback();
|
||||
bool deviceReady();
|
||||
|
||||
private:
|
||||
QByteArray m_device;
|
||||
bool resuming;
|
||||
int bytesAvailable;
|
||||
QTime timeStamp;
|
||||
qint64 elapsedTimeOffset;
|
||||
QTime timeStampOpened;
|
||||
qint32 buffer_size;
|
||||
qint32 period_size;
|
||||
qint64 totalTimeValue;
|
||||
bool pullMode;
|
||||
int intervalTime;
|
||||
static void QT_WIN_CALLBACK waveOutProc( HWAVEOUT hWaveOut, UINT uMsg,
|
||||
DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 );
|
||||
|
||||
QMutex mutex;
|
||||
|
||||
WAVEHDR* allocateBlocks(int size, int count);
|
||||
void freeBlocks(WAVEHDR* blockArray);
|
||||
bool open();
|
||||
void close();
|
||||
|
||||
WAVEFORMATEX wfx;
|
||||
HWAVEOUT hWaveOut;
|
||||
MMRESULT result;
|
||||
WAVEHDR header;
|
||||
WAVEHDR* waveBlocks;
|
||||
volatile bool finished;
|
||||
volatile int waveFreeBlockCount;
|
||||
int waveCurrentBlock;
|
||||
char* audioBuffer;
|
||||
};
|
||||
|
||||
class OutputPrivate : public QIODevice
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
OutputPrivate(QAudioOutputPrivate* audio);
|
||||
~OutputPrivate();
|
||||
|
||||
qint64 readData( char* data, qint64 len);
|
||||
qint64 writeData(const char* data, qint64 len);
|
||||
|
||||
private:
|
||||
QAudioOutputPrivate *audioDevice;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
16
src/multimedia/multimedia.pro
Normal file
16
src/multimedia/multimedia.pro
Normal file
@@ -0,0 +1,16 @@
|
||||
TARGET = QtMultimedia
|
||||
QPRO_PWD = $$PWD
|
||||
QT = core gui
|
||||
|
||||
DEFINES += QT_BUILD_MULTIMEDIA_LIB QT_NO_USING_NAMESPACE
|
||||
|
||||
unix|win32-g++*:QMAKE_PKGCONFIG_REQUIRES = QtCore QtGui
|
||||
|
||||
include(../qbase.pri)
|
||||
|
||||
include(audio/audio.pri)
|
||||
include(video/video.pri)
|
||||
|
||||
symbian: {
|
||||
TARGET.UID3 = 0x2001E627
|
||||
}
|
||||
201
src/multimedia/video/qabstractvideobuffer.cpp
Normal file
201
src/multimedia/video/qabstractvideobuffer.cpp
Normal file
@@ -0,0 +1,201 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qabstractvideobuffer_p.h"
|
||||
|
||||
#include <qvariant.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
/*!
|
||||
\class QAbstractVideoBuffer
|
||||
\brief The QAbstractVideoBuffer class is an abstraction for video data.
|
||||
\since 4.6
|
||||
|
||||
The QVideoFrame class makes use of a QAbstractVideoBuffer internally to reference a buffer of
|
||||
video data. Creating a subclass of QAbstractVideoBuffer will allow you to construct video
|
||||
frames from preallocated or static buffers.
|
||||
|
||||
The contents of a buffer can be accessed by mapping the buffer to memory using the map()
|
||||
function which returns a pointer to memory containing the contents of the the video buffer.
|
||||
The memory returned by map() is released by calling the unmap() function.
|
||||
|
||||
The handle() of a buffer may also be used to manipulate it's contents using type specific APIs.
|
||||
The type of a buffer's handle is given by the handleType() function.
|
||||
|
||||
\sa QVideoFrame
|
||||
*/
|
||||
|
||||
/*!
|
||||
\enum QAbstractVideoBuffer::HandleType
|
||||
|
||||
Identifies the type of a video buffers handle.
|
||||
|
||||
\value NoHandle The buffer has no handle, its data can only be accessed by mapping the buffer.
|
||||
\value GLTextureHandle The handle of the buffer is an OpenGL texture ID.
|
||||
\value XvShmImageHandle The handle contains pointer to shared memory XVideo image.
|
||||
\value CoreImageHandle The handle contains pointer to Mac OS X CIImage.
|
||||
\value QPixmapHandle The handle of the buffer is a QPixmap.
|
||||
\value UserHandle Start value for user defined handle types.
|
||||
|
||||
\sa handleType()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\enum QAbstractVideoBuffer::MapMode
|
||||
|
||||
Enumerates how a video buffer's data is mapped to memory.
|
||||
|
||||
\value NotMapped The video buffer has is not mapped to memory.
|
||||
\value ReadOnly The mapped memory is populated with data from the video buffer when mapped, but
|
||||
the content of the mapped memory may be discarded when unmapped.
|
||||
\value WriteOnly The mapped memory is uninitialized when mapped, and the content will be used to
|
||||
populate the video buffer when unmapped.
|
||||
\value ReadWrite The mapped memory is populated with data from the video buffer, and the
|
||||
video buffer is repopulated with the content of the mapped memory.
|
||||
|
||||
\sa mapMode(), map()
|
||||
*/
|
||||
|
||||
/*!
|
||||
Constructs an abstract video buffer of the given \a type.
|
||||
*/
|
||||
|
||||
QAbstractVideoBuffer::QAbstractVideoBuffer(HandleType type)
|
||||
: d_ptr(new QAbstractVideoBufferPrivate)
|
||||
{
|
||||
Q_D(QAbstractVideoBuffer);
|
||||
|
||||
d->handleType = type;
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
|
||||
QAbstractVideoBuffer::QAbstractVideoBuffer(QAbstractVideoBufferPrivate &dd, HandleType type)
|
||||
: d_ptr(&dd)
|
||||
{
|
||||
Q_D(QAbstractVideoBuffer);
|
||||
|
||||
d->handleType = type;
|
||||
}
|
||||
|
||||
/*!
|
||||
Destroys an abstract video buffer.
|
||||
*/
|
||||
|
||||
QAbstractVideoBuffer::~QAbstractVideoBuffer()
|
||||
{
|
||||
delete d_ptr;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the type of a video buffer's handle.
|
||||
|
||||
\sa handle()
|
||||
*/
|
||||
|
||||
QAbstractVideoBuffer::HandleType QAbstractVideoBuffer::handleType() const
|
||||
{
|
||||
return d_func()->handleType;
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QAbstractVideoBuffer::mapMode() const
|
||||
|
||||
Returns the mode a video buffer is mapped in.
|
||||
|
||||
\sa map()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QAbstractVideoBuffer::map(MapMode mode, int *numBytes, int *bytesPerLine)
|
||||
|
||||
Maps the contents of a video buffer to memory.
|
||||
|
||||
The map \a mode indicates whether the contents of the mapped memory should be read from and/or
|
||||
written to the buffer. If the map mode includes the QAbstractVideoBuffer::ReadOnly flag the
|
||||
mapped memory will be populated with the content of the video buffer when mapped. If the map
|
||||
mode includes the QAbstractVideoBuffer::WriteOnly flag the content of the mapped memory will be
|
||||
persisted in the buffer when unmapped.
|
||||
|
||||
When access to the data is no longer needed be sure to call the unmap() function to release the
|
||||
mapped memory.
|
||||
|
||||
Returns a pointer to the mapped memory region, or a null pointer if the mapping failed. The
|
||||
size in bytes of the mapped memory region is returned in \a numBytes, and the line stride in \a
|
||||
bytesPerLine.
|
||||
|
||||
When access to the data is no longer needed be sure to unmap() the buffer.
|
||||
|
||||
\note Writing to memory that is mapped as read-only is undefined, and may result in changes
|
||||
to shared data.
|
||||
|
||||
\sa unmap(), mapMode()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QAbstractVideoBuffer::unmap()
|
||||
|
||||
Releases the memory mapped by the map() function
|
||||
|
||||
If the \l {QAbstractVideoBuffer::MapMode}{MapMode} included the QAbstractVideoBuffer::WriteOnly
|
||||
flag this will persist the current content of the mapped memory to the video frame.
|
||||
|
||||
\sa map()
|
||||
*/
|
||||
|
||||
/*!
|
||||
Returns a type specific handle to the data buffer.
|
||||
|
||||
The type of the handle is given by handleType() function.
|
||||
|
||||
\sa handleType()
|
||||
*/
|
||||
|
||||
QVariant QAbstractVideoBuffer::handle() const
|
||||
{
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
|
||||
QT_END_NAMESPACE
|
||||
107
src/multimedia/video/qabstractvideobuffer.h
Normal file
107
src/multimedia/video/qabstractvideobuffer.h
Normal file
@@ -0,0 +1,107 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QABSTRACTVIDEOBUFFER_H
|
||||
#define QABSTRACTVIDEOBUFFER_H
|
||||
|
||||
#include <QtCore/qmetatype.h>
|
||||
|
||||
QT_BEGIN_HEADER
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QT_MODULE(Multimedia)
|
||||
|
||||
class QVariant;
|
||||
|
||||
class QAbstractVideoBufferPrivate;
|
||||
|
||||
class Q_MULTIMEDIA_EXPORT QAbstractVideoBuffer
|
||||
{
|
||||
public:
|
||||
enum HandleType
|
||||
{
|
||||
NoHandle,
|
||||
GLTextureHandle,
|
||||
XvShmImageHandle,
|
||||
CoreImageHandle,
|
||||
QPixmapHandle,
|
||||
UserHandle = 1000
|
||||
};
|
||||
|
||||
enum MapMode
|
||||
{
|
||||
NotMapped = 0x00,
|
||||
ReadOnly = 0x01,
|
||||
WriteOnly = 0x02,
|
||||
ReadWrite = ReadOnly | WriteOnly
|
||||
};
|
||||
|
||||
QAbstractVideoBuffer(HandleType type);
|
||||
virtual ~QAbstractVideoBuffer();
|
||||
|
||||
HandleType handleType() const;
|
||||
|
||||
virtual MapMode mapMode() const = 0;
|
||||
|
||||
virtual uchar *map(MapMode mode, int *numBytes, int *bytesPerLine) = 0;
|
||||
virtual void unmap() = 0;
|
||||
|
||||
virtual QVariant handle() const;
|
||||
|
||||
protected:
|
||||
QAbstractVideoBuffer(QAbstractVideoBufferPrivate &dd, HandleType type);
|
||||
|
||||
QAbstractVideoBufferPrivate *d_ptr;
|
||||
|
||||
private:
|
||||
Q_DECLARE_PRIVATE(QAbstractVideoBuffer)
|
||||
Q_DISABLE_COPY(QAbstractVideoBuffer)
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
Q_DECLARE_METATYPE(QAbstractVideoBuffer::HandleType)
|
||||
Q_DECLARE_METATYPE(QAbstractVideoBuffer::MapMode)
|
||||
|
||||
QT_END_HEADER
|
||||
|
||||
#endif
|
||||
76
src/multimedia/video/qabstractvideobuffer_p.h
Normal file
76
src/multimedia/video/qabstractvideobuffer_p.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QABSTRACTVIDEOBUFFER_P_H
|
||||
#define QABSTRACTVIDEOBUFFER_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include <QtCore/qshareddata.h>
|
||||
#include <QtMultimedia/qabstractvideobuffer.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QAbstractVideoBufferPrivate
|
||||
{
|
||||
public:
|
||||
QAbstractVideoBufferPrivate()
|
||||
: handleType(QAbstractVideoBuffer::NoHandle)
|
||||
{}
|
||||
|
||||
virtual ~QAbstractVideoBufferPrivate()
|
||||
{}
|
||||
|
||||
QAbstractVideoBuffer::HandleType handleType;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
283
src/multimedia/video/qabstractvideosurface.cpp
Normal file
283
src/multimedia/video/qabstractvideosurface.cpp
Normal file
@@ -0,0 +1,283 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qabstractvideosurface_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
/*!
|
||||
\class QAbstractVideoSurface
|
||||
\brief The QAbstractVideoSurface class is a base class for video presentation surfaces.
|
||||
\since 4.6
|
||||
|
||||
The QAbstractVideoSurface class defines the standard interface that video producers use to
|
||||
inter-operate with video presentation surfaces. It is not supposed to be instantiated directly.
|
||||
Instead, you should subclass it to create new video surfaces.
|
||||
|
||||
A video surface presents a continuous stream of identically formatted frames, where the format
|
||||
of each frame is compatible with a stream format supplied when starting a presentation.
|
||||
|
||||
A list of pixel formats a surface can present is given by the supportedPixelFormats() function,
|
||||
and the isFormatSupported() function will test if a video surface format is supported. If a
|
||||
format is not supported the nearestFormat() function may be able to suggest a similar format.
|
||||
For example if a surface supports fixed set of resolutions it may suggest the smallest
|
||||
supported resolution that contains the proposed resolution.
|
||||
|
||||
The start() function takes a supported format and enables a video surface. Once started a
|
||||
surface will begin displaying the frames it receives in the present() function. Surfaces may
|
||||
hold a reference to the buffer of a presented video frame until a new frame is presented or
|
||||
streaming is stopped. The stop() function will disable a surface and a release any video
|
||||
buffers it holds references to.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\enum QAbstractVideoSurface::Error
|
||||
This enum describes the errors that may be returned by the error() function.
|
||||
|
||||
\value NoError No error occurred.
|
||||
\value UnsupportedFormatError A video format was not supported.
|
||||
\value IncorrectFormatError A video frame was not compatible with the format of the surface.
|
||||
\value StoppedError The surface has not been started.
|
||||
\value ResourceError The surface could not allocate some resource.
|
||||
*/
|
||||
|
||||
/*!
|
||||
Constructs a video surface with the given \a parent.
|
||||
*/
|
||||
|
||||
QAbstractVideoSurface::QAbstractVideoSurface(QObject *parent)
|
||||
: QObject(*new QAbstractVideoSurfacePrivate, parent)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
|
||||
QAbstractVideoSurface::QAbstractVideoSurface(QAbstractVideoSurfacePrivate &dd, QObject *parent)
|
||||
: QObject(dd, parent)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Destroys a video surface.
|
||||
*/
|
||||
|
||||
QAbstractVideoSurface::~QAbstractVideoSurface()
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QAbstractVideoSurface::supportedPixelFormats(QAbstractVideoBuffer::HandleType type) const
|
||||
|
||||
Returns a list of pixel formats a video surface can present for a given handle \a type.
|
||||
|
||||
The pixel formats returned for the QAbstractVideoBuffer::NoHandle type are valid for any buffer
|
||||
that can be mapped in read-only mode.
|
||||
|
||||
Types that are first in the list can be assumed to be faster to render.
|
||||
*/
|
||||
|
||||
/*!
|
||||
Tests a video surface \a format to determine if a surface can accept it.
|
||||
|
||||
Returns true if the format is supported by the surface, and false otherwise.
|
||||
*/
|
||||
|
||||
bool QAbstractVideoSurface::isFormatSupported(const QVideoSurfaceFormat &format) const
|
||||
{
|
||||
return supportedPixelFormats(format.handleType()).contains(format.pixelFormat());
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a supported video surface format that is similar to \a format.
|
||||
|
||||
A similar surface format is one that has the same \l {QVideoSurfaceFormat::pixelFormat()}{pixel
|
||||
format} and \l {QVideoSurfaceFormat::handleType()}{handle type} but differs in some of the other
|
||||
properties. For example if there are restrictions on the \l {QVideoSurfaceFormat::frameSize()}
|
||||
{frame sizes} a video surface can accept it may suggest a format with a larger frame size and
|
||||
a \l {QVideoSurfaceFormat::viewport()}{viewport} the size of the original frame size.
|
||||
|
||||
If the format is already supported it will be returned unchanged, or if there is no similar
|
||||
supported format an invalid format will be returned.
|
||||
*/
|
||||
|
||||
QVideoSurfaceFormat QAbstractVideoSurface::nearestFormat(const QVideoSurfaceFormat &format) const
|
||||
{
|
||||
return isFormatSupported(format)
|
||||
? format
|
||||
: QVideoSurfaceFormat();
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QAbstractVideoSurface::supportedFormatsChanged()
|
||||
|
||||
Signals that the set of formats supported by a video surface has changed.
|
||||
|
||||
\sa supportedPixelFormats(), isFormatSupported()
|
||||
*/
|
||||
|
||||
/*!
|
||||
Returns the format of a video surface.
|
||||
*/
|
||||
|
||||
QVideoSurfaceFormat QAbstractVideoSurface::surfaceFormat() const
|
||||
{
|
||||
return d_func()->format;
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QAbstractVideoSurface::surfaceFormatChanged(const QVideoSurfaceFormat &format)
|
||||
|
||||
Signals that the configured \a format of a video surface has changed.
|
||||
|
||||
\sa surfaceFormat(), start()
|
||||
*/
|
||||
|
||||
/*!
|
||||
Starts a video surface presenting \a format frames.
|
||||
|
||||
Returns true if the surface was started, and false if an error occurred.
|
||||
|
||||
\sa isActive(), stop()
|
||||
*/
|
||||
|
||||
bool QAbstractVideoSurface::start(const QVideoSurfaceFormat &format)
|
||||
{
|
||||
Q_D(QAbstractVideoSurface);
|
||||
|
||||
bool wasActive = d->active;
|
||||
|
||||
d->active = true;
|
||||
d->format = format;
|
||||
d->error = NoError;
|
||||
|
||||
emit surfaceFormatChanged(d->format);
|
||||
|
||||
if (!wasActive)
|
||||
emit activeChanged(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
Stops a video surface presenting frames and releases any resources acquired in start().
|
||||
|
||||
\sa isActive(), start()
|
||||
*/
|
||||
|
||||
void QAbstractVideoSurface::stop()
|
||||
{
|
||||
Q_D(QAbstractVideoSurface);
|
||||
|
||||
if (d->active) {
|
||||
d->format = QVideoSurfaceFormat();
|
||||
d->active = false;
|
||||
|
||||
emit activeChanged(false);
|
||||
emit surfaceFormatChanged(d->format);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Indicates whether a video surface has been started.
|
||||
|
||||
Returns true if the surface has been started, and false otherwise.
|
||||
*/
|
||||
|
||||
bool QAbstractVideoSurface::isActive() const
|
||||
{
|
||||
return d_func()->active;
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QAbstractVideoSurface::activeChanged(bool active)
|
||||
|
||||
Signals that the \a active state of a video surface has changed.
|
||||
|
||||
\sa isActive(), start(), stop()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QAbstractVideoSurface::present(const QVideoFrame &frame)
|
||||
|
||||
Presents a video \a frame.
|
||||
|
||||
Returns true if the frame was presented, and false if an error occurred.
|
||||
|
||||
Not all surfaces will block until the presentation of a frame has completed. Calling present()
|
||||
on a non-blocking surface may fail if called before the presentation of a previous frame has
|
||||
completed. In such cases the surface may not return to a ready state until it's had an
|
||||
opportunity to process events.
|
||||
|
||||
If present() fails for any other reason the surface will immediately enter the stopped state
|
||||
and an error() value will be set.
|
||||
|
||||
A video surface must be in the started state for present() to succeed, and the format of the
|
||||
video frame must be compatible with the current video surface format.
|
||||
|
||||
\sa error()
|
||||
*/
|
||||
|
||||
/*!
|
||||
Returns the last error that occurred.
|
||||
|
||||
If a surface fails to start(), or stops unexpectedly this function can be called to discover
|
||||
what error occurred.
|
||||
*/
|
||||
|
||||
QAbstractVideoSurface::Error QAbstractVideoSurface::error() const
|
||||
{
|
||||
return d_func()->error;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the value of error() to \a error.
|
||||
*/
|
||||
|
||||
void QAbstractVideoSurface::setError(Error error)
|
||||
{
|
||||
Q_D(QAbstractVideoSurface);
|
||||
|
||||
d->error = error;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
110
src/multimedia/video/qabstractvideosurface.h
Normal file
110
src/multimedia/video/qabstractvideosurface.h
Normal file
@@ -0,0 +1,110 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QABSTRACTVIDEOSURFACE_H
|
||||
#define QABSTRACTVIDEOSURFACE_H
|
||||
|
||||
#include <QtCore/qobject.h>
|
||||
#include <QtMultimedia/qvideoframe.h>
|
||||
|
||||
QT_BEGIN_HEADER
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QT_MODULE(Multimedia)
|
||||
|
||||
class QRectF;
|
||||
class QVideoSurfaceFormat;
|
||||
|
||||
class QAbstractVideoSurfacePrivate;
|
||||
|
||||
class Q_MULTIMEDIA_EXPORT QAbstractVideoSurface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum Error
|
||||
{
|
||||
NoError,
|
||||
UnsupportedFormatError,
|
||||
IncorrectFormatError,
|
||||
StoppedError,
|
||||
ResourceError
|
||||
};
|
||||
|
||||
explicit QAbstractVideoSurface(QObject *parent = 0);
|
||||
~QAbstractVideoSurface();
|
||||
|
||||
virtual QList<QVideoFrame::PixelFormat> supportedPixelFormats(
|
||||
QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle) const = 0;
|
||||
virtual bool isFormatSupported(const QVideoSurfaceFormat &format) const;
|
||||
virtual QVideoSurfaceFormat nearestFormat(const QVideoSurfaceFormat &format) const;
|
||||
|
||||
QVideoSurfaceFormat surfaceFormat() const;
|
||||
|
||||
virtual bool start(const QVideoSurfaceFormat &format);
|
||||
virtual void stop();
|
||||
|
||||
bool isActive() const;
|
||||
|
||||
virtual bool present(const QVideoFrame &frame) = 0;
|
||||
|
||||
Error error() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void activeChanged(bool active);
|
||||
void surfaceFormatChanged(const QVideoSurfaceFormat &format);
|
||||
void supportedFormatsChanged();
|
||||
|
||||
protected:
|
||||
QAbstractVideoSurface(QAbstractVideoSurfacePrivate &dd, QObject *parent);
|
||||
|
||||
void setError(Error error);
|
||||
|
||||
private:
|
||||
Q_DECLARE_PRIVATE(QAbstractVideoSurface)
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
QT_END_HEADER
|
||||
|
||||
#endif
|
||||
78
src/multimedia/video/qabstractvideosurface_p.h
Normal file
78
src/multimedia/video/qabstractvideosurface_p.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QABSTRACTVIDEOSURFACE_P_H
|
||||
#define QABSTRACTVIDEOSURFACE_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include <QtMultimedia/qabstractvideosurface.h>
|
||||
#include <QtMultimedia/qvideosurfaceformat.h>
|
||||
#include <QtCore/private/qobject_p.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QAbstractVideoSurfacePrivate : public QObjectPrivate
|
||||
{
|
||||
public:
|
||||
QAbstractVideoSurfacePrivate()
|
||||
: error(QAbstractVideoSurface::NoError)
|
||||
, active(false)
|
||||
{
|
||||
}
|
||||
|
||||
mutable QAbstractVideoSurface::Error error;
|
||||
QVideoSurfaceFormat format;
|
||||
bool active;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
106
src/multimedia/video/qimagevideobuffer.cpp
Normal file
106
src/multimedia/video/qimagevideobuffer.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qimagevideobuffer_p.h"
|
||||
|
||||
#include <private/qabstractvideobuffer_p.h>
|
||||
|
||||
#include <qimage.h>
|
||||
#include <qvariant.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QImageVideoBufferPrivate : public QAbstractVideoBufferPrivate
|
||||
{
|
||||
public:
|
||||
QImageVideoBufferPrivate()
|
||||
: mapMode(QAbstractVideoBuffer::NotMapped)
|
||||
{
|
||||
}
|
||||
|
||||
QAbstractVideoBuffer::MapMode mapMode;
|
||||
QImage image;
|
||||
};
|
||||
|
||||
QImageVideoBuffer::QImageVideoBuffer(const QImage &image)
|
||||
: QAbstractVideoBuffer(*new QImageVideoBufferPrivate, NoHandle)
|
||||
{
|
||||
Q_D(QImageVideoBuffer);
|
||||
|
||||
d->image = image;
|
||||
}
|
||||
|
||||
QImageVideoBuffer::~QImageVideoBuffer()
|
||||
{
|
||||
}
|
||||
|
||||
QAbstractVideoBuffer::MapMode QImageVideoBuffer::mapMode() const
|
||||
{
|
||||
return d_func()->mapMode;
|
||||
}
|
||||
|
||||
uchar *QImageVideoBuffer::map(MapMode mode, int *numBytes, int *bytesPerLine)
|
||||
{
|
||||
Q_D(QImageVideoBuffer);
|
||||
|
||||
if (d->mapMode == NotMapped && d->image.bits() && mode != NotMapped) {
|
||||
d->mapMode = mode;
|
||||
|
||||
if (numBytes)
|
||||
*numBytes = d->image.byteCount();
|
||||
|
||||
if (bytesPerLine)
|
||||
*bytesPerLine = d->image.bytesPerLine();
|
||||
|
||||
return d->image.bits();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void QImageVideoBuffer::unmap()
|
||||
{
|
||||
Q_D(QImageVideoBuffer);
|
||||
|
||||
d->mapMode = NotMapped;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
79
src/multimedia/video/qimagevideobuffer_p.h
Normal file
79
src/multimedia/video/qimagevideobuffer_p.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QIMAGEVIDEOBUFFER_P_H
|
||||
#define QIMAGEVIDEOBUFFER_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include <QtMultimedia/qabstractvideobuffer.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QImage;
|
||||
|
||||
class QImageVideoBufferPrivate;
|
||||
|
||||
class Q_MULTIMEDIA_EXPORT QImageVideoBuffer : public QAbstractVideoBuffer
|
||||
{
|
||||
Q_DECLARE_PRIVATE(QImageVideoBuffer)
|
||||
public:
|
||||
QImageVideoBuffer(const QImage &image);
|
||||
~QImageVideoBuffer();
|
||||
|
||||
MapMode mapMode() const;
|
||||
|
||||
uchar *map(MapMode mode, int *numBytes, int *bytesPerLine);
|
||||
void unmap();
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
129
src/multimedia/video/qmemoryvideobuffer.cpp
Normal file
129
src/multimedia/video/qmemoryvideobuffer.cpp
Normal file
@@ -0,0 +1,129 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qmemoryvideobuffer_p.h"
|
||||
|
||||
#include <private/qabstractvideobuffer_p.h>
|
||||
#include <qbytearray.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QMemoryVideoBufferPrivate : public QAbstractVideoBufferPrivate
|
||||
{
|
||||
public:
|
||||
QMemoryVideoBufferPrivate()
|
||||
: bytesPerLine(0)
|
||||
, mapMode(QAbstractVideoBuffer::NotMapped)
|
||||
{
|
||||
}
|
||||
|
||||
int bytesPerLine;
|
||||
QAbstractVideoBuffer::MapMode mapMode;
|
||||
QByteArray data;
|
||||
};
|
||||
|
||||
/*!
|
||||
\class QMemoryVideoBuffer
|
||||
\brief The QMemoryVideoBuffer class provides a system memory allocated video data buffer.
|
||||
\internal
|
||||
|
||||
QMemoryVideoBuffer is the default video buffer for allocating system memory. It may be used to
|
||||
allocate memory for a QVideoFrame without implementing your own QAbstractVideoBuffer.
|
||||
*/
|
||||
|
||||
/*!
|
||||
Constructs a video buffer with an image stride of \a bytesPerLine from a byte \a array.
|
||||
*/
|
||||
QMemoryVideoBuffer::QMemoryVideoBuffer(const QByteArray &array, int bytesPerLine)
|
||||
: QAbstractVideoBuffer(*new QMemoryVideoBufferPrivate, NoHandle)
|
||||
{
|
||||
Q_D(QMemoryVideoBuffer);
|
||||
|
||||
d->data = array;
|
||||
d->bytesPerLine = bytesPerLine;
|
||||
}
|
||||
|
||||
/*!
|
||||
Destroys a system memory allocated video buffer.
|
||||
*/
|
||||
QMemoryVideoBuffer::~QMemoryVideoBuffer()
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
\reimp
|
||||
*/
|
||||
QAbstractVideoBuffer::MapMode QMemoryVideoBuffer::mapMode() const
|
||||
{
|
||||
return d_func()->mapMode;
|
||||
}
|
||||
|
||||
/*!
|
||||
\reimp
|
||||
*/
|
||||
uchar *QMemoryVideoBuffer::map(MapMode mode, int *numBytes, int *bytesPerLine)
|
||||
{
|
||||
Q_D(QMemoryVideoBuffer);
|
||||
|
||||
if (d->mapMode == NotMapped && d->data.data() && mode != NotMapped) {
|
||||
d->mapMode = mode;
|
||||
|
||||
if (numBytes)
|
||||
*numBytes = d->data.size();
|
||||
|
||||
if (bytesPerLine)
|
||||
*bytesPerLine = d->bytesPerLine;
|
||||
|
||||
return reinterpret_cast<uchar *>(d->data.data());
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\reimp
|
||||
*/
|
||||
void QMemoryVideoBuffer::unmap()
|
||||
{
|
||||
d_func()->mapMode = NotMapped;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
83
src/multimedia/video/qmemoryvideobuffer_p.h
Normal file
83
src/multimedia/video/qmemoryvideobuffer_p.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QMEMORYVIDEOBUFFER_P_H
|
||||
#define QMEMORYVIDEOBUFFER_P_H
|
||||
|
||||
#include <QtMultimedia/qabstractvideobuffer.h>
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
QT_BEGIN_HEADER
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QT_MODULE(Multimedia)
|
||||
|
||||
class QMemoryVideoBufferPrivate;
|
||||
|
||||
class Q_MULTIMEDIA_EXPORT QMemoryVideoBuffer : public QAbstractVideoBuffer
|
||||
{
|
||||
Q_DECLARE_PRIVATE(QMemoryVideoBuffer)
|
||||
public:
|
||||
QMemoryVideoBuffer(const QByteArray &data, int bytesPerLine);
|
||||
~QMemoryVideoBuffer();
|
||||
|
||||
MapMode mapMode() const;
|
||||
|
||||
uchar *map(MapMode mode, int *numBytes, int *bytesPerLine);
|
||||
void unmap();
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
QT_END_HEADER
|
||||
|
||||
#endif
|
||||
742
src/multimedia/video/qvideoframe.cpp
Normal file
742
src/multimedia/video/qvideoframe.cpp
Normal file
@@ -0,0 +1,742 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qvideoframe.h"
|
||||
|
||||
#include <private/qimagevideobuffer_p.h>
|
||||
#include <private/qmemoryvideobuffer_p.h>
|
||||
|
||||
#include <qimage.h>
|
||||
#include <qpair.h>
|
||||
#include <qsize.h>
|
||||
#include <qvariant.h>
|
||||
#include <qvector.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QVideoFramePrivate : public QSharedData
|
||||
{
|
||||
public:
|
||||
QVideoFramePrivate()
|
||||
: startTime(-1)
|
||||
, endTime(-1)
|
||||
, data(0)
|
||||
, mappedBytes(0)
|
||||
, bytesPerLine(0)
|
||||
, pixelFormat(QVideoFrame::Format_Invalid)
|
||||
, fieldType(QVideoFrame::ProgressiveFrame)
|
||||
, buffer(0)
|
||||
{
|
||||
}
|
||||
|
||||
QVideoFramePrivate(const QSize &size, QVideoFrame::PixelFormat format)
|
||||
: size(size)
|
||||
, startTime(-1)
|
||||
, endTime(-1)
|
||||
, data(0)
|
||||
, mappedBytes(0)
|
||||
, bytesPerLine(0)
|
||||
, pixelFormat(format)
|
||||
, fieldType(QVideoFrame::ProgressiveFrame)
|
||||
, buffer(0)
|
||||
{
|
||||
}
|
||||
|
||||
~QVideoFramePrivate()
|
||||
{
|
||||
delete buffer;
|
||||
}
|
||||
|
||||
QSize size;
|
||||
qint64 startTime;
|
||||
qint64 endTime;
|
||||
uchar *data;
|
||||
int mappedBytes;
|
||||
int bytesPerLine;
|
||||
QVideoFrame::PixelFormat pixelFormat;
|
||||
QVideoFrame::FieldType fieldType;
|
||||
QAbstractVideoBuffer *buffer;
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(QVideoFramePrivate)
|
||||
};
|
||||
|
||||
/*!
|
||||
\class QVideoFrame
|
||||
\brief The QVideoFrame class provides a representation of a frame of video data.
|
||||
\since 4.6
|
||||
|
||||
A QVideoFrame encapsulates the data of a video frame, and information about the frame.
|
||||
|
||||
The contents of a video frame can be mapped to memory using the map() function. While
|
||||
mapped the video data can accessed using the bits() function which returns a pointer to a
|
||||
buffer, the total size of which is given by the mappedBytes(), and the size of each line is given
|
||||
by bytesPerLine(). The return value of the handle() function may be used to access frame data
|
||||
using the internal buffer's native APIs.
|
||||
|
||||
The video data in a QVideoFrame is encapsulated in a QAbstractVideoBuffer. A QVideoFrame
|
||||
may be constructed from any buffer type by subclassing the QAbstractVideoBuffer class.
|
||||
|
||||
\note QVideoFrame is explicitly shared, any change made to video frame will also apply to any
|
||||
copies.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\enum QVideoFrame::PixelFormat
|
||||
|
||||
Enumerates video data types.
|
||||
|
||||
\value Format_Invalid
|
||||
The frame is invalid.
|
||||
|
||||
\value Format_ARGB32
|
||||
The frame is stored using a 32-bit ARGB format (0xAARRGGBB). This is equivalent to
|
||||
QImage::Format_ARGB32.
|
||||
|
||||
\value Format_ARGB32_Premultiplied
|
||||
The frame stored using a premultiplied 32-bit ARGB format (0xAARRGGBB). This is equivalent
|
||||
to QImage::Format_ARGB32_Premultiplied.
|
||||
|
||||
\value Format_RGB32
|
||||
The frame stored using a 32-bit RGB format (0xffRRGGBB). This is equivalent to
|
||||
QImage::Format_RGB32
|
||||
|
||||
\value Format_RGB24
|
||||
The frame is stored using a 24-bit RGB format (8-8-8). This is equivalent to
|
||||
QImage::Format_RGB888
|
||||
|
||||
\value Format_RGB565
|
||||
The frame is stored using a 16-bit RGB format (5-6-5). This is equivalent to
|
||||
QImage::Format_RGB16.
|
||||
|
||||
\value Format_RGB555
|
||||
The frame is stored using a 16-bit RGB format (5-5-5). This is equivalent to
|
||||
QImage::Format_RGB555.
|
||||
|
||||
\value Format_ARGB8565_Premultiplied
|
||||
The frame is stored using a 24-bit premultiplied ARGB format (8-6-6-5).
|
||||
|
||||
\value Format_BGRA32
|
||||
The frame is stored using a 32-bit ARGB format (0xBBGGRRAA).
|
||||
|
||||
\value Format_BGRA32_Premultiplied
|
||||
The frame is stored using a premultiplied 32bit BGRA format.
|
||||
|
||||
\value Format_BGR32
|
||||
The frame is stored using a 32-bit BGR format (0xBBGGRRff).
|
||||
|
||||
\value Format_BGR24
|
||||
The frame is stored using a 24-bit BGR format (0xBBGGRR).
|
||||
|
||||
\value Format_BGR565
|
||||
The frame is stored using a 16-bit BGR format (5-6-5).
|
||||
|
||||
\value Format_BGR555
|
||||
The frame is stored using a 16-bit BGR format (5-5-5).
|
||||
|
||||
\value Format_BGRA5658_Premultiplied
|
||||
The frame is stored using a 24-bit premultiplied BGRA format (5-6-5-8).
|
||||
|
||||
\value Format_AYUV444
|
||||
The frame is stored using a packed 32-bit AYUV format (0xAAYYUUVV).
|
||||
|
||||
\value Format_AYUV444_Premultiplied
|
||||
The frame is stored using a packed premultiplied 32-bit AYUV format (0xAAYYUUVV).
|
||||
|
||||
\value Format_YUV444
|
||||
The frame is stored using a 24-bit packed YUV format (8-8-8).
|
||||
|
||||
\value Format_YUV420P
|
||||
The frame is stored using an 8-bit per component planar YUV format with the U and V planes
|
||||
horizontally and vertically sub-sampled, i.e. the height and width of the U and V planes are
|
||||
half that of the Y plane.
|
||||
|
||||
\value Format_YV12
|
||||
The frame is stored using an 8-bit per component planar YVU format with the V and U planes
|
||||
horizontally and vertically sub-sampled, i.e. the height and width of the V and U planes are
|
||||
half that of the Y plane.
|
||||
|
||||
\value Format_UYVY
|
||||
The frame is stored using an 8-bit per component packed YUV format with the U and V planes
|
||||
horizontally sub-sampled (U-Y-V-Y), i.e. two horizontally adjacent pixels are stored as a 32-bit
|
||||
macropixel which has a Y value for each pixel and common U and V values.
|
||||
|
||||
\value Format_YUYV
|
||||
The frame is stored using an 8-bit per component packed YUV format with the U and V planes
|
||||
horizontally sub-sampled (Y-U-Y-V), i.e. two horizontally adjacent pixels are stored as a 32-bit
|
||||
macropixel which has a Y value for each pixel and common U and V values.
|
||||
|
||||
\value Format_NV12
|
||||
The frame is stored using an 8-bit per component semi-planar YUV format with a Y plane (Y)
|
||||
followed by a horizontally and vertically sub-sampled, packed UV plane (U-V).
|
||||
|
||||
\value Format_NV21
|
||||
The frame is stored using an 8-bit per component semi-planar YUV format with a Y plane (Y)
|
||||
followed by a horizontally and vertically sub-sampled, packed VU plane (V-U).
|
||||
|
||||
\value Format_IMC1
|
||||
The frame is stored using an 8-bit per component planar YUV format with the U and V planes
|
||||
horizontally and vertically sub-sampled. This is similar to the Format_YUV420P type, except
|
||||
that the bytes per line of the U and V planes are padded out to the same stride as the Y plane.
|
||||
|
||||
\value Format_IMC2
|
||||
The frame is stored using an 8-bit per component planar YUV format with the U and V planes
|
||||
horizontally and vertically sub-sampled. This is similar to the Format_YUV420P type, except
|
||||
that the lines of the U and V planes are interleaved, i.e. each line of U data is followed by a
|
||||
line of V data creating a single line of the same stride as the Y data.
|
||||
|
||||
\value Format_IMC3
|
||||
The frame is stored using an 8-bit per component planar YVU format with the V and U planes
|
||||
horizontally and vertically sub-sampled. This is similar to the Format_YV12 type, except that
|
||||
the bytes per line of the V and U planes are padded out to the same stride as the Y plane.
|
||||
|
||||
\value Format_IMC4
|
||||
The frame is stored using an 8-bit per component planar YVU format with the V and U planes
|
||||
horizontally and vertically sub-sampled. This is similar to the Format_YV12 type, except that
|
||||
the lines of the V and U planes are interleaved, i.e. each line of V data is followed by a line
|
||||
of U data creating a single line of the same stride as the Y data.
|
||||
|
||||
\value Format_Y8
|
||||
The frame is stored using an 8-bit greyscale format.
|
||||
|
||||
\value Format_Y16
|
||||
The frame is stored using a 16-bit linear greyscale format. Little endian.
|
||||
|
||||
\value Format_User
|
||||
Start value for user defined pixel formats.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\enum QVideoFrame::FieldType
|
||||
|
||||
Specifies the field an interlaced video frame belongs to.
|
||||
|
||||
\value ProgressiveFrame The frame is not interlaced.
|
||||
\value TopField The frame contains a top field.
|
||||
\value BottomField The frame contains a bottom field.
|
||||
\value InterlacedFrame The frame contains a merged top and bottom field.
|
||||
*/
|
||||
|
||||
/*!
|
||||
Constructs a null video frame.
|
||||
*/
|
||||
|
||||
QVideoFrame::QVideoFrame()
|
||||
: d(new QVideoFramePrivate)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Constructs a video frame from a \a buffer of the given pixel \a format and \a size in pixels.
|
||||
|
||||
\note This doesn't increment the reference count of the video buffer.
|
||||
*/
|
||||
|
||||
QVideoFrame::QVideoFrame(
|
||||
QAbstractVideoBuffer *buffer, const QSize &size, PixelFormat format)
|
||||
: d(new QVideoFramePrivate(size, format))
|
||||
{
|
||||
d->buffer = buffer;
|
||||
}
|
||||
|
||||
/*!
|
||||
Constructs a video frame of the given pixel \a format and \a size in pixels.
|
||||
|
||||
The \a bytesPerLine (stride) is the length of each scan line in bytes, and \a bytes is the total
|
||||
number of bytes that must be allocated for the frame.
|
||||
*/
|
||||
|
||||
QVideoFrame::QVideoFrame(int bytes, const QSize &size, int bytesPerLine, PixelFormat format)
|
||||
: d(new QVideoFramePrivate(size, format))
|
||||
{
|
||||
if (bytes > 0) {
|
||||
QByteArray data;
|
||||
data.resize(bytes);
|
||||
|
||||
// Check the memory was successfully allocated.
|
||||
if (!data.isEmpty())
|
||||
d->buffer = new QMemoryVideoBuffer(data, bytesPerLine);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Constructs a video frame from an \a image.
|
||||
|
||||
\note This will construct an invalid video frame if there is no frame type equivalent to the
|
||||
image format.
|
||||
|
||||
\sa pixelFormatFromImageFormat()
|
||||
*/
|
||||
|
||||
QVideoFrame::QVideoFrame(const QImage &image)
|
||||
: d(new QVideoFramePrivate(
|
||||
image.size(), pixelFormatFromImageFormat(image.format())))
|
||||
{
|
||||
if (d->pixelFormat != Format_Invalid)
|
||||
d->buffer = new QImageVideoBuffer(image);
|
||||
}
|
||||
|
||||
/*!
|
||||
Constructs a copy of \a other.
|
||||
*/
|
||||
|
||||
QVideoFrame::QVideoFrame(const QVideoFrame &other)
|
||||
: d(other.d)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Assigns the contents of \a other to a video frame.
|
||||
*/
|
||||
|
||||
QVideoFrame &QVideoFrame::operator =(const QVideoFrame &other)
|
||||
{
|
||||
d = other.d;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*!
|
||||
Destroys a video frame.
|
||||
*/
|
||||
|
||||
QVideoFrame::~QVideoFrame()
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Identifies whether a video frame is valid.
|
||||
|
||||
An invalid frame has no video buffer associated with it.
|
||||
|
||||
Returns true if the frame is valid, and false if it is not.
|
||||
*/
|
||||
|
||||
bool QVideoFrame::isValid() const
|
||||
{
|
||||
return d->buffer != 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the color format of a video frame.
|
||||
*/
|
||||
|
||||
QVideoFrame::PixelFormat QVideoFrame::pixelFormat() const
|
||||
{
|
||||
return d->pixelFormat;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the type of a video frame's handle.
|
||||
*/
|
||||
|
||||
QAbstractVideoBuffer::HandleType QVideoFrame::handleType() const
|
||||
{
|
||||
return d->buffer ? d->buffer->handleType() : QAbstractVideoBuffer::NoHandle;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the size of a video frame.
|
||||
*/
|
||||
|
||||
QSize QVideoFrame::size() const
|
||||
{
|
||||
return d->size;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the width of a video frame.
|
||||
*/
|
||||
|
||||
int QVideoFrame::width() const
|
||||
{
|
||||
return d->size.width();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the height of a video frame.
|
||||
*/
|
||||
|
||||
int QVideoFrame::height() const
|
||||
{
|
||||
return d->size.height();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the field an interlaced video frame belongs to.
|
||||
|
||||
If the video is not interlaced this will return WholeFrame.
|
||||
*/
|
||||
|
||||
QVideoFrame::FieldType QVideoFrame::fieldType() const
|
||||
{
|
||||
return d->fieldType;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the \a field an interlaced video frame belongs to.
|
||||
*/
|
||||
|
||||
void QVideoFrame::setFieldType(QVideoFrame::FieldType field)
|
||||
{
|
||||
d->fieldType = field;
|
||||
}
|
||||
|
||||
/*!
|
||||
Identifies if a video frame's contents are currently mapped to system memory.
|
||||
|
||||
This is a convenience function which checks that the \l {QAbstractVideoBuffer::MapMode}{MapMode}
|
||||
of the frame is not equal to QAbstractVideoBuffer::NotMapped.
|
||||
|
||||
Returns true if the contents of the video frame are mapped to system memory, and false
|
||||
otherwise.
|
||||
|
||||
\sa mapMode() QAbstractVideoBuffer::MapMode
|
||||
*/
|
||||
|
||||
bool QVideoFrame::isMapped() const
|
||||
{
|
||||
return d->buffer != 0 && d->buffer->mapMode() != QAbstractVideoBuffer::NotMapped;
|
||||
}
|
||||
|
||||
/*!
|
||||
Identifies if the mapped contents of a video frame will be persisted when the frame is unmapped.
|
||||
|
||||
This is a convenience function which checks if the \l {QAbstractVideoBuffer::MapMode}{MapMode}
|
||||
contains the QAbstractVideoBuffer::WriteOnly flag.
|
||||
|
||||
Returns true if the video frame will be updated when unmapped, and false otherwise.
|
||||
|
||||
\note The result of altering the data of a frame that is mapped in read-only mode is undefined.
|
||||
Depending on the buffer implementation the changes may be persisted, or worse alter a shared
|
||||
buffer.
|
||||
|
||||
\sa mapMode(), QAbstractVideoBuffer::MapMode
|
||||
*/
|
||||
|
||||
bool QVideoFrame::isWritable() const
|
||||
{
|
||||
return d->buffer != 0 && (d->buffer->mapMode() & QAbstractVideoBuffer::WriteOnly);
|
||||
}
|
||||
|
||||
/*!
|
||||
Identifies if the mapped contents of a video frame were read from the frame when it was mapped.
|
||||
|
||||
This is a convenience function which checks if the \l {QAbstractVideoBuffer::MapMode}{MapMode}
|
||||
contains the QAbstractVideoBuffer::WriteOnly flag.
|
||||
|
||||
Returns true if the contents of the mapped memory were read from the video frame, and false
|
||||
otherwise.
|
||||
|
||||
\sa mapMode(), QAbstractVideoBuffer::MapMode
|
||||
*/
|
||||
|
||||
bool QVideoFrame::isReadable() const
|
||||
{
|
||||
return d->buffer != 0 && (d->buffer->mapMode() & QAbstractVideoBuffer::ReadOnly);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the mode a video frame was mapped to system memory in.
|
||||
|
||||
\sa map(), QAbstractVideoBuffer::MapMode
|
||||
*/
|
||||
|
||||
QAbstractVideoBuffer::MapMode QVideoFrame::mapMode() const
|
||||
{
|
||||
return d->buffer != 0 ? d->buffer->mapMode() : QAbstractVideoBuffer::NotMapped;
|
||||
}
|
||||
|
||||
/*!
|
||||
Maps the contents of a video frame to memory.
|
||||
|
||||
The map \a mode indicates whether the contents of the mapped memory should be read from and/or
|
||||
written to the frame. If the map mode includes the QAbstractVideoBuffer::ReadOnly flag the
|
||||
mapped memory will be populated with the content of the video frame when mapped. If the map
|
||||
mode inclues the QAbstractVideoBuffer::WriteOnly flag the content of the mapped memory will be
|
||||
persisted in the frame when unmapped.
|
||||
|
||||
While mapped the contents of a video frame can be accessed directly through the pointer returned
|
||||
by the bits() function.
|
||||
|
||||
When access to the data is no longer needed be sure to call the unmap() function to release the
|
||||
mapped memory.
|
||||
|
||||
Returns true if the buffer was mapped to memory in the given \a mode and false otherwise.
|
||||
|
||||
\sa unmap(), mapMode(), bits()
|
||||
*/
|
||||
|
||||
bool QVideoFrame::map(QAbstractVideoBuffer::MapMode mode)
|
||||
{
|
||||
if (d->buffer != 0 && d->data == 0) {
|
||||
Q_ASSERT(d->bytesPerLine == 0);
|
||||
Q_ASSERT(d->mappedBytes == 0);
|
||||
|
||||
d->data = d->buffer->map(mode, &d->mappedBytes, &d->bytesPerLine);
|
||||
|
||||
return d->data != 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
Releases the memory mapped by the map() function.
|
||||
|
||||
If the \l {QAbstractVideoBuffer::MapMode}{MapMode} included the QAbstractVideoBuffer::WriteOnly
|
||||
flag this will persist the current content of the mapped memory to the video frame.
|
||||
|
||||
\sa map()
|
||||
*/
|
||||
|
||||
void QVideoFrame::unmap()
|
||||
{
|
||||
if (d->data != 0) {
|
||||
d->mappedBytes = 0;
|
||||
d->bytesPerLine = 0;
|
||||
d->data = 0;
|
||||
|
||||
d->buffer->unmap();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the number of bytes in a scan line.
|
||||
|
||||
\note This is the bytes per line of the first plane only. The bytes per line of subsequent
|
||||
planes should be calculated as per the frame type.
|
||||
|
||||
This value is only valid while the frame data is \l {map()}{mapped}.
|
||||
|
||||
\sa bits(), map(), mappedBytes()
|
||||
*/
|
||||
|
||||
int QVideoFrame::bytesPerLine() const
|
||||
{
|
||||
return d->bytesPerLine;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a pointer to the start of the frame data buffer.
|
||||
|
||||
This value is only valid while the frame data is \l {map()}{mapped}.
|
||||
|
||||
\sa map(), mappedBytes(), bytesPerLine()
|
||||
*/
|
||||
|
||||
uchar *QVideoFrame::bits()
|
||||
{
|
||||
return d->data;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a pointer to the start of the frame data buffer.
|
||||
|
||||
This value is only valid while the frame data is \l {map()}{mapped}.
|
||||
|
||||
\sa map(), mappedBytes(), bytesPerLine()
|
||||
*/
|
||||
|
||||
const uchar *QVideoFrame::bits() const
|
||||
{
|
||||
return d->data;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the number of bytes occupied by the mapped frame data.
|
||||
|
||||
This value is only valid while the frame data is \l {map()}{mapped}.
|
||||
|
||||
\sa map()
|
||||
*/
|
||||
|
||||
int QVideoFrame::mappedBytes() const
|
||||
{
|
||||
return d->mappedBytes;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a type specific handle to a video frame's buffer.
|
||||
|
||||
For an OpenGL texture this would be the texture ID.
|
||||
|
||||
\sa QAbstractVideoBuffer::handle()
|
||||
*/
|
||||
|
||||
QVariant QVideoFrame::handle() const
|
||||
{
|
||||
return d->buffer != 0 ? d->buffer->handle() : QVariant();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the presentation time when the frame should be displayed.
|
||||
*/
|
||||
|
||||
qint64 QVideoFrame::startTime() const
|
||||
{
|
||||
return d->startTime;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the presentation \a time when the frame should be displayed.
|
||||
*/
|
||||
|
||||
void QVideoFrame::setStartTime(qint64 time)
|
||||
{
|
||||
d->startTime = time;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the presentation time when a frame should stop being displayed.
|
||||
*/
|
||||
|
||||
qint64 QVideoFrame::endTime() const
|
||||
{
|
||||
return d->endTime;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the presentation \a time when a frame should stop being displayed.
|
||||
*/
|
||||
|
||||
void QVideoFrame::setEndTime(qint64 time)
|
||||
{
|
||||
d->endTime = time;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns an video pixel format equivalent to an image \a format. If there is no equivalent
|
||||
format QVideoFrame::InvalidType is returned instead.
|
||||
*/
|
||||
|
||||
QVideoFrame::PixelFormat QVideoFrame::pixelFormatFromImageFormat(QImage::Format format)
|
||||
{
|
||||
switch (format) {
|
||||
case QImage::Format_Invalid:
|
||||
case QImage::Format_Mono:
|
||||
case QImage::Format_MonoLSB:
|
||||
case QImage::Format_Indexed8:
|
||||
return Format_Invalid;
|
||||
case QImage::Format_RGB32:
|
||||
return Format_RGB32;
|
||||
case QImage::Format_ARGB32:
|
||||
return Format_ARGB32;
|
||||
case QImage::Format_ARGB32_Premultiplied:
|
||||
return Format_ARGB32_Premultiplied;
|
||||
case QImage::Format_RGB16:
|
||||
return Format_RGB565;
|
||||
case QImage::Format_ARGB8565_Premultiplied:
|
||||
return Format_ARGB8565_Premultiplied;
|
||||
case QImage::Format_RGB666:
|
||||
case QImage::Format_ARGB6666_Premultiplied:
|
||||
return Format_Invalid;
|
||||
case QImage::Format_RGB555:
|
||||
return Format_RGB555;
|
||||
case QImage::Format_ARGB8555_Premultiplied:
|
||||
return Format_Invalid;
|
||||
case QImage::Format_RGB888:
|
||||
return Format_RGB24;
|
||||
case QImage::Format_RGB444:
|
||||
case QImage::Format_ARGB4444_Premultiplied:
|
||||
return Format_Invalid;
|
||||
case QImage::NImageFormats:
|
||||
return Format_Invalid;
|
||||
}
|
||||
return Format_Invalid;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns an image format equivalent to a video frame pixel \a format. If there is no equivalent
|
||||
format QImage::Format_Invalid is returned instead.
|
||||
*/
|
||||
|
||||
QImage::Format QVideoFrame::imageFormatFromPixelFormat(PixelFormat format)
|
||||
{
|
||||
switch (format) {
|
||||
case Format_Invalid:
|
||||
return QImage::Format_Invalid;
|
||||
case Format_ARGB32:
|
||||
return QImage::Format_ARGB32;
|
||||
case Format_ARGB32_Premultiplied:
|
||||
return QImage::Format_ARGB32_Premultiplied;
|
||||
case Format_RGB32:
|
||||
return QImage::Format_RGB32;
|
||||
case Format_RGB24:
|
||||
return QImage::Format_RGB888;
|
||||
case Format_RGB565:
|
||||
return QImage::Format_RGB16;
|
||||
case Format_RGB555:
|
||||
return QImage::Format_RGB555;
|
||||
case Format_ARGB8565_Premultiplied:
|
||||
return QImage::Format_ARGB8565_Premultiplied;
|
||||
case Format_BGRA32:
|
||||
case Format_BGRA32_Premultiplied:
|
||||
case Format_BGR32:
|
||||
case Format_BGR24:
|
||||
return QImage::Format_Invalid;
|
||||
case Format_BGR565:
|
||||
case Format_BGR555:
|
||||
case Format_BGRA5658_Premultiplied:
|
||||
case Format_AYUV444:
|
||||
case Format_AYUV444_Premultiplied:
|
||||
case Format_YUV444:
|
||||
case Format_YUV420P:
|
||||
case Format_YV12:
|
||||
case Format_UYVY:
|
||||
case Format_YUYV:
|
||||
case Format_NV12:
|
||||
case Format_NV21:
|
||||
case Format_IMC1:
|
||||
case Format_IMC2:
|
||||
case Format_IMC3:
|
||||
case Format_IMC4:
|
||||
case Format_Y8:
|
||||
case Format_Y16:
|
||||
return QImage::Format_Invalid;
|
||||
case Format_User:
|
||||
return QImage::Format_Invalid;
|
||||
}
|
||||
return QImage::Format_Invalid;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
169
src/multimedia/video/qvideoframe.h
Normal file
169
src/multimedia/video/qvideoframe.h
Normal file
@@ -0,0 +1,169 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QVIDEOFRAME_H
|
||||
#define QVIDEOFRAME_H
|
||||
|
||||
#include <QtCore/qmetatype.h>
|
||||
#include <QtCore/qshareddata.h>
|
||||
#include <QtGui/qimage.h>
|
||||
#include <QtMultimedia/qabstractvideobuffer.h>
|
||||
|
||||
QT_BEGIN_HEADER
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QT_MODULE(Multimedia)
|
||||
|
||||
class QSize;
|
||||
class QVariant;
|
||||
|
||||
class QVideoFramePrivate;
|
||||
|
||||
class Q_MULTIMEDIA_EXPORT QVideoFrame
|
||||
{
|
||||
public:
|
||||
enum FieldType
|
||||
{
|
||||
ProgressiveFrame,
|
||||
TopField,
|
||||
BottomField,
|
||||
InterlacedFrame
|
||||
};
|
||||
|
||||
enum PixelFormat
|
||||
{
|
||||
Format_Invalid,
|
||||
Format_ARGB32,
|
||||
Format_ARGB32_Premultiplied,
|
||||
Format_RGB32,
|
||||
Format_RGB24,
|
||||
Format_RGB565,
|
||||
Format_RGB555,
|
||||
Format_ARGB8565_Premultiplied,
|
||||
Format_BGRA32,
|
||||
Format_BGRA32_Premultiplied,
|
||||
Format_BGR32,
|
||||
Format_BGR24,
|
||||
Format_BGR565,
|
||||
Format_BGR555,
|
||||
Format_BGRA5658_Premultiplied,
|
||||
|
||||
Format_AYUV444,
|
||||
Format_AYUV444_Premultiplied,
|
||||
Format_YUV444,
|
||||
Format_YUV420P,
|
||||
Format_YV12,
|
||||
Format_UYVY,
|
||||
Format_YUYV,
|
||||
Format_NV12,
|
||||
Format_NV21,
|
||||
Format_IMC1,
|
||||
Format_IMC2,
|
||||
Format_IMC3,
|
||||
Format_IMC4,
|
||||
Format_Y8,
|
||||
Format_Y16,
|
||||
|
||||
Format_User = 1000
|
||||
};
|
||||
|
||||
QVideoFrame();
|
||||
QVideoFrame(QAbstractVideoBuffer *buffer, const QSize &size, PixelFormat format);
|
||||
QVideoFrame(int bytes, const QSize &size, int bytesPerLine, PixelFormat format);
|
||||
QVideoFrame(const QImage &image);
|
||||
QVideoFrame(const QVideoFrame &other);
|
||||
~QVideoFrame();
|
||||
|
||||
QVideoFrame &operator =(const QVideoFrame &other);
|
||||
|
||||
bool isValid() const;
|
||||
|
||||
PixelFormat pixelFormat() const;
|
||||
|
||||
QAbstractVideoBuffer::HandleType handleType() const;
|
||||
|
||||
QSize size() const;
|
||||
int width() const;
|
||||
int height() const;
|
||||
|
||||
FieldType fieldType() const;
|
||||
void setFieldType(FieldType);
|
||||
|
||||
bool isMapped() const;
|
||||
bool isReadable() const;
|
||||
bool isWritable() const;
|
||||
|
||||
QAbstractVideoBuffer::MapMode mapMode() const;
|
||||
|
||||
bool map(QAbstractVideoBuffer::MapMode mode);
|
||||
void unmap();
|
||||
|
||||
int bytesPerLine() const;
|
||||
|
||||
uchar *bits();
|
||||
const uchar *bits() const;
|
||||
int mappedBytes() const;
|
||||
|
||||
QVariant handle() const;
|
||||
|
||||
qint64 startTime() const;
|
||||
void setStartTime(qint64 time);
|
||||
|
||||
qint64 endTime() const;
|
||||
void setEndTime(qint64 time);
|
||||
|
||||
static PixelFormat pixelFormatFromImageFormat(QImage::Format format);
|
||||
static QImage::Format imageFormatFromPixelFormat(PixelFormat format);
|
||||
|
||||
private:
|
||||
QExplicitlySharedDataPointer<QVideoFramePrivate> d;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
Q_DECLARE_METATYPE(QVideoFrame::FieldType)
|
||||
Q_DECLARE_METATYPE(QVideoFrame::PixelFormat)
|
||||
|
||||
QT_END_HEADER
|
||||
|
||||
#endif
|
||||
|
||||
704
src/multimedia/video/qvideosurfaceformat.cpp
Normal file
704
src/multimedia/video/qvideosurfaceformat.cpp
Normal file
@@ -0,0 +1,704 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qvideosurfaceformat.h"
|
||||
|
||||
#include <qdebug.h>
|
||||
#include <qmetatype.h>
|
||||
#include <qpair.h>
|
||||
#include <qvariant.h>
|
||||
#include <qvector.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QVideoSurfaceFormatPrivate : public QSharedData
|
||||
{
|
||||
public:
|
||||
QVideoSurfaceFormatPrivate()
|
||||
: pixelFormat(QVideoFrame::Format_Invalid)
|
||||
, handleType(QAbstractVideoBuffer::NoHandle)
|
||||
, scanLineDirection(QVideoSurfaceFormat::TopToBottom)
|
||||
, pixelAspectRatio(1, 1)
|
||||
, ycbcrColorSpace(QVideoSurfaceFormat::YCbCr_Undefined)
|
||||
, frameRate(0.0)
|
||||
{
|
||||
}
|
||||
|
||||
QVideoSurfaceFormatPrivate(
|
||||
const QSize &size,
|
||||
QVideoFrame::PixelFormat format,
|
||||
QAbstractVideoBuffer::HandleType type)
|
||||
: pixelFormat(format)
|
||||
, handleType(type)
|
||||
, scanLineDirection(QVideoSurfaceFormat::TopToBottom)
|
||||
, frameSize(size)
|
||||
, pixelAspectRatio(1, 1)
|
||||
, ycbcrColorSpace(QVideoSurfaceFormat::YCbCr_Undefined)
|
||||
, viewport(QPoint(0, 0), size)
|
||||
, frameRate(0.0)
|
||||
{
|
||||
}
|
||||
|
||||
QVideoSurfaceFormatPrivate(const QVideoSurfaceFormatPrivate &other)
|
||||
: QSharedData(other)
|
||||
, pixelFormat(other.pixelFormat)
|
||||
, handleType(other.handleType)
|
||||
, scanLineDirection(other.scanLineDirection)
|
||||
, frameSize(other.frameSize)
|
||||
, pixelAspectRatio(other.pixelAspectRatio)
|
||||
, ycbcrColorSpace(other.ycbcrColorSpace)
|
||||
, viewport(other.viewport)
|
||||
, frameRate(other.frameRate)
|
||||
, propertyNames(other.propertyNames)
|
||||
, propertyValues(other.propertyValues)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator ==(const QVideoSurfaceFormatPrivate &other) const
|
||||
{
|
||||
if (pixelFormat == other.pixelFormat
|
||||
&& handleType == other.handleType
|
||||
&& scanLineDirection == other.scanLineDirection
|
||||
&& frameSize == other.frameSize
|
||||
&& pixelAspectRatio == other.pixelAspectRatio
|
||||
&& viewport == other.viewport
|
||||
&& frameRatesEqual(frameRate, other.frameRate)
|
||||
&& ycbcrColorSpace == other.ycbcrColorSpace
|
||||
&& propertyNames.count() == other.propertyNames.count()) {
|
||||
for (int i = 0; i < propertyNames.count(); ++i) {
|
||||
int j = other.propertyNames.indexOf(propertyNames.at(i));
|
||||
|
||||
if (j == -1 || propertyValues.at(i) != other.propertyValues.at(j))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline static bool frameRatesEqual(qreal r1, qreal r2)
|
||||
{
|
||||
return qAbs(r1 - r2) <= 0.00001 * qMin(qAbs(r1), qAbs(r2));
|
||||
}
|
||||
|
||||
QVideoFrame::PixelFormat pixelFormat;
|
||||
QAbstractVideoBuffer::HandleType handleType;
|
||||
QVideoSurfaceFormat::Direction scanLineDirection;
|
||||
QSize frameSize;
|
||||
QSize pixelAspectRatio;
|
||||
QVideoSurfaceFormat::YCbCrColorSpace ycbcrColorSpace;
|
||||
QRect viewport;
|
||||
qreal frameRate;
|
||||
QList<QByteArray> propertyNames;
|
||||
QList<QVariant> propertyValues;
|
||||
};
|
||||
|
||||
/*!
|
||||
\class QVideoSurfaceFormat
|
||||
\brief The QVideoSurfaceFormat class specifies the stream format of a video presentation
|
||||
surface.
|
||||
\since 4.6
|
||||
|
||||
A video surface presents a stream of video frames. The surface's format describes the type of
|
||||
the frames and determines how they should be presented.
|
||||
|
||||
The core properties of a video stream required to setup a video surface are the pixel format
|
||||
given by pixelFormat(), and the frame dimensions given by frameSize().
|
||||
|
||||
If the surface is to present frames using a frame's handle a surface format will also include
|
||||
a handle type which is given by the handleType() function.
|
||||
|
||||
The region of a frame that is actually displayed on a video surface is given by the viewport().
|
||||
A stream may have a viewport less than the entire region of a frame to allow for videos smaller
|
||||
than the nearest optimal size of a video frame. For example the width of a frame may be
|
||||
extended so that the start of each scan line is eight byte aligned.
|
||||
|
||||
Other common properties are the pixelAspectRatio(), scanLineDirection(), and frameRate().
|
||||
Additionally a stream may have some additional type specific properties which are listed by the
|
||||
dynamicPropertyNames() function and can be accessed using the property(), and setProperty()
|
||||
functions.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\enum QVideoSurfaceFormat::Direction
|
||||
|
||||
Enumerates the layout direction of video scan lines.
|
||||
|
||||
\value TopToBottom Scan lines are arranged from the top of the frame to the bottom.
|
||||
\value BottomToTop Scan lines are arranged from the bottom of the frame to the top.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\enum QVideoSurfaceFormat::YCbCrColorSpace
|
||||
|
||||
Enumerates the Y'CbCr color space of video frames.
|
||||
|
||||
\value YCbCr_Undefined
|
||||
No color space is specified.
|
||||
|
||||
\value YCbCr_BT601
|
||||
A Y'CbCr color space defined by ITU-R recommendation BT.601
|
||||
with Y value range from 16 to 235, and Cb/Cr range from 16 to 240.
|
||||
Used in standard definition video.
|
||||
|
||||
\value YCbCr_BT709
|
||||
A Y'CbCr color space defined by ITU-R BT.709 with the same values range as YCbCr_BT601. Used
|
||||
for HDTV.
|
||||
|
||||
\value YCbCr_xvYCC601
|
||||
The BT.601 color space with the value range extended to 0 to 255.
|
||||
It is backward compatibile with BT.601 and uses values outside BT.601 range to represent
|
||||
wider colors range.
|
||||
|
||||
\value YCbCr_xvYCC709
|
||||
The BT.709 color space with the value range extended to 0 to 255.
|
||||
|
||||
\value YCbCr_JPEG
|
||||
The full range Y'CbCr color space used in JPEG files.
|
||||
*/
|
||||
|
||||
/*!
|
||||
Constructs a null video stream format.
|
||||
*/
|
||||
|
||||
QVideoSurfaceFormat::QVideoSurfaceFormat()
|
||||
: d(new QVideoSurfaceFormatPrivate)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Contructs a description of stream which receives stream of \a type buffers with given frame
|
||||
\a size and pixel \a format.
|
||||
*/
|
||||
|
||||
QVideoSurfaceFormat::QVideoSurfaceFormat(
|
||||
const QSize& size, QVideoFrame::PixelFormat format, QAbstractVideoBuffer::HandleType type)
|
||||
: d(new QVideoSurfaceFormatPrivate(size, format, type))
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Constructs a copy of \a other.
|
||||
*/
|
||||
|
||||
QVideoSurfaceFormat::QVideoSurfaceFormat(const QVideoSurfaceFormat &other)
|
||||
: d(other.d)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Assigns the values of \a other to a video stream description.
|
||||
*/
|
||||
|
||||
QVideoSurfaceFormat &QVideoSurfaceFormat::operator =(const QVideoSurfaceFormat &other)
|
||||
{
|
||||
d = other.d;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*!
|
||||
Destroys a video stream description.
|
||||
*/
|
||||
|
||||
QVideoSurfaceFormat::~QVideoSurfaceFormat()
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Identifies if a video surface format has a valid pixel format and frame size.
|
||||
|
||||
Returns true if the format is valid, and false otherwise.
|
||||
*/
|
||||
|
||||
bool QVideoSurfaceFormat::isValid() const
|
||||
{
|
||||
return d->pixelFormat != QVideoFrame::Format_Invalid && d->frameSize.isValid();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns true if \a other is the same as a video format, and false if they are the different.
|
||||
*/
|
||||
|
||||
bool QVideoSurfaceFormat::operator ==(const QVideoSurfaceFormat &other) const
|
||||
{
|
||||
return d == other.d || *d == *other.d;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns true if \a other is different to a video format, and false if they are the same.
|
||||
*/
|
||||
|
||||
bool QVideoSurfaceFormat::operator !=(const QVideoSurfaceFormat &other) const
|
||||
{
|
||||
return d != other.d && !(*d == *other.d);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the pixel format of frames in a video stream.
|
||||
*/
|
||||
|
||||
QVideoFrame::PixelFormat QVideoSurfaceFormat::pixelFormat() const
|
||||
{
|
||||
return d->pixelFormat;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the type of handle the surface uses to present the frame data.
|
||||
|
||||
If the handle type is QAbstractVideoBuffer::NoHandle buffers with any handle type are valid
|
||||
provided they can be \l {QAbstractVideoBuffer::map()}{mapped} with the
|
||||
QAbstractVideoBuffer::ReadOnly flag. If the handleType() is not QAbstractVideoBuffer::NoHandle
|
||||
then the handle type of the buffer be the same as that of the surface format.
|
||||
*/
|
||||
|
||||
QAbstractVideoBuffer::HandleType QVideoSurfaceFormat::handleType() const
|
||||
{
|
||||
return d->handleType;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the size of frames in a video stream.
|
||||
|
||||
\sa frameWidth(), frameHeight()
|
||||
*/
|
||||
|
||||
QSize QVideoSurfaceFormat::frameSize() const
|
||||
{
|
||||
return d->frameSize;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the width of frames in a video stream.
|
||||
|
||||
\sa frameSize(), frameHeight()
|
||||
*/
|
||||
|
||||
int QVideoSurfaceFormat::frameWidth() const
|
||||
{
|
||||
return d->frameSize.width();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the height of frame in a video stream.
|
||||
*/
|
||||
|
||||
int QVideoSurfaceFormat::frameHeight() const
|
||||
{
|
||||
return d->frameSize.height();
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the size of frames in a video stream to \a size.
|
||||
|
||||
This will reset the viewport() to fill the entire frame.
|
||||
*/
|
||||
|
||||
void QVideoSurfaceFormat::setFrameSize(const QSize &size)
|
||||
{
|
||||
d->frameSize = size;
|
||||
d->viewport = QRect(QPoint(0, 0), size);
|
||||
}
|
||||
|
||||
/*!
|
||||
\overload
|
||||
|
||||
Sets the \a width and \a height of frames in a video stream.
|
||||
|
||||
This will reset the viewport() to fill the entire frame.
|
||||
*/
|
||||
|
||||
void QVideoSurfaceFormat::setFrameSize(int width, int height)
|
||||
{
|
||||
d->frameSize = QSize(width, height);
|
||||
d->viewport = QRect(0, 0, width, height);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the viewport of a video stream.
|
||||
|
||||
The viewport is the region of a video frame that is actually displayed.
|
||||
|
||||
By default the viewport covers an entire frame.
|
||||
*/
|
||||
|
||||
QRect QVideoSurfaceFormat::viewport() const
|
||||
{
|
||||
return d->viewport;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the viewport of a video stream to \a viewport.
|
||||
*/
|
||||
|
||||
void QVideoSurfaceFormat::setViewport(const QRect &viewport)
|
||||
{
|
||||
d->viewport = viewport;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the direction of scan lines.
|
||||
*/
|
||||
|
||||
QVideoSurfaceFormat::Direction QVideoSurfaceFormat::scanLineDirection() const
|
||||
{
|
||||
return d->scanLineDirection;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the \a direction of scan lines.
|
||||
*/
|
||||
|
||||
void QVideoSurfaceFormat::setScanLineDirection(Direction direction)
|
||||
{
|
||||
d->scanLineDirection = direction;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the frame rate of a video stream in frames per second.
|
||||
*/
|
||||
|
||||
qreal QVideoSurfaceFormat::frameRate() const
|
||||
{
|
||||
return d->frameRate;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the frame \a rate of a video stream in frames per second.
|
||||
*/
|
||||
|
||||
void QVideoSurfaceFormat::setFrameRate(qreal rate)
|
||||
{
|
||||
d->frameRate = rate;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a video stream's pixel aspect ratio.
|
||||
*/
|
||||
|
||||
QSize QVideoSurfaceFormat::pixelAspectRatio() const
|
||||
{
|
||||
return d->pixelAspectRatio;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets a video stream's pixel aspect \a ratio.
|
||||
*/
|
||||
|
||||
void QVideoSurfaceFormat::setPixelAspectRatio(const QSize &ratio)
|
||||
{
|
||||
d->pixelAspectRatio = ratio;
|
||||
}
|
||||
|
||||
/*!
|
||||
\overload
|
||||
|
||||
Sets the \a horizontal and \a vertical elements of a video stream's pixel aspect ratio.
|
||||
*/
|
||||
|
||||
void QVideoSurfaceFormat::setPixelAspectRatio(int horizontal, int vertical)
|
||||
{
|
||||
d->pixelAspectRatio = QSize(horizontal, vertical);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the Y'CbCr color space of a video stream.
|
||||
*/
|
||||
|
||||
QVideoSurfaceFormat::YCbCrColorSpace QVideoSurfaceFormat::yCbCrColorSpace() const
|
||||
{
|
||||
return d->ycbcrColorSpace;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the Y'CbCr color \a space of a video stream.
|
||||
It is only used with raw YUV frame types.
|
||||
*/
|
||||
|
||||
void QVideoSurfaceFormat::setYCbCrColorSpace(QVideoSurfaceFormat::YCbCrColorSpace space)
|
||||
{
|
||||
d->ycbcrColorSpace = space;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a suggested size in pixels for the video stream.
|
||||
|
||||
This is the size of the viewport scaled according to the pixel aspect ratio.
|
||||
*/
|
||||
|
||||
QSize QVideoSurfaceFormat::sizeHint() const
|
||||
{
|
||||
QSize size = d->viewport.size();
|
||||
|
||||
if (d->pixelAspectRatio.height() != 0)
|
||||
size.setWidth(size.width() * d->pixelAspectRatio.width() / d->pixelAspectRatio.height());
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a list of video format dynamic property names.
|
||||
*/
|
||||
|
||||
QList<QByteArray> QVideoSurfaceFormat::propertyNames() const
|
||||
{
|
||||
return (QList<QByteArray>()
|
||||
<< "handleType"
|
||||
<< "pixelFormat"
|
||||
<< "frameSize"
|
||||
<< "frameWidth"
|
||||
<< "viewport"
|
||||
<< "scanLineDirection"
|
||||
<< "frameRate"
|
||||
<< "pixelAspectRatio"
|
||||
<< "sizeHint"
|
||||
<< "yCbCrColorSpace")
|
||||
+ d->propertyNames;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the value of the video format's \a name property.
|
||||
*/
|
||||
|
||||
QVariant QVideoSurfaceFormat::property(const char *name) const
|
||||
{
|
||||
if (qstrcmp(name, "handleType") == 0) {
|
||||
return QVariant::fromValue(d->handleType);
|
||||
} else if (qstrcmp(name, "pixelFormat") == 0) {
|
||||
return QVariant::fromValue(d->pixelFormat);
|
||||
} else if (qstrcmp(name, "handleType") == 0) {
|
||||
return QVariant::fromValue(d->handleType);
|
||||
} else if (qstrcmp(name, "frameSize") == 0) {
|
||||
return d->frameSize;
|
||||
} else if (qstrcmp(name, "frameWidth") == 0) {
|
||||
return d->frameSize.width();
|
||||
} else if (qstrcmp(name, "frameHeight") == 0) {
|
||||
return d->frameSize.height();
|
||||
} else if (qstrcmp(name, "viewport") == 0) {
|
||||
return d->viewport;
|
||||
} else if (qstrcmp(name, "scanLineDirection") == 0) {
|
||||
return QVariant::fromValue(d->scanLineDirection);
|
||||
} else if (qstrcmp(name, "frameRate") == 0) {
|
||||
return QVariant::fromValue(d->frameRate);
|
||||
} else if (qstrcmp(name, "pixelAspectRatio") == 0) {
|
||||
return QVariant::fromValue(d->pixelAspectRatio);
|
||||
} else if (qstrcmp(name, "sizeHint") == 0) {
|
||||
return sizeHint();
|
||||
} else if (qstrcmp(name, "yCbCrColorSpace") == 0) {
|
||||
return QVariant::fromValue(d->ycbcrColorSpace);
|
||||
} else {
|
||||
int id = 0;
|
||||
for (; id < d->propertyNames.count() && d->propertyNames.at(id) != name; ++id) {}
|
||||
|
||||
return id < d->propertyValues.count()
|
||||
? d->propertyValues.at(id)
|
||||
: QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the video format's \a name property to \a value.
|
||||
*/
|
||||
|
||||
void QVideoSurfaceFormat::setProperty(const char *name, const QVariant &value)
|
||||
{
|
||||
if (qstrcmp(name, "handleType") == 0) {
|
||||
// read only.
|
||||
} else if (qstrcmp(name, "pixelFormat") == 0) {
|
||||
// read only.
|
||||
} else if (qstrcmp(name, "frameSize") == 0) {
|
||||
if (value.canConvert<QSize>()) {
|
||||
d->frameSize = qvariant_cast<QSize>(value);
|
||||
d->viewport = QRect(QPoint(0, 0), d->frameSize);
|
||||
}
|
||||
} else if (qstrcmp(name, "frameWidth") == 0) {
|
||||
// read only.
|
||||
} else if (qstrcmp(name, "frameHeight") == 0) {
|
||||
// read only.
|
||||
} else if (qstrcmp(name, "viewport") == 0) {
|
||||
if (value.canConvert<QRect>())
|
||||
d->viewport = qvariant_cast<QRect>(value);
|
||||
} else if (qstrcmp(name, "scanLineDirection") == 0) {
|
||||
if (value.canConvert<Direction>())
|
||||
d->scanLineDirection = qvariant_cast<Direction>(value);
|
||||
} else if (qstrcmp(name, "frameRate") == 0) {
|
||||
if (value.canConvert<qreal>())
|
||||
d->frameRate = qvariant_cast<qreal>(value);
|
||||
} else if (qstrcmp(name, "pixelAspectRatio") == 0) {
|
||||
if (value.canConvert<QSize>())
|
||||
d->pixelAspectRatio = qvariant_cast<QSize>(value);
|
||||
} else if (qstrcmp(name, "sizeHint") == 0) {
|
||||
// read only.
|
||||
} else if (qstrcmp(name, "yCbCrColorSpace") == 0) {
|
||||
if (value.canConvert<YCbCrColorSpace>())
|
||||
d->ycbcrColorSpace = qvariant_cast<YCbCrColorSpace>(value);
|
||||
} else {
|
||||
int id = 0;
|
||||
for (; id < d->propertyNames.count() && d->propertyNames.at(id) != name; ++id) {}
|
||||
|
||||
if (id < d->propertyValues.count()) {
|
||||
if (value.isNull()) {
|
||||
d->propertyNames.removeAt(id);
|
||||
d->propertyValues.removeAt(id);
|
||||
} else {
|
||||
d->propertyValues[id] = value;
|
||||
}
|
||||
} else if (!value.isNull()) {
|
||||
d->propertyNames.append(QByteArray(name));
|
||||
d->propertyValues.append(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifndef QT_NO_DEBUG_STREAM
|
||||
QDebug operator<<(QDebug dbg, const QVideoSurfaceFormat &f)
|
||||
{
|
||||
QString typeName;
|
||||
switch (f.pixelFormat()) {
|
||||
case QVideoFrame::Format_Invalid:
|
||||
typeName = QLatin1String("Format_Invalid");
|
||||
break;
|
||||
case QVideoFrame::Format_ARGB32:
|
||||
typeName = QLatin1String("Format_ARGB32");
|
||||
break;
|
||||
case QVideoFrame::Format_ARGB32_Premultiplied:
|
||||
typeName = QLatin1String("Format_ARGB32_Premultiplied");
|
||||
break;
|
||||
case QVideoFrame::Format_RGB32:
|
||||
typeName = QLatin1String("Format_RGB32");
|
||||
break;
|
||||
case QVideoFrame::Format_RGB24:
|
||||
typeName = QLatin1String("Format_RGB24");
|
||||
break;
|
||||
case QVideoFrame::Format_RGB565:
|
||||
typeName = QLatin1String("Format_RGB565");
|
||||
break;
|
||||
case QVideoFrame::Format_RGB555:
|
||||
typeName = QLatin1String("Format_RGB555");
|
||||
break;
|
||||
case QVideoFrame::Format_ARGB8565_Premultiplied:
|
||||
typeName = QLatin1String("Format_ARGB8565_Premultiplied");
|
||||
break;
|
||||
case QVideoFrame::Format_BGRA32:
|
||||
typeName = QLatin1String("Format_BGRA32");
|
||||
break;
|
||||
case QVideoFrame::Format_BGRA32_Premultiplied:
|
||||
typeName = QLatin1String("Format_BGRA32_Premultiplied");
|
||||
break;
|
||||
case QVideoFrame::Format_BGR32:
|
||||
typeName = QLatin1String("Format_BGR32");
|
||||
break;
|
||||
case QVideoFrame::Format_BGR24:
|
||||
typeName = QLatin1String("Format_BGR24");
|
||||
break;
|
||||
case QVideoFrame::Format_BGR565:
|
||||
typeName = QLatin1String("Format_BGR565");
|
||||
break;
|
||||
case QVideoFrame::Format_BGR555:
|
||||
typeName = QLatin1String("Format_BGR555");
|
||||
break;
|
||||
case QVideoFrame::Format_BGRA5658_Premultiplied:
|
||||
typeName = QLatin1String("Format_BGRA5658_Premultiplied");
|
||||
break;
|
||||
case QVideoFrame::Format_AYUV444:
|
||||
typeName = QLatin1String("Format_AYUV444");
|
||||
break;
|
||||
case QVideoFrame::Format_AYUV444_Premultiplied:
|
||||
typeName = QLatin1String("Format_AYUV444_Premultiplied");
|
||||
break;
|
||||
case QVideoFrame::Format_YUV444:
|
||||
typeName = QLatin1String("Format_YUV444");
|
||||
break;
|
||||
case QVideoFrame::Format_YUV420P:
|
||||
typeName = QLatin1String("Format_YUV420P");
|
||||
break;
|
||||
case QVideoFrame::Format_YV12:
|
||||
typeName = QLatin1String("Format_YV12");
|
||||
break;
|
||||
case QVideoFrame::Format_UYVY:
|
||||
typeName = QLatin1String("Format_UYVY");
|
||||
break;
|
||||
case QVideoFrame::Format_YUYV:
|
||||
typeName = QLatin1String("Format_YUYV");
|
||||
break;
|
||||
case QVideoFrame::Format_NV12:
|
||||
typeName = QLatin1String("Format_NV12");
|
||||
break;
|
||||
case QVideoFrame::Format_NV21:
|
||||
typeName = QLatin1String("Format_NV21");
|
||||
break;
|
||||
case QVideoFrame::Format_IMC1:
|
||||
typeName = QLatin1String("Format_IMC1");
|
||||
break;
|
||||
case QVideoFrame::Format_IMC2:
|
||||
typeName = QLatin1String("Format_IMC2");
|
||||
break;
|
||||
case QVideoFrame::Format_IMC3:
|
||||
typeName = QLatin1String("Format_IMC3");
|
||||
break;
|
||||
case QVideoFrame::Format_IMC4:
|
||||
typeName = QLatin1String("Format_IMC4");
|
||||
break;
|
||||
case QVideoFrame::Format_Y8:
|
||||
typeName = QLatin1String("Format_Y8");
|
||||
break;
|
||||
case QVideoFrame::Format_Y16:
|
||||
typeName = QLatin1String("Format_Y16");
|
||||
default:
|
||||
typeName = QString(QLatin1String("UserType(%1)" )).arg(int(f.pixelFormat()));
|
||||
}
|
||||
|
||||
dbg.nospace() << "QVideoSurfaceFormat(" << typeName;
|
||||
dbg.nospace() << ", " << f.frameSize();
|
||||
dbg.nospace() << ", viewport=" << f.viewport();
|
||||
dbg.nospace() << ", pixelAspectRatio=" << f.pixelAspectRatio();
|
||||
dbg.nospace() << ")";
|
||||
|
||||
foreach(const QByteArray& propertyName, f.propertyNames())
|
||||
dbg << "\n " << propertyName.data() << " = " << f.property(propertyName.data());
|
||||
|
||||
return dbg.space();
|
||||
}
|
||||
#endif
|
||||
|
||||
QT_END_NAMESPACE
|
||||
147
src/multimedia/video/qvideosurfaceformat.h
Normal file
147
src/multimedia/video/qvideosurfaceformat.h
Normal file
@@ -0,0 +1,147 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtMultimedia module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** No Commercial Usage
|
||||
** This file contains pre-release code and may not be distributed.
|
||||
** You may use this file in accordance with the terms and conditions
|
||||
** contained in the Technology Preview License Agreement accompanying
|
||||
** this package.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** If you have questions regarding the use of this file, please contact
|
||||
** Nokia at qt-info@nokia.com.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QVIDEOSURFACEFORMAT_H
|
||||
#define QVIDEOSURFACEFORMAT_H
|
||||
|
||||
#include <QtCore/qlist.h>
|
||||
#include <QtCore/qpair.h>
|
||||
#include <QtCore/qshareddata.h>
|
||||
#include <QtCore/qsize.h>
|
||||
#include <QtGui/qimage.h>
|
||||
#include <QtMultimedia/qvideoframe.h>
|
||||
|
||||
QT_BEGIN_HEADER
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QT_MODULE(Multimedia)
|
||||
|
||||
class QDebug;
|
||||
|
||||
class QVideoSurfaceFormatPrivate;
|
||||
|
||||
class Q_MULTIMEDIA_EXPORT QVideoSurfaceFormat
|
||||
{
|
||||
public:
|
||||
enum Direction
|
||||
{
|
||||
TopToBottom,
|
||||
BottomToTop
|
||||
};
|
||||
|
||||
enum YCbCrColorSpace
|
||||
{
|
||||
YCbCr_Undefined,
|
||||
YCbCr_BT601,
|
||||
YCbCr_BT709,
|
||||
YCbCr_xvYCC601,
|
||||
YCbCr_xvYCC709,
|
||||
YCbCr_JPEG,
|
||||
#ifndef qdoc
|
||||
YCbCr_CustomMatrix
|
||||
#endif
|
||||
};
|
||||
|
||||
QVideoSurfaceFormat();
|
||||
QVideoSurfaceFormat(
|
||||
const QSize &size,
|
||||
QVideoFrame::PixelFormat pixelFormat,
|
||||
QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle);
|
||||
QVideoSurfaceFormat(const QVideoSurfaceFormat &format);
|
||||
~QVideoSurfaceFormat();
|
||||
|
||||
QVideoSurfaceFormat &operator =(const QVideoSurfaceFormat &format);
|
||||
|
||||
bool operator ==(const QVideoSurfaceFormat &format) const;
|
||||
bool operator !=(const QVideoSurfaceFormat &format) const;
|
||||
|
||||
bool isValid() const;
|
||||
|
||||
QVideoFrame::PixelFormat pixelFormat() const;
|
||||
QAbstractVideoBuffer::HandleType handleType() const;
|
||||
|
||||
QSize frameSize() const;
|
||||
void setFrameSize(const QSize &size);
|
||||
void setFrameSize(int width, int height);
|
||||
|
||||
int frameWidth() const;
|
||||
int frameHeight() const;
|
||||
|
||||
QRect viewport() const;
|
||||
void setViewport(const QRect &viewport);
|
||||
|
||||
Direction scanLineDirection() const;
|
||||
void setScanLineDirection(Direction direction);
|
||||
|
||||
qreal frameRate() const;
|
||||
void setFrameRate(qreal rate);
|
||||
|
||||
QSize pixelAspectRatio() const;
|
||||
void setPixelAspectRatio(const QSize &ratio);
|
||||
void setPixelAspectRatio(int width, int height);
|
||||
|
||||
YCbCrColorSpace yCbCrColorSpace() const;
|
||||
void setYCbCrColorSpace(YCbCrColorSpace colorSpace);
|
||||
|
||||
QSize sizeHint() const;
|
||||
|
||||
QList<QByteArray> propertyNames() const;
|
||||
QVariant property(const char *name) const;
|
||||
void setProperty(const char *name, const QVariant &value);
|
||||
|
||||
private:
|
||||
QSharedDataPointer<QVideoSurfaceFormatPrivate> d;
|
||||
};
|
||||
|
||||
#ifndef QT_NO_DEBUG_STREAM
|
||||
Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug, const QVideoSurfaceFormat &);
|
||||
#endif
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
Q_DECLARE_METATYPE(QVideoSurfaceFormat::Direction)
|
||||
Q_DECLARE_METATYPE(QVideoSurfaceFormat::YCbCrColorSpace)
|
||||
|
||||
QT_END_HEADER
|
||||
|
||||
#endif
|
||||
|
||||
21
src/multimedia/video/video.pri
Normal file
21
src/multimedia/video/video.pri
Normal file
@@ -0,0 +1,21 @@
|
||||
|
||||
INCLUDEPATH += $$PWD
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/qabstractvideobuffer.h \
|
||||
$$PWD/qabstractvideobuffer_p.h \
|
||||
$$PWD/qabstractvideosurface.h \
|
||||
$$PWD/qabstractvideosurface_p.h \
|
||||
$$PWD/qimagevideobuffer_p.h \
|
||||
$$PWD/qmemoryvideobuffer_p.h \
|
||||
$$PWD/qvideoframe.h \
|
||||
$$PWD/qvideosurfaceformat.h
|
||||
|
||||
SOURCES += \
|
||||
$$PWD/qabstractvideobuffer.cpp \
|
||||
$$PWD/qabstractvideosurface.cpp \
|
||||
$$PWD/qimagevideobuffer.cpp \
|
||||
$$PWD/qmemoryvideobuffer.cpp \
|
||||
$$PWD/qvideoframe.cpp \
|
||||
$$PWD/qvideosurfaceformat.cpp
|
||||
|
||||
2
src/src.pro
Normal file
2
src/src.pro
Normal file
@@ -0,0 +1,2 @@
|
||||
TEMPLATE = subdirs
|
||||
SUBDIRS += multimedia
|
||||
Reference in New Issue
Block a user