|
1 // Copyright (c) 2003-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // mmfswaudioinput.cpp |
|
15 // |
|
16 // |
|
17 #include "mmfswaudioinput.h" |
|
18 #include "mmfswaudioinputpriv.h" |
|
19 #include <d32soundsc.h> |
|
20 #include <e32debug.h> |
|
21 #include "mmf/utils/rateconvert.h" // if we need to resample |
|
22 _LIT(KPddFileName,"SOUNDSC.PDD"); |
|
23 _LIT(KLddFileName,"ESOUNDSC.LDD"); |
|
24 |
|
25 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
26 |
|
27 const TText* const KStateNames[] = // must agree with TState |
|
28 { |
|
29 _S("EStateCreated2"), |
|
30 _S("EStateInitialized2"), |
|
31 _S("EStateRecordWait2"), |
|
32 _S("EStateRecordWaitAck2"), |
|
33 }; |
|
34 |
|
35 static const TText* StateName(TInt aState) |
|
36 { |
|
37 return KStateNames[aState]; |
|
38 } |
|
39 |
|
40 const TText* const KRStateNames[] = // must agree with TRunningState |
|
41 { |
|
42 _S("ERStateRunning"), |
|
43 _S("ERStatePaused"), |
|
44 _S("ERStateFinishing"), |
|
45 _S("ERStateFinished"), |
|
46 _S("ERStateFailed"), |
|
47 }; |
|
48 |
|
49 static const TText* RStateName(TInt aState) |
|
50 { |
|
51 return KRStateNames[aState]; |
|
52 } |
|
53 |
|
54 #endif // SYMBIAN_SWCODEC_LOGGING |
|
55 |
|
56 #ifdef _DEBUG |
|
57 |
|
58 static void Panic(TInt aPanic) |
|
59 { |
|
60 _LIT(KPanicString, "SwAudioInput"); |
|
61 User::Panic(KPanicString, aPanic); |
|
62 } |
|
63 |
|
64 void CAudioInput::CheckFullInvariant() |
|
65 { |
|
66 CheckInvariant(); |
|
67 CheckActiveRecorders(); |
|
68 } |
|
69 |
|
70 void CAudioInput::CheckInvariant(TBool aKnownConstructed) |
|
71 { |
|
72 // full check would be that each recorder is in one, and only one, queue. |
|
73 // However, since the queues share the same infrastructure, checking the overall length of queues |
|
74 // is correct should suffice. During construction or deletion this may obviously vary |
|
75 TInt totalLength = QLength(iIdleQueue) + QLength(iRecordingQueue) + |
|
76 QLength(iPendingQueue) + QLength(iBusyQueue); |
|
77 if (aKnownConstructed) |
|
78 { |
|
79 __ASSERT_DEBUG(totalLength==KNumRecorders, Panic(KPanicBadTotalQueueLength)); |
|
80 } |
|
81 else |
|
82 { |
|
83 __ASSERT_DEBUG(totalLength<=KNumRecorders, Panic(KPanicBadTotalQueueLength2)); |
|
84 } |
|
85 __ASSERT_DEBUG(QLength(iBusyQueue)<=1, Panic(KPanicBadTotalQueueLength)); |
|
86 } |
|
87 |
|
88 #else // _DEBUG |
|
89 |
|
90 // inline versions that do nothing... |
|
91 |
|
92 inline void CAudioInput::CheckFullInvariant() |
|
93 { |
|
94 } |
|
95 |
|
96 inline void CAudioInput::CheckInvariant(TBool /*aKnownConstructed*/) |
|
97 { |
|
98 } |
|
99 |
|
100 #endif // _DEBUG |
|
101 |
|
102 const TInt KMinBufferSize = 4; // assume a good default? |
|
103 //Shared chunk driver does not support max. buffer size. 16K is given in order to simulate the old driver behavior. |
|
104 const TInt KMaxBufferSize = 0x4000; |
|
105 |
|
106 //Table that maps given linear value of volume to the corresponding decibel value. |
|
107 const TUint8 KLinearToDbConstantLookup[] = |
|
108 { |
|
109 0, // 0 |
|
110 158, |
|
111 170, |
|
112 177, |
|
113 182, |
|
114 186, |
|
115 189, |
|
116 192, |
|
117 194, |
|
118 196, |
|
119 198, // 10 |
|
120 200, |
|
121 201, |
|
122 203, |
|
123 204, |
|
124 205, |
|
125 206, |
|
126 207, |
|
127 208, |
|
128 209, |
|
129 210, // 20 |
|
130 211, |
|
131 212, |
|
132 213, |
|
133 213, |
|
134 214, |
|
135 215, |
|
136 215, |
|
137 216, |
|
138 217, |
|
139 217, // 30 |
|
140 218, |
|
141 218, |
|
142 219, |
|
143 219, |
|
144 220, |
|
145 220, |
|
146 221, |
|
147 221, |
|
148 222, |
|
149 222, // 40 |
|
150 223, |
|
151 223, |
|
152 224, |
|
153 224, |
|
154 224, |
|
155 225, |
|
156 225, |
|
157 225, |
|
158 226, |
|
159 226, // 50 |
|
160 226, |
|
161 227, |
|
162 227, |
|
163 227, |
|
164 228, |
|
165 228, |
|
166 228, |
|
167 229, |
|
168 229, |
|
169 229, // 60 |
|
170 230, |
|
171 230, |
|
172 230, |
|
173 230, |
|
174 231, |
|
175 231, |
|
176 231, |
|
177 231, |
|
178 232, |
|
179 232, // 70 |
|
180 232, |
|
181 232, |
|
182 233, |
|
183 233, |
|
184 233, |
|
185 233, |
|
186 234, |
|
187 234, |
|
188 234, |
|
189 234, // 80 |
|
190 235, |
|
191 235, |
|
192 235, |
|
193 235, |
|
194 235, |
|
195 236, |
|
196 236, |
|
197 236, |
|
198 236, |
|
199 236, // 90 |
|
200 237, |
|
201 237, |
|
202 237, |
|
203 237, |
|
204 237, |
|
205 237, |
|
206 238, |
|
207 238, |
|
208 238, |
|
209 238, // 100 |
|
210 238, |
|
211 239, |
|
212 239, |
|
213 239, |
|
214 239, |
|
215 239, |
|
216 239, |
|
217 240, |
|
218 240, |
|
219 240, // 110 |
|
220 240, |
|
221 240, |
|
222 240, |
|
223 240, |
|
224 241, |
|
225 241, |
|
226 241, |
|
227 241, |
|
228 241, |
|
229 241, // 120 |
|
230 241, |
|
231 242, |
|
232 242, |
|
233 242, |
|
234 242, |
|
235 242, |
|
236 242, |
|
237 242, |
|
238 243, |
|
239 243, // 130 |
|
240 243, |
|
241 243, |
|
242 243, |
|
243 243, |
|
244 243, |
|
245 244, |
|
246 244, |
|
247 244, |
|
248 244, |
|
249 244, // 140 |
|
250 244, |
|
251 244, |
|
252 244, |
|
253 245, |
|
254 245, |
|
255 245, |
|
256 245, |
|
257 245, |
|
258 245, |
|
259 245, // 150 |
|
260 245, |
|
261 245, |
|
262 246, |
|
263 246, |
|
264 246, |
|
265 246, |
|
266 246, |
|
267 246, |
|
268 246, |
|
269 246, // 160 |
|
270 246, |
|
271 247, |
|
272 247, |
|
273 247, |
|
274 247, |
|
275 247, |
|
276 247, |
|
277 247, |
|
278 247, |
|
279 247, // 170 |
|
280 247, |
|
281 248, |
|
282 248, |
|
283 248, |
|
284 248, |
|
285 248, |
|
286 248, |
|
287 248, |
|
288 248, |
|
289 248, // 180 |
|
290 248, |
|
291 249, |
|
292 249, |
|
293 249, |
|
294 249, |
|
295 249, |
|
296 249, |
|
297 249, |
|
298 249, |
|
299 249, // 190 |
|
300 249, |
|
301 250, |
|
302 250, |
|
303 250, |
|
304 250, |
|
305 250, |
|
306 250, |
|
307 250, |
|
308 250, |
|
309 250, // 200 |
|
310 250, |
|
311 250, |
|
312 250, |
|
313 251, |
|
314 251, |
|
315 251, |
|
316 251, |
|
317 251, |
|
318 251, |
|
319 251, // 210 |
|
320 251, |
|
321 251, |
|
322 251, |
|
323 251, |
|
324 251, |
|
325 252, |
|
326 252, |
|
327 252, |
|
328 252, |
|
329 252, // 220 |
|
330 252, |
|
331 252, |
|
332 252, |
|
333 252, |
|
334 252, |
|
335 252, |
|
336 252, |
|
337 252, |
|
338 253, |
|
339 253, // 230 |
|
340 253, |
|
341 253, |
|
342 253, |
|
343 253, |
|
344 253, |
|
345 253, |
|
346 253, |
|
347 253, |
|
348 253, |
|
349 253, // 240 |
|
350 253, |
|
351 254, |
|
352 254, |
|
353 254, |
|
354 254, |
|
355 254, |
|
356 254, |
|
357 254, |
|
358 254, |
|
359 254, // 250 |
|
360 254, |
|
361 254, |
|
362 254, |
|
363 254, |
|
364 254 |
|
365 }; |
|
366 |
|
367 // rate lookup table |
|
368 |
|
369 const TInt KNumSampleRates = 9; |
|
370 |
|
371 struct TSampleRateEnumTable |
|
372 { |
|
373 TInt iRate; |
|
374 TSoundRate iRateEnum; |
|
375 TUint iRateConstant; |
|
376 }; |
|
377 //Table that maps given samples per second to the corresponding enums in RSoundSc |
|
378 const TSampleRateEnumTable KRateEnumLookup[] = |
|
379 { |
|
380 {48000,ESoundRate48000Hz,KSoundRate48000Hz}, |
|
381 {44100,ESoundRate44100Hz,KSoundRate44100Hz}, |
|
382 {32000,ESoundRate32000Hz,KSoundRate32000Hz}, |
|
383 {24000,ESoundRate24000Hz,KSoundRate24000Hz}, |
|
384 {22050,ESoundRate22050Hz,KSoundRate22050Hz}, |
|
385 {16000,ESoundRate16000Hz,KSoundRate16000Hz}, |
|
386 {12000,ESoundRate12000Hz,KSoundRate12000Hz}, |
|
387 {11025,ESoundRate11025Hz,KSoundRate11025Hz}, |
|
388 {8000, ESoundRate8000Hz, KSoundRate8000Hz} |
|
389 }; |
|
390 |
|
391 // TAudioInputParams |
|
392 |
|
393 EXPORT_C TAudioInputParams::TAudioInputParams() : |
|
394 iSampleRate(0), iNominalBufferSize(0) |
|
395 { |
|
396 // none |
|
397 } |
|
398 |
|
399 // CAudioInput |
|
400 |
|
401 EXPORT_C MAudioInput* MAudioInput::CreateL(MAudioInputObserver& aObserver) |
|
402 { |
|
403 MAudioInput* result = CAudioInput::NewL(aObserver); |
|
404 return result; |
|
405 } |
|
406 |
|
407 CAudioInput* CAudioInput::NewL(MAudioInputObserver& aObserver) |
|
408 { |
|
409 CAudioInput* result = new CAudioInput(aObserver); |
|
410 CleanupStack::PushL(result); |
|
411 result->ConstructL(); |
|
412 CleanupStack::Pop(result); |
|
413 return result; |
|
414 } |
|
415 |
|
416 CAudioInput::CAudioInput(MAudioInputObserver& aObserver) : |
|
417 iObserver(aObserver), |
|
418 iIdleQueue(_FOFF(CRecorder,iLink)), |
|
419 iRecordingQueue(_FOFF(CRecorder,iLink)), |
|
420 iPendingQueue(_FOFF(CRecorder,iLink)), |
|
421 iBusyQueue(_FOFF(CRecorder,iLink)) |
|
422 { |
|
423 ASSERT(iState == EStateCreated2); // assume zero'ing initialises correctly |
|
424 } |
|
425 |
|
426 void CAudioInput::Release() |
|
427 // effective destructor call |
|
428 { |
|
429 delete this; |
|
430 } |
|
431 |
|
432 TAny* CAudioInput::Interface(TUid aInterfaceUid) |
|
433 { |
|
434 if (aInterfaceUid == KUidAIParamInterface) |
|
435 { |
|
436 MAIParamInterface* self = this; |
|
437 return self; |
|
438 } |
|
439 return NULL; |
|
440 } |
|
441 |
|
442 RSoundSc& CAudioInput::RecordSoundDevice() |
|
443 { |
|
444 ASSERT(iRecordSoundDevice.Handle()!=0); // should be open |
|
445 return iRecordSoundDevice; |
|
446 } |
|
447 |
|
448 CAudioInput::~CAudioInput() |
|
449 { |
|
450 CheckInvariant(EFalse); // may not be constructed |
|
451 Cancel(); |
|
452 for (TInt i = 0; i < KNumRecorders; i++) |
|
453 { |
|
454 // just in case, call cancel directly from this point too |
|
455 // Cancel depends on the active queue, and might not be quite the same. |
|
456 CRecorder* recorder = iRecorders[i]; |
|
457 if (recorder) |
|
458 { |
|
459 recorder->Cancel(); |
|
460 } |
|
461 delete recorder; |
|
462 } |
|
463 delete iAsyncCallBack; |
|
464 iConvBuff.Close(); |
|
465 iRecordSoundDevice.Close(); |
|
466 iChunk.Close(); |
|
467 } |
|
468 |
|
469 void CAudioInput::Cancel() |
|
470 { |
|
471 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
472 RDebug::Print(_L("--->CAudioInput::Cancel()")); |
|
473 #endif |
|
474 CancelRecorders(); |
|
475 if (iAsyncCallBack) |
|
476 { |
|
477 iAsyncCallBack->Cancel(); |
|
478 } |
|
479 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
480 RDebug::Print(_L("<---CAudioInput::Cancel()")); |
|
481 #endif |
|
482 } |
|
483 |
|
484 void CAudioInput::CancelRecorders() |
|
485 // if a recorder is active, then cancel it. Also move the list if required. |
|
486 { |
|
487 CheckInvariant(); // semi-invariant check - this is called from destructor |
|
488 |
|
489 CRecorder* recorder; |
|
490 while (QPop(recorder, iRecordingQueue)) |
|
491 { |
|
492 recorder->Cancel(); |
|
493 iIdleQueue.AddLast(*recorder); |
|
494 } |
|
495 CheckFullInvariant(); |
|
496 } |
|
497 |
|
498 void CAudioInput::CancelPendingRecorders() |
|
499 // take any recorder in the pending queue. ack the buffer and send to idle |
|
500 { |
|
501 CheckFullInvariant(); |
|
502 |
|
503 CRecorder* recorder; |
|
504 while (QPop(recorder, iPendingQueue)) |
|
505 { |
|
506 recorder->ReleaseBuffer(); |
|
507 iIdleQueue.AddLast(*recorder); |
|
508 } |
|
509 CheckFullInvariant(); |
|
510 } |
|
511 |
|
512 void CAudioInput::CancelBusyRecorder() |
|
513 // take busy recorder. ack the buffer and send to idle |
|
514 { |
|
515 CheckFullInvariant(); |
|
516 |
|
517 CRecorder* recorder; |
|
518 if (QPop(recorder, iBusyQueue)) |
|
519 { |
|
520 recorder->ReleaseBuffer(); |
|
521 iIdleQueue.AddLast(*recorder); |
|
522 } |
|
523 CheckFullInvariant(); |
|
524 } |
|
525 |
|
526 void CAudioInput::RecordAllIdle() |
|
527 // take any recorder in idle queue and set recording |
|
528 { |
|
529 CheckFullInvariant(); |
|
530 |
|
531 CRecorder* recorder; |
|
532 while (QPop(recorder, iIdleQueue)) |
|
533 { |
|
534 recorder->RecordData(); |
|
535 iRecordingQueue.AddLast(*recorder); |
|
536 } |
|
537 CheckFullInvariant(); |
|
538 } |
|
539 |
|
540 void CAudioInput::ConstructL() |
|
541 { |
|
542 for (TInt i = 0; i < KNumRecorders; i++) |
|
543 { |
|
544 iRecorders[i] = new (ELeave) CRecorder(*this, i); |
|
545 iIdleQueue.AddLast(*(iRecorders[i])); |
|
546 } |
|
547 iAsyncCallBack = new (ELeave) CAsyncCallBack(CActive::EPriorityStandard); |
|
548 TCallBack callback(Callback, this); |
|
549 iAsyncCallBack->Set(callback); |
|
550 User::LoadPhysicalDevice(KPddFileName); |
|
551 User::LoadLogicalDevice(KLddFileName); |
|
552 CheckFullInvariant(); |
|
553 } |
|
554 |
|
555 TInt CAudioInput::Initialize(const TAudioInputParams& aParams) |
|
556 { |
|
557 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
558 RDebug::Print(_L("--->CAudioInput::Initialize() state=%s rstate=%s"), StateName(iState), RStateName(iRState)); |
|
559 #endif |
|
560 CheckFullInvariant(); |
|
561 TInt error = KErrNone; |
|
562 if (iState == EStateCreated2) |
|
563 { |
|
564 if (!iRecordSoundDevice.Handle()) |
|
565 { |
|
566 error = iRecordSoundDevice.Open(KSoundScRxUnit0); |
|
567 if (error) |
|
568 { |
|
569 Close(); // TODO Close() required? |
|
570 } |
|
571 } |
|
572 if (!error) |
|
573 { |
|
574 iBufferLength = aParams.iNominalBufferSize; // will be updated by SetFormat() if required |
|
575 error = SetFormat(aParams); |
|
576 if (!error) |
|
577 { |
|
578 iRecordBufferConfig.iNumBuffers = KNumRecorders*2; // for each AO we create two buffers |
|
579 iRecordBufferConfig.iFlags = 0; |
|
580 iRecordBufferConfig.iBufferSizeInBytes = iBufferLength; |
|
581 ASSERT(iChunk.Handle()==0); // should not be already open |
|
582 TPckg<TRecordSharedChunkBufConfig> bufferConfigBuf( |
|
583 iRecordBufferConfig); |
|
584 error = iRecordSoundDevice.SetBufferChunkCreate( |
|
585 bufferConfigBuf, iChunk); |
|
586 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
587 RDebug::Print( |
|
588 _L("iRecordBufferConfig.iNumBuffers = [%d]"),iRecordBufferConfig.iNumBuffers); |
|
589 RDebug::Print( |
|
590 _L("iRecordBufferConfig.iFlags = [%d]"),iRecordBufferConfig.iFlags); |
|
591 RDebug::Print( |
|
592 _L("iRecordBufferConfig.iBufferSizeInBytes = [%d]"),iRecordBufferConfig.iBufferSizeInBytes); |
|
593 #endif |
|
594 if (error == KErrNone) |
|
595 { |
|
596 ASSERT(iChunk.Handle()); // should now be open |
|
597 iRecordSoundDevice.GetBufferConfig(bufferConfigBuf); // overwrite iRecordBufferConfig |
|
598 SetGain(aParams.iInitialGain); |
|
599 iState = EStateInitialized2; |
|
600 } |
|
601 } |
|
602 } |
|
603 } |
|
604 else |
|
605 { |
|
606 error = KErrNotReady; |
|
607 } |
|
608 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
609 RDebug::Print(_L("<---CAudioInput::Initialize(%d) state=%s rstate=%s"), error, StateName( |
|
610 iState), RStateName(iRState)); |
|
611 #endif |
|
612 CheckFullInvariant(); |
|
613 return error; |
|
614 } |
|
615 |
|
616 void CAudioInput::Close() |
|
617 { |
|
618 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
619 RDebug::Print(_L("--->CAudioInput::Close() state=%s rstate=%s"), StateName(iState), RStateName(iRState)); |
|
620 #endif |
|
621 CheckFullInvariant(); |
|
622 InternalStop(); // Technically this should not be required, as client should Stop() first, but just in case |
|
623 if (iState == EStateInitialized2) |
|
624 { |
|
625 iRecordSoundDevice.Close(); |
|
626 iChunk.Close(); |
|
627 iConvBuff.Close(); |
|
628 iState = EStateCreated2; |
|
629 } |
|
630 ASSERT(iState==EStateCreated2); |
|
631 ASSERT(QLength(iIdleQueue)==KNumRecorders); |
|
632 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
633 RDebug::Print(_L("<---CAudioInput::Close() state=%s rstate=%s"), StateName(iState), RStateName(iRState)); |
|
634 #endif |
|
635 CheckFullInvariant(); |
|
636 } |
|
637 |
|
638 TInt CAudioInput::Start() |
|
639 { |
|
640 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
641 RDebug::Print(_L("--->CAudioInput::Start() state=%s rstate=%s"), StateName(iState), RStateName(iRState)); |
|
642 #endif |
|
643 CheckFullInvariant(); |
|
644 TInt error = KErrNone; |
|
645 if (iState == EStateInitialized2) |
|
646 { |
|
647 RecordAllIdle(); |
|
648 iState = EStateRecordWait2; |
|
649 iRState = ERStateRunning; |
|
650 } |
|
651 else |
|
652 { |
|
653 error = KErrNotReady; |
|
654 } |
|
655 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
656 RDebug::Print(_L("<---CAudioInput::Start(%d) state=%s rstate=%s"), |
|
657 error, StateName(iState), RStateName(iRState)); |
|
658 #endif |
|
659 CheckFullInvariant(); |
|
660 return error; |
|
661 } |
|
662 |
|
663 void CAudioInput::BufferAck() |
|
664 { |
|
665 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
666 RDebug::Print(_L("--->CAudioInput::BufferAck() state=%s rstate=%s"), StateName(iState), RStateName(iRState)); |
|
667 #endif |
|
668 CheckFullInvariant(); |
|
669 ASSERT(iState==EStateRecordWaitAck2); |
|
670 HandleBufferAck(); |
|
671 iState = EStateRecordWait2; |
|
672 RequestCallback(); |
|
673 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
674 RDebug::Print(_L("<---CAudioInput::BufferAck() state=%s rstate=%s"), StateName(iState), RStateName(iRState)); |
|
675 #endif |
|
676 CheckFullInvariant(); |
|
677 } |
|
678 |
|
679 void CAudioInput::HandleBufferAck() |
|
680 { |
|
681 CRecorder* recorder = QPop(iBusyQueue); |
|
682 recorder->ReleaseBuffer(); |
|
683 if (iRState == ERStateRunning) |
|
684 { |
|
685 recorder->RecordData(); |
|
686 iRecordingQueue.AddLast(*recorder); |
|
687 } |
|
688 else |
|
689 { |
|
690 iIdleQueue.AddLast(*recorder); |
|
691 if (iRState == ERStatePaused && (QLength(iRecordingQueue)+QLength(iPendingQueue) == 0)) |
|
692 { |
|
693 iRState = ERStateFinishing; |
|
694 } |
|
695 } |
|
696 } |
|
697 |
|
698 TInt CAudioInput::Pause() |
|
699 { |
|
700 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
701 RDebug::Print(_L("--->CAudioInput::Pause() state=%s rstate=%s"), StateName(iState), RStateName(iRState)); |
|
702 #endif |
|
703 CheckFullInvariant(); |
|
704 TInt err = KErrNone; // note we are silent if called in wrong state |
|
705 |
|
706 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
707 RDebug::Print( |
|
708 _L("***!pause irecordingquelength %d pendingquelength %d ibusyquelength=%d"), QLength(iRecordingQueue), |
|
709 QLength(iPendingQueue), QLength(iBusyQueue)); |
|
710 #endif |
|
711 if ((iState == EStateRecordWait2 || iState == EStateRecordWaitAck2) && iRState==ERStateRunning) |
|
712 { |
|
713 iRecordSoundDevice.Pause(); |
|
714 |
|
715 iRState = ERStatePaused; |
|
716 } |
|
717 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
718 RDebug::Print(_L("<---CAudioInput::Pause(%d) state=%s err=%d"), err, StateName(iState), RStateName(iRState)); |
|
719 #endif |
|
720 CheckFullInvariant(); |
|
721 return err; |
|
722 } |
|
723 |
|
724 TInt CAudioInput::Resume() |
|
725 { |
|
726 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
727 RDebug::Print(_L("--->CAudioInput::Resume() state=%s rstate=%s"), StateName(iState), RStateName(iRState)); |
|
728 #endif |
|
729 CheckFullInvariant(); |
|
730 TInt err = KErrNone; // note we are silent if called in the wrong state |
|
731 if ((iState == EStateRecordWait2 || iState == EStateRecordWaitAck2) && |
|
732 ((iRState==ERStatePaused || iRState==ERStateFinishing || iRState==ERStateFinished))) |
|
733 { |
|
734 err = RecordSoundDevice().Resume(); |
|
735 |
|
736 RecordAllIdle(); |
|
737 |
|
738 iRState = ERStateRunning; |
|
739 } |
|
740 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
741 RDebug::Print(_L("<---CAudioInput::Resume(%d) state=%s rstate=%s"), err, StateName(iState), RStateName(iRState)); |
|
742 #endif |
|
743 CheckFullInvariant(); |
|
744 return err; |
|
745 } |
|
746 |
|
747 TInt CAudioInput::Flush() |
|
748 { |
|
749 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
750 RDebug::Print(_L("--->CAudioInput::Flush() state=%s rstate=%s"), StateName(iState), RStateName(iRState)); |
|
751 #endif |
|
752 CheckFullInvariant(); |
|
753 TInt error = KErrNotReady; |
|
754 if (iRState==ERStatePaused) |
|
755 { |
|
756 if (iState == EStateRecordWait2) |
|
757 { |
|
758 InternalFlush(); |
|
759 ASSERT(iState == EStateRecordWait2); // stay put |
|
760 error = KErrNone; |
|
761 } |
|
762 else if (iState == EStateRecordWaitAck2) |
|
763 { |
|
764 InternalFlush(); |
|
765 iState = EStateRecordWait2; |
|
766 error = KErrNone; |
|
767 } |
|
768 } |
|
769 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
770 RDebug::Print(_L("--->CAudioInput::Flush(%d) state=%s rstate=%s"), error, StateName(iState), RStateName(iRState)); |
|
771 #endif |
|
772 CheckFullInvariant(); |
|
773 return error; |
|
774 } |
|
775 |
|
776 void CAudioInput::Stop() |
|
777 { |
|
778 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
779 RDebug::Print(_L("--->CAudioInput::Stop() state=%s rstate=%s"), StateName(iState), RStateName(iRState)); |
|
780 #endif |
|
781 CheckFullInvariant(); |
|
782 InternalStop(); |
|
783 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
784 RDebug::Print(_L("<---CAudioInput::Stop() state=%s rstate=%s"), StateName(iState), RStateName(iRState)); |
|
785 #endif |
|
786 } |
|
787 |
|
788 void CAudioInput::InternalStop() |
|
789 // This stops all recording and returns pending and busy buffers to idle. Must be called when |
|
790 // client knows the buffer has been grabbed (e.g. _not_ before error callback) |
|
791 { |
|
792 CheckInvariant(); // Can be called from buffer error, so can't check full invariant. |
|
793 if (iState != EStateInitialized2 && iState != EStateCreated2) |
|
794 { |
|
795 InternalFlush(); |
|
796 iState = EStateInitialized2; |
|
797 } |
|
798 CheckFullInvariant(); |
|
799 ASSERT((QLength(iRecordingQueue) + QLength(iPendingQueue) + |
|
800 QLength(iBusyQueue))==0); // everything is stopped |
|
801 } |
|
802 |
|
803 void CAudioInput::InternalFlush() |
|
804 { |
|
805 CancelRecorders(); |
|
806 CancelPendingRecorders(); |
|
807 CancelBusyRecorder(); |
|
808 } |
|
809 |
|
810 void CAudioInput::BufferArrives(CRecorder* aRecorder) |
|
811 { |
|
812 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
813 RDebug::Print( |
|
814 _L("--->CAudioInput::BufferArrives(%d,%d) state=%s rstate=%s"), aRecorder->Index(), |
|
815 aRecorder->StatusOrOffset(), StateName(iState), RStateName(iRState)); |
|
816 #endif |
|
817 CheckInvariant(); // Can't use CheckFullInvariant() from RunL |
|
818 ASSERT(iState==EStateRecordWait2 || iState==EStateRecordWaitAck2); |
|
819 ASSERT(aRecorder->Offset()>=0); // assert we're not here due to an error |
|
820 iRecordingQueue.Remove(*aRecorder); |
|
821 iPendingQueue.AddLast(*aRecorder); |
|
822 if (iState==EStateRecordWait2) |
|
823 { |
|
824 RequestCallback(); |
|
825 } |
|
826 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
827 RDebug::Print(_L("<---CAudioInput::BufferArrives() state=%s rstate=%s"), |
|
828 StateName(iState), RStateName(iRState)); |
|
829 #endif |
|
830 CheckFullInvariant(); |
|
831 } |
|
832 |
|
833 void CAudioInput::UseBuffer(CRecorder* aRecorder) |
|
834 // incomming buffer is pointed to by iBufPtr. Either directly or via convert, use for callback |
|
835 { |
|
836 iBufPtr.Set(iChunk.Base() + aRecorder->Offset(), aRecorder->Length()); |
|
837 if (iConverter) |
|
838 { |
|
839 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
840 RDebug::Print(_L("iBufPtr length [%d] iconvbuff length [%d,%d]"), |
|
841 iBufPtr.Length(), iConvBuff.Length(), iConvBuff.MaxLength()); |
|
842 #endif |
|
843 __DEBUG_ONLY(TInt converted =) iConverter->Convert(iBufPtr, iConvBuff); |
|
844 // the following assert should check we convert the log. |
|
845 // Actually we sometimes fail at the end of the operation with what is effectively |
|
846 // the last buffer. Arguably a driver fault, but there we are |
|
847 // ASSERT(converted==iBufPtr.Length()); |
|
848 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
849 RDebug::Print(_L("iBufPtr length [%d] iconvbuff length after [%d,%d]"), |
|
850 iBufPtr.Length(), iConvBuff.Length(), iConvBuff.MaxLength()); |
|
851 #endif |
|
852 iObserver.InputBufferAvailable(iConvBuff); |
|
853 } |
|
854 else |
|
855 { |
|
856 iObserver.InputBufferAvailable(iBufPtr); |
|
857 } |
|
858 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
859 RDebug::Print(_L("12345 ibufptr = [0x%x]"),iBufPtr.Ptr()); |
|
860 #endif |
|
861 } |
|
862 |
|
863 void CAudioInput::BufferError(CRecorder* aRecorder, TInt aError) |
|
864 { |
|
865 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
866 RDebug::Print( |
|
867 _L("--->CAudioInput::BufferError(%d,%d) state=%s rstate=%s"), aRecorder->Index(), |
|
868 aError, StateName(iState), RStateName(iRState)); |
|
869 #endif |
|
870 CheckInvariant(); // Can't use CheckFullInvariant() from RunL |
|
871 if (aError==KErrCancel || aError==KErrOverflow) |
|
872 { |
|
873 // Cancel: sign of a Pause operation. If paused etc, then merely add to idle list. potentially generate finished signal |
|
874 // if not paused, then not clear but just in case request record again |
|
875 // Overflow: basically try again, but if paused merely add to idle. Check for last buffer just in case |
|
876 if (iRState!=ERStateRunning) |
|
877 { |
|
878 iRecordingQueue.Remove(*aRecorder); |
|
879 iIdleQueue.AddLast(*aRecorder); |
|
880 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
881 RDebug::Print( |
|
882 _L("***! irecordingquelength %d pendingquelength %d ibusyquelength=%d"), QLength(iRecordingQueue), |
|
883 QLength(iPendingQueue), QLength(iBusyQueue)); |
|
884 #endif |
|
885 if (iRState == ERStatePaused && (QLength(iRecordingQueue)+QLength(iPendingQueue)+QLength(iBusyQueue) == 0)) |
|
886 { |
|
887 iRState = ERStateFinishing; |
|
888 RequestCallback(); |
|
889 } |
|
890 } |
|
891 else |
|
892 { |
|
893 aRecorder->RecordData(); |
|
894 } |
|
895 } |
|
896 else |
|
897 { |
|
898 iRecordingQueue.Remove(*aRecorder); |
|
899 iIdleQueue.AddLast(*aRecorder); |
|
900 iRState = ERStateFailed; |
|
901 iObserver.InputError(aError); |
|
902 } |
|
903 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
904 RDebug::Print(_L("<---CAudioInput::BufferError() state=%s rstate=%s"), |
|
905 StateName(iState), RStateName(iRState)); |
|
906 #endif |
|
907 CheckFullInvariant(); |
|
908 } |
|
909 |
|
910 TInt CAudioInput::Callback(TAny* aPtr) |
|
911 { |
|
912 CAudioInput* self = static_cast<CAudioInput*> (aPtr); |
|
913 TRAPD(error,self->AsyncCallbackL()); |
|
914 return error; // TODO really have to handle error |
|
915 } |
|
916 |
|
917 void CAudioInput::RequestCallback() |
|
918 { |
|
919 // ensure iAsyncCallBack is active |
|
920 if (!iAsyncCallBack->IsActive()) |
|
921 { |
|
922 iAsyncCallBack->Call(); |
|
923 } |
|
924 } |
|
925 |
|
926 void CAudioInput::AsyncCallbackL() |
|
927 { |
|
928 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
929 RDebug::Print(_L("--->CAudioInput::AsyncCallbackL() state=%s rstate=%s"), |
|
930 StateName(iState), RStateName(iRState)); |
|
931 #endif |
|
932 CheckFullInvariant(); |
|
933 ASSERT(iState==EStateRecordWait2 || iState==EStateRecordWaitAck2); // should not occur in other states. Actually ignore in 2nd |
|
934 if (iState==EStateRecordWait2) |
|
935 { |
|
936 if (QLength(iPendingQueue)>0) |
|
937 { |
|
938 ASSERT(QLength(iBusyQueue)==0); |
|
939 iState = EStateRecordWaitAck2; // change state prior to callback, in case sync call from callback |
|
940 CRecorder* recorder = QPop(iPendingQueue); |
|
941 iBusyQueue.AddLast(*recorder); |
|
942 UseBuffer(recorder); |
|
943 } |
|
944 else |
|
945 { |
|
946 if (iRState == ERStateFinishing) |
|
947 { |
|
948 ASSERT(QLength(iRecordingQueue)+QLength(iPendingQueue)+QLength(iBusyQueue) == 0); // should be true |
|
949 iRState = ERStateFinished; |
|
950 iObserver.InputFinished(); |
|
951 } |
|
952 } |
|
953 } |
|
954 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
955 RDebug::Print(_L("<---CAudioInput::AsyncCallbackL() state=%s rstate=%s"), |
|
956 StateName(iState), RStateName(iRState)); |
|
957 #endif |
|
958 CheckFullInvariant(); |
|
959 } |
|
960 |
|
961 TInt CAudioInput::GetBufferSizes(TInt& aMinSize, TInt& aMaxSize) |
|
962 { |
|
963 aMinSize = KMinBufferSize; |
|
964 aMaxSize = KMaxBufferSize; |
|
965 return KErrNone; |
|
966 } |
|
967 |
|
968 TInt CAudioInput::SetGain(TInt aGain) |
|
969 { |
|
970 TInt error = KErrNone; // note: silent if in wrong state |
|
971 if (iRecordSoundDevice.Handle()) |
|
972 { |
|
973 // we have to switch from level to dB value |
|
974 if(aGain >=0 && aGain<=KSoundMaxVolume) |
|
975 { |
|
976 error = iRecordSoundDevice.SetVolume(KLinearToDbConstantLookup[aGain]); |
|
977 } |
|
978 else |
|
979 { |
|
980 error = KErrArgument; |
|
981 } |
|
982 } |
|
983 return error; |
|
984 } |
|
985 |
|
986 TInt CAudioInput::SetFormat(const TAudioInputParams& aFormat) |
|
987 { |
|
988 TInt err = KErrNotFound; |
|
989 TCurrentSoundFormatV02Buf formatBuf; |
|
990 TFormatData formatData; |
|
991 |
|
992 delete iConverter; |
|
993 iConverter = NULL; // setting this to NULL indicates we are not using converter. No other flag |
|
994 |
|
995 TInt wantedRate = aFormat.iSampleRate; |
|
996 for (TInt index = 0; index < KNumSampleRates; index++) |
|
997 { |
|
998 if (wantedRate == KRateEnumLookup[index].iRate) |
|
999 { |
|
1000 formatBuf().iRate = KRateEnumLookup[index].iRateEnum; |
|
1001 formatData.iSampleRate = wantedRate; |
|
1002 err = KErrNone; |
|
1003 break; |
|
1004 } |
|
1005 } |
|
1006 |
|
1007 if (err == KErrNone) |
|
1008 { |
|
1009 formatBuf().iChannels = aFormat.iNumChannels; |
|
1010 formatBuf().iEncoding = ESoundEncoding16BitPCM; |
|
1011 formatBuf().iDataFormat = ESoundDataFormatInterleaved; |
|
1012 err = iRecordSoundDevice.SetAudioFormat(formatBuf); |
|
1013 #if defined(SYMBIAN_SOUNDADAPTER_FORCECDRATES) || defined (SYMBIAN_SOUNDADAPTER_FORCESTEREO) |
|
1014 err = KErrNotSupported; // force Negotiate - for debugging |
|
1015 #endif |
|
1016 if (err == KErrNotSupported) |
|
1017 { |
|
1018 // don't support directly. Perhaps can rate convert? |
|
1019 err = NegotiateFormat(aFormat, formatData); |
|
1020 } |
|
1021 } |
|
1022 return err; |
|
1023 } |
|
1024 |
|
1025 TInt CAudioInput::NegotiateFormat(const TAudioInputParams& aFormat, TFormatData &aFormatData) |
|
1026 { |
|
1027 TInt err = KErrNotFound; |
|
1028 TCurrentSoundFormatV02Buf formatBuf; |
|
1029 |
|
1030 TInt origBufferLength = iBufferLength; // cache in case we change |
|
1031 |
|
1032 // find out first what the driver supports |
|
1033 TSoundFormatsSupportedV02Buf supportedFormat; |
|
1034 iRecordSoundDevice.Caps(supportedFormat); |
|
1035 TUint32 supportedRates = supportedFormat().iRates; |
|
1036 #ifdef SYMBIAN_SOUNDADAPTER_FORCECDRATES |
|
1037 supportedRates &= KSoundRate11025Hz | KSoundRate22050Hz |
|
1038 | KSoundRate44100Hz; // only use CD rates - for debugging |
|
1039 #endif |
|
1040 |
|
1041 // For RecordCase: |
|
1042 // We want the next rate above consistently - we go down from this to the requested rate. |
|
1043 // If there is one, we don't support - we _never_ upsample. |
|
1044 // note that the table is given in descending order, so we start with the highest |
|
1045 TInt wantedRate = aFormat.iSampleRate; |
|
1046 TInt takeTheFirst = EFalse; |
|
1047 TInt nextUpValidIndex = -1; |
|
1048 for (TInt index = 0; index < KNumSampleRates; index++) |
|
1049 { |
|
1050 TBool lookingAtRequestedRate = wantedRate |
|
1051 == KRateEnumLookup[index].iRate; |
|
1052 TSoundRate wantedEnum = KRateEnumLookup[index].iRateEnum; |
|
1053 TUint32 equivBitmap = KRateEnumLookup[index].iRateConstant; |
|
1054 TBool isSupported = (equivBitmap & supportedRates) != EFalse; |
|
1055 if (lookingAtRequestedRate || takeTheFirst) |
|
1056 { |
|
1057 if (isSupported) |
|
1058 { |
|
1059 // this rate is supported |
|
1060 formatBuf().iRate = wantedEnum; |
|
1061 aFormatData.iActualRate = KRateEnumLookup[index].iRate; |
|
1062 err = KErrNone; |
|
1063 break; |
|
1064 } |
|
1065 } |
|
1066 else if (!takeTheFirst) |
|
1067 { |
|
1068 // while we are still looking for the rate, want to cache any supported index |
|
1069 // at end of loop, this will be the first rate above ours that is supported |
|
1070 // use for fallback if required |
|
1071 if (isSupported) |
|
1072 { |
|
1073 nextUpValidIndex = index; |
|
1074 } |
|
1075 } |
|
1076 if (lookingAtRequestedRate) |
|
1077 { |
|
1078 // For record we just abort. |
|
1079 break; |
|
1080 } |
|
1081 } |
|
1082 |
|
1083 if (err) |
|
1084 { |
|
1085 // if there is one above the requested rate, use that |
|
1086 if (nextUpValidIndex >= 0) |
|
1087 { |
|
1088 TSoundRate wantedEnum = |
|
1089 KRateEnumLookup[nextUpValidIndex].iRateEnum; |
|
1090 formatBuf().iRate = wantedEnum; |
|
1091 aFormatData.iActualRate = KRateEnumLookup[nextUpValidIndex].iRate; |
|
1092 err = KErrNone; |
|
1093 } |
|
1094 } |
|
1095 |
|
1096 if (err) |
|
1097 { |
|
1098 // should have something! |
|
1099 return err; |
|
1100 } |
|
1101 |
|
1102 aFormatData.iSampleRate = wantedRate; // iSampleRate is our requested/apparent rate, not the device rate. |
|
1103 |
|
1104 TUint32 channelsSupported = supportedFormat().iChannels; |
|
1105 #ifdef SYMBIAN_SOUNDADAPTER_FORCESTEREO |
|
1106 channelsSupported &= KSoundStereoChannel; // don't use mono - for debugging |
|
1107 #endif |
|
1108 |
|
1109 if (aFormat.iNumChannels == 1) |
|
1110 { |
|
1111 aFormatData.iRequestedChannels = 1; |
|
1112 // want mono |
|
1113 if (channelsSupported & KSoundMonoChannel) |
|
1114 { |
|
1115 // mono is supported, as usual |
|
1116 aFormatData.iActualChannels = 1; |
|
1117 } |
|
1118 else if (channelsSupported & KSoundStereoChannel) |
|
1119 { |
|
1120 aFormatData.iActualChannels = 2; |
|
1121 iBufferLength *= 2; // double size, will do stereo->mono |
|
1122 } |
|
1123 else |
|
1124 { |
|
1125 return KErrNotSupported; // should not get this far for real |
|
1126 } |
|
1127 } |
|
1128 else if (aFormat.iNumChannels == 2) |
|
1129 { |
|
1130 aFormatData.iRequestedChannels = 2; |
|
1131 // want stereo |
|
1132 if (channelsSupported & KSoundStereoChannel) |
|
1133 { |
|
1134 // stereo is supported, as usual |
|
1135 aFormatData.iActualChannels = 2; |
|
1136 } |
|
1137 else if (channelsSupported & KSoundMonoChannel) |
|
1138 { |
|
1139 aFormatData.iActualChannels = 1; |
|
1140 iBufferLength /= 2; // halve size, will do mono->stereo |
|
1141 } |
|
1142 else |
|
1143 { |
|
1144 return KErrNotSupported; // should not get this far for real |
|
1145 } |
|
1146 } |
|
1147 else |
|
1148 { |
|
1149 return KErrNotSupported; // unknown number of channels requested! |
|
1150 } |
|
1151 |
|
1152 formatBuf().iChannels = aFormatData.iActualChannels; |
|
1153 |
|
1154 formatBuf().iEncoding = ESoundEncoding16BitPCM; |
|
1155 formatBuf().iDataFormat = ESoundDataFormatInterleaved; |
|
1156 err = iRecordSoundDevice.SetAudioFormat(formatBuf); |
|
1157 |
|
1158 if (!err) |
|
1159 { |
|
1160 ASSERT(!iConverter); // pre-condition at top of function anyway |
|
1161 // when recording we convert from actual to requested |
|
1162 TInt outputRateToUse = aFormatData.iSampleRate; |
|
1163 #ifdef SYMBIAN_SKIP_RESAMPLE_ON_RECORD |
|
1164 // with this macro just channel convert at most |
|
1165 outputRateToUse = aFormatData.iActualRate; |
|
1166 #endif |
|
1167 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG |
|
1168 RDebug::Print(_L("RMdaDevSound::CBody::NegotiateFormat: Convert:CreateL from %d/%d to %d/%d"), |
|
1169 aFormatData.iActualRate, aFormatData.iActualChannels, |
|
1170 aFormatData.iSampleRate, aFormatData.iRequestedChannels); |
|
1171 #endif |
|
1172 TRAP(err, iConverter = CChannelAndSampleRateConverter::CreateL(aFormatData.iActualRate, |
|
1173 aFormatData.iActualChannels, |
|
1174 outputRateToUse, |
|
1175 aFormatData.iRequestedChannels)); |
|
1176 } |
|
1177 if (!err && iConverter) |
|
1178 { |
|
1179 err = iConvBuff.Create(origBufferLength); |
|
1180 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
1181 RDebug::Print(_L("iBufferLength length [%d] iconvbuff length [%d,%d]"), |
|
1182 iBufferLength, iConvBuff.Length(), iConvBuff.MaxLength()); |
|
1183 #endif |
|
1184 } |
|
1185 |
|
1186 return err; |
|
1187 } |
|
1188 |
|
1189 TInt CAudioInput::GetSupportedSampleRates(RArray<TInt>& aSupportedSampleRates) |
|
1190 { |
|
1191 TInt err = KErrNone; |
|
1192 |
|
1193 if (iRecordSoundDevice.Handle()) |
|
1194 { |
|
1195 GetSupportedSampleRates(aSupportedSampleRates, iRecordSoundDevice); |
|
1196 } |
|
1197 else |
|
1198 {//temporarily open the device if we can |
|
1199 RSoundSc tempsound; |
|
1200 err = tempsound.Open(KSoundScRxUnit0); |
|
1201 if (!err) |
|
1202 { |
|
1203 err = GetSupportedSampleRates(aSupportedSampleRates, tempsound); |
|
1204 tempsound.Close(); |
|
1205 } |
|
1206 } |
|
1207 return err; |
|
1208 } |
|
1209 |
|
1210 TInt CAudioInput::GetSupportedSampleRates( |
|
1211 RArray<TInt>& aSupportedSampleRates, RSoundSc& aSoundDevice) |
|
1212 { |
|
1213 ASSERT(aSoundDevice.Handle()); // parent to ensure this is open |
|
1214 |
|
1215 TInt err = KErrNone; |
|
1216 |
|
1217 TSoundFormatsSupportedV02Buf supportedFormat; |
|
1218 aSoundDevice.Caps(supportedFormat); |
|
1219 TUint32 rates = supportedFormat().iRates; |
|
1220 |
|
1221 for (TInt i = KNumSampleRates - 1; i > 0; i--)//min to max |
|
1222 { |
|
1223 if (rates & KRateEnumLookup[i].iRateConstant) |
|
1224 { |
|
1225 err = aSupportedSampleRates.Append(KRateEnumLookup[i].iRate); |
|
1226 if (err) |
|
1227 { |
|
1228 break; |
|
1229 } |
|
1230 } |
|
1231 } |
|
1232 return err; |
|
1233 } |
|
1234 |
|
1235 TInt CAudioInput::QLength(TSglQue<CRecorder>& aQueue) |
|
1236 // count elements in List/Q. Have to use iterator to do this - it seems. |
|
1237 { |
|
1238 TSglQueIter<CRecorder> iter(aQueue); |
|
1239 TInt count=0; |
|
1240 while (iter++) |
|
1241 { |
|
1242 // like old-fashioned C string manipulations. iterate through all members |
|
1243 count++; |
|
1244 } |
|
1245 return count; |
|
1246 } |
|
1247 |
|
1248 CAudioInput::CRecorder* CAudioInput::QPop(TSglQue<CRecorder>& aQueue) |
|
1249 { |
|
1250 CRecorder* recorder = NULL; |
|
1251 if (! aQueue.IsEmpty()) |
|
1252 { |
|
1253 recorder = aQueue.First(); |
|
1254 aQueue.Remove(*recorder); |
|
1255 } |
|
1256 return recorder; |
|
1257 } |
|
1258 |
|
1259 #ifdef _DEBUG |
|
1260 |
|
1261 // these functions are used in invariant checking only |
|
1262 |
|
1263 void CAudioInput::CheckActiveRecorders(TSglQue<CRecorder>& aQueue, TBool aExpected, TInt aPanicCode) |
|
1264 // check that all the elements in the given Q are IsActive() or vice-versa |
|
1265 { |
|
1266 TSglQueIter<CRecorder> iter(aQueue); |
|
1267 |
|
1268 CRecorder* recorder; |
|
1269 while ((recorder=iter++)!=NULL) |
|
1270 { |
|
1271 TBool expected = aExpected != EFalse; // ensure these are either true or false |
|
1272 TBool active = recorder->IsActive() != EFalse; |
|
1273 __ASSERT_DEBUG(expected == active, Panic(aPanicCode)); |
|
1274 } |
|
1275 } |
|
1276 |
|
1277 void CAudioInput::CheckActiveRecorders() |
|
1278 // check that all the elements in the recordingQueue are IsActive() etc |
|
1279 // can't be used as CRecorder::RunL() pre-condition |
|
1280 { |
|
1281 CheckActiveRecorders(iRecordingQueue, ETrue, EPanicBusyRecorderNotActive); |
|
1282 CheckActiveRecorders(iIdleQueue, EFalse, EPanicNonBusyRecorderActive); |
|
1283 CheckActiveRecorders(iPendingQueue, EFalse, EPanicNonBusyRecorderActive); |
|
1284 CheckActiveRecorders(iBusyQueue, EFalse, EPanicNonBusyRecorderActive); |
|
1285 } |
|
1286 |
|
1287 #endif // _DEBUG |
|
1288 |
|
1289 // |
|
1290 // CRecorder |
|
1291 // |
|
1292 |
|
1293 |
|
1294 CAudioInput::CRecorder::CRecorder(CAudioInput& aParent, TInt aIndex) : |
|
1295 CActive(EPriorityStandard), iParent(aParent), iIndex(aIndex) |
|
1296 { |
|
1297 CActiveScheduler::Add(this); |
|
1298 } |
|
1299 |
|
1300 CAudioInput::CRecorder::~CRecorder() |
|
1301 { |
|
1302 Cancel(); |
|
1303 } |
|
1304 |
|
1305 void CAudioInput::CRecorder::Cancel() |
|
1306 { |
|
1307 // this override takes into account that ReleaseBuffer must be called - this is not the |
|
1308 // normal pattern where following Cancel() we're not concerned with the results |
|
1309 if (IsActive()) |
|
1310 { |
|
1311 ASSERT(!BufferHeld()); // if active then buffer held should be clear. don't reset then |
|
1312 CActive::Cancel(); |
|
1313 ReleaseBuffer(ETrue); // release - might have been a successful run! |
|
1314 } |
|
1315 else |
|
1316 { |
|
1317 ReleaseBuffer(); // this will release buffer if still outstanding |
|
1318 } |
|
1319 } |
|
1320 |
|
1321 void CAudioInput::CRecorder::RunL() |
|
1322 { |
|
1323 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
1324 RDebug::Print(_L("--->CAudioInput::CRecorder::RunL(%d, %d)"), Index(), |
|
1325 iStatus.Int()); |
|
1326 #endif |
|
1327 TInt errorOrOffset = iStatus.Int(); // negative -> error. non-negative is offset in chunk |
|
1328 |
|
1329 if (errorOrOffset < 0) |
|
1330 { |
|
1331 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
1332 RDebug::Print(_L("errorOrOffset = [%d]"),errorOrOffset); |
|
1333 #endif |
|
1334 // ReleaseBuffer(ETrue); // calls ReleaseBuffer() on error code. Driver requires this, even though seems wrong |
|
1335 iParent.BufferError(this, errorOrOffset); |
|
1336 } |
|
1337 else |
|
1338 { |
|
1339 ASSERT(!iBufferHeld); |
|
1340 iBufferHeld = ETrue; |
|
1341 |
|
1342 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
1343 RDebug::Print(_L("errorOrOffset = [%d]"),errorOrOffset); |
|
1344 #endif |
|
1345 // If a buffer larger than expected arrives truncate it. |
|
1346 iLength = Min(iLength,iParent.iBufferLength); |
|
1347 iParent.BufferArrives(this); |
|
1348 } |
|
1349 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
1350 RDebug::Print(_L("<---CAudioInput::CRecorder::RunL(%d)"), Index()); |
|
1351 #endif |
|
1352 } |
|
1353 |
|
1354 void CAudioInput::CRecorder::RecordData() |
|
1355 { |
|
1356 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
1357 RDebug::Print(_L("--->CAudioInput::CRecorder::RecordData(%d)"), Index()); |
|
1358 #endif |
|
1359 ASSERT(!iBufferHeld); |
|
1360 Deque(); // ensure we append to the AO queue, so if it comes to it we process oldest request first |
|
1361 CActiveScheduler::Add(this); |
|
1362 iLength = iParent.BufferLength(); // TODO do we have to set this first or is it an OUT param purely |
|
1363 iParent.RecordSoundDevice().RecordData(iStatus, iLength); |
|
1364 SetActive(); |
|
1365 |
|
1366 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
1367 RDebug::Print(_L("###****#####!!!! Buffer length [%d], status [%d] "), iLength, |
|
1368 iStatus.Int()); |
|
1369 #endif |
|
1370 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
1371 RDebug::Print(_L("<---CAudioInput::CRecorder::RecordData(%d)"), Index()); |
|
1372 #endif |
|
1373 } |
|
1374 |
|
1375 void CAudioInput::CRecorder::DoCancel() |
|
1376 { |
|
1377 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
1378 RDebug::Print(_L("--->CAudioInput::CRecorder::DoCancel(%d)"), Index()); |
|
1379 #endif |
|
1380 iParent.RecordSoundDevice().Cancel(iStatus); |
|
1381 #ifdef SYMBIAN_SWCODEC_LOGGING |
|
1382 RDebug::Print(_L("<---CAudioInput::CRecorder::DoCancel(%d)"), Index()); |
|
1383 #endif |
|
1384 } |
|
1385 |
|
1386 void CAudioInput::CRecorder::ReleaseBuffer(TBool aDoAnyway) |
|
1387 { |
|
1388 if (iBufferHeld || aDoAnyway) |
|
1389 { |
|
1390 iParent.RecordSoundDevice().ReleaseBuffer(iStatus.Int()); |
|
1391 iBufferHeld = EFalse; |
|
1392 } |
|
1393 } |
|
1394 |
|
1395 TInt CAudioInput::CRecorder::Index() const |
|
1396 { |
|
1397 return iIndex; |
|
1398 } |
|
1399 |
|
1400 TInt CAudioInput::CRecorder::Length() const |
|
1401 { |
|
1402 return iLength; |
|
1403 } |
|
1404 |
|
1405 TBool CAudioInput::CRecorder::IsBusy() const |
|
1406 { |
|
1407 return IsActive() || BufferHeld(); |
|
1408 } |
|
1409 |
|
1410 TBool CAudioInput::CRecorder::BufferHeld() const |
|
1411 // BufferHeld() means we're in control of a passed buffer |
|
1412 { |
|
1413 return iBufferHeld; |
|
1414 } |
|
1415 |
|
1416 TInt CAudioInput::CRecorder::Offset() const |
|
1417 // If we call this, we've discounted errors so can assert non-negative |
|
1418 { |
|
1419 TInt result = StatusOrOffset(); |
|
1420 ASSERT(result>=0); |
|
1421 return result; |
|
1422 } |
|
1423 |
|
1424 TInt CAudioInput::CRecorder::StatusOrOffset() const |
|
1425 // The iStatus assuming is valid |
|
1426 { |
|
1427 ASSERT(!IsActive()); // or would not be valid |
|
1428 TInt result = iStatus.Int(); |
|
1429 return result; |
|
1430 } |