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