You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1662 lines
47 KiB
1662 lines
47 KiB
/* (C) Copyright Microsoft Corporation 1991-1994. All Rights Reserved */
|
|
/* convert.c
|
|
*
|
|
* conversion utilites.
|
|
*/
|
|
#include <windows.h>
|
|
#include <windowsx.h>
|
|
#include <mmsystem.h>
|
|
#include <mmreg.h>
|
|
#include <commctrl.h>
|
|
#include <msacm.h>
|
|
#include <commdlg.h>
|
|
#include <dlgs.h>
|
|
#include <convert.h>
|
|
#include <msacmdlg.h>
|
|
|
|
#include "soundrec.h"
|
|
#ifdef CHICAGO
|
|
#include "helpids.h"
|
|
#endif
|
|
|
|
#define STRSAFE_LIB
|
|
#include <strsafe.h>
|
|
|
|
BOOL gfBreakOfDeath;
|
|
/*
|
|
**/
|
|
LPTSTR SoundRec_GetFormatName(
|
|
LPWAVEFORMATEX pwfx)
|
|
{
|
|
ACMFORMATTAGDETAILS aftd;
|
|
ACMFORMATDETAILS afd;
|
|
const TCHAR szFormat[] = TEXT("%s %s");
|
|
LPTSTR lpstr;
|
|
UINT cbstr;
|
|
HRESULT hr;
|
|
|
|
ZeroMemory(&aftd, sizeof(ACMFORMATTAGDETAILS));
|
|
aftd.cbStruct = sizeof(ACMFORMATTAGDETAILS);
|
|
aftd.dwFormatTag = pwfx->wFormatTag;
|
|
|
|
if (MMSYSERR_NOERROR != acmFormatTagDetails( NULL
|
|
, &aftd
|
|
, ACM_FORMATTAGDETAILSF_FORMATTAG))
|
|
{
|
|
aftd.szFormatTag[0] = 0;
|
|
}
|
|
|
|
ZeroMemory(&afd, sizeof(ACMFORMATDETAILS));
|
|
afd.cbStruct = sizeof(ACMFORMATDETAILS);
|
|
afd.pwfx = pwfx;
|
|
afd.dwFormatTag = pwfx->wFormatTag;
|
|
|
|
afd.cbwfx = sizeof(WAVEFORMATEX);
|
|
if (pwfx->wFormatTag != WAVE_FORMAT_PCM)
|
|
afd.cbwfx += pwfx->cbSize;
|
|
|
|
if (MMSYSERR_NOERROR != acmFormatDetails( NULL
|
|
, &afd
|
|
, ACM_FORMATDETAILSF_FORMAT))
|
|
{
|
|
afd.szFormat[0] = 0;
|
|
}
|
|
|
|
cbstr = sizeof(LPTSTR) * ( lstrlen(aftd.szFormatTag) + lstrlen(afd.szFormat) + lstrlen(szFormat));
|
|
lpstr = (LPTSTR) GlobalAllocPtr(GHND, cbstr);
|
|
if (lpstr)
|
|
{
|
|
hr = StringCbPrintf(lpstr, cbstr, szFormat, aftd.szFormatTag, afd.szFormat);
|
|
Assert( hr == S_OK );
|
|
}
|
|
return lpstr;
|
|
}
|
|
|
|
/*
|
|
* SaveAsHookProc
|
|
*
|
|
* This is a hook proc for the Save As common dialog to support conversion
|
|
* upon save.
|
|
**/
|
|
UINT_PTR CALLBACK SaveAsHookProc(
|
|
HWND hdlg,
|
|
UINT msg,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
#ifdef CHICAGO
|
|
int DlgItem;
|
|
static const DWORD aHelpIds[] = {
|
|
IDC_TXT_FORMAT, IDH_SOUNDREC_CHANGE,
|
|
IDC_CONVERTTO, IDH_SOUNDREC_CHANGE,
|
|
IDC_CONVERT_TO, IDH_SOUNDREC_CHANGE,
|
|
0, 0
|
|
};
|
|
extern DWORD aChooserHelpIds[];
|
|
extern UINT guChooserContextMenu;
|
|
extern UINT guChooserContextHelp;
|
|
|
|
//
|
|
// Handle context-sensitive help messages from acm dialog
|
|
//
|
|
if( msg == guChooserContextMenu )
|
|
{
|
|
WinHelp( (HWND)wParam, NULL, HELP_CONTEXTMENU,
|
|
(UINT_PTR)(LPSTR)aChooserHelpIds );
|
|
return TRUE;
|
|
}
|
|
else if( msg == guChooserContextHelp )
|
|
{
|
|
WinHelp( ((LPHELPINFO)lParam)->hItemHandle, NULL,
|
|
HELP_WM_HELP, (UINT_PTR)(LPSTR)aChooserHelpIds );
|
|
return TRUE;
|
|
}
|
|
|
|
#endif
|
|
|
|
switch (msg)
|
|
{
|
|
#ifdef CHICAGO
|
|
case WM_CONTEXTMENU:
|
|
DlgItem = GetDlgCtrlID((HWND)wParam);
|
|
|
|
//
|
|
// Only process the id's we are responsible for
|
|
//
|
|
if (DlgItem != IDC_CONVERTTO && DlgItem != IDC_CONVERT_TO && DlgItem != IDC_TXT_FORMAT)
|
|
break;
|
|
|
|
WinHelp((HWND)wParam, gachHelpFile, HELP_CONTEXTMENU,
|
|
(UINT_PTR)(LPSTR)aHelpIds);
|
|
|
|
return TRUE;
|
|
|
|
case WM_HELP:
|
|
{
|
|
LPHELPINFO lphi = (LPVOID) lParam;
|
|
|
|
//
|
|
// Only process the id's we are responsible for
|
|
//
|
|
DlgItem = GetDlgCtrlID(lphi->hItemHandle);
|
|
if (DlgItem != IDC_CONVERTTO && DlgItem != IDC_CONVERT_TO && DlgItem != IDC_TXT_FORMAT)
|
|
break;
|
|
WinHelp (lphi->hItemHandle, gachHelpFile, HELP_WM_HELP,
|
|
(UINT_PTR) (LPSTR) aHelpIds);
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
case WM_INITDIALOG:
|
|
{
|
|
LPTSTR lpszFormat;
|
|
PWAVEFORMATEX * ppwfx;
|
|
|
|
// passed in through lCustData
|
|
ppwfx = (PWAVEFORMATEX *)((OPENFILENAME *)(LPVOID)lParam)->lCustData;
|
|
|
|
SetProp(hdlg, TEXT("DATA"), (HANDLE)ppwfx);
|
|
|
|
lpszFormat = SoundRec_GetFormatName(gpWaveFormat);
|
|
if (lpszFormat)
|
|
{
|
|
SetDlgItemText(hdlg, IDC_CONVERT_TO, lpszFormat);
|
|
SetDlgItemText(hdlg, IDC_CONVERT_FROM, lpszFormat);
|
|
GlobalFreePtr(lpszFormat);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
{
|
|
PWAVEFORMATEX *ppwfx = (PWAVEFORMATEX *)GetProp(hdlg, TEXT("DATA"));
|
|
int id = GET_WM_COMMAND_ID(wParam, lParam);
|
|
switch (id)
|
|
{
|
|
case IDC_CONVERTTO:
|
|
{
|
|
PWAVEFORMATEX pwfxNew = NULL;
|
|
if (ChooseDestinationFormat(ghInst
|
|
,hdlg
|
|
,NULL
|
|
,&pwfxNew
|
|
,0L) == MMSYSERR_NOERROR)
|
|
{
|
|
LPTSTR lpszFormat;
|
|
if (*ppwfx)
|
|
GlobalFreePtr(*ppwfx);
|
|
|
|
//
|
|
// set string name
|
|
//
|
|
lpszFormat = SoundRec_GetFormatName(pwfxNew);
|
|
if (lpszFormat)
|
|
{
|
|
SetDlgItemText(hdlg, IDC_CONVERT_TO, lpszFormat);
|
|
GlobalFreePtr(lpszFormat);
|
|
}
|
|
//
|
|
// do something
|
|
//
|
|
*ppwfx = pwfxNew;
|
|
}
|
|
return TRUE;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_DESTROY:
|
|
RemoveProp(hdlg, TEXT("DATA"));
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Launches the chooser dialog for changing the destination format.
|
|
*/
|
|
MMRESULT FAR PASCAL
|
|
ChooseDestinationFormat(
|
|
HINSTANCE hInst,
|
|
HWND hwndParent,
|
|
PWAVEFORMATEX pwfxIn,
|
|
PWAVEFORMATEX *ppwfxOut,
|
|
DWORD fdwEnum)
|
|
{
|
|
ACMFORMATCHOOSE cwf;
|
|
MMRESULT mmr;
|
|
LPWAVEFORMATEX pwfx;
|
|
DWORD dwMaxFormatSize;
|
|
|
|
mmr = acmMetrics(NULL
|
|
, ACM_METRIC_MAX_SIZE_FORMAT
|
|
, (LPVOID)&dwMaxFormatSize);
|
|
|
|
if (mmr != MMSYSERR_NOERROR)
|
|
return mmr;
|
|
|
|
pwfx = (LPWAVEFORMATEX)GlobalAllocPtr(GHND, (UINT)dwMaxFormatSize);
|
|
if (pwfx == NULL)
|
|
return MMSYSERR_NOMEM;
|
|
|
|
memset(&cwf, 0, sizeof(cwf));
|
|
|
|
cwf.cbStruct = sizeof(cwf);
|
|
cwf.hwndOwner = hwndParent;
|
|
cwf.fdwStyle = 0L;
|
|
|
|
cwf.fdwEnum = 0L; // all formats!
|
|
cwf.pwfxEnum = NULL; // all formats!
|
|
|
|
if (fdwEnum)
|
|
{
|
|
cwf.fdwEnum = fdwEnum;
|
|
cwf.pwfxEnum = pwfxIn;
|
|
}
|
|
|
|
cwf.pwfx = (LPWAVEFORMATEX)pwfx;
|
|
cwf.cbwfx = dwMaxFormatSize;
|
|
|
|
#ifdef CHICAGO
|
|
cwf.fdwStyle |= ACMFORMATCHOOSE_STYLEF_CONTEXTHELP;
|
|
#endif
|
|
|
|
mmr = acmFormatChoose(&cwf);
|
|
|
|
if (mmr == MMSYSERR_NOERROR)
|
|
*ppwfxOut = pwfx;
|
|
else
|
|
{
|
|
*ppwfxOut = NULL;
|
|
GlobalFreePtr(pwfx);
|
|
}
|
|
return mmr;
|
|
|
|
}
|
|
#ifndef WM_APP
|
|
#define WM_APP 0x8000
|
|
#endif
|
|
|
|
#define MYWM_CANCEL (WM_APP+0)
|
|
#define MYWM_PROGRESS (WM_APP+1)
|
|
|
|
/* Update a progress dialog
|
|
*/
|
|
BOOL
|
|
ProgressUpdate (
|
|
PPROGRESS pPrg,
|
|
DWORD dwCompleted)
|
|
{
|
|
DWORD dwDone;
|
|
BOOL fCancel;
|
|
|
|
if (pPrg == NULL)
|
|
return TRUE;
|
|
|
|
dwDone = (dwCompleted * pPrg->dwTotal) / 100L + pPrg->dwComplete;
|
|
|
|
if (!IsWindow(pPrg->hPrg))
|
|
return FALSE;
|
|
|
|
SendMessage( pPrg->hPrg, MYWM_CANCEL, (WPARAM)&fCancel, 0L);
|
|
if (fCancel)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
PostMessage( pPrg->hPrg, MYWM_PROGRESS, (WPARAM)dwDone, 0L);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// should support a file handle as well.
|
|
//
|
|
|
|
typedef struct tConvertParam {
|
|
PWAVEFORMATEX pwfxSrc; // pwfx specifying source format
|
|
DWORD cbSrc; // size of the source buffer
|
|
LPBYTE pbSrc; // source buffer
|
|
PWAVEFORMATEX pwfxDst; // pwfx specifying dest format
|
|
DWORD * pcbDst; // return size of the dest buffer
|
|
LPBYTE * ppbDst; // dest buffer
|
|
DWORD cBlks; // number of blocks
|
|
PROGRESS Prg; // progress update
|
|
MMRESULT mmr; // MMSYSERR result
|
|
HANDLE hThread; // private
|
|
} ConvertParam, *PConvertParam;
|
|
|
|
/*
|
|
* */
|
|
DWORD ConvertThreadProc(LPVOID lpv)
|
|
{
|
|
PConvertParam pcp = (PConvertParam)lpv;
|
|
MMRESULT mmr;
|
|
|
|
mmr = ConvertMultipleFormats(pcp->pwfxSrc
|
|
, pcp->cbSrc
|
|
, pcp->pbSrc
|
|
, pcp->pwfxDst
|
|
, pcp->pcbDst
|
|
, pcp->ppbDst
|
|
, pcp->cBlks
|
|
, &pcp->Prg);
|
|
|
|
pcp->mmr = mmr;
|
|
PostMessage(pcp->Prg.hPrg, WM_CLOSE, 0, 0);
|
|
|
|
return 0; // end of thread!
|
|
}
|
|
|
|
static BOOL gfCancel = FALSE;
|
|
|
|
/*
|
|
* Progress_OnCommand
|
|
* */
|
|
void Progress_OnCommand(
|
|
HWND hdlg,
|
|
int id,
|
|
HWND hctl,
|
|
UINT unotify)
|
|
{
|
|
switch (id)
|
|
{
|
|
case IDCANCEL:
|
|
gfCancel = TRUE;
|
|
EndDialog(hdlg, FALSE);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Progress_Proc
|
|
* */
|
|
INT_PTR CALLBACK
|
|
Progress_Proc(
|
|
HWND hdlg,
|
|
UINT umsg,
|
|
WPARAM wparam,
|
|
LPARAM lparam)
|
|
{
|
|
switch (umsg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
HANDLE hThread;
|
|
PConvertParam pcp = (PConvertParam)(LPVOID)lparam;
|
|
HWND hprg;
|
|
DWORD thid;
|
|
LPTSTR lpsz;
|
|
|
|
hprg = GetDlgItem(hdlg, IDC_PROGRESSBAR);
|
|
|
|
SendMessage(hprg, PBM_SETRANGE, 0, MAKELONG(0, 100));
|
|
SendMessage(hprg, PBM_SETPOS, 0, 0);
|
|
|
|
SetProp(hdlg, TEXT("DATA"), (HANDLE)pcp);
|
|
pcp->Prg.hPrg = hdlg;
|
|
pcp->Prg.dwTotal = 100;
|
|
gfCancel = FALSE;
|
|
|
|
lpsz = SoundRec_GetFormatName(pcp->pwfxSrc);
|
|
if (lpsz)
|
|
{
|
|
SetDlgItemText(hdlg, IDC_CONVERT_FROM, lpsz);
|
|
GlobalFreePtr(lpsz);
|
|
}
|
|
|
|
lpsz = SoundRec_GetFormatName(pcp->pwfxDst);
|
|
if (lpsz)
|
|
{
|
|
SetDlgItemText(hdlg, IDC_CONVERT_TO, lpsz);
|
|
GlobalFreePtr(lpsz);
|
|
}
|
|
|
|
hThread = CreateThread( NULL // no special security
|
|
, 0 // default stack size
|
|
, (LPTHREAD_START_ROUTINE)ConvertThreadProc
|
|
, (LPVOID)pcp
|
|
, 0 // start running at once
|
|
, &thid );
|
|
|
|
pcp->hThread = hThread;
|
|
break;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
HANDLE_WM_COMMAND(hdlg, wparam, lparam, Progress_OnCommand);
|
|
break;
|
|
|
|
case MYWM_CANCEL:
|
|
{
|
|
BOOL *pf = (BOOL *)wparam;
|
|
if (pf)
|
|
*pf = gfCancel;
|
|
break;
|
|
}
|
|
|
|
case MYWM_PROGRESS:
|
|
{
|
|
HWND hprg = GetDlgItem(hdlg, IDC_PROGRESSBAR);
|
|
SendMessage(hprg, PBM_SETPOS, wparam, 0);
|
|
break;
|
|
}
|
|
|
|
case WM_DESTROY:
|
|
{
|
|
PConvertParam pcp = (ConvertParam *)GetProp(hdlg, TEXT("DATA"));
|
|
if (pcp)
|
|
{
|
|
//
|
|
// Make sure the thread exits
|
|
//
|
|
if (pcp->hThread)
|
|
{
|
|
WaitForSingleObject(pcp->hThread, 1000);
|
|
CloseHandle(pcp->hThread);
|
|
pcp->hThread = NULL;
|
|
}
|
|
RemoveProp(hdlg, TEXT("DATA"));
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/* Generic single step conversion.
|
|
*/
|
|
MMRESULT
|
|
ConvertFormatDialog(
|
|
HWND hParent,
|
|
PWAVEFORMATEX pwfxSrc, // pwfx specifying source format
|
|
DWORD cbSrc, // size of the source buffer
|
|
LPBYTE pbSrc, // source buffer
|
|
PWAVEFORMATEX pwfxDst, // pwfx specifying dest format
|
|
DWORD * pcbDst, // return size of the dest buffer
|
|
LPBYTE * ppbDst, // dest buffer
|
|
DWORD cBlks, // number of blocks
|
|
PPROGRESS pPrg) // progress update
|
|
{
|
|
ConvertParam cp;
|
|
|
|
*pcbDst = 0;
|
|
*ppbDst = NULL;
|
|
|
|
if (cbSrc == 0)
|
|
return MMSYSERR_NOERROR;
|
|
|
|
cp.pwfxSrc = pwfxSrc;
|
|
cp.cbSrc = cbSrc;
|
|
cp.pbSrc = pbSrc;
|
|
cp.pwfxDst = pwfxDst;
|
|
cp.pcbDst = pcbDst;
|
|
cp.ppbDst = ppbDst;
|
|
cp.cBlks = cBlks;
|
|
cp.mmr = MMSYSERR_ERROR; // fail on abnormal thread termination!
|
|
cp.hThread = NULL;
|
|
|
|
DialogBoxParam(ghInst
|
|
, MAKEINTRESOURCE(IDD_CONVERTING)
|
|
, hParent
|
|
, Progress_Proc
|
|
, (LPARAM)(LPVOID)&cp);
|
|
return cp.mmr;
|
|
}
|
|
|
|
#if DBG
|
|
void DumpASH(
|
|
MMRESULT mmr,
|
|
ACMSTREAMHEADER *pash)
|
|
{
|
|
TCHAR sz[256];
|
|
HRESULT hr;
|
|
|
|
hr = StringCchPrintf(sz,SIZEOF(sz),TEXT("mmr = %lx\r\n"), mmr);
|
|
Assert( hr == S_OK );
|
|
OutputDebugString(sz);
|
|
|
|
hr = StringCchPrintf(sz,SIZEOF(sz),TEXT("pash is %s\r\n"),IsBadWritePtr(pash, pash->cbStruct)?TEXT("bad"):TEXT("good"));
|
|
Assert( hr == S_OK );
|
|
OutputDebugString(sz);
|
|
|
|
OutputDebugString(TEXT("ACMSTREAMHEADER {\r\n"));
|
|
hr = StringCchPrintf(sz,SIZEOF(sz),TEXT("ash.cbStruct = %lx\r\n"),pash->cbStruct);
|
|
Assert( hr == S_OK );
|
|
OutputDebugString(sz);
|
|
|
|
hr = StringCchPrintf(sz,SIZEOF(sz),TEXT("ash.fdwStatus = %lx\r\n"),pash->fdwStatus);
|
|
Assert( hr == S_OK );
|
|
OutputDebugString(sz);
|
|
|
|
hr = StringCchPrintf(sz,SIZEOF(sz),TEXT("ash.pbSrc = %lx\r\n"),pash->pbSrc);
|
|
Assert( hr == S_OK );
|
|
OutputDebugString(sz);
|
|
|
|
hr = StringCchPrintf(sz,SIZEOF(sz),TEXT("ash.cbSrcLength = %lx\r\n"),pash->cbSrcLength);
|
|
Assert( hr == S_OK );
|
|
OutputDebugString(sz);
|
|
|
|
hr = StringCchPrintf(sz,SIZEOF(sz),TEXT("pbSrc is %s\r\n"),IsBadWritePtr(pash->pbSrc, pash->cbSrcLength)?TEXT("bad"):TEXT("good"));
|
|
Assert( hr == S_OK );
|
|
OutputDebugString(sz);
|
|
|
|
hr = StringCchPrintf(sz,SIZEOF(sz),TEXT("ash.cbSrcLengthUsed= %lx\r\n"),pash->cbSrcLengthUsed);
|
|
Assert( hr == S_OK );
|
|
OutputDebugString(sz);
|
|
|
|
hr = StringCchPrintf(sz,SIZEOF(sz),TEXT("ash.pbDst = %lx\r\n"),pash->pbDst);
|
|
Assert( hr == S_OK );
|
|
OutputDebugString(sz);
|
|
|
|
hr = StringCchPrintf(sz,SIZEOF(sz),TEXT("ash.cbDstLength = %lx\r\n"),pash->cbDstLength);
|
|
Assert( hr == S_OK );
|
|
OutputDebugString(sz);
|
|
|
|
hr = StringCchPrintf(sz,SIZEOF(sz),TEXT("pbDst is %s\r\n"),IsBadWritePtr(pash->pbDst, pash->cbDstLength)?TEXT("bad"):TEXT("good"));
|
|
Assert( hr == S_OK );
|
|
OutputDebugString(sz);
|
|
|
|
hr = StringCchPrintf(sz,SIZEOF(sz),TEXT("ash.cbDstLengthUsed= %lx\r\n"),pash->cbDstLengthUsed);
|
|
Assert( hr == S_OK );
|
|
OutputDebugString(sz);
|
|
|
|
OutputDebugString(TEXT("} ACMSTREAMHEADER\r\n"));
|
|
|
|
if (mmr != MMSYSERR_NOERROR)
|
|
DebugBreak();
|
|
}
|
|
|
|
void DumpWFX(
|
|
LPTSTR psz,
|
|
LPWAVEFORMATEX pwfx,
|
|
LPBYTE pbSamples,
|
|
DWORD cbSamples)
|
|
{
|
|
TCHAR sz[256];
|
|
HRESULT hr;
|
|
|
|
if (psz)
|
|
{
|
|
OutputDebugString(psz);
|
|
OutputDebugString(TEXT("\r\n"));
|
|
}
|
|
OutputDebugString(TEXT("WAVEFORMATEX {\r\n"));
|
|
|
|
hr = StringCchPrintf(sz , SIZEOF(sz) , TEXT("wfx.wFormatTag = %x\r\n")
|
|
, pwfx->wFormatTag);
|
|
Assert( hr == S_OK );
|
|
OutputDebugString(sz);
|
|
|
|
hr = StringCchPrintf(sz , SIZEOF(sz) , TEXT("wfx.nChannels = %x\r\n")
|
|
, pwfx->nChannels);
|
|
Assert( hr == S_OK );
|
|
OutputDebugString(sz);
|
|
|
|
hr = StringCchPrintf(sz , SIZEOF(sz) , TEXT("wfx.nSamplesPerSec = %lx\r\n")
|
|
, pwfx->nSamplesPerSec);
|
|
Assert( hr == S_OK );
|
|
OutputDebugString(sz);
|
|
|
|
hr = StringCchPrintf(sz , SIZEOF(sz) , TEXT("wfx.nAvgBytesPerSec = %lx\r\n")
|
|
, pwfx->nAvgBytesPerSec);
|
|
Assert( hr == S_OK );
|
|
OutputDebugString(sz);
|
|
|
|
hr = StringCchPrintf(sz , SIZEOF(sz) , TEXT("wfx.nBlockAlign = %x\r\n")
|
|
, pwfx->nBlockAlign);
|
|
Assert( hr == S_OK );
|
|
OutputDebugString(sz);
|
|
|
|
hr = StringCchPrintf(sz , SIZEOF(sz) , TEXT("wfx.wBitsPerSample = %x\r\n")
|
|
, pwfx->wBitsPerSample);
|
|
Assert( hr == S_OK );
|
|
OutputDebugString(sz);
|
|
|
|
hr = StringCchPrintf(sz , SIZEOF(sz) , TEXT("wfx.cbSize = %x\r\n")
|
|
, pwfx->cbSize);
|
|
Assert( hr == S_OK );
|
|
OutputDebugString(sz);
|
|
OutputDebugString(TEXT("} WAVEFORMATEX\r\n"));
|
|
|
|
hr = StringCchPrintf(sz , SIZEOF(sz) , TEXT("cbSamples = %d, that's %d ms\r\n")
|
|
, cbSamples
|
|
, wfSamplesToTime(pwfx, wfBytesToSamples(pwfx, cbSamples)));
|
|
Assert( hr == S_OK );
|
|
OutputDebugString(sz);
|
|
if (IsBadReadPtr(pbSamples, cbSamples))
|
|
{
|
|
OutputDebugString(TEXT("Bad Data (READ)!!!!!\r\n"));
|
|
DebugBreak();
|
|
}
|
|
if (IsBadWritePtr(pbSamples, cbSamples))
|
|
{
|
|
OutputDebugString(TEXT("Bad Data (WRITE)!!!!!\r\n"));
|
|
DebugBreak();
|
|
}
|
|
|
|
}
|
|
#else
|
|
#define DumpASH(x,y)
|
|
#define DumpWFX(x,y,z,a)
|
|
#endif
|
|
|
|
MMRESULT
|
|
ConvertMultipleFormats(
|
|
PWAVEFORMATEX pwfxSrc, // pwfx specifying source format
|
|
DWORD cbSrc, // size of the source buffer
|
|
LPBYTE pbSrc, // source buffer
|
|
PWAVEFORMATEX pwfxDst, // pwfx specifying dest format
|
|
DWORD * pcbDst, // return size of the dest buffer
|
|
LPBYTE * ppbDst, // dest buffer
|
|
DWORD cBlks, // number of blocks
|
|
PPROGRESS pPrg) // progress update
|
|
{
|
|
|
|
MMRESULT mmr;
|
|
WAVEFORMATEX wfxPCM1, wfxPCM2;
|
|
LPBYTE pbPCM1, pbPCM2;
|
|
DWORD cbPCM1, cbPCM2;
|
|
|
|
if (cbSrc == 0 || pbSrc == NULL)
|
|
{
|
|
pPrg->dwTotal = 100;
|
|
pPrg->dwComplete = 100;
|
|
|
|
ProgressUpdate(pPrg, 100);
|
|
|
|
*pcbDst = 0;
|
|
*ppbDst = NULL;
|
|
|
|
return MMSYSERR_NOERROR;
|
|
}
|
|
|
|
//
|
|
// Ask ACM to suggest a PCM format to convert to.
|
|
//
|
|
wfxPCM1.wFormatTag = WAVE_FORMAT_PCM;
|
|
mmr = acmFormatSuggest(NULL, pwfxSrc, &wfxPCM1, sizeof(WAVEFORMATEX),
|
|
ACM_FORMATSUGGESTF_WFORMATTAG);
|
|
|
|
if (mmr != MMSYSERR_NOERROR)
|
|
return mmr;
|
|
|
|
//
|
|
// Ask ACM to suggest a PCM format to convert from.
|
|
//
|
|
wfxPCM2.wFormatTag = WAVE_FORMAT_PCM;
|
|
|
|
mmr = acmFormatSuggest(NULL, pwfxDst, &wfxPCM2, sizeof(WAVEFORMATEX),
|
|
ACM_FORMATSUGGESTF_WFORMATTAG);
|
|
|
|
if (mmr != MMSYSERR_NOERROR)
|
|
return mmr;
|
|
|
|
//
|
|
// if either of the above suggestions failed, we cannot complete the
|
|
// conversion.
|
|
//
|
|
// now, we have the following steps to execute:
|
|
//
|
|
// *pwfxSrc -> wfxPCM1
|
|
// wfxPCM1 -> wfxPCM2
|
|
// wfxPCM2 -> *pwfxDst
|
|
//
|
|
// if either *pwfxSrc or *pwfxDst are PCM, we only need a two or one step
|
|
// conversion.
|
|
//
|
|
|
|
if (pwfxSrc->wFormatTag == WAVE_FORMAT_PCM
|
|
|| pwfxDst->wFormatTag == WAVE_FORMAT_PCM)
|
|
{
|
|
|
|
LPWAVEFORMATEX pwfx;
|
|
DWORD * pcb;
|
|
LPBYTE * ppb;
|
|
//
|
|
// single step conversion
|
|
//
|
|
if ((pwfxSrc->wFormatTag == WAVE_FORMAT_PCM
|
|
&& pwfxDst->wFormatTag == WAVE_FORMAT_PCM)
|
|
|| (pwfxSrc->wFormatTag == WAVE_FORMAT_PCM
|
|
&& memcmp(pwfxSrc, &wfxPCM2, sizeof(PCMWAVEFORMAT)) == 0)
|
|
|| (pwfxDst->wFormatTag == WAVE_FORMAT_PCM
|
|
&& memcmp(pwfxDst, &wfxPCM1, sizeof(PCMWAVEFORMAT)) == 0))
|
|
{
|
|
pPrg->dwTotal = 100;
|
|
pPrg->dwComplete = 0;
|
|
mmr = ConvertFormat(pwfxSrc
|
|
, cbSrc
|
|
, pbSrc
|
|
, pwfxDst
|
|
, pcbDst
|
|
, ppbDst
|
|
, cBlks
|
|
, pPrg);
|
|
return mmr;
|
|
}
|
|
|
|
//
|
|
// two step conversion required
|
|
//
|
|
if (pwfxSrc->wFormatTag == WAVE_FORMAT_PCM)
|
|
{
|
|
pwfx = &wfxPCM2;
|
|
pcb = &cbPCM2;
|
|
ppb = &pbPCM2;
|
|
}
|
|
else
|
|
{
|
|
pwfx = &wfxPCM1;
|
|
pcb = &cbPCM1;
|
|
ppb = &pbPCM1;
|
|
}
|
|
pPrg->dwTotal = 50;
|
|
pPrg->dwComplete = 0;
|
|
|
|
mmr = ConvertFormat(pwfxSrc
|
|
, cbSrc
|
|
, pbSrc
|
|
, pwfx
|
|
, pcb
|
|
, ppb
|
|
, cBlks
|
|
, pPrg);
|
|
|
|
if (mmr != MMSYSERR_NOERROR)
|
|
return mmr;
|
|
|
|
pPrg->dwTotal = 50;
|
|
pPrg->dwComplete = 50;
|
|
|
|
mmr = ConvertFormat(pwfx
|
|
, *pcb
|
|
, *ppb
|
|
, pwfxDst
|
|
, pcbDst
|
|
, ppbDst
|
|
, cBlks
|
|
, pPrg);
|
|
|
|
GlobalFreePtr(*ppb);
|
|
return mmr;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// three step conversion required
|
|
//
|
|
pPrg->dwTotal = 33;
|
|
pPrg->dwComplete = 1;
|
|
|
|
//
|
|
// Convert from Src to PCM1
|
|
//
|
|
mmr = ConvertFormat(pwfxSrc
|
|
, cbSrc
|
|
, pbSrc
|
|
, &wfxPCM1
|
|
, &cbPCM1
|
|
, &pbPCM1
|
|
, cBlks
|
|
, pPrg);
|
|
if (mmr != MMSYSERR_NOERROR)
|
|
return mmr;
|
|
|
|
pPrg->dwTotal = 33;
|
|
pPrg->dwComplete = 34;
|
|
|
|
//
|
|
// Convert from PCM1 to PCM2
|
|
//
|
|
mmr = ConvertFormat (&wfxPCM1
|
|
, cbPCM1
|
|
, pbPCM1
|
|
, &wfxPCM2
|
|
, &cbPCM2
|
|
, &pbPCM2
|
|
, cBlks
|
|
, pPrg);
|
|
|
|
GlobalFreePtr(pbPCM1);
|
|
|
|
if (mmr != MMSYSERR_NOERROR)
|
|
return mmr;
|
|
|
|
pPrg->dwTotal = 33;
|
|
pPrg->dwComplete = 67;
|
|
|
|
//
|
|
// Convert from PCM2 to DST
|
|
//
|
|
mmr = ConvertFormat (&wfxPCM2
|
|
, cbPCM2
|
|
, pbPCM2
|
|
, pwfxDst
|
|
, pcbDst
|
|
, ppbDst
|
|
, cBlks
|
|
, pPrg);
|
|
|
|
GlobalFreePtr(pbPCM2);
|
|
}
|
|
return mmr;
|
|
}
|
|
|
|
//
|
|
// add spilage to/from file i/o
|
|
//
|
|
|
|
/* Generic single step conversion.
|
|
*/
|
|
MMRESULT
|
|
ConvertFormat(
|
|
PWAVEFORMATEX pwfxSrc, // pwfx specifying source format
|
|
DWORD cbSrc, // size of the source buffer
|
|
LPBYTE pbSrc, // source buffer
|
|
PWAVEFORMATEX pwfxDst, // pwfx specifying dest format
|
|
DWORD * pcbDst, // return size of the dest buffer
|
|
LPBYTE * ppbDst, // dest buffer
|
|
DWORD cBlks, // number of blocks
|
|
PPROGRESS pPrg) // progress update
|
|
{
|
|
HACMSTREAM hasStream = NULL;
|
|
MMRESULT mmr = MMSYSERR_NOERROR;
|
|
|
|
//
|
|
// temporary copy buffers
|
|
//
|
|
DWORD cbSrcBuf = 0L;
|
|
LPBYTE pbSrcBuf = NULL;
|
|
|
|
DWORD cbDstBuf = 0L;
|
|
LPBYTE pbDstBuf = NULL;
|
|
|
|
//
|
|
// full destination buffers
|
|
//
|
|
DWORD cbDst = 0L;
|
|
LPBYTE pbDst = NULL;
|
|
|
|
DWORD cbSrcUsed = 0L;
|
|
DWORD cbDstUsed = 0L;
|
|
|
|
DWORD cbCopySrc = 0L;
|
|
DWORD cbCopyDst = 0L;
|
|
|
|
DWORD cbRem;
|
|
|
|
ACMSTREAMHEADER ash;
|
|
WORD nBlockAlign;
|
|
|
|
gfBreakOfDeath = FALSE;
|
|
|
|
DumpWFX(TEXT("ConvertFormat Input"), pwfxSrc, pbSrc, cbSrc);
|
|
|
|
if (cbSrc == 0 || pbSrc == NULL)
|
|
{
|
|
pPrg->dwTotal = 100;
|
|
pPrg->dwComplete = 100;
|
|
|
|
ProgressUpdate(pPrg, 100);
|
|
|
|
*pcbDst = 0;
|
|
*ppbDst = NULL;
|
|
|
|
return MMSYSERR_NOERROR;
|
|
}
|
|
|
|
//
|
|
// synchronous conversion
|
|
//
|
|
mmr = acmStreamOpen(&hasStream
|
|
, NULL
|
|
, pwfxSrc
|
|
, pwfxDst
|
|
, NULL // no filter. maybe later
|
|
, 0L
|
|
, 0L
|
|
, ACM_STREAMOPENF_NONREALTIME );
|
|
|
|
if (MMSYSERR_NOERROR != mmr)
|
|
{
|
|
return mmr;
|
|
}
|
|
|
|
//
|
|
// How big of a destination buffer do we need?
|
|
//
|
|
// WARNING: acmStreamSize only gives us an estimation. if, in the event
|
|
// it *underestimates* the destination buffer size we currently ignore
|
|
// it, causing a clipping of the end buffer
|
|
//
|
|
mmr = acmStreamSize(hasStream
|
|
, cbSrc
|
|
, &cbDst
|
|
, ACM_STREAMSIZEF_SOURCE);
|
|
|
|
if (MMSYSERR_NOERROR != mmr)
|
|
{
|
|
goto ExitCloseStream;
|
|
}
|
|
|
|
//
|
|
// allocate the destination buffer
|
|
//
|
|
pbDst = (LPBYTE)GlobalAllocPtr(GHND | GMEM_SHARE,cbDst);
|
|
|
|
if (pbDst == NULL)
|
|
{
|
|
mmr = MMSYSERR_NOMEM;
|
|
goto ExitCloseStream;
|
|
}
|
|
|
|
*ppbDst = pbDst;
|
|
*pcbDst = cbDst;
|
|
|
|
//
|
|
// chop up the data into 10 bitesize pieces
|
|
//
|
|
nBlockAlign = pwfxSrc->nBlockAlign;
|
|
|
|
cbSrcBuf = cbSrc / 10;
|
|
cbSrcBuf = cbSrcBuf - (cbSrcBuf % nBlockAlign);
|
|
cbSrcBuf = ( 0L == cbSrcBuf ) ? nBlockAlign : cbSrcBuf;
|
|
|
|
mmr = acmStreamSize(hasStream
|
|
, cbSrcBuf
|
|
, &cbDstBuf
|
|
, ACM_STREAMSIZEF_SOURCE);
|
|
|
|
if (MMSYSERR_NOERROR != mmr)
|
|
goto ExitFreeDestData;
|
|
|
|
//
|
|
// allocate source copy buffer
|
|
//
|
|
pbSrcBuf = (LPBYTE)GlobalAllocPtr(GHND | GMEM_SHARE,cbSrcBuf);
|
|
if (pbSrcBuf == NULL)
|
|
{
|
|
mmr = MMSYSERR_NOMEM;
|
|
goto ExitFreeDestData;
|
|
}
|
|
|
|
//
|
|
// allocate destination copy buffer
|
|
//
|
|
pbDstBuf = (LPBYTE)GlobalAllocPtr(GHND | GMEM_SHARE,cbDstBuf);
|
|
if (pbDstBuf == NULL)
|
|
{
|
|
mmr = MMSYSERR_NOMEM;
|
|
GlobalFreePtr(pbSrcBuf);
|
|
pbSrcBuf = NULL;
|
|
goto ExitFreeDestData;
|
|
}
|
|
|
|
//
|
|
// initialize the ash
|
|
//
|
|
ash.cbStruct = sizeof(ash);
|
|
ash.fdwStatus = 0L;
|
|
ash.pbSrc = pbSrcBuf;
|
|
ash.cbSrcLength = cbSrcBuf;
|
|
ash.cbSrcLengthUsed = 0L;
|
|
ash.pbDst = pbDstBuf;
|
|
ash.cbDstLength = cbDstBuf;
|
|
ash.cbDstLengthUsed = 0L;
|
|
|
|
//
|
|
// we will only need to prepare once, since the buffers are
|
|
// never moved.
|
|
//
|
|
mmr = acmStreamPrepareHeader(hasStream, &ash, 0L);
|
|
if (MMSYSERR_NOERROR != mmr)
|
|
goto ExitFreeTempBuffers;
|
|
|
|
//
|
|
// main blockalign conversion loop
|
|
//
|
|
while (cbSrcUsed < cbSrc)
|
|
{
|
|
cbCopySrc = min(cbSrcBuf, cbSrc - cbSrcUsed);
|
|
if (cbCopySrc > 0L)
|
|
memmove(pbSrcBuf, pbSrc, cbCopySrc);
|
|
|
|
#ifdef ACMBUG
|
|
//
|
|
// ACM has a bug wherein the destination buffer is validated for too
|
|
// much. If we exactly calculate the cbCopyDst here, ACM is sure to
|
|
// puke on the conversion before the last.
|
|
//
|
|
cbCopyDst = min(cbDstBuf, cbDst - cbDstUsed);
|
|
#else
|
|
cbCopyDst = cbDstBuf;
|
|
#endif
|
|
if (cbCopyDst == 0L || cbCopyDst == 0L)
|
|
break;
|
|
|
|
ash.cbSrcLength = cbCopySrc;
|
|
ash.cbSrcLengthUsed = 0L;
|
|
ash.cbDstLength = cbCopyDst;
|
|
ash.cbDstLengthUsed = 0L;
|
|
|
|
mmr = acmStreamConvert(hasStream
|
|
, &ash
|
|
, ACM_STREAMCONVERTF_BLOCKALIGN );
|
|
|
|
if (MMSYSERR_NOERROR != mmr)
|
|
{
|
|
DumpASH(mmr, &ash);
|
|
goto ExitUnprepareHeader;
|
|
}
|
|
|
|
//
|
|
// Update the user and test for cancel
|
|
//
|
|
if (!ProgressUpdate(pPrg, (cbSrcUsed * 100)/cbSrc))
|
|
{
|
|
mmr = MMSYSERR_ERROR;
|
|
goto ExitUnprepareHeader;
|
|
}
|
|
|
|
while (0 == (ACMSTREAMHEADER_STATUSF_DONE & ash.fdwStatus))
|
|
{
|
|
//
|
|
// I don't trust an infinite loop.
|
|
//
|
|
if (gfBreakOfDeath)
|
|
{
|
|
mmr = MMSYSERR_HANDLEBUSY; // Bad bad bad condition
|
|
goto ExitUnprepareHeader;
|
|
}
|
|
}
|
|
|
|
//
|
|
// always increment. we will have to carry over whatever the
|
|
// last conversion gives us back since this determined by.
|
|
//
|
|
cbSrcUsed += ash.cbSrcLengthUsed;
|
|
pbSrc += ash.cbSrcLengthUsed;
|
|
|
|
//
|
|
// loop terminating condition. if the conversion yields no
|
|
// destination data without error, we can only flush end data.
|
|
//
|
|
if (0L == ash.cbDstLengthUsed || cbDstUsed >= cbDst)
|
|
break;
|
|
|
|
#ifdef ACMBUG
|
|
memmove(pbDst, pbDstBuf, ash.cbDstLengthUsed );
|
|
cbDstUsed += ash.cbDstLengthUsed;
|
|
pbDst += ash.cbDstLengthUsed;
|
|
#else
|
|
cbRem = min(ash.cbDstLengthUsed, cbDst - cbDstUsed);
|
|
memmove(pbDst, pbDstBuf, cbRem);
|
|
|
|
cbDstUsed += cbRem;
|
|
pbDst += cbRem;
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// Flush remaining block-aligned end data to the destination stream.
|
|
// Example: A few bytes of source data were left unconverted because
|
|
// for some reason, the last
|
|
//
|
|
|
|
for (;cbDst - cbDstUsed > 0; )
|
|
{
|
|
cbCopySrc = min(cbSrcBuf, cbSrc - cbSrcUsed);
|
|
if (cbCopySrc > 0L)
|
|
memmove(pbSrcBuf, pbSrc, cbCopySrc);
|
|
|
|
#ifdef ACMBUG
|
|
cbCopyDst = min(cbDstBuf, cbDst - cbDstUsed);
|
|
#else
|
|
cbCopyDst = cbDstBuf;
|
|
#endif
|
|
|
|
ash.cbSrcLength = cbCopySrc;
|
|
ash.cbSrcLengthUsed = 0L;
|
|
ash.cbDstLength = cbCopyDst;
|
|
ash.cbDstLengthUsed = 0L;
|
|
|
|
mmr = acmStreamConvert(hasStream
|
|
, &ash
|
|
, ACM_STREAMCONVERTF_BLOCKALIGN |
|
|
ACM_STREAMCONVERTF_END );
|
|
|
|
if (MMSYSERR_NOERROR != mmr)
|
|
{
|
|
DumpASH(mmr, &ash);
|
|
goto ExitUnprepareHeader;
|
|
}
|
|
|
|
//
|
|
// Update the user and test for cancel
|
|
//
|
|
if (!ProgressUpdate(pPrg, (cbSrcUsed * 100)/cbSrc))
|
|
{
|
|
mmr = MMSYSERR_ERROR;
|
|
goto ExitUnprepareHeader;
|
|
}
|
|
|
|
while (0 == (ACMSTREAMHEADER_STATUSF_DONE & ash.fdwStatus))
|
|
{
|
|
//
|
|
// I don't trust an infinite loop.
|
|
//
|
|
if (gfBreakOfDeath)
|
|
{
|
|
mmr = MMSYSERR_HANDLEBUSY; // Bad bad bad condition
|
|
goto ExitUnprepareHeader;
|
|
}
|
|
}
|
|
cbSrcUsed += ash.cbSrcLengthUsed;
|
|
pbSrc += ash.cbSrcLengthUsed;
|
|
|
|
if (0L != ash.cbDstLengthUsed && cbDstUsed < cbDst)
|
|
{
|
|
#ifdef ACMBUG
|
|
memmove(pbDst, pbDstBuf, ash.cbDstLengthUsed);
|
|
|
|
cbDstUsed += ash.cbDstLengthUsed;
|
|
pbDst += ash.cbDstLengthUsed;
|
|
#else
|
|
cbRem = min(ash.cbDstLengthUsed, cbDst - cbDstUsed);
|
|
memmove(pbDst, pbDstBuf, cbRem);
|
|
cbDstUsed += cbRem;
|
|
pbDst += cbRem;
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// Last pass non-blockaligned end data
|
|
//
|
|
cbCopySrc = min(cbSrcBuf, cbSrc - cbSrcUsed);
|
|
if (cbCopySrc > 0L)
|
|
memmove(pbSrcBuf, pbSrc, cbCopySrc);
|
|
|
|
#ifdef ACMBUG
|
|
cbCopyDst = min(cbDstBuf, cbDst - cbDstUsed);
|
|
if (0L == cbCopyDst)
|
|
break;
|
|
#else
|
|
cbCopyDst = cbDstBuf;
|
|
#endif
|
|
|
|
ash.cbSrcLength = cbCopySrc;
|
|
ash.cbSrcLengthUsed = 0L;
|
|
ash.cbDstLength = cbCopyDst;
|
|
ash.cbDstLengthUsed = 0L;
|
|
|
|
mmr = acmStreamConvert(hasStream
|
|
, &ash
|
|
, ACM_STREAMCONVERTF_END );
|
|
|
|
if (MMSYSERR_NOERROR != mmr)
|
|
{
|
|
DumpASH(mmr, &ash);
|
|
goto ExitUnprepareHeader;
|
|
}
|
|
|
|
//
|
|
// Update the user and test for cancel
|
|
//
|
|
if (!ProgressUpdate(pPrg, (cbSrcUsed * 100)/cbSrc))
|
|
{
|
|
mmr = MMSYSERR_ERROR;
|
|
goto ExitUnprepareHeader;
|
|
}
|
|
|
|
while (0 == (ACMSTREAMHEADER_STATUSF_DONE & ash.fdwStatus))
|
|
{
|
|
//
|
|
// I don't trust an infinite loop.
|
|
//
|
|
if (gfBreakOfDeath)
|
|
{
|
|
mmr = MMSYSERR_HANDLEBUSY; // Bad bad bad condition
|
|
goto ExitUnprepareHeader;
|
|
}
|
|
}
|
|
|
|
cbSrcUsed += ash.cbSrcLengthUsed;
|
|
pbSrc += ash.cbSrcLengthUsed;
|
|
|
|
if (0L != ash.cbDstLengthUsed && cbDstUsed < cbDst)
|
|
{
|
|
#ifdef ACMBUG
|
|
memmove(pbDst, pbDstBuf, ash.cbDstLengthUsed);
|
|
|
|
cbDstUsed += ash.cbDstLengthUsed;
|
|
pbDst += ash.cbDstLengthUsed;
|
|
#else
|
|
cbRem = min(ash.cbDstLengthUsed, cbDst - cbDstUsed);
|
|
memmove(pbDst, pbDstBuf, cbRem);
|
|
cbDstUsed += cbRem;
|
|
pbDst += cbRem;
|
|
#endif
|
|
}
|
|
else // nothing's going to work
|
|
break;
|
|
}
|
|
|
|
*pcbDst = cbDstUsed;
|
|
DumpWFX(TEXT("ConvertFormat Output"), pwfxDst, *ppbDst, cbDstUsed);
|
|
|
|
ExitUnprepareHeader:
|
|
acmStreamUnprepareHeader(hasStream, &ash, 0L);
|
|
|
|
ExitFreeTempBuffers:
|
|
GlobalFreePtr(pbDstBuf);
|
|
GlobalFreePtr(pbSrcBuf);
|
|
|
|
if (MMSYSERR_NOERROR == mmr)
|
|
goto ExitCloseStream;
|
|
|
|
ExitFreeDestData:
|
|
GlobalFreePtr(*ppbDst);
|
|
*ppbDst = NULL;
|
|
*pcbDst = 0L;
|
|
|
|
ExitCloseStream:
|
|
acmStreamClose(hasStream,0L);
|
|
|
|
return mmr;
|
|
}
|
|
|
|
/* - - - - - - - - - */
|
|
void Properties_InitDocVars(
|
|
HWND hwnd,
|
|
PWAVEDOC pwd)
|
|
{
|
|
if (pwd->pwfx)
|
|
{
|
|
LPTSTR lpstr;
|
|
TCHAR sz[128];
|
|
TCHAR szFmt[128];
|
|
LONG lTime;
|
|
HINSTANCE hinst;
|
|
HRESULT hr;
|
|
|
|
lpstr = SoundRec_GetFormatName(pwd->pwfx);
|
|
if (lpstr)
|
|
{
|
|
SetDlgItemText(hwnd, IDC_AUDIOFORMAT, lpstr);
|
|
GlobalFreePtr(lpstr);
|
|
}
|
|
lTime = wfSamplesToTime(pwd->pwfx,wfBytesToSamples(pwd->pwfx,pwd->cbdata));
|
|
if (gfLZero || ((int)(lTime/1000) != 0))
|
|
hr = StringCchPrintf(sz, SIZEOF(sz), aszPositionFormat, (int)(lTime/1000), chDecimal,
|
|
(int)((lTime/10)%100));
|
|
else
|
|
hr = StringCchPrintf(sz, SIZEOF(sz), aszNoZeroPositionFormat, chDecimal,
|
|
(int)((lTime/10)%100));
|
|
Assert( hr == S_OK );
|
|
SetDlgItemText(hwnd, IDC_FILELEN, sz);
|
|
|
|
hinst = GetWindowInstance(hwnd);
|
|
if (hinst && LoadString(hinst, IDS_DATASIZE, szFmt, SIZEOF(szFmt)))
|
|
{
|
|
hr = StringCchPrintf(sz, SIZEOF(sz), szFmt, pwd->cbdata);
|
|
Assert( hr == S_OK );
|
|
SetDlgItemText(hwnd, IDC_DATASIZE, sz);
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* */
|
|
BOOL Properties_OnInitDialog(
|
|
HWND hwnd,
|
|
HWND hwndFocus,
|
|
LPARAM lParam)
|
|
{
|
|
HINSTANCE hinst;
|
|
HWND hChoice;
|
|
TCHAR sz[256];
|
|
int i;
|
|
|
|
//
|
|
// commence initialization
|
|
//
|
|
PWAVEDOC pwd = (PWAVEDOC)((LPPROPSHEETPAGE)lParam)->lParam;
|
|
if (pwd == NULL)
|
|
{
|
|
EndDialog(hwnd, FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
SetProp(hwnd, TEXT("DATA"), (HANDLE)pwd);
|
|
hinst = GetWindowInstance(hwnd);
|
|
|
|
//
|
|
// Set "static" property information
|
|
//
|
|
if (pwd->pszFileName)
|
|
SetDlgItemText(hwnd, IDC_FILENAME, pwd->pszFileName);
|
|
if (pwd->pszCopyright)
|
|
SetDlgItemText(hwnd, IDC_COPYRIGHT, pwd->pszCopyright);
|
|
else if (LoadString(hinst, IDS_NOCOPYRIGHT, sz, SIZEOF(sz)))
|
|
{
|
|
SetDlgItemText(hwnd, IDC_COPYRIGHT, sz);
|
|
}
|
|
if (pwd->hIcon)
|
|
Static_SetIcon(GetDlgItem(hwnd, IDC_DISPICON), pwd->hIcon);
|
|
|
|
//
|
|
// Set "volatile" property information
|
|
//
|
|
Properties_InitDocVars(hwnd, pwd);
|
|
|
|
//
|
|
// Initialize the enumeration choice combobox
|
|
//
|
|
hChoice = GetDlgItem(hwnd, IDC_CONVERTCHOOSEFROM);
|
|
if (waveOutGetNumDevs())
|
|
{
|
|
if (LoadString(hinst, IDS_SHOWPLAYABLE, sz, SIZEOF(sz)))
|
|
{
|
|
i = ComboBox_AddString(hChoice, sz);
|
|
ComboBox_SetItemData(hChoice, i, ACM_FORMATENUMF_OUTPUT);
|
|
}
|
|
}
|
|
if (waveInGetNumDevs())
|
|
{
|
|
if (LoadString(hinst, IDS_SHOWRECORDABLE, sz, SIZEOF(sz)))
|
|
{
|
|
i = ComboBox_AddString(hChoice, sz);
|
|
ComboBox_SetItemData(hChoice, i, ACM_FORMATENUMF_INPUT);
|
|
}
|
|
}
|
|
if (LoadString(hinst, IDS_SHOWALL, sz, SIZEOF(sz)))
|
|
{
|
|
i = ComboBox_AddString(hChoice, sz);
|
|
ComboBox_SetItemData(hChoice, i, 0L);
|
|
}
|
|
ComboBox_SetCurSel(hChoice, i);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* */
|
|
void Properties_OnDestroy(
|
|
HWND hwnd)
|
|
{
|
|
//
|
|
// commence cleanup
|
|
//
|
|
PWAVEDOC pwd = (PWAVEDOC)GetProp(hwnd, TEXT("DATA"));
|
|
if (pwd)
|
|
{
|
|
RemoveProp(hwnd, TEXT("DATA"));
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* */
|
|
BOOL PASCAL Properties_OnCommand(
|
|
HWND hwnd,
|
|
int id,
|
|
HWND hwndCtl,
|
|
UINT codeNotify)
|
|
{
|
|
switch (id)
|
|
{
|
|
case ID_APPLY:
|
|
return TRUE;
|
|
|
|
case IDOK:
|
|
break;
|
|
|
|
case IDCANCEL:
|
|
break;
|
|
|
|
case ID_INIT:
|
|
break;
|
|
|
|
case IDC_CONVERTTO:
|
|
{
|
|
PWAVEDOC pwd = (PWAVEDOC)GetProp(hwnd, TEXT("DATA"));
|
|
PWAVEFORMATEX pwfxNew = NULL;
|
|
DWORD fdwEnum = 0L;
|
|
int i;
|
|
HWND hChoice;
|
|
|
|
hChoice = GetDlgItem(hwnd, IDC_CONVERTCHOOSEFROM);
|
|
i = ComboBox_GetCurSel(hChoice);
|
|
fdwEnum = (DWORD)ComboBox_GetItemData(hChoice, i);
|
|
|
|
if (ChooseDestinationFormat(ghInst
|
|
,hwnd
|
|
,NULL
|
|
,&pwfxNew
|
|
,fdwEnum) == MMSYSERR_NOERROR)
|
|
{
|
|
DWORD cbNew;
|
|
LPBYTE pbNew;
|
|
DWORD cbSize = (pwfxNew->wFormatTag == WAVE_FORMAT_PCM)?
|
|
sizeof(PCMWAVEFORMAT):
|
|
sizeof(WAVEFORMATEX)+pwfxNew->cbSize;
|
|
|
|
if (memcmp(pwfxNew, pwd->pwfx, cbSize) == 0)
|
|
break;
|
|
StopWave();
|
|
BeginWaveEdit();
|
|
if (ConvertFormatDialog(hwnd
|
|
, pwd->pwfx
|
|
, pwd->cbdata
|
|
, pwd->pbdata
|
|
, pwfxNew
|
|
, &cbNew
|
|
, &pbNew
|
|
, 0
|
|
, NULL) == MMSYSERR_NOERROR)
|
|
{
|
|
GlobalFreePtr(pwd->pwfx);
|
|
if (pwd->pbdata)
|
|
GlobalFreePtr(pwd->pbdata);
|
|
|
|
pwd->pwfx = pwfxNew;
|
|
pwd->cbdata = cbNew;
|
|
pwd->pbdata = pbNew;
|
|
pwd->fChanged = TRUE;
|
|
|
|
if (pwd->lpv)
|
|
{
|
|
PSGLOBALS psg = (PSGLOBALS)pwd->lpv;
|
|
*psg->ppwfx = pwfxNew;
|
|
*psg->pcbwfx = sizeof(WAVEFORMATEX);
|
|
if (pwfxNew->wFormatTag != WAVE_FORMAT_PCM)
|
|
*psg->pcbwfx += pwfxNew->cbSize;
|
|
|
|
*psg->pcbdata = cbNew;
|
|
*psg->ppbdata = pwd->pbdata;
|
|
*psg->plSamples = wfBytesToSamples(pwfxNew, cbNew);
|
|
*psg->plSamplesValid = *psg->plSamples;
|
|
*psg->plWavePosition = 0;
|
|
UpdateDisplay(TRUE);
|
|
Properties_InitDocVars(hwnd, pwd);
|
|
}
|
|
EndWaveEdit(TRUE);
|
|
}
|
|
else
|
|
EndWaveEdit(FALSE);
|
|
|
|
}
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
* Properties_Proc
|
|
*/
|
|
INT_PTR CALLBACK
|
|
Properties_Proc(
|
|
HWND hdlg,
|
|
UINT umsg,
|
|
WPARAM wparam,
|
|
LPARAM lparam)
|
|
{
|
|
#ifdef CHICAGO
|
|
static const DWORD aHelpIds[] = {
|
|
IDC_DISPICON, IDH_SOUNDREC_ICON,
|
|
IDC_FILENAME, IDH_SOUNDREC_SNDTITLE,
|
|
IDC_TXT_COPYRIGHT, IDH_SOUNDREC_COPYRIGHT,
|
|
IDC_COPYRIGHT, IDH_SOUNDREC_COPYRIGHT,
|
|
IDC_TXT_FILELEN, IDH_SOUNDREC_LENGTH,
|
|
IDC_FILELEN, IDH_SOUNDREC_LENGTH,
|
|
IDC_TXT_DATASIZE, IDH_SOUNDREC_SIZE,
|
|
IDC_DATASIZE, IDH_SOUNDREC_SIZE,
|
|
IDC_TXT_AUDIOFORMAT, IDH_SOUNDREC_AUDIO,
|
|
IDC_AUDIOFORMAT, IDH_SOUNDREC_AUDIO,
|
|
IDC_CONVGROUP, IDH_SOUNDREC_COMM_GROUPBOX,
|
|
IDC_CONVERTCHOOSEFROM, IDH_SOUNDREC_CONVERT,
|
|
IDC_CONVERTTO, IDH_SOUNDREC_FORMAT,
|
|
0, 0
|
|
};
|
|
#endif
|
|
|
|
switch (umsg)
|
|
{
|
|
|
|
#ifdef CHICAGO
|
|
case WM_CONTEXTMENU:
|
|
WinHelp((HWND)wparam, gachHelpFile, HELP_CONTEXTMENU,
|
|
(UINT_PTR)(LPSTR)aHelpIds);
|
|
return TRUE;
|
|
|
|
case WM_HELP:
|
|
{
|
|
LPHELPINFO lphi = (LPVOID) lparam;
|
|
WinHelp (lphi->hItemHandle, gachHelpFile, HELP_WM_HELP,
|
|
(UINT_PTR) (LPSTR) aHelpIds);
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
case WM_INITDIALOG:
|
|
return HANDLE_WM_INITDIALOG(hdlg, wparam, lparam, Properties_OnInitDialog);
|
|
|
|
case WM_DESTROY:
|
|
HANDLE_WM_DESTROY(hdlg, wparam, lparam, Properties_OnDestroy);
|
|
break;
|
|
|
|
case WM_NOTIFY:
|
|
{
|
|
NMHDR *lpnm = (NMHDR FAR *)lparam;
|
|
switch(lpnm->code)
|
|
{
|
|
case PSN_KILLACTIVE:
|
|
FORWARD_WM_COMMAND(hdlg, IDOK, 0, 0, SendMessage);
|
|
break;
|
|
|
|
case PSN_APPLY:
|
|
FORWARD_WM_COMMAND(hdlg, ID_APPLY, 0, 0, SendMessage);
|
|
break;
|
|
|
|
case PSN_SETACTIVE:
|
|
FORWARD_WM_COMMAND(hdlg, ID_INIT, 0, 0, SendMessage);
|
|
break;
|
|
|
|
case PSN_RESET:
|
|
FORWARD_WM_COMMAND(hdlg, IDCANCEL, 0, 0, SendMessage);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case WM_COMMAND:
|
|
return HANDLE_WM_COMMAND(hdlg, wparam, lparam, Properties_OnCommand);
|
|
default:
|
|
{
|
|
#ifdef CHICAGO
|
|
extern DWORD aChooserHelpIds[];
|
|
extern UINT guChooserContextMenu;
|
|
extern UINT guChooserContextHelp;
|
|
//
|
|
// Handle context-sensitive help messages from acm dialog
|
|
//
|
|
if( umsg == guChooserContextMenu )
|
|
{
|
|
WinHelp( (HWND)wparam, NULL, HELP_CONTEXTMENU,
|
|
(UINT_PTR)(LPSTR)aChooserHelpIds );
|
|
}
|
|
else if( umsg == guChooserContextHelp )
|
|
{
|
|
WinHelp( ((LPHELPINFO)lparam)->hItemHandle, NULL,
|
|
HELP_WM_HELP, (UINT_PTR)(LPSTR)aChooserHelpIds );
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Wave document property sheet.
|
|
* */
|
|
BOOL
|
|
SoundRec_Properties(
|
|
HWND hwnd,
|
|
HINSTANCE hinst,
|
|
PWAVEDOC pwd)
|
|
{
|
|
PROPSHEETPAGE psp;
|
|
PROPSHEETHEADER psh;
|
|
TCHAR szCaption[256], szCapFmt[64];
|
|
HRESULT hr;
|
|
|
|
psp.dwSize = sizeof(PROPSHEETPAGE);
|
|
psp.dwFlags = PSP_DEFAULT;// | PSP_USETITLE;
|
|
psp.hInstance = hinst;
|
|
psp.pszTemplate = MAKEINTRESOURCE(IDD_PROPERTIES);
|
|
psp.pszIcon = NULL;
|
|
psp.pszTitle = NULL;
|
|
psp.pfnDlgProc = Properties_Proc;
|
|
psp.lParam = (LPARAM)(LPVOID)pwd;
|
|
psp.pfnCallback = NULL;
|
|
psp.pcRefParent = NULL;
|
|
|
|
psh.dwSize = sizeof(PROPSHEETHEADER);
|
|
psh.dwFlags = PSH_NOAPPLYNOW|PSH_PROPSHEETPAGE ;
|
|
psh.hwndParent = hwnd;
|
|
psh.hInstance = hinst;
|
|
psh.pszIcon = NULL;
|
|
|
|
if (LoadString(hinst, IDS_PROPERTIES, szCapFmt, SIZEOF(szCapFmt)))
|
|
{
|
|
hr = StringCchPrintf(szCaption, SIZEOF(szCaption), szCapFmt, pwd->pszFileName);
|
|
psh.pszCaption = szCaption;
|
|
}
|
|
else
|
|
psh.pszCaption = NULL;
|
|
|
|
psh.nPages = 1;
|
|
psh.nStartPage = 0;
|
|
psh.ppsp = &psp;
|
|
psh.pfnCallback = NULL;
|
|
|
|
PropertySheet(&psh);
|
|
|
|
return FALSE; // nothing changed?
|
|
}
|