/*
** FAAD - Freeware Advanced Audio Decoder
** Copyright (C) 2002 M. Bakker
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
**
** $Id: QCDFAAD.c,v 1.2 2003/04/28 19:04:35 menno Exp $
** based on menno's in_faad.dll plugin for Winamp
**
** The tag function has been removed because QCD supports ID3v1 & ID3v2 very well
** About how to tagging: Please read the "ReadMe.txt" first
**/
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <mmreg.h>
#include <commctrl.h>
#include <shellapi.h>
#include <stdlib.h>
#include <stdio.h>
#include "QCDInputDLL.h"
#include "resource.h"
#include <faad.h>
#include <aacinfo.h>
#include <filestream.h>
//#include <id3v2tag.h>
static char app_name[] = "QCDFAAD";
faadAACInfo file_info;
faacDecHandle hDecoder;
faacDecFrameInfo frameInfo;
HINSTANCE hInstance;
HWND hwndPlayer, hwndConfig, hwndAbout;
QCDModInitIn sQCDCallbacks, *QCDCallbacks;
BOOL oldAPIs = 0;
static char lastfn[MAX_PATH]; // currently playing file (used for getting info on the current file)
int file_length; // file length, in bytes
int paused; // are we paused?
int seek_needed; // if != -1, it is the point that the decode thread should seek to, in ms.
char *sample_buffer; // sample buffer
unsigned char *buffer; // input buffer
unsigned char *memmap_buffer; // input buffer for whole file
long memmap_index;
long buffercount, fileread, bytecount;
// seek table for ADTS header files
unsigned long *seek_table = NULL;
int seek_table_length=0;
int killPlayThread = 0; // the kill switch for the decode thread
HANDLE play_thread_handle = INVALID_HANDLE_VALUE; // the handle to the decode thread
FILE_STREAM *infile;
/* Function definitions */
int id3v2_tag(unsigned char *buffer);
DWORD WINAPI PlayThread(void *b); // the decode thread procedure
// general funcz
static void show_error(const char *message,...)
{
char foo[512];
va_list args;
va_start(args, message);
vsprintf(foo, message, args);
va_end(args);
MessageBox(hwndPlayer, foo, "FAAD Plug-in Error", MB_ICONSTOP);
}
// 1= use vbr display, 0 = use average bitrate. This value only controls what shows up in the
// configuration form. Also- Streaming uses an on-the-fly bitrate display regardless of this value.
long m_variable_bitrate_display=0;
long m_priority = 5;
long m_memmap_file = 0;
static char INI_FILE[MAX_PATH];
char *priority_text[] = { "",
"Decode Thread Priority: Lowest",
"Decode Thread Priority: Lower",
"Decode Thread Priority: Normal",
"Decode Thread Priority: Higher",
"Decode Thread Priority: Highest (default)"
};
long priority_table[] = {0, THREAD_PRIORITY_LOWEST, THREAD_PRIORITY_BELOW_NORMAL, THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_ABOVE_NORMAL, THREAD_PRIORITY_HIGHEST};
long current_file_mode = 0;
int PlayThread_memmap();
int PlayThread_file();
static void _r_s(char *name,char *data, int mlen)
{
char buf[10];
strcpy(buf,data);
GetPrivateProfileString(app_name,name,buf,data,mlen,INI_FILE);
}
#define RS(x) (_r_s(#x,x,sizeof(x)))
#define WS(x) (WritePrivateProfileString(app_name,#x,x,INI_FILE))
void config_read()
{
char variable_bitrate_display[10];
char priority[10];
char memmap_file[10];
char local_buffer_size[10];
char stream_buffer_size[10];
strcpy(variable_bitrate_display, "1");
strcpy(priority, "5");
strcpy(memmap_file, "0");
strcpy(local_buffer_size, "128");
strcpy(stream_buffer_size, "64");
RS(variable_bitrate_display);
RS(priority);
RS(memmap_file);
RS(local_buffer_size);
RS(stream_buffer_size);
m_priority = atoi(priority);
m_variable_bitrate_display = atoi(variable_bitrate_display);
m_memmap_file = atoi(memmap_file);
m_local_buffer_size = atoi(local_buffer_size);
m_stream_buffer_size = atoi(stream_buffer_size);
}
void config_write()
{
char variable_bitrate_display[10];
char priority[10];
char memmap_file[10];
char local_buffer_size[10];
char stream_buffer_size[10];
itoa(m_priority, priority, 10);
itoa(m_variable_bitrate_display, variable_bitrate_display, 10);
itoa(m_memmap_file, memmap_file, 10);
itoa(m_local_buffer_size, local_buffer_size, 10);
itoa(m_stream_buffer_size, stream_buffer_size, 10);
WS(variable_bitrate_display);
WS(priority);
WS(memmap_file);
WS(local_buffer_size);
WS(stream_buffer_size);
}
//-----------------------------------------------------------------------------
BOOL WINAPI DllMain(HINSTANCE hInst, DWORD fdwReason, LPVOID pRes)
{
if (fdwReason == DLL_PROCESS_ATTACH)
hInstance = hInst;
return TRUE;
}
//-----------------------------------------------------------------------------
//old entrypoint api
PLUGIN_API BOOL QInputModule(QCDModInitIn *ModInit, QCDModInfo *ModInfo)
{
ModInit->version = PLUGIN_API_VERSION;
ModInit->toModule.ShutDown = ShutDown;
ModInit->toModule.GetTrackExtents = GetTrackExtents;
ModInit->toModule.GetMediaSupported = GetMediaSupported;
ModInit->toModule.GetCurrentPosition= GetCurrentPosition;
ModInit->toModule.Play = Play;
ModInit->toModule.Pause = Pause;
ModInit->toModule.Stop = Stop;
ModInit->toModule.SetVolume = SetVolume;
ModInit->toModule.About = About;
ModInit->toModule.Configure = Configure;
QCDCallbacks = ModInit;
ModInfo->moduleString = "FAAD Plugin v1.0b";
/* read config */
QCDCallbacks->Service(opGetPluginSettingsFile, INI_FILE, MAX_PATH, 0);
config_read();
ModInfo->moduleExtensions = "AAC";
hwndPlayer = (HWND)ModInit->Service(opGetParentWnd, 0, 0, 0);
lastfn[0] = 0;
play_thread_handle = INVALID_HANDLE_VALUE;
oldAPIs = 1;
return TRUE;
}
//-----------------------------------------------------------------------------
PLUGIN_API QCDModInitIn* INPUTDLL_ENTRY_POINT()
{
sQCDCallbacks.version = PLUGIN_API_VERSION;
sQCDCallbacks.toModule.Initialize = Initialize;
sQCDCallbacks.toModule.ShutDown = ShutDown;
sQCDCallbacks.toModule.GetTrackExtents = GetTrackExtents;
sQCDCallbacks.toModule.GetMediaSupported = GetMediaSupported;
sQCDCallbacks.toModule.GetCurrentPosition = GetCurrentPosition;
sQCDCallbacks.toModule.Play = Play;
sQCDCallbacks.toModule.Pause = Pause;
sQCDCallbacks.toModule.Stop = Stop;
sQCDCallbacks.toModule.SetVolume = SetVolume;
sQCDCallbacks.toModule.About = About;
sQCDCallbacks.toModule.Configure = Configure;
QCDCallbacks = &sQCDCallbacks;
return &sQCDCallbacks;
}
//----------------------------------------------------------------------------
BOOL CALLBACK config_dialog_proc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
char tmp[10];
switch (message)
{
case WM_INITDIALOG:
/* Set priority slider range and previous position */
SendMessage(GetDlgItem(hwndDlg, THREAD_PRIORITY_SLIDER), TBM_SETRANGE, TRUE, MAKELONG(1, 5));
SendMessage(GetDlgItem(hwndDlg, THREAD_PRIORITY_SLIDER), TBM_SETPOS, TRUE, m_priority);
SetDlgItemText(hwndDlg, IDC_STATIC2, priority_text[m_priority]);
/* Put a limit to the amount of characters allowed in the buffer boxes */
SendMessage(GetDlgItem(hwndDlg, LOCAL_BUFFER_TXT), EM_LIMITTEXT, 4, 0);
SendMessage(GetDlgItem(hwndDlg, STREAM_BUFFER_TXT), EM_LIMITTEXT, 4, 0);
if(m_variable_bitrate_display)
SendMessage(GetDlgItem(hwndDlg, VARBITRATE_CHK), BM_SETCHECK, BST_CHECKED, 0);
if(m_memmap_file)
SendMessage(GetDlgItem(hwndDlg, IDC_MEMMAP), BM_SETCHECK, BST_CHECKED, 0);
itoa(m_local_buffer_size, tmp, 10);
SetDlgItemText(hwndDlg, LOCAL_BUFFER_TXT, tmp);
itoa(m_stream_buffer_size, tmp, 10);
SetDlgItemText(hwndDlg, STREAM_BUFFER_TXT, tmp);
return TRUE;
case WM_HSCROLL:
/* Thread priority slider moved */
if(GetDlgItem(hwndDlg, THREAD_PRIORITY_SLIDER) == (HWND) lParam)
{
int tmp;
tmp = SendMessage(GetDlgItem(hwndDlg, THREAD_PRIORITY_SLIDER), TBM_GETPOS, 0, 0);
if(tmp > 0)
{
m_priority = tmp;
SetDlgItemText(hwndDlg, IDC_STATIC2, priority_text[m_priority]);
if(play_thread_handle)
SetThreadPriority(play_thread_handle, priority_table[m_priority]);
}
}
return TRUE;
case WM_COMMAND:
if(HIWORD(wParam) == BN_CLICKED)
{
if(GetDlgItem(hwndDlg, VARBITRATE_CHK) == (HWND) lParam)
{
/* Variable Bitrate checkbox hit */
m_variable_bitrate_display = SendMessage(GetDlgItem(hwndDlg, VARBITRATE_CHK), BM_GETCHECK, 0, 0);
}
if(GetDlgItem(hwndDlg, IDC_MEMMAP) == (HWND) lParam)
{
/* Variable Bitrate checkbox hit */
m_memmap_file = SendMessage(GetDlgItem(hwndDlg, IDC_MEMMAP), BM_GETCHECK, 0, 0);
}
}
switch (LOWORD(wParam))
{
case OK_BTN:
/* User hit OK, save buffer settings (all others are set on command) */
GetDlgItemText(hwndDlg, LOCAL_BUFFER_TXT, tmp, 5);
m_local_buffer_size = atol(tmp);
GetDlgItemText(hwndDlg, STREAM_BUFFER_TXT, tmp, 5);
m_stream_buffer_size = atol(tmp);
config_write();
EndDialog(hwndDlg, wParam);
return TRUE;
case RESET_BTN:
SendMessage(GetDlgItem(hwndDlg, VARBITRATE_CHK), BM_SETCHECK, BST_CHECKED, 0);
m_variable_bitrate_display = 1;
SendMessage(GetDlgItem(hwndDlg, IDC_MEMMAP), BM_SETCHECK, BST_UNCHECKED, 0);
m_memmap_file = 0;
SendMessage(GetDlgItem(hwndDlg, THREAD_PRIORITY_SLIDER), TBM_SETPOS, TRUE, 5);
m_priority = 5;
SetDlgItemText(hwndDlg, IDC_STATIC2, priority_text[5]);
SetDlgItemText(hwndDlg, LOCAL_BUFFER_TXT, "128");
m_local_buffer_size = 128;
SetDlgItemText(hwndDlg, STREAM_BUFFER_TXT, "64");
m_stream_buffer_size = 64;
return TRUE;
case IDCANCEL:
case CANCEL_BTN:
/* User hit Cancel or the X, just close without saving buffer settings */
DestroyWindow(hwndDlg);
return TRUE;
}
}
return FALSE;
}
void Configure(int flags)
{
if(!IsWindow(hwndConfig))
hwndConfig = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_CONFIG), hwndPlayer, config_dialog_proc);
ShowWindow(hwndConfig, SW_NORMAL);
}
//-----------------------------------------------------------------------------
// proc of "About Dialog"
INT_PTR CALLBACK about_dialog_proc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static RECT rcLOGO, rcMail1, rcMail2/*, rcMail3*/;
POINT ptMouse;
static char szPluginVer[] = "QCD FAAD Input Plug-in v1.0b\nCompiled on " __TIME__ ", " __DATE__;
static char szFLACVer[] = "Using: FAAD2 v "FAAD2_VERSION" by";
switch (uMsg)
{
case WM_INITDIALOG:
case WM_MOVE:
GetWindowRect(GetDlgItem(hwndDlg, IDC_LOGO), &rcLOGO);
GetWindowRect(GetDlgItem(hwndDlg, IDC_MAIL1), &rcMail1);
GetWindowRect(GetDlgItem(hwndDlg, IDC_MAIL2), &rcMail2);
// GetWindowRect(GetDlgItem(hwndDlg, IDC_MAIL2), &rcMail3);
SetDlgItemText(hwndDlg, IDC_PLUGINVER, szPluginVer);
SetDlgItemText(hwndDlg, IDC_FAADVER, szFLACVer);
return TRUE;
case WM_MOUSEMOVE:
ptMouse.x = LOWORD(lParam);
ptMouse.y = HIWORD(lParam);
ClientToScreen(hwndDlg, &ptMouse);
if( (ptMouse.x >= rcLOGO.left && ptMouse.x <= rcLOGO.right &&
ptMouse.y >= rcLOGO.top && ptMouse.y<= rcLOGO.bottom)
||
(ptMouse.x >= rcMail1.left && ptMouse.x <= rcMail1.right &&
ptMouse.y >= rcMail1.top && ptMouse.y<= rcMail1.bottom)
||
(ptMouse.x >= rcMail2.left && ptMouse.x <= rcMail2.right &&
ptMouse.y >= rcMail2.top && ptMouse.y<= rcMail2.bottom)
/* ||
(ptMouse.x >= rcMail3.left && ptMouse.x <= rcMail3.right &&
ptMouse.y >= rcMail3.top && ptMouse.y<= rcMail3.bottom)*/ )
SetCursor(LoadCursor(NULL, MAKEINTRESOURCE(32649)));
else
SetCursor(LoadCursor(NULL, IDC_ARROW));
return TRUE;
case WM_LBUTTONDOWN:
ptMouse.x = LOWORD(lParam);
ptMouse.y = HIWORD(lParam);
ClientToScreen(hwndDlg, &ptMouse);
if(ptMouse.x >= rcLOGO.left && ptMouse.x <= rcLOGO.right &&
ptMouse.y >= rcLOGO.top && ptMouse.y<= rcLOGO.bottom)
ShellExecute(0, NULL, "http://www.audiocoding.com", NULL,NULL, SW_NORMAL);
else if(ptMouse.x >= rcMail1.left && ptMouse.x <= rcMail1.right &&
ptMouse.y >= rcMail1.top && ptMouse.y<= rcMail1.bottom)
ShellExecute(0, NULL, "mailto:shaohao@elong.com", NULL,NULL, SW_NORMAL);
else if(ptMouse.x >= rcMail2.left && ptMouse.x <= rcMail2.right &&
ptMouse.y >= rcMail2.top && ptMouse.y<= rcMail2.bottom)
ShellExecute(0, NULL, "mailto:menno@audiocoding.com", NULL,NULL, SW_NORMAL);
/* else if(ptMouse.x >= rcMail3.left && ptMouse.x <= rcMail3.right &&
ptMouse.y >= rcMail3.top && ptMouse.y<= rcMail3.bottom)
ShellExecute(0, NULL, "I don't know", NULL,NULL, SW_NORMAL);
*/
return TRUE;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDOK:
default:
DestroyWindow(hwndDlg);
return TRUE;
}
}
return FALSE;
}
void About(int flags)
{
if(!IsWindow(hwndAbout))
hwndAbout = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_ABOUT), hwndPlayer, about_dialog_proc);
ShowWindow(hwndAbout, SW_SHOW);
}
//-----------------------------------------------------------------------------
BOOL Initialize(QCDModInfo *ModInfo, int flags)
{
hwndPlayer = (HWND)QCDCallbacks->Service(opGetParentWnd, 0, 0, 0);
lastfn[0] = 0;
seek_needed = -1;
paused = 0;
play_thread_handle = INVALID_HANDLE_VALUE;
/* read config */
QCDCallbacks->Service(opGetPluginSettingsFile, INI_FILE, MAX_PATH, 0);
config_read();
ModInfo->moduleString = "FAAD Plugin v1.0b";
ModInfo->moduleExtensions = "AAC";
/* Initialize winsock, necessary for streaming */
WinsockInit();
// insert menu item into plugin menu
// QCDCallbacks->Service(opSetPluginMenuItem, hInstance, IDD_CONFIG, (long)"FAAD Plug-in");
return TRUE;
}
//----------------------------------------------------------------------------
void ShutDown(int flags)
{
Stop(lastfn, STOPFLAG_FORCESTOP);
if(buffer)
LocalFree(buffer);
/* Deallocate winsock */
WinsockDeInit();
// delete the inserted plugin menu
// QCDCallbacks->Service(opSetPluginMenuItem, hInstance, 0, 0);
}
//-----------------------------------------------------------------------------
BOOL GetMediaSupported(LPCSTR medianame, MediaInfo *mediaInfo)
{
FILE_STREAM *in;
faadAACInfo tmp;
char *ch = strrchr(medianame, '.');
if (!medianame || !*medianame)
return FALSE;
if(!ch)
return (lstrlen(medianame) > 2); // no extension defaults to me (if not drive letter)
/* Finally fixed */
if(StringComp(ch, ".aac", 4) == 0)
{
in = open_filestream((char *)medianame);
if(in != NULL && mediaInfo)
{
if(in->http)
{
/* No seeking in http streams */
mediaInfo->mediaType = DIGITAL_STREAM_MEDIA;
mediaInfo->op_canSeek = FALSE;
}
else
{
mediaInfo->mediaType = DIGITAL_FILE_MEDIA;
get_AAC_format((char *)medianame, &tmp, NULL, NULL, 1);
if(tmp.headertype == 2) /* ADTS header - seekable */
mediaInfo->op_canSeek = TRUE;
else
mediaInfo->op_canSeek = FALSE; /* ADIF or Headerless - not seekable */
}
close_filestream(in);
return TRUE;
}
else
{
close_filestream(in);
return FALSE;
}
}
else
return FALSE;
}
unsigned long samplerate, channels;
int play_memmap(char *fn)
{
int tagsize = 0;
infile = open_filestream(fn);
if (infile == NULL)
return 1;
fileread = filelength_filestream(infile);
memmap_buffer = (char*)LocalAlloc(LPTR, fileread);
read_buffer_filestream(infile, memmap_buffer, fileread);
/* skip id3v2 tag */
memmap_index = id3v2_tag(memmap_buffer);
hDecoder = faacDecOpen();
/* Copy the configuration dialog setting and use it as the default */
/* initialize the decoder, and get samplerate and channel info */
if( (buffercount = faacDecInit(hDecoder, memmap_buffer + memmap_index,
fileread - memmap_index - 1, &samplerate, &channels)) < 0 )
{
show_error("Error opening input file");
return 1;
}
memmap_index += buffercount;
PlayThread_memmap();
return 0;
}
int play_file(char *fn)
{
int k;
int tagsize;
ZeroMemory(buffer, 768*2);
infile = open_filestream(fn);
if (infile == NULL)
return 1;
fileread = filelength_filestream(infile);
buffercount = bytecount = 0;
read_buffer_filestream(infile, buffer, 768*2);
tagsize = id3v2_tag(buffer);
/* If we find a tag, run right over it */
if(tagsize)
{
if(infile->http)
{
int i;
/* Crude way of doing this, but I believe its fast enough to not make a big difference */
close_filestream(infile);
infile = open_filestream(fn);
for(i=0; i < tagsize; i++)
read_byte_filestream(infile);
}
else
seek_filestream(infile, tagsize, FILE_BEGIN);
bytecount = tagsize;
buffercount = 0;
read_buffer_filestream(infile, buffer, 768*2);
}
hDecoder = faacDecOpen();
/* Copy the configuration dialog setting and use it as the default */
/* initialize the decoder, and get samplerate and channel info */
if((buffercount = faacDecInit(hDecoder, buffer, 768*2, &samplerate, &channels)) < 0)
{
show_error("Error opening input file");
return 1;
}
if(buffercount > 0)
{
bytecount += buffercount;
for (k = 0; k < (768*2 - buffercount); k++)
buffer[k] = buffer[k + buffercount];
read_buffer_filestream(infile, buffer + (768*2) - buffercount, buffercount);
buffercount = 0;
}
PlayThread_file();
return 0;
}
//-----------------------------------------------------------------------------
BOOL Play(LPCSTR medianame, int playfrom, int playto, int flags)
{
if(stricmp(lastfn, medianame) != 0)
{
sQCDCallbacks.toPlayer.OutputStop(STOPFLAG_PLAYDONE);
Stop(lastfn, STOPFLAG_PLAYDONE);
}
if(paused)
{
// Update the player controls to reflect the new unpaused state
sQCDCallbacks.toPlayer.OutputPause(0);
Pause(medianame, PAUSE_DISABLED);
if (playfrom >= 0)
seek_needed = playfrom;
}
else if(play_thread_handle != INVALID_HANDLE_VALUE)
{
seek_needed = playfrom;
return TRUE;
}
else
{
int thread_id;
// alloc the input buffer
buffer = (unsigned char*)LocalAlloc(LPTR, 768*2);
current_file_mode = m_memmap_file;
if(current_file_mode)
{
if(play_memmap((char *)medianame))
return FALSE;
}
else
{
if(play_file((char *)medianame))
return FALSE;
}
if(seek_table)
{
free(seek_table);
seek_table = NULL;
seek_table_length = 0;
}
get_AAC_format((char *)medianame, &file_info, &seek_table, &seek_table_length, 0);
seek_needed = playfrom > 0 ? playfrom : -1;
killPlayThread = 0;
strcpy(lastfn,medianame);
/*
To RageAmp: This is really needed, because aacinfo isn't very accurate on ADIF files yet.
Can be fixed though :-)
*/
file_info.sampling_rate = samplerate;
file_info.channels = frameInfo.channels;
play_thread_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) PlayThread, (void *) &killPlayThread, 0, &thread_id);
if(!play_thread_handle)
return FALSE;
// Note: This line seriously slows down start up time
if(m_priority != 3) // if the priority in config window is set to normal, there is nothing to reset!
SetThreadPriority(play_thread_handle, priority_table[m_priority]);
}
return TRUE;
}
//-----------------------------------------------------------------------------
BOOL Pause(LPCSTR medianame, int flags)
{
if(QCDCallbacks->toPlayer.OutputPause(flags))
{
// send back pause/unpause notification
QCDCallbacks->toPlayer.PlayPaused(medianame, flags);
paused = flags;
return TRUE;
}
return FALSE;
}
//-----------------------------------------------------------------------------
BOOL Stop(LPCSTR medianame, int flags)
{
if(medianame && *medianame && stricmp(lastfn, medianame) == 0)
{
sQCDCallbacks.toPlayer.OutputStop(flags);
killPlayThread = 1;
if(play_thread_handle != INVALID_HANDLE_VALUE)
{
if(WaitForSingleObject(play_thread_handle, INFINITE) == WAIT_TIMEOUT)
{
// MessageBox(hwndPlayer, "FAAD thread kill timeout", "debug", 0);
TerminateThread(play_thread_handle,0);
}
CloseHandle(play_thread_handle);
play_thread_handle = INVALID_HANDLE_VALUE;
}
if (oldAPIs)
QCDCallbacks->toPlayer.PlayStopped(lastfn);
lastfn[0] = 0;
}
return TRUE;
}
int aac_seek(int pos_ms, int *sktable)
{
double offset_sec;
offset_sec = pos_ms / 1000.0;
if(!current_file_mode)
{
seek_filestream(infile, sktable[(int)(offset_sec+0.5)], FILE_BEGIN);
bytecount = sktable[(int)(offset_sec+0.5)];
buffercount = 0;
read_buffer_filestream(infile, buffer, 768*2);
}
else
{
memmap_index = sktable[(int)(offset_sec+0.5)];
}
return 0;
}
//-----------------------------------------------------------------------------
void SetVolume(int levelleft, int levelright, int flags)
{
QCDCallbacks->toPlayer.OutputSetVol(levelleft, levelright, flags);
}
//-----------------------------------------------------------------------------
BOOL GetCurrentPosition(LPCSTR medianame, long *track, long *offset)
{
return QCDCallbacks->toPlayer.OutputGetCurrentPosition((UINT*)offset, 0);
}
//-----------------------------------------------------------------------------
BOOL GetTrackExtents(LPCSTR medianame, TrackExtents *ext, int flags)
{
faadAACInfo tmp;
if(get_AAC_format((char*)medianame, &tmp, NULL, NULL, 1))
return FALSE;
ext->track = 1;
ext->start = 0;
ext->end = tmp.length;
ext->bytesize = tmp.bitrate * tmp.length;
ext->unitpersec = 1000;
return TRUE;
}
//--------------------------for play thread-------------------------------------
int last_frame;
DWORD WINAPI PlayThread(void *b)
{
BOOL done = FALSE, updatePos = FALSE;
int decode_pos_ms = 0; // current decoding position, in milliseconds
int l;
int decoded_frames=0;
int br_calc_frames=0;
int br_bytes_consumed=0;
unsigned long bytesconsumed;
last_frame = 0;
if(!done)
{
// open outputdevice
WAVEFORMATEX wf;
wf.wFormatTag = WAVE_FORMAT_PCM;
wf.cbSize = 0;
wf.nChannels = file_info.channels;
wf.wBitsPerSample = 16;
wf.nSamplesPerSec = file_info.sampling_rate;
wf.nBlockAlign = wf.nChannels * wf.wBitsPerSample / 8;
wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign;
if (!QCDCallbacks->toPlayer.OutputOpen(lastfn, &wf))
{
show_error("Error: Failed openning output plugin!");
done = TRUE; // cannot open sound device
}
}
while (! *((int *)b) )
{
/********************** SEEK ************************/
if (!done && seek_needed >= 0)
{
int seconds;
// Round off to a second
seconds = seek_needed - (seek_needed%1000);
QCDCallbacks->toPlayer.OutputFlush(decode_pos_ms);
aac_seek(seconds, seek_table);
decode_pos_ms = seconds;
decoded_frames = 0;
br_calc_frames = 0;
br_bytes_consumed = 0;
seek_needed = -1;
updatePos = 1;
}
/********************* QUIT *************************/
if (done)
{
if (QCDCallbacks->toPlayer.OutputDrain(0) && !(seek_needed >= 0))
{
play_thread_handle = INVALID_HANDLE_VALUE;
QCDCallbacks->toPlayer.OutputStop(STOPFLAG_PLAYDONE);
QCDCallbacks->toPlayer.PlayDone(lastfn);
}
else if (seek_needed >= 0)
{
done = FALSE;
continue;
}
break;
}
/******************* DECODE TO BUFFER ****************/
else
{
if (current_file_mode)
bytesconsumed = PlayThread_memmap();
else
bytesconsumed = PlayThread_file();
if(last_frame)
done = TRUE;
else
{
decoded_frames++;
br_calc_frames++;
br_bytes_consumed += bytesconsumed;
/* Update the variable bitrate about every second */
if(m_variable_bitrate_display && br_calc_frames == 43)
{
AudioInfo vai;
vai.struct_size = sizeof(AudioInfo);
vai.frequency = file_info.sampling_rate;
vai.bitrate = (int)((br_bytes_consumed * 8) / (decoded_frames / 43.07));
vai.mode = (channels == 2) ? 0 : 3;
vai.layer = 0;
vai.level = file_info.version;
QCDCallbacks->Service(opSetAudioInfo, &vai, sizeof(AudioInfo), 0);
br_calc_frames = 0;
}
if (!killPlayThread && (frameInfo.samples > 0))
{
//update the time display
if (updatePos)
{
QCDCallbacks->toPlayer.PositionUpdate(decode_pos_ms);
updatePos = 0;
}
{
WriteDataStruct wd;
l = frameInfo.samples * sizeof(short);
decode_pos_ms += (1024*1000)/file_info.sampling_rate;
wd.bytelen = l;
wd.data = sample_buffer;
wd.markerend = 0;
wd.markerstart = decode_pos_ms;
wd.bps = 16;
wd.nch = frameInfo.channels;
wd.numsamples =l/file_info.channels/(16/8);
wd.srate = file_info.sampling_rate;
if (!QCDCallbacks->toPlayer.OutputWrite(&wd))
done = TRUE;
}
}
}
}
Sleep(10);
}
// close up
play_thread_handle = INVALID_HANDLE_VALUE;
faacDecClose(hDecoder);
hDecoder = INVALID_HANDLE_VALUE;
close_filestream(infile);
infile = NULL;
if(seek_table)
{
free(seek_table);
seek_table = NULL;
seek_table_length = 0;
}
if(buffer)
{
LocalFree(buffer);
buffer = NULL;
}
if(memmap_buffer)
{
LocalFree(memmap_buffer);
memmap_buffer = NULL;
}
return 0;
}
// thread play funcs
int PlayThread_memmap()
{
sample_buffer = (char*)faacDecDecode(hDecoder, &frameInfo,
memmap_buffer + memmap_index, fileread - memmap_index - 1);
if (frameInfo.error)
{
// show_error(faacDecGetErrorMessage(frameInfo.error));
last_frame = 1;
}
memmap_index += frameInfo.bytesconsumed;
if (memmap_index >= fileread)
last_frame = 1;
return frameInfo.bytesconsumed;
}
int PlayThread_file()
{
int k;
if (buffercount > 0)
{
for (k = 0; k < (768*2 - buffercount); k++)
buffer[k] = buffer[k + buffercount];
read_buffer_filestream(infile, buffer + (768*2) - buffercount, buffercount);
buffercount = 0;
}
sample_buffer = (char*)faacDecDecode(hDecoder, &frameInfo, buffer, 768*2);
if (frameInfo.error)
{
// show_error(faacDecGetErrorMessage(frameInfo.error));
last_frame = 1;
}
buffercount += frameInfo.bytesconsumed;
bytecount += frameInfo.bytesconsumed;
if (bytecount >= fileread)
last_frame = 1;
return frameInfo.bytesconsumed;
}
// tag
int id3v2_tag(unsigned char *buffer)
{
if (StringComp(buffer, "ID3", 3) == 0)
{
unsigned long tagsize;
/* high bit is not used */
tagsize = (buffer[6] << 21) | (buffer[7] << 14) |
(buffer[8] << 7) | (buffer[9] << 0);
tagsize += 10;
return tagsize;
}
else
{
return 0;
}
}