?? cpi_player_engine.c
字號:
#include "stdafx.h"
#include "globals.h"
#include "CPI_Player.h"
#include "CPI_Player_Messages.h"
#include "CPI_Player_CoDec.h"
#include "CPI_Player_Output.h"
#include "CPI_Equaliser.h"
typedef struct __CPs_PlayerContext
{
CPs_PlayEngine* m_pBaseEngineParams;
CPs_CoDecModule m_CoDecs[CP_CODEC_last+1];
CPs_OutputModule m_OutputModules[CP_OUTPUT_last+1];
CPs_OutputModule* m_pCurrentOutputModule;
BOOL m_bOutputActive;
DWORD m_dwCurrentOutputModule;
int m_iInternalVolume;
int m_iLastSentTime_Secs;
int m_iLastSentTime_Proportion;
int m_iProportion_TrackLength;
int m_iOpenDevice_Freq_Hz;
BOOL m_bOpenDevice_Stereo;
BOOL m_bOpenDevice_16bit;
CPs_EqualiserModule m_Equaliser;
} CPs_PlayerContext;
void UpdateProgress(CPs_PlayerContext* pContext);
void EmptyOutputStream(CPs_PlayerContext* pContext);
void StartPlay(CPs_CoDecModule* pCoDec, CPs_PlayerContext* pContext);
void EnumOutputDevices(CPs_PlayerContext* pContext);
CPs_CoDecModule* OpenCoDec(CPs_PlayerContext* pContext, const char* pcFilename);
void CleanupCoDecs(CPs_PlayerContext* pContext);
void SetCurrentOutputModule(CPs_PlayerContext* pContext, CPs_OutputModule* pNewOuputModule, BOOL* pbForceRefill);
void AssociateFileExtensions(CPs_PlayerContext* pContext);
////////////////////////////////////////////////////////////////////////////////
DWORD WINAPI CPI_Player__EngineEP(void* pCookie)
{
BOOL bTerminateThread = FALSE;
HRESULT hr_ComState;
CPs_PlayerContext playercontext;
playercontext.m_pBaseEngineParams = (CPs_PlayEngine*)pCookie;
playercontext.m_bOutputActive = FALSE;
playercontext.m_iProportion_TrackLength = 0;
playercontext.m_iLastSentTime_Secs = -1;
playercontext.m_iLastSentTime_Proportion = -1;
playercontext.m_iInternalVolume = 100;
CP_CHECKOBJECT(playercontext.m_pBaseEngineParams);
CP_TRACE0("Cooler Engine Startup");
hr_ComState = CoInitialize(NULL);
// 為該線程初始化 USER32.DLL
{
MSG msgDummy;
PeekMessage(&msgDummy, 0, WM_USER, WM_USER, PM_NOREMOVE);
// 用信號通知此線程已經準備好輸入
SetEvent(playercontext.m_pBaseEngineParams->m_hEvtThreadReady);
}
// 初始化 CoDecs
CP_InitialiseCodec_MPEG(&playercontext.m_CoDecs[CP_CODEC_MPEG]);
CP_InitialiseCodec_WAV(&playercontext.m_CoDecs[CP_CODEC_WAV]);
// CP_InitialiseCodec_OGG(&playercontext.m_CoDecs[CP_CODEC_OGG]);
// CP_InitialiseCodec_WinAmpPlugin(&playercontext.m_CoDecs[CP_CODEC_WINAMPPLUGIN]);
// 初始化輸出模式
if(options.decoder_output_mode > CP_OUTPUT_last)
options.decoder_output_mode = CP_OUTPUT_last;
playercontext.m_dwCurrentOutputModule = options.decoder_output_mode;
CPI_Player_Output_Initialise_WaveMapper(&playercontext.m_OutputModules[CP_OUTPUT_WAVE]);
CPI_Player_Output_Initialise_DirectSound(&playercontext.m_OutputModules[CP_OUTPUT_DIRECTSOUND]);
CPI_Player_Output_Initialise_File(&playercontext.m_OutputModules[CP_OUTPUT_FILE]);
playercontext.m_pCurrentOutputModule = &playercontext.m_OutputModules[playercontext.m_dwCurrentOutputModule];
// 初始化 EQ
CPI_Player_Equaliser_Initialise_Basic(&playercontext.m_Equaliser);
do
{
// 處理任何未決的消息
BOOL bForceRefill = FALSE;
MSG msg;
DWORD dwWaitResult;
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
// 解碼引擎消息
switch(msg.message)
{
case CPTM_QUIT:
bTerminateThread = TRUE;
break;
case CPTM_OPENFILE:
{
char* pcFilename = (char*)msg.wParam;
// 如果這里有另一個不定的打開文件,那么忽略此文件。
// 當進程沒有響應(on an http connect for example),
// 而且用戶還在不停的點擊播放按鈕時,這將非常有用。
//- (這將導致大量的打開/關閉消息被放置倒隊列中,而這些消息將占用線程幾年的時間)
MSG msg2;
if(PeekMessage(&msg2, NULL, CPTM_OPENFILE, CPTM_OPENFILE, PM_NOREMOVE) == FALSE)
{
CPs_CoDecModule* pNewCoDec;
// 如果此處有CoDec播放,那么關閉之
if(playercontext.m_pCurrentOutputModule->m_pCoDec)
{
playercontext.m_pCurrentOutputModule->m_pCoDec->CloseFile(playercontext.m_pCurrentOutputModule->m_pCoDec);
playercontext.m_pCurrentOutputModule->m_pCoDec = NULL;
}
CP_TRACE1("Openfile \"%s\"", pcFilename);
pNewCoDec = OpenCoDec(&playercontext, pcFilename);
// 如果打開失敗,那么從接口申請一個新的流
if(pNewCoDec == NULL)
{
PostMessage(playercontext.m_pBaseEngineParams->m_hWndNotify, CPNM_PLAYERSTATE, (WPARAM)cppsEndOfStream, 0);
}
// 檢查文件格式 - 取樣率,聲道數或者取樣大小是否改變
// 然后,清除當前的輸出,關閉輸出裝備 (著將導致一個 gap
// - 但僅當格式改變時)
else if(playercontext.m_bOutputActive == TRUE)
{
CPs_FileInfo FileInfo;
pNewCoDec->GetFileInfo(pNewCoDec, &FileInfo);
if(//FileInfo.m_iFreq_Hz != playercontext.m_iOpenDevice_Freq_Hz
FileInfo.m_bStereo != playercontext.m_bOpenDevice_Stereo
|| FileInfo.m_b16bit != playercontext.m_bOpenDevice_16bit
)
{
CP_TRACE0("Stream format changes - clearing stream");
EmptyOutputStream(&playercontext);
StartPlay(pNewCoDec, &playercontext);
bForceRefill = TRUE;
}
}
playercontext.m_pCurrentOutputModule->m_pCoDec = pNewCoDec;
}
#ifdef _DEBUG
else
{
CP_TRACE1("Openfile of \"%s\" ignored due to other opens in the queue", pcFilename);
}
#endif
// 清空
free(pcFilename);
}
break;
case CPTM_SEEK:
if(playercontext.m_bOutputActive == TRUE)
{
// 如果有另一個消息在此位置,忽略其他的!
MSG msg2;
if(PeekMessage(&msg2, NULL, CPTM_SEEK, CPTM_SEEK, PM_NOREMOVE) == FALSE)
{
if(playercontext.m_pCurrentOutputModule->m_pCoDec)
playercontext.m_pCurrentOutputModule->m_pCoDec->Seek(playercontext.m_pCurrentOutputModule->m_pCoDec, (int)msg.wParam, (int)msg.lParam);
playercontext.m_pCurrentOutputModule->Flush(playercontext.m_pCurrentOutputModule);
bForceRefill = TRUE;
}
}
break;
case CPTM_PLAY:
if(playercontext.m_pCurrentOutputModule->m_pCoDec)
{
// 如果沒有輸出stage - 馬上初始化
if(playercontext.m_bOutputActive == FALSE)
{
StartPlay(playercontext.m_pCurrentOutputModule->m_pCoDec, &playercontext);
bForceRefill = TRUE;
}
playercontext.m_pCurrentOutputModule->SetPause(playercontext.m_pCurrentOutputModule, FALSE);
PostMessage(playercontext.m_pBaseEngineParams->m_hWndNotify, CPNM_PLAYERSTATE, (WPARAM)cppsPlaying, 0);
playercontext.m_iLastSentTime_Secs = -1;
playercontext.m_iLastSentTime_Proportion = -1;
UpdateProgress(&playercontext);
}
break;
case CPTM_STOP:
if(playercontext.m_pCurrentOutputModule->m_pCoDec)
{
playercontext.m_pCurrentOutputModule->m_pCoDec->CloseFile(playercontext.m_pCurrentOutputModule->m_pCoDec);
playercontext.m_pCurrentOutputModule->m_pCoDec = NULL;
}
if(playercontext.m_bOutputActive == TRUE)
{
playercontext.m_bOutputActive = FALSE;
playercontext.m_pCurrentOutputModule->Uninitialise(playercontext.m_pCurrentOutputModule);
}
PostMessage(playercontext.m_pBaseEngineParams->m_hWndNotify, CPNM_PLAYERSTATE, (WPARAM)cppsStopped, 0);
break;
case CPTM_PAUSE:
CP_TRACE0("Pause");
if(playercontext.m_bOutputActive == TRUE)
playercontext.m_pCurrentOutputModule->SetPause(playercontext.m_pCurrentOutputModule, TRUE);
PostMessage(playercontext.m_pBaseEngineParams->m_hWndNotify, CPNM_PLAYERSTATE, (WPARAM)cppsPaused, 0);
break;
case CPTM_SETPROGRESSTRACKLENGTH:
playercontext.m_iProportion_TrackLength = (int)msg.wParam;
break;
case CPTM_SENDSYNCCOOKIE:
PostMessage(playercontext.m_pBaseEngineParams->m_hWndNotify, CPNM_SYNCCOOKIE, msg.wParam, 0);
break;
case CPTM_BLOCKMSGUNTILENDOFSTREAM:
EmptyOutputStream(&playercontext);
break;
case CPTM_ENUMOUTPUTDEVICES:
EnumOutputDevices(&playercontext);
break;
case CPTM_SETEQSETTINGS:
{
MSG msg2;
CPs_EQSettings* pEQ = (CPs_EQSettings*)msg.wParam;
// 如果有另一個未決的EQ 消息,不要處理這個(試圖減少噪音)
if(PeekMessage(&msg2, NULL, CPTM_SETEQSETTINGS, CPTM_OPENFILE, PM_NOREMOVE) == FALSE)
{
BOOL bEQEnableStateChanged;
playercontext.m_Equaliser.ApplySettings(&playercontext.m_Equaliser, pEQ, &bEQEnableStateChanged);
// 清空緩存(這將音樂的不連續,但至少
// EQ 設置會立即改變
if(playercontext.m_bOutputActive == TRUE && playercontext.m_pCurrentOutputModule->OnEQChanged)
playercontext.m_pCurrentOutputModule->OnEQChanged(playercontext.m_pCurrentOutputModule);
}
free(pEQ);
}
break;
case CPTM_ONOUTPUTMODULECHANGE:
{
playercontext.m_dwCurrentOutputModule = options.decoder_output_mode;
SetCurrentOutputModule(&playercontext, NULL, &bForceRefill);
}
break;
case CPTM_ASSOCIATEFILEEXTENSIONS:
AssociateFileExtensions(&playercontext);
break;
case CPTM_SETINTERNALVOLUME:
playercontext.m_iInternalVolume = (int)msg.wParam;
if(playercontext.m_bOutputActive == TRUE && playercontext.m_pCurrentOutputModule->SetInternalVolume)
playercontext.m_pCurrentOutputModule->SetInternalVolume(playercontext.m_pCurrentOutputModule, playercontext.m_iInternalVolume);
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -