2012年04月09日

ID3タグの読み込み(id3lib) 第3部



「ID3タグの読み込み(id3lib) 第3部」では、MFCを使った用例とサンプルプログラムを紹介します。




MFCにおいてこのCID3Tagクラス
を使用して、実際に画像を表示する方法を解説します。まずはCID3Tagクラスのインスタンスへのポインタ定義のあるタブシートダイヤログクラスのヘッダーファイルの一部分です。

#pragma once

// CID3TabSheet dialog
class CMP3PropDlg;
class CMP3File;
class CID3Tag;
class CImageDlg;

//class CID3TabSheet;

class CID3TabSheet : public CDialog
{
DECLARE_DYNAMIC(CID3TabSheet)

public:
CID3TabSheet(LPCTSTR lpszTabName,CByteArray* pID3RawData,CWnd* pParent = NULL);
virtual ~CID3TabSheet(){};

// Dialog Data
enum { IDD = IDD_DIALOG_ID3META };

protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support

DECLARE_MESSAGE_MAP()
public:
//このダイヤログクラスの初期化関数
virtual BOOL OnInitDialog();
afx_msg void OnDestroy();
afx_msg void OnSize(UINT nType, int cx, int cy);

//ID3生データの取得(オーバーライド)
virtual CByteArray* GetID3RawData(){return m_pID3RawData;};

//リストビューのアップデート(オーバーライド)
virtual BOOL Update(int itemVisible);

protected:
CString m_strTabName; //タブシート名
CByteArray* m_pID3RawData; //ID3生データを格納するCByteArrayクラスへのポインタ

CMP3File* m_pMP3File; //対象とするCMP3Fileクラスへのポインタ
CMP3PropDlg* m_pDlgProp; //親のプロパティダイヤログ
CImageDlg* m_pDlgImage; //画像表示用ダイヤログ

CID3Tag* m_pID3Tag; //CID3Tagクラスのインスタンスへのポインタ
int m_iItemClicked; //最後にクリックされたリストビューの行番号
int m_iItemImage; //現在表示中の画像番号

//ID3タグの内容を表示するためのリストビューコントロール変数
CListCtrl m_listMeta;

//リストビューヘッダーの作成(オーバーライド)
virtual int _CreateListHeader();

public:
//リストビューにおいて現在選択されている行をクリップボードに取り込みます。
BOOL DoCopy();
//クリップボードの内容を指定行のリストに書き込みます。
BOOL DoPaste();
//リストビューにおいて現在選択されている行を削除します。
BOOL DoDelete();

afx_msg void OnMetaCut();
afx_msg void OnMetaCopy();
afx_msg void OnMetaPaste();
afx_msg void OnMetaDelete();

...
...

};


CID3TabSheetクラスのコンストラクタです。

//コンストラクタ
CID3TabSheet::CID3TabSheet(LPCTSTR lpszTabName,CByteArray* pID3RawData,CWnd* pParent /*=NULL*/)
: CDialog()
{
m_strTabName=lpszTabName;
m_pID3RawData=pID3RawData;
}


次にダイヤログクラスの初期化関数です。

BOOL CID3TabSheet::OnInitDialog()
{
CDialog::OnInitDialog();

// TODO: Add extra initialization here

CMP3FileTestDlg* pMainWnd=(CMP3FileTestDlg*)AfxGetMainWnd();
m_pDlgProp=pMainWnd->;GetPropDlg();

m_pMP3File=(CMP3File*)m_pDlgProp->GetMP3File();
ASSERT_KINDOF(CMP3File,m_pMP3File);

m_pDlgImage=m_pDlgProp->GetImageDlg();
ASSERT_KINDOF(CImageDlg,m_pDlgImage);

//CID3Tagクラスのインスタンスを作成する。
m_pID3Tag=new CID3Tag();

m_iItemClicked=-1;
m_iItemImage=-1;

_CreateListHeader();

Update(-1);

return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE

}

