AAFILE.C

//==========================================================================; 
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright 1992 - 1998 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
//
// aafile.c
//
// Description:
// Contains routines to deal with the FILE menu - loading and saving
// files, and also contains some support routines for dealing with
// RIFF files.
//
//
//==========================================================================;

#include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>
#include <memory.h>

#include <mmreg.h>
#include <msacm.h>

#include "appport.h"
#include "waveio.h"
#include "acmapp.h"

#include "debug.h"



//==========================================================================;
//
//
//
//
//==========================================================================;

//--------------------------------------------------------------------------;
//
// DWORD DosGetFileAttributes
//
// Description:
// INT 21 - DOS 2+ - GET FILE ATTRIBUTES
// AX = 4300h
// DS:DX -> ASCIZ file name or directory name without trailing slash
//
// Return: CF set on error
// AX = error code (01h,02h,03h,05h) (see AH=59h)
// CF clear if successful
// CX = file attributes (see AX=4301h)
//
// SeeAlso: AX=4301h, INT 2F/AX=110Fh
//
// --------
// INT 21 - DOS 2+ - PUT FILE ATTRIBUTES (CHMOD)
// AX = 4301h
// CX = file attribute bits
// bit 0 = read only
// 1 = hidden file
// 2 = system file
// 3 = volume label
// 4 = subdirectory
// 5 = written since backup ("archive" bit)
// 8 = shareable (Novell NetWare)
// DS:DX -> ASCIZ file name
//
// Return: CF set on error
// AX = error code (01h,02h,03h,05h) (see AH=59h)
// CF clear if successful
//
// Note: will not change volume label or directory attributes
//
// SeeAlso: AX=4300h, INT 2F/AX=110Eh
//
//
// Arguments:
// LPTSTR pszFilePath:
//
// Return (DWORD):
//
//
//--------------------------------------------------------------------------;
#ifndef WIN32
#pragma optimize("", off)
DWORD FNGLOBAL DosGetFileAttributes
(
LPTSTR pszFilePath
)
{
WORD fwDosAttributes;

_asm
{
push ds
mov ax, 4300h
lds dx, pszFilePath
int 21h
jnc Get_File_Attributes_Continue

xor cx, cx

Get_File_Attributes_Continue:

mov fwDosAttributes, cx
pop ds
}


return ((DWORD)fwDosAttributes);
} // DosGetFileAttributes()
#pragma optimize("", on)
#endif


//--------------------------------------------------------------------------;
//
// DWORD DosGetDateTime
//
// Description:
//
// Arguments:
// HFILE hf:
//
// Return (DWORD):
//
//
//--------------------------------------------------------------------------;
#ifndef WIN32
#pragma optimize("", off)
DWORD FNGLOBAL DosGetDateTime
(
HFILE hf
)
{
WORD wDosDate;
WORD wDosTime;

_asm
{
mov ax, 5700h
mov bx, hf
int 21h
jnc Get_Date_Time_Continue

xor cx, cx
xor dx, dx

Get_Date_Time_Continue:

mov wDosDate, dx
mov wDosTime, cx
}


return ((DWORD)MAKELONG(wDosDate, wDosTime));
} // DosGetDateTime()
#pragma optimize("", on)
#endif


//==========================================================================;
//
//
//
//
//==========================================================================;

//--------------------------------------------------------------------------;
//
// BOOL AcmAppFileSaveModified
//
// Description:
// This function tests if the current file has been modified, and
// if it has it gives the option of saving the file.
//
// NOTE! This function does *NOT* clear the modified bit for the
// file. The calling function must do this if necessary.
//
// Arguments:
// HWND hwnd: Handle to main window.
//
// PACMAPPFILEDESC paafd: Pointer to file descriptor.
//
// Return (BOOL):
// Returns TRUE if the calling function should continue--the file was
// either saved or the user does not wish to save it. Returns FALSE
// if the calling function should cancel its operation--the user
// wants to keep the data, but it has not been saved.
//
//--------------------------------------------------------------------------;

BOOL FNGLOBAL AcmAppFileSaveModified
(
HWND hwnd,
PACMAPPFILEDESC paafd
)
{
BOOL f;
int n;

//
// check if the contents of the file have been modified--if they have
// then ask the user if they want to save the current contents...
//
f = (0 != (ACMAPPFILEDESC_STATEF_MODIFIED & paafd->fdwState));
if (f)
{
//
// display an appropriate message box asking for the user's opinion
//
n = AppMsgBox(hwnd, MB_YESNOCANCEL | MB_ICONQUESTION| MB_SETFOREGROUND,
TEXT("The file '%s' has been modified. Do you want to save these changes?"),
(LPSTR)paafd->szFilePath);
switch (n)
{
case IDYES:
f = AppFileSave(hwnd, paafd, FALSE);
if (f)
break;

// -- fall through --

case IDCANCEL:
//
// don't continue!
//
return (FALSE);

case IDNO:
break;
}
}

//
// ok to continue...
//
return (TRUE);
} // AcmAppFileSaveModified()


//--------------------------------------------------------------------------;
//
// BOOL AcmAppFileNew
//
// Description:
//
// Arguments:
// HWND hwnd: Handle to main window.
//
// PACMAPPFILEDESC paafd: Pointer to file descriptor.
//
// Return (BOOL):
//
//--------------------------------------------------------------------------;

BOOL FNGLOBAL AcmAppFileNew
(
HWND hwnd,
PACMAPPFILEDESC paafd
)
{
TCHAR szFilePath[APP_MAX_FILE_PATH_CHARS];
TCHAR szFileTitle[APP_MAX_FILE_TITLE_CHARS];
MMRESULT mmr;
LPWAVEFORMATEX pwfx;
DWORD cbwfx;
BOOL f;
ACMFORMATCHOOSE afc;
HMMIO hmmio;
MMCKINFO ckRIFF;
MMCKINFO ck;
DWORD cSamples;



if (!gfAcmAvailable)
{
AppMsgBox(hwnd, MB_OK | MB_ICONEXCLAMATION,
TEXT("AcmAppFileNew() called when ACM is not installed!"));
return (FALSE);
}


//
// test for a modified file first...
//
f = AcmAppFileSaveModified(hwnd, paafd);
if (!f)
return (FALSE);


//
// get a filename
//
szFileTitle[0] = '\0';
szFilePath[0] = '\0';

f = AppGetFileName(hwnd, szFilePath, szFileTitle, APP_GETFILENAMEF_SAVE);
if (!f)
return (FALSE);


//
//
//
//
//
//
mmr = acmMetrics(NULL, ACM_METRIC_MAX_SIZE_FORMAT, &cbwfx);
if (MMSYSERR_NOERROR != mmr)
{
AppMsgBox(hwnd, MB_OK | MB_ICONEXCLAMATION,
TEXT("AcmAppFileNew() acmMetrics failed mmr=%u!"), mmr);
return (FALSE);
}

pwfx = (LPWAVEFORMATEX)GlobalAllocPtr(GHND, cbwfx);
if (NULL == pwfx)
{
AppMsgBox(hwnd, MB_OK | MB_ICONEXCLAMATION,
TEXT("AcmAppFileNew() GlobalAllocPtr(%lu) failed!"), cbwfx);
return (FALSE);
}


//
//
//
//
f = FALSE;


//
// initialize the ACMFORMATCHOOSE members
//
memset(&afc, 0, sizeof(afc));

afc.cbStruct = sizeof(afc);
afc.fdwStyle = ACMFORMATCHOOSE_STYLEF_SHOWHELP;
afc.hwndOwner = hwnd;
afc.pwfx = pwfx;
afc.cbwfx = cbwfx;
afc.pszTitle = TEXT("ACM App: New Format Choice");

afc.szFormatTag[0] = '\0';
afc.szFormat[0] = '\0';
afc.pszName = NULL;
afc.cchName = 0;

afc.fdwEnum = 0;
afc.pwfxEnum = NULL;

afc.hInstance = NULL;
afc.pszTemplateName = NULL;
afc.lCustData = 0L;
afc.pfnHook = NULL;


//
//
//
mmr = acmFormatChoose(&afc);
if (MMSYSERR_NOERROR != mmr)
{
if (ACMERR_CANCELED != mmr)
{
AppMsgBox(hwnd, MB_OK | MB_ICONEXCLAMATION,
TEXT("acmFormatChoose() failed with error = %u!"), mmr);
}

GlobalFreePtr(pwfx);
return (FALSE);
}


//
//
//
hmmio = mmioOpen(szFilePath,
NULL,
MMIO_CREATE | MMIO_WRITE | MMIO_EXCLUSIVE | MMIO_ALLOCBUF);
if (NULL == hmmio)
{
AppMsgBox(hwnd, MB_OK | MB_ICONEXCLAMATION,
TEXT("AcmAppFileNew() cannot create file '%s'!"), (LPSTR)szFilePath);

GlobalFreePtr(pwfx);
return (FALSE);
}


//
// create the RIFF chunk of form type 'WAVE'
//
ckRIFF.fccType = mmioFOURCC('W', 'A', 'V', 'E');
ckRIFF.cksize = 0L;
mmioCreateChunk(hmmio, &ckRIFF, MMIO_CREATERIFF);


//
// now create the destination fmt, fact, and data chunks _in that order_
//
// hmmio is now descended into the 'RIFF' chunk--create the format chunk
// and write the format header into it
//
cbwfx = SIZEOF_WAVEFORMATEX(pwfx);

ck.ckid = mmioFOURCC('f', 'm', 't', ' ');
ck.cksize = 0L;
mmioCreateChunk(hmmio, &ck, 0);

mmioWrite(hmmio, (HPSTR)pwfx, cbwfx);
mmioAscend(hmmio, &ck, 0);

//
// create the 'fact' chunk (not necessary for PCM--but is nice to have)
// since we are not writing any data to this file (yet), we set the
// samples contained in the file to 0..
//
ck.ckid = mmioFOURCC('f', 'a', 'c', 't');
ck.cksize = 0L;
mmioCreateChunk(hmmio, &ck, 0);

cSamples = 0L;
mmioWrite(hmmio, (HPSTR)&cSamples, sizeof(DWORD));
mmioAscend(hmmio, &ck, 0);


//
// create the data chunk with no data..
//
ck.ckid = mmioFOURCC('d', 'a', 't', 'a');
ck.cksize = 0L;
mmioCreateChunk(hmmio, &ck, 0);
mmioAscend(hmmio, &ck, 0);

mmioAscend(hmmio, &ckRIFF, 0);

mmioClose(hmmio, 0);


//
//
//
GlobalFreePtr(pwfx);

lstrcpy(paafd->szFilePath, szFilePath);
lstrcpy(paafd->szFileTitle, szFileTitle);

return (AcmAppFileOpen(hwnd, paafd));


//
// success
//
return (TRUE);
} // AcmAppFileNew()


