epoc-music.cpp

Go to the documentation of this file.
00001 /*
00002  !concept {:name => "Tracking music playback on Symbian"}
00003 */
00004 
00005 /** 
00006 
00007   Music artist and track logging
00008 
00009   References:
00010 
00011   Sample source code for example in the [Music Player Remote https://www.iyouit.eu/portal/Software.aspx] PyS60 extension.
00012 
00013   As for APIs, see the [Media Player Engine API http://wiki.forum.nokia.com/index.php/Media_Player_Engine_API]. Related, possibly interesting API: [Audio Metadata Reader API http://wiki.forum.nokia.com/index.php/Audio_Metadata_Reader_API]. [Music Player Remote Control API http://wiki.forum.nokia.com/index.php/Music_Player_Remote_Control_API] sounds highly related despite the name, but do note [this http://wiki.forum.nokia.com/index.php/KIS001005_-_Music_Player_Remote_Control_API_does_not_work_in_all_S60_3rd_Edition%2C_FP1_and_FP2_devices] issue. For more recent devices see [MPX Playback Utility API http://wiki.forum.nokia.com/index.php/TSS001652_-_Using_MPX_Playback_Utility_API].
00014 
00015  */
00016 
00017 #include "epoc-music.hpp"
00018 
00019 #include "er_errors.h"
00020 #include "ld_logging.h"
00021 #include "sa_sensor_list_log_db.h"
00022 #include "utils_cl2.h"
00023 
00024 NONSHARABLE_CLASS(MyTrackInfo)
00025 {
00026  public:
00027   MyTrackInfo() : iUrl(0), iArtist(0), iTitle(0), iAlbum(0) {}
00028 
00029   HBufC8* iUrl; // owned
00030   HBufC8* iArtist; // owned
00031   HBufC8* iTitle; // owned
00032   HBufC8* iAlbum; // owned
00033 
00034   void Reset() {
00035     DELETE_Z(iUrl);
00036     DELETE_Z(iTitle);
00037     DELETE_Z(iArtist);
00038     DELETE_Z(iAlbum);
00039   }
00040 
00041   ~MyTrackInfo() { Reset(); }
00042 
00043   int Cmp(const MyTrackInfo& another) const;
00044 };
00045 
00046 static int CmpHBufC8(const HBufC8* a, const HBufC8* b)
00047 {
00048   if (!a && !b) return 0;
00049   if (a && !b) return 1;
00050   if (!a && b) return -1;
00051   return (*a).Compare(*b);
00052 }
00053 
00054 int MyTrackInfo::Cmp(const MyTrackInfo& another) const
00055 {
00056   return CmpHBufC8(iUrl, another.iUrl) ||
00057     CmpHBufC8(iTitle, another.iTitle) ||
00058     CmpHBufC8(iArtist, another.iArtist) ||
00059     CmpHBufC8(iAlbum, another.iAlbum);
00060 }
00061 
00062 CSensor_music* CSensor_music::NewLC(ac_AppContext* aAppContext)
00063 {
00064   CSensor_music* obj = new (ELeave) CSensor_music(aAppContext);
00065   CleanupStack::PushL(obj);
00066   obj->ConstructL();
00067   return obj;
00068 }
00069 
00070 CSensor_music* CSensor_music::NewL(ac_AppContext* aAppContext)
00071 {
00072   CSensor_music* obj = CSensor_music::NewLC(aAppContext);
00073   CleanupStack::Pop(obj);
00074   return obj;
00075 }
00076 
00077 CSensor_music::~CSensor_music()
00078 {
00079   CloseSession();
00080   delete iTrackInfo;
00081   delete iOldTrackInfo;
00082 }
00083 
00084 void CSensor_music::CloseSession()
00085 {
00086   if (iPlaybackUtility) {
00087     //TRAP_IGNORE(iPlaybackUtility->CommandL(EPbCmdClose));
00088     //iPlaybackUtility->CancelRequest();
00089     iPlaybackUtility->Close();
00090     // We appear to be getting USER 42 if we delete iPlaybackUtility.
00091     // Hopefully Close() is enough.
00092     //delete iPlaybackUtility; // not
00093     iPlaybackUtility = NULL;
00094   }
00095 }
00096 
00097 CSensor_music::CSensor_music(ac_AppContext* aAppContext) :
00098   iAppContext(aAppContext)
00099 {
00100 }
00101 
00102 void CSensor_music::ConstructL()
00103 {
00104   iTrackInfo = new (ELeave) MyTrackInfo;
00105   iOldTrackInfo = new (ELeave) MyTrackInfo;
00106 
00107   iPlaybackUtility = MMPXPlaybackUtility::NewL(KPbModeActivePlayer, this);
00108 
00109   if ((iPlaybackUtility->StateL() != EPbStateNotInitialised) &&
00110       (iPlaybackUtility->StateL() != EPbStateInitialising)) {
00111     // Request info for anything already playing.
00112     RequestMediaL();
00113   }
00114 }
00115 
00116 #include <mpxmessagegeneraldefs.h>
00117 #include <mpxplaybackmessage.h>
00118 
00119 #include <glib/gprintf.h>
00120 
00121 void CSensor_music::HandlePlaybackMessage(CMPXMessage* aMessage, TInt errCode)
00122 {
00123   if (errCode) {
00124     guilogf("music: error %d", errCode);
00125     er_log_symbian(0, errCode, "INACTIVATE: music data fetch error");
00126     CloseSession();
00127     return;
00128   }
00129 
00130   int eventId = -1; // logged
00131   const char* detail = NULL; // logged
00132 #define DETAILBUFSIZE 50
00133   char detailBuf[DETAILBUFSIZE];
00134 #define DETAILFMT(f...) g_snprintf(detailBuf, DETAILBUFSIZE, f)
00135 
00136   TMPXMessageId msgId(*aMessage->Value<TMPXMessageId>(KMPXMessageGeneralId));
00137   //guilogf("music: message ID %d", msgId);
00138   if (msgId != KMPXMessageGeneral) {
00139     return;
00140   } else {
00141      eventId = aMessage->ValueTObjectL<TInt>(KMPXMessageGeneralEvent);
00142      //guilogf("music: event ID %d", eventId);
00143      switch (eventId)
00144        {
00145        case TMPXPlaybackMessage::ENoEvent:
00146    {
00147      guilogf("music: ENoEvent");
00148      break;
00149    }
00150        case TMPXPlaybackMessage::EError:
00151    {
00152      guilogf("music: EError");
00153      break;
00154    }
00155        case TMPXPlaybackMessage::ECommandReceived: /* seen often */
00156    {
00157      //guilogf("music: ECommandReceived");
00158      break;
00159    }
00160        case TMPXPlaybackMessage::ECommandComplete:
00161    {
00162      guilogf("music: ECommandComplete");
00163      break;
00164    }
00165        case TMPXPlaybackMessage::EPropertyChanged: /* seen often */
00166    {
00167      TMPXPlaybackProperty propId(aMessage->ValueTObjectL<TMPXPlaybackProperty>(KMPXMessageGeneralType));
00168      TInt value(aMessage->ValueTObjectL<TInt>(KMPXMessageGeneralData));
00169      const char* propStr;
00170      switch (propId)
00171        {
00172        case EPbPropertyVolume:
00173          {
00174      propStr = "EPbPropertyVolume";
00175      break;
00176          }
00177        case EPbPropertyMaxVolume:
00178          {
00179      propStr = "EPbPropertyMaxVolume";
00180      break;
00181          }
00182        case EPbPropertyVolumeRamp:
00183          {
00184      propStr = "EPbPropertyVolumeRamp";
00185      break;
00186          }
00187        case EPbPropertyMute:
00188          {
00189      propStr = "EPbPropertyMute";
00190      break;
00191          }
00192        case EPbPropertyBalance:
00193          {
00194      propStr = "EPbPropertyBalance";
00195      break;
00196          }
00197        case EPbPropertyEmbeddedMode:
00198          {
00199      propStr = "EPbPropertyEmbeddedMode";
00200      break;
00201          }
00202        case EPbPropertyCrossFade:
00203          {
00204      propStr = "EPbPropertyCrossFade";
00205      break;
00206          }
00207        case EPbPropertyRandomMode:
00208          {
00209      propStr = "EPbPropertyRandomMode";
00210      break;
00211          }
00212        case EPbPropertyRepeatMode:
00213          {
00214      propStr = "EPbPropertyRepeatMode";
00215      break;
00216          }
00217        case EPbPropertyAccessPoint:
00218          {
00219      propStr = "EPbPropertyAccessPoint";
00220      break;
00221          }
00222        case EPbPropertyPosition:
00223          {
00224 #if 0
00225      propStr = "EPbPropertyPosition";
00226      break;
00227 #else
00228                  // These events are very frequent as track position
00229                  // progress is frequently reported.
00230      return;
00231 #endif
00232          }
00233        case EPbPropertyDuration:
00234          {
00235      propStr = "EPbPropertyDuration";
00236      break;
00237          }
00238        case EPbPropertySongValid:
00239          {
00240      propStr = "EPbPropertySongValid";
00241      break;
00242          }
00243        case EPbPropertyRemote:
00244          {
00245      propStr = "EPbPropertyRemote";
00246      break;
00247          }
00248        case EPbPropertySupportedFeatures:
00249          {
00250      propStr = "EPbPropertySupportedFeatures";
00251      break;
00252          }
00253        default:
00254          {
00255            propStr = "<unknown>";
00256            break;
00257          }
00258        }
00259      DETAILFMT("{property: %d, value: %d}", propId, value);
00260      detail = detailBuf;
00261      guilogf("music: EPropertyChanged: '%s' = %d", propStr, value);
00262      break;
00263    }
00264        case TMPXPlaybackMessage::EStateChanged: /* seen often */
00265    {
00266      TInt stateId(aMessage->ValueTObjectL<TMPXPlaybackState>(KMPXMessageGeneralType));
00267      if (stateId == iOldPbState)
00268        // We seem to get repeats, avoid that.
00269        return;
00270      iOldPbState = stateId;
00271      const char* stateStr;
00272      switch (stateId)
00273        {
00274        case EPbStateNotInitialised:
00275          {
00276      stateStr = "EPbStateNotInitialised";
00277      break;
00278          }
00279        case EPbStateInitialising:
00280          {
00281      stateStr = "EPbStateInitialising";
00282      break;
00283          }
00284        case EPbStatePlaying:
00285          {
00286      stateStr = "EPbStatePlaying";
00287      break;
00288          }
00289        case EPbStatePaused:
00290          {
00291      stateStr = "EPbStatePaused";
00292      break;
00293          }
00294        case EPbStateStopped:
00295          {
00296      stateStr = "EPbStateStopped";
00297      break;
00298          }
00299        case EPbStateSeekingForward:
00300          {
00301      stateStr = "EPbStateSeekingForward";
00302      break;
00303          }
00304        case EPbStateSeekingBackward:
00305          {
00306      stateStr = "EPbStateSeekingBackward";
00307      break;
00308          }
00309        case EPbStateShuttingDown:
00310          {
00311      stateStr = "EPbStateShuttingDown";
00312      break;
00313          }
00314        case EPbStateBuffering:
00315          {
00316      stateStr = "EPbStateBuffering";
00317      break;
00318          }
00319        case EPbStateDownloading:
00320          {
00321      stateStr = "EPbStateDownloading";
00322      break;
00323          }
00324        default:
00325          {
00326            stateStr = "<unknown>";
00327            break;
00328          }
00329        }
00330      DETAILFMT("{state: %d}", stateId);
00331      detail = detailBuf;
00332      //guilogf("music: EStateChanged %d", stateId);
00333      guilogf("music: playback state %s (%d)", stateStr, stateId);
00334      break;
00335    }
00336        case TMPXPlaybackMessage::ESongCorrupt:
00337    {
00338      guilogf("music: ESongCorrupt");
00339      break;
00340    }
00341        case TMPXPlaybackMessage::ESongContainerChanged:
00342    {
00343      guilogf("music: ESongContainerChanged");
00344      break;
00345    }
00346        case TMPXPlaybackMessage::EInitializeComplete: /* seen often */
00347    {
00348      guilogf("music: EInitializeComplete");
00349      break;
00350    }
00351        case TMPXPlaybackMessage::ESongChanged:
00352    {
00353      guilogf("music: ESongChanged");
00354      break;
00355    }
00356        case TMPXPlaybackMessage::EPlayerChanged:
00357    {
00358      guilogf("music: EPlayerChanged");
00359      break;
00360    }
00361        case TMPXPlaybackMessage::EActivePlayerChanged:
00362    {
00363      guilogf("music: EActivePlayerChanged");
00364      break;
00365    }
00366        case TMPXPlaybackMessage::ESubPlayersChanged:
00367    {
00368      guilogf("music: ESubPlayersChanged");
00369      break;
00370    }
00371        case TMPXPlaybackMessage::EPlayerSelectionChanged:
00372    {
00373      guilogf("music: EPlayerSelectionChanged");
00374      break;
00375    }
00376        case TMPXPlaybackMessage::EDownloadStarted:
00377    {
00378      guilogf("music: EDownloadStarted");
00379      break;
00380    }
00381        case TMPXPlaybackMessage::EDownloadUpdated:
00382    {
00383      guilogf("music: EDownloadUpdated");
00384      break;
00385    }
00386        case TMPXPlaybackMessage::EDownloadComplete:
00387    {
00388      guilogf("music: EDownloadComplete");
00389      break;
00390    }
00391        case TMPXPlaybackMessage::EDownloadPositionChanged:
00392    {
00393      guilogf("music: EDownloadPositionChanged");
00394      break;
00395    }
00396        case TMPXPlaybackMessage::EDownloadStateChanged:
00397    {
00398      guilogf("music: EDownloadStateChanged");
00399      break;
00400    }
00401        case TMPXPlaybackMessage::EDownloadCmdPauseDownload:
00402    {
00403      guilogf("music: EDownloadCmdPauseDownload");
00404      break;
00405    }
00406        case TMPXPlaybackMessage::EDownloadCmdResumeDownload:
00407    {
00408      guilogf("music: EDownloadCmdResumeDownload");
00409      break;
00410    }
00411        case TMPXPlaybackMessage::EDownloadCmdCancelDownload:
00412    {
00413      guilogf("music: EDownloadCmdCancelDownload");
00414      break;
00415    }
00416        case TMPXPlaybackMessage::EAccessoryChanged:
00417    {
00418      guilogf("music: EAccessoryChanged");
00419      break;
00420    }
00421        case TMPXPlaybackMessage::EMediaChanged: /* seen often */
00422    {
00423      guilogf("music: EMediaChanged");
00424      RequestMediaL();
00425      break;
00426    }
00427        case TMPXPlaybackMessage::ESkipping:
00428    {
00429      guilogf("music: ESkipping");
00430      break;
00431    }
00432        case TMPXPlaybackMessage::ESkipEnd:
00433    {
00434      guilogf("music: ESkipEnd");
00435      break;
00436    }
00437        case TMPXPlaybackMessage::EPlayerUnavailable:
00438    {
00439      guilogf("music: EPlayerUnavailable");
00440      break;
00441    }
00442        case TMPXPlaybackMessage::EPlaylistUpdated:
00443    {
00444      guilogf("music: EPlaylistUpdated");
00445      RequestMediaL();
00446      break;
00447    }
00448        case TMPXPlaybackMessage::EReachedEndOfPlaylist:
00449    {
00450      guilogf("music: EReachedEndOfPlaylist");
00451      break;
00452    }
00453        default:
00454          {
00455      guilogf("music: unknown event");
00456            break;
00457          }
00458        }
00459 
00460      log_db_log_musicplayer(GetLogDb(), eventId, detail, NULL);
00461   }
00462 }
00463 
00464 #include <mpxmediageneraldefs.h>
00465 #include <mpxmediamusicdefs.h>
00466 
00467 // adapted from code in Music Player Remote Python extension
00468 void CSensor_music::RequestMediaL()
00469 {
00470   MMPXSource* source(iPlaybackUtility->Source());
00471   if (source)
00472     {
00473       RArray<TMPXAttribute> attrs;
00474       CleanupClosePushL(attrs);
00475       attrs.Append(KMPXMediaGeneralUri);
00476       attrs.Append(KMPXMediaGeneralTitle);
00477       attrs.Append(KMPXMediaMusicArtist);
00478       attrs.Append(KMPXMediaMusicAlbum);
00479       // The two-arg method is deprecated, we assume passing NULL as
00480       // the third arg has the same semantics. The arg is
00481       // CMPXAttributeSpecs* aSpecs (typedef CMPXMedia
00482       // CMPXAttributeSpecs).
00483       CMPXAttributeSpecs* specs = NULL;
00484       source->MediaL(attrs.Array(), *this, specs);
00485       CleanupStack::PopAndDestroy(&attrs);
00486     }
00487 }
00488 
00489 #define SWAP(t,x,y) { t _tmp = x; x = y; y = _tmp; }
00490 
00491 // adapted from code in Music Player Remote Python extension
00492 // also see mpxmedia.h
00493 void CSensor_music::HandleMediaL(const CMPXMedia& aMedia,
00494          TInt errCode)
00495 {
00496   //guilogf("HandleMediaL (%d)", errCode);
00497 
00498   if (errCode)
00499     return;
00500 
00501   SWAP(MyTrackInfo*, iOldTrackInfo, iTrackInfo); // current becomes old
00502 
00503   iTrackInfo->Reset(); // clear space for new data
00504 
00505   HBufC8*& iUrl = iTrackInfo->iUrl;
00506   HBufC8*& iArtist = iTrackInfo->iArtist;
00507   HBufC8*& iTitle = iTrackInfo->iTitle;
00508   HBufC8*& iAlbum = iTrackInfo->iAlbum;
00509 
00510   if (aMedia.IsSupported(KMPXMediaGeneralUri)) {
00511     const TDesC& text = aMedia.ValueText(KMPXMediaGeneralUri);
00512     /*
00513     TParsePtrC filePath(text);
00514     iUrl = ConvToUtf8ZL(filePath.Name());
00515     */
00516     iUrl = ConvToUtf8ZL(text);
00517   }
00518   if (aMedia.IsSupported(KMPXMediaGeneralTitle)) {
00519     iTitle = ConvToUtf8ZL(aMedia.ValueText(KMPXMediaGeneralTitle));
00520   }
00521   if (aMedia.IsSupported(KMPXMediaMusicArtist)) {
00522     iArtist = ConvToUtf8ZL(aMedia.ValueText(KMPXMediaMusicArtist));
00523   }
00524   if (aMedia.IsSupported(KMPXMediaMusicAlbum)) {
00525     iAlbum = ConvToUtf8ZL(aMedia.ValueText(KMPXMediaMusicAlbum));
00526   }
00527 
00528   if (iTrackInfo->Cmp(*iOldTrackInfo) == 0)
00529     return; // no duplicates
00530 
00531   const char* url = (iUrl ? (const char*)(iUrl->Ptr()) : NULL);
00532   const char* title = (iTitle ? (const char*)(iTitle->Ptr()) : NULL);
00533   const char* artist = (iArtist ? (const char*)(iArtist->Ptr()) : NULL);
00534   const char* album = (iAlbum ? (const char*)(iAlbum->Ptr()) : NULL);
00535 
00536   log_db_log_musictrack(GetLogDb(), url, title, artist, album, NULL);
00537 
00538   if (url)
00539     guilogf("music: URL '%s'", url);
00540   if (title)
00541     guilogf("music: title '%s'", title);
00542   if (artist)
00543     guilogf("music: artist '%s'", artist);
00544   if (album)
00545     guilogf("music: album '%s'", album);
00546 }
00547 
00548 /**
00549 
00550 Copyright 2011 Helsinki Institute for Information Technology (HIIT)
00551 and the authors. All rights reserved.
00552 
00553 Authors: Tero Hasu <tero.hasu@hut.fi>
00554 
00555 Permission is hereby granted, free of charge, to any person
00556 obtaining a copy of this software and associated documentation files
00557 (the "Software"), to deal in the Software without restriction,
00558 including without limitation the rights to use, copy, modify, merge,
00559 publish, distribute, sublicense, and/or sell copies of the Software,
00560 and to permit persons to whom the Software is furnished to do so,
00561 subject to the following conditions:
00562 
00563 The above copyright notice and this permission notice shall be
00564 included in all copies or substantial portions of the Software.
00565 
00566 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
00567 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00568 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00569 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
00570 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
00571 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
00572 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
00573 SOFTWARE.
00574 
00575  **/

ContextLogger2—ContextLogger2 Logger Daemon Internals—Generated on Mon May 2 13:49:52 2011 by Doxygen 1.6.1