1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/mm/mmlibs/mmfw/src/Client/Audio/mmfclientaudiooutputstream.cpp Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,1091 @@
1.4 +// Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies).
1.5 +// All rights reserved.
1.6 +// This component and the accompanying materials are made available
1.7 +// under the terms of "Eclipse Public License v1.0"
1.8 +// which accompanies this distribution, and is available
1.9 +// at the URL "http://www.eclipse.org/legal/epl-v10.html".
1.10 +//
1.11 +// Initial Contributors:
1.12 +// Nokia Corporation - initial contribution.
1.13 +//
1.14 +// Contributors:
1.15 +//
1.16 +// Description:
1.17 +//
1.18 +
1.19 +#include <mdaaudiooutputstream.h>
1.20 +#include <mda/common/audio.h>
1.21 +#include "mmfclientaudiooutputstream.h"
1.22 +#include "mmfclientaudiostreamutils.h"
1.23 +#include "MmfFifo.h"
1.24 +#include <mmf/common/mmfpaniccodes.h>
1.25 +
1.26 +#define WAIT_FOR_READY_ACTIVE_OBJECTS
1.27 +
1.28 +const TInt KMicroSecsInOneSec = 1000000;
1.29 +const TInt KUnknownVolume = -1; // means "don't set", must be negative
1.30 +const TInt KShutDownTimeInterval = 100000; //100 milli seconds
1.31 +
1.32 +/**
1.33 + *
1.34 + * Static NewL
1.35 + *
1.36 + * @return CMdaAudioOutputStream*
1.37 + *
1.38 + */
1.39 +EXPORT_C CMdaAudioOutputStream* CMdaAudioOutputStream::NewL(MMdaAudioOutputStreamCallback& aCallback,
1.40 + CMdaServer* /*aServer = NULL*/)
1.41 + {
1.42 + return NewL(aCallback, EMdaPriorityNormal, EMdaPriorityPreferenceTimeAndQuality);
1.43 + }
1.44 +
1.45 +/**
1.46 + *
1.47 + * Static NewL
1.48 + *
1.49 + * @return CMdaAudioOutputStream*
1.50 + *
1.51 + */
1.52 +EXPORT_C CMdaAudioOutputStream* CMdaAudioOutputStream::NewL(MMdaAudioOutputStreamCallback& aCallback,
1.53 + TInt aPriority,
1.54 + TInt aPref /*=EMdaPriorityPreferenceTimeAndQuality*/)
1.55 + {
1.56 + CMdaAudioOutputStream* self = new(ELeave) CMdaAudioOutputStream();
1.57 + CleanupStack::PushL(self);
1.58 + self->iProperties = CMMFMdaAudioOutputStream::NewL(aCallback, aPriority, aPref);
1.59 + CleanupStack::Pop(self);
1.60 + return self;
1.61 + }
1.62 +
1.63 +CMdaAudioOutputStream::CMdaAudioOutputStream()
1.64 + {
1.65 + }
1.66 +
1.67 +CMdaAudioOutputStream::~CMdaAudioOutputStream()
1.68 + {
1.69 + delete iProperties;
1.70 + }
1.71 +
1.72 +void CMdaAudioOutputStream::SetAudioPropertiesL(TInt aSampleRate, TInt aChannels)
1.73 + {
1.74 + ASSERT(iProperties);
1.75 + iProperties->SetAudioPropertiesL(aSampleRate, aChannels);
1.76 + }
1.77 +
1.78 +void CMdaAudioOutputStream::Open(TMdaPackage* aSettings)
1.79 + {
1.80 + ASSERT(iProperties);
1.81 + iProperties->Open(aSettings);
1.82 + }
1.83 +
1.84 +TInt CMdaAudioOutputStream::MaxVolume()
1.85 + {
1.86 + ASSERT(iProperties);
1.87 + return iProperties->MaxVolume();
1.88 + }
1.89 +
1.90 +TInt CMdaAudioOutputStream::Volume()
1.91 + {
1.92 + ASSERT(iProperties);
1.93 + return iProperties->Volume();
1.94 + }
1.95 +
1.96 +void CMdaAudioOutputStream::SetVolume(const TInt aNewVolume)
1.97 + {
1.98 + ASSERT(iProperties);
1.99 + iProperties->SetVolume(aNewVolume);
1.100 + }
1.101 +
1.102 +void CMdaAudioOutputStream::SetPriority(TInt aPriority, TInt aPref)
1.103 + {
1.104 + ASSERT(iProperties);
1.105 + iProperties->SetPriority(aPriority, aPref);
1.106 + }
1.107 +
1.108 +void CMdaAudioOutputStream::WriteL(const TDesC8& aData)
1.109 + {
1.110 + ASSERT(iProperties);
1.111 + iProperties->WriteL(aData);
1.112 + }
1.113 +
1.114 +void CMdaAudioOutputStream::Stop()
1.115 + {
1.116 + ASSERT(iProperties);
1.117 + iProperties->Stop();
1.118 + }
1.119 +
1.120 +EXPORT_C TInt CMdaAudioOutputStream::Pause()
1.121 + {
1.122 + ASSERT(iProperties);
1.123 + return iProperties->Pause();
1.124 + }
1.125 +
1.126 +EXPORT_C TInt CMdaAudioOutputStream::Resume()
1.127 + {
1.128 + ASSERT(iProperties);
1.129 + return iProperties->Resume();
1.130 + }
1.131 +
1.132 +const TTimeIntervalMicroSeconds& CMdaAudioOutputStream::Position()
1.133 + {
1.134 + ASSERT(iProperties);
1.135 + return iProperties->Position();
1.136 + }
1.137 +
1.138 +EXPORT_C void CMdaAudioOutputStream::SetBalanceL(TInt aBalance)
1.139 + {
1.140 + ASSERT(iProperties);
1.141 + iProperties->SetBalanceL(aBalance);
1.142 + }
1.143 +
1.144 +EXPORT_C TInt CMdaAudioOutputStream::GetBalanceL() const
1.145 + {
1.146 + ASSERT(iProperties);
1.147 + return iProperties->GetBalanceL();
1.148 + }
1.149 +
1.150 +EXPORT_C TInt CMdaAudioOutputStream::GetBytes()
1.151 + {
1.152 + ASSERT(iProperties);
1.153 + return iProperties->GetBytes();
1.154 + }
1.155 +
1.156 +EXPORT_C TInt CMdaAudioOutputStream::KeepOpenAtEnd()
1.157 + {
1.158 + ASSERT(iProperties);
1.159 + return iProperties->KeepOpenAtEnd();
1.160 + }
1.161 +
1.162 +EXPORT_C TInt CMdaAudioOutputStream::RequestStop()
1.163 + {
1.164 + ASSERT(iProperties);
1.165 + return iProperties->RequestStop();
1.166 + }
1.167 +
1.168 +/**
1.169 +
1.170 +*/
1.171 +EXPORT_C void CMdaAudioOutputStream::SetDataTypeL(TFourCC aAudioType)
1.172 + {
1.173 + ASSERT(iProperties);
1.174 + iProperties->SetDataTypeL(aAudioType);
1.175 + }
1.176 +
1.177 +
1.178 +/**
1.179 +
1.180 +*/
1.181 +EXPORT_C TFourCC CMdaAudioOutputStream::DataType() const
1.182 + {
1.183 + ASSERT(iProperties);
1.184 + return iProperties->DataType();
1.185 + }
1.186 +
1.187 +EXPORT_C TAny* CMdaAudioOutputStream::CustomInterface(TUid aInterfaceId)
1.188 + {
1.189 + ASSERT(iProperties);
1.190 + return iProperties->CustomInterface(aInterfaceId);
1.191 + }
1.192 +
1.193 +
1.194 +/**
1.195 +Registers the Event for Notification when resource is avaliable.
1.196 +
1.197 +@param aCallback
1.198 + The audio player observer interface.
1.199 +@param aNotificationEventUid
1.200 + The event uid to notify the client.
1.201 +@param aNotificationRegistrationData
1.202 + Notification registration specific data.
1.203 +
1.204 +@return An error code indicating if the registration was successful. KErrNone on success,
1.205 + otherwise another of the system-wide error codes.
1.206 +*/
1.207 +EXPORT_C TInt CMdaAudioOutputStream::RegisterAudioResourceNotification(MMMFAudioResourceNotificationCallback& aCallback,
1.208 + TUid aNotificationEventUid,
1.209 + const TDesC8& aNotificationRegistrationData)
1.210 + {
1.211 + ASSERT(iProperties);
1.212 + return iProperties->RegisterAudioResourceNotification(aCallback,aNotificationEventUid,aNotificationRegistrationData);
1.213 + }
1.214 +
1.215 +/**
1.216 +Cancels the registered notification event.
1.217 +
1.218 +@param aNotificationEventUid
1.219 + The Event to notify the client.
1.220 +
1.221 +@return An error code indicating if the registration was successful. KErrNone on success,
1.222 + otherwise another of the system-wide error codes.
1.223 +*/
1.224 +EXPORT_C TInt CMdaAudioOutputStream::CancelRegisterAudioResourceNotification(TUid aNotificationEventUid)
1.225 + {
1.226 + ASSERT(iProperties);
1.227 + return iProperties->CancelRegisterAudioResourceNotification(aNotificationEventUid);
1.228 + }
1.229 +
1.230 +/**
1.231 +Waits for the client to resume the play even after the default timer expires.
1.232 +
1.233 +@return An error code indicating if the registration was successful. KErrNone on success,
1.234 + otherwise another of the system-wide error codes.
1.235 +*/
1.236 +EXPORT_C TInt CMdaAudioOutputStream::WillResumePlay()
1.237 + {
1.238 + ASSERT(iProperties);
1.239 + return iProperties->WillResumePlay();
1.240 + }
1.241 +
1.242 +enum TMmAosPanic
1.243 + {
1.244 + EToneFinishedNotSupported,
1.245 + EBufferToBeEmptiedNotSupported,
1.246 + ERecordErrorNotSupported,
1.247 + EConvertErrorNotSupported,
1.248 + EDeviceMessageNotSupported,
1.249 + EReservedCall,
1.250 + EAOSStoppingError
1.251 + };
1.252 +
1.253 +_LIT(KMmfMdaAosCategory, "CMmfMdaAudioOutputStream");
1.254 +LOCAL_C void Panic(const TMmAosPanic aReason)
1.255 + {
1.256 + User::Panic(KMmfMdaAosCategory, aReason);
1.257 + }
1.258 +
1.259 +/**
1.260 + *
1.261 + * 2 phase construction function
1.262 + *
1.263 + */
1.264 +CMMFMdaAudioOutputStream* CMMFMdaAudioOutputStream::NewL(MMdaAudioOutputStreamCallback& aCallback)
1.265 + {
1.266 + return NewL(aCallback, EMdaPriorityNormal, EMdaPriorityPreferenceTimeAndQuality);
1.267 + }
1.268 +
1.269 +CMMFMdaAudioOutputStream* CMMFMdaAudioOutputStream::NewL(MMdaAudioOutputStreamCallback& aCallback, TInt aPriority, TInt aPref)
1.270 + {
1.271 + CMMFMdaAudioOutputStream* self = new(ELeave) CMMFMdaAudioOutputStream(aCallback);
1.272 + CleanupStack::PushL(self);
1.273 + self->ConstructL(aPriority, aPref);
1.274 + CleanupStack::Pop(); // self
1.275 + return self;
1.276 + }
1.277 +
1.278 +/**
1.279 + *
1.280 + * Construct
1.281 + *
1.282 + * @param "MMdaAudioOutputStreamCallback&"
1.283 + * a reference to MMdaAudioOutputStreamCallback
1.284 + * @param "TInt aPriority"
1.285 + * a priority value
1.286 + * @param "TInt aPref"
1.287 + * a perference value
1.288 + *
1.289 + */
1.290 +CMMFMdaAudioOutputStream::CMMFMdaAudioOutputStream(MMdaAudioOutputStreamCallback& aCallback)
1.291 + : iCallback(aCallback), iState(EStopped)
1.292 + {
1.293 + iDataTypeCode.Set(TFourCC(' ','P','1','6'));
1.294 + }
1.295 +
1.296 +/**
1.297 + *
1.298 + * Second phase constructor
1.299 + *
1.300 + */
1.301 +void CMMFMdaAudioOutputStream::ConstructL(TInt aPriority, TInt aPref)
1.302 + {
1.303 + iDevSound = CMMFDevSound::NewL();
1.304 + SetPriority(aPriority, aPref);
1.305 + iDevSoundIgnoresUnderflow = iDevSound->QueryIgnoresUnderflow();
1.306 + iFifo = new(ELeave) CMMFFifo<const TDesC8>();
1.307 + iActiveCallback = new(ELeave) CActiveCallback(iCallback);
1.308 + iActiveSchedulerWait = new(ELeave) CActiveSchedulerWait;
1.309 + iShutDownTimer = CPeriodic::NewL(CActive::EPriorityStandard);
1.310 + }
1.311 +
1.312 +/**
1.313 + *
1.314 + * Destructor
1.315 + *
1.316 + */
1.317 +CMMFMdaAudioOutputStream::~CMMFMdaAudioOutputStream()
1.318 + {
1.319 + delete iFifo;
1.320 + delete iDevSound;
1.321 + delete iActiveCallback;
1.322 + delete iActiveSchedulerWait;
1.323 + delete iShutDownTimer;
1.324 + }
1.325 +
1.326 +/**
1.327 + *
1.328 + * Set audio output stream properties
1.329 + *
1.330 + * @param "TInt aSampleRate"
1.331 + * a specified priority value
1.332 + * @param "TInt aChannels"
1.333 + * a specified preference value
1.334 + *
1.335 + */
1.336 +void CMMFMdaAudioOutputStream::SetAudioPropertiesL(TInt aSampleRate, TInt aChannels)
1.337 + {
1.338 + if (iIsOpenState==EIsOpen)
1.339 + {
1.340 + RealSetAudioPropertiesL(aSampleRate, aChannels);
1.341 + }
1.342 + else
1.343 + {
1.344 + // cache params for application later
1.345 + iSampleRate = aSampleRate;
1.346 + iChannels = aChannels;
1.347 + iVolume = KUnknownVolume;
1.348 + iValuesCached = ETrue;
1.349 + }
1.350 + }
1.351 +
1.352 +void CMMFMdaAudioOutputStream::RealSetAudioPropertiesL(TInt aSampleRate, TInt aChannels)
1.353 + {
1.354 + TMMFCapabilities config = iDevSound->Config();
1.355 + config.iChannels = StreamUtils::MapChannelsMdaToMMFL(aChannels);
1.356 + config.iRate = StreamUtils::MapSampleRateMdaToMMFL(aSampleRate);
1.357 + iDevSound->SetConfigL(config);
1.358 + }
1.359 +
1.360 +/**
1.361 + *
1.362 + * Open a audio ouptut stream
1.363 + *
1.364 + * @param "TMdaPackage* Settings"
1.365 + * a pointer point to TMdaPackage
1.366 + *
1.367 + */
1.368 +void CMMFMdaAudioOutputStream::Open(TMdaPackage* aSettings)
1.369 + {
1.370 + iIsOpenState = EIsOpening;
1.371 +
1.372 + // Use settings to set audio properties after the dev sound has been
1.373 + // successfully initialised. Note if the InitializeL() fails, something calls
1.374 + // InitializeComplete() and the iValuesCached flag is cleared. Also note
1.375 + // that if SetAudioPropertiesL() has already been called, there may already
1.376 + // be cached values
1.377 + if (aSettings && aSettings->Type().iUid == KUidMdaDataTypeSettingsDefine)
1.378 + {
1.379 + TMdaAudioDataSettings& audioSettings = *STATIC_CAST(TMdaAudioDataSettings*, aSettings);
1.380 + //Caching these values, which are later set in InitializeComplete
1.381 + iSampleRate = audioSettings.iSampleRate;
1.382 + iChannels = audioSettings.iChannels;
1.383 + iVolume = audioSettings.iVolume;
1.384 + iValuesCached = ETrue;
1.385 + }
1.386 +
1.387 + TRAPD(err, iDevSound->InitializeL(*this, iDataTypeCode, EMMFStatePlaying));
1.388 + if (err != KErrNone)
1.389 + {
1.390 + InitializeComplete(err);
1.391 + }
1.392 + }
1.393 +
1.394 +/**
1.395 + *
1.396 + * To get the maximum volume level
1.397 + *
1.398 + * @return "TInt"
1.399 + * the maximum volume value in integer
1.400 + *
1.401 + */
1.402 +TInt CMMFMdaAudioOutputStream::MaxVolume()
1.403 + {
1.404 + return iDevSound->MaxVolume();
1.405 + }
1.406 +
1.407 +/**
1.408 + *
1.409 + * To get the current volume level
1.410 + *
1.411 + * @return "TInt"
1.412 + * the current volume value in integer
1.413 + *
1.414 + */
1.415 +TInt CMMFMdaAudioOutputStream::Volume()
1.416 + {
1.417 + return iDevSound->Volume();
1.418 + }
1.419 +
1.420 +/**
1.421 + *
1.422 + * Set audio output stream volume to the specified value
1.423 + *
1.424 + * @param "TInt aVolume"
1.425 + * a specified volume value
1.426 + *
1.427 + */
1.428 +void CMMFMdaAudioOutputStream::SetVolume(TInt aVolume)
1.429 + {
1.430 + iDevSound->SetVolume(aVolume);
1.431 + }
1.432 +
1.433 +/**
1.434 + *
1.435 + * Set audio output stream balance
1.436 + *
1.437 + * @param "TInt aBalance"
1.438 + * a specified balance value
1.439 + *
1.440 + */
1.441 +void CMMFMdaAudioOutputStream::SetBalanceL(TInt aBalance)
1.442 + {
1.443 + // test and clip balance to min / max range [-100 <-> 100]
1.444 + // clip rather than leave as this isn't a leaving function
1.445 + if (aBalance < KMMFBalanceMaxLeft) aBalance = KMMFBalanceMaxLeft;
1.446 + if (aBalance > KMMFBalanceMaxRight) aBalance = KMMFBalanceMaxRight;
1.447 +
1.448 + // separate out left and right balance
1.449 + TInt left = 0;
1.450 + TInt right = 0;
1.451 + StreamUtils::CalculateLeftRightBalance( left, right, aBalance );
1.452 +
1.453 + // send the balance to SoundDevice
1.454 + iDevSound->SetPlayBalanceL(left, right);
1.455 + }
1.456 +
1.457 +/**
1.458 + *
1.459 + * To get the current balance value. This function may not return the same value
1.460 + * as passed to SetBalanceL depending on the internal implementation in
1.461 + * the underlying components.
1.462 + *
1.463 + * @return "TInt"
1.464 + * the current balance value in integer
1.465 + *
1.466 + */
1.467 +TInt CMMFMdaAudioOutputStream::GetBalanceL() const
1.468 + {
1.469 + TInt rightBalance = 0;
1.470 + TInt leftBalance = 0;
1.471 + iDevSound->GetPlayBalanceL(leftBalance, rightBalance);
1.472 + TInt balance = 0;
1.473 + StreamUtils::CalculateBalance( balance, leftBalance, rightBalance );
1.474 + return balance;
1.475 + }
1.476 +
1.477 +/**
1.478 + *
1.479 + * Set audio output stream priority
1.480 + *
1.481 + * @param "TInt aPriority"
1.482 + * a specified priority value
1.483 + * @param "TMdaPriorityPreference aPref"
1.484 + * a specified preference value
1.485 + *
1.486 + */
1.487 +void CMMFMdaAudioOutputStream::SetPriority(TInt aPriority, TInt aPref)
1.488 + {
1.489 + TMMFPrioritySettings settings;
1.490 + settings.iPriority = aPriority;
1.491 + settings.iPref = aPref;
1.492 + iDevSound->SetPrioritySettings(settings);
1.493 + }
1.494 +
1.495 +/**
1.496 + *
1.497 + * To write data to output stream
1.498 + * Note that if framed data eg gsm610 is being streamed then the buffer
1.499 + * size of aData should contain an intiger number of frames
1.500 + *
1.501 + * @param "const TDesC8& aData"
1.502 + * a stream data
1.503 + *
1.504 + */
1.505 +void CMMFMdaAudioOutputStream::WriteL(const TDesC8& aData)
1.506 + {
1.507 + if(iState==EStopping)
1.508 + {
1.509 + User::Leave(KErrNotReady);
1.510 + }
1.511 + iShutDownTimer->Cancel();
1.512 +
1.513 + TMMFFifoItem<const TDesC8>* item = new(ELeave) TMMFFifoItem<const TDesC8>(aData);
1.514 + iFifo->AddToFifo(*item);
1.515 +
1.516 + if(iState == EStopped)
1.517 + StartPlayL();
1.518 + else if((iState == EPlaying) && (iBuffer != NULL))
1.519 + { //if we are playing and we have a buffer waiting for data, use it.
1.520 + BufferToBeFilled(iBuffer);
1.521 + iBuffer = NULL;
1.522 + }
1.523 + if(iEventHolder != KNullUid)
1.524 + {
1.525 + TInt err = iDevSound->RegisterAsClient(iEventHolder,iNotificationDataHolder);
1.526 + iEventHolder = KNullUid;
1.527 + iNotificationDataHolder = KNullDesC8;
1.528 + if(err != KErrNone)
1.529 + {
1.530 + iCallback.MaoscPlayComplete(err);
1.531 + }
1.532 + }
1.533 + }
1.534 +
1.535 +/**
1.536 + *
1.537 + * If the audio stream is stopped, then notify the CDevSound to initilised play.
1.538 + * The CDevSound will automatically start calling back for buffers.
1.539 + */
1.540 +void CMMFMdaAudioOutputStream::StartPlayL()
1.541 +{
1.542 + if (iState == EStopped)
1.543 + {
1.544 + iFifoItemPos = 0;
1.545 + iCurrentSamplesPlayed = 0;
1.546 + iDevSound->PlayInitL();
1.547 + iState = EPlaying;
1.548 + iBuffer = NULL;
1.549 + }
1.550 +}
1.551 +
1.552 +
1.553 +/**
1.554 + *
1.555 + * To stop write data to stream
1.556 + *
1.557 + */
1.558 +void CMMFMdaAudioOutputStream::Stop()
1.559 + {
1.560 + iShutDownTimer->Cancel();
1.561 + EmptyFifo(KErrAbort);
1.562 +
1.563 + if(iState == EStopped)
1.564 + {
1.565 + return;
1.566 + }
1.567 +
1.568 + // stop the operation
1.569 + iDevSound->Stop();
1.570 + iState = EStopped;
1.571 + iBuffer = NULL;
1.572 +
1.573 + iCallback.MaoscPlayComplete(KErrCancel);
1.574 + }
1.575 +
1.576 +TInt CMMFMdaAudioOutputStream::RequestStop()
1.577 + {
1.578 + if(!iKeepOpenAtEnd)
1.579 + {
1.580 + return KErrNotSupported;
1.581 + }
1.582 +
1.583 + if(iState==EStopped || iState==EStopping)
1.584 + {
1.585 + return KErrNotReady;
1.586 + }
1.587 +
1.588 + if(iBuffer)
1.589 + {//means all the client buffers are used up and waiting for more data
1.590 + CMMFDataBuffer* dataBuffer = static_cast<CMMFDataBuffer*>(iBuffer);
1.591 + dataBuffer->Data().SetLength(0);
1.592 + dataBuffer->SetLastBuffer(ETrue);
1.593 + iDevSound->PlayData();
1.594 + iBuffer=NULL;
1.595 + }
1.596 + iState = EStopping;
1.597 + return KErrNone;
1.598 + }
1.599 +
1.600 +TInt CMMFMdaAudioOutputStream::KeepOpenAtEnd()
1.601 + {
1.602 + if(!iDevSoundIgnoresUnderflow)
1.603 + {
1.604 + return KErrNotSupported;
1.605 + }
1.606 + else
1.607 + {
1.608 + iKeepOpenAtEnd = ETrue;
1.609 + return KErrNone;
1.610 + }
1.611 + }
1.612 +
1.613 +/**
1.614 + *
1.615 + * To pause send data to stream
1.616 + *
1.617 + */
1.618 +TInt CMMFMdaAudioOutputStream::Pause()
1.619 + {
1.620 + if(iState != EPlaying)
1.621 + {
1.622 + return KErrNotReady;
1.623 + }
1.624 +
1.625 + else if(!iDevSound->IsResumeSupported())
1.626 + {
1.627 + return KErrNotSupported;
1.628 + }
1.629 +
1.630 + // pause the operation
1.631 + iDevSound->Pause();
1.632 + iState = EPaused;
1.633 + return KErrNone;
1.634 + }
1.635 +
1.636 +/**
1.637 + *
1.638 + * To resume send data to stream
1.639 + *
1.640 + */
1.641 +TInt CMMFMdaAudioOutputStream::Resume()
1.642 + {
1.643 + TInt err = KErrNone;
1.644 + if(iState != EPaused)
1.645 + {
1.646 + err = KErrNotReady;
1.647 + }
1.648 + else if(!iDevSound->IsResumeSupported())
1.649 + {
1.650 + err = KErrNotSupported;
1.651 + }
1.652 +
1.653 + // resume the operation
1.654 + if(err == KErrNone)
1.655 + {
1.656 + err = iDevSound->Resume();
1.657 + }
1.658 + if(err == KErrNone)
1.659 + {
1.660 + iState = EPlaying;
1.661 + }
1.662 +
1.663 + return err;
1.664 + }
1.665 +
1.666 +
1.667 +/**
1.668 + *
1.669 + * To get the current position in the data stream
1.670 + *
1.671 + * @return "TTimeIntervalMicroSeconds&"
1.672 + * the current position in integer
1.673 + *
1.674 + */
1.675 +const TTimeIntervalMicroSeconds& CMMFMdaAudioOutputStream::Position()
1.676 + {
1.677 + TInt64 position = iDevSound->SamplesPlayed();
1.678 + position = position * KMicroSecsInOneSec / StreamUtils::SampleRateAsValue(iDevSound->Config());
1.679 + 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
1.680 + return iPosition;
1.681 + }
1.682 +
1.683 +/**
1.684 + *
1.685 + * To return the current number of bytes rendered by audio hardware
1.686 + * @return "the current current number of bytes rendered by audio hardware in integer"
1.687 + *
1.688 + */
1.689 +TInt CMMFMdaAudioOutputStream::GetBytes()
1.690 + {
1.691 + return iDevSound->SamplesPlayed() * StreamUtils::BytesPerSample(iDevSound->Config());
1.692 + }
1.693 +
1.694 +/**
1.695 +
1.696 +*/
1.697 +void CMMFMdaAudioOutputStream::SetDataTypeL(TFourCC aAudioType)
1.698 + {
1.699 + if(iState != EStopped)
1.700 + User::Leave(KErrServerBusy);
1.701 +
1.702 + if(aAudioType == iDataTypeCode)
1.703 + return;
1.704 +
1.705 + TMMFPrioritySettings prioritySettings;
1.706 + prioritySettings.iState = EMMFStatePlaying;
1.707 + RArray<TFourCC> supportedDataTypes;
1.708 +
1.709 + CleanupClosePushL(supportedDataTypes);
1.710 +
1.711 + TRAPD(err, iDevSound->GetSupportedInputDataTypesL(supportedDataTypes, prioritySettings));
1.712 +
1.713 + if (err == KErrNone)
1.714 + {
1.715 + if (supportedDataTypes.Find(aAudioType) == KErrNotFound)
1.716 + {
1.717 + User::Leave(KErrNotSupported);
1.718 + }
1.719 + //if match, set the 4CC of AudioType to match
1.720 + iDataTypeCode.Set(aAudioType);
1.721 + }
1.722 + else //we had a real leave error from GetSupportedOuputDataTypesL
1.723 + {
1.724 + User::Leave(err);
1.725 + }
1.726 +
1.727 + CleanupStack::PopAndDestroy(&supportedDataTypes);
1.728 +
1.729 + if(iIsOpenState!=EIsNotOpen)
1.730 + {
1.731 + // need to recall or restart InitializeL() process
1.732 + iDevSound->CancelInitialize(); // call just in case mid-InitializeL. No harm if not.
1.733 + // if not supported then assume old DevSound behaviour anyway
1.734 + // where InitializeL() implicitly cancels, so no harm either way
1.735 + iIsOpenState = EIsOpening;
1.736 + iInitCallFrmSetDataType = ETrue;
1.737 + TRAPD(err, iDevSound->InitializeL(*this, iDataTypeCode, EMMFStatePlaying));
1.738 + if (err != KErrNone)
1.739 + {
1.740 + // Leave if error.
1.741 + iIsOpenState = EIsNotOpen;
1.742 + iInitCallFrmSetDataType = EFalse;
1.743 + User::Leave(err);
1.744 + }
1.745 + // In some implementations InitializeComplete is sent
1.746 + // in context, so check before starting activeSchedulerWait.
1.747 + else if(iIsOpenState == EIsOpening)
1.748 + {
1.749 + iInitializeState = KRequestPending;
1.750 + iActiveSchedulerWait->Start();
1.751 + }
1.752 + iInitCallFrmSetDataType = EFalse;
1.753 + User::LeaveIfError(iInitializeState);
1.754 + }
1.755 + }
1.756 +
1.757 +/**
1.758 +
1.759 +*/
1.760 +TFourCC CMMFMdaAudioOutputStream::DataType() const
1.761 + {
1.762 + return iDataTypeCode;
1.763 + }
1.764 +
1.765 +TInt CMMFMdaAudioOutputStream::RegisterAudioResourceNotification(MMMFAudioResourceNotificationCallback& aCallback,TUid aNotificationEventUid,const TDesC8& aNotificationRegistrationData)
1.766 + {
1.767 + iAudioResourceNotificationCallBack = &aCallback;
1.768 + TInt err = iDevSound->RegisterAsClient(aNotificationEventUid,aNotificationRegistrationData);
1.769 + if(err == KErrNotReady)
1.770 + {
1.771 + iEventHolder = aNotificationEventUid;
1.772 + iNotificationDataHolder = aNotificationRegistrationData;
1.773 + return KErrNone;
1.774 + }
1.775 + iEventHolder = KNullUid;
1.776 + iNotificationDataHolder = KNullDesC8;
1.777 + return err;
1.778 + }
1.779 +
1.780 +TInt CMMFMdaAudioOutputStream::CancelRegisterAudioResourceNotification(TUid aNotificationEventId)
1.781 + {
1.782 + TInt err = iDevSound->CancelRegisterAsClient(aNotificationEventId);
1.783 + if(err == KErrNotReady)
1.784 + {
1.785 + if(aNotificationEventId != KMMFEventCategoryAudioResourceAvailable)
1.786 + {
1.787 + return KErrNotSupported;
1.788 + }
1.789 + if(iEventHolder == KNullUid)
1.790 + {
1.791 + return KErrCancel;
1.792 + }
1.793 + iEventHolder = KNullUid;
1.794 + iNotificationDataHolder = KNullDesC8;
1.795 + return KErrNone;
1.796 + }
1.797 + return err;
1.798 + }
1.799 +
1.800 +TInt CMMFMdaAudioOutputStream::WillResumePlay()
1.801 + {
1.802 + return iDevSound->WillResumePlay();
1.803 + }
1.804 +
1.805 +/**
1.806 + *
1.807 + * To be called when intialize stream complete
1.808 + *
1.809 + * @param "TInt aError"
1.810 + * error code, initialize stream succeed when aError = 0
1.811 + *
1.812 + */
1.813 +void CMMFMdaAudioOutputStream::InitializeComplete(TInt aError)
1.814 + {
1.815 + TInt err = aError;
1.816 + if(err == KErrNone && iValuesCached)
1.817 + {
1.818 + TRAP(err, RealSetAudioPropertiesL(iSampleRate, iChannels));
1.819 + if(err == KErrNone && iVolume>=0)
1.820 + {
1.821 + SetVolume(iVolume);
1.822 + }
1.823 + }
1.824 + iValuesCached = EFalse; // whatever clear our cache
1.825 + if(iIsOpenState == EIsOpening)
1.826 + {
1.827 + // Signal for the MaoscOpenComplete callback to be called asynchronously.Ignore if InitializeL is called from SetDataTypeL
1.828 + if(!iInitCallFrmSetDataType)
1.829 + {
1.830 + iActiveCallback->Signal(err);
1.831 + }
1.832 + iIsOpenState = err ? EIsNotOpen : EIsOpen;
1.833 + if(iInitializeState == KRequestPending)
1.834 + {
1.835 + iInitializeState = err;
1.836 + iActiveSchedulerWait->AsyncStop();
1.837 + }
1.838 + else
1.839 + {
1.840 + iInitializeState = err;//Set the error if InitializeComplete is called in context of InitializeL.
1.841 + }
1.842 + }
1.843 + }
1.844 +
1.845 +/**
1.846 + *
1.847 + * Do not support
1.848 + *
1.849 + */
1.850 +void CMMFMdaAudioOutputStream::ToneFinished(TInt /*aError*/)
1.851 + {
1.852 + Panic(EToneFinishedNotSupported);
1.853 + }
1.854 +
1.855 +/**
1.856 + *
1.857 + * Called when sound device need data
1.858 + *
1.859 + * @param "CMMFBuffer* aBuffer"
1.860 + * a pointer point to CMMFBuffer, which is used to stored data
1.861 + *
1.862 + */
1.863 +void CMMFMdaAudioOutputStream::BufferToBeFilled(CMMFBuffer* aBuffer)
1.864 + {
1.865 + if (iState == EPlaying || iState == EStopping)
1.866 + {
1.867 + TMMFFifoItem<const TDesC8>* firstItem = iFifo->Get();
1.868 + //ASSERT firstItem != NULL
1.869 + if (iFifoItemPos >= firstItem->GetData().Length())
1.870 + {
1.871 + // We've played all segments of the first buffer in the fifo so we can delete it
1.872 + iFifo->RemoveFirstItem();
1.873 + iCallback.MaoscBufferCopied(KErrNone, firstItem->GetData());
1.874 + delete firstItem;
1.875 + iFifoItemPos = 0;
1.876 + }
1.877 + }
1.878 + TMMFFifoItem<const TDesC8>* firstItem = iFifo->Get();
1.879 +
1.880 + if (firstItem)
1.881 + {
1.882 + // Fill aBuffer with the next segment of the first buffer in the fifo
1.883 + TDes8& fillDes = STATIC_CAST(CMMFDataBuffer*, aBuffer)->Data();
1.884 + const TDesC8& readDes = firstItem->GetData();
1.885 + TInt readLen = Min(readDes.Length()-iFifoItemPos, aBuffer->RequestSize());
1.886 + fillDes = readDes.Mid(iFifoItemPos, readLen);
1.887 + iFifoItemPos+=readLen; // so we know where the next segment in the fifo item starts
1.888 + // Notify iDevSound the buffer is ready to be played
1.889 + iDevSound->PlayData();
1.890 + }
1.891 + else
1.892 + {
1.893 + if(iBuffer ==NULL)
1.894 + {
1.895 + //keep a record of the supplied buffer and use it when/if more user data is in the FIFO
1.896 + iBuffer = aBuffer;
1.897 + }
1.898 + if(!iKeepOpenAtEnd)
1.899 + {
1.900 + if (iDevSoundIgnoresUnderflow)
1.901 + {
1.902 + iCurrentSamplesPlayed = iDevSound->SamplesPlayed();
1.903 + StartShutDownTimer();
1.904 + }
1.905 + }
1.906 + else if(iState==EStopping)
1.907 + {
1.908 + CMMFDataBuffer* dataBuffer = static_cast<CMMFDataBuffer*>(aBuffer);
1.909 + dataBuffer->Data().SetLength(0);
1.910 + dataBuffer->SetLastBuffer(ETrue);
1.911 + iDevSound->PlayData();
1.912 + iBuffer=NULL;
1.913 + }
1.914 + }
1.915 +
1.916 + }
1.917 +
1.918 +void CMMFMdaAudioOutputStream::StartShutDownTimer()
1.919 + {
1.920 + iShutDownTimer->Start(KShutDownTimeInterval, KShutDownTimeInterval, TCallBack(ShutDownTimerComplete,this));
1.921 + }
1.922 +
1.923 +TInt CMMFMdaAudioOutputStream::ShutDownTimerComplete(TAny* aAudioOutputStream)
1.924 + {
1.925 + CMMFMdaAudioOutputStream* audioOutputStream = static_cast<CMMFMdaAudioOutputStream*>(aAudioOutputStream);
1.926 + audioOutputStream->DoShutDownTimerComplete();
1.927 + return KErrNone;
1.928 + }
1.929 +
1.930 +void CMMFMdaAudioOutputStream::DoShutDownTimerComplete()
1.931 + {
1.932 + iShutDownTimer->Cancel();
1.933 + TInt samplesPlayed = iDevSound->SamplesPlayed();
1.934 + if (samplesPlayed == iCurrentSamplesPlayed)
1.935 + {
1.936 + iDevSound->Stop();
1.937 + iState = EStopped;
1.938 + iBuffer = NULL;
1.939 + iCallback.MaoscPlayComplete(KErrUnderflow);
1.940 + }
1.941 + else
1.942 + {//desvound has not yet finished playing all the data. So wait for one more cycle
1.943 + //Restart Timer
1.944 + iCurrentSamplesPlayed = samplesPlayed;
1.945 + StartShutDownTimer();
1.946 + }
1.947 + }
1.948 +
1.949 +/**
1.950 + *
1.951 + * Called when play operation complete, successfully or otherwise
1.952 + *
1.953 + * @param "TInt aError"
1.954 + * an error value which will indicate playing successfully complete
1.955 + * if error value is 0
1.956 + *
1.957 + */
1.958 +void CMMFMdaAudioOutputStream::PlayError(TInt aError)
1.959 + {
1.960 + TInt err=aError;
1.961 + // if iDevSoundIgnoresUnderflow is true, then KErrUnderflow should not be produced by DevSound when we are not stopping
1.962 + __ASSERT_DEBUG(!iDevSoundIgnoresUnderflow || !(err==KErrUnderflow && iState!=EStopping), Panic(EAOSStoppingError));
1.963 + iState = EStopped;
1.964 + iBuffer = NULL;
1.965 + iShutDownTimer->Cancel();
1.966 + if (err == KErrNone || err == KErrUnderflow)
1.967 + {
1.968 + if (!iFifo->IsEmpty())
1.969 + {
1.970 + // We live again - the Fifo still has some data. The sound device
1.971 + // will have to be opened again though so there might be an
1.972 + // audible click.
1.973 + TRAP(err, StartPlayL());
1.974 + if (err == KErrNone)
1.975 + {
1.976 + return;
1.977 + }
1.978 + }
1.979 + }
1.980 +
1.981 + EmptyFifo(err);
1.982 +
1.983 + // Note - KErrUnderflow will be reported even when all the buffers have
1.984 + // successfully played
1.985 + iCallback.MaoscPlayComplete(err);
1.986 + }
1.987 +
1.988 +void CMMFMdaAudioOutputStream::EmptyFifo(TInt aError)
1.989 + {
1.990 + // Delete all buffers in the fifo and notify the observer
1.991 + TMMFFifoItem<const TDesC8>* firstItem;
1.992 + while((firstItem = iFifo->Get()) != NULL)
1.993 + {
1.994 + iFifo->RemoveFirstItem();
1.995 + iCallback.MaoscBufferCopied(aError, firstItem->GetData());
1.996 + delete firstItem;
1.997 + }
1.998 + }
1.999 +
1.1000 +void CMMFMdaAudioOutputStream::SendEventToClient(const TMMFEvent& aEvent)
1.1001 + {
1.1002 + if (aEvent.iEventType == KMMFEventCategoryAudioResourceAvailable)
1.1003 + {
1.1004 + // Retrieve the number of samples played
1.1005 + // For the event type KMMFEventCategoryAudioResourceAvailable GetResourceNotificationData() returns
1.1006 + // a package buffer as TMMFTimeIntervalMicroSecondsPckg, but the contents should be
1.1007 + // converted to an integer and interpreted as the data returned is samples played,
1.1008 + // but not as a microsecond value.
1.1009 + TBuf8<TMMFAudioConfig::KNotificationDataBufferSize> notificationData;
1.1010 + if (KErrNone != iDevSound->GetResourceNotificationData(aEvent.iEventType, notificationData))
1.1011 + {
1.1012 + notificationData.SetLength(0);
1.1013 + }
1.1014 + iAudioResourceNotificationCallBack->MarncResourceAvailable(aEvent.iEventType, notificationData);
1.1015 + }
1.1016 + }
1.1017 +
1.1018 +CMMFMdaAudioOutputStream::CActiveCallback::~CActiveCallback()
1.1019 + {
1.1020 + Cancel();
1.1021 + }
1.1022 +
1.1023 +CMMFMdaAudioOutputStream::CActiveCallback::CActiveCallback(MMdaAudioOutputStreamCallback& aCallback)
1.1024 + : CActive(EPriorityStandard), iCallback(aCallback)
1.1025 + {
1.1026 + CActiveScheduler::Add(this);
1.1027 + }
1.1028 +
1.1029 +void CMMFMdaAudioOutputStream::CActiveCallback::RunL()
1.1030 + {
1.1031 + iCallback.MaoscOpenComplete(iStatus.Int());
1.1032 + }
1.1033 +
1.1034 +void CMMFMdaAudioOutputStream::CActiveCallback::DoCancel()
1.1035 + {
1.1036 + }
1.1037 +
1.1038 +void CMMFMdaAudioOutputStream::CActiveCallback::Signal(const TInt aReason)
1.1039 + {
1.1040 + ASSERT(!IsActive());
1.1041 +
1.1042 + // Signal ourselves to run with the given completion code
1.1043 + TRequestStatus* status = &iStatus;
1.1044 + User::RequestComplete(status, aReason);
1.1045 + SetActive();
1.1046 + }
1.1047 +
1.1048 +
1.1049 +/**
1.1050 + *
1.1051 + * Do not support
1.1052 + *
1.1053 + */
1.1054 +void CMMFMdaAudioOutputStream::BufferToBeEmptied(CMMFBuffer* /*aBuffer*/)
1.1055 + {
1.1056 + Panic(EBufferToBeEmptiedNotSupported);
1.1057 + }
1.1058 +
1.1059 +/**
1.1060 + *
1.1061 + * Do not support
1.1062 + *
1.1063 + */
1.1064 +void CMMFMdaAudioOutputStream::RecordError(TInt /*aError*/)
1.1065 + {
1.1066 + Panic(ERecordErrorNotSupported);
1.1067 + }
1.1068 +
1.1069 +/**
1.1070 + *
1.1071 + * Do not support
1.1072 + *
1.1073 + */
1.1074 +void CMMFMdaAudioOutputStream::ConvertError(TInt /*aError*/)
1.1075 + {
1.1076 + Panic(EConvertErrorNotSupported);
1.1077 + }
1.1078 +
1.1079 +/**
1.1080 + *
1.1081 + * Do not support
1.1082 + *
1.1083 + */
1.1084 +void CMMFMdaAudioOutputStream::DeviceMessage(TUid /*aMessageType*/, const TDesC8& /*aMsg*/)
1.1085 + {
1.1086 + Panic(EDeviceMessageNotSupported);
1.1087 + }
1.1088 +
1.1089 +// CustomInferface - just pass on to DevSound.
1.1090 +TAny* CMMFMdaAudioOutputStream::CustomInterface(TUid aInterfaceId)
1.1091 + {
1.1092 + return iDevSound->CustomInterface(aInterfaceId);
1.1093 + }
1.1094 +