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