diff options
author | Qt Continuous Integration System <qt-info@nokia.com> | 2010-02-19 20:08:08 (GMT) |
---|---|---|
committer | Qt Continuous Integration System <qt-info@nokia.com> | 2010-02-19 20:08:08 (GMT) |
commit | 1a8fffa8cae5b4094adcd87bfc4cbc24599d7305 (patch) | |
tree | 2395a3bc3ba571b7a483f7cd1c86c6e9f0852dbc | |
parent | e4094573595ba085ccbd9d968fa87e988c5bfc5b (diff) | |
parent | ee395a5b2f9e0d5f26884dbf40d06f3df7671282 (diff) | |
download | Qt-1a8fffa8cae5b4094adcd87bfc4cbc24599d7305.zip Qt-1a8fffa8cae5b4094adcd87bfc4cbc24599d7305.tar.gz Qt-1a8fffa8cae5b4094adcd87bfc4cbc24599d7305.tar.bz2 |
Merge branch '4.6' of scm.dev.nokia.troll.no:qt/qt-s60-public into 4.6-integration
* '4.6' of scm.dev.nokia.troll.no:qt/qt-s60-public: (22 commits)
use egl properties when creating surfaces on symbian
work around the current include file structure on Symbian^3
Get rid of the dependency on the Symbian app layer
Fix incorrect license headers
Fix incorrect license headers
Supressed Icon sizes on QPushButton in QS60Style
Fixed libstdcpp.dll version autodetection for Symbian
Fixed linkage failure when building qmake on Unix platforms
Factored epocRoot implementation out of qmake
Factored readRegistryKey implementation out of qmake
ColorDialog is always shown as stripped-down version (for QVGA)
Virtual keyboard can't be closed anymore after tapping the text area
Revert "Factored readRegistryKey implementation out of qmake"
Revert "Factored epocRoot implementation out of qmake"
Pass QAudioDeviceInfo when creating audio input/output in examples
Symbian backend for QtMultimedia audio
Modified configure to detect SDK support for Symbian audio backend
Added implementation of epocRoot() function to configure
Removed duplicated implementation of readRegistryKeys from configure
Factored epocRoot implementation out of qmake
...
47 files changed, 3401 insertions, 360 deletions
diff --git a/configure.exe b/configure.exe Binary files differindex 09f4ef2..9974236 100755 --- a/configure.exe +++ b/configure.exe diff --git a/examples/multimedia/audioinput/audioinput.cpp b/examples/multimedia/audioinput/audioinput.cpp index 8a97640..b01a396 100644 --- a/examples/multimedia/audioinput/audioinput.cpp +++ b/examples/multimedia/audioinput/audioinput.cpp @@ -208,6 +208,7 @@ InputTest::InputTest() , m_modeButton(0) , m_suspendResumeButton(0) , m_deviceBox(0) + , m_device(QAudioDeviceInfo::defaultInputDevice()) , m_audioInfo(0) , m_audioInput(0) , m_input(0) @@ -279,7 +280,6 @@ void InputTest::initializeAudio() void InputTest::createAudioInput() { - m_audioInput = new QAudioInput(m_device, m_format, this); connect(m_audioInput, SIGNAL(notify()), SLOT(notified())); connect(m_audioInput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State))); diff --git a/examples/multimedia/audiooutput/audiooutput.cpp b/examples/multimedia/audiooutput/audiooutput.cpp index 4866d36..cbadf02 100644 --- a/examples/multimedia/audiooutput/audiooutput.cpp +++ b/examples/multimedia/audiooutput/audiooutput.cpp @@ -160,6 +160,7 @@ AudioTest::AudioTest() , m_modeButton(0) , m_suspendResumeButton(0) , m_deviceBox(0) + , m_device(QAudioDeviceInfo::defaultOutputDevice()) , m_generator(0) , m_audioOutput(0) , m_output(0) @@ -226,7 +227,7 @@ void AudioTest::createAudioOutput() { delete m_audioOutput; m_audioOutput = 0; - m_audioOutput = new QAudioOutput(m_format, this); + m_audioOutput = new QAudioOutput(m_device, m_format, this); connect(m_audioOutput, SIGNAL(notify()), SLOT(notified())); connect(m_audioOutput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State))); m_generator->start(); diff --git a/mkspecs/features/symbian/stl.prf b/mkspecs/features/symbian/stl.prf index e21ee5c..85c758a 100644 --- a/mkspecs/features/symbian/stl.prf +++ b/mkspecs/features/symbian/stl.prf @@ -15,11 +15,18 @@ INCLUDEPATH += $$OS_LAYER_STDCPP_SYSTEMINCLUDE INCLUDEPATH -= $$[QT_INSTALL_PREFIX]/mkspecs/common/symbian/stl-off # libstdcppv5 is preferred over libstdcpp as it has/uses the throwing version of operator new -exists($${EPOCROOT}epoc32/release/armv5/urel/libstdcppv5.dll)|exists($${EPOCROOT}epoc32/release/winscw/udeb/libstdcppv5.dll) { - LIBS *= -llibstdcppv5.dll +STL_LIB = -llibstdcppv5.dll - # STDCPP turns on standard C++ new behaviour (ie. throwing new) - MMP_RULES += "STDCPP" -} else { - LIBS *= -llibstdcpp.dll +# STDCPP turns on standard C++ new behaviour (ie. throwing new) +STL_MMP_RULE = "STDCPP" + +# Fall back to old implementation if that is the only one that is found +exists($${EPOCROOT}epoc32/release/armv5/urel/libstdcpp.dll)|exists($${EPOCROOT}epoc32/release/winscw/udeb/libstdcpp.dll) { + !exists($${EPOCROOT}epoc32/release/armv5/urel/libstdcppv5.dll):!exists($${EPOCROOT}epoc32/release/winscw/udeb/libstdcppv5.dll) { + STL_LIB = -llibstdcpp.dll + STL_MMP_RULE = + } } + +LIBS *= $$STL_LIB +MMP_RULES *= $$STL_MMP_RULE diff --git a/qmake/Makefile.unix b/qmake/Makefile.unix index fcf43c8..fa98fd6 100644 --- a/qmake/Makefile.unix +++ b/qmake/Makefile.unix @@ -10,7 +10,9 @@ OBJS=project.o property.o main.o makefile.o unixmake2.o unixmake.o \ mingw_make.o option.o winmakefile.o projectgenerator.o \ meta.o makefiledeps.o metamakefile.o xmloutput.o pbuilder_pbx.o \ borland_bmake.o msvc_dsp.o msvc_vcproj.o msvc_nmake.o msvc_objectmodel.o \ - symmake.o initprojectdeploy_symbian.o symmake_abld.o symmake_sbsv2.o + symmake.o initprojectdeploy_symbian.o symmake_abld.o symmake_sbsv2.o \ + registry.o \ + epocroot.o #qt code QOBJS=qtextcodec.o qutfcodec.o qstring.o qtextstream.o qiodevice.o qmalloc.o qglobal.o \ @@ -32,6 +34,8 @@ DEPEND_SRC=project.cpp property.cpp meta.cpp main.cpp generators/makefile.cpp ge generators/makefiledeps.cpp option.cpp generators/win32/mingw_make.cpp generators/makefile.cpp \ generators/win32/msvc_objectmodel.cpp generators/win32/msvc_nmake.cpp generators/win32/borland_bmake.cpp \ generators/symbian/symmake.cpp generators/symbian/initprojectdeploy_symbian.cpp \ + $(SOURCE_PATH)/tools/shared/windows/registry.cpp \ + $(SOURCE_PATH)/tools/shared/symbian/epocroot.cpp \ generators/symbian/symmake_abld.cpp generators/symbian/symmake_sbsv2.cpp \ $(SOURCE_PATH)/src/corelib/codecs/qtextcodec.cpp $(SOURCE_PATH)/src/corelib/codecs/qutfcodec.cpp \ $(SOURCE_PATH)/src/corelib/tools/qstring.cpp $(SOURCE_PATH)/src/corelib/io/qfile.cpp \ @@ -62,6 +66,7 @@ DEPEND_SRC=project.cpp property.cpp meta.cpp main.cpp generators/makefile.cpp ge CPPFLAGS = -I. -Igenerators -Igenerators/unix -Igenerators/win32 -Igenerators/mac -Igenerators/symbian \ -I$(BUILD_PATH)/include -I$(BUILD_PATH)/include/QtCore \ -I$(BUILD_PATH)/src/corelib/global -I$(BUILD_PATH)/src/corelib/xml \ + -I$(BUILD_PATH)/tools/shared \ -DQT_NO_PCRE \ -DQT_BUILD_QMAKE -DQT_BOOTSTRAPPED \ -DQT_NO_TEXTCODEC -DQT_NO_UNICODETABLES -DQT_NO_COMPONENT -DQT_NO_STL \ @@ -281,6 +286,12 @@ symmake_sbsv2.o: generators/symbian/symmake_sbsv2.cpp initprojectdeploy_symbian.o: generators/symbian/initprojectdeploy_symbian.cpp $(CXX) -c -o $@ $(CXXFLAGS) generators/symbian/initprojectdeploy_symbian.cpp +registry.o: $(SOURCE_PATH)/tools/shared/windows/registry.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/tools/shared/windows/registry.cpp + +epocroot.o: $(SOURCE_PATH)/tools/shared/symbian/epocroot.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/tools/shared/symbian/epocroot.cpp + projectgenerator.o: generators/projectgenerator.cpp $(CXX) -c -o $@ $(CXXFLAGS) generators/projectgenerator.cpp @@ -289,6 +300,7 @@ qxmlstream.o: $(SOURCE_PATH)/src/corelib/xml/qxmlstream.cpp qxmlutils.o: $(SOURCE_PATH)/src/corelib/xml/qxmlutils.cpp $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/xml/qxmlutils.cpp + #default rules .cpp.o: $(CXX) -c -o $@ $(CXXFLAGS) $< diff --git a/qmake/Makefile.win32 b/qmake/Makefile.win32 index e6bbcd5..48d84b7 100644 --- a/qmake/Makefile.win32 +++ b/qmake/Makefile.win32 @@ -34,6 +34,7 @@ CFLAGS = -c -Fo$@ \ -I$(BUILD_PATH)\src\corelib\global \ -I$(BUILD_PATH)\src\corelib\xml \ -I$(SOURCE_PATH)\mkspecs\$(QMAKESPEC) \ + -I$(SOURCE_PATH)\tools\shared \ -DQT_NO_TEXTCODEC -DQT_NO_UNICODETABLES -DQT_LITE_COMPONENT -DQT_NODLL -DQT_NO_STL \ -DQT_NO_COMPRESS -DUNICODE -DHAVE_QCONFIG_CPP -DQT_BUILD_QMAKE -DQT_NO_THREAD \ -DQT_NO_QOBJECT -DQT_NO_GEOM_VARIANT -DQT_NO_DATASTREAM -DQT_NO_PCRE -DQT_BOOTSTRAPPED \ @@ -59,6 +60,7 @@ CFLAGS = -c -o$@ \ -I$(SOURCE_PATH)\include -I$(SOURCE_PATH)\include\QtCore \ -I$(BUILD_PATH)\src\corelib\global \ -I$(SOURCE_PATH)\mkspecs\$(QMAKESPEC) \ + -I$(SOURCE_PATH)\tools\shared \ -DQT_NO_TEXTCODEC -DQT_NO_UNICODETABLES -DQT_LITE_COMPONENT -DQT_NODLL -DQT_NO_STL \ -DQT_NO_COMPRESS -DUNICODE -DHAVE_QCONFIG_CPP -DQT_BUILD_QMAKE -DQT_NO_THREAD \ -DQT_NO_QOBJECT -DQT_NO_GEOM_VARIANT @@ -75,6 +77,8 @@ OBJS = project.obj main.obj makefile.obj unixmake.obj unixmake2.obj mingw makefiledeps.obj metamakefile.obj xmloutput.obj pbuilder_pbx.obj \ borland_bmake.obj msvc_nmake.obj msvc_dsp.obj msvc_vcproj.obj \ msvc_objectmodel.obj symmake.obj initprojectdeploy_symbian.obj \ + registry.obj \ + epocroot.obj \ symmake_abld.obj symmake_sbsv2.obj !IFDEF QMAKE_OPENSOURCE_EDITION @@ -198,6 +202,8 @@ clean:: -del symmake_abld.obj -del symmake_sbsv2.obj -del initprojectdeploy_symbian.obj + -del registry.obj + -del epocroot.obj -del pbuilder_pbx.obj -del qxmlstream.obj -del qxmlutils.obj @@ -397,6 +403,12 @@ symmake_sbsv2.obj: $(SOURCE_PATH)/qmake/generators/symbian/symmake_sbsv2.cpp initprojectdeploy_symbian.obj: $(SOURCE_PATH)/qmake/generators/symbian/initprojectdeploy_symbian.cpp $(CXX) $(CXXFLAGS) generators/symbian/initprojectdeploy_symbian.cpp +registry.obj: $(SOURCE_PATH)/tools/shared/windows/registry.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/tools/shared/windows/registry.cpp + +epocroot.obj: $(SOURCE_PATH)/tools/shared/symbian/epocroot.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/tools/shared/symbian/epocroot.cpp + md5.obj: $(SOURCE_PATH)\src\3rdparty\md5\md5.cpp $(CXX) $(CXXFLAGS) $(SOURCE_PATH)\src\3rdparty\md5\md5.cpp diff --git a/qmake/Makefile.win32-g++ b/qmake/Makefile.win32-g++ index ade379b..169de3c 100644 --- a/qmake/Makefile.win32-g++ +++ b/qmake/Makefile.win32-g++ @@ -21,6 +21,7 @@ CFLAGS = -c -o$@ -O \ -I$(BUILD_PATH)/src/corelib/global \ -I$(BUILD_PATH)/src/corelib/xml \ -I$(SOURCE_PATH)/mkspecs/win32-g++ \ + -I$(SOURCE_PATH)/tools/shared \ -DQT_NO_TEXTCODEC -DQT_NO_UNICODETABLES -DQT_LITE_COMPONENT -DQT_NO_PCRE \ -DQT_NODLL -DQT_NO_STL -DQT_NO_COMPRESS -DUNICODE -DHAVE_QCONFIG_CPP \ -DQT_BUILD_QMAKE -DQT_NO_THREAD -DQT_NO_QOBJECT -DQT_NO_GEOM_VARIANT -DQT_NO_DATASTREAM \ @@ -38,6 +39,8 @@ OBJS = project.o main.o makefile.o unixmake.o unixmake2.o mingw_make.o \ makefiledeps.o metamakefile.o xmloutput.o pbuilder_pbx.o \ borland_bmake.o msvc_nmake.o msvc_dsp.o msvc_vcproj.o \ msvc_objectmodel.o symmake.o initprojectdeploy_symbian.o \ + registry.o \ + epocroot.o \ symmake_abld.o symmake_sbsv2.o ifdef QMAKE_OPENSOURCE_EDITION @@ -278,6 +281,12 @@ symmake_sbsv2.o: $(SOURCE_PATH)/qmake/generators/symbian/symmake_sbsv2.cpp initprojectdeploy_symbian.o: $(SOURCE_PATH)/qmake/generators/symbian/initprojectdeploy_symbian.cpp $(CXX) $(CXXFLAGS) generators/symbian/initprojectdeploy_symbian.cpp +registry.o: $(SOURCE_PATH)/tools/shared/windows/registry.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/tools/shared/windows/registry.cpp + +epocroot.o: $(SOURCE_PATH)/tools/shared/symbian/epocroot.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/tools/shared/symbian/epocroot.cpp + project.o: $(SOURCE_PATH)/qmake/project.cpp $(SOURCE_PATH)/qmake/project.h $(SOURCE_PATH)/qmake/option.h $(CXX) $(CXXFLAGS) project.cpp diff --git a/qmake/Makefile.win32-g++-sh b/qmake/Makefile.win32-g++-sh index 8d2723c..98237e7 100644 --- a/qmake/Makefile.win32-g++-sh +++ b/qmake/Makefile.win32-g++-sh @@ -21,6 +21,7 @@ CFLAGS = -c -o$@ -O \ -I$(BUILD_PATH)/src/corelib/global \ -I$(BUILD_PATH)/src/corelib/xml \ -I$(SOURCE_PATH)/mkspecs/win32-g++ \ + -I$(SOURCE_PATH)/tools/shared \ -DQT_NO_TEXTCODEC -DQT_NO_UNICODETABLES -DQT_LITE_COMPONENT -DQT_NO_PCRE \ -DQT_NODLL -DQT_NO_STL -DQT_NO_COMPRESS -DUNICODE -DHAVE_QCONFIG_CPP \ -DQT_BUILD_QMAKE -DQT_NO_THREAD -DQT_NO_QOBJECT -DQT_NO_GEOM_VARIANT -DQT_NO_DATASTREAM \ @@ -38,6 +39,8 @@ OBJS = project.o main.o makefile.o unixmake.o unixmake2.o mingw_make.o \ makefiledeps.o metamakefile.o xmloutput.o pbuilder_pbx.o \ borland_bmake.o msvc_nmake.o msvc_dsp.o msvc_vcproj.o \ msvc_objectmodel.o symmake.o initprojectdeploy_symbian.o \ + registry.o \ + epocroot.o \ symmake_abld.o symmake_sbsv2.o ifdef QMAKE_OPENSOURCE_EDITION @@ -277,6 +280,12 @@ symmake_sbsv2.o: $(SOURCE_PATH)/qmake/generators/symbian/symmake_sbsv2.cpp initprojectdeploy_symbian.o: $(SOURCE_PATH)/qmake/generators/symbian/initprojectdeploy_symbian.cpp $(CXX) $(CXXFLAGS) generators/symbian/initprojectdeploy_symbian.cpp +registry.o: $(SOURCE_PATH)/tools/shared/windows/registry.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/tools/shared/windows/registry.cpp + +epocroot.o: $(SOURCE_PATH)/tools/shared/symbian/epocroot.cpp + $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/tools/shared/symbian/epocroot.cpp + project.o: $(SOURCE_PATH)/qmake/project.cpp $(SOURCE_PATH)/qmake/project.h $(SOURCE_PATH)/qmake/option.h $(CXX) $(CXXFLAGS) project.cpp diff --git a/qmake/generators/symbian/initprojectdeploy_symbian.cpp b/qmake/generators/symbian/initprojectdeploy_symbian.cpp index 5fbff58..2a22305 100644 --- a/qmake/generators/symbian/initprojectdeploy_symbian.cpp +++ b/qmake/generators/symbian/initprojectdeploy_symbian.cpp @@ -46,105 +46,15 @@ #include <qsettings.h> #include <qdebug.h> +// Included from tools/shared +#include <symbian/epocroot.h> + #define SYSBIN_DIR "\\sys\\bin" #define SUFFIX_DLL "dll" #define SUFFIX_EXE "exe" #define SUFFIX_QTPLUGIN "qtplugin" -static void fixEpocRootStr(QString& path) -{ - path.replace("\\", "/"); - - if (path.size() > 1 && path[1] == QChar(':')) { - path = path.mid(2); - } - - if (!path.size() || path[path.size()-1] != QChar('/')) { - path += QChar('/'); - } -} - -#define SYMBIAN_SDKS_KEY "HKEY_LOCAL_MACHINE\\Software\\Symbian\\EPOC SDKs" - -static QString epocRootStr; - -QString epocRoot() -{ - if (!epocRootStr.isEmpty()) { - return epocRootStr; - } - - // First, check the env variable - epocRootStr = qgetenv("EPOCROOT"); - - if (epocRootStr.isEmpty()) { - // No EPOCROOT set, check the default device - // First check EPOCDEVICE env variable - QString defaultDevice = qgetenv("EPOCDEVICE"); - - // Check the windows registry via QSettings for devices.xml path - QSettings settings(SYMBIAN_SDKS_KEY, QSettings::NativeFormat); - QString devicesXmlPath = settings.value("CommonPath").toString(); - - if (!devicesXmlPath.isEmpty()) { - // Parse xml for correct device - devicesXmlPath += "/devices.xml"; - QFile devicesFile(devicesXmlPath); - if (devicesFile.open(QIODevice::ReadOnly)) { - QXmlStreamReader xml(&devicesFile); - while (!xml.atEnd()) { - xml.readNext(); - if (xml.isStartElement() && xml.name() == "devices") { - if (xml.attributes().value("version") == "1.0") { - // Look for correct device - while (!(xml.isEndElement() && xml.name() == "devices") && !xml.atEnd()) { - xml.readNext(); - if (xml.isStartElement() && xml.name() == "device") { - if ((defaultDevice.isEmpty() && xml.attributes().value("default") == "yes") || - (!defaultDevice.isEmpty() && (xml.attributes().value("id").toString() + QString(":") + xml.attributes().value("name").toString()) == defaultDevice)) { - // Found the correct device - while (!(xml.isEndElement() && xml.name() == "device") && !xml.atEnd()) { - xml.readNext(); - if (xml.isStartElement() && xml.name() == "epocroot") { - epocRootStr = xml.readElementText(); - fixEpocRootStr(epocRootStr); - return epocRootStr; - } - } - xml.raiseError("No epocroot element found"); - } - } - } - } else { - xml.raiseError("Invalid 'devices' element version"); - } - } - } - if (xml.hasError()) { - fprintf(stderr, "ERROR: \"%s\" when parsing devices.xml\n", qPrintable(xml.errorString())); - } - } else { - fprintf(stderr, "Could not open devices.xml (%s)\n", qPrintable(devicesXmlPath)); - } - } else { - fprintf(stderr, "Could not retrieve " SYMBIAN_SDKS_KEY " setting\n"); - } - - fprintf(stderr, "Failed to determine epoc root.\n"); - if (!defaultDevice.isEmpty()) - fprintf(stderr, "The device indicated by EPOCDEVICE environment variable (%s) could not be found.\n", qPrintable(defaultDevice)); - fprintf(stderr, "Either set EPOCROOT or EPOCDEVICE environment variable to a valid value, or provide a default Symbian device.\n"); - - // No valid device found; set epocroot to "/" - epocRootStr = QLatin1String("/"); - } - - fixEpocRootStr(epocRootStr); - return epocRootStr; -} - - static bool isPlugin(const QFileInfo& info, const QString& devicePath) { // Libraries are plugins if deployment path is something else than diff --git a/qmake/generators/symbian/initprojectdeploy_symbian.h b/qmake/generators/symbian/initprojectdeploy_symbian.h index e23e6a9..b409225 100644 --- a/qmake/generators/symbian/initprojectdeploy_symbian.h +++ b/qmake/generators/symbian/initprojectdeploy_symbian.h @@ -50,8 +50,6 @@ #include <qfile.h> #include <stdlib.h> -#include "epocroot.h" - #define PLUGIN_STUB_DIR "qmakepluginstubs" struct CopyItem diff --git a/qmake/generators/symbian/symmake.cpp b/qmake/generators/symbian/symmake.cpp index 2055f81..a712434 100644 --- a/qmake/generators/symbian/symmake.cpp +++ b/qmake/generators/symbian/symmake.cpp @@ -49,6 +49,9 @@ #include <stdlib.h> #include <qdebug.h> +// Included from tools/shared +#include <symbian/epocroot.h> + #define RESOURCE_DIRECTORY_MMP "/resource/apps" #define RESOURCE_DIRECTORY_RESOURCE "\\\\resource\\\\apps\\\\" #define REGISTRATION_RESOURCE_DIRECTORY_HW "/private/10003a3f/import/apps" diff --git a/qmake/generators/symbian/symmake_abld.cpp b/qmake/generators/symbian/symmake_abld.cpp index 033bcbe..6c62412 100644 --- a/qmake/generators/symbian/symmake_abld.cpp +++ b/qmake/generators/symbian/symmake_abld.cpp @@ -48,6 +48,9 @@ #include <qdatetime.h> #include <qdebug.h> +// Included from tools/shared +#include <symbian/epocroot.h> + #define DO_NOTHING_TARGET "do_nothing" #define CREATE_TEMPS_TARGET "create_temps" #define EXTENSION_CLEAN "extension_clean" diff --git a/qmake/generators/symbian/symmake_sbsv2.cpp b/qmake/generators/symbian/symmake_sbsv2.cpp index e081b19..8accfce 100644 --- a/qmake/generators/symbian/symmake_sbsv2.cpp +++ b/qmake/generators/symbian/symmake_sbsv2.cpp @@ -48,6 +48,9 @@ #include <qdatetime.h> #include <qdebug.h> +// Included from tools/shared +#include <symbian/epocroot.h> + SymbianSbsv2MakefileGenerator::SymbianSbsv2MakefileGenerator() : SymbianMakefileGenerator() { } SymbianSbsv2MakefileGenerator::~SymbianSbsv2MakefileGenerator() { } diff --git a/qmake/generators/win32/msvc_vcproj.cpp b/qmake/generators/win32/msvc_vcproj.cpp index 47986f5..58f95e9 100644 --- a/qmake/generators/win32/msvc_vcproj.cpp +++ b/qmake/generators/win32/msvc_vcproj.cpp @@ -67,6 +67,7 @@ QT_END_NAMESPACE #ifdef Q_OS_WIN32 #include <qt_windows.h> +#include <windows/registry.h> QT_BEGIN_NAMESPACE @@ -93,102 +94,6 @@ struct { {NETUnknown, "", ""}, }; -static QString keyPath(const QString &rKey) -{ - int idx = rKey.lastIndexOf(QLatin1Char('\\')); - if (idx == -1) - return QString(); - return rKey.left(idx + 1); -} - -static QString keyName(const QString &rKey) -{ - int idx = rKey.lastIndexOf(QLatin1Char('\\')); - if (idx == -1) - return rKey; - - QString res(rKey.mid(idx + 1)); - if (res == "Default" || res == ".") - res = ""; - return res; -} - -static QString readRegistryKey(HKEY parentHandle, const QString &rSubkey) -{ - - QString rSubkeyName = keyName(rSubkey); - QString rSubkeyPath = keyPath(rSubkey); - - HKEY handle = 0; - LONG res = RegOpenKeyEx(parentHandle, (wchar_t*)rSubkeyPath.utf16(), 0, KEY_READ, &handle); - - if (res != ERROR_SUCCESS) - return QString(); - - // get the size and type of the value - DWORD dataType; - DWORD dataSize; - res = RegQueryValueEx(handle, (wchar_t*)rSubkeyName.utf16(), 0, &dataType, 0, &dataSize); - if (res != ERROR_SUCCESS) { - RegCloseKey(handle); - return QString(); - } - - // get the value - QByteArray data(dataSize, 0); - res = RegQueryValueEx(handle, (wchar_t*)rSubkeyName.utf16(), 0, 0, - reinterpret_cast<unsigned char*>(data.data()), &dataSize); - if (res != ERROR_SUCCESS) { - RegCloseKey(handle); - return QString(); - } - - QString result; - switch (dataType) { - case REG_EXPAND_SZ: - case REG_SZ: { - result = QString::fromWCharArray(((const wchar_t *)data.constData())); - break; - } - - case REG_MULTI_SZ: { - QStringList l; - int i = 0; - for (;;) { - QString s = QString::fromWCharArray((const wchar_t *)data.constData() + i); - i += s.length() + 1; - - if (s.isEmpty()) - break; - l.append(s); - } - result = l.join(", "); - break; - } - - case REG_NONE: - case REG_BINARY: { - result = QString::fromWCharArray((const wchar_t *)data.constData(), data.size() / 2); - break; - } - - case REG_DWORD_BIG_ENDIAN: - case REG_DWORD: { - Q_ASSERT(data.size() == sizeof(int)); - int i; - memcpy((char*)&i, data.constData(), sizeof(int)); - result = QString::number(i); - break; - } - - default: - qWarning("QSettings: unknown data %d type in windows registry", dataType); - break; - } - - RegCloseKey(handle); - return result; -} QT_END_NAMESPACE #endif diff --git a/qmake/project.cpp b/qmake/project.cpp index 4ce8ba4..e4ef7dd 100644 --- a/qmake/project.cpp +++ b/qmake/project.cpp @@ -44,8 +44,6 @@ #include "option.h" #include "cachekeys.h" -#include "epocroot.h" - #include <qdatetime.h> #include <qfile.h> #include <qfileinfo.h> @@ -64,6 +62,9 @@ #include <stdio.h> #include <stdlib.h> +// Included from tools/shared +#include <symbian/epocroot.h> + #ifdef Q_OS_WIN32 #define QT_POPEN _popen #define QT_PCLOSE _pclose diff --git a/qmake/qmake.pri b/qmake/qmake.pri index efe4f36..05debe6 100644 --- a/qmake/qmake.pri +++ b/qmake/qmake.pri @@ -17,7 +17,9 @@ SOURCES += project.cpp property.cpp main.cpp generators/makefile.cpp \ generators/symbian/symmake.cpp \ generators/symbian/symmake_abld.cpp \ generators/symbian/symmake_sbsv2.cpp \ - generators/symbian/initprojectdeploy_symbian.cpp + generators/symbian/initprojectdeploy_symbian.cpp \ + windows/registry.cpp \ + symbian/epocroot.cpp HEADERS += project.h property.h generators/makefile.h \ generators/unix/unixmake.h meta.h option.h cachekeys.h \ @@ -29,8 +31,9 @@ HEADERS += project.h property.h generators/makefile.h \ generators/symbian/symmake.h \ generators/symbian/symmake_abld.h \ generators/symbian/symmake_sbsv2.h \ - generators/symbian/epocroot.h \ - generators/symbian/initprojectdeploy_symbian.h + generators/symbian/initprojectdeploy_symbian.h \ + windows/registry.h \ + symbian/epocroot.h contains(QT_EDITION, OpenSource) { DEFINES += QMAKE_OPENSOURCE_EDITION diff --git a/qmake/qmake.pro b/qmake/qmake.pro index 00dcbce..f3f9d53 100644 --- a/qmake/qmake.pro +++ b/qmake/qmake.pro @@ -27,5 +27,9 @@ INCPATH += generators \ $$QT_SOURCE_TREE/include \ $$QT_SOURCE_TREE/include/QtCore \ $$QT_SOURCE_TREE/qmake + +VPATH += $$QT_SOURCE_TREE/tools/shared +INCPATH += $$QT_SOURCE_TREE/tools/shared + include(qmake.pri) diff --git a/src/corelib/xml/qxmlstream_p.h b/src/corelib/xml/qxmlstream_p.h index 6b911d2..ac421cf 100644 --- a/src/corelib/xml/qxmlstream_p.h +++ b/src/corelib/xml/qxmlstream_p.h @@ -54,7 +54,7 @@ #ifndef QXMLSTREAM_P_H #define QXMLSTREAM_P_H -#if defined(Q_OS_VXWORKS) && defined(ERROR) +#if defined(ERROR) # undef ERROR #endif diff --git a/src/gui/dialogs/qcolordialog.cpp b/src/gui/dialogs/qcolordialog.cpp index 83ecc30..e6abf7f 100644 --- a/src/gui/dialogs/qcolordialog.cpp +++ b/src/gui/dialogs/qcolordialog.cpp @@ -1078,8 +1078,7 @@ QColorShower::QColorShower(QColorDialog *parent) #ifdef QT_SMALL_COLORDIALOG # ifdef Q_WS_S60 - QS60Data s60Data = QS60Data(); - const bool nonTouchUI = !s60Data.hasTouchscreen; + const bool nonTouchUI = !S60->hasTouchscreen; # elif defined Q_WS_MAEMO_5 const bool nonTouchUI = false; # endif @@ -1506,8 +1505,7 @@ void QColorDialogPrivate::init(const QColor &initial) #if defined(QT_SMALL_COLORDIALOG) # if defined(Q_WS_S60) - QS60Data s60Data = QS60Data(); - const bool nonTouchUI = !s60Data.hasTouchscreen; + const bool nonTouchUI = !S60->hasTouchscreen; # elif defined(Q_WS_MAEMO_5) const bool nonTouchUI = false; # endif diff --git a/src/gui/egl/qegl_symbian.cpp b/src/gui/egl/qegl_symbian.cpp index b1c9408..5a010cd 100644 --- a/src/gui/egl/qegl_symbian.cpp +++ b/src/gui/egl/qegl_symbian.cpp @@ -78,9 +78,9 @@ EGLSurface QEglContext::createSurface(QPaintDevice *device, const QEglProperties props = 0; EGLSurface surf; if (devType == QInternal::Widget) - surf = eglCreateWindowSurface(dpy, cfg, windowDrawable, 0); + surf = eglCreateWindowSurface(dpy, cfg, windowDrawable, props); else - surf = eglCreatePixmapSurface(dpy, cfg, pixmapDrawable, 0); + surf = eglCreatePixmapSurface(dpy, cfg, pixmapDrawable, props); if (surf == EGL_NO_SURFACE) qWarning("QEglContext::createSurface(): Unable to create EGL surface, error = 0x%x", eglGetError()); return surf; diff --git a/src/gui/inputmethod/qcoefepinputcontext_s60.cpp b/src/gui/inputmethod/qcoefepinputcontext_s60.cpp index 2b91711..1ac8ace 100644 --- a/src/gui/inputmethod/qcoefepinputcontext_s60.cpp +++ b/src/gui/inputmethod/qcoefepinputcontext_s60.cpp @@ -113,7 +113,7 @@ void QCoeFepInputContext::update() updateHints(false); // For pre-5.0 SDKs, we don't do text updates on S60 side. - if (QSysInfo::s60Version() != QSysInfo::SV_S60_5_0) { + if (QSysInfo::s60Version() < QSysInfo::SV_S60_5_0) { return; } @@ -740,6 +740,9 @@ void QCoeFepInputContext::GetScreenCoordinatesForFepL(TPoint& aLeftSideOfBaseLin void QCoeFepInputContext::DoCommitFepInlineEditL() { commitCurrentString(false); + if (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0) + ReportAknEdStateEvent(QT_EAknCursorPositionChanged); + } void QCoeFepInputContext::commitCurrentString(bool cancelFepTransaction) diff --git a/src/gui/styles/qs60style.cpp b/src/gui/styles/qs60style.cpp index ea7399f..565cc2c 100644 --- a/src/gui/styles/qs60style.cpp +++ b/src/gui/styles/qs60style.cpp @@ -120,6 +120,8 @@ QPixmap *QS60StylePrivate::m_background = 0; // theme palette QPalette *QS60StylePrivate::m_themePalette = 0; +qint64 QS60StylePrivate::m_webPaletteKey = 0; + const struct QS60StylePrivate::frameElementCenter QS60StylePrivate::m_frameElementsData[] = { {SE_ButtonNormal, QS60StyleEnums::SP_QsnFrButtonTbCenter}, {SE_ButtonPressed, QS60StyleEnums::SP_QsnFrButtonTbCenterPressed}, @@ -807,8 +809,12 @@ void QS60StylePrivate::setThemePaletteHash(QPalette *palette) const QPalette webPalette = *palette; webPalette.setColor(QPalette::WindowText, Qt::black); webPalette.setColor(QPalette::Text, Qt::black); + webPalette.setBrush(QPalette::Base, Qt::white); + QApplication::setPalette(webPalette, "QWebView"); QApplication::setPalette(webPalette, "QGraphicsWebView"); + + m_webPaletteKey = webPalette.cacheKey(); } QSize QS60StylePrivate::partSize(QS60StyleEnums::SkinParts part, SkinElementFlags flags) @@ -896,8 +902,11 @@ QSize QS60StylePrivate::partSize(QS60StyleEnums::SkinParts part, SkinElementFlag return result; } -bool QS60StylePrivate::canDrawThemeBackground(const QBrush &backgroundBrush) +bool QS60StylePrivate::canDrawThemeBackground(const QBrush &backgroundBrush, const QWidget *widget) { + // Always return true for web pages. + if (widget && m_webPaletteKey == QApplication::palette(widget).cacheKey()) + return true; //If brush is not changed from style's default values, draw theme graphics. return (backgroundBrush.color() == Qt::transparent || backgroundBrush.style() == Qt::NoBrush) ? true : false; @@ -1901,7 +1910,7 @@ void QS60Style::drawControl(ControlElement element, const QStyleOption *option, case CE_ShapedFrame: if (const QTextEdit *textEdit = qobject_cast<const QTextEdit *>(widget)) { const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(option); - if (QS60StylePrivate::canDrawThemeBackground(frame->palette.base())) + if (QS60StylePrivate::canDrawThemeBackground(frame->palette.base(), widget)) QS60StylePrivate::drawSkinElement(QS60StylePrivate::SE_Editor, painter, option->rect, flags); else QCommonStyle::drawControl(element, option, painter, widget); @@ -2013,7 +2022,7 @@ void QS60Style::drawPrimitive(PrimitiveElement element, const QStyleOption *opti if (widget && qobject_cast<const QComboBox *>(widget->parentWidget())) break; #endif - if (QS60StylePrivate::canDrawThemeBackground(option->palette.base())) + if (QS60StylePrivate::canDrawThemeBackground(option->palette.base(), widget)) QS60StylePrivate::drawSkinElement(QS60StylePrivate::SE_FrameLineEdit, painter, option->rect, flags); else commonStyleDraws = true; @@ -2093,7 +2102,7 @@ void QS60Style::drawPrimitive(PrimitiveElement element, const QStyleOption *opti case PE_PanelButtonTool: case PE_PanelButtonBevel: case PE_FrameButtonBevel: - if (QS60StylePrivate::canDrawThemeBackground(option->palette.base())) { + if (QS60StylePrivate::canDrawThemeBackground(option->palette.base(), widget)) { const bool isPressed = option->state & State_Sunken; const QS60StylePrivate::SkinElements skinElement = isPressed ? QS60StylePrivate::SE_ButtonPressed : QS60StylePrivate::SE_ButtonNormal; @@ -2125,7 +2134,7 @@ void QS60Style::drawPrimitive(PrimitiveElement element, const QStyleOption *opti case PE_IndicatorSpinDown: case PE_IndicatorSpinUp: if (const QStyleOptionSpinBox *spinBox = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) { - if (QS60StylePrivate::canDrawThemeBackground(spinBox->palette.base())) { + if (QS60StylePrivate::canDrawThemeBackground(spinBox->palette.base(), widget)) { QStyleOptionSpinBox optionSpinBox = *spinBox; const QS60StyleEnums::SkinParts part = (element == PE_IndicatorSpinUp) ? QS60StyleEnums::SP_QgnGrafScrollArrowUp : @@ -2140,7 +2149,7 @@ void QS60Style::drawPrimitive(PrimitiveElement element, const QStyleOption *opti #endif //QT_NO_SPINBOX #ifndef QT_NO_COMBOBOX if (const QStyleOptionFrame *cmb = qstyleoption_cast<const QStyleOptionFrame *>(option)) { - if (QS60StylePrivate::canDrawThemeBackground( option->palette.base())) { + if (QS60StylePrivate::canDrawThemeBackground( option->palette.base(), widget)) { // We want to draw down arrow here for comboboxes as well. QStyleOptionFrame optionsComboBox = *cmb; const QS60StyleEnums::SkinParts part = QS60StyleEnums::SP_QgnGrafScrollArrowDown; @@ -2179,7 +2188,7 @@ void QS60Style::drawPrimitive(PrimitiveElement element, const QStyleOption *opti #endif //QT_NO_MENU ) { //Need extra check since dialogs have their own theme background - if (QS60StylePrivate::canDrawThemeBackground(option->palette.base()) && + if (QS60StylePrivate::canDrawThemeBackground(option->palette.base(), widget) && option->palette.window().texture().cacheKey() == QS60StylePrivate::m_themePalette->window().texture().cacheKey()) QS60StylePrivate::drawSkinElement(QS60StylePrivate::SE_OptionsMenu, painter, option->rect, flags); @@ -2387,10 +2396,20 @@ QSize QS60Style::sizeFromContents(ContentsType ct, const QStyleOption *opt, case CT_PushButton: sz = QCommonStyle::sizeFromContents( ct, opt, csz, widget); //FIXME properly - style should calculate the location of border frame-part - sz += QSize(2 * pixelMetric(PM_ButtonMargin), 2 * pixelMetric(PM_ButtonMargin)); - if (const QAbstractButton *buttonWidget = (qobject_cast<const QAbstractButton *>(widget))) + if (const QAbstractButton *buttonWidget = (qobject_cast<const QAbstractButton *>(widget))) { if (buttonWidget->isCheckable()) sz += QSize(pixelMetric(PM_IndicatorWidth) + pixelMetric(PM_CheckBoxLabelSpacing), 0); + const int iconHeight = (!buttonWidget->icon().isNull()) ? buttonWidget->iconSize().height() : 0; + const int textHeight = (buttonWidget->text().length() > 0) ? + buttonWidget->fontMetrics().size(Qt::TextSingleLine, buttonWidget->text()).height() : 0; + const int decoratorHeight = (buttonWidget->isCheckable()) ? pixelMetric(PM_IndicatorHeight) : 0; + + const int contentHeight = + qMax(qMax(iconHeight, decoratorHeight) + pixelMetric(PM_ButtonMargin), + textHeight + 2*pixelMetric(PM_ButtonMargin)); + sz.setHeight(contentHeight); + sz += QSize(2 * pixelMetric(PM_ButtonMargin), 0); + } break; case CT_LineEdit: if (const QStyleOptionFrame *f = qstyleoption_cast<const QStyleOptionFrame *>(opt)) diff --git a/src/gui/styles/qs60style_p.h b/src/gui/styles/qs60style_p.h index ea30b81..16d82e7 100644 --- a/src/gui/styles/qs60style_p.h +++ b/src/gui/styles/qs60style_p.h @@ -542,7 +542,7 @@ public: //Checks that the current brush is transparent or has BrushStyle NoBrush, //so that theme graphic background can be drawn. - static bool canDrawThemeBackground(const QBrush &backgroundBrush); + static bool canDrawThemeBackground(const QBrush &backgroundBrush, const QWidget *widget); static int currentAnimationFrame(QS60StyleEnums::SkinParts part); #ifdef Q_WS_S60 @@ -596,6 +596,7 @@ private: QPalette m_originalPalette; QPointer<QFocusFrame> m_focusFrame; + static qint64 m_webPaletteKey; #ifdef Q_WS_S60 //list of progress bars having animation running diff --git a/src/gui/util/qdesktopservices_s60.cpp b/src/gui/util/qdesktopservices_s60.cpp index 319c4b0..adc4fc1 100644 --- a/src/gui/util/qdesktopservices_s60.cpp +++ b/src/gui/util/qdesktopservices_s60.cpp @@ -48,7 +48,6 @@ #include <qurl.h> #include <private/qcore_symbian_p.h> -#include <miutset.h> // KUidMsgTypeSMTP #include <txtrich.h> // CRichText #include <f32file.h> // TDriveUnit etc #include <eikenv.h> // CEikonEnv @@ -57,6 +56,9 @@ #include <rsendas.h> // RSendAs #include <rsendasmessage.h> // RSendAsMessage +// copied from miutset.h, so we don't get a dependency into the app layer +const TUid KUidMsgTypeSMTP = {0x10001028}; // 268439592 + #ifdef Q_WS_S60 # include <pathinfo.h> // PathInfo # ifdef USE_DOCUMENTHANDLER diff --git a/src/plugins/audio/audio.pro b/src/plugins/audio/audio.pro index e93b369..5f75a8d 100644 --- a/src/plugins/audio/audio.pro +++ b/src/plugins/audio/audio.pro @@ -1,3 +1,9 @@ TEMPLATE = subdirs +SUBDIRS = + +contains(QT_CONFIG, audio-backend) { + symbian { + SUBDIRS += symbian + } +} -#SUBDIRS += ossaudio diff --git a/src/plugins/audio/symbian/main.cpp b/src/plugins/audio/symbian/main.cpp new file mode 100644 index 0000000..536a8ec --- /dev/null +++ b/src/plugins/audio/symbian/main.cpp @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtMultimedia/qaudioengineplugin.h> +#include <QtMultimedia/qaudioengine.h> + +#include <qstringlist.h> +#include <qiodevice.h> +#include <qbytearray.h> +#include <qdebug.h> + +#include "symbianaudiodeviceinfo.h" +#include "symbianaudioinput.h" +#include "symbianaudiooutput.h" + +QT_BEGIN_NAMESPACE + +class SymbianAudioPlugin : public QAudioEnginePlugin +{ +public: + SymbianAudioPlugin(QObject *parent = 0); + ~SymbianAudioPlugin(); + + QStringList keys() const; + + QList<QByteArray> availableDevices(QAudio::Mode) const; + QAbstractAudioInput* createInput(const QByteArray& device, + const QAudioFormat& format = QAudioFormat()); + QAbstractAudioOutput* createOutput(const QByteArray& device, + const QAudioFormat& format = QAudioFormat()); + QAbstractAudioDeviceInfo* createDeviceInfo(const QByteArray& device, + QAudio::Mode mode); +}; + +SymbianAudioPlugin::SymbianAudioPlugin(QObject *parent) + : QAudioEnginePlugin(parent) +{ + +} + +SymbianAudioPlugin::~SymbianAudioPlugin() +{ + +} + +QStringList SymbianAudioPlugin::keys() const +{ + QStringList keys(QLatin1String("default")); + keys << QLatin1String("default"); + return keys; +} + +QList<QByteArray> SymbianAudioPlugin::availableDevices(QAudio::Mode mode) const +{ + Q_UNUSED(mode) + QList<QByteArray> devices; + devices.append("default"); + return devices; +} + +QAbstractAudioInput* SymbianAudioPlugin::createInput( + const QByteArray &device, const QAudioFormat &format) +{ + return new SymbianAudioInput(device, format); +} + +QAbstractAudioOutput* SymbianAudioPlugin::createOutput( + const QByteArray &device, const QAudioFormat &format) +{ + return new SymbianAudioOutput(device, format); +} + +QAbstractAudioDeviceInfo* SymbianAudioPlugin::createDeviceInfo( + const QByteArray& device, QAudio::Mode mode) +{ + return new SymbianAudioDeviceInfo(device, mode); +} + +Q_EXPORT_STATIC_PLUGIN(SymbianAudioPlugin) +Q_EXPORT_PLUGIN2(qaudio, SymbianAudioPlugin) + +QT_END_NAMESPACE + diff --git a/src/plugins/audio/symbian/symbian.pro b/src/plugins/audio/symbian/symbian.pro new file mode 100644 index 0000000..7355daa --- /dev/null +++ b/src/plugins/audio/symbian/symbian.pro @@ -0,0 +1,31 @@ +QT += multimedia +TARGET = qaudio + +# Paths to DevSound headers +INCLUDEPATH += /epoc32/include/mmf/common +INCLUDEPATH += /epoc32/include/mmf/server + +HEADERS += \ + symbianaudio.h \ + symbianaudiodeviceinfo.h \ + symbianaudioinput.h \ + symbianaudiooutput.h \ + symbianaudioutils.h + +SOURCES += \ + main.cpp \ + symbianaudiodeviceinfo.cpp \ + symbianaudioinput.cpp \ + symbianaudiooutput.cpp \ + symbianaudioutils.cpp + +LIBS += -lmmfdevsound + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/audio +target.path = $$[QT_INSTALL_PLUGINS]/audio +INSTALLS += target + +include(../../qpluginbase.pri) + +TARGET.UID3 = 0x2001E630 + diff --git a/src/plugins/audio/symbian/symbianaudio.h b/src/plugins/audio/symbian/symbianaudio.h new file mode 100644 index 0000000..3fc0419 --- /dev/null +++ b/src/plugins/audio/symbian/symbianaudio.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SYMBIANAUDIO_H +#define SYMBIANAUDIO_H + +#include <QtCore/qnamespace.h> + +QT_BEGIN_NAMESPACE + +namespace SymbianAudio { + +/** + * Default values used by audio input and output classes, when underlying + * DevSound instance has not yet been created. + */ + +const int DefaultBufferSize = 4096; // bytes +const int DefaultNotifyInterval = 1000; // ms + +/** + * Enumeration used to track state of internal DevSound instances. + * Values are translated to the corresponding QAudio::State values by + * SymbianAudio::Utils::stateNativeToQt. + */ +enum State { + ClosedState + , InitializingState + , ActiveState + , IdleState + , SuspendedState +}; + +} // namespace SymbianAudio + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/audio/symbian/symbianaudiodeviceinfo.cpp b/src/plugins/audio/symbian/symbianaudiodeviceinfo.cpp new file mode 100644 index 0000000..9701dad --- /dev/null +++ b/src/plugins/audio/symbian/symbianaudiodeviceinfo.cpp @@ -0,0 +1,191 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "symbianaudiodeviceinfo.h" +#include "symbianaudioutils.h" + +QT_BEGIN_NAMESPACE + +SymbianAudioDeviceInfo::SymbianAudioDeviceInfo(QByteArray device, + QAudio::Mode mode) + : m_deviceName(device) + , m_mode(mode) + , m_updated(false) +{ + QT_TRAP_THROWING(m_devsound.reset(CMMFDevSound::NewL())); +} + +SymbianAudioDeviceInfo::~SymbianAudioDeviceInfo() +{ + +} + +QAudioFormat SymbianAudioDeviceInfo::preferredFormat() const +{ + QAudioFormat format; + switch (m_mode) { + case QAudio::AudioOutput: + format.setFrequency(44100); + format.setChannels(2); + format.setSampleSize(16); + format.setByteOrder(QAudioFormat::LittleEndian); + format.setSampleType(QAudioFormat::SignedInt); + format.setCodec(QLatin1String("audio/pcm")); + break; + + case QAudio::AudioInput: + format.setFrequency(8000); + format.setChannels(1); + format.setSampleSize(16); + format.setByteOrder(QAudioFormat::LittleEndian); + format.setSampleType(QAudioFormat::SignedInt); + format.setCodec(QLatin1String("audio/pcm")); + break; + + default: + Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid mode"); + } + + if (!isFormatSupported(format)) { + if (m_frequencies.size()) + format.setFrequency(m_frequencies[0]); + if (m_channels.size()) + format.setChannels(m_channels[0]); + if (m_sampleSizes.size()) + format.setSampleSize(m_sampleSizes[0]); + if (m_byteOrders.size()) + format.setByteOrder(m_byteOrders[0]); + if (m_sampleTypes.size()) + format.setSampleType(m_sampleTypes[0]); + } + + return format; +} + +bool SymbianAudioDeviceInfo::isFormatSupported( + const QAudioFormat &format) const +{ + getSupportedFormats(); + const bool supported = + m_codecs.contains(format.codec()) + && m_frequencies.contains(format.frequency()) + && m_channels.contains(format.channels()) + && m_sampleSizes.contains(format.sampleSize()) + && m_byteOrders.contains(format.byteOrder()) + && m_sampleTypes.contains(format.sampleType()); + + return supported; +} + +QAudioFormat SymbianAudioDeviceInfo::nearestFormat(const QAudioFormat &format) const +{ + if (isFormatSupported(format)) + return format; + else + return preferredFormat(); +} + +QString SymbianAudioDeviceInfo::deviceName() const +{ + return m_deviceName; +} + +QStringList SymbianAudioDeviceInfo::codecList() +{ + getSupportedFormats(); + return m_codecs; +} + +QList<int> SymbianAudioDeviceInfo::frequencyList() +{ + getSupportedFormats(); + return m_frequencies; +} + +QList<int> SymbianAudioDeviceInfo::channelsList() +{ + getSupportedFormats(); + return m_channels; +} + +QList<int> SymbianAudioDeviceInfo::sampleSizeList() +{ + getSupportedFormats(); + return m_sampleSizes; +} + +QList<QAudioFormat::Endian> SymbianAudioDeviceInfo::byteOrderList() +{ + getSupportedFormats(); + return m_byteOrders; +} + +QList<QAudioFormat::SampleType> SymbianAudioDeviceInfo::sampleTypeList() +{ + getSupportedFormats(); + return m_sampleTypes; +} + +QList<QByteArray> SymbianAudioDeviceInfo::deviceList(QAudio::Mode mode) +{ + Q_UNUSED(mode) + QList<QByteArray> devices; + devices.append("default"); + return devices; +} + +void SymbianAudioDeviceInfo::getSupportedFormats() const +{ + if (!m_updated) { + QScopedPointer<SymbianAudio::DevSoundCapabilities> caps( + new SymbianAudio::DevSoundCapabilities(*m_devsound, m_mode)); + + SymbianAudio::Utils::capabilitiesNativeToQt(*caps, + m_frequencies, m_channels, m_sampleSizes, + m_byteOrders, m_sampleTypes); + + m_codecs.append(QLatin1String("audio/pcm")); + + m_updated = true; + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/audio/symbian/symbianaudiodeviceinfo.h b/src/plugins/audio/symbian/symbianaudiodeviceinfo.h new file mode 100644 index 0000000..250804d --- /dev/null +++ b/src/plugins/audio/symbian/symbianaudiodeviceinfo.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SYMBIANAUDIODEVICEINFO_H +#define SYMBIANAUDIODEVICEINFO_H + +#include <QtMultimedia/qaudioengine.h> +#include <sounddevice.h> + +QT_BEGIN_NAMESPACE + +class SymbianAudioDeviceInfo + : public QAbstractAudioDeviceInfo +{ + Q_OBJECT + +public: + SymbianAudioDeviceInfo(QByteArray device, QAudio::Mode mode); + ~SymbianAudioDeviceInfo(); + + // QAbstractAudioDeviceInfo + QAudioFormat preferredFormat() const; + bool isFormatSupported(const QAudioFormat &format) const; + QAudioFormat nearestFormat(const QAudioFormat &format) const; + QString deviceName() const; + QStringList codecList(); + QList<int> frequencyList(); + QList<int> channelsList(); + QList<int> sampleSizeList(); + QList<QAudioFormat::Endian> byteOrderList(); + QList<QAudioFormat::SampleType> sampleTypeList(); + QList<QByteArray> deviceList(QAudio::Mode); + +private: + void getSupportedFormats() const; + +private: + QScopedPointer<CMMFDevSound> m_devsound; + + QString m_deviceName; + QAudio::Mode m_mode; + + // Mutable to allow lazy initialization when called from const-qualified + // public functions (isFormatSupported, nearestFormat) + mutable bool m_updated; + mutable QStringList m_codecs; + mutable QList<int> m_frequencies; + mutable QList<int> m_channels; + mutable QList<int> m_sampleSizes; + mutable QList<QAudioFormat::Endian> m_byteOrders; + mutable QList<QAudioFormat::SampleType> m_sampleTypes; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/audio/symbian/symbianaudioinput.cpp b/src/plugins/audio/symbian/symbianaudioinput.cpp new file mode 100644 index 0000000..8200925 --- /dev/null +++ b/src/plugins/audio/symbian/symbianaudioinput.cpp @@ -0,0 +1,595 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "symbianaudioinput.h" +#include "symbianaudioutils.h" + +QT_BEGIN_NAMESPACE + +//----------------------------------------------------------------------------- +// Constants +//----------------------------------------------------------------------------- + +const int PushInterval = 50; // ms + + +//----------------------------------------------------------------------------- +// Private class +//----------------------------------------------------------------------------- + +SymbianAudioInputPrivate::SymbianAudioInputPrivate( + SymbianAudioInput *audioDevice) + : m_audioDevice(audioDevice) +{ + +} + +SymbianAudioInputPrivate::~SymbianAudioInputPrivate() +{ + +} + +qint64 SymbianAudioInputPrivate::readData(char *data, qint64 len) +{ + qint64 totalRead = 0; + + if (m_audioDevice->state() == QAudio::ActiveState || + m_audioDevice->state() == QAudio::IdleState) { + + while (totalRead < len) { + const qint64 read = m_audioDevice->read(data + totalRead, + len - totalRead); + if (read > 0) + totalRead += read; + else + break; + } + } + + return totalRead; +} + +qint64 SymbianAudioInputPrivate::writeData(const char *data, qint64 len) +{ + Q_UNUSED(data) + Q_UNUSED(len) + return 0; +} + +void SymbianAudioInputPrivate::dataReady() +{ + emit readyRead(); +} + + +//----------------------------------------------------------------------------- +// Public functions +//----------------------------------------------------------------------------- + +SymbianAudioInput::SymbianAudioInput(const QByteArray &device, + const QAudioFormat &format) + : m_device(device) + , m_format(format) + , m_clientBufferSize(SymbianAudio::DefaultBufferSize) + , m_notifyInterval(SymbianAudio::DefaultNotifyInterval) + , m_notifyTimer(new QTimer(this)) + , m_error(QAudio::NoError) + , m_internalState(SymbianAudio::ClosedState) + , m_externalState(QAudio::StoppedState) + , m_pullMode(false) + , m_sink(0) + , m_pullTimer(new QTimer(this)) + , m_devSoundBuffer(0) + , m_devSoundBufferSize(0) + , m_totalBytesReady(0) + , m_devSoundBufferPos(0) + , m_totalSamplesRecorded(0) +{ + connect(m_notifyTimer.data(), SIGNAL(timeout()), this, SIGNAL(notify())); + + SymbianAudio::Utils::formatQtToNative(m_format, m_nativeFourCC, + m_nativeFormat); + + m_pullTimer->setInterval(PushInterval); + connect(m_pullTimer.data(), SIGNAL(timeout()), this, SLOT(pullData())); +} + +SymbianAudioInput::~SymbianAudioInput() +{ + close(); +} + +QIODevice* SymbianAudioInput::start(QIODevice *device) +{ + stop(); + + open(); + if (SymbianAudio::ClosedState != m_internalState) { + if (device) { + m_pullMode = true; + m_sink = device; + } else { + m_sink = new SymbianAudioInputPrivate(this); + m_sink->open(QIODevice::ReadOnly | QIODevice::Unbuffered); + } + + m_elapsed.restart(); + } + + return m_sink; +} + +void SymbianAudioInput::stop() +{ + close(); +} + +void SymbianAudioInput::reset() +{ + m_totalSamplesRecorded += getSamplesRecorded(); + m_devSound->Stop(); + startRecording(); +} + +void SymbianAudioInput::suspend() +{ + if (SymbianAudio::ActiveState == m_internalState + || SymbianAudio::IdleState == m_internalState) { + m_notifyTimer->stop(); + m_pullTimer->stop(); + m_devSound->Pause(); + const qint64 samplesRecorded = getSamplesRecorded(); + m_totalSamplesRecorded += samplesRecorded; + + if (m_devSoundBuffer) { + m_devSoundBufferQ.append(m_devSoundBuffer); + m_devSoundBuffer = 0; + } + + setState(SymbianAudio::SuspendedState); + } +} + +void SymbianAudioInput::resume() +{ + if (SymbianAudio::SuspendedState == m_internalState) + startDataTransfer(); +} + +int SymbianAudioInput::bytesReady() const +{ + Q_ASSERT(m_devSoundBufferPos <= m_totalBytesReady); + return m_totalBytesReady - m_devSoundBufferPos; +} + +int SymbianAudioInput::periodSize() const +{ + return bufferSize(); +} + +void SymbianAudioInput::setBufferSize(int value) +{ + // Note that DevSound does not allow its client to specify the buffer size. + // This functionality is available via custom interfaces, but since these + // cannot be guaranteed to work across all DevSound implementations, we + // do not use them here. + // In order to comply with the expected bevahiour of QAudioInput, we store + // the value and return it from bufferSize(), but the underlying DevSound + // buffer size remains unchanged. + if (value > 0) + m_clientBufferSize = value; +} + +int SymbianAudioInput::bufferSize() const +{ + return m_devSoundBufferSize ? m_devSoundBufferSize : m_clientBufferSize; +} + +void SymbianAudioInput::setNotifyInterval(int ms) +{ + if (ms > 0) { + const int oldNotifyInterval = m_notifyInterval; + m_notifyInterval = ms; + if (m_notifyTimer->isActive() && ms != oldNotifyInterval) + m_notifyTimer->start(m_notifyInterval); + } +} + +int SymbianAudioInput::notifyInterval() const +{ + return m_notifyInterval; +} + +qint64 SymbianAudioInput::processedUSecs() const +{ + int samplesPlayed = 0; + if (m_devSound && SymbianAudio::SuspendedState != m_internalState) + samplesPlayed = getSamplesRecorded(); + + // Protect against division by zero + Q_ASSERT_X(m_format.frequency() > 0, Q_FUNC_INFO, "Invalid frequency"); + + const qint64 result = qint64(1000000) * + (samplesPlayed + m_totalSamplesRecorded) + / m_format.frequency(); + + return result; +} + +qint64 SymbianAudioInput::elapsedUSecs() const +{ + const qint64 result = (QAudio::StoppedState == state()) ? + 0 : m_elapsed.elapsed() * 1000; + return result; +} + +QAudio::Error SymbianAudioInput::error() const +{ + return m_error; +} + +QAudio::State SymbianAudioInput::state() const +{ + return m_externalState; +} + +QAudioFormat SymbianAudioInput::format() const +{ + return m_format; +} + +//----------------------------------------------------------------------------- +// MDevSoundObserver implementation +//----------------------------------------------------------------------------- + +void SymbianAudioInput::InitializeComplete(TInt aError) +{ + Q_ASSERT_X(SymbianAudio::InitializingState == m_internalState, + Q_FUNC_INFO, "Invalid state"); + + if (KErrNone == aError) + startRecording(); +} + +void SymbianAudioInput::ToneFinished(TInt aError) +{ + Q_UNUSED(aError) + // This class doesn't use DevSound's tone playback functions, so should + // never receive this callback. + Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected callback"); +} + +void SymbianAudioInput::BufferToBeFilled(CMMFBuffer *aBuffer) +{ + Q_UNUSED(aBuffer) + // This class doesn't use DevSound in play mode, so should never receive + // this callback. + Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected callback"); +} + +void SymbianAudioInput::PlayError(TInt aError) +{ + Q_UNUSED(aError) + // This class doesn't use DevSound in play mode, so should never receive + // this callback. + Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected callback"); +} + +void SymbianAudioInput::BufferToBeEmptied(CMMFBuffer *aBuffer) +{ + // Following receipt of this callback, DevSound should not provide another + // buffer until we have returned the current one. + Q_ASSERT_X(!m_devSoundBuffer, Q_FUNC_INFO, "Buffer already held"); + + CMMFDataBuffer *const buffer = static_cast<CMMFDataBuffer*>(aBuffer); + + if (!m_devSoundBufferSize) + m_devSoundBufferSize = buffer->Data().MaxLength(); + + m_totalBytesReady += buffer->Data().Length(); + + if (SymbianAudio::SuspendedState == m_internalState) { + m_devSoundBufferQ.append(buffer); + } else { + // Will be returned to DevSound by bufferEmptied(). + m_devSoundBuffer = buffer; + m_devSoundBufferPos = 0; + + if (bytesReady() && !m_pullMode) + pushData(); + } +} + +void SymbianAudioInput::RecordError(TInt aError) +{ + Q_UNUSED(aError) + setError(QAudio::IOError); +} + +void SymbianAudioInput::ConvertError(TInt aError) +{ + Q_UNUSED(aError) + // This class doesn't use DevSound's format conversion functions, so + // should never receive this callback. + Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected callback"); +} + +void SymbianAudioInput::DeviceMessage(TUid aMessageType, const TDesC8 &aMsg) +{ + Q_UNUSED(aMessageType) + Q_UNUSED(aMsg) + // Ignore this callback. +} + +//----------------------------------------------------------------------------- +// Private functions +//----------------------------------------------------------------------------- + +void SymbianAudioInput::open() +{ + Q_ASSERT_X(SymbianAudio::ClosedState == m_internalState, + Q_FUNC_INFO, "DevSound already opened"); + + QT_TRAP_THROWING( m_devSound.reset(CMMFDevSound::NewL()) ) + + QScopedPointer<SymbianAudio::DevSoundCapabilities> caps( + new SymbianAudio::DevSoundCapabilities(*m_devSound, QAudio::AudioInput)); + + int err = SymbianAudio::Utils::isFormatSupported(m_format, *caps) ? + KErrNone : KErrNotSupported; + + if (KErrNone == err) { + setState(SymbianAudio::InitializingState); + TRAP(err, m_devSound->InitializeL(*this, m_nativeFourCC, + EMMFStateRecording)); + } + + if (KErrNone != err) { + setError(QAudio::OpenError); + m_devSound.reset(); + } +} + +void SymbianAudioInput::startRecording() +{ + const int samplesRecorded = m_devSound->SamplesRecorded(); + Q_ASSERT(samplesRecorded == 0); + + TRAPD(err, startDevSoundL()); + if (KErrNone == err) { + startDataTransfer(); + } else { + setError(QAudio::OpenError); + close(); + } +} + +void SymbianAudioInput::startDevSoundL() +{ + TMMFCapabilities nativeFormat = m_devSound->Config(); + m_nativeFormat.iBufferSize = nativeFormat.iBufferSize; + m_devSound->SetConfigL(m_nativeFormat); + m_devSound->RecordInitL(); +} + +void SymbianAudioInput::startDataTransfer() +{ + m_notifyTimer->start(m_notifyInterval); + + if (m_pullMode) + m_pullTimer->start(); + + if (bytesReady()) { + setState(SymbianAudio::ActiveState); + if (!m_pullMode) + pushData(); + } else { + if (SymbianAudio::SuspendedState == m_internalState) + setState(SymbianAudio::ActiveState); + else + setState(SymbianAudio::IdleState); + } +} + +CMMFDataBuffer* SymbianAudioInput::currentBuffer() const +{ + CMMFDataBuffer *result = m_devSoundBuffer; + if (!result && !m_devSoundBufferQ.empty()) + result = m_devSoundBufferQ.front(); + return result; +} + +void SymbianAudioInput::pushData() +{ + Q_ASSERT_X(bytesReady(), Q_FUNC_INFO, "No data available"); + Q_ASSERT_X(!m_pullMode, Q_FUNC_INFO, "pushData called when in pull mode"); + qobject_cast<SymbianAudioInputPrivate *>(m_sink)->dataReady(); +} + +qint64 SymbianAudioInput::read(char *data, qint64 len) +{ + // SymbianAudioInputPrivate is ready to read data + + Q_ASSERT_X(!m_pullMode, Q_FUNC_INFO, + "read called when in pull mode"); + + qint64 bytesRead = 0; + + CMMFDataBuffer *buffer = 0; + while ((buffer = currentBuffer()) && (bytesRead < len)) { + if (SymbianAudio::IdleState == m_internalState) + setState(SymbianAudio::ActiveState); + + TDesC8 &inputBuffer = buffer->Data(); + + const qint64 inputBytes = bytesReady(); + const qint64 outputBytes = len - bytesRead; + const qint64 copyBytes = outputBytes < inputBytes ? + outputBytes : inputBytes; + + memcpy(data, inputBuffer.Ptr() + m_devSoundBufferPos, copyBytes); + + m_devSoundBufferPos += copyBytes; + data += copyBytes; + bytesRead += copyBytes; + + if (!bytesReady()) + bufferEmptied(); + } + + return bytesRead; +} + +void SymbianAudioInput::pullData() +{ + Q_ASSERT_X(m_pullMode, Q_FUNC_INFO, + "pullData called when in push mode"); + + CMMFDataBuffer *buffer = 0; + while (buffer = currentBuffer()) { + if (SymbianAudio::IdleState == m_internalState) + setState(SymbianAudio::ActiveState); + + TDesC8 &inputBuffer = buffer->Data(); + + const qint64 inputBytes = bytesReady(); + const qint64 bytesPushed = m_sink->write( + (char*)inputBuffer.Ptr() + m_devSoundBufferPos, inputBytes); + + m_devSoundBufferPos += bytesPushed; + + if (!bytesReady()) + bufferEmptied(); + + if (!bytesPushed) + break; + } +} + +void SymbianAudioInput::bufferEmptied() +{ + m_devSoundBufferPos = 0; + + if (m_devSoundBuffer) { + m_totalBytesReady -= m_devSoundBuffer->Data().Length(); + m_devSoundBuffer = 0; + m_devSound->RecordData(); + } else { + Q_ASSERT(!m_devSoundBufferQ.empty()); + m_totalBytesReady -= m_devSoundBufferQ.front()->Data().Length(); + m_devSoundBufferQ.erase(m_devSoundBufferQ.begin()); + + // If the queue has been emptied, resume transfer from the hardware + if (m_devSoundBufferQ.empty()) + m_devSound->RecordInitL(); + } + + Q_ASSERT(m_totalBytesReady >= 0); +} + +void SymbianAudioInput::close() +{ + m_notifyTimer->stop(); + m_pullTimer->stop(); + + m_error = QAudio::NoError; + + if (m_devSound) + m_devSound->Stop(); + m_devSound.reset(); + m_devSoundBuffer = 0; + m_devSoundBufferSize = 0; + m_totalBytesReady = 0; + + if (!m_pullMode) // m_sink is owned + delete m_sink; + m_pullMode = false; + m_sink = 0; + + m_devSoundBufferQ.clear(); + m_devSoundBufferPos = 0; + m_totalSamplesRecorded = 0; + + setState(SymbianAudio::ClosedState); +} + +qint64 SymbianAudioInput::getSamplesRecorded() const +{ + qint64 result = 0; + if (m_devSound) + result = qint64(m_devSound->SamplesRecorded()); + return result; +} + +void SymbianAudioInput::setError(QAudio::Error error) +{ + m_error = error; + + // Although no state transition actually occurs here, a stateChanged event + // must be emitted to inform the client that the call to start() was + // unsuccessful. + if (QAudio::OpenError == error) + emit stateChanged(QAudio::StoppedState); + + // Close the DevSound instance. This causes a transition to StoppedState. + // This must be done asynchronously in case the current function was called + // from a DevSound event handler, in which case deleting the DevSound + // instance may cause an exception. + QMetaObject::invokeMethod(this, "close", Qt::QueuedConnection); +} + +void SymbianAudioInput::setState(SymbianAudio::State newInternalState) +{ + const QAudio::State oldExternalState = m_externalState; + m_internalState = newInternalState; + m_externalState = SymbianAudio::Utils::stateNativeToQt( + m_internalState, initializingState()); + + if (m_externalState != oldExternalState) + emit stateChanged(m_externalState); +} + +QAudio::State SymbianAudioInput::initializingState() const +{ + return QAudio::IdleState; +} + +QT_END_NAMESPACE diff --git a/src/plugins/audio/symbian/symbianaudioinput.h b/src/plugins/audio/symbian/symbianaudioinput.h new file mode 100644 index 0000000..34b7d38 --- /dev/null +++ b/src/plugins/audio/symbian/symbianaudioinput.h @@ -0,0 +1,177 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SYMBIANAUDIOINPUT_H +#define SYMBIANAUDIOINPUT_H + +#include <QtMultimedia/qaudioengine.h> +#include <QTime> +#include <QTimer> +#include <sounddevice.h> +#include "symbianaudio.h" + +QT_BEGIN_NAMESPACE + +class SymbianAudioInput; + +class SymbianAudioInputPrivate : public QIODevice +{ + friend class SymbianAudioInput; + Q_OBJECT +public: + SymbianAudioInputPrivate(SymbianAudioInput *audio); + ~SymbianAudioInputPrivate(); + + qint64 readData(char *data, qint64 len); + qint64 writeData(const char *data, qint64 len); + + void dataReady(); + +private: + SymbianAudioInput *const m_audioDevice; +}; + +class SymbianAudioInput + : public QAbstractAudioInput + , public MDevSoundObserver +{ + friend class SymbianAudioInputPrivate; + Q_OBJECT +public: + SymbianAudioInput(const QByteArray &device, + const QAudioFormat &audioFormat); + ~SymbianAudioInput(); + + // QAbstractAudioInput + QIODevice* start(QIODevice *device = 0); + void stop(); + void reset(); + void suspend(); + void resume(); + int bytesReady() const; + int periodSize() const; + void setBufferSize(int value); + int bufferSize() const; + void setNotifyInterval(int milliSeconds); + int notifyInterval() const; + qint64 processedUSecs() const; + qint64 elapsedUSecs() const; + QAudio::Error error() const; + QAudio::State state() const; + QAudioFormat format() const; + + // MDevSoundObserver + void InitializeComplete(TInt aError); + void ToneFinished(TInt aError); + void BufferToBeFilled(CMMFBuffer *aBuffer); + void PlayError(TInt aError); + void BufferToBeEmptied(CMMFBuffer *aBuffer); + void RecordError(TInt aError); + void ConvertError(TInt aError); + void DeviceMessage(TUid aMessageType, const TDesC8 &aMsg); + +private slots: + void pullData(); + +private: + void open(); + void startRecording(); + void startDevSoundL(); + void startDataTransfer(); + CMMFDataBuffer* currentBuffer() const; + void pushData(); + qint64 read(char *data, qint64 len); + void bufferEmptied(); + Q_INVOKABLE void close(); + + qint64 getSamplesRecorded() const; + + void setError(QAudio::Error error); + void setState(SymbianAudio::State state); + + QAudio::State initializingState() const; + +private: + const QByteArray m_device; + const QAudioFormat m_format; + + int m_clientBufferSize; + int m_notifyInterval; + QScopedPointer<QTimer> m_notifyTimer; + QTime m_elapsed; + QAudio::Error m_error; + + SymbianAudio::State m_internalState; + QAudio::State m_externalState; + + bool m_pullMode; + QIODevice *m_sink; + + QScopedPointer<QTimer> m_pullTimer; + + QScopedPointer<CMMFDevSound> m_devSound; + TUint32 m_nativeFourCC; + TMMFCapabilities m_nativeFormat; + + // Latest buffer provided by DevSound, to be empied of data. + CMMFDataBuffer *m_devSoundBuffer; + + int m_devSoundBufferSize; + + // Total amount of data in buffers provided by DevSound + int m_totalBytesReady; + + // Queue of buffers returned after call to CMMFDevSound::Pause(). + QList<CMMFDataBuffer *> m_devSoundBufferQ; + + // Current read position within m_devSoundBuffer + qint64 m_devSoundBufferPos; + + // Samples recorded up to the last call to suspend(). It is necessary + // to cache this because suspend() is implemented using + // CMMFDevSound::Stop(), which resets DevSound's SamplesRecorded() counter. + quint32 m_totalSamplesRecorded; + +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/audio/symbian/symbianaudiooutput.cpp b/src/plugins/audio/symbian/symbianaudiooutput.cpp new file mode 100644 index 0000000..385e169 --- /dev/null +++ b/src/plugins/audio/symbian/symbianaudiooutput.cpp @@ -0,0 +1,697 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "symbianaudiooutput.h" +#include "symbianaudioutils.h" + +QT_BEGIN_NAMESPACE + +//----------------------------------------------------------------------------- +// Constants +//----------------------------------------------------------------------------- + +const int UnderflowTimerInterval = 50; // ms + + +//----------------------------------------------------------------------------- +// Private class +//----------------------------------------------------------------------------- + +SymbianAudioOutputPrivate::SymbianAudioOutputPrivate( + SymbianAudioOutput *audioDevice) + : m_audioDevice(audioDevice) +{ + +} + +SymbianAudioOutputPrivate::~SymbianAudioOutputPrivate() +{ + +} + +qint64 SymbianAudioOutputPrivate::readData(char *data, qint64 len) +{ + Q_UNUSED(data) + Q_UNUSED(len) + return 0; +} + +qint64 SymbianAudioOutputPrivate::writeData(const char *data, qint64 len) +{ + qint64 totalWritten = 0; + + if (m_audioDevice->state() == QAudio::ActiveState || + m_audioDevice->state() == QAudio::IdleState) { + + while (totalWritten < len) { + const qint64 written = m_audioDevice->pushData(data + totalWritten, + len - totalWritten); + if (written > 0) + totalWritten += written; + else + break; + } + } + + return totalWritten; +} + + +//----------------------------------------------------------------------------- +// Public functions +//----------------------------------------------------------------------------- + +SymbianAudioOutput::SymbianAudioOutput(const QByteArray &device, + const QAudioFormat &format) + : m_device(device) + , m_format(format) + , m_clientBufferSize(SymbianAudio::DefaultBufferSize) + , m_notifyInterval(SymbianAudio::DefaultNotifyInterval) + , m_notifyTimer(new QTimer(this)) + , m_error(QAudio::NoError) + , m_internalState(SymbianAudio::ClosedState) + , m_externalState(QAudio::StoppedState) + , m_pullMode(false) + , m_source(0) + , m_devSoundBuffer(0) + , m_devSoundBufferSize(0) + , m_bytesWritten(0) + , m_pushDataReady(false) + , m_bytesPadding(0) + , m_underflow(false) + , m_lastBuffer(false) + , m_underflowTimer(new QTimer(this)) + , m_samplesPlayed(0) + , m_totalSamplesPlayed(0) +{ + connect(m_notifyTimer.data(), SIGNAL(timeout()), this, SIGNAL(notify())); + + SymbianAudio::Utils::formatQtToNative(m_format, m_nativeFourCC, + m_nativeFormat); + + m_underflowTimer->setInterval(UnderflowTimerInterval); + connect(m_underflowTimer.data(), SIGNAL(timeout()), this, + SLOT(underflowTimerExpired())); +} + +SymbianAudioOutput::~SymbianAudioOutput() +{ + close(); +} + +QIODevice* SymbianAudioOutput::start(QIODevice *device) +{ + stop(); + + // We have to set these before the call to open() because of the + // logic in initializingState() + if (device) { + m_pullMode = true; + m_source = device; + } + + open(); + + if (SymbianAudio::ClosedState != m_internalState) { + if (device) { + connect(m_source, SIGNAL(readyRead()), this, SLOT(dataReady())); + } else { + m_source = new SymbianAudioOutputPrivate(this); + m_source->open(QIODevice::WriteOnly | QIODevice::Unbuffered); + } + + m_elapsed.restart(); + } + + return m_source; +} + +void SymbianAudioOutput::stop() +{ + close(); +} + +void SymbianAudioOutput::reset() +{ + m_totalSamplesPlayed += getSamplesPlayed(); + m_devSound->Stop(); + m_bytesPadding = 0; + startPlayback(); +} + +void SymbianAudioOutput::suspend() +{ + if (SymbianAudio::ActiveState == m_internalState + || SymbianAudio::IdleState == m_internalState) { + m_notifyTimer->stop(); + m_underflowTimer->stop(); + + const qint64 samplesWritten = SymbianAudio::Utils::bytesToSamples( + m_format, m_bytesWritten); + m_bytesWritten = 0; + + const qint64 samplesPlayed = getSamplesPlayed(); + + // CMMFDevSound::Pause() is not guaranteed to work correctly in all + // implementations, for play-mode DevSound sessions. We therefore + // have to implement suspend() by calling CMMFDevSound::Stop(). + // Because this causes buffered data to be dropped, we replace the + // lost data with silence following a call to resume(), in order to + // ensure that processedUSecs() returns the correct value. + m_devSound->Stop(); + m_totalSamplesPlayed += samplesPlayed; + + // Calculate the amount of data dropped + const qint64 paddingSamples = samplesWritten - samplesPlayed; + m_bytesPadding = SymbianAudio::Utils::samplesToBytes(m_format, + paddingSamples); + + setState(SymbianAudio::SuspendedState); + } +} + +void SymbianAudioOutput::resume() +{ + if (SymbianAudio::SuspendedState == m_internalState) + startPlayback(); +} + +int SymbianAudioOutput::bytesFree() const +{ + int result = 0; + if (m_devSoundBuffer) { + const TDes8 &outputBuffer = m_devSoundBuffer->Data(); + result = outputBuffer.MaxLength() - outputBuffer.Length(); + } + return result; +} + +int SymbianAudioOutput::periodSize() const +{ + return bufferSize(); +} + +void SymbianAudioOutput::setBufferSize(int value) +{ + // Note that DevSound does not allow its client to specify the buffer size. + // This functionality is available via custom interfaces, but since these + // cannot be guaranteed to work across all DevSound implementations, we + // do not use them here. + // In order to comply with the expected bevahiour of QAudioOutput, we store + // the value and return it from bufferSize(), but the underlying DevSound + // buffer size remains unchanged. + if (value > 0) + m_clientBufferSize = value; +} + +int SymbianAudioOutput::bufferSize() const +{ + return m_devSoundBufferSize ? m_devSoundBufferSize : m_clientBufferSize; +} + +void SymbianAudioOutput::setNotifyInterval(int ms) +{ + if (ms > 0) { + const int oldNotifyInterval = m_notifyInterval; + m_notifyInterval = ms; + if (m_notifyTimer->isActive() && ms != oldNotifyInterval) + m_notifyTimer->start(m_notifyInterval); + } +} + +int SymbianAudioOutput::notifyInterval() const +{ + return m_notifyInterval; +} + +qint64 SymbianAudioOutput::processedUSecs() const +{ + int samplesPlayed = 0; + if (m_devSound && SymbianAudio::SuspendedState != m_internalState) + samplesPlayed = getSamplesPlayed(); + + // Protect against division by zero + Q_ASSERT_X(m_format.frequency() > 0, Q_FUNC_INFO, "Invalid frequency"); + + const qint64 result = qint64(1000000) * + (samplesPlayed + m_totalSamplesPlayed) + / m_format.frequency(); + + return result; +} + +qint64 SymbianAudioOutput::elapsedUSecs() const +{ + const qint64 result = (QAudio::StoppedState == state()) ? + 0 : m_elapsed.elapsed() * 1000; + return result; +} + +QAudio::Error SymbianAudioOutput::error() const +{ + return m_error; +} + +QAudio::State SymbianAudioOutput::state() const +{ + return m_externalState; +} + +QAudioFormat SymbianAudioOutput::format() const +{ + return m_format; +} + +//----------------------------------------------------------------------------- +// MDevSoundObserver implementation +//----------------------------------------------------------------------------- + +void SymbianAudioOutput::InitializeComplete(TInt aError) +{ + Q_ASSERT_X(SymbianAudio::InitializingState == m_internalState, + Q_FUNC_INFO, "Invalid state"); + + if (KErrNone == aError) + startPlayback(); +} + +void SymbianAudioOutput::ToneFinished(TInt aError) +{ + Q_UNUSED(aError) + // This class doesn't use DevSound's tone playback functions, so should + // never receive this callback. + Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected callback"); +} + +void SymbianAudioOutput::BufferToBeFilled(CMMFBuffer *aBuffer) +{ + // Following receipt of this callback, DevSound should not provide another + // buffer until we have returned the current one. + Q_ASSERT_X(!m_devSoundBuffer, Q_FUNC_INFO, "Buffer already held"); + + // Will be returned to DevSound by bufferFilled(). + m_devSoundBuffer = static_cast<CMMFDataBuffer*>(aBuffer); + + if (!m_devSoundBufferSize) + m_devSoundBufferSize = m_devSoundBuffer->Data().MaxLength(); + + writePaddingData(); + + if (m_pullMode && isDataReady() && !m_bytesPadding) + pullData(); +} + +void SymbianAudioOutput::PlayError(TInt aError) +{ + switch (aError) { + case KErrUnderflow: + m_underflow = true; + if (m_pullMode && !m_lastBuffer) + setError(QAudio::UnderrunError); + else + setState(SymbianAudio::IdleState); + break; + default: + setError(QAudio::IOError); + break; + } +} + +void SymbianAudioOutput::BufferToBeEmptied(CMMFBuffer *aBuffer) +{ + Q_UNUSED(aBuffer) + // This class doesn't use DevSound in record mode, so should never receive + // this callback. + Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected callback"); +} + +void SymbianAudioOutput::RecordError(TInt aError) +{ + Q_UNUSED(aError) + // This class doesn't use DevSound in record mode, so should never receive + // this callback. + Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected callback"); +} + +void SymbianAudioOutput::ConvertError(TInt aError) +{ + Q_UNUSED(aError) + // This class doesn't use DevSound's format conversion functions, so + // should never receive this callback. + Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected callback"); +} + +void SymbianAudioOutput::DeviceMessage(TUid aMessageType, const TDesC8 &aMsg) +{ + Q_UNUSED(aMessageType) + Q_UNUSED(aMsg) + // Ignore this callback. +} + +//----------------------------------------------------------------------------- +// Private functions +//----------------------------------------------------------------------------- + +void SymbianAudioOutput::dataReady() +{ + // Client-provided QIODevice has data ready to read. + + Q_ASSERT_X(m_source->bytesAvailable(), Q_FUNC_INFO, + "readyRead signal received, but no data available"); + + if (!m_bytesPadding) + pullData(); +} + +void SymbianAudioOutput::underflowTimerExpired() +{ + const TInt samplesPlayed = getSamplesPlayed(); + if (m_samplesPlayed && (samplesPlayed == m_samplesPlayed)) { + setError(QAudio::UnderrunError); + } else { + m_samplesPlayed = samplesPlayed; + m_underflowTimer->start(); + } +} + +void SymbianAudioOutput::open() +{ + Q_ASSERT_X(SymbianAudio::ClosedState == m_internalState, + Q_FUNC_INFO, "DevSound already opened"); + + QT_TRAP_THROWING( m_devSound.reset(CMMFDevSound::NewL()) ) + + QScopedPointer<SymbianAudio::DevSoundCapabilities> caps( + new SymbianAudio::DevSoundCapabilities(*m_devSound, + QAudio::AudioOutput)); + + int err = SymbianAudio::Utils::isFormatSupported(m_format, *caps) ? + KErrNone : KErrNotSupported; + + if (KErrNone == err) { + setState(SymbianAudio::InitializingState); + TRAP(err, m_devSound->InitializeL(*this, m_nativeFourCC, + EMMFStatePlaying)); + } + + if (KErrNone != err) { + setError(QAudio::OpenError); + m_devSound.reset(); + } +} + +void SymbianAudioOutput::startPlayback() +{ + TRAPD(err, startDevSoundL()); + if (KErrNone == err) { + if (isDataReady()) + setState(SymbianAudio::ActiveState); + else + setState(SymbianAudio::IdleState); + + m_notifyTimer->start(m_notifyInterval); + m_underflow = false; + + Q_ASSERT(m_devSound->SamplesPlayed() == 0); + + writePaddingData(); + + if (m_pullMode && m_source->bytesAvailable() && !m_bytesPadding) + dataReady(); + } else { + setError(QAudio::OpenError); + close(); + } +} + +void SymbianAudioOutput::startDevSoundL() +{ + TMMFCapabilities nativeFormat = m_devSound->Config(); + m_nativeFormat.iBufferSize = nativeFormat.iBufferSize; + m_devSound->SetConfigL(m_nativeFormat); + m_devSound->PlayInitL(); +} + +void SymbianAudioOutput::writePaddingData() +{ + // See comments in suspend() + + while (m_devSoundBuffer && m_bytesPadding) { + if (SymbianAudio::IdleState == m_internalState) + setState(SymbianAudio::ActiveState); + + TDes8 &outputBuffer = m_devSoundBuffer->Data(); + const qint64 outputBytes = bytesFree(); + const qint64 paddingBytes = outputBytes < m_bytesPadding ? + outputBytes : m_bytesPadding; + unsigned char *ptr = const_cast<unsigned char*>(outputBuffer.Ptr()); + Mem::FillZ(ptr, paddingBytes); + outputBuffer.SetLength(outputBuffer.Length() + paddingBytes); + m_bytesPadding -= paddingBytes; + + if (m_pullMode && m_source->atEnd()) + lastBufferFilled(); + if (paddingBytes == outputBytes) + bufferFilled(); + } +} + +qint64 SymbianAudioOutput::pushData(const char *data, qint64 len) +{ + // Data has been written to SymbianAudioOutputPrivate + + Q_ASSERT_X(!m_pullMode, Q_FUNC_INFO, + "pushData called when in pull mode"); + + const unsigned char *const inputPtr = + reinterpret_cast<const unsigned char*>(data); + qint64 bytesWritten = 0; + + if (SymbianAudio::IdleState == m_internalState) + setState(SymbianAudio::ActiveState); + + while (m_devSoundBuffer && (bytesWritten < len)) { + // writePaddingData() is called from BufferToBeFilled(), so we should + // never have any padding data left at this point. + Q_ASSERT_X(0 == m_bytesPadding, Q_FUNC_INFO, + "Padding bytes remaining in pushData"); + + TDes8 &outputBuffer = m_devSoundBuffer->Data(); + + const qint64 outputBytes = bytesFree(); + const qint64 inputBytes = len - bytesWritten; + const qint64 copyBytes = outputBytes < inputBytes ? + outputBytes : inputBytes; + + outputBuffer.Append(inputPtr + bytesWritten, copyBytes); + bytesWritten += copyBytes; + + bufferFilled(); + } + + m_pushDataReady = (bytesWritten < len); + + // If DevSound is still initializing (m_internalState == InitializingState), + // we cannot transition m_internalState to ActiveState, but we must emit + // an (external) state change from IdleState to ActiveState. The following + // call triggers this signal. + setState(m_internalState); + + return bytesWritten; +} + +void SymbianAudioOutput::pullData() +{ + Q_ASSERT_X(m_pullMode, Q_FUNC_INFO, + "pullData called when in push mode"); + + if (m_bytesPadding) + m_bytesPadding = 1; + + // writePaddingData() is called by BufferToBeFilled() before pullData(), + // so we should never have any padding data left at this point. + Q_ASSERT_X(0 == m_bytesPadding, Q_FUNC_INFO, + "Padding bytes remaining in pullData"); + + qint64 inputBytes = m_source->bytesAvailable(); + while (m_devSoundBuffer && inputBytes) { + if (SymbianAudio::IdleState == m_internalState) + setState(SymbianAudio::ActiveState); + + TDes8 &outputBuffer = m_devSoundBuffer->Data(); + + const qint64 outputBytes = bytesFree(); + const qint64 copyBytes = outputBytes < inputBytes ? + outputBytes : inputBytes; + + char *outputPtr = (char*)(outputBuffer.Ptr() + outputBuffer.Length()); + const qint64 bytesCopied = m_source->read(outputPtr, copyBytes); + Q_ASSERT(bytesCopied == copyBytes); + outputBuffer.SetLength(outputBuffer.Length() + bytesCopied); + inputBytes -= bytesCopied; + + if (m_source->atEnd()) + lastBufferFilled(); + else if (copyBytes == outputBytes) + bufferFilled(); + } +} + +void SymbianAudioOutput::bufferFilled() +{ + Q_ASSERT_X(m_devSoundBuffer, Q_FUNC_INFO, "No buffer to return"); + + const TDes8 &outputBuffer = m_devSoundBuffer->Data(); + m_bytesWritten += outputBuffer.Length(); + + m_devSoundBuffer = 0; + + m_samplesPlayed = getSamplesPlayed(); + m_underflowTimer->start(); + + if (QAudio::UnderrunError == m_error) + m_error = QAudio::NoError; + + m_devSound->PlayData(); +} + +void SymbianAudioOutput::lastBufferFilled() +{ + Q_ASSERT_X(m_devSoundBuffer, Q_FUNC_INFO, "No buffer to fill"); + Q_ASSERT_X(!m_lastBuffer, Q_FUNC_INFO, "Last buffer already sent"); + m_lastBuffer = true; + m_devSoundBuffer->SetLastBuffer(ETrue); + bufferFilled(); +} + +void SymbianAudioOutput::close() +{ + m_notifyTimer->stop(); + m_underflowTimer->stop(); + + m_error = QAudio::NoError; + + if (m_devSound) + m_devSound->Stop(); + m_devSound.reset(); + m_devSoundBuffer = 0; + m_devSoundBufferSize = 0; + + if (!m_pullMode) // m_source is owned + delete m_source; + m_pullMode = false; + m_source = 0; + + m_bytesWritten = 0; + m_pushDataReady = false; + m_bytesPadding = 0; + m_underflow = false; + m_lastBuffer = false; + m_samplesPlayed = 0; + m_totalSamplesPlayed = 0; + + setState(SymbianAudio::ClosedState); +} + +qint64 SymbianAudioOutput::getSamplesPlayed() const +{ + qint64 result = 0; + if (m_devSound) { + const qint64 samplesWritten = SymbianAudio::Utils::bytesToSamples( + m_format, m_bytesWritten); + + if (m_underflow) { + result = samplesWritten; + } else { + // This is necessary because some DevSound implementations report + // that they have played more data than has actually been provided to them + // by the client. + const qint64 devSoundSamplesPlayed(m_devSound->SamplesPlayed()); + result = qMin(devSoundSamplesPlayed, samplesWritten); + } + } + return result; +} + +void SymbianAudioOutput::setError(QAudio::Error error) +{ + m_error = error; + + // Although no state transition actually occurs here, a stateChanged event + // must be emitted to inform the client that the call to start() was + // unsuccessful. + if (QAudio::OpenError == error) + emit stateChanged(QAudio::StoppedState); + + if (QAudio::UnderrunError == error) + setState(SymbianAudio::IdleState); + else + // Close the DevSound instance. This causes a transition to + // StoppedState. This must be done asynchronously in case the + // current function was called from a DevSound event handler, in which + // case deleting the DevSound instance may cause an exception. + QMetaObject::invokeMethod(this, "close", Qt::QueuedConnection); +} + +void SymbianAudioOutput::setState(SymbianAudio::State newInternalState) +{ + const QAudio::State oldExternalState = m_externalState; + m_internalState = newInternalState; + m_externalState = SymbianAudio::Utils::stateNativeToQt( + m_internalState, initializingState()); + + if (m_externalState != oldExternalState) + emit stateChanged(m_externalState); +} + +bool SymbianAudioOutput::isDataReady() const +{ + return (m_source && m_source->bytesAvailable()) + || m_bytesPadding + || m_pushDataReady; +} + +QAudio::State SymbianAudioOutput::initializingState() const +{ + return isDataReady() ? QAudio::ActiveState : QAudio::IdleState; +} + +QT_END_NAMESPACE diff --git a/src/plugins/audio/symbian/symbianaudiooutput.h b/src/plugins/audio/symbian/symbianaudiooutput.h new file mode 100644 index 0000000..8994e46 --- /dev/null +++ b/src/plugins/audio/symbian/symbianaudiooutput.h @@ -0,0 +1,199 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SYMBIANAUDIOOUTPUT_H +#define SYMBIANAUDIOOUTPUT_H + +#include <QtMultimedia/qaudioengine.h> +#include <QTime> +#include <QTimer> +#include <sounddevice.h> +#include "symbianaudio.h" + +QT_BEGIN_NAMESPACE + +class SymbianAudioOutput; + +class SymbianAudioOutputPrivate : public QIODevice +{ + friend class SymbianAudioOutput; + Q_OBJECT +public: + SymbianAudioOutputPrivate(SymbianAudioOutput *audio); + ~SymbianAudioOutputPrivate(); + + qint64 readData(char *data, qint64 len); + qint64 writeData(const char *data, qint64 len); + +private: + SymbianAudioOutput *const m_audioDevice; +}; + +class SymbianAudioOutput + : public QAbstractAudioOutput + , public MDevSoundObserver +{ + friend class SymbianAudioOutputPrivate; + Q_OBJECT +public: + SymbianAudioOutput(const QByteArray &device, + const QAudioFormat &audioFormat); + ~SymbianAudioOutput(); + + // QAbstractAudioOutput + QIODevice* start(QIODevice *device = 0); + void stop(); + void reset(); + void suspend(); + void resume(); + int bytesFree() const; + int periodSize() const; + void setBufferSize(int value); + int bufferSize() const; + void setNotifyInterval(int milliSeconds); + int notifyInterval() const; + qint64 processedUSecs() const; + qint64 elapsedUSecs() const; + QAudio::Error error() const; + QAudio::State state() const; + QAudioFormat format() const; + + // MDevSoundObserver + void InitializeComplete(TInt aError); + void ToneFinished(TInt aError); + void BufferToBeFilled(CMMFBuffer *aBuffer); + void PlayError(TInt aError); + void BufferToBeEmptied(CMMFBuffer *aBuffer); + void RecordError(TInt aError); + void ConvertError(TInt aError); + void DeviceMessage(TUid aMessageType, const TDesC8 &aMsg); + +private slots: + void dataReady(); + void underflowTimerExpired(); + +private: + void open(); + void startPlayback(); + void startDevSoundL(); + void writePaddingData(); + qint64 pushData(const char *data, qint64 len); + void pullData(); + void bufferFilled(); + void lastBufferFilled(); + Q_INVOKABLE void close(); + + qint64 getSamplesPlayed() const; + + void setError(QAudio::Error error); + void setState(SymbianAudio::State state); + + bool isDataReady() const; + QAudio::State initializingState() const; + +private: + const QByteArray m_device; + const QAudioFormat m_format; + + int m_clientBufferSize; + int m_notifyInterval; + QScopedPointer<QTimer> m_notifyTimer; + QTime m_elapsed; + QAudio::Error m_error; + + SymbianAudio::State m_internalState; + QAudio::State m_externalState; + + bool m_pullMode; + QIODevice *m_source; + + QScopedPointer<CMMFDevSound> m_devSound; + TUint32 m_nativeFourCC; + TMMFCapabilities m_nativeFormat; + + // Buffer provided by DevSound, to be filled with data. + CMMFDataBuffer *m_devSoundBuffer; + + int m_devSoundBufferSize; + + // Number of bytes transferred from QIODevice to QAudioOutput. It is + // necessary to count this because data is dropped when suspend() is + // called. The difference between the position reported by DevSound and + // this value allows us to calculate m_bytesPadding; + quint32 m_bytesWritten; + + // True if client has provided data while the audio subsystem was not + // ready to consume it. + bool m_pushDataReady; + + // Number of zero bytes which will be written when client calls resume(). + quint32 m_bytesPadding; + + // True if PlayError(KErrUnderflow) has been called. + bool m_underflow; + + // True if a buffer marked with the "last buffer" flag has been provided + // to DevSound. + bool m_lastBuffer; + + // Some DevSound implementations ignore all underflow errors raised by the + // audio driver, unless the last buffer flag has been set by the client. + // In push-mode playback, this flag will never be set, so the underflow + // error will never be reported. In order to work around this, a timer + // is used, which gets reset every time the client provides more data. If + // the timer expires, an underflow error is raised by this object. + QScopedPointer<QTimer> m_underflowTimer; + + // Result of previous call to CMMFDevSound::SamplesPlayed(). This value is + // used to determine whether, when m_underflowTimer expires, an + // underflow error has actually occurred. + quint32 m_samplesPlayed; + + // Samples played up to the last call to suspend(). It is necessary + // to cache this because suspend() is implemented using + // CMMFDevSound::Stop(), which resets DevSound's SamplesPlayed() counter. + quint32 m_totalSamplesPlayed; + +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/audio/symbian/symbianaudioutils.cpp b/src/plugins/audio/symbian/symbianaudioutils.cpp new file mode 100644 index 0000000..f04c198 --- /dev/null +++ b/src/plugins/audio/symbian/symbianaudioutils.cpp @@ -0,0 +1,395 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "symbianaudioutils.h" +#include <mmffourcc.h> + +QT_BEGIN_NAMESPACE + +namespace SymbianAudio { + +DevSoundCapabilities::DevSoundCapabilities(CMMFDevSound &devsound, + QAudio::Mode mode) +{ + QT_TRAP_THROWING(constructL(devsound, mode)); +} + +DevSoundCapabilities::~DevSoundCapabilities() +{ + m_fourCC.Close(); +} + +void DevSoundCapabilities::constructL(CMMFDevSound &devsound, + QAudio::Mode mode) +{ + m_caps = devsound.Capabilities(); + + TMMFPrioritySettings settings; + + switch (mode) { + case QAudio::AudioOutput: + settings.iState = EMMFStatePlaying; + devsound.GetSupportedInputDataTypesL(m_fourCC, settings); + break; + + case QAudio::AudioInput: + settings.iState = EMMFStateRecording; + devsound.GetSupportedInputDataTypesL(m_fourCC, settings); + break; + + default: + Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid mode"); + } +} + +namespace Utils { + +//----------------------------------------------------------------------------- +// Static data +//----------------------------------------------------------------------------- + +// Sample rate / frequency + +typedef TMMFSampleRate SampleRateNative; +typedef int SampleRateQt; + +const int SampleRateCount = 12; + +const SampleRateNative SampleRateListNative[SampleRateCount] = { + EMMFSampleRate8000Hz + , EMMFSampleRate11025Hz + , EMMFSampleRate12000Hz + , EMMFSampleRate16000Hz + , EMMFSampleRate22050Hz + , EMMFSampleRate24000Hz + , EMMFSampleRate32000Hz + , EMMFSampleRate44100Hz + , EMMFSampleRate48000Hz + , EMMFSampleRate64000Hz + , EMMFSampleRate88200Hz + , EMMFSampleRate96000Hz +}; + +const SampleRateQt SampleRateListQt[SampleRateCount] = { + 8000 + , 11025 + , 12000 + , 16000 + , 22050 + , 24000 + , 32000 + , 44100 + , 48000 + , 64000 + , 88200 + , 96000 +}; + +// Channels + +typedef TMMFMonoStereo ChannelsNative; +typedef int ChannelsQt; + +const int ChannelsCount = 2; + +const ChannelsNative ChannelsListNative[ChannelsCount] = { + EMMFMono + , EMMFStereo +}; + +const ChannelsQt ChannelsListQt[ChannelsCount] = { + 1 + , 2 +}; + +// Encoding + +const int EncodingCount = 6; + +const TUint32 EncodingFourCC[EncodingCount] = { + KMMFFourCCCodePCM8 // 0 + , KMMFFourCCCodePCMU8 // 1 + , KMMFFourCCCodePCM16 // 2 + , KMMFFourCCCodePCMU16 // 3 + , KMMFFourCCCodePCM16B // 4 + , KMMFFourCCCodePCMU16B // 5 +}; + +// The characterised DevSound API specification states that the iEncoding +// field in TMMFCapabilities is ignored, and that the FourCC should be used +// to specify the PCM encoding. +// See "SGL.GT0287.102 Multimedia DevSound Baseline Compatibility.doc" in the +// mm_info/mm_docs repository. +const TMMFSoundEncoding EncodingNative[EncodingCount] = { + EMMFSoundEncoding16BitPCM // 0 + , EMMFSoundEncoding16BitPCM // 1 + , EMMFSoundEncoding16BitPCM // 2 + , EMMFSoundEncoding16BitPCM // 3 + , EMMFSoundEncoding16BitPCM // 4 + , EMMFSoundEncoding16BitPCM // 5 +}; + + +const int EncodingSampleSize[EncodingCount] = { + 8 // 0 + , 8 // 1 + , 16 // 2 + , 16 // 3 + , 16 // 4 + , 16 // 5 +}; + +const QAudioFormat::Endian EncodingByteOrder[EncodingCount] = { + QAudioFormat::LittleEndian // 0 + , QAudioFormat::LittleEndian // 1 + , QAudioFormat::LittleEndian // 2 + , QAudioFormat::LittleEndian // 3 + , QAudioFormat::BigEndian // 4 + , QAudioFormat::BigEndian // 5 +}; + +const QAudioFormat::SampleType EncodingSampleType[EncodingCount] = { + QAudioFormat::SignedInt // 0 + , QAudioFormat::UnSignedInt // 1 + , QAudioFormat::SignedInt // 2 + , QAudioFormat::UnSignedInt // 3 + , QAudioFormat::SignedInt // 4 + , QAudioFormat::UnSignedInt // 5 +}; + + +//----------------------------------------------------------------------------- +// Private functions +//----------------------------------------------------------------------------- + +// Helper functions for implementing parameter conversions + +template<typename Input> +bool findValue(const Input *inputArray, int length, Input input, int &index) { + bool result = false; + for (int i=0; !result && i<length; ++i) + if (inputArray[i] == input) { + index = i; + result = true; + } + return result; +} + +template<typename Input, typename Output> +bool convertValue(const Input *inputArray, const Output *outputArray, + int length, Input input, Output &output) { + int index; + const bool result = findValue<Input>(inputArray, length, input, index); + if (result) + output = outputArray[index]; + return result; +} + +/** + * Macro which is used to generate the implementation of the conversion + * functions. The implementation is just a wrapper around the templated + * convertValue function, e.g. + * + * CONVERSION_FUNCTION_IMPL(SampleRate, Qt, Native) + * + * expands to + * + * bool SampleRateQtToNative(int input, TMMFSampleRate &output) { + * return convertValue<SampleRateQt, SampleRateNative> + * (SampleRateListQt, SampleRateListNative, SampleRateCount, + * input, output); + * } + */ +#define CONVERSION_FUNCTION_IMPL(FieldLc, Field, Input, Output) \ +bool FieldLc##Input##To##Output(Field##Input input, Field##Output &output) { \ + return convertValue<Field##Input, Field##Output>(Field##List##Input, \ + Field##List##Output, Field##Count, input, output); \ +} + +//----------------------------------------------------------------------------- +// Local helper functions +//----------------------------------------------------------------------------- + +CONVERSION_FUNCTION_IMPL(sampleRate, SampleRate, Qt, Native) +CONVERSION_FUNCTION_IMPL(sampleRate, SampleRate, Native, Qt) +CONVERSION_FUNCTION_IMPL(channels, Channels, Qt, Native) +CONVERSION_FUNCTION_IMPL(channels, Channels, Native, Qt) + +bool sampleInfoQtToNative(int inputSampleSize, + QAudioFormat::Endian inputByteOrder, + QAudioFormat::SampleType inputSampleType, + TUint32 &outputFourCC, + TMMFSoundEncoding &outputEncoding) { + + bool found = false; + + for (int i=0; i<EncodingCount && !found; ++i) { + if ( EncodingSampleSize[i] == inputSampleSize + && EncodingByteOrder[i] == inputByteOrder + && EncodingSampleType[i] == inputSampleType) { + outputFourCC = EncodingFourCC[i]; + outputEncoding = EncodingNative[i]; // EMMFSoundEncoding16BitPCM + found = true; + } + } + + return found; +} + +//----------------------------------------------------------------------------- +// Public functions +//----------------------------------------------------------------------------- + +void capabilitiesNativeToQt(const DevSoundCapabilities &caps, + QList<int> &frequencies, + QList<int> &channels, + QList<int> &sampleSizes, + QList<QAudioFormat::Endian> &byteOrders, + QList<QAudioFormat::SampleType> &sampleTypes) { + + frequencies.clear(); + sampleSizes.clear(); + byteOrders.clear(); + sampleTypes.clear(); + channels.clear(); + + for (int i=0; i<SampleRateCount; ++i) + if (caps.caps().iRate & SampleRateListNative[i]) + frequencies += SampleRateListQt[i]; + + for (int i=0; i<ChannelsCount; ++i) + if (caps.caps().iChannels & ChannelsListNative[i]) + channels += ChannelsListQt[i]; + + for (int i=0; i<EncodingCount; ++i) { + if (caps.fourCC().Find(EncodingFourCC[i]) != KErrNotFound) { + sampleSizes += EncodingSampleSize[i]; + byteOrders += EncodingByteOrder[i]; + sampleTypes += EncodingSampleType[i]; + } + } + +} + +bool isFormatSupported(const QAudioFormat &formatQt, + const DevSoundCapabilities &caps) { + TMMFCapabilities formatNative; + TUint32 fourCC; + + bool result = false; + if (formatQt.codec() == "audio/pcm" && + formatQtToNative(formatQt, fourCC, formatNative)) { + result = + (formatNative.iRate & caps.caps().iRate) + && (formatNative.iChannels & caps.caps().iChannels) + && (caps.fourCC().Find(fourCC) != KErrNotFound); + } + return result; +} + +bool formatQtToNative(const QAudioFormat &inputFormat, + TUint32 &outputFourCC, + TMMFCapabilities &outputFormat) { + + bool result = false; + + // Need to use temporary variables because TMMFCapabilities fields are all + // TInt, rather than MMF enumerated types. + TMMFSampleRate outputSampleRate; + TMMFMonoStereo outputChannels; + TMMFSoundEncoding outputEncoding; + + if (inputFormat.codec() == "audio/pcm") { + result = + sampleRateQtToNative(inputFormat.frequency(), outputSampleRate) + && channelsQtToNative(inputFormat.channels(), outputChannels) + && sampleInfoQtToNative(inputFormat.sampleSize(), + inputFormat.byteOrder(), + inputFormat.sampleType(), + outputFourCC, + outputEncoding); + } + + if (result) { + outputFormat.iRate = outputSampleRate; + outputFormat.iChannels = outputChannels; + outputFormat.iEncoding = outputEncoding; + } + + return result; +} + +QAudio::State stateNativeToQt(State nativeState, + QAudio::State initializingState) +{ + switch (nativeState) { + case ClosedState: + return QAudio::StoppedState; + case InitializingState: + return initializingState; + case ActiveState: + return QAudio::ActiveState; + case IdleState: + return QAudio::IdleState; + case SuspendedState: + return QAudio::SuspendedState; + default: + Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid state"); + return QAudio::StoppedState; // suppress compiler warning + } +} + +qint64 bytesToSamples(const QAudioFormat &format, qint64 length) +{ + return length / ((format.sampleSize() / 8) * format.channels()); +} + +qint64 samplesToBytes(const QAudioFormat &format, qint64 samples) +{ + return samples * (format.sampleSize() / 8) * format.channels(); +} + +} // namespace Utils +} // namespace SymbianAudio + +QT_END_NAMESPACE + + diff --git a/src/plugins/audio/symbian/symbianaudioutils.h b/src/plugins/audio/symbian/symbianaudioutils.h new file mode 100644 index 0000000..53274e0 --- /dev/null +++ b/src/plugins/audio/symbian/symbianaudioutils.h @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SYMBIANAUDIOUTILS_H +#define SYMBIANAUDIOUTILS_H + +#include <QtCore/qnamespace.h> +#include <QtMultimedia/qaudioformat.h> +#include <QtMultimedia/qaudio.h> +#include <sounddevice.h> +#include "symbianaudio.h" + +QT_BEGIN_NAMESPACE + +namespace SymbianAudio { + +/* + * Helper class for querying DevSound codec / format support + */ +class DevSoundCapabilities { +public: + DevSoundCapabilities(CMMFDevSound &devsound, QAudio::Mode mode); + ~DevSoundCapabilities(); + + const RArray<TFourCC>& fourCC() const { return m_fourCC; } + const TMMFCapabilities& caps() const { return m_caps; } + +private: + void constructL(CMMFDevSound &devsound, QAudio::Mode mode); + +private: + RArray<TFourCC> m_fourCC; + TMMFCapabilities m_caps; +}; + +namespace Utils { + +/** + * Convert native audio capabilities to QAudio lists. + */ +void capabilitiesNativeToQt(const DevSoundCapabilities &caps, + QList<int> &frequencies, + QList<int> &channels, + QList<int> &sampleSizes, + QList<QAudioFormat::Endian> &byteOrders, + QList<QAudioFormat::SampleType> &sampleTypes); + +/** + * Check whether format is supported. + */ +bool isFormatSupported(const QAudioFormat &format, + const DevSoundCapabilities &caps); + +/** + * Convert QAudioFormat to native format types. + * + * Note that, despite the name, DevSound uses TMMFCapabilities to specify + * single formats as well as capabilities. + * + * Note that this function does not modify outputFormat.iBufferSize. + */ +bool formatQtToNative(const QAudioFormat &inputFormat, + TUint32 &outputFourCC, + TMMFCapabilities &outputFormat); + +/** + * Convert internal states to QAudio states. + */ +QAudio::State stateNativeToQt(State nativeState, + QAudio::State initializingState); + +/** + * Convert data length to number of samples. + */ +qint64 bytesToSamples(const QAudioFormat &format, qint64 length); + +/** + * Convert number of samples to data length. + */ +qint64 samplesToBytes(const QAudioFormat &format, qint64 samples); + +} // namespace Utils +} // namespace SymbianAudio + +QT_END_NAMESPACE + +#endif diff --git a/src/qbase.pri b/src/qbase.pri index db55365..6f2dfa4 100644 --- a/src/qbase.pri +++ b/src/qbase.pri @@ -108,6 +108,10 @@ symbian { } } load(armcc_warnings) + + # workaround for the fact that some of our required includes in Symbian^3 + # now depend upon files in epoc32/include/platform + INCLUDEPATH += $$OS_LAYER_SYSTEMINCLUDE } win32-borland:INCLUDEPATH += kernel diff --git a/src/s60installs/qt.iby b/src/s60installs/qt.iby index a6a96ec..724451b 100644 --- a/src/s60installs/qt.iby +++ b/src/s60installs/qt.iby @@ -80,6 +80,9 @@ file=ABI_DIR\BUILD_DIR\qsvgicon.dll SHARED_LIB_DIR\qsvgicon.dll PAG // Phonon MMF backend file=ABI_DIR\BUILD_DIR\phonon_mmf.dll SHARED_LIB_DIR\phonon_mmf.dll PAGED +// QtMultimedia audio backend +file=ABI_DIR\BUILD_DIR\qaudio.dll SHARED_LIB_DIR\qaudio.dll PAGED + // graphicssystems file=ABI_DIR\BUILD_DIR\qvggraphicssystem.dll SHARED_LIB_DIR\qvggraphicssystem.dll PAGED @@ -109,6 +112,9 @@ data=\epoc32\data\z\resource\qt\plugins\iconengines\qsvgicon.qtplugin resou // Phonon MMF backend data=\epoc32\data\z\resource\qt\plugins\phonon_backend\phonon_mmf.qtplugin resource\qt\plugins\phonon_backend\phonon_mmf.qtplugin +// QtMultimedia audio backend +data=\epoc32\data\qt\qtlibspluginstubs\qaudio.qtplugin resource\qt\plugins\audio\qaudio.qtplugin + // graphicssystems data=\epoc32\data\z\resource\qt\plugins\graphicssystems\qvggraphicssystem.qtplugin resource\qt\plugins\graphicssystems\qvggraphicssystem.qtplugin diff --git a/src/s60installs/s60installs.pro b/src/s60installs/s60installs.pro index 5318693..1f3b4a6 100644 --- a/src/s60installs/s60installs.pro +++ b/src/s60installs/s60installs.pro @@ -78,6 +78,12 @@ symbian: { DEPLOYMENT += phonon_backend_plugins } + contains(QT_CONFIG, audio-backend) { + qaudio_backend_plugins.sources += qaudio.dll + qaudio_backend_plugins.path = c:$$QT_PLUGINS_BASE_DIR/audio + DEPLOYMENT += qaudio_backend_plugins + } + # Support backup & restore for Qt libraries qtbackup.sources = backup_registration.xml qtbackup.path = c:/private/10202D56/import/packages/$$replace(TARGET.UID3, 0x,) diff --git a/tools/configure/configure.pro b/tools/configure/configure.pro index 243183c..91de7c2 100644 --- a/tools/configure/configure.pro +++ b/tools/configure/configure.pro @@ -3,8 +3,8 @@ DESTDIR = ../.. CONFIG += console flat CONFIG -= moc qt -DEFINES = UNICODE QT_NODLL QT_NO_CODECS QT_NO_TEXTCODEC QT_NO_UNICODETABLES QT_LITE_COMPONENT QT_NO_STL QT_NO_COMPRESS QT_BUILD_QMAKE QT_NO_THREAD QT_NO_QOBJECT _CRT_SECURE_NO_DEPRECATE - +DEFINES = UNICODE QT_NODLL QT_NO_CODECS QT_NO_TEXTCODEC QT_NO_UNICODETABLES QT_LITE_COMPONENT QT_NO_STL QT_NO_COMPRESS QT_NO_THREAD QT_NO_QOBJECT _CRT_SECURE_NO_DEPRECATE +DEFINES += QT_BOOTSTRAPPED win32 : LIBS += -lole32 -ladvapi32 win32-msvc.net | win32-msvc2* : QMAKE_CXXFLAGS += /EHsc @@ -27,6 +27,7 @@ INCPATH += $$QT_SOURCE_TREE/src/corelib/arch/generic \ $$QT_SOURCE_TREE/src/corelib/global \ $$QT_BUILD_TREE/include \ $$QT_BUILD_TREE/include/QtCore \ + $$QT_BUILD_TREE/tools/shared HEADERS = configureapp.h environment.h tools.h\ $$QT_SOURCE_TREE/src/corelib/tools/qbytearray.h \ @@ -58,7 +59,11 @@ HEADERS = configureapp.h environment.h tools.h\ $$QT_SOURCE_TREE/src/corelib/tools/qstring.h \ $$QT_SOURCE_TREE/src/corelib/tools/qstringlist.h \ $$QT_SOURCE_TREE/src/corelib/tools/qstringmatcher.h \ - $$QT_SOURCE_TREE/src/corelib/tools/qunicodetables_p.h + $$QT_SOURCE_TREE/src/corelib/tools/qunicodetables_p.h \ + $$QT_SOURCE_TREE/src/corelib/xml/qxmlstream.h \ + $$QT_SOURCE_TREE/src/corelib/xml/qxmlutils_p.h \ + $$QT_SOURCE_TREE/tools/shared/symbian/epocroot.h \ + $$QT_SOURCE_TREE/tools/shared/windows/registry.h SOURCES = main.cpp configureapp.cpp environment.cpp tools.cpp \ @@ -102,7 +107,11 @@ SOURCES = main.cpp configureapp.cpp environment.cpp tools.cpp \ $$QT_SOURCE_TREE/src/corelib/tools/qpoint.cpp \ $$QT_SOURCE_TREE/src/corelib/tools/qrect.cpp \ $$QT_SOURCE_TREE/src/corelib/kernel/qmetatype.cpp \ - $$QT_SOURCE_TREE/src/corelib/global/qmalloc.cpp + $$QT_SOURCE_TREE/src/corelib/global/qmalloc.cpp \ + $$QT_SOURCE_TREE/src/corelib/xml/qxmlstream.cpp \ + $$QT_SOURCE_TREE/src/corelib/xml/qxmlutils.cpp \ + $$QT_SOURCE_TREE/tools/shared/symbian/epocroot.cpp \ + $$QT_SOURCE_TREE/tools/shared/windows/registry.cpp win32:SOURCES += $$QT_SOURCE_TREE/src/corelib/io/qfsfileengine_win.cpp diff --git a/tools/configure/configureapp.cpp b/tools/configure/configureapp.cpp index 9e0cb70..d521276 100644 --- a/tools/configure/configureapp.cpp +++ b/tools/configure/configureapp.cpp @@ -247,7 +247,7 @@ Configure::Configure( int& argc, char** argv ) dictionary[ "PHONON" ] = "auto"; dictionary[ "PHONON_BACKEND" ] = "yes"; dictionary[ "MULTIMEDIA" ] = "yes"; - dictionary[ "AUDIO_BACKEND" ] = "yes"; + dictionary[ "AUDIO_BACKEND" ] = "auto"; dictionary[ "DIRECTSHOW" ] = "no"; dictionary[ "WEBKIT" ] = "auto"; dictionary[ "DECLARATIVE" ] = "auto"; @@ -2065,6 +2065,52 @@ bool Configure::checkAvailability(const QString &part) available = (dictionary.value("QMAKESPEC") == "win32-msvc2005") || (dictionary.value("QMAKESPEC") == "win32-msvc2008") || (dictionary.value("QMAKESPEC") == "win32-g++"); } else if (part == "DECLARATIVE") { available = QFile::exists(sourcePath + "/src/declarative/qml/qmlcomponent.h"); + } else if (part == "AUDIO_BACKEND") { + available = true; + if (dictionary.contains("XQMAKESPEC") && dictionary["XQMAKESPEC"].startsWith("symbian")) { + QString epocRoot = Environment::symbianEpocRoot(); + const QDir epocRootDir(epocRoot); + if (epocRootDir.exists()) { + QStringList paths; + paths << "epoc32/release/armv5/lib/mmfdevsound.dso" + << "epoc32/release/armv5/lib/mmfdevsound.lib" + << "epoc32/release/winscw/udeb/mmfdevsound.dll" + << "epoc32/release/winscw/udeb/mmfdevsound.lib" + << "epoc32/include/mmf/server/sounddevice.h"; + + QStringList::iterator i = paths.begin(); + while (i != paths.end()) { + const QString &path = epocRoot + *i; + if (QFile::exists(path)) + i = paths.erase(i); + else + ++i; + } + + available = (paths.size() == 0); + if (!available) { + if (epocRoot.isNull() || epocRoot == "") + epocRoot = "<empty string>"; + cout << endl + << "The QtMultimedia audio backend will not be built because required" << endl + << "support for CMMFDevSound was not found in the SDK." << endl + << "The SDK which was examined was located at the following path:" << endl + << " " << epocRoot << endl + << "The following required files were missing from the SDK:" << endl; + QString path; + foreach (path, paths) + cout << " " << path << endl; + cout << endl; + } + } else { + cout << endl + << "The SDK root was determined to be '" << epocRoot << "'." << endl + << "This directory was not found, so the SDK could not be checked for" << endl + << "CMMFDevSound support. The QtMultimedia audio backend will therefore" << endl + << "not be built." << endl << endl; + available = false; + } + } } return available; @@ -2153,6 +2199,8 @@ void Configure::autoDetection() dictionary["WEBKIT"] = checkAvailability("WEBKIT") ? "yes" : "no"; if (dictionary["DECLARATIVE"] == "auto") dictionary["DECLARATIVE"] = checkAvailability("DECLARATIVE") ? "yes" : "no"; + if (dictionary["AUDIO_BACKEND"] == "auto") + dictionary["AUDIO_BACKEND"] = checkAvailability("AUDIO_BACKEND") ? "yes" : "no"; // Qt/WinCE remote test application if (dictionary["CETEST"] == "auto") diff --git a/tools/configure/environment.cpp b/tools/configure/environment.cpp index af6f9e5..e93f9a0 100644 --- a/tools/configure/environment.cpp +++ b/tools/configure/environment.cpp @@ -60,6 +60,8 @@ using namespace std; #include <qt_windows.h> #endif +#include <symbian/epocroot.h> // from tools/shared +#include <windows/registry.h> // from tools/shared QT_BEGIN_NAMESPACE @@ -97,126 +99,6 @@ CompilerInfo *Environment::compilerInfo(Compiler compiler) } /*! - Returns the path part of a registry key. - Ei. - For a key - "Software\\Microsoft\\VisualStudio\\8.0\\Setup\\VC\\ProductDir" - it returns - "Software\\Microsoft\\VisualStudio\\8.0\\Setup\\VC\\" -*/ -QString Environment::keyPath(const QString &rKey) -{ - int idx = rKey.lastIndexOf(QLatin1Char('\\')); - if (idx == -1) - return QString(); - return rKey.left(idx + 1); -} - -/*! - Returns the name part of a registry key. - Ei. - For a key - "Software\\Microsoft\\VisualStudio\\8.0\\Setup\\VC\\ProductDir" - it returns - "ProductDir" -*/ -QString Environment::keyName(const QString &rKey) -{ - int idx = rKey.lastIndexOf(QLatin1Char('\\')); - if (idx == -1) - return rKey; - - QString res(rKey.mid(idx + 1)); - if (res == "Default" || res == ".") - res = ""; - return res; -} - -/*! - Returns a registry keys value in string form. - If the registry key does not exist, or cannot be accessed, a - QString() is returned. -*/ -QString Environment::readRegistryKey(HKEY parentHandle, const QString &rSubkey) -{ -#ifndef Q_OS_WIN32 - return QString(); -#else - QString rSubkeyName = keyName(rSubkey); - QString rSubkeyPath = keyPath(rSubkey); - - HKEY handle = 0; - LONG res = RegOpenKeyEx(parentHandle, (wchar_t*)rSubkeyPath.utf16(), 0, KEY_READ, &handle); - if (res != ERROR_SUCCESS) - return QString(); - - // get the size and type of the value - DWORD dataType; - DWORD dataSize; - res = RegQueryValueEx(handle, (wchar_t*)rSubkeyName.utf16(), 0, &dataType, 0, &dataSize); - if (res != ERROR_SUCCESS) { - RegCloseKey(handle); - return QString(); - } - - // get the value - QByteArray data(dataSize, 0); - res = RegQueryValueEx(handle, (wchar_t*)rSubkeyName.utf16(), 0, 0, - reinterpret_cast<unsigned char*>(data.data()), &dataSize); - if (res != ERROR_SUCCESS) { - RegCloseKey(handle); - return QString(); - } - - QString result; - switch (dataType) { - case REG_EXPAND_SZ: - case REG_SZ: { - result = QString::fromWCharArray(((const wchar_t *)data.constData())); - break; - } - - case REG_MULTI_SZ: { - QStringList l; - int i = 0; - for (;;) { - QString s = QString::fromWCharArray((const wchar_t *)data.constData() + i); - i += s.length() + 1; - - if (s.isEmpty()) - break; - l.append(s); - } - result = l.join(", "); - break; - } - - case REG_NONE: - case REG_BINARY: { - result = QString::fromWCharArray((const wchar_t *)data.constData(), data.size() / 2); - break; - } - - case REG_DWORD_BIG_ENDIAN: - case REG_DWORD: { - Q_ASSERT(data.size() == sizeof(int)); - int i; - memcpy((char*)&i, data.constData(), sizeof(int)); - result = QString::number(i); - break; - } - - default: - qWarning("QSettings: unknown data %d type in windows registry", dataType); - break; - } - - RegCloseKey(handle); - return result; -#endif -} - -/*! Returns the qmakespec for the compiler detected on the system. */ QString Environment::detectQMakeSpec() @@ -579,4 +461,10 @@ bool Environment::rmdir(const QString &name) return result; } +QString Environment::symbianEpocRoot() +{ + // Call function defined in tools/shared/symbian/epocroot.h + return ::epocRoot(); +} + QT_END_NAMESPACE diff --git a/tools/configure/environment.h b/tools/configure/environment.h index 2d0eafd..b1cbe3a 100644 --- a/tools/configure/environment.h +++ b/tools/configure/environment.h @@ -71,13 +71,12 @@ public: static bool cpdir(const QString &srcDir, const QString &destDir); static bool rmdir(const QString &name); + static QString symbianEpocRoot(); + private: static Compiler detectedCompiler; static CompilerInfo *compilerInfo(Compiler compiler); - static QString keyPath(const QString &rKey); - static QString keyName(const QString &rKey); - static QString readRegistryKey(HKEY parentHandle, const QString &rSubkey); }; diff --git a/tools/shared/symbian/epocroot.cpp b/tools/shared/symbian/epocroot.cpp new file mode 100644 index 0000000..071477d --- /dev/null +++ b/tools/shared/symbian/epocroot.cpp @@ -0,0 +1,230 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <iostream> + +#include <QtCore/qdir.h> +#include <QtCore/qxmlstream.h> + +#include "epocroot.h" +#include "../windows/registry.h" + +// Registry key under which the location of the Symbian devices.xml file is +// stored. +// Note that, on 64-bit machines, this key is located under the 32-bit +// compatibility key: +// HKEY_LOCAL_MACHINE\Software\Wow6432Node +#define SYMBIAN_SDKS_REG_SUBKEY "Software\\Symbian\\EPOC SDKs\\CommonPath" + +#ifdef Q_OS_WIN32 +# define SYMBIAN_SDKS_REG_HANDLE HKEY_LOCAL_MACHINE +#else +# define SYMBIAN_SDKS_REG_HANDLE 0 +#endif + +// Value which is populated and returned by the epocRoot() function. +// Stored as a static value in order to avoid unnecessary re-evaluation. +static QString epocRootValue; + +#ifdef QT_BUILD_QMAKE +std::ostream &operator<<(std::ostream &s, const QString &val) { + s << val.toLocal8Bit().data(); + return s; +} +#else +// Operator implemented in configureapp.cpp +std::ostream &operator<<(std::ostream &s, const QString &val); +#endif + +QString getDevicesXmlPath() + { + // Note that the following call will return a null string on platforms other + // than Windows. If support is required on other platforms for devices.xml, + // an alternative mechanism for retrieving the location of this file will + // be required. + return readRegistryKey(SYMBIAN_SDKS_REG_HANDLE, SYMBIAN_SDKS_REG_SUBKEY); + } + +/** + * Checks whether epocRootValue points to an existent directory. + * If not, epocRootValue is set to an empty string and an error message is printed. + */ +void checkEpocRootExists(const QString &source) +{ + if (!epocRootValue.isEmpty()) { + QDir dir(epocRootValue); + if (!dir.exists()) { + std::cerr << "Warning: " << source << " is set to an invalid path: " << epocRootValue << std::endl; + epocRootValue = QString(); + } + } +} + +/** + * Translate path from Windows to Qt format. + */ +static void fixEpocRoot(QString &path) +{ + path.replace("\\", "/"); + + if (path.size() > 1 && path[1] == QChar(':')) { + path = path.mid(2); + } + + if (!path.size() || path[path.size()-1] != QChar('/')) { + path += QChar('/'); + } +} + +/** + * Determine the epoc root for the currently active SDK. + */ +QString epocRoot() +{ + if (epocRootValue.isEmpty()) { + // 1. If environment variable EPOCROOT is set and points to an existent + // directory, this is returned. + epocRootValue = qgetenv("EPOCROOT"); + checkEpocRootExists("EPOCROOT"); + + if (epocRootValue.isEmpty()) { + // 2. The location of devices.xml is specified by a registry key. If this + // file exists, it is parsed. + QString devicesXmlPath = getDevicesXmlPath(); + if (devicesXmlPath.isEmpty()) { + std::cerr << "Error: Symbian SDK registry key not found" << std::endl; + } else { + devicesXmlPath += "/devices.xml"; + QFile devicesFile(devicesXmlPath); + if (devicesFile.open(QIODevice::ReadOnly)) { + + // 3. If the EPOCDEVICE environment variable is set and a corresponding + // entry is found in devices.xml, and its epocroot value points to an + // existent directory, it is returned. + // 4. If a device element marked as default is found in devices.xml and its + // epocroot value points to an existent directory, this is returned. + + const QString epocDeviceValue = qgetenv("EPOCDEVICE"); + bool epocDeviceFound = false; + + QXmlStreamReader xml(&devicesFile); + while (!xml.atEnd()) { + xml.readNext(); + if (xml.isStartElement() && xml.name() == "devices") { + if (xml.attributes().value("version") == "1.0") { + while (!(xml.isEndElement() && xml.name() == "devices") && !xml.atEnd()) { + xml.readNext(); + if (xml.isStartElement() && xml.name() == "device") { + const bool isDefault = xml.attributes().value("default") == "yes"; + const QString id = xml.attributes().value("id").toString(); + const QString name = xml.attributes().value("name").toString(); + const bool epocDeviceMatch = (id + ":" + name) == epocDeviceValue; + epocDeviceFound |= epocDeviceMatch; + + if((epocDeviceValue.isEmpty() && isDefault) || epocDeviceMatch) { + // Found a matching device + while (!(xml.isEndElement() && xml.name() == "device") && !xml.atEnd()) { + xml.readNext(); + if (xml.isStartElement() && xml.name() == "epocroot") { + epocRootValue = xml.readElementText(); + const QString deviceSource = epocDeviceValue.isEmpty() + ? "default device" + : "EPOCDEVICE (" + epocDeviceValue + ")"; + checkEpocRootExists(deviceSource); + } + } + + if (epocRootValue.isEmpty()) + xml.raiseError("No epocroot element found"); + } + } + } + } else { + xml.raiseError("Invalid 'devices' element version"); + } + } + } + if (xml.hasError()) { + std::cerr << "Error: \"" << xml.errorString() << "\" when parsing devices.xml" << std::endl; + } else { + if (epocRootValue.isEmpty()) { + if (!epocDeviceValue.isEmpty()) { + if (epocDeviceFound) { + std::cerr << "Error: missing or invalid epocroot attribute " + << "in device '" << epocDeviceValue << "'"; + } else { + std::cerr << "Error: no device matching EPOCDEVICE (" + << epocDeviceValue << ")"; + } + } else { + if (epocDeviceFound) { + std::cerr << "Error: missing or invalid epocroot attribute " + << "in default device"; + } else { + std::cerr << "Error: no default device"; + } + } + std::cerr << " found in devices.xml file." << std::endl; + } + } + } else { + std::cerr << "Error: could not open file " << devicesXmlPath << std::endl; + } + } + } + + if (epocRootValue.isEmpty()) { + // 5. An empty string is returned. + std::cerr << "Error: failed to find epoc root" << std::endl + << "Either" << std::endl + << " 1. Set EPOCROOT environment variable to a valid value" << std::endl + << " or 2. Ensure that the HKEY_LOCAL_MACHINE\\" SYMBIAN_SDKS_REG_SUBKEY + " registry key is set, and then" << std::endl + << " a. Set EPOCDEVICE environment variable to a valid device" << std::endl + << " or b. Specify a default device in the devices.xml file." << std::endl; + } else { + fixEpocRoot(epocRootValue); + } + } + + return epocRootValue; +} + diff --git a/tools/shared/symbian/epocroot.h b/tools/shared/symbian/epocroot.h new file mode 100644 index 0000000..9846485 --- /dev/null +++ b/tools/shared/symbian/epocroot.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SYMBIAN_EPOCROOT_H +#define SYMBIAN_EPOCROOT_H + +#include <QtCore/qstring.h> + +/** + * Determine the epoc root for the currently active SDK. + * + * The algorithm used is as follows: + * 1. If environment variable EPOCROOT is set and points to an existent + * directory, this is returned. + * 2. The location of devices.xml is specified by a registry key. If this + * file exists, it is parsed. + * 3. If the EPOCDEVICE environment variable is set and a corresponding + * entry is found in devices.xml, and its epocroot value points to an + * existent directory, it is returned. + * 4. If a device element marked as default is found in devices.xml and its + * epocroot value points to an existent directory, this is returned. + * 5. An empty string is returned. + * + * Any return value other than the empty string therefore is guaranteed to + * point to an existent directory. + */ +QString epocRoot(); + +#endif // EPOCROOT_H diff --git a/tools/shared/windows/registry.cpp b/tools/shared/windows/registry.cpp new file mode 100644 index 0000000..d342d78 --- /dev/null +++ b/tools/shared/windows/registry.cpp @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qstringlist.h> +#include "registry.h" + +/*! + Returns the path part of a registry key. + e.g. + For a key + "Software\\Microsoft\\VisualStudio\\8.0\\Setup\\VC\\ProductDir" + it returns + "Software\\Microsoft\\VisualStudio\\8.0\\Setup\\VC\\" +*/ +static QString keyPath(const QString &rKey) +{ + int idx = rKey.lastIndexOf(QLatin1Char('\\')); + if (idx == -1) + return QString(); + return rKey.left(idx + 1); +} + +/*! + Returns the name part of a registry key. + e.g. + For a key + "Software\\Microsoft\\VisualStudio\\8.0\\Setup\\VC\\ProductDir" + it returns + "ProductDir" +*/ +static QString keyName(const QString &rKey) +{ + int idx = rKey.lastIndexOf(QLatin1Char('\\')); + if (idx == -1) + return rKey; + + QString res(rKey.mid(idx + 1)); + if (res == "Default" || res == ".") + res = ""; + return res; +} + +QString readRegistryKey(HKEY parentHandle, const QString &rSubkey) +{ + QString result; + +#ifdef Q_OS_WIN32 + QString rSubkeyName = keyName(rSubkey); + QString rSubkeyPath = keyPath(rSubkey); + + HKEY handle = 0; + LONG res = RegOpenKeyEx(parentHandle, (wchar_t*)rSubkeyPath.utf16(), 0, KEY_READ, &handle); + + if (res != ERROR_SUCCESS) + return QString(); + + // get the size and type of the value + DWORD dataType; + DWORD dataSize; + res = RegQueryValueEx(handle, (wchar_t*)rSubkeyName.utf16(), 0, &dataType, 0, &dataSize); + if (res != ERROR_SUCCESS) { + RegCloseKey(handle); + return QString(); + } + + // get the value + QByteArray data(dataSize, 0); + res = RegQueryValueEx(handle, (wchar_t*)rSubkeyName.utf16(), 0, 0, + reinterpret_cast<unsigned char*>(data.data()), &dataSize); + if (res != ERROR_SUCCESS) { + RegCloseKey(handle); + return QString(); + } + + switch (dataType) { + case REG_EXPAND_SZ: + case REG_SZ: { + result = QString::fromWCharArray(((const wchar_t *)data.constData())); + break; + } + + case REG_MULTI_SZ: { + QStringList l; + int i = 0; + for (;;) { + QString s = QString::fromWCharArray((const wchar_t *)data.constData() + i); + i += s.length() + 1; + + if (s.isEmpty()) + break; + l.append(s); + } + result = l.join(", "); + break; + } + + case REG_NONE: + case REG_BINARY: { + result = QString::fromWCharArray((const wchar_t *)data.constData(), data.size() / 2); + break; + } + + case REG_DWORD_BIG_ENDIAN: + case REG_DWORD: { + Q_ASSERT(data.size() == sizeof(int)); + int i; + memcpy((char*)&i, data.constData(), sizeof(int)); + result = QString::number(i); + break; + } + + default: + qWarning("QSettings: unknown data %d type in windows registry", dataType); + break; + } + + RegCloseKey(handle); +#endif + + return result; +} + + diff --git a/qmake/generators/symbian/epocroot.h b/tools/shared/windows/registry.h index be26438..3896527 100644 --- a/qmake/generators/symbian/epocroot.h +++ b/tools/shared/windows/registry.h @@ -39,13 +39,26 @@ ** ****************************************************************************/ -#ifndef EPOCROOT_H -#define EPOCROOT_H +#ifndef WINDOWS_REGISTRY_H +#define WINDOWS_REGISTRY_H -#include <qstring.h> +#include <QtCore/qglobal.h> -// Implementation of epocRoot method is in initprojectdeploy_symbian.cpp -// Defined in separate header for inclusion clarity -extern QString epocRoot(); +#ifdef Q_OS_WIN32 + #include <QtCore/qt_windows.h> +#else + typedef void* HKEY; +#endif -#endif // EPOCROOT_H +#include <QtCore/qstring.h> + +/** + * Read a value from the Windows registry. + * + * If the key is not found, or the registry cannot be accessed (for example + * if this code is compiled for a platform other than Windows), a null + * string is returned. + */ +QString readRegistryKey(HKEY parentHandle, const QString &rSubkey); + +#endif // WINDOWS_REGISTRY_H |