diff options
Diffstat (limited to 'src')
113 files changed, 11010 insertions, 922 deletions
diff --git a/src/activeqt/container/qaxbase.cpp b/src/activeqt/container/qaxbase.cpp index 4fc9926..d602946 100644 --- a/src/activeqt/container/qaxbase.cpp +++ b/src/activeqt/container/qaxbase.cpp @@ -3204,12 +3204,12 @@ static const char qt_meta_stringdata_QAxBase[] = { }; static QMetaObject qaxobject_staticMetaObject = { - &QObject::staticMetaObject, qt_meta_stringdata_QAxBase, - qt_meta_data_QAxBase, 0 + { &QObject::staticMetaObject, qt_meta_stringdata_QAxBase, + qt_meta_data_QAxBase, 0 } }; static QMetaObject qaxwidget_staticMetaObject = { - &QWidget::staticMetaObject, qt_meta_stringdata_QAxBase, - qt_meta_data_QAxBase, 0 + { &QWidget::staticMetaObject, qt_meta_stringdata_QAxBase, + qt_meta_data_QAxBase, 0 } }; /*! @@ -3692,6 +3692,8 @@ int QAxBase::qt_metacall(QMetaObject::Call call, int id, void **v) case QMetaMethod::Slot: id = internalInvoke(call, id, v); break; + default: + break; } break; case QMetaObject::ReadProperty: @@ -3706,6 +3708,8 @@ int QAxBase::qt_metacall(QMetaObject::Call call, int id, void **v) case QMetaObject::QueryPropertyUser: id -= mo->propertyCount(); break; + default: + break; } Q_ASSERT(id < 0); return id; @@ -3905,7 +3909,7 @@ bool QAxBase::dynamicCallHelper(const char *name, void *inout, QList<QVariant> & else paramType = d->metaobj->paramType(normFunction, i, &out); - if (!parse && d->useMetaObject && var.type() == QVariant::String || var.type() == QVariant::ByteArray) { + if ((!parse && d->useMetaObject && var.type() == QVariant::String) || var.type() == QVariant::ByteArray) { int enumIndex =mo->indexOfEnumerator(paramType); if (enumIndex != -1) { QMetaEnum metaEnum =mo->enumerator(enumIndex); diff --git a/src/activeqt/container/qaxwidget.cpp b/src/activeqt/container/qaxwidget.cpp index ff6bcb8..e4c9d42 100644 --- a/src/activeqt/container/qaxwidget.cpp +++ b/src/activeqt/container/qaxwidget.cpp @@ -531,7 +531,7 @@ bool axc_FilterProc(void *m) } QAxClientSite::QAxClientSite(QAxWidget *c) -: ref(1), widget(c), host(0), eventTranslated(true) +: eventTranslated(true), ref(1), widget(c), host(0) { aggregatedObject = widget->createAggregate(); if (aggregatedObject) { diff --git a/src/activeqt/shared/qaxtypes.cpp b/src/activeqt/shared/qaxtypes.cpp index 49aa99c..63891c4 100644 --- a/src/activeqt/shared/qaxtypes.cpp +++ b/src/activeqt/shared/qaxtypes.cpp @@ -552,7 +552,7 @@ bool QVariantToVARIANT(const QVariant &var, VARIANT &arg, const QByteArray &type int maxColumns = col.count(); if (maxColumns) { is2D = true; - SAFEARRAYBOUND rgsabound[2] = {0}; + SAFEARRAYBOUND rgsabound[2] = { {0} }; rgsabound[0].cElements = count; rgsabound[1].cElements = maxColumns; array = SafeArrayCreate(VT_VARIANT, 2, rgsabound); diff --git a/src/corelib/animation/qabstractanimation.cpp b/src/corelib/animation/qabstractanimation.cpp index ced86d2..1d274c9 100644 --- a/src/corelib/animation/qabstractanimation.cpp +++ b/src/corelib/animation/qabstractanimation.cpp @@ -177,17 +177,6 @@ QUnifiedTimer *QUnifiedTimer::instance() return inst; } -void QUnifiedTimer::updateRecentlyStartedAnimations() -{ - if (animationsToStart.isEmpty()) - return; - - animations += animationsToStart; - updateTimer(); //we make sure we start the timer there - - animationsToStart.clear(); -} - void QUnifiedTimer::timerEvent(QTimerEvent *event) { //this is simply the time we last received a tick @@ -195,15 +184,16 @@ void QUnifiedTimer::timerEvent(QTimerEvent *event) if (time.isValid()) lastTick = consistentTiming ? oldLastTick + timingInterval : time.elapsed(); - //we transfer the waiting animations into the "really running" state - updateRecentlyStartedAnimations(); if (event->timerId() == startStopAnimationTimer.timerId()) { startStopAnimationTimer.stop(); + //we transfer the waiting animations into the "really running" state + animations += animationsToStart; + animationsToStart.clear(); if (animations.isEmpty()) { animationTimer.stop(); time = QTime(); - } else { + } else if (!animationTimer.isActive()) { animationTimer.start(timingInterval, this); lastTick = 0; time.start(); @@ -219,27 +209,19 @@ void QUnifiedTimer::timerEvent(QTimerEvent *event) } } -void QUnifiedTimer::updateTimer() -{ - //we delay the call to start and stop for the animation timer so that if you - //stop and start animations in batch you don't stop/start the timer too often. - if (!startStopAnimationTimer.isActive()) - startStopAnimationTimer.start(0, this); // we delay the actual start of the animation -} - void QUnifiedTimer::registerAnimation(QAbstractAnimation *animation) { if (animations.contains(animation) ||animationsToStart.contains(animation)) return; animationsToStart << animation; - updateTimer(); + startStopAnimationTimer.start(0, this); // we delay the check if we should start/stop the global timer } void QUnifiedTimer::unregisterAnimation(QAbstractAnimation *animation) { animations.removeAll(animation); animationsToStart.removeAll(animation); - updateTimer(); + startStopAnimationTimer.start(0, this); // we delay the check if we should start/stop the global timer } diff --git a/src/corelib/animation/qabstractanimation_p.h b/src/corelib/animation/qabstractanimation_p.h index 0d8402e..b281aa2 100644 --- a/src/corelib/animation/qabstractanimation_p.h +++ b/src/corelib/animation/qabstractanimation_p.h @@ -135,11 +135,8 @@ public: protected: void timerEvent(QTimerEvent *); - void updateTimer(); private: - void updateRecentlyStartedAnimations(); - QBasicTimer animationTimer, startStopAnimationTimer; QTime time; int lastTick; diff --git a/src/corelib/animation/qparallelanimationgroup.cpp b/src/corelib/animation/qparallelanimationgroup.cpp index 5e4b0d2..8aa04a4 100644 --- a/src/corelib/animation/qparallelanimationgroup.cpp +++ b/src/corelib/animation/qparallelanimationgroup.cpp @@ -214,7 +214,8 @@ void QParallelAnimationGroup::updateState(QAbstractAnimation::State oldState, d->connectUncontrolledAnimations(); for (int i = 0; i < d->animations.size(); ++i) { QAbstractAnimation *animation = d->animations.at(i); - animation->stop(); + if (oldState == Stopped) + animation->stop(); animation->setDirection(d->direction); animation->start(); } diff --git a/src/corelib/arch/qatomic_vxworks.h b/src/corelib/arch/qatomic_vxworks.h index 573a44d..b441210 100644 --- a/src/corelib/arch/qatomic_vxworks.h +++ b/src/corelib/arch/qatomic_vxworks.h @@ -34,7 +34,7 @@ ** met: http://www.gnu.org/copyleft/gpl.html. ** ** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. +** contact the sales department at http://www.qtsoftware.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h index dc3667b..86f0757 100644 --- a/src/corelib/global/qglobal.h +++ b/src/corelib/global/qglobal.h @@ -285,7 +285,7 @@ namespace QT_NAMESPACE {} # endif #endif -#if defined(Q_OS_MAC64) && !defined(QT_MAC_USE_COCOA) +#if defined(Q_OS_MAC64) && !defined(QT_MAC_USE_COCOA) && !defined(QT_BUILD_QMAKE) #error "You are building a 64-bit application, but using a 32-bit version of Qt. Check your build configuration." #endif @@ -317,11 +317,8 @@ namespace QT_NAMESPACE {} # if !defined(MAC_OS_X_VERSION_10_6) # define MAC_OS_X_VERSION_10_6 MAC_OS_X_VERSION_10_5 + 1 # endif -# if (MAC_OS_X_VERSION_MAX_ALLOWED == MAC_OS_X_VERSION_10_6) -# warning "Support for this version of Mac OS X is still preliminary" -# endif # if (MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_6) -# error "This version of Mac OS X is unsupported" +# warning "This version of Mac OS X is unsupported" # endif #endif @@ -1220,6 +1217,11 @@ class QDataStream; # else # define Q_OPENGL_EXPORT Q_DECL_IMPORT # endif +# if defined(QT_BUILD_MULTIMEDIA_LIB) +# define Q_MULTIMEDIA_EXPORT Q_DECL_EXPORT +# else +# define Q_MULTIMEDIA_EXPORT Q_DECL_IMPORT +# endif # if defined(QT_BUILD_OPENVG_LIB) # define Q_OPENVG_EXPORT Q_DECL_EXPORT # else @@ -1264,6 +1266,7 @@ class QDataStream; # define Q_SVG_EXPORT Q_DECL_IMPORT # define Q_CANVAS_EXPORT Q_DECL_IMPORT # define Q_OPENGL_EXPORT Q_DECL_IMPORT +# define Q_MULTIMEDIA_EXPORT Q_DECL_IMPORT # define Q_OPENVG_EXPORT Q_DECL_IMPORT # define Q_XML_EXPORT Q_DECL_IMPORT # define Q_XMLPATTERNS_EXPORT Q_DECL_IMPORT @@ -1290,6 +1293,7 @@ class QDataStream; # define Q_NETWORK_EXPORT Q_DECL_EXPORT # define Q_SVG_EXPORT Q_DECL_EXPORT # define Q_OPENGL_EXPORT Q_DECL_EXPORT +# define Q_MULTIMEDIA_EXPORT Q_DECL_EXPORT # define Q_OPENVG_EXPORT Q_DECL_EXPORT # define Q_XML_EXPORT Q_DECL_EXPORT # define Q_XMLPATTERNS_EXPORT Q_DECL_EXPORT @@ -1303,6 +1307,7 @@ class QDataStream; # define Q_NETWORK_EXPORT # define Q_SVG_EXPORT # define Q_OPENGL_EXPORT +# define Q_MULTIMEDIA_EXPORT # define Q_XML_EXPORT # define Q_XMLPATTERNS_EXPORT # define Q_SCRIPT_EXPORT @@ -2452,12 +2457,14 @@ Q_CORE_EXPORT int qt_exception2SymbianError(const std::exception& ex); #define QT_MODULE_DBUS 0x08000 #define QT_MODULE_SCRIPTTOOLS 0x10000 #define QT_MODULE_OPENVG 0x20000 +#define QT_MODULE_MULTIMEDIA 0x40000 /* Qt editions */ #define QT_EDITION_CONSOLE (QT_MODULE_CORE \ | QT_MODULE_NETWORK \ | QT_MODULE_SQL \ | QT_MODULE_SCRIPT \ + | QT_MODULE_MULTIMEDIA \ | QT_MODULE_XML \ | QT_MODULE_XMLPATTERNS \ | QT_MODULE_TEST \ @@ -2473,6 +2480,7 @@ Q_CORE_EXPORT int qt_exception2SymbianError(const std::exception& ex); | QT_MODULE_OPENGL \ | QT_MODULE_OPENVG \ | QT_MODULE_SQL \ + | QT_MODULE_MULTIMEDIA \ | QT_MODULE_XML \ | QT_MODULE_XMLPATTERNS \ | QT_MODULE_SCRIPT \ @@ -2524,6 +2532,9 @@ QT_LICENSED_MODULE(OpenVG) #if (QT_EDITION & QT_MODULE_SQL) QT_LICENSED_MODULE(Sql) #endif +#if (QT_EDITION & QT_MODULE_MULTIMEDIA) +QT_LICENSED_MODULE(Multimedia) +#endif #if (QT_EDITION & QT_MODULE_XML) QT_LICENSED_MODULE(Xml) #endif diff --git a/src/corelib/kernel/qabstractitemmodel.cpp b/src/corelib/kernel/qabstractitemmodel.cpp index bc95c60..5d5d4cc 100644 --- a/src/corelib/kernel/qabstractitemmodel.cpp +++ b/src/corelib/kernel/qabstractitemmodel.cpp @@ -1884,6 +1884,8 @@ QSize QAbstractItemModel::span(const QModelIndex &) const } /*! + \since 4.6 + Sets the model's role names to \a roleNames. This function is provided to allow mapping of role identifiers to @@ -1900,6 +1902,8 @@ void QAbstractItemModel::setRoleNames(const QHash<int,QByteArray> &roleNames) } /*! + \since 4.6 + Returns the model's role names. \sa setRoleNames() diff --git a/src/corelib/kernel/qcore_unix.cpp b/src/corelib/kernel/qcore_unix.cpp index b57d385..efa9c6d 100644 --- a/src/corelib/kernel/qcore_unix.cpp +++ b/src/corelib/kernel/qcore_unix.cpp @@ -161,76 +161,3 @@ int qt_safe_select(int nfds, fd_set *fdread, fd_set *fdwrite, fd_set *fdexcept, } QT_END_NAMESPACE - -#ifdef Q_OS_LINUX -// Don't wait for libc to supply the calls we need -// Make syscalls directly - -# if defined(__GLIBC__) && (__GLIBC__ * 0x100 + __GLIBC_MINOR__) >= 0x0204 -// glibc 2.4 has syscall(...) -# include <sys/syscall.h> -# include <asm/unistd.h> -# else -// no syscall(...) -static inline int syscall(...) { errno = ENOSYS; return -1;} -# endif - -# ifndef __NR_dup3 -# if defined(__i386__) -# define __NR_dup3 330 -# define __NR_pipe2 331 -# elif defined(__x86_64__) -# define __NR_dup3 292 -# define __NR_pipe2 293 -# elif defined(__ia64__) -# define __NR_dup3 1316 -# define __NR_pipe2 1317 -# else -// set the syscalls to absurd numbers so that they'll cause ENOSYS errors -# warning "Please port the pipe2/dup3 code to this platform" -# define __NR_dup3 -1 -# define __NR_pipe2 -1 -# endif -# endif - -# if !defined(__NR_socketcall) && !defined(__NR_accept4) -# if defined(__x86_64__) -# define __NR_accept4 288 -# elif defined(__ia64__) -// not assigned yet to IA-64 -# define __NR_accept4 -1 -# else -// set the syscalls to absurd numbers so that they'll cause ENOSYS errors -# warning "Please port the accept4 code to this platform" -# define __NR_accept4 -1 -# endif -# endif - -QT_BEGIN_NAMESPACE -namespace QtLibcSupplement { - int pipe2(int pipes[], int flags) - { - return syscall(__NR_pipe2, pipes, flags); - } - - int dup3(int oldfd, int newfd, int flags) - { - return syscall(__NR_dup3, oldfd, newfd, flags); - } - - int accept4(int s, sockaddr *addr, QT_SOCKLEN_T *addrlen, int flags) - { -# if defined(__NR_socketcall) - // This platform uses socketcall() instead of raw syscalls - // the SYS_ACCEPT4 number is cross-platform: 18 - return syscall(__NR_socketcall, 18, &s); -# else - return syscall(__NR_accept4, s, addr, addrlen, flags); -# endif - - Q_UNUSED(addr); Q_UNUSED(addrlen); Q_UNUSED(flags); // they're actually used - } -} -QT_END_NAMESPACE -#endif // Q_OS_LINUX - diff --git a/src/corelib/kernel/qcore_unix_p.h b/src/corelib/kernel/qcore_unix_p.h index e301c92..9a6069d 100644 --- a/src/corelib/kernel/qcore_unix_p.h +++ b/src/corelib/kernel/qcore_unix_p.h @@ -73,32 +73,16 @@ struct sockaddr; -#if defined(Q_OS_LINUX) && defined(__GLIBC__) && (__GLIBC__ * 0x100 + __GLIBC_MINOR__) >= 0x0204 -// Linux supports thread-safe FD_CLOEXEC +#if defined(Q_OS_LINUX) && defined(O_CLOEXEC) # define QT_UNIX_SUPPORTS_THREADSAFE_CLOEXEC 1 - -// add defines for the consts for Linux -# ifndef O_CLOEXEC -# define O_CLOEXEC 02000000 -# endif -# ifndef FD_DUPFD_CLOEXEC -# define F_DUPFD_CLOEXEC 1030 -# endif -# ifndef SOCK_CLOEXEC -# define SOCK_CLOEXEC O_CLOEXEC -# endif -# ifndef SOCK_NONBLOCK -# define SOCK_NONBLOCK O_NONBLOCK -# endif -# ifndef MSG_CMSG_CLOEXEC -# define MSG_CMSG_CLOEXEC 0x40000000 -# endif - QT_BEGIN_NAMESPACE namespace QtLibcSupplement { - Q_CORE_EXPORT int accept4(int, sockaddr *, QT_SOCKLEN_T *, int flags); - Q_CORE_EXPORT int dup3(int oldfd, int newfd, int flags); - Q_CORE_EXPORT int pipe2(int pipes[], int flags); + inline int accept4(int, sockaddr *, QT_SOCKLEN_T *, int) + { errno = ENOSYS; return -1; } + inline int dup3(int, int, int) + { errno = ENOSYS; return -1; } + inline int pipe2(int [], int ) + { errno = ENOSYS; return -1; } } QT_END_NAMESPACE using namespace QT_PREPEND_NAMESPACE(QtLibcSupplement); @@ -190,7 +174,7 @@ static inline int qt_safe_pipe(int pipefd[2], int flags = 0) #endif register int ret; -#if QT_UNIX_SUPPORTS_THREADSAFE_CLOEXEC +#if QT_UNIX_SUPPORTS_THREADSAFE_CLOEXEC && defined(O_CLOEXEC) // use pipe2 flags |= O_CLOEXEC; ret = ::pipe2(pipefd, flags); // pipe2 is Linux-specific and is documented not to return EINTR @@ -246,7 +230,7 @@ static inline int qt_safe_dup2(int oldfd, int newfd, int flags = FD_CLOEXEC) Q_ASSERT(flags == FD_CLOEXEC || flags == 0); register int ret; -#if QT_UNIX_SUPPORTS_THREADSAFE_CLOEXEC +#if QT_UNIX_SUPPORTS_THREADSAFE_CLOEXEC && defined(O_CLOEXEC) // use dup3 if (flags & FD_CLOEXEC) { EINTR_LOOP(ret, ::dup3(oldfd, newfd, O_CLOEXEC)); diff --git a/src/corelib/kernel/qcoreevent.cpp b/src/corelib/kernel/qcoreevent.cpp index 3cb8fe6..72bad5f 100644 --- a/src/corelib/kernel/qcoreevent.cpp +++ b/src/corelib/kernel/qcoreevent.cpp @@ -272,7 +272,7 @@ QT_BEGIN_NAMESPACE \omitvalue CocoaRequestModal \omitvalue Signal \omitvalue SymbianDeferredFocusChanged - \omitvalue WinGesture + \omitvalue NativeGesture */ /*! diff --git a/src/corelib/kernel/qcoreevent.h b/src/corelib/kernel/qcoreevent.h index c361fd5..42e3d24 100644 --- a/src/corelib/kernel/qcoreevent.h +++ b/src/corelib/kernel/qcoreevent.h @@ -276,7 +276,7 @@ public: TouchUpdate = 195, TouchEnd = 196, - WinGesture = 197, + NativeGesture = 197, // Internal for platform gesture support RequestSoftwareInputPanel = 199, CloseSoftwareInputPanel = 200, diff --git a/src/corelib/kernel/qeventdispatcher_glib.cpp b/src/corelib/kernel/qeventdispatcher_glib.cpp index 7610631..2bbe560 100644 --- a/src/corelib/kernel/qeventdispatcher_glib.cpp +++ b/src/corelib/kernel/qeventdispatcher_glib.cpp @@ -263,6 +263,7 @@ QEventDispatcherGlibPrivate::QEventDispatcherGlibPrivate(GMainContext *context) (void) new (&timerSource->timerList) QTimerInfoList(); timerSource->processEventsFlags = QEventLoop::AllEvents; g_source_set_can_recurse(&timerSource->source, true); + g_source_set_priority(&timerSource->source, G_PRIORITY_DEFAULT_IDLE); g_source_attach(&timerSource->source, mainContext); } diff --git a/src/corelib/kernel/qfunctions_vxworks.cpp b/src/corelib/kernel/qfunctions_vxworks.cpp index 6d5e7cc..def8f4c 100644 --- a/src/corelib/kernel/qfunctions_vxworks.cpp +++ b/src/corelib/kernel/qfunctions_vxworks.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Qt Software Information (qt-info@nokia.com) +** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the QtCore module of the Qt Toolkit. ** @@ -34,7 +34,7 @@ ** met: http://www.gnu.org/copyleft/gpl.html. ** ** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. +** contact the sales department at http://www.qtsoftware.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/corelib/kernel/qfunctions_vxworks.h b/src/corelib/kernel/qfunctions_vxworks.h index cc98948..e31d495 100644 --- a/src/corelib/kernel/qfunctions_vxworks.h +++ b/src/corelib/kernel/qfunctions_vxworks.h @@ -34,7 +34,7 @@ ** met: http://www.gnu.org/copyleft/gpl.html. ** ** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. +** contact the sales department at http://www.qtsoftware.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index 7f8d2e8..1d0b2c8 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -1352,6 +1352,8 @@ int QMetaMethod::attributes() const } /*! + \since 4.6 + Returns this method's index. */ int QMetaMethod::methodIndex() const @@ -2078,6 +2080,8 @@ int QMetaProperty::userType() const } /*! + \since 4.6 + Returns this property's index. */ int QMetaProperty::propertyIndex() const diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 2630b63..e6947a0 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -123,8 +123,11 @@ extern "C" Q_CORE_EXPORT void qt_removeObject(QObject *) } } +QObjectData::~QObjectData() {} +QDeclarativeData::~QDeclarativeData() {} + QObjectPrivate::QObjectPrivate(int version) - : threadData(0), currentSender(0), declarativeData(0), connectionLists(0), senders(0) + : threadData(0), connectionLists(0), senders(0), currentSender(0), currentChildBeingDeleted(0), declarativeData(0), objectGuards(0) { if (version != QObjectPrivateVersion) qFatal("Cannot mix incompatible Qt libraries"); @@ -145,7 +148,6 @@ QObjectPrivate::QObjectPrivate(int version) inEventHandler = false; inThreadChangeEvent = false; deleteWatch = 0; - objectGuards = 0; metaObject = 0; hasGuards = false; } @@ -794,6 +796,8 @@ QObject::~QObject() QT_TRY { emit destroyed(this); + if (d->declarativeData) + d->declarativeData->destroyed(this); // ### TODO: Can this throw? } QT_CATCH(...) { // all the signal/slots connections are still in place - if we don't // quit now, we will crash pretty soon. @@ -892,14 +896,6 @@ QObject::~QObject() d->eventFilters.clear(); - // As declarativeData is in a union with currentChildBeingDeleted, this must - // be done (and declarativeData set back to 0) before deleting children. - if(d->declarativeData) { - QDeclarativeData *dd = d->declarativeData; - d->declarativeData = 0; - dd->destroyed(this); - } - if (!d->children.isEmpty()) d->deleteChildren(); @@ -1906,13 +1902,12 @@ void QObjectPrivate::deleteChildren() // don't use qDeleteAll as the destructor of the child might // delete siblings for (int i = 0; i < children.count(); ++i) { - QObject *child = children.at(i); + currentChildBeingDeleted = children.at(i); children[i] = 0; - if (child) - child->d_func()->parent = 0; - delete child; + delete currentChildBeingDeleted; } children.clear(); + currentChildBeingDeleted = 0; wasDeleted = reallyWasDeleted; } @@ -1923,14 +1918,20 @@ void QObjectPrivate::setParent_helper(QObject *o) return; if (parent) { QObjectPrivate *parentD = parent->d_func(); - const int index = parentD->children.indexOf(q); - if (parentD->wasDeleted) { - parentD->children[index] = 0; + if (parentD->wasDeleted && wasDeleted + && parentD->currentChildBeingDeleted == q) { + // don't do anything since QObjectPrivate::deleteChildren() already + // cleared our entry in parentD->children. } else { - parentD->children.removeAt(index); - if (sendChildEvents && parentD->receiveChildEvents) { - QChildEvent e(QEvent::ChildRemoved, q); - QCoreApplication::sendEvent(parent, &e); + const int index = parentD->children.indexOf(q); + if (parentD->wasDeleted) { + parentD->children[index] = 0; + } else { + parentD->children.removeAt(index); + if (sendChildEvents && parentD->receiveChildEvents) { + QChildEvent e(QEvent::ChildRemoved, q); + QCoreApplication::sendEvent(parent, &e); + } } } } @@ -3965,19 +3966,12 @@ QDebug operator<<(QDebug dbg, const QObject *o) { Synonym for QList<QObject *>. */ -#ifdef QT_JAMBI_BUILD -class QDPtrAccessor : public QObject { -public: - QObjectData *d() const { return d_ptr; } -}; -#endif - void qDeleteInEventHandler(QObject *o) { #ifdef QT_JAMBI_BUILD if (!o) return; - ((QDPtrAccessor *) o)->d()->inEventHandler = false; + QObjectPrivate::get(o)->inEventHandler = false; #endif delete o; } diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h index 0b41c9a..e58ee6c 100644 --- a/src/corelib/kernel/qobject_p.h +++ b/src/corelib/kernel/qobject_p.h @@ -82,15 +82,13 @@ void Q_CORE_EXPORT qt_register_signal_spy_callbacks(const QSignalSpyCallbackSet extern QSignalSpyCallbackSet Q_CORE_EXPORT qt_signal_spy_callback_set; -inline QObjectData::~QObjectData() {} - enum { QObjectPrivateVersion = QT_VERSION }; -class QDeclarativeData +class Q_CORE_EXPORT QDeclarativeData { public: - virtual ~QDeclarativeData() {} - virtual void destroyed(QObject *) {} + virtual ~QDeclarativeData(); + virtual void destroyed(QObject *) = 0; }; class Q_CORE_EXPORT QObjectPrivate : public QObjectData @@ -98,47 +96,6 @@ class Q_CORE_EXPORT QObjectPrivate : public QObjectData Q_DECLARE_PUBLIC(QObject) public: - QObjectPrivate(int version = QObjectPrivateVersion); - virtual ~QObjectPrivate(); - -#ifdef QT3_SUPPORT - QList<QObject *> pendingChildInsertedEvents; - void sendPendingChildInsertedEvents(); - void removePendingChildInsertedEvents(QObject *child); -#else - // preserve binary compatibility with code compiled without Qt 3 support - QList<QObject *> unused; -#endif - - // id of the thread that owns the object - QThreadData *threadData; - void moveToThread_helper(); - void setThreadData_helper(QThreadData *currentData, QThreadData *targetData); - void _q_reregisterTimers(void *pointer); - - struct Sender - { - QObject *sender; - int signal; - int ref; - }; - // object currently activating the object - Sender *currentSender; - - QDeclarativeData *declarativeData; - - bool isSender(const QObject *receiver, const char *signal) const; - QObjectList receiverList(const char *signal) const; - QObjectList senderList() const; - - QList<QPointer<QObject> > eventFilters; - - void setParent_helper(QObject *); - - void deleteChildren(); - - static void clearGuards(QObject *); - struct ExtraData { #ifndef QT_NO_USERDATA @@ -147,12 +104,7 @@ public: QList<QByteArray> propertyNames; QList<QVariant> propertyValues; }; - ExtraData *extraData; - mutable quint32 connectedSignals[2]; - - QString objectName; - // Note: you must hold the signalSlotLock() before accessing the lists below or calling the functions struct Connection { QObject *sender; @@ -167,11 +119,34 @@ public: }; typedef QList<Connection *> ConnectionList; - QObjectConnectionListVector *connectionLists; + struct Sender + { + QObject *sender; + int signal; + int ref; + }; + + + QObjectPrivate(int version = QObjectPrivateVersion); + virtual ~QObjectPrivate(); + void deleteChildren(); + + void setParent_helper(QObject *); + void moveToThread_helper(); + void setThreadData_helper(QThreadData *currentData, QThreadData *targetData); + void _q_reregisterTimers(void *pointer); + + bool isSender(const QObject *receiver, const char *signal) const; + QObjectList receiverList(const char *signal) const; + QObjectList senderList() const; + void addConnection(int signal, Connection *c); void cleanConnectionLists(); - Connection *senders; //linked list; +#ifdef QT3_SUPPORT + void sendPendingChildInsertedEvents(); + void removePendingChildInsertedEvents(QObject *child); +#endif static Sender *setCurrentSender(QObject *receiver, Sender *sender); @@ -180,13 +155,39 @@ public: Sender *previousSender); static int *setDeleteWatch(QObjectPrivate *d, int *newWatch); static void resetDeleteWatch(QObjectPrivate *d, int *oldWatch, int deleteWatch); - - int *deleteWatch; - QGuard<QObject> *objectGuards; + static void clearGuards(QObject *); static QObjectPrivate *get(QObject *o) { return o->d_func(); } + +public: + QString objectName; + ExtraData *extraData; // extra data set by the user + QThreadData *threadData; // id of the thread that owns the object + + QObjectConnectionListVector *connectionLists; + + Connection *senders; // linked list of connections connected to this object + Sender *currentSender; // object currently activating the object + mutable quint32 connectedSignals[2]; // 64-bit, so doesn't cause padding on 64-bit platforms + +#ifdef QT3_SUPPORT + QList<QObject *> pendingChildInsertedEvents; +#else + // preserve binary compatibility with code compiled without Qt 3 support + // ### why? + QList<QObject *> unused; +#endif + + QList<QPointer<QObject> > eventFilters; + QObject *currentChildBeingDeleted; + + // these objects are all used to indicate that a QObject was deleted + // plus QPointer, which keeps a separate list + QDeclarativeData *declarativeData; + QGuard<QObject> *objectGuards; + int *deleteWatch; }; inline void q_guard_addGuard(QGuard<QObject> *g) diff --git a/src/corelib/tools/qbytedata_p.h b/src/corelib/tools/qbytedata_p.h index cc10ea2..f3724f6 100644 --- a/src/corelib/tools/qbytedata_p.h +++ b/src/corelib/tools/qbytedata_p.h @@ -42,8 +42,18 @@ #ifndef QBYTEDATA_H #define QBYTEDATA_H -#include <qbytearray.h> +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// +#include <qbytearray.h> QT_BEGIN_NAMESPACE diff --git a/src/corelib/tools/qsharedpointer.cpp b/src/corelib/tools/qsharedpointer.cpp index 8a63d64..f7b014e 100644 --- a/src/corelib/tools/qsharedpointer.cpp +++ b/src/corelib/tools/qsharedpointer.cpp @@ -133,7 +133,7 @@ To access the pointer that QWeakPointer is tracking, you must first create a QSharedPointer object and verify if the pointer - is null or not. + is null or not. See QWeakPointer::toStrongRef() for more information. \sa QSharedPointer, QScopedPointer */ @@ -215,6 +215,8 @@ If \tt T is a derived type of the template parameter of this class, QSharedPointer will perform an automatic cast. Otherwise, you will get a compiler error. + + \sa QWeakPointer::toStrongRef() */ /*! @@ -367,6 +369,8 @@ Returns a weak reference object that shares the pointer referenced by this object. + + \sa QWeakPointer::QWeakPointer(const QSharedPointer<T> &) */ /*! @@ -483,10 +487,78 @@ */ /*! + \fn T *QWeakPointer::data() const + \since 4.6 + + Returns the value of the pointer being tracked by this QWeakPointer, + \b without ensuring that it cannot get deleted. To have that guarantee, + use toStrongRef(), which returns a QSharedPointer object. If this + function can determine that the pointer has already been deleted, it + returns 0. + + It is ok to obtain the value of the pointer and using that value itself, + like for example in debugging statements: + + \code + qDebug("Tracking %p", weakref.data()); + \endcode + + However, dereferencing the pointer is only allowed if you can guarantee + by external means that the pointer does not get deleted. For example, + if you can be certain that no other thread can delete it, nor the + functions that you may call. + + If that is the case, then the following code is valid: + + \code + // this pointer cannot be used in another thread + // so other threads cannot delete it + QWeakPointer<int> weakref = obtainReference(); + + Object *obj = weakref.data(); + if (obj) { + // if the pointer wasn't deleted yet, we know it can't get + // deleted by our own code here nor the functions we call + otherFunction(obj); + } + \endcode + + Use this function with care. + + \sa isNull(), toStrongRef() +*/ + +/*! \fn QSharedPointer<T> QWeakPointer::toStrongRef() const Promotes this weak reference to a strong one and returns a - QSharedPointer object holding that reference. + QSharedPointer object holding that reference. When promoting to + QSharedPointer, this function verifies if the object has been deleted + already or not. If it hasn't, this function increases the reference + count to the shared object, thus ensuring that it will not get + deleted. + + Since this function can fail to obtain a valid strong reference to the + shared object, you should always verify if the conversion succeeded, + by calling QSharedPointer::isNull() on the returned object. + + For example, the following code promotes a QWeakPointer that was held + to a strong reference and, if it succeeded, it prints the value of the + integer that was held: + + \code + QWeakPointer<int> weakref; + + // ... + + QSharedPointer<int> strong = weakref.toStrongRef(); + if (strong) + qDebug() << "The value is:" << *strong; + else + qDebug() << "The value has already been deleted"; + \endcode + + \sa QSharedPointer::QSharedPointer(const QWeakPointer<T> &) */ /*! diff --git a/src/corelib/tools/qsharedpointer_impl.h b/src/corelib/tools/qsharedpointer_impl.h index 9a5532c..25373b3 100644 --- a/src/corelib/tools/qsharedpointer_impl.h +++ b/src/corelib/tools/qsharedpointer_impl.h @@ -504,6 +504,7 @@ public: inline operator bool() const { return isNull() ? 0 : &QWeakPointer::value; } #endif inline bool operator !() const { return isNull(); } + inline T *data() const { return d == 0 || d->strongref == 0 ? 0 : value; } inline QWeakPointer() : d(0), value(0) { } inline ~QWeakPointer() { if (d && !d->weakref.deref()) delete d; } diff --git a/src/gui/dialogs/qfiledialog.cpp b/src/gui/dialogs/qfiledialog.cpp index 6198661..3f7da57 100644 --- a/src/gui/dialogs/qfiledialog.cpp +++ b/src/gui/dialogs/qfiledialog.cpp @@ -2872,7 +2872,8 @@ void QFileDialogPrivate::_q_goToDirectory(const QString &path) } const char *qt_file_dialog_filter_reg_exp = - "^([^()]*)\\(([a-zA-Z0-9_.*? +;#\\-\\[\\]@\\{\\}/!<>\\$%&=^~:\\|]*)\\)$"; +"(\\W|[a-zA-Z0-9 -]*)\\(([a-zA-Z0-9_.*? +;#\\-\\[\\]@\\{\\}/!<>\\$%&=^~:\\|]*)\\)$"; + // Makes a list of filters from a normal filter string "Image Files (*.png *.jpg)" QStringList qt_clean_filter_list(const QString &filter) diff --git a/src/gui/egl/qegl_x11.cpp b/src/gui/egl/qegl_x11.cpp index daaa4ba..6772592 100644 --- a/src/gui/egl/qegl_x11.cpp +++ b/src/gui/egl/qegl_x11.cpp @@ -39,15 +39,18 @@ ** ****************************************************************************/ +#include <QtCore/qdebug.h> + +#include <private/qt_x11_p.h> +#include <QtGui/qx11info_x11.h> +#include <private/qpixmapdata_p.h> +#include <private/qpixmap_x11_p.h> + #include <QtGui/qpaintdevice.h> #include <QtGui/qpixmap.h> #include <QtGui/qwidget.h> -#include <QtCore/qdebug.h> #include "qegl_p.h" -#include <QtGui/qx11info_x11.h> -#include <X11/Xlib.h> -#include <X11/Xutil.h> QT_BEGIN_NAMESPACE diff --git a/src/gui/embedded/qkbdqnx_qws.cpp b/src/gui/embedded/qkbdqnx_qws.cpp index 06163c7..089b868 100644 --- a/src/gui/embedded/qkbdqnx_qws.cpp +++ b/src/gui/embedded/qkbdqnx_qws.cpp @@ -6,11 +6,11 @@ ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ -** Commercial Usage -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Nokia. +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser diff --git a/src/gui/embedded/qkbdqnx_qws.h b/src/gui/embedded/qkbdqnx_qws.h index c046c8d..fa3ae56 100644 --- a/src/gui/embedded/qkbdqnx_qws.h +++ b/src/gui/embedded/qkbdqnx_qws.h @@ -6,11 +6,11 @@ ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ -** Commercial Usage -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Nokia. +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser diff --git a/src/gui/embedded/qmouseqnx_qws.cpp b/src/gui/embedded/qmouseqnx_qws.cpp index 98f8f06..59cd5be 100644 --- a/src/gui/embedded/qmouseqnx_qws.cpp +++ b/src/gui/embedded/qmouseqnx_qws.cpp @@ -6,11 +6,11 @@ ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ -** Commercial Usage -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Nokia. +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser diff --git a/src/gui/embedded/qmouseqnx_qws.h b/src/gui/embedded/qmouseqnx_qws.h index a61562e..f8cad4a 100644 --- a/src/gui/embedded/qmouseqnx_qws.h +++ b/src/gui/embedded/qmouseqnx_qws.h @@ -6,11 +6,11 @@ ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ -** Commercial Usage -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Nokia. +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser diff --git a/src/gui/embedded/qscreenqnx_qws.cpp b/src/gui/embedded/qscreenqnx_qws.cpp index 7101bc7..c79ee59 100644 --- a/src/gui/embedded/qscreenqnx_qws.cpp +++ b/src/gui/embedded/qscreenqnx_qws.cpp @@ -6,11 +6,11 @@ ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ -** Commercial Usage -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Nokia. +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser diff --git a/src/gui/embedded/qscreenqnx_qws.h b/src/gui/embedded/qscreenqnx_qws.h index 837c061..30312fe 100644 --- a/src/gui/embedded/qscreenqnx_qws.h +++ b/src/gui/embedded/qscreenqnx_qws.h @@ -6,11 +6,11 @@ ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ -** Commercial Usage -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Nokia. +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index 7e73863..d529976 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -676,19 +676,22 @@ void QGraphicsItemPrivate::updateAncestorFlag(QGraphicsItem::GraphicsItemFlag ch return; } - // Inherit the enabled-state from our parents. - if ((parent && ((parent->d_ptr->ancestorFlags & flag) - || (int(parent->d_ptr->flags & childFlag) == childFlag) + if (parent) { + // Inherit the enabled-state from our parents. + if ((parent->d_ptr->ancestorFlags & flag) + || (int(parent->d_ptr->flags & childFlag) == childFlag) || (childFlag == -1 && parent->d_ptr->handlesChildEvents) - || (childFlag == -2 && parent->d_ptr->filtersDescendantEvents)))) { - enabled = true; - ancestorFlags |= flag; - } - - // Top-level root items don't have any ancestors, so there are no - // ancestor flags either. - if (!parent) + || (childFlag == -2 && parent->d_ptr->filtersDescendantEvents)) { + enabled = true; + ancestorFlags |= flag; + } else { + ancestorFlags &= ~flag; + } + } else { + // Top-level root items don't have any ancestors, so there are no + // ancestor flags either. ancestorFlags = 0; + } } else { // Don't set or propagate the ancestor flag if it's already correct. if (((ancestorFlags & flag) && enabled) || (!(ancestorFlags & flag) && !enabled)) @@ -6384,7 +6387,7 @@ void QGraphicsItem::inputMethodEvent(QInputMethodEvent *event) surrounding text and reconversions. \a query specifies which property is queried. - \sa inputMethodEvent() + \sa inputMethodEvent(), QInputMethodEvent, QInputContext */ QVariant QGraphicsItem::inputMethodQuery(Qt::InputMethodQuery query) const { @@ -6400,6 +6403,39 @@ QVariant QGraphicsItem::inputMethodQuery(Qt::InputMethodQuery query) const } /*! + Returns the current input method hints of this item. + + Input method hints are only relevant for input items. + The hints are used by the input method to indicate how it should operate. + For example, if the Qt::ImhNumbersOnly flag is set, the input method may change + its visual components to reflect that only numbers can be entered. + + The effect may vary between input method implementations. + + \since 4.6 + + \sa setInputMethodHints(), inputMethodQuery(), QInputContext +*/ +Qt::InputMethodHints QGraphicsItem::inputMethodHints() const +{ + Q_D(const QGraphicsItem); + return d->imHints; +} + +/*! + Sets the current input method hints of this item to \a hints. + + \since 4.6 + + \sa inputMethodHints(), inputMethodQuery(), QInputContext +*/ +void QGraphicsItem::setInputMethodHints(Qt::InputMethodHints hints) +{ + Q_D(QGraphicsItem); + d->imHints = hints; +} + +/*! This virtual function is called by QGraphicsItem to notify custom items that some part of the item's state changes. By reimplementing this function, your can react to a change, and in some cases, (depending on \a diff --git a/src/gui/graphicsview/qgraphicsitem.h b/src/gui/graphicsview/qgraphicsitem.h index 2210323..6f9222e 100644 --- a/src/gui/graphicsview/qgraphicsitem.h +++ b/src/gui/graphicsview/qgraphicsitem.h @@ -376,6 +376,9 @@ public: QVariant data(int key) const; void setData(int key, const QVariant &value); + Qt::InputMethodHints inputMethodHints() const; + void setInputMethodHints(Qt::InputMethodHints hints); + enum { Type = 1, UserType = 65536 diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h index ba01b52..239c250 100644 --- a/src/gui/graphicsview/qgraphicsitem_p.h +++ b/src/gui/graphicsview/qgraphicsitem_p.h @@ -126,6 +126,7 @@ public: depth(0), focusProxy(0), subFocusItem(0), + imHints(Qt::ImhNone), acceptedMouseButtons(0x1f), visible(1), explicitlyHidden(0), @@ -419,8 +420,9 @@ public: int depth; QGraphicsItem *focusProxy; QGraphicsItem *subFocusItem; + Qt::InputMethodHints imHints; - // Packed 32 bytes + // Packed 32 bits quint32 acceptedMouseButtons : 5; quint32 visible : 1; quint32 explicitlyHidden : 1; diff --git a/src/gui/graphicsview/qgraphicstransform.cpp b/src/gui/graphicsview/qgraphicstransform.cpp index 69b6002..9e73176 100644 --- a/src/gui/graphicsview/qgraphicstransform.cpp +++ b/src/gui/graphicsview/qgraphicstransform.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Qt Software Information (qt-info@nokia.com) +** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the QtDeclarative module of the Qt Toolkit. ** @@ -34,7 +34,7 @@ ** met: http://www.gnu.org/copyleft/gpl.html. ** ** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. +** contact the sales department at http://www.qtsoftware.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/gui/itemviews/qabstractitemview.cpp b/src/gui/itemviews/qabstractitemview.cpp index afcb918..9ad9da7 100644 --- a/src/gui/itemviews/qabstractitemview.cpp +++ b/src/gui/itemviews/qabstractitemview.cpp @@ -961,6 +961,7 @@ void QAbstractItemView::reset() d->currentIndexSet = false; setState(NoState); setRootIndex(QModelIndex()); + d->selectionModel->reset(); } /*! diff --git a/src/gui/itemviews/qitemdelegate.cpp b/src/gui/itemviews/qitemdelegate.cpp index 2c3d127..82d75ba 100644 --- a/src/gui/itemviews/qitemdelegate.cpp +++ b/src/gui/itemviews/qitemdelegate.cpp @@ -1238,12 +1238,7 @@ bool QItemDelegate::eventFilter(QObject *object, QEvent *event) if (QDragManager::self() && QDragManager::self()->object != 0) return false; #endif - // Opening a modal dialog will start a new eventloop - // that will process the deleteLater event. - if (QApplication::activeModalWidget() - && !QApplication::activeModalWidget()->isAncestorOf(editor) - && qobject_cast<QDialog*>(QApplication::activeModalWidget())) - return false; + emit commitData(editor); emit closeEditor(editor, NoHint); } diff --git a/src/gui/itemviews/qstyleditemdelegate.cpp b/src/gui/itemviews/qstyleditemdelegate.cpp index 5730c86..f64a8ea 100644 --- a/src/gui/itemviews/qstyleditemdelegate.cpp +++ b/src/gui/itemviews/qstyleditemdelegate.cpp @@ -686,13 +686,7 @@ bool QStyledItemDelegate::eventFilter(QObject *object, QEvent *event) if (QDragManager::self() && QDragManager::self()->object != 0) return false; #endif - // Opening a modal dialog will start a new eventloop - // that will process the deleteLater event. - QWidget *activeModalWidget = QApplication::activeModalWidget(); - if (activeModalWidget - && !activeModalWidget->isAncestorOf(editor) - && qobject_cast<QDialog*>(activeModalWidget)) - return false; + emit commitData(editor); emit closeEditor(editor, NoHint); } diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index 7aeab39..5d1ef8c 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -4129,7 +4129,7 @@ bool QApplication::notify(QObject *receiver, QEvent *e) res = d->notify_helper(receiver, e); break; - case QEvent::WinGesture: + case QEvent::NativeGesture: { // only propagate the first gesture event (after the GID_BEGIN) QWidget *w = static_cast<QWidget *>(receiver); diff --git a/src/gui/kernel/qapplication_win.cpp b/src/gui/kernel/qapplication_win.cpp index 8eeac65..0a38870 100644 --- a/src/gui/kernel/qapplication_win.cpp +++ b/src/gui/kernel/qapplication_win.cpp @@ -3735,22 +3735,22 @@ bool QETWidget::translateGestureEvent(const MSG &msg) alienWidget = 0; QWidget *widget = alienWidget ? alienWidget : this; - QWinGestureEvent event; + QNativeGestureEvent event; event.sequenceId = gi.dwSequenceID; event.position = QPoint(gi.ptsLocation.x, gi.ptsLocation.y); if (bResult) { switch (gi.dwID) { case GID_BEGIN: - // we are not interested in this type of event. + event.gestureType = QNativeGestureEvent::GestureBegin; break; case GID_END: - event.gestureType = QWinGestureEvent::GestureEnd; + event.gestureType = QNativeGestureEvent::GestureEnd; break; case GID_ZOOM: - event.gestureType = QWinGestureEvent::Pinch; + event.gestureType = QNativeGestureEvent::Pinch; break; case GID_PAN: - event.gestureType = QWinGestureEvent::Pan; + event.gestureType = QNativeGestureEvent::Pan; break; case GID_ROTATE: case GID_TWOFINGERTAP: @@ -3758,7 +3758,7 @@ bool QETWidget::translateGestureEvent(const MSG &msg) default: break; } - if (event.gestureType != QWinGestureEvent::None) + if (event.gestureType != QNativeGestureEvent::None) qt_sendSpontaneousEvent(widget, &event); } else { DWORD dwErr = GetLastError(); diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index 2a91181..0e5d5b9 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -49,7 +49,6 @@ #include "qmime.h" #include "qdnd_p.h" #include "qevent_p.h" -#include "qgesture.h" QT_BEGIN_NAMESPACE diff --git a/src/gui/kernel/qevent_p.h b/src/gui/kernel/qevent_p.h index 67441ea..92c4fc1 100644 --- a/src/gui/kernel/qevent_p.h +++ b/src/gui/kernel/qevent_p.h @@ -119,20 +119,30 @@ public: qreal pressure; }; -class QWinGestureEvent : public QEvent +class QNativeGestureEvent : public QEvent { public: enum Type { None, + GestureBegin, GestureEnd, Pan, Pinch }; - QWinGestureEvent() : QEvent(QEvent::WinGesture), gestureType(None), sequenceId(0) { } + QNativeGestureEvent() + : QEvent(QEvent::NativeGesture), gestureType(None) +#ifdef Q_WS_WIN + , sequenceId(0) +#endif + { + } + Type gestureType; +#ifdef Q_WS_WIN QPoint position; ulong sequenceId; +#endif }; QT_END_NAMESPACE diff --git a/src/gui/kernel/qgesture.cpp b/src/gui/kernel/qgesture.cpp index 1f98013..32ac4f8 100644 --- a/src/gui/kernel/qgesture.cpp +++ b/src/gui/kernel/qgesture.cpp @@ -205,51 +205,6 @@ void QGesture::setState(Qt::GestureState state) } /*! - \property QGesture::startPos - - \brief The start position of the gesture (if relevant). -*/ -QPoint QGesture::startPos() const -{ - return d_func()->startPos; -} - -void QGesture::setStartPos(const QPoint &point) -{ - d_func()->startPos = point; -} - -/*! - \property QGesture::lastPos - - \brief The last recorded position of the gesture (if relevant). -*/ -QPoint QGesture::lastPos() const -{ - return d_func()->lastPos; -} - -void QGesture::setLastPos(const QPoint &point) -{ - d_func()->lastPos = point; -} - -/*! - \property QGesture::pos - - \brief The current position of the gesture (if relevant). -*/ -QPoint QGesture::pos() const -{ - return d_func()->pos; -} - -void QGesture::setPos(const QPoint &point) -{ - d_func()->pos = point; -} - -/*! Sets the \a graphicsItem the gesture is filtering events for. The gesture will install an event filter to the \a graphicsItem and diff --git a/src/gui/kernel/qgesture.h b/src/gui/kernel/qgesture.h index 8390d11..a4bd2c2 100644 --- a/src/gui/kernel/qgesture.h +++ b/src/gui/kernel/qgesture.h @@ -64,10 +64,6 @@ class Q_GUI_EXPORT QGesture : public QObject Q_PROPERTY(Qt::GestureState state READ state) - Q_PROPERTY(QPoint startPos READ startPos WRITE setStartPos) - Q_PROPERTY(QPoint lastPos READ lastPos WRITE setLastPos) - Q_PROPERTY(QPoint pos READ pos WRITE setPos) - public: explicit QGesture(QObject *parent = 0); ~QGesture(); @@ -80,19 +76,13 @@ public: virtual void reset(); Qt::GestureState state() const; - void setState(Qt::GestureState state); - - QPoint startPos() const; - void setStartPos(const QPoint &point); - QPoint lastPos() const; - void setLastPos(const QPoint &point); - QPoint pos() const; - void setPos(const QPoint &point); protected: QGesture(QGesturePrivate &dd, QObject *parent); bool eventFilter(QObject*, QEvent*); + void setState(Qt::GestureState state); + Q_SIGNALS: void started(); void triggered(); diff --git a/src/gui/kernel/qgesture_p.h b/src/gui/kernel/qgesture_p.h index 99f572f..56eaee7 100644 --- a/src/gui/kernel/qgesture_p.h +++ b/src/gui/kernel/qgesture_p.h @@ -73,22 +73,11 @@ public: { } - void init(const QPoint &startPos, const QPoint &lastPos, - const QPoint &pos) - { - this->startPos = startPos; - this->lastPos = lastPos; - this->pos = pos; - } QGraphicsItem *graphicsItem; QGraphicsItem *eventFilterProxyGraphicsItem; Qt::GestureState state; - - QPoint startPos; - QPoint lastPos; - QPoint pos; }; QT_END_NAMESPACE diff --git a/src/gui/kernel/qstandardgestures.cpp b/src/gui/kernel/qstandardgestures.cpp index c8b11c5..4753416 100644 --- a/src/gui/kernel/qstandardgestures.cpp +++ b/src/gui/kernel/qstandardgestures.cpp @@ -45,9 +45,14 @@ #include <qabstractscrollarea.h> #include <qscrollbar.h> #include <private/qapplication_p.h> +#include <private/qevent_p.h> QT_BEGIN_NAMESPACE +#ifdef Q_WS_WIN +QApplicationPrivate* getQApplicationPrivateInternal(); +#endif + /*! \class QPanGesture \since 4.6 @@ -68,7 +73,6 @@ QPanGesture::QPanGesture(QWidget *parent) { #ifdef Q_WS_WIN if (parent) { - QApplicationPrivate* getQApplicationPrivateInternal(); QApplicationPrivate *qAppPriv = getQApplicationPrivateInternal(); qAppPriv->widgetGestures[parent].pan = this; } @@ -93,9 +97,81 @@ bool QPanGesture::event(QEvent *event) break; } #endif + +#if defined(Q_OS_MAC) && !defined(QT_MAC_USE_COCOA) + Q_D(QPanGesture); + if (event->type() == QEvent::Timer) { + const QTimerEvent *te = static_cast<QTimerEvent *>(event); + if (te->timerId() == d->panFinishedTimer) { + killTimer(d->panFinishedTimer); + d->panFinishedTimer = 0; + d->lastOffset = QSize(0, 0); + setState(Qt::GestureFinished); + emit triggered(); + setState(Qt::NoGesture); + } + } +#endif + return QObject::event(event); } +bool QPanGesture::eventFilter(QObject *receiver, QEvent *event) +{ +#ifdef Q_WS_WIN + if (receiver->isWidgetType() && event->type() == QEvent::NativeGesture) { + QNativeGestureEvent *ev = static_cast<QNativeGestureEvent*>(event); + QApplicationPrivate *qAppPriv = getQApplicationPrivateInternal(); + QApplicationPrivate::WidgetStandardGesturesMap::iterator it; + it = qAppPriv->widgetGestures.find(static_cast<QWidget*>(receiver)); + if (it == qAppPriv->widgetGestures.end()) + return false; + QPanGesture *gesture = it.value().pan; + if (!gesture) + return false; + Qt::GestureState nextState = state(); + switch(ev->gestureType) { + case QNativeGestureEvent::GestureBegin: + // next we might receive the first gesture update event, so we + // prepare for it. + setState(Qt::GestureStarted); + return false; + case QNativeGestureEvent::Pan: + nextState = Qt::GestureUpdated; + break; + case QNativeGestureEvent::GestureEnd: + if (state() != QNativeGestureEvent::Pan) + return false; // some other gesture has ended + setState(Qt::GestureFinished); + nextState = Qt::GestureFinished; + break; + default: + return false; + } + QPanGesturePrivate *d = gesture->d_func(); + if (state() == Qt::GestureStarted) { + d->lastPosition = ev->position; + d->lastOffset = d->totalOffset = QSize(); + } else { + d->lastOffset = QSize(ev->position.x() - d->lastPosition.x(), + ev->position.y() - d->lastPosition.y()); + d->totalOffset += d->lastOffset; + } + d->lastPosition = ev->position; + + if (state() == Qt::GestureStarted) + emit gesture->started(); + emit gesture->triggered(); + if (state() == Qt::GestureFinished) + emit gesture->finished(); + event->accept(); + gesture->setState(nextState); + return true; + } +#endif + return QGesture::eventFilter(receiver, event); +} + /*! \internal */ bool QPanGesture::filterEvent(QEvent *event) { @@ -104,28 +180,34 @@ bool QPanGesture::filterEvent(QEvent *event) return false; const QTouchEvent *ev = static_cast<const QTouchEvent*>(event); if (event->type() == QEvent::TouchBegin) { - d->touchPoints = ev->touchPoints(); - const QPoint p = ev->touchPoints().at(0).pos().toPoint(); - setStartPos(p); - setLastPos(p); - setPos(p); - return false; + QTouchEvent::TouchPoint p = ev->touchPoints().at(0); + d->lastPosition = p.pos().toPoint(); + d->lastOffset = d->totalOffset = QSize(); } else if (event->type() == QEvent::TouchEnd) { if (state() != Qt::NoGesture) { setState(Qt::GestureFinished); - setLastPos(pos()); - setPos(ev->touchPoints().at(0).pos().toPoint()); + if (!ev->touchPoints().isEmpty()) { + QTouchEvent::TouchPoint p = ev->touchPoints().at(0); + const QPoint pos = p.pos().toPoint(); + const QPoint lastPos = p.lastPos().toPoint(); + const QPoint startPos = p.startPos().toPoint(); + d->lastOffset = QSize(pos.x() - lastPos.x(), pos.y() - lastPos.y()); + d->totalOffset = QSize(pos.x() - startPos.x(), pos.y() - startPos.y()); + } emit triggered(); emit finished(); } setState(Qt::NoGesture); reset(); } else if (event->type() == QEvent::TouchUpdate) { - d->touchPoints = ev->touchPoints(); - QPointF pt = d->touchPoints.at(0).pos() - d->touchPoints.at(0).startPos(); - setLastPos(pos()); - setPos(ev->touchPoints().at(0).pos().toPoint()); - if (pt.x() > 10 || pt.y() > 10 || pt.x() < -10 || pt.y() < -10) { + QTouchEvent::TouchPoint p = ev->touchPoints().at(0); + const QPoint pos = p.pos().toPoint(); + const QPoint lastPos = p.lastPos().toPoint(); + const QPoint startPos = p.startPos().toPoint(); + d->lastOffset = QSize(pos.x() - lastPos.x(), pos.y() - lastPos.y()); + d->totalOffset = QSize(pos.x() - startPos.x(), pos.y() - startPos.y()); + if (d->totalOffset.width() > 10 || d->totalOffset.height() > 10 || + d->totalOffset.width() < -10 || d->totalOffset.height() < -10) { if (state() == Qt::NoGesture) setState(Qt::GestureStarted); else @@ -133,6 +215,36 @@ bool QPanGesture::filterEvent(QEvent *event) emit triggered(); } } +#ifdef Q_OS_MAC + else if (event->type() == QEvent::Wheel) { + // On Mac, there is really no native panning gesture. Instead, a two + // finger pan is delivered as mouse wheel events. Otoh, on Windows, you + // either get mouse wheel events or pan events. We have decided to make this + // the Qt behaviour as well, meaning that on Mac, wheel + // events will be masked away when listening for pan events. +#ifndef QT_MAC_USE_COCOA + // In Carbon we receive neither touch-, nor pan gesture events. + // So we create pan gestures by converting wheel events. After all, this + // is how things are supposed to work on mac in the first place. + const QWheelEvent *wev = static_cast<const QWheelEvent*>(event); + int offset = wev->delta() / -120; + d->lastOffset = wev->orientation() == Qt::Horizontal ? QSize(offset, 0) : QSize(0, offset); + + if (state() == Qt::NoGesture) { + setState(Qt::GestureStarted); + d->totalOffset = d->lastOffset; + } else { + setState(Qt::GestureUpdated); + d->totalOffset += d->lastOffset; + } + + killTimer(d->panFinishedTimer); + d->panFinishedTimer = startTimer(200); + emit triggered(); +#endif + return true; + } +#endif return false; } @@ -140,7 +252,14 @@ bool QPanGesture::filterEvent(QEvent *event) void QPanGesture::reset() { Q_D(QPanGesture); - d->touchPoints.clear(); + d->lastOffset = d->totalOffset = QSize(); + d->lastPosition = QPoint(); +#if defined(Q_OS_MAC) && !defined(QT_MAC_USE_COCOA) + if (d->panFinishedTimer) { + killTimer(d->panFinishedTimer); + d->panFinishedTimer = 0; + } +#endif } /*! @@ -150,8 +269,8 @@ void QPanGesture::reset() */ QSize QPanGesture::totalOffset() const { - QPoint pt = pos() - startPos(); - return QSize(pt.x(), pt.y()); + Q_D(const QPanGesture); + return d->totalOffset; } /*! @@ -162,93 +281,11 @@ QSize QPanGesture::totalOffset() const */ QSize QPanGesture::lastOffset() const { - QPoint pt = pos() - lastPos(); - return QSize(pt.x(), pt.y()); -} - -/*! - \class QTapAndHoldGesture - \since 4.6 - - \brief The QTapAndHoldGesture class represents a Tap-and-Hold gesture, - providing additional information. -*/ - -const int QTapAndHoldGesturePrivate::iterationCount = 40; -const int QTapAndHoldGesturePrivate::iterationTimeout = 50; - -/*! - Creates a new Tap and Hold gesture handler object and marks it as a child - of \a parent. - - On some platforms like Windows there is a system-wide tap and hold gesture - that cannot be overriden, hence the gesture might never trigger and default - context menu will be shown instead. -*/ -QTapAndHoldGesture::QTapAndHoldGesture(QWidget *parent) - : QGesture(*new QTapAndHoldGesturePrivate, parent) -{ -} - -/*! \internal */ -bool QTapAndHoldGesture::filterEvent(QEvent *event) -{ - Q_D(QTapAndHoldGesture); - if (!event->spontaneous()) - return false; - const QTouchEvent *ev = static_cast<const QTouchEvent*>(event); - switch (event->type()) { - case QEvent::TouchBegin: { - if (d->timer.isActive()) - d->timer.stop(); - d->timer.start(QTapAndHoldGesturePrivate::iterationTimeout, this); - const QPoint p = ev->touchPoints().at(0).pos().toPoint(); - setStartPos(p); - setLastPos(p); - setPos(p); - break; - } - case QEvent::TouchUpdate: - if (ev->touchPoints().size() != 1) - reset(); - else if ((startPos() - ev->touchPoints().at(0).pos().toPoint()).manhattanLength() > 15) - reset(); - break; - case QEvent::TouchEnd: - reset(); - break; - default: - break; - } - return false; + Q_D(const QPanGesture); + return d->lastOffset; } -/*! \internal */ -void QTapAndHoldGesture::timerEvent(QTimerEvent *event) -{ - Q_D(QTapAndHoldGesture); - if (event->timerId() != d->timer.timerId()) - return; - if (d->iteration == QTapAndHoldGesturePrivate::iterationCount) { - d->timer.stop(); - setState(Qt::GestureFinished); - emit triggered(); - } else { - setState(Qt::GestureStarted); - emit triggered(); - } - ++d->iteration; -} +QT_END_NAMESPACE -/*! \internal */ -void QTapAndHoldGesture::reset() -{ - Q_D(QTapAndHoldGesture); - if (state() != Qt::NoGesture) - emit cancelled(); - setState(Qt::NoGesture); - d->timer.stop(); - d->iteration = 0; -} +#include "moc_qstandardgestures.cpp" -QT_END_NAMESPACE diff --git a/src/gui/kernel/qstandardgestures.h b/src/gui/kernel/qstandardgestures.h index a777253..fc7cb00 100644 --- a/src/gui/kernel/qstandardgestures.h +++ b/src/gui/kernel/qstandardgestures.h @@ -71,29 +71,13 @@ public: QSize totalOffset() const; QSize lastOffset() const; -protected: +private: bool event(QEvent *event); + bool eventFilter(QObject *receiver, QEvent *event); -private: friend class QWidget; }; -class QTapAndHoldGesturePrivate; -class Q_GUI_EXPORT QTapAndHoldGesture : public QGesture -{ - Q_OBJECT - Q_DECLARE_SCOPED_PRIVATE(QTapAndHoldGesture) - -public: - QTapAndHoldGesture(QWidget *parent); - - bool filterEvent(QEvent *event); - void reset(); - -protected: - void timerEvent(QTimerEvent *event); -}; - QT_END_NAMESPACE QT_END_HEADER diff --git a/src/gui/kernel/qstandardgestures_p.h b/src/gui/kernel/qstandardgestures_p.h index bb11c9f..0fe24ee 100644 --- a/src/gui/kernel/qstandardgestures_p.h +++ b/src/gui/kernel/qstandardgestures_p.h @@ -60,6 +60,8 @@ #include "qgesture.h" #include "qgesture_p.h" +#include "qstandardgestures.h" + QT_BEGIN_NAMESPACE class QPanGesturePrivate : public QGesturePrivate @@ -67,23 +69,20 @@ class QPanGesturePrivate : public QGesturePrivate Q_DECLARE_PUBLIC(QPanGesture) public: - QPanGesturePrivate() { } - - QList<QTouchEvent::TouchPoint> touchPoints; -}; + QPanGesturePrivate() + { +#if defined(Q_OS_MAC) && !defined(QT_MAC_USE_COCOA) + panFinishedTimer = 0; +#endif + } -class QTapAndHoldGesturePrivate : public QGesturePrivate -{ - Q_DECLARE_PUBLIC(QTapAndHoldGesture) - -public: - QTapAndHoldGesturePrivate() - : iteration(0) { } + QSize totalOffset; + QSize lastOffset; + QPoint lastPosition; - QBasicTimer timer; - int iteration; - static const int iterationCount; - static const int iterationTimeout; +#if defined(Q_OS_MAC) && !defined(QT_MAC_USE_COCOA) + int panFinishedTimer; +#endif }; QT_END_NAMESPACE diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp index e990358..a8157d5 100644 --- a/src/gui/kernel/qwidget.cpp +++ b/src/gui/kernel/qwidget.cpp @@ -1454,10 +1454,9 @@ QWidget::~QWidget() // set all QPointers for this object to zero QObjectPrivate::clearGuards(this); - if(d->declarativeData) { - QDeclarativeData *dd = d->declarativeData; - d->declarativeData = 0; - dd->destroyed(this); + if (d->declarativeData) { + d->declarativeData->destroyed(this); + d->declarativeData = 0; // don't activate again in ~QObject } if (!d->children.isEmpty()) @@ -8070,59 +8069,6 @@ bool QWidget::event(QEvent *event) #endif break; } -#ifdef Q_WS_WIN - case QEvent::WinGesture: { - QWinGestureEvent *ev = static_cast<QWinGestureEvent*>(event); - QApplicationPrivate *qAppPriv = qApp->d_func(); - QApplicationPrivate::WidgetStandardGesturesMap::iterator it; - it = qAppPriv->widgetGestures.find(this); - if (it != qAppPriv->widgetGestures.end()) { - Qt::GestureState state = Qt::GestureUpdated; - if (qAppPriv->lastGestureId == 0) - state = Qt::GestureStarted; - QWinGestureEvent::Type type = ev->gestureType; - if (ev->gestureType == QWinGestureEvent::GestureEnd) { - type = (QWinGestureEvent::Type)qAppPriv->lastGestureId; - state = Qt::GestureFinished; - } - - QGesture *gesture = 0; - switch (type) { - case QWinGestureEvent::Pan: { - QPanGesture *pan = it.value().pan; - gesture = pan; - if (state == Qt::GestureStarted) { - gesture->setStartPos(ev->position); - gesture->setLastPos(ev->position); - } else { - gesture->setLastPos(gesture->pos()); - } - gesture->setPos(ev->position); - break; - } - case QWinGestureEvent::Pinch: - break; - default: - break; - } - if (gesture) { - gesture->setState(state); - if (state == Qt::GestureStarted) - emit gesture->started(); - emit gesture->triggered(); - if (state == Qt::GestureFinished) - emit gesture->finished(); - event->accept(); - } - if (ev->gestureType == QWinGestureEvent::GestureEnd) { - qAppPriv->lastGestureId = 0; - } else { - qAppPriv->lastGestureId = type; - } - } - break; - } -#endif #ifndef QT_NO_PROPERTIES case QEvent::DynamicPropertyChange: { const QByteArray &propName = static_cast<QDynamicPropertyChangeEvent *>(event)->propertyName(); diff --git a/src/gui/painting/qoutlinemapper.cpp b/src/gui/painting/qoutlinemapper.cpp index 70125b9..1287672 100644 --- a/src/gui/painting/qoutlinemapper.cpp +++ b/src/gui/painting/qoutlinemapper.cpp @@ -201,6 +201,8 @@ void QOutlineMapper::endOutline() const QVectorPath vp((qreal *)m_elements.data(), m_elements.size(), m_element_types.data()); QPainterPath path = vp.convertToPainterPath(); path = QTransform(m_m11, m_m12, m_m13, m_m21, m_m22, m_m23, m_dx, m_dy, m_m33).map(path); + if (!(m_outline.flags & QT_FT_OUTLINE_EVEN_ODD_FILL)) + path.setFillRule(Qt::WindingFill); uint old_txop = m_txop; m_txop = QTransform::TxNone; if (path.isEmpty()) diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index e4c6f57..fa71af3 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -1672,7 +1672,7 @@ bool QPainter::begin(QPaintDevice *pd) if (!d->engine) { qWarning("QPainter::begin: Paint device returned engine == 0, type: %d", pd->devType()); - return true; + return false; } // Slip a painter state into the engine before we do any other operations diff --git a/src/gui/painting/qregion_mac.cpp b/src/gui/painting/qregion_mac.cpp index 9b0e99f..6fe7805 100644 --- a/src/gui/painting/qregion_mac.cpp +++ b/src/gui/painting/qregion_mac.cpp @@ -173,7 +173,6 @@ RgnHandle QRegion::toQDRgnForUpdate_sys() const // detect overflow. Tested for use with HIViewSetNeedsDisplayInRegion // in QWidgetPrivate::update_sys(). enum { HIViewSetNeedsDisplayInRegionOverflow = 10000 }; // empirically determined conservative value - qDebug() << qt_r->x() << qt_r->y() << qt_r->right() << qt_r->bottom(); if (qt_r->right() > HIViewSetNeedsDisplayInRegionOverflow || qt_r->bottom() > HIViewSetNeedsDisplayInRegionOverflow) { qt_mac_dispose_rgn(tmp_rgn); qt_mac_dispose_rgn(rgnHandle); diff --git a/src/gui/painting/qregion_win.cpp b/src/gui/painting/qregion_win.cpp index 2d5e76b..8708461 100644 --- a/src/gui/painting/qregion_win.cpp +++ b/src/gui/painting/qregion_win.cpp @@ -57,7 +57,7 @@ HRGN qt_tryCreateRegion(QRegion::RegionType type, int left, int top, int right, { const int tries = 10; for (int i = 0; i < tries; ++i) { - HRGN region; + HRGN region = 0; switch (type) { case QRegion::Rectangle: region = CreateRectRgn(left, top, right, bottom); @@ -96,7 +96,7 @@ QRegion qt_region_from_HRGN(HRGN rgn) QRegion region; RECT *r = reinterpret_cast<RECT*>(rd->Buffer); - for (int i = 0; i < rd->rdh.nCount; ++i) { + for (uint i = 0; i < rd->rdh.nCount; ++i) { QRect rect; rect.setCoords(r->left, r->top, r->right - 1, r->bottom - 1); ++r; diff --git a/src/gui/painting/qtransform.cpp b/src/gui/painting/qtransform.cpp index 0a64e4e..a13b494 100644 --- a/src/gui/painting/qtransform.cpp +++ b/src/gui/painting/qtransform.cpp @@ -1541,6 +1541,7 @@ static QPainterPath mapProjective(const QTransform &transform, const QPainterPat if (path.elementCount() > 0 && lastMoveTo != last) lineTo_clipped(result, transform, last, lastMoveTo, needsMoveTo, false); + result.setFillRule(path.fillRule()); return result; } diff --git a/src/gui/styles/gtksymbols.cpp b/src/gui/styles/gtksymbols.cpp index c2c7876..f947ac1 100644 --- a/src/gui/styles/gtksymbols.cpp +++ b/src/gui/styles/gtksymbols.cpp @@ -752,7 +752,24 @@ static void setupGtkFileChooser(GtkWidget* gtkFileChooser, QWidget *parent, QGtk::gtk_file_filter_set_name(gtkFilter, qPrintable(name.isEmpty() ? extensions.join(QLS(", ")) : name)); foreach (const QString &fileExtension, extensions) { - QGtk::gtk_file_filter_add_pattern (gtkFilter, qPrintable(fileExtension)); + // Note Gtk file dialogs are by default case sensitive + // and only supports basic glob syntax so we + // rewrite .xyz to .[xX][yY][zZ] + QString caseInsensitive; + for (int i = 0 ; i < fileExtension.length() ; ++i) { + QChar ch = fileExtension.at(i); + if (ch.isLetter()) { + caseInsensitive.append( + QLatin1Char('[') + + ch.toLower() + + ch.toUpper() + + QLatin1Char(']')); + } else { + caseInsensitive.append(ch); + } + } + QGtk::gtk_file_filter_add_pattern (gtkFilter, qPrintable(caseInsensitive)); + } if (filterMap) filterMap->insert(gtkFilter, rawfilter); diff --git a/src/gui/util/qcompleter.cpp b/src/gui/util/qcompleter.cpp index 80678aa..29763e2 100644 --- a/src/gui/util/qcompleter.cpp +++ b/src/gui/util/qcompleter.cpp @@ -771,7 +771,7 @@ QMatchData QUnsortedModelEngine::filter(const QString& part, const QModelIndex& /////////////////////////////////////////////////////////////////////////////// QCompleterPrivate::QCompleterPrivate() : widget(0), proxy(0), popup(0), cs(Qt::CaseSensitive), role(Qt::EditRole), column(0), - sorting(QCompleter::UnsortedModel), wrap(true), maxVisibleItems(7), eatFocusOut(true) + maxVisibleItems(7), sorting(QCompleter::UnsortedModel), wrap(true), eatFocusOut(true) { } diff --git a/src/gui/widgets/qlinecontrol.cpp b/src/gui/widgets/qlinecontrol.cpp index 90ec1e3..64832c8 100644 --- a/src/gui/widgets/qlinecontrol.cpp +++ b/src/gui/widgets/qlinecontrol.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Qt Software Information (qt-info@nokia.com) +** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the QtGui module of the Qt Toolkit. ** @@ -34,7 +34,7 @@ ** met: http://www.gnu.org/copyleft/gpl.html. ** ** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. +** contact the sales department at http://www.qtsoftware.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/gui/widgets/qlineedit_p.cpp b/src/gui/widgets/qlineedit_p.cpp index 0e39304..f0ec8ad 100644 --- a/src/gui/widgets/qlineedit_p.cpp +++ b/src/gui/widgets/qlineedit_p.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Qt Software Information (qt-info@nokia.com) +** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the QtGui module of the Qt Toolkit. ** @@ -34,7 +34,7 @@ ** met: http://www.gnu.org/copyleft/gpl.html. ** ** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. +** contact the sales department at http://www.qtsoftware.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/gui/widgets/qmenu.cpp b/src/gui/widgets/qmenu.cpp index f130a29..4b48fce 100644 --- a/src/gui/widgets/qmenu.cpp +++ b/src/gui/widgets/qmenu.cpp @@ -229,6 +229,10 @@ void QMenuPrivate::updateActionRects() const const int hmargin = style->pixelMetric(QStyle::PM_MenuHMargin, 0, q), vmargin = style->pixelMetric(QStyle::PM_MenuVMargin, 0, q), icone = style->pixelMetric(QStyle::PM_SmallIconSize, 0, q); + const int fw = style->pixelMetric(QStyle::PM_MenuPanelWidth, 0, q); + + const int sfcMargin = style->sizeFromContents(QStyle::CT_Menu, 0, QApplication::globalStrut(), q).width() - QApplication::globalStrut().width(); + const int min_column_width = q->minimumWidth() - (sfcMargin + leftmargin + rightmargin + 2 * (fw + hmargin)); //for compatability now - will have to refactor this away.. tabWidth = 0; @@ -301,7 +305,7 @@ void QMenuPrivate::updateActionRects() const if (!sz.isEmpty()) { - max_column_width = qMax(max_column_width, sz.width()); + max_column_width = qMax(min_column_width, qMax(max_column_width, sz.width())); //wrapping if (!scroll && y+sz.height()+vmargin > dh - (style->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, 0, q) * 2)) { @@ -317,7 +321,6 @@ void QMenuPrivate::updateActionRects() const max_column_width += tabWidth; //finally add in the tab width //calculate position - const int fw = style->pixelMetric(QStyle::PM_MenuPanelWidth, 0, q); const int base_y = vmargin + fw + topmargin + (scroll ? scroll->scrollOffset : 0) + (tearoff ? style->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, q) : 0); @@ -1710,9 +1713,7 @@ QSize QMenu::sizeHint() const QSize s; QStyleOption opt(0); - opt.rect = rect(); - opt.palette = palette(); - opt.state = QStyle::State_None; + opt.init(this); for (int i = 0; i < d->actionRects.count(); ++i) { const QRect &rect = d->actionRects.at(i); if (rect.isNull()) diff --git a/src/gui/widgets/qtextedit.cpp b/src/gui/widgets/qtextedit.cpp index 2de661a..e5c4d4f 100644 --- a/src/gui/widgets/qtextedit.cpp +++ b/src/gui/widgets/qtextedit.cpp @@ -2639,12 +2639,12 @@ void QTextEditPrivate::_q_gestureTriggered() return; QScrollBar *hBar = q->horizontalScrollBar(); QScrollBar *vBar = q->verticalScrollBar(); - QPoint delta = g->pos() - (g->lastPos().isNull() ? g->pos() : g->lastPos()); + QSize delta = g->lastOffset(); if (!delta.isNull()) { if (QApplication::isRightToLeft()) - delta.rx() *= -1; - int newX = hBar->value() - delta.x(); - int newY = vBar->value() - delta.y(); + delta.rwidth() *= -1; + int newX = hBar->value() - delta.width(); + int newY = vBar->value() - delta.height(); hbar->setValue(newX); vbar->setValue(newY); } diff --git a/src/multimedia/audio/audio.pri b/src/multimedia/audio/audio.pri new file mode 100644 index 0000000..3ddb23b --- /dev/null +++ b/src/multimedia/audio/audio.pri @@ -0,0 +1,56 @@ +HEADERS += $$PWD/qaudio.h \ + $$PWD/qaudioformat.h \ + $$PWD/qaudioinput.h \ + $$PWD/qaudiooutput.h \ + $$PWD/qaudiodeviceinfo.h \ + $$PWD/qaudioengineplugin.h \ + $$PWD/qaudioengine.h \ + $$PWD/qaudiodevicefactory_p.h \ + $$PWD/qaudiodeviceid.h \ + $$PWD/qaudiodeviceid_p.h + + +SOURCES += $$PWD/qaudio.cpp \ + $$PWD/qaudioformat.cpp \ + $$PWD/qaudiodeviceinfo.cpp \ + $$PWD/qaudiooutput.cpp \ + $$PWD/qaudioinput.cpp \ + $$PWD/qaudioengineplugin.cpp \ + $$PWD/qaudioengine.cpp \ + $$PWD/qaudiodevicefactory.cpp \ + $$PWD/qaudiodeviceid.cpp + +mac { + HEADERS += $$PWD/qaudioinput_mac_p.h \ + $$PWD/qaudiooutput_mac_p.h \ + $$PWD/qaudiodeviceinfo_mac_p.h \ + $$PWD/qaudio_mac_p.h + + SOURCES += $$PWD/qaudiodeviceinfo_mac_p.cpp \ + $$PWD/qaudiooutput_mac_p.cpp \ + $$PWD/qaudioinput_mac_p.cpp \ + $$PWD/qaudio_mac.cpp + + LIBS += -framework CoreAudio -framework AudioUnit -framework AudioToolbox + +} else:win32 { + + HEADERS += $$PWD/qaudioinput_win32_p.h $$PWD/qaudiooutput_win32_p.h $$PWD/qaudiodeviceinfo_win32_p.h + SOURCES += $$PWD/qaudiodeviceinfo_win32_p.cpp \ + $$PWD/qaudiooutput_win32_p.cpp \ + $$PWD/qaudioinput_win32_p.cpp + !wince*:LIBS += -lwinmm + wince*:LIBS += -lcoredll + +} else:unix { + unix:contains(QT_CONFIG, alsa) { + linux-*|freebsd-*|openbsd-*:{ + DEFINES += HAS_ALSA + HEADERS += $$PWD/qaudiooutput_alsa_p.h $$PWD/qaudioinput_alsa_p.h $$PWD/qaudiodeviceinfo_alsa_p.h + SOURCES += $$PWD/qaudiodeviceinfo_alsa_p.cpp \ + $$PWD/qaudiooutput_alsa_p.cpp \ + $$PWD/qaudioinput_alsa_p.cpp + LIBS += -lasound + } + } +} diff --git a/src/multimedia/audio/qaudio.cpp b/src/multimedia/audio/qaudio.cpp new file mode 100644 index 0000000..5ba6493 --- /dev/null +++ b/src/multimedia/audio/qaudio.cpp @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtMultimedia/qaudio.h> + + +QT_BEGIN_NAMESPACE + +namespace QAudio +{ + +class RegisterMetaTypes +{ +public: + RegisterMetaTypes() + { + qRegisterMetaType<QAudio::Error>(); + qRegisterMetaType<QAudio::State>(); + qRegisterMetaType<QAudio::Mode>(); + } + +} _register; + +} + +/*! + \namespace QAudio + \brief The QAudio namespace contains enums used by the audio classes. + \since 4.6 +*/ + +/*! + \enum QAudio::Error + + \value NoError No errors have occurred + \value OpenError An error opening the audio device + \value IOError An error occurred during read/write of audio device + \value UnderrunError Audio data is not being fed to the audio device at a fast enough rate + \value FatalError A non-recoverable error has occurred, the audio device is not usable at this time. +*/ + +/*! + \enum QAudio::State + + \value ActiveState Audio data is being processed, this state is set after start() is called + and while audio data is available to be processed. + \value SuspendState The audio device is in a suspended state, this state will only be entered + after suspend() is called. + \value StopState The audio device is closed, not processing any audio data + \value IdleState The QIODevice passed in has no data and audio system's buffer is empty, this state + is set after start() is called and while no audio data is available to be processed. +*/ + +/*! + \enum QAudio::Mode + + \value AudioOutput audio output device + \value AudioInput audio input device +*/ + + +QT_END_NAMESPACE + diff --git a/src/multimedia/audio/qaudio.h b/src/multimedia/audio/qaudio.h new file mode 100644 index 0000000..b996cc6 --- /dev/null +++ b/src/multimedia/audio/qaudio.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QAUDIO_H +#define QAUDIO_H + + +#include <QtCore/qglobal.h> +#include <QtCore/qmetatype.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + +namespace QAudio +{ + enum Error { NoError, OpenError, IOError, UnderrunError, FatalError }; + enum State { ActiveState, SuspendState, StopState, IdleState }; + enum Mode { AudioInput, AudioOutput }; +} + +QT_END_NAMESPACE + +QT_END_HEADER + +Q_DECLARE_METATYPE(QAudio::Error); +Q_DECLARE_METATYPE(QAudio::State); +Q_DECLARE_METATYPE(QAudio::Mode); + +#endif // QAUDIO_H diff --git a/src/multimedia/audio/qaudio_mac.cpp b/src/multimedia/audio/qaudio_mac.cpp new file mode 100644 index 0000000..cfa7ca3 --- /dev/null +++ b/src/multimedia/audio/qaudio_mac.cpp @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qaudio_mac_p.h" + +QT_BEGIN_NAMESPACE + +// Debugging +QDebug operator<<(QDebug dbg, const QAudioFormat& audioFormat) +{ + dbg.nospace() << "QAudioFormat(" << + audioFormat.frequency() << "," << + audioFormat.channels() << "," << + audioFormat.sampleSize()<< "," << + audioFormat.codec() << "," << + audioFormat.byteOrder() << "," << + audioFormat.sampleType() << ")"; + + return dbg.space(); +} + + +// Conversion +QAudioFormat toQAudioFormat(AudioStreamBasicDescription const& sf) +{ + QAudioFormat audioFormat; + + audioFormat.setFrequency(sf.mSampleRate); + audioFormat.setChannels(sf.mChannelsPerFrame); + audioFormat.setSampleSize(sf.mBitsPerChannel); + audioFormat.setCodec(QString::fromLatin1("audio/pcm")); + audioFormat.setByteOrder(sf.mFormatFlags & kLinearPCMFormatFlagIsBigEndian != 0 ? QAudioFormat::BigEndian : QAudioFormat::LittleEndian); + QAudioFormat::SampleType type = QAudioFormat::UnSignedInt; + if ((sf.mFormatFlags & kLinearPCMFormatFlagIsSignedInteger) != 0) + type = QAudioFormat::SignedInt; + else if ((sf.mFormatFlags & kLinearPCMFormatFlagIsFloat) != 0) + type = QAudioFormat::Float; + audioFormat.setSampleType(type); + + return audioFormat; +} + +AudioStreamBasicDescription toAudioStreamBasicDescription(QAudioFormat const& audioFormat) +{ + AudioStreamBasicDescription sf; + + sf.mFormatFlags = kAudioFormatFlagIsPacked; + sf.mSampleRate = audioFormat.frequency(); + sf.mFramesPerPacket = 1; + sf.mChannelsPerFrame = audioFormat.channels(); + sf.mBitsPerChannel = audioFormat.sampleSize(); + sf.mBytesPerFrame = sf.mChannelsPerFrame * (sf.mBitsPerChannel / 8); + sf.mBytesPerPacket = sf.mFramesPerPacket * sf.mBytesPerFrame; + sf.mFormatID = kAudioFormatLinearPCM; + + switch (audioFormat.sampleType()) { + case QAudioFormat::SignedInt: sf.mFormatFlags |= kAudioFormatFlagIsSignedInteger; break; + case QAudioFormat::UnSignedInt: /* default */ break; + case QAudioFormat::Float: sf.mFormatFlags |= kAudioFormatFlagIsFloat; break; + case QAudioFormat::Unknown: default: break; + } + + return sf; +} + +// QAudioRingBuffer +QAudioRingBuffer::QAudioRingBuffer(int bufferSize): + m_bufferSize(bufferSize) +{ + m_buffer = new char[m_bufferSize]; + reset(); +} + +QAudioRingBuffer::~QAudioRingBuffer() +{ + delete m_buffer; +} + +int QAudioRingBuffer::used() const +{ + return m_bufferUsed; +} + +int QAudioRingBuffer::free() const +{ + return m_bufferSize - m_bufferUsed; +} + +int QAudioRingBuffer::size() const +{ + return m_bufferSize; +} + +void QAudioRingBuffer::reset() +{ + m_readPos = 0; + m_writePos = 0; + m_bufferUsed = 0; +} + +QT_END_NAMESPACE + + diff --git a/src/multimedia/audio/qaudio_mac_p.h b/src/multimedia/audio/qaudio_mac_p.h new file mode 100644 index 0000000..8e2d522 --- /dev/null +++ b/src/multimedia/audio/qaudio_mac_p.h @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#ifndef QAUDIO_MAC_P_H +#define QAUDIO_MAC_P_H + +#include <CoreAudio/CoreAudio.h> + +#include <QtCore/qdebug.h> +#include <QtCore/qatomic.h> + +#include <QtMultimedia/qaudioformat.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + + +extern QDebug operator<<(QDebug dbg, const QAudioFormat& audioFormat); + +extern QAudioFormat toQAudioFormat(const AudioStreamBasicDescription& streamFormat); +extern AudioStreamBasicDescription toAudioStreamBasicDescription(QAudioFormat const& audioFormat); + +class QAudioRingBuffer +{ +public: + typedef QPair<char*, int> Region; + + QAudioRingBuffer(int bufferSize); + ~QAudioRingBuffer(); + + Region acquireReadRegion(int size) + { + const int used = m_bufferUsed.fetchAndAddAcquire(0); + + if (used > 0) { + const int readSize = qMin(size, qMin(m_bufferSize - m_readPos, used)); + + return readSize > 0 ? Region(m_buffer + m_readPos, readSize) : Region(0, 0); + } + + return Region(0, 0); + } + + void releaseReadRegion(Region const& region) + { + m_readPos = (m_readPos + region.second) % m_bufferSize; + + m_bufferUsed.fetchAndAddRelease(-region.second); + } + + Region acquireWriteRegion(int size) + { + const int free = m_bufferSize - m_bufferUsed.fetchAndAddAcquire(0); + + if (free > 0) { + const int writeSize = qMin(size, qMin(m_bufferSize - m_writePos, free)); + + return writeSize > 0 ? Region(m_buffer + m_writePos, writeSize) : Region(0, 0); + } + + return Region(0, 0); + } + + void releaseWriteRegion(Region const& region) + { + m_writePos = (m_writePos + region.second) % m_bufferSize; + + m_bufferUsed.fetchAndAddRelease(region.second); + } + + int used() const; + int free() const; + int size() const; + + void reset(); + +private: + int m_bufferSize; + int m_readPos; + int m_writePos; + char* m_buffer; + QAtomicInt m_bufferUsed; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QAUDIO_MAC_P_H + + diff --git a/src/multimedia/audio/qaudiodevicefactory.cpp b/src/multimedia/audio/qaudiodevicefactory.cpp new file mode 100644 index 0000000..35e9c91 --- /dev/null +++ b/src/multimedia/audio/qaudiodevicefactory.cpp @@ -0,0 +1,250 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qdebug.h> +#include <QtMultimedia/qaudioengine.h> +#include <QtMultimedia/qaudioengineplugin.h> +#include <private/qfactoryloader_p.h> +#include "qaudiodevicefactory_p.h" +#include "qaudiodeviceid_p.h" + +#if defined(Q_OS_WIN) +#include "qaudiodeviceinfo_win32_p.h" +#include "qaudiooutput_win32_p.h" +#include "qaudioinput_win32_p.h" +#elif defined(Q_OS_MAC) +#include "qaudiodeviceinfo_mac_p.h" +#include "qaudiooutput_mac_p.h" +#include "qaudioinput_mac_p.h" +#elif defined(HAS_ALSA) +#include "qaudiodeviceinfo_alsa_p.h" +#include "qaudiooutput_alsa_p.h" +#include "qaudioinput_alsa_p.h" +#endif + +QT_BEGIN_NAMESPACE + +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, + (QAudioEngineFactoryInterface_iid, QLatin1String("/audio"), Qt::CaseInsensitive)) + + +class QNullDeviceInfo : public QAbstractAudioDeviceInfo +{ +public: + QAudioFormat preferredFormat() const { qWarning()<<"using null deviceinfo, none available"; return QAudioFormat(); } + bool isFormatSupported(const QAudioFormat& ) const { return false; } + QAudioFormat nearestFormat(const QAudioFormat& ) const { return QAudioFormat(); } + QString deviceName() const { return QString(); } + QStringList codecList() { return QStringList(); } + QList<int> frequencyList() { return QList<int>(); } + QList<int> channelsList() { return QList<int>(); } + QList<int> sampleSizeList() { return QList<int>(); } + QList<QAudioFormat::Endian> byteOrderList() { return QList<QAudioFormat::Endian>(); } + QList<QAudioFormat::SampleType> sampleTypeList() { return QList<QAudioFormat::SampleType>(); } +}; + +class QNullInputDevice : public QAbstractAudioInput +{ +public: + QIODevice* start(QIODevice* ) { qWarning()<<"using null input device, none available"; return 0; } + void stop() {} + void reset() {} + void suspend() {} + void resume() {} + int bytesReady() const { return 0; } + int periodSize() const { return 0; } + void setBufferSize(int ) {} + int bufferSize() const { return 0; } + void setNotifyInterval(int ) {} + int notifyInterval() const { return 0; } + qint64 totalTime() const { return 0; } + qint64 clock() const { return 0; } + QAudio::Error error() const { return QAudio::OpenError; } + QAudio::State state() const { return QAudio::StopState; } + QAudioFormat format() const { return QAudioFormat(); } +}; + +class QNullOutputDevice : public QAbstractAudioOutput +{ +public: + QIODevice* start(QIODevice* ) { qWarning()<<"using null output device, none available"; return 0; } + void stop() {} + void reset() {} + void suspend() {} + void resume() {} + int bytesFree() const { return 0; } + int periodSize() const { return 0; } + void setBufferSize(int ) {} + int bufferSize() const { return 0; } + void setNotifyInterval(int ) {} + int notifyInterval() const { return 0; } + qint64 totalTime() const { return 0; } + qint64 clock() const { return 0; } + QAudio::Error error() const { return QAudio::OpenError; } + QAudio::State state() const { return QAudio::StopState; } + QAudioFormat format() const { return QAudioFormat(); } +}; + +QList<QAudioDeviceId> QAudioDeviceFactory::deviceList(QAudio::Mode mode) +{ + QList<QAudioDeviceId> devices; +#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA)) + foreach (const QByteArray &handle, QAudioDeviceInfoPrivate::deviceList(mode)) + devices += createDeviceId(QLatin1String("builtin"), mode, handle); +#endif + QFactoryLoader* l = loader(); + + foreach (QString const& key, l->keys()) { + QAudioEngineFactoryInterface* plugin = qobject_cast<QAudioEngineFactoryInterface*>(l->instance(key)); + if (plugin) { + foreach (QByteArray const& handle, plugin->deviceList(mode)) + devices += createDeviceId(key, mode, handle); + } + + delete plugin; + } + + return devices; +} + +QAudioDeviceId QAudioDeviceFactory::defaultInputDevice() +{ + QAudioEngineFactoryInterface* plugin = qobject_cast<QAudioEngineFactoryInterface*>(loader()->instance(QLatin1String("default"))); + + if (plugin) { + QList<QByteArray> list = plugin->deviceList(QAudio::AudioInput); + if (list.size() > 0) + return createDeviceId(QLatin1String("default"), QAudio::AudioInput, list.at(0)); + } +#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA)) + return createDeviceId(QLatin1String("builtin"), QAudio::AudioInput, QAudioDeviceInfoPrivate::defaultInputDevice()); +#endif + return QAudioDeviceId(); +} + +QAudioDeviceId QAudioDeviceFactory::defaultOutputDevice() +{ + QAudioEngineFactoryInterface* plugin = qobject_cast<QAudioEngineFactoryInterface*>(loader()->instance(QLatin1String("default"))); + + if (plugin) { + QList<QByteArray> list = plugin->deviceList(QAudio::AudioOutput); + if (list.size() > 0) + return createDeviceId(QLatin1String("default"), QAudio::AudioOutput, list.at(0)); + } +#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA)) + return createDeviceId(QLatin1String("builtin"), QAudio::AudioOutput, QAudioDeviceInfoPrivate::defaultOutputDevice()); +#endif + return QAudioDeviceId(); +} + +QAbstractAudioDeviceInfo* QAudioDeviceFactory::audioDeviceInfo(QAudioDeviceId const& id) +{ + if (id.isNull()) + return new QNullDeviceInfo(); +#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA)) + if (id.d->key == QLatin1String("builtin")) + return new QAudioDeviceInfoPrivate(id.d->handle, QAudio::Mode(id.d->mode)); +#endif + QAudioEngineFactoryInterface* plugin = qobject_cast<QAudioEngineFactoryInterface*>(loader()->instance(id.d->key)); + + if (plugin) + return plugin->createDeviceInfo(id.d->handle, QAudio::Mode(id.d->mode)); + + return new QNullDeviceInfo(); +} + +QAbstractAudioInput* QAudioDeviceFactory::createDefaultInputDevice(QAudioFormat const &format) +{ + return createInputDevice(defaultInputDevice(), format); +} + +QAbstractAudioOutput* QAudioDeviceFactory::createDefaultOutputDevice(QAudioFormat const &format) +{ + return createOutputDevice(defaultOutputDevice(), format); +} + +QAbstractAudioInput* QAudioDeviceFactory::createInputDevice(QAudioDeviceId const& id, QAudioFormat const &format) +{ + if (id.isNull()) + return new QNullInputDevice(); +#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA)) + if (id.d->key == QLatin1String("builtin")) { + if(!defaultInputDevice().isNull()) + return new QAudioInputPrivate(id.d->handle, format); + else + return new QNullInputDevice(); + } +#endif + QAudioEngineFactoryInterface* plugin = qobject_cast<QAudioEngineFactoryInterface*>(loader()->instance((id.d->key))); + + if (plugin) + return plugin->createInput(id.d->handle, format); + + return new QNullInputDevice(); +} + +QAbstractAudioOutput* QAudioDeviceFactory::createOutputDevice(QAudioDeviceId const& id, QAudioFormat const &format) +{ + if (id.isNull()) + return new QNullOutputDevice(); +#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA)) + if (id.d->key == QLatin1String("builtin")) { + if(!defaultOutputDevice().isNull()) + return new QAudioOutputPrivate(id.d->handle, format); + else + return new QNullOutputDevice(); + } +#endif + QAudioEngineFactoryInterface* plugin = qobject_cast<QAudioEngineFactoryInterface*>(loader()->instance(id.d->key)); + + if (plugin) + return plugin->createOutput(id.d->handle, format); + + return new QNullOutputDevice(); +} + +QAudioDeviceId QAudioDeviceFactory::createDeviceId(QString const& key, int mode, QByteArray const& handle) +{ + return QAudioDeviceId(new QAudioDeviceIdPrivate(key, mode, handle)); +} + +QT_END_NAMESPACE + diff --git a/src/multimedia/audio/qaudiodevicefactory_p.h b/src/multimedia/audio/qaudiodevicefactory_p.h new file mode 100644 index 0000000..a8e2b28 --- /dev/null +++ b/src/multimedia/audio/qaudiodevicefactory_p.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QAUDIODEVICEFACTORY_P_H +#define QAUDIODEVICEFACTORY_P_H + +#include <QtCore/qglobal.h> +#include <QtCore/qbytearray.h> +#include <QtCore/qlist.h> + +#include <QtMultimedia/qaudiodeviceid.h> +#include <QtMultimedia/qaudiodeviceinfo.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + +class QAbstractAudioInput; +class QAbstractAudioOutput; + + +class QAudioDeviceFactory +{ +public: + static QList<QAudioDeviceId> deviceList(QAudio::Mode mode); + + static QAudioDeviceId defaultInputDevice(); + static QAudioDeviceId defaultOutputDevice(); + + static QAbstractAudioDeviceInfo* audioDeviceInfo(QAudioDeviceId const &device); + + static QAbstractAudioInput* createDefaultInputDevice(QAudioFormat const &format); + static QAbstractAudioOutput* createDefaultOutputDevice(QAudioFormat const &format); + + static QAbstractAudioInput* createInputDevice(QAudioDeviceId const &device, QAudioFormat const &format); + static QAbstractAudioOutput* createOutputDevice(QAudioDeviceId const &device, QAudioFormat const &format); + + static QAbstractAudioInput* createNullInput(); + static QAbstractAudioOutput* createNullOutput(); + + static QAudioDeviceId createDeviceId(QString const& key, int mode, QByteArray const& handle); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QAUDIODEVICEFACTORY_P_H + diff --git a/src/multimedia/audio/qaudiodeviceid.cpp b/src/multimedia/audio/qaudiodeviceid.cpp new file mode 100644 index 0000000..21a9cd8 --- /dev/null +++ b/src/multimedia/audio/qaudiodeviceid.cpp @@ -0,0 +1,168 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qstring.h> +#include <QtCore/qbytearray.h> +#include <QtCore/qdatastream.h> + +#include <QtMultimedia/qaudiodeviceid.h> +#include "qaudiodeviceid_p.h" + + +QT_BEGIN_NAMESPACE + +/*! + \class QAudioDeviceId + \brief The QAudioDeviceId class provides means for identifying a unique input or output device on a system. + + \inmodule QtMultimedia + \ingroup multimedia + \since 4.6 + + \sa QAudioDeviceInfo, QAudioOutput, QAudioInput +*/ + +/*! + Construct a new null QAudioDeviceId. +*/ + +QAudioDeviceId::QAudioDeviceId() +{ +} + +/*! + Copy the QAudDeviceId referenced by \a other. +*/ + +QAudioDeviceId::QAudioDeviceId(const QAudioDeviceId &other): + d(other.d) +{ +} + +/*! + Destroy the QAudioDeviceId. +*/ + +QAudioDeviceId::~QAudioDeviceId() +{ +} + +/*! + Make a copy of the \a other QAudioDeviceId. +*/ + +QAudioDeviceId& QAudioDeviceId::operator=(const QAudioDeviceId &other) +{ + d = other.d; + return *this; +} + +/*! + Compare with the \a other QAudioDeviceId, return true if they are the same; + otherwise false. +*/ + +bool QAudioDeviceId::operator==(const QAudioDeviceId &other) const +{ + return (d.constData() == 0 && other.d.constData() == 0) || + (d.constData() != 0 && other.d.constData() != 0 && + d->key == other.d->key && d->mode == other.d->mode && d->handle == other.d->handle); +} + +/*! + Compare with the \a other QAudioDeviceId, return false if they are the same; + otherwise true. +*/ + +bool QAudioDeviceId::operator!=(const QAudioDeviceId &other) const +{ + return !(*this == other); +} + +/*! + Returns true if this is not a valid QAudioDeviceId; otherwise false. +*/ + +bool QAudioDeviceId::isNull() const +{ + return d.constData() == 0; +} + +/*! + \internal +*/ + +QAudioDeviceId::QAudioDeviceId(QAudioDeviceIdPrivate* data): + d(data) +{ +} + +/*! + \internal +*/ + +QAudioDeviceIdPrivate::QAudioDeviceIdPrivate(QString const& k, int m, QByteArray const& h): + key(k), mode(m), handle(h) +{ +} + +#ifndef QT_NO_DATASTREAM +Q_MULTIMEDIA_EXPORT QDataStream &operator<<(QDataStream &s, const QAudioDeviceId &id) +{ + s << id.d->key << id.d->mode << id.d->handle; + return s; +} + +Q_MULTIMEDIA_EXPORT QDataStream &operator>>(QDataStream &s, QAudioDeviceId &id) +{ + QString key; + int mode; + QByteArray handle; + + s >> key >> mode >> handle; + id = QAudioDeviceId(new QAudioDeviceIdPrivate(key, mode, handle)); + + return s; +} +#endif + + +QT_END_NAMESPACE diff --git a/src/multimedia/audio/qaudiodeviceid.h b/src/multimedia/audio/qaudiodeviceid.h new file mode 100644 index 0000000..f9188d3 --- /dev/null +++ b/src/multimedia/audio/qaudiodeviceid.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QAUDIODEVICEID_H +#define QAUDIODEVICEID_H + +#include <QtCore/qshareddata.h> +#include <QtCore/qmetatype.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + + +class QAudioDeviceFactory; +class QAudioDeviceIdPrivate; + +class Q_MULTIMEDIA_EXPORT QAudioDeviceId +{ + friend class QAudioDeviceFactory; + friend Q_MULTIMEDIA_EXPORT QDataStream &operator<<(QDataStream&, const QAudioDeviceId&); + friend Q_MULTIMEDIA_EXPORT QDataStream &operator>>(QDataStream&, QAudioDeviceId&); + +public: + QAudioDeviceId(); + QAudioDeviceId(const QAudioDeviceId &other); + ~QAudioDeviceId(); + + QAudioDeviceId& operator=(const QAudioDeviceId &other); + bool operator==(const QAudioDeviceId &id) const; + bool operator!=(const QAudioDeviceId &id) const; + + bool isNull() const; + +private: + QAudioDeviceId(QAudioDeviceIdPrivate *data); + + QSharedDataPointer<QAudioDeviceIdPrivate> d; +}; + +#ifndef QT_NO_DATASTREAM +Q_MULTIMEDIA_EXPORT QDataStream &operator<<(QDataStream&, const QAudioDeviceId&); +Q_MULTIMEDIA_EXPORT QDataStream &operator>>(QDataStream&, QAudioDeviceId&); +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +Q_DECLARE_METATYPE(QAudioDeviceId); + + +#endif // QAUDIODEVICEID_H diff --git a/src/multimedia/audio/qaudiodeviceid_p.h b/src/multimedia/audio/qaudiodeviceid_p.h new file mode 100644 index 0000000..3034574 --- /dev/null +++ b/src/multimedia/audio/qaudiodeviceid_p.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QAUDIODEVICEIDPRIVATE_H +#define QAUDIODEVICEIDPRIVATE_H + +#include <QtCore/qstring.h> +#include <QtCore/qbytearray.h> +#include <QtCore/qshareddata.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + + +class QAudioDeviceIdPrivate : public QSharedData +{ +public: + QAudioDeviceIdPrivate(QString const& k, int m, QByteArray const& h); + + QString key; + int mode; + QByteArray handle; +}; + + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QAUDIODEVICEIDPRIVATE_H diff --git a/src/multimedia/audio/qaudiodeviceinfo.cpp b/src/multimedia/audio/qaudiodeviceinfo.cpp new file mode 100644 index 0000000..c1895d7 --- /dev/null +++ b/src/multimedia/audio/qaudiodeviceinfo.cpp @@ -0,0 +1,270 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qaudiodevicefactory_p.h" +#include <QtMultimedia/qaudioengine.h> +#include <QtMultimedia/qaudiodeviceinfo.h> + + +QT_BEGIN_NAMESPACE + +/*! + \class QAudioDeviceInfo + \brief The QAudioDeviceInfo class provides an interface to query audio devices and their functionality. + + \inmodule QtMultimedia + \ingroup multimedia + \since 4.6 + + QAudioDeviceInfo lets you query for audio devices--such as sound + cards and USB headsets--that are currently available on the system. + The audio devices available are dependent on the platform or audio plugins installed. + + You can also query each device for the formats it supports. A + format in this context is a set consisting of a specific byte + order, channel, codec, frequency, sample rate, and sample type. A + format is represented by the QAudioFormat class. + + The values supported by the the device for each of these + parameters can be fetched with + supportedByteOrders(), supportedChannels(), supportedCodecs(), + supportedFrequencies(), supportedSampleSizes(), and + supportedSampleTypes(). The combinations supported are dependent on the platform, + audio plugins installed and the audio device capabilities. If you need a specific format, you can check if + the device supports it with isFormatSupported(), or fetch a + supported format that is as close as possible to the format with + nearestFormat(). + + A QAudioDeviceInfo is constructed with a QAudioDeviceId, which is + an identifier for a physical device. It is used by Qt to construct + classes that communicate with the device--such as + QAudioDeviceInfo, QAudioInput, and QAudioOutput. The static + functions defaultInputDevice(), defaultOutputDevice(), and + deviceList() let you get a hold of the ids for all available + devices. You fetch ids based on whether you will use the device + for input or output; this is specified by the QAudio::Mode enum. + The QAudioDeviceId returned are only valid for the QAudio::Mode. + + For instance: + + \code + foreach(QAudioDeviceId audioId, QAudioDeviceInfo::deviceList(QAudio::AudioOutput)) { + QAudioDeviceInfo info(audioId); + qDebug() << "Device name: " << info.deviceName(); + } + \endcode + + In this code sample, we loop through all devices that are able to output + sound, i.e., play an audio stream in a supported format. For each device we + find, we simply print the deviceName(). + + \sa QAudioOutput, QAudioInput, QAudioDeviceId +*/ + +/*! + Construct a new audio device info and attach it to \a parent. + Using the audio device with the specified \a id. +*/ + +QAudioDeviceInfo::QAudioDeviceInfo(const QAudioDeviceId &id, QObject *parent): + QObject(parent) +{ + d = QAudioDeviceFactory::audioDeviceInfo(id); +} + +/*! + Destroy this audio device info. +*/ + +QAudioDeviceInfo::~QAudioDeviceInfo() +{ + delete d; +} + +/*! + Returns human readable name of audio device. + + Device names vary depending on platform/audio plugin being used. + + They are a unique string identifiers for the audio device. + + eg. default, Intel, U0x46d0x9a4 +*/ + +QString QAudioDeviceInfo::deviceName() const +{ + return d->deviceName(); +} + +/*! + Returns true if \a settings are supported by the audio device of this QAudioDeviceInfo. +*/ + +bool QAudioDeviceInfo::isFormatSupported(const QAudioFormat &settings) const +{ + return d->isFormatSupported(settings); +} + +/*! + Returns QAudioFormat of default settings. + + These settings are provided by the platform/audio plugin being used. + + They also are dependent on the QAudio::Mode being used. + + A typical audio system would provide something like: + \list + \o Input settings: 8000Hz mono 8 bit. + \o Output settings: 44100Hz stereo 16 bit little endian. + \endlist +*/ + +QAudioFormat QAudioDeviceInfo::preferredFormat() const +{ + return d->preferredFormat(); +} + +/*! + Returns closest QAudioFormat to \a settings that system audio supports. + + These settings are provided by the platform/audio plugin being used. + + They also are dependent on the QAudio::Mode being used. +*/ + +QAudioFormat QAudioDeviceInfo::nearestFormat(const QAudioFormat &settings) const +{ + return d->nearestFormat(settings); +} + +/*! + Returns a list of supported codecs. + + All platform and plugin implementations should provide support for: + + "audio/pcm" - Linear PCM + + For writing plugins to support additional codecs refer to: + + http://www.iana.org/assignments/media-types/audio/ +*/ + +QStringList QAudioDeviceInfo::supportedCodecs() const +{ + return d->codecList(); +} + +/*! + Returns a list of supported frequencies. +*/ + +QList<int> QAudioDeviceInfo::supportedFrequencies() const +{ + return d->frequencyList(); +} + +/*! + Returns a list of supported channels. +*/ + +QList<int> QAudioDeviceInfo::supportedChannels() const +{ + return d->channelsList(); +} + +/*! + Returns a list of supported sample sizes. +*/ + +QList<int> QAudioDeviceInfo::supportedSampleSizes() const +{ + return d->sampleSizeList(); +} + +/*! + Returns a list of supported byte orders. +*/ + +QList<QAudioFormat::Endian> QAudioDeviceInfo::supportedByteOrders() const +{ + return d->byteOrderList(); +} + +/*! + Returns a list of supported sample types. +*/ + +QList<QAudioFormat::SampleType> QAudioDeviceInfo::supportedSampleTypes() const +{ + return d->sampleTypeList(); +} + +/*! + Returns the name of the default input audio device. + All platform and audio plugin implementations provide a default audio device to use. +*/ + +QAudioDeviceId QAudioDeviceInfo::defaultInputDevice() +{ + return QAudioDeviceFactory::defaultInputDevice(); +} + +/*! + Returns the name of the default output audio device. + All platform and audio plugin implementations provide a default audio device to use. +*/ + +QAudioDeviceId QAudioDeviceInfo::defaultOutputDevice() +{ + return QAudioDeviceFactory::defaultOutputDevice(); +} + +/*! + Returns a list of audio devices that support \a mode. +*/ + +QList<QAudioDeviceId> QAudioDeviceInfo::deviceList(QAudio::Mode mode) +{ + return QAudioDeviceFactory::deviceList(mode); +} + +QT_END_NAMESPACE + diff --git a/src/multimedia/audio/qaudiodeviceinfo.h b/src/multimedia/audio/qaudiodeviceinfo.h new file mode 100644 index 0000000..444a00a --- /dev/null +++ b/src/multimedia/audio/qaudiodeviceinfo.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QAUDIODEVICEINFO_H +#define QAUDIODEVICEINFO_H + +#include <QtCore/qobject.h> +#include <QtCore/qglobal.h> +#include <QtCore/qbytearray.h> +#include <QtCore/qstring.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qlist.h> + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudioformat.h> +#include <QtMultimedia/qaudiodeviceid.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + + +class QAbstractAudioDeviceInfo; + +class Q_MULTIMEDIA_EXPORT QAudioDeviceInfo : public QObject +{ + Q_OBJECT + +public: + explicit QAudioDeviceInfo(const QAudioDeviceId &id, QObject *parent = 0); + ~QAudioDeviceInfo(); + + QString deviceName() const; + + bool isFormatSupported(const QAudioFormat &format) const; + QAudioFormat preferredFormat() const; + QAudioFormat nearestFormat(const QAudioFormat &format) const; + + QStringList supportedCodecs() const; + QList<int> supportedFrequencies() const; + QList<int> supportedChannels() const; + QList<int> supportedSampleSizes() const; + QList<QAudioFormat::Endian> supportedByteOrders() const; + QList<QAudioFormat::SampleType> supportedSampleTypes() const; + + static QAudioDeviceId defaultInputDevice(); + static QAudioDeviceId defaultOutputDevice(); + + static QList<QAudioDeviceId> deviceList(QAudio::Mode mode); + +private: + Q_DISABLE_COPY(QAudioDeviceInfo) + + QAbstractAudioDeviceInfo* d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QAUDIODEVICEINFO_H diff --git a/src/multimedia/audio/qaudiodeviceinfo_alsa_p.cpp b/src/multimedia/audio/qaudiodeviceinfo_alsa_p.cpp new file mode 100644 index 0000000..fe45f82 --- /dev/null +++ b/src/multimedia/audio/qaudiodeviceinfo_alsa_p.cpp @@ -0,0 +1,394 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qaudiodeviceinfo_alsa_p.h" + +QAudioDeviceInfoPrivate::QAudioDeviceInfoPrivate(QByteArray dev, QAudio::Mode mode) +{ + handle = 0; + + device = QLatin1String(dev); + this->mode = mode; +} + +QAudioDeviceInfoPrivate::~QAudioDeviceInfoPrivate() +{ + close(); +} + +bool QAudioDeviceInfoPrivate::isFormatSupported(const QAudioFormat& format) const +{ + return testSettings(format); +} + +QAudioFormat QAudioDeviceInfoPrivate::preferredFormat() const +{ + QAudioFormat nearest; + if(mode == QAudio::AudioOutput) { + nearest.setFrequency(44100); + nearest.setChannels(2); + nearest.setByteOrder(QAudioFormat::LittleEndian); + nearest.setSampleType(QAudioFormat::SignedInt); + nearest.setSampleSize(16); + nearest.setCodec(tr("audio/pcm")); + } else { + nearest.setFrequency(8000); + nearest.setChannels(1); + nearest.setSampleType(QAudioFormat::SignedInt); + nearest.setSampleSize(8); + nearest.setCodec(tr("audio/pcm")); + } + return nearest; +} + +QAudioFormat QAudioDeviceInfoPrivate::nearestFormat(const QAudioFormat& format) const +{ + if(testSettings(format)) + return format; + else + return preferredFormat(); +} + +QString QAudioDeviceInfoPrivate::deviceName() const +{ + return device; +} + +QStringList QAudioDeviceInfoPrivate::codecList() +{ + updateLists(); + return codecz; +} + +QList<int> QAudioDeviceInfoPrivate::frequencyList() +{ + updateLists(); + return freqz; +} + +QList<int> QAudioDeviceInfoPrivate::channelsList() +{ + updateLists(); + return channelz; +} + +QList<int> QAudioDeviceInfoPrivate::sampleSizeList() +{ + updateLists(); + return sizez; +} + +QList<QAudioFormat::Endian> QAudioDeviceInfoPrivate::byteOrderList() +{ + updateLists(); + return byteOrderz; +} + +QList<QAudioFormat::SampleType> QAudioDeviceInfoPrivate::sampleTypeList() +{ + updateLists(); + return typez; +} + +bool QAudioDeviceInfoPrivate::open() +{ + int err = 0; + QString dev = device; + if(!dev.contains(tr("default"))) { + int idx = snd_card_get_index(dev.toLocal8Bit().constData()); + dev = QString(tr("hw:%1,0")).arg(idx); + } + if(mode == QAudio::AudioOutput) { + err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_PLAYBACK,0); + } else { + err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_CAPTURE,0); + } + if(err < 0) { + handle = 0; + return false; + } + return true; +} + +void QAudioDeviceInfoPrivate::close() +{ + if(handle) + snd_pcm_close(handle); + handle = 0; +} + +bool QAudioDeviceInfoPrivate::testSettings(const QAudioFormat& format) const +{ + // Set nearest to closest settings that do work. + // See if what is in settings will work (return value). + + int err = 0; + snd_pcm_t* handle; + snd_pcm_hw_params_t *params; + QString dev = device; + + // open() + if(!dev.contains(tr("default"))) { + int idx = snd_card_get_index(dev.toLocal8Bit().constData()); + dev = QString(tr("hw:%1,0")).arg(idx); + } + if(mode == QAudio::AudioOutput) { + err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_PLAYBACK,0); + } else { + err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_CAPTURE,0); + } + if(err < 0) { + handle = 0; + return false; + } + + bool testChannel = false; + bool testCodec = false; + bool testFreq = false; + bool testType = false; + bool testSize = false; + + int dir = 0; + + snd_pcm_nonblock( handle, 0 ); + snd_pcm_hw_params_alloca( ¶ms ); + snd_pcm_hw_params_any( handle, params ); + + // For now, just accept only audio/pcm codec + if(!format.codec().startsWith(tr("audio/pcm"))) { + err=-1; + } else + testCodec = true; + + if(err>=0 && format.channels() != -1) { + err = snd_pcm_hw_params_test_channels(handle,params,format.channels()); + if(err>=0) + err = snd_pcm_hw_params_set_channels(handle,params,format.channels()); + if(err>=0) + testChannel = true; + } + + if(err>=0 && format.frequency() != -1) { + err = snd_pcm_hw_params_test_rate(handle,params,format.frequency(),0); + if(err>=0) + err = snd_pcm_hw_params_set_rate(handle,params,format.frequency(),dir); + if(err>=0) + testFreq = true; + } + + if((err>=0 && format.sampleSize() != -1) && + (format.sampleType() != QAudioFormat::Unknown)) { + switch(format.sampleSize()) { + case 8: + if(format.sampleType() == QAudioFormat::SignedInt) + err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S8); + else if(format.sampleType() == QAudioFormat::UnSignedInt) + err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U8); + break; + case 16: + if(format.sampleType() == QAudioFormat::SignedInt) { + if(format.byteOrder() == QAudioFormat::LittleEndian) + err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_LE); + else if(format.byteOrder() == QAudioFormat::BigEndian) + err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_BE); + } else if(format.sampleType() == QAudioFormat::UnSignedInt) { + if(format.byteOrder() == QAudioFormat::LittleEndian) + err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U16_LE); + else if(format.byteOrder() == QAudioFormat::BigEndian) + err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U16_BE); + } + break; + case 32: + if(format.sampleType() == QAudioFormat::SignedInt) { + if(format.byteOrder() == QAudioFormat::LittleEndian) + err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S32_LE); + else if(format.byteOrder() == QAudioFormat::BigEndian) + err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S32_BE); + } else if(format.sampleType() == QAudioFormat::UnSignedInt) { + if(format.byteOrder() == QAudioFormat::LittleEndian) + err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U32_LE); + else if(format.byteOrder() == QAudioFormat::BigEndian) + err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U32_BE); + } + } + if(err>=0) { + testSize = true; + testType = true; + } + } + if(err>=0) + err = snd_pcm_hw_params(handle, params); + + if(err == 0) { + // settings work + // close() + if(handle) + snd_pcm_close(handle); + return true; + } + if(handle) + snd_pcm_close(handle); + + return false; +} + +void QAudioDeviceInfoPrivate::updateLists() +{ + // redo all lists based on current settings + freqz.clear(); + channelz.clear(); + sizez.clear(); + byteOrderz.clear(); + typez.clear(); + codecz.clear(); + + if(!handle) + open(); + + if(!handle) + return; + + for(int i=0; i<(int)MAX_SAMPLE_RATES; i++) { + //if(snd_pcm_hw_params_test_rate(handle, params, SAMPLE_RATES[i], dir) == 0) + freqz.append(SAMPLE_RATES[i]); + } + channelz.append(1); + channelz.append(2); + sizez.append(8); + sizez.append(16); + sizez.append(32); + byteOrderz.append(QAudioFormat::LittleEndian); + byteOrderz.append(QAudioFormat::BigEndian); + typez.append(QAudioFormat::SignedInt); + typez.append(QAudioFormat::UnSignedInt); + typez.append(QAudioFormat::Float); + codecz.append(tr("audio/pcm")); + close(); +} + +QList<QByteArray> QAudioDeviceInfoPrivate::deviceList(QAudio::Mode mode) +{ + QAudio::Mode _m; + QList<QByteArray> devices; + QByteArray filter; + QString dir; + + // Create a list of all current audio devices that support mode + void **hints, **n; + char *name, *descr, *io; + + if(snd_device_name_hint(-1, "pcm", &hints) < 0) { + qWarning()<<"no alsa devices available"; + return devices; + } + n = hints; + + while (*n != NULL) { + _m = QAudio::AudioOutput; + name = snd_device_name_get_hint(*n, "NAME"); + descr = snd_device_name_get_hint(*n, "DESC"); + io = snd_device_name_get_hint(*n, "IOID"); + dir = QString::fromUtf8(io); + if((name != NULL) && (descr != NULL) && ((io == NULL) || (dir.length() ==filter.length()))) { + if(dir.length() == 5) + _m = QAudio::AudioInput; + if(io == NULL) + _m = mode; + + QString str = tr(name); + + if(str.contains(tr("default"))) { + int pos = str.indexOf(tr("="),0); + devices.append(str.mid(pos+1).toLocal8Bit().constData()); + } + } + if(name != NULL) + free(name); + if(descr != NULL) + free(descr); + if(io != NULL) + free(io); + n++; + } + snd_device_name_free_hint(hints); + + if(devices.size() > 0) { + devices.append("default"); + if(mode == QAudio::AudioInput) { + filter.append("Input"); + } else { + filter.append("Output"); + } + } + + return devices; +} + +QByteArray QAudioDeviceInfoPrivate::defaultInputDevice() +{ + QList<QByteArray> devices = deviceList(QAudio::AudioInput); + if(devices.size() == 0) + return QByteArray(); + + return QByteArray("default"); +} + +QByteArray QAudioDeviceInfoPrivate::defaultOutputDevice() +{ + QList<QByteArray> devices = deviceList(QAudio::AudioOutput); + if(devices.size() == 0) + return QByteArray(); + + return QByteArray("default"); +} + + diff --git a/src/multimedia/audio/qaudiodeviceinfo_alsa_p.h b/src/multimedia/audio/qaudiodeviceinfo_alsa_p.h new file mode 100644 index 0000000..3ac9239 --- /dev/null +++ b/src/multimedia/audio/qaudiodeviceinfo_alsa_p.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#ifndef QAUDIODEVICEINFOALSA_H +#define QAUDIODEVICEINFOALSA_H + +#include <alsa/asoundlib.h> + +#include <QtCore/qbytearray.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qlist.h> +#include <QtCore/qdebug.h> + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudioengine.h> + +const unsigned int MAX_SAMPLE_RATES = 5; +const unsigned int SAMPLE_RATES[] = + { 8000, 11025, 22050, 44100, 48000 }; + +class QAudioDeviceInfoPrivate : public QAbstractAudioDeviceInfo +{ + Q_OBJECT +public: + QAudioDeviceInfoPrivate(QByteArray dev,QAudio::Mode mode); + ~QAudioDeviceInfoPrivate(); + + bool testSettings(const QAudioFormat& format) const; + void updateLists(); + QAudioFormat preferredFormat() const; + bool isFormatSupported(const QAudioFormat& format) const; + QAudioFormat nearestFormat(const QAudioFormat& format) const; + QString deviceName() const; + QStringList codecList(); + QList<int> frequencyList(); + QList<int> channelsList(); + QList<int> sampleSizeList(); + QList<QAudioFormat::Endian> byteOrderList(); + QList<QAudioFormat::SampleType> sampleTypeList(); + static QByteArray defaultInputDevice(); + static QByteArray defaultOutputDevice(); + static QList<QByteArray> deviceList(QAudio::Mode); + +private: + bool open(); + void close(); + + QString device; + QAudio::Mode mode; + QAudioFormat nearest; + QList<int> freqz; + QList<int> channelz; + QList<int> sizez; + QList<QAudioFormat::Endian> byteOrderz; + QStringList codecz; + QList<QAudioFormat::SampleType> typez; + snd_pcm_t* handle; + snd_pcm_hw_params_t *params; +}; + +#endif + diff --git a/src/multimedia/audio/qaudiodeviceinfo_mac_p.cpp b/src/multimedia/audio/qaudiodeviceinfo_mac_p.cpp new file mode 100644 index 0000000..c94e0c4 --- /dev/null +++ b/src/multimedia/audio/qaudiodeviceinfo_mac_p.cpp @@ -0,0 +1,357 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qstringlist.h> +#include <QtCore/qlist.h> +#include <QtCore/qbytearray.h> +#include <QtCore/qdatastream.h> +#include <QtCore/qdebug.h> +#include <private/qcore_mac_p.h> + +#include <QtMultimedia/qaudiodeviceinfo.h> +#include "qaudio_mac_p.h" +#include "qaudiodeviceinfo_mac_p.h" + + + +QT_BEGIN_NAMESPACE + + +QAudioDeviceInfoPrivate::QAudioDeviceInfoPrivate(QByteArray const& handle, QAudio::Mode) +{ + QDataStream ds(handle); + quint32 did, tm; + + ds >> did >> tm >> name; + deviceId = AudioDeviceID(did); + mode = QAudio::Mode(tm); +} + +bool QAudioDeviceInfoPrivate::isFormatSupported(const QAudioFormat& format) const +{ + return format.codec() == QString::fromLatin1("audio/pcm"); +} + +QAudioFormat QAudioDeviceInfoPrivate::preferredFormat() const +{ + QAudioFormat rc; + + UInt32 propSize = 0; + + if (AudioDeviceGetPropertyInfo(deviceId, + 0, + mode == QAudio::AudioInput, + kAudioDevicePropertyStreams, + &propSize, + 0) == noErr) { + + const int sc = propSize / sizeof(AudioStreamID); + + if (sc > 0) { + AudioStreamID* streams = new AudioStreamID[sc]; + + if (AudioDeviceGetProperty(deviceId, + 0, + mode == QAudio::AudioInput, + kAudioDevicePropertyStreams, + &propSize, + streams) == noErr) { + + for (int i = 0; i < sc; ++i) { + if (AudioStreamGetPropertyInfo(streams[i], + 0, + kAudioStreamPropertyPhysicalFormat, + &propSize, + 0) == noErr) { + + AudioStreamBasicDescription sf; + + if (AudioStreamGetProperty(streams[i], + 0, + kAudioStreamPropertyPhysicalFormat, + &propSize, + &sf) == noErr) { + rc = toQAudioFormat(sf); + break; + } + } + } + } + + delete streams; + } + } + + return rc; +} + +QAudioFormat QAudioDeviceInfoPrivate::nearestFormat(const QAudioFormat& format) const +{ + QAudioFormat rc(format); + QAudioFormat target = preferredFormat(); + + if (!format.codec().isEmpty() && format.codec() != QString::fromLatin1("audio/pcm")) + return QAudioFormat(); + + rc.setCodec(QString::fromLatin1("audio/pcm")); + + if (rc.frequency() != target.frequency()) + rc.setFrequency(target.frequency()); + if (rc.channels() != target.channels()) + rc.setChannels(target.channels()); + if (rc.sampleSize() != target.sampleSize()) + rc.setSampleSize(target.sampleSize()); + if (rc.byteOrder() != target.byteOrder()) + rc.setByteOrder(target.byteOrder()); + if (rc.sampleType() != target.sampleType()) + rc.setSampleType(target.sampleType()); + + return rc; +} + +QString QAudioDeviceInfoPrivate::deviceName() const +{ + return name; +} + +QStringList QAudioDeviceInfoPrivate::codecList() +{ + return QStringList() << QString::fromLatin1("audio/pcm"); +} + +QList<int> QAudioDeviceInfoPrivate::frequencyList() +{ + QSet<int> rc; + + // Add some common frequencies + rc << 8000 << 11025 << 22050 << 44100; + + // + UInt32 propSize = 0; + + if (AudioDeviceGetPropertyInfo(deviceId, + 0, + mode == QAudio::AudioInput, + kAudioDevicePropertyAvailableNominalSampleRates, + &propSize, + 0) == noErr) { + + const int pc = propSize / sizeof(AudioValueRange); + + if (pc > 0) { + AudioValueRange* vr = new AudioValueRange[pc]; + + if (AudioDeviceGetProperty(deviceId, + 0, + mode == QAudio::AudioInput, + kAudioDevicePropertyAvailableNominalSampleRates, + &propSize, + vr) == noErr) { + + for (int i = 0; i < pc; ++i) + rc << vr[i].mMaximum; + } + + delete vr; + } + } + + return rc.toList(); +} + +QList<int> QAudioDeviceInfoPrivate::channelsList() +{ + QList<int> rc; + + // Can mix down to 1 channel + rc << 1; + + UInt32 propSize = 0; + int channels = 0; + + if (AudioDeviceGetPropertyInfo(deviceId, + 0, + mode == QAudio::AudioInput, + kAudioDevicePropertyStreamConfiguration, + &propSize, + 0) == noErr) { + + AudioBufferList* audioBufferList = static_cast<AudioBufferList*>(qMalloc(propSize)); + + if (audioBufferList != 0) { + if (AudioDeviceGetProperty(deviceId, + 0, + mode == QAudio::AudioInput, + kAudioDevicePropertyStreamConfiguration, + &propSize, + audioBufferList) == noErr) { + + for (int i = 0; i < int(audioBufferList->mNumberBuffers); ++i) { + channels += audioBufferList->mBuffers[i].mNumberChannels; + rc << channels; + } + } + + qFree(audioBufferList); + } + } + + return rc; +} + +QList<int> QAudioDeviceInfoPrivate::sampleSizeList() +{ + return QList<int>() << 8 << 16 << 24 << 32 << 64; +} + +QList<QAudioFormat::Endian> QAudioDeviceInfoPrivate::byteOrderList() +{ + return QList<QAudioFormat::Endian>() << QAudioFormat::LittleEndian << QAudioFormat::BigEndian; +} + +QList<QAudioFormat::SampleType> QAudioDeviceInfoPrivate::sampleTypeList() +{ + return QList<QAudioFormat::SampleType>() << QAudioFormat::SignedInt << QAudioFormat::UnSignedInt << QAudioFormat::Float; +} + +static QByteArray get_device_info(AudioDeviceID audioDevice, QAudio::Mode mode) +{ + UInt32 size; + QByteArray device; + QDataStream ds(&device, QIODevice::WriteOnly); + AudioStreamBasicDescription sf; + CFStringRef name; + Boolean isInput = mode == QAudio::AudioInput; + + // Id + ds << quint32(audioDevice); + + // Mode + size = sizeof(AudioStreamBasicDescription); + if (AudioDeviceGetProperty(audioDevice, 0, isInput, kAudioDevicePropertyStreamFormat, + &size, &sf) != noErr) { + return QByteArray(); + } + ds << quint32(mode); + + // Name + size = sizeof(CFStringRef); + if (AudioDeviceGetProperty(audioDevice, 0, isInput, kAudioObjectPropertyName, + &size, &name) != noErr) { + qWarning() << "QAudioDeviceInfo: Unable to find device name"; + } + ds << QCFString::toQString(name); + + CFRelease(name); + + return device; +} + +QByteArray QAudioDeviceInfoPrivate::defaultInputDevice() +{ + AudioDeviceID audioDevice; + UInt32 size = sizeof(audioDevice); + + if (AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &size, + &audioDevice) != noErr) { + qWarning() << "QAudioDeviceInfo: Unable to find default input device"; + return QByteArray(); + } + + return get_device_info(audioDevice, QAudio::AudioInput); +} + +QByteArray QAudioDeviceInfoPrivate::defaultOutputDevice() +{ + AudioDeviceID audioDevice; + UInt32 size = sizeof(audioDevice); + + if (AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, + &audioDevice) != noErr) { + qWarning() << "QAudioDeviceInfo: Unable to find default output device"; + return QByteArray(); + } + + return get_device_info(audioDevice, QAudio::AudioOutput); +} + +QList<QByteArray> QAudioDeviceInfoPrivate::deviceList(QAudio::Mode mode) +{ + QList<QByteArray> devices; + + UInt32 propSize = 0; + + if (AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &propSize, 0) == noErr) { + + const int dc = propSize / sizeof(AudioDeviceID); + + if (dc > 0) { + AudioDeviceID* audioDevices = new AudioDeviceID[dc]; + + if (AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &propSize, audioDevices) == noErr) { + for (int i = 0; i < dc; ++i) { + QByteArray info = get_device_info(audioDevices[i], mode); + if (!info.isNull()) + devices << info; + } + } + + delete audioDevices; + } + } + + return devices; +} + + +QT_END_NAMESPACE + diff --git a/src/multimedia/audio/qaudiodeviceinfo_mac_p.h b/src/multimedia/audio/qaudiodeviceinfo_mac_p.h new file mode 100644 index 0000000..ee593de --- /dev/null +++ b/src/multimedia/audio/qaudiodeviceinfo_mac_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#ifndef QDEVICEINFO_MAC_P_H +#define QDEVICEINFO_MAC_P_H + +#include <CoreAudio/CoreAudio.h> + +#include <QtMultimedia/qaudioengine.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QAudioDeviceInfoPrivate : public QAbstractAudioDeviceInfo +{ +public: + AudioDeviceID deviceId; + QString name; + QAudio::Mode mode; + + QAudioDeviceInfoPrivate(QByteArray const& handle, QAudio::Mode mode); + + bool isFormatSupported(const QAudioFormat& format) const; + QAudioFormat preferredFormat() const; + QAudioFormat nearestFormat(const QAudioFormat& format) const; + + QString deviceName() const; + + QStringList codecList(); + QList<int> frequencyList(); + QList<int> channelsList(); + QList<int> sampleSizeList(); + QList<QAudioFormat::Endian> byteOrderList(); + QList<QAudioFormat::SampleType> sampleTypeList(); + + static QByteArray defaultInputDevice(); + static QByteArray defaultOutputDevice(); + + static QList<QByteArray> deviceList(QAudio::Mode mode); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDEVICEINFO_MAC_P_H diff --git a/src/multimedia/audio/qaudiodeviceinfo_win32_p.cpp b/src/multimedia/audio/qaudiodeviceinfo_win32_p.cpp new file mode 100644 index 0000000..1a1995a --- /dev/null +++ b/src/multimedia/audio/qaudiodeviceinfo_win32_p.cpp @@ -0,0 +1,378 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#include <windows.h> +#include <mmsystem.h> +#include "qaudiodeviceinfo_win32_p.h" + +// For mingw toolchain mmsystem.h only defines half the defines, so add if needed. +#ifndef WAVE_FORMAT_44M08 +#define WAVE_FORMAT_44M08 0x00000100 +#define WAVE_FORMAT_44S08 0x00000200 +#define WAVE_FORMAT_44M16 0x00000400 +#define WAVE_FORMAT_44S16 0x00000800 +#define WAVE_FORMAT_48M08 0x00001000 +#define WAVE_FORMAT_48S08 0x00002000 +#define WAVE_FORMAT_48M16 0x00004000 +#define WAVE_FORMAT_48S16 0x00008000 +#define WAVE_FORMAT_96M08 0x00010000 +#define WAVE_FORMAT_96S08 0x00020000 +#define WAVE_FORMAT_96M16 0x00040000 +#define WAVE_FORMAT_96S16 0x00080000 +#endif + + +QAudioDeviceInfoPrivate::QAudioDeviceInfoPrivate(QByteArray dev, QAudio::Mode mode) +{ + device = QLatin1String(dev); + this->mode = mode; +} + +QAudioDeviceInfoPrivate::~QAudioDeviceInfoPrivate() +{ + close(); +} + +bool QAudioDeviceInfoPrivate::isFormatSupported(const QAudioFormat& format) const +{ + return testSettings(format); +} + +QAudioFormat QAudioDeviceInfoPrivate::preferredFormat() const +{ + QAudioFormat nearest; + if(mode == QAudio::AudioOutput) { + nearest.setFrequency(44100); + nearest.setChannels(2); + nearest.setByteOrder(QAudioFormat::LittleEndian); + nearest.setSampleType(QAudioFormat::SignedInt); + nearest.setSampleSize(16); + nearest.setCodec(tr("audio/pcm")); + } else { + nearest.setFrequency(11025); + nearest.setChannels(1); + nearest.setSampleType(QAudioFormat::SignedInt); + nearest.setSampleSize(8); + nearest.setCodec(tr("audio/pcm")); + } + return nearest; +} + +QAudioFormat QAudioDeviceInfoPrivate::nearestFormat(const QAudioFormat& format) const +{ + if(testSettings(format)) + return format; + else + return preferredFormat(); +} + +QString QAudioDeviceInfoPrivate::deviceName() const +{ + return device; +} + +QStringList QAudioDeviceInfoPrivate::codecList() +{ + updateLists(); + return codecz; +} + +QList<int> QAudioDeviceInfoPrivate::frequencyList() +{ + updateLists(); + return freqz; +} + +QList<int> QAudioDeviceInfoPrivate::channelsList() +{ + updateLists(); + return channelz; +} + +QList<int> QAudioDeviceInfoPrivate::sampleSizeList() +{ + updateLists(); + return sizez; +} + +QList<QAudioFormat::Endian> QAudioDeviceInfoPrivate::byteOrderList() +{ + updateLists(); + return byteOrderz; +} + +QList<QAudioFormat::SampleType> QAudioDeviceInfoPrivate::sampleTypeList() +{ + updateLists(); + return typez; +} + + +bool QAudioDeviceInfoPrivate::open() +{ + return true; +} + +void QAudioDeviceInfoPrivate::close() +{ +} + +bool QAudioDeviceInfoPrivate::testSettings(const QAudioFormat& format) const +{ + // Set nearest to closest settings that do work. + // See if what is in settings will work (return value). + + bool testChannel = false; + bool testCodec = false; + bool testFreq = false; + + int err = 0; + + // For now, just accept only audio/pcm codec + if(!format.codec().startsWith(tr("audio/pcm"))) { + err=-1; + } else + testCodec = true; + + if(err>=0 && format.channels() != -1) { + testChannel = true; + } + + if(err>=0 && format.frequency() != -1) { + testFreq = true; + } + + if(err == 0) { + // settings work + return true; + } + return false; +} + +void QAudioDeviceInfoPrivate::updateLists() +{ + // redo all lists based on current settings + bool base = false; + bool match = false; + DWORD fmt = NULL; + QString tmp; + + if(device.compare(tr("default")) == 0) + base = true; + + if(mode == QAudio::AudioOutput) { + WAVEOUTCAPS woc; + unsigned long iNumDevs,i; + iNumDevs = waveOutGetNumDevs(); + for(i=0;i<iNumDevs;i++) { + if(waveOutGetDevCaps(i, &woc, sizeof(WAVEOUTCAPS)) + == MMSYSERR_NOERROR) { + tmp = QString::fromUtf16((const unsigned short*)woc.szPname); + if(tmp.compare(device) == 0) { + match = true; + fmt = woc.dwFormats; + break; + } + if(base) { + match = true; + fmt = woc.dwFormats; + break; + } + } + } + } else { + WAVEINCAPS woc; + unsigned long iNumDevs,i; + iNumDevs = waveInGetNumDevs(); + for(i=0;i<iNumDevs;i++) { + if(waveInGetDevCaps(i, &woc, sizeof(WAVEINCAPS)) + == MMSYSERR_NOERROR) { + tmp = QString::fromUtf16((const unsigned short*)woc.szPname); + if(tmp.compare(device) == 0) { + match = true; + fmt = woc.dwFormats; + break; + } + if(base) { + match = true; + fmt = woc.dwFormats; + break; + } + } + } + } + sizez.clear(); + freqz.clear(); + channelz.clear(); + byteOrderz.clear(); + typez.clear(); + codecz.clear(); + + if(match) { + if((fmt && WAVE_FORMAT_1M08) + || (fmt && WAVE_FORMAT_1S08) + || (fmt && WAVE_FORMAT_2M08) + || (fmt && WAVE_FORMAT_2S08) + || (fmt && WAVE_FORMAT_4M08) + || (fmt && WAVE_FORMAT_4S08) +#ifndef Q_OS_WINCE + || (fmt && WAVE_FORMAT_48M08) + || (fmt && WAVE_FORMAT_48S08) + || (fmt && WAVE_FORMAT_96M08) + || (fmt && WAVE_FORMAT_96S08) +#endif + ) { + sizez.append(8); + } + if((fmt && WAVE_FORMAT_1M16) + || (fmt && WAVE_FORMAT_1S16) + || (fmt && WAVE_FORMAT_2M16) + || (fmt && WAVE_FORMAT_2S16) + || (fmt && WAVE_FORMAT_4M16) + || (fmt && WAVE_FORMAT_4S16) +#ifndef Q_OS_WINCE + || (fmt && WAVE_FORMAT_48M16) + || (fmt && WAVE_FORMAT_48S16) + || (fmt && WAVE_FORMAT_96M16) + || (fmt && WAVE_FORMAT_96S16) +#endif + ) { + sizez.append(16); + } + if((fmt && WAVE_FORMAT_1M08) + || (fmt && WAVE_FORMAT_1S08) + || (fmt && WAVE_FORMAT_1M16) + || (fmt && WAVE_FORMAT_1S16)) { + freqz.append(11025); + } + if((fmt && WAVE_FORMAT_2M08) + || (fmt && WAVE_FORMAT_2S08) + || (fmt && WAVE_FORMAT_2M16) + || (fmt && WAVE_FORMAT_2S16)) { + freqz.append(22050); + } + if((fmt && WAVE_FORMAT_4M08) + || (fmt && WAVE_FORMAT_4S08) + || (fmt && WAVE_FORMAT_4M16) + || (fmt && WAVE_FORMAT_4S16)) { + freqz.append(44100); + } +#ifndef Q_OS_WINCE + if((fmt && WAVE_FORMAT_48M08) + || (fmt && WAVE_FORMAT_48S08) + || (fmt && WAVE_FORMAT_48M16) + || (fmt && WAVE_FORMAT_48S16)) { + freqz.append(48000); + } + if((fmt && WAVE_FORMAT_96M08) + || (fmt && WAVE_FORMAT_96S08) + || (fmt && WAVE_FORMAT_96M16) + || (fmt && WAVE_FORMAT_96S16)) { + freqz.append(96000); + } +#endif + channelz.append(1); + channelz.append(2); + + byteOrderz.append(QAudioFormat::LittleEndian); + + typez.append(QAudioFormat::SignedInt); + typez.append(QAudioFormat::UnSignedInt); + + codecz.append(tr("audio/pcm")); + } +} + +QList<QByteArray> QAudioDeviceInfoPrivate::deviceList(QAudio::Mode mode) +{ + Q_UNUSED(mode) + + QList<QByteArray> devices; + + devices.append("default"); + + if(mode == QAudio::AudioOutput) { + WAVEOUTCAPS woc; + unsigned long iNumDevs,i; + iNumDevs = waveOutGetNumDevs(); + for(i=0;i<iNumDevs;i++) { + if(waveOutGetDevCaps(i, &woc, sizeof(WAVEOUTCAPS)) + == MMSYSERR_NOERROR) { + devices.append(QString::fromUtf16((const unsigned short*)woc.szPname).toLocal8Bit().constData()); + } + } + } else { + WAVEINCAPS woc; + unsigned long iNumDevs,i; + iNumDevs = waveInGetNumDevs(); + for(i=0;i<iNumDevs;i++) { + if(waveInGetDevCaps(i, &woc, sizeof(WAVEINCAPS)) + == MMSYSERR_NOERROR) { + devices.append(QString::fromUtf16((const unsigned short*)woc.szPname).toLocal8Bit().constData()); + } + } + + } + return devices; +} + +QByteArray QAudioDeviceInfoPrivate::defaultOutputDevice() +{ + return QByteArray("default"); +} + +QByteArray QAudioDeviceInfoPrivate::defaultInputDevice() +{ + return QByteArray("default"); +} + diff --git a/src/multimedia/audio/qaudiodeviceinfo_win32_p.h b/src/multimedia/audio/qaudiodeviceinfo_win32_p.h new file mode 100644 index 0000000..140a741 --- /dev/null +++ b/src/multimedia/audio/qaudiodeviceinfo_win32_p.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#ifndef QAUDIODEVICEINFOWIN_H +#define QAUDIODEVICEINFOWIN_H + +#include <QtCore/qbytearray.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qlist.h> +#include <QtCore/qdebug.h> + +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudioengine.h> + + +const unsigned int MAX_SAMPLE_RATES = 5; +const unsigned int SAMPLE_RATES[] = { 8000, 11025, 22050, 44100, 48000 }; + +class QAudioDeviceInfoPrivate : public QAbstractAudioDeviceInfo +{ + Q_OBJECT + +public: + QAudioDeviceInfoPrivate(QByteArray dev,QAudio::Mode mode); + ~QAudioDeviceInfoPrivate(); + + bool open(); + void close(); + + bool testSettings(const QAudioFormat& format) const; + void updateLists(); + QAudioFormat preferredFormat() const; + bool isFormatSupported(const QAudioFormat& format) const; + QAudioFormat nearestFormat(const QAudioFormat& format) const; + QString deviceName() const; + QStringList codecList(); + QList<int> frequencyList(); + QList<int> channelsList(); + QList<int> sampleSizeList(); + QList<QAudioFormat::Endian> byteOrderList(); + QList<QAudioFormat::SampleType> sampleTypeList(); + static QByteArray defaultInputDevice(); + static QByteArray defaultOutputDevice(); + static QList<QByteArray> deviceList(QAudio::Mode); + +private: + QAudio::Mode mode; + QString device; + QAudioFormat nearest; + QList<int> freqz; + QList<int> channelz; + QList<int> sizez; + QList<QAudioFormat::Endian> byteOrderz; + QStringList codecz; + QList<QAudioFormat::SampleType> typez; +}; + +#endif diff --git a/src/multimedia/audio/qaudioengine.cpp b/src/multimedia/audio/qaudioengine.cpp new file mode 100644 index 0000000..930977e --- /dev/null +++ b/src/multimedia/audio/qaudioengine.cpp @@ -0,0 +1,343 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtMultimedia/qaudioengine.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QAbstractAudioDeviceInfo + \brief The QAbstractAudioDeviceInfo class provides access for QAudioDeviceInfo to access the audio + device provided by the plugin. + \internal + + \ingroup multimedia + + This class implements the audio functionality for + QAudioDeviceInfo, i.e., QAudioDeviceInfo keeps a + QAbstractAudioDeviceInfo and routes function calls to it. For a + description of the functionality that QAbstractAudioDeviceInfo + implements, you can read the class and functions documentation of + QAudioDeviceInfo. + + \sa QAudioDeviceInfo +*/ + +/*! + \fn virtual QAudioFormat QAbstractAudioDeviceInfo::preferredFormat() const + Returns the nearest settings. +*/ + +/*! + \fn virtual bool QAbstractAudioDeviceInfo::isFormatSupported(const QAudioFormat& format) const + Returns true if \a format is available from audio device. +*/ + +/*! + \fn virtual QAudioFormat QAbstractAudioDeviceInfo::nearestFormat(const QAudioFormat& format) const + Returns the nearest settings \a format. +*/ + +/*! + \fn virtual QString QAbstractAudioDeviceInfo::deviceName() const + Returns the audio device name. +*/ + +/*! + \fn virtual QStringList QAbstractAudioDeviceInfo::codecList() + Returns the list of currently available codecs. +*/ + +/*! + \fn virtual QList<int> QAbstractAudioDeviceInfo::frequencyList() + Returns the list of currently available frequencies. +*/ + +/*! + \fn virtual QList<int> QAbstractAudioDeviceInfo::channelsList() + Returns the list of currently available channels. +*/ + +/*! + \fn virtual QList<int> QAbstractAudioDeviceInfo::sampleSizeList() + Returns the list of currently available sample sizes. +*/ + +/*! + \fn virtual QList<QAudioFormat::Endian> QAbstractAudioDeviceInfo::byteOrderList() + Returns the list of currently available byte orders. +*/ + +/*! + \fn virtual QList<QAudioFormat::SampleType> QAbstractAudioDeviceInfo::sampleTypeList() + Returns the list of currently available sample types. +*/ + +/*! + \class QAbstractAudioOutput + \brief The QAbstractAudioOutput class provides access for QAudioOutput to access the audio + device provided by the plugin. + \internal + + \ingroup multimedia + + QAbstractAudioOutput implements audio functionality for + QAudioOutput, i.e., QAudioOutput routes function calls to + QAbstractAudioOutput. For a description of the functionality that + is implemented, see the QAudioOutput class and function + descriptions. + + \sa QAudioOutput +*/ + +/*! + \fn virtual QIODevice* QAbstractAudioOutput::start(QIODevice* device) + Uses the \a device as the QIODevice to transfer data. If \a device is null then the class + creates an internal QIODevice. Returns a pointer to the QIODevice being used to handle + the data transfer. This QIODevice can be used to write() audio data directly. Passing a + QIODevice allows the data to be transfered without any extra code. +*/ + +/*! + \fn virtual void QAbstractAudioOutput::stop() + Stops the audio output. +*/ + +/*! + \fn virtual void QAbstractAudioOutput::reset() + Drops all audio data in the buffers, resets buffers to zero. +*/ + +/*! + \fn virtual void QAbstractAudioOutput::suspend() + Stops processing audio data, preserving buffered audio data. +*/ + +/*! + \fn virtual void QAbstractAudioOutput::resume() + Resumes processing audio data after a suspend() +*/ + +/*! + \fn virtual int QAbstractAudioOutput::bytesFree() const + Returns the free space available in bytes in the audio buffer. +*/ + +/*! + \fn virtual int QAbstractAudioOutput::periodSize() const + Returns the period size in bytes. +*/ + +/*! + \fn virtual void QAbstractAudioOutput::setBufferSize(int value) + Sets the audio buffer size to \a value in bytes. +*/ + +/*! + \fn virtual int QAbstractAudioOutput::bufferSize() const + Returns the audio buffer size in bytes. +*/ + +/*! + \fn virtual void QAbstractAudioOutput::setNotifyInterval(int ms) + Sets the interval for notify() signal to be emitted. This is based on the \a ms + of audio data processed not on actual real-time. The resolution of the timer + is platform specific. +*/ + +/*! + \fn virtual int QAbstractAudioOutput::notifyInterval() const + Returns the notify interval in milliseconds. +*/ + +/*! + \fn virtual qint64 QAbstractAudioOutput::totalTime() const + Returns the amount of audio data processed since start() was called in milliseconds. +*/ + +/*! + \fn virtual qint64 QAbstractAudioOutput::clock() const + Returns the milliseconds since start() was called, including time in Idle and suspend states. +*/ + +/*! + \fn virtual QAudio::Error QAbstractAudioOutput::error() const + Returns the error state. +*/ + +/*! + \fn virtual QAudio::State QAbstractAudioOutput::state() const + Returns the state of audio processing. +*/ + +/*! + \fn virtual QAudioFormat QAbstractAudioOutput::format() const + Returns the QAudioFormat being used. +*/ + +/*! + \fn QAbstractAudioOutput::stateChanged(QAudio::State state) + This signal is emitted when the device \a state has changed. +*/ + +/*! + \fn QAbstractAudioOutput::notify() + This signal is emitted when x ms of audio data has been processed + the interval set by setNotifyInterval(x). +*/ + + +/*! + \class QAbstractAudioInput + \brief The QAbstractAudioInput class provides access for QAudioInput to access the audio + device provided by the plugin. + \internal + + \ingroup multimedia + + QAudioDeviceInput keeps an instance of QAbstractAudioInput and + routes calls to functions of the same name to QAbstractAudioInput. + This means that it is QAbstractAudioInput that implements the + audio functionality. For a description of the functionality, see + the QAudioInput class description. + + \sa QAudioInput +*/ + +/*! + \fn virtual QIODevice* QAbstractAudioInput::start(QIODevice* device) + Uses the \a device as the QIODevice to transfer data. If \a device is null + then the class creates an internal QIODevice. Returns a pointer to the + QIODevice being used to handle the data transfer. This QIODevice can be used to + read() audio data directly. Passing a QIODevice allows the data to be transfered + without any extra code. +*/ + +/*! + \fn virtual void QAbstractAudioInput::stop() + Stops the audio input. +*/ + +/*! + \fn virtual void QAbstractAudioInput::reset() + Drops all audio data in the buffers, resets buffers to zero. +*/ + +/*! + \fn virtual void QAbstractAudioInput::suspend() + Stops processing audio data, preserving buffered audio data. +*/ + +/*! + \fn virtual void QAbstractAudioInput::resume() + Resumes processing audio data after a suspend(). +*/ + +/*! + \fn virtual int QAbstractAudioInput::bytesReady() const + Returns the amount of audio data available to read in bytes. +*/ + +/*! + \fn virtual int QAbstractAudioInput::periodSize() const + Returns the period size in bytes. +*/ + +/*! + \fn virtual void QAbstractAudioInput::setBufferSize(int value) + Sets the audio buffer size to \a value in milliseconds. +*/ + +/*! + \fn virtual int QAbstractAudioInput::bufferSize() const + Returns the audio buffer size in milliseconds. +*/ + +/*! + \fn virtual void QAbstractAudioInput::setNotifyInterval(int ms) + Sets the interval for notify() signal to be emitted. This is based + on the \a ms of audio data processed not on actual real-time. + The resolution of the timer is platform specific. +*/ + +/*! + \fn virtual int QAbstractAudioInput::notifyInterval() const + Returns the notify interval in milliseconds. +*/ + +/*! + \fn virtual qint64 QAbstractAudioInput::totalTime() const + Returns the amount of audio data processed since start() was called in milliseconds. +*/ + +/*! + \fn virtual qint64 QAbstractAudioInput::clock() const + Returns the milliseconds since start() was called, including time in Idle and suspend states. +*/ + +/*! + \fn virtual QAudio::Error QAbstractAudioInput::error() const + Returns the error state. +*/ + +/*! + \fn virtual QAudio::State QAbstractAudioInput::state() const + Returns the state of audio processing. +*/ + +/*! + \fn virtual QAudioFormat QAbstractAudioInput::format() const + Returns the QAudioFormat being used +*/ + +/*! + \fn QAbstractAudioInput::stateChanged(QAudio::State state) + This signal is emitted when the device \a state has changed. +*/ + +/*! + \fn QAbstractAudioInput::notify() + This signal is emitted when x ms of audio data has been processed + the interval set by setNotifyInterval(x). +*/ + + +QT_END_NAMESPACE diff --git a/src/multimedia/audio/qaudioengine.h b/src/multimedia/audio/qaudioengine.h new file mode 100644 index 0000000..b0aa762 --- /dev/null +++ b/src/multimedia/audio/qaudioengine.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QAUDIOENGINE_H +#define QAUDIOENGINE_H + +#include <QtCore/qglobal.h> +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudioformat.h> +#include <QtMultimedia/qaudiodeviceinfo.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + +class Q_MULTIMEDIA_EXPORT QAbstractAudioDeviceInfo : public QObject +{ + Q_OBJECT + +public: + virtual QAudioFormat preferredFormat() const = 0; + virtual bool isFormatSupported(const QAudioFormat &format) const = 0; + virtual QAudioFormat nearestFormat(const QAudioFormat &format) const = 0; + virtual QString deviceName() const = 0; + virtual QStringList codecList() = 0; + virtual QList<int> frequencyList() = 0; + virtual QList<int> channelsList() = 0; + virtual QList<int> sampleSizeList() = 0; + virtual QList<QAudioFormat::Endian> byteOrderList() = 0; + virtual QList<QAudioFormat::SampleType> sampleTypeList() = 0; +}; + +class Q_MULTIMEDIA_EXPORT QAbstractAudioOutput : public QObject +{ + Q_OBJECT + +public: + virtual QIODevice* start(QIODevice* device) = 0; + virtual void stop() = 0; + virtual void reset() = 0; + virtual void suspend() = 0; + virtual void resume() = 0; + virtual int bytesFree() const = 0; + virtual int periodSize() const = 0; + virtual void setBufferSize(int value) = 0; + virtual int bufferSize() const = 0; + virtual void setNotifyInterval(int milliSeconds) = 0; + virtual int notifyInterval() const = 0; + virtual qint64 totalTime() const = 0; + virtual qint64 clock() const = 0; + virtual QAudio::Error error() const = 0; + virtual QAudio::State state() const = 0; + virtual QAudioFormat format() const = 0; + +Q_SIGNALS: + void stateChanged(QAudio::State); + void notify(); +}; + +class Q_MULTIMEDIA_EXPORT QAbstractAudioInput : public QObject +{ + Q_OBJECT + +public: + virtual QIODevice* start(QIODevice* device) = 0; + virtual void stop() = 0; + virtual void reset() = 0; + virtual void suspend() = 0; + virtual void resume() = 0; + virtual int bytesReady() const = 0; + virtual int periodSize() const = 0; + virtual void setBufferSize(int value) = 0; + virtual int bufferSize() const = 0; + virtual void setNotifyInterval(int milliSeconds) = 0; + virtual int notifyInterval() const = 0; + virtual qint64 totalTime() const = 0; + virtual qint64 clock() const = 0; + virtual QAudio::Error error() const = 0; + virtual QAudio::State state() const = 0; + virtual QAudioFormat format() const = 0; + +Q_SIGNALS: + void stateChanged(QAudio::State); + void notify(); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QAUDIOENGINE_H diff --git a/src/multimedia/audio/qaudioengineplugin.cpp b/src/multimedia/audio/qaudioengineplugin.cpp new file mode 100644 index 0000000..6e39004 --- /dev/null +++ b/src/multimedia/audio/qaudioengineplugin.cpp @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtMultimedia/qaudioengineplugin.h> + +QT_BEGIN_NAMESPACE + +QAudioEnginePlugin::QAudioEnginePlugin(QObject* parent) : + QObject(parent) +{} + +QAudioEnginePlugin::~QAudioEnginePlugin() +{} + +QT_END_NAMESPACE diff --git a/src/multimedia/audio/qaudioengineplugin.h b/src/multimedia/audio/qaudioengineplugin.h new file mode 100644 index 0000000..8eab2d7 --- /dev/null +++ b/src/multimedia/audio/qaudioengineplugin.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QAUDIOENGINEPLUGIN_H +#define QAUDIOENGINEPLUGIN_H + +#include <QtCore/qstring.h> +#include <QtCore/qplugin.h> +#include <QtCore/qfactoryinterface.h> + +#include <QtMultimedia/qaudioformat.h> +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudioengine.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + +struct Q_MULTIMEDIA_EXPORT QAudioEngineFactoryInterface : public QFactoryInterface +{ + virtual QList<QByteArray> deviceList(QAudio::Mode) const = 0; + virtual QAbstractAudioInput* createInput(const QByteArray& device, const QAudioFormat& format = QAudioFormat()) = 0; + virtual QAbstractAudioOutput* createOutput(const QByteArray& device, const QAudioFormat& format = QAudioFormat()) = 0; + virtual QAbstractAudioDeviceInfo* createDeviceInfo(const QByteArray& device, QAudio::Mode mode) = 0; +}; + +#define QAudioEngineFactoryInterface_iid \ + "com.nokia.qt.QAudioEngineFactoryInterface" +Q_DECLARE_INTERFACE(QAudioEngineFactoryInterface, QAudioEngineFactoryInterface_iid) + +class Q_MULTIMEDIA_EXPORT QAudioEnginePlugin : public QObject, public QAudioEngineFactoryInterface +{ + Q_OBJECT + Q_INTERFACES(QAudioEngineFactoryInterface:QFactoryInterface) + +public: + QAudioEnginePlugin(QObject *parent = 0); + ~QAudioEnginePlugin(); + + virtual QStringList keys() const = 0; + virtual QList<QByteArray> deviceList(QAudio::Mode) const = 0; + virtual QAbstractAudioInput* createInput(const QByteArray& device, const QAudioFormat& format = QAudioFormat()) = 0; + virtual QAbstractAudioOutput* createOutput(const QByteArray& device, const QAudioFormat& format = QAudioFormat()) = 0; + virtual QAbstractAudioDeviceInfo* createDeviceInfo(const QByteArray& device, QAudio::Mode mode) = 0; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QAUDIOENGINEPLUGIN_H diff --git a/src/multimedia/audio/qaudioformat.cpp b/src/multimedia/audio/qaudioformat.cpp new file mode 100644 index 0000000..6632c63 --- /dev/null +++ b/src/multimedia/audio/qaudioformat.cpp @@ -0,0 +1,335 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtMultimedia/qaudioformat.h> + + +QT_BEGIN_NAMESPACE + + +class QAudioFormatPrivate : public QSharedData +{ +public: + QAudioFormatPrivate() + { + frequency = -1; + channels = -1; + sampleSize = -1; + byteOrder = QAudioFormat::Endian(QSysInfo::ByteOrder); + sampleType = QAudioFormat::Unknown; + } + + QString codec; + QAudioFormat::Endian byteOrder; + QAudioFormat::SampleType sampleType; + + int frequency; + int channels; + int sampleSize; +}; + +/*! + \class QAudioFormat + \brief The QAudioFormat class stores audio parameter information. + + \inmodule QtMultimedia + \ingroup multimedia + \since 4.6 + + An audio format specifies how data in an audio stream is arranged, + i.e, how the stream is to be interpreted. The encoding itself is + specified by the codec() used for the stream. + + In addition to the encoding, QAudioFormat contains other + parameters that further specify how the audio data is arranged. + These are the frequency, the number of channels, the sample size, + the sample type, and the byte order. The following table describes + these in more detail. + + \table + \header + \o Parameter + \o Description + \row + \o Frequency + \o Samples per second of audio data in Hertz. + \row + \o Number of channels + \o The number of audio channels (typically one for mono + or two for stereo) + \row + \o Sample size + \o How much data is stored in each sample (typically 8 + or 16) + \row + \o Sample type + \o Numerical representation of sample (typically signed integer, + unsigned integer or float) + \row + \o Byte order + \o Byte ordering of sample (typically little endian, big endian) + \endtable + + You can obtain audio formats compatible with the audio device used + through functions in QAudioDeviceInfo. This class also lets you + query available parameter values for a device, so that you can set + the parameters yourself. See the QAudioDeviceInfo class + description for details. +*/ + +/*! + Construct a new audio format. + + Values are initialized as follows: + \list + \o frequency() = -1 + \o channels() = -1 + \o sampleSize() = -1 + \o byteOrder() = QAudioFormat::Endian(QSysInfo::ByteOrder) + \o sampleType() = QAudioFormat::Unknown + \c codec() = "" + \endlist +*/ + +QAudioFormat::QAudioFormat(): + d(new QAudioFormatPrivate) +{ +} + +/*! + Construct a new audio format using \a other. +*/ + +QAudioFormat::QAudioFormat(const QAudioFormat &other): + d(other.d) +{ +} + +/*! + Destroy this audio format. +*/ + +QAudioFormat::~QAudioFormat() +{ +} + +/*! + Assigns \a other to this QAudioFormat implementation. +*/ + +QAudioFormat& QAudioFormat::operator=(const QAudioFormat &other) +{ + d = other.d; + return *this; +} + +/*! + Returns true if this QAudioFormat is equal to the \a other + QAudioFormat; otherwise returns false. + + All elements of QAudioFormat are used for the comparison. +*/ + +bool QAudioFormat::operator==(const QAudioFormat &other) const +{ + return d->frequency == other.d->frequency && + d->channels == other.d->channels && + d->sampleSize == other.d->sampleSize && + d->byteOrder == other.d->byteOrder && + d->codec == other.d->codec && + d->sampleType == other.d->sampleType; +} + +/*! + Returns true if this QAudioFormat is not equal to the \a other + QAudioFormat; otherwise returns false. + + All elements of QAudioFormat are used for the comparison. +*/ + +bool QAudioFormat::operator!=(const QAudioFormat& other) const +{ + return !(*this == other); +} + +/*! + Returns true if any of the parameters are invalid. +*/ + +bool QAudioFormat::isNull() const +{ + return d->frequency == -1 && d->channels == -1 && + d->sampleSize == -1 && + d->byteOrder == QAudioFormat::Endian(QSysInfo::ByteOrder) && + d->sampleType == QAudioFormat::Unknown && + d->codec.isNull(); +} + +/*! + Sets the frequency to \a frequency. +*/ + +void QAudioFormat::setFrequency(int frequency) +{ + d->frequency = frequency; +} + +/*! + Returns the current frequency value. +*/ + +int QAudioFormat::frequency() const +{ + return d->frequency; +} + +/*! + Sets the channels to \a channels. +*/ + +void QAudioFormat::setChannels(int channels) +{ + d->channels = channels; +} + +/*! + Returns the current channel value. +*/ + +int QAudioFormat::channels() const +{ + return d->channels; +} + +/*! + Sets the sampleSize to \a sampleSize. +*/ + +void QAudioFormat::setSampleSize(int sampleSize) +{ + d->sampleSize = sampleSize; +} + +/*! + Returns the current sampleSize value. +*/ + +int QAudioFormat::sampleSize() const +{ + return d->sampleSize; +} + +/*! + Sets the codec to \a codec. + + \sa QAudioDeviceInfo::supportedCodecs() +*/ + +void QAudioFormat::setCodec(QString codec) +{ + d->codec = codec; +} + +/*! + Returns the current codec value. + + \sa QAudioDeviceInfo::supportedCodecs() +*/ + +QString QAudioFormat::codec() const +{ + return d->codec; +} + +/*! + Sets the byteOrder to \a byteOrder. +*/ + +void QAudioFormat::setByteOrder(QAudioFormat::Endian byteOrder) +{ + d->byteOrder = byteOrder; +} + +/*! + Returns the current byteOrder value. +*/ + +QAudioFormat::Endian QAudioFormat::byteOrder() const +{ + return d->byteOrder; +} + +/*! + Sets the sampleType to \a sampleType. +*/ + +void QAudioFormat::setSampleType(QAudioFormat::SampleType sampleType) +{ + d->sampleType = sampleType; +} + +/*! + Returns the current SampleType value. +*/ + +QAudioFormat::SampleType QAudioFormat::sampleType() const +{ + return d->sampleType; +} + +/*! + \enum QAudioFormat::SampleType + + \value Unknown Not Set + \value SignedInt samples are signed integers + \value UnSignedInt samples are unsigned intergers + \value Float samples are floats +*/ + +/*! + \enum QAudioFormat::Endian + + \value BigEndian samples are big endian byte order + \value LittleEndian samples are little endian byte order +*/ + +QT_END_NAMESPACE + diff --git a/src/multimedia/audio/qaudioformat.h b/src/multimedia/audio/qaudioformat.h new file mode 100644 index 0000000..c740969 --- /dev/null +++ b/src/multimedia/audio/qaudioformat.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QAUDIOFORMAT_H +#define QAUDIOFORMAT_H + +#include <QtCore/qobject.h> +#include <QtCore/qglobal.h> +#include <QtCore/qshareddata.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + + +class QAudioFormatPrivate; + +class Q_MULTIMEDIA_EXPORT QAudioFormat +{ +public: + enum SampleType { Unknown, SignedInt, UnSignedInt, Float }; + enum Endian { BigEndian = QSysInfo::BigEndian, LittleEndian = QSysInfo::LittleEndian }; + + QAudioFormat(); + QAudioFormat(const QAudioFormat &other); + ~QAudioFormat(); + + QAudioFormat& operator=(const QAudioFormat &other); + bool operator==(const QAudioFormat &other) const; + bool operator!=(const QAudioFormat &other) const; + + bool isNull() const; + + void setFrequency(int frequency); + int frequency() const; + + void setChannels(int channels); + int channels() const; + + void setSampleSize(int sampleSize); + int sampleSize() const; + + void setCodec(QString codec); + QString codec() const; + + void setByteOrder(QAudioFormat::Endian byteOrder); + QAudioFormat::Endian byteOrder() const; + + void setSampleType(QAudioFormat::SampleType sampleType); + QAudioFormat::SampleType sampleType() const; + +private: + QSharedDataPointer<QAudioFormatPrivate> d; +}; + + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QAUDIOFORMAT_H diff --git a/src/multimedia/audio/qaudioinput.cpp b/src/multimedia/audio/qaudioinput.cpp new file mode 100644 index 0000000..17cacc6 --- /dev/null +++ b/src/multimedia/audio/qaudioinput.cpp @@ -0,0 +1,400 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudioengine.h> +#include <QtMultimedia/qaudioinput.h> + +#include "qaudiodevicefactory_p.h" + + +QT_BEGIN_NAMESPACE + +/*! + \class QAudioInput + \brief The QAudioInput class provides an interface for receiving audio data from an audio input device. + + \inmodule QtMultimedia + \ingroup multimedia + \since 4.6 + + You can construct an audio input with the system's + \l{QAudioDeviceInfo::defaultInputDevice()}{default audio input + device}. It is also possible to create QAudioInput with a + specific QAudioDeviceId. When you create the audio input, you + should also send in the QAudioFormat to be used for the recording + (see the QAudioFormat class description for details). + + To record to a file: + + QAudioInput lets you record audio with an audio input device. The + default constructor of this class will use the systems default + audio device, but you can also specify a QAudioDeviceId for a + specific device. You also need to pass in the QAudioFormat in + which you wish to record. + + Starting up the QAudioInput is simply a matter of calling start() + with a QIODevice opened for writing. For instance, to record to a + file, you can: + + \code + { + QFile outputFile; + outputFile.setFileName("/tmp/test.raw"); + outputFile.open( QIODevice::WriteOnly | QIODevice::Truncate ); + + QAudioFormat format; + // set up the format you want, eg. + format.setFrequency(8000); + format.setChannels(1); + format.setSampleSize(8); + format.setCodec("audio/pcm"); + format.setByteOrder(QAudioFormat::LittleEndian); + format.setSampleType(QAudioFormat::UnSignedInt); + + QAudioInput *audio = new QAudioInput(format, this); + QTimer::singleShot(3000, this, SLOT(stopRecording())); + audio->start(outputFile); + // Records audio for 3000ms + } + \endcode + + This will start recording if the format specified is supported by + the input device (you can check this with + QAudioDeviceInfo::isFormatSupported(). In case there are any + snags, use the error() function to check what went wrong. We stop + recording in the \c stopRecording() slot. + + \code + void stopRecording() + { + audio->stop(); + outputFile->close(); + } + \endcode + + At any point in time, QAudioInput will be in one of four states: + active, suspended, stopped, or idle. These states are specified by + the QAudio::State enum. You can request a state change directly through + suspend(), resume(), stop(), reset(), and start(). The current + state is reported by state(). QAudioOutput will also signal you + when the state changes (stateChanged()). + + QAudioInput provides several ways of measuring the time that has + passed since the start() of the recording. The \c totalTime() + function returns the length of the stream in microseconds written, + i.e., it leaves out the times the audio input was suspended or idle. + The clock() function returns the time elapsed since start() was called regardless of + which states the QAudioInput has been in. + + If an error should occur, you can fetch its reason with error(). + The possible error reasons are described by the QAudio::Error enum. + + \sa QAudioOutput, QAudioDeviceInfo +*/ + +/*! + Construct a new audio input and attach it to \a parent. + The default audio input device is used with the output + \a format parameters. +*/ + +QAudioInput::QAudioInput(const QAudioFormat &format, QObject *parent): + QObject(parent) +{ + d = QAudioDeviceFactory::createDefaultInputDevice(format); + connect(d, SIGNAL(notify()), SIGNAL(notify())); + connect(d, SIGNAL(stateChanged(QAudio::State)), SIGNAL(stateChanged(QAudio::State))); +} + +/*! + Construct a new audio input and attach it to \a parent. + The \a id of the audio input device is used with the input + \a format parameters. +*/ + +QAudioInput::QAudioInput(const QAudioDeviceId &id, const QAudioFormat &format, QObject *parent): + QObject(parent) +{ + d = QAudioDeviceFactory::createInputDevice(id, format); + connect(d, SIGNAL(notify()), SIGNAL(notify())); + connect(d, SIGNAL(stateChanged(QAudio::State)), SIGNAL(stateChanged(QAudio::State))); +} + +/*! + Destroy this audio input. +*/ + +QAudioInput::~QAudioInput() +{ + delete d; +} + +/*! + Uses the \a device as the QIODevice to transfer data. + If \a device is null then the class creates an internal QIODevice. + Returns a pointer to the QIODevice being used to handle the data + transfer. This QIODevice can be used to read() audio data + directly. + Passing a QIODevice allows the data to be transfered without any extra code. + All that is required is to open the QIODevice. + + /sa QIODevice +*/ + +QIODevice* QAudioInput::start(QIODevice* device) +{ + /* + PULL MODE (valid QIODevice) + -If currently not StopState, stop + -If previous start was push mode, delete internal QIODevice. + -open audio input. + If ok, NoError and ActiveState, else OpenError and StopState. + -emit stateChanged() + -return device + + PUSH MODE (device = 0) + -If currently not StopState, stop + -If no internal QIODevice, create one. + -open audio input. + -If ok, NoError and IdleState, else OpenError and StopState + -emit stateChanged() + -return internal QIODevice + */ + return d->start(device); +} + +/*! + Returns the QAudioFormat being used. +*/ + +QAudioFormat QAudioInput::format() const +{ + return d->format(); +} + +/*! + Stops the audio input. +*/ + +void QAudioInput::stop() +{ + /* + -If StopState, return + -set to StopState + -detach from audio device + -emit stateChanged() + */ + d->stop(); +} + +/*! + Drops all audio data in the buffers, resets buffers to zero. +*/ + +void QAudioInput::reset() +{ + /* + -drop all buffered audio, set buffers to zero. + -call stop() + */ + d->reset(); +} + +/*! + Stops processing audio data, preserving buffered audio data. +*/ + +void QAudioInput::suspend() +{ + /* + -If not ActiveState|IdleState, return + -stop processing audio, saving all buffered audio data + -set NoError and SuspendState + -emit stateChanged() + */ + d->suspend(); +} + +/*! + Resumes processing audio data after a suspend(). +*/ + +void QAudioInput::resume() +{ + /* + -If SuspendState, return + -resume audio + -(PULL MODE): set ActiveState, NoError + -(PUSH MODE): set IdleState, NoError + -kick start audio if needed + -emit stateChanged() + */ + d->resume(); +} + +/*! + Sets the audio buffer size to \a value milliseconds. + + Note: This function can be called anytime before start(), calls to this + are ignored after start(). It should not be assumed that the buffer size + set is the actual buffer size used, calling bufferSize() anytime after start() + will return the actual buffer size being used. + +*/ + +void QAudioInput::setBufferSize(int value) +{ + d->setBufferSize(value); +} + +/*! + Returns the audio buffer size in milliseconds. + + If called before start(), returns platform default value. + If called before start() but setBufferSize() was called prior, returns value set by setBufferSize(). + If called after start(), returns the actual buffer size being used. This may not be what was set previously + by setBufferSize(). + +*/ + +int QAudioInput::bufferSize() const +{ + return d->bufferSize(); +} + +/*! + Returns the amount of audio data available to read in bytes. +*/ + +int QAudioInput::bytesReady() const +{ + /* + -If not ActiveState|IdleState, return 0 + -return amount of audio data available to read + */ + return d->bytesReady(); +} + +/*! + Returns the period size in bytes. + + Note: This is the recommended read size in bytes. +*/ + +int QAudioInput::periodSize() const +{ + return d->periodSize(); +} + +/*! + Sets the interval for notify() signal to be emitted. + This is based on the \a ms of audio data processed + not on actual real-time. The resolution of the timer is platform specific. +*/ + +void QAudioInput::setNotifyInterval(int ms) +{ + d->setNotifyInterval(ms); +} + +/*! + Returns the notify interval in milliseconds. +*/ + +int QAudioInput::notifyInterval() const +{ + return d->notifyInterval(); +} + +/*! + Returns the amount of audio data processed since start() + was called in microseconds. +*/ + +qint64 QAudioInput::totalTime() const +{ + return d->totalTime(); +} + +/*! + Returns the microseconds since start() was called, including time in Idle and + Suspend states. +*/ + +qint64 QAudioInput::clock() const +{ + return d->clock(); +} + +/*! + Returns the error state. +*/ + +QAudio::Error QAudioInput::error() const +{ + return d->error(); +} + +/*! + Returns the state of audio processing. +*/ + +QAudio::State QAudioInput::state() const +{ + return d->state(); +} + +/*! + \fn QAudioInput::stateChanged(QAudio::State state) + This signal is emitted when the device \a state has changed. +*/ + +/*! + \fn QAudioInput::notify() + This signal is emitted when x ms of audio data has been processed + the interval set by setNotifyInterval(x). +*/ + +QT_END_NAMESPACE + diff --git a/src/multimedia/audio/qaudioinput.h b/src/multimedia/audio/qaudioinput.h new file mode 100644 index 0000000..b0f1aed --- /dev/null +++ b/src/multimedia/audio/qaudioinput.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QAUDIOINPUT_H +#define QAUDIOINPUT_H + +#include <QtCore/qiodevice.h> +#include <QtCore/qglobal.h> + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudioformat.h> +#include <QtMultimedia/qaudiodeviceid.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + + +class QAbstractAudioInput; + +class Q_MULTIMEDIA_EXPORT QAudioInput : public QObject +{ + Q_OBJECT + +public: + explicit QAudioInput(const QAudioFormat &format = QAudioFormat(), QObject *parent = 0); + explicit QAudioInput(const QAudioDeviceId &id, const QAudioFormat &format = QAudioFormat(), QObject *parent = 0); + ~QAudioInput(); + + QAudioFormat format() const; + + QIODevice* start(QIODevice *device = 0); + void stop(); + void reset(); + void suspend(); + void resume(); + + void setBufferSize(int bytes); + int bufferSize() const; + + int bytesReady() const; + int periodSize() const; + + void setNotifyInterval(int milliSeconds); + int notifyInterval() const; + + qint64 totalTime() const; + qint64 clock() const; + + QAudio::Error error() const; + QAudio::State state() const; + +Q_SIGNALS: + void stateChanged(QAudio::State); + void notify(); + +private: + Q_DISABLE_COPY(QAudioInput); + + QAbstractAudioInput* d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QAUDIOINPUT_H diff --git a/src/multimedia/audio/qaudioinput_alsa_p.cpp b/src/multimedia/audio/qaudioinput_alsa_p.cpp new file mode 100644 index 0000000..6f00469 --- /dev/null +++ b/src/multimedia/audio/qaudioinput_alsa_p.cpp @@ -0,0 +1,688 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qcoreapplication.h> +#include "qaudioinput_alsa_p.h" + +//#define DEBUG_AUDIO 1 + +QAudioInputPrivate::QAudioInputPrivate(const QByteArray &device, const QAudioFormat& audioFormat): + settings(audioFormat) +{ + bytesAvailable = 0; + handle = 0; + ahandler = 0; + access = SND_PCM_ACCESS_RW_INTERLEAVED; + pcmformat = SND_PCM_FORMAT_S16; + buffer_size = 0; + period_size = 0; + buffer_time = 100000; + period_time = 20000; + totalTimeValue = 0; + intervalTime = 1000; + audioBuffer = 0; + errorState = QAudio::NoError; + deviceState = QAudio::StopState; + audioSource = 0; + pullMode = true; + resuming = false; + + QStringList list1 = QString(tr(device)).split(tr(":")); + m_device = QByteArray(list1.at(0).toLocal8Bit().constData()); + + timer = new QTimer(this); + connect(timer,SIGNAL(timeout()),SLOT(userFeed())); +} + +QAudioInputPrivate::~QAudioInputPrivate() +{ + close(); + disconnect(timer, SIGNAL(timeout())); + QCoreApplication::processEvents(); + delete timer; +} + +QAudio::Error QAudioInputPrivate::error() const +{ + return errorState; +} + +QAudio::State QAudioInputPrivate::state() const +{ + return deviceState; +} + + +QAudioFormat QAudioInputPrivate::format() const +{ + return settings; +} + +int QAudioInputPrivate::xrun_recovery(int err) +{ + int count = 0; + bool reset = false; + + if(err == -EPIPE) { + errorState = QAudio::UnderrunError; + err = snd_pcm_prepare(handle); + if(err < 0) + reset = true; + + } else if((err == -ESTRPIPE)||(err == -EIO)) { + errorState = QAudio::IOError; + while((err = snd_pcm_resume(handle)) == -EAGAIN){ + usleep(100); + count++; + if(count > 5) { + reset = true; + break; + } + } + if(err < 0) { + err = snd_pcm_prepare(handle); + if(err < 0) + reset = true; + } + } + if(reset) { + close(); + open(); + snd_pcm_prepare(handle); + return 0; + } + return err; +} + +int QAudioInputPrivate::setFormat() +{ + snd_pcm_format_t format = SND_PCM_FORMAT_S16; + + if(settings.sampleSize() == 8) { + format = SND_PCM_FORMAT_U8; + } else if(settings.sampleSize() == 16) { + if(settings.sampleType() == QAudioFormat::SignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + format = SND_PCM_FORMAT_S16_LE; + else + format = SND_PCM_FORMAT_S16_BE; + } else if(settings.sampleType() == QAudioFormat::UnSignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + format = SND_PCM_FORMAT_U16_LE; + else + format = SND_PCM_FORMAT_U16_BE; + } + } else if(settings.sampleSize() == 24) { + if(settings.sampleType() == QAudioFormat::SignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + format = SND_PCM_FORMAT_S24_LE; + else + format = SND_PCM_FORMAT_S24_BE; + } else if(settings.sampleType() == QAudioFormat::UnSignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + format = SND_PCM_FORMAT_U24_LE; + else + format = SND_PCM_FORMAT_U24_BE; + } + } else if(settings.sampleSize() == 32) { + if(settings.sampleType() == QAudioFormat::SignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + format = SND_PCM_FORMAT_S32_LE; + else + format = SND_PCM_FORMAT_S32_BE; + } else if(settings.sampleType() == QAudioFormat::UnSignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + format = SND_PCM_FORMAT_U32_LE; + else + format = SND_PCM_FORMAT_U32_BE; + } else if(settings.sampleType() == QAudioFormat::Float) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + format = SND_PCM_FORMAT_FLOAT_LE; + else + format = SND_PCM_FORMAT_FLOAT_BE; + } + } else if(settings.sampleSize() == 64) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + format = SND_PCM_FORMAT_FLOAT64_LE; + else + format = SND_PCM_FORMAT_FLOAT64_BE; + } + + return snd_pcm_hw_params_set_format( handle, hwparams, format); +} + +QIODevice* QAudioInputPrivate::start(QIODevice* device) +{ + if(deviceState != QAudio::StopState) + close(); + + if(!pullMode && audioSource) { + delete audioSource; + } + + if(device) { + //set to pull mode + pullMode = true; + audioSource = device; + } else { + //set to push mode + pullMode = false; + audioSource = new InputPrivate(this); + audioSource->open(QIODevice::ReadOnly | QIODevice::Unbuffered); + } + + if( !open() ) + return 0; + + emit stateChanged(deviceState); + + return audioSource; +} + +void QAudioInputPrivate::stop() +{ + if(deviceState == QAudio::StopState) + return; + + deviceState = QAudio::StopState; + + close(); + emit stateChanged(deviceState); +} + +bool QAudioInputPrivate::open() +{ +#ifdef DEBUG_AUDIO + QTime now(QTime::currentTime()); + qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()"; +#endif + timeStamp.restart(); + + int dir; + int err=-1; + int count=0; + unsigned int freakuency=settings.frequency(); + + QString dev = QString(tr(m_device.constData())); + if(!dev.contains(tr("default"))) { + dev = QString(tr("default:CARD=%1")).arg(tr(m_device.constData())); + } + + // Step 1: try and open the device + while((count < 5) && (err < 0)) { + err=snd_pcm_open(&handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_CAPTURE,0); + if(err < 0) + count++; + } + if (( err < 0)||(handle == 0)) { + errorState = QAudio::OpenError; + deviceState = QAudio::StopState; + emit stateChanged(deviceState); + return false; + } + snd_pcm_nonblock( handle, 0 ); + + // Step 2: Set the desired HW parameters. + snd_pcm_hw_params_alloca( &hwparams ); + + bool fatal = false; + QString errMessage; + unsigned int chunks = 8; + + err = snd_pcm_hw_params_any( handle, hwparams ); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioInput: snd_pcm_hw_params_any: err = %1")).arg(err); + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_rate_resample( handle, hwparams, 1 ); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioInput: snd_pcm_hw_params_set_rate_resample: err = %1")).arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_access( handle, hwparams, access ); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioInput: snd_pcm_hw_params_set_access: err = %1")).arg(err); + } + } + if ( !fatal ) { + err = setFormat(); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioInput: snd_pcm_hw_params_set_format: err = %1")).arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_channels( handle, hwparams, (unsigned int)settings.channels() ); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioInput: snd_pcm_hw_params_set_channels: err = %1")).arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_rate_near( handle, hwparams, &freakuency, 0 ); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioInput: snd_pcm_hw_params_set_rate_near: err = %1")).arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, &dir); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioInput: snd_pcm_hw_params_set_buffer_time_near: err = %1")).arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, &dir); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioInput: snd_pcm_hw_params_set_period_time_near: err = %1")).arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_periods_near(handle, hwparams, &chunks, &dir); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioInput: snd_pcm_hw_params_set_periods_near: err = %1")).arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params(handle, hwparams); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioInput: snd_pcm_hw_params: err = %1")).arg(err); + } + } + if( err < 0) { + qWarning()<<errMessage; + errorState = QAudio::OpenError; + deviceState = QAudio::StopState; + emit stateChanged(deviceState); + return false; + } + snd_pcm_hw_params_get_buffer_size(hwparams,&buffer_frames); + buffer_size = snd_pcm_frames_to_bytes(handle,buffer_frames); + snd_pcm_hw_params_get_period_size(hwparams,&period_frames, &dir); + period_size = snd_pcm_frames_to_bytes(handle,period_frames); + snd_pcm_hw_params_get_buffer_time(hwparams,&buffer_time, &dir); + snd_pcm_hw_params_get_period_time(hwparams,&period_time, &dir); + + // Step 3: Set the desired SW parameters. + snd_pcm_sw_params_t *swparams; + snd_pcm_sw_params_alloca(&swparams); + snd_pcm_sw_params_current(handle, swparams); + snd_pcm_sw_params_set_start_threshold(handle,swparams,period_frames); + snd_pcm_sw_params_set_stop_threshold(handle,swparams,buffer_frames); + snd_pcm_sw_params_set_avail_min(handle, swparams,period_frames); + snd_pcm_sw_params(handle, swparams); + + // Step 4: Prepare audio + if(audioBuffer == 0) + audioBuffer = new char[buffer_size]; + snd_pcm_prepare( handle ); + snd_pcm_start(handle); + + // Step 5: Setup timer + bytesAvailable = bytesReady(); + + if(pullMode) + connect(audioSource,SIGNAL(readyRead()),this,SLOT(userFeed())); + + // Step 6: Start audio processing + chunks = buffer_size/period_size; + timer->start(period_time*chunks/2000); + + errorState = QAudio::NoError; + deviceState = QAudio::ActiveState; + + totalTimeValue = 0; + + return true; +} + +void QAudioInputPrivate::close() +{ + deviceState = QAudio::StopState; + timer->stop(); + + if ( handle ) { + snd_pcm_drop( handle ); + snd_pcm_close( handle ); + handle = 0; + delete [] audioBuffer; + audioBuffer=0; + } +} + +int QAudioInputPrivate::bytesReady() const +{ + if(resuming) + return period_size; + + if(deviceState != QAudio::ActiveState) + return 0; + int frames = snd_pcm_avail_update(handle); + if((int)frames > (int)buffer_frames) + frames = buffer_frames; + + return snd_pcm_frames_to_bytes(handle, frames); +} + +qint64 QAudioInputPrivate::read(char* data, qint64 len) +{ + Q_UNUSED(data) + Q_UNUSED(len) + // Read in some audio data and write it to QIODevice, pull mode + if ( !handle ) + return 0; + + bytesAvailable = bytesReady(); + + int count=0, err = 0; + while(count < 5) { + int chunks = bytesAvailable/period_size; + int frames = chunks*period_frames; + if(frames > (int)buffer_frames) + frames = buffer_frames; + int readFrames = snd_pcm_readi(handle, audioBuffer, frames); + if (readFrames >= 0) { + err = snd_pcm_frames_to_bytes(handle, readFrames); +#ifdef DEBUG_AUDIO + qDebug()<<QString(tr("PULL: read in bytes = %1 (frames=%2)")).arg(err).arg(readFrames).toLatin1().constData(); +#endif + break; + } else if((readFrames == -EAGAIN) || (readFrames == -EINTR)) { + errorState = QAudio::IOError; + err = 0; + break; + } else { + if(readFrames == -EPIPE) { + errorState = QAudio::UnderrunError; + err = snd_pcm_prepare(handle); + } else if(readFrames == -ESTRPIPE) { + err = snd_pcm_prepare(handle); + } + if(err != 0) break; + } + count++; + } + if(err > 0) { + // got some send it onward +#ifdef DEBUG_AUDIO + qDebug()<<"PULL: frames to write to QIODevice = "<< + snd_pcm_bytes_to_frames( handle, (int)err )<<" ("<<err<<") bytes"; +#endif + if(deviceState != QAudio::ActiveState) + return 0; + + qint64 l = audioSource->write(audioBuffer,err); + if(l < 0) { + close(); + errorState = QAudio::IOError; + deviceState = QAudio::StopState; + emit stateChanged(deviceState); + } else if(l == 0) { + errorState = QAudio::NoError; + deviceState = QAudio::IdleState; + } else { + totalTimeValue += snd_pcm_bytes_to_frames(handle, err)*1000000/settings.frequency(); + resuming = false; + errorState = QAudio::NoError; + deviceState = QAudio::ActiveState; + } + return l; + } + return 0; +} + +void QAudioInputPrivate::resume() +{ + if(deviceState == QAudio::SuspendState) { + int err = 0; + + if(handle) { + err = snd_pcm_prepare( handle ); + if(err < 0) + xrun_recovery(err); + + err = snd_pcm_start(handle); + if(err < 0) + xrun_recovery(err); + + bytesAvailable = buffer_size; + } + resuming = true; + deviceState = QAudio::ActiveState; + int chunks = buffer_size/period_size; + timer->start(buffer_time*chunks/2000); + emit stateChanged(deviceState); + } +} + +void QAudioInputPrivate::setBufferSize(int value) +{ + buffer_size = value; +} + +int QAudioInputPrivate::bufferSize() const +{ + return buffer_size; +} + +int QAudioInputPrivate::periodSize() const +{ + return period_size; +} + +void QAudioInputPrivate::setNotifyInterval(int ms) +{ + intervalTime = ms; +} + +int QAudioInputPrivate::notifyInterval() const +{ + return intervalTime; +} + +qint64 QAudioInputPrivate::totalTime() const +{ + return totalTimeValue; +} + +void QAudioInputPrivate::suspend() +{ + if(deviceState == QAudio::ActiveState||resuming) { + timer->stop(); + deviceState = QAudio::SuspendState; + emit stateChanged(deviceState); + } +} + +void QAudioInputPrivate::userFeed() +{ + if(deviceState == QAudio::StopState || deviceState == QAudio::SuspendState) + return; +#ifdef DEBUG_AUDIO + QTime now(QTime::currentTime()); + qDebug()<<now.second()<<"s "<<now.msec()<<"ms :userFeed() IN"; +#endif + deviceReady(); +} + +bool QAudioInputPrivate::deviceReady() +{ + if(pullMode) { + // reads some audio data and writes it to QIODevice + read(0,0); + } else { + // emits readyRead() so user will call read() on QIODevice to get some audio data + InputPrivate* a = qobject_cast<InputPrivate*>(audioSource); + a->trigger(); + } + bytesAvailable = bytesReady(); + + if(deviceState != QAudio::ActiveState) + return true; + + if(timeStamp.elapsed() > intervalTime && intervalTime > 50) { + emit notify(); + timeStamp.restart(); + } + return true; +} + +qint64 QAudioInputPrivate::clock() const +{ + if(!handle) + return 0; + + if(deviceState != QAudio::ActiveState) + return 0; + + snd_pcm_status_t* status; + snd_pcm_status_alloca(&status); + + snd_timestamp_t t1,t2; + if( snd_pcm_status(handle, status) >= 0) { + snd_pcm_status_get_tstamp(status,&t1); + snd_pcm_status_get_trigger_tstamp(status,&t2); + t1.tv_sec-=t2.tv_sec; + + signed long l = (signed long)t1.tv_usec - (signed long)t2.tv_usec; + if(l < 0) { + t1.tv_sec--; + l = -l; + l %= 1000000; + } + return ((t1.tv_sec * 1000)+l/1000); + } else + return 0; +} + +void QAudioInputPrivate::reset() +{ + if(handle) + snd_pcm_reset(handle); +} + +void QAudioInputPrivate::drain() +{ + if(handle) + snd_pcm_drain(handle); +} + +InputPrivate::InputPrivate(QAudioInputPrivate* audio) +{ + audioDevice = qobject_cast<QAudioInputPrivate*>(audio); +} + +InputPrivate::~InputPrivate() +{ +} + +qint64 InputPrivate::readData( char* data, qint64 len) +{ + // push mode, user read() called + if((audioDevice->state() != QAudio::ActiveState) && !audioDevice->resuming) + return 0; + + int readFrames; + int count=0, err = 0; + + while(count < 5) { + int frames = snd_pcm_bytes_to_frames(audioDevice->handle, len); + readFrames = snd_pcm_readi(audioDevice->handle, data, frames); + if (readFrames >= 0) { + err = snd_pcm_frames_to_bytes(audioDevice->handle, readFrames); +#ifdef DEBUG_AUDIO + qDebug()<<QString(tr("PUSH: read in bytes = %1 (frames=%2)")).arg(err).arg(readFrames).toLatin1().constData(); +#endif + break; + } else if((readFrames == -EAGAIN) || (readFrames == -EINTR)) { + audioDevice->errorState = QAudio::IOError; + err = 0; + break; + } else { + if(readFrames == -EPIPE) { + audioDevice->errorState = QAudio::UnderrunError; + err = snd_pcm_prepare(audioDevice->handle); + } else if(readFrames == -ESTRPIPE) { + err = snd_pcm_prepare(audioDevice->handle); + } + if(err != 0) break; + } + count++; + } + if(err > 0 && readFrames > 0) { + audioDevice->totalTimeValue += readFrames*1000/audioDevice->settings.frequency()*1000; + audioDevice->deviceState = QAudio::ActiveState; + return err; + } + return 0; +} + +qint64 InputPrivate::writeData(const char* data, qint64 len) +{ + Q_UNUSED(data) + Q_UNUSED(len) + return 0; +} + +void InputPrivate::trigger() +{ + emit readyRead(); +} + diff --git a/src/multimedia/audio/qaudioinput_alsa_p.h b/src/multimedia/audio/qaudioinput_alsa_p.h new file mode 100644 index 0000000..21c8064 --- /dev/null +++ b/src/multimedia/audio/qaudioinput_alsa_p.h @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#ifndef QAUDIOINPUTALSA_H +#define QAUDIOINPUTALSA_H + +#include <alsa/asoundlib.h> + +#include <QtCore/qfile.h> +#include <QtCore/qdebug.h> +#include <QtCore/qtimer.h> +#include <QtCore/qstring.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qdatetime.h> + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudioengine.h> + +class InputPrivate; + +class QAudioInputPrivate : public QAbstractAudioInput +{ + Q_OBJECT +public: + QAudioInputPrivate(const QByteArray &device, const QAudioFormat& audioFormat); + ~QAudioInputPrivate(); + + qint64 read(char* data, qint64 len); + + QIODevice* start(QIODevice* device = 0); + void stop(); + void reset(); + void suspend(); + void resume(); + int bytesReady() const; + int periodSize() const; + void setBufferSize(int value); + int bufferSize() const; + void setNotifyInterval(int milliSeconds); + int notifyInterval() const; + qint64 totalTime() const; + qint64 clock() const; + QAudio::Error error() const; + QAudio::State state() const; + QAudioFormat format() const; + bool resuming; + snd_pcm_t* handle; + qint64 totalTimeValue; + QIODevice* audioSource; + QAudioFormat settings; + QAudio::Error errorState; + QAudio::State deviceState; + +private slots: + void userFeed(); + bool deviceReady(); + +private: + int xrun_recovery(int err); + int setFormat(); + bool open(); + void close(); + void drain(); + + QTimer* timer; + QTime timeStamp; + int intervalTime; + char* audioBuffer; + int bytesAvailable; + QByteArray m_device; + bool pullMode; + int buffer_size; + int period_size; + unsigned int buffer_time; + unsigned int period_time; + snd_pcm_uframes_t buffer_frames; + snd_pcm_uframes_t period_frames; + snd_async_handler_t* ahandler; + snd_pcm_access_t access; + snd_pcm_format_t pcmformat; + snd_timestamp_t* timestamp; + snd_pcm_hw_params_t *hwparams; +}; + +class InputPrivate : public QIODevice +{ + Q_OBJECT +public: + InputPrivate(QAudioInputPrivate* audio); + ~InputPrivate(); + + qint64 readData( char* data, qint64 len); + qint64 writeData(const char* data, qint64 len); + + void trigger(); +private: + QAudioInputPrivate *audioDevice; +}; + +#endif diff --git a/src/multimedia/audio/qaudioinput_mac_p.cpp b/src/multimedia/audio/qaudioinput_mac_p.cpp new file mode 100644 index 0000000..5400c85 --- /dev/null +++ b/src/multimedia/audio/qaudioinput_mac_p.cpp @@ -0,0 +1,930 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qendian.h> +#include <QtCore/qtimer.h> +#include <QtCore/qdebug.h> + +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudioinput.h> + +#include "qaudio_mac_p.h" +#include "qaudioinput_mac_p.h" + + +QT_BEGIN_NAMESPACE + + +namespace +{ + +static const int default_buffer_size = 4 * 1024; + +class QAudioBufferList +{ +public: + QAudioBufferList(AudioStreamBasicDescription const& streamFormat): + owner(false), + sf(streamFormat) + { + const bool isInterleaved = (sf.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0; + const int numberOfBuffers = isInterleaved ? 1 : sf.mChannelsPerFrame; + + dataSize = 0; + + bfs = reinterpret_cast<AudioBufferList*>(qMalloc(sizeof(AudioBufferList) + + (sizeof(AudioBuffer) * numberOfBuffers))); + + bfs->mNumberBuffers = numberOfBuffers; + for (int i = 0; i < numberOfBuffers; ++i) { + bfs->mBuffers[i].mNumberChannels = isInterleaved ? numberOfBuffers : 1; + bfs->mBuffers[i].mDataByteSize = 0; + bfs->mBuffers[i].mData = 0; + } + } + + QAudioBufferList(AudioStreamBasicDescription const& streamFormat, char* buffer, int bufferSize): + owner(false), + sf(streamFormat), + bfs(0) + { + dataSize = bufferSize; + + bfs = reinterpret_cast<AudioBufferList*>(qMalloc(sizeof(AudioBufferList) + sizeof(AudioBuffer))); + + bfs->mNumberBuffers = 1; + bfs->mBuffers[0].mNumberChannels = 1; + bfs->mBuffers[0].mDataByteSize = dataSize; + bfs->mBuffers[0].mData = buffer; + } + + QAudioBufferList(AudioStreamBasicDescription const& streamFormat, int framesToBuffer): + owner(true), + sf(streamFormat), + bfs(0) + { + const bool isInterleaved = (sf.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0; + const int numberOfBuffers = isInterleaved ? 1 : sf.mChannelsPerFrame; + + dataSize = framesToBuffer * sf.mBytesPerFrame; + + bfs = reinterpret_cast<AudioBufferList*>(qMalloc(sizeof(AudioBufferList) + + (sizeof(AudioBuffer) * numberOfBuffers))); + bfs->mNumberBuffers = numberOfBuffers; + for (int i = 0; i < numberOfBuffers; ++i) { + bfs->mBuffers[i].mNumberChannels = isInterleaved ? numberOfBuffers : 1; + bfs->mBuffers[i].mDataByteSize = dataSize; + bfs->mBuffers[i].mData = qMalloc(dataSize); + } + } + + ~QAudioBufferList() + { + if (owner) { + for (UInt32 i = 0; i < bfs->mNumberBuffers; ++i) + qFree(bfs->mBuffers[i].mData); + } + + qFree(bfs); + } + + AudioBufferList* audioBufferList() const + { + return bfs; + } + + char* data(int buffer = 0) const + { + return static_cast<char*>(bfs->mBuffers[buffer].mData); + } + + qint64 bufferSize(int buffer = 0) const + { + return bfs->mBuffers[buffer].mDataByteSize; + } + + int frameCount(int buffer = 0) const + { + return bfs->mBuffers[buffer].mDataByteSize / sf.mBytesPerFrame; + } + + int packetCount(int buffer = 0) const + { + return bfs->mBuffers[buffer].mDataByteSize / sf.mBytesPerPacket; + } + + int packetSize() const + { + return sf.mBytesPerPacket; + } + + void reset() + { + for (UInt32 i = 0; i < bfs->mNumberBuffers; ++i) + bfs->mBuffers[i].mDataByteSize = dataSize; + } + +private: + bool owner; + int dataSize; + AudioStreamBasicDescription sf; + AudioBufferList* bfs; +}; + +class QAudioPacketFeeder +{ +public: + QAudioPacketFeeder(QAudioBufferList* abl): + audioBufferList(abl) + { + totalPackets = audioBufferList->packetCount(); + position = 0; + } + + bool feed(AudioBufferList& dst, UInt32& packetCount) + { + if (position == totalPackets) { + dst.mBuffers[0].mDataByteSize = 0; + packetCount = 0; + return false; + } + + if (totalPackets - position < packetCount) + packetCount = totalPackets - position; + + dst.mBuffers[0].mDataByteSize = packetCount * audioBufferList->packetSize(); + dst.mBuffers[0].mData = audioBufferList->data() + (position * audioBufferList->packetSize()); + + position += packetCount; + + return true; + } + +private: + UInt32 totalPackets; + UInt32 position; + QAudioBufferList* audioBufferList; +}; + +class QAudioInputBuffer : public QObject +{ + Q_OBJECT + +public: + QAudioInputBuffer(int bufferSize, + int maxPeriodSize, + AudioStreamBasicDescription const& inputFormat, + AudioStreamBasicDescription const& outputFormat, + QObject* parent): + QObject(parent), + m_deviceError(false), + m_inputFormat(inputFormat), + m_outputFormat(outputFormat) + { + m_maxPeriodSize = maxPeriodSize; + m_periodTime = m_maxPeriodSize / m_outputFormat.mBytesPerFrame * 1000 / m_outputFormat.mSampleRate; + m_buffer = new QAudioRingBuffer(bufferSize + (bufferSize % maxPeriodSize == 0 ? 0 : maxPeriodSize - (bufferSize % maxPeriodSize))); + m_inputBufferList = new QAudioBufferList(m_inputFormat); + + m_flushTimer = new QTimer(this); + connect(m_flushTimer, SIGNAL(timeout()), SLOT(flushBuffer())); + + if (inputFormat.mSampleRate != outputFormat.mSampleRate) { + if (AudioConverterNew(&m_inputFormat, &m_outputFormat, &m_audioConverter) != noErr) { + qWarning() << "QAudioInput: Unable to create an Audio Converter"; + m_audioConverter = 0; + } + } + } + + ~QAudioInputBuffer() + { + delete m_buffer; + } + + qint64 renderFromDevice(AudioUnit audioUnit, + AudioUnitRenderActionFlags* ioActionFlags, + const AudioTimeStamp* inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames) + { + const bool wasEmpty = m_buffer->used() == 0; + + OSStatus err; + qint64 framesRendered = 0; + + m_inputBufferList->reset(); + err = AudioUnitRender(audioUnit, + ioActionFlags, + inTimeStamp, + inBusNumber, + inNumberFrames, + m_inputBufferList->audioBufferList()); + + if (m_audioConverter != 0) { + QAudioPacketFeeder feeder(m_inputBufferList); + + bool wecan = true; + int copied = 0; + + const int available = m_buffer->free(); + + while (err == noErr && wecan) { + QAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(available); + + if (region.second > 0) { + AudioBufferList output; + output.mNumberBuffers = 1; + output.mBuffers[0].mNumberChannels = 1; + output.mBuffers[0].mDataByteSize = region.second; + output.mBuffers[0].mData = region.first; + + UInt32 packetSize = region.second / m_outputFormat.mBytesPerPacket; + err = AudioConverterFillComplexBuffer(m_audioConverter, + converterCallback, + &feeder, + &packetSize, + &output, + 0); + + region.second = output.mBuffers[0].mDataByteSize; + copied += region.second; + + m_buffer->releaseWriteRegion(region); + } + else + wecan = false; + } + + framesRendered += copied / m_outputFormat.mBytesPerFrame; + } + else { + const int available = m_inputBufferList->bufferSize(); + bool wecan = true; + int copied = 0; + + while (wecan && copied < available) { + QAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(available - copied); + + if (region.second > 0) { + memcpy(region.first, m_inputBufferList->data() + copied, region.second); + copied += region.second; + } + else + wecan = false; + + m_buffer->releaseWriteRegion(region); + } + + framesRendered = copied / m_outputFormat.mBytesPerFrame; + } + + if (wasEmpty && framesRendered > 0) + emit readyRead(); + + return framesRendered; + } + + qint64 readBytes(char* data, qint64 len) + { + bool wecan = true; + qint64 bytesCopied = 0; + + len -= len % m_maxPeriodSize; + while (wecan && bytesCopied < len) { + QAudioRingBuffer::Region region = m_buffer->acquireReadRegion(len - bytesCopied); + + if (region.second > 0) { + memcpy(data + bytesCopied, region.first, region.second); + bytesCopied += region.second; + } + else + wecan = false; + + m_buffer->releaseReadRegion(region); + } + + return bytesCopied; + } + + void setFlushDevice(QIODevice* device) + { + if (m_device != device) + m_device = device; + } + + void startFlushTimer() + { + if (m_device != 0) { + m_flushTimer->start((m_buffer->size() - (m_maxPeriodSize * 2)) / m_maxPeriodSize * m_periodTime); + } + } + + void stopFlushTimer() + { + m_flushTimer->stop(); + } + + void flush(bool all = false) + { + const int used = m_buffer->used(); + const int readSize = all ? used : used - (used % m_maxPeriodSize); + + if (readSize > 0) { + bool wecan = true; + int flushed = 0; + + while (!m_deviceError && wecan && flushed < readSize) { + QAudioRingBuffer::Region region = m_buffer->acquireReadRegion(readSize - flushed); + + if (region.second > 0) { + int bytesWritten = m_device->write(region.first, region.second); + if (bytesWritten < 0) { + stopFlushTimer(); + m_deviceError = true; + } + else { + region.second = bytesWritten; + flushed += bytesWritten; + wecan = bytesWritten != 0; + } + } + else + wecan = false; + + m_buffer->releaseReadRegion(region); + } + } + } + + void reset() + { + m_buffer->reset(); + m_deviceError = false; + } + + int available() const + { + return m_buffer->free(); + } + + int used() const + { + return m_buffer->used(); + } + +signals: + void readyRead(); + +private slots: + void flushBuffer() + { + flush(); + } + +private: + bool m_deviceError; + int m_maxPeriodSize; + int m_periodTime; + QIODevice* m_device; + QTimer* m_flushTimer; + QAudioRingBuffer* m_buffer; + QAudioBufferList* m_inputBufferList; + AudioConverterRef m_audioConverter; + AudioStreamBasicDescription m_inputFormat; + AudioStreamBasicDescription m_outputFormat; + + const static OSStatus as_empty = 'qtem'; + + // Converter callback + static OSStatus converterCallback(AudioConverterRef inAudioConverter, + UInt32* ioNumberDataPackets, + AudioBufferList* ioData, + AudioStreamPacketDescription** outDataPacketDescription, + void* inUserData) + { + Q_UNUSED(inAudioConverter); + Q_UNUSED(outDataPacketDescription); + + QAudioPacketFeeder* feeder = static_cast<QAudioPacketFeeder*>(inUserData); + + if (!feeder->feed(*ioData, *ioNumberDataPackets)) + return as_empty; + + return noErr; + } +}; + + +class MacInputDevice : public QIODevice +{ + Q_OBJECT + +public: + MacInputDevice(QAudioInputBuffer* audioBuffer, QObject* parent): + QIODevice(parent), + m_audioBuffer(audioBuffer) + { + open(QIODevice::ReadOnly | QIODevice::Unbuffered); + connect(m_audioBuffer, SIGNAL(readyRead()), SIGNAL(readyRead())); + } + + qint64 readData(char* data, qint64 len) + { + return m_audioBuffer->readBytes(data, len); + } + + qint64 writeData(const char* data, qint64 len) + { + Q_UNUSED(data); + Q_UNUSED(len); + + return 0; + } + + bool isSequential() const + { + return true; + } + +private: + QAudioInputBuffer* m_audioBuffer; +}; + +} + + +QAudioInputPrivate::QAudioInputPrivate(const QByteArray& device, QAudioFormat const& format): + audioFormat(format) +{ + QDataStream ds(device); + quint32 did, mode; + + ds >> did >> mode; + + if (QAudio::Mode(mode) == QAudio::AudioOutput) + errorCode = QAudio::OpenError; + else { + isOpen = false; + audioDeviceId = AudioDeviceID(did); + audioUnit = 0; + startTime = 0; + totalFrames = 0; + audioBuffer = 0; + internalBufferSize = default_buffer_size; + clockFrequency = AudioGetHostClockFrequency() / 1000; + errorCode = QAudio::NoError; + stateCode = QAudio::StopState; + + intervalTimer = new QTimer(this); + intervalTimer->setInterval(1000); + connect(intervalTimer, SIGNAL(timeout()), SIGNAL(notify())); + } +} + +QAudioInputPrivate::~QAudioInputPrivate() +{ + close(); +} + +bool QAudioInputPrivate::open() +{ + UInt32 size = 0; + + if (isOpen) + return true; + + ComponentDescription cd; + cd.componentType = kAudioUnitType_Output; + cd.componentSubType = kAudioUnitSubType_HALOutput; + cd.componentManufacturer = kAudioUnitManufacturer_Apple; + cd.componentFlags = 0; + cd.componentFlagsMask = 0; + + // Open + Component cp = FindNextComponent(NULL, &cd); + if (cp == 0) { + qWarning() << "QAudioInput: Failed to find HAL Output component"; + return false; + } + + if (OpenAComponent(cp, &audioUnit) != noErr) { + qWarning() << "QAudioInput: Unable to Open Output Component"; + return false; + } + + // Set mode + // switch to input mode + UInt32 enable = 1; + if (AudioUnitSetProperty(audioUnit, + kAudioOutputUnitProperty_EnableIO, + kAudioUnitScope_Input, + 1, + &enable, + sizeof(enable)) != noErr) { + qWarning() << "QAudioInput: Unabled to switch to input mode (Enable Input)"; + return false; + } + + enable = 0; + if (AudioUnitSetProperty(audioUnit, + kAudioOutputUnitProperty_EnableIO, + kAudioUnitScope_Output, + 0, + &enable, + sizeof(enable)) != noErr) { + qWarning() << "QAudioInput: Unabled to switch to input mode (Disable output)"; + return false; + } + + // register callback + AURenderCallbackStruct cb; + cb.inputProc = inputCallback; + cb.inputProcRefCon = this; + + if (AudioUnitSetProperty(audioUnit, + kAudioOutputUnitProperty_SetInputCallback, + kAudioUnitScope_Global, + 0, + &cb, + sizeof(cb)) != noErr) { + qWarning() << "QAudioInput: Failed to set AudioUnit callback"; + return false; + } + + // Set Audio Device + if (AudioUnitSetProperty(audioUnit, + kAudioOutputUnitProperty_CurrentDevice, + kAudioUnitScope_Global, + 0, + &audioDeviceId, + sizeof(audioDeviceId)) != noErr) { + qWarning() << "QAudioInput: Unable to use configured device"; + return false; + } + + // Set format + streamFormat = toAudioStreamBasicDescription(audioFormat); + + size = sizeof(deviceFormat); + if (AudioUnitGetProperty(audioUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, + 1, + &deviceFormat, + &size) != noErr) { + qWarning() << "QAudioInput: Unable to retrieve device format"; + return false; + } + + // If the device frequency is different to the requested use a converter + if (deviceFormat.mSampleRate != streamFormat.mSampleRate) { + AudioUnitSetProperty(audioUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Output, + 1, + &deviceFormat, + sizeof(streamFormat)); + } + else { + AudioUnitSetProperty(audioUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Output, + 1, + &streamFormat, + sizeof(streamFormat)); + } + + // Setup buffers + UInt32 numberOfFrames; + size = sizeof(UInt32); + if (AudioUnitGetProperty(audioUnit, + kAudioDevicePropertyBufferFrameSize, + kAudioUnitScope_Global, + 0, + &numberOfFrames, + &size) != noErr) { + qWarning() << "QAudioInput: Failed to get audio period size"; + return false; + } + + // Allocate buffer + periodSizeBytes = (numberOfFrames * streamFormat.mSampleRate / deviceFormat.mSampleRate) * + streamFormat.mBytesPerFrame; + if (internalBufferSize < periodSizeBytes * 2) + internalBufferSize = periodSizeBytes * 2; + else + internalBufferSize -= internalBufferSize % streamFormat.mBytesPerFrame; + + audioBuffer = new QAudioInputBuffer(internalBufferSize, + periodSizeBytes, + deviceFormat, + streamFormat, + this); + + audioIO = new MacInputDevice(audioBuffer, this); + + // Init + if (AudioUnitInitialize(audioUnit) != noErr) { + qWarning() << "QAudioInput: Failed to initialize AudioUnit"; + return false; + } + + isOpen = true; + + return isOpen; +} + +void QAudioInputPrivate::close() +{ + if (audioUnit != 0) { + AudioOutputUnitStop(audioUnit); + AudioUnitUninitialize(audioUnit); + CloseComponent(audioUnit); + } + + delete audioBuffer; +} + +QAudioFormat QAudioInputPrivate::format() const +{ + return audioFormat; +} + +QIODevice* QAudioInputPrivate::start(QIODevice* device) +{ + QIODevice* op = device; + + if (!open()) { + stateCode = QAudio::StopState; + errorCode = QAudio::OpenError; + return audioIO; + } + + reset(); + audioBuffer->reset(); + audioBuffer->setFlushDevice(op); + + if (op == 0) + op = audioIO; + + // Start + startTime = AudioGetCurrentHostTime(); + totalFrames = 0; + + audioThreadStart(); + + return op; +} + +void QAudioInputPrivate::stop() +{ + QMutexLocker lock(&mutex); + if (stateCode != QAudio::StopState) { + audioThreadStop(); + audioBuffer->flush(true); + + errorCode = QAudio::NoError; + stateCode = QAudio::StopState; + QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode)); + } +} + +void QAudioInputPrivate::reset() +{ + QMutexLocker lock(&mutex); + if (stateCode != QAudio::StopState) { + audioThreadStop(); + + errorCode = QAudio::NoError; + stateCode = QAudio::StopState; + QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode)); + } +} + +void QAudioInputPrivate::suspend() +{ + QMutexLocker lock(&mutex); + if (stateCode == QAudio::ActiveState || stateCode == QAudio::IdleState) { + audioThreadStop(); + + errorCode = QAudio::NoError; + stateCode = QAudio::SuspendState; + QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode)); + } +} + +void QAudioInputPrivate::resume() +{ + QMutexLocker lock(&mutex); + if (stateCode == QAudio::SuspendState) { + audioThreadStart(); + + errorCode = QAudio::NoError; + stateCode = QAudio::ActiveState; + QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode)); + } +} + +int QAudioInputPrivate::bytesReady() const +{ + return audioBuffer->used(); +} + +int QAudioInputPrivate::periodSize() const +{ + return periodSizeBytes; +} + +void QAudioInputPrivate::setBufferSize(int bs) +{ + internalBufferSize = bs; +} + +int QAudioInputPrivate::bufferSize() const +{ + return internalBufferSize; +} + +void QAudioInputPrivate::setNotifyInterval(int milliSeconds) +{ + intervalTimer->setInterval(milliSeconds); +} + +int QAudioInputPrivate::notifyInterval() const +{ + return intervalTimer->interval(); +} + +qint64 QAudioInputPrivate::totalTime() const +{ + return totalFrames * 1000000 / audioFormat.frequency(); +} + +qint64 QAudioInputPrivate::clock() const +{ + return (AudioGetCurrentHostTime() - startTime) / (clockFrequency / 1000); +} + +QAudio::Error QAudioInputPrivate::error() const +{ + return errorCode; +} + +QAudio::State QAudioInputPrivate::state() const +{ + return stateCode; +} + +void QAudioInputPrivate::audioThreadStop() +{ + stopTimers(); + if (audioThreadState.testAndSetAcquire(Running, Stopped)) + threadFinished.wait(&mutex); +} + +void QAudioInputPrivate::audioThreadStart() +{ + startTimers(); + audioThreadState = Running; + AudioOutputUnitStart(audioUnit); +} + +void QAudioInputPrivate::audioDeviceStop() +{ + AudioOutputUnitStop(audioUnit); + audioThreadState = Stopped; + threadFinished.wakeOne(); +} + +void QAudioInputPrivate::audioDeviceFull() +{ + QMutexLocker lock(&mutex); + if (stateCode == QAudio::ActiveState) { + audioDeviceStop(); + + errorCode = QAudio::UnderrunError; + stateCode = QAudio::IdleState; + QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection); + } +} + +void QAudioInputPrivate::audioDeviceError() +{ + QMutexLocker lock(&mutex); + if (stateCode == QAudio::ActiveState) { + audioDeviceStop(); + + errorCode = QAudio::IOError; + stateCode = QAudio::StopState; + QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection); + } +} + +void QAudioInputPrivate::startTimers() +{ + audioBuffer->startFlushTimer(); + intervalTimer->start(); +} + +void QAudioInputPrivate::stopTimers() +{ + audioBuffer->stopFlushTimer(); + intervalTimer->stop(); +} + +void QAudioInputPrivate::deviceStopped() +{ + stopTimers(); + emit stateChanged(stateCode); +} + +// Input callback +OSStatus QAudioInputPrivate::inputCallback(void* inRefCon, + AudioUnitRenderActionFlags* ioActionFlags, + const AudioTimeStamp* inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList* ioData) +{ + Q_UNUSED(ioData); + + QAudioInputPrivate* d = static_cast<QAudioInputPrivate*>(inRefCon); + + const int threadState = d->audioThreadState.fetchAndAddAcquire(0); + if (threadState == Stopped) + d->audioDeviceStop(); + else { + qint64 framesWritten; + + framesWritten = d->audioBuffer->renderFromDevice(d->audioUnit, + ioActionFlags, + inTimeStamp, + inBusNumber, + inNumberFrames); + + if (framesWritten > 0) + d->totalFrames += framesWritten; + else if (framesWritten == 0) + d->audioDeviceFull(); + else if (framesWritten < 0) + d->audioDeviceError(); + } + + return noErr; +} + + +QT_END_NAMESPACE + +#include "qaudioinput_mac_p.moc" + diff --git a/src/multimedia/audio/qaudioinput_mac_p.h b/src/multimedia/audio/qaudioinput_mac_p.h new file mode 100644 index 0000000..98ef9ce --- /dev/null +++ b/src/multimedia/audio/qaudioinput_mac_p.h @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#ifndef QAUDIOINPUT_MAC_P_H +#define QAUDIOINPUT_MAC_P_H + +#include <CoreServices/CoreServices.h> +#include <CoreAudio/CoreAudio.h> +#include <AudioUnit/AudioUnit.h> +#include <AudioToolbox/AudioToolbox.h> + +#include <QtCore/qobject.h> +#include <QtCore/qmutex.h> +#include <QtCore/qwaitcondition.h> +#include <QtCore/qatomic.h> + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudioformat.h> +#include <QtMultimedia/qaudioengine.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QTimer; +class QIODevice; + +namespace +{ +class QAudioInputBuffer; +} + +class QAudioInputPrivate : public QAbstractAudioInput +{ + Q_OBJECT + +public: + bool isOpen; + int periodSizeBytes; + int internalBufferSize; + qint64 totalFrames; + QAudioFormat audioFormat; + QIODevice* audioIO; + AudioUnit audioUnit; + AudioDeviceID audioDeviceId; + Float64 clockFrequency; + UInt64 startTime; + QAudio::Error errorCode; + QAudio::State stateCode; + QAudioInputBuffer* audioBuffer; + QMutex mutex; + QWaitCondition threadFinished; + QAtomicInt audioThreadState; + QTimer* intervalTimer; + AudioStreamBasicDescription streamFormat; + AudioStreamBasicDescription deviceFormat; + + QAudioInputPrivate(const QByteArray& device, QAudioFormat const& format); + ~QAudioInputPrivate(); + + bool open(); + void close(); + + QAudioFormat format() const; + + QIODevice* start(QIODevice* device); + void stop(); + void reset(); + void suspend(); + void resume(); + void idle(); + + int bytesReady() const; + int periodSize() const; + + void setBufferSize(int value); + int bufferSize() const; + + void setNotifyInterval(int milliSeconds); + int notifyInterval() const; + + qint64 totalTime() const; + qint64 clock() const; + + QAudio::Error error() const; + QAudio::State state() const; + + void audioThreadStart(); + void audioThreadStop(); + + void audioDeviceStop(); + void audioDeviceFull(); + void audioDeviceError(); + + void startTimers(); + void stopTimers(); + +signals: + void stateChanged(QAudio::State); + void notify(); + +private slots: + void deviceStopped(); + +private: + enum { Running, Stopped }; + + // Input callback + static OSStatus inputCallback(void* inRefCon, + AudioUnitRenderActionFlags* ioActionFlags, + const AudioTimeStamp* inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList* ioData); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QAUDIOINPUT_MAC_P_H diff --git a/src/multimedia/audio/qaudioinput_win32_p.cpp b/src/multimedia/audio/qaudioinput_win32_p.cpp new file mode 100644 index 0000000..e5b6e0d --- /dev/null +++ b/src/multimedia/audio/qaudioinput_win32_p.cpp @@ -0,0 +1,540 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#include "qaudioinput_win32_p.h" + +//#define DEBUG_AUDIO 1 + + +QAudioInputPrivate::QAudioInputPrivate(const QByteArray &device, const QAudioFormat& audioFormat): + settings(audioFormat) +{ + bytesAvailable = 0; + buffer_size = 0; + period_size = 0; + m_device = device; + totalTimeValue = 0; + intervalTime = 1000; + errorState = QAudio::NoError; + deviceState = QAudio::StopState; + audioSource = 0; + pullMode = true; + resuming = false; + + connect(this,SIGNAL(processMore()),SLOT(deviceReady())); + + InitializeCriticalSection(&waveInCriticalSection); +} + +QAudioInputPrivate::~QAudioInputPrivate() +{ + close(); + DeleteCriticalSection(&waveInCriticalSection); +} + +void CALLBACK QAudioInputPrivate::waveInProc( HWAVEIN hWaveIn, UINT uMsg, + DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 ) +{ + Q_UNUSED(dwParam1) + Q_UNUSED(dwParam2) + Q_UNUSED(hWaveIn) + + QAudioInputPrivate* qAudio; + qAudio = (QAudioInputPrivate*)(dwInstance); + if(!qAudio) + return; + + switch(uMsg) { + case WIM_OPEN: + break; + case WIM_DATA: + EnterCriticalSection(&waveInCriticalSection); + if(qAudio->waveFreeBlockCount > 0) + qAudio->waveFreeBlockCount--; + LeaveCriticalSection(&waveInCriticalSection); + qAudio->feedback(); + break; + case WIM_CLOSE: + break; + default: + return; + } +} + +WAVEHDR* QAudioInputPrivate::allocateBlocks(int size, int count) +{ + int i; + unsigned char* buffer; + WAVEHDR* blocks; + DWORD totalBufferSize = (size + sizeof(WAVEHDR))*count; + + if((buffer=(unsigned char*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY, + totalBufferSize)) == 0) { + qWarning("QAudioInput: Memory allocation error"); + return 0; + } + blocks = (WAVEHDR*)buffer; + buffer += sizeof(WAVEHDR)*count; + for(i = 0; i < count; i++) { + blocks[i].dwBufferLength = size; + blocks[i].lpData = (LPSTR)buffer; + blocks[i].dwBytesRecorded=0; + blocks[i].dwUser = 0L; + blocks[i].dwFlags = 0L; + blocks[i].dwLoops = 0L; + result = waveInPrepareHeader(hWaveIn,&blocks[i], sizeof(WAVEHDR)); + if(result != MMSYSERR_NOERROR) { + qWarning("QAudioInput: Can't prepare block %d",i); + return 0; + } + buffer += size; + } + return blocks; +} + +void QAudioInputPrivate::freeBlocks(WAVEHDR* blockArray) +{ + HeapFree(GetProcessHeap(), 0, blockArray); +} + +QAudio::Error QAudioInputPrivate::error() const +{ + return errorState; +} + +QAudio::State QAudioInputPrivate::state() const +{ + return deviceState; +} + +QAudioFormat QAudioInputPrivate::format() const +{ + return settings; +} + +QIODevice* QAudioInputPrivate::start(QIODevice* device) +{ + if(deviceState != QAudio::StopState) + close(); + + if(!pullMode && audioSource) { + delete audioSource; + } + + if(device) { + //set to pull mode + pullMode = true; + audioSource = device; + } else { + //set to push mode + pullMode = false; + audioSource = new InputPrivate(this); + audioSource->open(QIODevice::ReadOnly | QIODevice::Unbuffered); + } + + if( !open() ) + return 0; + + emit stateChanged(deviceState); + + return audioSource; +} + +void QAudioInputPrivate::stop() +{ + if(deviceState == QAudio::StopState) + return; + + deviceState = QAudio::StopState; + + close(); + emit stateChanged(deviceState); +} + +bool QAudioInputPrivate::open() +{ +#ifdef DEBUG_AUDIO + QTime now(QTime::currentTime()); + qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()"; +#endif + header = 0; + if(buffer_size == 0) { + // Default buffer size, 100ms, default period size is 20ms + buffer_size = settings.frequency()*settings.channels()*(settings.sampleSize()/8)*0.1; + period_size = buffer_size/5; + } else { + period_size = buffer_size/5; + } + timeStamp.restart(); + wfx.nSamplesPerSec = settings.frequency(); + wfx.wBitsPerSample = settings.sampleSize(); + wfx.nChannels = settings.channels(); + wfx.cbSize = 0; + + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nBlockAlign = (wfx.wBitsPerSample >> 3) * wfx.nChannels; + wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec; + + UINT_PTR devId = WAVE_MAPPER; + + WAVEINCAPS wic; + unsigned long iNumDevs,ii; + iNumDevs = waveInGetNumDevs(); + for(ii=0;ii<iNumDevs;ii++) { + if(waveInGetDevCaps(ii, &wic, sizeof(WAVEINCAPS)) + == MMSYSERR_NOERROR) { + QString tmp; + tmp = QString::fromUtf16((const unsigned short*)wic.szPname); + if(tmp.compare(tr(m_device)) == 0) { + devId = ii; + break; + } + } + } + + if(waveInOpen(&hWaveIn, devId, &wfx, + (DWORD_PTR)&waveInProc, + (DWORD_PTR) this, + CALLBACK_FUNCTION) != MMSYSERR_NOERROR) { + errorState = QAudio::OpenError; + deviceState = QAudio::StopState; + emit stateChanged(deviceState); + qWarning("QAudioInput: failed to open audio device"); + return false; + } + waveBlocks = allocateBlocks(period_size, buffer_size/period_size); + waveFreeBlockCount = buffer_size/period_size; + waveCurrentBlock = 0; + + for(int i=0; i<buffer_size/period_size; i++) { + result = waveInAddBuffer(hWaveIn, &waveBlocks[i], sizeof(WAVEHDR)); + if(result != MMSYSERR_NOERROR) { + qWarning("QAudioInput: failed to setup block %d,err=%d",i,result); + errorState = QAudio::OpenError; + deviceState = QAudio::StopState; + emit stateChanged(deviceState); + return false; + } + } + result = waveInStart(hWaveIn); + if(result) { + qWarning("QAudioInput: failed to start audio input"); + errorState = QAudio::OpenError; + deviceState = QAudio::StopState; + emit stateChanged(deviceState); + return false; + } + timeStampOpened.restart(); + totalTimeValue = 0; + errorState = QAudio::NoError; + deviceState = QAudio::ActiveState; + return true; +} + +void QAudioInputPrivate::close() +{ + deviceState = QAudio::StopState; + int delay = (buffer_size-bytesReady())*1000/(settings.frequency() + *settings.channels()*(settings.sampleSize()/8)); + waveInReset(hWaveIn); + Sleep(delay+10); + + for(int i=0; i<waveFreeBlockCount; i++) { + if(waveBlocks[i].dwFlags & WHDR_PREPARED) + waveInUnprepareHeader(hWaveIn,&waveBlocks[i],sizeof(WAVEHDR)); + } + freeBlocks(waveBlocks); + waveInClose(hWaveIn); +} + +int QAudioInputPrivate::bytesReady() const +{ + int buf = ((buffer_size/period_size)-waveFreeBlockCount)*period_size; + if(buf < 0) + buf = 0; + return buf; +} + +qint64 QAudioInputPrivate::read(char* data, qint64 len) +{ + bool done = false; + + char* p = data; + qint64 l = 0; + qint64 written = 0; + while(!done) { + // Read in some audio data + if(waveBlocks[header].dwBytesRecorded > 0) { + if(pullMode) { + l = audioSource->write(waveBlocks[header].lpData, + waveBlocks[header].dwBytesRecorded); +#ifdef DEBUG_AUDIO + qDebug()<<"IN: "<<waveBlocks[header].dwBytesRecorded<<", OUT: "<<l; +#endif + if(l < 0) { + // error + qWarning("QAudioInput: IOError"); + errorState = QAudio::IOError; + + } else if(l == 0) { + // cant write to IODevice + qWarning("QAudioInput: IOError, can't write to QIODevice"); + errorState = QAudio::IOError; + + } else { + totalTimeValue += waveBlocks[header].dwBytesRecorded + /((settings.channels()*settings.sampleSize()/8)) + *10000/settings.frequency()*100; + errorState = QAudio::NoError; + deviceState = QAudio::ActiveState; + resuming = false; + } + } else { + // push mode + memcpy(p,waveBlocks[header].lpData,waveBlocks[header].dwBytesRecorded); + l = waveBlocks[header].dwBytesRecorded; +#ifdef DEBUG_AUDIO + qDebug()<<"IN: "<<waveBlocks[header].dwBytesRecorded<<", OUT: "<<l; +#endif + totalTimeValue += waveBlocks[header].dwBytesRecorded + /((settings.channels()*settings.sampleSize()/8)) + *10000/settings.frequency()*100; + errorState = QAudio::NoError; + deviceState = QAudio::ActiveState; + resuming = false; + } + } else { + //no data, not ready yet, next time + return 0; + } + EnterCriticalSection(&waveInCriticalSection); + waveFreeBlockCount++; + LeaveCriticalSection(&waveInCriticalSection); + waveBlocks[header].dwBytesRecorded=0; + waveBlocks[header].dwFlags = 0L; + result = waveInPrepareHeader(hWaveIn,&waveBlocks[header], sizeof(WAVEHDR)); + if(result != MMSYSERR_NOERROR) { + qWarning("QAudioInput: failed to prepare block %d,err=%d",header,result); + errorState = QAudio::OpenError; + deviceState = QAudio::StopState; + emit stateChanged(deviceState); + } + result = waveInAddBuffer(hWaveIn, &waveBlocks[header], sizeof(WAVEHDR)); + if(result != MMSYSERR_NOERROR) { + qWarning("QAudioInput: failed to setup block %d,err=%d",header,result); + errorState = QAudio::OpenError; + deviceState = QAudio::StopState; + emit stateChanged(deviceState); + } + header++; + if(header >= buffer_size/period_size) + header = 0; + p+=l; + + if(!pullMode) { + if(l+period_size > len && waveFreeBlockCount == buffer_size/period_size) + done = true; + } else { + if(waveFreeBlockCount == buffer_size/period_size) + done = true; + } + written+=l; + } +#ifdef DEBUG_AUDIO + qDebug()<<"read in len="<<written; +#endif + return written; +} + +void QAudioInputPrivate::resume() +{ + if(deviceState == QAudio::SuspendState) { + deviceState = QAudio::ActiveState; + for(int i=0; i<buffer_size/period_size; i++) { + result = waveInAddBuffer(hWaveIn, &waveBlocks[i], sizeof(WAVEHDR)); + if(result != MMSYSERR_NOERROR) { + qWarning("QAudioInput: failed to setup block %d,err=%d",i,result); + errorState = QAudio::OpenError; + deviceState = QAudio::StopState; + emit stateChanged(deviceState); + return; + } + } + waveFreeBlockCount = buffer_size/period_size; + waveCurrentBlock = 0; + header = 0; + resuming = true; + waveInStart(hWaveIn); + QTimer::singleShot(20,this,SLOT(feedback())); + emit stateChanged(deviceState); + } +} + +void QAudioInputPrivate::setBufferSize(int value) +{ + buffer_size = value; +} + +int QAudioInputPrivate::bufferSize() const +{ + return buffer_size; +} + +int QAudioInputPrivate::periodSize() const +{ + return period_size; +} + +void QAudioInputPrivate::setNotifyInterval(int ms) +{ + intervalTime = ms; +} + +int QAudioInputPrivate::notifyInterval() const +{ + return intervalTime; +} + +qint64 QAudioInputPrivate::totalTime() const +{ + return totalTimeValue; +} + +void QAudioInputPrivate::suspend() +{ + if(deviceState == QAudio::ActiveState) { + waveInReset(hWaveIn); + deviceState = QAudio::SuspendState; + emit stateChanged(deviceState); + } +} + +void QAudioInputPrivate::feedback() +{ +#ifdef DEBUG_AUDIO + QTime now(QTime::currentTime()); + qDebug()<<now.second()<<"s "<<now.msec()<<"ms :feedback() INPUT"; +#endif + bytesAvailable = bytesReady(); + + if(!(deviceState==QAudio::StopState||deviceState==QAudio::SuspendState)) + emit processMore(); +} + +bool QAudioInputPrivate::deviceReady() +{ +#ifdef DEBUG_AUDIO + QTime now(QTime::currentTime()); + qDebug()<<now.second()<<"s "<<now.msec()<<"ms :deviceReady() INPUT"; +#endif + if(pullMode) { + // reads some audio data and writes it to QIODevice + read(0,0); + } else { + // emits readyRead() so user will call read() on QIODevice to get some audio data + InputPrivate* a = qobject_cast<InputPrivate*>(audioSource); + a->trigger(); + } + if(deviceState != QAudio::ActiveState) + return true; + + if(timeStamp.elapsed() > intervalTime && intervalTime > 50) { + emit notify(); + timeStamp.restart(); + } + return true; +} + +qint64 QAudioInputPrivate::clock() const +{ + if(deviceState != QAudio::ActiveState) + return 0; + + return timeStampOpened.elapsed(); +} + +void QAudioInputPrivate::reset() +{ + close(); +} + +InputPrivate::InputPrivate(QAudioInputPrivate* audio) +{ + audioDevice = qobject_cast<QAudioInputPrivate*>(audio); +} + +InputPrivate::~InputPrivate() {} + +qint64 InputPrivate::readData( char* data, qint64 len) +{ + // push mode, user read() called + if(audioDevice->deviceState != QAudio::ActiveState) + return 0; + // Read in some audio data + return audioDevice->read(data,len); +} + +qint64 InputPrivate::writeData(const char* data, qint64 len) +{ + Q_UNUSED(data) + Q_UNUSED(len) + + emit readyRead(); + return 0; +} + +void InputPrivate::trigger() +{ + emit readyRead(); +} + diff --git a/src/multimedia/audio/qaudioinput_win32_p.h b/src/multimedia/audio/qaudioinput_win32_p.h new file mode 100644 index 0000000..32464f0 --- /dev/null +++ b/src/multimedia/audio/qaudioinput_win32_p.h @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QAUDIOINPUTWIN_H +#define QAUDIOINPUTWIN_H + +#include <windows.h> +#include <mmsystem.h> + +#include <QtCore/qfile.h> +#include <QtCore/qdebug.h> +#include <QtCore/qtimer.h> +#include <QtCore/qstring.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qdatetime.h> + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudioengine.h> + + +static CRITICAL_SECTION waveInCriticalSection; + +class QAudioInputPrivate : public QAbstractAudioInput +{ + Q_OBJECT +public: + QAudioInputPrivate(const QByteArray &device, const QAudioFormat& audioFormat); + ~QAudioInputPrivate(); + + qint64 read(char* data, qint64 len); + + QAudioFormat format() const; + QIODevice* start(QIODevice* device = 0); + void stop(); + void reset(); + void suspend(); + void resume(); + int bytesReady() const; + int periodSize() const; + void setBufferSize(int value); + int bufferSize() const; + void setNotifyInterval(int milliSeconds); + int notifyInterval() const; + qint64 totalTime() const; + qint64 clock() const; + QAudio::Error error() const; + QAudio::State state() const; + + QIODevice* audioSource; + QAudioFormat settings; + QAudio::Error errorState; + QAudio::State deviceState; + +private: + qint32 buffer_size; + qint32 period_size; + qint32 header; + QByteArray m_device; + int bytesAvailable; + int intervalTime; + QTime timeStamp; + QTime timeStampOpened; + qint64 totalTimeValue; + bool pullMode; + bool resuming; + WAVEFORMATEX wfx; + HWAVEIN hWaveIn; + MMRESULT result; + WAVEHDR* waveBlocks; + volatile int waveFreeBlockCount; + int waveCurrentBlock; + + static void CALLBACK waveInProc( HWAVEIN hWaveIn, UINT uMsg, + DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 ); + + WAVEHDR* allocateBlocks(int size, int count); + void freeBlocks(WAVEHDR* blockArray); + bool open(); + void close(); + +private slots: + void feedback(); + bool deviceReady(); + +signals: + void processMore(); +}; + +class InputPrivate : public QIODevice +{ + Q_OBJECT +public: + InputPrivate(QAudioInputPrivate* audio); + ~InputPrivate(); + + qint64 readData( char* data, qint64 len); + qint64 writeData(const char* data, qint64 len); + + void trigger(); +private: + QAudioInputPrivate *audioDevice; +}; + +#endif diff --git a/src/multimedia/audio/qaudiooutput.cpp b/src/multimedia/audio/qaudiooutput.cpp new file mode 100644 index 0000000..785da61 --- /dev/null +++ b/src/multimedia/audio/qaudiooutput.cpp @@ -0,0 +1,403 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudioengine.h> +#include <QtMultimedia/qaudiooutput.h> + +#include "qaudiodevicefactory_p.h" + + +QT_BEGIN_NAMESPACE + +/*! + \class QAudioOutput + \brief The QAudioOutput class provides an interface for sending audio data to an audio output device. + + \inmodule QtMultimedia + \ingroup multimedia + \since 4.6 + + You can construct an audio output with the system's + \l{QAudioDeviceInfo::defaultOutputDevice()}{default audio output + device}. It is also possible to create QAudioOutput with a + specific QAudioDeviceId. When you create the audio output, you + should also send in the QAudioFormat to be used for the playback + (see the QAudioFormat class description for details). + + To play a file: + + Starting to play an audio stream is simply a matter of calling + start() with a QIODevice. QAudioOutput will then fetch the data it + needs from the io device. So playing back an audio file is as + simple as: + + \code + QFile inputFile; + inputFile.setFileName("/tmp/test.raw"); + inputFile.open(QIODevice::ReadOnly); + + QAudioFormat format; + // Set up the format, eg. + format.setFrequency(8000); + format.setChannels(1); + format.setSampleSize(8); + format.setCodec("audio/pcm"); + format.setByteOrder(QAudioFormat::LittleEndian); + format.setSampleType(QAudioFormat::UnSignedInt); + + QAudioOutput *audio = new QAudioOutput(format, this); + connect(audio,SIGNAL(stateChanged(QAudio::State)),SLOT(finishedPlaying(QAudio::State))); + audio->start(inputFile); + + \endcode + + The file will start playing assuming that the audio system and + output device support it. If you run out of luck, check what's + up with the error() function. + + After the file has finished playing, we need to stop the device: + + \code + void finishedPlaying(QAudio::State state) + { + if(state == QAudio::IdleState) { + audio->stop(); + inputFile.close(); + } + } + \endcode + + At any given time, the QAudioOutput will be in one of four states: + active, suspended, stopped, or idle. These states are described + by the QAudio::State enum. + State changes are reported through the stateChanged() signal. You + can use this signal to, for instance, update the GUI of the + application; the mundane example here being changing the state of + a \c { play/pause } button. You request a state change directly + with suspend(), stop(), reset(), resume(), and start(). + + While the stream is playing, you can set a notify interval in + milliseconds with setNotifyInterval(). This interval specifies the + time between two emissions of the notify() signal. This is + relative to the position in the stream, i.e., if the QAudioOutput + is in the SuspendedState or the IdleState, the notify() signal is + not emitted. A typical use-case would be to update a + \l{QSlider}{slider} that allows seeking in the stream. + If you want the time since playback started regardless of which + states the audio output has been in, clock() is the function for you. + + If an error occurs, you can fetch the \l{QAudio::Error}{error + type} with the error() function. Please see the QAudio::Error enum + for a description of the possible errors that are reported. + + If an error is encountered state changes to QAudio::StopState. + + \sa QAudioInput, QAudioDeviceInfo +*/ + +/*! + Construct a new audio output and attach it to \a parent. + The default audio output device is used with the output + \a format parameters. +*/ + +QAudioOutput::QAudioOutput(const QAudioFormat &format, QObject *parent): + QObject(parent) +{ + d = QAudioDeviceFactory::createDefaultOutputDevice(format); + connect(d, SIGNAL(notify()), SIGNAL(notify())); + connect(d, SIGNAL(stateChanged(QAudio::State)), SIGNAL(stateChanged(QAudio::State))); +} + +/*! + Construct a new audio output and attach it to \a parent. + The \a id of the audio output device is used with the output + \a format parameters. +*/ + +QAudioOutput::QAudioOutput(const QAudioDeviceId &id, const QAudioFormat &format, QObject *parent): + QObject(parent) +{ + d = QAudioDeviceFactory::createOutputDevice(id, format); + connect(d, SIGNAL(notify()), SIGNAL(notify())); + connect(d, SIGNAL(stateChanged(QAudio::State)), SIGNAL(stateChanged(QAudio::State))); +} + +/*! + Destroys this audio output. +*/ + +QAudioOutput::~QAudioOutput() +{ + delete d; +} + +/*! + Returns the QAudioFormat being used. + +*/ + +QAudioFormat QAudioOutput::format() const +{ + return d->format(); +} + +/*! + Uses the \a device as the QIODevice to transfer data. + If \a device is null then the class creates an internal QIODevice. + Returns a pointer to the QIODevice being used to handle the data + transfer. This QIODevice can be used to write() audio data + directly. + Passing a QIODevice allows the data to be transfered without any extra code. + All that is required is to open the QIODevice. + + /sa QIODevice +*/ + +QIODevice* QAudioOutput::start(QIODevice* device) +{ + /* + PULL MODE (valid QIODevice) + -If currently not StopState, stop. + -If previous start was push mode, delete internal QIODevice. + -open audio output. + -If ok, NoError and ActiveState, else OpenError and StopState + -emit stateChanged() + -return device + + PUSH MODE (device = 0) + -If currently not StopState, stop. + -If no internal QIODevice, create one. + -open audio output. + -If ok, NoError and IdleState, else OpenError and StopState + -emit stateChanged() + -return internal QIODevice + */ + return d->start(device); +} + +/*! + Stops the audio output. +*/ + +void QAudioOutput::stop() +{ + /* + -If StopState, return + -set to StopState + -detach from audio device + -emit stateChanged() + */ + d->stop(); +} + +/*! + Drops all audio data in the buffers, resets buffers to zero. +*/ + +void QAudioOutput::reset() +{ + /* + -drop all buffered audio, set buffers to zero. + -call stop() + */ + d->reset(); +} + +/*! + Stops processing audio data, preserving buffered audio data. +*/ + +void QAudioOutput::suspend() +{ + /* + -If not ActiveState|IdleState, return + -stop processing audio, saving all buffered audio data + -set NoError and SuspendState + -emit stateChanged() + */ + d->suspend(); +} + +/*! + Resumes processing audio data after a suspend(). +*/ + +void QAudioOutput::resume() +{ + /* + -If SuspendState, return + -resume audio + -(PULL MODE): set ActiveState, NoError + -(PUSH MODE): set IdleState, NoError + -kick start audio if needed + -emit stateChanged() + */ + d->resume(); +} + +/*! + Returns the free space available in bytes in the audio buffer. +*/ + +int QAudioOutput::bytesFree() const +{ + /* + -If not ActiveState|IdleState, return 0 + -return space available in audio buffer in bytes + */ + return d->bytesFree(); +} + +/*! + Returns the period size in bytes. + + Note: This is the recommended write size in bytes. +*/ + +int QAudioOutput::periodSize() const +{ + return d->periodSize(); +} + +/*! + Sets the audio buffer size to \a value in bytes. + + Note: This function can be called anytime before start(), calls to this + are ignored after start(). It should not be assumed that the buffer size + set is the actual buffer size used, calling bufferSize() anytime after start() + will return the actual buffer size being used. +*/ + +void QAudioOutput::setBufferSize(int value) +{ + d->setBufferSize(value); +} + +/*! + Returns the audio buffer size in bytes. + + If called before start(), returns platform default value. + If called before start() but setBufferSize() was called prior, returns value set by setBufferSize(). + If called after start(), returns the actual buffer size being used. This may not be what was set previously + by setBufferSize(). + +*/ + +int QAudioOutput::bufferSize() const +{ + return d->bufferSize(); +} + +/*! + Sets the interval for notify() signal to be emitted. + This is based on the \a ms of audio data processed + not on actual real-time. The resolution of the timer is platform specific. +*/ + +void QAudioOutput::setNotifyInterval(int ms) +{ + d->setNotifyInterval(ms); +} + +/*! + Returns the notify interval in milliseconds. +*/ + +int QAudioOutput::notifyInterval() const +{ + return d->notifyInterval(); +} + +/*! + Returns the amount of audio data processed since start() + was called in microseconds. +*/ + +qint64 QAudioOutput::totalTime() const +{ + return d->totalTime(); +} + +/*! + Returns the microseconds since start() was called, including time in Idle and + Suspend states. +*/ + +qint64 QAudioOutput::clock() const +{ + return d->clock(); +} + +/*! + Returns the error state. +*/ + +QAudio::Error QAudioOutput::error() const +{ + return d->error(); +} + +/*! + Returns the state of audio processing. +*/ + +QAudio::State QAudioOutput::state() const +{ + return d->state(); +} + +/*! + \fn QAudioOutput::stateChanged(QAudio::State state) + This signal is emitted when the device \a state has changed. + This is the current state of the audio output. +*/ + +/*! + \fn QAudioOutput::notify() + This signal is emitted when x ms of audio data has been processed + the interval set by setNotifyInterval(x). +*/ + +QT_END_NAMESPACE diff --git a/src/multimedia/audio/qaudiooutput.h b/src/multimedia/audio/qaudiooutput.h new file mode 100644 index 0000000..95e28ea --- /dev/null +++ b/src/multimedia/audio/qaudiooutput.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QAUDIOOUTPUT_H +#define QAUDIOOUTPUT_H + +#include <QtCore/qiodevice.h> +#include <QtCore/qglobal.h> + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudioformat.h> +#include <QtMultimedia/qaudiodeviceid.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + + +class QAbstractAudioOutput; + +class Q_MULTIMEDIA_EXPORT QAudioOutput : public QObject +{ + Q_OBJECT + +public: + explicit QAudioOutput(const QAudioFormat &format = QAudioFormat(), QObject *parent = 0); + explicit QAudioOutput(const QAudioDeviceId &id, const QAudioFormat &format = QAudioFormat(), QObject *parent = 0); + ~QAudioOutput(); + + QAudioFormat format() const; + + QIODevice* start(QIODevice *device = 0); + void stop(); + void reset(); + void suspend(); + void resume(); + + void setBufferSize(int bytes); + int bufferSize() const; + + int bytesFree() const; + int periodSize() const; + + void setNotifyInterval(int milliSeconds); + int notifyInterval() const; + + qint64 totalTime() const; + qint64 clock() const; + + QAudio::Error error() const; + QAudio::State state() const; + +Q_SIGNALS: + void stateChanged(QAudio::State); + void notify(); + +private: + Q_DISABLE_COPY(QAudioOutput) + + QAbstractAudioOutput* d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QAUDIOOUTPUT_H diff --git a/src/multimedia/audio/qaudiooutput_alsa_p.cpp b/src/multimedia/audio/qaudiooutput_alsa_p.cpp new file mode 100644 index 0000000..d41c449 --- /dev/null +++ b/src/multimedia/audio/qaudiooutput_alsa_p.cpp @@ -0,0 +1,706 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qcoreapplication.h> +#include "qaudiooutput_alsa_p.h" + +//#define DEBUG_AUDIO 1 + +QAudioOutputPrivate::QAudioOutputPrivate(const QByteArray &device, const QAudioFormat& audioFormat): + settings(audioFormat) +{ + bytesAvailable = 0; + handle = 0; + ahandler = 0; + access = SND_PCM_ACCESS_RW_INTERLEAVED; + pcmformat = SND_PCM_FORMAT_S16; + buffer_frames = 0; + period_frames = 0; + buffer_size = 0; + period_size = 0; + buffer_time = 100000; + period_time = 20000; + totalTimeValue = 0; + intervalTime = 1000; + audioBuffer = 0; + errorState = QAudio::NoError; + deviceState = QAudio::StopState; + audioSource = 0; + pullMode = true; + resuming = false; + opened = false; + + QStringList list1 = QString(tr(device)).split(tr(":")); + m_device = QByteArray(list1.at(0).toLocal8Bit().constData()); + + timer = new QTimer(this); + connect(timer,SIGNAL(timeout()),SLOT(userFeed())); +} + +QAudioOutputPrivate::~QAudioOutputPrivate() +{ + close(); + disconnect(timer, SIGNAL(timeout())); + QCoreApplication::processEvents(); + delete timer; +} + +QAudio::Error QAudioOutputPrivate::error() const +{ + return errorState; +} + +QAudio::State QAudioOutputPrivate::state() const +{ + return deviceState; +} + +void QAudioOutputPrivate::async_callback(snd_async_handler_t *ahandler) +{ + QAudioOutputPrivate* audioOut; + + audioOut = static_cast<QAudioOutputPrivate*> + (snd_async_handler_get_callback_private(ahandler)); + + if((audioOut->deviceState==QAudio::ActiveState)||(audioOut->resuming)) + audioOut->feedback(); +} + +int QAudioOutputPrivate::xrun_recovery(int err) +{ + int count = 0; + bool reset = false; + + if(err == -EPIPE) { + errorState = QAudio::UnderrunError; + err = snd_pcm_prepare(handle); + if(err < 0) + reset = true; + + } else if((err == -ESTRPIPE)||(err == -EIO)) { + errorState = QAudio::IOError; + while((err = snd_pcm_resume(handle)) == -EAGAIN){ + usleep(100); + count++; + if(count > 5) { + reset = true; + break; + } + } + if(err < 0) { + err = snd_pcm_prepare(handle); + if(err < 0) + reset = true; + } + } + if(reset) { + close(); + open(); + snd_pcm_prepare(handle); + return 0; + } + return err; +} + +int QAudioOutputPrivate::setFormat() +{ + snd_pcm_format_t pcmformat = SND_PCM_FORMAT_S16; + + if(settings.sampleSize() == 8) { + pcmformat = SND_PCM_FORMAT_U8; + + } else if(settings.sampleSize() == 16) { + if(settings.sampleType() == QAudioFormat::SignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + pcmformat = SND_PCM_FORMAT_S16_LE; + else + pcmformat = SND_PCM_FORMAT_S16_BE; + } else if(settings.sampleType() == QAudioFormat::UnSignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + pcmformat = SND_PCM_FORMAT_U16_LE; + else + pcmformat = SND_PCM_FORMAT_U16_BE; + } + } else if(settings.sampleSize() == 24) { + if(settings.sampleType() == QAudioFormat::SignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + pcmformat = SND_PCM_FORMAT_S24_LE; + else + pcmformat = SND_PCM_FORMAT_S24_BE; + } else if(settings.sampleType() == QAudioFormat::UnSignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + pcmformat = SND_PCM_FORMAT_U24_LE; + else + pcmformat = SND_PCM_FORMAT_U24_BE; + } + } else if(settings.sampleSize() == 32) { + if(settings.sampleType() == QAudioFormat::SignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + pcmformat = SND_PCM_FORMAT_S32_LE; + else + pcmformat = SND_PCM_FORMAT_S32_BE; + } else if(settings.sampleType() == QAudioFormat::UnSignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + pcmformat = SND_PCM_FORMAT_U32_LE; + else + pcmformat = SND_PCM_FORMAT_U32_BE; + } else if(settings.sampleType() == QAudioFormat::Float) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + pcmformat = SND_PCM_FORMAT_FLOAT_LE; + else + pcmformat = SND_PCM_FORMAT_FLOAT_BE; + } + } else if(settings.sampleSize() == 64) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + pcmformat = SND_PCM_FORMAT_FLOAT64_LE; + else + pcmformat = SND_PCM_FORMAT_FLOAT64_BE; + } + + return snd_pcm_hw_params_set_format( handle, hwparams, pcmformat); +} + +QIODevice* QAudioOutputPrivate::start(QIODevice* device) +{ + if(deviceState != QAudio::StopState) + deviceState = QAudio::StopState; + + errorState = QAudio::NoError; + + // Handle change of mode + if(audioSource && pullMode && !device) { + // pull -> push + close(); + audioSource = 0; + } else if(audioSource && !pullMode && device) { + // push -> pull + close(); + delete audioSource; + audioSource = 0; + } + + if(device) { + //set to pull mode + pullMode = true; + audioSource = device; + deviceState = QAudio::ActiveState; + } else { + //set to push mode + if(!audioSource) { + audioSource = new OutputPrivate(this); + audioSource->open(QIODevice::WriteOnly|QIODevice::Unbuffered); + } + pullMode = false; + deviceState = QAudio::IdleState; + } + + open(); + + emit stateChanged(deviceState); + + return audioSource; +} + +void QAudioOutputPrivate::stop() +{ + if(deviceState == QAudio::StopState) + return; + deviceState = QAudio::StopState; + close(); + emit stateChanged(deviceState); +} + +bool QAudioOutputPrivate::open() +{ + if(opened) + return true; + +#ifdef DEBUG_AUDIO + QTime now(QTime::currentTime()); + qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()"; +#endif + timeStamp.restart(); + + int dir; + int err=-1; + int count=0; + unsigned int freakuency=settings.frequency(); + + QString dev = tr(m_device.constData()); + if(!dev.contains(tr("default"))) { + dev = QString(tr("default:CARD=%1")).arg(tr(m_device.constData())); + } + // Step 1: try and open the device + while((count < 5) && (err < 0)) { + err=snd_pcm_open(&handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_PLAYBACK,0); + if(err < 0) + count++; + } + if (( err < 0)||(handle == 0)) { + errorState = QAudio::OpenError; + deviceState = QAudio::StopState; + return false; + } + snd_pcm_nonblock( handle, 0 ); + + // Step 2: Set the desired HW parameters. + snd_pcm_hw_params_alloca( &hwparams ); + + bool fatal = false; + QString errMessage; + unsigned int chunks = 8; + + err = snd_pcm_hw_params_any( handle, hwparams ); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioOutput: snd_pcm_hw_params_any: err = %1")).arg(err); + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_rate_resample( handle, hwparams, 1 ); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioOutput: snd_pcm_hw_params_set_rate_resample: err = %1")).arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_access( handle, hwparams, access ); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioOutput: snd_pcm_hw_params_set_access: err = %1")).arg(err); + } + } + if ( !fatal ) { + err = setFormat(); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioOutput: snd_pcm_hw_params_set_format: err = %1")).arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_channels( handle, hwparams, (unsigned int)settings.channels() ); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioOutput: snd_pcm_hw_params_set_channels: err = %1")).arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_rate_near( handle, hwparams, &freakuency, 0 ); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioOutput: snd_pcm_hw_params_set_rate_near: err = %1")).arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, &dir); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioOutput: snd_pcm_hw_params_set_buffer_time_near: err = %1")).arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, &dir); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioOutput: snd_pcm_hw_params_set_period_time_near: err = %1")).arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_periods_near(handle, hwparams, &chunks, &dir); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioOutput: snd_pcm_hw_params_set_periods_near: err = %1")).arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params(handle, hwparams); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioOutput: snd_pcm_hw_params: err = %1")).arg(err); + } + } + if( err < 0) { + qWarning()<<errMessage; + errorState = QAudio::OpenError; + deviceState = QAudio::StopState; + return false; + } + snd_pcm_hw_params_get_buffer_size(hwparams,&buffer_frames); + buffer_size = snd_pcm_frames_to_bytes(handle,buffer_frames); + snd_pcm_hw_params_get_period_size(hwparams,&period_frames, &dir); + period_size = snd_pcm_frames_to_bytes(handle,period_frames); + snd_pcm_hw_params_get_buffer_time(hwparams,&buffer_time, &dir); + snd_pcm_hw_params_get_period_time(hwparams,&period_time, &dir); + + // Step 3: Set the desired SW parameters. + snd_pcm_sw_params_t *swparams; + snd_pcm_sw_params_alloca(&swparams); + snd_pcm_sw_params_current(handle, swparams); + snd_pcm_sw_params_set_start_threshold(handle,swparams,period_frames); + snd_pcm_sw_params_set_stop_threshold(handle,swparams,buffer_frames); + snd_pcm_sw_params_set_avail_min(handle, swparams,period_frames); + snd_pcm_sw_params(handle, swparams); + + // Step 4: Prepare audio + if(audioBuffer == 0) + audioBuffer = new char[snd_pcm_frames_to_bytes(handle,buffer_frames)]; + snd_pcm_prepare( handle ); + snd_pcm_start(handle); + + // Step 5: Setup callback and timer fallback + snd_async_add_pcm_handler(&ahandler, handle, async_callback, this); + bytesAvailable = bytesFree(); + + // Step 6: Start audio processing + timer->start(period_time/1000); + + errorState = QAudio::NoError; + totalTimeValue = 0; + opened = true; + + return true; +} + +void QAudioOutputPrivate::close() +{ + deviceState = QAudio::StopState; + timer->stop(); + + if ( handle ) { + snd_pcm_drain( handle ); + snd_pcm_close( handle ); + handle = 0; + delete [] audioBuffer; + audioBuffer=0; + } + if(!pullMode && audioSource) { + delete audioSource; + audioSource = 0; + } + opened = false; +} + +int QAudioOutputPrivate::bytesFree() const +{ + if(resuming) + return period_size; + + if(deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState) + return 0; + int frames = snd_pcm_avail_update(handle); + if((int)frames > (int)buffer_frames) + frames = buffer_frames; + + return snd_pcm_frames_to_bytes(handle, frames); +} + +qint64 QAudioOutputPrivate::write( const char *data, qint64 len ) +{ + // Write out some audio data + if ( !handle ) + return 0; +#ifdef DEBUG_AUDIO + qDebug()<<"frames to write out = "<< + snd_pcm_bytes_to_frames( handle, (int)len )<<" ("<<len<<") bytes"; +#endif + int frames, err; + int space = bytesFree(); + if(len < space) { + // Just write it + frames = snd_pcm_bytes_to_frames( handle, (int)len ); + err = snd_pcm_writei( handle, data, frames ); + } else { + // Only write space worth + frames = snd_pcm_bytes_to_frames( handle, (int)space ); + err = snd_pcm_writei( handle, data, frames ); + } + if(err > 0) { + totalTimeValue += err*1000000/settings.frequency(); + resuming = false; + errorState = QAudio::NoError; + deviceState = QAudio::ActiveState; + return snd_pcm_frames_to_bytes( handle, err ); + } else + err = xrun_recovery(err); + + if(err < 0) { + close(); + errorState = QAudio::FatalError; + deviceState = QAudio::StopState; + emit stateChanged(deviceState); + } + return 0; +} + +int QAudioOutputPrivate::periodSize() const +{ + return period_size; +} + +void QAudioOutputPrivate::setBufferSize(int value) +{ + if(deviceState == QAudio::StopState) + buffer_size = value; +} + +int QAudioOutputPrivate::bufferSize() const +{ + return buffer_size; +} + +void QAudioOutputPrivate::setNotifyInterval(int ms) +{ + intervalTime = ms; +} + +int QAudioOutputPrivate::notifyInterval() const +{ + return intervalTime; +} + +qint64 QAudioOutputPrivate::totalTime() const +{ + return totalTimeValue; +} + +void QAudioOutputPrivate::resume() +{ + if(deviceState == QAudio::SuspendState) { + int err = 0; + + if(handle) { + err = snd_pcm_prepare( handle ); + if(err < 0) + xrun_recovery(err); + + err = snd_pcm_start(handle); + if(err < 0) + xrun_recovery(err); + + bytesAvailable = (int)snd_pcm_frames_to_bytes(handle, buffer_frames); + } + resuming = true; + if(pullMode) + deviceState = QAudio::ActiveState; + else + deviceState = QAudio::IdleState; + + errorState = QAudio::NoError; + timer->start(period_time/1000); + emit stateChanged(deviceState); + } +} + +QAudioFormat QAudioOutputPrivate::format() const +{ + return settings; +} + +void QAudioOutputPrivate::suspend() +{ + if(deviceState == QAudio::ActiveState || deviceState == QAudio::IdleState || resuming) { + timer->stop(); + deviceState = QAudio::SuspendState; + errorState = QAudio::NoError; + emit stateChanged(deviceState); + } +} + +void QAudioOutputPrivate::userFeed() +{ + if(deviceState == QAudio::StopState || deviceState == QAudio::SuspendState) + return; +#ifdef DEBUG_AUDIO + QTime now(QTime::currentTime()); + qDebug()<<now.second()<<"s "<<now.msec()<<"ms :userFeed() OUT"; +#endif + if(deviceState == QAudio::IdleState) + bytesAvailable = bytesFree(); + + deviceReady(); +} + +void QAudioOutputPrivate::feedback() +{ + QMetaObject::invokeMethod(this, SLOT(updateAvailable()), Qt::QueuedConnection); +} + +void QAudioOutputPrivate::updateAvailable() +{ +#ifdef DEBUG_AUDIO + QTime now(QTime::currentTime()); + qDebug()<<now.second()<<"s "<<now.msec()<<"ms :updateAvailable()"; +#endif + bytesAvailable = bytesFree(); +} + +bool QAudioOutputPrivate::deviceReady() +{ + if(pullMode) { + int l = 0; + int chunks = bytesAvailable/period_size; + if(chunks==0) { + bytesAvailable = bytesFree(); + return false; + } +#ifdef DEBUG_AUDIO + qDebug()<<"deviceReady() avail="<<bytesAvailable<<" bytes, period size="<<period_size<<" bytes"; + qDebug()<<"deviceReady() no. of chunks that can fit ="<<chunks<<", chunks in bytes ="<<period_size*chunks; +#endif + int input = period_frames*chunks; + if(input > (int)buffer_frames) + input = buffer_frames; + l = audioSource->read(audioBuffer,snd_pcm_frames_to_bytes(handle, input)); + if(l > 0) { + // Got some data to output + if(deviceState != QAudio::ActiveState) + return true; + write(audioBuffer,l); + bytesAvailable = bytesFree(); + + } else if(l == 0) { + // Did not get any data to output + bytesAvailable = bytesFree(); + if(bytesAvailable > snd_pcm_frames_to_bytes(handle, buffer_frames-period_frames)) { + // Underrun + errorState = QAudio::UnderrunError; + deviceState = QAudio::IdleState; + emit stateChanged(deviceState); + } + + } else if(l < 0) { + close(); + errorState = QAudio::IOError; + emit stateChanged(deviceState); + } + } else + bytesAvailable = bytesFree(); + + if(deviceState != QAudio::ActiveState) + return true; + + if(timeStamp.elapsed() > intervalTime && intervalTime > 50) { + emit notify(); + timeStamp.restart(); + } + return true; +} + +qint64 QAudioOutputPrivate::clock() const +{ + if(!handle) + return 0; + + if(deviceState != QAudio::ActiveState) + return 0; + + snd_pcm_status_t* status; + snd_pcm_status_alloca(&status); + + snd_timestamp_t t1,t2; + if( snd_pcm_status(handle, status) >= 0) { + snd_pcm_status_get_tstamp(status,&t1); + snd_pcm_status_get_trigger_tstamp(status,&t2); + t1.tv_sec-=t2.tv_sec; + + signed long l = (signed long)t1.tv_usec - (signed long)t2.tv_usec; + if(l < 0) { + t1.tv_sec--; + l = -l; + l %= 1000000; + } + return ((t1.tv_sec * 1000)+l/1000); + } else + return 0; + return 0; +} + +void QAudioOutputPrivate::reset() +{ + if(handle) + snd_pcm_reset(handle); + + stop(); +} + +OutputPrivate::OutputPrivate(QAudioOutputPrivate* audio) +{ + audioDevice = qobject_cast<QAudioOutputPrivate*>(audio); +} + +OutputPrivate::~OutputPrivate() {} + +qint64 OutputPrivate::readData( char* data, qint64 len) +{ + Q_UNUSED(data) + Q_UNUSED(len) + + return 0; +} + +qint64 OutputPrivate::writeData(const char* data, qint64 len) +{ + int retry = 0; + qint64 written = 0; + if((audioDevice->deviceState == QAudio::ActiveState) + ||(audioDevice->deviceState == QAudio::IdleState)) { + while(written < len) { + int chunk = audioDevice->write(data+written,(len-written)); + if(chunk <= 0) + retry++; + written+=chunk; + if(retry > 10) + return written; + } + } + return written; + +} diff --git a/src/multimedia/audio/qaudiooutput_alsa_p.h b/src/multimedia/audio/qaudiooutput_alsa_p.h new file mode 100644 index 0000000..f243bad --- /dev/null +++ b/src/multimedia/audio/qaudiooutput_alsa_p.h @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QAUDIOOUTPUTALSA_H +#define QAUDIOOUTPUTALSA_H + +#include <alsa/asoundlib.h> + +#include <QtCore/qfile.h> +#include <QtCore/qdebug.h> +#include <QtCore/qtimer.h> +#include <QtCore/qstring.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qdatetime.h> + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudioengine.h> + +class OutputPrivate; + +class QAudioOutputPrivate : public QAbstractAudioOutput +{ + friend class OutputPrivate; + Q_OBJECT +public: + QAudioOutputPrivate(const QByteArray &device, const QAudioFormat& audioFormat); + ~QAudioOutputPrivate(); + + qint64 write( const char *data, qint64 len ); + + QIODevice* start(QIODevice* device = 0); + void stop(); + void reset(); + void suspend(); + void resume(); + int bytesFree() const; + int periodSize() const; + void setBufferSize(int value); + int bufferSize() const; + void setNotifyInterval(int milliSeconds); + int notifyInterval() const; + qint64 totalTime() const; + qint64 clock() const; + QAudio::Error error() const; + QAudio::State state() const; + QAudioFormat format() const; + + QIODevice* audioSource; + QAudioFormat settings; + QAudio::Error errorState; + QAudio::State deviceState; + +private slots: + void userFeed(); + void feedback(); + void updateAvailable(); + bool deviceReady(); + +signals: + void processMore(); + +private: + bool opened; + bool pullMode; + bool resuming; + int buffer_size; + int period_size; + int intervalTime; + qint64 totalTimeValue; + unsigned int buffer_time; + unsigned int period_time; + snd_pcm_uframes_t buffer_frames; + snd_pcm_uframes_t period_frames; + static void async_callback(snd_async_handler_t *ahandler); + int xrun_recovery(int err); + + int setFormat(); + bool open(); + void close(); + + QTimer* timer; + QByteArray m_device; + int bytesAvailable; + QTime timeStamp; + char* audioBuffer; + snd_pcm_t* handle; + snd_async_handler_t* ahandler; + snd_pcm_access_t access; + snd_pcm_format_t pcmformat; + snd_timestamp_t* timestamp; + snd_pcm_hw_params_t *hwparams; +}; + +class OutputPrivate : public QIODevice +{ + friend class QAudioOutputPrivate; + Q_OBJECT +public: + OutputPrivate(QAudioOutputPrivate* audio); + ~OutputPrivate(); + + qint64 readData( char* data, qint64 len); + qint64 writeData(const char* data, qint64 len); + +private: + QAudioOutputPrivate *audioDevice; +}; + +#endif diff --git a/src/multimedia/audio/qaudiooutput_mac_p.cpp b/src/multimedia/audio/qaudiooutput_mac_p.cpp new file mode 100644 index 0000000..0d6e615 --- /dev/null +++ b/src/multimedia/audio/qaudiooutput_mac_p.cpp @@ -0,0 +1,700 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <CoreServices/CoreServices.h> +#include <CoreAudio/CoreAudio.h> +#include <AudioUnit/AudioUnit.h> +#include <AudioToolbox/AudioToolbox.h> + +#include <QtCore/qendian.h> +#include <QtCore/qbuffer.h> +#include <QtCore/qtimer.h> +#include <QtCore/qdebug.h> + +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudiooutput.h> + +#include "qaudio_mac_p.h" +#include "qaudiooutput_mac_p.h" + + +QT_BEGIN_NAMESPACE + + +namespace +{ + +static const int default_buffer_size = 8 * 1024; + + +class QAudioOutputBuffer : public QObject +{ + Q_OBJECT + +public: + QAudioOutputBuffer(int bufferSize, int maxPeriodSize, QAudioFormat const& audioFormat): + m_deviceError(false), + m_maxPeriodSize(maxPeriodSize), + m_device(0) + { + m_buffer = new QAudioRingBuffer(bufferSize + (bufferSize % maxPeriodSize == 0 ? 0 : maxPeriodSize - (bufferSize % maxPeriodSize))); + m_bytesPerFrame = (audioFormat.sampleSize() / 8) * audioFormat.channels(); + m_periodTime = m_maxPeriodSize / m_bytesPerFrame * 1000 / audioFormat.frequency(); + + m_fillTimer = new QTimer(this); + connect(m_fillTimer, SIGNAL(timeout()), SLOT(fillBuffer())); + } + + ~QAudioOutputBuffer() + { + delete m_buffer; + } + + qint64 readFrames(char* data, qint64 maxFrames) + { + bool wecan = true; + qint64 framesRead = 0; + + while (wecan && framesRead < maxFrames) { + QAudioRingBuffer::Region region = m_buffer->acquireReadRegion((maxFrames - framesRead) * m_bytesPerFrame); + + if (region.second > 0) { + region.second -= region.second % m_bytesPerFrame; + memcpy(data + (framesRead * m_bytesPerFrame), region.first, region.second); + framesRead += region.second / m_bytesPerFrame; + } + else + wecan = false; + + m_buffer->releaseReadRegion(region); + } + + if (framesRead == 0 && m_deviceError) + framesRead = -1; + + return framesRead; + } + + qint64 writeBytes(const char* data, qint64 maxSize) + { + bool wecan = true; + qint64 bytesWritten = 0; + + maxSize -= maxSize % m_bytesPerFrame; + while (wecan && bytesWritten < maxSize) { + QAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(maxSize - bytesWritten); + + if (region.second > 0) { + memcpy(region.first, data + bytesWritten, region.second); + bytesWritten += region.second; + } + else + wecan = false; + + m_buffer->releaseWriteRegion(region); + } + + if (bytesWritten > 0) + emit readyRead(); + + return bytesWritten; + } + + int available() const + { + return m_buffer->free(); + } + + void reset() + { + m_buffer->reset(); + m_deviceError = false; + } + + void setPrefetchDevice(QIODevice* device) + { + if (m_device != device) { + m_device = device; + if (m_device != 0) + fillBuffer(); + } + } + + void startFillTimer() + { + if (m_device != 0) + m_fillTimer->start(m_buffer->size() / 2 / m_maxPeriodSize * m_periodTime); + } + + void stopFillTimer() + { + m_fillTimer->stop(); + } + +signals: + void readyRead(); + +private slots: + void fillBuffer() + { + const int free = m_buffer->free(); + const int writeSize = free - (free % m_maxPeriodSize); + + if (writeSize > 0) { + bool wecan = true; + int filled = 0; + + while (!m_deviceError && wecan && filled < writeSize) { + QAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(writeSize - filled); + + if (region.second > 0) { + region.second = m_device->read(region.first, region.second); + if (region.second > 0) + filled += region.second; + else if (region.second == 0) + wecan = false; + else if (region.second < 0) { + m_fillTimer->stop(); + region.second = 0; + m_deviceError = true; + } + } + else + wecan = false; + + m_buffer->releaseWriteRegion(region); + } + + if (filled > 0) + emit readyRead(); + } + } + +private: + bool m_deviceError; + int m_maxPeriodSize; + int m_bytesPerFrame; + int m_periodTime; + QIODevice* m_device; + QTimer* m_fillTimer; + QAudioRingBuffer* m_buffer; +}; + + +} + +class MacOutputDevice : public QIODevice +{ + Q_OBJECT + +public: + MacOutputDevice(QAudioOutputBuffer* audioBuffer, QObject* parent): + QIODevice(parent), + m_audioBuffer(audioBuffer) + { + open(QIODevice::WriteOnly | QIODevice::Unbuffered); + } + + qint64 readData(char* data, qint64 len) + { + Q_UNUSED(data); + Q_UNUSED(len); + + return 0; + } + + qint64 writeData(const char* data, qint64 len) + { + return m_audioBuffer->writeBytes(data, len); + } + + bool isSequential() const + { + return true; + } + +private: + QAudioOutputBuffer* m_audioBuffer; +}; + + +QAudioOutputPrivate::QAudioOutputPrivate(const QByteArray& device, const QAudioFormat& format): + audioFormat(format) +{ + QDataStream ds(device); + quint32 did, mode; + + ds >> did >> mode; + + if (QAudio::Mode(mode) == QAudio::AudioInput) + errorCode = QAudio::OpenError; + else { + isOpen = false; + audioDeviceId = AudioDeviceID(did); + audioUnit = 0; + audioIO = 0; + startTime = 0; + totalFrames = 0; + audioBuffer = 0; + internalBufferSize = default_buffer_size; + clockFrequency = AudioGetHostClockFrequency() / 1000; + errorCode = QAudio::NoError; + stateCode = QAudio::StopState; + audioThreadState = Stopped; + + intervalTimer = new QTimer(this); + intervalTimer->setInterval(1000); + connect(intervalTimer, SIGNAL(timeout()), SIGNAL(notify())); + } +} + +QAudioOutputPrivate::~QAudioOutputPrivate() +{ + close(); +} + +bool QAudioOutputPrivate::open() +{ + if (errorCode != QAudio::NoError) + return false; + + if (isOpen) + return true; + + ComponentDescription cd; + cd.componentType = kAudioUnitType_Output; + cd.componentSubType = kAudioUnitSubType_HALOutput; + cd.componentManufacturer = kAudioUnitManufacturer_Apple; + cd.componentFlags = 0; + cd.componentFlagsMask = 0; + + // Open + Component cp = FindNextComponent(NULL, &cd); + if (cp == 0) { + qWarning() << "QAudioOutput: Failed to find HAL Output component"; + return false; + } + + if (OpenAComponent(cp, &audioUnit) != noErr) { + qWarning() << "QAudioOutput: Unable to Open Output Component"; + return false; + } + + // register callback + AURenderCallbackStruct cb; + cb.inputProc = renderCallback; + cb.inputProcRefCon = this; + + if (AudioUnitSetProperty(audioUnit, + kAudioUnitProperty_SetRenderCallback, + kAudioUnitScope_Global, + 0, + &cb, + sizeof(cb)) != noErr) { + qWarning() << "QAudioOutput: Failed to set AudioUnit callback"; + return false; + } + + // Set Audio Device + if (AudioUnitSetProperty(audioUnit, + kAudioOutputUnitProperty_CurrentDevice, + kAudioUnitScope_Global, + 0, + &audioDeviceId, + sizeof(audioDeviceId)) != noErr) { + qWarning() << "QAudioOutput: Unable to use configured device"; + return false; + } + + // Set stream format + streamFormat = toAudioStreamBasicDescription(audioFormat); + + UInt32 size = sizeof(deviceFormat); + if (AudioUnitGetProperty(audioUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, + 0, + &deviceFormat, + &size) != noErr) { + qWarning() << "QAudioOutput: Unable to retrieve device format"; + return false; + } + + if (AudioUnitSetProperty(audioUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, + 0, + &streamFormat, + sizeof(streamFormat)) != noErr) { + qWarning() << "QAudioOutput: Unable to Set Stream information"; + return false; + } + + // Allocate buffer + UInt32 numberOfFrames = 0; + size = sizeof(UInt32); + if (AudioUnitGetProperty(audioUnit, + kAudioDevicePropertyBufferFrameSize, + kAudioUnitScope_Global, + 0, + &numberOfFrames, + &size) != noErr) { + qWarning() << "QAudioInput: Failed to get audio period size"; + return false; + } + + periodSizeBytes = (numberOfFrames * streamFormat.mSampleRate / deviceFormat.mSampleRate) * + streamFormat.mBytesPerFrame; + if (internalBufferSize < periodSizeBytes * 2) + internalBufferSize = periodSizeBytes * 2; + else + internalBufferSize -= internalBufferSize % streamFormat.mBytesPerFrame; + + audioBuffer = new QAudioOutputBuffer(internalBufferSize, periodSizeBytes, audioFormat); + connect(audioBuffer, SIGNAL(readyRead()), SLOT(inputReady())); // Pull + + audioIO = new MacOutputDevice(audioBuffer, this); + + // Init + if (AudioUnitInitialize(audioUnit)) { + qWarning() << "QAudioOutput: Failed to initialize AudioUnit"; + return false; + } + + isOpen = true; + + return true; +} + +void QAudioOutputPrivate::close() +{ + if (audioUnit != 0) { + AudioOutputUnitStop(audioUnit); + AudioUnitUninitialize(audioUnit); + CloseComponent(audioUnit); + } + + delete audioBuffer; +} + +QAudioFormat QAudioOutputPrivate::format() const +{ + return audioFormat; +} + +QIODevice* QAudioOutputPrivate::start(QIODevice* device) +{ + QIODevice* op = device; + + if (!open()) { + stateCode = QAudio::StopState; + errorCode = QAudio::OpenError; + return audioIO; + } + + reset(); + audioBuffer->reset(); + audioBuffer->setPrefetchDevice(op); + + if (op == 0) { + op = audioIO; + stateCode = QAudio::IdleState; + } + else + stateCode = QAudio::ActiveState; + + // Start + errorCode = QAudio::NoError; + totalFrames = 0; + startTime = AudioGetCurrentHostTime(); + + if (stateCode == QAudio::ActiveState) + audioThreadStart(); + + return op; +} + +void QAudioOutputPrivate::stop() +{ + QMutexLocker lock(&mutex); + if (stateCode != QAudio::StopState) { + audioThreadDrain(); + + stateCode = QAudio::StopState; + errorCode = QAudio::NoError; + QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode)); + } +} + +void QAudioOutputPrivate::reset() +{ + QMutexLocker lock(&mutex); + if (stateCode != QAudio::StopState) { + audioThreadStop(); + + stateCode = QAudio::StopState; + errorCode = QAudio::NoError; + QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode)); + } +} + +void QAudioOutputPrivate::suspend() +{ + QMutexLocker lock(&mutex); + if (stateCode == QAudio::ActiveState || stateCode == QAudio::IdleState) { + audioThreadStop(); + + stateCode = QAudio::SuspendState; + errorCode = QAudio::NoError; + QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode)); + } +} + +void QAudioOutputPrivate::resume() +{ + QMutexLocker lock(&mutex); + if (stateCode == QAudio::SuspendState) { + audioThreadStart(); + + stateCode = QAudio::ActiveState; + errorCode = QAudio::NoError; + QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode)); + } +} + +int QAudioOutputPrivate::bytesFree() const +{ + return audioBuffer->available(); +} + +int QAudioOutputPrivate::periodSize() const +{ + return periodSizeBytes; +} + +void QAudioOutputPrivate::setBufferSize(int bs) +{ + if (stateCode == QAudio::StopState) + internalBufferSize = bs; +} + +int QAudioOutputPrivate::bufferSize() const +{ + return internalBufferSize; +} + +void QAudioOutputPrivate::setNotifyInterval(int milliSeconds) +{ + intervalTimer->setInterval(milliSeconds); +} + +int QAudioOutputPrivate::notifyInterval() const +{ + return intervalTimer->interval(); +} + +qint64 QAudioOutputPrivate::totalTime() const +{ + return totalFrames * 1000000 / audioFormat.frequency(); +} + +qint64 QAudioOutputPrivate::clock() const +{ + return (AudioGetCurrentHostTime() - startTime) / (clockFrequency / 1000); +} + +QAudio::Error QAudioOutputPrivate::error() const +{ + return errorCode; +} + +QAudio::State QAudioOutputPrivate::state() const +{ + return stateCode; +} + +void QAudioOutputPrivate::audioThreadStart() +{ + startTimers(); + audioThreadState = Running; + AudioOutputUnitStart(audioUnit); +} + +void QAudioOutputPrivate::audioThreadStop() +{ + stopTimers(); + if (audioThreadState.testAndSetAcquire(Running, Stopped)) + threadFinished.wait(&mutex); +} + +void QAudioOutputPrivate::audioThreadDrain() +{ + stopTimers(); + if (audioThreadState.testAndSetAcquire(Running, Draining)) + threadFinished.wait(&mutex); +} + +void QAudioOutputPrivate::audioDeviceStop() +{ + AudioOutputUnitStop(audioUnit); + audioThreadState = Stopped; + threadFinished.wakeOne(); +} + +void QAudioOutputPrivate::audioDeviceIdle() +{ + QMutexLocker lock(&mutex); + if (stateCode == QAudio::ActiveState) { + audioDeviceStop(); + + errorCode = QAudio::UnderrunError; + stateCode = QAudio::IdleState; + QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection); + } +} + +void QAudioOutputPrivate::audioDeviceError() +{ + QMutexLocker lock(&mutex); + if (stateCode == QAudio::ActiveState) { + audioDeviceStop(); + + errorCode = QAudio::IOError; + stateCode = QAudio::StopState; + QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection); + } +} + +void QAudioOutputPrivate::startTimers() +{ + audioBuffer->startFillTimer(); + intervalTimer->start(); +} + +void QAudioOutputPrivate::stopTimers() +{ + audioBuffer->stopFillTimer(); + intervalTimer->stop(); +} + + +void QAudioOutputPrivate::deviceStopped() +{ + intervalTimer->stop(); + emit stateChanged(stateCode); +} + +void QAudioOutputPrivate::inputReady() +{ + QMutexLocker lock(&mutex); + if (stateCode == QAudio::IdleState) { + audioThreadStart(); + + stateCode = QAudio::ActiveState; + errorCode = QAudio::NoError; + + QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode)); + } +} + + +OSStatus QAudioOutputPrivate::renderCallback(void* inRefCon, + AudioUnitRenderActionFlags* ioActionFlags, + const AudioTimeStamp* inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList* ioData) +{ + Q_UNUSED(ioActionFlags) + Q_UNUSED(inTimeStamp) + Q_UNUSED(inBusNumber) + Q_UNUSED(inNumberFrames) + + QAudioOutputPrivate* d = static_cast<QAudioOutputPrivate*>(inRefCon); + + const int threadState = d->audioThreadState.fetchAndAddAcquire(0); + if (threadState == Stopped) { + ioData->mBuffers[0].mDataByteSize = 0; + d->audioDeviceStop(); + } + else { + const UInt32 bytesPerFrame = d->streamFormat.mBytesPerFrame; + qint64 framesRead; + + framesRead = d->audioBuffer->readFrames((char*)ioData->mBuffers[0].mData, + ioData->mBuffers[0].mDataByteSize / bytesPerFrame); + + if (framesRead > 0) { + ioData->mBuffers[0].mDataByteSize = framesRead * bytesPerFrame; + d->totalFrames += framesRead; + } + else { + ioData->mBuffers[0].mDataByteSize = 0; + if (framesRead == 0) { + if (threadState == Draining) + d->audioDeviceStop(); + else + d->audioDeviceIdle(); + } + else + d->audioDeviceError(); + } + } + + return noErr; +} + + +QT_END_NAMESPACE + +#include "qaudiooutput_mac_p.moc" + diff --git a/src/multimedia/audio/qaudiooutput_mac_p.h b/src/multimedia/audio/qaudiooutput_mac_p.h new file mode 100644 index 0000000..c85cab4 --- /dev/null +++ b/src/multimedia/audio/qaudiooutput_mac_p.h @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QAUDIOOUTPUT_MAC_P_H +#define QAUDIOOUTPUT_MAC_P_H + +#include <CoreServices/CoreServices.h> +#include <CoreAudio/CoreAudio.h> +#include <AudioUnit/AudioUnit.h> +#include <AudioToolbox/AudioToolbox.h> + +#include <QtCore/qobject.h> +#include <QtCore/qmutex.h> +#include <QtCore/qwaitcondition.h> +#include <QtCore/qtimer.h> +#include <QtCore/qatomic.h> + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudioformat.h> +#include <QtMultimedia/qaudioengine.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QIODevice; + +namespace +{ +class QAudioOutputBuffer; +} + +class QAudioOutputPrivate : public QAbstractAudioOutput +{ + Q_OBJECT + +public: + bool isOpen; + int internalBufferSize; + int periodSizeBytes; + qint64 totalFrames; + QAudioFormat audioFormat; + QIODevice* audioIO; + AudioDeviceID audioDeviceId; + AudioUnit audioUnit; + Float64 clockFrequency; + UInt64 startTime; + AudioStreamBasicDescription deviceFormat; + AudioStreamBasicDescription streamFormat; + QAudioOutputBuffer* audioBuffer; + QAtomicInt audioThreadState; + QWaitCondition threadFinished; + QMutex mutex; + QTimer* intervalTimer; + + QAudio::Error errorCode; + QAudio::State stateCode; + + QAudioOutputPrivate(const QByteArray& device, const QAudioFormat& format); + ~QAudioOutputPrivate(); + + bool open(); + void close(); + + QAudioFormat format() const; + + QIODevice* start(QIODevice* device); + void stop(); + void reset(); + void suspend(); + void resume(); + + int bytesFree() const; + int periodSize() const; + + void setBufferSize(int value); + int bufferSize() const; + + void setNotifyInterval(int milliSeconds); + int notifyInterval() const; + + qint64 totalTime() const; + qint64 clock() const; + + QAudio::Error error() const; + QAudio::State state() const; + + void audioThreadStart(); + void audioThreadStop(); + void audioThreadDrain(); + + void audioDeviceStop(); + void audioDeviceIdle(); + void audioDeviceError(); + + void startTimers(); + void stopTimers(); + +signals: + void stateChanged(QAudio::State); + void notify(); + +private slots: + void deviceStopped(); + void inputReady(); + +private: + enum { Running, Draining, Stopped }; + + static OSStatus renderCallback(void* inRefCon, + AudioUnitRenderActionFlags* ioActionFlags, + const AudioTimeStamp* inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList* ioData); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/multimedia/audio/qaudiooutput_win32_p.cpp b/src/multimedia/audio/qaudiooutput_win32_p.cpp new file mode 100644 index 0000000..f681936 --- /dev/null +++ b/src/multimedia/audio/qaudiooutput_win32_p.cpp @@ -0,0 +1,502 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qaudiooutput_win32_p.h" + +//#define DEBUG_AUDIO 1 + +QAudioOutputPrivate::QAudioOutputPrivate(const QByteArray &device, const QAudioFormat& audioFormat): + settings(audioFormat) +{ + bytesAvailable = 0; + buffer_size = 0; + period_size = 0; + m_device = device; + totalTimeValue = 0; + intervalTime = 1000; + audioBuffer = 0; + errorState = QAudio::NoError; + deviceState = QAudio::StopState; + audioSource = 0; + pullMode = true; + InitializeCriticalSection(&waveOutCriticalSection); +} + +QAudioOutputPrivate::~QAudioOutputPrivate() +{ + close(); + DeleteCriticalSection(&waveOutCriticalSection); +} + +void CALLBACK QAudioOutputPrivate::waveOutProc( HWAVEOUT hWaveOut, UINT uMsg, + DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 ) +{ + Q_UNUSED(dwParam1) + Q_UNUSED(dwParam2) + Q_UNUSED(hWaveOut) + + QAudioOutputPrivate* qAudio; + qAudio = (QAudioOutputPrivate*)(dwInstance); + if(!qAudio) + return; + + switch(uMsg) { + case WOM_OPEN: + qAudio->feedback(); + break; + case WOM_CLOSE: + return; + case WOM_DONE: + EnterCriticalSection(&waveOutCriticalSection); + qAudio->waveFreeBlockCount++; + if(qAudio->waveFreeBlockCount >= qAudio->buffer_size/qAudio->period_size) + qAudio->waveFreeBlockCount = qAudio->buffer_size/qAudio->period_size; + LeaveCriticalSection(&waveOutCriticalSection); + qAudio->feedback(); + break; + default: + return; + } +} + +WAVEHDR* QAudioOutputPrivate::allocateBlocks(int size, int count) +{ + int i; + unsigned char* buffer; + WAVEHDR* blocks; + DWORD totalBufferSize = (size + sizeof(WAVEHDR))*count; + + if((buffer=(unsigned char*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY, + totalBufferSize)) == 0) { + qWarning("QAudioOutput: Memory allocation error"); + return 0; + } + blocks = (WAVEHDR*)buffer; + buffer += sizeof(WAVEHDR)*count; + for(i = 0; i < count; i++) { + blocks[i].dwBufferLength = size; + blocks[i].lpData = (LPSTR)buffer; + buffer += size; + } + return blocks; +} + +void QAudioOutputPrivate::freeBlocks(WAVEHDR* blockArray) +{ + HeapFree(GetProcessHeap(), 0, blockArray); +} + +QAudioFormat QAudioOutputPrivate::format() const +{ + return settings; +} + +QIODevice* QAudioOutputPrivate::start(QIODevice* device) +{ + if(deviceState != QAudio::StopState) + close(); + + if(!pullMode && audioSource) { + delete audioSource; + } + + if(device) { + //set to pull mode + pullMode = true; + audioSource = device; + deviceState = QAudio::ActiveState; + } else { + //set to push mode + pullMode = false; + audioSource = new OutputPrivate(this); + audioSource->open(QIODevice::WriteOnly|QIODevice::Unbuffered); + deviceState = QAudio::IdleState; + } + + if( !open() ) + return 0; + + emit stateChanged(deviceState); + + return audioSource; +} + +void QAudioOutputPrivate::stop() +{ + if(deviceState == QAudio::StopState) + return; + deviceState = QAudio::StopState; + close(); + if(!pullMode && audioSource) { + delete audioSource; + audioSource = 0; + } + emit stateChanged(deviceState); +} + +bool QAudioOutputPrivate::open() +{ +#ifdef DEBUG_AUDIO + QTime now(QTime::currentTime()); + qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()"; +#endif + if(buffer_size == 0) { + // Default buffer size, 200ms, default period size is 40ms + buffer_size = settings.frequency()*settings.channels()*(settings.sampleSize()/8)*0.2; + period_size = buffer_size/5; + } else { + period_size = buffer_size/5; + } + waveBlocks = allocateBlocks(period_size, buffer_size/period_size); + waveFreeBlockCount = buffer_size/period_size; + waveCurrentBlock = 0; + + if(audioBuffer == 0) + audioBuffer = new char[buffer_size]; + + timeStamp.restart(); + + wfx.nSamplesPerSec = settings.frequency(); + wfx.wBitsPerSample = settings.sampleSize(); + wfx.nChannels = settings.channels(); + wfx.cbSize = 0; + + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nBlockAlign = (wfx.wBitsPerSample >> 3) * wfx.nChannels; + wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec; + + UINT_PTR devId = WAVE_MAPPER; + + WAVEOUTCAPS woc; + unsigned long iNumDevs,ii; + iNumDevs = waveOutGetNumDevs(); + for(ii=0;ii<iNumDevs;ii++) { + if(waveOutGetDevCaps(ii, &woc, sizeof(WAVEOUTCAPS)) + == MMSYSERR_NOERROR) { + QString tmp; + tmp = QString::fromUtf16((const unsigned short*)woc.szPname); + if(tmp.compare(tr(m_device)) == 0) { + devId = ii; + break; + } + } + } + + if(waveOutOpen(&hWaveOut, devId, &wfx, + (DWORD_PTR)&waveOutProc, + (DWORD_PTR) this, + CALLBACK_FUNCTION) != MMSYSERR_NOERROR) { + errorState = QAudio::OpenError; + deviceState = QAudio::StopState; + emit stateChanged(deviceState); + qWarning("QAudioOutput: open error"); + return false; + } + + totalTimeValue = 0; + timeStampOpened.restart(); + + errorState = QAudio::NoError; + if(pullMode) { + deviceState = QAudio::ActiveState; + QTimer::singleShot(10, this, SLOT(feedback())); + } else + deviceState = QAudio::IdleState; + + return true; +} + +void QAudioOutputPrivate::close() +{ + if(deviceState == QAudio::StopState) + return; + + deviceState = QAudio::StopState; + int delay = (buffer_size-bytesFree())*1000/(settings.frequency() + *settings.channels()*(settings.sampleSize()/8)); + waveOutReset(hWaveOut); + Sleep(delay+10); + + freeBlocks(waveBlocks); + waveOutClose(hWaveOut); + delete [] audioBuffer; + audioBuffer = 0; + buffer_size = 0; +} + +int QAudioOutputPrivate::bytesFree() const +{ + int buf; + buf = waveFreeBlockCount*period_size; + return buf; +} + +int QAudioOutputPrivate::periodSize() const +{ + return period_size; +} + +void QAudioOutputPrivate::setBufferSize(int value) +{ + if(deviceState == QAudio::StopState) + buffer_size = value; +} + +int QAudioOutputPrivate::bufferSize() const +{ + return buffer_size; +} + +void QAudioOutputPrivate::setNotifyInterval(int ms) +{ + intervalTime = ms; +} + +int QAudioOutputPrivate::notifyInterval() const +{ + return intervalTime; +} + +qint64 QAudioOutputPrivate::totalTime() const +{ + return totalTimeValue; +} + +qint64 QAudioOutputPrivate::write( const char *data, qint64 len ) +{ + // Write out some audio data + + char* p = (char*)data; + int l = (int)len; + + WAVEHDR* current; + int remain; + current = &waveBlocks[waveCurrentBlock]; + while(l > 0) { + if(waveFreeBlockCount==0) + break; + + if(current->dwFlags & WHDR_PREPARED) + waveOutUnprepareHeader(hWaveOut, current, sizeof(WAVEHDR)); + + if(l < period_size) + remain = l; + else + remain = period_size; + memcpy(current->lpData, p, remain); + + l -= remain; + p += remain; + current->dwBufferLength = remain; + waveOutPrepareHeader(hWaveOut, current, sizeof(WAVEHDR)); + waveOutWrite(hWaveOut, current, sizeof(WAVEHDR)); + + EnterCriticalSection(&waveOutCriticalSection); + waveFreeBlockCount--; + LeaveCriticalSection(&waveOutCriticalSection); +#ifdef DEBUG_AUDIO + qDebug("write out l=%d, waveFreeBlockCount=%d", + current->dwBufferLength,waveFreeBlockCount); +#endif + totalTimeValue += current->dwBufferLength + /(settings.channels()*(settings.sampleSize()/8)) + *1000000/settings.frequency();; + waveCurrentBlock++; + waveCurrentBlock %= buffer_size/period_size; + current = &waveBlocks[waveCurrentBlock]; + current->dwUser = 0; + } + return (len-l); +} + +void QAudioOutputPrivate::resume() +{ + if(deviceState == QAudio::SuspendState) { + deviceState = QAudio::ActiveState; + errorState = QAudio::NoError; + waveOutRestart(hWaveOut); + QTimer::singleShot(10, this, SLOT(feedback())); + emit stateChanged(deviceState); + } +} + +void QAudioOutputPrivate::suspend() +{ + if(deviceState == QAudio::ActiveState) { + waveOutPause(hWaveOut); + deviceState = QAudio::SuspendState; + errorState = QAudio::NoError; + emit stateChanged(deviceState); + } +} + +void QAudioOutputPrivate::feedback() +{ +#ifdef DEBUG_AUDIO + QTime now(QTime::currentTime()); + qDebug()<<now.second()<<"s "<<now.msec()<<"ms :feedback()"; +#endif + bytesAvailable = bytesFree(); + + if(!(deviceState==QAudio::StopState||deviceState==QAudio::SuspendState)) { + if(bytesAvailable >= period_size) + QMetaObject::invokeMethod(this, "deviceReady", Qt::QueuedConnection); + } +} + +bool QAudioOutputPrivate::deviceReady() +{ + if(pullMode) { + int i = 0; + int chunks = bytesAvailable/period_size; +#ifdef DEBUG_AUDIO + qDebug()<<"deviceReady() avail="<<bytesAvailable<<" bytes, period size="<<period_size<<" bytes"; + qDebug()<<"deviceReady() no. of chunks that can fit ="<<chunks<<", chunks in bytes ="<<chunks*period_size; +#endif + bool startup = false; + if(totalTimeValue == 0) + startup = true; + + if(startup) + waveOutPause(hWaveOut); + int input = period_size*chunks; + int l = audioSource->read(audioBuffer,input); + if(l > 0) { + int out= write(audioBuffer,l); + if(out > 0) + deviceState = QAudio::ActiveState; + if(startup) + waveOutRestart(hWaveOut); + } else if(l == 0) { + bytesAvailable = bytesFree(); + if(waveFreeBlockCount == buffer_size/period_size) { + errorState = QAudio::UnderrunError; + deviceState = QAudio::IdleState; + emit stateChanged(deviceState); + } + + } else if(i < 0) { + bytesAvailable = bytesFree(); + errorState = QAudio::IOError; + } + } + if(deviceState != QAudio::ActiveState) + return true; + + if(timeStamp.elapsed() > intervalTime && intervalTime > 50) { + emit notify(); + timeStamp.restart(); + } + + return true; +} + +qint64 QAudioOutputPrivate::clock() const +{ + if(deviceState != QAudio::ActiveState) + return 0; + + return timeStampOpened.elapsed(); +} + +QAudio::Error QAudioOutputPrivate::error() const +{ + return errorState; +} + +QAudio::State QAudioOutputPrivate::state() const +{ + return deviceState; +} + +void QAudioOutputPrivate::reset() +{ + close(); +} + +OutputPrivate::OutputPrivate(QAudioOutputPrivate* audio) +{ + audioDevice = qobject_cast<QAudioOutputPrivate*>(audio); +} + +OutputPrivate::~OutputPrivate() {} + +qint64 OutputPrivate::readData( char* data, qint64 len) +{ + Q_UNUSED(data) + Q_UNUSED(len) + + return 0; +} + +qint64 OutputPrivate::writeData(const char* data, qint64 len) +{ + int retry = 0; + qint64 written = 0; + + if((audioDevice->deviceState == QAudio::ActiveState) + ||(audioDevice->deviceState == QAudio::IdleState)) { + qint64 l = len; + while(written < l) { + int chunk = audioDevice->write(data+written,(l-written)); + if(chunk <= 0) + retry++; + else + written+=chunk; + + if(retry > 10) + return written; + } + audioDevice->deviceState = QAudio::ActiveState; + } + return written; +} diff --git a/src/multimedia/audio/qaudiooutput_win32_p.h b/src/multimedia/audio/qaudiooutput_win32_p.h new file mode 100644 index 0000000..91f14f5 --- /dev/null +++ b/src/multimedia/audio/qaudiooutput_win32_p.h @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QAUDIOOUTPUTWIN_H +#define QAUDIOOUTPUTWIN_H + +#include <windows.h> +#include <mmsystem.h> + +#include <QtCore/qdebug.h> +#include <QtCore/qtimer.h> +#include <QtCore/qstring.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qdatetime.h> + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudioengine.h> + + +static CRITICAL_SECTION waveOutCriticalSection; + +class QAudioOutputPrivate : public QAbstractAudioOutput +{ + Q_OBJECT +public: + QAudioOutputPrivate(const QByteArray &device, const QAudioFormat& audioFormat); + ~QAudioOutputPrivate(); + + qint64 write( const char *data, qint64 len ); + + QAudioFormat format() const; + QIODevice* start(QIODevice* device = 0); + void stop(); + void reset(); + void suspend(); + void resume(); + int bytesFree() const; + int periodSize() const; + void setBufferSize(int value); + int bufferSize() const; + void setNotifyInterval(int milliSeconds); + int notifyInterval() const; + qint64 totalTime() const; + qint64 clock() const; + QAudio::Error error() const; + QAudio::State state() const; + + QIODevice* audioSource; + QAudioFormat settings; + QAudio::Error errorState; + QAudio::State deviceState; + +private slots: + void feedback(); + bool deviceReady(); + +signals: + void stateChanged(QAudio::State); + void notify(); + +private: + QByteArray m_device; + bool resuming; + int bytesAvailable; + QTime timeStamp; + QTime timeStampOpened; + qint32 buffer_size; + qint32 period_size; + qint64 totalTimeValue; + bool pullMode; + int intervalTime; + static void CALLBACK waveOutProc( HWAVEOUT hWaveOut, UINT uMsg, + DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 ); + + WAVEHDR* allocateBlocks(int size, int count); + void freeBlocks(WAVEHDR* blockArray); + bool open(); + void close(); + + WAVEFORMATEX wfx; + HWAVEOUT hWaveOut; + MMRESULT result; + WAVEHDR header; + WAVEHDR* waveBlocks; + volatile int waveFreeBlockCount; + int waveCurrentBlock; + char* audioBuffer; +}; + +class OutputPrivate : public QIODevice +{ + Q_OBJECT +public: + OutputPrivate(QAudioOutputPrivate* audio); + ~OutputPrivate(); + + qint64 readData( char* data, qint64 len); + qint64 writeData(const char* data, qint64 len); + +private: + QAudioOutputPrivate *audioDevice; +}; + +#endif diff --git a/src/multimedia/multimedia.pro b/src/multimedia/multimedia.pro new file mode 100644 index 0000000..7d38afa --- /dev/null +++ b/src/multimedia/multimedia.pro @@ -0,0 +1,11 @@ +TARGET = QtMultimedia +QPRO_PWD = $$PWD +QT = core +DEFINES += QT_BUILD_MULTIMEDIA_LIB + +win32-msvc*:QMAKE_LIBS += $$QMAKE_LIBS_CORE + +unix:QMAKE_PKGCONFIG_REQUIRES = QtCore + +include(../qbase.pri) +include(audio/audio.pri) diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index 20e4802..93d738c 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -89,58 +89,11 @@ QHttpNetworkConnectionPrivate::~QHttpNetworkConnectionPrivate() delete []channels; } -void QHttpNetworkConnectionPrivate::connectSignals(QAbstractSocket *socket) -{ - Q_Q(QHttpNetworkConnection); - - QObject::connect(socket, SIGNAL(bytesWritten(qint64)), - q, SLOT(_q_bytesWritten(qint64)), - Qt::DirectConnection); - QObject::connect(socket, SIGNAL(connected()), - q, SLOT(_q_connected()), - Qt::DirectConnection); - QObject::connect(socket, SIGNAL(readyRead()), - q, SLOT(_q_readyRead()), - Qt::DirectConnection); - QObject::connect(socket, SIGNAL(disconnected()), - q, SLOT(_q_disconnected()), - Qt::DirectConnection); - QObject::connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), - q, SLOT(_q_error(QAbstractSocket::SocketError)), - Qt::DirectConnection); -#ifndef QT_NO_NETWORKPROXY - QObject::connect(socket, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)), - q, SLOT(_q_proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)), - Qt::DirectConnection); -#endif - -#ifndef QT_NO_OPENSSL - QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket); - if (sslSocket) { - // won't be a sslSocket if encrypt is false - QObject::connect(sslSocket, SIGNAL(encrypted()), - q, SLOT(_q_encrypted()), - Qt::DirectConnection); - QObject::connect(sslSocket, SIGNAL(sslErrors(const QList<QSslError>&)), - q, SLOT(_q_sslErrors(const QList<QSslError>&)), - Qt::DirectConnection); - } -#endif -} - void QHttpNetworkConnectionPrivate::init() { - for (int i = 0; i < channelCount; ++i) { -#ifndef QT_NO_OPENSSL - if (encrypt) - channels[i].socket = new QSslSocket; - else - channels[i].socket = new QTcpSocket; -#else - channels[i].socket = new QTcpSocket; -#endif - - connectSignals(channels[i].socket); + for (int i = 0; i < channelCount; i++) { + channels[i].setConnection(this->q_func()); + channels[i].init(); } } @@ -178,35 +131,6 @@ bool QHttpNetworkConnectionPrivate::isSocketReading(QAbstractSocket *socket) con return (i != -1 && (channels[i].state & QHttpNetworkConnectionChannel::ReadingState)); } -void QHttpNetworkConnectionPrivate::appendUncompressedData(QHttpNetworkReply &reply, QByteArray &qba) -{ - reply.d_func()->responseData.append(qba); - - // clear the original! helps with implicit sharing and - // avoiding memcpy when the user is reading the data - qba.clear(); -} - -void QHttpNetworkConnectionPrivate::appendUncompressedData(QHttpNetworkReply &reply, QByteDataBuffer &data) -{ - reply.d_func()->responseData.append(data); - - // clear the original! helps with implicit sharing and - // avoiding memcpy when the user is reading the data - data.clear(); -} - -void QHttpNetworkConnectionPrivate::appendCompressedData(QHttpNetworkReply &reply, QByteDataBuffer &data) -{ - // Work in progress: Later we will directly use a list of QByteArray or a QRingBuffer - // instead of one QByteArray. - for(int i = 0; i < data.bufferCount(); i++) { - QByteArray &byteData = data[i]; - reply.d_func()->compressedData.append(byteData.constData(), byteData.size()); - } - data.clear(); -} - qint64 QHttpNetworkConnectionPrivate::uncompressedBytesAvailable(const QHttpNetworkReply &reply) const { return reply.d_func()->responseData.byteAmount(); @@ -408,7 +332,7 @@ bool QHttpNetworkConnectionPrivate::sendRequest(QAbstractSocket *socket) QNonContiguousByteDevice* uploadByteDevice = channels[i].request.uploadByteDevice(); if (uploadByteDevice) { // connect the signals so this function gets called again - QObject::connect(uploadByteDevice, SIGNAL(readyRead()), q, SLOT(_q_uploadDataReadyRead())); + QObject::connect(uploadByteDevice, SIGNAL(readyRead()), &channels[i], SLOT(_q_uploadDataReadyRead())); channels[i].bytesTotal = channels[i].request.contentLength(); } else { @@ -487,7 +411,7 @@ bool QHttpNetworkConnectionPrivate::sendRequest(QAbstractSocket *socket) { QNonContiguousByteDevice* uploadByteDevice = channels[i].request.uploadByteDevice(); if (uploadByteDevice) { - QObject::disconnect(uploadByteDevice, SIGNAL(readyRead()), q, SLOT(_q_uploadDataReadyRead())); + QObject::disconnect(uploadByteDevice, SIGNAL(readyRead()), &channels[i], SLOT(_q_uploadDataReadyRead())); } // ensure we try to receive a reply in all cases, even if _q_readyRead_ hat not been called // this is needed if the sends an reply before we have finished sending the request. In that @@ -566,7 +490,7 @@ bool QHttpNetworkConnectionPrivate::expand(QAbstractSocket *socket, QHttpNetwork if (ret >= retCheck) { if (inflated.size()) { reply->d_func()->totalProgress += inflated.size(); - appendUncompressedData(*reply, inflated); + reply->d_func()->appendUncompressedReplyData(inflated); if (shouldEmitSignals(reply)) { // important: At the point of this readyRead(), inflated must be cleared, // else implicit sharing will trigger memcpy when the user is reading data! @@ -687,9 +611,9 @@ void QHttpNetworkConnectionPrivate::receiveReply(QAbstractSocket *socket, QHttpN bytes = reply->d_func()->readBody(socket, &byteDatas); if (bytes) { if (reply->d_func()->autoDecompress) - appendCompressedData(*reply, byteDatas); + reply->d_func()->appendCompressedReplyData(byteDatas); else - appendUncompressedData(*reply, byteDatas); + reply->d_func()->appendUncompressedReplyData(byteDatas); if (!reply->d_func()->autoDecompress) { reply->d_func()->totalProgress += bytes; @@ -1006,11 +930,7 @@ void QHttpNetworkConnectionPrivate::unqueueAndSendRequest(QAbstractSocket *socke void QHttpNetworkConnectionPrivate::closeChannel(int channel) { - QAbstractSocket *socket = channels[channel].socket; - socket->blockSignals(true); - socket->close(); - socket->blockSignals(false); - channels[channel].state = QHttpNetworkConnectionChannel::IdleState; + channels[channel].close(); } void QHttpNetworkConnectionPrivate::resendCurrentRequest(QAbstractSocket *socket) @@ -1104,53 +1024,6 @@ void QHttpNetworkConnectionPrivate::removeReply(QHttpNetworkReply *reply) } -//private slots -void QHttpNetworkConnectionPrivate::_q_readyRead() -{ - Q_Q(QHttpNetworkConnection); - QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender()); - if (!socket) - return; // ### error - if (isSocketWaiting(socket) || isSocketReading(socket)) { - int i = indexOf(socket); - channels[i].state = QHttpNetworkConnectionChannel::ReadingState; - if (channels[i].reply) - receiveReply(socket, channels[i].reply); - } - // ### error -} - -void QHttpNetworkConnectionPrivate::_q_bytesWritten(qint64 bytes) -{ - Q_UNUSED(bytes); - Q_Q(QHttpNetworkConnection); - QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender()); - if (!socket) - return; // ### error - // bytes have been written to the socket. write even more of them :) - if (isSocketWriting(socket)) - sendRequest(socket); - // otherwise we do nothing -} - -void QHttpNetworkConnectionPrivate::_q_disconnected() -{ - Q_Q(QHttpNetworkConnection); - QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender()); - if (!socket) - return; // ### error - // read the available data before closing - int i = indexOf(socket); - if (isSocketWaiting(socket) || isSocketReading(socket)) { - channels[i].state = QHttpNetworkConnectionChannel::ReadingState; - if (channels[i].reply) - receiveReply(socket, channels[i].reply); - } else if (channels[i].state == QHttpNetworkConnectionChannel::IdleState && channels[i].resendCurrent) { - // re-sending request because the socket was in ClosingState - QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection); - } - channels[i].state = QHttpNetworkConnectionChannel::IdleState; -} void QHttpNetworkConnectionPrivate::_q_startNextRequest() { @@ -1190,121 +1063,6 @@ void QHttpNetworkConnectionPrivate::_q_restartAuthPendingRequests() } } -void QHttpNetworkConnectionPrivate::_q_connected() -{ - Q_Q(QHttpNetworkConnection); - QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender()); - if (!socket) - return; // ### error - - // improve performance since we get the request sent by the kernel ASAP - socket->setSocketOption(QAbstractSocket::LowDelayOption, 1); - - int i = indexOf(socket); - // ### FIXME: if the server closes the connection unexpectedly, we shouldn't send the same broken request again! - //channels[i].reconnectAttempts = 2; - if (!channels[i].pendingEncrypt) { - channels[i].state = QHttpNetworkConnectionChannel::IdleState; - if (channels[i].reply) - sendRequest(socket); - else - closeChannel(i); - } -} - - -void QHttpNetworkConnectionPrivate::_q_error(QAbstractSocket::SocketError socketError) -{ - Q_Q(QHttpNetworkConnection); - QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender()); - if (!socket) - return; - bool send2Reply = false; - int i = indexOf(socket); - QNetworkReply::NetworkError errorCode = QNetworkReply::UnknownNetworkError; - - switch (socketError) { - case QAbstractSocket::HostNotFoundError: - errorCode = QNetworkReply::HostNotFoundError; - break; - case QAbstractSocket::ConnectionRefusedError: - errorCode = QNetworkReply::ConnectionRefusedError; - break; - case QAbstractSocket::RemoteHostClosedError: - // try to reconnect/resend before sending an error. - // while "Reading" the _q_disconnected() will handle this. - if (channels[i].state != QHttpNetworkConnectionChannel::IdleState && channels[i].state != QHttpNetworkConnectionChannel::ReadingState) { - if (channels[i].reconnectAttempts-- > 0) { - resendCurrentRequest(socket); - return; - } else { - send2Reply = true; - errorCode = QNetworkReply::RemoteHostClosedError; - } - } else { - return; - } - break; - case QAbstractSocket::SocketTimeoutError: - // try to reconnect/resend before sending an error. - if (channels[i].state == QHttpNetworkConnectionChannel::WritingState && (channels[i].reconnectAttempts-- > 0)) { - resendCurrentRequest(socket); - return; - } - send2Reply = true; - errorCode = QNetworkReply::TimeoutError; - break; - case QAbstractSocket::ProxyAuthenticationRequiredError: - errorCode = QNetworkReply::ProxyAuthenticationRequiredError; - break; - case QAbstractSocket::SslHandshakeFailedError: - errorCode = QNetworkReply::SslHandshakeFailedError; - break; - default: - // all other errors are treated as NetworkError - errorCode = QNetworkReply::UnknownNetworkError; - break; - } - QPointer<QObject> that = q; - QString errorString = errorDetail(errorCode, socket); - if (send2Reply) { - if (channels[i].reply) { - channels[i].reply->d_func()->errorString = errorString; - // this error matters only to this reply - emit channels[i].reply->finishedWithError(errorCode, errorString); - } - // send the next request - QMetaObject::invokeMethod(that, "_q_startNextRequest", Qt::QueuedConnection); - } else { - // the failure affects all requests. - emit q->error(errorCode, errorString); - } - if (that) //signals make enter the event loop - closeChannel(i); -} - -#ifndef QT_NO_NETWORKPROXY -void QHttpNetworkConnectionPrivate::_q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator* auth) -{ - Q_Q(QHttpNetworkConnection); - emit q->proxyAuthenticationRequired(proxy, auth, q); -} -#endif - -void QHttpNetworkConnectionPrivate::_q_uploadDataReadyRead() -{ - Q_Q(QHttpNetworkConnection); - // upload data emitted readyRead() - // find out which channel it is for - QObject *sender = q->sender(); - - for (int i = 0; i < channelCount; ++i) { - if (sender == channels[i].request.uploadByteDevice()) { - sendRequest(channels[i].socket); - break; - } - } -} QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 port, bool encrypt, QObject *parent) : QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt)), parent) @@ -1399,27 +1157,6 @@ QNetworkProxy QHttpNetworkConnection::transparentProxy() const // SSL support below #ifndef QT_NO_OPENSSL -void QHttpNetworkConnectionPrivate::_q_encrypted() -{ - Q_Q(QHttpNetworkConnection); - QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender()); - if (!socket) - return; // ### error - int i = indexOf(socket); - channels[i].state = QHttpNetworkConnectionChannel::IdleState; - sendRequest(socket); -} - -void QHttpNetworkConnectionPrivate::_q_sslErrors(const QList<QSslError> &errors) -{ - Q_Q(QHttpNetworkConnection); - QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender()); - if (!socket) - return; - //QNetworkReply::NetworkError errorCode = QNetworkReply::ProtocolFailure; - emit q->sslErrors(errors); -} - QSslConfiguration QHttpNetworkConnectionPrivate::sslConfiguration(const QHttpNetworkReply &reply) const { if (!encrypt) diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h index eb7a955..d8e21cd 100644 --- a/src/network/access/qhttpnetworkconnection_p.h +++ b/src/network/access/qhttpnetworkconnection_p.h @@ -138,23 +138,10 @@ private: Q_DECLARE_SCOPED_PRIVATE(QHttpNetworkConnection) Q_DISABLE_COPY(QHttpNetworkConnection) friend class QHttpNetworkReply; + friend class QHttpNetworkConnectionChannel; - Q_PRIVATE_SLOT(d_func(), void _q_bytesWritten(qint64)) - Q_PRIVATE_SLOT(d_func(), void _q_readyRead()) - Q_PRIVATE_SLOT(d_func(), void _q_disconnected()) Q_PRIVATE_SLOT(d_func(), void _q_startNextRequest()) Q_PRIVATE_SLOT(d_func(), void _q_restartAuthPendingRequests()) - Q_PRIVATE_SLOT(d_func(), void _q_connected()) - Q_PRIVATE_SLOT(d_func(), void _q_error(QAbstractSocket::SocketError)) -#ifndef QT_NO_NETWORKPROXY - Q_PRIVATE_SLOT(d_func(), void _q_proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)) -#endif - Q_PRIVATE_SLOT(d_func(), void _q_uploadDataReadyRead()) - -#ifndef QT_NO_OPENSSL - Q_PRIVATE_SLOT(d_func(), void _q_encrypted()) - Q_PRIVATE_SLOT(d_func(), void _q_sslErrors(const QList<QSslError>&)) -#endif }; @@ -169,7 +156,6 @@ public: QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt); ~QHttpNetworkConnectionPrivate(); void init(); - void connectSignals(QAbstractSocket *socket); enum { ChunkSize = 4096 }; @@ -189,18 +175,8 @@ public: void copyCredentials(int fromChannel, QAuthenticator *auth, bool isProxy); // private slots - void _q_bytesWritten(qint64 bytes); // proceed sending - void _q_readyRead(); // pending data to read - void _q_disconnected(); // disconnected from host void _q_startNextRequest(); // send the next request from the queue void _q_restartAuthPendingRequests(); // send the currently blocked request - void _q_connected(); // start sending request - void _q_error(QAbstractSocket::SocketError); // error from socket -#ifndef QT_NO_NETWORKPROXY - void _q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth); // from transparent proxy -#endif - - void _q_uploadDataReadyRead(); void createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request); bool ensureConnection(QAbstractSocket *socket); @@ -221,10 +197,6 @@ public: bool pendingAuthSignal; // there is an incomplete authentication signal bool pendingProxyAuthSignal; // there is an incomplete proxy authentication signal - void appendUncompressedData(QHttpNetworkReply &reply, QByteArray &qba); - void appendUncompressedData(QHttpNetworkReply &reply, QByteDataBuffer &data); - void appendCompressedData(QHttpNetworkReply &reply, QByteDataBuffer &data); - qint64 uncompressedBytesAvailable(const QHttpNetworkReply &reply) const; qint64 uncompressedBytesAvailableNextBlock(const QHttpNetworkReply &reply) const; qint64 compressedBytesAvailable(const QHttpNetworkReply &reply) const; @@ -237,8 +209,6 @@ public: inline bool expectContent(QHttpNetworkReply *reply); #ifndef QT_NO_OPENSSL - void _q_encrypted(); // start sending request (https) - void _q_sslErrors(const QList<QSslError> &errors); // ssl errors from the socket QSslConfiguration sslConfiguration(const QHttpNetworkReply &reply) const; #endif @@ -249,6 +219,8 @@ public: //The request queues QList<HttpMessagePair> highPriorityQueue; QList<HttpMessagePair> lowPriorityQueue; + + friend class QHttpNetworkConnectionChannel; }; diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index a04b530..6cd46fd 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -57,6 +57,220 @@ QT_BEGIN_NAMESPACE // TODO: Put channel specific stuff here so it does not polute qhttpnetworkconnection.cpp +void QHttpNetworkConnectionChannel::init() +{ +#ifndef QT_NO_OPENSSL + if (connection->d_func()->encrypt) + socket = new QSslSocket; + else + socket = new QTcpSocket; +#else + socket = new QTcpSocket; +#endif + + QObject::connect(socket, SIGNAL(bytesWritten(qint64)), + this, SLOT(_q_bytesWritten(qint64)), + Qt::DirectConnection); + QObject::connect(socket, SIGNAL(connected()), + this, SLOT(_q_connected()), + Qt::DirectConnection); + QObject::connect(socket, SIGNAL(readyRead()), + this, SLOT(_q_readyRead()), + Qt::DirectConnection); + QObject::connect(socket, SIGNAL(disconnected()), + this, SLOT(_q_disconnected()), + Qt::DirectConnection); + QObject::connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(_q_error(QAbstractSocket::SocketError)), + Qt::DirectConnection); +#ifndef QT_NO_NETWORKPROXY + QObject::connect(socket, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)), + this, SLOT(_q_proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)), + Qt::DirectConnection); +#endif + +#ifndef QT_NO_OPENSSL + QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket); + if (sslSocket) { + // won't be a sslSocket if encrypt is false + QObject::connect(sslSocket, SIGNAL(encrypted()), + this, SLOT(_q_encrypted()), + Qt::DirectConnection); + QObject::connect(sslSocket, SIGNAL(sslErrors(const QList<QSslError>&)), + this, SLOT(_q_sslErrors(const QList<QSslError>&)), + Qt::DirectConnection); + } +#endif +} + + +void QHttpNetworkConnectionChannel::close() +{ + socket->blockSignals(true); + socket->close(); + socket->blockSignals(false); + state = QHttpNetworkConnectionChannel::IdleState; +} + + +//private slots +void QHttpNetworkConnectionChannel::_q_readyRead() +{ + if (!socket) + return; // ### error + if (connection->d_func()->isSocketWaiting(socket) || connection->d_func()->isSocketReading(socket)) { + state = QHttpNetworkConnectionChannel::ReadingState; + if (reply) + connection->d_func()->receiveReply(socket, reply); + } + // ### error +} + +void QHttpNetworkConnectionChannel::_q_bytesWritten(qint64 bytes) +{ + Q_UNUSED(bytes); + if (!socket) + return; // ### error + // bytes have been written to the socket. write even more of them :) + if (connection->d_func()->isSocketWriting(socket)) + connection->d_func()->sendRequest(socket); + // otherwise we do nothing +} + +void QHttpNetworkConnectionChannel::_q_disconnected() +{ + if (!socket) + return; // ### error + // read the available data before closing + if (connection->d_func()->isSocketWaiting(socket) || connection->d_func()->isSocketReading(socket)) { + state = QHttpNetworkConnectionChannel::ReadingState; + if (reply) + connection->d_func()->receiveReply(socket, reply); + } else if (state == QHttpNetworkConnectionChannel::IdleState && resendCurrent) { + // re-sending request because the socket was in ClosingState + QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); + } + state = QHttpNetworkConnectionChannel::IdleState; +} + + +void QHttpNetworkConnectionChannel::_q_connected() +{ + if (!socket) + return; // ### error + + // improve performance since we get the request sent by the kernel ASAP + socket->setSocketOption(QAbstractSocket::LowDelayOption, 1); + + // ### FIXME: if the server closes the connection unexpectedly, we shouldn't send the same broken request again! + //channels[i].reconnectAttempts = 2; + if (!pendingEncrypt) { + state = QHttpNetworkConnectionChannel::IdleState; + if (reply) + connection->d_func()->sendRequest(socket); + else + close(); + } +} + + +void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socketError) +{ + if (!socket) + return; + bool send2Reply = false; + QNetworkReply::NetworkError errorCode = QNetworkReply::UnknownNetworkError; + + switch (socketError) { + case QAbstractSocket::HostNotFoundError: + errorCode = QNetworkReply::HostNotFoundError; + break; + case QAbstractSocket::ConnectionRefusedError: + errorCode = QNetworkReply::ConnectionRefusedError; + break; + case QAbstractSocket::RemoteHostClosedError: + // try to reconnect/resend before sending an error. + // while "Reading" the _q_disconnected() will handle this. + if (state != QHttpNetworkConnectionChannel::IdleState && state != QHttpNetworkConnectionChannel::ReadingState) { + if (reconnectAttempts-- > 0) { + connection->d_func()->resendCurrentRequest(socket); + return; + } else { + send2Reply = true; + errorCode = QNetworkReply::RemoteHostClosedError; + } + } else { + return; + } + break; + case QAbstractSocket::SocketTimeoutError: + // try to reconnect/resend before sending an error. + if (state == QHttpNetworkConnectionChannel::WritingState && (reconnectAttempts-- > 0)) { + connection->d_func()->resendCurrentRequest(socket); + return; + } + send2Reply = true; + errorCode = QNetworkReply::TimeoutError; + break; + case QAbstractSocket::ProxyAuthenticationRequiredError: + errorCode = QNetworkReply::ProxyAuthenticationRequiredError; + break; + case QAbstractSocket::SslHandshakeFailedError: + errorCode = QNetworkReply::SslHandshakeFailedError; + break; + default: + // all other errors are treated as NetworkError + errorCode = QNetworkReply::UnknownNetworkError; + break; + } + QPointer<QObject> that = connection; + QString errorString = connection->d_func()->errorDetail(errorCode, socket); + if (send2Reply) { + if (reply) { + reply->d_func()->errorString = errorString; + // this error matters only to this reply + emit reply->finishedWithError(errorCode, errorString); + } + // send the next request + QMetaObject::invokeMethod(that, "_q_startNextRequest", Qt::QueuedConnection); + } else { + // the failure affects all requests. + emit connection->error(errorCode, errorString); + } + if (that) //signal emission triggered event loop + close(); +} + +#ifndef QT_NO_NETWORKPROXY +void QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator* auth) +{ + emit connection->proxyAuthenticationRequired(proxy, auth, connection); +} +#endif + +void QHttpNetworkConnectionChannel::_q_uploadDataReadyRead() +{ + connection->d_func()->sendRequest(socket); +} + +#ifndef QT_NO_OPENSSL +void QHttpNetworkConnectionChannel::_q_encrypted() +{ + if (!socket) + return; // ### error + state = QHttpNetworkConnectionChannel::IdleState; + connection->d_func()->sendRequest(socket); +} + +void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors) +{ + if (!socket) + return; + //QNetworkReply::NetworkError errorCode = QNetworkReply::ProtocolFailure; + emit connection->sslErrors(errors); +} +#endif + QT_END_NAMESPACE #include "moc_qhttpnetworkconnectionchannel_p.cpp" diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h index cbabc67..013e7a5 100644 --- a/src/network/access/qhttpnetworkconnectionchannel_p.h +++ b/src/network/access/qhttpnetworkconnectionchannel_p.h @@ -80,6 +80,7 @@ QT_BEGIN_NAMESPACE class QHttpNetworkRequest; class QHttpNetworkReply; class QByteArray; +class QHttpNetworkConnection; class QHttpNetworkConnectionChannel : public QObject { Q_OBJECT @@ -117,7 +118,31 @@ public: #ifndef QT_NO_OPENSSL , ignoreAllSslErrors(false) #endif + , connection(0) {} + + void setConnection(QHttpNetworkConnection *c) {connection = c;} + QHttpNetworkConnection *connection; + + void init(); + void close(); + + protected slots: + void _q_bytesWritten(qint64 bytes); // proceed sending + void _q_readyRead(); // pending data to read + void _q_disconnected(); // disconnected from host + void _q_connected(); // start sending request + void _q_error(QAbstractSocket::SocketError); // error from socket +#ifndef QT_NO_NETWORKPROXY + void _q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth); // from transparent proxy +#endif + + void _q_uploadDataReadyRead(); + +#ifndef QT_NO_OPENSSL + void _q_encrypted(); // start sending request (https) + void _q_sslErrors(const QList<QSslError> &errors); // ssl errors from the socket +#endif }; diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp index c11b1a6..52672d5 100644 --- a/src/network/access/qhttpnetworkreply.cpp +++ b/src/network/access/qhttpnetworkreply.cpp @@ -685,6 +685,36 @@ qint64 QHttpNetworkReplyPrivate::getChunkSize(QIODevice *in, qint64 *chunkSize) return bytes; } +void QHttpNetworkReplyPrivate::appendUncompressedReplyData(QByteArray &qba) +{ + responseData.append(qba); + + // clear the original! helps with implicit sharing and + // avoiding memcpy when the user is reading the data + qba.clear(); +} + +void QHttpNetworkReplyPrivate::appendUncompressedReplyData(QByteDataBuffer &data) +{ + responseData.append(data); + + // clear the original! helps with implicit sharing and + // avoiding memcpy when the user is reading the data + data.clear(); +} + +void QHttpNetworkReplyPrivate::appendCompressedReplyData(QByteDataBuffer &data) +{ + // Work in progress: Later we will directly use a list of QByteArray or a QRingBuffer + // instead of one QByteArray. + for(int i = 0; i < data.bufferCount(); i++) { + QByteArray &byteData = data[i]; + compressedData.append(byteData.constData(), byteData.size()); + } + data.clear(); +} + + // SSL support below #ifndef QT_NO_OPENSSL diff --git a/src/network/access/qhttpnetworkreply_p.h b/src/network/access/qhttpnetworkreply_p.h index 0982685..27c5860 100644 --- a/src/network/access/qhttpnetworkreply_p.h +++ b/src/network/access/qhttpnetworkreply_p.h @@ -149,6 +149,7 @@ private: Q_DECLARE_SCOPED_PRIVATE(QHttpNetworkReply) friend class QHttpNetworkConnection; friend class QHttpNetworkConnectionPrivate; + friend class QHttpNetworkConnectionChannel; }; @@ -171,6 +172,10 @@ public: qint64 readReplyBodyChunked(QIODevice *in, QByteDataBuffer *out); qint64 getChunkSize(QIODevice *in, qint64 *chunkSize); + void appendUncompressedReplyData(QByteArray &qba); + void appendUncompressedReplyData(QByteDataBuffer &data); + void appendCompressedReplyData(QByteDataBuffer &data); + qint64 bytesAvailable() const; bool isChunked(); bool connectionCloseEnabled(); diff --git a/src/network/socket/qnet_unix_p.h b/src/network/socket/qnet_unix_p.h index f38c2fc..040b3ec 100644 --- a/src/network/socket/qnet_unix_p.h +++ b/src/network/socket/qnet_unix_p.h @@ -85,7 +85,7 @@ static inline int qt_safe_socket(int domain, int type, int protocol, int flags = Q_ASSERT((flags & ~O_NONBLOCK) == 0); register int fd; -#ifdef SOCK_CLOEXEC +#if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK) int newtype = type | SOCK_CLOEXEC; if (flags & O_NONBLOCK) newtype |= SOCK_NONBLOCK; diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index e124c2e..bd7daf2 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -4603,7 +4603,7 @@ QGLFormat QGLDrawable::format() const GLuint QGLDrawable::bindTexture(const QImage &image, GLenum target, GLint format) { - QGLTexture *texture; + QGLTexture *texture = 0; if (widget) texture = widget->d_func()->glcx->d_func()->bindTexture(image, target, format, true); else if (buffer) @@ -4619,7 +4619,7 @@ GLuint QGLDrawable::bindTexture(const QImage &image, GLenum target, GLint format GLuint QGLDrawable::bindTexture(const QPixmap &pixmap, GLenum target, GLint format) { - QGLTexture *texture; + QGLTexture *texture = 0; if (widget) texture = widget->d_func()->glcx->d_func()->bindTexture(pixmap, target, format, true, true); else if (buffer) @@ -4717,6 +4717,7 @@ void QGLShareRegister::removeShare(const QGLContext *context) { int count = it.value().removeAll(context); Q_ASSERT(count == 1); + Q_UNUSED(count); Q_ASSERT(it.value().size() != 0); if (it.value().size() == 1) diff --git a/src/plugins/audio/audio.pro b/src/plugins/audio/audio.pro new file mode 100644 index 0000000..e93b369 --- /dev/null +++ b/src/plugins/audio/audio.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs + +#SUBDIRS += ossaudio diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro index 76056a32..004b816 100644 --- a/src/plugins/plugins.pro +++ b/src/plugins/plugins.pro @@ -11,3 +11,4 @@ embedded:SUBDIRS *= gfxdrivers decorations mousedrivers kbddrivers !win32:!embedded:!mac:!symbian:SUBDIRS *= inputmethods symbian:SUBDIRS += s60 contains(QT_CONFIG, phonon): SUBDIRS *= phonon +contains(QT_CONFIG, multimedia): SUBDIRS *= audio diff --git a/src/sql/drivers/mysql/qsql_mysql.cpp b/src/sql/drivers/mysql/qsql_mysql.cpp index bd6f7b9..39ef1ef 100644 --- a/src/sql/drivers/mysql/qsql_mysql.cpp +++ b/src/sql/drivers/mysql/qsql_mysql.cpp @@ -85,11 +85,10 @@ public: #else tc(0), #endif - preparedQuerys(false), preparedQuerysEnabled(false) {} + preparedQuerysEnabled(false) {} MYSQL *mysql; QTextCodec *tc; - bool preparedQuerys; bool preparedQuerysEnabled; }; @@ -172,6 +171,7 @@ public: #if MYSQL_VERSION_ID >= 40108 , stmt(0), meta(0), inBinds(0), outBinds(0) #endif + , preparedQuery(false) { connect(dp, SIGNAL(destroyed()), this, SLOT(driverDestroyed())); } @@ -209,6 +209,9 @@ public: MYSQL_BIND *inBinds; MYSQL_BIND *outBinds; #endif + + bool preparedQuery; + private Q_SLOTS: void driverDestroyed() { driver = NULL; } }; @@ -399,7 +402,7 @@ QMYSQLResult::~QMYSQLResult() QVariant QMYSQLResult::handle() const { #if MYSQL_VERSION_ID >= 40108 - if(d->driver && d->driver->d->preparedQuerys) + if(d->preparedQuery) return d->meta ? qVariantFromValue(d->meta) : qVariantFromValue(d->stmt); else #endif @@ -454,9 +457,6 @@ void QMYSQLResult::cleanup() d->row = NULL; setAt(-1); setActive(false); - - if(d->driver) - d->driver->d->preparedQuerys = d->driver->d->preparedQuerysEnabled; } bool QMYSQLResult::fetch(int i) @@ -474,7 +474,7 @@ bool QMYSQLResult::fetch(int i) } if (at() == i) return true; - if (d->driver->d->preparedQuerys) { + if (d->preparedQuery) { #if MYSQL_VERSION_ID >= 40108 mysql_stmt_data_seek(d->stmt, i); @@ -507,7 +507,7 @@ bool QMYSQLResult::fetchNext() { if(!d->driver) return false; - if (d->driver->d->preparedQuerys) { + if (d->preparedQuery) { #if MYSQL_VERSION_ID >= 40108 if (mysql_stmt_fetch(d->stmt)) return false; @@ -534,7 +534,7 @@ bool QMYSQLResult::fetchLast() } my_ulonglong numRows; - if (d->driver->d->preparedQuerys) { + if (d->preparedQuery) { #if MYSQL_VERSION_ID >= 40108 numRows = mysql_stmt_num_rows(d->stmt); #else @@ -574,7 +574,7 @@ QVariant QMYSQLResult::data(int field) int fieldLength = 0; const QMYSQLResultPrivate::QMyField &f = d->fields.at(field); QString val; - if (d->driver->d->preparedQuerys) { + if (d->preparedQuery) { if (f.nullIndicator) return QVariant(f.type); @@ -634,7 +634,7 @@ QVariant QMYSQLResult::data(int field) case QVariant::ByteArray: { QByteArray ba; - if (d->driver->d->preparedQuerys) { + if (d->preparedQuery) { ba = QByteArray(f.outField, f.bufLength); } else { ba = QByteArray(d->row[field], fieldLength); @@ -651,7 +651,7 @@ QVariant QMYSQLResult::data(int field) bool QMYSQLResult::isNull(int field) { - if (d->driver->d->preparedQuerys) + if (d->preparedQuery) return d->fields.at(field).nullIndicator; else return d->row[field] == NULL; @@ -662,11 +662,9 @@ bool QMYSQLResult::reset (const QString& query) if (!driver() || !driver()->isOpen() || driver()->isOpenError() || !d->driver) return false; - if(d->driver->d->preparedQuerysEnabled && prepare(query)) { - d->driver->d->preparedQuerys = true; - return exec(); - } - d->driver->d->preparedQuerys = false; + d->preparedQuery = false; + + cleanup(); const QByteArray encQuery(fromUnicode(d->driver->d->tc, query)); if (mysql_real_query(d->driver->d->mysql, encQuery.data(), encQuery.length())) { @@ -699,7 +697,7 @@ bool QMYSQLResult::reset (const QString& query) int QMYSQLResult::size() { if (d->driver && isSelect()) - if (d->driver->d->preparedQuerys) + if (d->preparedQuery) #if MYSQL_VERSION_ID >= 40108 return mysql_stmt_num_rows(d->stmt); #else @@ -721,7 +719,7 @@ QVariant QMYSQLResult::lastInsertId() const if (!isActive() || !d->driver) return QVariant(); - if (d->driver->d->preparedQuerys) { + if (d->preparedQuery) { #if MYSQL_VERSION_ID >= 40108 quint64 id = mysql_stmt_insert_id(d->stmt); if (id) @@ -743,7 +741,7 @@ QSqlRecord QMYSQLResult::record() const return info; #if MYSQL_VERSION_ID >= 40108 - res = d->driver->d->preparedQuerys ? d->meta : d->result; + res = d->preparedQuery ? d->meta : d->result; #else res = d->result; #endif @@ -856,7 +854,7 @@ bool QMYSQLResult::prepare(const QString& query) return false; #if MYSQL_VERSION_ID >= 40108 cleanup(); - if (!d->driver->d->preparedQuerys) + if (!d->driver->d->preparedQuerysEnabled) return QSqlResult::prepare(query); int r; @@ -886,6 +884,7 @@ bool QMYSQLResult::prepare(const QString& query) } setSelect(d->bindInValues()); + d->preparedQuery = true; return true; #else return false; @@ -896,7 +895,7 @@ bool QMYSQLResult::exec() { if (!d->driver) return false; - if (!d->driver->d->preparedQuerys) + if (!d->preparedQuery) return QSqlResult::exec(); if (!d->stmt) return false; @@ -1338,9 +1337,6 @@ QSqlIndex QMYSQLDriver::primaryIndex(const QString& tablename) const if (!isOpen()) return idx; - prepQ = d->preparedQuerysEnabled; - d->preparedQuerysEnabled = false; - QSqlQuery i(createResult()); QString stmt(QLatin1String("show index from %1;")); QSqlRecord fil = record(tablename); @@ -1353,7 +1349,6 @@ QSqlIndex QMYSQLDriver::primaryIndex(const QString& tablename) const } } - d->preparedQuerysEnabled = prepQ; return idx; } diff --git a/src/sql/drivers/psql/qsql_psql.cpp b/src/sql/drivers/psql/qsql_psql.cpp index c61c526..4fd0c11 100644 --- a/src/sql/drivers/psql/qsql_psql.cpp +++ b/src/sql/drivers/psql/qsql_psql.cpp @@ -940,7 +940,7 @@ QSqlIndex QPSQLDriver::primaryIndex(const QString& tablename) const case QPSQLDriver::Version6: stmt = QLatin1String("select pg_att1.attname, int(pg_att1.atttypid), pg_cl.relname " "from pg_attribute pg_att1, pg_attribute pg_att2, pg_class pg_cl, pg_index pg_ind " - "where lower(pg_cl.relname) = '%1_pkey' " + "where pg_cl.relname = '%1_pkey' " "and pg_cl.oid = pg_ind.indexrelid " "and pg_att2.attrelid = pg_ind.indexrelid " "and pg_att1.attrelid = pg_ind.indrelid " @@ -951,7 +951,7 @@ QSqlIndex QPSQLDriver::primaryIndex(const QString& tablename) const case QPSQLDriver::Version71: stmt = QLatin1String("select pg_att1.attname, pg_att1.atttypid::int, pg_cl.relname " "from pg_attribute pg_att1, pg_attribute pg_att2, pg_class pg_cl, pg_index pg_ind " - "where lower(pg_cl.relname) = '%1_pkey' " + "where pg_cl.relname = '%1_pkey' " "and pg_cl.oid = pg_ind.indexrelid " "and pg_att2.attrelid = pg_ind.indexrelid " "and pg_att1.attrelid = pg_ind.indrelid " @@ -1016,7 +1016,7 @@ QSqlRecord QPSQLDriver::record(const QString& tablename) const "pg_attribute.attnotnull, pg_attribute.attlen, pg_attribute.atttypmod, " "int(pg_attribute.attrelid), pg_attribute.attnum " "from pg_class, pg_attribute " - "where lower(pg_class.relname) = '%1' " + "where pg_class.relname = '%1' " "and pg_attribute.attnum > 0 " "and pg_attribute.attrelid = pg_class.oid "); break; @@ -1025,7 +1025,7 @@ QSqlRecord QPSQLDriver::record(const QString& tablename) const "pg_attribute.attnotnull, pg_attribute.attlen, pg_attribute.atttypmod, " "pg_attribute.attrelid::int, pg_attribute.attnum " "from pg_class, pg_attribute " - "where lower(pg_class.relname) = '%1' " + "where pg_class.relname = '%1' " "and pg_attribute.attnum > 0 " "and pg_attribute.attrelid = pg_class.oid "); break; @@ -1036,7 +1036,7 @@ QSqlRecord QPSQLDriver::record(const QString& tablename) const "from pg_class, pg_attribute " "left join pg_attrdef on (pg_attrdef.adrelid = " "pg_attribute.attrelid and pg_attrdef.adnum = pg_attribute.attnum) " - "where lower(pg_class.relname) = '%1' " + "where pg_class.relname = '%1' " "and pg_attribute.attnum > 0 " "and pg_attribute.attrelid = pg_class.oid " "order by pg_attribute.attnum "); diff --git a/src/src.pro b/src/src.pro index 872dbef..607209a 100644 --- a/src/src.pro +++ b/src/src.pro @@ -25,6 +25,7 @@ contains(QT_CONFIG, opengl)|contains(QT_CONFIG, opengles1)|contains(QT_CONFIG, o contains(QT_CONFIG, openvg): SRC_SUBDIRS += src_openvg contains(QT_CONFIG, xmlpatterns): SRC_SUBDIRS += src_xmlpatterns contains(QT_CONFIG, phonon): SRC_SUBDIRS += src_phonon +contains(QT_CONFIG, multimedia): SRC_SUBDIRS += src_multimedia contains(QT_CONFIG, svg): SRC_SUBDIRS += src_svg contains(QT_CONFIG, webkit) { #exists($$QT_SOURCE_TREE/src/3rdparty/webkit/JavaScriptCore/JavaScriptCore.pro): SRC_SUBDIRS += src_javascriptcore @@ -73,6 +74,8 @@ src_qt3support.subdir = $$QT_SOURCE_TREE/src/qt3support src_qt3support.target = sub-qt3support src_phonon.subdir = $$QT_SOURCE_TREE/src/phonon src_phonon.target = sub-phonon +src_multimedia.subdir = $$QT_SOURCE_TREE/src/multimedia +src_multimedia.target = sub-multimedia src_tools_uic3.subdir = $$QT_SOURCE_TREE/src/tools/uic3 src_tools_uic3.target = sub-uic3 src_activeqt.subdir = $$QT_SOURCE_TREE/src/activeqt @@ -109,6 +112,7 @@ src_webkit.target = sub-webkit src_testlib.depends = src_corelib src_qt3support.depends = src_gui src_xml src_network src_sql src_phonon.depends = src_gui + src_multimedia.depends = src_gui src_tools_uic3.depends = src_qt3support src_xml src_tools_idc.depends = src_corelib src_tools_activeqt.depends = src_tools_idc src_gui |