/*
[構成プロパティ]-[VC++ ディレクトリ]
[インクルード ディレクトリ]
C:\Program Files\Microsoft DirectX SDK (February 2010)\Include
C:\WinDDK\7600.16385.1\src\storage\tools\spti
[ライブラリ ディレクトリ]
C:\Program Files\Microsoft DirectX SDK (February 2010)\Lib\x86
Unicode文字セット
*/
#pragma comment(lib, "dsound.lib")
#pragma comment(lib, "dxguid.lib")
//#include <Windows.h>
#include <tchar.h>
#include <dsound.h>
#include <ntddscsi.h>
#include <spti.h>
#include <stddef.h>
#include "resource.h"
#define REVWORD(w) (((w<<8)&0xff00)|((w>>8)&0xff))
#define REVDWORD(dw) (((dw<<24)&0xff000000)|((dw<<8)&0xff0000)|((dw>>8)&0xff00)|((dw>>24)&0xff))
#define COMRELEASE(obj) if(obj){obj->Release();obj=NULL;}
#define NOTIFYEVENTS 2
#define CDDASECT 2352
#define READSECT 15
struct READTOC {
WORD wLen;
BYTE byStartTrack;
BYTE byEndTrack;
struct TRACKDESC {
BYTE d0;
BYTE data;
BYTE byTrack;
BYTE d1;
DWORD dwLBA;
} td[100];
};
// 関数プロトタイプ宣言
INT_PTR CALLBACK MainDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
void OnInitDialog(HWND hDlg);
int TrackList(HWND hWnd, TCHAR cDrv);
int ReadToc(TCHAR cDrv);
void OnPlay(HWND hDlg);
int Play(TCHAR cDrive, UINT uTrack, HWND hWnd);
int InitDirectSound(HWND hWnd);
int ReadBuf(DWORD dwStartOffset, DWORD dwWriteSize);
DWORD ExecCommand(HANDLE hDev, PUCHAR pucCdb, UCHAR ucCdbLen, PVOID pvData, ULONG ulDataLen);
// 外部変数構造体
static struct {
BOOL bPlay;
// CD-DA
HANDLE hDev;
BYTE byToc[sizeof(READTOC)+0xf];
READTOC *ptoc;
DWORD dwStartAddr;
DWORD dwEndAddr;
DWORD dwLBA;
// DirectSound
LPDIRECTSOUND8 pDS;
LPDIRECTSOUNDBUFFER pPri;
LPDIRECTSOUNDBUFFER pSec;
LPDIRECTSOUNDNOTIFY pNotify;
HANDLE hEvent[NOTIFYEVENTS];
DSBPOSITIONNOTIFY dspn[NOTIFYEVENTS];
DWORD dwBufferBytes;
DWORD dwEoe;
} g;
//==============================================================================
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int)
{
DialogBox(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, MainDlgProc);
return 0;
}
//------------------------------------------------------------------------------
INT_PTR CALLBACK MainDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
INT_PTR nRet = TRUE; // メッセージを処理した
switch (uMsg) {
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDC_DEVICELIST:
{
TCHAR szBuf[3+1];
HWND hWnd = GetDlgItem(hDlg, IDC_DEVICELIST);
LRESULT lr = SendMessage(hWnd, CB_GETCURSEL, 0, 0);
SendMessage(hWnd, CB_GETLBTEXT, lr, (LPARAM)szBuf);
if (szBuf[0]) {
TrackList(hDlg, szBuf[0]);
}
}
break;
case IDC_PLAY:
if (g.bPlay) {
g.bPlay = FALSE;
break;
}
g.bPlay = TRUE;
OnPlay(hDlg);
g.bPlay = FALSE;
break;
case IDC_EXIT:
if (g.bPlay == FALSE) {
SendMessage(hDlg, WM_CLOSE, 0, 0);
}
break;
}
break;
case WM_CLOSE:
EndDialog(hDlg, 0);
break;
case WM_INITDIALOG:
g.hDev = INVALID_HANDLE_VALUE;
OnInitDialog(hDlg);
break;
case WM_DESTROY:
if (g.hDev != INVALID_HANDLE_VALUE) {
CloseHandle(g.hDev);
}
break;
default:
nRet = FALSE; // メッセージを処理しなかった
}
return nRet;
}
//------------------------------------------------------------------------------
void OnInitDialog(HWND hDlg)
{
TCHAR szBuf[26*4+1];
LPCTSTR pszDrv;
HWND hWnd;
DWORD dwr;
UINT ur;
// CD-ROMドライブ一覧
hWnd = GetDlgItem(hDlg, IDC_DEVICELIST);
dwr = GetLogicalDriveStrings(_countof(szBuf) - 1, szBuf);
for (pszDrv = szBuf; *pszDrv; pszDrv += 4) {
ur = GetDriveType(pszDrv);
if (ur == DRIVE_CDROM) {
SendMessage(hWnd, CB_ADDSTRING, 0, (LPARAM)pszDrv);
}
}
SendMessage(hWnd, CB_SETCURSEL, 0, 0);
SendMessage(hWnd, CB_GETLBTEXT, 0, (LPARAM)szBuf);
TrackList(hDlg, szBuf[0]);
}
//------------------------------------------------------------------------------
int TrackList(HWND hDlg, TCHAR cDrv)
{
TCHAR szBuf[64];
HWND hWnd;
BYTE by;
hWnd = GetDlgItem(hDlg, IDC_TRACKLIST);
SendMessage(hWnd, CB_RESETCONTENT, 0, 0);
if (ReadToc(cDrv)) {
return -1;
}
for (by = 0; by < g.ptoc->byEndTrack; by++) {
DWORD dwStartAddr = REVDWORD(g.ptoc->td[by].dwLBA);
DWORD dwEndAddr = REVDWORD(g.ptoc->td[by+1].dwLBA) - 1;
DWORD dwLen = dwEndAddr - dwStartAddr + 1;
_stprintf_s(szBuf, _T("%2u, LBA[%6u-%6u], Len[%6u]"),
by+1, dwStartAddr, dwEndAddr, dwLen);
SendMessage(hWnd, CB_ADDSTRING, 0, (LPARAM)szBuf);
}
SendMessage(hWnd, CB_SETCURSEL, 0, 0);
return 0;
}
//------------------------------------------------------------------------------
int ReadToc(TCHAR cDrv)
{
TCHAR szDev[] = _T("\\\\.\\@:");
UCHAR ucCdb[16];
DWORD dwr;
// デバイスのオープン
szDev[4] = cDrv;
if (g.hDev != INVALID_HANDLE_VALUE) {
CloseHandle(g.hDev);
}
g.hDev = CreateFile(szDev, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
NULL, OPEN_EXISTING, 0, NULL);
if (g.hDev == INVALID_HANDLE_VALUE) {
return -1;
}
// TOCの読み込み
g.ptoc = (READTOC *)(((UINT_PTR)g.byToc + 0xf) & ~0xf);
ZeroMemory(g.ptoc, sizeof *g.ptoc);
ZeroMemory(ucCdb, sizeof ucCdb);
ucCdb[0] = SCSIOP_READ_TOC;
ucCdb[6] = 1;
*(PWORD)(ucCdb + 7) = REVWORD(sizeof *g.ptoc);
dwr = ExecCommand(g.hDev, ucCdb, 10, g.ptoc, sizeof *g.ptoc);
if (dwr == 0) {
return -1;
}
return 0;
}
//------------------------------------------------------------------------------
void OnPlay(HWND hDlg)
{
TCHAR szBuf[64];
TCHAR cDrive;
UINT uTrack;
HWND hWnd;
LRESULT lr;
// ドライブ
hWnd = GetDlgItem(hDlg, IDC_DEVICELIST);
lr = SendMessage(hWnd, CB_GETCURSEL, 0, 0);
SendMessage(hWnd, CB_GETLBTEXT, lr, (LPARAM)szBuf);
cDrive = szBuf[0];
if (!cDrive) {
return;
}
// トラック
hWnd = GetDlgItem(hDlg, IDC_TRACKLIST);
lr = SendMessage(hWnd, CB_GETCURSEL, 0, 0);
SendMessage(hWnd, CB_GETLBTEXT, lr, (LPARAM)szBuf);
szBuf[2] = _T('\0');
uTrack = _ttoi(szBuf);
if (99 < uTrack) {
return;
}
Play(cDrive, uTrack, hDlg);
}
//------------------------------------------------------------------------------
int Play(TCHAR cDrive, UINT uTrack, HWND hWnd)
{
int iRet = -1; // 失敗
// トラックチェック
if (g.ptoc->td[uTrack-1].data & 0x04) { // データトラック
goto Exit;
}
// トラックLBA
g.dwStartAddr = REVDWORD(g.ptoc->td[uTrack-1].dwLBA);
g.dwEndAddr = REVDWORD(g.ptoc->td[uTrack].dwLBA) - 1;
// DirectSoundの準備
if (InitDirectSound(hWnd)) {
goto Exit;
}
// 再生
g.dwLBA = g.dwStartAddr;
g.dwEoe = -1;
if (ReadBuf(0, g.dwBufferBytes)) {
goto Exit;
}
HRESULT hr;
hr = g.pSec->SetCurrentPosition(0);
hr = g.pSec->Play(0, 0, DSBPLAY_LOOPING);
DWORD dwEventOffset = WaitForMultipleObjects(NOTIFYEVENTS, g.hEvent, FALSE, INFINITE);
while (g.bPlay) {
dwEventOffset = WaitForMultipleObjects(NOTIFYEVENTS, g.hEvent, FALSE, INFINITE);
dwEventOffset -= WAIT_OBJECT_0;
if (dwEventOffset == g.dwEoe) {
break;
}
if (g.dwEoe < NOTIFYEVENTS) {
continue;
}
DWORD dwStartOffset;
DWORD dwWriteSize;
if (dwEventOffset) {
dwStartOffset = g.dspn[dwEventOffset-1].dwOffset;
dwWriteSize = g.dspn[dwEventOffset].dwOffset - dwStartOffset;
} else {
dwStartOffset = g.dspn[NOTIFYEVENTS-1].dwOffset;
dwWriteSize = g.dwBufferBytes - dwStartOffset;
}
if (ReadBuf(dwStartOffset, dwWriteSize)) {
goto Exit;
}
if (g.dwEndAddr <= g.dwLBA) {
g.dwEoe = dwEventOffset;
}
// メッセージ
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
hr = g.pSec->Stop();
iRet = 0;
Exit:
COMRELEASE(g.pNotify);
COMRELEASE(g.pSec);
COMRELEASE(g.pPri);
COMRELEASE(g.pDS);
return iRet;
}
//------------------------------------------------------------------------------
int InitDirectSound(HWND hWnd)
{
HRESULT hr;
//----------------------------------------------------------------------
// DirectSoundの初期化
hr = DirectSoundCreate8(&DSDEVID_DefaultPlayback, &g.pDS, NULL);
if (FAILED(hr)) {
return -1;
}
// 協調レベルの設定
hr = g.pDS->SetCooperativeLevel(hWnd, DSSCL_PRIORITY);
if (FAILED(hr)) {
return -1;
}
//----------------------------------------------------------------------
DSBUFFERDESC bd;
WAVEFORMATEX wfx;
ZeroMemory(&wfx, sizeof wfx);
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = 2;
wfx.nSamplesPerSec = 44100;
wfx.nAvgBytesPerSec = 44100 * 2 * 2;
wfx.nBlockAlign = 2 * 2;
wfx.wBitsPerSample = 16;
// プライマリバッファの作成
ZeroMemory(&bd, sizeof bd);
bd.dwSize = sizeof bd;
bd.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPAN;
hr = g.pDS->CreateSoundBuffer(&bd, &g.pPri, NULL);
if (FAILED(hr)) {
return -1;
}
// プライマリバッファのフォーマット指定
hr = g.pPri->SetFormat(&wfx);
if (FAILED(hr)) {
return -1;
}
// セカンダリバッファの作成
ZeroMemory(&bd, sizeof bd);
bd.dwSize = sizeof bd;
bd.lpwfxFormat = &wfx;
bd.dwFlags = DSBCAPS_GETCURRENTPOSITION2
| DSBCAPS_CTRLPOSITIONNOTIFY
| DSBCAPS_GLOBALFOCUS;
bd.dwBufferBytes = 44100 * 2 * 2 * 2; // 2秒
hr = g.pDS->CreateSoundBuffer(&bd, &g.pSec, NULL);
if (FAILED(hr)) {
return -1;
}
g.dwBufferBytes = bd.dwBufferBytes;
//----------------------------------------------------------------------
// イベント
hr = g.pSec->QueryInterface(IID_IDirectSoundNotify, (LPVOID *)&g.pNotify);
if (FAILED(hr)) {
return -1;
}
for (int n = 0; n < NOTIFYEVENTS; n++) {
g.hEvent[n] = CreateEvent(NULL, FALSE, FALSE, NULL); // CloseHandle
g.dspn[n].dwOffset = (n * bd.dwBufferBytes / NOTIFYEVENTS);
g.dspn[n].hEventNotify = g.hEvent[n];
}
hr = g.pNotify->SetNotificationPositions(NOTIFYEVENTS, g.dspn);
if (FAILED(hr)) {
return -1;
}
return 0;
}
//------------------------------------------------------------------------------
// セカンダリバッファへの読み込み
int ReadBuf(DWORD dwOffset, DWORD dwBytes)
{
BYTE byBuf[CDDASECT * READSECT + 0xf]; // パラグラフ境界
PBYTE pbyBuf = (PBYTE)(((UINT_PTR)byBuf + 0xf) & ~0xf);
UCHAR ucCdb[16];
ZeroMemory(pbyBuf, CDDASECT * READSECT);
ZeroMemory(ucCdb, sizeof ucCdb);
ucCdb[0] = 0xbe; // SCSIOP_READ_CD
ucCdb[9] = 0x10;
// IDirectSoundBuffer
LPVOID pvAudioPtr[2];
DWORD dwAudioBytes[2];
HRESULT hr;
hr = g.pSec->Lock(dwOffset, dwBytes,
&pvAudioPtr[0], &dwAudioBytes[0], &pvAudioPtr[1], &dwAudioBytes[1], 0);
DWORD dwReadSect = dwBytes / CDDASECT;
for (DWORD dwSect = 0; (dwSect < dwReadSect) && (g.dwLBA < g.dwEndAddr); ) {
DWORD dwReadSize;
if (g.dwLBA + READSECT > g.dwEndAddr) {
dwReadSize = g.dwEndAddr - g.dwLBA;
g.dwEoe = 0;
} else {
dwReadSize = dwReadSect - dwSect;
if (READSECT < dwReadSize) {
dwReadSize = READSECT;
}
}
*(PDWORD)(ucCdb + 2) = REVDWORD(g.dwLBA);
ucCdb[8] = (UCHAR)dwReadSize;
DWORD dwr = ExecCommand(g.hDev, ucCdb, 12, pbyBuf, CDDASECT * READSECT);
if (dwr == 0) {
return -1;
}
DWORD dwPos = CDDASECT * dwSect;
memcpy_s((PBYTE)pvAudioPtr[0] + dwPos, dwAudioBytes[0] - dwPos,
pbyBuf, CDDASECT * dwReadSize);
dwSect += dwReadSize;
g.dwLBA += dwReadSize;
}
hr = g.pSec->Unlock(pvAudioPtr[0], dwAudioBytes[0], pvAudioPtr[1], dwAudioBytes[1]);
return 0;
}
//------------------------------------------------------------------------------
DWORD ExecCommand(HANDLE hDevice, PUCHAR pucCdb, UCHAR ucCdbLen, PVOID pvData, ULONG ulDataLen)
{
SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER swb;
DWORD dwBytesReturned;
BOOL br;
ZeroMemory(&swb, sizeof swb);
swb.sptd.Length = sizeof swb.sptd;
//
swb.sptd.CdbLength = ucCdbLen;
swb.sptd.SenseInfoLength = sizeof swb.ucSenseBuf;
swb.sptd.DataIn = SCSI_IOCTL_DATA_IN;
swb.sptd.DataTransferLength = ulDataLen;
swb.sptd.TimeOutValue = 10; // 秒
swb.sptd.DataBuffer = pvData;
swb.sptd.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf);
memcpy_s(&swb.sptd.Cdb, sizeof swb.sptd.Cdb, pucCdb, ucCdbLen);
br = DeviceIoControl(
hDevice,
IOCTL_SCSI_PASS_THROUGH_DIRECT,
&swb, sizeof swb, // In
&swb, sizeof swb, // Out
&dwBytesReturned,
NULL);
if (br == FALSE) {
return 0;
}
return dwBytesReturned;
}
// resource script
#include <windows.h>
#include "resource.h"
IDD_MAIN DIALOGEX 100, 100, 159, 53
STYLE WS_POPUPWINDOW | WS_MINIMIZEBOX
EXSTYLE WS_EX_APPWINDOW
CAPTION "spticdda"
FONT 9, "MS Pゴシック"
BEGIN
DEFPUSHBUTTON "終了",IDC_EXIT,111,37,46,14
LTEXT "デバイス",IDC_STATIC,2,3,27,12,SS_CENTERIMAGE
COMBOBOX IDC_DEVICELIST,29,3,128,215,
CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "トラック",IDC_STATIC,2,20,27,12,SS_CENTERIMAGE
COMBOBOX IDC_TRACKLIST,29,20,128,230,
CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
PUSHBUTTON "再生",IDC_PLAY,60,37,46,14
END