PsfPlayer v0.30

git-svn-id: http://svn.purei.org/purei/trunk@561 b36208d7-6611-0410-8bec-b1987f11c4a2
This commit is contained in:
jpd002 2009-11-26 13:56:30 +00:00
parent 8273d783d1
commit 2188aa037f
12 changed files with 494 additions and 24 deletions

View File

@ -205,6 +205,14 @@
RelativePath=".\Source\HighResTimer.h"
>
</File>
<File
RelativePath=".\Source\Playlist.cpp"
>
</File>
<File
RelativePath=".\Source\Playlist.h"
>
</File>
<File
RelativePath=".\Source\PsfBase.cpp"
>
@ -320,6 +328,10 @@
RelativePath=".\Source\win32_ui\MiniDebugger.h"
>
</File>
<File
RelativePath=".\Source\win32_ui\Panel.h"
>
</File>
<File
RelativePath=".\Source\win32_ui\PlayerWnd.cpp"
>
@ -328,6 +340,14 @@
RelativePath=".\Source\win32_ui\PlayerWnd.h"
>
</File>
<File
RelativePath=".\Source\win32_ui\PlaylistPanel.cpp"
>
</File>
<File
RelativePath=".\Source\win32_ui\PlaylistPanel.h"
>
</File>
<File
RelativePath=".\Source\win32_ui\Res.rc"
>
@ -368,6 +388,10 @@
RelativePath=".\Source\win32_ui\StdAfx.h"
>
</File>
<File
RelativePath=".\Source\win32_ui\TimeToString.h"
>
</File>
</Filter>
</Filter>
<Filter

View File

@ -0,0 +1,28 @@
#include "Playlist.h"
CPlaylist::CPlaylist()
{
}
CPlaylist::~CPlaylist()
{
}
void CPlaylist::AddItem(const char* path, const CPsfTags& tags)
{
PLAYLIST_ITEM item;
item.title = tags.GetTagValue("title");
item.length = CPsfTags::ConvertTimeString(tags.GetTagValue("length").c_str());
m_playlistItems[path] = item;
PlaylistItemMapIterator itemIterator(m_playlistItems.find(path));
OnItemChange(itemIterator);
}
bool CPlaylist::DoesItemExist(const char* path)
{
return true;
}

View File

@ -0,0 +1,33 @@
#ifndef _PLAYLIST_H_
#define _PLAYLIST_H_
#include "PsfTags.h"
#include <boost/signal.hpp>
class CPlaylist
{
public:
struct PLAYLIST_ITEM
{
std::wstring title;
double length;
};
typedef std::map<std::string, PLAYLIST_ITEM> PlaylistItemMap;
typedef PlaylistItemMap::const_iterator PlaylistItemMapIterator;
typedef boost::signal<void (const PlaylistItemMapIterator&)> OnItemChangeEvent;
CPlaylist();
virtual ~CPlaylist();
void AddItem(const char*, const CPsfTags&);
bool DoesItemExist(const char*);
OnItemChangeEvent OnItemChange;
private:
PlaylistItemMap m_playlistItems;
};
#endif

View File

