add/update copyright and license notices
[flac.git] / src / plugin_winamp2 / in_flac.c
1 /* in_flac - Winamp2 FLAC input plugin
2  * Copyright (C) 2000,2001  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
24 #include "in2.h"
25 #include "FLAC/all.h"
26
27 BOOL WINAPI _DllMainCRTStartup(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
28 {
29         return TRUE;
30 }
31
32 /* post this to the main window at end of file (after playback as stopped) */
33 #define WM_WA_MPEG_EOF WM_USER+2
34
35 typedef struct {
36         bool abort_flag;
37         unsigned total_samples;
38         unsigned bits_per_sample;
39         unsigned channels;
40         unsigned sample_rate;
41         unsigned length_in_ms;
42 } stream_info_struct;
43
44 static bool stream_init(const char *infile);
45 static FLAC__StreamDecoderWriteStatus write_callback(const FLAC__FileDecoder *decoder, const FLAC__Frame *frame, const int32 *buffer[], void *client_data);
46 static void metadata_callback(const FLAC__FileDecoder *decoder, const FLAC__StreamMetaData *metadata, void *client_data);
47 static void error_callback(const FLAC__FileDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
48
49 In_Module mod; /* the output module (declared near the bottom of this file) */
50 char lastfn[MAX_PATH]; /* currently playing file (used for getting info on the current file) */
51 int decode_pos_ms; /* current decoding position, in milliseconds */
52 int paused; /* are we paused? */
53 int seek_needed; /* if != -1, it is the point that the decode thread should seek to, in ms. */
54 int16 reservoir[FLAC__MAX_BLOCK_SIZE * 2 * 2]; /* *2 for max channels, another *2 for overflow */
55 char sample_buffer[576 * 2 * (16/8) * 2]; /* 2 for max channels, (16/8) for max bytes per sample, and 2 for who knows what */
56 unsigned samples_in_reservoir;
57 static stream_info_struct stream_info;
58 static FLAC__FileDecoder *decoder;
59
60 int killDecodeThread=0;                                 /* the kill switch for the decode thread */
61 HANDLE thread_handle=INVALID_HANDLE_VALUE;      /* the handle to the decode thread */
62
63 DWORD WINAPI __stdcall DecodeThread(void *b); /* the decode thread procedure */
64
65 void config(HWND hwndParent)
66 {
67         MessageBox(hwndParent, "No configuration.", "Configuration", MB_OK);
68         /* if we had a configuration we'd want to write it here :) */
69 }
70 void about(HWND hwndParent)
71 {
72         MessageBox(hwndParent,"Winamp FLAC Plugin v" FLAC__VERSION_STRING ", by Josh Coalson\nSee http://flac.sourceforge.net/","About FLAC Plugin",MB_OK);
73 }
74
75 void init()
76 {
77         decoder = FLAC__file_decoder_get_new_instance();
78 }
79
80 void quit()
81 {
82         if(decoder)
83                 FLAC__file_decoder_free_instance(decoder);
84 }
85
86 int isourfile(char *fn) { return 0; }
87 /* used for detecting URL streams.. unused here. strncmp(fn,"http://",7) to detect HTTP streams, etc */
88
89 int play(char *fn)
90 {
91         int maxlatency;
92         int thread_id;
93         HANDLE input_file=INVALID_HANDLE_VALUE;
94
95         if(0 == decoder) {
96                 return 1;
97         }
98
99         input_file = CreateFile(fn,GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
100         if (input_file == INVALID_HANDLE_VALUE) {
101                 return 1;
102         }
103         CloseHandle(input_file);
104
105         if(!stream_init(fn)) {
106                 return 1;
107         }
108
109         strcpy(lastfn,fn);
110         paused=0;
111         decode_pos_ms=0;
112         seek_needed=-1;
113         samples_in_reservoir = 0;
114
115         maxlatency = mod.outMod->Open(stream_info.sample_rate, stream_info.channels, stream_info.bits_per_sample, -1,-1);
116         if (maxlatency < 0) { /* error opening device */
117                 return 1;
118         }
119
120         /* dividing by 1000 for the first parameter of setinfo makes it */
121         /* display 'H'... for hundred.. i.e. 14H Kbps. */
122         mod.SetInfo((stream_info.sample_rate*stream_info.bits_per_sample*stream_info.channels)/1000,stream_info.sample_rate/1000,stream_info.channels,1);
123
124         /* initialize vis stuff */
125         mod.SAVSAInit(maxlatency,stream_info.sample_rate);
126         mod.VSASetInfo(stream_info.sample_rate,stream_info.channels);
127
128         mod.outMod->SetVolume(-666); /* set the output plug-ins default volume */
129
130         killDecodeThread=0;
131         thread_handle = (HANDLE) CreateThread(NULL,0,(LPTHREAD_START_ROUTINE) DecodeThread,(void *) &killDecodeThread,0,&thread_id);
132
133         return 0;
134 }
135
136 void pause()
137 {
138         paused=1;
139         mod.outMod->Pause(1);
140 }
141
142 void unpause()
143 {
144         paused=0;
145         mod.outMod->Pause(0);
146 }
147 int ispaused()
148 {
149         return paused;
150 }
151
152 void stop()
153 {
154         if (thread_handle != INVALID_HANDLE_VALUE) {
155                 killDecodeThread=1;
156                 if (WaitForSingleObject(thread_handle,INFINITE) == WAIT_TIMEOUT) {
157                         MessageBox(mod.hMainWindow,"error asking thread to die!\n","error killing decode thread",0);
158                         TerminateThread(thread_handle,0);
159                 }
160                 CloseHandle(thread_handle);
161                 thread_handle = INVALID_HANDLE_VALUE;
162         }
163         if(decoder) {
164                 if(decoder->state != FLAC__FILE_DECODER_UNINITIALIZED)
165                         FLAC__file_decoder_finish(decoder);
166         }
167
168         mod.outMod->Close();
169
170         mod.SAVSADeInit();
171 }
172
173 int getlength()
174 {
175         return (int)stream_info.length_in_ms;
176 }
177
178 int getoutputtime()
179 {
180         return decode_pos_ms+(mod.outMod->GetOutputTime()-mod.outMod->GetWrittenTime());
181 }
182
183 void setoutputtime(int time_in_ms)
184 {
185         seek_needed=time_in_ms;
186 }
187
188 void setvolume(int volume) { mod.outMod->SetVolume(volume); }
189 void setpan(int pan) { mod.outMod->SetPan(pan); }
190
191 int infoDlg(char *fn, HWND hwnd)
192 {
193         /* TODO: implement info dialog. */
194         return 0;
195 }
196
197 void getfileinfo(char *filename, char *title, int *length_in_ms)
198 {
199         if (!filename || !*filename) { /* currently playing file */
200                 if (length_in_ms)
201                         *length_in_ms=getlength();
202                 if (title) {
203                         char *p=lastfn+strlen(lastfn);
204                         while (*p != '\\' && p >= lastfn) p--;
205                         strcpy(title,++p);
206                 }
207         }
208         else { /* some other file */
209                 if (length_in_ms) {
210                         FLAC__FileDecoder *tmp_decoder = FLAC__file_decoder_get_new_instance();
211                         stream_info_struct tmp_stream_info;
212                         tmp_decoder->check_md5 = false; /* turn off MD5 checking in the decoder */
213                         tmp_stream_info.abort_flag = false;
214                         if(FLAC__file_decoder_init(tmp_decoder, filename, write_callback, metadata_callback, error_callback, &tmp_stream_info) != FLAC__FILE_DECODER_OK)
215                                 return;
216                         if(!FLAC__file_decoder_process_metadata(tmp_decoder))
217                                 return;
218
219                         *length_in_ms = (int)tmp_stream_info.length_in_ms;
220
221                         if(tmp_decoder->state != FLAC__FILE_DECODER_UNINITIALIZED)
222                                 FLAC__file_decoder_finish(tmp_decoder);
223                         FLAC__file_decoder_free_instance(tmp_decoder);
224                 }
225                 if (title) {
226                         char *p=filename+strlen(filename);
227                         while (*p != '\\' && p >= filename) p--;
228                         strcpy(title,++p);
229                 }
230         }
231 }
232
233 void eq_set(int on, char data[10], int preamp)
234 {
235 }
236
237 DWORD WINAPI __stdcall DecodeThread(void *b)
238 {
239         int done=0;
240
241         while (! *((int *)b) ) {
242                 unsigned channels = stream_info.channels;
243                 unsigned bits_per_sample = stream_info.bits_per_sample;
244                 unsigned bytes_per_sample = (bits_per_sample+7)/8;
245                 unsigned sample_rate = stream_info.sample_rate;
246                 if (seek_needed != -1) {
247                         const double distance = (double)seek_needed / (double)getlength();
248                         unsigned target_sample = (unsigned)(distance * (double)stream_info.total_samples);
249                         if(FLAC__file_decoder_seek_absolute(decoder, (uint64)target_sample)) {
250                                 decode_pos_ms = (int)(distance * (double)getlength());
251                                 seek_needed=-1;
252                                 done=0;
253                                 mod.outMod->Flush(decode_pos_ms);
254                         }
255                 }
256                 if (done) {
257                         if (!mod.outMod->IsPlaying()) {
258                                 PostMessage(mod.hMainWindow,WM_WA_MPEG_EOF,0,0);
259                                 return 0;
260                         }
261                         Sleep(10);
262                 }
263                 else if (mod.outMod->CanWrite() >= ((int)(576*channels*bytes_per_sample) << (mod.dsp_isactive()?1:0))) {
264                         while(samples_in_reservoir < 576) {
265                                 if(decoder->state == FLAC__FILE_DECODER_END_OF_FILE) {
266                                         done = 1;
267                                         break;
268                                 }
269                                 else if(!FLAC__file_decoder_process_one_frame(decoder))
270                                         break;
271                         }
272
273                         if (samples_in_reservoir == 0) {
274                                 done=1;
275                         }
276                         else {
277                                 unsigned i, n = min(samples_in_reservoir, 576), delta;
278                                 int l;
279                                 signed short *ssbuffer = (signed short *)sample_buffer;
280
281                                 for(i = 0; i < n*channels; i++)
282                                         ssbuffer[i] = reservoir[i];
283                                 delta = i;
284                                 for( ; i < samples_in_reservoir*channels; i++)
285                                         reservoir[i-delta] = reservoir[i];
286                                 samples_in_reservoir -= n;
287                                 l = n * channels * bytes_per_sample;
288
289                                 mod.SAAddPCMData((char *)sample_buffer,channels,bits_per_sample,decode_pos_ms);
290                                 mod.VSAAddPCMData((char *)sample_buffer,channels,bits_per_sample,decode_pos_ms);
291                                 decode_pos_ms+=(n*1000 + sample_rate/2)/sample_rate;
292                                 if (mod.dsp_isactive())
293                                         l=mod.dsp_dosamples((short *)sample_buffer,n/channels/bytes_per_sample,bits_per_sample,channels,sample_rate) * (channels*bytes_per_sample);
294                                 mod.outMod->Write(sample_buffer,l);
295                         }
296                 }
297                 else Sleep(20);
298         }
299         return 0;
300 }
301
302
303
304 In_Module mod =
305 {
306         IN_VER,
307         "Reference FLAC Player v" FLAC__VERSION_STRING,
308         0,      /* hMainWindow */
309         0,  /* hDllInstance */
310         "FLAC\0FLAC Audio File (*.FLAC)\0"
311         ,
312         1,      /* is_seekable */
313         1, /* uses output */
314         config,
315         about,
316         init,
317         quit,
318         getfileinfo,
319         infoDlg,
320         isourfile,
321         play,
322         pause,
323         unpause,
324         ispaused,
325         stop,
326
327         getlength,
328         getoutputtime,
329         setoutputtime,
330
331         setvolume,
332         setpan,
333
334         0,0,0,0,0,0,0,0,0, /* vis stuff */
335
336
337         0,0, /* dsp */
338
339         eq_set,
340
341         NULL,           /* setinfo */
342
343         0 /* out_mod */
344
345 };
346
347 __declspec( dllexport ) In_Module * winampGetInModule2()
348 {
349         return &mod;
350 }
351
352
353 /***********************************************************************
354  * local routines
355  **********************************************************************/
356 bool stream_init(const char *infile)
357 {
358         decoder->check_md5 = false; /* turn off MD5 checking in the decoder */
359         if(FLAC__file_decoder_init(decoder, infile, write_callback, metadata_callback, error_callback, &stream_info) != FLAC__FILE_DECODER_OK) {
360                 MessageBox(mod.hMainWindow,"ERROR initializing decoder, state = %d\n","ERROR initializing decoder",0);
361                 return false;
362         }
363
364         stream_info.abort_flag = false;
365         if(!FLAC__file_decoder_process_metadata(decoder)) {
366                 return false;
367         }
368
369         return true;
370 }
371
372 FLAC__StreamDecoderWriteStatus write_callback(const FLAC__FileDecoder *decoder, const FLAC__Frame *frame, const int32 *buffer[], void *client_data)
373 {
374         stream_info_struct *stream_info = (stream_info_struct *)client_data;
375         const unsigned bps = stream_info->bits_per_sample, channels = stream_info->channels, wide_samples = frame->header.blocksize;
376         unsigned wide_sample, sample, channel;
377
378         (void)decoder;
379
380         if(stream_info->abort_flag)
381                 return FLAC__STREAM_DECODER_WRITE_ABORT;
382
383         for(sample = samples_in_reservoir*channels, wide_sample = 0; wide_sample < wide_samples; wide_sample++)
384                 for(channel = 0; channel < channels; channel++, sample++)
385                         reservoir[sample] = (int16)buffer[channel][wide_sample];
386
387         samples_in_reservoir += wide_samples;
388
389         return FLAC__STREAM_DECODER_WRITE_CONTINUE;
390 }
391
392 void metadata_callback(const FLAC__FileDecoder *decoder, const FLAC__StreamMetaData *metadata, void *client_data)
393 {
394         stream_info_struct *stream_info = (stream_info_struct *)client_data;
395         (void)decoder;
396         if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
397                 FLAC__ASSERT(metadata->data.stream_info.total_samples < 0x100000000); /* this plugin can only handle < 4 gigasamples */
398                 stream_info->total_samples = (unsigned)(metadata->data.stream_info.total_samples&0xffffffff);
399                 stream_info->bits_per_sample = metadata->data.stream_info.bits_per_sample;
400                 stream_info->channels = metadata->data.stream_info.channels;
401                 stream_info->sample_rate = metadata->data.stream_info.sample_rate;
402
403                 if(stream_info->bits_per_sample != 16) {
404                         MessageBox(mod.hMainWindow,"ERROR: plugin can only handle 16-bit samples\n","ERROR: plugin can only handle 16-bit samples",0);
405                         stream_info->abort_flag = true;
406                         return;
407                 }
408                 stream_info->length_in_ms = stream_info->total_samples * 10 / (stream_info->sample_rate / 100);
409         }
410 }
411
412 void error_callback(const FLAC__FileDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
413 {
414         stream_info_struct *stream_info = (stream_info_struct *)client_data;
415         (void)decoder;
416         if(status != FLAC__STREAM_DECODER_ERROR_LOST_SYNC)
417                 stream_info->abort_flag = true;
418 }