|
1 // Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // This file contains an implementation of the ToneGenerator interface |
|
15 // that converts all tone generation requests in to sampled audio |
|
16 // data to be played through the normal local sampled audio interface |
|
17 // |
|
18 // |
|
19 |
|
20 #include "ToneGenerator.h" |
|
21 #include <e32math.h> |
|
22 #include <mda/common/resource.h> |
|
23 |
|
24 /****************************************************************************** |
|
25 * Tone Generators |
|
26 * |
|
27 * The following classes are used to generate simple frequency/duration tones, |
|
28 * DTMF, and SymbianOS tone sequences in a WINS environment. The below code |
|
29 * should only be considered for WINS. |
|
30 ******************************************************************************/ |
|
31 |
|
32 // this defines the maximum possible amplitude allowed for TSineGen::SetFrequency() |
|
33 const TInt KMaxAmplitude = 0x8000; |
|
34 |
|
35 // default number of samples for trailing silence following a Tone |
|
36 const TInt KDefaultTrailingSilenceSamples = 20; |
|
37 |
|
38 // |
|
39 // Sine tone generator |
|
40 // |
|
41 |
|
42 const TInt16 TSineGen::SineTable[KMaxSineTable] = |
|
43 { |
|
44 0, 804, 1607, 2410, 3211, 4011, 4807, 5601, |
|
45 6392, 7179, 7961, 8739, 9511, 10278, 11038, 11792, |
|
46 12539, 13278, 14009, 14732, 15446, 16150, 16845, 17530, |
|
47 18204, 18867, 19519, 20159, 20787, 21402, 22004, 22594, |
|
48 23169, 23731, 24278, 24811, 25329, 25831, 26318, 26789, |
|
49 27244, 27683, 28105, 28510, 28897, 29268, 29621, 29955, |
|
50 30272, 30571, 30851, 31113, 31356, 31580, 31785, 31970, |
|
51 32137, 32284, 32412, 32520, 32609, 32678, 32727, 32757, |
|
52 32767, 32757, 32727, 32678, 32609, 32520, 32412, 32284, |
|
53 32137, 31970, 31785, 31580, 31356, 31113, 30851, 30571, |
|
54 30272, 29955, 29621, 29268, 28897, 28510, 28105, 27683, |
|
55 27244, 26789, 26318, 25831, 25329, 24811, 24278, 23731, |
|
56 23169, 22594, 22004, 21402, 20787, 20159, 19519, 18867, |
|
57 18204, 17530, 16845, 16150, 15446, 14732, 14009, 13278, |
|
58 12539, 11792, 11038, 10278, 9511, 8739, 7961, 7179, |
|
59 6392, 5601, 4807, 4011, 3211, 2410, 1607, 804, |
|
60 0, -804, -1607, -2410, -3211, -4011, -4807, -5601, |
|
61 -6392, -7179, -7961, -8739, -9511,-10278,-11038,-11792, |
|
62 -12539,-13278,-14009,-14732,-15446,-16150,-16845,-17530, |
|
63 -18204,-18867,-19519,-20159,-20787,-21402,-22004,-22594, |
|
64 -23169,-23731,-24278,-24811,-25329,-25831,-26318,-26789, |
|
65 -27244,-27683,-28105,-28510,-28897,-29268,-29621,-29955, |
|
66 -30272,-30571,-30851,-31113,-31356,-31580,-31785,-31970, |
|
67 -32137,-32284,-32412,-32520,-32609,-32678,-32727,-32757, |
|
68 -32767,-32757,-32727,-32678,-32609,-32520,-32412,-32284, |
|
69 -32137,-31970,-31785,-31580,-31356,-31113,-30851,-30571, |
|
70 -30272,-29955,-29621,-29268,-28897,-28510,-28105,-27683, |
|
71 -27244,-26789,-26318,-25831,-25329,-24811,-24278,-23731, |
|
72 -23169,-22594,-22004,-21402,-20787,-20159,-19519,-18867, |
|
73 -18204,-17530,-16845,-16150,-15446,-14732,-14009,-13278, |
|
74 -12539,-11792,-11038,-10278, -9511, -8739, -7961, -7179, |
|
75 -6392, -5601, -4807, -4011, -3211, -2410, -1607, -804, |
|
76 }; |
|
77 |
|
78 const TInt16 TSineGen::IncTable[KMaxSineTable] = |
|
79 { |
|
80 804, 803, 803, 801, 800, 796, 794, |
|
81 791, 787, 782, 778, 772, 767, 760, 754, |
|
82 747, 739, 731, 723, 714, 704, 695, 685, |
|
83 674, 663, 652, 640, 628, 615, 602, 590, |
|
84 575, 562, 547, 533, 518, 502, 487, 471, |
|
85 455, 439, 422, 405, 387, 371, 353, 334, |
|
86 317, 299, 280, 262, 243, 224, 205, 185, |
|
87 167, 147, 128, 108, 89, 69, 49, 30, |
|
88 10, -10, -30, -49, -69, -89, -108, -128, |
|
89 -147, -167, -185, -205, -224, -243, -262, -280, |
|
90 -299, -317, -334, -353, -371, -387, -405, -422, |
|
91 -439, -455, -471, -487, -502, -518, -533, -547, |
|
92 -562, -575, -590, -602, -615, -628, -640, -652, |
|
93 -663, -674, -685, -695, -704, -714, -723, -731, |
|
94 -739, -747, -754, -760, -767, -772, -778, -782, |
|
95 -787, -791, -794, -796, -800, -801, -803, -803, |
|
96 -804, -804, -803, -803, -801, -800, -796, -794, |
|
97 -791, -787, -782, -778, -772, -767, -760, -754, |
|
98 -747, -739, -731, -723, -714, -704, -695, -685, |
|
99 -674, -663, -652, -640, -628, -615, -602, -590, |
|
100 -575, -562, -547, -533, -518, -502, -487, -471, |
|
101 -455, -439, -422, -405, -387, -371, -353, -334, |
|
102 -317, -299, -280, -262, -243, -224, -205, -185, |
|
103 -167, -147, -128, -108, -89, -69, -49, -30, |
|
104 -10, 10, 30, 49, 69, 89, 108, 128, |
|
105 147, 167, 185, 205, 224, 243, 262, 280, |
|
106 299, 317, 334, 353, 371, 387, 405, 422, |
|
107 439, 455, 471, 487, 502, 518, 533, 547, |
|
108 562, 575, 590, 602, 615, 628, 640, 652, |
|
109 663, 674, 685, 695, 704, 714, 723, 731, |
|
110 739, 747, 754, 760, 767, 772, 778, 782, |
|
111 787, 791, 794, 796, 800, 801, 803, 803, |
|
112 804 |
|
113 }; |
|
114 |
|
115 void TSineGen::SetFrequency(TInt aFrequency,TInt aAmplitude) |
|
116 // |
|
117 // Given the frequency set iStep. |
|
118 // Reset iPosition to the equivalent of 0 degrees. |
|
119 // In the special case of aFrequency==4KHz set iPosition to 90 degrees. |
|
120 // |
|
121 { |
|
122 |
|
123 if (aAmplitude>(1<<15)) |
|
124 iAmplitude=(1<<15); |
|
125 else if (aAmplitude<-(1<<15)) |
|
126 iAmplitude=-(1<<15); |
|
127 else |
|
128 iAmplitude=aAmplitude; |
|
129 // |
|
130 // There are 256 entries in the sine table to traverse 360 degrees. |
|
131 // The codec requires samples at a rate of 8000 per second. |
|
132 // Thus for a 1Hz tone the step will be 256/8000 or 4/125. |
|
133 // Now we need need the integer part of the result to end up in |
|
134 // the MSB so we need to multiply by 2^24. This gives the formula |
|
135 // step = (f*4*2^24)/125 or (f*2^26)/125. |
|
136 // Our highest frequency is 4KHz so that the term (f*2^26) exceeds |
|
137 // a 32 bit result by 4000/2^6 (2^6 is the number of significant bits |
|
138 // left after a multiply by 2^26). i.e. 6 bits. We overcome this by |
|
139 // having 6 bits less in the fraction, so the new formula becomes |
|
140 // ((f*2^20)/125)*2^6. This still gives us 20 significant bits in the |
|
141 // fraction. |
|
142 // |
|
143 |
|
144 iStep=(((TUint)aFrequency<<20)/125)<<6; |
|
145 iPosition=(aFrequency==4000 ? 0x40000000 : 0); |
|
146 } |
|
147 |
|
148 TInt TSineGen::NextSample() |
|
149 // |
|
150 // Generate the next sample using linear interpolation |
|
151 // |
|
152 { |
|
153 TUint pos=iPosition>>24; |
|
154 TInt amp=((IncTable[pos]*((iPosition&0x00ffffff)>>20))); |
|
155 amp>>=4; |
|
156 amp+=SineTable[pos]; |
|
157 amp=(amp*iAmplitude)>>15; |
|
158 iPosition+=iStep; |
|
159 return(amp); |
|
160 } |
|
161 |
|
162 void TSineWave::Generate(TInt16* aDest,TInt aCount) |
|
163 // |
|
164 // Called when more samples need to be generated. |
|
165 // |
|
166 { |
|
167 while (aCount--) |
|
168 { |
|
169 *aDest++=STATIC_CAST(TInt16,iGen1.NextSample()+iGen2.NextSample()); |
|
170 } |
|
171 } |
|
172 |
|
173 void TSineWave::SetFrequency(TInt aFrequency,TInt aAmplitude) |
|
174 // |
|
175 // Set to generate a single frequency |
|
176 // |
|
177 { |
|
178 SetFrequency(aFrequency,aAmplitude,0,0); |
|
179 } |
|
180 |
|
181 void TSineWave::SetFrequency(TInt aFrequency1,TInt aAmplitude1,TInt aFrequency2,TInt aAmplitude2) |
|
182 // |
|
183 // Set to generate two frequencies |
|
184 // |
|
185 { |
|
186 iGen1.SetFrequency(aFrequency1,aAmplitude1); |
|
187 iGen2.SetFrequency(aFrequency2,aAmplitude2); |
|
188 } |
|
189 |
|
190 // |
|
191 // TMdaToneGenerator |
|
192 // |
|
193 |
|
194 void TMdaToneGenerator::Configure(TInt aRate, TInt aChannels, TInt aRepeats, TInt aSilence, TInt aRampUp) |
|
195 // |
|
196 // Set up this tone generator to generate data at the desired sample rate |
|
197 // and number of channels (typically mono/stereo) |
|
198 // |
|
199 { |
|
200 iRate = aRate; |
|
201 iChannels = aChannels; |
|
202 iSamplesLeft = 0; |
|
203 iIncompleteVolume = 0; |
|
204 iRampUpRemainder = 0; |
|
205 iRampUp = ETrue; // Default ramping to on as it is normally useful |
|
206 iRampDown = ETrue; |
|
207 iIncompleteRampDown = EFalse; |
|
208 iIncompleteRampUp = EFalse; |
|
209 iRepeats = aRepeats; |
|
210 iSilenceBetweenRepeats = aSilence; |
|
211 iRampUpCount = aRampUp; |
|
212 iRampUpLeft = aRampUp; |
|
213 iAfterRepeatSilence = EFalse; |
|
214 } |
|
215 |
|
216 LOCAL_C void RampVolume(TInt16* aData,TInt aCount,TInt aStartVol,TInt aEndVol) |
|
217 // |
|
218 // Simple function to ramp down the volume of some samples |
|
219 // Typically used to prevent "clicking" artifacts at the beginning/end of tones |
|
220 // |
|
221 { |
|
222 TInt step = (aEndVol - aStartVol)/aCount; |
|
223 while (aCount--) |
|
224 { |
|
225 TInt data = TInt(*aData) * aStartVol; |
|
226 *aData++ = TInt16(data>>15); |
|
227 aStartVol += step; |
|
228 } |
|
229 } |
|
230 |
|
231 TInt TMdaToneGenerator::FillBuffer(TDes8& aBuffer) |
|
232 // |
|
233 // Fill the supplied buffer with tone data |
|
234 // Sets the buffer length to zero if there is no more data to play |
|
235 // The buffer must have a max length of at least one sample * channels |
|
236 // e.g. 2 bytes mono, 4 bytes stereo |
|
237 // |
|
238 { |
|
239 const TInt KRampUpSamples = 50; |
|
240 const TInt KRampDownSamples = 50; |
|
241 |
|
242 ASSERT(aBuffer.MaxLength()>= (iChannels<<1)); |
|
243 aBuffer.SetMax(); |
|
244 |
|
245 TBool silence; |
|
246 TInt samples = 0; // |
|
247 TInt used = 0; // Data used |
|
248 TInt avail = aBuffer.Length(); // Data filled |
|
249 TInt count = 0; // Data to be converted |
|
250 TBool rampUp = EFalse; |
|
251 |
|
252 TMdaPtr8 fill; |
|
253 fill.Set(aBuffer); // Pointer to data left to be filled |
|
254 |
|
255 // |
|
256 // The rest of this function will loop around continually until the buffer |
|
257 // is filled or there is no more data to play |
|
258 // |
|
259 |
|
260 Restart: |
|
261 silence = EFalse; // Reset |
|
262 if (iSamplesLeft == 0) |
|
263 { |
|
264 if (iTrailingSilence == 0) |
|
265 { |
|
266 TInt error = GetNextTone(); |
|
267 if (error) |
|
268 return error; |
|
269 |
|
270 rampUp = ETrue; |
|
271 if ((iSamplesLeft==0)&&(iTrailingSilence==0)) |
|
272 { |
|
273 if ((iSilenceBetweenRepeats)&&(!iAfterRepeatSilence)) |
|
274 { |
|
275 iTrailingSilence = iSilenceBetweenRepeats; |
|
276 iAfterRepeatSilence = ETrue; |
|
277 goto Restart; |
|
278 } |
|
279 else |
|
280 { |
|
281 if ((iRepeats>0)||(iRepeats==KMdaRepeatForever)) |
|
282 { |
|
283 iAfterRepeatSilence = EFalse; |
|
284 if (iRepeats>0) |
|
285 iRepeats--; |
|
286 |
|
287 Reset(); |
|
288 goto Restart; |
|
289 } |
|
290 } |
|
291 // No more to play |
|
292 goto Finished; |
|
293 } |
|
294 goto Restart; |
|
295 } |
|
296 else |
|
297 { |
|
298 silence = ETrue; |
|
299 samples = iTrailingSilence; |
|
300 } |
|
301 } |
|
302 else |
|
303 samples = iSamplesLeft; |
|
304 |
|
305 count = Min(samples,avail>>1); |
|
306 fill.SetLength(count<<1); |
|
307 |
|
308 if (!silence) |
|
309 { // Generate wave |
|
310 iSineWave.Generate(REINTERPRET_CAST(TInt16*,&fill[0]),count); |
|
311 |
|
312 if (iRampUp) |
|
313 { |
|
314 // Ramp up volume at beginning of tone |
|
315 if (rampUp) |
|
316 { // Fade in first few samples |
|
317 if(count < KRampUpSamples) |
|
318 { |
|
319 // Partial rampup due to being at the end of the buffer |
|
320 TInt fadeInLength = Min(KRampUpSamples,(fill.Length()>>1)); |
|
321 iIncompleteVolume = (count*((1<<15)/KRampUpSamples)); |
|
322 RampVolume(CONST_CAST(TInt16*,REINTERPRET_CAST(const TInt16*,(&fill[0]))),fadeInLength,0,iIncompleteVolume); |
|
323 iRampUpRemainder = fadeInLength; |
|
324 iIncompleteRampUp = ETrue; |
|
325 } |
|
326 else |
|
327 { |
|
328 // Normal rampup |
|
329 TInt fadeInLength = Min(Min(KRampUpSamples,iSamplesLeft),(fill.Length()>>1)); |
|
330 RampVolume(CONST_CAST(TInt16*,REINTERPRET_CAST(const TInt16*,(&fill[0]))),fadeInLength,0,1<<15); |
|
331 iIncompleteRampUp = EFalse; |
|
332 } |
|
333 } |
|
334 else if (iIncompleteRampUp) |
|
335 { |
|
336 // Completing partial rampup at the start of a new buffer |
|
337 TInt fadeInLength = Min(Min((KRampUpSamples-iRampUpRemainder),iSamplesLeft),(fill.Length()>>1)); |
|
338 RampVolume(CONST_CAST(TInt16*,REINTERPRET_CAST(const TInt16*,(&fill[0]))),fadeInLength,iIncompleteVolume,1<<15); |
|
339 iIncompleteRampUp = EFalse; |
|
340 } |
|
341 } |
|
342 if (iRampDown) |
|
343 { // Ramp down volume at end of tone |
|
344 if ((iSamplesLeft-count) < KRampDownSamples) |
|
345 { |
|
346 if(iSamplesLeft-count == 0) |
|
347 { |
|
348 // Fade out last few samples |
|
349 TInt startVolume = 1<<15;; |
|
350 TInt fadeOutLength = Min(Min(KRampDownSamples,iSamplesLeft),(fill.Length()>>1)); |
|
351 |
|
352 if(iIncompleteRampDown) |
|
353 { |
|
354 // Completing partial rampdown at the start of a new buffer |
|
355 startVolume -= iIncompleteVolume; |
|
356 iIncompleteRampDown = EFalse; |
|
357 } |
|
358 |
|
359 RampVolume(CONST_CAST(TInt16*,REINTERPRET_CAST(const TInt16*,(&(fill.Right(fadeOutLength<<1))[0]))),fadeOutLength,startVolume,0); |
|
360 } |
|
361 else if(iSamplesLeft-count > 0) |
|
362 { |
|
363 // Partial rampdown due to being at the end of the buffer |
|
364 TInt rampDifference = (KRampDownSamples-(iSamplesLeft-count)); |
|
365 TInt fadeOutLength = Min(Min(rampDifference,iSamplesLeft),(fill.Length()>>1)); |
|
366 iIncompleteVolume = ((rampDifference*(1<<15))/KRampDownSamples); |
|
367 |
|
368 RampVolume(CONST_CAST(TInt16*,REINTERPRET_CAST(const TInt16*,(&(fill.Right(fadeOutLength<<1))[0]))),fadeOutLength,1<<15,(1<<15)-iIncompleteVolume); |
|
369 |
|
370 iIncompleteRampDown = ETrue; |
|
371 } |
|
372 } |
|
373 } |
|
374 iSamplesLeft -= count; |
|
375 } |
|
376 else |
|
377 { // Generate silence |
|
378 fill.FillZ(count<<1); |
|
379 iTrailingSilence -= count; |
|
380 } |
|
381 |
|
382 used += count<<1; |
|
383 avail -= count<<1; |
|
384 fill.Shift(count<<1); |
|
385 |
|
386 if (avail>(iChannels<<1)) |
|
387 goto Restart; |
|
388 |
|
389 Finished: |
|
390 |
|
391 aBuffer.SetLength(used); |
|
392 |
|
393 // Do any ramp up that is required |
|
394 if (iRampUpLeft>0) |
|
395 { |
|
396 TInt words = iRampUpLeft * iChannels; |
|
397 words = Min(words,used>>1); |
|
398 if (words>0) // In case buffer has zero length... |
|
399 { |
|
400 TInt left = iRampUpLeft * iChannels; |
|
401 TInt rampup = iRampUpCount * iChannels; |
|
402 iRampUpLeft -= words/iChannels; |
|
403 TInt16* sample = REINTERPRET_CAST(TInt16*,&aBuffer[0]); |
|
404 while (words--) |
|
405 { |
|
406 *sample++ = STATIC_CAST(TInt16,(TInt32(*sample)*(rampup-(left--)))/rampup); |
|
407 } |
|
408 } |
|
409 } |
|
410 |
|
411 return KErrNone; |
|
412 } |
|
413 |
|
414 TInt TMdaToneGenerator::DurationToSamples(const TTimeIntervalMicroSeconds& aDuration) |
|
415 // |
|
416 // Convert the given duration to a sample count using the current settings |
|
417 // |
|
418 { |
|
419 const TInt64 KTInt64OneMilion = 1000000; |
|
420 |
|
421 // Calculate duration as samples |
|
422 TInt64 microSeconds(aDuration.Int64()); // MSVC doesn't like "aDuration.Int64()" in line below |
|
423 TInt64 dur = ((TInt64(iRate) * TInt64(iChannels) * microSeconds) / KTInt64OneMilion); |
|
424 if (I64HIGH(dur)>0) |
|
425 return KMaxTInt; // Ridiculous! |
|
426 else |
|
427 return I64LOW(dur); |
|
428 } |
|
429 |
|
430 // |
|
431 // TMdaSimpleToneGenerator |
|
432 // |
|
433 |
|
434 void TMdaSimpleToneGenerator::Reset() |
|
435 { |
|
436 iPlayed = EFalse; |
|
437 } |
|
438 |
|
439 void TMdaSimpleToneGenerator::SetFrequencyAndDuration(TInt aFrequency, const TTimeIntervalMicroSeconds& aDuration) |
|
440 // |
|
441 // Store the frequency and duration of the specified sine tone |
|
442 // |
|
443 { |
|
444 iFrequency = aFrequency; |
|
445 iDuration = aDuration; |
|
446 iPlayed = EFalse; |
|
447 } |
|
448 |
|
449 TInt TMdaSimpleToneGenerator::GetNextTone() |
|
450 // |
|
451 // Simple implementation - just sets the supplied frequency and duration |
|
452 // |
|
453 { |
|
454 // This class only plays one tone for the specified duration |
|
455 if (!iPlayed) |
|
456 { |
|
457 iSamplesLeft = I64LOW((iDuration.Int64() * TInt64(iRate))/1000000); |
|
458 iSineWave.SetFrequency(iFrequency,1<<14); |
|
459 iPlayed = ETrue; |
|
460 iTrailingSilence = 20; // Just to stop clicking |
|
461 } |
|
462 return KErrNone; |
|
463 } |
|
464 |
|
465 // |
|
466 // TMdaDualToneGenerator |
|
467 // |
|
468 |
|
469 void TMdaDualToneGenerator::Reset() |
|
470 { |
|
471 iPlayed = EFalse; |
|
472 } |
|
473 |
|
474 void TMdaDualToneGenerator::SetFrequencyAndDuration(TInt aFrequencyOne, TInt aFrequencyTwo, const TTimeIntervalMicroSeconds& aDuration) |
|
475 { |
|
476 // Store the frequencies and duration of the specified dual tone |
|
477 iFrequencyOne = aFrequencyOne; |
|
478 iFrequencyTwo = aFrequencyTwo; |
|
479 iDuration = aDuration; |
|
480 iPlayed = EFalse; |
|
481 } |
|
482 |
|
483 // |
|
484 // This is called by TMdaToneGenerator::FillBuffer() |
|
485 // to calculate the number of samples (iSamplesLeft) that will be needed |
|
486 // for the tone to be played and to initialize the sine wave generator. |
|
487 // If the tone has already been played, then leaves iSamplesLeft |
|
488 // unmodified (should be zero) to indicate that it has finished. |
|
489 // |
|
490 TInt TMdaDualToneGenerator::GetNextTone() |
|
491 { |
|
492 // This class only plays one tone for the specified duration |
|
493 if (!iPlayed) |
|
494 { |
|
495 iSamplesLeft = I64LOW((iDuration.Int64() * TInt64(iRate))/KOneMillionMicroSeconds); |
|
496 iSineWave.SetFrequency(iFrequencyOne, KMaxAmplitude/2, iFrequencyTwo, KMaxAmplitude/2); |
|
497 iPlayed = ETrue; |
|
498 iTrailingSilence = KDefaultTrailingSilenceSamples; // Just to stop clicking |
|
499 } |
|
500 return KErrNone; |
|
501 } |
|
502 // |
|
503 // TMdaDTMFGenerator |
|
504 // |
|
505 |
|
506 const TInt KRecalculateToneLengths = KMinTInt; |
|
507 |
|
508 void TMdaDTMFGenerator::Reset() |
|
509 { |
|
510 iChar = 0; |
|
511 } |
|
512 |
|
513 void TMdaDTMFGenerator::SetToneDurations(const TTimeIntervalMicroSeconds32 aOn, |
|
514 const TTimeIntervalMicroSeconds32 aOff, |
|
515 const TTimeIntervalMicroSeconds32 aPause) |
|
516 // |
|
517 // Setup the DTMF tone durations |
|
518 // aOn can be == -1 indicating should play first tone indefinately |
|
519 // |
|
520 { |
|
521 ASSERT(aOn.Int() >=-1); |
|
522 ASSERT(aOff.Int()>=0); |
|
523 ASSERT(aPause.Int()>=0); |
|
524 |
|
525 iOn = aOn; |
|
526 iOff = aOff; |
|
527 iPause = aPause; |
|
528 |
|
529 iOnSamples = KRecalculateToneLengths; // Must recalculate these later |
|
530 } |
|
531 |
|
532 void TMdaDTMFGenerator::SetString(const TDesC& aDTMFString) |
|
533 // |
|
534 // Store the DTMF string to be played |
|
535 // No need to validate it as it will already have been checked |
|
536 // |
|
537 { |
|
538 iChar = 0; |
|
539 iDTMFString = &aDTMFString; |
|
540 } |
|
541 |
|
542 const TUint8 KDtmfVolumeTable[4][4]= |
|
543 // |
|
544 // Relative strengths to assign to different DTMF tones |
|
545 // |
|
546 // This is only important if DTMFs are being played through a speaker |
|
547 // and need to be machine-recognisable. This table compensates for frequency |
|
548 // drop-off in the speaker and can boost the relative volume of some |
|
549 // frequencies so they are still within tolerance. |
|
550 // |
|
551 // The values normally need to be determined using a frequency analyser on |
|
552 // the hardware |
|
553 // |
|
554 // Each column == same low frequency (697, 770, 852, 941 Hz) |
|
555 // Each row == same high frequency (1209, 1336, 1477, 1633 Hz) |
|
556 // |
|
557 // The value are interpreted as ratios: |
|
558 // 0 == 100% low |
|
559 // 7f == 50% low, 50% high |
|
560 // ff == 100% high |
|
561 // |
|
562 { |
|
563 {38,27,29,37}, |
|
564 {46,36,36,46}, |
|
565 {62,47,49,58}, |
|
566 {70,56,60,68} |
|
567 }; |
|
568 |
|
569 const TUint8 KDtmfTone697=0x0; |
|
570 const TUint8 KDtmfTone770=0x1; |
|
571 const TUint8 KDtmfTone852=0x2; |
|
572 const TUint8 KDtmfTone941=0x3; |
|
573 |
|
574 const TUint8 KDtmfTone1209=0x00; |
|
575 const TUint8 KDtmfTone1336=0x10; |
|
576 const TUint8 KDtmfTone1477=0x20; |
|
577 const TUint8 KDtmfTone1633=0x30; |
|
578 |
|
579 const TUint8 KDtmfToneTable[16]= |
|
580 { |
|
581 KDtmfTone941|KDtmfTone1336,//0 |
|
582 KDtmfTone697|KDtmfTone1209,//1 |
|
583 KDtmfTone697|KDtmfTone1336,//2 |
|
584 KDtmfTone697|KDtmfTone1477,//3 |
|
585 KDtmfTone770|KDtmfTone1209,//4 |
|
586 KDtmfTone770|KDtmfTone1336,//5 |
|
587 KDtmfTone770|KDtmfTone1477,//6 |
|
588 KDtmfTone852|KDtmfTone1209,//7 |
|
589 KDtmfTone852|KDtmfTone1336,//8 |
|
590 KDtmfTone852|KDtmfTone1477,//9 |
|
591 |
|
592 KDtmfTone697|KDtmfTone1633,//A |
|
593 KDtmfTone770|KDtmfTone1633,//B |
|
594 KDtmfTone852|KDtmfTone1633,//C |
|
595 KDtmfTone941|KDtmfTone1633,//D |
|
596 KDtmfTone941|KDtmfTone1209,//E or * |
|
597 KDtmfTone941|KDtmfTone1477,//F or # |
|
598 }; |
|
599 |
|
600 TInt TMdaDTMFGenerator::GetNextTone() |
|
601 // |
|
602 // Setup frequency/duration/silence settings for next DTMF tone |
|
603 // Supported characters are 0-9 A-F * # , and any kind of white space |
|
604 // |
|
605 { |
|
606 TBool onlyPlayFirstTone = EFalse; |
|
607 |
|
608 if (iOnSamples == KRecalculateToneLengths) |
|
609 { |
|
610 // Must recalculate tone durations as samples |
|
611 |
|
612 // Handle special case where tone on duration negative |
|
613 // - meaning play first character indefinately |
|
614 if (iOn.Int()>=0) |
|
615 iOnSamples = DurationToSamples(TInt64(iOn.Int())); |
|
616 else |
|
617 { |
|
618 onlyPlayFirstTone = ETrue; |
|
619 iOnSamples = -1; |
|
620 } |
|
621 |
|
622 iOffSamples = DurationToSamples(TInt64(iOff.Int())); |
|
623 iPauseSamples = DurationToSamples(TInt64(iPause.Int())); |
|
624 } |
|
625 |
|
626 ASSERT(iDTMFString); |
|
627 |
|
628 if (iChar==iDTMFString->Length()) |
|
629 return KErrNone; // Finished. Nothing to do |
|
630 |
|
631 TInt highFrequency = 0; |
|
632 TInt highVolume = 0; |
|
633 TInt lowFrequency = 0; |
|
634 TInt lowVolume =0; |
|
635 |
|
636 Retry: |
|
637 TChar c((*iDTMFString)[iChar++]); |
|
638 if ((TUint)c=='#' || (TUint)c=='*' || c.IsHexDigit()) |
|
639 { |
|
640 TInt tableIndex; |
|
641 switch ((TUint)c) |
|
642 { |
|
643 case '*': |
|
644 tableIndex=14; |
|
645 break; |
|
646 case '#': |
|
647 tableIndex=15; |
|
648 break; |
|
649 default: |
|
650 if (c.IsDigit()) |
|
651 tableIndex=(TUint)c-'0'; |
|
652 else //letter |
|
653 { |
|
654 c.UpperCase(); |
|
655 tableIndex=(TUint)c-'A'+10; |
|
656 } |
|
657 } |
|
658 TInt high=KDtmfToneTable[tableIndex]&0xf0; |
|
659 TInt low=KDtmfToneTable[tableIndex]&0x0f; |
|
660 switch(high) |
|
661 { |
|
662 case KDtmfTone1209: |
|
663 highFrequency=1209; |
|
664 break; |
|
665 case KDtmfTone1336: |
|
666 highFrequency=1336; |
|
667 break; |
|
668 case KDtmfTone1477: |
|
669 highFrequency=1477; |
|
670 break; |
|
671 default://KDtmfTone1633: |
|
672 highFrequency=1633; |
|
673 break; |
|
674 } |
|
675 switch(low) |
|
676 { |
|
677 case KDtmfTone697: |
|
678 lowFrequency=697; |
|
679 break; |
|
680 case KDtmfTone770: |
|
681 lowFrequency=770; |
|
682 break; |
|
683 case KDtmfTone852: |
|
684 lowFrequency=852; |
|
685 break; |
|
686 default://KDtmfTone941: |
|
687 lowFrequency=941; |
|
688 break; |
|
689 } |
|
690 high>>=4; |
|
691 const TUint8* dtmfVolumes=&KDtmfVolumeTable[0][0]; |
|
692 TInt volume=dtmfVolumes[((low)<<2)+(high)]<<7; |
|
693 highVolume = volume; |
|
694 lowVolume = (1<<15)-volume; |
|
695 |
|
696 iTrailingSilence = iOffSamples; |
|
697 iSamplesLeft = iOnSamples; |
|
698 } |
|
699 else if ((TUint)c==',') |
|
700 { |
|
701 iTrailingSilence = iPauseSamples; |
|
702 iSamplesLeft = 0; |
|
703 } |
|
704 else if (c.IsSpace()) |
|
705 { |
|
706 if (iChar < iDTMFString->Length()) |
|
707 goto Retry; |
|
708 } |
|
709 else |
|
710 return KErrCorrupt; |
|
711 |
|
712 if (iOnSamples < 0) // Play only first character for ever |
|
713 { |
|
714 iTrailingSilence = 0; |
|
715 iSamplesLeft = iRate * iChannels; // One second of samples |
|
716 iChar = 0; // Reset so this character is played again next time |
|
717 iRampDown = EFalse; |
|
718 if (!onlyPlayFirstTone) |
|
719 { |
|
720 iRampUp = EFalse; |
|
721 // This is not the first time around so we should not |
|
722 // reset the tone generator - it will already have the |
|
723 // correct settings and setting them again would cause |
|
724 // an audible discontinuity |
|
725 return KErrNone; |
|
726 } |
|
727 } |
|
728 |
|
729 iSineWave.SetFrequency(highFrequency,highVolume,lowFrequency,lowVolume); |
|
730 return KErrNone; |
|
731 } |
|
732 |
|
733 // |
|
734 // TMdaSequenceGenerator |
|
735 // |
|
736 |
|
737 // |
|
738 // Sequence constants |
|
739 // |
|
740 |
|
741 //const TInt KMaxFixedSequenceStack=KMaxSequenceStack;//Max nesting level of FixedSequences * 2 |
|
742 #ifdef _DEBUG |
|
743 const TInt16 KFixedSequenceSignatureOne='S'+('Q'<<8); |
|
744 const TInt16 KFixedSequenceSignatureTwo='N'+('C'<<8); |
|
745 #endif // _DEBUG |
|
746 |
|
747 const TInt KFixedSequenceFunctionReturn=-1; |
|
748 const TInt KFixedSequenceFunctionStartLoop=-2; |
|
749 const TInt KFixedSequenceFunctionEndLoop=-3; |
|
750 |
|
751 void TMdaSequenceGenerator::Reset() |
|
752 { |
|
753 iInstructionPtr = REINTERPRET_CAST(const TInt16*,&((*iSequenceData)[0])); |
|
754 iInstructionPtr += 2; // Skip signature |
|
755 iStackIndex = 0; |
|
756 } |
|
757 |
|
758 void TMdaSequenceGenerator::SetSequenceData(const TDesC8& aSequenceData) |
|
759 // |
|
760 // Store the sequence data to be played |
|
761 // No need to validate it as it will already have been checked |
|
762 // |
|
763 { |
|
764 iSequenceData = &aSequenceData; |
|
765 iInstructionPtr = REINTERPRET_CAST(const TInt16*,&aSequenceData[0]); |
|
766 iLastInstruction = iInstructionPtr + (iSequenceData->Length()>>1) - 1; |
|
767 |
|
768 // These are asserts because this should not be called if signature not present |
|
769 ASSERT(*iInstructionPtr == KFixedSequenceSignatureOne); |
|
770 ASSERT(*(iInstructionPtr+1) == KFixedSequenceSignatureTwo); |
|
771 |
|
772 iInstructionPtr += 2; // Skip signature |
|
773 |
|
774 iStackIndex = 0; |
|
775 } |
|
776 |
|
777 TInt TMdaSequenceGenerator::GetNextTone() |
|
778 // |
|
779 // |
|
780 { |
|
781 ASSERT(iInstructionPtr); // Sanity check |
|
782 |
|
783 TInt ret = KRequestPending; |
|
784 while (ret == KRequestPending) |
|
785 { |
|
786 if (iInstructionPtr > iLastInstruction) |
|
787 ret = KErrCorrupt; |
|
788 else if (*iInstructionPtr<=0) |
|
789 { |
|
790 switch (*iInstructionPtr) |
|
791 { |
|
792 case KFixedSequenceFunctionReturn: // End of sequence |
|
793 ret = KErrNone; |
|
794 break; |
|
795 |
|
796 case KFixedSequenceFunctionStartLoop: |
|
797 if (iStackIndex>2) // Validate - can only nest twice |
|
798 ret = KErrCorrupt; |
|
799 else if ((iInstructionPtr+2) > iLastInstruction) |
|
800 ret = KErrCorrupt; // Don't run off end of sequence |
|
801 else |
|
802 { |
|
803 iStack[iStackIndex++]=(TInt)(iInstructionPtr+2); |
|
804 iStack[iStackIndex++]=(TInt)*(iInstructionPtr+1); |
|
805 iInstructionPtr+=2; |
|
806 } |
|
807 break; |
|
808 |
|
809 case KFixedSequenceFunctionEndLoop: |
|
810 if (iStackIndex==0) // Validate - must already be nested |
|
811 ret = KErrCorrupt; |
|
812 else |
|
813 { |
|
814 if ((--iStack[iStackIndex-1])!=0) |
|
815 iInstructionPtr=(TInt16*)iStack[iStackIndex-2]; |
|
816 else |
|
817 { |
|
818 iStackIndex-=2; |
|
819 iInstructionPtr++; |
|
820 } |
|
821 } |
|
822 break; |
|
823 |
|
824 default: // Bad sequence |
|
825 ret = KErrCorrupt; |
|
826 } |
|
827 } |
|
828 else |
|
829 { |
|
830 if ((iInstructionPtr+5) > iLastInstruction) |
|
831 ret = KErrCorrupt; // Don't run off end of sequence |
|
832 else |
|
833 { |
|
834 iSamplesLeft = *iInstructionPtr++; |
|
835 TInt freqOne = *iInstructionPtr++; |
|
836 TInt volOne = *iInstructionPtr++; |
|
837 TInt freqTwo = *iInstructionPtr++; |
|
838 TInt volTwo = *iInstructionPtr++; |
|
839 |
|
840 if ((volOne> 1<<15)||(volTwo > 1<<15)) |
|
841 ret = KErrCorrupt; |
|
842 else |
|
843 { |
|
844 iSineWave.SetFrequency(freqOne,volOne,freqTwo,volTwo); |
|
845 ret = KErrNone; |
|
846 } |
|
847 } |
|
848 } |
|
849 } |
|
850 return ret; |
|
851 } |
|
852 |
|
853 // --------------------------------- |
|
854 // Code to generate sine table files used by tone generator |
|
855 // Optionally called from InitL() |
|
856 // #define GENERATE_SINE_TABLES 1 |
|
857 #ifdef GENERATE_SINE_TABLES |
|
858 LOCAL_C GenerateSineTableL() |
|
859 { |
|
860 _LIT(KSineFile,"sine.txt"); |
|
861 _LIT(KSineIncFile,"sineinc.txt"); |
|
862 |
|
863 RFile file; |
|
864 file.Replace(MdaManager::Fs(),KSineFile,EFileWrite); |
|
865 CleanupClosePushL(file); |
|
866 |
|
867 RFile file2; |
|
868 file2.Replace(MdaManager::Fs(),KSineIncFile,EFileWrite); |
|
869 CleanupClosePushL(file2); |
|
870 |
|
871 const TReal pi=3.141592653589; |
|
872 const TReal twopi=pi*2; |
|
873 const TReal samples = 256.0; |
|
874 const TReal step = twopi/samples; |
|
875 |
|
876 TBuf8<128> sinebuffer; |
|
877 TBuf8<128> incbuffer; |
|
878 TReal res; |
|
879 TInt first=0; |
|
880 TInt last=KMaxTInt; |
|
881 TInt current; |
|
882 _LIT8(KFormat,"%6d,"); |
|
883 _LIT8(KNewLine,"\n"); |
|
884 |
|
885 for(TReal angle=0.0;angle<=(twopi-step);) // Copes with rounding errors |
|
886 { |
|
887 sinebuffer.Zero(); |
|
888 incbuffer.Zero(); |
|
889 for (int i=0;i<8;i++) |
|
890 { |
|
891 User::LeaveIfError(Math::Sin(res,angle)); |
|
892 current = TInt(KMaxTInt16*res); |
|
893 sinebuffer.AppendFormat(KFormat,current); |
|
894 if (last != KMaxTInt) |
|
895 incbuffer.AppendFormat(KFormat,current-last); |
|
896 else |
|
897 first = current; |
|
898 last = current; |
|
899 angle += step; |
|
900 } |
|
901 sinebuffer.Append(KNewLine); |
|
902 incbuffer.Append(KNewLine); |
|
903 file.Write(sinebuffer); |
|
904 file2.Write(incbuffer); |
|
905 } |
|
906 |
|
907 // Write fine difference to incbuffer - differnece between first and last |
|
908 incbuffer.Zero(); |
|
909 incbuffer.AppendFormat(KFormat,first-last); |
|
910 incbuffer.Append(KNewLine); |
|
911 file2.Write(incbuffer); |
|
912 |
|
913 CleanupStack::PopAndDestroy(2); |
|
914 } |
|
915 #endif |
|
916 //------------------------------- |