|
1 // Copyright (c) 2002-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 // |
|
15 |
|
16 #include <mdaaudiooutputstream.h> |
|
17 #include <mda/common/audio.h> |
|
18 #include "mmfclientaudiooutputstream.h" |
|
19 #include "mmfclientaudiostreamutils.h" |
|
20 #include "MmfFifo.h" |
|
21 #include <mmf/common/mmfpaniccodes.h> |
|
22 |
|
23 #define WAIT_FOR_READY_ACTIVE_OBJECTS |
|
24 |
|
25 const TInt KMicroSecsInOneSec = 1000000; |
|
26 const TInt KUnknownVolume = -1; // means "don't set", must be negative |
|
27 const TInt KShutDownTimeInterval = 100000; //100 milli seconds |
|
28 |
|
29 /** |
|
30 * |
|
31 * Static NewL |
|
32 * |
|
33 * @return CMdaAudioOutputStream* |
|
34 * |
|
35 */ |
|
36 EXPORT_C CMdaAudioOutputStream* CMdaAudioOutputStream::NewL(MMdaAudioOutputStreamCallback& aCallback, |
|
37 CMdaServer* /*aServer = NULL*/) |
|
38 { |
|
39 return NewL(aCallback, EMdaPriorityNormal, EMdaPriorityPreferenceTimeAndQuality); |
|
40 } |
|
41 |
|
42 /** |
|
43 * |
|
44 * Static NewL |
|
45 * |
|
46 * @return CMdaAudioOutputStream* |
|
47 * |
|
48 */ |
|
49 EXPORT_C CMdaAudioOutputStream* CMdaAudioOutputStream::NewL(MMdaAudioOutputStreamCallback& aCallback, |
|
50 TInt aPriority, |
|
51 TInt aPref /*=EMdaPriorityPreferenceTimeAndQuality*/) |
|
52 { |
|
53 CMdaAudioOutputStream* self = new(ELeave) CMdaAudioOutputStream(); |
|
54 CleanupStack::PushL(self); |
|
55 self->iProperties = CMMFMdaAudioOutputStream::NewL(aCallback, aPriority, aPref); |
|
56 CleanupStack::Pop(self); |
|
57 return self; |
|
58 } |
|
59 |
|
60 CMdaAudioOutputStream::CMdaAudioOutputStream() |
|
61 { |
|
62 } |
|
63 |
|
64 CMdaAudioOutputStream::~CMdaAudioOutputStream() |
|
65 { |
|
66 delete iProperties; |
|
67 } |
|
68 |
|
69 void CMdaAudioOutputStream::SetAudioPropertiesL(TInt aSampleRate, TInt aChannels) |
|
70 { |
|
71 ASSERT(iProperties); |
|
72 iProperties->SetAudioPropertiesL(aSampleRate, aChannels); |
|
73 } |
|
74 |
|
75 void CMdaAudioOutputStream::Open(TMdaPackage* aSettings) |
|
76 { |
|
77 ASSERT(iProperties); |
|
78 iProperties->Open(aSettings); |
|
79 } |
|
80 |
|
81 TInt CMdaAudioOutputStream::MaxVolume() |
|
82 { |
|
83 ASSERT(iProperties); |
|
84 return iProperties->MaxVolume(); |
|
85 } |
|
86 |
|
87 TInt CMdaAudioOutputStream::Volume() |
|
88 { |
|
89 ASSERT(iProperties); |
|
90 return iProperties->Volume(); |
|
91 } |
|
92 |
|
93 void CMdaAudioOutputStream::SetVolume(const TInt aNewVolume) |
|
94 { |
|
95 ASSERT(iProperties); |
|
96 iProperties->SetVolume(aNewVolume); |
|
97 } |
|
98 |
|
99 void CMdaAudioOutputStream::SetPriority(TInt aPriority, TInt aPref) |
|
100 { |
|
101 ASSERT(iProperties); |
|
102 iProperties->SetPriority(aPriority, aPref); |
|
103 } |
|
104 |
|
105 void CMdaAudioOutputStream::WriteL(const TDesC8& aData) |
|
106 { |
|
107 ASSERT(iProperties); |
|
108 iProperties->WriteL(aData); |
|
109 } |
|
110 |
|
111 void CMdaAudioOutputStream::Stop() |
|
112 { |
|
113 ASSERT(iProperties); |
|
114 iProperties->Stop(); |
|
115 } |
|
116 |
|
117 EXPORT_C TInt CMdaAudioOutputStream::Pause() |
|
118 { |
|
119 ASSERT(iProperties); |
|
120 return iProperties->Pause(); |
|
121 } |
|
122 |
|
123 EXPORT_C TInt CMdaAudioOutputStream::Resume() |
|
124 { |
|
125 ASSERT(iProperties); |
|
126 return iProperties->Resume(); |
|
127 } |
|
128 |
|
129 const TTimeIntervalMicroSeconds& CMdaAudioOutputStream::Position() |
|
130 { |
|
131 ASSERT(iProperties); |
|
132 return iProperties->Position(); |
|
133 } |
|
134 |
|
135 EXPORT_C void CMdaAudioOutputStream::SetBalanceL(TInt aBalance) |
|
136 { |
|
137 ASSERT(iProperties); |
|
138 iProperties->SetBalanceL(aBalance); |
|
139 } |
|
140 |
|
141 EXPORT_C TInt CMdaAudioOutputStream::GetBalanceL() const |
|
142 { |
|
143 ASSERT(iProperties); |
|
144 return iProperties->GetBalanceL(); |
|
145 } |
|
146 |
|
147 EXPORT_C TInt CMdaAudioOutputStream::GetBytes() |
|
148 { |
|
149 ASSERT(iProperties); |
|
150 return iProperties->GetBytes(); |
|
151 } |
|
152 |
|
153 EXPORT_C TInt CMdaAudioOutputStream::KeepOpenAtEnd() |
|
154 { |
|
155 ASSERT(iProperties); |
|
156 return iProperties->KeepOpenAtEnd(); |
|
157 } |
|
158 |
|
159 EXPORT_C TInt CMdaAudioOutputStream::RequestStop() |
|
160 { |
|
161 ASSERT(iProperties); |
|
162 return iProperties->RequestStop(); |
|
163 } |
|
164 |
|
165 /** |
|
166 |
|
167 */ |
|
168 EXPORT_C void CMdaAudioOutputStream::SetDataTypeL(TFourCC aAudioType) |
|
169 { |
|
170 ASSERT(iProperties); |
|
171 iProperties->SetDataTypeL(aAudioType); |
|
172 } |
|
173 |
|
174 |
|
175 /** |
|
176 |
|
177 */ |
|
178 EXPORT_C TFourCC CMdaAudioOutputStream::DataType() const |
|
179 { |
|
180 ASSERT(iProperties); |
|
181 return iProperties->DataType(); |
|
182 } |
|
183 |
|
184 EXPORT_C TAny* CMdaAudioOutputStream::CustomInterface(TUid aInterfaceId) |
|
185 { |
|
186 ASSERT(iProperties); |
|
187 return iProperties->CustomInterface(aInterfaceId); |
|
188 } |
|
189 |
|
190 |
|
191 /** |
|
192 Registers the Event for Notification when resource is avaliable. |
|
193 |
|
194 @param aCallback |
|
195 The audio player observer interface. |
|
196 @param aNotificationEventUid |
|
197 The event uid to notify the client. |
|
198 @param aNotificationRegistrationData |
|
199 Notification registration specific data. |
|
200 |
|
201 @return An error code indicating if the registration was successful. KErrNone on success, |
|
202 otherwise another of the system-wide error codes. |
|
203 */ |
|
204 EXPORT_C TInt CMdaAudioOutputStream::RegisterAudioResourceNotification(MMMFAudioResourceNotificationCallback& aCallback, |
|
205 TUid aNotificationEventUid, |
|
206 const TDesC8& aNotificationRegistrationData) |
|
207 { |
|
208 ASSERT(iProperties); |
|
209 return iProperties->RegisterAudioResourceNotification(aCallback,aNotificationEventUid,aNotificationRegistrationData); |
|
210 } |
|
211 |
|
212 /** |
|
213 Cancels the registered notification event. |
|
214 |
|
215 @param aNotificationEventUid |
|
216 The Event to notify the client. |
|
217 |
|
218 @return An error code indicating if the registration was successful. KErrNone on success, |
|
219 otherwise another of the system-wide error codes. |
|
220 */ |
|
221 EXPORT_C TInt CMdaAudioOutputStream::CancelRegisterAudioResourceNotification(TUid aNotificationEventUid) |
|
222 { |
|
223 ASSERT(iProperties); |
|
224 return iProperties->CancelRegisterAudioResourceNotification(aNotificationEventUid); |
|
225 } |
|
226 |
|
227 /** |
|
228 Waits for the client to resume the play even after the default timer expires. |
|
229 |
|
230 @return An error code indicating if the registration was successful. KErrNone on success, |
|
231 otherwise another of the system-wide error codes. |
|
232 */ |
|
233 EXPORT_C TInt CMdaAudioOutputStream::WillResumePlay() |
|
234 { |
|
235 ASSERT(iProperties); |
|
236 return iProperties->WillResumePlay(); |
|
237 } |
|
238 |
|
239 enum TMmAosPanic |
|
240 { |
|
241 EToneFinishedNotSupported, |
|
242 EBufferToBeEmptiedNotSupported, |
|
243 ERecordErrorNotSupported, |
|
244 EConvertErrorNotSupported, |
|
245 EDeviceMessageNotSupported, |
|
246 EReservedCall, |
|
247 EAOSStoppingError |
|
248 }; |
|
249 |
|
250 _LIT(KMmfMdaAosCategory, "CMmfMdaAudioOutputStream"); |
|
251 LOCAL_C void Panic(const TMmAosPanic aReason) |
|
252 { |
|
253 User::Panic(KMmfMdaAosCategory, aReason); |
|
254 } |
|
255 |
|
256 /** |
|
257 * |
|
258 * 2 phase construction function |
|
259 * |
|
260 */ |
|
261 CMMFMdaAudioOutputStream* CMMFMdaAudioOutputStream::NewL(MMdaAudioOutputStreamCallback& aCallback) |
|
262 { |
|
263 return NewL(aCallback, EMdaPriorityNormal, EMdaPriorityPreferenceTimeAndQuality); |
|
264 } |
|
265 |
|
266 CMMFMdaAudioOutputStream* CMMFMdaAudioOutputStream::NewL(MMdaAudioOutputStreamCallback& aCallback, TInt aPriority, TInt aPref) |
|
267 { |
|
268 CMMFMdaAudioOutputStream* self = new(ELeave) CMMFMdaAudioOutputStream(aCallback); |
|
269 CleanupStack::PushL(self); |
|
270 self->ConstructL(aPriority, aPref); |
|
271 CleanupStack::Pop(); // self |
|
272 return self; |
|
273 } |
|
274 |
|
275 /** |
|
276 * |
|
277 * Construct |
|
278 * |
|
279 * @param "MMdaAudioOutputStreamCallback&" |
|
280 * a reference to MMdaAudioOutputStreamCallback |
|
281 * @param "TInt aPriority" |
|
282 * a priority value |
|
283 * @param "TInt aPref" |
|
284 * a perference value |
|
285 * |
|
286 */ |
|
287 CMMFMdaAudioOutputStream::CMMFMdaAudioOutputStream(MMdaAudioOutputStreamCallback& aCallback) |
|
288 : iCallback(aCallback), iState(EStopped) |
|
289 { |
|
290 iDataTypeCode.Set(TFourCC(' ','P','1','6')); |
|
291 } |
|
292 |
|
293 /** |
|
294 * |
|
295 * Second phase constructor |
|
296 * |
|
297 */ |
|
298 void CMMFMdaAudioOutputStream::ConstructL(TInt aPriority, TInt aPref) |
|
299 { |
|
300 iDevSound = CMMFDevSound::NewL(); |
|
301 SetPriority(aPriority, aPref); |
|
302 iDevSoundIgnoresUnderflow = iDevSound->QueryIgnoresUnderflow(); |
|
303 iFifo = new(ELeave) CMMFFifo<const TDesC8>(); |
|
304 iActiveCallback = new(ELeave) CActiveCallback(iCallback); |
|
305 iActiveSchedulerWait = new(ELeave) CActiveSchedulerWait; |
|
306 iShutDownTimer = CPeriodic::NewL(CActive::EPriorityStandard); |
|
307 } |
|
308 |
|
309 /** |
|
310 * |
|
311 * Destructor |
|
312 * |
|
313 */ |
|
314 CMMFMdaAudioOutputStream::~CMMFMdaAudioOutputStream() |
|
315 { |
|
316 delete iFifo; |
|
317 delete iDevSound; |
|
318 delete iActiveCallback; |
|
319 delete iActiveSchedulerWait; |
|
320 delete iShutDownTimer; |
|
321 } |
|
322 |
|
323 /** |
|
324 * |
|
325 * Set audio output stream properties |
|
326 * |
|
327 * @param "TInt aSampleRate" |
|
328 * a specified priority value |
|
329 * @param "TInt aChannels" |
|
330 * a specified preference value |
|
331 * |
|
332 */ |
|
333 void CMMFMdaAudioOutputStream::SetAudioPropertiesL(TInt aSampleRate, TInt aChannels) |
|
334 { |
|
335 if (iIsOpenState==EIsOpen) |
|
336 { |
|
337 RealSetAudioPropertiesL(aSampleRate, aChannels); |
|
338 } |
|
339 else |
|
340 { |
|
341 // cache params for application later |
|
342 iSampleRate = aSampleRate; |
|
343 iChannels = aChannels; |
|
344 iVolume = KUnknownVolume; |
|
345 iValuesCached = ETrue; |
|
346 } |
|
347 } |
|
348 |
|
349 void CMMFMdaAudioOutputStream::RealSetAudioPropertiesL(TInt aSampleRate, TInt aChannels) |
|
350 { |
|
351 TMMFCapabilities config = iDevSound->Config(); |
|
352 config.iChannels = StreamUtils::MapChannelsMdaToMMFL(aChannels); |
|
353 config.iRate = StreamUtils::MapSampleRateMdaToMMFL(aSampleRate); |
|
354 iDevSound->SetConfigL(config); |
|
355 } |
|
356 |
|
357 /** |
|
358 * |
|
359 * Open a audio ouptut stream |
|
360 * |
|
361 * @param "TMdaPackage* Settings" |
|
362 * a pointer point to TMdaPackage |
|
363 * |
|
364 */ |
|
365 void CMMFMdaAudioOutputStream::Open(TMdaPackage* aSettings) |
|
366 { |
|
367 iIsOpenState = EIsOpening; |
|
368 |
|
369 // Use settings to set audio properties after the dev sound has been |
|
370 // successfully initialised. Note if the InitializeL() fails, something calls |
|
371 // InitializeComplete() and the iValuesCached flag is cleared. Also note |
|
372 // that if SetAudioPropertiesL() has already been called, there may already |
|
373 // be cached values |
|
374 if (aSettings && aSettings->Type().iUid == KUidMdaDataTypeSettingsDefine) |
|
375 { |
|
376 TMdaAudioDataSettings& audioSettings = *STATIC_CAST(TMdaAudioDataSettings*, aSettings); |
|
377 //Caching these values, which are later set in InitializeComplete |
|
378 iSampleRate = audioSettings.iSampleRate; |
|
379 iChannels = audioSettings.iChannels; |
|
380 iVolume = audioSettings.iVolume; |
|
381 iValuesCached = ETrue; |
|
382 } |
|
383 |
|
384 TRAPD(err, iDevSound->InitializeL(*this, iDataTypeCode, EMMFStatePlaying)); |
|
385 if (err != KErrNone) |
|
386 { |
|
387 InitializeComplete(err); |
|
388 } |
|
389 } |
|
390 |
|
391 /** |
|
392 * |
|
393 * To get the maximum volume level |
|
394 * |
|
395 * @return "TInt" |
|
396 * the maximum volume value in integer |
|
397 * |
|
398 */ |
|
399 TInt CMMFMdaAudioOutputStream::MaxVolume() |
|
400 { |
|
401 return iDevSound->MaxVolume(); |
|
402 } |
|
403 |
|
404 /** |
|
405 * |
|
406 * To get the current volume level |
|
407 * |
|
408 * @return "TInt" |
|
409 * the current volume value in integer |
|
410 * |
|
411 */ |
|
412 TInt CMMFMdaAudioOutputStream::Volume() |
|
413 { |
|
414 return iDevSound->Volume(); |
|
415 } |
|
416 |
|
417 /** |
|
418 * |
|
419 * Set audio output stream volume to the specified value |
|
420 * |
|
421 * @param "TInt aVolume" |
|
422 * a specified volume value |
|
423 * |
|
424 */ |
|
425 void CMMFMdaAudioOutputStream::SetVolume(TInt aVolume) |
|
426 { |
|
427 iDevSound->SetVolume(aVolume); |
|
428 } |
|
429 |
|
430 /** |
|
431 * |
|
432 * Set audio output stream balance |
|
433 * |
|
434 * @param "TInt aBalance" |
|
435 * a specified balance value |
|
436 * |
|
437 */ |
|
438 void CMMFMdaAudioOutputStream::SetBalanceL(TInt aBalance) |
|
439 { |
|
440 // test and clip balance to min / max range [-100 <-> 100] |
|
441 // clip rather than leave as this isn't a leaving function |
|
442 if (aBalance < KMMFBalanceMaxLeft) aBalance = KMMFBalanceMaxLeft; |
|
443 if (aBalance > KMMFBalanceMaxRight) aBalance = KMMFBalanceMaxRight; |
|
444 |
|
445 // separate out left and right balance |
|
446 TInt left = 0; |
|
447 TInt right = 0; |
|
448 StreamUtils::CalculateLeftRightBalance( left, right, aBalance ); |
|
449 |
|
450 // send the balance to SoundDevice |
|
451 iDevSound->SetPlayBalanceL(left, right); |
|
452 } |
|
453 |
|
454 /** |
|
455 * |
|
456 * To get the current balance value. This function may not return the same value |
|
457 * as passed to SetBalanceL depending on the internal implementation in |
|
458 * the underlying components. |
|
459 * |
|
460 * @return "TInt" |
|
461 * the current balance value in integer |
|
462 * |
|
463 */ |
|
464 TInt CMMFMdaAudioOutputStream::GetBalanceL() const |
|
465 { |
|
466 TInt rightBalance = 0; |
|
467 TInt leftBalance = 0; |
|
468 iDevSound->GetPlayBalanceL(leftBalance, rightBalance); |
|
469 TInt balance = 0; |
|
470 StreamUtils::CalculateBalance( balance, leftBalance, rightBalance ); |
|
471 return balance; |
|
472 } |
|
473 |
|
474 /** |
|
475 * |
|
476 * Set audio output stream priority |
|
477 * |
|
478 * @param "TInt aPriority" |
|
479 * a specified priority value |
|
480 * @param "TMdaPriorityPreference aPref" |
|
481 * a specified preference value |
|
482 * |
|
483 */ |
|
484 void CMMFMdaAudioOutputStream::SetPriority(TInt aPriority, TInt aPref) |
|
485 { |
|
486 TMMFPrioritySettings settings; |
|
487 settings.iPriority = aPriority; |
|
488 settings.iPref = aPref; |
|
489 iDevSound->SetPrioritySettings(settings); |
|
490 } |
|
491 |
|
492 /** |
|
493 * |
|
494 * To write data to output stream |
|
495 * Note that if framed data eg gsm610 is being streamed then the buffer |
|
496 * size of aData should contain an intiger number of frames |
|
497 * |
|
498 * @param "const TDesC8& aData" |
|
499 * a stream data |
|
500 * |
|
501 */ |
|
502 void CMMFMdaAudioOutputStream::WriteL(const TDesC8& aData) |
|
503 { |
|
504 if(iState==EStopping) |
|
505 { |
|
506 User::Leave(KErrNotReady); |
|
507 } |
|
508 iShutDownTimer->Cancel(); |
|
509 |
|
510 TMMFFifoItem<const TDesC8>* item = new(ELeave) TMMFFifoItem<const TDesC8>(aData); |
|
511 iFifo->AddToFifo(*item); |
|
512 |
|
513 if(iState == EStopped) |
|
514 StartPlayL(); |
|
515 else if((iState == EPlaying) && (iBuffer != NULL)) |
|
516 { //if we are playing and we have a buffer waiting for data, use it. |
|
517 BufferToBeFilled(iBuffer); |
|
518 iBuffer = NULL; |
|
519 } |
|
520 if(iEventHolder != KNullUid) |
|
521 { |
|
522 TInt err = iDevSound->RegisterAsClient(iEventHolder,iNotificationDataHolder); |
|
523 iEventHolder = KNullUid; |
|
524 iNotificationDataHolder = KNullDesC8; |
|
525 if(err != KErrNone) |
|
526 { |
|
527 iCallback.MaoscPlayComplete(err); |
|
528 } |
|
529 } |
|
530 } |
|
531 |
|
532 /** |
|
533 * |
|
534 * If the audio stream is stopped, then notify the CDevSound to initilised play. |
|
535 * The CDevSound will automatically start calling back for buffers. |
|
536 */ |
|
537 void CMMFMdaAudioOutputStream::StartPlayL() |
|
538 { |
|
539 if (iState == EStopped) |
|
540 { |
|
541 iFifoItemPos = 0; |
|
542 iCurrentSamplesPlayed = 0; |
|
543 iDevSound->PlayInitL(); |
|
544 iState = EPlaying; |
|
545 iBuffer = NULL; |
|
546 } |
|
547 } |
|
548 |
|
549 |
|
550 /** |
|
551 * |
|
552 * To stop write data to stream |
|
553 * |
|
554 */ |
|
555 void CMMFMdaAudioOutputStream::Stop() |
|
556 { |
|
557 iShutDownTimer->Cancel(); |
|
558 EmptyFifo(KErrAbort); |
|
559 |
|
560 if(iState == EStopped) |
|
561 { |
|
562 return; |
|
563 } |
|
564 |
|
565 // stop the operation |
|
566 iDevSound->Stop(); |
|
567 iState = EStopped; |
|
568 iBuffer = NULL; |
|
569 |
|
570 iCallback.MaoscPlayComplete(KErrCancel); |
|
571 } |
|
572 |
|
573 TInt CMMFMdaAudioOutputStream::RequestStop() |
|
574 { |
|
575 if(!iKeepOpenAtEnd) |
|
576 { |
|
577 return KErrNotSupported; |
|
578 } |
|
579 |
|
580 if(iState==EStopped || iState==EStopping) |
|
581 { |
|
582 return KErrNotReady; |
|
583 } |
|
584 |
|
585 if(iBuffer) |
|
586 {//means all the client buffers are used up and waiting for more data |
|
587 CMMFDataBuffer* dataBuffer = static_cast<CMMFDataBuffer*>(iBuffer); |
|
588 dataBuffer->Data().SetLength(0); |
|
589 dataBuffer->SetLastBuffer(ETrue); |
|
590 iDevSound->PlayData(); |
|
591 iBuffer=NULL; |
|
592 } |
|
593 iState = EStopping; |
|
594 return KErrNone; |
|
595 } |
|
596 |
|
597 TInt CMMFMdaAudioOutputStream::KeepOpenAtEnd() |
|
598 { |
|
599 if(!iDevSoundIgnoresUnderflow) |
|
600 { |
|
601 return KErrNotSupported; |
|
602 } |
|
603 else |
|
604 { |
|
605 iKeepOpenAtEnd = ETrue; |
|
606 return KErrNone; |
|
607 } |
|
608 } |
|
609 |
|
610 /** |
|
611 * |
|
612 * To pause send data to stream |
|
613 * |
|
614 */ |
|
615 TInt CMMFMdaAudioOutputStream::Pause() |
|
616 { |
|
617 if(iState != EPlaying) |
|
618 { |
|
619 return KErrNotReady; |
|
620 } |
|
621 |
|
622 else if(!iDevSound->IsResumeSupported()) |
|
623 { |
|
624 return KErrNotSupported; |
|
625 } |
|
626 |
|
627 // pause the operation |
|
628 iDevSound->Pause(); |
|
629 iState = EPaused; |
|
630 return KErrNone; |
|
631 } |
|
632 |
|
633 /** |
|
634 * |
|
635 * To resume send data to stream |
|
636 * |
|
637 */ |
|
638 TInt CMMFMdaAudioOutputStream::Resume() |
|
639 { |
|
640 TInt err = KErrNone; |
|
641 if(iState != EPaused) |
|
642 { |
|
643 err = KErrNotReady; |
|
644 } |
|
645 else if(!iDevSound->IsResumeSupported()) |
|
646 { |
|
647 err = KErrNotSupported; |
|
648 } |
|
649 |
|
650 // resume the operation |
|
651 if(err == KErrNone) |
|
652 { |
|
653 err = iDevSound->Resume(); |
|
654 } |
|
655 if(err == KErrNone) |
|
656 { |
|
657 iState = EPlaying; |
|
658 } |
|
659 |
|
660 return err; |
|
661 } |
|
662 |
|
663 |
|
664 /** |
|
665 * |
|
666 * To get the current position in the data stream |
|
667 * |
|
668 * @return "TTimeIntervalMicroSeconds&" |
|
669 * the current position in integer |
|
670 * |
|
671 */ |
|
672 const TTimeIntervalMicroSeconds& CMMFMdaAudioOutputStream::Position() |
|
673 { |
|
674 TInt64 position = iDevSound->SamplesPlayed(); |
|
675 position = position * KMicroSecsInOneSec / StreamUtils::SampleRateAsValue(iDevSound->Config()); |
|
676 iPosition = (iState == EPlaying || iState == EStopping || iState == EPaused) ? position : 0; // Shouldn't need to check for playing but CMMFDevSound doesn't reset bytes played after a stop |
|
677 return iPosition; |
|
678 } |
|
679 |
|
680 /** |
|
681 * |
|
682 * To return the current number of bytes rendered by audio hardware |
|
683 * @return "the current current number of bytes rendered by audio hardware in integer" |
|
684 * |
|
685 */ |
|
686 TInt CMMFMdaAudioOutputStream::GetBytes() |
|
687 { |
|
688 return iDevSound->SamplesPlayed() * StreamUtils::BytesPerSample(iDevSound->Config()); |
|
689 } |
|
690 |
|
691 /** |
|
692 |
|
693 */ |
|
694 void CMMFMdaAudioOutputStream::SetDataTypeL(TFourCC aAudioType) |
|
695 { |
|
696 if(iState != EStopped) |
|
697 User::Leave(KErrServerBusy); |
|
698 |
|
699 if(aAudioType == iDataTypeCode) |
|
700 return; |
|
701 |
|
702 TMMFPrioritySettings prioritySettings; |
|
703 prioritySettings.iState = EMMFStatePlaying; |
|
704 RArray<TFourCC> supportedDataTypes; |
|
705 |
|
706 CleanupClosePushL(supportedDataTypes); |
|
707 |
|
708 TRAPD(err, iDevSound->GetSupportedInputDataTypesL(supportedDataTypes, prioritySettings)); |
|
709 |
|
710 if (err == KErrNone) |
|
711 { |
|
712 if (supportedDataTypes.Find(aAudioType) == KErrNotFound) |
|
713 { |
|
714 User::Leave(KErrNotSupported); |
|
715 } |
|
716 //if match, set the 4CC of AudioType to match |
|
717 iDataTypeCode.Set(aAudioType); |
|
718 } |
|
719 else //we had a real leave error from GetSupportedOuputDataTypesL |
|
720 { |
|
721 User::Leave(err); |
|
722 } |
|
723 |
|
724 CleanupStack::PopAndDestroy(&supportedDataTypes); |
|
725 |
|
726 if(iIsOpenState!=EIsNotOpen) |
|
727 { |
|
728 // need to recall or restart InitializeL() process |
|
729 iDevSound->CancelInitialize(); // call just in case mid-InitializeL. No harm if not. |
|
730 // if not supported then assume old DevSound behaviour anyway |
|
731 // where InitializeL() implicitly cancels, so no harm either way |
|
732 iIsOpenState = EIsOpening; |
|
733 iInitCallFrmSetDataType = ETrue; |
|
734 TRAPD(err, iDevSound->InitializeL(*this, iDataTypeCode, EMMFStatePlaying)); |
|
735 if (err != KErrNone) |
|
736 { |
|
737 // Leave if error. |
|
738 iIsOpenState = EIsNotOpen; |
|
739 iInitCallFrmSetDataType = EFalse; |
|
740 User::Leave(err); |
|
741 } |
|
742 // In some implementations InitializeComplete is sent |
|
743 // in context, so check before starting activeSchedulerWait. |
|
744 else if(iIsOpenState == EIsOpening) |
|
745 { |
|
746 iInitializeState = KRequestPending; |
|
747 iActiveSchedulerWait->Start(); |
|
748 } |
|
749 iInitCallFrmSetDataType = EFalse; |
|
750 User::LeaveIfError(iInitializeState); |
|
751 } |
|
752 } |
|
753 |
|
754 /** |
|
755 |
|
756 */ |
|
757 TFourCC CMMFMdaAudioOutputStream::DataType() const |
|
758 { |
|
759 return iDataTypeCode; |
|
760 } |
|
761 |
|
762 TInt CMMFMdaAudioOutputStream::RegisterAudioResourceNotification(MMMFAudioResourceNotificationCallback& aCallback,TUid aNotificationEventUid,const TDesC8& aNotificationRegistrationData) |
|
763 { |
|
764 iAudioResourceNotificationCallBack = &aCallback; |
|
765 TInt err = iDevSound->RegisterAsClient(aNotificationEventUid,aNotificationRegistrationData); |
|
766 if(err == KErrNotReady) |
|
767 { |
|
768 iEventHolder = aNotificationEventUid; |
|
769 iNotificationDataHolder = aNotificationRegistrationData; |
|
770 return KErrNone; |
|
771 } |
|
772 iEventHolder = KNullUid; |
|
773 iNotificationDataHolder = KNullDesC8; |
|
774 return err; |
|
775 } |
|
776 |
|
777 TInt CMMFMdaAudioOutputStream::CancelRegisterAudioResourceNotification(TUid aNotificationEventId) |
|
778 { |
|
779 TInt err = iDevSound->CancelRegisterAsClient(aNotificationEventId); |
|
780 if(err == KErrNotReady) |
|
781 { |
|
782 if(aNotificationEventId != KMMFEventCategoryAudioResourceAvailable) |
|
783 { |
|
784 return KErrNotSupported; |
|
785 } |
|
786 if(iEventHolder == KNullUid) |
|
787 { |
|
788 return KErrCancel; |
|
789 } |
|
790 iEventHolder = KNullUid; |
|
791 iNotificationDataHolder = KNullDesC8; |
|
792 return KErrNone; |
|
793 } |
|
794 return err; |
|
795 } |
|
796 |
|
797 TInt CMMFMdaAudioOutputStream::WillResumePlay() |
|
798 { |
|
799 return iDevSound->WillResumePlay(); |
|
800 } |
|
801 |
|
802 /** |
|
803 * |
|
804 * To be called when intialize stream complete |
|
805 * |
|
806 * @param "TInt aError" |
|
807 * error code, initialize stream succeed when aError = 0 |
|
808 * |
|
809 */ |
|
810 void CMMFMdaAudioOutputStream::InitializeComplete(TInt aError) |
|
811 { |
|
812 TInt err = aError; |
|
813 if(err == KErrNone && iValuesCached) |
|
814 { |
|
815 TRAP(err, RealSetAudioPropertiesL(iSampleRate, iChannels)); |
|
816 if(err == KErrNone && iVolume>=0) |
|
817 { |
|
818 SetVolume(iVolume); |
|
819 } |
|
820 } |
|
821 iValuesCached = EFalse; // whatever clear our cache |
|
822 if(iIsOpenState == EIsOpening) |
|
823 { |
|
824 // Signal for the MaoscOpenComplete callback to be called asynchronously.Ignore if InitializeL is called from SetDataTypeL |
|
825 if(!iInitCallFrmSetDataType) |
|
826 { |
|
827 iActiveCallback->Signal(err); |
|
828 } |
|
829 iIsOpenState = err ? EIsNotOpen : EIsOpen; |
|
830 if(iInitializeState == KRequestPending) |
|
831 { |
|
832 iInitializeState = err; |
|
833 iActiveSchedulerWait->AsyncStop(); |
|
834 } |
|
835 else |
|
836 { |
|
837 iInitializeState = err;//Set the error if InitializeComplete is called in context of InitializeL. |
|
838 } |
|
839 } |
|
840 } |
|
841 |
|
842 /** |
|
843 * |
|
844 * Do not support |
|
845 * |
|
846 */ |
|
847 void CMMFMdaAudioOutputStream::ToneFinished(TInt /*aError*/) |
|
848 { |
|
849 Panic(EToneFinishedNotSupported); |
|
850 } |
|
851 |
|
852 /** |
|
853 * |
|
854 * Called when sound device need data |
|
855 * |
|
856 * @param "CMMFBuffer* aBuffer" |
|
857 * a pointer point to CMMFBuffer, which is used to stored data |
|
858 * |
|
859 */ |
|
860 void CMMFMdaAudioOutputStream::BufferToBeFilled(CMMFBuffer* aBuffer) |
|
861 { |
|
862 if (iState == EPlaying || iState == EStopping) |
|
863 { |
|
864 TMMFFifoItem<const TDesC8>* firstItem = iFifo->Get(); |
|
865 //ASSERT firstItem != NULL |
|
866 if (iFifoItemPos >= firstItem->GetData().Length()) |
|
867 { |
|
868 // We've played all segments of the first buffer in the fifo so we can delete it |
|
869 iFifo->RemoveFirstItem(); |
|
870 iCallback.MaoscBufferCopied(KErrNone, firstItem->GetData()); |
|
871 delete firstItem; |
|
872 iFifoItemPos = 0; |
|
873 } |
|
874 } |
|
875 TMMFFifoItem<const TDesC8>* firstItem = iFifo->Get(); |
|
876 |
|
877 if (firstItem) |
|
878 { |
|
879 // Fill aBuffer with the next segment of the first buffer in the fifo |
|
880 TDes8& fillDes = STATIC_CAST(CMMFDataBuffer*, aBuffer)->Data(); |
|
881 const TDesC8& readDes = firstItem->GetData(); |
|
882 TInt readLen = Min(readDes.Length()-iFifoItemPos, aBuffer->RequestSize()); |
|
883 fillDes = readDes.Mid(iFifoItemPos, readLen); |
|
884 iFifoItemPos+=readLen; // so we know where the next segment in the fifo item starts |
|
885 // Notify iDevSound the buffer is ready to be played |
|
886 iDevSound->PlayData(); |
|
887 } |
|
888 else |
|
889 { |
|
890 if(iBuffer ==NULL) |
|
891 { |
|
892 //keep a record of the supplied buffer and use it when/if more user data is in the FIFO |
|
893 iBuffer = aBuffer; |
|
894 } |
|
895 if(!iKeepOpenAtEnd) |
|
896 { |
|
897 if (iDevSoundIgnoresUnderflow) |
|
898 { |
|
899 iCurrentSamplesPlayed = iDevSound->SamplesPlayed(); |
|
900 StartShutDownTimer(); |
|
901 } |
|
902 } |
|
903 else if(iState==EStopping) |
|
904 { |
|
905 CMMFDataBuffer* dataBuffer = static_cast<CMMFDataBuffer*>(aBuffer); |
|
906 dataBuffer->Data().SetLength(0); |
|
907 dataBuffer->SetLastBuffer(ETrue); |
|
908 iDevSound->PlayData(); |
|
909 iBuffer=NULL; |
|
910 } |
|
911 } |
|
912 |
|
913 } |
|
914 |
|
915 void CMMFMdaAudioOutputStream::StartShutDownTimer() |
|
916 { |
|
917 iShutDownTimer->Start(KShutDownTimeInterval, KShutDownTimeInterval, TCallBack(ShutDownTimerComplete,this)); |
|
918 } |
|
919 |
|
920 TInt CMMFMdaAudioOutputStream::ShutDownTimerComplete(TAny* aAudioOutputStream) |
|
921 { |
|
922 CMMFMdaAudioOutputStream* audioOutputStream = static_cast<CMMFMdaAudioOutputStream*>(aAudioOutputStream); |
|
923 audioOutputStream->DoShutDownTimerComplete(); |
|
924 return KErrNone; |
|
925 } |
|
926 |
|
927 void CMMFMdaAudioOutputStream::DoShutDownTimerComplete() |
|
928 { |
|
929 iShutDownTimer->Cancel(); |
|
930 TInt samplesPlayed = iDevSound->SamplesPlayed(); |
|
931 if (samplesPlayed == iCurrentSamplesPlayed) |
|
932 { |
|
933 iDevSound->Stop(); |
|
934 iState = EStopped; |
|
935 iBuffer = NULL; |
|
936 iCallback.MaoscPlayComplete(KErrUnderflow); |
|
937 } |
|
938 else |
|
939 {//desvound has not yet finished playing all the data. So wait for one more cycle |
|
940 //Restart Timer |
|
941 iCurrentSamplesPlayed = samplesPlayed; |
|
942 StartShutDownTimer(); |
|
943 } |
|
944 } |
|
945 |
|
946 /** |
|
947 * |
|
948 * Called when play operation complete, successfully or otherwise |
|
949 * |
|
950 * @param "TInt aError" |
|
951 * an error value which will indicate playing successfully complete |
|
952 * if error value is 0 |
|
953 * |
|
954 */ |
|
955 void CMMFMdaAudioOutputStream::PlayError(TInt aError) |
|
956 { |
|
957 TInt err=aError; |
|
958 // if iDevSoundIgnoresUnderflow is true, then KErrUnderflow should not be produced by DevSound when we are not stopping |
|
959 __ASSERT_DEBUG(!iDevSoundIgnoresUnderflow || !(err==KErrUnderflow && iState!=EStopping), Panic(EAOSStoppingError)); |
|
960 iState = EStopped; |
|
961 iBuffer = NULL; |
|
962 iShutDownTimer->Cancel(); |
|
963 if (err == KErrNone || err == KErrUnderflow) |
|
964 { |
|
965 if (!iFifo->IsEmpty()) |
|
966 { |
|
967 // We live again - the Fifo still has some data. The sound device |
|
968 // will have to be opened again though so there might be an |
|
969 // audible click. |
|
970 TRAP(err, StartPlayL()); |
|
971 if (err == KErrNone) |
|
972 { |
|
973 return; |
|
974 } |
|
975 } |
|
976 } |
|
977 |
|
978 EmptyFifo(err); |
|
979 |
|
980 // Note - KErrUnderflow will be reported even when all the buffers have |
|
981 // successfully played |
|
982 iCallback.MaoscPlayComplete(err); |
|
983 } |
|
984 |
|
985 void CMMFMdaAudioOutputStream::EmptyFifo(TInt aError) |
|
986 { |
|
987 // Delete all buffers in the fifo and notify the observer |
|
988 TMMFFifoItem<const TDesC8>* firstItem; |
|
989 while((firstItem = iFifo->Get()) != NULL) |
|
990 { |
|
991 iFifo->RemoveFirstItem(); |
|
992 iCallback.MaoscBufferCopied(aError, firstItem->GetData()); |
|
993 delete firstItem; |
|
994 } |
|
995 } |
|
996 |
|
997 void CMMFMdaAudioOutputStream::SendEventToClient(const TMMFEvent& aEvent) |
|
998 { |
|
999 if (aEvent.iEventType == KMMFEventCategoryAudioResourceAvailable) |
|
1000 { |
|
1001 // Retrieve the number of samples played |
|
1002 // For the event type KMMFEventCategoryAudioResourceAvailable GetResourceNotificationData() returns |
|
1003 // a package buffer as TMMFTimeIntervalMicroSecondsPckg, but the contents should be |
|
1004 // converted to an integer and interpreted as the data returned is samples played, |
|
1005 // but not as a microsecond value. |
|
1006 TBuf8<TMMFAudioConfig::KNotificationDataBufferSize> notificationData; |
|
1007 if (KErrNone != iDevSound->GetResourceNotificationData(aEvent.iEventType, notificationData)) |
|
1008 { |
|
1009 notificationData.SetLength(0); |
|
1010 } |
|
1011 iAudioResourceNotificationCallBack->MarncResourceAvailable(aEvent.iEventType, notificationData); |
|
1012 } |
|
1013 } |
|
1014 |
|
1015 CMMFMdaAudioOutputStream::CActiveCallback::~CActiveCallback() |
|
1016 { |
|
1017 Cancel(); |
|
1018 } |
|
1019 |
|
1020 CMMFMdaAudioOutputStream::CActiveCallback::CActiveCallback(MMdaAudioOutputStreamCallback& aCallback) |
|
1021 : CActive(EPriorityStandard), iCallback(aCallback) |
|
1022 { |
|
1023 CActiveScheduler::Add(this); |
|
1024 } |
|
1025 |
|
1026 void CMMFMdaAudioOutputStream::CActiveCallback::RunL() |
|
1027 { |
|
1028 iCallback.MaoscOpenComplete(iStatus.Int()); |
|
1029 } |
|
1030 |
|
1031 void CMMFMdaAudioOutputStream::CActiveCallback::DoCancel() |
|
1032 { |
|
1033 } |
|
1034 |
|
1035 void CMMFMdaAudioOutputStream::CActiveCallback::Signal(const TInt aReason) |
|
1036 { |
|
1037 ASSERT(!IsActive()); |
|
1038 |
|
1039 // Signal ourselves to run with the given completion code |
|
1040 TRequestStatus* status = &iStatus; |
|
1041 User::RequestComplete(status, aReason); |
|
1042 SetActive(); |
|
1043 } |
|
1044 |
|
1045 |
|
1046 /** |
|
1047 * |
|
1048 * Do not support |
|
1049 * |
|
1050 */ |
|
1051 void CMMFMdaAudioOutputStream::BufferToBeEmptied(CMMFBuffer* /*aBuffer*/) |
|
1052 { |
|
1053 Panic(EBufferToBeEmptiedNotSupported); |
|
1054 } |
|
1055 |
|
1056 /** |
|
1057 * |
|
1058 * Do not support |
|
1059 * |
|
1060 */ |
|
1061 void CMMFMdaAudioOutputStream::RecordError(TInt /*aError*/) |
|
1062 { |
|
1063 Panic(ERecordErrorNotSupported); |
|
1064 } |
|
1065 |
|
1066 /** |
|
1067 * |
|
1068 * Do not support |
|
1069 * |
|
1070 */ |
|
1071 void CMMFMdaAudioOutputStream::ConvertError(TInt /*aError*/) |
|
1072 { |
|
1073 Panic(EConvertErrorNotSupported); |
|
1074 } |
|
1075 |
|
1076 /** |
|
1077 * |
|
1078 * Do not support |
|
1079 * |
|
1080 */ |
|
1081 void CMMFMdaAudioOutputStream::DeviceMessage(TUid /*aMessageType*/, const TDesC8& /*aMsg*/) |
|
1082 { |
|
1083 Panic(EDeviceMessageNotSupported); |
|
1084 } |
|
1085 |
|
1086 // CustomInferface - just pass on to DevSound. |
|
1087 TAny* CMMFMdaAudioOutputStream::CustomInterface(TUid aInterfaceId) |
|
1088 { |
|
1089 return iDevSound->CustomInterface(aInterfaceId); |
|
1090 } |
|
1091 |