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