// Copyright (c) 2003-2009 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:
//

#include "midiclientutilitybody.h"

const TInt KMimeLength         = 256;
const TInt KMinChannel         = 0;
const TInt KMaxChannel         = 15;
const TInt KMinNote            = 0;
const TInt KMaxNote            = 127;
const TInt KMinNoteOnVelocity  = 0;
const TInt KMaxNoteOnVelocity  = 127;
const TInt KMinNoteOffVelocity = 0;
const TInt KMaxNoteOffVelocity = 127;
const TInt KMinInstrumentId    = 0;
const TInt KMaxInstrumentId    = 127;


CMidiClientUtility::CBody* CMidiClientUtility::CBody::NewL(CMidiClientUtility* aParent,
														   MMidiClientUtilityObserver& aObserver, 
														   TInt aPriority, 
														   TInt aPref,
														   TBool aUseSharedHeap)
	{
	CBody* self = new(ELeave) CBody(aParent, aObserver, aPriority, aPref);
	CleanupStack::PushL(self);
	self->ConstructL(aUseSharedHeap);
	CleanupStack::Pop(self);
	return self;
	}

CMidiClientUtility::CBody::CBody(CMidiClientUtility* aParent, 
								 MMidiClientUtilityObserver& aObserver, 
								 TInt aPriority, 
								 TInt aPref) :
	iObserver(aObserver),
	iMidiControllerCommands(iController),
	iDRMCustomCommands(iController)

	{
	iParent = aParent;
	iState = EMidiStateClosedDisengaged;
	iPrioritySettings.iPriority = aPriority;
	iPrioritySettings.iPref = aPref;
	iIntervalSec = ETrue;
	iStopPosition = TTimeIntervalMicroSeconds(0);
	}

CMidiClientUtility::CBody::~CBody()
	{
	delete iAddDataSourceSinkAsync;
	if (iMidiControllerEventMonitor)
		iMidiControllerEventMonitor->Cancel();
		
	iController.Close();
	delete iMidiControllerEventMonitor;
	delete iMimeType;
	delete iRepeatTrailingSilenceTimer;
	delete iSource;
	}

void CMidiClientUtility::CBody::ConstructL(TBool aUseSharedHeap)
	{
	iMidiControllerEventMonitor = CMidiControllerEventMonitor::NewL(*this, iMidiControllerCommands, *iParent);
	iMimeType = HBufC8::NewL(KMimeLength);
		
	RMMFControllerImplInfoArray controllers;
	CleanupResetAndDestroyPushL(controllers);
	CMMFControllerPluginSelectionParameters* cSelect = CMMFControllerPluginSelectionParameters::NewLC();

	// Select the media IDs to allow
	RArray<TUid> mediaIds;
	CleanupClosePushL(mediaIds);
	User::LeaveIfError(mediaIds.Append(KUidMediaTypeMidi));
	cSelect->SetMediaIdsL(mediaIds,CMMFPluginSelectionParameters::EAllowOnlySuppliedMediaIds);
	CleanupStack::PopAndDestroy();//mediaIds
	cSelect->ListImplementationsL(controllers);
	// Open and configure a controller
	User::LeaveIfError(DoOpen(controllers, KUidMmfAudioOutput, KNullDesC8, aUseSharedHeap));
	CleanupStack::PopAndDestroy(2);//controllers, cSelect
	iRepeatTrailingSilenceTimer = CRepeatTrailingSilenceTimer::NewL(*this);
	iAddDataSourceSinkAsync = CMMFAddDataSourceSinkAsync::NewL(*this);
	}

void CMidiClientUtility::CBody::MadssaoAddDataSourceSinkAsyncComplete(TInt aError, const TMMFMessageDestination& aHandle)
	{
	if (aError == KErrNone)
		{
		iSourceHandle = aHandle;
		}
	else
		{
 		iMidiControllerEventMonitor->SelfComplete(aError);
		}
	}
void CMidiClientUtility::CBody::OpenFile(const TDesC& aFileName)
	{
	TMMFFileConfig sourceCfg;
	sourceCfg().iPath = aFileName;
	// Add the data source to the controller. MmcuoStateChanged will be call on completition.
	iAddDataSourceSinkAsync->AddDataSource(iController, KUidMmfFileSource, sourceCfg);
	}

void CMidiClientUtility::CBody::OpenFile(RFile& aFile)
	{
		// Add the data source to the controller. MmcuoStateChanged will be call on completition.
	iAddDataSourceSinkAsync->AddFileHandleDataSource(iController, aFile);
	}

void CMidiClientUtility::CBody::OpenFile(const TMMSource& aSource)
	{
	TRAPD(err, DoOpenFileL(aSource));
	if (err != KErrNone)
		{
 		iMidiControllerEventMonitor->SelfComplete(err);
		}
	}
	
	
void CMidiClientUtility::CBody::DoOpenFileL(const TMMSource& aSource)
	{
	delete iSource;
	iSource = NULL;
	iSource = CMMFileSourceSink::NewL(KUidMmfFileSource, aSource);
	static_cast<CMMFileSourceSink*>(iSource)->EvaluateIntentL( aSource.Intent() );
	iAddDataSourceSinkAsync->AddDataSource(iController, 
								iSource->SourceSinkUid(),
								iSource->SourceSinkData());
	}

