Qt 4.8
qaudioinput_win32_p.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtMultimedia module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
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 
54 #include "qaudioinput_win32_p.h"
55 
57 
58 //#define DEBUG_AUDIO 1
59 
60 QAudioInputPrivate::QAudioInputPrivate(const QByteArray &device, const QAudioFormat& audioFormat):
61  settings(audioFormat)
62 {
63  bytesAvailable = 0;
64  buffer_size = 0;
65  period_size = 0;
66  m_device = device;
67  totalTimeValue = 0;
68  intervalTime = 1000;
71  audioSource = 0;
72  pullMode = true;
73  resuming = false;
74  finished = false;
75 }
76 
78 {
79  stop();
80 }
81 
83  DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2 )
84 {
85  Q_UNUSED(dwParam1)
86  Q_UNUSED(dwParam2)
87  Q_UNUSED(hWaveIn)
88 
89  QAudioInputPrivate* qAudio;
90  qAudio = (QAudioInputPrivate*)(dwInstance);
91  if(!qAudio)
92  return;
93 
94  QMutexLocker(&qAudio->mutex);
95 
96  switch(uMsg) {
97  case WIM_OPEN:
98  break;
99  case WIM_DATA:
100  if(qAudio->waveFreeBlockCount > 0)
101  qAudio->waveFreeBlockCount--;
102  qAudio->feedback();
103  break;
104  case WIM_CLOSE:
105  qAudio->finished = true;
106  break;
107  default:
108  return;
109  }
110 }
111 
112 WAVEHDR* QAudioInputPrivate::allocateBlocks(int size, int count)
113 {
114  int i;
115  unsigned char* buffer;
116  WAVEHDR* blocks;
117  DWORD totalBufferSize = (size + sizeof(WAVEHDR))*count;
118 
119  if((buffer=(unsigned char*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,
120  totalBufferSize)) == 0) {
121  qWarning("QAudioInput: Memory allocation error");
122  return 0;
123  }
124  blocks = (WAVEHDR*)buffer;
125  buffer += sizeof(WAVEHDR)*count;
126  for(i = 0; i < count; i++) {
127  blocks[i].dwBufferLength = size;
128  blocks[i].lpData = (LPSTR)buffer;
129  blocks[i].dwBytesRecorded=0;
130  blocks[i].dwUser = 0L;
131  blocks[i].dwFlags = 0L;
132  blocks[i].dwLoops = 0L;
133  result = waveInPrepareHeader(hWaveIn,&blocks[i], sizeof(WAVEHDR));
134  if(result != MMSYSERR_NOERROR) {
135  qWarning("QAudioInput: Can't prepare block %d",i);
136  return 0;
137  }
138  buffer += size;
139  }
140  return blocks;
141 }
142 
143 void QAudioInputPrivate::freeBlocks(WAVEHDR* blockArray)
144 {
145  WAVEHDR* blocks = blockArray;
146 
147  int count = buffer_size/period_size;
148 
149  for(int i = 0; i < count; i++) {
150  waveInUnprepareHeader(hWaveIn,blocks, sizeof(WAVEHDR));
151  blocks++;
152  }
153  HeapFree(GetProcessHeap(), 0, blockArray);
154 }
155 
157 {
158  return errorState;
159 }
160 
162 {
163  return deviceState;
164 }
165 
167 {
168  return settings;
169 }
170 
172 {
174  close();
175 
176  if(!pullMode && audioSource) {
177  delete audioSource;
178  }
179 
180  if(device) {
181  //set to pull mode
182  pullMode = true;
183  audioSource = device;
185  } else {
186  //set to push mode
187  pullMode = false;
189  audioSource = new InputPrivate(this);
191  }
192 
193  if( !open() )
194  return 0;
195 
197 
198  return audioSource;
199 }
200 
202 {
204  return;
205 
206  close();
208 }
209 
211 {
212 #ifdef DEBUG_AUDIO
213  QTime now(QTime::currentTime());
214  qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()";
215 #endif
216  header = 0;
217  period_size = 0;
218 
219  if (!settings.isValid()) {
220  qWarning("QAudioInput: open error, invalid format.");
221  } else if (settings.channels() <= 0) {
222  qWarning("QAudioInput: open error, invalid number of channels (%d).",
223  settings.channels());
224  } else if (settings.sampleSize() <= 0) {
225  qWarning("QAudioInput: open error, invalid sample size (%d).",
226  settings.sampleSize());
227  } else if (settings.frequency() < 8000 || settings.frequency() > 96000) {
228  qWarning("QAudioInput: open error, frequency out of range (%d).", settings.frequency());
229  } else if (buffer_size == 0) {
230 
232  = (settings.frequency()
233  * settings.channels()
234  * settings.sampleSize()
235 #ifndef Q_OS_WINCE // Default buffer size, 200ms, default period size is 40ms
236  + 39) / 40;
237  period_size = buffer_size / 5;
238  } else {
239  period_size = buffer_size / 5;
240 #else // For wince reduce size to 40ms for buffer size and 20ms period
241  + 199) / 200;
242  period_size = buffer_size / 2;
243  } else {
244  period_size = buffer_size / 2;
245 #endif
246  }
247 
248  if (period_size == 0) {
252  return false;
253  }
254 
255  timeStamp.restart();
256  elapsedTimeOffset = 0;
257  wfx.nSamplesPerSec = settings.frequency();
258  wfx.wBitsPerSample = settings.sampleSize();
259  wfx.nChannels = settings.channels();
260  wfx.cbSize = 0;
261 
262  wfx.wFormatTag = WAVE_FORMAT_PCM;
263  wfx.nBlockAlign = (wfx.wBitsPerSample >> 3) * wfx.nChannels;
264  wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec;
265 
266  UINT_PTR devId = WAVE_MAPPER;
267 
268  WAVEINCAPS wic;
269  unsigned long iNumDevs,ii;
270  iNumDevs = waveInGetNumDevs();
271  for(ii=0;ii<iNumDevs;ii++) {
272  if(waveInGetDevCaps(ii, &wic, sizeof(WAVEINCAPS))
273  == MMSYSERR_NOERROR) {
274  QString tmp;
275  tmp = QString((const QChar *)wic.szPname);
276  if(tmp.compare(QLatin1String(m_device)) == 0) {
277  devId = ii;
278  break;
279  }
280  }
281  }
282 
283  if(waveInOpen(&hWaveIn, devId, &wfx,
285  (DWORD_PTR) this,
286  CALLBACK_FUNCTION) != MMSYSERR_NOERROR) {
290  qWarning("QAudioInput: failed to open audio device");
291  return false;
292  }
294 
295  if(waveBlocks == 0) {
299  qWarning("QAudioInput: failed to allocate blocks. open failed");
300  return false;
301  }
302 
303  mutex.lock();
305  mutex.unlock();
306 
307  waveCurrentBlock = 0;
308 
309  for(int i=0; i<buffer_size/period_size; i++) {
310  result = waveInAddBuffer(hWaveIn, &waveBlocks[i], sizeof(WAVEHDR));
311  if(result != MMSYSERR_NOERROR) {
312  qWarning("QAudioInput: failed to setup block %d,err=%d",i,result);
316  return false;
317  }
318  }
319  result = waveInStart(hWaveIn);
320  if(result) {
321  qWarning("QAudioInput: failed to start audio input");
325  return false;
326  }
328  elapsedTimeOffset = 0;
329  totalTimeValue = 0;
331  return true;
332 }
333 
335 {
337  return;
338 
340  waveInReset(hWaveIn);
341 
342  mutex.lock();
343  for (int i=0; i<waveFreeBlockCount; i++)
344  waveInUnprepareHeader(hWaveIn,&waveBlocks[i],sizeof(WAVEHDR));
346  mutex.unlock();
347 
348  waveInClose(hWaveIn);
349 
350  int count = 0;
351  while(!finished && count < 500) {
352  count++;
353  Sleep(10);
354  }
355 }
356 
358 {
359  if(period_size == 0 || buffer_size == 0)
360  return 0;
361 
363  if(buf < 0)
364  buf = 0;
365  return buf;
366 }
367 
369 {
370  bool done = false;
371 
372  char* p = data;
373  qint64 l = 0;
374  qint64 written = 0;
375  while(!done) {
376  // Read in some audio data
377  if(waveBlocks[header].dwBytesRecorded > 0 && waveBlocks[header].dwFlags & WHDR_DONE) {
378  if(pullMode) {
379  l = audioSource->write(waveBlocks[header].lpData,
380  waveBlocks[header].dwBytesRecorded);
381 #ifdef DEBUG_AUDIO
382  qDebug()<<"IN: "<<waveBlocks[header].dwBytesRecorded<<", OUT: "<<l;
383 #endif
384  if(l < 0) {
385  // error
386  qWarning("QAudioInput: IOError");
388 
389  } else if(l == 0) {
390  // cant write to IODevice
391  qWarning("QAudioInput: IOError, can't write to QIODevice");
393 
394  } else {
395  totalTimeValue += waveBlocks[header].dwBytesRecorded;
400  }
401  resuming = false;
402  }
403  } else {
404  l = qMin<qint64>(len, waveBlocks[header].dwBytesRecorded);
405  // push mode
406  memcpy(p, waveBlocks[header].lpData, l);
407 
408  len -= l;
409 
410 #ifdef DEBUG_AUDIO
411  qDebug()<<"IN: "<<waveBlocks[header].dwBytesRecorded<<", OUT: "<<l;
412 #endif
413  totalTimeValue += waveBlocks[header].dwBytesRecorded;
418  }
419  resuming = false;
420  }
421  } else {
422  //no data, not ready yet, next time
423  break;
424  }
425 
426  waveInUnprepareHeader(hWaveIn,&waveBlocks[header], sizeof(WAVEHDR));
427 
428  mutex.lock();
430  mutex.unlock();
431 
432  waveBlocks[header].dwBytesRecorded=0;
433  waveBlocks[header].dwFlags = 0L;
434  result = waveInPrepareHeader(hWaveIn,&waveBlocks[header], sizeof(WAVEHDR));
435  if(result != MMSYSERR_NOERROR) {
436  result = waveInPrepareHeader(hWaveIn,&waveBlocks[header], sizeof(WAVEHDR));
437  qWarning("QAudioInput: failed to prepare block %d,err=%d",header,result);
439 
440  mutex.lock();
442  mutex.unlock();
443 
444  return 0;
445  }
446  result = waveInAddBuffer(hWaveIn, &waveBlocks[header], sizeof(WAVEHDR));
447  if(result != MMSYSERR_NOERROR) {
448  qWarning("QAudioInput: failed to setup block %d,err=%d",header,result);
450 
451  mutex.lock();
453  mutex.unlock();
454 
455  return 0;
456  }
457  header++;
458  if(header >= buffer_size/period_size)
459  header = 0;
460  p+=l;
461 
462  mutex.lock();
463  if(!pullMode) {
465  done = true;
466  } else {
468  done = true;
469  }
470  mutex.unlock();
471 
472  written+=l;
473  }
474 #ifdef DEBUG_AUDIO
475  qDebug()<<"read in len="<<written;
476 #endif
477  return written;
478 }
479 
481 {
484  for(int i=0; i<buffer_size/period_size; i++) {
485  result = waveInAddBuffer(hWaveIn, &waveBlocks[i], sizeof(WAVEHDR));
486  if(result != MMSYSERR_NOERROR) {
487  qWarning("QAudioInput: failed to setup block %d,err=%d",i,result);
491  return;
492  }
493  }
494 
495  mutex.lock();
497  mutex.unlock();
498 
499  waveCurrentBlock = 0;
500  header = 0;
501  resuming = true;
502  waveInStart(hWaveIn);
503  QTimer::singleShot(20,this,SLOT(feedback()));
505  }
506 }
507 
508 void QAudioInputPrivate::setBufferSize(int value)
509 {
510  buffer_size = value;
511 }
512 
514 {
515  return buffer_size;
516 }
517 
519 {
520  return period_size;
521 }
522 
524 {
525  intervalTime = qMax(0, ms);
526 }
527 
529 {
530  return intervalTime;
531 }
532 
534 {
536  return 0;
537  qint64 result = qint64(1000000) * totalTimeValue /
540 
541  return result;
542 }
543 
545 {
547  waveInReset(hWaveIn);
550  }
551 }
552 
554 {
555 #ifdef DEBUG_AUDIO
556  QTime now(QTime::currentTime());
557  qDebug()<<now.second()<<"s "<<now.msec()<<"ms :feedback() INPUT "<<this;
558 #endif
560  QMetaObject::invokeMethod(this, "deviceReady", Qt::QueuedConnection);
561 }
562 
564 {
566 #ifdef DEBUG_AUDIO
567  QTime now(QTime::currentTime());
568  qDebug()<<now.second()<<"s "<<now.msec()<<"ms :deviceReady() INPUT";
569 #endif
571  return true;
572 
573  if(pullMode) {
574  // reads some audio data and writes it to QIODevice
575  read(0, buffer_size);
576  } else {
577  // emits readyRead() so user will call read() on QIODevice to get some audio data
579  a->trigger();
580  }
581 
583  emit notify();
585  timeStamp.restart();
586  }
587  return true;
588 }
589 
591 {
593  return 0;
594 
595  return timeStampOpened.elapsed()*1000;
596 }
597 
599 {
600  close();
601 }
602 
604 {
605  audioDevice = qobject_cast<QAudioInputPrivate*>(audio);
606 }
607 
609 
611 {
612  // push mode, user read() called
613  if(audioDevice->deviceState != QAudio::ActiveState &&
614  audioDevice->deviceState != QAudio::IdleState)
615  return 0;
616  // Read in some audio data
617  return audioDevice->read(data,len);
618 }
619 
620 qint64 InputPrivate::writeData(const char* data, qint64 len)
621 {
622  Q_UNUSED(data)
623  Q_UNUSED(len)
624 
625  emit readyRead();
626  return 0;
627 }
628 
630 {
631  emit readyRead();
632 }
633 
Error
Definition: qaudio.h:58
QAudioFormat format() const
Returns the QAudioFormat being used.
WAVEHDR * allocateBlocks(int size, int count)
void resume()
Resumes processing audio data after a suspend().
void suspend()
Stops processing audio data, preserving buffered audio data.
int frequency() const
Use sampleRate() instead.
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
void lock()
Locks the mutex.
Definition: qmutex.cpp:151
qint64 processedUSecs() const
Returns the amount of audio data processed since start() was called in milliseconds.
int bufferSize() const
Returns the audio buffer size in milliseconds.
void stop()
Stops the audio input.
void setNotifyInterval(int milliSeconds)
Sets the interval for notify() signal to be emitted.
InputPrivate(QAudioInputPrivate *audio)
The QByteArray class provides an array of bytes.
Definition: qbytearray.h:135
QIODevice * start(QIODevice *device=0)
Uses the device as the QIODevice to transfer data.
#define SLOT(a)
Definition: qobjectdefs.h:226
int msec() const
Returns the millisecond part (0 to 999) of the time.
Definition: qdatetime.cpp:1611
long ASN1_INTEGER_get ASN1_INTEGER * a
The QString class provides a Unicode character string.
Definition: qstring.h:83
T * qobject_cast(QObject *object)
Definition: qobject.h:375
int sampleSize() const
Returns the current sample size value.
int bytesReady() const
Returns the amount of audio data available to read in bytes.
The QChar class provides a 16-bit Unicode character.
Definition: qchar.h:72
qint64 elapsed() const
Returns the number of milliseconds since this QElapsedTimer was last started.
Q_DECL_CONSTEXPR const T & qMax(const T &a, const T &b)
Definition: qglobal.h:1217
int restart()
Sets this time to the current time and returns the number of milliseconds that have elapsed since the...
Definition: qdatetime.cpp:2095
void setBufferSize(int value)
Sets the audio buffer size to value in milliseconds.
void stateChanged(QAudio::State)
This signal is emitted when the device state has changed.
Q_CORE_EXPORT void qDebug(const char *,...)
QAudio::State state() const
Returns the state of audio processing.
#define QT_WIN_CALLBACK
Definition: qglobal.h:1178
The QTime class provides clock time functions.
Definition: qdatetime.h:148
qint64 read(char *data, qint64 len)
int notifyInterval() const
Returns the notify interval in milliseconds.
#define QT_BEGIN_NAMESPACE
This macro expands to.
Definition: qglobal.h:89
qint64 restart()
Restarts the timer and returns the time elapsed since the previous start.
QAudio::Error errorState
void freeBlocks(WAVEHDR *blockArray)
qint64 elapsedUSecs() const
Returns the milliseconds since start() was called, including time in Idle and suspend states...
#define emit
Definition: qobjectdefs.h:76
Q_CORE_EXPORT void qWarning(const char *,...)
int second() const
Returns the second part (0 to 59) of the time.
Definition: qdatetime.cpp:1600
static const char * data(const QByteArray &arr)
The QLatin1String class provides a thin wrapper around an US-ASCII/Latin-1 encoded string literal...
Definition: qstring.h:654
void reset()
Drops all audio data in the buffers, resets buffers to zero.
#define DWORD_PTR
__int64 qint64
Definition: qglobal.h:942
static void QT_WIN_CALLBACK waveInProc(HWAVEIN hWaveIn, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
bool isValid() const
Returns true if all of the parameters are valid.
void unlock()
Unlocks the mutex.
Definition: qmutex.cpp:296
int elapsed() const
Returns the number of milliseconds that have elapsed since the last time start() or restart() was cal...
Definition: qdatetime.cpp:2123
void notify()
This signal is emitted when x ms of audio data has been processed the interval set by setNotifyInterv...
The QMutexLocker class is a convenience class that simplifies locking and unlocking mutexes...
Definition: qmutex.h:101
int compare(const QString &s) const
Definition: qstring.cpp:5037
State
Definition: qaudio.h:59
static QTime currentTime()
Returns the current time as reported by the system clock.
Definition: qdatetime.cpp:3125
#define WAVE_FORMAT_PCM
volatile int waveFreeBlockCount
bool singleShot
This static function calls a slot after a given time interval.
Definition: qtimer.h:59
QFactoryLoader * l
virtual bool open(OpenMode mode)
Opens the device and sets its OpenMode to mode.
Definition: qiodevice.cpp:570
int periodSize() const
Returns the period size in bytes.
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(0), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
Invokes the member (a signal or a slot name) on the object obj.
qint64 writeData(const char *data, qint64 len)
Writes up to maxSize bytes from data to the device.
QAudio::Error error() const
Returns the error state.
The QAudioFormat class stores audio parameter information.
Definition: qaudioformat.h:60
qint64 readData(char *data, qint64 len)
Reads up to maxSize bytes from the device into data, and returns the number of bytes read or -1 if an...
QAudio::State deviceState
qint64 write(const char *data, qint64 len)
Writes at most maxSize bytes of data from data to the device.
Definition: qiodevice.cpp:1342
The QIODevice class is the base interface class of all I/O devices in Qt.
Definition: qiodevice.h:66
#define Q_UNUSED(x)
Indicates to the compiler that the parameter with the specified name is not used in the body of a fun...
Definition: qglobal.h:1729
QAudioInputPrivate(const QByteArray &device, const QAudioFormat &audioFormat)
int channels() const
Use channelCount() instead.