609 lines
15 KiB
C++
609 lines
15 KiB
C++
// 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;
|
|
}
|