2011年03月05日

CD-DAの再生 第1部



コンパクトディスク(CD)音源データは、音楽等の音声を収めるCD-DA (Compact Disc Digital Audio)という規格で書かれています。
CD-DAのデータ構造は、開始トラックから終了トラックまで、音源データセクタ単位で連続して書かれており、実際に曲と曲との区切りというものは存在しません。
そのため曲単位毎に再生する場合には、その曲に相当するトラックの開始時間(MSF単位)からセクタ番号を割り出し、そこに書かれた音源データDeviceIoControl関数を使って直読みしながら、WAVEデータとして再生します。



DeviceIoControl関数を使用するためにWindows Driver Device Kit (DDK)が必要になりますが、現在はWindows Driver Kit (WDK)に統合されて、以下のサイトから入手できますがCDROMに焼かないと使えないようです。

Microsoft Download Center Windows Driver Kit Version 7.1.0

CDDAをDeviceIoControl関数を使って読み込むのに必要なヘッダーファイルのインクルード定義です。

#include "devioctl.h"
#include "ntddcdrm.h"
#include "WaveFile.h" //「WAVEファイルの再生」で作成したヘッダーファイルです。


IOCTL_CDROM_READ_TOCファンクションでは得られないトラックの終了時間セッション情報を補うために、CDROM_SESSION構造体CDDA_TOC構造体を独自に定義しました。

//TRACKINFO構造体
typedef struct _TRACKINFO
{
BYTE Control; //トラックの属性(詳しくは「NCITS XXX T10/1363-D Revision - 02A」の「Table 337」を参照して下さい。)
BYTE Addr; //Q Sub-Channel属性(詳しくは「NCITS XXX T10/1363-D Revision - 02A」の「Table 336」を参照して下さい。)
BYTE Session; //所属するセッション番号
BYTE Address[4]; //開始セクタ位置
} TRACKINFO;

//CDDA_TOC構造体
typedef struct _CDDA_TOC
{
BYTE FirstTrack; //開始トラック
BYTE LastTrack; //終了トラック
//トラック毎の情報
TRACKINFO TrackData[MAXIMUM_NUMBER_TRACKS];
} CDDA_TOC;

//SESSIONINFO構造体
typedef struct _SESSIONINFO
{
BYTE SessionStart; //セッションの開始トラック番号
BYTE SessionStop; //セッションの終了トラック番号
DWORD SessionFAD; //セッションの開始セクタ番号
DWORD SessionEndFAD; //セッションの終了セクタ番号
} SESSIONINFO;

//CDROM_SESSION構造体
typedef struct _CDROM_SESSION
{
BYTE FirstSession; //最初のセッション番号
BYTE LastSession; //最後のセッション番号
//セッション毎の情報
SESSIONINFO sessionInfo[MAXIMUM_NUMBER_TRACKS];
} CDROM_SESSION;


cdaファイルの情報を格納するCDAFORMAT構造体を独自に定義しました。

//CDAFORMAT構造体
typedef struct tagCDAFORMAT
{
WORD dwFileVersion; //cdaファイルバージョン。(今のところ1)
WORD dwNumberOfTrack;//トラック番号
DWORD dwSerialNumber; //CDのシリアル番号
DWORD dwStartHsg; //開始セクタ番号
DWORD dwLengthHsg; //セクタ数
DWORD dwStartRedBook; //トラックの開始位置(レッドブック)
DWORD dwLengthRedBook;//トラックの長さ (レッドブック)
}CDAFORMAT,*LPCDAFORMAT;


CD-DAのセクタ当りのバイト数と1秒間にCD-DAが読み出すセクタ数を定義します。

//CD-DAのセクタ当りのバイト数
#define RAW_SECTOR_SIZE 2352
//1秒間にCD-DAが読み出すセクタ数
#define CD_BLOCKS_PER_SECOND 75


Msf時間からセクタ番号を算出するマクロ定義です。

//Msf時間からセクタ番号を算出します。
#define MSF2FAD(msf) (((msf[1])*(CD_BLOCKS_PER_SECOND*60))+((msf[2])*CD_BLOCKS_PER_SECOND)+(msf[3]))


