1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/mm/mmhais/refacladapt/src/tonehwdevice/ToneGenerator.cpp Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,916 @@
1.4 +// Copyright (c) 1997-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 +// This file contains an implementation of the ToneGenerator interface
1.18 +// that converts all tone generation requests in to sampled audio
1.19 +// data to be played through the normal local sampled audio interface
1.20 +//
1.21 +//
1.22 +
1.23 +#include "ToneGenerator.h"
1.24 +#include <e32math.h>
1.25 +#include <mda/common/resource.h>
1.26 +
1.27 +/******************************************************************************
1.28 +* Tone Generators
1.29 +*
1.30 +* The following classes are used to generate simple frequency/duration tones,
1.31 +* DTMF, and SymbianOS tone sequences in a WINS environment. The below code
1.32 +* should only be considered for WINS.
1.33 +******************************************************************************/
1.34 +
1.35 +// this defines the maximum possible amplitude allowed for TSineGen::SetFrequency()
1.36 +const TInt KMaxAmplitude = 0x8000;
1.37 +
1.38 +// default number of samples for trailing silence following a Tone
1.39 +const TInt KDefaultTrailingSilenceSamples = 20;
1.40 +
1.41 +//
1.42 +// Sine tone generator
1.43 +//
1.44 +
1.45 +const TInt16 TSineGen::SineTable[KMaxSineTable] =
1.46 + {
1.47 + 0, 804, 1607, 2410, 3211, 4011, 4807, 5601,
1.48 + 6392, 7179, 7961, 8739, 9511, 10278, 11038, 11792,
1.49 + 12539, 13278, 14009, 14732, 15446, 16150, 16845, 17530,
1.50 + 18204, 18867, 19519, 20159, 20787, 21402, 22004, 22594,
1.51 + 23169, 23731, 24278, 24811, 25329, 25831, 26318, 26789,
1.52 + 27244, 27683, 28105, 28510, 28897, 29268, 29621, 29955,
1.53 + 30272, 30571, 30851, 31113, 31356, 31580, 31785, 31970,
1.54 + 32137, 32284, 32412, 32520, 32609, 32678, 32727, 32757,
1.55 + 32767, 32757, 32727, 32678, 32609, 32520, 32412, 32284,
1.56 + 32137, 31970, 31785, 31580, 31356, 31113, 30851, 30571,
1.57 + 30272, 29955, 29621, 29268, 28897, 28510, 28105, 27683,
1.58 + 27244, 26789, 26318, 25831, 25329, 24811, 24278, 23731,
1.59 + 23169, 22594, 22004, 21402, 20787, 20159, 19519, 18867,
1.60 + 18204, 17530, 16845, 16150, 15446, 14732, 14009, 13278,
1.61 + 12539, 11792, 11038, 10278, 9511, 8739, 7961, 7179,
1.62 + 6392, 5601, 4807, 4011, 3211, 2410, 1607, 804,
1.63 + 0, -804, -1607, -2410, -3211, -4011, -4807, -5601,
1.64 + -6392, -7179, -7961, -8739, -9511,-10278,-11038,-11792,
1.65 + -12539,-13278,-14009,-14732,-15446,-16150,-16845,-17530,
1.66 + -18204,-18867,-19519,-20159,-20787,-21402,-22004,-22594,
1.67 + -23169,-23731,-24278,-24811,-25329,-25831,-26318,-26789,
1.68 + -27244,-27683,-28105,-28510,-28897,-29268,-29621,-29955,
1.69 + -30272,-30571,-30851,-31113,-31356,-31580,-31785,-31970,
1.70 + -32137,-32284,-32412,-32520,-32609,-32678,-32727,-32757,
1.71 + -32767,-32757,-32727,-32678,-32609,-32520,-32412,-32284,
1.72 + -32137,-31970,-31785,-31580,-31356,-31113,-30851,-30571,
1.73 + -30272,-29955,-29621,-29268,-28897,-28510,-28105,-27683,
1.74 + -27244,-26789,-26318,-25831,-25329,-24811,-24278,-23731,
1.75 + -23169,-22594,-22004,-21402,-20787,-20159,-19519,-18867,
1.76 + -18204,-17530,-16845,-16150,-15446,-14732,-14009,-13278,
1.77 + -12539,-11792,-11038,-10278, -9511, -8739, -7961, -7179,
1.78 + -6392, -5601, -4807, -4011, -3211, -2410, -1607, -804,
1.79 + };
1.80 +
1.81 +const TInt16 TSineGen::IncTable[KMaxSineTable] =
1.82 + {
1.83 + 804, 803, 803, 801, 800, 796, 794,
1.84 + 791, 787, 782, 778, 772, 767, 760, 754,
1.85 + 747, 739, 731, 723, 714, 704, 695, 685,
1.86 + 674, 663, 652, 640, 628, 615, 602, 590,
1.87 + 575, 562, 547, 533, 518, 502, 487, 471,
1.88 + 455, 439, 422, 405, 387, 371, 353, 334,
1.89 + 317, 299, 280, 262, 243, 224, 205, 185,
1.90 + 167, 147, 128, 108, 89, 69, 49, 30,
1.91 + 10, -10, -30, -49, -69, -89, -108, -128,
1.92 + -147, -167, -185, -205, -224, -243, -262, -280,
1.93 + -299, -317, -334, -353, -371, -387, -405, -422,
1.94 + -439, -455, -471, -487, -502, -518, -533, -547,
1.95 + -562, -575, -590, -602, -615, -628, -640, -652,
1.96 + -663, -674, -685, -695, -704, -714, -723, -731,
1.97 + -739, -747, -754, -760, -767, -772, -778, -782,
1.98 + -787, -791, -794, -796, -800, -801, -803, -803,
1.99 + -804, -804, -803, -803, -801, -800, -796, -794,
1.100 + -791, -787, -782, -778, -772, -767, -760, -754,
1.101 + -747, -739, -731, -723, -714, -704, -695, -685,
1.102 + -674, -663, -652, -640, -628, -615, -602, -590,
1.103 + -575, -562, -547, -533, -518, -502, -487, -471,
1.104 + -455, -439, -422, -405, -387, -371, -353, -334,
1.105 + -317, -299, -280, -262, -243, -224, -205, -185,
1.106 + -167, -147, -128, -108, -89, -69, -49, -30,
1.107 + -10, 10, 30, 49, 69, 89, 108, 128,
1.108 + 147, 167, 185, 205, 224, 243, 262, 280,
1.109 + 299, 317, 334, 353, 371, 387, 405, 422,
1.110 + 439, 455, 471, 487, 502, 518, 533, 547,
1.111 + 562, 575, 590, 602, 615, 628, 640, 652,
1.112 + 663, 674, 685, 695, 704, 714, 723, 731,
1.113 + 739, 747, 754, 760, 767, 772, 778, 782,
1.114 + 787, 791, 794, 796, 800, 801, 803, 803,
1.115 + 804
1.116 + };
1.117 +
1.118 +void TSineGen::SetFrequency(TInt aFrequency,TInt aAmplitude)
1.119 +//
1.120 +// Given the frequency set iStep.
1.121 +// Reset iPosition to the equivalent of 0 degrees.
1.122 +// In the special case of aFrequency==4KHz set iPosition to 90 degrees.
1.123 +//
1.124 + {
1.125 +
1.126 + if (aAmplitude>(1<<15))
1.127 + iAmplitude=(1<<15);
1.128 + else if (aAmplitude<-(1<<15))
1.129 + iAmplitude=-(1<<15);
1.130 + else
1.131 + iAmplitude=aAmplitude;
1.132 +//
1.133 +// There are 256 entries in the sine table to traverse 360 degrees.
1.134 +// The codec requires samples at a rate of 8000 per second.
1.135 +// Thus for a 1Hz tone the step will be 256/8000 or 4/125.
1.136 +// Now we need need the integer part of the result to end up in
1.137 +// the MSB so we need to multiply by 2^24. This gives the formula
1.138 +// step = (f*4*2^24)/125 or (f*2^26)/125.
1.139 +// Our highest frequency is 4KHz so that the term (f*2^26) exceeds
1.140 +// a 32 bit result by 4000/2^6 (2^6 is the number of significant bits
1.141 +// left after a multiply by 2^26). i.e. 6 bits. We overcome this by
1.142 +// having 6 bits less in the fraction, so the new formula becomes
1.143 +// ((f*2^20)/125)*2^6. This still gives us 20 significant bits in the
1.144 +// fraction.
1.145 +//
1.146 +
1.147 + iStep=(((TUint)aFrequency<<20)/125)<<6;
1.148 + iPosition=(aFrequency==4000 ? 0x40000000 : 0);
1.149 + }
1.150 +
1.151 +TInt TSineGen::NextSample()
1.152 +//
1.153 +// Generate the next sample using linear interpolation
1.154 +//
1.155 + {
1.156 + TUint pos=iPosition>>24;
1.157 + TInt amp=((IncTable[pos]*((iPosition&0x00ffffff)>>20)));
1.158 + amp>>=4;
1.159 + amp+=SineTable[pos];
1.160 + amp=(amp*iAmplitude)>>15;
1.161 + iPosition+=iStep;
1.162 + return(amp);
1.163 + }
1.164 +
1.165 +void TSineWave::Generate(TInt16* aDest,TInt aCount)
1.166 +//
1.167 +// Called when more samples need to be generated.
1.168 +//
1.169 + {
1.170 + while (aCount--)
1.171 + {
1.172 + *aDest++=STATIC_CAST(TInt16,iGen1.NextSample()+iGen2.NextSample());
1.173 + }
1.174 + }
1.175 +
1.176 +void TSineWave::SetFrequency(TInt aFrequency,TInt aAmplitude)
1.177 +//
1.178 +// Set to generate a single frequency
1.179 +//
1.180 + {
1.181 + SetFrequency(aFrequency,aAmplitude,0,0);
1.182 + }
1.183 +
1.184 +void TSineWave::SetFrequency(TInt aFrequency1,TInt aAmplitude1,TInt aFrequency2,TInt aAmplitude2)
1.185 +//
1.186 +// Set to generate two frequencies
1.187 +//
1.188 + {
1.189 + iGen1.SetFrequency(aFrequency1,aAmplitude1);
1.190 + iGen2.SetFrequency(aFrequency2,aAmplitude2);
1.191 + }
1.192 +
1.193 +//
1.194 +// TMdaToneGenerator
1.195 +//
1.196 +
1.197 +void TMdaToneGenerator::Configure(TInt aRate, TInt aChannels, TInt aRepeats, TInt aSilence, TInt aRampUp)
1.198 +//
1.199 +// Set up this tone generator to generate data at the desired sample rate
1.200 +// and number of channels (typically mono/stereo)
1.201 +//
1.202 + {
1.203 + iRate = aRate;
1.204 + iChannels = aChannels;
1.205 + iSamplesLeft = 0;
1.206 + iIncompleteVolume = 0;
1.207 + iRampUpRemainder = 0;
1.208 + iRampUp = ETrue; // Default ramping to on as it is normally useful
1.209 + iRampDown = ETrue;
1.210 + iIncompleteRampDown = EFalse;
1.211 + iIncompleteRampUp = EFalse;
1.212 + iRepeats = aRepeats;
1.213 + iSilenceBetweenRepeats = aSilence;
1.214 + iRampUpCount = aRampUp;
1.215 + iRampUpLeft = aRampUp;
1.216 + iAfterRepeatSilence = EFalse;
1.217 + }
1.218 +
1.219 +LOCAL_C void RampVolume(TInt16* aData,TInt aCount,TInt aStartVol,TInt aEndVol)
1.220 +//
1.221 +// Simple function to ramp down the volume of some samples
1.222 +// Typically used to prevent "clicking" artifacts at the beginning/end of tones
1.223 +//
1.224 + {
1.225 + TInt step = (aEndVol - aStartVol)/aCount;
1.226 + while (aCount--)
1.227 + {
1.228 + TInt data = TInt(*aData) * aStartVol;
1.229 + *aData++ = TInt16(data>>15);
1.230 + aStartVol += step;
1.231 + }
1.232 + }
1.233 +
1.234 +TInt TMdaToneGenerator::FillBuffer(TDes8& aBuffer)
1.235 +//
1.236 +// Fill the supplied buffer with tone data
1.237 +// Sets the buffer length to zero if there is no more data to play
1.238 +// The buffer must have a max length of at least one sample * channels
1.239 +// e.g. 2 bytes mono, 4 bytes stereo
1.240 +//
1.241 + {
1.242 + const TInt KRampUpSamples = 50;
1.243 + const TInt KRampDownSamples = 50;
1.244 +
1.245 + ASSERT(aBuffer.MaxLength()>= (iChannels<<1));
1.246 + aBuffer.SetMax();
1.247 +
1.248 + TBool silence;
1.249 + TInt samples = 0; //
1.250 + TInt used = 0; // Data used
1.251 + TInt avail = aBuffer.Length(); // Data filled
1.252 + TInt count = 0; // Data to be converted
1.253 + TBool rampUp = EFalse;
1.254 +
1.255 + TMdaPtr8 fill;
1.256 + fill.Set(aBuffer); // Pointer to data left to be filled
1.257 +
1.258 + //
1.259 + // The rest of this function will loop around continually until the buffer
1.260 + // is filled or there is no more data to play
1.261 + //
1.262 +
1.263 +Restart:
1.264 + silence = EFalse; // Reset
1.265 + if (iSamplesLeft == 0)
1.266 + {
1.267 + if (iTrailingSilence == 0)
1.268 + {
1.269 + TInt error = GetNextTone();
1.270 + if (error)
1.271 + return error;
1.272 +
1.273 + rampUp = ETrue;
1.274 + if ((iSamplesLeft==0)&&(iTrailingSilence==0))
1.275 + {
1.276 + if ((iSilenceBetweenRepeats)&&(!iAfterRepeatSilence))
1.277 + {
1.278 + iTrailingSilence = iSilenceBetweenRepeats;
1.279 + iAfterRepeatSilence = ETrue;
1.280 + goto Restart;
1.281 + }
1.282 + else
1.283 + {
1.284 + if ((iRepeats>0)||(iRepeats==KMdaRepeatForever))
1.285 + {
1.286 + iAfterRepeatSilence = EFalse;
1.287 + if (iRepeats>0)
1.288 + iRepeats--;
1.289 +
1.290 + Reset();
1.291 + goto Restart;
1.292 + }
1.293 + }
1.294 + // No more to play
1.295 + goto Finished;
1.296 + }
1.297 + goto Restart;
1.298 + }
1.299 + else
1.300 + {
1.301 + silence = ETrue;
1.302 + samples = iTrailingSilence;
1.303 + }
1.304 + }
1.305 + else
1.306 + samples = iSamplesLeft;
1.307 +
1.308 + count = Min(samples,avail>>1);
1.309 + fill.SetLength(count<<1);
1.310 +
1.311 + if (!silence)
1.312 + { // Generate wave
1.313 + iSineWave.Generate(REINTERPRET_CAST(TInt16*,&fill[0]),count);
1.314 +
1.315 + if (iRampUp)
1.316 + {
1.317 + // Ramp up volume at beginning of tone
1.318 + if (rampUp)
1.319 + { // Fade in first few samples
1.320 + if(count < KRampUpSamples)
1.321 + {
1.322 + // Partial rampup due to being at the end of the buffer
1.323 + TInt fadeInLength = Min(KRampUpSamples,(fill.Length()>>1));
1.324 + iIncompleteVolume = (count*((1<<15)/KRampUpSamples));
1.325 + RampVolume(CONST_CAST(TInt16*,REINTERPRET_CAST(const TInt16*,(&fill[0]))),fadeInLength,0,iIncompleteVolume);
1.326 + iRampUpRemainder = fadeInLength;
1.327 + iIncompleteRampUp = ETrue;
1.328 + }
1.329 + else
1.330 + {
1.331 + // Normal rampup
1.332 + TInt fadeInLength = Min(Min(KRampUpSamples,iSamplesLeft),(fill.Length()>>1));
1.333 + RampVolume(CONST_CAST(TInt16*,REINTERPRET_CAST(const TInt16*,(&fill[0]))),fadeInLength,0,1<<15);
1.334 + iIncompleteRampUp = EFalse;
1.335 + }
1.336 + }
1.337 + else if (iIncompleteRampUp)
1.338 + {
1.339 + // Completing partial rampup at the start of a new buffer
1.340 + TInt fadeInLength = Min(Min((KRampUpSamples-iRampUpRemainder),iSamplesLeft),(fill.Length()>>1));
1.341 + RampVolume(CONST_CAST(TInt16*,REINTERPRET_CAST(const TInt16*,(&fill[0]))),fadeInLength,iIncompleteVolume,1<<15);
1.342 + iIncompleteRampUp = EFalse;
1.343 + }
1.344 + }
1.345 + if (iRampDown)
1.346 + { // Ramp down volume at end of tone
1.347 + if ((iSamplesLeft-count) < KRampDownSamples)
1.348 + {
1.349 + if(iSamplesLeft-count == 0)
1.350 + {
1.351 + // Fade out last few samples
1.352 + TInt startVolume = 1<<15;;
1.353 + TInt fadeOutLength = Min(Min(KRampDownSamples,iSamplesLeft),(fill.Length()>>1));
1.354 +
1.355 + if(iIncompleteRampDown)
1.356 + {
1.357 + // Completing partial rampdown at the start of a new buffer
1.358 + startVolume -= iIncompleteVolume;
1.359 + iIncompleteRampDown = EFalse;
1.360 + }
1.361 +
1.362 + RampVolume(CONST_CAST(TInt16*,REINTERPRET_CAST(const TInt16*,(&(fill.Right(fadeOutLength<<1))[0]))),fadeOutLength,startVolume,0);
1.363 + }
1.364 + else if(iSamplesLeft-count > 0)
1.365 + {
1.366 + // Partial rampdown due to being at the end of the buffer
1.367 + TInt rampDifference = (KRampDownSamples-(iSamplesLeft-count));
1.368 + TInt fadeOutLength = Min(Min(rampDifference,iSamplesLeft),(fill.Length()>>1));
1.369 + iIncompleteVolume = ((rampDifference*(1<<15))/KRampDownSamples);
1.370 +
1.371 + RampVolume(CONST_CAST(TInt16*,REINTERPRET_CAST(const TInt16*,(&(fill.Right(fadeOutLength<<1))[0]))),fadeOutLength,1<<15,(1<<15)-iIncompleteVolume);
1.372 +
1.373 + iIncompleteRampDown = ETrue;
1.374 + }
1.375 + }
1.376 + }
1.377 + iSamplesLeft -= count;
1.378 + }
1.379 + else
1.380 + { // Generate silence
1.381 + fill.FillZ(count<<1);
1.382 + iTrailingSilence -= count;
1.383 + }
1.384 +
1.385 + used += count<<1;
1.386 + avail -= count<<1;
1.387 + fill.Shift(count<<1);
1.388 +
1.389 + if (avail>(iChannels<<1))
1.390 + goto Restart;
1.391 +
1.392 +Finished:
1.393 +
1.394 + aBuffer.SetLength(used);
1.395 +
1.396 + // Do any ramp up that is required
1.397 + if (iRampUpLeft>0)
1.398 + {
1.399 + TInt words = iRampUpLeft * iChannels;
1.400 + words = Min(words,used>>1);
1.401 + if (words>0) // In case buffer has zero length...
1.402 + {
1.403 + TInt left = iRampUpLeft * iChannels;
1.404 + TInt rampup = iRampUpCount * iChannels;
1.405 + iRampUpLeft -= words/iChannels;
1.406 + TInt16* sample = REINTERPRET_CAST(TInt16*,&aBuffer[0]);
1.407 + while (words--)
1.408 + {
1.409 + *sample++ = STATIC_CAST(TInt16,(TInt32(*sample)*(rampup-(left--)))/rampup);
1.410 + }
1.411 + }
1.412 + }
1.413 +
1.414 + return KErrNone;
1.415 + }
1.416 +
1.417 +TInt TMdaToneGenerator::DurationToSamples(const TTimeIntervalMicroSeconds& aDuration)
1.418 +//
1.419 +// Convert the given duration to a sample count using the current settings
1.420 +//
1.421 + {
1.422 + const TInt64 KTInt64OneMilion = 1000000;
1.423 +
1.424 + // Calculate duration as samples
1.425 + TInt64 microSeconds(aDuration.Int64()); // MSVC doesn't like "aDuration.Int64()" in line below
1.426 + TInt64 dur = ((TInt64(iRate) * TInt64(iChannels) * microSeconds) / KTInt64OneMilion);
1.427 + if (I64HIGH(dur)>0)
1.428 + return KMaxTInt; // Ridiculous!
1.429 + else
1.430 + return I64LOW(dur);
1.431 + }
1.432 +
1.433 +//
1.434 +// TMdaSimpleToneGenerator
1.435 +//
1.436 +
1.437 +void TMdaSimpleToneGenerator::Reset()
1.438 + {
1.439 + iPlayed = EFalse;
1.440 + }
1.441 +
1.442 +void TMdaSimpleToneGenerator::SetFrequencyAndDuration(TInt aFrequency, const TTimeIntervalMicroSeconds& aDuration)
1.443 +//
1.444 +// Store the frequency and duration of the specified sine tone
1.445 +//
1.446 + {
1.447 + iFrequency = aFrequency;
1.448 + iDuration = aDuration;
1.449 + iPlayed = EFalse;
1.450 + }
1.451 +
1.452 +TInt TMdaSimpleToneGenerator::GetNextTone()
1.453 +//
1.454 +// Simple implementation - just sets the supplied frequency and duration
1.455 +//
1.456 + {
1.457 + // This class only plays one tone for the specified duration
1.458 + if (!iPlayed)
1.459 + {
1.460 + iSamplesLeft = I64LOW((iDuration.Int64() * TInt64(iRate))/1000000);
1.461 + iSineWave.SetFrequency(iFrequency,1<<14);
1.462 + iPlayed = ETrue;
1.463 + iTrailingSilence = 20; // Just to stop clicking
1.464 + }
1.465 + return KErrNone;
1.466 + }
1.467 +
1.468 +//
1.469 +// TMdaDualToneGenerator
1.470 +//
1.471 +
1.472 +void TMdaDualToneGenerator::Reset()
1.473 + {
1.474 + iPlayed = EFalse;
1.475 + }
1.476 +
1.477 +void TMdaDualToneGenerator::SetFrequencyAndDuration(TInt aFrequencyOne, TInt aFrequencyTwo, const TTimeIntervalMicroSeconds& aDuration)
1.478 + {
1.479 + // Store the frequencies and duration of the specified dual tone
1.480 + iFrequencyOne = aFrequencyOne;
1.481 + iFrequencyTwo = aFrequencyTwo;
1.482 + iDuration = aDuration;
1.483 + iPlayed = EFalse;
1.484 + }
1.485 +
1.486 +//
1.487 +// This is called by TMdaToneGenerator::FillBuffer()
1.488 +// to calculate the number of samples (iSamplesLeft) that will be needed
1.489 +// for the tone to be played and to initialize the sine wave generator.
1.490 +// If the tone has already been played, then leaves iSamplesLeft
1.491 +// unmodified (should be zero) to indicate that it has finished.
1.492 +//
1.493 +TInt TMdaDualToneGenerator::GetNextTone()
1.494 + {
1.495 + // This class only plays one tone for the specified duration
1.496 + if (!iPlayed)
1.497 + {
1.498 + iSamplesLeft = I64LOW((iDuration.Int64() * TInt64(iRate))/KOneMillionMicroSeconds);
1.499 + iSineWave.SetFrequency(iFrequencyOne, KMaxAmplitude/2, iFrequencyTwo, KMaxAmplitude/2);
1.500 + iPlayed = ETrue;
1.501 + iTrailingSilence = KDefaultTrailingSilenceSamples; // Just to stop clicking
1.502 + }
1.503 + return KErrNone;
1.504 + }
1.505 +//
1.506 +// TMdaDTMFGenerator
1.507 +//
1.508 +
1.509 +const TInt KRecalculateToneLengths = KMinTInt;
1.510 +
1.511 +void TMdaDTMFGenerator::Reset()
1.512 + {
1.513 + iChar = 0;
1.514 + }
1.515 +
1.516 +void TMdaDTMFGenerator::SetToneDurations(const TTimeIntervalMicroSeconds32 aOn,
1.517 + const TTimeIntervalMicroSeconds32 aOff,
1.518 + const TTimeIntervalMicroSeconds32 aPause)
1.519 +//
1.520 +// Setup the DTMF tone durations
1.521 +// aOn can be == -1 indicating should play first tone indefinately
1.522 +//
1.523 + {
1.524 + ASSERT(aOn.Int() >=-1);
1.525 + ASSERT(aOff.Int()>=0);
1.526 + ASSERT(aPause.Int()>=0);
1.527 +
1.528 + iOn = aOn;
1.529 + iOff = aOff;
1.530 + iPause = aPause;
1.531 +
1.532 + iOnSamples = KRecalculateToneLengths; // Must recalculate these later
1.533 + }
1.534 +
1.535 +void TMdaDTMFGenerator::SetString(const TDesC& aDTMFString)
1.536 +//
1.537 +// Store the DTMF string to be played
1.538 +// No need to validate it as it will already have been checked
1.539 +//
1.540 + {
1.541 + iChar = 0;
1.542 + iDTMFString = &aDTMFString;
1.543 + }
1.544 +
1.545 +const TUint8 KDtmfVolumeTable[4][4]=
1.546 +//
1.547 +// Relative strengths to assign to different DTMF tones
1.548 +//
1.549 +// This is only important if DTMFs are being played through a speaker
1.550 +// and need to be machine-recognisable. This table compensates for frequency
1.551 +// drop-off in the speaker and can boost the relative volume of some
1.552 +// frequencies so they are still within tolerance.
1.553 +//
1.554 +// The values normally need to be determined using a frequency analyser on
1.555 +// the hardware
1.556 +//
1.557 +// Each column == same low frequency (697, 770, 852, 941 Hz)
1.558 +// Each row == same high frequency (1209, 1336, 1477, 1633 Hz)
1.559 +//
1.560 +// The value are interpreted as ratios:
1.561 +// 0 == 100% low
1.562 +// 7f == 50% low, 50% high
1.563 +// ff == 100% high
1.564 +//
1.565 + {
1.566 + {38,27,29,37},
1.567 + {46,36,36,46},
1.568 + {62,47,49,58},
1.569 + {70,56,60,68}
1.570 + };
1.571 +
1.572 +const TUint8 KDtmfTone697=0x0;
1.573 +const TUint8 KDtmfTone770=0x1;
1.574 +const TUint8 KDtmfTone852=0x2;
1.575 +const TUint8 KDtmfTone941=0x3;
1.576 +
1.577 +const TUint8 KDtmfTone1209=0x00;
1.578 +const TUint8 KDtmfTone1336=0x10;
1.579 +const TUint8 KDtmfTone1477=0x20;
1.580 +const TUint8 KDtmfTone1633=0x30;
1.581 +
1.582 +const TUint8 KDtmfToneTable[16]=
1.583 + {
1.584 + KDtmfTone941|KDtmfTone1336,//0
1.585 + KDtmfTone697|KDtmfTone1209,//1
1.586 + KDtmfTone697|KDtmfTone1336,//2
1.587 + KDtmfTone697|KDtmfTone1477,//3
1.588 + KDtmfTone770|KDtmfTone1209,//4
1.589 + KDtmfTone770|KDtmfTone1336,//5
1.590 + KDtmfTone770|KDtmfTone1477,//6
1.591 + KDtmfTone852|KDtmfTone1209,//7
1.592 + KDtmfTone852|KDtmfTone1336,//8
1.593 + KDtmfTone852|KDtmfTone1477,//9
1.594 +
1.595 + KDtmfTone697|KDtmfTone1633,//A
1.596 + KDtmfTone770|KDtmfTone1633,//B
1.597 + KDtmfTone852|KDtmfTone1633,//C
1.598 + KDtmfTone941|KDtmfTone1633,//D
1.599 + KDtmfTone941|KDtmfTone1209,//E or *
1.600 + KDtmfTone941|KDtmfTone1477,//F or #
1.601 + };
1.602 +
1.603 +TInt TMdaDTMFGenerator::GetNextTone()
1.604 +//
1.605 +// Setup frequency/duration/silence settings for next DTMF tone
1.606 +// Supported characters are 0-9 A-F * # , and any kind of white space
1.607 +//
1.608 + {
1.609 + TBool onlyPlayFirstTone = EFalse;
1.610 +
1.611 + if (iOnSamples == KRecalculateToneLengths)
1.612 + {
1.613 + // Must recalculate tone durations as samples
1.614 +
1.615 + // Handle special case where tone on duration negative
1.616 + // - meaning play first character indefinately
1.617 + if (iOn.Int()>=0)
1.618 + iOnSamples = DurationToSamples(TInt64(iOn.Int()));
1.619 + else
1.620 + {
1.621 + onlyPlayFirstTone = ETrue;
1.622 + iOnSamples = -1;
1.623 + }
1.624 +
1.625 + iOffSamples = DurationToSamples(TInt64(iOff.Int()));
1.626 + iPauseSamples = DurationToSamples(TInt64(iPause.Int()));
1.627 + }
1.628 +
1.629 + ASSERT(iDTMFString);
1.630 +
1.631 + if (iChar==iDTMFString->Length())
1.632 + return KErrNone; // Finished. Nothing to do
1.633 +
1.634 + TInt highFrequency = 0;
1.635 + TInt highVolume = 0;
1.636 + TInt lowFrequency = 0;
1.637 + TInt lowVolume =0;
1.638 +
1.639 +Retry:
1.640 + TChar c((*iDTMFString)[iChar++]);
1.641 + if ((TUint)c=='#' || (TUint)c=='*' || c.IsHexDigit())
1.642 + {
1.643 + TInt tableIndex;
1.644 + switch ((TUint)c)
1.645 + {
1.646 + case '*':
1.647 + tableIndex=14;
1.648 + break;
1.649 + case '#':
1.650 + tableIndex=15;
1.651 + break;
1.652 + default:
1.653 + if (c.IsDigit())
1.654 + tableIndex=(TUint)c-'0';
1.655 + else //letter
1.656 + {
1.657 + c.UpperCase();
1.658 + tableIndex=(TUint)c-'A'+10;
1.659 + }
1.660 + }
1.661 + TInt high=KDtmfToneTable[tableIndex]&0xf0;
1.662 + TInt low=KDtmfToneTable[tableIndex]&0x0f;
1.663 + switch(high)
1.664 + {
1.665 + case KDtmfTone1209:
1.666 + highFrequency=1209;
1.667 + break;
1.668 + case KDtmfTone1336:
1.669 + highFrequency=1336;
1.670 + break;
1.671 + case KDtmfTone1477:
1.672 + highFrequency=1477;
1.673 + break;
1.674 + default://KDtmfTone1633:
1.675 + highFrequency=1633;
1.676 + break;
1.677 + }
1.678 + switch(low)
1.679 + {
1.680 + case KDtmfTone697:
1.681 + lowFrequency=697;
1.682 + break;
1.683 + case KDtmfTone770:
1.684 + lowFrequency=770;
1.685 + break;
1.686 + case KDtmfTone852:
1.687 + lowFrequency=852;
1.688 + break;
1.689 + default://KDtmfTone941:
1.690 + lowFrequency=941;
1.691 + break;
1.692 + }
1.693 + high>>=4;
1.694 + const TUint8* dtmfVolumes=&KDtmfVolumeTable[0][0];
1.695 + TInt volume=dtmfVolumes[((low)<<2)+(high)]<<7;
1.696 + highVolume = volume;
1.697 + lowVolume = (1<<15)-volume;
1.698 +
1.699 + iTrailingSilence = iOffSamples;
1.700 + iSamplesLeft = iOnSamples;
1.701 + }
1.702 + else if ((TUint)c==',')
1.703 + {
1.704 + iTrailingSilence = iPauseSamples;
1.705 + iSamplesLeft = 0;
1.706 + }
1.707 + else if (c.IsSpace())
1.708 + {
1.709 + if (iChar < iDTMFString->Length())
1.710 + goto Retry;
1.711 + }
1.712 + else
1.713 + return KErrCorrupt;
1.714 +
1.715 + if (iOnSamples < 0) // Play only first character for ever
1.716 + {
1.717 + iTrailingSilence = 0;
1.718 + iSamplesLeft = iRate * iChannels; // One second of samples
1.719 + iChar = 0; // Reset so this character is played again next time
1.720 + iRampDown = EFalse;
1.721 + if (!onlyPlayFirstTone)
1.722 + {
1.723 + iRampUp = EFalse;
1.724 + // This is not the first time around so we should not
1.725 + // reset the tone generator - it will already have the
1.726 + // correct settings and setting them again would cause
1.727 + // an audible discontinuity
1.728 + return KErrNone;
1.729 + }
1.730 + }
1.731 +
1.732 + iSineWave.SetFrequency(highFrequency,highVolume,lowFrequency,lowVolume);
1.733 + return KErrNone;
1.734 + }
1.735 +
1.736 +//
1.737 +// TMdaSequenceGenerator
1.738 +//
1.739 +
1.740 +//
1.741 +// Sequence constants
1.742 +//
1.743 +
1.744 +//const TInt KMaxFixedSequenceStack=KMaxSequenceStack;//Max nesting level of FixedSequences * 2
1.745 +#ifdef _DEBUG
1.746 +const TInt16 KFixedSequenceSignatureOne='S'+('Q'<<8);
1.747 +const TInt16 KFixedSequenceSignatureTwo='N'+('C'<<8);
1.748 +#endif // _DEBUG
1.749 +
1.750 +const TInt KFixedSequenceFunctionReturn=-1;
1.751 +const TInt KFixedSequenceFunctionStartLoop=-2;
1.752 +const TInt KFixedSequenceFunctionEndLoop=-3;
1.753 +
1.754 +void TMdaSequenceGenerator::Reset()
1.755 + {
1.756 + iInstructionPtr = REINTERPRET_CAST(const TInt16*,&((*iSequenceData)[0]));
1.757 + iInstructionPtr += 2; // Skip signature
1.758 + iStackIndex = 0;
1.759 + }
1.760 +
1.761 +void TMdaSequenceGenerator::SetSequenceData(const TDesC8& aSequenceData)
1.762 +//
1.763 +// Store the sequence data to be played
1.764 +// No need to validate it as it will already have been checked
1.765 +//
1.766 + {
1.767 + iSequenceData = &aSequenceData;
1.768 + iInstructionPtr = REINTERPRET_CAST(const TInt16*,&aSequenceData[0]);
1.769 + iLastInstruction = iInstructionPtr + (iSequenceData->Length()>>1) - 1;
1.770 +
1.771 + // These are asserts because this should not be called if signature not present
1.772 + ASSERT(*iInstructionPtr == KFixedSequenceSignatureOne);
1.773 + ASSERT(*(iInstructionPtr+1) == KFixedSequenceSignatureTwo);
1.774 +
1.775 + iInstructionPtr += 2; // Skip signature
1.776 +
1.777 + iStackIndex = 0;
1.778 + }
1.779 +
1.780 +TInt TMdaSequenceGenerator::GetNextTone()
1.781 +//
1.782 +//
1.783 + {
1.784 + ASSERT(iInstructionPtr); // Sanity check
1.785 +
1.786 + TInt ret = KRequestPending;
1.787 + while (ret == KRequestPending)
1.788 + {
1.789 + if (iInstructionPtr > iLastInstruction)
1.790 + ret = KErrCorrupt;
1.791 + else if (*iInstructionPtr<=0)
1.792 + {
1.793 + switch (*iInstructionPtr)
1.794 + {
1.795 + case KFixedSequenceFunctionReturn: // End of sequence
1.796 + ret = KErrNone;
1.797 + break;
1.798 +
1.799 + case KFixedSequenceFunctionStartLoop:
1.800 + if (iStackIndex>2) // Validate - can only nest twice
1.801 + ret = KErrCorrupt;
1.802 + else if ((iInstructionPtr+2) > iLastInstruction)
1.803 + ret = KErrCorrupt; // Don't run off end of sequence
1.804 + else
1.805 + {
1.806 + iStack[iStackIndex++]=(TInt)(iInstructionPtr+2);
1.807 + iStack[iStackIndex++]=(TInt)*(iInstructionPtr+1);
1.808 + iInstructionPtr+=2;
1.809 + }
1.810 + break;
1.811 +
1.812 + case KFixedSequenceFunctionEndLoop:
1.813 + if (iStackIndex==0) // Validate - must already be nested
1.814 + ret = KErrCorrupt;
1.815 + else
1.816 + {
1.817 + if ((--iStack[iStackIndex-1])!=0)
1.818 + iInstructionPtr=(TInt16*)iStack[iStackIndex-2];
1.819 + else
1.820 + {
1.821 + iStackIndex-=2;
1.822 + iInstructionPtr++;
1.823 + }
1.824 + }
1.825 + break;
1.826 +
1.827 + default: // Bad sequence
1.828 + ret = KErrCorrupt;
1.829 + }
1.830 + }
1.831 + else
1.832 + {
1.833 + if ((iInstructionPtr+5) > iLastInstruction)
1.834 + ret = KErrCorrupt; // Don't run off end of sequence
1.835 + else
1.836 + {
1.837 + iSamplesLeft = *iInstructionPtr++;
1.838 + TInt freqOne = *iInstructionPtr++;
1.839 + TInt volOne = *iInstructionPtr++;
1.840 + TInt freqTwo = *iInstructionPtr++;
1.841 + TInt volTwo = *iInstructionPtr++;
1.842 +
1.843 + if ((volOne> 1<<15)||(volTwo > 1<<15))
1.844 + ret = KErrCorrupt;
1.845 + else
1.846 + {
1.847 + iSineWave.SetFrequency(freqOne,volOne,freqTwo,volTwo);
1.848 + ret = KErrNone;
1.849 + }
1.850 + }
1.851 + }
1.852 + }
1.853 + return ret;
1.854 + }
1.855 +
1.856 +// ---------------------------------
1.857 +// Code to generate sine table files used by tone generator
1.858 +// Optionally called from InitL()
1.859 +// #define GENERATE_SINE_TABLES 1
1.860 +#ifdef GENERATE_SINE_TABLES
1.861 +LOCAL_C GenerateSineTableL()
1.862 + {
1.863 + _LIT(KSineFile,"sine.txt");
1.864 + _LIT(KSineIncFile,"sineinc.txt");
1.865 +
1.866 + RFile file;
1.867 + file.Replace(MdaManager::Fs(),KSineFile,EFileWrite);
1.868 + CleanupClosePushL(file);
1.869 +
1.870 + RFile file2;
1.871 + file2.Replace(MdaManager::Fs(),KSineIncFile,EFileWrite);
1.872 + CleanupClosePushL(file2);
1.873 +
1.874 + const TReal pi=3.141592653589;
1.875 + const TReal twopi=pi*2;
1.876 + const TReal samples = 256.0;
1.877 + const TReal step = twopi/samples;
1.878 +
1.879 + TBuf8<128> sinebuffer;
1.880 + TBuf8<128> incbuffer;
1.881 + TReal res;
1.882 + TInt first=0;
1.883 + TInt last=KMaxTInt;
1.884 + TInt current;
1.885 + _LIT8(KFormat,"%6d,");
1.886 + _LIT8(KNewLine,"\n");
1.887 +
1.888 + for(TReal angle=0.0;angle<=(twopi-step);) // Copes with rounding errors
1.889 + {
1.890 + sinebuffer.Zero();
1.891 + incbuffer.Zero();
1.892 + for (int i=0;i<8;i++)
1.893 + {
1.894 + User::LeaveIfError(Math::Sin(res,angle));
1.895 + current = TInt(KMaxTInt16*res);
1.896 + sinebuffer.AppendFormat(KFormat,current);
1.897 + if (last != KMaxTInt)
1.898 + incbuffer.AppendFormat(KFormat,current-last);
1.899 + else
1.900 + first = current;
1.901 + last = current;
1.902 + angle += step;
1.903 + }
1.904 + sinebuffer.Append(KNewLine);
1.905 + incbuffer.Append(KNewLine);
1.906 + file.Write(sinebuffer);
1.907 + file2.Write(incbuffer);
1.908 + }
1.909 +
1.910 + // Write fine difference to incbuffer - differnece between first and last
1.911 + incbuffer.Zero();
1.912 + incbuffer.AppendFormat(KFormat,first-last);
1.913 + incbuffer.Append(KNewLine);
1.914 + file2.Write(incbuffer);
1.915 +
1.916 + CleanupStack::PopAndDestroy(2);
1.917 + }
1.918 +#endif
1.919 +//-------------------------------