From cc2160de5cc05dc3aa77f3a34358e66f6193c8c9 Mon Sep 17 00:00:00 2001 From: Michał Cichoń Date: Tue, 26 Jun 2012 20:35:30 +0200 Subject: Add support for AAC. --- faad2/src/plugins/QCD/QCDFAAD.c | 1030 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 1030 insertions(+) create mode 100644 faad2/src/plugins/QCD/QCDFAAD.c (limited to 'faad2/src/plugins/QCD/QCDFAAD.c') diff --git a/faad2/src/plugins/QCD/QCDFAAD.c b/faad2/src/plugins/QCD/QCDFAAD.c new file mode 100644 index 0000000..1d12295 --- /dev/null +++ b/faad2/src/plugins/QCD/QCDFAAD.c @@ -0,0 +1,1030 @@ +/* +** 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 +#include +#include +#include +#include +#include +#include "QCDInputDLL.h" + +#include "resource.h" + +#include +#include +#include +//#include + +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; + } +} \ No newline at end of file -- cgit v1.2.3