CD-DA読み込みに使用するstdcall関数です。

//セクター番号からMsf時間を算出します。
void __stdcall _FAD2MSF(DWORD FAD, UCHAR* lpMsf)
{
ASSERT(::AfxIsValidAddress(lpMsf,sizeof(UCHAR)*3));
lpMsf[0]=(UCHAR)(FAD/75/60);
FAD-=(lpMsf[0]*75*60);
lpMsf[1]=(UCHAR)(FAD/75);
FAD-=(lpMsf[1]*75);
lpMsf[2]=(UCHAR)FAD;
}

//CDDAのタイトル名の作成
CString __stdcall _FillCDDATitle(UINT track)
{
if ((track<1)||(track>99)) return FALSE;
CString str;
str.Format(_T("Track%02d"),track);
str.ReleaseBuffer();
return str;
}

//CDDAのパス名の作成
CString __stdcall _FillCDDAPath(TCHAR tchDrive,UINT track)
{
CString strTitle=_FillCDDATitle(track);
if (strTitle.IsEmpty()) return FALSE;
CString str;
str.Format(_T("%c:\\%s.cdda"),tchDrive,strTitle.GetBuffer());
str.ReleaseBuffer();
return str;
}

//入力パス名を解析して、ドライブ名、トラック番号、拡張子文字列を取得します。
BOOL __stdcall _ParseCDDAPathName(LPCTSTR lpszPathName,TCHAR* pchDrive,UINT* lpTrack,CString& strExt)
{
ASSERT(::AfxIsValidAddress(pchDrive,sizeof(TCHAR),TRUE));
ASSERT(::AfxIsValidAddress(lpTrack,sizeof(UINT),TRUE));

TCHAR tszDrive[]=_T("C:\\");
TCHAR tchDrive=0;
UINT track=0;
LPTSTR lpszExt=strExt.GetBufferSetLength(16);
int nRes=::_stscanf(lpszPathName,_T("%c:\\Track%2d%s"),&tszDrive[0],&track,lpszExt);
if ((nRes!=3)||
(::GetDriveType(tszDrive)!=DRIVE_CDROM)||
((track<1)&&(track>99))||
((strExt!=_T(".cdda"))&&(strExt!=_T(".cda"))))
return FALSE;
*pchDrive=tszDrive[0];
*lpTrack=track;
return TRUE;
}

//引数で指定されたドライブ文字から順にドライブを検索して、
//CDROMのドライブ番号を取得します。

TCHAR __stdcall _FindCDDrive(TCHAR tchFirst)
{
TCHAR tszDrive[]=_T("C:\\");

if (('a'<=tchFirst)&&('z'>=tchFirst)) tchFirst=tchFirst-'a'+'A';
if (('A'<=tchFirst)&&('Z'>=tchFirst)) tszDrive[0]=tchFirst;

while(tszDrive[0]<='Z'){
if (::GetDriveType(tszDrive)==DRIVE_CDROM) return tszDrive[0];
else tszDrive[0]++;
}
return FALSE;
}


CD-DAの再生をWAVEファイルと同様操作にするために、CWaveFileクラスを基底クラスにしたCDDAFileクラスを独自に定義しました。

