|
|
/* (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
|