Updated MSVC6 project files, bundled necessary parts of foobar2000 0.8.3 SDK
This commit is contained in:
parent
7a88e3e80d
commit
16c57263de
|
@ -43,7 +43,7 @@ RSC=rc.exe
|
||||||
# PROP Ignore_Export_Lib 0
|
# PROP Ignore_Export_Lib 0
|
||||||
# PROP Target_Dir ""
|
# PROP Target_Dir ""
|
||||||
# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "FOO_XSF8_EXPORTS" /YX /FD /c
|
# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "FOO_XSF8_EXPORTS" /YX /FD /c
|
||||||
# ADD CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "FOO_XSF8_EXPORTS" /YX /FD /c
|
# ADD CPP /nologo /MD /W3 /GX /O2 /I "src" /I "src/foobar8" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "FOO_XSF8_EXPORTS" /YX /FD /c
|
||||||
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
||||||
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
||||||
# ADD BASE RSC /l 0x411 /d "NDEBUG"
|
# ADD BASE RSC /l 0x411 /d "NDEBUG"
|
||||||
|
@ -53,7 +53,7 @@ BSC32=bscmake.exe
|
||||||
# ADD BSC32 /nologo
|
# ADD BSC32 /nologo
|
||||||
LINK32=link.exe
|
LINK32=link.exe
|
||||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
|
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
|
||||||
# ADD LINK32 pfc.lib foobar2000_SDK.lib foobar2000_sdk_helpers.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"foo_8_vio2sf.dll"
|
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"foo_8_vio2sf.dll"
|
||||||
|
|
||||||
!ELSEIF "$(CFG)" == "foo_xsf8 - Win32 Debug"
|
!ELSEIF "$(CFG)" == "foo_xsf8 - Win32 Debug"
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ LINK32=link.exe
|
||||||
# PROP Ignore_Export_Lib 0
|
# PROP Ignore_Export_Lib 0
|
||||||
# PROP Target_Dir ""
|
# PROP Target_Dir ""
|
||||||
# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "FOO_XSF8_EXPORTS" /YX /FD /GZ /c
|
# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "FOO_XSF8_EXPORTS" /YX /FD /GZ /c
|
||||||
# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "FOO_XSF8_EXPORTS" /YX /FD /GZ /c
|
# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "src/foobar8" /I "src" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "FOO_XSF8_EXPORTS" /YX /FD /GZ /c
|
||||||
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
|
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
|
||||||
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
|
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
|
||||||
# ADD BASE RSC /l 0x411 /d "_DEBUG"
|
# ADD BASE RSC /l 0x411 /d "_DEBUG"
|
||||||
|
@ -79,7 +79,7 @@ BSC32=bscmake.exe
|
||||||
# ADD BSC32 /nologo
|
# ADD BSC32 /nologo
|
||||||
LINK32=link.exe
|
LINK32=link.exe
|
||||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
|
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
|
||||||
# ADD LINK32 pfcD.lib foobar2000_SDKD.lib foobar2000_sdk_helpersD.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"C:\Program Files\foobar2000_8\components\foo_8_vio2sf.dll" /pdbtype:sept
|
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"foo_8_vio2sf.dll" /pdbtype:sept
|
||||||
|
|
||||||
!ENDIF
|
!ENDIF
|
||||||
|
|
||||||
|
|
|
@ -1,24 +1,24 @@
|
||||||
# Microsoft Developer Studio Project File - Name="in_xsf" - Package Owner=<4>
|
# Microsoft Developer Studio Project File - Name="in_xsf" - Package Owner=<4>
|
||||||
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||||
# ** 編集しないでください **
|
# ** DO NOT EDIT **
|
||||||
|
|
||||||
# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
|
# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
|
||||||
|
|
||||||
CFG=in_xsf - Win32 Debug
|
CFG=in_xsf - Win32 Debug
|
||||||
!MESSAGE これは有効なメイクファイルではありません。 このプロジェクトをビルドするためには NMAKE を使用してください。
|
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
||||||
!MESSAGE [メイクファイルのエクスポート] コマンドを使用して実行してください
|
!MESSAGE use the Export Makefile command and run
|
||||||
!MESSAGE
|
!MESSAGE
|
||||||
!MESSAGE NMAKE /f "in_xsf.mak".
|
!MESSAGE NMAKE /f "in_xsf.mak".
|
||||||
!MESSAGE
|
!MESSAGE
|
||||||
!MESSAGE NMAKE の実行時に構成を指定できます
|
!MESSAGE You can specify a configuration when running NMAKE
|
||||||
!MESSAGE コマンド ライン上でマクロの設定を定義します。例:
|
!MESSAGE by defining the macro CFG on the command line. For example:
|
||||||
!MESSAGE
|
!MESSAGE
|
||||||
!MESSAGE NMAKE /f "in_xsf.mak" CFG="in_xsf - Win32 Debug"
|
!MESSAGE NMAKE /f "in_xsf.mak" CFG="in_xsf - Win32 Debug"
|
||||||
!MESSAGE
|
!MESSAGE
|
||||||
!MESSAGE 選択可能なビルド モード:
|
!MESSAGE Possible choices for configuration are:
|
||||||
!MESSAGE
|
!MESSAGE
|
||||||
!MESSAGE "in_xsf - Win32 Release" ("Win32 (x86) Dynamic-Link Library" 用)
|
!MESSAGE "in_xsf - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
|
||||||
!MESSAGE "in_xsf - Win32 Debug" ("Win32 (x86) Dynamic-Link Library" 用)
|
!MESSAGE "in_xsf - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
|
||||||
!MESSAGE
|
!MESSAGE
|
||||||
|
|
||||||
# Begin Project
|
# Begin Project
|
||||||
|
@ -43,7 +43,7 @@ RSC=rc.exe
|
||||||
# PROP Ignore_Export_Lib 0
|
# PROP Ignore_Export_Lib 0
|
||||||
# PROP Target_Dir ""
|
# PROP Target_Dir ""
|
||||||
# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "IN_XSF_EXPORTS" /YX /FD /c
|
# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "IN_XSF_EXPORTS" /YX /FD /c
|
||||||
# ADD CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "IN_XSF_EXPORTS" /FD /c
|
# ADD CPP /nologo /MD /W3 /GX /O2 /I "src" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "IN_XSF_EXPORTS" /FD /c
|
||||||
# SUBTRACT CPP /YX
|
# SUBTRACT CPP /YX
|
||||||
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
||||||
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
||||||
|
@ -70,7 +70,7 @@ LINK32=link.exe
|
||||||
# PROP Ignore_Export_Lib 0
|
# PROP Ignore_Export_Lib 0
|
||||||
# PROP Target_Dir ""
|
# PROP Target_Dir ""
|
||||||
# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "IN_XSF_EXPORTS" /YX /FD /GZ /c
|
# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "IN_XSF_EXPORTS" /YX /FD /GZ /c
|
||||||
# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "src/aosdk" /I "src/aosdk/zlib" /D "LSB_FIRST" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "IN_XSF_EXPORTS" /YX /FD /GZ /c
|
# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "src/aosdk" /I "src/aosdk/zlib" /I "src" /D "LSB_FIRST" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "IN_XSF_EXPORTS" /YX /FD /GZ /c
|
||||||
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
|
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
|
||||||
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
|
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
|
||||||
# ADD BASE RSC /l 0x411 /d "_DEBUG"
|
# ADD BASE RSC /l 0x411 /d "_DEBUG"
|
||||||
|
@ -80,7 +80,7 @@ BSC32=bscmake.exe
|
||||||
# ADD BSC32 /nologo
|
# ADD BSC32 /nologo
|
||||||
LINK32=link.exe
|
LINK32=link.exe
|
||||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
|
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
|
||||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"C:\Program Files\Winamp\Plugins/in_vio2sf.dll" /pdbtype:sept
|
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"in_vio2sf.dll" /pdbtype:sept
|
||||||
|
|
||||||
!ENDIF
|
!ENDIF
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ BSC32=bscmake.exe
|
||||||
# ADD BSC32 /nologo
|
# ADD BSC32 /nologo
|
||||||
LINK32=link.exe
|
LINK32=link.exe
|
||||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
|
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
|
||||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /pdb:none /machine:I386 /out:"C:\Program Files\Winamp\Plugins/in_vio2sfu.dll"
|
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /pdb:none /machine:I386 /out:"in_vio2sfu.dll"
|
||||||
|
|
||||||
!ELSEIF "$(CFG)" == "in_xsfu - Win32 Debug"
|
!ELSEIF "$(CFG)" == "in_xsfu - Win32 Debug"
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ BSC32=bscmake.exe
|
||||||
# ADD BSC32 /nologo
|
# ADD BSC32 /nologo
|
||||||
LINK32=link.exe
|
LINK32=link.exe
|
||||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
|
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
|
||||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"C:\Program Files\Winamp\Plugins/in_vio2sfu.dll" /pdbtype:sept
|
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"in_vio2sfu.dll" /pdbtype:sept
|
||||||
|
|
||||||
!ENDIF
|
!ENDIF
|
||||||
|
|
||||||
|
|
|
@ -1,24 +1,24 @@
|
||||||
# Microsoft Developer Studio Project File - Name="kpixsf" - Package Owner=<4>
|
# Microsoft Developer Studio Project File - Name="kpixsf" - Package Owner=<4>
|
||||||
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||||
# ** 編集しないでください **
|
# ** DO NOT EDIT **
|
||||||
|
|
||||||
# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
|
# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
|
||||||
|
|
||||||
CFG=kpixsf - Win32 Debug
|
CFG=kpixsf - Win32 Debug
|
||||||
!MESSAGE これは有効なメイクファイルではありません。 このプロジェクトをビルドするためには NMAKE を使用してください。
|
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
||||||
!MESSAGE [メイクファイルのエクスポート] コマンドを使用して実行してください
|
!MESSAGE use the Export Makefile command and run
|
||||||
!MESSAGE
|
!MESSAGE
|
||||||
!MESSAGE NMAKE /f "kpixsf.mak".
|
!MESSAGE NMAKE /f "kpixsf.mak".
|
||||||
!MESSAGE
|
!MESSAGE
|
||||||
!MESSAGE NMAKE の実行時に構成を指定できます
|
!MESSAGE You can specify a configuration when running NMAKE
|
||||||
!MESSAGE コマンド ライン上でマクロの設定を定義します。例:
|
!MESSAGE by defining the macro CFG on the command line. For example:
|
||||||
!MESSAGE
|
!MESSAGE
|
||||||
!MESSAGE NMAKE /f "kpixsf.mak" CFG="kpixsf - Win32 Debug"
|
!MESSAGE NMAKE /f "kpixsf.mak" CFG="kpixsf - Win32 Debug"
|
||||||
!MESSAGE
|
!MESSAGE
|
||||||
!MESSAGE 選択可能なビルド モード:
|
!MESSAGE Possible choices for configuration are:
|
||||||
!MESSAGE
|
!MESSAGE
|
||||||
!MESSAGE "kpixsf - Win32 Release" ("Win32 (x86) Dynamic-Link Library" 用)
|
!MESSAGE "kpixsf - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
|
||||||
!MESSAGE "kpixsf - Win32 Debug" ("Win32 (x86) Dynamic-Link Library" 用)
|
!MESSAGE "kpixsf - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
|
||||||
!MESSAGE
|
!MESSAGE
|
||||||
|
|
||||||
# Begin Project
|
# Begin Project
|
||||||
|
@ -80,7 +80,7 @@ BSC32=bscmake.exe
|
||||||
# ADD BSC32 /nologo
|
# ADD BSC32 /nologo
|
||||||
LINK32=link.exe
|
LINK32=link.exe
|
||||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
|
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
|
||||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib shlwapi.lib /nologo /dll /debug /machine:I386 /out:"C:\usr\bin\kbmed242_beta4\Plugins/vio2sf.kpi" /pdbtype:sept
|
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib shlwapi.lib /nologo /dll /debug /machine:I386 /out:"vio2sf.kpi" /pdbtype:sept
|
||||||
|
|
||||||
!ENDIF
|
!ENDIF
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,323 @@
|
||||||
|
#include "foobar2000.h"
|
||||||
|
|
||||||
|
bool audio_chunk::set_data(const audio_sample * src,unsigned samples,unsigned nch,unsigned srate)
|
||||||
|
{
|
||||||
|
bool rv = false;
|
||||||
|
unsigned size = samples * nch;
|
||||||
|
audio_sample * out = check_data_size(size);
|
||||||
|
if (out)
|
||||||
|
{
|
||||||
|
if (src)
|
||||||
|
mem_ops<audio_sample>::copy(out,src,size);
|
||||||
|
else
|
||||||
|
mem_ops<audio_sample>::set(out,0,size);
|
||||||
|
set_sample_count(samples);
|
||||||
|
set_channels(nch);
|
||||||
|
set_srate(srate);
|
||||||
|
rv = true;
|
||||||
|
}
|
||||||
|
else reset();
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool check_exclusive(unsigned val, unsigned mask)
|
||||||
|
{
|
||||||
|
return (val&mask)!=0 && (val&mask)!=mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
//leet superfast fixedpoint->floatingpoint convertah
|
||||||
|
//somewhat big though due to excess templating
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
template<class T,bool b_swap,bool b_signed,bool b_pad> class msvc6_sucks_v2 { public:
|
||||||
|
inline static void do_fixedpoint_convert(const void * source,unsigned bps,unsigned count,audio_sample* buffer)
|
||||||
|
{
|
||||||
|
const char * src = (const char *) source;
|
||||||
|
unsigned bytes = bps>>3;
|
||||||
|
unsigned n;
|
||||||
|
T max = ((T)1)<<(bps-1);
|
||||||
|
|
||||||
|
T negmask = - max;
|
||||||
|
|
||||||
|
ASSUME(bytes<=sizeof(T));
|
||||||
|
|
||||||
|
double div = 1.0 / (double)(1<<(bps-1));
|
||||||
|
for(n=0;n<count;n++)
|
||||||
|
{
|
||||||
|
T temp;
|
||||||
|
if (b_pad)
|
||||||
|
{
|
||||||
|
temp = 0;
|
||||||
|
memcpy(&temp,src,bytes);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
temp = * reinterpret_cast<const T*>(src);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (b_swap) byte_order_helper::swap_order(&temp,bytes);
|
||||||
|
|
||||||
|
if (!b_signed) temp ^= max;
|
||||||
|
|
||||||
|
if (b_pad)
|
||||||
|
{
|
||||||
|
if (temp & max) temp |= negmask;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (b_pad)
|
||||||
|
src += bytes;
|
||||||
|
else
|
||||||
|
src += sizeof(T);
|
||||||
|
|
||||||
|
|
||||||
|
buffer[n] = (audio_sample) ( (double)temp * div );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T,bool b_pad> class msvc6_sucks { public:
|
||||||
|
inline static void do_fixedpoint_convert(bool b_swap,bool b_signed,const void * source,unsigned bps,unsigned count,audio_sample* buffer)
|
||||||
|
{
|
||||||
|
if (sizeof(T)==1)
|
||||||
|
{
|
||||||
|
if (b_signed)
|
||||||
|
{
|
||||||
|
msvc6_sucks_v2<T,false,true,b_pad>::do_fixedpoint_convert(source,bps,count,buffer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
msvc6_sucks_v2<T,false,false,b_pad>::do_fixedpoint_convert(source,bps,count,buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (b_swap)
|
||||||
|
{
|
||||||
|
if (b_signed)
|
||||||
|
{
|
||||||
|
msvc6_sucks_v2<T,true,true,b_pad>::do_fixedpoint_convert(source,bps,count,buffer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
msvc6_sucks_v2<T,true,false,b_pad>::do_fixedpoint_convert(source,bps,count,buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (b_signed)
|
||||||
|
{
|
||||||
|
msvc6_sucks_v2<T,false,true,b_pad>::do_fixedpoint_convert(source,bps,count,buffer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
msvc6_sucks_v2<T,false,false,b_pad>::do_fixedpoint_convert(source,bps,count,buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
bool audio_chunk::set_data_fixedpoint_ex(const void * source,unsigned size,unsigned srate,unsigned nch,unsigned bps,unsigned flags)
|
||||||
|
{
|
||||||
|
assert( check_exclusive(flags,FLAG_SIGNED|FLAG_UNSIGNED) );
|
||||||
|
assert( check_exclusive(flags,FLAG_LITTLE_ENDIAN|FLAG_BIG_ENDIAN) );
|
||||||
|
|
||||||
|
bool need_swap = !!(flags & FLAG_BIG_ENDIAN);
|
||||||
|
if (byte_order_helper::machine_is_big_endian()) need_swap = !need_swap;
|
||||||
|
|
||||||
|
unsigned count = size / (bps/8);
|
||||||
|
audio_sample * buffer = check_data_size(count);
|
||||||
|
if (buffer==0) {reset();return false;}
|
||||||
|
bool b_signed = !!(flags & FLAG_SIGNED);
|
||||||
|
|
||||||
|
switch(bps)
|
||||||
|
{
|
||||||
|
case 8:
|
||||||
|
msvc6_sucks<char,false>::do_fixedpoint_convert(need_swap,b_signed,source,bps,count,buffer);
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
msvc6_sucks<short,false>::do_fixedpoint_convert(need_swap,b_signed,source,bps,count,buffer);
|
||||||
|
break;
|
||||||
|
case 24:
|
||||||
|
msvc6_sucks<long,true>::do_fixedpoint_convert(need_swap,b_signed,source,bps,count,buffer);
|
||||||
|
break;
|
||||||
|
case 32:
|
||||||
|
msvc6_sucks<long,false>::do_fixedpoint_convert(need_swap,b_signed,source,bps,count,buffer);
|
||||||
|
break;
|
||||||
|
case 40:
|
||||||
|
case 48:
|
||||||
|
case 56:
|
||||||
|
case 64:
|
||||||
|
msvc6_sucks<__int64,true>::do_fixedpoint_convert(need_swap,b_signed,source,bps,count,buffer);
|
||||||
|
break;
|
||||||
|
/*
|
||||||
|
additional template would bump size while i doubt if anyone playing PCM above 32bit would care about performance gain
|
||||||
|
msvc6_sucks<__int64,false>::do_fixedpoint_convert(need_swap,b_signed,source,bps,count,buffer);
|
||||||
|
break;*/
|
||||||
|
default:
|
||||||
|
//unknown size, cant convert
|
||||||
|
mem_ops<audio_sample>::setval(buffer,0,count);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
set_sample_count(count/nch);
|
||||||
|
set_srate(srate);
|
||||||
|
set_channels(nch);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool audio_chunk::set_data_floatingpoint_ex(const void * ptr,unsigned size,unsigned srate,unsigned nch,unsigned bps,unsigned flags)
|
||||||
|
{
|
||||||
|
assert(bps==32 || bps==64);
|
||||||
|
assert( check_exclusive(flags,FLAG_LITTLE_ENDIAN|FLAG_BIG_ENDIAN) );
|
||||||
|
assert( ! (flags & (FLAG_SIGNED|FLAG_UNSIGNED) ) );
|
||||||
|
|
||||||
|
void (* p_orderfunc)(void * ptr,unsigned bytes) = (flags & FLAG_BIG_ENDIAN) ? byte_order_helper::order_be_to_native : byte_order_helper::order_le_to_native;
|
||||||
|
|
||||||
|
unsigned bytes_per_sample = bps>>3;
|
||||||
|
|
||||||
|
unsigned count = size / bytes_per_sample;
|
||||||
|
audio_sample * out = check_data_size(count);
|
||||||
|
if (out==0) {reset();return false;}
|
||||||
|
unsigned char temp[64/8];
|
||||||
|
const unsigned char * src = (const unsigned char *) ptr;
|
||||||
|
while(count)
|
||||||
|
{
|
||||||
|
audio_sample val;
|
||||||
|
memcpy(temp,src,bytes_per_sample);
|
||||||
|
p_orderfunc(temp,bytes_per_sample);
|
||||||
|
if (bps==32) val = (audio_sample) *(float*)&temp;
|
||||||
|
else val = (audio_sample) *(double*)&temp;
|
||||||
|
*(out++) = val;
|
||||||
|
src += bytes_per_sample;
|
||||||
|
count--;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if audio_sample_size == 64
|
||||||
|
bool audio_chunk::set_data_32(const float * src,UINT samples,UINT nch,UINT srate)
|
||||||
|
{
|
||||||
|
unsigned size = samples * nch;
|
||||||
|
audio_sample * out = check_data_size(size);
|
||||||
|
if (out==0) {reset();return false;}
|
||||||
|
dsp_util::convert_32_to_64(src,out,size);
|
||||||
|
set_sample_count(samples);
|
||||||
|
set_channels(nch);
|
||||||
|
set_srate(srate);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
bool audio_chunk::set_data_64(const double * src,UINT samples,UINT nch,UINT srate)
|
||||||
|
{
|
||||||
|
unsigned size = samples * nch;
|
||||||
|
audio_sample * out = check_data_size(size);
|
||||||
|
if (out==0) {reset();return false;}
|
||||||
|
dsp_util::convert_64_to_32(src,out,size);
|
||||||
|
set_sample_count(samples);
|
||||||
|
set_channels(nch);
|
||||||
|
set_srate(srate);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool audio_chunk::is_valid()
|
||||||
|
{
|
||||||
|
unsigned nch = get_channels();
|
||||||
|
if (nch==0 || nch>256) return false;
|
||||||
|
unsigned srate = get_srate();
|
||||||
|
if (srate<1000 || srate>1000000) return false;
|
||||||
|
unsigned samples = get_sample_count();
|
||||||
|
if (samples==0 || samples >= 0x80000000 / (sizeof(audio_sample) * nch) ) return false;
|
||||||
|
unsigned size = get_data_size();
|
||||||
|
if (samples * nch > size) return false;
|
||||||
|
if (!get_data()) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool audio_chunk::pad_with_silence_ex(unsigned samples,unsigned hint_nch,unsigned hint_srate)
|
||||||
|
{
|
||||||
|
if (is_empty())
|
||||||
|
{
|
||||||
|
if (hint_srate && hint_nch)
|
||||||
|
{
|
||||||
|
return set_data(0,samples,hint_nch,hint_srate);
|
||||||
|
}
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (hint_srate && hint_srate != get_srate()) samples = MulDiv(samples,get_srate(),hint_srate);
|
||||||
|
if (samples > get_sample_count())
|
||||||
|
{
|
||||||
|
unsigned old_size = get_sample_count() * get_channels();
|
||||||
|
unsigned new_size = samples * get_channels();
|
||||||
|
audio_sample * ptr = check_data_size(new_size);
|
||||||
|
if (ptr)
|
||||||
|
{
|
||||||
|
mem_ops<audio_sample>::set(ptr + old_size,0,new_size - old_size);
|
||||||
|
set_sample_count(samples);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
else return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool audio_chunk::pad_with_silence(unsigned samples)
|
||||||
|
{
|
||||||
|
if (samples > get_sample_count())
|
||||||
|
{
|
||||||
|
unsigned old_size = get_sample_count() * get_channels();
|
||||||
|
unsigned new_size = samples * get_channels();
|
||||||
|
audio_sample * ptr = check_data_size(new_size);
|
||||||
|
if (ptr)
|
||||||
|
{
|
||||||
|
mem_ops<audio_sample>::set(ptr + old_size,0,new_size - old_size);
|
||||||
|
set_sample_count(samples);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
else return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool audio_chunk::insert_silence_fromstart(unsigned samples)
|
||||||
|
{
|
||||||
|
unsigned old_size = get_sample_count() * get_channels();
|
||||||
|
unsigned delta = samples * get_channels();
|
||||||
|
unsigned new_size = old_size + delta;
|
||||||
|
audio_sample * ptr = check_data_size(new_size);
|
||||||
|
if (ptr)
|
||||||
|
{
|
||||||
|
mem_ops<audio_sample>::move(ptr+delta,ptr,old_size);
|
||||||
|
mem_ops<audio_sample>::set(ptr,0,delta);
|
||||||
|
set_sample_count(get_sample_count() + samples);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned audio_chunk::skip_first_samples(unsigned samples_delta)
|
||||||
|
{
|
||||||
|
unsigned samples_old = get_sample_count();
|
||||||
|
if (samples_delta >= samples_old)
|
||||||
|
{
|
||||||
|
set_sample_count(0);
|
||||||
|
set_data_size(0);
|
||||||
|
return samples_old;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unsigned samples_new = samples_old - samples_delta;
|
||||||
|
unsigned nch = get_channels();
|
||||||
|
audio_sample * ptr = get_data();
|
||||||
|
mem_ops<audio_sample>::move(ptr,ptr+nch*samples_delta,nch*samples_new);
|
||||||
|
set_sample_count(samples_new);
|
||||||
|
set_data_size(nch*samples_new);
|
||||||
|
return samples_delta;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,158 @@
|
||||||
|
#ifndef _AUDIO_CHUNK_H_
|
||||||
|
#define _AUDIO_CHUNK_H_
|
||||||
|
|
||||||
|
#define audio_sample_size 64
|
||||||
|
|
||||||
|
#if audio_sample_size == 32
|
||||||
|
typedef float audio_sample;
|
||||||
|
#define audio_sample_asm dword
|
||||||
|
#elif audio_sample_size == 64
|
||||||
|
typedef double audio_sample;
|
||||||
|
#define audio_sample_asm qword
|
||||||
|
#else
|
||||||
|
#error wrong audio_sample_size
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define audio_sample_bytes (audio_sample_size/8)
|
||||||
|
|
||||||
|
|
||||||
|
//channel order same as in windows APIs - for 4ch: L/R/BL/BR, for 5.1: L/R/C/LF/BL/BR
|
||||||
|
|
||||||
|
class NOVTABLE audio_chunk
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
audio_chunk() {}
|
||||||
|
~audio_chunk() {}
|
||||||
|
public:
|
||||||
|
|
||||||
|
|
||||||
|
virtual audio_sample * get_data()=0;
|
||||||
|
virtual const audio_sample * get_data() const = 0;
|
||||||
|
virtual unsigned get_data_size() const = 0;//buffer size in audio_samples; must be at least samples * nch;
|
||||||
|
virtual audio_sample * set_data_size(unsigned new_size)=0;
|
||||||
|
|
||||||
|
virtual unsigned get_srate() const = 0;
|
||||||
|
virtual void set_srate(unsigned val)=0;
|
||||||
|
virtual unsigned get_channels() const = 0;
|
||||||
|
virtual void set_channels(unsigned val)=0;
|
||||||
|
|
||||||
|
virtual unsigned get_sample_count() const = 0;
|
||||||
|
virtual void set_sample_count(unsigned val)=0;
|
||||||
|
|
||||||
|
inline audio_sample * check_data_size(unsigned new_size) {if (new_size > get_data_size()) return set_data_size(new_size); else return get_data();}
|
||||||
|
|
||||||
|
inline bool copy_from(const audio_chunk * src)
|
||||||
|
{
|
||||||
|
return set_data(src->get_data(),src->get_sample_count(),src->get_channels(),src->get_srate());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline double get_duration() const
|
||||||
|
{
|
||||||
|
double rv = 0;
|
||||||
|
unsigned srate = get_srate (), samples = get_sample_count();
|
||||||
|
if (srate>0 && samples>0) rv = (double)samples/(double)srate;
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool is_empty() const {return get_channels()==0 || get_srate()==0 || get_sample_count()==0;}
|
||||||
|
|
||||||
|
bool is_valid();
|
||||||
|
|
||||||
|
inline unsigned get_data_length() const {return get_sample_count() * get_channels();}//actual amount of audio_samples in buffer
|
||||||
|
|
||||||
|
inline void reset()
|
||||||
|
{
|
||||||
|
set_sample_count(0);
|
||||||
|
set_srate(0);
|
||||||
|
set_channels(0);
|
||||||
|
}
|
||||||
|
inline void reset_data()
|
||||||
|
{
|
||||||
|
reset();
|
||||||
|
set_data_size(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool set_data(const audio_sample * src,unsigned samples,unsigned nch,unsigned srate);//returns false on failure (eg. memory error)
|
||||||
|
|
||||||
|
|
||||||
|
//helper routines for converting different input data formats
|
||||||
|
inline bool set_data_fixedpoint(const void * ptr,unsigned bytes,unsigned srate,unsigned nch,unsigned bps)
|
||||||
|
{
|
||||||
|
return set_data_fixedpoint_ex(ptr,bytes,srate,nch,bps,(bps==8 ? FLAG_UNSIGNED : FLAG_SIGNED) | flags_autoendian());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool set_data_fixedpoint_unsigned(const void * ptr,unsigned bytes,unsigned srate,unsigned nch,unsigned bps)
|
||||||
|
{
|
||||||
|
return set_data_fixedpoint_ex(ptr,bytes,srate,nch,bps,FLAG_UNSIGNED | flags_autoendian());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool set_data_fixedpoint_signed(const void * ptr,unsigned bytes,unsigned srate,unsigned nch,unsigned bps)
|
||||||
|
{
|
||||||
|
return set_data_fixedpoint_ex(ptr,bytes,srate,nch,bps,FLAG_SIGNED | flags_autoendian());
|
||||||
|
}
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
FLAG_LITTLE_ENDIAN = 1,
|
||||||
|
FLAG_BIG_ENDIAN = 2,
|
||||||
|
FLAG_SIGNED = 4,
|
||||||
|
FLAG_UNSIGNED = 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
inline static unsigned flags_autoendian()
|
||||||
|
{
|
||||||
|
return byte_order_helper::machine_is_big_endian() ? FLAG_BIG_ENDIAN : FLAG_LITTLE_ENDIAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool set_data_fixedpoint_ex(const void * ptr,unsigned bytes,unsigned srate,unsigned nch,unsigned bps,unsigned flags);//flags - see FLAG_* above
|
||||||
|
|
||||||
|
bool set_data_floatingpoint_ex(const void * ptr,unsigned bytes,unsigned srate,unsigned nch,unsigned bps,unsigned flags);//signed/unsigned flags dont apply
|
||||||
|
|
||||||
|
#if audio_sample_size == 64
|
||||||
|
bool set_data_32(const float * src,unsigned samples,unsigned nch,unsigned srate);
|
||||||
|
inline bool set_data_64(const double * src,unsigned samples,unsigned nch,unsigned srate) {return set_data(src,samples,nch,srate);}
|
||||||
|
#else
|
||||||
|
inline bool set_data_32(const float * src,unsigned samples,unsigned nch,unsigned srate) {return set_data(src,samples,nch,srate);}
|
||||||
|
bool set_data_64(const double * src,unsigned samples,unsigned nch,unsigned srate);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool pad_with_silence_ex(unsigned samples,unsigned hint_nch,unsigned hint_srate);
|
||||||
|
bool pad_with_silence(unsigned samples);
|
||||||
|
bool insert_silence_fromstart(unsigned samples);
|
||||||
|
unsigned skip_first_samples(unsigned samples);
|
||||||
|
};
|
||||||
|
|
||||||
|
class audio_chunk_i : public audio_chunk
|
||||||
|
{
|
||||||
|
mem_block_aligned_t<audio_sample> m_data;
|
||||||
|
unsigned m_srate,m_nch,m_samples;
|
||||||
|
public:
|
||||||
|
audio_chunk_i() : m_srate(0), m_nch(0), m_samples(0) {}
|
||||||
|
audio_chunk_i(const audio_sample * src,unsigned samples,unsigned nch,unsigned srate) : m_srate(0), m_nch(0), m_samples(0)
|
||||||
|
{set_data(src,samples,nch,srate);}
|
||||||
|
|
||||||
|
virtual audio_sample * get_data() {return m_data;}
|
||||||
|
virtual const audio_sample * get_data() const {return m_data;}
|
||||||
|
virtual unsigned get_data_size() const {return m_data.get_size();}
|
||||||
|
virtual audio_sample * set_data_size(unsigned new_size) {return m_data.set_size(new_size);}
|
||||||
|
|
||||||
|
virtual unsigned get_srate() const {return m_srate;}
|
||||||
|
virtual void set_srate(unsigned val) {m_srate=val;}
|
||||||
|
virtual unsigned get_channels() const {return m_nch;}
|
||||||
|
virtual void set_channels(unsigned val) {m_nch = val;}
|
||||||
|
|
||||||
|
virtual unsigned get_sample_count() const {return m_samples;}
|
||||||
|
virtual void set_sample_count(unsigned val) {m_samples = val;}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct output_chunk //used by float->pcm and output
|
||||||
|
{
|
||||||
|
void * data;
|
||||||
|
int size;//in bytes
|
||||||
|
int srate;
|
||||||
|
int nch;//number of interleaved channels
|
||||||
|
int bps;//bits-per-sample
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //_AUDIO_CHUNK_H_
|
|
@ -0,0 +1,13 @@
|
||||||
|
#include "foobar2000.h"
|
||||||
|
|
||||||
|
void commandline_handler_metadb_handle::on_file(const char * url)
|
||||||
|
{
|
||||||
|
playlist_loader_callback_i callback;
|
||||||
|
|
||||||
|
playlist_loader::process_path_ex(url,&callback);
|
||||||
|
|
||||||
|
{
|
||||||
|
unsigned n,m=callback.get_count();
|
||||||
|
for(n=0;n<m;n++) on_file(callback.get_item(n));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
#ifndef _FOOBAR2000_SDK_COMMANDLINE_H_
|
||||||
|
#define _FOOBAR2000_SDK_COMMANDLINE_H_
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
|
||||||
|
class commandline_handler : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
|
||||||
|
enum result
|
||||||
|
{
|
||||||
|
RESULT_NOT_OURS,//not our command
|
||||||
|
RESULT_PROCESSED,//command processed
|
||||||
|
RESULT_PROCESSED_EXPECT_FILES,//command processed, we want to takeover file urls after this command
|
||||||
|
};
|
||||||
|
virtual result on_token(const char * token)=0;
|
||||||
|
virtual void on_file(const char * url) {};//optional
|
||||||
|
virtual void on_files_done() {};//optional
|
||||||
|
virtual bool want_directories() {return false;}
|
||||||
|
};
|
||||||
|
|
||||||
|
class commandline_handler_metadb_handle : public commandline_handler//helper
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
virtual void on_file(const char * url);
|
||||||
|
virtual bool want_directories() {return true;}
|
||||||
|
public:
|
||||||
|
virtual result on_token(const char * token)=0;
|
||||||
|
virtual void on_files_done() {};
|
||||||
|
|
||||||
|
virtual void on_file(metadb_handle * ptr)=0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
how commandline_handler is used:
|
||||||
|
|
||||||
|
scenario #1:
|
||||||
|
creation => on_token() => deletion
|
||||||
|
scenario #2:
|
||||||
|
creation => on_token() returning RESULT_PROCESSED_EXPECT_FILES => on_file(), on_file().... => on_files_done() => deletion
|
||||||
|
*/
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class commandline_handler_factory : public service_factory_t<commandline_handler,T> {};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif //_FOOBAR2000_SDK_COMMANDLINE_H_
|
|
@ -0,0 +1,48 @@
|
||||||
|
#ifndef _COMPONENT_H_
|
||||||
|
#define _COMPONENT_H_
|
||||||
|
|
||||||
|
#include "foobar2000.h"
|
||||||
|
|
||||||
|
class foobar2000_client
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum {FOOBAR2000_CLIENT_VERSION_COMPATIBLE = 27, FOOBAR2000_CLIENT_VERSION=35}; //changes everytime global compatibility is broken
|
||||||
|
virtual int get_version() {return FOOBAR2000_CLIENT_VERSION;}
|
||||||
|
virtual service_factory_base * get_service_list() {return service_factory_base::list_get_pointer();}
|
||||||
|
|
||||||
|
virtual void get_config(cfg_var::write_config_callback * out)
|
||||||
|
{
|
||||||
|
cfg_var::config_write_file(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void set_config(const void * data,int size)
|
||||||
|
{
|
||||||
|
cfg_var::config_read_file(data,size);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void reset_config()
|
||||||
|
{
|
||||||
|
//0.8: deprecated
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void set_library_path(const char * path,const char * name);
|
||||||
|
|
||||||
|
virtual void services_init(bool val);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class NOVTABLE foobar2000_api
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual service_base * service_enum_create(const GUID &g,int n)=0;
|
||||||
|
virtual int service_enum_get_count(const GUID &g)=0;
|
||||||
|
virtual HWND get_main_window()=0;
|
||||||
|
virtual bool assert_main_thread()=0;
|
||||||
|
virtual bool is_main_thread()=0;
|
||||||
|
virtual const char * get_profile_path()=0;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern foobar2000_client g_client;
|
||||||
|
extern foobar2000_api * g_api;
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,6 @@
|
||||||
|
#ifndef _COMPONENT_CLIENT_H_
|
||||||
|
#define _COMPONENT_CLIENT_H_
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif //_COMPONENT_CLIENT_H_
|
|
@ -0,0 +1,7 @@
|
||||||
|
#ifndef _COMPONENTS_MENU_H_
|
||||||
|
#define _COMPONENTS_MENU_H_
|
||||||
|
|
||||||
|
#error deprecated, see menu_item.h
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,56 @@
|
||||||
|
#ifndef _COMPONENTVERSION_H_
|
||||||
|
#define _COMPONENTVERSION_H_
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
#include "interface_helper.h"
|
||||||
|
|
||||||
|
//reminder: all strings are UTF-8
|
||||||
|
|
||||||
|
class NOVTABLE componentversion : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void get_file_name(string_base & out)=0;
|
||||||
|
virtual void get_component_name(string_base & out)=0;
|
||||||
|
virtual void get_component_version(string_base & out)=0;
|
||||||
|
virtual void get_about_message(string_base & out)=0;//about message uses "\n" for line separators
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
};
|
||||||
|
|
||||||
|
class componentversion_i_simple : public componentversion
|
||||||
|
{
|
||||||
|
const char * name,*version,*about;
|
||||||
|
public:
|
||||||
|
//do not derive/override
|
||||||
|
virtual void get_file_name(string_base & out) {out.set_string(service_factory_base::get_my_file_name());}
|
||||||
|
virtual void get_component_name(string_base & out) {out.set_string(name?name:"");}
|
||||||
|
virtual void get_component_version(string_base & out) {out.set_string(version?version:"");}
|
||||||
|
virtual void get_about_message(string_base & out) {out.set_string(about?about:"");}
|
||||||
|
componentversion_i_simple(const char * p_name,const char * p_version,const char * p_about) : name(p_name), version(p_version), about(p_about ? p_about : "") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class componentversion_i_copy : public componentversion
|
||||||
|
{
|
||||||
|
string_simple name,version,about;
|
||||||
|
public:
|
||||||
|
//do not derive/override
|
||||||
|
virtual void get_file_name(string_base & out) {out.set_string(service_factory_base::get_my_file_name());}
|
||||||
|
virtual void get_component_name(string_base & out) {out.set_string(name);}
|
||||||
|
virtual void get_component_version(string_base & out) {out.set_string(version);}
|
||||||
|
virtual void get_about_message(string_base & out) {out.set_string(about);}
|
||||||
|
componentversion_i_copy(const char * p_name,const char * p_version,const char * p_about) : name(p_name), version(p_version), about(p_about ? p_about : "") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#define DECLARE_COMPONENT_VERSION(NAME,VERSION,ABOUT) \
|
||||||
|
static service_factory_single_transparent_p3_t<componentversion,componentversion_i_simple,const char*,const char*,const char*> g_componentversion_service(NAME,VERSION,ABOUT);
|
||||||
|
|
||||||
|
#define DECLARE_COMPONENT_VERSION_COPY(NAME,VERSION,ABOUT) \
|
||||||
|
static service_factory_single_transparent_p3_t<componentversion,componentversion_i_copy,const char*,const char*,const char*> g_componentversion_service(NAME,VERSION,ABOUT);
|
||||||
|
|
||||||
|
//usage: DECLARE_COMPONENT_VERSION("blah","v1.337",0)
|
||||||
|
//about message is optional can be null
|
||||||
|
//_copy version copies strings around instead of keeping pointers (bigger but sometimes needed, eg. if strings are created as string_printf() or something inside constructor
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef _CONFIG_H_
|
||||||
|
#define _CONFIG_H_
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
|
||||||
|
class NOVTABLE config : public service_base
|
||||||
|
{
|
||||||
|
public: //NOTE your config class instance will get deleted BEFORE returned window is destroyed or even used
|
||||||
|
virtual HWND create(HWND parent)=0;
|
||||||
|
virtual const char * get_name()=0;
|
||||||
|
virtual const char * get_parent_name() {return 0;}//optional, retuns name of parent config item
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class config_factory : public service_factory_single_t<config,T> {};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,475 @@
|
||||||
|
#ifndef _FOOBAR2000_CONFIG_VAR_H_
|
||||||
|
#define _FOOBAR2000_CONFIG_VAR_H_
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
|
||||||
|
#include "interface_helper.h"
|
||||||
|
|
||||||
|
#include "initquit.h"
|
||||||
|
|
||||||
|
// "externally visible" config vars that can be accessed by other components
|
||||||
|
|
||||||
|
//note: callbacks from different threads are allowed, keep your stuff mt-safe
|
||||||
|
|
||||||
|
|
||||||
|
class config_var;
|
||||||
|
|
||||||
|
class config_var_callback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void on_changed(const config_var * ptr)=0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class callback_list
|
||||||
|
{
|
||||||
|
ptr_list_simple< config_var_callback > list;
|
||||||
|
public:
|
||||||
|
void add(config_var_callback * ptr) {list.add_item(ptr);}
|
||||||
|
void remove(config_var_callback * ptr) {list.remove_item(ptr);}
|
||||||
|
void call(const config_var * ptr) const
|
||||||
|
{
|
||||||
|
int n,m=list.get_count();
|
||||||
|
for(n=0;n<m;n++)
|
||||||
|
list[n]->on_changed(ptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class config_var : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
class NOVTABLE write_config_callback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void write(const void * ptr,int bytes)=0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class write_config_callback_i : public write_config_callback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
mem_block data;
|
||||||
|
write_config_callback_i() {data.set_mem_logic(mem_block::ALLOC_FAST_DONTGODOWN);}
|
||||||
|
virtual void write(const void * ptr,int bytes) {data.append(ptr,bytes);}
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual const char * get_name() const = 0;
|
||||||
|
virtual bool is_public() const {return true;}
|
||||||
|
virtual void get_raw_data(config_var::write_config_callback * out) const = 0;
|
||||||
|
virtual void set_raw_data(const void * data,int size) = 0;
|
||||||
|
virtual void reset()=0;
|
||||||
|
virtual GUID get_type() const = 0;
|
||||||
|
virtual void add_callback(config_var_callback * ptr,bool calloninit=false)=0;
|
||||||
|
virtual void remove_callback(config_var_callback * ptr)=0;
|
||||||
|
virtual const char * get_library() {return core_api::get_my_file_name();}
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
|
||||||
|
static config_var * g_find(GUID type,const char * name)
|
||||||
|
{
|
||||||
|
service_enum_t<config_var> e;
|
||||||
|
config_var * ptr;
|
||||||
|
for(ptr=e.first();ptr;ptr=e.next())
|
||||||
|
{
|
||||||
|
if (ptr->get_type()==type && ptr->is_public() && !stricmp_utf8(name,ptr->get_name())) return ptr;
|
||||||
|
ptr->service_release();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void g_add_callback(GUID type,const char * name,config_var_callback * cb,bool calloninit=false)
|
||||||
|
{
|
||||||
|
config_var * ptr = g_find(type,name);
|
||||||
|
if (ptr) {ptr->add_callback(cb,calloninit);ptr->service_release();}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void g_remove_callback(GUID type,const char * name,config_var_callback * cb)
|
||||||
|
{
|
||||||
|
config_var * ptr = g_find(type,name);
|
||||||
|
if (ptr) {ptr->remove_callback(cb);ptr->service_release();}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class NOVTABLE config_var_int : public config_var
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const GUID var_type;
|
||||||
|
inline static const GUID & get_var_type() {return var_type;}
|
||||||
|
|
||||||
|
virtual const char * get_name() const = 0;
|
||||||
|
virtual void get_raw_data(config_var::write_config_callback * out) const = 0;
|
||||||
|
virtual void set_raw_data(const void * data,int size) = 0;
|
||||||
|
virtual void reset()=0;
|
||||||
|
virtual GUID get_type() const {return get_var_type();}
|
||||||
|
|
||||||
|
virtual int get_value() const = 0;
|
||||||
|
virtual void set_value(int val) = 0;
|
||||||
|
|
||||||
|
static config_var_int * g_find(const char * name)
|
||||||
|
{
|
||||||
|
return static_cast<config_var_int*>(config_var::g_find(get_var_type(),name));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int g_get_value(const char * name)
|
||||||
|
{
|
||||||
|
int rv = 0;
|
||||||
|
config_var_int * ptr = g_find(name);
|
||||||
|
if (ptr) {rv = ptr->get_value();ptr->service_release();}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void g_set_value(const char * name,int val)
|
||||||
|
{
|
||||||
|
config_var_int * ptr = g_find(name);
|
||||||
|
if (ptr) {ptr->set_value(val);ptr->service_release();}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void g_add_callback(const char * name,config_var_callback * cb,bool calloninit=false)
|
||||||
|
{
|
||||||
|
config_var_int * ptr = g_find(name);
|
||||||
|
if (ptr) {ptr->add_callback(cb,calloninit);ptr->service_release();}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void g_remove_callback(const char * name,config_var_callback * cb)
|
||||||
|
{
|
||||||
|
config_var_int * ptr = g_find(name);
|
||||||
|
if (ptr) {ptr->remove_callback(cb);ptr->service_release();}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
extern const GUID config_var_struct_var_type;
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class NOVTABLE config_var_struct : public config_var
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
inline static const GUID & get_var_type() {return config_var_struct_var_type;}
|
||||||
|
|
||||||
|
virtual const char * get_name() const = 0;
|
||||||
|
virtual void get_raw_data(config_var::write_config_callback * out) const = 0;
|
||||||
|
virtual void set_raw_data(const void * data,int size) = 0;
|
||||||
|
virtual void reset()=0;
|
||||||
|
virtual GUID get_type() const {return get_var_type();}
|
||||||
|
virtual unsigned get_struct_size() {return sizeof(T);}
|
||||||
|
|
||||||
|
virtual void get_value(T & out) const = 0;
|
||||||
|
virtual void set_value(const T& src) = 0;
|
||||||
|
|
||||||
|
static config_var_struct<T> * g_find(const char * name)
|
||||||
|
{
|
||||||
|
config_var_struct<T> * temp = static_cast<config_var_struct<T>*>(config_var::g_find(get_var_type(),name));
|
||||||
|
assert(temp==0 || temp->get_struct_size() == sizeof(T) );
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool g_get_value(const char * name,T& out)
|
||||||
|
{
|
||||||
|
bool rv = false;
|
||||||
|
config_var_struct<T> * ptr = g_find(name);
|
||||||
|
if (ptr) {ptr->get_value(out);ptr->service_release();rv=true;}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void g_set_value(const char * name,const T& val)
|
||||||
|
{
|
||||||
|
config_var_struct<T> * ptr = g_find(name);
|
||||||
|
if (ptr) {ptr->set_value(val);ptr->service_release();}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void g_add_callback(const char * name,config_var_callback * cb,bool calloninit=false)
|
||||||
|
{
|
||||||
|
config_var_struct<T> * ptr = g_find(name);
|
||||||
|
if (ptr) {ptr->add_callback(cb,calloninit);ptr->service_release();}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void g_remove_callback(const char * name,config_var_callback * cb)
|
||||||
|
{
|
||||||
|
config_var_struct<T> * ptr = g_find(name);
|
||||||
|
if (ptr) {ptr->remove_callback(cb);ptr->service_release();}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class config_var_struct_i : public config_var_struct<T>
|
||||||
|
{
|
||||||
|
string_simple name;
|
||||||
|
T value,def;
|
||||||
|
mutable critical_section sync;
|
||||||
|
callback_list callbacks;
|
||||||
|
public:
|
||||||
|
virtual const char * get_name() const {return name;}
|
||||||
|
virtual void get_raw_data(config_var::write_config_callback * out) const {insync(sync);out->write(&value,sizeof(value));}
|
||||||
|
virtual void set_raw_data(const void * data,int size)
|
||||||
|
{
|
||||||
|
insync(sync);
|
||||||
|
if (size==sizeof(value)) value = *(T*)data;
|
||||||
|
callbacks.call(this);
|
||||||
|
}
|
||||||
|
virtual void reset()
|
||||||
|
{
|
||||||
|
insync(sync);
|
||||||
|
value = def;
|
||||||
|
callbacks.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void add_callback(config_var_callback * ptr,bool calloninit=false)
|
||||||
|
{
|
||||||
|
insync(sync);
|
||||||
|
callbacks.add(ptr);
|
||||||
|
if (calloninit) ptr->on_changed(this);
|
||||||
|
}
|
||||||
|
virtual void remove_callback(config_var_callback * ptr)
|
||||||
|
{
|
||||||
|
insync(sync);
|
||||||
|
callbacks.remove(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void get_value(T & out) const {insync(sync);out = value;}
|
||||||
|
virtual void set_value(const T & p_val)
|
||||||
|
{
|
||||||
|
insync(sync);
|
||||||
|
if (memcmp(&value,&p_val,sizeof(value)))
|
||||||
|
{
|
||||||
|
value = p_val;
|
||||||
|
callbacks.call(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config_var_struct_i(const char * p_name,T p_value) : name(p_name), value(p_value), def(p_value) {}
|
||||||
|
|
||||||
|
void operator=(const T& v) {set_value(v);}
|
||||||
|
inline operator T() const {T temp;get_value(temp);return temp;}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class NOVTABLE config_var_string : public config_var
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const GUID var_type;
|
||||||
|
inline static const GUID & get_var_type() {return var_type;}
|
||||||
|
|
||||||
|
virtual const char * get_name() const = 0;
|
||||||
|
virtual void get_raw_data(config_var::write_config_callback * out) const = 0;
|
||||||
|
virtual void set_raw_data(const void * data,int size) = 0;
|
||||||
|
virtual void reset()=0;
|
||||||
|
virtual GUID get_type() const {return get_var_type();}
|
||||||
|
|
||||||
|
virtual void get_value(string_base & out) const = 0;
|
||||||
|
virtual void set_value(const char * val) = 0;
|
||||||
|
|
||||||
|
static config_var_string * g_find(const char * name)
|
||||||
|
{
|
||||||
|
return static_cast<config_var_string*>(config_var::g_find(get_var_type(),name));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void g_get_value(const char * name,string_base & out)
|
||||||
|
{
|
||||||
|
config_var_string * ptr = g_find(name);
|
||||||
|
if (ptr) {ptr->get_value(out);ptr->service_release();}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void g_set_value(const char * name,const char * val)
|
||||||
|
{
|
||||||
|
config_var_string * ptr = g_find(name);
|
||||||
|
if (ptr) {ptr->set_value(val);ptr->service_release();}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void g_add_callback(const char * name,config_var_callback * cb,bool calloninit=false)
|
||||||
|
{
|
||||||
|
config_var_string * ptr = g_find(name);
|
||||||
|
if (ptr) {ptr->add_callback(cb,calloninit);ptr->service_release();}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void g_remove_callback(const char * name,config_var_callback * cb)
|
||||||
|
{
|
||||||
|
config_var_string * ptr = g_find(name);
|
||||||
|
if (ptr) {ptr->remove_callback(cb);ptr->service_release();}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class config_var_int_i : public config_var_int
|
||||||
|
{
|
||||||
|
string_simple name;
|
||||||
|
int value,def;
|
||||||
|
mutable critical_section sync;
|
||||||
|
callback_list callbacks;
|
||||||
|
public:
|
||||||
|
virtual const char * get_name() const {return name;}
|
||||||
|
virtual void get_raw_data(config_var::write_config_callback * out) const {insync(sync);out->write(&value,sizeof(int));}
|
||||||
|
virtual void set_raw_data(const void * data,int size)//bah not endian safe
|
||||||
|
{
|
||||||
|
insync(sync);
|
||||||
|
if (size==sizeof(int)) value = *(int*)data;
|
||||||
|
callbacks.call(this);
|
||||||
|
}
|
||||||
|
virtual void reset()
|
||||||
|
{
|
||||||
|
insync(sync);
|
||||||
|
value = def;
|
||||||
|
callbacks.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void add_callback(config_var_callback * ptr,bool calloninit=false)
|
||||||
|
{
|
||||||
|
insync(sync);
|
||||||
|
callbacks.add(ptr);
|
||||||
|
if (calloninit) ptr->on_changed(this);
|
||||||
|
}
|
||||||
|
virtual void remove_callback(config_var_callback * ptr)
|
||||||
|
{
|
||||||
|
insync(sync);
|
||||||
|
callbacks.remove(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int get_value() const {insync(sync);return value;}
|
||||||
|
virtual void set_value(int p_val)
|
||||||
|
{
|
||||||
|
insync(sync);
|
||||||
|
if (value!=p_val)
|
||||||
|
{
|
||||||
|
value = p_val;
|
||||||
|
callbacks.call(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config_var_int_i(const char * p_name,int p_value) : name(p_name), value(p_value), def(p_value) {}
|
||||||
|
|
||||||
|
int operator=(int v) {set_value(v);return v;}
|
||||||
|
operator int() const {return get_value();}
|
||||||
|
};
|
||||||
|
|
||||||
|
class config_var_string_i : public config_var_string
|
||||||
|
{
|
||||||
|
string_simple name,value,def;
|
||||||
|
mutable critical_section sync;
|
||||||
|
callback_list callbacks;
|
||||||
|
public:
|
||||||
|
virtual const char * get_name() const {return name;}
|
||||||
|
virtual void get_raw_data(config_var::write_config_callback * out) const
|
||||||
|
{
|
||||||
|
insync(sync);
|
||||||
|
out->write(value.get_ptr(),strlen(value));
|
||||||
|
}
|
||||||
|
virtual void set_raw_data(const void * data,int size)
|
||||||
|
{
|
||||||
|
insync(sync);
|
||||||
|
value.set_string_n((const char*)data,size);
|
||||||
|
callbacks.call(this);
|
||||||
|
}
|
||||||
|
virtual void reset()
|
||||||
|
{
|
||||||
|
insync(sync);
|
||||||
|
value = def;
|
||||||
|
callbacks.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual GUID get_type() const {return get_var_type();}
|
||||||
|
|
||||||
|
virtual void get_value(string_base & out) const
|
||||||
|
{
|
||||||
|
insync(sync);
|
||||||
|
out.set_string(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void set_value(const char * val)
|
||||||
|
{
|
||||||
|
insync(sync);
|
||||||
|
if (strcmp(value,val))
|
||||||
|
{
|
||||||
|
value.set_string(val);
|
||||||
|
callbacks.call(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void add_callback(config_var_callback * ptr,bool calloninit=false)
|
||||||
|
{
|
||||||
|
insync(sync);
|
||||||
|
callbacks.add(ptr);
|
||||||
|
if (calloninit) ptr->on_changed(this);
|
||||||
|
}
|
||||||
|
virtual void remove_callback(config_var_callback * ptr)
|
||||||
|
{
|
||||||
|
insync(sync);
|
||||||
|
callbacks.remove(ptr);
|
||||||
|
}
|
||||||
|
config_var_string_i(const char * p_name,const char * p_value) : name(p_name), value(p_value), def(p_value) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
//only static creation!
|
||||||
|
template<class vartype>
|
||||||
|
class config_var_callback_autoreg : public config_var_callback
|
||||||
|
{
|
||||||
|
class callback_autoreg_initquit : public initquit_autoreg
|
||||||
|
{
|
||||||
|
config_var_callback_autoreg * cb;
|
||||||
|
public:
|
||||||
|
callback_autoreg_initquit(config_var_callback_autoreg * p_cb) {cb = p_cb;}
|
||||||
|
virtual void on_init() {cb->on_init();}
|
||||||
|
virtual void on_quit() {cb->on_quit();}
|
||||||
|
};
|
||||||
|
|
||||||
|
vartype * var;
|
||||||
|
string_simple varname;
|
||||||
|
virtual void on_changed(const config_var * ptr) {on_changed(static_cast<const vartype*>(ptr));}
|
||||||
|
callback_autoreg_initquit * p_initquit;
|
||||||
|
bool calloninit;
|
||||||
|
protected:
|
||||||
|
virtual void on_changed(const vartype * ptr)=0;
|
||||||
|
public:
|
||||||
|
void on_init()
|
||||||
|
{
|
||||||
|
if (!var && !varname.is_empty())
|
||||||
|
var = vartype::g_find(varname);
|
||||||
|
if (var) var->add_callback(this,calloninit);
|
||||||
|
}
|
||||||
|
void on_quit()
|
||||||
|
{
|
||||||
|
if (var) var->remove_callback(this);
|
||||||
|
}
|
||||||
|
config_var_callback_autoreg(vartype * p_var,bool p_calloninit = false)
|
||||||
|
{
|
||||||
|
calloninit = p_calloninit;
|
||||||
|
var = p_var;
|
||||||
|
p_initquit = new callback_autoreg_initquit(this);
|
||||||
|
}
|
||||||
|
config_var_callback_autoreg(const char * name,bool p_calloninit = false)
|
||||||
|
{
|
||||||
|
calloninit = p_calloninit;
|
||||||
|
var = 0;
|
||||||
|
varname = name;
|
||||||
|
p_initquit = new callback_autoreg_initquit(this);
|
||||||
|
}
|
||||||
|
~config_var_callback_autoreg()
|
||||||
|
{
|
||||||
|
delete p_initquit;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class vartype>
|
||||||
|
class config_var_callback_simple : public config_var_callback_autoreg<vartype>
|
||||||
|
{
|
||||||
|
void (*func)(const vartype *);
|
||||||
|
virtual void on_changed(const vartype * ptr) {func(ptr);}
|
||||||
|
public:
|
||||||
|
config_var_callback_simple(vartype * p_var,void (*p_func)(const vartype *),bool p_calloninit = false)
|
||||||
|
: config_var_callback_autoreg<vartype>(p_var,p_calloninit) {func = p_func;}
|
||||||
|
|
||||||
|
config_var_callback_simple(const char * name,void (*p_func)(const vartype *),bool p_calloninit = false)
|
||||||
|
: config_var_callback_autoreg<vartype>(name,p_calloninit) {func = p_func;}
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DECLARE_CONFIG_VAR_INT(VARNAME,NAME,VALUE) \
|
||||||
|
static service_factory_single_transparent_p2_t<config_var,config_var_int_i,const char*,int> VARNAME(NAME,VALUE);
|
||||||
|
|
||||||
|
|
||||||
|
#define DECLARE_CONFIG_VAR_STRING(VARNAME,NAME,VALUE) \
|
||||||
|
static service_factory_single_transparent_p2_t<config_var,config_var_string_i,const char*,const char*> VARNAME(NAME,VALUE);
|
||||||
|
|
||||||
|
#define DECLARE_CONFIG_VAR_STRUCT(TYPE,VARNAME,NAME,VALUE) \
|
||||||
|
static service_factory_single_transparent_p2_t<config_var,config_var_struct_i<TYPE>,const char*,TYPE> VARNAME(NAME,VALUE);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,94 @@
|
||||||
|
#include "foobar2000.h"
|
||||||
|
|
||||||
|
bool console::present()
|
||||||
|
{
|
||||||
|
bool rv = false;
|
||||||
|
console * ptr = service_enum_create_t(console,0);
|
||||||
|
if (ptr)
|
||||||
|
{
|
||||||
|
rv = true;
|
||||||
|
ptr->service_release();
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
void console::popup()
|
||||||
|
{
|
||||||
|
console * ptr = service_enum_create_t(console,0);
|
||||||
|
if (ptr)
|
||||||
|
{
|
||||||
|
ptr->_popup();
|
||||||
|
ptr->service_release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void console::beep()
|
||||||
|
{
|
||||||
|
console * ptr = service_enum_create_t(console,0);
|
||||||
|
if (ptr)
|
||||||
|
{
|
||||||
|
ptr->_beep();
|
||||||
|
ptr->service_release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void console::output(int severity,const char * message,const char * source)
|
||||||
|
{
|
||||||
|
console * ptr = service_enum_create_t(console,0);
|
||||||
|
if (ptr)
|
||||||
|
{
|
||||||
|
ptr->_output(severity,message,source);
|
||||||
|
ptr->service_release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void console::output(int severity,const char * message)
|
||||||
|
{
|
||||||
|
output(severity,message,core_api::get_my_file_name());
|
||||||
|
}
|
||||||
|
|
||||||
|
void console::info_location(const class playable_location * src)
|
||||||
|
{
|
||||||
|
const char * path = src->get_path();
|
||||||
|
int number = src->get_subsong_index();
|
||||||
|
info(uStringPrintf("location: \"%s\" (%i)",path,number));
|
||||||
|
}
|
||||||
|
|
||||||
|
void console::info_location(class metadb_handle * src)
|
||||||
|
{
|
||||||
|
info_location(src->handle_get_location());
|
||||||
|
}
|
||||||
|
|
||||||
|
void console::info_location(const class file_info * src)
|
||||||
|
{
|
||||||
|
info_location(src->get_location());
|
||||||
|
}
|
||||||
|
|
||||||
|
void console::info_full(const class file_info * src)
|
||||||
|
{
|
||||||
|
info_location(src);
|
||||||
|
string8_fastalloc temp;
|
||||||
|
unsigned n,m;
|
||||||
|
m = src->meta_get_count();
|
||||||
|
info("meta:");
|
||||||
|
for(n=0;n<m;n++)
|
||||||
|
{
|
||||||
|
temp.reset();
|
||||||
|
temp += src->meta_enum_name(n);
|
||||||
|
temp += "=";
|
||||||
|
temp += src->meta_enum_value(n);
|
||||||
|
info(temp);
|
||||||
|
}
|
||||||
|
info(temp);
|
||||||
|
m = src->info_get_count();
|
||||||
|
info("info:");
|
||||||
|
for(n=0;n<m;n++)
|
||||||
|
{
|
||||||
|
temp.reset();
|
||||||
|
temp += src->info_enum_name(n);
|
||||||
|
temp += "=";
|
||||||
|
temp += src->info_enum_value(n);
|
||||||
|
info(temp);
|
||||||
|
}
|
||||||
|
info(temp);
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
#ifndef _CONSOLE_H_
|
||||||
|
#define _CONSOLE_H_
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
|
||||||
|
//for sending text (errors etc) to console component
|
||||||
|
//strings are all utf-8
|
||||||
|
|
||||||
|
class NOVTABLE console : public service_base
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
virtual void _output(int severity,const char * source,const char * message)=0;
|
||||||
|
virtual void _popup()=0;
|
||||||
|
virtual void _beep()=0;
|
||||||
|
public:
|
||||||
|
enum {
|
||||||
|
SEVERITY_INFO = 1,
|
||||||
|
SEVERITY_WARNING = 2,
|
||||||
|
SEVERITY_CRITICAL = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void output(int severity,const char * message,const char * source);
|
||||||
|
static void output(int severity,const char * message);
|
||||||
|
static void info(const char * message) {output(SEVERITY_INFO,message);}
|
||||||
|
static void warning(const char * message) {output(SEVERITY_WARNING,message);}
|
||||||
|
static void error(const char * message) {output(SEVERITY_CRITICAL,message);}
|
||||||
|
static bool present();//returns if console component is present
|
||||||
|
static void popup();
|
||||||
|
static void beep();
|
||||||
|
static void info_location(const class playable_location * src);
|
||||||
|
static void info_location(class metadb_handle * src);
|
||||||
|
static void info_location(const class file_info * src);
|
||||||
|
static void info_full(const class file_info * src);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
|
||||||
|
//usage: console::warning("blah warning !");
|
||||||
|
};
|
||||||
|
|
||||||
|
//all console methods are multi-thread safe, you can call them from any context
|
||||||
|
|
||||||
|
#define __crash_to_messagebox(x) __except(uMessageBox(0,string_print_crash(GetExceptionInformation(),x),0,0),1)
|
||||||
|
#define __crash_to_console __except(console::error(string_print_crash(GetExceptionInformation())),1)
|
||||||
|
#define __crash_to_console_ex(x) __except(console::error(string_print_crash(GetExceptionInformation(),x)),1)
|
||||||
|
/*
|
||||||
|
usage
|
||||||
|
__try {
|
||||||
|
blah();
|
||||||
|
} __crash_to_console_ex("blah()")
|
||||||
|
{
|
||||||
|
//might want to print additional info about what caused the crash here
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1 @@
|
||||||
|
#error DEPRECATED
|
|
@ -0,0 +1,18 @@
|
||||||
|
#ifndef _CORE_API_H_
|
||||||
|
#define _CORE_API_H_
|
||||||
|
|
||||||
|
#include "../../pfc/pfc.h"
|
||||||
|
|
||||||
|
namespace core_api
|
||||||
|
{
|
||||||
|
HINSTANCE get_my_instance();
|
||||||
|
const char * get_my_file_name();// eg. "foo_asdf" for foo_asdf.dll
|
||||||
|
const char * get_my_full_path();//eg. file://c:\blah\foobar2000\foo_asdf.dll
|
||||||
|
HWND get_main_window();
|
||||||
|
bool are_services_available();
|
||||||
|
bool assert_main_thread();
|
||||||
|
bool is_main_thread();
|
||||||
|
const char * get_profile_path();//eg. "c:\documents_and_settings\username\blah\foobar2000\"
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef _COREVERSION_H_
|
||||||
|
#define _COREVERSION_H_
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
|
||||||
|
class core_version_info : public service_base
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
virtual const char * _get_version_string()=0;
|
||||||
|
public:
|
||||||
|
static const char * get_version_string()
|
||||||
|
{
|
||||||
|
const char * ret = "";
|
||||||
|
core_version_info * ptr = service_enum_create_t(core_version_info,0);
|
||||||
|
if (ptr)
|
||||||
|
{
|
||||||
|
ret = ptr->_get_version_string();
|
||||||
|
ptr->service_release();
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //_COREVERSION_H_
|
|
@ -0,0 +1,37 @@
|
||||||
|
#ifndef _CVT_FLOAT_TO_LINEAR_H_
|
||||||
|
#define _CVT_FLOAT_TO_LINEAR_H_
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
#include "audio_chunk.h"
|
||||||
|
|
||||||
|
//use this for converting floatingpoint -> linear PCM, one implementation in core, do not override
|
||||||
|
class NOVTABLE cvt_float_to_linear : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual int run(const audio_chunk * chunk,output_chunk * out_chunk,UINT out_bps,UINT use_dither,UINT use_pad=0)=0;
|
||||||
|
//return 1 on success, 0 on failure (eg. unsupported bps)
|
||||||
|
//use_pad = bps to pad to, 0 for auto
|
||||||
|
|
||||||
|
//if you want to flush on seek or between tracks or whatever, just service_release() and recreate
|
||||||
|
|
||||||
|
static cvt_float_to_linear * get()//helper
|
||||||
|
{
|
||||||
|
return service_enum_create_t(cvt_float_to_linear,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Notes on output data format.
|
||||||
|
|
||||||
|
Following win32 conventions, out_chunk will point to buffer containing signed integers of specified size (physically always aligned to 8bits, actual resolution might be smaller according to passed params).
|
||||||
|
EXception, if output format is 8bit, values in the buffer are unsigned (just like everywhere in win32).
|
||||||
|
If out_bps isn't a multiply of 8, it will be used as conversion resolution, but results will be padded to multiply of 8-bit (and out_chunk's bps member will be set to padded bps).
|
||||||
|
out_chunk pointers are maintained by cvt_float_to_linear object, and become invalid after releasing the object or calling run() again.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,27 @@
|
||||||
|
#include "foobar2000.h"
|
||||||
|
|
||||||
|
|
||||||
|
diskwriter * diskwriter::instantiate(const GUID & guid)
|
||||||
|
{
|
||||||
|
service_enum_t<diskwriter> e;
|
||||||
|
diskwriter * ptr;
|
||||||
|
for(ptr=e.first();ptr;ptr=e.next())
|
||||||
|
{
|
||||||
|
if (ptr->get_guid() == guid) return ptr;
|
||||||
|
ptr->service_release();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool diskwriter::instantiate_test(const GUID & guid)
|
||||||
|
{
|
||||||
|
service_enum_t<diskwriter> e;
|
||||||
|
diskwriter * ptr;
|
||||||
|
for(ptr=e.first();ptr;ptr=e.next())
|
||||||
|
{
|
||||||
|
bool found = (ptr->get_guid() == guid) ? true : false;
|
||||||
|
ptr->service_release();
|
||||||
|
if (found) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
#ifndef _DISKWRITER_H_
|
||||||
|
#define _DISKWRITER_H_
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
#include "interface_helper.h"
|
||||||
|
#include "audio_chunk.h"
|
||||||
|
#include "file_info.h"
|
||||||
|
|
||||||
|
//one instance of object will be called *only* with one output file
|
||||||
|
class NOVTABLE diskwriter : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
|
||||||
|
|
||||||
|
virtual service_base * service_query(const GUID & guid)
|
||||||
|
{
|
||||||
|
if (guid == get_class_guid()) {service_add_ref();return this;}
|
||||||
|
else return service_base::service_query(guid);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void get_name(string_base & out)=0;//name of your diskwriter service. never supposed to change
|
||||||
|
virtual bool get_extension(string_base & out)=0;//return false if you don't actually write files; result is allowed to depend on preset
|
||||||
|
|
||||||
|
virtual bool open(const char * out_path,unsigned expected_sample_rate,unsigned expected_bps,double expected_length,bool should_dither)=0;
|
||||||
|
//expected_sample_rate may be null in rare cases, rely on it only as much as you must
|
||||||
|
//expected_sample_rate/expected_bps are pulled from first file on to-encode list (there may be more), if you actually need them and start getting chunks with different specs, return error
|
||||||
|
//expected_length is only as reliable as inputs reporting length, may not be strictly accurate
|
||||||
|
|
||||||
|
virtual bool on_source_track(metadb_handle * ptr) {return true;}//return false to abort
|
||||||
|
|
||||||
|
virtual bool process_samples(const audio_chunk * src)=0;//return true on success, false on failure
|
||||||
|
|
||||||
|
virtual void flush()=0;
|
||||||
|
//close output file etc
|
||||||
|
|
||||||
|
virtual GUID get_guid()=0;//GUID of your diskwriter subclass, for storing config
|
||||||
|
|
||||||
|
virtual void preset_set_data(const void * ptr,unsigned bytes)=0;
|
||||||
|
virtual void preset_get_data(cfg_var::write_config_callback * out)=0;
|
||||||
|
virtual bool preset_configurable()=0;//return if you support config dialog or not
|
||||||
|
virtual bool preset_configure(HWND parent)=0;//return false if user aborted the operation
|
||||||
|
|
||||||
|
virtual unsigned preset_default_get_count()=0;
|
||||||
|
virtual void preset_default_enumerate(unsigned index,cfg_var::write_config_callback * out)=0;
|
||||||
|
virtual bool preset_get_description(string_base & out)=0;
|
||||||
|
|
||||||
|
|
||||||
|
static diskwriter * instantiate(const GUID & guid);
|
||||||
|
static bool instantiate_test(const GUID & guid);
|
||||||
|
};
|
||||||
|
|
||||||
|
class NOVTABLE diskwriter_presetless : public diskwriter //helper, preset-less
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void preset_set_data(const void * ptr,unsigned bytes) {}
|
||||||
|
virtual void preset_get_data(cfg_var::write_config_callback * out) {}
|
||||||
|
virtual bool preset_configurable() {return false;}
|
||||||
|
virtual bool preset_configure(HWND parent) {return false;}
|
||||||
|
|
||||||
|
|
||||||
|
virtual unsigned preset_default_get_count() {return 1;}
|
||||||
|
virtual void preset_default_enumerate(unsigned index,cfg_var::write_config_callback * out) {}
|
||||||
|
virtual bool preset_get_description(string_base & out) {return false;}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class diskwriter_factory : public service_factory_t<diskwriter,T> {};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,316 @@
|
||||||
|
#include "foobar2000.h"
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && !defined(_DEBUG) && defined(_M_IX86)
|
||||||
|
#define DSP_HAVE_ASM
|
||||||
|
#endif
|
||||||
|
|
||||||
|
GUID dsp::guid_from_name(const char * name)
|
||||||
|
{
|
||||||
|
service_enum_t<dsp> e;
|
||||||
|
dsp * ptr;
|
||||||
|
for(ptr=e.first();ptr;ptr = e.next())
|
||||||
|
{
|
||||||
|
if (!stricmp_utf8(ptr->get_name(),name))
|
||||||
|
{
|
||||||
|
GUID g = ptr->get_guid();
|
||||||
|
ptr->service_release();
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
ptr->service_release();
|
||||||
|
}
|
||||||
|
return mem_ops<GUID>::make_null_item();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * dsp::name_from_guid(GUID g)
|
||||||
|
{
|
||||||
|
service_enum_t<dsp> e;
|
||||||
|
dsp * ptr;
|
||||||
|
for(ptr=e.first();ptr;ptr = e.next())
|
||||||
|
{
|
||||||
|
if (ptr->get_guid()==g)
|
||||||
|
{
|
||||||
|
const char * name = ptr->get_name();
|
||||||
|
ptr->service_release();
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
ptr->service_release();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsp * dsp::instance_from_guid(GUID g)
|
||||||
|
{
|
||||||
|
service_enum_t<dsp> e;
|
||||||
|
dsp * ptr;
|
||||||
|
for(ptr=e.first();ptr;ptr = e.next())
|
||||||
|
{
|
||||||
|
if (ptr->get_guid()==g) return ptr;
|
||||||
|
ptr->service_release();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef DSP_HAVE_ASM
|
||||||
|
void dsp_util::scale(audio_sample * ptr,double scale,unsigned count)
|
||||||
|
{
|
||||||
|
for(;count;count--)
|
||||||
|
{
|
||||||
|
(*ptr) = (audio_sample)((*ptr) * scale);
|
||||||
|
ptr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
|
||||||
|
__declspec(naked) void dsp_util::scale(audio_sample * ptr,double scale,unsigned count)
|
||||||
|
{
|
||||||
|
_asm
|
||||||
|
{//ptr: [esp+4], scale: [esp+8], count: [esp+16]
|
||||||
|
mov ecx,[esp+16]
|
||||||
|
mov edx,[esp+4]
|
||||||
|
fld qword ptr [esp+8]
|
||||||
|
mov eax,ecx
|
||||||
|
shr ecx,1
|
||||||
|
jz l12
|
||||||
|
l1:
|
||||||
|
fld audio_sample_asm ptr [edx]
|
||||||
|
fld audio_sample_asm ptr [edx+audio_sample_bytes]
|
||||||
|
fmul st,st(2)
|
||||||
|
fxch
|
||||||
|
dec ecx
|
||||||
|
fmul st,st(2)
|
||||||
|
fstp audio_sample_asm ptr [edx]
|
||||||
|
fstp audio_sample_asm ptr [edx+audio_sample_bytes]
|
||||||
|
lea edx,[edx+audio_sample_bytes*2]
|
||||||
|
jnz l1
|
||||||
|
l12:
|
||||||
|
test eax,1
|
||||||
|
jz l2
|
||||||
|
fld audio_sample_asm ptr [edx]
|
||||||
|
fmul st,st(1)
|
||||||
|
fstp audio_sample_asm ptr [edx]
|
||||||
|
l2:
|
||||||
|
fstp st
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void dsp_util::kill_denormal_32(float * fptr,unsigned count)
|
||||||
|
{
|
||||||
|
DWORD * ptr = reinterpret_cast<DWORD*>(fptr);
|
||||||
|
for(;count;count--)
|
||||||
|
{
|
||||||
|
DWORD t = *ptr;
|
||||||
|
if ((t & 0x007FFFFF) && !(t & 0x7F800000)) *ptr=0;
|
||||||
|
ptr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void dsp_util::kill_denormal_64(double * fptr,unsigned count)
|
||||||
|
{
|
||||||
|
unsigned __int64 * ptr = reinterpret_cast<unsigned __int64*>(fptr);
|
||||||
|
for(;count;count--)
|
||||||
|
{
|
||||||
|
unsigned __int64 t = *ptr;
|
||||||
|
if ((t & 0x000FFFFFFFFFFFFF) && !(t & 0x7FF0000000000000)) *ptr=0;
|
||||||
|
ptr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned dsp_chunk_list_i::get_count() const {return data.get_count();}
|
||||||
|
|
||||||
|
audio_chunk * dsp_chunk_list_i::get_item(unsigned n) const {return n>=0 && n<(unsigned)data.get_count() ? data[n] : 0;}
|
||||||
|
|
||||||
|
void dsp_chunk_list_i::remove_by_idx(unsigned idx)
|
||||||
|
{
|
||||||
|
if (idx>=0 && idx<(unsigned)data.get_count())
|
||||||
|
recycled.add_item(data.remove_by_idx(idx));
|
||||||
|
}
|
||||||
|
|
||||||
|
void dsp_chunk_list_i::remove_mask(const bit_array & mask)
|
||||||
|
{
|
||||||
|
unsigned n, m = data.get_count();
|
||||||
|
for(n=0;n<m;n++)
|
||||||
|
if (mask[m])
|
||||||
|
recycled.add_item(data[n]);
|
||||||
|
data.remove_mask(mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
audio_chunk * dsp_chunk_list_i::insert_item(unsigned idx,unsigned hint_size)
|
||||||
|
{
|
||||||
|
unsigned max = get_count();
|
||||||
|
if (idx<0) idx=0;
|
||||||
|
else if (idx>max) idx = max;
|
||||||
|
audio_chunk_i * ret = 0;
|
||||||
|
if (recycled.get_count()>0)
|
||||||
|
{
|
||||||
|
unsigned best;
|
||||||
|
if (hint_size>0)
|
||||||
|
{
|
||||||
|
best = 0;
|
||||||
|
unsigned best_found = recycled[0]->get_data_size(), n, total = recycled.get_count();
|
||||||
|
for(n=1;n<total;n++)
|
||||||
|
{
|
||||||
|
if (best_found==hint_size) break;
|
||||||
|
unsigned size = recycled[n]->get_data_size();
|
||||||
|
int delta_old = abs((int)best_found - (int)hint_size), delta_new = abs((int)size - (int)hint_size);
|
||||||
|
if (delta_new < delta_old)
|
||||||
|
{
|
||||||
|
best_found = size;
|
||||||
|
best = n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else best = recycled.get_count()-1;
|
||||||
|
|
||||||
|
ret = recycled.remove_by_idx(best);
|
||||||
|
ret->set_sample_count(0);
|
||||||
|
ret->set_channels(0);
|
||||||
|
ret->set_srate(0);
|
||||||
|
}
|
||||||
|
else ret = new audio_chunk_i;
|
||||||
|
if (idx==max) data.add_item(ret);
|
||||||
|
else data.insert_item(ret,idx);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsp_chunk_list_i::~dsp_chunk_list_i() {data.delete_all();recycled.delete_all();}
|
||||||
|
|
||||||
|
void dsp_chunk_list::remove_bad_chunks()
|
||||||
|
{
|
||||||
|
bool blah = false;
|
||||||
|
unsigned idx;
|
||||||
|
for(idx=0;idx<get_count();)
|
||||||
|
{
|
||||||
|
audio_chunk * chunk = get_item(idx);
|
||||||
|
if (!chunk->is_valid())
|
||||||
|
{
|
||||||
|
chunk->reset();
|
||||||
|
remove_by_idx(idx);
|
||||||
|
blah = true;
|
||||||
|
}
|
||||||
|
else idx++;
|
||||||
|
}
|
||||||
|
if (blah) console::error("one or more bad chunks removed from dsp chunk list");
|
||||||
|
}
|
||||||
|
|
||||||
|
__int64 dsp_util::duration_samples_from_time(double time,unsigned srate)
|
||||||
|
{
|
||||||
|
return (__int64)floor((double)srate * time + 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef DSP_HAVE_ASM
|
||||||
|
|
||||||
|
__declspec(naked) void __fastcall dsp_util::convert_32_to_64(const float * src,double * dest,unsigned count)
|
||||||
|
{
|
||||||
|
_asm//src: ecx, dest: edx, count: eax
|
||||||
|
{
|
||||||
|
mov eax,dword ptr [esp+4]
|
||||||
|
shr eax,2
|
||||||
|
jz l2
|
||||||
|
l1: fld dword ptr [ecx]
|
||||||
|
fld dword ptr [ecx+4]
|
||||||
|
fstp qword ptr [edx+8]
|
||||||
|
fstp qword ptr [edx]
|
||||||
|
dec eax
|
||||||
|
fld dword ptr [ecx+8]
|
||||||
|
fld dword ptr [ecx+12]
|
||||||
|
fstp qword ptr [edx+24]
|
||||||
|
fstp qword ptr [edx+16]
|
||||||
|
lea ecx,[ecx+16]
|
||||||
|
lea edx,[edx+32]
|
||||||
|
jnz l1
|
||||||
|
l2: mov eax,dword ptr [esp+4]
|
||||||
|
and eax,3
|
||||||
|
jz l4
|
||||||
|
l3: fld dword ptr [ecx]
|
||||||
|
dec eax
|
||||||
|
fstp qword ptr [edx]
|
||||||
|
lea ecx,[ecx+4]
|
||||||
|
lea edx,[edx+8]
|
||||||
|
jnz l3
|
||||||
|
l4: ret 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(naked) void __fastcall dsp_util::convert_64_to_32(const double * src,float * dest,unsigned count)
|
||||||
|
{
|
||||||
|
_asm//src: ecx, dest: edx, count: eax
|
||||||
|
{
|
||||||
|
mov eax,dword ptr [esp+4]
|
||||||
|
shr eax,2
|
||||||
|
jz l2
|
||||||
|
l1: fld qword ptr [ecx]
|
||||||
|
fld qword ptr [ecx+8]
|
||||||
|
fstp dword ptr [edx+4]
|
||||||
|
fstp dword ptr [edx]
|
||||||
|
dec eax
|
||||||
|
fld qword ptr [ecx+16]
|
||||||
|
fld qword ptr [ecx+24]
|
||||||
|
fstp dword ptr [edx+12]
|
||||||
|
fstp dword ptr [edx+8]
|
||||||
|
lea ecx,[ecx+32]
|
||||||
|
lea edx,[edx+16]
|
||||||
|
jnz l1
|
||||||
|
l2: mov eax,dword ptr [esp+4]
|
||||||
|
and eax,3
|
||||||
|
jz l4
|
||||||
|
l3: fld qword ptr [ecx]
|
||||||
|
dec eax
|
||||||
|
fstp dword ptr [edx]
|
||||||
|
lea ecx,[ecx+8]
|
||||||
|
lea edx,[edx+4]
|
||||||
|
jnz l3
|
||||||
|
l4: ret 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
void __fastcall dsp_util::convert_32_to_64(const float * src,double * dest,unsigned count)
|
||||||
|
{
|
||||||
|
for(;count;count--)
|
||||||
|
*(dest++) = (double)*(src++);
|
||||||
|
}
|
||||||
|
|
||||||
|
void __fastcall dsp_util::convert_64_to_32(const double * src,float * dest,unsigned count)
|
||||||
|
{
|
||||||
|
for(;count;count--)
|
||||||
|
*(dest++) = (float)*(src++);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
void dsp::get_config(cfg_var::write_config_callback * out)
|
||||||
|
{
|
||||||
|
dsp_v2 * this_v2 = service_query_t(dsp_v2,this);
|
||||||
|
if (this_v2)
|
||||||
|
{
|
||||||
|
this_v2->get_config(out);
|
||||||
|
this_v2->service_release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void dsp::set_config(const void * src,unsigned bytes)
|
||||||
|
{
|
||||||
|
dsp_v2 * this_v2 = service_query_t(dsp_v2,this);
|
||||||
|
if (this_v2)
|
||||||
|
{
|
||||||
|
this_v2->set_config(src,bytes);
|
||||||
|
this_v2->service_release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool dsp::popup_config(HWND parent)
|
||||||
|
{
|
||||||
|
bool rv = false;
|
||||||
|
dsp_v2 * this_v2 = service_query_t(dsp_v2,this);
|
||||||
|
if (this_v2)
|
||||||
|
{
|
||||||
|
rv = this_v2->popup_config(parent);
|
||||||
|
this_v2->service_release();
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
|
@ -0,0 +1,196 @@
|
||||||
|
#ifndef _DSP_H_
|
||||||
|
#define _DSP_H_
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
#include "audio_chunk.h"
|
||||||
|
#include "interface_helper.h"
|
||||||
|
|
||||||
|
namespace dsp_util
|
||||||
|
{
|
||||||
|
__int64 duration_samples_from_time(double time,unsigned srate);
|
||||||
|
|
||||||
|
void kill_denormal_32(float * ptr,unsigned count);
|
||||||
|
void kill_denormal_64(double * ptr,unsigned count);
|
||||||
|
void scale(audio_sample * ptr,double scale,unsigned count);
|
||||||
|
inline void kill_denormal(audio_sample * ptr,unsigned count)
|
||||||
|
{
|
||||||
|
#if audio_sample_size == 32
|
||||||
|
kill_denormal_32(ptr,count);
|
||||||
|
#else
|
||||||
|
kill_denormal_64(ptr,count);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
inline void scale_chunk(audio_chunk * ptr,double scale) {dsp_util::scale(ptr->get_data(),scale,ptr->get_data_length());}
|
||||||
|
|
||||||
|
void __fastcall convert_32_to_64(const float * src,double * dest,unsigned count);
|
||||||
|
void __fastcall convert_64_to_32(const double * src,float * dest,unsigned count);
|
||||||
|
};
|
||||||
|
|
||||||
|
class NOVTABLE dsp_chunk_list//interface (cross-dll safe)
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual unsigned get_count() const = 0;
|
||||||
|
virtual audio_chunk * get_item(unsigned n) const = 0;
|
||||||
|
virtual void remove_by_idx(unsigned idx) = 0;
|
||||||
|
virtual void remove_mask(const bit_array & mask) = 0;
|
||||||
|
virtual audio_chunk * insert_item(unsigned idx,unsigned hint_size=0) = 0;
|
||||||
|
|
||||||
|
audio_chunk * add_item(unsigned hint_size=0) {return insert_item(get_count(),hint_size);}
|
||||||
|
|
||||||
|
void remove_all() {remove_mask(bit_array_true());}
|
||||||
|
|
||||||
|
double get_duration()
|
||||||
|
{
|
||||||
|
double rv = 0;
|
||||||
|
unsigned n,m = get_count();
|
||||||
|
for(n=0;n<m;n++) rv += get_item(n)->get_duration();
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_chunk(const audio_chunk * chunk)
|
||||||
|
{
|
||||||
|
audio_chunk * dst = insert_item(get_count(),chunk->get_data_length());
|
||||||
|
if (dst) dst->copy_from(chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove_bad_chunks();
|
||||||
|
};
|
||||||
|
|
||||||
|
class dsp_chunk_list_i : public dsp_chunk_list//implementation
|
||||||
|
{
|
||||||
|
ptr_list_simple<audio_chunk_i> data,recycled;
|
||||||
|
public:
|
||||||
|
virtual unsigned get_count() const;
|
||||||
|
virtual audio_chunk * get_item(unsigned n) const;
|
||||||
|
virtual void remove_by_idx(unsigned idx);
|
||||||
|
virtual void remove_mask(const bit_array & mask);
|
||||||
|
virtual audio_chunk * insert_item(unsigned idx,unsigned hint_size=0);
|
||||||
|
~dsp_chunk_list_i();
|
||||||
|
};
|
||||||
|
|
||||||
|
class NOVTABLE dsp : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
END_OF_TRACK = 1, //flush whatever you need to when tracks change
|
||||||
|
FLUSH = 2 //flush everything
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual GUID get_guid()=0;
|
||||||
|
virtual const char * get_name()=0;
|
||||||
|
virtual void run(dsp_chunk_list * list,class metadb_handle * cur_file,int flags)=0;//int flags <= see flags above
|
||||||
|
//cur_file is OPTIONAL and may be null
|
||||||
|
virtual void flush() {}//after seek etc
|
||||||
|
virtual double get_latency() {return 0;}//amount of data buffered (in seconds)
|
||||||
|
virtual int need_track_change_mark() {return 0;}//return 1 if you need to know exact track change point (eg. for crossfading, removing silence), will force-flush any DSPs placed before you so when you get END_OF_TRACK, chunks you get contain last samples of the track; will often break regular gapless playback so don't use it unless you have reasons to
|
||||||
|
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
|
||||||
|
static GUID guid_from_name(const char * name);//may fail if DSP isn't present (eg. dll got removed)
|
||||||
|
static const char * name_from_guid(GUID g);//may fail if DSP isn't present (eg. dll got removed)
|
||||||
|
static dsp * instance_from_guid(GUID g);//may fail if DSP isn't present (eg. dll got removed)
|
||||||
|
|
||||||
|
virtual service_base * service_query(const GUID & guid)
|
||||||
|
{
|
||||||
|
if (guid == get_class_guid()) {service_add_ref();return this;}
|
||||||
|
else return service_base::service_query(guid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_config(cfg_var::write_config_callback * out);
|
||||||
|
void set_config(const void * src,unsigned bytes);
|
||||||
|
bool popup_config(HWND parent);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class NOVTABLE dsp_v2 : public dsp
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void config_get(cfg_var::write_config_callback * out) {};
|
||||||
|
virtual void config_set(const void * src,unsigned bytes) {};
|
||||||
|
virtual bool config_popup(HWND parent) {return false;};//return false if you don't support config dialog
|
||||||
|
virtual bool config_supported()=0;
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
|
||||||
|
|
||||||
|
virtual service_base * service_query(const GUID & guid)
|
||||||
|
{
|
||||||
|
if (guid == get_class_guid()) {service_add_ref();return this;}
|
||||||
|
else return dsp::service_query(guid);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class dsp_i_base_t : public T
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
dsp_chunk_list * list;
|
||||||
|
unsigned chunk_ptr;
|
||||||
|
metadb_handle * cur_file;
|
||||||
|
virtual void run(dsp_chunk_list * p_list,class metadb_handle * p_cur_file,int flags);
|
||||||
|
protected:
|
||||||
|
inline metadb_handle * get_cur_file() {return cur_file;}// call only from on_chunk / on_endoftrack (on_endoftrack will give info on track being finished); may return null !!
|
||||||
|
dsp_i_base_t() {list = 0;cur_file=0;chunk_ptr=0;}
|
||||||
|
audio_chunk * insert_chunk(unsigned hint_size = 0) //call only from on_endoftrack / on_endofplayback / on_chunk
|
||||||
|
{//hint_size - optional, amout of buffer space you want to use
|
||||||
|
return list ? list->insert_item(chunk_ptr++,hint_size) : 0;
|
||||||
|
}
|
||||||
|
//override these
|
||||||
|
virtual void on_endoftrack()//use insert_chunk() if you have data you want to dump
|
||||||
|
{
|
||||||
|
}
|
||||||
|
virtual void on_endofplayback()
|
||||||
|
{//use insert_chunk() if you have data you want to dump
|
||||||
|
|
||||||
|
}
|
||||||
|
virtual bool on_chunk(audio_chunk * chunk)
|
||||||
|
{//return true if your chunk, possibly modified needs to be put back into chain, false if you want it removed
|
||||||
|
//use insert_chunk() if you want to insert pending data before current chunk
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual GUID get_guid()=0;
|
||||||
|
virtual const char * get_name()=0;
|
||||||
|
|
||||||
|
virtual void flush() {}//after seek etc
|
||||||
|
virtual double get_latency() {return 0;}//amount of data buffered (in seconds)
|
||||||
|
virtual int need_track_change_mark() {return 0;}//return 1 if you need to know exact track change point (eg. for crossfading, removing silence), will force-flush any DSPs placed before you so when you get END_OF_TRACK, chunks you get contain last samples of the track; will often break regular gapless playback so don't use it unless you have reasons to
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
void dsp_i_base_t<T>::run(dsp_chunk_list * p_list,class metadb_handle * p_cur_file,int flags)
|
||||||
|
{
|
||||||
|
list = p_list;
|
||||||
|
cur_file = p_cur_file;
|
||||||
|
for(chunk_ptr = 0;chunk_ptr<list->get_count();chunk_ptr++)
|
||||||
|
{
|
||||||
|
audio_chunk * c = list->get_item(chunk_ptr);
|
||||||
|
if (c->is_empty() || !on_chunk(c))
|
||||||
|
list->remove_by_idx(chunk_ptr--);
|
||||||
|
}
|
||||||
|
if (flags & FLUSH)
|
||||||
|
on_endofplayback();
|
||||||
|
else if (flags & END_OF_TRACK)
|
||||||
|
on_endoftrack();
|
||||||
|
|
||||||
|
list = 0;
|
||||||
|
cur_file = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class dsp_i_base : public dsp_i_base_t<dsp> {};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class dsp_factory : public service_factory_t<dsp,T> {};
|
||||||
|
|
||||||
|
|
||||||
|
#include "dsp_manager.h"
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,52 @@
|
||||||
|
#ifndef _DSP_MANAGER_H_
|
||||||
|
#define _DSP_MANAGER_H_
|
||||||
|
|
||||||
|
#include "dsp.h"
|
||||||
|
|
||||||
|
//use this to run user-configured DSPs on your data stream
|
||||||
|
//one implementation in main exe, dont override
|
||||||
|
class NOVTABLE dsp_manager : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual double run(dsp_chunk_list * list,class metadb_handle * cur_file,int eof)=0;
|
||||||
|
virtual void flush();
|
||||||
|
|
||||||
|
virtual void set_chain(int b_use_custom,unsigned int count,const GUID * list)=0;
|
||||||
|
//user settings from core config will be used when b_use_custom is zero, custom DSP list specified by count/list params will be used when non-zero
|
||||||
|
//user settings are used by default if you don't call set_chain()
|
||||||
|
|
||||||
|
class NOVTABLE get_default_chain_callback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void on_list(unsigned int num,const GUID * ptr)=0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class get_default_chain_callback_i : public get_default_chain_callback
|
||||||
|
{
|
||||||
|
mem_block_t<GUID> & data;
|
||||||
|
public:
|
||||||
|
virtual void on_list(unsigned int num,const GUID * ptr)
|
||||||
|
{
|
||||||
|
data.set_size(num);
|
||||||
|
data.copy(ptr,num);
|
||||||
|
}
|
||||||
|
get_default_chain_callback_i(mem_block_t<GUID> & param) : data(param) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
//use this to get user's DSP config from core preferences
|
||||||
|
virtual void get_default_chain(get_default_chain_callback * cb)=0;
|
||||||
|
|
||||||
|
void get_default_chain(mem_block_t<GUID> & dest)//helper
|
||||||
|
{
|
||||||
|
get_default_chain(&get_default_chain_callback_i(dest));
|
||||||
|
}
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
|
||||||
|
static dsp_manager* create() {return service_enum_create_t(dsp_manager,0);}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,221 @@
|
||||||
|
#include "foobar2000.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <locale.h>
|
||||||
|
|
||||||
|
void file_info::copy(const file_info * src)
|
||||||
|
{
|
||||||
|
meta_remove_all();
|
||||||
|
info_remove_all();
|
||||||
|
set_location(src->get_location());
|
||||||
|
set_length(src->get_length());
|
||||||
|
int n;
|
||||||
|
for(n=0;n<src->meta_get_count();n++)
|
||||||
|
meta_add(src->meta_enum_name(n),src->meta_enum_value(n));
|
||||||
|
for(n=0;n<src->info_get_count();n++)
|
||||||
|
info_set(src->info_enum_name(n),src->info_enum_value(n));
|
||||||
|
}
|
||||||
|
|
||||||
|
void file_info::info_set_int(const char * name,__int64 value)
|
||||||
|
{
|
||||||
|
assert(is_valid_utf8(name));
|
||||||
|
char temp[32];
|
||||||
|
_i64toa(value,temp,10);
|
||||||
|
info_set(name,temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void file_info::info_set_float(const char * name,double value,unsigned precision,bool force_sign,const char * unit)
|
||||||
|
{
|
||||||
|
assert(is_valid_utf8(name));
|
||||||
|
assert(unit==0 || strlen(unit) <= 64);
|
||||||
|
char temp[128];
|
||||||
|
pfc_float_to_string(temp,value,precision,force_sign);
|
||||||
|
if (unit)
|
||||||
|
{
|
||||||
|
strcat(temp," ");
|
||||||
|
strcat(temp,unit);
|
||||||
|
}
|
||||||
|
info_set(name,temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void file_info::info_set_replaygain_track_gain(double value)
|
||||||
|
{
|
||||||
|
info_set_float("replaygain_track_gain",value,2,true,"dB");
|
||||||
|
}
|
||||||
|
|
||||||
|
void file_info::info_set_replaygain_album_gain(double value)
|
||||||
|
{
|
||||||
|
info_set_float("replaygain_album_gain",value,2,true,"dB");
|
||||||
|
}
|
||||||
|
|
||||||
|
void file_info::info_set_replaygain_track_peak(double value)
|
||||||
|
{
|
||||||
|
info_set_float("replaygain_track_peak",value,6,false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void file_info::info_set_replaygain_album_peak(double value)
|
||||||
|
{
|
||||||
|
info_set_float("replaygain_album_peak",value,6,false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int file_info::info_get_idx(const char * name) const
|
||||||
|
{
|
||||||
|
assert(is_valid_utf8(name));
|
||||||
|
int n,m=info_get_count();
|
||||||
|
for(n=0;n<m;n++)
|
||||||
|
{
|
||||||
|
if (!stricmp_utf8(name,info_enum_name(n)))
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int file_info::meta_get_idx(const char * name,int num) const
|
||||||
|
{
|
||||||
|
assert(is_valid_utf8(name));
|
||||||
|
int n,m=meta_get_count();
|
||||||
|
for(n=0;n<m;n++)
|
||||||
|
{
|
||||||
|
if (!stricmp_utf8(name,meta_enum_name(n)))
|
||||||
|
{
|
||||||
|
if (num==0) return n;
|
||||||
|
num--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
__int64 file_info::info_get_int(const char * name) const
|
||||||
|
{
|
||||||
|
assert(is_valid_utf8(name));
|
||||||
|
const char * val = info_get(name);
|
||||||
|
if (val==0) return 0;
|
||||||
|
return _atoi64(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
__int64 file_info::info_get_length_samples() const
|
||||||
|
{
|
||||||
|
__int64 ret = 0;
|
||||||
|
double len = get_length();
|
||||||
|
__int64 srate = info_get_int("samplerate");
|
||||||
|
|
||||||
|
if (srate>0 && len>0)
|
||||||
|
{
|
||||||
|
ret = dsp_util::duration_samples_from_time(len,(unsigned)srate);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void file_info::meta_add_wide(const WCHAR * name,const WCHAR* value)
|
||||||
|
{//unicode (UTF-16) version
|
||||||
|
meta_add(string_utf8_from_wide(name),string_utf8_from_wide(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void file_info::meta_add_ansi(const char * name,const char * value)
|
||||||
|
{//ANSI version
|
||||||
|
meta_add(string_utf8_from_ansi(name),string_utf8_from_ansi(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void file_info::meta_set_wide(const WCHAR * name,const WCHAR* value)
|
||||||
|
{//widechar version
|
||||||
|
meta_set(string_utf8_from_wide(name),string_utf8_from_wide(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void file_info::meta_set_ansi(const char * name,const char * value)
|
||||||
|
{//ANSI version
|
||||||
|
meta_set(string_utf8_from_ansi(name),string_utf8_from_ansi(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void file_info::meta_add_n(const char * name,int name_len,const char * value,int value_len)
|
||||||
|
{
|
||||||
|
meta_add(string_simple(name,name_len),string_simple(value,value_len));
|
||||||
|
}
|
||||||
|
|
||||||
|
void file_info::meta_remove_field(const char * name)//removes ALL fields of given name
|
||||||
|
{
|
||||||
|
assert(is_valid_utf8(name));
|
||||||
|
int n;
|
||||||
|
for(n=meta_get_count()-1;n>=0;n--)
|
||||||
|
{
|
||||||
|
if (!stricmp_utf8(meta_enum_name(n),name))
|
||||||
|
meta_remove(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void file_info::meta_set(const char * name,const char * value) //deletes all fields of given name (if any), then adds new one
|
||||||
|
{
|
||||||
|
assert(is_valid_utf8(name));
|
||||||
|
assert(is_valid_utf8(value));
|
||||||
|
meta_remove_field(name);
|
||||||
|
meta_add(name,value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * file_info::meta_get(const char * name,int num) const
|
||||||
|
{
|
||||||
|
assert(is_valid_utf8(name));
|
||||||
|
int idx = meta_get_idx(name,num);
|
||||||
|
if (idx<0) return 0;
|
||||||
|
else return meta_enum_value(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
int file_info::meta_get_count_by_name(const char* name) const
|
||||||
|
{
|
||||||
|
assert(is_valid_utf8(name));
|
||||||
|
int n,m=meta_get_count();
|
||||||
|
int rv=0;
|
||||||
|
for(n=0;n<m;n++)
|
||||||
|
{
|
||||||
|
if (!stricmp_utf8(name,meta_enum_name(n)))
|
||||||
|
rv++;
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * file_info::info_get(const char * name) const
|
||||||
|
{
|
||||||
|
assert(is_valid_utf8(name));
|
||||||
|
int idx = info_get_idx(name);
|
||||||
|
if (idx<0) return 0;
|
||||||
|
else return info_enum_value(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void file_info::info_remove_field(const char * name)
|
||||||
|
{
|
||||||
|
assert(is_valid_utf8(name));
|
||||||
|
int n;
|
||||||
|
for(n=info_get_count()-1;n>=0;n--)
|
||||||
|
{
|
||||||
|
if (!stricmp_utf8(name,info_enum_name(n)))
|
||||||
|
info_remove(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_valid_bps(__int64 val)
|
||||||
|
{
|
||||||
|
return val>0 && val<=256;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned file_info::info_get_decoded_bps()
|
||||||
|
{
|
||||||
|
__int64 val = info_get_int("decoded_bitspersample");
|
||||||
|
if (is_valid_bps(val)) return (unsigned)val;
|
||||||
|
val = info_get_int("bitspersample");
|
||||||
|
if (is_valid_bps(val)) return (unsigned)val;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double file_info::info_get_float(const char * name)
|
||||||
|
{
|
||||||
|
const char * ptr = info_get(name);
|
||||||
|
if (ptr) return pfc_string_to_float(ptr);
|
||||||
|
else return 0;
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
#ifndef _FILE_INFO_H_
|
||||||
|
#define _FILE_INFO_H_
|
||||||
|
|
||||||
|
#include "playable_location.h"
|
||||||
|
#include "playlist_entry.h"//for compatibility with old code
|
||||||
|
|
||||||
|
//file_info == playable_location + length + metadata + tech infos
|
||||||
|
//also see: metadb.h
|
||||||
|
|
||||||
|
//all char* strings are UTF-8, including filenames, unless comments state otherwise
|
||||||
|
|
||||||
|
class NOVTABLE file_info //interface (cross-dll-safe)
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~file_info() {}
|
||||||
|
//interface
|
||||||
|
|
||||||
|
virtual void copy(const file_info * src);//can be overridden
|
||||||
|
|
||||||
|
//multiple fields of the same name ARE allowed.
|
||||||
|
virtual int meta_get_count() const = 0;
|
||||||
|
virtual const char * meta_enum_name(int n) const = 0;
|
||||||
|
virtual const char * meta_enum_value(int n) const = 0;
|
||||||
|
virtual void meta_modify_value(int n,const char * new_value) = 0;
|
||||||
|
virtual void meta_insert(int index,const char * name,const char * value) = 0;
|
||||||
|
virtual void meta_add(const char * name,const char * value) = 0;
|
||||||
|
virtual void meta_remove(int n) = 0;
|
||||||
|
virtual void meta_remove_all() = 0;
|
||||||
|
virtual int meta_get_idx(const char * name,int num = 0) const;
|
||||||
|
//tech infos (bitrate, replaygain, etc), not user-editable
|
||||||
|
//multiple fields of the same are NOT allowed.
|
||||||
|
virtual void info_set(const char * name,const char * value) = 0;//replaces existing field if found
|
||||||
|
virtual int info_get_count() const = 0;
|
||||||
|
virtual const char * info_enum_name(int n) const = 0;
|
||||||
|
virtual const char * info_enum_value(int n) const = 0;
|
||||||
|
virtual void info_remove(int n) = 0;
|
||||||
|
virtual void info_remove_all() = 0;
|
||||||
|
virtual int info_get_idx(const char * name) const;
|
||||||
|
|
||||||
|
virtual const playable_location * get_location() const = 0;
|
||||||
|
virtual void set_location(const playable_location *)=0;
|
||||||
|
|
||||||
|
virtual void set_length(double) = 0;//length in seconds
|
||||||
|
virtual double get_length() const = 0;
|
||||||
|
|
||||||
|
|
||||||
|
inline int get_subsong_index() const {return get_location()->get_number();}
|
||||||
|
inline const char * get_file_path() const {return get_location()->get_path();}
|
||||||
|
|
||||||
|
|
||||||
|
inline void reset() {meta_remove_all();info_remove_all();set_length(0);}
|
||||||
|
|
||||||
|
//helper stuff for setting meta
|
||||||
|
void meta_add_wide(const WCHAR * name,const WCHAR* value);
|
||||||
|
void meta_add_ansi(const char * name,const char * value);
|
||||||
|
void meta_set_wide(const WCHAR * name,const WCHAR* value);
|
||||||
|
void meta_set_ansi(const char * name,const char * value);
|
||||||
|
void meta_add_n(const char * name,int name_len,const char * value,int value_len);
|
||||||
|
void meta_remove_field(const char * name);//removes ALL fields of given name
|
||||||
|
void meta_set(const char * name,const char * value); //deletes all fields of given name (if any), then adds new one
|
||||||
|
const char * meta_get(const char * name,int num = 0) const;
|
||||||
|
int meta_get_count_by_name(const char* name) const;
|
||||||
|
const char * info_get(const char * name) const;
|
||||||
|
void info_remove_field(const char * name);
|
||||||
|
|
||||||
|
__int64 info_get_int(const char * name) const;
|
||||||
|
__int64 info_get_length_samples() const;
|
||||||
|
double info_get_float(const char * name);
|
||||||
|
void info_set_int(const char * name,__int64 value);
|
||||||
|
void info_set_float(const char * name,double value,unsigned precision,bool force_sign = false,const char * unit = 0);
|
||||||
|
void info_set_replaygain_track_gain(double value);
|
||||||
|
void info_set_replaygain_album_gain(double value);
|
||||||
|
void info_set_replaygain_track_peak(double value);
|
||||||
|
void info_set_replaygain_album_peak(double value);
|
||||||
|
|
||||||
|
inline __int64 info_get_bitrate_vbr() {return info_get_int("bitrate_dynamic");}
|
||||||
|
inline void info_set_bitrate_vbr(__int64 val) {info_set_int("bitrate_dynamic",val);}
|
||||||
|
inline __int64 info_get_bitrate() {return info_get_int("bitrate");}
|
||||||
|
inline void info_set_bitrate(__int64 val) {info_set_int("bitrate",val);}
|
||||||
|
|
||||||
|
unsigned info_get_decoded_bps();//what bps the stream originally was (before converting to audio_sample), 0 if unknown
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
recommended meta types:
|
||||||
|
TITLE
|
||||||
|
ARTIST
|
||||||
|
ALBUM
|
||||||
|
TRACKNUMBER (not TRACK)
|
||||||
|
DATE (not YEAR)
|
||||||
|
DISC (for disc number in a multidisc album)
|
||||||
|
GENRE
|
||||||
|
COMMENT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#endif //_FILE_INFO_H_
|
|
@ -0,0 +1,93 @@
|
||||||
|
#ifndef _FILE_INFO_HELPER_H_
|
||||||
|
#define _FILE_INFO_HELPER_H_
|
||||||
|
|
||||||
|
#include "file_info.h"
|
||||||
|
#include "metadb_handle.h"
|
||||||
|
|
||||||
|
class file_info_i : public file_info
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
playable_location_i location;
|
||||||
|
double length;
|
||||||
|
|
||||||
|
struct entry
|
||||||
|
{
|
||||||
|
string_simple name,value;
|
||||||
|
entry(const char * p_name,const char * p_value) : name(p_name), value(p_value) {}
|
||||||
|
};
|
||||||
|
ptr_list_t<entry> meta,info;
|
||||||
|
public:
|
||||||
|
file_info_i(const playable_location & src) : location(src), length(0) {}
|
||||||
|
file_info_i(const playable_location * src) : location(*src), length(0) {}
|
||||||
|
|
||||||
|
file_info_i(const char * fn,int num) : location(fn,num), length(0) {}
|
||||||
|
|
||||||
|
file_info_i() : length(0) {}
|
||||||
|
|
||||||
|
file_info_i(metadb_handle * src)
|
||||||
|
: location(*src->handle_get_location()), length(0)
|
||||||
|
{
|
||||||
|
src->handle_query(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
file_info_i(const file_info & src) : length(0) {copy(&src);}
|
||||||
|
|
||||||
|
|
||||||
|
~file_info_i() {meta.delete_all();info.delete_all();}
|
||||||
|
|
||||||
|
//multiple fields of the same name ARE allowed.
|
||||||
|
virtual int meta_get_count() const {return meta.get_count();}
|
||||||
|
virtual const char * meta_enum_name(int n) const {return meta[n]->name;}
|
||||||
|
virtual const char * meta_enum_value(int n) const {return meta[n]->value;}
|
||||||
|
|
||||||
|
virtual void meta_modify_value(int n,const char * new_value)
|
||||||
|
{
|
||||||
|
assert(is_valid_utf8(new_value));
|
||||||
|
meta[n]->value=new_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void meta_insert(int index,const char * name,const char * value)
|
||||||
|
{
|
||||||
|
assert(is_valid_utf8(name));
|
||||||
|
assert(is_valid_utf8(value));
|
||||||
|
meta.insert_item(new entry(name,value),index);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void meta_add(const char * name,const char * value)
|
||||||
|
{
|
||||||
|
assert(is_valid_utf8(name));
|
||||||
|
assert(is_valid_utf8(value));
|
||||||
|
meta.add_item(new entry(name,value));
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void meta_remove(int n) {meta.delete_by_idx(n);}
|
||||||
|
virtual void meta_remove_all() {meta.delete_all();}
|
||||||
|
|
||||||
|
//tech infos (bitrate, replaygain, etc), not user-editable
|
||||||
|
//multiple fields of the same are NOT allowed.
|
||||||
|
virtual void info_set(const char * name,const char * value)
|
||||||
|
{
|
||||||
|
assert(is_valid_utf8(name));
|
||||||
|
assert(is_valid_utf8(value));
|
||||||
|
info_remove_field(name);
|
||||||
|
info.add_item(new entry(name,value));
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int info_get_count() const {return info.get_count();}
|
||||||
|
virtual const char * info_enum_value(int n) const {return info[n]->value;}
|
||||||
|
virtual const char * info_enum_name(int n) const {return info[n]->name;}
|
||||||
|
virtual void info_remove(int n) {info.delete_by_idx(n);}
|
||||||
|
virtual void info_remove_all() {info.delete_all();}
|
||||||
|
|
||||||
|
|
||||||
|
virtual const playable_location * get_location() const {return &location;}
|
||||||
|
virtual void set_location(const playable_location * z) {location.copy(z);}
|
||||||
|
|
||||||
|
virtual void set_length(double v) {length = v;}
|
||||||
|
virtual double get_length() const {return length;}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#define file_info_i_full file_info_i
|
||||||
|
|
||||||
|
#endif //_FILE_INFO_HELPER_H_
|
|
@ -0,0 +1,49 @@
|
||||||
|
#ifndef _FOOBAR2000_H_
|
||||||
|
#define _FOOBAR2000_H_
|
||||||
|
|
||||||
|
#include "../../pfc/pfc.h"
|
||||||
|
|
||||||
|
#include "utf8api.h"
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
|
||||||
|
#include "audio_chunk.h"
|
||||||
|
#include "componentversion.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "config_var.h"
|
||||||
|
#include "console.h"
|
||||||
|
#include "coreversion.h"
|
||||||
|
#include "cvt_float_to_linear.h"
|
||||||
|
#include "diskwriter.h"
|
||||||
|
#include "dsp.h"
|
||||||
|
#include "dsp_manager.h"
|
||||||
|
#include "file_info.h"
|
||||||
|
#include "file_info_helper.h"
|
||||||
|
#include "initquit.h"
|
||||||
|
#include "input.h"
|
||||||
|
#include "input_helpers.h"
|
||||||
|
#include "menu_item.h"
|
||||||
|
#include "menu_manager.h"
|
||||||
|
#include "modeless_dialog.h"
|
||||||
|
#include "output.h"
|
||||||
|
#include "output_manager.h"
|
||||||
|
#include "play_callback.h"
|
||||||
|
#include "play_control.h"
|
||||||
|
#include "playable_location.h"
|
||||||
|
#include "playback_core.h"
|
||||||
|
#include "playlist.h"
|
||||||
|
#include "playlist_loader.h"
|
||||||
|
#include "reader.h"
|
||||||
|
#include "replaygain.h"
|
||||||
|
#include "resampler.h"
|
||||||
|
#include "service_helper.h"
|
||||||
|
#include "tagread.h"
|
||||||
|
#include "titleformat.h"
|
||||||
|
#include "ui.h"
|
||||||
|
#include "unpack.h"
|
||||||
|
#include "vis.h"
|
||||||
|
#include "playlist_switcher.h"
|
||||||
|
#include "packet_decoder.h"
|
||||||
|
#include "commandline.h"
|
||||||
|
|
||||||
|
#endif //_FOOBAR2000_H_
|
|
@ -0,0 +1,399 @@
|
||||||
|
# Microsoft Developer Studio Project File - Name="foobar2000_SDK" - Package Owner=<4>
|
||||||
|
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||||
|
# ** DO NOT EDIT **
|
||||||
|
|
||||||
|
# TARGTYPE "Win32 (x86) Static Library" 0x0104
|
||||||
|
|
||||||
|
CFG=foobar2000_SDK - Win32 Debug
|
||||||
|
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
||||||
|
!MESSAGE use the Export Makefile command and run
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE NMAKE /f "foobar2000_SDK.mak".
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE You can specify a configuration when running NMAKE
|
||||||
|
!MESSAGE by defining the macro CFG on the command line. For example:
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE NMAKE /f "foobar2000_SDK.mak" CFG="foobar2000_SDK - Win32 Debug"
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE Possible choices for configuration are:
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE "foobar2000_SDK - Win32 Release" (based on "Win32 (x86) Static Library")
|
||||||
|
!MESSAGE "foobar2000_SDK - Win32 Debug" (based on "Win32 (x86) Static Library")
|
||||||
|
!MESSAGE
|
||||||
|
|
||||||
|
# Begin Project
|
||||||
|
# PROP AllowPerConfigDependencies 0
|
||||||
|
# PROP Scc_ProjName ""
|
||||||
|
# PROP Scc_LocalPath ""
|
||||||
|
CPP=cl.exe
|
||||||
|
RSC=rc.exe
|
||||||
|
|
||||||
|
!IF "$(CFG)" == "foobar2000_SDK - Win32 Release"
|
||||||
|
|
||||||
|
# PROP BASE Use_MFC 0
|
||||||
|
# PROP BASE Use_Debug_Libraries 0
|
||||||
|
# PROP BASE Output_Dir "Release"
|
||||||
|
# PROP BASE Intermediate_Dir "Release"
|
||||||
|
# PROP BASE Target_Dir ""
|
||||||
|
# PROP Use_MFC 0
|
||||||
|
# PROP Use_Debug_Libraries 0
|
||||||
|
# PROP Output_Dir "Release"
|
||||||
|
# PROP Intermediate_Dir "Release"
|
||||||
|
# PROP Target_Dir ""
|
||||||
|
F90=df.exe
|
||||||
|
MTL=midl.exe
|
||||||
|
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
|
||||||
|
# ADD CPP /nologo /MD /W3 /O1 /Oy /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /D "UNICODE" /D "_UNICODE" /Yu"foobar2000.h" /FD /c
|
||||||
|
# ADD BASE RSC /l 0x409 /d "NDEBUG"
|
||||||
|
# ADD RSC /l 0x409 /d "NDEBUG"
|
||||||
|
BSC32=bscmake.exe
|
||||||
|
# ADD BASE BSC32 /nologo
|
||||||
|
# ADD BSC32 /nologo
|
||||||
|
LIB32=link.exe -lib
|
||||||
|
# ADD BASE LIB32 /nologo
|
||||||
|
# ADD LIB32 /nologo
|
||||||
|
|
||||||
|
!ELSEIF "$(CFG)" == "foobar2000_SDK - Win32 Debug"
|
||||||
|
|
||||||
|
# PROP BASE Use_MFC 0
|
||||||
|
# PROP BASE Use_Debug_Libraries 1
|
||||||
|
# PROP BASE Output_Dir "Debug"
|
||||||
|
# PROP BASE Intermediate_Dir "Debug"
|
||||||
|
# PROP BASE Target_Dir ""
|
||||||
|
# PROP Use_MFC 0
|
||||||
|
# PROP Use_Debug_Libraries 1
|
||||||
|
# PROP Output_Dir "Debug"
|
||||||
|
# PROP Intermediate_Dir "Debug"
|
||||||
|
# PROP Target_Dir ""
|
||||||
|
F90=df.exe
|
||||||
|
MTL=midl.exe
|
||||||
|
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
|
||||||
|
# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /D "UNICODE" /D "_UNICODE" /Yu"foobar2000.h" /FD /GZ /c
|
||||||
|
# ADD BASE RSC /l 0x409 /d "_DEBUG"
|
||||||
|
# ADD RSC /l 0x409 /d "_DEBUG"
|
||||||
|
BSC32=bscmake.exe
|
||||||
|
# ADD BASE BSC32 /nologo
|
||||||
|
# ADD BSC32 /nologo
|
||||||
|
LIB32=link.exe -lib
|
||||||
|
# ADD BASE LIB32 /nologo
|
||||||
|
# ADD LIB32 /nologo
|
||||||
|
|
||||||
|
!ENDIF
|
||||||
|
|
||||||
|
# Begin Target
|
||||||
|
|
||||||
|
# Name "foobar2000_SDK - Win32 Release"
|
||||||
|
# Name "foobar2000_SDK - Win32 Debug"
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\audio_chunk.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\audio_chunk.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\commandline.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\commandline.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\component.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\componentversion.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\config.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\config_var.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\console.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\console.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\core_api.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\coreversion.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\cvt_float_to_linear.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\diskwriter.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\diskwriter.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\dsp.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\dsp.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\dsp_manager.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\file_info.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\file_info.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\file_info_helper.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\foobar2000.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\guids.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\initquit.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\input.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\input.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\input_helpers.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\input_helpers.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\menu_item.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\menu_manager.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\menu_manager.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\metadb.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\metadb.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\metadb_handle.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\metadb_handle.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\modeless_dialog.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\modeless_dialog.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\output.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\output.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\output_manager.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\packet_decoder.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\packet_decoder.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\play_callback.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\play_control.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\play_control.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\playable_location.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\playable_location.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\playback_core.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\playlist.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\playlist.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\playlist_entry.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\playlist_loader.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\playlist_loader.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\playlist_switcher.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\reader.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\reader.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\reader_helper.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\reader_helper_mem.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\replaygain.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\resampler.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\service.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\service.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\service_helper.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\service_impl.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\stdafx.cpp
|
||||||
|
|
||||||
|
!IF "$(CFG)" == "foobar2000_SDK - Win32 Release"
|
||||||
|
|
||||||
|
# ADD CPP /Yc
|
||||||
|
|
||||||
|
!ELSEIF "$(CFG)" == "foobar2000_SDK - Win32 Debug"
|
||||||
|
|
||||||
|
# ADD CPP /Yc"foobar2000.h"
|
||||||
|
|
||||||
|
!ENDIF
|
||||||
|
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\tagread.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\tagread.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\titleformat.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\titleformat.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\ui.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\ui.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\unpack.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\utf8api.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\utf8api.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\vis.h
|
||||||
|
# End Source File
|
||||||
|
# End Target
|
||||||
|
# End Project
|
|
@ -0,0 +1,583 @@
|
||||||
|
#include "foobar2000.h"
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define FOOGUIDDECL __declspec(selectany) //hack against msvc linker stupidity
|
||||||
|
#else
|
||||||
|
#define FOOGUIDDECL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// {10BB3EBD-DDF7-4975-A3CC-23084829453E}
|
||||||
|
FOOGUIDDECL const GUID componentversion::class_guid =
|
||||||
|
{ 0x10bb3ebd, 0xddf7, 0x4975, { 0xa3, 0xcc, 0x23, 0x8, 0x48, 0x29, 0x45, 0x3e } };
|
||||||
|
|
||||||
|
// {7255E8D0-3FCF-4781-B93B-D06CB88DFAFA}
|
||||||
|
FOOGUIDDECL const GUID config::class_guid =
|
||||||
|
{ 0x7255e8d0, 0x3fcf, 0x4781, { 0xb9, 0x3b, 0xd0, 0x6c, 0xb8, 0x8d, 0xfa, 0xfa } };
|
||||||
|
|
||||||
|
// {056B51C7-E78C-4a4e-B84E-6BFF3F8AB8F2}
|
||||||
|
FOOGUIDDECL const GUID console::class_guid =
|
||||||
|
{ 0x56b51c7, 0xe78c, 0x4a4e, { 0xb8, 0x4e, 0x6b, 0xff, 0x3f, 0x8a, 0xb8, 0xf2 } };
|
||||||
|
|
||||||
|
// {0C36A649-9EA0-4f48-B229-0CB2AA9AB6F4}
|
||||||
|
FOOGUIDDECL const GUID core_version_info::class_guid =
|
||||||
|
{ 0xc36a649, 0x9ea0, 0x4f48, { 0xb2, 0x29, 0xc, 0xb2, 0xaa, 0x9a, 0xb6, 0xf4 } };
|
||||||
|
|
||||||
|
// {545F99D1-602D-4175-867F-F7C6AD680A81}
|
||||||
|
FOOGUIDDECL const GUID cvt_float_to_linear::class_guid =
|
||||||
|
{ 0x545f99d1, 0x602d, 0x4175, { 0x86, 0x7f, 0xf7, 0xc6, 0xad, 0x68, 0xa, 0x81 } };
|
||||||
|
|
||||||
|
// {D9239DB0-210B-4680-82AF-C37DBDD6A43D}
|
||||||
|
FOOGUIDDECL const GUID dsp_v2::class_guid =
|
||||||
|
{ 0xd9239db0, 0x210b, 0x4680, { 0x82, 0xaf, 0xc3, 0x7d, 0xbd, 0xd6, 0xa4, 0x3d } };
|
||||||
|
|
||||||
|
// {2A42AFEA-940B-455b-AEFF-CFDACAF52AFF}
|
||||||
|
FOOGUIDDECL const GUID dsp::class_guid =
|
||||||
|
{ 0x2a42afea, 0x940b, 0x455b, { 0xae, 0xff, 0xcf, 0xda, 0xca, 0xf5, 0x2a, 0xff } };
|
||||||
|
|
||||||
|
// {D63655B2-B0CE-4069-BDF9-E364E43FF23C}
|
||||||
|
FOOGUIDDECL const GUID input::class_guid =
|
||||||
|
{ 0xd63655b2, 0xb0ce, 0x4069, { 0xbd, 0xf9, 0xe3, 0x64, 0xe4, 0x3f, 0xf2, 0x3c } };
|
||||||
|
|
||||||
|
// {113773C4-B387-4b48-8BDF-AB58BC6CE538}
|
||||||
|
FOOGUIDDECL const GUID initquit::class_guid =
|
||||||
|
{ 0x113773c4, 0xb387, 0x4b48, { 0x8b, 0xdf, 0xab, 0x58, 0xbc, 0x6c, 0xe5, 0x38 } };
|
||||||
|
|
||||||
|
// {609D48C8-C6A6-4784-8BBD-FDD32BF0740E}
|
||||||
|
FOOGUIDDECL const GUID metadb::class_guid =
|
||||||
|
{ 0x609d48c8, 0xc6a6, 0x4784, { 0x8b, 0xbd, 0xfd, 0xd3, 0x2b, 0xf0, 0x74, 0xe } };
|
||||||
|
|
||||||
|
// {D5286BB4-FDED-47ef-A623-4C3FF056DEC1}
|
||||||
|
FOOGUIDDECL const GUID metadb_callback::class_guid =
|
||||||
|
{ 0xd5286bb4, 0xfded, 0x47ef, { 0xa6, 0x23, 0x4c, 0x3f, 0xf0, 0x56, 0xde, 0xc1 } };
|
||||||
|
|
||||||
|
// {065190FC-9C8A-4b2b-A5EC-E21EBE288234}
|
||||||
|
FOOGUIDDECL const GUID metadb_callback_simple::class_guid =
|
||||||
|
{ 0x65190fc, 0x9c8a, 0x4b2b, { 0xa5, 0xec, 0xe2, 0x1e, 0xbe, 0x28, 0x82, 0x34 } };
|
||||||
|
|
||||||
|
// {1C0802F7-CF24-49ef-B914-8B9866F19779}
|
||||||
|
FOOGUIDDECL const GUID menu_item::class_guid =
|
||||||
|
{ 0x1c0802f7, 0xcf24, 0x49ef, { 0xb9, 0x14, 0x8b, 0x98, 0x66, 0xf1, 0x97, 0x79 } };
|
||||||
|
|
||||||
|
// {C1CAF378-BCAF-4271-AE4D-45A49B9C3B79}
|
||||||
|
FOOGUIDDECL const GUID output::class_guid =
|
||||||
|
{ 0xc1caf378, 0xbcaf, 0x4271, { 0xae, 0x4d, 0x45, 0xa4, 0x9b, 0x9c, 0x3b, 0x79 } };
|
||||||
|
|
||||||
|
// {D9CF88FF-AC2B-4a8a-8A50-DA0933A03792}
|
||||||
|
FOOGUIDDECL const GUID visualisation::class_guid =
|
||||||
|
{ 0xd9cf88ff, 0xac2b, 0x4a8a, { 0x8a, 0x50, 0xda, 0x9, 0x33, 0xa0, 0x37, 0x92 } };
|
||||||
|
|
||||||
|
// {A7E799BC-9774-41cf-8785-0D91634F937B}
|
||||||
|
FOOGUIDDECL const GUID packet_decoder::class_guid =
|
||||||
|
{ 0xa7e799bc, 0x9774, 0x41cf, { 0x87, 0x85, 0xd, 0x91, 0x63, 0x4f, 0x93, 0x7b } };
|
||||||
|
|
||||||
|
// {D3BD5F53-A6D6-4346-991F-CF14DFAD2B3A}
|
||||||
|
FOOGUIDDECL const GUID menu_manager::class_guid =
|
||||||
|
{ 0xd3bd5f53, 0xa6d6, 0x4346, { 0x99, 0x1f, 0xcf, 0x14, 0xdf, 0xad, 0x2b, 0x3a } };
|
||||||
|
|
||||||
|
// {61CF61E5-7C8E-469b-88E7-CE9B217DD38E}
|
||||||
|
FOOGUIDDECL const GUID menu_manager_defaults::class_guid =
|
||||||
|
{ 0x61cf61e5, 0x7c8e, 0x469b, { 0x88, 0xe7, 0xce, 0x9b, 0x21, 0x7d, 0xd3, 0x8e } };
|
||||||
|
|
||||||
|
// {640E006E-2934-4d6c-8327-4FA9F341ECF2}
|
||||||
|
FOOGUIDDECL const GUID input_file_type::class_guid =
|
||||||
|
{ 0x640e006e, 0x2934, 0x4d6c, { 0x83, 0x27, 0x4f, 0xa9, 0xf3, 0x41, 0xec, 0xf2 } };
|
||||||
|
|
||||||
|
// {B2518A41-A251-4941-9E28-BC51F7B840C3}
|
||||||
|
FOOGUIDDECL const GUID input_v2::class_guid =
|
||||||
|
{ 0xb2518a41, 0xa251, 0x4941, { 0x9e, 0x28, 0xbc, 0x51, 0xf7, 0xb8, 0x40, 0xc3 } };
|
||||||
|
|
||||||
|
// {2DEB68AB-49ED-460a-BFC0-4936AD39BA4F}
|
||||||
|
FOOGUIDDECL const GUID diskwriter::class_guid =
|
||||||
|
{ 0x2deb68ab, 0x49ed, 0x460a, { 0xbf, 0xc0, 0x49, 0x36, 0xad, 0x39, 0xba, 0x4f } };
|
||||||
|
|
||||||
|
// {8C6775A2-56FC-4a64-89F4-2609595C86A2}
|
||||||
|
FOOGUIDDECL const GUID ui_control::class_guid =
|
||||||
|
{ 0x8c6775a2, 0x56fc, 0x4a64, { 0x89, 0xf4, 0x26, 0x9, 0x59, 0x5c, 0x86, 0xa2 } };
|
||||||
|
|
||||||
|
// {52BD7A17-540C-4a97-B812-72BC84EC4FF5}
|
||||||
|
FOOGUIDDECL const GUID ui_drop_item_callback::class_guid =
|
||||||
|
{ 0x52bd7a17, 0x540c, 0x4a97, { 0xb8, 0x12, 0x72, 0xbc, 0x84, 0xec, 0x4f, 0xf5 } };
|
||||||
|
|
||||||
|
// {85605A78-101A-47cb-9541-4101EEBAC724}
|
||||||
|
FOOGUIDDECL const GUID track_indexer_v2::class_guid =
|
||||||
|
{ 0x85605a78, 0x101a, 0x47cb, { 0x95, 0x41, 0x41, 0x1, 0xee, 0xba, 0xc7, 0x24 } };
|
||||||
|
|
||||||
|
// {550B3A19-42A4-4c0f-91F2-90550189CBFF}
|
||||||
|
FOOGUIDDECL const GUID commandline_handler::class_guid =
|
||||||
|
{ 0x550b3a19, 0x42a4, 0x4c0f, { 0x91, 0xf2, 0x90, 0x55, 0x1, 0x89, 0xcb, 0xff } };
|
||||||
|
|
||||||
|
// {B92230CE-12A1-49f4-AFC7-3B5C57AD3760}
|
||||||
|
FOOGUIDDECL const GUID playback_flow_control::class_guid =
|
||||||
|
{ 0xb92230ce, 0x12a1, 0x49f4, { 0xaf, 0xc7, 0x3b, 0x5c, 0x57, 0xad, 0x37, 0x60 } };
|
||||||
|
|
||||||
|
// {7E82BE03-5956-412b-A9C4-6D1A3E2D7123}
|
||||||
|
FOOGUIDDECL const GUID playback_flow_control_v2::class_guid =
|
||||||
|
{ 0x7e82be03, 0x5956, 0x412b, { 0xa9, 0xc4, 0x6d, 0x1a, 0x3e, 0x2d, 0x71, 0x23 } };
|
||||||
|
|
||||||
|
// {8A17F5C7-0EA0-409c-93F6-3A2BF9CE65BF}
|
||||||
|
FOOGUIDDECL const GUID dsp_manager::class_guid =
|
||||||
|
{ 0x8a17f5c7, 0xea0, 0x409c, { 0x93, 0xf6, 0x3a, 0x2b, 0xf9, 0xce, 0x65, 0xbf } };
|
||||||
|
|
||||||
|
// {C71B99BD-12C5-48fe-A9C0-469F6FEA88BF}
|
||||||
|
FOOGUIDDECL const GUID modeless_dialog_manager::class_guid =
|
||||||
|
{ 0xc71b99bd, 0x12c5, 0x48fe, { 0xa9, 0xc0, 0x46, 0x9f, 0x6f, 0xea, 0x88, 0xbf } };
|
||||||
|
|
||||||
|
// {05C42951-1373-43ed-8F0C-F7B55D867868}
|
||||||
|
FOOGUIDDECL const GUID output_manager::class_guid =
|
||||||
|
{ 0x5c42951, 0x1373, 0x43ed, { 0x8f, 0xc, 0xf7, 0xb5, 0x5d, 0x86, 0x78, 0x68 } };
|
||||||
|
|
||||||
|
// {CF91D5CB-743B-401a-A8CC-E149F8944CCB}
|
||||||
|
FOOGUIDDECL const GUID play_callback::class_guid =
|
||||||
|
{ 0xcf91d5cb, 0x743b, 0x401a, { 0xa8, 0xcc, 0xe1, 0x49, 0xf8, 0x94, 0x4c, 0xcb } };
|
||||||
|
|
||||||
|
// {66E4FD37-5839-4233-A2D3-787E86A69011}
|
||||||
|
FOOGUIDDECL const GUID play_control::class_guid =
|
||||||
|
{ 0x66e4fd37, 0x5839, 0x4233, { 0xa2, 0xd3, 0x78, 0x7e, 0x86, 0xa6, 0x90, 0x11 } };
|
||||||
|
|
||||||
|
// {04AEC60C-BF22-46a6-B69E-BB8B4BE31AC2}
|
||||||
|
FOOGUIDDECL const GUID playback_core::class_guid =
|
||||||
|
{ 0x4aec60c, 0xbf22, 0x46a6, { 0xb6, 0x9e, 0xbb, 0x8b, 0x4b, 0xe3, 0x1a, 0xc2 } };
|
||||||
|
|
||||||
|
// {1673784D-E82E-4a70-8FFE-3D5613BD7C3C}
|
||||||
|
FOOGUIDDECL const GUID play_sound::class_guid =
|
||||||
|
{ 0x1673784d, 0xe82e, 0x4a70, { 0x8f, 0xfe, 0x3d, 0x56, 0x13, 0xbd, 0x7c, 0x3c } };
|
||||||
|
|
||||||
|
// {F384709D-7B49-472f-8D41-9D622877BA4E}
|
||||||
|
FOOGUIDDECL const GUID playlist_callback::class_guid =
|
||||||
|
{ 0xf384709d, 0x7b49, 0x472f, { 0x8d, 0x41, 0x9d, 0x62, 0x28, 0x77, 0xba, 0x4e } };
|
||||||
|
|
||||||
|
// {D2E5F92B-3424-4822-AE60-8663E6D26EAB}
|
||||||
|
FOOGUIDDECL const GUID playlist_loader::class_guid =
|
||||||
|
{ 0xd2e5f92b, 0x3424, 0x4822, { 0xae, 0x60, 0x86, 0x63, 0xe6, 0xd2, 0x6e, 0xab } };
|
||||||
|
|
||||||
|
// {95510C89-9968-4ebe-9968-5290CAAB39D2}
|
||||||
|
FOOGUIDDECL const GUID track_indexer::class_guid =
|
||||||
|
{ 0x95510c89, 0x9968, 0x4ebe, { 0x99, 0x68, 0x52, 0x90, 0xca, 0xab, 0x39, 0xd2 } };
|
||||||
|
|
||||||
|
// {2FBCE1E5-902E-49e0-B9CF-CE0FBA765348}
|
||||||
|
FOOGUIDDECL const GUID file::class_guid =
|
||||||
|
{ 0x2fbce1e5, 0x902e, 0x49e0, { 0xb9, 0xcf, 0xce, 0xf, 0xba, 0x76, 0x53, 0x48 } };
|
||||||
|
|
||||||
|
// {E84E9847-ADBB-447b-92F8-E9446EF2F427}
|
||||||
|
FOOGUIDDECL const GUID directory::class_guid =
|
||||||
|
{ 0xe84e9847, 0xadbb, 0x447b, { 0x92, 0xf8, 0xe9, 0x44, 0x6e, 0xf2, 0xf4, 0x27 } };
|
||||||
|
|
||||||
|
// {9098AF12-61A3-4caa-8AA9-BB95C2EF8346}
|
||||||
|
FOOGUIDDECL const GUID unpacker::class_guid =
|
||||||
|
{ 0x9098af12, 0x61a3, 0x4caa, { 0x8a, 0xa9, 0xbb, 0x95, 0xc2, 0xef, 0x83, 0x46 } };
|
||||||
|
|
||||||
|
// {EC707440-FA3E-4d12-9876-FC369F04D4A4}
|
||||||
|
FOOGUIDDECL const GUID archive::class_guid =
|
||||||
|
{ 0xec707440, 0xfa3e, 0x4d12, { 0x98, 0x76, 0xfc, 0x36, 0x9f, 0x4, 0xd4, 0xa4 } };
|
||||||
|
|
||||||
|
// {B2F9FC40-3E55-4b23-A2C9-22BAAD8795B1}
|
||||||
|
FOOGUIDDECL const GUID reader::class_guid =
|
||||||
|
{ 0xb2f9fc40, 0x3e55, 0x4b23, { 0xa2, 0xc9, 0x22, 0xba, 0xad, 0x87, 0x95, 0xb1 } };
|
||||||
|
|
||||||
|
// {C40598AA-089B-4c9f-AFCA-5FEE1CA1973D}
|
||||||
|
FOOGUIDDECL const GUID reader_dynamicinfo::class_guid =
|
||||||
|
{ 0xc40598aa, 0x89b, 0x4c9f, { 0xaf, 0xca, 0x5f, 0xee, 0x1c, 0xa1, 0x97, 0x3d } };
|
||||||
|
|
||||||
|
// {376B8EA5-DA53-4a83-881B-E92CF73D8D60}
|
||||||
|
FOOGUIDDECL const GUID reader_filetime::class_guid =
|
||||||
|
{ 0x376b8ea5, 0xda53, 0x4a83, { 0x88, 0x1b, 0xe9, 0x2c, 0xf7, 0x3d, 0x8d, 0x60 } };
|
||||||
|
|
||||||
|
// {A00CB77D-ED72-4031-806B-4E45AF995241}
|
||||||
|
FOOGUIDDECL const GUID replaygain::class_guid =
|
||||||
|
{ 0xa00cb77d, 0xed72, 0x4031, { 0x80, 0x6b, 0x4e, 0x45, 0xaf, 0x99, 0x52, 0x41 } };
|
||||||
|
|
||||||
|
// {3FEED4FC-A400-4a30-8E73-F0ECD114D7E8}
|
||||||
|
FOOGUIDDECL const GUID resampler::class_guid =
|
||||||
|
{ 0x3feed4fc, 0xa400, 0x4a30, { 0x8e, 0x73, 0xf0, 0xec, 0xd1, 0x14, 0xd7, 0xe8 } };
|
||||||
|
|
||||||
|
// {BB360EDA-1E11-4604-87FC-56CE6C249DD7}
|
||||||
|
FOOGUIDDECL const GUID tag_reader::class_guid =
|
||||||
|
{ 0xbb360eda, 0x1e11, 0x4604, { 0x87, 0xfc, 0x56, 0xce, 0x6c, 0x24, 0x9d, 0xd7 } };
|
||||||
|
|
||||||
|
// {2BE790FB-070F-4b96-BF68-DA579EACE69A}
|
||||||
|
FOOGUIDDECL const GUID tag_writer::class_guid =
|
||||||
|
{ 0x2be790fb, 0x70f, 0x4b96, { 0xbf, 0x68, 0xda, 0x57, 0x9e, 0xac, 0xe6, 0x9a } };
|
||||||
|
|
||||||
|
// {E434F1CA-92DA-482c-A76A-2129EE1CD2E3}
|
||||||
|
FOOGUIDDECL const GUID tag_remover::class_guid =
|
||||||
|
{ 0xe434f1ca, 0x92da, 0x482c, { 0xa7, 0x6a, 0x21, 0x29, 0xee, 0x1c, 0xd2, 0xe3 } };
|
||||||
|
|
||||||
|
// {3F7674AB-044C-4796-8801-6C443C244D88}
|
||||||
|
FOOGUIDDECL const GUID titleformat::class_guid =
|
||||||
|
{ 0x3f7674ab, 0x44c, 0x4796, { 0x88, 0x1, 0x6c, 0x44, 0x3c, 0x24, 0x4d, 0x88 } };
|
||||||
|
|
||||||
|
// {A2AC631F-38D0-46ee-B9B1-F1599DF02BB6}
|
||||||
|
FOOGUIDDECL const GUID user_interface::class_guid =
|
||||||
|
{ 0xa2ac631f, 0x38d0, 0x46ee, { 0xb9, 0xb1, 0xf1, 0x59, 0x9d, 0xf0, 0x2b, 0xb6 } };
|
||||||
|
|
||||||
|
// {8634CA5E-7CE1-419c-97C9-B47B5B6C538F}
|
||||||
|
FOOGUIDDECL const GUID playlist_switcher::class_guid =
|
||||||
|
{ 0x8634ca5e, 0x7ce1, 0x419c, { 0x97, 0xc9, 0xb4, 0x7b, 0x5b, 0x6c, 0x53, 0x8f } };
|
||||||
|
|
||||||
|
// {7F0FB76C-6BB7-4435-8337-8E7598F44BDA}
|
||||||
|
FOOGUIDDECL const GUID playlist_switcher_callback::class_guid =
|
||||||
|
{ 0x7f0fb76c, 0x6bb7, 0x4435, { 0x83, 0x37, 0x8e, 0x75, 0x98, 0xf4, 0x4b, 0xda } };
|
||||||
|
|
||||||
|
// {8CC36FD2-9BC6-46b9-A748-1568DE4CD602}
|
||||||
|
FOOGUIDDECL const GUID playlist_oper::class_guid =
|
||||||
|
{ 0x8cc36fd2, 0x9bc6, 0x46b9, { 0xa7, 0x48, 0x15, 0x68, 0xde, 0x4c, 0xd6, 0x2 } };
|
||||||
|
|
||||||
|
// {994C0D0E-319E-45f3-92FC-518616E73ADC}
|
||||||
|
FOOGUIDDECL const GUID menu_item::caller_now_playing =
|
||||||
|
{ 0x994c0d0e, 0x319e, 0x45f3, { 0x92, 0xfc, 0x51, 0x86, 0x16, 0xe7, 0x3a, 0xdc } };
|
||||||
|
|
||||||
|
// {47502BA1-816D-4a3e-ADE5-A7A9860A67DB}
|
||||||
|
FOOGUIDDECL const GUID menu_item::caller_playlist =
|
||||||
|
{ 0x47502ba1, 0x816d, 0x4a3e, { 0xad, 0xe5, 0xa7, 0xa9, 0x86, 0xa, 0x67, 0xdb } };
|
||||||
|
|
||||||
|
// {00000000-0000-0000-0000-000000000000}
|
||||||
|
FOOGUIDDECL const GUID menu_item::caller_undefined =
|
||||||
|
{ 0x00000000, 0x0000, 0x0000, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } };
|
||||||
|
|
||||||
|
// {79D28AA9-4E71-4b9b-98BA-36DB16593FDF}
|
||||||
|
FOOGUIDDECL const GUID menu_item_v2::class_guid =
|
||||||
|
{ 0x79d28aa9, 0x4e71, 0x4b9b, { 0x98, 0xba, 0x36, 0xdb, 0x16, 0x59, 0x3f, 0xdf } };
|
||||||
|
|
||||||
|
// {95DE5842-30F5-4f72-B40C-191663782F80}
|
||||||
|
FOOGUIDDECL const GUID keyboard_shortcut_manager::class_guid =
|
||||||
|
{ 0x95de5842, 0x30f5, 0x4f72, { 0xb4, 0xc, 0x19, 0x16, 0x63, 0x78, 0x2f, 0x80 } };
|
||||||
|
|
||||||
|
// {FF77A2C6-7ACD-483d-841C-A052E5CA5E03}
|
||||||
|
FOOGUIDDECL const GUID config_var::class_guid =
|
||||||
|
{ 0xff77a2c6, 0x7acd, 0x483d, { 0x84, 0x1c, 0xa0, 0x52, 0xe5, 0xca, 0x5e, 0x3 } };
|
||||||
|
|
||||||
|
// {30F95BEB-FDF4-4a75-B597-60CAF93B39C4}
|
||||||
|
FOOGUIDDECL const GUID packet_decoder::owner_MP4 =
|
||||||
|
{ 0x30f95beb, 0xfdf4, 0x4a75, { 0xb5, 0x97, 0x60, 0xca, 0xf9, 0x3b, 0x39, 0xc4 } };
|
||||||
|
|
||||||
|
// {5C2DE804-EAEE-4b8e-8C14-9207A2549BBE}
|
||||||
|
FOOGUIDDECL const GUID packet_decoder::owner_matroska =
|
||||||
|
{ 0x5c2de804, 0xeaee, 0x4b8e, { 0x8c, 0x14, 0x92, 0x7, 0xa2, 0x54, 0x9b, 0xbe } };
|
||||||
|
|
||||||
|
// {7B741A69-1AC7-440d-A01D-88536DD4DE1C}
|
||||||
|
FOOGUIDDECL const GUID packet_decoder::owner_MP3 =
|
||||||
|
{ 0x7b741a69, 0x1ac7, 0x440d, { 0xa0, 0x1d, 0x88, 0x53, 0x6d, 0xd4, 0xde, 0x1c } };
|
||||||
|
|
||||||
|
// {BC73F9FC-0107-480e-BF0E-BE58AF7AF328}
|
||||||
|
FOOGUIDDECL const GUID packet_decoder::property_samplerate =
|
||||||
|
{ 0xbc73f9fc, 0x107, 0x480e, { 0xbf, 0xe, 0xbe, 0x58, 0xaf, 0x7a, 0xf3, 0x28 } };
|
||||||
|
|
||||||
|
// {E5D19AAD-931B-48ac-AA6E-95E2C23BEC37}
|
||||||
|
FOOGUIDDECL const GUID packet_decoder::property_bitspersample =
|
||||||
|
{ 0xe5d19aad, 0x931b, 0x48ac, { 0xaa, 0x6e, 0x95, 0xe2, 0xc2, 0x3b, 0xec, 0x37 } };
|
||||||
|
|
||||||
|
// {1AFA1145-E774-4c26-91D6-3F5DD61E260E}
|
||||||
|
FOOGUIDDECL const GUID packet_decoder::property_channels =
|
||||||
|
{ 0x1afa1145, 0xe774, 0x4c26, { 0x91, 0xd6, 0x3f, 0x5d, 0xd6, 0x1e, 0x26, 0xe } };
|
||||||
|
|
||||||
|
// {29C556DA-065A-4d24-8A11-0F9DBC05A817}
|
||||||
|
FOOGUIDDECL const GUID packet_decoder::property_byteorder =
|
||||||
|
{ 0x29c556da, 0x65a, 0x4d24, { 0x8a, 0x11, 0xf, 0x9d, 0xbc, 0x5, 0xa8, 0x17 } };
|
||||||
|
|
||||||
|
// {0759C32F-E78E-4479-B0C0-B653DFA014D8}
|
||||||
|
FOOGUIDDECL const GUID packet_decoder::property_signed =
|
||||||
|
{ 0x759c32f, 0xe78e, 0x4479, { 0xb0, 0xc0, 0xb6, 0x53, 0xdf, 0xa0, 0x14, 0xd8 } };
|
||||||
|
|
||||||
|
// {646930F3-30AC-40e7-B7E4-EB7DFD2C0A47}
|
||||||
|
FOOGUIDDECL const GUID config_var_int::var_type =
|
||||||
|
{ 0x646930f3, 0x30ac, 0x40e7, { 0xb7, 0xe4, 0xeb, 0x7d, 0xfd, 0x2c, 0xa, 0x47 } };
|
||||||
|
|
||||||
|
// {335B8AA3-D05F-459a-A15C-115A3AEB2507}
|
||||||
|
FOOGUIDDECL const GUID config_var_struct_var_type =
|
||||||
|
{ 0x335b8aa3, 0xd05f, 0x459a, { 0xa1, 0x5c, 0x11, 0x5a, 0x3a, 0xeb, 0x25, 0x7 } };
|
||||||
|
|
||||||
|
// {FA9DA2B0-1F5E-42ab-866B-A567E1CE861B}
|
||||||
|
FOOGUIDDECL const GUID config_var_string::var_type =
|
||||||
|
{ 0xfa9da2b0, 0x1f5e, 0x42ab, { 0x86, 0x6b, 0xa5, 0x67, 0xe1, 0xce, 0x86, 0x1b } };
|
||||||
|
|
||||||
|
// {6F441057-1D18-4a58-9AC4-8F409CDA7DFD}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_context_file_properties =
|
||||||
|
{ 0x6f441057, 0x1d18, 0x4a58, { 0x9a, 0xc4, 0x8f, 0x40, 0x9c, 0xda, 0x7d, 0xfd } };
|
||||||
|
|
||||||
|
// {6F441057-1D18-4a58-9AC4-8F409CDA7DFD}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_context_file_open_directory =
|
||||||
|
{ 0x6f441057, 0x1d18, 0x4a58, { 0x9a, 0xc4, 0x8f, 0x40, 0x9c, 0xda, 0x7d, 0xfd } };
|
||||||
|
|
||||||
|
// {FFE18008-BCA2-4b29-AB88-8816B492C434}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_context_copy_names =
|
||||||
|
{ 0xffe18008, 0xbca2, 0x4b29, { 0xab, 0x88, 0x88, 0x16, 0xb4, 0x92, 0xc4, 0x34 } };
|
||||||
|
|
||||||
|
// {44B8F02B-5408-4361-8240-18DEC881B95E}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_context_send_to_playlist =
|
||||||
|
{ 0x44b8f02b, 0x5408, 0x4361, { 0x82, 0x40, 0x18, 0xde, 0xc8, 0x81, 0xb9, 0x5e } };
|
||||||
|
|
||||||
|
// {8C3BA2CB-BC4D-4752-8282-C6F9AED75A78}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_context_reload_info =
|
||||||
|
{ 0x8c3ba2cb, 0xbc4d, 0x4752, { 0x82, 0x82, 0xc6, 0xf9, 0xae, 0xd7, 0x5a, 0x78 } };
|
||||||
|
|
||||||
|
// {BD045EA4-E5E9-4206-8FF9-12AD9F5DCDE1}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_context_reload_info_if_changed =
|
||||||
|
{ 0xbd045ea4, 0xe5e9, 0x4206, { 0x8f, 0xf9, 0x12, 0xad, 0x9f, 0x5d, 0xcd, 0xe1 } };
|
||||||
|
|
||||||
|
// {684D9FBB-4383-44a2-9789-7EE1376209C6}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_context_rewrite_info =
|
||||||
|
{ 0x684d9fbb, 0x4383, 0x44a2, { 0x97, 0x89, 0x7e, 0xe1, 0x37, 0x62, 0x9, 0xc6 } };
|
||||||
|
|
||||||
|
// {860179B7-962F-4340-ACAD-0DDAF060A6B8}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_context_remove_tags =
|
||||||
|
{ 0x860179b7, 0x962f, 0x4340, { 0xac, 0xad, 0xd, 0xda, 0xf0, 0x60, 0xa6, 0xb8 } };
|
||||||
|
|
||||||
|
// {4DD1E1AD-F481-480c-BC3E-DD9C878EAFC3}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_context_remove_from_database =
|
||||||
|
{ 0x4dd1e1ad, 0xf481, 0x480c, { 0xbc, 0x3e, 0xdd, 0x9c, 0x87, 0x8e, 0xaf, 0xc3 } };
|
||||||
|
|
||||||
|
// {A7E7ECB7-1943-4907-83B3-60E92353C7FA}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_context_convert_run =
|
||||||
|
{ 0xa7e7ecb7, 0x1943, 0x4907, { 0x83, 0xb3, 0x60, 0xe9, 0x23, 0x53, 0xc7, 0xfa } };
|
||||||
|
|
||||||
|
// {A58AE6EA-A34F-4879-B25C-F31F2CBC4DA9}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_context_convert_run_singlefile =
|
||||||
|
{ 0xa58ae6ea, 0xa34f, 0x4879, { 0xb2, 0x5c, 0xf3, 0x1f, 0x2c, 0xbc, 0x4d, 0xa9 } };
|
||||||
|
|
||||||
|
// {A8EFA42D-76CB-42bc-8803-70516567B13D}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_context_write_cd =
|
||||||
|
{ 0xa8efa42d, 0x76cb, 0x42bc, { 0x88, 0x3, 0x70, 0x51, 0x65, 0x67, 0xb1, 0x3d } };
|
||||||
|
|
||||||
|
// {487DAED1-02FB-4336-A813-5E90317AB041}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_context_rg_scan_track =
|
||||||
|
{ 0x487daed1, 0x2fb, 0x4336, { 0xa8, 0x13, 0x5e, 0x90, 0x31, 0x7a, 0xb0, 0x41 } };
|
||||||
|
|
||||||
|
// {3850F34C-F619-4296-B8A0-26C617B1D16C}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_context_rg_scan_album =
|
||||||
|
{ 0x3850f34c, 0xf619, 0x4296, { 0xb8, 0xa0, 0x26, 0xc6, 0x17, 0xb1, 0xd1, 0x6c } };
|
||||||
|
|
||||||
|
// {6A2DBA02-260C-436f-8F41-0190A4298266}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_context_rg_scan_album_multi =
|
||||||
|
{ 0x6a2dba02, 0x260c, 0x436f, { 0x8f, 0x41, 0x1, 0x90, 0xa4, 0x29, 0x82, 0x66 } };
|
||||||
|
|
||||||
|
// {54C82A92-5824-4381-8D1B-79FBB1E2ABB8}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_context_rg_remove =
|
||||||
|
{ 0x54c82a92, 0x5824, 0x4381, { 0x8d, 0x1b, 0x79, 0xfb, 0xb1, 0xe2, 0xab, 0xb8 } };
|
||||||
|
|
||||||
|
// {69EAA594-13D9-4237-9BD7-11A39FDD1454}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_context_save_playlist =
|
||||||
|
{ 0x69eaa594, 0x13d9, 0x4237, { 0x9b, 0xd7, 0x11, 0xa3, 0x9f, 0xdd, 0x14, 0x54 } };
|
||||||
|
|
||||||
|
// {2BEB6836-C657-40ef-BB6E-D5B222AB89CE}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_context_masstag_edit =
|
||||||
|
{ 0x2beb6836, 0xc657, 0x40ef, { 0xbb, 0x6e, 0xd5, 0xb2, 0x22, 0xab, 0x89, 0xce } };
|
||||||
|
|
||||||
|
// {A579FF07-5D0B-48ed-A071-B6FCE4385AA9}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_context_masstag_rename =
|
||||||
|
{ 0xa579ff07, 0x5d0b, 0x48ed, { 0xa0, 0x71, 0xb6, 0xfc, 0xe4, 0x38, 0x5a, 0xa9 } };
|
||||||
|
|
||||||
|
// {77CFBCD0-98DC-4015-B327-D7142C664806}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_always_on_top =
|
||||||
|
{ 0x77cfbcd0, 0x98dc, 0x4015, { 0xb3, 0x27, 0xd7, 0x14, 0x2c, 0x66, 0x48, 0x6 } };
|
||||||
|
|
||||||
|
// {11213A01-9F36-4e69-A1BB-7A72F418DE3A}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_preferences =
|
||||||
|
{ 0x11213a01, 0x9f36, 0x4e69, { 0xa1, 0xbb, 0x7a, 0x72, 0xf4, 0x18, 0xde, 0x3a } };
|
||||||
|
|
||||||
|
// {EDA23441-5D38-4499-A22C-FE0CE0A987D9}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_about =
|
||||||
|
{ 0xeda23441, 0x5d38, 0x4499, { 0xa2, 0x2c, 0xfe, 0xc, 0xe0, 0xa9, 0x87, 0xd9 } };
|
||||||
|
|
||||||
|
// {6D38C73A-15D8-472c-8E68-6F946B82ECB4}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_exit =
|
||||||
|
{ 0x6d38c73a, 0x15d8, 0x472c, { 0x8e, 0x68, 0x6f, 0x94, 0x6b, 0x82, 0xec, 0xb4 } };
|
||||||
|
|
||||||
|
// {EF9B60FE-CB03-4c40-A8FD-3F1821020B37}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_restart =
|
||||||
|
{ 0xef9b60fe, 0xcb03, 0x4c40, { 0xa8, 0xfd, 0x3f, 0x18, 0x21, 0x2, 0xb, 0x37 } };
|
||||||
|
|
||||||
|
// {90500F09-F16F-415e-A047-AC5045C95FFE}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_activate =
|
||||||
|
{ 0x90500f09, 0xf16f, 0x415e, { 0xa0, 0x47, 0xac, 0x50, 0x45, 0xc9, 0x5f, 0xfe } };
|
||||||
|
|
||||||
|
// {13C17E8D-0D6F-41a4-B24A-59371043E925}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_hide =
|
||||||
|
{ 0x13c17e8d, 0xd6f, 0x41a4, { 0xb2, 0x4a, 0x59, 0x37, 0x10, 0x43, 0xe9, 0x25 } };
|
||||||
|
|
||||||
|
// {D9473FB2-BF11-4be0-972F-0E43F166A118}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_activate_or_hide =
|
||||||
|
{ 0xd9473fb2, 0xbf11, 0x4be0, { 0x97, 0x2f, 0xe, 0x43, 0xf1, 0x66, 0xa1, 0x18 } };
|
||||||
|
|
||||||
|
// {91E349DA-8800-42e5-BC8C-F3A92577AE84}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_titleformat_help =
|
||||||
|
{ 0x91e349da, 0x8800, 0x42e5, { 0xbc, 0x8c, 0xf3, 0xa9, 0x25, 0x77, 0xae, 0x84 } };
|
||||||
|
|
||||||
|
// {FBCFE01C-6C57-4e6a-A9F1-62334640DC91}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_follow_cursor =
|
||||||
|
{ 0xfbcfe01c, 0x6c57, 0x4e6a, { 0xa9, 0xf1, 0x62, 0x33, 0x46, 0x40, 0xdc, 0x91 } };
|
||||||
|
|
||||||
|
// {E58895A0-A2F0-45b6-8799-1440E4DB7284}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_next =
|
||||||
|
{ 0xe58895a0, 0xa2f0, 0x45b6, { 0x87, 0x99, 0x14, 0x40, 0xe4, 0xdb, 0x72, 0x84 } };
|
||||||
|
|
||||||
|
// {059C4566-4708-4ebf-8139-6A8EA5A9DFC8}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_previous =
|
||||||
|
{ 0x59c4566, 0x4708, 0x4ebf, { 0x81, 0x39, 0x6a, 0x8e, 0xa5, 0xa9, 0xdf, 0xc8 } };
|
||||||
|
|
||||||
|
// {18B1278A-F1BB-4b48-BC3D-6EC9EF80AD19}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_next_or_random =
|
||||||
|
{ 0x18b1278a, 0xf1bb, 0x4b48, { 0xbc, 0x3d, 0x6e, 0xc9, 0xef, 0x80, 0xad, 0x19 } };
|
||||||
|
|
||||||
|
// {42BDA765-30A8-40fa-BFA4-6A4E2F2B2CE9}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_random =
|
||||||
|
{ 0x42bda765, 0x30a8, 0x40fa, { 0xbf, 0xa4, 0x6a, 0x4e, 0x2f, 0x2b, 0x2c, 0xe9 } };
|
||||||
|
|
||||||
|
// {FCEF5262-7FA5-452e-A527-C14E0CB582DE}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_pause =
|
||||||
|
{ 0xfcef5262, 0x7fa5, 0x452e, { 0xa5, 0x27, 0xc1, 0x4e, 0xc, 0xb5, 0x82, 0xde } };
|
||||||
|
|
||||||
|
// {D3F83B15-D4AF-4586-8BD0-4EA415E31FE1}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_play =
|
||||||
|
{ 0xd3f83b15, 0xd4af, 0x4586, { 0x8b, 0xd0, 0x4e, 0xa4, 0x15, 0xe3, 0x1f, 0xe1 } };
|
||||||
|
|
||||||
|
// {8DEBC44E-EDA2-48d4-8696-31FC29D1F383}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_play_or_pause =
|
||||||
|
{ 0x8debc44e, 0xeda2, 0x48d4, { 0x86, 0x96, 0x31, 0xfc, 0x29, 0xd1, 0xf3, 0x83 } };
|
||||||
|
|
||||||
|
// {2DF17F25-80B9-4a43-B21D-620458FDDE1E}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_rg_set_album =
|
||||||
|
{ 0x2df17f25, 0x80b9, 0x4a43, { 0xb2, 0x1d, 0x62, 0x4, 0x58, 0xfd, 0xde, 0x1e } };
|
||||||
|
|
||||||
|
// {C26F1955-5753-4836-887F-84A563DD6DD9}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_rg_set_track =
|
||||||
|
{ 0xc26f1955, 0x5753, 0x4836, { 0x88, 0x7f, 0x84, 0xa5, 0x63, 0xdd, 0x6d, 0xd9 } };
|
||||||
|
|
||||||
|
// {8D2D808E-6AA2-455b-A2F1-CDB019328140}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_rg_disable =
|
||||||
|
{ 0x8d2d808e, 0x6aa2, 0x455b, { 0xa2, 0xf1, 0xcd, 0xb0, 0x19, 0x32, 0x81, 0x40 } };
|
||||||
|
|
||||||
|
// {C3378028-165F-4374-966C-2FA2E0FCD3A8}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_stop =
|
||||||
|
{ 0xc3378028, 0x165f, 0x4374, { 0x96, 0x6c, 0x2f, 0xa2, 0xe0, 0xfc, 0xd3, 0xa8 } };
|
||||||
|
|
||||||
|
// {EE057982-22F9-4862-A986-859E463316FB}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_stop_after_current =
|
||||||
|
{ 0xee057982, 0x22f9, 0x4862, { 0xa9, 0x86, 0x85, 0x9e, 0x46, 0x33, 0x16, 0xfb } };
|
||||||
|
|
||||||
|
// {11DC6526-73C4-44f0-91B1-DE5C2D26B0C7}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_volume_down =
|
||||||
|
{ 0x11dc6526, 0x73c4, 0x44f0, { 0x91, 0xb1, 0xde, 0x5c, 0x2d, 0x26, 0xb0, 0xc7 } };
|
||||||
|
|
||||||
|
// {A313E630-A04A-4ae8-B5B4-0A944AC964FF}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_volume_up =
|
||||||
|
{ 0xa313e630, 0xa04a, 0x4ae8, { 0xb5, 0xb4, 0xa, 0x94, 0x4a, 0xc9, 0x64, 0xff } };
|
||||||
|
|
||||||
|
// {4A36285B-B4AF-46ed-A1AA-6333057F485B}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_volume_mute =
|
||||||
|
{ 0x4a36285b, 0xb4af, 0x46ed, { 0xa1, 0xaa, 0x63, 0x33, 0x5, 0x7f, 0x48, 0x5b } };
|
||||||
|
|
||||||
|
// {2DC43C22-CA09-4ef9-A61E-7A0C1DAAE21E}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_add_directory =
|
||||||
|
{ 0x2dc43c22, 0xca09, 0x4ef9, { 0xa6, 0x1e, 0x7a, 0xc, 0x1d, 0xaa, 0xe2, 0x1e } };
|
||||||
|
|
||||||
|
// {FC89C278-4475-4853-96C9-F7F05E8CC837}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_add_files =
|
||||||
|
{ 0xfc89c278, 0x4475, 0x4853, { 0x96, 0xc9, 0xf7, 0xf0, 0x5e, 0x8c, 0xc8, 0x37 } };
|
||||||
|
|
||||||
|
// {9CA39D38-AC9B-4cca-B0CE-C0F62D188114}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_add_location =
|
||||||
|
{ 0x9ca39d38, 0xac9b, 0x4cca, { 0xb0, 0xce, 0xc0, 0xf6, 0x2d, 0x18, 0x81, 0x14 } };
|
||||||
|
|
||||||
|
// {73D6E69D-0DC9-4d5c-A7EE-FF4BE3896B08}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_add_playlist =
|
||||||
|
{ 0x73d6e69d, 0xdc9, 0x4d5c, { 0xa7, 0xee, 0xff, 0x4b, 0xe3, 0x89, 0x6b, 0x8 } };
|
||||||
|
|
||||||
|
// {55559142-7AEA-4c20-9B72-1D48E970A274}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_clear_playlist =
|
||||||
|
{ 0x55559142, 0x7aea, 0x4c20, { 0x9b, 0x72, 0x1d, 0x48, 0xe9, 0x70, 0xa2, 0x74 } };
|
||||||
|
|
||||||
|
// {BF72488F-36AC-46b3-A36D-193E60C79BC5}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_create_playlist =
|
||||||
|
{ 0xbf72488f, 0x36ac, 0x46b3, { 0xa3, 0x6d, 0x19, 0x3e, 0x60, 0xc7, 0x9b, 0xc5 } };
|
||||||
|
|
||||||
|
// {59E99BEE-42A3-4526-B06D-56C0C49F0BC1}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_highlight_playing =
|
||||||
|
{ 0x59e99bee, 0x42a3, 0x4526, { 0xb0, 0x6d, 0x56, 0xc0, 0xc4, 0x9f, 0xb, 0xc1 } };
|
||||||
|
|
||||||
|
// {D94393D4-9DBB-4e5c-BE8C-BE9CA80E214D}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_load_playlist =
|
||||||
|
{ 0xd94393d4, 0x9dbb, 0x4e5c, { 0xbe, 0x8c, 0xbe, 0x9c, 0xa8, 0xe, 0x21, 0x4d } };
|
||||||
|
|
||||||
|
// {EE1308C5-EBD2-48f1-959D-2627069C2E0F}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_next_playlist =
|
||||||
|
{ 0xee1308c5, 0xebd2, 0x48f1, { 0x95, 0x9d, 0x26, 0x27, 0x6, 0x9c, 0x2e, 0xf } };
|
||||||
|
|
||||||
|
// {486ECDA3-7BA2-49e9-BB44-4DB9DF9320C7}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_previous_playlist =
|
||||||
|
{ 0x486ecda3, 0x7ba2, 0x49e9, { 0xbb, 0x44, 0x4d, 0xb9, 0xdf, 0x93, 0x20, 0xc7 } };
|
||||||
|
|
||||||
|
// {69C778AA-B836-40a0-89CD-7A2E50C102CB}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_open =
|
||||||
|
{ 0x69c778aa, 0xb836, 0x40a0, { 0x89, 0xcd, 0x7a, 0x2e, 0x50, 0xc1, 0x2, 0xcb } };
|
||||||
|
|
||||||
|
// {EB7FB5A4-5904-4d2c-B66C-D882A3B15277}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_remove_playlist =
|
||||||
|
{ 0xeb7fb5a4, 0x5904, 0x4d2c, { 0xb6, 0x6c, 0xd8, 0x82, 0xa3, 0xb1, 0x52, 0x77 } };
|
||||||
|
|
||||||
|
// {C297BADB-8098-45a9-A5E8-B53A0D780CE3}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_remove_dead_entries =
|
||||||
|
{ 0xc297badb, 0x8098, 0x45a9, { 0xa5, 0xe8, 0xb5, 0x3a, 0xd, 0x78, 0xc, 0xe3 } };
|
||||||
|
|
||||||
|
// {D08C2921-7750-4979-98F9-3A513A31FF96}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_remove_duplicates =
|
||||||
|
{ 0xd08c2921, 0x7750, 0x4979, { 0x98, 0xf9, 0x3a, 0x51, 0x3a, 0x31, 0xff, 0x96 } };
|
||||||
|
|
||||||
|
// {D3A25E47-BA98-4e6b-95AD-A7502919EB75}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_rename_playlist =
|
||||||
|
{ 0xd3a25e47, 0xba98, 0x4e6b, { 0x95, 0xad, 0xa7, 0x50, 0x29, 0x19, 0xeb, 0x75 } };
|
||||||
|
|
||||||
|
// {0FDCFC65-9B39-445a-AA88-4D245F217480}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_save_all_playlists =
|
||||||
|
{ 0xfdcfc65, 0x9b39, 0x445a, { 0xaa, 0x88, 0x4d, 0x24, 0x5f, 0x21, 0x74, 0x80 } };
|
||||||
|
|
||||||
|
// {370B720B-4CF7-465b-908C-2D2ADD027900}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_save_playlist =
|
||||||
|
{ 0x370b720b, 0x4cf7, 0x465b, { 0x90, 0x8c, 0x2d, 0x2a, 0xdd, 0x2, 0x79, 0x0 } };
|
||||||
|
|
||||||
|
// {A6CFC2A8-56B3-4d12-88E7-0237961AC47E}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_playlist_search =
|
||||||
|
{ 0xa6cfc2a8, 0x56b3, 0x4d12, { 0x88, 0xe7, 0x2, 0x37, 0x96, 0x1a, 0xc4, 0x7e } };
|
||||||
|
|
||||||
|
// {383D4E8D-7E30-4fb8-B5DD-8C975D89E58E}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_playlist_sel_crop =
|
||||||
|
{ 0x383d4e8d, 0x7e30, 0x4fb8, { 0xb5, 0xdd, 0x8c, 0x97, 0x5d, 0x89, 0xe5, 0x8e } };
|
||||||
|
|
||||||
|
// {E0EEA319-E282-4e6c-8B82-4DFD42A1D4ED}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_playlist_sel_remove =
|
||||||
|
{ 0xe0eea319, 0xe282, 0x4e6c, { 0x8b, 0x82, 0x4d, 0xfd, 0x42, 0xa1, 0xd4, 0xed } };
|
||||||
|
|
||||||
|
// {F0845920-7F6D-40ac-B2EB-3D00C2C8260B}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_playlist_sel_invert =
|
||||||
|
{ 0xf0845920, 0x7f6d, 0x40ac, { 0xb2, 0xeb, 0x3d, 0x0, 0xc2, 0xc8, 0x26, 0xb } };
|
||||||
|
|
||||||
|
// {29910B33-79E9-40da-992B-5A4AA4281F78}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_playlist_undo =
|
||||||
|
{ 0x29910b33, 0x79e9, 0x40da, { 0x99, 0x2b, 0x5a, 0x4a, 0xa4, 0x28, 0x1f, 0x78 } };
|
||||||
|
|
||||||
|
// {02D89A8A-5F7D-41c3-A215-6731D8621036}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_show_console =
|
||||||
|
{ 0x2d89a8a, 0x5f7d, 0x41c3, { 0xa2, 0x15, 0x67, 0x31, 0xd8, 0x62, 0x10, 0x36 } };
|
||||||
|
|
||||||
|
// {E6970E91-33BE-4288-AC01-4B02F07B5D38}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_play_cd =
|
||||||
|
{ 0xe6970e91, 0x33be, 0x4288, { 0xac, 0x1, 0x4b, 0x2, 0xf0, 0x7b, 0x5d, 0x38 } };
|
||||||
|
|
||||||
|
// {1073AB1D-38ED-4957-8B06-38BC878C1F40}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_restart_resetconfig =
|
||||||
|
{ 0x1073ab1d, 0x38ed, 0x4957, { 0x8b, 0x6, 0x38, 0xbc, 0x87, 0x8c, 0x1f, 0x40 } };
|
||||||
|
|
||||||
|
// {FDC8A1C2-93EF-4415-8C20-60B6517F0B5F}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_record =
|
||||||
|
{ 0xfdc8a1c2, 0x93ef, 0x4415, { 0x8c, 0x20, 0x60, 0xb6, 0x51, 0x7f, 0xb, 0x5f } };
|
||||||
|
|
||||||
|
// {45EB37D2-3CD9-4f0a-9B20-8EAE649D7A9F}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_playlist_moveback =
|
||||||
|
{ 0x45eb37d2, 0x3cd9, 0x4f0a, { 0x9b, 0x20, 0x8e, 0xae, 0x64, 0x9d, 0x7a, 0x9f } };
|
||||||
|
|
||||||
|
// {02298938-596A-41f7-A398-19766A06E6EB}
|
||||||
|
FOOGUIDDECL const GUID standard_commands::guid_main_playlist_moveforward =
|
||||||
|
{ 0x2298938, 0x596a, 0x41f7, { 0xa3, 0x98, 0x19, 0x76, 0x6a, 0x6, 0xe6, 0xeb } };
|
||||||
|
|
||||||
|
// {DE5A47E0-28BE-4c49-BEEA-F0DE65C489A4}
|
||||||
|
FOOGUIDDECL const GUID playlist_loader_v2::class_guid=
|
||||||
|
{ 0xde5a47e0, 0x28be, 0x4c49, { 0xbe, 0xea, 0xf0, 0xde, 0x65, 0xc4, 0x89, 0xa4 } };
|
|
@ -0,0 +1,39 @@
|
||||||
|
#ifndef _INITQUIT_H_
|
||||||
|
#define _INITQUIT_H_
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
|
||||||
|
//init/quit callback, on_init is called after main window has been created, on_quit is called before main window is destroyed
|
||||||
|
class NOVTABLE initquit : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void on_init() {}
|
||||||
|
virtual void on_quit() {}
|
||||||
|
virtual void on_system_shutdown() {}//called instead of on_quit() when system is shutting down
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class initquit_factory : public service_factory_single_t<initquit,T> {};
|
||||||
|
|
||||||
|
class initquit_autoreg : public service_impl_single_t<initquit>
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
service_factory_single_ref_t<initquit,initquit_autoreg> * p_factory;
|
||||||
|
public:
|
||||||
|
initquit_autoreg() {p_factory = new service_factory_single_ref_t<initquit,initquit_autoreg>(*this);}
|
||||||
|
~initquit_autoreg() {delete p_factory;}
|
||||||
|
};
|
||||||
|
|
||||||
|
class initquit_simple : public initquit_autoreg
|
||||||
|
{
|
||||||
|
void (*func)(bool is_init);
|
||||||
|
virtual void on_init() {func(true);}
|
||||||
|
virtual void on_quit() {func(false);}
|
||||||
|
public:
|
||||||
|
initquit_simple(void (*p_func)(bool is_init)) {func=p_func;}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,497 @@
|
||||||
|
#include "foobar2000.h"
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
input* input::g_open(reader * r,file_info * info,unsigned flags,__int64 * modtime,__int64 * filesize,bool * new_info)
|
||||||
|
{
|
||||||
|
TRACK_CALL_TEXT("input::g_open");
|
||||||
|
|
||||||
|
service_enum_t<input> e;
|
||||||
|
input * i;
|
||||||
|
const char * filename = info->get_file_path();
|
||||||
|
|
||||||
|
if (r)//check mime types first
|
||||||
|
{
|
||||||
|
string8 content_type;
|
||||||
|
if (r->get_content_type(content_type))
|
||||||
|
{
|
||||||
|
for(i=e.first();i;i = e.next())
|
||||||
|
{
|
||||||
|
if (i->needs_reader() && i->is_our_content_type(filename,content_type))
|
||||||
|
{
|
||||||
|
if (i->open_ex(r,info,flags,modtime,filesize,new_info)) return i;
|
||||||
|
if (!r->seek(0)) {i->service_release(); return 0;}
|
||||||
|
}
|
||||||
|
i->service_release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string_extension_8 extension(filename);
|
||||||
|
for(i=e.first();i;i = e.next())
|
||||||
|
{
|
||||||
|
if (!!r == !!i->needs_reader())
|
||||||
|
{
|
||||||
|
if (i->test_filename(filename,extension))
|
||||||
|
{
|
||||||
|
if (i->open_ex(r,info,flags,modtime,filesize,new_info)) return i;
|
||||||
|
if (r)
|
||||||
|
{
|
||||||
|
if (!r->seek(0)) {i->service_release();return 0;}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i->service_release();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool g_get_info_internal(unsigned flags,file_info * info,reader * r,__int64 * modtime,__int64 * filesize,bool * b_new_info)
|
||||||
|
{
|
||||||
|
//return 0 on failure, 1 on success
|
||||||
|
bool my_reader = false;
|
||||||
|
const char * filename = info->get_file_path();
|
||||||
|
string_extension_8 extension(filename);
|
||||||
|
bool rv = false;
|
||||||
|
|
||||||
|
if (r==0) //try readerless inputs first
|
||||||
|
{
|
||||||
|
service_enum_t<input> e;
|
||||||
|
input * i;
|
||||||
|
for(i=e.first();i;i = e.next())
|
||||||
|
{
|
||||||
|
rv = false;
|
||||||
|
if (!i->needs_reader())
|
||||||
|
{
|
||||||
|
if (i->test_filename(filename,extension))
|
||||||
|
rv = i->open_ex(0,info,flags,modtime,filesize,b_new_info);
|
||||||
|
}
|
||||||
|
i->service_release();
|
||||||
|
if (rv) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = file::g_open(info->get_file_path(),reader::MODE_READ);
|
||||||
|
if (r==0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
my_reader = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
service_enum_t<input> e;
|
||||||
|
input * i;
|
||||||
|
|
||||||
|
{//check content type
|
||||||
|
string8 content_type;
|
||||||
|
if (r->get_content_type(content_type))
|
||||||
|
{
|
||||||
|
for(i=e.first();i;i = e.next())
|
||||||
|
{
|
||||||
|
rv = false;
|
||||||
|
if (i->needs_reader() && i->is_our_content_type(info->get_file_path(),content_type))
|
||||||
|
{
|
||||||
|
if (!r->seek(0))
|
||||||
|
{
|
||||||
|
i->service_release();
|
||||||
|
if (my_reader) r->reader_release();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
rv = i->open_ex(r,info,flags,modtime,filesize,b_new_info);
|
||||||
|
}
|
||||||
|
i->service_release();
|
||||||
|
if (rv) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!rv)//if no luck with content type, proceed with classic open method
|
||||||
|
{
|
||||||
|
for(i=e.first();i;i = e.next())
|
||||||
|
{
|
||||||
|
rv = false;
|
||||||
|
if (i->needs_reader())
|
||||||
|
{
|
||||||
|
if (!r->seek(0))
|
||||||
|
{
|
||||||
|
i->service_release();
|
||||||
|
if (my_reader) r->reader_release();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (i->test_filename(filename,extension))
|
||||||
|
rv= i->open_ex(r,info,flags,modtime,filesize,b_new_info);
|
||||||
|
}
|
||||||
|
i->service_release();
|
||||||
|
if (rv) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (my_reader) r->reader_release();
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool input::g_check_info(file_info * info,reader * r,__int64 * modtime,__int64 * filesize)
|
||||||
|
{
|
||||||
|
if (modtime==0) return false;
|
||||||
|
__int64 time = *modtime;
|
||||||
|
__int64 size = -1;
|
||||||
|
bool new_info = false;
|
||||||
|
if (!g_get_info_internal(0,info,r,&time,&size,&new_info)) return false;
|
||||||
|
if (new_info)
|
||||||
|
{
|
||||||
|
*modtime = time;
|
||||||
|
*filesize = size;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool input::g_get_info(file_info * info,reader * r,__int64 * modtime,__int64 * filesize)
|
||||||
|
{
|
||||||
|
return g_get_info_internal(OPEN_FLAG_GET_INFO,info,r,modtime,filesize,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool input::g_test_filename(const char * filename)
|
||||||
|
{
|
||||||
|
service_enum_t<input> e;
|
||||||
|
input * i;
|
||||||
|
string_extension_8 extension(filename);
|
||||||
|
for(i=e.first();i;i = e.next())
|
||||||
|
{
|
||||||
|
bool rv = false;
|
||||||
|
rv = i->test_filename(filename,extension);
|
||||||
|
i->service_release();
|
||||||
|
if (rv) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int input_pcm::run(audio_chunk * chunk)
|
||||||
|
{
|
||||||
|
int bps,srate,nch;
|
||||||
|
void * source;
|
||||||
|
int size;
|
||||||
|
int rv = get_samples_pcm(&source,&size,&srate,&bps,&nch);
|
||||||
|
if (rv>0) chunk->set_data_fixedpoint(source,size,srate,nch,bps);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
input::set_info_t input::g_set_info_readerless(const file_info * info,__int64 * modtime,__int64 * filesize)
|
||||||
|
{
|
||||||
|
set_info_t rv = SET_INFO_FAILURE;
|
||||||
|
const char * filename = info->get_file_path();
|
||||||
|
string_extension_8 extension(filename);
|
||||||
|
service_enum_t<input> e;
|
||||||
|
input * i;
|
||||||
|
for(i=e.first();i;i = e.next())
|
||||||
|
{
|
||||||
|
rv = SET_INFO_FAILURE;
|
||||||
|
if (!i->needs_reader())
|
||||||
|
{
|
||||||
|
if (i->test_filename(filename,extension))
|
||||||
|
rv = i->set_info_ex(0,info,modtime,filesize);
|
||||||
|
}
|
||||||
|
i->service_release();
|
||||||
|
if (rv != SET_INFO_FAILURE) return rv;
|
||||||
|
}
|
||||||
|
return SET_INFO_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
input::set_info_t input::g_set_info_reader(const file_info * info,reader * r,__int64 * modtime,__int64 * filesize)
|
||||||
|
{
|
||||||
|
const char * filename = info->get_file_path();
|
||||||
|
string_extension_8 extension(filename);
|
||||||
|
set_info_t rv = SET_INFO_FAILURE;
|
||||||
|
|
||||||
|
service_enum_t<input> e;
|
||||||
|
input * i;
|
||||||
|
bool got_busy = false;
|
||||||
|
for(i=e.first();i;i = e.next())
|
||||||
|
{
|
||||||
|
rv = SET_INFO_FAILURE;
|
||||||
|
if (i->needs_reader())
|
||||||
|
{
|
||||||
|
r->seek(0);
|
||||||
|
if (i->test_filename(filename,extension))
|
||||||
|
rv = i->set_info_ex(r,info,modtime,filesize);
|
||||||
|
}
|
||||||
|
i->service_release();
|
||||||
|
if (rv==SET_INFO_SUCCESS) break;
|
||||||
|
else if (rv==SET_INFO_BUSY) got_busy = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rv == SET_INFO_SUCCESS ? SET_INFO_SUCCESS : got_busy ? SET_INFO_BUSY : SET_INFO_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
input::set_info_t input::g_set_info(const file_info * info,reader * r,__int64 * modtime,__int64 * filesize)
|
||||||
|
{
|
||||||
|
bool my_reader = false;
|
||||||
|
set_info_t rv = SET_INFO_FAILURE;
|
||||||
|
|
||||||
|
if (r==0)
|
||||||
|
{
|
||||||
|
rv = g_set_info_readerless(info,modtime,filesize);
|
||||||
|
if (rv != SET_INFO_FAILURE) return rv;
|
||||||
|
|
||||||
|
r = file::g_open(info->get_file_path(),reader::MODE_WRITE_EXISTING);
|
||||||
|
if (r==0)
|
||||||
|
{
|
||||||
|
int flags = file::g_exists(info->get_file_path());
|
||||||
|
if (flags & file::FILE_EXISTS_WRITEABLE) return SET_INFO_BUSY;
|
||||||
|
else return SET_INFO_FAILURE;
|
||||||
|
}
|
||||||
|
my_reader = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
rv = g_set_info_reader(info,r,modtime,filesize);
|
||||||
|
|
||||||
|
if (my_reader) r->reader_release();
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool input::is_entry_dead(const playable_location * entry)
|
||||||
|
{
|
||||||
|
const char * path = entry->get_path();
|
||||||
|
if (file::g_dont_read_infos(path)) return false;
|
||||||
|
int exists = file::g_exists(path);
|
||||||
|
if (exists)
|
||||||
|
{
|
||||||
|
return !g_test_filename(path);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//special fix to keep bloody flac tag updating hack from breaking things
|
||||||
|
//allow non-existent files ONLY when some readerless input accepts them and no reader-based inputs accept them
|
||||||
|
string_extension_8 extension(path);
|
||||||
|
service_enum_t<input> e;
|
||||||
|
input * i;
|
||||||
|
bool found_reader = false, found_readerless = false;
|
||||||
|
for(i=e.first();i;i = e.next())
|
||||||
|
{
|
||||||
|
if (i->test_filename(path,extension))
|
||||||
|
{
|
||||||
|
if (i->needs_reader())
|
||||||
|
found_reader = true;
|
||||||
|
else
|
||||||
|
found_readerless = true;
|
||||||
|
}
|
||||||
|
i->service_release();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ! ( found_readerless && !found_reader );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool input_test_filename_helper::test_filename(const char * filename)
|
||||||
|
{
|
||||||
|
if (!inited)
|
||||||
|
{
|
||||||
|
service_enum_t<input> e;
|
||||||
|
input * i;
|
||||||
|
for(i=e.first();i;i = e.next())
|
||||||
|
inputs.add_item(i);
|
||||||
|
inited=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
string_extension_8 extension(filename);
|
||||||
|
|
||||||
|
int n,m=inputs.get_count();
|
||||||
|
for(n=0;n<m;n++)
|
||||||
|
{
|
||||||
|
if (inputs[n]->test_filename(filename,extension)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
input_test_filename_helper::~input_test_filename_helper()
|
||||||
|
{
|
||||||
|
unsigned n;
|
||||||
|
for(n=0;n<inputs.get_count();n++)
|
||||||
|
inputs[n]->service_release();
|
||||||
|
inputs.remove_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
void input_file_type::build_openfile_mask(string_base & out, bool b_include_playlists)
|
||||||
|
{
|
||||||
|
string8_fastalloc name,mask,mask_alltypes,out_temp;
|
||||||
|
|
||||||
|
if (b_include_playlists)
|
||||||
|
{
|
||||||
|
playlist_loader * ptr;
|
||||||
|
service_enum_t<playlist_loader> e;
|
||||||
|
for(ptr=e.first();ptr;ptr=e.next())
|
||||||
|
{
|
||||||
|
if (!mask.is_empty()) mask += ";";
|
||||||
|
mask += "*.";
|
||||||
|
mask += ptr->get_extension();
|
||||||
|
}
|
||||||
|
out_temp += "Playlists";
|
||||||
|
out_temp += "|";
|
||||||
|
out_temp += mask;
|
||||||
|
out_temp += "|";
|
||||||
|
|
||||||
|
if (!mask_alltypes.is_empty())
|
||||||
|
{
|
||||||
|
if (mask_alltypes[mask_alltypes.length()-1]!=';')
|
||||||
|
mask_alltypes += ";";
|
||||||
|
}
|
||||||
|
mask_alltypes += mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
input_file_type * ptr;
|
||||||
|
service_enum_t<input_file_type> e;
|
||||||
|
for(ptr=e.first();ptr;ptr=e.next())
|
||||||
|
{
|
||||||
|
unsigned n,m = ptr->get_count();
|
||||||
|
for(n=0;n<m;n++)
|
||||||
|
{
|
||||||
|
name.reset();
|
||||||
|
mask.reset();
|
||||||
|
if (ptr->get_name(n,name) && ptr->get_mask(n,mask))
|
||||||
|
{
|
||||||
|
if (!strchr(name,'|') && !strchr(mask,'|'))
|
||||||
|
{
|
||||||
|
out_temp += name;
|
||||||
|
out_temp += "|";
|
||||||
|
out_temp += mask;
|
||||||
|
out_temp += "|";
|
||||||
|
if (!mask_alltypes.is_empty())
|
||||||
|
{
|
||||||
|
if (mask_alltypes[mask_alltypes.length()-1]!=';')
|
||||||
|
mask_alltypes += ";";
|
||||||
|
}
|
||||||
|
mask_alltypes += mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ptr->service_release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.reset();
|
||||||
|
out += "All files|*.*|";
|
||||||
|
if (!mask_alltypes.is_empty())
|
||||||
|
{
|
||||||
|
out += "All supported types|";
|
||||||
|
out += mask_alltypes;
|
||||||
|
out += "|";
|
||||||
|
}
|
||||||
|
out += out_temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool open_ex_internal(input * i,reader * r,file_info * info,unsigned flags,__int64 * modtime,__int64 * filesize,bool * b_new_info)
|
||||||
|
{
|
||||||
|
if (!!r != !!i->needs_reader()) return false;
|
||||||
|
if (r)
|
||||||
|
{
|
||||||
|
__int64 newmodtime;
|
||||||
|
__int64 newfilesize;
|
||||||
|
if (modtime)
|
||||||
|
{
|
||||||
|
newmodtime = r->get_modification_time();
|
||||||
|
if (newmodtime != *modtime) flags |= input::OPEN_FLAG_GET_INFO;
|
||||||
|
}
|
||||||
|
if (filesize)
|
||||||
|
{
|
||||||
|
newfilesize = r->get_length();
|
||||||
|
if (newfilesize != *filesize) flags |= input::OPEN_FLAG_GET_INFO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(flags&(input::OPEN_FLAG_GET_INFO|input::OPEN_FLAG_DECODE))) return true;
|
||||||
|
|
||||||
|
if (!i->open(r,info,flags)) return false;
|
||||||
|
|
||||||
|
if (b_new_info && (flags & input::OPEN_FLAG_GET_INFO)) *b_new_info = true;
|
||||||
|
|
||||||
|
if (filesize) *filesize = r->get_length();
|
||||||
|
if (modtime) *modtime = newmodtime;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!i->open(0,info,flags)) return false;
|
||||||
|
|
||||||
|
if (b_new_info && (flags & input::OPEN_FLAG_GET_INFO)) *b_new_info = true;
|
||||||
|
|
||||||
|
if (filesize) *filesize = -1;
|
||||||
|
if (modtime) *modtime = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool input::open_ex(reader * r,file_info * info,unsigned flags,__int64 * modtime,__int64 * filesize,bool * b_new_info)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
input_v2 * this_v2 = service_query_t(input_v2,this);
|
||||||
|
if (this_v2)
|
||||||
|
{
|
||||||
|
bool rv = this_v2->open_ex(r,info,flags,modtime,filesize,b_new_info);
|
||||||
|
this_v2->service_release();
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return open_ex_internal(this,r,info,flags,modtime,filesize,b_new_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool input_v2::open_ex(reader * r,file_info * info,unsigned flags,__int64 * modtime,__int64 * filesize,bool * b_new_info)
|
||||||
|
{//to be overridden if needed (possibly needed for weird readerless inputs), this implementation is the default behavior
|
||||||
|
return open_ex_internal(this,r,info,flags,modtime,filesize,b_new_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
static input::set_info_t set_info_ex_internal(input * i,reader *r,const file_info * info,__int64 * modtime,__int64 * filesize)
|
||||||
|
{
|
||||||
|
input::set_info_t rv = i->set_info(r,info);
|
||||||
|
if (r)
|
||||||
|
{
|
||||||
|
if (modtime) *modtime = r->get_modification_time();
|
||||||
|
if (filesize) *filesize = r->get_length();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (modtime) *modtime = 0;
|
||||||
|
if (filesize) *filesize = -1;
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
input::set_info_t input::set_info_ex(reader *r,const file_info * info,__int64 * modtime,__int64 * filesize)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
input_v2 * this_v2 = service_query_t(input_v2,this);
|
||||||
|
if (this_v2)
|
||||||
|
{
|
||||||
|
set_info_t rv = this_v2->set_info_ex(r,info,modtime,filesize);
|
||||||
|
this_v2->service_release();
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return set_info_ex_internal(this,r,info,modtime,filesize);
|
||||||
|
}
|
||||||
|
|
||||||
|
input::set_info_t input_v2::set_info_ex(reader *r,const file_info * info,__int64 * modtime,__int64 * filesize)
|
||||||
|
{
|
||||||
|
return set_info_ex_internal(this,r,info,modtime,filesize);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool input::is_reopen_safe()
|
||||||
|
{
|
||||||
|
bool rv = false;
|
||||||
|
input_v2 * this_v2 = service_query_t(input_v2,this);
|
||||||
|
if (this_v2)
|
||||||
|
{
|
||||||
|
rv = this_v2->is_reopen_safe();
|
||||||
|
this_v2->service_release();
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
|
@ -0,0 +1,186 @@
|
||||||
|
#ifndef _INPUT_H_
|
||||||
|
#define _INPUT_H_
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
|
||||||
|
#include "reader.h"
|
||||||
|
#include "file_info.h"
|
||||||
|
|
||||||
|
#include "audio_chunk.h"
|
||||||
|
|
||||||
|
/***************************
|
||||||
|
how input class is used
|
||||||
|
|
||||||
|
case 0: checking filename
|
||||||
|
create => test_filename() => destroy
|
||||||
|
|
||||||
|
case 1: info reading
|
||||||
|
create => test_filename() => open(r,info,OPEN_FLAG_GET_INFO) => destroy
|
||||||
|
|
||||||
|
case 2: playback
|
||||||
|
create => testfilename() => open(r,info,OPEN_FLAG_DECODE | [blah blah blah]) => run() .... => destroy
|
||||||
|
note: you must fill file_info stuff too if you get OPEN_FLAG_GET_INFO
|
||||||
|
|
||||||
|
case 3: info writing
|
||||||
|
create => testfilename() => set_info(r,info) => destroy
|
||||||
|
|
||||||
|
|
||||||
|
there's no possibility of opening different files from the same input instance.
|
||||||
|
|
||||||
|
when you get set_info() call, you have exclusive write access to the file; if user attempts to update currently played file, fb2k will wait until the file is free
|
||||||
|
|
||||||
|
NOTE: when trying to decode unseekable source (reader->can_seek() returns 0), you should expect the reader you get to be able to seek back up to 64k
|
||||||
|
|
||||||
|
|
||||||
|
REMINDER: ALL strings are UTF-8
|
||||||
|
|
||||||
|
|
||||||
|
***************************/
|
||||||
|
|
||||||
|
class NOVTABLE input : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
OPEN_FLAG_GET_INFO = 1,//if specified, you must pass all metadata/etc to file_info, otherwise you dont need to pass any data to file_info (but it wont blow up if you do, it will be just ignored)
|
||||||
|
OPEN_FLAG_DECODE = 2,//open for decoding
|
||||||
|
OPEN_FLAG_NO_SEEKING = 4,//when combined with OPEN_FLAG_DECODE, informs you that you don't need to be able to seek (still need to extract track length though)
|
||||||
|
OPEN_FLAG_NO_LOOPING = 8,//when combined with OPEN_FLAG_DECODE, you're being replaygainscanned or something, if your input can decode indefinitely, disable that
|
||||||
|
};
|
||||||
|
|
||||||
|
//you should expect either OPEN_FLAG_GET_INFO alone, or OPEN_FLAG_DECODE (possibly with modifiers), or both OPEN_FLAG_GET_INFO and OPEN_FLAG_DECODE (extract info and start decoding)
|
||||||
|
|
||||||
|
enum set_info_t
|
||||||
|
{
|
||||||
|
SET_INFO_SUCCESS,
|
||||||
|
SET_INFO_FAILURE,
|
||||||
|
SET_INFO_BUSY,
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual bool open(reader * r,file_info * info,unsigned flags)=0;
|
||||||
|
//caller is responsible for deleting the reader; reader pointer is valid until you get deleted; pass all your metadata/etc to info
|
||||||
|
//flags => see OPEN_FLAG_* above
|
||||||
|
//lazy solution: ignore flags, fill info and set up decoder
|
||||||
|
//somewhat more efficient solution: avoid either setting up decoder or filling info depending on flags
|
||||||
|
|
||||||
|
virtual bool is_our_content_type(const char * url,const char * type) {return 0;}//for mime type checks, before test_filename
|
||||||
|
virtual bool test_filename(const char * full_path,const char * extension)=0; //perform extension/filename tests, return 1 if the file might be one of our types; do ONLY file extension checks etc, no opening; doesn't hurt if you return 1 for a file that doesn't actually belong to you
|
||||||
|
|
||||||
|
|
||||||
|
virtual set_info_t set_info(reader *r,const file_info * info)=0;//reader with exclusive write access
|
||||||
|
|
||||||
|
virtual bool needs_reader() {return true;}//return if you read files or not (if you return 0, reader pointer in open/set_info will be null
|
||||||
|
virtual int run(audio_chunk * chunk)=0;
|
||||||
|
// return 1 on success, -1 on failure, 0 on EOF
|
||||||
|
|
||||||
|
virtual bool seek(double seconds)=0;//return 1 on success, 0 on failure; if EOF, return 1 and return 0 in next run() pass
|
||||||
|
|
||||||
|
virtual bool can_seek() {return true;}//return 0 if the file you're playing can't be seeked
|
||||||
|
|
||||||
|
virtual void abort() {} //async call, abort current seek()/run(), ensure multithread safety on your side
|
||||||
|
|
||||||
|
virtual bool get_dynamic_info(file_info * out, double * timestamp_delta,bool * b_track_change) {return false;}
|
||||||
|
//for dynamic song titles / VBR bitrate / etc
|
||||||
|
//out initially contains currently displayed info (either last get_dynamic_info result or current database info)
|
||||||
|
//timestamp_delta - you can use it to tell the core when this info should be displayed (in seconds, relative to first sample of last decoded chunk), initially set to 0
|
||||||
|
//get_dynamic_info is called after each run() (or not called at all if caller doesn't care about dynamic info)
|
||||||
|
//return false to keep old info, or true to modify it
|
||||||
|
//set b_track_change to true if you want to indicate new song
|
||||||
|
//please keep in mind that updating dynamic info (returning true from this func) generates some overhead (mallocing to pass new info around, play_callback calls, GUI updates and titleformatting), ; if you implement useless features like realtime vbr bitrate display, make sure that they are optional and disabled by default
|
||||||
|
|
||||||
|
static input* g_open(reader * r,file_info * info,unsigned flags,__int64 * modtime = 0,__int64 * filesize=0,bool * new_info=0);
|
||||||
|
static bool g_test_filename(const char * fn);
|
||||||
|
static bool g_get_info(file_info * info,reader * r = 0,__int64 * modtime = 0,__int64 * filesize=0);
|
||||||
|
static bool g_check_info(file_info * info,reader * r = 0,__int64 * modtime = 0,__int64 * filesize=0);//loads info if newer
|
||||||
|
static set_info_t g_set_info_readerless(const file_info * info,__int64 * modtime = 0,__int64 * filesize=0);
|
||||||
|
static set_info_t g_set_info_reader(const file_info * info,reader * r, __int64 * modtime = 0,__int64 * filesize=0);
|
||||||
|
static set_info_t g_set_info(const file_info * info,reader * r = 0,__int64 * modtime = 0,__int64 * filesize=0);
|
||||||
|
static bool is_entry_dead(const playable_location * entry);//tests if playlist_entry appears to be alive or not
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
|
||||||
|
virtual service_base * service_query(const GUID & guid)
|
||||||
|
{
|
||||||
|
if (guid == get_class_guid()) {service_add_ref();return this;}
|
||||||
|
else return service_base::service_query(guid);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool open_ex(reader * r,file_info * info,unsigned flags,__int64 * modification_time,__int64 * file_size,bool * new_info);
|
||||||
|
set_info_t set_info_ex(reader *r,const file_info * info,__int64 * modtime,__int64 * filesize);//reader with exclusive write access
|
||||||
|
bool is_reopen_safe();
|
||||||
|
};
|
||||||
|
|
||||||
|
class input_v2 : public input//extension for readerless operations in 0.8, to allow modification_time processing
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
|
||||||
|
virtual service_base * service_query(const GUID & guid)
|
||||||
|
{
|
||||||
|
if (guid == get_class_guid()) {service_add_ref();return this;}
|
||||||
|
else return input::service_query(guid);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//time is a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601; 0 for invalid/unknown time
|
||||||
|
virtual bool open_ex(reader * r,file_info * info,unsigned flags,__int64 * modification_time,__int64 * file_size,bool * new_info);
|
||||||
|
//if modification_time pointer is null, open as usual
|
||||||
|
//otherwise, if file time is different than *modification_time, set *modification_time to modification time and proceed as if flags had OPEN_FLAG_GET_INFO
|
||||||
|
//if new_info pointer isnt null, set *new_info to true/false depending if you used OPEN_FLAG_GET_INFO
|
||||||
|
|
||||||
|
virtual set_info_t set_info_ex(reader *r,const file_info * info,__int64 * modtime,__int64 * filesize);//reader with exclusive write access
|
||||||
|
|
||||||
|
virtual bool get_album_art(const playable_location * src,reader * in,reader * out) {return false;};//reserved
|
||||||
|
|
||||||
|
virtual bool is_reopen_safe() {return false;}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class input_factory : public service_factory_t<input,T> {};
|
||||||
|
|
||||||
|
|
||||||
|
class input_file_type : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual unsigned get_count()=0;
|
||||||
|
virtual bool get_name(unsigned idx,string_base & out)=0;//e.g. "MPEG file"
|
||||||
|
virtual bool get_mask(unsigned idx,string_base & out)=0;//e.g. "*.MP3;*.MP2"; separate with semicolons
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
|
||||||
|
virtual service_base * service_query(const GUID & guid)
|
||||||
|
{
|
||||||
|
if (guid == get_class_guid()) {service_add_ref();return this;}
|
||||||
|
else return service_base::service_query(guid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void build_openfile_mask(string_base & out,bool b_include_playlists=true);
|
||||||
|
};
|
||||||
|
|
||||||
|
class input_file_type_i : public service_impl_single_t<input_file_type>
|
||||||
|
{
|
||||||
|
const char * name, * mask;
|
||||||
|
public:
|
||||||
|
virtual unsigned get_count() {return 1;}
|
||||||
|
input_file_type_i(const char * p_name, const char * p_mask) : name(p_name), mask(p_mask) {}
|
||||||
|
virtual bool get_name(unsigned idx,string_base & out) {if (idx==0) {out = name; return true;} else return false;}
|
||||||
|
virtual bool get_mask(unsigned idx,string_base & out) {if (idx==0) {out = mask; return true;} else return false;}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class input_file_type_factory : public service_factory_t<input_file_type,T> {};
|
||||||
|
|
||||||
|
#define DECLARE_FILE_TYPE(NAME,MASK) \
|
||||||
|
namespace { static input_file_type_i g_filetype_instance(NAME,MASK); \
|
||||||
|
static service_factory_single_ref_t<input_file_type,input_file_type_i> g_filetype_service(g_filetype_instance); }
|
||||||
|
|
||||||
|
|
||||||
|
//USAGE: DECLARE_FILE_TYPE("Blah file","*.blah;*.bleh");
|
||||||
|
|
||||||
|
#include "input_helpers.h"
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,348 @@
|
||||||
|
#include "foobar2000.h"
|
||||||
|
|
||||||
|
input_helper::input_helper()
|
||||||
|
{
|
||||||
|
seek_to = -1;
|
||||||
|
full_buffer_limit = 0;
|
||||||
|
p_input=0;
|
||||||
|
p_reader=0;
|
||||||
|
reader_overridden = false;
|
||||||
|
b_no_seek = false;
|
||||||
|
b_no_loop = false;
|
||||||
|
b_want_info = true;
|
||||||
|
b_aborting = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
input_helper::~input_helper()
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool input_helper::open_internal(file_info * info,reader * p_reader_override,__int64 *modification_time,__int64 * file_size,bool * new_info)
|
||||||
|
{
|
||||||
|
TRACK_CALL_TEXT("input_helper::open_internal");
|
||||||
|
|
||||||
|
info->meta_remove_all();
|
||||||
|
info->info_remove_all();
|
||||||
|
|
||||||
|
|
||||||
|
if (attempt_reopen_internal(info,p_reader_override,modification_time,file_size,new_info))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * path = info->get_file_path();//file_info always has canonic path
|
||||||
|
|
||||||
|
sync.enter();
|
||||||
|
|
||||||
|
close_input_internal();
|
||||||
|
|
||||||
|
sync.leave();
|
||||||
|
|
||||||
|
|
||||||
|
if (p_reader_override)
|
||||||
|
{
|
||||||
|
sync.enter();
|
||||||
|
close_reader_internal();
|
||||||
|
override_reader(p_reader_override);
|
||||||
|
sync.leave();
|
||||||
|
}
|
||||||
|
else if (p_reader && !stricmp_utf8(my_location.get_path(),path) && p_reader->can_seek())
|
||||||
|
{
|
||||||
|
p_reader->seek(0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sync.enter();
|
||||||
|
close_reader_internal();
|
||||||
|
sync.leave();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_reader == 0)
|
||||||
|
{
|
||||||
|
input * p_input_temp = input::g_open(0,info,get_open_flags(),modification_time,file_size,new_info);
|
||||||
|
if (p_input_temp)
|
||||||
|
{
|
||||||
|
sync.enter();
|
||||||
|
p_input = p_input_temp;
|
||||||
|
sync.leave();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
sync.enter();
|
||||||
|
|
||||||
|
p_reader = file::g_get_reader(path);
|
||||||
|
|
||||||
|
sync.leave();
|
||||||
|
|
||||||
|
if (p_reader==0) return false;
|
||||||
|
|
||||||
|
if (p_reader->open(path,reader::MODE_READ)==0) {close();return false;}
|
||||||
|
|
||||||
|
setup_fullbuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
input * temp = input::g_open(p_reader,info,get_open_flags(),modification_time,file_size,new_info);
|
||||||
|
if (temp)
|
||||||
|
{
|
||||||
|
sync.enter();
|
||||||
|
p_input = temp;
|
||||||
|
sync.leave();
|
||||||
|
}
|
||||||
|
else {close();return false;}
|
||||||
|
}
|
||||||
|
|
||||||
|
my_location.copy(info->get_location());
|
||||||
|
|
||||||
|
seek_to = -1;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool input_helper::open(file_info * info,reader * p_reader_override)
|
||||||
|
{
|
||||||
|
b_want_info = true;
|
||||||
|
return open_internal(info,p_reader_override,0,0,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool input_helper::open_noinfo(file_info * info, reader * p_reader_override)
|
||||||
|
{
|
||||||
|
b_want_info = false;
|
||||||
|
return open_internal(info,p_reader_override,0,0,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool input_helper::open(const playable_location * src,reader * p_reader_override)
|
||||||
|
{
|
||||||
|
b_want_info = false;
|
||||||
|
file_info_i_full info(src);
|
||||||
|
return open_internal(&info,p_reader_override,0,0,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool input_helper::open(metadb_handle * p_handle, reader * p_reader_override)
|
||||||
|
{
|
||||||
|
bool l_want_info = !p_handle->handle_query(0,true);
|
||||||
|
|
||||||
|
__int64 time = p_handle->handle_get_modification_time();
|
||||||
|
__int64 newtime = time;
|
||||||
|
__int64 file_size = p_handle->handle_get_file_size();
|
||||||
|
bool b_new_info = false;
|
||||||
|
|
||||||
|
b_want_info = l_want_info;
|
||||||
|
file_info_i_full info(p_handle->handle_get_location());
|
||||||
|
bool rv = open_internal(&info,p_reader_override,&newtime,&file_size,&b_new_info);
|
||||||
|
if (rv)
|
||||||
|
{
|
||||||
|
if (b_new_info) p_handle->handle_hint_ex(&info,newtime,file_size,true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (input::is_entry_dead(p_handle->handle_get_location()))
|
||||||
|
p_handle->handle_entry_dead();
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
void input_helper::close_input_internal()
|
||||||
|
{
|
||||||
|
if (p_input)
|
||||||
|
{
|
||||||
|
p_input->service_release();
|
||||||
|
p_input = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void input_helper::close_reader_internal()
|
||||||
|
{
|
||||||
|
if (p_reader)
|
||||||
|
{
|
||||||
|
if (!reader_overridden) p_reader->service_release();
|
||||||
|
p_reader=0;
|
||||||
|
reader_overridden = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void input_helper::close()
|
||||||
|
{
|
||||||
|
TRACK_CALL_TEXT("input_helper::close");
|
||||||
|
insync(sync);
|
||||||
|
close_input_internal();
|
||||||
|
close_reader_internal();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool input_helper::is_open() {return !!p_input;}
|
||||||
|
|
||||||
|
int input_helper::run(audio_chunk * chunk)//fucko: syncing this will break abort
|
||||||
|
{
|
||||||
|
chunk->reset();
|
||||||
|
|
||||||
|
if (p_input==0) return -1;
|
||||||
|
|
||||||
|
if (seek_to>=0)
|
||||||
|
{
|
||||||
|
double target = seek_to;
|
||||||
|
seek_to = -1;
|
||||||
|
|
||||||
|
{
|
||||||
|
TRACK_CALL_TEXT("input::seek");
|
||||||
|
if (!p_input->seek(target)) return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
{
|
||||||
|
TRACK_CALL_TEXT("input::seek::run");
|
||||||
|
rv = p_input->run(chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rv>0 && !chunk->is_valid())
|
||||||
|
{
|
||||||
|
console::error("input::run() produced invalid chunk:");
|
||||||
|
console::info_location(&my_location);
|
||||||
|
chunk->reset();
|
||||||
|
rv = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool input_helper::seek(double seconds)
|
||||||
|
{
|
||||||
|
insync(sync);
|
||||||
|
if (!p_input || b_no_seek) return false;
|
||||||
|
seek_to = seconds;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool input_helper::can_seek()
|
||||||
|
{
|
||||||
|
insync(sync);
|
||||||
|
return p_input ? p_input->can_seek() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void input_helper::abort()
|
||||||
|
{
|
||||||
|
TRACK_CALL_TEXT("input_helper::abort");
|
||||||
|
insync(sync);
|
||||||
|
b_aborting = true;
|
||||||
|
if (p_reader) p_reader->abort();
|
||||||
|
if (p_input) p_input->abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
void input_helper::on_idle()
|
||||||
|
{
|
||||||
|
insync(sync);
|
||||||
|
if (p_reader) p_reader->on_idle();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool input_helper::get_dynamic_info(file_info * out,double * timestamp_delta, bool * b_track_change)
|
||||||
|
{
|
||||||
|
TRACK_CALL_TEXT("input_helper::get_dynamic_info");
|
||||||
|
insync(sync);
|
||||||
|
return p_input ? p_input->get_dynamic_info(out,timestamp_delta,b_track_change) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool input_helper::attempt_reopen_internal(file_info * info,reader * p_reader_override,__int64 * modification_time,__int64* file_size,bool * new_info)
|
||||||
|
{
|
||||||
|
TRACK_CALL_TEXT("input_helper::attempt_reopen_internal");
|
||||||
|
assert(sync.get_lock_count_check()==0);
|
||||||
|
const char * path = info->get_file_path();
|
||||||
|
|
||||||
|
bool b_reopen_available,b_same_file;
|
||||||
|
sync.enter();
|
||||||
|
b_same_file = p_input && !p_reader_override && my_location.compare(info->get_location())==0 && p_input->can_seek();
|
||||||
|
if (!b_same_file)
|
||||||
|
b_reopen_available = !(!p_input || (p_reader_override && !p_reader) || !p_input->is_reopen_safe() || !p_input->test_filename(path,string_extension(path)));
|
||||||
|
sync.leave();
|
||||||
|
if (b_same_file && !b_want_info)
|
||||||
|
{
|
||||||
|
return p_input->seek(0);
|
||||||
|
}
|
||||||
|
else if (!b_reopen_available) return false;
|
||||||
|
|
||||||
|
if (p_reader_override)
|
||||||
|
{//caller wants us to get their reader
|
||||||
|
sync.enter();
|
||||||
|
close_reader_internal();
|
||||||
|
override_reader(p_reader_override);
|
||||||
|
sync.leave();
|
||||||
|
}
|
||||||
|
else if (p_reader && !stricmp_utf8(my_location.get_path(),path) && p_reader->can_seek())
|
||||||
|
{//same file
|
||||||
|
p_reader->seek(0);
|
||||||
|
}
|
||||||
|
else if (p_reader)
|
||||||
|
{//different file
|
||||||
|
sync.enter();
|
||||||
|
close_reader_internal();
|
||||||
|
p_reader = file::g_get_reader(path);
|
||||||
|
sync.leave();
|
||||||
|
if (p_reader==0) return false;
|
||||||
|
if (p_reader->open(path,reader::MODE_READ) == 0)
|
||||||
|
{
|
||||||
|
sync.enter();
|
||||||
|
close_reader_internal();
|
||||||
|
sync.leave();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
setup_fullbuffer();
|
||||||
|
}
|
||||||
|
//else must be readerless input
|
||||||
|
|
||||||
|
if (p_input->open_ex(p_reader,info,get_open_flags(),modification_time,file_size,new_info))
|
||||||
|
{
|
||||||
|
my_location.copy(info->get_location());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void input_helper::override_reader(reader* p_reader_override)
|
||||||
|
{
|
||||||
|
assert(sync.get_lock_count_check()>0);
|
||||||
|
assert(p_reader == 0);
|
||||||
|
p_reader = p_reader_override;
|
||||||
|
reader_overridden = !!p_reader_override;
|
||||||
|
}
|
||||||
|
|
||||||
|
void input_helper::setup_fullbuffer()
|
||||||
|
{
|
||||||
|
assert(p_reader);
|
||||||
|
assert(sync.get_lock_count_check()==0);
|
||||||
|
if (full_buffer_limit>0)
|
||||||
|
{
|
||||||
|
__int64 size = p_reader->get_length();
|
||||||
|
if (size>0 && size<=full_buffer_limit)
|
||||||
|
{
|
||||||
|
reader * temp = reader_membuffer::create(p_reader,p_reader->get_modification_time());
|
||||||
|
if (temp)
|
||||||
|
{
|
||||||
|
sync.enter();
|
||||||
|
p_reader->service_release();
|
||||||
|
p_reader = temp;
|
||||||
|
sync.leave();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned input_helper::get_open_flags()
|
||||||
|
{
|
||||||
|
return input::OPEN_FLAG_DECODE | (b_want_info ? input::OPEN_FLAG_GET_INFO : 0) | (b_no_seek ? input::OPEN_FLAG_NO_SEEKING : 0) | (b_no_loop ? input::OPEN_FLAG_NO_LOOPING : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void input_helper::override(const playable_location * new_location,input * new_input,reader * new_reader)
|
||||||
|
{
|
||||||
|
insync(sync);
|
||||||
|
close();
|
||||||
|
if (new_location) my_location.copy(new_location);
|
||||||
|
else my_location.copy(make_playable_location("UNKNOWN",0));
|
||||||
|
p_input = new_input;
|
||||||
|
p_reader = new_reader;
|
||||||
|
seek_to = -1;
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
#ifndef _INPUT_HELPERS_H_
|
||||||
|
#define _INPUT_HELPERS_H_
|
||||||
|
|
||||||
|
#include "input.h"
|
||||||
|
|
||||||
|
class NOVTABLE input_pcm : public input
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual int run(audio_chunk * chunk);
|
||||||
|
protected:
|
||||||
|
virtual int get_samples_pcm(void ** buffer,int * size,int * srate,int * bps,int * nch)=0;//same return value as input::run()
|
||||||
|
};
|
||||||
|
|
||||||
|
class input_helper//takes care of opening inputs etc
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
critical_section sync;
|
||||||
|
playable_location_i my_location;
|
||||||
|
input * p_input;
|
||||||
|
reader * p_reader;
|
||||||
|
__int64 full_buffer_limit;
|
||||||
|
bool b_aborting;
|
||||||
|
bool reader_overridden;
|
||||||
|
bool b_no_seek,b_no_loop,b_want_info;
|
||||||
|
double seek_to;
|
||||||
|
|
||||||
|
bool open_file_internal(const char * path,reader * p_reader_override);
|
||||||
|
bool attempt_reopen_internal(file_info * info,reader * p_reader_override,__int64 * modtime,__int64* filesize,bool * new_info);
|
||||||
|
bool open_internal(file_info * info,reader * p_reader_override,__int64 * modtime,__int64* filesize,bool * new_info);
|
||||||
|
void close_input_internal();
|
||||||
|
void close_reader_internal();
|
||||||
|
void override_reader(reader*);
|
||||||
|
void setup_fullbuffer();
|
||||||
|
unsigned get_open_flags();
|
||||||
|
public:
|
||||||
|
input_helper();
|
||||||
|
~input_helper();
|
||||||
|
bool open(file_info * info,reader * p_reader_override = 0);
|
||||||
|
bool open_noinfo(file_info * info, reader * p_reader_override=0);
|
||||||
|
bool open(const playable_location * src,reader * p_reader_override = 0);//helper
|
||||||
|
inline bool open(const char * path,reader * p_reader_override = 0)
|
||||||
|
{return open(make_playable_location(path,0),p_reader_override);}
|
||||||
|
|
||||||
|
bool open(class metadb_handle * p_handle, reader * p_reader_override=0);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void override(const playable_location * new_location,input * new_input,reader * new_reader);
|
||||||
|
|
||||||
|
void close();
|
||||||
|
bool is_open();
|
||||||
|
int run(audio_chunk * chunk);
|
||||||
|
bool seek(double seconds);
|
||||||
|
bool can_seek();
|
||||||
|
void abort();
|
||||||
|
void set_full_buffer(__int64 val) {full_buffer_limit = val;}//default 0 to disable, any positive value to set upper file size limit for full file buffering
|
||||||
|
void on_idle();
|
||||||
|
bool get_dynamic_info(file_info * out,double *timestamp_delta,bool *b_track_change);
|
||||||
|
|
||||||
|
//call these before opening
|
||||||
|
inline void hint_no_seeking(bool state) {b_no_seek = state;}
|
||||||
|
inline void hint_no_looping(bool state) {b_no_loop = state;}
|
||||||
|
|
||||||
|
bool open_errorhandled(class metadb_handle * p_handle, reader * p_reader_override=0);
|
||||||
|
int run_errorhandled(audio_chunk * chunk);
|
||||||
|
bool seek_errorhandled(double seconds);
|
||||||
|
void close_errorhandled();
|
||||||
|
};
|
||||||
|
|
||||||
|
class input_test_filename_helper//avoids bloody bottleneck with creating inputs when mass-testing filenames
|
||||||
|
{
|
||||||
|
ptr_list_t<input> inputs;
|
||||||
|
bool inited;
|
||||||
|
public:
|
||||||
|
inline input_test_filename_helper() {inited=false;}
|
||||||
|
|
||||||
|
bool test_filename(const char * filename);
|
||||||
|
~input_test_filename_helper();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef _PTR_LIST_INTERFACE_H_
|
||||||
|
#define _PTR_LIST_INTERFACE_H_
|
||||||
|
|
||||||
|
#include "../../pfc/pfc.h"
|
||||||
|
|
||||||
|
|
||||||
|
//DEPRECATED (wheee)
|
||||||
|
|
||||||
|
#define make_string_interface(blah) blah;
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,178 @@
|
||||||
|
#ifndef _FOOBAR2000_MENU_ITEM_H_
|
||||||
|
#define _FOOBAR2000_MENU_ITEM_H_
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
#include "interface_helper.h"
|
||||||
|
#include "metadb_handle.h"
|
||||||
|
|
||||||
|
class NOVTABLE menu_item : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum type
|
||||||
|
{
|
||||||
|
TYPE_MAIN,
|
||||||
|
TYPE_CONTEXT,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum enabled_state
|
||||||
|
{
|
||||||
|
DEFAULT_OFF,
|
||||||
|
DEFAULT_ON,
|
||||||
|
FORCE_ON,
|
||||||
|
FORCE_OFF,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
FLAG_CHECKED = 1,
|
||||||
|
FLAG_DISABLED = 2,
|
||||||
|
FLAG_GRAYED = 4,
|
||||||
|
FLAG_DISABLED_GRAYED = FLAG_DISABLED|FLAG_GRAYED,
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual enabled_state get_enabled_state(unsigned idx) {return DEFAULT_ON;}//return if menu item should be enabled by default or not
|
||||||
|
|
||||||
|
virtual type get_type()=0;
|
||||||
|
virtual unsigned get_num_items()=0;
|
||||||
|
virtual void enum_item(unsigned n,string_base & out)=0;//return full formal name, like "playback/play", 0<=n<get_num_items(), no error return
|
||||||
|
|
||||||
|
virtual bool get_display_data(unsigned n,const ptr_list_base<metadb_handle> & data,string_base & out,unsigned & displayflags)=0;//displayflags - see FLAG_* above; displayflags are set to 0 before calling
|
||||||
|
virtual void perform_command(unsigned n,const ptr_list_base<metadb_handle> & data)=0;
|
||||||
|
|
||||||
|
virtual bool get_description(unsigned n,string_base & out) {return false;}
|
||||||
|
|
||||||
|
virtual void * get_glyph(unsigned n) {return 0;}//RESERVED, do not override
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
|
||||||
|
virtual service_base * service_query(const GUID & guid)
|
||||||
|
{
|
||||||
|
if (guid == get_class_guid()) {service_add_ref();return this;}
|
||||||
|
else return service_base::service_query(guid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const GUID caller_now_playing;
|
||||||
|
static const GUID caller_playlist;
|
||||||
|
static const GUID caller_undefined;
|
||||||
|
|
||||||
|
bool get_display_data(unsigned n,const ptr_list_base<metadb_handle> & data,string_base & out,unsigned & displayflags,const GUID & caller);
|
||||||
|
void perform_command(unsigned n,const ptr_list_base<metadb_handle> & data,const GUID & caller);
|
||||||
|
bool enum_item_guid(unsigned n,GUID & out);
|
||||||
|
};
|
||||||
|
|
||||||
|
class NOVTABLE menu_item_v2 : public menu_item
|
||||||
|
{
|
||||||
|
virtual bool get_display_data(unsigned n,const ptr_list_base<metadb_handle> & data,string_base & out,unsigned & displayflags)
|
||||||
|
{return get_display_data(n,data,out,displayflags,caller_undefined);}
|
||||||
|
virtual void perform_command(unsigned n,const ptr_list_base<metadb_handle> & data)
|
||||||
|
{perform_command(n,data,caller_undefined);}
|
||||||
|
public:
|
||||||
|
virtual bool get_display_data(unsigned n,const ptr_list_base<metadb_handle> & data,string_base & out,unsigned & displayflags,const GUID & caller)=0;
|
||||||
|
virtual void perform_command(unsigned n,const ptr_list_base<metadb_handle> & data,const GUID & caller)=0;
|
||||||
|
virtual bool enum_item_guid(unsigned n,GUID & out) {return false;}//returns false if item doesnt have a GUID
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
|
||||||
|
virtual service_base * service_query(const GUID & guid)
|
||||||
|
{
|
||||||
|
if (guid == get_class_guid()) {service_add_ref();return this;}
|
||||||
|
else return menu_item::service_query(guid);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class NOVTABLE menu_item_main : public menu_item_v2
|
||||||
|
{
|
||||||
|
virtual type get_type() {return TYPE_MAIN;}
|
||||||
|
|
||||||
|
virtual bool get_display_data(unsigned n,const ptr_list_base<metadb_handle> & data,string_base & out,unsigned & displayflags,const GUID & caller)
|
||||||
|
{
|
||||||
|
assert(n>=0 && n<get_num_items());
|
||||||
|
if (!is_available(n)) return false;
|
||||||
|
string8 temp;
|
||||||
|
enum_item(n,temp);
|
||||||
|
const char * ptr = strrchr(temp,'/');
|
||||||
|
if (ptr) ptr++;
|
||||||
|
else ptr = temp;
|
||||||
|
out.set_string(ptr);
|
||||||
|
displayflags = (is_checked(n) ? FLAG_CHECKED : 0) | (is_disabled(n) ? (FLAG_DISABLED|FLAG_GRAYED) : 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void perform_command(unsigned n,const ptr_list_base<metadb_handle> & data,const GUID & caller) {perform_command(n);}
|
||||||
|
protected:
|
||||||
|
//override these
|
||||||
|
virtual unsigned get_num_items()=0;
|
||||||
|
virtual void enum_item(unsigned n,string_base & out)=0;
|
||||||
|
virtual void perform_command(unsigned n)=0;
|
||||||
|
virtual bool is_checked(unsigned n) {return false;}
|
||||||
|
virtual bool is_disabled(unsigned n) {return false;}
|
||||||
|
virtual bool is_available(unsigned n) {return true;}
|
||||||
|
virtual bool get_description(unsigned n,string_base & out) {return false;}
|
||||||
|
};
|
||||||
|
|
||||||
|
class NOVTABLE menu_item_main_single : public menu_item_main //helper
|
||||||
|
{
|
||||||
|
virtual unsigned get_num_items() {return 1;}
|
||||||
|
virtual void enum_item(unsigned n,string_base & out)
|
||||||
|
{
|
||||||
|
if (n==0) get_name(out);
|
||||||
|
}
|
||||||
|
virtual void perform_command(unsigned n)
|
||||||
|
{
|
||||||
|
if (n==0) run();
|
||||||
|
}
|
||||||
|
virtual bool is_checked(unsigned n) {assert(n==0); return is_checked();};
|
||||||
|
virtual bool is_disabled(unsigned n) {assert(n==0); return is_disabled();}
|
||||||
|
virtual bool get_description(unsigned n,string_base & out) {assert(n==0); return get_description(out);}
|
||||||
|
protected://override these
|
||||||
|
virtual void get_name(string_base & out)=0;
|
||||||
|
virtual void run()=0;
|
||||||
|
virtual bool is_checked() {return false;}
|
||||||
|
virtual bool is_disabled() {return false;}
|
||||||
|
virtual bool get_description(string_base & out) {return false;}
|
||||||
|
};
|
||||||
|
|
||||||
|
class NOVTABLE menu_item_context : public menu_item_v2
|
||||||
|
{
|
||||||
|
virtual type get_type() {return TYPE_CONTEXT;}
|
||||||
|
virtual bool get_display_data(unsigned n,const ptr_list_base<metadb_handle> & data,string_base & out,unsigned & displayflags,const GUID & caller)
|
||||||
|
{
|
||||||
|
bool rv = false;
|
||||||
|
assert(n>=0 && n<get_num_items());
|
||||||
|
if (data.get_count()>0)
|
||||||
|
{
|
||||||
|
rv = context_get_display(n,data,out,displayflags,caller);
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
virtual void perform_command(unsigned n,const ptr_list_base<metadb_handle> & data,const GUID & caller)
|
||||||
|
{
|
||||||
|
if (data.get_count()>0) context_command(n,data,caller);
|
||||||
|
}
|
||||||
|
virtual bool is_checked(unsigned n,const ptr_list_base<metadb_handle> & data) {return false;}
|
||||||
|
protected:
|
||||||
|
//override these
|
||||||
|
virtual unsigned get_num_items()=0;
|
||||||
|
virtual void enum_item(unsigned n,string_base & out)=0;
|
||||||
|
virtual void context_command(unsigned n,const ptr_list_base<metadb_handle> & data,const GUID& caller)=0;
|
||||||
|
virtual bool context_get_display(unsigned n,const ptr_list_base<metadb_handle> & data,string_base & out,unsigned & displayflags,const GUID &)
|
||||||
|
{
|
||||||
|
assert(n>=0 && n<get_num_items());
|
||||||
|
string8 temp;
|
||||||
|
enum_item(n,temp);
|
||||||
|
const char * ptr = strrchr(temp,'/');
|
||||||
|
if (ptr) ptr++;
|
||||||
|
else ptr = temp;
|
||||||
|
out.set_string(ptr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class menu_item_factory : public service_factory_single_t<menu_item,T> {};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //_FOOBAR2000_MENU_ITEM_H_
|
|
@ -0,0 +1,632 @@
|
||||||
|
#include "foobar2000.h"
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
|
||||||
|
static void fix_ampersand(const char * src,string_base & out)
|
||||||
|
{
|
||||||
|
unsigned ptr = 0;
|
||||||
|
while(src[ptr])
|
||||||
|
{
|
||||||
|
if (src[ptr]=='&')
|
||||||
|
{
|
||||||
|
out.add_string("&&");
|
||||||
|
ptr++;
|
||||||
|
while(src[ptr]=='&')
|
||||||
|
{
|
||||||
|
out.add_string("&&");
|
||||||
|
ptr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else out.add_byte(src[ptr++]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned flags_to_win32(unsigned flags)
|
||||||
|
{
|
||||||
|
unsigned ret = 0;
|
||||||
|
if (flags & menu_item::FLAG_CHECKED) ret |= MF_CHECKED;
|
||||||
|
if (flags & menu_item::FLAG_DISABLED) ret |= MF_DISABLED;
|
||||||
|
if (flags & menu_item::FLAG_GRAYED) ret |= MF_GRAYED;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void menu_manager::win32_build_menu(HMENU menu,menu_node * parent,int base_id,int max_id)//menu item identifiers are base_id<=N<base_id+max_id (if theres too many items, they will be clipped)
|
||||||
|
{
|
||||||
|
if (parent!=0 && parent->get_type()==menu_node::TYPE_POPUP)
|
||||||
|
{
|
||||||
|
string8_fastalloc temp;
|
||||||
|
int child_idx,child_num = parent->get_num_children();
|
||||||
|
for(child_idx=0;child_idx<child_num;child_idx++)
|
||||||
|
{
|
||||||
|
menu_node * child = parent->get_child(child_idx);
|
||||||
|
if (child)
|
||||||
|
{
|
||||||
|
const char * name = child->get_name();
|
||||||
|
if (strchr(name,'&')) {fix_ampersand(name,temp);name=temp;}
|
||||||
|
menu_node::type type = child->get_type();
|
||||||
|
if (type==menu_node::TYPE_POPUP)
|
||||||
|
{
|
||||||
|
HMENU new_menu = CreatePopupMenu();
|
||||||
|
uAppendMenu(menu,MF_STRING|MF_POPUP | flags_to_win32(child->get_display_flags()),(UINT)new_menu,name);
|
||||||
|
win32_build_menu(new_menu,child,base_id,max_id);
|
||||||
|
}
|
||||||
|
else if (type==menu_node::TYPE_SEPARATOR)
|
||||||
|
{
|
||||||
|
uAppendMenu(menu,MF_SEPARATOR,0,0);
|
||||||
|
}
|
||||||
|
else if (type==menu_node::TYPE_COMMAND)
|
||||||
|
{
|
||||||
|
int id = child->get_id();
|
||||||
|
if (id>=0 && (max_id<0 || id<max_id))
|
||||||
|
{
|
||||||
|
uAppendMenu(menu,MF_STRING | flags_to_win32(child->get_display_flags()),base_id+id,name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static bool run_command_internal(menu_item::type type,const char * name,const ptr_list_base<metadb_handle> & data,const GUID & caller)
|
||||||
|
{
|
||||||
|
service_enum_t<menu_item> e;
|
||||||
|
menu_item * ptr;
|
||||||
|
bool done = false;
|
||||||
|
string8_fastalloc action_name;
|
||||||
|
for(ptr=e.first();ptr;ptr=e.next())
|
||||||
|
{
|
||||||
|
if (ptr->get_type()==type)
|
||||||
|
{
|
||||||
|
unsigned action,num_actions = ptr->get_num_items();
|
||||||
|
for(action=0;action<num_actions;action++)
|
||||||
|
{
|
||||||
|
action_name.reset();
|
||||||
|
ptr->enum_item(action,action_name);
|
||||||
|
if (!stricmp_utf8(name,action_name))
|
||||||
|
{
|
||||||
|
TRACK_CALL_TEXT(uStringPrintf("menu_manager::run_command()/\"%s\"",action_name.get_ptr()));
|
||||||
|
ptr->perform_command(action,data,caller);
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ptr->service_release();
|
||||||
|
if (done) break;
|
||||||
|
}
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool run_command_internal(menu_item::type type,const GUID & guid,const ptr_list_base<metadb_handle> & data,const GUID & caller)
|
||||||
|
{
|
||||||
|
service_enum_t<menu_item> e;
|
||||||
|
menu_item * ptr;
|
||||||
|
bool done = false;
|
||||||
|
GUID action_guid;
|
||||||
|
for(ptr=e.first();ptr;ptr=e.next())
|
||||||
|
{
|
||||||
|
if (ptr->get_type()==type)
|
||||||
|
{
|
||||||
|
unsigned action,num_actions = ptr->get_num_items();
|
||||||
|
for(action=0;action<num_actions;action++)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (ptr->enum_item_guid(action,action_guid))
|
||||||
|
{
|
||||||
|
if (action_guid == guid)
|
||||||
|
{
|
||||||
|
TRACK_CALL_TEXT("menu_manager::run_command(), by GUID");
|
||||||
|
ptr->perform_command(action,data,caller);
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ptr->service_release();
|
||||||
|
if (done) break;
|
||||||
|
}
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool menu_manager::run_command(const GUID & guid)
|
||||||
|
{
|
||||||
|
return run_command_internal(menu_item::TYPE_MAIN,guid,ptr_list_t<metadb_handle>(),menu_item::caller_undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool menu_manager::run_command_context(const GUID & guid,const ptr_list_base<metadb_handle> & data)
|
||||||
|
{
|
||||||
|
return run_command_internal(menu_item::TYPE_CONTEXT,guid,data,menu_item::caller_undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool menu_manager::run_command_context_ex(const GUID & guid,const ptr_list_base<metadb_handle> & data,const GUID & caller)
|
||||||
|
{
|
||||||
|
return run_command_internal(menu_item::TYPE_CONTEXT,guid,data,caller);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool menu_manager::run_command(const char * name)
|
||||||
|
{
|
||||||
|
return run_command_internal(menu_item::TYPE_MAIN,name,ptr_list_t<metadb_handle>(),menu_item::caller_undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool menu_manager::run_command_context_ex(const char * name,const ptr_list_base<metadb_handle> & data,const GUID & caller)
|
||||||
|
{
|
||||||
|
return run_command_internal(menu_item::TYPE_CONTEXT,name,data,caller);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool menu_manager::run_command_context(const char * name,const ptr_list_base<metadb_handle> &data)
|
||||||
|
{
|
||||||
|
return run_command_internal(menu_item::TYPE_CONTEXT,name,data,menu_item::caller_undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool menu_manager::run_command_context_playlist(const char * name)
|
||||||
|
{
|
||||||
|
metadb_handle_list temp;
|
||||||
|
playlist_oper::get()->get_sel_items(temp);
|
||||||
|
bool rv = run_command_context_ex(name,temp,menu_item::caller_playlist);
|
||||||
|
temp.delete_all();
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool g_test_command(const char * name,menu_item::type m_type)
|
||||||
|
{
|
||||||
|
service_enum_t<menu_item> e;
|
||||||
|
menu_item * ptr;
|
||||||
|
bool done = false;
|
||||||
|
string8_fastalloc action_name;
|
||||||
|
for(ptr=e.first();ptr;ptr=e.next())
|
||||||
|
{
|
||||||
|
if (ptr->get_type()==m_type)
|
||||||
|
{
|
||||||
|
unsigned action,num_actions = ptr->get_num_items();
|
||||||
|
for(action=0;action<num_actions;action++)
|
||||||
|
{
|
||||||
|
action_name.reset();
|
||||||
|
ptr->enum_item(action,action_name);
|
||||||
|
if (!stricmp_utf8(name,action_name))
|
||||||
|
{
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ptr->service_release();
|
||||||
|
if (done) break;
|
||||||
|
}
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool menu_manager::test_command(const char * name)
|
||||||
|
{
|
||||||
|
return g_test_command(name,menu_item::TYPE_MAIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool menu_manager::test_command_context(const char * name)
|
||||||
|
{
|
||||||
|
return g_test_command(name,menu_item::TYPE_CONTEXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool g_is_checked(const char * name,menu_item::type type, const ptr_list_base<metadb_handle> & data,const GUID & caller)
|
||||||
|
{
|
||||||
|
service_enum_t<menu_item> e;
|
||||||
|
menu_item * ptr;
|
||||||
|
bool done = false, rv = false;
|
||||||
|
string8_fastalloc dummystring,action_name;
|
||||||
|
for(ptr=e.first();ptr;ptr=e.next())
|
||||||
|
{
|
||||||
|
if (ptr->get_type()==type)
|
||||||
|
{
|
||||||
|
unsigned action,num_actions = ptr->get_num_items();
|
||||||
|
for(action=0;action<num_actions;action++)
|
||||||
|
{
|
||||||
|
action_name.reset();
|
||||||
|
ptr->enum_item(action,action_name);
|
||||||
|
if (!stricmp_utf8(name,action_name))
|
||||||
|
{
|
||||||
|
unsigned displayflags = 0;
|
||||||
|
if (ptr->get_display_data(action,data,dummystring,displayflags,caller))
|
||||||
|
{
|
||||||
|
rv = !!(displayflags & menu_item::FLAG_CHECKED);
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ptr->service_release();
|
||||||
|
if (done) break;
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool g_is_checked(const GUID & guid,menu_item::type type, const ptr_list_base<metadb_handle> & data,const GUID & caller)
|
||||||
|
{
|
||||||
|
service_enum_t<menu_item> e;
|
||||||
|
menu_item * ptr;
|
||||||
|
bool done = false, rv = false;
|
||||||
|
string8_fastalloc dummystring;
|
||||||
|
GUID action_guid;
|
||||||
|
for(ptr=e.first();ptr;ptr=e.next())
|
||||||
|
{
|
||||||
|
if (ptr->get_type()==type)
|
||||||
|
{
|
||||||
|
unsigned action,num_actions = ptr->get_num_items();
|
||||||
|
for(action=0;action<num_actions;action++)
|
||||||
|
{
|
||||||
|
if (ptr->enum_item_guid(action,action_guid))
|
||||||
|
{
|
||||||
|
if (action_guid == guid)
|
||||||
|
{
|
||||||
|
unsigned displayflags = 0;
|
||||||
|
if (ptr->get_display_data(action,data,dummystring,displayflags,caller))
|
||||||
|
{
|
||||||
|
rv = !!(displayflags & menu_item::FLAG_CHECKED);
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ptr->service_release();
|
||||||
|
if (done) break;
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool menu_manager::is_command_checked(const GUID & guid)
|
||||||
|
{
|
||||||
|
return g_is_checked(guid,menu_item::TYPE_MAIN,ptr_list_t<metadb_handle>(),menu_item::caller_undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool menu_manager::is_command_checked(const char * name)
|
||||||
|
{
|
||||||
|
return g_is_checked(name,menu_item::TYPE_MAIN,ptr_list_t<metadb_handle>(),menu_item::caller_undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool menu_manager::is_command_checked_context(const GUID & guid,metadb_handle_list & data)
|
||||||
|
{
|
||||||
|
return g_is_checked(guid,menu_item::TYPE_MAIN,data,menu_item::caller_undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool menu_manager::is_command_checked_context(const char * name,metadb_handle_list & data)
|
||||||
|
{
|
||||||
|
return g_is_checked(name,menu_item::TYPE_MAIN,data,menu_item::caller_undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool menu_manager::is_command_checked_context_playlist(const GUID & guid)
|
||||||
|
{
|
||||||
|
bool rv;
|
||||||
|
metadb_handle_list temp;
|
||||||
|
playlist_oper::get()->get_data(temp);
|
||||||
|
rv = g_is_checked(guid,menu_item::TYPE_MAIN,temp,menu_item::caller_playlist);
|
||||||
|
temp.delete_all();
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool menu_manager::is_command_checked_context_playlist(const char * name)
|
||||||
|
{
|
||||||
|
bool rv;
|
||||||
|
metadb_handle_list temp;
|
||||||
|
playlist_oper::get()->get_data(temp);
|
||||||
|
rv = g_is_checked(name,menu_item::TYPE_MAIN,temp,menu_item::caller_playlist);
|
||||||
|
temp.delete_all();
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool menu_manager::execute_by_id(unsigned id)
|
||||||
|
{
|
||||||
|
bool rv = false;
|
||||||
|
menu_node * ptr = find_by_id(id);
|
||||||
|
if (ptr) {rv=true;ptr->execute();}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
|
||||||
|
void menu_manager::win32_run_menu_popup(HWND parent,const POINT * pt)
|
||||||
|
{
|
||||||
|
enum {ID_CUSTOM_BASE = 1};
|
||||||
|
|
||||||
|
int cmd;
|
||||||
|
|
||||||
|
{
|
||||||
|
POINT p;
|
||||||
|
if (pt) p = *pt;
|
||||||
|
else GetCursorPos(&p);
|
||||||
|
HMENU hmenu = CreatePopupMenu();
|
||||||
|
|
||||||
|
v_win32_build_menu(hmenu,ID_CUSTOM_BASE,-1);
|
||||||
|
v_win32_auto_mnemonics(hmenu);
|
||||||
|
|
||||||
|
cmd = TrackPopupMenu(hmenu,TPM_RIGHTBUTTON|TPM_NONOTIFY|TPM_RETURNCMD,p.x,p.y,0,parent,0);
|
||||||
|
|
||||||
|
DestroyMenu(hmenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd>0)
|
||||||
|
{
|
||||||
|
if (cmd>=ID_CUSTOM_BASE)
|
||||||
|
{
|
||||||
|
execute_by_id(cmd - ID_CUSTOM_BASE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void menu_manager::win32_run_menu_context(HWND parent,const metadb_handle_list & data,const POINT * pt,unsigned flags)
|
||||||
|
{
|
||||||
|
menu_manager * p_manager = menu_manager::create();
|
||||||
|
p_manager->init_context(data,flags);
|
||||||
|
p_manager->win32_run_menu_popup(parent,pt);
|
||||||
|
p_manager->service_release();
|
||||||
|
}
|
||||||
|
|
||||||
|
void menu_manager::win32_run_menu_context_playlist(HWND parent,const POINT * pt,unsigned flags)
|
||||||
|
{
|
||||||
|
menu_manager * p_manager = menu_manager::create();
|
||||||
|
p_manager->init_context_playlist(flags);
|
||||||
|
p_manager->win32_run_menu_popup(parent,pt);
|
||||||
|
p_manager->service_release();
|
||||||
|
}
|
||||||
|
|
||||||
|
void menu_manager::win32_run_menu_main(HWND parent,const char * path,const POINT * pt,unsigned flags)
|
||||||
|
{
|
||||||
|
menu_manager * p_manager = menu_manager::create();
|
||||||
|
p_manager->init_main(path,flags);
|
||||||
|
p_manager->win32_run_menu_popup(parent,pt);
|
||||||
|
p_manager->service_release();
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
class mnemonic_manager
|
||||||
|
{
|
||||||
|
string8_fastalloc used;
|
||||||
|
bool is_used(unsigned c)
|
||||||
|
{
|
||||||
|
char temp[8];
|
||||||
|
temp[utf8_encode_char(char_lower(c),temp)]=0;
|
||||||
|
return !!strstr(used,temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_alphanumeric(char c)
|
||||||
|
{
|
||||||
|
return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void insert(const char * src,unsigned idx,string_base & out)
|
||||||
|
{
|
||||||
|
out.reset();
|
||||||
|
out.add_string(src,idx);
|
||||||
|
out.add_string("&");
|
||||||
|
out.add_string(src+idx);
|
||||||
|
used.add_char(char_lower(src[idx]));
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
bool check_string(const char * src)
|
||||||
|
{//check for existing mnemonics
|
||||||
|
const char * ptr = src;
|
||||||
|
while(ptr = strchr(ptr,'&'))
|
||||||
|
{
|
||||||
|
if (ptr[1]=='&') ptr+=2;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unsigned c = 0;
|
||||||
|
if (utf8_decode_char(ptr+1,&c)>0)
|
||||||
|
{
|
||||||
|
if (!is_used(c)) used.add_char(char_lower(c));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool process_string(const char * src,string_base & out)//returns if changed
|
||||||
|
{
|
||||||
|
if (check_string(src)) {out=src;return false;}
|
||||||
|
unsigned idx=0;
|
||||||
|
while(src[idx]==' ') idx++;
|
||||||
|
while(src[idx])
|
||||||
|
{
|
||||||
|
if (is_alphanumeric(src[idx]) && !is_used(src[idx]))
|
||||||
|
{
|
||||||
|
insert(src,idx,out);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(src[idx] && src[idx]!=' ' && src[idx]!='\t') idx++;
|
||||||
|
if (src[idx]=='\t') break;
|
||||||
|
while(src[idx]==' ') idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//no success picking first letter of one of words
|
||||||
|
idx=0;
|
||||||
|
while(src[idx])
|
||||||
|
{
|
||||||
|
if (src[idx]=='\t') break;
|
||||||
|
if (is_alphanumeric(src[idx]) && !is_used(src[idx]))
|
||||||
|
{
|
||||||
|
insert(src,idx,out);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//giving up
|
||||||
|
out = src;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void menu_manager::win32_auto_mnemonics(HMENU menu)
|
||||||
|
{
|
||||||
|
mnemonic_manager mgr;
|
||||||
|
unsigned n, m = GetMenuItemCount(menu);
|
||||||
|
string8_fastalloc temp,temp2;
|
||||||
|
for(n=0;n<m;n++)//first pass, check existing mnemonics
|
||||||
|
{
|
||||||
|
unsigned type = uGetMenuItemType(menu,n);
|
||||||
|
if (type==MFT_STRING)
|
||||||
|
{
|
||||||
|
uGetMenuString(menu,n,temp,MF_BYPOSITION);
|
||||||
|
mgr.check_string(temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(n=0;n<m;n++)
|
||||||
|
{
|
||||||
|
HMENU submenu = GetSubMenu(menu,n);
|
||||||
|
if (submenu) win32_auto_mnemonics(submenu);
|
||||||
|
|
||||||
|
{
|
||||||
|
unsigned type = uGetMenuItemType(menu,n);
|
||||||
|
if (type==MFT_STRING)
|
||||||
|
{
|
||||||
|
unsigned state = submenu ? 0 : GetMenuState(menu,n,MF_BYPOSITION);
|
||||||
|
unsigned id = GetMenuItemID(menu,n);
|
||||||
|
uGetMenuString(menu,n,temp,MF_BYPOSITION);
|
||||||
|
if (mgr.process_string(temp,temp2))
|
||||||
|
{
|
||||||
|
uModifyMenu(menu,n,MF_BYPOSITION|MF_STRING|state,id,temp2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool menu_manager::get_description(menu_item::type type,const char * name,string_base & out)
|
||||||
|
{
|
||||||
|
service_enum_t<menu_item> e;
|
||||||
|
menu_item * ptr;
|
||||||
|
string8_fastalloc action_name;
|
||||||
|
for(ptr=e.first();ptr;ptr=e.next())
|
||||||
|
{
|
||||||
|
if (ptr->get_type()==type)
|
||||||
|
{
|
||||||
|
unsigned action,num_actions = ptr->get_num_items();
|
||||||
|
for(action=0;action<num_actions;action++)
|
||||||
|
{
|
||||||
|
action_name.reset();
|
||||||
|
ptr->enum_item(action,action_name);
|
||||||
|
if (!stricmp_utf8(name,action_name))
|
||||||
|
{
|
||||||
|
bool rv = ptr->get_description(action,out);
|
||||||
|
ptr->service_release();
|
||||||
|
if (!rv) out.reset();
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ptr->service_release();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool test_key(unsigned k)
|
||||||
|
{
|
||||||
|
return (GetKeyState(k) & 0x8000) ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define F_SHIFT (HOTKEYF_SHIFT<<8)
|
||||||
|
#define F_CTRL (HOTKEYF_CONTROL<<8)
|
||||||
|
#define F_ALT (HOTKEYF_ALT<<8)
|
||||||
|
#define F_WIN (HOTKEYF_EXT<<8)
|
||||||
|
|
||||||
|
static unsigned get_key_code(WPARAM wp)
|
||||||
|
{
|
||||||
|
unsigned code = wp & 0xFF;
|
||||||
|
if (test_key(VK_CONTROL)) code|=F_CTRL;
|
||||||
|
if (test_key(VK_SHIFT)) code|=F_SHIFT;
|
||||||
|
if (test_key(VK_MENU)) code|=F_ALT;
|
||||||
|
if (test_key(VK_LWIN) || test_key(VK_RWIN)) code|=F_WIN;
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool keyboard_shortcut_manager::on_keydown(shortcut_type type,WPARAM wp)
|
||||||
|
{
|
||||||
|
if (type==TYPE_CONTEXT) return false;
|
||||||
|
metadb_handle_list dummy;
|
||||||
|
return process_keydown(type,dummy,get_key_code(wp));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool keyboard_shortcut_manager::on_keydown_context(const ptr_list_base<metadb_handle> & data,WPARAM wp,const GUID & caller)
|
||||||
|
{
|
||||||
|
if (data.get_count()==0) return false;
|
||||||
|
return process_keydown_ex(TYPE_CONTEXT,data,get_key_code(wp),caller);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool keyboard_shortcut_manager::on_keydown_auto(WPARAM wp)
|
||||||
|
{
|
||||||
|
if (on_keydown(TYPE_MAIN,wp)) return true;
|
||||||
|
if (on_keydown(TYPE_CONTEXT_PLAYLIST,wp)) return true;
|
||||||
|
if (on_keydown(TYPE_CONTEXT_NOW_PLAYING,wp)) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool keyboard_shortcut_manager::on_keydown_auto_playlist(WPARAM wp)
|
||||||
|
{
|
||||||
|
metadb_handle_list data;
|
||||||
|
bool rv = false;
|
||||||
|
playlist_oper::get()->get_sel_items(data);
|
||||||
|
rv = on_keydown_auto_context(data,wp,menu_item::caller_playlist);
|
||||||
|
data.delete_all();
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool keyboard_shortcut_manager::on_keydown_auto_context(const ptr_list_base<metadb_handle> & data,WPARAM wp,const GUID & caller)
|
||||||
|
{
|
||||||
|
if (on_keydown_context(data,wp,caller)) return true;
|
||||||
|
else return on_keydown_auto(wp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool menu_item::get_display_data(unsigned n,const ptr_list_base<metadb_handle> & data,string_base & out,unsigned & displayflags,const GUID & caller)
|
||||||
|
{
|
||||||
|
bool rv;
|
||||||
|
menu_item_v2 * this_v2 = service_query_t(menu_item_v2,this);
|
||||||
|
if (this_v2)
|
||||||
|
{
|
||||||
|
rv = this_v2->get_display_data(n,data,out,displayflags,caller);
|
||||||
|
this_v2->service_release();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rv = get_display_data(n,data,out,displayflags);
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
void menu_item::perform_command(unsigned n,const ptr_list_base<metadb_handle> & data,const GUID & caller)
|
||||||
|
{
|
||||||
|
menu_item_v2 * this_v2 = service_query_t(menu_item_v2,this);
|
||||||
|
if (this_v2)
|
||||||
|
{
|
||||||
|
this_v2->perform_command(n,data,caller);
|
||||||
|
this_v2->service_release();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
perform_command(n,data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool menu_item::enum_item_guid(unsigned n,GUID & out)
|
||||||
|
{
|
||||||
|
bool rv = false;
|
||||||
|
menu_item_v2 * this_v2 = service_query_t(menu_item_v2,this);
|
||||||
|
if (this_v2)
|
||||||
|
{
|
||||||
|
rv = this_v2->enum_item_guid(n,out);
|
||||||
|
this_v2->service_release();
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
|
@ -0,0 +1,286 @@
|
||||||
|
#ifndef _FOOBAR2000_MENU_MANAGER_H_
|
||||||
|
#define _FOOBAR2000_MENU_MANAGER_H_
|
||||||
|
|
||||||
|
#include "menu_item.h"
|
||||||
|
#include "metadb_handle.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class NOVTABLE keyboard_shortcut_manager : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static keyboard_shortcut_manager * get() {return service_enum_create_t(keyboard_shortcut_manager,0);}
|
||||||
|
|
||||||
|
enum shortcut_type
|
||||||
|
{
|
||||||
|
TYPE_MAIN,
|
||||||
|
TYPE_CONTEXT,
|
||||||
|
TYPE_CONTEXT_PLAYLIST,
|
||||||
|
TYPE_CONTEXT_NOW_PLAYING,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
virtual bool process_keydown(shortcut_type type,const ptr_list_base<metadb_handle> & data,unsigned keycode)=0;
|
||||||
|
virtual bool process_keydown_ex(shortcut_type type,const ptr_list_base<metadb_handle> & data,unsigned keycode,const GUID & caller)=0;
|
||||||
|
//caller guid - see menu_item_v2, menu_item::caller_*
|
||||||
|
bool on_keydown(shortcut_type type,WPARAM wp);
|
||||||
|
bool on_keydown_context(const ptr_list_base<metadb_handle> & data,WPARAM wp,const GUID & caller);
|
||||||
|
bool on_keydown_auto(WPARAM wp);
|
||||||
|
bool on_keydown_auto_playlist(WPARAM wp);
|
||||||
|
bool on_keydown_auto_context(const ptr_list_base<metadb_handle> & data,WPARAM wp,const GUID & caller);
|
||||||
|
|
||||||
|
virtual bool get_key_description_for_action(const char * action_name, string_base & out, shortcut_type type, bool is_global)=0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
|
||||||
|
/*
|
||||||
|
usage:
|
||||||
|
|
||||||
|
in a windowproc:
|
||||||
|
|
||||||
|
case WM_KEYDOWN:
|
||||||
|
keyboard_shortcut_manager::get()->on_keydown(wparam);
|
||||||
|
break;
|
||||||
|
case WM_SYSKEYDOWN:
|
||||||
|
keyboard_shortcut_manager::get()->on_keydown(wparam);
|
||||||
|
break;
|
||||||
|
|
||||||
|
return value is true if key got translated to one of user-configured actions, false if not
|
||||||
|
*/
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class NOVTABLE menu_node
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum type
|
||||||
|
{
|
||||||
|
TYPE_POPUP,TYPE_COMMAND,TYPE_SEPARATOR
|
||||||
|
};
|
||||||
|
virtual type get_type()=0;
|
||||||
|
virtual const char * get_name()=0;
|
||||||
|
virtual unsigned get_num_children()=0;//TYPE_POPUP only
|
||||||
|
virtual menu_node * get_child(unsigned n)=0;//TYPE_POPUP only
|
||||||
|
virtual unsigned get_display_flags()=0;//TYPE_COMMAND/TYPE_POPUP only, see menu_item::FLAG_*
|
||||||
|
virtual unsigned get_id()=0;//TYPE_COMMAND only, returns zero-based index (helpful for win32 menu command ids)
|
||||||
|
virtual void execute()=0;//TYPE_COMMAND only
|
||||||
|
virtual bool get_description(string_base & out)=0;//TYPE_COMMAND only
|
||||||
|
virtual bool get_full_name(string_base & out)=0;//TYPE_COMMAND only
|
||||||
|
virtual void * get_glyph()=0;//RESERVED, do not use
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class NOVTABLE menu_manager : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
FLAG_SHOW_SHORTCUTS = 1,
|
||||||
|
FLAG_SHOW_SHORTCUTS_GLOBAL = 2,
|
||||||
|
};
|
||||||
|
virtual void init_context(const ptr_list_base<metadb_handle> & data,unsigned flags)=0;//flags - see FLAG_* above
|
||||||
|
virtual void init_context_playlist(unsigned flags)=0;
|
||||||
|
virtual void init_main(const char * path,unsigned flags)=0;
|
||||||
|
virtual menu_node * get_root()=0;//releasing menu_manager service releaases nodes; root may be null in case of error or something
|
||||||
|
virtual menu_node * find_by_id(unsigned id)=0;
|
||||||
|
virtual void set_shortcut_preference(const keyboard_shortcut_manager::shortcut_type * data,unsigned count)=0;
|
||||||
|
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
|
||||||
|
static menu_manager * create() {return service_enum_create_t(menu_manager,0);}//you must service_release pointers you obtained
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
static void win32_build_menu(HMENU menu,menu_node * parent,int base_id,int max_id);//menu item identifiers are base_id<=N<base_id+max_id (if theres too many items, they will be clipped)
|
||||||
|
static void win32_run_menu_context(HWND parent,const metadb_handle_list & data, const POINT * pt = 0,unsigned flags = 0);
|
||||||
|
static void win32_run_menu_context_playlist(HWND parent,const POINT * pt = 0,unsigned flags = 0);
|
||||||
|
static void win32_run_menu_main(HWND parent,const char * path,const POINT * pt = 0,unsigned flags = 0);
|
||||||
|
void win32_run_menu_popup(HWND parent,const POINT * pt = 0);
|
||||||
|
void win32_build_menu(HMENU menu,int base_id,int max_id) {win32_build_menu(menu,get_root(),base_id,max_id);}
|
||||||
|
static void win32_auto_mnemonics(HMENU menu);
|
||||||
|
//virtual versions, calling them generates smaller code (you use implementation in core instead of statically linking to one from sdk)
|
||||||
|
virtual void v_win32_build_menu(HMENU menu,int base_id,int max_id) {win32_build_menu(menu,base_id,max_id);}
|
||||||
|
virtual void v_run_command(const char * name) {run_command(name);}
|
||||||
|
virtual void v_run_command_context(const char * name,const ptr_list_base<metadb_handle> & list) {run_command_context(name,list);}
|
||||||
|
virtual void v_run_command_context_playlist(const char * name) {run_command_context_playlist(name);}
|
||||||
|
|
||||||
|
virtual void v_win32_auto_mnemonics(HMENU menu) {win32_auto_mnemonics(menu);}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//new (0.8 beta3)
|
||||||
|
virtual void init_context_ex(const ptr_list_base<metadb_handle> & data,unsigned flags,const GUID & caller)=0;
|
||||||
|
virtual bool init_context_now_playing(unsigned flags)=0;//returns false if not playing
|
||||||
|
virtual void init_main_ex(const char * path,unsigned flags,const GUID & caller)=0;
|
||||||
|
|
||||||
|
bool execute_by_id(unsigned id);
|
||||||
|
|
||||||
|
static bool run_command(const GUID & guid);
|
||||||
|
static bool run_command_context(const GUID & guid,const ptr_list_base<metadb_handle> & data);
|
||||||
|
static bool run_command_context_ex(const GUID & guid,const ptr_list_base<metadb_handle> & data,const GUID & caller);
|
||||||
|
|
||||||
|
static bool run_command(const char * name);//returns false if not found
|
||||||
|
static bool run_command_context(const char * name,const ptr_list_base<metadb_handle> & data);
|
||||||
|
static bool run_command_context_playlist(const char * name);
|
||||||
|
static bool run_command_context_ex(const char * name,const ptr_list_base<metadb_handle> & data,const GUID & caller);
|
||||||
|
|
||||||
|
static bool test_command(const char * name);//checks if a command of this name exists
|
||||||
|
static bool test_command_context(const char * name);
|
||||||
|
|
||||||
|
static bool is_command_checked(const char * name);
|
||||||
|
static bool is_command_checked(const GUID & guid);
|
||||||
|
static bool is_command_checked_context(const char * name,metadb_handle_list & data);
|
||||||
|
static bool is_command_checked_context(const GUID & guid,metadb_handle_list & data);
|
||||||
|
static bool is_command_checked_context_playlist(const char * name);
|
||||||
|
static bool is_command_checked_context_playlist(const GUID & guid);
|
||||||
|
|
||||||
|
static bool get_description(menu_item::type type,const char * path,string_base & out);
|
||||||
|
|
||||||
|
virtual service_base * service_query(const GUID & guid)
|
||||||
|
{
|
||||||
|
if (guid == get_class_guid()) {service_add_ref();return this;}
|
||||||
|
else return service_base::service_query(guid);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class NOVTABLE menu_manager_defaults_callback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void set_path(const char * path)=0;
|
||||||
|
virtual void add_command(const char * name)=0;//full name/path of a command, according to menu_item::enum_item, case-sensitive!
|
||||||
|
virtual void add_separator()=0;
|
||||||
|
virtual bool is_command_present(const char * name)=0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NOVTABLE menu_manager_defaults : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual GUID get_guid()=0;
|
||||||
|
virtual menu_item::type get_type()=0;
|
||||||
|
virtual void run(menu_manager_defaults_callback * callback)=0;
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class standard_commands
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const GUID
|
||||||
|
guid_context_file_properties, guid_context_file_open_directory, guid_context_copy_names,
|
||||||
|
guid_context_send_to_playlist, guid_context_reload_info, guid_context_reload_info_if_changed,
|
||||||
|
guid_context_rewrite_info, guid_context_remove_tags, guid_context_remove_from_database,
|
||||||
|
guid_context_convert_run, guid_context_convert_run_singlefile,guid_context_write_cd,
|
||||||
|
guid_context_rg_scan_track, guid_context_rg_scan_album, guid_context_rg_scan_album_multi,
|
||||||
|
guid_context_rg_remove, guid_context_save_playlist, guid_context_masstag_edit,
|
||||||
|
guid_context_masstag_rename,
|
||||||
|
guid_main_always_on_top, guid_main_preferences, guid_main_about,
|
||||||
|
guid_main_exit, guid_main_restart, guid_main_activate,
|
||||||
|
guid_main_hide, guid_main_activate_or_hide, guid_main_titleformat_help,
|
||||||
|
guid_main_follow_cursor, guid_main_next, guid_main_previous,
|
||||||
|
guid_main_next_or_random, guid_main_random, guid_main_pause,
|
||||||
|
guid_main_play, guid_main_play_or_pause, guid_main_rg_set_album,
|
||||||
|
guid_main_rg_set_track, guid_main_rg_disable, guid_main_stop,
|
||||||
|
guid_main_stop_after_current, guid_main_volume_down, guid_main_volume_up,
|
||||||
|
guid_main_volume_mute, guid_main_add_directory, guid_main_add_files,
|
||||||
|
guid_main_add_location, guid_main_add_playlist, guid_main_clear_playlist,
|
||||||
|
guid_main_create_playlist, guid_main_highlight_playing, guid_main_load_playlist,
|
||||||
|
guid_main_next_playlist, guid_main_previous_playlist, guid_main_open,
|
||||||
|
guid_main_remove_playlist, guid_main_remove_dead_entries, guid_main_remove_duplicates,
|
||||||
|
guid_main_rename_playlist, guid_main_save_all_playlists, guid_main_save_playlist,
|
||||||
|
guid_main_playlist_search, guid_main_playlist_sel_crop, guid_main_playlist_sel_remove,
|
||||||
|
guid_main_playlist_sel_invert, guid_main_playlist_undo, guid_main_show_console,
|
||||||
|
guid_main_play_cd, guid_main_restart_resetconfig, guid_main_record,
|
||||||
|
guid_main_playlist_moveback, guid_main_playlist_moveforward
|
||||||
|
;
|
||||||
|
|
||||||
|
static inline bool run_main(const GUID & guid) {return menu_manager::run_command(guid);}
|
||||||
|
static inline bool run_context(const GUID & guid,const ptr_list_base<metadb_handle> &data) {return menu_manager::run_command_context(guid,data);}
|
||||||
|
static inline bool run_context(const GUID & guid,const ptr_list_base<metadb_handle> &data,const GUID& caller) {return menu_manager::run_command_context_ex(guid,data,caller);}
|
||||||
|
|
||||||
|
static inline bool context_file_properties(const ptr_list_base<metadb_handle> &data,const GUID& caller = menu_item::caller_undefined) {return run_context(guid_context_file_properties,data,caller);}
|
||||||
|
static inline bool context_file_open_directory(const ptr_list_base<metadb_handle> &data,const GUID& caller = menu_item::caller_undefined) {return run_context(guid_context_file_open_directory,data,caller);}
|
||||||
|
static inline bool context_copy_names(const ptr_list_base<metadb_handle> &data,const GUID& caller = menu_item::caller_undefined) {return run_context(guid_context_copy_names,data,caller);}
|
||||||
|
static inline bool context_send_to_playlist(const ptr_list_base<metadb_handle> &data,const GUID& caller = menu_item::caller_undefined) {return run_context(guid_context_send_to_playlist,data,caller);}
|
||||||
|
static inline bool context_reload_info(const ptr_list_base<metadb_handle> &data,const GUID& caller = menu_item::caller_undefined) {return run_context(guid_context_reload_info,data,caller);}
|
||||||
|
static inline bool context_reload_info_if_changed(const ptr_list_base<metadb_handle> &data,const GUID& caller = menu_item::caller_undefined) {return run_context(guid_context_reload_info_if_changed,data,caller);}
|
||||||
|
static inline bool context_rewrite_info(const ptr_list_base<metadb_handle> &data,const GUID& caller = menu_item::caller_undefined) {return run_context(guid_context_rewrite_info,data,caller);}
|
||||||
|
static inline bool context_remove_tags(const ptr_list_base<metadb_handle> &data,const GUID& caller = menu_item::caller_undefined) {return run_context(guid_context_remove_tags,data,caller);}
|
||||||
|
static inline bool context_remove_from_database(const ptr_list_base<metadb_handle> &data,const GUID& caller = menu_item::caller_undefined) {return run_context(guid_context_remove_from_database,data,caller);}
|
||||||
|
static inline bool context_convert_run(const ptr_list_base<metadb_handle> &data,const GUID& caller = menu_item::caller_undefined) {return run_context(guid_context_convert_run,data,caller);}
|
||||||
|
static inline bool context_convert_run_singlefile(const ptr_list_base<metadb_handle> &data,const GUID& caller = menu_item::caller_undefined) {return run_context(guid_context_convert_run_singlefile,data,caller);}
|
||||||
|
static inline bool context_write_cd(const ptr_list_base<metadb_handle> &data,const GUID& caller = menu_item::caller_undefined) {return run_context(guid_context_write_cd,data,caller);}
|
||||||
|
static inline bool context_rg_scan_track(const ptr_list_base<metadb_handle> &data,const GUID& caller = menu_item::caller_undefined) {return run_context(guid_context_rg_scan_track,data,caller);}
|
||||||
|
static inline bool context_rg_scan_album(const ptr_list_base<metadb_handle> &data,const GUID& caller = menu_item::caller_undefined) {return run_context(guid_context_rg_scan_album,data,caller);}
|
||||||
|
static inline bool context_rg_scan_album_multi(const ptr_list_base<metadb_handle> &data,const GUID& caller = menu_item::caller_undefined) {return run_context(guid_context_rg_scan_album_multi,data,caller);}
|
||||||
|
static inline bool context_rg_remove(const ptr_list_base<metadb_handle> &data,const GUID& caller = menu_item::caller_undefined) {return run_context(guid_context_rg_remove,data,caller);}
|
||||||
|
static inline bool context_save_playlist(const ptr_list_base<metadb_handle> &data,const GUID& caller = menu_item::caller_undefined) {return run_context(guid_context_save_playlist,data,caller);}
|
||||||
|
static inline bool context_masstag_edit(const ptr_list_base<metadb_handle> &data,const GUID& caller = menu_item::caller_undefined) {return run_context(guid_context_masstag_edit,data,caller);}
|
||||||
|
static inline bool context_masstag_rename(const ptr_list_base<metadb_handle> &data,const GUID& caller = menu_item::caller_undefined) {return run_context(guid_context_masstag_rename,data,caller);}
|
||||||
|
static inline bool main_always_on_top() {return run_main(guid_main_always_on_top);}
|
||||||
|
static inline bool main_preferences() {return run_main(guid_main_preferences);}
|
||||||
|
static inline bool main_about() {return run_main(guid_main_about);}
|
||||||
|
static inline bool main_exit() {return run_main(guid_main_exit);}
|
||||||
|
static inline bool main_restart() {return run_main(guid_main_restart);}
|
||||||
|
static inline bool main_activate() {return run_main(guid_main_activate);}
|
||||||
|
static inline bool main_hide() {return run_main(guid_main_hide);}
|
||||||
|
static inline bool main_activate_or_hide() {return run_main(guid_main_activate_or_hide);}
|
||||||
|
static inline bool main_titleformat_help() {return run_main(guid_main_titleformat_help);}
|
||||||
|
static inline bool main_follow_cursor() {return run_main(guid_main_follow_cursor);}
|
||||||
|
static inline bool main_next() {return run_main(guid_main_next);}
|
||||||
|
static inline bool main_previous() {return run_main(guid_main_previous);}
|
||||||
|
static inline bool main_next_or_random() {return run_main(guid_main_next_or_random);}
|
||||||
|
static inline bool main_random() {return run_main(guid_main_random);}
|
||||||
|
static inline bool main_pause() {return run_main(guid_main_pause);}
|
||||||
|
static inline bool main_play() {return run_main(guid_main_play);}
|
||||||
|
static inline bool main_play_or_pause() {return run_main(guid_main_play_or_pause);}
|
||||||
|
static inline bool main_rg_set_album() {return run_main(guid_main_rg_set_album);}
|
||||||
|
static inline bool main_rg_set_track() {return run_main(guid_main_rg_set_track);}
|
||||||
|
static inline bool main_rg_disable() {return run_main(guid_main_rg_disable);}
|
||||||
|
static inline bool main_stop() {return run_main(guid_main_stop);}
|
||||||
|
static inline bool main_stop_after_current() {return run_main(guid_main_stop_after_current);}
|
||||||
|
static inline bool main_volume_down() {return run_main(guid_main_volume_down);}
|
||||||
|
static inline bool main_volume_up() {return run_main(guid_main_volume_up);}
|
||||||
|
static inline bool main_volume_mute() {return run_main(guid_main_volume_mute);}
|
||||||
|
static inline bool main_add_directory() {return run_main(guid_main_add_directory);}
|
||||||
|
static inline bool main_add_files() {return run_main(guid_main_add_files);}
|
||||||
|
static inline bool main_add_location() {return run_main(guid_main_add_location);}
|
||||||
|
static inline bool main_add_playlist() {return run_main(guid_main_add_playlist);}
|
||||||
|
static inline bool main_clear_playlist() {return run_main(guid_main_clear_playlist);}
|
||||||
|
static inline bool main_create_playlist() {return run_main(guid_main_create_playlist);}
|
||||||
|
static inline bool main_highlight_playing() {return run_main(guid_main_highlight_playing);}
|
||||||
|
static inline bool main_load_playlist() {return run_main(guid_main_load_playlist);}
|
||||||
|
static inline bool main_next_playlist() {return run_main(guid_main_next_playlist);}
|
||||||
|
static inline bool main_previous_playlist() {return run_main(guid_main_previous_playlist);}
|
||||||
|
static inline bool main_open() {return run_main(guid_main_open);}
|
||||||
|
static inline bool main_remove_playlist() {return run_main(guid_main_remove_playlist);}
|
||||||
|
static inline bool main_remove_dead_entries() {return run_main(guid_main_remove_dead_entries);}
|
||||||
|
static inline bool main_remove_duplicates() {return run_main(guid_main_remove_duplicates);}
|
||||||
|
static inline bool main_rename_playlist() {return run_main(guid_main_rename_playlist);}
|
||||||
|
static inline bool main_save_all_playlists() {return run_main(guid_main_save_all_playlists);}
|
||||||
|
static inline bool main_save_playlist() {return run_main(guid_main_save_playlist);}
|
||||||
|
static inline bool main_playlist_search() {return run_main(guid_main_playlist_search);}
|
||||||
|
static inline bool main_playlist_sel_crop() {return run_main(guid_main_playlist_sel_crop);}
|
||||||
|
static inline bool main_playlist_sel_remove() {return run_main(guid_main_playlist_sel_remove);}
|
||||||
|
static inline bool main_playlist_sel_invert() {return run_main(guid_main_playlist_sel_invert);}
|
||||||
|
static inline bool main_playlist_undo() {return run_main(guid_main_playlist_undo);}
|
||||||
|
static inline bool main_show_console() {return run_main(guid_main_show_console);}
|
||||||
|
static inline bool main_play_cd() {return run_main(guid_main_play_cd);}
|
||||||
|
static inline bool main_restart_resetconfig() {return run_main(guid_main_restart_resetconfig);}
|
||||||
|
static inline bool main_playlist_moveback() {return run_main(guid_main_playlist_moveback);}
|
||||||
|
static inline bool main_playlist_moveforward() {return run_main(guid_main_playlist_moveforward);}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //_FOOBAR2000_MENU_MANAGER_H_
|
|
@ -0,0 +1,147 @@
|
||||||
|
#include "foobar2000.h"
|
||||||
|
|
||||||
|
int metadb::query(file_info * out)
|
||||||
|
{
|
||||||
|
playable_location_i temp(*out->get_location());
|
||||||
|
return query(&temp,out);
|
||||||
|
}
|
||||||
|
|
||||||
|
int metadb::query_flush(file_info * out)
|
||||||
|
{
|
||||||
|
playable_location_i temp(*out->get_location());
|
||||||
|
return query_flush(&temp,out);
|
||||||
|
}
|
||||||
|
|
||||||
|
int metadb::query_dbonly(file_info * out)
|
||||||
|
{
|
||||||
|
playable_location_i temp(*out->get_location());
|
||||||
|
return query_dbonly(&temp,out);
|
||||||
|
}
|
||||||
|
|
||||||
|
int metadb::query_meta_field(const playable_location * src,const char * name,int num,string8 & out)
|
||||||
|
{
|
||||||
|
int rv = 0;
|
||||||
|
metadb_handle * ptr = handle_create(src);
|
||||||
|
if (ptr)
|
||||||
|
{
|
||||||
|
rv = ptr->handle_query_meta_field(name,num,out);
|
||||||
|
ptr->handle_release();
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
int metadb::update_info(const file_info * info,bool dbonly)//using playable_location from file_info, return -1 on failure, 0 if pending, 1 on immediate success; may take a few good seconds to execute
|
||||||
|
{
|
||||||
|
int rv;
|
||||||
|
metadb_handle * handle = handle_create(info->get_location());
|
||||||
|
rv = handle->handle_update_info(info,dbonly);
|
||||||
|
handle->handle_release();
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
void metadb::user_requested_remove(const playable_location * entry)//use this when user requests files to be removed from database (but not deleted physically)
|
||||||
|
{
|
||||||
|
metadb_handle * ptr = handle_create(entry);
|
||||||
|
ptr->handle_user_requested_remove();
|
||||||
|
ptr->handle_release();
|
||||||
|
}
|
||||||
|
|
||||||
|
int metadb::query(const playable_location * entry,file_info * out)
|
||||||
|
{
|
||||||
|
int rv;
|
||||||
|
metadb_handle * handle = handle_create(entry);
|
||||||
|
rv = handle->handle_query(out,false);
|
||||||
|
handle->handle_release();
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
int metadb::query_flush(const playable_location * entry,file_info * out)
|
||||||
|
{
|
||||||
|
int rv;
|
||||||
|
metadb_handle * handle = handle_create(entry);
|
||||||
|
handle->handle_flush_info();
|
||||||
|
rv = handle->handle_query(out,false);
|
||||||
|
handle->handle_release();
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
int metadb::query_dbonly(const playable_location * entry,file_info * out)
|
||||||
|
{
|
||||||
|
int rv;
|
||||||
|
metadb_handle * handle = handle_create(entry);
|
||||||
|
rv = handle->handle_query(out,true);
|
||||||
|
handle->handle_release();
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
int metadb::precache(const playable_location * entry,reader * r)
|
||||||
|
{
|
||||||
|
int rv;
|
||||||
|
metadb_handle * handle = handle_create(entry);
|
||||||
|
rv = handle->handle_precache(r);
|
||||||
|
handle->handle_release();
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
void metadb::precache(const char * filename,reader * r)
|
||||||
|
{
|
||||||
|
if (r)
|
||||||
|
{
|
||||||
|
metadb_handle_list temp;
|
||||||
|
track_indexer::g_get_tracks_ex(filename,temp,r);
|
||||||
|
unsigned n,m=temp.get_count();
|
||||||
|
for(n=0;n<m;n++)
|
||||||
|
{
|
||||||
|
metadb_handle * ptr = temp[n];
|
||||||
|
if (!stricmp_utf8(filename,ptr->handle_get_path()))
|
||||||
|
{
|
||||||
|
r->seek(0);
|
||||||
|
ptr->handle_precache(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
temp.delete_all();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void metadb::hint(const file_info * src)
|
||||||
|
{
|
||||||
|
metadb_handle * handle = handle_create_hint(src);
|
||||||
|
handle->handle_release();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int metadb::format_title(const playable_location * source,string_base & out,const char * spec,const char * extra_items)
|
||||||
|
{
|
||||||
|
int rv;
|
||||||
|
metadb_handle * handle = handle_create(source);
|
||||||
|
rv = handle->handle_format_title(out,spec,extra_items);
|
||||||
|
handle->handle_release();
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
metadb * metadb::get()//safe not to release it; don't call from DLL startup / static object constructor
|
||||||
|
{
|
||||||
|
static metadb * ptr;
|
||||||
|
if (ptr==0) ptr = service_enum_create_t(metadb,0);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
#ifdef _DEBUG
|
||||||
|
int metadb::database_lock_count()
|
||||||
|
{
|
||||||
|
int val = database_try_lock();
|
||||||
|
if (val)
|
||||||
|
val = database_unlock();
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void metadb::database_delockify(int wanted_count)
|
||||||
|
{
|
||||||
|
assert(wanted_count >= 0);
|
||||||
|
if (wanted_count<0) wanted_count = 0;
|
||||||
|
int count = database_lock_count();
|
||||||
|
while(count < wanted_count) count = database_lock();
|
||||||
|
while(count > wanted_count) count = database_unlock();
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,122 @@
|
||||||
|
#ifndef _METADB_H_
|
||||||
|
#define _METADB_H_
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
#include "interface_helper.h"
|
||||||
|
#include "file_info.h"
|
||||||
|
|
||||||
|
class input;
|
||||||
|
class reader;
|
||||||
|
|
||||||
|
|
||||||
|
#include "metadb_handle.h"
|
||||||
|
|
||||||
|
//only one implementation in main exe, DO NOT DERIVE FROM THIS
|
||||||
|
class NOVTABLE metadb : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual int update_info_flush()=0;//tries to perform any pending tag update operations, may take a few good seconds to execute
|
||||||
|
|
||||||
|
virtual void file_moved(const char * src,const char * dst)=0;//will transfer existing database data to new path (and notify callbacks about file being moved)
|
||||||
|
virtual void file_copied(const char * src,const char * dst)=0;
|
||||||
|
virtual void file_removed(const char * src)=0;//use this to tell database that url [src] is dead
|
||||||
|
virtual void entry_dead(const playable_location * entry)=0;//same as file_removed but taking playable_location
|
||||||
|
|
||||||
|
virtual int database_lock()=0;//enters database's internal critical section, prevents other threads from accessing database as long as it's locked
|
||||||
|
virtual int database_try_lock()=0;//returns immediately without waiting, may fail, returns 0 on failure
|
||||||
|
virtual int database_unlock()=0;
|
||||||
|
|
||||||
|
|
||||||
|
virtual metadb_handle * handle_create(const playable_location * src)=0;//should never fail
|
||||||
|
virtual metadb_handle * handle_create_hint(const file_info * src)=0;//should never fail
|
||||||
|
|
||||||
|
virtual void get_all_entries(ptr_list_base<metadb_handle> & out)=0;//you get duplicate handles of all database items, you are responsible for releasing them
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
|
||||||
|
static metadb * get();//safe not to release it; don't call from DLL startup / static object constructor
|
||||||
|
|
||||||
|
//helper
|
||||||
|
static int g_format_title(const playable_location * source,string_base & out,const char * spec,const char * extra_items=0)
|
||||||
|
{
|
||||||
|
return get()->format_title(source,out,spec,extra_items);
|
||||||
|
}
|
||||||
|
|
||||||
|
int query(file_info * out);
|
||||||
|
int query_flush(file_info * out);
|
||||||
|
int query_dbonly(file_info * out);
|
||||||
|
int query_meta_field(const playable_location * src,const char * name,int num,string8 & out);
|
||||||
|
int update_info(const file_info * info,bool dbonly=false);//using playable_location from file_info, return -1 on failure, 0 if pending, 1 on immediate success; may take a few good seconds to execute
|
||||||
|
void user_requested_remove(const playable_location * entry);//use this when user requests files to be removed from database (but not deleted physically)
|
||||||
|
int query(const playable_location * entry,file_info * out);
|
||||||
|
int query_flush(const playable_location * entry,file_info * out);
|
||||||
|
int query_dbonly(const playable_location * entry,file_info * out);
|
||||||
|
int precache(const playable_location * entry,reader * r);
|
||||||
|
void precache(const char * filename,reader * r);
|
||||||
|
void hint(const file_info * src);
|
||||||
|
|
||||||
|
int format_title(const playable_location * source,string_base & out,const char * spec,const char * extra_items);
|
||||||
|
//reminder: see titleformat.h for descriptions of titleformatting parameters
|
||||||
|
//return 1 on success, 0 on failure (cant get info, used empty fields and filename)
|
||||||
|
|
||||||
|
inline void update_info_dbonly(const file_info * info) {update_info(info,true);} //updates database, doesnt modify files
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
int database_lock_count();
|
||||||
|
void database_delockify(int wanted_count);
|
||||||
|
inline void assert_not_locked()
|
||||||
|
{
|
||||||
|
assert(database_lock_count()==0);
|
||||||
|
}
|
||||||
|
inline void assert_locked()
|
||||||
|
{
|
||||||
|
assert(database_lock_count()>0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
virtual service_base * service_query(const GUID & guid)
|
||||||
|
{
|
||||||
|
if (guid == get_class_guid()) {service_add_ref();return this;}
|
||||||
|
else return service_base::service_query(guid);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void handle_release_multi(metadb_handle * const * list,unsigned count)=0;
|
||||||
|
virtual void handle_add_ref_multi(metadb_handle * const * list,unsigned count)=0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NOVTABLE metadb_callback : public service_base//use service_factory_single_t to instantiate, you will get called with database notifications
|
||||||
|
{//IMPORTANT: metadb_callback calls may happen from different threads ! watch out for deadlocks / calling things that work only from main thread / etc
|
||||||
|
public:
|
||||||
|
virtual void on_info_edited(metadb_handle * handle) {}
|
||||||
|
virtual void on_info_reloaded(metadb_handle * handle) {}
|
||||||
|
virtual void on_file_removed(metadb_handle * handle) {}
|
||||||
|
virtual void on_file_moved(metadb_handle * src,metadb_handle * dst) {}
|
||||||
|
virtual void on_file_new(metadb_handle * handle) {}//reserved for future use
|
||||||
|
virtual void on_file_removed_user(metadb_handle * handle) {}//user requested file to be removed from db, file didn't get physically removed
|
||||||
|
//warning: all callback methods are called inside database_lock section ! watch out for deadlock conditions if you use SendMessage() or critical sections
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
};
|
||||||
|
|
||||||
|
class NOVTABLE metadb_callback_simple : public service_base
|
||||||
|
{//this is always properly sync'd to main thread, unlike metadb_callback above
|
||||||
|
public:
|
||||||
|
virtual void on_info_change(metadb_handle * handle)=0;
|
||||||
|
virtual void on_file_moved(metadb_handle * src,metadb_handle * dst)=0;
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class in_metadb_sync
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
in_metadb_sync() {metadb::get()->database_lock();}
|
||||||
|
~in_metadb_sync() {metadb::get()->database_unlock();}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,224 @@
|
||||||
|
#include "foobar2000.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
struct custom_sort_data
|
||||||
|
{
|
||||||
|
HANDLE text;
|
||||||
|
int index;
|
||||||
|
custom_sort_data(const char * p_text,int p_idx)
|
||||||
|
: text(uSortStringCreate(p_text)), index(p_idx)
|
||||||
|
{}
|
||||||
|
~custom_sort_data() {uSortStringFree(text);}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
static int __cdecl custom_sort_compare(const custom_sort_data * &elem1, const custom_sort_data *&elem2 )
|
||||||
|
{//depends on unicode/ansi shit, nonportable (win32 lstrcmpi)
|
||||||
|
return uSortStringCompare(elem1->text,elem2->text);//uStringCompare
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void metadb_handle_list::sort_by_format_get_order(int * order,const char * spec,const char * extra_items) const
|
||||||
|
{
|
||||||
|
ptr_list_simple<custom_sort_data> data;
|
||||||
|
unsigned n;
|
||||||
|
string8 temp;
|
||||||
|
string8 temp2;
|
||||||
|
temp.set_mem_logic(mem_block::ALLOC_FAST_DONTGODOWN);
|
||||||
|
temp.prealloc(512);
|
||||||
|
temp2.set_mem_logic(mem_block::ALLOC_FAST_DONTGODOWN);
|
||||||
|
for(n=0;n<get_count();n++)
|
||||||
|
{
|
||||||
|
get_item(n)->handle_format_title(temp,spec,extra_items);
|
||||||
|
const char * ptr = temp.get_ptr();
|
||||||
|
if (strchr(ptr,3))
|
||||||
|
{
|
||||||
|
titleformat::remove_color_marks(ptr,temp2);
|
||||||
|
ptr = temp2;
|
||||||
|
}
|
||||||
|
data.add_item(new custom_sort_data(ptr,n));
|
||||||
|
}
|
||||||
|
|
||||||
|
data.sort(custom_sort_compare);
|
||||||
|
|
||||||
|
for(n=0;n<data.get_count();n++)
|
||||||
|
{
|
||||||
|
order[n]=data[n]->index;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.delete_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
void metadb_handle_list::sort_by_format(const char * spec,const char * extra_items)
|
||||||
|
{
|
||||||
|
unsigned count = get_count();
|
||||||
|
mem_block_t<int> order(count);
|
||||||
|
sort_by_format_get_order(order,spec,extra_items);
|
||||||
|
apply_order(order,count);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool metadb_handle::handle_query_meta_field(const char * name,int num,string_base & out)
|
||||||
|
{
|
||||||
|
bool rv = false;
|
||||||
|
handle_lock();
|
||||||
|
const file_info * info = handle_query_locked();
|
||||||
|
if (info)
|
||||||
|
{
|
||||||
|
const char * val = info->meta_get(name,num);
|
||||||
|
if (val)
|
||||||
|
{
|
||||||
|
out = val;
|
||||||
|
rv = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handle_unlock();
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
int metadb_handle::handle_query_meta_field_int(const char * name,int num)//returns 0 if not found
|
||||||
|
{
|
||||||
|
int rv = 0;
|
||||||
|
handle_lock();
|
||||||
|
const file_info * info = handle_query_locked();
|
||||||
|
if (info)
|
||||||
|
{
|
||||||
|
const char * val = info->meta_get(name,num);
|
||||||
|
if (val)
|
||||||
|
{
|
||||||
|
rv = atoi(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handle_unlock();
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
double metadb_handle::handle_get_length()
|
||||||
|
{
|
||||||
|
double rv = 0;
|
||||||
|
handle_lock();
|
||||||
|
const file_info * info = handle_query_locked();
|
||||||
|
if (info)
|
||||||
|
rv = info->get_length();
|
||||||
|
handle_unlock();
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
__int64 metadb_handle::handle_get_length_samples()
|
||||||
|
{
|
||||||
|
__int64 rv = 0;
|
||||||
|
handle_lock();
|
||||||
|
const file_info * info = handle_query_locked();
|
||||||
|
if (info)
|
||||||
|
rv = info->info_get_length_samples();
|
||||||
|
handle_unlock();
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void metadb_handle_list::delete_item(metadb_handle * ptr)
|
||||||
|
{
|
||||||
|
remove_item(ptr);
|
||||||
|
ptr->handle_release();
|
||||||
|
}
|
||||||
|
|
||||||
|
void metadb_handle_list::delete_by_idx(int idx)
|
||||||
|
{
|
||||||
|
metadb_handle * ptr = remove_by_idx(idx);
|
||||||
|
if (ptr) ptr->handle_release();
|
||||||
|
}
|
||||||
|
|
||||||
|
void metadb_handle_list::delete_all()
|
||||||
|
{
|
||||||
|
if (get_count()>0)
|
||||||
|
{
|
||||||
|
metadb::get()->handle_release_multi(get_ptr(),get_count());
|
||||||
|
remove_all();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void metadb_handle_list::add_ref_all()
|
||||||
|
{
|
||||||
|
if (get_count()>0) metadb::get()->handle_add_ref_multi(get_ptr(),get_count());
|
||||||
|
}
|
||||||
|
|
||||||
|
void metadb_handle_list::delete_mask(const bit_array & mask)
|
||||||
|
{
|
||||||
|
unsigned n,m=get_count();
|
||||||
|
for_each_bit_array(n,mask,true,0,m)
|
||||||
|
get_item(n)->handle_release();
|
||||||
|
remove_mask(mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
void metadb_handle_list::filter_mask_del(const bit_array & mask)
|
||||||
|
{
|
||||||
|
unsigned n,m=get_count();
|
||||||
|
for_each_bit_array(n,mask,false,0,m)
|
||||||
|
get_item(n)->handle_release();
|
||||||
|
filter_mask(mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
void metadb_handle_list::remove_duplicates(bool b_release)
|
||||||
|
{
|
||||||
|
unsigned count = get_count();
|
||||||
|
if (count>0)
|
||||||
|
{
|
||||||
|
bit_array_bittable mask(count);
|
||||||
|
mem_block_t<int> order(count);
|
||||||
|
|
||||||
|
sort_by_format_get_order(order,"%_path_raw%|$num(%_subsong%,9)",0);
|
||||||
|
|
||||||
|
unsigned n;
|
||||||
|
for(n=0;n<count-1;n++)
|
||||||
|
{
|
||||||
|
if (get_item(order[n])==get_item(order[n+1]))
|
||||||
|
{
|
||||||
|
mask.set(order[n+1],true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (b_release) delete_mask(mask);
|
||||||
|
else remove_mask(mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int remove_duplicates_quick_compare(const metadb_handle * &val1,const metadb_handle * &val2)
|
||||||
|
{
|
||||||
|
if (val1<val2) return -1;
|
||||||
|
else if (val1>val2) return 1;
|
||||||
|
else return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void metadb_handle_list::remove_duplicates_quick(bool b_release)
|
||||||
|
{
|
||||||
|
unsigned count = get_count();
|
||||||
|
if (count>0)
|
||||||
|
{
|
||||||
|
sort(remove_duplicates_quick_compare);
|
||||||
|
bit_array_bittable mask(count);
|
||||||
|
unsigned n;
|
||||||
|
bool b_found = false;
|
||||||
|
for(n=0;n<count-1;n++)
|
||||||
|
{
|
||||||
|
if (get_item(n)==get_item(n+1))
|
||||||
|
{
|
||||||
|
b_found = true;
|
||||||
|
mask.set(n+1,true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (b_found)
|
||||||
|
{
|
||||||
|
if (b_release) delete_mask(mask);
|
||||||
|
else remove_mask(mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void metadb_handle_list::duplicate_list(const ptr_list_base<metadb_handle> & source)
|
||||||
|
{
|
||||||
|
unsigned n,m = source.get_count();
|
||||||
|
for(n=0;n<m;n++) add_item(source[n]->handle_duplicate());
|
||||||
|
}
|
||||||
|
|
||||||
|
void metadb_handle_list::sort_by_path_quick()
|
||||||
|
{
|
||||||
|
sort_by_format("%_path_raw%",0);
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
#ifndef _FOOBAR2000_METADB_HANDLE_H_
|
||||||
|
#define _FOOBAR2000_METADB_HANDLE_H_
|
||||||
|
|
||||||
|
#include "file_info.h"
|
||||||
|
|
||||||
|
class reader;
|
||||||
|
|
||||||
|
//metadb_handle is a reference to file_info stored in database (see: file_info.h)
|
||||||
|
//as long as handle is active, info about particular is kept in database and immediately available (unless file i/o error occurs or the file gets removed)
|
||||||
|
//metadb_handle is the most efficient way of passing around playable resource locations and info about them, since all you have to do is to copy a pointer (and possibly call handle_add_ref)
|
||||||
|
//metadb_handle also lets you efficiently store locations of playable resources without having to waste memory on file path strings allocated by playable_location etc
|
||||||
|
//metadb_handle lets you pull file_info immediately without searching database, unlike metadb::query and other metadb api calls
|
||||||
|
//metadb_handle objects keep their reference counts, eg. you need to make a matching handle_release() call for every handle_add_ref()
|
||||||
|
|
||||||
|
/*
|
||||||
|
common n00b FAQ:
|
||||||
|
|
||||||
|
getting file location from metadb_handle -> use handle_get_location(), it never fails, returned pointer valid until you release the handle
|
||||||
|
handle_query_locked MAY fail and return null when file io error occurs or something, so CHECK the result BEFORE using it
|
||||||
|
make sure you call handle_lock/handle_unlock when you use handle_query_locked, otherwise some other thread might modify database data while you are working with result and thing will be fucked
|
||||||
|
|
||||||
|
unless commented otherwise, functions taking metadb_handle as parameter don't release the handle and add a reference if they store it; when function returns a metadb_handle, calling code is responsible for releasing it
|
||||||
|
EXCEPTION: all ptr_list-style objects dont touch reference counts when they receive/return metadb_handle; that includes ptr_list_interface<metadb_handle>, ptr_list_t<metadb_handle> and metadb_handle_list
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
class NOVTABLE metadb_handle
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void handle_add_ref()=0;
|
||||||
|
virtual void handle_release()=0;
|
||||||
|
virtual int handle_query(file_info * dest,bool dbonly = false)=0;//dest can be null if you're only checking if info is available
|
||||||
|
virtual const file_info * handle_query_locked(bool dbonly = false)=0;//must be inside database_lock()-protected section, may return 0 if info is not available for some reason
|
||||||
|
virtual const playable_location * handle_get_location() const = 0;//never fails, returned pointer valid till you call release
|
||||||
|
virtual void handle_hint(const file_info * src)=0;
|
||||||
|
virtual int handle_format_title(string_base & out,const char * spec,const char * extra_items)=0;//reminder: see titleformat.h for descriptions of titleformatting parameters
|
||||||
|
virtual int handle_flush_info()=0;//tries to reload info from file
|
||||||
|
virtual int handle_update_info(const file_info * src,bool dbonly=false)=0;//return value - see metadb::update_info(); src may be null if you want to update file with info which is currently in database
|
||||||
|
virtual void handle_entry_dead()=0;//for reporting dead files; need to call handle_release() separately
|
||||||
|
virtual int handle_precache(reader * r=0)=0;//makes sure that info is in database; reader is optional
|
||||||
|
virtual void handle_user_requested_remove()=0;//user requests this object to be removed from database (but file isn't being physically removed)
|
||||||
|
virtual int handle_lock()=0;//same as metadb::database_lock()
|
||||||
|
virtual int handle_try_lock()=0;
|
||||||
|
virtual int handle_unlock()=0;//same as metadb::database_unlock()
|
||||||
|
virtual int handle_update_and_unlock(const file_info * info)=0;//new in 0.7.1; will unlock even if update fails
|
||||||
|
|
||||||
|
//new in 0.8
|
||||||
|
//time is a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601; 0 for invalid/unknown time
|
||||||
|
virtual __int64 handle_get_modification_time() const = 0;
|
||||||
|
virtual void handle_set_modification_time(__int64)=0;
|
||||||
|
virtual __int64 handle_get_file_size() const = 0;
|
||||||
|
virtual void handle_set_file_size(__int64)=0;
|
||||||
|
virtual void handle_hint_ex(const file_info * src,__int64 time,__int64 size,bool b_fresh)=0;
|
||||||
|
virtual int handle_check_if_changed()=0;//compares known modification time with file system and reloads info if needed
|
||||||
|
virtual int handle_is_permcached() const = 0;//returns if this object is in "database" or not
|
||||||
|
virtual int handle_get_relative_path(string_base & out) const = 0;
|
||||||
|
|
||||||
|
|
||||||
|
inline int handle_query_dbonly(file_info * dest) {return handle_query(dest,true);}
|
||||||
|
inline const file_info * handle_query_locked_dbonly() {return handle_query_locked(true);}
|
||||||
|
|
||||||
|
|
||||||
|
inline metadb_handle * handle_duplicate() {handle_add_ref();return this;}
|
||||||
|
|
||||||
|
bool handle_query_meta_field(const char * name,int num,string_base & out);
|
||||||
|
int handle_query_meta_field_int(const char * name,int num);//returns 0 if not found
|
||||||
|
double handle_get_length();
|
||||||
|
__int64 handle_get_length_samples();
|
||||||
|
|
||||||
|
inline const char * handle_get_path() const//never fails
|
||||||
|
{
|
||||||
|
return handle_get_location()->get_path();
|
||||||
|
}
|
||||||
|
inline int handle_get_subsong_index() const
|
||||||
|
{
|
||||||
|
return handle_get_location()->get_subsong_index();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
protected:
|
||||||
|
metadb_handle() {}
|
||||||
|
~metadb_handle() {}//verboten, avoid ptr_list_t stupidity
|
||||||
|
};
|
||||||
|
|
||||||
|
//important: standard conventions with addref'ing before returning / functions addref'ing their params DO NOT apply to pointer lists !!!
|
||||||
|
|
||||||
|
class metadb_handle_list : public ptr_list_t<metadb_handle>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void delete_item(metadb_handle * ptr);
|
||||||
|
void delete_by_idx(int idx);
|
||||||
|
void delete_all();
|
||||||
|
void add_ref_all();
|
||||||
|
void delete_mask(const bit_array & mask);
|
||||||
|
void filter_mask_del(const bit_array & mask);
|
||||||
|
void duplicate_list(const ptr_list_base<metadb_handle> & source);
|
||||||
|
|
||||||
|
void sort_by_format_get_order(int * order,const char * spec,const char * extra_items) const; //only outputs order, doesnt modify list
|
||||||
|
void sort_by_format(const char * spec,const char * extra_items);
|
||||||
|
void remove_duplicates(bool b_release = true);
|
||||||
|
void remove_duplicates_quick(bool b_release = true);//sorts by pointer values internally
|
||||||
|
void sort_by_path_quick();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class metadb_handle_lock
|
||||||
|
{
|
||||||
|
metadb_handle * ptr;
|
||||||
|
public:
|
||||||
|
inline metadb_handle_lock(metadb_handle * param)
|
||||||
|
{
|
||||||
|
ptr = param->handle_duplicate();
|
||||||
|
ptr->handle_lock();
|
||||||
|
}
|
||||||
|
inline ~metadb_handle_lock() {ptr->handle_unlock();ptr->handle_release();}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //_FOOBAR2000_METADB_HANDLE_H_
|
|
@ -0,0 +1,23 @@
|
||||||
|
#include "foobar2000.h"
|
||||||
|
|
||||||
|
void modeless_dialog_manager::add(HWND wnd)
|
||||||
|
{
|
||||||
|
service_enum_t<modeless_dialog_manager> e;
|
||||||
|
modeless_dialog_manager * ptr;
|
||||||
|
for(ptr=e.first();ptr;ptr=e.next())
|
||||||
|
{
|
||||||
|
ptr->_add(wnd);
|
||||||
|
ptr->service_release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void modeless_dialog_manager::remove(HWND wnd)
|
||||||
|
{
|
||||||
|
service_enum_t<modeless_dialog_manager> e;
|
||||||
|
modeless_dialog_manager * ptr;
|
||||||
|
for(ptr=e.first();ptr;ptr=e.next())
|
||||||
|
{
|
||||||
|
ptr->_remove(wnd);
|
||||||
|
ptr->service_release();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
#ifndef _MODELESS_DIALOG_H_
|
||||||
|
#define _MODELESS_DIALOG_H_
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
|
||||||
|
//use this to hook your modeless dialogs to main message loop and get IsDialogMessage() stuff
|
||||||
|
//not needed for config pages
|
||||||
|
|
||||||
|
class modeless_dialog_manager : public service_base
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
virtual void _add(HWND wnd)=0;
|
||||||
|
virtual void _remove(HWND wnd)=0;
|
||||||
|
public:
|
||||||
|
static void add(HWND wnd);
|
||||||
|
static void remove(HWND wnd);
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //_MODELESS_DIALOG_H_
|
|
@ -0,0 +1,19 @@
|
||||||
|
#include "foobar2000.h"
|
||||||
|
|
||||||
|
output * output::create(GUID g)
|
||||||
|
{
|
||||||
|
output * ptr;
|
||||||
|
service_enum_t<output> e;
|
||||||
|
for(ptr=e.first();ptr;ptr=e.next())
|
||||||
|
{
|
||||||
|
if (ptr->get_guid()==g) return ptr;
|
||||||
|
ptr->service_release();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void output_i_base::set_error(const char * msg)
|
||||||
|
{
|
||||||
|
console::error(msg);
|
||||||
|
}
|
|
@ -0,0 +1,166 @@
|
||||||
|
#ifndef _OUTPUT_H_
|
||||||
|
#define _OUTPUT_H_
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
#include "audio_chunk.h"
|
||||||
|
#include "interface_helper.h"
|
||||||
|
|
||||||
|
|
||||||
|
class NOVTABLE output : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual double get_latency()=0;//in seconds
|
||||||
|
virtual int process_samples(const output_chunk * chunk,int format_code)=0;//1 on success, 0 on error, no sleeping, copies data to its own buffer immediately; format_code - see enums below
|
||||||
|
virtual int update()=0;//return 1 if ready for next write, 0 if still working with last process_samples(), -1 on error
|
||||||
|
virtual void pause(int state)=0;
|
||||||
|
virtual void flush()=0;
|
||||||
|
virtual void force_play()=0;
|
||||||
|
virtual GUID get_guid()=0;//for storing config
|
||||||
|
virtual const char * get_name()=0;//for preferences
|
||||||
|
virtual const char * get_config_page_name() {return 0;}//for "go to settings" button, return 0 if you dont have a config page
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
DATA_FORMAT_LINEAR_PCM,
|
||||||
|
DATA_FORMAT_IEEE_FLOAT,//32bit float
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
|
||||||
|
static output * create(GUID g);
|
||||||
|
|
||||||
|
virtual service_base * service_query(const GUID & guid)
|
||||||
|
{
|
||||||
|
if (guid == get_class_guid()) {service_add_ref();return this;}
|
||||||
|
else return service_base::service_query(guid);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
//implementation helper, you can derive from this instead of deriving directly from output
|
||||||
|
class NOVTABLE output_i_base : public output
|
||||||
|
{
|
||||||
|
mem_block_t<char> chunk;
|
||||||
|
int chunk_size,chunk_ptr;
|
||||||
|
int new_bps,new_srate,new_nch;
|
||||||
|
int cur_bps,cur_srate,cur_nch;
|
||||||
|
int is_open;
|
||||||
|
int cur_format_code,new_format_code;
|
||||||
|
|
||||||
|
|
||||||
|
protected:
|
||||||
|
output_i_base()
|
||||||
|
{
|
||||||
|
is_open = 0;
|
||||||
|
chunk_size = chunk_ptr = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_error(const char * msg);
|
||||||
|
|
||||||
|
//override me
|
||||||
|
virtual int open_ex(int srate,int bps,int nch,int format_code)//return 1 on success, 0 on failure, might get called when you are open already (eg. new PCM format)
|
||||||
|
{
|
||||||
|
if (format_code==DATA_FORMAT_LINEAR_PCM)
|
||||||
|
{
|
||||||
|
return open(srate,bps,nch);//call old version if not overridden
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
set_error("Unsupported output data format.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
virtual int open(int srate,int bps,int nch) {return 0;}//old version
|
||||||
|
virtual int can_write()=0;//in bytes
|
||||||
|
virtual int write(const char * data,int bytes)=0;//return 1 on success, 0 on error, bytes always <= last can_write() return value
|
||||||
|
virtual int get_latency_bytes()=0;//amount of data buffered (write_offset-playback_offset), in bytes
|
||||||
|
virtual void pause(int state)=0;
|
||||||
|
virtual void force_play()=0;
|
||||||
|
virtual int do_flush() {return 1;} //return 1 if you need reopen, 0 if successfully flushed
|
||||||
|
virtual int is_playing() {return get_latency_bytes()>0;}
|
||||||
|
virtual GUID get_guid()=0;
|
||||||
|
virtual const char * get_name()=0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual double get_latency()
|
||||||
|
{
|
||||||
|
double rv = 0;
|
||||||
|
if (is_open)
|
||||||
|
{
|
||||||
|
int delta = get_latency_bytes();
|
||||||
|
if (delta<0) delta=0;
|
||||||
|
rv += (double) (delta / (cur_nch * cur_bps/8)) / (double) cur_srate;
|
||||||
|
}
|
||||||
|
if (chunk_size>0 && chunk_ptr<chunk_size)
|
||||||
|
{
|
||||||
|
rv += (double)( (chunk_size-chunk_ptr) / (new_nch * new_bps / 8) ) / (double) new_srate;
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int process_samples(const output_chunk * p_chunk,int format_code)
|
||||||
|
{
|
||||||
|
chunk.copy((const char*)p_chunk->data,p_chunk->size);
|
||||||
|
chunk_size = p_chunk->size;
|
||||||
|
chunk_ptr = 0;
|
||||||
|
new_bps = p_chunk->bps;
|
||||||
|
new_srate = p_chunk->srate;
|
||||||
|
new_nch = p_chunk->nch;
|
||||||
|
new_format_code = format_code;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
virtual int update()
|
||||||
|
{
|
||||||
|
if (chunk_size<=0 || chunk_ptr>=chunk_size) return 1;
|
||||||
|
if (is_open && (cur_bps!=new_bps || cur_srate!=new_srate || cur_nch!=new_nch || cur_format_code!=new_format_code))
|
||||||
|
{
|
||||||
|
force_play();
|
||||||
|
if (is_playing()) return 0;
|
||||||
|
is_open = 0;
|
||||||
|
}
|
||||||
|
if (!is_open)
|
||||||
|
{
|
||||||
|
if (!open_ex(new_srate,new_bps,new_nch,new_format_code))
|
||||||
|
return -1;//error
|
||||||
|
cur_srate = new_srate;
|
||||||
|
cur_nch = new_nch;
|
||||||
|
cur_bps = new_bps;
|
||||||
|
cur_format_code = new_format_code;
|
||||||
|
is_open = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cw;
|
||||||
|
while((cw = can_write())>0 && chunk_ptr<chunk_size)
|
||||||
|
{
|
||||||
|
int d = chunk_size - chunk_ptr;
|
||||||
|
if (d>cw) d=cw;
|
||||||
|
if (!write(chunk+chunk_ptr,d)) return -1;
|
||||||
|
chunk_ptr+=d;
|
||||||
|
}
|
||||||
|
if (chunk_ptr<chunk_size)
|
||||||
|
{
|
||||||
|
force_play();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void flush()
|
||||||
|
{
|
||||||
|
if (is_open)
|
||||||
|
{
|
||||||
|
if (do_flush()) is_open = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class output_factory : public service_factory_t<output,T> {};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,54 @@
|
||||||
|
#ifndef _OUTPUT_MANAGER_H_
|
||||||
|
#define _OUTPUT_MANAGER_H_
|
||||||
|
|
||||||
|
#include "output.h"
|
||||||
|
|
||||||
|
struct output_config
|
||||||
|
{
|
||||||
|
GUID output;//null guid for default user-configured device
|
||||||
|
|
||||||
|
int b_override_format;
|
||||||
|
|
||||||
|
struct format_t
|
||||||
|
{
|
||||||
|
int bps,bps_pad;
|
||||||
|
int b_float;
|
||||||
|
int b_dither;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
format_t format;//format valid only if b_override_format is set to non-zero, otherwise user settings from core preferences are used
|
||||||
|
|
||||||
|
output_config()
|
||||||
|
{
|
||||||
|
memset(&output,0,sizeof(output));
|
||||||
|
b_override_format = 0;
|
||||||
|
|
||||||
|
format.bps = 16;
|
||||||
|
format.bps_pad = 16;
|
||||||
|
format.b_float = 0;
|
||||||
|
format.b_dither = 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class NOVTABLE output_manager : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
|
||||||
|
static output_manager * create() {return service_enum_create_t(output_manager,0);}//may fail on pre-0.63
|
||||||
|
|
||||||
|
virtual void get_default_config(output_config * out)=0;//retrieves current user settings from core preferences; do not pass returned data to set_config (same effect as not calling set_config at all or passing uninitialized struct)
|
||||||
|
|
||||||
|
virtual void set_config(const output_config * param)=0;//optionally call before first use
|
||||||
|
|
||||||
|
virtual double get_latency()=0;//in seconds
|
||||||
|
virtual int process_samples(const audio_chunk* chunk)=0;//1 on success, 0 on error, no sleeping, copies data to its own buffer immediately; format_code - see enums below
|
||||||
|
virtual int update()=0;//return 1 if ready for next write, 0 if still working with last process_samples(), -1 on error
|
||||||
|
virtual void pause(int state)=0;
|
||||||
|
virtual void flush()=0;
|
||||||
|
virtual void force_play()=0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //_OUTPUT_MANAGER_H_
|
|
@ -0,0 +1,13 @@
|
||||||
|
#include "foobar2000.h"
|
||||||
|
|
||||||
|
packet_decoder * packet_decoder::g_open(const GUID & owner,unsigned param1,const void * param2,unsigned param2size,file_info * info)
|
||||||
|
{
|
||||||
|
service_enum_t<packet_decoder> e;
|
||||||
|
packet_decoder * ptr;
|
||||||
|
for(ptr=e.first();ptr;ptr=e.next())
|
||||||
|
{
|
||||||
|
if (ptr->open_stream(owner,param1,param2,param2size,info)) return ptr;
|
||||||
|
ptr->service_release();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
#ifndef _FOOBAR2000_PACKET_DECODER_H_
|
||||||
|
#define _FOOBAR2000_PACKET_DECODER_H_
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
#include "audio_chunk.h"
|
||||||
|
|
||||||
|
class packet_decoder : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
|
||||||
|
virtual bool open_stream(const GUID & owner,unsigned param1,const void * param2,unsigned param2size,file_info * info)=0;//owner - container GUID, param1/2 - contents depend on owner
|
||||||
|
virtual unsigned set_stream_property(const GUID & type,unsigned param1,const void * param2,unsigned param2size,file_info * info) {return 0;}//optional, return values depend on guid, return 0 by default
|
||||||
|
|
||||||
|
virtual unsigned get_max_frame_dependency()=0;
|
||||||
|
virtual double get_max_frame_dependency_time()=0;
|
||||||
|
|
||||||
|
virtual void reset_after_seek()=0;
|
||||||
|
virtual bool decode(const void * buffer,unsigned bytes,audio_chunk * out)=0;//may return empty chunk (decoder delay etc), caller must check for it (and not crash on samplerate==0 etc)
|
||||||
|
virtual const char * get_name()=0;//for diagnostic purposes, codec list, etc
|
||||||
|
|
||||||
|
static packet_decoder * g_open(const GUID & owner,unsigned param1,const void * param2,unsigned param2size,file_info * info);
|
||||||
|
|
||||||
|
static const GUID owner_MP4,owner_matroska,owner_MP3;
|
||||||
|
|
||||||
|
struct matroska_setup
|
||||||
|
{
|
||||||
|
const char * codec_id;
|
||||||
|
unsigned sample_rate,sample_rate_output;
|
||||||
|
unsigned channels;
|
||||||
|
unsigned codec_private_size;
|
||||||
|
const void * codec_private;
|
||||||
|
};
|
||||||
|
//owner_MP4: param1 - codec ID (MP4 audio type), param2 - MP4 codec initialization data
|
||||||
|
//owner_MP3: raw MP3/MP2 file, parameters ignored
|
||||||
|
//owner_matroska: param2 = matroska_setup struct, param2size size must be equal to sizeof(matroska_setup)
|
||||||
|
|
||||||
|
|
||||||
|
//these are used to initialize PCM decoder
|
||||||
|
static const GUID property_samplerate,property_bitspersample,property_channels,property_byteorder,property_signed;
|
||||||
|
//property_samplerate : param1 == sample rate in hz
|
||||||
|
//property_bitspersample : param1 == bits per sample
|
||||||
|
//property_channels : param1 == channel count
|
||||||
|
//property_byteorder : if (param1) little_endian; else big_endian;
|
||||||
|
//property_signed : if (param1) signed; else unsigned;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class packet_decoder_factory : public service_factory_t<packet_decoder,T> {};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //_FOOBAR2000_PACKET_DECODER_H_
|
|
@ -0,0 +1,44 @@
|
||||||
|
#ifndef _PLAY_CALLBACK_H_
|
||||||
|
#define _PLAY_CALLBACK_H_
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
|
||||||
|
#include "metadb_handle.h"
|
||||||
|
//callbacks for getting notified about playback status
|
||||||
|
//multithread safety: all play_callback api calls come from main thread.
|
||||||
|
|
||||||
|
#include "play_control.h"
|
||||||
|
|
||||||
|
class NOVTABLE play_callback : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum {
|
||||||
|
MASK_on_playback_starting = 1<<0, MASK_on_playback_new_track = 1<<1, MASK_on_playback_stop = 1<<2,
|
||||||
|
MASK_on_playback_seek = 1<<3, MASK_on_playback_pause = 1<<4, MASK_on_playback_edited = 1<<5,
|
||||||
|
MASK_on_playback_dynamic_info = 1<<6, MASK_on_playback_time = 1<<7, MASK_on_volume_change = 1<<8,
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual unsigned get_callback_mask() {return (unsigned)(-1);}
|
||||||
|
//returns combination of MASK_* above, return value must be constant, called only once
|
||||||
|
//system uses it as a hint to avoid calling you when not needed, to reduce working code size (and "memory usage")
|
||||||
|
|
||||||
|
virtual void on_playback_starting()=0;
|
||||||
|
virtual void on_playback_new_track(metadb_handle * track)=0;
|
||||||
|
virtual void on_playback_stop(play_control::stop_reason reason)=0;
|
||||||
|
virtual void on_playback_seek(double time)=0;
|
||||||
|
virtual void on_playback_pause(int state)=0;
|
||||||
|
virtual void on_playback_edited(metadb_handle * track)=0;//currently played file got edited
|
||||||
|
virtual void on_playback_dynamic_info(const file_info * info,bool b_track_change)=0;
|
||||||
|
virtual void on_playback_time(metadb_handle * track,double val) {}//called every second
|
||||||
|
virtual void on_volume_change(int new_val) {};//may also happen when not playing
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class play_callback_factory : public service_factory_single_t<play_callback,T> {};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,9 @@
|
||||||
|
#include "foobar2000.h"
|
||||||
|
|
||||||
|
|
||||||
|
play_control * play_control::get()
|
||||||
|
{
|
||||||
|
static play_control * ptr;
|
||||||
|
if (!ptr) ptr = service_enum_create_t(play_control,0);
|
||||||
|
return ptr;
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
#ifndef _PLAY_CONTROL_H_
|
||||||
|
#define _PLAY_CONTROL_H_
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
#include "interface_helper.h"
|
||||||
|
#include "metadb_handle.h"
|
||||||
|
//important: you should call api commands declared in this header ONLY from main thread !!
|
||||||
|
//do not call them from a database_lock section, some commands (eg. those stopping playback) will deadlock if you do !
|
||||||
|
|
||||||
|
|
||||||
|
//note:
|
||||||
|
//all methods returning metadb_handle return add_ref'd handles, you have to handle_release them
|
||||||
|
//all methods taking metadb_handle will add_ref them if they store them, so it's your responsibility to later release handles you pass
|
||||||
|
|
||||||
|
class NOVTABLE play_control : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum stop_reason {
|
||||||
|
STOP_REASON_USER = 0,
|
||||||
|
STOP_REASON_EOF,
|
||||||
|
STOP_REASON_STARTING_ANOTHER,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
enum track_command {
|
||||||
|
TRACK_COMMAND_ABORT = -1,
|
||||||
|
TRACK_COMMAND_DEFAULT = 0,
|
||||||
|
TRACK_COMMAND_PLAY,
|
||||||
|
TRACK_COMMAND_NEXT,
|
||||||
|
TRACK_COMMAND_PREV,
|
||||||
|
TRACK_COMMAND_SETTRACK,
|
||||||
|
TRACK_COMMAND_RAND,
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual metadb_handle * get_now_playing()=0;//will return 0 if not playing
|
||||||
|
virtual void play_item(metadb_handle * handle)=0;
|
||||||
|
virtual void play_start(track_command cmd = TRACK_COMMAND_PLAY)=0;
|
||||||
|
virtual void play_stop(stop_reason reason = STOP_REASON_USER)=0;
|
||||||
|
virtual bool is_playing()=0;
|
||||||
|
virtual bool is_paused()=0;
|
||||||
|
virtual double get_playback_time()=0;
|
||||||
|
virtual void pause()=0;
|
||||||
|
|
||||||
|
virtual void show_config(const char * page_name)=0;
|
||||||
|
|
||||||
|
virtual bool get_stop_after_current()=0;
|
||||||
|
virtual void set_stop_after_current(bool state)=0;
|
||||||
|
virtual void set_volume(int)=0;
|
||||||
|
virtual int get_volume()=0;//volume is in 0.01 dB
|
||||||
|
virtual void volume_up()=0;
|
||||||
|
virtual void volume_down()=0;
|
||||||
|
virtual void volume_mute_toggle()=0;
|
||||||
|
|
||||||
|
virtual void playback_seek(double param)=0;
|
||||||
|
virtual void playback_seek_delta(double param)=0;
|
||||||
|
virtual bool playback_can_seek()=0;
|
||||||
|
virtual double playback_get_position()=0;
|
||||||
|
|
||||||
|
//reminder: see titleformat.h for descriptions of titleformatting parameters
|
||||||
|
virtual void playback_format_title(string_base & out,const char * spec,const char * extra_items=0)=0;
|
||||||
|
|
||||||
|
virtual bool playback_format_title_ex(metadb_handle * handle,string_base & out,const char * spec,const char * extra_items,bool b_include_time,bool b_include_dynamicinfo)=0;//0.7.1 and above
|
||||||
|
|
||||||
|
double playback_get_length()
|
||||||
|
{
|
||||||
|
double rv = 0;
|
||||||
|
metadb_handle * ptr = get_now_playing();
|
||||||
|
if (ptr)
|
||||||
|
{
|
||||||
|
rv = ptr->handle_get_length();
|
||||||
|
ptr->handle_release();
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void toggle_stop_after_current() {set_stop_after_current(!get_stop_after_current());}
|
||||||
|
|
||||||
|
static play_control * get();
|
||||||
|
|
||||||
|
static void g_show_config(const char * page_name)
|
||||||
|
{
|
||||||
|
get()->show_config(page_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//playlist_oper moved to playlist.h
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,14 @@
|
||||||
|
#include "foobar2000.h"
|
||||||
|
|
||||||
|
int playable_location::compare(const playable_location * src) const
|
||||||
|
{
|
||||||
|
int ret = uStringCompare(get_path(),src->get_path());
|
||||||
|
if (ret!=0) return ret;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int n1 = get_number(), n2 = src->get_number();
|
||||||
|
if (n1<n2) return -1;
|
||||||
|
else if (n1>n2) return 1;
|
||||||
|
else return 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
#ifndef _FOOBAR2000_PLAYABLE_LOCATION_H_
|
||||||
|
#define _FOOBAR2000_PLAYABLE_LOCATION_H_
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
|
||||||
|
#include "interface_helper.h"
|
||||||
|
|
||||||
|
//playable_location stores location of a playable resource, currently implemented as file path and integer for indicating multiple playable "subsongs" per file
|
||||||
|
//also see: file_info.h
|
||||||
|
//for getting more info about resource referenced by a playable_location, see metadb.h
|
||||||
|
|
||||||
|
//char* strings are all UTF-8
|
||||||
|
|
||||||
|
class playable_location//interface (for passing around between DLLs)
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual const char * get_path() const =0;
|
||||||
|
virtual void set_path(const char*)=0;
|
||||||
|
virtual int get_number() const =0;
|
||||||
|
virtual void set_number(int)=0;
|
||||||
|
virtual ~playable_location() {};
|
||||||
|
|
||||||
|
void copy(const playable_location * src)
|
||||||
|
{
|
||||||
|
set_path(src->get_path());
|
||||||
|
set_number(src->get_number());
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
int compare_sort(const playable_location * src) const
|
||||||
|
{
|
||||||
|
int ret = stricmp_utf8_full(file_path_display(get_path()),file_path_display(src->get_path()));
|
||||||
|
if (ret!=0) return ret;
|
||||||
|
else return compare(src);
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int compare(const playable_location * src) const;
|
||||||
|
|
||||||
|
const playable_location & operator=(const playable_location & src)
|
||||||
|
{
|
||||||
|
copy(&src);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool is_empty() {return get_path()[0]==0 && get_number()==0;}
|
||||||
|
inline void reset() {set_path("");set_number(0);}
|
||||||
|
inline int get_subsong_index() const {return get_number();}
|
||||||
|
inline void set_subsong_index(int v) {set_number(v);}
|
||||||
|
};
|
||||||
|
|
||||||
|
class playable_location_i : public playable_location//implementation
|
||||||
|
{
|
||||||
|
string_simple path;
|
||||||
|
int number;
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual const char * get_path() const {return path;}
|
||||||
|
virtual void set_path(const char* z) {path=z;}
|
||||||
|
virtual int get_number() const {return number;}
|
||||||
|
virtual void set_number(int z) {number=z;}
|
||||||
|
|
||||||
|
playable_location_i() {number=0;}
|
||||||
|
|
||||||
|
const playable_location_i & operator=(const playable_location & src)
|
||||||
|
{
|
||||||
|
copy(&src);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
playable_location_i(const char * p_path,int p_number) : path(p_path), number(p_number) {}
|
||||||
|
|
||||||
|
playable_location_i(const playable_location & src) {copy(&src);}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// usage: something( make_playable_location("file://c:\blah.ogg",0) );
|
||||||
|
// only for use as a parameter to a function taking const playable_location*
|
||||||
|
class make_playable_location : private playable_location
|
||||||
|
{
|
||||||
|
const char * path;
|
||||||
|
int num;
|
||||||
|
virtual const char * get_path() const {return path;}
|
||||||
|
virtual void set_path(const char*) {}
|
||||||
|
virtual int get_number() const {return num;}
|
||||||
|
virtual void set_number(int) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
make_playable_location(const char * p_path,int p_num) : path(p_path), num(p_num) {}
|
||||||
|
operator const playable_location * () {return this;}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //_FOOBAR2000_PLAYABLE_LOCATION_H_
|
|
@ -0,0 +1,177 @@
|
||||||
|
#ifndef _PLAYBACK_CORE_H_
|
||||||
|
#define _PLAYBACK_CORE_H_
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
#include "metadb_handle.h"
|
||||||
|
#include "replaygain.h"
|
||||||
|
#include "output_manager.h"
|
||||||
|
|
||||||
|
struct playback_config
|
||||||
|
{
|
||||||
|
enum dsp_mode_t
|
||||||
|
{
|
||||||
|
DSP_DEFAULT,//DSP enabled, use core settings
|
||||||
|
DSP_DISABLED,
|
||||||
|
DSP_CUSTOM,//DSP enabled, use custom settings
|
||||||
|
};
|
||||||
|
|
||||||
|
replaygain::mode_t replaygain_mode;
|
||||||
|
dsp_mode_t dsp_mode;
|
||||||
|
int process_total_time_counter;//boolean
|
||||||
|
int low_priority;//boolean
|
||||||
|
int process_dynamic_info;//boolean
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
unsigned int count;
|
||||||
|
const GUID * guids;
|
||||||
|
} dsp_custom;//valid only if dsp_mode = DSP_CUSTOM
|
||||||
|
|
||||||
|
output_config output;
|
||||||
|
|
||||||
|
playback_config()
|
||||||
|
{
|
||||||
|
replaygain_mode = replaygain::MODE_DEFAULT;
|
||||||
|
dsp_mode = DSP_DEFAULT;
|
||||||
|
dsp_custom.count = 0;
|
||||||
|
dsp_custom.guids = 0;
|
||||||
|
process_total_time_counter = 0;
|
||||||
|
low_priority = 0;
|
||||||
|
process_dynamic_info = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class NOVTABLE playback_core : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
struct track//reminder: if you pass this around, functions you call will not free the metadb_handle; if you return this, make sure you handle_add_ref(), because the code calling you will be responsible for freeing
|
||||||
|
{
|
||||||
|
metadb_handle * handle;
|
||||||
|
int user;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class NOVTABLE callback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum file_error_handler
|
||||||
|
{
|
||||||
|
file_error_abort,
|
||||||
|
file_error_continue,
|
||||||
|
file_error_replace
|
||||||
|
};
|
||||||
|
virtual bool get_next_track(track * out)=0;//return false if end-of-playlist, true if out struct is filled with data
|
||||||
|
virtual void on_file_open(const track * src) {}//called before attempting file open; use only for logging etc
|
||||||
|
virtual void on_file_open_success(const track * ptr) {}
|
||||||
|
virtual file_error_handler on_file_error(const track * src,track * replace) {return file_error_abort;}
|
||||||
|
virtual void on_track_change(const track * ptr) {};//called when currently played track has changed, some time after get_next_track
|
||||||
|
virtual void on_dynamic_info(const file_info * info,bool b_track_change) {}
|
||||||
|
virtual void on_exiting(bool error) {};//called when terminating (either end-of-last-track or some kind of error or stop())
|
||||||
|
|
||||||
|
//DO NOT call any playback_core methods from inside callback methods !
|
||||||
|
//all callback methods are called from playback thread, you are responsible for taking proper anti-deadlock measures (eg. dont call SendMessage() directly)
|
||||||
|
//safest way to handle end-of-playback: create a window from main thread, PostMessage to it when you get callbacks, execute callback handlers (and safely call playback_core api) from message handlers
|
||||||
|
};
|
||||||
|
|
||||||
|
class NOVTABLE vis_callback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void on_data(class audio_chunk * chunk,double timestamp)=0;//timestamp == timestamp of first sample in the chunk, according to get_cur_time_fromstart() timing
|
||||||
|
virtual void on_flush()=0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct stats
|
||||||
|
{
|
||||||
|
double decoding_position,playback_position,dsp_latency,output_latency;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//MUST be called first, immediately after creation (hint: create() does this for you)
|
||||||
|
virtual void set_callback(callback * ptr)=0;
|
||||||
|
|
||||||
|
virtual void set_vis_callback(vis_callback * ptr)=0;//optional
|
||||||
|
|
||||||
|
virtual void set_config(const playback_config * data)=0;//call before starting playback
|
||||||
|
|
||||||
|
virtual void start()=0;
|
||||||
|
virtual void stop()=0;
|
||||||
|
virtual bool is_playing()=0;
|
||||||
|
virtual bool is_finished()=0;
|
||||||
|
virtual bool is_paused()=0;
|
||||||
|
virtual void pause(int paused)=0;
|
||||||
|
|
||||||
|
virtual bool can_seek()=0;
|
||||||
|
virtual void seek(double time)=0;
|
||||||
|
virtual void seek_delta(double time_delta)=0;
|
||||||
|
|
||||||
|
virtual double get_cur_time()=0;
|
||||||
|
virtual double get_cur_time_fromstart()=0;
|
||||||
|
virtual bool get_cur_track(track * out)=0;
|
||||||
|
virtual bool get_stats(stats * out)=0;
|
||||||
|
|
||||||
|
|
||||||
|
metadb_handle * get_cur_track_handle()
|
||||||
|
{
|
||||||
|
track t;
|
||||||
|
if (get_cur_track(&t)) return t.handle;
|
||||||
|
else return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double get_total_time()
|
||||||
|
{
|
||||||
|
double rv = 0;
|
||||||
|
metadb_handle * ptr = get_cur_track_handle();
|
||||||
|
if (ptr)
|
||||||
|
{
|
||||||
|
rv = ptr->handle_get_length();
|
||||||
|
ptr->handle_release();
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static playback_core * create(callback * cb)
|
||||||
|
{
|
||||||
|
playback_core * ptr = service_enum_create_t(playback_core,0);
|
||||||
|
if (ptr) ptr->set_callback(cb);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
};
|
||||||
|
|
||||||
|
class NOVTABLE play_sound : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
USE_REPLAYGAIN = 1,
|
||||||
|
USE_DSP = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual void play(metadb_handle * file,const playback_config * settings=0)=0;//settings are optional; if non-zero, they will be passed to playback_core being used
|
||||||
|
virtual void stop_all()=0;
|
||||||
|
|
||||||
|
static play_sound * get() {return service_enum_create_t(play_sound,0);}//safe not to release
|
||||||
|
|
||||||
|
static void g_play(metadb_handle * file,const playback_config * settings)
|
||||||
|
{
|
||||||
|
play_sound * ptr = get();
|
||||||
|
if (ptr) ptr->play(file,settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void g_stop_all()
|
||||||
|
{
|
||||||
|
play_sound * ptr = get();
|
||||||
|
if (ptr) ptr->stop_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,145 @@
|
||||||
|
#include "foobar2000.h"
|
||||||
|
|
||||||
|
void playlist_oper::move_items(int delta)
|
||||||
|
{
|
||||||
|
if (delta==0) return;
|
||||||
|
|
||||||
|
int count = get_count();
|
||||||
|
int n;
|
||||||
|
|
||||||
|
mem_block_t<int> order(count);
|
||||||
|
mem_block_t<bool> selection(count);
|
||||||
|
|
||||||
|
for(n=0;n<count;n++)
|
||||||
|
order[n]=n;
|
||||||
|
|
||||||
|
get_sel_mask(selection,count);
|
||||||
|
|
||||||
|
if (delta<0)
|
||||||
|
{
|
||||||
|
for(;delta<0;delta++)
|
||||||
|
{
|
||||||
|
int idx;
|
||||||
|
for(idx=1;idx<count;idx++)
|
||||||
|
{
|
||||||
|
if (selection[idx] && !selection[idx-1])
|
||||||
|
{
|
||||||
|
order.swap(idx,idx-1);
|
||||||
|
selection.swap(idx,idx-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for(;delta>0;delta--)
|
||||||
|
{
|
||||||
|
int idx;
|
||||||
|
for(idx=count-2;idx>=0;idx--)
|
||||||
|
{
|
||||||
|
if (selection[idx] && !selection[idx+1])
|
||||||
|
{
|
||||||
|
order.swap(idx,idx+1);
|
||||||
|
selection.swap(idx,idx+1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply_order(order,count);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
void playlist_callback::on_order(const int * order,int count)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
for(n=0;n<count;n++) if (order[n]!=n) on_modified(n);
|
||||||
|
int focus = playlist_oper::get()->get_focus();
|
||||||
|
if (focus>=0 && order[focus]!=focus) on_focus_change(order[focus],focus);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
playlist_oper * playlist_oper::get()
|
||||||
|
{//since this is called really often, lets cache a pointer instead of flooding service core
|
||||||
|
static playlist_oper * g_ptr;
|
||||||
|
if (g_ptr==0) g_ptr = service_enum_create_t(playlist_oper,0);
|
||||||
|
return g_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void playlist_oper::remove_sel(bool crop)
|
||||||
|
{
|
||||||
|
bit_array_bittable mask(get_count());
|
||||||
|
get_sel_mask(mask);
|
||||||
|
if (crop) remove_mask(bit_array_not(mask));
|
||||||
|
else remove_mask(mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned playlist_switcher::find_playlist(const char * name)
|
||||||
|
{
|
||||||
|
unsigned n,max = get_num_playlists();
|
||||||
|
string8_fastalloc temp;
|
||||||
|
for(n=0;n<max;n++)
|
||||||
|
{
|
||||||
|
temp.reset();
|
||||||
|
if (get_playlist_name(n,temp))
|
||||||
|
{
|
||||||
|
if (!stricmp_utf8(temp,name)) return n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (unsigned)(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned playlist_switcher::find_or_create_playlist(const char * name)
|
||||||
|
{
|
||||||
|
unsigned idx = find_playlist(name);
|
||||||
|
if (idx==(unsigned)(-1))
|
||||||
|
{
|
||||||
|
metadb_handle_list dummy;
|
||||||
|
idx = create_playlist_fixname(string8(name),dummy);
|
||||||
|
}
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
playlist_switcher * playlist_switcher::get()
|
||||||
|
{
|
||||||
|
return service_enum_create_t(playlist_switcher,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool playlist_switcher::delete_playlist_autoswitch(unsigned idx)
|
||||||
|
{
|
||||||
|
unsigned num,active;
|
||||||
|
num = get_num_playlists();
|
||||||
|
active = get_active_playlist();
|
||||||
|
if (idx>=num || num<=1) return false;
|
||||||
|
if (idx==active)
|
||||||
|
{
|
||||||
|
if (idx+1>=num) active = idx-1;
|
||||||
|
else active = idx+1;
|
||||||
|
set_active_playlist(active);
|
||||||
|
}
|
||||||
|
return delete_playlist(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool playlist_switcher::find_or_create_playlist_activate(const char * name)
|
||||||
|
{
|
||||||
|
bool rv = false;
|
||||||
|
unsigned idx = find_or_create_playlist(name);
|
||||||
|
if (idx != (unsigned)(-1))
|
||||||
|
rv = set_active_playlist(idx);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
int playback_flow_control::get_next(int previous_index,int focus_item,int total,int advance,bool follow_focus,bool user_advance,unsigned playlist)
|
||||||
|
{
|
||||||
|
int rv = -1;
|
||||||
|
playback_flow_control_v2 * this_v2 = service_query_t(playback_flow_control_v2,this);
|
||||||
|
if (this_v2)
|
||||||
|
{
|
||||||
|
rv = this_v2->get_next(previous_index,focus_item,total,advance,follow_focus,user_advance,playlist);
|
||||||
|
this_v2->service_release();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
console::error(uStringPrintf("\"%s\" needs to be updated to 0.8 SDK or newer in order to function.",get_name()));
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
|
@ -0,0 +1,173 @@
|
||||||
|
#ifndef _PLAYLIST_H_
|
||||||
|
#define _PLAYLIST_H_
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
#include "metadb_handle.h"
|
||||||
|
#include "play_control.h"
|
||||||
|
#include "interface_helper.h"
|
||||||
|
|
||||||
|
//important: playlist engine is SINGLE-THREADED. call any APIs not from main thread and things will either blow up or refuse to work. all callbacks can be assumed to come from main thread.
|
||||||
|
|
||||||
|
|
||||||
|
//notes:
|
||||||
|
//all methods returning metadb_handle return add_ref'd handles, you have to handle_release them
|
||||||
|
//all methods taking metadb_handle will add_ref them if they store them, so it's your responsibility to later release handles you pass
|
||||||
|
|
||||||
|
class NOVTABLE playlist_oper : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//retrieving info
|
||||||
|
virtual int get_count()=0;
|
||||||
|
virtual bool get_sel(int idx)=0;//returns reference to a static object that holds state of playlist selection
|
||||||
|
virtual bool get_sel_mask(bool * out,unsigned count)=0;
|
||||||
|
virtual void get_sel_mask(bit_array_var & out);
|
||||||
|
virtual metadb_handle * get_item(int index)=0;
|
||||||
|
virtual int get_now_playing()=0;//will return -1 when not playing or currently played track isn't on playlist
|
||||||
|
virtual int get_focus()=0;
|
||||||
|
virtual int get_playback_cursor()=0;
|
||||||
|
virtual void format_title(int idx,string_base & out,const char * spec,const char * extra_items)=0;//spec may be null, will use core settings; extra items are optional
|
||||||
|
virtual void get_format_spec(string_base & out)=0;
|
||||||
|
|
||||||
|
//modifying playlist
|
||||||
|
virtual void set_sel_mask(const bit_array & sel,const bit_array & mask)=0;
|
||||||
|
virtual void remove_mask(const bit_array & items)=0;
|
||||||
|
virtual void replace_item(int idx,metadb_handle * handle)=0;
|
||||||
|
virtual void set_focus(int idx)=0;
|
||||||
|
virtual void add_items(const ptr_list_base<metadb_handle> & data,bool replace = false,int insert_idx = -1,bool select = false,bool filter = true)=0;
|
||||||
|
//data - entries to insert, replace = replace-current-playlist flag, insert_idx - where to insert (-1 will append them), select - should new items be selected or not
|
||||||
|
virtual void add_locations(const ptr_list_base<const char> & urls,bool replace = false,int insert_idx = -1,bool select = false,bool filter = true)=0;
|
||||||
|
|
||||||
|
virtual void apply_order(const int * order,int count)=0;
|
||||||
|
virtual void sort_by_format(const char * spec,int sel_only)=0;//spec==0 => randomizes
|
||||||
|
virtual void set_playback_cursor(int idx)=0;
|
||||||
|
virtual metadb_handle * playback_advance(play_control::track_command cmd)=0;//deprecated in 0.8
|
||||||
|
virtual void undo_restore()=0;
|
||||||
|
virtual void undo_reset()=0;
|
||||||
|
virtual void ensure_visible(int idx)=0;
|
||||||
|
|
||||||
|
//other
|
||||||
|
virtual void play_item(int idx)=0;
|
||||||
|
|
||||||
|
|
||||||
|
//new (0.8)
|
||||||
|
virtual bool process_locations(const ptr_list_base<const char> & urls,ptr_list_base<metadb_handle> & out,bool filter = true,const char * mask = 0)=0;
|
||||||
|
virtual bool process_dropped_files(interface IDataObject * pDataObject,ptr_list_base<metadb_handle> & out,bool filter = true)=0;
|
||||||
|
virtual bool process_dropped_files_check(interface IDataObject * pDataObject)=0;
|
||||||
|
virtual interface IDataObject * create_dataobject(const ptr_list_base<metadb_handle> & data)=0;
|
||||||
|
virtual void get_sel_items(ptr_list_base<metadb_handle> & out)=0;
|
||||||
|
virtual int get_sel_count()=0;
|
||||||
|
virtual void get_items_mask(ptr_list_base<metadb_handle> & out, const bit_array & mask)=0;
|
||||||
|
|
||||||
|
//helper
|
||||||
|
|
||||||
|
inline void add_location(const char * url,bool replace = false,int insert_idx = -1,bool select = false,bool filter = true)
|
||||||
|
{
|
||||||
|
ptr_list_t<const char> temp;
|
||||||
|
temp.add_item(url);
|
||||||
|
add_locations(temp,replace,insert_idx,select,filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void set_sel_mask(const bit_array & sel) {set_sel_mask(sel,bit_array_true());}
|
||||||
|
inline void set_sel(int idx,bool state) {set_sel_mask(bit_array_val(state),bit_array_one(idx));}
|
||||||
|
inline void remove_item(int idx) {remove_mask(bit_array_one(idx));}
|
||||||
|
inline void set_focus_sel(int idx) {set_focus(idx);set_sel_mask(bit_array_one(idx));}
|
||||||
|
inline void reset() {remove_mask(bit_array_true());}
|
||||||
|
inline void remove_all() {remove_mask(bit_array_true());}
|
||||||
|
inline void set_sel_one(int idx) {set_sel_mask(bit_array_one(idx));}
|
||||||
|
|
||||||
|
inline bool is_sel_count_greater(int v)
|
||||||
|
{
|
||||||
|
return get_sel_count()>v;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void remove_sel(bool crop);
|
||||||
|
|
||||||
|
void move_items(int delta);
|
||||||
|
|
||||||
|
static playlist_oper * get();
|
||||||
|
|
||||||
|
inline metadb_handle * get_focus_item()
|
||||||
|
{
|
||||||
|
return get_item(get_focus());//may return null
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_data(ptr_list_interface<metadb_handle> & out)
|
||||||
|
{
|
||||||
|
get_items_mask(out,bit_array_true());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//note: certain methods may get called outside initquit !
|
||||||
|
//playlist_callback methods are called by playlist engine.
|
||||||
|
class NOVTABLE playlist_callback : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void on_items_added(int start, int count)=0;//inside any of these methods, you can call playlist_oper APIs to get exact info about what happened (but only methods that read playlist state, not those that modify it)
|
||||||
|
virtual void on_order(const int * order,int count)=0;//changes selection too; doesnt actually change set of items that are selected or item having focus, just changes their order
|
||||||
|
virtual void on_items_removing(const bit_array & mask)=0;//called before actually removing them
|
||||||
|
virtual void on_items_removed(const bit_array & mask)=0;//note: items being removed are de-selected first always
|
||||||
|
virtual void on_sel_change(int idx,bool state)=0;
|
||||||
|
virtual void on_sel_change_multi(const bit_array & before,const bit_array & after,int count)=0;
|
||||||
|
virtual void on_focus_change(int from,int to)=0;//focus may be -1 when no item has focus; reminder: focus may also change on other callbacks
|
||||||
|
virtual void on_modified(int idx)=0;
|
||||||
|
virtual void on_replaced(int idx) {on_modified(idx);}
|
||||||
|
virtual void on_ensure_visible(int idx) {}
|
||||||
|
virtual void on_default_format_changed() {}
|
||||||
|
//note: if you cache strings or whatever, make sure to flush cache when total item count changes, because stuff like %_playlist_total% may change as well (not to mention %_playlist_number% everywhere)
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
};
|
||||||
|
|
||||||
|
class NOVTABLE playback_flow_control : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual const char * get_name()=0;
|
||||||
|
|
||||||
|
virtual int get_next(int previous_index,int focus_item,int total,int advance,bool follow_focus,bool user_advance) {return -1;}//deprecated
|
||||||
|
|
||||||
|
int get_next(int previous_index,int focus_item,int total,int advance,bool follow_focus,bool user_advance,unsigned playlist);
|
||||||
|
|
||||||
|
//int advance : -1 for prev, 0 for play, 1 for next
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
|
||||||
|
virtual service_base * service_query(const GUID & guid)
|
||||||
|
{
|
||||||
|
if (guid == get_class_guid()) {service_add_ref();return this;}
|
||||||
|
else return service_base::service_query(guid);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class NOVTABLE playback_flow_control_v2 : public playback_flow_control
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual int get_next(int previous_index,int focus_item,int total,int advance,bool follow_focus,bool user_advance,unsigned playlist)=0;
|
||||||
|
//previous_index and focus_item may be -1; return -1 to stop
|
||||||
|
//IMPORTANT (changed in 0.8): you are in playlist indicated by <unsigned playlist>, use playlist_switcher methods to access data
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
|
||||||
|
virtual service_base * service_query(const GUID & guid)
|
||||||
|
{
|
||||||
|
if (guid == get_class_guid()) {service_add_ref();return this;}
|
||||||
|
else return playback_flow_control::service_query(guid);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class playback_flow_control_factory : public service_factory_single_t<playback_flow_control,T> {};
|
||||||
|
|
||||||
|
//helper code for manipulating configuration of playback flow control (repeat/shuffle/whatever) => see ../helpers/playback_order_helper.h
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,15 @@
|
||||||
|
#ifndef _PLAYLIST_ENTRY_H_
|
||||||
|
#define _PLAYLIST_ENTRY_H_
|
||||||
|
|
||||||
|
//DEPRECATED (or rather, renamed to playable_location to prevent further confusion with playlist-related operations)
|
||||||
|
|
||||||
|
#include "playable_location.h"
|
||||||
|
|
||||||
|
//backwards-compatibility defines
|
||||||
|
|
||||||
|
#define playlist_entry playable_location
|
||||||
|
#define playlist_entry_i playable_location_i
|
||||||
|
#define make_playlist_entry make_playable_location
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,220 @@
|
||||||
|
#include "foobar2000.h"
|
||||||
|
|
||||||
|
int playlist_loader::load_playlist(const char * p_filename,playlist_loader_callback * callback)
|
||||||
|
{
|
||||||
|
TRACK_CALL_TEXT("playlist_loader::load_playlist");
|
||||||
|
string8 filename = file_path_canonical(p_filename);
|
||||||
|
service_enum_t<playlist_loader> e;
|
||||||
|
playlist_loader * l;
|
||||||
|
|
||||||
|
string_extension_8 extension(filename);
|
||||||
|
|
||||||
|
bool found = 0;
|
||||||
|
reader * r = 0;
|
||||||
|
|
||||||
|
for(l=e.first();l && !found;l = e.next())
|
||||||
|
{
|
||||||
|
if (!stricmp_utf8(l->get_extension(),extension))
|
||||||
|
{
|
||||||
|
found = 1;
|
||||||
|
if (r==0)
|
||||||
|
r = file::g_open(filename,reader::MODE_READ);
|
||||||
|
if (r)
|
||||||
|
{
|
||||||
|
r->seek(0);
|
||||||
|
int rv=l->open(filename,r,callback);
|
||||||
|
if (!rv) found=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l->service_release();
|
||||||
|
}
|
||||||
|
if (r) r->reader_release();
|
||||||
|
return found ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void playlist_loader::process_path(const char * p_filename,playlist_loader_callback * callback,playlist_loader_callback::entry_type type)
|
||||||
|
{
|
||||||
|
TRACK_CALL_TEXT("playlist_loader::process_path");
|
||||||
|
file_path_canonical filename(p_filename);
|
||||||
|
if (!callback->on_progress(0)) return;
|
||||||
|
|
||||||
|
{
|
||||||
|
directory_callback_i dir_list;
|
||||||
|
if (directory::g_list(filename,&dir_list,callback))
|
||||||
|
{
|
||||||
|
unsigned n;
|
||||||
|
for(n=0;n<dir_list.get_count();n++)
|
||||||
|
{
|
||||||
|
const char * param = dir_list[n];
|
||||||
|
process_path(param,callback,playlist_loader_callback::DIRECTORY_ENUMERATED);
|
||||||
|
if (!callback->on_progress(0)) break;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
|
||||||
|
service_enum_t<archive> e;
|
||||||
|
archive * d;
|
||||||
|
for(d=e.first();d;d=e.next())
|
||||||
|
{
|
||||||
|
int rv;
|
||||||
|
rv = d->list(filename,&dir_list,callback);
|
||||||
|
|
||||||
|
if (rv)
|
||||||
|
{
|
||||||
|
unsigned n;
|
||||||
|
for(n=0;n<dir_list.get_count();n++)
|
||||||
|
{
|
||||||
|
const char * param = dir_list[n];
|
||||||
|
process_path(param,callback,playlist_loader_callback::DIRECTORY_ENUMERATED);
|
||||||
|
if (!callback->on_progress(0)) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!callback->on_progress(0)) break;
|
||||||
|
d->precache(filename,callback);
|
||||||
|
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
d->service_release();
|
||||||
|
if (found) break;
|
||||||
|
|
||||||
|
if (!callback->on_progress(0)) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found || !callback->on_progress(0)) return;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (input::g_test_filename(filename))
|
||||||
|
{
|
||||||
|
track_indexer::g_get_tracks_wrap(filename,callback,type,0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int playlist_loader::save_playlist(const char * p_filename,const ptr_list_base<metadb_handle> & data)
|
||||||
|
{
|
||||||
|
TRACK_CALL_TEXT("playlist_loader::save_playlist");
|
||||||
|
string8 filename = file_path_canonical(p_filename);
|
||||||
|
reader * r = file::g_open(filename,reader::MODE_WRITE_NEW);
|
||||||
|
if (!r) return 0;
|
||||||
|
int rv = 0;
|
||||||
|
|
||||||
|
string_extension ext(filename);
|
||||||
|
|
||||||
|
service_enum_t<playlist_loader> e;
|
||||||
|
playlist_loader * l;
|
||||||
|
for(l=e.first();l;l = e.next())
|
||||||
|
{
|
||||||
|
if (l->can_write() && !stricmp_utf8(ext,l->get_extension()) && l->write(filename,r,data))
|
||||||
|
{
|
||||||
|
rv=1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
l->service_release();
|
||||||
|
}
|
||||||
|
|
||||||
|
r->reader_release();
|
||||||
|
|
||||||
|
if (!rv) file::g_remove(filename);
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
int track_indexer::g_get_tracks_callback(const char * filename,callback * out,reader * r)
|
||||||
|
{
|
||||||
|
TRACK_CALL_TEXT("track_indexer::g_get_tracks_callback");
|
||||||
|
service_enum_t<track_indexer> e;
|
||||||
|
track_indexer * t;
|
||||||
|
|
||||||
|
for(t=e.first();t;t = e.next())
|
||||||
|
{
|
||||||
|
int rv;
|
||||||
|
rv = t->get_tracks(filename,out,r);
|
||||||
|
t->service_release();
|
||||||
|
if (rv>0) return rv;
|
||||||
|
}
|
||||||
|
out->on_entry(make_playable_location(filename,0));//assume single track if noone reports
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int track_indexer::get_tracks_ex(const char * filename,ptr_list_base<metadb_handle> & out,reader * r)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
track_indexer_v2 * this_v2 = service_query_t(track_indexer_v2,this);
|
||||||
|
if (this_v2)
|
||||||
|
{
|
||||||
|
int rv = this_v2->get_tracks_ex(filename,out,r);
|
||||||
|
this_v2->service_release();
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return get_tracks(filename,&callback_i_metadb(out),r);
|
||||||
|
}
|
||||||
|
|
||||||
|
int track_indexer::g_get_tracks_ex(const char * filename,ptr_list_base<metadb_handle> & out,reader * r)
|
||||||
|
{
|
||||||
|
TRACK_CALL_TEXT("track_indexer::g_get_tracks_ex");
|
||||||
|
service_enum_t<track_indexer> e;
|
||||||
|
track_indexer * t;
|
||||||
|
|
||||||
|
for(t=e.first();t;t = e.next())
|
||||||
|
{
|
||||||
|
int rv;
|
||||||
|
rv = t->get_tracks_ex(filename,out,r);
|
||||||
|
t->service_release();
|
||||||
|
if (rv>0) return rv;
|
||||||
|
}
|
||||||
|
out.add_item(metadb::get()->handle_create(make_playable_location(filename,0)));//assume single track if noone reports
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int track_indexer::g_get_tracks_wrap(const char * filename,playlist_loader_callback * out,playlist_loader_callback::entry_type type,reader * r)
|
||||||
|
{
|
||||||
|
service_enum_t<track_indexer> e;
|
||||||
|
track_indexer * ptr;
|
||||||
|
for(ptr=e.first();ptr;ptr=e.next())
|
||||||
|
{
|
||||||
|
int rv = 0;
|
||||||
|
track_indexer_v2 * ptr_v2 = service_query_t(track_indexer_v2,ptr);
|
||||||
|
if (ptr_v2)
|
||||||
|
{
|
||||||
|
metadb_handle_list temp;
|
||||||
|
rv = ptr_v2->get_tracks_ex(filename,temp,r);
|
||||||
|
ptr_v2->service_release();
|
||||||
|
if (rv>0)
|
||||||
|
{
|
||||||
|
unsigned n, m = temp.get_count();
|
||||||
|
for(n=0;n<m;n++) out->on_entry_dbhandle(temp[n],type);
|
||||||
|
}
|
||||||
|
temp.delete_all();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rv = ptr->get_tracks(filename,&callback_i_wrap(out,type),r);
|
||||||
|
}
|
||||||
|
ptr->service_release();
|
||||||
|
if (rv>0) return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
out->on_entry(make_playable_location(filename,0),type);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool playlist_loader::is_our_content_type(const char * param)
|
||||||
|
{
|
||||||
|
bool rv = false;
|
||||||
|
playlist_loader_v2 * this_v2 = service_query_t(playlist_loader_v2,this);
|
||||||
|
if (this_v2)
|
||||||
|
{
|
||||||
|
rv = this_v2->is_our_content_type(param);
|
||||||
|
this_v2->service_release();
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
|
@ -0,0 +1,188 @@
|
||||||
|
#ifndef _PLAYLIST_LOADER_H_
|
||||||
|
#define _PLAYLIST_LOADER_H_
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
|
||||||
|
#include "reader.h"
|
||||||
|
|
||||||
|
#include "metadb.h"
|
||||||
|
|
||||||
|
class NOVTABLE playlist_loader_callback //receives new playlist entries from playlists/filesystem/whatever
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum entry_type
|
||||||
|
{
|
||||||
|
USER_REQUESTED,
|
||||||
|
DIRECTORY_ENUMERATED,
|
||||||
|
FROM_PLAYLIST,
|
||||||
|
};
|
||||||
|
virtual int on_progress(const char * path) {return 1;}
|
||||||
|
//for monitoring progress only, return 1 to continue, 0 to abort; note that you will most likely get called a few times after first 0 return
|
||||||
|
//path might be null if playlist loader is just checking if you are aborting or not
|
||||||
|
|
||||||
|
virtual void on_entry(const playable_location * src,entry_type type)=0;
|
||||||
|
virtual void on_entry_have_info(const file_info * src,entry_type type) {on_entry(src->get_location(),type);}
|
||||||
|
virtual void on_entry_dbhandle(metadb_handle * ptr,entry_type type) {on_entry(ptr->handle_get_location(),type);}
|
||||||
|
};
|
||||||
|
|
||||||
|
class playlist_loader_callback_i : public playlist_loader_callback
|
||||||
|
{
|
||||||
|
metadb_handle_list data;
|
||||||
|
public:
|
||||||
|
|
||||||
|
~playlist_loader_callback_i() {data.delete_all();}
|
||||||
|
|
||||||
|
metadb_handle * get_item(unsigned idx) {return data[idx];}
|
||||||
|
metadb_handle * operator[](unsigned idx) {return data[idx];}
|
||||||
|
unsigned get_count() {return data.get_count();}
|
||||||
|
|
||||||
|
const metadb_handle_list & get_data() {return data;}
|
||||||
|
|
||||||
|
virtual int on_progress(const char * path) {return 1;}
|
||||||
|
virtual void on_entry(const playable_location * src,entry_type type)
|
||||||
|
{data.add_item(metadb::get()->handle_create(src));}
|
||||||
|
virtual void on_entry_have_info(const file_info * src,entry_type type)
|
||||||
|
{data.add_item(metadb::get()->handle_create_hint(src));}
|
||||||
|
virtual void on_entry_dbhandle(metadb_handle * ptr,entry_type type)
|
||||||
|
{data.add_item(ptr->handle_duplicate());}
|
||||||
|
};
|
||||||
|
|
||||||
|
class NOVTABLE playlist_loader : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual int open(const char * filename, reader * r,playlist_loader_callback * callback)=0; //send new entries to callback
|
||||||
|
virtual int write(const char * filename, reader * r,const ptr_list_base<metadb_handle> & data)=0;
|
||||||
|
virtual const char * get_extension()=0;
|
||||||
|
virtual bool can_write()=0;
|
||||||
|
bool is_our_content_type(const char*);
|
||||||
|
|
||||||
|
static int load_playlist(const char * filename,playlist_loader_callback * callback);
|
||||||
|
//attempts to load file as a playlist, return 0 on failure
|
||||||
|
|
||||||
|
static int save_playlist(const char * filename,const ptr_list_base<metadb_handle> & data);
|
||||||
|
//saves playlist into file
|
||||||
|
|
||||||
|
static void process_path(const char * filename,playlist_loader_callback * callback,playlist_loader_callback::entry_type type = playlist_loader_callback::USER_REQUESTED);
|
||||||
|
//adds contents of filename (can be directory) to playlist, doesnt load playlists
|
||||||
|
|
||||||
|
//this helper also loads playlists
|
||||||
|
//return 1 if loaded as playlist, 0 if loaded as files
|
||||||
|
static int process_path_ex(const char * filename,playlist_loader_callback * callback,playlist_loader_callback::entry_type type = playlist_loader_callback::USER_REQUESTED)
|
||||||
|
{
|
||||||
|
if (!load_playlist(filename,callback))
|
||||||
|
{
|
||||||
|
process_path(filename,callback,type);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
};
|
||||||
|
|
||||||
|
class NOVTABLE playlist_loader_v2 : public playlist_loader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual bool is_our_content_type(const char * param) = 0;
|
||||||
|
|
||||||
|
virtual service_base * service_query(const GUID & guid)
|
||||||
|
{
|
||||||
|
if (guid == get_class_guid()) {service_add_ref();return this;}
|
||||||
|
else return service_base::service_query(guid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
};
|
||||||
|
|
||||||
|
class NOVTABLE track_indexer : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
class NOVTABLE callback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void on_entry(const playable_location * src)=0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class callback_i : public callback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ptr_list_t<playable_location_i> data;
|
||||||
|
virtual void on_entry(const playable_location * src) {data.add_item(new playable_location_i(*src));}
|
||||||
|
~callback_i() {data.delete_all();}
|
||||||
|
};
|
||||||
|
|
||||||
|
class callback_i_ref : public callback
|
||||||
|
{
|
||||||
|
ptr_list_t<playable_location_i> &data;
|
||||||
|
public:
|
||||||
|
virtual void on_entry(const playable_location * src) {data.add_item(new playable_location_i(*src));}
|
||||||
|
callback_i_ref(ptr_list_t<playable_location_i> &p_data) : data(p_data) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class callback_i_wrap : public callback
|
||||||
|
{
|
||||||
|
playlist_loader_callback * out;
|
||||||
|
playlist_loader_callback::entry_type type;
|
||||||
|
public:
|
||||||
|
virtual void on_entry(const playable_location * src) {out->on_entry(src,type);}
|
||||||
|
callback_i_wrap(playlist_loader_callback * p_out,playlist_loader_callback::entry_type p_type) : out(p_out), type(p_type) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class callback_i_metadb : public callback
|
||||||
|
{
|
||||||
|
ptr_list_base<metadb_handle> & out;
|
||||||
|
metadb * p_metadb;
|
||||||
|
public:
|
||||||
|
virtual void on_entry(const playable_location * src) {out.add_item(p_metadb->handle_create(src));}
|
||||||
|
callback_i_metadb(ptr_list_base<metadb_handle> & p_out) : out(p_out), p_metadb(metadb::get()) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual int get_tracks(const char * filename,callback* out,reader * r)=0;//return 0 on failure / not our file, 1 on success; send your subsongs to out; reader CAN BE NULL, if so, create/destroy your own one
|
||||||
|
//return either playlist entries with same filename, or playlist entries belonging to a readerless input (eg. cdda://)
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
|
||||||
|
static int g_get_tracks_callback(const char * filename,callback * out,reader * r=0);
|
||||||
|
static int g_get_tracks(const char * filename,ptr_list_t<playable_location_i> & out,reader * r=0)
|
||||||
|
{
|
||||||
|
return g_get_tracks_callback(filename,&callback_i_ref(out),r);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int g_get_tracks_wrap(const char * filename,playlist_loader_callback * out,playlist_loader_callback::entry_type type,reader * r=0);
|
||||||
|
int get_tracks_ex(const char * filename,ptr_list_base<metadb_handle> & out,reader * r = 0);
|
||||||
|
static int g_get_tracks_ex(const char * filename,ptr_list_base<metadb_handle> & out,reader * r = 0);
|
||||||
|
|
||||||
|
virtual service_base * service_query(const GUID & guid)
|
||||||
|
{
|
||||||
|
if (guid == get_class_guid()) {service_add_ref();return this;}
|
||||||
|
else return service_base::service_query(guid);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class NOVTABLE track_indexer_v2 : public track_indexer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
|
||||||
|
//since you usually have to parse the damn file anyway, here's a method that lets you precache info to database already to avoid redundant file operations later
|
||||||
|
virtual int get_tracks_ex(const char * filename,ptr_list_base<metadb_handle> & out,reader * r = 0)=0;//return 0 on failure / not our file, 1 on success; send your subsongs to out; reader CAN BE NULL, if so, create/destroy your own one
|
||||||
|
|
||||||
|
virtual service_base * service_query(const GUID & guid)
|
||||||
|
{
|
||||||
|
if (guid == get_class_guid()) {service_add_ref();return this;}
|
||||||
|
else return track_indexer::service_query(guid);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class track_indexer_factory : public service_factory_t<track_indexer,T> {};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //_PLAYLIST_LOADER_H_
|
|
@ -0,0 +1,66 @@
|
||||||
|
#ifndef _FOOBAR2000_PLAYLIST_SWITCHER_
|
||||||
|
#define _FOOBAR2000_PLAYLIST_SWITCHER_
|
||||||
|
|
||||||
|
|
||||||
|
//all calls from main app thread only !
|
||||||
|
|
||||||
|
class NOVTABLE playlist_switcher : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
|
||||||
|
virtual unsigned get_num_playlists()=0;
|
||||||
|
virtual bool get_playlist_name(unsigned idx,string_base & out)=0;
|
||||||
|
virtual bool get_playlist_data(unsigned idx,ptr_list_interface<metadb_handle> & out)=0;
|
||||||
|
virtual unsigned get_playlist_size(unsigned idx)=0;//returns number of entries
|
||||||
|
virtual bool get_playlist_selection_mask(unsigned idx,bit_array_var & out)=0;
|
||||||
|
virtual unsigned get_active_playlist()=0;
|
||||||
|
|
||||||
|
virtual bool set_active_playlist(unsigned idx)=0;
|
||||||
|
virtual unsigned create_playlist(const char * name,const ptr_list_interface<metadb_handle> & data)=0;//returns index (or 0 on failure; index is always >0 because there is always at least one playlist present)
|
||||||
|
virtual unsigned create_playlist_fixname(string_base & name,const ptr_list_interface<metadb_handle> & data)=0;//name may change when playlist of this name is already present or passed name contains invalid chars; returns index (or 0 on failure; index is always >0 because there is always at least one playlist present)
|
||||||
|
virtual bool delete_playlist(unsigned idx)=0;//will fail if you are trying to delete only playlist, or currently active playlist
|
||||||
|
virtual bool reorder(const int * order,unsigned count)=0;
|
||||||
|
virtual bool rename_playlist(unsigned idx,const char * new_name)=0;//will fail if name exists or contains invalid chars
|
||||||
|
virtual bool rename_playlist_fixname(unsigned idx,string_base & name)=0;//will attempt to correct the name before failing
|
||||||
|
|
||||||
|
virtual metadb_handle * get_item(unsigned playlist,unsigned idx)=0;
|
||||||
|
virtual bool format_title(unsigned playlist,unsigned idx,string_base & out,const char * spec,const char * extra_items)=0;//spec may be null, will use core settings; extra items are optional
|
||||||
|
|
||||||
|
//new (0.8)
|
||||||
|
virtual unsigned get_playing_playlist()=0;
|
||||||
|
virtual metadb_handle * playback_advance(play_control::track_command cmd,unsigned * p_playlist,unsigned * p_item,bool * b_is_active_playlist)=0;//deprecated in 0.8
|
||||||
|
virtual void reset_playing_playlist()=0;
|
||||||
|
virtual void highlight_playing_item()=0;
|
||||||
|
|
||||||
|
static playlist_switcher * get();
|
||||||
|
|
||||||
|
unsigned find_playlist(const char * name);//returns ((unsigned)-1) when not found
|
||||||
|
unsigned find_or_create_playlist(const char * name);
|
||||||
|
bool find_or_create_playlist_activate(const char * name);
|
||||||
|
bool delete_playlist_autoswitch(unsigned idx);
|
||||||
|
};
|
||||||
|
|
||||||
|
class playlist_switcher_callback : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
|
||||||
|
virtual void on_playlist_switch_before(unsigned from,unsigned to)=0;//called before switching
|
||||||
|
virtual void on_playlist_switch_after(unsigned from,unsigned to)=0;//called after switching
|
||||||
|
|
||||||
|
virtual void on_reorder(const int * order,unsigned count)=0;
|
||||||
|
virtual void on_new_playlist(const char * name,unsigned idx,const ptr_list_interface<metadb_handle> & data)=0;
|
||||||
|
virtual void on_delete_playlist(unsigned idx)=0;
|
||||||
|
virtual void on_rename_playlist(unsigned idx,const char * new_name)=0;
|
||||||
|
|
||||||
|
virtual void on_item_replaced(unsigned pls,unsigned item,metadb_handle * from,metadb_handle * to)=0;//workaround for inactive-playlist-needs-modification-when-using-masstagger-autorename; does not work for active playlist !
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class playlist_switcher_callback_factory : public service_factory_single_t<playlist_switcher_callback,T> {};
|
||||||
|
|
||||||
|
|
||||||
|
#endif//_FOOBAR2000_PLAYLIST_SWITCHER_
|
|
@ -0,0 +1,709 @@
|
||||||
|
#include "foobar2000.h"
|
||||||
|
|
||||||
|
|
||||||
|
reader * unpacker::g_open(reader * p)
|
||||||
|
{
|
||||||
|
service_enum_t<unpacker> e;
|
||||||
|
unpacker * ptr;
|
||||||
|
for(ptr=e.first();ptr;ptr=e.next())
|
||||||
|
{
|
||||||
|
p->seek(0);
|
||||||
|
reader * r = ptr->open(p);
|
||||||
|
ptr->service_release();
|
||||||
|
if (r) return r;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool reader::seek2(__int64 position,int mode)
|
||||||
|
{
|
||||||
|
switch(mode)
|
||||||
|
{
|
||||||
|
case SEEK_CUR:
|
||||||
|
{
|
||||||
|
__int64 delta = get_position();
|
||||||
|
if (delta<0) return false;
|
||||||
|
position+=delta;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SEEK_END:
|
||||||
|
{
|
||||||
|
__int64 delta = get_length();
|
||||||
|
if (delta<0) return false;
|
||||||
|
position+=delta;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return seek(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
__int64 reader::transfer(reader * src,reader * dst,__int64 bytes)
|
||||||
|
{
|
||||||
|
enum{BUFSIZE = 1024*1024};
|
||||||
|
mem_block temp;
|
||||||
|
void* ptr = temp.set_size((int)(BUFSIZE<bytes ? BUFSIZE : bytes));
|
||||||
|
__int64 done = 0;
|
||||||
|
while(done<bytes)
|
||||||
|
{
|
||||||
|
__int64 delta = bytes-done;
|
||||||
|
if (delta>BUFSIZE) delta = BUFSIZE;
|
||||||
|
delta = src->read(ptr,(int)delta);
|
||||||
|
if (delta<=0) break;
|
||||||
|
delta = dst->write(ptr,(int)delta);
|
||||||
|
if (delta<=0) break;
|
||||||
|
done += delta;
|
||||||
|
|
||||||
|
}
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void file::g_get_canonical_path(const char * path,string_base & out)
|
||||||
|
{
|
||||||
|
service_enum_t<file> e;
|
||||||
|
file * ptr;
|
||||||
|
for(ptr=e.first();ptr;ptr=e.next())
|
||||||
|
{
|
||||||
|
int rv = ptr->get_canonical_path(path,out);
|
||||||
|
ptr->service_release();
|
||||||
|
if (rv) return;
|
||||||
|
}
|
||||||
|
//fucko: noone wants to process this, lets copy over
|
||||||
|
out.set_string(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void file::g_get_display_path(const char * path,string_base & out)
|
||||||
|
{
|
||||||
|
file * ptr = g_get_interface(path);
|
||||||
|
if (ptr==0)
|
||||||
|
{
|
||||||
|
//fucko: noone wants to process this, lets copy over
|
||||||
|
out.set_string(path);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!ptr->get_display_path(path,out))
|
||||||
|
out.set_string(path);
|
||||||
|
ptr->service_release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file * file::g_get_interface(const char * path)
|
||||||
|
{
|
||||||
|
service_enum_t<file> e;
|
||||||
|
file * ptr;
|
||||||
|
for(ptr=e.first();ptr;ptr=e.next())
|
||||||
|
{
|
||||||
|
if (ptr->is_our_path(path))
|
||||||
|
return ptr;
|
||||||
|
ptr->service_release();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
reader * file::g_get_reader(const char * path)
|
||||||
|
{
|
||||||
|
file * ptr = g_get_interface(path);
|
||||||
|
if (ptr==0) return 0;
|
||||||
|
reader * r = ptr->get_reader(path);
|
||||||
|
ptr->service_release();
|
||||||
|
if (r==0) return 0;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
reader * file::g_open(const char * path,reader::MODE mode)
|
||||||
|
{
|
||||||
|
string8 path_c;
|
||||||
|
g_get_canonical_path(path,path_c);
|
||||||
|
file * ptr = g_get_interface(path_c);
|
||||||
|
if (ptr==0) return 0;
|
||||||
|
reader * r = ptr->get_reader(path_c);
|
||||||
|
ptr->service_release();
|
||||||
|
if (r!=0)
|
||||||
|
{
|
||||||
|
if (r->open(path_c,mode)==0)
|
||||||
|
{
|
||||||
|
r->reader_release();
|
||||||
|
r=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
int file::g_exists(const char * path)
|
||||||
|
{
|
||||||
|
string8 path_c;
|
||||||
|
g_get_canonical_path(path,path_c);
|
||||||
|
file * ptr = g_get_interface(path_c);
|
||||||
|
if (ptr==0) return 0;
|
||||||
|
int rv = ptr->exists(path_c);
|
||||||
|
ptr->service_release();
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
int file::g_remove(const char * path)
|
||||||
|
{
|
||||||
|
string8 path_c;
|
||||||
|
g_get_canonical_path(path,path_c);
|
||||||
|
file * ptr = g_get_interface(path_c);
|
||||||
|
if (ptr==0) return 0;
|
||||||
|
int rv = ptr->remove(path_c);
|
||||||
|
ptr->service_release();
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
int file::g_create_directory(const char * path)
|
||||||
|
{
|
||||||
|
string8 path_c;
|
||||||
|
g_get_canonical_path(path,path_c);
|
||||||
|
file * ptr = g_get_interface(path_c);
|
||||||
|
if (ptr==0) return 0;
|
||||||
|
int rv = ptr->create_directory(path_c);
|
||||||
|
ptr->service_release();
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
int file::g_move(const char * src,const char * dst)
|
||||||
|
{
|
||||||
|
service_enum_t<file> e;
|
||||||
|
file * ptr;
|
||||||
|
int rv = 0;
|
||||||
|
for(ptr=e.first();ptr;ptr=e.next())
|
||||||
|
{
|
||||||
|
if (ptr->is_our_path(src) && ptr->is_our_path(dst))
|
||||||
|
{
|
||||||
|
rv = ptr->move(src,dst);
|
||||||
|
}
|
||||||
|
ptr->service_release();
|
||||||
|
if (rv) break;
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
int file::g_move_ex(const char * _src,const char * _dst)
|
||||||
|
{
|
||||||
|
string8 src,dst;
|
||||||
|
g_get_canonical_path(_src,src);
|
||||||
|
g_get_canonical_path(_dst,dst);
|
||||||
|
|
||||||
|
service_enum_t<file> e;
|
||||||
|
file * ptr;
|
||||||
|
int rv = 0;
|
||||||
|
for(ptr=e.first();ptr;ptr=e.next())
|
||||||
|
{
|
||||||
|
if (ptr->is_our_path(src) && ptr->is_our_path(dst))
|
||||||
|
{
|
||||||
|
rv = ptr->move(src,dst);
|
||||||
|
}
|
||||||
|
ptr->service_release();
|
||||||
|
if (rv) break;
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
int directory::g_list(const char * path,directory_callback * out,playlist_loader_callback * callback,bool b_recur)
|
||||||
|
{
|
||||||
|
service_enum_t<directory> e;
|
||||||
|
directory * d;
|
||||||
|
for(d=e.first();d;d=e.next())
|
||||||
|
{
|
||||||
|
int rv;
|
||||||
|
rv = d->list(path,out,callback,b_recur);
|
||||||
|
d->service_release();
|
||||||
|
if (rv) return 1;
|
||||||
|
if (callback && !callback->on_progress(0)) break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void path_pack_string(string8 & out,const char * src)
|
||||||
|
{
|
||||||
|
out.add_char('|');
|
||||||
|
out.add_int(strlen(src));
|
||||||
|
out.add_char('|');
|
||||||
|
out.add_string(src);
|
||||||
|
out.add_char('|');
|
||||||
|
}
|
||||||
|
|
||||||
|
static int path_unpack_string(string8 & out,const char * src)
|
||||||
|
{
|
||||||
|
int ptr=0;
|
||||||
|
if (src[ptr++]!='|') return -1;
|
||||||
|
int len = atoi(src+ptr);
|
||||||
|
if (len<=0) return -1;
|
||||||
|
while(src[ptr]!=0 && src[ptr]!='|') ptr++;
|
||||||
|
if (src[ptr]!='|') return -1;
|
||||||
|
ptr++;
|
||||||
|
int start = ptr;
|
||||||
|
while(ptr-start<len)
|
||||||
|
{
|
||||||
|
if (src[ptr]==0) return -1;
|
||||||
|
ptr++;
|
||||||
|
}
|
||||||
|
if (src[ptr]!='|') return -1;
|
||||||
|
out.add_string_n(&src[start],len);
|
||||||
|
ptr++;
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
reader * file::g_open_precache(const char * path)
|
||||||
|
{
|
||||||
|
string8 path_c;
|
||||||
|
g_get_canonical_path(path,path_c);
|
||||||
|
file * ptr = g_get_interface(path_c);
|
||||||
|
if (ptr==0) return 0;
|
||||||
|
if (ptr->dont_read_infos(path))
|
||||||
|
{
|
||||||
|
ptr->service_release();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
reader * r = ptr->get_reader(path_c);
|
||||||
|
ptr->service_release();
|
||||||
|
if (r!=0)
|
||||||
|
{
|
||||||
|
if (r->open(path_c,reader::MODE_READ)==0)
|
||||||
|
{
|
||||||
|
r->reader_release();
|
||||||
|
r=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
int file::g_dont_read_infos(const char * path)
|
||||||
|
{
|
||||||
|
int rv = -1;
|
||||||
|
file * ptr = g_get_interface(path);
|
||||||
|
if (ptr)
|
||||||
|
{
|
||||||
|
rv = ptr->dont_read_infos(path);
|
||||||
|
ptr->service_release();
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool reader_backbuffer_wrap::seek(__int64 position)
|
||||||
|
{
|
||||||
|
if (aborting) return false;
|
||||||
|
if (position>reader_pos)
|
||||||
|
{
|
||||||
|
if (position > reader_pos + buffer.get_size())
|
||||||
|
{
|
||||||
|
reader_pos = position - buffer.get_size();
|
||||||
|
m_reader->seek(reader_pos);
|
||||||
|
buffered = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
read_pos = reader_pos;
|
||||||
|
__int64 skip = (position-reader_pos);
|
||||||
|
char temp[256];
|
||||||
|
while(skip>0)
|
||||||
|
{
|
||||||
|
__int64 delta = sizeof(temp);
|
||||||
|
if (delta>skip) delta=skip;
|
||||||
|
if (read(temp,(int)delta)!=delta) return false;
|
||||||
|
skip-=delta;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (reader_pos-position>(__int64)buffered)
|
||||||
|
{
|
||||||
|
read_pos = reader_pos = position;
|
||||||
|
m_reader->seek(reader_pos);
|
||||||
|
buffered = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
read_pos = position;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int file::g_relative_path_create(const char * file_path,const char * playlist_path,string_base & out)
|
||||||
|
{
|
||||||
|
file * ptr = g_get_interface(file_path);
|
||||||
|
int rv = 0;
|
||||||
|
if (ptr)
|
||||||
|
{
|
||||||
|
rv = ptr->relative_path_create(file_path,playlist_path,out);
|
||||||
|
ptr->service_release();
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
int file::g_relative_path_parse(const char * relative_path,const char * playlist_path,string_base & out)
|
||||||
|
{
|
||||||
|
service_enum_t<file> e;
|
||||||
|
file * ptr;
|
||||||
|
for(ptr=e.first();ptr;ptr=e.next())
|
||||||
|
{
|
||||||
|
int rv = ptr->relative_path_parse(relative_path,playlist_path,out);
|
||||||
|
ptr->service_release();
|
||||||
|
if (rv) return rv;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int archive_i::get_canonical_path(const char * path,string_base & out)
|
||||||
|
{
|
||||||
|
if (is_our_path(path))
|
||||||
|
{
|
||||||
|
out.set_string(path);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int archive_i::is_our_path(const char * path)
|
||||||
|
{
|
||||||
|
if (strncmp(path,"unpack://",9)) return 0;
|
||||||
|
const char * type = get_archive_type();
|
||||||
|
path += 9;
|
||||||
|
while(*type)
|
||||||
|
{
|
||||||
|
if (*type!=*path) return 0;
|
||||||
|
type++;
|
||||||
|
path++;
|
||||||
|
}
|
||||||
|
if (*path!='|') return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int archive_i::get_display_path(const char * path,string_base & out)
|
||||||
|
{
|
||||||
|
string8 archive,file;
|
||||||
|
if (parse_unpack_path(path,archive,file))
|
||||||
|
{
|
||||||
|
g_get_display_path(archive,out);
|
||||||
|
out.add_string("|");
|
||||||
|
out.add_string(file);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int archive_i::exists(const char * path)
|
||||||
|
{
|
||||||
|
string8 archive,file;
|
||||||
|
if (parse_unpack_path(path,archive,file))
|
||||||
|
{
|
||||||
|
if (g_exists(archive))
|
||||||
|
{
|
||||||
|
if (g_dont_read_infos(archive)) return 1;
|
||||||
|
else return exists_in_archive(archive,file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int archive_i::remove(const char * path)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int archive_i::move(const char * src,const char * dst)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int archive_i::dont_read_infos(const char * src)
|
||||||
|
{
|
||||||
|
string8 archive,file;
|
||||||
|
if (parse_unpack_path(src,archive,file))
|
||||||
|
{
|
||||||
|
return g_dont_read_infos(archive);
|
||||||
|
}
|
||||||
|
else return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int archive_i::relative_path_create(const char * file_path,const char * playlist_path,string_base & out)
|
||||||
|
{
|
||||||
|
string8 archive,file;
|
||||||
|
if (parse_unpack_path(file_path,archive,file))
|
||||||
|
{
|
||||||
|
string8 archive_rel;
|
||||||
|
if (g_relative_path_create(archive,playlist_path,archive_rel))
|
||||||
|
{
|
||||||
|
string8 out_path;
|
||||||
|
make_unpack_path(out_path,archive_rel,file);
|
||||||
|
out.set_string(out_path);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int archive_i::relative_path_parse(const char * relative_path,const char * playlist_path,string_base & out)
|
||||||
|
{
|
||||||
|
if (!is_our_path(relative_path)) return 0;
|
||||||
|
string8 archive_rel,file;
|
||||||
|
if (parse_unpack_path(relative_path,archive_rel,file))
|
||||||
|
{
|
||||||
|
string8 archive;
|
||||||
|
if (g_relative_path_parse(archive_rel,playlist_path,archive))
|
||||||
|
{
|
||||||
|
string8 out_path;
|
||||||
|
make_unpack_path(out_path,archive,file);
|
||||||
|
out.set_string(out_path);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "unpack://zip|17|file://c:\unf.rar|meh.mp3"
|
||||||
|
|
||||||
|
bool archive_i::parse_unpack_path(const char * path,string8 & archive,string8 & file)
|
||||||
|
{
|
||||||
|
path = strchr(path,'|');
|
||||||
|
if (!path) return false;
|
||||||
|
int delta = path_unpack_string(archive,path);
|
||||||
|
if (delta<0) return false;
|
||||||
|
path += delta;
|
||||||
|
file = path;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void archive_i::make_unpack_path(string8 & path,const char * archive,const char * file,const char * name)
|
||||||
|
{
|
||||||
|
path = "unpack://";
|
||||||
|
path += name;
|
||||||
|
path_pack_string(path,archive);
|
||||||
|
path += file;
|
||||||
|
}
|
||||||
|
|
||||||
|
void archive_i::make_unpack_path(string8 & path,const char * archive,const char * file) {make_unpack_path(path,archive,file,get_archive_type());}
|
||||||
|
|
||||||
|
|
||||||
|
FILE * file::streamio_open(const char * path,const char * flags)
|
||||||
|
{
|
||||||
|
FILE * ret = 0;
|
||||||
|
string8 temp;
|
||||||
|
g_get_canonical_path(path,temp);
|
||||||
|
if (!strncmp(temp,"file://",7))
|
||||||
|
{
|
||||||
|
ret = _wfopen(string_wide_from_utf8(path+7),string_wide_from_utf8(flags));
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reader::reader_release_safe()
|
||||||
|
{
|
||||||
|
reader_release();
|
||||||
|
/* try {
|
||||||
|
if (this) reader_release();
|
||||||
|
} catch(...) {}*/
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
|
||||||
|
bool reader::read_byte(BYTE & val)
|
||||||
|
{
|
||||||
|
return read(&val,sizeof(val))==sizeof(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool reader::read_word(WORD & val)
|
||||||
|
{
|
||||||
|
return read(&val,sizeof(val))==sizeof(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool reader::read_dword(DWORD & val)
|
||||||
|
{
|
||||||
|
return read(&val,sizeof(val))==sizeof(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool reader::read_qword(QWORD & val)
|
||||||
|
{
|
||||||
|
return read(&val,sizeof(val))==sizeof(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool reader::write_byte(BYTE val)
|
||||||
|
{
|
||||||
|
return write(&val,sizeof(val))==sizeof(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool reader::write_word(WORD val)
|
||||||
|
{
|
||||||
|
return write(&val,sizeof(val))==sizeof(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool reader::write_dword(DWORD val)
|
||||||
|
{
|
||||||
|
return write(&val,sizeof(val))==sizeof(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool reader::write_qword(QWORD val)
|
||||||
|
{
|
||||||
|
return write(&val,sizeof(val))==sizeof(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
//endian helpers
|
||||||
|
bool reader::read_word_be(WORD &val)
|
||||||
|
{
|
||||||
|
WORD temp;
|
||||||
|
if (!read_word(temp)) return false;
|
||||||
|
val = byte_order_helper::word_be_to_native(temp);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool reader::read_word_le(WORD &val)
|
||||||
|
{
|
||||||
|
WORD temp;
|
||||||
|
if (!read_word(temp)) return false;
|
||||||
|
val = byte_order_helper::word_le_to_native(temp);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool reader::read_dword_be(DWORD &val)
|
||||||
|
{
|
||||||
|
DWORD temp;
|
||||||
|
if (!read_dword(temp)) return false;
|
||||||
|
val = byte_order_helper::dword_be_to_native(temp);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool reader::read_dword_le(DWORD &val)
|
||||||
|
{
|
||||||
|
DWORD temp;
|
||||||
|
if (!read_dword(temp)) return false;
|
||||||
|
val = byte_order_helper::dword_le_to_native(temp);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
bool reader::read_qword_be(QWORD &val)
|
||||||
|
{
|
||||||
|
QWORD temp;
|
||||||
|
if (!read_qword(temp)) return false;
|
||||||
|
val = byte_order_helper::qword_be_to_native(temp);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool reader::read_qword_le(QWORD &val)
|
||||||
|
{
|
||||||
|
QWORD temp;
|
||||||
|
if (!read_qword(temp)) return false;
|
||||||
|
val = byte_order_helper::qword_le_to_native(temp);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool reader::write_word_be(WORD val)
|
||||||
|
{
|
||||||
|
return write_word(byte_order_helper::word_native_to_be(val));
|
||||||
|
}
|
||||||
|
bool reader::write_word_le(WORD val)
|
||||||
|
{
|
||||||
|
return write_word(byte_order_helper::word_native_to_le(val));
|
||||||
|
}
|
||||||
|
bool reader::write_dword_be(DWORD val)
|
||||||
|
{
|
||||||
|
return write_dword(byte_order_helper::dword_native_to_be(val));
|
||||||
|
}
|
||||||
|
bool reader::write_dword_le(DWORD val)
|
||||||
|
{
|
||||||
|
return write_dword(byte_order_helper::dword_native_to_le(val));
|
||||||
|
}
|
||||||
|
bool reader::write_qword_be(QWORD val)
|
||||||
|
{
|
||||||
|
return write_qword(byte_order_helper::qword_native_to_be(val));
|
||||||
|
}
|
||||||
|
bool reader::write_qword_le(QWORD val)
|
||||||
|
{
|
||||||
|
return write_qword(byte_order_helper::qword_native_to_le(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool reader::read_guid_le(GUID & g)
|
||||||
|
{
|
||||||
|
if (read(&g,sizeof(g))!=sizeof(g)) return false;
|
||||||
|
byte_order_helper::guid_le_to_native(g);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool reader::read_guid_be(GUID & g)
|
||||||
|
{
|
||||||
|
if (read(&g,sizeof(g))!=sizeof(g)) return false;
|
||||||
|
byte_order_helper::guid_be_to_native(g);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool reader::write_guid_le(const GUID & g)
|
||||||
|
{
|
||||||
|
GUID temp = g;
|
||||||
|
byte_order_helper::guid_native_to_le(temp);
|
||||||
|
return write(&temp,sizeof(temp))==sizeof(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool reader::write_guid_be(const GUID & g)
|
||||||
|
{
|
||||||
|
GUID temp = g;
|
||||||
|
byte_order_helper::guid_native_to_be(temp);
|
||||||
|
return write(&temp,sizeof(temp))==sizeof(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
__int64 reader::get_modification_time()
|
||||||
|
{
|
||||||
|
__int64 rv = 0;
|
||||||
|
reader_filetime * ptr = service_query_t(reader_filetime,this);
|
||||||
|
if (ptr)
|
||||||
|
{
|
||||||
|
rv = ptr->get_modification_time();
|
||||||
|
ptr->service_release();
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class directory_callback_isempty : public directory_callback
|
||||||
|
{
|
||||||
|
bool m_isempty;
|
||||||
|
public:
|
||||||
|
directory_callback_isempty() : m_isempty(true) {}
|
||||||
|
void on_file(const char * url) {m_isempty = false;}//api fixme: should signal enumeration abort
|
||||||
|
bool isempty() {return m_isempty;}
|
||||||
|
};
|
||||||
|
|
||||||
|
class directory_callback_dummy : public directory_callback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void on_file(const char * url) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool directory::g_is_empty(const char * path)
|
||||||
|
{
|
||||||
|
service_enum_t<directory> e;
|
||||||
|
directory * d;
|
||||||
|
for(d=e.first();d;d=e.next())
|
||||||
|
{
|
||||||
|
int rv;
|
||||||
|
directory_callback_isempty callback;
|
||||||
|
rv = d->list(path,&callback,0,true);//blargh @ old API not reporting directories inside, need to run in recur mode, fixme
|
||||||
|
d->service_release();
|
||||||
|
if (rv) return callback.isempty();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool directory::g_is_valid_dir(const char * path)
|
||||||
|
{
|
||||||
|
service_enum_t<directory> e;
|
||||||
|
directory * d;
|
||||||
|
for(d=e.first();d;d=e.next())
|
||||||
|
{
|
||||||
|
int rv;
|
||||||
|
directory_callback_isempty callback;
|
||||||
|
rv = d->list(path,&directory_callback_dummy(),0,false);//another API fixme
|
||||||
|
d->service_release();
|
||||||
|
if (rv) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
|
@ -0,0 +1,319 @@
|
||||||
|
#ifndef _READER_H_
|
||||||
|
#define _READER_H_
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
#include "interface_helper.h"
|
||||||
|
|
||||||
|
class playlist_loader_callback;
|
||||||
|
|
||||||
|
//file paths are all UTF-8
|
||||||
|
|
||||||
|
class NOVTABLE reader : public service_base
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
virtual ~reader() {};
|
||||||
|
reader() {};
|
||||||
|
public:
|
||||||
|
enum MODE {
|
||||||
|
MODE_READ = 1,//open with READ access and READ-only sharing
|
||||||
|
MODE_WRITE_EXISTING,//open existing file with full R/W access and NO sharing
|
||||||
|
MODE_WRITE_NEW,//create new file with full R/W access and NO sharing
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual bool open(const char *path,MODE mode)=0;
|
||||||
|
//open should be called ONLY ONCE on a reader instance, with the same path as passed to file::get_reader
|
||||||
|
//you MAY get deleted before open()
|
||||||
|
|
||||||
|
virtual unsigned read(void *buffer, unsigned length)=0; //0 on error/eof, number of bytes read otherwise (<length if eof)
|
||||||
|
virtual unsigned write(const void *buffer, unsigned length)=0; //returns amount of bytes written
|
||||||
|
virtual __int64 get_length()=0; //-1 if nonseekable/unknown
|
||||||
|
virtual __int64 get_position()=0; //-1 if nonseekable/unknown
|
||||||
|
virtual bool set_eof() {return false;}
|
||||||
|
virtual bool seek(__int64 position)=0;//returns 1 on success, 0 on failure
|
||||||
|
virtual bool seek2(__int64 position,int mode);//helper, implemented in reader.cpp, no need to override
|
||||||
|
virtual bool can_seek() {return true;}//if false, all seek calls will fail
|
||||||
|
virtual void abort() {} //aborts current operation (eg. when stopping a http stream), async call, ensure multithread safety on your side
|
||||||
|
virtual bool get_content_type(string_base & out) {return 0;}//mime type, return 1 on success, 0 if not supported
|
||||||
|
virtual bool is_in_memory() {return 0;}//return 1 if your file is fully buffered in memory
|
||||||
|
virtual void on_idle() {}
|
||||||
|
virtual bool error() {return false;}
|
||||||
|
|
||||||
|
virtual void reader_release() {service_release();}
|
||||||
|
void reader_release_safe();
|
||||||
|
|
||||||
|
__int64 get_modification_time();
|
||||||
|
|
||||||
|
//helper
|
||||||
|
static __int64 transfer(reader * src,reader * dst,__int64 bytes);
|
||||||
|
|
||||||
|
|
||||||
|
virtual service_base * service_query(const GUID & guid)
|
||||||
|
{
|
||||||
|
if (guid == get_class_guid()) {service_add_ref();return this;}
|
||||||
|
else return service_base::service_query(guid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}//only for service_query compatibility
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
bool read_x_le(T & val)
|
||||||
|
{
|
||||||
|
if (read(&val,sizeof(val))!=sizeof(val)) return false;
|
||||||
|
byte_order_helper::order_le_to_native(&val,sizeof(val));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
bool read_x_be(T & val)
|
||||||
|
{
|
||||||
|
if (read(&val,sizeof(val))!=sizeof(val)) return false;
|
||||||
|
byte_order_helper::order_be_to_native(&val,sizeof(val));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
bool write_x_le(T val)
|
||||||
|
{
|
||||||
|
byte_order_helper::order_native_to_le(&val,sizeof(val));
|
||||||
|
return write(&val,sizeof(val))==sizeof(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
bool write_x_be(T val)
|
||||||
|
{
|
||||||
|
byte_order_helper::order_native_to_be(&val,sizeof(val));
|
||||||
|
return write(&val,sizeof(val))==sizeof(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
//endian helpers
|
||||||
|
inline bool read_word_be(WORD & val) {return read_x_be(val);}
|
||||||
|
inline bool read_word_le(WORD & val) {return read_x_le(val);}
|
||||||
|
inline bool read_dword_be(DWORD & val) {return read_x_be(val);}
|
||||||
|
inline bool read_dword_le(DWORD & val) {return read_x_le(val);}
|
||||||
|
inline bool read_qword_be(QWORD & val) {return read_x_be(val);}
|
||||||
|
inline bool read_qword_le(QWORD & val) {return read_x_le(val);}
|
||||||
|
inline bool read_float_be(float & val) {return read_x_be(val);}
|
||||||
|
inline bool read_float_le(float & val) {return read_x_le(val);}
|
||||||
|
inline bool read_double_be(double & val) {return read_x_be(val);}
|
||||||
|
inline bool read_double_le(double & val) {return read_x_le(val);}
|
||||||
|
inline bool write_word_be(WORD val) {return write_x_be(val);}
|
||||||
|
inline bool write_word_le(WORD val) {return write_x_le(val);}
|
||||||
|
inline bool write_dword_be(DWORD val) {return write_x_be(val);}
|
||||||
|
inline bool write_dword_le(DWORD val) {return write_x_le(val);}
|
||||||
|
inline bool write_qword_be(QWORD val) {return write_x_be(val);}
|
||||||
|
inline bool write_qword_le(QWORD val) {return write_x_le(val);}
|
||||||
|
inline bool write_float_be(float val) {return write_x_be(val);}
|
||||||
|
inline bool write_float_le(float val) {return write_x_le(val);}
|
||||||
|
inline bool write_double_be(double val) {return write_x_be(val);}
|
||||||
|
inline bool write_double_le(double val) {return write_x_le(val);}
|
||||||
|
bool read_guid_le(GUID & g);
|
||||||
|
bool read_guid_be(GUID & g);
|
||||||
|
bool write_guid_le(const GUID & g);
|
||||||
|
bool write_guid_be(const GUID & g);
|
||||||
|
};
|
||||||
|
|
||||||
|
class reader_filetime : public reader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//time is a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601; 0 for invalid/unknown time
|
||||||
|
virtual __int64 get_modification_time()=0;
|
||||||
|
|
||||||
|
virtual service_base * service_query(const GUID & guid)
|
||||||
|
{
|
||||||
|
if (guid == get_class_guid()) {service_add_ref();return this;}
|
||||||
|
else return reader::service_query(guid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}//only for service_query
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class reader_dynamicinfo : public reader//for shoutcast metadata BS
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual bool is_dynamic_info_enabled()=0;//checks if currently open stream gets dynamic metadata
|
||||||
|
|
||||||
|
virtual bool get_dynamic_info(class file_info * out, bool * b_track_change)=0;//see input::get_dynamic_info
|
||||||
|
|
||||||
|
virtual service_base * service_query(const GUID & guid)
|
||||||
|
{
|
||||||
|
if (guid == get_class_guid()) {service_add_ref();return this;}
|
||||||
|
else return reader::service_query(guid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}//only for service_query
|
||||||
|
};
|
||||||
|
|
||||||
|
class NOVTABLE file : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual int get_canonical_path(const char * path,string_base & out)=0;//return 1 on success, 0 on failure / not our file
|
||||||
|
virtual int is_our_path(const char * path)=0;
|
||||||
|
|
||||||
|
virtual int get_display_path(const char * path,string_base & out)=0;
|
||||||
|
//converts canonical path to "human-readable" path (eg. removes file:// )
|
||||||
|
//return 1 on success, 0 on failure
|
||||||
|
//generally OK to leave it returning 0
|
||||||
|
|
||||||
|
//in in all subsequent calls other than get_canonical_path, you should expect file path that has been accepted by your is_our_path
|
||||||
|
|
||||||
|
//everything else than get_canonical_path() should expect strings that have already been processed by get_canonical_path()
|
||||||
|
//get_canonical_path() is called WITHOUT calling is_our_file first
|
||||||
|
virtual reader * get_reader(const char * path)=0;//note: reader will usually stay alive after you are service_release'd; do not open the file at this point, just create a reader capable of opening this file and return it
|
||||||
|
virtual int exists(const char * path)=0;//for return values, see FILE_EXISTS* below
|
||||||
|
virtual int remove(const char * path)=0;//return 1 on success and 0 on failure; also removes directories
|
||||||
|
virtual int move(const char * src,const char * dst)=0;
|
||||||
|
virtual int dont_read_infos(const char * src) {return 0;}//return 1 if infos shouldn't be precached from you (eg. HTTP)
|
||||||
|
|
||||||
|
virtual int relative_path_create(const char * file_path,const char * playlist_path,string_base & out) {return 0;}
|
||||||
|
//eg. file_path : "file://z:\dir\subdir\blah.mpc", playlist_path: "file://z:\dir\playlist.fpl", out: "file://subdir\blah.mpc"
|
||||||
|
//file_path == canonical path accepted by your class, playlist_path == canonical path possibly belonging to some other file class, set out to a string your relative_path_parse will recognize (use url-style notation to prevent confusion)
|
||||||
|
//dont worry about output string being possibly passed to anything else than relative_path_parse
|
||||||
|
//return 1 on success, 0 if relative path can't be created (will store as full path)
|
||||||
|
virtual int relative_path_parse(const char * relative_path,const char * playlist_path,string_base & out) {return 0;}
|
||||||
|
//eg. relative_path: "file://subdir\blah.mpc", playlist_path: "file://z:\dir\playlist.fpl", out: "file://z:\dir\subdir\blah.mpc"
|
||||||
|
//relative_path == relative path produced by some file class (possibly you), check it before processing)
|
||||||
|
//output canonical path to the file on success
|
||||||
|
//return: 1 on success, 0 on failure / not-our-file
|
||||||
|
|
||||||
|
virtual int create_directory(const char * path) {return 0;}
|
||||||
|
|
||||||
|
//helpers
|
||||||
|
static void g_get_canonical_path(const char * path,string_base & out);
|
||||||
|
static void g_get_display_path(const char * path,string_base & out);
|
||||||
|
|
||||||
|
static file * g_get_interface(const char * path);//path is AFTER get_canonical_path
|
||||||
|
static reader * g_get_reader(const char * path);//path is AFTER get_canonical_path (you're gonna need canonical version for reader::open() anyway)
|
||||||
|
static int g_dont_read_infos(const char * path);//path is AFTER get_canonical_path
|
||||||
|
//these below do get_canonical_path internally
|
||||||
|
static reader * g_open(const char * path,reader::MODE mode);//get_canonical_path + get_reader + reader->open; slow with http etc (you cant abort it)
|
||||||
|
static reader * g_open_read(const char * path) {return g_open(path,reader::MODE_READ);}
|
||||||
|
static reader * g_open_precache(const char * path);//open only for precaching data (eg. will fail on http etc)
|
||||||
|
static int g_exists(const char * path);
|
||||||
|
static int g_remove(const char * path);
|
||||||
|
static int g_move(const char * src,const char * dst);//needs canonical path
|
||||||
|
static int g_move_ex(const char * src,const char * dst);//converts to canonical path first
|
||||||
|
static int g_relative_path_create(const char * file_path,const char * playlist_path,string_base & out);
|
||||||
|
static int g_relative_path_parse(const char * relative_path,const char * playlist_path,string_base & out);
|
||||||
|
|
||||||
|
static int g_create_directory(const char * path);
|
||||||
|
|
||||||
|
static FILE * streamio_open(const char * path,const char * flags); // if for some bloody reason you ever need stream io compatibility, use this, INSTEAD of calling fopen() on the path string you've got; will only work with file:// (and not with http://, unpack:// or whatever)
|
||||||
|
|
||||||
|
inline static reader * g_open_temp() {return g_open("tempfile://",reader::MODE_WRITE_NEW);}
|
||||||
|
inline static reader * g_open_tempmem()
|
||||||
|
{
|
||||||
|
reader * r = g_open("tempmem://",reader::MODE_WRITE_NEW);
|
||||||
|
if (r==0) r = g_open_temp();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum {
|
||||||
|
FILE_EXISTS = 1,//use as flags, FILE_EXISTS_WRITEABLE implies FILE_EXISTS
|
||||||
|
FILE_EXISTS_WRITEABLE = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
};
|
||||||
|
|
||||||
|
class NOVTABLE directory_callback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void on_file(const char * url)=0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class directory_callback_i : public directory_callback
|
||||||
|
{
|
||||||
|
ptr_list_t<char> data;
|
||||||
|
static int sortfunc(const char * & p1, const char * & p2) {return stricmp_utf8(p1,p2);}
|
||||||
|
public:
|
||||||
|
virtual void on_file(const char * url) {data.add_item(strdup(url));}
|
||||||
|
~directory_callback_i() {data.free_all();}
|
||||||
|
unsigned get_count() {return data.get_count();}
|
||||||
|
const char * operator[](unsigned n) {return data[n];}
|
||||||
|
const char * get_item(unsigned n) {return data[n];}
|
||||||
|
void sort() {data.sort(sortfunc);}
|
||||||
|
};
|
||||||
|
|
||||||
|
class NOVTABLE directory : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual int list(const char * path,directory_callback * out,playlist_loader_callback * callback,bool b_recur = true)=0;
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
//helper
|
||||||
|
static int g_list(const char * path,directory_callback * out,playlist_loader_callback * callback,bool b_recur = true);
|
||||||
|
//playlist_loader_callback pointer is optional and used only for querying if enumeration should be aborted
|
||||||
|
|
||||||
|
static bool g_is_valid_dir(const char * path);
|
||||||
|
static bool g_is_empty(const char * path);
|
||||||
|
};
|
||||||
|
|
||||||
|
class NOVTABLE archive : public file//dont derive from this, use archive_i class below
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual int list(const char * path,directory_callback * out,playlist_loader_callback * callback)=0;
|
||||||
|
virtual int precache(const char * path,playlist_loader_callback * callback) {return 0;} //optional - if you are slow (eg. rar), metadb::precache() your files in optimal way using supplied reader pointer
|
||||||
|
//playlist_loader_callback ONLY for on_progress calls
|
||||||
|
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
};
|
||||||
|
|
||||||
|
class NOVTABLE archive_i : public archive // derive from this
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
//do not override these
|
||||||
|
virtual int get_canonical_path(const char * path,string_base & out);
|
||||||
|
virtual int is_our_path(const char * path);
|
||||||
|
virtual int get_display_path(const char * path,string_base & out);
|
||||||
|
virtual int exists(const char * path);
|
||||||
|
virtual int remove(const char * path);
|
||||||
|
virtual int move(const char * src,const char * dst);
|
||||||
|
virtual int dont_read_infos(const char * src);
|
||||||
|
virtual int relative_path_create(const char * file_path,const char * playlist_path,string_base & out);
|
||||||
|
virtual int relative_path_parse(const char * relative_path,const char * playlist_path,string_base & out);
|
||||||
|
protected:
|
||||||
|
//override these
|
||||||
|
virtual const char * get_archive_type()=0;//eg. "zip", must be lowercase
|
||||||
|
virtual int exists_in_archive(const char * archive,const char * file) {return 1;}
|
||||||
|
|
||||||
|
public:
|
||||||
|
//override these
|
||||||
|
virtual reader * get_reader(const char * path)=0;
|
||||||
|
virtual int list(const char * path,directory_callback * out,playlist_loader_callback * callback)=0;
|
||||||
|
virtual int precache(const char * path,playlist_loader_callback * callback) {return 0;}//optional - if you are slow (eg. rar), metadb::precache() your files in optimal way using supplied reader pointer
|
||||||
|
//playlist_loader_callback ONLY for on_progress calls
|
||||||
|
|
||||||
|
|
||||||
|
static bool parse_unpack_path(const char * path,string8 & archive,string8 & file);
|
||||||
|
static void make_unpack_path(string8 & path,const char * archive,const char * file,const char * name);
|
||||||
|
void make_unpack_path(string8 & path,const char * archive,const char * file);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class archive_factory
|
||||||
|
{
|
||||||
|
service_factory_single_t<file,T> factory1;
|
||||||
|
service_factory_single_t<archive,T> factory2;
|
||||||
|
public:
|
||||||
|
archive_factory() {}
|
||||||
|
T& get_static_instance() {return factory1.get_static_instance();}
|
||||||
|
};
|
||||||
|
|
||||||
|
//register as:
|
||||||
|
// static archive_factory<archive_foo> foo;
|
||||||
|
|
||||||
|
|
||||||
|
#include "reader_helper.h"
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,263 @@
|
||||||
|
#ifndef _READER_HELPER_H_
|
||||||
|
#define _READER_HELPER_H_
|
||||||
|
|
||||||
|
//helper
|
||||||
|
class file_path_canonical : public string8
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
file_path_canonical(const char * src)
|
||||||
|
{
|
||||||
|
file::g_get_canonical_path(src,*this);
|
||||||
|
}
|
||||||
|
operator const char * () {return get_ptr();}
|
||||||
|
};
|
||||||
|
|
||||||
|
class file_path_display : public string8
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
file_path_display(const char * src)
|
||||||
|
{
|
||||||
|
file::g_get_display_path(src,*this);
|
||||||
|
}
|
||||||
|
operator const char * () {return get_ptr();}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//HELPER, allows short seek back on unseekable reader, readonly
|
||||||
|
template<class T>
|
||||||
|
class reader_seekback_t : public T
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
bool aborting;
|
||||||
|
mem_block_t<char> buffer;
|
||||||
|
virtual ~reader_seekback_t() {}
|
||||||
|
virtual int seekback_read(void * buffer,int size)=0;
|
||||||
|
__int64 read_pos,reader_pos;
|
||||||
|
int buffer_pos,buffered;
|
||||||
|
public:
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
DEFAULT_SEEKBACK_SIZE = 0x10000
|
||||||
|
};
|
||||||
|
reader_seekback_t(int size = DEFAULT_SEEKBACK_SIZE) : buffer(size) {aborting=0;read_pos=0;reader_pos=0;buffered=0;buffer_pos=0;}
|
||||||
|
virtual unsigned read(void *buffer, unsigned length);
|
||||||
|
virtual unsigned write(const void *buffer, unsigned length) {return 0;}
|
||||||
|
// virtual __int64 get_length() {return m_reader->get_length();}
|
||||||
|
virtual __int64 get_position() {return read_pos;}
|
||||||
|
virtual bool set_eof() {return false;}
|
||||||
|
virtual bool seek(__int64 position);
|
||||||
|
virtual bool can_seek() {return 0;}
|
||||||
|
virtual void abort() {aborting=1;}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef reader_seekback_t< reader > reader_seekback;
|
||||||
|
|
||||||
|
class reader_seekback_wrap : public reader_seekback
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
reader * m_reader;
|
||||||
|
virtual int seekback_read(void * buffer,int size)
|
||||||
|
{
|
||||||
|
return m_reader->read(buffer,size);
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
reader_seekback_wrap(reader * p_reader,int size)
|
||||||
|
: reader_seekback(size), m_reader(p_reader) {}
|
||||||
|
virtual bool open(const char *path,MODE mode) {return false;}//dummy
|
||||||
|
virtual __int64 get_length() {return m_reader->get_length();}
|
||||||
|
virtual void abort() {m_reader->abort();reader_seekback::abort();}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class reader_backbuffer_wrap : public reader_seekback_wrap
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
reader_backbuffer_wrap(reader * p_reader,int size)
|
||||||
|
: reader_seekback_wrap(p_reader,size) {}
|
||||||
|
virtual bool seek(__int64 position);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class reader_membuffer : public reader_filetime
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
mem_block_t<char> buffer;
|
||||||
|
int buffer_pos;
|
||||||
|
reader_membuffer(__int64 modtime) : m_modtime(modtime) {}
|
||||||
|
virtual unsigned write(const void*, unsigned) {return 0;}
|
||||||
|
__int64 m_modtime;
|
||||||
|
protected:
|
||||||
|
bool init(reader * src)
|
||||||
|
{
|
||||||
|
if (src->is_in_memory()) return false;//already buffered
|
||||||
|
__int64 size64 = src->get_length();
|
||||||
|
if (size64<=0 || size64>0x7FFFFFFF) return false;
|
||||||
|
unsigned size = (unsigned)size64;
|
||||||
|
if (!buffer.set_size(size))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
src->seek(0);
|
||||||
|
buffer_pos=0;
|
||||||
|
if (src->read(buffer.get_ptr(),size)!=size)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
static reader * create(reader * src,__int64 modtime = 0)
|
||||||
|
{
|
||||||
|
reader_membuffer * ptr = new service_impl_p1_t<reader_membuffer,__int64>(modtime);
|
||||||
|
if (ptr)
|
||||||
|
{
|
||||||
|
if (!ptr->init(src)) {delete ptr; ptr = 0;}
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool open(const char *path,MODE mode) {return false;}//dummy
|
||||||
|
|
||||||
|
virtual unsigned read(void *out, unsigned length)
|
||||||
|
{
|
||||||
|
unsigned max = buffer.get_size() - buffer_pos;
|
||||||
|
if (length>max) length = max;
|
||||||
|
memcpy(out,buffer.get_ptr()+buffer_pos,length);
|
||||||
|
buffer_pos += length;
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int write(const void *buffer, int length) {return 0;}
|
||||||
|
virtual __int64 get_length() {return buffer.get_size();}
|
||||||
|
virtual __int64 get_position() {return buffer_pos;}
|
||||||
|
virtual bool set_eof() {return 0;}
|
||||||
|
virtual bool seek(__int64 position)
|
||||||
|
{
|
||||||
|
if (position>(__int64)buffer.get_size())
|
||||||
|
position = (__int64)buffer.get_size();
|
||||||
|
else if (position<0) position=0;
|
||||||
|
|
||||||
|
buffer_pos = (int)position;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
virtual bool can_seek() {return true;}
|
||||||
|
virtual bool is_in_memory() {return true;}
|
||||||
|
virtual __int64 get_modification_time() {return m_modtime;}
|
||||||
|
};
|
||||||
|
|
||||||
|
class reader_limited : public reader
|
||||||
|
{
|
||||||
|
virtual bool open(const char * filename,MODE mode) {return false;}//dummy
|
||||||
|
reader * r;
|
||||||
|
__int64 begin;
|
||||||
|
__int64 end;
|
||||||
|
virtual unsigned write(const void * , unsigned) {return 0;}
|
||||||
|
public:
|
||||||
|
reader_limited() {r=0;begin=0;end=0;}
|
||||||
|
reader_limited(reader * p_r,__int64 p_begin,__int64 p_end)
|
||||||
|
{
|
||||||
|
r = p_r;
|
||||||
|
begin = p_begin;
|
||||||
|
end = p_end;
|
||||||
|
r->seek(begin);
|
||||||
|
}
|
||||||
|
|
||||||
|
void init(reader * p_r,__int64 p_begin,__int64 p_end)
|
||||||
|
{
|
||||||
|
r = p_r;
|
||||||
|
begin = p_begin;
|
||||||
|
end = p_end;
|
||||||
|
r->seek(begin);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual unsigned read(void *buffer, unsigned length)
|
||||||
|
{
|
||||||
|
__int64 pos = r->get_position();
|
||||||
|
if ((__int64)length > end - pos) length = (unsigned)(end - pos);
|
||||||
|
return r->read(buffer,length);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual __int64 get_length()
|
||||||
|
{
|
||||||
|
return end-begin;
|
||||||
|
}
|
||||||
|
virtual __int64 get_position()
|
||||||
|
{
|
||||||
|
return r->get_position()-begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool seek(__int64 position)
|
||||||
|
{
|
||||||
|
return r->seek(position+begin);
|
||||||
|
}
|
||||||
|
virtual bool can_seek() {return r->can_seek();}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
bool reader_seekback_t<T>::seek(__int64 position)
|
||||||
|
{
|
||||||
|
if (aborting) return false;
|
||||||
|
if (position>reader_pos)
|
||||||
|
{
|
||||||
|
read_pos = reader_pos;
|
||||||
|
__int64 skip = (int)(position-reader_pos);
|
||||||
|
char temp[256];
|
||||||
|
while(skip>0)
|
||||||
|
{
|
||||||
|
__int64 delta = sizeof(temp);
|
||||||
|
if (delta>skip) delta=skip;
|
||||||
|
if (read(temp,(int)delta)!=delta) return 0;
|
||||||
|
skip-=delta;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (reader_pos-position>(__int64)buffered)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
read_pos = position;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template<class T>
|
||||||
|
unsigned reader_seekback_t<T>::read(void *p_out, unsigned length)
|
||||||
|
{
|
||||||
|
if (aborting) return 0;
|
||||||
|
//buffer can be null
|
||||||
|
int done = 0;
|
||||||
|
char * out = (char*)p_out;
|
||||||
|
if (read_pos<reader_pos)
|
||||||
|
{
|
||||||
|
if (reader_pos-read_pos>(__int64)buffered) return 0;
|
||||||
|
unsigned delta = (unsigned)(reader_pos-read_pos);
|
||||||
|
if (delta>length) delta = length;
|
||||||
|
buffer.read_circular(buffer_pos - (unsigned)(reader_pos-read_pos),out,delta);
|
||||||
|
read_pos+=delta;
|
||||||
|
length-=delta;
|
||||||
|
out+=delta;
|
||||||
|
done += delta;
|
||||||
|
}
|
||||||
|
if (length>0)
|
||||||
|
{
|
||||||
|
unsigned delta = seekback_read(out,length);
|
||||||
|
buffer_pos = buffer.write_circular(buffer_pos,out,delta);
|
||||||
|
read_pos+=delta;
|
||||||
|
reader_pos+=delta;
|
||||||
|
length-=delta;
|
||||||
|
out+=delta;
|
||||||
|
done+=delta;
|
||||||
|
|
||||||
|
buffered+=delta;
|
||||||
|
if (buffered>(int)buffer.get_size()) buffered=buffer.get_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif//_READER_HELPER_H_
|
|
@ -0,0 +1,79 @@
|
||||||
|
#ifndef _READER_HELPER_MEM_
|
||||||
|
#define _READER_HELPER_MEM_
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class reader_mem_base_t : public T
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
char * data;
|
||||||
|
unsigned size,pos;
|
||||||
|
protected:
|
||||||
|
bool free_on_exit;
|
||||||
|
void mem_init(void * p_data,int p_size)
|
||||||
|
{
|
||||||
|
if (data && free_on_exit) free(data);
|
||||||
|
data = (char*)p_data;
|
||||||
|
size = p_size;
|
||||||
|
pos=0;
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
reader_mem_base_t() {data=0;size=0;pos=0;free_on_exit=true;}
|
||||||
|
~reader_mem_base_t() {if (free_on_exit && data) free(data);}
|
||||||
|
|
||||||
|
virtual unsigned write(const void*, unsigned) {return 0;}
|
||||||
|
|
||||||
|
virtual unsigned read(void *buffer, unsigned length)
|
||||||
|
{
|
||||||
|
if (pos + length > size) length = size - pos;
|
||||||
|
memcpy(buffer,data+pos,length);
|
||||||
|
pos+=length;
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual __int64 get_length()
|
||||||
|
{
|
||||||
|
return (__int64)size;
|
||||||
|
}
|
||||||
|
virtual __int64 get_position()
|
||||||
|
{
|
||||||
|
return (__int64)pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool seek(__int64 position)
|
||||||
|
{
|
||||||
|
if (position < 0) position=0;
|
||||||
|
else if (position>(__int64)size) position=size;
|
||||||
|
pos = (int)position;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
virtual bool is_in_memory() {return 1;}
|
||||||
|
};
|
||||||
|
|
||||||
|
#define reader_mem_base reader_mem_base_t< service_impl_t<reader> >
|
||||||
|
|
||||||
|
class reader_mem_temp : public reader_mem_base_t< service_impl_t<reader_filetime> >
|
||||||
|
{
|
||||||
|
__int64 m_time;
|
||||||
|
virtual bool open(const char * filename,MODE mode) {return 0;}//dummy
|
||||||
|
virtual __int64 get_modification_time() {return m_time;}
|
||||||
|
public:
|
||||||
|
reader_mem_temp(void * p_data,int p_size,__int64 p_time = 0) : m_time(p_time)
|
||||||
|
{
|
||||||
|
free_on_exit = false;
|
||||||
|
mem_init(p_data,p_size);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class reader_mem : public reader_mem_base
|
||||||
|
{
|
||||||
|
virtual bool open(const char * filename,MODE mode) {return 0;}//dummy
|
||||||
|
public:
|
||||||
|
reader_mem(void * p_data,int p_size)
|
||||||
|
{
|
||||||
|
free_on_exit = true;
|
||||||
|
mem_init(p_data,p_size);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //_READER_HELPER_MEM_
|
|
@ -0,0 +1,85 @@
|
||||||
|
#ifndef _FOOBAR2000_SDK_REPLAYGAIN_H_
|
||||||
|
#define _FOOBAR2000_SDK_REPLAYGAIN_H_
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
|
||||||
|
#include "file_info.h"
|
||||||
|
#include "metadb_handle.h"
|
||||||
|
#include "metadb.h"
|
||||||
|
|
||||||
|
|
||||||
|
class NOVTABLE replaygain : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum mode_t
|
||||||
|
{
|
||||||
|
MODE_DEFAULT,//uses settings from core config
|
||||||
|
MODE_DISABLED,
|
||||||
|
MODE_TRACK,
|
||||||
|
MODE_ALBUM,
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual double query_scale(const file_info * info,mode_t mode = MODE_DEFAULT)=0;
|
||||||
|
virtual mode_t get_user_settings()=0;
|
||||||
|
|
||||||
|
static replaygain * get()//helper
|
||||||
|
{
|
||||||
|
return service_enum_create_t(replaygain,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static mode_t g_get_user_settings()//returns whatever MODE_DEFAULT currently corresponds to
|
||||||
|
{
|
||||||
|
mode_t rv = MODE_DISABLED;
|
||||||
|
replaygain * ptr = get();
|
||||||
|
if (ptr)
|
||||||
|
{
|
||||||
|
rv = ptr->get_user_settings();
|
||||||
|
ptr->service_release();
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static double g_query_scale(const file_info* info,mode_t mode = MODE_DEFAULT)
|
||||||
|
{
|
||||||
|
double rv = 1;
|
||||||
|
replaygain * ptr = get();
|
||||||
|
if (ptr)
|
||||||
|
{
|
||||||
|
rv = ptr->query_scale(info,mode);
|
||||||
|
ptr->service_release();
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static double g_query_scale(const playable_location* entry,mode_t mode = MODE_DEFAULT)
|
||||||
|
{
|
||||||
|
double rv = 1;
|
||||||
|
metadb * p_metadb = metadb::get();
|
||||||
|
if (p_metadb)
|
||||||
|
{
|
||||||
|
metadb_handle * handle = p_metadb->handle_create(entry);
|
||||||
|
if (handle)
|
||||||
|
{
|
||||||
|
rv = g_query_scale(handle,mode);
|
||||||
|
handle->handle_release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static double g_query_scale(metadb_handle * handle,mode_t mode = MODE_DEFAULT)
|
||||||
|
{
|
||||||
|
double rv = 1;
|
||||||
|
handle->handle_lock();
|
||||||
|
const file_info * info = handle->handle_query_locked();
|
||||||
|
if (info)
|
||||||
|
rv = g_query_scale(info,mode);
|
||||||
|
handle->handle_unlock();
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //_FOOBAR2000_SDK_REPLAYGAIN_H_
|
|
@ -0,0 +1,164 @@
|
||||||
|
#ifndef _RESAMPLER_H_
|
||||||
|
#define _RESAMPLER_H_
|
||||||
|
|
||||||
|
#include "dsp.h"
|
||||||
|
|
||||||
|
//hint: use resampler_helper class declared below to resample
|
||||||
|
|
||||||
|
class NOVTABLE resampler : public dsp
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
|
||||||
|
virtual void set_dest_sample_rate(unsigned srate)=0;
|
||||||
|
virtual void set_config(unsigned flags)=0;//flags - see below
|
||||||
|
virtual bool is_conversion_supported(unsigned src_srate,unsigned dst_srate)=0;
|
||||||
|
|
||||||
|
virtual service_base * service_query(const GUID & guid)
|
||||||
|
{
|
||||||
|
if (guid == get_class_guid()) {service_add_ref();return this;}
|
||||||
|
else return dsp::service_query(guid);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
FLAG_FAST = 0x0,
|
||||||
|
FLAG_SLOW = 0x1,
|
||||||
|
};
|
||||||
|
|
||||||
|
//helper
|
||||||
|
static bool get_interface(resampler ** out_resampler,dsp ** out_dsp)//may fail if no resampler DSP is installed
|
||||||
|
{//you need to release both returned pointers
|
||||||
|
service_enum_t<dsp> e;
|
||||||
|
dsp * ptr;
|
||||||
|
for(ptr=e.first();ptr;ptr=e.next())
|
||||||
|
{
|
||||||
|
resampler * ptr2 = service_query_t(resampler,ptr);
|
||||||
|
if (ptr2)
|
||||||
|
{
|
||||||
|
*out_resampler = ptr2;
|
||||||
|
*out_dsp = ptr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
ptr->service_release();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool get_interface_ex(resampler ** out_resampler,dsp ** out_dsp,unsigned src_srate,unsigned dst_srate)//may fail if no resampler DSP is installed or installed ones cant handle this conversion
|
||||||
|
{//you need to release both returned pointers
|
||||||
|
service_enum_t<dsp> e;
|
||||||
|
dsp * ptr;
|
||||||
|
for(ptr=e.first();ptr;ptr=e.next())
|
||||||
|
{
|
||||||
|
resampler * ptr2 = service_query_t(resampler,ptr);
|
||||||
|
if (ptr2)
|
||||||
|
{
|
||||||
|
if (ptr2->is_conversion_supported(src_srate,dst_srate))
|
||||||
|
{
|
||||||
|
*out_resampler = ptr2;
|
||||||
|
*out_dsp = ptr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ptr2->service_release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ptr->service_release();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static dsp * create(unsigned srate,unsigned flags)
|
||||||
|
{
|
||||||
|
dsp * p_resampler_dsp;
|
||||||
|
resampler * p_resampler;
|
||||||
|
if (resampler::get_interface(&p_resampler,&p_resampler_dsp))
|
||||||
|
{
|
||||||
|
p_resampler->set_config(flags);
|
||||||
|
p_resampler->set_dest_sample_rate(srate);
|
||||||
|
p_resampler->service_release();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
p_resampler_dsp = 0;
|
||||||
|
}
|
||||||
|
return p_resampler_dsp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static dsp * create_ex(unsigned src_srate,unsigned dst_srate,unsigned flags)
|
||||||
|
{
|
||||||
|
dsp * p_resampler_dsp;
|
||||||
|
resampler * p_resampler;
|
||||||
|
if (resampler::get_interface_ex(&p_resampler,&p_resampler_dsp,src_srate,dst_srate))
|
||||||
|
{
|
||||||
|
p_resampler->set_config(flags);
|
||||||
|
p_resampler->set_dest_sample_rate(dst_srate);
|
||||||
|
p_resampler->service_release();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
p_resampler_dsp = 0;
|
||||||
|
}
|
||||||
|
return p_resampler_dsp;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class resampler_helper
|
||||||
|
{
|
||||||
|
dsp * p_dsp;
|
||||||
|
unsigned expected_dst_srate;
|
||||||
|
public:
|
||||||
|
explicit resampler_helper()
|
||||||
|
{
|
||||||
|
p_dsp = 0;
|
||||||
|
expected_dst_srate = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup_ex(unsigned src_srate,unsigned dst_srate,unsigned flags)
|
||||||
|
{
|
||||||
|
if (!p_dsp) p_dsp = resampler::create_ex(src_srate,dst_srate,flags);
|
||||||
|
expected_dst_srate = dst_srate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup(unsigned flags,unsigned dst_srate)
|
||||||
|
{
|
||||||
|
if (!p_dsp) p_dsp = resampler::create(dst_srate,flags);
|
||||||
|
expected_dst_srate = dst_srate;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool run(dsp_chunk_list * list,unsigned flags)//returns false on failure; flags - see dsp.h
|
||||||
|
{
|
||||||
|
if (!p_dsp || expected_dst_srate<=0) return false;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
p_dsp->run(list,0,flags);
|
||||||
|
bool rv = true;
|
||||||
|
unsigned n,m=list->get_count();
|
||||||
|
for(n=0;n<m;n++)
|
||||||
|
{
|
||||||
|
if (list->get_item(n)->get_srate() != expected_dst_srate) {rv = false;break;}
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush() {if (p_dsp) p_dsp->flush();}
|
||||||
|
|
||||||
|
double get_latency()
|
||||||
|
{
|
||||||
|
return p_dsp ? p_dsp->get_latency() : 0;;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
~resampler_helper()
|
||||||
|
{
|
||||||
|
if (p_dsp) p_dsp->service_release();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif//_RESAMPLER_H_
|
|
@ -0,0 +1,66 @@
|
||||||
|
#include "foobar2000.h"
|
||||||
|
#include "component.h"
|
||||||
|
|
||||||
|
foobar2000_client g_client;
|
||||||
|
foobar2000_api * g_api;
|
||||||
|
|
||||||
|
service_base * service_factory_base::enum_create(const GUID &g,int n)
|
||||||
|
{
|
||||||
|
assert(core_api::are_services_available());
|
||||||
|
return g_api->service_enum_create(g,n);
|
||||||
|
}
|
||||||
|
|
||||||
|
int service_factory_base::enum_get_count(const GUID &g)
|
||||||
|
{
|
||||||
|
assert(core_api::are_services_available());
|
||||||
|
return g_api->service_enum_get_count(g);
|
||||||
|
}
|
||||||
|
|
||||||
|
service_factory_base * service_factory_base::list=0;
|
||||||
|
|
||||||
|
service_enum::service_enum(const GUID &g)
|
||||||
|
{
|
||||||
|
guid = g;
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void service_enum::reset()
|
||||||
|
{
|
||||||
|
service_ptr=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
service_base * service_enum::next()
|
||||||
|
{
|
||||||
|
return service_factory_base::enum_create(guid,service_ptr++);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
void service_base::service_release_safe()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (this) service_release();
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
#ifdef WIN32
|
||||||
|
|
||||||
|
long interlocked_increment(long * var)
|
||||||
|
{
|
||||||
|
assert(!((unsigned)var&3));
|
||||||
|
return InterlockedIncrement(var);
|
||||||
|
}
|
||||||
|
|
||||||
|
long interlocked_decrement(long * var)
|
||||||
|
{
|
||||||
|
assert(!((unsigned)var&3));
|
||||||
|
return InterlockedDecrement(var);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#error portme
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,246 @@
|
||||||
|
#ifndef _SERVICE_H_
|
||||||
|
#define _SERVICE_H_
|
||||||
|
|
||||||
|
#include "../../pfc/pfc.h"
|
||||||
|
|
||||||
|
#include "utf8api.h"
|
||||||
|
|
||||||
|
#include "core_api.h"
|
||||||
|
|
||||||
|
long interlocked_increment(long * var);//note: win32 sucks, return values arent reliable, they can be used only to determine if new value is <0, 0 or >0
|
||||||
|
long interlocked_decrement(long * var);
|
||||||
|
|
||||||
|
class NOVTABLE service_base //ALL service classes MUST inherit from this
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual int service_release() = 0;
|
||||||
|
virtual int service_add_ref() = 0;
|
||||||
|
virtual service_base * service_query(const GUID & guid) {return 0;}
|
||||||
|
};
|
||||||
|
|
||||||
|
#include "service_impl.h"
|
||||||
|
/*
|
||||||
|
//msvc6 sucks
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
static T* service_query_t(service_base * ptr) {return static_cast<T*>(ptr->service_query(T::get_class_guid()));}
|
||||||
|
|
||||||
|
*/
|
||||||
|
#define service_query_t(T,ptr) (static_cast<T*>(ptr->service_query(T::get_class_guid())))
|
||||||
|
//user service_release on returned object when youre done
|
||||||
|
|
||||||
|
class NOVTABLE service_factory_base
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
static service_factory_base *list;
|
||||||
|
service_factory_base * next;
|
||||||
|
GUID guid;
|
||||||
|
protected:
|
||||||
|
service_factory_base(const GUID & p_guid) {assert(!core_api::are_services_available());guid=p_guid;next=list;list=this;}
|
||||||
|
public:
|
||||||
|
|
||||||
|
inline const GUID & get_class_guid() const {return guid;}
|
||||||
|
|
||||||
|
inline static service_factory_base * list_get_pointer() {return list;}
|
||||||
|
inline service_factory_base * list_get_next() {return next;}
|
||||||
|
|
||||||
|
static service_base * enum_create(const GUID &g,int n);
|
||||||
|
static int enum_get_count(const GUID &g);
|
||||||
|
inline static bool is_service_present(const GUID & g) {return enum_get_count(g)>0;}
|
||||||
|
|
||||||
|
#ifdef FOOBAR2000_EXE
|
||||||
|
static void process_components_directory(const char * path,service_factory_base ** & blah);
|
||||||
|
static void on_app_init(const char * exe_path);
|
||||||
|
static void on_app_shutdown();
|
||||||
|
static void config_reset(const char * name = 0);
|
||||||
|
static void on_app_post_init();
|
||||||
|
static void on_saveconfig(bool b_reset=false);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//obsolete, use core_api
|
||||||
|
inline static HINSTANCE get_my_instance() {return core_api::get_my_instance();}
|
||||||
|
inline static const char * get_my_file_name() {return core_api::get_my_file_name();}
|
||||||
|
inline static const char * get_my_full_path() {return core_api::get_my_full_path();}
|
||||||
|
inline static HWND get_main_window() {return core_api::get_main_window();}
|
||||||
|
|
||||||
|
virtual service_base * instance_create() = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
//msvc6 sucks
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
static T * service_enum_create_t(int n) {return (T*)service_factory_base::enum_create(T::get_class_guid(),n);}
|
||||||
|
template<class T>
|
||||||
|
static int service_enum_get_count_t() {return service_factory_base::enum_get_count(T::get_class_guid());}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define service_enum_create_t(T,n) (static_cast<T*>(service_factory_base::enum_create(T::get_class_guid(),n)))
|
||||||
|
#define service_enum_get_count_t(T) (service_factory_base::enum_get_count(T::get_class_guid()))
|
||||||
|
#define service_enum_is_present(g) (service_factory_base::is_service_present(g))
|
||||||
|
#define service_enum_is_present_t(T) (service_factory_base::is_service_present(T::get_class_guid()))
|
||||||
|
|
||||||
|
|
||||||
|
class service_enum
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
int service_ptr;
|
||||||
|
GUID guid;
|
||||||
|
public:
|
||||||
|
service_enum(const GUID &);
|
||||||
|
void reset();
|
||||||
|
service_base * first() {reset();return next();}
|
||||||
|
service_base * next();
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class B>
|
||||||
|
class service_enum_t : private service_enum
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
service_enum_t() : service_enum(B::get_class_guid()) {}
|
||||||
|
void reset() {service_enum::reset();}
|
||||||
|
B * first() {return (B*)(service_enum::first());}
|
||||||
|
B * next() {return (B*)(service_enum::next());}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template<class B,class T>
|
||||||
|
class service_factory_t : public service_factory_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
service_factory_t() : service_factory_base(B::get_class_guid())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~service_factory_t()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual service_base * instance_create()
|
||||||
|
{
|
||||||
|
return (service_base*)(B*)(T*)new service_impl_t<T>;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class B,class T>
|
||||||
|
class service_factory_single_t : public service_factory_base
|
||||||
|
{
|
||||||
|
service_impl_single_t<T> g_instance;
|
||||||
|
public:
|
||||||
|
service_factory_single_t() : service_factory_base(B::get_class_guid()) {}
|
||||||
|
|
||||||
|
~service_factory_single_t() {}
|
||||||
|
|
||||||
|
virtual service_base * instance_create()
|
||||||
|
{
|
||||||
|
g_instance.service_add_ref();
|
||||||
|
return (service_base*)(B*)(T*)&g_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline T& get_static_instance() const {return (T&)g_instance;}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class B,class T>
|
||||||
|
class service_factory_single_ref_t : public service_factory_base
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
T & instance;
|
||||||
|
public:
|
||||||
|
service_factory_single_ref_t(T& param) : instance(param), service_factory_base(B::get_class_guid()) {}
|
||||||
|
|
||||||
|
virtual service_base * instance_create()
|
||||||
|
{
|
||||||
|
service_base * ret = dynamic_cast<service_base*>(&instance);
|
||||||
|
ret->service_add_ref();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline T& get_static_instance() {return instance;}
|
||||||
|
|
||||||
|
virtual void instance_release(service_base * ptr) {assert(0);}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template<class B,class T>
|
||||||
|
class service_factory_single_transparent_t : public service_factory_base, public service_impl_single_t<T>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
service_factory_single_transparent_t() : service_factory_base(B::get_class_guid()) {}
|
||||||
|
|
||||||
|
virtual service_base * instance_create()
|
||||||
|
{
|
||||||
|
service_add_ref();
|
||||||
|
return (service_base*)(B*)(T*)this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline T& get_static_instance() const {return *(T*)this;}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class B,class T,class P1>
|
||||||
|
class service_factory_single_transparent_p1_t : public service_factory_base, public service_impl_single_p1_t<T,P1>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
service_factory_single_transparent_p1_t(P1 p1) : service_factory_base(B::get_class_guid()), service_impl_single_p1_t<T,P1>(p1) {}
|
||||||
|
|
||||||
|
|
||||||
|
virtual service_base * instance_create()
|
||||||
|
{
|
||||||
|
service_add_ref();
|
||||||
|
return (service_base*)(B*)(T*)this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline T& get_static_instance() const {return *(T*)this;}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class B,class T,class P1,class P2>
|
||||||
|
class service_factory_single_transparent_p2_t : public service_factory_base, public service_impl_single_p2_t<T,P1,P2>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
service_factory_single_transparent_p2_t(P1 p1,P2 p2) : service_factory_base(B::get_class_guid()), service_impl_single_p2_t<T,P1,P2>(p1,p2) {}
|
||||||
|
|
||||||
|
|
||||||
|
virtual service_base * instance_create()
|
||||||
|
{
|
||||||
|
service_add_ref();
|
||||||
|
return (service_base*)(B*)(T*)this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline T& get_static_instance() const {return *(T*)this;}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class B,class T,class P1,class P2,class P3>
|
||||||
|
class service_factory_single_transparent_p3_t : public service_factory_base, public service_impl_single_p3_t<T,P1,P2,P3>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
service_factory_single_transparent_p3_t(P1 p1,P2 p2,P3 p3) : service_factory_base(B::get_class_guid()), service_impl_single_p3_t<T,P1,P2,P3>(p1,p2,p3) {}
|
||||||
|
|
||||||
|
|
||||||
|
virtual service_base * instance_create()
|
||||||
|
{
|
||||||
|
service_add_ref();
|
||||||
|
return (service_base*)(B*)(T*)this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline T& get_static_instance() const {return *(T*)this;}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class B,class T,class P1,class P2,class P3,class P4>
|
||||||
|
class service_factory_single_transparent_p4_t : public service_factory_base, public service_impl_single_p4_t<T,P1,P2,P3,P4>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
service_factory_single_transparent_p4_t(P1 p1,P2 p2,P3 p3,P4 p4) : service_factory_base(B::get_class_guid()), service_impl_single_p4_t<T,P1,P2,P3,P4>(p1,p2,p3,p4) {}
|
||||||
|
|
||||||
|
|
||||||
|
virtual service_base * instance_create()
|
||||||
|
{
|
||||||
|
service_add_ref();
|
||||||
|
return (service_base*)(B*)(T*)this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline T& get_static_instance() const {return *(T*)this;}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,68 @@
|
||||||
|
#ifndef _FOOBAR2000_SERVICE_HELPER_H_
|
||||||
|
#define _FOOBAR2000_SERVICE_HELPER_H_
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class service_list_t : public ptr_list_t<T>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void delete_item(T * ptr)
|
||||||
|
{
|
||||||
|
remove_item(ptr);
|
||||||
|
ptr->service_release();
|
||||||
|
}
|
||||||
|
|
||||||
|
void delete_by_idx(int idx)
|
||||||
|
{
|
||||||
|
T * ptr = remove_by_idx(idx);
|
||||||
|
if (ptr) ptr->service_release();
|
||||||
|
}
|
||||||
|
|
||||||
|
void delete_all()
|
||||||
|
{
|
||||||
|
int n,max=get_count();
|
||||||
|
for(n=0;n<max;n++)
|
||||||
|
{
|
||||||
|
T * ptr = get_item(n);
|
||||||
|
if (ptr) ptr->service_release();
|
||||||
|
}
|
||||||
|
remove_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
void delete_mask(const bit_array & mask)
|
||||||
|
{
|
||||||
|
int n,m=get_count();
|
||||||
|
for(n=0;n<m;n++)
|
||||||
|
if (mask[n]) get_item(n)->service_release();
|
||||||
|
remove_mask(mask);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class service_list_autodel_t : public service_list_t<T>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
inline ~service_list_autodel_t() {delete_all();}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class service_ptr_autodel_t
|
||||||
|
{
|
||||||
|
T * ptr;
|
||||||
|
public:
|
||||||
|
inline T* operator=(T* param) {return ptr = param;}
|
||||||
|
inline T* operator->() {return ptr;}
|
||||||
|
inline ~service_ptr_autodel_t() {if (ptr) ptr->service_release();}
|
||||||
|
inline service_ptr_autodel_t(T * param=0) {ptr=param;}
|
||||||
|
inline operator T* () {return ptr;}
|
||||||
|
inline void release()
|
||||||
|
{
|
||||||
|
T* temp = ptr;
|
||||||
|
ptr = 0;
|
||||||
|
if (temp) temp->service_release_safe();
|
||||||
|
}
|
||||||
|
inline bool is_empty() {return ptr==0;}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,104 @@
|
||||||
|
//only meant to be included from service.h
|
||||||
|
|
||||||
|
class service_reference_counter
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
long refcount;
|
||||||
|
public:
|
||||||
|
inline service_reference_counter() : refcount(1) {}
|
||||||
|
inline long increment() {return interlocked_increment(&refcount);}
|
||||||
|
inline long decrement() {return interlocked_decrement(&refcount);}
|
||||||
|
};
|
||||||
|
|
||||||
|
#define __implement_service_base \
|
||||||
|
private: \
|
||||||
|
service_reference_counter m_reference_counter; \
|
||||||
|
public: \
|
||||||
|
virtual int service_release() {long ret = m_reference_counter.decrement(); if (ret == 0) delete this; return ret;} \
|
||||||
|
virtual int service_add_ref() {return m_reference_counter.increment();}
|
||||||
|
|
||||||
|
#define __implement_service_base_single \
|
||||||
|
public: \
|
||||||
|
virtual int service_release() {return 1;} \
|
||||||
|
virtual int service_add_ref() {return 1;}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class service_impl_t : public T
|
||||||
|
{
|
||||||
|
__implement_service_base;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T,class P1>
|
||||||
|
class service_impl_p1_t : public T
|
||||||
|
{
|
||||||
|
__implement_service_base;
|
||||||
|
public:
|
||||||
|
service_impl_p1_t(P1 p1) : T(p1) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T,class P1,class P2>
|
||||||
|
class service_impl_p2_t : public T
|
||||||
|
{
|
||||||
|
__implement_service_base;
|
||||||
|
public:
|
||||||
|
service_impl_p2_t(P1 p1,P2 p2) : T(p1,p2) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T,class P1,class P2,class P3>
|
||||||
|
class service_impl_p3_t : public T
|
||||||
|
{
|
||||||
|
__implement_service_base;
|
||||||
|
public:
|
||||||
|
service_impl_p3_t(P1 p1,P2 p2,P3 p3) : T(p1,p2,p3) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T,class P1,class P2,class P3,class P4>
|
||||||
|
class service_impl_p4_t : public T
|
||||||
|
{
|
||||||
|
__implement_service_base;
|
||||||
|
public:
|
||||||
|
service_impl_p4_t(P1 p1,P2 p2,P3 p3,P4 p4) : T(p1,p2,p3,p4) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class service_impl_single_t : public T
|
||||||
|
{
|
||||||
|
__implement_service_base_single;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T,class P1>
|
||||||
|
class service_impl_single_p1_t : public T
|
||||||
|
{
|
||||||
|
__implement_service_base_single;
|
||||||
|
public:
|
||||||
|
service_impl_single_p1_t(P1 p1) : T(p1) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T,class P1,class P2>
|
||||||
|
class service_impl_single_p2_t : public T
|
||||||
|
{
|
||||||
|
__implement_service_base_single;
|
||||||
|
public:
|
||||||
|
service_impl_single_p2_t(P1 p1,P2 p2) : T(p1,p2) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T,class P1,class P2,class P3>
|
||||||
|
class service_impl_single_p3_t : public T
|
||||||
|
{
|
||||||
|
__implement_service_base_single;
|
||||||
|
public:
|
||||||
|
service_impl_single_p3_t(P1 p1,P2 p2,P3 p3) : T(p1,p2,p3) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T,class P1,class P2,class P3,class P4>
|
||||||
|
class service_impl_single_p4_t : public T
|
||||||
|
{
|
||||||
|
__implement_service_base_single;
|
||||||
|
public:
|
||||||
|
service_impl_single_p4_t(P1 p1,P2 p2,P3 p3,P4 p4) : T(p1,p2,p3,p4) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#undef __implement_service_base
|
||||||
|
#undef __implement_service_base_single
|
|
@ -0,0 +1 @@
|
||||||
|
#error DEPRECATED
|
|
@ -0,0 +1,2 @@
|
||||||
|
//cpp used to generate precompiled header
|
||||||
|
#include "foobar2000.h"
|
|
@ -0,0 +1,98 @@
|
||||||
|
#include "foobar2000.h"
|
||||||
|
|
||||||
|
int tag_reader::g_run(reader * r,file_info * info,const char * name)
|
||||||
|
{
|
||||||
|
if (!r->can_seek()) return 0;//dont bother
|
||||||
|
__int64 offset = r->get_position();
|
||||||
|
int rv=0;
|
||||||
|
service_enum_t<tag_reader> e;
|
||||||
|
tag_reader * ptr;
|
||||||
|
for(ptr=e.first();ptr && !rv;ptr = e.next())
|
||||||
|
{
|
||||||
|
const char * s_name = ptr->get_name();
|
||||||
|
if (!stricmp_utf8(name,s_name))
|
||||||
|
{
|
||||||
|
r->seek(0);
|
||||||
|
rv = ptr->run(r,info);
|
||||||
|
}
|
||||||
|
ptr->service_release();
|
||||||
|
}
|
||||||
|
r->seek(offset);
|
||||||
|
// if (rv) info->info_set("tagtype",name);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tag_reader::g_run_multi(reader * r,file_info * info,const char * list)
|
||||||
|
{
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
const char * sep = strchr(list,'|');
|
||||||
|
if (sep==0) return *list ? g_run(r,info,list) : 0;
|
||||||
|
if (sep>list && g_run(r,info,string8(list,sep-list))) return 1;
|
||||||
|
list = sep + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int tag_writer::g_run(reader * r,const file_info * info,const char * name)
|
||||||
|
{
|
||||||
|
if (!r->can_seek()) return 0;//dont bother
|
||||||
|
__int64 offset = r->get_position();
|
||||||
|
int rv=0;
|
||||||
|
service_enum_t<tag_writer> e;
|
||||||
|
tag_writer * ptr;
|
||||||
|
for(ptr=e.first();ptr && !rv;ptr = e.next())
|
||||||
|
{
|
||||||
|
const char * s_name = ptr->get_name();
|
||||||
|
|
||||||
|
if (!stricmp_utf8(name,s_name))
|
||||||
|
{
|
||||||
|
r->seek(0);
|
||||||
|
rv = ptr->run(r,info);
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr->service_release();
|
||||||
|
}
|
||||||
|
r->seek(offset);
|
||||||
|
return rv;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void tag_remover::g_run(reader * r)
|
||||||
|
{
|
||||||
|
service_enum_t<tag_remover> e;
|
||||||
|
tag_remover * ptr;
|
||||||
|
for(ptr=e.first();ptr;ptr = e.next())
|
||||||
|
{
|
||||||
|
r->seek(0);
|
||||||
|
ptr->run(r);
|
||||||
|
ptr->service_release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tag_reader::g_have_type(const char * name)
|
||||||
|
{
|
||||||
|
service_enum_t<tag_reader> e;
|
||||||
|
tag_reader * ptr;
|
||||||
|
bool found = false;
|
||||||
|
for(ptr=e.first();ptr;ptr=e.next())
|
||||||
|
{
|
||||||
|
if (!stricmp_utf8(name,ptr->get_name())) found = true;
|
||||||
|
ptr->service_release();
|
||||||
|
if (found) break;
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tag_writer::g_have_type(const char * name)
|
||||||
|
{
|
||||||
|
service_enum_t<tag_writer> e;
|
||||||
|
tag_writer * ptr;
|
||||||
|
bool found = false;
|
||||||
|
for(ptr=e.first();ptr;ptr=e.next())
|
||||||
|
{
|
||||||
|
if (!stricmp_utf8(name,ptr->get_name())) found = true;
|
||||||
|
ptr->service_release();
|
||||||
|
if (found) break;
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
#ifndef _TAGREAD_H_
|
||||||
|
#define _TAGREAD_H_
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
#include "reader.h"
|
||||||
|
#include "file_info.h"
|
||||||
|
|
||||||
|
//helper class to let others use "standard" tag readers/writers in foo_input_std, do not reimplement
|
||||||
|
|
||||||
|
class NOVTABLE tag_reader : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual const char * get_name()=0;
|
||||||
|
virtual int run(reader * r,file_info * info)=0;//return 1 on success, 0 on failure / unknown format
|
||||||
|
|
||||||
|
static int g_run(reader * r,file_info * info,const char * name);//will seek back to original file offset
|
||||||
|
static int g_run_multi(reader * r,file_info * info,const char * list);//will seek back to original file offset
|
||||||
|
static bool g_have_type(const char * name);
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
};
|
||||||
|
|
||||||
|
class NOVTABLE tag_writer : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual const char * get_name()=0;
|
||||||
|
virtual int run(reader * r,const file_info * info)=0;//return 1 on success, 0 on failure / unknown format
|
||||||
|
|
||||||
|
static int g_run(reader * r,const file_info * info,const char * name);//will seek back to original file offset
|
||||||
|
static bool g_have_type(const char * name);
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
};
|
||||||
|
|
||||||
|
class NOVTABLE tag_remover : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void run(reader * r)=0;
|
||||||
|
|
||||||
|
static void g_run(reader * r);
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,26 @@
|
||||||
|
#include "foobar2000.h"
|
||||||
|
|
||||||
|
void titleformat::g_run(const file_info * source,string_base & out,const char * spec,const char * extra_items)
|
||||||
|
{
|
||||||
|
titleformat * ptr = service_enum_create_t(titleformat,0);
|
||||||
|
if (ptr)
|
||||||
|
{
|
||||||
|
ptr->run(source,out,spec,extra_items);
|
||||||
|
ptr->service_release();//actually safe not to release it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void titleformat::remove_color_marks(const char * src,string8 & out)//helper
|
||||||
|
{
|
||||||
|
out.reset();
|
||||||
|
while(*src)
|
||||||
|
{
|
||||||
|
if (*src==3)
|
||||||
|
{
|
||||||
|
src++;
|
||||||
|
while(*src && *src!=3) src++;
|
||||||
|
if (*src==3) src++;
|
||||||
|
}
|
||||||
|
else out.add_byte(*src++);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
#ifndef _FOOBAR2000_TITLEFORMAT_H_
|
||||||
|
#define _FOOBAR2000_TITLEFORMAT_H_
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
|
||||||
|
#include "file_info.h"
|
||||||
|
#include "config_var.h"
|
||||||
|
|
||||||
|
//use this to call titleformatting directly (bypassing database)
|
||||||
|
//implemented in the core, dont override
|
||||||
|
|
||||||
|
class titleformat : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void run(const file_info * source,string_base & out,const char * spec,const char * extra_items)=0;
|
||||||
|
//source - file info to use
|
||||||
|
//out - string receiving results
|
||||||
|
//spec - formatting specification string to use, e.g. "%ARTIST% - %TITLE%"
|
||||||
|
//extra_items - %_name% variables, name=value pairs, null-separated, terminated with two nulls, e.g. "name=value\0name=value\0"
|
||||||
|
//extra_items can be null
|
||||||
|
|
||||||
|
//helpers
|
||||||
|
static void g_run(const file_info * source,string_base & out,const char * spec,const char * extra_items);
|
||||||
|
|
||||||
|
static void remove_color_marks(const char * src,string8 & out);//helper
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //_FOOBAR2000_TITLEFORMAT_H_
|
|
@ -0,0 +1,27 @@
|
||||||
|
#include "foobar2000.h"
|
||||||
|
|
||||||
|
bool ui_drop_item_callback::g_on_drop(interface IDataObject * pDataObject)
|
||||||
|
{
|
||||||
|
service_enum_t<ui_drop_item_callback> e;
|
||||||
|
ui_drop_item_callback * ptr;
|
||||||
|
for(ptr=e.first();ptr;ptr=e.next())
|
||||||
|
{
|
||||||
|
bool status = ptr->on_drop(pDataObject);
|
||||||
|
ptr->service_release();
|
||||||
|
if (status) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ui_drop_item_callback::g_is_accepted_type(interface IDataObject * pDataObject)
|
||||||
|
{
|
||||||
|
service_enum_t<ui_drop_item_callback> e;
|
||||||
|
ui_drop_item_callback * ptr;
|
||||||
|
for(ptr=e.first();ptr;ptr=e.next())
|
||||||
|
{
|
||||||
|
bool status = ptr->is_accepted_type(pDataObject);
|
||||||
|
ptr->service_release();
|
||||||
|
if (status) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
#ifndef _FOOBAR2000_UI_H_
|
||||||
|
#define _FOOBAR2000_UI_H_
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
|
||||||
|
#ifndef WIN32
|
||||||
|
#error PORTME
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class NOVTABLE user_interface : public service_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef BOOL (WINAPI * HookProc_t)(HWND wnd,UINT msg,WPARAM wp,LPARAM lp,LRESULT * ret);
|
||||||
|
//HookProc usage:
|
||||||
|
//in your windowproc, call HookProc first, and if it returns true, return LRESULT value it passed to you
|
||||||
|
|
||||||
|
virtual const char * get_name()=0;
|
||||||
|
virtual HWND init(HookProc_t hook)=0;//create your window here
|
||||||
|
virtual void shutdown(bool endsession)=0;//you need to destroy your window here
|
||||||
|
virtual void activate()=0;
|
||||||
|
virtual void hide()=0;
|
||||||
|
virtual bool is_visible()=0;//for activate/hide command
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
|
||||||
|
static user_interface * g_find(const char * name)
|
||||||
|
{
|
||||||
|
service_enum_t<user_interface> e;
|
||||||
|
user_interface * ptr;
|
||||||
|
for(ptr=e.first();ptr;ptr=e.next())
|
||||||
|
{
|
||||||
|
if (!stricmp_utf8(ptr->get_name(),name)) return ptr;
|
||||||
|
ptr->service_release();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class user_interface_factory : public service_factory_single_t<user_interface,T> {};
|
||||||
|
|
||||||
|
//new (0.8)
|
||||||
|
class NOVTABLE ui_control : public service_base//implemented in the core, do not override
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
|
||||||
|
virtual bool is_visible()=0;
|
||||||
|
virtual void activate()=0;
|
||||||
|
virtual void hide()=0;
|
||||||
|
virtual HICON get_main_icon()=0;//no need to free returned handle
|
||||||
|
virtual HICON load_main_icon(unsigned width,unsigned height)=0;//use DestroyIcon() to free it
|
||||||
|
|
||||||
|
static ui_control * get() {return service_enum_create_t(ui_control,0);}//no need to service_release
|
||||||
|
};
|
||||||
|
|
||||||
|
class NOVTABLE ui_drop_item_callback : public service_base //called from UI when some object (ie. files from explorer) is dropped
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
|
||||||
|
virtual bool on_drop(interface IDataObject * pDataObject)=0;//return true if you processed the data, false if not
|
||||||
|
virtual bool is_accepted_type(interface IDataObject * pDataObject)=0;
|
||||||
|
|
||||||
|
static bool g_on_drop(interface IDataObject * pDataObject);
|
||||||
|
static bool g_is_accepted_type(interface IDataObject * pDataObject);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class ui_drop_item_callback_factory : public service_factory_single_t<ui_drop_item_callback,T> {};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,25 @@
|
||||||
|
#ifndef _UNPACK_H_
|
||||||
|
#define _UNPACK_H_
|
||||||
|
|
||||||
|
#include "reader.h"
|
||||||
|
|
||||||
|
class NOVTABLE unpacker : public service_base
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
//override this
|
||||||
|
virtual reader * open(reader * src)=0;
|
||||||
|
public:
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
|
||||||
|
static reader * g_open(reader * r);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
usage:
|
||||||
|
you have a reader to an zip/rar/gzip/whatever archive containing just a single file you want to read, eg. a module
|
||||||
|
do unpacker::g_open() on that reader
|
||||||
|
returns 0 on failure (not a known archive or cant read it) or pointer to a new reader reading contents of that archive on success
|
||||||
|
*/
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
#include "foobar2000.h"
|
||||||
|
|
||||||
|
HWND uCreateDialog(UINT id,HWND parent,DLGPROC proc,long param)
|
||||||
|
{
|
||||||
|
return uCreateDialog(core_api::get_my_instance(),id,parent,proc,param);
|
||||||
|
}
|
||||||
|
|
||||||
|
int uDialogBox(UINT id,HWND parent,DLGPROC proc,long param)
|
||||||
|
{
|
||||||
|
return uDialogBox(core_api::get_my_instance(),id,parent,proc,param);
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef _FOOBAR2000_UTF8API_H_
|
||||||
|
#define _FOOBAR2000_UTF8API_H_
|
||||||
|
|
||||||
|
#include "../utf8api/utf8api.h"
|
||||||
|
|
||||||
|
HWND uCreateDialog(UINT id,HWND parent,DLGPROC proc,long param=0);
|
||||||
|
int uDialogBox(UINT id,HWND parent,DLGPROC proc,long param=0);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,57 @@
|
||||||
|
#ifndef _FOOBAR2000_VIS_H_
|
||||||
|
#define _FOOBAR2000_VIS_H_
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
#include "audio_chunk.h"
|
||||||
|
|
||||||
|
typedef float vis_sample;
|
||||||
|
|
||||||
|
struct vis_chunk
|
||||||
|
{
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
FLAG_LAGGED = 1, //unless you need to process all samples in the stream for some reason (eg. scanning for peak), you should ignore chunks marked as "lagged"
|
||||||
|
};
|
||||||
|
|
||||||
|
vis_sample * data;
|
||||||
|
unsigned samples;// sizeof(data) == sizeof(vis_sample) * samples * nch;
|
||||||
|
unsigned srate,nch;
|
||||||
|
vis_sample * spectrum;
|
||||||
|
unsigned spectrum_size;// sizeof(spectrum) == sizeof(vis_sample) * spectrum_size * nch;
|
||||||
|
unsigned flags;//see FLAG_* above
|
||||||
|
|
||||||
|
inline double get_duration() const {return srate>0 ? (double)samples / (double)srate : 0;}
|
||||||
|
|
||||||
|
};//both data and spectrum are channel-interleaved
|
||||||
|
|
||||||
|
#define visualisation visualization
|
||||||
|
#define visualisation_factory visualization_factory
|
||||||
|
|
||||||
|
class visualization : public service_base
|
||||||
|
{
|
||||||
|
public://all calls are from main thread
|
||||||
|
virtual void on_data(const vis_chunk * data)=0;
|
||||||
|
virtual void on_flush()=0;
|
||||||
|
virtual double get_expected_latency() {return 0;}//return time in seconds; allowed to change when running
|
||||||
|
|
||||||
|
//allowed to change in runtime
|
||||||
|
virtual bool is_active()=0;
|
||||||
|
virtual bool need_spectrum()=0;
|
||||||
|
|
||||||
|
static const GUID class_guid;
|
||||||
|
static inline const GUID & get_class_guid() {return class_guid;}
|
||||||
|
|
||||||
|
static bool is_vis_manager_present()
|
||||||
|
{
|
||||||
|
static const GUID guid =
|
||||||
|
{ 0x5ca94fe1, 0x7593, 0x47de, { 0x8a, 0xdf, 0x8e, 0x36, 0xb4, 0x93, 0xa6, 0xd0 } };
|
||||||
|
return service_enum_is_present(guid);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class visualization_factory : public service_factory_single_t<visualization,T> {};
|
||||||
|
|
||||||
|
//usage: static visualisation_factory<myvisualisation> blah;
|
||||||
|
|
||||||
|
#endif //_FOOBAR2000_VIS_H_
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,93 @@
|
||||||
|
#include "../SDK/foobar2000.h"
|
||||||
|
#include "../SDK/component.h"
|
||||||
|
|
||||||
|
static HINSTANCE g_hIns;
|
||||||
|
|
||||||
|
static string_simple g_name,g_full_path;
|
||||||
|
|
||||||
|
static bool g_services_available = false;
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
static void selftest()
|
||||||
|
{
|
||||||
|
assert(sizeof(bool)==1);
|
||||||
|
assert(sizeof(int)==4);
|
||||||
|
assert(sizeof(__int64)==8);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
__declspec(dllexport) foobar2000_client * _cdecl foobar2000_get_interface(foobar2000_api * p_api,HINSTANCE hIns)
|
||||||
|
{
|
||||||
|
#ifdef _DEBUG
|
||||||
|
selftest();
|
||||||
|
#endif
|
||||||
|
cfg_var::config_on_app_init();
|
||||||
|
g_hIns = hIns;
|
||||||
|
g_api = p_api;
|
||||||
|
|
||||||
|
return &g_client;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
namespace core_api
|
||||||
|
{
|
||||||
|
|
||||||
|
HINSTANCE get_my_instance()
|
||||||
|
{
|
||||||
|
return g_hIns;
|
||||||
|
}
|
||||||
|
|
||||||
|
HWND get_main_window()
|
||||||
|
{
|
||||||
|
return g_api->get_main_window();
|
||||||
|
}
|
||||||
|
const char * get_my_file_name()
|
||||||
|
{
|
||||||
|
return g_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * get_my_full_path()
|
||||||
|
{
|
||||||
|
return g_full_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool are_services_available()
|
||||||
|
{
|
||||||
|
return g_services_available;
|
||||||
|
}
|
||||||
|
bool assert_main_thread()
|
||||||
|
{
|
||||||
|
return (g_services_available && g_api) ? g_api->assert_main_thread() : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_main_thread()
|
||||||
|
{
|
||||||
|
return (g_services_available && g_api) ? g_api->is_main_thread() : true;
|
||||||
|
}
|
||||||
|
const char * get_profile_path()
|
||||||
|
{
|
||||||
|
return (g_services_available && g_api) ? g_api->get_profile_path() : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void foobar2000_client::set_library_path(const char * path,const char * name)
|
||||||
|
{
|
||||||
|
g_full_path = path;
|
||||||
|
g_name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void foobar2000_client::services_init(bool val)
|
||||||
|
{
|
||||||
|
g_services_available = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef _DEBUG
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#if _MSC_VER==1200
|
||||||
|
#pragma comment(linker,"/opt:nowin98")
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
|
@ -0,0 +1,6 @@
|
||||||
|
// stdafx.cpp : source file that includes just the standard includes
|
||||||
|
// foobar2000_sdk_helpers.pch will be the pre-compiled header
|
||||||
|
// stdafx.obj will contain the pre-compiled type information
|
||||||
|
|
||||||
|
#include "stdafx.h"
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
// stdafx.h : include file for standard system include files,
|
||||||
|
// or project specific include files that are used frequently, but
|
||||||
|
// are changed infrequently
|
||||||
|
//
|
||||||
|
|
||||||
|
#if !defined(AFX_STDAFX_H__6356EC2B_6DD1_4BE8_935C_87ECBA8697E4__INCLUDED_)
|
||||||
|
#define AFX_STDAFX_H__6356EC2B_6DD1_4BE8_935C_87ECBA8697E4__INCLUDED_
|
||||||
|
|
||||||
|
#if _MSC_VER > 1000
|
||||||
|
#pragma once
|
||||||
|
#endif // _MSC_VER > 1000
|
||||||
|
|
||||||
|
|
||||||
|
#include "../SDK/foobar2000.h"
|
||||||
|
#include "helpers.h"
|
||||||
|
|
||||||
|
// TODO: reference additional headers your program requires here
|
||||||
|
|
||||||
|
//{{AFX_INSERT_LOCATION}}
|
||||||
|
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
|
||||||
|
|
||||||
|
#endif // !defined(AFX_STDAFX_H__6356EC2B_6DD1_4BE8_935C_87ECBA8697E4__INCLUDED_)
|
|
@ -0,0 +1,109 @@
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "create_directory_helper.h"
|
||||||
|
|
||||||
|
namespace create_directory_helper
|
||||||
|
{
|
||||||
|
void create_directory_structure(const char * path)
|
||||||
|
{
|
||||||
|
mem_block_t<char> temp(strlen(path)+1);
|
||||||
|
strcpy(temp,path);
|
||||||
|
char * ptr = strstr(temp,":\\");
|
||||||
|
if (ptr)
|
||||||
|
{
|
||||||
|
ptr+=2;
|
||||||
|
while(*ptr)
|
||||||
|
{
|
||||||
|
if (*ptr == '\\')
|
||||||
|
{
|
||||||
|
*ptr = 0;
|
||||||
|
file::g_create_directory(temp);
|
||||||
|
*ptr = '\\';
|
||||||
|
}
|
||||||
|
ptr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_bad_dirchar(char c)
|
||||||
|
{
|
||||||
|
return c==' ' || c=='.';
|
||||||
|
}
|
||||||
|
|
||||||
|
void make_path(const char * parent,const char * filename,const char * extension,bool allow_new_dirs,string8 & out)
|
||||||
|
{
|
||||||
|
out.reset();
|
||||||
|
if (parent && *parent)
|
||||||
|
{
|
||||||
|
out = parent;
|
||||||
|
out.fix_dir_separator('\\');
|
||||||
|
}
|
||||||
|
bool last_char_is_dir_sep = true;
|
||||||
|
while(*filename)
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
if (allow_new_dirs && is_bad_dirchar(*filename))
|
||||||
|
{
|
||||||
|
const char * ptr = filename+1;
|
||||||
|
while(is_bad_dirchar(*ptr)) ptr++;
|
||||||
|
if (*ptr!='\\' && *ptr!='/') out.add_string_n(filename,ptr-filename);
|
||||||
|
filename = ptr;
|
||||||
|
if (*filename==0) break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (is_path_bad_char(*filename))
|
||||||
|
{
|
||||||
|
if (allow_new_dirs && (*filename=='\\' || *filename=='/'))
|
||||||
|
{
|
||||||
|
if (!last_char_is_dir_sep)
|
||||||
|
{
|
||||||
|
out.add_char('\\');
|
||||||
|
last_char_is_dir_sep = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
out.add_char('_');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
out.add_byte(*filename);
|
||||||
|
last_char_is_dir_sep = false;
|
||||||
|
}
|
||||||
|
filename++;
|
||||||
|
}
|
||||||
|
if (out.length()>0 && out[out.length()-1]=='\\')
|
||||||
|
{
|
||||||
|
out.add_string("noname");
|
||||||
|
}
|
||||||
|
if (extension && *extension)
|
||||||
|
{
|
||||||
|
out.add_char('.');
|
||||||
|
out.add_string(extension);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void create_directory_helper::format_filename(metadb_handle * handle,const char * spec,string8 & out,const char * extra)
|
||||||
|
{
|
||||||
|
out = "";
|
||||||
|
string8 temp;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
const char * ptr1 = strchr(spec,'\\'), *ptr2 = strchr(spec,'/');
|
||||||
|
if ((!ptr1 && ptr2) || (ptr1 && ptr2 && ptr2<ptr1)) ptr1 = ptr2;
|
||||||
|
if (ptr1)
|
||||||
|
{
|
||||||
|
handle->handle_format_title(temp,string_simple(spec,ptr1-spec),extra);
|
||||||
|
temp.fix_filename_chars();
|
||||||
|
spec = ptr1+1;
|
||||||
|
out += temp;
|
||||||
|
out += "\\";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
handle->handle_format_title(temp,spec,extra);
|
||||||
|
temp.fix_filename_chars();
|
||||||
|
out += temp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
#ifndef _CREATE_DIRECTORY_HELPER_H_
|
||||||
|
#define _CREATE_DIRECTORY_HELPER_H_
|
||||||
|
|
||||||
|
namespace create_directory_helper {
|
||||||
|
void create_directory_structure(const char * path);
|
||||||
|
void make_path(const char * parent,const char * filename,const char * extension,bool allow_new_dirs,string8 & out);
|
||||||
|
void format_filename(class metadb_handle * handle,const char * spec,string8 & out,const char * extra=0);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif//_CREATE_DIRECTORY_HELPER_H_
|
|
@ -0,0 +1,201 @@
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "dialog_resize_helper.h"
|
||||||
|
|
||||||
|
void resize::calc_xy(HWND wnd,UINT id,RECT &r,RECT & o)
|
||||||
|
{
|
||||||
|
RECT c;
|
||||||
|
GetClientRect(wnd,&c);
|
||||||
|
SetWindowPos(GetDlgItem(wnd,id),0,0,0,
|
||||||
|
r.right-r.left+c.right-o.right,
|
||||||
|
r.bottom-r.top+c.bottom-o.bottom,
|
||||||
|
SWP_NOMOVE|SWP_NOZORDER);
|
||||||
|
}
|
||||||
|
|
||||||
|
void resize::calc_move_xy(HWND wnd,UINT id,RECT &r,RECT & o)
|
||||||
|
{
|
||||||
|
RECT c;
|
||||||
|
GetClientRect(wnd,&c);
|
||||||
|
SetWindowPos(GetDlgItem(wnd,id),0,
|
||||||
|
c.right-o.right+r.left,
|
||||||
|
c.bottom-o.bottom+r.top,
|
||||||
|
0,0,SWP_NOSIZE|SWP_NOZORDER);
|
||||||
|
}
|
||||||
|
|
||||||
|
void resize::calc_move_x(HWND wnd,UINT id,RECT &r,RECT & o)
|
||||||
|
{
|
||||||
|
RECT c;
|
||||||
|
GetClientRect(wnd,&c);
|
||||||
|
SetWindowPos(GetDlgItem(wnd,id),0,
|
||||||
|
c.right-o.right+r.left,
|
||||||
|
r.top,
|
||||||
|
0,0,SWP_NOSIZE|SWP_NOZORDER);
|
||||||
|
}
|
||||||
|
|
||||||
|
void resize::calc_move_x_size_y(HWND wnd,UINT id,RECT &r,RECT & o)
|
||||||
|
{
|
||||||
|
RECT c;
|
||||||
|
GetClientRect(wnd,&c);
|
||||||
|
SetWindowPos(GetDlgItem(wnd,id),0,
|
||||||
|
c.right-o.right+r.left,
|
||||||
|
r.top,
|
||||||
|
r.right-r.left,
|
||||||
|
r.bottom-r.top+c.bottom-o.bottom,
|
||||||
|
SWP_NOZORDER);
|
||||||
|
}
|
||||||
|
|
||||||
|
void resize::calc_move_y(HWND wnd,UINT id,RECT &r,RECT & o)
|
||||||
|
{
|
||||||
|
RECT c;
|
||||||
|
GetClientRect(wnd,&c);
|
||||||
|
SetWindowPos(GetDlgItem(wnd,id),0,
|
||||||
|
r.left,
|
||||||
|
c.bottom-o.bottom+r.top,
|
||||||
|
0,0,SWP_NOSIZE|SWP_NOZORDER);
|
||||||
|
}
|
||||||
|
|
||||||
|
void resize::calc_x(HWND wnd,UINT id,RECT &r,RECT & o)
|
||||||
|
{
|
||||||
|
RECT c;
|
||||||
|
GetClientRect(wnd,&c);
|
||||||
|
SetWindowPos(GetDlgItem(wnd,id),0,0,0,
|
||||||
|
r.right-r.left+c.right-o.right,
|
||||||
|
r.bottom-r.top,
|
||||||
|
SWP_NOMOVE|SWP_NOZORDER);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetChildRect(HWND wnd,UINT id,RECT* child)
|
||||||
|
{
|
||||||
|
RECT temp;
|
||||||
|
GetWindowRect(GetDlgItem(wnd,id),&temp);
|
||||||
|
MapWindowPoints(0,wnd,(POINT*)&temp,2);
|
||||||
|
*child = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
class dialog_resize_helper
|
||||||
|
{
|
||||||
|
struct entry { RECT orig; UINT id; UINT flags };
|
||||||
|
mem_block_list<entry> data;
|
||||||
|
RECT orig_client;
|
||||||
|
HWND parent;
|
||||||
|
public:
|
||||||
|
enum {
|
||||||
|
X_MOVE = 1, X_SIZE = 2, Y_MOVE = 4, Y_SIZE = 8
|
||||||
|
};
|
||||||
|
void set_parent(HWND wnd);
|
||||||
|
void add_item(UINT id,UINT flags);
|
||||||
|
void reset();
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
void dialog_resize_helper::set_parent(HWND wnd)
|
||||||
|
{
|
||||||
|
reset();
|
||||||
|
parent = wnd;
|
||||||
|
GetClientRect(parent,&orig_client);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dialog_resize_helper::reset()
|
||||||
|
{
|
||||||
|
parent = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dialog_resize_helper::on_wm_size()
|
||||||
|
{
|
||||||
|
if (parent)
|
||||||
|
{
|
||||||
|
unsigned n;
|
||||||
|
for(n=0;n<m_table_size;n++)
|
||||||
|
{
|
||||||
|
param & e = m_table[n];
|
||||||
|
const RECT & orig_rect = rects[n];
|
||||||
|
RECT cur_client;
|
||||||
|
GetClientRect(parent,&cur_client);
|
||||||
|
HWND wnd = GetDlgItem(parent,e.id);
|
||||||
|
|
||||||
|
unsigned dest_x = orig_rect.left, dest_y = orig_rect.top,
|
||||||
|
dest_cx = orig_rect.right - orig_rect.left, dest_cy = orig_rect.bottom - orig_rect.top;
|
||||||
|
|
||||||
|
int delta_x = cur_client.right - orig_client.right,
|
||||||
|
delta_y = cur_client.bottom - orig_client.bottom;
|
||||||
|
|
||||||
|
if (e.flags & X_MOVE)
|
||||||
|
dest_x += delta_x;
|
||||||
|
else if (e.flags & X_SIZE)
|
||||||
|
dest_cx += delta_x;
|
||||||
|
|
||||||
|
if (e.flags & Y_MOVE)
|
||||||
|
dest_y += delta_y;
|
||||||
|
else if (e.flags & Y_SIZE)
|
||||||
|
dest_cy += delta_y;
|
||||||
|
|
||||||
|
SetWindowPos(wnd,0,dest_x,dest_y,dest_cx,dest_cy,SWP_NOZORDER);
|
||||||
|
}
|
||||||
|
RedrawWindow(parent,0,0,RDW_INVALIDATE|RDW_UPDATENOW);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool dialog_resize_helper::process_message(HWND wnd,UINT msg,WPARAM wp,LPARAM lp)
|
||||||
|
{
|
||||||
|
switch(msg)
|
||||||
|
{
|
||||||
|
case WM_SIZE:
|
||||||
|
on_wm_size();
|
||||||
|
return true;
|
||||||
|
case WM_GETMINMAXINFO:
|
||||||
|
{
|
||||||
|
RECT r;
|
||||||
|
LPMINMAXINFO info = (LPMINMAXINFO) lp;
|
||||||
|
if (max_x && max_y)
|
||||||
|
{
|
||||||
|
r.left = 0; r.right = max_x;
|
||||||
|
r.top = 0; r.bottom = max_y;
|
||||||
|
MapDialogRect(wnd,&r);
|
||||||
|
info->ptMaxTrackSize.x = r.right;
|
||||||
|
info->ptMaxTrackSize.y = r.bottom;
|
||||||
|
}
|
||||||
|
if (min_x && min_y)
|
||||||
|
{
|
||||||
|
r.left = 0; r.right = min_x;
|
||||||
|
r.top = 0; r.bottom = min_y;
|
||||||
|
MapDialogRect(wnd,&r);
|
||||||
|
info->ptMinTrackSize.x = r.right;
|
||||||
|
info->ptMinTrackSize.y = r.bottom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
case WM_INITDIALOG:
|
||||||
|
set_parent(wnd);
|
||||||
|
{
|
||||||
|
unsigned n;
|
||||||
|
for(n=0;n<m_table_size;n++)
|
||||||
|
{
|
||||||
|
GetChildRect(parent,m_table[n].id,&rects[n]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case WM_DESTROY:
|
||||||
|
reset();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
dialog_resize_helper::dialog_resize_helper(const param * src,unsigned count,unsigned p_min_x,unsigned p_min_y,unsigned p_max_x,unsigned p_max_y)
|
||||||
|
: min_x(p_min_x), min_y(p_min_y), max_x(p_max_x), max_y(p_max_y), parent(0)
|
||||||
|
{
|
||||||
|
m_table_size = count;
|
||||||
|
m_table = new param[count];
|
||||||
|
unsigned n;
|
||||||
|
for(n=0;n<count;n++)
|
||||||
|
m_table[n] = src[n];
|
||||||
|
rects = new RECT[count];
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog_resize_helper::~dialog_resize_helper()
|
||||||
|
{
|
||||||
|
delete[] m_table;
|
||||||
|
delete[] rects;
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
#ifndef _DIALOG_RESIZE_HELPER_H_
|
||||||
|
#define _DIALOG_RESIZE_HELPER_H_
|
||||||
|
|
||||||
|
|
||||||
|
//deprecated, use dialog_resize_helper class
|
||||||
|
namespace resize
|
||||||
|
{
|
||||||
|
void calc_xy(HWND wnd,UINT id,RECT &r,RECT & o);
|
||||||
|
void calc_move_xy(HWND wnd,UINT id,RECT &r,RECT & o);
|
||||||
|
void calc_move_x(HWND wnd,UINT id,RECT &r,RECT & o);
|
||||||
|
void calc_move_x_size_y(HWND wnd,UINT id,RECT &r,RECT & o);
|
||||||
|
void calc_move_y(HWND wnd,UINT id,RECT &r,RECT & o);
|
||||||
|
void calc_x(HWND wnd,UINT id,RECT &r,RECT & o);
|
||||||
|
};
|
||||||
|
|
||||||
|
void GetChildRect(HWND wnd,UINT id,RECT* child);
|
||||||
|
|
||||||
|
|
||||||
|
class dialog_resize_helper
|
||||||
|
{
|
||||||
|
RECT * rects;
|
||||||
|
RECT orig_client;
|
||||||
|
HWND parent;
|
||||||
|
unsigned min_x,min_y,max_x,max_y;
|
||||||
|
|
||||||
|
public:
|
||||||
|
struct param {
|
||||||
|
unsigned short id;
|
||||||
|
unsigned short flags;
|
||||||
|
};
|
||||||
|
private:
|
||||||
|
param * m_table;
|
||||||
|
unsigned m_table_size;
|
||||||
|
|
||||||
|
void set_parent(HWND wnd);
|
||||||
|
void add_item(UINT id,UINT flags);
|
||||||
|
void reset();
|
||||||
|
void on_wm_size();
|
||||||
|
void add_items(const param* table,unsigned count);
|
||||||
|
public:
|
||||||
|
inline void set_min_size(unsigned x,unsigned y) {min_x = x; min_y = y;}
|
||||||
|
inline void set_max_size(unsigned x,unsigned y) {max_x = x; max_y = y;}
|
||||||
|
|
||||||
|
enum {
|
||||||
|
X_MOVE = 1, X_SIZE = 2, Y_MOVE = 4, Y_SIZE = 8,
|
||||||
|
XY_MOVE = X_MOVE|Y_MOVE, XY_SIZE = X_SIZE|Y_SIZE,
|
||||||
|
X_MOVE_Y_SIZE = X_MOVE|Y_SIZE, X_SIZE_Y_MOVE = X_SIZE|Y_MOVE,
|
||||||
|
};
|
||||||
|
bool process_message(HWND wnd,UINT msg,WPARAM wp,LPARAM lp);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
explicit dialog_resize_helper(const param * src,unsigned count,unsigned p_min_x,unsigned p_min_y,unsigned p_max_x,unsigned p_max_y);
|
||||||
|
~dialog_resize_helper();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,87 @@
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "dropdown_helper.h"
|
||||||
|
|
||||||
|
|
||||||
|
void cfg_dropdown_history::build_list(ptr_list_simple<char> & out)
|
||||||
|
{
|
||||||
|
const char * src = data;
|
||||||
|
while(*src)
|
||||||
|
{
|
||||||
|
int ptr = 0;
|
||||||
|
while(src[ptr] && src[ptr]!=separator) ptr++;
|
||||||
|
if (ptr>0)
|
||||||
|
{
|
||||||
|
out.add_item(strdup_n(src,ptr));
|
||||||
|
src += ptr;
|
||||||
|
}
|
||||||
|
while(*src==separator) src++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cfg_dropdown_history::parse_list(const ptr_list_simple<char> & src)
|
||||||
|
{
|
||||||
|
unsigned n;
|
||||||
|
string8 temp;
|
||||||
|
temp.set_mem_logic(mem_block::ALLOC_FAST);
|
||||||
|
for(n=0;n<src.get_count();n++)
|
||||||
|
{
|
||||||
|
temp.add_string(src[n]);
|
||||||
|
temp.add_char(separator);
|
||||||
|
}
|
||||||
|
data = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cfg_dropdown_history::setup_dropdown(HWND wnd)
|
||||||
|
{
|
||||||
|
ptr_list_simple<char> list;
|
||||||
|
build_list(list);
|
||||||
|
unsigned n;
|
||||||
|
for(n=0;n<list.get_count();n++)
|
||||||
|
{
|
||||||
|
uSendMessageText(wnd,CB_ADDSTRING,0,list[n]);
|
||||||
|
}
|
||||||
|
list.free_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
void cfg_dropdown_history::add_item(const char * item)
|
||||||
|
{
|
||||||
|
if (!item || !*item) return;
|
||||||
|
string8 meh;
|
||||||
|
if (strchr(item,separator))
|
||||||
|
{
|
||||||
|
uReplaceChar(meh,item,-1,separator,'|',false);
|
||||||
|
item = meh;
|
||||||
|
}
|
||||||
|
ptr_list_simple<char> list;
|
||||||
|
build_list(list);
|
||||||
|
unsigned n;
|
||||||
|
bool found = false;
|
||||||
|
for(n=0;n<list.get_count();n++)
|
||||||
|
{
|
||||||
|
if (!strcmp(list[n],item))
|
||||||
|
{
|
||||||
|
char* temp = list.remove_by_idx(n);
|
||||||
|
list.insert_item(temp,0);
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
while(list.get_count() > max) list.delete_by_idx(list.get_count()-1);
|
||||||
|
list.insert_item(strdup(item),0);
|
||||||
|
}
|
||||||
|
parse_list(list);
|
||||||
|
list.free_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cfg_dropdown_history::is_empty()
|
||||||
|
{
|
||||||
|
const char * src = data;
|
||||||
|
while(*src)
|
||||||
|
{
|
||||||
|
if (*src!=separator) return false;
|
||||||
|
src++;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
#ifndef _DROPDOWN_HELPER_H_
|
||||||
|
#define _DROPDOWN_HELPER_H_
|
||||||
|
|
||||||
|
|
||||||
|
class cfg_dropdown_history
|
||||||
|
{
|
||||||
|
enum {separator = '\n'};
|
||||||
|
cfg_string data;
|
||||||
|
unsigned max;
|
||||||
|
void build_list(ptr_list_simple<char> & out);
|
||||||
|
void parse_list(const ptr_list_simple<char> & src);
|
||||||
|
public:
|
||||||
|
cfg_dropdown_history(const char * name,unsigned p_max = 10,const char * init_vals = "") : data(name,init_vals) {max = p_max;}
|
||||||
|
void setup_dropdown(HWND wnd);
|
||||||
|
void setup_dropdown(HWND wnd,UINT id) {setup_dropdown(GetDlgItem(wnd,id));}
|
||||||
|
void add_item(const char * item);
|
||||||
|
bool is_empty();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //_DROPDOWN_HELPER_H_
|
|
@ -0,0 +1,165 @@
|
||||||
|
# Microsoft Developer Studio Project File - Name="foobar2000_sdk_helpers" - Package Owner=<4>
|
||||||
|
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||||
|
# ** DO NOT EDIT **
|
||||||
|
|
||||||
|
# TARGTYPE "Win32 (x86) Static Library" 0x0104
|
||||||
|
|
||||||
|
CFG=foobar2000_sdk_helpers - Win32 Debug
|
||||||
|
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
||||||
|
!MESSAGE use the Export Makefile command and run
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE NMAKE /f "foobar2000_sdk_helpers.mak".
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE You can specify a configuration when running NMAKE
|
||||||
|
!MESSAGE by defining the macro CFG on the command line. For example:
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE NMAKE /f "foobar2000_sdk_helpers.mak" CFG="foobar2000_sdk_helpers - Win32 Debug"
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE Possible choices for configuration are:
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE "foobar2000_sdk_helpers - Win32 Release" (based on "Win32 (x86) Static Library")
|
||||||
|
!MESSAGE "foobar2000_sdk_helpers - Win32 Debug" (based on "Win32 (x86) Static Library")
|
||||||
|
!MESSAGE
|
||||||
|
|
||||||
|
# Begin Project
|
||||||
|
# PROP AllowPerConfigDependencies 0
|
||||||
|
# PROP Scc_ProjName ""
|
||||||
|
# PROP Scc_LocalPath ""
|
||||||
|
CPP=cl.exe
|
||||||
|
RSC=rc.exe
|
||||||
|
|
||||||
|
!IF "$(CFG)" == "foobar2000_sdk_helpers - Win32 Release"
|
||||||
|
|
||||||
|
# PROP BASE Use_MFC 0
|
||||||
|
# PROP BASE Use_Debug_Libraries 0
|
||||||
|
# PROP BASE Output_Dir "Release"
|
||||||
|
# PROP BASE Intermediate_Dir "Release"
|
||||||
|
# PROP BASE Target_Dir ""
|
||||||
|
# PROP Use_MFC 0
|
||||||
|
# PROP Use_Debug_Libraries 0
|
||||||
|
# PROP Output_Dir "Release"
|
||||||
|
# PROP Intermediate_Dir "Release"
|
||||||
|
# PROP Target_Dir ""
|
||||||
|
F90=df.exe
|
||||||
|
MTL=midl.exe
|
||||||
|
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Yu"stdafx.h" /FD /c
|
||||||
|
# ADD CPP /nologo /MD /W3 /O1 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Yu"stdafx.h" /FD /c
|
||||||
|
# ADD BASE RSC /l 0x409 /d "NDEBUG"
|
||||||
|
# ADD RSC /l 0x409 /d "NDEBUG"
|
||||||
|
BSC32=bscmake.exe
|
||||||
|
# ADD BASE BSC32 /nologo
|
||||||
|
# ADD BSC32 /nologo
|
||||||
|
LIB32=link.exe -lib
|
||||||
|
# ADD BASE LIB32 /nologo
|
||||||
|
# ADD LIB32 /nologo
|
||||||
|
|
||||||
|
!ELSEIF "$(CFG)" == "foobar2000_sdk_helpers - Win32 Debug"
|
||||||
|
|
||||||
|
# PROP BASE Use_MFC 0
|
||||||
|
# PROP BASE Use_Debug_Libraries 1
|
||||||
|
# PROP BASE Output_Dir "Debug"
|
||||||
|
# PROP BASE Intermediate_Dir "Debug"
|
||||||
|
# PROP BASE Target_Dir ""
|
||||||
|
# PROP Use_MFC 0
|
||||||
|
# PROP Use_Debug_Libraries 1
|
||||||
|
# PROP Output_Dir "Debug"
|
||||||
|
# PROP Intermediate_Dir "Debug"
|
||||||
|
# PROP Target_Dir ""
|
||||||
|
F90=df.exe
|
||||||
|
MTL=midl.exe
|
||||||
|
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Yu"stdafx.h" /FD /GZ /c
|
||||||
|
# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Yu"stdafx.h" /FD /GZ /c
|
||||||
|
# ADD BASE RSC /l 0x409 /d "_DEBUG"
|
||||||
|
# ADD RSC /l 0x409 /d "_DEBUG"
|
||||||
|
BSC32=bscmake.exe
|
||||||
|
# ADD BASE BSC32 /nologo
|
||||||
|
# ADD BSC32 /nologo
|
||||||
|
LIB32=link.exe -lib
|
||||||
|
# ADD BASE LIB32 /nologo
|
||||||
|
# ADD LIB32 /nologo
|
||||||
|
|
||||||
|
!ENDIF
|
||||||
|
|
||||||
|
# Begin Target
|
||||||
|
|
||||||
|
# Name "foobar2000_sdk_helpers - Win32 Release"
|
||||||
|
# Name "foobar2000_sdk_helpers - Win32 Debug"
|
||||||
|
# Begin Group "Source Files"
|
||||||
|
|
||||||
|
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\create_directory_helper.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\dialog_resize_helper.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\dropdown_helper.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\playback_order_helper.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\StdAfx.cpp
|
||||||
|
# ADD CPP /Yc"stdafx.h"
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\wildcard.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\win32_dialog.cpp
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\window_placement_helper.cpp
|
||||||
|
# End Source File
|
||||||
|
# End Group
|
||||||
|
# Begin Group "Header Files"
|
||||||
|
|
||||||
|
# PROP Default_Filter "h;hpp;hxx;hm;inl"
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\create_directory_helper.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\dialog_resize_helper.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\dropdown_helper.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\helpers.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\playback_order_helper.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\StdAfx.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\wildcard.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\win32_dialog.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\window_placement_helper.h
|
||||||
|
# End Source File
|
||||||
|
# End Group
|
||||||
|
# End Target
|
||||||
|
# End Project
|
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef _FOOBAR2000_SDK_HELPERS_H_
|
||||||
|
#define _FOOBAR2000_SDK_HELPERS_H_
|
||||||
|
|
||||||
|
#include "create_directory_helper.h"
|
||||||
|
#include "dialog_resize_helper.h"
|
||||||
|
#include "dropdown_helper.h"
|
||||||
|
#include "window_placement_helper.h"
|
||||||
|
#include "win32_dialog.h"
|
||||||
|
#include "playback_order_helper.h"
|
||||||
|
#include "wildcard.h"
|
||||||
|
|
||||||
|
#endif //_FOOBAR2000_SDK_HELPERS_H_
|
|
@ -0,0 +1,82 @@
|
||||||
|
#include "stdafx.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace playback_order_helper
|
||||||
|
{
|
||||||
|
static const char variable_name[] = "CORE/Playback flow control";
|
||||||
|
|
||||||
|
unsigned get_count()
|
||||||
|
{
|
||||||
|
return service_enum_get_count_t(playback_flow_control);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * get_name_from_index(unsigned idx)
|
||||||
|
{
|
||||||
|
const char * ret = 0;
|
||||||
|
assert(idx>=0);
|
||||||
|
assert(idx<get_count());
|
||||||
|
playback_flow_control * ptr = service_enum_create_t(playback_flow_control,idx);
|
||||||
|
assert(ptr);
|
||||||
|
ret = ptr->get_name();
|
||||||
|
ptr->service_release();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned get_index_from_name(const char * name)
|
||||||
|
{
|
||||||
|
unsigned idx, max = get_count();
|
||||||
|
for(idx=0;idx<max;idx++)
|
||||||
|
{
|
||||||
|
const char * ptr = get_name_from_index(idx);
|
||||||
|
assert(ptr);
|
||||||
|
if (!stricmp_utf8(ptr,name))
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
return order_invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned get_config_by_index()
|
||||||
|
{
|
||||||
|
string8 name;
|
||||||
|
get_config_by_name(name);
|
||||||
|
return get_index_from_name(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * get_config_by_name()
|
||||||
|
{
|
||||||
|
unsigned idx = get_config_by_index();
|
||||||
|
if (idx==order_invalid) return "Default";
|
||||||
|
else return get_name_from_index(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_config_by_name(string_base & out)
|
||||||
|
{
|
||||||
|
config_var_string::g_get_value(variable_name,out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_config_by_name(const char * name)
|
||||||
|
{
|
||||||
|
assert(core_api::is_main_thread());
|
||||||
|
config_var_string::g_set_value(variable_name,name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_config_by_index(unsigned idx)
|
||||||
|
{
|
||||||
|
assert(core_api::is_main_thread());
|
||||||
|
const char * name = get_name_from_index(idx);
|
||||||
|
assert(name);
|
||||||
|
set_config_by_name(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
modify_callback::modify_callback(void (*p_func)(const char * newval,unsigned newidx),bool calloninit)
|
||||||
|
: config_var_callback_autoreg<config_var_string>(variable_name,calloninit), func(p_func) {}
|
||||||
|
|
||||||
|
void modify_callback::on_changed(const config_var_string * ptr)
|
||||||
|
{
|
||||||
|
string8 name;
|
||||||
|
ptr->get_value(name);
|
||||||
|
func(name,get_index_from_name(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef _FOOBAR2000_PLAYBACK_ORDER_HELPER_H_
|
||||||
|
#define _FOOBAR2000_PLAYBACK_ORDER_HELPER_H_
|
||||||
|
|
||||||
|
namespace playback_order_helper {
|
||||||
|
enum { order_invalid = (unsigned)(-1) };
|
||||||
|
unsigned get_count();
|
||||||
|
const char * get_name_from_index(unsigned idx); //index is 0 ... get_count()-1
|
||||||
|
unsigned get_index_from_name(const char * name);//returns order_invalid if not found
|
||||||
|
unsigned get_config_by_index();//order_invalid on error
|
||||||
|
const char * get_config_by_name();//reverts to "default" on error
|
||||||
|
void get_config_by_name(string_base & out);//may return name of order that doesn't exist anymore (e.g. user removing dlls)
|
||||||
|
void set_config_by_name(const char * name);
|
||||||
|
void set_config_by_index(unsigned idx);
|
||||||
|
|
||||||
|
class modify_callback : public config_var_callback_autoreg<config_var_string>
|
||||||
|
{
|
||||||
|
void (*func)(const char * newval,unsigned newidx);
|
||||||
|
virtual void on_changed(const config_var_string * ptr);
|
||||||
|
public:
|
||||||
|
modify_callback(void (*p_func)(const char * newval,unsigned newidx), bool calloninit = false);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
inline order_exists(const char * name) {return get_index_from_name(name)!=order_invalid;}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,44 @@
|
||||||
|
#include "stdafx.h"
|
||||||
|
|
||||||
|
static bool test_recur(const char * fn,const char * rm,bool b_sep)
|
||||||
|
{
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
if ((b_sep && *rm==';') || *rm==0) return *fn==0;
|
||||||
|
else if (*rm=='*')
|
||||||
|
{
|
||||||
|
rm++;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (test_recur(fn,rm,b_sep)) return true;
|
||||||
|
} while(utf8_advance(fn));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (*fn==0) return false;
|
||||||
|
else if (*rm!='?' && char_lower(utf8_get_char(fn))!=char_lower(utf8_get_char(rm))) return false;
|
||||||
|
|
||||||
|
fn = utf8_char_next(fn); rm = utf8_char_next(rm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wildcard_helper::test_path(const char * path,const char * pattern,bool b_sep) {return test(path + string8::g_scan_filename(path),pattern,b_sep);}
|
||||||
|
|
||||||
|
bool wildcard_helper::test(const char * fn,const char * pattern,bool b_sep)
|
||||||
|
{
|
||||||
|
if (!b_sep) return test_recur(fn,pattern,false);
|
||||||
|
const char * rm=pattern;
|
||||||
|
while(*rm)
|
||||||
|
{
|
||||||
|
if (test_recur(fn,rm,true)) return true;
|
||||||
|
while(*rm && *rm!=';') rm++;
|
||||||
|
if (*rm==';')
|
||||||
|
{
|
||||||
|
while(*rm==';') rm++;
|
||||||
|
while(*rm==' ') rm++;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wildcard_helper::has_wildcards(const char * str) {return strchr(str,'*') || strchr(str,'?');}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue