1 /*
2 ** FAAD - Freeware Advanced Audio Decoder
3 ** Copyright (C) 2002 M. Bakker
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 **
19 ** $Id: QCDFAAD.c,v 1.2 2003/04/28 19:04:35 menno Exp $
20 ** based on menno's in_faad.dll plugin for Winamp
21 **
22 ** The tag function has been removed because QCD supports ID3v1 & ID3v2 very well
23 ** About how to tagging: Please read the "ReadMe.txt" first
24 **/
25
26 #define WIN32_LEAN_AND_MEAN
27
28 #include <windows.h>
29 #include <mmreg.h>
30 #include <commctrl.h>
31 #include <shellapi.h>
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include "QCDInputDLL.h"
35
36 #include "resource.h"
37
38 #include <faad.h>
39 #include <aacinfo.h>
40 #include <filestream.h>
41 //#include <id3v2tag.h>
42
43 static char app_name[] = "QCDFAAD";
44
45 faadAACInfo file_info;
46
47 faacDecHandle hDecoder;
48 faacDecFrameInfo frameInfo;
49
50 HINSTANCE hInstance;
51 HWND hwndPlayer, hwndConfig, hwndAbout;
52 QCDModInitIn sQCDCallbacks, *QCDCallbacks;
53 BOOL oldAPIs = 0;
54 static char lastfn[MAX_PATH]; // currently playing file (used for getting info on the current file)
55 int file_length; // file length, in bytes
56 int paused; // are we paused?
57 int seek_needed; // if != -1, it is the point that the decode thread should seek to, in ms.
58
59 char *sample_buffer; // sample buffer
60 unsigned char *buffer; // input buffer
61 unsigned char *memmap_buffer; // input buffer for whole file
62 long memmap_index;
63
64 long buffercount, fileread, bytecount;
65
66 // seek table for ADTS header files
67 unsigned long *seek_table = NULL;
68 int seek_table_length=0;
69
70 int killPlayThread = 0; // the kill switch for the decode thread
71 HANDLE play_thread_handle = INVALID_HANDLE_VALUE; // the handle to the decode thread
72 FILE_STREAM *infile;
73
74 /* Function definitions */
75 int id3v2_tag(unsigned char *buffer);
76 DWORD WINAPI PlayThread(void *b); // the decode thread procedure
77
78 // general funcz
show_error(const char * message,...)79 static void show_error(const char *message,...)
80 {
81 char foo[512];
82 va_list args;
83 va_start(args, message);
84 vsprintf(foo, message, args);
85 va_end(args);
86 MessageBox(hwndPlayer, foo, "FAAD Plug-in Error", MB_ICONSTOP);
87 }
88
89
90 // 1= use vbr display, 0 = use average bitrate. This value only controls what shows up in the
91 // configuration form. Also- Streaming uses an on-the-fly bitrate display regardless of this value.
92 long m_variable_bitrate_display=0;
93 long m_priority = 5;
94 long m_memmap_file = 0;
95 static char INI_FILE[MAX_PATH];
96
97 char *priority_text[] = { "",
98 "Decode Thread Priority: Lowest",
99 "Decode Thread Priority: Lower",
100 "Decode Thread Priority: Normal",
101 "Decode Thread Priority: Higher",
102 "Decode Thread Priority: Highest (default)"
103 };
104
105 long priority_table[] = {0, THREAD_PRIORITY_LOWEST, THREAD_PRIORITY_BELOW_NORMAL, THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_ABOVE_NORMAL, THREAD_PRIORITY_HIGHEST};
106
107 long current_file_mode = 0;
108
109 int PlayThread_memmap();
110 int PlayThread_file();
111
_r_s(char * name,char * data,int mlen)112 static void _r_s(char *name,char *data, int mlen)
113 {
114 char buf[10];
115 strcpy(buf,data);
116 GetPrivateProfileString(app_name,name,buf,data,mlen,INI_FILE);
117 }
118
119 #define RS(x) (_r_s(#x,x,sizeof(x)))
120 #define WS(x) (WritePrivateProfileString(app_name,#x,x,INI_FILE))
121
config_read()122 void config_read()
123 {
124 char variable_bitrate_display[10];
125 char priority[10];
126 char memmap_file[10];
127 char local_buffer_size[10];
128 char stream_buffer_size[10];
129
130 strcpy(variable_bitrate_display, "1");
131 strcpy(priority, "5");
132 strcpy(memmap_file, "0");
133 strcpy(local_buffer_size, "128");
134 strcpy(stream_buffer_size, "64");
135
136 RS(variable_bitrate_display);
137 RS(priority);
138 RS(memmap_file);
139 RS(local_buffer_size);
140 RS(stream_buffer_size);
141
142 m_priority = atoi(priority);
143 m_variable_bitrate_display = atoi(variable_bitrate_display);
144 m_memmap_file = atoi(memmap_file);
145 m_local_buffer_size = atoi(local_buffer_size);
146 m_stream_buffer_size = atoi(stream_buffer_size);
147 }
148
config_write()149 void config_write()
150 {
151 char variable_bitrate_display[10];
152 char priority[10];
153 char memmap_file[10];
154 char local_buffer_size[10];
155 char stream_buffer_size[10];
156
157 itoa(m_priority, priority, 10);
158 itoa(m_variable_bitrate_display, variable_bitrate_display, 10);
159 itoa(m_memmap_file, memmap_file, 10);
160 itoa(m_local_buffer_size, local_buffer_size, 10);
161 itoa(m_stream_buffer_size, stream_buffer_size, 10);
162
163 WS(variable_bitrate_display);
164 WS(priority);
165 WS(memmap_file);
166 WS(local_buffer_size);
167 WS(stream_buffer_size);
168 }
169
170 //-----------------------------------------------------------------------------
171
DllMain(HINSTANCE hInst,DWORD fdwReason,LPVOID pRes)172 BOOL WINAPI DllMain(HINSTANCE hInst, DWORD fdwReason, LPVOID pRes)
173 {
174 if (fdwReason == DLL_PROCESS_ATTACH)
175 hInstance = hInst;
176 return TRUE;
177 }
178
179 //-----------------------------------------------------------------------------
180 //old entrypoint api
QInputModule(QCDModInitIn * ModInit,QCDModInfo * ModInfo)181 PLUGIN_API BOOL QInputModule(QCDModInitIn *ModInit, QCDModInfo *ModInfo)
182 {
183 ModInit->version = PLUGIN_API_VERSION;
184 ModInit->toModule.ShutDown = ShutDown;
185 ModInit->toModule.GetTrackExtents = GetTrackExtents;
186 ModInit->toModule.GetMediaSupported = GetMediaSupported;
187 ModInit->toModule.GetCurrentPosition= GetCurrentPosition;
188 ModInit->toModule.Play = Play;
189 ModInit->toModule.Pause = Pause;
190 ModInit->toModule.Stop = Stop;
191 ModInit->toModule.SetVolume = SetVolume;
192 ModInit->toModule.About = About;
193 ModInit->toModule.Configure = Configure;
194 QCDCallbacks = ModInit;
195
196 ModInfo->moduleString = "FAAD Plugin v1.0b";
197 /* read config */
198 QCDCallbacks->Service(opGetPluginSettingsFile, INI_FILE, MAX_PATH, 0);
199
200 config_read();
201 ModInfo->moduleExtensions = "AAC";
202
203 hwndPlayer = (HWND)ModInit->Service(opGetParentWnd, 0, 0, 0);
204 lastfn[0] = 0;
205 play_thread_handle = INVALID_HANDLE_VALUE;
206
207 oldAPIs = 1;
208
209 return TRUE;
210 }
211
212 //-----------------------------------------------------------------------------
213
INPUTDLL_ENTRY_POINT()214 PLUGIN_API QCDModInitIn* INPUTDLL_ENTRY_POINT()
215 {
216 sQCDCallbacks.version = PLUGIN_API_VERSION;
217 sQCDCallbacks.toModule.Initialize = Initialize;
218 sQCDCallbacks.toModule.ShutDown = ShutDown;
219 sQCDCallbacks.toModule.GetTrackExtents = GetTrackExtents;
220 sQCDCallbacks.toModule.GetMediaSupported = GetMediaSupported;
221 sQCDCallbacks.toModule.GetCurrentPosition = GetCurrentPosition;
222 sQCDCallbacks.toModule.Play = Play;
223 sQCDCallbacks.toModule.Pause = Pause;
224 sQCDCallbacks.toModule.Stop = Stop;
225 sQCDCallbacks.toModule.SetVolume = SetVolume;
226 sQCDCallbacks.toModule.About = About;
227 sQCDCallbacks.toModule.Configure = Configure;
228
229 QCDCallbacks = &sQCDCallbacks;
230 return &sQCDCallbacks;
231 }
232
233 //----------------------------------------------------------------------------
234
config_dialog_proc(HWND hwndDlg,UINT message,WPARAM wParam,LPARAM lParam)235 BOOL CALLBACK config_dialog_proc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
236 {
237 char tmp[10];
238
239 switch (message)
240 {
241 case WM_INITDIALOG:
242 /* Set priority slider range and previous position */
243 SendMessage(GetDlgItem(hwndDlg, THREAD_PRIORITY_SLIDER), TBM_SETRANGE, TRUE, MAKELONG(1, 5));
244 SendMessage(GetDlgItem(hwndDlg, THREAD_PRIORITY_SLIDER), TBM_SETPOS, TRUE, m_priority);
245 SetDlgItemText(hwndDlg, IDC_STATIC2, priority_text[m_priority]);
246
247 /* Put a limit to the amount of characters allowed in the buffer boxes */
248 SendMessage(GetDlgItem(hwndDlg, LOCAL_BUFFER_TXT), EM_LIMITTEXT, 4, 0);
249 SendMessage(GetDlgItem(hwndDlg, STREAM_BUFFER_TXT), EM_LIMITTEXT, 4, 0);
250
251 if(m_variable_bitrate_display)
252 SendMessage(GetDlgItem(hwndDlg, VARBITRATE_CHK), BM_SETCHECK, BST_CHECKED, 0);
253 if(m_memmap_file)
254 SendMessage(GetDlgItem(hwndDlg, IDC_MEMMAP), BM_SETCHECK, BST_CHECKED, 0);
255
256 itoa(m_local_buffer_size, tmp, 10);
257 SetDlgItemText(hwndDlg, LOCAL_BUFFER_TXT, tmp);
258
259 itoa(m_stream_buffer_size, tmp, 10);
260 SetDlgItemText(hwndDlg, STREAM_BUFFER_TXT, tmp);
261
262 return TRUE;
263
264 case WM_HSCROLL:
265
266 /* Thread priority slider moved */
267 if(GetDlgItem(hwndDlg, THREAD_PRIORITY_SLIDER) == (HWND) lParam)
268 {
269 int tmp;
270 tmp = SendMessage(GetDlgItem(hwndDlg, THREAD_PRIORITY_SLIDER), TBM_GETPOS, 0, 0);
271
272 if(tmp > 0)
273 {
274 m_priority = tmp;
275
276 SetDlgItemText(hwndDlg, IDC_STATIC2, priority_text[m_priority]);
277
278 if(play_thread_handle)
279 SetThreadPriority(play_thread_handle, priority_table[m_priority]);
280 }
281 }
282
283 return TRUE;
284
285 case WM_COMMAND:
286
287 if(HIWORD(wParam) == BN_CLICKED)
288 {
289 if(GetDlgItem(hwndDlg, VARBITRATE_CHK) == (HWND) lParam)
290 {
291 /* Variable Bitrate checkbox hit */
292 m_variable_bitrate_display = SendMessage(GetDlgItem(hwndDlg, VARBITRATE_CHK), BM_GETCHECK, 0, 0);
293 }
294 if(GetDlgItem(hwndDlg, IDC_MEMMAP) == (HWND) lParam)
295 {
296 /* Variable Bitrate checkbox hit */
297 m_memmap_file = SendMessage(GetDlgItem(hwndDlg, IDC_MEMMAP), BM_GETCHECK, 0, 0);
298 }
299 }
300
301 switch (LOWORD(wParam))
302 {
303 case OK_BTN:
304 /* User hit OK, save buffer settings (all others are set on command) */
305 GetDlgItemText(hwndDlg, LOCAL_BUFFER_TXT, tmp, 5);
306 m_local_buffer_size = atol(tmp);
307
308 GetDlgItemText(hwndDlg, STREAM_BUFFER_TXT, tmp, 5);
309 m_stream_buffer_size = atol(tmp);
310
311 config_write();
312
313 EndDialog(hwndDlg, wParam);
314 return TRUE;
315 case RESET_BTN:
316 SendMessage(GetDlgItem(hwndDlg, VARBITRATE_CHK), BM_SETCHECK, BST_CHECKED, 0);
317 m_variable_bitrate_display = 1;
318 SendMessage(GetDlgItem(hwndDlg, IDC_MEMMAP), BM_SETCHECK, BST_UNCHECKED, 0);
319 m_memmap_file = 0;
320 SendMessage(GetDlgItem(hwndDlg, THREAD_PRIORITY_SLIDER), TBM_SETPOS, TRUE, 5);
321 m_priority = 5;
322 SetDlgItemText(hwndDlg, IDC_STATIC2, priority_text[5]);
323 SetDlgItemText(hwndDlg, LOCAL_BUFFER_TXT, "128");
324 m_local_buffer_size = 128;
325 SetDlgItemText(hwndDlg, STREAM_BUFFER_TXT, "64");
326 m_stream_buffer_size = 64;
327 return TRUE;
328 case IDCANCEL:
329 case CANCEL_BTN:
330 /* User hit Cancel or the X, just close without saving buffer settings */
331 DestroyWindow(hwndDlg);
332 return TRUE;
333 }
334 }
335 return FALSE;
336 }
337
338
Configure(int flags)339 void Configure(int flags)
340 {
341 if(!IsWindow(hwndConfig))
342 hwndConfig = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_CONFIG), hwndPlayer, config_dialog_proc);
343 ShowWindow(hwndConfig, SW_NORMAL);
344 }
345
346 //-----------------------------------------------------------------------------
347 // proc of "About Dialog"
about_dialog_proc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)348 INT_PTR CALLBACK about_dialog_proc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
349 {
350 static RECT rcLOGO, rcMail1, rcMail2/*, rcMail3*/;
351 POINT ptMouse;
352 static char szPluginVer[] = "QCD FAAD Input Plug-in v1.0b\nCompiled on " __TIME__ ", " __DATE__;
353 static char szFLACVer[] = "Using: FAAD2 v "FAAD2_VERSION" by";
354
355 switch (uMsg)
356 {
357 case WM_INITDIALOG:
358 case WM_MOVE:
359 GetWindowRect(GetDlgItem(hwndDlg, IDC_LOGO), &rcLOGO);
360 GetWindowRect(GetDlgItem(hwndDlg, IDC_MAIL1), &rcMail1);
361 GetWindowRect(GetDlgItem(hwndDlg, IDC_MAIL2), &rcMail2);
362 // GetWindowRect(GetDlgItem(hwndDlg, IDC_MAIL2), &rcMail3);
363
364 SetDlgItemText(hwndDlg, IDC_PLUGINVER, szPluginVer);
365 SetDlgItemText(hwndDlg, IDC_FAADVER, szFLACVer);
366
367 return TRUE;
368 case WM_MOUSEMOVE:
369 ptMouse.x = LOWORD(lParam);
370 ptMouse.y = HIWORD(lParam);
371 ClientToScreen(hwndDlg, &ptMouse);
372 if( (ptMouse.x >= rcLOGO.left && ptMouse.x <= rcLOGO.right &&
373 ptMouse.y >= rcLOGO.top && ptMouse.y<= rcLOGO.bottom)
374 ||
375 (ptMouse.x >= rcMail1.left && ptMouse.x <= rcMail1.right &&
376 ptMouse.y >= rcMail1.top && ptMouse.y<= rcMail1.bottom)
377 ||
378 (ptMouse.x >= rcMail2.left && ptMouse.x <= rcMail2.right &&
379 ptMouse.y >= rcMail2.top && ptMouse.y<= rcMail2.bottom)
380 /* ||
381 (ptMouse.x >= rcMail3.left && ptMouse.x <= rcMail3.right &&
382 ptMouse.y >= rcMail3.top && ptMouse.y<= rcMail3.bottom)*/ )
383 SetCursor(LoadCursor(NULL, MAKEINTRESOURCE(32649)));
384 else
385 SetCursor(LoadCursor(NULL, IDC_ARROW));
386
387 return TRUE;
388 case WM_LBUTTONDOWN:
389 ptMouse.x = LOWORD(lParam);
390 ptMouse.y = HIWORD(lParam);
391 ClientToScreen(hwndDlg, &ptMouse);
392 if(ptMouse.x >= rcLOGO.left && ptMouse.x <= rcLOGO.right &&
393 ptMouse.y >= rcLOGO.top && ptMouse.y<= rcLOGO.bottom)
394 ShellExecute(0, NULL, "http://www.audiocoding.com", NULL,NULL, SW_NORMAL);
395 else if(ptMouse.x >= rcMail1.left && ptMouse.x <= rcMail1.right &&
396 ptMouse.y >= rcMail1.top && ptMouse.y<= rcMail1.bottom)
397 ShellExecute(0, NULL, "mailto:shaohao@elong.com", NULL,NULL, SW_NORMAL);
398 else if(ptMouse.x >= rcMail2.left && ptMouse.x <= rcMail2.right &&
399 ptMouse.y >= rcMail2.top && ptMouse.y<= rcMail2.bottom)
400 ShellExecute(0, NULL, "mailto:menno@audiocoding.com", NULL,NULL, SW_NORMAL);
401 /* else if(ptMouse.x >= rcMail3.left && ptMouse.x <= rcMail3.right &&
402 ptMouse.y >= rcMail3.top && ptMouse.y<= rcMail3.bottom)
403 ShellExecute(0, NULL, "I don't know", NULL,NULL, SW_NORMAL);
404 */
405 return TRUE;
406 case WM_COMMAND:
407 switch(LOWORD(wParam))
408 {
409 case IDOK:
410 default:
411 DestroyWindow(hwndDlg);
412 return TRUE;
413 }
414 }
415 return FALSE;
416 }
417
About(int flags)418 void About(int flags)
419 {
420 if(!IsWindow(hwndAbout))
421 hwndAbout = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_ABOUT), hwndPlayer, about_dialog_proc);
422 ShowWindow(hwndAbout, SW_SHOW);
423 }
424
425 //-----------------------------------------------------------------------------
426
Initialize(QCDModInfo * ModInfo,int flags)427 BOOL Initialize(QCDModInfo *ModInfo, int flags)
428 {
429 hwndPlayer = (HWND)QCDCallbacks->Service(opGetParentWnd, 0, 0, 0);
430
431 lastfn[0] = 0;
432 seek_needed = -1;
433 paused = 0;
434 play_thread_handle = INVALID_HANDLE_VALUE;
435
436 /* read config */
437 QCDCallbacks->Service(opGetPluginSettingsFile, INI_FILE, MAX_PATH, 0);
438 config_read();
439
440 ModInfo->moduleString = "FAAD Plugin v1.0b";
441 ModInfo->moduleExtensions = "AAC";
442
443 /* Initialize winsock, necessary for streaming */
444 WinsockInit();
445
446 // insert menu item into plugin menu
447 // QCDCallbacks->Service(opSetPluginMenuItem, hInstance, IDD_CONFIG, (long)"FAAD Plug-in");
448
449 return TRUE;
450 }
451
452 //----------------------------------------------------------------------------
453
ShutDown(int flags)454 void ShutDown(int flags)
455 {
456 Stop(lastfn, STOPFLAG_FORCESTOP);
457
458 if(buffer)
459 LocalFree(buffer);
460
461 /* Deallocate winsock */
462 WinsockDeInit();
463
464 // delete the inserted plugin menu
465 // QCDCallbacks->Service(opSetPluginMenuItem, hInstance, 0, 0);
466 }
467
468 //-----------------------------------------------------------------------------
469
GetMediaSupported(LPCSTR medianame,MediaInfo * mediaInfo)470 BOOL GetMediaSupported(LPCSTR medianame, MediaInfo *mediaInfo)
471 {
472 FILE_STREAM *in;
473 faadAACInfo tmp;
474 char *ch = strrchr(medianame, '.');
475
476 if (!medianame || !*medianame)
477 return FALSE;
478
479 if(!ch)
480 return (lstrlen(medianame) > 2); // no extension defaults to me (if not drive letter)
481
482 /* Finally fixed */
483 if(StringComp(ch, ".aac", 4) == 0)
484 {
485 in = open_filestream((char *)medianame);
486
487 if(in != NULL && mediaInfo)
488 {
489 if(in->http)
490 {
491 /* No seeking in http streams */
492 mediaInfo->mediaType = DIGITAL_STREAM_MEDIA;
493 mediaInfo->op_canSeek = FALSE;
494 }
495 else
496 {
497 mediaInfo->mediaType = DIGITAL_FILE_MEDIA;
498 get_AAC_format((char *)medianame, &tmp, NULL, NULL, 1);
499 if(tmp.headertype == 2) /* ADTS header - seekable */
500 mediaInfo->op_canSeek = TRUE;
501 else
502 mediaInfo->op_canSeek = FALSE; /* ADIF or Headerless - not seekable */
503 }
504
505 close_filestream(in);
506 return TRUE;
507 }
508 else
509 {
510 close_filestream(in);
511 return FALSE;
512 }
513 }
514 else
515 return FALSE;
516 }
517
518 unsigned long samplerate, channels;
519
play_memmap(char * fn)520 int play_memmap(char *fn)
521 {
522 int tagsize = 0;
523
524 infile = open_filestream(fn);
525
526 if (infile == NULL)
527 return 1;
528
529 fileread = filelength_filestream(infile);
530
531 memmap_buffer = (char*)LocalAlloc(LPTR, fileread);
532 read_buffer_filestream(infile, memmap_buffer, fileread);
533
534 /* skip id3v2 tag */
535 memmap_index = id3v2_tag(memmap_buffer);
536
537 hDecoder = faacDecOpen();
538
539 /* Copy the configuration dialog setting and use it as the default */
540 /* initialize the decoder, and get samplerate and channel info */
541
542 if( (buffercount = faacDecInit(hDecoder, memmap_buffer + memmap_index,
543 fileread - memmap_index - 1, &samplerate, &channels)) < 0 )
544 {
545 show_error("Error opening input file");
546 return 1;
547 }
548
549 memmap_index += buffercount;
550
551 PlayThread_memmap();
552
553 return 0;
554 }
555
play_file(char * fn)556 int play_file(char *fn)
557 {
558 int k;
559 int tagsize;
560
561 ZeroMemory(buffer, 768*2);
562
563 infile = open_filestream(fn);
564
565 if (infile == NULL)
566 return 1;
567
568 fileread = filelength_filestream(infile);
569
570 buffercount = bytecount = 0;
571 read_buffer_filestream(infile, buffer, 768*2);
572
573 tagsize = id3v2_tag(buffer);
574
575 /* If we find a tag, run right over it */
576 if(tagsize)
577 {
578 if(infile->http)
579 {
580 int i;
581 /* Crude way of doing this, but I believe its fast enough to not make a big difference */
582 close_filestream(infile);
583 infile = open_filestream(fn);
584
585 for(i=0; i < tagsize; i++)
586 read_byte_filestream(infile);
587 }
588 else
589 seek_filestream(infile, tagsize, FILE_BEGIN);
590
591 bytecount = tagsize;
592 buffercount = 0;
593 read_buffer_filestream(infile, buffer, 768*2);
594 }
595
596 hDecoder = faacDecOpen();
597
598 /* Copy the configuration dialog setting and use it as the default */
599 /* initialize the decoder, and get samplerate and channel info */
600
601 if((buffercount = faacDecInit(hDecoder, buffer, 768*2, &samplerate, &channels)) < 0)
602 {
603 show_error("Error opening input file");
604 return 1;
605 }
606
607 if(buffercount > 0)
608 {
609 bytecount += buffercount;
610
611 for (k = 0; k < (768*2 - buffercount); k++)
612 buffer[k] = buffer[k + buffercount];
613
614 read_buffer_filestream(infile, buffer + (768*2) - buffercount, buffercount);
615 buffercount = 0;
616 }
617
618 PlayThread_file();
619
620 return 0;
621 }
622
623
624 //-----------------------------------------------------------------------------
625
Play(LPCSTR medianame,int playfrom,int playto,int flags)626 BOOL Play(LPCSTR medianame, int playfrom, int playto, int flags)
627 {
628 if(stricmp(lastfn, medianame) != 0)
629 {
630 sQCDCallbacks.toPlayer.OutputStop(STOPFLAG_PLAYDONE);
631 Stop(lastfn, STOPFLAG_PLAYDONE);
632 }
633
634 if(paused)
635 {
636 // Update the player controls to reflect the new unpaused state
637 sQCDCallbacks.toPlayer.OutputPause(0);
638
639 Pause(medianame, PAUSE_DISABLED);
640
641 if (playfrom >= 0)
642 seek_needed = playfrom;
643 }
644 else if(play_thread_handle != INVALID_HANDLE_VALUE)
645 {
646 seek_needed = playfrom;
647 return TRUE;
648 }
649 else
650 {
651 int thread_id;
652
653 // alloc the input buffer
654 buffer = (unsigned char*)LocalAlloc(LPTR, 768*2);
655
656 current_file_mode = m_memmap_file;
657
658 if(current_file_mode)
659 {
660 if(play_memmap((char *)medianame))
661 return FALSE;
662 }
663 else
664 {
665 if(play_file((char *)medianame))
666 return FALSE;
667 }
668
669 if(seek_table)
670 {
671 free(seek_table);
672 seek_table = NULL;
673 seek_table_length = 0;
674 }
675
676 get_AAC_format((char *)medianame, &file_info, &seek_table, &seek_table_length, 0);
677
678 seek_needed = playfrom > 0 ? playfrom : -1;
679 killPlayThread = 0;
680 strcpy(lastfn,medianame);
681
682 /*
683 To RageAmp: This is really needed, because aacinfo isn't very accurate on ADIF files yet.
684 Can be fixed though :-)
685 */
686 file_info.sampling_rate = samplerate;
687 file_info.channels = frameInfo.channels;
688
689 play_thread_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) PlayThread, (void *) &killPlayThread, 0, &thread_id);
690 if(!play_thread_handle)
691 return FALSE;
692
693 // Note: This line seriously slows down start up time
694 if(m_priority != 3) // if the priority in config window is set to normal, there is nothing to reset!
695 SetThreadPriority(play_thread_handle, priority_table[m_priority]);
696
697 }
698
699 return TRUE;
700 }
701
702 //-----------------------------------------------------------------------------
703
Pause(LPCSTR medianame,int flags)704 BOOL Pause(LPCSTR medianame, int flags)
705 {
706 if(QCDCallbacks->toPlayer.OutputPause(flags))
707 {
708 // send back pause/unpause notification
709 QCDCallbacks->toPlayer.PlayPaused(medianame, flags);
710 paused = flags;
711 return TRUE;
712 }
713 return FALSE;
714 }
715
716 //-----------------------------------------------------------------------------
717
Stop(LPCSTR medianame,int flags)718 BOOL Stop(LPCSTR medianame, int flags)
719 {
720 if(medianame && *medianame && stricmp(lastfn, medianame) == 0)
721 {
722 sQCDCallbacks.toPlayer.OutputStop(flags);
723
724 killPlayThread = 1;
725 if(play_thread_handle != INVALID_HANDLE_VALUE)
726 {
727 if(WaitForSingleObject(play_thread_handle, INFINITE) == WAIT_TIMEOUT)
728 {
729 // MessageBox(hwndPlayer, "FAAD thread kill timeout", "debug", 0);
730 TerminateThread(play_thread_handle,0);
731 }
732 CloseHandle(play_thread_handle);
733 play_thread_handle = INVALID_HANDLE_VALUE;
734 }
735
736 if (oldAPIs)
737 QCDCallbacks->toPlayer.PlayStopped(lastfn);
738
739 lastfn[0] = 0;
740 }
741
742 return TRUE;
743 }
744
aac_seek(int pos_ms,int * sktable)745 int aac_seek(int pos_ms, int *sktable)
746 {
747 double offset_sec;
748
749 offset_sec = pos_ms / 1000.0;
750 if(!current_file_mode)
751 {
752 seek_filestream(infile, sktable[(int)(offset_sec+0.5)], FILE_BEGIN);
753
754 bytecount = sktable[(int)(offset_sec+0.5)];
755 buffercount = 0;
756 read_buffer_filestream(infile, buffer, 768*2);
757 }
758 else
759 {
760 memmap_index = sktable[(int)(offset_sec+0.5)];
761 }
762
763 return 0;
764 }
765
766 //-----------------------------------------------------------------------------
767
SetVolume(int levelleft,int levelright,int flags)768 void SetVolume(int levelleft, int levelright, int flags)
769 {
770 QCDCallbacks->toPlayer.OutputSetVol(levelleft, levelright, flags);
771 }
772
773 //-----------------------------------------------------------------------------
774
GetCurrentPosition(LPCSTR medianame,long * track,long * offset)775 BOOL GetCurrentPosition(LPCSTR medianame, long *track, long *offset)
776 {
777 return QCDCallbacks->toPlayer.OutputGetCurrentPosition((UINT*)offset, 0);
778 }
779
780 //-----------------------------------------------------------------------------
781
GetTrackExtents(LPCSTR medianame,TrackExtents * ext,int flags)782 BOOL GetTrackExtents(LPCSTR medianame, TrackExtents *ext, int flags)
783 {
784 faadAACInfo tmp;
785
786 if(get_AAC_format((char*)medianame, &tmp, NULL, NULL, 1))
787 return FALSE;
788
789 ext->track = 1;
790 ext->start = 0;
791 ext->end = tmp.length;
792 ext->bytesize = tmp.bitrate * tmp.length;
793 ext->unitpersec = 1000;
794
795 return TRUE;
796 }
797
798 //--------------------------for play thread-------------------------------------
799
800 int last_frame;
801
PlayThread(void * b)802 DWORD WINAPI PlayThread(void *b)
803 {
804 BOOL done = FALSE, updatePos = FALSE;
805 int decode_pos_ms = 0; // current decoding position, in milliseconds
806 int l;
807 int decoded_frames=0;
808 int br_calc_frames=0;
809 int br_bytes_consumed=0;
810 unsigned long bytesconsumed;
811
812 last_frame = 0;
813
814 if(!done)
815 {
816 // open outputdevice
817 WAVEFORMATEX wf;
818 wf.wFormatTag = WAVE_FORMAT_PCM;
819 wf.cbSize = 0;
820 wf.nChannels = file_info.channels;
821 wf.wBitsPerSample = 16;
822 wf.nSamplesPerSec = file_info.sampling_rate;
823 wf.nBlockAlign = wf.nChannels * wf.wBitsPerSample / 8;
824 wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign;
825 if (!QCDCallbacks->toPlayer.OutputOpen(lastfn, &wf))
826 {
827 show_error("Error: Failed openning output plugin!");
828 done = TRUE; // cannot open sound device
829 }
830 }
831
832 while (! *((int *)b) )
833 {
834 /********************** SEEK ************************/
835 if (!done && seek_needed >= 0)
836 {
837 int seconds;
838
839 // Round off to a second
840 seconds = seek_needed - (seek_needed%1000);
841 QCDCallbacks->toPlayer.OutputFlush(decode_pos_ms);
842 aac_seek(seconds, seek_table);
843 decode_pos_ms = seconds;
844 decoded_frames = 0;
845 br_calc_frames = 0;
846 br_bytes_consumed = 0;
847
848 seek_needed = -1;
849 updatePos = 1;
850 }
851
852 /********************* QUIT *************************/
853 if (done)
854 {
855 if (QCDCallbacks->toPlayer.OutputDrain(0) && !(seek_needed >= 0))
856 {
857 play_thread_handle = INVALID_HANDLE_VALUE;
858 QCDCallbacks->toPlayer.OutputStop(STOPFLAG_PLAYDONE);
859 QCDCallbacks->toPlayer.PlayDone(lastfn);
860 }
861 else if (seek_needed >= 0)
862 {
863 done = FALSE;
864 continue;
865 }
866 break;
867 }
868
869 /******************* DECODE TO BUFFER ****************/
870 else
871 {
872 if (current_file_mode)
873 bytesconsumed = PlayThread_memmap();
874 else
875 bytesconsumed = PlayThread_file();
876
877 if(last_frame)
878 done = TRUE;
879 else
880 {
881
882 decoded_frames++;
883 br_calc_frames++;
884 br_bytes_consumed += bytesconsumed;
885
886 /* Update the variable bitrate about every second */
887 if(m_variable_bitrate_display && br_calc_frames == 43)
888 {
889 AudioInfo vai;
890 vai.struct_size = sizeof(AudioInfo);
891 vai.frequency = file_info.sampling_rate;
892 vai.bitrate = (int)((br_bytes_consumed * 8) / (decoded_frames / 43.07));
893 vai.mode = (channels == 2) ? 0 : 3;
894 vai.layer = 0;
895 vai.level = file_info.version;
896 QCDCallbacks->Service(opSetAudioInfo, &vai, sizeof(AudioInfo), 0);
897
898 br_calc_frames = 0;
899 }
900
901 if (!killPlayThread && (frameInfo.samples > 0))
902 {
903 //update the time display
904 if (updatePos)
905 {
906 QCDCallbacks->toPlayer.PositionUpdate(decode_pos_ms);
907 updatePos = 0;
908 }
909
910 {
911 WriteDataStruct wd;
912
913 l = frameInfo.samples * sizeof(short);
914
915 decode_pos_ms += (1024*1000)/file_info.sampling_rate;
916
917 wd.bytelen = l;
918 wd.data = sample_buffer;
919 wd.markerend = 0;
920 wd.markerstart = decode_pos_ms;
921 wd.bps = 16;
922 wd.nch = frameInfo.channels;
923 wd.numsamples =l/file_info.channels/(16/8);
924 wd.srate = file_info.sampling_rate;
925
926 if (!QCDCallbacks->toPlayer.OutputWrite(&wd))
927 done = TRUE;
928 }
929 }
930 }
931 }
932 Sleep(10);
933 }
934
935 // close up
936 play_thread_handle = INVALID_HANDLE_VALUE;
937
938 faacDecClose(hDecoder);
939 hDecoder = INVALID_HANDLE_VALUE;
940 close_filestream(infile);
941 infile = NULL;
942
943 if(seek_table)
944 {
945 free(seek_table);
946 seek_table = NULL;
947 seek_table_length = 0;
948 }
949
950 if(buffer)
951 {
952 LocalFree(buffer);
953 buffer = NULL;
954 }
955 if(memmap_buffer)
956 {
957 LocalFree(memmap_buffer);
958 memmap_buffer = NULL;
959 }
960
961 return 0;
962 }
963
964 // thread play funcs
PlayThread_memmap()965 int PlayThread_memmap()
966 {
967 sample_buffer = (char*)faacDecDecode(hDecoder, &frameInfo,
968 memmap_buffer + memmap_index, fileread - memmap_index - 1);
969 if (frameInfo.error)
970 {
971 // show_error(faacDecGetErrorMessage(frameInfo.error));
972 last_frame = 1;
973 }
974
975 memmap_index += frameInfo.bytesconsumed;
976 if (memmap_index >= fileread)
977 last_frame = 1;
978
979 return frameInfo.bytesconsumed;
980 }
981
PlayThread_file()982 int PlayThread_file()
983 {
984 int k;
985
986 if (buffercount > 0)
987 {
988 for (k = 0; k < (768*2 - buffercount); k++)
989 buffer[k] = buffer[k + buffercount];
990
991 read_buffer_filestream(infile, buffer + (768*2) - buffercount, buffercount);
992 buffercount = 0;
993 }
994
995 sample_buffer = (char*)faacDecDecode(hDecoder, &frameInfo, buffer, 768*2);
996 if (frameInfo.error)
997 {
998 // show_error(faacDecGetErrorMessage(frameInfo.error));
999 last_frame = 1;
1000 }
1001
1002 buffercount += frameInfo.bytesconsumed;
1003
1004 bytecount += frameInfo.bytesconsumed;
1005 if (bytecount >= fileread)
1006 last_frame = 1;
1007
1008 return frameInfo.bytesconsumed;
1009 }
1010
1011 // tag
id3v2_tag(unsigned char * buffer)1012 int id3v2_tag(unsigned char *buffer)
1013 {
1014 if (StringComp(buffer, "ID3", 3) == 0)
1015 {
1016 unsigned long tagsize;
1017
1018 /* high bit is not used */
1019 tagsize = (buffer[6] << 21) | (buffer[7] << 14) |
1020 (buffer[8] << 7) | (buffer[9] << 0);
1021
1022 tagsize += 10;
1023
1024 return tagsize;
1025 }
1026 else
1027 {
1028 return 0;
1029 }
1030 }