📄 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 + -