//--------------------------------------------------------------------------;
//
// BOOL AcmAppFileOpen
//
// Description:
// This function opens the specified file and get the important info
// from it.
//
// NOTE! This function does NOT check for a modified file! It is
// assumed that the calling function took care of everything before
// calling this function.
//
// Arguments:
// HWND hwnd: Handle to main window.
//
// PACMAPPFILEDESC paafd: Pointer to file descriptor.
//
// Return (BOOL):
// The return value is TRUE if the function is successful. It is FALSE
// if an error occurred. If an error does occur, then the contents
// of the file descriptor will remain unchanged.
//
//
//--------------------------------------------------------------------------;

BOOL FNGLOBAL AcmAppFileOpen
(
HWND hwnd,
PACMAPPFILEDESC paafd
)
{
WAVEIOCB wio;
WIOERR werr;

#ifdef WIN32
HANDLE hf;
#else
#define SEEK_SET 0 // flags for _lseek
#define SEEK_CUR 1
#define SEEK_END 2

HFILE hf;
OFSTRUCT of;
DWORD dw;
#endif
DWORD cbFileSize;
BOOL fReturn;



//
// blow previous stuff...
//
if (NULL != paafd->pwfx)
{
GlobalFreePtr(paafd->pwfx);
paafd->pwfx = NULL;
paafd->cbwfx = 0;
}

paafd->fdwState = 0L;
paafd->cbFileSize = 0L;
paafd->uDosChangeDate = 0;
paafd->uDosChangeTime = 0;
paafd->fdwFileAttributes = 0L;
paafd->dwDataBytes = 0L;
paafd->dwDataSamples = 0L;


//
// open the file for reading..
//
#ifdef WIN32
hf = CreateFile(paafd->szFilePath, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, 0, 0);
if (INVALID_HANDLE_VALUE == hf)
return (FALSE);
#else
of.cBytes = sizeof(of);
hf = OpenFile(paafd->szFilePath, &of, OF_READ);
if (HFILE_ERROR == hf)
return (FALSE);
#endif

//
// assume the worst
//
fReturn = FALSE;

//
// determine the length in _bytes_ of the file
//
#ifdef WIN32
cbFileSize = GetFileSize((HANDLE)hf, NULL);
#else
cbFileSize = _llseek(hf, 0L, SEEK_END);
_llseek(hf, 0L, SEEK_SET);
#endif


//
//
//
//
paafd->cbFileSize = cbFileSize;

#ifdef WIN32
{
BY_HANDLE_FILE_INFORMATION bhfi;
WORD wDosChangeDate;
WORD wDosChangeTime;

GetFileInformationByHandle(hf, &bhfi);

paafd->fdwFileAttributes = bhfi.dwFileAttributes;

FileTimeToDosDateTime(&bhfi.ftLastWriteTime,
&wDosChangeDate, &wDosChangeTime);

paafd->uDosChangeDate = (UINT)wDosChangeDate;
paafd->uDosChangeTime = (UINT)wDosChangeTime;
}
#else
paafd->fdwFileAttributes = DosGetFileAttributes(paafd->szFilePath);

dw = DosGetDateTime(hf);
paafd->uDosChangeDate = LOWORD(dw);
paafd->uDosChangeTime = HIWORD(dw);
#endif


//
// now return the fully qualified path and title for the file
//
#ifndef WIN32
lstrcpy(paafd->szFilePath, of.szPathName);
#endif
AppGetFileTitle(paafd->szFilePath, paafd->szFileTitle);

#ifdef WIN32
CloseHandle(hf);
#else
_lclose(hf);
#endif


//
//
//
//
werr = wioFileOpen(&wio, paafd->szFilePath, 0L);
if (WIOERR_NOERROR == werr)
{
UINT cbwfx;

cbwfx = SIZEOF_WAVEFORMATEX(wio.pwfx);

paafd->pwfx = (LPWAVEFORMATEX)GlobalAllocPtr(GHND, cbwfx);
if (NULL != paafd->pwfx)
{
_fmemcpy(paafd->pwfx, wio.pwfx, cbwfx);

paafd->cbwfx = cbwfx;

paafd->dwDataBytes = wio.dwDataBytes;
paafd->dwDataSamples = wio.dwDataSamples;

fReturn = TRUE;
}

wioFileClose(&wio, 0L);
}
else
{
AppMsgBox(hwnd, MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL,
TEXT("The file '%s' cannot be loaded as a wave file (wio error=%u)."),
(LPTSTR)paafd->szFilePath, werr);
}


//
// !!! before returning, we really should try to display a error
// message... memory error, etc..
//
return (fReturn);
} // AcmAppFileOpen()


//--------------------------------------------------------------------------;
//
// BOOL AcmAppOpenInstance
//
// Description:
//
//
// Arguments:
// HWND hwnd:
//
// LPCTSTR pszFilePath:
//
// BOOL fForceOpen:
//
// Return (BOOL):
//
//--------------------------------------------------------------------------;

