怎样抓CD音轨存为WMA文件

前言:

        在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!!!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值