/* (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 #include #include #include #include #include #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 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 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 */ /* 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 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 . */ 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 to the beginning of * the buffer */ memmove(gpWaveSamples, gpWaveSamples + wfSamplesToBytes(gpWaveFormat, glWavePosition), wfSamplesToBytes(gpWaveFormat, glWaveSamplesValid - glWavePosition)); /* reallocate the buffer to be samples smaller */ AllocWaveBuffer(glWaveSamplesValid - glWavePosition, TRUE, TRUE); glWavePosition = 0L; EndWaveEdit(TRUE); /* update the display */ UpdateDisplay(TRUE); } /* DeleteBefore */ /* DeleteAfter() * * Delete samples after . */ 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 samples in size */ AllocWaveBuffer(glWavePosition, TRUE, TRUE); EndWaveEdit(TRUE); /* update the display */ UpdateDisplay(TRUE); } /* DeleteAfter */ /* ChangeVolume(fIncrease) * * Increase the volume (if is TRUE) or decrease the volume * (if 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 points within the wave buffer and 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). * * 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 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 point to the end * of the wave that's copied into */ pchWaveBuf = achWaveBuf; pchEndWave = FindWave(pchStartWave, pchMaxWave, &pchWaveBuf); if (pchWaveBuf == achWaveBuf) break; /* read another wave -- make now point to the end * of that wave that's copied into */ 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; inSamplesPerSec , 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 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 32767L ? 32767 : (short) lSample) ); } ++piSrc; ++piDst; } } EndWaveEdit(TRUE); if (hcurPrev != NULL) SetCursor(hcurPrev); /* update the display */ UpdateDisplay(TRUE); } /* AddReverb */ #endif //REVERB