2025-11-28 00:35:46 +09:00

489 lines
13 KiB
C++

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
#include "stdafx.h"
#include "resource.h"
#include "wave.h"
#include <mmreg.h>
#include "ChildView.h"
#include <msacm.h>
// -----------------------------------------------------------------------------------------
/*
* WaveOpenFile : opens a wave file, validates format, extracts parameters
*/
HANDLE
WaveOpenFile
(
IN LPCSTR szFileName,
OUT WAVEFORMATEX** ppWaveFormatEx,
OUT PULONG pcbData
)
{
HMMIO hmmio; // mmio style file handle
MMCKINFO mmckinfoParent; // parent chunk info struct
MMCKINFO mmckinfoSubchunk; // sub chunk
HWND hwnd = AfxGetMainWnd()->m_hWnd;
HANDLE hFile = INVALID_HANDLE_VALUE;
ULONG cbFormat = 0;
LONG lDataPos = 0;
// open the file
if(!(hmmio = mmioOpen((LPSTR)szFileName, NULL, MMIO_READ | MMIO_ALLOCBUF)))
{
return FALSE;
}
// verify that the file is of type "WAVE"
mmckinfoParent.fccType = MAKEFOURCC('W', 'A', 'V', 'E');
if(mmioDescend(hmmio, (LPMMCKINFO)&mmckinfoParent, NULL, MMIO_FINDRIFF))
{
// not a "WAVE" file!!
MessageBox(hwnd, "WaveOpenFile: Missing 'WAVE' chunk.This does not appear to be a wave file.", "MultiChan : Error opening wave file!", MB_ICONEXCLAMATION | MB_OK);
goto _error_;
}
// find the "fmt " chunk (must be a subchunk of the "WAVE" chunk)
mmckinfoSubchunk.ckid = MAKEFOURCC('f', 'm', 't', ' ');
if(mmioDescend(hmmio, (LPMMCKINFO)&mmckinfoSubchunk, (LPMMCKINFO)&mmckinfoParent, MMIO_FINDCHUNK))
{
// file has no "fmt " chunk
MessageBox(hwnd, "WaveOpenFile: Missing 'fmt ' chunk", "MultiChan : Error opening wave file!", MB_ICONEXCLAMATION | MB_OK);
goto _error_;
}
// get the size of the "fmt " chunk
cbFormat = mmckinfoSubchunk.cksize;
// allocate waveformat. The "max" part ensures that the cbSize member of the WAVEFORMATEX exists and = 0
*ppWaveFormatEx = (LPWAVEFORMATEX)LocalAlloc(LPTR, max(cbFormat, sizeof(WAVEFORMATEX)));
if(!*ppWaveFormatEx)
{
// failed to allocate
MessageBox(hwnd, "WaveOpenFile: Failed to allocate memory", "MultiChan : Error opening wave file!", MB_ICONEXCLAMATION | MB_OK);
goto _error_;
}
// read the "fmt " chunk
if(mmioRead(hmmio, (HPSTR)*ppWaveFormatEx, cbFormat) != (LONG)cbFormat)
{
// failed to read the "fmt " chunk
MessageBox(hwnd, "WaveOpenFile: Error reading 'fmt ' chunk", "MultiChan : Error opening wave file!", MB_ICONEXCLAMATION | MB_OK);
goto _error_;
}
// ascend out o' the "fmt " chunk
mmioAscend(hmmio, &mmckinfoSubchunk, 0);
// and find the data subchunk. (current file pos should be at the beginning of the
// data chunk, but when you assume...)
mmckinfoSubchunk.ckid = MAKEFOURCC('d', 'a', 't', 'a');
if(mmioDescend(hmmio, &mmckinfoSubchunk, &mmckinfoParent, MMIO_FINDCHUNK))
{
// wave file doesn't appear to have a data chunk
MessageBox(hwnd, "WaveOpenFile: Missing 'data' chunk", "MultiChan : Error opening wave file!", MB_ICONEXCLAMATION | MB_OK);
goto _error_;
}
// get the size of the "data" subchunk
*pcbData = mmckinfoSubchunk.cksize;
if(0L == *pcbData)
{
// the data chunk contains no data
MessageBox(hwnd, "WaveOpenFile: Error reading 'data' chunk", "MultiChan : Error opening wave file!", MB_ICONEXCLAMATION | MB_OK);
goto _error_;
}
// remember the data
lDataPos = mmioSeek(hmmio, 0, SEEK_CUR);
mmioClose(hmmio, 0);
//
// now we are done with the mmio functions. Reopen the file and seek to the data chunk using normal
// file io routines
//
__try
{
hFile =
CreateFile
(
szFileName,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
MessageBox(hwnd, "WaveOpenFile : Handling exception thrown by CreateFile!", "MultiChan : Error opening wave file!", MB_ICONEXCLAMATION | MB_OK);
goto _error_;
}
if (!IsValidHandle(hFile))
{
goto _error_;
}
SetFilePointer
(
hFile,
lDataPos,
NULL,
FILE_BEGIN
);
return hFile;
_error_:
SafeCloseHandle(hFile);
if(hmmio)
{
mmioClose(hmmio, 0);
}
return INVALID_HANDLE_VALUE;
}
// -----------------------------------------------------------------------------------------
/*
* WaveSaveFile : creates and writes the mixed wave; returns success code
*/
BOOL
WaveSaveFile
(
IN LPCSTR szFileName,
OUT WAVEFORMATEX* pWaveFormatEx,
IN PVOID pvData,
IN ULONG cbData
)
{
HANDLE hFile = INVALID_HANDLE_VALUE;
ULONG cbWritten;
HRSRC hrsrc = (HRSRC)INVALID_HANDLE_VALUE;
void* pv = NULL;
//
// now we are done with the mmio functions. Reopen the file and seek to the data chunk using normal
// file io routines
//
__try
{
hFile =
CreateFile
(
szFileName,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
MessageBox(AfxGetMainWnd()->m_hWnd, "WaveSaveFile : Handling exception thrown by CreateFile!", "MultiChan : Error creating wave file", MB_ICONEXCLAMATION | MB_OK);
goto _error_;
}
if (!IsValidHandle(hFile))
goto _error_;
//
// write file header
//
// standard wave goo
struct
{
DWORD fourccData;
DWORD dwDataLength;
} DataHeader;
DataHeader.fourccData = MAKEFOURCC('d','a','t','a');
DataHeader.dwDataLength = cbData;
struct
{
DWORD dwRiff;
DWORD dwFileSize;
DWORD dwWave;
DWORD dwFormat;
DWORD dwFormatLength;
} FileHeader;
FileHeader.dwRiff = MAKEFOURCC('R','I','F','F');
FileHeader.dwWave = MAKEFOURCC('W','A','V','E');
FileHeader.dwFormat = MAKEFOURCC('f','m','t',' ');
FileHeader.dwFormatLength = sizeof(WAVEFORMATEX) + pWaveFormatEx->cbSize;
FileHeader.dwFileSize = sizeof(FileHeader) + FileHeader.dwFormatLength + sizeof(DataHeader) + cbData;
if(!WriteFile(hFile, &FileHeader, sizeof(FileHeader), &cbWritten, NULL))
goto _error_;
// wave format
if(!WriteFile(hFile, pWaveFormatEx, sizeof(WAVEFORMATEX) + pWaveFormatEx->cbSize, &cbWritten, NULL))
goto _error_;
// data chuck
if(!WriteFile(hFile, &DataHeader, sizeof(DataHeader), &cbWritten, NULL))
goto _error_;
//
// Write wave data
//
if(!WriteFile(hFile, pvData, cbData, &cbWritten, NULL))
goto _error_;
SafeCloseHandle( hFile );
return TRUE;
_error_:
MessageBox(NULL, "WaveSaveFile : Error Writing File", "MultiChan : Error!", MB_ICONEXCLAMATION | MB_OK);
SafeCloseHandle( hFile );
return FALSE;
}
// ===============================================================
/*
* WavePlayFileCB
* callback for the waveOut functions
* notifies the originating window
*/
void
CALLBACK
WavePlayFileCB
(
HWAVEOUT hwo,
UINT uMsg,
DWORD_PTR dwInstance,
DWORD_PTR dwParam1,
DWORD_PTR dwParam2
)
{
DWORD mmr = MMSYSERR_BASE;
ASSERT( ( CDialog * )dwInstance );
switch( uMsg )
{
case WOM_OPEN:
{
//
// notify window
//
(( CDialog * )dwInstance)->PostMessage( WM_START_PLAYBACK, 0, 0 );
break;
}
case WOM_DONE:
{
//
// notify window
//
(( CDialog * )dwInstance)->PostMessage( WM_STOP_PLAYBACK, 0, 0 );
break;
}
case WOM_CLOSE:
{
break;
}
default:
ASSERT(0);
}
return;
}
/*
* WaveDisablePlayback
* prevent other channels from being played at this time
*/
void
WaveTogglePlayback
(
CDlgSrc * pdlgSelf,
const BOOL fSwitch
)
{
CDlgSrc * pdlgSrc = 0;
//
// fSwitch toggles between disable (FALSE) and allow (TRUE)
//
for
(
POSITION position = g_listSources.GetHeadPosition();
position;
)
{
pdlgSrc = g_listSources.GetNext( position );
//
// disable/enable the play button. the stop button is unaffected
//
pdlgSrc->m_fPlayable = fSwitch;
(pdlgSrc->m_butPlay).EnableWindow( fSwitch );
}
//
// check target
//
if( pdlgSelf )
{
// allow stop
(pdlgSelf->m_butStop).EnableWindow( !fSwitch );
// destination may not be mixed already
if( g_pdlgDest->m_fPlayable )
{
g_pdlgDest->m_fPlayable = fSwitch;
(g_pdlgDest->m_butPlay).EnableWindow( fSwitch );
}
else
{
g_pdlgDest->m_fPlayable = fSwitch & (0 != g_pdlgDest->m_pbData);
(g_pdlgDest->m_butPlay).EnableWindow( g_pdlgDest->m_fPlayable );
}
}
else
{
g_pdlgDest->m_fPlayable = fSwitch;
(g_pdlgDest->m_butPlay).EnableWindow( fSwitch );
(g_pdlgDest->m_butStop).EnableWindow( !fSwitch );
}
} // WaveTogglePlayback
// ----------------------------------------------------------------------------------
// TrapMMError
// return: FALSE if it is an error, TRUE otherwise
// ----------------------------------------------------------------------------------
BOOL
TrapMMError
(
MMRESULT mmRes,
LPCSTR szAPI
)
{
// no error
if(MMSYSERR_NOERROR == mmRes)
{
return( TRUE );
}
HWND hwndMain = AfxGetMainWnd()->m_hWnd;
const size_t ERROR_STRING_SIZE_CCH = 128;
char szError[ERROR_STRING_SIZE_CCH] = "";
char szErrMsg[ERROR_STRING_SIZE_CCH] = "";
HRESULT hr = S_OK;
// basic mmsys error
if( MMSYSERR_LASTERROR >= mmRes )
{
if( MMSYSERR_NOMEM == waveOutGetErrorText( mmRes, szError, 128 ) )
{
hr = StringCchPrintfA( szErrMsg, ERROR_STRING_SIZE_CCH, "Insufficient memory to complete the task.");
}
else
{
hr = StringCchPrintfA( szErrMsg, ERROR_STRING_SIZE_CCH, "ERROR : %s returned : %s", szAPI, szError );
}
}
else
{
// other errors
switch( mmRes )
{
case WAVERR_BADFORMAT :
{
hr = StringCchCopyA( szError, ERROR_STRING_SIZE_CCH, " : Attempt to open an unsupported waveform-audio format." );
break;
}
case WAVERR_STILLPLAYING :
{
hr = StringCchCopyA( szError, ERROR_STRING_SIZE_CCH, " : There is another waveform resource still playing." );
break;
}
case WAVERR_UNPREPARED :
{
hr = StringCchCopyA( szError, ERROR_STRING_SIZE_CCH, " : Header is not prepared." );
break;
}
case WAVERR_SYNC :
{
hr = StringCchCopyA( szError, ERROR_STRING_SIZE_CCH, " : Device is synchronous." );
break;
}
case ACMERR_NOTPOSSIBLE :
{
hr = StringCchCopyA( szError, ERROR_STRING_SIZE_CCH, " : The specified action is not possible in the current context." );
break;
}
case ACMERR_BUSY :
{
hr = StringCchCopyA( szError, ERROR_STRING_SIZE_CCH, " : The driver is currently in use and cannot be reused." );
break;
}
case ACMERR_UNPREPARED :
{
hr = StringCchCopyA( szError, ERROR_STRING_SIZE_CCH, " : The stream header is not currently prepared." );
break;
}
case ACMERR_CANCELED :
{
hr = StringCchCopyA( szError, ERROR_STRING_SIZE_CCH, " : The action has been canceled." );
break;
}
default :
{
// default
hr = StringCchPrintfA( szError, ERROR_STRING_SIZE_CCH, "an unrecognized error code (%d).", mmRes );
break;
}
} // switch
if (SUCCEEDED(hr))
{
hr = StringCchPrintfA( szErrMsg, ERROR_STRING_SIZE_CCH, "ERROR : %s returned %s.", szAPI, szError );
}
}
// Display the string.
if (SUCCEEDED(hr))
{
MessageBox(hwndMain, szErrMsg, "MultiChan : Error!", MB_ICONEXCLAMATION | MB_OK);
}
return( FALSE );
}