1713 lines
51 KiB
C
1713 lines
51 KiB
C
/* (C) Copyright Microsoft Corporation 1991-1994. All Rights Reserved */
|
||
/* edit.c
|
||
*
|
||
* Editing operations and special effects.
|
||
*/
|
||
|
||
/* Revision History.
|
||
* 4/ Feb/91 LaurieGr (AKA LKG) Ported to WIN32 / WIN16 common code
|
||
* 14/Feb/94 LaurieGr merged Motown and Daytona versions
|
||
*/
|
||
|
||
#include "nocrap.h"
|
||
#include <windows.h>
|
||
#include <windowsx.h>
|
||
#include <commdlg.h>
|
||
#include <mmsystem.h>
|
||
#include <mmreg.h>
|
||
#include <string.h>
|
||
|
||
#define INCLUDE_OLESTUBS
|
||
#include "SoundRec.h"
|
||
#include "srecids.h"
|
||
|
||
/* constants */
|
||
#define CHVOL_INCDELTAVOLUME 25 // ChangeVolume: % to inc volume by
|
||
#define CHVOL_DECDELTAVOLUME 20 // ChangeVolume: % to dec volume by
|
||
|
||
#define ECHO_VOLUME 25 // AddEcho: % to multiply echo samples
|
||
#define ECHO_DELAY 150 // AddEcho: millisec delay for echo
|
||
#define WAVEBUFSIZE 400 // IncreasePitch, DecreasePitch
|
||
#define FINDWAVE_PICKYNESS 5 // how picky is FindWave?
|
||
|
||
extern char aszInitFile[]; // soundrec.c
|
||
|
||
static SZCODE aszSamplesFormat[] = TEXT("%d%c%02d");
|
||
static SZCODE aszSamplesNoZeroFormat[] = TEXT("%c%02d");
|
||
|
||
/* InsertFile(void)
|
||
*
|
||
* Prompt for the name of a WAVE file to insert at the current position.
|
||
*/
|
||
void FAR PASCAL
|
||
InsertFile(BOOL fPaste)
|
||
{
|
||
TCHAR achFileName[_MAX_PATH]; // name of file to insert
|
||
WAVEFORMATEX* pwfInsert=NULL; // WAVE file format of given file
|
||
DWORD cb; // size of WAVEFORMATEX
|
||
HPBYTE pInsertSamples = NULL; // samples from file to insert
|
||
long lInsertSamples; // number of samples in given file
|
||
long lSamplesToInsert;// no. samp. at samp. rate of cur. file
|
||
TCHAR ach[80]; // buffer for string loading
|
||
HCURSOR hcurPrev = NULL; // cursor before hourglass
|
||
HPBYTE pchSrc; // pointer into source wave buffer
|
||
short * piSrc; // 16-bit pointer
|
||
HPBYTE pchDst; // pointer into destination wave buffer
|
||
short * piDst; // 16-bit pointer
|
||
long lSamplesDst; // bytes to copy into destination buffer
|
||
long lDDA; // used to implement DDA algorithm
|
||
HMMIO hmmio; // Handle to open file to read from
|
||
|
||
BOOL fDirty = TRUE; // Is the buffer Dirty?
|
||
|
||
BOOL fStereoIn;
|
||
BOOL fStereoOut;
|
||
BOOL fEightIn;
|
||
BOOL fEightOut;
|
||
BOOL fEditWave = FALSE;
|
||
int iTemp;
|
||
int iTemp2;
|
||
OPENFILENAME ofn;
|
||
|
||
#ifdef DOWECARE
|
||
/* HACK from "server.c" to read objects without CF_WAVE */
|
||
extern WORD cfNative;
|
||
#endif
|
||
|
||
if (glWaveSamplesValid > 0 && !IsWaveFormatPCM(gpWaveFormat))
|
||
return;
|
||
|
||
if (fPaste) {
|
||
MMIOINFO mmioinfo;
|
||
HANDLE h;
|
||
|
||
BeginWaveEdit();
|
||
|
||
if (!OpenClipboard(ghwndApp))
|
||
return;
|
||
|
||
LoadString(ghInst, IDS_CLIPBOARD, achFileName, SIZEOF(achFileName));
|
||
|
||
h = GetClipboardData(CF_WAVE);
|
||
#ifdef DOWECARE
|
||
if (!h) h = GetClipboardData(cfNative);
|
||
#endif
|
||
if (h)
|
||
{
|
||
mmioinfo.fccIOProc = FOURCC_MEM;
|
||
mmioinfo.pIOProc = NULL;
|
||
mmioinfo.pchBuffer = GlobalLock(h);
|
||
mmioinfo.cchBuffer = (long)GlobalSize(h); // initial size
|
||
mmioinfo.adwInfo[0] = 0; // grow by this much
|
||
hmmio = mmioOpen(NULL, &mmioinfo, MMIO_READ);
|
||
}
|
||
else
|
||
{
|
||
hmmio = NULL;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
BOOL f;
|
||
|
||
achFileName[0] = 0;
|
||
|
||
/* prompt user for file to open */
|
||
LoadString(ghInst, IDS_INSERTFILE, ach, SIZEOF(ach));
|
||
ofn.lStructSize = sizeof(OPENFILENAME);
|
||
ofn.hwndOwner = ghwndApp;
|
||
ofn.hInstance = NULL;
|
||
ofn.lpstrFilter = aszFilter;
|
||
ofn.lpstrCustomFilter = NULL;
|
||
ofn.nMaxCustFilter = 0;
|
||
ofn.nFilterIndex = 1;
|
||
ofn.lpstrFile = achFileName;
|
||
ofn.nMaxFile = SIZEOF(achFileName);
|
||
ofn.lpstrFileTitle = NULL;
|
||
ofn.nMaxFileTitle = 0;
|
||
ofn.lpstrInitialDir = NULL;
|
||
ofn.lpstrTitle = ach;
|
||
ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY;
|
||
ofn.nFileOffset = 0;
|
||
ofn.nFileExtension = 0;
|
||
ofn.lpstrDefExt = NULL;
|
||
ofn.lCustData = 0;
|
||
ofn.lpfnHook = NULL;
|
||
ofn.lpTemplateName = NULL;
|
||
f = GetOpenFileName(&ofn); // get the filename
|
||
|
||
// did we succeed or not?
|
||
if (!f)
|
||
goto RETURN_ERROR;
|
||
|
||
/* read the WAVE file */
|
||
hmmio = mmioOpen(achFileName, NULL, MMIO_READ | MMIO_ALLOCBUF);
|
||
}
|
||
|
||
if (hmmio != NULL)
|
||
{
|
||
MMRESULT mmr;
|
||
|
||
//
|
||
// show hourglass cursor
|
||
//
|
||
hcurPrev = SetCursor(LoadCursor(NULL, IDC_WAIT));
|
||
|
||
//
|
||
// read the WAVE file
|
||
//
|
||
mmr = ReadWaveFile( hmmio
|
||
, &pwfInsert
|
||
, &cb
|
||
, &pInsertSamples
|
||
, &lInsertSamples
|
||
, achFileName
|
||
, FALSE );
|
||
|
||
mmioClose(hmmio, 0);
|
||
|
||
if (mmr != MMSYSERR_NOERROR)
|
||
goto RETURN_ERROR;
|
||
|
||
if (lInsertSamples == 0)
|
||
goto RETURN_SUCCESS;
|
||
|
||
if (pInsertSamples == NULL)
|
||
goto RETURN_ERROR;
|
||
|
||
if (glWaveSamplesValid > 0 && !IsWaveFormatPCM(pwfInsert))
|
||
{
|
||
|
||
ErrorResBox( ghwndApp
|
||
, ghInst
|
||
, MB_ICONEXCLAMATION | MB_OK
|
||
, IDS_APPTITLE
|
||
, fPaste ? IDS_CANTPASTE : IDS_NOTASUPPORTEDFILE
|
||
, (LPTSTR) achFileName
|
||
);
|
||
goto RETURN_ERROR;
|
||
}
|
||
} else {
|
||
ErrorResBox(ghwndApp, ghInst, MB_ICONEXCLAMATION | MB_OK,
|
||
IDS_APPTITLE, IDS_ERROROPEN, (LPTSTR) achFileName);
|
||
goto RETURN_ERROR;
|
||
}
|
||
|
||
//jyg:moved
|
||
// BeginWaveEdit();
|
||
fEditWave = TRUE;
|
||
|
||
//
|
||
// if the current file is empty, treat the insert like a open
|
||
//
|
||
if (glWaveSamplesValid == 0)
|
||
{
|
||
DestroyWave();
|
||
|
||
gpWaveSamples = pInsertSamples;
|
||
glWaveSamples = lInsertSamples;
|
||
glWaveSamplesValid = lInsertSamples;
|
||
gpWaveFormat = pwfInsert;
|
||
gcbWaveFormat = cb;
|
||
|
||
pInsertSamples = NULL;
|
||
pwfInsert = NULL;
|
||
|
||
goto RETURN_SUCCESS;
|
||
}
|
||
|
||
fStereoIn = pwfInsert->nChannels != 1;
|
||
fStereoOut = gpWaveFormat->nChannels != 1;
|
||
|
||
fEightIn = ((LPWAVEFORMATEX)pwfInsert)->wBitsPerSample == 8;
|
||
fEightOut = ((LPWAVEFORMATEX)gpWaveFormat)->wBitsPerSample == 8;
|
||
|
||
/* figure out how many bytes need to be inserted */
|
||
lSamplesToInsert = MulDiv(lInsertSamples, gpWaveFormat->nSamplesPerSec,
|
||
pwfInsert->nSamplesPerSec);
|
||
#ifdef DEBUG
|
||
DPF(TEXT("insert %ld samples, converting from %ld Hz to %ld Hz\n"),
|
||
lInsertSamples, pwfInsert->nSamplesPerSec,
|
||
gpWaveFormat->nSamplesPerSec);
|
||
DPF(TEXT("so %ld samples need to be inserted at position %ld\n"),
|
||
lSamplesToInsert, glWavePosition);
|
||
#endif
|
||
|
||
/* reallocate the WAVE buffer to be big enough */
|
||
if (!AllocWaveBuffer(glWaveSamplesValid + lSamplesToInsert, TRUE, TRUE))
|
||
goto RETURN_ERROR;
|
||
glWaveSamplesValid += lSamplesToInsert;
|
||
|
||
/* create a "gap" in the WAVE buffer to go from this:
|
||
* |---glWavePosition---|-rest-of-buffer-|
|
||
* to this:
|
||
* |---glWavePosition---|----lSamplesToInsert----|-rest-of-buffer-|
|
||
* where <glWaveSamplesValid> is the size of the buffer
|
||
* *after* reallocation
|
||
*/
|
||
memmove( gpWaveSamples + wfSamplesToBytes(gpWaveFormat, glWavePosition + lSamplesToInsert)
|
||
, gpWaveSamples + wfSamplesToBytes(gpWaveFormat, glWavePosition)
|
||
, wfSamplesToBytes(gpWaveFormat, glWaveSamplesValid - (glWavePosition + lSamplesToInsert))
|
||
);
|
||
|
||
/* copy the read-in WAVE file into the "gap" */
|
||
pchDst = gpWaveSamples + wfSamplesToBytes(gpWaveFormat,glWavePosition);
|
||
piDst = (short *) pchDst;
|
||
|
||
lSamplesDst = lSamplesToInsert;
|
||
pchSrc = pInsertSamples;
|
||
piSrc = (short *) pchSrc;
|
||
|
||
lDDA = -((LONG)gpWaveFormat->nSamplesPerSec);
|
||
while (lSamplesDst > 0)
|
||
{
|
||
/* get a sample, convert to right format */
|
||
if (fEightIn) {
|
||
iTemp = *((BYTE *) pchSrc);
|
||
if (fStereoIn) {
|
||
iTemp2 = (unsigned char) *(pchSrc+1);
|
||
if (!fStereoOut) {
|
||
iTemp = (iTemp + iTemp2) / 2;
|
||
}
|
||
}
|
||
else
|
||
iTemp2 = iTemp;
|
||
|
||
if (!fEightOut) {
|
||
iTemp = (iTemp - 128) << 8;
|
||
iTemp2 = (iTemp2 - 128) << 8;
|
||
}
|
||
} else {
|
||
iTemp = *piSrc;
|
||
if (fStereoIn) {
|
||
iTemp2 = *(piSrc+1);
|
||
if (!fStereoOut) {
|
||
iTemp = (int) ( ( ((long)iTemp) + ((long) iTemp2)
|
||
) / 2);
|
||
}
|
||
}
|
||
else
|
||
iTemp2 = iTemp;
|
||
|
||
if (fEightOut) {
|
||
iTemp = (iTemp >> 8) + 128;
|
||
iTemp2 = (iTemp2 >> 8) + 128;
|
||
}
|
||
}
|
||
|
||
/* Output a sample */
|
||
if (fEightOut)
|
||
{ // Cast on lvalue eliminated -- LKG
|
||
*(BYTE *) pchDst = (BYTE) iTemp;
|
||
pchDst = (BYTE *)pchDst + 1;
|
||
}
|
||
else
|
||
*piDst++ = (short)iTemp;
|
||
if (fStereoOut) {
|
||
if (fEightOut)
|
||
{ // Cast on lvalue eliminated -- LKG
|
||
*(BYTE *) pchDst = (BYTE) iTemp2;
|
||
pchDst = (BYTE *)pchDst + 1;
|
||
}
|
||
else
|
||
*piDst++ = (short)iTemp2;
|
||
}
|
||
lSamplesDst--;
|
||
|
||
/* increment <pchSrc> at the correct rate so that the
|
||
* sampling rate of the input file is converted to match
|
||
* the sampling rate of the current file
|
||
*/
|
||
lDDA += pwfInsert->nSamplesPerSec;
|
||
while (lDDA >= 0) {
|
||
lDDA -= gpWaveFormat->nSamplesPerSec;
|
||
if (fEightIn)
|
||
pchSrc++;
|
||
else
|
||
piSrc++;
|
||
if (fStereoIn) {
|
||
if (fEightIn)
|
||
pchSrc++;
|
||
else
|
||
piSrc++;
|
||
}
|
||
}
|
||
}
|
||
#ifdef DEBUG
|
||
if (!fEightIn)
|
||
pchSrc = (HPBYTE) piSrc;
|
||
DPF(TEXT("copied %ld bytes from insertion buffer\n"), (long) (pchSrc - pInsertSamples));
|
||
#endif
|
||
|
||
goto RETURN_SUCCESS;
|
||
|
||
RETURN_ERROR: // do error exit without error message
|
||
fDirty = FALSE;
|
||
|
||
RETURN_SUCCESS: // normal exit
|
||
|
||
if (fPaste)
|
||
CloseClipboard();
|
||
|
||
if (pInsertSamples != NULL)
|
||
GlobalFreePtr(pInsertSamples);
|
||
|
||
if (pwfInsert != NULL)
|
||
GlobalFreePtr(pwfInsert);
|
||
|
||
if (hcurPrev != NULL)
|
||
SetCursor(hcurPrev);
|
||
|
||
if (fEditWave == TRUE)
|
||
EndWaveEdit(fDirty);
|
||
|
||
/* update the display */
|
||
UpdateDisplay(TRUE);
|
||
}
|
||
|
||
|
||
/* MixWithFile(void)
|
||
*
|
||
* Prompt for the name of a WAVE file to mix with the audio starting at
|
||
* the current location.
|
||
*/
|
||
void FAR PASCAL
|
||
MixWithFile(BOOL fPaste)
|
||
{
|
||
TCHAR achFileName[_MAX_PATH]; // name of file to mix with
|
||
WAVEFORMATEX* pwfMix=NULL; // WAVE file format of given file
|
||
UINT cb;
|
||
HPBYTE pMixSamples = NULL; // samples from file to mix with
|
||
long lMixSamples; // number of samples in given file
|
||
long lSamplesToMix; // no. Samples at samp. rate. of cur. file
|
||
long lSamplesToAdd; // no. Samples to add in
|
||
TCHAR ach[80]; // buffer for string loading
|
||
HCURSOR hcurPrev = NULL; // cursor before hourglass
|
||
HPBYTE pchSrc; // pointer into source wave buffer
|
||
HPBYTE pchDst; // pointer into destination wave buffer
|
||
short * piSrc; // pointer into source wave buffer
|
||
short * piDst; // pointer into destination wave buffer
|
||
long lSamplesDst; // Samples to copy into destination buffer
|
||
long lDDA; // used to implement DDA algorithm
|
||
int iSample; // value of a waveform sample
|
||
long lSample; // value of a waveform sample
|
||
HMMIO hmmio;
|
||
|
||
BOOL fDirty = TRUE;
|
||
|
||
BOOL fStereoIn;
|
||
BOOL fStereoOut;
|
||
BOOL fEightIn;
|
||
BOOL fEightOut;
|
||
BOOL fEditWave = FALSE;
|
||
int iTemp;
|
||
int iTemp2;
|
||
OPENFILENAME ofn;
|
||
|
||
#ifdef DOWECARE
|
||
/* HACK from "server.c" to read objects without CF_WAVE */
|
||
extern WORD cfNative;
|
||
#endif
|
||
|
||
if (glWaveSamplesValid > 0 && !IsWaveFormatPCM(gpWaveFormat))
|
||
return;
|
||
|
||
if (fPaste) {
|
||
MMIOINFO mmioinfo;
|
||
HANDLE h;
|
||
|
||
BeginWaveEdit();
|
||
if (!OpenClipboard(ghwndApp))
|
||
return;
|
||
|
||
LoadString(ghInst, IDS_CLIPBOARD, achFileName, SIZEOF(achFileName));
|
||
|
||
h = GetClipboardData(CF_WAVE);
|
||
#ifdef DOWECARE
|
||
if (!h) h = GetClipboardData(cfNative);
|
||
#endif
|
||
if (h) {
|
||
mmioinfo.fccIOProc = FOURCC_MEM;
|
||
mmioinfo.pIOProc = NULL;
|
||
mmioinfo.pchBuffer = GlobalLock(h);
|
||
mmioinfo.cchBuffer = (long)GlobalSize(h); // initial size
|
||
mmioinfo.adwInfo[0] = 0; // grow by this much
|
||
hmmio = mmioOpen(NULL, &mmioinfo, MMIO_READ);
|
||
}
|
||
else {
|
||
hmmio = NULL;
|
||
}
|
||
}
|
||
else {
|
||
BOOL f;
|
||
|
||
achFileName[0] = 0;
|
||
|
||
/* prompt user for file to open */
|
||
LoadString(ghInst, IDS_MIXWITHFILE, ach, SIZEOF(ach));
|
||
ofn.lStructSize = sizeof(OPENFILENAME);
|
||
ofn.hwndOwner = ghwndApp;
|
||
ofn.hInstance = NULL;
|
||
ofn.lpstrFilter = aszFilter;
|
||
ofn.lpstrCustomFilter = NULL;
|
||
ofn.nMaxCustFilter = 0;
|
||
ofn.nFilterIndex = 1;
|
||
ofn.lpstrFile = achFileName;
|
||
ofn.nMaxFile = SIZEOF(achFileName);
|
||
ofn.lpstrFileTitle = NULL;
|
||
ofn.nMaxFileTitle = 0;
|
||
ofn.lpstrInitialDir = NULL;
|
||
ofn.lpstrTitle = ach;
|
||
ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY;
|
||
ofn.nFileOffset = 0;
|
||
ofn.nFileExtension = 0;
|
||
ofn.lpstrDefExt = NULL;
|
||
ofn.lCustData = 0;
|
||
ofn.lpfnHook = NULL;
|
||
ofn.lpTemplateName = NULL;
|
||
f = GetOpenFileName(&ofn); // get the filename for mixing
|
||
|
||
// see if we continue
|
||
if (!f)
|
||
goto RETURN_ERROR;
|
||
|
||
/* read the WAVE file */
|
||
hmmio = mmioOpen(achFileName, NULL, MMIO_READ | MMIO_ALLOCBUF);
|
||
}
|
||
|
||
if (hmmio != NULL)
|
||
{
|
||
MMRESULT mmr;
|
||
|
||
//
|
||
// show hourglass cursor
|
||
//
|
||
hcurPrev = SetCursor(LoadCursor(NULL, IDC_WAIT));
|
||
|
||
//
|
||
// read the WAVE file
|
||
//
|
||
mmr = ReadWaveFile( hmmio
|
||
, &pwfMix // wave format
|
||
, &cb // wave format size
|
||
, &pMixSamples // samples
|
||
, &lMixSamples // number of samples
|
||
, achFileName // file name for error
|
||
, FALSE ); // cache riff?
|
||
|
||
mmioClose(hmmio, 0);
|
||
|
||
|
||
if (mmr != MMSYSERR_NOERROR)
|
||
goto RETURN_ERROR;
|
||
|
||
if (lMixSamples == 0)
|
||
goto RETURN_SUCCESS;
|
||
|
||
if (pMixSamples == NULL)
|
||
goto RETURN_ERROR;
|
||
|
||
if (glWaveSamplesValid > 0 && !IsWaveFormatPCM(pwfMix)) {
|
||
ErrorResBox( ghwndApp
|
||
, ghInst
|
||
, MB_ICONEXCLAMATION | MB_OK
|
||
, IDS_APPTITLE
|
||
, fPaste ? IDS_CANTPASTE : IDS_NOTASUPPORTEDFILE
|
||
, (LPTSTR) achFileName
|
||
);
|
||
goto RETURN_ERROR;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
ErrorResBox(ghwndApp, ghInst, MB_ICONEXCLAMATION | MB_OK,
|
||
IDS_APPTITLE, IDS_ERROROPEN, (LPTSTR) achFileName);
|
||
goto RETURN_ERROR;
|
||
}
|
||
|
||
//jyg: moved
|
||
// BeginWaveEdit();
|
||
fEditWave = TRUE;
|
||
|
||
//
|
||
// if the current file is empty, treat the insert like a open
|
||
//
|
||
if (glWaveSamplesValid == 0)
|
||
{
|
||
DestroyWave();
|
||
|
||
gpWaveSamples = pMixSamples;
|
||
glWaveSamples = lMixSamples;
|
||
glWaveSamplesValid = lMixSamples;
|
||
gpWaveFormat = pwfMix;
|
||
gcbWaveFormat = cb;
|
||
|
||
pMixSamples = NULL;
|
||
pwfMix = NULL;
|
||
|
||
goto RETURN_SUCCESS;
|
||
}
|
||
|
||
fStereoIn = pwfMix->nChannels != 1;
|
||
fStereoOut = gpWaveFormat->nChannels != 1;
|
||
|
||
fEightIn = ((LPWAVEFORMATEX)pwfMix)->wBitsPerSample == 8;
|
||
fEightOut = ((LPWAVEFORMATEX)gpWaveFormat)->wBitsPerSample == 8;
|
||
|
||
/* figure out how many Samples need to be mixed in */
|
||
lSamplesToMix = MulDiv(lMixSamples, gpWaveFormat->nSamplesPerSec,
|
||
pwfMix->nSamplesPerSec);
|
||
lSamplesToAdd = lSamplesToMix - (glWaveSamplesValid - glWavePosition);
|
||
if (lSamplesToAdd < 0)
|
||
lSamplesToAdd = 0;
|
||
#ifdef DEBUG
|
||
DPF(TEXT("mix in %ld samples, converting from %ld Hz to %ld Hz\n"),
|
||
lMixSamples, pwfMix->nSamplesPerSec,
|
||
gpWaveFormat->nSamplesPerSec);
|
||
DPF(TEXT("so %ld Samples need to be mixed in at position %ld (add %ld)\n"),
|
||
lSamplesToMix, glWavePosition, lSamplesToAdd);
|
||
#endif
|
||
|
||
if (lSamplesToAdd > 0) {
|
||
|
||
/* mixing the specified file at the current location will
|
||
* require the current file's wave buffer to be expanded
|
||
* by <lSamplesToAdd>
|
||
*/
|
||
|
||
/* reallocate the WAVE buffer to be big enough */
|
||
if (!AllocWaveBuffer(glWaveSamplesValid + lSamplesToAdd,TRUE, TRUE))
|
||
goto RETURN_ERROR;
|
||
|
||
/* fill in the new part of the buffer with silence
|
||
*/
|
||
lSamplesDst = lSamplesToAdd;
|
||
|
||
/* If stereo, just twice as many samples
|
||
*/
|
||
if (fStereoOut)
|
||
lSamplesDst *= 2;
|
||
|
||
pchDst = gpWaveSamples + wfSamplesToBytes(gpWaveFormat,glWaveSamplesValid);
|
||
|
||
if (fEightOut) {
|
||
while (lSamplesDst-- > 0) {
|
||
// cast on lvalue eliminated
|
||
*((BYTE *) pchDst) = 128;
|
||
pchDst = (BYTE *)pchDst + 1;
|
||
}
|
||
}
|
||
else {
|
||
piDst = (short *) pchDst;
|
||
while (lSamplesDst-- > 0) {
|
||
*((short *) piDst) = 0;
|
||
piDst = (short *)piDst + 1;
|
||
}
|
||
}
|
||
glWaveSamplesValid += lSamplesToAdd;
|
||
}
|
||
|
||
/* mix the read-in WAVE file with the current file starting at the
|
||
* current position
|
||
*/
|
||
pchDst = gpWaveSamples + wfSamplesToBytes(gpWaveFormat, glWavePosition);
|
||
piDst = (short *) pchDst;
|
||
|
||
lSamplesDst = lSamplesToMix;
|
||
pchSrc = pMixSamples;
|
||
piSrc = (short *) pchSrc;
|
||
|
||
lDDA = -((LONG)gpWaveFormat->nSamplesPerSec);
|
||
while (lSamplesDst > 0)
|
||
{
|
||
/* get a sample, convert to right format */
|
||
if (fEightIn) {
|
||
iTemp = (int) (unsigned char) *pchSrc;
|
||
if (fStereoIn) {
|
||
iTemp2 = (int) (unsigned char) *(pchSrc+1);
|
||
if (!fStereoOut) {
|
||
iTemp = (iTemp + iTemp2) / 2;
|
||
}
|
||
} else
|
||
iTemp2 = iTemp;
|
||
|
||
if (!fEightOut) {
|
||
iTemp = (iTemp - 128) << 8;
|
||
iTemp2 = (iTemp2 - 128) << 8;
|
||
}
|
||
} else {
|
||
iTemp = *piSrc;
|
||
if (fStereoIn) {
|
||
iTemp2 = *(piSrc+1);
|
||
if (!fStereoOut) {
|
||
iTemp = (int) ((((long) iTemp)
|
||
+ ((long) iTemp2)) / 2);
|
||
}
|
||
} else
|
||
iTemp2 = iTemp;
|
||
|
||
if (fEightOut) {
|
||
iTemp = (iTemp >> 8) + 128;
|
||
iTemp2 = (iTemp2 >> 8) + 128;
|
||
}
|
||
}
|
||
|
||
/* Output a sample */
|
||
if (fEightOut)
|
||
{
|
||
iSample = (int) *((BYTE *) pchDst)
|
||
+ iTemp - 128;
|
||
*((BYTE *) pchDst++) = (BYTE)
|
||
(iSample < 0 ? 0 :
|
||
(iSample > 255 ? 255 : iSample));
|
||
}
|
||
else
|
||
{
|
||
lSample = (long) *((short *) piDst)
|
||
+ (long) iTemp;
|
||
*((short *) piDst++) = (int)
|
||
(lSample < -32768L
|
||
? -32768 : (lSample > 32767L
|
||
? 32767 : (short) lSample));
|
||
}
|
||
if (fStereoOut) {
|
||
if (fEightOut)
|
||
{
|
||
iSample = (int) *((BYTE *) pchDst)
|
||
+ iTemp2 - 128;
|
||
*((BYTE *) pchDst++) = (BYTE)
|
||
(iSample < 0
|
||
? 0 : (iSample > 255
|
||
? 255 : iSample));
|
||
}
|
||
else
|
||
{
|
||
lSample = (long) *((short *) piDst)
|
||
+ (long) iTemp2;
|
||
*((short *) piDst++) = (short)
|
||
(lSample < -32768L
|
||
? -32768 : (lSample > 32767L
|
||
? 32767 : (short) lSample));
|
||
}
|
||
}
|
||
lSamplesDst--;
|
||
|
||
/* increment <pchSrc> at the correct rate so that the
|
||
* sampling rate of the input file is converted to match
|
||
* the sampling rate of the current file
|
||
*/
|
||
lDDA += pwfMix->nSamplesPerSec;
|
||
while (lDDA >= 0)
|
||
{
|
||
lDDA -= gpWaveFormat->nSamplesPerSec;
|
||
if (fEightIn)
|
||
pchSrc++;
|
||
else
|
||
piSrc++;
|
||
if (fStereoIn) {
|
||
if (fEightIn)
|
||
pchSrc++;
|
||
else
|
||
piSrc++;
|
||
}
|
||
}
|
||
}
|
||
#ifdef DEBUG
|
||
if (!fEightIn)
|
||
pchSrc = (HPBYTE) piSrc;
|
||
DPF(TEXT("copied %ld bytes from mix buffer\n"),
|
||
(long) (pchSrc - pMixSamples));
|
||
#endif
|
||
|
||
goto RETURN_SUCCESS;
|
||
|
||
RETURN_ERROR: // do error exit without error message
|
||
fDirty = FALSE;
|
||
|
||
RETURN_SUCCESS: // normal exit
|
||
|
||
if (fPaste)
|
||
CloseClipboard();
|
||
|
||
if (pMixSamples != NULL)
|
||
GlobalFreePtr(pMixSamples);
|
||
|
||
if (pwfMix != NULL)
|
||
GlobalFreePtr(pwfMix);
|
||
|
||
if (hcurPrev != NULL)
|
||
SetCursor(hcurPrev);
|
||
|
||
if (fEditWave == TRUE)
|
||
EndWaveEdit(fDirty);
|
||
|
||
/* update the display */
|
||
UpdateDisplay(TRUE);
|
||
}
|
||
|
||
|
||
/* DeleteBefore()
|
||
*
|
||
* Delete samples before <glWavePosition>.
|
||
*/
|
||
void FAR PASCAL
|
||
DeleteBefore(void)
|
||
{
|
||
TCHAR ach[40];
|
||
long lTime;
|
||
int id;
|
||
|
||
if (glWavePosition == 0) // nothing to do?
|
||
return; // don't set dirty flag
|
||
|
||
BeginWaveEdit();
|
||
|
||
/* jyg - made this conditional because of rounding errors at
|
||
* the end of buffer case
|
||
*/
|
||
if (glWavePosition != glWaveSamplesValid)
|
||
glWavePosition = wfSamplesToSamples(gpWaveFormat, glWavePosition);
|
||
|
||
/* get the current wave position */
|
||
lTime = wfSamplesToTime(gpWaveFormat, glWavePosition);
|
||
if (gfLZero || ((int)(lTime/1000) != 0)) // ??? what are these casts ???
|
||
wsprintf(ach, aszSamplesFormat, (int)(lTime/1000), chDecimal, (int)((lTime/10)%100));
|
||
else
|
||
wsprintf(ach, aszSamplesNoZeroFormat, chDecimal, (int)((lTime/10)%100));
|
||
|
||
|
||
/* prompt user for permission */
|
||
|
||
id = ErrorResBox(ghwndApp, ghInst, MB_ICONEXCLAMATION | MB_OKCANCEL,
|
||
IDS_APPTITLE, IDS_DELBEFOREWARN, (LPTSTR) ach);
|
||
|
||
if (id != IDOK)
|
||
return;
|
||
|
||
/* copy the samples after <glWavePosition> to the beginning of
|
||
* the buffer
|
||
*/
|
||
memmove(gpWaveSamples,
|
||
gpWaveSamples + wfSamplesToBytes(gpWaveFormat, glWavePosition),
|
||
wfSamplesToBytes(gpWaveFormat, glWaveSamplesValid - glWavePosition));
|
||
|
||
/* reallocate the buffer to be <glWavePosition> samples smaller */
|
||
AllocWaveBuffer(glWaveSamplesValid - glWavePosition, TRUE, TRUE);
|
||
glWavePosition = 0L;
|
||
|
||
EndWaveEdit(TRUE);
|
||
|
||
/* update the display */
|
||
UpdateDisplay(TRUE);
|
||
} /* DeleteBefore */
|
||
|
||
|
||
/* DeleteAfter()
|
||
*
|
||
* Delete samples after <glWavePosition>.
|
||
*/
|
||
void FAR PASCAL
|
||
DeleteAfter(void)
|
||
{
|
||
TCHAR ach[40];
|
||
long lTime;
|
||
int id;
|
||
|
||
if (glWavePosition == glWaveSamplesValid) // nothing to do?
|
||
return; // don't set dirty flag
|
||
|
||
glWavePosition = wfSamplesToSamples(gpWaveFormat, glWavePosition);
|
||
|
||
BeginWaveEdit();
|
||
|
||
/* get the current wave position */
|
||
lTime = wfSamplesToTime(gpWaveFormat, glWavePosition);
|
||
if (gfLZero || ((int)(lTime/1000) != 0)) // ??? casts ???
|
||
wsprintf(ach, aszSamplesFormat, (int)(lTime/1000), chDecimal, (int)((lTime/10)%100));
|
||
else
|
||
wsprintf(ach, aszSamplesNoZeroFormat, chDecimal, (int)((lTime/10)%100));
|
||
|
||
/* prompt user for permission */
|
||
|
||
id = ErrorResBox(ghwndApp, ghInst, MB_ICONEXCLAMATION | MB_OKCANCEL,
|
||
IDS_APPTITLE, IDS_DELAFTERWARN, (LPTSTR) ach);
|
||
|
||
if (id != IDOK)
|
||
return;
|
||
|
||
/* reallocate the buffer to be <glWavePosition> samples in size */
|
||
AllocWaveBuffer(glWavePosition, TRUE, TRUE);
|
||
|
||
EndWaveEdit(TRUE);
|
||
|
||
/* update the display */
|
||
UpdateDisplay(TRUE);
|
||
} /* DeleteAfter */
|
||
|
||
|
||
/* ChangeVolume(fIncrease)
|
||
*
|
||
* Increase the volume (if <fIncrease> is TRUE) or decrease the volume
|
||
* (if <fIncrease> is FALSE) of samples in the wave buffer by CHVOL_DELTAVOLUME
|
||
* percent.
|
||
*/
|
||
void FAR PASCAL
|
||
ChangeVolume(BOOL fIncrease)
|
||
{
|
||
HPBYTE pch = gpWaveSamples; // ptr. into waveform buffer
|
||
long lSamples; // samples to modify
|
||
HCURSOR hcurPrev = NULL; // cursor before hourglass
|
||
int iFactor; // amount to multiply amplitude by
|
||
short * pi = (short *) gpWaveSamples;
|
||
|
||
if (glWaveSamplesValid == 0L) // nothing to do?
|
||
return; // don't set dirty flag
|
||
|
||
if (!IsWaveFormatPCM(gpWaveFormat))
|
||
return;
|
||
|
||
/* show hourglass cursor */
|
||
hcurPrev = SetCursor(LoadCursor(NULL, IDC_WAIT));
|
||
|
||
BeginWaveEdit();
|
||
|
||
/* for stereo, just twice as many samples */
|
||
lSamples = glWaveSamplesValid * gpWaveFormat->nChannels;
|
||
|
||
iFactor = 100 + (fIncrease ? CHVOL_INCDELTAVOLUME : -CHVOL_DECDELTAVOLUME);
|
||
if (((LPWAVEFORMATEX)gpWaveFormat)->wBitsPerSample == 8) {
|
||
/* 8-bit: samples 0-255 */
|
||
int iTemp;
|
||
while (lSamples-- > 0)
|
||
{
|
||
iTemp = ( ((short) *((BYTE *) pch) - 128)
|
||
* iFactor
|
||
)
|
||
/ 100 + 128;
|
||
*((BYTE *) pch++) = (BYTE)
|
||
(iTemp < 0 ? 0 : (iTemp > 255 ? 255 : iTemp));
|
||
}
|
||
} else {
|
||
/* 16-bit: samples -32768 - 32767 */
|
||
long lTemp;
|
||
while (lSamples-- > 0)
|
||
{
|
||
lTemp = (((long) *pi) * iFactor) / 100;
|
||
*(pi++) = (short) (lTemp < -32768L ? -32768 :
|
||
(lTemp > 32767L ?
|
||
32767 : (short) lTemp));
|
||
}
|
||
}
|
||
|
||
EndWaveEdit(TRUE);
|
||
|
||
if (hcurPrev != NULL)
|
||
SetCursor(hcurPrev);
|
||
|
||
/* update the display */
|
||
UpdateDisplay(TRUE);
|
||
}
|
||
|
||
|
||
/* MakeFaster()
|
||
*
|
||
* Make the sound play twice as fast.
|
||
*/
|
||
void FAR PASCAL
|
||
MakeFaster(void)
|
||
{
|
||
HPBYTE pchSrc; // pointer into source part of buffer
|
||
HPBYTE pchDst; // pointer into destination part
|
||
short * piSrc;
|
||
short * piDst;
|
||
long lSamplesDst; // samples to copy into destination buffer
|
||
HCURSOR hcurPrev = NULL; // cursor before hourglass
|
||
|
||
if (glWaveSamplesValid == 0L) // nothing to do?
|
||
return; // don't set dirty flag
|
||
|
||
if (!IsWaveFormatPCM(gpWaveFormat))
|
||
return;
|
||
|
||
/* show hourglass cursor */
|
||
hcurPrev = SetCursor(LoadCursor(NULL, IDC_WAIT));
|
||
|
||
BeginWaveEdit();
|
||
|
||
/* move the current position so it will correspond to the same point
|
||
* in the audio before and after the change-pitch operation
|
||
*/
|
||
glWavePosition /= 2L;
|
||
|
||
/* delete every other sample */
|
||
lSamplesDst = glWaveSamplesValid / 2L;
|
||
if (((LPWAVEFORMATEX)gpWaveFormat)->wBitsPerSample == 8) {
|
||
pchSrc = pchDst = gpWaveSamples;
|
||
if (gpWaveFormat->nChannels == 1) {
|
||
while (lSamplesDst-- > 0)
|
||
{
|
||
*pchDst++ = *pchSrc++;
|
||
pchSrc++;
|
||
}
|
||
} else {
|
||
while (lSamplesDst-- > 0)
|
||
{
|
||
*pchDst++ = *pchSrc++;
|
||
*pchDst++ = *pchSrc++;
|
||
pchSrc++;
|
||
pchSrc++;
|
||
}
|
||
}
|
||
} else {
|
||
piSrc = piDst = (short *) gpWaveSamples;
|
||
if (gpWaveFormat->nChannels == 1) {
|
||
while (lSamplesDst-- > 0)
|
||
{
|
||
*piDst++ = *piSrc++;
|
||
piSrc++;
|
||
}
|
||
} else {
|
||
while (lSamplesDst-- > 0)
|
||
{
|
||
*piDst++ = *piSrc++;
|
||
*piDst++ = *piSrc++;
|
||
piSrc++;
|
||
piSrc++;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* reallocate the WAVE buffer to be half as big enough */
|
||
//!!WinEval(AllocWaveBuffer(glWaveSamplesValid / 2L));
|
||
AllocWaveBuffer(glWaveSamplesValid / 2L, TRUE, TRUE);
|
||
|
||
EndWaveEdit(TRUE);
|
||
|
||
if (hcurPrev != NULL)
|
||
SetCursor(hcurPrev);
|
||
|
||
/* update the display */
|
||
UpdateDisplay(TRUE);
|
||
}
|
||
|
||
|
||
/* MakeSlower()
|
||
*
|
||
* Make the sound play twice as slow.
|
||
*/
|
||
void FAR PASCAL
|
||
MakeSlower(void)
|
||
{
|
||
HPBYTE pchSrc; // pointer into source part of buffer
|
||
HPBYTE pchDst; // pointer into destination part
|
||
short * piSrc;
|
||
short * piDst;
|
||
|
||
long lSamplesSrc; // samples to copy from source buffer
|
||
HCURSOR hcurPrev = NULL; // cursor before hourglass
|
||
long lPrevPosition; // previous "current position"
|
||
|
||
int iSample; // current source sample
|
||
int iPrevSample; // previous sample (for interpolation)
|
||
int iSample2;
|
||
int iPrevSample2;
|
||
|
||
long lSample;
|
||
long lPrevSample;
|
||
long lSample2;
|
||
long lPrevSample2;
|
||
|
||
if (glWaveSamplesValid == 0L) // nothing to do?
|
||
return; // don't set dirty flag
|
||
|
||
if (!IsWaveFormatPCM(gpWaveFormat))
|
||
return;
|
||
|
||
/* show hourglass cursor */
|
||
hcurPrev = SetCursor(LoadCursor(NULL, IDC_WAIT));
|
||
|
||
BeginWaveEdit();
|
||
|
||
/* reallocate the WAVE buffer to be twice as big */
|
||
lPrevPosition = glWavePosition;
|
||
if (!AllocWaveBuffer(glWaveSamplesValid * 2L, TRUE, TRUE))
|
||
goto RETURN;
|
||
|
||
/* each source sample generates two destination samples;
|
||
* use interpolation to generate new samples; must go backwards
|
||
* through the buffer to avoid destroying data
|
||
*/
|
||
pchSrc = gpWaveSamples + wfSamplesToBytes(gpWaveFormat, glWaveSamplesValid);
|
||
pchDst = gpWaveSamples + wfSamplesToBytes(gpWaveFormat, glWaveSamplesValid * 2L);
|
||
lSamplesSrc = glWaveSamplesValid;
|
||
|
||
if (((LPWAVEFORMATEX)gpWaveFormat)->wBitsPerSample == 8)
|
||
{
|
||
if (gpWaveFormat->nChannels == 1)
|
||
{
|
||
iPrevSample = *((BYTE *) (pchSrc - 1));
|
||
while (lSamplesSrc-- > 0)
|
||
{
|
||
pchSrc = ((BYTE *) pchSrc) - 1;
|
||
iSample = *((BYTE *) pchSrc);
|
||
|
||
*--pchDst = (BYTE)((iSample + iPrevSample)/2);
|
||
*--pchDst = (BYTE) iSample;
|
||
iPrevSample = iSample;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
iPrevSample = *((BYTE *) (pchSrc - 2));
|
||
iPrevSample2 = *((BYTE *) (pchSrc - 1));
|
||
while (lSamplesSrc-- > 0)
|
||
{
|
||
pchSrc = ((BYTE *) pchSrc)-1;
|
||
iSample2 = *((BYTE *) pchSrc);
|
||
|
||
pchSrc = ((BYTE *) pchSrc)-1;
|
||
iSample = *((BYTE *) pchSrc);
|
||
|
||
*--pchDst = (BYTE)((iSample2 + iPrevSample2)
|
||
/ 2);
|
||
*--pchDst = (BYTE)((iSample + iPrevSample)
|
||
/ 2);
|
||
*--pchDst = (BYTE) iSample2;
|
||
*--pchDst = (BYTE) iSample;
|
||
iPrevSample = iSample;
|
||
iPrevSample2 = iSample2;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
piDst = (short *) pchDst;
|
||
piSrc = (short *) pchSrc;
|
||
|
||
if (gpWaveFormat->nChannels == 1)
|
||
{
|
||
lPrevSample = *(piSrc - 1);
|
||
while (lSamplesSrc-- > 0)
|
||
{
|
||
lSample = *--piSrc;
|
||
*--piDst = (short)((lSample + lPrevSample)/2);
|
||
*--piDst = (short) lSample;
|
||
lPrevSample = lSample;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
lPrevSample = *(piSrc - 2);
|
||
lPrevSample2 = *(piSrc - 1);
|
||
while (lSamplesSrc-- > 0)
|
||
{
|
||
lSample2 = *--piSrc;
|
||
lSample = *--piSrc;
|
||
*--piDst = (short)((lSample2 + lPrevSample2)/2);
|
||
*--piDst = (short)((lSample + lPrevSample) / 2);
|
||
*--piDst = (short) lSample2;
|
||
*--piDst = (short) lSample;
|
||
lPrevSample = lSample;
|
||
lPrevSample2 = lSample2;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* the entire buffer now contains valid samples */
|
||
glWaveSamplesValid *= 2L;
|
||
|
||
/* move the current position so it will correspond to the same point
|
||
* in the audio before and after the change-pitch operation
|
||
*/
|
||
glWavePosition = lPrevPosition * 2L;
|
||
//!!WinAssert(glWavePosition <= glWaveSamplesValid);
|
||
|
||
RETURN:
|
||
EndWaveEdit(TRUE);
|
||
|
||
if (hcurPrev != NULL)
|
||
SetCursor(hcurPrev);
|
||
|
||
/* update the display */
|
||
UpdateDisplay(TRUE);
|
||
}
|
||
|
||
|
||
#if 0
|
||
|
||
/* pchNew = FindWave(pch, pchEnd, ppchWaveBuf)
|
||
*
|
||
* Assuming <pch> points within the wave buffer and <pchEnd> points past the
|
||
* end of the buffer, find the beginning of the next "wave", i.e. the point
|
||
* where the waveform starts rising (after it has fallen).
|
||
*
|
||
* <ppchWaveBuf> points to a pointer that points to a buffer that is filled
|
||
* in with a copy of the wave. The pointer <*ppchWaveBuf> is modified and
|
||
* upon return will point past the end of the wave.
|
||
*/
|
||
HPBYTE NEAR PASCAL
|
||
FindWave(HPBYTE pch, HPBYTE pchEnd, NPBYTE *ppchWaveBuf)
|
||
{
|
||
BYTE bLowest = 255;
|
||
BYTE bHighest = 0;
|
||
BYTE bLowPoint;
|
||
BYTE bHighPoint;
|
||
BYTE bDelta;
|
||
HPBYTE pchWalk;
|
||
BYTE b;
|
||
#ifdef VERBOSEDEBUG
|
||
NPBYTE pchWaveBufInit = *ppchWaveBuf;
|
||
#endif
|
||
|
||
if (pch == pchEnd)
|
||
return pch;
|
||
|
||
for (pchWalk = pch; pchWalk != pchEnd; pchWalk++)
|
||
{
|
||
b = *pchWalk;
|
||
b = *((BYTE *) pchWalk);
|
||
if (bLowest > b)
|
||
bLowest = b;
|
||
if (bHighest < b)
|
||
bHighest = b;
|
||
}
|
||
|
||
bDelta = (bHighest - bLowest) / FINDWAVE_PICKYNESS;
|
||
bLowPoint = bLowest + bDelta;
|
||
bHighPoint = bHighest - bDelta;
|
||
//!!WinAssert(bLowPoint >= bLowest);
|
||
//!!WinAssert(bHighPoint <= bHighest);
|
||
#ifdef VERBOSEDEBUG
|
||
DPF(TEXT("0x%08lX: %3d to %3d"), (DWORD) pch,
|
||
(int) bLowPoint, (int) bHighPoint);
|
||
#endif
|
||
|
||
if (bLowPoint == bHighPoint)
|
||
{
|
||
/* avoid infinite loop */
|
||
*(*ppchWaveBuf)++ = *((BYTE *) pch++);
|
||
#ifdef VERBOSEDEBUG
|
||
DPF(TEXT(" (equal)\n"));
|
||
#endif
|
||
return pch;
|
||
}
|
||
|
||
/* find a "peak" */
|
||
while ((pch != pchEnd) && (*((BYTE *) pch) < bHighPoint))
|
||
*(*ppchWaveBuf)++ = *((BYTE *) pch++);
|
||
|
||
/* find a "valley" */
|
||
while ((pch != pchEnd) && (*((BYTE *) pch) > bLowPoint))
|
||
*(*ppchWaveBuf)++ = *((BYTE *) pch++);
|
||
|
||
#ifdef VERBOSEDEBUG
|
||
DPF(TEXT(" (copied %d)\n"), *ppchWaveBuf - pchWaveBufInit);
|
||
#endif
|
||
|
||
return pch;
|
||
}
|
||
|
||
#endif
|
||
|
||
|
||
#if 0
|
||
|
||
/* IncreasePitch()
|
||
*
|
||
* Increase the pitch of samples in the wave buffer by one octave.
|
||
*/
|
||
void FAR PASCAL
|
||
IncreasePitch(void)
|
||
{
|
||
HCURSOR hcurPrev = NULL; // cursor before hourglass
|
||
HPBYTE pchEndFile; // end of file's buffer
|
||
HPBYTE pchStartWave; // start of one wave
|
||
HPBYTE pchMaxWave; // last place where wave may end
|
||
HPBYTE pchEndWave; // end an actual wave
|
||
char achWaveBuf[WAVEBUFSIZE];
|
||
NPBYTE pchWaveBuf;
|
||
NPBYTE pchSrc;
|
||
HPBYTE pchDst;
|
||
|
||
if (glWaveSamplesValid == 0L) // nothing to do?
|
||
return; // don't set dirty flag
|
||
|
||
if (!IsWaveFormatPCM(gpWaveFormat))
|
||
return;
|
||
|
||
/* show hourglass cursor */
|
||
hcurPrev = SetCursor(LoadCursor(NULL, IDC_WAIT));
|
||
|
||
BeginWaveEdit();
|
||
|
||
/* find each wave in the wave buffer and double it */
|
||
pchEndFile = gpWaveSamples + glWaveSamplesValid;
|
||
pchStartWave = gpWaveSamples;
|
||
while (TRUE)
|
||
{
|
||
pchMaxWave = pchStartWave + WAVEBUFSIZE;
|
||
if (pchMaxWave > pchEndFile)
|
||
pchMaxWave = pchEndFile;
|
||
pchWaveBuf = achWaveBuf;
|
||
pchEndWave = FindWave(pchStartWave, pchMaxWave, &pchWaveBuf);
|
||
pchSrc = achWaveBuf;
|
||
pchDst = pchStartWave;
|
||
if (pchSrc == pchWaveBuf)
|
||
break; // no samples copied
|
||
|
||
while (pchDst != pchEndWave)
|
||
{
|
||
*pchDst++ = *pchSrc++;
|
||
pchSrc++;
|
||
if (pchSrc >= pchWaveBuf)
|
||
{
|
||
if (pchSrc == pchWaveBuf)
|
||
pchSrc = achWaveBuf;
|
||
else
|
||
pchSrc = achWaveBuf + 1;
|
||
}
|
||
}
|
||
|
||
pchStartWave = pchEndWave;
|
||
}
|
||
|
||
EndWaveEdit(TRUE);
|
||
|
||
if (hcurPrev != NULL)
|
||
SetCursor(hcurPrev);
|
||
|
||
/* update the display */
|
||
UpdateDisplay(TRUE);
|
||
}
|
||
|
||
#endif
|
||
|
||
|
||
#if 0
|
||
|
||
/* DecreasePitch()
|
||
*
|
||
* Decrease the pitch of samples in the wave buffer by one octave.
|
||
*/
|
||
void FAR PASCAL
|
||
DecreasePitch(void)
|
||
{
|
||
HCURSOR hcurPrev = NULL; // cursor before hourglass
|
||
HPBYTE pchEndFile; // end of file's buffer
|
||
HPBYTE pchStartWave; // start of one wave
|
||
HPBYTE pchMaxWave; // last place where wave may end
|
||
HPBYTE pchEndWave; // end an actual wave
|
||
char achWaveBuf[WAVEBUFSIZE];
|
||
NPBYTE pchWaveBuf; // end of first wave in <achWaveBuf>
|
||
NPBYTE pchSrc; // place to read samples from
|
||
NPBYTE pchSrcEnd; // end of place to read samples from
|
||
int iSample; // current source sample
|
||
int iPrevSample; // previous sample (for interpolation)
|
||
HPBYTE pchDst; // where result gets put in buffer
|
||
long lNewFileSize; // file size after pitch change
|
||
|
||
if (glWaveSamplesValid == 0L) // nothing to do?
|
||
return; // don't set dirty flag
|
||
|
||
if (!IsWaveFormatPCM(gpWaveFormat))
|
||
return;
|
||
|
||
/* show hourglass cursor */
|
||
hcurPrev = SetCursor(LoadCursor(NULL, IDC_WAIT));
|
||
|
||
BeginWaveEdit();
|
||
|
||
/* find each pair of waves in the wave buffer, discard the longer
|
||
* of the two waves, and expand the shorter of the two waves to
|
||
* twice its size
|
||
*/
|
||
pchEndFile = gpWaveSamples + glWaveSamplesValid;
|
||
pchStartWave = gpWaveSamples; // read waves from here
|
||
pchDst = gpWaveSamples; // write waves to here
|
||
while (TRUE)
|
||
{
|
||
pchMaxWave = pchStartWave + WAVEBUFSIZE;
|
||
if (pchMaxWave > pchEndFile)
|
||
pchMaxWave = pchEndFile;
|
||
|
||
/* read one wave -- make <pchWaveBuf> point to the end
|
||
* of the wave that's copied into <achWaveBuf>
|
||
*/
|
||
pchWaveBuf = achWaveBuf;
|
||
pchEndWave = FindWave(pchStartWave, pchMaxWave, &pchWaveBuf);
|
||
if (pchWaveBuf == achWaveBuf)
|
||
break;
|
||
|
||
/* read another wave -- make <pchWaveBuf> now point to the end
|
||
* of that wave that's copied into <achWaveBuf>
|
||
*/
|
||
pchEndWave = FindWave(pchEndWave, pchMaxWave, &pchWaveBuf);
|
||
|
||
pchSrc = achWaveBuf;
|
||
pchSrcEnd = achWaveBuf + ((pchWaveBuf - achWaveBuf) / 2);
|
||
iPrevSample = *((BYTE *) pchSrc);
|
||
while (pchSrc != pchSrcEnd)
|
||
{
|
||
iSample = *((BYTE *) pchSrc)++;
|
||
*pchDst++ = (BYTE) ((iSample + iPrevSample) / 2);
|
||
*pchDst++ = iSample;
|
||
iPrevSample = iSample;
|
||
}
|
||
|
||
pchStartWave = pchEndWave;
|
||
}
|
||
|
||
/* file may have shrunk */
|
||
lNewFileSize = pchDst - gpWaveSamples;
|
||
//!!WinAssert(lNewFileSize <= glWaveSamplesValid);
|
||
#ifdef DEBUG
|
||
DPF(TEXT("old file size is %ld, new size is %ld\n"),
|
||
glWaveSamplesValid, lNewFileSize);
|
||
#endif
|
||
AllocWaveBuffer(lNewFileSize, TRUE, TRUE);
|
||
|
||
EndWaveEdit(TRUE);
|
||
|
||
if (hcurPrev != NULL)
|
||
SetCursor(hcurPrev);
|
||
|
||
/* update the display */
|
||
UpdateDisplay(TRUE);
|
||
}
|
||
|
||
#endif
|
||
|
||
|
||
/* AddEcho()
|
||
*
|
||
* Add echo to samples in the wave buffer.
|
||
*/
|
||
void FAR PASCAL
|
||
AddEcho(void)
|
||
{
|
||
HCURSOR hcurPrev = NULL; // cursor before hourglass
|
||
long lDeltaSamples; // no. samples for echo delay
|
||
long lSamples; // no. samples to modify
|
||
int iAmpSrc; // current source sample amplitude
|
||
int iAmpDst; // current destination sample amplitude
|
||
|
||
if (!IsWaveFormatPCM(gpWaveFormat))
|
||
return;
|
||
|
||
BeginWaveEdit();
|
||
|
||
/* figure out how many samples need to be modified */
|
||
lDeltaSamples = MulDiv((long) ECHO_DELAY,
|
||
gpWaveFormat->nSamplesPerSec, 1000L);
|
||
|
||
/* Set lSamples to be number of samples * number of channels */
|
||
lSamples = (glWaveSamplesValid - lDeltaSamples)
|
||
* gpWaveFormat->nChannels;
|
||
|
||
if (lSamples <= 0L) // nothing to do?
|
||
return; // don't set dirty flag
|
||
|
||
/* show hourglass cursor */
|
||
hcurPrev = SetCursor(LoadCursor(NULL, IDC_WAIT));
|
||
|
||
/* copy ECHO_VOLUME percent of each source sample (starting at
|
||
* ECHO_DELAY milliseconds from the end of the the buffer)
|
||
* to the each destination sample (starting at the end of the
|
||
* buffer)
|
||
*/
|
||
if (((LPWAVEFORMATEX)gpWaveFormat)->wBitsPerSample == 8)
|
||
{
|
||
HPBYTE pchSrc; // pointer into source part of buffer
|
||
HPBYTE pchDst; // pointer into destination part
|
||
int iSample; // destination sample
|
||
|
||
pchSrc = gpWaveSamples + wfSamplesToBytes(gpWaveFormat, glWaveSamplesValid - lDeltaSamples);
|
||
pchDst = gpWaveSamples + wfSamplesToBytes(gpWaveFormat, glWaveSamplesValid);
|
||
|
||
while (lSamples-- > 0)
|
||
{
|
||
pchSrc = ((BYTE *) pchSrc) - 1;
|
||
iAmpSrc = (int) *((BYTE *) pchSrc) - 128;
|
||
|
||
pchDst = ((BYTE *) pchDst) - 1;
|
||
iAmpDst = (int) *((BYTE *) pchDst) - 128;
|
||
|
||
iSample = iAmpDst + (iAmpSrc * ECHO_VOLUME) / 100
|
||
+ 128;
|
||
*((BYTE *) pchDst) = (BYTE)
|
||
(iSample < 0 ? 0 : (iSample > 255
|
||
? 255 : iSample));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
short * piSrc; // pointer into source part of buffer
|
||
short * piDst; // pointer into destination part
|
||
long lSample;// destination sample
|
||
|
||
piSrc = (short *) (gpWaveSamples + wfSamplesToBytes(gpWaveFormat, glWaveSamplesValid - lDeltaSamples));
|
||
piDst = (short *) (gpWaveSamples + wfSamplesToBytes(gpWaveFormat, glWaveSamplesValid));
|
||
|
||
while (lSamples-- > 0)
|
||
{
|
||
iAmpSrc = *--piSrc;
|
||
iAmpDst = *--piDst;
|
||
lSample = ((long) iAmpSrc * ECHO_VOLUME) / 100 + (long) iAmpDst;
|
||
|
||
*piDst = (short) (lSample < -32768L
|
||
? -32768 : (lSample > 32767L
|
||
? 32767 : (short) lSample));
|
||
}
|
||
}
|
||
|
||
EndWaveEdit(TRUE);
|
||
|
||
if (hcurPrev != NULL)
|
||
SetCursor(hcurPrev);
|
||
|
||
/* update the display */
|
||
UpdateDisplay(TRUE);
|
||
}
|
||
|
||
|
||
/* Reverse()
|
||
*
|
||
* Reverse samples in the wave buffer.
|
||
*/
|
||
void FAR PASCAL
|
||
Reverse(void)
|
||
{
|
||
HCURSOR hcurPrev = NULL; // cursor before hourglass
|
||
HPBYTE pchA, pchB; // pointers into buffer
|
||
short * piA;
|
||
short * piB;
|
||
long lSamples; // no. Samples to modify
|
||
char chTmp; // for swapping
|
||
int iTmp;
|
||
|
||
if (glWaveSamplesValid == 0L) // nothing to do?
|
||
return; // don't set dirty flag
|
||
|
||
if (!IsWaveFormatPCM(gpWaveFormat))
|
||
return;
|
||
|
||
BeginWaveEdit();
|
||
|
||
/* show hourglass cursor */
|
||
hcurPrev = SetCursor(LoadCursor(NULL, IDC_WAIT));
|
||
|
||
lSamples = glWaveSamplesValid / 2;
|
||
|
||
if (((LPWAVEFORMATEX)gpWaveFormat)->wBitsPerSample == 8)
|
||
{
|
||
pchA = gpWaveSamples;
|
||
if (gpWaveFormat->nChannels == 1)
|
||
{
|
||
pchB = gpWaveSamples + wfSamplesToBytes(gpWaveFormat, glWaveSamplesValid);
|
||
|
||
while (lSamples-- > 0)
|
||
{
|
||
chTmp = *pchA;
|
||
*pchA++ = *--pchB;
|
||
*pchB = chTmp;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
pchB = gpWaveSamples + wfSamplesToBytes(gpWaveFormat, glWaveSamplesValid - 1);
|
||
|
||
while (lSamples-- > 0)
|
||
{
|
||
chTmp = *pchA;
|
||
*pchA = *pchB;
|
||
*pchB = chTmp;
|
||
chTmp = pchA[1];
|
||
pchA[1] = pchB[1];
|
||
pchB[1] = chTmp;
|
||
pchA += 2;
|
||
pchB -= 2;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
piA = (short *) gpWaveSamples;
|
||
if (gpWaveFormat->nChannels == 1)
|
||
{
|
||
piB = (short *) (gpWaveSamples + wfSamplesToBytes(gpWaveFormat, glWaveSamplesValid));
|
||
|
||
while (lSamples-- > 0)
|
||
{
|
||
iTmp = *piA;
|
||
*piA++ = *--piB;
|
||
*piB = (short)iTmp;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
piB = (short *) (gpWaveSamples + wfSamplesToBytes(gpWaveFormat, glWaveSamplesValid - 1));
|
||
|
||
while (lSamples-- > 0)
|
||
{
|
||
iTmp = *piA;
|
||
*piA = *piB;
|
||
*piB = (short)iTmp;
|
||
iTmp = piA[1];
|
||
piA[1] = piB[1];
|
||
piB[1] = (short)iTmp;
|
||
piA += 2;
|
||
piB -= 2;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* move the current position so it corresponds to the same point
|
||
* in the audio as it did before the reverse operation
|
||
*/
|
||
glWavePosition = glWaveSamplesValid - glWavePosition;
|
||
|
||
EndWaveEdit(TRUE);
|
||
|
||
if (hcurPrev != NULL)
|
||
SetCursor(hcurPrev);
|
||
|
||
/* update the display */
|
||
UpdateDisplay(TRUE);
|
||
}
|
||
|
||
#if defined(REVERB)
|
||
|
||
/* AddReverb()
|
||
*
|
||
* Add reverberation to samples in the wave buffer.
|
||
* Very similar to add echo, but instead of adding a single
|
||
* shot we
|
||
* 1. have multiple echoes
|
||
* 2. Have feedback so that each echo also generates an echo
|
||
* Danger: Because some of the echo times are short, there
|
||
* is likely to be high correlation between the wave
|
||
* at the source and destination points. In this case
|
||
* we don't get an echo at all, we get a resonance.
|
||
* The effect of a large hall DOES give resonances,
|
||
* but we should scatter them about to avoid making
|
||
* any sharp resonance.
|
||
* The first echo is also chosen to be long enough that
|
||
* its primary resonance will be below any normal speaking
|
||
* voice. 20mSec is 50Hz and an octave below bass range.
|
||
* Low levels of sound suffer badly from quantisation noise
|
||
* which can get quite bad. For this reason it's probably
|
||
* better to have the multipliers as powers of 2.
|
||
*
|
||
* Cheat: The reverb does NOT extend the total time (no realloc (yet).
|
||
*
|
||
* This takes a lot of compute - and is not really very much different
|
||
* in sound to AddEcho. Conclusion -- NOT IN PRODUCT.
|
||
*
|
||
*/
|
||
void FAR PASCAL
|
||
AddReverb(void)
|
||
{
|
||
HCURSOR hcurPrev = NULL; // cursor before hourglass
|
||
long lSamples; // no. samples to modify
|
||
int iAmpSrc; // current source sample amplitude
|
||
int iAmpDst; // current destination sample amplitude
|
||
int i;
|
||
|
||
typedef struct
|
||
{ long Offset; // delay in samples
|
||
long Delay; // delay in mSec
|
||
int Vol; // volume multiplier in units of 1/256
|
||
} ECHO;
|
||
|
||
#define CREVERB 3
|
||
|
||
ECHO Reverb[CREVERB] = { 0, 18, 64
|
||
, 0, 64, 64
|
||
};
|
||
|
||
if (!IsWaveFormatPCM(gpWaveFormat))
|
||
return;
|
||
|
||
BeginWaveEdit();
|
||
|
||
/* Convert millisec figures into samples */
|
||
for (i=0; i<CREVERB; ++i)
|
||
{ Reverb[i].Offset = MulDiv( Reverb[i].Delay
|
||
, gpWaveFormat->nSamplesPerSec
|
||
, 1000L
|
||
);
|
||
|
||
// I think this could have the effect of putting the reverb
|
||
// from one stereo channel onto the other one sometimes.
|
||
// It's a feature! (Fix is to make Offset always even)
|
||
}
|
||
|
||
if (lSamples <= 0L) // nothing to do?
|
||
return; // don't set dirty flag
|
||
|
||
/* show hourglass cursor */
|
||
hcurPrev = SetCursor(LoadCursor(NULL, IDC_WAIT));
|
||
|
||
lSamples = glWaveSamplesValid * gpWaveFormat->nChannels;
|
||
|
||
/* Work through the buffer left to right adding in the reverbs */
|
||
if (((LPPCMWAVEFORMAT)gpWaveFormat)->wBitsPerSample == 8)
|
||
{
|
||
BYTE * pbSrc; // pointer into source part of buffer
|
||
BYTE * pbDst; // pointer into destination part
|
||
int iSample; // destination sample
|
||
|
||
|
||
for (i=0; i<CREVERB; ++i)
|
||
{ long cSamp; // loop counter
|
||
int Vol = Reverb[i].Vol;
|
||
pbSrc = gpWaveSamples;
|
||
pbDst = gpWaveSamples+Reverb[i].Offset; // but elsewhere if realloc
|
||
cSamp = lSamples-Reverb[i].Offset;
|
||
while (cSamp-- > 0)
|
||
{
|
||
iAmpSrc = (*pbSrc) - 128;
|
||
iSample = *pbDst + MulDiv(iAmpSrc, Vol, 256);
|
||
*pbDst = (iSample < 0 ? 0 : (iSample > 255 ? 255 : iSample));
|
||
|
||
++pbSrc;
|
||
++pbDst;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
int short * piSrc; // pointer into source part of buffer
|
||
int short * piDst; // pointer into destination part
|
||
long lSample;// destination sample
|
||
|
||
piSrc = gpWaveSamples;
|
||
piDst = gpWaveSamples;
|
||
|
||
while (lSamples-- > 0)
|
||
{
|
||
iAmpSrc = *piSrc;
|
||
for (i=0; i<CREVERB; ++i)
|
||
{ int short * piD = piDst + Reverb[i].Offset; // !!not win16
|
||
lSample = *piD + MulDiv(iAmpSrc, Reverb[i].Vol, 256);
|
||
*piDst = (short) ( lSample < -32768L
|
||
? -32768
|
||
: (lSample > 32767L ? 32767 : (short) lSample)
|
||
);
|
||
}
|
||
|
||
++piSrc;
|
||
++piDst;
|
||
}
|
||
}
|
||
|
||
EndWaveEdit(TRUE);
|
||
|
||
if (hcurPrev != NULL)
|
||
SetCursor(hcurPrev);
|
||
|
||
/* update the display */
|
||
UpdateDisplay(TRUE);
|
||
} /* AddReverb */
|
||
#endif //REVERB
|
||
|