1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/mm/devsound/devsoundrefplugin/src/sounddevice/ToneGenerator.cpp Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,917 @@
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 + TInt32 sampleValue = static_cast<TInt32>((*sample)*(rampup-(left--)));
1.410 + *sample++ = static_cast<TInt16>(sampleValue/rampup);
1.411 + }
1.412 + }
1.413 + }
1.414 +
1.415 + return KErrNone;
1.416 + }
1.417 +
1.418 +TInt TMdaToneGenerator::DurationToSamples(const TTimeIntervalMicroSeconds& aDuration)
1.419 +//
1.420 +// Convert the given duration to a sample count using the current settings
1.421 +//
1.422 + {
1.423 + const TInt64 KTInt64OneMilion = 1000000;
1.424 +
1.425 + // Calculate duration as samples
1.426 + TInt64 microSeconds(aDuration.Int64()); // MSVC doesn't like "aDuration.Int64()" in line below
1.427 + TInt64 dur = ((TInt64(iRate) * TInt64(iChannels) * microSeconds) / KTInt64OneMilion);
1.428 + if (I64HIGH(dur)>0)
1.429 + return KMaxTInt; // Ridiculous!
1.430 + else
1.431 + return I64LOW(dur);
1.432 + }
1.433 +
1.434 +//
1.435 +// TMdaSimpleToneGenerator
1.436 +//
1.437 +
1.438 +void TMdaSimpleToneGenerator::Reset()
1.439 + {
1.440 + iPlayed = EFalse;
1.441 + }
1.442 +
1.443 +void TMdaSimpleToneGenerator::SetFrequencyAndDuration(TInt aFrequency, const TTimeIntervalMicroSeconds& aDuration)
1.444 +//
1.445 +// Store the frequency and duration of the specified sine tone
1.446 +//
1.447 + {
1.448 + iFrequency = aFrequency;
1.449 + iDuration = aDuration;
1.450 + iPlayed = EFalse;
1.451 + }
1.452 +
1.453 +TInt TMdaSimpleToneGenerator::GetNextTone()
1.454 +//
1.455 +// Simple implementation - just sets the supplied frequency and duration
1.456 +//
1.457 + {
1.458 + // This class only plays one tone for the specified duration
1.459 + if (!iPlayed)
1.460 + {
1.461 + iSamplesLeft = I64LOW((iDuration.Int64() * TInt64(iRate))/1000000);
1.462 + iSineWave.SetFrequency(iFrequency,1<<14);
1.463 + iPlayed = ETrue;
1.464 + iTrailingSilence = 20; // Just to stop clicking
1.465 + }
1.466 + return KErrNone;
1.467 + }
1.468 +
1.469 +//
1.470 +// TMdaDualToneGenerator
1.471 +//
1.472 +
1.473 +void TMdaDualToneGenerator::Reset()
1.474 + {
1.475 + iPlayed = EFalse;
1.476 + }
1.477 +
1.478 +void TMdaDualToneGenerator::SetFrequencyAndDuration(TInt aFrequencyOne, TInt aFrequencyTwo, const TTimeIntervalMicroSeconds& aDuration)
1.479 + {
1.480 + // Store the frequencies and duration of the specified dual tone
1.481 + iFrequencyOne = aFrequencyOne;
1.482 + iFrequencyTwo = aFrequencyTwo;
1.483 + iDuration = aDuration;
1.484 + iPlayed = EFalse;
1.485 + }
1.486 +
1.487 +//
1.488 +// This is called by TMdaToneGenerator::FillBuffer()
1.489 +// to calculate the number of samples (iSamplesLeft) that will be needed
1.490 +// for the tone to be played and to initialize the sine wave generator.
1.491 +// If the tone has already been played, then leaves iSamplesLeft
1.492 +// unmodified (should be zero) to indicate that it has finished.
1.493 +//
1.494 +TInt TMdaDualToneGenerator::GetNextTone()
1.495 + {
1.496 + // This class only plays one tone for the specified duration
1.497 + if (!iPlayed)
1.498 + {
1.499 + iSamplesLeft = I64LOW((iDuration.Int64() * TInt64(iRate))/KOneMillionMicroSeconds);
1.500 + iSineWave.SetFrequency(iFrequencyOne, KMaxAmplitude/2, iFrequencyTwo, KMaxAmplitude/2);
1.501 + iPlayed = ETrue;
1.502 + iTrailingSilence = KDefaultTrailingSilenceSamples; // Just to stop clicking
1.503 + }
1.504 + return KErrNone;
1.505 + }
1.506 +//
1.507 +// TMdaDTMFGenerator
1.508 +//
1.509 +
1.510 +const TInt KRecalculateToneLengths = KMinTInt;
1.511 +
1.512 +void TMdaDTMFGenerator::Reset()
1.513 + {
1.514 + iChar = 0;
1.515 + }
1.516 +
1.517 +void TMdaDTMFGenerator::SetToneDurations(const TTimeIntervalMicroSeconds32 aOn,
1.518 + const TTimeIntervalMicroSeconds32 aOff,
1.519 + const TTimeIntervalMicroSeconds32 aPause)
1.520 +//
1.521 +// Setup the DTMF tone durations
1.522 +// aOn can be == -1 indicating should play first tone indefinately
1.523 +//
1.524 + {
1.525 + ASSERT(aOn.Int() >=-1);
1.526 + ASSERT(aOff.Int()>=0);
1.527 + ASSERT(aPause.Int()>=0);
1.528 +
1.529 + iOn = aOn;
1.530 + iOff = aOff;
1.531 + iPause = aPause;
1.532 +
1.533 + iOnSamples = KRecalculateToneLengths; // Must recalculate these later
1.534 + }
1.535 +
1.536 +void TMdaDTMFGenerator::SetString(const TDesC& aDTMFString)
1.537 +//
1.538 +// Store the DTMF string to be played
1.539 +// No need to validate it as it will already have been checked
1.540 +//
1.541 + {
1.542 + iChar = 0;
1.543 + iDTMFString = &aDTMFString;
1.544 + }
1.545 +
1.546 +const TUint8 KDtmfVolumeTable[4][4]=
1.547 +//
1.548 +// Relative strengths to assign to different DTMF tones
1.549 +//
1.550 +// This is only important if DTMFs are being played through a speaker
1.551 +// and need to be machine-recognisable. This table compensates for frequency
1.552 +// drop-off in the speaker and can boost the relative volume of some
1.553 +// frequencies so they are still within tolerance.
1.554 +//
1.555 +// The values normally need to be determined using a frequency analyser on
1.556 +// the hardware
1.557 +//
1.558 +// Each column == same low frequency (697, 770, 852, 941 Hz)
1.559 +// Each row == same high frequency (1209, 1336, 1477, 1633 Hz)
1.560 +//
1.561 +// The value are interpreted as ratios:
1.562 +// 0 == 100% low
1.563 +// 7f == 50% low, 50% high
1.564 +// ff == 100% high
1.565 +//
1.566 + {
1.567 + {38,27,29,37},
1.568 + {46,36,36,46},
1.569 + {62,47,49,58},
1.570 + {70,56,60,68}
1.571 + };
1.572 +
1.573 +const TUint8 KDtmfTone697=0x0;
1.574 +const TUint8 KDtmfTone770=0x1;
1.575 +const TUint8 KDtmfTone852=0x2;
1.576 +const TUint8 KDtmfTone941=0x3;
1.577 +
1.578 +const TUint8 KDtmfTone1209=0x00;
1.579 +const TUint8 KDtmfTone1336=0x10;
1.580 +const TUint8 KDtmfTone1477=0x20;
1.581 +const TUint8 KDtmfTone1633=0x30;
1.582 +
1.583 +const TUint8 KDtmfToneTable[16]=
1.584 + {
1.585 + KDtmfTone941|KDtmfTone1336,//0
1.586 + KDtmfTone697|KDtmfTone1209,//1
1.587 + KDtmfTone697|KDtmfTone1336,//2
1.588 + KDtmfTone697|KDtmfTone1477,//3
1.589 + KDtmfTone770|KDtmfTone1209,//4
1.590 + KDtmfTone770|KDtmfTone1336,//5
1.591 + KDtmfTone770|KDtmfTone1477,//6
1.592 + KDtmfTone852|KDtmfTone1209,//7
1.593 + KDtmfTone852|KDtmfTone1336,//8
1.594 + KDtmfTone852|KDtmfTone1477,//9
1.595 +
1.596 + KDtmfTone697|KDtmfTone1633,//A
1.597 + KDtmfTone770|KDtmfTone1633,//B
1.598 + KDtmfTone852|KDtmfTone1633,//C
1.599 + KDtmfTone941|KDtmfTone1633,//D
1.600 + KDtmfTone941|KDtmfTone1209,//E or *
1.601 + KDtmfTone941|KDtmfTone1477,//F or #
1.602 + };
1.603 +
1.604 +TInt TMdaDTMFGenerator::GetNextTone()
1.605 +//
1.606 +// Setup frequency/duration/silence settings for next DTMF tone
1.607 +// Supported characters are 0-9 A-F * # , and any kind of white space
1.608 +//
1.609 + {
1.610 + TBool onlyPlayFirstTone = EFalse;
1.611 +
1.612 + if (iOnSamples == KRecalculateToneLengths)
1.613 + {
1.614 + // Must recalculate tone durations as samples
1.615 +
1.616 + // Handle special case where tone on duration negative
1.617 + // - meaning play first character indefinately
1.618 + if (iOn.Int()>=0)
1.619 + iOnSamples = DurationToSamples(TInt64(iOn.Int()));
1.620 + else
1.621 + {
1.622 + onlyPlayFirstTone = ETrue;
1.623 + iOnSamples = -1;
1.624 + }
1.625 +
1.626 + iOffSamples = DurationToSamples(TInt64(iOff.Int()));
1.627 + iPauseSamples = DurationToSamples(TInt64(iPause.Int()));
1.628 + }
1.629 +
1.630 + ASSERT(iDTMFString);
1.631 +
1.632 + if (iChar==iDTMFString->Length())
1.633 + return KErrNone; // Finished. Nothing to do
1.634 +
1.635 + TInt highFrequency = 0;
1.636 + TInt highVolume = 0;
1.637 + TInt lowFrequency = 0;
1.638 + TInt lowVolume =0;
1.639 +
1.640 +Retry:
1.641 + TChar c((*iDTMFString)[iChar++]);
1.642 + if ((TUint)c=='#' || (TUint)c=='*' || c.IsHexDigit())
1.643 + {
1.644 + TInt tableIndex;
1.645 + switch ((TUint)c)
1.646 + {
1.647 + case '*':
1.648 + tableIndex=14;
1.649 + break;
1.650 + case '#':
1.651 + tableIndex=15;
1.652 + break;
1.653 + default:
1.654 + if (c.IsDigit())
1.655 + tableIndex=(TUint)c-'0';
1.656 + else //letter
1.657 + {
1.658 + c.UpperCase();
1.659 + tableIndex=(TUint)c-'A'+10;
1.660 + }
1.661 + }
1.662 + TInt high=KDtmfToneTable[tableIndex]&0xf0;
1.663 + TInt low=KDtmfToneTable[tableIndex]&0x0f;
1.664 + switch(high)
1.665 + {
1.666 + case KDtmfTone1209:
1.667 + highFrequency=1209;
1.668 + break;
1.669 + case KDtmfTone1336:
1.670 + highFrequency=1336;
1.671 + break;
1.672 + case KDtmfTone1477:
1.673 + highFrequency=1477;
1.674 + break;
1.675 + default://KDtmfTone1633:
1.676 + highFrequency=1633;
1.677 + break;
1.678 + }
1.679 + switch(low)
1.680 + {
1.681 + case KDtmfTone697:
1.682 + lowFrequency=697;
1.683 + break;
1.684 + case KDtmfTone770:
1.685 + lowFrequency=770;
1.686 + break;
1.687 + case KDtmfTone852:
1.688 + lowFrequency=852;
1.689 + break;
1.690 + default://KDtmfTone941:
1.691 + lowFrequency=941;
1.692 + break;
1.693 + }
1.694 + high>>=4;
1.695 + const TUint8* dtmfVolumes=&KDtmfVolumeTable[0][0];
1.696 + TInt volume=dtmfVolumes[((low)<<2)+(high)]<<7;
1.697 + highVolume = volume;
1.698 + lowVolume = (1<<15)-volume;
1.699 +
1.700 + iTrailingSilence = iOffSamples;
1.701 + iSamplesLeft = iOnSamples;
1.702 + }
1.703 + else if ((TUint)c==',')
1.704 + {
1.705 + iTrailingSilence = iPauseSamples;
1.706 + iSamplesLeft = 0;
1.707 + }
1.708 + else if (c.IsSpace())
1.709 + {
1.710 + if (iChar < iDTMFString->Length())
1.711 + goto Retry;
1.712 + }
1.713 + else
1.714 + return KErrCorrupt;
1.715 +
1.716 + if (iOnSamples < 0) // Play only first character for ever
1.717 + {
1.718 + iTrailingSilence = 0;
1.719 + iSamplesLeft = iRate * iChannels; // One second of samples
1.720 + iChar = 0; // Reset so this character is played again next time
1.721 + iRampDown = EFalse;
1.722 + if (!onlyPlayFirstTone)
1.723 + {
1.724 + iRampUp = EFalse;
1.725 + // This is not the first time around so we should not
1.726 + // reset the tone generator - it will already have the
1.727 + // correct settings and setting them again would cause
1.728 + // an audible discontinuity
1.729 + return KErrNone;
1.730 + }
1.731 + }
1.732 +
1.733 + iSineWave.SetFrequency(highFrequency,highVolume,lowFrequency,lowVolume);
1.734 + return KErrNone;
1.735 + }
1.736 +
1.737 +//
1.738 +// TMdaSequenceGenerator
1.739 +//
1.740 +
1.741 +//
1.742 +// Sequence constants
1.743 +//
1.744 +
1.745 +//const TInt KMaxFixedSequenceStack=KMaxSequenceStack;//Max nesting level of FixedSequences * 2
1.746 +#ifdef _DEBUG
1.747 +const TInt16 KFixedSequenceSignatureOne='S'+('Q'<<8);
1.748 +const TInt16 KFixedSequenceSignatureTwo='N'+('C'<<8);
1.749 +#endif // _DEBUG
1.750 +
1.751 +const TInt KFixedSequenceFunctionReturn=-1;
1.752 +const TInt KFixedSequenceFunctionStartLoop=-2;
1.753 +const TInt KFixedSequenceFunctionEndLoop=-3;
1.754 +
1.755 +void TMdaSequenceGenerator::Reset()
1.756 + {
1.757 + iInstructionPtr = REINTERPRET_CAST(const TInt16*,&((*iSequenceData)[0]));
1.758 + iInstructionPtr += 2; // Skip signature
1.759 + iStackIndex = 0;
1.760 + }
1.761 +
1.762 +void TMdaSequenceGenerator::SetSequenceData(const TDesC8& aSequenceData)
1.763 +//
1.764 +// Store the sequence data to be played
1.765 +// No need to validate it as it will already have been checked
1.766 +//
1.767 + {
1.768 + iSequenceData = &aSequenceData;
1.769 + iInstructionPtr = REINTERPRET_CAST(const TInt16*,&aSequenceData[0]);
1.770 + iLastInstruction = iInstructionPtr + (iSequenceData->Length()>>1) - 1;
1.771 +
1.772 + // These are asserts because this should not be called if signature not present
1.773 + ASSERT(*iInstructionPtr == KFixedSequenceSignatureOne);
1.774 + ASSERT(*(iInstructionPtr+1) == KFixedSequenceSignatureTwo);
1.775 +
1.776 + iInstructionPtr += 2; // Skip signature
1.777 +
1.778 + iStackIndex = 0;
1.779 + }
1.780 +
1.781 +TInt TMdaSequenceGenerator::GetNextTone()
1.782 +//
1.783 +//
1.784 + {
1.785 + ASSERT(iInstructionPtr); // Sanity check
1.786 +
1.787 + TInt ret = KRequestPending;
1.788 + while (ret == KRequestPending)
1.789 + {
1.790 + if (iInstructionPtr > iLastInstruction)
1.791 + ret = KErrCorrupt;
1.792 + else if (*iInstructionPtr<=0)
1.793 + {
1.794 + switch (*iInstructionPtr)
1.795 + {
1.796 + case KFixedSequenceFunctionReturn: // End of sequence
1.797 + ret = KErrNone;
1.798 + break;
1.799 +
1.800 + case KFixedSequenceFunctionStartLoop:
1.801 + if (iStackIndex>2) // Validate - can only nest twice
1.802 + ret = KErrCorrupt;
1.803 + else if ((iInstructionPtr+2) > iLastInstruction)
1.804 + ret = KErrCorrupt; // Don't run off end of sequence
1.805 + else
1.806 + {
1.807 + iStack[iStackIndex++]=(TInt)(iInstructionPtr+2);
1.808 + iStack[iStackIndex++]=(TInt)*(iInstructionPtr+1);
1.809 + iInstructionPtr+=2;
1.810 + }
1.811 + break;
1.812 +
1.813 + case KFixedSequenceFunctionEndLoop:
1.814 + if (iStackIndex==0) // Validate - must already be nested
1.815 + ret = KErrCorrupt;
1.816 + else
1.817 + {
1.818 + if ((--iStack[iStackIndex-1])!=0)
1.819 + iInstructionPtr=(TInt16*)iStack[iStackIndex-2];
1.820 + else
1.821 + {
1.822 + iStackIndex-=2;
1.823 + iInstructionPtr++;
1.824 + }
1.825 + }
1.826 + break;
1.827 +
1.828 + default: // Bad sequence
1.829 + ret = KErrCorrupt;
1.830 + }
1.831 + }
1.832 + else
1.833 + {
1.834 + if ((iInstructionPtr+5) > iLastInstruction)
1.835 + ret = KErrCorrupt; // Don't run off end of sequence
1.836 + else
1.837 + {
1.838 + iSamplesLeft = *iInstructionPtr++;
1.839 + TInt freqOne = *iInstructionPtr++;
1.840 + TInt volOne = *iInstructionPtr++;
1.841 + TInt freqTwo = *iInstructionPtr++;
1.842 + TInt volTwo = *iInstructionPtr++;
1.843 +
1.844 + if ((volOne> 1<<15)||(volTwo > 1<<15))
1.845 + ret = KErrCorrupt;
1.846 + else
1.847 + {
1.848 + iSineWave.SetFrequency(freqOne,volOne,freqTwo,volTwo);
1.849 + ret = KErrNone;
1.850 + }
1.851 + }
1.852 + }
1.853 + }
1.854 + return ret;
1.855 + }
1.856 +
1.857 +// ---------------------------------
1.858 +// Code to generate sine table files used by tone generator
1.859 +// Optionally called from InitL()
1.860 +// #define GENERATE_SINE_TABLES 1
1.861 +#ifdef GENERATE_SINE_TABLES
1.862 +LOCAL_C GenerateSineTableL()
1.863 + {
1.864 + _LIT(KSineFile,"sine.txt");
1.865 + _LIT(KSineIncFile,"sineinc.txt");
1.866 +
1.867 + RFile file;
1.868 + file.Replace(MdaManager::Fs(),KSineFile,EFileWrite);
1.869 + CleanupClosePushL(file);
1.870 +
1.871 + RFile file2;
1.872 + file2.Replace(MdaManager::Fs(),KSineIncFile,EFileWrite);
1.873 + CleanupClosePushL(file2);
1.874 +
1.875 + const TReal pi=3.141592653589;
1.876 + const TReal twopi=pi*2;
1.877 + const TReal samples = 256.0;
1.878 + const TReal step = twopi/samples;
1.879 +
1.880 + TBuf8<128> sinebuffer;
1.881 + TBuf8<128> incbuffer;
1.882 + TReal res;
1.883 + TInt first=0;
1.884 + TInt last=KMaxTInt;
1.885 + TInt current;
1.886 + _LIT8(KFormat,"%6d,");
1.887 + _LIT8(KNewLine,"\n");
1.888 +
1.889 + for(TReal angle=0.0;angle<=(twopi-step);) // Copes with rounding errors
1.890 + {
1.891 + sinebuffer.Zero();
1.892 + incbuffer.Zero();
1.893 + for (int i=0;i<8;i++)
1.894 + {
1.895 + User::LeaveIfError(Math::Sin(res,angle));
1.896 + current = TInt(KMaxTInt16*res);
1.897 + sinebuffer.AppendFormat(KFormat,current);
1.898 + if (last != KMaxTInt)
1.899 + incbuffer.AppendFormat(KFormat,current-last);
1.900 + else
1.901 + first = current;
1.902 + last = current;
1.903 + angle += step;
1.904 + }
1.905 + sinebuffer.Append(KNewLine);
1.906 + incbuffer.Append(KNewLine);
1.907 + file.Write(sinebuffer);
1.908 + file2.Write(incbuffer);
1.909 + }
1.910 +
1.911 + // Write fine difference to incbuffer - differnece between first and last
1.912 + incbuffer.Zero();
1.913 + incbuffer.AppendFormat(KFormat,first-last);
1.914 + incbuffer.Append(KNewLine);
1.915 + file2.Write(incbuffer);
1.916 +
1.917 + CleanupStack::PopAndDestroy(2);
1.918 + }
1.919 +#endif
1.920 +//-------------------------------