BOOL FNLOCAL AcmAppOpenInstance
(
HWND hwnd,
LPCTSTR pszFilePath,
BOOL fForceOpen
)
{
TCHAR szCmdLine[APP_MAX_FILE_PATH_CHARS * 2];
BOOL f;
UINT uDosErr;


//
//
//
if (!fForceOpen)
{
if (0 == (APP_OPTIONSF_AUTOOPEN * gfuAppOptions))
{
return (TRUE);
}
}

//
//
//
if (0 == GetModuleFileName(ghinst, szCmdLine, SIZEOF(szCmdLine)))
{
//
// this would be fatal
//
AppMsgBox(hwnd, MB_ICONEXCLAMATION | MB_OK,
TEXT("GetModuleFileName() is unable to return self reference!"));

return (FALSE);
}


lstrcat(szCmdLine, TEXT(" "));
lstrcat(szCmdLine, pszFilePath);

#ifdef WIN32
{
STARTUPINFO si;
PROCESS_INFORMATION pi;

//
// perform the equivalent of WinExec in NT, but we use a Unicode string
//
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOW;

f = CreateProcess(NULL,
szCmdLine,
NULL,
NULL,
FALSE,
0,
NULL,
NULL,
&si,
&pi);

if (f)
{
//
// as the docs say.. wait 10 second for process to go idle before
// continuing.
//
WaitForInputIdle(pi.hProcess, 10000);

CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
else
{
uDosErr = GetLastError();
}
}
#else
{
uDosErr = WinExec(szCmdLine, SW_SHOW);
f = (uDosErr >= 32);
}
#endif

if (!f)
{
AppMsgBox(hwnd, MB_ICONEXCLAMATION | MB_OK,
TEXT("WinExec(%s) failed! DOS error=%u."),
(LPTSTR)szCmdLine, uDosErr);

}

return (f);
} // AcmAppOpenInstance()





//--------------------------------------------------------------------------;
//
// BOOL AcmAppFileSave
//
// Description:
// This function saves the file to the specified file.
//
// NOTE! This function does NOT bring up a save file chooser dialog
// if the file path is invalid. The calling function is responsible
// for making sure the file path is valid before calling this function.
//
// This function also does NOT modify the 'modified' bit of the file
// descriptor. This is up to the calling function.
//
// Arguments:
// HWND hwnd: Handle to main window.
//
// PACMAPPFILEDESC paafd: Pointer to file descriptor.
//
// Return (BOOL):
// The return value is TRUE if the function is successful. It is FALSE
// if an error occurred. If an error does occur, then the contents
// of the file descriptor was not saved.
//
//--------------------------------------------------------------------------;

BOOL FNGLOBAL AcmAppFileSave
(
HWND hwnd,
PACMAPPFILEDESC paafd,
PTSTR pszFilePath,
PTSTR pszFileTitle,
UINT fuSave
)
{

return (FALSE);
} // AcmAppFileSave()






//==========================================================================;
//==========================================================================;
//==========================================================================;
//==========================================================================;









//
//
//

#define IDD_INFOLIST 100
#define IDD_INFOINFO 101
#define IDD_INFOTEXT 102

#ifdef RC_INVOKED

#define DLG_INFOEDIT 31

#else

#define DLG_INFOEDIT MAKEINTRESOURCE(31)

#endif

////////////////////////////////////////////////////////////////////////////

typedef struct tCHUNK
{
FOURCC fcc;
DWORD cksize;
BYTE data[];
} CHUNK, * PCHUNK, far * LPCHUNK;



typedef struct tDISP
{
DWORD cfid; // Clipboard id of data
HANDLE h; // handle to data
struct tDISP * next; // next in list
} DISP;

typedef struct tINFODATA
{
WORD index; // index into aINFO
WORD wFlags; // flags for chunk
DWORD dwINFOOffset; // offset in file to INFO chunk

#define INFOCHUNK_MODIFIED 1
#define INFOCHUNK_REVERT 2 // command to revert to original text

LPCTSTR lpText; // text of modified chunk. None if NULL.

struct tINFODATA near * pnext; // next read sub-chunk
} INFODATA, * PINFODATA, FAR * LPINFODATA;

typedef struct tINFOCHUNK
{
LPTSTR lpChunk; // complete chunk in memory (GlobalPtr)
DWORD cksize; // size of chunk data
PINFODATA pHead; // first sub-chunk data
} INFOCHUNK, * PINFOCHUNK, FAR * LPINFOCHUNK;

////////////////////////////////////////////////////////////////////////////
//
// error returns from RIFF functions
//
#define RIFFERR_BASE (0)
#define RIFFERR_NOERROR (0)
#define RIFFERR_ERROR (RIFFERR_BASE+1)
#define RIFFERR_BADPARAM (RIFFERR_BASE+2)
#define RIFFERR_FILEERROR (RIFFERR_BASE+3)
#define RIFFERR_NOMEM (RIFFERR_BASE+4)
#define RIFFERR_BADFILE (RIFFERR_BASE+5)

////////////////////////////////////////////////////////////////////////////
//
// public function prototypes
//

#define RIFFAPI FAR PASCAL


BOOL RIFFAPI riffCopyList(HMMIO hmmioSrc, HMMIO hmmioDst, const LPMMCKINFO lpck);
BOOL RIFFAPI riffCopyChunk(HMMIO hmmioSrc, HMMIO hmmioDst, const LPMMCKINFO lpck);

LRESULT RIFFAPI riffInitINFO(INFOCHUNK FAR * FAR * lplpInfo);
LRESULT RIFFAPI riffReadINFO(HMMIO hmmio, const LPMMCKINFO lpck, LPINFOCHUNK lpInfo);
LRESULT RIFFAPI riffEditINFO(HWND hwnd, LPINFOCHUNK lpInfo, HINSTANCE hInst);
LRESULT RIFFAPI riffFreeINFO(INFOCHUNK FAR * FAR * lpnpInfo);
LRESULT RIFFAPI riffWriteINFO(HMMIO hmmioDst, LPINFOCHUNK lpInfo);


LRESULT RIFFAPI riffReadDISP(HMMIO hmmio, LPMMCKINFO lpck, DISP FAR * FAR * lpnpDisp);
LRESULT RIFFAPI riffFreeDISP(DISP FAR * FAR * lpnpDisp);
LRESULT RIFFAPI riffWriteDISP(HMMIO hmmio, DISP FAR * FAR * lpnpDisp);


LRESULT NEAR PASCAL riffParseINFO(const LPINFOCHUNK lpInfo);



/** BOOL RIFFAPI riffCopyChunk(HMMIO hmmioSrc, HMMIO hmmioDst, const LPMMCKINFO lpck)
*
* DESCRIPTION:
*
*
* ARGUMENTS:
* (LPWAVECONVCB lpwc, LPMMCKINFO lpck)
*
* RETURN (BOOL NEAR PASCAL):
*
*
* NOTES:
*
** */

BOOL RIFFAPI riffCopyChunk(HMMIO hmmioSrc, HMMIO hmmioDst, const LPMMCKINFO lpck)
{
MMCKINFO ck;
HPSTR hpBuf;

//
//
//
hpBuf = (HPSTR)GlobalAllocPtr(GHND, lpck->cksize);
if (!hpBuf)
return (FALSE);

ck.ckid = lpck->ckid;
ck.cksize = lpck->cksize;
if (mmioCreateChunk(hmmioDst, &ck, 0))
goto rscc_Error;

if (mmioRead(hmmioSrc, hpBuf, lpck->cksize) != (LONG)lpck->cksize)
goto rscc_Error;

if (mmioWrite(hmmioDst, hpBuf, lpck->cksize) != (LONG)lpck->cksize)
goto rscc_Error;

if (mmioAscend(hmmioDst, &ck, 0))
goto rscc_Error;

if (hpBuf)
GlobalFreePtr(hpBuf);

return (TRUE);

rscc_Error:

if (hpBuf)
GlobalFreePtr(hpBuf);

return (FALSE);
} /* RIFFSupCopyChunk() */

/** BOOL RIFFAPI riffCopyList(HMMIO hmmioSrc, HMMIO hmmioDst, const LPMMCKINFO lpck)
*
* DESCRIPTION:
*
*
* ARGUMENTS:
* (HMMIO hmmioSrc, HMMIO hmmioDst, LPMMCKINFO lpck)
*
* RETURN (BOOL NEAR PASCAL):
*
*
* NOTES:
*
** */

BOOL RIFFAPI riffCopyList(HMMIO hmmioSrc, HMMIO hmmioDst, const LPMMCKINFO lpck)
{
MMCKINFO ck;
HPSTR hpBuf;
DWORD dwCopySize;

hpBuf = (HPSTR)GlobalAllocPtr(GHND, lpck->cksize);
if (!hpBuf)
return (FALSE);

dwCopySize=lpck->cksize;

// mmio leaves us after LIST ID

ck.ckid = lpck->ckid;
ck.cksize = dwCopySize;
ck.fccType= lpck->fccType;

if (mmioCreateChunk(hmmioDst, &ck, MMIO_CREATELIST))
goto rscl_Error;

// we already wrote 'LIST' ID, so reduce byte count
dwCopySize-=sizeof(FOURCC);

if (mmioRead(hmmioSrc, hpBuf, dwCopySize) != (LONG)dwCopySize)
goto rscl_Error;

if (mmioWrite(hmmioDst, hpBuf, dwCopySize) != (LONG)dwCopySize)
goto rscl_Error;

if (mmioAscend(hmmioDst, &ck, 0))
goto rscl_Error;

if (hpBuf)
GlobalFreePtr(hpBuf);

return (TRUE);

rscl_Error:

if (hpBuf)
GlobalFreePtr(hpBuf);

return (FALSE);
} /* RIFFSupCopyList() */


/////////////////////////////////////////////////////////////////////////////

typedef struct tINFO
{
PTSTR pFOURCC;
PTSTR pShort;
PTSTR pLong;
} INFO;

static INFO aINFO[]=
{
TEXT("IARL"), TEXT("Archival Location"), TEXT("Indicates where the subject of the file is archived."),
TEXT("IART"), TEXT("Artist"), TEXT("Lists the artist of the original subject of the file. For example, \"Michaelangelo.\""),
TEXT("ICMS"), TEXT("Commissioned"), TEXT("Lists the name of the person or organization that commissioned the subject of the file. For example, \"Pope Julian II.\""),
TEXT("ICMT"), TEXT("Comments"), TEXT("Provides general comments about the file or the subject of the file. If the comment is several sentences long, end each sentence with a period. Do not include newline characters."),
TEXT("ICOP"), TEXT("Copyright"), TEXT("Records the copyright information for the file. For example, \"Copyright Encyclopedia International 1991.\" If there are multiple copyrights, separate them by a semicolon followed by a space."),
TEXT("ICRD"), TEXT("Creation date"), TEXT("Specifies the date the subject of the file was created. List dates in year-month-day format, padding one-digit months and days with a zero on the left. For example, \"1553-05-03\" for May 3, 1553."),
TEXT("ICRP"), TEXT("Cropped"), TEXT("Describes whether an image has been cropped and, if so, how it was cropped. For example, \"lower right corner.\""),
TEXT("IDIM"), TEXT("Dimensions"), TEXT("Specifies the size of the original subject of the file. For example, \"8.5 in h, 11 in w.\""),
TEXT("IDPI"), TEXT("Dots Per Inch"), TEXT("Stores dots per inch setting of the digitizer used to produce the file, such as \"300.\""),
TEXT("IENG"), TEXT("Engineer"), TEXT("Stores the name of the engineer who worked on the file. If there are multiple engineers, separate the names by a semicolon and a blank. For example, \"Smith, John; Adams, Joe.\""),
TEXT("IGNR"), TEXT("Genre"), TEXT("Describes the original work, such as, \"landscape,\" \"portrait,\" \"still life,\" etc."),
TEXT("IKEY"), TEXT("Keywords"), TEXT("Provides a list of keywords that refer to the file or subject of the file. Separate multiple keywords with a semicolon and a blank. For example, \"Seattle; aerial view; scenery.\""),
TEXT("ILGT"), TEXT("Lightness"), TEXT("Describes the changes in lightness settings on the digitizer required to produce the file. Note that the format of this information depends on hardware used."),
TEXT("IMED"), TEXT("Medium"), TEXT("Describes the original subject of the file, such as, \"computer image,\" \"drawing,\" \"lithograph,\" and so forth."),
TEXT("INAM"), TEXT("Name"), TEXT("Stores the title of the subject of the file, such as, \"Seattle From Above.\""),
TEXT("IPLT"), TEXT("Palette Setting"), TEXT("Specifies the number of colors requested when digitizing an image, such as \"256.\""),
TEXT("IPRD"), TEXT("Product"), TEXT("Specifies the name of the title the file was originally intended for, such as \"Encyclopedia of Pacific Northwest Geography.\""),
TEXT("ISBJ"), TEXT("Subject"), TEXT("Describes the contents of the file, such as \"Aerial view of Seattle.\""),

TEXT("ISFT"), TEXT("Software"),           TEXT("Identifies the name of the software package used to create the file, such as \"Microsoft WaveEdit.\""), 
TEXT("ISHP"), TEXT("Sharpness"), TEXT("Identifies the changes in sharpness for the digitizer required to produce the file (the format depends on the hardware used)."),
TEXT("ISRC"), TEXT("Source"), TEXT("Identifies the name of the person or organization who supplied the original subject of the file. For example, \"Trey Research.\""),
TEXT("ISRF"), TEXT("Source Form"), TEXT("Identifies the original form of the material that was digitized, such as \"slide,\" \"paper,\" \"map,\" and so forth. This is not necessarily the same as IMED."),
TEXT("ITCH"), TEXT("Technician"), TEXT("Identifies the technician who digitized the subject file. For example, \"Smith, John.\""),

NULL, NULL, NULL

};


void NEAR PASCAL riffInsertINFO(LPINFOCHUNK lpInfo, const PINFODATA pInfo)
{
PINFODATA pI;

if(!lpInfo)
return;

if(!lpInfo->pHead)
{
lpInfo->pHead=pInfo;
return;
}

pI=lpInfo->pHead;
while(pI->pnext)
{
pI=pI->pnext;
}
// insert at end
pI->pnext=pInfo;

return;
}

PINFODATA NEAR PASCAL riffCreateINFO(WORD id, WORD wFlags, DWORD dwInfoOffset, LPCTSTR lpText)
{
PINFODATA pI;
pI=(PINFODATA)LocalAlloc(LPTR,sizeof(INFODATA));
if(!pI)
return NULL;

pI->index=id;
pI->wFlags=wFlags;
pI->dwINFOOffset=dwInfoOffset;
pI->lpText=lpText;

return pI;
}

LRESULT RIFFAPI riffInitINFO(INFOCHUNK FAR * FAR * lplpInfo)
{
LPINFOCHUNK lpInfo;
WORD id;
PINFODATA pI;

lpInfo=(LPINFOCHUNK)GlobalAllocPtr(GHND, sizeof(INFOCHUNK));
if(!lpInfo)
return RIFFERR_NOMEM;
*lplpInfo=lpInfo;

for (id=0;aINFO[id].pFOURCC;id++)
{
pI=riffCreateINFO(id, 0, 0L, NULL); // create empty INFO
riffInsertINFO(lpInfo,pI);
}
return RIFFERR_NOERROR;
}

LRESULT RIFFAPI riffReadINFO(HMMIO hmmio, const LPMMCKINFO lpck, LPINFOCHUNK lpInfo)
{
DWORD dwInfoSize;

dwInfoSize=lpck->cksize - sizeof(FOURCC); // take out 'INFO'

lpInfo->cksize=dwInfoSize;
lpInfo->lpChunk=(LPTSTR)GlobalAllocPtr(GHND, dwInfoSize);
if(!lpInfo->lpChunk)
return RIFFERR_NOMEM;

if (mmioRead(hmmio, (HPSTR)lpInfo->lpChunk, dwInfoSize) != (LONG)dwInfoSize)
return RIFFERR_FILEERROR;
else
return riffParseINFO(lpInfo);
}

PINFODATA NEAR PASCAL riffFindPIINFO(const LPINFOCHUNK lpInfo, FOURCC fcc)
{
PINFODATA pI;

pI=lpInfo->pHead;
while(pI)
{
if(mmioStringToFOURCC(aINFO[pI->index].pFOURCC,0)==fcc)
return(pI);
pI=pI->pnext;
}
return NULL;
}

void NEAR PASCAL riffModifyINFO(const LPINFOCHUNK lpInfo, PINFODATA pI, WORD wFlags, DWORD dw, LPCTSTR lpText)
{
if(!pI)
return;

pI->wFlags=wFlags;
if(!(wFlags&INFOCHUNK_MODIFIED))
pI->dwINFOOffset=dw;

if(pI->lpText)
{
if(lpText)
{
if(!lstrcmp(lpText,pI->lpText))
{
// they are the same, don't bother changing...
GlobalFreePtr(lpText);
}
else
{
GlobalFreePtr(pI->lpText);
pI->lpText=lpText;
}
}
else if(wFlags&INFOCHUNK_REVERT)
{
GlobalFreePtr(pI->lpText);
pI->lpText=NULL;
}
}
else if(lpText)
{
// if no read data, don't bother to check....
if(!lpInfo->lpChunk && *lpText)
{
pI->lpText=lpText;
}
else if(lstrcmp(lpText, (LPTSTR)lpInfo->lpChunk+pI->dwINFOOffset))
{ // new text...
if(*lpText)
// NOT the same, set...
pI->lpText=lpText;
else
// new is blank, do nothing...
GlobalFreePtr(lpText);
}
else
// the same, don't bother...
GlobalFreePtr(lpText);
}
}

WORD NEAR PASCAL riffFindaINFO(FOURCC fcc)
{
WORD id;

for (id=0;aINFO[id].pFOURCC;id++)
{
if(mmioStringToFOURCC(aINFO[id].pFOURCC,0)==fcc)
return id;
}
return 0xFFFF;
}



LRESULT NEAR PASCAL riffParseINFO(const LPINFOCHUNK lpInfo)
{
LPTSTR lpBuf;
DWORD dwCurInfoOffset;
PINFODATA pI;
LPCHUNK lpck;

lpBuf=lpInfo->lpChunk;
for(dwCurInfoOffset=0;dwCurInfoOffset<lpInfo->cksize;)
{
lpck=(LPCHUNK)((LPSTR)(lpBuf+dwCurInfoOffset));
dwCurInfoOffset+=sizeof(CHUNK); // dwCurInfoOffset is offset of data
pI=riffFindPIINFO(lpInfo,lpck->fcc);
if(!pI)
{
int n;

// file contains unknown INFO chunk
n = AppMsgBox(NULL, MB_YESNO | MB_ICONEXCLAMATION | MB_TASKMODAL,
TEXT("This wave file contains an unknown item in the INFO chunk: '%4.4s'. Open anyway?"),
(LPCSTR)(lpck));
if (n == IDNO)
{
return RIFFERR_BADFILE;
}


}
else
{
// modify entry to show text (data) from file...
riffModifyINFO(lpInfo, pI, 0, dwCurInfoOffset, NULL);
}
dwCurInfoOffset+=lpck->cksize+(lpck->cksize&1); // skip past data
}

return RIFFERR_NOERROR;
}

LRESULT RIFFAPI riffFreeINFO(INFOCHUNK FAR * FAR * lplpInfo)
{
PINFODATA pI;
PINFODATA pIT;
LPINFOCHUNK lpInfo;
LRESULT lr;

lr = RIFFERR_BADPARAM;

if(!lplpInfo)
goto riff_FI_Error;

lpInfo=*lplpInfo;
if(!lpInfo)
goto riff_FI_Error;

if(lpInfo->lpChunk)
GlobalFreePtr(lpInfo->lpChunk);


pI=lpInfo->pHead;

while(pI)
{
pIT=pI;
pI=pI->pnext;
LocalFree((HANDLE)pIT);
}


//

GlobalFreePtr(lpInfo);
*lplpInfo=NULL;
return RIFFERR_NOERROR;

riff_FI_Error:
return lr;
}


TCHAR szBuf[255];

static BOOL NEAR PASCAL riffSetupEditBoxINFO(HWND hdlg, const LPINFOCHUNK lpInfo, WORD wFlags)
{
static PTSTR szFormat = TEXT("%-4s%c %-25s");
PINFODATA pI;
WORD iSel;
HWND hLB;

hLB=GetDlgItem(hdlg, IDD_INFOLIST);
if(wFlags&INFOCHUNK_MODIFIED)
{
iSel = ComboBox_GetCurSel(hLB);

}
else
iSel = 0;

ComboBox_ResetContent(hLB);

pI=lpInfo->pHead;

while(pI)
{
wsprintf(szBuf,szFormat,
(LPCSTR)aINFO[pI->index].pFOURCC,
(pI->dwINFOOffset || ( (pI->lpText) && (pI->lpText[0]) ) ) ?
'*' : ' ',
(LPCSTR)aINFO[pI->index].pShort
);

ComboBox_AddString(hLB, szBuf);
pI=pI->pnext;
}
ComboBox_SetCurSel(hLB, iSel);

if(!(wFlags&INFOCHUNK_MODIFIED))
{
// FIRST time only
pI=lpInfo->pHead;
if(pI)
if(pI->lpText)
// Modified text
SetDlgItemText(hdlg, IDD_INFOTEXT, (LPCTSTR)pI->lpText);
else if(pI->dwINFOOffset)
// default text
SetDlgItemText(hdlg, IDD_INFOTEXT, (LPCTSTR)(lpInfo->lpChunk+pI->dwINFOOffset));
else
// no text
SetDlgItemText(hdlg, IDD_INFOTEXT, (LPCTSTR)TEXT(""));
SetDlgItemText(hdlg, IDD_INFOINFO, (LPCTSTR)aINFO[0].pLong);
}
return TRUE;
}


static BOOL Cls_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam)
{
LPINFOCHUNK lpInfo;
HFONT hFont;
HWND hLB;

lpInfo = (LPINFOCHUNK)lParam;
if(!lpInfo)
return FALSE;

SetWindowLong(hwnd, DWL_USER, (LONG)lpInfo);

hFont = GetStockFont(SYSTEM_FIXED_FONT);

hLB=GetDlgItem(hwnd, IDD_INFOLIST);
SetWindowFont(hLB, hFont, FALSE);

riffSetupEditBoxINFO(hwnd, lpInfo, 0);

return TRUE;
}