void CMidiClientUtility::CBody::OpenDes(const TDesC8& aDescriptor)
	{
	TMMFDescriptorConfig sourceCfg;
	sourceCfg().iDes = (TAny*)&aDescriptor;
	sourceCfg().iDesThreadId = RThread().Id();
	// Add the data source to the controller. MmcuoStateChanged will be call on completition.
	iAddDataSourceSinkAsync->AddDataSource(iController, KUidMmfDescriptorSource, sourceCfg);
	}

void CMidiClientUtility::CBody::OpenUrl(const TDesC& aUrl,TInt aIapId,const TDesC8& /*aMimeType*/)
	{
	TRAPD(err, DoOpenUrlL(aUrl, aIapId));

	if (err != KErrNone)
		{
 		iMidiControllerEventMonitor->SelfComplete(err);
		}

	}

void CMidiClientUtility::CBody::DoOpenUrlL(const TDesC& aUrl,TInt aIapId)
	{
	CMMFUrlParams* sourceCfg = CMMFUrlParams::NewLC(aUrl, aIapId);
	CBufFlat* sourceCfgBuffer = sourceCfg->ExternalizeToCBufFlatLC();
	// Add the data source to the controller. MmcuoStateChanged will be call on completition.
	iAddDataSourceSinkAsync->AddDataSource(iController, KUidMmfUrlSource, sourceCfgBuffer->Ptr(0));
	CleanupStack::PopAndDestroy(2, sourceCfg);//sourceCfgBuffer, sourceCfg
	}

void CMidiClientUtility::CBody::Close()
	{
	iMidiControllerCommands.Close();
	}

void CMidiClientUtility::CBody::Play()
	{
	TInt err = iController.Prime();
	if (err==KErrNone)
		{
		err=iController.Play();
		}
	if (err!=KErrNone)
		{
		iMidiControllerEventMonitor->SelfComplete(err);
		}
	}

void CMidiClientUtility::CBody::Stop(const TTimeIntervalMicroSeconds& aFadeOutDuration)
	{
	iMidiControllerCommands.Stop(aFadeOutDuration);
	}
	
/**
 *
 * Returns the current state of the MIDI client utility
 * with regard to MIDI resources.
 *
 * @return "TMidiState" The current state of the utility
 *
 * @since	7.0s
 */

TMidiState CMidiClientUtility::CBody::State() const
	{
	return iState;
	}

void CMidiClientUtility::CBody::PlayNoteL(TInt aChannel,TInt aNote,const TTimeIntervalMicroSeconds& aDuration,TInt aNoteOnVelocity,TInt aNoteOffVelocity)
	{
	if((aChannel >= KMinChannel && aChannel <= KMaxChannel) 
	&& (aNote >= KMinNote && aNote <= KMaxNote) 
	&& (aNoteOnVelocity >= KMinNoteOnVelocity && aNoteOnVelocity <= KMaxNoteOnVelocity) 
	&& (aNoteOffVelocity >= KMinNoteOffVelocity && aNoteOffVelocity <= KMaxNoteOffVelocity))
		{	
		User::LeaveIfError(iMidiControllerCommands.PlayNote(aChannel, aNote, aDuration, aNoteOnVelocity, aNoteOffVelocity));
		}
	else
		{
		User::Leave(KErrArgument);
		}
	}

void CMidiClientUtility::CBody::PlayNoteL(TInt aChannel,TInt aNote, const TTimeIntervalMicroSeconds& aStartTime, const TTimeIntervalMicroSeconds& aDuration, TInt aNoteOnVelocity, TInt aNoteOffVelocity)
	{
	if((aChannel >= KMinChannel && aChannel <= KMaxChannel) 
	&& (aNote >= KMinNote && aNote <= KMaxNote) 
	&& (aNoteOnVelocity >= KMinNoteOnVelocity && aNoteOnVelocity <= KMaxNoteOnVelocity) 
	&& (aNoteOffVelocity >= KMinNoteOffVelocity && aNoteOffVelocity <= KMaxNoteOffVelocity))
		{
		User::LeaveIfError(iMidiControllerCommands.PlayNote(aChannel, aNote, aStartTime, aDuration, aNoteOnVelocity, aNoteOffVelocity));
		}
	else
		{
		User::Leave(KErrArgument);
		}
	}

void CMidiClientUtility::CBody::StopNotes(TInt aChannel)
	{
	if(aChannel >= KMinChannel && aChannel <= KMaxChannel) 
		{
		iMidiControllerCommands.StopNotes(aChannel);
		}
	}

void CMidiClientUtility::CBody::NoteOnL(TInt aChannel,TInt aNote,TInt aVelocity)
	{
	if((aChannel >= KMinChannel && aChannel <= KMaxChannel) 
	&& (aNote >= KMinNote && aNote <= KMaxNote) 
	&& (aVelocity >= KMinNoteOnVelocity && aVelocity <= KMaxNoteOnVelocity))
		{
		User::LeaveIfError(iMidiControllerCommands.NoteOn(aChannel, aNote, aVelocity));
		}
	else
		{
		User::Leave(KErrArgument);
		}
	}

