fc2ブログ

ホーム > CDDAファイルフォーマット > CDDBの読み込み

CDDBの読み込み



CDDB(Compact Disc DataBase)とは、音楽CDの演奏者やアルバム名、曲名などの情報が格納されたデータベースのことで、本章では数あるCDDBサイトのなかでもフリーCDDBであるfreeDBに接続して、その情報を取得する方法を解説します。

読み込み手順は以下の通りです。

  1. CDDAの各トラックの開始位置時間(秒)と総演奏時間、総トラック数からdiscidを算出します。

  2. 算出したdiscidと各トラックの開始セクタ位置、CDDAの終了時間(秒)をパラメータにして「QueryコマンドURL」を作成し、freeDBに問い合わせて目的のCDに該当するジャンルとdiscidを取得します。

  3. 取得したdiscidとジャンルを元に「ReadコマンドURL」を作成し、freeDBに接続して目的のCDの演奏者やアルバム名、曲名などの情報を取得します。






MFCのWinInet APIを使用した、freeDB接続のためのHTTPクライアント関数です。

#include <afxinet.h>

//入力されたURLからHtmlテキストを受信します。
// lpszURL:問い合わせ先のURL文字列
// strHtmls:受信したHtmlテキストを格納するclass CString

BOOL __stdcall _ReadHTTP(LPCTSTR lpszURL,CString& strHtmls)
{
CString strServerName;
CString strObject;
DWORD dwServiceType;
INTERNET_PORT nPort;

//URLを解析します。
if(!AfxParseURL(lpszURL,dwServiceType,strServerName,strObject,nPort))
{
TRACE0("URLの書式が正しくありません。");
return FALSE;
}

//プロトコルをチェックします。
if ((dwServiceType!=AFX_INET_SERVICE_HTTP)&&
(dwServiceType!=AFX_INET_SERVICE_HTTPS))
{
TRACE0("対応していないプロトコルです。");
return FALSE;
}

//リクエストの準備で使うフラグの設定
DWORD dwFlags=INTERNET_FLAG_RELOAD|INTERNET_FLAG_NO_CACHE_WRITE|INTERNET_FLAG_KEEP_CONNECTION;
if (dwServiceType==AFX_INET_SERVICE_HTTPS) dwFlags|=INTERNET_FLAG_SECURE;

//インスタンスの生成
CInternetSession session(_T("Read CDDB from FreeDB"));
CHttpConnection *pServer = NULL;
CHttpFile *pFile= NULL;

BOOL bResult=TRUE;
//エラー時の例外を監視します。
try{
//Webサーバーへの接続処理
pServer=session.GetHttpConnection(strServerName,dwFlags,nPort);
//リクエストの準備
pFile=pServer->OpenRequest(CHttpConnection::HTTP_VERB_GET,strObject,NULL,1,NULL,_T("HTTP/1.1"),dwFlags);
//リクエストの実行
pFile->SendRequest();
//Htmlテキストを取得します。
TCHAR szBuff[1024];
while(UINT nRead=pFile->Read(szBuff,sizeof(szBuff)-1)){
*(szBuff+nRead)='\0';
strHtmls.Append(szBuff);
}
}
//エラー処理
catch(CInternetException *e){
TCHAR szMessage[256];
UINT nContext=0;
e->GetErrorMessage(szMessage,sizeof(szMessage)-1,&nContext);
TRACE0(szMessage);
e->Delete();
bResult=FALSE;
}

//終了処理
if(pFile){
pFile->Close();
delete pFile;
}
if (pServer){
pServer->Close();
delete pServer;
}
session.Close();
return bResult;
}


「CD-DAの再生 第1部」で解説したCDDAFileクラスに、下記のメンバ関数メンバ変数を新たに追加します。

//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;//演奏手順



CDDAFileクラスコンストラクタに、上で追加したメンバー変数の初期化部分を新たに追加します。

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;


CDDAの各トラックの開始位置時間(秒)と総演奏時間、総トラック数からdiscidを算出し、算出したdiscidと各トラックの開始セクタ位置、CDDAの終了時間(秒)をパラメータにして「QueryコマンドURL」を作成し、freeDBに問い合わせて、目的のCDに該当するジャンルdiscidを取得します。

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

