1 /* |
|
2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: Implements the ECom plugin for HTI audio playback control |
|
15 * service. |
|
16 * |
|
17 */ |
|
18 |
|
19 |
|
20 |
|
21 // INCLUDE FILES |
|
22 #include <apgcli.h> |
|
23 #include <AudioPreference.h> |
|
24 #include <bautils.h> |
|
25 #include <e32std.h> |
|
26 #include <HtiDispatcherInterface.h> |
|
27 #include <HtiLogging.h> |
|
28 #include <pathinfo.h> |
|
29 |
|
30 #include "HtiAudioServicePlugin.h" |
|
31 |
|
32 // EXTERNAL DATA STRUCTURES |
|
33 |
|
34 // EXTERNAL FUNCTION PROTOTYPES |
|
35 |
|
36 // CONSTANTS |
|
37 const static TInt KPlayToneCmdLength = 13; |
|
38 const static TInt KStopCmdLength = 1; |
|
39 const static TInt KSetVolCmdLength = 2; |
|
40 const static TInt KListCmdMinLength = 4; |
|
41 const static TInt KPlayDtmfCmdMinLength = 17; |
|
42 const static TInt KPlayFileCmdMinLength = 21; |
|
43 const static TInt KDurationCmdMinLength = 6; |
|
44 const static TInt KMaxVolCmdMinLength = 6; |
|
45 |
|
46 const static TInt KTUintSize = sizeof( TUint ); |
|
47 |
|
48 // MACROS |
|
49 |
|
50 // LOCAL CONSTANTS AND MACROS |
|
51 const static TUid KAudioServiceUid = { 0x10210CCB }; |
|
52 |
|
53 _LIT( KBackslash, "\\" ); |
|
54 _LIT( KRngMimeType, "application/vnd.nokia.ringing-tone" ); |
|
55 _LIT( KAudioMimeType, "audio/*" ); |
|
56 |
|
57 // NOTE: Max length for error description is defined |
|
58 // in HtiDispatcherInterface.h (currently 118). |
|
59 _LIT8( KErrorNoCmd, "ERROR: No command given" ); |
|
60 _LIT8( KErrorUnknownCmd, "ERROR: Unknown Audio Service command" ); |
|
61 _LIT8( KErrorInvalidParameters, |
|
62 "ERROR: Invalid parameter data for this command" ); |
|
63 _LIT8( KErrorInvalidPath, "ERROR: Invalid path" ); |
|
64 _LIT8( KErrorToneInitFailed, "ERROR: Tone initialization failed" ); |
|
65 _LIT8( KErrorTonePlayFailed, "ERROR: Tone playing failed" ); |
|
66 _LIT8( KErrorFileInitFailed, "ERROR: File playing initialization failed" ); |
|
67 _LIT8( KErrorFilePlayFailed, "ERROR: File playing failed" ); |
|
68 _LIT8( KErrorBusyPlaying, "ERROR: Currently busy playing" ); |
|
69 _LIT8( KErrorNothingPlaying, "ERROR: Nothing playing" ); |
|
70 _LIT8( KErrorDurationFailed, "ERROR: Duration query failed" ); |
|
71 _LIT8( KErrorMaxVolFailed, "ERROR: Max volume query failed" ); |
|
72 _LIT8( KErrorPosition, "ERROR: Invalid start or end position value" ); |
|
73 |
|
74 // MODULE DATA STRUCTURES |
|
75 |
|
76 // LOCAL FUNCTION PROTOTYPES |
|
77 |
|
78 // FORWARD DECLARATIONS |
|
79 |
|
80 // ============================= LOCAL FUNCTIONS =============================== |
|
81 |
|
82 // ============================ MEMBER FUNCTIONS =============================== |
|
83 |
|
84 // ----------------------------------------------------------------------------- |
|
85 // CHtiAudioServicePlugin::CHtiAudioServicePlugin |
|
86 // C++ default constructor can NOT contain any code, that might leave. |
|
87 // ----------------------------------------------------------------------------- |
|
88 // |
|
89 CHtiAudioServicePlugin::CHtiAudioServicePlugin():iIsBusy( EFalse ), |
|
90 iIsPlaying( EFalse ), |
|
91 iCommandId( 0 ), |
|
92 iPlayCommandId( 0 ), |
|
93 iMessage( NULL ), |
|
94 iErrorCode( 0 ), |
|
95 iVolume( 0 ), |
|
96 iRepeats( 0 ), |
|
97 iTrailingSilence( 0 ), |
|
98 iDtmfLength( 0 ), |
|
99 iDtmfGapLength( 0 ), |
|
100 iStartPos( 0 ), |
|
101 iEndPos( 0 ) |
|
102 { |
|
103 } |
|
104 |
|
105 |
|
106 // ----------------------------------------------------------------------------- |
|
107 // CHtiAudioServicePlugin::ConstructL |
|
108 // Symbian 2nd phase constructor can leave. |
|
109 // ----------------------------------------------------------------------------- |
|
110 // |
|
111 void CHtiAudioServicePlugin::ConstructL() |
|
112 { |
|
113 } |
|
114 |
|
115 |
|
116 // ----------------------------------------------------------------------------- |
|
117 // CHtiAudioServicePlugin::NewL |
|
118 // Two-phased constructor. |
|
119 // ----------------------------------------------------------------------------- |
|
120 // |
|
121 CHtiAudioServicePlugin* CHtiAudioServicePlugin::NewL() |
|
122 { |
|
123 CHtiAudioServicePlugin* self = new (ELeave) CHtiAudioServicePlugin; |
|
124 CleanupStack::PushL( self ); |
|
125 self->ConstructL(); |
|
126 CleanupStack::Pop(); |
|
127 return self; |
|
128 } |
|
129 |
|
130 |
|
131 // Destructor |
|
132 CHtiAudioServicePlugin::~CHtiAudioServicePlugin() |
|
133 { |
|
134 delete iMessage; |
|
135 iMessage = NULL; |
|
136 delete iAudioPlayer; |
|
137 iAudioPlayer = NULL; |
|
138 delete iTonePlayer; |
|
139 iTonePlayer = NULL; |
|
140 } |
|
141 |
|
142 |
|
143 // ----------------------------------------------------------------------------- |
|
144 // CHtiAudioServicePlugin::ProcessMessageL |
|
145 // ----------------------------------------------------------------------------- |
|
146 // |
|
147 void CHtiAudioServicePlugin::ProcessMessageL( const TDesC8& aMessage, |
|
148 THtiMessagePriority /*aPriority*/ ) |
|
149 { |
|
150 HTI_LOG_FUNC_IN( "CHtiAudioServicePlugin::ProcessMessageL" ); |
|
151 HTI_LOG_FORMAT( "Message length = %d", aMessage.Length() ); |
|
152 |
|
153 if ( iIsBusy ) |
|
154 { |
|
155 HTI_LOG_TEXT( "Plugin is busy - leaving" ); |
|
156 User::Leave( KErrInUse ); |
|
157 } |
|
158 |
|
159 // Will be set to EFalse in the SendResponseMsg or SendErrorResponseMsg |
|
160 // methods when the response has been successfully sent and the plugin is |
|
161 // ready for next message. |
|
162 iIsBusy = ETrue; |
|
163 |
|
164 if ( aMessage.Length() < 1 ) |
|
165 { |
|
166 User::LeaveIfError( |
|
167 SendErrorResponseMsg( KErrArgument, KErrorNoCmd ) ); |
|
168 return; |
|
169 } |
|
170 |
|
171 iCommandId = aMessage[0]; |
|
172 HTI_LOG_FORMAT( "Command = %d", iCommandId ); |
|
173 TInt err = KErrNone; |
|
174 |
|
175 if ( iCommandId == ECmdListAudioFiles ) |
|
176 { |
|
177 TRAP( err, HandleListAudioFilesCmdL( aMessage ) ); |
|
178 } |
|
179 |
|
180 else if ( iCommandId == ECmdPlayFile ) |
|
181 { |
|
182 TRAP( err, HandlePlayFileCmdL( aMessage ) ); |
|
183 } |
|
184 |
|
185 else if ( iCommandId == ECmdPlayTone ) |
|
186 { |
|
187 TRAP( err, HandlePlayToneCmdL( aMessage ) ); |
|
188 } |
|
189 |
|
190 else if ( iCommandId == ECmdPlayDtmf ) |
|
191 { |
|
192 TRAP( err, HandlePlayDtmfCmdL( aMessage ) ); |
|
193 } |
|
194 |
|
195 else if ( iCommandId == ECmdStop ) |
|
196 { |
|
197 TRAP( err, HandleStopCmdL( aMessage ) ); |
|
198 } |
|
199 |
|
200 else if ( iCommandId == ECmdGetDuration ) |
|
201 { |
|
202 TRAP( err, HandleGetDurationCmdL( aMessage ) ); |
|
203 } |
|
204 |
|
205 else if ( iCommandId == ECmdGetMaxVol ) |
|
206 { |
|
207 TRAP( err, HandleGetMaxVolCmdL( aMessage ) ); |
|
208 } |
|
209 |
|
210 else if ( iCommandId == ECmdSetVol ) |
|
211 { |
|
212 TRAP( err, HandleSetVolCmdL( aMessage ) ); |
|
213 } |
|
214 |
|
215 else |
|
216 { |
|
217 User::LeaveIfError( |
|
218 SendErrorResponseMsg( KErrArgument, KErrorUnknownCmd ) ); |
|
219 } |
|
220 |
|
221 if ( err != KErrNone ) |
|
222 { |
|
223 User::LeaveIfError( |
|
224 SendErrorResponseMsg( err, KNullDesC8, iCommandId ) ); |
|
225 } |
|
226 |
|
227 HTI_LOG_FUNC_OUT( "CHtiAudioServicePlugin::ProcessMessageL" ); |
|
228 } |
|
229 |
|
230 |
|
231 // ----------------------------------------------------------------------------- |
|
232 // CHtiAudioServicePlugin::HandleListAudioFilesCmdL() |
|
233 // ----------------------------------------------------------------------------- |
|
234 // |
|
235 void CHtiAudioServicePlugin::HandleListAudioFilesCmdL( const TDesC8& aMessage ) |
|
236 { |
|
237 HTI_LOG_FUNC_IN( "CHtiAudioServicePlugin::HandleListAudioFilesCmdL" ); |
|
238 |
|
239 RFs fsSession; |
|
240 User::LeaveIfError( fsSession.Connect() ); |
|
241 CleanupClosePushL( fsSession ); |
|
242 |
|
243 // Build a list of directories to scan |
|
244 CDesCArraySeg* directories = new (ELeave) CDesCArraySeg( 5 ); |
|
245 CleanupStack::PushL( directories ); |
|
246 |
|
247 if ( aMessage.Length() == 1 ) // Add default sound directories |
|
248 { |
|
249 TFileName directory; |
|
250 |
|
251 // ROM |
|
252 directory.Append( PathInfo::RomRootPath() ); |
|
253 directory.Append( PathInfo::SoundsPath() ); |
|
254 if ( BaflUtils::PathExists( fsSession, directory ) ) |
|
255 { |
|
256 directories->AppendL( directory ); |
|
257 AddSubdirsRecursivelyL( directories->MdcaPoint( |
|
258 directories->Count() - 1 ), *directories, fsSession ); |
|
259 } |
|
260 |
|
261 // Phone memory |
|
262 directory.Zero(); |
|
263 directory.Append( PathInfo::PhoneMemoryRootPath() ); |
|
264 directory.Append( PathInfo::SoundsPath() ); |
|
265 if ( BaflUtils::PathExists( fsSession, directory ) ) |
|
266 { |
|
267 directories->AppendL( directory ); |
|
268 AddSubdirsRecursivelyL( directories->MdcaPoint( |
|
269 directories->Count() - 1 ), *directories, fsSession ); |
|
270 } |
|
271 |
|
272 // Memory card |
|
273 directory.Zero(); |
|
274 directory.Append( PathInfo::MemoryCardRootPath() ); |
|
275 directory.Append( PathInfo::SoundsPath() ); |
|
276 if ( BaflUtils::PathExists( fsSession, directory ) ) |
|
277 { |
|
278 directories->AppendL( directory ); |
|
279 AddSubdirsRecursivelyL( directories->MdcaPoint( |
|
280 directories->Count() - 1 ), *directories, fsSession ); |
|
281 } |
|
282 } |
|
283 |
|
284 else // Add given directory |
|
285 { |
|
286 if ( aMessage.Length() < KListCmdMinLength ) |
|
287 { |
|
288 User::LeaveIfError( SendErrorResponseMsg( |
|
289 KErrArgument, KErrorInvalidParameters ) ); |
|
290 CleanupStack::PopAndDestroy( 2 ); // directories, fsSession |
|
291 return; |
|
292 } |
|
293 |
|
294 TInt pathLength = aMessage[1]; |
|
295 if ( ( aMessage.Length() - pathLength ) != KListCmdMinLength - 2 ) |
|
296 { |
|
297 User::LeaveIfError( SendErrorResponseMsg( |
|
298 KErrArgument, KErrorInvalidParameters ) ); |
|
299 CleanupStack::PopAndDestroy( 2 ); // directories, fsSession |
|
300 return; |
|
301 } |
|
302 |
|
303 TFileName directory; |
|
304 TInt nextOffset = ParseString( aMessage, 1, directory ); |
|
305 TInt dirLength = directory.Length(); |
|
306 if ( dirLength < 2 || nextOffset < 0 ) |
|
307 { |
|
308 User::LeaveIfError( SendErrorResponseMsg( |
|
309 KErrArgument, KErrorInvalidParameters ) ); |
|
310 CleanupStack::PopAndDestroy( 2 ); // directories, fsSession |
|
311 return; |
|
312 } |
|
313 |
|
314 HTI_LOG_DES( directory ); |
|
315 |
|
316 if ( directory[dirLength - 1] != '\\' ) |
|
317 { |
|
318 HTI_LOG_TEXT( "Adding backslash to the end" ); |
|
319 directory.Append( KBackslash ); |
|
320 HTI_LOG_DES( directory ); |
|
321 } |
|
322 |
|
323 if ( BaflUtils::PathExists( fsSession, directory ) ) |
|
324 { |
|
325 HTI_LOG_TEXT( "Given path exists" ); |
|
326 directories->AppendL( directory ); |
|
327 AddSubdirsRecursivelyL( directories->MdcaPoint( |
|
328 directories->Count() - 1 ), *directories, fsSession ); |
|
329 } |
|
330 } |
|
331 |
|
332 // Buffer for the file list that is returned |
|
333 CBufFlat* fileListBuf = CBufFlat::NewL( 256 ); |
|
334 CleanupStack::PushL( fileListBuf ); |
|
335 TInt bufPos = 0; |
|
336 |
|
337 TInt audioFileCount = 0; |
|
338 TInt dirCount( directories->Count() ); |
|
339 HTI_LOG_FORMAT( "Total directory count = %d", dirCount ); |
|
340 |
|
341 if ( dirCount == 0 ) |
|
342 { |
|
343 HTI_LOG_TEXT( "The given directory did not exist" ); |
|
344 User::LeaveIfError( SendErrorResponseMsg( |
|
345 KErrArgument, KErrorInvalidPath ) ); |
|
346 CleanupStack::PopAndDestroy( 3 ); // fileListBuf, directories, fsSession |
|
347 return; |
|
348 } |
|
349 |
|
350 // Loop all the directories |
|
351 for ( TInt i = 0; i < dirCount; i++ ) |
|
352 { |
|
353 HTI_LOG_TEXT( "Reading dir:" ); |
|
354 HTI_LOG_DES( directories->MdcaPoint( i ) ); |
|
355 CDir* dir; |
|
356 TInt err = fsSession.GetDir( directories->MdcaPoint( i ), |
|
357 KEntryAttNormal, ESortNone, dir ); |
|
358 if ( err ) |
|
359 { |
|
360 delete dir; |
|
361 dir = NULL; |
|
362 continue; // This dir is skipped |
|
363 } |
|
364 CleanupStack::PushL( dir ); |
|
365 |
|
366 // Loop all the entries in this directory |
|
367 TInt fileCount( dir->Count() ); |
|
368 for ( TInt j = 0; j < fileCount; j++ ) |
|
369 { |
|
370 TFileName filePath; |
|
371 filePath.Copy( directories->MdcaPoint( i ) ); |
|
372 filePath.Append( ( *dir )[j].iName ); |
|
373 |
|
374 // Check MIME type match |
|
375 if ( MatchMimeTypeL( filePath, KAudioMimeType ) || |
|
376 MatchMimeTypeL( filePath, KRngMimeType ) ) |
|
377 { |
|
378 HBufC8* filePathBuf8 = HBufC8::NewLC( KMaxFileName ); |
|
379 filePathBuf8->Des().Copy( filePath ); |
|
380 TInt pathLength = filePathBuf8->Length(); |
|
381 HTI_LOG_DES( *filePathBuf8 ); |
|
382 fileListBuf->ExpandL( bufPos, pathLength + 1 ); |
|
383 TBuf8<1> lengthBuf; |
|
384 lengthBuf.Append( pathLength ); |
|
385 fileListBuf->Write( bufPos, lengthBuf, 1 ); |
|
386 bufPos++; |
|
387 fileListBuf->Write( bufPos, filePathBuf8->Ptr(), pathLength ); |
|
388 bufPos += pathLength; |
|
389 CleanupStack::PopAndDestroy(); // filePathBuf8 |
|
390 audioFileCount++; |
|
391 } |
|
392 |
|
393 } // files loop |
|
394 CleanupStack::PopAndDestroy(); // dir |
|
395 } // directories loop |
|
396 |
|
397 HTI_LOG_FORMAT( "Total audio file count = %d", audioFileCount ); |
|
398 |
|
399 // All files added - write number of files to the beginning of buffer... |
|
400 TBuf8<2> countBuf; |
|
401 countBuf.Append( (TUint8*)(&audioFileCount), 2 ); |
|
402 fileListBuf->InsertL( 0, countBuf, 2 ); |
|
403 |
|
404 // ...and send it away |
|
405 TPtr8 ptr = fileListBuf->Ptr( 0 ); |
|
406 User::LeaveIfError( SendResponseMsg( ptr ) ); |
|
407 |
|
408 CleanupStack::PopAndDestroy( 3 ); // fileListBuf, directories, fsSession |
|
409 |
|
410 HTI_LOG_FUNC_OUT( "CHtiAudioServicePlugin::HandleListAudioFilesCmdL" ); |
|
411 } |
|
412 |
|
413 |
|
414 // ----------------------------------------------------------------------------- |
|
415 // CHtiAudioServicePlugin::HandlePlayFileCmdL() |
|
416 // ----------------------------------------------------------------------------- |
|
417 // |
|
418 void CHtiAudioServicePlugin::HandlePlayFileCmdL( const TDesC8&aMessage ) |
|
419 { |
|
420 HTI_LOG_FUNC_IN( "CHtiAudioServicePlugin::HandlePlayFileCmdL" ); |
|
421 |
|
422 if ( iIsPlaying ) |
|
423 { |
|
424 User::LeaveIfError( SendErrorResponseMsg( |
|
425 KErrInUse, KErrorBusyPlaying ) ); |
|
426 return; |
|
427 } |
|
428 |
|
429 iPlayCommandId = ECmdPlayFile; |
|
430 |
|
431 /* |
|
432 Message bytes: |
|
433 0 = command code |
|
434 1 = path length |
|
435 2 - n = full path to file |
|
436 next 1 = volume |
|
437 next 4 = start position |
|
438 next 4 = end position |
|
439 next 1 = repeats |
|
440 next 4 = silence between repeats |
|
441 next 1 = audio setting |
|
442 */ |
|
443 |
|
444 if ( aMessage.Length() < KPlayFileCmdMinLength ) |
|
445 { |
|
446 User::LeaveIfError( SendErrorResponseMsg( |
|
447 KErrArgument, KErrorInvalidParameters ) ); |
|
448 return; |
|
449 } |
|
450 |
|
451 // Parse parameter values from the message |
|
452 const TUint8* ptr = aMessage.Ptr(); |
|
453 TInt pathLength = aMessage[1]; |
|
454 |
|
455 if ( ( aMessage.Length() - pathLength ) != |
|
456 ( KPlayFileCmdMinLength - 4 ) ) |
|
457 { |
|
458 User::LeaveIfError( SendErrorResponseMsg( |
|
459 KErrArgument, KErrorInvalidParameters ) ); |
|
460 return; |
|
461 } |
|
462 |
|
463 TFileName filePath; |
|
464 TInt nextOffset = ParseString( aMessage, 1, filePath ); |
|
465 if ( filePath.Length() < 2 || nextOffset < 0 ) |
|
466 { |
|
467 User::LeaveIfError( SendErrorResponseMsg( |
|
468 KErrArgument, KErrorInvalidParameters ) ); |
|
469 return; |
|
470 } |
|
471 HTI_LOG_TEXT( "Full file path:" ); |
|
472 HTI_LOG_DES( filePath ); |
|
473 iVolume = aMessage[nextOffset]; |
|
474 nextOffset++; |
|
475 HTI_LOG_FORMAT( "Volume = %d", iVolume ); |
|
476 iStartPos = ParseUint32( ptr + nextOffset ); |
|
477 HTI_LOG_FORMAT( "Start position = %d", iStartPos ); |
|
478 nextOffset += 4; |
|
479 iEndPos = ParseUint32( ptr + nextOffset ); |
|
480 HTI_LOG_FORMAT( "End position = %d", iEndPos ); |
|
481 nextOffset += 4; |
|
482 iRepeats = aMessage[nextOffset]; |
|
483 nextOffset++; |
|
484 HTI_LOG_FORMAT( "Repeats = %d", iRepeats ); |
|
485 iTrailingSilence = ParseUint32( ptr + nextOffset ); |
|
486 HTI_LOG_FORMAT( "Trailing silence = %d", iTrailingSilence ); |
|
487 nextOffset += 4; |
|
488 TInt audioSetting = aMessage[nextOffset]; |
|
489 HTI_LOG_FORMAT( "Audio setting = %d", audioSetting ); |
|
490 |
|
491 // Set audio settings |
|
492 if ( audioSetting > ERingTonePreview ) audioSetting = EDefault; |
|
493 SetAudioSettings( ( TAudioSetting ) audioSetting ); |
|
494 |
|
495 // Check if file is rng ringtone, it has to be played using tone player - |
|
496 // other formats are played with audio player. |
|
497 |
|
498 TInt err = KErrNone; |
|
499 TBool isRng = EFalse; |
|
500 TRAP( err, isRng = MatchMimeTypeL( filePath, KRngMimeType ) ); |
|
501 |
|
502 if ( err ) |
|
503 { |
|
504 User::LeaveIfError( SendErrorResponseMsg( err, KErrorFileInitFailed ) ); |
|
505 return; |
|
506 } |
|
507 |
|
508 if ( isRng ) |
|
509 { |
|
510 HTI_LOG_TEXT( "File is RNG - creating tone player" ); |
|
511 TRAP( err, iTonePlayer = CMdaAudioToneUtility::NewL( |
|
512 *this, NULL, iAudioPriority, iAudioPriorityPreference ) ); |
|
513 } |
|
514 |
|
515 else |
|
516 { |
|
517 HTI_LOG_TEXT( "File is not RNG - creating audio player" ); |
|
518 TRAP( err, iAudioPlayer = CMdaAudioPlayerUtility::NewFilePlayerL( |
|
519 filePath, *this, iAudioPriority, iAudioPriorityPreference ) ); |
|
520 // MapcInitComplete callback function will be called |
|
521 } |
|
522 |
|
523 if ( err ) |
|
524 { |
|
525 delete iAudioPlayer; |
|
526 iAudioPlayer = NULL; |
|
527 delete iTonePlayer; |
|
528 iTonePlayer = NULL; |
|
529 User::LeaveIfError( SendErrorResponseMsg( |
|
530 err, KErrorFileInitFailed ) ); |
|
531 } |
|
532 |
|
533 if ( iTonePlayer ) |
|
534 { |
|
535 iTonePlayer->PrepareToPlayFileSequence( filePath ); |
|
536 // MatoPrepareComplete callback function will be called |
|
537 } |
|
538 |
|
539 HTI_LOG_FUNC_OUT( "CHtiAudioServicePlugin::HandlePlayFileCmdL" ); |
|
540 } |
|
541 |
|
542 |
|
543 // ----------------------------------------------------------------------------- |
|
544 // CHtiAudioServicePlugin::HandlePlayToneCmdL() |
|
545 // ----------------------------------------------------------------------------- |
|
546 // |
|
547 void CHtiAudioServicePlugin::HandlePlayToneCmdL( const TDesC8& aMessage ) |
|
548 { |
|
549 HTI_LOG_FUNC_IN( "CHtiAudioServicePlugin::HandlePlayToneCmdL" ); |
|
550 |
|
551 if ( iIsPlaying ) |
|
552 { |
|
553 User::LeaveIfError( SendErrorResponseMsg( |
|
554 KErrInUse, KErrorBusyPlaying ) ); |
|
555 return; |
|
556 } |
|
557 |
|
558 iPlayCommandId = ECmdPlayTone; |
|
559 |
|
560 /* |
|
561 Message bytes: |
|
562 0 = command code |
|
563 1 - 2 = frequency value |
|
564 3 - 6 = duration value |
|
565 7 = volume value |
|
566 8 = repeat value |
|
567 9 - 12 = silence between repeats |
|
568 */ |
|
569 |
|
570 if ( aMessage.Length() != KPlayToneCmdLength ) |
|
571 { |
|
572 User::LeaveIfError( SendErrorResponseMsg( |
|
573 KErrArgument, KErrorInvalidParameters ) ); |
|
574 return; |
|
575 } |
|
576 |
|
577 // Parse parameter values from the message |
|
578 const TUint8* ptr = aMessage.Ptr(); |
|
579 TInt frequency = ParseUint16( ptr + 1 ); |
|
580 HTI_LOG_FORMAT( "Freq = %d", frequency ); |
|
581 TUint duration = ParseUint32( ptr + 3 ); |
|
582 HTI_LOG_FORMAT( "Duration = %d", duration ); |
|
583 iVolume = aMessage[7]; |
|
584 HTI_LOG_FORMAT( "Volume = %d", iVolume ); |
|
585 iRepeats = aMessage[8]; |
|
586 HTI_LOG_FORMAT( "Repeats = %d", iRepeats ); |
|
587 iTrailingSilence = ParseUint32( ptr + 9 ); |
|
588 HTI_LOG_FORMAT( "Silence = %d", iTrailingSilence ); |
|
589 |
|
590 TRAPD( err, iTonePlayer = CMdaAudioToneUtility::NewL( *this ) ); |
|
591 |
|
592 if ( err ) |
|
593 { |
|
594 delete iTonePlayer; |
|
595 iTonePlayer = NULL; |
|
596 User::LeaveIfError( SendErrorResponseMsg( |
|
597 err, KErrorToneInitFailed ) ); |
|
598 } |
|
599 |
|
600 iTonePlayer->PrepareToPlayTone( frequency, |
|
601 TTimeIntervalMicroSeconds( duration ) ); |
|
602 // MatoPrepareComplete callback function will be called when ready to play |
|
603 HTI_LOG_FUNC_OUT( "CHtiAudioServicePlugin::HandlePlayToneCmdL" ); |
|
604 } |
|
605 |
|
606 |
|
607 // ----------------------------------------------------------------------------- |
|
608 // CHtiAudioServicePlugin::HandlePlayDtmfCmdL() |
|
609 // ----------------------------------------------------------------------------- |
|
610 // |
|
611 void CHtiAudioServicePlugin::HandlePlayDtmfCmdL( const TDesC8& aMessage ) |
|
612 { |
|
613 HTI_LOG_FUNC_IN( "CHtiAudioServicePlugin::HandlePlayDtmfCmdL" ); |
|
614 |
|
615 if ( iIsPlaying ) |
|
616 { |
|
617 User::LeaveIfError( SendErrorResponseMsg( |
|
618 KErrInUse, KErrorBusyPlaying ) ); |
|
619 return; |
|
620 } |
|
621 |
|
622 iPlayCommandId = ECmdPlayDtmf; |
|
623 |
|
624 /* |
|
625 Message bytes: |
|
626 0 = command code |
|
627 1 = DTMF string length |
|
628 2 - n = dtmf string |
|
629 next 4 = tone length |
|
630 next 4 = tone gap length |
|
631 next 1 = volume |
|
632 next 1 = repeats |
|
633 next 4 = silence between repeats |
|
634 */ |
|
635 |
|
636 if ( aMessage.Length() < KPlayDtmfCmdMinLength ) |
|
637 { |
|
638 User::LeaveIfError( SendErrorResponseMsg( |
|
639 KErrArgument, KErrorInvalidParameters ) ); |
|
640 return; |
|
641 } |
|
642 |
|
643 // Parse parameter values from the message |
|
644 const TUint8* ptr = aMessage.Ptr(); |
|
645 TInt stringLength = aMessage[1]; |
|
646 |
|
647 if ( ( aMessage.Length() - stringLength ) != |
|
648 ( KPlayDtmfCmdMinLength - 1 ) ) |
|
649 { |
|
650 User::LeaveIfError( SendErrorResponseMsg( |
|
651 KErrArgument, KErrorInvalidParameters ) ); |
|
652 return; |
|
653 } |
|
654 |
|
655 TBuf<255> dtmfString; |
|
656 TInt nextOffset = ParseString( aMessage, 1, dtmfString ); |
|
657 if ( dtmfString.Length() < 1 || nextOffset < 0 ) |
|
658 { |
|
659 User::LeaveIfError( SendErrorResponseMsg( |
|
660 KErrArgument, KErrorInvalidParameters ) ); |
|
661 return; |
|
662 } |
|
663 HTI_LOG_TEXT( "DTMF string:" ); |
|
664 HTI_LOG_DES( dtmfString ); |
|
665 iDtmfLength = ParseUint32( ptr + nextOffset ); |
|
666 nextOffset += 4; |
|
667 HTI_LOG_FORMAT( "DTMF length = %d", iDtmfLength ); |
|
668 iDtmfGapLength = ParseUint32( ptr + nextOffset ); |
|
669 nextOffset += 4; |
|
670 HTI_LOG_FORMAT( "DTMF gap length = %d", iDtmfGapLength ); |
|
671 iVolume = aMessage[nextOffset]; |
|
672 nextOffset++; |
|
673 HTI_LOG_FORMAT( "Volume = %d", iVolume ); |
|
674 iRepeats = aMessage[nextOffset]; |
|
675 nextOffset++; |
|
676 HTI_LOG_FORMAT( "Repeats = %d", iRepeats ); |
|
677 iTrailingSilence = ParseUint32( ptr + nextOffset ); |
|
678 HTI_LOG_FORMAT( "Trailing silence = %d", iTrailingSilence ); |
|
679 |
|
680 SetAudioSettings( EDtmfString ); |
|
681 |
|
682 TRAPD( err, iTonePlayer = CMdaAudioToneUtility::NewL( |
|
683 *this, NULL, iAudioPriority, iAudioPriorityPreference ) ); |
|
684 |
|
685 if ( err ) |
|
686 { |
|
687 delete iTonePlayer; |
|
688 iTonePlayer = NULL; |
|
689 User::LeaveIfError( SendErrorResponseMsg( err, KErrorToneInitFailed ) ); |
|
690 } |
|
691 |
|
692 iTonePlayer->PrepareToPlayDTMFString( dtmfString ); |
|
693 // MatoPrepareComplete callback function will be called when ready to play |
|
694 HTI_LOG_FUNC_OUT( "CHtiAudioServicePlugin::HandlePlayDtmfCmdL" ); |
|
695 } |
|
696 |
|
697 |
|
698 // ----------------------------------------------------------------------------- |
|
699 // CHtiAudioServicePlugin::HandleStopCmdL() |
|
700 // ----------------------------------------------------------------------------- |
|
701 // |
|
702 void CHtiAudioServicePlugin::HandleStopCmdL( const TDesC8& aMessage ) |
|
703 { |
|
704 HTI_LOG_FUNC_IN( "CHtiAudioServicePlugin::HandleStopCmdL" ); |
|
705 |
|
706 if ( aMessage.Length() != KStopCmdLength ) |
|
707 { |
|
708 User::LeaveIfError( SendErrorResponseMsg( |
|
709 KErrArgument, KErrorInvalidParameters ) ); |
|
710 return; |
|
711 } |
|
712 |
|
713 if ( !iIsPlaying ) |
|
714 { |
|
715 HTI_LOG_TEXT( "Not playing - nothing to stop" ); |
|
716 // Just send "OK" reply if nothing is currently playing |
|
717 User::LeaveIfError( SendResponseMsg( _L8( "OK" ) ) ); |
|
718 } |
|
719 |
|
720 else |
|
721 { |
|
722 if ( iAudioPlayer ) |
|
723 { |
|
724 HTI_LOG_TEXT( "Stopping audio player" ); |
|
725 iAudioPlayer->Stop(); |
|
726 iIsPlaying = EFalse; |
|
727 delete iAudioPlayer; |
|
728 iAudioPlayer = NULL; |
|
729 // According to documentation should call MapcPlayComplete callback |
|
730 // method but it doesn't, so sending reply here. |
|
731 User::LeaveIfError( SendResponseMsg( _L8( "OK" ) ) ); |
|
732 } |
|
733 |
|
734 else if ( iTonePlayer ) |
|
735 { |
|
736 HTI_LOG_TEXT( "Stopping tone player" ); |
|
737 iTonePlayer->CancelPlay(); |
|
738 iIsPlaying = EFalse; |
|
739 delete iTonePlayer; |
|
740 iTonePlayer = NULL; |
|
741 // Callback method MatoPlayComplete is not called - |
|
742 // sending reply here. |
|
743 User::LeaveIfError( SendResponseMsg( _L8( "OK" ) ) ); |
|
744 } |
|
745 } |
|
746 HTI_LOG_FUNC_OUT( "CHtiAudioServicePlugin::HandleStopCmdL" ); |
|
747 } |
|
748 |
|
749 |
|
750 // ----------------------------------------------------------------------------- |
|
751 // CHtiAudioServicePlugin::HandleGetDurationCmdL() |
|
752 // ----------------------------------------------------------------------------- |
|
753 // |
|
754 void CHtiAudioServicePlugin::HandleGetDurationCmdL( const TDesC8& aMessage ) |
|
755 { |
|
756 HTI_LOG_FUNC_IN( "CHtiAudioServicePlugin::HandleGetDurationCmdL" ); |
|
757 |
|
758 if ( iIsPlaying ) |
|
759 { |
|
760 // If currently playing, no parameters allowed. Returns the duration |
|
761 // of currently playing sound. |
|
762 if ( aMessage.Length() != 1 ) |
|
763 { |
|
764 User::LeaveIfError( SendErrorResponseMsg( |
|
765 KErrInUse, KErrorBusyPlaying ) ); |
|
766 return; |
|
767 } |
|
768 |
|
769 if ( iAudioPlayer ) |
|
770 { |
|
771 TTimeIntervalMicroSeconds durationValue = |
|
772 iAudioPlayer->Duration(); |
|
773 |
|
774 if ( I64HIGH( durationValue.Int64() ) > 0 ) |
|
775 { |
|
776 User::LeaveIfError( SendErrorResponseMsg( |
|
777 KErrOverflow, KErrorDurationFailed ) ); |
|
778 return; |
|
779 } |
|
780 else |
|
781 { |
|
782 TUint duration = I64LOW( durationValue.Int64() ); |
|
783 TBuf8<KTUintSize> durationBuf; |
|
784 durationBuf.Append( (TUint8*)(&duration), KTUintSize ); |
|
785 User::LeaveIfError( SendResponseMsg( durationBuf ) ); |
|
786 return; |
|
787 } |
|
788 } |
|
789 |
|
790 else // Duration supported only for audio player |
|
791 { |
|
792 User::LeaveIfError( SendErrorResponseMsg( |
|
793 KErrNotSupported, KErrorDurationFailed ) ); |
|
794 return; |
|
795 } |
|
796 } |
|
797 |
|
798 /* Command must have file path parameter if not currently playing. |
|
799 Message bytes: |
|
800 0 = command code |
|
801 1 = path length |
|
802 2 - n = full path to file |
|
803 */ |
|
804 |
|
805 if ( aMessage.Length() < KDurationCmdMinLength ) |
|
806 { |
|
807 User::LeaveIfError( SendErrorResponseMsg( |
|
808 KErrArgument, KErrorInvalidParameters ) ); |
|
809 return; |
|
810 } |
|
811 |
|
812 TInt pathLength = aMessage[1]; |
|
813 if ( ( aMessage.Length() - pathLength ) != |
|
814 ( KDurationCmdMinLength - 4 ) ) |
|
815 { |
|
816 User::LeaveIfError( SendErrorResponseMsg( |
|
817 KErrArgument, KErrorInvalidParameters ) ); |
|
818 return; |
|
819 } |
|
820 |
|
821 // Parse parameter values from the message |
|
822 TFileName filePath; |
|
823 TInt nextOffset = ParseString( aMessage, 1, filePath ); |
|
824 if ( filePath.Length() < 2 || nextOffset < 0 ) |
|
825 { |
|
826 User::LeaveIfError( SendErrorResponseMsg( |
|
827 KErrArgument, KErrorInvalidParameters ) ); |
|
828 return; |
|
829 } |
|
830 HTI_LOG_TEXT( "Full file path:" ); |
|
831 HTI_LOG_DES( filePath ); |
|
832 |
|
833 TRAPD( err, iAudioPlayer = CMdaAudioPlayerUtility::NewFilePlayerL( |
|
834 filePath, *this ) ); |
|
835 if ( err ) |
|
836 { |
|
837 delete iAudioPlayer; |
|
838 iAudioPlayer = NULL; |
|
839 User::LeaveIfError( SendErrorResponseMsg( err, KErrorDurationFailed ) ); |
|
840 } |
|
841 |
|
842 // MapcInitComplete callback function will be called |
|
843 HTI_LOG_FUNC_OUT( "CHtiAudioServicePlugin::HandleGetDurationCmdL" ); |
|
844 } |
|
845 |
|
846 |
|
847 // ----------------------------------------------------------------------------- |
|
848 // CHtiAudioServicePlugin::HandleGetMaxVolCmdL() |
|
849 // ----------------------------------------------------------------------------- |
|
850 // |
|
851 void CHtiAudioServicePlugin::HandleGetMaxVolCmdL( const TDesC8& aMessage ) |
|
852 { |
|
853 HTI_LOG_FUNC_IN( "CHtiAudioServicePlugin::HandleGetMaxVolCmdL" ); |
|
854 |
|
855 if ( iIsPlaying ) |
|
856 { |
|
857 // If currently playing, no parameters allowed. Returns the max volume |
|
858 // of currently playing sound. |
|
859 if ( aMessage.Length() != 1 ) |
|
860 { |
|
861 User::LeaveIfError( SendErrorResponseMsg( |
|
862 KErrInUse, KErrorBusyPlaying ) ); |
|
863 return; |
|
864 } |
|
865 |
|
866 TInt maxVol = -1; |
|
867 |
|
868 if ( iAudioPlayer ) |
|
869 { |
|
870 maxVol = iAudioPlayer->MaxVolume(); |
|
871 } |
|
872 |
|
873 else if ( iTonePlayer ) |
|
874 { |
|
875 maxVol = iTonePlayer->MaxVolume(); |
|
876 } |
|
877 |
|
878 HTI_LOG_FORMAT( "Max volume = %d", maxVol ); |
|
879 |
|
880 if ( maxVol < 0 ) |
|
881 { |
|
882 // Should not happen |
|
883 User::LeaveIfError( SendErrorResponseMsg( |
|
884 KErrGeneral, KErrorMaxVolFailed ) ); |
|
885 return; |
|
886 } |
|
887 |
|
888 if ( maxVol > 255 ) maxVol = 255; |
|
889 TBuf8<1> maxVolBuf; |
|
890 maxVolBuf.Append( maxVol ); |
|
891 User::LeaveIfError( SendResponseMsg( maxVolBuf ) ); |
|
892 return; |
|
893 } |
|
894 |
|
895 /* |
|
896 Message bytes: |
|
897 0 = command code |
|
898 1 = path length |
|
899 2 - n = full path to file |
|
900 */ |
|
901 |
|
902 if ( aMessage.Length() < KMaxVolCmdMinLength ) |
|
903 { |
|
904 User::LeaveIfError( SendErrorResponseMsg( |
|
905 KErrArgument, KErrorInvalidParameters ) ); |
|
906 return; |
|
907 } |
|
908 |
|
909 TInt pathLength = aMessage[1]; |
|
910 if ( ( aMessage.Length() - pathLength ) != ( KMaxVolCmdMinLength - 4 ) ) |
|
911 { |
|
912 User::LeaveIfError( SendErrorResponseMsg( |
|
913 KErrArgument, KErrorInvalidParameters ) ); |
|
914 return; |
|
915 } |
|
916 |
|
917 // Parse parameter values from the message |
|
918 TFileName filePath; |
|
919 TInt nextOffset = ParseString( aMessage, 1, filePath ); |
|
920 if ( filePath.Length() < 2 || nextOffset < 0 ) |
|
921 { |
|
922 User::LeaveIfError( SendErrorResponseMsg( |
|
923 KErrArgument, KErrorInvalidParameters ) ); |
|
924 return; |
|
925 } |
|
926 HTI_LOG_TEXT( "Full file path:" ); |
|
927 HTI_LOG_DES( filePath ); |
|
928 |
|
929 TInt err = KErrNone; |
|
930 TBool isRng = EFalse; |
|
931 TRAP( err, isRng = MatchMimeTypeL( filePath, KRngMimeType ) ); |
|
932 |
|
933 if ( err ) |
|
934 { |
|
935 User::LeaveIfError( SendErrorResponseMsg( err, KErrorMaxVolFailed ) ); |
|
936 return; |
|
937 } |
|
938 |
|
939 if ( isRng ) |
|
940 { |
|
941 HTI_LOG_TEXT( "File is RNG - creating tone player" ); |
|
942 TRAP( err, iTonePlayer = CMdaAudioToneUtility::NewL( *this ) ); |
|
943 } |
|
944 |
|
945 else |
|
946 { |
|
947 HTI_LOG_TEXT( "File is not RNG - creating audio player" ); |
|
948 TRAP( err, iAudioPlayer = CMdaAudioPlayerUtility::NewFilePlayerL( |
|
949 filePath, *this ) ); |
|
950 // MapcInitComplete callback function will be called |
|
951 } |
|
952 |
|
953 if ( err ) |
|
954 { |
|
955 delete iAudioPlayer; |
|
956 iAudioPlayer = NULL; |
|
957 delete iTonePlayer; |
|
958 iTonePlayer = NULL; |
|
959 User::LeaveIfError( SendErrorResponseMsg( err, KErrorMaxVolFailed ) ); |
|
960 } |
|
961 |
|
962 if ( iTonePlayer ) |
|
963 { |
|
964 iTonePlayer->PrepareToPlayFileSequence( filePath ); |
|
965 // MatoPrepareComplete callback function will be called |
|
966 } |
|
967 |
|
968 HTI_LOG_FUNC_OUT( "CHtiAudioServicePlugin::HandleGetMaxVolCmdL" ); |
|
969 } |
|
970 |
|
971 |
|
972 // ----------------------------------------------------------------------------- |
|
973 // CHtiAudioServicePlugin::HandleSetVolCmdL() |
|
974 // ----------------------------------------------------------------------------- |
|
975 // |
|
976 void CHtiAudioServicePlugin::HandleSetVolCmdL( const TDesC8& aMessage ) |
|
977 { |
|
978 HTI_LOG_FUNC_IN( "CHtiAudioServicePlugin::HandleSetVolCmdL" ); |
|
979 |
|
980 if ( aMessage.Length() != KSetVolCmdLength ) |
|
981 { |
|
982 User::LeaveIfError( SendErrorResponseMsg( KErrArgument, |
|
983 KErrorInvalidParameters ) ); |
|
984 } |
|
985 |
|
986 if ( !iIsPlaying ) |
|
987 { |
|
988 HTI_LOG_TEXT( "Nothing playing - not setting volume" ); |
|
989 User::LeaveIfError( SendErrorResponseMsg( |
|
990 KErrNotReady, KErrorNothingPlaying ) ); |
|
991 } |
|
992 |
|
993 else |
|
994 { |
|
995 TInt volume = aMessage[1]; // [0] = command code, [1] = volume value |
|
996 HTI_LOG_FORMAT( "requested volume = %d", volume ); |
|
997 |
|
998 if ( iAudioPlayer ) |
|
999 { |
|
1000 HTI_LOG_TEXT( "Setting audio player volume" ); |
|
1001 TInt maxVol = iAudioPlayer->MaxVolume(); |
|
1002 HTI_LOG_FORMAT( "max volume = %d", maxVol ); |
|
1003 if ( volume > maxVol ) volume = maxVol; |
|
1004 iAudioPlayer->SetVolume( volume ); |
|
1005 TBuf8<1> volBuf; |
|
1006 volBuf.Append( volume ); |
|
1007 User::LeaveIfError( SendResponseMsg( volBuf ) ); |
|
1008 } |
|
1009 else if ( iTonePlayer ) |
|
1010 { |
|
1011 HTI_LOG_TEXT( "Setting tone player volume" ); |
|
1012 TInt maxVol = iTonePlayer->MaxVolume(); |
|
1013 HTI_LOG_FORMAT( "max volume = %d", maxVol ); |
|
1014 if ( volume > maxVol ) volume = maxVol; |
|
1015 iTonePlayer->SetVolume( volume ); |
|
1016 TBuf8<1> volBuf; |
|
1017 volBuf.Append( volume ); |
|
1018 User::LeaveIfError( SendResponseMsg( volBuf ) ); |
|
1019 } |
|
1020 } |
|
1021 HTI_LOG_FUNC_OUT( "CHtiAudioServicePlugin::HandleSetVolCmdL" ); |
|
1022 } |
|
1023 |
|
1024 |
|
1025 // ----------------------------------------------------------------------------- |
|
1026 // CHtiAudioServicePlugin::MatoPrepareComplete() |
|
1027 // Tone player prepare complete |
|
1028 // ----------------------------------------------------------------------------- |
|
1029 // |
|
1030 void CHtiAudioServicePlugin::MatoPrepareComplete( TInt aError ) |
|
1031 { |
|
1032 HTI_LOG_FUNC_IN( "CHtiAudioServicePlugin::MatoPrepareComplete" ); |
|
1033 |
|
1034 if ( iCommandId == ECmdGetMaxVol ) |
|
1035 { |
|
1036 if ( aError ) |
|
1037 { |
|
1038 SendErrorResponseMsg( aError, KErrorMaxVolFailed ); |
|
1039 } |
|
1040 |
|
1041 else |
|
1042 { |
|
1043 TInt maxVol = iTonePlayer->MaxVolume(); |
|
1044 HTI_LOG_FORMAT( "Max volume = %d", maxVol ); |
|
1045 if ( maxVol > 255 ) maxVol = 255; |
|
1046 TBuf8<1> maxVolBuf; |
|
1047 maxVolBuf.Append( maxVol ); |
|
1048 SendResponseMsg( maxVolBuf ); |
|
1049 } |
|
1050 |
|
1051 delete iTonePlayer; |
|
1052 iTonePlayer = NULL; |
|
1053 return; |
|
1054 } |
|
1055 |
|
1056 if ( aError ) |
|
1057 { |
|
1058 SendErrorResponseMsg( aError, KErrorToneInitFailed ); |
|
1059 delete iTonePlayer; |
|
1060 iTonePlayer = NULL; |
|
1061 } |
|
1062 |
|
1063 else |
|
1064 { |
|
1065 if ( iCommandId == ECmdPlayDtmf ) |
|
1066 { |
|
1067 iTonePlayer->SetDTMFLengths( |
|
1068 TTimeIntervalMicroSeconds32( iDtmfLength ), |
|
1069 TTimeIntervalMicroSeconds32( iDtmfGapLength ), |
|
1070 TTimeIntervalMicroSeconds32( 0 ) ); |
|
1071 } |
|
1072 |
|
1073 if ( iVolume > iTonePlayer->MaxVolume() ) |
|
1074 { |
|
1075 iVolume = iTonePlayer->MaxVolume(); |
|
1076 } |
|
1077 |
|
1078 iTonePlayer->SetVolume( iVolume ); |
|
1079 iTonePlayer->SetRepeats( iRepeats + 1, |
|
1080 TTimeIntervalMicroSeconds( iTrailingSilence ) ); |
|
1081 iIsPlaying = ETrue; |
|
1082 iTonePlayer->Play(); |
|
1083 iIsBusy = EFalse; |
|
1084 // MatoPlayComplete callback function will be called when playing ends. |
|
1085 } |
|
1086 |
|
1087 HTI_LOG_FUNC_OUT( "CHtiAudioServicePlugin::MatoPrepareComplete" ); |
|
1088 } |
|
1089 |
|
1090 |
|
1091 // ----------------------------------------------------------------------------- |
|
1092 // CHtiAudioServicePlugin::MatoPlayComplete() |
|
1093 // Tone play complete |
|
1094 // ----------------------------------------------------------------------------- |
|
1095 // |
|
1096 void CHtiAudioServicePlugin::MatoPlayComplete( TInt aError ) |
|
1097 { |
|
1098 HTI_LOG_FUNC_IN( "CHtiAudioServicePlugin::MatoPlayComplete" ); |
|
1099 |
|
1100 iIsPlaying = EFalse; |
|
1101 iIsBusy = ETrue; // Busy dispatching the play complete message |
|
1102 |
|
1103 if ( aError ) |
|
1104 { |
|
1105 SendErrorResponseMsg( aError, KErrorTonePlayFailed, iPlayCommandId ); |
|
1106 } |
|
1107 |
|
1108 else |
|
1109 { |
|
1110 SendResponseMsg( _L8( "OK" ), iPlayCommandId ); |
|
1111 } |
|
1112 |
|
1113 delete iTonePlayer; |
|
1114 iTonePlayer = NULL; |
|
1115 |
|
1116 HTI_LOG_FUNC_OUT( "CHtiAudioServicePlugin::MatoPlayComplete" ); |
|
1117 } |
|
1118 |
|
1119 |
|
1120 // ----------------------------------------------------------------------------- |
|
1121 // CHtiAudioServicePlugin::MapcInitComplete() |
|
1122 // Audio player init complete |
|
1123 // ----------------------------------------------------------------------------- |
|
1124 // |
|
1125 void CHtiAudioServicePlugin::MapcInitComplete( TInt aError, |
|
1126 const TTimeIntervalMicroSeconds& aDuration ) |
|
1127 { |
|
1128 HTI_LOG_FUNC_IN( "CHtiAudioServicePlugin::MapcInitComplete" ); |
|
1129 |
|
1130 if ( iCommandId == ECmdPlayFile ) |
|
1131 { |
|
1132 if ( aError ) |
|
1133 { |
|
1134 SendErrorResponseMsg( aError, KErrorFileInitFailed ); |
|
1135 delete iAudioPlayer; |
|
1136 iAudioPlayer = NULL; |
|
1137 } |
|
1138 |
|
1139 else |
|
1140 { |
|
1141 if ( iEndPos < iStartPos || |
|
1142 TTimeIntervalMicroSeconds( iStartPos ) > aDuration ) |
|
1143 { |
|
1144 SendErrorResponseMsg( KErrArgument, KErrorPosition ); |
|
1145 delete iAudioPlayer; |
|
1146 iAudioPlayer = NULL; |
|
1147 return; |
|
1148 } |
|
1149 |
|
1150 if ( iEndPos > 0 ) |
|
1151 { |
|
1152 iAudioPlayer->SetPlayWindow( |
|
1153 TTimeIntervalMicroSeconds( iStartPos ), |
|
1154 TTimeIntervalMicroSeconds( iEndPos ) ); |
|
1155 } |
|
1156 |
|
1157 HTI_LOG_FORMAT( "Max volume = %d", iAudioPlayer->MaxVolume() ); |
|
1158 HTI_LOG_FORMAT( "Setting volume = %d", iVolume ); |
|
1159 if ( iVolume > iAudioPlayer->MaxVolume() ) |
|
1160 { |
|
1161 iVolume = iAudioPlayer->MaxVolume(); |
|
1162 } |
|
1163 |
|
1164 iAudioPlayer->SetVolume( iVolume ); |
|
1165 iAudioPlayer->SetRepeats( iRepeats, |
|
1166 TTimeIntervalMicroSeconds( iTrailingSilence ) ); |
|
1167 iIsPlaying = ETrue; |
|
1168 iAudioPlayer->Play(); |
|
1169 |
|
1170 // Have to do this after play command because |
|
1171 // volume setting before play seems to have no effect. |
|
1172 iAudioPlayer->SetVolume( 0 ); |
|
1173 iAudioPlayer->SetVolume( iVolume ); |
|
1174 |
|
1175 iIsBusy = EFalse; |
|
1176 // MapcPlayComplete callback function is called when playing ends |
|
1177 } |
|
1178 } |
|
1179 |
|
1180 else if ( iCommandId == ECmdGetDuration ) |
|
1181 { |
|
1182 if ( aError ) |
|
1183 { |
|
1184 SendErrorResponseMsg( aError, KErrorDurationFailed ); |
|
1185 } |
|
1186 |
|
1187 else |
|
1188 { |
|
1189 if ( I64HIGH( aDuration.Int64() ) > 0 ) |
|
1190 { |
|
1191 SendErrorResponseMsg( KErrOverflow, KErrorDurationFailed ); |
|
1192 } |
|
1193 else |
|
1194 { |
|
1195 TUint duration = I64LOW( aDuration.Int64() ); |
|
1196 TBuf8<KTUintSize> durationBuf; |
|
1197 durationBuf.Append( (TUint8*)(&duration), KTUintSize ); |
|
1198 SendResponseMsg( durationBuf ); |
|
1199 } |
|
1200 } |
|
1201 delete iAudioPlayer; |
|
1202 iAudioPlayer = NULL; |
|
1203 } |
|
1204 |
|
1205 else if ( iCommandId == ECmdGetMaxVol ) |
|
1206 { |
|
1207 if ( aError ) |
|
1208 { |
|
1209 SendErrorResponseMsg( aError, KErrorMaxVolFailed ); |
|
1210 } |
|
1211 |
|
1212 else |
|
1213 { |
|
1214 TInt maxVol = iAudioPlayer->MaxVolume(); |
|
1215 HTI_LOG_FORMAT( "Max volume = %d", maxVol ); |
|
1216 if ( maxVol > 255 ) maxVol = 255; |
|
1217 TBuf8<1> maxVolBuf; |
|
1218 maxVolBuf.Append( maxVol ); |
|
1219 SendResponseMsg( maxVolBuf ); |
|
1220 } |
|
1221 delete iAudioPlayer; |
|
1222 iAudioPlayer = NULL; |
|
1223 } |
|
1224 |
|
1225 HTI_LOG_FUNC_OUT( "CHtiAudioServicePlugin::MapcInitComplete" ); |
|
1226 } |
|
1227 |
|
1228 |
|
1229 // ----------------------------------------------------------------------------- |
|
1230 // CHtiAudioServicePlugin::MapcPlayComplete() |
|
1231 // Audio play complete |
|
1232 // ----------------------------------------------------------------------------- |
|
1233 // |
|
1234 void CHtiAudioServicePlugin::MapcPlayComplete( TInt aError ) |
|
1235 { |
|
1236 HTI_LOG_FUNC_IN( "CHtiAudioServicePlugin::MapcPlayComplete" ); |
|
1237 |
|
1238 iIsPlaying = EFalse; |
|
1239 iIsBusy = ETrue; // Busy dispatching the play complete message |
|
1240 |
|
1241 if ( aError ) |
|
1242 { |
|
1243 SendErrorResponseMsg( aError, KErrorFilePlayFailed, ECmdPlayFile ); |
|
1244 } |
|
1245 |
|
1246 else |
|
1247 { |
|
1248 SendResponseMsg( _L8( "OK" ), iPlayCommandId ); |
|
1249 } |
|
1250 |
|
1251 delete iAudioPlayer; |
|
1252 iAudioPlayer = NULL; |
|
1253 |
|
1254 HTI_LOG_FUNC_OUT( "CHtiAudioServicePlugin::MapcPlayComplete" ); |
|
1255 } |
|
1256 |
|
1257 |
|
1258 // ----------------------------------------------------------------------------- |
|
1259 // CHtiAudioServicePlugin::NotifyMemoryChange |
|
1260 // Called when HTI Framework has dispatched a message forward and the amount |
|
1261 // of free memory in the message queue has changed. |
|
1262 // ----------------------------------------------------------------------------- |
|
1263 // |
|
1264 void CHtiAudioServicePlugin::NotifyMemoryChange( TInt aAvailableMemory ) |
|
1265 { |
|
1266 if ( iIsBusy && iMessage ) |
|
1267 { |
|
1268 if ( aAvailableMemory >= iMessage->Size() ) |
|
1269 { |
|
1270 if ( iErrorCode == 0 ) |
|
1271 { |
|
1272 TInt err = iDispatcher->DispatchOutgoingMessage( |
|
1273 iMessage, KAudioServiceUid ); |
|
1274 |
|
1275 if ( err == KErrNone ) |
|
1276 { |
|
1277 // Ownership of iMessage has been transferred |
|
1278 iMessage = NULL; |
|
1279 iIsBusy = EFalse; |
|
1280 iDispatcher->RemoveMemoryObserver( this ); |
|
1281 } |
|
1282 |
|
1283 else if ( err == KErrNoMemory ) |
|
1284 { |
|
1285 // Keep retrying. |
|
1286 } |
|
1287 |
|
1288 else // Give up on sending |
|
1289 { |
|
1290 delete iMessage; |
|
1291 iMessage = NULL; |
|
1292 iIsBusy = EFalse; |
|
1293 iDispatcher->RemoveMemoryObserver( this ); |
|
1294 } |
|
1295 } |
|
1296 |
|
1297 else |
|
1298 { |
|
1299 TInt err = iDispatcher->DispatchOutgoingErrorMessage( |
|
1300 iErrorCode, *iMessage, KAudioServiceUid ); |
|
1301 |
|
1302 // If it was success or some other error than KErrNoMemory |
|
1303 // we are done sending or trying to send this message. |
|
1304 if ( err != KErrNoMemory ) |
|
1305 { |
|
1306 delete iMessage; |
|
1307 iMessage = NULL; |
|
1308 iIsBusy = EFalse; |
|
1309 iDispatcher->RemoveMemoryObserver( this ); |
|
1310 } |
|
1311 |
|
1312 else |
|
1313 { |
|
1314 // Keep retrying. |
|
1315 } |
|
1316 } |
|
1317 } |
|
1318 } |
|
1319 } |
|
1320 |
|
1321 |
|
1322 // ----------------------------------------------------------------------------- |
|
1323 // CHtiAudioServicePlugin::IsBusy |
|
1324 // ----------------------------------------------------------------------------- |
|
1325 // |
|
1326 TBool CHtiAudioServicePlugin::IsBusy() |
|
1327 { |
|
1328 return iIsBusy; |
|
1329 } |
|
1330 |
|
1331 |
|
1332 // ----------------------------------------------------------------------------- |
|
1333 // CHtiAudioServicePlugin::SendResponseMsg |
|
1334 // Sends a message out to the message dispatcher. |
|
1335 // ----------------------------------------------------------------------------- |
|
1336 // |
|
1337 TInt CHtiAudioServicePlugin::SendResponseMsg( const TDesC8& aMsg, |
|
1338 const TUint8 aCommandId ) |
|
1339 { |
|
1340 HTI_LOG_FUNC_IN( "CHtiAudioServicePlugin::SendResponseMsg" ); |
|
1341 |
|
1342 iErrorCode = 0; |
|
1343 |
|
1344 if ( iDispatcher == NULL ) |
|
1345 { |
|
1346 iIsBusy = EFalse; |
|
1347 return KErrGeneral; |
|
1348 } |
|
1349 |
|
1350 iDispatcher->RemoveMemoryObserver( this ); |
|
1351 |
|
1352 delete iMessage; |
|
1353 iMessage = NULL; |
|
1354 iMessage = HBufC8::New( aMsg.Length() + 1 ); |
|
1355 |
|
1356 if ( iMessage == NULL ) |
|
1357 { |
|
1358 iIsBusy = EFalse; |
|
1359 return KErrNoMemory; |
|
1360 } |
|
1361 |
|
1362 TPtr8 ptr8 = iMessage->Des(); |
|
1363 if ( aCommandId != 0 ) |
|
1364 { |
|
1365 ptr8.Append( aCommandId ); |
|
1366 } |
|
1367 else |
|
1368 { |
|
1369 ptr8.Append( iCommandId ); |
|
1370 } |
|
1371 |
|
1372 ptr8.Append( aMsg ); |
|
1373 |
|
1374 TInt err = KErrNone; |
|
1375 |
|
1376 err = iDispatcher->DispatchOutgoingMessage( iMessage, KAudioServiceUid ); |
|
1377 |
|
1378 if ( err == KErrNoMemory ) |
|
1379 { |
|
1380 HTI_LOG_TEXT( "Message queue memory full - waiting" ); |
|
1381 iIsBusy = ETrue; // Should already be true, but just in case |
|
1382 iDispatcher->AddMemoryObserver( this ); |
|
1383 // For the caller of this method all is OK, sending is just delayed |
|
1384 err = KErrNone; |
|
1385 } |
|
1386 |
|
1387 else if ( err == KErrNone ) |
|
1388 { |
|
1389 HTI_LOG_TEXT( "Message sent to dispatcher" ); |
|
1390 iMessage = NULL; // Ownership of iMessage has been transferred |
|
1391 iIsBusy = EFalse; |
|
1392 } |
|
1393 |
|
1394 else // give up on sending |
|
1395 { |
|
1396 HTI_LOG_FORMAT( "Other dispatcher error %d", err ); |
|
1397 delete iMessage; |
|
1398 iMessage = NULL; |
|
1399 iIsBusy = EFalse; |
|
1400 } |
|
1401 |
|
1402 HTI_LOG_FUNC_OUT( "CHtiAudioServicePlugin::SendResponseMsg" ); |
|
1403 return err; |
|
1404 } |
|
1405 |
|
1406 |
|
1407 // ----------------------------------------------------------------------------- |
|
1408 // CHtiAudioServicePlugin::SendErrorResponseMsg |
|
1409 // Sends an error message out to the message dispatcher. |
|
1410 // ----------------------------------------------------------------------------- |
|
1411 // |
|
1412 TInt CHtiAudioServicePlugin::SendErrorResponseMsg( TInt aErrorCode, |
|
1413 const TDesC8& aErrorDescription, |
|
1414 const TUint8 aCommandId ) |
|
1415 { |
|
1416 HTI_LOG_FUNC_IN( "CHtiAudioServicePlugin::SendErrorResponseMsg" ); |
|
1417 |
|
1418 iErrorCode = aErrorCode; |
|
1419 |
|
1420 if ( iDispatcher == NULL ) |
|
1421 { |
|
1422 iIsBusy = EFalse; |
|
1423 return KErrGeneral; |
|
1424 } |
|
1425 |
|
1426 iDispatcher->RemoveMemoryObserver( this ); |
|
1427 |
|
1428 delete iMessage; |
|
1429 iMessage = NULL; |
|
1430 iMessage = HBufC8::New( aErrorDescription.Length() + 1 ); |
|
1431 |
|
1432 if ( iMessage == NULL ) |
|
1433 { |
|
1434 iIsBusy = EFalse; |
|
1435 return KErrNoMemory; |
|
1436 } |
|
1437 |
|
1438 TPtr8 ptr8 = iMessage->Des(); |
|
1439 if ( aCommandId != 0 ) |
|
1440 { |
|
1441 ptr8.Append( aCommandId ); |
|
1442 } |
|
1443 else |
|
1444 { |
|
1445 ptr8.Append( iCommandId ); |
|
1446 } |
|
1447 |
|
1448 ptr8.Append( aErrorDescription ); |
|
1449 |
|
1450 TInt err = KErrNone; |
|
1451 |
|
1452 err = iDispatcher->DispatchOutgoingErrorMessage( |
|
1453 aErrorCode, *iMessage, KAudioServiceUid ); |
|
1454 |
|
1455 if ( err == KErrNoMemory ) |
|
1456 { |
|
1457 HTI_LOG_TEXT( "Message queue memory full - waiting" ); |
|
1458 iIsBusy = ETrue; // Should already be true, but just in case |
|
1459 iDispatcher->AddMemoryObserver( this ); |
|
1460 // For the caller of this method all is OK, sending is just delayed |
|
1461 err = KErrNone; |
|
1462 } |
|
1463 |
|
1464 else if ( err == KErrNone ) |
|
1465 { |
|
1466 HTI_LOG_TEXT( "Error message sent to dispatcher" ); |
|
1467 delete iMessage; |
|
1468 iMessage = NULL; |
|
1469 iIsBusy = EFalse; |
|
1470 } |
|
1471 |
|
1472 else // give up on sending |
|
1473 { |
|
1474 HTI_LOG_FORMAT( "Other dispatcher error %d", err ); |
|
1475 delete iMessage; |
|
1476 iMessage = NULL; |
|
1477 iIsBusy = EFalse; |
|
1478 } |
|
1479 |
|
1480 HTI_LOG_FUNC_OUT( "CHtiAudioServicePlugin::SendErrorResponseMsg" ); |
|
1481 return err; |
|
1482 } |
|
1483 |
|
1484 |
|
1485 // ----------------------------------------------------------------------------- |
|
1486 // CHtiAudioServicePlugin::ParseString() |
|
1487 // ----------------------------------------------------------------------------- |
|
1488 // |
|
1489 TInt CHtiAudioServicePlugin::ParseString( const TDesC8& aRequest, |
|
1490 TInt aOffset, |
|
1491 TDes& aResult ) |
|
1492 { |
|
1493 HTI_LOG_FUNC_IN( "CHtiAudioServicePlugin::ParseString" ); |
|
1494 |
|
1495 // If offset outside the string return empty string |
|
1496 if ( aOffset >= aRequest.Size() ) |
|
1497 { |
|
1498 return aOffset; |
|
1499 } |
|
1500 |
|
1501 TInt length = aRequest[aOffset]; |
|
1502 HTI_LOG_FORMAT( "String length = %d", length ); |
|
1503 |
|
1504 // If length is zero return empty string |
|
1505 if ( length < 1 ) |
|
1506 { |
|
1507 return aOffset + 1; |
|
1508 } |
|
1509 |
|
1510 if ( length > aResult.MaxLength() ) |
|
1511 { |
|
1512 return KErrBadDescriptor; |
|
1513 } |
|
1514 |
|
1515 TInt nextOffset = length + aOffset + 1; |
|
1516 HTI_LOG_FORMAT( "Next offset = %d", nextOffset ); |
|
1517 HTI_LOG_FORMAT( "Request size = %d", aRequest.Size() ); |
|
1518 |
|
1519 if ( nextOffset > aRequest.Size() ) |
|
1520 { |
|
1521 return KErrArgument; |
|
1522 } |
|
1523 |
|
1524 aResult.Copy( aRequest.Mid( aOffset + 1, length ) ); |
|
1525 |
|
1526 HTI_LOG_FUNC_OUT( "CHtiAudioServicePlugin::ParseString" ); |
|
1527 return nextOffset; |
|
1528 } |
|
1529 |
|
1530 |
|
1531 // ----------------------------------------------------------------------------- |
|
1532 // CHtiAudioServicePlugin::AddSubdirsRecursivelyL |
|
1533 // Scan all subdirectories from the given path and add them to the aArray. |
|
1534 // ----------------------------------------------------------------------------- |
|
1535 // |
|
1536 void CHtiAudioServicePlugin::AddSubdirsRecursivelyL( const TDesC& aPath, |
|
1537 CDesCArraySeg& aArray, |
|
1538 RFs& aFs ) |
|
1539 { |
|
1540 HTI_LOG_FUNC_IN( "CHtiAudioServicePlugin::AddSubdirsRecursivelyL" ); |
|
1541 |
|
1542 CDirScan* dirScan = CDirScan::NewL( aFs ); |
|
1543 CleanupStack::PushL( dirScan ); |
|
1544 |
|
1545 TFileName* path = new (ELeave) TFileName; |
|
1546 CleanupStack::PushL( path ); |
|
1547 |
|
1548 CDir* directory = NULL; |
|
1549 TPtrC currentPath; |
|
1550 |
|
1551 dirScan->SetScanDataL( aPath, KEntryAttMatchExclusive | KEntryAttDir, ESortNone ); |
|
1552 dirScan->NextL( directory ); |
|
1553 |
|
1554 while ( directory ) |
|
1555 { |
|
1556 CleanupStack::PushL( directory ); |
|
1557 currentPath.Set( dirScan->FullPath() ); |
|
1558 |
|
1559 TInt dirCount( directory->Count() ); |
|
1560 for ( TInt i = 0; i < dirCount ; ++i ) |
|
1561 { |
|
1562 path->Copy( currentPath ); |
|
1563 path->Append( ( *directory )[ i ].iName ); |
|
1564 path->Append( KBackslash ); |
|
1565 aArray.AppendL( *path ); |
|
1566 } |
|
1567 |
|
1568 CleanupStack::PopAndDestroy( directory ); |
|
1569 directory = NULL; |
|
1570 |
|
1571 dirScan->NextL( directory ); |
|
1572 } |
|
1573 |
|
1574 CleanupStack::PopAndDestroy( 2 ); // path, dirScan |
|
1575 |
|
1576 HTI_LOG_FUNC_OUT( "CHtiAudioServicePlugin::AddSubdirsRecursivelyL" ); |
|
1577 } |
|
1578 |
|
1579 |
|
1580 // ----------------------------------------------------------------------------- |
|
1581 // CHtiAudioServicePlugin::MatchMimeTypeL |
|
1582 // Check if the MIME type of the given file matches to the given pattern. |
|
1583 // ----------------------------------------------------------------------------- |
|
1584 // |
|
1585 TBool CHtiAudioServicePlugin::MatchMimeTypeL( const TDesC& aFilePath, |
|
1586 const TDesC& aMimeTypeMatchPattern ) |
|
1587 { |
|
1588 HTI_LOG_FUNC_IN( "CHtiAudioServicePlugin::MatchMimeTypeL" ); |
|
1589 |
|
1590 RApaLsSession apaSession; |
|
1591 User::LeaveIfError( apaSession.Connect() ); |
|
1592 CleanupClosePushL( apaSession ); |
|
1593 |
|
1594 TUid dummyUid( KNullUid ); |
|
1595 TDataType dataType; |
|
1596 User::LeaveIfError( apaSession.AppForDocument( aFilePath, |
|
1597 dummyUid, |
|
1598 dataType ) ); |
|
1599 CleanupStack::PopAndDestroy(); // apaSession |
|
1600 |
|
1601 if ( dataType.Des().MatchF( aMimeTypeMatchPattern ) >= 0 ) |
|
1602 { |
|
1603 HTI_LOG_TEXT( "Match" ); |
|
1604 return ETrue; |
|
1605 } |
|
1606 |
|
1607 HTI_LOG_TEXT( "Not match" ); |
|
1608 HTI_LOG_FUNC_OUT( "CHtiAudioServicePlugin::MatchMimeTypeL" ); |
|
1609 return EFalse; |
|
1610 } |
|
1611 |
|
1612 |
|
1613 // ----------------------------------------------------------------------------- |
|
1614 // CHtiAudioServicePlugin::SetAudioSettings |
|
1615 // Set the audio priority and priority preference values. |
|
1616 // ----------------------------------------------------------------------------- |
|
1617 // |
|
1618 void CHtiAudioServicePlugin::SetAudioSettings( TAudioSetting aSetting ) |
|
1619 { |
|
1620 HTI_LOG_FUNC_IN( "CHtiAudioServicePlugin::SetAudioSettings" ); |
|
1621 HTI_LOG_FORMAT( "Setting values for audio setting %d", aSetting ); |
|
1622 |
|
1623 switch ( aSetting ) |
|
1624 { |
|
1625 case EGeneralMusic: |
|
1626 { |
|
1627 iAudioPriority = KAudioPriorityRealOnePlayer; |
|
1628 iAudioPriorityPreference = |
|
1629 ( TMdaPriorityPreference ) KAudioPrefRealOneLocalPlayback; |
|
1630 break; |
|
1631 } |
|
1632 |
|
1633 case ERingTonePreview: |
|
1634 { |
|
1635 iAudioPriority = KAudioPriorityRingingTonePreview; |
|
1636 iAudioPriorityPreference = |
|
1637 ( TMdaPriorityPreference ) KAudioPrefRingFilePreview; |
|
1638 break; |
|
1639 } |
|
1640 /* |
|
1641 case EIncomingCall: |
|
1642 { |
|
1643 iAudioPriority = KAudioPriorityPhoneCall; |
|
1644 iAudioPriorityPreference = |
|
1645 ( TMdaPriorityPreference ) KAudioPrefIncomingCall; |
|
1646 break; |
|
1647 } |
|
1648 */ |
|
1649 case EDtmfString: |
|
1650 { |
|
1651 |
|
1652 iAudioPriority = KAudioPriorityDTMFString; |
|
1653 iAudioPriorityPreference = |
|
1654 ( TMdaPriorityPreference ) KAudioDTMFString; |
|
1655 break; |
|
1656 } |
|
1657 |
|
1658 default: |
|
1659 { |
|
1660 iAudioPriority = EMdaPriorityNormal; |
|
1661 iAudioPriorityPreference = EMdaPriorityPreferenceTimeAndQuality; |
|
1662 break; |
|
1663 } |
|
1664 } |
|
1665 |
|
1666 HTI_LOG_FUNC_OUT( "CHtiAudioServicePlugin::SetAudioSettings" ); |
|
1667 } |
|
1668 |
|
1669 |
|
1670 // ========================== OTHER EXPORTED FUNCTIONS ========================= |
|
1671 |
|
1672 // End of File |
|