source: trunk/src/multimedia/audio/qaudioinput_win32_p.cpp@ 646

Last change on this file since 646 was 561, checked in by Dmitry A. Kuminov, 16 years ago

trunk: Merged in qt 4.6.1 sources.

  • Property svn:eol-style set to native
File size: 17.0 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtMultimedia module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
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** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
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
54#include "qaudioinput_win32_p.h"
55
56QT_BEGIN_NAMESPACE
57
58//#define DEBUG_AUDIO 1
59
60static CRITICAL_SECTION waveInCriticalSection;
61
62static const int minimumIntervalTime = 50;
63
64QAudioInputPrivate::QAudioInputPrivate(const QByteArray &device, const QAudioFormat& audioFormat):
65 settings(audioFormat)
66{
67 bytesAvailable = 0;
68 buffer_size = 0;
69 period_size = 0;
70 m_device = device;
71 totalTimeValue = 0;
72 intervalTime = 1000;
73 errorState = QAudio::NoError;
74 deviceState = QAudio::StoppedState;
75 audioSource = 0;
76 pullMode = true;
77 resuming = false;
78 finished = false;
79
80 connect(this,SIGNAL(processMore()),SLOT(deviceReady()));
81
82 InitializeCriticalSection(&waveInCriticalSection);
83}
84
85QAudioInputPrivate::~QAudioInputPrivate()
86{
87 stop();
88 DeleteCriticalSection(&waveInCriticalSection);
89}
90
91void CALLBACK QAudioInputPrivate::waveInProc( HWAVEIN hWaveIn, UINT uMsg,
92 DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 )
93{
94 Q_UNUSED(dwParam1)
95 Q_UNUSED(dwParam2)
96 Q_UNUSED(hWaveIn)
97
98 QAudioInputPrivate* qAudio;
99 qAudio = (QAudioInputPrivate*)(dwInstance);
100 if(!qAudio)
101 return;
102
103 switch(uMsg) {
104 case WIM_OPEN:
105 break;
106 case WIM_DATA:
107 EnterCriticalSection(&waveInCriticalSection);
108 if(qAudio->waveFreeBlockCount > 0)
109 qAudio->waveFreeBlockCount--;
110 qAudio->feedback();
111 LeaveCriticalSection(&waveInCriticalSection);
112 break;
113 case WIM_CLOSE:
114 EnterCriticalSection(&waveInCriticalSection);
115 qAudio->finished = true;
116 LeaveCriticalSection(&waveInCriticalSection);
117 break;
118 default:
119 return;
120 }
121}
122
123WAVEHDR* QAudioInputPrivate::allocateBlocks(int size, int count)
124{
125 int i;
126 unsigned char* buffer;
127 WAVEHDR* blocks;
128 DWORD totalBufferSize = (size + sizeof(WAVEHDR))*count;
129
130 if((buffer=(unsigned char*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,
131 totalBufferSize)) == 0) {
132 qWarning("QAudioInput: Memory allocation error");
133 return 0;
134 }
135 blocks = (WAVEHDR*)buffer;
136 buffer += sizeof(WAVEHDR)*count;
137 for(i = 0; i < count; i++) {
138 blocks[i].dwBufferLength = size;
139 blocks[i].lpData = (LPSTR)buffer;
140 blocks[i].dwBytesRecorded=0;
141 blocks[i].dwUser = 0L;
142 blocks[i].dwFlags = 0L;
143 blocks[i].dwLoops = 0L;
144 result = waveInPrepareHeader(hWaveIn,&blocks[i], sizeof(WAVEHDR));
145 if(result != MMSYSERR_NOERROR) {
146 qWarning("QAudioInput: Can't prepare block %d",i);
147 return 0;
148 }
149 buffer += size;
150 }
151 return blocks;
152}
153
154void QAudioInputPrivate::freeBlocks(WAVEHDR* blockArray)
155{
156 WAVEHDR* blocks = blockArray;
157
158 int count = buffer_size/period_size;
159
160 for(int i = 0; i < count; i++) {
161 waveInUnprepareHeader(hWaveIn,&blocks[i], sizeof(WAVEHDR));
162 blocks+=sizeof(WAVEHDR);
163 }
164 HeapFree(GetProcessHeap(), 0, blockArray);
165}
166
167QAudio::Error QAudioInputPrivate::error() const
168{
169 return errorState;
170}
171
172QAudio::State QAudioInputPrivate::state() const
173{
174 return deviceState;
175}
176
177QAudioFormat QAudioInputPrivate::format() const
178{
179 return settings;
180}
181
182QIODevice* QAudioInputPrivate::start(QIODevice* device)
183{
184 if(deviceState != QAudio::StoppedState)
185 close();
186
187 if(!pullMode && audioSource) {
188 delete audioSource;
189 }
190
191 if(device) {
192 //set to pull mode
193 pullMode = true;
194 audioSource = device;
195 } else {
196 //set to push mode
197 pullMode = false;
198 audioSource = new InputPrivate(this);
199 audioSource->open(QIODevice::ReadOnly | QIODevice::Unbuffered);
200 }
201
202 if( !open() )
203 return 0;
204
205 emit stateChanged(deviceState);
206
207 return audioSource;
208}
209
210void QAudioInputPrivate::stop()
211{
212 if(deviceState == QAudio::StoppedState)
213 return;
214
215 close();
216 emit stateChanged(deviceState);
217}
218
219bool QAudioInputPrivate::open()
220{
221#ifdef DEBUG_AUDIO
222 QTime now(QTime::currentTime());
223 qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()";
224#endif
225 header = 0;
226 if(buffer_size == 0) {
227 // Default buffer size, 100ms, default period size is 20ms
228 buffer_size = settings.frequency()*settings.channels()*(settings.sampleSize()/8)*0.1;
229 period_size = buffer_size/5;
230 } else {
231 period_size = buffer_size/5;
232 }
233 timeStamp.restart();
234 elapsedTimeOffset = 0;
235 wfx.nSamplesPerSec = settings.frequency();
236 wfx.wBitsPerSample = settings.sampleSize();
237 wfx.nChannels = settings.channels();
238 wfx.cbSize = 0;
239
240 wfx.wFormatTag = WAVE_FORMAT_PCM;
241 wfx.nBlockAlign = (wfx.wBitsPerSample >> 3) * wfx.nChannels;
242 wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec;
243
244 UINT_PTR devId = WAVE_MAPPER;
245
246 WAVEINCAPS wic;
247 unsigned long iNumDevs,ii;
248 iNumDevs = waveInGetNumDevs();
249 for(ii=0;ii<iNumDevs;ii++) {
250 if(waveInGetDevCaps(ii, &wic, sizeof(WAVEINCAPS))
251 == MMSYSERR_NOERROR) {
252 QString tmp;
253 tmp = QString::fromUtf16((const unsigned short*)wic.szPname);
254 if(tmp.compare(QLatin1String(m_device)) == 0) {
255 devId = ii;
256 break;
257 }
258 }
259 }
260
261 if(waveInOpen(&hWaveIn, devId, &wfx,
262 (DWORD_PTR)&waveInProc,
263 (DWORD_PTR) this,
264 CALLBACK_FUNCTION) != MMSYSERR_NOERROR) {
265 errorState = QAudio::OpenError;
266 deviceState = QAudio::StoppedState;
267 emit stateChanged(deviceState);
268 qWarning("QAudioInput: failed to open audio device");
269 return false;
270 }
271 waveBlocks = allocateBlocks(period_size, buffer_size/period_size);
272
273 if(waveBlocks == 0) {
274 errorState = QAudio::OpenError;
275 deviceState = QAudio::StoppedState;
276 emit stateChanged(deviceState);
277 qWarning("QAudioInput: failed to allocate blocks. open failed");
278 return false;
279 }
280
281 EnterCriticalSection(&waveInCriticalSection);
282 waveFreeBlockCount = buffer_size/period_size;
283 LeaveCriticalSection(&waveInCriticalSection);
284
285 waveCurrentBlock = 0;
286
287 for(int i=0; i<buffer_size/period_size; i++) {
288 result = waveInAddBuffer(hWaveIn, &waveBlocks[i], sizeof(WAVEHDR));
289 if(result != MMSYSERR_NOERROR) {
290 qWarning("QAudioInput: failed to setup block %d,err=%d",i,result);
291 errorState = QAudio::OpenError;
292 deviceState = QAudio::StoppedState;
293 emit stateChanged(deviceState);
294 return false;
295 }
296 }
297 result = waveInStart(hWaveIn);
298 if(result) {
299 qWarning("QAudioInput: failed to start audio input");
300 errorState = QAudio::OpenError;
301 deviceState = QAudio::StoppedState;
302 emit stateChanged(deviceState);
303 return false;
304 }
305 timeStampOpened.restart();
306 elapsedTimeOffset = 0;
307 totalTimeValue = 0;
308 errorState = QAudio::NoError;
309 deviceState = QAudio::ActiveState;
310 return true;
311}
312
313void QAudioInputPrivate::close()
314{
315 if(deviceState == QAudio::StoppedState)
316 return;
317
318 waveInReset(hWaveIn);
319 waveInClose(hWaveIn);
320 deviceState = QAudio::StoppedState;
321
322 int count = 0;
323 while(!finished && count < 500) {
324 count++;
325 Sleep(10);
326 }
327
328 EnterCriticalSection(&waveInCriticalSection);
329 for(int i=0; i<waveFreeBlockCount; i++) {
330 if(waveBlocks[i].dwFlags & WHDR_PREPARED)
331 waveInUnprepareHeader(hWaveIn,&waveBlocks[i],sizeof(WAVEHDR));
332 }
333 LeaveCriticalSection(&waveInCriticalSection);
334 freeBlocks(waveBlocks);
335}
336
337int QAudioInputPrivate::bytesReady() const
338{
339 if(period_size == 0 || buffer_size == 0)
340 return 0;
341
342 int buf = ((buffer_size/period_size)-waveFreeBlockCount)*period_size;
343 if(buf < 0)
344 buf = 0;
345 return buf;
346}
347
348qint64 QAudioInputPrivate::read(char* data, qint64 len)
349{
350 bool done = false;
351
352 char* p = data;
353 qint64 l = 0;
354 qint64 written = 0;
355
356 while(!done) {
357 // Read in some audio data
358 if(waveBlocks[header].dwBytesRecorded > 0 && waveBlocks[header].dwFlags & WHDR_DONE) {
359 if(pullMode) {
360 l = audioSource->write(waveBlocks[header].lpData,
361 waveBlocks[header].dwBytesRecorded);
362#ifdef DEBUG_AUDIO
363 qDebug()<<"IN: "<<waveBlocks[header].dwBytesRecorded<<", OUT: "<<l;
364#endif
365 if(l < 0) {
366 // error
367 qWarning("QAudioInput: IOError");
368 errorState = QAudio::IOError;
369
370 } else if(l == 0) {
371 // cant write to IODevice
372 qWarning("QAudioInput: IOError, can't write to QIODevice");
373 errorState = QAudio::IOError;
374
375 } else {
376 totalTimeValue += waveBlocks[header].dwBytesRecorded
377 /((settings.channels()*settings.sampleSize()/8))
378 *10000/settings.frequency()*100;
379 errorState = QAudio::NoError;
380 deviceState = QAudio::ActiveState;
381 resuming = false;
382 }
383 } else {
384 // push mode
385 memcpy(p,waveBlocks[header].lpData,waveBlocks[header].dwBytesRecorded);
386 l = waveBlocks[header].dwBytesRecorded;
387#ifdef DEBUG_AUDIO
388 qDebug()<<"IN: "<<waveBlocks[header].dwBytesRecorded<<", OUT: "<<l;
389#endif
390 totalTimeValue += waveBlocks[header].dwBytesRecorded
391 /((settings.channels()*settings.sampleSize()/8))
392 *10000/settings.frequency()*100;
393 errorState = QAudio::NoError;
394 deviceState = QAudio::ActiveState;
395 resuming = false;
396 }
397 } else {
398 //no data, not ready yet, next time
399 return 0;
400 }
401
402 waveInUnprepareHeader(hWaveIn,&waveBlocks[header], sizeof(WAVEHDR));
403
404 EnterCriticalSection(&waveInCriticalSection);
405 waveFreeBlockCount++;
406 LeaveCriticalSection(&waveInCriticalSection);
407 waveBlocks[header].dwBytesRecorded=0;
408 waveBlocks[header].dwFlags = 0L;
409 result = waveInPrepareHeader(hWaveIn,&waveBlocks[header], sizeof(WAVEHDR));
410 if(result != MMSYSERR_NOERROR) {
411 result = waveInPrepareHeader(hWaveIn,&waveBlocks[header], sizeof(WAVEHDR));
412 qWarning("QAudioInput: failed to prepare block %d,err=%d",header,result);
413 errorState = QAudio::IOError;
414 EnterCriticalSection(&waveInCriticalSection);
415 waveFreeBlockCount--;
416 LeaveCriticalSection(&waveInCriticalSection);
417 return 0;
418 }
419 result = waveInAddBuffer(hWaveIn, &waveBlocks[header], sizeof(WAVEHDR));
420 if(result != MMSYSERR_NOERROR) {
421 qWarning("QAudioInput: failed to setup block %d,err=%d",header,result);
422 errorState = QAudio::IOError;
423 EnterCriticalSection(&waveInCriticalSection);
424 waveFreeBlockCount--;
425 LeaveCriticalSection(&waveInCriticalSection);
426 return 0;
427 }
428 header++;
429 if(header >= buffer_size/period_size)
430 header = 0;
431 p+=l;
432
433 EnterCriticalSection(&waveInCriticalSection);
434 if(!pullMode) {
435 if(l+period_size > len && waveFreeBlockCount == buffer_size/period_size)
436 done = true;
437 } else {
438 if(waveFreeBlockCount == buffer_size/period_size)
439 done = true;
440 }
441 LeaveCriticalSection(&waveInCriticalSection);
442
443 written+=l;
444 }
445#ifdef DEBUG_AUDIO
446 qDebug()<<"read in len="<<written;
447#endif
448 return written;
449}
450
451void QAudioInputPrivate::resume()
452{
453 if(deviceState == QAudio::SuspendedState) {
454 deviceState = QAudio::ActiveState;
455 for(int i=0; i<buffer_size/period_size; i++) {
456 result = waveInAddBuffer(hWaveIn, &waveBlocks[i], sizeof(WAVEHDR));
457 if(result != MMSYSERR_NOERROR) {
458 qWarning("QAudioInput: failed to setup block %d,err=%d",i,result);
459 errorState = QAudio::OpenError;
460 deviceState = QAudio::StoppedState;
461 emit stateChanged(deviceState);
462 return;
463 }
464 }
465 EnterCriticalSection(&waveInCriticalSection);
466 waveFreeBlockCount = buffer_size/period_size;
467 LeaveCriticalSection(&waveInCriticalSection);
468
469 waveCurrentBlock = 0;
470 header = 0;
471 resuming = true;
472 waveInStart(hWaveIn);
473 QTimer::singleShot(20,this,SLOT(feedback()));
474 emit stateChanged(deviceState);
475 }
476}
477
478void QAudioInputPrivate::setBufferSize(int value)
479{
480 buffer_size = value;
481}
482
483int QAudioInputPrivate::bufferSize() const
484{
485 return buffer_size;
486}
487
488int QAudioInputPrivate::periodSize() const
489{
490 return period_size;
491}
492
493void QAudioInputPrivate::setNotifyInterval(int ms)
494{
495 if(ms >= minimumIntervalTime)
496 intervalTime = ms;
497 else
498 intervalTime = minimumIntervalTime;
499}
500
501int QAudioInputPrivate::notifyInterval() const
502{
503 return intervalTime;
504}
505
506qint64 QAudioInputPrivate::processedUSecs() const
507{
508 return totalTimeValue;
509}
510
511void QAudioInputPrivate::suspend()
512{
513 if(deviceState == QAudio::ActiveState) {
514 waveInReset(hWaveIn);
515 deviceState = QAudio::SuspendedState;
516 emit stateChanged(deviceState);
517 }
518}
519
520void QAudioInputPrivate::feedback()
521{
522#ifdef DEBUG_AUDIO
523 QTime now(QTime::currentTime());
524 qDebug()<<now.second()<<"s "<<now.msec()<<"ms :feedback() INPUT "<<this;
525#endif
526 bytesAvailable = bytesReady();
527
528 if(!(deviceState==QAudio::StoppedState||deviceState==QAudio::SuspendedState))
529 emit processMore();
530}
531
532bool QAudioInputPrivate::deviceReady()
533{
534#ifdef DEBUG_AUDIO
535 QTime now(QTime::currentTime());
536 qDebug()<<now.second()<<"s "<<now.msec()<<"ms :deviceReady() INPUT";
537#endif
538 if(pullMode) {
539 // reads some audio data and writes it to QIODevice
540 read(0,0);
541 } else {
542 // emits readyRead() so user will call read() on QIODevice to get some audio data
543 InputPrivate* a = qobject_cast<InputPrivate*>(audioSource);
544 a->trigger();
545 }
546 if(deviceState != QAudio::ActiveState)
547 return true;
548
549 if((timeStamp.elapsed() + elapsedTimeOffset) > intervalTime) {
550 emit notify();
551 elapsedTimeOffset = timeStamp.elapsed() + elapsedTimeOffset - intervalTime;
552 timeStamp.restart();
553 }
554 return true;
555}
556
557qint64 QAudioInputPrivate::elapsedUSecs() const
558{
559 if (deviceState == QAudio::StoppedState)
560 return 0;
561
562 return timeStampOpened.elapsed()*1000;
563}
564
565void QAudioInputPrivate::reset()
566{
567 close();
568}
569
570InputPrivate::InputPrivate(QAudioInputPrivate* audio)
571{
572 audioDevice = qobject_cast<QAudioInputPrivate*>(audio);
573}
574
575InputPrivate::~InputPrivate() {}
576
577qint64 InputPrivate::readData( char* data, qint64 len)
578{
579 // push mode, user read() called
580 if(audioDevice->deviceState != QAudio::ActiveState)
581 return 0;
582 // Read in some audio data
583 return audioDevice->read(data,len);
584}
585
586qint64 InputPrivate::writeData(const char* data, qint64 len)
587{
588 Q_UNUSED(data)
589 Q_UNUSED(len)
590
591 emit readyRead();
592 return 0;
593}
594
595void InputPrivate::trigger()
596{
597 emit readyRead();
598}
599
600QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.