//#define PLUGIN_NAME "Nullsoft Waveform Decoder" #define PLUGIN_VERSION L"3.27" #include "../Winamp/in2.h" #include "../Winamp/wa_ipc.h" #include "main.h" #include "AudioThread.h" #include "resource.h" #include "config.h" #include "api__in_wave.h" #include #include "../Agave/Language/api_language.h" #include #include "../nu/ns_wc.h" #include "../nu/AutoWide.h" #include "../nu/AutoCharFn.h" #include "VirtualIO.h" #include #include "../nu/Singleton.h" #include "RawReader.h" api_config *AGAVE_API_CONFIG = NULL; // wasabi based services for localisation support api_language *WASABI_API_LNG = NULL; HINSTANCE WASABI_API_LNG_HINST = 0; HINSTANCE WASABI_API_ORIG_HINST = 0; static RawMediaReaderService raw_media_reader_service; static SingletonServiceFactory raw_factory; template void ServiceBuild( api_T *&api_t, GUID factoryGUID_t ) { if ( WASABI_API_SVC ) { waServiceFactory *factory = WASABI_API_SVC->service_getServiceByGuid( factoryGUID_t ); if ( factory ) api_t = reinterpret_cast( factory->getInterface() ); } } template void ServiceRelease( api_T *api_t, GUID factoryGUID_t ) { if ( WASABI_API_SVC && api_t ) { waServiceFactory *factory = WASABI_API_SVC->service_getServiceByGuid( factoryGUID_t ); if ( factory ) factory->releaseInterface( api_t ); } api_t = NULL; } volatile int currentSongLength = 0; SNDFILE *sndFile = NULL; wchar_t curFile[MAX_PATH*4] = L""; char *INI_FILE; class SoundFile { public: SoundFile( const wchar_t *filename, int mode, SF_INFO *info ) { info->format = 0; //reader = CreateUnicodeReader(filename); //if (reader) //sndFile = sf_open_virtual(&unicode_io, SFM_READ, info, reader); sndFile = sf_wchar_open( filename, SFM_READ, info ); } ~SoundFile() { if ( sndFile ) sf_close( sndFile ); //if (reader) //DestroyUnicodeReader(reader); sndFile = NULL; } operator SNDFILE *() { return sndFile; } SNDFILE *operator ->() { return sndFile; } operator bool() { return !!sndFile; } bool operator !() { return !sndFile; } SNDFILE *sndFile = NULL; //void *reader; }; void Config( HWND hwnd ) { WASABI_API_DIALOGBOXW( IDD_CONFIG, hwnd, PreferencesDialogProc ); } int DoAboutMessageBox( HWND parent, wchar_t *title, wchar_t *message ) { MSGBOXPARAMSW msgbx = { sizeof( MSGBOXPARAMSW ),0 }; msgbx.lpszText = message; msgbx.lpszCaption = title; msgbx.lpszIcon = MAKEINTRESOURCEW( 102 ); msgbx.hInstance = GetModuleHandle( 0 ); msgbx.dwStyle = MB_USERICON; msgbx.hwndOwner = parent; return MessageBoxIndirectW( &msgbx ); } void About( HWND hwndParent ) { wchar_t message[ 1024 ] = { 0 }, text[ 1024 ] = { 0 }; char ver[ 128 ] = { 0 }; sf_command( 0, SFC_GET_LIB_VERSION, ver, 128 ); WASABI_API_LNGSTRINGW_BUF( IDS_NULLSOFT_WAVEFORM_DECODER_OLD, text, 1024 ); StringCchPrintfW( message, 1024, WASABI_API_LNGSTRINGW( IDS_ABOUT_TEXT ), plugin.description, __DATE__, ver ); DoAboutMessageBox( hwndParent, text, message ); } int Init() { if ( !IsWindow( plugin.hMainWindow ) ) return IN_INIT_FAILURE; ServiceBuild( AGAVE_API_CONFIG, AgaveConfigGUID ); // loader so that we can get the localisation service api for use ServiceBuild( WASABI_API_LNG, languageApiGUID ); raw_factory.Register( WASABI_API_SVC, &raw_media_reader_service ); // need to have this initialised before we try to do anything with localisation features WASABI_API_START_LANG( plugin.hDllInstance, InWavLangGUID ); static wchar_t szDescription[ 256 ]; StringCchPrintfW( szDescription, 256, WASABI_API_LNGSTRINGW( IDS_NULLSOFT_WAVEFORM_DECODER ), PLUGIN_VERSION ); plugin.description = (char *)szDescription; INI_FILE = (char *)SendMessage( plugin.hMainWindow, WM_WA_IPC, 0, IPC_GETINIFILE ); BuildDefaultExtensions(); GetPrivateProfileStringA( "in_wave", "extensions", defaultExtensions, config_extensions, 1024, INI_FILE ); SetFileExtensions( config_extensions ); return IN_INIT_SUCCESS; } void Quit() { if ( lstrcmpiA( config_extensions, defaultExtensions ) ) WritePrivateProfileStringA( "in_wave", "extensions", config_extensions, INI_FILE ); else WritePrivateProfileStringA( "in_wave", "extensions", 0, INI_FILE ); ServiceRelease( AGAVE_API_CONFIG, AgaveConfigGUID ); WASABI_API_SVC->service_deregister( &raw_factory ); } void GetFileInfo( const wchar_t *file, wchar_t *title, int *length_in_ms ) { SNDFILE *tempSndFile = 0; SF_INFO info; info.format = 0; const wchar_t *fn = ( file && file[ 0 ] ) ? file : curFile; tempSndFile = sf_wchar_open( fn, SFM_READ, &info ); if ( tempSndFile ) { if ( length_in_ms ) { *length_in_ms = MulDiv( (int)info.frames, 1000, info.samplerate ); // TODO: is this correct? if ( !file || !file[ 0 ] ) currentSongLength = *length_in_ms; } if ( title ) { const char *meta = sf_get_string( tempSndFile, SF_STR_TITLE ); if ( meta && meta[ 0 ] ) MultiByteToWideCharSZ( CP_UTF8, 0, meta, -1, title, GETFILEINFO_TITLE_LENGTH ); else { lstrcpynW( title, fn, GETFILEINFO_TITLE_LENGTH ); PathStripPathW( title ); } } sf_close( tempSndFile ); } else { *length_in_ms = -1; if ( title ) { lstrcpynW( title, fn, GETFILEINFO_TITLE_LENGTH ); PathStripPathW( title ); } } } int InfoBox( const wchar_t *file, HWND hwndParent ) { SNDFILE *metaFile = 0; SF_INFO info; info.format = 0; metaFile = sf_wchar_open( file, SFM_READ, &info ); if ( metaFile ) { SF_FORMAT_INFO formatInfo; formatInfo.format = info.format & SF_FORMAT_SUBMASK; sf_command( 0, SFC_GET_FORMAT_INFO, &formatInfo, sizeof( formatInfo ) ); char temp[ 1024 ] = { 0 }; StringCchPrintfA( temp, 1024, WASABI_API_LNGSTRING( IDS_INFO_STR_FMT ), formatInfo.name, info.channels, info.samplerate ); MessageBoxA( NULL, temp, WASABI_API_LNGSTRING( IDS_FILE_INFORMATION ), MB_OK ); sf_close( metaFile ); } return INFOBOX_UNCHANGED; } int IsOurFile( const wchar_t *file ) { return 0; } int Play( const wchar_t *file ) { AudioThreadInit(); lstrcpynW( curFile, file, MAX_PATH * 4 ); QueueUserAPC( APCStart, audioThread, (ULONG_PTR)curFile ); return 0; } static int paused = 0; void Pause() { paused = 1; QueueUserAPC( APCPause, audioThread, (ULONG_PTR)1 ); } void UnPause() { paused = 0; QueueUserAPC( APCPause, audioThread, (ULONG_PTR)0 ); } int IsPaused() { return paused; } void Stop() { QueueUserAPC( APCStop, audioThread, (ULONG_PTR)0 ); WaitForSingleObject( stopped, INFINITE ); plugin.outMod->Close(); plugin.SAVSADeInit(); Kill(); WaitForSingleObject( audioThread, INFINITE ); AudioThreadQuit(); } int GetLength() { return currentSongLength; } int GetOutputTime() { if ( plugin.outMod ) return plugin.outMod->GetOutputTime(); else return 0; } void SetOutputTime( int time_in_ms ) { QueueUserAPC( APCSeek, audioThread, (ULONG_PTR)time_in_ms ); } int pan = 0; int volume = -666; void SetVolume( int _volume ) { volume = _volume; if ( plugin.outMod ) plugin.outMod->SetVolume( volume ); } void SetPan( int _pan ) { pan = _pan; if ( plugin.outMod ) plugin.outMod->SetPan( pan ); } void EQSet( int on, char data[ 10 ], int preamp ) {} In_Module plugin = { IN_VER_RET, "nullsoft(in_wave.dll)", 0, 0, 0, 1, 1, Config, About, Init, Quit, GetFileInfo, InfoBox, IsOurFile, Play, Pause, UnPause, IsPaused, Stop, GetLength, GetOutputTime, SetOutputTime, SetVolume, SetPan, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, EQSet, 0, 0 }; extern "C" __declspec( dllexport ) In_Module * winampGetInModule2() { return &plugin; } inline bool KeywordMatch(const char *mainString, const char *keyword) { return !lstrcmpiA(mainString, keyword); } extern "C" __declspec( dllexport ) int winampGetExtendedFileInfoW( const wchar_t *fn, const char *data, wchar_t *dest, int destlen ) { if ( KeywordMatch( data, "type" ) ) { StringCchCopyW( dest, destlen, L"0" ); return 1; } if ( KeywordMatch( data, "family" ) ) { LPCWSTR ext = PathFindExtensionW( fn ); if ( L'.' != *ext ) return 0; return GetExtensionName( ++ext, dest, destlen ); } if ( KeywordMatch( data, "mime" ) ) { LPCWSTR ext = PathFindExtensionW( fn ); if ( ext && !_wcsicmp( ext, L".wav" ) ) { StringCchCopyW( dest, destlen, L"audio/wav" ); return 1; } return 0; } if ( !fn || ( fn && !fn[ 0 ] ) ) return 0; SF_INFO info; SoundFile metaFile( fn, SFM_READ, &info ); if ( !metaFile ) return 0; dest[ 0 ] = 0; if ( KeywordMatch( data, "artist" ) ) { const char *meta = sf_get_string( metaFile, SF_STR_ARTIST ); if ( meta ) lstrcpynW( dest, AutoWide( meta ), destlen ); } else if ( KeywordMatch( data, "title" ) ) { const char *meta = sf_get_string( metaFile, SF_STR_TITLE ); if ( meta ) lstrcpynW( dest, AutoWide( meta ), destlen ); } else if ( KeywordMatch( data, "comment" ) ) { const char *meta = sf_get_string( metaFile, SF_STR_COMMENT ); if ( meta ) lstrcpynW( dest, AutoWide( meta ), destlen ); } else if ( KeywordMatch( data, "bitrate" ) ) { int br = CalcBitRate( &info ); if ( br ) StringCchPrintfW( dest, destlen, L"%d", br ); } else if ( KeywordMatch( data, "length" ) ) { uint64_t length = info.frames * 1000 / info.samplerate; StringCchPrintfW( dest, destlen, L"%I64u", length ); } else return 0; return 1; }