void CMidiClientUtility::CBody::NoteOffL(TInt aChannel,TInt aNote,TInt aVelocity)
	{
	if((aChannel >= KMinChannel && aChannel <= KMaxChannel) 
	&& (aNote >= KMinNote && aNote <= KMaxNote) 
	&& (aVelocity >= KMinNoteOffVelocity && aVelocity <= KMaxNoteOffVelocity))
		{
		User::LeaveIfError(iMidiControllerCommands.NoteOff(aChannel, aNote, aVelocity));
		}
	else
		{
		User::Leave(KErrArgument);
		}
	}

TInt CMidiClientUtility::CBody::PlaybackRateL() const
	{
	TInt rate;
	User::LeaveIfError(iMidiControllerCommands.PlaybackRate(rate));
	return rate;
	}

void CMidiClientUtility::CBody::SetPlaybackRateL(TInt aRate)
	{
	User::LeaveIfError(iMidiControllerCommands.SetPlaybackRate(aRate));
	}

TInt CMidiClientUtility::CBody::MaxPlaybackRateL() const
	{
	TInt maxRate;
	User::LeaveIfError(iMidiControllerCommands.MaxPlaybackRate(maxRate));
	return maxRate;
	}

TInt CMidiClientUtility::CBody::MinPlaybackRateL() const
	{
	TInt minRate;
	User::LeaveIfError(iMidiControllerCommands.MinPlaybackRate(minRate));
	return minRate;
	}


TInt CMidiClientUtility::CBody::TempoMicroBeatsPerMinuteL() const
	{
	TInt microBeatsPerMinute;
	User::LeaveIfError(iMidiControllerCommands.TempoMicroBeatsPerMinute(microBeatsPerMinute));
	return microBeatsPerMinute;
	}

void CMidiClientUtility::CBody::SetTempoL(TInt aMicroBeatsPerMinute)
	{
	if(aMicroBeatsPerMinute > 0)
		{
		User::LeaveIfError(iMidiControllerCommands.SetTempo(aMicroBeatsPerMinute));
		}
	else
		{
		User::Leave(KErrArgument);
		}
	}

TInt CMidiClientUtility::CBody::PitchTranspositionCentsL() const
	{
	TInt cents;
	User::LeaveIfError(iMidiControllerCommands.PitchTranspositionCents(cents));
	return cents;
	}

TInt CMidiClientUtility::CBody::SetPitchTranspositionL(TInt aCents)
	{
	TInt pitchApplied = 0;
	//we do not check aCents value - it is expected the controller will report KErrArgument
	//if the pitch level is not supported.
	User::LeaveIfError(iMidiControllerCommands.SetPitchTransposition(aCents, pitchApplied));

	return pitchApplied;
	}

TTimeIntervalMicroSeconds CMidiClientUtility::CBody::DurationMicroSecondsL() const
	{
	TTimeIntervalMicroSeconds duration;
	User::LeaveIfError(iController.GetDuration(duration));
	return duration;
	}

TInt64 CMidiClientUtility::CBody::DurationMicroBeatsL() const
	{
	TInt64 duration;
	User::LeaveIfError(iMidiControllerCommands.DurationMicroBeats(duration));
	return duration;
	}

TInt CMidiClientUtility::CBody::NumTracksL() const
	{
	TInt tracks;
	User::LeaveIfError(iMidiControllerCommands.NumTracks(tracks));
	return tracks;
	}

void CMidiClientUtility::CBody::SetTrackMuteL(TInt aTrack, TBool aMuted) const
	{
	TInt numTracks = NumTracksL();
	if((aTrack >= 0) && (aTrack < numTracks))
		{
		User::LeaveIfError(iMidiControllerCommands.SetTrackMute(aTrack, aMuted));
		}
	else
		{
		User::Leave(KErrArgument);
		}
	}

const TDesC8& CMidiClientUtility::CBody::MimeTypeL()
	{
	TPtr8 des = iMimeType->Des();
	User::LeaveIfError(iMidiControllerCommands.MimeType(des));
	return *iMimeType;
	}

TTimeIntervalMicroSeconds CMidiClientUtility::CBody::PositionMicroSecondsL() const
	{
	TTimeIntervalMicroSeconds position;
	User::LeaveIfError(iController.GetPosition(position));
	return position;
	}

void CMidiClientUtility::CBody::SetPositionMicroSecondsL(const TTimeIntervalMicroSeconds& aPosition)
	{
	TTimeIntervalMicroSeconds maxPosition = DurationMicroSecondsL();
	TTimeIntervalMicroSeconds minPosition(0);

	TTimeIntervalMicroSeconds position = aPosition;
	if (aPosition > maxPosition)
		{
		position = maxPosition;
		}
	if (aPosition < minPosition)
		{
		position = minPosition;
		}
	User::LeaveIfError(iController.SetPosition(position));
	}

TInt64 CMidiClientUtility::CBody::PositionMicroBeatsL() const
	{
	TInt64 position;
	User::LeaveIfError(iMidiControllerCommands.PositionMicroBeats(position));
	return position;
	}

void CMidiClientUtility::CBody::SetPositionMicroBeatsL(TInt64 aMicroBeats)
	{
	TInt64 maxPosition = DurationMicroBeatsL();
	TInt64 minPosition(0);

	TInt64 position = aMicroBeats;
	if (aMicroBeats > maxPosition)
		{
		position = maxPosition;
		}
	if (aMicroBeats < minPosition)
		{
		position = minPosition;
		}
	User::LeaveIfError(iMidiControllerCommands.SetPositionMicroBeats(position));
	}