@ -32,29 +32,105 @@ void CPsfTags::SetDefaultCharEncoding(const CHAR_ENCODING& encoding)
m_defaultEncoding = encoding;
}
bool CPsfTags::HasTag(const char* tagName)
bool CPsfTags::HasTag(const char* tagName) const
{
ConstTagIterator tagIterator = m_tags.find(tagName);
return tagIterator != m_tags.end();
}
string CPsfTags::GetRawTagValue(const char* tagName)
string CPsfTags::GetRawTagValue(const char* tagName) const
{
ConstTagIterator tagIterator = m_tags.find(tagName);
if(tagIterator == m_tags.end()) return "";
return tagIterator->second;
}
wstring CPsfTags::GetTagValue(const char* tagName)
wstring CPsfTags::GetTagValue(const char* tagName) const
{
return m_stringConverter(GetRawTagValue(tagName));
}
wstring CPsfTags::DecodeTagValue(const char* value)
wstring CPsfTags::DecodeTagValue(const char* value) const
{
return m_stringConverter(string(value));
}
double CPsfTags::ConvertTimeString(const wchar_t* value)
{
double time = 0;
enum
{
STATE_CHECK_HAS_SEPARATOR,
STATE_CHECK_HAS_OTHER_SEPARATOR,
STATE_SPLIT_SECONDS_DECIMAL,
STATE_DONE
};
struct FindSeparator
{
const wchar_t* operator()(const wchar_t* string)
{
const wchar_t* separator = wcschr(string, ':');
if(separator == NULL)
{
separator = wcschr(string, ';');
}
return separator;
}
};
unsigned int currentState = STATE_CHECK_HAS_SEPARATOR;
std::wstring firstUnit;
while(currentState != STATE_DONE)
{
if(currentState == STATE_CHECK_HAS_SEPARATOR)
{
const wchar_t* separator = FindSeparator()(value);
if(separator == NULL)
{
//No colon found -> doesn't have hours or minutes
currentState = STATE_SPLIT_SECONDS_DECIMAL;
continue;
}
firstUnit = std::wstring(value, separator);
currentState = STATE_CHECK_HAS_OTHER_SEPARATOR;
value = separator + 1;
}
else if(currentState == STATE_CHECK_HAS_OTHER_SEPARATOR)
{
const wchar_t* separator = FindSeparator()(value);
std::wstring minutesStr, hoursStr;
if(separator == NULL)
{
//No colon found -> first unit is minutes
minutesStr = firstUnit;
}
else
{
hoursStr = firstUnit;
minutesStr = std::wstring(value, separator);
value = separator + 1;
}
int hours = wcstol(hoursStr.c_str(), NULL, 10);
int minutes = wcstol(minutesStr.c_str(), NULL, 10);
currentState = STATE_SPLIT_SECONDS_DECIMAL;
time += minutes * 60;
time += hours * 60 * 60;
}
else if(currentState == STATE_SPLIT_SECONDS_DECIMAL)
{
double seconds = wcstod(value, NULL);
time += seconds;
currentState = STATE_DONE;
}
}
return time;
}
CPsfTags::ConstTagIterator CPsfTags::GetTagsBegin() const
{
return m_tags.begin();

View File

@ -22,10 +22,12 @@ public:
CPsfTags(const TagMap&);
virtual ~CPsfTags();
void SetDefaultCharEncoding(const CHAR_ENCODING&);
bool HasTag(const char*);
std::string GetRawTagValue(const char*);
std::wstring GetTagValue(const char*);
std::wstring DecodeTagValue(const char*);
bool HasTag(const char*) const;
std::string GetRawTagValue(const char*) const;
std::wstring GetTagValue(const char*) const;
std::wstring DecodeTagValue(const char*) const;
static double ConvertTimeString(const wchar_t*);
ConstTagIterator GetTagsBegin() const;
ConstTagIterator GetTagsEnd() const;

View File

@ -275,7 +275,7 @@ void CPsfVm::ThreadProc()
m_OnRunningStateChange();
}
#else
if(!m_spuHandler || !m_spuHandler->HasFreeBuffers())
if(m_spuHandler && !m_spuHandler->HasFreeBuffers())
{
boost::this_thread::sleep(boost::posix_time::milliseconds(16));
m_spuHandler->RecycleBuffers();
@ -309,7 +309,10 @@ void CPsfVm::ThreadProc()
currentBlock++;
if(currentBlock == blockCount)
{
m_spuHandler->Write(samples, blockSize * blockCount, 44100);
if(m_spuHandler)
{
m_spuHandler->Write(samples, blockSize * blockCount, 44100);
}
currentBlock = 0;
}
}

View File

