main.cpp 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. //#define PLUGIN_NAME "Nullsoft Waveform Decoder"
  2. #define PLUGIN_VERSION L"3.27"
  3. #include "../Winamp/in2.h"
  4. #include "../Winamp/wa_ipc.h"
  5. #include "main.h"
  6. #include "AudioThread.h"
  7. #include "resource.h"
  8. #include "config.h"
  9. #include "api__in_wave.h"
  10. #include <shlwapi.h>
  11. #include "../Agave/Language/api_language.h"
  12. #include <api/service/waservicefactory.h>
  13. #include "../nu/ns_wc.h"
  14. #include "../nu/AutoWide.h"
  15. #include "../nu/AutoCharFn.h"
  16. #include "VirtualIO.h"
  17. #include <strsafe.h>
  18. #include "../nu/Singleton.h"
  19. #include "RawReader.h"
  20. api_config *AGAVE_API_CONFIG = NULL;
  21. // wasabi based services for localisation support
  22. api_language *WASABI_API_LNG = NULL;
  23. HINSTANCE WASABI_API_LNG_HINST = 0;
  24. HINSTANCE WASABI_API_ORIG_HINST = 0;
  25. static RawMediaReaderService raw_media_reader_service;
  26. static SingletonServiceFactory<svc_raw_media_reader, RawMediaReaderService> raw_factory;
  27. template <class api_T>
  28. void ServiceBuild( api_T *&api_t, GUID factoryGUID_t )
  29. {
  30. if ( WASABI_API_SVC )
  31. {
  32. waServiceFactory *factory = WASABI_API_SVC->service_getServiceByGuid( factoryGUID_t );
  33. if ( factory )
  34. api_t = reinterpret_cast<api_T *>( factory->getInterface() );
  35. }
  36. }
  37. template <class api_T>
  38. void ServiceRelease( api_T *api_t, GUID factoryGUID_t )
  39. {
  40. if ( WASABI_API_SVC && api_t )
  41. {
  42. waServiceFactory *factory = WASABI_API_SVC->service_getServiceByGuid( factoryGUID_t );
  43. if ( factory )
  44. factory->releaseInterface( api_t );
  45. }
  46. api_t = NULL;
  47. }
  48. volatile int currentSongLength = 0;
  49. SNDFILE *sndFile = NULL;
  50. wchar_t curFile[MAX_PATH*4] = L"";
  51. char *INI_FILE;
  52. class SoundFile
  53. {
  54. public:
  55. SoundFile( const wchar_t *filename, int mode, SF_INFO *info )
  56. {
  57. info->format = 0;
  58. //reader = CreateUnicodeReader(filename);
  59. //if (reader)
  60. //sndFile = sf_open_virtual(&unicode_io, SFM_READ, info, reader);
  61. sndFile = sf_wchar_open( filename, SFM_READ, info );
  62. }
  63. ~SoundFile()
  64. {
  65. if ( sndFile )
  66. sf_close( sndFile );
  67. //if (reader)
  68. //DestroyUnicodeReader(reader);
  69. sndFile = NULL;
  70. }
  71. operator SNDFILE *() { return sndFile; }
  72. SNDFILE *operator ->() { return sndFile; }
  73. operator bool() { return !!sndFile; }
  74. bool operator !() { return !sndFile; }
  75. SNDFILE *sndFile = NULL;
  76. //void *reader;
  77. };
  78. void Config( HWND hwnd )
  79. {
  80. WASABI_API_DIALOGBOXW( IDD_CONFIG, hwnd, PreferencesDialogProc );
  81. }
  82. int DoAboutMessageBox( HWND parent, wchar_t *title, wchar_t *message )
  83. {
  84. MSGBOXPARAMSW msgbx = { sizeof( MSGBOXPARAMSW ),0 };
  85. msgbx.lpszText = message;
  86. msgbx.lpszCaption = title;
  87. msgbx.lpszIcon = MAKEINTRESOURCEW( 102 );
  88. msgbx.hInstance = GetModuleHandle( 0 );
  89. msgbx.dwStyle = MB_USERICON;
  90. msgbx.hwndOwner = parent;
  91. return MessageBoxIndirectW( &msgbx );
  92. }
  93. void About( HWND hwndParent )
  94. {
  95. wchar_t message[ 1024 ] = { 0 }, text[ 1024 ] = { 0 };
  96. char ver[ 128 ] = { 0 };
  97. sf_command( 0, SFC_GET_LIB_VERSION, ver, 128 );
  98. WASABI_API_LNGSTRINGW_BUF( IDS_NULLSOFT_WAVEFORM_DECODER_OLD, text, 1024 );
  99. StringCchPrintfW( message, 1024, WASABI_API_LNGSTRINGW( IDS_ABOUT_TEXT ), plugin.description, __DATE__, ver );
  100. DoAboutMessageBox( hwndParent, text, message );
  101. }
  102. int Init()
  103. {
  104. if ( !IsWindow( plugin.hMainWindow ) )
  105. return IN_INIT_FAILURE;
  106. ServiceBuild( AGAVE_API_CONFIG, AgaveConfigGUID );
  107. // loader so that we can get the localisation service api for use
  108. ServiceBuild( WASABI_API_LNG, languageApiGUID );
  109. raw_factory.Register( WASABI_API_SVC, &raw_media_reader_service );
  110. // need to have this initialised before we try to do anything with localisation features
  111. WASABI_API_START_LANG( plugin.hDllInstance, InWavLangGUID );
  112. static wchar_t szDescription[ 256 ];
  113. StringCchPrintfW( szDescription, 256, WASABI_API_LNGSTRINGW( IDS_NULLSOFT_WAVEFORM_DECODER ), PLUGIN_VERSION );
  114. plugin.description = (char *)szDescription;
  115. INI_FILE = (char *)SendMessage( plugin.hMainWindow, WM_WA_IPC, 0, IPC_GETINIFILE );
  116. BuildDefaultExtensions();
  117. GetPrivateProfileStringA( "in_wave", "extensions", defaultExtensions, config_extensions, 1024, INI_FILE );
  118. SetFileExtensions( config_extensions );
  119. return IN_INIT_SUCCESS;
  120. }
  121. void Quit()
  122. {
  123. if ( lstrcmpiA( config_extensions, defaultExtensions ) )
  124. WritePrivateProfileStringA( "in_wave", "extensions", config_extensions, INI_FILE );
  125. else
  126. WritePrivateProfileStringA( "in_wave", "extensions", 0, INI_FILE );
  127. ServiceRelease( AGAVE_API_CONFIG, AgaveConfigGUID );
  128. WASABI_API_SVC->service_deregister( &raw_factory );
  129. }
  130. void GetFileInfo( const wchar_t *file, wchar_t *title, int *length_in_ms )
  131. {
  132. SNDFILE *tempSndFile = 0;
  133. SF_INFO info;
  134. info.format = 0;
  135. const wchar_t *fn = ( file && file[ 0 ] ) ? file : curFile;
  136. tempSndFile = sf_wchar_open( fn, SFM_READ, &info );
  137. if ( tempSndFile )
  138. {
  139. if ( length_in_ms )
  140. {
  141. *length_in_ms = MulDiv( (int)info.frames, 1000, info.samplerate ); // TODO: is this correct?
  142. if ( !file || !file[ 0 ] )
  143. currentSongLength = *length_in_ms;
  144. }
  145. if ( title )
  146. {
  147. const char *meta = sf_get_string( tempSndFile, SF_STR_TITLE );
  148. if ( meta && meta[ 0 ] )
  149. MultiByteToWideCharSZ( CP_UTF8, 0, meta, -1, title, GETFILEINFO_TITLE_LENGTH );
  150. else
  151. {
  152. lstrcpynW( title, fn, GETFILEINFO_TITLE_LENGTH );
  153. PathStripPathW( title );
  154. }
  155. }
  156. sf_close( tempSndFile );
  157. }
  158. else
  159. {
  160. *length_in_ms = -1;
  161. if ( title )
  162. {
  163. lstrcpynW( title, fn, GETFILEINFO_TITLE_LENGTH );
  164. PathStripPathW( title );
  165. }
  166. }
  167. }
  168. int InfoBox( const wchar_t *file, HWND hwndParent )
  169. {
  170. SNDFILE *metaFile = 0;
  171. SF_INFO info;
  172. info.format = 0;
  173. metaFile = sf_wchar_open( file, SFM_READ, &info );
  174. if ( metaFile )
  175. {
  176. SF_FORMAT_INFO formatInfo;
  177. formatInfo.format = info.format & SF_FORMAT_SUBMASK;
  178. sf_command( 0, SFC_GET_FORMAT_INFO, &formatInfo, sizeof( formatInfo ) );
  179. char temp[ 1024 ] = { 0 };
  180. StringCchPrintfA( temp, 1024, WASABI_API_LNGSTRING( IDS_INFO_STR_FMT ), formatInfo.name, info.channels, info.samplerate );
  181. MessageBoxA( NULL, temp, WASABI_API_LNGSTRING( IDS_FILE_INFORMATION ), MB_OK );
  182. sf_close( metaFile );
  183. }
  184. return INFOBOX_UNCHANGED;
  185. }
  186. int IsOurFile( const wchar_t *file )
  187. {
  188. return 0;
  189. }
  190. int Play( const wchar_t *file )
  191. {
  192. AudioThreadInit();
  193. lstrcpynW( curFile, file, MAX_PATH * 4 );
  194. QueueUserAPC( APCStart, audioThread, (ULONG_PTR)curFile );
  195. return 0;
  196. }
  197. static int paused = 0;
  198. void Pause()
  199. {
  200. paused = 1;
  201. QueueUserAPC( APCPause, audioThread, (ULONG_PTR)1 );
  202. }
  203. void UnPause()
  204. {
  205. paused = 0;
  206. QueueUserAPC( APCPause, audioThread, (ULONG_PTR)0 );
  207. }
  208. int IsPaused()
  209. {
  210. return paused;
  211. }
  212. void Stop()
  213. {
  214. QueueUserAPC( APCStop, audioThread, (ULONG_PTR)0 );
  215. WaitForSingleObject( stopped, INFINITE );
  216. plugin.outMod->Close();
  217. plugin.SAVSADeInit();
  218. Kill();
  219. WaitForSingleObject( audioThread, INFINITE );
  220. AudioThreadQuit();
  221. }
  222. int GetLength()
  223. {
  224. return currentSongLength;
  225. }
  226. int GetOutputTime()
  227. {
  228. if ( plugin.outMod )
  229. return plugin.outMod->GetOutputTime();
  230. else
  231. return 0;
  232. }
  233. void SetOutputTime( int time_in_ms )
  234. {
  235. QueueUserAPC( APCSeek, audioThread, (ULONG_PTR)time_in_ms );
  236. }
  237. int pan = 0;
  238. int volume = -666;
  239. void SetVolume( int _volume )
  240. {
  241. volume = _volume;
  242. if ( plugin.outMod )
  243. plugin.outMod->SetVolume( volume );
  244. }
  245. void SetPan( int _pan )
  246. {
  247. pan = _pan;
  248. if ( plugin.outMod )
  249. plugin.outMod->SetPan( pan );
  250. }
  251. void EQSet( int on, char data[ 10 ], int preamp )
  252. {}
  253. In_Module plugin = {
  254. IN_VER_RET,
  255. "nullsoft(in_wave.dll)",
  256. 0,
  257. 0,
  258. 0,
  259. 1,
  260. 1,
  261. Config,
  262. About,
  263. Init,
  264. Quit,
  265. GetFileInfo,
  266. InfoBox,
  267. IsOurFile,
  268. Play,
  269. Pause,
  270. UnPause,
  271. IsPaused,
  272. Stop,
  273. GetLength,
  274. GetOutputTime,
  275. SetOutputTime,
  276. SetVolume,
  277. SetPan,
  278. 0,
  279. 0,
  280. 0,
  281. 0,
  282. 0,
  283. 0,
  284. 0,
  285. 0,
  286. 0,
  287. 0,
  288. 0,
  289. EQSet,
  290. 0,
  291. 0
  292. };
  293. extern "C" __declspec( dllexport ) In_Module * winampGetInModule2()
  294. {
  295. return &plugin;
  296. }
  297. inline bool KeywordMatch(const char *mainString, const char *keyword)
  298. {
  299. return !lstrcmpiA(mainString, keyword);
  300. }
  301. extern "C" __declspec( dllexport ) int winampGetExtendedFileInfoW( const wchar_t *fn, const char *data, wchar_t *dest, int destlen )
  302. {
  303. if ( KeywordMatch( data, "type" ) )
  304. {
  305. StringCchCopyW( dest, destlen, L"0" );
  306. return 1;
  307. }
  308. if ( KeywordMatch( data, "family" ) )
  309. {
  310. LPCWSTR ext = PathFindExtensionW( fn );
  311. if ( L'.' != *ext )
  312. return 0;
  313. return GetExtensionName( ++ext, dest, destlen );
  314. }
  315. if ( KeywordMatch( data, "mime" ) )
  316. {
  317. LPCWSTR ext = PathFindExtensionW( fn );
  318. if ( ext && !_wcsicmp( ext, L".wav" ) )
  319. {
  320. StringCchCopyW( dest, destlen, L"audio/wav" );
  321. return 1;
  322. }
  323. return 0;
  324. }
  325. if ( !fn || ( fn && !fn[ 0 ] ) )
  326. return 0;
  327. SF_INFO info;
  328. SoundFile metaFile( fn, SFM_READ, &info );
  329. if ( !metaFile )
  330. return 0;
  331. dest[ 0 ] = 0;
  332. if ( KeywordMatch( data, "artist" ) )
  333. {
  334. const char *meta = sf_get_string( metaFile, SF_STR_ARTIST );
  335. if ( meta )
  336. lstrcpynW( dest, AutoWide( meta ), destlen );
  337. }
  338. else if ( KeywordMatch( data, "title" ) )
  339. {
  340. const char *meta = sf_get_string( metaFile, SF_STR_TITLE );
  341. if ( meta )
  342. lstrcpynW( dest, AutoWide( meta ), destlen );
  343. }
  344. else if ( KeywordMatch( data, "comment" ) )
  345. {
  346. const char *meta = sf_get_string( metaFile, SF_STR_COMMENT );
  347. if ( meta )
  348. lstrcpynW( dest, AutoWide( meta ), destlen );
  349. }
  350. else if ( KeywordMatch( data, "bitrate" ) )
  351. {
  352. int br = CalcBitRate( &info );
  353. if ( br )
  354. StringCchPrintfW( dest, destlen, L"%d", br );
  355. }
  356. else if ( KeywordMatch( data, "length" ) )
  357. {
  358. uint64_t length = info.frames * 1000 / info.samplerate;
  359. StringCchPrintfW( dest, destlen, L"%I64u", length );
  360. }
  361. else
  362. return 0;
  363. return 1;
  364. }