void CMidiClientUtility::CBody::SetSyncUpdateCallbackIntervalL(const TTimeIntervalMicroSeconds& aMicroSeconds, TInt64 aMicroBeats)
	{
	
	if((aMicroSeconds > TTimeIntervalMicroSeconds(0)) || (aMicroSeconds == TTimeIntervalMicroSeconds(0) && aMicroBeats == 0))
		{
		iIntervalSec = ETrue;
		}
	else 
		{
		if (aMicroBeats > 0)
			{
			iIntervalSec = EFalse;
			}
		else
			{
			User::Leave(KErrArgument);
			}
		}
	
	User::LeaveIfError(iMidiControllerCommands.SetSyncUpdateCallbackInterval(aMicroSeconds, aMicroBeats));
	}

TInt CMidiClientUtility::CBody::SendMessageL(const TDesC8& aMidiMessage)
	{
	TInt numByteProc;
	User::LeaveIfError(iMidiControllerCommands.SendMessage(aMidiMessage, numByteProc));
	return numByteProc;
	}

TInt CMidiClientUtility::CBody::SendMessageL(const TDesC8& aMidiMessage,const TTimeIntervalMicroSeconds& aTime)
	{
	TInt numByteProc;
	User::LeaveIfError(iMidiControllerCommands.SendMessage(aMidiMessage, aTime, numByteProc));
	return numByteProc;
	}

void CMidiClientUtility::CBody::SendMipMessageL(const RArray<TMipMessageEntry>& aEntry)
	{
	User::LeaveIfError(iMidiControllerCommands.SendMipMessage(aEntry));
	}

TInt CMidiClientUtility::CBody::NumberOfBanksL(TBool aCustom) const
	{
	TInt numBanks;
	User::LeaveIfError(iMidiControllerCommands.NumberOfBanks(aCustom, numBanks));
	return numBanks;
	}

TInt CMidiClientUtility::CBody::GetBankIdL(TBool aCustom,TInt aBankIndex) const
	{
	TInt numBanks = NumberOfBanksL(aCustom);
	TInt bankId = 0;
	if(aBankIndex >= 0 && aBankIndex < numBanks)
		{
		User::LeaveIfError(iMidiControllerCommands.GetBankId(aCustom, aBankIndex, bankId));
		}
	else
		{
		User::Leave(KErrArgument);
		}
	return bankId;
	}

void CMidiClientUtility::CBody::LoadCustomBankL(const TDesC& aFileName,TInt& aBankCollectionIndex)
	{
	User::LeaveIfError(iMidiControllerCommands.LoadCustomBank(aFileName, aBankCollectionIndex));
	}

void CMidiClientUtility::CBody::UnloadCustomBankL(TInt aBankCollectionIndex)
	{
	User::LeaveIfError(iMidiControllerCommands.UnloadCustomBank(aBankCollectionIndex));
	}

TBool CMidiClientUtility::CBody::CustomBankLoadedL(TInt aBankCollectionIndex) const
	{
	TBool bankLoaded;
	User::LeaveIfError(iMidiControllerCommands.CustomBankLoaded(aBankCollectionIndex, bankLoaded));
	return bankLoaded;
	}

void CMidiClientUtility::CBody::UnloadAllCustomBanksL()
	{
	User::LeaveIfError(iMidiControllerCommands.UnloadAllCustomBanks());
	}

TInt CMidiClientUtility::CBody::NumberOfInstrumentsL(TInt aBankId,TBool aCustom) const
	{
	TInt numInstruments;
	User::LeaveIfError(iMidiControllerCommands.NumberOfInstruments(aBankId, aCustom, numInstruments));
	return numInstruments;
	}

TInt CMidiClientUtility::CBody::GetInstrumentIdL(TInt aBankId,TBool aCustom,TInt aInstrumentIndex) const
	{
	TInt numInstruments = NumberOfInstrumentsL(aBankId, aCustom);
	TInt instrumentId = 0;
	if(aInstrumentIndex >=0 && aInstrumentIndex < numInstruments)
		{
		User::LeaveIfError(iMidiControllerCommands.GetInstrumentId(aBankId, aCustom, aInstrumentIndex, instrumentId));
		}
	else
		{
		User::Leave(KErrArgument);
		}
	return instrumentId;
	}

HBufC* CMidiClientUtility::CBody::InstrumentNameL(TInt aBankId, TBool aCustom, TInt aInstrumentId) const
	{
	HBufC* instrumentName = NULL;

	if(aInstrumentId >= KMinInstrumentId && aInstrumentId <= KMaxInstrumentId)
		{
		instrumentName = iMidiControllerCommands.InstrumentNameL(aBankId, aCustom, aInstrumentId);
		}
	else
		{
		User::Leave(KErrArgument);
		}

	return instrumentName;
	}

void CMidiClientUtility::CBody::SetInstrumentL(TInt aChannel,TInt aBankId,TInt aInstrumentId)
	{
	if((aChannel >= KMinChannel && aChannel <= KMaxChannel)  
	&& (aInstrumentId >= KMinInstrumentId && aInstrumentId <= KMaxInstrumentId))
		{
		User::LeaveIfError(iMidiControllerCommands.SetInstrument(aChannel, aBankId, aInstrumentId));
		}
	else
		{
		User::Leave(KErrArgument);
		}
	}