int CDDAFile::CDDBQuery()
{
if (m_hDrive==INVALID_HANDLE_VALUE) return FALSE;

//受信した文字列リストを削除します。
m_strGenre.RemoveAll();

//トラック数を算出します。
UINT nTracks=m_TOC.LastTrack-m_TOC.FirstTrack+1;

UINT total=0;
UINT secondStart=0;
for(UINT track=m_TOC.FirstTrack;track<=m_TOC.LastTrack;track++){
//トラックの開始時間を算出します。
UINT second=m_TOC.TrackData[track-1].Address[1]*60+m_TOC.TrackData[track-1].Address[2];
//開始トラックの場合は、開始時間(秒)を保存します。
if (track==m_TOC.FirstTrack) secondStart=second;
//トラックの演奏時間(10進数)の全ての桁を加算します。
UINT digitTotal=0;
while(second>0){
digitTotal+=(second%10);
second/=10;
}
//全てのトラックの桁加算結果を合計します。
total+=digitTotal;
}
//合計を255で割った余りを求めます。
total%=255;

//終了トラックの演奏時間を算出します。
UINT secondEnd=m_TOC.TrackData[m_TOC.LastTrack].Address[1]*60+m_TOC.TrackData[m_TOC.LastTrack].Address[2];
//実際の演奏時間を算出します。
UINT secondPlay=secondEnd-secondStart;
//ディスクIDを算出します。
UINT discid=(total<<24)|(secondPlay<<8)|nTracks;
//ディスクIDを16進数8桁の文字列にします。
CString strDiscID;
strDiscID.Format(_T("%08x"),discid);

//クエリー命令文字列の作成
CString strQuery;
strQuery.Format(_T("query+%s+%d"),strDiscID,nTracks);

for(UINT track=m_TOC.FirstTrack;track<=m_TOC.LastTrack;track++){
//トラックごとのセクター番号を文字列に追加します。
UINT posSector=MSF2FAD(m_TOC.TrackData[track-1].Address);
strQuery.AppendFormat(_T("+%d"),posSector);
}
//ディスクの終了時間を追加します。
strQuery.AppendFormat(_T("+%d"),secondEnd);

//問い合わせURLを作成します。
CString strURL;
strURL.Format(_T("%s?cmd=cddb+%s&hello=%s+%s+%s+%s&proto=%d"),m_strURL,strQuery,
m_strUser,m_strServer,m_strSystem,m_strVersion,m_nProtocol);

//インターネットに接続して、Html文字列を受信します。
CString strHtmls;
if (!_ReadHTTP(strURL,strHtmls)) return FALSE;

//サーバーからの応答コードを取得します。
int code=0;
_stscanf(strHtmls,_T("%d"),&code);
//応答文字列解析用のインデックス変数
int index=4,delim;
switch(code){
case 210://入力したディスクIDに正確な情報が複数見つかった場合。
case 211://不正確ながら、入力したディスクIDに近い情報が複数見つかった場合。
//改行文字を探します。
index=strHtmls.Find(_T("\r\n"),index);
if (index==-1) return FALSE;
//次の行の先頭にインデックスを進めます。
index+=2;
case 200://入力したディスクIDに正確な情報が見つかった場合。
//終了文字になるまでループします。
while(strHtmls.GetAt(index)!='.'){
//一行分の文字列を取り出して、リストに格納します。
delim=strHtmls.Find(_T("\r\n"),index);
if (delim==-1) break;
m_strGenre.Add(strHtmls.Mid(index,delim-index));
//次の行の先頭にインデックスを進めます。
index=delim+2;
}
break;
default:
return FALSE;
}
//取得した情報の個数を返して終了します。
return (int)m_strGenre.GetCount();
}


CDDBQuery関数で取得した文字列リスト内の中で入力番号に対応する文字列から、ジャンル文字列discid文字列を抽出し、CDDBRead関数を呼び出します。

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

BOOL CDDAFile::CDDBRead(int indexGenre)
{
ASSERT((indexGenre>=0)&&(indexGenre<m_strGenre.GetCount()));
CString str=m_strGenre.GetAt(indexGenre);
//ジャンル文字列を取り出します。
int delim=str.Find(' ');
if (delim==-1) return FALSE;
CString strGenre=str.Left(delim);
int index=delim+1;
//ディスクID文字列を取り出します。
delim=str.Find(' ',index);
if (delim==-1) return FALSE;
CString strDiscID=str.Mid(index,delim-index);
index=delim+1;
//演奏者名/アルバム名の取得
CString strTitle=str.Right(str.GetLength()-index);
return CDDBRead(strGenre,strDiscID);
}


入力されたジャンル文字列discid文字列を元に「ReadコマンドURL」を作成し、freeDBに接続して目的のCDの演奏者やアルバム名、曲名などの情報を取得します。

//CDDB(FreeDB)の読み込み
// lpszGenre:ジャンル文字列
// lpszDiscID:ディスクID文字列(16進8桁表記でa~fは小文字)