//リストビューヘッダーの作成
int CID3TabSheet::_CreateListHeader()
{
ASSERT(m_listMeta.m_hWnd);

//リストビュースタイルの初期化
LONG Style=::GetWindowLong(m_listMeta.m_hWnd,GWL_STYLE);
Style|= LVS_REPORT|LVS_SHOWSELALWAYS|LVS_AUTOARRANGE;
::SetWindowLong(m_listMeta.m_hWnd,GWL_STYLE,Style);

Style=m_listMeta.GetExtendedStyle();
Style|=LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES;
m_listMeta.SetExtendedStyle(Style);

//ヘッダーコントロールへのポインタを取得
CHeaderCtrl* pHeaderCtrl=m_listMeta.GetHeaderCtrl();
ASSERT(pHeaderCtrl);
if (!pHeaderCtrl) return FALSE;

//ヘッダーの消去
while(pHeaderCtrl->GetItemCount())
m_listMeta.DeleteColumn(0);

//ヘッダーの作成
LVCOLUMN lvc={0};
#define COLUMNS 9
TCHAR caption[][32]={_T("ID"),_T("フレーム名"),_T("見出し"),_T("内容"),
_T("エンコード"),_T("属性/言語"),
_T("バイナリー"),_T("タイムスタンプ"),_T("画像名")
};

int cxColumn[COLUMNS]={45,90,66,150,80,80,150,80,80};
int fmt[COLUMNS]={LVCFMT_LEFT,LVCFMT_LEFT,LVCFMT_LEFT,LVCFMT_LEFT,
LVCFMT_LEFT,LVCFMT_LEFT,LVCFMT_LEFT,LVCFMT_LEFT,
LVCFMT_LEFT
};
lvc.mask=LVCF_WIDTH|LVCF_TEXT|LVCF_SUBITEM|LVCF_FMT;

for (int i=0;i<COLUMNS;i++){
lvc.iSubItem=i;
lvc.pszText =caption[i];
lvc.cx =cxColumn[i];
lvc.fmt =fmt[i];
if (m_listMeta.InsertColumn(i,&lvc)==-1) return FALSE;
}
return pHeaderCtrl->GetItemCount();
}


ID3タグの内容を読み出して、リストビューの内容を更新する関数です。

//リストビューの更新
BOOL CID3TabSheet::Update(int itemVisible)
{
ASSERT_KINDOF(CMP3File,m_pMP3File);
ASSERT_KINDOF(CID3Tag,m_pID3Tag);

//ID3タグの内容を削除します。
m_pID3Tag->Delete();
//リストビューのアイテムを削除します。
m_listMeta.DeleteAllItems();

//現在のCMP3Fileクラス内にあるID3タグの生データのサイズ及び、ポインタを取得します。
CByteArray* pID3RawData=GetID3RawData();
if (!pID3RawData) return FALSE;
int size=(int)pID3RawData->GetSize();
BYTE* data=pID3RawData->GetData();

//ID3タグの内容を解析します。
UINT nframes=(AfxIsValidAddress(data,size,TRUE))?
m_pID3Tag->Parse(data,size):0;
if (!nframes) return FALSE;

//最初の画像だけ読み出すようにします。
BOOL bImageAttached=FALSE;
//リストビューにID3タグの内容を書き込みます。
for (UINT i=0;i<nframes;i++){
//CID3Tagクラスから、該当する番号のフレームの内容を読み出します。
m_pID3Tag->GetFrame(i);

//一番目のカラムにフレームIDを書き込みます。
int index=m_listMeta.InsertItem(i,(LPCTSTR)m_pID3Tag->m_strID);
if (index<0) break;

//二番目以降のカラムに、それぞれの項目に該当する内容を書き込みます。
int subItem=1;
m_listMeta.SetItemText(index,subItem++,m_pID3Tag->m_strTitle);
m_listMeta.SetItemText(index,subItem++,m_pID3Tag->m_strDesc);
m_listMeta.SetItemText(index,subItem++,m_pID3Tag->m_strText);
m_listMeta.SetItemText(index,subItem++,m_pID3Tag->m_strTextEnc);
m_listMeta.SetItemText(index,subItem++,m_pID3Tag->m_strImmediate);

//バイナリデータからDumpテキストを作成します。
int length=m_pID3Tag->m_strBinary.GetAllocLength();
//バイナリデータが256バイトを超える場合は、256バイトまでとします。
if (length>256) length=256;
BYTE* binary=(BYTE*)m_pID3Tag->m_strBinary.GetBuffer();
CString strBinaryText=g_BinaryToDumpText(binary,length);
m_listMeta.SetItemText(index,subItem++,strBinaryText);
m_listMeta.SetItemText(index,subItem++,m_pID3Tag->m_strTimeStamp);
m_listMeta.SetItemText(index,subItem++,m_pID3Tag->m_strPictureName);

//まだ画像を読み込んでいない場合で、画像フレームの場合。
if ((!bImageAttached)&&
(m_pID3Tag->m_strBinary.GetAllocLength())&&
((m_pID3Tag->m_strID==_T("PIC"))||(m_pID3Tag->m_strID==_T("APIC"))))
{
//ウェブ上から画像データを読み込む場合は、
//まだ実例を見たことがないので省略します。

if (m_pID3Tag->m_strImmediate.Find(_T("-->"))>=0) ASSERT(FALSE);

//引数にバイナリーデータを入力して、画像表示ダイヤログを更新します。
m_pDlgImage->Update(m_pID3Tag->;m_strBinary,index);
//画像表示用ダイヤログのタイトルに画像名を書き込みます。
m_pDlgImage->SetWindowText(m_pID3Tag->;m_strPictureName.GetBuffer());

//表示画像フレーム番号がまだ設定されていない場合は、
//現在の画像のフレーム番号を設定します。

if (m_iItemImage<0) m_iItemImage=i;
//画像読み込み済みにします。
bImageAttached=TRUE;
}
}
//引数の着目行が有効な値なら、着目行を選択表示にして、
//着目行が見えるようにスクロールします。

if (itemVisible>=0){
m_listMeta.SetItemState(itemVisible,LVIS_SELECTED,LVIS_SELECTED);
m_listMeta.EnsureVisible(itemVisible,FALSE);
}
return TRUE;
}


