sl@0: // Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // This file contains an implementation of the ToneGenerator interface sl@0: // that converts all tone generation requests in to sampled audio sl@0: // data to be played through the normal local sampled audio interface sl@0: // sl@0: // sl@0: sl@0: #include "BtToneGenerator.h" sl@0: sl@0: #include sl@0: #include sl@0: sl@0: /****************************************************************************** sl@0: * Tone Generators sl@0: * sl@0: * The following classes are used to generate simple frequency/duration tones, sl@0: * DTMF, and SymbianOS tone sequences in a WINS environment. The below code sl@0: * should only be considered for WINS. sl@0: ******************************************************************************/ sl@0: sl@0: // this defines the maximum possible amplitude allowed for TSineGen::SetFrequency() sl@0: const TInt KMaxAmplitude = 0x8000; sl@0: sl@0: // default number of samples for trailing silence following a Tone sl@0: const TInt KDefaultTrailingSilenceSamples = 20; sl@0: sl@0: // sl@0: // Sine tone generator sl@0: // sl@0: sl@0: const TInt16 TSineGen::SineTable[KMaxSineTable] = sl@0: { sl@0: 0, 804, 1607, 2410, 3211, 4011, 4807, 5601, sl@0: 6392, 7179, 7961, 8739, 9511, 10278, 11038, 11792, sl@0: 12539, 13278, 14009, 14732, 15446, 16150, 16845, 17530, sl@0: 18204, 18867, 19519, 20159, 20787, 21402, 22004, 22594, sl@0: 23169, 23731, 24278, 24811, 25329, 25831, 26318, 26789, sl@0: 27244, 27683, 28105, 28510, 28897, 29268, 29621, 29955, sl@0: 30272, 30571, 30851, 31113, 31356, 31580, 31785, 31970, sl@0: 32137, 32284, 32412, 32520, 32609, 32678, 32727, 32757, sl@0: 32767, 32757, 32727, 32678, 32609, 32520, 32412, 32284, sl@0: 32137, 31970, 31785, 31580, 31356, 31113, 30851, 30571, sl@0: 30272, 29955, 29621, 29268, 28897, 28510, 28105, 27683, sl@0: 27244, 26789, 26318, 25831, 25329, 24811, 24278, 23731, sl@0: 23169, 22594, 22004, 21402, 20787, 20159, 19519, 18867, sl@0: 18204, 17530, 16845, 16150, 15446, 14732, 14009, 13278, sl@0: 12539, 11792, 11038, 10278, 9511, 8739, 7961, 7179, sl@0: 6392, 5601, 4807, 4011, 3211, 2410, 1607, 804, sl@0: 0, -804, -1607, -2410, -3211, -4011, -4807, -5601, sl@0: -6392, -7179, -7961, -8739, -9511,-10278,-11038,-11792, sl@0: -12539,-13278,-14009,-14732,-15446,-16150,-16845,-17530, sl@0: -18204,-18867,-19519,-20159,-20787,-21402,-22004,-22594, sl@0: -23169,-23731,-24278,-24811,-25329,-25831,-26318,-26789, sl@0: -27244,-27683,-28105,-28510,-28897,-29268,-29621,-29955, sl@0: -30272,-30571,-30851,-31113,-31356,-31580,-31785,-31970, sl@0: -32137,-32284,-32412,-32520,-32609,-32678,-32727,-32757, sl@0: -32767,-32757,-32727,-32678,-32609,-32520,-32412,-32284, sl@0: -32137,-31970,-31785,-31580,-31356,-31113,-30851,-30571, sl@0: -30272,-29955,-29621,-29268,-28897,-28510,-28105,-27683, sl@0: -27244,-26789,-26318,-25831,-25329,-24811,-24278,-23731, sl@0: -23169,-22594,-22004,-21402,-20787,-20159,-19519,-18867, sl@0: -18204,-17530,-16845,-16150,-15446,-14732,-14009,-13278, sl@0: -12539,-11792,-11038,-10278, -9511, -8739, -7961, -7179, sl@0: -6392, -5601, -4807, -4011, -3211, -2410, -1607, -804, sl@0: }; sl@0: sl@0: const TInt16 TSineGen::IncTable[KMaxSineTable] = sl@0: { sl@0: 804, 803, 803, 801, 800, 796, 794, sl@0: 791, 787, 782, 778, 772, 767, 760, 754, sl@0: 747, 739, 731, 723, 714, 704, 695, 685, sl@0: 674, 663, 652, 640, 628, 615, 602, 590, sl@0: 575, 562, 547, 533, 518, 502, 487, 471, sl@0: 455, 439, 422, 405, 387, 371, 353, 334, sl@0: 317, 299, 280, 262, 243, 224, 205, 185, sl@0: 167, 147, 128, 108, 89, 69, 49, 30, sl@0: 10, -10, -30, -49, -69, -89, -108, -128, sl@0: -147, -167, -185, -205, -224, -243, -262, -280, sl@0: -299, -317, -334, -353, -371, -387, -405, -422, sl@0: -439, -455, -471, -487, -502, -518, -533, -547, sl@0: -562, -575, -590, -602, -615, -628, -640, -652, sl@0: -663, -674, -685, -695, -704, -714, -723, -731, sl@0: -739, -747, -754, -760, -767, -772, -778, -782, sl@0: -787, -791, -794, -796, -800, -801, -803, -803, sl@0: -804, -804, -803, -803, -801, -800, -796, -794, sl@0: -791, -787, -782, -778, -772, -767, -760, -754, sl@0: -747, -739, -731, -723, -714, -704, -695, -685, sl@0: -674, -663, -652, -640, -628, -615, -602, -590, sl@0: -575, -562, -547, -533, -518, -502, -487, -471, sl@0: -455, -439, -422, -405, -387, -371, -353, -334, sl@0: -317, -299, -280, -262, -243, -224, -205, -185, sl@0: -167, -147, -128, -108, -89, -69, -49, -30, sl@0: -10, 10, 30, 49, 69, 89, 108, 128, sl@0: 147, 167, 185, 205, 224, 243, 262, 280, sl@0: 299, 317, 334, 353, 371, 387, 405, 422, sl@0: 439, 455, 471, 487, 502, 518, 533, 547, sl@0: 562, 575, 590, 602, 615, 628, 640, 652, sl@0: 663, 674, 685, 695, 704, 714, 723, 731, sl@0: 739, 747, 754, 760, 767, 772, 778, 782, sl@0: 787, 791, 794, 796, 800, 801, 803, 803, sl@0: 804 sl@0: }; sl@0: sl@0: void TSineGen::SetFrequency(TInt aFrequency,TInt aAmplitude) sl@0: // sl@0: // Given the frequency set iStep. sl@0: // Reset iPosition to the equivalent of 0 degrees. sl@0: // In the special case of aFrequency==4KHz set iPosition to 90 degrees. sl@0: // sl@0: { sl@0: sl@0: if (aAmplitude>(1<<15)) sl@0: iAmplitude=(1<<15); sl@0: else if (aAmplitude<-(1<<15)) sl@0: iAmplitude=-(1<<15); sl@0: else sl@0: iAmplitude=aAmplitude; sl@0: // sl@0: // There are 256 entries in the sine table to traverse 360 degrees. sl@0: // The codec requires samples at a rate of 8000 per second. sl@0: // Thus for a 1Hz tone the step will be 256/8000 or 4/125. sl@0: // Now we need need the integer part of the result to end up in sl@0: // the MSB so we need to multiply by 2^24. This gives the formula sl@0: // step = (f*4*2^24)/125 or (f*2^26)/125. sl@0: // Our highest frequency is 4KHz so that the term (f*2^26) exceeds sl@0: // a 32 bit result by 4000/2^6 (2^6 is the number of significant bits sl@0: // left after a multiply by 2^26). i.e. 6 bits. We overcome this by sl@0: // having 6 bits less in the fraction, so the new formula becomes sl@0: // ((f*2^20)/125)*2^6. This still gives us 20 significant bits in the sl@0: // fraction. sl@0: // sl@0: sl@0: iStep=(((TUint)aFrequency<<20)/125)<<6; sl@0: iPosition=(aFrequency==4000 ? 0x40000000 : 0); sl@0: } sl@0: sl@0: TInt TSineGen::NextSample() sl@0: // sl@0: // Generate the next sample using linear interpolation sl@0: // sl@0: { sl@0: TUint pos=iPosition>>24; sl@0: TInt amp=((IncTable[pos]*((iPosition&0x00ffffff)>>20))); sl@0: amp>>=4; sl@0: amp+=SineTable[pos]; sl@0: amp=(amp*iAmplitude)>>15; sl@0: iPosition+=iStep; sl@0: return(amp); sl@0: } sl@0: sl@0: void TSineWave::Generate(TInt16* aDest,TInt aCount) sl@0: // sl@0: // Called when more samples need to be generated. sl@0: // sl@0: { sl@0: while (aCount--) sl@0: { sl@0: *aDest++=STATIC_CAST(TInt16,iGen1.NextSample()+iGen2.NextSample()); sl@0: } sl@0: } sl@0: sl@0: void TSineWave::SetFrequency(TInt aFrequency,TInt aAmplitude) sl@0: // sl@0: // Set to generate a single frequency sl@0: // sl@0: { sl@0: SetFrequency(aFrequency,aAmplitude,0,0); sl@0: } sl@0: sl@0: void TSineWave::SetFrequency(TInt aFrequency1,TInt aAmplitude1,TInt aFrequency2,TInt aAmplitude2) sl@0: // sl@0: // Set to generate two frequencies sl@0: // sl@0: { sl@0: iGen1.SetFrequency(aFrequency1,aAmplitude1); sl@0: iGen2.SetFrequency(aFrequency2,aAmplitude2); sl@0: } sl@0: sl@0: sl@0: // sl@0: // TMdaToneGenerator sl@0: // sl@0: sl@0: void TMdaToneGenerator::Configure(TInt aRate, TInt aChannels, TInt aRepeats, TInt aSilence, TInt aRampUp) sl@0: // sl@0: // Set up this tone generator to generate data at the desired sample rate sl@0: // and number of channels (typically mono/stereo) sl@0: // sl@0: { sl@0: iRate = aRate; sl@0: iChannels = aChannels; sl@0: iSamplesLeft = 0; sl@0: iRampUp = ETrue; // Default ramping to on as it is normally useful sl@0: iRampDown = ETrue; sl@0: iRepeats = aRepeats; sl@0: iSilenceBetweenRepeats = aSilence; sl@0: iRampUpCount = aRampUp; sl@0: iRampUpLeft = aRampUp; sl@0: iAfterRepeatSilence = EFalse; sl@0: } sl@0: sl@0: LOCAL_C void RampVolume(TInt16* aData,TInt aCount,TInt aStartVol,TInt aEndVol) sl@0: // sl@0: // Simple function to ramp down the volume of some samples sl@0: // Typically used to prevent "clicking" artifacts at the beginning/end of tones sl@0: // sl@0: { sl@0: TInt step = (aEndVol - aStartVol)/aCount; sl@0: while (aCount--) sl@0: { sl@0: TInt data = TInt(*aData) * aStartVol; sl@0: *aData++ = TInt16(data>>15); sl@0: aStartVol += step; sl@0: } sl@0: } sl@0: sl@0: TInt TMdaToneGenerator::FillBuffer(TDes8& aBuffer) sl@0: // sl@0: // Fill the supplied buffer with tone data sl@0: // Sets the buffer length to zero if there is no more data to play sl@0: // The buffer must have a max length of at least one sample * channels sl@0: // e.g. 2 bytes mono, 4 bytes stereo sl@0: // sl@0: { sl@0: ASSERT(aBuffer.MaxLength()>= (iChannels<<1)); sl@0: aBuffer.SetMax(); sl@0: sl@0: TBool silence; sl@0: TInt samples = 0; // sl@0: TInt used = 0; // Data used sl@0: TInt avail = aBuffer.Length(); // Data filled sl@0: TInt count = 0; // Data to be converted sl@0: TBool rampUp = EFalse; sl@0: sl@0: TMdaPtr8 fill; sl@0: fill.Set(aBuffer); // Pointer to data left to be filled sl@0: sl@0: // sl@0: // The rest of this function will loop around continually until the buffer sl@0: // is filled or there is no more data to play sl@0: // sl@0: sl@0: Restart: sl@0: silence = EFalse; // Reset sl@0: if (iSamplesLeft == 0) sl@0: { sl@0: if (iTrailingSilence == 0) sl@0: { sl@0: TInt error = GetNextTone(); sl@0: if (error) sl@0: return error; sl@0: sl@0: rampUp = ETrue; sl@0: if ((iSamplesLeft==0)&&(iTrailingSilence==0)) sl@0: { sl@0: if ((iSilenceBetweenRepeats)&&(!iAfterRepeatSilence)) sl@0: { sl@0: iTrailingSilence = iSilenceBetweenRepeats; sl@0: iAfterRepeatSilence = ETrue; sl@0: goto Restart; sl@0: } sl@0: else sl@0: { sl@0: if ((iRepeats>0)||(iRepeats==KMdaRepeatForever)) sl@0: { sl@0: iAfterRepeatSilence = EFalse; sl@0: if (iRepeats>0) sl@0: iRepeats--; sl@0: sl@0: Reset(); sl@0: goto Restart; sl@0: } sl@0: } sl@0: // No more to play sl@0: goto Finished; sl@0: } sl@0: goto Restart; sl@0: } sl@0: else sl@0: { sl@0: silence = ETrue; sl@0: samples = iTrailingSilence; sl@0: } sl@0: } sl@0: else sl@0: samples = iSamplesLeft; sl@0: sl@0: count = Min(samples,avail>>1); sl@0: fill.SetLength(count<<1); sl@0: sl@0: if (!silence) sl@0: { // Generate wave sl@0: iSineWave.Generate(REINTERPRET_CAST(TInt16*,&fill[0]),count); sl@0: if (iRampUp) sl@0: { // Ramp up volume at beginning of tone sl@0: const TInt KRampUpSamples = 50; sl@0: if (rampUp) sl@0: { // Fade in first few samples sl@0: TInt fadeInLength = Min(Min(KRampUpSamples,iSamplesLeft),(fill.Length()>>1)); sl@0: RampVolume(CONST_CAST(TInt16*,REINTERPRET_CAST(const TInt16*,(&fill[0]))),fadeInLength,0,1<<15); sl@0: } sl@0: } sl@0: if (iRampDown) sl@0: { // Ramp down volume at end of tone sl@0: const TInt KRampDownSamples = 50; sl@0: if ((iSamplesLeft-count) < KRampDownSamples) sl@0: { // Fade out last few samples sl@0: TInt fadeOutLength = Min(Min(KRampDownSamples,iSamplesLeft),(fill.Length()>>1)); sl@0: RampVolume(CONST_CAST(TInt16*,REINTERPRET_CAST(const TInt16*,(&(fill.Right(fadeOutLength<<1))[0]))),fadeOutLength,1<<15,0); sl@0: } sl@0: } sl@0: iSamplesLeft -= count; sl@0: } sl@0: else sl@0: { // Generate silence sl@0: fill.FillZ(count<<1); sl@0: iTrailingSilence -= count; sl@0: } sl@0: sl@0: used += count<<1; sl@0: avail -= count<<1; sl@0: fill.Shift(count<<1); sl@0: sl@0: if (avail>(iChannels<<1)) sl@0: goto Restart; sl@0: sl@0: Finished: sl@0: sl@0: aBuffer.SetLength(used); sl@0: sl@0: // Do any ramp up that is required sl@0: if (iRampUpLeft>0) sl@0: { sl@0: TInt words = iRampUpLeft * iChannels; sl@0: words = Min(words,used>>1); sl@0: if (words>0) // In case buffer has zero length... sl@0: { sl@0: TInt left = iRampUpLeft * iChannels; sl@0: TInt rampup = iRampUpCount * iChannels; sl@0: iRampUpLeft -= words/iChannels; sl@0: TInt16* sample = REINTERPRET_CAST(TInt16*,&aBuffer[0]); sl@0: while (words--) sl@0: { sl@0: *sample++ = STATIC_CAST(TInt16,(TInt32(*sample)*(rampup-(left--)))/rampup); sl@0: } sl@0: } sl@0: } sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: TInt TMdaToneGenerator::DurationToSamples(const TTimeIntervalMicroSeconds& aDuration) sl@0: // sl@0: // Convert the given duration to a sample count using the current settings sl@0: // sl@0: { sl@0: const TInt64 KTInt64OneMilion = 1000000; sl@0: sl@0: // Calculate duration as samples sl@0: TInt64 microSeconds(aDuration.Int64()); // MSVC doesn't like "aDuration.Int64()" in line below sl@0: TInt64 dur = ((TInt64(iRate) * TInt64(iChannels) * microSeconds) / KTInt64OneMilion); sl@0: if (I64HIGH(dur)>0) sl@0: return KMaxTInt; // Ridiculous! sl@0: else sl@0: return I64LOW(dur); sl@0: } sl@0: sl@0: // sl@0: // TMdaSimpleToneGenerator sl@0: // sl@0: sl@0: void TMdaSimpleToneGenerator::Reset() sl@0: { sl@0: iPlayed = EFalse; sl@0: } sl@0: sl@0: void TMdaSimpleToneGenerator::SetFrequencyAndDuration(TInt aFrequency, const TTimeIntervalMicroSeconds& aDuration) sl@0: // sl@0: // Store the frequency and duration of the specified sine tone sl@0: // sl@0: { sl@0: iFrequency = aFrequency; sl@0: iDuration = aDuration; sl@0: iPlayed = EFalse; sl@0: } sl@0: sl@0: TInt TMdaSimpleToneGenerator::GetNextTone() sl@0: // sl@0: // Simple implementation - just sets the supplied frequency and duration sl@0: // sl@0: { sl@0: // This class only plays one tone for the specified duration sl@0: if (!iPlayed) sl@0: { sl@0: iSamplesLeft = I64LOW((iDuration.Int64() * TInt64(iRate))/1000000); sl@0: iSineWave.SetFrequency(iFrequency,1<<14); sl@0: iPlayed = ETrue; sl@0: iTrailingSilence = 20; // Just to stop clicking sl@0: } sl@0: return KErrNone; sl@0: } sl@0: sl@0: // sl@0: // TMdaDualToneGenerator sl@0: // sl@0: sl@0: void TMdaDualToneGenerator::Reset() sl@0: { sl@0: iPlayed = EFalse; sl@0: } sl@0: sl@0: void TMdaDualToneGenerator::SetFrequencyAndDuration(TInt aFrequencyOne, TInt aFrequencyTwo, const TTimeIntervalMicroSeconds& aDuration) sl@0: { sl@0: // Store the frequencies and duration of the specified dual tone sl@0: iFrequencyOne = aFrequencyOne; sl@0: iFrequencyTwo = aFrequencyTwo; sl@0: iDuration = aDuration; sl@0: iPlayed = EFalse; sl@0: } sl@0: sl@0: // sl@0: // This is called by TMdaToneGenerator::FillBuffer() sl@0: // to calculate the number of samples (iSamplesLeft) that will be needed sl@0: // for the tone to be played and to initialize the sine wave generator. sl@0: // If the tone has already been played, then leaves iSamplesLeft sl@0: // unmodified (should be zero) to indicate that it has finished. sl@0: // sl@0: TInt TMdaDualToneGenerator::GetNextTone() sl@0: { sl@0: // This class only plays one tone for the specified duration sl@0: if (!iPlayed) sl@0: { sl@0: iSamplesLeft = I64LOW((iDuration.Int64() * TInt64(iRate))/KOneMillionMicroSeconds); sl@0: iSineWave.SetFrequency(iFrequencyOne, KMaxAmplitude/2, iFrequencyTwo, KMaxAmplitude/2); sl@0: iPlayed = ETrue; sl@0: iTrailingSilence = KDefaultTrailingSilenceSamples; // Just to stop clicking sl@0: } sl@0: return KErrNone; sl@0: } sl@0: // sl@0: // TMdaDTMFGenerator sl@0: // sl@0: sl@0: const TInt KRecalculateToneLengths = KMinTInt; sl@0: sl@0: void TMdaDTMFGenerator::Reset() sl@0: { sl@0: iChar = 0; sl@0: } sl@0: sl@0: void TMdaDTMFGenerator::SetToneDurations(const TTimeIntervalMicroSeconds32 aOn, sl@0: const TTimeIntervalMicroSeconds32 aOff, sl@0: const TTimeIntervalMicroSeconds32 aPause) sl@0: // sl@0: // Setup the DTMF tone durations sl@0: // aOn can be == -1 indicating should play first tone indefinately sl@0: // sl@0: { sl@0: ASSERT(aOn.Int() >=-1); sl@0: ASSERT(aOff.Int()>=0); sl@0: ASSERT(aPause.Int()>=0); sl@0: sl@0: iOn = aOn; sl@0: iOff = aOff; sl@0: iPause = aPause; sl@0: sl@0: iOnSamples = KRecalculateToneLengths; // Must recalculate these later sl@0: } sl@0: sl@0: void TMdaDTMFGenerator::SetString(const TDesC& aDTMFString) sl@0: // sl@0: // Store the DTMF string to be played sl@0: // No need to validate it as it will already have been checked sl@0: // sl@0: { sl@0: iChar = 0; sl@0: iDTMFString = &aDTMFString; sl@0: } sl@0: sl@0: const TUint8 KDtmfVolumeTable[4][4]= sl@0: // sl@0: // Relative strengths to assign to different DTMF tones sl@0: // sl@0: // This is only important if DTMFs are being played through a speaker sl@0: // and need to be machine-recognisable. This table compensates for frequency sl@0: // drop-off in the speaker and can boost the relative volume of some sl@0: // frequencies so they are still within tolerance. sl@0: // sl@0: // The values normally need to be determined using a frequency analyser on sl@0: // the hardware sl@0: // sl@0: // Each column == same low frequency (697, 770, 852, 941 Hz) sl@0: // Each row == same high frequency (1209, 1336, 1477, 1633 Hz) sl@0: // sl@0: // The value are interpreted as ratios: sl@0: // 0 == 100% low sl@0: // 7f == 50% low, 50% high sl@0: // ff == 100% high sl@0: // sl@0: { sl@0: {38,27,29,37}, sl@0: {46,36,36,46}, sl@0: {62,47,49,58}, sl@0: {70,56,60,68} sl@0: }; sl@0: sl@0: const TUint8 KDtmfTone697=0x0; sl@0: const TUint8 KDtmfTone770=0x1; sl@0: const TUint8 KDtmfTone852=0x2; sl@0: const TUint8 KDtmfTone941=0x3; sl@0: sl@0: const TUint8 KDtmfTone1209=0x00; sl@0: const TUint8 KDtmfTone1336=0x10; sl@0: const TUint8 KDtmfTone1477=0x20; sl@0: const TUint8 KDtmfTone1633=0x30; sl@0: sl@0: const TUint8 KDtmfToneTable[16]= sl@0: { sl@0: KDtmfTone941|KDtmfTone1336,//0 sl@0: KDtmfTone697|KDtmfTone1209,//1 sl@0: KDtmfTone697|KDtmfTone1336,//2 sl@0: KDtmfTone697|KDtmfTone1477,//3 sl@0: KDtmfTone770|KDtmfTone1209,//4 sl@0: KDtmfTone770|KDtmfTone1336,//5 sl@0: KDtmfTone770|KDtmfTone1477,//6 sl@0: KDtmfTone852|KDtmfTone1209,//7 sl@0: KDtmfTone852|KDtmfTone1336,//8 sl@0: KDtmfTone852|KDtmfTone1477,//9 sl@0: sl@0: KDtmfTone697|KDtmfTone1633,//A sl@0: KDtmfTone770|KDtmfTone1633,//B sl@0: KDtmfTone852|KDtmfTone1633,//C sl@0: KDtmfTone941|KDtmfTone1633,//D sl@0: KDtmfTone941|KDtmfTone1209,//E or * sl@0: KDtmfTone941|KDtmfTone1477,//F or # sl@0: }; sl@0: sl@0: TInt TMdaDTMFGenerator::GetNextTone() sl@0: // sl@0: // Setup frequency/duration/silence settings for next DTMF tone sl@0: // Supported characters are 0-9 A-F * # , and any kind of white space sl@0: // sl@0: { sl@0: TBool onlyPlayFirstTone = EFalse; sl@0: sl@0: if (iOnSamples == KRecalculateToneLengths) sl@0: { sl@0: // Must recalculate tone durations as samples sl@0: sl@0: // Handle special case where tone on duration negative sl@0: // - meaning play first character indefinately sl@0: if (iOn.Int()>=0) sl@0: iOnSamples = DurationToSamples(TInt64(iOn.Int())); sl@0: else sl@0: { sl@0: onlyPlayFirstTone = ETrue; sl@0: iOnSamples = -1; sl@0: } sl@0: sl@0: iOffSamples = DurationToSamples(TInt64(iOff.Int())); sl@0: iPauseSamples = DurationToSamples(TInt64(iPause.Int())); sl@0: } sl@0: sl@0: ASSERT(iDTMFString); sl@0: sl@0: if (iChar==iDTMFString->Length()) sl@0: return KErrNone; // Finished. Nothing to do sl@0: sl@0: TInt highFrequency = 0; sl@0: TInt highVolume = 0; sl@0: TInt lowFrequency = 0; sl@0: TInt lowVolume =0; sl@0: sl@0: Retry: sl@0: TChar c((*iDTMFString)[iChar++]); sl@0: if ((TUint)c=='#' || (TUint)c=='*' || c.IsHexDigit()) sl@0: { sl@0: TInt tableIndex; sl@0: switch ((TUint)c) sl@0: { sl@0: case '*': sl@0: tableIndex=14; sl@0: break; sl@0: case '#': sl@0: tableIndex=15; sl@0: break; sl@0: default: sl@0: if (c.IsDigit()) sl@0: tableIndex=(TUint)c-'0'; sl@0: else //letter sl@0: { sl@0: c.UpperCase(); sl@0: tableIndex=(TUint)c-'A'+10; sl@0: } sl@0: } sl@0: TInt high=KDtmfToneTable[tableIndex]&0xf0; sl@0: TInt low=KDtmfToneTable[tableIndex]&0x0f; sl@0: switch(high) sl@0: { sl@0: case KDtmfTone1209: sl@0: highFrequency=1209; sl@0: break; sl@0: case KDtmfTone1336: sl@0: highFrequency=1336; sl@0: break; sl@0: case KDtmfTone1477: sl@0: highFrequency=1477; sl@0: break; sl@0: default://KDtmfTone1633: sl@0: highFrequency=1633; sl@0: break; sl@0: } sl@0: switch(low) sl@0: { sl@0: case KDtmfTone697: sl@0: lowFrequency=697; sl@0: break; sl@0: case KDtmfTone770: sl@0: lowFrequency=770; sl@0: break; sl@0: case KDtmfTone852: sl@0: lowFrequency=852; sl@0: break; sl@0: default://KDtmfTone941: sl@0: lowFrequency=941; sl@0: break; sl@0: } sl@0: high>>=4; sl@0: const TUint8* dtmfVolumes=&KDtmfVolumeTable[0][0]; sl@0: TInt volume=dtmfVolumes[((low)<<2)+(high)]<<7; sl@0: highVolume = volume; sl@0: lowVolume = (1<<15)-volume; sl@0: sl@0: iTrailingSilence = iOffSamples; sl@0: iSamplesLeft = iOnSamples; sl@0: } sl@0: else if ((TUint)c==',') sl@0: { sl@0: iTrailingSilence = iPauseSamples; sl@0: iSamplesLeft = 0; sl@0: } sl@0: else if (c.IsSpace()) sl@0: { sl@0: if (iChar < iDTMFString->Length()) sl@0: goto Retry; sl@0: } sl@0: else sl@0: return KErrCorrupt; sl@0: sl@0: if (iOnSamples < 0) // Play only first character for ever sl@0: { sl@0: iTrailingSilence = 0; sl@0: iSamplesLeft = iRate * iChannels; // One second of samples sl@0: iChar = 0; // Reset so this character is played again next time sl@0: iRampDown = EFalse; sl@0: if (!onlyPlayFirstTone) sl@0: { sl@0: iRampUp = EFalse; sl@0: // This is not the first time around so we should not sl@0: // reset the tone generator - it will already have the sl@0: // correct settings and setting them again would cause sl@0: // an audible discontinuity sl@0: return KErrNone; sl@0: } sl@0: } sl@0: sl@0: iSineWave.SetFrequency(highFrequency,highVolume,lowFrequency,lowVolume); sl@0: return KErrNone; sl@0: } sl@0: sl@0: // sl@0: // TMdaSequenceGenerator sl@0: // sl@0: sl@0: // sl@0: // Sequence constants sl@0: // sl@0: sl@0: //const TInt KMaxFixedSequenceStack=KMaxSequenceStack;//Max nesting level of FixedSequences * 2 sl@0: #ifdef _DEBUG sl@0: const TInt16 KFixedSequenceSignatureOne='S'+('Q'<<8); sl@0: const TInt16 KFixedSequenceSignatureTwo='N'+('C'<<8); sl@0: #endif // _DEBUG sl@0: sl@0: const TInt KFixedSequenceFunctionReturn=-1; sl@0: const TInt KFixedSequenceFunctionStartLoop=-2; sl@0: const TInt KFixedSequenceFunctionEndLoop=-3; sl@0: sl@0: void TMdaSequenceGenerator::Reset() sl@0: { sl@0: iInstructionPtr = REINTERPRET_CAST(const TInt16*,&((*iSequenceData)[0])); sl@0: iInstructionPtr += 2; // Skip signature sl@0: iStackIndex = 0; sl@0: } sl@0: sl@0: void TMdaSequenceGenerator::SetSequenceData(const TDesC8& aSequenceData) sl@0: // sl@0: // Store the sequence data to be played sl@0: // No need to validate it as it will already have been checked sl@0: // sl@0: { sl@0: iSequenceData = &aSequenceData; sl@0: iInstructionPtr = REINTERPRET_CAST(const TInt16*,&aSequenceData[0]); sl@0: iLastInstruction = iInstructionPtr + (iSequenceData->Length()>>1) - 1; sl@0: sl@0: // These are asserts because this should not be called if signature not present sl@0: ASSERT(*iInstructionPtr == KFixedSequenceSignatureOne); sl@0: ASSERT(*(iInstructionPtr+1) == KFixedSequenceSignatureTwo); sl@0: sl@0: iInstructionPtr += 2; // Skip signature sl@0: sl@0: iStackIndex = 0; sl@0: } sl@0: sl@0: TInt TMdaSequenceGenerator::GetNextTone() sl@0: // sl@0: // sl@0: { sl@0: ASSERT(iInstructionPtr); // Sanity check sl@0: sl@0: TInt ret = KRequestPending; sl@0: while (ret == KRequestPending) sl@0: { sl@0: if (iInstructionPtr > iLastInstruction) sl@0: ret = KErrCorrupt; sl@0: else if (*iInstructionPtr<=0) sl@0: { sl@0: switch (*iInstructionPtr) sl@0: { sl@0: case KFixedSequenceFunctionReturn: // End of sequence sl@0: ret = KErrNone; sl@0: break; sl@0: sl@0: case KFixedSequenceFunctionStartLoop: sl@0: if (iStackIndex>2) // Validate - can only nest twice sl@0: ret = KErrCorrupt; sl@0: else if ((iInstructionPtr+2) > iLastInstruction) sl@0: ret = KErrCorrupt; // Don't run off end of sequence sl@0: else sl@0: { sl@0: iStack[iStackIndex++]=(TInt)(iInstructionPtr+2); sl@0: iStack[iStackIndex++]=(TInt)*(iInstructionPtr+1); sl@0: iInstructionPtr+=2; sl@0: } sl@0: break; sl@0: sl@0: case KFixedSequenceFunctionEndLoop: sl@0: if (iStackIndex==0) // Validate - must already be nested sl@0: ret = KErrCorrupt; sl@0: else sl@0: { sl@0: if ((--iStack[iStackIndex-1])!=0) sl@0: iInstructionPtr=(TInt16*)iStack[iStackIndex-2]; sl@0: else sl@0: { sl@0: iStackIndex-=2; sl@0: iInstructionPtr++; sl@0: } sl@0: } sl@0: break; sl@0: sl@0: default: // Bad sequence sl@0: ret = KErrCorrupt; sl@0: } sl@0: } sl@0: else sl@0: { sl@0: if ((iInstructionPtr+5) > iLastInstruction) sl@0: ret = KErrCorrupt; // Don't run off end of sequence sl@0: else sl@0: { sl@0: iSamplesLeft = *iInstructionPtr++; sl@0: TInt freqOne = *iInstructionPtr++; sl@0: TInt volOne = *iInstructionPtr++; sl@0: TInt freqTwo = *iInstructionPtr++; sl@0: TInt volTwo = *iInstructionPtr++; sl@0: sl@0: if ((volOne> 1<<15)||(volTwo > 1<<15)) sl@0: ret = KErrCorrupt; sl@0: else sl@0: { sl@0: iSineWave.SetFrequency(freqOne,volOne,freqTwo,volTwo); sl@0: ret = KErrNone; sl@0: } sl@0: } sl@0: } sl@0: } sl@0: return ret; sl@0: } sl@0: sl@0: // --------------------------------- sl@0: // Code to generate sine table files used by tone generator sl@0: // Optionally called from InitL() sl@0: // #define GENERATE_SINE_TABLES 1 sl@0: #ifdef GENERATE_SINE_TABLES sl@0: LOCAL_C GenerateSineTableL() sl@0: { sl@0: _LIT(KSineFile,"sine.txt"); sl@0: _LIT(KSineIncFile,"sineinc.txt"); sl@0: sl@0: RFile file; sl@0: file.Replace(MdaManager::Fs(),KSineFile,EFileWrite); sl@0: CleanupClosePushL(file); sl@0: sl@0: RFile file2; sl@0: file2.Replace(MdaManager::Fs(),KSineIncFile,EFileWrite); sl@0: CleanupClosePushL(file2); sl@0: sl@0: const TReal pi=3.141592653589; sl@0: const TReal twopi=pi*2; sl@0: const TReal samples = 256.0; sl@0: const TReal step = twopi/samples; sl@0: sl@0: TBuf8<128> sinebuffer; sl@0: TBuf8<128> incbuffer; sl@0: TReal res; sl@0: TInt first=0; sl@0: TInt last=KMaxTInt; sl@0: TInt current; sl@0: _LIT8(KFormat,"%6d,"); sl@0: _LIT8(KNewLine,"\n"); sl@0: sl@0: for(TReal angle=0.0;angle<=(twopi-step);) // Copes with rounding errors sl@0: { sl@0: sinebuffer.Zero(); sl@0: incbuffer.Zero(); sl@0: for (int i=0;i<8;i++) sl@0: { sl@0: User::LeaveIfError(Math::Sin(res,angle)); sl@0: current = TInt(KMaxTInt16*res); sl@0: sinebuffer.AppendFormat(KFormat,current); sl@0: if (last != KMaxTInt) sl@0: incbuffer.AppendFormat(KFormat,current-last); sl@0: else sl@0: first = current; sl@0: last = current; sl@0: angle += step; sl@0: } sl@0: sinebuffer.Append(KNewLine); sl@0: incbuffer.Append(KNewLine); sl@0: file.Write(sinebuffer); sl@0: file2.Write(incbuffer); sl@0: } sl@0: sl@0: // Write fine difference to incbuffer - differnece between first and last sl@0: incbuffer.Zero(); sl@0: incbuffer.AppendFormat(KFormat,first-last); sl@0: incbuffer.Append(KNewLine); sl@0: file2.Write(incbuffer); sl@0: sl@0: CleanupStack::PopAndDestroy(2); sl@0: } sl@0: #endif sl@0: //-------------------------------