2
** FAAD - Freeware Advanced Audio Decoder
3
** Copyright (C) 2002 M. Bakker
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.
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.
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.
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
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
26
#define WIN32_LEAN_AND_MEAN
34
#include "QCDInputDLL.h"
40
#include <filestream.h>
41
//#include <id3v2tag.h>
43
static char app_name[] = "QCDFAAD";
45
faadAACInfo file_info;
47
faacDecHandle hDecoder;
48
faacDecFrameInfo frameInfo;
51
HWND hwndPlayer, hwndConfig, hwndAbout;
52
QCDModInitIn sQCDCallbacks, *QCDCallbacks;
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.
59
char *sample_buffer; // sample buffer
60
unsigned char *buffer; // input buffer
61
unsigned char *memmap_buffer; // input buffer for whole file
64
long buffercount, fileread, bytecount;
66
// seek table for ADTS header files
67
unsigned long *seek_table = NULL;
68
int seek_table_length=0;
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
74
/* Function definitions */
75
int id3v2_tag(unsigned char *buffer);
76
DWORD WINAPI PlayThread(void *b); // the decode thread procedure
79
static void show_error(const char *message,...)
83
va_start(args, message);
84
vsprintf(foo, message, args);
86
MessageBox(hwndPlayer, foo, "FAAD Plug-in Error", MB_ICONSTOP);
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;
94
long m_memmap_file = 0;
95
static char INI_FILE[MAX_PATH];
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)"
105
long priority_table[] = {0, THREAD_PRIORITY_LOWEST, THREAD_PRIORITY_BELOW_NORMAL, THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_ABOVE_NORMAL, THREAD_PRIORITY_HIGHEST};
107
long current_file_mode = 0;
109
int PlayThread_memmap();
110
int PlayThread_file();
112
static void _r_s(char *name,char *data, int mlen)
116
GetPrivateProfileString(app_name,name,buf,data,mlen,INI_FILE);
119
#define RS(x) (_r_s(#x,x,sizeof(x)))
120
#define WS(x) (WritePrivateProfileString(app_name,#x,x,INI_FILE))
124
char variable_bitrate_display[10];
126
char memmap_file[10];
127
char local_buffer_size[10];
128
char stream_buffer_size[10];
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");
136
RS(variable_bitrate_display);
139
RS(local_buffer_size);
140
RS(stream_buffer_size);
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);
151
char variable_bitrate_display[10];
153
char memmap_file[10];
154
char local_buffer_size[10];
155
char stream_buffer_size[10];
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);
163
WS(variable_bitrate_display);
166
WS(local_buffer_size);
167
WS(stream_buffer_size);
170
//-----------------------------------------------------------------------------
172
BOOL WINAPI DllMain(HINSTANCE hInst, DWORD fdwReason, LPVOID pRes)
174
if (fdwReason == DLL_PROCESS_ATTACH)
179
//-----------------------------------------------------------------------------
181
PLUGIN_API BOOL QInputModule(QCDModInitIn *ModInit, QCDModInfo *ModInfo)
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;
196
ModInfo->moduleString = "FAAD Plugin v1.0b";
198
QCDCallbacks->Service(opGetPluginSettingsFile, INI_FILE, MAX_PATH, 0);
201
ModInfo->moduleExtensions = "AAC";
203
hwndPlayer = (HWND)ModInit->Service(opGetParentWnd, 0, 0, 0);
205
play_thread_handle = INVALID_HANDLE_VALUE;
212
//-----------------------------------------------------------------------------
214
PLUGIN_API QCDModInitIn* INPUTDLL_ENTRY_POINT()
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;
229
QCDCallbacks = &sQCDCallbacks;
230
return &sQCDCallbacks;
233
//----------------------------------------------------------------------------
235
BOOL CALLBACK config_dialog_proc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
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]);
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);
251
if(m_variable_bitrate_display)
252
SendMessage(GetDlgItem(hwndDlg, VARBITRATE_CHK), BM_SETCHECK, BST_CHECKED, 0);
254
SendMessage(GetDlgItem(hwndDlg, IDC_MEMMAP), BM_SETCHECK, BST_CHECKED, 0);
256
itoa(m_local_buffer_size, tmp, 10);
257
SetDlgItemText(hwndDlg, LOCAL_BUFFER_TXT, tmp);
259
itoa(m_stream_buffer_size, tmp, 10);
260
SetDlgItemText(hwndDlg, STREAM_BUFFER_TXT, tmp);
266
/* Thread priority slider moved */
267
if(GetDlgItem(hwndDlg, THREAD_PRIORITY_SLIDER) == (HWND) lParam)
270
tmp = SendMessage(GetDlgItem(hwndDlg, THREAD_PRIORITY_SLIDER), TBM_GETPOS, 0, 0);
276
SetDlgItemText(hwndDlg, IDC_STATIC2, priority_text[m_priority]);
278
if(play_thread_handle)
279
SetThreadPriority(play_thread_handle, priority_table[m_priority]);
287
if(HIWORD(wParam) == BN_CLICKED)
289
if(GetDlgItem(hwndDlg, VARBITRATE_CHK) == (HWND) lParam)
291
/* Variable Bitrate checkbox hit */
292
m_variable_bitrate_display = SendMessage(GetDlgItem(hwndDlg, VARBITRATE_CHK), BM_GETCHECK, 0, 0);
294
if(GetDlgItem(hwndDlg, IDC_MEMMAP) == (HWND) lParam)
296
/* Variable Bitrate checkbox hit */
297
m_memmap_file = SendMessage(GetDlgItem(hwndDlg, IDC_MEMMAP), BM_GETCHECK, 0, 0);
301
switch (LOWORD(wParam))
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);
308
GetDlgItemText(hwndDlg, STREAM_BUFFER_TXT, tmp, 5);
309
m_stream_buffer_size = atol(tmp);
313
EndDialog(hwndDlg, wParam);
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);
320
SendMessage(GetDlgItem(hwndDlg, THREAD_PRIORITY_SLIDER), TBM_SETPOS, TRUE, 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;
330
/* User hit Cancel or the X, just close without saving buffer settings */
331
DestroyWindow(hwndDlg);
339
void Configure(int flags)
341
if(!IsWindow(hwndConfig))
342
hwndConfig = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_CONFIG), hwndPlayer, config_dialog_proc);
343
ShowWindow(hwndConfig, SW_NORMAL);
346
//-----------------------------------------------------------------------------
347
// proc of "About Dialog"
348
INT_PTR CALLBACK about_dialog_proc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
350
static RECT rcLOGO, rcMail1, rcMail2/*, rcMail3*/;
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";
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);
364
SetDlgItemText(hwndDlg, IDC_PLUGINVER, szPluginVer);
365
SetDlgItemText(hwndDlg, IDC_FAADVER, szFLACVer);
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)
375
(ptMouse.x >= rcMail1.left && ptMouse.x <= rcMail1.right &&
376
ptMouse.y >= rcMail1.top && ptMouse.y<= rcMail1.bottom)
378
(ptMouse.x >= rcMail2.left && ptMouse.x <= rcMail2.right &&
379
ptMouse.y >= rcMail2.top && ptMouse.y<= rcMail2.bottom)
381
(ptMouse.x >= rcMail3.left && ptMouse.x <= rcMail3.right &&
382
ptMouse.y >= rcMail3.top && ptMouse.y<= rcMail3.bottom)*/ )
383
SetCursor(LoadCursor(NULL, MAKEINTRESOURCE(32649)));
385
SetCursor(LoadCursor(NULL, IDC_ARROW));
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);
407
switch(LOWORD(wParam))
411
DestroyWindow(hwndDlg);
418
void About(int flags)
420
if(!IsWindow(hwndAbout))
421
hwndAbout = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_ABOUT), hwndPlayer, about_dialog_proc);
422
ShowWindow(hwndAbout, SW_SHOW);
425
//-----------------------------------------------------------------------------
427
BOOL Initialize(QCDModInfo *ModInfo, int flags)
429
hwndPlayer = (HWND)QCDCallbacks->Service(opGetParentWnd, 0, 0, 0);
434
play_thread_handle = INVALID_HANDLE_VALUE;
437
QCDCallbacks->Service(opGetPluginSettingsFile, INI_FILE, MAX_PATH, 0);
440
ModInfo->moduleString = "FAAD Plugin v1.0b";
441
ModInfo->moduleExtensions = "AAC";
443
/* Initialize winsock, necessary for streaming */
446
// insert menu item into plugin menu
447
// QCDCallbacks->Service(opSetPluginMenuItem, hInstance, IDD_CONFIG, (long)"FAAD Plug-in");
452
//----------------------------------------------------------------------------
454
void ShutDown(int flags)
456
Stop(lastfn, STOPFLAG_FORCESTOP);
461
/* Deallocate winsock */
464
// delete the inserted plugin menu
465
// QCDCallbacks->Service(opSetPluginMenuItem, hInstance, 0, 0);
468
//-----------------------------------------------------------------------------
470
BOOL GetMediaSupported(LPCSTR medianame, MediaInfo *mediaInfo)
474
char *ch = strrchr(medianame, '.');
476
if (!medianame || !*medianame)
480
return (lstrlen(medianame) > 2); // no extension defaults to me (if not drive letter)
483
if(StringComp(ch, ".aac", 4) == 0)
485
in = open_filestream((char *)medianame);
487
if(in != NULL && mediaInfo)
491
/* No seeking in http streams */
492
mediaInfo->mediaType = DIGITAL_STREAM_MEDIA;
493
mediaInfo->op_canSeek = FALSE;
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;
502
mediaInfo->op_canSeek = FALSE; /* ADIF or Headerless - not seekable */
505
close_filestream(in);
510
close_filestream(in);
518
unsigned long samplerate, channels;
520
int play_memmap(char *fn)
524
infile = open_filestream(fn);
529
fileread = filelength_filestream(infile);
531
memmap_buffer = (char*)LocalAlloc(LPTR, fileread);
532
read_buffer_filestream(infile, memmap_buffer, fileread);
535
memmap_index = id3v2_tag(memmap_buffer);
537
hDecoder = faacDecOpen();
539
/* Copy the configuration dialog setting and use it as the default */
540
/* initialize the decoder, and get samplerate and channel info */
542
if( (buffercount = faacDecInit(hDecoder, memmap_buffer + memmap_index,
543
fileread - memmap_index - 1, &samplerate, &channels)) < 0 )
545
show_error("Error opening input file");
549
memmap_index += buffercount;
556
int play_file(char *fn)
561
ZeroMemory(buffer, 768*2);
563
infile = open_filestream(fn);
568
fileread = filelength_filestream(infile);
570
buffercount = bytecount = 0;
571
read_buffer_filestream(infile, buffer, 768*2);
573
tagsize = id3v2_tag(buffer);
575
/* If we find a tag, run right over it */
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);
585
for(i=0; i < tagsize; i++)
586
read_byte_filestream(infile);
589
seek_filestream(infile, tagsize, FILE_BEGIN);
593
read_buffer_filestream(infile, buffer, 768*2);
596
hDecoder = faacDecOpen();
598
/* Copy the configuration dialog setting and use it as the default */
599
/* initialize the decoder, and get samplerate and channel info */
601
if((buffercount = faacDecInit(hDecoder, buffer, 768*2, &samplerate, &channels)) < 0)
603
show_error("Error opening input file");
609
bytecount += buffercount;
611
for (k = 0; k < (768*2 - buffercount); k++)
612
buffer[k] = buffer[k + buffercount];
614
read_buffer_filestream(infile, buffer + (768*2) - buffercount, buffercount);
624
//-----------------------------------------------------------------------------
626
BOOL Play(LPCSTR medianame, int playfrom, int playto, int flags)
628
if(stricmp(lastfn, medianame) != 0)
630
sQCDCallbacks.toPlayer.OutputStop(STOPFLAG_PLAYDONE);
631
Stop(lastfn, STOPFLAG_PLAYDONE);
636
// Update the player controls to reflect the new unpaused state
637
sQCDCallbacks.toPlayer.OutputPause(0);
639
Pause(medianame, PAUSE_DISABLED);
642
seek_needed = playfrom;
644
else if(play_thread_handle != INVALID_HANDLE_VALUE)
646
seek_needed = playfrom;
653
// alloc the input buffer
654
buffer = (unsigned char*)LocalAlloc(LPTR, 768*2);
656
current_file_mode = m_memmap_file;
658
if(current_file_mode)
660
if(play_memmap((char *)medianame))
665
if(play_file((char *)medianame))
673
seek_table_length = 0;
676
get_AAC_format((char *)medianame, &file_info, &seek_table, &seek_table_length, 0);
678
seek_needed = playfrom > 0 ? playfrom : -1;
680
strcpy(lastfn,medianame);
683
To RageAmp: This is really needed, because aacinfo isn't very accurate on ADIF files yet.
684
Can be fixed though :-)
686
file_info.sampling_rate = samplerate;
687
file_info.channels = frameInfo.channels;
689
play_thread_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) PlayThread, (void *) &killPlayThread, 0, &thread_id);
690
if(!play_thread_handle)
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]);
702
//-----------------------------------------------------------------------------
704
BOOL Pause(LPCSTR medianame, int flags)
706
if(QCDCallbacks->toPlayer.OutputPause(flags))
708
// send back pause/unpause notification
709
QCDCallbacks->toPlayer.PlayPaused(medianame, flags);
716
//-----------------------------------------------------------------------------
718
BOOL Stop(LPCSTR medianame, int flags)
720
if(medianame && *medianame && stricmp(lastfn, medianame) == 0)
722
sQCDCallbacks.toPlayer.OutputStop(flags);
725
if(play_thread_handle != INVALID_HANDLE_VALUE)
727
if(WaitForSingleObject(play_thread_handle, INFINITE) == WAIT_TIMEOUT)
729
// MessageBox(hwndPlayer, "FAAD thread kill timeout", "debug", 0);
730
TerminateThread(play_thread_handle,0);
732
CloseHandle(play_thread_handle);
733
play_thread_handle = INVALID_HANDLE_VALUE;
737
QCDCallbacks->toPlayer.PlayStopped(lastfn);
745
int aac_seek(int pos_ms, int *sktable)
749
offset_sec = pos_ms / 1000.0;
750
if(!current_file_mode)
752
seek_filestream(infile, sktable[(int)(offset_sec+0.5)], FILE_BEGIN);
754
bytecount = sktable[(int)(offset_sec+0.5)];
756
read_buffer_filestream(infile, buffer, 768*2);
760
memmap_index = sktable[(int)(offset_sec+0.5)];
766
//-----------------------------------------------------------------------------
768
void SetVolume(int levelleft, int levelright, int flags)
770
QCDCallbacks->toPlayer.OutputSetVol(levelleft, levelright, flags);
773
//-----------------------------------------------------------------------------
775
BOOL GetCurrentPosition(LPCSTR medianame, long *track, long *offset)
777
return QCDCallbacks->toPlayer.OutputGetCurrentPosition((UINT*)offset, 0);
780
//-----------------------------------------------------------------------------
782
BOOL GetTrackExtents(LPCSTR medianame, TrackExtents *ext, int flags)
786
if(get_AAC_format((char*)medianame, &tmp, NULL, NULL, 1))
791
ext->end = tmp.length;
792
ext->bytesize = tmp.bitrate * tmp.length;
793
ext->unitpersec = 1000;
798
//--------------------------for play thread-------------------------------------
802
DWORD WINAPI PlayThread(void *b)
804
BOOL done = FALSE, updatePos = FALSE;
805
int decode_pos_ms = 0; // current decoding position, in milliseconds
807
int decoded_frames=0;
808
int br_calc_frames=0;
809
int br_bytes_consumed=0;
810
unsigned long bytesconsumed;
818
wf.wFormatTag = WAVE_FORMAT_PCM;
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))
827
show_error("Error: Failed openning output plugin!");
828
done = TRUE; // cannot open sound device
832
while (! *((int *)b) )
834
/********************** SEEK ************************/
835
if (!done && seek_needed >= 0)
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;
846
br_bytes_consumed = 0;
852
/********************* QUIT *************************/
855
if (QCDCallbacks->toPlayer.OutputDrain(0) && !(seek_needed >= 0))
857
play_thread_handle = INVALID_HANDLE_VALUE;
858
QCDCallbacks->toPlayer.OutputStop(STOPFLAG_PLAYDONE);
859
QCDCallbacks->toPlayer.PlayDone(lastfn);
861
else if (seek_needed >= 0)
869
/******************* DECODE TO BUFFER ****************/
872
if (current_file_mode)
873
bytesconsumed = PlayThread_memmap();
875
bytesconsumed = PlayThread_file();
884
br_bytes_consumed += bytesconsumed;
886
/* Update the variable bitrate about every second */
887
if(m_variable_bitrate_display && br_calc_frames == 43)
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;
895
vai.level = file_info.version;
896
QCDCallbacks->Service(opSetAudioInfo, &vai, sizeof(AudioInfo), 0);
901
if (!killPlayThread && (frameInfo.samples > 0))
903
//update the time display
906
QCDCallbacks->toPlayer.PositionUpdate(decode_pos_ms);
913
l = frameInfo.samples * sizeof(short);
915
decode_pos_ms += (1024*1000)/file_info.sampling_rate;
918
wd.data = sample_buffer;
920
wd.markerstart = decode_pos_ms;
922
wd.nch = frameInfo.channels;
923
wd.numsamples =l/file_info.channels/(16/8);
924
wd.srate = file_info.sampling_rate;
926
if (!QCDCallbacks->toPlayer.OutputWrite(&wd))
936
play_thread_handle = INVALID_HANDLE_VALUE;
938
faacDecClose(hDecoder);
939
hDecoder = INVALID_HANDLE_VALUE;
940
close_filestream(infile);
947
seek_table_length = 0;
957
LocalFree(memmap_buffer);
958
memmap_buffer = NULL;
965
int PlayThread_memmap()
967
sample_buffer = (char*)faacDecDecode(hDecoder, &frameInfo,
968
memmap_buffer + memmap_index, fileread - memmap_index - 1);
971
// show_error(faacDecGetErrorMessage(frameInfo.error));
975
memmap_index += frameInfo.bytesconsumed;
976
if (memmap_index >= fileread)
979
return frameInfo.bytesconsumed;
982
int PlayThread_file()
988
for (k = 0; k < (768*2 - buffercount); k++)
989
buffer[k] = buffer[k + buffercount];
991
read_buffer_filestream(infile, buffer + (768*2) - buffercount, buffercount);
995
sample_buffer = (char*)faacDecDecode(hDecoder, &frameInfo, buffer, 768*2);
998
// show_error(faacDecGetErrorMessage(frameInfo.error));
1002
buffercount += frameInfo.bytesconsumed;
1004
bytecount += frameInfo.bytesconsumed;
1005
if (bytecount >= fileread)
1008
return frameInfo.bytesconsumed;
1012
int id3v2_tag(unsigned char *buffer)
1014
if (StringComp(buffer, "ID3", 3) == 0)
1016
unsigned long tagsize;
1018
/* high bit is not used */
1019
tagsize = (buffer[6] << 21) | (buffer[7] << 14) |
1020
(buffer[8] << 7) | (buffer[9] << 0);
2
** FAAD - Freeware Advanced Audio Decoder
3
** Copyright (C) 2002 M. Bakker
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.
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.
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.
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
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
26
#define WIN32_LEAN_AND_MEAN
34
#include "QCDInputDLL.h"
40
#include <filestream.h>
41
//#include <id3v2tag.h>
43
static char app_name[] = "QCDFAAD";
45
faadAACInfo file_info;
47
faacDecHandle hDecoder;
48
faacDecFrameInfo frameInfo;
51
HWND hwndPlayer, hwndConfig, hwndAbout;
52
QCDModInitIn sQCDCallbacks, *QCDCallbacks;
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.
59
char *sample_buffer; // sample buffer
60
unsigned char *buffer; // input buffer
61
unsigned char *memmap_buffer; // input buffer for whole file
64
long buffercount, fileread, bytecount;
66
// seek table for ADTS header files
67
unsigned long *seek_table = NULL;
68
int seek_table_length=0;
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
74
/* Function definitions */
75
int id3v2_tag(unsigned char *buffer);
76
DWORD WINAPI PlayThread(void *b); // the decode thread procedure
79
static void show_error(const char *message,...)
83
va_start(args, message);
84
vsprintf(foo, message, args);
86
MessageBox(hwndPlayer, foo, "FAAD Plug-in Error", MB_ICONSTOP);
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;
94
long m_memmap_file = 0;
95
static char INI_FILE[MAX_PATH];
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)"
105
long priority_table[] = {0, THREAD_PRIORITY_LOWEST, THREAD_PRIORITY_BELOW_NORMAL, THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_ABOVE_NORMAL, THREAD_PRIORITY_HIGHEST};
107
long current_file_mode = 0;
109
int PlayThread_memmap();
110
int PlayThread_file();
112
static void _r_s(char *name,char *data, int mlen)
116
GetPrivateProfileString(app_name,name,buf,data,mlen,INI_FILE);
119
#define RS(x) (_r_s(#x,x,sizeof(x)))
120
#define WS(x) (WritePrivateProfileString(app_name,#x,x,INI_FILE))
124
char variable_bitrate_display[10];
126
char memmap_file[10];
127
char local_buffer_size[10];
128
char stream_buffer_size[10];
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");
136
RS(variable_bitrate_display);
139
RS(local_buffer_size);
140
RS(stream_buffer_size);
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);
151
char variable_bitrate_display[10];
153
char memmap_file[10];
154
char local_buffer_size[10];
155
char stream_buffer_size[10];
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);
163
WS(variable_bitrate_display);
166
WS(local_buffer_size);
167
WS(stream_buffer_size);
170
//-----------------------------------------------------------------------------
172
BOOL WINAPI DllMain(HINSTANCE hInst, DWORD fdwReason, LPVOID pRes)
174
if (fdwReason == DLL_PROCESS_ATTACH)
179
//-----------------------------------------------------------------------------
181
PLUGIN_API BOOL QInputModule(QCDModInitIn *ModInit, QCDModInfo *ModInfo)
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;
196
ModInfo->moduleString = "FAAD Plugin v1.0b";
198
QCDCallbacks->Service(opGetPluginSettingsFile, INI_FILE, MAX_PATH, 0);
201
ModInfo->moduleExtensions = "AAC";
203
hwndPlayer = (HWND)ModInit->Service(opGetParentWnd, 0, 0, 0);
205
play_thread_handle = INVALID_HANDLE_VALUE;
212
//-----------------------------------------------------------------------------
214
PLUGIN_API QCDModInitIn* INPUTDLL_ENTRY_POINT()
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;
229
QCDCallbacks = &sQCDCallbacks;
230
return &sQCDCallbacks;
233
//----------------------------------------------------------------------------
235
BOOL CALLBACK config_dialog_proc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
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]);
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);
251
if(m_variable_bitrate_display)
252
SendMessage(GetDlgItem(hwndDlg, VARBITRATE_CHK), BM_SETCHECK, BST_CHECKED, 0);
254
SendMessage(GetDlgItem(hwndDlg, IDC_MEMMAP), BM_SETCHECK, BST_CHECKED, 0);
256
itoa(m_local_buffer_size, tmp, 10);
257
SetDlgItemText(hwndDlg, LOCAL_BUFFER_TXT, tmp);
259
itoa(m_stream_buffer_size, tmp, 10);
260
SetDlgItemText(hwndDlg, STREAM_BUFFER_TXT, tmp);
266
/* Thread priority slider moved */
267
if(GetDlgItem(hwndDlg, THREAD_PRIORITY_SLIDER) == (HWND) lParam)
270
tmp = SendMessage(GetDlgItem(hwndDlg, THREAD_PRIORITY_SLIDER), TBM_GETPOS, 0, 0);
276
SetDlgItemText(hwndDlg, IDC_STATIC2, priority_text[m_priority]);
278
if(play_thread_handle)
279
SetThreadPriority(play_thread_handle, priority_table[m_priority]);
287
if(HIWORD(wParam) == BN_CLICKED)
289
if(GetDlgItem(hwndDlg, VARBITRATE_CHK) == (HWND) lParam)
291
/* Variable Bitrate checkbox hit */
292
m_variable_bitrate_display = SendMessage(GetDlgItem(hwndDlg, VARBITRATE_CHK), BM_GETCHECK, 0, 0);
294
if(GetDlgItem(hwndDlg, IDC_MEMMAP) == (HWND) lParam)
296
/* Variable Bitrate checkbox hit */
297
m_memmap_file = SendMessage(GetDlgItem(hwndDlg, IDC_MEMMAP), BM_GETCHECK, 0, 0);
301
switch (LOWORD(wParam))
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);
308
GetDlgItemText(hwndDlg, STREAM_BUFFER_TXT, tmp, 5);
309
m_stream_buffer_size = atol(tmp);
313
EndDialog(hwndDlg, wParam);
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);
320
SendMessage(GetDlgItem(hwndDlg, THREAD_PRIORITY_SLIDER), TBM_SETPOS, TRUE, 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;
330
/* User hit Cancel or the X, just close without saving buffer settings */
331
DestroyWindow(hwndDlg);
339
void Configure(int flags)
341
if(!IsWindow(hwndConfig))
342
hwndConfig = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_CONFIG), hwndPlayer, config_dialog_proc);
343
ShowWindow(hwndConfig, SW_NORMAL);
346
//-----------------------------------------------------------------------------
347
// proc of "About Dialog"
348
INT_PTR CALLBACK about_dialog_proc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
350
static RECT rcLOGO, rcMail1, rcMail2/*, rcMail3*/;
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";
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);
364
SetDlgItemText(hwndDlg, IDC_PLUGINVER, szPluginVer);
365
SetDlgItemText(hwndDlg, IDC_FAADVER, szFLACVer);
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)
375
(ptMouse.x >= rcMail1.left && ptMouse.x <= rcMail1.right &&
376
ptMouse.y >= rcMail1.top && ptMouse.y<= rcMail1.bottom)
378
(ptMouse.x >= rcMail2.left && ptMouse.x <= rcMail2.right &&
379
ptMouse.y >= rcMail2.top && ptMouse.y<= rcMail2.bottom)
381
(ptMouse.x >= rcMail3.left && ptMouse.x <= rcMail3.right &&
382
ptMouse.y >= rcMail3.top && ptMouse.y<= rcMail3.bottom)*/ )
383
SetCursor(LoadCursor(NULL, MAKEINTRESOURCE(32649)));
385
SetCursor(LoadCursor(NULL, IDC_ARROW));
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);
407
switch(LOWORD(wParam))
411
DestroyWindow(hwndDlg);
418
void About(int flags)
420
if(!IsWindow(hwndAbout))
421
hwndAbout = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_ABOUT), hwndPlayer, about_dialog_proc);
422
ShowWindow(hwndAbout, SW_SHOW);
425
//-----------------------------------------------------------------------------
427
BOOL Initialize(QCDModInfo *ModInfo, int flags)
429
hwndPlayer = (HWND)QCDCallbacks->Service(opGetParentWnd, 0, 0, 0);
434
play_thread_handle = INVALID_HANDLE_VALUE;
437
QCDCallbacks->Service(opGetPluginSettingsFile, INI_FILE, MAX_PATH, 0);
440
ModInfo->moduleString = "FAAD Plugin v1.0b";
441
ModInfo->moduleExtensions = "AAC";
443
/* Initialize winsock, necessary for streaming */
446
// insert menu item into plugin menu
447
// QCDCallbacks->Service(opSetPluginMenuItem, hInstance, IDD_CONFIG, (long)"FAAD Plug-in");
452
//----------------------------------------------------------------------------
454
void ShutDown(int flags)
456
Stop(lastfn, STOPFLAG_FORCESTOP);
461
/* Deallocate winsock */
464
// delete the inserted plugin menu
465
// QCDCallbacks->Service(opSetPluginMenuItem, hInstance, 0, 0);
468
//-----------------------------------------------------------------------------
470
BOOL GetMediaSupported(LPCSTR medianame, MediaInfo *mediaInfo)
474
char *ch = strrchr(medianame, '.');
476
if (!medianame || !*medianame)
480
return (lstrlen(medianame) > 2); // no extension defaults to me (if not drive letter)
483
if(StringComp(ch, ".aac", 4) == 0)
485
in = open_filestream((char *)medianame);
487
if(in != NULL && mediaInfo)
491
/* No seeking in http streams */
492
mediaInfo->mediaType = DIGITAL_STREAM_MEDIA;
493
mediaInfo->op_canSeek = FALSE;
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;
502
mediaInfo->op_canSeek = FALSE; /* ADIF or Headerless - not seekable */
505
close_filestream(in);
510
close_filestream(in);
518
unsigned long samplerate, channels;
520
int play_memmap(char *fn)
524
infile = open_filestream(fn);
529
fileread = filelength_filestream(infile);
531
memmap_buffer = (char*)LocalAlloc(LPTR, fileread);
532
read_buffer_filestream(infile, memmap_buffer, fileread);
535
memmap_index = id3v2_tag(memmap_buffer);
537
hDecoder = faacDecOpen();
539
/* Copy the configuration dialog setting and use it as the default */
540
/* initialize the decoder, and get samplerate and channel info */
542
if( (buffercount = faacDecInit(hDecoder, memmap_buffer + memmap_index,
543
fileread - memmap_index - 1, &samplerate, &channels)) < 0 )
545
show_error("Error opening input file");
549
memmap_index += buffercount;
556
int play_file(char *fn)
561
ZeroMemory(buffer, 768*2);
563
infile = open_filestream(fn);
568
fileread = filelength_filestream(infile);
570
buffercount = bytecount = 0;
571
read_buffer_filestream(infile, buffer, 768*2);
573
tagsize = id3v2_tag(buffer);
575
/* If we find a tag, run right over it */
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);
585
for(i=0; i < tagsize; i++)
586
read_byte_filestream(infile);
589
seek_filestream(infile, tagsize, FILE_BEGIN);
593
read_buffer_filestream(infile, buffer, 768*2);
596
hDecoder = faacDecOpen();
598
/* Copy the configuration dialog setting and use it as the default */
599
/* initialize the decoder, and get samplerate and channel info */
601
if((buffercount = faacDecInit(hDecoder, buffer, 768*2, &samplerate, &channels)) < 0)
603
show_error("Error opening input file");
609
bytecount += buffercount;
611
for (k = 0; k < (768*2 - buffercount); k++)
612
buffer[k] = buffer[k + buffercount];
614
read_buffer_filestream(infile, buffer + (768*2) - buffercount, buffercount);
624
//-----------------------------------------------------------------------------
626
BOOL Play(LPCSTR medianame, int playfrom, int playto, int flags)
628
if(stricmp(lastfn, medianame) != 0)
630
sQCDCallbacks.toPlayer.OutputStop(STOPFLAG_PLAYDONE);
631
Stop(lastfn, STOPFLAG_PLAYDONE);
636
// Update the player controls to reflect the new unpaused state
637
sQCDCallbacks.toPlayer.OutputPause(0);
639
Pause(medianame, PAUSE_DISABLED);
642
seek_needed = playfrom;
644
else if(play_thread_handle != INVALID_HANDLE_VALUE)
646
seek_needed = playfrom;
653
// alloc the input buffer
654
buffer = (unsigned char*)LocalAlloc(LPTR, 768*2);
656
current_file_mode = m_memmap_file;
658
if(current_file_mode)
660
if(play_memmap((char *)medianame))
665
if(play_file((char *)medianame))
673
seek_table_length = 0;
676
get_AAC_format((char *)medianame, &file_info, &seek_table, &seek_table_length, 0);
678
seek_needed = playfrom > 0 ? playfrom : -1;
680
strcpy(lastfn,medianame);
683
To RageAmp: This is really needed, because aacinfo isn't very accurate on ADIF files yet.
684
Can be fixed though :-)
686
file_info.sampling_rate = samplerate;
687
file_info.channels = frameInfo.channels;
689
play_thread_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) PlayThread, (void *) &killPlayThread, 0, &thread_id);
690
if(!play_thread_handle)
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]);
702
//-----------------------------------------------------------------------------
704
BOOL Pause(LPCSTR medianame, int flags)
706
if(QCDCallbacks->toPlayer.OutputPause(flags))
708
// send back pause/unpause notification
709
QCDCallbacks->toPlayer.PlayPaused(medianame, flags);
716
//-----------------------------------------------------------------------------
718
BOOL Stop(LPCSTR medianame, int flags)
720
if(medianame && *medianame && stricmp(lastfn, medianame) == 0)
722
sQCDCallbacks.toPlayer.OutputStop(flags);
725
if(play_thread_handle != INVALID_HANDLE_VALUE)
727
if(WaitForSingleObject(play_thread_handle, INFINITE) == WAIT_TIMEOUT)
729
// MessageBox(hwndPlayer, "FAAD thread kill timeout", "debug", 0);
730
TerminateThread(play_thread_handle,0);
732
CloseHandle(play_thread_handle);
733
play_thread_handle = INVALID_HANDLE_VALUE;
737
QCDCallbacks->toPlayer.PlayStopped(lastfn);
745
int aac_seek(int pos_ms, int *sktable)
749
offset_sec = pos_ms / 1000.0;
750
if(!current_file_mode)
752
seek_filestream(infile, sktable[(int)(offset_sec+0.5)], FILE_BEGIN);
754
bytecount = sktable[(int)(offset_sec+0.5)];
756
read_buffer_filestream(infile, buffer, 768*2);
760
memmap_index = sktable[(int)(offset_sec+0.5)];
766
//-----------------------------------------------------------------------------
768
void SetVolume(int levelleft, int levelright, int flags)
770
QCDCallbacks->toPlayer.OutputSetVol(levelleft, levelright, flags);
773
//-----------------------------------------------------------------------------
775
BOOL GetCurrentPosition(LPCSTR medianame, long *track, long *offset)
777
return QCDCallbacks->toPlayer.OutputGetCurrentPosition((UINT*)offset, 0);
780
//-----------------------------------------------------------------------------
782
BOOL GetTrackExtents(LPCSTR medianame, TrackExtents *ext, int flags)
786
if(get_AAC_format((char*)medianame, &tmp, NULL, NULL, 1))
791
ext->end = tmp.length;
792
ext->bytesize = tmp.bitrate * tmp.length;
793
ext->unitpersec = 1000;
798
//--------------------------for play thread-------------------------------------
802
DWORD WINAPI PlayThread(void *b)
804
BOOL done = FALSE, updatePos = FALSE;
805
int decode_pos_ms = 0; // current decoding position, in milliseconds
807
int decoded_frames=0;
808
int br_calc_frames=0;
809
int br_bytes_consumed=0;
810
unsigned long bytesconsumed;
818
wf.wFormatTag = WAVE_FORMAT_PCM;
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))
827
show_error("Error: Failed openning output plugin!");
828
done = TRUE; // cannot open sound device
832
while (! *((int *)b) )
834
/********************** SEEK ************************/
835
if (!done && seek_needed >= 0)
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;
846
br_bytes_consumed = 0;
852
/********************* QUIT *************************/
855
if (QCDCallbacks->toPlayer.OutputDrain(0) && !(seek_needed >= 0))
857
play_thread_handle = INVALID_HANDLE_VALUE;
858
QCDCallbacks->toPlayer.OutputStop(STOPFLAG_PLAYDONE);
859
QCDCallbacks->toPlayer.PlayDone(lastfn);
861
else if (seek_needed >= 0)
869
/******************* DECODE TO BUFFER ****************/
872
if (current_file_mode)
873
bytesconsumed = PlayThread_memmap();
875
bytesconsumed = PlayThread_file();
884
br_bytes_consumed += bytesconsumed;
886
/* Update the variable bitrate about every second */
887
if(m_variable_bitrate_display && br_calc_frames == 43)
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;
895
vai.level = file_info.version;
896
QCDCallbacks->Service(opSetAudioInfo, &vai, sizeof(AudioInfo), 0);
901
if (!killPlayThread && (frameInfo.samples > 0))
903
//update the time display
906
QCDCallbacks->toPlayer.PositionUpdate(decode_pos_ms);
913
l = frameInfo.samples * sizeof(short);
915
decode_pos_ms += (1024*1000)/file_info.sampling_rate;
918
wd.data = sample_buffer;
920
wd.markerstart = decode_pos_ms;
922
wd.nch = frameInfo.channels;
923
wd.numsamples =l/file_info.channels/(16/8);
924
wd.srate = file_info.sampling_rate;
926
if (!QCDCallbacks->toPlayer.OutputWrite(&wd))
936
play_thread_handle = INVALID_HANDLE_VALUE;
938
faacDecClose(hDecoder);
939
hDecoder = INVALID_HANDLE_VALUE;
940
close_filestream(infile);
947
seek_table_length = 0;
957
LocalFree(memmap_buffer);
958
memmap_buffer = NULL;
965
int PlayThread_memmap()
967
sample_buffer = (char*)faacDecDecode(hDecoder, &frameInfo,
968
memmap_buffer + memmap_index, fileread - memmap_index - 1);
971
// show_error(faacDecGetErrorMessage(frameInfo.error));
975
memmap_index += frameInfo.bytesconsumed;
976
if (memmap_index >= fileread)
979
return frameInfo.bytesconsumed;
982
int PlayThread_file()
988
for (k = 0; k < (768*2 - buffercount); k++)
989
buffer[k] = buffer[k + buffercount];
991
read_buffer_filestream(infile, buffer + (768*2) - buffercount, buffercount);
995
sample_buffer = (char*)faacDecDecode(hDecoder, &frameInfo, buffer, 768*2);
998
// show_error(faacDecGetErrorMessage(frameInfo.error));
1002
buffercount += frameInfo.bytesconsumed;
1004
bytecount += frameInfo.bytesconsumed;
1005
if (bytecount >= fileread)
1008
return frameInfo.bytesconsumed;
1012
int id3v2_tag(unsigned char *buffer)
1014
if (StringComp(buffer, "ID3", 3) == 0)
1016
unsigned long tagsize;
1018
/* high bit is not used */
1019
tagsize = (buffer[6] << 21) | (buffer[7] << 14) |
1020
(buffer[8] << 7) | (buffer[9] << 0);
b'\\ No newline at end of file'