開発環境 Microsoft Visual C++ 2010 Express (SP1)
実行環境 Microsoft Windows XP Home Edition (SP3)
プロジェクトの種類 Win32 プロジェクト
プロジェクト名 spticdda
アプリケーションの種類 Windows アプリケーション
追加のオプション 空のプロジェクト
文字セット Unicode

Microsoft DirectX SDK (February 2010)
Windows Driver Kit Version 7.1.0

参考

spticdda.cpp
/*
[構成プロパティ]-[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.h
#define IDD_MAIN		100
 
#define IDC_STATIC		-1
#define IDC_EXIT		1000
#define IDC_DEVICELIST		1001
#define IDC_TRACKLIST		1002
#define IDC_PLAY		1003
 

spticdda.rc
// 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
 
最終更新:2012年10月14日 22:20