@ -22,9 +22,9 @@
CMainWindow::SPUHANDLER_INFO CMainWindow::m_handlerInfo[] =
{
{ 1, _T("Win32 WaveOut"), _T("SH_WaveOut.dll") },
{ 2, _T("OpenAL"), _T("SH_OpenAL.dll") },
{ NULL, NULL, NULL },
{ 1, _T("Win32 WaveOut"), _T("SH_WaveOut.dll") },
{ 2, _T("OpenAL"), _T("SH_OpenAL.dll") },
{ NULL, NULL, NULL },
};
using namespace Framework;
@ -35,19 +35,27 @@ m_virtualMachine(virtualMachine),
m_ready(false),
m_frames(0),
m_writes(0),
m_selectedAudioHandler(0),
m_ejectButton(NULL),
m_pauseButton(NULL),
m_aboutButton(NULL)
m_aboutButton(NULL),
m_playlistPanel(NULL)
{
m_selectedAudioHandler = m_handlerInfo[0].id;
for(unsigned int i = 0; i < MAX_PANELS; i++)
{
m_panels[i] = NULL;
}
if(!DoesWindowClassExist(CLSNAME))
if(!DoesWindowClassExist(CLSNAME))
{
RegisterClassEx(&Win32::CWindow::MakeWndClass(CLSNAME));
}
// Create(WNDSTYLEEX, CLSNAME, APP_NAME, WNDSTYLE, Win32::CRect(0, 0, 320, 480), NULL, NULL);
Create(WNDSTYLEEX, CLSNAME, APP_NAME, WNDSTYLE, Win32::CRect(0, 0, 200, 175), NULL, NULL);
Win32::CRect clientRect(0, 0, 320, 480);
AdjustWindowRectEx(clientRect, WNDSTYLE, FALSE, WNDSTYLEEX);
Create(WNDSTYLEEX, CLSNAME, APP_NAME, WNDSTYLE, clientRect, NULL, NULL);
SetClassPtr();
SetTimer(m_hWnd, 0, 1000, NULL);
@ -61,11 +69,13 @@ m_aboutButton(NULL)
m_virtualMachine.OnNewFrame.connect(std::tr1::bind(&CMainWindow::OnNewFrame, this));
m_virtualMachine.OnBufferWrite.connect(std::tr1::bind(&CMainWindow::OnBufferWrite, this, std::tr1::placeholders::_1));
m_virtualMachine.SetSpuHandler(std::tr1::bind(&CMainWindow::CreateHandler, m_handlerInfo[0].dllName));
ChangeAudioPlugin(0);
m_timerLabel = new Win32::CStatic(m_hWnd, _T(""), SS_CENTER);
m_titleLabel = new Win32::CStatic(m_hWnd, _T(""), SS_CENTER | SS_NOPREFIX);
m_placeHolder = new Win32::CStatic(m_hWnd, Win32::CRect(0, 0, 1, 1), SS_BLACKRECT);
m_nextButton = new Win32::CButton(_T(">>"), m_hWnd, Win32::CRect(0, 0, 1, 1));
m_prevButton = new Win32::CButton(_T("<<"), m_hWnd, Win32::CRect(0, 0, 1, 1));
m_pauseButton = new Win32::CButton(_T("Pause"), m_hWnd, Win32::CRect(0, 0, 1, 1));
@ -77,7 +87,7 @@ m_aboutButton(NULL)
VerticalLayoutContainer(
LayoutExpression(Win32::CLayoutWindow::CreateTextBoxBehavior(100, 15, m_titleLabel)) +
LayoutExpression(Win32::CLayoutWindow::CreateTextBoxBehavior(100, 15, m_timerLabel)) +
LayoutExpression(CLayoutStretch::Create()) +
LayoutExpression(Win32::CLayoutWindow::CreateCustomBehavior(300, 200, 1, 1, m_placeHolder)) +
HorizontalLayoutContainer(
LayoutExpression(Win32::CLayoutWindow::CreateTextBoxBehavior(100, 30, m_prevButton)) +
LayoutExpression(CLayoutStretch::Create()) +
@ -92,6 +102,7 @@ m_aboutButton(NULL)
)
);
//Create tray icon
Win32::CTrayIcon* trayIcon = m_trayIconServer.Insert();
trayIcon->SetIcon(LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_MAIN)));
trayIcon->SetTip(_T("PsfPlayer"));
@ -99,12 +110,21 @@ m_aboutButton(NULL)
m_trayIconServer.RegisterHandler(std::tr1::bind(&CMainWindow::OnTrayIconEvent, this,
std::tr1::placeholders::_1, std::tr1::placeholders::_2));
//Create play list panel
m_playlistPanel = new CPlaylistPanel(m_placeHolder->m_hWnd, m_playlist);
m_playlistPanel->OnItemDblClick.connect(std::tr1::bind(&CMainWindow::OnPlaylistItemDblClick, this, std::tr1::placeholders::_1));
//Initialize panels
m_panels[0] = m_playlistPanel;
CreateAudioPluginMenu();
UpdateAudioPluginMenu();
UpdateTimer();
UpdateTitle();
UpdateButtons();
RefreshLayout();
m_panels[0]->Show(SW_SHOW);
}
CMainWindow::~CMainWindow()
@ -117,14 +137,42 @@ void CMainWindow::RefreshLayout()
RECT rc = GetClientRect();
m_layout->SetRect(rc.left + 10, rc.top + 10, rc.right - 10, rc.bottom - 10);
m_layout->RefreshGeometry();
for(unsigned int i = 0; i < MAX_PANELS; i++)
{
CPanel* panel(m_panels[i]);
if(panel != NULL)
{
panel->RefreshLayout();
}
}
}
CSoundHandler* CMainWindow::CreateHandler(const TCHAR* libraryPath)
{
typedef CSoundHandler* (*HandlerFactoryFunction)();
HMODULE module = LoadLibrary(libraryPath);
HandlerFactoryFunction handlerFactory = reinterpret_cast<HandlerFactoryFunction>(GetProcAddress(module, "HandlerFactory"));
CSoundHandler* result = handlerFactory();
CSoundHandler* result = NULL;
try
{
HMODULE module = LoadLibrary(libraryPath);
if(module == NULL)
{
throw std::exception();
}
HandlerFactoryFunction handlerFactory = reinterpret_cast<HandlerFactoryFunction>(GetProcAddress(module, "HandlerFactory"));
if(handlerFactory == NULL)
{
throw std::exception();
}
result = handlerFactory();
}
catch(...)
{
tstring errorMessage = _T("Couldn't create sound handler present in '") + tstring(libraryPath) + _T("'.");
MessageBox(m_hWnd, errorMessage.c_str(), APP_NAME, 16);
}
return result;
}
@ -196,6 +244,11 @@ long CMainWindow::OnSize(unsigned int type, unsigned int width, unsigned int hei
return TRUE;
}
void CMainWindow::OnPlaylistItemDblClick(const char* path)
{
Load(path);
}
void CMainWindow::OnTrayIconEvent(Win32::CTrayIcon* icon, LPARAM param)
{
switch(param)
@ -297,7 +350,7 @@ void CMainWindow::ChangeAudioPlugin(unsigned int pluginIdx)
{
SPUHANDLER_INFO* handlerInfo = m_handlerInfo + pluginIdx;
m_selectedAudioHandler = handlerInfo->id;
m_virtualMachine.SetSpuHandler(std::tr1::bind(&CMainWindow::CreateHandler, handlerInfo->dllName));
m_virtualMachine.SetSpuHandler(std::tr1::bind(&CMainWindow::CreateHandler, this, handlerInfo->dllName));
UpdateAudioPluginMenu();
}
@ -365,6 +418,9 @@ void CMainWindow::Load(const char* path)
}
m_virtualMachine.Resume();
m_playlist.AddItem(path, m_tags);
m_ready = true;
}
catch(const exception& except)
@ -374,6 +430,7 @@ void CMainWindow::Load(const char* path)
MessageBox(m_hWnd, errorString.c_str(), NULL, 16);
m_ready = false;
}
UpdateTitle();
UpdateButtons();
UpdateTimer();