//CDDAFileクラス
class CDDAFile : public CWaveFile
{
public:
//コンストラクタ
CDDAFile(CWnd* pWndOwner=NULL);
//デストラクタ
virtual ~CDDAFile();

//セクタ情報の初期化
void Init();
//演奏を停止して、セクタ情報を初期化します。
virtual void Delete();

//オーバーライド関数-------------------------------------
//CDROMデバイスがオープンされているかどうか?

virtual BOOL IsCDDAFile(){return (m_hDrive!=INVALID_HANDLE_VALUE);};
//読み込み中のデバイスハンドルの有効/無効を基底クラスに返します。
virtual BOOL IsOpen(){return (IsCDDAFile())? TRUE:CWaveFile::IsOpen();};

//CDDA音源データのストリーム読み込み
virtual UINT StreamIn(LPVOID pBuffer,UINT nBufSize,UINT* pIndexSample);
//一時停止します。
virtual BOOL Pause();
//総演奏予定時間を取得します。
virtual UINT GetTotalTime();
//指定トラックの演奏時間文字列を返します。(mm:ss:dddms)
CString GetTimeString(UINT nTrack);

//CD-DA操作用関数----------------------------------------
//CDROMデバイスのオープン

BOOL OpenCDDA(TCHAR tchDrive='C');
//CDROMデバイスのクローズ
BOOL CloseCDDA();
//現在のセクタ位置からCD-DA音源データを読み込みます。
// pBuffer:音源データを書き込むバッファ
// nBufSize:バッファサイズ(2352xNバイトになるようにして下さい。)

UINT ReadCDDA(PBYTE pBuffer,UINT nBufSize);
//ディスクチェンジ許可/禁止
BOOL PreventMediaRemoval(BOOL bPrevent);
//CDROMのドアを閉める
BOOL CloseDoor();
//CDROMのドアを開ける
BOOL OpenDoor();
//アクセス可能かどうか
BOOL IsReady();
//CDROMを停止する
BOOL StopCDDA();

//cdaファイルを開いてReadCDAFormat関数を呼び出します。
BOOL LoadCDA(LPCTSTR lpszFileName);
//cdaファイルからCDAFORMAT構造体を読み込みます。
BOOL ReadCDAFormat(HMMIO hmmio);

//TOCの読み込み
BOOL ReadTOC();
//CD-DAを開いてTOCとCDTextを読み込みます。
// tchDrive:CDROMのドライブ番号

BOOL LoadCDDA(TCHAR tchDrive=0);

//CDDAのセッション番号の取得
// 0:CDDAセッションが見つかりません。

BOOL GetCDDASession(){return m_sessionCDDA;};
//トラック範囲の取得
BOOL GetTrackRange(LPDWORD lpdwFirstTrack,LPDWORD lpdwLastTrack,UINT session);
//セクター範囲の取得
BOOL GetSectorRange(UINT track,UINT* lpStartSector,UINT* lpStopSector);

//CDテキスト-------------------------------------------
//CDテキストを格納する文字列リスト
// m_strText[ 0]:アルバム名[ 1]:演奏者 [ 2]:作詞家 [ 3]:作曲者
// [ 4]:編曲者 [ 5]:メッセージ[ 6]:ディスクID[ 7]:ジャンル
// [ 8]:TOC情報 [ 9]:TOC情報2 [10]~[13]予約
// [14]:UPC EAN [15]:サイズ情報

CStringArray m_listCDText[16];

//CDテキストの読み込み
// retry:読み込みに失敗した場合のリトライ回数。

BOOL ReadCDText(int retry=5);
//CDテキストの有無
BOOL HasCDText(){return (m_listCDText[0].GetSize()>0);};
//指定番号/トラックのCDテキスト文字列の取得
LPCTSTR GetCDTextString(UINT index,UINT uTrack);
//指定トラックのアルバム名(曲名)の取得
LPCTSTR GetAlbumName(UINT uTrack){return GetCDTextString(0,uTrack);};
//指定トラックのアーティスト名の取得
LPCTSTR GetArtistName(UINT uTrack){return GetCDTextString(1,uTrack);};

//CDDB(FreeDB)-----------------------------
//CDDB(FreeDB)へ問い合わせ
// 戻り値:取得した情報の個数

int CDDBQuery();
//CDDB(FreeDB)の読み込み
// indexGenre:選択する文字列に対応するリスト(m_strGenre)内の番号

BOOL CDDBRead(int indexGenre);
// lpszGenre:ジャンル文字列
// lpszDiscID:ディスクID文字列(16進8桁表記でa~fは小文字)

BOOL CDDBRead(LPCTSTR lpszGenre,LPCTSTR lpszDiscID);

//CDDB(FreeDB)への問い合わせに必要な情報
CString m_strURL; //問い合わせ先のURL
CString m_strUser; //ユーザー名
CString m_strServer; //サーバー名
CString m_strSystem; //システム名
CString m_strVersion; //バージョン名
int m_nProtocol; //プロトコル番号

//CDDB(FreeDB)の問い合わせで得られた文字列
CStringArray m_listGenre; //入手できるジャンルのリスト
CString m_strCDDB; //送られてきた文字列
CString m_strYear; //CDのリリース年
CStringArray m_listExtd; //拡張データ
CStringArray m_listExtt; //拡張トラックデータ
CString m_strPlayOrder;//演奏手順

protected:
//オーバーライド関数-------------------------------------
//指定ファイル名に相当するトラックをオープンします。
// lpszFileName:ドライブ番号:\Track??.cda(cdaファイルを読み込みます。)
// :ドライブ番号:\Track??.cdda(cdaファイルがない)
// :上記以外はCWaveFile::ReadFile関数を呼び出します。

virtual BOOL ReadFile(LPCTSTR lpszFileName);

//基底クラスに読み込み用のバッファサイズを戻します。
virtual UINT GetSizeOfLoadBuffer(){
return (IsCDDAFile())?RAW_SECTOR_SIZE*8:CWaveOut::GetSizeOfLoadBuffer();
};
//基底クラスに保存バッファサイズを戻します。
virtual UINT GetSizeOfSaveBuffer(){
return (IsCDDAFile())?RAW_SECTOR_SIZE*8:CWaveFile::GetSizeOfSaveBuffer();
};

//CD-DA操作用変数-----------------------------
public:
TCHAR m_tchDrive; //CDROMのドライブ番号
HANDLE m_hDrive; //CDROMのデバイスハンドル
CDROM_SESSION m_session; //CDROMセッション情報
CDDA_TOC m_TOC; //CDDA TOC
UINT m_sessionCDDA; //CDDAデータのあるセッション番号
CDAFORMAT m_cdafmt; //cdaファイル情報

protected:
UINT m_nStartSector; //開始セクタ番号
UINT m_nStopSector; //終了セクタ番号
UINT m_indexSector; //現在のセクタ番号
UINT m_nTotalSector; //現在のトラックのセクタ数

CByteArray m_byteCache; //前回読み込んだデータの残りを一時保存するキャッシュ
};


