major plugin revamp based on x-fixer's code
[flac.git] / src / plugin_winamp2 / in_flac.c
1 /* in_flac - Winamp2 FLAC input plugin\r
2  * Copyright (C) 2000,2001,2002,2003,2004  Josh Coalson\r
3  *\r
4  * This program is free software; you can redistribute it and/or\r
5  * modify it under the terms of the GNU General Public License\r
6  * as published by the Free Software Foundation; either version 2\r
7  * of the License, or (at your option) any later version.\r
8  *\r
9  * This program is distributed in the hope that it will be useful,\r
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
12  * GNU General Public License for more details.\r
13  *\r
14  * You should have received a copy of the GNU General Public License\r
15  * along with this program; if not, write to the Free Software\r
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
17  */\r
18 \r
19 #include <windows.h>\r
20 #include <stdio.h>\r
21 \r
22 #include "winamp2/in2.h"\r
23 #include "config.h"\r
24 #include "infobox.h"\r
25 #include "tagz.h"\r
26 \r
27 #define PLUGIN_VERSION          "1.1.1"\r
28 \r
29 static In_Module mod_;                      /* the input module (declared near the bottom of this file) */\r
30 static char lastfn_[MAX_PATH];              /* currently playing file (used for getting info on the current file) */\r
31 flac_config_t flac_cfg;\r
32 \r
33 static file_info_struct file_info_;\r
34 static int paused;\r
35 static FLAC__FileDecoder *decoder_;\r
36 static char sample_buffer_[SAMPLES_PER_WRITE * FLAC_PLUGIN__MAX_SUPPORTED_CHANNELS * (24/8) * 2];\r
37 /* (24/8) for max bytes per sample, and 2 for DSPs */\r
38 \r
39 static HANDLE thread_handle = NULL;         /* the handle to the decode thread */\r
40 static DWORD WINAPI DecodeThread(void *b);  /* the decode thread procedure */\r
41 \r
42 /*\r
43  *  init/quit\r
44  */\r
45 \r
46 static void init()\r
47 {\r
48         decoder_ = FLAC__file_decoder_new();\r
49         strcpy(lastfn_, "");\r
50 \r
51         InitConfig();\r
52         ReadConfig();\r
53         InitInfobox();\r
54 }\r
55 \r
56 static void quit()\r
57 {\r
58         WriteConfig();\r
59         DeinitInfobox();\r
60         FLAC_plugin__decoder_delete(decoder_);\r
61         decoder_ = 0;\r
62 }\r
63 \r
64 /*\r
65  *  open/close\r
66  */\r
67 \r
68 static int isourfile(char *fn) { return 0; }\r
69 \r
70 static int play(char *fn)\r
71 {\r
72         LONGLONG filesize;\r
73         DWORD thread_id;\r
74         int   maxlatency;\r
75         /* checks */\r
76         if (decoder_ == 0) return 1;\r
77         if (!(filesize = FileSize(fn))) return -1;\r
78         /* init decoder */\r
79         if (!FLAC_plugin__decoder_init(decoder_, fn, filesize, &file_info_, &flac_cfg.output))\r
80                 return 1;\r
81         strcpy(lastfn_, fn);\r
82         /* open output */\r
83         maxlatency = mod_.outMod->Open(file_info_.sample_rate, file_info_.channels, file_info_.output_bits_per_sample, -1, -1);\r
84         if (maxlatency < 0)\r
85         {\r
86                 FLAC_plugin__decoder_finish(decoder_);\r
87                 return 1;\r
88         }\r
89         /* set defaults */\r
90         mod_.outMod->SetVolume(-666);\r
91         mod_.outMod->SetPan(0);\r
92         /* initialize vis stuff */\r
93         mod_.SAVSAInit(maxlatency, file_info_.sample_rate);\r
94         mod_.VSASetInfo(file_info_.sample_rate, file_info_.channels);\r
95         /* set info */\r
96         mod_.SetInfo(file_info_.average_bps, file_info_.sample_rate/1000, file_info_.channels, 1);\r
97         /* start playing thread */\r
98         paused = 0;\r
99         thread_handle = CreateThread(NULL, 0, DecodeThread, NULL, 0, &thread_id);\r
100         if (!thread_handle)     return 1;\r
101 \r
102         return 0;\r
103 }\r
104 \r
105 static void stop()\r
106 {\r
107         if (thread_handle)\r
108         {\r
109                 file_info_.is_playing = false;\r
110                 if (WaitForSingleObject(thread_handle, 2000) == WAIT_TIMEOUT)\r
111                 {\r
112                         FLAC_plugin__show_error("Error while stopping decoding thread.");\r
113                         TerminateThread(thread_handle, 0);\r
114                 }\r
115                 CloseHandle(thread_handle);\r
116                 thread_handle = NULL;\r
117         }\r
118 \r
119         FLAC_plugin__decoder_finish(decoder_);\r
120         mod_.outMod->Close();\r
121         mod_.SAVSADeInit();\r
122 }\r
123 \r
124 /*\r
125  *  play control\r
126  */\r
127 \r
128 static void pause()\r
129 {\r
130         paused = 1;\r
131         mod_.outMod->Pause(1);\r
132 }\r
133 \r
134 static void unpause()\r
135 {\r
136         paused = 0;\r
137         mod_.outMod->Pause(0);\r
138 }\r
139 \r
140 static int ispaused()\r
141 {\r
142         return paused;\r
143 }\r
144 \r
145 static int getlength()\r
146 {\r
147         return file_info_.length_in_msec;\r
148 }\r
149 \r
150 static int getoutputtime()\r
151 {\r
152         return mod_.outMod->GetOutputTime();\r
153 }\r
154 \r
155 static void setoutputtime(int time_in_ms)\r
156 {\r
157         file_info_.seek_to = time_in_ms;\r
158 }\r
159 \r
160 static void setvolume(int volume)\r
161 {\r
162         mod_.outMod->SetVolume(volume);\r
163 }\r
164 \r
165 static void setpan(int pan)\r
166 {\r
167         mod_.outMod->SetPan(pan);\r
168 }\r
169 \r
170 static void eq_set(int on, char data[10], int preamp) {}\r
171 \r
172 /*\r
173  *  playing loop\r
174  */\r
175 \r
176 static void do_vis(char *data, int nch, int resolution, int position, unsigned samples)\r
177 {\r
178         static char vis_buffer[SAMPLES_PER_WRITE * FLAC_PLUGIN__MAX_SUPPORTED_CHANNELS];\r
179         char *ptr;\r
180         int size, count;\r
181 \r
182         /*\r
183          * Winamp visuals may have problems accepting sample sizes larger than\r
184          * 16 bits, so we reduce the sample size here if necessary.\r
185          */\r
186 \r
187         switch(resolution) {\r
188                 case 32:\r
189                 case 24:\r
190                         size  = resolution / 8;\r
191                         count = samples * nch;\r
192                         data += size - 1;\r
193 \r
194                         ptr = vis_buffer;\r
195                         while(count--) {\r
196                                 *ptr++ = data[0] ^ 0x80;\r
197                                 data += size;\r
198                         }\r
199 \r
200                         data = vis_buffer;\r
201                         resolution = 8;\r
202                         /* fall through */\r
203                 case 16:\r
204                 case 8:\r
205                         mod_.SAAddPCMData(data, nch, resolution, position);\r
206                         mod_.VSAAddPCMData(data, nch, resolution, position);\r
207         }\r
208 }\r
209 \r
210 static DWORD WINAPI DecodeThread(void *unused)\r
211 {\r
212         const unsigned channels = file_info_.channels;\r
213         const unsigned bits_per_sample = file_info_.bits_per_sample;\r
214         const unsigned target_bps = file_info_.output_bits_per_sample;\r
215         const unsigned sample_rate = file_info_.sample_rate;\r
216         const unsigned fact = channels * (target_bps/8);\r
217 \r
218         while (file_info_.is_playing)\r
219         {\r
220                 /* seek needed */\r
221                 if (file_info_.seek_to != -1)\r
222                 {\r
223                         const int pos = FLAC_plugin__seek(decoder_, &file_info_);\r
224                         if (pos != -1) mod_.outMod->Flush(pos);\r
225                 }\r
226                 /* stream ended */\r
227                 else if (file_info_.eof)\r
228                 {\r
229                         if (!mod_.outMod->IsPlaying())\r
230                         {\r
231                                 PostMessage(mod_.hMainWindow, WM_WA_MPEG_EOF, 0, 0);\r
232                                 return 0;\r
233                         }\r
234                         Sleep(10);\r
235                 }\r
236                 /* decode */\r
237                 else\r
238                 {\r
239                         /* decode samples */\r
240                         int bytes = FLAC_plugin__decode(decoder_, &file_info_, sample_buffer_);\r
241                         const int n = bytes / fact;\r
242                         /* visualization */\r
243                         do_vis(sample_buffer_, channels, target_bps, mod_.outMod->GetWrittenTime(), n);\r
244                         /* dsp */\r
245                         if (mod_.dsp_isactive())\r
246                                 bytes = mod_.dsp_dosamples((short*)sample_buffer_, n, target_bps, channels, sample_rate) * fact;\r
247                         /* output */\r
248                         while (mod_.outMod->CanWrite()<bytes && file_info_.is_playing && file_info_.seek_to==-1)\r
249                                 Sleep(20);\r
250                         if (file_info_.is_playing && file_info_.seek_to==-1)\r
251                                 mod_.outMod->Write(sample_buffer_, bytes);\r
252                         /* show bitrate */\r
253                         if (flac_cfg.display.show_bps)\r
254                         {\r
255                                 const int rate = FLAC_plugin__get_rate(mod_.outMod->GetWrittenTime(), mod_.outMod->GetOutputTime(), &file_info_);\r
256                                 if (rate) mod_.SetInfo(rate/1000, file_info_.sample_rate/1000, file_info_.channels, 1);\r
257                         }\r
258                 }\r
259         }\r
260 \r
261         return 0;\r
262 }\r
263 \r
264 /*\r
265  *  title formatting\r
266  */\r
267 \r
268 static const T_CHAR *get_tag(const T_CHAR *tag, void *param)\r
269 {\r
270         FLAC_Plugin__CanonicalTag *t = (FLAC_Plugin__CanonicalTag*)param;\r
271         const T_CHAR *val = FLAC_plugin__canonical_get(t, tag);\r
272         /* some "user friendly cheavats" */\r
273         if (!val)\r
274         {\r
275                 if (!wcsicmp(tag, L"ARTIST"))\r
276                 {\r
277                         val = FLAC_plugin__canonical_get(t, L"PERFORMER");\r
278                         if (!val) val = FLAC_plugin__canonical_get(t, L"COMPOSER");\r
279                 }\r
280                 else if (!wcsicmp(tag, L"YEAR") || !wcsicmp(tag, L"DATE"))\r
281                 {\r
282                         val = FLAC_plugin__canonical_get(t, L"YEAR_RECORDED");\r
283                         if (!val) val = FLAC_plugin__canonical_get(t, L"YEAR_PERFORMED");\r
284                 }\r
285         }\r
286 \r
287         return val;\r
288 }\r
289 \r
290 static void format_title(const char *filename, WCHAR *title, unsigned max_size)\r
291 {\r
292         FLAC_Plugin__CanonicalTag tag;\r
293 \r
294         ReadTags(filename, &tag, true);\r
295 \r
296         tagz_format(flac_cfg.title.tag_format_w, get_tag, NULL, &tag, title, max_size);\r
297 \r
298         FLAC_plugin__canonical_tag_clear(&tag);\r
299 }\r
300 \r
301 static void getfileinfo(char *filename, char *title, int *length_in_msec)\r
302 {\r
303         FLAC__StreamMetadata streaminfo;\r
304 \r
305         if (!filename || !*filename)\r
306         {\r
307                 filename = lastfn_;\r
308                 if (length_in_msec)\r
309                 {\r
310                         *length_in_msec = file_info_.length_in_msec;\r
311                         length_in_msec  = 0;    /* force skip in following code */\r
312                 }\r
313         }\r
314 \r
315         if (!FLAC__metadata_get_streaminfo(filename, &streaminfo))\r
316         {\r
317                 if (length_in_msec)\r
318                         *length_in_msec = -1;\r
319                 return;\r
320         }\r
321 \r
322         if (title)\r
323         {\r
324                 static WCHAR buffer[400];\r
325                 format_title(filename, buffer, 400);\r
326                 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, buffer, -1, title, 400, NULL, NULL);\r
327         }\r
328 \r
329         if (length_in_msec)\r
330                 *length_in_msec = (int)(streaminfo.data.stream_info.total_samples*10 / (streaminfo.data.stream_info.sample_rate/100));\r
331 }\r
332 \r
333 /*\r
334  *  interface\r
335  */\r
336 \r
337 void FLAC_plugin__show_error(const char *message,...)\r
338 {\r
339         char foo[512];\r
340         va_list args;\r
341         va_start(args, message);\r
342         vsprintf(foo, message, args);\r
343         va_end(args);\r
344         MessageBox(mod_.hMainWindow, foo, "FLAC Plug-in Error", MB_ICONSTOP);\r
345 }\r
346 \r
347 static void about(HWND hwndParent)\r
348 {\r
349         MessageBox(hwndParent, "Winamp2 FLAC Plugin v"PLUGIN_VERSION"\nby Josh Coalson and X-Fixer\n\nuses libFLAC "VERSION"\nSee http://flac.sourceforge.net/\n", "About FLAC Plugin", MB_ICONINFORMATION);\r
350 }\r
351 \r
352 static void config(HWND hwndParent)\r
353 {\r
354         if (DoConfig(mod_.hDllInstance, hwndParent))\r
355                 WriteConfig();\r
356 }\r
357 \r
358 static int infobox(char *fn, HWND hwnd)\r
359 {\r
360         DoInfoBox(mod_.hDllInstance, hwnd, fn);\r
361         return 0;\r
362 }\r
363 \r
364 /*\r
365  *  exported stuff\r
366  */\r
367 \r
368 static In_Module mod_ =\r
369 {\r
370         IN_VER,\r
371         "Reference FLAC Decoder v" PLUGIN_VERSION,\r
372         0,                                    /* hMainWindow */\r
373         0,                                    /* hDllInstance */\r
374         "FLAC\0FLAC Audio File (*.FLAC)\0",\r
375         1,                                    /* is_seekable */\r
376         1,                                    /* uses output */\r
377         config,\r
378         about,\r
379         init,\r
380         quit,\r
381         getfileinfo,\r
382         infobox,\r
383         isourfile,\r
384         play,\r
385         pause,\r
386         unpause,\r
387         ispaused,\r
388         stop,\r
389 \r
390         getlength,\r
391         getoutputtime,\r
392         setoutputtime,\r
393 \r
394         setvolume,\r
395         setpan,\r
396 \r
397         0,0,0,0,0,0,0,0,0,                    /* vis stuff */\r
398         0,0,                                  /* dsp */\r
399         eq_set,\r
400         NULL,                                 /* setinfo */\r
401         0                                     /* out_mod */\r
402 };\r
403 \r
404 __declspec(dllexport) In_Module *winampGetInModule2()\r
405 {\r
406         return &mod_;\r
407 }\r
408 \r
409 BOOL WINAPI _DllMainCRTStartup(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)\r
410 {\r
411         return TRUE;\r
412 }\r