add support for merged canonical tags, decimation of input to visualization modules
[flac.git] / src / plugin_winamp2 / in_flac.c
1 /* in_flac - Winamp2 FLAC input plugin
2  * Copyright (C) 2000,2001,2002  Josh Coalson
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program 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
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17  */
18
19 #include <windows.h>
20 #include <mmreg.h>
21 #include <msacm.h>
22 #include <math.h>
23 #include <stdio.h>
24
25 #include "in2.h"
26 #include "FLAC/all.h"
27 #include "plugin_common/all.h"
28
29 #ifdef max
30 #undef max
31 #endif
32 #define max(a,b) ((a)>(b)?(a):(b))
33
34
35 #define FLAC__DO_DITHER
36
37 BOOL WINAPI _DllMainCRTStartup(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
38 {
39         return TRUE;
40 }
41
42 /* post this to the main window at end of file (after playback as stopped) */
43 #define WM_WA_MPEG_EOF WM_USER+2
44
45 typedef struct {
46         FLAC__bool abort_flag;
47         unsigned total_samples;
48         unsigned bits_per_sample;
49         unsigned channels;
50         unsigned sample_rate;
51         unsigned length_in_msec;
52 } file_info_struct;
53
54 static FLAC__bool safe_decoder_init_(const char *infilename, FLAC__FileDecoder *decoder);
55 static void safe_decoder_finish_(FLAC__FileDecoder *decoder);
56 static void safe_decoder_delete_(FLAC__FileDecoder *decoder);
57 static FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__FileDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data);
58 static void metadata_callback_(const FLAC__FileDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data);
59 static void error_callback_(const FLAC__FileDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
60 static void get_description_(const char *filename, char *description, unsigned max_size);
61
62 In_Module mod_; /* the output module (declared near the bottom of this file) */
63 char lastfn_[MAX_PATH]; /* currently playing file (used for getting info on the current file) */
64 int decode_pos_ms_; /* current decoding position, in milliseconds */
65 int paused_; /* are we paused? */
66 int seek_needed_; /* if != -1, it is the point that the decode thread should seek to, in ms. */
67
68 #define SAMPLES_PER_WRITE 576
69 FLAC__int32 reservoir_[FLAC__MAX_BLOCK_SIZE * 2/*for overflow*/ * FLAC_PLUGIN__MAX_SUPPORTED_CHANNELS];
70 char sample_buffer_[SAMPLES_PER_WRITE * FLAC_PLUGIN__MAX_SUPPORTED_CHANNELS * (24/8) * 2]; /* (24/8) for max bytes per sample, and 2 for who knows what */
71 unsigned wide_samples_in_reservoir_;
72 static file_info_struct file_info_;
73 static FLAC__FileDecoder *decoder_;
74
75 int killDecodeThread = 0;                                       /* the kill switch for the decode thread */
76 HANDLE thread_handle = INVALID_HANDLE_VALUE;    /* the handle to the decode thread */
77
78 DWORD WINAPI __stdcall DecodeThread(void *b); /* the decode thread procedure */
79
80
81 static void do_vis(char *data, int nch, int resolution, int position, unsigned samples)
82 {
83         static char vis_buffer[SAMPLES_PER_WRITE * FLAC_PLUGIN__MAX_SUPPORTED_CHANNELS];
84         char *ptr;
85         int size, count;
86
87         /*
88          * Winamp visuals may have problems accepting sample sizes larger than
89          * 16 bits, so we reduce the sample size here if necessary.
90          */
91
92         switch(resolution) {
93                 case 32:
94                 case 24:
95                         size  = resolution / 8;
96                         count = samples * nch;
97
98                         ptr = vis_buffer;
99                         while(count--) {
100                                 data += size;
101                                 *ptr++ = data[-1] ^ 0x80;
102                         }
103
104                         data = vis_buffer;
105                         resolution = 8;
106
107                         /* fall through */
108                 case 16:
109                 case 8:
110                 default:
111                         mod_.SAAddPCMData(data, nch, resolution, position);
112                         mod_.VSAAddPCMData(data, nch, resolution, position);
113         }
114 }
115
116 void config(HWND hwndParent)
117 {
118         MessageBox(hwndParent, "No configuration.", "Configuration", MB_OK);
119         /* if we had a configuration we'd want to write it here :) */
120 }
121 void about(HWND hwndParent)
122 {
123         MessageBox(hwndParent, "Winamp FLAC Plugin v" FLAC__VERSION_STRING ", by Josh Coalson\nSee http://flac.sourceforge.net/", "About FLAC Plugin", MB_OK);
124 }
125
126 void init()
127 {
128         decoder_ = FLAC__file_decoder_new();
129         strcpy(lastfn_, "");
130 }
131
132 void quit()
133 {
134         safe_decoder_delete_(decoder_);
135         decoder_ = 0;
136 }
137
138 int isourfile(char *fn) { return 0; }
139 /* used for detecting URL streams.. unused here. strncmp(fn, "http://", 7) to detect HTTP streams, etc */
140
141 int play(char *fn)
142 {
143         int maxlatency;
144         int thread_id;
145         HANDLE input_file = INVALID_HANDLE_VALUE;
146 #ifdef FLAC__DO_DITHER
147         const unsigned output_bits_per_sample = 16;
148 #else
149         const unsigned output_bits_per_sample = file_info_.bits_per_sample;
150 #endif
151
152         if(0 == decoder_) {
153                 return 1;
154         }
155
156         input_file = CreateFile(fn, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
157         if(input_file == INVALID_HANDLE_VALUE) {
158                 return -1;
159         }
160         CloseHandle(input_file);
161
162         if(!safe_decoder_init_(fn, decoder_)) {
163                 return 1;
164         }
165
166         strcpy(lastfn_, fn);
167         paused_ = 0;
168         decode_pos_ms_ = 0;
169         seek_needed_ = -1;
170         wide_samples_in_reservoir_ = 0;
171
172         maxlatency = mod_.outMod->Open(file_info_.sample_rate, file_info_.channels, output_bits_per_sample, -1, -1);
173         if(maxlatency < 0) { /* error opening device */
174                 return 1;
175         }
176
177         /* dividing by 1000 for the first parameter of setinfo makes it */
178         /* display 'H'... for hundred.. i.e. 14H Kbps. */
179         mod_.SetInfo((file_info_.sample_rate*file_info_.bits_per_sample*file_info_.channels)/1000, file_info_.sample_rate/1000, file_info_.channels, 1);
180
181         /* initialize vis stuff */
182         mod_.SAVSAInit(maxlatency, file_info_.sample_rate);
183         mod_.VSASetInfo(file_info_.sample_rate, file_info_.channels);
184
185         mod_.outMod->SetVolume(-666); /* set the output plug-ins default volume */
186
187         killDecodeThread = 0;
188         thread_handle = (HANDLE) CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) DecodeThread, (void *) &killDecodeThread, 0, &thread_id);
189
190         return 0;
191 }
192
193 void pause()
194 {
195         paused_ = 1;
196         mod_.outMod->Pause(1);
197 }
198
199 void unpause()
200 {
201         paused_ = 0;
202         mod_.outMod->Pause(0);
203 }
204 int ispaused()
205 {
206         return paused_;
207 }
208
209 void stop()
210 {
211         if(thread_handle != INVALID_HANDLE_VALUE) {
212                 killDecodeThread = 1;
213                 if(WaitForSingleObject(thread_handle, INFINITE) == WAIT_TIMEOUT) {
214                         MessageBox(mod_.hMainWindow, "error asking thread to die!\n", "error killing decode thread", 0);
215                         TerminateThread(thread_handle, 0);
216                 }
217                 CloseHandle(thread_handle);
218                 thread_handle = INVALID_HANDLE_VALUE;
219         }
220         safe_decoder_finish_(decoder_);
221
222         mod_.outMod->Close();
223
224         mod_.SAVSADeInit();
225 }
226
227 int getlength()
228 {
229         return (int)file_info_.length_in_msec;
230 }
231
232 int getoutputtime()
233 {
234         return decode_pos_ms_ + (mod_.outMod->GetOutputTime() - mod_.outMod->GetWrittenTime());
235 }
236
237 void setoutputtime(int time_in_ms)
238 {
239         seek_needed_ = time_in_ms;
240 }
241
242 void setvolume(int volume) { mod_.outMod->SetVolume(volume); }
243 void setpan(int pan) { mod_.outMod->SetPan(pan); }
244
245 int infoDlg(char *fn, HWND hwnd)
246 {
247         /* @@@TODO: implement info dialog. */
248         return 0;
249 }
250
251 void getfileinfo(char *filename, char *title, int *length_in_msec)
252 {
253         FLAC__StreamMetadata streaminfo;
254
255         if(0 == filename || filename[0] == '\0') {
256                 filename = lastfn_;
257                 if(length_in_msec) {
258                         *length_in_msec = getlength();
259                         length_in_msec = 0; /* force skip in following code */
260                 }
261         }
262
263         if(!FLAC__metadata_get_streaminfo(filename, &streaminfo)) {
264                 MessageBox(mod_.hMainWindow, filename, "ERROR: invalid/missing FLAC metadata", 0);
265                 if(title) {
266                         static const char *errtitle = "Invalid FLAC File: ";
267                         sprintf(title, "%s\"%s\"", errtitle, filename);
268                 }
269                 if(length_in_msec)
270                         *length_in_msec = -1;
271                 return;
272         }
273
274         if(title) {
275                 get_description_(filename, title, MAX_PATH);
276         }
277         if(length_in_msec)
278                 *length_in_msec = (int)(streaminfo.data.stream_info.total_samples * 10 / (streaminfo.data.stream_info.sample_rate / 100));
279 }
280
281 void eq_set(int on, char data[10], int preamp)
282 {
283 }
284
285 DWORD WINAPI __stdcall DecodeThread(void *b)
286 {
287         int done = 0;
288
289         while(! *((int *)b) ) {
290                 const unsigned channels = file_info_.channels;
291                 const unsigned bits_per_sample = file_info_.bits_per_sample;
292 #ifdef FLAC__DO_DITHER
293                 const unsigned target_bps = 16;
294 #else
295                 const unsigned target_bps = bits_per_sample;
296 #endif
297                 const unsigned sample_rate = file_info_.sample_rate;
298                 if(seek_needed_ != -1) {
299                         const double distance = (double)seek_needed_ / (double)getlength();
300                         const unsigned target_sample = (unsigned)(distance * (double)file_info_.total_samples);
301                         if(FLAC__file_decoder_seek_absolute(decoder_, (FLAC__uint64)target_sample)) {
302                                 decode_pos_ms_ = (int)(distance * (double)getlength());
303                                 seek_needed_ = -1;
304                                 done = 0;
305                                 mod_.outMod->Flush(decode_pos_ms_);
306                         }
307                 }
308                 if(done) {
309                         if(!mod_.outMod->IsPlaying()) {
310                                 PostMessage(mod_.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
311                                 return 0;
312                         }
313                         Sleep(10);
314                 }
315                 else if(mod_.outMod->CanWrite() >= ((int)(SAMPLES_PER_WRITE*channels*((target_bps+7)/8)) << (mod_.dsp_isactive()?1:0))) {
316                         while(wide_samples_in_reservoir_ < SAMPLES_PER_WRITE) {
317                                 if(FLAC__file_decoder_get_state(decoder_) == FLAC__FILE_DECODER_END_OF_FILE) {
318                                         done = 1;
319                                         break;
320                                 }
321                                 else if(!FLAC__file_decoder_process_single(decoder_)) {
322                                         MessageBox(mod_.hMainWindow, FLAC__FileDecoderStateString[FLAC__file_decoder_get_state(decoder_)], "READ ERROR processing frame", 0);
323                                         done = 1;
324                                         break;
325                                 }
326                         }
327
328                         if(wide_samples_in_reservoir_ == 0) {
329                                 done = 1;
330                         }
331                         else {
332                                 const unsigned n = min(wide_samples_in_reservoir_, SAMPLES_PER_WRITE);
333                                 const unsigned delta = n * channels;
334                                 int bytes = (int)FLAC__plugin_common__pack_pcm_signed_little_endian(sample_buffer_, reservoir_, n, channels, bits_per_sample, target_bps);
335                                 unsigned i;
336
337                                 for(i = delta; i < wide_samples_in_reservoir_ * channels; i++)
338                                         reservoir_[i-delta] = reservoir_[i];
339                                 wide_samples_in_reservoir_ -= n;
340
341                                 do_vis((char *)sample_buffer_, channels, target_bps, decode_pos_ms_, n);
342                                 decode_pos_ms_ += (n*1000 + sample_rate/2)/sample_rate;
343                                 if(mod_.dsp_isactive())
344                                         bytes = mod_.dsp_dosamples((short *)sample_buffer_, n, target_bps, channels, sample_rate) * (channels*target_bps/8);
345                                 mod_.outMod->Write(sample_buffer_, bytes);
346                         }
347                 }
348                 else Sleep(20);
349         }
350         return 0;
351 }
352
353
354
355 In_Module mod_ =
356 {
357         IN_VER,
358         "Reference FLAC Player v" FLAC__VERSION_STRING,
359         0,      /* hMainWindow */
360         0,  /* hDllInstance */
361         "FLAC\0FLAC Audio File (*.FLAC)\0"
362         ,
363         1,      /* is_seekable */
364         1, /* uses output */
365         config,
366         about,
367         init,
368         quit,
369         getfileinfo,
370         infoDlg,
371         isourfile,
372         play,
373         pause,
374         unpause,
375         ispaused,
376         stop,
377
378         getlength,
379         getoutputtime,
380         setoutputtime,
381
382         setvolume,
383         setpan,
384
385         0,0,0,0,0,0,0,0,0, /* vis stuff */
386
387
388         0,0, /* dsp */
389
390         eq_set,
391
392         NULL,           /* setinfo */
393
394         0 /* out_mod */
395
396 };
397
398 __declspec( dllexport ) In_Module * winampGetInModule2()
399 {
400         return &mod_;
401 }
402
403
404 /***********************************************************************
405  * local routines
406  **********************************************************************/
407 FLAC__bool safe_decoder_init_(const char *filename, FLAC__FileDecoder *decoder)
408 {
409         if(decoder == 0) {
410                 MessageBox(mod_.hMainWindow, "Decoder instance is NULL", "ERROR initializing decoder", 0);
411                 return false;
412         }
413
414         safe_decoder_finish_(decoder);
415
416         FLAC__file_decoder_set_md5_checking(decoder, false);
417         FLAC__file_decoder_set_filename(decoder, filename);
418         FLAC__file_decoder_set_write_callback(decoder, write_callback_);
419         FLAC__file_decoder_set_metadata_callback(decoder, metadata_callback_);
420         FLAC__file_decoder_set_error_callback(decoder, error_callback_);
421         FLAC__file_decoder_set_client_data(decoder, &file_info_);
422         if(FLAC__file_decoder_init(decoder) != FLAC__FILE_DECODER_OK) {
423                 MessageBox(mod_.hMainWindow, FLAC__FileDecoderStateString[FLAC__file_decoder_get_state(decoder)], "ERROR initializing decoder", 0);
424                 return false;
425         }
426
427         file_info_.abort_flag = false;
428         if(!FLAC__file_decoder_process_until_end_of_metadata(decoder)) {
429                 MessageBox(mod_.hMainWindow, FLAC__FileDecoderStateString[FLAC__file_decoder_get_state(decoder)], "ERROR processing metadata", 0);
430                 return false;
431         }
432
433         if(file_info_.abort_flag) {
434                 /* metadata callback already popped up the error dialog */
435                 return false;
436         }
437
438         return true;
439 }
440
441 void safe_decoder_finish_(FLAC__FileDecoder *decoder)
442 {
443         if(decoder && FLAC__file_decoder_get_state(decoder) != FLAC__FILE_DECODER_UNINITIALIZED)
444                 FLAC__file_decoder_finish(decoder);
445 }
446
447 void safe_decoder_delete_(FLAC__FileDecoder *decoder)
448 {
449         if(decoder) {
450                 safe_decoder_finish_(decoder);
451                 FLAC__file_decoder_delete(decoder);
452         }
453 }
454
455 FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__FileDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
456 {
457         file_info_struct *file_info = (file_info_struct *)client_data;
458         const unsigned channels = file_info->channels, wide_samples = frame->header.blocksize;
459         unsigned wide_sample, offset_sample, channel;
460
461         (void)decoder;
462
463         if(file_info->abort_flag)
464                 return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
465
466         for(offset_sample = wide_samples_in_reservoir_ * channels, wide_sample = 0; wide_sample < wide_samples; wide_sample++)
467                 for(channel = 0; channel < channels; channel++, offset_sample++)
468                         reservoir_[offset_sample] = buffer[channel][wide_sample];
469
470         wide_samples_in_reservoir_ += wide_samples;
471
472         return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
473 }
474
475 void metadata_callback_(const FLAC__FileDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
476 {
477         file_info_struct *file_info = (file_info_struct *)client_data;
478         (void)decoder;
479         if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
480                 FLAC__ASSERT(metadata->data.stream_info.total_samples < 0x100000000); /* this plugin can only handle < 4 gigasamples */
481                 file_info->total_samples = (unsigned)(metadata->data.stream_info.total_samples&0xffffffff);
482                 file_info->bits_per_sample = metadata->data.stream_info.bits_per_sample;
483                 file_info->channels = metadata->data.stream_info.channels;
484                 file_info->sample_rate = metadata->data.stream_info.sample_rate;
485
486 #ifdef FLAC__DO_DITHER
487                 if(file_info->bits_per_sample != 16 && file_info->bits_per_sample != 24) {
488                         MessageBox(mod_.hMainWindow, "ERROR: plugin can only handle 16/24-bit samples\n", "ERROR: plugin can only handle 16/24-bit samples", 0);
489                         file_info->abort_flag = true;
490                         return;
491                 }
492 #else
493                 if(file_info->bits_per_sample != 8 && file_info->bits_per_sample != 16 && file_info->bits_per_sample != 24) {
494                         MessageBox(mod_.hMainWindow, "ERROR: plugin can only handle 8/16/24-bit samples\n", "ERROR: plugin can only handle 8/16/24-bit samples", 0);
495                         file_info->abort_flag = true;
496                         return;
497                 }
498 #endif
499                 file_info->length_in_msec = file_info->total_samples * 10 / (file_info->sample_rate / 100);
500         }
501 }
502
503 void error_callback_(const FLAC__FileDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
504 {
505         file_info_struct *file_info = (file_info_struct *)client_data;
506         (void)decoder;
507         if(status != FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC)
508                 file_info->abort_flag = true;
509 }
510
511 static FLAC__bool local__is_blank(const char *s)
512 {
513         return 0 == s || *s == '\0';
514 }
515
516 void get_description_(const char *filename, char *description, unsigned max_size)
517 {
518         FLAC_Plugin__CanonicalTag tag;
519
520         FLAC_plugin__canonical_tag_init(&tag);
521         FLAC_plugin__canonical_tag_get_combined(filename, &tag);
522
523         /* @@@ when config window is done, add code here for converting to user charset ala plugin_xmms */
524
525         if(local__is_blank(tag.performer) && local__is_blank(tag.composer) && local__is_blank(tag.title)) {
526                 /* set the description to the filename */
527                 char *p;
528                 const char *temp = strrchr(filename, '\\');
529                 if(0 == temp)
530                         temp = strrchr(filename, '/');
531                 if(0 == temp)
532                         temp = filename;
533                 else
534                         temp++;
535                 strncpy(description, temp, max_size);
536                 description[max_size-1] = '\0';
537                 if(0 != (p = strrchr(description, '.')))
538                         *p = '\0';
539         }
540         else {
541                 char *artist = tag.performer[0]? tag.performer : tag.composer[0]? tag.composer : "Unknown Artist";
542                 char *title = tag.title[0]? tag.title : "Untitled";
543
544                 /* there's no snprintf in VC6 so we get sloppy */
545 #if 0
546                 snprintf(description, max_size, "%s - %s", artist, title);
547 #else
548                 const unsigned needed = strlen(artist) + strlen(title) + 3 + 1;
549                 if(needed <= max_size)
550                         sprintf(description, "%s - %s", artist, title);
551                 else {
552                         char *p = malloc(needed);
553                         if(0 != p) {
554                                 sprintf(p, "%s - %s", artist, title);
555                                 p[max_size-1] = '\0';
556                                 strcpy(description, p);
557                         }
558                 }
559 #endif
560         }
561                 
562         FLAC_plugin__canonical_tag_clear(&tag);
563 }