static void Cls_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
{
LPINFOCHUNK lpInfo;
PINFODATA pI;
WORD iSel;
int i;
LPTSTR lpstr;

lpInfo=(LPINFOCHUNK)GetWindowLong(hwnd, DWL_USER);

switch(id)
{
case IDOK:
case IDCANCEL:
EndDialog(hwnd, (id == IDOK));
break;
case IDD_INFOLIST:
switch(codeNotify)
{
case CBN_SELCHANGE:

iSel = ComboBox_GetCurSel(GetDlgItem(hwnd, id));
SetDlgItemText(hwnd, IDD_INFOINFO, (LPCTSTR)aINFO[iSel].pLong);

pI=lpInfo->pHead;
while(pI)
{
if(pI->index==iSel)
break;
pI=pI->pnext;
}
if(pI)
{
if(pI->lpText)
// Modified text
SetDlgItemText(hwnd, IDD_INFOTEXT, (LPCTSTR)pI->lpText);
else if(pI->dwINFOOffset)
// default text
SetDlgItemText(hwnd, IDD_INFOTEXT, (LPCTSTR)(lpInfo->lpChunk+pI->dwINFOOffset));
else
// no text
SetDlgItemText(hwnd, IDD_INFOTEXT, (LPCTSTR)TEXT(""));
}
else
SetDlgItemText(hwnd, IDD_INFOINFO, (LPCTSTR)TEXT("Can't FIND iSel"));
break;
}

case IDD_INFOTEXT:
switch(codeNotify)
{
case EN_KILLFOCUS:
// get text out and give to current id
iSel=(WORD)SendDlgItemMessage(hwnd,IDD_INFOLIST,CB_GETCURSEL,0,0L);
pI=lpInfo->pHead;
while(pI)
{
if(pI->index==iSel)
break;
pI=pI->pnext;
}
if(pI)
{
i=GetDlgItemText(hwnd, IDD_INFOTEXT, szBuf,sizeof(szBuf));
lpstr=(LPTSTR)GlobalAllocPtr(GHND,(DWORD)i+1);
if(!lpstr)
break;

lstrcpy(lpstr,szBuf);

riffModifyINFO(lpInfo, pI, INFOCHUNK_MODIFIED, 0, lpstr);

riffSetupEditBoxINFO(hwnd, lpInfo, INFOCHUNK_MODIFIED);
}
else
SetDlgItemText(hwnd, IDD_INFOINFO, (LPCTSTR)TEXT("Can't FIND iSel"));
break;

}
break;
case IDD_INFOINFO:
break;
}

}

