Updated MSVC6 project files, bundled necessary parts of foobar2000 0.8.3 SDK

This commit is contained in:
kode54 2010-01-09 01:53:29 +00:00
parent 7a88e3e80d
commit 16c57263de
146 changed files with 20954 additions and 31 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}
}

View File

@ -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_

View File

@ -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));
}
}

View File

@ -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_

View File

@ -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

View File

@ -0,0 +1,6 @@
#ifndef _COMPONENT_CLIENT_H_
#define _COMPONENT_CLIENT_H_
#endif //_COMPONENT_CLIENT_H_

View File

@ -0,0 +1,7 @@
#ifndef _COMPONENTS_MENU_H_
#define _COMPONENTS_MENU_H_
#error deprecated, see menu_item.h
#endif

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -0,0 +1 @@
#error DEPRECATED

View File

@ -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

View File

@ -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_

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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_

View File

@ -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_

View File

@ -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_

View File

@ -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

View File

@ -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 } };

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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_

View File

@ -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;
}

View File

@ -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_

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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_

View File

@ -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();
}
}

View File

@ -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_

View File

@ -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);
}

View File

@ -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

View File

@ -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_

View File

@ -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;
}

View File

@ -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_

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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_

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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_

View File

@ -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_

View File

@ -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;
}

View File

@ -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

View File

@ -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_

View File

@ -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_

View File

@ -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_

View File

@ -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_

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1 @@
#error DEPRECATED

View File

@ -0,0 +1,2 @@
//cpp used to generate precompiled header
#include "foobar2000.h"

View File

@ -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;
}

View File

@ -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

View File

@ -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++);
}
}

View File

@ -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_

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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_)

View File

@ -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;
}
}
}

View File

@ -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_

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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_

View File

@ -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

View File

@ -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_

View File

@ -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));
}
}

View File

@ -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

View File

@ -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