// TestMP3 MP3ファイルの再生
#pragma comment(lib, "winmm")
#pragma comment(lib, "msacm32")
#include <Windows.h>
#include <MMReg.h>
#include <MSAcm.h>
#include "resource.h"
struct ID3V2HEADER {
BYTE id[3];
BYTE version[2];
BYTE flag;
BYTE size[4];
};
typedef ID3V2HEADER *LPID3V2HEADER;
// 関数プロトタイプ宣言
INT_PTR CALLBACK DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
void OnDropFiles(HWND hDlg, WPARAM wParam);
void OnPlay(HWND hDlg);
void OnStop(HWND hDlg);
void Stop(void);
BOOL IsId3v2(LPBYTE lpData, DWORD dwDataSize, LPDWORD lpdwTagSize);
BOOL GetMp3Format(LPBYTE lpData, LPMPEGLAYER3WAVEFORMAT lpmf);
BOOL DecodeToWave(LPWAVEFORMATEX lpwfSrc, LPBYTE lpSrcData, DWORD dwSrcSize, LPWAVEFORMATEX lpwfDest, LPBYTE *lplpDestData, LPDWORD lpdwDestSize);
BOOL LoadMP3(LPTSTR lpszFile, LPMPEGLAYER3WAVEFORMAT lpmf, LPBYTE *lplpData, LPDWORD lpdwSize);
// 外部変数
TCHAR szFile[MAX_PATH];
LPBYTE lpWaveData;
HWAVEOUT hwo;
WAVEHDR wh;
//==============================================================================
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int)
{
DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG), NULL, DlgProc);
return 0;
}
INT_PTR CALLBACK DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
INT_PTR result = TRUE;
switch (uMsg) {
case MM_WOM_DONE:
//waveOutWrite((HWAVEOUT)wParam, (LPWAVEHDR)lParam, sizeof(WAVEHDR));
OnStop(hDlg);
break;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDOK:
OnPlay(hDlg);
break;
case IDCANCEL:
OnStop(hDlg);
break;
}
break;
case WM_DROPFILES:
OnDropFiles(hDlg, wParam);
DragFinish((HDROP)wParam);
break;
case WM_CLOSE:
EndDialog(hDlg, 0);
break;
case WM_DESTROY:
Stop();
break;
default:
result = FALSE;
}
return result;
}
void OnDropFiles(HWND hDlg, WPARAM wParam)
{
HDROP hDrop = (HDROP)wParam;
DragQueryFile(hDrop, 0, szFile, _countof(szFile));
SetDlgItemText(hDlg, IDC_EDIT, szFile);
}
void OnPlay(HWND hDlg)
{
DWORD dwMP3Size;
LPBYTE lpMP3Data = NULL;
MPEGLAYER3WAVEFORMAT mf;
if (!LoadMP3(szFile, &mf, &lpMP3Data, &dwMP3Size)) {
return;
}
WAVEFORMATEX wf;
DWORD dwWaveSize;
if (!DecodeToWave((LPWAVEFORMATEX)&mf, lpMP3Data, dwMP3Size, &wf, &lpWaveData, &dwWaveSize)) {
delete[] lpMP3Data;
return;
}
delete[] lpMP3Data;
if (waveOutOpen(&hwo, WAVE_MAPPER, &wf, (DWORD_PTR)hDlg, 0, CALLBACK_WINDOW) != MMSYSERR_NOERROR) {
return;
}
wh.lpData = (LPSTR)lpWaveData;
wh.dwBufferLength = dwWaveSize;
wh.dwFlags = 0;
waveOutPrepareHeader(hwo, &wh, sizeof(WAVEHDR));
waveOutWrite(hwo, &wh, sizeof(WAVEHDR));
EnableWindow(GetDlgItem(hDlg, IDOK), FALSE);
}
void OnStop(HWND hDlg)
{
Stop();
EnableWindow(GetDlgItem(hDlg, IDOK), TRUE);
}
void Stop(void)
{
if (hwo) {
waveOutReset(hwo);
waveOutUnprepareHeader(hwo, &wh, sizeof(WAVEHDR));
waveOutClose(hwo);
hwo = NULL;
}
if (lpWaveData) {
delete[] lpWaveData;
lpWaveData = NULL;
}
}
BOOL GetMp3Format(LPBYTE lpData, LPMPEGLAYER3WAVEFORMAT lpmf)
{
static const DWORD dwBitTableLayer3[][16] = {
{0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0},
{0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0}
};
static const DWORD dwSampleTable[][3] = {
{44100, 48000, 32000},
{22050, 24000, 16000}
};
// 同期ヘッダ
if (lpData[0] != 0xff || lpData[1] >> 5 != 0x07) {
return FALSE;
}
// バージョン
BYTE version;
switch (lpData[1] >> 3 & 0x03) {
case 3: // 11=MPEG1
version = 1;
break;
case 2: // 10=MPEG2
version = 2;
break;
default:
return FALSE;
}
// レイヤー
if ((lpData[1] >> 1 & 0x03) != 1) { // 01=Leyer3
return FALSE;
}
// ビットレート
BYTE index = lpData[2] >> 4;
DWORD dwBitRate = dwBitTableLayer3[version - 1][index];
// サンプリングレート
index = lpData[2] >> 2 & 0x03;
DWORD dwSampleRate = dwSampleTable[version - 1][index];
// パディング
BYTE padding = lpData[2] >> 1 & 0x01;
// チャンネルモード
BYTE channel = (lpData[3] >> 6) == 3 ? 1 : 2; // 11=シングルチャネル
WORD wBlockSize = (WORD)((1152 * dwBitRate * 1000 / dwSampleRate) / 8) + padding;
lpmf->wfx.wFormatTag = WAVE_FORMAT_MPEGLAYER3;
lpmf->wfx.nChannels = channel;
lpmf->wfx.nSamplesPerSec = dwSampleRate;
lpmf->wfx.nAvgBytesPerSec = (dwBitRate * 1000) / 8;
lpmf->wfx.nBlockAlign = 1;
lpmf->wfx.wBitsPerSample = 0;
lpmf->wfx.cbSize = MPEGLAYER3_WFX_EXTRA_BYTES;
lpmf->wID = MPEGLAYER3_ID_MPEG;
lpmf->fdwFlags = padding ? MPEGLAYER3_FLAG_PADDING_ON : MPEGLAYER3_FLAG_PADDING_OFF;
lpmf->nBlockSize = wBlockSize;
lpmf->nFramesPerBlock = 1;
lpmf->nCodecDelay = 0x571;
return TRUE;
}
BOOL DecodeToWave(LPWAVEFORMATEX lpwfSrc, LPBYTE lpSrcData, DWORD dwSrcSize, LPWAVEFORMATEX lpwfDest, LPBYTE *lplpDestData, LPDWORD lpdwDestSize)
{
lpwfDest->wFormatTag = WAVE_FORMAT_PCM;
acmFormatSuggest(NULL, lpwfSrc, lpwfDest, sizeof(WAVEFORMATEX), ACM_FORMATSUGGESTF_WFORMATTAG);
HACMSTREAM has;
if (acmStreamOpen(&has, NULL, lpwfSrc, lpwfDest, NULL, 0, 0, ACM_STREAMOPENF_NONREALTIME) != 0) {
MessageBox(NULL, TEXT("変換ストリームのオープンに失敗しました。"), NULL, MB_ICONWARNING);
return FALSE;
}
DWORD dwDestSize;
acmStreamSize(has, dwSrcSize, &dwDestSize, ACM_STREAMSIZEF_SOURCE);
LPBYTE lpDestData = new BYTE[dwDestSize];
ACMSTREAMHEADER ash;
ZeroMemory(&ash, sizeof(ACMSTREAMHEADER));
ash.cbStruct = sizeof(ACMSTREAMHEADER);
ash.pbSrc = lpSrcData;
ash.cbSrcLength = dwSrcSize;
ash.pbDst = lpDestData;
ash.cbDstLength = dwDestSize;
acmStreamPrepareHeader(has, &ash, 0);
BOOL bResult = (acmStreamConvert(has, &ash, 0) == 0);
acmStreamUnprepareHeader(has, &ash, 0);
if (bResult) {
*lplpDestData = lpDestData;
*lpdwDestSize = ash.cbDstLengthUsed;
} else {
MessageBox(NULL, TEXT("変換に失敗しました。"), NULL, MB_ICONWARNING);
*lplpDestData = NULL;
*lpdwDestSize = 0;
delete[] lpDestData;
}
return bResult;
}
BOOL IsId3v2(LPBYTE lpData, DWORD dwDataSize, LPDWORD lpdwTagSize)
{
BOOL bResult;
LPID3V2HEADER lpHeader = (LPID3V2HEADER)lpData;
if (memcmp(lpHeader->id, "ID3", 3) == 0) {
*lpdwTagSize = ((lpHeader->size[0] << 21)|(lpHeader->size[1] << 14)|(lpHeader->size[2] << 7)|(lpHeader->size[3])) + 10;
bResult = TRUE;
} else {
LPBYTE lp = (lpData + dwDataSize) - 128;
*lpdwTagSize = (memcmp(lp, "TAG", 3) == 0) ? 128 : 0;
bResult = FALSE;
}
return bResult;
}
BOOL LoadMP3(LPTSTR lpszFileName, LPMPEGLAYER3WAVEFORMAT lpmf, LPBYTE *lplpData, LPDWORD lpdwSize)
{
HMMIO hmmio = mmioOpen(lpszFileName, NULL, MMIO_READ);
if (hmmio == NULL) {
MessageBox(NULL, TEXT("ファイルのオープンに失敗しました。"), NULL, MB_ICONWARNING);
return FALSE;
}
DWORD dwSize = mmioSeek(hmmio, 0, SEEK_END);
LPBYTE lpData = new BYTE[dwSize];
mmioSeek(hmmio, 0, SEEK_SET);
mmioRead(hmmio, (HPSTR)lpData, dwSize);
mmioClose(hmmio, 0);
DWORD dwTagSize;
LPBYTE lpMp3Data;
if (IsId3v2(lpData, dwSize, &dwTagSize)) {
dwSize -= dwTagSize;
lpMp3Data = new BYTE[dwSize];
CopyMemory(lpMp3Data, lpData + dwTagSize, dwSize);
delete[] lpData;
} else {
dwSize -= dwTagSize;
lpMp3Data = lpData;
}
if (!GetMp3Format(lpMp3Data, lpmf)) {
delete[] lpMp3Data;
return FALSE;
}
*lplpData = lpMp3Data;
*lpdwSize = dwSize;
return TRUE;
}