CDDAFileクラスのインライン関数です。

//CDROMのドアを閉める
inline BOOL CDDAFile::CloseDoor()
{
if (m_hDrive==INVALID_HANDLE_VALUE) return FALSE;
DWORD BytesReturned=0;
return ::DeviceIoControl(m_hDrive,IOCTL_CDROM_LOAD_MEDIA,NULL,0,NULL,0,&BytesReturned,0);
}

//CDROMのドアを開ける
inline BOOL CDDAFile::OpenDoor()
{
if (m_hDrive==INVALID_HANDLE_VALUE) return FALSE;
DWORD BytesReturned=0;
return ::DeviceIoControl(m_hDrive,IOCTL_CDROM_EJECT_MEDIA,NULL,0,NULL,0,&BytesReturned,0);
}

//アクセス可能かどうか
inline BOOL CDDAFile::IsReady()
{
if (m_hDrive==INVALID_HANDLE_VALUE) return FALSE;
DWORD BytesReturned=0;
return ::DeviceIoControl(m_hDrive,IOCTL_CDROM_CHECK_VERIFY,NULL,0,NULL,0,&BytesReturned,0);
}

//CDROMを停止する
inline BOOL CDDAFile::StopCDDA()
{
if (m_hDrive==INVALID_HANDLE_VALUE) return FALSE;
DWORD BytesReturned=0;
return ::DeviceIoControl(m_hDrive,IOCTL_CDROM_STOP_AUDIO,NULL,0,NULL,0,&BytesReturned,0);
}