画像表示用ダイヤログ(CImageDlgクラス)のクラス定義です。

class CGIFFile;
// CImageDlg dialog
class CImageDlg : public CDialog
{
DECLARE_DYNAMIC(CImageDlg)

public:
CImageDlg(CWnd* pParent = NULL); // standard constructor
virtual ~CImageDlg();

void Init();
void Delete();

// Dialog Data
enum { IDD = IDD_DIALOG_IMAGE };

protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support

DECLARE_MESSAGE_MAP()

HBITMAP m_hBitmap; //現在表示中のビットマップ画像ハンドル
CGIFFile* m_pGif; //GIF画像を格納したCGIFFileクラスへのポインタ
BOOL m_bFirstTime; //一番最初の画像表示かどうか?
public:
virtual BOOL OnInitDialog();
afx_msg void OnPaint();
LPARAM m_lParam; //現在の画像に関する32ビット情報を保存します。

//表示サイズ/レイアウトを再設定します。
BOOL RecalcLayout();

//ビットマップハンドルから、ピクチャーボックスを更新します。
BOOL Update(HBITMAP hBitmap,LPARAM lParam = 0);
//CGFIFileクラスから、画像を更新します。
BOOL Update(CGIFFile* pGif,LPARAM lParam = 0);

//CFileクラスから、画像を更新します。
BOOL Update(CFile& infile,LPARAM lParam = 0);
//CStringクラスから、画像を更新します。
BOOL Update(CString& strImage,LPARAM lParam = 0);

//現在の画像を削除します。
void DeleteImage();
//現在ビットマップハンドルが、存在するかどうか?
BOOL HasImage(){return ((m_hBitmap!=0)||(m_pGif!=0));};

...
...

};


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

CImageDlg::CImageDlg(CWnd* pParent /*=NULL*/)
: CDialog(CImageDlg::IDD, pParent)
{
m_bFirstTime=TRUE;
Init();
}

CImageDlg::~CImageDlg()
{
Delete();
}


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

void CImageDlg::Init()
{
m_lParam=0;
m_hBitmap=NULL;
m_pGif=NULL;
}

void CImageDlg::Delete()
{
if (m_hBitmap) ::DeleteObject(m_hBitmap);
if (m_pGif) delete m_pGif;
Init();
}

void CImageDlg::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: Add your message handler code here
// Do not call CDialog::OnPaint() for painting messages

//クライアント領域を取得します。

CRect rcClient;
GetClientRect(&rcClient);
int width=rcClient.Width();
int height=rcClient.Height();

//クライアント領域一杯に背景画像を作成します。
CBitmap bmpMem;
bmpMem.CreateCompatibleBitmap(&dc,width,height);
CDC dcMem;
dcMem.CreateCompatibleDC(&dc);
dcMem.SetStretchBltMode(COLORONCOLOR);
CBitmap* pBmpOld=dcMem.SelectObject(&bmpMem);
//背景色はデスクトップと同じ色にします。
dcMem.FillSolidRect(0,0,width,height,::GetSysColor(COLOR_DESKTOP));

//画像サイズを取得します。
int cxImage,cyImage;
BITMAP bm={0};

BOOL bHasImage=FALSE;
if (m_pGif){
cxImage=m_pGif->Width();
cyImage=m_pGif->Height();
bHasImage=TRUE;
}
else if (::GetObject(m_hBitmap,sizeof(BITMAP),&bm)){
cxImage=bm.bmWidth;
cyImage=abs(bm.bmHeight);
bHasImage=TRUE;
}

//画像がある場合は背景画像に画像を重ね書きします。
if (bHasImage){
//画像のアスペクト比を変えずに、クライアント領域に画像サイズを合わせます。
float aspectClient=(float)width/(float)height;
float aspectImage=(float)cxImage/(float)cyImage;
int x,y,cx,cy;
//画面より画像が横長の時
if (aspectImage>aspectClient){
x=0;cx=width;
cy=(int)((float)cx/aspectImage);
y=(height-cy)/2;
}
//画面より画像が縦長の時
else{
y=0;cy=height;
cx=(int)((float)cy*aspectImage);
x=(width-cx)/2;
}
//画像を背景画像に書き込みます。
if (m_pGif)
//CGIFFileクラスの場合は、CGIFFileクラスの描画関数を呼び出します。
m_pGif->OnDraw(&dcMem,x,y,cx,cy);
else
//それ以外は、ビットマップ描画関数を呼び出します。
_DrawBitmap(dcMem.GetSafeHdc(),x,y,cx,cy,m_hBitmap,NULL,0,0,cxImage,cyImage);
}
//背景画像をダイヤログのデバイスコンテキストにコピーします。
dc.BitBlt(0,0,width,height,&dcMem,0,0,SRCCOPY);

dcMem.SelectObject(pBmpOld);
bmpMem.DeleteObject();
dcMem.DeleteDC();
}

//表示サイズ/レイアウトを再設定します。
BOOL CImageDlg::RecalcLayout()
{
CWnd* pMainWnd=AfxGetMainWnd();

//画像サイズを取得します。
int cxImage,cyImage;
BITMAP bm={0};
if (m_pGif){
cxImage=m_pGif->Width();
cyImage=m_pGif->Height();
}
//ビットマップハンドルからBITMAP構造体を取得します。
else if (::GetObject(m_hBitmap,sizeof(BITMAP),&bm)){
cxImage=bm.bmWidth;
cyImage=abs(bm.bmHeight);
}
else return FALSE;

//メインダイヤログのクライアント領域を取得します。
CRect rectClient;
pMainWnd->GetClientRect(&rectClient);

//画像のアスペクト比を変えずに、クライアント領域に画像サイズを合わせます。
int width=rectClient.Width();
int height=width;
if ((cxImage)&&(cyImage))
height=(int)((float)height*(float)cyImage/(float)cxImage);

//一番最初の場合は、メインダイヤログが画像サイズを含むよう、
//メインダイヤログをリサイズします。

if (m_bFirstTime){
m_bFirstTime=FALSE;
CRect rectMain;
pMainWnd->GetWindowRect(&rectMain);
int heightMain=rectMain.Height()+height;
//ダイヤログの高さがスクリーン高さの半分より高い場合は、
//スクリーン高さの半分をダイヤログの高さとします。

int cyMax=GetSystemMetrics(SM_CYFULLSCREEN);
if (heightMain>cyMax/2) heightMain=cyMax/2;
rectMain.top=rectMain.bottom-heightMain;
//ダイヤログ上部がスクリーン上部を突き抜ける場合は、
//スクリーン上端にダイヤログの上部を合わせます。

if (rectMain.top<0){
rectMain.bottom=heightMain;
rectMain.top=0;
}
pMainWnd->MoveWindow(&rectMain);
}
//画像を更新します。
InvalidateRect(NULL,TRUE);
return TRUE;
}

