1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/mm/mmlibs/mmfw/src/Plugin/AudioOutput/MmfAudioOutput.cpp Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,872 @@
1.4 +// Copyright (c) 2005-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 "MmfAudioOutput.h"
1.20 +#include <ecom/implementationproxy.h>
1.21 +#include <mmf/server/mmfformat.h>
1.22 +#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
1.23 +#include <mmf/common/mmfhelper.h>
1.24 +#endif
1.25 +#include <mmf/plugin/mmfaudioiointerfaceuids.hrh>
1.26 +
1.27 +void Panic(TInt aPanicCode)
1.28 + {
1.29 + _LIT(KMMFAudioOutputPanicCategory, "MMFAudioOutput");
1.30 + User::Panic(KMMFAudioOutputPanicCategory, aPanicCode);
1.31 + }
1.32 +
1.33 +/**
1.34 +Allocates and constructs a new audio output sink.
1.35 +
1.36 +Static standard SymbianOS 2 phase constuction method.
1.37 +
1.38 +@return A pointer to the new sink.
1.39 +*/
1.40 +MDataSink* CMMFAudioOutput::NewSinkL()
1.41 + {
1.42 + CMMFAudioOutput* self = new (ELeave) CMMFAudioOutput ;
1.43 + CleanupStack::PushL(self);
1.44 + self->ConstructL();
1.45 + CleanupStack::Pop();
1.46 + return STATIC_CAST( MDataSink*, self ) ;
1.47 + }
1.48 +
1.49 +/**
1.50 +Standard SymbianOS ConstructL.
1.51 +
1.52 +Used to initialise member varibles with device specific behaviour.
1.53 +*/
1.54 +void CMMFAudioOutput::ConstructL()
1.55 + {
1.56 + iInitializeState = KErrNone;
1.57 + iDataTypeCode = KMMFFourCCCodePCM16;
1.58 + iNeedsSWConversion = EFalse;
1.59 + iSourceSampleRate = 0;
1.60 + iActiveSchedulerWait = new(ELeave) CActiveSchedulerWait;
1.61 + }
1.62 +
1.63 +/**
1.64 +Overridable constuction specific to this datasource.
1.65 +
1.66 +The default implementation does nothing.
1.67 +
1.68 +@param aInitData
1.69 + The initialisation data.
1.70 +*/
1.71 +void CMMFAudioOutput::ConstructSinkL( const TDesC8& /*aInitData*/ )
1.72 + {
1.73 + }
1.74 +
1.75 +
1.76 +/**
1.77 +@deprecated
1.78 +
1.79 +Gets audio from hardware device abstracted MMFDevsound (not used).
1.80 +
1.81 +@param aBuffer
1.82 + The data to write out to a Hardware Device.
1.83 +@param aSupplier
1.84 + The MDataSource consuming the data contained in aBuffer
1.85 +*/
1.86 +void CMMFAudioOutput::HWEmptyBufferL(CMMFBuffer* /*aBuffer*/, MDataSource* /*aSupplier*/)
1.87 + {
1.88 + }
1.89 +
1.90 +/**
1.91 +Sends audio to MMFDevsound.
1.92 +
1.93 +@param aBuffer
1.94 + The data to write out.
1.95 +@param aSupplier
1.96 + The search criteria for the supplier.
1.97 +@param aMediaId
1.98 + The type of data supplied - currently ignored.
1.99 +*/
1.100 +void CMMFAudioOutput::EmptyBufferL(CMMFBuffer* aBuffer, MDataSource* aSupplier, TMediaId /*aMediaId*/)
1.101 + {
1.102 + iSupplier = aSupplier;
1.103 +
1.104 + if (!iMMFDevSound)
1.105 + Panic(EMMFAudioOutputDevSoundNotLoaded);
1.106 +
1.107 + if (aSupplier == NULL)
1.108 + User::Leave(KErrArgument);
1.109 +
1.110 + // In order to avoid changes at the datapath data transfer state machine
1.111 + // EmptyBufferL is still being called even
1.112 + // if the first time there is no buffer to send
1.113 + if (!iCanSendBuffers)
1.114 + {
1.115 +
1.116 + iCanSendBuffers = ETrue;
1.117 + return;
1.118 + }
1.119 +
1.120 + if (iNeedsSWConversion)
1.121 + {//need to perform channel & sample rate conversion before writing to clip
1.122 + CMMFDataBuffer* audio;
1.123 +
1.124 + //need to make sure aBuffer is not null before it is used
1.125 + if (aBuffer == NULL)
1.126 + {
1.127 + User::Leave(KErrArgument);
1.128 + }
1.129 +
1.130 + //buffer check is placed here before possible use of the buffer
1.131 + if (!CMMFBuffer::IsSupportedDataBuffer(aBuffer->Type()))
1.132 + {
1.133 + User::Leave(KErrNotSupported);
1.134 + }
1.135 +
1.136 + iConvertBuffer = CMMFDataBuffer::NewL(((CMMFDataBuffer*)aBuffer)->Data().Length());
1.137 + iChannelAndSampleRateConverter->Convert(*(CMMFDataBuffer*)aBuffer, *iConvertBuffer);
1.138 + audio = iConvertBuffer;
1.139 +
1.140 + //copy our converted data back into the real buffer to return to datapath
1.141 + TDes8& dest = STATIC_CAST( CMMFDataBuffer*, aBuffer )->Data() ;
1.142 + dest.SetLength(0);
1.143 + dest.Copy(audio->Data());
1.144 + }
1.145 +
1.146 + if (iState != EDevSoundReady && iState != EPaused) // If we're paused we still feed a buffer to DevSound
1.147 + User::Leave(KErrNotReady);
1.148 +
1.149 + iMMFDevSound->PlayData();
1.150 +
1.151 +#if defined(__AUDIO_PROFILING)
1.152 + RDebug::ProfileEnd(0);
1.153 + User::Leave(KErrEof);
1.154 +#endif // defined(__AUDIO_PROFILING)
1.155 +
1.156 +
1.157 + // The following will never have been allocated unless
1.158 + // software conversion was required, and due to certain DevSound
1.159 + // implementations, this requirement can change dynamically.
1.160 + delete iConvertBuffer;
1.161 + iConvertBuffer = NULL;
1.162 + }
1.163 +
1.164 +
1.165 +
1.166 +/**
1.167 +Negotiates with the source to set, for example, the sample rate and number of channels.
1.168 +
1.169 +Called if the sink's setup depends on source.
1.170 +
1.171 +@param aSource
1.172 + The data source with which to negotiate.
1.173 +*/
1.174 +void CMMFAudioOutput::NegotiateL(MDataSource& aSource)
1.175 + {
1.176 + if (aSource.DataSourceType() == KUidMmfFormatDecode)
1.177 + {//source is a clip so for now set sink settings to match source
1.178 + iSourceSampleRate = ((CMMFFormatDecode&)aSource).SampleRate();
1.179 + iSourceChannels = ((CMMFFormatDecode&)aSource).NumChannels();
1.180 + iSourceFourCC.Set(aSource.SourceDataTypeCode(TMediaId(KUidMediaTypeAudio)));
1.181 +
1.182 + ((CMMFFormatDecode&)aSource).SuggestSourceBufferSize(KAudioOutputDefaultFrameSize);
1.183 + }
1.184 +
1.185 + // Query DevSound capabilities and Try to use DevSound sample rate and
1.186 + // mono/stereo capability
1.187 + if (!iMMFDevSound)
1.188 + Panic(EMMFAudioOutputDevSoundNotLoaded);
1.189 +
1.190 + TMMFState prioritySettingsState = iPrioritySettings.iState; //should be EMMFStatePlaying
1.191 + //to use the GetSupportedInputDatatypes but we'll save it just in case it's not
1.192 + iPrioritySettings.iState = EMMFStatePlaying; //if playing does not support any output data types
1.193 + RArray<TFourCC> supportedDataTypes;
1.194 + //note Input data types becuase if we are playing audio ie audio output
1.195 + //the data is sent as an input to DevSound
1.196 + TRAPD(err, iMMFDevSound->GetSupportedInputDataTypesL(supportedDataTypes, iPrioritySettings));
1.197 + iPrioritySettings.iState = prioritySettingsState;
1.198 + if (err == KErrNone)
1.199 + {
1.200 + if (supportedDataTypes.Find(iSourceFourCC) == KErrNotFound)
1.201 + {//the source fourCC code could not be found in the list of
1.202 + //data types supported by the Devsound therefor default to pcm16
1.203 + iDataTypeCode = KMMFFourCCCodePCM16;
1.204 + }
1.205 + else
1.206 + {
1.207 + //the DevSound does support the same datatype as the source
1.208 + //so set the fourcc to that of the source
1.209 + iDataTypeCode = iSourceFourCC;
1.210 + }
1.211 + }
1.212 + supportedDataTypes.Close();
1.213 + if (err == KErrNotSupported)
1.214 + {//if the Devsound does not support the GetSupportedOutputDataTypesL method
1.215 + //then assume that the DevSound is pcm16 only
1.216 + iDataTypeCode = KMMFFourCCCodePCM16;
1.217 + }
1.218 + else if (err != KErrNone) //we had a real leave error from GetSupportedOuputDataTypesL
1.219 + {
1.220 + User::Leave(err);
1.221 + }
1.222 +
1.223 + // Prevent defect when SinkPrimeL is called before NegotiateL()
1.224 + // since characterization is ambiguous
1.225 + if(iState == EDevSoundReady)
1.226 + {
1.227 + iState = EIdle;
1.228 + }
1.229 + //INC40817 do the initialize here as capabilities may depend on datatype
1.230 + //note this implies client of audiooutput must call negotiate
1.231 + iMMFDevSound->InitializeL(*this, iDataTypeCode, EMMFStatePlaying);
1.232 +
1.233 + // In some implementations InitializeComplete is sent
1.234 + // in context, so check before starting activeSchedulerWait.
1.235 + if (iState != EDevSoundReady)
1.236 + {
1.237 + iInitializeState = KRequestPending;
1.238 + iActiveSchedulerWait->Start();
1.239 + }
1.240 +
1.241 + User::LeaveIfError(iInitializeState);
1.242 +
1.243 + // Reset the following flag in case DevSound's capabilities have
1.244 + // changed since we were last here: INC037165
1.245 + iNeedsSWConversion = EFalse;
1.246 + TMMFCapabilities devSoundCaps;
1.247 + devSoundCaps = iMMFDevSound->Capabilities();
1.248 + // Default PCM16
1.249 + iDevSoundConfig.iEncoding = EMMFSoundEncoding16BitPCM;
1.250 + // 1 = Monophonic and 2 == Stereo
1.251 + if (((iSourceChannels == 1) && (devSoundCaps.iChannels & EMMFMono)) ||
1.252 + ((iSourceChannels == 2) && (devSoundCaps.iChannels & EMMFStereo)))
1.253 + iDevSoundConfig.iChannels = iSourceChannels;
1.254 + else //default or SW conversion, e.g. stereo on mono support
1.255 + {
1.256 + iDevSoundConfig.iChannels = EMMFMono;
1.257 + iNeedsSWConversion = ETrue;
1.258 + iSWConvertChannels = 1;
1.259 + iSWConvertSampleRate = iSourceSampleRate;
1.260 + }
1.261 +
1.262 + // Check for std sample rates.
1.263 + if ((iSourceSampleRate == 96000) && (devSoundCaps.iRate & EMMFSampleRate96000Hz))
1.264 + iDevSoundConfig.iRate = EMMFSampleRate96000Hz;
1.265 + else if ((iSourceSampleRate == 88200) && (devSoundCaps.iRate & EMMFSampleRate88200Hz))
1.266 + iDevSoundConfig.iRate = EMMFSampleRate88200Hz;
1.267 + else if ((iSourceSampleRate == 64000) && (devSoundCaps.iRate & EMMFSampleRate64000Hz))
1.268 + iDevSoundConfig.iRate = EMMFSampleRate64000Hz;
1.269 + else if ((iSourceSampleRate == 48000) && (devSoundCaps.iRate & EMMFSampleRate48000Hz))
1.270 + iDevSoundConfig.iRate = EMMFSampleRate48000Hz;
1.271 + else if ((iSourceSampleRate == 44100) && (devSoundCaps.iRate & EMMFSampleRate44100Hz))
1.272 + iDevSoundConfig.iRate = EMMFSampleRate44100Hz;
1.273 + else if ((iSourceSampleRate == 32000) && (devSoundCaps.iRate & EMMFSampleRate32000Hz))
1.274 + iDevSoundConfig.iRate = EMMFSampleRate32000Hz;
1.275 + else if ((iSourceSampleRate == 24000) && (devSoundCaps.iRate & EMMFSampleRate24000Hz))
1.276 + iDevSoundConfig.iRate = EMMFSampleRate24000Hz;
1.277 + else if ((iSourceSampleRate == 22050) && (devSoundCaps.iRate & EMMFSampleRate22050Hz))
1.278 + iDevSoundConfig.iRate = EMMFSampleRate22050Hz;
1.279 + else if ((iSourceSampleRate == 16000) && (devSoundCaps.iRate & EMMFSampleRate16000Hz))
1.280 + iDevSoundConfig.iRate = EMMFSampleRate16000Hz;
1.281 + else if ((iSourceSampleRate == 12000) && (devSoundCaps.iRate & EMMFSampleRate12000Hz))
1.282 + iDevSoundConfig.iRate = EMMFSampleRate12000Hz;
1.283 + else if ((iSourceSampleRate == 11025) && (devSoundCaps.iRate & EMMFSampleRate11025Hz))
1.284 + iDevSoundConfig.iRate = EMMFSampleRate11025Hz;
1.285 + else if ((iSourceSampleRate == 8000) && (devSoundCaps.iRate & EMMFSampleRate8000Hz))
1.286 + iDevSoundConfig.iRate = EMMFSampleRate8000Hz;
1.287 + else // non standard sample rate
1.288 + {
1.289 + iNeedsSWConversion = ETrue;
1.290 + // we need to choose to the closest, and smaller standard sample rate
1.291 + // and eventually convert the audio samples to this standard sample rate
1.292 + //NB: this list must be in ascending order
1.293 + const TInt KNumSampleRates = 12;
1.294 + static const TUint supportedSR[KNumSampleRates][2] = {{8000, EMMFSampleRate8000Hz},
1.295 + {11025, EMMFSampleRate11025Hz},
1.296 + {12000, EMMFSampleRate12000Hz},
1.297 + {16000, EMMFSampleRate16000Hz},
1.298 + {22050, EMMFSampleRate22050Hz},
1.299 + {24000, EMMFSampleRate24000Hz},
1.300 + {32000, EMMFSampleRate32000Hz},
1.301 + {44100, EMMFSampleRate44100Hz},
1.302 + {48000, EMMFSampleRate48000Hz},
1.303 + {64000, EMMFSampleRate64000Hz},
1.304 + {88200, EMMFSampleRate88200Hz},
1.305 + {96000, EMMFSampleRate96000Hz}};
1.306 +
1.307 + //Only support down sampling
1.308 + if (iSourceSampleRate < supportedSR[0][0])
1.309 + User::Leave(KErrNotSupported);
1.310 +
1.311 +
1.312 + TInt sampleRateIndex = KNumSampleRates;
1.313 +
1.314 + //find the source sampleRateIndex
1.315 + for (sampleRateIndex--; sampleRateIndex > -1; sampleRateIndex--)
1.316 + {
1.317 + if(iSourceSampleRate >= supportedSR[sampleRateIndex][0])
1.318 + {
1.319 + break;
1.320 + }
1.321 + }
1.322 +
1.323 + //find the highest sink sample rate below the source rate
1.324 + for (; sampleRateIndex > -1; sampleRateIndex--)
1.325 + {
1.326 + if(devSoundCaps.iRate & supportedSR[sampleRateIndex][1])
1.327 + {
1.328 + iSWConvertSampleRate = supportedSR[sampleRateIndex][0];
1.329 + iDevSoundConfig.iRate = supportedSR[sampleRateIndex][1];
1.330 + break;
1.331 + }
1.332 + }
1.333 +
1.334 + //if a suitable sink sample rate is not available
1.335 + if (sampleRateIndex < 0)
1.336 + User::Leave(KErrNotSupported);
1.337 +
1.338 + // set the channels as well
1.339 + iSWConvertChannels = iDevSoundConfig.iChannels;
1.340 + } // else // non standard sample rate
1.341 +
1.342 + if (iNeedsSWConversion)
1.343 + {//can only software convert if datatype is pcm16
1.344 + //note cannot set non standard sample rates on DevSound API
1.345 + //as the API does not allow this
1.346 + //we need to reinitialize the devsound with pcm16 in this case
1.347 + iDataTypeCode = KMMFFourCCCodePCM16;
1.348 + iState = EIdle;
1.349 + iMMFDevSound->InitializeL(*this, iDataTypeCode, EMMFStatePlaying);
1.350 +
1.351 + // In some implementations InitializeComplete is called
1.352 + // in context, so check before starting activeSchedulerWait.
1.353 + if (iState != EDevSoundReady)
1.354 + {
1.355 + iInitializeState = KRequestPending;
1.356 + iActiveSchedulerWait->Start();
1.357 + }
1.358 +
1.359 + User::LeaveIfError(iInitializeState);
1.360 + }
1.361 + }
1.362 +
1.363 +/**
1.364 +Sets the sink's priority settings.
1.365 +
1.366 +@param aPrioritySettings
1.367 + The sink's priority settings. Takes enumerations to determine audio playback priority.
1.368 + Higher numbers mean high priority (can interrupt lower priorities).
1.369 +*/
1.370 +void CMMFAudioOutput::SetSinkPrioritySettings(const TMMFPrioritySettings& aPrioritySettings)
1.371 + {
1.372 + iPrioritySettings = aPrioritySettings;
1.373 + if (!iMMFDevSound)
1.374 + Panic(EMMFAudioOutputDevSoundNotLoaded);
1.375 + else
1.376 + iMMFDevSound->SetPrioritySettings(iPrioritySettings);
1.377 + }
1.378 +
1.379 +/**
1.380 +Gets the sink's data type code.
1.381 +
1.382 +Used by datapath MDataSource / MDataSink for codec matching.
1.383 +
1.384 +@param aMediaId
1.385 + The Media ID. Optional parameter to specifiy specific stream when datasource contains more
1.386 + than one stream of data.
1.387 +
1.388 +@return The 4CC of the data expected by this sink.
1.389 +*/
1.390 +TFourCC CMMFAudioOutput::SinkDataTypeCode(TMediaId /*aMediaId*/)
1.391 + {
1.392 + return iDataTypeCode;
1.393 + }
1.394 +
1.395 +/**
1.396 +Sets the sink's data type code.
1.397 +
1.398 +@param aSinkFourCC
1.399 + The 4CC of the data to be supplied to this sink.
1.400 +@param aMediaId
1.401 + The Media ID. Optional parameter to specifiy specific stream when datasource contains more
1.402 + than one stream of data.
1.403 +
1.404 +@return An error code indicating if the function call was successful. KErrNone on success, otherwise
1.405 + another of the system-wide error codes.
1.406 +*/
1.407 +TInt CMMFAudioOutput::SetSinkDataTypeCode(TFourCC aSinkFourCC, TMediaId /*aMediaId*/)
1.408 + {//will check with devsound to see if aSinkFourCC is supported
1.409 + //when this is added to devsound
1.410 + iDataTypeCode = aSinkFourCC;
1.411 + return KErrNone;
1.412 + }
1.413 +
1.414 +/**
1.415 +Primes the sink.
1.416 +
1.417 +This is a virtual function that each derived class must implement, but may be left blank for default
1.418 +behaviour.
1.419 +
1.420 +Called by CMMFDataPath::PrimeL().
1.421 +*/
1.422 +void CMMFAudioOutput::SinkPrimeL()
1.423 + {
1.424 + if(iPlayStarted != EFalse)
1.425 + {
1.426 + return;
1.427 + }
1.428 + iPlayStarted = EFalse;
1.429 + iCanSendBuffers = EFalse;
1.430 + if (iState == EIdle)
1.431 + {
1.432 + if (!iMMFDevSound)
1.433 + {
1.434 + User::Leave(KErrNotReady);
1.435 + }
1.436 + iState = EDevSoundReady;
1.437 + }
1.438 + }
1.439 +
1.440 +/**
1.441 +Pauses the sink.
1.442 +
1.443 +This is a virtual function that each derived class must implement, but may be left blank for default
1.444 +behaviour.
1.445 +*/
1.446 +void CMMFAudioOutput::SinkPauseL()
1.447 + {
1.448 + if (!iMMFDevSound)
1.449 + Panic(EMMFAudioOutputDevSoundNotLoaded);
1.450 + else
1.451 + iMMFDevSound->Pause();
1.452 + iPlayStarted = EFalse;
1.453 + iState = EPaused;
1.454 + }
1.455 +
1.456 +/**
1.457 +Starts playing the sink.
1.458 +
1.459 +This is a virtual function that each derived class must implement, but may be left blank for default
1.460 +behaviour.
1.461 +*/
1.462 +void CMMFAudioOutput::SinkPlayL()
1.463 + {
1.464 + if (iState == EPaused)
1.465 + {
1.466 + TBool isResumeSupported = iMMFDevSound->IsResumeSupported();
1.467 + // CMMFAudioOutput is not supposed to be paused
1.468 + // if Resume is not supported by DevSound
1.469 + if(!isResumeSupported)
1.470 + {
1.471 + User::Leave(KErrNotSupported);
1.472 + }
1.473 +
1.474 + User::LeaveIfError(iMMFDevSound->Resume());
1.475 + iPlayStarted = ETrue;
1.476 + }
1.477 + else if (iPlayStarted == EFalse)
1.478 + {
1.479 + ConfigDevSoundL();
1.480 +
1.481 + // This is a one-shot to "prime" MMFDevSound as first buffer uninitialised
1.482 + iMMFDevSound->PlayInitL();
1.483 + iPlayStarted = ETrue;
1.484 + }
1.485 + if ((iNeedsSWConversion)&&(iSourceChannels>0))
1.486 + {//can only do SW convert - therefore need to do a conversion
1.487 + //currently only pcm16 is supported so return with an error if format not pcm16
1.488 + if (!iChannelAndSampleRateConverterFactory)
1.489 + {
1.490 + iChannelAndSampleRateConverterFactory
1.491 + = new(ELeave)CMMFChannelAndSampleRateConverterFactory;
1.492 + iChannelAndSampleRateConverter =
1.493 + iChannelAndSampleRateConverterFactory->CreateConverterL( iSourceSampleRate, iSourceChannels,
1.494 + iSWConvertSampleRate, iSWConvertChannels);
1.495 + }
1.496 + //need to create an intermediate buffer in which to place the converted data
1.497 + }
1.498 + iState = EDevSoundReady;
1.499 + }
1.500 +
1.501 +/**
1.502 +Stops the sink.
1.503 +
1.504 +This is a virtual function that each derived class must implement, but may be left blank for default
1.505 +behaviour.
1.506 +*/
1.507 +void CMMFAudioOutput::SinkStopL()
1.508 + {
1.509 + if (iState == EDevSoundReady)
1.510 + {//not waiting on a buffer being played so stop devsound now
1.511 + iState = EIdle;
1.512 + if (iPlayStarted)
1.513 + {
1.514 + if (!iMMFDevSound)
1.515 + {
1.516 + Panic(EMMFAudioOutputDevSoundNotLoaded);
1.517 + }
1.518 + else
1.519 + {
1.520 + iPlayStarted = EFalse;
1.521 + iMMFDevSound->Stop();
1.522 + }
1.523 + }
1.524 + }
1.525 + else if (iState == EPaused) //DEF46250 need to handle pause separately as we should always stop regardless of the state of iFirstBufferSent
1.526 + {
1.527 + iPlayStarted = EFalse;
1.528 + iMMFDevSound->Stop();
1.529 + iState = EIdle;
1.530 + }
1.531 + iCanSendBuffers = EFalse;
1.532 + }
1.533 +
1.534 +/**
1.535 +Returns the playback state (EStopped, EPlaying, EPaused etc) of this sink
1.536 +*/
1.537 +TInt CMMFAudioOutput::State()
1.538 + {
1.539 + return iState;
1.540 + }
1.541 +
1.542 +/**
1.543 +Logs on the sink's thread.
1.544 +
1.545 +Thread specific initialization procedure for this device. Runs automatically on thread construction.
1.546 +
1.547 +@param aEventHandler
1.548 + The event handler.
1.549 +
1.550 +@return An error code indicating if the function call was successful. KErrNone on success, otherwise
1.551 + another of the system-wide error codes.
1.552 +*/
1.553 +TInt CMMFAudioOutput::SinkThreadLogon(MAsyncEventHandler& aEventHandler)
1.554 + {
1.555 + iEventHandler = &aEventHandler;
1.556 + TInt err = KErrNone;
1.557 + if (!iDevSoundLoaded)
1.558 + TRAP(err, LoadL());
1.559 + return err;
1.560 + }
1.561 +
1.562 +/**
1.563 +Logs off the sink thread.
1.564 +
1.565 +Thread specific destruction procedure for this device. Runs automatically on thread destruction.
1.566 +*/
1.567 +void CMMFAudioOutput::SinkThreadLogoff()
1.568 + {
1.569 + if(iMMFDevSound)
1.570 + {
1.571 + iMMFDevSound->Stop();
1.572 + delete iMMFDevSound;
1.573 + iMMFDevSound = NULL;
1.574 + }
1.575 + iDevSoundLoaded = EFalse;
1.576 + iState = EIdle;
1.577 + }
1.578 +
1.579 +/**
1.580 +Called by MDataSource to pass back a full buffer to the sink.
1.581 +
1.582 +Should never be called by a sink, as sinks empty buffers, not fill them.
1.583 +
1.584 +@param aBuffer
1.585 + The filled buffer.
1.586 +*/
1.587 +void CMMFAudioOutput::BufferFilledL(CMMFBuffer* /*aBuffer*/)
1.588 + {
1.589 + Panic(EMMFAudioOutputPanicBufferFilledLNotSupported);
1.590 + }
1.591 +
1.592 +/**
1.593 +Tests whether a sink buffer can be created.
1.594 +
1.595 +The default implementation returns true.
1.596 +
1.597 +@return A boolean indicating if the sink buffer can be created. ETrue if it can, otherwise EFalse.
1.598 +*/
1.599 +TBool CMMFAudioOutput::CanCreateSinkBuffer()
1.600 + {
1.601 + return ETrue;
1.602 + }
1.603 +
1.604 +/**
1.605 +Creates a sink buffer.
1.606 +
1.607 +Intended for asynchronous usage (buffers supplied by Devsound device)
1.608 +
1.609 +@param aMediaId
1.610 + The Media ID.
1.611 +@param aReference
1.612 + A boolean indicating if MDataSink owns the buffer. ETrue if does, otherwise EFalse.
1.613 +
1.614 +@return A sink buffer.
1.615 +*/
1.616 +CMMFBuffer* CMMFAudioOutput::CreateSinkBufferL(TMediaId /*aMediaId*/, TBool &aReference)
1.617 + {
1.618 + //iDevSoundBuffer = CMMFDataBuffer::NewL(KAudioOutputDefaultFrameSize);
1.619 + iDevSoundBuffer = NULL; //DevSound supplies this buffer in first callback
1.620 + aReference = ETrue;
1.621 + if ( iNeedsSWConversion )
1.622 + return iConvertBuffer;
1.623 + else
1.624 + return iDevSoundBuffer;
1.625 + }
1.626 +
1.627 +/**
1.628 +Standard SymbianOS destructor.
1.629 +*/
1.630 +CMMFAudioOutput::~CMMFAudioOutput()
1.631 + {
1.632 + // The following will never have been allocated unless
1.633 + // software conversion was required, and due to certain DevSound
1.634 + // implementations, this requirement can change dynamically.
1.635 + delete iChannelAndSampleRateConverterFactory;
1.636 + delete iConvertBuffer;
1.637 +
1.638 + if (iMMFDevSound)
1.639 + {
1.640 + iMMFDevSound->Stop();
1.641 + delete iMMFDevSound;
1.642 + }
1.643 + delete iActiveSchedulerWait;
1.644 + }
1.645 +
1.646 +void CMMFAudioOutput::ConfigDevSoundL()
1.647 + {
1.648 + iMMFDevSound->SetConfigL(iDevSoundConfig);
1.649 + }
1.650 +
1.651 +
1.652 +/**
1.653 +@deprecated
1.654 +
1.655 +This method should not be used - it is provided to maintain SC with v7.0s.
1.656 +
1.657 +@param aAudioType
1.658 + The 4CC of the data supplied by this source.
1.659 +*/
1.660 +void CMMFAudioOutput::SetDataTypeL(TFourCC aAudioType)
1.661 + {
1.662 + if (aAudioType != KMMFFourCCCodePCM16)
1.663 + {
1.664 + User::Leave(KErrNotSupported);
1.665 + }
1.666 + }
1.667 +
1.668 +
1.669 +/**
1.670 +@deprecated
1.671 +
1.672 +This method should not be used - it is provided to maintain SC with v7.0s.
1.673 +
1.674 +@return The 4CC of the data supplied by this source.
1.675 +*/
1.676 +TFourCC CMMFAudioOutput::DataType() const
1.677 + {
1.678 + return KMMFFourCCCodePCM16;
1.679 + }
1.680 +
1.681 +/**
1.682 +Queries about DevSound resume support
1.683 +
1.684 +@return ETrue if DevSound does support resume, EFalse otherwise
1.685 +*/
1.686 +TBool CMMFAudioOutput::IsResumeSupported()
1.687 + {
1.688 + TBool isSupported = EFalse;
1.689 + if (iMMFDevSound)
1.690 + {
1.691 + isSupported = iMMFDevSound->IsResumeSupported();
1.692 + }
1.693 + return isSupported;
1.694 + }
1.695 +
1.696 +
1.697 +/**
1.698 +Loads audio device drivers and initialise this device.
1.699 +*/
1.700 +void CMMFAudioOutput::LoadL()
1.701 + {
1.702 + iPlayStarted = EFalse;
1.703 + if (iState != EDevSoundReady)
1.704 + iState = EIdle;
1.705 +
1.706 + iMMFDevSound = CMMFDevSound::NewL();
1.707 +
1.708 + //This is done to maintain compatibility with the media server
1.709 + iMMFDevSound->SetVolume(iMMFDevSound->MaxVolume() - 1);
1.710 +
1.711 + //note cannot perform further initlaisation here untill the datatype is known
1.712 +
1.713 + iDevSoundLoaded = ETrue;
1.714 + }
1.715 +
1.716 +/**
1.717 +DeviceMessage MMFDevSoundObserver
1.718 +*/
1.719 +void CMMFAudioOutput::DeviceMessage(TUid /*aMessageType*/, const TDesC8& /*aMsg*/)
1.720 + {
1.721 + }
1.722 +
1.723 +
1.724 +/**
1.725 +ToneFinished MMFDevSoundObserver called when a tone has finished or interrupted
1.726 +
1.727 +Should never get called.
1.728 +*/
1.729 +void CMMFAudioOutput::ToneFinished(TInt /*aError*/)
1.730 + {
1.731 + //we should never get a tone error in MMFAudioOutput!
1.732 + __ASSERT_DEBUG(EFalse, Panic(EMMFAudioOutputPanicToneFinishedNotSupported));
1.733 + }
1.734 +
1.735 +
1.736 +/**
1.737 +RecordError MMFDevSoundObserver called when recording has halted.
1.738 +
1.739 +Should never get called.
1.740 +*/
1.741 +void CMMFAudioOutput::RecordError(TInt /*aError*/)
1.742 + {
1.743 + //we should never get a recording error in MMFAudioOutput!
1.744 + __ASSERT_DEBUG(EFalse, Panic(EMMFAudioOutputPanicRecordErrorNotSupported));
1.745 + }
1.746 +
1.747 +/**
1.748 +InitializeComplete MMFDevSoundObserver called when devsound initialisation completed.
1.749 +*/
1.750 +void CMMFAudioOutput::InitializeComplete(TInt aError)
1.751 + {
1.752 +
1.753 + if (aError == KErrNone)
1.754 + {
1.755 + iState = EDevSoundReady;
1.756 + }
1.757 +
1.758 + if(iInitializeState == KRequestPending)
1.759 + {
1.760 + iInitializeState = aError;
1.761 + iActiveSchedulerWait->AsyncStop();
1.762 + }
1.763 + }
1.764 +
1.765 +/**
1.766 +BufferToBeEmptied MMFDevSoundObserver - should never get called.
1.767 +*/
1.768 +void CMMFAudioOutput::BufferToBeEmptied(CMMFBuffer* /*aBuffer*/)
1.769 + {
1.770 + __ASSERT_DEBUG(EFalse, Panic(EMMFAudioOutputPanicRecordErrorNotSupported));
1.771 + }
1.772 +
1.773 +/**
1.774 +BufferToBeFilled MMFDevSoundObserver.
1.775 +Called when buffer used up.
1.776 +*/
1.777 +void CMMFAudioOutput::BufferToBeFilled(CMMFBuffer* aBuffer)
1.778 + {
1.779 + TInt err = KErrNone;
1.780 +
1.781 + TRAP(err, iSupplier->BufferEmptiedL(aBuffer));
1.782 +
1.783 + //This error needs handling properly
1.784 + __ASSERT_DEBUG(!err, Panic(err));
1.785 + }
1.786 +
1.787 +/**
1.788 +PlayError MMFDevSoundObserver.
1.789 +
1.790 +Called when stopped due to error or EOF.
1.791 +*/
1.792 +void CMMFAudioOutput::PlayError(TInt aError)
1.793 + {
1.794 + iMMFDevsoundError = aError;
1.795 +
1.796 + //send EOF to client
1.797 + TMMFEvent event(KMMFEventCategoryPlaybackComplete, aError);
1.798 + SendEventToClient(event);
1.799 +
1.800 + //stop stack overflow / looping problem - AD
1.801 + if (aError == KErrCancel)
1.802 + return;
1.803 +
1.804 + // NB KErrInUse, KErrDied OR KErrAccessDenied may be returned by the policy server
1.805 + // to indicate that the sound device is in use by another higher priority client.
1.806 + if (aError == KErrInUse || aError == KErrDied || aError == KErrAccessDenied)
1.807 + return;
1.808 +
1.809 + if (iState == EIdle)
1.810 + {//probably have stopped audio output and have got an underflow from last buffer
1.811 + iMMFDevSound->Stop();
1.812 + iPlayStarted = EFalse;
1.813 + iCanSendBuffers = EFalse;
1.814 + }
1.815 + }
1.816 +
1.817 +
1.818 +/**
1.819 +ConvertError MMFDevSoundObserver.
1.820 +
1.821 +Should never get called.
1.822 +*/
1.823 +void CMMFAudioOutput::ConvertError(TInt /*aError*/)
1.824 + {
1.825 + }
1.826 +
1.827 +
1.828 +/**
1.829 +Returns the number of bytes played.
1.830 +
1.831 +@return The number of bytes played. If 16-bit divide this number returned by 2 to get word length.
1.832 +*/
1.833 +TInt CMMFAudioOutput::BytesPlayed()
1.834 + {
1.835 + if (!iMMFDevSound)
1.836 + Panic(EMMFAudioOutputDevSoundNotLoaded);
1.837 + return iMMFDevSound->SamplesPlayed();
1.838 + }
1.839 +
1.840 +/**
1.841 +Returns the sound device.
1.842 +
1.843 +Accessor function exposing public CMMFDevsound methods.
1.844 +
1.845 +@return A reference to a CMMFDevSound objector.
1.846 +*/
1.847 +CMMFDevSound& CMMFAudioOutput::SoundDevice()
1.848 + {
1.849 + if (!iMMFDevSound)
1.850 + Panic(EMMFAudioOutputDevSoundNotLoaded);
1.851 + return *iMMFDevSound;
1.852 + }
1.853 +
1.854 +void CMMFAudioOutput::SendEventToClient(const TMMFEvent& aEvent)
1.855 + {
1.856 + iEventHandler->SendEventToClient(aEvent);
1.857 + }
1.858 +// __________________________________________________________________________
1.859 +// Exported proxy for instantiation method resolution
1.860 +// Define the interface UIDs
1.861 +
1.862 +
1.863 +
1.864 +const TImplementationProxy ImplementationTable[] =
1.865 + {
1.866 + IMPLEMENTATION_PROXY_ENTRY(KMmfUidAudioOutputInterface, CMMFAudioOutput::NewSinkL)
1.867 + };
1.868 +
1.869 +EXPORT_C const TImplementationProxy* ImplementationGroupProxy(TInt& aTableCount)
1.870 + {
1.871 + aTableCount = sizeof(ImplementationTable) / sizeof(TImplementationProxy);
1.872 +
1.873 + return ImplementationTable;
1.874 + }
1.875 +