📄 mp3.c
字号:
/* @(#) mp3.c, last edit: 4/9/1998
* @(#) Copyright (C) 1998 Syntrillium Software (www.syntrillium.com)
Cool Edit (and Cool Edit Pro) MP3 file reader based on MAPLAY by Jeff Tsay
A bunch of convoluted code was added to this file filter in order to allow
for the choice of volume level during decode, and format to decode to.
That's right, you can decode to 16-bit by truncation, or by dithering, or
keep an entire 32-bit float for each sample for use in Cool Edit Pro.
Versions Cool Edit that do not support 32-bit will still say "32-bit" when
the file is loaded, but rest assured, it's only 16-bit.
Most of the files for MAPLAY were left untouched, and many were not needed
for this port, so only the files needed for the port are included here.
Search for MAPLAY 1.2 or later for original source (and if the source gets
updated, it should be trivial to add it to this build). Very few of the
other files were modified, and when they were, they were bracketed by
"#ifdef COOLPRO" for easy location and such.
The Obuffer class was modified to take in floats instead of ints for
reading in with greater accuracy. Since MPEG (which is similar to JPEG
in this respect) doesn't try to encode the exact sample values, but
instead is psychoacoustic in nature, higher quality can be gained by
encoding from a 32-bit source (I haven't found an encoder that can take
floating point samples yet) and then decoding back to 32-bit. Nothing
in MPEG says it should be limited to 16-bit I believe (but don't quote
me on this... I do not know much about the internals of the MPEG format).
Some links to the binaries of ports of maplay are on Jeff's homepage at:
http://www-inst.eecs.berkeley.edu/~ctsay/mp2win32.html
Believe it or not, the speed is much faster than original ISO MPEG code,
but still slower than Fraunhoffer. Their Windows ACM codec is about
twice as fast for reading. But the good news is it that it's still fast
enough for real time previewing (auto-play) on pretty much all Pentiums.
If you purchase an ACM codec for writing Layer 3 MPEG, use the updated
ACM Waveform format type found at http://www.syntrillium.com which allows
for saving any ACM format as raw data thus allowing the saving directly
to .mp3 files if you have the codec which can write MPEG .wav files.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <windows.h>
#include <math.h>
#include "filters.h"
#include "ceglue.h"
#include "resource.h"
// Hey! Someone want a _real_ challenge? Implement Layer 3 encoding as well!
typedef struct output_tag // any special vars associated with output file
{ short nFile;
long lSize;
} MYOUTPUT;
typedef struct input_tag // any special vars associated with input file
{ short nFile;
long lSize;
long lSamprate;
DWORD dwFormat;
BOOL bOpenedMPEG;
MPEG_INFO mpi;
double fAttenuation;
float fScaleBy;
WORD wBitsPerSample;
BOOL bDither;
HANDLE h32bit;
short * pi32bit;
} MYINPUT;
HANDLE myInstance=NULL;
BOOL WINAPI DllMain (HANDLE hModule, DWORD fdwReason, LPVOID lpReserved)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
myInstance=hModule;
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
myInstance=NULL;
break;
}
return TRUE;
}
// Fill COOLQUERY structure with information regarding this file filter
__declspec(dllexport) short FAR PASCAL QueryCoolFilter(COOLQUERY far * cq)
{ lstrcpy(cq->szName,"MPEG 3");
lstrcpy(cq->szCopyright,"MPEG (Layer 1,2,3) Decoder (GNU)");
lstrcpy(cq->szExt,"MP3");
lstrcpy(cq->szExt2,"MP2");
lstrcpy(cq->szExt3,"MP1");
cq->lChunkSize=16384;
cq->dwFlags=QF_RATEADJUSTABLE|QF_CANLOAD|QF_CANDO32BITFLOATS|QF_HASOPTIONSBOX;
cq->Stereo8=0;
cq->Stereo16=R_32075|R_44100|R_48000;
cq->Stereo24=0;
cq->Stereo32=R_32075|R_44100|R_48000;
cq->Mono8=0;
cq->Mono16=R_32075|R_44100|R_48000;
cq->Mono24=0;
cq->Mono32=R_32075|R_44100|R_48000;
cq->Quad32=0;
cq->Quad16=0;
cq->Quad8=0;
return C_VALIDLIBRARY;
}
// Just our little way of not including stdio.h to do float to string cvting
char * ftos(char *tmp, double d)
{ double dd;
double mplier;
short maxdecimal=9;
char * otemp;
otemp=tmp;
if (d==0)
{ *tmp='0';
tmp++;
*tmp=0x00;
return otemp;
}
if (d<0)
{ *tmp='-';
tmp++;
d=-d;
}
d=d+(1.0/65535.0/32768.0);
dd=d;
mplier=1.0;
while (dd>=1.0)
{ dd/=10.0;
mplier*=10.0;
}
mplier/=10.0;
while (mplier>=1.0)
{ *tmp='0'+(short)(d/mplier);
tmp++;
d=d-mplier*(short)(d/mplier);
mplier/=10.0;
}
if (d!=0.0)
{ short digit;
*tmp='.';
tmp++;
while ((d!=0.0) && (maxdecimal!=0))
{ d*=10.0;
digit=(short)d;
d-=(double)digit;
*tmp='0'+digit;
tmp++;
maxdecimal--;
}
while (*(tmp-1)=='0')
tmp--;
if (*(tmp-1)=='.')
tmp--;
}
*tmp=0x00;
return otemp;
}
__declspec(dllexport) long FAR PASCAL FilterGetFileSize(HANDLE hInput)
{
long lSize=0L;
if (hInput)
{ MYINPUT *mi;
mi=(MYINPUT *)GlobalLock(hInput);
lSize=mi->lSize;
GlobalUnlock(hInput);
}
return lSize;
}
__declspec(dllexport) BOOL FAR PASCAL FilterGetActualFormat(HANDLE hInput,DWORD dwOptions,long * plSamprate, WORD * pwChannels, WORD * pwBitsPerSample)
{
if (hInput)
{ MYINPUT *mi;
mi=(MYINPUT *)GlobalLock(hInput);
*pwBitsPerSample=HIWORD(dwOptions)&0x00FF;
GlobalUnlock(hInput);
}
return TRUE;
}
__declspec(dllexport) BOOL FAR PASCAL FilterUnderstandsFormat(LPSTR filename)
{
if ((lstrlen(filename)>4) &&
((strcmpi(filename+lstrlen(filename)-4,".mp3")==0) ||
(strcmpi(filename+lstrlen(filename)-4,".mp2")==0) ||
(strcmpi(filename+lstrlen(filename)-4,".mp1")==0)
))
{
return TRUE;
}
return FALSE;
}
__declspec(dllexport) BOOL FAR PASCAL DIALOGMsgProc(HWND hWndDlg, UINT Message, WPARAM wParam, LPARAM lParam)
{ switch(Message)
{ case WM_INITDIALOG:
{ WORD wBitsPerSample;
double fAttenuation;
BOOL bDither;
char tmp[30];
wBitsPerSample=HIWORD(lParam)&0x00FF;
bDither=(lParam&0x01000000)?TRUE:FALSE;
fAttenuation=(double)((DWORD)lParam&0xffff)/100.0-200.0;
if (fAttenuation>200.0)
fAttenuation=0.0;
//wsprintf(tmp," Read MPEG As %d-bit ",wBitsPerSample);
//SetDlgItemText(hWndDlg,IDC_TEXT1,tmp);
if (wBitsPerSample==16)
{ CheckDlgButton(hWndDlg,IDC_RADIO1,TRUE);
}
else if (wBitsPerSample==32)
{ CheckDlgButton(hWndDlg,IDC_RADIO2,TRUE);
}
else
{ CheckDlgButton(hWndDlg,IDC_RADIO1,TRUE);
}
CheckDlgButton(hWndDlg,IDC_CHECK1,bDither);
ftos(tmp,fAttenuation);
SetDlgItemText(hWndDlg,IDC_EDIT1,tmp);
EnableWindow(GetDlgItem(hWndDlg,IDC_CHECK1),IsDlgButtonChecked(hWndDlg,IDC_RADIO1)?TRUE:FALSE);
}
break;
case WM_CLOSE:
PostMessage(hWndDlg, WM_COMMAND, IDCANCEL, 0L);
break;
case WM_COMMAND:
{ WORD wNotifyCode=HIWORD(wParam);
wParam=(WPARAM)LOWORD(wParam);
switch(wParam)
{
case IDC_RADIO1:
case IDC_RADIO2:
EnableWindow(GetDlgItem(hWndDlg,IDC_CHECK1),IsDlgButtonChecked(hWndDlg,IDC_RADIO1)?TRUE:FALSE);
break;
case IDOK:
{ long nDialogReturn=0;
char tmp[30];
if (IsDlgButtonChecked(hWndDlg,IDC_RADIO2))
{ nDialogReturn=32L<<16;
}
else
{ nDialogReturn=16L<<16;
}
GetDlgItemText(hWndDlg,IDC_EDIT1,tmp,29);
nDialogReturn|=(WORD)max(0,min(40000,(atof(tmp)+200.0)*100.0));
if (IsDlgButtonChecked(hWndDlg,IDC_CHECK1))
nDialogReturn|=0x01000000;
EndDialog(hWndDlg, nDialogReturn);
}
break;
case IDCANCEL:
EndDialog(hWndDlg, FALSE);
break;
}
break;
}
default:
return FALSE;
}
return TRUE;
}
__declspec(dllexport) DWORD FAR PASCAL FilterGetOptions(HWND hWnd, HINSTANCE hInst, long lSamprate, WORD wChannels, WORD wBitsPerSample, DWORD dwOptions) // return 0 if no options box
{
long nDialogReturn=0L;
FARPROC lpfnDIALOGMsgProc;
lpfnDIALOGMsgProc = GetProcAddress(hInst,(LPCSTR)MAKELONG(20,0));
if (dwOptions==0)
{
nDialogReturn=GetPrivateProfileInt("MPEG Filter","InputFormat",0,"coolrw.ini");
if (nDialogReturn==0)
nDialogReturn=(WORD)max(0,min(40000,(100.0+200.0)*100.0));
}
else
nDialogReturn=dwOptions;
// These were to force the dialog to have _only_ 16 or 32... now we let user choose
//nDialogReturn=(nDialogReturn&0xFF00FFFF)|(((DWORD)wBitsPerSample)<<8);
//nDialogReturn|=MAKELONG(nDialogReturn&0x0FF,wBitsPerSample);
nDialogReturn = (long)DialogBoxParam((HINSTANCE)hInst,(LPCSTR)MAKEINTRESOURCE(IDD_COMPRESSION), (HWND)hWnd, (DLGPROC)lpfnDIALOGMsgProc,nDialogReturn);
if (nDialogReturn)
{ char m[30];
wsprintf(m,"%d",nDialogReturn);
WritePrivateProfileString("MPEG Filter","InputFormat",m,"coolrw.ini");
}
return nDialogReturn;
}
__declspec(dllexport) DWORD FAR PASCAL FilterSetOptions2(HANDLE hInput, DWORD dwOptions, long * plSamprate, WORD * pwChannels, WORD * pwBitsPerSample)
{
DWORD dwOldOptions;
MYINPUT *mi;
mi=(MYINPUT *)GlobalLock(hInput);
dwOldOptions=mi->dwFormat;
if (!dwOptions)
{ dwOptions=(DWORD)GetPrivateProfileInt("MPEG Filter","InputFormat",0,"coolrw.ini");
if (!dwOptions)
dwOptions=(WORD)max(0,min(40000,(200.0)*100.0))|(16L<<16);
}
mi->wBitsPerSample=HIWORD(dwOptions)&0x00FF;
mi->fAttenuation=(double)((DWORD)dwOptions&0xffff)/100.0-200.0;
mi->bDither=(dwOptions&0x01000000)?TRUE:FALSE;
mi->dwFormat=dwOptions;
if ((mi->fAttenuation<-199.0) || (mi->fAttenuation>200.0))
mi->fAttenuation=0;
if (mi->wBitsPerSample==16)
{ *pwBitsPerSample=16;
if (!mi->h32bit)
{ mi->h32bit=GlobalAlloc(GMEM_MOVEABLE|GMEM_ZEROINIT,32768);
mi->pi32bit=(short *)GlobalLock(mi->h32bit);
}
}
else
{ *pwBitsPerSample=32;
}
mi->lSize=(int)(mi->mpi.fLengthInMilliseconds*(*plSamprate)/1000.0)*(*pwChannels)*(*pwBitsPerSample/8);
mi->fScaleBy=(float)(32768.0*(pow(10.0,mi->fAttenuation/20.0)));
GlobalUnlock(hInput);
return dwOldOptions;
}
// Dummy, but must be here... FilterSetOptions2 is actually called instead if it exists
__declspec(dllexport) DWORD FAR PASCAL FilterSetOptions(HANDLE hInput, DWORD dwOptions, long lSamprate, WORD wChannels, WORD wBitsPerSample)
{
DWORD dwOldOptions;
MYINPUT *mi;
mi=(MYINPUT *)GlobalLock(hInput);
dwOldOptions=mi->dwFormat;
mi->dwFormat=dwOptions;
mi->wBitsPerSample=HIWORD(dwOptions)&0x00FF;
mi->fAttenuation=(double)((DWORD)dwOptions&0xffff)/100.0-200.0;
mi->bDither=(dwOptions&0x01000000)?TRUE:FALSE;
if ((mi->fAttenuation<-199.0) || (mi->fAttenuation>200.0))
mi->fAttenuation=0;
if (mi->wBitsPerSample==16)
{ if (!mi->h32bit)
{ mi->h32bit=GlobalAlloc(GMEM_MOVEABLE|GMEM_ZEROINIT,32768);
mi->pi32bit=(short *)GlobalLock(mi->h32bit);
}
}
mi->lSize=(int)(mi->mpi.fLengthInMilliseconds*(lSamprate)/1000.0)*(wChannels)*(wBitsPerSample/8);
mi->fScaleBy=(float)(32768.0*(pow(10.0,mi->fAttenuation/20.0)));
GlobalUnlock(hInput);
return dwOldOptions;
}
__declspec(dllexport) DWORD FAR PASCAL FilterOptions(HANDLE hInput)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -