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 }