BOOL FNGLOBAL DlgProcINFOEdit(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG:
return (BOOL)(UINT)(DWORD)(LRESULT)HANDLE_WM_INITDIALOG(hdlg, wParam, lParam, Cls_OnInitDialog);

case WM_COMMAND:
HANDLE_WM_COMMAND(hdlg, wParam, lParam, Cls_OnCommand);
break;
}

return FALSE;
}


LRESULT RIFFAPI riffEditINFO(HWND hwnd, LPINFOCHUNK lpInfo, HINSTANCE hInst)
{
LRESULT lr;
DLGPROC lpfn;
#ifdef DEBUG
int i;
#endif

lr = RIFFERR_BADPARAM;

if(!lpInfo)
goto riff_EI_Error;

if (lpfn = (DLGPROC)MakeProcInstance((FARPROC)DlgProcINFOEdit, hInst))
{
#ifdef DEBUG
i=
#endif
DialogBoxParam(hInst, DLG_INFOEDIT, hwnd, lpfn, (LPARAM)(LPVOID)lpInfo);
FreeProcInstance((FARPROC)lpfn);
lr=RIFFERR_NOERROR;
#ifdef DEBUG
if(i==-1)
{
MessageBox(hwnd, TEXT("INFO Edit Error: DLG_INFOEDIT not found. Check .RC file."), TEXT("RIFF SUP module"), MB_OK|MB_ICONEXCLAMATION|MB_TASKMODAL);
lr=RIFFERR_ERROR;
}
#endif

}
#ifdef DEBUG
else
{
MessageBox(hwnd, TEXT("INFO Edit Error: Can't MakeProcInstace()"), TEXT("RIFF SUP module"), MB_OK|MB_ICONEXCLAMATION|MB_TASKMODAL);
lr=RIFFERR_ERROR;
}
#endif

riff_EI_Error:
return lr;
}

LRESULT RIFFAPI riffWriteINFO(HMMIO hmmioDst, LPINFOCHUNK lpInfo)
{
LRESULT lr;
MMCKINFO ck;
MMCKINFO ckINFO;
PINFODATA pI;
LPTSTR lpstr;
BOOL fList=FALSE;

lr = RIFFERR_BADPARAM;

if(!hmmioDst || !lpInfo)
goto riff_SI_Error;

lr=RIFFERR_FILEERROR;

ckINFO.ckid = mmioFOURCC('L', 'I', 'S', 'T');
ckINFO.cksize = 0; // mmio fill fill it in later
ckINFO.fccType= mmioFOURCC('I', 'N', 'F', 'O');

pI=lpInfo->pHead;

while(pI)
{
if(pI->lpText)
// Modified text
lpstr=(LPTSTR)pI->lpText;
else if(pI->dwINFOOffset)
// default text
lpstr=(lpInfo->lpChunk+pI->dwINFOOffset);
else
// no text
lpstr=NULL;
if(lpstr)
{
if(!fList)
{
// only create if needed...
if (mmioCreateChunk(hmmioDst, &ckINFO, MMIO_CREATELIST))
goto riff_SI_Error;
fList=TRUE;
}

ck.ckid=mmioStringToFOURCC(aINFO[pI->index].pFOURCC,0);
ck.cksize=lstrlen(lpstr)+1;
ck.fccType=0;
if (mmioCreateChunk(hmmioDst, &ck, 0))
goto riff_SI_Error;

if (mmioWrite(hmmioDst, (LPBYTE)lpstr, ck.cksize) != (LONG)(ck.cksize))
goto riff_SI_Error;

if (mmioAscend(hmmioDst, &ck, 0))
goto riff_SI_Error;

}
pI=pI->pnext;
}

if(fList)
{
if (mmioAscend(hmmioDst, &ckINFO, 0))
goto riff_SI_Error;
}

return RIFFERR_NOERROR;

riff_SI_Error:
return lr;

}


///////////////////////////////////////////////////////////////////////////////

LRESULT RIFFAPI riffReadDISP(HMMIO hmmio, LPMMCKINFO lpck, DISP FAR * FAR * lpnpDisp)
{
LRESULT lr;
lr = RIFFERR_ERROR;

return lr;
}

LRESULT RIFFAPI riffFreeDISP(DISP FAR * FAR * lpnpDisp)
{
LRESULT lr;
lr = RIFFERR_ERROR;

return lr;
}

LRESULT RIFFAPI riffWriteDISP(HMMIO hmmio, DISP FAR * FAR * lpnpDisp)
{
LRESULT lr;
lr = RIFFERR_ERROR;

return lr;
}









//==========================================================================;
//==========================================================================;
//==========================================================================;
//==========================================================================;




BOOL gfCancelConvert;


#define WM_CONVERT_BEGIN (WM_USER + 100)
#define WM_CONVERT_END (WM_USER + 101)

#define BeginConvert(hwnd, paacd) PostMessage(hwnd, WM_CONVERT_BEGIN, 0, (LPARAM)(UINT)paacd)
#define EndConvert(hwnd, f, paacd) PostMessage(hwnd, WM_CONVERT_END, (WPARAM)f, (LPARAM)(UINT)paacd)


//--------------------------------------------------------------------------;
//
// void AppDlgYield
//
// Description:
//
//
// Arguments:
// HWND hdlg:
//
// Return (void):
//
//--------------------------------------------------------------------------;