View File

@ -4,11 +4,14 @@
#include "../PsfVm.h"
#include "../PsfBase.h"
#include "../PsfTags.h"
#include "../Playlist.h"
#include "win32/Window.h"
#include "win32/Static.h"
#include "win32/Button.h"
#include "win32/Layouts.h"
#include "win32/TrayIconServer.h"
#include "Panel.h"
#include "PlaylistPanel.h"
class CMainWindow : public Framework::Win32::CWindow
{
@ -24,6 +27,11 @@ protected:
long OnTimer();
private:
enum
{
MAX_PANELS = 1,
};
struct SPUHANDLER_INFO
{
int id;
@ -31,7 +39,7 @@ private:
const TCHAR* dllName;
};
static CSoundHandler* CreateHandler(const TCHAR*);
CSoundHandler* CreateHandler(const TCHAR*);
void OnNewFrame();
void OnBufferWrite(int);
@ -41,6 +49,7 @@ private:
void OnAbout();
void Load(const char*);
void OnPlaylistItemDblClick(const char*);
void OnTrayIconEvent(Framework::Win32::CTrayIcon*, LPARAM);
void DisplayTrayMenu();
void UpdateTimer();
@ -55,6 +64,8 @@ private:
Framework::Win32::CStatic* m_titleLabel;
Framework::LayoutObjectPtr m_layout;
Framework::Win32::CStatic* m_placeHolder;
Framework::Win32::CButton* m_nextButton;
Framework::Win32::CButton* m_prevButton;
Framework::Win32::CButton* m_pauseButton;
@ -63,10 +74,14 @@ private:
Framework::Win32::CTrayIconServer m_trayIconServer;
CPanel* m_panels[MAX_PANELS];
CPlaylistPanel* m_playlistPanel;
HMENU m_popupMenu;
CPsfVm& m_virtualMachine;
CPsfTags m_tags;
CPlaylist m_playlist;
bool m_ready;
uint64 m_frames;
int m_writes;

View File

@ -0,0 +1,16 @@
#ifndef _PANEL_H_
#define _PANEL_H_
#include "win32/Window.h"
class CPanel : public Framework::Win32::CWindow
{
public:
virtual ~CPanel() {}
virtual void RefreshLayout() = 0;
private:
};
#endif

View File

@ -0,0 +1,150 @@
#include "PlaylistPanel.h"
#include "win32/Rect.h"
#include "TimeToString.h"
#include "string_cast.h"
#define CLSNAME _T("PlaylistPanel")
#define WNDSTYLE (WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CHILD)
#define WNDSTYLEEX (0)
#define WM_UPDATEVIS (WM_USER + 1)
using namespace Framework;
CPlaylistPanel::CPlaylistPanel(HWND parentWnd, CPlaylist& playlist)
: m_playlistView(NULL)
, m_playlist(playlist)
, m_nextListViewKey(0)
{
if(!DoesWindowClassExist(CLSNAME))
{
RegisterClassEx(&Win32::CWindow::MakeWndClass(CLSNAME));
}
Create(WNDSTYLEEX, CLSNAME, _T(""), WNDSTYLE, Win32::CRect(0, 0, 1, 1), parentWnd, NULL);
SetClassPtr();
m_playlistView = new Win32::CListView(m_hWnd, Win32::CRect(0, 0, 1, 1), LVS_REPORT | LVS_SORTASCENDING);
m_playlistView->SetExtendedListViewStyle(m_playlistView->GetExtendedListViewStyle() | LVS_EX_FULLROWSELECT);
CreateColumns();
m_playlist.OnItemChange.connect(std::tr1::bind(&CPlaylistPanel::OnPlaylistItemChange, this, std::tr1::placeholders::_1));
m_nextListViewKey = 0;
}
CPlaylistPanel::~CPlaylistPanel()
{
}
long CPlaylistPanel::OnNotify(WPARAM wParam, NMHDR* hdr)
{
if(CWindow::IsNotifySource(m_playlistView, hdr))
{
switch(hdr->code)
{
case NM_DBLCLK:
OnPlaylistViewDblClick(reinterpret_cast<NMITEMACTIVATE*>(hdr));
break;
}
}
return FALSE;
}
void CPlaylistPanel::OnPlaylistViewDblClick(NMITEMACTIVATE* itemActivate)
{
if(itemActivate->iItem == -1) return;
uint32 param = m_playlistView->GetItemData(itemActivate->iItem);
ListViewKeyMap::right_const_iterator keyIterator = m_listViewKeys.right.find(param);
assert(keyIterator != m_listViewKeys.right.end());
if(keyIterator != m_listViewKeys.right.end())
{
OnItemDblClick(keyIterator->second.c_str());
}
}
void CPlaylistPanel::AddItem(uint32 key, const TCHAR* title, const TCHAR* length)
{
LVITEM item;
memset(&item, 0, sizeof(LVITEM));
item.pszText = const_cast<TCHAR*>(title);
item.lParam = key;
item.mask = LVIF_TEXT | LVIF_PARAM;
int itemIdx = m_playlistView->InsertItem(&item);
// m_playlist->SetItemText(itemIndex, 1, TimeToString<std::tstring>(length).c_str());
m_playlistView->SetItemText(itemIdx, 1, length);
}
void CPlaylistPanel::ModifyItem(uint32 key, const TCHAR* title, const TCHAR* length)
{
int itemIdx = m_playlistView->FindItemData(key);
assert(itemIdx != -1);
if(itemIdx == -1) return;
m_playlistView->SetItemText(itemIdx, 0, title);
m_playlistView->SetItemText(itemIdx, 1, length);
}
void CPlaylistPanel::CreateColumns()
{
LVCOLUMN column;
memset(&column, 0, sizeof(LVCOLUMN));
column.pszText = _T("Title");
column.mask = LVCF_TEXT;
m_playlistView->InsertColumn(0, &column);
memset(&column, 0, sizeof(LVCOLUMN));
column.pszText = _T("Length");
column.mask = LVCF_TEXT;
m_playlistView->InsertColumn(1, &column);
}
void CPlaylistPanel::OnPlaylistItemChange(const CPlaylist::PlaylistItemMapIterator& itemIterator)
{
const CPlaylist::PLAYLIST_ITEM& item(itemIterator->second);
std::tstring title, length;
title = string_cast<std::tstring>(item.title);
length = TimeToString<std::tstring>(item.length);
ListViewKeyMap::left_const_iterator keyIterator(m_listViewKeys.left.find(itemIterator->first));
if(keyIterator == m_listViewKeys.left.end())
{
//Element
uint32 newKey = ++m_nextListViewKey;
m_listViewKeys.insert(ListViewKeyMap::value_type(itemIterator->first, newKey));
AddItem(newKey, title.c_str(), length.c_str());
}
else
{
uint32 key = keyIterator->second;
ModifyItem(key, title.c_str(), length.c_str());
}
}
void CPlaylistPanel::RefreshLayout()
{
//Resize panel
{
Win32::CRect clientRect(0, 0, 0, 0);
::GetClientRect(GetParent(), clientRect);
SetSizePosition(clientRect);
}
//Resize list view
{
Win32::CRect clientRect(0, 0, 0, 0);
GetClientRect(clientRect);
m_playlistView->SetSizePosition(clientRect);
}
//Resize columns
{
RECT clientRect = m_playlistView->GetClientRect();
m_playlistView->SetColumnWidth(0, 3 * clientRect.right / 4);
m_playlistView->SetColumnWidth(1, 1 * clientRect.right / 4);
}
}

View File

@ -0,0 +1,40 @@
#ifndef _PLAYLISTPANEL_H_
#define _PLAYLISTPANEL_H_
#include "Panel.h"
#include "Playlist.h"
#include "win32/ListView.h"
#include <boost/bimap.hpp>
class CPlaylistPanel : public CPanel, public boost::signals::trackable
{
public:
typedef boost::signal<void (const char*)> OnItemDblClickEvent;
CPlaylistPanel(HWND, CPlaylist&);
virtual ~CPlaylistPanel();
void RefreshLayout();
OnItemDblClickEvent OnItemDblClick;
protected:
long OnNotify(WPARAM, NMHDR*);
private:
typedef boost::bimap<std::string, unsigned int> ListViewKeyMap;
void CreateColumns();
void OnPlaylistItemChange(const CPlaylist::PlaylistItemMapIterator&);
void AddItem(uint32, const TCHAR*, const TCHAR*);
void ModifyItem(uint32, const TCHAR*, const TCHAR*);
void OnPlaylistViewDblClick(NMITEMACTIVATE*);
CPlaylist& m_playlist;
Framework::Win32::CListView* m_playlistView;
ListViewKeyMap m_listViewKeys;
uint32 m_nextListViewKey;
};
#endif

View File

@ -0,0 +1,26 @@
#ifndef _TIMETOSTRING_H_
#define _TIMETOSTRING_H_
#include <string>
template <typename StringType>
static StringType TimeToString(double time)
{
// StringType result, separator;
StringType separator;
separator += ':';
unsigned int secs = static_cast<unsigned int>(time) % 60;
unsigned int mins = static_cast<unsigned int>(time) / 60;
std::basic_stringstream<StringType::value_type> result;
result << mins << separator;
result.width(2);
result.fill('0');
result << secs;
// result =
// boost::lexical_cast<StringType>(mins) +
// separator +
// boost::lexical_cast<StringType>(secs);
return result.str();
}
#endif