Files
Rocky5 921f9fe9f8 Clean Copy
If ever adding the XBMC4Xbox source to GitHub use "git add -f -A" or it wont add all the files.
2024-01-08 19:01:29 +00:00

725 lines
18 KiB
C++

/*
* Copyright (C) 2005-2013 Team XBMC
* http://xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This Program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with XBMC; see the file COPYING. If not, see
* <http://www.gnu.org/licenses/>.
*
*/
#include "system.h"
#include "utils/log.h"
#include "CdgParser.h"
#include "Application.h"
#include "Util.h"
#include "utils/URIUtils.h"
#include "AudioContext.h"
#include "GUIInfoManager.h"
#include "music/tags/MusicInfoTag.h"
#include "GUIWindowManager.h"
#include "settings/Settings.h"
using namespace MUSIC_INFO;
using namespace XFILE;
//CdgLoader
CCdgLoader::CCdgLoader()
{
m_strFileName.Empty();
m_CdgFileState = FILE_NOT_LOADED;
m_pBuffer = NULL;
m_uiFileLength = 0;
m_uiLoadedBytes = 0;
m_uiStreamChunk = STREAM_CHUNK;
}
CCdgLoader::~CCdgLoader()
{
StopStream();
}
void CCdgLoader::StreamFile(CStdString strfilename)
{
CSingleLock lock (m_CritSection);
m_strFileName = strfilename;
URIUtils::RemoveExtension(m_strFileName);
m_strFileName += ".cdg";
CThread::Create(false);
}
void CCdgLoader::StopStream()
{
CSingleLock lock (m_CritSection);
CThread::StopThread();
m_uiLoadedBytes = 0;
m_CdgFileState = FILE_NOT_LOADED;
if (m_pBuffer)
SAFE_DELETE_ARRAY(m_pBuffer);
}
SubCode* CCdgLoader::GetCurSubCode()
{
CSingleLock lock (m_CritSection);
SubCode* pFirst = GetFirstLoaded();
SubCode* pLast = GetLastLoaded();
if (!pFirst || !pLast || !m_pSubCode) return NULL;
if (m_pSubCode < pFirst || m_pSubCode > pLast) return NULL;
return m_pSubCode;
}
bool CCdgLoader::SetNextSubCode()
{
CSingleLock lock (m_CritSection);
SubCode* pFirst = GetFirstLoaded();
SubCode* pLast = GetLastLoaded();
if (!pFirst || !pLast || !m_pSubCode) return false;
if (m_pSubCode < pFirst || m_pSubCode >= pLast) return false;
m_pSubCode++;
return true;
}
errCode CCdgLoader::GetFileState()
{
CSingleLock lock (m_CritSection);
return m_CdgFileState;
}
CStdString CCdgLoader::GetFileName()
{
CSingleLock lock (m_CritSection);
return m_strFileName;
}
SubCode* CCdgLoader::GetFirstLoaded()
{
if (!m_uiLoadedBytes)
return NULL;
return (SubCode*) m_pBuffer;
}
SubCode* CCdgLoader::GetLastLoaded()
{
if (m_uiLoadedBytes < sizeof(SubCode) || m_uiLoadedBytes > m_uiFileLength )
return NULL;
return ((SubCode*) (m_pBuffer)) + m_uiLoadedBytes / sizeof(SubCode) - 1;
}
void CCdgLoader::OnStartup()
{
CSingleLock lock (m_CritSection);
if (!CFile::Exists(m_strFileName))
{
m_CdgFileState = FILE_ERR_NOT_FOUND;
return ;
}
if (!m_File.Open(m_strFileName, TRUE))
{
m_CdgFileState = FILE_ERR_OPENING;
return ;
}
m_uiFileLength = (int)m_File.GetLength(); // ASSUMES FILELENGTH IS LESS THAN 2^32 bytes!!!
if (!m_uiFileLength) return ;
m_File.Seek(0, SEEK_SET);
if (m_pBuffer)
SAFE_DELETE_ARRAY(m_pBuffer);
m_pBuffer = new BYTE[m_uiFileLength];
if (!m_pBuffer)
{
m_CdgFileState = FILE_ERR_NO_MEM;
return ;
}
m_uiLoadedBytes = 0;
m_pSubCode = (SubCode*) m_pBuffer;
m_CdgFileState = FILE_LOADING;
}
void CCdgLoader::Process()
{
if (m_CdgFileState != FILE_LOADING && m_CdgFileState != FILE_SKIP) return ;
if (m_uiFileLength < m_uiStreamChunk)
m_uiLoadedBytes = m_File.Read(m_pBuffer, m_uiFileLength);
else
{
UINT uiNumReadings = m_uiFileLength / m_uiStreamChunk;
UINT uiRemainder = m_uiFileLength % m_uiStreamChunk;
UINT uiCurReading = 0;
while (!CThread::m_bStop && uiCurReading < uiNumReadings)
{
m_uiLoadedBytes += m_File.Read(m_pBuffer + m_uiLoadedBytes, m_uiStreamChunk);
uiCurReading++;
}
if (uiRemainder && m_uiLoadedBytes + uiRemainder == m_uiFileLength)
m_uiLoadedBytes += m_File.Read(m_pBuffer + m_uiLoadedBytes, uiRemainder);
}
}
void CCdgLoader::OnExit()
{
m_File.Close();
if (m_uiFileLength && m_uiLoadedBytes == m_uiFileLength)
m_CdgFileState = FILE_LOADED;
}
//CdgReader
CCdgReader::CCdgReader()
{
m_pLoader = NULL;
m_fStartingTime = 0.0f;
m_fAVDelay = 0.0f;
m_uiNumReadSubCodes = 0;
m_Cdg.ClearDisplay();
}
CCdgReader::~CCdgReader()
{
StopThread();
}
bool CCdgReader::Attach(CCdgLoader* pLoader)
{
CSingleLock lock (m_CritSection);
if (!m_pLoader)
m_pLoader = pLoader;
if (!m_pLoader) return false;
return true;
}
void CCdgReader::DetachLoader()
{
StopThread();
CSingleLock lock (m_CritSection);
m_pLoader = NULL;
}
bool CCdgReader::Start(float fStartTime)
{
CSingleLock lock (m_CritSection);
if (!m_pLoader) return false;
m_fStartingTime = fStartTime;
SetAVDelay(g_advancedSettings.m_karaokeSyncDelay);
m_uiNumReadSubCodes = 0;
m_Cdg.ClearDisplay();
m_FileState = FILE_LOADED;
CThread::Create(false);
return true;
}
void CCdgReader::SetAVDelay(float fDelay)
{
m_fAVDelay = fDelay;
#ifdef _DEBUG
m_fAVDelay -= DEBUG_AVDELAY_MOD;
#endif
}
float CCdgReader::GetAVDelay()
{
return m_fAVDelay;
}
errCode CCdgReader::GetFileState()
{
CSingleLock lock (m_CritSection);
if (m_FileState == FILE_SKIP)
return m_FileState;
if (!m_pLoader) return FILE_NOT_LOADED;
return m_pLoader->GetFileState();
}
CCdg* CCdgReader::GetCdg()
{
return (CCdg*) &m_Cdg;
}
CStdString CCdgReader::GetFileName()
{
CSingleLock lock (m_CritSection);
if (m_pLoader)
return m_pLoader->GetFileName();
return "";
}
void CCdgReader::ReadUpToTime(float secs)
{
if (secs < 0) return ;
if (!(m_pLoader->GetCurSubCode())) return ;
UINT uiFinalOffset = (UINT) (secs * PARSING_FREQ);
if ( m_uiNumReadSubCodes >= uiFinalOffset) return ;
UINT i;
for (i = m_uiNumReadSubCodes; i <= uiFinalOffset; i++)
{
m_Cdg.ReadSubCode(m_pLoader->GetCurSubCode());
if (m_pLoader->SetNextSubCode())
m_uiNumReadSubCodes++;
}
}
void CCdgReader::SkipUpToTime(float secs)
{
if (secs < 0) return ;
m_FileState= FILE_SKIP;
UINT uiFinalOffset = (UINT) (secs * PARSING_FREQ);
// is this needed?
if ( m_uiNumReadSubCodes > uiFinalOffset) return ;
for (UINT i = m_uiNumReadSubCodes; i <= uiFinalOffset; i++)
{
//m_Cdg.ReadSubCode(m_pLoader->GetCurSubCode());
if (m_pLoader->SetNextSubCode())
m_uiNumReadSubCodes++;
}
m_FileState = FILE_LOADING;
}
void CCdgReader::OnStartup()
{}
void CCdgReader::Process()
{
double fCurTime = 0.0f;
bool bIsFirstPass = true;
double fNewTime=0.f;
CStdString strExt;
URIUtils::GetExtension(m_pLoader->GetFileName(),strExt);
strExt = m_pLoader->GetFileName().substr(0,m_pLoader->GetFileName().size()-strExt.size());
while (!CThread::m_bStop)
{
CSingleLock lock (m_CritSection);
double fDiff;
const CMusicInfoTag* tag = g_infoManager.GetCurrentSongTag();
if (!tag || tag->GetURL().substr(0,strExt.size()) != strExt)
{
Sleep(15);
if (CThread::m_bStop)
return;
URIUtils::GetExtension(m_pLoader->GetFileName(),strExt);
strExt = m_pLoader->GetFileName().substr(0,m_pLoader->GetFileName().size()-strExt.size());
fDiff = 0.f;
}
else
{
fNewTime=g_application.GetTime();
fDiff = fNewTime-fCurTime-m_fAVDelay;
}
if (fDiff < -0.3f)
{
CStdString strFile = m_pLoader->GetFileName();
m_pLoader->StopStream();
while (m_pLoader->GetCurSubCode()) {}
m_pLoader->StreamFile(strFile);
m_uiNumReadSubCodes = 0;
m_Cdg.ClearDisplay();
fNewTime = g_application.GetTime();
SkipUpToTime((float)fNewTime-m_fAVDelay);
}
else
ReadUpToTime((float)fNewTime-m_fAVDelay);
fCurTime = fNewTime;
lock.Leave();
Sleep(15);
}
}
void CCdgReader::OnExit()
{}
//CdgRenderer
CCdgRenderer::CCdgRenderer()
{
m_pd3dDevice = NULL;
m_pCdgTexture = NULL;
m_pReader = NULL;
m_pCdg = NULL;
m_bRender = false;
m_bgAlpha = 0x00000000;
m_fgAlpha = 0xFF000000;
}
CCdgRenderer::~CCdgRenderer()
{
ReleaseGraphics();
}
bool CCdgRenderer::Attach(CCdgReader* pReader)
{
CSingleLock lock (m_CritSection);
if (!m_pReader)
m_pReader = pReader;
if (!m_pReader) return false;
if (!m_pCdg)
m_pCdg = m_pReader->GetCdg();
if (!m_pCdg) return false;
return true;
}
void CCdgRenderer::DetachReader()
{
CSingleLock lock (m_CritSection);
m_pReader = NULL;
m_pCdg = NULL;
}
void CCdgRenderer::Render()
{
CSingleLock lock (m_CritSection);
if (!m_pReader) return ;
m_FileState = m_pReader->GetFileState();
if (m_FileState == FILE_NOT_LOADED || m_FileState == FILE_SKIP) return ;
if (m_FileState == FILE_LOADED || m_FileState == FILE_LOADING )
{
if (!m_bRender) return ;
UpdateTexture();
DrawTexture();
return ;
}
else
{
CStdString strMessage, strFileName;
strFileName = URIUtils::GetFileName(m_pReader->GetFileName());
switch (m_FileState)
{
case FILE_ERR_NOT_FOUND:
strMessage.Format("%s not found", strFileName.c_str());
break;
case FILE_ERR_OPENING:
strMessage.Format("Error opening %s", strFileName.c_str());
break;
case FILE_ERR_LOADING:
strMessage.Format("Error loading %s", strFileName.c_str());
break;
case FILE_ERR_NO_MEM:
strMessage = "Out of memory";
break;
}
// don't render the message to the screen, just log it
// Hmmm. Can't seem to be able to log
// CLog::Log(LOGWARNING, "Karaoke CDG Renderer: %s", strMessage.c_str());
/* CGUIFont* pFont = g_fontManager.GetFont("font14");
if(pFont)
pFont->DrawText(60,60, 0xffffffff, strMessage);*/
}
}
bool CCdgRenderer::InitGraphics()
{
CSingleLock lock (m_CritSection);
if (!m_pd3dDevice)
m_pd3dDevice = g_graphicsContext.Get3DDevice();
if (!m_pd3dDevice) return false;
// set the colours
m_bgAlpha = 0;
if (g_guiSettings.GetString("musicplayer.visualisation").Equals("None"))
m_bgAlpha = 0xff000000;
m_fgAlpha = 0xff000000;
if (!m_pCdgTexture)
m_pd3dDevice->CreateTexture(WIDTH, HEIGHT, 0, 0, D3DFMT_LIN_A8R8G8B8, D3DPOOL_MANAGED, &m_pCdgTexture);
if (!m_pCdgTexture) return false;
m_bRender = true;
return true;
}
void CCdgRenderer::ReleaseGraphics()
{
CSingleLock lock (m_CritSection);
SAFE_RELEASE(m_pCdgTexture);
m_bRender = false;
}
void CCdgRenderer::DrawTexture()
{
m_pd3dDevice->SetVertexShader( D3DFVF_CUSTOMVERTEX );
m_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
m_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
m_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
m_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ADDRESSU, D3DTADDRESS_CLAMP );
m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ADDRESSV, D3DTADDRESS_CLAMP );
m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE );
m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE );
m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );
m_pd3dDevice->SetTexture(0, m_pCdgTexture);
m_pd3dDevice->Begin(D3DPT_QUADLIST);
RESOLUTION res = g_graphicsContext.GetVideoResolution();
m_pd3dDevice->SetVertexData2f( D3DVSDE_TEXCOORD0, (float)BORDERWIDTH, (float) BORDERHEIGHT);
m_pd3dDevice->SetVertexData4f( D3DVSDE_VERTEX, (float)g_settings.m_ResInfo[res].Overscan.left, (float) g_settings.m_ResInfo[res].Overscan.top, 0, 0 );
m_pd3dDevice->SetVertexData2f( D3DVSDE_TEXCOORD0, (float)(WIDTH - BORDERWIDTH), (float) BORDERHEIGHT);
m_pd3dDevice->SetVertexData4f( D3DVSDE_VERTEX, (float)g_settings.m_ResInfo[res].Overscan.right, (float) g_settings.m_ResInfo[res].Overscan.top, 0, 0 );
m_pd3dDevice->SetVertexData2f( D3DVSDE_TEXCOORD0, (float)(WIDTH - BORDERWIDTH), (float)(HEIGHT - BORDERHEIGHT));
m_pd3dDevice->SetVertexData4f( D3DVSDE_VERTEX, (float)g_settings.m_ResInfo[res].Overscan.right, (float) g_settings.m_ResInfo[res].Overscan.bottom, 0, 0);
m_pd3dDevice->SetVertexData2f( D3DVSDE_TEXCOORD0, (float)BORDERWIDTH, (float)(HEIGHT - BORDERHEIGHT));
m_pd3dDevice->SetVertexData4f( D3DVSDE_VERTEX, (float)g_settings.m_ResInfo[res].Overscan.left, (float) g_settings.m_ResInfo[res].Overscan.bottom, 0, 0 );
m_pd3dDevice->End();
}
void CCdgRenderer::UpdateTexture()
{
D3DLOCKED_RECT LockedRect;
m_pCdgTexture->LockRect(0, &LockedRect, NULL, 0L);
for (UINT j = 0; j < HEIGHT; j++ )
{
DWORD *texel = (DWORD *)((BYTE *)LockedRect.pBits + j * LockedRect.Pitch);
for (UINT i = 0; i < WIDTH; i++ )
{
BYTE ClutOffset = m_pCdg->GetClutOffset(j + m_pCdg->GetVOffset() , i + m_pCdg->GetHOffset());
TEX_COLOR TexColor = ConvertColor(m_pCdg->GetColor(ClutOffset));
if (TexColor >> 24) //Only override transp. for opaque alpha
{
TexColor &= 0x00FFFFFF;
if (ClutOffset == m_pCdg->GetBackgroundColor())
TexColor |= m_bgAlpha;
else
TexColor |= m_fgAlpha;
}
*texel++ = TexColor;
}
}
m_pCdgTexture->UnlockRect(0);
}
TEX_COLOR CCdgRenderer::ConvertColor(CDG_COLOR CdgColor)
{
TEX_COLOR red, green, blue, alpha;
blue = (TEX_COLOR)((CdgColor & 0x000F) * 17);
green = ((TEX_COLOR)(((CdgColor & 0x00F0) >> 4) * 17)) << 8;
red = ((TEX_COLOR)(((CdgColor & 0x0F00) >> 8) * 17)) << 16;
alpha = ((TEX_COLOR)(((CdgColor & 0xF000) >> 12) * 17)) << 24;
return alpha | red | green | blue;
}
void CCdgRenderer::SetBGalpha(TEX_COLOR alpha)
{
m_bgAlpha = alpha;
}
//CdgParser
CCdgParser::CCdgParser()
{
m_pReader = NULL;
m_pLoader = NULL;
m_pRenderer = NULL;
m_pVoiceManager = NULL;
m_bIsRunning = false;
}
CCdgParser::~CCdgParser()
{
FreeGraphics();
Free();
}
bool CCdgParser::AllocGraphics()
{
CSingleLock lock (m_CritSection);
if (!AllocRenderer()) return false;
m_pRenderer->InitGraphics();
if (m_pReader)
m_pRenderer->Attach(m_pReader);
return true;
}
void CCdgParser::FreeGraphics()
{
CSingleLock lock (m_CritSection);
if (m_pRenderer)
SAFE_DELETE(m_pRenderer);
}
bool CCdgParser::Start(CStdString strSongPath)
{
if (!StartLoader(strSongPath)) return false;
if (!StartReader()) return false;
/* make sure we have fullscreen viz */
/* hmm won't this switch to fullscreen after each track?? */
if (g_windowManager.GetActiveWindow() != WINDOW_VISUALISATION)
g_windowManager.ActivateWindow(WINDOW_VISUALISATION);
// Karaoke patch (114097) ...
if ( g_guiSettings.GetBool("karaoke.voiceenabled") )
{
CDG_VOICE_MANAGER_CONFIG VoiceConfig;
VoiceConfig.dwVoicePacketTime = 20; // 20ms (can't be lower than this)
VoiceConfig.dwMaxStoredPackets = 2;
VoiceConfig.pDSound = g_audioContext.GetDirectSoundDevice();
VoiceConfig.pCallbackContext = this;
VoiceConfig.pfnVoiceDeviceCallback = NULL;
VoiceConfig.pfnVoiceDataCallback = NULL;
StartVoice(&VoiceConfig);
}
// ... Karaoke patch (114097)
m_bIsRunning = true;
return true;
}
void CCdgParser::Stop()
{
StopReader();
StopLoader();
StopVoice();
m_bIsRunning = false;
}
void CCdgParser::Free()
{
FreeReader();
FreeLoader();
}
void CCdgParser::SetAVDelay(float fDelay)
{
CSingleLock lock (m_CritSection);
if (m_pReader)
m_pReader->SetAVDelay(fDelay);
}
void CCdgParser::SetBGTransparent(bool bTransparent /* = true */)
{
if (m_pRenderer)
{
TEX_COLOR alpha = 0;
if (!bTransparent)
alpha = 0xff000000;
m_pRenderer->SetBGalpha(alpha);
}
}
float CCdgParser::GetAVDelay()
{
CSingleLock lock (m_CritSection);
if (m_pReader)
return m_pReader->GetAVDelay();
return g_advancedSettings.m_karaokeSyncDelay;
}
void CCdgParser::Render()
{
CSingleLock lock (m_CritSection);
if (m_pRenderer)
m_pRenderer->Render();
}
bool CCdgParser::AllocLoader()
{
if (!m_pLoader)
m_pLoader = new CCdgLoader;
if (!m_pLoader) return false;
return true;
}
bool CCdgParser::AllocReader()
{
if (!m_pReader)
m_pReader = new CCdgReader;
if (!m_pReader) return false;
return true;
}
bool CCdgParser::AllocRenderer()
{
if (!m_pRenderer)
m_pRenderer = new CCdgRenderer;
if (!m_pRenderer) return false;
return true;
}
bool CCdgParser::StartLoader(CStdString strSongPath)
{
CSingleLock lock (m_CritSection);
if (!AllocLoader()) return false;
URIUtils::RemoveExtension(strSongPath);
strSongPath += ".cdg";
if (CFile::Exists(strSongPath))
{
m_pLoader->StreamFile(strSongPath);
return true;
}
return false;
}
void CCdgParser::StopLoader()
{
CSingleLock lock (m_CritSection);
if (m_pLoader)
m_pLoader->StopStream();
}
void CCdgParser::FreeLoader()
{
CSingleLock lock (m_CritSection);
if (m_pReader)
m_pReader->DetachLoader();
if (m_pLoader)
SAFE_DELETE(m_pLoader);
}
bool CCdgParser::StartReader()
{
CSingleLock lock (m_CritSection);
if (!AllocReader()) return false;
if (m_pLoader)
m_pReader->Attach(m_pLoader);
m_pReader->Start((float)g_application.GetTime());
return true;
}
void CCdgParser::StopReader()
{
CSingleLock lock (m_CritSection);
if (m_pReader)
m_pReader->StopThread();
}
void CCdgParser::FreeReader()
{
CSingleLock lock (m_CritSection);
if (m_pRenderer)
m_pRenderer->DetachReader();
if (m_pReader)
SAFE_DELETE(m_pReader);
}
// Karaoke patch (114097) ...
bool CCdgParser::AllocVoice()
{
if (!m_pVoiceManager)
m_pVoiceManager = new CCdgVoiceManager;
if (!m_pVoiceManager) return false;
return true;
}
bool CCdgParser::StartVoice(CDG_VOICE_MANAGER_CONFIG* pConfig)
{
CSingleLock lock (m_CritSection);
if (!AllocVoice()) return false;
if (m_pVoiceManager)
m_pVoiceManager->Initialize(pConfig);
return true;
}
void CCdgParser::StopVoice()
{
CSingleLock lock (m_CritSection);
if (m_pVoiceManager)
m_pVoiceManager->Shutdown();
}
void CCdgParser:: FreeVoice()
{
CSingleLock lock (m_CritSection);
if (m_pVoiceManager)
SAFE_DELETE(m_pVoiceManager);
}
void CCdgParser:: ProcessVoice()
{
CSingleLock lock (m_CritSection);
if (m_pVoiceManager)
m_pVoiceManager->ProcessVoice();
}
// ... Karaoke patch (114097)