//現在の画像を削除します。
void CImageDlg::DeleteImage()
{
//すべてのメンバー変数を削除します。
Delete();
//再描画します。
InvalidateRect(NULL,TRUE);
}


最後にCImageDlgクラスの更新関数です。

//CStringクラスから画像を更新します。
BOOL CImageDlg::Update(CString& strImage,LPARAM lParam)
{
CMemFile infile((BYTE*)strImage.GetBuffer(),(UINT)strImage.GetAllocLength());
BOOL bResult=Update(infile,lParam);
infile.Detach();
infile.Close();
return bResult;
}

//CFileクラスから画像を更新します。
BOOL CImageDlg::Update(CFile& infile,LPARAM lParam)
{
ULONGLONG pos=infile.GetPosition();
//JPEG画像として読み込みます。
HBITMAP hBitmap=_JpegStreamIn(&infile);
//読み込めない場合は、PNG画像として再度読み込みます。
infile.Seek(pos,CFile::begin);
if (!hBitmap) hBitmap=_PNGStreamIn(&infile);

//ビットマップの読み込みに成功した場合は、ビットマップハンドルで画像を更新します。
if (hBitmap) return Update(hBitmap,lParam);

//GIF画像として読み込みます。
infile.Seek(pos,CFile::begin);
CGIFFile* pGif=new CGIFFile(this);
if (pGif->StreamIn(&infile,-1)) return Update(pGif,lParam);

delete pGif;
return FALSE;
}

//ビットマップハンドルから、ピクチャーボックスを更新します。
BOOL CImageDlg::Update(HBITMAP hBitmap,LPARAM lParam)
{
Delete();
//ビットマップハンドルからBITMAP構造体の取得に失敗したら、エラー終了します。
BITMAP bm={0};
if (!::GetObject(hBitmap,sizeof(BITMAP),&bm)) return FALSE;

//ビットマップハンドルをメンバー変数に設定します。
m_hBitmap=hBitmap;

//画像に関する32ビット情報をメンバー変数に保存します。
m_lParam=lParam;

//画像位置を更新します。
RecalcLayout();
return TRUE;
}

//CGFIFileクラスから画像を更新します。
BOOL CImageDlg::Update(CGIFFile* pGif,LPARAM lParam)
{
//メンバー変数を削除します。
Delete();
m_pGif=pGif;

//画像に関する32ビット情報をメンバー変数に保存します。
m_lParam=lParam;

//画像サイズを再計算してから再表示します。
RecalcLayout();
//複数画像の場合はアニメーション再生を開始します。
if (m_pGif->m_nImageCount>1) m_pGif->Play();
return TRUE;
}



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

上記のzipファイルをダウンロードして、「id3libのインストール」">で作成した「id3lib-3.8.3」フォルダーがある同じフォルダー内にWinRAR等を使って解凍すると、「MP3FileTest05R」という名のフォルダーが作成されます。



「MP3FileTest05R」フォルダー内にある「MP3FileTest.sln」を開いて「F5」キーを押すと、ビルド確認のダイヤログが表示されるので「Yes」を選択してソリューションをビルドします。



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



メインダイヤログのメニューから「ファイル」→「開く」を選択すると、「ファイルを開く」ダイヤログが表示されます。



ID3タグの入ったMP3ファイルを選択し「開く」ボタンを押すと、コンボボックスにファイル名が表示され、スライドバーの動きに合わせて、MP3ファイルが再生されます。同時に「MP3のプロパティ」ダイヤログが表示され、画像があればメインダイヤログ上部に表示されます。


sideback




「MP3のプロパティ」ダイヤログのそれぞれのタブシートに、各バージョンのID3タグの内容が表示されているか確認して下さい。




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

メールアドレス:

ホームページアドレス:

コメント:

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


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

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