BOOL CDDAFile::CDDBRead(LPCTSTR lpszGenre,LPCTSTR lpszDiscID)
{
//CDDBのテキストデータを削除します。
m_strCDDB.Empty();
m_strYear.Empty();
m_strPlayOrder.Empty();
m_listExtd.RemoveAll();
m_listExtt.RemoveAll();
m_listExtt.SetSize(1+m_TOC.LastTrack);

//読み込み命令のための問い合わせURLを作成します。
CString strURL;
strURL.Format(_T("%s?cmd=cddb+read+%s+%s+&hello=%s+%s+%s+%s&proto=%d"),m_strURL,lpszGenre,
lpszDiscID,m_strUser,m_strServer,m_strSystem,m_strVersion,m_nProtocol);

//インターネットに接続して、Html文字列を受信します。
CString strHtmls;
if (!_ReadHTTP(strURL,strHtmls)) return FALSE;

//送られてきた文字列を保存します。
m_strCDDB=strHtmls;

//ディスクID文字列の取得
int index=strHtmls.Find(_T("DISCID="));
if (index==-1) return FALSE;
index+=7;
int delim=strHtmls.Find(_T("\r\n"),index);
if (delim==-1) return FALSE;
CString strDiscID=strHtmls.Mid(index,delim-index);
index=delim+2;

//演奏者名/アルバム名を探します。
index=strHtmls.Find(_T("DTITLE="));
if (index==-1) return FALSE;
index+=7;
delim=strHtmls.Find(_T("\r\n"),index);
if (delim==-1) return FALSE;

//CDテキストを格納する文字列リストを削除します。
for(int i=0;i<16;i++) m_listCDText[i].RemoveAll();
//文字列リストにトラック数分配列を確保します。
for(int i=0;i<16;i++) m_listCDText[i].SetSize(1+m_TOC.LastTrack);

//ディスクID文字列を文字列リストに保存します。
m_listCDText[6].SetAt(0,strDiscID);

//演奏者名/アルバム名を取り出します。
CString strTitle=strHtmls.Mid(index,delim-index);
index=delim+2;
//スラッシュ記号を探して演奏者名とアルバム名に分け、それぞれをCDテキストに格納します。
int slash=strTitle.Find(_T(" / "));
if (slash==-1) m_listCDText[0].SetAt(0,strTitle);
else{
m_listCDText[0].SetAt(0,strTitle.Right(strTitle.GetLength()-(slash+3)));
m_listCDText[1].SetAt(0,strTitle.Left(slash));
}

//CDのリリースされた年を探します。
int year=strHtmls.Find(_T("DYEAR="));
if (year!=-1){
//見つかったら、CDテキストに格納します。
year+=6;
delim=strHtmls.Find(_T("\r\n"),year);
if (delim!=-1){
m_strYear=strHtmls.Mid(year,delim-year);
index=delim+2;
}
}

//ジャンル文字列を探します。
int genre=strHtmls.Find(_T("DGENRE="));
if (genre!=-1){
//見つかったら、CDテキストに格納します。
genre+=7;
delim=strHtmls.Find(_T("\r\n"),genre);
if (delim!=-1){
CString strGenre=strHtmls.Mid(genre,delim-genre);
m_listCDText[7].SetAt(0,strGenre);
index=delim+2;
}
}
//見つからない場合は、CDDB読み込み時に使用したジャンル文字列を設定します。
else m_listCDText[7].SetAt(0,lpszGenre);

//曲名を探します。
int track=1;
while(TRUE){
index=strHtmls.Find(_T("TTITLE"),index);
if (index==-1) break;
index+=6;
//トラック番号を取得します。
CString strTrack=strHtmls.Mid(index,2);
_stscanf(strTrack,_T("%d"),&track);
track++;
//曲名を取り出しでCDテキストに格納します。
delim=strHtmls.Find('=',index);
if (delim==-1) break;
index=delim+1;
delim=strHtmls.Find(_T("\r\n"),index);
if (delim==-1) break;
m_listCDText[0].SetAt(track,strHtmls.Mid(index,delim-index));
index=delim+2;
}

//拡張データ文字列を探します。
int iStart=0;
int extd=0;
do{
extd=strHtmls.Find(_T("EXTD="),iStart);
if (extd!=-1){
//見つかったら、CDテキストに格納します。
extd+=5;
delim=strHtmls.Find(_T("\r\n"),extd);
CString strExtd;
if (delim!=-1){
strExtd=strHtmls.Mid(extd,delim-extd);
m_listExtd.Add(strExtd);
index=delim+2;
iStart=index;
//UPCコードを検索します。
int upc=strExtd.Find(_T("UPC = "));
if (upc!=-1){
upc+=6;
delim=strExtd.Find(_T("\\n"),upc);
if (delim!=-1){
CString strUpc=strExtd.Mid(upc,delim-upc);
m_listCDText[14].SetAt(0,strUpc);
}
}
//CDのリリース年がまだない場合は、検索します。
if (m_strYear.IsEmpty()){
int year=strExtd.Find(_T("YEAR: "));
if (year==-1) year=strExtd.Find(_T("Year: "));
if (year!=-1){
year+=6;
if ((strExtd.GetLength()-year)>=4)
m_strYear=strExtd.Mid(year,4);
}
}
}
}
}
while(extd>-1);

//拡張されたトラックデータを探します。
index=0;
while(TRUE){
index=strHtmls.Find(_T("EXTT"),index);
if (index==-1) break;
index+=4;
//トラック番号を取得します。
CString strTrack=strHtmls.Mid(index,2);
_stscanf(strTrack,_T("%d"),&track);
track++;
delim=strHtmls.Find('=',index);
if (delim==-1) break;
index=delim+1;
delim=strHtmls.Find(_T("\r\n"),index);
if (delim==-1) break;
int length=delim-index;
//拡張されたトラックデータがあれば、文字列を取り出して、
//CDテキストに格納します。

if (length>0){
CString strExtt=strHtmls.Mid(index,delim-index);
m_listExtt.SetAt(track,strExtt);
}
index=delim+2;
}

//演奏順序データを探します。
int playorder=strHtmls.Find(_T("PLAYORDER="));
if (playorder!=-1){
//見つかったら、CDテキストに格納します。
playorder+=10;
delim=strHtmls.Find(_T("\r\n"),playorder);
if (delim!=-1){
m_strPlayOrder=strHtmls.Mid(playorder,delim-playorder);
index=delim+2;
}
}
return TRUE;
}