void CMidiClientUtility::CBody::LoadCustomInstrumentL(const TDesC& aFileName, TInt aFileBankId, TInt aFileInstrumentId, TInt aMemoryBankId, TInt aMemoryInstrumentId)
	{
	if((aFileInstrumentId >= KMinInstrumentId && aFileInstrumentId <= KMaxInstrumentId) 
	&& (aMemoryInstrumentId >= KMinInstrumentId && aMemoryInstrumentId <= KMaxInstrumentId))
		{
		User::LeaveIfError(iMidiControllerCommands.LoadCustomInstrument(aFileName, aFileBankId, aFileInstrumentId, aMemoryBankId, aMemoryInstrumentId));
		}	
	else
		{
		User::Leave(KErrArgument);
		}
	}

void CMidiClientUtility::CBody::UnloadCustomInstrumentL(TInt aCustomBankId,TInt aInstrumentId)
	{
	if(aInstrumentId >= KMinInstrumentId && aInstrumentId <= KMaxInstrumentId)
		{
		User::LeaveIfError(iMidiControllerCommands.UnloadCustomInstrument(aCustomBankId, aInstrumentId));
		}
	else
		{
		User::Leave(KErrArgument);
		}
	}

HBufC* CMidiClientUtility::CBody::PercussionKeyNameL(TInt aNote, TInt aBankId, TBool aCustom, TInt aInstrumentId) const
	{
	HBufC* pKeyName = NULL;

	if((aNote >= KMinNote && aNote <= KMaxNote) 
	&& (aInstrumentId >= KMinInstrumentId && aInstrumentId <= KMaxInstrumentId))
		{
		pKeyName = iMidiControllerCommands.PercussionKeyNameL(aNote, aBankId, aCustom, aInstrumentId);
		}
	else
		{
		User::Leave(KErrArgument);
		}

	return pKeyName;
	}

void CMidiClientUtility::CBody::StopTimeL(TTimeIntervalMicroSeconds& aStopTime) const
	{
	User::LeaveIfError(iMidiControllerCommands.StopTime(aStopTime));
	}

void CMidiClientUtility::CBody::SetStopTimeL(const TTimeIntervalMicroSeconds& aStopTime)
	{
	TTimeIntervalMicroSeconds duration = DurationMicroSecondsL();
	if(aStopTime >= TTimeIntervalMicroSeconds(0) && aStopTime <= duration)
		{
		User::LeaveIfError(iMidiControllerCommands.SetStopTime(aStopTime));
		}
	else
		{
		User::Leave(KErrArgument);
		}
	}

void CMidiClientUtility::CBody::SetRepeatsL(TInt aRepeatNumberOfTimes, const TTimeIntervalMicroSeconds& aTrailingSilence)
	{
	if((aRepeatNumberOfTimes >= 0) && (aTrailingSilence >= TTimeIntervalMicroSeconds(0)))
		{
		User::LeaveIfError(iMidiControllerCommands.SetRepeats(aRepeatNumberOfTimes, aTrailingSilence));
		}
	else
		{
		User::Leave(KErrArgument);
		}	
	}

TInt CMidiClientUtility::CBody::PolyphonyL() const
	{
	TInt numNotes;
	TInt maxPoly = MaxPolyphonyL();
	User::LeaveIfError(iMidiControllerCommands.Polyphony(numNotes));
	if(maxPoly <= numNotes)
		{
		return maxPoly;
		}
	else
		{
		return numNotes;	
		}
	}

TInt CMidiClientUtility::CBody::MaxPolyphonyL() const
	{
	TInt maxNotes;
	User::LeaveIfError(iMidiControllerCommands.MaxPolyphony(maxNotes));
	return maxNotes;
	}

TInt CMidiClientUtility::CBody::ChannelsSupportedL() const
	{
	TInt channels;
	User::LeaveIfError(iMidiControllerCommands.ChannelsSupported(channels));
	return channels;
	}

TReal32 CMidiClientUtility::CBody::ChannelVolumeL(TInt aChannel) const
	{
	TReal32 channelVol;
	if(aChannel >= KMinChannel && aChannel <= KMaxChannel) 
		{
		User::LeaveIfError(iMidiControllerCommands.ChannelVolume(aChannel, channelVol));
		}
	else
		{
		User::Leave(KErrArgument);	
		}
	return channelVol;
	}

TReal32 CMidiClientUtility::CBody::MaxChannelVolumeL() const
	{
	TReal32 maxChanVol;
	User::LeaveIfError(iMidiControllerCommands.MaxChannelVolume(maxChanVol));
	return maxChanVol;
	}

void CMidiClientUtility::CBody::SetChannelVolumeL(TInt aChannel,TReal32 aVolume)
	{
	TReal32 maxChanVol = MaxChannelVolumeL();
	if((aChannel >= KMinChannel && aChannel <= KMaxChannel) && aVolume <= maxChanVol)
		{
		User::LeaveIfError(iMidiControllerCommands.SetChannelVolume(aChannel, aVolume));
		}
	else
		{
		User::Leave(KErrArgument);
		}
	}

void CMidiClientUtility::CBody::SetChannelMuteL(TInt aChannel,TBool aMuted)
	{
	if(aChannel >= KMinChannel && aChannel <= KMaxChannel)
		{
		User::LeaveIfError(iMidiControllerCommands.SetChannelMute(aChannel, aMuted));
		}
	else
		{
		User::Leave(KErrArgument);
		}
	}

