114 , m_internalState(SymbianAudio::ClosedState) |
114 , m_internalState(SymbianAudio::ClosedState) |
115 , m_externalState(QAudio::StoppedState) |
115 , m_externalState(QAudio::StoppedState) |
116 , m_pullMode(false) |
116 , m_pullMode(false) |
117 , m_sink(0) |
117 , m_sink(0) |
118 , m_pullTimer(new QTimer(this)) |
118 , m_pullTimer(new QTimer(this)) |
|
119 , m_devSound(0) |
119 , m_devSoundBuffer(0) |
120 , m_devSoundBuffer(0) |
120 , m_devSoundBufferSize(0) |
121 , m_devSoundBufferSize(0) |
121 , m_totalBytesReady(0) |
122 , m_totalBytesReady(0) |
122 , m_devSoundBufferPos(0) |
123 , m_devSoundBufferPos(0) |
123 , m_totalSamplesRecorded(0) |
124 , m_totalSamplesRecorded(0) |
124 { |
125 { |
|
126 qRegisterMetaType<CMMFBuffer *>("CMMFBuffer *"); |
|
127 |
125 connect(m_notifyTimer.data(), SIGNAL(timeout()), this, SIGNAL(notify())); |
128 connect(m_notifyTimer.data(), SIGNAL(timeout()), this, SIGNAL(notify())); |
126 |
|
127 SymbianAudio::Utils::formatQtToNative(m_format, m_nativeFourCC, |
|
128 m_nativeFormat); |
|
129 |
129 |
130 m_pullTimer->setInterval(PushInterval); |
130 m_pullTimer->setInterval(PushInterval); |
131 connect(m_pullTimer.data(), SIGNAL(timeout()), this, SLOT(pullData())); |
131 connect(m_pullTimer.data(), SIGNAL(timeout()), this, SLOT(pullData())); |
132 } |
132 } |
133 |
133 |
273 QAudioFormat QAudioInputPrivate::format() const |
279 QAudioFormat QAudioInputPrivate::format() const |
274 { |
280 { |
275 return m_format; |
281 return m_format; |
276 } |
282 } |
277 |
283 |
278 //----------------------------------------------------------------------------- |
|
279 // MDevSoundObserver implementation |
|
280 //----------------------------------------------------------------------------- |
|
281 |
|
282 void QAudioInputPrivate::InitializeComplete(TInt aError) |
|
283 { |
|
284 Q_ASSERT_X(SymbianAudio::InitializingState == m_internalState, |
|
285 Q_FUNC_INFO, "Invalid state"); |
|
286 |
|
287 if (KErrNone == aError) |
|
288 startRecording(); |
|
289 } |
|
290 |
|
291 void QAudioInputPrivate::ToneFinished(TInt aError) |
|
292 { |
|
293 Q_UNUSED(aError) |
|
294 // This class doesn't use DevSound's tone playback functions, so should |
|
295 // never receive this callback. |
|
296 Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected callback"); |
|
297 } |
|
298 |
|
299 void QAudioInputPrivate::BufferToBeFilled(CMMFBuffer *aBuffer) |
|
300 { |
|
301 Q_UNUSED(aBuffer) |
|
302 // This class doesn't use DevSound in play mode, so should never receive |
|
303 // this callback. |
|
304 Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected callback"); |
|
305 } |
|
306 |
|
307 void QAudioInputPrivate::PlayError(TInt aError) |
|
308 { |
|
309 Q_UNUSED(aError) |
|
310 // This class doesn't use DevSound in play mode, so should never receive |
|
311 // this callback. |
|
312 Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected callback"); |
|
313 } |
|
314 |
|
315 void QAudioInputPrivate::BufferToBeEmptied(CMMFBuffer *aBuffer) |
|
316 { |
|
317 // Following receipt of this callback, DevSound should not provide another |
|
318 // buffer until we have returned the current one. |
|
319 Q_ASSERT_X(!m_devSoundBuffer, Q_FUNC_INFO, "Buffer already held"); |
|
320 |
|
321 CMMFDataBuffer *const buffer = static_cast<CMMFDataBuffer*>(aBuffer); |
|
322 |
|
323 if (!m_devSoundBufferSize) |
|
324 m_devSoundBufferSize = buffer->Data().MaxLength(); |
|
325 |
|
326 m_totalBytesReady += buffer->Data().Length(); |
|
327 |
|
328 if (SymbianAudio::SuspendedState == m_internalState) { |
|
329 m_devSoundBufferQ.append(buffer); |
|
330 } else { |
|
331 // Will be returned to DevSound by bufferEmptied(). |
|
332 m_devSoundBuffer = buffer; |
|
333 m_devSoundBufferPos = 0; |
|
334 |
|
335 if (bytesReady() && !m_pullMode) |
|
336 pushData(); |
|
337 } |
|
338 } |
|
339 |
|
340 void QAudioInputPrivate::RecordError(TInt aError) |
|
341 { |
|
342 Q_UNUSED(aError) |
|
343 setError(QAudio::IOError); |
|
344 } |
|
345 |
|
346 void QAudioInputPrivate::ConvertError(TInt aError) |
|
347 { |
|
348 Q_UNUSED(aError) |
|
349 // This class doesn't use DevSound's format conversion functions, so |
|
350 // should never receive this callback. |
|
351 Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected callback"); |
|
352 } |
|
353 |
|
354 void QAudioInputPrivate::DeviceMessage(TUid aMessageType, const TDesC8 &aMsg) |
|
355 { |
|
356 Q_UNUSED(aMessageType) |
|
357 Q_UNUSED(aMsg) |
|
358 // Ignore this callback. |
|
359 } |
|
360 |
284 |
361 //----------------------------------------------------------------------------- |
285 //----------------------------------------------------------------------------- |
362 // Private functions |
286 // Private functions |
363 //----------------------------------------------------------------------------- |
287 //----------------------------------------------------------------------------- |
364 |
288 |
365 void QAudioInputPrivate::open() |
289 void QAudioInputPrivate::open() |
366 { |
290 { |
367 Q_ASSERT_X(SymbianAudio::ClosedState == m_internalState, |
291 Q_ASSERT_X(SymbianAudio::ClosedState == m_internalState, |
368 Q_FUNC_INFO, "DevSound already opened"); |
292 Q_FUNC_INFO, "DevSound already opened"); |
369 |
293 |
370 QT_TRAP_THROWING( m_devSound.reset(CMMFDevSound::NewL()) ) |
294 Q_ASSERT(!m_devSound); |
371 |
295 m_devSound = new SymbianAudio::DevSoundWrapper(QAudio::AudioInput, this); |
372 QScopedPointer<SymbianAudio::DevSoundCapabilities> caps( |
296 |
373 new SymbianAudio::DevSoundCapabilities(*m_devSound, QAudio::AudioInput)); |
297 connect(m_devSound, SIGNAL(initializeComplete(int)), |
374 |
298 this, SLOT(devsoundInitializeComplete(int))); |
375 int err = SymbianAudio::Utils::isFormatSupported(m_format, *caps) ? |
299 connect(m_devSound, SIGNAL(bufferToBeProcessed(CMMFBuffer *)), |
376 KErrNone : KErrNotSupported; |
300 this, SLOT(devsoundBufferToBeEmptied(CMMFBuffer *))); |
377 |
301 connect(m_devSound, SIGNAL(processingError(int)), |
378 if (KErrNone == err) { |
302 this, SLOT(devsoundRecordError(int))); |
379 setState(SymbianAudio::InitializingState); |
303 |
380 TRAP(err, m_devSound->InitializeL(*this, m_nativeFourCC, |
304 setState(SymbianAudio::InitializingState); |
381 EMMFStateRecording)); |
305 m_devSound->initialize(m_format.codec()); |
382 } |
|
383 |
|
384 if (KErrNone != err) { |
|
385 setError(QAudio::OpenError); |
|
386 m_devSound.reset(); |
|
387 } |
|
388 } |
306 } |
389 |
307 |
390 void QAudioInputPrivate::startRecording() |
308 void QAudioInputPrivate::startRecording() |
391 { |
309 { |
392 const int samplesRecorded = m_devSound->SamplesRecorded(); |
310 const int samplesRecorded = m_devSound->samplesProcessed(); |
393 Q_ASSERT(samplesRecorded == 0); |
311 Q_ASSERT(samplesRecorded == 0); |
394 |
312 |
395 TRAPD(err, startDevSoundL()); |
313 bool ok = m_devSound->setFormat(m_format); |
396 if (KErrNone == err) { |
314 if (ok) |
|
315 ok = m_devSound->start(); |
|
316 |
|
317 if (ok) { |
397 startDataTransfer(); |
318 startDataTransfer(); |
398 } else { |
319 } else { |
399 setError(QAudio::OpenError); |
320 setError(QAudio::OpenError); |
400 close(); |
321 close(); |
401 } |
322 } |
402 } |
323 } |
403 |
324 |
404 void QAudioInputPrivate::startDevSoundL() |
|
405 { |
|
406 TMMFCapabilities nativeFormat = m_devSound->Config(); |
|
407 m_nativeFormat.iBufferSize = nativeFormat.iBufferSize; |
|
408 m_devSound->SetConfigL(m_nativeFormat); |
|
409 m_devSound->RecordInitL(); |
|
410 } |
|
411 |
|
412 void QAudioInputPrivate::startDataTransfer() |
325 void QAudioInputPrivate::startDataTransfer() |
413 { |
326 { |
414 m_notifyTimer->start(m_notifyInterval); |
327 if (m_notifyInterval) |
|
328 m_notifyTimer->start(m_notifyInterval); |
415 |
329 |
416 if (m_pullMode) |
330 if (m_pullMode) |
417 m_pullTimer->start(); |
331 m_pullTimer->start(); |
418 |
332 |
419 if (bytesReady()) { |
333 if (bytesReady()) { |
501 if (!bytesPushed) |
415 if (!bytesPushed) |
502 break; |
416 break; |
503 } |
417 } |
504 } |
418 } |
505 |
419 |
|
420 void QAudioInputPrivate::devsoundInitializeComplete(int err) |
|
421 { |
|
422 Q_ASSERT_X(SymbianAudio::InitializingState == m_internalState, |
|
423 Q_FUNC_INFO, "Invalid state"); |
|
424 |
|
425 if (!err && m_devSound->isFormatSupported(m_format)) |
|
426 startRecording(); |
|
427 else |
|
428 setError(QAudio::OpenError); |
|
429 } |
|
430 |
|
431 void QAudioInputPrivate::devsoundBufferToBeEmptied(CMMFBuffer *baseBuffer) |
|
432 { |
|
433 // Following receipt of this signal, DevSound should not provide another |
|
434 // buffer until we have returned the current one. |
|
435 Q_ASSERT_X(!m_devSoundBuffer, Q_FUNC_INFO, "Buffer already held"); |
|
436 |
|
437 CMMFDataBuffer *const buffer = static_cast<CMMFDataBuffer*>(baseBuffer); |
|
438 |
|
439 if (!m_devSoundBufferSize) |
|
440 m_devSoundBufferSize = buffer->Data().MaxLength(); |
|
441 |
|
442 m_totalBytesReady += buffer->Data().Length(); |
|
443 |
|
444 if (SymbianAudio::SuspendedState == m_internalState) { |
|
445 m_devSoundBufferQ.append(buffer); |
|
446 } else { |
|
447 // Will be returned to DevSoundWrapper by bufferProcessed(). |
|
448 m_devSoundBuffer = buffer; |
|
449 m_devSoundBufferPos = 0; |
|
450 |
|
451 if (bytesReady() && !m_pullMode) |
|
452 pushData(); |
|
453 } |
|
454 } |
|
455 |
|
456 void QAudioInputPrivate::devsoundRecordError(int err) |
|
457 { |
|
458 Q_UNUSED(err) |
|
459 setError(QAudio::IOError); |
|
460 } |
|
461 |
506 void QAudioInputPrivate::bufferEmptied() |
462 void QAudioInputPrivate::bufferEmptied() |
507 { |
463 { |
508 m_devSoundBufferPos = 0; |
464 m_devSoundBufferPos = 0; |
509 |
465 |
510 if (m_devSoundBuffer) { |
466 if (m_devSoundBuffer) { |
511 m_totalBytesReady -= m_devSoundBuffer->Data().Length(); |
467 m_totalBytesReady -= m_devSoundBuffer->Data().Length(); |
512 m_devSoundBuffer = 0; |
468 m_devSoundBuffer = 0; |
513 m_devSound->RecordData(); |
469 m_devSound->bufferProcessed(); |
514 } else { |
470 } else { |
515 Q_ASSERT(!m_devSoundBufferQ.empty()); |
471 Q_ASSERT(!m_devSoundBufferQ.empty()); |
516 m_totalBytesReady -= m_devSoundBufferQ.front()->Data().Length(); |
472 m_totalBytesReady -= m_devSoundBufferQ.front()->Data().Length(); |
517 m_devSoundBufferQ.erase(m_devSoundBufferQ.begin()); |
473 m_devSoundBufferQ.erase(m_devSoundBufferQ.begin()); |
518 |
474 |
519 // If the queue has been emptied, resume transfer from the hardware |
475 // If the queue has been emptied, resume transfer from the hardware |
520 if (m_devSoundBufferQ.empty()) |
476 if (m_devSoundBufferQ.empty()) |
521 m_devSound->RecordInitL(); |
477 if (!m_devSound->start()) |
|
478 setError(QAudio::IOError); |
522 } |
479 } |
523 |
480 |
524 Q_ASSERT(m_totalBytesReady >= 0); |
481 Q_ASSERT(m_totalBytesReady >= 0); |
525 } |
482 } |
526 |
483 |
552 |
511 |
553 qint64 QAudioInputPrivate::getSamplesRecorded() const |
512 qint64 QAudioInputPrivate::getSamplesRecorded() const |
554 { |
513 { |
555 qint64 result = 0; |
514 qint64 result = 0; |
556 if (m_devSound) |
515 if (m_devSound) |
557 result = qint64(m_devSound->SamplesRecorded()); |
516 result = qint64(m_devSound->samplesProcessed()); |
558 return result; |
517 return result; |
559 } |
518 } |
560 |
519 |
561 void QAudioInputPrivate::setError(QAudio::Error error) |
520 void QAudioInputPrivate::setError(QAudio::Error error) |
562 { |
521 { |
563 m_error = error; |
522 m_error = error; |
564 |
523 |
565 // Although no state transition actually occurs here, a stateChanged event |
524 // Although no state transition actually occurs here, a stateChanged event |
566 // must be emitted to inform the client that the call to start() was |
525 // must be emitted to inform the client that the call to start() was |
567 // unsuccessful. |
526 // unsuccessful. |
568 if (QAudio::OpenError == error) |
527 if (QAudio::OpenError == error) { |
569 emit stateChanged(QAudio::StoppedState); |
528 emit stateChanged(QAudio::StoppedState); |
570 |
529 } else { |
571 // Close the DevSound instance. This causes a transition to StoppedState. |
530 if (QAudio::UnderrunError == error) |
572 // This must be done asynchronously in case the current function was called |
531 setState(SymbianAudio::IdleState); |
573 // from a DevSound event handler, in which case deleting the DevSound |
532 else |
574 // instance may cause an exception. |
533 // Close the DevSound instance. This causes a transition to |
575 QMetaObject::invokeMethod(this, "close", Qt::QueuedConnection); |
534 // StoppedState. This must be done asynchronously in case the |
|
535 // current function was called from a DevSound event handler, in which |
|
536 // case deleting the DevSound instance may cause an exception. |
|
537 QMetaObject::invokeMethod(this, "close", Qt::QueuedConnection); |
|
538 } |
576 } |
539 } |
577 |
540 |
578 void QAudioInputPrivate::setState(SymbianAudio::State newInternalState) |
541 void QAudioInputPrivate::setState(SymbianAudio::State newInternalState) |
579 { |
542 { |
580 const QAudio::State oldExternalState = m_externalState; |
543 const QAudio::State oldExternalState = m_externalState; |
581 m_internalState = newInternalState; |
544 m_internalState = newInternalState; |
582 m_externalState = SymbianAudio::Utils::stateNativeToQt( |
545 m_externalState = SymbianAudio::Utils::stateNativeToQt(m_internalState); |
583 m_internalState, initializingState()); |
|
584 |
546 |
585 if (m_externalState != oldExternalState) |
547 if (m_externalState != oldExternalState) |
586 emit stateChanged(m_externalState); |
548 emit stateChanged(m_externalState); |
587 } |
549 } |
588 |
550 |
589 QAudio::State QAudioInputPrivate::initializingState() const |
|
590 { |
|
591 return QAudio::IdleState; |
|
592 } |
|
593 |
|
594 QT_END_NAMESPACE |
551 QT_END_NAMESPACE |