// WaveStereo2Mono.cpp: implementation of the CWaveStereo2Mono class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "WAVStereo2Mono.h"
#include "WaveStereo2Mono.h"
#include <io.h>
// 为 MCI 函数添加库文件
#include <mmsystem.h>
#pragma comment ( lib, "winmm.lib"
)
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CWaveStereo2Mono::CWaveStereo2Mono ()
{
}
CWaveStereo2Mono::~CWaveStereo2Mono()
{
}
//
// 如果指定了文件名就直接返回,否则打开一个对话框来让用户选择一个文件
//
CString CWaveStereo2Mono::GetFileName(BOOL bOpen, LPCTSTR lpszFileName/*=NULL*/)
{
if (
lpszFileName && strlen(lpszFileName) > 0 &&
( ( bOpen && _access ( lpszFileName, 0 ) == 0 ) || !bOpen )
)
{
return lpszFileName;
}
char szCurDir[MAX_PATH] = {0};
DWORD dwRet = ::GetModuleFileName(NULL,(LPTSTR)szCurDir,sizeof(szCurDir));
if ( szCurDir[dwRet-1] != '//' )
strncat ( szCurDir, "//", sizeof(szCurDir) );
CString csDefaultFileName = _T( "chrys.wav"
);
char *szFilter = "*.wav|*.wav||";
CFileDialog FileDlg ( bOpen, ".wav", csDefaultFileName, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, szFilter, NULL );
FileDlg.m_ofn.lpstrInitialDir = szCurDir;
if ( FileDlg.DoModal () != IDOK )
return "";
return FileDlg.GetPathName();
}
//
// 获取wave文件头,返回PCM数据的偏移地址,失败时返回 -1,用户放弃返回0
//
int CWaveStereo2Mono::GetWaveFileHeader ( OUT WAVEFORMATEX &wf, OUT DWORD &dwPCMDataSize, IN LPCTSTR lpszFileName/*=NULL*/ )
{
CString csFileName = GetFileName ( TRUE, lpszFileName );
if ( csFileName.IsEmpty() ) return 0;
char szHeaderData[1024] = {0};
dwPCMDataSize = 0;
CFile File;
TRY
{
if ( !File.Open ( csFileName, CFile::modeRead |CFile::shareDenyNone ) )
return -1;
if ( File.Read ( szHeaderData, sizeof(szHeaderData) ) < 44 )
return IsNotWaveFile ();
File.Close ();
}
CATCH ( CFileException, e )
{
#ifdef _DEBUG
afxDump << "Handle file failed."
<< e->m_cause << "/n";
#endif
}
END_CATCH
LPDWORD pdw = NULL, pdwEnd = NULL;
DWORD dwRiff = 0, dwType = 0, dwLength = 0;
pdw = (DWORD *) szHeaderData;
dwRiff = *pdw++;
dwLength = *pdw++;
dwType = *pdw++;
// 判断文件头是否为"RIFF"字符
if ( dwRiff != mmioFOURCC ('R', 'I', 'F', 'F') )
return IsNotWaveFile();
// 判断文件格式是否为"WAVE"
if ( dwType != mmioFOURCC ('W', 'A', 'V', 'E') )
return IsNotWaveFile();
// 寻找格式块, 数据块位置及数据长度
pdwEnd = (DWORD *)( (BYTE *) szHeaderData+dwLength - 4 );
BOOL m_bend = FALSE;
int nOffset = -1;
// pdw文件没有指到末尾并且没有获取到声音数据时继续;
while ( (pdw < pdwEnd) && (!m_bend) )
{
dwType = *pdw++;
dwLength = *pdw++;
switch (dwType)
{
// 如果为"fmt"标志
case mmioFOURCC('f', 'm', 't', ' '):
if (dwLength < sizeof (WAVEFORMAT) )
return IsNotWaveFile();
memcpy ( &wf, pdw, sizeof (WAVEFORMATEX) );
break;
// 如果为"data"标志
case mmioFOURCC('d', 'a', 't', 'a'):
if ( dwPCMDataSize < 1 )
{
dwPCMDataSize = dwLength;
// 获取声音数据块的长度
m_bend = TRUE;
nOffset = ( (DWORD)pdw - (DWORD)szHeaderData );
}
break;
}
pdw = (DWORD *)((BYTE *) pdw + ((dwLength + 1)&~1));//修改pdw指针,继续循环
}
if ( !m_bend ) return IsNotWaveFile ();
return nOffset;
}
BOOL CWaveStereo2Mono::SaveWaveFileHeader ( IN WAVEFORMATEX &wf, IN DWORD dwFileSize, IN DWORD dwPCMDataSize, IN CFile &File )
{
File.Write ( "RIFF", 4 );
File.Write ( &dwFileSize, sizeof(DWORD) );
File.Write ( "WAVEfmt ", 8 );
DWORD dwLength = sizeof(WAVEFORMATEX);
File.Write ( &dwLength, sizeof(DWORD) );
File.Write ( &wf, sizeof(WAVEFORMATEX) );
File.Write ( "data", 4 );
File.Write ( &dwPCMDataSize, sizeof(DWORD) );
return TRUE;
}
int CWaveStereo2Mono::IsNotWaveFile()
{
#ifdef _DEBUG
AfxMessageBox ( "Is not wave file"
);
#endif
return -1;
}
BOOL CWaveStereo2Mono::CopyChannelData (
IN LPVOID lpData, // 需要处理的PCM数据
IN int nSize, // PCM数据长度
IN int nSampleSize, // 取样大小
OUT LPVOID lpLeftData, // 保存左声道数据缓冲,该缓冲不能小于nSize的一半,最好是和nSize相等
IN OUT int &nLeftSize,
OUT LPVOID lpRightData, // 同上,保存右声道数据
IN OUT int &nRightSize
)
{
if ( !lpData || nSize < 1 || nLeftSize < nSize || nRightSize < nSize )
return FALSE;
ASSERT ( lpLeftData!=NULL && AfxIsValidAddress(lpLeftData,nLeftSize,TRUE));
ASSERT ( lpRightData!=NULL && AfxIsValidAddress(lpRightData,nRightSize,TRUE));
char *pPtr1 = (char*)lpData;
char *pPtr2 = NULL;
int nCountLeft = 0, nCountRight = 0;
for ( int i=0;
i<nSize;
i+=2*nSampleSize )
{
memcpy ( (char*)lpLeftData+nCountLeft, pPtr1, nSampleSize );
nCountLeft += nSampleSize;
pPtr2 = pPtr1 + nSampleSize;
if ( (DWORD)pPtr2 - (DWORD)lpData >= (DWORD)nSize )
break;
memcpy ( (char*)lpRightData+nCountRight, pPtr2, nSampleSize );
nCountRight += nSampleSize;
pPtr1 = pPtr2+nSampleSize;
}
nLeftSize = nCountLeft;
nRightSize = nCountRight;
return TRUE;
}
//
// 提取单声道文件从立体声文件中
// return : --------------------------------------------------------------------------
// 1 - 成功
// 0 - 失败
// -1 - 用户放弃
//
int CWaveStereo2Mono:
ickupStereoFile(IN LPCTSTR lpszWaveFileName/*=NULL*/)
{
CString csFileName_Src = GetFileName ( TRUE, lpszWaveFileName );
if ( csFileName_Src.GetLength() < 4 ) return -1;
CString csFileName_Left = csFileName_Src;
csFileName_Left.Insert ( csFileName_Src.GetLength() - 4, "_Left"
);
CString csFileName_Right = csFileName_Src;
csFileName_Right.Insert ( csFileName_Src.GetLength() - 4, "_Right"
);
// 先读取头信息
WAVEFORMATEX wf = {0};
DWORD dwPCMDataSize = 0;
int nOffset = GetWaveFileHeader ( wf, dwPCMDataSize, csFileName_Src );
if ( nOffset < 1 )
return 0;
// 不是立体声wave文件
if ( wf.nChannels != 2 )
{
#ifdef _DEBUG
AfxMessageBox ( "Is not stereo wave file"
);
#endif
return 0;
}
char szTempData[1024] = {0}, szTempData_Left[1024] = {0}, szTempData_Right[1024] = {0};
CFile File, File_Left, File_Right;
TRY
{
File.Open ( csFileName_Src, CFile::modeRead | CFile::shareDenyNone );
File_Left.Open ( csFileName_Left, CFile::modeWrite | CFile::modeCreate );
File_Right.Open ( csFileName_Right, CFile::modeWrite | CFile::modeCreate );
// 先写头数据
SaveWaveFileHeader ( wf, 0, 0, File_Left );
SaveWaveFileHeader ( wf, 0, 0, File_Right );
// 将文件指针移到PCM数据处
File.Seek ( nOffset, CFile::begin
);
// 循环读取文件数据块进行处理
UINT nReadSize = 0;
DWORD nSampleSize = wf.wBitsPerSample / 8;
DWORD dwPCMDataSize_Left = 0, dwPCMDataSize_Right = 0;
while ( (nReadSize = File.Read ( szTempData, sizeof(szTempData) )) > 0 )
{
int nLeftSize = sizeof(szTempData_Left);
int nRightSize = sizeof(szTempData_Right);
VERIFY ( CopyChannelData ( szTempData, nReadSize, nSampleSize, szTempData_Left, nLeftSize, szTempData_Right, nRightSize ) );
File_Left.Write ( szTempData_Left, nLeftSize );
File_Right.Write ( szTempData_Right, nRightSize );
dwPCMDataSize_Left += nLeftSize;
dwPCMDataSize_Right += nRightSize;
}
// 修正头信息
wf.nChannels = 1;
wf.nAvgBytesPerSec /= 2;
wf.nBlockAlign /= 2;
DWORD dwFileSize = dwPCMDataSize_Left + MIN_WAVE_HEADER_SIZE;
File_Left.SeekTobegin
();
SaveWaveFileHeader ( wf, dwFileSize, dwPCMDataSize_Left, File_Left );
File_Right.SeekTobegin
();
dwFileSize = dwPCMDataSize_Right + MIN_WAVE_HEADER_SIZE;
SaveWaveFileHeader ( wf, dwFileSize, dwPCMDataSize_Right, File_Right );
}
CATCH ( CFileException, e )
{
#ifdef _DEBUG
afxDump << "Handle file failed."
<< e->m_cause << "/n";
#endif
return 0;
}
END_CATCH
File.Close ();
File_Left.Close ();
File_Right.Close ();
#ifdef _DEBUG
::ShellExecute ( NULL,_T("open"
, csFileName_Right, NULL,NULL,SW_SHOW );
#endif
return 1;
}