TInt CMidiClientUtility::CBody::VolumeL() const
	{
	TInt vol;
	User::LeaveIfError(iMidiControllerCommands.Volume(vol));
	return vol;
	}

TInt CMidiClientUtility::CBody::MaxVolumeL() const
	{
	TInt maxVol;
	User::LeaveIfError(iMidiControllerCommands.MaxVolume(maxVol));
	return maxVol;
	}

void CMidiClientUtility::CBody::SetVolumeL(TInt aVolume)
	{
	User::LeaveIfError(iMidiControllerCommands.SetVolume(aVolume));
	}

void CMidiClientUtility::CBody::SetVolumeRampL(const TTimeIntervalMicroSeconds& aRampDuration)
	{
	User::LeaveIfError(iMidiControllerCommands.SetVolumeRamp(aRampDuration));
	}


TInt CMidiClientUtility::CBody::GetBalanceL() const
	{
	TInt balance;
	User::LeaveIfError(iMidiControllerCommands.GetBalance(balance));
	return balance;
	}

void CMidiClientUtility::CBody::SetBalanceL(TInt aBalance)
	{
	User::LeaveIfError(iMidiControllerCommands.SetBalance(aBalance));
	}

void CMidiClientUtility::CBody::SetPriorityL(TInt aPriority, TInt aPref)
	{
	TMMFPrioritySettings priority;
	priority.iPriority = aPriority;
	priority.iPref = aPref;

	User::LeaveIfError(iController.SetPrioritySettings(priority));
	}

TInt CMidiClientUtility::CBody::NumberOfMetaDataEntriesL() const
	{
	TInt numMetaData;
	User::LeaveIfError(iController.GetNumberOfMetaDataEntries(numMetaData));
	return numMetaData;
	}

CMMFMetaDataEntry* CMidiClientUtility::CBody::GetMetaDataEntryL(TInt aMetaDataIndex) const
	{
	CMMFMetaDataEntry* metaDataEntry = iController.GetMetaDataEntryL(aMetaDataIndex);
	return metaDataEntry;
	}

void CMidiClientUtility::CBody::CustomCommandSyncL(const TMMFMessageDestinationPckg& aDestination, TInt aFunction, const TDesC8& aDataTo1, const TDesC8& aDataTo2, TDes8& aDataFrom)
	{
	User::LeaveIfError(iController.CustomCommandSync(aDestination, aFunction, aDataTo1, aDataTo2, aDataFrom));
	}

void CMidiClientUtility::CBody::CustomCommandSyncL(const TMMFMessageDestinationPckg& aDestination, TInt aFunction, const TDesC8& aDataTo1, const TDesC8& aDataTo2)
	{
	User::LeaveIfError(iController.CustomCommandSync(aDestination, aFunction, aDataTo1, aDataTo2));
	}

void CMidiClientUtility::CBody::CustomCommandAsync(const TMMFMessageDestinationPckg& aDestination, TInt aFunction, const TDesC8& aDataTo1, const TDesC8& aDataTo2, TDes8& aDataFrom, TRequestStatus& aStatus)
	{
	iController.CustomCommandAsync(aDestination, aFunction, aDataTo1, aDataTo2, aDataFrom, aStatus);
	}

void CMidiClientUtility::CBody::CustomCommandAsync(const TMMFMessageDestinationPckg& aDestination, TInt aFunction, const TDesC8& aDataTo1, const TDesC8& aDataTo2, TRequestStatus& aStatus)
	{
	iController.CustomCommandAsync(aDestination, aFunction, aDataTo1, aDataTo2, aStatus);
	}

MMMFDRMCustomCommand* CMidiClientUtility::CBody::GetDRMCustomCommand()
	{
	if (iDRMCustomCommands.IsSupported())
		{
		return static_cast<MMMFDRMCustomCommand*>(&iDRMCustomCommands);
		}
	return NULL;
	}
	
