diff options
Diffstat (limited to 'Src/Plugins/Input/in_vorbis')
47 files changed, 9027 insertions, 0 deletions
diff --git a/Src/Plugins/Input/in_vorbis/DlgBase.cpp b/Src/Plugins/Input/in_vorbis/DlgBase.cpp new file mode 100644 index 00000000..43b0f771 --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/DlgBase.cpp @@ -0,0 +1,67 @@ +#include "DlgBase.h" + +void DlgBase::MakeComboEdit(UINT id, DWORD s) +{ + HWND w = GetDlgItem(wnd, id); + RECT r; + GetChildRect(id, r); + DestroyWindow(w); + CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", 0, WS_CHILD | s, r.left - 1, r.top - 1, r.right - r.left, r.bottom - r.top, wnd, (HMENU)id, 0, 0); +} + +void DlgBase::GetChildRect(UINT id, RECT& child) +{ + RECT r_parent, r_child; + GetWindowRect(wnd, &r_parent); + GetWindowRect(GetDlgItem(wnd, id), &r_child); + int dx = r_parent.left; + int dy = r_parent.top; + if (!(GetWindowLong(wnd, GWL_STYLE)&WS_CHILD)) + { + dy += GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYDLGFRAME); + dx += GetSystemMetrics(SM_CXDLGFRAME); + } + child.left = r_child.left - dx; + child.right = r_child.right - dx; + child.top = r_child.top - dy; + child.bottom = r_child.bottom - dy; +} + +void DlgBase::do_sizing(UINT wp, RECT * r) + { + UINT dx, dy; + dx = r->right - r->left; + dy = r->bottom - r->top; + if (dx < min_size_x_w) + { + switch (wp) + { + case WMSZ_BOTTOMLEFT: + case WMSZ_LEFT: + case WMSZ_TOPLEFT: + r->left = r->right - min_size_x_w; + break; + case WMSZ_BOTTOMRIGHT: + case WMSZ_RIGHT: + case WMSZ_TOPRIGHT: + r->right = r->left + min_size_x_w; + break; + } + } + if (dy < min_size_y_w) + { + switch (wp) + { + case WMSZ_BOTTOMLEFT: + case WMSZ_BOTTOM: + case WMSZ_BOTTOMRIGHT: + r->bottom = r->top + min_size_y_w; + break; + case WMSZ_TOPLEFT: + case WMSZ_TOP: + case WMSZ_TOPRIGHT: + r->top = r->bottom - min_size_y_w; + break; + } + } + }
\ No newline at end of file diff --git a/Src/Plugins/Input/in_vorbis/DlgBase.h b/Src/Plugins/Input/in_vorbis/DlgBase.h new file mode 100644 index 00000000..a194e836 --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/DlgBase.h @@ -0,0 +1,118 @@ +#include <windows.h> +#include "main.h" + +static void SetWindowRect(HWND w, RECT * r) +{ + SetWindowPos(w, 0, r->left, r->top, r->right - r->left, r->bottom - r->top, SWP_NOZORDER | SWP_NOCOPYBITS); +} + +class DlgBase +{ + public: + BOOL isDialogMessage(MSG * m) + { + return wnd ? IsDialogMessage(wnd, m) : 0; + } +protected: + void endDialog(int x) + { + EndDialog(wnd, x); + } + + void _do_size_x(RECT * r, UINT id, UINT wx, UINT min_x) + { + RECT r1 = {r->left, r->top, wx - min_x + r->right, r->bottom}; + SetWindowRect(GetDlgItem(wnd, id), &r1); + } + + void _do_size_xy(RECT * r, UINT id, UINT wx, UINT wy, UINT min_x, UINT min_y) + { + RECT r1 = {r->left, r->top, wx - min_x + r->right, wy - min_y + r->bottom}; + SetWindowRect(GetDlgItem(wnd, id), &r1); + } + + void _do_align_x_size_y(RECT * r, UINT id, UINT wx, UINT wy, UINT min_x, UINT min_y) + { + RECT r1 = {wx - min_x + r->left, r->top, wx - min_x + r->right, wy - min_y + r->bottom}; + SetWindowRect(GetDlgItem(wnd, id), &r1); + } + + void _do_align_x(RECT * r, UINT id, UINT wx, UINT min_x) + { + RECT r1 = {wx - min_x + r->left, r->top, wx - min_x + r->right, r->bottom}; + SetWindowRect(GetDlgItem(wnd, id), &r1); + } + + void _do_align_xy(RECT * r, UINT id, UINT wx, UINT wy, UINT min_x, UINT min_y) + { + RECT r1 = {wx - min_x + r->left, wy - min_y + r->top, wx - min_x + r->right, wy - min_y + r->bottom}; + SetWindowRect(GetDlgItem(wnd, id), &r1); + } + +#define do_size_x(id,r) _do_size_x(r,id,sx,min_size_x) +#define do_size_xy(id,r) _do_size_xy(r,id,sx,sy,min_size_x,min_size_y) +#define do_align_x_size_y(id,r) _do_align_x_size_y(r,id,sx,sy,min_size_x,min_size_y) +#define do_align_xy(id,r) _do_align_xy(r,id,sx,sy,min_size_x,min_size_y) +#define do_align_x(id,r) _do_align_x(r,id,sx,min_size_x) + + HWND wnd; + UINT min_size_x, min_size_y; + UINT min_size_x_w, min_size_y_w; + + void do_sizing(UINT wp, RECT * r); + void MakeComboEdit(UINT id, DWORD s); + void GetChildRect(UINT id, RECT& child); + + virtual BOOL DlgProc(UINT msg, WPARAM wp, LPARAM lp) { return 0;}; + static BOOL CALLBACK TheDialogProc(HWND wnd, UINT msg, WPARAM wp, LPARAM lp) + { + DlgBase * p; + if (msg == WM_INITDIALOG) + { + p = (DlgBase*)lp; + SetWindowLong(wnd, DWL_USER, lp); + p->wnd = wnd; + RECT r; + GetClientRect(wnd, &r); + p->min_size_x = r.right; + p->min_size_y = r.bottom; + GetWindowRect(wnd, &r); + p->min_size_x_w = r.right - r.left; + p->min_size_y_w = r.bottom - r.top; + } + else p = (DlgBase*)GetWindowLong(wnd, DWL_USER); + BOOL rv = 0; + if (p) + { + rv = p->DlgProc(msg, wp, lp); + if (msg == WM_DESTROY) + { + p->wnd = 0; + SetWindowLong(wnd, DWL_USER, 0); + } + } + return rv; + } + HWND myCreateDialog(UINT id, HWND parent) + { + return CreateDialogParamT(hIns, (char*)id, parent, TheDialogProc, (long)this); + } + virtual void myProcessMessage(MSG * msg) + { + if (!IsDialogMessage(wnd, msg)) + { + TranslateMessage(msg); + DispatchMessage(msg); + } + } + + int myDialogBox(UINT id, HWND parent) + { + return DialogBoxParamT(hIns, (char*)id, parent, TheDialogProc, (long)this); + } + DlgBase() { + wnd = 0; + min_size_x = min_size_y = min_size_x_w = min_size_y_w = 0; + } + virtual ~DlgBase() {} +}; diff --git a/Src/Plugins/Input/in_vorbis/ExtendedRead.cpp b/Src/Plugins/Input/in_vorbis/ExtendedRead.cpp new file mode 100644 index 00000000..c89f829d --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/ExtendedRead.cpp @@ -0,0 +1,65 @@ +#include "main.h" +#include "decoder.h" + +extern "C" +{ + //returns handle!=0 if successful, 0 if error + //size will return the final nb of bytes written to the output, -1 if unknown + __declspec( dllexport ) intptr_t winampGetExtendedRead_openW(const wchar_t *fn, int *size, int *bps, int *nch, int *srate) { + VorbisFile * f = VorbisFile::Create(fn,false); + if(f) { + if(!*bps) *bps=16; // FUCKO HAX + Decoder * d = new Decoder(); + d->Init(f, *bps, *nch, false, false); + *nch = (int)d->nch; + *srate = (int)d->sr; + *bps = (int)d->bps; + *size = (int)(f->Length() * (double)((*nch) * (*srate) * (*bps/8))); + return (intptr_t)d; + } + return 0; + } + + __declspec( dllexport ) intptr_t winampGetExtendedRead_openW_float(const wchar_t *fn, int *size, int *bps, int *nch, int *srate) { + VorbisFile * f = VorbisFile::Create(fn,false); + if(f) { + Decoder * d = new Decoder(); + d->Init(f, *bps, *nch, true, false); + *nch = (int)d->nch; + *srate = (int)d->sr; + *bps = (int)d->bps; + *size = (int)(f->Length() * (double)((*nch) * (*srate) * (*bps/8))); + return (intptr_t)d; + } + return 0; + } + + //returns nb of bytes read. -1 if read error (like CD ejected). if (ret<len), EOF is assumed + __declspec( dllexport ) intptr_t winampGetExtendedRead_getData(intptr_t handle, char *dest, size_t len, int *killswitch) { + Decoder * d = (Decoder *)handle; + size_t used = 0; + for(;;) { + used += (UINT)d->Read((UINT)(len - used),dest + used); + if(used >= len) break; + if(!d->DoFrame()) break; + if(*killswitch) break; + if (used) + return used; + } + return used; + } + + // return nonzero on success, zero on failure. + __declspec( dllexport ) int winampGetExtendedRead_setTime(intptr_t handle, int millisecs) { + Decoder * d = (Decoder *)handle; + d->Flush(); + return !d->Seek(((double)millisecs) / 1000.0); + } + + __declspec( dllexport ) void winampGetExtendedRead_close(intptr_t handle) { + Decoder * d = (Decoder *)handle; + d->Flush(); + delete d->file; + delete d; + } +}
\ No newline at end of file diff --git a/Src/Plugins/Input/in_vorbis/about.cpp b/Src/Plugins/Input/in_vorbis/about.cpp new file mode 100644 index 00000000..32409519 --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/about.cpp @@ -0,0 +1,264 @@ +#include "main.h" +#include "api__in_vorbis.h" +#include "resource.h" +#include <strsafe.h> + +/*static UINT xiphframes_ids[12]={IDB_BITMAP1,IDB_BITMAP2,IDB_BITMAP3,IDB_BITMAP4,IDB_BITMAP5,IDB_BITMAP6,IDB_BITMAP7,IDB_BITMAP8,IDB_BITMAP9,IDB_BITMAP10,IDB_BITMAP11,IDB_BITMAP12}; +static HBITMAP xiphframes[12];*/ + +static UINT xiphframes_ids[12]={IDB_PNG1,IDB_PNG2,IDB_PNG3,IDB_PNG4,IDB_PNG5,IDB_PNG6,IDB_PNG7,IDB_PNG8,IDB_PNG9,IDB_PNG10,IDB_PNG11,IDB_PNG12}; +static ARGB32 *xiphframes[12] = {0}; +static HBITMAP xiphframesBmp[12] = {0}; + +static void slap(HWND wnd,int v) +{ + long hi=GetWindowLong(wnd,4); + if (v) hi+=v*1000; + else hi=0; + SetWindowLong(wnd,4,hi); +} + +static CfgInt cfg_rpm("rpm",0); + +static int visible_rpm,visible_max_rpm; +static char show_rpm=0; +static DWORD last_visible_rpm; + +ARGB32 * loadImg(const void * data, int len, int *w, int *h, bool ldata=false) +{ + FOURCC imgload = svc_imageLoader::getServiceType(); + int n = (int)mod.service->service_getNumServices(imgload); + for(int i=0; i<n; i++) + { + waServiceFactory *sf = mod.service->service_enumService(imgload,i); + if(sf) + { + svc_imageLoader * l = (svc_imageLoader*)sf->getInterface(); + if(l) + { + if(l->testData(data,len)) + { + ARGB32* ret; + if(ldata) ret = l->loadImageData(data,len,w,h); + else ret = l->loadImage(data,len,w,h); + sf->releaseInterface(l); + return ret; + } + sf->releaseInterface(l); + } + } + } + return NULL; +} + +ARGB32 * loadRrc(int id, wchar_t * sec, int *w, int *h, bool data=false) +{ + DWORD size=0; + HGLOBAL resourceHandle = WASABI_API_LOADRESFROMFILEW(sec, MAKEINTRESOURCEW(id), &size); + if(resourceHandle) + { + ARGB32* ret = loadImg(resourceHandle,size,w,h,data); + UnlockResource(resourceHandle); + return ret; + } + return NULL; +} + +static LRESULT WINAPI XiphProc(HWND wnd,UINT msg,WPARAM wp,LPARAM lp) +{ + switch(msg) + { + case WM_CREATE: + SetWindowLong(wnd,8,last_visible_rpm=GetTickCount()); + SetTimer(wnd,666,10,0); + visible_rpm=-1; + visible_max_rpm=-1; + show_rpm=0; + break; + case WM_TIMER: + if (wp==666) + { + long low=GetWindowLong(wnd,0); + long hi=GetWindowLong(wnd,4); + + long org=low&~0xFFFF; + + int rpm=MulDiv(abs(hi),1000*60,12*0x10000); + + DWORD t=GetTickCount(); + DWORD ot=(DWORD)SetWindowLong(wnd,8,t); + bool redraw=0; + + if (rpm>25) show_rpm=1; + if (cfg_rpm<rpm) cfg_rpm=rpm; + + if (show_rpm && (t&~0x3F)!=(ot&~0x3F)) + { + wchar_t foo[128] = {0}; + if (visible_rpm<rpm || (visible_rpm>rpm && (t-last_visible_rpm)>333)) + { + last_visible_rpm=t; + visible_rpm=rpm; + StringCchPrintfW(foo,128,WASABI_API_LNGSTRINGW(IDS_GAME_SPEED),rpm); + SetDlgItemTextW(GetParent(wnd),IDC_RPM,foo); + } + if (visible_max_rpm!=cfg_rpm) + { + visible_max_rpm=cfg_rpm; + StringCchPrintfW(foo,128,WASABI_API_LNGSTRINGW(IDS_BEST_RPM),(int)cfg_rpm); + SetDlgItemTextW(GetParent(wnd),IDC_RPM2,foo); + } + } + + low+=hi*(t-ot); + while(low<0) low+=12*0x10000; + while(low>=12*0x10000) low-=12*0x10000; + + { + int z=hi>>6; + if (z) hi-=z; + else if (hi>0) hi--; + else if (hi<0) hi++; + } + + SetWindowLong(wnd,0,low); + SetWindowLong(wnd,4,hi); + if (redraw || (low&~0xFFFF)!=org) + { + RedrawWindow(wnd,0,0,RDW_INVALIDATE); + } + KillTimer(wnd,666); + SetTimer(wnd,666,10,0); + } + break; + case WM_LBUTTONDOWN: + slap(wnd,-1); + break; + case WM_RBUTTONDOWN: + slap(wnd,1); + break; + case WM_MBUTTONDOWN: + slap(wnd,0); + break; + case WM_PAINT: + { + int i=(GetWindowLong(wnd,0))>>16; + HDC dc = CreateCompatibleDC(0); + + if (!xiphframesBmp[i]) + { + int cur_w = 0, cur_h = 0; + xiphframes[i] = loadRrc(xiphframes_ids[i], L"PNG", &cur_w, &cur_h, true); + + BITMAPINFO bmi = {0}; + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = cur_w; + bmi.bmiHeader.biHeight = -cur_h; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + void *bits = 0; + if(xiphframesBmp[i]) DeleteObject(xiphframesBmp[i]); + xiphframesBmp[i] = CreateDIBSection(dc, &bmi, DIB_RGB_COLORS, &bits, NULL, 0); + memcpy(bits, xiphframes[i], cur_w * cur_h * 4); + } + + if (xiphframesBmp[i]) + { + HGDIOBJ foo = SelectObject(dc, xiphframesBmp[i]); + HDC wdc = GetDC(wnd); + RECT r = {0}; + GetClientRect(wnd, &r); + FillRect(wdc, &r, GetSysColorBrush(COLOR_3DFACE)); + + BLENDFUNCTION blendFn = {0}; + blendFn.BlendOp = AC_SRC_OVER; + blendFn.SourceConstantAlpha = 255; + blendFn.AlphaFormat = AC_SRC_ALPHA; + AlphaBlend(wdc, 2, 2, r.right - 2, r.bottom - 2, dc, 0, 0, 63, 63, blendFn); + + ReleaseDC(wnd, wdc); + SelectObject(dc, foo); + } + DeleteDC(dc); + } + break; + case WM_DESTROY: + { + for (int i = 0; i < ARRAYSIZE(xiphframes_ids); i++) + { + if(xiphframesBmp[i]) DeleteObject(xiphframesBmp[i]); xiphframesBmp[i] = 0; + if(xiphframes[i] && WASABI_API_MEMMGR) WASABI_API_MEMMGR->sysFree((void *)xiphframes[i]); xiphframes[i] = 0; + } + KillTimer(wnd,666); + break; + } + }; + return DefWindowProc(wnd,msg,wp,lp); +} + +static BOOL CALLBACK AboutProc(HWND wnd,UINT msg,WPARAM wp,LPARAM lp) +{ + switch(msg) + { + case WM_INITDIALOG: + { + wchar_t tmp[1024] = {0}, tmp2[1024] = {0}, *t1 = tmp, *t2 = tmp2, text[1024] = {0}; + SetWindowTextW(wnd,WASABI_API_LNGSTRINGW_BUF(IDS_NULLSOFT_VORBIS_DECODER_OLD,text,1024)); + StringCchPrintfW(tmp,1024,WASABI_API_LNGSTRINGW(IDS_ABOUT_TEXT),mod.description,__DATE__); + // due to quirks with the more common resource editors, is easier to just store the string + // internally only with \n and post-process to be \r\n (as here) so it will appear correctly + // on new lines as is wanted (silly multiline edit controls) + while(t1 && *t1 && (t2 - tmp2 < 1024)) + { + if(*t1 == L'\n') + { + *t2 = L'\r'; + t2 = CharNextW(t2); + } + *t2 = *t1; + t1 = CharNextW(t1); + t2 = CharNextW(t2); + } + + SetDlgItemTextW(wnd,IDC_ABOUT_TEXT,tmp2); + // fixes the incorrect selection of the text on dialog opening + PostMessage(GetDlgItem(wnd,IDC_ABOUT_TEXT),EM_SETSEL,-1,0); + return 1; + } + case WM_COMMAND: + if (wp==IDOK || wp==IDCANCEL) + { + do_cfg(1); + EndDialog(wnd,0); + } + break; + } + return 0; +} + +void About(HWND hwndParent) +{ + static char got_xiph; + if (!got_xiph) + { + WNDCLASS wc= + { + 0, + XiphProc, + 0, + 12, + WASABI_API_LNG_HINST, + 0, + LoadCursor(0,IDC_ARROW), + 0, + 0, + L"XIPH_CLASS", + }; + + RegisterClassW(&wc); + got_xiph=1; + } + + WASABI_API_DIALOGBOXW(IDD_ABOUT,hwndParent,AboutProc); +}
\ No newline at end of file diff --git a/Src/Plugins/Input/in_vorbis/api__in_vorbis.h b/Src/Plugins/Input/in_vorbis/api__in_vorbis.h new file mode 100644 index 00000000..5f89683e --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/api__in_vorbis.h @@ -0,0 +1,21 @@ +#ifndef NULLSOFT_API_H +#define NULLSOFT_API_H + +#include "../Agave/Config/api_config.h" +#include "../Agave/Language/api_language.h" + +#include <api/application/api_application.h> +extern api_application *applicationApi; +#define WASABI_API_APP applicationApi + +#include <api/service/svcs/svc_imgload.h> + +#include <api/service/api_service.h> + +#include <api/service/waServiceFactory.h> + +#include <api/memmgr/api_memmgr.h> +extern api_memmgr *memmgrApi; +#define WASABI_API_MEMMGR memmgrApi + +#endif
\ No newline at end of file diff --git a/Src/Plugins/Input/in_vorbis/c_string.cpp b/Src/Plugins/Input/in_vorbis/c_string.cpp new file mode 100644 index 00000000..4310a0dd --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/c_string.cpp @@ -0,0 +1,96 @@ +#define STRICT +#include <windows.h> +#include <malloc.h> +#include <stdio.h> +#include "c_string.h" +#include "../nu/ns_wc.h" + +extern BOOL is_nt; + +template<class myChar> +void string_base<myChar>::makespace(UINT s) +{ + if (size<s) + { + int oldSize = size; + do size<<=1; while(size<s); + myChar *newPtr = (myChar*)realloc(ptr,size*sizeof(myChar)); + if (!newPtr) + { + newPtr = (myChar*)malloc(size*sizeof(myChar)); + if (newPtr) + { + memcpy(newPtr, ptr, oldSize*sizeof(myChar)); + free(ptr); + ptr = newPtr; + } + else return ; + } + else ptr = newPtr; + } +} + +void String::s_GetWindowText(HWND w) +{ + Reset(); + int len=GetWindowTextLengthA(w)+1; + GetWindowTextA(w,StringTempA(*this,len),len); +} + +void StringW::s_GetWindowText(HWND w) +{ + Reset(); + int len=GetWindowTextLengthW(w)+1; + GetWindowTextW(w,StringTempW(*this,len),len); +} + +void String::SetStringW(const WCHAR * c) +{ + UINT len=(lstrlenW(c)+1)*2; + WideCharToMultiByteSZ(CP_ACP,0,c,-1,StringTempA(*this,len),len,0,0); +} + +void StringW::SetStringA(const char * c) +{ + UINT len=(UINT)strlen(c)+1; + MultiByteToWideCharSZ(CP_ACP,0,c,-1,StringTempW(*this,len),len); +} + +void String::AddStringW(const WCHAR * c) +{ + AddString(String(c)); +} + +void StringW::AddStringA(const char * c) +{ + AddString(StringW(c)); +} + +void String::s_SetWindowText(HWND w) +{ + SetWindowTextA(w,*this); +} + +void StringW::s_SetWindowText(HWND w) +{ + SetWindowTextW(w,*this); +} + + +StringPrintf::StringPrintf(const char * fmt,...) +{ + va_list list; + va_start(list,fmt); + vsprintf(StringTempA(*this,1024),fmt,list); + va_end(list); +} + +StringPrintfW::StringPrintfW(const WCHAR * fmt,...) +{ + va_list list; + va_start(list,fmt); + vswprintf(StringTempW(*this,1024),1024,fmt,list); + va_end(list); +} + +String::String(const StringW & z) {AddStringW(z);}
\ No newline at end of file diff --git a/Src/Plugins/Input/in_vorbis/c_string.h b/Src/Plugins/Input/in_vorbis/c_string.h new file mode 100644 index 00000000..9773c078 --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/c_string.h @@ -0,0 +1,156 @@ +#pragma once +template <class myChar> +class string_base +{ +private: + myChar * ptr; + UINT size,used; + void makespace(UINT s); + static UINT mylen(const myChar * p) {UINT r=0;while(p[r]) r++;return r;} +public: + void AddChar(myChar c) + { + makespace(used+2); + ptr[used++]=c; + ptr[used]=0; + } + string_base() + { + used=0; + size=128; + ptr=(myChar*)malloc(size*sizeof(myChar)); + ptr[0]=0; + } + + ~string_base() { if (ptr) free(ptr);} + + operator const myChar*() const {return ptr;} + + const myChar & operator*() const {return *ptr;} + + UINT Length() const {return used;} + + void AddString(const myChar * c) + { + UINT d=mylen(c); + makespace(used+d+1); + memcpy(ptr+used,c,sizeof(myChar)*d); + used+=d; + ptr[used]=0; + } + + void Reset() {Truncate(0);} + void Truncate(UINT x) {if (used>x) {used=x;ptr[x]=0;}} + + void SetString(const myChar * s) {Reset();AddString(s);} + + myChar * BufferStart(UINT n) + { + makespace(n+1); + memset(ptr,0,size); + return ptr; + } + + inline void BufferDone() {used=mylen(ptr);} + + void SetChar(UINT offset,myChar c)//hack for some ghey routines + { + if (!c) Truncate(offset); + else if (offset<used) ptr[offset]=c; + } +}; + +template<class myChar> +class StringTemp +{ +private: + string_base<myChar> * parent; + myChar * data; +public: + StringTemp(string_base<myChar> & s,UINT siz) {parent=&s;data=s.BufferStart(siz);} + ~StringTemp() {parent->BufferDone();} + operator myChar* () {return data;} +}; + +#define StringTempW StringTemp<WCHAR> +#define StringTempA StringTemp<char> + +class StringW; + +class String : public string_base<char> +{ +public: + String() {} + String(HWND w) {s_GetWindowText(w);} + String(const char* z) {SetString(z);} + String(const WCHAR* z) {SetStringW(z);} + String(const String& z) {SetString(z);} + String(const StringW& z); + void AddStringW(const WCHAR * c); + void SetStringW(const WCHAR * c); + void s_GetWindowText(HWND w); + void s_SetWindowText(HWND w); + void operator=(const char * s) {SetString(s);} + void operator+=(const char * s) {AddString(s);} + void operator=(String & s) {SetString(s);} + void operator+=(String & s) {AddString(s);} + inline void s_GetDlgItemText(HWND w,UINT id) {s_GetWindowText(GetDlgItem(w,id));} + inline void s_SetDlgItemText(HWND w,UINT id) {s_SetWindowText(GetDlgItem(w,id));} +}; + +class StringW : public string_base<WCHAR> +{ +public: + StringW() {} + StringW(HWND w) {s_GetWindowText(w);} + StringW(const WCHAR * z) {SetString(z);} + void AddStringA(const char * c); + void SetStringA(const char * c); + StringW(const char * z) {SetStringA(z);} + StringW(const StringW & z) {SetString(z);} + StringW(const String & z) {SetStringA(z);} + void s_GetWindowText(HWND w); + void s_SetWindowText(HWND w); + void operator=(const WCHAR * s) {SetString(s);} + void operator+=(const WCHAR * s) { if (s) AddString(s);} + void operator=(StringW & s) {SetString(s);} + void operator+=(StringW & s) {AddString(s);} + inline void s_GetDlgItemText(HWND w,UINT id) {s_GetWindowText(GetDlgItem(w,id));} + inline void s_SetDlgItemText(HWND w,UINT id) {s_SetWindowText(GetDlgItem(w,id));} + bool reg_read(const char *name); + void reg_write(const char *name); +}; + + +class StringPrintf : public String +{ +public: + StringPrintf(const char * fmt,...); +}; + +class StringPrintfW : public StringW +{ +public: + StringPrintfW(const WCHAR * fmt,...); +}; + +template<class myChar> +class StringF2T : public string_base<myChar> +{ +public: + StringF2T(const myChar * fn) + { + const myChar * ptr=fn,*dot=0,*src=fn; + while(ptr && *ptr) + { + if (*ptr=='\\' || *ptr=='/' || *ptr==':') src=ptr+1; + else if (*ptr=='.') dot=ptr; + ptr++; + } + + while(src && *src && (!dot || src<dot)) AddChar(*(src++)); + } +}; + +#define StringF2T_A StringF2T<char> +#define StringF2T_W StringF2T<WCHAR>
\ No newline at end of file diff --git a/Src/Plugins/Input/in_vorbis/chainedstream_parse.cpp b/Src/Plugins/Input/in_vorbis/chainedstream_parse.cpp new file mode 100644 index 00000000..35ed0198 --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/chainedstream_parse.cpp @@ -0,0 +1,82 @@ +#include <windows.h> +#include <stdlib.h> +#include <stdio.h> + +#include <ogg/ogg.h> +#include <vorbis/vorbisfile.h> + +static size_t callback_fread(void *ptr, size_t size, size_t nmemb, HANDLE hFile) +{ + DWORD bw = 0; + ReadFile(hFile,ptr,(DWORD)(size*nmemb),&bw,0); + return bw/size; +} + +static size_t callback_write(void * ptr, size_t size, size_t nmemb, HANDLE hFile) +{ + DWORD bw = 0; + WriteFile(hFile,ptr,(DWORD)(size*nmemb),&bw,0); + return bw/size; +} + +static int callback_fseek(HANDLE hFile, __int64 offset, int whence) +{ + __int64 temp = offset; + SetFilePointer(hFile,*(DWORD*)&temp,((long*)&temp+1),whence); + return 0; +} + +static int callback_fclose(HANDLE f) +{ + return 0; +} + +static __int64 callback_ftell(HANDLE hFile) +{ + __int64 ret=0; + *(DWORD*)&ret = SetFilePointer(hFile,0,((long*)&ret+1),FILE_CURRENT); + return ret; +} + +static void* callbacks[4]= +{ + callback_fread,callback_fseek,callback_fclose,callback_ftell +}; + +namespace ogg_helper +{ + int num_get_tracks(HANDLE hFile/*track_indexer::callback * out,reader * r*/) + { + SetFilePointer(hFile,0,0,FILE_BEGIN); + OggVorbis_File l_vf; + memset(&l_vf,0,sizeof(l_vf)); + if (ov_open_callbacks(hFile,&l_vf,0,0,*(ov_callbacks*)callbacks)) + { + return 0; + } + int rv = l_vf.links; + ov_clear(&l_vf); + return rv; + } + + int query_chained_stream_offset(HANDLE hFile,int idx,__int64 * out_beginning,__int64 * out_end) + { + SetFilePointer(hFile,0,0,FILE_BEGIN); + OggVorbis_File l_vf; + memset(&l_vf,0,sizeof(l_vf)); + if (ov_open_callbacks(hFile,&l_vf,0,0,*(ov_callbacks*)callbacks)) + { + return 0; + } + int retval = 0; + if (idx>=0 && idx<l_vf.links) + { + retval = 1; + *out_beginning = l_vf.offsets[idx]; + *out_end = l_vf.offsets[idx+1]; + } + + ov_clear(&l_vf); + return retval; + } +}
\ No newline at end of file diff --git a/Src/Plugins/Input/in_vorbis/config.cpp b/Src/Plugins/Input/in_vorbis/config.cpp new file mode 100644 index 00000000..fc3d6afd --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/config.cpp @@ -0,0 +1,335 @@ +#include "main.h" +#include "api__in_vorbis.h" +#include "../nu/ns_wc.h" +#include <commctrl.h> +#include <shlobj.h> +#include "../winamp/wa_ipc.h" +#include "../nu/AutoChar.h" +#include <strsafe.h> + +int mc6_dm_names_ids[]={IDS_LEAVE_AS_IS,IDS_REMAP_6_CHANNELS,IDS_DOWNMIX_TO_4_CHANNELS,IDS_DOWNMIX_TO_2_CHANNELS_DS,IDS_DOWNMIX_TO_2_CHANNELS_DS2,IDS_DOWNMIX_TO_MONO}; +int mc6_map_names_id[]={IDS_CORRECT_FL_FC_FR_BL_BR_LFE,IDS_BROKEN_FL_FR_FC_BL_BR_LFE}; +int32_t priority_tab[7]={THREAD_PRIORITY_IDLE,THREAD_PRIORITY_LOWEST,THREAD_PRIORITY_BELOW_NORMAL,THREAD_PRIORITY_NORMAL,THREAD_PRIORITY_ABOVE_NORMAL,THREAD_PRIORITY_HIGHEST,THREAD_PRIORITY_TIME_CRITICAL}; + +char* defaultDumpDir() +{ + static char dumpdir[MAX_PATH] = {0}; + if(FAILED(SHGetFolderPathA(NULL, CSIDL_MYMUSIC, NULL, SHGFP_TYPE_CURRENT, dumpdir))) + { + if(FAILED(SHGetFolderPathA(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, dumpdir))) + { + lstrcpynA(dumpdir, "C:\\", MAX_PATH); + } + } + return dumpdir; +} + +CfgString + cfg_ssave_format("ssave_format","%filename%"), + cfg_dumpdir("dumpdir",defaultDumpDir()); + +CfgInt + cfg_http_bsize("http_bsize",0x10000), + cfg_fsave("fsave",0), + cfg_abr("abr",0), + cfg_proxy_mode("proxy_mode",2), + cfg_prebuf1("prebuf1",50), + cfg_prebuf2("prebuf2",75), + cfg_httpseek2("httpseek2",0), + cfg_fix0r("fix0r",1), + cfg_mc6_dm("mc6_dm",0), + cfg_mc6_map("_mc6_map",0), + cfg_remember_infosize("remember_infosize",1), + cfg_fullbuf("fullbuf",0), + cfg_cur_tab("cur_tab",0); + +static int old_preamp; +CfgFont cfg_font("font"); +static LOGFONT cfg_font_edit; + +BOOL CALLBACK browseEnumProc(HWND hwnd, LPARAM lParam) +{ + char cl[32] = {0}; + GetClassNameA(hwnd, cl, ARRAYSIZE(cl)); + if (!lstrcmpiA(cl, WC_TREEVIEWA)) + { + PostMessage(hwnd, TVM_ENSUREVISIBLE, 0, (LPARAM)TreeView_GetSelection(hwnd)); + return FALSE; + } + + return TRUE; +} + +static int CALLBACK browzaproc(HWND hwnd, UINT msg, LPARAM lp, LPARAM dat) +{ + if (msg == BFFM_INITIALIZED) + { + SendMessageW(hwnd, BFFM_SETSELECTIONW, 1, (LPARAM)dat); + + // this is not nice but it fixes the selection not working correctly on all OSes + EnumChildWindows(hwnd, browseEnumProc, 0); + } + return 0; +} + +static void d_browza(HWND wnd,HWND bt,wchar_t* tx) +{ + IMalloc* pMalloc=0; + + SHGetMalloc(&pMalloc); + if (!pMalloc) return; + + wchar_t dir[MAX_PATH] = {0}; + GetWindowTextW(bt,dir,MAX_PATH); + BROWSEINFOW bi= + { + wnd, + 0, + 0, + tx, + BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE, + browzaproc, + (LPARAM)dir, + 0 + }; + ITEMIDLIST* li=SHBrowseForFolderW(&bi); + if (li) + { + SHGetPathFromIDListW(li,dir); + SetWindowTextW(bt,dir); + pMalloc->Free(li); + } + + pMalloc->Release(); +} + +static BOOL CALLBACK CfgProc1(HWND wnd,UINT msg,WPARAM wp,LPARAM lp) +{ + switch(msg) + { + case WM_INITDIALOG: + { + wchar_t temp[128] = {0}, cfg_dialog_name[128] = {0}; + StringCchPrintfW(cfg_dialog_name,128,WASABI_API_LNGSTRINGW(IDS_TITLE_PREFERENCES), + WASABI_API_LNGSTRINGW_BUF(IDS_NULLSOFT_VORBIS_DECODER_OLD, temp, 128)); + SetWindowTextW(wnd,cfg_dialog_name); + + SendDlgItemMessage(wnd,IDC_FULLBUF,BM_SETCHECK,cfg_fullbuf,0); + + UINT n; + HWND w=GetDlgItem(wnd,IDC_MC6_DM); + for(n=0;n<sizeof(mc6_dm_names_ids)/sizeof(mc6_dm_names_ids[0]);n++) + { + SendMessageW(w,CB_ADDSTRING,0,(LPARAM)WASABI_API_LNGSTRINGW(mc6_dm_names_ids[n])); + } + SendMessage(w,CB_SETCURSEL,cfg_mc6_dm,0); + + w=GetDlgItem(wnd,IDC_MC6_MAP); + for(n=0;n<sizeof(mc6_map_names_id)/sizeof(mc6_map_names_id[0]);n++) + { + SendMessageW(w,CB_ADDSTRING,0,(LPARAM)WASABI_API_LNGSTRINGW(mc6_map_names_id[n])); + } + SendMessage(w,CB_SETCURSEL,cfg_mc6_map,0); + + SendDlgItemMessage(wnd,IDC_AVG_BR,BM_SETCHECK,cfg_abr,0); + + SetDlgItemInt(wnd,IDC_HTTP_BSIZE,cfg_http_bsize>>10,0); + if (cfg_fsave) SendDlgItemMessage(wnd,IDC_FSAVE,BM_SETCHECK,1,0); + if (cfg_fix0r) SendDlgItemMessage(wnd,IDC_FIX0R,BM_SETCHECK,1,0); + cfg_dumpdir.s_SetDlgItemText(wnd,IDC_STREAM_SAVE); + w=GetDlgItem(wnd,IDC_PROXY); + SendMessageW(w,CB_ADDSTRING,0,(LPARAM)WASABI_API_LNGSTRINGW(IDS_NEVER)); + SendMessageW(w,CB_ADDSTRING,0,(LPARAM)WASABI_API_LNGSTRINGW(IDS_PORT_80_ONLY)); + SendMessageW(w,CB_ADDSTRING,0,(LPARAM)WASABI_API_LNGSTRINGW(IDS_ALWAYS)); + SendMessage(w,CB_SETCURSEL,cfg_proxy_mode,0); + + w=GetDlgItem(wnd,IDC_SLIDER1); + SendMessage(w,TBM_SETRANGE,0,MAKELONG(1,100)); + SendMessage(w,TBM_SETPOS,1,cfg_prebuf1); + w=GetDlgItem(wnd,IDC_SLIDER2); + SendMessage(w,TBM_SETRANGE,0,MAKELONG(1,100)); + SendMessage(w,TBM_SETPOS,1,cfg_prebuf2); + + cfg_ssave_format.s_SetDlgItemText(wnd,IDC_SSAVE_FMT); + SendMessage(wnd,WM_COMMAND,MAKEWPARAM(IDC_FSAVE,BN_CLICKED),(LPARAM)GetDlgItem(wnd,IDC_FSAVE)); + } + return 1; + + case WM_COMMAND: + switch(LOWORD(wp)) + { + case IDC_STREAM_SAVE: + d_browza(wnd,(HWND)lp,WASABI_API_LNGSTRINGW(IDS_SELECT_OUTPUT_DIRECTORY)); + break; + + case IDC_SSAVE_FMT_DEF: + SetDlgItemText(wnd,IDC_SSAVE_FMT,L"%filename%"); + break; + + case IDC_FSAVE: + { + int checked = IsDlgButtonChecked(wnd,IDC_FSAVE); + EnableWindow(GetDlgItem(wnd,IDC_STREAM_SAVE),checked); + EnableWindow(GetDlgItem(wnd,IDC_SSAVE_FMT),checked); + EnableWindow(GetDlgItem(wnd,IDC_SSAVE_FMT_DEF),checked); + } + break; + + case IDOK: + case IDCANCEL: + { + if (LOWORD(wp) == IDOK) + { + cfg_fullbuf=(int)SendDlgItemMessage(wnd,IDC_FULLBUF,BM_GETCHECK,0,0); + + cfg_mc6_dm=(int)SendDlgItemMessage(wnd,IDC_MC6_DM,CB_GETCURSEL,0,0); + cfg_mc6_map=(int)SendDlgItemMessage(wnd,IDC_MC6_MAP,CB_GETCURSEL,0,0); + + cfg_abr=(int)SendDlgItemMessage(wnd,IDC_AVG_BR,BM_GETCHECK,0,0); + + cfg_dumpdir.s_GetDlgItemText(wnd,IDC_STREAM_SAVE); + cfg_http_bsize=GetDlgItemInt(wnd,IDC_HTTP_BSIZE,0,0)<<10; + cfg_fsave=(int)SendDlgItemMessage(wnd,IDC_FSAVE,BM_GETCHECK,0,0); + cfg_fix0r=(int)SendDlgItemMessage(wnd,IDC_FIX0R,BM_GETCHECK,0,0); + cfg_proxy_mode=(int)SendDlgItemMessage(wnd,IDC_PROXY,CB_GETCURSEL,0,0); + cfg_prebuf1=(int)SendDlgItemMessage(wnd,IDC_SLIDER1,TBM_GETPOS,0,0); + cfg_prebuf2=(int)SendDlgItemMessage(wnd,IDC_SLIDER2,TBM_GETPOS,0,0); + cfg_ssave_format.s_GetDlgItemText(wnd,IDC_SSAVE_FMT); + } + do_cfg(1); + EndDialog(wnd,(LOWORD(wp) == IDOK)); + } + break; + } + break; + } + + const int controls[] = + { + IDC_SLIDER1, + IDC_SLIDER2, + }; + if (FALSE != WASABI_API_APP->DirectMouseWheel_ProcessDialogMessage(wnd, msg, wp, lp, controls, ARRAYSIZE(controls))) + { + return TRUE; + } + + return 0; +} + +extern HANDLE hThread;//hack + +void Config(HWND p) +{ + if (WASABI_API_DIALOGBOXPARAMW(IDD_CONFIG,p,CfgProc1,0)) + { + if (hThread) PostMessage(mod.hMainWindow,WM_USER,0,243); + } +} + +int CfgVar::read_int(const char *inifile, const char *section,const char * name,int def) +{ + return GetPrivateProfileIntA(section, name, def, inifile); +} + +void CfgVar::write_int(const char *inifile, const char *section, const char * name,int val) +{ + char temp[32] = {0}; + StringCchPrintfA(temp, 32, "%d", val); + WritePrivateProfileStringA(section, name, temp, inifile); +} + +void CfgVar::write_struct(const char *inifile, const char *section, const char * name, void * ptr,UINT size) +{ + WritePrivateProfileStructA("in_vorbis", name, ptr, size, INI_FILE); +} + +bool CfgVar::read_struct(const char *inifile, const char *section, const char * name,void * ptr,UINT size) +{ + return !!GetPrivateProfileStructA("in_vorbis", name, ptr, size, INI_FILE); +} + +void do_cfg(int s) +{ + #define CFG_VERSION 0x10204 + + if (!s) + { + if (CfgVar::read_int(INI_FILE, "in_vorbis", "version",0)==CFG_VERSION) + CfgVar::ReadConfig(); + } + else + { + CfgVar::WriteConfig(); + CfgVar::write_int(INI_FILE, "in_vorbis", "version",CFG_VERSION); + } +} + +CfgVar * CfgVar::list=0; + +void CfgVar::ReadConfig() +{ + CfgVar * p=list; + while(p) + { + p->Read(p->name); + p=p->next; + } +} + +void CfgVar::WriteConfig() +{ + CfgVar * p=list; + while(p) + { + p->Write(p->name); + p=p->next; + } +} + +bool StringW::reg_read(const char * name) +{ + char utf8_data[2048] = {0}; + wchar_t utf16_data[2048] = {0}; + GetPrivateProfileStringA("in_vorbis", name, "@default@", utf8_data, 2048, INI_FILE); + if (!strcmp("@default@", utf8_data)) + return false; + + MultiByteToWideCharSZ(CP_UTF8, 0, utf8_data, -1, utf16_data, 2048); + SetString(utf16_data); + + return true; +} + +void StringW::reg_write(const char * name) +{ + WritePrivateProfileStringA("in_vorbis", name, AutoChar((const WCHAR *)*this, CP_UTF8), INI_FILE); +} + +void CfgString::Read(const char * name) +{ + reg_read(name); +} + +void CfgString::Write(const char * name) +{ + StringW temp; + if (temp.reg_read(name)) + { + if (wcscmp(temp,*this)) reg_write(name); + } + else + { + if (wcscmp(def,*this)) reg_write(name); + } +} + +void CfgInt::Write(const char * name) +{ + if (read_int(INI_FILE, "in_vorbis", name,def)!=value) write_int(INI_FILE, "in_vorbis",name,value); +} + +void CfgInt::Read(const char * name) +{ + value=read_int(INI_FILE, "in_vorbis", name,def); +}
\ No newline at end of file diff --git a/Src/Plugins/Input/in_vorbis/core_api.h b/Src/Plugins/Input/in_vorbis/core_api.h new file mode 100644 index 00000000..07d48b5b --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/core_api.h @@ -0,0 +1,567 @@ + +/* Winamp 3 Player core api v0.1 +** (c)2000 nullsoft jcf/ct/dk +** Notes: +** Keep in mind that this header file is subject to change prior to the +** release of Winamp 3. The ability to configure plug-ins has yet to be +** added and is the first and foremost concern of the engineering team. +*/ + +#ifndef __CORE_API_H +#define __CORE_API_H +/* +// Visual C 6 makes big unaligned dlls. the following will correct it +#ifndef _DEBUG +// release optimizations +// /Og (global optimizations), /Os (favor small code), /Oy (no frame pointers) +#pragma optimize("gsy",on) +#pragma comment(linker,"/RELEASE") +// set the 512-byte alignment +#pragma comment(linker,"/opt:nowin98") +#endif +*/ +// Use Assert in your code to catch errors that shouldn't happen, when compiled in release mode, they are #defined out +#ifndef ASSERT +#ifdef _DEBUG +#define ASSERT(x) if (!(x)) MessageBox(NULL,"ASSERT FAILED: " #x,"ASSERT FAILED in " __FILE__ ,MB_OK|MB_ICONSTOP); +#else +#define ASSERT(x) +#endif +#endif + + + +/* CLASS DESCRIPTIONS */ + +/* WReader +** File reader module class, ie. opens and reads files, streams +*/ +class WReader; + +/* WInputInfo +** Class that returns information (length, title, metadata) about a specified file +*/ +class WInputInfo; + +/* WInfo_callback +** Player's interface that provides Winamp 3 core functions to your WInputInfo classes +*/ +class WInfo_callback; + +/* WInputSource +** Input Source manager base class, ie. decodes mp3's, wavs +*/ +class WInputSource; + +/* WOutputFilter +** Abstract base class for any Output Filter plug-in, ie. changes pitch, converts format, outputs to speakers +*/ +class WOutputFilter; + +/* WPlayer_callback +** Player's interface that provides Winamp 3 core functions to your Input Sources and Output Filter plug-ins +** (Getting a reader for opening a file, sending stuff about what's going on to the Winamp 3 core) +*/ +class WPlayer_callback; + + + + +class WPlayer_callback +{ + public: + /* GetReader + ** Allows your Input Source and Output Filter plugins to request a reader from Winamp, + ** so you don't have to worry about opening files or streams + */ + virtual WReader *GetReader(char *url)=0; + + + /* The 3 following functions allows your Input Source and Output Filter plugins to send error/warning/status + ** messages back to the Winamp 3 core + */ + + /* Error + ** playback should stop (soundcard driver error, etc) + */ + virtual void Error(char *reason)=0; + + /* Warning + ** warning (something recoverable, like file not found, etc) + */ + virtual void Warning(char *warning)=0; + + /* Status + ** status update (nothing really wrong) + */ + virtual void Status(char *status)=0; + + + + + /* TitleChange + ** should be called if the current file titlename changes during the decoding + */ + virtual void TitleChange(char *new_title)=0; + + /* InfoChange + ** should be called if the infos about the current file changes during the decoding + */ + virtual void InfoChange(char *new_info_str, int new_length)=0; + + /* UrlChange + ** should be called if the current file associated URL changes during the decoding + */ + virtual void UrlChange(char *new_url)=0; +}; + + + + +class WInfo_callback +{ + public: + /* GetReader + ** Allows your WInfo classes to request a reader from Winamp + ** so you don't have to worry about opening files or streams + */ + virtual WReader *GetReader(char *url)=0; +}; + + + + +class WInputInfo +{ + public: + /* WInputInfo + ** WInputInfo constructor + */ + WInputInfo(){ }; + + /* m_info + ** Filled by Winamp. Pointer to WInputInfo callback function + */ + WInfo_callback *m_info; + + /* Open + ** Called by Winamp to request informations about a specified media (file, url, etc...) + ** returns 0 if succesful, 1 if not + ** + ** You must open, get all information and close the specified file here and store + ** the useful information into member elements for quick access by other functions + */ + virtual int Open(char *url) { return 1; } + + /* GetTitle + ** Called by Winamp to get the decoded title about the file opened + ** i.e. id3 title name, etc... + */ + virtual void GetTitle(char *buf, int maxlen) { if (maxlen>0) buf[0]=0; }; + + /* GetInfoString + ** Called by Winamp to get extra informations about the file opened + ** i.e. "160kbps stereo 44Khz" for MP3 files,"4 channels" for MOD files,etc... + */ + virtual void GetInfoString(char *buf, int maxlen) { if (maxlen>0) buf[0]=0; }; + + /* GetLength + ** Called by Winamp to retrieves media type length in milliseconds + ** returns -1 if length is undefined/infinite + */ + virtual int GetLength(void) { return -1; }; + + /* GetMetaData + ** Fetches metadata by attribute name (Artist, Album, Bitrate, etc...) + ** attribute names are non case-sensitive. + ** returns size of data + */ + virtual int GetMetaData(char *name, char *data, int data_len) { if (data&&data_len>0) *data=0; return 0; } + + /* ~WInputInfo + ** WInputInfo virtual destructor + */ + //virtual ~WInputInfo() { }; + virtual void Release(int)=0; +}; + + + + + + + + +/* WINAMP Output Filter NOTIFY MESSAGES +** Messages returned to notify Output Filter plug-ins of events +*/ + +typedef enum { + + /* WOFNM_FILETITLECHANGE + ** Sent when the song changes + ** param1=new filename song + ** param2=new title song + */ + WOFNM_FILETITLECHANGE=1024, + + /* WOFNM_ENDOFDECODE + ** Sent when decoding ends + */ + WOFNM_ENDOFDECODE, + +} WOutputFilterNotifyMsg; + + + + +class WOutputFilter +{ + protected: + /* WOutputFilter + ** WOutputFilter constructor + */ + WOutputFilter() { m_next=NULL; }; + + public: + /* m_player + ** Filled by Winamp. Pointer to Winamp 3 core player interface + */ + WPlayer_callback *m_player; + + /* m_next + ** Internally used by Winamp. Pointer to next activated Output Filter + */ + WOutputFilter *m_next; + + /* ~WOutputFilter + ** WOutputFilter destructor + */ + //virtual ~WOutputFilter() { }; + virtual void Release(int)=0; + + /* GetDescription + ** Retrieves your plug-in's text description + */ + virtual char *GetDescription() { return "Unknown"; }; + + /* ProcessSamples + ** Render data as it receives it + ** sampledata: Data to process + ** bytes: number of bytes to process + ** bps: Bits per sample (8 or 16) + ** nch: Number of channels (1 or 2) + ** srate: Sample rate in Hz + ** killswitch: Will be set to 1 by winamp if stop if requested. Poll the pointed value very often to + ** make sure Winamp doesn't hang + ** + ** Returns the number of processed bytes or -1 if unable to open the device or an error occured. + ** + ** You have to open your device (ie. Directsound) the first time this function is called. + */ + virtual int ProcessSamples(char *sampledata, int bytes, int *bps, int *nch, int *srate, bool *killswitch) { return bytes; } + + /* FlushSamples + ** Flushes output buffers so that all is written + */ + virtual void FlushSamples(bool *killswitch) { }; + + /* Restart + ** Called by Winamp after a seek + */ + virtual void Restart(void) { } + + /* GetLatency + ** Returns < 0 for a final output latency, > 0 for an additive + */ + virtual int GetLatency(void) { return 0; } + + /* Pause + ** Suspends output + */ + virtual void Pause(int pause) { } + + /* ShutDown + ** Completely stops output + ** + ** Close your device here (not in destructor) + */ + virtual void ShutDown(void) { } + + /* SetVolume + ** Sets the volume (0 to 255) + ** return 1 if volume successfully modified + */ + virtual int SetVolume(int volume) { return 0; } + + /* SetPan + ** Sets Left-Right sound balance (-127 to 127) + ** return 1 if pan successfully modified + */ + virtual int SetPan(int pan) { return 0; } + + /* Notify + ** Called by Winamp to notify what's going on + */ + virtual void Notify(WOutputFilterNotifyMsg msg, int data1, int data2) { } + +}; + + + + +class WInputSource +{ + + protected: + /* WInputSource + ** WInputSource constructor + */ + WInputSource(){ }; + + + public: + /* m_player + ** Filled by Winamp. Pointer to Winamp 3 core interface + */ + WPlayer_callback *m_player; + + /* GetDescription + ** Retrieves your plug-in's text description + */ + virtual char *GetDescription() { return "Unknown"; }; + + /* UsesOutputFilters + ** Returns whether or not the Output Filter pipeline can be used + */ + virtual int UsesOutputFilters(void) { return 1; } + + /* Open + ** Used to open and prepare input media type + */ + virtual int Open(char *url, bool *killswitch)=0; + + /* GetSamples + ** This function must fill bps, nch and srate. + ** Here, you have to fill the sample_buffer with decoded data. Be sure to fill it with the specified + ** size (bytes). Use an internal buffer, etc ... + ** + ** sample_buffer: buffer to put decoded data into + ** bytes: size of the sample_buffer + ** bps: Bits par sample (8 or 16) + ** nch: Number of channels (1 or 2) + ** srate: Sample rate in Hz + ** killswitch: Will be set to 1 by winamp if stop if requested. Poll the pointed value very often to + ** make sure Winamp doesn't hang + */ + virtual int GetSamples(char *sample_buffer, int bytes, int *bps, int *nch, int *srate, bool *killswitch)=0; + + /* SetVolume + ** Sets the volume (0 to 255) + ** Return 1 if volume has been set + */ + virtual int SetVolume(int volume) { return 0; }; + + /* SetPan + ** Sets Left-Right sound balance (-127 to 127) + ** return 1 if pan successfully modified + */ + virtual int SetPan(int pan) { return 0; }; + + /* SetPosition + ** Sets position in ms. returns 0 on success, 1 if seek impossible + */ + virtual int SetPosition(int)=0; + + /* Pause + ** Suspends input + */ + virtual void Pause(int pause) { }; + + /* GetPosition + ** Retrieve position in milliseconds + */ + virtual int GetPosition(void) { return 0; } + + /* GetTitle + ** Called by Winamp to get the decoded title about the file opened + ** i.e. stream name, id3 title name, etc... + */ + virtual void GetTitle(char *buf, int maxlen) { if(maxlen>0) buf[0]=0; }; + + /* GetInfoString + ** Called by Winamp to get extra informations about the file openend + ** i.e. "32kbps 44khz", etc... + */ + virtual void GetInfoString(char *buf, int maxlen) { if(maxlen>0) buf[0]=0; }; + + /* GetLength + ** Called by Winamp to retrieves media type length in milliseconds + ** returns -1 if length is undefined/infinite + */ + virtual int GetLength(void) { return -1; } + + /* ~WInputSource + ** ~WInputSource virtual destructor + */ + //virtual ~WInputSource() { }; + virtual void Release(int)=0; +}; + + + + +class WReader +{ + protected: + + /* WReader + ** WReader constructor + */ + WReader() { } + + public: + + /* m_player + ** Filled by Winamp. Pointer to Winamp 3 core interface + */ + WPlayer_callback *m_player; + + /* GetDescription + ** Retrieves your plug-in's text description + */ + virtual char *GetDescription() { return "Unknown"; }; + + /* Open + ** Used to open a file, return 0 on success + */ + virtual int Open(char *url, bool *killswitch)=0; + + /* Read + ** Returns number of BYTES read (if < length then eof or killswitch) + */ + virtual int Read(char *buffer, int length, bool *killswitch)=0; + + /* GetLength + ** Returns length of the entire file in BYTES, return -1 on unknown/infinite (as for a stream) + */ + virtual int GetLength(void)=0; + + /* CanSeek + ** Returns 1 if you can skip ahead in the file, 0 if not + */ + virtual int CanSeek(void)=0; + + /* Seek + ** Jump to a certain absolute position + */ + virtual int Seek(int position, bool *killswitch)=0; + + /* GetHeader + ** Retrieve header. Used in read_http to retrieve the HTTP header + */ + virtual char *GetHeader(char *name) { return 0; } + + /* ~WReader + ** WReader virtual destructor + */ + //virtual ~WReader() { } + virtual void Release(int)=0; +}; + + + + +/* DLL PLUGINS EXPORT STRUCTURES */ + +#define READ_VER 0x100 +#define IN_VER 0x100 +#define OF_VER 0x100 + + +typedef struct +{ + /* version + ** Version revision number + */ + int version; + + /* description + ** Text description of the reader plug-in + */ + char *description; + + /* create + ** Function pointer to create a reader module + */ + WReader *(*create)(); + + /* ismine + ** Determines whether or not a file should be read by this plug-in + */ + int (*ismine)(char *url); + +} reader_source; + + + + +typedef struct +{ + /* version + ** Version revision number + */ + int version; + + /* description + ** Text description of the input plug-in + */ + char *description; + + /* extension_list + ** Defines all the supported filetypes by this input plug-in + ** In semicolon delimited format ("ext;desc;ext;desc" etc). + */ + char *extension_list; + + /* ismine + ** called before extension checks, to allow detection of tone://,http://, etc + ** Determines whether or not a file type should be decoded by this plug-in + */ + int (*ismine)(char *filename); + + /* create + ** Function pointer to create a decoder module + */ + WInputSource *(*create)(void); + + /* createinfo + ** Function pointer to create a decoder module information + */ + WInputInfo *(*createinfo)(void); + +} input_source; + + + +typedef struct +{ + /* version + ** Version revision number + */ + int version; + + /* description + ** Text description of the output plug-in + */ + char *description; + + /* create + ** Function pointer to create an Output Filter + */ + WOutputFilter *(*create)(); + +} output_filter; + + +#endif
\ No newline at end of file diff --git a/Src/Plugins/Input/in_vorbis/decoder.cpp b/Src/Plugins/Input/in_vorbis/decoder.cpp new file mode 100644 index 00000000..54cd364e --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/decoder.cpp @@ -0,0 +1,459 @@ +#include "main.h" +#include "decoder.h" +#include <math.h> +#include <locale.h> +#pragma warning(disable:4244) +#include "shaper.h" +#include "api__in_vorbis.h" + +Decoder::~Decoder() {if (shaper) delete shaper;} + +extern CfgInt + cfg_mc6_dm, cfg_mc6_map; +/* +if (vorbis_cfg.use_hq_preamp) +{ + sample *= pow(10., preamp_db/20); + + //hard 6dB limiting + if (sample < -0.5) + sample = tanh((sample + 0.5) / (1-0.5)) * (1-0.5) - 0.5; + else if (sample > 0.5) + sample = tanh((sample - 0.5) / (1-0.5)) * (1-0.5) + 0.5; +} */ + +#if 0 +static float q_tanh(float x) +{ + double foo1, foo2; + foo1 = pow(2.71828182845904523536028747135266, x); + foo2 = 1.0 / foo1; + return (foo1 -foo2) / (foo1 + foo2); +} +#else +#define q_tanh tanh +#endif + +float VorbisFile::GetGain() +{ + float peak; + + vorbis_comment * c; + float scale = 1.0f; + c = ov_comment(&vf, -1); + peak = 0.99f; + if (c) + { + if (AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain", false)) + { + char * _peak = 0, *_gain = 0; + float gain = 0; + bool have_rg = 0; + float lwing_gain = 0; + char *gain1 = 0, *gain2 = 0, *peak1 = 0, *peak2 = 0; + gain1 = vorbis_comment_query(c, "replaygain_album_gain", 0); + if (!gain1) gain1 = vorbis_comment_query(c, "rg_audiophile", 0); + gain2 = vorbis_comment_query(c, "replaygain_track_gain", 0); + if (!gain2) gain2 = vorbis_comment_query(c, "rg_radio", 0); + + peak1 = vorbis_comment_query(c, "replaygain_album_peak", 0); + peak2 = vorbis_comment_query(c, "replaygain_track_peak", 0); + switch (AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"replaygain_source", 0)) + { + case 0: // track + _gain = gain2; + if (!_gain && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false)) + _gain = gain1; + _peak = peak2; + if (!_peak && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false)) + _peak = peak1; + break; + case 1: // album + _gain = gain1; + if (!_gain && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false)) + _gain = gain2; + _peak = peak1; + if (!_peak && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false)) + _peak = peak2; + break; + } + + if (!_peak) + { + _peak = vorbis_comment_query(c, "rg_peak", 0); + } + + _locale_t C_locale = WASABI_API_LNG->Get_C_NumericLocale(); + + if (_peak) peak = _atof_l(_peak, C_locale); + if (_gain) gain = _atof_l(_gain, C_locale); + + if (!_peak && !_gain) + { + char * l = vorbis_comment_query(c, "lwing_gain", 0); + if (l) + { + lwing_gain = _atof_l(l, C_locale); + have_rg = 1; + } + } + else have_rg = 1; + + if (!have_rg) + { + gain = AGAVE_API_CONFIG->GetFloat(playbackConfigGroupGUID, L"non_replaygain", -6.0f); + } + + scale = powf(10, (gain) / 20.0f); + if (lwing_gain) + scale *= lwing_gain; + else if (have_rg) + switch (AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"replaygain_mode", 1)) + { + case 1: // apply gain, but don't clip + if (scale*peak > 1.0) scale = 1.0 / peak; + break; + case 2: // normalize + scale = 1.0 / peak; + break; + case 3: // no clipping + if (peak > 1.0f) + scale = 1.0 / peak; + break; + } + } + } + + + return scale; +} + +void Decoder::process_rg() +{ + scale = file->GetGain(); +} + +void Decoder::setup_mc() +{ + if (AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"mono", false)) + nch = 1; + else if (src_nch == 6) + { + switch (cfg_mc6_dm) + { + case 2: + nch = 4; + break; + case 3: + case 4: + nch = 2; + break; + case 5: + nch = 1; + break; + } + + if (nch > 2 && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"surround", true)) + nch = 2; + } +} + +void Decoder::Flush() +{ + bptr = 0; + pcmbuf = 0; + data = 0; + pos = 0; + if (shaper) {delete shaper;shaper = 0;} +} + +void Decoder::Init(VorbisFile * f, UINT _bits, UINT _nch, bool _useFloat, bool allowRG) +{ + useFloat = _useFloat; + + file = f; + vorbis_info * i = ov_info(&file->vf, -1); + + if (allowRG) + process_rg(); + else + scale = 1.0f; + + if (useFloat) + { + dither = false; + bps = 32; + } + else + { + dither = AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"dither", true); + + if (_bits) + bps = _bits; + else + bps = AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"bits", 16); + } + + if (useFloat) + { + clipmin = -10000; // some arbitrarily large number + clipmax = 10000; // some arbitrarily large number + } + else + { + clipmin = - (1 << (bps - 1)); + clipmax = (1 << (bps - 1)) - 1; + } + sr = i->rate; + nch = src_nch = i->channels; + Flush(); + cur_link = file->vf.current_link; + + if (_nch) + nch = _nch; + else + setup_mc(); +} + +UINT Decoder::DataAvailable() +{ + return data * (bps >> 3); +} + +int Decoder::DoFrame() +{ + need_reopen = 0; + while (1) + { + data = ov_read_float(&file->vf, &pcmbuf, 576, 0); + if ((int)data <= 0) + { + if (data == OV_HOLE) {continue;} + data = 0; + return 0; + } + break; + } + + pos = 0; + if (cur_link != file->vf.current_link) + { + vorbis_info* i = ov_info(&file->vf, -1); + if (sr != (UINT)i->rate || src_nch != (UINT)i->channels) + { + UINT old_nch = nch, old_sr = sr; + if (shaper) {delete shaper;shaper = 0;} + sr = i->rate; + src_nch = nch = i->channels; + setup_mc(); + if (nch != old_nch || sr != old_sr) + { + need_reopen = 1; + } + } + process_rg(); + cur_link = file->vf.current_link; + } + data *= nch; + return 1; +} + +int Decoder::Read(UINT bytes, void * buf) +{ + UINT wr = 0; + if (buf && bytes && data > 0) + { + char* out = (char*)buf; + UINT d; + double mul; + int ofs; + float * img; + + d = bytes / (bps >> 3); + if (d > data) d = data; + if (!d) return 0; + data -= d; + if (useFloat) + { + mul = 1.0; + ofs = 0; + } + else + { + mul = (double)( (1 << ((bps) - 1)) - 1 ); + ofs = (bps == 8) ? 0x80 : 0; + } + wr += d * (bps >> 3); + + img = (float*)alloca(sizeof(float) * nch); + do + { + UINT cur_ch; + if (nch == 1 && src_nch > 0) + { + UINT c; + img[0] = 0; + for (c = 0;c < src_nch;c++) + { + img[0] += pcmbuf[c][pos]; + } + img[0] /= (float)src_nch; + } + else if (nch == src_nch && !(nch == 6 && cfg_mc6_dm == 1)) + { + UINT c; + for (c = 0;c < nch;c++) + { + img[c] = pcmbuf[c][pos]; + } + } + else if (src_nch == 6) + { + UINT FL, FR, C; + if (cfg_mc6_map == 1) + { + FL = 0; + FR = 1; + C = 2; + } + else + { + FL = 0; + C = 1; + FR = 2; + } + + if (nch == 6) + { //remap order for correct 5.1 output + img[0] = pcmbuf[FL][pos]; + img[1] = pcmbuf[FR][pos]; + img[2] = pcmbuf[C][pos]; + img[3] = pcmbuf[5][pos]; + img[4] = pcmbuf[3][pos]; + img[5] = pcmbuf[4][pos]; + } + else if (nch == 2) + { + /* + FL FR C BL BR LFE + 0 1 2 3 4 5 + + L,C,R,SL,SR,LFE + 0 1 2 3 4 5 + + + output: + FL FR C LFE BL BR + + + stereo: + Lt=L+0.707*(V-SL-SR+LFE) + Rt=R+0.707*(C+SL+SR+LFE) + + + Lt=L+0.707*(C+LFE) + Rt=R+0.707*(C+LFE) + SLt=SL + SRt=SR + + */ + if (cfg_mc6_dm == 4) //ds2 + { + const double a = pow(10., 1.5 / 20.), b = 1 / a; + img[0] = pcmbuf[FL][pos] + 0.707 * (pcmbuf[C][pos] - a * pcmbuf[3][pos] - b * pcmbuf[4][pos] + pcmbuf[5][pos]); + img[1] = pcmbuf[FR][pos] + 0.707 * (pcmbuf[C][pos] + b * pcmbuf[3][pos] + a * pcmbuf[4][pos] + pcmbuf[5][pos]); + } + else + { + img[0] = pcmbuf[FL][pos] + 0.707 * (pcmbuf[C][pos] - pcmbuf[3][pos] - pcmbuf[4][pos] + pcmbuf[5][pos]); + img[1] = pcmbuf[FR][pos] + 0.707 * (pcmbuf[C][pos] + pcmbuf[3][pos] + pcmbuf[4][pos] + pcmbuf[5][pos]); + } + } + else if (nch == 4) + { + img[0] = pcmbuf[FL][pos] + 0.707 * (pcmbuf[C][pos] + pcmbuf[5][pos]); + img[1] = pcmbuf[FR][pos] + 0.707 * (pcmbuf[C][pos] + pcmbuf[5][pos]); + img[2] = pcmbuf[3][pos]; + img[3] = pcmbuf[4][pos]; + } + } + + for (cur_ch = 0;cur_ch < nch;cur_ch++) + { + float v = img[cur_ch]; + int val; + v *= scale; + v *= mul; + if (dither) + { + if (!shaper) + { + //Shaper(int freq,int _nch,int min,int max,int _dtype,int pdf,double noiseamp); + shaper = new Shaper(sr, nch, clipmin, clipmax, 2, DITHER_TRIANGLE, 0); + } + // double peak=0; + val = shaper->do_shaping(v /*,&peak*/, cur_ch); + //shaper clips for us + } + else + { + val = (int)v; + if (val < clipmin) val = clipmin; + else if (val > clipmax) val = clipmax; + //1<<16 = 0x10000 + + } + val += ofs; + + switch (bps) + { + case 8: + *(BYTE*)out = (UINT)val; + break; + case 16: + *(short*)out = val; + break; + case 24: + { + ((BYTE*)out)[0] = (UINT)val; + ((BYTE*)out)[1] = (UINT)val >> 8; + ((BYTE*)out)[2] = (UINT)val >> 16; + } + break; + case 32: + if (useFloat) + { + *(float *)out = v; + } + else + { + //*(long*)out=val; + //break; + *(long*)out = 0; + } + break; + }; + out += (bps >> 3); + d--; + } + pos++; + } + while (d); + + } + return wr; +} + +int VorbisFile::Seek(double p) { return ov_time_seek(&vf, p);} + +int Decoder::Seek(double p) +{ + Flush(); + return file->Seek(p); +} + +//char *vorbis_comment_query(vorbis_comment *vc, char *tag, int count) +const char* VorbisFile::get_meta(const char* tag, UINT c) +{ + return vorbis_comment_query(vf.seekable ? vf.vc + vf.current_link : vf.vc, (char*)tag, c); +}
\ No newline at end of file diff --git a/Src/Plugins/Input/in_vorbis/decoder.h b/Src/Plugins/Input/in_vorbis/decoder.h new file mode 100644 index 00000000..aa2b8c01 --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/decoder.h @@ -0,0 +1,41 @@ +class Shaper; + +class Decoder +{ +private: + void process_rg(); + void setup_mc(); + float* bptr; + float** pcmbuf; + Shaper * shaper; + UINT data,pos; + float scale; + int cur_link; + int cur_preamp; + int clipmin,clipmax; +public: + VorbisFile * file; + + UINT nch,sr,kbps,bps,src_nch; + + Decoder() + { + memset(this,0,sizeof(*this)); + } + + ~Decoder(); + + int Seek(double p); + int Read(UINT bytes,void * buf); + void Flush(); + void Init(VorbisFile * f, UINT _bits=0, UINT _nch=0, bool _useFloat=false, bool allowRG=true); + void wa2_setinfo(UINT cur_bitrate); + + UINT DataAvailable(); + int DoFrame(); + bool need_reopen; + int play_init(); + bool play_inited; + bool dither; + bool useFloat; +};
\ No newline at end of file diff --git a/Src/Plugins/Input/in_vorbis/genres.c b/Src/Plugins/Input/in_vorbis/genres.c new file mode 100644 index 00000000..3f71c504 --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/genres.c @@ -0,0 +1,97 @@ +#include <windows.h> +#include "genres.h" +#include <shlwapi.h> + +extern const wchar_t *INI_DIRECTORY; + +static void file_init(wchar_t *file_path, wchar_t *fn) +{ + PathCombineW(file_path, INI_DIRECTORY, fn); +} + +static char eol[2]={13,10}; + +static char get_char(HANDLE f,BOOL * eof) +{ + DWORD br=0; + char r=0; + ReadFile(f,&r,1,&br,0); + if (!br) *eof=1; + return r; +} + +void genres_read(HWND wnd, wchar_t* fn) +{ + char temp[MAX_GENRE] = {0}; + char add[MAX_GENRE] = {0}; + BOOL eof=0; + char c = 0; + wchar_t file_path[MAX_PATH] = {0}; + HANDLE f; + + file_init(file_path, fn); + + f = CreateFileW(file_path, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + if (f==INVALID_HANDLE_VALUE) return; + GetWindowTextA(wnd,add,MAX_GENRE); + while(!eof) + { + UINT ptr=0; + BOOL start=1; + while(ptr<MAX_GENRE-1) + { + c=get_char(f,&eof); + if (eof) break; + if (c==10 || c==13) + { + if (start) continue; + else break; + } + start=0; + temp[ptr++]=c; + } + if (ptr) + { + temp[ptr]=0; + SendMessage(wnd,CB_ADDSTRING,0, (LPARAM)temp); + if (add[0]) + { + if (!_stricmp(add,temp)) add[0]=0; + } + } + } + CloseHandle(f); + if (add[0]) SendMessage(wnd,CB_ADDSTRING,0,(LPARAM)add); +} + +void genres_write(HWND wnd, wchar_t* fn) +{ + wchar_t file_path[MAX_PATH] = {0}; + char temp[MAX_GENRE] = {0}; + UINT max = 0,n = 0; + DWORD bw = 0; + HANDLE f; + { + char add[MAX_GENRE] = {0}; + GetWindowTextA(wnd,add,MAX_GENRE); + if (!add[0]) return; + max=(UINT)SendMessage(wnd,CB_GETCOUNT,0,0); + for(n=0;n<max;n++) + { + SendMessage(wnd,CB_GETLBTEXT,n,(LPARAM)temp); + if (!_stricmp(temp,add)) return; + } + SendMessage(wnd,CB_ADDSTRING,0,(LPARAM)add); + } + file_init(file_path, fn); + f = CreateFileW(file_path, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0); + if (f==INVALID_HANDLE_VALUE) return; + max=(UINT)SendMessage(wnd,CB_GETCOUNT,0,0); + for(n=0;n<max;n++) + { + SendMessage(wnd,CB_GETLBTEXT,n,(LPARAM)temp); + bw = 0; WriteFile(f,temp,(DWORD)strlen(temp),&bw,0); + bw = 0; WriteFile(f,eol,2,&bw,0); + } + CloseHandle(f); +}
\ No newline at end of file diff --git a/Src/Plugins/Input/in_vorbis/genres.h b/Src/Plugins/Input/in_vorbis/genres.h new file mode 100644 index 00000000..ad840701 --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/genres.h @@ -0,0 +1,16 @@ +#ifndef NULLSOFT_IN_VORBIS_GENRES_H +#define NULLSOFT_IN_VORBIS_GENRES_H + +#ifdef __cplusplus +extern "C" { +#endif + +void genres_read(HWND wnd, wchar_t* fn); +void genres_write(HWND wnd, wchar_t* fn); +#define MAX_GENRE 256 + +#ifdef __cplusplus +} +#endif + +#endif
\ No newline at end of file diff --git a/Src/Plugins/Input/in_vorbis/http.cpp b/Src/Plugins/Input/in_vorbis/http.cpp new file mode 100644 index 00000000..87998084 --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/http.cpp @@ -0,0 +1,596 @@ +#include "api__in_vorbis.h" +#include "../../..\Components\wac_network\wac_network_http_receiver_api.h" +#include "rf.h" +#include "main.h" +#include "../Winamp/wa_ipc.h" +#include <api/service/waservicefactory.h> +#include "../nu/AutoWide.h" +#include "../nu/AutoChar.h" +extern CfgInt cfg_fix0r,cfg_httpseek2,cfg_proxy_mode,cfg_prebuf1,cfg_prebuf2,cfg_fsave,cfg_http_bsize; + +#define CANSEEK + +WORD *wdup(const char * src);//info.c + +#define zeromem(x) memset(&x,0,sizeof(x)) + +class StreamSave +{ + private: + ogg_sync_state oy_src; + ogg_stream_state os_src; + ogg_stream_state os_dst; + ogg_page og_src; + ogg_page og_dst; + ogg_packet op; + StringW tmp_fn; + BOOL is_temp; + BOOL got_streams,got_delta,use_fix0r; + ogg_int64_t pcm_delta; + int packets,serial; + HANDLE hFile; + public: + + StreamSave() + { + zeromem(oy_src); + zeromem(os_src); + zeromem(os_dst); + zeromem(og_src); + zeromem(og_dst); + zeromem(op); + got_streams=0; + got_delta=0; + pcm_delta=0; + hFile=0; + packets=0; + serial=0; + is_temp=1; + + tmp_fn=cfg_dumpdir; + if (tmp_fn[tmp_fn.Length()-1]!='\\') tmp_fn.AddChar('\\'); + + tmp_fn+=StringPrintfW(L"oggtemp%u.foo",GetTickCount64()&0xFFFF); + + hFile=CreateFileW(tmp_fn,GENERIC_WRITE,0,0,CREATE_ALWAYS,FILE_ATTRIBUTE_HIDDEN,0); + if (hFile==INVALID_HANDLE_VALUE) hFile=0; + else + { + ogg_sync_init(&oy_src); + use_fix0r=cfg_fix0r; + } + }; + + void Write(void * ptr,UINT siz) + { + if (!hFile) return; + + void * b=ogg_sync_buffer(&oy_src,siz); + memcpy(b,ptr,siz); + ogg_sync_wrote(&oy_src,siz); + + while(ogg_sync_pageout(&oy_src,&og_src)>0) + { + if (!got_streams) + { + serial=ogg_page_serialno(&og_src); + ogg_stream_init(&os_src,serial); + ogg_stream_init(&os_dst,serial); + got_streams=1; + packets=0; + got_delta=0; + } + else if (serial!=ogg_page_serialno(&og_src)) + { + if (got_streams) + { + /*while(ogg_stream_flush(&os_dst,&og_dst)) + { + write_page(dst,&og_dst,&wb); + }*/ + + ogg_stream_clear(&os_src); + ogg_stream_clear(&os_dst); + } + serial=ogg_page_serialno(&og_src); + ogg_stream_init(&os_src,serial); + ogg_stream_init(&os_dst,serial); + + packets=0; + got_delta=0; + } + + ogg_stream_pagein(&os_src,&og_src); + while(ogg_stream_packetout(&os_src,&op)>0) + { + if (use_fix0r && !got_delta && packets>2 && op.granulepos>=0) //hack to fix saved streams + { + got_delta=1; + if (op.granulepos>4096*(packets-2)) pcm_delta=op.granulepos; + } + if (got_delta) + { + if (op.granulepos>=pcm_delta) op.granulepos-=pcm_delta; + else if (op.granulepos>0) op.granulepos=0; + } + ogg_stream_packetin(&os_dst,&op); + packets++; + } + + while((packets==3 ? ogg_stream_flush(&os_dst,&og_dst) : ogg_stream_pageout(&os_dst,&og_dst))>0) + { + DWORD bw = 0; + WriteFile(hFile,og_dst.header,og_dst.header_len,&bw,0); + bw = 0; WriteFile(hFile,og_dst.body,og_dst.body_len,&bw,0); + } + } + } + + void FixName(VorbisFile * vf,const char * streamname) + { + if (!hFile) return; + CloseHandle(hFile); + StringW fn(cfg_dumpdir); + + if (fn[fn.Length()-1]!='\\') fn.AddChar('\\'); + + UINT n=fn.Length(); + fn+=(wchar_t *)AutoWide(vf->get_meta("TITLE", 0), CP_UTF8); + UINT m=fn.Length(); + + while(n<m) + { + char * b="/\\:*?\"<>|"; + while(b && *b) + { + if (fn[n]==*b) {fn.SetChar(n,'_');break;} + b++; + } + n++; + }; + fn.AddStringA(".ogg"); + if (!MoveFileW(tmp_fn,fn)) + { + DeleteFileW(fn); + MoveFileW(tmp_fn,fn); + } + SetFileAttributesW(fn,FILE_ATTRIBUTE_NORMAL); + hFile=CreateFileW(fn,GENERIC_WRITE,0,0,OPEN_EXISTING,0,0); + + if (hFile==INVALID_HANDLE_VALUE) {hFile=0;} + else SetFilePointer(hFile,0,0,FILE_END); + is_temp=0; + } + + ~StreamSave() + { + if (hFile) + { + /*if (got_streams) + { + while(ogg_stream_flush(&os_dst,&og_dst)) + { + write_page(dst,&og_dst,&wb); + } + }*/ + + ogg_stream_clear(&os_src); + ogg_stream_clear(&os_dst); + + SetFilePointer(hFile,0,0,FILE_CURRENT); + CloseHandle(hFile); + if (is_temp) DeleteFileW(tmp_fn); + } + ogg_sync_clear(&oy_src); + } +}; + +static const char * do_proxy(const char * url) +{ + switch(cfg_proxy_mode) + { + default: + return 0; + case 1: + { + const char * p=strstr(url,"://"); + if (!p) p=url; + while(p && *p && *p!=':' && *p!='/') p++; + if (p && *p==':') + { + if (atoi(p+1)!=80) return 0; + } + } + case 2: + { + char *x = (char *)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GET_PROXY_STRING); + if (x == (char *)1 || !x || !*x) + return 0; + return x; + } + } +} + +class VorbisFile_HTTP : public VorbisFile +{ + protected: + api_httpreceiver *get; + UINT bsize; + uint64_t len; + UINT pos; + UINT seekpos; + BOOL can_seek; + StreamSave * saver; + virtual void Idle(); + virtual int f_seek(__int64 offset,int whence); + virtual size_t f_read(UINT siz,void * ptr); + virtual UINT f_tell(); + virtual UINT FileSize() {return len;} + bool is_live; + + public: + virtual int GetType() {return TYPE_HTTP;} + virtual bool IsLive() {return is_live;} + bool http_init(); + + void do_prebuf() {VorbisFile::do_prebuf();fillbuf(bsize * cfg_prebuf1 / 100,0);} + + VorbisFile_HTTP(UINT s, const wchar_t *u,bool is_info, bool hasauth) : VorbisFile(u,is_info), usedauth(hasauth) + { + get=0; + can_seek=0; + len=pos=seekpos=0; + bsize=s; + saver=0; + m_needs_auth=0; + lpinfo[0]=0; + force_lpinfo[0]=0; + is_live = false; + memset(dlg_realm, 0, sizeof(dlg_realm)); + } + + ~VorbisFile_HTTP() + { + if (get) + { + waServiceFactory *sf = mod.service->service_getServiceByGuid(httpreceiverGUID); + if (sf) + sf->releaseInterface(get); + get=0; + } + if (saver) delete saver; + } + + void fillbuf(UINT max,bool shutup); + + size_t _http_read(char* ptr,size_t total); + int reconnect(UINT ofs); + virtual void post_init() + { + if (saver) saver->FixName(this,get->getheader("ice-name")); + } + + static BOOL CALLBACK httpDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam); + int m_needs_auth; + char dlg_realm[256]; + char lpinfo[256]; + char force_lpinfo[256]; + bool usedauth; +}; + +int VorbisFile_HTTP::reconnect(UINT ofs) +{ + // get.reset_headers(); + get->addheader("User-Agent: WinampOGG/5.24(MPEG stream compatible)"); + get->addheader("Accept:*/*"); + if (ofs>0) get->addheader(StringPrintf("Range: bytes=%u-",ofs)); + get->connect(AutoChar(url)); + + Status(WASABI_API_LNGSTRINGW(IDS_CONNECTING)); + + int st=get->run(); + if (st<0) + { + return 1; + } + return 0; +} + +void VorbisFile_HTTP::fillbuf(UINT max,bool shutup) +{ + if (len>0 && pos+max>len) max=len-pos; + while(!Aborting() && !abort_prebuf) //stop prebuffering if we wanna seek + { + if (!shutup) + { + Status(StringPrintfW(WASABI_API_LNGSTRINGW(IDS_PREBUFFERING), get->bytes_available()*100/bsize)); + } + if (get->run()) break; + if (Aborting() || abort_prebuf || get->bytes_available()>=(int)max) break; + Sleep(2); + } + if (!shutup) + { + Status(0); + } +} + +size_t VorbisFile_HTTP::_http_read(char* ptr,size_t total) +{ +#ifdef CANSEEK + if (seekpos!=-1) + { + UINT sp=seekpos; + seekpos=-1; + if (sp!=pos) + { + if (sp>pos && sp<=pos+get->bytes_available()) + { + get->get_bytes(0,sp-pos); + } + else + { + if (reconnect(sp)) + { + return 0;//oh well... + } + } + pos=sp; + } + } +#endif + UINT wr=0; + while(!Aborting() && wr<total) + { + int st=get->run(); + int d=get->get_bytes(ptr,(int)total-wr); + if (st && !d) break; + wr+=d; + ptr+=d; + pos+=d; + if ((len>0 && pos>=len) || wr>=total || Aborting()) break; + if (use_prebuf) fillbuf(bsize * cfg_prebuf2 / 100,0); + else Sleep(1); + } + return wr; +} + +void VorbisFile_HTTP::Idle() +{ + get->run(); + Sleep(1); + get->run(); + Sleep(1); +} + +size_t VorbisFile_HTTP::f_read(UINT siz,void* ptr) +{ + if (Aborting()) return 0;//fixme + int i=(int)_http_read((char*)ptr,siz); + if (i>0 && saver) saver->Write(ptr,i); + return i; +} + +int VorbisFile_HTTP::f_seek(ogg_int64_t offset,int whence) +{ +#ifdef CANSEEK + if (can_seek) + { + switch(whence) + { + case FILE_BEGIN: + seekpos=(int)offset; + break; + case FILE_END: + seekpos=len+(int)offset; + break; + case FILE_CURRENT: + seekpos=pos+(int)offset; + break; + } + if (seekpos>len) seekpos=len; + return 0; + } + else +#endif + return -1; +} + +UINT VorbisFile_HTTP::f_tell() +{ +#ifdef CANSEEK + if (can_seek) + { + if (seekpos!=-1) return seekpos; + else return pos; + } + else +#endif + return -1; +} + +HWND GetDialogBoxParent() +{ + HWND parent = (HWND)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GETDIALOGBOXPARENT); + if (!parent || parent == (HWND)1) + return mod.hMainWindow; + return parent; +} + +bool VorbisFile_HTTP::http_init() +{ + if (mod.service) + { + waServiceFactory *sf = mod.service->service_getServiceByGuid(httpreceiverGUID); + if (sf) get = (api_httpreceiver *)sf->getInterface(); + } + if (!get) return false; + get->open(API_DNS_AUTODNS, bsize, do_proxy(AutoChar(url))); + + if (reconnect(0)) + { + return 0; + } + +#ifdef CANSEEK + // if (cfg_httpseek) + { + //need to get http headers first + while(!memcmp(get->getallheaders(),"\0\0",2)) + { + if (get->run()<0 || Aborting()) + { + int reply = get->getreplycode(); + if ( reply == 401 ) + { + api_connection *mcon=get->GetConnection(); + if ( mcon && mcon->GetReceiveBytesAvailable()) + { + char p[1024]=""; + while ( mcon->GetReceiveBytesAvailable() ) + { + char b[2048]=""; + mcon->ReceiveLine(b,2048); + if ( *b ) + { + char *t= strstr(b,"WWW-Authenticate:"); + if ( t && *t ) + { + char *y = strstr(t,"\""); + if ( y && *y ) + { + y++; + if ( *y ) + { + char *u = strstr(y,"\""); + if ( u && *u ) + { + *u = 0; + wsprintfA(p,"%s",y); + } + } + } + } + } + } + + if ( *p ) // found our realm + { + if (!force_lpinfo[0]) GetPrivateProfileStringA("HTTP-AUTH",p,"",force_lpinfo,sizeof(force_lpinfo),INI_FILE); + if (!force_lpinfo[0] || lpinfo[0] || usedauth ) + { + lstrcpynA(dlg_realm,p,sizeof(dlg_realm)); + if (!WASABI_API_DIALOGBOXPARAM(IDD_HTTPAUTH, GetDialogBoxParent(), httpDlgProc, (LPARAM)this)) + { + force_lpinfo[0]=0; + } + else + { + WritePrivateProfileStringA("HTTP-AUTH",p,force_lpinfo,INI_FILE); + } + } + Status(WASABI_API_LNGSTRINGW(IDS_AUTH_REQUIRED)); + m_needs_auth=1; + } + } + } + return 0; + } + //hg->get.wait(10); + Sleep(1); + } + len=get->content_length(); + const char* poo=get->getheader("icy-name"); + if (poo) stream_title=poo; + if (cfg_httpseek2 && len) can_seek=1; + is_live=(len<=0); + } +#endif + + //if (hg->len==0 || hg->len==-1) hg->can_seek=0; + seekpos=-1; + + if (cfg_fsave && !can_seek) + { + saver=new StreamSave; + } + return 1; +} + +VorbisFile * VorbisFile::Create_HTTP(const char * url,bool is_info) +{ + VorbisFile_HTTP * r=new VorbisFile_HTTP(cfg_http_bsize,AutoWide(url),is_info, false); + if (r) + { + if (!r->http_init()) + { + int trys=0; + while ( r && r->m_needs_auth && trys++ < 2) + { + const char *p=strstr(url,"://"); + if (p && *p) + { + p += 3; + if (p && *p) + { + char lurl[4096] = {0}; + wsprintfA(lurl, "http://%s@%s", r->force_lpinfo, p); + delete r; + r = new VorbisFile_HTTP(cfg_http_bsize,AutoWide(lurl),is_info, true); + if (r && r->http_init()) + { + return r; + } + } + } + } + delete r; + r=0; + } + } + return r; +} + +BOOL CALLBACK VorbisFile_HTTP::httpDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam) +{ + VorbisFile_HTTP *_this; + switch (uMsg) + { + case WM_INITDIALOG: +#ifdef WIN64 + SetWindowLong(hwndDlg, GWLP_USERDATA, (LONG)lParam); + _this = (VorbisFile_HTTP*)(GetWindowLong(hwndDlg, GWLP_USERDATA)); +#else + SetWindowLong(hwndDlg, GWL_USERDATA, (LONG)lParam); + _this = (VorbisFile_HTTP*)GetWindowLong(hwndDlg, GWL_USERDATA); +#endif + if (_this->force_lpinfo[0]) + SetDlgItemTextA(hwndDlg,IDC_EDITAUTH,_this->force_lpinfo); + else SetDlgItemTextA(hwndDlg,IDC_EDITAUTH,_this->lpinfo); + SetDlgItemTextA(hwndDlg,IDC_REALM,_this->dlg_realm); + return 1; + + case WM_COMMAND: +#ifdef WIN64 + _this = (VorbisFile_HTTP*)GetWindowLong(hwndDlg, GWLP_USERDATA); +#else + _this = (VorbisFile_HTTP*)GetWindowLong(hwndDlg, GWL_USERDATA); +#endif + if (LOWORD(wParam) == IDOKAUTH) + { + char *p; + GetDlgItemTextA(hwndDlg,IDC_EDITAUTH,_this->force_lpinfo,sizeof(_this->force_lpinfo)); + p = strstr(_this->force_lpinfo,"\r"); + if ( p && *p ) *p=0; + p = strstr(_this->force_lpinfo,"\n"); + if ( p && *p ) *p=0; + EndDialog(hwndDlg,1); + } + else if (LOWORD(wParam) == IDCANCELAUTH) + { + EndDialog(hwndDlg,0); + } + break; + } + return 0; +}
\ No newline at end of file diff --git a/Src/Plugins/Input/in_vorbis/in_vorbis.rc b/Src/Plugins/Input/in_vorbis/in_vorbis.rc new file mode 100644 index 00000000..a8d4b5e3 --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/in_vorbis.rc @@ -0,0 +1,415 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Polish resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_PLK) +#ifdef _WIN32 +LANGUAGE LANG_POLISH, SUBLANG_DEFAULT +#pragma code_page(1250) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "#include ""version.rc2""\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // Polish resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_ABOUT DIALOGEX 0, 0, 173, 113 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + CONTROL "",IDC_CUSTOM1,"XIPH_CLASS",0x0,6,8,44,40 + CTEXT "",IDC_RPM,7,56,45,8 + CTEXT "",IDC_RPM2,4,64,51,8 + LTEXT "",IDC_ABOUT_TEXT,58,8,110,85 + DEFPUSHBUTTON "OK",IDCANCEL,117,93,50,14 +END + +IDD_HTTPAUTH DIALOGEX 0, 0, 154, 70 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Ogg Authentication" +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + LTEXT "Realm: ",IDC_STATIC,4,4,25,8 + LTEXT "",IDC_REALM,37,4,113,8 + LTEXT "Enter your login and password in the form of:\n\tlogin:password",IDC_STATIC,4,15,146,17 + EDITTEXT IDC_EDITAUTH,4,35,146,12,ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOKAUTH,4,52,50,13 + PUSHBUTTON "Cancel",IDCANCELAUTH,100,53,50,13 +END + +IDD_INFO_DLG_NEW DIALOGEX 0, 0, 329, 238 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "Ogg Vorbis Info" +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + LTEXT "File/URL :",-1,6,6,34,8 + EDITTEXT IDC_URL,43,5,282,12,ES_AUTOHSCROLL | ES_READONLY + GROUPBOX "File / Stream Info",IDC_STATIC_MISC,224,20,101,120 + EDITTEXT IDC_MISC,231,30,88,107,ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY | NOT WS_BORDER + GROUPBOX "Chained Streams",IDC_STATIC_CS,224,155,101,30 + PUSHBUTTON "<= previous",IDC_PREV_STREAM,229,167,44,13 + PUSHBUTTON "next =>",IDC_NEXT_STREAM,276,167,43,13 + CONTROL "Hide special fields",IDC_HIDE_SPEC,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,229,190,73,10 + PUSHBUTTON "",IDC_MODE_TOGGLE,228,203,93,13 + PUSHBUTTON "Close",IDCANCEL,273,220,48,13 +END + +IDD_INFO_PANEL_SIMPLE DIALOGEX 0, 0, 227, 218 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + GROUPBOX "Standard Tags",IDC_STATIC_STD,3,2,219,119 + LTEXT "Title",IDC_STATIC,29,16,14,8 + EDITTEXT IDC_TITLE,48,14,168,12,ES_AUTOHSCROLL + LTEXT "Artist",IDC_STATIC,27,32,16,8 + EDITTEXT IDC_ARTIST,48,30,168,12,ES_AUTOHSCROLL + LTEXT "Album",IDC_STATIC,24,48,19,8 + EDITTEXT IDC_ALBUM,48,46,124,12,ES_AUTOHSCROLL + LTEXT "Track:",IDC_STATIC_TRACK,175,48,21,8 + EDITTEXT IDC_TRACK,198,46,18,12,ES_AUTOHSCROLL + LTEXT "Year",IDC_STATIC,28,64,16,8 + EDITTEXT IDC_DATE,48,62,56,12,ES_AUTOHSCROLL + LTEXT "Genre",IDC_STATIC,117,64,22,8 + COMBOBOX IDC_GENRE,140,62,77,68,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "Comment",IDC_STATIC,13,80,29,8 + EDITTEXT IDC_COMMENT,48,78,169,37,ES_MULTILINE | ES_WANTRETURN | WS_VSCROLL + GROUPBOX "Other",IDC_STATIC_TAGS,3,123,219,91 + LISTBOX IDC_LIST,10,134,207,75,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP +END + +IDD_INFO_PANEL_ADVANCED DIALOGEX 0, 0, 227, 218 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + LISTBOX IDC_LIST,3,3,219,212,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP +END + +IDD_INFO DIALOGEX 0, 0, 341, 164 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + GROUPBOX "Advanced",IDC_STATIC,0,0,341,164 + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,6,13,164,143 + LTEXT "Name:",IDC_STATIC,175,13,22,8 + EDITTEXT IDC_NAME,175,23,159,14,ES_AUTOHSCROLL + LTEXT "Value:",IDC_STATIC,175,39,21,8 + EDITTEXT IDC_VALUE,175,49,159,90,ES_MULTILINE | ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL + PUSHBUTTON "Add New",IDC_BUTTON_ADD,176,142,50,13 + PUSHBUTTON "Delete",IDC_BUTTON_DEL,230,142,50,13 + PUSHBUTTON "Delete All",IDC_BUTTON_DELALL,284,142,50,13 +END + +IDD_CONFIG DIALOGEX 0, 0, 222, 261 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + GROUPBOX "Decoding",IDC_STATIC,4,4,214,93 + CONTROL "Buffer full files from disk",IDC_FULLBUF,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,17,89,10 + CONTROL "Show average bitrate while playing",IDC_AVG_BR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,31,128,10 + GROUPBOX "5.1-Channel Input Processing",IDC_STATIC,10,44,201,47 + LTEXT "Apply conversion:",IDC_STATIC,16,58,72,8,0,WS_EX_RIGHT + COMBOBOX IDC_MC6_DM,95,56,110,96,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Source channel order:",IDC_STATIC,16,75,72,8,0,WS_EX_RIGHT + COMBOBOX IDC_MC6_MAP,95,73,110,96,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + GROUPBOX "Streaming",IDC_STATIC,4,101,214,139 + LTEXT "Buffer size",IDC_STATIC,10,115,34,8 + EDITTEXT IDC_HTTP_BSIZE,48,113,28,12,ES_AUTOHSCROLL | ES_NUMBER + LTEXT "KB",IDC_STATIC,80,115,9,8 + GROUPBOX "Prebuffering:",IDC_STATIC,10,129,201,44 + LTEXT "At start:",IDC_STATIC,16,142,31,8 + CONTROL "",IDC_SLIDER1,"msctls_trackbar32",TBS_NOTICKS | WS_TABSTOP,70,141,130,10 + LTEXT "After underrun:",IDC_STATIC,16,153,54,8 + CONTROL "",IDC_SLIDER2,"msctls_trackbar32",TBS_NOTICKS | WS_TABSTOP,70,152,130,10 + LTEXT "0%",IDC_STATIC,71,161,13,8 + LTEXT "50%",IDC_STATIC,122,161,18,8 + LTEXT "100%",IDC_STATIC,185,161,20,8 + LTEXT "Use Winamp's proxy server settings:",IDC_STATIC,10,179,120,8 + COMBOBOX IDC_PROXY,134,177,77,58,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + GROUPBOX "",IDC_STATIC,10,191,201,43 + CONTROL "Save streamed files to:",IDC_FSAVE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,201,89,10 + PUSHBUTTON "default",IDC_SSAVE_FMT_DEF,165,200,40,11 + PUSHBUTTON "",IDC_STREAM_SAVE,15,215,105,13 + EDITTEXT IDC_SSAVE_FMT,120,215,85,13,ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,124,244,45,13 + PUSHBUTTON "Cancel",IDCANCEL,173,244,45,13 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_ABOUT, DIALOG + BEGIN + LEFTMARGIN, 4 + RIGHTMARGIN, 168 + TOPMARGIN, 4 + BOTTOMMARGIN, 107 + END + + IDD_HTTPAUTH, DIALOG + BEGIN + LEFTMARGIN, 4 + RIGHTMARGIN, 150 + TOPMARGIN, 4 + BOTTOMMARGIN, 66 + END + + IDD_CONFIG, DIALOG + BEGIN + LEFTMARGIN, 4 + RIGHTMARGIN, 218 + TOPMARGIN, 4 + BOTTOMMARGIN, 257 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +#if defined(APSTUDIO_INVOKED) || defined(DISABLED) +#if defined(APSTUDIO_INVOKED) +IDB_BITMAP9$(DISABLED) BITMAP "oggdrop\\120.bmp" +#else +IDB_BITMAP9 BITMAP "oggdrop\\120.bmp" +#endif +#endif +#if defined(APSTUDIO_INVOKED) || defined(DISABLED) +#if defined(APSTUDIO_INVOKED) +IDB_BITMAP8$(DISABLED) BITMAP "oggdrop\\119.bmp" +#else +IDB_BITMAP8 BITMAP "oggdrop\\119.bmp" +#endif +#endif +#if defined(APSTUDIO_INVOKED) || defined(DISABLED) +#if defined(APSTUDIO_INVOKED) +IDB_BITMAP7$(DISABLED) BITMAP "oggdrop\\118.bmp" +#else +IDB_BITMAP7 BITMAP "oggdrop\\118.bmp" +#endif +#endif +#if defined(APSTUDIO_INVOKED) || defined(DISABLED) +#if defined(APSTUDIO_INVOKED) +IDB_BITMAP6$(DISABLED) BITMAP "oggdrop\\117.bmp" +#else +IDB_BITMAP6 BITMAP "oggdrop\\117.bmp" +#endif +#endif +#if defined(APSTUDIO_INVOKED) || defined(DISABLED) +#if defined(APSTUDIO_INVOKED) +IDB_BITMAP5$(DISABLED) BITMAP "oggdrop\\116.bmp" +#else +IDB_BITMAP5 BITMAP "oggdrop\\116.bmp" +#endif +#endif +#if defined(APSTUDIO_INVOKED) || defined(DISABLED) +#if defined(APSTUDIO_INVOKED) +IDB_BITMAP4$(DISABLED) BITMAP "oggdrop\\115.bmp" +#else +IDB_BITMAP4 BITMAP "oggdrop\\115.bmp" +#endif +#endif +#if defined(APSTUDIO_INVOKED) || defined(DISABLED) +#if defined(APSTUDIO_INVOKED) +IDB_BITMAP3$(DISABLED) BITMAP "oggdrop\\114.bmp" +#else +IDB_BITMAP3 BITMAP "oggdrop\\114.bmp" +#endif +#endif +#if defined(APSTUDIO_INVOKED) || defined(DISABLED) +#if defined(APSTUDIO_INVOKED) +IDB_BITMAP2$(DISABLED) BITMAP "oggdrop\\113.bmp" +#else +IDB_BITMAP2 BITMAP "oggdrop\\113.bmp" +#endif +#endif +#if defined(APSTUDIO_INVOKED) || defined(DISABLED) +#if defined(APSTUDIO_INVOKED) +IDB_BITMAP12$(DISABLED) BITMAP "oggdrop\\123.bmp" +#else +IDB_BITMAP12 BITMAP "oggdrop\\123.bmp" +#endif +#endif +#if defined(APSTUDIO_INVOKED) || defined(DISABLED) +#if defined(APSTUDIO_INVOKED) +IDB_BITMAP11$(DISABLED) BITMAP "oggdrop\\122.bmp" +#else +IDB_BITMAP11 BITMAP "oggdrop\\122.bmp" +#endif +#endif +#if defined(APSTUDIO_INVOKED) || defined(DISABLED) +#if defined(APSTUDIO_INVOKED) +IDB_BITMAP10$(DISABLED) BITMAP "oggdrop\\121.bmp" +#else +IDB_BITMAP10 BITMAP "oggdrop\\121.bmp" +#endif +#endif +#if defined(APSTUDIO_INVOKED) || defined(DISABLED) +#if defined(APSTUDIO_INVOKED) +IDB_BITMAP1$(DISABLED) BITMAP "oggdrop\\112.bmp" +#else +IDB_BITMAP1 BITMAP "oggdrop\\112.bmp" +#endif +#endif + +///////////////////////////////////////////////////////////////////////////// +// +// PNG +// + +IDB_PNG1 PNG "oggdrop\\112.png" +IDB_PNG2 PNG "oggdrop\\113.png" +IDB_PNG3 PNG "oggdrop\\114.png" +IDB_PNG4 PNG "oggdrop\\115.png" +IDB_PNG5 PNG "oggdrop\\116.png" +IDB_PNG6 PNG "oggdrop\\117.png" +IDB_PNG7 PNG "oggdrop\\118.png" +IDB_PNG8 PNG "oggdrop\\119.png" +IDB_PNG9 PNG "oggdrop\\120.png" +IDB_PNG10 PNG "oggdrop\\121.png" +IDB_PNG11 PNG "oggdrop\\122.png" +IDB_PNG12 PNG "oggdrop\\123.png" + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_NULLSOFT_VORBIS_DECODER "Nullsoft Vorbis Decoder v%s" + 65535 "{5C5BCA4E-279E-4867-8E24-58C8B186959A}" +END + +STRINGTABLE +BEGIN + IDS_NULLSOFT_VORBIS_DECODER_OLD "Nullsoft Vorbis Decoder" + IDS_FILE_ERROR "file error" + IDS_LENGTH "Length: " + IDS_AVERAGE_BITRATE "Average bitrate: " + IDS_FILE_SIZE "File size: " + IDS_NOMINAL_BITRATE "Nominal bitrate: " + IDS_MIN_BITRATE "Min bitrate: " + IDS_MAX_BITRATE "Max bitrate: " + IDS_CHANNELS "Channels" + IDS_SAMPLING_RATE "Sampling rate" + IDS_SERIAL_NUMBER "Serial number" + IDS_VERSION "Version" +END + +STRINGTABLE +BEGIN + IDS_VENDOR "Vendor" + IDS_TO_SIMPLE_MODE "<< to simple mode" + IDS_TO_ADVANCED_MODE "to advanced mode >>" + IDS_OGG_VORBIS_INFO "Ogg Vorbis info - %s" + IDS_BEST_RPM "Best: %u RPM" + IDS_LEAVE_AS_IS "leave as is" + IDS_REMAP_6_CHANNELS "remap 6 channels" + IDS_DOWNMIX_TO_4_CHANNELS "downmix to 4 channels" + IDS_DOWNMIX_TO_2_CHANNELS_DS "downmix to 2 channels (DS)" + IDS_DOWNMIX_TO_2_CHANNELS_DS2 "downmix to 2 channels (DS2)" + IDS_DOWNMIX_TO_MONO "downmix to mono" + IDS_CORRECT_FL_FC_FR_BL_BR_LFE "correct (FL FC FR BL BR LFE)" + IDS_BROKEN_FL_FR_FC_BL_BR_LFE "broken (FL FR FC BL BR LFE)" +END + +STRINGTABLE +BEGIN + IDS_ABOUT_TEXT "%s\n© 2001-2023 Winamp SA\nWritten by: Peter Pawlowski\nBuild date: %hs\n\nThanks to:\n Craig Freer\n Tomi 'Nebularia' Jylhä-Ollila\n Jack Moffitt\n Szabolcs Péter" + IDS_TITLE_PREFERENCES "%s Preferences" + IDS_NEVER "never" + IDS_PORT_80_ONLY "port 80 only" + IDS_ALWAYS "always" + IDS_SELECT_OUTPUT_DIRECTORY "Select output directory:" +END + +STRINGTABLE +BEGIN + IDS_CONNECTING "Connecting..." + IDS_PREBUFFERING "Prebuffering : %u%%" + IDS_AUTH_REQUIRED "Authentication Required" + IDS_OGG_FILES "Ogg Vorbis Files (*.OGG;*.OGA)" + IDS_NAME "Name" + IDS_VALUE "Value" + IDS_KBPS "kbps" + IDS_HZ "Hz" + IDS_GAME_SPEED "%u RPM" + IDS_BYTES "bytes" + IDS_FAMILY_STRING "Ogg Vorbis File" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#include "version.rc2" + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Src/Plugins/Input/in_vorbis/in_vorbis.sln b/Src/Plugins/Input/in_vorbis/in_vorbis.sln new file mode 100644 index 00000000..fe0c2e6d --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/in_vorbis.sln @@ -0,0 +1,65 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29424.173 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "in_vorbis", "in_vorbis.vcxproj", "{7C58AC02-7941-42BE-8F97-9BF2064D6631}" + ProjectSection(ProjectDependencies) = postProject + {4FC28B55-2A14-43D5-86F7-201054F338A9} = {4FC28B55-2A14-43D5-86F7-201054F338A9} + {49238ED1-3146-49AB-9523-E9826EE4A0C8} = {49238ED1-3146-49AB-9523-E9826EE4A0C8} + {EC9475D2-FEE2-4F8C-9BB9-A11D5EB597C4} = {EC9475D2-FEE2-4F8C-9BB9-A11D5EB597C4} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libogg", "..\libogg\libogg.vcxproj", "{4FC28B55-2A14-43D5-86F7-201054F338A9}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vorbis_static", "..\libvorbis\win32\vorbis_static.vcxproj", "{49238ED1-3146-49AB-9523-E9826EE4A0C8}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vorbisfile_static", "..\libvorbis\win32\vorbisfile_static.vcxproj", "{EC9475D2-FEE2-4F8C-9BB9-A11D5EB597C4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7C58AC02-7941-42BE-8F97-9BF2064D6631}.Debug|Win32.ActiveCfg = Debug|Win32 + {7C58AC02-7941-42BE-8F97-9BF2064D6631}.Debug|Win32.Build.0 = Debug|Win32 + {7C58AC02-7941-42BE-8F97-9BF2064D6631}.Release|Win32.ActiveCfg = Release|Win32 + {7C58AC02-7941-42BE-8F97-9BF2064D6631}.Release|Win32.Build.0 = Release|Win32 + {7C58AC02-7941-42BE-8F97-9BF2064D6631}.Debug|x64.ActiveCfg = Debug|x64 + {7C58AC02-7941-42BE-8F97-9BF2064D6631}.Debug|x64.Build.0 = Debug|x64 + {7C58AC02-7941-42BE-8F97-9BF2064D6631}.Release|x64.ActiveCfg = Release|x64 + {7C58AC02-7941-42BE-8F97-9BF2064D6631}.Release|x64.Build.0 = Release|x64 + {49238ED1-3146-49AB-9523-E9826EE4A0C8}.Debug|Win32.ActiveCfg = Debug|Win32 + {49238ED1-3146-49AB-9523-E9826EE4A0C8}.Debug|Win32.Build.0 = Debug|Win32 + {49238ED1-3146-49AB-9523-E9826EE4A0C8}.Release|Win32.ActiveCfg = Release|Win32 + {49238ED1-3146-49AB-9523-E9826EE4A0C8}.Release|Win32.Build.0 = Release|Win32 + {49238ED1-3146-49AB-9523-E9826EE4A0C8}.Debug|x64.ActiveCfg = Debug|x64 + {49238ED1-3146-49AB-9523-E9826EE4A0C8}.Debug|x64.Build.0 = Debug|x64 + {49238ED1-3146-49AB-9523-E9826EE4A0C8}.Release|x64.ActiveCfg = Release|x64 + {49238ED1-3146-49AB-9523-E9826EE4A0C8}.Release|x64.Build.0 = Release|x64 + {EC9475D2-FEE2-4F8C-9BB9-A11D5EB597C4}.Debug|Win32.ActiveCfg = Debug|Win32 + {EC9475D2-FEE2-4F8C-9BB9-A11D5EB597C4}.Debug|Win32.Build.0 = Debug|Win32 + {EC9475D2-FEE2-4F8C-9BB9-A11D5EB597C4}.Release|Win32.ActiveCfg = Release|Win32 + {EC9475D2-FEE2-4F8C-9BB9-A11D5EB597C4}.Release|Win32.Build.0 = Release|Win32 + {EC9475D2-FEE2-4F8C-9BB9-A11D5EB597C4}.Debug|x64.ActiveCfg = Debug|x64 + {EC9475D2-FEE2-4F8C-9BB9-A11D5EB597C4}.Debug|x64.Build.0 = Debug|x64 + {EC9475D2-FEE2-4F8C-9BB9-A11D5EB597C4}.Release|x64.ActiveCfg = Release|x64 + {EC9475D2-FEE2-4F8C-9BB9-A11D5EB597C4}.Release|x64.Build.0 = Release|x64 + {4FC28B55-2A14-43D5-86F7-201054F338A9}.Debug|Win32.ActiveCfg = Debug|Win32 + {4FC28B55-2A14-43D5-86F7-201054F338A9}.Debug|Win32.Build.0 = Debug|Win32 + {4FC28B55-2A14-43D5-86F7-201054F338A9}.Release|Win32.ActiveCfg = Release|Win32 + {4FC28B55-2A14-43D5-86F7-201054F338A9}.Release|Win32.Build.0 = Release|Win32 + {4FC28B55-2A14-43D5-86F7-201054F338A9}.Debug|x64.ActiveCfg = Debug|x64 + {4FC28B55-2A14-43D5-86F7-201054F338A9}.Debug|x64.Build.0 = Debug|x64 + {4FC28B55-2A14-43D5-86F7-201054F338A9}.Release|x64.ActiveCfg = Release|x64 + {4FC28B55-2A14-43D5-86F7-201054F338A9}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {3248659F-0FFC-4CF4-A9BC-F7D10C713D5D} + EndGlobalSection +EndGlobal diff --git a/Src/Plugins/Input/in_vorbis/in_vorbis.vcxproj b/Src/Plugins/Input/in_vorbis/in_vorbis.vcxproj new file mode 100644 index 00000000..3bfe43ab --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/in_vorbis.vcxproj @@ -0,0 +1,339 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{7C58AC02-7941-42BE-8F97-9BF2064D6631}</ProjectGuid> + <RootNamespace>in_vorbis</RootNamespace> + <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <IncludePath>$(IncludePath)</IncludePath> + <LibraryPath>$(LibraryPath)</LibraryPath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <IncludePath>$(IncludePath)</IncludePath> + <LibraryPath>$(LibraryPath)</LibraryPath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + </PropertyGroup> + <PropertyGroup Label="Vcpkg"> + <VcpkgEnableManifest>false</VcpkgEnableManifest> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgConfiguration>Debug</VcpkgConfiguration> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + <VcpkgConfiguration>Debug</VcpkgConfiguration> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <IntrinsicFunctions>true</IntrinsicFunctions> + <AdditionalIncludeDirectories>.;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;WA2;TAGZ_UNICODE;UNICODE_INPUT_PLUGIN;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MinimalRebuild>false</MinimalRebuild> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> + <ObjectFileName>$(IntDir)</ObjectFileName> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <DisableSpecificWarnings>4090;4312;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <ResourceCompile> + <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <Culture>0x0415</Culture> + </ResourceCompile> + <Link> + <AdditionalDependencies>shlwapi.lib;Msimg32.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>true</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <TargetMachine>MachineX86</TargetMachine> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <MapFileName>$(IntDir)$(TargetName).map</MapFileName> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <IntrinsicFunctions>true</IntrinsicFunctions> + <AdditionalIncludeDirectories>.;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN64;_DEBUG;_WINDOWS;WA2;TAGZ_UNICODE;UNICODE_INPUT_PLUGIN;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MinimalRebuild>false</MinimalRebuild> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> + <ObjectFileName>$(IntDir)</ObjectFileName> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <DisableSpecificWarnings>4090;4312;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <ResourceCompile> + <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <Culture>0x0415</Culture> + </ResourceCompile> + <Link> + <AdditionalDependencies>shlwapi.lib;Msimg32.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>true</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <MapFileName>$(IntDir)$(TargetName).map</MapFileName> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <Optimization>MinSpace</Optimization> + <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> + <FavorSizeOrSpeed>Size</FavorSizeOrSpeed> + <AdditionalIncludeDirectories>.;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;TAGZ_UNICODE;UNICODE_INPUT_PLUGIN;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <StringPooling>true</StringPooling> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <BufferSecurityCheck>true</BufferSecurityCheck> + <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> + <ObjectFileName>$(IntDir)</ObjectFileName> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>None</DebugInformationFormat> + <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed> + <DisableSpecificWarnings>4090;4312;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <ResourceCompile> + <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <Culture>0x0415</Culture> + </ResourceCompile> + <Link> + <AdditionalDependencies>shlwapi.lib;Msimg32.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>false</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <MapFileName>$(IntDir)$(TargetName).map</MapFileName> + <SubSystem>Windows</SubSystem> + <OptimizeReferences>true</OptimizeReferences> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <TargetMachine>MachineX86</TargetMachine> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <Optimization>MinSpace</Optimization> + <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> + <FavorSizeOrSpeed>Size</FavorSizeOrSpeed> + <AdditionalIncludeDirectories>.;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN64;NDEBUG;_WINDOWS;TAGZ_UNICODE;UNICODE_INPUT_PLUGIN;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <StringPooling>true</StringPooling> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <BufferSecurityCheck>true</BufferSecurityCheck> + <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation> + <ObjectFileName>$(IntDir)</ObjectFileName> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>None</DebugInformationFormat> + <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed> + <DisableSpecificWarnings>4090;4312;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings> + </ClCompile> + <ResourceCompile> + <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <Culture>0x0415</Culture> + </ResourceCompile> + <Link> + <AdditionalDependencies>shlwapi.lib;Msimg32.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>false</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <MapFileName>$(IntDir)$(TargetName).map</MapFileName> + <SubSystem>Windows</SubSystem> + <OptimizeReferences>true</OptimizeReferences> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ProjectReference Include="..\..\..\external_dependencies\openmpt-trunk\include\vorbis\win32\vorbisfile_static.vcxproj"> + <Project>{ec9475d2-fee2-4f8c-9bb9-a11d5eb597c4}</Project> + </ProjectReference> + <ProjectReference Include="..\..\..\Wasabi\Wasabi.vcxproj"> + <Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project> + </ProjectReference> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\..\..\nsmkv\Lacing.cpp" /> + <ClCompile Include="..\..\..\nsmkv\vint.cpp" /> + <ClCompile Include="about.cpp" /> + <ClCompile Include="chainedstream_parse.cpp" /> + <ClCompile Include="config.cpp" /> + <ClCompile Include="c_string.cpp" /> + <ClCompile Include="decoder.cpp" /> + <ClCompile Include="ExtendedRead.cpp" /> + <ClCompile Include="genres.c" /> + <ClCompile Include="http.cpp" /> + <ClCompile Include="infobox.cpp" /> + <ClCompile Include="info_.cpp" /> + <ClCompile Include="localfile.cpp" /> + <ClCompile Include="mkv_vorbis_decoder.cpp" /> + <ClCompile Include="shaper.cpp" /> + <ClCompile Include="vcedit.c" /> + <ClCompile Include="wa2.cpp" /> + <ClCompile Include="winnt_helper.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="api__in_vorbis.h" /> + <ClInclude Include="c_string.h" /> + <ClInclude Include="decoder.h" /> + <ClInclude Include="genres.h" /> + <ClInclude Include="main.h" /> + <ClInclude Include="mkv_vorbis_decoder.h" /> + <ClInclude Include="resource.h" /> + <ClInclude Include="shaper.h" /> + <ClInclude Include="vcedit.h" /> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="in_vorbis.rc" /> + </ItemGroup> + <ItemGroup> + <Image Include="oggdrop\112.png" /> + <Image Include="oggdrop\113.png" /> + <Image Include="oggdrop\114.png" /> + <Image Include="oggdrop\115.png" /> + <Image Include="oggdrop\116.png" /> + <Image Include="oggdrop\117.png" /> + <Image Include="oggdrop\118.png" /> + <Image Include="oggdrop\119.png" /> + <Image Include="oggdrop\120.png" /> + <Image Include="oggdrop\121.png" /> + <Image Include="oggdrop\122.png" /> + <Image Include="oggdrop\123.png" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/Src/Plugins/Input/in_vorbis/in_vorbis.vcxproj.filters b/Src/Plugins/Input/in_vorbis/in_vorbis.vcxproj.filters new file mode 100644 index 00000000..6ce4ad5b --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/in_vorbis.vcxproj.filters @@ -0,0 +1,145 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <ClCompile Include="winnt_helper.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="wa2.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="vcedit.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="shaper.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="mkv_vorbis_decoder.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="localfile.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="infobox.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="info_.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="http.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="genres.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="ExtendedRead.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="decoder.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="config.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="chainedstream_parse.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="c_string.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="about.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\..\nsmkv\Lacing.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\..\nsmkv\vint.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="api__in_vorbis.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="c_string.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="decoder.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="genres.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="main.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="mkv_vorbis_decoder.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="resource.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="shaper.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="vcedit.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <Image Include="oggdrop\112.png"> + <Filter>Image Files</Filter> + </Image> + <Image Include="oggdrop\113.png"> + <Filter>Image Files</Filter> + </Image> + <Image Include="oggdrop\114.png"> + <Filter>Image Files</Filter> + </Image> + <Image Include="oggdrop\115.png"> + <Filter>Image Files</Filter> + </Image> + <Image Include="oggdrop\116.png"> + <Filter>Image Files</Filter> + </Image> + <Image Include="oggdrop\117.png"> + <Filter>Image Files</Filter> + </Image> + <Image Include="oggdrop\118.png"> + <Filter>Image Files</Filter> + </Image> + <Image Include="oggdrop\119.png"> + <Filter>Image Files</Filter> + </Image> + <Image Include="oggdrop\120.png"> + <Filter>Image Files</Filter> + </Image> + <Image Include="oggdrop\121.png"> + <Filter>Image Files</Filter> + </Image> + <Image Include="oggdrop\122.png"> + <Filter>Image Files</Filter> + </Image> + <Image Include="oggdrop\123.png"> + <Filter>Image Files</Filter> + </Image> + </ItemGroup> + <ItemGroup> + <Filter Include="Header Files"> + <UniqueIdentifier>{7c471b36-309f-402b-a247-e640a094f9bc}</UniqueIdentifier> + </Filter> + <Filter Include="Ressource Files"> + <UniqueIdentifier>{735fc214-c8e5-4619-8c70-a184a8373c92}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files"> + <UniqueIdentifier>{00aa1653-baa3-4e88-b6d1-fa408b0c4a85}</UniqueIdentifier> + </Filter> + <Filter Include="Image Files"> + <UniqueIdentifier>{32126a2c-dd90-4901-a5e1-aee5b7363e37}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="in_vorbis.rc"> + <Filter>Ressource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/Src/Plugins/Input/in_vorbis/info_.cpp b/Src/Plugins/Input/in_vorbis/info_.cpp new file mode 100644 index 00000000..687dc737 --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/info_.cpp @@ -0,0 +1,658 @@ +//tag editor file i/o code, title formatting interface +#include "main.h" +#include "genres.h" +#include "../nu/AutoWide.h" +#include "../nu/AutoChar.h" +#include "../nu/ns_wc.h" +#include "api__in_vorbis.h" +#include <wchar.h> +#include <math.h> +#include <shlwapi.h> +#include "vcedit.h" +#include <strsafe.h> +#include "resource.h" + +namespace ogg_helper //chainedstream_parse +{ + int num_get_tracks(HANDLE hFile); + int query_chained_stream_offset(HANDLE hFile, int idx, __int64 * out_beginning, __int64 * out_end); +} + +/*static void xfer(HANDLE src, HANDLE dst, __int64 size) +{ + enum { BUFFER = 1024 * 1024 }; + void * buffer = malloc((int)(BUFFER > size ? size : BUFFER)); + while (size > 0) + { + int d = BUFFER; + if ((__int64)d > size) d = (int)size; + DWORD br = 0; + ReadFile(src, buffer, d, &br, 0); + WriteFile(dst, buffer, d, &br, 0); + size -= d; + } +}*/ + +static void seek64(HANDLE src, __int64 offset) +{ + __int64 temp = offset; + SetFilePointer(src, *(DWORD*)&temp, ((long*)&temp + 1), FILE_BEGIN); +} + +extern OSVERSIONINFO os_ver; +extern HANDLE hThread; + +static DWORDLONG get_space(const wchar_t * url) +{ + ULARGE_INTEGER free_space; + char zzz[4] = {(char)url[0], (char)url[1], (char)url[2], 0}; //"c:\"; + + free_space.QuadPart = 0; + + if (os_ver.dwPlatformId == VER_PLATFORM_WIN32_NT || (os_ver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS && LOWORD(os_ver.dwBuildNumber) > 1000)) + { + static BOOL (WINAPI* pGetDiskFreeSpaceEx)(LPCTSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER); + if (!pGetDiskFreeSpaceEx) + { + pGetDiskFreeSpaceEx = (BOOL (WINAPI*)(LPCTSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER))GetProcAddress(GetModuleHandle(L"kernel32.dll"), "GetDiskFreeSpaceExA"); + } + if (pGetDiskFreeSpaceEx) + { + ULARGE_INTEGER blah1, blah2; + pGetDiskFreeSpaceEx((LPCTSTR)zzz, &free_space, &blah1, &blah2); + } + } + if (!free_space.QuadPart) + { + DWORD spc, bps, nfc, tnc; + GetDiskFreeSpaceA(zzz, &spc, &bps, &nfc, &tnc); + free_space.QuadPart = UInt32x32To64(spc * bps, nfc); + } + return free_space.QuadPart; +} + +bool sync_movefile(const wchar_t * src, const wchar_t * dst); + +struct vcedit_param +{ + HANDLE hFile; + __int64 remaining; +}; + +static size_t callback_fread(void *ptr, size_t size, size_t nmemb, vcedit_param * param) +{ + int to_read = (int)(nmemb *size); + if (to_read > param->remaining) to_read = (int)param->remaining; + DWORD br = 0; + ReadFile(param->hFile, ptr, to_read, &br, 0); + param->remaining -= br; + return br / size; +} + +static size_t callback_write(const void *ptr, size_t size, size_t nmemb, HANDLE hFile) +{ + DWORD bw = 0; + WriteFile(hFile, ptr, (DWORD)(size*nmemb), &bw, 0); + return bw / size; +} + +BOOL modify_file(const wchar_t* url, const vorbis_comment * comments, int links) +{ //also used for stream save fix + HANDLE dst = INVALID_HANDLE_VALUE; + int scream = 0; + StringW tmp; + + winampGetExtendedFileInfoW_Cleanup(); + + tmp = url; + tmp += L".tmp"; + + HANDLE src = CreateFileW(url, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + + if (src != INVALID_HANDLE_VALUE) + { + ULARGE_INTEGER src_size; + src_size.LowPart = GetFileSize(src, &src_size.HighPart); + if (src_size.QuadPart > get_space(url)) + { //shit happens... try default temp location + StringW tempdir; + GetTempPathW(MAX_PATH, StringTempW(tempdir, MAX_PATH)); + if (get_space(tempdir) < src_size.QuadPart) + { //oh well + CloseHandle(src); + src = INVALID_HANDLE_VALUE; + } + { + tmp = tempdir; + if (tmp[tmp.Length() - 1] != '\\') tmp.AddChar('\\'); + + StringCchPrintfW(StringTempW(tempdir, MAX_PATH), MAX_PATH, L"ogg%u_%u.tmp", GetTickCount(), GetCurrentProcessId()); + tmp.AddString(tempdir); + } + } + dst = CreateFileW(tmp, GENERIC_WRITE | GENERIC_READ, 0, 0, CREATE_ALWAYS, 0, 0); + } + + if (dst != INVALID_HANDLE_VALUE && src != INVALID_HANDLE_VALUE) + { + { + FILETIME ct; + GetFileTime(src, &ct, 0, 0); + SetFileTime(dst, &ct, 0, 0); + } + + int num_links = ogg_helper::num_get_tracks(src); + if (num_links < links) scream = 1; + else + { + int cur_link; + for (cur_link = 0; cur_link < links && !scream; cur_link++) + { + __int64 stream_beginning, stream_end; + if (ogg_helper::query_chained_stream_offset(src, cur_link, &stream_beginning, &stream_end)) + { + seek64(src, stream_beginning); + vcedit_state *vs; + vcedit_param param; + param.hFile = src; + param.remaining = stream_end - stream_beginning; + vs = vcedit_new_state(); + if (vcedit_open_callbacks(vs, ¶m, (vcedit_read_func)callback_fread, (vcedit_write_func)callback_write) < 0) + { + scream = 1; + } + else + { + vorbis_comment * vc = vcedit_comments(vs); + vorbis_comment_clear(vc); + vorbis_comment_init(vc); + const vorbis_comment * vc_src = comments + cur_link; + + int n; + for (n = 0;n < vc_src->comments;n++) + { + if (vc_src->user_comments[n]) + vorbis_comment_add(vc, vc_src->user_comments[n]); + } + + vcedit_write(vs, dst); + vcedit_clear(vs); + } + } + } + } + } + else scream = 1; + if (src != INVALID_HANDLE_VALUE) CloseHandle(src); + if (dst != INVALID_HANDLE_VALUE) + { + CloseHandle(dst); + if (scream) + { + DeleteFileW(tmp); + } + } + + if (!scream) + { + BOOL f_sync; + EnterCriticalSection(&sync); + + f_sync = !_wcsicmp(url, cur_file) && hThread; //check for i/o conflict with currently played file + LeaveCriticalSection(&sync); + if (f_sync) + { //drat, it's now playing + scream = !sync_movefile(tmp, url); + } + else + { + if (!DeleteFileW(url)) scream = 1; + else + { + if (!MoveFileW(tmp, url)) + { + if (!CopyFileW(tmp, url, 0)) scream = 1; + DeleteFileW(tmp); + } + } + } + } + if (scream) return 0; + else return 1; +} + +wchar_t *wdup(const char * src) +{ + return _wcsdup(StringW(src)); +} + +extern StringW stat_disp; + +void GetFileInfo(const wchar_t *file, wchar_t *title, int *len) +{ + VorbisFile* vf = 0; + BOOL is_cur_file = 0; + BOOL is_vf_local = 1; + if (title) *title = 0; + if (len) *len = -1; + + if (!file || !*file) + { + file = cur_file; + is_cur_file = 1; + } + else if (!lstrcmpiW(file, cur_file)) + { + is_cur_file = 1; + } + + if (title && stat_disp.Length() > 0 && is_cur_file) + { + lstrcpynW(title, stat_disp, 256); + title = 0; + } + + if (!len && !title) return ; + + if (is_cur_file) + { + EnterCriticalSection(&sync); + if (theFile) + { + vf = theFile; + is_vf_local = 0; + } + else + LeaveCriticalSection(&sync); + } + + if (!vf) + { + vf = VorbisFile::Create(file, 1); + if (!vf) + { + if (title) + { + lstrcpynW(title, PathFindFileNameW(file), 256); + wchar_t *blah = PathFindExtensionW(title); + *blah=0; + } + return ; + } + } + + if (len) + { + *len = (int)(vf->Length() * 1000); + } + + if (title) + { + const char *t = vf->get_meta("ARTIST", 0); + if (t) + { + MultiByteToWideCharSZ(CP_UTF8, 0, t, -1, title, 256); + t = vf->get_meta("TITLE", 0); + if (t) + { + StringCchCatW(title, 256, L" - "); + StringCchCatW(title, 256, AutoWide(t, CP_UTF8)); + } + } + else + { + const char *t = vf->get_meta("TITLE", 0); + if (t) + MultiByteToWideCharSZ(CP_UTF8, 0, t, -1, title, 256); + else + { + lstrcpynW(title, PathFindFileNameW(file), 256); + wchar_t *blah = PathFindExtensionW(title); + *blah=0; + } + } + } + //q: + if (is_vf_local) + delete vf; + else + LeaveCriticalSection(&sync); +} + +void w9x_itow(wchar_t *dest, int value, int destlen) +{ + StringCchPrintfW(dest, destlen, L"%d", value); + +} +void w9x_utow(wchar_t *dest, int value, int destlen) +{ + StringCchPrintfW(dest, destlen, L"%u", value); +} +void w9x_htow(wchar_t *dest, int value, int destlen) +{ + StringCchPrintfW(dest, destlen, L"%08x", value); +} + +static void print_misc(VorbisFile * _vf,int link,wchar_t * out, int outlen) +{ + OggVorbis_File * vf=&_vf->vf; + double t=ov_time_total(vf,link); + vorbis_info * vi=ov_info(vf,link); + vorbis_comment * vc=ov_comment(vf,link); + if (!vi || !vc) {WASABI_API_LNGSTRINGW_BUF(IDS_FILE_ERROR,out,outlen);return;} + + wchar_t kbps_str[16] = {0}; + WASABI_API_LNGSTRINGW_BUF(IDS_KBPS, kbps_str, 16); + + wchar_t length[48]=L"", avgbitrate[48]=L"", filesize[48]=L"", nombitrate[48]=L"", maxbitrate[48]=L"", minbitrate[48]=L""; + if (t>0) + { + int h = (int)(t/3600.0); + int m = (int)(t/60.0)%60; + int s = (int)t%60; + + if(h>0) StringCchPrintfW(length,48,L"%s%u:%02u:%02u\r\n",WASABI_API_LNGSTRINGW(IDS_LENGTH),h,m,s); + else if(m>0) StringCchPrintfW(length,48,L"%s%u:%02u\r\n",WASABI_API_LNGSTRINGW(IDS_LENGTH),m,s); + else if(s>0) StringCchPrintfW(length,48,L"%s%u\r\n",WASABI_API_LNGSTRINGW(IDS_LENGTH),s); + + UINT fs=_vf->FileSize(); + if (fs>0) + { + int kbps = (int)(((double)fs)/(t*125.0)); + wchar_t tmp[32] = {0}; + StringCchPrintfW(avgbitrate,48,L"%s%u %s\r\n",WASABI_API_LNGSTRINGW(IDS_AVERAGE_BITRATE),kbps,kbps_str); + + int fs1=fs/1000000; + int fs2=(fs/1000)%1000; + int fs3=fs%1000; + if(fs1) + StringCchPrintfW(filesize,48,L"%s%u%03u%03u %s\r\n",WASABI_API_LNGSTRINGW(IDS_FILE_SIZE),fs1,fs2,fs3,WASABI_API_LNGSTRINGW_BUF(IDS_BYTES,tmp,32)); + else if(fs2) + StringCchPrintfW(filesize,48,L"%s%u%03u %s\r\n",WASABI_API_LNGSTRINGW(IDS_FILE_SIZE),fs2,fs3,WASABI_API_LNGSTRINGW_BUF(IDS_BYTES,tmp,32)); + else + StringCchPrintfW(filesize,48,L"%s%u %s\r\n",WASABI_API_LNGSTRINGW(IDS_FILE_SIZE),fs3,WASABI_API_LNGSTRINGW_BUF(IDS_BYTES,tmp,32)); + } + } + + if (vi->bitrate_nominal>0) + StringCchPrintfW(nombitrate,48,L"%s%u %s\r\n",WASABI_API_LNGSTRINGW(IDS_NOMINAL_BITRATE),vi->bitrate_nominal/1000,kbps_str); + + if (vi->bitrate_lower>0) + StringCchPrintfW(minbitrate,48,L"%s%u %s\r\n",WASABI_API_LNGSTRINGW(IDS_MIN_BITRATE),vi->bitrate_lower/1000,kbps_str); + + if (vi->bitrate_nominal>0) + StringCchPrintfW(maxbitrate,48,L"%s%u %s\r\n",WASABI_API_LNGSTRINGW(IDS_MAX_BITRATE),vi->bitrate_nominal/1000,kbps_str); + + wchar_t tmp[32] = {0}, tmp2[32] = {0}, tmp3[32] = {0}, tmp4[32] = {0}, tmp5[32] = {0}, hzStr[8] = {0}; + StringCchPrintfW(out,outlen,L"%s%s%s%s%s%s%s: %u\r\n%s: %u %s\r\n%s: %u\r\n%s: %u\r\n%s: \r\n%s", + length, avgbitrate, filesize, nombitrate, maxbitrate, minbitrate, + WASABI_API_LNGSTRINGW_BUF(IDS_CHANNELS,tmp,32),vi->channels, + WASABI_API_LNGSTRINGW_BUF(IDS_SAMPLING_RATE,tmp2,32),vi->rate, WASABI_API_LNGSTRINGW_BUF(IDS_HZ,hzStr,8), + WASABI_API_LNGSTRINGW_BUF(IDS_SERIAL_NUMBER,tmp3,32),ov_serialnumber(vf,link), + WASABI_API_LNGSTRINGW_BUF(IDS_VERSION,tmp4,32),vi->version, + WASABI_API_LNGSTRINGW_BUF(IDS_VENDOR,tmp5,32),(wchar_t*)AutoWide(vc->vendor,CP_UTF8)); +} + +static VorbisFile* last_vf = 0; +static wchar_t last_file[MAX_PATH] = {0}; +static FILETIME ftLastWriteTime; + +// is used to determine if the last write time of the file has changed when +// asked to get the metadata for the same cached file so we can update things +BOOL HasFileTimeChanged(const wchar_t *fn) +{ + WIN32_FILE_ATTRIBUTE_DATA fileData = {0}; + if (GetFileAttributesExW(fn, GetFileExInfoStandard, &fileData) == TRUE) + { + if(CompareFileTime(&ftLastWriteTime, &fileData.ftLastWriteTime)) + { + ftLastWriteTime = fileData.ftLastWriteTime; + return TRUE; + } + } + return FALSE; +} + +void UpdateFileTimeChanged(const wchar_t *fn) +{ + WIN32_FILE_ATTRIBUTE_DATA fileData; + if (GetFileAttributesExW(fn, GetFileExInfoStandard, &fileData) == TRUE) + { + ftLastWriteTime = fileData.ftLastWriteTime; + } +} + +// need to call this when we shut down just to make sure things are correctly cleaned up +//(the joys of caching for speed) +void winampGetExtendedFileInfoW_Cleanup(void) +{ + if (last_vf) + { + delete last_vf; + last_vf = 0; + } + last_file[0] = 0; +} + +static void CALLBACK winampGetExtendedFileInfoW_Timer(HWND hwnd, UINT uMsg, UINT_PTR eventId, DWORD elapsed) +{ + // TODO need to do a better way of getting and caching the metadata + // this is _just_ a temp fix for the file being locked when it + // it really needs 'class Info' to be able to cache and read. + KillTimer(hwnd, eventId); + winampGetExtendedFileInfoW_Cleanup(); +} + +bool KeywordMatch(const char *mainString, const char *keyword) +{ + return !_stricmp(mainString, keyword); +} + +#define START_TAG_ALIAS(name, alias) if (KeywordMatch(data, name)) lookup=alias +#define TAG_ALIAS(name, alias) else if (KeywordMatch(data, name)) lookup=alias +extern "C" __declspec( dllexport ) int winampGetExtendedFileInfoW(const wchar_t *fn, const char *data, wchar_t *dest, int destlen) +{ + if (!_stricmp(data, "type")) + { + dest[0] = '0'; + dest[1] = 0; + return 1; + } + else if (!_stricmp(data, "rateable")) + { + dest[0] = '1'; + dest[1] = 0; + return 1; + } + else if (!_stricmp(data, "streammetadata")) + { + return 0; + } + + if (!fn || (fn && !fn[0])) return 0; + + if (!_stricmp(data, "family")) + { + LPCWSTR e; + int pID = -1; + DWORD lcid; + e = PathFindExtensionW(fn); + if (L'.' != *e) return 0; + e++; + lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); + if (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, e, -1, L"OGG", -1) || + CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, e, -1, L"OGA", -1)) pID = IDS_FAMILY_STRING; + if (pID != -1 && S_OK == StringCchCopyW(dest, destlen, WASABI_API_LNGSTRINGW(pID))) return 1; + return 0; + } + + if (!_stricmp(data, "mime")) + { + StringCchCopyW(dest, destlen, L"audio/ogg"); + return 1; + } + + // attempt to cache/use a cached instance of VorbisFile to speed up metadata queries + // which is especially needed with large ogg files (like with a 4Mb embedded image!) + VorbisFile* vf = 0; + if(last_file[0] && !_wcsicmp(last_file, fn) && !HasFileTimeChanged(fn)) + { + vf = last_vf; + } + else + { + // different file so clean up if there's a cached instance + if(last_vf) + { + delete last_vf; + last_vf = 0; + } + // open the new file and cache the current filename for subsequent query checks + vf = VorbisFile::Create(fn, 1); + lstrcpynW(last_file, fn, MAX_PATH); + } + + if (!vf) return 0; + else last_vf = vf; + + // TODO need to do a better way of getting and caching the metadata + // this is _just_ a temp fix for the file being locked when it + // it really needs 'class Info' to be able to cache and read. + SetTimer(mod.hMainWindow, 256, 2000, winampGetExtendedFileInfoW_Timer); + + const char *lookup = 0; + if (!_stricmp(data, "length")) + { + int len = (int)(vf->Length() * 1000); + w9x_itow(dest, len, destlen); + return 1; + } + else if (!_stricmp(data, "bitrate")) + { + int br = vf->get_avg_bitrate(); + w9x_itow(dest, br, destlen); + return 1; + } + else if (!_stricmp(data, "SERIALNUMBER")) + { + w9x_utow(dest, ov_serialnumber(&vf->vf, -1), destlen); + return 1; + } + else if (!_stricmp(data, "SERIALNUMBER_HEX")) + { + w9x_htow(dest, ov_serialnumber(&vf->vf, -1), destlen); + return 1; + } + else if (!_stricmp(data, "gain")) + { + float gain = 20.0f*(float)log10(vf->GetGain()); + StringCchPrintfW(dest, destlen, L"%-+.2f dB", gain); + return 1; + } + else if(!_stricmp(data,"formatinformation")) + { + print_misc(vf,0,dest,destlen); + return 1; + } + TAG_ALIAS("title", "TITLE"); + TAG_ALIAS("artist", "ARTIST"); + TAG_ALIAS("album", "ALBUM"); + TAG_ALIAS("genre", "GENRE"); + TAG_ALIAS("comment", "COMMENT"); + TAG_ALIAS("year", "DATE"); + TAG_ALIAS("track", "TRACKNUMBER"); + TAG_ALIAS("albumartist", "ALBUMARTIST"); + TAG_ALIAS("composer", "COMPOSER"); + TAG_ALIAS("disc", "DISCNUMBER"); + TAG_ALIAS("publisher", "PUBLISHER"); + TAG_ALIAS("conductor", "CONDUCTOR"); + TAG_ALIAS("tool", "ENCODED-BY"); + TAG_ALIAS("replaygain_track_gain", "REPLAYGAIN_TRACK_GAIN"); + TAG_ALIAS("replaygain_track_peak", "REPLAYGAIN_TRACK_PEAK"); + TAG_ALIAS("replaygain_album_gain", "REPLAYGAIN_ALBUM_GAIN"); + TAG_ALIAS("replaygain_album_peak", "REPLAYGAIN_ALBUM_PEAK"); + TAG_ALIAS("GracenoteFileID", "GRACENOTEFILEID"); + TAG_ALIAS("GracenoteExtData", "GRACENOTEEXTDATA"); + TAG_ALIAS("bpm", "BPM"); + TAG_ALIAS("remixing", "REMIXING"); + TAG_ALIAS("subtitle", "VERSION"); + TAG_ALIAS("isrc", "ISRC"); + TAG_ALIAS("category", "CATEGORY"); + TAG_ALIAS("rating", "RATING"); + TAG_ALIAS("producer", "PRODUCER"); + + if (!lookup) + return 0; + + const char *value = vf->get_meta(lookup, 0); + + if(KeywordMatch("comment",data)) { + if(!value || !*value) value = vf->get_meta("DESCRIPTION", 0); + } + + if(KeywordMatch("year",data)) { + if(!value || !*value) value = vf->get_meta("YEAR", 0); + } + + if(KeywordMatch("track",data)) { + if(!value || !*value) value = vf->get_meta("TRACK", 0); + } + + if(KeywordMatch("albumartist",data)) { + if(!value || !*value) value = vf->get_meta("ALBUM ARTIST", 0); + if(!value || !*value) value = vf->get_meta("ENSEMBLE", 0); + } + + if(KeywordMatch("publisher",data)) { + if(!value || !*value) value = vf->get_meta("ORGANIZATION", 0); + } + + if(KeywordMatch("category",data)) { + if(!value || !*value) value = vf->get_meta("CONTENTGROUP", 0); + if(!value || !*value) value = vf->get_meta("GROUPING", 0); + } + + if(KeywordMatch(data, "rating")) { + if(!value || !*value) value = vf->get_meta("RATING", 0); + if(value && *value) { + int rating = atoi(value); + // keeps things limited to our range of 0-100 + if (rating >= 100) { + rating = 5; + } + // 1-100 case + else if (rating > 5 && rating < 100) { + rating /= 20; + // shift up by one rating when in next band + // 1-20 = 1, 21-40 = 2, 41-60 = 3, 61-80 = 4, 81-100 = 5 + rating += ((atoi(value) - (rating * 20)) > 0); + } + // Remove support for old 1-10 range + /* or maybe we're dealing with a 1-10 range + else if (rating > 5) { + rating /= 2; + } */ + + // otherwise it is hopefully in the 0-5 range + else if (rating > 0 && rating <= 5) { + } + // otherwise just make sure and set zero + else { + rating = 0; + } + + StringCchPrintfW(dest, destlen, L"%u", rating); + return 1; + } + } + + if(value) + MultiByteToWideCharSZ(CP_UTF8, 0, value, -1, dest, destlen); + else + { + dest[0]=0; + return 1; + } + + return 1; +}
\ No newline at end of file diff --git a/Src/Plugins/Input/in_vorbis/infobox.cpp b/Src/Plugins/Input/in_vorbis/infobox.cpp new file mode 100644 index 00000000..30d90056 --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/infobox.cpp @@ -0,0 +1,1457 @@ +#include "main.h" +#include "api__in_vorbis.h" +#include "genres.h" +#include <commctrl.h> +#include "../Agave/Language/api_language.h" +#include "../nu/AutoWide.h" +#include "../nu/AutoChar.h" +#include "resource.h" +#include <strsafe.h> + +extern In_Module mod; +/* old PP info box. still used for streams */ + + +extern CfgFont cfg_font; + +extern CfgInt cfg_modeless,cfg_remember_infosize; + +static CfgInt + cfg_hide_special_fields("hide_special_fields",1), + cfg_adv_info("adv_info",0), + cfg_infobox_sx("infobox_sx",0), + cfg_infobox_sy("infobox_sy",0), + cfg_infobox_x("infobox_x",0), + cfg_infobox_y("infobox_y",0); + +void SetDlgItemTextWrap(HWND w,UINT id,wchar_t * tx) +{ + SetDlgItemTextW(w,id,tx); +} + +typedef struct +{ + UINT id; + wchar_t * name; +} STD_TAG_ITEM; + +#define N_STD_TAGZ 7 + +extern BOOL cfg_adv_warn; + +static const STD_TAG_ITEM std_tagz[N_STD_TAGZ]= +{ + {IDC_TITLE,L"TITLE"}, + {IDC_ARTIST,L"ARTIST"}, + {IDC_ALBUM,L"ALBUM"}, + {IDC_GENRE,L"GENRE"}, + {IDC_DATE,L"DATE"}, + {IDC_COMMENT,L"COMMENT"}, + {IDC_TRACK,L"TRACKNUMBER"}, +}; + +const wchar_t * special_fields[]={L"RG_PEAK",L"RG_RADIO",L"RG_AUDIOPHILE",L"LWING_GAIN",L"REPLAYGAIN_ALBUM_GAIN",L"REPLAYGAIN_ALBUM_PEAK",L"REPLAYGAIN_TRACK_GAIN",L"REPLAYGAIN_TRACK_PEAK"}; +#define N_SPECIAL_FIELDS (sizeof(special_fields)/sizeof(special_fields[0])) + + +typedef struct tagTAG +{ + tagTAG * next; + wchar_t * name; + wchar_t * value; +} TAG; + +typedef struct +{ + wchar_t *name,*value; +} TAGDESC; + +class OggTagData +{ +public: + TAG * tags; + TAG ** last; + TAG * newtag() + { + TAG * t=new TAG; + *last=t; + last=&t->next; + t->next=0; + return t; + } + + OggTagData() + { + tags=0; + last=&tags; + } + + String vendor; + + OggTagData(vorbis_comment * vc) : vendor(vc->vendor) + { + tags=0; + last=&tags; + int n; + for(n=0;n<vc->comments;n++) + { + TAG * t=newtag(); + char * c=vc->user_comments[n]; + char * p=strchr(c,'='); + if (p) + { + int size = MultiByteToWideChar(CP_UTF8, 0, c, (int)(p-c), 0,0); + t->name=(wchar_t*)malloc((size+1)*sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8, 0, c, (int)(p-c), t->name, size); + t->name[size]=0; + p++; + } + else + { + t->name=_wcsdup(L"COMMENT"); + p=c; + } + + int size = MultiByteToWideChar(CP_UTF8, 0, p, -1, 0,0); + t->value=(wchar_t*)malloc((size)*sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8, 0, p, -1, t->value, size); + } + } + void Clear() + { + TAG * t=tags; + while(t) + { + TAG * t1=t->next; + free(t->name); + free(t->value); + delete t; + t=t1; + } + tags=0; + last=&tags; + } + void AddTag(const wchar_t * name,const wchar_t * value) + { + TAG * t=newtag(); + t->name=_wcsdup(name); + t->value=_wcsdup(value); + } + ~OggTagData() + { + Clear(); + } +}; + +static void SetWindowRect(HWND w,RECT * r) +{ + SetWindowPos(w,0,r->left,r->top,r->right-r->left,r->bottom-r->top,SWP_NOZORDER|SWP_NOCOPYBITS|SWP_NOACTIVATE); +} + +class DlgBase +{ +protected: + bool DieOnDestroyWindow,is_modeless,is_modal_ex,modal_ex_quit; + int modal_ex_quit_val; + + void endDialog(int x) + { + if (is_modeless) DestroyWindow(wnd); + else if (is_modal_ex) + { + modal_ex_quit=1; + modal_ex_quit_val=x; + DestroyWindow(wnd); + } + else EndDialog(wnd,x); + } + + void _do_size_x(RECT * r,UINT id,UINT wx,UINT min_x) + { + RECT r1={r->left,r->top,(LONG)(wx-min_x)+r->right,r->bottom}; + SetWindowRect(GetDlgItem(wnd,id),&r1); + } + + void _do_size_xy(RECT * r,UINT id,UINT wx,UINT wy,UINT min_x,UINT min_y) + { + RECT r1={r->left,r->top,(LONG)(wx-min_x)+r->right,(LONG)(wy-min_y)+r->bottom}; + SetWindowRect(GetDlgItem(wnd,id),&r1); + } + + void _do_align_x_size_y(RECT * r,UINT id,UINT wx,UINT wy,UINT min_x,UINT min_y) + { + RECT r1={ (LONG)(wx-min_x)+r->left,r->top,(LONG)(wx-min_x)+r->right,(LONG)(wy-min_y)+r->bottom}; + SetWindowRect(GetDlgItem(wnd,id),&r1); + } + + void _do_align_x(RECT * r,UINT id,UINT wx,UINT min_x) + { + RECT r1={ (LONG)(wx-min_x+r)->left,(LONG)r->top,(LONG)(wx-min_x+r)->right,(LONG)r->bottom}; + SetWindowRect(GetDlgItem(wnd,id),&r1); + } + + void _do_align_xy(RECT * r,UINT id,UINT wx,UINT wy,UINT min_x,UINT min_y) + { + RECT r1={(LONG)(wx-min_x+r)->left,(LONG)(wy-min_y+r)->top,(LONG)(wx- min_x+r)->right,(LONG)(wy-min_y+r)->bottom}; + SetWindowRect(GetDlgItem(wnd,id),&r1); + } + +#define do_size_x(id,r) _do_size_x(r,id,sx,min_size_x) +#define do_size_xy(id,r) _do_size_xy(r,id,sx,sy,min_size_x,min_size_y) +#define do_align_x_size_y(id,r) _do_align_x_size_y(r,id,sx,sy,min_size_x,min_size_y) +#define do_align_xy(id,r) _do_align_xy(r,id,sx,sy,min_size_x,min_size_y) +#define do_align_x(id,r) _do_align_x(r,id,sx,min_size_x) + + HWND wnd; + UINT min_size_x,min_size_y; + UINT min_size_x_w,min_size_y_w; + + void do_sizing(UINT wp,RECT * r) + { + UINT dx,dy; + dx=r->right-r->left; + dy=r->bottom-r->top; + if (dx<min_size_x_w) + { + switch(wp) + { + case WMSZ_BOTTOMLEFT: + case WMSZ_LEFT: + case WMSZ_TOPLEFT: + r->left=r->right-min_size_x_w; + break; + case WMSZ_BOTTOMRIGHT: + case WMSZ_RIGHT: + case WMSZ_TOPRIGHT: + r->right=r->left+min_size_x_w; + break; + } + } + if (dy<min_size_y_w) + { + switch(wp) + { + case WMSZ_BOTTOMLEFT: + case WMSZ_BOTTOM: + case WMSZ_BOTTOMRIGHT: + r->bottom=r->top+min_size_y_w; + break; + case WMSZ_TOPLEFT: + case WMSZ_TOP: + case WMSZ_TOPRIGHT: + r->top=r->bottom-min_size_y_w; + break; + } + } + } + void MakeComboEdit(UINT id,DWORD s) + { + HWND w=GetDlgItem(wnd,id); + RECT r; + GetChildRect(id,r); + DestroyWindow(w); + CreateWindowEx( WS_EX_CLIENTEDGE, L"EDIT", 0, WS_CHILD | s, r.left - 1, r.top - 1, r.right - r.left, r.bottom - r.top, wnd, (HMENU)id, 0, 0 ); + } + void GetChildRect(UINT id,RECT& child) + { + RECT r_parent,r_child; + GetWindowRect(wnd,&r_parent); + GetWindowRect(GetDlgItem(wnd,id),&r_child); + int dx=r_parent.left; + int dy=r_parent.top; + if (!(GetWindowLong(wnd,GWL_STYLE)&WS_CHILD)) + { + dy+=GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYDLGFRAME); + dx+=GetSystemMetrics(SM_CXDLGFRAME); + } + child.left=r_child.left-dx; + child.right=r_child.right-dx; + child.top=r_child.top-dy; + child.bottom=r_child.bottom-dy; + } + + virtual BOOL DlgProc(UINT msg,WPARAM wp,LPARAM lp) {return 0;}; + static BOOL CALLBACK TheDialogProc(HWND wnd,UINT msg,WPARAM wp,LPARAM lp) + { + DlgBase * p; + if (msg==WM_INITDIALOG) + { + p=(DlgBase*)lp; +#ifdef WIN64 + SetWindowLong(wnd, DWLP_USER, (LONG)lp); +#else + SetWindowLong(wnd, DWL_USER, lp); +#endif + p->wnd=wnd; + RECT r; + GetClientRect(wnd,&r); + p->min_size_x=r.right; + p->min_size_y=r.bottom; + GetWindowRect(wnd,&r); + p->min_size_x_w=r.right-r.left; + p->min_size_y_w=r.bottom-r.top; + } +#ifdef WIN64 + else p = (DlgBase*)GetWindowLong(wnd, DWLP_USER); +#else + else p = (DlgBase*)GetWindowLong(wnd, DWL_USER); +#endif + BOOL rv=0; + if (p) + { + rv=p->DlgProc(msg,wp,lp); + if (msg==WM_DESTROY) + { + p->wnd=0; + if (p->DieOnDestroyWindow) + { + delete p; +#ifdef WIN64 + SetWindowLong(wnd, DWLP_USER, 0); +#else + SetWindowLong(wnd, DWL_USER, 0); +#endif + } + } + } + return rv; + } + HWND myCreateDialog(UINT id,HWND parent) + { + DieOnDestroyWindow=1; + is_modeless=1; + is_modal_ex=0; + return WASABI_API_CREATEDIALOGPARAMW(id,parent,TheDialogProc,(LPARAM)this); + } + virtual void myProcessMessage(MSG * msg) + { + if (!IsDialogMessage(wnd,msg)) + { + TranslateMessage(msg); + DispatchMessage(msg); + } + } + + int myDialogBoxEx(UINT id,HWND parent) + { + DieOnDestroyWindow=0; + is_modeless=0; + is_modal_ex=1; + modal_ex_quit=0; + modal_ex_quit_val=-1; + WASABI_API_CREATEDIALOGPARAMW(id,parent,TheDialogProc,(LPARAM)this); + if (wnd) + { + BOOL b=IsWindowEnabled(parent); + + if (b) EnableWindow(parent,0); + + MSG msg; + while(!modal_ex_quit && GetMessage(&msg,0,0,0)) + { + myProcessMessage(&msg); + } + + if (wnd) + { + DestroyWindow(wnd); + wnd=0; + } + + if (b) EnableWindow(parent,1); + SetActiveWindow(parent); + } + return modal_ex_quit_val; + } + + int myDialogBox(UINT id,HWND parent) + { + DieOnDestroyWindow=0; + is_modeless=0; + is_modal_ex=0; + return (int)WASABI_API_DIALOGBOXPARAMW(id,parent,TheDialogProc,(LPARAM)this); + } + DlgBase() + { + wnd=0; + DieOnDestroyWindow=0; + is_modeless=0; + is_modal_ex=0; + modal_ex_quit=0; + modal_ex_quit_val=0; + min_size_x=min_size_y=min_size_x_w=min_size_y_w=0; + } + virtual ~DlgBase() {DieOnDestroyWindow=0;if (wnd) DestroyWindow(wnd);} +public: + BOOL isDialogMessage(MSG * m) {return wnd ? IsDialogMessage(wnd,m) : 0;} +}; + +static char tags_file[]="tags.txt"; +static wchar_t genres_file[] = L"genres.txt"; + +class /*_declspec(novtable) */InfoDlgPanel : public DlgBase +{ +protected: + HFONT font; + HWND list; + RECT info_list; + OggTagData hidden; + + InfoDlgPanel(UINT id,HWND parent,HFONT foo) + { + font=foo; + myCreateDialog(id,parent); + //SetWindowLong(wnd,GWL_STYLE,GetWindowLong(wnd,GWL_STYLE)|WS_TABSTOP); + list=GetDlgItem(wnd,IDC_LIST); + SendMessage(list,WM_SETFONT,(WPARAM)font,0); + + GetChildRect(IDC_LIST,info_list); + } + + int lb_addstring(TAGDESC * tag,int idx=-1) + { + StringW tmp; + tmp+=tag->name; + tmp+=L"="; + tmp+=tag->value; + + const WCHAR * p=(const WCHAR*)tmp; + const WCHAR * foo=wcsstr(p,L"\x0d\x0a"); + if (foo) + { + tmp.Truncate((UINT)(foo-p)); + tmp.AddString(L" (...)"); + } + int rv= + (int)SendMessageW(list, + idx<0 ? LB_ADDSTRING : LB_INSERTSTRING, + idx<0 ? 0 : idx, + ((LPARAM)(const WCHAR*)tmp) + ); + if (rv>=0) SendMessage(list,LB_SETITEMDATA,rv,(LPARAM)tag); + else + { + free(tag->name); + free(tag->value); + delete tag; + } + return rv; + + } + + virtual void OnUpdateRect(UINT sx,UINT sy) + {//WM_SIZE-ish + do_size_xy(IDC_LIST,&info_list); + } + + virtual BOOL DlgProc(UINT msg,WPARAM wp,LPARAM lp) + { + switch(msg) + { + case WM_DESTROY: + lb_clear(); + list=0; + break; + case WM_COMMAND: + switch(wp) + { + case IDOK: + case IDCANCEL: + SendMessage(GetParent(wnd),msg,wp,lp); + break; + } + break; +// case WM_INITDIALOG: +// return 1; + } + return 0; + } + + void lb_clear() + { + if (!list) return; + int num=(int)SendMessage(list,LB_GETCOUNT,0,0); + while(num>0) + { + TAGDESC * l=(TAGDESC*)SendMessage(list,LB_GETITEMDATA,0,0); + if (l) + { + free(l->name); + free(l->value); + delete l; + } + SendMessage(list,LB_DELETESTRING,0,0); + num--; + } + } + + virtual void SetTag(wchar_t * name,wchar_t * value) + { + TAGDESC * l=new TAGDESC; + l->name=_wcsdup(name); + l->value=_wcsdup(value); + lb_addstring(l); + } + +public: + virtual void Clear() + { + hidden.Clear(); + lb_clear(); + } + void UpdateRect(RECT &r) + { + SetWindowPos(wnd,0,r.left,r.top,r.right-r.left,r.bottom-r.top,SWP_NOZORDER|SWP_NOACTIVATE|SWP_SHOWWINDOW); + RECT z; + GetClientRect(wnd,&z); + OnUpdateRect(z.right-z.left,z.bottom-z.top); + } + + virtual void GetTags(OggTagData & tags) + { + if (!list) return; + int num=(int)SendMessage(list,LB_GETCOUNT,0,0); + int n; + for(n=0;n<num;n++) + { + TAGDESC * l=(TAGDESC*)SendMessage(list,LB_GETITEMDATA,n,0); + tags.AddTag(l->name,l->value); + } + TAG * t=hidden.tags; + while(t) + { + tags.AddTag(t->name,t->value); + t=t->next; + } + } + + void SetTags(OggTagData & tags,BOOL hidespec) + { + TAG * t=tags.tags; + while(t) + { + bool hide=0; + if (hidespec) + { + int n; + for(n=0;n<N_SPECIAL_FIELDS;n++) + { + if (!_wcsicmp(t->name,special_fields[n])) + { + hidden.AddTag(t->name,t->value); + hide=1;break; + } + } + } + if (!hide) SetTag(t->name,t->value); + t=t->next; + } + } +}; + +class InfoDlgPanel_adv : public InfoDlgPanel +{ +private: + virtual void OnUpdateRect(UINT sx,UINT sy) + { + InfoDlgPanel::OnUpdateRect(sx,sy); + } +public: + InfoDlgPanel_adv(HWND parent,HFONT foo) : InfoDlgPanel(IDD_INFO_PANEL_ADVANCED,parent,foo) + { + } +}; + +class InfoDlgPanel_simple : public InfoDlgPanel +{ +private: + RECT info_static_std,info_title,info_artist,info_album,info_track,info_genre,info_comment,info_static_track,info_static_tags; + wchar_t *tag_bk[N_STD_TAGZ]; + BOOL STFU; +protected: + virtual void OnUpdateRect(UINT sx,UINT sy) + { + do_size_x(IDC_STATIC_STD,&info_static_std); + do_size_x(IDC_TITLE,&info_title); + do_size_x(IDC_ARTIST,&info_artist); + do_size_x(IDC_ALBUM,&info_album); + do_align_x(IDC_TRACK,&info_track); + do_size_x(IDC_GENRE,&info_genre); + do_size_x(IDC_COMMENT,&info_comment); + do_align_x(IDC_STATIC_TRACK,&info_static_track); + do_size_xy(IDC_STATIC_TAGS,&info_static_tags); + InfoDlgPanel::OnUpdateRect(sx,sy); + } + virtual BOOL DlgProc(UINT msg,WPARAM wp,LPARAM lp) + { + switch(msg) + { + case WM_COMMAND: + if (!STFU && (HIWORD(wp)==EN_CHANGE || HIWORD(wp)==CBN_EDITCHANGE || HIWORD(wp)==CBN_SELCHANGE)) + { + UINT n; + for(n=0;n<N_STD_TAGZ;n++) + { + if (LOWORD(wp)==std_tagz[n].id) + { + if (tag_bk[n]) {free(tag_bk[n]);tag_bk[n]=0;} + break; + } + } + } + break; + } + return InfoDlgPanel::DlgProc(msg,wp,lp); + } +public: + virtual void Clear() + { + int n; + for(n=0;n<N_STD_TAGZ;n++) + { + if (tag_bk[n]) + { + free(tag_bk[n]); + tag_bk[n]=0; + } + SetDlgItemText(wnd,std_tagz[n].id,L""); + } + InfoDlgPanel::Clear(); + + } + ~InfoDlgPanel_simple() + { + UINT n; + for(n=0;n<N_STD_TAGZ;n++) + { + if (tag_bk[n]) free(tag_bk[n]); + } + } + InfoDlgPanel_simple(HWND parent,HFONT foo) : InfoDlgPanel(IDD_INFO_PANEL_SIMPLE,parent,foo) + { + STFU=0; + memset(tag_bk,0,sizeof(tag_bk)); + UINT n; + MakeComboEdit(IDC_GENRE,ES_READONLY|ES_AUTOHSCROLL|WS_VISIBLE); + for(n=0;n<N_STD_TAGZ;n++) + { + HWND w=GetDlgItem(wnd,std_tagz[n].id); + SendMessage(w,WM_SETFONT,(WPARAM)font,0); + SendMessage(w,EM_SETREADONLY,1,0); + } + + GetChildRect(IDC_STATIC_STD,info_static_std); + GetChildRect(IDC_TITLE,info_title); + GetChildRect(IDC_ARTIST,info_artist); + GetChildRect(IDC_ALBUM,info_album); + GetChildRect(IDC_TRACK,info_track); + GetChildRect(IDC_GENRE,info_genre); + GetChildRect(IDC_COMMENT,info_comment); + GetChildRect(IDC_STATIC_TRACK,info_static_track); + GetChildRect(IDC_STATIC_TAGS,info_static_tags); + + genres_read(GetDlgItem(wnd,IDC_GENRE), genres_file); + } + + virtual void GetTags(OggTagData & tags) + { + genres_write(GetDlgItem(wnd,IDC_GENRE),genres_file); + UINT n; + for(n=0;n<N_STD_TAGZ;n++) + { + if (tag_bk[n]) + { + tags.AddTag(std_tagz[n].name,tag_bk[n]); + } + else + { + StringW t; + t.s_GetWindowText(GetDlgItem(wnd,std_tagz[n].id)); + if (t.Length()>0) + { + tags.AddTag(std_tagz[n].name,t); + } + } + } + InfoDlgPanel::GetTags(tags); + } + virtual void SetTag(wchar_t * name,wchar_t * value) + { + STFU=1; + UINT n; + for(n=0;n<N_STD_TAGZ;n++) + { + if (tag_bk[n]) continue; + if (!_wcsicmp(name,std_tagz[n].name)) + { + tag_bk[n]=_wcsdup(value); + SetDlgItemTextWrap(wnd,std_tagz[n].id,value); + STFU=0; + return; + } + } + STFU=0; + InfoDlgPanel::SetTag(name,value); + } +}; + +char * rstrcpy(char* s1,char* s2) +{ + while(s2 && *s2) *(s1++)=*(s2++); + return s1; +} + +static void _inline print_misc(VorbisFile * _vf,int link,char * out,int len) +{ + OggVorbis_File * vf=&_vf->vf; + char* p=out, kbps_str[16] = {0}; + double t=ov_time_total(vf,link); + vorbis_info * vi=ov_info(vf,link); + vorbis_comment * vc=ov_comment(vf,link); + if (!vi || !vc) {WASABI_API_LNGSTRING_BUF(IDS_FILE_ERROR,out,512);return;} + + StringCchPrintfA(kbps_str, 16, " %s\r\n", WASABI_API_LNGSTRING(IDS_KBPS)); + + if (t>0) + { + p=rstrcpy(p,WASABI_API_LNGSTRING(IDS_LENGTH)); + int h=(int)(t/3600.0); + if (h>0) + { + uitoa(h,p); + while(p && *p) p++; + *(p++)=':'; + } + int m=(int)(t/60.0)%60; +// if (m>0 || h>0) + { + sprintf(p,h>0 ? "%02u" : "%u",m); + while(p && *p) p++; + *(p++)=':'; + } + int s=(int)t%60; + //sprintf(p,(m>0 || h>0) ? "%02u" : "%u seconds",s); + sprintf(p,"%02d",s); + while(p && *p) p++; + p=rstrcpy(p,"\r\n"); + +// uitoa((int)(t*1000.0),p); +// while(p && *p) p++; +// p=rstrcpy(p," ms"); + UINT fs=_vf->FileSize(); + if (fs>0) + { + if (vf->links==1) + { + p=rstrcpy(p,WASABI_API_LNGSTRING(IDS_AVERAGE_BITRATE)); + uitoa((int)(((double)fs)/(t*125.0)),p); + while(p && *p) p++; + p=rstrcpy(p,kbps_str); + } + if (fs>0) + { + p=rstrcpy(p,WASABI_API_LNGSTRING(IDS_FILE_SIZE)); + + UINT fs1=fs/1000000; + UINT fs2=(fs/1000)%1000; + UINT fs3=fs%1000; + if (fs1) sprintf(p,"%u%03u%03u %s\r\n",fs1,fs2,fs3,WASABI_API_LNGSTRING(IDS_BYTES)); + else if (fs2) sprintf(p,"%u%03u %s\r\n",fs2,fs3,WASABI_API_LNGSTRING(IDS_BYTES)); + else sprintf(p,"%u %s\r\n",fs3,WASABI_API_LNGSTRING(IDS_BYTES)); + while(p && *p) p++; + } + } + } + if (vi->bitrate_nominal>0) + { + p=rstrcpy(p,WASABI_API_LNGSTRING(IDS_NOMINAL_BITRATE)); + uitoa(vi->bitrate_nominal/1000,p); + while(p && *p) p++; + p=rstrcpy(p,kbps_str); + } + if (vi->bitrate_lower>0) + { + p=rstrcpy(p,WASABI_API_LNGSTRING(IDS_MIN_BITRATE)); + uitoa(vi->bitrate_lower/1000,p); + while(p && *p) p++; + p=rstrcpy(p,kbps_str); + } + if (vi->bitrate_upper>0) + { + p=rstrcpy(p,WASABI_API_LNGSTRING(IDS_MAX_BITRATE)); + uitoa(vi->bitrate_upper/1000,p); + while(p && *p) p++; + p=rstrcpy(p,kbps_str); + } + + char tmp[32] = {0}, tmp2[32] = {0}, tmp3[32] = {0}, tmp4[32] = {0}, tmp5[32] = {0}, tmp6[8] = {0}; + StringCchPrintfA(p,len, "%s : %u\r\n" + "%s : %u %s\r\n" + "%s : %u\r\n" + "%s : %u\r\n" + "%s : \r\n%s", + WASABI_API_LNGSTRING_BUF(IDS_CHANNELS,tmp,32),vi->channels, + WASABI_API_LNGSTRING_BUF(IDS_SAMPLING_RATE,tmp2,32),vi->rate,WASABI_API_LNGSTRING_BUF(IDS_HZ,tmp6,8), + WASABI_API_LNGSTRING_BUF(IDS_SERIAL_NUMBER,tmp3,32),ov_serialnumber(vf,link), + WASABI_API_LNGSTRING_BUF(IDS_VERSION,tmp4,32),vi->version, + WASABI_API_LNGSTRING_BUF(IDS_VENDOR,tmp5,32),vc->vendor); +} + +class InfoDlg : public DlgBase +{ +private: + InfoDlg * next; + static InfoDlg* Instances; + OggTagData **tags; + char ** infos; + int n_streams,cur_stream; + StringW url; + HFONT ui_font; + BOOL is_adv; + InfoDlgPanel* panel; + RECT info_static_misc,info_misc,info_url,info_cancel,info_mode,info_static_cs,info_cs_next,info_cs_prev,info_hidespec; + + void calc_panel_rect(RECT &r) + { + RECT cr,cr1,cr2; + GetClientRect(wnd,&cr); + GetChildRect(IDC_STATIC_MISC,cr1); + GetChildRect(IDC_URL,cr2); + r.left=0; + r.top=cr2.bottom+1; + r.right=cr1.left-1; + r.bottom=cr.bottom; + } + void do_size() + { + if (panel) + { + RECT r; + calc_panel_rect(r); + panel->UpdateRect(r); + } + } + void do_panel(BOOL mode,int d_stream) + { + //if (panel && is_adv==mode && !d_stream) return; + if (panel) + { + if (mode!=is_adv) + { + delete panel; + panel=0; + } + else + { + panel->Clear(); + } + } + cur_stream+=d_stream; + if (cur_stream<0) cur_stream=0; + else if (cur_stream>=n_streams) cur_stream=n_streams-1; + is_adv=mode; + if (!panel) + { + panel = mode ? (InfoDlgPanel*) new InfoDlgPanel_adv(wnd,ui_font) : (InfoDlgPanel*) new InfoDlgPanel_simple(wnd,ui_font); + do_size(); + } + if (panel) + { + panel->SetTags(*tags[cur_stream],(BOOL)SendDlgItemMessage(wnd,IDC_HIDE_SPEC,BM_GETCHECK,0,0)); + } + SetDlgItemText(wnd,IDC_MODE_TOGGLE,WASABI_API_LNGSTRINGW((is_adv ? IDS_TO_SIMPLE_MODE : IDS_TO_ADVANCED_MODE))); + EnableWindow(GetDlgItem(wnd,IDC_PREV_STREAM),cur_stream>0); + EnableWindow(GetDlgItem(wnd,IDC_NEXT_STREAM),cur_stream<n_streams-1); + // TODO + SetDlgItemTextA(wnd,IDC_MISC,infos[cur_stream]); + } +protected: + virtual BOOL DlgProc(UINT msg,WPARAM wp,LPARAM lp) + { + switch(msg) + { + case WM_INITDIALOG: + if (n_streams<=1) + { + ShowWindow(GetDlgItem(wnd,IDC_STATIC_CS),SW_HIDE); + ShowWindow(GetDlgItem(wnd,IDC_PREV_STREAM),SW_HIDE); + ShowWindow(GetDlgItem(wnd,IDC_NEXT_STREAM),SW_HIDE); + } + + SendDlgItemMessage(wnd,IDC_HIDE_SPEC,BM_SETCHECK,cfg_hide_special_fields,0); + + do_panel(cfg_adv_info,0); + url.s_SetDlgItemText(wnd,IDC_URL); + GetChildRect(IDC_URL,info_url); + GetChildRect(IDC_STATIC_MISC,info_static_misc); + GetChildRect(IDC_MISC,info_misc); + GetChildRect(IDCANCEL,info_cancel); + GetChildRect(IDC_MODE_TOGGLE,info_mode); + GetChildRect(IDC_STATIC_CS,info_static_cs); + GetChildRect(IDC_NEXT_STREAM,info_cs_next); + GetChildRect(IDC_PREV_STREAM,info_cs_prev); + GetChildRect(IDC_HIDE_SPEC,info_hidespec); + + if (cfg_remember_infosize && cfg_infobox_sx>0 && cfg_infobox_sy>0) + { + int max_x=GetSystemMetrics(SM_CXSCREEN),max_y=GetSystemMetrics(SM_CYSCREEN); + if (cfg_infobox_x<0) cfg_infobox_x=0; + else if (cfg_infobox_x+cfg_infobox_sx>max_x) cfg_infobox_x=max_x-cfg_infobox_sx; + if (cfg_infobox_y<0) cfg_infobox_y=0; + else if (cfg_infobox_y+cfg_infobox_sy>max_y) cfg_infobox_y=max_y-cfg_infobox_sy; + + SetWindowPos(wnd,0,cfg_infobox_x,cfg_infobox_y,cfg_infobox_sx,cfg_infobox_sy,SWP_NOZORDER|SWP_NOACTIVATE); + } + + StringPrintfW(WASABI_API_LNGSTRINGW(IDS_OGG_VORBIS_INFO),(const WCHAR*)StringF2T_W((const WCHAR*)url)).s_SetWindowText(wnd); + return 1; + case WM_SIZE: + { + UINT sx=LOWORD(lp),sy=HIWORD(lp); + do_size_x(IDC_URL,&info_url); + do_align_x(IDC_STATIC_MISC,&info_static_misc); + do_align_x(IDC_MISC,&info_misc); + do_align_xy(IDCANCEL,&info_cancel); + do_align_xy(IDC_MODE_TOGGLE,&info_mode); + do_align_xy(IDC_STATIC_CS,&info_static_cs); + do_align_xy(IDC_PREV_STREAM,&info_cs_prev); + do_align_xy(IDC_NEXT_STREAM,&info_cs_next); + do_align_xy(IDC_HIDE_SPEC,&info_hidespec); + } + //RedrawWindow(wnd,0,0,RDW_INVALIDATE); + do_size(); + break; + case WM_SIZING: + do_sizing((UINT)wp,(RECT*)lp); + break; + case WM_COMMAND: + switch(wp) + { + case IDCANCEL: + endDialog(0); + break; + case IDC_MODE_TOGGLE: + do_panel(!is_adv,0); + break; + case IDC_PREV_STREAM: + do_panel(is_adv,-1); + break; + case IDC_NEXT_STREAM: + do_panel(is_adv,1); + break; + case IDC_HIDE_SPEC: + cfg_hide_special_fields=(int)SendMessage((HWND)lp,BM_GETCHECK,0,0); + do_panel(is_adv,0); + break; + } + break; + case WM_CLOSE: + endDialog(0); + break; + case WM_DESTROY: + if (!is_modeless)//fucko close + { + modal_ex_quit=1; + } + + { + RECT r; + GetWindowRect(wnd,&r); + cfg_infobox_sx=r.right-r.left; + cfg_infobox_sy=r.bottom-r.top; + cfg_infobox_x=r.left; + cfg_infobox_y=r.top; + } + break; + } + return 0; + } + virtual void myProcessMessage(MSG * msg) + { + if (!panel || !panel->isDialogMessage(msg)) + { + DlgBase::myProcessMessage(msg); + } + } + +public: + InfoDlg(VorbisFile * _vf,const wchar_t * _url) + : url(_url), is_adv(FALSE) + { + OggVorbis_File * vf=&_vf->vf; + n_streams=vf->links; + cur_stream=vf->current_link; + tags=(OggTagData**)malloc(n_streams*sizeof(void*)); + int n; + for(n=0;n<n_streams;n++) tags[n]=new OggTagData(ov_comment(vf,n)); + infos=(char**)malloc(sizeof(void*)*n_streams); + for(n=0;n<n_streams;n++) + { + int l = 512+(int)strlen(vf->vc->vendor); + infos[n]=(char*)malloc(l); + print_misc(_vf,n,infos[n],l); + } + + ui_font=CreateFontIndirect(&cfg_font.data); + panel=0; + next=0; + } + ~InfoDlg() + { + if (ui_font) DeleteObject((HGDIOBJ)ui_font); + cfg_adv_info=is_adv; + if (tags) + { + int n; + for(n=0;n<n_streams;n++) + delete tags[n]; + free(tags); + } + if (infos) + { + int n; + for(n=0;n<n_streams;n++) free(infos[n]); + free(infos); + } + + InfoDlg ** pp=&Instances,*p=Instances; + while(p) + { + if (p==this) + { + *pp=next; + break; + } + else {pp=&p->next;p=*pp;} + } + } + void Run(HWND parent,bool modeless) + { + next=Instances; + Instances=this;//HACK - prevent crash on shutdown (used to be only for modeless) + + if (modeless) + { + myCreateDialog(IDD_INFO_DLG_NEW,parent); + } + else myDialogBoxEx(IDD_INFO_DLG_NEW,parent); + } + +friend int RunInfoDlg(const in_char * url,HWND parent); +}; + +int RunInfoDlg(const in_char * url,HWND parent) +{ + static bool in_modal; + if (in_modal) return 1; + else in_modal=1; + VorbisFile * vf; + bool vf_global=0; + int ret=0; + StringW _url; + _url.AddString(url); + + { + InfoDlg * p=InfoDlg::Instances; + while(p) + { + if (!_wcsicmp(p->url,_url)) + { + ShowWindow(p->wnd,SW_SHOW); + SetActiveWindow(p->wnd); + return 0; + } + p=p->next; + } + } + + EnterCriticalSection(&sync); + if ((url==cur_file || !_wcsicmp(url,cur_file)) && theFile) {vf=theFile;vf_global=1;} + else + { + LeaveCriticalSection(&sync); + vf=VorbisFile::Create(url,1); + if (!vf) + { + in_modal=0; + return 0; + } + } + { + InfoDlg d(vf,_url); + if (vf_global) LeaveCriticalSection(&sync); + else delete vf; + d.Run(parent,0); + ret = !d.modal_ex_quit_val; + } + in_modal=0; + return ret; +} + +InfoDlg* InfoDlg::Instances=0; +/* end crappy PP dialog */ + +bool VorbisTagToWinampTag(wchar_t * tag, int len) +{ +#define TAG_ALIAS(b,a) if(!_wcsicmp(L ## a, tag)) { lstrcpynW(tag, L ## b, len); return true; } + TAG_ALIAS("title", "TITLE"); + TAG_ALIAS("artist", "ARTIST"); + TAG_ALIAS("album", "ALBUM"); + TAG_ALIAS("genre", "GENRE"); + TAG_ALIAS("comment", "COMMENT"); + TAG_ALIAS("year", "DATE"); + TAG_ALIAS("track", "TRACKNUMBER"); + TAG_ALIAS("albumartist", "ALBUMARTIST"); + TAG_ALIAS("composer", "COMPOSER"); + TAG_ALIAS("disc", "DISCNUMBER"); + TAG_ALIAS("publisher", "PUBLISHER"); + TAG_ALIAS("conductor", "CONDUCTOR"); + TAG_ALIAS("bpm", "BPM"); + return false; +#undef TAG_ALIAS +} + +bool WinampTagToVorbisTag(wchar_t * tag, int len) +{ +#define TAG_ALIAS(a,b) if(!_wcsicmp(L ## a, tag)) { lstrcpynW(tag, L ## b, len); return true; } + TAG_ALIAS("title", "TITLE"); + TAG_ALIAS("artist", "ARTIST"); + TAG_ALIAS("album", "ALBUM"); + TAG_ALIAS("genre", "GENRE"); + TAG_ALIAS("comment", "COMMENT"); + TAG_ALIAS("year", "DATE"); + TAG_ALIAS("track", "TRACKNUMBER"); + TAG_ALIAS("albumartist", "ALBUMARTIST"); + TAG_ALIAS("composer", "COMPOSER"); + TAG_ALIAS("disc", "DISCNUMBER"); + TAG_ALIAS("publisher", "PUBLISHER"); + TAG_ALIAS("conductor", "CONDUCTOR"); + TAG_ALIAS("bpm", "BPM"); + return false; +#undef TAG_ALIAS +} + +static INT_PTR CALLBACK ChildProc_Advanced(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { + static int sel=-1; + static int ismychange=0; + switch(msg) + { + case WM_NOTIFYFORMAT: + return NFR_UNICODE; + case WM_INITDIALOG: + { + #define ListView_InsertColumnW(hwnd, iCol, pcol) \ + (int)SNDMSG((hwnd), LVM_INSERTCOLUMNW, (WPARAM)(int)(iCol), (LPARAM)(const LV_COLUMNW *)(pcol)) + SetWindowLongPtr(hwndDlg,GWLP_USERDATA,lParam); + sel=-1; + HWND hwndlist = GetDlgItem(hwndDlg,IDC_LIST); + ListView_SetExtendedListViewStyle(hwndlist, LVS_EX_FULLROWSELECT); + LVCOLUMNW lvc = {0, }; + lvc.mask = LVCF_TEXT|LVCF_WIDTH; + lvc.pszText = WASABI_API_LNGSTRINGW(IDS_NAME); + lvc.cx = 82; + ListView_InsertColumnW(hwndlist, 0, &lvc); + lvc.pszText = WASABI_API_LNGSTRINGW(IDS_VALUE); + lvc.cx = 160; + ListView_InsertColumnW(hwndlist, 1, &lvc); + + Info *info = (Info *)lParam; + int n = info->GetNumMetadataItems(); + for(int i=0; i<n; i++) { + wchar_t key[512] = {0}; + wchar_t value[2048] = {0}; + info->EnumMetadata(i,key,512,value,2048); + if(value[0] && key[0]) { + LVITEMW lvi={LVIF_TEXT,i,0}; + lvi.pszText = key; + SendMessage(hwndlist,LVM_INSERTITEMW,0,(LPARAM)&lvi); + lvi.iSubItem=1; + lvi.pszText = (wchar_t*)value; + SendMessage(hwndlist,LVM_SETITEMW,0,(LPARAM)&lvi); + } + } + ListView_SetColumnWidth(hwndlist,0,(n?LVSCW_AUTOSIZE:LVSCW_AUTOSIZE_USEHEADER)); + ListView_SetColumnWidth(hwndlist,1,(n?LVSCW_AUTOSIZE:LVSCW_AUTOSIZE_USEHEADER)); + + SetDlgItemTextW(hwndDlg,IDC_NAME,L""); + SetDlgItemTextW(hwndDlg,IDC_VALUE,L""); + EnableWindow(GetDlgItem(hwndDlg,IDC_NAME),FALSE); + EnableWindow(GetDlgItem(hwndDlg,IDC_VALUE),FALSE); + EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON_DEL),FALSE); + } + break; + case WM_DESTROY: + { + HWND hwndlist = GetDlgItem(hwndDlg,IDC_LIST); + ListView_DeleteAllItems(hwndlist); + while(ListView_DeleteColumn(hwndlist,0)); + Info * info = (Info*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA); + delete info; + } + break; + case WM_USER: + if(wParam && lParam && !ismychange) + { + wchar_t * value = (wchar_t*)lParam; + wchar_t tag[100] = {0}; + lstrcpynW(tag,(wchar_t*)wParam,100); + WinampTagToVorbisTag(tag,100); + Info *info = (Info *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + if(!*value) + { + info->RemoveMetadata(tag); + if(!_wcsicmp(L"ALBUMARTIST",tag)) + { + // need to remove these two, also, or else it's gonna look like delete doesn't work + // if the file was tagged using these alternate fields + info->RemoveMetadata(L"ALBUM ARTIST"); + info->RemoveMetadata(L"ENSEMBLE"); + } + if(!_wcsicmp(L"PUBLISHER",tag)) + { + // need to remove this also, or else it's gonna look like delete doesn't work + // if the file was tagged using this alternate field + info->RemoveMetadata(L"ORGANIZATION"); + } + if(!_wcsicmp(L"DATE",tag)) + { + // need to remove this also, or else it's gonna look like delete doesn't work + // if the file was tagged using this alternate field + info->RemoveMetadata(L"YEAR"); + } + if(!_wcsicmp(L"TRACKNUMBER",tag)) + { + // need to remove this also, or else it's gonna look like delete doesn't work + // if the file was tagged using this alternate field + info->RemoveMetadata(L"TRACK"); + } + } + else + { + info->SetMetadata(tag,value); + } + + HWND hlist = GetDlgItem(hwndDlg,IDC_LIST); + int n = ListView_GetItemCount(hlist); + for(int i=0; i<n; i++) + { + wchar_t key[100]=L""; + LVITEMW lvi={LVIF_TEXT,i,0}; + lvi.pszText=key; + lvi.cchTextMax=100; + SendMessage(hlist,LVM_GETITEMW,0,(LPARAM)&lvi); + if(!_wcsicmp(key,tag)) + { + lvi.iSubItem = 1; + lvi.pszText = value; + SendMessage(hlist,LVM_SETITEMW,0,(LPARAM)&lvi); + if(!*value) + ListView_DeleteItem(hlist,i); + else if(ListView_GetItemState(hlist,i,LVIS_SELECTED)) + SetDlgItemTextW(hwndDlg,IDC_VALUE,value); + return 0; + } + } + // bew hew, not found + LVITEMW lvi={0,0x7FFFFFF0,0}; + n = (int)SendMessage(hlist,LVM_INSERTITEMW,0,(LPARAM)&lvi); + lvi.mask = LVIF_TEXT; + lvi.iItem = n; + lvi.iSubItem = 0; + lvi.pszText = tag; + SendMessage(hlist,LVM_SETITEMW,0,(LPARAM)&lvi); + lvi.iSubItem = 1; + lvi.pszText = value; + SendMessage(hlist,LVM_SETITEMW,0,(LPARAM)&lvi); + } + break; + case WM_NOTIFY: + { + LPNMHDR l=(LPNMHDR)lParam; + if(l->idFrom==IDC_LIST && l->code == LVN_KEYDOWN) { + if((((LPNMLVKEYDOWN)l)->wVKey) == VK_DELETE){ + int selitem = ListView_GetNextItem(l->hwndFrom,-1,LVNI_SELECTED|LVNI_FOCUSED); + if(selitem != -1) + SendMessage(hwndDlg,WM_COMMAND,MAKEWPARAM(IDC_BUTTON_DEL,BN_CLICKED),(LPARAM)GetDlgItem(hwndDlg,IDC_BUTTON_DEL)); + } + } + else if(l->idFrom==IDC_LIST && l->code == LVN_ITEMCHANGED) { + LPNMLISTVIEW lv=(LPNMLISTVIEW)lParam; + if(lv->uNewState & LVIS_SELECTED) { + int n = lv->iItem; + LVITEMW lvi={LVIF_TEXT,lv->iItem,0}; + wchar_t key[100] = {0}; + wchar_t value[1024] = {0}; + lvi.pszText=key; + lvi.cchTextMax=100; + SendMessage(l->hwndFrom,LVM_GETITEMW,0,(LPARAM)&lvi); + lvi.pszText=value; + lvi.cchTextMax=1024; + lvi.iSubItem=1; + SendMessage(l->hwndFrom,LVM_GETITEMW,0,(LPARAM)&lvi); + SetDlgItemTextW(hwndDlg,IDC_NAME,key); + SetDlgItemTextW(hwndDlg,IDC_VALUE,value); + sel = n; + EnableWindow(GetDlgItem(hwndDlg,IDC_NAME),TRUE); + EnableWindow(GetDlgItem(hwndDlg,IDC_VALUE),TRUE); + EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON_DEL),TRUE); + } + if(lv->uOldState & LVIS_SELECTED) { + sel = -1; + SetDlgItemTextW(hwndDlg,IDC_NAME,L""); + SetDlgItemTextW(hwndDlg,IDC_VALUE,L""); + EnableWindow(GetDlgItem(hwndDlg,IDC_NAME),FALSE); + EnableWindow(GetDlgItem(hwndDlg,IDC_VALUE),FALSE); + EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON_DEL),FALSE); + } + } + } + break; + case WM_COMMAND: + switch(LOWORD(wParam)) { + case IDOK: + { + Info * info = (Info*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA); + if (!info->Save()) + { + MessageBox(hwndDlg, + L"Cannot save metadata: Error writing file or file is read-only.", + L"Error saving metadata.", + MB_OK); + } + } + break; + case IDC_NAME: + case IDC_VALUE: + if(HIWORD(wParam) == EN_CHANGE && sel>=0) { + wchar_t key[100] = {0}; + wchar_t value[1024] = {0}; + LVITEMW lvi={LVIF_TEXT,sel,0}; + GetDlgItemTextW(hwndDlg,IDC_NAME,key,100); + GetDlgItemTextW(hwndDlg,IDC_VALUE,value,1024); + lvi.pszText=key; + lvi.cchTextMax=100; + SendMessage(GetDlgItem(hwndDlg,IDC_LIST),LVM_SETITEMW,0,(LPARAM)&lvi); + lvi.pszText=value; + lvi.cchTextMax=1024; + lvi.iSubItem=1; + SendMessage(GetDlgItem(hwndDlg,IDC_LIST),LVM_SETITEMW,0,(LPARAM)&lvi); + VorbisTagToWinampTag(key,100); + ismychange=1; + SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)key,(WPARAM)value); + ismychange=0; + } + else if(HIWORD(wParam) == EN_KILLFOCUS && sel>=0) { + wchar_t key[100] = {0}; + wchar_t value[1024] = {0}; + GetDlgItemTextW(hwndDlg,IDC_NAME,key,100); + GetDlgItemTextW(hwndDlg,IDC_VALUE,value,1024); + Info *info = (Info *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + wchar_t oldkey[100]=L""; + bool newitem=true; + if(sel < info->GetNumMetadataItems()) { + info->EnumMetadata(sel,oldkey,100,0,0); + newitem=false; + } + + if(!newitem && wcscmp(oldkey,key)) { // key changed + info->SetTag(sel,key); + } else { + info->SetMetadata(key,value); + } + VorbisTagToWinampTag(key,100); + ismychange=1; + SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)key,(WPARAM)value); + ismychange=0; + } + break; + case IDC_BUTTON_DEL: + if(sel >= 0) { + wchar_t tag[100] = {0}; + GetDlgItemTextW(hwndDlg,IDC_NAME,tag,100); + SetDlgItemTextW(hwndDlg,IDC_NAME,L""); + SetDlgItemTextW(hwndDlg,IDC_VALUE,L""); + EnableWindow(GetDlgItem(hwndDlg,IDC_NAME),FALSE); + EnableWindow(GetDlgItem(hwndDlg,IDC_VALUE),FALSE); + EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON_DEL),FALSE); + Info *info = (Info *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + if(sel < info->GetNumMetadataItems()) + info->RemoveMetadata(sel); + ListView_DeleteItem(GetDlgItem(hwndDlg,IDC_LIST),sel); + sel=-1; + VorbisTagToWinampTag(tag,100); + ismychange=1; + SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)tag,(WPARAM)L""); + ismychange=0; + } + break; + case IDC_BUTTON_DELALL: + ListView_DeleteAllItems(GetDlgItem(hwndDlg,IDC_LIST)); + SetDlgItemTextW(hwndDlg,IDC_NAME,L""); + SetDlgItemTextW(hwndDlg,IDC_VALUE,L""); + EnableWindow(GetDlgItem(hwndDlg,IDC_NAME),FALSE); + EnableWindow(GetDlgItem(hwndDlg,IDC_VALUE),FALSE); + EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON_DEL),FALSE); + sel=-1; + { + Info *info = (Info *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + int n = info->GetNumMetadataItems(); + while(n>0) { + --n; + wchar_t tag[100] = {0}; + info->EnumMetadata(n,tag,100,0,0); + VorbisTagToWinampTag(tag,100); + ismychange=1; + SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)tag,(WPARAM)L""); + ismychange=0; + info->RemoveMetadata(n); + } + } + break; + case IDC_BUTTON_ADD: + { + HWND hwndlist = GetDlgItem(hwndDlg,IDC_LIST); + LVITEMW lvi={0,0x7FFFFFF0,0}; + int n = (int)SendMessage(hwndlist,LVM_INSERTITEMW,0,(LPARAM)&lvi); + ListView_SetItemState(hwndlist,n,LVIS_SELECTED,LVIS_SELECTED); + } + break; + } + break; + } + return 0; +} + +extern "C" +{ + // return 1 if you want winamp to show it's own file info dialogue, 0 if you want to show your own (via In_Module.InfoBox) + // if returning 1, remember to implement winampGetExtendedFileInfo("formatinformation")! + __declspec(dllexport) int winampUseUnifiedFileInfoDlg(const wchar_t * fn) + { + if (PathIsURLW(fn)) + return 0; + return 1; + } + + // should return a child window of 513x271 pixels (341x164 in msvc dlg units), or return NULL for no tab. + // Fill in name (a buffer of namelen characters), this is the title of the tab (defaults to "Advanced"). + // filename will be valid for the life of your window. n is the tab number. This function will first be + // called with n == 0, then n == 1 and so on until you return NULL (so you can add as many tabs as you like). + // The window you return will recieve WM_COMMAND, IDOK/IDCANCEL messages when the user clicks OK or Cancel. + // when the user edits a field which is duplicated in another pane, do a SendMessage(GetParent(hwnd),WM_USER,(WPARAM)L"fieldname",(LPARAM)L"newvalue"); + // this will be broadcast to all panes (including yours) as a WM_USER. + __declspec(dllexport) HWND winampAddUnifiedFileInfoPane(int n, const wchar_t * filename, HWND parent, wchar_t *name, size_t namelen) + { + if(n == 0) { // add first pane + SetPropW(parent,L"INBUILT_NOWRITEINFO", (HANDLE)1); + Info *info = new Info(filename); + if(info->Error()) + { + delete info; + return NULL; + } + return WASABI_API_CREATEDIALOGPARAMW(IDD_INFO,parent,ChildProc_Advanced,(LPARAM)info); + } + return NULL; + } +};
\ No newline at end of file diff --git a/Src/Plugins/Input/in_vorbis/localfile.cpp b/Src/Plugins/Input/in_vorbis/localfile.cpp new file mode 100644 index 00000000..7f163f7d --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/localfile.cpp @@ -0,0 +1,260 @@ +#include "main.h" +#include "../nu/AutoChar.h" + +extern CfgInt cfg_fullbuf; + +int VorbisFile::_f_close(void *) {return 0;} + +int VorbisFile::_f_seek(void* rs,__int64 offset,int whence) +{ + return ((VorbisFile*)rs)->f_seek(offset,whence); +} + +size_t VorbisFile::_f_read(void* ptr,size_t size,size_t nmemb,void * rs) +{ + return ((VorbisFile*)rs)->f_read((UINT)(size*nmemb),ptr); +} + +long VorbisFile::_f_tell(void* rs) +{ + return ((VorbisFile*)rs)->f_tell(); +} + +ov_callbacks VorbisFile::oc={_f_read,_f_seek,_f_close,_f_tell}; + +static __int64 Seek64(HANDLE hf, __int64 distance, DWORD MoveMethod) +{ + LARGE_INTEGER li; + + li.QuadPart = distance; + + li.LowPart = SetFilePointer (hf, li.LowPart, &li.HighPart, MoveMethod); + + if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) + { + li.QuadPart = -1; + } + + return li.QuadPart; +} + +static __int64 FileSize64(HANDLE file) +{ + LARGE_INTEGER position; + position.QuadPart=0; + position.LowPart = GetFileSize(file, (LPDWORD)&position.HighPart); + + if (position.LowPart == INVALID_FILE_SIZE && GetLastError() != NO_ERROR) + return INVALID_FILE_SIZE; + else + return position.QuadPart; +} + +class VorbisFile_Local : public VorbisFile +{ +private: + HANDLE hFile; +protected: + int f_seek(__int64 offset,int whence) + { + if(whence==SEEK_SET) offset+=baseoffs; + if (Seek64(hFile,offset,whence) != INVALID_SET_FILE_POINTER) return 0; + else return -1; + } + + size_t f_read(UINT siz,void * ptr) + { + DWORD bw=0; + ReadFile(hFile,ptr,siz,&bw,0); + return bw; + } + + UINT f_tell() + { + return (UINT)(SetFilePointer(hFile,0,0,FILE_CURRENT)-baseoffs); + } + + UINT FileSize() + { + return (UINT)(FileSize64(hFile)-baseoffs); + } + +public: + virtual int GetType() {return TYPE_LOCAL;} + VorbisFile_Local(HANDLE f,const wchar_t * u,bool is_info) : VorbisFile(u,is_info) {hFile=f;} + ~VorbisFile_Local() {CloseHandle(hFile);} +}; + +class VorbisFile_Mem : public VorbisFile +{ + BYTE * block; + UINT size,ptr; +protected: + int f_seek(__int64 offset,int whence) + { + switch(whence) + { + case SEEK_SET: + ptr=(UINT)(offset+baseoffs); + break; + case SEEK_CUR: + ptr+=(UINT)offset; + break; + case SEEK_END: + ptr=size+whence; + break; + } + if (ptr<=size) return 0; + else {ptr=size;return -1;} + } + + size_t f_read(UINT siz,void * out) + { + UINT d=size-ptr; + if (d>siz) d=siz; + memcpy(out,block+ptr,d); + ptr+=d; + return d; + } + + UINT f_tell() + { + return (UINT)(ptr-baseoffs); + } + + UINT FileSize() + { + return (UINT)(size-baseoffs); + } + +public: + virtual int GetType() {return TYPE_LOCAL;} + + VorbisFile_Mem(HANDLE f,const wchar_t * u,bool is_info) : VorbisFile(u,is_info) + { + size=GetFileSize(f,0); + ptr=0; + block=(BYTE*)malloc(size); + DWORD br = 0; + ReadFile(f,block,size,&br,0); + CloseHandle(f); + } + + ~VorbisFile_Mem() {free(block);} +}; + +VorbisFile * VorbisFile::Create(const wchar_t *url, bool is_info) +{ + VorbisFile * r; + if (PathIsURLW(url)) + { + if (is_info) return 0; + r=Create_HTTP(AutoChar(url),is_info); + } + else + { + __int64 baseoffs=0; + HANDLE f=CreateFileW(url,GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE,0,OPEN_EXISTING,0,0); + if (f==INVALID_HANDLE_VALUE) return 0; + { + DWORD dw = 0, br = 0; + ReadFile(f,&dw,4,&br,0); + if(br==4 && dw=='SggO') + { + SetFilePointer(f,0,0,FILE_BEGIN); + } + else if(br==4 && dw=='FFIR') + { + //RIFF file + DWORD wavhdr = 0, nb = 0; + SetFilePointer(f,4,0,FILE_CURRENT); + ReadFile(f,&wavhdr,4,&nb,0); + if(nb!=4 || wavhdr!='EVAW') + { + goto abort; + } + + //find data starting point + char tmp[1024] = {0}; + ReadFile(f,&tmp,1024,&nb,0); + for(int i=0;i<1020;i++) + if(tmp[i]=='d'&&tmp[i+1]=='a'&&tmp[i+2]=='t'&&tmp[i+3]=='a') + { + baseoffs=i+12+8; + Seek64(f, baseoffs, FILE_BEGIN); + } + + if(!baseoffs) goto abort; + } + else + { + abort: + CloseHandle(f); + return 0; + } + } + + r=cfg_fullbuf ? (VorbisFile*)new VorbisFile_Mem(f,url,is_info) : (VorbisFile*)new VorbisFile_Local(f,url,is_info); + r->setBaseOffset(baseoffs); + } + if (r && !r->init()) + { + delete r; + r=0; + } + return r; +} + +bool VorbisFile::init() +{ + if (ov_open_callbacks(this,&vf,0,0,oc)) return 0; + //TODO bitrate + UINT siz=FileSize(); + double len=Length(); + if (siz>0 && len>0) + { + UINT divisor = (UINT)(len*125.0); + if (divisor) + avg_kbps=siz/divisor; + } + + post_init(); + return 1; +} + +int is_http(const char* url) +{ + return (!_strnicmp(url,"http://",7) || !_strnicmp(url,"https://",8)); +} + +void VorbisFile::set_meta(const vorbis_comment * vc,int links) +{ + if (links == vf.links) + { + int n; + for(n=0;n<links;n++) + { + vorbis_comment_clear(vf.vc+n); + /* + extern void vorbis_comment_init(vorbis_comment *vc); + extern void vorbis_comment_add(vorbis_comment *vc, char *comment); + extern void vorbis_comment_add_tag(vorbis_comment *vc,char *tag, char *contents); + extern char *vorbis_comment_query(vorbis_comment *vc, char *tag, int count); + extern int vorbis_comment_query_count(vorbis_comment *vc, char *tag); + extern void vorbis_comment_clear(vorbis_comment *vc); + */ + } + _ogg_free(vf.vc); + vf.vc = (vorbis_comment*) _ogg_calloc(links,sizeof(vorbis_comment)); + for(n=0;n<links;n++) + { + vorbis_comment_init(vf.vc+n); + int c; + for(c=0;c<vc[n].comments;c++) + { + vorbis_comment_add(vf.vc+n,vc[n].user_comments[c]); + } + vf.vc[n].vendor = _strdup(vc[n].vendor); + } + } +}
\ No newline at end of file diff --git a/Src/Plugins/Input/in_vorbis/main.h b/Src/Plugins/Input/in_vorbis/main.h new file mode 100644 index 00000000..79c09dae --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/main.h @@ -0,0 +1,220 @@ +#ifndef IN_VORBIS_MAIN_H +#define IN_VORBIS_MAIN_H + +#define WINSOCK_API_LINKAGE + +#ifndef STRICT +#define STRICT +#endif + +#include <windows.h> + +extern int (*warand)(); +extern float (*warandf)(); + +inline void * z_malloc(int x) +{ + void* foo=malloc(x); + if (foo) memset(foo,0,x); + return foo; +} +#include <shlwapi.h> +#include <malloc.h> +#define uitoa(x,y) _itoa(x,y,10) +#define atoui atoi + +#include <vorbis\vorbisfile.h> +#include "c_string.h" +#include "../Winamp/in2.h" + +extern In_Module mod; + +#include "resource.h" + +#define VER L"1.79" +#define _NAME "Nullsoft Vorbis Decoder" + +extern "C" +{ + extern const char *INI_FILE; + extern const wchar_t *INI_DIRECTORY; +} +class CfgVar +{ +private: + String name; + CfgVar * next; + static CfgVar * list; +public: + + static void ReadConfig(); + static void WriteConfig(); + + //helpers + static bool read_struct(const char *inifile, const char *section, const char * name,void * ptr,UINT size); + static void write_struct(const char *inifile, const char *section, const char * name,void * ptr,UINT size); + static void write_int(const char *inifile, const char *section, const char * name,int val); + static int read_int(const char *inifile, const char *section,const char * name,int def); + +protected: + CfgVar(const char * n) : name(n) {next=list;list=this;} + virtual void Read(const char * name)=0; + virtual void Write(const char * name)=0; +}; + +class CfgInt : private CfgVar +{ +private: + int def,value; +public: + CfgInt(const char * name,int _def) : CfgVar(name) {value=def=_def;} + inline int operator=(int x) {value=x;return value;} + inline operator int() {return value;} +private: + virtual void Read(const char * name); + virtual void Write(const char * name); +}; + +class CfgString : private CfgVar, public StringW +{ +private: + StringW def; +public: + CfgString(const char * name,const char * _def) : CfgVar(name), StringW(_def), def(_def) {} +private: + virtual void Read(const char * name); + virtual void Write(const char * name); +}; + +template<class T> +class CfgStructT : private CfgVar +{ +public: + T data; + CfgStructT(const char * name) : CfgVar(name) {} +private: + void Read(const char * name) { read_struct(INI_FILE, "in_vorbis",name,&data,sizeof(data));} + void Write(const char * name) {if (IsValueDefault()) WritePrivateProfileStringA("in_vorbis", name, 0, INI_FILE); else write_struct(INI_FILE, "in_vorbis", name, &data, sizeof(data));} +protected: + virtual bool IsValueDefault() {return 0;} +}; + + +class CfgFont : public CfgStructT<LOGFONT> +{ +private: + void get_def(LOGFONT * f) {memset(f,0,sizeof(LOGFONT));GetObject(GetStockObject(DEFAULT_GUI_FONT),sizeof(LOGFONT),f);} + virtual bool IsValueDefault() + { + LOGFONT t; + get_def(&t); + return !memcmp(&data,&t,sizeof(LOGFONT)); + } +public: + CfgFont(const char * name) : CfgStructT<LOGFONT>(name) + { + get_def(&data); + } +}; + +extern int32_t priority_tab[7]; +extern HINSTANCE hIns; + +extern CfgString cfg_ssave_format,cfg_dumpdir; + +int is_http(const char* url); + +class VorbisFile +{ +protected: + virtual int f_seek(__int64 offset,int whence)=0; + virtual size_t f_read(UINT siz,void * ptr)=0; + virtual UINT f_tell()=0; + static int _f_close(void *); + static int _f_seek(void* rs,__int64 offset,int whence); + static size_t _f_read(void* ptr,size_t size,size_t nmemb,void * rs); + static long _f_tell(void* rs); + static ov_callbacks oc; + static VorbisFile * Create_HTTP(const char * url,bool is_info); + VorbisFile(const wchar_t * u, bool is_info) : url(u) {memset(&vf,0,sizeof(vf));stopping=0;abort_prebuf=0;avg_kbps=0;use_prebuf=0;primary=!is_info; baseoffs=0;} + bool init(); + virtual void post_init() {}; + UINT avg_kbps; + bool Aborting(); + + __int64 baseoffs; +public: + enum {TYPE_LOCAL,TYPE_HTTP}; + virtual int GetType()=0; + virtual bool IsLive() {return 0;} + virtual void do_prebuf() {use_prebuf=1;abort_prebuf=0;}; + StringW url; + String withlp; + String stream_title; + bool stopping,abort_prebuf,use_prebuf; + bool primary;//display status messages or not + OggVorbis_File vf; + UINT get_avg_bitrate() + { + if (avg_kbps>0) return avg_kbps; + vorbis_info * vi=ov_info(&vf,-1); + if (!vi) return 0; + return vi->bitrate_nominal/1000; + } + + const char* get_meta(const char* tag,UINT c); + void set_meta(const vorbis_comment * vc,int links); + + static VorbisFile * Create(const wchar_t * url,bool is_info); + + double Length() {return ov_time_total(&vf,-1);} + double GetPos() {return ov_time_tell(&vf);} + int Seek(double p); + void Status(const wchar_t * zzz); + virtual UINT FileSize()=0; + + virtual ~VorbisFile() {ov_clear(&vf);} + virtual void Idle() {Sleep(10);} + + virtual void setBaseOffset(__int64 offs) { baseoffs=offs; } + + float GetGain(); +}; + +extern VorbisFile * theFile; + +extern StringW cur_file; + +extern CRITICAL_SECTION sync; + +BOOL modify_file(const wchar_t* url,const vorbis_comment * comments,int links); +void winampGetExtendedFileInfoW_Cleanup(void); +void UpdateFileTimeChanged(const wchar_t *fn); +void do_cfg(int s); +bool KeywordMatch(const char *mainString, const char *keyword); + +class Info +{ +public: + Info(const wchar_t *filename); + ~Info(); + bool Save(); + int Error() { return vc==0?1:0; } + int GetNumMetadataItems(); + void EnumMetadata(int n,wchar_t *key,int keylen, wchar_t *val, int vallen); + void RemoveMetadata(wchar_t * key); + void RemoveMetadata(int n); + void SetMetadata(wchar_t *key, wchar_t *val); + void SetMetadata(int n, wchar_t *key, wchar_t *val); + void SetTag(int n,wchar_t *key); // changes the key name +private: + const wchar_t *filename; + vorbis_comment * vc; + int numstreams, stream; +}; + +// {B6CB4A7C-A8D0-4c55-8E60-9F7A7A23DA0F} +static const GUID playbackConfigGroupGUID = + { 0xb6cb4a7c, 0xa8d0, 0x4c55, { 0x8e, 0x60, 0x9f, 0x7a, 0x7a, 0x23, 0xda, 0xf } }; + +#endif //IN_VORBIS_MAIN_H
\ No newline at end of file diff --git a/Src/Plugins/Input/in_vorbis/mkv_vorbis_decoder.cpp b/Src/Plugins/Input/in_vorbis/mkv_vorbis_decoder.cpp new file mode 100644 index 00000000..39f1c1c3 --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/mkv_vorbis_decoder.cpp @@ -0,0 +1,194 @@ +#include "mkv_vorbis_decoder.h" +#include "../nsmkv/Lacing.h" +#include "../nsmkv/Cluster.h" +#include <math.h> + + +int MKVDecoderCreator::CreateAudioDecoder(const char *codec_id, + const nsmkv::TrackEntryData *track_entry_data, const nsmkv::AudioData *audio_data, + unsigned int preferred_bits, unsigned int max_channels, bool floating_point, + ifc_mkvaudiodecoder **decoder) +{ + if (!strcmp(codec_id, "A_VORBIS")) + { + MKVVorbis *vorbis = new MKVVorbis; + vorbis_info_init(&vorbis->info); + vorbis_comment_init(&vorbis->comment); + nsmkv::LacingState lacing_state; + if (nsmkv::Lacing::GetState(nsmkv::BlockBinary::XIPH_LACING, (const uint8_t *)track_entry_data->codec_private, track_entry_data->codec_private_len, &lacing_state)) + { + const uint8_t *frame; + size_t frame_len; + uint16_t frame_number=0; + while (nsmkv::Lacing::GetFrame(frame_number, (const uint8_t *)track_entry_data->codec_private, track_entry_data->codec_private_len, &frame, &frame_len, &lacing_state)) + { + ogg_packet packet = {const_cast<uint8_t *>(frame), (long)frame_len, (frame_number==0), 0, 0 /*-1?*/, vorbis->packet_number++}; + int ret = vorbis_synthesis_headerin(&vorbis->info, &vorbis->comment, &packet); + if (ret != 0) + goto bail; + frame_number++; + } + if (vorbis_synthesis_init(&vorbis->dsp, &vorbis->info) == 0 + && vorbis_block_init(&vorbis->dsp, &vorbis->block) == 0) + { + vorbis->bps = preferred_bits?preferred_bits:16; + *decoder = vorbis; + return CREATEDECODER_SUCCESS; + } + } + +bail: + delete vorbis; + return CREATEDECODER_FAILURE; + } + + return CREATEDECODER_NOT_MINE; +} + + +#define CBCLASS MKVDecoderCreator +START_DISPATCH; +CB(CREATE_AUDIO_DECODER, CreateAudioDecoder) +END_DISPATCH; +#undef CBCLASS + +MKVVorbis::MKVVorbis() +{ + bps=16; + packet_number=0; +} + +#define PA_CLIP_( val, min, max )\ + { val = ((val) < (min)) ? (min) : (((val) > (max)) ? (max) : (val)); } + +#if defined(_M_IX86) +static __inline long float_to_long(double t) +{ + long r; + __asm fld t + __asm fistp r + return r; +} +#else +#define float_to_long(x) ((long)( x )) +#endif + +inline static void clip(double &x, double a, double b) +{ + double x1 = fabs (x - a); + double x2 = fabs (x - b); + x = x1 + (a + b); + x -= x2; + x *= 0.5; +} + +static void Float32_To_Int24_Clip(void *destinationBuffer, void *sourceBuffer, size_t count, size_t channels, double gain) +{ + float *src = (float*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + gain*=65536.*32768.; + while ( count-- ) + { + /* convert to 32 bit and drop the low 8 bits */ + double scaled = *src * gain; + clip( scaled, -2147483648., 2147483647.); + signed long temp = (signed long) scaled; + + dest[0] = (unsigned char)(temp >> 8); + dest[1] = (unsigned char)(temp >> 16); + dest[2] = (unsigned char)(temp >> 24); + + src++; + dest += 3*channels; + } +} + +static void Float32_To_Int16_Clip(void *destinationBuffer, void *sourceBuffer, size_t count, size_t channels, double gain) +{ + float *src = (float*)sourceBuffer; + signed short *dest = (signed short*)destinationBuffer; + + gain*=32768.0; + while ( count-- ) + { + long samp = float_to_long((*src) * gain/* - 0.5*/); + + PA_CLIP_( samp, -0x8000, 0x7FFF ); + *dest = (signed short) samp; + + src ++; + dest += channels; + } +} + +int MKVVorbis::DecodeBlock(void *inputBuffer, size_t inputBufferBytes, void *outputBuffer, size_t *outputBufferBytes) +{ + uint8_t *out = (uint8_t *)outputBuffer; + ogg_packet packet = {(uint8_t *)inputBuffer, (long)inputBufferBytes, 0, 0, 0 -1, packet_number++}; + int ret = vorbis_synthesis(&block, &packet); + if (ret == 0) + { + vorbis_synthesis_blockin(&dsp,&block); + long channels = info.channels; + float **pcm; + int samples = vorbis_synthesis_pcmout(&dsp, &pcm); + if (samples) + { + switch(bps) + { + case 16: + for(int i=0;i<channels;i++) + { + Float32_To_Int16_Clip(out, pcm[i], samples, channels, 1.0 /*gain*/); + out+=2; + } + break; + case 24: + for(int i=0;i<channels;i++) + { + Float32_To_Int24_Clip(out, pcm[i], samples, channels, 1.0 /*gain*/); + out+=3; + } + break; + } + } + *outputBufferBytes = samples*channels*bps/8; + // let the decoder know we're processed them + vorbis_synthesis_read(&dsp,samples); + return MKV_SUCCESS; + } + return MKV_FAILURE; +} + +int MKVVorbis::GetOutputProperties(unsigned int *sampleRate, unsigned int *channels, unsigned int *bitsPerSample, bool *isFloat) +{ + *sampleRate = info.rate; + *channels = info.channels; + *bitsPerSample = bps; + *isFloat = false; // TODO + return MKV_SUCCESS; +} + +void MKVVorbis::Flush() +{ + vorbis_synthesis_restart(&dsp); +} + +void MKVVorbis::Close() +{ + // TODO: benski> verify + vorbis_info_clear(&info); + vorbis_comment_clear(&comment); + vorbis_dsp_clear(&dsp); + vorbis_block_clear(&block); + delete this; +} +#define CBCLASS MKVVorbis +START_DISPATCH; +//CB(OUTPUT_FRAME_SIZE, OutputFrameSize) +CB(GET_OUTPUT_PROPERTIES, GetOutputProperties) +CB(DECODE_BLOCK, DecodeBlock) +VCB(FLUSH, Flush) +VCB(CLOSE, Close) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/Plugins/Input/in_vorbis/mkv_vorbis_decoder.h b/Src/Plugins/Input/in_vorbis/mkv_vorbis_decoder.h new file mode 100644 index 00000000..0893ab25 --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/mkv_vorbis_decoder.h @@ -0,0 +1,41 @@ +#pragma once +#include "../in_mkv/ifc_mkvaudiodecoder.h" +#include "../in_mkv/svc_mkvdecoder.h" +#include <vorbis/codec.h> + +// {6058D315-2F08-4b2f-903E-4C2E6B5EFFA9} +static const GUID mkv_vorbis_guid = +{ 0x6058d315, 0x2f08, 0x4b2f, { 0x90, 0x3e, 0x4c, 0x2e, 0x6b, 0x5e, 0xff, 0xa9 } }; + + +class MKVDecoderCreator : public svc_mkvdecoder +{ +public: + static const char *getServiceName() { return "Vorbis MKV Decoder"; } + static GUID getServiceGuid() { return mkv_vorbis_guid; } + int CreateAudioDecoder(const char *codec_id, + const nsmkv::TrackEntryData *track_entry_data, const nsmkv::AudioData *audio_data, + unsigned int preferred_bits, unsigned int max_channels, bool floating_point, + ifc_mkvaudiodecoder **decoder); +protected: + RECVS_DISPATCH; +}; + +class MKVVorbis : public ifc_mkvaudiodecoder +{ +public: + MKVVorbis(); + int DecodeBlock(void *inputBuffer, size_t inputBufferBytes, void *outputBuffer, size_t *outputBufferBytes); + int GetOutputProperties(unsigned int *sampleRate, unsigned int *channels, unsigned int *bitsPerSample, bool *isFloat); + void Flush(); + void Close(); +//private: + unsigned int bps; + vorbis_info info; + vorbis_dsp_state dsp; + vorbis_block block; + vorbis_comment comment; + ogg_int64_t packet_number; +protected: + RECVS_DISPATCH; +};
\ No newline at end of file diff --git a/Src/Plugins/Input/in_vorbis/oggdrop/112.png b/Src/Plugins/Input/in_vorbis/oggdrop/112.png Binary files differnew file mode 100644 index 00000000..4799bd0f --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/oggdrop/112.png diff --git a/Src/Plugins/Input/in_vorbis/oggdrop/113.png b/Src/Plugins/Input/in_vorbis/oggdrop/113.png Binary files differnew file mode 100644 index 00000000..4cd9b4ac --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/oggdrop/113.png diff --git a/Src/Plugins/Input/in_vorbis/oggdrop/114.png b/Src/Plugins/Input/in_vorbis/oggdrop/114.png Binary files differnew file mode 100644 index 00000000..88192596 --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/oggdrop/114.png diff --git a/Src/Plugins/Input/in_vorbis/oggdrop/115.png b/Src/Plugins/Input/in_vorbis/oggdrop/115.png Binary files differnew file mode 100644 index 00000000..60c94b0b --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/oggdrop/115.png diff --git a/Src/Plugins/Input/in_vorbis/oggdrop/116.png b/Src/Plugins/Input/in_vorbis/oggdrop/116.png Binary files differnew file mode 100644 index 00000000..1d6fb8a2 --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/oggdrop/116.png diff --git a/Src/Plugins/Input/in_vorbis/oggdrop/117.png b/Src/Plugins/Input/in_vorbis/oggdrop/117.png Binary files differnew file mode 100644 index 00000000..135963e7 --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/oggdrop/117.png diff --git a/Src/Plugins/Input/in_vorbis/oggdrop/118.png b/Src/Plugins/Input/in_vorbis/oggdrop/118.png Binary files differnew file mode 100644 index 00000000..57a3a1ac --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/oggdrop/118.png diff --git a/Src/Plugins/Input/in_vorbis/oggdrop/119.png b/Src/Plugins/Input/in_vorbis/oggdrop/119.png Binary files differnew file mode 100644 index 00000000..a3baf233 --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/oggdrop/119.png diff --git a/Src/Plugins/Input/in_vorbis/oggdrop/120.png b/Src/Plugins/Input/in_vorbis/oggdrop/120.png Binary files differnew file mode 100644 index 00000000..fb6a3ce9 --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/oggdrop/120.png diff --git a/Src/Plugins/Input/in_vorbis/oggdrop/121.png b/Src/Plugins/Input/in_vorbis/oggdrop/121.png Binary files differnew file mode 100644 index 00000000..23ff9813 --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/oggdrop/121.png diff --git a/Src/Plugins/Input/in_vorbis/oggdrop/122.png b/Src/Plugins/Input/in_vorbis/oggdrop/122.png Binary files differnew file mode 100644 index 00000000..6b52d9e2 --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/oggdrop/122.png diff --git a/Src/Plugins/Input/in_vorbis/oggdrop/123.png b/Src/Plugins/Input/in_vorbis/oggdrop/123.png Binary files differnew file mode 100644 index 00000000..60284d13 --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/oggdrop/123.png diff --git a/Src/Plugins/Input/in_vorbis/resource.h b/Src/Plugins/Input/in_vorbis/resource.h new file mode 100644 index 00000000..425c40ea --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/resource.h @@ -0,0 +1,177 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by ogg.rc +// +#define IDS_NULLSOFT_VORBIS_DECODER_OLD 0 +#define IDS_PLEASE_ENTER_TAG_NAME 1 +#define IDS_TAG_NAME_CONTAINS_INVALID_CHARS 2 +#define IDOKAUTH 3 +#define IDS_ERASE_ALL_FIELDS_ON_LIST 3 +#define IDCANCELAUTH 4 +#define IDS_WARNING 4 +#define IDS_FILE_ERROR 5 +#define IDS_LENGTH 6 +#define IDS_AVERAGE_BITRATE 7 +#define IDS_FILE_SIZE 8 +#define IDS_NOMINAL_BITRATE 9 +#define IDS_MIN_BITRATE 10 +#define IDS_MAX_BITRATE 11 +#define IDS_CHANNELS 12 +#define IDS_SAMPLING_RATE 13 +#define IDS_SERIAL_NUMBER 14 +#define IDS_VERSION 15 +#define IDS_Vendor 16 +#define IDS_VENDOR 16 +#define IDS_TO_SIMPLE_MODE 17 +#define IDS_TO_ADVANCED_MODE 18 +#define IDS_OGG_VORBIS_INFO 19 +#define IDS_WRITE_ERROR 20 +#define IDS_BEST_RPM 21 +#define IDS_ABOUT 22 +#define IDS_LEAVE_AS_IS 24 +#define IDS_REMAP_6_CHANNELS 25 +#define IDS_DOWNMIX_TO_4_CHANNELS 26 +#define IDS_DOWNMIX_TO_2_CHANNELS_DS 27 +#define IDS_DOWNMIX_TO_2_CHANNELS_DS2 28 +#define IDS_DOWNMIX_TO_MONO 29 +#define IDS_CORRECT_FL_FC_FR_BL_BR_LFE 30 +#define IDS_BROKEN_FL_FR_FC_BL_BR_LFE 31 +#define IDS_IDLE 32 +#define IDS_ABOUT_TEXT 32 +#define IDS_LOWEST 33 +#define IDS_BELOW_NORMAL 34 +#define IDS_NORMAL 35 +#define IDS_ABOVE_NORMAL 36 +#define IDS_HIGHEST 37 +#define IDS_TIME_CRITICAL 38 +#define IDS_TITLE_PREFERENCES 39 +#define IDS_DO_NOT_ENABLE_24_BIT_OUTPUT 40 +#define IDS_ONE_TIME_FAQ_REMINDER 41 +#define IDS_RUNNING_ON_NT_OS 42 +#define IDS_RUNNING_ON_WIN9X 43 +#define IDS_NEVER 44 +#define IDS_PORT_80_ONLY 45 +#define IDS_ALWAYS 46 +#define IDS_SELECT_OUTPUT_DIRECTORY 47 +#define IDS_DECODING 48 +#define IDS_DISPLAY 49 +#define IDS_STREAMING 50 +#define IDS_CONNECTING 51 +#define IDS_PREBUFFERING 52 +#define IDS_AUTH_REQUIRED 53 +#define IDS_OGG_FILES 54 +#define IDS_NAME 55 +#define IDS_VALUE 56 +#define IDS_KBPS 57 +#define IDS_STRING2 58 +#define IDS_HZ 58 +#define IDS_GAME_SPEED 59 +#define IDS_STRING1 60 +#define IDS_BYTES 60 +#define IDS_FAMILY_STRING 61 +#define IDC_CONFIG_TAB1 101 +#define IDD_INFO_DLG 102 +#define IDC_CONFIG_TAB2 102 +#define IDC_CONFIG_TAB3 103 +#define IDD_INFO_DLG1 104 +#define IDC_CONFIG_TAB4 104 +#define IDD_ABOUT 112 +#define IDB_BITMAP1 115 +#define IDB_BITMAP2 116 +#define IDB_BITMAP3 117 +#define IDB_BITMAP4 118 +#define IDB_BITMAP5 119 +#define IDB_BITMAP6 120 +#define IDB_BITMAP7 121 +#define IDB_BITMAP8 122 +#define IDB_BITMAP9 123 +#define IDB_BITMAP10 124 +#define IDB_BITMAP11 125 +#define IDB_BITMAP12 126 +#define IDD_HTTPAUTH 128 +#define IDD_INFO 131 +#define IDD_DIALOG1 132 +#define IDD_CONFIG 132 +#define IDD_INFO_DLG_NEW 133 +#define IDD_INFO_PANEL_ADVANCED 134 +#define IDD_INFO_PANEL_SIMPLE 135 +#define IDB_PNG1 136 +#define IDB_PNG2 137 +#define IDB_PNG3 138 +#define IDB_PNG4 139 +#define IDB_PNG5 140 +#define IDB_PNG6 141 +#define IDB_PNG7 142 +#define IDB_PNG8 143 +#define IDB_PNG9 144 +#define IDB_PNG10 145 +#define IDB_PNG11 146 +#define IDB_PNG12 147 +#define IDC_LIST 1001 +#define IDC_NAME 1002 +#define IDC_TITLE 1002 +#define IDC_VALUE 1003 +#define IDC_ARTIST 1003 +#define IDC_HTTP_BSIZE 1004 +#define IDC_ALBUM 1004 +#define IDC_STREAM_SAVE 1005 +#define IDC_GENRE 1005 +#define IDC_FSAVE 1006 +#define IDC_YEAR 1006 +#define IDC_DATE 1006 +#define IDC_CUSTOM1 1007 +#define IDC_MISC 1007 +#define IDC_AVG_BR 1008 +#define IDC_FIX0R 1009 +#define IDC_PROXY 1012 +#define IDC_URL 1012 +#define IDC_SLIDER1 1013 +#define IDC_SLIDER2 1014 +#define IDC_BUTTON_ADD 1015 +#define IDC_BUTTON_DEL 1016 +#define IDC_BUTTON_DELALL 1017 +#define IDC_RPM 1018 +#define IDC_RPM2 1019 +#define IDC_MC6_DM 1020 +#define IDC_MC6_MAP 1021 +#define IDC_SSAVE_FMT 1022 +#define IDC_SSAVE_FMT_DEF 1023 +#define IDC_FULLBUF 1025 +#define IDC_EDITAUTH 1026 +#define IDC_REALM 1027 +#define IDC_TRACK 1028 +#define IDC_STATIC_MISC 1034 +#define IDC_STATIC_TAGS 1035 +#define IDC_STATIC_STD 1036 +#define IDC_STATIC_TRACK 1037 +#define IDC_SEPARATE 1038 +#define IDC_DELETE_ALL 1039 +#define IDC_RG 1040 +#define IDC_RG_MODE 1041 +#define IDC_NOCLIP 1042 +#define IDC_HARDLIMIT 1043 +#define IDC_PREAMP_STAT 1044 +#define IDC_TAB 1045 +#define IDC_MODE_TOGGLE 1053 +#define IDC_NEXT_STREAM 1054 +#define IDC_PREV_STREAM 1055 +#define IDC_STATIC_CS 1056 +#define IDC_HIDE_SPEC 1058 +#define IDC_COMMENT 1060 +#define IDC_ABOUT_TEXT 1061 +#define IDC_OS_BLAH 1062 +#define IDC_REMEMBER_INFOSIZE 1063 +#define IDC_FONTNAME 1064 +#define IDC_PREAMP_RG 1065 +#define IDS_NULLSOFT_VORBIS_DECODER 65534 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 148 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1067 +#define _APS_NEXT_SYMED_VALUE 105 +#endif +#endif diff --git a/Src/Plugins/Input/in_vorbis/rf.h b/Src/Plugins/Input/in_vorbis/rf.h new file mode 100644 index 00000000..70bd85bb --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/rf.h @@ -0,0 +1,108 @@ +#ifndef _RF_H_ +#define _RF_H_ + +//based on Tempura specs. +//NOT compatible with WA3 alphas + +class WReader +{ + protected: + + /* WReader + ** WReader constructor + */ + WReader() : m_player(0) { } + + public: + + /* m_player + ** Filled by Winamp. Pointer to Winamp 3 core interface + */ + /*WPlayer_callback*/ void *m_player; //PP: hack. read_file.dll doesn't call it at all. simply don't touch it + + /* GetDescription + ** Retrieves your plug-in's text description + */ + virtual char *GetDescription() { return "Unknown"; }; + + /* Open + ** Used to open a file, return 0 on success + */ + virtual int Open(char *url, bool *killswitch)=0; + + /* Read + ** Returns number of BYTES read (if < length then eof or killswitch) + */ + virtual int Read(char *buffer, int length, bool *killswitch)=0; + + /* GetLength + ** Returns length of the entire file in BYTES, return -1 on unknown/infinite (as for a stream) + */ + virtual int GetLength(void)=0; + + /* CanSeek + ** Returns 1 if you can skip ahead in the file, 0 if not + */ + virtual int CanSeek(void)=0; //PP: currently available read_file.dll vesions can always seek in any direction + + /* Seek + ** Jump to a certain absolute position + */ + virtual int Seek(int position, bool *killswitch)=0; + + /* GetHeader + ** Retrieve header. Used in read_http to retrieve the HTTP header + */ + virtual char *GetHeader(char *name) { return 0; } + + /* ~WReader + ** WReader virtual destructor + */ + //virtual ~WReader() { } + virtual void Release(int) {}; + //PP: hack - shut up linker when getting rid of evil CRT library; seems to work OK under Tempura +}; + + + + +#define READ_VER 0x100 + +typedef struct +{ + /* version + ** Version revision number + */ + int version; + + /* description + ** Text description of the reader plug-in + */ + char *description; + + /* create + ** Function pointer to create a reader module + */ + WReader *(*create)(); + + /* ismine + ** Determines whether or not a file should be read by this plug-in + */ + int (*ismine)(char *url); + +} reader_source; + +//exported symbol is: +//int readerSource(HINSTANCE,reader_source**); + +/* +(not a part of Tempura specs) +int _stdcall gzip_writefile(char* path,void* buf,DWORD size) - writes a memory block to a GZIP file - in_midi calls it from file info box + +other hacks: +recent versions understand file://... urls, can do partial file access (eg. "partial://00006666-66660000:c:\foo\bar.dat\zzz.wav" (zzz.wav is the "display name" + extension to make winamp select correct plug-in) and auto-detect CD drive letter (eg. #:\x.mp3 will scan all drives for that file; also works with partial:// ) +you can (for an example) build a playlist which will play Unreal soundtrack directly from the game CD on any system +latest read_file.dll is bundled with the midi plug-in: http://www.blorp.com/~peter/zips/in_midi.zip +*/ + +#endif
\ No newline at end of file diff --git a/Src/Plugins/Input/in_vorbis/shaper.cpp b/Src/Plugins/Input/in_vorbis/shaper.cpp new file mode 100644 index 00000000..85779a87 --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/shaper.cpp @@ -0,0 +1,245 @@ +#include "Shaper.h" + +#ifndef M_PI +#define M_PI 3.1415926535897932384626433832795028842 +#endif + +#define RANDBUFLEN 65536 + +#define RINT(x) ((x) >= 0 ? ((int)((x) + 0.5)) : ((int)((x) - 0.5))) + +const int scoeffreq[] = + { + 0, 48000, 44100, 37800, 32000, 22050, 48000, 44100 + }; + + +const int scoeflen[] = + { + 1, 16, 20, 16, 16, 15, 16, 15 + }; + +const int samp[] = + { + 8, 18, 27, 8, 8, 8, 10, 9 + }; + +const double shapercoefs[8][21] = + { + { + -1 + } + , /* triangular dither */ + + { -2.8720729351043701172, 5.0413231849670410156, -6.2442994117736816406, 5.8483986854553222656, + -3.7067542076110839844, 1.0495119094848632812, 1.1830236911773681641, -2.1126792430877685547, + 1.9094531536102294922, -0.99913084506988525391, 0.17090806365013122559, 0.32615602016448974609, + -0.39127644896507263184, 0.26876461505889892578, -0.097676105797290802002, 0.023473845794796943665, + }, /* 48k, N=16, amp=18 */ + + { -2.6773197650909423828, 4.8308925628662109375, -6.570110321044921875, 7.4572014808654785156, + -6.7263274192810058594, 4.8481650352478027344, -2.0412089824676513672, -0.7006359100341796875, + 2.9537565708160400391, -4.0800385475158691406, 4.1845216751098632812, -3.3311812877655029297, + 2.1179926395416259766, -0.879302978515625, 0.031759146600961685181, 0.42382788658142089844, + -0.47882103919982910156, 0.35490813851356506348, -0.17496839165687561035, 0.060908168554306030273, + }, /* 44.1k, N=20, amp=27 */ + + { -1.6335992813110351562, 2.2615492343902587891, -2.4077029228210449219, 2.6341717243194580078, + -2.1440362930297851562, 1.8153258562088012695, -1.0816224813461303711, 0.70302653312683105469, + -0.15991993248462677002, -0.041549518704414367676, 0.29416576027870178223, -0.2518316805362701416, + 0.27766478061676025391, -0.15785403549671173096, 0.10165894031524658203, -0.016833892092108726501, + }, /* 37.8k, N=16 */ + + { -0.82901298999786376953, 0.98922657966613769531, -0.59825712442398071289, 1.0028809309005737305, + -0.59938216209411621094, 0.79502451419830322266, -0.42723315954208374023, 0.54492527246475219727, + -0.30792605876922607422, 0.36871799826622009277, -0.18792048096656799316, 0.2261127084493637085, + -0.10573341697454452515, 0.11435490846633911133, -0.038800679147243499756, 0.040842197835445404053, + }, /* 32k, N=16 */ + + { -0.065229974687099456787, 0.54981261491775512695, 0.40278548002243041992, 0.31783768534660339355, + 0.28201797604560852051, 0.16985194385051727295, 0.15433363616466522217, 0.12507140636444091797, + 0.08903945237398147583, 0.064410120248794555664, 0.047146003693342208862, 0.032805237919092178345, + 0.028495194390416145325, 0.011695005930960178375, 0.011831838637590408325, + }, /* 22.05k, N=15 */ + + { -2.3925774097442626953, 3.4350297451019287109, -3.1853709220886230469, 1.8117271661758422852, + 0.20124770700931549072, -1.4759907722473144531, 1.7210904359817504883, -0.97746700048446655273, + 0.13790138065814971924, 0.38185903429985046387, -0.27421241998672485352, -0.066584214568138122559, + 0.35223302245140075684, -0.37672343850135803223, 0.23964276909828186035, -0.068674825131893157959, + }, /* 48k, N=16, amp=10 */ + + { -2.0833916664123535156, 3.0418450832366943359, -3.2047898769378662109, 2.7571926116943359375, + -1.4978630542755126953, 0.3427594602108001709, 0.71733748912811279297, -1.0737057924270629883, + 1.0225815773010253906, -0.56649994850158691406, 0.20968692004680633545, 0.065378531813621520996, + -0.10322438180446624756, 0.067442022264003753662, 0.00495197344571352005, + }, /* 44.1k, N=15, amp=9 */ + +#if 0 + { -3.0259189605712890625, 6.0268716812133789062, -9.195003509521484375, 11.824929237365722656, + -12.767142295837402344, 11.917946815490722656, -9.1739168167114257812, 5.3712320327758789062, + -1.1393624544143676758, -2.4484779834747314453, 4.9719839096069335938, -6.0392003059387207031, + 5.9359521865844726562, -4.903278350830078125, 3.5527443885803222656, -2.1909697055816650391, + 1.1672389507293701172, -0.4903914332389831543, 0.16519790887832641602, -0.023217858746647834778, + }, /* 44.1k, N=20 */ +#endif + }; + +#define POOLSIZE 97 + +Shaper::Shaper(int freq, int _nch, int min, int max, int _dtype, int pdf, double noiseamp) +{ + int i; + float pool[POOLSIZE] = {0}; + + nch = _nch; + dtype = _dtype; + + for (i = 1;i < 6;i++) if (freq == scoeffreq[i]) break; + /* if ((dtype == 3 || dtype == 4) && i == 6) { + fprintf(stderr,"Warning: ATH based noise shaping for destination frequency %dHz is not available, using triangular dither\n",freq); + }*/ + if (dtype == 2 || i == 6) i = 0; + if (dtype == 4 && (i == 1 || i == 2)) i += 5; + + shaper_type = i; + + shapebuf = (double**)malloc(sizeof(double *) * nch); + shaper_len = scoeflen[shaper_type]; + + for (i = 0;i < nch;i++) + shapebuf[i] = (double*)calloc(shaper_len, sizeof(double)); + + shaper_clipmin = min; + shaper_clipmax = max; + + randbuf = (REAL*)malloc(sizeof(REAL) * RANDBUFLEN); + + for (i = 0;i < POOLSIZE;i++) pool[i] = warandf(); + + switch (pdf) + { + case DITHER_RECTANGLE: // rectangular + for (i = 0;i < RANDBUFLEN;i++) + { + float r; + int p; + + p = warand() % POOLSIZE; + r = pool[p]; pool[p] = warandf(); + randbuf[i] = (REAL)(noiseamp * (((double)r) - 0.5)); + } + break; + + case DITHER_TRIANGLE: + for (i = 0;i < RANDBUFLEN;i++) + { + float r1, r2; + int p; + + p = warand() % POOLSIZE; + r1 = pool[p]; pool[p] = warandf(); + p = warand() % POOLSIZE; + r2 = pool[p]; pool[p] = warandf(); + randbuf[i] = (REAL)(noiseamp * ((((double)r1)) - (((double)r2)))); + } + break; +#if 0 + case DITHER_GAUSSIAN: // gaussian + for (i = 0;i < RANDBUFLEN;i++) + { + int sw = 0; + double t, u; + double r; + int p; + + if (sw == 0) + { + sw = 1; + + p = warand() % POOLSIZE; + r = ((double)pool[p]); pool[p] = warandf(); + + t = sqrt(-2 * log(1 - r)); + + p = warand() % POOLSIZE; + r = ((double)pool[p]); pool[p] = warandf(); + + u = 2 * M_PI * r; + + randbuf[i] = noiseamp * t * cos(u); + } + else + { + sw = 0; + + randbuf[i] = noiseamp * t * sin(u); + } + } + break; +#endif + } + + randptr = 0; + +// if (dtype == 0 || dtype == 1) return 1; + //return samp[shaper_type]; +} + +Shaper::~Shaper() +{ + int i; + + for (i = 0;i < nch;i++) free(shapebuf[i]); + free(shapebuf); + free(randbuf); +} + +int Shaper::do_shaping(double s,/*double *peak,*/int ch) +{ + double u, h; + int i; + + if (dtype == 1) + { + s += randbuf[randptr++ & (RANDBUFLEN-1)]; + + if (s < shaper_clipmin) + { + //double d = (double)s / shaper_clipmin; + //*peak = *peak < d ? d : *peak; + s = shaper_clipmin; + } + if (s > shaper_clipmax) + { + //double d = (double)s / shaper_clipmax; + //*peak = *peak < d ? d : *peak; + s = shaper_clipmax; + } + + return RINT(s); + } + + h = 0; + for (i = 0;i < shaper_len;i++) + h += shapercoefs[shaper_type][i] * shapebuf[ch][i]; + s += h; + u = s; + s += randbuf[randptr++ & (RANDBUFLEN-1)]; + if (s < shaper_clipmin) + { + //double d = (double)s / shaper_clipmin; + //*peak = *peak < d ? d : *peak; + s = shaper_clipmin; + } + if (s > shaper_clipmax) + { + //double d = (double)s / shaper_clipmax; + //*peak = *peak < d ? d : *peak; + s = shaper_clipmax; + } + s = RINT(s); + for (i = shaper_len - 2;i >= 0;i--) shapebuf[ch][i+1] = shapebuf[ch][i]; + shapebuf[ch][0] = s - u; + + return (int)s; +}
\ No newline at end of file diff --git a/Src/Plugins/Input/in_vorbis/shaper.h b/Src/Plugins/Input/in_vorbis/shaper.h new file mode 100644 index 00000000..13da7fd2 --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/shaper.h @@ -0,0 +1,30 @@ +//from SSRC +#ifndef NULLSOFT_VORBIS_SHAPER_H +#define NULLSOFT_VORBIS_SHAPER_H +#include "main.h" + +typedef float REAL; +enum +{ + DITHER_RECTANGLE=0, + DITHER_TRIANGLE=1, + DITHER_GAUSSIAN=2, +}; +class Shaper +{ + double **shapebuf; + int shaper_type,shaper_len,shaper_clipmin,shaper_clipmax; + REAL *randbuf; + int randptr; + int dtype; + int nch; + + public: + Shaper(int freq,int _nch,int min,int max,int _dtype,int pdf,double noiseamp); + + int do_shaping(double s,/*double *peak,*/int ch); + + ~Shaper(); +}; + +#endif
\ No newline at end of file diff --git a/Src/Plugins/Input/in_vorbis/unihack.cpp b/Src/Plugins/Input/in_vorbis/unihack.cpp new file mode 100644 index 00000000..ada2e3d8 --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/unihack.cpp @@ -0,0 +1,2 @@ +#include "main.h" + diff --git a/Src/Plugins/Input/in_vorbis/vcedit.c b/Src/Plugins/Input/in_vorbis/vcedit.c new file mode 100644 index 00000000..25f40cd8 --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/vcedit.c @@ -0,0 +1,491 @@ +/* This program is licensed under the GNU Library General Public License, version 2, + * a copy of which is included with this program (LICENCE.LGPL). + * + * (c) 2000-2001 Michael Smith <msmith@labyrinth.net.au> + * + * + * Comment editing backend, suitable for use by nice frontend interfaces. + * + * last modified: $Id: vcedit.c,v 1.3 2013/10/22 14:17:11 dromagod Exp $ + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ogg/ogg.h> +#include <vorbis/codec.h> + +#include "vcedit.h" +//#include "i18n.h" + + +#define CHUNKSIZE 4096 + +vcedit_state *vcedit_new_state(void) +{ + vcedit_state *state = malloc(sizeof(vcedit_state)); + memset(state, 0, sizeof(vcedit_state)); + + return state; +} + +char *vcedit_error(vcedit_state *state) +{ + return state->lasterror; +} + +vorbis_comment *vcedit_comments(vcedit_state *state) +{ + return state->vc; +} + +static void vcedit_clear_internals(vcedit_state *state) +{ + if(state->vc) + { + vorbis_comment_clear(state->vc); + free(state->vc); + } + if(state->os) + { + ogg_stream_clear(state->os); + free(state->os); + } + if(state->oy) + { + ogg_sync_clear(state->oy); + free(state->oy); + } + if(state->vendor) + free(state->vendor); + if(state->mainbuf) + free(state->mainbuf); + if(state->bookbuf) + free(state->bookbuf); + if(state->vi) { + vorbis_info_clear(state->vi); + free(state->vi); + } + + memset(state, 0, sizeof(*state)); +} + +void vcedit_clear(vcedit_state *state) +{ + if(state) + { + vcedit_clear_internals(state); + free(state); + } +} + +/* Next two functions pulled straight from libvorbis, apart from one change + * - we don't want to overwrite the vendor string. + */ +static void _v_writestring(oggpack_buffer *o,char *s, int len) +{ + while(len--) + { + oggpack_write(o,*s++,8); + } +} + +static int _commentheader_out(vorbis_comment *vc, char *vendor, ogg_packet *op) +{ + oggpack_buffer opb; + + oggpack_writeinit(&opb); + + /* preamble */ + oggpack_write(&opb,0x03,8); + _v_writestring(&opb,"vorbis", 6); + + /* vendor */ + oggpack_write(&opb,(unsigned long)strlen(vendor),32); + _v_writestring(&opb,vendor, (int)strlen(vendor)); + + /* comments */ + oggpack_write(&opb,vc->comments,32); + if(vc->comments){ + int i; + for(i=0;i<vc->comments;i++){ + if(vc->user_comments[i]){ + oggpack_write(&opb,vc->comment_lengths[i],32); + _v_writestring(&opb,vc->user_comments[i], + vc->comment_lengths[i]); + }else{ + oggpack_write(&opb,0,32); + } + } + } + oggpack_write(&opb,1,1); + + op->packet = _ogg_malloc(oggpack_bytes(&opb)); + memcpy(op->packet, opb.buffer, oggpack_bytes(&opb)); + + op->bytes=oggpack_bytes(&opb); + op->b_o_s=0; + op->e_o_s=0; + op->granulepos=0; + + oggpack_writeclear(&opb); + return 0; +} + +static int _blocksize(vcedit_state *s, ogg_packet *p) +{ + int this = vorbis_packet_blocksize(s->vi, p); + int ret = (this + s->prevW)/4; + + if(!s->prevW) + { + s->prevW = this; + return 0; + } + + s->prevW = this; + return ret; +} + +static int _fetch_next_packet(vcedit_state *s, ogg_packet *p, ogg_page *page) +{ + int result = ogg_stream_packetout(s->os, p); + + if(result > 0) + return 1; + else + { + if(s->eosin) + return 0; + while(ogg_sync_pageout(s->oy, page) <= 0) + { + char *buffer = ogg_sync_buffer(s->oy, CHUNKSIZE); + int bytes = (int)s->read(buffer,1, CHUNKSIZE, s->in); + ogg_sync_wrote(s->oy, bytes); + if(bytes == 0) + return 0; + } + if(ogg_page_eos(page)) + s->eosin = 1; + else if(ogg_page_serialno(page) != s->serial) + { + s->eosin = 1; + s->extrapage = 1; + return 0; + } + + ogg_stream_pagein(s->os, page); + return _fetch_next_packet(s, p, page); + } +} + +int vcedit_open(vcedit_state *state, FILE *in) +{ + return vcedit_open_callbacks(state, (void *)in, + (vcedit_read_func)fread, (vcedit_write_func)fwrite); +} + +int vcedit_open_callbacks(vcedit_state *state, void *in, + vcedit_read_func read_func, vcedit_write_func write_func) +{ + + char *buffer; + int bytes,i; + ogg_packet *header; + ogg_packet header_main; + ogg_packet header_comments; + ogg_packet header_codebooks; + ogg_page og; + + state->in = in; + state->read = read_func; + state->write = write_func; + + state->oy = malloc(sizeof(ogg_sync_state)); + ogg_sync_init(state->oy); + + buffer = ogg_sync_buffer(state->oy, CHUNKSIZE); + bytes = (int)state->read(buffer, 1, CHUNKSIZE, state->in); + + ogg_sync_wrote(state->oy, bytes); + + if(ogg_sync_pageout(state->oy, &og) != 1) + { + if(bytes<CHUNKSIZE) + state->lasterror = "Input truncated or empty."; + else + state->lasterror = "Input is not an Ogg bitstream."; + goto err; + } + + state->serial = ogg_page_serialno(&og); + + state->os = malloc(sizeof(ogg_stream_state)); + ogg_stream_init(state->os, state->serial); + + state->vi = malloc(sizeof(vorbis_info)); + vorbis_info_init(state->vi); + + state->vc = malloc(sizeof(vorbis_comment)); + vorbis_comment_init(state->vc); + + if(ogg_stream_pagein(state->os, &og) < 0) + { + state->lasterror = "Error reading first page of Ogg bitstream."; + goto err; + } + + if(ogg_stream_packetout(state->os, &header_main) != 1) + { + state->lasterror = "Error reading initial header packet."; + goto err; + } + + if(vorbis_synthesis_headerin(state->vi, state->vc, &header_main) < 0) + { + state->lasterror = "Ogg bitstream does not contain vorbis data."; + goto err; + } + + state->mainlen = header_main.bytes; + state->mainbuf = malloc(state->mainlen); + memcpy(state->mainbuf, header_main.packet, header_main.bytes); + + i = 0; + header = &header_comments; + while(i<2) { + while(i<2) { + int result = ogg_sync_pageout(state->oy, &og); + if(result == 0) break; /* Too little data so far */ + else if(result == 1) + { + ogg_stream_pagein(state->os, &og); + while(i<2) + { + result = ogg_stream_packetout(state->os, header); + if(result == 0) break; + if(result == -1) + { + state->lasterror = "Corrupt secondary header."; + goto err; + } + vorbis_synthesis_headerin(state->vi, state->vc, header); + if(i==1) + { + state->booklen = header->bytes; + state->bookbuf = malloc(state->booklen); + memcpy(state->bookbuf, header->packet, + header->bytes); + } + i++; + header = &header_codebooks; + } + } + } + + buffer = ogg_sync_buffer(state->oy, CHUNKSIZE); + bytes = (int)state->read(buffer, 1, CHUNKSIZE, state->in); + if(bytes == 0 && i < 2) + { + state->lasterror = "EOF before end of vorbis headers."; + goto err; + } + ogg_sync_wrote(state->oy, bytes); + } + + /* Copy the vendor tag */ + bytes = (int)strlen(state->vc->vendor); + state->vendor = malloc(bytes +1); + strncpy(state->vendor, state->vc->vendor, bytes); + + /* Headers are done! */ + return 0; + +err: + vcedit_clear_internals(state); + return -1; +} + +int vcedit_write(vcedit_state *state, void *out) +{ + ogg_stream_state streamout; + ogg_packet header_main; + ogg_packet header_comments; + ogg_packet header_codebooks; + + ogg_page ogout, ogin; + ogg_packet op; + ogg_int64_t granpos = 0; + int result; + char *buffer; + int bytes; + int needflush=0, needout=0; + + state->eosin = 0; + state->extrapage = 0; + + header_main.bytes = state->mainlen; + header_main.packet = state->mainbuf; + header_main.b_o_s = 1; + header_main.e_o_s = 0; + header_main.granulepos = 0; + + header_codebooks.bytes = state->booklen; + header_codebooks.packet = state->bookbuf; + header_codebooks.b_o_s = 0; + header_codebooks.e_o_s = 0; + header_codebooks.granulepos = 0; + + ogg_stream_init(&streamout, state->serial); + + _commentheader_out(state->vc, state->vendor, &header_comments); + + ogg_stream_packetin(&streamout, &header_main); + ogg_stream_packetin(&streamout, &header_comments); + ogg_stream_packetin(&streamout, &header_codebooks); + + while((result = ogg_stream_flush(&streamout, &ogout))) + { + if(state->write(ogout.header,1,ogout.header_len, out) != + (size_t) ogout.header_len) + goto cleanup; + if(state->write(ogout.body,1,ogout.body_len, out) != + (size_t) ogout.body_len) + goto cleanup; + } + + while(_fetch_next_packet(state, &op, &ogin)) + { + int size; + size = _blocksize(state, &op); + granpos += size; + + if(needflush) + { + if(ogg_stream_flush(&streamout, &ogout)) + { + if(state->write(ogout.header,1,ogout.header_len, + out) != (size_t) ogout.header_len) + goto cleanup; + if(state->write(ogout.body,1,ogout.body_len, + out) != (size_t) ogout.body_len) + goto cleanup; + } + } + else if(needout) + { + if(ogg_stream_pageout(&streamout, &ogout)) + { + if(state->write(ogout.header,1,ogout.header_len, + out) != (size_t) ogout.header_len) + goto cleanup; + if(state->write(ogout.body,1,ogout.body_len, + out) != (size_t) ogout.body_len) + goto cleanup; + } + } + + needflush=needout=0; + + if(op.granulepos == -1) + { + op.granulepos = granpos; + ogg_stream_packetin(&streamout, &op); + } + else /* granulepos is set, validly. Use it, and force a flush to + account for shortened blocks (vcut) when appropriate */ + { + if(granpos > op.granulepos) + { + granpos = op.granulepos; + ogg_stream_packetin(&streamout, &op); + needflush=1; + } + else + { + ogg_stream_packetin(&streamout, &op); + needout=1; + } + } + } + + streamout.e_o_s = 1; + while(ogg_stream_flush(&streamout, &ogout)) + { + if(state->write(ogout.header,1,ogout.header_len, + out) != (size_t) ogout.header_len) + goto cleanup; + if(state->write(ogout.body,1,ogout.body_len, + out) != (size_t) ogout.body_len) + goto cleanup; + } + + if (state->extrapage) + { + if(state->write(ogin.header,1,ogin.header_len, + out) != (size_t) ogin.header_len) + goto cleanup; + if (state->write(ogin.body,1,ogin.body_len, out) != + (size_t) ogin.body_len) + goto cleanup; + } + + state->eosin=0; /* clear it, because not all paths to here do */ + while(!state->eosin) /* We reached eos, not eof */ + { + /* We copy the rest of the stream (other logical streams) + * through, a page at a time. */ + while(1) + { + result = ogg_sync_pageout(state->oy, &ogout); + if(result==0) + break; + if(result<0) + state->lasterror = "Corrupt or missing data, continuing..."; + else + { + /* Don't bother going through the rest, we can just + * write the page out now */ + if(state->write(ogout.header,1,ogout.header_len, + out) != (size_t) ogout.header_len) { +// fprintf(stderr, "Bumming out\n"); + goto cleanup; + } + if(state->write(ogout.body,1,ogout.body_len, out) != + (size_t) ogout.body_len) { + // fprintf(stderr, "Bumming out 2\n"); + goto cleanup; + } + } + } + buffer = ogg_sync_buffer(state->oy, CHUNKSIZE); + bytes = (int)state->read(buffer,1, CHUNKSIZE, state->in); + ogg_sync_wrote(state->oy, bytes); + if(bytes == 0) + { + state->eosin = 1; + break; + } + } + + +cleanup: + ogg_stream_clear(&streamout); + ogg_packet_clear(&header_comments); + + free(state->mainbuf); + free(state->bookbuf); + state->mainbuf = state->bookbuf = NULL; + + if(!state->eosin) + { + state->lasterror = + "Error writing stream to output. " + "Output stream may be corrupted or truncated."; + return -1; + } + + return 0; +} + diff --git a/Src/Plugins/Input/in_vorbis/vcedit.h b/Src/Plugins/Input/in_vorbis/vcedit.h new file mode 100644 index 00000000..80cc5a6b --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/vcedit.h @@ -0,0 +1,62 @@ +/* This program is licensed under the GNU Library General Public License, version 2, + * a copy of which is included with this program (with filename LICENSE.LGPL). + * + * (c) 2000-2001 Michael Smith <msmith@labyrinth.net.au> + * + * VCEdit header. + * + * last modified: $ID:$ + */ + +#ifndef __VCEDIT_H +#define __VCEDIT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdio.h> +#include <ogg/ogg.h> +#include <vorbis/codec.h> + +typedef size_t (*vcedit_read_func)(void *, size_t, size_t, void *); +typedef size_t (*vcedit_write_func)(const void *, size_t, size_t, void *); + +typedef struct { + ogg_sync_state *oy; + ogg_stream_state *os; + + vorbis_comment *vc; + vorbis_info *vi; + + vcedit_read_func read; + vcedit_write_func write; + + void *in; + long serial; + unsigned char *mainbuf; + unsigned char *bookbuf; + int mainlen; + int booklen; + char *lasterror; + char *vendor; + int prevW; + int extrapage; + int eosin; +} vcedit_state; + +extern vcedit_state * vcedit_new_state(void); +extern void vcedit_clear(vcedit_state *state); +extern vorbis_comment * vcedit_comments(vcedit_state *state); +extern int vcedit_open(vcedit_state *state, FILE *in); +extern int vcedit_open_callbacks(vcedit_state *state, void *in, + vcedit_read_func read_func, vcedit_write_func write_func); +extern int vcedit_write(vcedit_state *state, void *out); +extern char * vcedit_error(vcedit_state *state); + +#ifdef __cplusplus +} +#endif + +#endif /* __VCEDIT_H */ + diff --git a/Src/Plugins/Input/in_vorbis/version.rc2 b/Src/Plugins/Input/in_vorbis/version.rc2 new file mode 100644 index 00000000..b6a16388 --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/version.rc2 @@ -0,0 +1,39 @@ + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// +#include "../../../Winamp/buildType.h" +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,79,0,0 + PRODUCTVERSION WINAMP_PRODUCTVER + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Winamp SA" + VALUE "FileDescription", "Winamp Input Plug-in" + VALUE "FileVersion", "1,79,0,0" + VALUE "InternalName", "Nullsoft Vorbis Decoder" + VALUE "LegalCopyright", "Copyright © 2001-2023 Winamp SA" + VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA" + VALUE "OriginalFilename", "in_vorbis.dll" + VALUE "ProductName", "Winamp" + VALUE "ProductVersion", STR_WINAMP_PRODUCTVER + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/Src/Plugins/Input/in_vorbis/wa2.cpp b/Src/Plugins/Input/in_vorbis/wa2.cpp new file mode 100644 index 00000000..3920a0ee --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/wa2.cpp @@ -0,0 +1,1097 @@ +#include "main.h" +#include "genres.h" +#include "decoder.h" +#include "api__in_vorbis.h" +#include "../Winamp/wa_ipc.h" +#include "../nu/Singleton.h" +#include "mkv_vorbis_decoder.h" +#include <shlwapi.h> +#include "../nu/AutoWide.h" +#include "../nu/AutoChar.h" +#include <strsafe.h> +#include <api/service/waservicefactory.h> + +template <class api_T> +void ServiceBuild(api_T *&api_t, GUID factoryGUID_t) +{ + if (mod.service) + { + waServiceFactory *factory = mod.service->service_getServiceByGuid(factoryGUID_t); + if (factory) + api_t = reinterpret_cast<api_T *>( factory->getInterface() ); + } +} + +template <class api_T> +void ServiceRelease(api_T *api_t, GUID factoryGUID_t) +{ + if (mod.service && api_t) + { + waServiceFactory *factory = mod.service->service_getServiceByGuid(factoryGUID_t); + if (factory) + factory->releaseInterface(api_t); + } + api_t = NULL; +} + +VorbisFile * theFile = 0; + +extern CfgInt cfg_abr,cfg_httpseek2; + +OSVERSIONINFO os_ver = {0}; + +static int pos_ms; +static int seek_to=-1; +static int length; +static bool kill; + +StringW stat_disp; + +void show_stat(const wchar_t* txt) +{ + if (txt) + { + stat_disp=txt; + PostMessage(mod.hMainWindow,WM_USER,0,243); + } + else + stat_disp=L""; +} + +static int is_out_open; +static int paused; +static int volume=255; +static int pan=0; +StringW cur_file; + +CRITICAL_SECTION sync; + +HANDLE hThread=0; + +void Config(HWND); +void About(HWND p); +void do_cfg(int s); +void GetFileInfo(const in_char *file, wchar_t *title, int *len); + +const char *INI_FILE=0; +const wchar_t *INI_DIRECTORY=0; +int (*warand)()=0; +float (*warandf)()=0; + +api_application *WASABI_API_APP = 0; +// wasabi based services for localisation support +api_language *WASABI_API_LNG = 0; +HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0; +api_memmgr* WASABI_API_MEMMGR = 0; +api_config *AGAVE_API_CONFIG=0; + +static MKVDecoderCreator mkvCreator; +static SingletonServiceFactory<svc_mkvdecoder, MKVDecoderCreator> mkvFactory; + +void SetFileExtensions(void) +{ + static char fileExtensionsString[1200] = {0}; // "OGG\0Ogg files (*.OGG)\0" + char* end = 0; + StringCchCopyExA(fileExtensionsString, 1200, "OGG;OGA", &end, 0, 0); + StringCchCopyExA(end+1, 1200, WASABI_API_LNGSTRING(IDS_OGG_FILES), 0, 0, 0); + mod.FileExtensions = fileExtensionsString; +} + +int Init() +{ + if (!IsWindow(mod.hMainWindow)) + return IN_INIT_FAILURE; + + mod.UsesOutputPlug|=8; + + warand = (int (*)())SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GET_RANDFUNC); + warandf = (float (*)())SendMessage(mod.hMainWindow, WM_WA_IPC, 1, IPC_GET_RANDFUNC); + + ServiceBuild(WASABI_API_MEMMGR, memMgrApiServiceGuid); + ServiceBuild(AGAVE_API_CONFIG, AgaveConfigGUID); + // loader so that we can get the localisation service api for use + ServiceBuild(WASABI_API_LNG, languageApiGUID); + ServiceBuild(WASABI_API_APP, applicationApiServiceGuid); + + mkvFactory.Register(mod.service, &mkvCreator); + + // need to have this initialised before we try to do anything with localisation features + WASABI_API_START_LANG(mod.hDllInstance,InVorbisLangGUID); + + static wchar_t szDescription[256]; + StringCchPrintfW(szDescription,256,WASABI_API_LNGSTRINGW(IDS_NULLSOFT_VORBIS_DECODER),VER); + mod.description = (char*)szDescription; + + SetFileExtensions(); + + INI_FILE = (const char *)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GETINIFILE); + INI_DIRECTORY = (const wchar_t *)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GETINIDIRECTORYW); + + os_ver.dwOSVersionInfoSize=sizeof(os_ver); + GetVersionEx(&os_ver); + + InitializeCriticalSection(&sync); + do_cfg(0); + return IN_INIT_SUCCESS; +} + +void Quit() +{ + winampGetExtendedFileInfoW_Cleanup(); + DeleteCriticalSection(&sync); + mkvFactory.Deregister(mod.service); + ServiceRelease(WASABI_API_MEMMGR, memMgrApiServiceGuid); + ServiceRelease(AGAVE_API_CONFIG, AgaveConfigGUID); + ServiceRelease(WASABI_API_LNG, languageApiGUID); + ServiceRelease(WASABI_API_APP, applicationApiServiceGuid); +} + +int GetLength() +{ + return length; +} + +int IsOurFile(const in_char *fn) +{ + if (PathIsURLW(fn)) + { + const wchar_t *foo=wcsrchr(fn,L'.'); + return foo ? !_wcsicmp(foo,L".ogg") : 0; + } + else return 0; +} + +static UINT kbps_disp; + +static void out_close() +{ + if (is_out_open) + { + mod.outMod->Close(); + mod.SAVSADeInit(); + is_out_open=0; + } +} + +static bool need_full_setinfo; + +static int out_open(const Decoder &dec) +{ + int max_l=mod.outMod->Open(dec.sr,dec.nch,dec.bps,-1,-1); + if (max_l<0) return 0; + mod.outMod->SetVolume(-666); + mod.outMod->SetPan(pan); + mod.SAVSAInit(max_l,dec.sr); + mod.VSASetInfo(dec.sr,dec.nch); + + is_out_open=1; + need_full_setinfo=1; + return 1; +} + +void Decoder::wa2_setinfo(UINT cur) +{ + UINT disp=file->get_avg_bitrate(); + if (!cfg_abr) + { + disp=cur; + } + if ((disp && disp!=kbps_disp) || need_full_setinfo) + { + kbps_disp=disp; + if (need_full_setinfo) + { + mod.SetInfo(disp,sr/1000,nch,1); + need_full_setinfo=0; + } + else mod.SetInfo(disp,-1,-1,1); + } +} + +static bool need_movefile; +static void process_movefile(); + +void alloc_buffers(Decoder & dec,short ** visbuf,char ** sample_buf,int * s_size) +{ + *s_size=576 * (dec.bps>>3) * dec.nch; + + if (*sample_buf) *sample_buf=(char*)realloc(*sample_buf,*s_size*2); + else *sample_buf=(char*)malloc(*s_size*2); + + if (dec.bps>16) + { + int vs=576*2*dec.nch; + if (*visbuf) *visbuf=(short*)realloc(*visbuf,vs); + else *visbuf=(short*)malloc(vs); + } + else if (*visbuf) {free(*visbuf);*visbuf=0;} +} + +static DWORD WINAPI PlayThread(Decoder &dec) +{ + int pos_base=0; + int samp_wr=0; + int done=0; + int upd=0; + __int64 brate; + int br_div,br_t; + short* visbuf=0; + char *sample_buf=0; + int retries=0; + int s_size=0; + + pos_ms=0; + + { + int r; + r=dec.play_init(); + if (r) + { + if (!kill) Sleep(50); + if (!kill) Sleep(50); + if (!kill) Sleep(50); + if (!kill) Sleep(50); + if (!kill) + { + if (r==2) PostMessage(mod.hMainWindow,WM_WA_MPEG_EOF,0,0); + else PostMessage(mod.hMainWindow,WM_COMMAND,40047,0); + } + delete &dec; + return 0; + } + theFile->do_prebuf(); + } + + brate=0; + br_div=0; + upd=0; + + alloc_buffers(dec,&visbuf,&sample_buf,&s_size); + + //int f_type=theFile->GetType(); + bool is_live=theFile->IsLive(); + + while(!kill) + { + if (!theFile) break;//ugh + if (seek_to!= -1) + { + UINT _st=seek_to; + int r=1; + seek_to=-1; + if (theFile) + { + theFile->use_prebuf=0; + int link=theFile->vf.current_link; + r=dec.Seek((double)_st*0.001); + if (link!=theFile->vf.current_link) PostMessage(mod.hMainWindow,WM_USER,0,243); + } + else r=1; + if (!r) + { + pos_base=pos_ms=_st; + mod.outMod->Flush(pos_ms); + samp_wr=0; + done=0; + theFile->do_prebuf(); + } + } + + if (need_movefile && paused)//HACK, prevent stupid lockup + { + process_movefile(); + if (!theFile) break;//#@! + dec.file=theFile; + dec.Flush(); + } + + if (done) + { + // mod.outMod->CanWrite(); + if (!mod.outMod->IsPlaying()) + { + PostMessage(mod.hMainWindow,WM_WA_MPEG_EOF,0,0); + break; + } + Sleep(10); + } + else if (mod.outMod->CanWrite() >= (s_size<<(mod.dsp_isactive()?1:0))) + { + int l=0; + while(1) + { + if (!dec.need_reopen) + { + l+=dec.Read(s_size-l,sample_buf+l); + if (l>=s_size) break; + + int link=theFile->vf.current_link; + if (need_movefile)//safe not to flush here + { + process_movefile(); + if (!theFile) break;//#@! + dec.file=theFile; + } + if (!dec.DoFrame()) break; + if (kill) break; + + if (link!=theFile->vf.current_link) + { + PostMessage(mod.hMainWindow,WM_USER,0,243); + } + br_t=ov_bitrate_instant(&theFile->vf); + if (br_t>0) + { + int i = dec.DataAvailable()/((dec.bps/8)*dec.nch); + br_div+=i; + brate+=(__int64)(br_t*i); + } + if (need_full_setinfo || (!((++upd)%200) && br_div)) + { + if (!br_div) {br_div=1;brate=theFile->get_avg_bitrate();} + dec.wa2_setinfo((int)((__int64)brate/(__int64)br_div/(__int64)1000)); + brate=0; + br_div=0; + } + } + if (dec.need_reopen) + {//blargh, new PCM format + if (l>0) break;//got samples to play, we'll deal with this later + //l=0; + while(!kill && mod.outMod->IsPlaying()) Sleep(1); + if (kill) break; + out_close(); + if (!out_open(dec))//boo + { + PostMessage(mod.hMainWindow,WM_COMMAND,40047,0); + kill=1; + break; + } + alloc_buffers(dec,&visbuf,&sample_buf,&s_size); + dec.need_reopen=0; + } + } + + if (kill || !theFile) break; + + if (l<=0 && (!is_live || (--retries)<0)) + { + mod.outMod->Write(sample_buf,0); + done=1; + } + else if (l<=0) + { + int r; + out_close(); + EnterCriticalSection(&sync); + delete theFile; + theFile=0; + LeaveCriticalSection(&sync); + if (sample_buf) + { + free(sample_buf); + sample_buf=0; + } + + r=dec.play_init(); + + if (r) + { + mod.outMod->Write(sample_buf,0); + done=1; + } + else + { + theFile->do_prebuf(); + } + } + else + { + if (l<s_size) memset(sample_buf+l,dec.bps==8 ? 0x80 : 0,s_size-l); + char * vis=sample_buf; + UINT vis_bps=dec.bps; + if (dec.bps>16) + { + UINT n; + UINT d=dec.bps>>3; + char * foo=sample_buf+d-2; + for(n=0;n<576*dec.nch;n++) + { + visbuf[n]=*(short*)foo; + foo+=d; + } + vis=(char*)visbuf; + vis_bps=16; + } + mod.SAAddPCMData(vis,dec.nch,vis_bps,pos_ms); + mod.VSAAddPCMData(vis,dec.nch,vis_bps,pos_ms); + + if (mod.dsp_isactive()) + { + l=(l<<3)/(dec.bps*dec.nch); + l=mod.dsp_dosamples((short*)sample_buf,l,dec.bps,dec.nch,dec.sr); + l*=(dec.nch*dec.bps)>>3; + } + if (kill) break; + mod.outMod->Write((char*)sample_buf,l); + + samp_wr+=(8*l)/(dec.bps*dec.nch); + pos_ms=pos_base+MulDiv(1000,samp_wr,dec.sr); + } + } + else + { + theFile->Idle(); + } + } + + // out_close(); + // gay gapless plugins puke, need to call this from stop + // ok, hetero (out_wave v2.x / out_ds v1.4+) gapless plugins wouldn't puke anymore + + if (theFile) + { + VorbisFile * t=theFile; + EnterCriticalSection(&sync); + theFile=0; + LeaveCriticalSection(&sync); + delete t; + } + + if (sample_buf) + { + free(sample_buf); + sample_buf=0; + } + + if (need_movefile) process_movefile(); + + /* if (!kill) + { + CloseHandle(hThread); + hThread=0; + }*/ + + if (visbuf) free(visbuf); + + delete &dec; + return 0; +} + +static StringW move_src,move_dst; +static bool mf_ret; + +static void do_movefile() +{ + mf_ret=1; + winampGetExtendedFileInfoW_Cleanup(); + if (!DeleteFileW(move_dst)) mf_ret=0; + else + { + if (!MoveFileW(move_src,move_dst)) + { + if (!CopyFileW(move_src,move_dst,0)) mf_ret=0; + DeleteFileW(move_src); + } + } +} + +static void process_movefile() +{ + if (theFile) + { + StringW f_path; + f_path.AddString(theFile->url); + double pos=theFile->GetPos(); + EnterCriticalSection(&sync); + delete theFile; + theFile=0; + + do_movefile(); + + theFile=VorbisFile::Create(f_path,0); + LeaveCriticalSection(&sync); + if (theFile) + { + theFile->Seek(pos); + } + } + else do_movefile(); + need_movefile=0; +} + +bool sync_movefile(const wchar_t * src,const wchar_t * dst)//called from info_.cpp +{ + move_src=src; + move_dst=dst; + need_movefile=1; + if (!theFile) process_movefile(); + else + { + while(need_movefile && hThread) Sleep(1); + if (need_movefile) process_movefile();//shouldnt really happen + move_src=L""; + move_dst=L""; + PostMessage(mod.hMainWindow,WM_USER,0,243); + } + + return mf_ret; +} + + +int Decoder::play_init()//still messy +{ + if (play_inited) return 0; + + kbps_disp=0; + + VorbisFile * t=VorbisFile::Create(cur_file,0); + if (!t) + { +#ifdef _DEBUG + OutputDebugString(L"can't open file\n"); +#endif + // if (scream) MessageBox(mod.hMainWindow,"error opening file",0,MB_ICONERROR); + return 2; + } + Init(t); + if (!out_open(*this)) + { +#ifdef _DEBUG + OutputDebugString(L"can't open output\n"); +#endif + delete t; + return 1; + } + + EnterCriticalSection(&sync); + theFile=t; + LeaveCriticalSection(&sync); + + wa2_setinfo(theFile->get_avg_bitrate()); + + { + double v=theFile->Length(); + if (v==OV_EINVAL || v<=0) length=-1; + else length=(int)(v*1000.0); + } + + play_inited=1; + + return 0; +} + +int Play(const in_char *fn) +{ + seek_to=-1; + kill=0; + length=0; + paused=0; + + show_stat(0); + + EnterCriticalSection(&sync); + cur_file=fn; + LeaveCriticalSection(&sync); + + Decoder * dec=new Decoder; + + if (!PathIsURLW(fn)) + { + mod.is_seekable=1; +#if 1 + int rv=dec->play_init(); + if (rv) + { + delete dec; + if (rv==2) return -1; + return 1; + } +#endif + } + else mod.is_seekable=cfg_httpseek2; + + { + DWORD id; + hThread=CreateThread(0,0,(LPTHREAD_START_ROUTINE)PlayThread,dec,CREATE_SUSPENDED,&id); + } + + if (hThread) + { + SetThreadPriority(hThread, (int)AGAVE_API_CONFIG->GetInt(playbackConfigGroupGUID, L"priority", THREAD_PRIORITY_HIGHEST)); + ResumeThread(hThread); + return 0; + } + else + { + out_close(); + delete dec; + return 1; + } +} + +void Pause() +{ + if (!paused) + { + mod.outMod->Pause(1); + paused=1; + } +} + +void UnPause() +{ + if (paused) + { + mod.outMod->Pause(0); + paused=0; + } +} + +int IsPaused() +{ + return paused; +} + +void Stop() +{ + if (hThread) + { + kill=1; + EnterCriticalSection(&sync); + if (theFile) theFile->stopping=1; + LeaveCriticalSection(&sync); + if (WaitForSingleObject(hThread,10000)!=WAIT_OBJECT_0) + { + TerminateThread(hThread,0); + //MessageBox(mod.hMainWindow,"error asking thread to die",0,MB_ICONERROR); + } + CloseHandle(hThread); + hThread=0; + out_close(); + } + show_stat(0); + winampGetExtendedFileInfoW_Cleanup(); +} + +void EQSet(int on, char data[10], int preamp) +{ +} + +int GetOutputTime() +{ + return pos_ms+(mod.outMod->GetOutputTime()-mod.outMod->GetWrittenTime()); +} + +void SetOutputTime(int t) +{ + seek_to=t; + EnterCriticalSection(&sync); + if (theFile) theFile->abort_prebuf=1; + LeaveCriticalSection(&sync); +} + +void SetVolume(int v) +{ + mod.outMod->SetVolume(volume=v); +} + +void SetPan(int p) +{ + mod.outMod->SetPan(pan=p); +} + +//int InfoBox(char *file, HWND parent); //old +int RunInfoDlg(const in_char * url,HWND parent); + +In_Module mod= +{ + IN_VER_RET, + "nullsoft(in_vorbis.dll)", + 0,0, + 0, + 1, + 1, + + Config, + About, + + Init, + Quit, + + GetFileInfo, + RunInfoDlg, + + IsOurFile, + Play, + Pause, + UnPause, + IsPaused, + Stop, + + GetLength, + GetOutputTime, + SetOutputTime, + + SetVolume, + SetPan, + + 0,0,0,0,0,0,0,0,0,0,0, + EQSet, + + 0, + 0, +}; + +extern "C" { + __declspec( dllexport ) In_Module * winampGetInModule2() + { + return &mod; + } +} + +void VorbisFile::Status(const wchar_t * zzz) +{ + if (primary) + show_stat(zzz); +} + +bool VorbisFile::Aborting() +{ + return stopping || (primary && kill); +} + + +Info::Info(const wchar_t *filename) : filename(filename), vc(0) +{ + VorbisFile * vf = VorbisFile::Create(filename,true); + if(!vf) + return; + + numstreams = vf->vf.links; + if(numstreams) + { + // now copy the comment section to our own memory... + stream = vf->vf.current_link; // this is the stream we're editing... + vc = (vorbis_comment*)calloc(sizeof(vorbis_comment),numstreams); + + for(int i=0; i<numstreams; i++) + { // one comment section per stream + vorbis_comment *c = ov_comment(&vf->vf,i); + + vc[i].comments = c->comments; + vc[i].user_comments = (char **)malloc(sizeof(char*)*c->comments); + vc[i].comment_lengths = (int *)malloc(sizeof(int)*c->comments); + + for(int j=0;j<vc[i].comments;j++) + { // copy the comments over + vc[i].user_comments[j] = _strdup(c->user_comments[j]); + vc[i].comment_lengths[j] = c->comment_lengths[j]; + } + vc[i].vendor=_strdup(c->vendor); + } + } + delete vf; +} + +Info::~Info() +{ + if(vc) { + for(int i=0; i < numstreams; i++) + vorbis_comment_clear(&vc[i]); + free(vc); + } +} + +bool Info::Save() +{ + return !!modify_file(filename,vc,numstreams); +} + +int Info::GetNumMetadataItems() +{ + return vc[stream].comments; +} + +void Info::EnumMetadata(int n, wchar_t *key, int keylen, wchar_t *val, int vallen) +{ + if(keylen) key[0]=0; + if(vallen) val[0]=0; + if(!vc) return; + if(!vc[stream].user_comments[n]) return; + AutoWide comment(vc[stream].user_comments[n],CP_UTF8); + const wchar_t* eq = wcschr((const wchar_t*)comment,L'='); + if(eq) + { + if(keylen) lstrcpynW(key,comment,(int)min(eq - comment + 1,keylen)); + if(vallen) lstrcpynW(val,eq+1,vallen); + } + else + { + if(keylen) lstrcpynW(key,L"COMMENT",keylen); + if(vallen) lstrcpynW(val,comment,vallen); + } +} + +void Info::RemoveMetadata(wchar_t * key) +{ + wchar_t k[256] = {0}; + for(int i=0; i<GetNumMetadataItems(); i++) + { + EnumMetadata(i,k,256,0,0); + if(_wcsicmp(k,key)==0) + RemoveMetadata(i); + } +} + +void Info::RemoveMetadata(int n) +{ + if(!vc) return; + free(vc[stream].user_comments[n]); + + for(int i=n+1; i<vc[stream].comments; i++) + { + vc[stream].user_comments[i-1] = vc[stream].user_comments[i]; + if(vc[stream].comment_lengths) + vc[stream].comment_lengths[i-1] = vc[stream].comment_lengths[i]; + } + vc[stream].comments--; + vc[stream].user_comments = (char**)realloc(vc[stream].user_comments,sizeof(vc[stream].user_comments[0]) * vc[stream].comments); + if(vc[stream].comment_lengths) + vc[stream].comment_lengths = (int*)realloc(vc[stream].comment_lengths,sizeof(vc[stream].comment_lengths[0]) * vc[stream].comments); +} + +void Info::SetMetadata(wchar_t *key, wchar_t *val) +{ + bool set=false; + wchar_t k[256] = {0}; + for(int i=0; i<GetNumMetadataItems(); i++) + { + EnumMetadata(i,k,256,0,0); + if(_wcsicmp(k,key)==0) + { + SetMetadata(i,key,val); + set=true; + } + } + if(!set) + { + int n = vc[stream].comments++; + vc[stream].user_comments = (char**)realloc(vc[stream].user_comments,sizeof(vc[stream].user_comments[0]) * vc[stream].comments); + if(vc[stream].comment_lengths) + vc[stream].comment_lengths = (int*)realloc(vc[stream].comment_lengths,sizeof(vc[stream].comment_lengths[0]) * vc[stream].comments); + vc[stream].user_comments[n] = NULL; + SetMetadata(n,key,val); + } +} + +void Info::SetMetadata(int n, wchar_t *key, wchar_t *val) +{ + AutoChar k(key,CP_UTF8); + AutoChar v(val,CP_UTF8); + + int l = (int)(strlen(k)+strlen(v)+2); + char * c = (char*)malloc(l); + StringCchPrintfA(c,l,"%s=%s",(char*)k,(char*)v); + + if(vc[stream].user_comments[n]) + free(vc[stream].user_comments[n]); + + vc[stream].user_comments[n] = c; + if(vc[stream].comment_lengths) + vc[stream].comment_lengths[n] = l-1; +} + +void Info::SetTag(int n,wchar_t *key) // changes the key name +{ + wchar_t val[2048] = {0}; + EnumMetadata(n,NULL,0,val,2048); + SetMetadata(n,key,val); +} + +Info *setMetadata = 0; + +extern "C" +{ + static wchar_t m_lastfn[2048]; + + #define START_TAG_ALIAS(name, alias) if (KeywordMatch(data, name)) lookup=alias + #define TAG_ALIAS(name, alias) else if (KeywordMatch(data, name)) lookup=alias + + __declspec( dllexport ) int winampSetExtendedFileInfoW(const wchar_t *fn, const char *data, wchar_t *val) + { + if (!setMetadata || setMetadata && wcscmp(fn,m_lastfn)) + { + if (setMetadata) + { + delete setMetadata; + setMetadata = 0; + } + + setMetadata = new Info(fn); + if(setMetadata->Error()) + { + delete setMetadata; + m_lastfn[0] = 0; + return 0; + } + lstrcpynW(m_lastfn,fn, 2048); + } + + wchar_t *lookup=0; + START_TAG_ALIAS("artist", L"ARTIST"); + TAG_ALIAS("title", L"TITLE"); + TAG_ALIAS("album", L"ALBUM"); + TAG_ALIAS("genre", L"GENRE"); + TAG_ALIAS("comment", L"COMMENT"); + TAG_ALIAS("year", L"DATE"); + TAG_ALIAS("track", L"TRACKNUMBER"); + TAG_ALIAS("albumartist", L"ALBUM ARTIST"); + TAG_ALIAS("composer", L"COMPOSER"); + TAG_ALIAS("disc", L"DISCNUMBER"); + TAG_ALIAS("publisher", L"PUBLISHER"); + TAG_ALIAS("conductor", L"CONDUCTOR"); + TAG_ALIAS("tool", L"ENCODED-BY"); + TAG_ALIAS("replaygain_track_gain", L"REPLAYGAIN_TRACK_GAIN"); + TAG_ALIAS("replaygain_track_peak", L"REPLAYGAIN_TRACK_PEAK"); + TAG_ALIAS("replaygain_album_gain", L"REPLAYGAIN_ALBUM_GAIN"); + TAG_ALIAS("replaygain_album_peak", L"REPLAYGAIN_ALBUM_PEAK"); + TAG_ALIAS("GracenoteFileID", L"GRACENOTEFILEID"); + TAG_ALIAS("GracenoteExtData", L"GRACENOTEEXTDATA"); + TAG_ALIAS("bpm", L"BPM"); + TAG_ALIAS("remixing", L"REMIXING"); + TAG_ALIAS("subtitle", L"VERSION"); + TAG_ALIAS("isrc", L"ISRC"); + TAG_ALIAS("category", L"CATEGORY"); + TAG_ALIAS("rating", L"RATING"); + TAG_ALIAS("producer", L"PRODUCER"); + + if (!lookup) + return 0; + +#if 0 + if (val && *val) + { + if(KeywordMatch("rating",data)) + { + wchar_t temp[128] = {0}; + StringCchPrintfW(temp, 128, L"%u", _wtoi(val)*20); + val=temp; + } + } + AutoChar utf8(val, CP_UTF8); + + for(int i=0;i<m_vc->comments;i++) + { + char *c=m_vc[m_curstream].user_comments[i]; + if(!c) continue; + char *p=strchr(c,'='); + if (p && *p) + { + if(strlen(data) == (p-c) && !_strnicmp(c,data,p-c)) + { + //found! + if (val && val[0]) + { + int added_buf_len = strlen(utf8)+strlen(lookup)+2; + m_vc[m_curstream].user_comments[i]=(char *)realloc(m_vc[m_curstream].user_comments[i],added_buf_len); + StringCchPrintfA(m_vc[m_curstream].user_comments[i],added_buf_len,"%s=%s",lookup,(char *)utf8); + m_vc[m_curstream].comment_lengths[i]=strlen(m_vc[m_curstream].user_comments[i]); + } + else + { + free(m_vc[m_curstream].user_comments[i]); + m_vc[m_curstream].user_comments[i]=0; + m_vc[m_curstream].comment_lengths[i]=0; + } + return 1; + } + } + } + + //not found, so create new field + if (val && val[0]) + { + int k=m_vc[m_curstream].comments++; + m_vc[m_curstream].user_comments=(char **)realloc(m_vc[m_curstream].user_comments,sizeof(char*)*m_vc[m_curstream].comments); + m_vc[m_curstream].comment_lengths=(int *)realloc(m_vc[m_curstream].comment_lengths,sizeof(int)*m_vc[m_curstream].comments); + int added_buf_len = strlen(utf8)+strlen(lookup)+2; + m_vc[m_curstream].user_comments[k]=(char *)malloc(added_buf_len); + StringCchPrintfA(m_vc[m_curstream].user_comments[k],added_buf_len,"%s=%s",lookup,(char *)utf8); + m_vc[m_curstream].comment_lengths[k]=strlen(m_vc[m_curstream].user_comments[k]); + } +#endif + + if (val && *val) + { + if(KeywordMatch("rating",data)) + { + wchar_t temp[128] = {0}; + StringCchPrintfW(temp, 128, L"%u", _wtoi(val)*20); + setMetadata->SetMetadata(lookup, temp); + } + else + { + setMetadata->SetMetadata(lookup, val); + } + } + else + { + setMetadata->RemoveMetadata(lookup); + if(KeywordMatch("comment",data)) + { + // need to remove this one also, or else it's gonna look like delete doesn't work + // if the file was tagged using this alternate field + setMetadata->RemoveMetadata(L"DESCRIPTION"); + } + else if(KeywordMatch("year",data)) + { + // need to remove this one also, or else it's gonna look like delete doesn't work + // if the file was tagged using this alternate field + setMetadata->RemoveMetadata(L"YEAR"); + } + else if(KeywordMatch("track",data)) + { + // need to remove this one also, or else it's gonna look like delete doesn't work + // if the file was tagged using this alternate field + setMetadata->RemoveMetadata(L"TRACK"); + } + else if(KeywordMatch("albumartist",data)) + { + // need to remove these two, also, or else it's gonna look like delete doesn't work + // if the file was tagged using these alternate fields + setMetadata->RemoveMetadata(L"ALBUMARTIST"); + setMetadata->RemoveMetadata(L"ENSEMBLE"); + } + else if(KeywordMatch("publisher",data)) + { + // need to remove this one also, or else it's gonna look like delete doesn't work + // if the file was tagged using this alternate field + setMetadata->RemoveMetadata(L"ORGANIZATION"); + } + else if(KeywordMatch("category",data)) + { + // need to remove these two also, or else it's gonna look like delete doesn't work + // if the file was tagged using these alternate fields + setMetadata->RemoveMetadata(L"CONTENTGROUP"); + setMetadata->RemoveMetadata(L"GROUPING"); + } + } + + return 1; + } + + __declspec( dllexport ) int winampWriteExtendedFileInfo() + { + if(!setMetadata) return 0; + + bool ret = setMetadata->Save(); + delete setMetadata; + setMetadata = 0; + + // update last modified so we're not re-queried on our own updates + UpdateFileTimeChanged(m_lastfn); + + return ret; + } +}
\ No newline at end of file diff --git a/Src/Plugins/Input/in_vorbis/winnt_helper.cpp b/Src/Plugins/Input/in_vorbis/winnt_helper.cpp new file mode 100644 index 00000000..da44ca42 --- /dev/null +++ b/Src/Plugins/Input/in_vorbis/winnt_helper.cpp @@ -0,0 +1,2 @@ +#include "main.h" +#include "api__in_vorbis.h" |