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