1 /* in_flac - Winamp2 FLAC input plugin
\r
2 * Copyright (C) 2000,2001,2002,2003,2004,2005,2006 Josh Coalson
\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
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
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
20 # include <config.h>
\r
23 #include <windows.h>
\r
24 #include <limits.h> /* for INT_MAX */
\r
27 #include "winamp2/in2.h"
\r
28 #include "configure.h"
\r
29 #include "infobox.h"
\r
32 #define PLUGIN_VERSION "1.1.3"
\r
34 static In_Module mod_; /* the input module (declared near the bottom of this file) */
\r
35 static char lastfn_[MAX_PATH]; /* currently playing file (used for getting info on the current file) */
\r
36 flac_config_t flac_cfg;
\r
38 static stream_data_struct stream_data_;
\r
40 static FLAC__StreamDecoder *decoder_;
\r
41 static char sample_buffer_[SAMPLES_PER_WRITE * FLAC_PLUGIN__MAX_SUPPORTED_CHANNELS * (24/8) * 2];
\r
42 /* (24/8) for max bytes per sample, and 2 for DSPs */
\r
44 static HANDLE thread_handle = NULL; /* the handle to the decode thread */
\r
45 static DWORD WINAPI DecodeThread(void *b); /* the decode thread procedure */
\r
53 decoder_ = FLAC__stream_decoder_new();
\r
54 strcpy(lastfn_, "");
\r
65 FLAC_plugin__decoder_delete(decoder_);
\r
73 static int isourfile(char *fn) { return 0; }
\r
75 static int play(char *fn)
\r
81 if (decoder_ == 0) return 1;
\r
82 if (!(filesize = FileSize(fn))) return -1;
\r
84 if (!FLAC_plugin__decoder_init(decoder_, fn, filesize, &stream_data_, &flac_cfg.output))
\r
86 strcpy(lastfn_, fn);
\r
88 maxlatency = mod_.outMod->Open(stream_data_.sample_rate, stream_data_.channels, stream_data_.output_bits_per_sample, -1, -1);
\r
91 FLAC_plugin__decoder_finish(decoder_);
\r
95 mod_.outMod->SetVolume(-666);
\r
96 mod_.outMod->SetPan(0);
\r
97 /* initialize vis stuff */
\r
98 mod_.SAVSAInit(maxlatency, stream_data_.sample_rate);
\r
99 mod_.VSASetInfo(stream_data_.sample_rate, stream_data_.channels);
\r
101 mod_.SetInfo(stream_data_.average_bps, stream_data_.sample_rate/1000, stream_data_.channels, 1);
\r
102 /* start playing thread */
\r
104 thread_handle = CreateThread(NULL, 0, DecodeThread, NULL, 0, &thread_id);
\r
105 if (!thread_handle) return 1;
\r
114 stream_data_.is_playing = false;
\r
115 if (WaitForSingleObject(thread_handle, 2000) == WAIT_TIMEOUT)
\r
117 FLAC_plugin__show_error("Error while stopping decoding thread.");
\r
118 TerminateThread(thread_handle, 0);
\r
120 CloseHandle(thread_handle);
\r
121 thread_handle = NULL;
\r
124 FLAC_plugin__decoder_finish(decoder_);
\r
125 mod_.outMod->Close();
\r
126 mod_.SAVSADeInit();
\r
133 static void pause()
\r
136 mod_.outMod->Pause(1);
\r
139 static void unpause()
\r
142 mod_.outMod->Pause(0);
\r
145 static int ispaused()
\r
150 static int getlength()
\r
152 return stream_data_.length_in_msec;
\r
155 static int getoutputtime()
\r
157 return mod_.outMod->GetOutputTime();
\r
160 static void setoutputtime(int time_in_ms)
\r
162 stream_data_.seek_to = time_in_ms;
\r
165 static void setvolume(int volume)
\r
167 mod_.outMod->SetVolume(volume);
\r
170 static void setpan(int pan)
\r
172 mod_.outMod->SetPan(pan);
\r
175 static void eq_set(int on, char data[10], int preamp) {}
\r
181 static void do_vis(char *data, int nch, int resolution, int position, unsigned samples)
\r
183 static char vis_buffer[SAMPLES_PER_WRITE * FLAC_PLUGIN__MAX_SUPPORTED_CHANNELS];
\r
188 * Winamp visuals may have problems accepting sample sizes larger than
\r
189 * 16 bits, so we reduce the sample size here if necessary.
\r
192 switch(resolution) {
\r
195 size = resolution / 8;
\r
196 count = samples * nch;
\r
201 *ptr++ = data[0] ^ 0x80;
\r
210 mod_.SAAddPCMData(data, nch, resolution, position);
\r
211 mod_.VSAAddPCMData(data, nch, resolution, position);
\r
215 static DWORD WINAPI DecodeThread(void *unused)
\r
217 const unsigned channels = stream_data_.channels;
\r
218 const unsigned bits_per_sample = stream_data_.bits_per_sample;
\r
219 const unsigned target_bps = stream_data_.output_bits_per_sample;
\r
220 const unsigned sample_rate = stream_data_.sample_rate;
\r
221 const unsigned fact = channels * (target_bps/8);
\r
223 while (stream_data_.is_playing)
\r
226 if (stream_data_.seek_to != -1)
\r
228 const int pos = FLAC_plugin__seek(decoder_, &stream_data_);
\r
229 if (pos != -1) mod_.outMod->Flush(pos);
\r
232 else if (stream_data_.eof)
\r
234 if (!mod_.outMod->IsPlaying())
\r
236 PostMessage(mod_.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
\r
244 /* decode samples */
\r
245 int bytes = FLAC_plugin__decode(decoder_, &stream_data_, sample_buffer_);
\r
246 const int n = bytes / fact;
\r
247 /* visualization */
\r
248 do_vis(sample_buffer_, channels, target_bps, mod_.outMod->GetWrittenTime(), n);
\r
250 if (mod_.dsp_isactive())
\r
251 bytes = mod_.dsp_dosamples((short*)sample_buffer_, n, target_bps, channels, sample_rate) * fact;
\r
253 while (mod_.outMod->CanWrite()<bytes && stream_data_.is_playing && stream_data_.seek_to==-1)
\r
255 if (stream_data_.is_playing && stream_data_.seek_to==-1)
\r
256 mod_.outMod->Write(sample_buffer_, bytes);
\r
258 if (flac_cfg.display.show_bps)
\r
260 const int rate = FLAC_plugin__get_rate(mod_.outMod->GetWrittenTime(), mod_.outMod->GetOutputTime(), &stream_data_);
\r
261 if (rate) mod_.SetInfo(rate/1000, stream_data_.sample_rate/1000, stream_data_.channels, 1);
\r
273 static T_CHAR *get_tag(const T_CHAR *tag, void *param)
\r
275 FLAC__StreamMetadata *tags = (FLAC__StreamMetadata*)param;
\r
281 /* Vorbis comment names must be ASCII, so convert 'tag' first */
\r
282 tagname = malloc(wcslen(tag)+1);
\r
283 for(p=tagname;*tag;) {
\r
289 *p++ = (char)(*tag++);
\r
293 val = FLAC_plugin__tags_get_tag_ucs2(tags, tagname);
\r
295 /* some "user friendly cheavats" */
\r
298 if (!wcsicmp(tag, L"ARTIST"))
\r
300 val = FLAC_plugin__tags_get_tag_ucs2(tags, "PERFORMER");
\r
301 if (!val) val = FLAC_plugin__tags_get_tag_ucs2(tags, "COMPOSER");
\r
303 else if (!wcsicmp(tag, L"YEAR") || !wcsicmp(tag, L"DATE"))
\r
305 val = FLAC_plugin__tags_get_tag_ucs2(tags, "YEAR_RECORDED");
\r
306 if (!val) val = FLAC_plugin__tags_get_tag_ucs2(tags, "YEAR_PERFORMED");
\r
313 static void free_tag(T_CHAR *tag, void *param)
\r
319 static void format_title(const char *filename, WCHAR *title, unsigned max_size)
\r
321 FLAC__StreamMetadata *tags;
\r
323 ReadTags(filename, &tags, /*forDisplay=*/true);
\r
325 tagz_format(flac_cfg.title.tag_format_w, get_tag, free_tag, tags, title, max_size);
\r
327 FLAC_plugin__tags_destroy(&tags);
\r
330 static void getfileinfo(char *filename, char *title, int *length_in_msec)
\r
332 FLAC__StreamMetadata streaminfo;
\r
334 if (!filename || !*filename) {
\r
335 filename = lastfn_;
\r
336 if (length_in_msec) {
\r
337 *length_in_msec = stream_data_.length_in_msec;
\r
338 length_in_msec = 0; /* force skip in following code */
\r
342 if (!FLAC__metadata_get_streaminfo(filename, &streaminfo)) {
\r
343 if (length_in_msec)
\r
344 *length_in_msec = -1;
\r
349 static WCHAR buffer[400];
\r
350 format_title(filename, buffer, 400);
\r
351 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, buffer, -1, title, 400, NULL, NULL);
\r
354 if (length_in_msec) {
\r
355 /* with VC++ you have to spoon feed it the casting from uint64->int64->double */
\r
356 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
359 *length_in_msec = (int)l;
\r
367 void FLAC_plugin__show_error(const char *message,...)
\r
371 va_start(args, message);
\r
372 vsprintf(foo, message, args);
\r
374 MessageBox(mod_.hMainWindow, foo, "FLAC Plug-in Error", MB_ICONSTOP);
\r
377 static void about(HWND hwndParent)
\r
379 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
382 static void config(HWND hwndParent)
\r
384 if (DoConfig(mod_.hDllInstance, hwndParent))
\r
388 static int infobox(char *fn, HWND hwnd)
\r
390 DoInfoBox(mod_.hDllInstance, hwnd, fn);
\r
398 static In_Module mod_ =
\r
401 "FLAC Decoder v" PLUGIN_VERSION,
\r
402 0, /* hMainWindow */
\r
403 0, /* hDllInstance */
\r
404 "FLAC\0FLAC Audio File (*.FLAC)\0",
\r
405 1, /* is_seekable */
\r
406 1, /* uses output */
\r
427 0,0,0,0,0,0,0,0,0, /* vis stuff */
\r
430 NULL, /* setinfo */
\r
434 __declspec(dllexport) In_Module *winampGetInModule2()
\r
439 BOOL WINAPI _DllMainCRTStartup(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
\r