// CDPlay.cpp : Implementation of CCDPlay #include "windows.h" #include "CDPlay.h" #include "cdapi.h" #include "cdplayer.h" #include "literals.h" #include "playres.h" #include "tchar.h" #include "trklst.h" extern HINSTANCE g_dllInst; extern BOOL g_fOleInitialized; extern TCHAR g_szTimeSep[10]; ///////////////////////////////////////////////////////////////////////////// // CCDPlay CCDPlay::CCDPlay() { m_hMenu = NULL; m_hwndMain = NULL; m_pSink = NULL; m_dwRef = 0; InitIcons(); } CCDPlay::~CCDPlay() { //close device ... //destroy objects if (m_hIcon16) { DestroyIcon(m_hIcon16); m_hIcon16 = NULL; } if (m_hIcon32) { DestroyIcon(m_hIcon32); m_hIcon32 = NULL; } if (m_hMenu) { DestroyMenu(m_hMenu); m_hMenu = NULL; } // Cleanup from cd player stuff DeleteCriticalSection (&g_csTOCSerialize); if (g_fOleInitialized) { OleUninitialize(); } } STDMETHODIMP CCDPlay::QueryInterface(REFIID riid, void** ppv) { *ppv = NULL; if (IID_IUnknown == riid || IID_IMMComponent == riid) { *ppv = this; } if (IID_IMMComponentAutomation == riid) { *ppv = (IMMComponentAutomation*)this; } if (NULL==*ppv) { return E_NOINTERFACE; } AddRef(); return S_OK; } STDMETHODIMP_(ULONG) CCDPlay::AddRef(void) { return ++m_dwRef; } STDMETHODIMP_(ULONG) CCDPlay::Release(void) { if (0!=--m_dwRef) return m_dwRef; delete this; return 0; } STDMETHODIMP CCDPlay::GetInfo(MMCOMPDATA* mmCompData) { mmCompData->hiconSmall = m_hIcon16; mmCompData->hiconLarge = m_hIcon32; mmCompData->nAniResID = -1; mmCompData->hInst = g_dllInst; _tcscpy(mmCompData->szName,TEXT("CD")); QueryVolumeSupport(&mmCompData->fVolume, &mmCompData->fPan); //try to get the rect required by the ledwnd, //this could change if there are large fonts involved TCHAR szDisp[MAX_PATH]; LoadString(g_dllInst, STR_DISPLAY_LABELS, szDisp, sizeof(szDisp)/sizeof(TCHAR)); HWND hwndDisp = GetDlgItem(m_hwndMain,IDC_LED); HDC hdc = GetDC(hwndDisp); LOGFONT lf; int iLogPelsY; iLogPelsY = GetDeviceCaps( hdc, LOGPIXELSY ); ZeroMemory( &lf, sizeof(lf) ); HFONT hTempFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT); GetObject(hTempFont,sizeof(lf),&lf); lf.lfHeight = (-10 * iLogPelsY) / 72; if (lf.lfCharSet == ANSI_CHARSET) { lf.lfWeight = FW_BOLD; } lf.lfOutPrecision = OUT_DEFAULT_PRECIS; lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; lf.lfQuality = PROOF_QUALITY; lf.lfPitchAndFamily = DEFAULT_PITCH | FF_SWISS; HFONT hDispFont = CreateFontIndirect(&lf); HFONT hOrgFont = (HFONT)SelectObject(hdc,hDispFont); GetClientRect(hwndDisp,&(mmCompData->rect)); DrawText( hdc, // handle to device context szDisp, // pointer to string to draw -1, // string length, in characters &(mmCompData->rect), // pointer to struct with formatting dimensions DT_CALCRECT|DT_EXPANDTABS|DT_NOPREFIX); SelectObject(hdc,hOrgFont); DeleteObject(hDispFont); ReleaseDC(hwndDisp,hdc); return S_OK; } BOOL CALLBACK TransDlgProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) { return FALSE; } extern HWND PASCAL WinFake(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow, HWND hwndMain, IMMFWNotifySink* pSink); STDMETHODIMP CCDPlay::Init(IMMFWNotifySink* pSink, HWND hwndMain, RECT* pRect, HWND* phwndComp, HMENU* phMenu) { HRESULT hr = S_OK; //save sink pointer m_pSink = pSink; //load custom menu m_hMenu = LoadMenu(g_dllInst, MAKEINTRESOURCE(IDR_MAINMENU)); *phMenu = m_hMenu; //create the main window here //fake cd player into thinking it can go) m_hwndMain = WinFake(g_dllInst,NULL,"",SW_NORMAL,hwndMain,pSink); //set up pointer to main window of app *phwndComp = m_hwndMain; return hr; } void GetTOC(int cdrom, TCHAR* szNetQuery) { DWORD dwRet; MCI_SET_PARMS mciSet; unsigned long m_toc[101]; ZeroMemory( &mciSet, sizeof(mciSet) ); mciSet.dwTimeFormat = MCI_FORMAT_MSF; mciSendCommand( g_Devices[ cdrom ]->hCd, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD_PTR)(LPVOID)&mciSet ); MCI_STATUS_PARMS mciStatus; long lAddress, lStartPos, lDiskLen; int i; ZeroMemory( &mciStatus, sizeof(mciStatus) ); mciStatus.dwItem = MCI_STATUS_NUMBER_OF_TRACKS; // // NOTE: none of the mciSendCommand calls below bother to check the // return code. This is asking for trouble... but if the // commands fail we cannot do much about it. // dwRet = mciSendCommand( g_Devices[ cdrom ]->hCd, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)(LPVOID)&mciStatus); int tracks = -1; tracks = (UCHAR)mciStatus.dwReturn; mciStatus.dwItem = MCI_STATUS_POSITION; for ( i = 0; i < tracks; i++ ) { mciStatus.dwTrack = i + 1; dwRet = mciSendCommand( g_Devices[ cdrom ]->hCd, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK, (DWORD_PTR)(LPVOID)&mciStatus); lAddress = (long)mciStatus.dwReturn; //converts "packed" time into pure frames lAddress = (MCI_MSF_MINUTE(lAddress) * FRAMES_PER_MINUTE) + (MCI_MSF_SECOND(lAddress) * FRAMES_PER_SECOND) + (MCI_MSF_FRAME( lAddress)); m_toc[i] = lAddress; if (i==0) { lStartPos = lAddress; } } mciStatus.dwItem = MCI_STATUS_LENGTH; dwRet = mciSendCommand( g_Devices[ cdrom ]->hCd, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)(LPVOID)&mciStatus); /* ** Convert the total disk length into frames */ lAddress = (long)mciStatus.dwReturn; lDiskLen = (MCI_MSF_MINUTE(lAddress) * FRAMES_PER_MINUTE) + (MCI_MSF_SECOND(lAddress) * FRAMES_PER_SECOND) + (MCI_MSF_FRAME( lAddress)); /* ** Now, determine the absolute start position of the sentinel ** track. That is, the special track that marks the end of the ** disk. */ lAddress = lStartPos + lDiskLen + 1; //dstewart: add one for true time m_toc[i] = lAddress; wsprintf(szNetQuery,TEXT("cd=%X"),tracks); //add each frame stattime to query, include end time of disc TCHAR tempstr[MAX_PATH]; for (i = 0; i < tracks+1; i++) { wsprintf(tempstr,TEXT("+%X"),m_toc[i]); _tcscat(szNetQuery,tempstr); } } STDMETHODIMP CCDPlay::OnAction(MMACTIONS mmActionID, LPVOID pAction) { HRESULT hr = S_OK; switch (mmActionID) { case MMACTION_PLAY: { SendMessage(m_hwndMain,WM_COMMAND,MAKEWPARAM(IDM_PLAYBAR_PLAY,0),0); } break; case MMACTION_STOP: { SendMessage(m_hwndMain,WM_COMMAND,MAKEWPARAM(IDM_PLAYBAR_STOP,0),0); } break; case MMACTION_UNLOADMEDIA: //"eject" { SendMessage(m_hwndMain,WM_COMMAND,MAKEWPARAM(IDM_PLAYBAR_EJECT,0),0); } break; case MMACTION_NEXTTRACK: { SendMessage(m_hwndMain,WM_COMMAND,MAKEWPARAM(IDM_PLAYBAR_NEXTTRACK,0),0); } break; case MMACTION_PREVTRACK: { SendMessage(m_hwndMain,WM_COMMAND,MAKEWPARAM(IDM_PLAYBAR_PREVTRACK,0),0); } break; case MMACTION_PAUSE: { SendMessage(m_hwndMain,WM_COMMAND,MAKEWPARAM(IDM_PLAYBAR_PAUSE,0),0); } break; case MMACTION_REWIND: { SendMessage(m_hwndMain,WM_COMMAND,MAKEWPARAM(IDM_PLAYBAR_SKIPBACK,0),0); } break; case MMACTION_FFWD: { SendMessage(m_hwndMain,WM_COMMAND,MAKEWPARAM(IDM_PLAYBAR_SKIPFORE,0),0); } break; case MMACTION_NEXTMEDIA: { SendMessage(m_hwndMain,WM_COMMAND,MAKEWPARAM(IDM_PLAYBAR_EJECT,0),0); } break; case MMACTION_GETMEDIAID: { MMMEDIAID* pID = (MMMEDIAID*)pAction; if (pID->nDrive == -1) { pID->nDrive = g_CurrCdrom; } wsprintf( pID->szMediaID, g_szSectionF, g_Devices[pID->nDrive]->CdInfo.Id ); pID->dwMediaID = g_Devices[pID->nDrive]->CdInfo.Id; pID->dwNumTracks = NUMTRACKS(pID->nDrive); _tcscpy(pID->szArtist,ARTIST(pID->nDrive)); _tcscpy(pID->szTitle,TITLE(pID->nDrive)); PTRACK_INF t; if (CURRTRACK(pID->nDrive)!=NULL) { t = FindTrackNodeFromTocIndex( CURRTRACK(pID->nDrive)->TocIndex, ALLTRACKS( pID->nDrive ) ); if (t) { _tcscpy(pID->szTrack,t->name); } } } break; case MMACTION_GETNETQUERY: { MMNETQUERY* pQuery = (MMNETQUERY*)pAction; if (pQuery->nDrive == -1) { pQuery->nDrive = g_CurrCdrom; } GetTOC(pQuery->nDrive,pQuery->szNetQuery); } break; case MMACTION_READSETTINGS : { ReadSettings((void*)pAction); UpdateDisplay( DISPLAY_UPD_LED | DISPLAY_UPD_TRACK_TIME | DISPLAY_UPD_TRACK_NAME ); } break; case MMACTION_GETTRACKINFO : { LPMMTRACKORDISC pInfo = (LPMMTRACKORDISC)pAction; return (GetTrackInfo(pInfo)); } break; case MMACTION_GETDISCINFO : { LPMMTRACKORDISC pInfo = (LPMMTRACKORDISC)pAction; return (GetDiscInfo(pInfo)); } break; case MMACTION_SETTRACK : { LPMMCHANGETRACK pTrack = (LPMMCHANGETRACK)pAction; SetTrack(pTrack->nNewTrack); } break; case MMACTION_SETDISC : { LPMMCHANGEDISC pDisc = (LPMMCHANGEDISC)pAction; SetDisc(pDisc->nNewDisc); } break; default: { hr = E_NOTIMPL; } break; } return hr; } //QueryVolumeSupport is just a helper function ... you don't have to do it this way STDMETHODIMP CCDPlay::QueryVolumeSupport(BOOL* pVolume, BOOL* pPan) { *pVolume = FALSE; *pPan = FALSE; return S_OK; } //InitIcons is just a helper function ... you don't have to do it this way void CCDPlay::InitIcons() { m_hIcon16 = NULL; m_hIcon32 = NULL; m_hIcon16 = (HICON)LoadImage(g_dllInst, MAKEINTRESOURCE(IDI_ICON_CDPLAY), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR); m_hIcon32 = LoadIcon(g_dllInst, MAKEINTRESOURCE(IDI_ICON_CDPLAY)); } /* * NormalizeNameForMenuDisplay This function turns a string like "Twist & Shout" into "Twist && Shout" because otherwise it will look like "Twist _Shout" in the menu due to the accelerator char */ void CCDPlay::NormalizeNameForMenuDisplay(TCHAR* szInput, TCHAR* szOutput, DWORD cbLen) { ZeroMemory(szOutput,cbLen); WORD index1 = 0; WORD index2 = 0; for (; index1 < _tcslen(szInput); index1++) { szOutput[index2] = szInput[index1]; if (szOutput[index2] == TEXT('&')) { szOutput[++index2] = TEXT('&'); } index2++; } } //try to find and return the track info referenced by pInfo->nNumber HRESULT CCDPlay::GetTrackInfo(LPMMTRACKORDISC pInfo) { HRESULT hr = E_FAIL; if (pInfo) { int index = -1; PTRACK_PLAY playlist; for( playlist = SAVELIST(g_CurrCdrom); playlist != NULL; playlist = playlist->nextplay ) { index++; PTRACK_INF t = NULL; t = FindTrackNodeFromTocIndex( playlist->TocIndex, ALLTRACKS( g_CurrCdrom ) ); if ((t) && (index == pInfo->nNumber)) { int mtemp, stemp; FigureTrackTime(g_CurrCdrom, t->TocIndex, &mtemp, &stemp ); TCHAR szDisplayTrack[TRACK_TITLE_LENGTH*2]; NormalizeNameForMenuDisplay(t->name,szDisplayTrack,sizeof(szDisplayTrack)); wsprintf(pInfo->szName,TEXT("%i. %s (%i%s%02i)"),t->TocIndex + 1,szDisplayTrack,mtemp,g_szTimeSep,stemp); pInfo->nID = t->TocIndex; if (playlist->TocIndex == CURRTRACK(g_CurrCdrom)->TocIndex) { pInfo->fCurrent = TRUE; } else { pInfo->fCurrent = FALSE; } hr = S_OK; //indicate that indexed track was found } //end if track ok } //end stepping } //end if pinfo valid return (hr); } HRESULT CCDPlay::GetDiscInfo(LPMMTRACKORDISC pInfo) { HRESULT hr = E_FAIL; if (pInfo) { for(int i = 0; i < g_NumCdDevices; i++) { if (i == pInfo->nNumber) { TCHAR szDisplayTitle[TITLE_LENGTH*2]; NormalizeNameForMenuDisplay(TITLE(i),szDisplayTitle,sizeof(szDisplayTitle)); TCHAR szDisplayArtist[ARTIST_LENGTH*2]; NormalizeNameForMenuDisplay(ARTIST(i),szDisplayArtist,sizeof(szDisplayArtist)); if (g_Devices[i]->State & (CD_BEING_SCANNED | CD_NO_CD | CD_DATA_CD_LOADED | CD_IN_USE)) { wsprintf(pInfo->szName,TEXT("<%c:> %s"),g_Devices[i]->drive,szDisplayTitle); } else { wsprintf(pInfo->szName,TEXT("<%c:> %s (%s)"), g_Devices[i]->drive,szDisplayTitle,szDisplayArtist); } pInfo->nID = i; if (i == g_CurrCdrom) { pInfo->fCurrent = TRUE; } else { pInfo->fCurrent = FALSE; } hr = S_OK; } //end if match } //end for } //end if pinfo valid return (hr); } void CCDPlay::SetTrack(int nTrack) { PTRACK_INF tr; tr = ALLTRACKS( g_CurrCdrom ); if ( tr != NULL ) { PTRACK_PLAY trCurrent = CURRTRACK(g_CurrCdrom); tr = FindTrackNodeFromTocIndex(nTrack,ALLTRACKS(g_CurrCdrom)); if (tr->TocIndex != trCurrent->TocIndex) { PTRACK_PLAY p = NULL; for (p = PLAYLIST(g_CurrCdrom); (p!=NULL) && (p->TocIndex != tr->TocIndex); p = p->nextplay); if (p) { TimeAdjustSkipToTrack( g_CurrCdrom, p ); } //if not null } //if not equal to current } //if tr not null } void CCDPlay::SetDisc(int nDisc) { SwitchToCdrom(nDisc, FALSE); MMONDISCCHANGED mmOnDisc; mmOnDisc.nNewDisc = g_CurrCdrom; mmOnDisc.fDisplayVolChange = TRUE; g_pSink->OnEvent(MMEVENT_ONDISCCHANGED,&mmOnDisc); } extern "C" HRESULT WINAPI CDPLAY_CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, void ** ppvObj) { CCDPlay* pObj; HRESULT hr = E_OUTOFMEMORY; *ppvObj = NULL; if (NULL!=pUnkOuter && IID_IUnknown!=riid) { return CLASS_E_NOAGGREGATION; } pObj = new CCDPlay(); if (NULL==pObj) { return hr; } hr = pObj->QueryInterface(riid, ppvObj); if (FAILED(hr)) { delete pObj; } return hr; }