TInt CMidiClientUtility::CBody::DoOpen(const RMMFControllerImplInfoArray& aControllers, TUid aSinkUid, const TDesC8& aSinkData, TBool aUseSharedHeap)
	{
	// Make sure any existing controller is closed.
	iMidiControllerEventMonitor->Cancel();
	iController.Close();

	// Try opening and configuring each controller in turn
	TInt error = KErrNotSupported;
	TInt index = 0;
	while (error)
		{
		// Break if we're at the end of the array of controllers
		if (index >= aControllers.Count())
			break;

		// Open the controller
		error = iController.Open(aControllers[index]->Uid(), iPrioritySettings, aUseSharedHeap);

		// If the controller was opened without error, start receiving events from it.
		if (error==KErrNone)
			{
			iMidiControllerEventMonitor->Start();
			}

		// Add the data sink
		if (error==KErrNone)
			error = iController.AddDataSink(aSinkUid, aSinkData, iSinkHandle);

		// If an error occurred in any of the above, close the controller.
		if (error!=KErrNone)
			{
			iMidiControllerEventMonitor->Cancel();
			iController.Close();
			}

		// increment index
		index++;
		}

	return error;
	}

   void CMidiClientUtility::CBody::HandleMidiEvent(const CMMFMidiEvent& aEvent)
   	{
   	if(aEvent.iEventType == KMMFEventCategoryMidiOpenDataSourceComplete ||
   	   aEvent.iEventType == KMMFEventCategoryMidiClose ||
   	   aEvent.iEventType == KMMFEventCategoryMidiPrime ||
   	   aEvent.iEventType == KMMFEventCategoryMidiPlaying ||
   	   aEvent.iEventType == KMMFEventCategoryMidiPlaybackIncompatible ||
   	   aEvent.iEventType == KMMFEventCategoryMidiPlaybackSilent)
   		{
   		iState = aEvent.iNewState;
   		iObserver.MmcuoStateChanged(aEvent.iOldState, aEvent.iNewState, aEvent.iMicroSeconds, aEvent.iErrorCode);
   		
   		if (aEvent.iEventType == KMMFEventCategoryMidiClose)
   			{
			if (iSourceHandle.DestinationHandle())
				{
				iController.RemoveDataSource(iSourceHandle);
				}
			}	
 		}
   	else if(aEvent.iEventType == KMMFEventCategoryMidiPlayingComplete)
   		{
 		iState = aEvent.iNewState;
   		iObserver.MmcuoStateChanged(aEvent.iOldState, aEvent.iNewState, aEvent.iMicroSeconds, aEvent.iErrorCode);
   		}
   	else if(aEvent.iEventType == KMMFEventCategoryMidiSyncUpdate)
   		{
   		iObserver.MmcuoSyncUpdate(aEvent.iMicroSeconds, aEvent.iMicroBeats);
   		}
   	else if(aEvent.iEventType == KMMFEventCategoryTempoChanged)
   		{
   		iObserver.MmcuoTempoChanged(aEvent.iTempoMicroBeats);
   		}
   	else if(aEvent.iEventType == KMMFEventCategoryVolumeChanged)
   		{
   		iObserver.MmcuoVolumeChanged(aEvent.iChannel, aEvent.iVolumeInDecibels);
   		}
   	else if(aEvent.iEventType == KMMFEventCategoryMuteChanged)
   		{
   		iObserver.MmcuoMuteChanged(aEvent.iChannel, aEvent.iMute);
   		}
   	else if(aEvent.iEventType == KMMFEventCategoryMetaDataEntryFound)
   		{
   		iObserver.MmcuoMetaDataEntryFound(aEvent.iMetaDataEntryId, aEvent.iMicroSeconds);
   		}
   	else if(aEvent.iEventType == KMMFEventCategoryMipMessageReceived)
   		{
   		iObserver.MmcuoMipMessageReceived(aEvent.iMipMessage);
   		}
   	else if(aEvent.iEventType == KMMFEventCategoryPolyphonyChanged)
   		{
   		iObserver.MmcuoPolyphonyChanged(aEvent.iPolyphony);
   		}
   	else if(aEvent.iEventType == KMMFEventCategoryInstrumentChanged)
   		{
   		iObserver.MmcuoInstrumentChanged(aEvent.iChannel,aEvent.iBankId,aEvent.iInstrumentId);
   		}
   	else if((iState == EMidiStateOpenPlaying) || (iState == EMidiStatePlaybackIncompatible) || 
   			(iState == EMidiStatePlaybackSilent) || (iState == EMidiStateClosedEngaged) ||
   			(iState == EMidiStateOpenEngaged))
   		{
 		iState = aEvent.iNewState;
   		iObserver.MmcuoStateChanged(aEvent.iOldState, aEvent.iNewState, aEvent.iMicroSeconds, aEvent.iErrorCode);

   		}
   	else if (aEvent.iEventType == KMMFErrorCategoryControllerGeneralError)
   		{
   		iObserver.MmcuoStateChanged(iState, iState, TTimeIntervalMicroSeconds(0), aEvent.iErrorCode);
   		}
   	else
   		{
   		// FIXME - what do we do when we don't understand the error type?
   		}
   	}

/**
 *
 * Used to change the value of MaxPolyphonyL()
 */ 	
void CMidiClientUtility::CBody::SetMaxPolyphonyL(TInt aMaxNotes)
	{
	User::LeaveIfError(iMidiControllerCommands.SetMaxPolyphony(aMaxNotes));
	}

TInt CMidiClientUtility::CBody::GetRepeats()
	{
	TInt numRepeats = 0;
	iMidiControllerCommands.GetRepeats(numRepeats);
	return numRepeats;
	}

void CMidiClientUtility::CBody::LoadCustomBankDataL(const TDesC8& aBankData,TInt& aBankId)
	{
	User::LeaveIfError(iMidiControllerCommands.LoadCustomBankData(aBankData, aBankId));
	}

void CMidiClientUtility::CBody::LoadCustomInstrumentDataL(const TDesC8& aInstrumentData, TInt aBankDataId, TInt aInstrumentDataId, TInt aMemoryBankId, TInt aMemoryInstrumentId)
	{
	if((aInstrumentDataId >= KMinInstrumentId && aInstrumentDataId <= KMaxInstrumentId) 
	&& (aMemoryInstrumentId >= KMinInstrumentId && aMemoryInstrumentId <= KMaxInstrumentId))
		{
		User::LeaveIfError(iMidiControllerCommands.LoadCustomInstrumentData(aInstrumentData, aBankDataId, aInstrumentDataId, aMemoryBankId, aMemoryInstrumentId));
		}
	else
		{
		User::Leave(KErrArgument);
		}
	}