//指定番号/トラックのCDテキスト文字列の取得
inline LPCTSTR CDDAFile::GetCDTextString(UINT index,UINT uTrack)
{
//CDテキストがない場合は、FALSEを返して終了します。
if (!HasCDText()) return FALSE;
if ((uTrack>m_TOC.LastTrack)||
((UINT)m_listCDText[index].GetSize()-1<uTrack)||
(m_listCDText[index][uTrack].IsEmpty()))
return FALSE;
return m_listCDText[index][uTrack];
}


CDDAFileクラスのコンストラクタ/デストラクタです。

//コンストラクタ
CDDAFile::CDDAFile(CWnd* pWndOwner)
:CWaveFile(pWndOwner)
{
//CDROMのドライブ番号を取得しておきます。
m_tchDrive=_FindCDDrive();
m_hDrive=INVALID_HANDLE_VALUE;
::ZeroMemory(&m_TOC,sizeof(CDDA_TOC));
::ZeroMemory(&m_session,sizeof(CDROM_SESSION));
m_sessionCDDA=0;

m_strURL =_T("http://www.freedb2.org/~cddb/cddb.cgi");
m_strURL =_T("http://freedbtest.dyndns.org/~cddb/cddb.cgi");//日本語版
m_strUser =_T("Hiroshi");
m_strServer =_T("MultiMediaFileFormat");
m_strSystem =_T("CDDAFileTest02");
m_strVersion=_T("v1.0");
m_nProtocol =1;

Init();
}

//デストラクタ
CDDAFile::~CDDAFile()
{
//演奏を停止します。
Delete();
//CDROMを停止します。
StopCDDA();
//CDROMデバイスをクローズします。
if (m_hDrive!=INVALID_HANDLE_VALUE) CloseCDDA();
}


CDDAFileクラスのメンバ関数です。

//セクタ情報の初期化
void CDDAFile::Init()
{
m_nStartSector=0;
m_nStopSector =0;
m_indexSector =0;
m_nTotalSector=0;
::ZeroMemory(&m_cdafmt,sizeof(CDAFORMAT));
m_byteCache.RemoveAll();
}

//演奏を停止して、セクタ情報を初期化します。
void CDDAFile::Delete()
{
//演奏を停止します。
CWaveFile::Delete();
//セクタ情報の初期化
Init();
}

//CDDAのオープン
BOOL CDDAFile::OpenCDDA(TCHAR tchDrive)
{
//ディスクデバイスをオープンするための文字列です。
TCHAR tszDrive[]=_T("\\\\.\\C:");

//引数のCDROMのドライブ番号がない場合、CDROMのドライブ番号を検索取得します。
tchDrive=_FindCDDrive(tchDrive);
if (!tchDrive) return FALSE;

tszDrive[4]=tchDrive;
//CDROMデバイスをオープンします。
HANDLE hDrive=::CreateFile(tszDrive,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,
FILE_ATTRIBUTE_READONLY|FILE_FLAG_SEQUENTIAL_SCAN,(HANDLE)NULL);
//CDROMのデバイスハンドルが無効なら、FALSEを返して終了します。
if(hDrive==INVALID_HANDLE_VALUE) return FALSE;

//CDROMのデバイスハンドルをメンバー変数に格納します。
if (m_hDrive!=INVALID_HANDLE_VALUE) CloseCDDA();
m_hDrive=hDrive;
//ディスクチェンジを許可します。
PreventMediaRemoval(FALSE);
//ドライブ番号をメンバー変数に格納します。
m_tchDrive=tchDrive;
return TRUE;
}

//CDDAのクローズ
BOOL CDDAFile::CloseCDDA()
{
//ディスクチェンジを許可します。
PreventMediaRemoval(FALSE);
//CDROMデバイスをクローズします。
BOOL bResult=::CloseHandle(m_hDrive);
m_hDrive=INVALID_HANDLE_VALUE;
return bResult;
}

//現在のセクタ位置からCD-DA音源データを読み込みます。
// pBuffer:音源データを書き込むバッファ
// nBufSize:バッファサイズ(2352xNバイトになるようにして下さい。)

