前言:
在CSDN上也耗了很久,但是每次都是看人家的文章或帖子,奉行的都是“拿来主义”,从来没有自己的文章发表,有了blog,我也有机会了,但是没有什么含金量的文章,也不知写哪方面的东西,前段时间在论坛上发了个帖子,主要是CD转WMA方面的,但是帖子很快就沉了,因为没有几个人知道。经过自己的努力,终于做出来了,鉴于很多人还不知道怎么作,所以斗胆在此乱发一通,希望高手还望见谅!
正文:
网上有CD to WAV的文章和源码。但wav和wma还是有很大不同,我是参考了怎样读CD信息的相关代码,然后自己实现了一个CCDAudioInput的类,该类封装了CD的信息和读取CD数据的相关操作。主要的难点是怎样把CD数据存入wma文件里,因为wma是Microsoft的asf文件格式,所以我看了Microsoft的windows media SDK和里面的samples。windows media SDK主要是以com组件实现的开发库,个人觉得只要大概了解com应该问题不大。为了方便Wma的操作我设计了一个CWmvWriter的类,里面包括了对wma文件的写入操作以及一些初始化工作。有了这两个类,我们就可以轻而易举的实现我们的功能。最后我们用一个button调用我么CD to wma的功能。在调用之前一定要现调用 hr = CoInitialize( NULL )用于初始化。下面把CCAudioInput和CWmvWriter类的主要部分贴出。
CCAudioInput.h:
#pragma once
#include <ntddcdrm.h> // NT4DDK库
#include <winioctl.h>
#include <Mmreg.h>
// CCDAudioInput command target
#define NSECTORS 13
#define UNDERSAMPLING 1
#define CB_CDDASECTOR 2368
#define CB_QSUBCHANNEL 16
#define CB_CDROMSECTOR 2048
#define CB_AUDIO (CB_CDDASECTOR-CB_QSUBCHANNEL)
class CCDAudioInput
{
public:
CStringArray m_Tracks; // the array of audio track
CStringArray m_TrackTitles; // the array of track title
CString m_sArtist; // the cd's artist
CString m_sDrive; // the CD-ROM drive name
HANDLE m_hDevice; // the Device Handle of CD-ROM
CDROM_TOC CdromTOC;
public:
CCDAudioInput(CString &Drive);
CCDAudioInput(const CCDAudioInput &cdInfo);
CCDAudioInput();
virtual ~CCDAudioInput();
// Decide the CDROM if is ready
BOOL IsReady(void);
// Get the end of sector
DWORD GetEndSector(int nTrack);
// Get the start sector
DWORD GetStartSector(int nTrack);
// Read sector data to Buffer from CD-ROM
BOOL ReadSector(int Sector, BYTE Buffer[], int NumSectors);
CCDAudioInput& operator=(const CCDAudioInput &m_cdInfo);
};
CCAudioInput.cpp:
#include "stdafx.h"
#include "CDAudioInput.h"
#include ".\cdaudioinput.h"
// CCDAudioInput
// CCDAudioInput member functions
CCDAudioInput::CCDAudioInput()
{
m_sArtist = "";
m_hDevice = INVALID_HANDLE_VALUE;
m_Tracks.RemoveAll();
}
CCDAudioInput::CCDAudioInput(CString &Drive)
{
m_sDrive = Drive;
m_hDevice = CreateFile("\\\\.\\"+m_sDrive, GENERIC_READ,FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, 0, NULL);
}
CCDAudioInput::CCDAudioInput(const CCDAudioInput &cdInfo)
{
m_sDrive = cdInfo.m_sDrive;
m_hDevice = cdInfo.m_hDevice;
m_sArtist = cdInfo.m_sArtist;
m_Tracks.Copy(cdInfo.m_Tracks);
}
CCDAudioInput::~CCDAudioInput()
{
}
CCDAudioInput& CCDAudioInput::operator=(const CCDAudioInput &m_cdInfo)
{
if(this != &m_cdInfo)
{
m_sDrive = m_cdInfo.m_sDrive;
m_hDevice = m_cdInfo.m_hDevice;
m_sArtist = m_cdInfo.m_sArtist;
m_Tracks.Copy(m_cdInfo.m_Tracks);
}
return *this;
}
// Decide the CDROM if is ready
BOOL CCDAudioInput::IsReady(void)
{
DWORD dwOutBytes;
return (DeviceIoControl(m_hDevice,IOCTL_STORAGE_CHECK_VERIFY, NULL, 0,NULL, 0, &dwOutBytes, (LPOVERLAPPED)NULL));
}
// Get the end of sector
DWORD CCDAudioInput::GetEndSector(int nTrack)
{
return (CdromTOC.TrackData[nTrack].Address[1]*60*75 + CdromTOC.TrackData[nTrack].Address[2]*75 +
CdromTOC.TrackData[nTrack].Address[3])-151;
}
// Get the start sector
DWORD CCDAudioInput::GetStartSector(int nTrack)
{
return (CdromTOC.TrackData[nTrack-1].Address[1]*60*75 + CdromTOC.TrackData[nTrack-1].Address[2]*75 +
CdromTOC.TrackData[nTrack-1].Address[3])-150;
}
BOOL CCDAudioInput::ReadSector(int Sector, BYTE Buffer[], int NumSectors)
{
DWORD dwOutBytes;
RAW_READ_INFO rri;
rri.TrackMode =(TRACK_MODE_TYPE)2;
rri.SectorCount = (DWORD)NumSectors;
rri.DiskOffset.QuadPart = (DWORD64)(Sector*CB_CDROMSECTOR);
if (DeviceIoControl(m_hDevice,IOCTL_CDROM_RAW_READ,&rri, sizeof(rri), Buffer, (DWORD)NumSectors*CB_AUDIO,&dwOutBytes,(LPOVERLAPPED)NULL))
return TRUE;
return FALSE;
}
CWmvWriter.h:
#pragma once
// CWmvWriter command target
#include "wmsdk.h" // windows media SDK header file
#include "wmsdkidl.h" // windows media SDK header file
#include <Mmreg.h>
#include "wmsysprf.h" // windows media SDK header file
#ifndef SAFE_RELEASE
#define SAFE_RELEASE( x ) if ( NULL != x ) { x->Release( ); x = NULL; }
#endif // SAFE_RELEASE
#ifndef SAFE_ARRAYDELETE
#define SAFE_ARRAYDELETE( x ) if( x ) { delete [] x; x = NULL; }
#endif //SAFE_ARRAYDELETE
const LPWSTR wszDefaultConnectionName = L"CDTOWMA";
typedef struct MediaTypeTAG{
WAVEFORMATEX m_pWFX;
GUID m_Type;
}MEDIATYPE, *PMEDIATYPE;
class CWmvWriter : public CObject
{
public:
CWmvWriter();
virtual ~CWmvWriter();
HRESULT Initial(TCHAR* ptszInFile, TCHAR* ptszOutFile, IWMProfile* pProfile);
HRESULT AddAudioStream(IWMProfile * pIWMProfile, DWORD dwSampleRate, DWORD dwChannels, WORD wBitsPerSample, WORD* pwStreamNum, WCHAR ** pwszConnectionName);
HRESULT UpdateProfile(IWMProfile * pProfile);
public:
IWMWriter *m_pWMWriter;
WM_MEDIA_TYPE mt;
// WAVEFORMATEX *m_pWFX;
MEDIATYPE m_MediaType;
HRESULT SetStreamBasics(IWMStreamConfig * pIWMStreamConfig, IWMProfile * pIWMProfile, LPWSTR pwszStreamName, LPWSTR pwszConnectionName, DWORD dwBitrate, WM_MEDIA_TYPE * pmt);
HRESULT CreateEmptyProfile(IWMProfile ** ppIWMProfile);
HRESULT WriteSample(BYTE* pBuffer, DWORD Length);
DWORD m_AudioInput;
DWORD m_MsAudioTime;
};
CWmvWriter.cpp:
// WmvWriter.cpp : implementation file
#include "stdafx.h"
#include "WmvWriter.h"
#include ".\wmvwriter.h"
// CWmvWriter
CWmvWriter::CWmvWriter()
: m_AudioInput(0)
, m_MsAudioTime(0)
{
m_pWMWriter = NULL;
ZeroMemory(&m_MediaType, sizeof(MEDIATYPE));
}
CWmvWriter::~CWmvWriter()
{
}
HRESULT ConvertTCharToWChar( TCHAR * ptszInput, WCHAR ** pwszOutput )
{
int cchOutput = 0;
if( NULL == ptszInput || NULL == pwszOutput )
{
return( E_INVALIDARG );
}
//
// Get output buffer size
//
#ifdef UNICODE
cchOutput = wcslen( ptszInput ) + 1;
#else //UNICODE
cchOutput = MultiByteToWideChar( CP_ACP, 0, ptszInput, -1, NULL, 0 );
if( 0 == cchOutput )
{
return( HRESULT_FROM_WIN32( GetLastError() ) );
}
#endif // UNICODE
*pwszOutput = new WCHAR[ cchOutput ];
if( NULL == *pwszOutput)
{
return( E_OUTOFMEMORY );
}
#ifdef UNICODE
wcsncpy( *pwszOutput, ptszInput, cchOutput );
#else //UNICODE
if( 0 == MultiByteToWideChar( CP_ACP, 0, ptszInput, -1, *pwszOutput, cchOutput ) )
{
SAFE_ARRAYDELETE( *pwszOutput );
return( HRESULT_FROM_WIN32( GetLastError() ) );
}
#endif // UNICODE
return( S_OK );
}
// CWmvWriter member functions
HRESULT CWmvWriter::Initial(TCHAR* ptszInFile, TCHAR* ptszOutFile, IWMProfile* pProfile)
{
HRESULT hr = S_OK;
if((ptszOutFile == NULL) || (pProfile == NULL))
return E_INVALIDARG;
hr = WMCreateWriter(NULL, &m_pWMWriter);
if(FAILED(hr))
{
AfxMessageBox("Failed to Create WMWriter.");
return (hr);
}
hr = UpdateProfile(pProfile);
if(FAILED(hr))
{
AfxMessageBox("Failed to Call UpdateProfile.");
return (hr);
}
hr = m_pWMWriter->SetProfile(pProfile);
if(FAILED(hr))
{
AfxMessageBox("Failed to Call SetProfile.");
return hr;
}
if( NULL != ptszOutFile )
{
WCHAR * pwszOutFile = NULL;
hr = ConvertTCharToWChar( ptszOutFile, &pwszOutFile );
hr = m_pWMWriter->SetOutputFilename( pwszOutFile );
SAFE_ARRAYDELETE( pwszOutFile );
if(FAILED(hr))
{
AfxMessageBox("Failed to Call SetOutputFilename.");
return (hr);
}
}
return hr;
}
HRESULT CWmvWriter::UpdateProfile(IWMProfile * pProfile)
{
HRESULT hr = S_OK;
if(pProfile == NULL)
return E_INVALIDARG;
m_MediaType.m_Type = WMMEDIATYPE_Audio;
m_MediaType.m_pWFX.wFormatTag=WAVE_FORMAT_PCM ;
m_MediaType.m_pWFX.nSamplesPerSec=48000;
m_MediaType.m_pWFX.wBitsPerSample=16;
m_MediaType.m_pWFX.nChannels=2;
m_MediaType.m_pWFX.cbSize=0;
m_MediaType.m_pWFX.nBlockAlign = m_MediaType.m_pWFX.nChannels*(m_MediaType.m_pWFX.wBitsPerSample/8);
m_MediaType.m_pWFX.nAvgBytesPerSec = m_MediaType.m_pWFX.nSamplesPerSec*m_MediaType.m_pWFX.nBlockAlign;
WORD wStreamNum = 0;
WCHAR *pwszConnectionName;
hr = AddAudioStream( pProfile, m_MediaType.m_pWFX.nSamplesPerSec, m_MediaType.m_pWFX.nChannels
, m_MediaType.m_pWFX.wBitsPerSample, &wStreamNum, &pwszConnectionName);
if(FAILED(hr))
{
AfxMessageBox("Failed to Call AddAudioStream.");
return hr;
}
return hr;
}
HRESULT CWmvWriter::AddAudioStream(IWMProfile * pIWMProfile, DWORD dwSampleRate, DWORD dwChannels,
WORD wBitsPerSample, WORD* pwStreamNum, WCHAR ** pwszConnectionName)
{
HRESULT hr = S_OK;
IWMProfileManager * pIWMProfileManager = NULL;
IWMStreamConfig * pIWMStreamConfig = NULL;
IWMMediaProps * pIMP = NULL;
IWMCodecInfo * pIWMInfo = NULL;
WAVEFORMATEX * pWfx = NULL;
WM_MEDIA_TYPE * pType = NULL;
if((NULL == pIWMProfile) || (NULL == pwStreamNum))
return E_INVALIDARG;
do{
hr = WMCreateProfileManager(&pIWMProfileManager);
if(FAILED(hr)) break;
hr = pIWMProfileManager->QueryInterface(IID_IWMCodecInfo, (void**) &pIWMInfo);
if(FAILED(hr)) break;
DWORD i, j;
DWORD cCodecs;
hr = pIWMInfo->GetCodecInfoCount(WMMEDIATYPE_Audio, &cCodecs);
if(FAILED(hr)) break;
for(i=0; i<cCodecs; i++)
{
DWORD cFormats;
hr = pIWMInfo->GetCodecFormatCount( WMMEDIATYPE_Audio, i, &cFormats );
if( FAILED( hr ) ) break;
// Find a proper format in this codec
for(j=0; j<cFormats; j++)
{
if( NULL != pType )
SAFE_ARRAYDELETE( pType );
DWORD cbType = 0;
hr = pIWMInfo->GetCodecFormat( WMMEDIATYPE_Audio, i, j, &pIWMStreamConfig );
if(FAILED(hr)) break;
SAFE_RELEASE( pIMP );
hr = pIWMStreamConfig->QueryInterface( IID_IWMMediaProps, (void **)&pIMP );
if(FAILED(hr)) break;
hr = pIMP->GetMediaType( NULL, &cbType );
if(FAILED(hr)) break;
pType = (WM_MEDIA_TYPE *) new BYTE[ cbType ];
if( NULL == pType )
{
hr = E_OUTOFMEMORY;
break;
}
hr = pIMP->GetMediaType( pType, &cbType );
if(FAILED(hr)) break;
if( pType->formattype != WMFORMAT_WaveFormatEx )
{
hr = E_FAIL;
break;
}
pWfx = (WAVEFORMATEX *) pType->pbFormat;
if( pWfx->nSamplesPerSec == dwSampleRate && pWfx->nChannels == dwChannels &&
pWfx->wBitsPerSample >= wBitsPerSample )
{
break;
}
SAFE_RELEASE( pIWMStreamConfig );
}
if( FAILED( hr ) || NULL != pIWMStreamConfig )
break;
}
if(FAILED(hr)) break;
if( NULL == pIWMStreamConfig )
{
hr = NS_E_AUDIO_CODEC_NOT_INSTALLED;
break;
}
hr = SetStreamBasics( pIWMStreamConfig,
pIWMProfile,
L"Audio Stream",
L"Audio",
pWfx->nAvgBytesPerSec * 8,
pType );
if( FAILED( hr ) )
{
break;
}
*pwszConnectionName = new WCHAR[ wcslen( wszDefaultConnectionName ) + 4 ];
if( NULL == *pwszConnectionName )
{
hr = E_OUTOFMEMORY;
break;
}
hr = pIWMProfile->AddStream( pIWMStreamConfig );
if( FAILED( hr ) )
{
SAFE_ARRAYDELETE( *pwszConnectionName );
break;
}
hr = pIWMStreamConfig->GetStreamNumber( pwStreamNum );
if( FAILED( hr ) )
{
SAFE_ARRAYDELETE( *pwszConnectionName );
break;
}
//
// each stream in the profile has to have unique connection name
// let's use stream number to create it
//
if( *pwStreamNum > 127 )
{
hr = E_FAIL;
break;
}
swprintf( *pwszConnectionName, L"%s%d", wszDefaultConnectionName, (DWORD)*pwStreamNum );
hr = pIWMStreamConfig->SetConnectionName( *pwszConnectionName );
if( FAILED( hr ) )
{
SAFE_ARRAYDELETE( *pwszConnectionName );
break;
}
hr = pIWMProfile->ReconfigStream( pIWMStreamConfig );
if( FAILED( hr ) )
{
SAFE_ARRAYDELETE( *pwszConnectionName );
break;
}
}
while( FALSE );
SAFE_ARRAYDELETE( pType );
SAFE_RELEASE( pIWMInfo );
SAFE_RELEASE( pIWMStreamConfig );
SAFE_RELEASE( pIMP );
SAFE_RELEASE( pIWMProfileManager );
return( hr );
}
HRESULT CWmvWriter::SetStreamBasics(IWMStreamConfig *pIWMStreamConfig, IWMProfile *pIWMProfile, LPWSTR pwszStreamName,
LPWSTR pwszConnectionName, DWORD dwBitrate, WM_MEDIA_TYPE * pmt)
{
HRESULT hr = S_OK;
IWMMediaProps * pIWMMediaProps = NULL;
IWMStreamConfig * pIWMStreamConfig2 = NULL;
WORD wStreamNum = 0;
if( NULL == pIWMStreamConfig || NULL == pIWMProfile || NULL == pmt )
{
return( E_INVALIDARG );
}
do
{
hr = pIWMProfile->CreateNewStream( pmt->majortype, &pIWMStreamConfig2 );
if( FAILED( hr ) )
{
break;
}
hr = pIWMStreamConfig2->GetStreamNumber( &wStreamNum );
SAFE_RELEASE( pIWMStreamConfig2 );
if( FAILED( hr ) )
{
break;
}
hr = pIWMStreamConfig->SetStreamNumber( wStreamNum );
if( FAILED( hr ) )
{
break;
}
hr = pIWMStreamConfig->SetStreamName( pwszStreamName );
if( FAILED( hr ) )
{
break;
}
hr = pIWMStreamConfig->SetConnectionName( pwszConnectionName );
if( FAILED( hr ) )
{
break;
}
hr = pIWMStreamConfig->SetBitrate( dwBitrate );
if( FAILED( hr ) )
{
break;
}
hr = pIWMStreamConfig->QueryInterface( IID_IWMMediaProps,
(void **) &pIWMMediaProps );
if( FAILED( hr ) )
{
break;
}
hr = pIWMMediaProps->SetMediaType( pmt );
if( FAILED( hr ) )
{
break;
}
}
while( FALSE );
SAFE_RELEASE( pIWMMediaProps );
return( hr );
}
HRESULT CWmvWriter::CreateEmptyProfile(IWMProfile ** ppIWMProfile)
{
HRESULT hr = S_OK;
IWMProfileManager* pIWMProfileManager = NULL;
if( NULL == ppIWMProfile )
{
return( E_POINTER );
}
do
{
//
// Create profile manager
//
hr = WMCreateProfileManager( &pIWMProfileManager );
if( FAILED( hr ) )
{
break;
}
hr = pIWMProfileManager->CreateEmptyProfile( WMT_VER_8_0, ppIWMProfile );
if( FAILED( hr ) )
{
break;
}
}
while( FALSE );
//
// Release all resources
//
SAFE_RELEASE( pIWMProfileManager );
return( hr );
}
HRESULT CWmvWriter::WriteSample(BYTE* pBuffer, DWORD Length)
{
HRESULT hr = S_OK;
if(m_pWMWriter != NULL)
{
INSSBuffer *pSample;
BYTE *pSampleBuff;
HRESULT hr = m_pWMWriter->AllocateSample(Length, &pSample);
if(FAILED(hr))
{
AfxMessageBox("Call AllocateSample Failure!");
return hr;
}
pSample->GetBufferAndLength(&pSampleBuff, &Length);
memcpy(pSampleBuff, pBuffer, Length);
pSample->SetLength(Length);
// Write(pSample);
hr = m_pWMWriter->WriteSample(m_AudioInput, m_MsAudioTime * 10000, 0, pSample);
if(FAILED(hr))
{
AfxMessageBox("Call WriteSample Failure!");
return hr;
}
m_MsAudioTime += MulDiv(Length, 1000, m_MediaType.m_pWFX.nAvgBytesPerSec);
}else AfxMessageBox("Write Sample Fail!");
return hr;
}
类的实现有了,那现在实现button的功能吧,为了方便描述,我这里只是把CD的Track1转为wma。
BYTE Data[CB_AUDIO*NSECTORS];
int StartSector, EndSector, Sectors2Read;
StartSector = cdInfo->GetStartSector(1); // cdInfo是一个CCDAudioInput类指针
EndSector = cdInfo->GetEndSector(1);
m_Progress.SetRange(0, EndSector - StartSector); // m_Progress是进度条
strcat(MusicDir, "Track1.wma"); // MusicDir是输出文件
HRESULT hr = S_OK;
hr = CoInitialize( NULL ); // 记得调用初始化
CWmvWriter m_Writer;
IWMProfile *pProfile;
m_Writer.CreateEmptyProfile(&pProfile);
m_Writer.Initial("", MusicDir, pProfile);
m_Writer.m_pWMWriter->BeginWriting();
for(int sector=StartSector; sector<EndSector; sector+=NSECTORS)
{
Sectors2Read = ( (sector + NSECTORS) < EndSector )?NSECTORS:(EndSector-sector);
if (cdInfo->ReadSector(sector, Data, Sectors2Read))
{
hr = m_Writer.WriteSample(Data, sizeof(Data));
if(FAILED(hr))
{
AfxMessageBox("Failed to WriteSample.");
break;
}
m_Progress.SetPos(m_Progress.GetPos() + Sectors2Read);
}
}
m_Writer.m_pWMWriter->Flush();
m_Writer.m_pWMWriter->EndWriting();
}
结尾:
为了能编译这个程序,你要安装NT4DDK开发包并包含里面的头文件,主要用于对CD音轨的操作,然后还要加入windows media SDK的头文件以及kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib msvcrtd.lib wmvcore.lib winmm.lib odbc32.lib odbccp32.lib 库文件。
好了,今天就到此为止,有什么question,please mailto: ss_liao@163.com, MSN: ss_liao@163.com。大家一起study。Thanks!!!