void CMidiClientUtility::CBody::SetBankL(TBool aCustom)
	{
	User::LeaveIfError(iMidiControllerCommands.SetBank(aCustom));
	}

TBool CMidiClientUtility::CBody::IsTrackMuteL(TInt aTrack) const
	{
	TBool mute;
	User::LeaveIfError(iMidiControllerCommands.IsTrackMute(aTrack, mute));
	return mute;
	}

TBool CMidiClientUtility::CBody::IsChannelMuteL(TInt aChannel) const
	{
	TBool mute;
	if (aChannel >= KMinChannel && aChannel <= KMaxChannel)
		{
		User::LeaveIfError(iMidiControllerCommands.IsChannelMute(aChannel, mute));	
		}
	else
		{
		User::Leave(KErrArgument);	
		}
		
	return mute;
	}

void CMidiClientUtility::CBody::GetInstrumentL(TInt aChannel, TInt& aInstrumentId, TInt& aBankId)
	{
	if (aChannel >= KMinChannel && aChannel <= KMaxChannel)
		{
		User::LeaveIfError(iMidiControllerCommands.GetInstrument(aChannel, aInstrumentId, aBankId));	
		}
	else
		{
		User::Leave(KErrArgument);		
		}
	}

void CMidiClientUtility::CBody::RepeatTrailingSilenceTimerComplete()
	{
	Play();
	}

CRepeatTrailingSilenceTimer* CRepeatTrailingSilenceTimer::NewL(MRepeatTrailingSilenceTimerObs& aObs)
	{
	CRepeatTrailingSilenceTimer* s = new(ELeave) CRepeatTrailingSilenceTimer(aObs);
	CleanupStack::PushL(s);
	s->ConstructL();
	CleanupStack::Pop();
	return s;
	}

void CRepeatTrailingSilenceTimer::RunL()
	{
	iObs.RepeatTrailingSilenceTimerComplete();
	}

CRepeatTrailingSilenceTimer::CRepeatTrailingSilenceTimer(MRepeatTrailingSilenceTimerObs& aObs) :
	CTimer(EPriorityHigh),
	iObs(aObs)
	{
	CActiveScheduler::Add(this);
	}

//
//
//
//

CMidiControllerEventMonitor* CMidiControllerEventMonitor::NewL(MMidiControllerEventMonitorObserver& aMidiObserver, 
												RMidiControllerCustomCommands& aMidiControllerCustomCommands, const CMidiClientUtility& aParent)
	{
	CMidiControllerEventMonitor* self = new(ELeave) CMidiControllerEventMonitor(aMidiObserver, aMidiControllerCustomCommands, aParent);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

void CMidiControllerEventMonitor::ConstructL()
	{
	iMidiEvent = new (ELeave) CMMFMidiEvent();
	}

CMidiControllerEventMonitor::CMidiControllerEventMonitor(MMidiControllerEventMonitorObserver& aMidiObserver, 
													   RMidiControllerCustomCommands& aMidiControllerCustomCommands, const CMidiClientUtility& aParent) :
	CActive(EPriorityStandard),
	iMidiObserver(aMidiObserver), 
	iMidiControllerCustomCommands(aMidiControllerCustomCommands),
	iParent(aParent)
	{
	CActiveScheduler::Add(this);
	}

CMidiControllerEventMonitor::~CMidiControllerEventMonitor()
	{
	Cancel();
	delete iMidiEvent;
	}

/**
Start receiving events from the controller.

This can only be called once the controller is open.
*/
void CMidiControllerEventMonitor::Start()
	{
	iMidiControllerCustomCommands.ReceiveEvents(iSizeOfEvent, iStatus);
	SetActive();
	}

void CMidiControllerEventMonitor::RunL()
	{
	User::LeaveIfError(iStatus.Int());

	// Create a buffer big enough to hold the event, then retrieve it from the server
	HBufC8* buf = HBufC8::NewLC(iSizeOfEvent());
	TPtr8 bufPtr = buf->Des();
	User::LeaveIfError(iMidiControllerCustomCommands.RetrieveEvent(bufPtr));

	// Now internalize a CMMFMidiEvent with the info in the buffer
	RDesReadStream stream(bufPtr);
	CleanupClosePushL(stream);

	CMMFMidiEvent* theEvent = new (ELeave) CMMFMidiEvent();
	
	CleanupStack::PushL(theEvent);
	theEvent->InternalizeL(stream);
	
	iMidiObserver.HandleMidiEvent(*theEvent);
	Start();

	CleanupStack::PopAndDestroy(3);//buf, stream, theEvent
	}

void CMidiControllerEventMonitor::SelfComplete(TInt aError)
	{
	Cancel();
	TRequestStatus *status = &iStatus;
	if(!IsActive())
		SetActive();
	User::RequestComplete(status, aError);
	}

void CMidiControllerEventMonitor::DoCancel()
	{
	iMidiControllerCustomCommands.CancelReceiveEvents();
	}

TInt CMidiControllerEventMonitor::RunError(TInt aError)
	{
	iMidiEvent->iEventType = KMMFErrorCategoryControllerGeneralError;
	iMidiEvent->iErrorCode = aError;
	iMidiEvent->iOldState = iParent.State();
	iMidiEvent->iNewState = iMidiEvent->iOldState;

	iMidiObserver.HandleMidiEvent(*iMidiEvent);
	Start();
	return KErrNone;
	}