UINT CDDAFile::ReadCDDA(PBYTE pBuffer,UINT nBufSize)
{
//バッファサイズから読み込むセクタ数を算出します。
UINT nSector=nBufSize/RAW_SECTOR_SIZE;
//現在の読み込みセクタの絶対位置を算出します。
UINT sectorPos=m_nStartSector+m_indexSector;
//現在の読み込みセクタの絶対位置が最終セクタ位置を超えている場合は、エラー終了します。
if (sectorPos>m_nStopSector) return FALSE;
//残りのセクタ数を算出します。
UINT nRemain=m_nStopSector-sectorPos;
//読み込むセクタ数が残りセクタ数より多い場合は、
//残りセクタ数を読み込むセクタ数に代入します。

if (nSector>nRemain) nSector=nRemain;
//セクタ数が1に満たないなら、FALSEを返して終了します。
if (nSector<1) return FALSE;

//RAW_READ_INFO構造体を作成します。
RAW_READ_INFO rawReadInfo;
rawReadInfo.TrackMode=CDDA;
rawReadInfo.SectorCount=nSector;
rawReadInfo.DiskOffset.QuadPart=sectorPos*2048;

//CD-DAの音源データを読み込みます。
DWORD BytesReturned=0;
BOOL bResult=::DeviceIoControl(m_hDrive,IOCTL_CDROM_RAW_READ,&rawReadInfo,sizeof(RAW_READ_INFO),
pBuffer,nBufSize,&BytesReturned,0);
if (!bResult) return FALSE;

//トラック内の現在位置を、読み込んだセクタ数分だけ進めます。
m_indexSector+=rawReadInfo.SectorCount;
//読み込んだバイト数を返して終了します。
return rawReadInfo.SectorCount*RAW_SECTOR_SIZE;
}

//ディスクチェンジ許可/禁止
BOOL CDDAFile::PreventMediaRemoval(BOOL bPrevent)
{
//CDROMのデバイスハンドルが無効なら、FALSEを返して終了します。
if (m_hDrive==INVALID_HANDLE_VALUE) return FALSE;
PREVENT_MEDIA_REMOVAL pmr;
pmr.PreventMediaRemoval=(bPrevent)?1:0;
DWORD BytesReturned=0;
return ::DeviceIoControl(m_hDrive,IOCTL_CDROM_MEDIA_REMOVAL,&pmr,
sizeof(PREVENT_MEDIA_REMOVAL),NULL,0,&BytesReturned,0);
}

//一時停止する
BOOL CDDAFile::Pause()
{
//CWaveOut::Pause関数を先に実行します。
BOOL bResult=CWaveOut::Pause();
//CDROMのデバイスハンドルが無効なら、結果を返して終了します。
if (m_hDrive==INVALID_HANDLE_VALUE) return bResult;

DWORD BytesReturned=0;
//一時停止中なら、CDROMを一時停止します。
if (IsPausing())
::DeviceIoControl(m_hDrive,IOCTL_CDROM_PAUSE_AUDIO,0,0,0,0,&BytesReturned,0);
//それ以外なら、CDROMを復帰します。
else
::DeviceIoControl(m_hDrive,IOCTL_CDROM_PAUSE_AUDIO,0,0,0,0,&BytesReturned,0);
return bResult;
}

//総演奏予定時間を取得します。
UINT CDDAFile::GetTotalTime()
{
//セクタ数が無効なら、基底クラスの関数を呼び出します。
if (m_nTotalSector==0)
return CWaveFile::GetTotalTime();
UCHAR Msf[3]={0};
//セクター番号からMsf時間を算出します。
_FAD2MSF(m_nTotalSector,Msf);
return Msf[0]*60000+Msf[1]*1000+Msf[2]*1000/75;
}

//指定トラックの演奏時間文字列を返します。(mm:ss:dddms)
CString CDDAFile::GetTimeString(UINT nTrack)
{
ASSERT(m_TOC.FirstTrack<=nTrack);
ASSERT(m_TOC.LastTrack=nTrack);

UINT startSector,stopSector;
DWORD FAD=(GetSectorRange(nTrack,&startSector,&stopSector))?stopSector-startSector:0;

UCHAR Msf[3]={0};
_FAD2MSF(FAD,Msf);
CString str;
str.Format(_T("%02d:%02d:%03d"),Msf[0],Msf[1],Msf[2]*1000/75);
str.ReleaseBuffer();
return str;
}

