diff options
88 files changed, 12950 insertions, 2 deletions
@@ -43,6 +43,7 @@ my %modules = ( # path to module name map "QtDBus" => "$basedir/src/dbus", "QtWebKit" => "$basedir/src/3rdparty/webkit/WebCore", "phonon" => "$basedir/src/phonon", + "QtMultimedia" => "$basedir/src/multimedia", ); my %moduleheaders = ( # restrict the module headers to those found in relative path "QtWebKit" => "../WebKit/qt/Api", diff --git a/config.tests/unix/alsa/alsa.pro b/config.tests/unix/alsa/alsa.pro new file mode 100644 index 0000000..4931d38 --- /dev/null +++ b/config.tests/unix/alsa/alsa.pro @@ -0,0 +1,4 @@ +SOURCES = alsatest.cpp +LIBS+=-lasound +CONFIG -= qt dylib +mac:CONFIG -= app_bundle diff --git a/config.tests/unix/alsa/alsatest.cpp b/config.tests/unix/alsa/alsatest.cpp new file mode 100644 index 0000000..af2f5d9 --- /dev/null +++ b/config.tests/unix/alsa/alsatest.cpp @@ -0,0 +1,6 @@ +#include <alsa/asoundlib.h> +int main(int argc,char **argv) +{ + return 0; +} + @@ -609,6 +609,7 @@ CFG_NOBUILD_PARTS="" CFG_RELEASE_QMAKE=no CFG_PHONON=auto CFG_PHONON_BACKEND=yes +CFG_MULTIMEDIA=yes CFG_SVG=yes CFG_WEBKIT=auto # (yes|no|auto) @@ -709,6 +710,7 @@ OPT_VERBOSE=no OPT_HELP= CFG_SILENT=no CFG_GRAPHICS_SYSTEM=default +CFG_ALSA=auto # initalize variables used for installation QT_INSTALL_PREFIX= @@ -848,7 +850,7 @@ while [ "$#" -gt 0 ]; do VAL=no ;; #Qt style yes options - -incremental|-qvfb|-profile|-shared|-static|-sm|-xinerama|-xshape|-xshape|-xinput|-reduce-exports|-pch|-separate-debug-info|-stl|-freetype|-xcursor|-xfixes|-xrandr|-xrender|-mitshm|-fontconfig|-xkb|-nis|-qdbus|-dbus|-dbus-linked|-glib|-gstreamer|-gtkstyle|-cups|-iconv|-largefile|-h|-help|-v|-verbose|-debug|-release|-fast|-accessibility|-confirm-license|-gnumake|-framework|-qt3support|-debug-and-release|-exceptions|-cocoa|-universal|-prefix-install|-silent|-armfpa|-optimized-qmake|-dwarf2|-reduce-relocations|-sse|-openssl|-openssl-linked|-ptmalloc|-xmlpatterns|-phonon|-phonon-backend|-svg|-webkit|-scripttools|-rpath|-force-pkg-config) + -incremental|-qvfb|-profile|-shared|-static|-sm|-xinerama|-xshape|-xshape|-xinput|-reduce-exports|-pch|-separate-debug-info|-stl|-freetype|-xcursor|-xfixes|-xrandr|-xrender|-mitshm|-fontconfig|-xkb|-nis|-qdbus|-dbus|-dbus-linked|-glib|-gstreamer|-gtkstyle|-cups|-iconv|-largefile|-h|-help|-v|-verbose|-debug|-release|-fast|-accessibility|-confirm-license|-gnumake|-framework|-qt3support|-debug-and-release|-exceptions|-cocoa|-universal|-prefix-install|-silent|-armfpa|-optimized-qmake|-dwarf2|-reduce-relocations|-sse|-openssl|-openssl-linked|-ptmalloc|-xmlpatterns|-phonon|-phonon-backend|-multimedia|-svg|-webkit|-scripttools|-rpath|-force-pkg-config) VAR=`echo $1 | sed "s,^-\(.*\),\1,"` VAL=yes ;; @@ -1976,6 +1978,13 @@ while [ "$#" -gt 0 ]; do UNKNOWN_OPT=yes fi ;; + multimedia) + if [ "$VAL" = "yes" ] || [ "$VAL" = "no" ]; then + CFG_MULTIMEDIA="$VAL" + else + UNKNOWN_OPT=yes + fi + ;; *) UNKNOWN_OPT=yes ;; @@ -3152,6 +3161,7 @@ Usage: $relconf [-h] [-prefix <dir>] [-prefix-install] [-bindir <dir>] [-libdir [-no-optimized-qmake] [-optimized-qmake] [-no-xmlpatterns] [-xmlpatterns] [-no-phonon] [-phonon] [-no-phonon-backend] [-phonon-backend] [-no-openssl] [-openssl] [-openssl-linked] + [-no-multimedia] [-multimedia] [-no-gtkstyle] [-gtkstyle] [-no-svg] [-svg] [-no-webkit] [-webkit] [-no-scripttools] [-scripttools] @@ -3292,6 +3302,9 @@ fi -no-phonon-backend.. Do not build the platform phonon plugin. + -phonon-backend..... Build the platform phonon plugin. + -no-multimedia ..... Do not build the multimedia module. + * -multimedia ........ Build the multimedia module. + -no-svg ............ Do not build the SVG module. + -svg ............... Build the SVG module. @@ -5664,6 +5677,14 @@ if [ "$CFG_PTMALLOC" != "no" ]; then QMakeVar add QMAKE_LFLAGS "$outpath/lib/libptmalloc3.a" fi +if [ "$CFG_ALSA" = "auto" ]; then + if "$unixtests/compile.test" "$XQMAKESPEC" "$QMAKE_CONFIG" $OPT_VERBOSE "$relpath" "$outpath" config.tests/unix/alsa "alsa" $L_FLAGS $I_FLAGS $l_FLAGS; then + CFG_ALSA=yes + else + CFG_ALSA=no + fi +fi + #------------------------------------------------------------------------------- # ask for all that hasn't been auto-detected or specified in the arguments #------------------------------------------------------------------------------- @@ -6104,6 +6125,10 @@ if [ "$CFG_EXCEPTIONS" != "no" ]; then QTCONFIG_CONFIG="$QTCONFIG_CONFIG exceptions" fi +if [ "$CFG_ALSA" = "yes" ]; then + QT_CONFIG="$QT_CONFIG alsa" +fi + # # Some Qt modules are too advanced in C++ for some old compilers # Detect here the platforms where they are known to work. @@ -6275,6 +6300,12 @@ else QCONFIG_FLAGS="$QCONFIG_FLAGS QT_NO_SCRIPTTOOLS" fi +if [ "$CFG_MULTIMEDIA" = "yes" ]; then + QT_CONFIG="$QT_CONFIG multimedia" +else + QCONFIG_FLAGS="$QCONFIG_FLAGS QT_NO_MULTIMEDIA" +fi + if [ "$CFG_EXCEPTIONS" = "no" ]; then case "$COMPILER" in g++*) @@ -6776,6 +6807,7 @@ fi [ "$CFG_XFIXES" = "runtime" ] && QCONFIG_FLAGS="$QCONFIG_FLAGS QT_RUNTIME_XFIXES" [ "$CFG_XRANDR" = "runtime" ] && QCONFIG_FLAGS="$QCONFIG_FLAGS QT_RUNTIME_XRANDR" [ "$CFG_XINPUT" = "runtime" ] && QCONFIG_FLAGS="$QCONFIG_FLAGS QT_RUNTIME_XINPUT" +[ "$CFG_ALSA" = "no" ] && QCONFIG_FLAGS="$QCONFIG_FLAGS QT_NO_ALSA" # sort QCONFIG_FLAGS for neatness if we can [ '!' -z "$AWK" ] && QCONFIG_FLAGS=`echo $QCONFIG_FLAGS | $AWK '{ gsub(" ", "\n"); print }' | sort | uniq` @@ -7135,6 +7167,7 @@ echo "QtConcurrent code.... $CFG_CONCURRENT" echo "QtScriptTools module $CFG_SCRIPTTOOLS" echo "QtXmlPatterns module $CFG_XMLPATTERNS" echo "Phonon module ....... $CFG_PHONON" +echo "Multimedia module ... $CFG_MULTIMEDIA" echo "SVG module .......... $CFG_SVG" echo "WebKit module ....... $CFG_WEBKIT" echo "STL support ......... $CFG_STL" @@ -7285,6 +7318,7 @@ if [ "$PLATFORM_MAC" = "yes" ] && [ "$CFG_FRAMEWORK" = "yes" ] && [ "$CFG_DEBUG" echo "NOTE: Mac OS X frameworks implicitly build debug and release Qt libraries." echo fi +echo "alsa support ........ $CFG_ALSA" echo sepath=`echo "$relpath" | sed -e 's/\\./\\\\./g'` diff --git a/configure.exe b/configure.exe Binary files differindex 322819e..cf5885a 100644 --- a/configure.exe +++ b/configure.exe diff --git a/demos/qtdemo/xml/examples.xml b/demos/qtdemo/xml/examples.xml index 006bfd0..1b0b533 100644 --- a/demos/qtdemo/xml/examples.xml +++ b/demos/qtdemo/xml/examples.xml @@ -154,6 +154,11 @@ <category dirname="phonon" name="Phonon"> <example filename="musicplayer" name="Music Player" /> </category> + <category dirname="multimedia/audio" name="Multimedia"> + <example filename="audiodevices" name="Audio Devices" /> + <example filename="audiooutput" name="Audio Output" /> + <example filename="audioinput" name="Audio Input" /> + </category> <category dirname="richtext" name="Rich Text"> <example filename="calendar" name="Calendar" /> <example filename="orderform" name="Order Form" /> diff --git a/doc/src/examples-overview.qdoc b/doc/src/examples-overview.qdoc index 4313c43..3db4a45 100644 --- a/doc/src/examples-overview.qdoc +++ b/doc/src/examples-overview.qdoc @@ -238,6 +238,17 @@ These examples demonstrate the basic techniques used to take advantage of OpenGL in Qt applications. + \section1 \l{Qt Examples#Multimedia}{Multimedia} + + \l{Qt Examples#Multimedia} + + Qt provides low-level audio support on linux,windows and mac platforms by default and + an audio plugin API to allow developers to implement there own audio support for + custom devices and platforms. + + These examples demonstrate the basic techniques used to take advantage of + Audio API in Qt applications. + \section1 \l{Qt Examples#SQL}{SQL} \l{Qt Examples#SQL}{\inlineimage sql-examples.png diff --git a/doc/src/examples.qdoc b/doc/src/examples.qdoc index e85acd1..74a9bd8 100644 --- a/doc/src/examples.qdoc +++ b/doc/src/examples.qdoc @@ -255,6 +255,14 @@ \o \l{phonon/musicplayer}{Music Player}\raisedaster \endlist + \section1 Multimedia + + \list + \o \l{multimedia/audio/audiodevices}{Audio Devices}\raisedaster + \o \l{multimedia/audio/audiooutput}{Audio Output}\raisedaster + \o \l{multimedia/audio/audioinput}{Audio Input}\raisedaster + \endlist + \section1 Qt Designer \list diff --git a/doc/src/examples/audiodevices.qdoc b/doc/src/examples/audiodevices.qdoc new file mode 100644 index 0000000..3b8cdd8 --- /dev/null +++ b/doc/src/examples/audiodevices.qdoc @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the documentation 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example multimedia/audio/audiodevices + \title Audio Devices Example + + The Audio Devices example demonstrates the basic use of QAudioDeviceInfo class + provided with Qt. + + Qt provides the QAudioDeviceInfo class to enable audio querying within + a standard application user interface. + + This example allows you to browse audio devices available and try out different + configurations to see if they are supported. +*/ diff --git a/doc/src/examples/audioinput.qdoc b/doc/src/examples/audioinput.qdoc new file mode 100644 index 0000000..5dfe76e --- /dev/null +++ b/doc/src/examples/audioinput.qdoc @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the documentation 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example multimedia/audio/audioinput + \title AudioInput Example + + The Audio Input example demonstrates the basic use of QAudioInput class + provided with Qt. + + Qt provides the QAudioInput class to enable audio functionality within + a standard application user interface. + + This example uses a fast-fourier transform on the input audio from the microphone + and displays the output. +*/ diff --git a/doc/src/examples/audiooutput.qdoc b/doc/src/examples/audiooutput.qdoc new file mode 100644 index 0000000..9c8a75f --- /dev/null +++ b/doc/src/examples/audiooutput.qdoc @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the documentation 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example multimedia/audio/audiooutput + \title Audio Output Example + + The Audio Output example demonstrates the basic use of the QAudioOutput class + provided with Qt. + + This example provides a tone generator to supply continuous audio playback. + The first button allows pause and resume of the playback. + The second button allows toggling between push and pull modes of operation. +*/ diff --git a/examples/examples.pro b/examples/examples.pro index 5855e4f..e6cece9 100644 --- a/examples/examples.pro +++ b/examples/examples.pro @@ -26,6 +26,7 @@ SUBDIRS = \ multitouch \ gestures +contains(QT_CONFIG, multimedia):!static: SUBDIRS += multimedia contains(QT_CONFIG, phonon):!static: SUBDIRS += phonon contains(QT_CONFIG, webkit): SUBDIRS += webkit embedded:SUBDIRS += qws diff --git a/examples/multimedia/README b/examples/multimedia/README new file mode 100644 index 0000000..aa78e36 --- /dev/null +++ b/examples/multimedia/README @@ -0,0 +1,34 @@ + +The example launcher provided with Qt can be used to explore each of the +examples in this directory. + +Documentation for these examples can be found via the Tutorial and Examples +link in the main Qt documentation. + + +Finding the Qt Examples and Demos launcher +========================================== + +On Windows: + +The launcher can be accessed via the Windows Start menu. Select the menu +entry entitled "Qt Examples and Demos" entry in the submenu containing +the Qt tools. + +On Mac OS X: + +For the binary distribution, the qtdemo executable is installed in the +/Developer/Applications/Qt directory. For the source distribution, it is +installed alongside the other Qt tools on the path specified when Qt is +configured. + +On Unix/Linux: + +The qtdemo executable is installed alongside the other Qt tools on the path +specified when Qt is configured. + +On all platforms: + +The source code for the launcher can be found in the demos/qtdemo directory +in the Qt package. This example is built at the same time as the Qt libraries, +tools, examples, and demonstrations. diff --git a/examples/multimedia/audio/audio.pro b/examples/multimedia/audio/audio.pro new file mode 100644 index 0000000..c64bb34 --- /dev/null +++ b/examples/multimedia/audio/audio.pro @@ -0,0 +1,10 @@ +TEMPLATE = subdirs +SUBDIRS = audioinput \ + audiooutput \ + audiodevices + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/multimedia/audio +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS audio.pro README +sources.path = $$[QT_INSTALL_EXAMPLES]/multimedia/audio +INSTALLS += target sources diff --git a/examples/multimedia/audio/audiodevices/audiodevices.cpp b/examples/multimedia/audio/audiodevices/audiodevices.cpp new file mode 100644 index 0000000..2a3af98 --- /dev/null +++ b/examples/multimedia/audio/audiodevices/audiodevices.cpp @@ -0,0 +1,272 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QDebug> +#include <QAudioDeviceInfo> + +#include "audiodevices.h" + +AudioDevicesBase::AudioDevicesBase( QMainWindow *parent, Qt::WFlags f ) +{ + Q_UNUSED(parent) + Q_UNUSED(f) + setupUi( this ); +} + +AudioDevicesBase::~AudioDevicesBase() {} + + +AudioTest::AudioTest( QMainWindow *parent, Qt::WFlags f ) + : AudioDevicesBase( parent, f ) +{ + mode = QAudio::AudioOutput; + modeBox->addItem("Input"); + modeBox->addItem("Output"); + + connect(testButton,SIGNAL(clicked()),SLOT(test())); + connect(modeBox,SIGNAL(activated(int)),SLOT(modeChanged(int))); + connect(deviceBox,SIGNAL(activated(int)),SLOT(deviceChanged(int))); + connect(frequencyBox,SIGNAL(activated(int)),SLOT(freqChanged(int))); + connect(channelsBox,SIGNAL(activated(int)),SLOT(channelChanged(int))); + connect(codecsBox,SIGNAL(activated(int)),SLOT(codecChanged(int))); + connect(sampleSizesBox,SIGNAL(activated(int)),SLOT(sampleSizeChanged(int))); + connect(sampleTypesBox,SIGNAL(activated(int)),SLOT(sampleTypeChanged(int))); + connect(endianBox,SIGNAL(activated(int)),SLOT(endianChanged(int))); + + device = 0; + + modeBox->setCurrentIndex(0); + modeChanged(0); + deviceBox->setCurrentIndex(0); + deviceChanged(0); +} + +AudioTest::~AudioTest() +{ +} + +void AudioTest::test() +{ + // tries to set all the settings picked. + + if(device) { + if(device->isFormatSupported(settings)) { + logOutput->append("Success"); + nearestFreq->setText(""); + nearestChannel->setText(""); + nearestCodec->setText(""); + nearestSampleSize->setText(""); + nearestSampleType->setText(""); + nearestEndian->setText(""); + } else { + QAudioFormat nearest = device->nearestFormat(settings); + logOutput->append(tr("Failed")); + nearestFreq->setText(QString("%1").arg(nearest.frequency())); + nearestChannel->setText(QString("%1").arg(nearest.channels())); + nearestCodec->setText(nearest.codec()); + nearestSampleSize->setText(QString("%1").arg(nearest.sampleSize())); + + switch(nearest.sampleType()) { + case QAudioFormat::SignedInt: + nearestSampleType->setText("SignedInt"); + break; + case QAudioFormat::UnSignedInt: + nearestSampleType->setText("UnSignedInt"); + break; + case QAudioFormat::Float: + nearestSampleType->setText("Float"); + break; + case QAudioFormat::Unknown: + nearestSampleType->setText("Unknown"); + } + switch(nearest.byteOrder()) { + case QAudioFormat::LittleEndian: + nearestEndian->setText("LittleEndian"); + break; + case QAudioFormat::BigEndian: + nearestEndian->setText("BigEndian"); + } + } + } + else + logOutput->append("No Device"); +} + +void AudioTest::modeChanged(int idx) +{ + // mode has changed + if(idx == 0) + mode=QAudio::AudioInput; + else + mode=QAudio::AudioOutput; + + deviceBox->clear(); + QList<QAudioDeviceId> devices = QAudioDeviceInfo::deviceList(mode); + for(int i = 0; i < devices.size(); ++i) { + deviceBox->addItem(QAudioDeviceInfo(devices.at(i)).deviceName(), qVariantFromValue(devices.at(i))); + } +} + +void AudioTest::deviceChanged(int idx) +{ + delete device; + device = 0; + + if (deviceBox->count() == 0) + return; + + // device has changed + deviceHandle = deviceBox->itemData(idx).value<QAudioDeviceId>(); + device = new QAudioDeviceInfo(deviceHandle, this); + + frequencyBox->clear(); + QList<int> freqz = device->supportedFrequencies(); + for(int i = 0; i < freqz.size(); ++i) + frequencyBox->addItem(QString("%1").arg(freqz.at(i))); + if(freqz.size()) + settings.setFrequency(freqz.at(0)); + + channelsBox->clear(); + QList<int> chz = device->supportedChannels(); + for(int i = 0; i < chz.size(); ++i) + channelsBox->addItem(QString("%1").arg(chz.at(i))); + if(chz.size()) + settings.setChannels(chz.at(0)); + + codecsBox->clear(); + QStringList codecz = device->supportedCodecs(); + for(int i = 0; i < codecz.size(); ++i) + codecsBox->addItem(QString("%1").arg(codecz.at(i))); + if(codecz.size()) + settings.setCodec(codecz.at(0)); + // Add false to create failed condition! + codecsBox->addItem("audio/mpeg"); + + sampleSizesBox->clear(); + QList<int> sampleSizez = device->supportedSampleSizes(); + for(int i = 0; i < sampleSizez.size(); ++i) + sampleSizesBox->addItem(QString("%1").arg(sampleSizez.at(i))); + if(sampleSizez.size()) + settings.setSampleSize(sampleSizez.at(0)); + + sampleTypesBox->clear(); + QList<QAudioFormat::SampleType> sampleTypez = device->supportedSampleTypes(); + for(int i = 0; i < sampleTypez.size(); ++i) { + switch(sampleTypez.at(i)) { + case QAudioFormat::SignedInt: + sampleTypesBox->addItem("SignedInt"); + break; + case QAudioFormat::UnSignedInt: + sampleTypesBox->addItem("UnSignedInt"); + break; + case QAudioFormat::Float: + sampleTypesBox->addItem("Float"); + break; + case QAudioFormat::Unknown: + sampleTypesBox->addItem("Unknown"); + } + if(sampleTypez.size()) + settings.setSampleType(sampleTypez.at(0)); + } + + endianBox->clear(); + QList<QAudioFormat::Endian> endianz = device->supportedByteOrders(); + for(int i = 0; i < endianz.size(); ++i) { + switch(endianz.at(i)) { + case QAudioFormat::LittleEndian: + endianBox->addItem("Little Endian"); + break; + case QAudioFormat::BigEndian: + endianBox->addItem("Big Endian"); + break; + } + } + if(endianz.size()) + settings.setByteOrder(endianz.at(0)); +} + +void AudioTest::freqChanged(int idx) +{ + // freq has changed + settings.setFrequency(frequencyBox->itemText(idx).toInt()); +} + +void AudioTest::channelChanged(int idx) +{ + settings.setChannels(channelsBox->itemText(idx).toInt()); +} + +void AudioTest::codecChanged(int idx) +{ + settings.setCodec(codecsBox->itemText(idx)); +} + +void AudioTest::sampleSizeChanged(int idx) +{ + settings.setSampleSize(sampleSizesBox->itemText(idx).toInt()); +} + +void AudioTest::sampleTypeChanged(int idx) +{ + switch(sampleTypesBox->itemText(idx).toInt()) { + case QAudioFormat::SignedInt: + settings.setSampleType(QAudioFormat::SignedInt); + break; + case QAudioFormat::UnSignedInt: + settings.setSampleType(QAudioFormat::UnSignedInt); + break; + case QAudioFormat::Float: + settings.setSampleType(QAudioFormat::Float); + } +} + +void AudioTest::endianChanged(int idx) +{ + switch(endianBox->itemText(idx).toInt()) { + case QAudioFormat::LittleEndian: + settings.setByteOrder(QAudioFormat::LittleEndian); + break; + case QAudioFormat::BigEndian: + settings.setByteOrder(QAudioFormat::BigEndian); + } +} + diff --git a/examples/multimedia/audio/audiodevices/audiodevices.h b/examples/multimedia/audio/audiodevices/audiodevices.h new file mode 100644 index 0000000..34a531b --- /dev/null +++ b/examples/multimedia/audio/audiodevices/audiodevices.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QObject> +#include <QMainWindow> +#include <QAudioDeviceInfo> + +#include "ui_audiodevicesbase.h" + +class AudioDevicesBase : public QMainWindow, public Ui::AudioDevicesBase +{ +public: + AudioDevicesBase( QMainWindow *parent = 0, Qt::WFlags f = 0 ); + virtual ~AudioDevicesBase(); +}; + +class AudioTest : public AudioDevicesBase +{ + Q_OBJECT +public: + AudioTest( QMainWindow *parent = 0, Qt::WFlags f = 0 ); + virtual ~AudioTest(); + + QAudioDeviceId deviceHandle; + QAudioDeviceInfo* device; + QAudioFormat settings; + QAudio::Mode mode; + +private slots: + void modeChanged(int idx); + void deviceChanged(int idx); + void freqChanged(int idx); + void channelChanged(int idx); + void codecChanged(int idx); + void sampleSizeChanged(int idx); + void sampleTypeChanged(int idx); + void endianChanged(int idx); + void test(); +}; + diff --git a/examples/multimedia/audio/audiodevices/audiodevices.pro b/examples/multimedia/audio/audiodevices/audiodevices.pro new file mode 100644 index 0000000..adc4890 --- /dev/null +++ b/examples/multimedia/audio/audiodevices/audiodevices.pro @@ -0,0 +1,12 @@ +HEADERS = audiodevices.h +SOURCES = audiodevices.cpp \ + main.cpp +FORMS += audiodevicesbase.ui + +QT += multimedia + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/multimedia/audio/audiodevices +sources.files = $$SOURCES *.h $$RESOURCES $$FORMS audiodevices.pro +sources.path = $$[QT_INSTALL_EXAMPLES]/multimedia/audio/audiodevices +INSTALLS += target sources diff --git a/examples/multimedia/audio/audiodevices/audiodevicesbase.ui b/examples/multimedia/audio/audiodevices/audiodevicesbase.ui new file mode 100644 index 0000000..674f201 --- /dev/null +++ b/examples/multimedia/audio/audiodevices/audiodevicesbase.ui @@ -0,0 +1,255 @@ +<ui version="4.0" > + <class>AudioDevicesBase</class> + <widget class="QMainWindow" name="AudioDevicesBase" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>504</width> + <height>702</height> + </rect> + </property> + <property name="windowTitle" > + <string>AudioDevicesBase</string> + </property> + <widget class="QWidget" name="centralwidget" > + <property name="geometry" > + <rect> + <x>0</x> + <y>28</y> + <width>504</width> + <height>653</height> + </rect> + </property> + <widget class="QWidget" name="layoutWidget" > + <property name="geometry" > + <rect> + <x>40</x> + <y>21</y> + <width>321</width> + <height>506</height> + </rect> + </property> + <layout class="QGridLayout" name="gridLayout" > + <item row="0" column="0" > + <widget class="QLabel" name="deviceLabel" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Preferred" hsizetype="Preferred" > + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text" > + <string>Device</string> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QLabel" name="modeLabel" > + <property name="text" > + <string>Mode</string> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QComboBox" name="deviceBox" /> + </item> + <item row="1" column="1" > + <widget class="QComboBox" name="modeBox" /> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="actualLabel" > + <property name="frameShape" > + <enum>QFrame::Panel</enum> + </property> + <property name="frameShadow" > + <enum>QFrame::Raised</enum> + </property> + <property name="text" > + <string>Actual Settings</string> + </property> + <property name="alignment" > + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item row="2" column="1" > + <widget class="QLabel" name="nearestLabel" > + <property name="frameShape" > + <enum>QFrame::Panel</enum> + </property> + <property name="frameShadow" > + <enum>QFrame::Raised</enum> + </property> + <property name="text" > + <string>Nearest Settings</string> + </property> + <property name="alignment" > + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item row="3" column="0" > + <widget class="QLabel" name="actualFreqLabel" > + <property name="text" > + <string>Frequency</string> + </property> + </widget> + </item> + <item row="3" column="1" > + <widget class="QLabel" name="nearestFreqLabel" > + <property name="text" > + <string>Frequency</string> + </property> + </widget> + </item> + <item row="4" column="0" > + <widget class="QComboBox" name="frequencyBox" /> + </item> + <item row="4" column="1" > + <widget class="QLineEdit" name="nearestFreq" /> + </item> + <item row="5" column="0" > + <widget class="QLabel" name="actualChannelsLabel" > + <property name="text" > + <string>Channels</string> + </property> + </widget> + </item> + <item row="5" column="1" > + <widget class="QLabel" name="nearestChannelLabel" > + <property name="text" > + <string>Channel</string> + </property> + </widget> + </item> + <item row="6" column="0" > + <widget class="QComboBox" name="channelsBox" /> + </item> + <item row="6" column="1" > + <widget class="QLineEdit" name="nearestChannel" /> + </item> + <item row="7" column="0" > + <widget class="QLabel" name="actualCodecLabel" > + <property name="text" > + <string>Codecs</string> + </property> + </widget> + </item> + <item row="7" column="1" > + <widget class="QLabel" name="nearestCodecLabel" > + <property name="text" > + <string>Codec</string> + </property> + </widget> + </item> + <item row="8" column="0" > + <widget class="QComboBox" name="codecsBox" /> + </item> + <item row="8" column="1" > + <widget class="QLineEdit" name="nearestCodec" /> + </item> + <item row="9" column="0" > + <widget class="QLabel" name="actualSampleSizeLabel" > + <property name="text" > + <string>SampleSize</string> + </property> + </widget> + </item> + <item row="9" column="1" > + <widget class="QLabel" name="nearestSampleSizeLabel" > + <property name="text" > + <string>SampleSize</string> + </property> + </widget> + </item> + <item row="10" column="0" > + <widget class="QComboBox" name="sampleSizesBox" /> + </item> + <item row="10" column="1" > + <widget class="QLineEdit" name="nearestSampleSize" /> + </item> + <item row="11" column="0" > + <widget class="QLabel" name="actualSampleTypeLabel" > + <property name="text" > + <string>SampleType</string> + </property> + </widget> + </item> + <item row="11" column="1" > + <widget class="QLabel" name="nearestSampleTypeLabel" > + <property name="text" > + <string>SampleType</string> + </property> + </widget> + </item> + <item row="12" column="0" > + <widget class="QComboBox" name="sampleTypesBox" /> + </item> + <item row="12" column="1" > + <widget class="QLineEdit" name="nearestSampleType" /> + </item> + <item row="13" column="0" > + <widget class="QLabel" name="actualEndianLabel" > + <property name="text" > + <string>Endianess</string> + </property> + </widget> + </item> + <item row="13" column="1" > + <widget class="QLabel" name="nearestEndianLabel" > + <property name="text" > + <string>Endianess</string> + </property> + </widget> + </item> + <item row="14" column="0" > + <widget class="QComboBox" name="endianBox" /> + </item> + <item row="14" column="1" > + <widget class="QLineEdit" name="nearestEndian" /> + </item> + <item row="15" column="0" colspan="2" > + <widget class="QTextEdit" name="logOutput" > + <property name="minimumSize" > + <size> + <width>0</width> + <height>40</height> + </size> + </property> + </widget> + </item> + <item row="16" column="0" colspan="2" > + <widget class="QPushButton" name="testButton" > + <property name="text" > + <string>Test</string> + </property> + </widget> + </item> + </layout> + </widget> + </widget> + <widget class="QMenuBar" name="menubar" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>504</width> + <height>28</height> + </rect> + </property> + </widget> + <widget class="QStatusBar" name="statusbar" > + <property name="geometry" > + <rect> + <x>0</x> + <y>681</y> + <width>504</width> + <height>21</height> + </rect> + </property> + </widget> + </widget> + <resources/> + <connections/> +</ui> diff --git a/examples/multimedia/audio/audiodevices/main.cpp b/examples/multimedia/audio/audiodevices/main.cpp new file mode 100644 index 0000000..12e413e --- /dev/null +++ b/examples/multimedia/audio/audiodevices/main.cpp @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui> + +#include "audiodevices.h" + +int main(int argv, char **args) +{ + QApplication app(argv, args); + app.setApplicationName("Audio Device Test"); + + AudioTest audio; + audio.show(); + + return app.exec(); +} diff --git a/examples/multimedia/audio/audioinput/audioinput.cpp b/examples/multimedia/audio/audioinput/audioinput.cpp new file mode 100644 index 0000000..ae7d84c --- /dev/null +++ b/examples/multimedia/audio/audioinput/audioinput.cpp @@ -0,0 +1,376 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <stdlib.h> +#include <math.h> + +#include <QDebug> +#include <QPainter> +#include <QVBoxLayout> + +#include <QAudioDeviceInfo> +#include <QAudioInput> +#include "audioinput.h" + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +Spectrum::Spectrum(QObject* parent, QAudioInput* device, float* out) + :QIODevice( parent ) +{ + input = device; + output = out; + + unsigned int i; + + // Allocate sample buffer and initialize sin and cos lookup tables + fftState = (fft_state *) malloc (sizeof(fft_state)); + + for(i = 0; i < BUFFER_SIZE; i++) { + bitReverse[i] = reverseBits(i); + } + for(i = 0; i < BUFFER_SIZE / 2; i++) { + float j = 2 * M_PI * i / BUFFER_SIZE; + costable[i] = cos(j); + sintable[i] = sin(j); + } +} + +Spectrum::~Spectrum() +{ +} + +void Spectrum::start() +{ + open(QIODevice::WriteOnly); +} + +void Spectrum::stop() +{ + close(); +} + +qint64 Spectrum::readData(char *data, qint64 maxlen) +{ + Q_UNUSED(data) + Q_UNUSED(maxlen) + + return 0; +} + +qint64 Spectrum::writeData(const char *data, qint64 len) +{ + performFFT((sound_sample*)data); + emit update(); + + return len; +} + +int Spectrum::reverseBits(unsigned int initial) { + // BIT-REVERSE-COPY(a,A) + + unsigned int reversed = 0, loop; + for(loop = 0; loop < BUFFER_SIZE_LOG; loop++) { + reversed <<= 1; + reversed += (initial & 1); + initial >>= 1; + } + return reversed; +} + +void Spectrum::performFFT(const sound_sample *input) { + /* Convert to reverse bit order for FFT */ + prepFFT(input, fftState->real, fftState->imag); + + /* Calculate FFT */ + calcFFT(fftState->real, fftState->imag); + + /* Convert FFT to intensities */ + outputFFT(fftState->real, fftState->imag); +} + +void Spectrum::prepFFT(const sound_sample *input, float * re, float * im) { + unsigned int i; + float *realptr = re; + float *imagptr = im; + + /* Get input, in reverse bit order */ + for(i = 0; i < BUFFER_SIZE; i++) { + *realptr++ = input[bitReverse[i]]; + *imagptr++ = 0; + } +} + +void Spectrum::calcFFT(float * re, float * im) { + unsigned int i, j, k; + unsigned int exchanges; + float fact_real, fact_imag; + float tmp_real, tmp_imag; + unsigned int factfact; + + /* Set up some variables to reduce calculation in the loops */ + exchanges = 1; + factfact = BUFFER_SIZE / 2; + + /* divide and conquer method */ + for(i = BUFFER_SIZE_LOG; i != 0; i--) { + for(j = 0; j != exchanges; j++) { + fact_real = costable[j * factfact]; + fact_imag = sintable[j * factfact]; + for(k = j; k < BUFFER_SIZE; k += exchanges << 1) { + int k1 = k + exchanges; + tmp_real = fact_real * re[k1] - fact_imag * im[k1]; + tmp_imag = fact_real * im[k1] + fact_imag * re[k1]; + re[k1] = re[k] - tmp_real; + im[k1] = im[k] - tmp_imag; + re[k] += tmp_real; + im[k] += tmp_imag; + } + } + exchanges <<= 1; + factfact >>= 1; + } +} + +void Spectrum::outputFFT(const float * re, const float * im) { + const float *realptr = re; + const float *imagptr = im; + float *outputptr = output; + + float *endptr = output + BUFFER_SIZE / 2; + + /* Convert FFT to intensities */ + + while(outputptr <= endptr) { + *outputptr = (*realptr * *realptr) + (*imagptr * *imagptr); + outputptr++; realptr++; imagptr++; + } + *output /= 4; + *endptr /= 4; +} + + +RenderArea::RenderArea(QWidget *parent) + : QWidget(parent) +{ + setBackgroundRole(QPalette::Base); + setAutoFillBackground(true); + + samples = 0; + sampleSize = 0; + setMinimumHeight(30); + setMinimumWidth(200); +} + +void RenderArea::paintEvent(QPaintEvent * /* event */) +{ + QPainter painter(this); + + if(sampleSize == 0) + return; + + painter.setPen(Qt::red); + int max = 0; + for(int i=0;i<sampleSize;i++) { + int m = (int)(sqrt(samples[i])/32768); + if(m > max) + max = m; + } + int x1,y1,x2,y2; + + for(int i=0;i<10;i++) { + x1 = painter.viewport().left()+11; + y1 = painter.viewport().top()+10+i; + x2 = painter.viewport().right()-20-max; + y2 = painter.viewport().top()+10+i; + if(x2 < painter.viewport().left()+10) + x2 = painter.viewport().left()+10; + + painter.drawLine(QPoint(x1,y1),QPoint(x2,y2)); + } + + painter.setPen(Qt::black); + painter.drawRect(QRect(painter.viewport().left()+10, painter.viewport().top()+10, + painter.viewport().right()-20, painter.viewport().bottom()-20)); +} + +void RenderArea::spectrum(float* output, int size) +{ + samples = output; + sampleSize = size; + repaint(); +} + + +InputTest::InputTest() +{ + QWidget *window = new QWidget; + QVBoxLayout* layout = new QVBoxLayout; + + canvas = new RenderArea; + layout->addWidget(canvas); + + deviceBox = new QComboBox(this); + QList<QAudioDeviceId> devices = QAudioDeviceInfo::deviceList(QAudio::AudioInput); + for(int i = 0; i < devices.size(); ++i) { + deviceBox->addItem(QAudioDeviceInfo(devices.at(i)).deviceName(), qVariantFromValue(devices.at(i))); + } + connect(deviceBox,SIGNAL(activated(int)),SLOT(deviceChanged(int))); + layout->addWidget(deviceBox); + + button = new QPushButton(this); + button->setText(tr("Click for Push Mode")); + connect(button,SIGNAL(clicked()),SLOT(toggleMode())); + layout->addWidget(button); + + button2 = new QPushButton(this); + button2->setText(tr("Click To Suspend")); + connect(button2,SIGNAL(clicked()),SLOT(toggleSuspend())); + layout->addWidget(button2); + + window->setLayout(layout); + setCentralWidget(window); + window->show(); + + buffer = new char[BUFFER_SIZE*10]; + output = new float[1024]; + + pullMode = true; + + format.setFrequency(8000); + format.setChannels(1); + format.setSampleSize(8); + format.setSampleType(QAudioFormat::UnSignedInt); + format.setByteOrder(QAudioFormat::LittleEndian); + format.setCodec("audio/pcm"); + + audioInput = new QAudioInput(format,this); + connect(audioInput,SIGNAL(notify()),SLOT(status())); + connect(audioInput,SIGNAL(stateChanged(QAudio::State)),SLOT(state(QAudio::State))); + spec = new Spectrum(this,audioInput,output); + connect(spec,SIGNAL(update()),SLOT(refreshDisplay())); + spec->start(); + audioInput->start(spec); +} + +InputTest::~InputTest() {} + +void InputTest::status() +{ + qWarning()<<"bytesReady = "<<audioInput->bytesReady()<<" bytes, clock = "<<audioInput->clock()<<"ms, totalTime = "<<audioInput->totalTime()/1000<<"ms"; +} + +void InputTest::readMore() +{ + if(!audioInput) + return; + qint64 len = audioInput->bytesReady(); + if(len > BUFFER_SIZE*10) + len = BUFFER_SIZE*10; + qint64 l = input->read(buffer,len); + if(l > 0) { + spec->write(buffer,l); + } +} + +void InputTest::toggleMode() +{ + // Change bewteen pull and push modes + audioInput->stop(); + + if(pullMode) { + button->setText(tr("Click for Push Mode")); + input = audioInput->start(0); + connect(input,SIGNAL(readyRead()),SLOT(readMore())); + pullMode = false; + } else { + button->setText(tr("Click for Pull Mode")); + pullMode = true; + audioInput->start(spec); + } +} + +void InputTest::toggleSuspend() +{ + // toggle suspend/resume + if(audioInput->state() == QAudio::SuspendState) { + qWarning()<<"status: Suspended, resume()"; + audioInput->resume(); + button2->setText("Click To Suspend"); + } else if (audioInput->state() == QAudio::ActiveState) { + qWarning()<<"status: Active, suspend()"; + audioInput->suspend(); + button2->setText("Click To Resume"); + } else if (audioInput->state() == QAudio::StopState) { + qWarning()<<"status: Stopped, resume()"; + audioInput->resume(); + button2->setText("Click To Suspend"); + } else if (audioInput->state() == QAudio::IdleState) { + qWarning()<<"status: IdleState"; + } +} + +void InputTest::state(QAudio::State state) +{ + qWarning()<<" state="<<state; +} + +void InputTest::refreshDisplay() +{ + canvas->spectrum(output,256); + canvas->repaint(); +} + +void InputTest::deviceChanged(int idx) +{ + spec->stop(); + audioInput->stop(); + audioInput->disconnect(this); + delete audioInput; + + device = deviceBox->itemData(idx).value<QAudioDeviceId>(); + audioInput = new QAudioInput(device, format, this); + connect(audioInput,SIGNAL(notify()),SLOT(status())); + connect(audioInput,SIGNAL(stateChanged(QAudio::State)),SLOT(state(QAudio::State))); + spec->start(); + audioInput->start(spec); +} diff --git a/examples/multimedia/audio/audioinput/audioinput.h b/examples/multimedia/audio/audioinput/audioinput.h new file mode 100644 index 0000000..3a6b356 --- /dev/null +++ b/examples/multimedia/audio/audioinput/audioinput.h @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QPixmap> +#include <QWidget> +#include <QObject> +#include <QMainWindow> +#include <QPushButton> +#include <QComboBox> + +#include <qaudioinput.h> + +#define BUFFER_SIZE_LOG 9 +#define BUFFER_SIZE (1 << BUFFER_SIZE_LOG) + +struct _struct_fft_state { + float real[BUFFER_SIZE]; + float imag[BUFFER_SIZE]; +}; +typedef _struct_fft_state fft_state; +typedef short int sound_sample; + +class Spectrum : public QIODevice +{ + Q_OBJECT +public: + Spectrum(QObject* parent, QAudioInput* device, float* out); + ~Spectrum(); + + void start(); + void stop(); + + qint64 readData(char *data, qint64 maxlen); + qint64 writeData(const char *data, qint64 len); + + QAudioInput* input; + float* output; + fft_state* fftState; + + unsigned int bitReverse[BUFFER_SIZE]; + float sintable[BUFFER_SIZE / 2]; + float costable[BUFFER_SIZE / 2]; + + void prepFFT (const sound_sample *input, float *re, float *im); + void calcFFT (float *re, float *im); + void outputFFT (const float *re, const float *im); + int reverseBits (unsigned int initial); + void performFFT (const sound_sample *input); + +signals: + void update(); +}; + + +class RenderArea : public QWidget +{ + Q_OBJECT + +public: + RenderArea(QWidget *parent = 0); + + void spectrum(float* output, int size); + +protected: + void paintEvent(QPaintEvent *event); + +private: + QPixmap pixmap; + + float* samples; + int sampleSize; +}; + +class InputTest : public QMainWindow +{ + Q_OBJECT +public: + InputTest(); + ~InputTest(); + + QAudioDeviceId device; + QAudioFormat format; + QAudioInput* audioInput; + Spectrum* spec; + QIODevice* input; + RenderArea* canvas; + + bool pullMode; + + QPushButton* button; + QPushButton* button2; + QComboBox* deviceBox; + + char* buffer; + float* output; + +private slots: + void refreshDisplay(); + void status(); + void readMore(); + void toggleMode(); + void toggleSuspend(); + void state(QAudio::State s); + void deviceChanged(int idx); +}; + diff --git a/examples/multimedia/audio/audioinput/audioinput.pro b/examples/multimedia/audio/audioinput/audioinput.pro new file mode 100644 index 0000000..d930750 --- /dev/null +++ b/examples/multimedia/audio/audioinput/audioinput.pro @@ -0,0 +1,12 @@ +HEADERS = audioinput.h +SOURCES = audioinput.cpp \ + main.cpp + +QT += multimedia + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/multimedia/audio/audioinput +sources.files = $$SOURCES *.h $$RESOURCES $$FORMS audioinput.pro +sources.path = $$[QT_INSTALL_EXAMPLES]/multimedia/audio/audioinput +INSTALLS += target sources + diff --git a/examples/multimedia/audio/audioinput/main.cpp b/examples/multimedia/audio/audioinput/main.cpp new file mode 100644 index 0000000..64a9b04 --- /dev/null +++ b/examples/multimedia/audio/audioinput/main.cpp @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui> + +#include "audioinput.h" + +int main(int argv, char **args) +{ + QApplication app(argv, args); + app.setApplicationName("Audio Input Test"); + + InputTest input; + input.show(); + + return app.exec(); +} diff --git a/examples/multimedia/audio/audiooutput/audiooutput.cpp b/examples/multimedia/audio/audiooutput/audiooutput.cpp new file mode 100644 index 0000000..be26f19 --- /dev/null +++ b/examples/multimedia/audio/audiooutput/audiooutput.cpp @@ -0,0 +1,280 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QDebug> +#include <QVBoxLayout> + +#include <QAudioOutput> +#include <QAudioDeviceInfo> +#include "audiooutput.h" + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +Generator::Generator(QObject *parent) + :QIODevice( parent ) +{ + finished = false; + buffer = new char[SECONDS*44100*4+1000]; + t=buffer; + len=fillData(t+4,450,SECONDS); /* left channel, 450Hz sine */ + len+=fillData(t+6,452,SECONDS); /* right channel, 452Hz sine */ + putLong(t,len); + putLong(buffer+4,len+8+16+8); + pos = 0; + total = len+8+16+8; +} + +Generator::~Generator() +{ + delete [] buffer; +} + +void Generator::start() +{ + open(QIODevice::ReadOnly); +} + +void Generator::stop() +{ + close(); +} + +int Generator::putShort(char *t, unsigned int value) +{ + *(unsigned char *)(t++)=value&255; + *(unsigned char *)(t)=(value/256)&255; + return 2; +} + +int Generator::putLong(char *t, unsigned int value) +{ + *(unsigned char *)(t++)=value&255; + *(unsigned char *)(t++)=(value/256)&255; + *(unsigned char *)(t++)=(value/(256*256))&255; + *(unsigned char *)(t)=(value/(256*256*256))&255; + return 4; +} + +int Generator::fillData(char *start, int frequency, int seconds) +{ + int i, len=0; + int value; + for(i=0; i<seconds*44100; i++) { + value=(int)(32767.0*sin(2.0*M_PI*((double)(i))*(double)(frequency)/44100.0)); + putShort(start, value); + start += 4; + len+=2; + } + return len; +} + +qint64 Generator::readData(char *data, qint64 maxlen) +{ + int len = maxlen; + if(len > 16384) + len = 16384; + + if(len < (SECONDS*44100*4+1000)-pos) { + // Normal + memcpy(data,t+pos,len); + pos+=len; + return len; + } else { + // Whats left and reset to start + qint64 left = (SECONDS*44100*4+1000)-pos; + memcpy(data,t+pos,left); + pos=0; + return left; + } +} + +qint64 Generator::writeData(const char *data, qint64 len) +{ + Q_UNUSED(data); + Q_UNUSED(len); + + return 0; +} + +AudioTest::AudioTest() +{ + QWidget *window = new QWidget; + QVBoxLayout* layout = new QVBoxLayout; + + deviceBox = new QComboBox(this); + QList<QAudioDeviceId> devices = QAudioDeviceInfo::deviceList(QAudio::AudioOutput); + for(int i = 0; i < devices.size(); ++i) { + deviceBox->addItem(QAudioDeviceInfo(devices.at(i)).deviceName(), qVariantFromValue(devices.at(i))); + } + connect(deviceBox,SIGNAL(activated(int)),SLOT(deviceChanged(int))); + layout->addWidget(deviceBox); + + button = new QPushButton(this); + button->setText(tr("Click for Push Mode")); + connect(button,SIGNAL(clicked()),SLOT(toggle())); + layout->addWidget(button); + + button2 = new QPushButton(this); + button2->setText(tr("Click To Suspend")); + connect(button2,SIGNAL(clicked()),SLOT(togglePlay())); + layout->addWidget(button2); + + window->setLayout(layout); + setCentralWidget(window); + window->show(); + + buffer = new char[BUFFER_SIZE]; + + gen = new Generator(this); + + pullMode = true; + + timer = new QTimer(this); + connect(timer,SIGNAL(timeout()),SLOT(writeMore())); + + gen->start(); + + settings.setFrequency(44100); + settings.setChannels(2); + settings.setSampleSize(16); + settings.setCodec("audio/pcm"); + settings.setByteOrder(QAudioFormat::LittleEndian); + settings.setSampleType(QAudioFormat::SignedInt); + audioOutput = new QAudioOutput(settings,this); + connect(audioOutput,SIGNAL(notify()),SLOT(status())); + connect(audioOutput,SIGNAL(stateChanged(QAudio::State)),SLOT(state(QAudio::State))); + + audioOutput->start(gen); +} + +AudioTest::~AudioTest() +{ + delete [] buffer; +} + +void AudioTest::deviceChanged(int idx) +{ + timer->stop(); + gen->stop(); + audioOutput->stop(); + audioOutput->disconnect(this); + delete audioOutput; + + device = deviceBox->itemData(idx).value<QAudioDeviceId>(); + audioOutput = new QAudioOutput(device,settings,this); + connect(audioOutput,SIGNAL(notify()),SLOT(status())); + connect(audioOutput,SIGNAL(stateChanged(QAudio::State)),SLOT(state(QAudio::State))); + gen->start(); + audioOutput->start(gen); +} + +void AudioTest::status() +{ + qWarning()<<"byteFree = "<<audioOutput->bytesFree()<<" bytes, clock = "<<audioOutput->clock()<<"ms, totalTime = "<<audioOutput->totalTime()/1000<<"ms"; +} + +void AudioTest::writeMore() +{ + if(!audioOutput) + return; + + if(audioOutput->state() == QAudio::StopState) + return; + + int l; + int out; + + int chunks = audioOutput->bytesFree()/audioOutput->periodSize(); + while(chunks) { + l = gen->read(buffer,audioOutput->periodSize()); + if(l > 0) + out = output->write(buffer,l); + if(l != audioOutput->periodSize()) + break; + chunks--; + } +} + +void AudioTest::toggle() +{ + // Change between pull and push modes + + timer->stop(); + audioOutput->stop(); + + if (pullMode) { + button->setText("Click for Pull Mode"); + output = audioOutput->start(0); + pullMode = false; + timer->start(20); + } else { + button->setText("Click for Push Mode"); + pullMode = true; + audioOutput->start(gen); + } +} + +void AudioTest::togglePlay() +{ + // toggle suspend/resume + if(audioOutput->state() == QAudio::SuspendState) { + qWarning()<<"status: Suspended, resume()"; + audioOutput->resume(); + button2->setText("Click To Suspend"); + } else if (audioOutput->state() == QAudio::ActiveState) { + qWarning()<<"status: Active, suspend()"; + audioOutput->suspend(); + button2->setText("Click To Resume"); + } else if (audioOutput->state() == QAudio::StopState) { + qWarning()<<"status: Stopped, resume()"; + audioOutput->resume(); + button2->setText("Click To Suspend"); + } else if (audioOutput->state() == QAudio::IdleState) { + qWarning()<<"status: IdleState"; + } +} + +void AudioTest::state(QAudio::State state) +{ + qWarning()<<" state="<<state; +} diff --git a/examples/multimedia/audio/audiooutput/audiooutput.h b/examples/multimedia/audio/audiooutput/audiooutput.h new file mode 100644 index 0000000..1e21b48 --- /dev/null +++ b/examples/multimedia/audio/audiooutput/audiooutput.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <math.h> + +#define BUFFER_SIZE 32768 +#define SECONDS 3 + +#include <QObject> +#include <QMainWindow> +#include <QIODevice> +#include <QTimer> +#include <QPushButton> +#include <QComboBox> + +#include <QAudioOutput> + +class Generator : public QIODevice +{ + Q_OBJECT +public: + Generator(QObject *parent); + ~Generator(); + + void start(); + void stop(); + + char *t; + int len; + int pos; + int total; + char *buffer; + bool finished; + int chunk_size; + + qint64 readData(char *data, qint64 maxlen); + qint64 writeData(const char *data, qint64 len); + +private: + int putShort(char *t, unsigned int value); + int putLong(char *t, unsigned int value); + int fillData(char *start, int frequency, int seconds); +}; + +class AudioTest : public QMainWindow +{ + Q_OBJECT +public: + AudioTest(); + ~AudioTest(); + + QAudioDeviceId device; + Generator* gen; + QAudioOutput* audioOutput; + QIODevice* output; + QTimer* timer; + QAudioFormat settings; + + bool pullMode; + char* buffer; + + QPushButton* button; + QPushButton* button2; + QComboBox* deviceBox; + +private slots: + void status(); + void writeMore(); + void toggle(); + void togglePlay(); + void state(QAudio::State s); + void deviceChanged(int idx); +}; + diff --git a/examples/multimedia/audio/audiooutput/audiooutput.pro b/examples/multimedia/audio/audiooutput/audiooutput.pro new file mode 100644 index 0000000..08f43ce --- /dev/null +++ b/examples/multimedia/audio/audiooutput/audiooutput.pro @@ -0,0 +1,11 @@ +HEADERS = audiooutput.h +SOURCES = audiooutput.cpp \ + main.cpp + +QT += multimedia + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/multimedia/audio/audiooutput +sources.files = $$SOURCES *.h $$RESOURCES $$FORMS audiooutput.pro +sources.path = $$[QT_INSTALL_EXAMPLES]/multimedia/audio/audiooutput +INSTALLS += target sources diff --git a/examples/multimedia/audio/audiooutput/main.cpp b/examples/multimedia/audio/audiooutput/main.cpp new file mode 100644 index 0000000..fc319bd --- /dev/null +++ b/examples/multimedia/audio/audiooutput/main.cpp @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtGui> + +#include "audiooutput.h" + +int main(int argv, char **args) +{ + QApplication app(argv, args); + app.setApplicationName("Audio Output Test"); + + AudioTest audio; + audio.show(); + + return app.exec(); +} diff --git a/examples/multimedia/multimedia.pro b/examples/multimedia/multimedia.pro new file mode 100644 index 0000000..ac78b15 --- /dev/null +++ b/examples/multimedia/multimedia.pro @@ -0,0 +1,8 @@ +TEMPLATE = subdirs +SUBDIRS = audio + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/multimedia +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS multimedia.pro README +sources.path = $$[QT_INSTALL_EXAMPLES]/multimedia +INSTALLS += target sources diff --git a/mkspecs/features/qt.prf b/mkspecs/features/qt.prf index 1bac953..a03a313 100644 --- a/mkspecs/features/qt.prf +++ b/mkspecs/features/qt.prf @@ -36,7 +36,7 @@ INCLUDEPATH = $$QMAKE_INCDIR_QT $$INCLUDEPATH #prepending prevents us from picki win32:INCLUDEPATH += $$QMAKE_INCDIR_QT/ActiveQt # As order does matter for static libs, we reorder the QT variable here -TMPLIBS = webkit phonon dbus testlib script scripttools svg qt3support sql xmlpatterns xml egl opengl openvg gui network core +TMPLIBS = multimedia webkit phonon dbus testlib script scripttools svg qt3support sql xmlpatterns xml egl opengl openvg gui network core for(QTLIB, $$list($$TMPLIBS)) { contains(QT, $$QTLIB): QT_ORDERED += $$QTLIB } @@ -152,6 +152,7 @@ for(QTLIB, $$list($$lower($$unique(QT)))) { else:isEqual(QTLIB, dbus):qlib = QtDBus else:isEqual(QTLIB, phonon):qlib = phonon else:isEqual(QTLIB, webkit):qlib = QtWebKit + else:isEqual(QTLIB, multimedia):qlib = QtMultimedia else:message("Unknown QT: $$QTLIB"):qlib = !isEmpty(qlib) { target_qt:isEqual(TARGET, qlib) { diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h index 18d5a9f..1711f16 100644 --- a/src/corelib/global/qglobal.h +++ b/src/corelib/global/qglobal.h @@ -1173,6 +1173,11 @@ class QDataStream; # else # define Q_OPENGL_EXPORT Q_DECL_IMPORT # endif +# if defined(QT_BUILD_MULTIMEDIA_LIB) +# define Q_MULTIMEDIA_EXPORT Q_DECL_EXPORT +# else +# define Q_MULTIMEDIA_EXPORT Q_DECL_IMPORT +# endif # if defined(QT_BUILD_OPENVG_LIB) # define Q_OPENVG_EXPORT Q_DECL_EXPORT # else @@ -1217,6 +1222,7 @@ class QDataStream; # define Q_SVG_EXPORT Q_DECL_IMPORT # define Q_CANVAS_EXPORT Q_DECL_IMPORT # define Q_OPENGL_EXPORT Q_DECL_IMPORT +# define Q_MULTIMEDIA_EXPORT Q_DECL_IMPORT # define Q_OPENVG_EXPORT Q_DECL_IMPORT # define Q_XML_EXPORT Q_DECL_IMPORT # define Q_XMLPATTERNS_EXPORT Q_DECL_IMPORT @@ -1243,6 +1249,7 @@ class QDataStream; # define Q_NETWORK_EXPORT Q_DECL_EXPORT # define Q_SVG_EXPORT Q_DECL_EXPORT # define Q_OPENGL_EXPORT Q_DECL_EXPORT +# define Q_MULTIMEDIA_EXPORT Q_DECL_EXPORT # define Q_OPENVG_EXPORT Q_DECL_EXPORT # define Q_XML_EXPORT Q_DECL_EXPORT # define Q_XMLPATTERNS_EXPORT Q_DECL_EXPORT @@ -1256,6 +1263,7 @@ class QDataStream; # define Q_NETWORK_EXPORT # define Q_SVG_EXPORT # define Q_OPENGL_EXPORT +# define Q_MULTIMEDIA_EXPORT # define Q_XML_EXPORT # define Q_XMLPATTERNS_EXPORT # define Q_SCRIPT_EXPORT @@ -2305,12 +2313,14 @@ QT3_SUPPORT Q_CORE_EXPORT const char *qInstallPathSysconf(); #define QT_MODULE_DBUS 0x08000 #define QT_MODULE_SCRIPTTOOLS 0x10000 #define QT_MODULE_OPENVG 0x20000 +#define QT_MODULE_MULTIMEDIA 0x40000 /* Qt editions */ #define QT_EDITION_CONSOLE (QT_MODULE_CORE \ | QT_MODULE_NETWORK \ | QT_MODULE_SQL \ | QT_MODULE_SCRIPT \ + | QT_MODULE_MULTIMEDIA \ | QT_MODULE_XML \ | QT_MODULE_XMLPATTERNS \ | QT_MODULE_TEST \ @@ -2326,6 +2336,7 @@ QT3_SUPPORT Q_CORE_EXPORT const char *qInstallPathSysconf(); | QT_MODULE_OPENGL \ | QT_MODULE_OPENVG \ | QT_MODULE_SQL \ + | QT_MODULE_MULTIMEDIA \ | QT_MODULE_XML \ | QT_MODULE_XMLPATTERNS \ | QT_MODULE_SCRIPT \ @@ -2374,6 +2385,9 @@ QT_LICENSED_MODULE(OpenVG) #if (QT_EDITION & QT_MODULE_SQL) QT_LICENSED_MODULE(Sql) #endif +#if (QT_EDITION & QT_MODULE_MULTIMEDIA) +QT_LICENSED_MODULE(Multimedia) +#endif #if (QT_EDITION & QT_MODULE_XML) QT_LICENSED_MODULE(Xml) #endif diff --git a/src/multimedia/audio/audio.pri b/src/multimedia/audio/audio.pri new file mode 100644 index 0000000..3ddb23b --- /dev/null +++ b/src/multimedia/audio/audio.pri @@ -0,0 +1,56 @@ +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 \ + $$PWD/qaudiodeviceid.h \ + $$PWD/qaudiodeviceid_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 \ + $$PWD/qaudiodeviceid.cpp + +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 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: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 += -lasound + } + } +} diff --git a/src/multimedia/audio/qaudio.cpp b/src/multimedia/audio/qaudio.cpp new file mode 100644 index 0000000..5ba6493 --- /dev/null +++ b/src/multimedia/audio/qaudio.cpp @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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. + \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 SuspendState The audio device is in a suspended state, this state will only be entered + after suspend() is called. + \value StopState 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 + diff --git a/src/multimedia/audio/qaudio.h b/src/multimedia/audio/qaudio.h new file mode 100644 index 0000000..b996cc6 --- /dev/null +++ b/src/multimedia/audio/qaudio.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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, SuspendState, StopState, 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 diff --git a/src/multimedia/audio/qaudio_mac.cpp b/src/multimedia/audio/qaudio_mac.cpp new file mode 100644 index 0000000..cfa7ca3 --- /dev/null +++ b/src/multimedia/audio/qaudio_mac.cpp @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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 & kLinearPCMFormatFlagIsBigEndian != 0 ? QAudioFormat::BigEndian : QAudioFormat::LittleEndian); + QAudioFormat::SampleType type = QAudioFormat::UnSignedInt; + if ((sf.mFormatFlags & kLinearPCMFormatFlagIsSignedInteger) != 0) + type = QAudioFormat::SignedInt; + else if ((sf.mFormatFlags & kLinearPCMFormatFlagIsFloat) != 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; + } + + 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 + + diff --git a/src/multimedia/audio/qaudio_mac_p.h b/src/multimedia/audio/qaudio_mac_p.h new file mode 100644 index 0000000..8e2d522 --- /dev/null +++ b/src/multimedia/audio/qaudio_mac_p.h @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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 + + diff --git a/src/multimedia/audio/qaudiodevicefactory.cpp b/src/multimedia/audio/qaudiodevicefactory.cpp new file mode 100644 index 0000000..35e9c91 --- /dev/null +++ b/src/multimedia/audio/qaudiodevicefactory.cpp @@ -0,0 +1,250 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qdebug.h> +#include <QtMultimedia/qaudioengine.h> +#include <QtMultimedia/qaudioengineplugin.h> +#include <private/qfactoryloader_p.h> +#include "qaudiodevicefactory_p.h" +#include "qaudiodeviceid_p.h" + +#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" +#endif + +QT_BEGIN_NAMESPACE + +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, + (QAudioEngineFactoryInterface_iid, QLatin1String("/audio"), Qt::CaseInsensitive)) + + +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 totalTime() const { return 0; } + qint64 clock() const { return 0; } + QAudio::Error error() const { return QAudio::OpenError; } + QAudio::State state() const { return QAudio::StopState; } + 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 totalTime() const { return 0; } + qint64 clock() const { return 0; } + QAudio::Error error() const { return QAudio::OpenError; } + QAudio::State state() const { return QAudio::StopState; } + QAudioFormat format() const { return QAudioFormat(); } +}; + +QList<QAudioDeviceId> QAudioDeviceFactory::deviceList(QAudio::Mode mode) +{ + QList<QAudioDeviceId> devices; +#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA)) + foreach (const QByteArray &handle, QAudioDeviceInfoPrivate::deviceList(mode)) + devices += createDeviceId(QLatin1String("builtin"), mode, handle); +#endif + QFactoryLoader* l = loader(); + + foreach (QString const& key, l->keys()) { + QAudioEngineFactoryInterface* plugin = qobject_cast<QAudioEngineFactoryInterface*>(l->instance(key)); + if (plugin) { + foreach (QByteArray const& handle, plugin->deviceList(mode)) + devices += createDeviceId(key, mode, handle); + } + + delete plugin; + } + + return devices; +} + +QAudioDeviceId QAudioDeviceFactory::defaultInputDevice() +{ + QAudioEngineFactoryInterface* plugin = qobject_cast<QAudioEngineFactoryInterface*>(loader()->instance(QLatin1String("default"))); + + if (plugin) { + QList<QByteArray> list = plugin->deviceList(QAudio::AudioInput); + if (list.size() > 0) + return createDeviceId(QLatin1String("default"), QAudio::AudioInput, list.at(0)); + } +#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA)) + return createDeviceId(QLatin1String("builtin"), QAudio::AudioInput, QAudioDeviceInfoPrivate::defaultInputDevice()); +#endif + return QAudioDeviceId(); +} + +QAudioDeviceId QAudioDeviceFactory::defaultOutputDevice() +{ + QAudioEngineFactoryInterface* plugin = qobject_cast<QAudioEngineFactoryInterface*>(loader()->instance(QLatin1String("default"))); + + if (plugin) { + QList<QByteArray> list = plugin->deviceList(QAudio::AudioOutput); + if (list.size() > 0) + return createDeviceId(QLatin1String("default"), QAudio::AudioOutput, list.at(0)); + } +#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA)) + return createDeviceId(QLatin1String("builtin"), QAudio::AudioOutput, QAudioDeviceInfoPrivate::defaultOutputDevice()); +#endif + return QAudioDeviceId(); +} + +QAbstractAudioDeviceInfo* QAudioDeviceFactory::audioDeviceInfo(QAudioDeviceId const& id) +{ + if (id.isNull()) + return new QNullDeviceInfo(); +#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA)) + if (id.d->key == QLatin1String("builtin")) + return new QAudioDeviceInfoPrivate(id.d->handle, QAudio::Mode(id.d->mode)); +#endif + QAudioEngineFactoryInterface* plugin = qobject_cast<QAudioEngineFactoryInterface*>(loader()->instance(id.d->key)); + + if (plugin) + return plugin->createDeviceInfo(id.d->handle, QAudio::Mode(id.d->mode)); + + return new QNullDeviceInfo(); +} + +QAbstractAudioInput* QAudioDeviceFactory::createDefaultInputDevice(QAudioFormat const &format) +{ + return createInputDevice(defaultInputDevice(), format); +} + +QAbstractAudioOutput* QAudioDeviceFactory::createDefaultOutputDevice(QAudioFormat const &format) +{ + return createOutputDevice(defaultOutputDevice(), format); +} + +QAbstractAudioInput* QAudioDeviceFactory::createInputDevice(QAudioDeviceId const& id, QAudioFormat const &format) +{ + if (id.isNull()) + return new QNullInputDevice(); +#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA)) + if (id.d->key == QLatin1String("builtin")) { + if(!defaultInputDevice().isNull()) + return new QAudioInputPrivate(id.d->handle, format); + else + return new QNullInputDevice(); + } +#endif + QAudioEngineFactoryInterface* plugin = qobject_cast<QAudioEngineFactoryInterface*>(loader()->instance((id.d->key))); + + if (plugin) + return plugin->createInput(id.d->handle, format); + + return new QNullInputDevice(); +} + +QAbstractAudioOutput* QAudioDeviceFactory::createOutputDevice(QAudioDeviceId const& id, QAudioFormat const &format) +{ + if (id.isNull()) + return new QNullOutputDevice(); +#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA)) + if (id.d->key == QLatin1String("builtin")) { + if(!defaultOutputDevice().isNull()) + return new QAudioOutputPrivate(id.d->handle, format); + else + return new QNullOutputDevice(); + } +#endif + QAudioEngineFactoryInterface* plugin = qobject_cast<QAudioEngineFactoryInterface*>(loader()->instance(id.d->key)); + + if (plugin) + return plugin->createOutput(id.d->handle, format); + + return new QNullOutputDevice(); +} + +QAudioDeviceId QAudioDeviceFactory::createDeviceId(QString const& key, int mode, QByteArray const& handle) +{ + return QAudioDeviceId(new QAudioDeviceIdPrivate(key, mode, handle)); +} + +QT_END_NAMESPACE + diff --git a/src/multimedia/audio/qaudiodevicefactory_p.h b/src/multimedia/audio/qaudiodevicefactory_p.h new file mode 100644 index 0000000..a8e2b28 --- /dev/null +++ b/src/multimedia/audio/qaudiodevicefactory_p.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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/qaudiodeviceid.h> +#include <QtMultimedia/qaudiodeviceinfo.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + +class QAbstractAudioInput; +class QAbstractAudioOutput; + + +class QAudioDeviceFactory +{ +public: + static QList<QAudioDeviceId> deviceList(QAudio::Mode mode); + + static QAudioDeviceId defaultInputDevice(); + static QAudioDeviceId defaultOutputDevice(); + + static QAbstractAudioDeviceInfo* audioDeviceInfo(QAudioDeviceId const &device); + + static QAbstractAudioInput* createDefaultInputDevice(QAudioFormat const &format); + static QAbstractAudioOutput* createDefaultOutputDevice(QAudioFormat const &format); + + static QAbstractAudioInput* createInputDevice(QAudioDeviceId const &device, QAudioFormat const &format); + static QAbstractAudioOutput* createOutputDevice(QAudioDeviceId const &device, QAudioFormat const &format); + + static QAbstractAudioInput* createNullInput(); + static QAbstractAudioOutput* createNullOutput(); + + static QAudioDeviceId createDeviceId(QString const& key, int mode, QByteArray const& handle); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QAUDIODEVICEFACTORY_P_H + diff --git a/src/multimedia/audio/qaudiodeviceid.cpp b/src/multimedia/audio/qaudiodeviceid.cpp new file mode 100644 index 0000000..21a9cd8 --- /dev/null +++ b/src/multimedia/audio/qaudiodeviceid.cpp @@ -0,0 +1,168 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qstring.h> +#include <QtCore/qbytearray.h> +#include <QtCore/qdatastream.h> + +#include <QtMultimedia/qaudiodeviceid.h> +#include "qaudiodeviceid_p.h" + + +QT_BEGIN_NAMESPACE + +/*! + \class QAudioDeviceId + \brief The QAudioDeviceId class provides means for identifying a unique input or output device on a system. + + \inmodule QtMultimedia + \ingroup multimedia + \since 4.6 + + \sa QAudioDeviceInfo, QAudioOutput, QAudioInput +*/ + +/*! + Construct a new null QAudioDeviceId. +*/ + +QAudioDeviceId::QAudioDeviceId() +{ +} + +/*! + Copy the QAudDeviceId referenced by \a other. +*/ + +QAudioDeviceId::QAudioDeviceId(const QAudioDeviceId &other): + d(other.d) +{ +} + +/*! + Destroy the QAudioDeviceId. +*/ + +QAudioDeviceId::~QAudioDeviceId() +{ +} + +/*! + Make a copy of the \a other QAudioDeviceId. +*/ + +QAudioDeviceId& QAudioDeviceId::operator=(const QAudioDeviceId &other) +{ + d = other.d; + return *this; +} + +/*! + Compare with the \a other QAudioDeviceId, return true if they are the same; + otherwise false. +*/ + +bool QAudioDeviceId::operator==(const QAudioDeviceId &other) const +{ + return (d.constData() == 0 && other.d.constData() == 0) || + (d.constData() != 0 && other.d.constData() != 0 && + d->key == other.d->key && d->mode == other.d->mode && d->handle == other.d->handle); +} + +/*! + Compare with the \a other QAudioDeviceId, return false if they are the same; + otherwise true. +*/ + +bool QAudioDeviceId::operator!=(const QAudioDeviceId &other) const +{ + return !(*this == other); +} + +/*! + Returns true if this is not a valid QAudioDeviceId; otherwise false. +*/ + +bool QAudioDeviceId::isNull() const +{ + return d.constData() == 0; +} + +/*! + \internal +*/ + +QAudioDeviceId::QAudioDeviceId(QAudioDeviceIdPrivate* data): + d(data) +{ +} + +/*! + \internal +*/ + +QAudioDeviceIdPrivate::QAudioDeviceIdPrivate(QString const& k, int m, QByteArray const& h): + key(k), mode(m), handle(h) +{ +} + +#ifndef QT_NO_DATASTREAM +Q_MULTIMEDIA_EXPORT QDataStream &operator<<(QDataStream &s, const QAudioDeviceId &id) +{ + s << id.d->key << id.d->mode << id.d->handle; + return s; +} + +Q_MULTIMEDIA_EXPORT QDataStream &operator>>(QDataStream &s, QAudioDeviceId &id) +{ + QString key; + int mode; + QByteArray handle; + + s >> key >> mode >> handle; + id = QAudioDeviceId(new QAudioDeviceIdPrivate(key, mode, handle)); + + return s; +} +#endif + + +QT_END_NAMESPACE diff --git a/src/multimedia/audio/qaudiodeviceid.h b/src/multimedia/audio/qaudiodeviceid.h new file mode 100644 index 0000000..f9188d3 --- /dev/null +++ b/src/multimedia/audio/qaudiodeviceid.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QAUDIODEVICEID_H +#define QAUDIODEVICEID_H + +#include <QtCore/qshareddata.h> +#include <QtCore/qmetatype.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + + +class QAudioDeviceFactory; +class QAudioDeviceIdPrivate; + +class Q_MULTIMEDIA_EXPORT QAudioDeviceId +{ + friend class QAudioDeviceFactory; + friend Q_MULTIMEDIA_EXPORT QDataStream &operator<<(QDataStream&, const QAudioDeviceId&); + friend Q_MULTIMEDIA_EXPORT QDataStream &operator>>(QDataStream&, QAudioDeviceId&); + +public: + QAudioDeviceId(); + QAudioDeviceId(const QAudioDeviceId &other); + ~QAudioDeviceId(); + + QAudioDeviceId& operator=(const QAudioDeviceId &other); + bool operator==(const QAudioDeviceId &id) const; + bool operator!=(const QAudioDeviceId &id) const; + + bool isNull() const; + +private: + QAudioDeviceId(QAudioDeviceIdPrivate *data); + + QSharedDataPointer<QAudioDeviceIdPrivate> d; +}; + +#ifndef QT_NO_DATASTREAM +Q_MULTIMEDIA_EXPORT QDataStream &operator<<(QDataStream&, const QAudioDeviceId&); +Q_MULTIMEDIA_EXPORT QDataStream &operator>>(QDataStream&, QAudioDeviceId&); +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +Q_DECLARE_METATYPE(QAudioDeviceId); + + +#endif // QAUDIODEVICEID_H diff --git a/src/multimedia/audio/qaudiodeviceid_p.h b/src/multimedia/audio/qaudiodeviceid_p.h new file mode 100644 index 0000000..3034574 --- /dev/null +++ b/src/multimedia/audio/qaudiodeviceid_p.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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 QAUDIODEVICEIDPRIVATE_H +#define QAUDIODEVICEIDPRIVATE_H + +#include <QtCore/qstring.h> +#include <QtCore/qbytearray.h> +#include <QtCore/qshareddata.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + + +class QAudioDeviceIdPrivate : public QSharedData +{ +public: + QAudioDeviceIdPrivate(QString const& k, int m, QByteArray const& h); + + QString key; + int mode; + QByteArray handle; +}; + + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QAUDIODEVICEIDPRIVATE_H diff --git a/src/multimedia/audio/qaudiodeviceinfo.cpp b/src/multimedia/audio/qaudiodeviceinfo.cpp new file mode 100644 index 0000000..c1895d7 --- /dev/null +++ b/src/multimedia/audio/qaudiodeviceinfo.cpp @@ -0,0 +1,270 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qaudiodevicefactory_p.h" +#include <QtMultimedia/qaudioengine.h> +#include <QtMultimedia/qaudiodeviceinfo.h> + + +QT_BEGIN_NAMESPACE + +/*! + \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(), supportedChannels(), supportedCodecs(), + supportedFrequencies(), 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(). + + A QAudioDeviceInfo is constructed with a QAudioDeviceId, which is + an identifier for a physical device. It is used by Qt to construct + classes that communicate with the device--such as + QAudioDeviceInfo, QAudioInput, and QAudioOutput. The static + functions defaultInputDevice(), defaultOutputDevice(), and + deviceList() let you get a hold of the ids for all available + devices. You fetch ids based on whether you will use the device + for input or output; this is specified by the QAudio::Mode enum. + The QAudioDeviceId returned are only valid for the QAudio::Mode. + + For instance: + + \code + foreach(QAudioDeviceId audioId, QAudioDeviceInfo::deviceList(QAudio::AudioOutput)) { + QAudioDeviceInfo info(audioId); + qDebug() << "Device name: " << info.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, QAudioDeviceId +*/ + +/*! + Construct a new audio device info and attach it to \a parent. + Using the audio device with the specified \a id. +*/ + +QAudioDeviceInfo::QAudioDeviceInfo(const QAudioDeviceId &id, QObject *parent): + QObject(parent) +{ + d = QAudioDeviceFactory::audioDeviceInfo(id); +} + +/*! + Destroy this audio device info. +*/ + +QAudioDeviceInfo::~QAudioDeviceInfo() +{ + delete d; +} + +/*! + 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 d->deviceName(); +} + +/*! + Returns true if \a settings are supported by the audio device of this QAudioDeviceInfo. +*/ + +bool QAudioDeviceInfo::isFormatSupported(const QAudioFormat &settings) const +{ + return d->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 d->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 +{ + return d->nearestFormat(settings); +} + +/*! + 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 d->codecList(); +} + +/*! + Returns a list of supported frequencies. +*/ + +QList<int> QAudioDeviceInfo::supportedFrequencies() const +{ + return d->frequencyList(); +} + +/*! + Returns a list of supported channels. +*/ + +QList<int> QAudioDeviceInfo::supportedChannels() const +{ + return d->channelsList(); +} + +/*! + Returns a list of supported sample sizes. +*/ + +QList<int> QAudioDeviceInfo::supportedSampleSizes() const +{ + return d->sampleSizeList(); +} + +/*! + Returns a list of supported byte orders. +*/ + +QList<QAudioFormat::Endian> QAudioDeviceInfo::supportedByteOrders() const +{ + return d->byteOrderList(); +} + +/*! + Returns a list of supported sample types. +*/ + +QList<QAudioFormat::SampleType> QAudioDeviceInfo::supportedSampleTypes() const +{ + return d->sampleTypeList(); +} + +/*! + Returns the name of the default input audio device. + All platform and audio plugin implementations provide a default audio device to use. +*/ + +QAudioDeviceId 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. +*/ + +QAudioDeviceId QAudioDeviceInfo::defaultOutputDevice() +{ + return QAudioDeviceFactory::defaultOutputDevice(); +} + +/*! + Returns a list of audio devices that support \a mode. +*/ + +QList<QAudioDeviceId> QAudioDeviceInfo::deviceList(QAudio::Mode mode) +{ + return QAudioDeviceFactory::deviceList(mode); +} + +QT_END_NAMESPACE + diff --git a/src/multimedia/audio/qaudiodeviceinfo.h b/src/multimedia/audio/qaudiodeviceinfo.h new file mode 100644 index 0000000..444a00a --- /dev/null +++ b/src/multimedia/audio/qaudiodeviceinfo.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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> +#include <QtMultimedia/qaudiodeviceid.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + + +class QAbstractAudioDeviceInfo; + +class Q_MULTIMEDIA_EXPORT QAudioDeviceInfo : public QObject +{ + Q_OBJECT + +public: + explicit QAudioDeviceInfo(const QAudioDeviceId &id, QObject *parent = 0); + ~QAudioDeviceInfo(); + + 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> supportedChannels() const; + QList<int> supportedSampleSizes() const; + QList<QAudioFormat::Endian> supportedByteOrders() const; + QList<QAudioFormat::SampleType> supportedSampleTypes() const; + + static QAudioDeviceId defaultInputDevice(); + static QAudioDeviceId defaultOutputDevice(); + + static QList<QAudioDeviceId> deviceList(QAudio::Mode mode); + +private: + Q_DISABLE_COPY(QAudioDeviceInfo) + + QAbstractAudioDeviceInfo* d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QAUDIODEVICEINFO_H diff --git a/src/multimedia/audio/qaudiodeviceinfo_alsa_p.cpp b/src/multimedia/audio/qaudiodeviceinfo_alsa_p.cpp new file mode 100644 index 0000000..fe45f82 --- /dev/null +++ b/src/multimedia/audio/qaudiodeviceinfo_alsa_p.cpp @@ -0,0 +1,394 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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" + +QAudioDeviceInfoPrivate::QAudioDeviceInfoPrivate(QByteArray dev, QAudio::Mode mode) +{ + handle = 0; + + device = QLatin1String(dev); + this->mode = mode; +} + +QAudioDeviceInfoPrivate::~QAudioDeviceInfoPrivate() +{ + close(); +} + +bool QAudioDeviceInfoPrivate::isFormatSupported(const QAudioFormat& format) const +{ + return testSettings(format); +} + +QAudioFormat QAudioDeviceInfoPrivate::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(tr("audio/pcm")); + } else { + nearest.setFrequency(8000); + nearest.setChannels(1); + nearest.setSampleType(QAudioFormat::SignedInt); + nearest.setSampleSize(8); + nearest.setCodec(tr("audio/pcm")); + } + return nearest; +} + +QAudioFormat QAudioDeviceInfoPrivate::nearestFormat(const QAudioFormat& format) const +{ + if(testSettings(format)) + return format; + else + return preferredFormat(); +} + +QString QAudioDeviceInfoPrivate::deviceName() const +{ + return device; +} + +QStringList QAudioDeviceInfoPrivate::codecList() +{ + updateLists(); + return codecz; +} + +QList<int> QAudioDeviceInfoPrivate::frequencyList() +{ + updateLists(); + return freqz; +} + +QList<int> QAudioDeviceInfoPrivate::channelsList() +{ + updateLists(); + return channelz; +} + +QList<int> QAudioDeviceInfoPrivate::sampleSizeList() +{ + updateLists(); + return sizez; +} + +QList<QAudioFormat::Endian> QAudioDeviceInfoPrivate::byteOrderList() +{ + updateLists(); + return byteOrderz; +} + +QList<QAudioFormat::SampleType> QAudioDeviceInfoPrivate::sampleTypeList() +{ + updateLists(); + return typez; +} + +bool QAudioDeviceInfoPrivate::open() +{ + int err = 0; + QString dev = device; + if(!dev.contains(tr("default"))) { + int idx = snd_card_get_index(dev.toLocal8Bit().constData()); + dev = QString(tr("hw:%1,0")).arg(idx); + } + 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 QAudioDeviceInfoPrivate::close() +{ + if(handle) + snd_pcm_close(handle); + handle = 0; +} + +bool QAudioDeviceInfoPrivate::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; + + // open() + if(!dev.contains(tr("default"))) { + int idx = snd_card_get_index(dev.toLocal8Bit().constData()); + dev = QString(tr("hw:%1,0")).arg(idx); + } + 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 ); + + // For now, just accept only audio/pcm codec + if(!format.codec().startsWith(tr("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 QAudioDeviceInfoPrivate::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); + 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(tr("audio/pcm")); + close(); +} + +QList<QByteArray> QAudioDeviceInfoPrivate::deviceList(QAudio::Mode mode) +{ + QAudio::Mode _m; + QList<QByteArray> devices; + QByteArray filter; + QString dir; + + // 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; + + while (*n != NULL) { + _m = QAudio::AudioOutput; + name = snd_device_name_get_hint(*n, "NAME"); + descr = snd_device_name_get_hint(*n, "DESC"); + io = snd_device_name_get_hint(*n, "IOID"); + dir = QString::fromUtf8(io); + if((name != NULL) && (descr != NULL) && ((io == NULL) || (dir.length() ==filter.length()))) { + if(dir.length() == 5) + _m = QAudio::AudioInput; + if(io == NULL) + _m = mode; + + QString str = tr(name); + + if(str.contains(tr("default"))) { + int pos = str.indexOf(tr("="),0); + devices.append(str.mid(pos+1).toLocal8Bit().constData()); + } + } + if(name != NULL) + 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"); + if(mode == QAudio::AudioInput) { + filter.append("Input"); + } else { + filter.append("Output"); + } + } + + return devices; +} + +QByteArray QAudioDeviceInfoPrivate::defaultInputDevice() +{ + QList<QByteArray> devices = deviceList(QAudio::AudioInput); + if(devices.size() == 0) + return QByteArray(); + + return QByteArray("default"); +} + +QByteArray QAudioDeviceInfoPrivate::defaultOutputDevice() +{ + QList<QByteArray> devices = deviceList(QAudio::AudioOutput); + if(devices.size() == 0) + return QByteArray(); + + return QByteArray("default"); +} + + diff --git a/src/multimedia/audio/qaudiodeviceinfo_alsa_p.h b/src/multimedia/audio/qaudiodeviceinfo_alsa_p.h new file mode 100644 index 0000000..3ac9239 --- /dev/null +++ b/src/multimedia/audio/qaudiodeviceinfo_alsa_p.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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> + +const unsigned int MAX_SAMPLE_RATES = 5; +const unsigned int SAMPLE_RATES[] = + { 8000, 11025, 22050, 44100, 48000 }; + +class QAudioDeviceInfoPrivate : public QAbstractAudioDeviceInfo +{ + Q_OBJECT +public: + QAudioDeviceInfoPrivate(QByteArray dev,QAudio::Mode mode); + ~QAudioDeviceInfoPrivate(); + + 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> deviceList(QAudio::Mode); + +private: + bool open(); + void close(); + + 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; +}; + +#endif + diff --git a/src/multimedia/audio/qaudiodeviceinfo_mac_p.cpp b/src/multimedia/audio/qaudiodeviceinfo_mac_p.cpp new file mode 100644 index 0000000..c94e0c4 --- /dev/null +++ b/src/multimedia/audio/qaudiodeviceinfo_mac_p.cpp @@ -0,0 +1,357 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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 + + +QAudioDeviceInfoPrivate::QAudioDeviceInfoPrivate(QByteArray const& handle, QAudio::Mode) +{ + QDataStream ds(handle); + quint32 did, tm; + + ds >> did >> tm >> name; + deviceId = AudioDeviceID(did); + mode = QAudio::Mode(tm); +} + +bool QAudioDeviceInfoPrivate::isFormatSupported(const QAudioFormat& format) const +{ + return format.codec() == QString::fromLatin1("audio/pcm"); +} + +QAudioFormat QAudioDeviceInfoPrivate::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 QAudioDeviceInfoPrivate::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 QAudioDeviceInfoPrivate::deviceName() const +{ + return name; +} + +QStringList QAudioDeviceInfoPrivate::codecList() +{ + return QStringList() << QString::fromLatin1("audio/pcm"); +} + +QList<int> QAudioDeviceInfoPrivate::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> QAudioDeviceInfoPrivate::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> QAudioDeviceInfoPrivate::sampleSizeList() +{ + return QList<int>() << 8 << 16 << 24 << 32 << 64; +} + +QList<QAudioFormat::Endian> QAudioDeviceInfoPrivate::byteOrderList() +{ + return QList<QAudioFormat::Endian>() << QAudioFormat::LittleEndian << QAudioFormat::BigEndian; +} + +QList<QAudioFormat::SampleType> QAudioDeviceInfoPrivate::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 QAudioDeviceInfoPrivate::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 QAudioDeviceInfoPrivate::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> QAudioDeviceInfoPrivate::deviceList(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 + diff --git a/src/multimedia/audio/qaudiodeviceinfo_mac_p.h b/src/multimedia/audio/qaudiodeviceinfo_mac_p.h new file mode 100644 index 0000000..ee593de --- /dev/null +++ b/src/multimedia/audio/qaudiodeviceinfo_mac_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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 QAudioDeviceInfoPrivate : public QAbstractAudioDeviceInfo +{ +public: + AudioDeviceID deviceId; + QString name; + QAudio::Mode mode; + + QAudioDeviceInfoPrivate(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> deviceList(QAudio::Mode mode); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDEVICEINFO_MAC_P_H diff --git a/src/multimedia/audio/qaudiodeviceinfo_win32_p.cpp b/src/multimedia/audio/qaudiodeviceinfo_win32_p.cpp new file mode 100644 index 0000000..1a1995a --- /dev/null +++ b/src/multimedia/audio/qaudiodeviceinfo_win32_p.cpp @@ -0,0 +1,378 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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" + +// 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 + + +QAudioDeviceInfoPrivate::QAudioDeviceInfoPrivate(QByteArray dev, QAudio::Mode mode) +{ + device = QLatin1String(dev); + this->mode = mode; +} + +QAudioDeviceInfoPrivate::~QAudioDeviceInfoPrivate() +{ + close(); +} + +bool QAudioDeviceInfoPrivate::isFormatSupported(const QAudioFormat& format) const +{ + return testSettings(format); +} + +QAudioFormat QAudioDeviceInfoPrivate::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(tr("audio/pcm")); + } else { + nearest.setFrequency(11025); + nearest.setChannels(1); + nearest.setSampleType(QAudioFormat::SignedInt); + nearest.setSampleSize(8); + nearest.setCodec(tr("audio/pcm")); + } + return nearest; +} + +QAudioFormat QAudioDeviceInfoPrivate::nearestFormat(const QAudioFormat& format) const +{ + if(testSettings(format)) + return format; + else + return preferredFormat(); +} + +QString QAudioDeviceInfoPrivate::deviceName() const +{ + return device; +} + +QStringList QAudioDeviceInfoPrivate::codecList() +{ + updateLists(); + return codecz; +} + +QList<int> QAudioDeviceInfoPrivate::frequencyList() +{ + updateLists(); + return freqz; +} + +QList<int> QAudioDeviceInfoPrivate::channelsList() +{ + updateLists(); + return channelz; +} + +QList<int> QAudioDeviceInfoPrivate::sampleSizeList() +{ + updateLists(); + return sizez; +} + +QList<QAudioFormat::Endian> QAudioDeviceInfoPrivate::byteOrderList() +{ + updateLists(); + return byteOrderz; +} + +QList<QAudioFormat::SampleType> QAudioDeviceInfoPrivate::sampleTypeList() +{ + updateLists(); + return typez; +} + + +bool QAudioDeviceInfoPrivate::open() +{ + return true; +} + +void QAudioDeviceInfoPrivate::close() +{ +} + +bool QAudioDeviceInfoPrivate::testSettings(const QAudioFormat& format) const +{ + // Set nearest to closest settings that do work. + // See if what is in settings will work (return value). + + bool testChannel = false; + bool testCodec = false; + bool testFreq = false; + + int err = 0; + + // For now, just accept only audio/pcm codec + if(!format.codec().startsWith(tr("audio/pcm"))) { + err=-1; + } else + testCodec = true; + + if(err>=0 && format.channels() != -1) { + testChannel = true; + } + + if(err>=0 && format.frequency() != -1) { + testFreq = true; + } + + if(err == 0) { + // settings work + return true; + } + return false; +} + +void QAudioDeviceInfoPrivate::updateLists() +{ + // redo all lists based on current settings + bool base = false; + bool match = false; + DWORD fmt = NULL; + QString tmp; + + if(device.compare(tr("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::fromUtf16((const unsigned short*)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::fromUtf16((const unsigned short*)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); + + byteOrderz.append(QAudioFormat::LittleEndian); + + typez.append(QAudioFormat::SignedInt); + typez.append(QAudioFormat::UnSignedInt); + + codecz.append(tr("audio/pcm")); + } +} + +QList<QByteArray> QAudioDeviceInfoPrivate::deviceList(QAudio::Mode mode) +{ + Q_UNUSED(mode) + + QList<QByteArray> devices; + + devices.append("default"); + + 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::fromUtf16((const unsigned short*)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::fromUtf16((const unsigned short*)woc.szPname).toLocal8Bit().constData()); + } + } + + } + return devices; +} + +QByteArray QAudioDeviceInfoPrivate::defaultOutputDevice() +{ + return QByteArray("default"); +} + +QByteArray QAudioDeviceInfoPrivate::defaultInputDevice() +{ + return QByteArray("default"); +} + diff --git a/src/multimedia/audio/qaudiodeviceinfo_win32_p.h b/src/multimedia/audio/qaudiodeviceinfo_win32_p.h new file mode 100644 index 0000000..140a741 --- /dev/null +++ b/src/multimedia/audio/qaudiodeviceinfo_win32_p.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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> + + +const unsigned int MAX_SAMPLE_RATES = 5; +const unsigned int SAMPLE_RATES[] = { 8000, 11025, 22050, 44100, 48000 }; + +class QAudioDeviceInfoPrivate : public QAbstractAudioDeviceInfo +{ + Q_OBJECT + +public: + QAudioDeviceInfoPrivate(QByteArray dev,QAudio::Mode mode); + ~QAudioDeviceInfoPrivate(); + + 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> deviceList(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; +}; + +#endif diff --git a/src/multimedia/audio/qaudioengine.cpp b/src/multimedia/audio/qaudioengine.cpp new file mode 100644 index 0000000..930977e --- /dev/null +++ b/src/multimedia/audio/qaudioengine.cpp @@ -0,0 +1,343 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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 transfered 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::totalTime() const + Returns the amount of audio data processed since start() was called in milliseconds. +*/ + +/*! + \fn virtual qint64 QAbstractAudioOutput::clock() 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 transfered + 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::totalTime() const + Returns the amount of audio data processed since start() was called in milliseconds. +*/ + +/*! + \fn virtual qint64 QAbstractAudioInput::clock() 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 diff --git a/src/multimedia/audio/qaudioengine.h b/src/multimedia/audio/qaudioengine.h new file mode 100644 index 0000000..b0aa762 --- /dev/null +++ b/src/multimedia/audio/qaudioengine.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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 totalTime() const = 0; + virtual qint64 clock() 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 totalTime() const = 0; + virtual qint64 clock() 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 diff --git a/src/multimedia/audio/qaudioengineplugin.cpp b/src/multimedia/audio/qaudioengineplugin.cpp new file mode 100644 index 0000000..6e39004 --- /dev/null +++ b/src/multimedia/audio/qaudioengineplugin.cpp @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtMultimedia/qaudioengineplugin.h> + +QT_BEGIN_NAMESPACE + +QAudioEnginePlugin::QAudioEnginePlugin(QObject* parent) : + QObject(parent) +{} + +QAudioEnginePlugin::~QAudioEnginePlugin() +{} + +QT_END_NAMESPACE diff --git a/src/multimedia/audio/qaudioengineplugin.h b/src/multimedia/audio/qaudioengineplugin.h new file mode 100644 index 0000000..8eab2d7 --- /dev/null +++ b/src/multimedia/audio/qaudioengineplugin.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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> deviceList(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> deviceList(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 diff --git a/src/multimedia/audio/qaudioformat.cpp b/src/multimedia/audio/qaudioformat.cpp new file mode 100644 index 0000000..6632c63 --- /dev/null +++ b/src/multimedia/audio/qaudioformat.cpp @@ -0,0 +1,335 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#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; + } + + 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 Frequency + \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) + \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. +*/ + +/*! + Construct a new audio format. + + Values are initialized as follows: + \list + \o frequency() = -1 + \o channels() = -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 any of the parameters are invalid. +*/ + +bool QAudioFormat::isNull() const +{ + return d->frequency == -1 && d->channels == -1 && + d->sampleSize == -1 && + d->byteOrder == QAudioFormat::Endian(QSysInfo::ByteOrder) && + d->sampleType == QAudioFormat::Unknown && + d->codec.isNull(); +} + +/*! + Sets the frequency to \a frequency. +*/ + +void QAudioFormat::setFrequency(int frequency) +{ + d->frequency = frequency; +} + +/*! + Returns the current frequency value. +*/ + +int QAudioFormat::frequency() const +{ + return d->frequency; +} + +/*! + Sets the channels to \a channels. +*/ + +void QAudioFormat::setChannels(int channels) +{ + d->channels = channels; +} + +/*! + Returns the current channel value. +*/ + +int QAudioFormat::channels() const +{ + return d->channels; +} + +/*! + Sets the sampleSize to \a sampleSize. +*/ + +void QAudioFormat::setSampleSize(int sampleSize) +{ + d->sampleSize = sampleSize; +} + +/*! + Returns the current sampleSize value. +*/ + +int QAudioFormat::sampleSize() const +{ + return d->sampleSize; +} + +/*! + Sets the codec to \a codec. + + \sa QAudioDeviceInfo::supportedCodecs() +*/ + +void QAudioFormat::setCodec(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 + diff --git a/src/multimedia/audio/qaudioformat.h b/src/multimedia/audio/qaudioformat.h new file mode 100644 index 0000000..c740969 --- /dev/null +++ b/src/multimedia/audio/qaudioformat.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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 isNull() const; + + void setFrequency(int frequency); + int frequency() const; + + void setChannels(int channels); + int channels() const; + + void setSampleSize(int sampleSize); + int sampleSize() const; + + void setCodec(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 diff --git a/src/multimedia/audio/qaudioinput.cpp b/src/multimedia/audio/qaudioinput.cpp new file mode 100644 index 0000000..17cacc6 --- /dev/null +++ b/src/multimedia/audio/qaudioinput.cpp @@ -0,0 +1,400 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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 QAudioDeviceId. 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 QAudioDeviceId 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; + 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); + + QAudioInput *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(); + } + \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 totalTime() + 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 clock() 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. + + \sa QAudioOutput, QAudioDeviceInfo +*/ + +/*! + 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 \a id of the audio input device is used with the input + \a format parameters. +*/ + +QAudioInput::QAudioInput(const QAudioDeviceId &id, const QAudioFormat &format, QObject *parent): + QObject(parent) +{ + d = QAudioDeviceFactory::createInputDevice(id, 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. + 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 transfered without any extra code. + All that is required is to open the QIODevice. + + /sa QIODevice +*/ + +QIODevice* QAudioInput::start(QIODevice* device) +{ + /* + PULL MODE (valid QIODevice) + -If currently not StopState, stop + -If previous start was push mode, delete internal QIODevice. + -open audio input. + If ok, NoError and ActiveState, else OpenError and StopState. + -emit stateChanged() + -return device + + PUSH MODE (device = 0) + -If currently not StopState, stop + -If no internal QIODevice, create one. + -open audio input. + -If ok, NoError and IdleState, else OpenError and StopState + -emit stateChanged() + -return internal QIODevice + */ + return d->start(device); +} + +/*! + Returns the QAudioFormat being used. +*/ + +QAudioFormat QAudioInput::format() const +{ + return d->format(); +} + +/*! + Stops the audio input. +*/ + +void QAudioInput::stop() +{ + /* + -If StopState, return + -set to StopState + -detach from audio device + -emit stateChanged() + */ + d->stop(); +} + +/*! + Drops all audio data in the buffers, resets buffers to zero. +*/ + +void QAudioInput::reset() +{ + /* + -drop all buffered audio, set buffers to zero. + -call stop() + */ + d->reset(); +} + +/*! + Stops processing audio data, preserving buffered audio data. +*/ + +void QAudioInput::suspend() +{ + /* + -If not ActiveState|IdleState, return + -stop processing audio, saving all buffered audio data + -set NoError and SuspendState + -emit stateChanged() + */ + d->suspend(); +} + +/*! + Resumes processing audio data after a suspend(). +*/ + +void QAudioInput::resume() +{ + /* + -If SuspendState, return + -resume audio + -(PULL MODE): set ActiveState, NoError + -(PUSH MODE): set IdleState, NoError + -kick start audio if needed + -emit stateChanged() + */ + 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. +*/ + +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 resolution of the timer is platform specific. +*/ + +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::totalTime() const +{ + return d->totalTime(); +} + +/*! + Returns the microseconds since start() was called, including time in Idle and + Suspend states. +*/ + +qint64 QAudioInput::clock() const +{ + return d->clock(); +} + +/*! + 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 + diff --git a/src/multimedia/audio/qaudioinput.h b/src/multimedia/audio/qaudioinput.h new file mode 100644 index 0000000..b0f1aed --- /dev/null +++ b/src/multimedia/audio/qaudioinput.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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/qaudiodeviceid.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 QAudioDeviceId &id, const QAudioFormat &format = QAudioFormat(), QObject *parent = 0); + ~QAudioInput(); + + QAudioFormat format() const; + + QIODevice* start(QIODevice *device = 0); + 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 totalTime() const; + qint64 clock() 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 diff --git a/src/multimedia/audio/qaudioinput_alsa_p.cpp b/src/multimedia/audio/qaudioinput_alsa_p.cpp new file mode 100644 index 0000000..6f00469 --- /dev/null +++ b/src/multimedia/audio/qaudioinput_alsa_p.cpp @@ -0,0 +1,688 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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" + +//#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::StopState; + audioSource = 0; + pullMode = true; + resuming = false; + + QStringList list1 = QString(tr(device)).split(tr(":")); + m_device = QByteArray(list1.at(0).toLocal8Bit().constData()); + + 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 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_S16; + + 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 snd_pcm_hw_params_set_format( handle, hwparams, format); +} + +QIODevice* QAudioInputPrivate::start(QIODevice* device) +{ + if(deviceState != QAudio::StopState) + close(); + + if(!pullMode && audioSource) { + delete audioSource; + } + + if(device) { + //set to pull mode + pullMode = true; + audioSource = device; + } else { + //set to push mode + pullMode = false; + 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::StopState) + return; + + deviceState = QAudio::StopState; + + close(); + emit stateChanged(deviceState); +} + +bool QAudioInputPrivate::open() +{ +#ifdef DEBUG_AUDIO + QTime now(QTime::currentTime()); + qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()"; +#endif + timeStamp.restart(); + + int dir; + int err=-1; + int count=0; + unsigned int freakuency=settings.frequency(); + + QString dev = QString(tr(m_device.constData())); + if(!dev.contains(tr("default"))) { + dev = QString(tr("default:CARD=%1")).arg(tr(m_device.constData())); + } + + // 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::StopState; + 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(tr("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(tr("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(tr("QAudioInput: snd_pcm_hw_params_set_access: err = %1")).arg(err); + } + } + if ( !fatal ) { + err = setFormat(); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("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(tr("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(tr("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(tr("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(tr("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(tr("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(tr("QAudioInput: snd_pcm_hw_params: err = %1")).arg(err); + } + } + if( err < 0) { + qWarning()<<errMessage; + errorState = QAudio::OpenError; + deviceState = QAudio::StopState; + 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 = bytesReady(); + + 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; + deviceState = QAudio::ActiveState; + + totalTimeValue = 0; + + return true; +} + +void QAudioInputPrivate::close() +{ + deviceState = QAudio::StopState; + timer->stop(); + + if ( handle ) { + snd_pcm_drop( handle ); + snd_pcm_close( handle ); + handle = 0; + delete [] audioBuffer; + audioBuffer=0; + } +} + +int QAudioInputPrivate::bytesReady() const +{ + if(resuming) + return period_size; + + if(deviceState != QAudio::ActiveState) + 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 QAudioInputPrivate::read(char* data, qint64 len) +{ + Q_UNUSED(data) + Q_UNUSED(len) + // Read in some audio data and write it to QIODevice, pull mode + if ( !handle ) + return 0; + + bytesAvailable = bytesReady(); + + int count=0, err = 0; + while(count < 5) { + int chunks = bytesAvailable/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(tr("PULL: 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()<<"PULL: frames to write to QIODevice = "<< + snd_pcm_bytes_to_frames( handle, (int)err )<<" ("<<err<<") bytes"; +#endif + if(deviceState != QAudio::ActiveState) + return 0; + + qint64 l = audioSource->write(audioBuffer,err); + if(l < 0) { + close(); + errorState = QAudio::IOError; + deviceState = QAudio::StopState; + emit stateChanged(deviceState); + } else if(l == 0) { + errorState = QAudio::NoError; + deviceState = QAudio::IdleState; + } else { + totalTimeValue += snd_pcm_bytes_to_frames(handle, err)*1000000/settings.frequency(); + resuming = false; + errorState = QAudio::NoError; + deviceState = QAudio::ActiveState; + } + return l; + } + return 0; +} + +void QAudioInputPrivate::resume() +{ + if(deviceState == QAudio::SuspendState) { + 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(buffer_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 = ms; +} + +int QAudioInputPrivate::notifyInterval() const +{ + return intervalTime; +} + +qint64 QAudioInputPrivate::totalTime() const +{ + return totalTimeValue; +} + +void QAudioInputPrivate::suspend() +{ + if(deviceState == QAudio::ActiveState||resuming) { + timer->stop(); + deviceState = QAudio::SuspendState; + emit stateChanged(deviceState); + } +} + +void QAudioInputPrivate::userFeed() +{ + if(deviceState == QAudio::StopState || deviceState == QAudio::SuspendState) + 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,0); + } else { + // emits readyRead() so user will call read() on QIODevice to get some audio data + InputPrivate* a = qobject_cast<InputPrivate*>(audioSource); + a->trigger(); + } + bytesAvailable = bytesReady(); + + if(deviceState != QAudio::ActiveState) + return true; + + if(timeStamp.elapsed() > intervalTime && intervalTime > 50) { + emit notify(); + timeStamp.restart(); + } + return true; +} + +qint64 QAudioInputPrivate::clock() const +{ + if(!handle) + return 0; + + if(deviceState != QAudio::ActiveState) + return 0; + + snd_pcm_status_t* status; + snd_pcm_status_alloca(&status); + + snd_timestamp_t t1,t2; + if( snd_pcm_status(handle, status) >= 0) { + snd_pcm_status_get_tstamp(status,&t1); + snd_pcm_status_get_trigger_tstamp(status,&t2); + t1.tv_sec-=t2.tv_sec; + + signed long l = (signed long)t1.tv_usec - (signed long)t2.tv_usec; + if(l < 0) { + t1.tv_sec--; + l = -l; + l %= 1000000; + } + return ((t1.tv_sec * 1000)+l/1000); + } else + return 0; +} + +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) +{ + // push mode, user read() called + if((audioDevice->state() != QAudio::ActiveState) && !audioDevice->resuming) + return 0; + + int readFrames; + int count=0, err = 0; + + while(count < 5) { + int frames = snd_pcm_bytes_to_frames(audioDevice->handle, len); + readFrames = snd_pcm_readi(audioDevice->handle, data, frames); + if (readFrames >= 0) { + err = snd_pcm_frames_to_bytes(audioDevice->handle, readFrames); +#ifdef DEBUG_AUDIO + qDebug()<<QString(tr("PUSH: read in bytes = %1 (frames=%2)")).arg(err).arg(readFrames).toLatin1().constData(); +#endif + break; + } else if((readFrames == -EAGAIN) || (readFrames == -EINTR)) { + audioDevice->errorState = QAudio::IOError; + err = 0; + break; + } else { + if(readFrames == -EPIPE) { + audioDevice->errorState = QAudio::UnderrunError; + err = snd_pcm_prepare(audioDevice->handle); + } else if(readFrames == -ESTRPIPE) { + err = snd_pcm_prepare(audioDevice->handle); + } + if(err != 0) break; + } + count++; + } + if(err > 0 && readFrames > 0) { + audioDevice->totalTimeValue += readFrames*1000/audioDevice->settings.frequency()*1000; + audioDevice->deviceState = QAudio::ActiveState; + return err; + } + return 0; +} + +qint64 InputPrivate::writeData(const char* data, qint64 len) +{ + Q_UNUSED(data) + Q_UNUSED(len) + return 0; +} + +void InputPrivate::trigger() +{ + emit readyRead(); +} + diff --git a/src/multimedia/audio/qaudioinput_alsa_p.h b/src/multimedia/audio/qaudioinput_alsa_p.h new file mode 100644 index 0000000..21c8064 --- /dev/null +++ b/src/multimedia/audio/qaudioinput_alsa_p.h @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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/qdatetime.h> + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudioengine.h> + +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 totalTime() const; + qint64 clock() 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 xrun_recovery(int err); + int setFormat(); + bool open(); + void close(); + void drain(); + + QTimer* timer; + QTime timeStamp; + 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; +}; + +#endif diff --git a/src/multimedia/audio/qaudioinput_mac_p.cpp b/src/multimedia/audio/qaudioinput_mac_p.cpp new file mode 100644 index 0000000..5400c85 --- /dev/null +++ b/src/multimedia/audio/qaudioinput_mac_p.cpp @@ -0,0 +1,930 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudioinput.h> + +#include "qaudio_mac_p.h" +#include "qaudioinput_mac_p.h" + + +QT_BEGIN_NAMESPACE + + +namespace +{ + +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; + } + +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; + } + +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_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 (inputFormat.mSampleRate != outputFormat.mSampleRate) { + 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 wasEmpty = m_buffer->used() == 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); + + bool wecan = true; + int copied = 0; + + const int available = m_buffer->free(); + + while (err == noErr && wecan) { + QAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(available); + + if (region.second > 0) { + 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); + } + else + wecan = false; + } + + 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 (wasEmpty && 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) + { + 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 { + isOpen = false; + audioDeviceId = AudioDeviceID(did); + audioUnit = 0; + startTime = 0; + totalFrames = 0; + audioBuffer = 0; + internalBufferSize = default_buffer_size; + clockFrequency = AudioGetHostClockFrequency() / 1000; + errorCode = QAudio::NoError; + stateCode = QAudio::StopState; + + intervalTimer = new QTimer(this); + intervalTimer->setInterval(1000); + connect(intervalTimer, SIGNAL(timeout()), SIGNAL(notify())); + } +} + +QAudioInputPrivate::~QAudioInputPrivate() +{ + close(); +} + +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: Unabled 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: Unabled 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 + streamFormat = toAudioStreamBasicDescription(audioFormat); + + size = sizeof(deviceFormat); + if (AudioUnitGetProperty(audioUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, + 1, + &deviceFormat, + &size) != noErr) { + qWarning() << "QAudioInput: Unable to retrieve device format"; + return false; + } + + // If the device frequency is different to the requested use a converter + if (deviceFormat.mSampleRate != streamFormat.mSampleRate) { + AudioUnitSetProperty(audioUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Output, + 1, + &deviceFormat, + sizeof(streamFormat)); + } + else { + AudioUnitSetProperty(audioUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Output, + 1, + &streamFormat, + sizeof(streamFormat)); + } + + // 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.mSampleRate / deviceFormat.mSampleRate) * + streamFormat.mBytesPerFrame; + if (internalBufferSize < periodSizeBytes * 2) + internalBufferSize = periodSizeBytes * 2; + else + internalBufferSize -= internalBufferSize % streamFormat.mBytesPerFrame; + + audioBuffer = new QAudioInputBuffer(internalBufferSize, + periodSizeBytes, + deviceFormat, + streamFormat, + this); + + audioIO = new 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 (!open()) { + stateCode = QAudio::StopState; + errorCode = QAudio::OpenError; + return audioIO; + } + + reset(); + audioBuffer->reset(); + audioBuffer->setFlushDevice(op); + + if (op == 0) + op = audioIO; + + // Start + startTime = AudioGetCurrentHostTime(); + totalFrames = 0; + + audioThreadStart(); + + return op; +} + +void QAudioInputPrivate::stop() +{ + QMutexLocker lock(&mutex); + if (stateCode != QAudio::StopState) { + audioThreadStop(); + audioBuffer->flush(true); + + errorCode = QAudio::NoError; + stateCode = QAudio::StopState; + QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode)); + } +} + +void QAudioInputPrivate::reset() +{ + QMutexLocker lock(&mutex); + if (stateCode != QAudio::StopState) { + audioThreadStop(); + + errorCode = QAudio::NoError; + stateCode = QAudio::StopState; + 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::SuspendState; + QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode)); + } +} + +void QAudioInputPrivate::resume() +{ + QMutexLocker lock(&mutex); + if (stateCode == QAudio::SuspendState) { + 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) +{ + intervalTimer->setInterval(milliSeconds); +} + +int QAudioInputPrivate::notifyInterval() const +{ + return intervalTimer->interval(); +} + +qint64 QAudioInputPrivate::totalTime() const +{ + return totalFrames * 1000000 / audioFormat.frequency(); +} + +qint64 QAudioInputPrivate::clock() const +{ + 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::StopState; + QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection); + } +} + +void QAudioInputPrivate::startTimers() +{ + audioBuffer->startFlushTimer(); + 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" + diff --git a/src/multimedia/audio/qaudioinput_mac_p.h b/src/multimedia/audio/qaudioinput_mac_p.h new file mode 100644 index 0000000..98ef9ce --- /dev/null +++ b/src/multimedia/audio/qaudioinput_mac_p.h @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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; + +namespace +{ +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; + QAudioInputBuffer* audioBuffer; + QMutex mutex; + QWaitCondition threadFinished; + QAtomicInt audioThreadState; + QTimer* intervalTimer; + AudioStreamBasicDescription streamFormat; + AudioStreamBasicDescription deviceFormat; + + 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 totalTime() const; + qint64 clock() const; + + QAudio::Error error() const; + QAudio::State state() const; + + void audioThreadStart(); + void audioThreadStop(); + + void audioDeviceStop(); + void audioDeviceFull(); + void audioDeviceError(); + + void startTimers(); + void stopTimers(); + +signals: + void stateChanged(QAudio::State); + void notify(); + +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 diff --git a/src/multimedia/audio/qaudioinput_win32_p.cpp b/src/multimedia/audio/qaudioinput_win32_p.cpp new file mode 100644 index 0000000..e5b6e0d --- /dev/null +++ b/src/multimedia/audio/qaudioinput_win32_p.cpp @@ -0,0 +1,540 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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" + +//#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::StopState; + audioSource = 0; + pullMode = true; + resuming = false; + + connect(this,SIGNAL(processMore()),SLOT(deviceReady())); + + InitializeCriticalSection(&waveInCriticalSection); +} + +QAudioInputPrivate::~QAudioInputPrivate() +{ + close(); + DeleteCriticalSection(&waveInCriticalSection); +} + +void 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; + + switch(uMsg) { + case WIM_OPEN: + break; + case WIM_DATA: + EnterCriticalSection(&waveInCriticalSection); + if(qAudio->waveFreeBlockCount > 0) + qAudio->waveFreeBlockCount--; + LeaveCriticalSection(&waveInCriticalSection); + qAudio->feedback(); + break; + case WIM_CLOSE: + 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) +{ + 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::StopState) + close(); + + if(!pullMode && audioSource) { + delete audioSource; + } + + if(device) { + //set to pull mode + pullMode = true; + audioSource = device; + } else { + //set to push mode + pullMode = false; + 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::StopState) + return; + + deviceState = QAudio::StopState; + + 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; + if(buffer_size == 0) { + // Default buffer size, 100ms, default period size is 20ms + buffer_size = settings.frequency()*settings.channels()*(settings.sampleSize()/8)*0.1; + period_size = buffer_size/5; + } else { + period_size = buffer_size/5; + } + timeStamp.restart(); + 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::fromUtf16((const unsigned short*)wic.szPname); + if(tmp.compare(tr(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::StopState; + emit stateChanged(deviceState); + qWarning("QAudioInput: failed to open audio device"); + return false; + } + waveBlocks = allocateBlocks(period_size, buffer_size/period_size); + waveFreeBlockCount = buffer_size/period_size; + 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::StopState; + emit stateChanged(deviceState); + return false; + } + } + result = waveInStart(hWaveIn); + if(result) { + qWarning("QAudioInput: failed to start audio input"); + errorState = QAudio::OpenError; + deviceState = QAudio::StopState; + emit stateChanged(deviceState); + return false; + } + timeStampOpened.restart(); + totalTimeValue = 0; + errorState = QAudio::NoError; + deviceState = QAudio::ActiveState; + return true; +} + +void QAudioInputPrivate::close() +{ + deviceState = QAudio::StopState; + int delay = (buffer_size-bytesReady())*1000/(settings.frequency() + *settings.channels()*(settings.sampleSize()/8)); + waveInReset(hWaveIn); + Sleep(delay+10); + + for(int i=0; i<waveFreeBlockCount; i++) { + if(waveBlocks[i].dwFlags & WHDR_PREPARED) + waveInUnprepareHeader(hWaveIn,&waveBlocks[i],sizeof(WAVEHDR)); + } + freeBlocks(waveBlocks); + waveInClose(hWaveIn); +} + +int QAudioInputPrivate::bytesReady() const +{ + 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) { + 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 + /((settings.channels()*settings.sampleSize()/8)) + *10000/settings.frequency()*100; + errorState = QAudio::NoError; + deviceState = QAudio::ActiveState; + resuming = false; + } + } else { + // push mode + memcpy(p,waveBlocks[header].lpData,waveBlocks[header].dwBytesRecorded); + l = waveBlocks[header].dwBytesRecorded; +#ifdef DEBUG_AUDIO + qDebug()<<"IN: "<<waveBlocks[header].dwBytesRecorded<<", OUT: "<<l; +#endif + totalTimeValue += waveBlocks[header].dwBytesRecorded + /((settings.channels()*settings.sampleSize()/8)) + *10000/settings.frequency()*100; + errorState = QAudio::NoError; + deviceState = QAudio::ActiveState; + resuming = false; + } + } else { + //no data, not ready yet, next time + return 0; + } + EnterCriticalSection(&waveInCriticalSection); + waveFreeBlockCount++; + LeaveCriticalSection(&waveInCriticalSection); + waveBlocks[header].dwBytesRecorded=0; + waveBlocks[header].dwFlags = 0L; + result = waveInPrepareHeader(hWaveIn,&waveBlocks[header], sizeof(WAVEHDR)); + if(result != MMSYSERR_NOERROR) { + qWarning("QAudioInput: failed to prepare block %d,err=%d",header,result); + errorState = QAudio::OpenError; + deviceState = QAudio::StopState; + emit stateChanged(deviceState); + } + result = waveInAddBuffer(hWaveIn, &waveBlocks[header], sizeof(WAVEHDR)); + if(result != MMSYSERR_NOERROR) { + qWarning("QAudioInput: failed to setup block %d,err=%d",header,result); + errorState = QAudio::OpenError; + deviceState = QAudio::StopState; + emit stateChanged(deviceState); + } + header++; + if(header >= buffer_size/period_size) + header = 0; + p+=l; + + if(!pullMode) { + if(l+period_size > len && waveFreeBlockCount == buffer_size/period_size) + done = true; + } else { + if(waveFreeBlockCount == buffer_size/period_size) + done = true; + } + written+=l; + } +#ifdef DEBUG_AUDIO + qDebug()<<"read in len="<<written; +#endif + return written; +} + +void QAudioInputPrivate::resume() +{ + if(deviceState == QAudio::SuspendState) { + 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::StopState; + emit stateChanged(deviceState); + return; + } + } + waveFreeBlockCount = buffer_size/period_size; + 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 = ms; +} + +int QAudioInputPrivate::notifyInterval() const +{ + return intervalTime; +} + +qint64 QAudioInputPrivate::totalTime() const +{ + return totalTimeValue; +} + +void QAudioInputPrivate::suspend() +{ + if(deviceState == QAudio::ActiveState) { + waveInReset(hWaveIn); + deviceState = QAudio::SuspendState; + emit stateChanged(deviceState); + } +} + +void QAudioInputPrivate::feedback() +{ +#ifdef DEBUG_AUDIO + QTime now(QTime::currentTime()); + qDebug()<<now.second()<<"s "<<now.msec()<<"ms :feedback() INPUT"; +#endif + bytesAvailable = bytesReady(); + + if(!(deviceState==QAudio::StopState||deviceState==QAudio::SuspendState)) + emit processMore(); +} + +bool QAudioInputPrivate::deviceReady() +{ +#ifdef DEBUG_AUDIO + QTime now(QTime::currentTime()); + qDebug()<<now.second()<<"s "<<now.msec()<<"ms :deviceReady() INPUT"; +#endif + if(pullMode) { + // reads some audio data and writes it to QIODevice + read(0,0); + } else { + // emits readyRead() so user will call read() on QIODevice to get some audio data + InputPrivate* a = qobject_cast<InputPrivate*>(audioSource); + a->trigger(); + } + if(deviceState != QAudio::ActiveState) + return true; + + if(timeStamp.elapsed() > intervalTime && intervalTime > 50) { + emit notify(); + timeStamp.restart(); + } + return true; +} + +qint64 QAudioInputPrivate::clock() const +{ + if(deviceState != QAudio::ActiveState) + return 0; + + return timeStampOpened.elapsed(); +} + +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) + 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(); +} + diff --git a/src/multimedia/audio/qaudioinput_win32_p.h b/src/multimedia/audio/qaudioinput_win32_p.h new file mode 100644 index 0000000..32464f0 --- /dev/null +++ b/src/multimedia/audio/qaudioinput_win32_p.h @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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 <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudioengine.h> + + +static CRITICAL_SECTION waveInCriticalSection; + +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 totalTime() const; + qint64 clock() 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; + QTime timeStampOpened; + qint64 totalTimeValue; + bool pullMode; + bool resuming; + WAVEFORMATEX wfx; + HWAVEIN hWaveIn; + MMRESULT result; + WAVEHDR* waveBlocks; + volatile int waveFreeBlockCount; + int waveCurrentBlock; + + static void 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; +}; + +#endif diff --git a/src/multimedia/audio/qaudiooutput.cpp b/src/multimedia/audio/qaudiooutput.cpp new file mode 100644 index 0000000..785da61 --- /dev/null +++ b/src/multimedia/audio/qaudiooutput.cpp @@ -0,0 +1,403 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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 QAudioDeviceId. 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; + 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); + + QAudioOutput *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(); + } + } + \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, clock() 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. + + If an error is encountered state changes to QAudio::StopState. + + \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 \a id of the audio output device is used with the output + \a format parameters. +*/ + +QAudioOutput::QAudioOutput(const QAudioDeviceId &id, const QAudioFormat &format, QObject *parent): + QObject(parent) +{ + d = QAudioDeviceFactory::createOutputDevice(id, 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. + 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 transfered without any extra code. + All that is required is to open the QIODevice. + + /sa QIODevice +*/ + +QIODevice* QAudioOutput::start(QIODevice* device) +{ + /* + PULL MODE (valid QIODevice) + -If currently not StopState, stop. + -If previous start was push mode, delete internal QIODevice. + -open audio output. + -If ok, NoError and ActiveState, else OpenError and StopState + -emit stateChanged() + -return device + + PUSH MODE (device = 0) + -If currently not StopState, stop. + -If no internal QIODevice, create one. + -open audio output. + -If ok, NoError and IdleState, else OpenError and StopState + -emit stateChanged() + -return internal QIODevice + */ + return d->start(device); +} + +/*! + Stops the audio output. +*/ + +void QAudioOutput::stop() +{ + /* + -If StopState, return + -set to StopState + -detach from audio device + -emit stateChanged() + */ + d->stop(); +} + +/*! + Drops all audio data in the buffers, resets buffers to zero. +*/ + +void QAudioOutput::reset() +{ + /* + -drop all buffered audio, set buffers to zero. + -call stop() + */ + d->reset(); +} + +/*! + Stops processing audio data, preserving buffered audio data. +*/ + +void QAudioOutput::suspend() +{ + /* + -If not ActiveState|IdleState, return + -stop processing audio, saving all buffered audio data + -set NoError and SuspendState + -emit stateChanged() + */ + d->suspend(); +} + +/*! + Resumes processing audio data after a suspend(). +*/ + +void QAudioOutput::resume() +{ + /* + -If SuspendState, return + -resume audio + -(PULL MODE): set ActiveState, NoError + -(PUSH MODE): set IdleState, NoError + -kick start audio if needed + -emit stateChanged() + */ + d->resume(); +} + +/*! + Returns the free space available in bytes in the audio buffer. +*/ + +int QAudioOutput::bytesFree() const +{ + /* + -If not ActiveState|IdleState, return 0 + -return space available in audio buffer in bytes + */ + 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 resolution of the timer is platform specific. +*/ + +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 since start() + was called in microseconds. +*/ + +qint64 QAudioOutput::totalTime() const +{ + return d->totalTime(); +} + +/*! + Returns the microseconds since start() was called, including time in Idle and + Suspend states. +*/ + +qint64 QAudioOutput::clock() const +{ + return d->clock(); +} + +/*! + 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 diff --git a/src/multimedia/audio/qaudiooutput.h b/src/multimedia/audio/qaudiooutput.h new file mode 100644 index 0000000..95e28ea --- /dev/null +++ b/src/multimedia/audio/qaudiooutput.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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/qaudiodeviceid.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 QAudioDeviceId &id, const QAudioFormat &format = QAudioFormat(), QObject *parent = 0); + ~QAudioOutput(); + + QAudioFormat format() const; + + QIODevice* start(QIODevice *device = 0); + 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 totalTime() const; + qint64 clock() 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 diff --git a/src/multimedia/audio/qaudiooutput_alsa_p.cpp b/src/multimedia/audio/qaudiooutput_alsa_p.cpp new file mode 100644 index 0000000..d41c449 --- /dev/null +++ b/src/multimedia/audio/qaudiooutput_alsa_p.cpp @@ -0,0 +1,706 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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" + +//#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::StopState; + audioSource = 0; + pullMode = true; + resuming = false; + opened = false; + + QStringList list1 = QString(tr(device)).split(tr(":")); + m_device = QByteArray(list1.at(0).toLocal8Bit().constData()); + + 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_S16; + + 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 snd_pcm_hw_params_set_format( handle, hwparams, pcmformat); +} + +QIODevice* QAudioOutputPrivate::start(QIODevice* device) +{ + if(deviceState != QAudio::StopState) + deviceState = QAudio::StopState; + + 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::StopState) + return; + deviceState = QAudio::StopState; + 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(); + + int dir; + int err=-1; + int count=0; + unsigned int freakuency=settings.frequency(); + + QString dev = tr(m_device.constData()); + if(!dev.contains(tr("default"))) { + dev = QString(tr("default:CARD=%1")).arg(tr(m_device.constData())); + } + // 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::StopState; + 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(tr("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(tr("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(tr("QAudioOutput: snd_pcm_hw_params_set_access: err = %1")).arg(err); + } + } + if ( !fatal ) { + err = setFormat(); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("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(tr("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(tr("QAudioOutput: 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(tr("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(tr("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(tr("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(tr("QAudioOutput: snd_pcm_hw_params: err = %1")).arg(err); + } + } + if( err < 0) { + qWarning()<<errMessage; + errorState = QAudio::OpenError; + deviceState = QAudio::StopState; + 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); + + errorState = QAudio::NoError; + totalTimeValue = 0; + opened = true; + + return true; +} + +void QAudioOutputPrivate::close() +{ + deviceState = QAudio::StopState; + 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*1000000/settings.frequency(); + resuming = false; + errorState = QAudio::NoError; + deviceState = QAudio::ActiveState; + return snd_pcm_frames_to_bytes( handle, err ); + } else + err = xrun_recovery(err); + + if(err < 0) { + close(); + errorState = QAudio::FatalError; + deviceState = QAudio::StopState; + emit stateChanged(deviceState); + } + return 0; +} + +int QAudioOutputPrivate::periodSize() const +{ + return period_size; +} + +void QAudioOutputPrivate::setBufferSize(int value) +{ + if(deviceState == QAudio::StopState) + buffer_size = value; +} + +int QAudioOutputPrivate::bufferSize() const +{ + return buffer_size; +} + +void QAudioOutputPrivate::setNotifyInterval(int ms) +{ + intervalTime = ms; +} + +int QAudioOutputPrivate::notifyInterval() const +{ + return intervalTime; +} + +qint64 QAudioOutputPrivate::totalTime() const +{ + return totalTimeValue; +} + +void QAudioOutputPrivate::resume() +{ + if(deviceState == QAudio::SuspendState) { + 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; + if(pullMode) + deviceState = QAudio::ActiveState; + else + deviceState = QAudio::IdleState; + + 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::SuspendState; + errorState = QAudio::NoError; + emit stateChanged(deviceState); + } +} + +void QAudioOutputPrivate::userFeed() +{ + if(deviceState == QAudio::StopState || deviceState == QAudio::SuspendState) + 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() +{ + QMetaObject::invokeMethod(this, SLOT(updateAvailable()), Qt::QueuedConnection); +} + +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; + write(audioBuffer,l); + 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 + errorState = QAudio::UnderrunError; + deviceState = QAudio::IdleState; + emit stateChanged(deviceState); + } + + } else if(l < 0) { + close(); + errorState = QAudio::IOError; + emit stateChanged(deviceState); + } + } else + bytesAvailable = bytesFree(); + + if(deviceState != QAudio::ActiveState) + return true; + + if(timeStamp.elapsed() > intervalTime && intervalTime > 50) { + emit notify(); + timeStamp.restart(); + } + return true; +} + +qint64 QAudioOutputPrivate::clock() const +{ + if(!handle) + return 0; + + if(deviceState != QAudio::ActiveState) + return 0; + + snd_pcm_status_t* status; + snd_pcm_status_alloca(&status); + + snd_timestamp_t t1,t2; + if( snd_pcm_status(handle, status) >= 0) { + snd_pcm_status_get_tstamp(status,&t1); + snd_pcm_status_get_trigger_tstamp(status,&t2); + t1.tv_sec-=t2.tv_sec; + + signed long l = (signed long)t1.tv_usec - (signed long)t2.tv_usec; + if(l < 0) { + t1.tv_sec--; + l = -l; + l %= 1000000; + } + return ((t1.tv_sec * 1000)+l/1000); + } else + return 0; + return 0; +} + +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; + +} diff --git a/src/multimedia/audio/qaudiooutput_alsa_p.h b/src/multimedia/audio/qaudiooutput_alsa_p.h new file mode 100644 index 0000000..f243bad --- /dev/null +++ b/src/multimedia/audio/qaudiooutput_alsa_p.h @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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/qdatetime.h> + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudioengine.h> + +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 totalTime() const; + qint64 clock() 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; + QTime timeStamp; + 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; +}; + +#endif diff --git a/src/multimedia/audio/qaudiooutput_mac_p.cpp b/src/multimedia/audio/qaudiooutput_mac_p.cpp new file mode 100644 index 0000000..0d6e615 --- /dev/null +++ b/src/multimedia/audio/qaudiooutput_mac_p.cpp @@ -0,0 +1,700 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudiooutput.h> + +#include "qaudio_mac_p.h" +#include "qaudiooutput_mac_p.h" + + +QT_BEGIN_NAMESPACE + + +namespace +{ + +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(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: + 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 { + isOpen = false; + audioDeviceId = AudioDeviceID(did); + audioUnit = 0; + audioIO = 0; + startTime = 0; + totalFrames = 0; + audioBuffer = 0; + internalBufferSize = default_buffer_size; + clockFrequency = AudioGetHostClockFrequency() / 1000; + errorCode = QAudio::NoError; + stateCode = QAudio::StopState; + audioThreadState = Stopped; + + intervalTimer = new QTimer(this); + intervalTimer->setInterval(1000); + connect(intervalTimer, SIGNAL(timeout()), SIGNAL(notify())); + } +} + +QAudioOutputPrivate::~QAudioOutputPrivate() +{ + 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(deviceFormat); + if (AudioUnitGetProperty(audioUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, + 0, + &deviceFormat, + &size) != noErr) { + qWarning() << "QAudioOutput: Unable to retrieve device format"; + return false; + } + + 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.mSampleRate / deviceFormat.mSampleRate) * + streamFormat.mBytesPerFrame; + if (internalBufferSize < periodSizeBytes * 2) + internalBufferSize = periodSizeBytes * 2; + else + internalBufferSize -= internalBufferSize % streamFormat.mBytesPerFrame; + + audioBuffer = new 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 (!open()) { + stateCode = QAudio::StopState; + 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(); + + return op; +} + +void QAudioOutputPrivate::stop() +{ + QMutexLocker lock(&mutex); + if (stateCode != QAudio::StopState) { + audioThreadDrain(); + + stateCode = QAudio::StopState; + errorCode = QAudio::NoError; + QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode)); + } +} + +void QAudioOutputPrivate::reset() +{ + QMutexLocker lock(&mutex); + if (stateCode != QAudio::StopState) { + audioThreadStop(); + + stateCode = QAudio::StopState; + 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::SuspendState; + errorCode = QAudio::NoError; + QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode)); + } +} + +void QAudioOutputPrivate::resume() +{ + QMutexLocker lock(&mutex); + if (stateCode == QAudio::SuspendState) { + 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::StopState) + internalBufferSize = bs; +} + +int QAudioOutputPrivate::bufferSize() const +{ + return internalBufferSize; +} + +void QAudioOutputPrivate::setNotifyInterval(int milliSeconds) +{ + intervalTimer->setInterval(milliSeconds); +} + +int QAudioOutputPrivate::notifyInterval() const +{ + return intervalTimer->interval(); +} + +qint64 QAudioOutputPrivate::totalTime() const +{ + return totalFrames * 1000000 / audioFormat.frequency(); +} + +qint64 QAudioOutputPrivate::clock() const +{ + 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::StopState; + QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection); + } +} + +void QAudioOutputPrivate::startTimers() +{ + audioBuffer->startFillTimer(); + 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" + diff --git a/src/multimedia/audio/qaudiooutput_mac_p.h b/src/multimedia/audio/qaudiooutput_mac_p.h new file mode 100644 index 0000000..c85cab4 --- /dev/null +++ b/src/multimedia/audio/qaudiooutput_mac_p.h @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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; + +namespace +{ +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; + QAudioOutputBuffer* audioBuffer; + QAtomicInt audioThreadState; + QWaitCondition threadFinished; + QMutex mutex; + QTimer* intervalTimer; + + 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 totalTime() const; + qint64 clock() 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(); + +signals: + void stateChanged(QAudio::State); + void notify(); + +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 diff --git a/src/multimedia/audio/qaudiooutput_win32_p.cpp b/src/multimedia/audio/qaudiooutput_win32_p.cpp new file mode 100644 index 0000000..f681936 --- /dev/null +++ b/src/multimedia/audio/qaudiooutput_win32_p.cpp @@ -0,0 +1,502 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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" + +//#define DEBUG_AUDIO 1 + +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::StopState; + audioSource = 0; + pullMode = true; + InitializeCriticalSection(&waveOutCriticalSection); +} + +QAudioOutputPrivate::~QAudioOutputPrivate() +{ + close(); + DeleteCriticalSection(&waveOutCriticalSection); +} + +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; + + switch(uMsg) { + case WOM_OPEN: + qAudio->feedback(); + break; + case WOM_CLOSE: + return; + case WOM_DONE: + EnterCriticalSection(&waveOutCriticalSection); + qAudio->waveFreeBlockCount++; + if(qAudio->waveFreeBlockCount >= qAudio->buffer_size/qAudio->period_size) + qAudio->waveFreeBlockCount = qAudio->buffer_size/qAudio->period_size; + LeaveCriticalSection(&waveOutCriticalSection); + 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) +{ + HeapFree(GetProcessHeap(), 0, blockArray); +} + +QAudioFormat QAudioOutputPrivate::format() const +{ + return settings; +} + +QIODevice* QAudioOutputPrivate::start(QIODevice* device) +{ + if(deviceState != QAudio::StopState) + 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::StopState) + return; + deviceState = QAudio::StopState; + 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 + if(buffer_size == 0) { + // Default buffer size, 200ms, default period size is 40ms + buffer_size = settings.frequency()*settings.channels()*(settings.sampleSize()/8)*0.2; + period_size = buffer_size/5; + } else { + period_size = buffer_size/5; + } + waveBlocks = allocateBlocks(period_size, buffer_size/period_size); + waveFreeBlockCount = buffer_size/period_size; + waveCurrentBlock = 0; + + if(audioBuffer == 0) + audioBuffer = new char[buffer_size]; + + timeStamp.restart(); + + 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::fromUtf16((const unsigned short*)woc.szPname); + if(tmp.compare(tr(m_device)) == 0) { + devId = ii; + break; + } + } + } + + if(waveOutOpen(&hWaveOut, devId, &wfx, + (DWORD_PTR)&waveOutProc, + (DWORD_PTR) this, + CALLBACK_FUNCTION) != MMSYSERR_NOERROR) { + errorState = QAudio::OpenError; + deviceState = QAudio::StopState; + emit stateChanged(deviceState); + qWarning("QAudioOutput: open error"); + return false; + } + + totalTimeValue = 0; + timeStampOpened.restart(); + + 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::StopState) + return; + + deviceState = QAudio::StopState; + 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::StopState) + buffer_size = value; +} + +int QAudioOutputPrivate::bufferSize() const +{ + return buffer_size; +} + +void QAudioOutputPrivate::setNotifyInterval(int ms) +{ + intervalTime = ms; +} + +int QAudioOutputPrivate::notifyInterval() const +{ + return intervalTime; +} + +qint64 QAudioOutputPrivate::totalTime() const +{ + return totalTimeValue; +} + +qint64 QAudioOutputPrivate::write( const char *data, qint64 len ) +{ + // Write out some audio data + + char* p = (char*)data; + int l = (int)len; + + WAVEHDR* current; + int remain; + current = &waveBlocks[waveCurrentBlock]; + while(l > 0) { + if(waveFreeBlockCount==0) + break; + + 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)); + + EnterCriticalSection(&waveOutCriticalSection); + waveFreeBlockCount--; + LeaveCriticalSection(&waveOutCriticalSection); +#ifdef DEBUG_AUDIO + qDebug("write out l=%d, waveFreeBlockCount=%d", + current->dwBufferLength,waveFreeBlockCount); +#endif + totalTimeValue += current->dwBufferLength + /(settings.channels()*(settings.sampleSize()/8)) + *1000000/settings.frequency();; + waveCurrentBlock++; + waveCurrentBlock %= buffer_size/period_size; + current = &waveBlocks[waveCurrentBlock]; + current->dwUser = 0; + } + return (len-l); +} + +void QAudioOutputPrivate::resume() +{ + if(deviceState == QAudio::SuspendState) { + deviceState = QAudio::ActiveState; + errorState = QAudio::NoError; + waveOutRestart(hWaveOut); + QTimer::singleShot(10, this, SLOT(feedback())); + emit stateChanged(deviceState); + } +} + +void QAudioOutputPrivate::suspend() +{ + if(deviceState == QAudio::ActiveState) { + waveOutPause(hWaveOut); + deviceState = QAudio::SuspendState; + 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::StopState||deviceState==QAudio::SuspendState)) { + if(bytesAvailable >= period_size) + QMetaObject::invokeMethod(this, "deviceReady", Qt::QueuedConnection); + } +} + +bool QAudioOutputPrivate::deviceReady() +{ + if(pullMode) { + int i = 0; + 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; + + 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) + deviceState = QAudio::ActiveState; + if(startup) + waveOutRestart(hWaveOut); + } else if(l == 0) { + bytesAvailable = bytesFree(); + if(waveFreeBlockCount == buffer_size/period_size) { + errorState = QAudio::UnderrunError; + deviceState = QAudio::IdleState; + emit stateChanged(deviceState); + } + + } else if(i < 0) { + bytesAvailable = bytesFree(); + errorState = QAudio::IOError; + } + } + if(deviceState != QAudio::ActiveState) + return true; + + if(timeStamp.elapsed() > intervalTime && intervalTime > 50) { + emit notify(); + timeStamp.restart(); + } + + return true; +} + +qint64 QAudioOutputPrivate::clock() const +{ + if(deviceState != QAudio::ActiveState) + return 0; + + return timeStampOpened.elapsed(); +} + +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; +} diff --git a/src/multimedia/audio/qaudiooutput_win32_p.h b/src/multimedia/audio/qaudiooutput_win32_p.h new file mode 100644 index 0000000..91f14f5 --- /dev/null +++ b/src/multimedia/audio/qaudiooutput_win32_p.h @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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 <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudioengine.h> + + +static CRITICAL_SECTION waveOutCriticalSection; + +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 totalTime() const; + qint64 clock() 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(); + +signals: + void stateChanged(QAudio::State); + void notify(); + +private: + QByteArray m_device; + bool resuming; + int bytesAvailable; + QTime timeStamp; + QTime timeStampOpened; + qint32 buffer_size; + qint32 period_size; + qint64 totalTimeValue; + bool pullMode; + int intervalTime; + static void CALLBACK waveOutProc( HWAVEOUT hWaveOut, UINT uMsg, + DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 ); + + 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 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; +}; + +#endif diff --git a/src/multimedia/multimedia.pro b/src/multimedia/multimedia.pro new file mode 100644 index 0000000..7d38afa --- /dev/null +++ b/src/multimedia/multimedia.pro @@ -0,0 +1,11 @@ +TARGET = QtMultimedia +QPRO_PWD = $$PWD +QT = core +DEFINES += QT_BUILD_MULTIMEDIA_LIB + +win32-msvc*:QMAKE_LIBS += $$QMAKE_LIBS_CORE + +unix:QMAKE_PKGCONFIG_REQUIRES = QtCore + +include(../qbase.pri) +include(audio/audio.pri) diff --git a/src/plugins/audio/audio.pro b/src/plugins/audio/audio.pro new file mode 100644 index 0000000..e93b369 --- /dev/null +++ b/src/plugins/audio/audio.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs + +#SUBDIRS += ossaudio diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro index cf4edf1..45669e4 100644 --- a/src/plugins/plugins.pro +++ b/src/plugins/plugins.pro @@ -10,3 +10,4 @@ unix { embedded:SUBDIRS *= gfxdrivers decorations mousedrivers kbddrivers !win32:!embedded:!mac:SUBDIRS *= inputmethods contains(QT_CONFIG, phonon): SUBDIRS *= phonon +contains(QT_CONFIG, multimedia): SUBDIRS *= audio diff --git a/src/src.pro b/src/src.pro index 34db728..54ed6da 100644 --- a/src/src.pro +++ b/src/src.pro @@ -22,6 +22,7 @@ contains(QT_CONFIG, opengl)|contains(QT_CONFIG, opengles1)|contains(QT_CONFIG, o contains(QT_CONFIG, openvg): SRC_SUBDIRS += src_openvg contains(QT_CONFIG, xmlpatterns): SRC_SUBDIRS += src_xmlpatterns contains(QT_CONFIG, phonon): SRC_SUBDIRS += src_phonon +contains(QT_CONFIG, multimedia): SRC_SUBDIRS += src_multimedia contains(QT_CONFIG, svg): SRC_SUBDIRS += src_svg contains(QT_CONFIG, webkit) { #exists($$QT_SOURCE_TREE/src/3rdparty/webkit/JavaScriptCore/JavaScriptCore.pro): SRC_SUBDIRS += src_javascriptcore @@ -68,6 +69,8 @@ src_qt3support.subdir = $$QT_SOURCE_TREE/src/qt3support src_qt3support.target = sub-qt3support src_phonon.subdir = $$QT_SOURCE_TREE/src/phonon src_phonon.target = sub-phonon +src_multimedia.subdir = $$QT_SOURCE_TREE/src/multimedia +src_multimedia.target = sub-multimedia src_tools_uic3.subdir = $$QT_SOURCE_TREE/src/tools/uic3 src_tools_uic3.target = sub-uic3 src_activeqt.subdir = $$QT_SOURCE_TREE/src/activeqt @@ -104,6 +107,7 @@ src_webkit.target = sub-webkit src_testlib.depends = src_corelib src_qt3support.depends = src_gui src_xml src_network src_sql src_phonon.depends = src_gui + src_multimedia.depends = src_gui src_tools_uic3.depends = src_qt3support src_xml src_tools_idc.depends = src_corelib src_tools_activeqt.depends = src_tools_idc src_gui diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index b4a6600..2550dcd 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -284,6 +284,10 @@ SUBDIRS += _networkselftest \ qsocks5socketengine \ qsortfilterproxymodel \ qsound \ + qaudiodeviceid \ + qaudioformat \ + qaudiooutput \ + qaudioinput \ qspinbox \ qsplitter \ qsql \ diff --git a/tests/auto/qaudiodeviceid/qaudiodeviceid.pro b/tests/auto/qaudiodeviceid/qaudiodeviceid.pro new file mode 100644 index 0000000..e0c7d4d --- /dev/null +++ b/tests/auto/qaudiodeviceid/qaudiodeviceid.pro @@ -0,0 +1,7 @@ +load(qttest_p4) + +DEFINES += SRCDIR=\\\"$$PWD/\\\" + +SOURCES += tst_qaudiodeviceid.cpp + +QT = core multimedia diff --git a/tests/auto/qaudiodeviceid/tst_qaudiodeviceid.cpp b/tests/auto/qaudiodeviceid/tst_qaudiodeviceid.cpp new file mode 100644 index 0000000..d40118b --- /dev/null +++ b/tests/auto/qaudiodeviceid/tst_qaudiodeviceid.cpp @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include <QtCore/qlocale.h> +#include <qaudiodeviceid.h> +#include <qaudiodeviceinfo.h> + +#include <QStringList> +#include <QList> + + +class tst_QAudioDeviceId : public QObject +{ + Q_OBJECT + +public: + tst_QAudioDeviceId(QObject* parent=0) : QObject(parent) {} + +private slots: + void checkNull(); + void checkEquality(); +}; + +void tst_QAudioDeviceId::checkNull() +{ + // Default constructed is null. + QAudioDeviceId deviceId0; + QVERIFY(deviceId0.isNull()); + + // Null is transferred + QAudioDeviceId deviceId1(deviceId0); + QVERIFY(deviceId1.isNull()); +} + +void tst_QAudioDeviceId::checkEquality() +{ + QAudioDeviceId deviceId0; + QAudioDeviceId deviceId1; + + // Null ids are equivalent + QVERIFY(deviceId0 == deviceId1); + QVERIFY(!(deviceId0 != deviceId1)); + + deviceId1 = QAudioDeviceInfo::defaultOutputDevice(); + + // Different + QVERIFY(deviceId0 != deviceId1); + QVERIFY(!(deviceId0 == deviceId1)); + + // Same + deviceId0 = deviceId1; + + QVERIFY(deviceId0 == deviceId1); + QVERIFY(!(deviceId0 != deviceId1)); +} + +QTEST_MAIN(tst_QAudioDeviceId) + +#include "tst_qaudiodeviceid.moc" diff --git a/tests/auto/qaudiodeviceinfo/qaudiodeviceinfo.pro b/tests/auto/qaudiodeviceinfo/qaudiodeviceinfo.pro new file mode 100644 index 0000000..695987c --- /dev/null +++ b/tests/auto/qaudiodeviceinfo/qaudiodeviceinfo.pro @@ -0,0 +1,7 @@ +load(qttest_p4) + +DEFINES += SRCDIR=\\\"$$PWD/\\\" + +SOURCES += tst_qaudiodeviceinfo.cpp + +QT = core multimedia diff --git a/tests/auto/qaudiodeviceinfo/tst_qaudiodeviceinfo.cpp b/tests/auto/qaudiodeviceinfo/tst_qaudiodeviceinfo.cpp new file mode 100644 index 0000000..c2cd97e --- /dev/null +++ b/tests/auto/qaudiodeviceinfo/tst_qaudiodeviceinfo.cpp @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> +#include <QtCore/qlocale.h> +#include <qaudiodeviceinfo.h> + +#include <QStringList> +#include <QList> + + +class tst_QAudioDeviceInfo : public QObject +{ + Q_OBJECT +public: + tst_QAudioDeviceInfo(QObject* parent=0) : QObject(parent) {} + +private slots: + void checkAvailableDefaultInput(); + void checkAvailableDefaultOutput(); + void outputList(); + void codecs(); + void channels(); + void sampleSizes(); + void byteOrders(); + void sampleTypes(); + void frequencies(); + void isformat(); + void preferred(); + void nearest(); + +private: + QAudioDeviceInfo* device; +}; + +void tst_QAudioDeviceInfo::checkAvailableDefaultInput() +{ + QVERIFY(!QAudioDeviceInfo::defaultInputDevice().isNull()); +} + +void tst_QAudioDeviceInfo::checkAvailableDefaultOutput() +{ + QVERIFY(!QAudioDeviceInfo::defaultOutputDevice().isNull()); +} + +void tst_QAudioDeviceInfo::outputList() +{ + QList<QAudioDeviceId> devices = QAudioDeviceInfo::deviceList(QAudio::AudioOutput); + QVERIFY(devices.size() > 0); + device = new QAudioDeviceInfo(devices.at(0), this); +} + +void tst_QAudioDeviceInfo::codecs() +{ + QStringList avail = device->supportedCodecs(); + QVERIFY(avail.size() > 0); +} + +void tst_QAudioDeviceInfo::channels() +{ + QList<int> avail = device->supportedChannels(); + QVERIFY(avail.size() > 0); +} + +void tst_QAudioDeviceInfo::sampleSizes() +{ + QList<int> avail = device->supportedSampleSizes(); + QVERIFY(avail.size() > 0); +} + +void tst_QAudioDeviceInfo::byteOrders() +{ + QList<QAudioFormat::Endian> avail = device->supportedByteOrders(); + QVERIFY(avail.size() > 0); +} + +void tst_QAudioDeviceInfo::sampleTypes() +{ + QList<QAudioFormat::SampleType> avail = device->supportedSampleTypes(); + QVERIFY(avail.size() > 0); +} + +void tst_QAudioDeviceInfo::frequencies() +{ + QList<int> avail = device->supportedFrequencies(); + QVERIFY(avail.size() > 0); +} + +void tst_QAudioDeviceInfo::isformat() +{ + QAudioFormat format; + format.setFrequency(44100); + format.setChannels(2); + format.setSampleType(QAudioFormat::SignedInt); + format.setByteOrder(QAudioFormat::LittleEndian); + format.setSampleSize(16); + format.setCodec("audio/pcm"); + + // Should always be true for these format + QVERIFY(device->isFormatSupported(format)); +} + +void tst_QAudioDeviceInfo::preferred() +{ + QAudioFormat format = device->preferredFormat(); + QVERIFY(format.frequency() == 44100); + QVERIFY(format.channels() == 2); +} + +void tst_QAudioDeviceInfo::nearest() +{ + QAudioFormat format1, format2; + format1.setFrequency(8000); + format2 = device->nearestFormat(format1); + QVERIFY(format2.frequency() == 44100); +} + +QTEST_MAIN(tst_QAudioDeviceInfo) + +#include "tst_qaudiodeviceinfo.moc" diff --git a/tests/auto/qaudioformat/qaudioformat.pro b/tests/auto/qaudioformat/qaudioformat.pro new file mode 100644 index 0000000..78962d7 --- /dev/null +++ b/tests/auto/qaudioformat/qaudioformat.pro @@ -0,0 +1,7 @@ +load(qttest_p4) + +DEFINES += SRCDIR=\\\"$$PWD/\\\" + +SOURCES += tst_qaudioformat.cpp + +QT = core multimedia diff --git a/tests/auto/qaudioformat/tst_qaudioformat.cpp b/tests/auto/qaudioformat/tst_qaudioformat.cpp new file mode 100644 index 0000000..bcfc78f --- /dev/null +++ b/tests/auto/qaudioformat/tst_qaudioformat.cpp @@ -0,0 +1,180 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> +#include <QtCore/qlocale.h> +#include <qaudioformat.h> + +#include <QStringList> +#include <QList> + + +class tst_QAudioFormat : public QObject +{ + Q_OBJECT + +public: + tst_QAudioFormat(QObject* parent=0) : QObject(parent) {} + +private slots: + void checkNull(); + void checkFrequency(); + void checkChannels(); + void checkSampleSize(); + void checkCodec(); + void checkByteOrder(); + void checkSampleType(); + void checkEquality(); + void checkAssignment(); +}; + +void tst_QAudioFormat::checkNull() +{ + // Default constructed QAudioFormat is null. + QAudioFormat audioFormat0; + QVERIFY(audioFormat0.isNull()); + + // Null is transferred + QAudioFormat audioFormat1(audioFormat0); + QVERIFY(audioFormat1.isNull()); + + // Null is voided on activity + audioFormat0.setFrequency(44100); + QVERIFY(!audioFormat0.isNull()); +} + +void tst_QAudioFormat::checkFrequency() +{ + QAudioFormat audioFormat; + audioFormat.setFrequency(44100); + QVERIFY(audioFormat.frequency() == 44100); +} + +void tst_QAudioFormat::checkChannels() +{ + QAudioFormat audioFormat; + audioFormat.setChannels(2); + QVERIFY(audioFormat.channels() == 2); +} + +void tst_QAudioFormat::checkSampleSize() +{ + QAudioFormat audioFormat; + audioFormat.setSampleSize(16); + QVERIFY(audioFormat.sampleSize() == 16); +} + +void tst_QAudioFormat::checkCodec() +{ + QAudioFormat audioFormat; + audioFormat.setCodec(QString::fromLatin1("audio/pcm")); + QVERIFY(audioFormat.codec() == QString::fromLatin1("audio/pcm")); +} + +void tst_QAudioFormat::checkByteOrder() +{ + QAudioFormat audioFormat; + audioFormat.setByteOrder(QAudioFormat::LittleEndian); + QVERIFY(audioFormat.byteOrder() == QAudioFormat::LittleEndian); +} + +void tst_QAudioFormat::checkSampleType() +{ + QAudioFormat audioFormat; + audioFormat.setSampleType(QAudioFormat::SignedInt); + QVERIFY(audioFormat.sampleType() == QAudioFormat::SignedInt); +} + +void tst_QAudioFormat::checkEquality() +{ + QAudioFormat audioFormat0; + QAudioFormat audioFormat1; + + // Null formats are equivalent + QVERIFY(audioFormat0 == audioFormat1); + QVERIFY(!(audioFormat0 != audioFormat1)); + + // on filled formats + audioFormat0.setFrequency(8000); + audioFormat0.setChannels(1); + audioFormat0.setSampleSize(8); + audioFormat0.setCodec("audio/pcm"); + audioFormat0.setByteOrder(QAudioFormat::LittleEndian); + audioFormat0.setSampleType(QAudioFormat::UnSignedInt); + + audioFormat1.setFrequency(8000); + audioFormat1.setChannels(1); + audioFormat1.setSampleSize(8); + audioFormat1.setCodec("audio/pcm"); + audioFormat1.setByteOrder(QAudioFormat::LittleEndian); + audioFormat1.setSampleType(QAudioFormat::UnSignedInt); + + QVERIFY(audioFormat0 == audioFormat1); + QVERIFY(!(audioFormat0 != audioFormat1)); + + audioFormat0.setFrequency(44100); + QVERIFY(audioFormat0 != audioFormat1); + QVERIFY(!(audioFormat0 == audioFormat1)); +} + +void tst_QAudioFormat::checkAssignment() +{ + QAudioFormat audioFormat0; + QAudioFormat audioFormat1; + + audioFormat0.setFrequency(8000); + audioFormat0.setChannels(1); + audioFormat0.setSampleSize(8); + audioFormat0.setCodec("audio/pcm"); + audioFormat0.setByteOrder(QAudioFormat::LittleEndian); + audioFormat0.setSampleType(QAudioFormat::UnSignedInt); + + audioFormat1 = audioFormat0; + QVERIFY(audioFormat1 == audioFormat0); + + QAudioFormat audioFormat2(audioFormat0); + QVERIFY(audioFormat2 == audioFormat0); +} + +QTEST_MAIN(tst_QAudioFormat) + +#include "tst_qaudioformat.moc" diff --git a/tests/auto/qaudioinput/qaudioinput.pro b/tests/auto/qaudioinput/qaudioinput.pro new file mode 100644 index 0000000..8a03749 --- /dev/null +++ b/tests/auto/qaudioinput/qaudioinput.pro @@ -0,0 +1,7 @@ +load(qttest_p4) + +DEFINES += SRCDIR=\\\"$$PWD/\\\" + +SOURCES += tst_qaudioinput.cpp + +QT = core multimedia diff --git a/tests/auto/qaudioinput/tst_qaudioinput.cpp b/tests/auto/qaudioinput/tst_qaudioinput.cpp new file mode 100644 index 0000000..6e16320 --- /dev/null +++ b/tests/auto/qaudioinput/tst_qaudioinput.cpp @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include <QtCore/qlocale.h> +#include <qaudioinput.h> +#include <qaudiodeviceinfo.h> +#include <qaudio.h> +#include <qaudioformat.h> + + +class tst_QAudioInput : public QObject +{ + Q_OBJECT +public: + tst_QAudioInput(QObject* parent=0) : QObject(parent) {} + +private slots: + void initTestCase(); + void settings(); + void notifyInterval(); + void pullFile(); + +private: + QAudioFormat format; + QAudioInput* audio; +}; + +void tst_QAudioInput::initTestCase() +{ + format.setFrequency(8000); + format.setChannels(1); + format.setSampleSize(8); + format.setCodec("audio/pcm"); + format.setByteOrder(QAudioFormat::LittleEndian); + format.setSampleType(QAudioFormat::UnSignedInt); + + audio = new QAudioInput(format, this); +} + +void tst_QAudioInput::settings() +{ + QAudioFormat f = audio->format(); + + QVERIFY(format.channels() == f.channels()); + QVERIFY(format.frequency() == f.frequency()); + QVERIFY(format.sampleSize() == f.sampleSize()); + QVERIFY(format.codec() == f.codec()); + QVERIFY(format.byteOrder() == f.byteOrder()); + QVERIFY(format.sampleType() == f.sampleType()); +} + +void tst_QAudioInput::notifyInterval() +{ + QVERIFY(audio->notifyInterval() == 1000); // Default + + audio->setNotifyInterval(500); + QVERIFY(audio->notifyInterval() == 500); // Custom + + audio->setNotifyInterval(1000); // reset +} + +void tst_QAudioInput::pullFile() +{ + QFile filename(SRCDIR "test.raw"); + filename.open( QIODevice::WriteOnly | QIODevice::Truncate ); + + QSignalSpy readSignal(audio, SIGNAL(notify())); + audio->start(&filename); + + QTest::qWait(5000); + + QVERIFY(readSignal.count() > 0); + QVERIFY(audio->totalTime() > 0); + + audio->stop(); + filename.close(); +} + +QTEST_MAIN(tst_QAudioInput) + +#include "tst_qaudioinput.moc" diff --git a/tests/auto/qaudiooutput/4.wav b/tests/auto/qaudiooutput/4.wav Binary files differnew file mode 100644 index 0000000..e31b0609 --- /dev/null +++ b/tests/auto/qaudiooutput/4.wav diff --git a/tests/auto/qaudiooutput/qaudiooutput.pro b/tests/auto/qaudiooutput/qaudiooutput.pro new file mode 100644 index 0000000..6c07c64 --- /dev/null +++ b/tests/auto/qaudiooutput/qaudiooutput.pro @@ -0,0 +1,12 @@ +load(qttest_p4) + +DEFINES += SRCDIR=\\\"$$PWD/\\\" + +SOURCES += tst_qaudiooutput.cpp + +QT = core multimedia + +wince*: { + deploy.sources += 4.wav + DEPLOYMENT = deploy +} diff --git a/tests/auto/qaudiooutput/tst_qaudiooutput.cpp b/tests/auto/qaudiooutput/tst_qaudiooutput.cpp new file mode 100644 index 0000000..0f94faa --- /dev/null +++ b/tests/auto/qaudiooutput/tst_qaudiooutput.cpp @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + + +#include <QtTest/QtTest> +#include <QtCore/qlocale.h> +#include <qaudiooutput.h> +#include <qaudiodeviceinfo.h> +#include <qaudio.h> +#include <qaudioformat.h> + + +class tst_QAudioOutput : public QObject +{ + Q_OBJECT +public: + tst_QAudioOutput(QObject* parent=0) : QObject(parent) {} + +private slots: + void initTestCase(); + void settings(); + void notifyInterval(); + void pullFile(); + void pushFile(); + +private: + QAudioFormat format; + QAudioOutput* audio; +}; + +void tst_QAudioOutput::initTestCase() +{ + format.setFrequency(8000); + format.setChannels(1); + format.setSampleSize(8); + format.setCodec("audio/pcm"); + format.setByteOrder(QAudioFormat::LittleEndian); + format.setSampleType(QAudioFormat::UnSignedInt); + + audio = new QAudioOutput(format, this); +} + +void tst_QAudioOutput::settings() +{ + QAudioFormat f = audio->format(); + + QVERIFY(format.channels() == f.channels()); + QVERIFY(format.frequency() == f.frequency()); + QVERIFY(format.sampleSize() == f.sampleSize()); + QVERIFY(format.codec() == f.codec()); + QVERIFY(format.byteOrder() == f.byteOrder()); + QVERIFY(format.sampleType() == f.sampleType()); +} + +void tst_QAudioOutput::notifyInterval() +{ + QVERIFY(audio->notifyInterval() == 1000); // Default + + audio->setNotifyInterval(500); + QVERIFY(audio->notifyInterval() == 500); // Custom + + audio->setNotifyInterval(1000); // reset +} + +void tst_QAudioOutput::pullFile() +{ + QFile filename(SRCDIR "4.wav"); + QVERIFY(filename.exists()); + filename.open(QIODevice::ReadOnly); + + QSignalSpy readSignal(audio, SIGNAL(notify())); + audio->setNotifyInterval(100); + audio->start(&filename); + + QTestEventLoop::instance().enterLoop(1); + // 4.wav is a little less than 700ms, so notify should fire 6 times! + QVERIFY(readSignal.count() >= 6); + QVERIFY(audio->totalTime() == 692250); + + audio->stop(); + filename.close(); +} + +void tst_QAudioOutput::pushFile() +{ + QFile filename(SRCDIR "4.wav"); + QVERIFY(filename.exists()); + filename.open(QIODevice::ReadOnly); + + const qint64 fileSize = filename.size(); + + QIODevice* feed = audio->start(0); + + char* buffer = new char[fileSize]; + filename.read(buffer, fileSize); + + qint64 counter=0; + qint64 written=0; + while(written < fileSize) { + written+=feed->write(buffer+written,fileSize-written); + QTest::qWait(20); + counter++; + } + QTestEventLoop::instance().enterLoop(1); + + QVERIFY(written == fileSize); + QVERIFY(audio->totalTime() == 692250); + + audio->stop(); + filename.close(); + delete [] buffer; + delete audio; +} + +QTEST_MAIN(tst_QAudioOutput) + +#include "tst_qaudiooutput.moc" diff --git a/tests/auto/tests.xml b/tests/auto/tests.xml index a5386b2..ccfc380 100644 --- a/tests/auto/tests.xml +++ b/tests/auto/tests.xml @@ -271,6 +271,10 @@ <Test name="qsocks5socketengine" location="tests/auto/qsocks5socketengine/tst_qsocks5socketengine" /> <Test name="qsortfilterproxymodel" location="tests/auto/qsortfilterproxymodel/tst_qsortfilterproxymodel" /> <Test name="qsound" location="tests/auto/qsound/tst_qsound" /> + <Test name="qaudiodeviceid" location="tests/auto/qaudiodeviceid/tst_qaudiodeviceid" /> + <Test name="qaudioformat" location="tests/auto/qaudioformat/tst_qaudioformat" /> + <Test name="qaudiooutput" location="tests/auto/qaudiooutput/tst_qaudiooutput" /> + <Test name="qaudioinput" location="tests/auto/qaudioinput/tst_qaudioinput" /> <Test name="qsourcelocation" location="tests/auto/qsourcelocation/tst_qsourcelocation" /> <Test name="qspinbox" location="tests/auto/qspinbox/tst_qspinbox" /> <Test name="qsplitter" location="tests/auto/qsplitter/tst_qsplitter" /> @@ -676,6 +680,10 @@ <Test id="qsocks5socketengine" /> <Test id="qsortfilterproxymodel" /> <Test id="qsound" /> + <Test id="qaudiodeviceid" /> + <Test id="qaudioformat" /> + <Test id="qaudiooutput" /> + <Test id="qaudioinput" /> <Test id="qsourcelocation" /> <Test id="qspinbox" /> <Test id="qsplitter" /> diff --git a/tools/configure/configureapp.cpp b/tools/configure/configureapp.cpp index c938919..f32e7dc 100644 --- a/tools/configure/configureapp.cpp +++ b/tools/configure/configureapp.cpp @@ -245,6 +245,7 @@ Configure::Configure( int& argc, char** argv ) dictionary[ "XMLPATTERNS" ] = "auto"; dictionary[ "PHONON" ] = "auto"; dictionary[ "PHONON_BACKEND" ] = "yes"; + dictionary[ "MULTIMEDIA" ] = "yes"; dictionary[ "DIRECTSHOW" ] = "no"; dictionary[ "WEBKIT" ] = "auto"; dictionary[ "PLUGIN_MANIFESTS" ] = "yes"; @@ -843,6 +844,10 @@ void Configure::parseCmdLine() dictionary[ "XMLPATTERNS" ] = "no"; } else if( configCmdLine.at(i) == "-xmlpatterns" ) { dictionary[ "XMLPATTERNS" ] = "yes"; + } else if( configCmdLine.at(i) == "-no-multimedia" ) { + dictionary[ "MULTIMEDIA" ] = "no"; + } else if( configCmdLine.at(i) == "-multimedia" ) { + dictionary[ "MULTIMEDIA" ] = "yes"; } else if( configCmdLine.at(i) == "-no-phonon" ) { dictionary[ "PHONON" ] = "no"; } else if( configCmdLine.at(i) == "-phonon" ) { @@ -1425,6 +1430,7 @@ bool Configure::displayHelp() "[-no-openssl] [-no-dbus] [-dbus] [-dbus-linked] [-platform <spec>]\n" "[-qtnamespace <namespace>] [-qtlibinfix <infix>] [-no-phonon]\n" "[-phonon] [-no-phonon-backend] [-phonon-backend]\n" + "[-no-multimedia] [-multimedia]\n" "[-no-webkit] [-webkit]\n" "[-no-scripttools] [-scripttools]\n" "[-graphicssystem raster|opengl]\n\n", 0, 7); @@ -1599,6 +1605,8 @@ bool Configure::displayHelp() desc("PHONON", "yes", "-phonon", "Compile the Phonon module (Phonon is built if a decent C++ compiler is used.)"); desc("PHONON_BACKEND","no", "-no-phonon-backend","Do not compile the platform-specific Phonon backend-plugin"); desc("PHONON_BACKEND","yes","-phonon-backend", "Compile in the platform-specific Phonon backend-plugin"); + desc("MULTIMEDIA", "no", "-no-multimedia", "Do not compile the multimedia module"); + desc("MULTIMEDIA", "yes","-multimedia", "Compile in multimedia module"); desc("WEBKIT", "no", "-no-webkit", "Do not compile in the WebKit module"); desc("WEBKIT", "yes", "-webkit", "Compile in the WebKit module (WebKit is built if a decent C++ compiler is used.)"); desc("SCRIPTTOOLS", "no", "-no-scripttools", "Do not build the QtScriptTools module."); @@ -1867,6 +1875,8 @@ bool Configure::checkAvailability(const QString &part) if (!findFile("msdmo.lib")) cout << "msdmo.lib not found" << endl; if (!findFile("d3d9.h")) cout << "d3d9.h not found" << endl; } + } else if (part == "MULTIMEDIA") { + available = true; } else if (part == "WEBKIT") { available = (dictionary.value("QMAKESPEC") == "win32-msvc2005") || (dictionary.value("QMAKESPEC") == "win32-msvc2008") || (dictionary.value("QMAKESPEC") == "win32-g++"); } else if (part == "SCRIPTTOOLS") { @@ -2302,6 +2312,9 @@ void Configure::generateOutputVars() qtConfig += "phonon-backend"; } + if (dictionary["MULTIMEDIA"] == "yes") + qtConfig += "multimedia"; + if (dictionary["WEBKIT"] == "yes") qtConfig += "webkit"; @@ -2662,6 +2675,7 @@ void Configure::generateConfigfiles() if(dictionary["IPV6"] == "no") qconfigList += "QT_NO_IPV6"; if(dictionary["WEBKIT"] == "no") qconfigList += "QT_NO_WEBKIT"; if(dictionary["PHONON"] == "no") qconfigList += "QT_NO_PHONON"; + if(dictionary["MULTIMEDIA"] == "no") qconfigList += "QT_NO_MULTIMEDIA"; if(dictionary["XMLPATTERNS"] == "no") qconfigList += "QT_NO_XMLPATTERNS"; if(dictionary["SCRIPTTOOLS"] == "no") qconfigList += "QT_NO_SCRIPTTOOLS"; @@ -2920,6 +2934,7 @@ void Configure::displayConfig() cout << "QtDBus support.............." << dictionary[ "DBUS" ] << endl; cout << "QtXmlPatterns support......." << dictionary[ "XMLPATTERNS" ] << endl; cout << "Phonon support.............." << dictionary[ "PHONON" ] << endl; + cout << "Multimedia support.........." << dictionary[ "MULTIMEDIA" ] << endl; cout << "WebKit support.............." << dictionary[ "WEBKIT" ] << endl; cout << "QtScriptTools support......." << dictionary[ "SCRIPTTOOLS" ] << endl; cout << "Graphics System............." << dictionary[ "GRAPHICS_SYSTEM" ] << endl; |