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