void FNLOCAL AppDlgYield
(
HWND hdlg
)
{
MSG msg;

while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if ((hdlg == NULL) || !IsDialogMessage(hdlg, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
} // AppDlgYield()


//--------------------------------------------------------------------------;
//
// BOOL AcmAppConvertEnd
//
// Description:
//
//
// Arguments:
// HWND hdlg:
//
// PAACONVERTDESC paacd:
//
// Return (BOOL):
//
//
//--------------------------------------------------------------------------;

BOOL FNLOCAL AcmAppConvertEnd
(
HWND hdlg,
PAACONVERTDESC paacd
)
{
MMRESULT mmr;
LPACMSTREAMHEADER pash;


//
//
//
//
if (NULL != paacd->hmmioSrc)
{
mmioClose(paacd->hmmioSrc, 0);
paacd->hmmioSrc = NULL;
}

if (NULL != paacd->hmmioDst)
{
mmioAscend(paacd->hmmioDst, &paacd->ckDst, 0);
mmioAscend(paacd->hmmioDst, &paacd->ckDstRIFF, 0);

mmioClose(paacd->hmmioDst, 0);
paacd->hmmioDst = NULL;
}


//
//
//
//
if (NULL != paacd->has)
{
pash = &paacd->ash;

if (ACMSTREAMHEADER_STATUSF_PREPARED & pash->fdwStatus)
{
pash->cbSrcLength = paacd->cbSrcReadSize;
pash->cbDstLength = paacd->cbDstBufSize;

mmr = acmStreamUnprepareHeader(paacd->has, &paacd->ash, 0L);
if (MMSYSERR_NOERROR != mmr)
{
AppMsgBox(hdlg, MB_OK | MB_ICONEXCLAMATION,
TEXT("acmStreamUnprepareHeader() failed with error = %u!"), mmr);
}
}

//
//
//
acmStreamClose(paacd->has, 0L);
paacd->has = NULL;

if (NULL != paacd->had)
{
acmDriverClose(paacd->had, 0L);
paacd->had = NULL;
}
}


//
//
//
//
if (NULL != paacd->pbSrc)
{
GlobalFreePtr(paacd->pbSrc);
paacd->pbSrc = NULL;
}

if (NULL != paacd->pbDst)
{
GlobalFreePtr(paacd->pbDst);
paacd->pbDst = NULL;
}


return (TRUE);
} // AcmAppConvertEnd()


//--------------------------------------------------------------------------;
//
// BOOL AcmAppConvertBegin
//
// Description:
//
//
// Arguments:
// HWND hdlg:
//
// PAACONVERTDESC paacd:
//
// Return (BOOL):
//
//
//--------------------------------------------------------------------------;

BOOL FNLOCAL AcmAppConvertBegin
(
HWND hdlg,
PAACONVERTDESC paacd
)
{
TCHAR ach[40];
MMRESULT mmr;
MMCKINFO ckSrcRIFF;
MMCKINFO ck;
DWORD dw;
LPACMSTREAMHEADER pash;
LPWAVEFILTER pwfltr;


//
//
//
if (NULL != paacd->hadid)
{
mmr = acmDriverOpen(&paacd->had, paacd->hadid, 0L);
if (MMSYSERR_NOERROR != mmr)
{
AcmAppGetErrorString(mmr, ach);
AppMsgBox(hdlg, MB_OK | MB_ICONEXCLAMATION,
TEXT("The selected driver (hadid=%.04Xh) cannot be opened. %s (%u)"),
paacd->hadid, (LPSTR)ach, mmr);
return (FALSE);
}
}


//
//
//
//
pwfltr = paacd->fApplyFilter ? paacd->pwfltr : (LPWAVEFILTER)NULL;

mmr = acmStreamOpen(&paacd->has,
paacd->had,
paacd->pwfxSrc,
paacd->pwfxDst,
pwfltr,
0L,
0L,
paacd->fdwOpen);

if (MMSYSERR_NOERROR != mmr)
{
AppMsgBox(hdlg, MB_OK | MB_ICONEXCLAMATION,
TEXT("acmStreamOpen() failed with error = %u!"), mmr);

return (FALSE);
}


//
//
//
mmr = acmStreamSize(paacd->has,
paacd->cbSrcReadSize,
&paacd->cbDstBufSize,
ACM_STREAMSIZEF_SOURCE);

if (MMSYSERR_NOERROR != mmr)
{
AppMsgBox(hdlg, MB_OK | MB_ICONEXCLAMATION,
TEXT("acmStreamSize() failed with error = %u!"), mmr);

return (FALSE);
}



//
// first try to open the file, etc.. open the given file for reading
// using buffered I/O
//
paacd->hmmioSrc = mmioOpen(paacd->szFilePathSrc,
NULL,
MMIO_READ | MMIO_DENYWRITE | MMIO_ALLOCBUF);
if (NULL == paacd->hmmioSrc)
goto aacb_Error;

//
//
//
paacd->hmmioDst = mmioOpen(paacd->szFilePathDst,
NULL,
MMIO_CREATE | MMIO_WRITE | MMIO_EXCLUSIVE | MMIO_ALLOCBUF);
if (NULL == paacd->hmmioDst)
goto aacb_Error;



//
//
//
//
pash = &paacd->ash;
pash->fdwStatus = 0L;


//
// allocate the src and dst buffers for reading/converting data
//
paacd->pbSrc = (HPSTR)GlobalAllocPtr(GHND, paacd->cbSrcReadSize);
if (NULL == paacd->pbSrc)
goto aacb_Error;

paacd->pbDst = (HPSTR)GlobalAllocPtr(GHND, paacd->cbDstBufSize);
if (NULL == paacd->pbDst)
goto aacb_Error;


//
//
//
//
pash->cbStruct = sizeof(*pash);
pash->fdwStatus = 0L;
pash->dwUser = 0L;
pash->pbSrc = paacd->pbSrc;
pash->cbSrcLength = paacd->cbSrcReadSize;
pash->cbSrcLengthUsed = 0L;
pash->dwSrcUser = paacd->cbSrcReadSize;
pash->pbDst = paacd->pbDst;
pash->cbDstLength = paacd->cbDstBufSize;
pash->cbDstLengthUsed = 0L;
pash->dwDstUser = paacd->cbDstBufSize;

mmr = acmStreamPrepareHeader(paacd->has, pash, 0L);
if (MMSYSERR_NOERROR != mmr)
{
AppMsgBox(hdlg, MB_OK | MB_ICONEXCLAMATION,
TEXT("acmStreamPrepareHeader() failed with error = %u!"), mmr);

goto aacb_Error;
}



//
// create the RIFF chunk of form type 'WAVE'
//
//
paacd->ckDstRIFF.fccType = mmioFOURCC('W', 'A', 'V', 'E');
paacd->ckDstRIFF.cksize = 0L;
if (mmioCreateChunk(paacd->hmmioDst, &paacd->ckDstRIFF, MMIO_CREATERIFF))
goto aacb_Error;

//
// locate a 'WAVE' form type in a 'RIFF' thing...
//
ckSrcRIFF.fccType = mmioFOURCC('W', 'A', 'V', 'E');
if (mmioDescend(paacd->hmmioSrc, (LPMMCKINFO)&ckSrcRIFF, NULL, MMIO_FINDRIFF))
goto aacb_Error;

//
// we found a WAVE chunk--now go through and get all subchunks that
// we know how to deal with...
//
while (mmioDescend(paacd->hmmioSrc, &ck, &ckSrcRIFF, 0) == 0)
{
//
// quickly check for corrupt RIFF file--don't ascend past end!
//
if ((ck.dwDataOffset + ck.cksize) > (ckSrcRIFF.dwDataOffset + ckSrcRIFF.cksize))
goto aacb_Error;

switch (ck.ckid)
{
//
// explicitly skip these...
//
//
//
case mmioFOURCC('f', 'm', 't', ' '):
break;

case mmioFOURCC('d', 'a', 't', 'a'):
break;

case mmioFOURCC('f', 'a', 'c', 't'):
break;

case mmioFOURCC('J', 'U', 'N', 'K'):
break;

case mmioFOURCC('P', 'A', 'D', ' '):
break;

case mmioFOURCC('c', 'u', 'e', ' '):
break;


//
// copy chunks that are OK to copy
//
//
//
case mmioFOURCC('p', 'l', 's', 't'):
// although without the 'cue' chunk, it doesn't make much sense
riffCopyChunk(paacd->hmmioSrc, paacd->hmmioDst, &ck);
break;

case mmioFOURCC('D', 'I', 'S', 'P'):
riffCopyChunk(paacd->hmmioSrc, paacd->hmmioDst, &ck);
break;


//
// don't copy unknown chunks
//
//
//
default:
break;
}

//
// step up to prepare for next chunk..
//
mmioAscend(paacd->hmmioSrc, &ck, 0);
}

#if 0
//
// now write out possibly editted chunks...
//
if (riffWriteINFO(paacd->hmmioDst, (glpwio->pInfo)))
{
goto aacb_Error;
}
#endif

//
// go back to beginning of data portion of WAVE chunk
//
if (-1 == mmioSeek(paacd->hmmioSrc, ckSrcRIFF.dwDataOffset + sizeof(FOURCC), SEEK_SET))
goto aacb_Error;

ck.ckid = mmioFOURCC('d', 'a', 't', 'a');
mmioDescend(paacd->hmmioSrc, &ck, &ckSrcRIFF, MMIO_FINDCHUNK);


//
// now create the destination fmt, fact, and data chunks _in that order_
//
//
//
// hmmio is now descended into the 'RIFF' chunk--create the format chunk
// and write the format header into it
//
dw = SIZEOF_WAVEFORMATEX(paacd->pwfxDst);

paacd->ckDst.ckid = mmioFOURCC('f', 'm', 't', ' ');
paacd->ckDst.cksize = dw;
if (mmioCreateChunk(paacd->hmmioDst, &paacd->ckDst, 0))
goto aacb_Error;

if (mmioWrite(paacd->hmmioDst, (HPSTR)paacd->pwfxDst, dw) != (LONG)dw)
goto aacb_Error;

if (mmioAscend(paacd->hmmioDst, &paacd->ckDst, 0) != 0)
goto aacb_Error;

//
// create the 'fact' chunk (not necessary for PCM--but is nice to have)
// since we are not writing any data to this file (yet), we set the
// samples contained in the file to 0..
//
paacd->ckDst.ckid = mmioFOURCC('f', 'a', 'c', 't');
paacd->ckDst.cksize = 0L;
if (mmioCreateChunk(paacd->hmmioDst, &paacd->ckDst, 0))
goto aacb_Error;

if (mmioWrite(paacd->hmmioDst, (HPSTR)&paacd->dwSrcSamples, sizeof(DWORD)) != sizeof(DWORD))
goto aacb_Error;

if (mmioAscend(paacd->hmmioDst, &paacd->ckDst, 0) != 0)
goto aacb_Error;


//
// create the data chunk AND STAY DESCENDED... for reasons that will
// become apparent later..
//
paacd->ckDst.ckid = mmioFOURCC('d', 'a', 't', 'a');
paacd->ckDst.cksize = 0L;
if (mmioCreateChunk(paacd->hmmioDst, &paacd->ckDst, 0))
goto aacb_Error;

//
// at this point, BOTH the src and dst files are sitting at the very
// beginning of their data chunks--so we can READ from the source,
// CONVERT the data, then WRITE it to the destination file...
//
return (TRUE);


//
//
//
//
aacb_Error:

AcmAppConvertEnd(hdlg, paacd);

return (FALSE);
} // AcmAppConvertBegin()


//--------------------------------------------------------------------------;
//
// BOOL AcmAppConvertConvert
//
// Description:
//
//
// Arguments:
// HWND hdlg:
//
// PAACONVERTDESC paacd:
//
// Return (BOOL):
//
//
//--------------------------------------------------------------------------;

BOOL FNLOCAL AcmAppConvertConvert
(
HWND hdlg,
PAACONVERTDESC paacd
)
{
MMRESULT mmr;
TCHAR ach[40];
DWORD dw;
WORD w;
DWORD dwCurrent;
WORD wCurPercent;
LPACMSTREAMHEADER pash;
DWORD cbRead;
DWORD dwTime;


wCurPercent = (WORD)-1;

paacd->cTotalConverts = 0L;
paacd->dwTimeTotal = 0L;
paacd->dwTimeLongest = 0L;
if (0 == paacd->cbSrcData)
{
paacd->dwTimeShortest = 0L;
paacd->dwShortestConvert = 0L;
paacd->dwLongestConvert = 0L;
}
else
{
paacd->dwTimeShortest = (DWORD)-1L;
paacd->dwShortestConvert = (DWORD)-1L;

paacd->dwLongestConvert  = (DWORD)-1L; 
}

pash = &paacd->ash;

for (dwCurrent = 0; dwCurrent < paacd->cbSrcData; )
{
w = (WORD)((dwCurrent * 100) / paacd->cbSrcData);
if (w != wCurPercent)
{
wCurPercent = w;
wsprintf(ach, TEXT("%u%%"), wCurPercent);

if (hdlg)
SetDlgItemText(hdlg, IDD_AACONVERT_TXT_STATUS, ach);
}

AppDlgYield(hdlg);

if (gfCancelConvert)
goto aacc_Error;

//
//
//
cbRead = min(paacd->cbSrcReadSize, paacd->cbSrcData - dwCurrent);
dw = mmioRead(paacd->hmmioSrc, paacd->pbSrc, cbRead);
if (0L == dw)
break;


AppDlgYield(hdlg);
if (gfCancelConvert)
goto aacc_Error;



//
//
//
pash->cbSrcLength = dw;
pash->cbDstLengthUsed = 0L;



dwTime = timeGetTime();

mmr = acmStreamConvert(paacd->has,
&paacd->ash,
ACM_STREAMCONVERTF_BLOCKALIGN);

if (MMSYSERR_NOERROR != mmr)
{
AppMsgBox(hdlg, MB_OK | MB_ICONEXCLAMATION,
TEXT("acmStreamConvert() failed with error = %u!"), mmr);
goto aacc_Error;
}

while (0 == (ACMSTREAMHEADER_STATUSF_DONE & ((AACONVERTDESC volatile *)paacd)->ash.fdwStatus))
;

dwTime = timeGetTime() - dwTime;


paacd->dwTimeTotal += dwTime;

if (dwTime < paacd->dwTimeShortest)
{
paacd->dwTimeShortest = dwTime;
paacd->dwShortestConvert = paacd->cTotalConverts;
}

if (dwTime > paacd->dwTimeLongest)
{
paacd->dwTimeLongest = dwTime;
paacd->dwLongestConvert = paacd->cTotalConverts;
}

paacd->cTotalConverts++;


AppDlgYield(hdlg);
if (gfCancelConvert)
goto aacc_Error;


//
//
//
dw = (cbRead - pash->cbSrcLengthUsed);
if (0L != dw)
{
mmioSeek(paacd->hmmioSrc, -(LONG)dw, SEEK_CUR);
}

dwCurrent += pash->cbSrcLengthUsed;


//
//
//
dw = pash->cbDstLengthUsed;
if (0L == dw)
break;

if (mmioWrite(paacd->hmmioDst, paacd->pbDst, dw) != (LONG)dw)
goto aacc_Error;
}


//
//
//
//
//
//
wCurPercent = (WORD)-1;

for (;paacd->cbSrcData;)
{
w = (WORD)((dwCurrent * 100) / paacd->cbSrcData);
if (w != wCurPercent)
{
wCurPercent = w;
wsprintf(ach, TEXT("Cleanup Pass -- %u%%"), wCurPercent);

if (hdlg)
SetDlgItemText(hdlg, IDD_AACONVERT_TXT_STATUS, ach);
}

AppDlgYield(hdlg);
if (gfCancelConvert)
goto aacc_Error;


//
//
//
dw = 0L;
cbRead = min(paacd->cbSrcReadSize, paacd->cbSrcData - dwCurrent);
if (0L != cbRead)
{
dw = mmioRead(paacd->hmmioSrc, paacd->pbSrc, cbRead);
if (0L == dw)
break;
}


AppDlgYield(hdlg);
if (gfCancelConvert)
goto aacc_Error;



//
//
//
pash->cbSrcLength = dw;
pash->cbDstLengthUsed = 0L;



dwTime = timeGetTime();

mmr = acmStreamConvert(paacd->has,
&paacd->ash,
ACM_STREAMCONVERTF_BLOCKALIGN |
ACM_STREAMCONVERTF_END);

if (MMSYSERR_NOERROR != mmr)
{
AppMsgBox(hdlg, MB_OK | MB_ICONEXCLAMATION,
TEXT("acmStreamConvert() failed with error = %u!"), mmr);
goto aacc_Error;
}

while (0 == (ACMSTREAMHEADER_STATUSF_DONE & ((AACONVERTDESC volatile *)paacd)->ash.fdwStatus))
;

dwTime = timeGetTime() - dwTime;


paacd->dwTimeTotal += dwTime;

if (dwTime < paacd->dwTimeShortest)
{
paacd->dwTimeShortest = dwTime;
paacd->dwShortestConvert = paacd->cTotalConverts;
}

if (dwTime > paacd->dwTimeLongest)
{
paacd->dwTimeLongest = dwTime;
paacd->dwLongestConvert = paacd->cTotalConverts;
}

paacd->cTotalConverts++;


AppDlgYield(hdlg);
if (gfCancelConvert)
goto aacc_Error;

//
//
//
dw = pash->cbDstLengthUsed;
if (0L == dw)
{
pash->cbDstLengthUsed = 0L;

//
// BUGBUG BOBSTER
// What if the last conversion ate up some of the source bytes?
// It's possible - the codec could cache some of the data
// without actually converting it, right? The pbSrc pointer
// might have to be incremented, and cbSrcLength might have to
// to be decreased by cbSrcLengthUsed. This probably wouldn't
// happen with most of our codecs though...?
//
mmr = acmStreamConvert(paacd->has,
&paacd->ash,
ACM_STREAMCONVERTF_END);

if (MMSYSERR_NOERROR == mmr)
{
while (0 == (ACMSTREAMHEADER_STATUSF_DONE & ((AACONVERTDESC volatile *)paacd)->ash.fdwStatus))
;
}

dw = pash->cbDstLengthUsed;
if (0L == dw)
break;
}

if (mmioWrite(paacd->hmmioDst, paacd->pbDst, dw) != (LONG)dw)
goto aacc_Error;

//
//
//
dw = (cbRead - pash->cbSrcLengthUsed);
if (0L != dw)
{
mmioSeek(paacd->hmmioSrc, -(LONG)dw, SEEK_CUR);
}

dwCurrent += pash->cbSrcLengthUsed;

if (0L == pash->cbDstLengthUsed)
break;
}

if (hdlg)
EndConvert(hdlg, !gfCancelConvert, paacd);

return (!gfCancelConvert);


aacc_Error:

if (hdlg)
EndConvert(hdlg, FALSE, paacd);
return (FALSE);
} // AcmAppConvertConvert()


//--------------------------------------------------------------------------;
//
// BOOL AcmAppConvertDlgProc
//
// Description:
//
//
// Arguments:
// HWND hdlg:
//
// UINT uMsg:
//
// WPARAM wParam:
//
// LPARAM lParam:
//
// Return (BOOL):
//
//
//--------------------------------------------------------------------------;

BOOL FNEXPORT AcmAppConvertDlgProc
(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
PAACONVERTDESC paacd;
UINT uId;

paacd = (PAACONVERTDESC)(UINT)GetWindowLong(hwnd, DWL_USER);

switch (uMsg)
{
case WM_INITDIALOG:
paacd = (PAACONVERTDESC)(UINT)lParam;

SetWindowLong(hwnd, DWL_USER, lParam);

SetWindowFont(GetDlgItem(hwnd, IDD_AACONVERT_TXT_INFILEPATH), ghfontApp, FALSE);
SetWindowFont(GetDlgItem(hwnd, IDD_AACONVERT_TXT_OUTFILEPATH), ghfontApp, FALSE);
SetWindowFont(GetDlgItem(hwnd, IDD_AACONVERT_TXT_STATUS), ghfontApp, FALSE);

SetDlgItemText(hwnd, IDD_AACONVERT_TXT_INFILEPATH, paacd->szFilePathSrc);
SetDlgItemText(hwnd, IDD_AACONVERT_TXT_OUTFILEPATH, paacd->szFilePathDst);

BeginConvert(hwnd, paacd);
return (TRUE);


case WM_CONVERT_BEGIN:
gfCancelConvert = FALSE;
if (AcmAppConvertBegin(hwnd, paacd))
{
AcmAppConvertConvert(hwnd, paacd);
}
else
{
EndConvert(hwnd, FALSE, paacd);
}
break;


case WM_CONVERT_END:
AcmAppConvertEnd(hwnd, paacd);
EndDialog(hwnd, !gfCancelConvert);
break;


case WM_COMMAND:
uId = GET_WM_COMMAND_ID(wParam, lParam);
if (IDCANCEL == uId)
{
gfCancelConvert = TRUE;
}
break;
}

return (FALSE);
} // AcmAppConvertDlgProc()


//--------------------------------------------------------------------------;
//
// BOOL AcmAppMultiThreadConvert
//
// Description:
//
//
// Arguments:
// HWND hdlg:
//
// UINT uMsg:
//
// WPARAM wParam:
//
// LPARAM lParam:
//
// Return (BOOL):
//
//
//--------------------------------------------------------------------------;

LONG AcmAppMultiThreadConvert
(
PAACONVERTDESC paacd
)
{
if (AcmAppConvertBegin(NULL, paacd))
{
AcmAppConvertConvert(NULL, paacd);
}

AcmAppConvertEnd(NULL, paacd);

AcmAppOpenInstance(NULL, paacd->szFilePathDst, FALSE);

//
// clean up...
//
if (NULL != paacd->pwfxDst)
{
GlobalFreePtr(paacd->pwfxDst);
paacd->pwfxDst = NULL;
}

if (NULL != paacd->pwfltr)
{
GlobalFreePtr(paacd->pwfltr);
paacd->pwfltr = NULL;
}

paacd->pwfxSrc = NULL;

LocalFree((HLOCAL)paacd);

return (TRUE);

} // AcmAppMultiThreadConvert()



//==========================================================================;
//
//
//
//
//==========================================================================;

//--------------------------------------------------------------------------;
//
// BOOL AcmAppFileConvert
//
// Description:
//
//
// Arguments:
// HWND hwnd:
//
// PACMAPPFILEDESC paafd:
//
// Return (BOOL):
//
//--------------------------------------------------------------------------;

BOOL FNGLOBAL AcmAppFileConvert
(
HWND hwnd,
PACMAPPFILEDESC paafd
)
{
BOOL f;
DWORD nAvgBytesPerSec;
DWORD nBlockAlign;
DWORD dwTimeAverage;
PAACONVERTDESC paacd;

paacd = (PAACONVERTDESC)LocalAlloc(LPTR, sizeof(*paacd));
if (NULL == paacd)
{
return (FALSE);
}


//
//
//
paacd->hmmioSrc = NULL;
paacd->hmmioDst = NULL;

//
// default to 1 second per convert buffer..
//
paacd->uBufferTimePerConvert = 1000;

paacd->dwSrcSamples = paafd->dwDataSamples;


//
// compute source bytes to read (round down to nearest block for
// one second of data)
//
nAvgBytesPerSec = paafd->pwfx->nAvgBytesPerSec;
nBlockAlign = paafd->pwfx->nBlockAlign;
paacd->cbSrcReadSize = nAvgBytesPerSec - (nAvgBytesPerSec % nBlockAlign);

paacd->cbDstBufSize = 0L;
paacd->fdwOpen = 0L;

lstrcpy(paacd->szFilePathSrc, paafd->szFilePath);
paacd->pwfxSrc = paafd->pwfx;
paacd->pbSrc = NULL;

paacd->cbSrcData = paafd->dwDataBytes;

lstrcpy(paacd->szFilePathDst, gszLastSaveFile);
paacd->pwfxDst = NULL;
paacd->pbDst = NULL;

paacd->fApplyFilter = FALSE;
paacd->pwfltr = NULL;


paacd->cTotalConverts = 0L;
paacd->dwTimeTotal = 0L;
paacd->dwTimeShortest = (DWORD)-1L;
paacd->dwShortestConvert = (DWORD)-1L;
paacd->dwTimeLongest = 0L;
paacd->dwLongestConvert = (DWORD)-1L;

//
//
//
f = DialogBoxParam(ghinst,
DLG_AACHOOSER,
hwnd,
AcmAppDlgProcChooser,
(LPARAM)(UINT)paacd);
if (f)
{
lstrcpy(gszLastSaveFile, paacd->szFilePathDst);

//
//
//
f = DialogBoxParam(ghinst,
DLG_AACONVERT,
hwnd,
AcmAppConvertDlgProc,
(LPARAM)(UINT)paacd);
if (!f)
{
AppMsgBox(hwnd, MB_OK | MB_ICONEXCLAMATION,
TEXT("Conversion aborted--destination file is corrupt!"));
}


if (paacd->cTotalConverts > 1)
{
dwTimeAverage = paacd->dwTimeTotal;
dwTimeAverage -= paacd->dwTimeShortest;

dwTimeAverage /= (paacd->cTotalConverts - 1);
}
else
{
dwTimeAverage = paacd->dwTimeTotal;
}

AppMsgBox(hwnd, MB_OK | MB_ICONEXCLAMATION,
TEXT("Conversion Statistics:\n\nTotal Time:\t%lu ms\nTotal Converts:\t%lu\nShortest Time:\t%lu ms (on %lu)\nLongest Time:\t%lu ms (on %lu)\n\nAverage Time:\t%lu ms"),
paacd->dwTimeTotal,
paacd->cTotalConverts,
paacd->dwTimeShortest,
paacd->dwShortestConvert,
paacd->dwTimeLongest,
paacd->dwLongestConvert,
dwTimeAverage);

if (f)
{
AcmAppOpenInstance(hwnd, paacd->szFilePathDst, FALSE);
}
}


//
//
//
if (NULL != paacd->pwfxDst)
{
GlobalFreePtr(paacd->pwfxDst);
paacd->pwfxDst = NULL;
}

if (NULL != paacd->pwfltr)
{
GlobalFreePtr(paacd->pwfltr);
paacd->pwfltr = NULL;
}

paacd->pwfxSrc = NULL;


LocalFree((HLOCAL)paacd);

return (f);
} // AcmAppFileConvert()


//--------------------------------------------------------------------------;
//
// BOOL AcmAppMultiThreadedCallback
//
// Description:
//
//
// Arguments:
// HACMDRIVERID hadid:
//
// DWORD dwInstance:
//
// DWORD fdwSupport:
//
// Return (BOOL):
//
//
//--------------------------------------------------------------------------;

BOOL FNEXPORT AcmAppMultiThreadedCallback
(
HACMDRIVERID hadid,
LPACMFORMATDETAILS pafd,
DWORD dwInstance,
DWORD fdwSupport
)
{
DWORD nAvgBytesPerSec;
DWORD nBlockAlign;
PAACONVERTDESC paacd;
PACMAPPFILEDESC paafd;
#ifdef WIN32
int i = -1;
ACMFORMATTAGDETAILS aftd;
MMRESULT mmr;
HANDLE hThrd;
LONG lThreadId;
#endif

//
// I have sent myself (PACMAPPFILEDESC)paafd for the source
// file. I need to copy the relevent information from this structure
// into a (PAACONVERTDESC)paacd and send it off to the thread created
// below.
//
paafd = (PACMAPPFILEDESC)dwInstance;

paacd = (PAACONVERTDESC)LocalAlloc(LPTR, sizeof(*paacd));
if (NULL == paacd)
{
return (FALSE);
}

//
//
//
paacd->hmmioSrc = NULL;
paacd->hmmioDst = NULL;

//
// default to 1 second per convert buffer..
//
paacd->uBufferTimePerConvert = 1000;

paacd->dwSrcSamples = paafd->dwDataSamples;


//
// compute source bytes to read (round down to nearest block for
// one second of data)
//
nAvgBytesPerSec = paafd->pwfx->nAvgBytesPerSec;
nBlockAlign = paafd->pwfx->nBlockAlign;
paacd->cbSrcReadSize = nAvgBytesPerSec - (nAvgBytesPerSec % nBlockAlign);

paacd->cbDstBufSize = 0L;
paacd->fdwOpen = ACM_STREAMOPENF_NONREALTIME;

lstrcpy(paacd->szFilePathSrc, paafd->szFilePath);
paacd->pwfxSrc = paafd->pwfx;
paacd->pbSrc = NULL;

paacd->cbSrcData = paafd->dwDataBytes;



paacd->pwfxDst = (LPWAVEFORMATEX)GlobalAllocPtr(GHND, SIZEOF_WAVEFORMATEX(pafd->pwfx));
if (NULL == paacd->pwfxDst)
{
return (FALSE);
}
_fmemcpy(paacd->pwfxDst,pafd->pwfx,SIZEOF_WAVEFORMATEX(pafd->pwfx));
paacd->pbDst = NULL;

paacd->fApplyFilter = FALSE;
paacd->pwfltr = NULL;


paacd->cTotalConverts = 0L;
paacd->dwTimeTotal = 0L;
paacd->dwTimeShortest = (DWORD)-1L;
paacd->dwShortestConvert = (DWORD)-1L;
paacd->dwTimeLongest = 0L;
paacd->dwLongestConvert = (DWORD)-1L;


//
// Under Win32 I can spawn a thread for each conversion.
// Under Win16 I have to wait for each of the conversions to complete.
//
#ifdef WIN32
//
// Remove file extension and add extra info
//
lstrcpy(paacd->szFilePathDst, paafd->szFilePath);

while ((paacd->szFilePathDst[++i] != '.') &&
(paacd->szFilePathDst[i] != '('));
paacd->szFilePathDst[i] = '\0';

//
// initialize all unused members of the ACMFORMATTAGDETAILS
// structure to zero
//
memset(&aftd, 0, sizeof(aftd));

//
// fill in the required members of the ACMFORMATTAGDETAILS
// structure for the ACM_FORMATTAGDETAILSF_FORMATTAG query
//
aftd.cbStruct = sizeof(aftd);
aftd.dwFormatTag = pafd->pwfx->wFormatTag;

mmr = acmFormatTagDetails(NULL,
&aftd,
ACM_FORMATTAGDETAILSF_FORMATTAG);
if (MMSYSERR_NOERROR != mmr)
{
AppMsgBox(NULL, MB_OK | MB_ICONEXCLAMATION,
TEXT("Conversion aborted--could not construct filename"));
}

//
// Construct a long filename based on the format type
//
wsprintf(paacd->szFilePathDst, "%s(%s %s).wav",
(LPCTSTR)paacd->szFilePathDst,
(LPCTSTR)aftd.szFormatTag,
(LPCTSTR)pafd->szFormat);

hThrd = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE)AcmAppMultiThreadConvert,
(LPVOID)paacd,
0,
(LPDWORD)&lThreadId );

if (!hThrd)
{
AppMsgBox(NULL, MB_OK | MB_ICONEXCLAMATION,
TEXT("Conversion aborted--failure doing conversion"));
}

//
// Since I don't use this handle anywhere, I will close it
//
CloseHandle(hThrd);
#else
//
// Don't want to make too much trouble for the Win16 version.
//
wsprintf(paacd->szFilePathDst, "%d%c%d%d.wav",
pafd->pwfx->wFormatTag,
(2 == pafd->pwfx->nChannels) ? 's' : 'm',
(UINT)(pafd->pwfx->nAvgBytesPerSec * 8 /
pafd->pwfx->nSamplesPerSec /
pafd->pwfx->nChannels),
pafd->pwfx->nSamplesPerSec/1000);

AcmAppMultiThreadConvert(paacd);
#endif

//
// return TRUE to continue with enumeration
//
return (TRUE);
} // AcmAppMultiThreadedCallback()


