diff -r 000000000000 -r d54f32e146dd tactilefeedback/tactilefeedbackresolver/plugins/tactileaudioplugin/src/tactileaudioplayer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tactilefeedback/tactilefeedbackresolver/plugins/tactileaudioplugin/src/tactileaudioplayer.cpp Thu Dec 17 08:53:38 2009 +0200 @@ -0,0 +1,690 @@ +/* +* Copyright (c) 2007 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: Class for producing audio feedback. +* Part of: Tactile Feedback. +* +*/ + + +#include + +#include +#include + +#include +#include +#include + +#include "tactilefeedbackprivatecrkeys.h" +#include "tactilefeedbacktrace.h" + +#include "tactileaudioplayer.h" +#include "OstTraceDefinitions.h" +#ifdef OST_TRACE_COMPILER_IN_USE +#include "tactileaudioplayerTraces.h" +#endif + +const TInt KRiffHeaderSize = 44; +_LIT8( KRiff, "RIFF" ); +_LIT8( KWave, "WAVE" ); +_LIT8( KFmt, "fmt " ); +_LIT8( KData, "data" ); + +// --------------------------------------------------------------------------- +// Constructor. +// --------------------------------------------------------------------------- +// +CTactileAudioPlayer::CTactileAudioPlayer( CRepository& aRepository ): + iRepository( aRepository ) + { + } + +// --------------------------------------------------------------------------- +// 2nd phase constructor. +// --------------------------------------------------------------------------- +// +void CTactileAudioPlayer::ConstructL() + { + TRACE("CTactileAudioPlayer::ConstructL()"); + + ReadSettingsL(); + + CreateWavPlayerL(); + + iCenRepNotifier = CCenRepNotifyHandler::NewL( *this, + iRepository, + CCenRepNotifyHandler::EIntKey, + KTactileFeedbackAudioVolume ); + iCenRepNotifier->StartListeningL(); + + TRACE("CTactileAudioPlayer::ConstructL() - end"); + } + +// --------------------------------------------------------------------------- +// 2-phased constructor. +// --------------------------------------------------------------------------- +// +CTactileAudioPlayer* CTactileAudioPlayer::NewL( CRepository& aRepository ) + { + CTactileAudioPlayer* self = + new ( ELeave ) CTactileAudioPlayer( aRepository ); + CleanupStack::PushL( self ); + self->ConstructL( ); + CleanupStack::Pop( self ); + return self; + } + +// --------------------------------------------------------------------------- +// Destructor. +// --------------------------------------------------------------------------- +// +CTactileAudioPlayer::~CTactileAudioPlayer() + { + if ( iAudioPlayer ) + { + iAudioPlayer->Stop(); + delete iAudioPlayer; + } + + delete iCenRepNotifier; + delete iSensitiveSample; + delete iBasicSample; + delete iSensitiveFileName; + delete iBasicFileName; + + iVolumeLevels.Close(); + } + + +// --------------------------------------------------------------------------- +// From class CTactilePlayer +// +// Currently we only select volume level according to logical feedback type +// (i.e. the .wav -file cannot be selected separately for each logical +// feedback type). +// +// We don't do anything in case volume level has been set to zero for the +// given feedback type (this way e.g. dragging feedback can be disabled +// totally if that is wanted). +// +// Notice that currently the ETouchFeedbackSensitive -feedback is a bit +// troublesome: Even though we have our own thread for feedback playing, +// it still lags behind when something is dragged on screen. Final solution +// may be to configure audio .wav dragging feedback OFF, and use audio +// tone for dragging instead. +// --------------------------------------------------------------------------- +// +TInt CTactileAudioPlayer::PlayFeedback( TTouchLogicalFeedback aFeedback ) + { + TRACE2( "CTactileAudioPlayer::PlayFeedback( %d )", aFeedback ); + TInt volumeIndex(0); + + switch ( aFeedback ) + { + case ETouchFeedbackBasic: // flow through + case ETouchFeedbackBasicButton: // flow through + case ETouchFeedbackList: // flow through + case ETouchFeedbackBoundaryList: // flow through + case ETouchFeedbackSlider: // flow through + case ETouchFeedbackEdit: // flow through + case ETouchFeedbackSensitiveInput: + case ETouchFeedbackLineSelection: // flow through + case ETouchFeedbackBlankSelection: // flow through + case ETouchFeedbackTextSelection: // flow through + case ETouchFeedbackEmptyLineSelection: // flow through + case ETouchFeedbackTab: // flow through + case ETouchFeedbackPopUp: // flow through + case ETouchFeedbackIncreasingPopUp: // flow through + case ETouchFeedbackDecreasingPopUp: // flow through + case ETouchFeedbackFlick: // flow through + case ETouchFeedbackCheckbox: // flow through + case ETouchFeedbackCharacterInputButton: + case ETouchFeedbackMultiTouchRecognized: + volumeIndex = 0; + break; + case ETouchFeedbackSensitive: // flow through + case ETouchFeedbackSensitiveButton: // flow through + case ETouchFeedbackSensitiveList: + volumeIndex = 1; + break; + default: + // should not be there at all + TRACE2( "CTactileAudioPlayer::PlayFeedback - %d is not a feedback type - returning", aFeedback ); + break; + } + + + if ( volumeIndex <= iVolumeLevels.Count() && + iVolumeLevels[volumeIndex] > 0 && + iAudioPlayer ) + { + switch ( iState ) + { + case ETactileAudioInitialising: + // can't play anything yet + break; + case ETactileAudioPlaying: // fall trough + case ETactileAudioReady: + OstTrace1( TACTILE_PERFORMANCE, TACTILE_PLAY_AUDIO_FEEDBACK_1, + "e_TACTILE_PLAY_AUDIO_FEEDBACK 1 0x%x", aFeedback ); + + if ( aFeedback == ETouchFeedbackBasic ) + { + TRAP_IGNORE( iAudioPlayer->WriteL( *iBasicSample ) ); + } + else + { + TRAP_IGNORE( iAudioPlayer->WriteL( *iSensitiveSample ) ); + } + OstTrace1( TACTILE_PERFORMANCE, TACTILE_PLAY_AUDIO_FEEDBACK_0, + "e_TACTILE_PLAY_AUDIO_FEEDBACK 0 0x%x", aFeedback ); + iState = ETactileAudioPlaying; + break; + case ETactileAudioError: + break; + default: + // should not be here + TRACE("CTactileAudioPlayer::PlayFeedback - error with audio device"); + break; + + } + + } + else + { + if ( !iAudioPlayer ) + { + TRACE("CTactileAudioPlayer::PlayFeedback - iAudioPlayer is NULL!"); + } + else + { + TRACE("CTactileAudioPlayer::PlayFeedback - volume levels not ok"); + } + } + TRACE2( "CTactileAudioPlayer::PlayFeedback( %d ) - end", aFeedback ); + return KErrNone; + } + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +TInt CTactileAudioPlayer::PlayPreviewFeedback( TInt aLevel, + TTouchLogicalFeedback aFeedback ) + { + TRACE("CTactileAudioPlayer::PlayPreviewFeedback - Begin"); + TInt ret( KErrArgument ); + + iOriginalVolume = iCurrentVolume; + iCurrentVolume = aLevel; + iAudioPlayer->SetVolume( ScaledVolume() ); + ret = PlayFeedback( aFeedback ); + iCurrentVolume = iOriginalVolume; + iAudioPlayer->SetVolume( ScaledVolume() ); + + TRACE("CTactileAudioPlayer::PlayPreviewFeedback - End"); + return ret; + } + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +void CTactileAudioPlayer::MaoscOpenComplete( TInt aError ) + { + if ( aError == KErrNone ) + { + iCurrentVolume = iVolumeLevels[0]; + iMaxRawVolume = iAudioPlayer->MaxVolume(); + iRepository.Get( KTactileFeedbackAudioVolume, iCurrentVolume ); + iAudioPlayer->SetVolume( ScaledVolume() ); + TRAP_IGNORE( iAudioPlayer->SetDataTypeL( KMMFFourCCCodePCM16 ) ); + iState = ETactileAudioReady; + } + else + { + TRACE2("CTactileAudioPlayer::MaoscOpenComplete( %d ) failed, will not play feedback", aError ); + delete iAudioPlayer; + iAudioPlayer = NULL; + + iState = ETactileAudioError; + } + } + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +void CTactileAudioPlayer::MaoscPlayComplete( TInt aError ) + { + TRACE("CTactileAudioPlayer::MaoscPlayComplete - Begin"); + iAudioPlayer->Stop(); + if ( aError == KErrCorrupt ) + { + TRACE2("CTactileAudioPlayer::MaoscPlayComplete( %d ) failed, will not play feedback", aError ); + delete iAudioPlayer; + iAudioPlayer = NULL; + + iState = ETactileAudioError; + } + else + { + iState = ETactileAudioReady; + } + TRACE("CTactileAudioPlayer::MaoscPlayComplete - End"); + } + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +void CTactileAudioPlayer::MaoscBufferCopied( TInt aError, const TDesC8& /*aBuffer*/ ) + { + if ( !aError ) + { + iState = ETactileAudioReady; // may write another sample + } + else + { + TRACE2("CTactileAudioPlayer::MaoscPlayComplete( %d ) failed", aError); + } + } + +// --------------------------------------------------------------------------- +// Settings are now not read completely, as only the .wav -file of basic +// feedback is read, and same file is used for sensitive feedback. +// Using of separate files for basic and sensitive feedback +// can be added later in case necessary. +// --------------------------------------------------------------------------- +// +void CTactileAudioPlayer::ReadSettingsL() + { + TRACE("CTactileAudioPlayer::ReadSettingsL - Begin"); + + iVolumeLevels.Reset(); + + delete iSensitiveFileName; + iSensitiveFileName = NULL; + + delete iBasicFileName; + iBasicFileName = NULL; + + iSensitiveFileName = HBufC::NewL( KMaxFileName ); + TPtr sensitiveFileName = iSensitiveFileName->Des(); + + iBasicFileName = HBufC::NewL( KMaxFileName ); + TPtr basicFileName = iBasicFileName->Des(); + + TInt basicVolumeLevel = 0; + TInt sensitiveVolumeLevel = 0; + + // Read volume values for level 2 + iRepository.Get( KTactileAudioWavVolumeBasicLevel2, basicVolumeLevel ); + + + iRepository.Get( KTactileAudioWavVolumeSensitiveLevel2, sensitiveVolumeLevel ); + + // Read file names + iRepository.Get( KTactileAudioWavFileBasicLevel2, basicFileName ); + + iRepository.Get( KTactileAudioWavFileSensitiveLevel2, sensitiveFileName ); + + iVolumeLevels.Append( basicVolumeLevel ); + iVolumeLevels.Append( sensitiveVolumeLevel ); + + TRACE("CTactileAudioPlayer::ReadSettingsL() - End"); + } + + +// --------------------------------------------------------------------------- +// ScaledVolume() +// return 100% scaled & sanity checked volume value +// --------------------------------------------------------------------------- +// +TInt CTactileAudioPlayer::ScaledVolume() + { + if ( iMaxRawVolume == KErrNotFound ) + { + TRACE( "CTactileAudioPlayer::ScaledVolume() Audio Hardware is not initialized properly" ); + return 0; + } + + TInt volume = iMaxRawVolume * iCurrentVolume / 100; + + if ( volume > iMaxRawVolume ) + { + // sanity check, we might get anything from cenrep + volume = iMaxRawVolume; + } + + TRACE4( "CTactileAudioPlayer::ScaledVolume() iCurrentVolume:%d scales volume to:%d out of iMaxRawVolume:%d", + iCurrentVolume, volume, iMaxRawVolume ); + + return volume; + } + +/* +* Wave file structure: Riff header + Wave format chunk + Wave data chunk +* +* Riff header: +* ================================================== +* Offset Size Description Value +* 0x00 4 Chunk ID "RIFF" +* 0x04 4 Chunk Data Size ( file size ) - 8 +* 0x08 4 RIFF Type "WAVE" +* 0x0c * Wave chunks * +* +* Wave Format Chunk: +* ================================================== +* Offset Size Description Value +* 0x00 4 Chunk ID "fmt " +* 0x04 4 Chunk Data Size 16 + extra format bytes ( 0 for normal wav files) +* 0x08 2 Compression code 1...65535 ( 1 for PCM uncompressed) +* 0x0a 2 Nbr of channels 1...65535 +* 0x0c 4 Sample rate 1...0xFFFFFFFF +* 0x10 4 Average bytes per sec +* 0x14 2 block align +* 0x16 2 siginificant bits per sample +* 0x18 2 extra format bytes 0...65535 +* 0x1a * extra format bytes, if any * +* +* Wave Data Chunk: +* ================================================== +* Offset Size Description Value +* 0x00 4 Chunk ID "data" +* 0x04 4 Chunk data size +* 0x08 * sample data * +* +*/ +void CTactileAudioPlayer::ReadSampleL( RFile& aFile, + HBufC8*& aDes, + TUint& aChannels, + TUint& aSampleRate ) + { + TRACE("CTactileAudioPlayer::ReadSampleL - Start"); + const TInt fmtOffset = 0x0c; + const TInt dataOffset = fmtOffset + 0x18; + + TBuf8 header; + TInt err = aFile.Read( header, KRiffHeaderSize ); + if ( err ) + { + TRACE("CTactileAudioPlayer::ReadSampleL: reading from file failed, aborting"); + User::Leave( err ); + } + + TPtr8 p = header.LeftTPtr( 4 ); + + if ( p.Compare( KRiff ) ) + { + TRACE("CTactileAudioPlayer::ReadSampleL: no RIFF header found, aborting" ); + User::Leave( KErrCorrupt ); + } + + p = header.MidTPtr( 0x08, 4 ); + if ( p.Compare( KWave ) ) + { + TRACE("CTactileAudioPlayer::ReadSampleL: not a WAVE file, aborting" ); + User::Leave( KErrCorrupt ); + } + + p = header.MidTPtr( fmtOffset + 0x00, 4 ); + if ( p.Compare( KFmt ) ) + { + TRACE("CTactileAudioPlayer::ReadSampleL: no 'fmt ' chunk found, aborting" ); + User::Leave( KErrCorrupt ); + } + + p = header.MidTPtr( dataOffset, 4 ); + if ( p.Compare( KData ) ) + { + TRACE("CTactileAudioPlayer::ReadSampleL: no 'data' chunk found, aborting" ); + User::Leave( KErrCorrupt ); + } + + TUint8 lo = header[ fmtOffset + 0x08 ]; + TUint8 hi = header[ fmtOffset + 0x08 + 1 ]; + if ( !( lo == 1 && hi == 0) ) + { + TRACE("CTactileAudioPlayer::ReadSampleL: non PCM wav not supported, aborting" ); + User::Leave( KErrNotSupported ); + } + + lo = header[ fmtOffset + 0x0a ]; + hi = header[ fmtOffset + 0x0a + 1 ]; + aChannels = lo; + if ( !(aChannels == 1 || aChannels == 2 && hi == 0) ) + { + TRACE2("CTactileAudioPlayer::ReadSampleL: unsupported number of channels ( %d ), aborting", aChannels ); + User::Leave( KErrNotSupported ); + } + + aSampleRate = 0; + for ( TInt i = 0; i < 4; i++ ) + { + lo = header[ fmtOffset + 0x0c + i ]; + TUint32 tmp = lo; + tmp = tmp << i * 8; + + aSampleRate = aSampleRate | tmp; + } + + lo = header[ fmtOffset + 0x16 ]; + hi = header[ fmtOffset + 0x16 + 1 ]; + TUint16 bitsPerSample = hi; + bitsPerSample = bitsPerSample << 8; + bitsPerSample = bitsPerSample | lo; + + if ( bitsPerSample != 16 ) + { + TRACE2("CTactileAudioPlayer::ReadSampleL: %d bits per sample not supported", bitsPerSample ); + User::Leave( KErrNotSupported ); + } + + TUint32 bytesPerSample = bitsPerSample / 8; + + // how many bytes for 6 ms + TUint bytesNeeded = ( aSampleRate * aChannels * bytesPerSample * 6 ) / 1000; + + TInt fsize( 0 ); + if ( aFile.Size( fsize ) == KErrNone && fsize >= bytesNeeded + KRiffHeaderSize ) + { + aDes = HBufC8::NewL( bytesNeeded ); + TPtr8 des = aDes->Des(); + aFile.Read( des, bytesNeeded ); + } + else + { + TRACE("CTactileAudioPlayer::ReadSampleL: Less than 6ms content in file, aborting" ); + User::Leave( KErrNotSupported ); + } + + TRACE3("CTactileAudioPlayer::ReadSampleL %dHz %dchannel sample read successfully - End", + iSampleRate, iChannels); + } + +// --------------------------------------------------------------------------- +// Audio data reading from file and creation of the actual player utility. +// +// Rest of the initializations are done in MapcInitComplete -function. +// +// Notice that CMdaAudioOutputStream usage is not a good idea, as it +// buffers samples until buffer is full and flushes all in a row. Result is +// no feedback first, then a machine gun burst at some completely +// unrelated moment. +// --------------------------------------------------------------------------- +// +void CTactileAudioPlayer::CreateWavPlayerL() + { + TRACE("CTactileAudioPlayer::CreateWavPlayerL - Start"); + iState = ETactileAudioInitialising; + delete iBasicSample; + iBasicSample = NULL; + delete iSensitiveSample; + iSensitiveSample = NULL; + + if ( !( iBasicFileName && iBasicFileName->Length() > 0 && + iSensitiveFileName && iSensitiveFileName->Length() > 0 )) + { + TRACE("CTactileAudioPlayer::CreateWavPlayerL filenames missing, aborting"); + User::Leave( KErrBadName ); + } + + RFs fs; + User::LeaveIfError ( fs.Connect() ); + CleanupClosePushL( fs ); + + RFile file; + TInt err = file.Open( fs, *iBasicFileName, EFileRead ); + if ( err ) + { + TRACE2("CTactileAudioPlayer::CreateWavPlayerL could not open %S, aborting", iBasicFileName ); + User::Leave( err ); + } + + CleanupClosePushL( file ); + ReadSampleL( file, iBasicSample, iChannels, iSampleRate ); + CleanupStack::PopAndDestroy(&file); + + RFile file2; + err = file2.Open( fs, *iSensitiveFileName, EFileRead ); + if ( err ) + { + TRACE2("CTactileAudioPlayer::CreateWavPlayerL could not open %S, aborting", iSensitiveFileName ); + User::Leave( err ); + } + + CleanupClosePushL( file2 ); + + TUint sampleRate(0); + TUint channels(0); + ReadSampleL( file2, iSensitiveSample, channels, sampleRate ); + CleanupStack::PopAndDestroy( &file2 ); + CleanupStack::PopAndDestroy( &fs ); + + if ( channels != iChannels || sampleRate != iSampleRate ) + { + TRACE("CTactileAudioPlayer::CreateWavPlayerL Sample rates and number of channels must be same for both files, aborting"); + User::Leave( KErrNotSupported ); + } + + TInt priority = KAudioPriorityKeyPressNonDTMFWithFeedback; + TMdaPriorityPreference pref = static_cast(KAudioPrefKeyPressNonDTMFWithFeedback); + + iAudioPlayer = CMdaAudioOutputStream::NewL( *this ,priority, pref); + + TMdaAudioDataSettings streamSettings; + switch ( iChannels ) + { + case 1: + streamSettings.iChannels = TMdaAudioDataSettings::EChannelsMono; + break; + case 2: + streamSettings.iChannels = TMdaAudioDataSettings::EChannelsStereo; + TRACE("CTactileAudioPlayer::CreateWavPlayerL Warning: stereo wav for feedback"); + break; + default: + TRACE2("CTactileAudioPlayer::CreateWavPlayerL: unsupported number of channels %d, aborting", iChannels ); + User::Leave( KErrNotSupported ); + break; + } + + // there must be easier way to do this... + switch( iSampleRate ) + { + case 48000: + streamSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate48000Hz; + streamSettings.iCaps = TMdaAudioDataSettings::ESampleRate48000Hz; + break; + case 44100: + TRACE2("CTactileAudioPlayer::CreateWavPlayerL: warning, samplerate %d causes performance problem", iSampleRate ); + streamSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate44100Hz; + streamSettings.iCaps = TMdaAudioDataSettings::ESampleRate44100Hz; + break; + case 32000: + streamSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate32000Hz; + streamSettings.iCaps = TMdaAudioDataSettings::ESampleRate32000Hz; + break; + case 24000: + streamSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate24000Hz; + streamSettings.iCaps = TMdaAudioDataSettings::ESampleRate24000Hz; + break; + case 16000: + streamSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate16000Hz; + streamSettings.iCaps = TMdaAudioDataSettings::ESampleRate16000Hz; + break; + case 12000: + streamSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate12000Hz; + streamSettings.iCaps = TMdaAudioDataSettings::ESampleRate12000Hz; + break; + case 11025: + TRACE2("CTactileAudioPlayer::CreateWavPlayerL: warning, samplerate %d causes performance problem",iSampleRate ); + streamSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate11025Hz; + streamSettings.iCaps = TMdaAudioDataSettings::ESampleRate11025Hz; + break; + case 8000: + streamSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate8000Hz; + streamSettings.iCaps = TMdaAudioDataSettings::ESampleRate8000Hz; + break; + default: + TRACE2("CTactileAudioPlayer::CreateWavPlayerL: unsupported samplerate %d, aborting",iSampleRate ); + User::Leave( KErrNotSupported ); + break; + } + + iAudioPlayer->Open( &streamSettings ); + + TRACE("CTactileAudioPlayer::CreateWavPlayerL - End"); + } + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +void CTactileAudioPlayer::HandleNotifyInt( TUint32 aId, TInt aNewValue ) + { + TRACE("CTactileTonePlayer::HandleNotifyInt - Begin"); + if ( aId == KTactileFeedbackAudioVolume ) + { + iCurrentVolume = aNewValue; + iAudioPlayer->SetVolume( ScaledVolume() ); + } + TRACE("CTactileTonePlayer::HandleNotifyInt - End"); + } + + +//--------------------------------------------------------------------------- +// ImplementationTable[] +// +//--------------------------------------------------------------------------- +// +const TImplementationProxy ImplementationTable[] = + { + IMPLEMENTATION_PROXY_ENTRY( 0x2002133A, CTactileAudioPlayer::NewL ) + }; + +//--------------------------------------------------------------------------- +// TImplementationProxy* ImplementationGroupProxy() +// +//--------------------------------------------------------------------------- +// +EXPORT_C const TImplementationProxy* ImplementationGroupProxy( TInt& aTableCount ) + { + aTableCount = sizeof(ImplementationTable) / sizeof(TImplementationProxy); + return ImplementationTable; + } + + + +// End of file