CDDBQuery関数で目的のCDに該当するジャンルdiscidを取得した後、目的のジャンルの番号を引数にいれてCDDBRead関数を呼び出しTRUEが戻ればfreeDBからのCDDBの読み込みは成功です。

サンプルプログラム(VisualC++net2003ソリューション)CDDAFileTest02.zip

上記のzipファイルをダウンロードしてからWinRAR等で解凍し、「CDDAFileTest02」フォルダー内にある「CDDAFileTest.sln」を開きます。
「F5」キーを押すと、ビルド確認のダイヤログが表示されるので「Yes」を選択してソリューションをビルドします。



ビルドが終わると直ちにプログラムが自動起動して下の図にあるダイヤログが表示されます。



パソコン本体のCDROMドライブに音楽CDを入れてCDROMドライブのドアをクローズし、ダイヤログにある「CDDA」ボタンをクリックするとCDが読み込まれてトラック1から再生されます。
(※この時ウィンドウズのCDROMドライブのAUTOPLAY機能は解除しておいて下さい。)

CDテキストがない場合、ダイヤログのタイトルバーは「CDDAFileTest」のままで、曲名はトラック1から順に「Track??.cdda」と表示されますが、freeDBからのCDDBの読み込みに成功すると、ダイヤログのタイトルバーにアーティスト名とアルバムタイトル名が表示され、コンボボックスにCD内の全ての曲名が表示されます。



CDDBが読み込まれなかった、或いは表示された曲名等が本来のものと食い違う場合は、メニューから「ツール」/「CDDBの読み込み...」を選択し、「CDDB読み込み設定」ダイヤログを表示させます。



まず「問い合わせ先」コンボで問い合わせ先URLを変更し、「上記の設定でジャンルの再確認」ボタンを押します。ジャンルが読み込まれたところで「ジャンル別」コンボで適当なジャンルを選択して、「読み込む」ボタンを押します。ダイヤログのタイトルバーにアーティスト名とアルバムタイトル名が表示され、コンボボックスにCD内の全ての曲名が表示されるのを確認して下さい。



<<CDDAファイルの再生 第2部"|ページの先頭MP3ファイルの再生 第1部>>

スポンサーサイト



コメント: 0

この記事へのコメント
ブログ著者にのみ知らせます。

Trackback+Pingback: 0

TrackBack URL for this entry
http://hiroshi0945.blog75.fc2.com/tb.php/51-26aeaeee
Listed below are links to weblogs that reference
CDDBの読み込み from マルチメディアファイルフォーマット

Home > CDDAファイルフォーマット > CDDBの読み込み

タグクラウド
ブロとも申請フォーム

この人とブロともになる

ブロとも一覧

このページの先頭へ戻る