qtmobility/src/multimedia/audio/qaudiooutput_win32_p.cpp
changeset 14 6fbed849b4f4
equal deleted inserted replaced
11:06b8e2af4411 14:6fbed849b4f4
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the Qt Mobility Components.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 //
       
    43 //  W A R N I N G
       
    44 //  -------------
       
    45 //
       
    46 // This file is not part of the Qt API.  It exists for the convenience
       
    47 // of other Qt classes.  This header file may change from version to
       
    48 // version without notice, or even be removed.
       
    49 //
       
    50 // We mean it.
       
    51 //
       
    52 
       
    53 #include "qaudiooutput_win32_p.h"
       
    54 
       
    55 //#define DEBUG_AUDIO 1
       
    56 
       
    57 QT_BEGIN_NAMESPACE
       
    58 
       
    59 QAudioOutputPrivate::QAudioOutputPrivate(const QByteArray &device)
       
    60 {
       
    61     bytesAvailable = 0;
       
    62     buffer_size = 0;
       
    63     period_size = 0;
       
    64     m_device = device;
       
    65     totalTimeValue = 0;
       
    66     intervalTime = 1000;
       
    67     audioBuffer = 0;
       
    68     errorState = QAudio::NoError;
       
    69     deviceState = QAudio::StoppedState;
       
    70     audioSource = 0;
       
    71     pullMode = true;
       
    72     finished = false;
       
    73 }
       
    74 
       
    75 QAudioOutputPrivate::~QAudioOutputPrivate()
       
    76 {
       
    77     mutex.lock();
       
    78     finished = true;
       
    79     mutex.unlock();
       
    80 
       
    81     close();
       
    82 }
       
    83 
       
    84 void CALLBACK QAudioOutputPrivate::waveOutProc( HWAVEOUT hWaveOut, UINT uMsg,
       
    85         DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 )
       
    86 {
       
    87     Q_UNUSED(dwParam1)
       
    88     Q_UNUSED(dwParam2)
       
    89     Q_UNUSED(hWaveOut)
       
    90 
       
    91     QAudioOutputPrivate* qAudio;
       
    92     qAudio = (QAudioOutputPrivate*)(dwInstance);
       
    93     if(!qAudio)
       
    94         return;
       
    95 
       
    96     QMutexLocker(&qAudio->mutex);
       
    97 
       
    98     switch(uMsg) {
       
    99         case WOM_OPEN:
       
   100             qAudio->feedback();
       
   101             break;
       
   102         case WOM_CLOSE:
       
   103             return;
       
   104         case WOM_DONE:
       
   105             if(qAudio->finished || qAudio->buffer_size == 0 || qAudio->period_size == 0) {
       
   106                 return;
       
   107 	    }
       
   108             qAudio->waveFreeBlockCount++;
       
   109             if(qAudio->waveFreeBlockCount >= qAudio->buffer_size/qAudio->period_size)
       
   110                 qAudio->waveFreeBlockCount = qAudio->buffer_size/qAudio->period_size;
       
   111             qAudio->feedback();
       
   112             break;
       
   113         default:
       
   114             return;
       
   115     }
       
   116 }
       
   117 
       
   118 WAVEHDR* QAudioOutputPrivate::allocateBlocks(int size, int count)
       
   119 {
       
   120     int i;
       
   121     unsigned char* buffer;
       
   122     WAVEHDR* blocks;
       
   123     DWORD totalBufferSize = (size + sizeof(WAVEHDR))*count;
       
   124 
       
   125     if((buffer=(unsigned char*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,
       
   126                     totalBufferSize)) == 0) {
       
   127         qWarning("QAudioOutput: Memory allocation error");
       
   128         return 0;
       
   129     }
       
   130     blocks = (WAVEHDR*)buffer;
       
   131     buffer += sizeof(WAVEHDR)*count;
       
   132     for(i = 0; i < count; i++) {
       
   133         blocks[i].dwBufferLength = size;
       
   134         blocks[i].lpData = (LPSTR)buffer;
       
   135         buffer += size;
       
   136     }
       
   137     return blocks;
       
   138 }
       
   139 
       
   140 void QAudioOutputPrivate::freeBlocks(WAVEHDR* blockArray)
       
   141 {
       
   142     WAVEHDR* blocks = blockArray;
       
   143 
       
   144     int count = buffer_size/period_size;
       
   145 
       
   146     for(int i = 0; i < count; i++) {
       
   147         waveOutUnprepareHeader(hWaveOut,blocks, sizeof(WAVEHDR));
       
   148         blocks+=sizeof(WAVEHDR);
       
   149     }
       
   150     HeapFree(GetProcessHeap(), 0, blockArray);
       
   151 }
       
   152 
       
   153 QAudioFormat QAudioOutputPrivate::format() const
       
   154 {
       
   155     return settings;
       
   156 }
       
   157 
       
   158 void QAudioOutputPrivate::setFormat(const QAudioFormat& fmt)
       
   159 {
       
   160     if (deviceState == QAudio::StoppedState)
       
   161         settings = fmt;
       
   162 }
       
   163 
       
   164 void QAudioOutputPrivate::start(QIODevice* device)
       
   165 {
       
   166     if(deviceState != QAudio::StoppedState)
       
   167         close();
       
   168 
       
   169     if(!pullMode && audioSource)
       
   170         delete audioSource;
       
   171 
       
   172     pullMode = true;
       
   173     audioSource = device;
       
   174 
       
   175     deviceState = QAudio::ActiveState;
       
   176 
       
   177     if(!open())
       
   178         return;
       
   179 
       
   180     emit stateChanged(deviceState);
       
   181 }
       
   182 
       
   183 QIODevice* QAudioOutputPrivate::start()
       
   184 {
       
   185     if(deviceState != QAudio::StoppedState)
       
   186         close();
       
   187 
       
   188     if(!pullMode && audioSource)
       
   189         delete audioSource;
       
   190 
       
   191     pullMode = false;
       
   192     audioSource = new OutputPrivate(this);
       
   193     audioSource->open(QIODevice::WriteOnly|QIODevice::Unbuffered);
       
   194 
       
   195     deviceState = QAudio::IdleState;
       
   196 
       
   197     if(!open())
       
   198         return 0;
       
   199 
       
   200     emit stateChanged(deviceState);
       
   201 
       
   202     return audioSource;
       
   203 }
       
   204 
       
   205 void QAudioOutputPrivate::stop()
       
   206 {
       
   207     if(deviceState == QAudio::StoppedState)
       
   208         return;
       
   209     close();
       
   210     if(!pullMode && audioSource) {
       
   211         delete audioSource;
       
   212         audioSource = 0;
       
   213     }
       
   214     emit stateChanged(deviceState);
       
   215 }
       
   216 
       
   217 bool QAudioOutputPrivate::open()
       
   218 {
       
   219 #ifdef DEBUG_AUDIO
       
   220     QTime now(QTime::currentTime());
       
   221     qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()";
       
   222 #endif
       
   223     if (!(settings.frequency() >= 8000 && settings.frequency() <= 48000)) {
       
   224         errorState = QAudio::OpenError;
       
   225         deviceState = QAudio::StoppedState;
       
   226         emit stateChanged(deviceState);
       
   227         qWarning("QAudioOutput: open error, frequency out of range.");
       
   228         return false;
       
   229     }
       
   230     if(buffer_size == 0) {
       
   231         // Default buffer size, 200ms, default period size is 40ms
       
   232         buffer_size = settings.frequency()*settings.channels()*(settings.sampleSize()/8)*0.2;
       
   233 	period_size = buffer_size/5;
       
   234     } else {
       
   235         period_size = buffer_size/5;
       
   236     }
       
   237     waveBlocks = allocateBlocks(period_size, buffer_size/period_size);
       
   238 
       
   239     mutex.lock();
       
   240     waveFreeBlockCount = buffer_size/period_size;
       
   241     mutex.unlock();
       
   242 
       
   243     waveCurrentBlock = 0;
       
   244 
       
   245     if(audioBuffer == 0)
       
   246         audioBuffer = new char[buffer_size];
       
   247 
       
   248     timeStamp.restart();
       
   249     elapsedTimeOffset = 0;
       
   250 
       
   251     wfx.nSamplesPerSec = settings.frequency();
       
   252     wfx.wBitsPerSample = settings.sampleSize();
       
   253     wfx.nChannels = settings.channels();
       
   254     wfx.cbSize = 0;
       
   255 
       
   256     wfx.wFormatTag = WAVE_FORMAT_PCM;
       
   257     wfx.nBlockAlign = (wfx.wBitsPerSample >> 3) * wfx.nChannels;
       
   258     wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec;
       
   259 
       
   260     UINT_PTR devId = WAVE_MAPPER;
       
   261 
       
   262     WAVEOUTCAPS woc;
       
   263     unsigned long iNumDevs,ii;
       
   264     iNumDevs = waveOutGetNumDevs();
       
   265     for(ii=0;ii<iNumDevs;ii++) {
       
   266         if(waveOutGetDevCaps(ii, &woc, sizeof(WAVEOUTCAPS))
       
   267 	    == MMSYSERR_NOERROR) {
       
   268 	    QString tmp;
       
   269             tmp = QString::fromWCharArray(woc.szPname);
       
   270             if(tmp.compare(QLatin1String(m_device)) == 0) {
       
   271 	        devId = ii;
       
   272 		break;
       
   273 	    }
       
   274 	}
       
   275     }
       
   276 
       
   277     if(waveOutOpen(&hWaveOut, devId, &wfx,
       
   278                 (DWORD_PTR)&waveOutProc,
       
   279                 (DWORD_PTR) this,
       
   280                 CALLBACK_FUNCTION) != MMSYSERR_NOERROR) {
       
   281         errorState = QAudio::OpenError;
       
   282         deviceState = QAudio::StoppedState;
       
   283         emit stateChanged(deviceState);
       
   284         qWarning("QAudioOutput: open error");
       
   285         return false;
       
   286     }
       
   287 
       
   288     totalTimeValue = 0;
       
   289     timeStampOpened.restart();
       
   290     elapsedTimeOffset = 0;
       
   291 
       
   292     errorState = QAudio::NoError;
       
   293     if(pullMode) {
       
   294         deviceState = QAudio::ActiveState;
       
   295         QTimer::singleShot(10, this, SLOT(feedback()));
       
   296     } else
       
   297         deviceState = QAudio::IdleState;
       
   298 
       
   299     return true;
       
   300 }
       
   301 
       
   302 void QAudioOutputPrivate::close()
       
   303 {
       
   304     if(deviceState == QAudio::StoppedState)
       
   305         return;
       
   306 
       
   307     deviceState = QAudio::StoppedState;
       
   308     errorState = QAudio::NoError;
       
   309     int delay = (buffer_size-bytesFree())*1000/(settings.frequency()
       
   310                   *settings.channels()*(settings.sampleSize()/8));
       
   311     waveOutReset(hWaveOut);
       
   312     Sleep(delay+10);
       
   313 
       
   314     freeBlocks(waveBlocks);
       
   315     waveOutClose(hWaveOut);
       
   316     delete [] audioBuffer;
       
   317     audioBuffer = 0;
       
   318     buffer_size = 0;
       
   319 }
       
   320 
       
   321 int QAudioOutputPrivate::bytesFree() const
       
   322 {
       
   323     int buf;
       
   324     buf = waveFreeBlockCount*period_size;
       
   325 
       
   326     return buf;
       
   327 }
       
   328 
       
   329 int QAudioOutputPrivate::periodSize() const
       
   330 {
       
   331     return period_size;
       
   332 }
       
   333 
       
   334 void QAudioOutputPrivate::setBufferSize(int value)
       
   335 {
       
   336     if(deviceState == QAudio::StoppedState)
       
   337         buffer_size = value;
       
   338 }
       
   339 
       
   340 int QAudioOutputPrivate::bufferSize() const
       
   341 {
       
   342     return buffer_size;
       
   343 }
       
   344 
       
   345 void QAudioOutputPrivate::setNotifyInterval(int ms)
       
   346 {
       
   347     intervalTime = qMax(0, ms);
       
   348 }
       
   349 
       
   350 int QAudioOutputPrivate::notifyInterval() const
       
   351 {
       
   352     return intervalTime;
       
   353 }
       
   354 
       
   355 qint64 QAudioOutputPrivate::processedUSecs() const
       
   356 {
       
   357     if (deviceState == QAudio::StoppedState)
       
   358         return 0;
       
   359     qint64 result = qint64(1000000) * totalTimeValue /
       
   360         (settings.channels()*(settings.sampleSize()/8)) /
       
   361         settings.frequency();
       
   362 
       
   363     return result;
       
   364 }
       
   365 
       
   366 qint64 QAudioOutputPrivate::write( const char *data, qint64 len )
       
   367 {
       
   368     // Write out some audio data
       
   369     if (deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState)
       
   370         return 0;
       
   371 
       
   372     char* p = (char*)data;
       
   373     int l = (int)len;
       
   374 
       
   375     WAVEHDR* current;
       
   376     int remain;
       
   377     current = &waveBlocks[waveCurrentBlock];
       
   378     while(l > 0) {
       
   379         mutex.lock();
       
   380         if(waveFreeBlockCount==0) {
       
   381             mutex.unlock();
       
   382             break;
       
   383         }
       
   384         mutex.unlock();
       
   385 
       
   386         if(current->dwFlags & WHDR_PREPARED)
       
   387             waveOutUnprepareHeader(hWaveOut, current, sizeof(WAVEHDR));
       
   388 
       
   389         if(l < period_size)
       
   390             remain = l;
       
   391         else
       
   392             remain = period_size;
       
   393         memcpy(current->lpData, p, remain);
       
   394 
       
   395         l -= remain;
       
   396         p += remain;
       
   397         current->dwBufferLength = remain;
       
   398         waveOutPrepareHeader(hWaveOut, current, sizeof(WAVEHDR));
       
   399         waveOutWrite(hWaveOut, current, sizeof(WAVEHDR));
       
   400 
       
   401         mutex.lock();
       
   402         waveFreeBlockCount--;
       
   403 #ifdef DEBUG_AUDIO
       
   404         qDebug("write out l=%d, waveFreeBlockCount=%d",
       
   405                 current->dwBufferLength,waveFreeBlockCount);
       
   406 #endif
       
   407         mutex.unlock();
       
   408 
       
   409         totalTimeValue += current->dwBufferLength;
       
   410         waveCurrentBlock++;
       
   411         waveCurrentBlock %= buffer_size/period_size;
       
   412         current = &waveBlocks[waveCurrentBlock];
       
   413         current->dwUser = 0;
       
   414         errorState = QAudio::NoError;
       
   415         if (deviceState != QAudio::ActiveState) {
       
   416             deviceState = QAudio::ActiveState;
       
   417             emit stateChanged(deviceState);
       
   418         }
       
   419     }
       
   420     return (len-l);
       
   421 }
       
   422 
       
   423 void QAudioOutputPrivate::resume()
       
   424 {
       
   425     if(deviceState == QAudio::SuspendedState) {
       
   426         deviceState = QAudio::ActiveState;
       
   427         errorState = QAudio::NoError;
       
   428         waveOutRestart(hWaveOut);
       
   429         QTimer::singleShot(10, this, SLOT(feedback()));
       
   430         emit stateChanged(deviceState);
       
   431     }
       
   432 }
       
   433 
       
   434 void QAudioOutputPrivate::suspend()
       
   435 {
       
   436     if(deviceState == QAudio::ActiveState || deviceState == QAudio::IdleState) {
       
   437         int delay = (buffer_size-bytesFree())*1000/(settings.frequency()
       
   438                 *settings.channels()*(settings.sampleSize()/8));
       
   439         waveOutPause(hWaveOut);
       
   440         Sleep(delay+10);
       
   441         deviceState = QAudio::SuspendedState;
       
   442         errorState = QAudio::NoError;
       
   443         emit stateChanged(deviceState);
       
   444     }
       
   445 }
       
   446 
       
   447 void QAudioOutputPrivate::feedback()
       
   448 {
       
   449 #ifdef DEBUG_AUDIO
       
   450     QTime now(QTime::currentTime());
       
   451     qDebug()<<now.second()<<"s "<<now.msec()<<"ms :feedback()";
       
   452 #endif
       
   453     bytesAvailable = bytesFree();
       
   454 
       
   455     if(!(deviceState==QAudio::StoppedState||deviceState==QAudio::SuspendedState)) {
       
   456         if(bytesAvailable >= period_size)
       
   457             QMetaObject::invokeMethod(this, "deviceReady", Qt::QueuedConnection);
       
   458     }
       
   459 }
       
   460 
       
   461 bool QAudioOutputPrivate::deviceReady()
       
   462 {
       
   463     if(deviceState == QAudio::StoppedState || deviceState == QAudio::SuspendedState)
       
   464         return false;
       
   465 
       
   466     if(pullMode) {
       
   467         int chunks = bytesAvailable/period_size;
       
   468 #ifdef DEBUG_AUDIO
       
   469         qDebug()<<"deviceReady() avail="<<bytesAvailable<<" bytes, period size="<<period_size<<" bytes";
       
   470         qDebug()<<"deviceReady() no. of chunks that can fit ="<<chunks<<", chunks in bytes ="<<chunks*period_size;
       
   471 #endif
       
   472         bool startup = false;
       
   473         if(totalTimeValue == 0)
       
   474 	    startup = true;
       
   475 
       
   476 	bool full=false;
       
   477 
       
   478         mutex.lock();
       
   479 	if(waveFreeBlockCount==0) full = true;
       
   480         mutex.unlock();
       
   481 
       
   482 	if (full){
       
   483 #ifdef DEBUG_AUDIO
       
   484             qDebug() << "Skipping data as unable to write";
       
   485 #endif
       
   486 	    if((timeStamp.elapsed() + elapsedTimeOffset) > intervalTime ) {
       
   487                 emit notify();
       
   488 		elapsedTimeOffset = timeStamp.elapsed() + elapsedTimeOffset - intervalTime;
       
   489 		timeStamp.restart();
       
   490 	    }
       
   491 	    return true;
       
   492 	}
       
   493 
       
   494         if(startup)
       
   495 	    waveOutPause(hWaveOut);
       
   496         int input = period_size*chunks;
       
   497         int l = audioSource->read(audioBuffer,input);
       
   498         if(l > 0) {
       
   499             int out= write(audioBuffer,l);
       
   500             if(out > 0) {
       
   501                 if (deviceState != QAudio::ActiveState) {
       
   502                     deviceState = QAudio::ActiveState;
       
   503                     emit stateChanged(deviceState);
       
   504                 }
       
   505             }
       
   506             if ( out < l) {
       
   507                 // Didnt write all data
       
   508                 audioSource->seek(audioSource->pos()-(l-out));
       
   509             }
       
   510 	    if(startup)
       
   511 	        waveOutRestart(hWaveOut);
       
   512         } else if(l == 0) {
       
   513             bytesAvailable = bytesFree();
       
   514 
       
   515             int check = 0;
       
   516 
       
   517             mutex.lock();
       
   518             check = waveFreeBlockCount;
       
   519             mutex.unlock();
       
   520 
       
   521             if(check == buffer_size/period_size) {
       
   522                 if (deviceState != QAudio::IdleState) {
       
   523                     errorState = QAudio::UnderrunError;
       
   524                     deviceState = QAudio::IdleState;
       
   525                     emit stateChanged(deviceState);
       
   526                 }
       
   527             }
       
   528 
       
   529         } else if(l < 0) {
       
   530             bytesAvailable = bytesFree();
       
   531             if (errorState != QAudio::IOError)
       
   532                 errorState = QAudio::IOError;
       
   533         }
       
   534     } else {
       
   535         int buffered;
       
   536 
       
   537         mutex.lock();
       
   538 	buffered = waveFreeBlockCount;
       
   539         mutex.unlock();
       
   540 
       
   541         if (buffered >= buffer_size/period_size && deviceState == QAudio::ActiveState) {
       
   542             if (deviceState != QAudio::IdleState) {
       
   543                 errorState = QAudio::UnderrunError;
       
   544                 deviceState = QAudio::IdleState;
       
   545                 emit stateChanged(deviceState);
       
   546             }
       
   547         }
       
   548     }
       
   549     if(deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState)
       
   550         return true;
       
   551 
       
   552     if(intervalTime && (timeStamp.elapsed() + elapsedTimeOffset) > intervalTime) {
       
   553         emit notify();
       
   554 	elapsedTimeOffset = timeStamp.elapsed() + elapsedTimeOffset - intervalTime;
       
   555         timeStamp.restart();
       
   556     }
       
   557 
       
   558     return true;
       
   559 }
       
   560 
       
   561 qint64 QAudioOutputPrivate::elapsedUSecs() const
       
   562 {
       
   563     if (deviceState == QAudio::StoppedState)
       
   564         return 0;
       
   565 
       
   566     return timeStampOpened.elapsed()*1000;
       
   567 }
       
   568 
       
   569 QAudio::Error QAudioOutputPrivate::error() const
       
   570 {
       
   571     return errorState;
       
   572 }
       
   573 
       
   574 QAudio::State QAudioOutputPrivate::state() const
       
   575 {
       
   576     return deviceState;
       
   577 }
       
   578 
       
   579 void QAudioOutputPrivate::reset()
       
   580 {
       
   581     close();
       
   582 }
       
   583 
       
   584 OutputPrivate::OutputPrivate(QAudioOutputPrivate* audio)
       
   585 {
       
   586     audioDevice = qobject_cast<QAudioOutputPrivate*>(audio);
       
   587 }
       
   588 
       
   589 OutputPrivate::~OutputPrivate() {}
       
   590 
       
   591 qint64 OutputPrivate::readData( char* data, qint64 len)
       
   592 {
       
   593     Q_UNUSED(data)
       
   594     Q_UNUSED(len)
       
   595 
       
   596     return 0;
       
   597 }
       
   598 
       
   599 qint64 OutputPrivate::writeData(const char* data, qint64 len)
       
   600 {
       
   601     int retry = 0;
       
   602     qint64 written = 0;
       
   603 
       
   604     if((audioDevice->deviceState == QAudio::ActiveState)
       
   605             ||(audioDevice->deviceState == QAudio::IdleState)) {
       
   606         qint64 l = len;
       
   607         while(written < l) {
       
   608             int chunk = audioDevice->write(data+written,(l-written));
       
   609             if(chunk <= 0)
       
   610                 retry++;
       
   611             else
       
   612                 written+=chunk;
       
   613 
       
   614             if(retry > 10)
       
   615                 return written;
       
   616         }
       
   617         audioDevice->deviceState = QAudio::ActiveState;
       
   618     }
       
   619     return written;
       
   620 }
       
   621 
       
   622 QT_END_NAMESPACE