//--------------------------------------------------------------------------;
//
// BOOL AcmAppMultiThreadedConvertAll
//
// Description:
//
//
// Arguments:
// HWND hwnd:
//
// PACMAPPFILEDESC paafd:
//
// Return (BOOL):
//
//--------------------------------------------------------------------------;

BOOL FNGLOBAL AcmAppMultiThreadedConvertAll
(
HWND hwnd,
PACMAPPFILEDESC paafd
)
{
MMRESULT mmr;
ACMFORMATDETAILS afd;
DWORD cbwfx;


//
// Call acmFormatEnum to enumerated all formats that the source format
// can convert to.
//
// Use the wave format for the enumeration
//

mmr = acmMetrics(NULL, ACM_METRIC_MAX_SIZE_FORMAT, &cbwfx);
if (MMSYSERR_NOERROR != mmr)
{
AppMsgBox(hwnd, MB_OK | MB_ICONEXCLAMATION,
TEXT("AcmAppMultiThreadedConvertAll() acmMetrics failed mmr=%u!"), mmr);
return (FALSE);
}

memset(&afd, 0, sizeof(afd));
afd.cbStruct = sizeof(afd);
afd.dwFormatTag = WAVE_FORMAT_UNKNOWN;

afd.pwfx = (LPWAVEFORMATEX)GlobalAllocPtr(GHND, (UINT)cbwfx);
if (NULL == afd.pwfx)
{
AppMsgBox(hwnd, MB_OK | MB_ICONEXCLAMATION,
TEXT("AcmAppMultiThreadedConvertAll() LocalAlloc failed!"));
return (FALSE);
}

_fmemcpy(afd.pwfx, paafd->pwfx, SIZEOF_WAVEFORMATEX(paafd->pwfx));
afd.cbwfx = cbwfx;


mmr = acmFormatEnum(NULL, &afd, AcmAppMultiThreadedCallback, (DWORD)(LPVOID)paafd,
ACM_FORMATENUMF_CONVERT);
if (MMSYSERR_NOERROR != mmr)
{
AppMsgBox(hwnd, MB_OK | MB_ICONEXCLAMATION,
TEXT("AcmAppMultiThreadedConvertAll() acmFormatEnum failed mmr=%u!"), mmr);
return (FALSE);
}

GlobalFreePtr(afd.pwfx);

return TRUE;

} // AcmAppMultiThreadedConvertAll()