javauis/mmapi_akn/baseline/src/cmmamidiplayer.cpp
branchRCL_3
changeset 14 04becd199f91
child 21 4376525cdefb
equal deleted inserted replaced
13:f5050f1da672 14:04becd199f91
       
     1 /*
       
     2 * Copyright (c) 2002-2007 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:  This class is used for MIDI.
       
    15 *
       
    16 */
       
    17 
       
    18 
       
    19 //  INCLUDE FILES
       
    20 #include <mmf/server/mmffile.h>
       
    21 #include <jdebug.h>
       
    22 #include <e32base.h>
       
    23 #include <AudioPreference.h>
       
    24 
       
    25 #include "cmmamidiplayer.h"
       
    26 #include "mmmadisplay.h"
       
    27 
       
    28 const TInt KErrorMessageSize = 32;
       
    29 _LIT(KErrDefaultError, "Symbian OS Error: %d");
       
    30 const TInt KMidiDefaultBank = 0x3c80;
       
    31 const TInt KMidiDrumBank = 0x3c7c;
       
    32 const TInt KMidiDrumChannel = 9;
       
    33 const TInt KMidiChannels = 16;
       
    34 const TInt KMidiDefaultInstrument = 1;
       
    35 
       
    36 CMMAMIDIPlayer* CMMAMIDIPlayer::NewLC(const TDesC& aContentType,
       
    37                                       TFileName aFileName)
       
    38 {
       
    39     CMMAMIDIPlayer* self = new(ELeave) CMMAMIDIPlayer(aFileName);
       
    40     CleanupStack::PushL(self);
       
    41     self->ConstructL(aContentType);
       
    42     return self;
       
    43 }
       
    44 
       
    45 CMMAMIDIPlayer::~CMMAMIDIPlayer()
       
    46 {
       
    47     CloseClientUtility();
       
    48     // new function added to CMMAPlayer delete the controls before the destruction of iMidi.
       
    49     DeleteControls();
       
    50     delete iMidi;
       
    51     delete iActiveSchedulerWait;
       
    52     iObservers.Close();
       
    53 
       
    54     DEBUG("MMA::CMMAMIDIPlayer::~CMMAMIDIPlayer");
       
    55 }
       
    56 
       
    57 void CMMAMIDIPlayer::ConstructL(const TDesC& aContentType)
       
    58 {
       
    59     DEBUG("CMMAMIDIPlayer::ConstructL");
       
    60     iContentType = aContentType.AllocL();
       
    61     iActiveSchedulerWait = new(ELeave)CActiveSchedulerWait;
       
    62     iMidi = CMidiClientUtility::NewL(*this, KAudioPriorityRecording,
       
    63                                      KMMAMIDIPriorityPreference, ETrue);
       
    64 
       
    65     CMMAPlayer::ConstructL();
       
    66 }
       
    67 
       
    68 CMMAMIDIPlayer::CMMAMIDIPlayer(TFileName aFileName):
       
    69         iFileName(aFileName),
       
    70         iMediaTime(KTimeUnknown), iStartedEventTime(0)
       
    71 {
       
    72 }
       
    73 
       
    74 EXPORT_C CMidiClientUtility* CMMAMIDIPlayer::MidiClient() const
       
    75 {
       
    76     return iMidi;
       
    77 }
       
    78 
       
    79 
       
    80 void CMMAMIDIPlayer::RealizeL()
       
    81 {
       
    82     DEBUG("CMMAMIDIPlayer::RealizeL");
       
    83     CMMAPlayer::RealizeL();
       
    84 }
       
    85 
       
    86 void CMMAMIDIPlayer::PrefetchL()
       
    87 {
       
    88     DEBUG_INT("CMMAMIDIPlayer::PrefetchL stream count %d", iSourceStreams.Count());
       
    89     if (iFileName != KNullDesC)
       
    90     {
       
    91         iMidi->OpenFile(iFileName);
       
    92     }
       
    93     else if (iSourceStreams.Count() == 0)
       
    94     {
       
    95         // We have no data, but need to initialize the player
       
    96         // Preparing all channels
       
    97         for (TInt i = 0; i < KMidiChannels; i++)
       
    98         {
       
    99             iMidi->SetInstrumentL(i, KMidiDefaultBank,
       
   100                                   KMidiDefaultInstrument);
       
   101         }
       
   102 
       
   103         // Setting drums to channel 10
       
   104         iMidi->SetInstrumentL(KMidiDrumChannel, KMidiDrumBank,
       
   105                               KMidiDefaultInstrument);
       
   106 
       
   107         // Start it immediately in order to get MIDIControl work without
       
   108         // calling the start. This is how reference implementation works.
       
   109         iMidi->Play();
       
   110     }
       
   111     else
       
   112     {
       
   113         // Created with content, audio player will initialize controller
       
   114         // and data source.
       
   115         iSourceStreams[ 0 ]->ReadAllL();
       
   116     }
       
   117 }
       
   118 
       
   119 //
       
   120 // This method is called when CMMASourceStreamReader finished reading
       
   121 // initiated in PrefetchL
       
   122 //
       
   123 void CMMAMIDIPlayer::ReadCompletedL(TInt aStatus, const TDesC8& aData)
       
   124 {
       
   125     if (aStatus < KErrNone)
       
   126     {
       
   127         PostActionCompleted(aStatus);
       
   128     }
       
   129     else
       
   130     {
       
   131         // we're not finished yet
       
   132         iMidi->OpenDes(aData);      //wait for MMidiClientUtilityObserver::MmcuoStateChanged
       
   133     }
       
   134 }
       
   135 
       
   136 void CMMAMIDIPlayer::DeallocateL()
       
   137 {
       
   138     DEBUG("MMA: CMMAMidiPlayer: DeallocateL +");
       
   139     if (iState == EPrefetched)
       
   140     {
       
   141         CloseClientUtility();
       
   142         ResetSourceStreams();
       
   143         ChangeState(ERealized);
       
   144     }
       
   145     DEBUG("MMA: CMMAMidiPlayer: DeallocateL -");
       
   146 }
       
   147 
       
   148 void CMMAMIDIPlayer::StartL()
       
   149 {
       
   150     iMediaTime = KTimeUnknown;
       
   151 
       
   152     // Player is already started if this player is constructed with
       
   153     // device://midi locator.
       
   154     TBool isDeviceMidi = (iSourceStreams.Count() == 0 &&
       
   155                           iFileName == KNullDesC);
       
   156     if (!isDeviceMidi)
       
   157     {
       
   158         iMidi->Play();
       
   159     }
       
   160 
       
   161     // inform java side
       
   162     PostLongEvent(CMMAPlayerEvent::EStarted, iStartedEventTime);
       
   163     ChangeState(EStarted);
       
   164 
       
   165     // To achieve similar functionality as reference implementation,
       
   166     // END_OF_MEDIA must be sent right after Start on device://midi.
       
   167     if (isDeviceMidi)
       
   168     {
       
   169         PostLongEvent(CMMAPlayerEvent::EEndOfMedia, iStartedEventTime);
       
   170         ChangeState(EPrefetched);
       
   171     }
       
   172     PostActionCompleted(KErrNone);   // java start return
       
   173 }
       
   174 
       
   175 void CMMAMIDIPlayer::StopL(TBool aPostEvent)
       
   176 {
       
   177     DEBUG("MMA: CMMAMidiPlayer::StopL");
       
   178     if (iState == EStarted)
       
   179     {
       
   180         TInt64 time;
       
   181         GetMediaTime(&time);
       
   182         iStartedEventTime = time;
       
   183 
       
   184         // do the stop only if we are playing some content
       
   185         if ((iSourceStreams.Count() > 0) ||
       
   186                 (iFileName != KNullDesC))
       
   187         {
       
   188             // should actually pause!!!
       
   189             iMidi->Stop(TTimeIntervalMicroSeconds(0));
       
   190         }
       
   191 
       
   192         if (aPostEvent)
       
   193         {
       
   194             PostLongEvent(CMMAPlayerEvent::EStopped, time);
       
   195         }
       
   196         // go back to prefetched state
       
   197         ChangeState(EPrefetched);
       
   198     }
       
   199 }
       
   200 
       
   201 void CMMAMIDIPlayer::GetDuration(TInt64* aDuration)
       
   202 {
       
   203     // Special case for device://midi
       
   204     if ((iSourceStreams.Count() == 0) &&
       
   205             (iFileName == KNullDesC))
       
   206     {
       
   207         iDuration = KErrNone;
       
   208     }
       
   209     else if (iDuration == KTimeUnknown)
       
   210     {
       
   211         TRAPD(err, iDuration = iMidi->DurationMicroSecondsL().Int64());
       
   212         if (err != KErrNone)
       
   213         {
       
   214             // Duration was not available.
       
   215             iDuration = KTimeUnknown;
       
   216         }
       
   217     }
       
   218 
       
   219     *aDuration = iDuration;
       
   220 }
       
   221 
       
   222 void CMMAMIDIPlayer::SetMediaTimeL(TInt64* aTime)
       
   223 {
       
   224     TTimeIntervalMicroSeconds position(*aTime);
       
   225     iMidi->SetPositionMicroSecondsL(position);
       
   226 
       
   227     // Reset cached media time, because actual set position may be
       
   228     // something else than aTime.
       
   229     iMediaTime = KTimeUnknown;
       
   230 
       
   231     // Inform about the position change to the StateListeners
       
   232     ChangeState(iState);
       
   233 
       
   234     // Get the actual media time
       
   235     GetMediaTime(aTime);
       
   236 
       
   237     iStartedEventTime = iMediaTime;
       
   238 }
       
   239 
       
   240 void CMMAMIDIPlayer::GetMediaTime(TInt64* aMediaTime)
       
   241 {
       
   242     // Special case for device://midi
       
   243     if ((iSourceStreams.Count() == 0) &&
       
   244             (iFileName == KNullDesC))
       
   245     {
       
   246         iMediaTime = KErrNone;
       
   247     }
       
   248     else if (iMediaTime == KTimeUnknown || iState == EStarted)
       
   249     {
       
   250         TTimeIntervalMicroSeconds position(0);
       
   251         TRAPD(error, position = iMidi->PositionMicroSecondsL());
       
   252 
       
   253         if (error == KErrNone)
       
   254         {
       
   255             TInt64 newTime = position.Int64();
       
   256 
       
   257             // Sanity check for media time going backwards or beyond the
       
   258             // duration.
       
   259             // Some native controls may return zero media time for
       
   260             // a few moments just before playback will complete.
       
   261             if (newTime < iMediaTime ||
       
   262                     (iDuration > 0 && newTime > iDuration))
       
   263             {
       
   264                 GetDuration(&iMediaTime);
       
   265             }
       
   266             else
       
   267             {
       
   268                 // set return value
       
   269                 iMediaTime = newTime;
       
   270             }
       
   271         }
       
   272         else
       
   273         {
       
   274             // media time cannot be get
       
   275             iMediaTime = KTimeUnknown;
       
   276         }
       
   277     }
       
   278     *aMediaTime = iMediaTime;
       
   279 }
       
   280 
       
   281 void CMMAMIDIPlayer::ReInitializeMidiEngineL(const TDesC8* aMidiSequence)
       
   282 {
       
   283     DEBUG("MMA: CMMAMIDIPlayer: ReInitializeMidiEngineL: + ");
       
   284 
       
   285     CloseClientUtility();
       
   286 
       
   287     DEBUG("MMA: CMMAMidiPlayer: ReInitializeMidiEngineL: Opening descriptor");
       
   288 
       
   289     iMidi->OpenDes(*aMidiSequence);
       
   290     // Wait until asynchronous OpenDes call has completed
       
   291     if (!iActiveSchedulerWait->IsStarted())
       
   292     {
       
   293         iActiveSchedulerWait->Start();
       
   294     }
       
   295     ChangeState(EPrefetched);
       
   296     DEBUG("MMA: CMMAMIDIPlayer: ReInitializeMidiEngineL: - ");
       
   297 }
       
   298 
       
   299 void CMMAMIDIPlayer::addObserverL(MMidiClientUtilityObserver* aObserver)
       
   300 {
       
   301     iObservers.AppendL(aObserver);
       
   302 }
       
   303 
       
   304 void CMMAMIDIPlayer::CloseL()
       
   305 {
       
   306     CMMAPlayer::CloseL();
       
   307     CloseClientUtility();
       
   308 }
       
   309 
       
   310 const TDesC& CMMAMIDIPlayer::Type()
       
   311 {
       
   312     return KMMAMIDIPlayer;
       
   313 }
       
   314 
       
   315 void CMMAMIDIPlayer::PlayCompleteL(TInt aError)
       
   316 {
       
   317     DEBUG("MMA: CMMAMidiPlayer: PlayCompleteL +");
       
   318     DEBUG_INT("MMA: CMMAMidiPlayer: PlayCompleteL: Error=%d", aError);
       
   319     TInt64 duration;
       
   320     GetDuration(&duration);
       
   321     iMediaTime = duration;
       
   322     iStartedEventTime = 0;
       
   323 
       
   324     iMidi->Stop(TTimeIntervalMicroSeconds(0));
       
   325 
       
   326     // go back to prefetched state
       
   327     ChangeState(EPrefetched);   // ready to play again
       
   328 
       
   329     // Send 'Stopped' only when stop() is called.
       
   330     PostLongEvent(CMMAPlayerEvent::EEndOfMedia, duration);
       
   331 
       
   332     if (aError == KErrNone)
       
   333     {
       
   334         iRepeatCount++;
       
   335 
       
   336         if (iRepeatForever || iRepeatCount < iRepeatNumberOfTimes)
       
   337         {
       
   338             StartL();
       
   339         }
       
   340         else
       
   341         {
       
   342             iRepeatCount = 0;
       
   343         }
       
   344     }
       
   345     else
       
   346     {
       
   347         // error has occured, setting correct number of
       
   348         // repeats for next start
       
   349         SetLoopCount(iRepeatNumberOfTimes);
       
   350     }
       
   351     DEBUG("MMA: CMMAMidiPlayer: PlayCompleteL -");
       
   352 }
       
   353 
       
   354 void CMMAMIDIPlayer::MmcuoStateChanged(TMidiState aOldState,
       
   355                                        TMidiState aNewState,
       
   356                                        const TTimeIntervalMicroSeconds& aTime,
       
   357                                        TInt aError)
       
   358 {
       
   359     TInt err = aError;
       
   360 
       
   361     DEBUG_INT3("MMA: CMMAMIDIPlayer: MmcuoStateChanged: Old=%d, New=%d, Error=%d",
       
   362                aOldState,
       
   363                aNewState,
       
   364                err);
       
   365     // Closing the utility or reinitialising
       
   366 #ifdef RD_JAVA_TMIDISTATECHANGE
       
   367     if (iActiveSchedulerWait->IsStarted() &&
       
   368             ((aNewState == EMidiStateClosedDisengaged) ||
       
   369              (aNewState == EMidiStateOpenDisengaged) ||
       
   370              (aNewState == EMidiStateClosedEngaged)))
       
   371 #else
       
   372     if (iActiveSchedulerWait->IsStarted() &&
       
   373             ((aNewState == EClosedDisengaged) ||
       
   374              (aNewState == EOpenDisengaged) ||
       
   375              (aNewState == EClosedEngaged)))
       
   376 #endif
       
   377     {
       
   378         iActiveSchedulerWait->AsyncStop();
       
   379     }
       
   380     // changing from realized to prefetched state
       
   381 #ifdef RD_JAVA_TMIDISTATECHANGE
       
   382     else if ((iState == ERealized) &&
       
   383              (aOldState == EMidiStateClosedDisengaged) &&
       
   384              ((aNewState == EMidiStateOpenDisengaged) ||
       
   385               (aNewState == EMidiStateClosedEngaged) ||
       
   386               (aNewState == EMidiStateClosedDisengaged)))
       
   387 #else
       
   388     else if ((iState == ERealized) &&
       
   389              (aOldState == EClosed) &&
       
   390              ((aNewState == EOpen) || (aNewState == EClosedEngaged) ||
       
   391               (aNewState == EClosed)))     // EClosed is EClosedDisengaged
       
   392 #endif
       
   393     {
       
   394         if (aError == KErrNone)
       
   395         {
       
   396             // prefetch succeed
       
   397             ChangeState(EPrefetched);
       
   398         }
       
   399         // else state remains realized
       
   400 
       
   401         // inform java
       
   402         PostActionCompleted(aError);
       
   403         err = KErrNone; // don't report the error again
       
   404     }
       
   405 #ifdef RD_JAVA_TMIDISTATECHANGE
       
   406     else if ((aOldState == EMidiStateOpenPlaying) &&
       
   407              (aNewState == EMidiStateOpenEngaged) &&
       
   408              (iState == EStarted))
       
   409 #else
       
   410     else if ((aOldState == EPlaying) &&
       
   411              (aNewState == EOpenEngaged) &&
       
   412              (iState == EStarted))
       
   413 #endif
       
   414     {
       
   415         // If iState is not EStarted PlayCompleteL may not be called because
       
   416         // player may be already stopped.
       
   417 
       
   418         // playing completed
       
   419         TRAPD(playErr, PlayCompleteL(aError));
       
   420         if (playErr != KErrNone)
       
   421         {
       
   422             err = playErr;
       
   423         }
       
   424     }
       
   425 
       
   426     if (err != KErrNone)
       
   427     {
       
   428         TBuf<KErrorMessageSize> errorMessage;
       
   429         errorMessage.Format(KErrDefaultError, err);
       
   430         PostStringEvent(CMMAPlayerEvent::EError, errorMessage);
       
   431         if (iActiveSchedulerWait->IsStarted())
       
   432         {
       
   433             iActiveSchedulerWait->AsyncStop();
       
   434         }
       
   435     }
       
   436 
       
   437     // notify observers
       
   438     TInt count = iObservers.Count();
       
   439     for (TInt i = 0; i < count; i++)
       
   440     {
       
   441         iObservers[ i ]->MmcuoStateChanged(aOldState, aNewState, aTime, aError);
       
   442     }
       
   443 
       
   444     DEBUG_INT("MMA: CMMAMIDIPlayer: MmcuoStateChanged: midi state %d",
       
   445               iMidi->State());
       
   446 }
       
   447 
       
   448 void CMMAMIDIPlayer::MmcuoTempoChanged(TInt aMicroBeatsPerMinute)
       
   449 {
       
   450     // notify observers
       
   451     TInt count = iObservers.Count();
       
   452     for (TInt i = 0; i < count; i++)
       
   453     {
       
   454         iObservers[ i ]->MmcuoTempoChanged(aMicroBeatsPerMinute);
       
   455     }
       
   456 }
       
   457 
       
   458 void CMMAMIDIPlayer::MmcuoVolumeChanged(TInt aChannel,TReal32 aVolumeInDecibels)
       
   459 {
       
   460     // notify observers
       
   461     TInt count = iObservers.Count();
       
   462     for (TInt i = 0; i < count; i++)
       
   463     {
       
   464         iObservers[ i ]->MmcuoVolumeChanged(aChannel, aVolumeInDecibels);
       
   465     }
       
   466 }
       
   467 
       
   468 void CMMAMIDIPlayer::MmcuoMuteChanged(TInt aChannel,TBool aMuted)
       
   469 {
       
   470     // notify observers
       
   471     TInt count = iObservers.Count();
       
   472     for (TInt i = 0; i < count; i++)
       
   473     {
       
   474         iObservers[ i ]->MmcuoMuteChanged(aChannel, aMuted);
       
   475     }
       
   476 }
       
   477 
       
   478 void CMMAMIDIPlayer::MmcuoSyncUpdate(const TTimeIntervalMicroSeconds& aMicroSeconds,TInt64 aMicroBeats)
       
   479 {
       
   480     // notify observers
       
   481     TInt count = iObservers.Count();
       
   482     for (TInt i = 0; i < count; i++)
       
   483     {
       
   484         iObservers[ i ]->MmcuoSyncUpdate(aMicroSeconds, aMicroBeats);
       
   485     }
       
   486 }
       
   487 
       
   488 void CMMAMIDIPlayer::MmcuoMetaDataEntryFound(const TInt aMetaDataEntryId,const TTimeIntervalMicroSeconds& aPosition)
       
   489 {
       
   490     // notify observers
       
   491     TInt count = iObservers.Count();
       
   492     for (TInt i = 0; i < count; i++)
       
   493     {
       
   494         iObservers[ i ]->MmcuoMetaDataEntryFound(aMetaDataEntryId, aPosition);
       
   495     }
       
   496 }
       
   497 
       
   498 void CMMAMIDIPlayer::MmcuoMipMessageReceived(const RArray<TMipMessageEntry>& aMessage)
       
   499 {
       
   500     // notify observers
       
   501     TInt count = iObservers.Count();
       
   502     for (TInt i = 0; i < count; i++)
       
   503     {
       
   504         iObservers[ i ]->MmcuoMipMessageReceived(aMessage);
       
   505     }
       
   506 }
       
   507 
       
   508 void CMMAMIDIPlayer::MmcuoPolyphonyChanged(TInt aNewPolyphony)
       
   509 {
       
   510     // notify observers
       
   511     TInt count = iObservers.Count();
       
   512     for (TInt i = 0; i < count; i++)
       
   513     {
       
   514         iObservers[ i ]->MmcuoPolyphonyChanged(aNewPolyphony);
       
   515     }
       
   516 }
       
   517 
       
   518 void CMMAMIDIPlayer::MmcuoInstrumentChanged(TInt aChannel,TInt aBankId,TInt aInstrumentId)
       
   519 {
       
   520     // notify observers
       
   521     TInt count = iObservers.Count();
       
   522     for (TInt i = 0; i < count; i++)
       
   523     {
       
   524         iObservers[ i ]->MmcuoInstrumentChanged(aChannel, aBankId, aInstrumentId);
       
   525     }
       
   526 }
       
   527 
       
   528 void CMMAMIDIPlayer::CloseClientUtility()
       
   529 {
       
   530     DEBUG("MMA: CMMAMidiPlayer: CloseClientUtility +");
       
   531     if (iMidi &&
       
   532             iActiveSchedulerWait &&
       
   533 #ifdef RD_JAVA_TMIDISTATECHANGE
       
   534             (iMidi->State() != EMidiStateClosedDisengaged))
       
   535 #else
       
   536             (iMidi->State() != EClosed))
       
   537 #endif
       
   538     {
       
   539         // Have to stop midi before closing,
       
   540         // this case is for device://midi
       
   541 #ifdef RD_JAVA_TMIDISTATECHANGE
       
   542         if (iMidi->State() == EMidiStateClosedEngaged)
       
   543 #else
       
   544         if (iMidi->State() == EClosedEngaged)
       
   545 #endif
       
   546         {
       
   547             iMidi->Stop(TTimeIntervalMicroSeconds(0));
       
   548             if (!iActiveSchedulerWait->IsStarted())
       
   549             {
       
   550                 iActiveSchedulerWait->Start();
       
   551             }
       
   552         }
       
   553         else
       
   554         {
       
   555             // Calling Close and Stop or Stop and Close should
       
   556             // always lead to EClosed state.
       
   557 
       
   558             // From EOpenEngaged or EOpenPlaying to EClosedEngaged
       
   559             // or from EOpenDisengaged to EClosedDisengaged
       
   560             iMidi->Close();
       
   561             if (!iActiveSchedulerWait->IsStarted())
       
   562             {
       
   563                 iActiveSchedulerWait->Start();
       
   564             }
       
   565 
       
   566             DEBUG_INT("State after Close: %d", iMidi->State());
       
   567 
       
   568             // If not in EClosedDisengaged yet
       
   569 #ifdef RD_JAVA_TMIDISTATECHANGE
       
   570             if (iMidi->State() != EMidiStateClosedDisengaged)
       
   571 #else
       
   572             if (iMidi->State() != EClosed)
       
   573 #endif
       
   574             {
       
   575                 // From EClosedEngaged to EClosedDisengaged
       
   576                 iMidi->Stop(TTimeIntervalMicroSeconds(0));
       
   577                 if (!iActiveSchedulerWait->IsStarted())
       
   578                 {
       
   579                     iActiveSchedulerWait->Start();
       
   580                 }
       
   581                 DEBUG_INT("State after Stop: %d", iMidi->State());
       
   582             }
       
   583         }
       
   584         DEBUG_INT("MMA: CMMAMidiPlayer: CloseClientUtility: State after close: %d", iMidi->State());
       
   585     }
       
   586     DEBUG("MMA: CMMAMidiPlayer: CloseClientUtility -");
       
   587 }
       
   588 
       
   589 //  END OF FILE