//セクター範囲の取得
BOOL CDDAFile::GetSectorRange(UINT track,UINT* lpStartSector,UINT* lpStopSector)
{
if ((m_TOC.FirstTrack>track)||(m_TOC.LastTrack<track)) return FALSE;

//開始セクタ番号を算出します。
UINT startSector=MSF2FAD(m_TOC.TrackData[track-1].Address)-150;
//終了セクタ番号を算出します。
UINT stopSector =MSF2FAD(m_TOC.TrackData[track].Address)-150;

//トラック番号が所属するセッションの最終トラック番号と一致する場合は、
//そのセッションの最終セクタ番号を終了セクタ番号とします。

int sessionNumber=m_TOC.TrackData[track-1].Session;
if (track==m_session.sessionInfo[sessionNumber-1].SessionStop)
stopSector=m_session.sessionInfo[sessionNumber-1].SessionEndFAD;

//引数の開始トラックへのポインタが有効な場合は、開始トラック番号を取り込みます。
if (AfxIsValidAddress(lpStartSector,sizeof(UINT))) *lpStartSector=startSector;
//引数の終了トラックへのポインタが有効な場合は、終了トラック番号を取り込みます。
if (AfxIsValidAddress(lpStopSector,sizeof(UINT))) *lpStopSector=stopSector;
return TRUE;
}


//cdaファイルを開いてReadCDAFormat関数を呼び出します。
BOOL CDDAFile::LoadCDA(LPCTSTR lpszFileName)
{
//WAVEファイルのオープン
HMMIO hmmio=::mmioOpen((LPTSTR)lpszFileName,NULL,MMIO_READ);
if(!hmmio) return FALSE;
BOOL bResult=ReadCDAFormat(hmmio);
::mmioClose(hmmio,0);
return bResult;
}

//cdaファイルからCDAFORMAT構造体を読み込みます。
BOOL CDDAFile::ReadCDAFormat(HMMIO hmmio)
{
ASSERT(hmmio);

//RIFFチャンクに進入してCDDAチャンクを探します。
MMCKINFO mmckRiff;
mmckRiff.fccType=::mmioStringToFOURCC("CDDA", 0);
MMRESULT mmr=::mmioDescend(hmmio,&mmckRiff,NULL,MMIO_FINDRIFF);
//CDDAチャンクがなければFALSEを返して終了する。
if (mmr!=MMSYSERR_NOERROR) return FALSE;

//fmtチャンクに進入します。
mmckRiff.ckid=::mmioStringToFOURCC("fmt ",0);
mmr=::mmioDescend(hmmio,&mmckRiff,NULL,MMIO_FINDCHUNK);
if (mmr!=MMSYSERR_NOERROR) return FALSE;

//fmtチャンクを読み込む。
CDAFORMAT cdafmt;
if (mmckRiff.cksize<sizeof(CDAFORMAT)) return FALSE;
LONG size=::mmioRead(hmmio,(HPSTR)&cdafmt,sizeof(CDAFORMAT));
if (size!=sizeof(CDAFORMAT)) return FALSE;

//CDAFORMAT構造体の内容をメンバー変数に保存します。
::CopyMemory(&m_cdafmt,&cdafmt,sizeof(CDAFORMAT));
m_nStartSector=m_cdafmt.dwStartHsg;
m_nStopSector =m_nStartSector+m_cdafmt.dwLengthHsg;
m_nTotalSector=m_cdafmt.dwLengthHsg;

//fmtチャンクから退出します。
::mmioAscend(hmmio,&mmckRiff,0);
return TRUE;
}






posted by ひろし at 20:52| Comment(0) | CDDAファイルフォーマット | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
×

この広告は180日以上新しい記事の投稿がないブログに表示されております。