以下代码测试能正常运行,供参考
代码包括DirectSound播放wav声音,但是只能播放PMC格式wav,对ADPCM的WAV无法播放,网上资料好像说要解码ADPCM,有高手看到的话求解下,还有利用mciSendCommand播放声音-好像说这个只能播放单个声音,后面就是DirectMusic对wav和MDI的播放
// TestSoundPlay.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <windowsx.h>
#include <dsound.h>
#include "dmusici.h"
#include "dxutil.h"
#include <iostream>
#include <sstream>
#include <string>
#include <math.h>
#include <conio.h>
#include<Digitalv.h>
#include <assert.h>
using namespace std;
#pragma comment( lib, "winmm.lib")
#pragma comment( lib, "dxerr8.lib")
#pragma comment( lib, "dsound.lib")
#pragma comment( lib, "dxguid.lib")
#pragma comment( lib, "strmiids.lib")
//#pragma comment( lib, "odbc32.lib")
//#pragma comment( lib, "odbccp32.lib")
HWND g_hWnd = NULL;
/***获取控制台窗口句柄方法1***/
typedef HWND (WINAPI *PROCGETCONSOLEWINDOW)();
PROCGETCONSOLEWINDOW GetConsoleWindow;
HWND GetWindHwnd()
{
HMODULE hKernel32 = GetModuleHandle("kernel32");
GetConsoleWindow = (PROCGETCONSOLEWINDOW)GetProcAddress(hKernel32,"GetConsoleWindow");
return GetConsoleWindow();
}
/***获取控制台窗口句柄方法2***/
HWND GetConsoleWindowHandle()
{
char title[512];
HWND hWnd;
GetConsoleTitle(title, sizeof(title));
hWnd = FindWindow(NULL, title);
return(hWnd);
}
//枚举声音设备
BOOL CALLBACK DSEnumProc(LPGUID lpGUID,
LPCTSTR lpszDesc,
LPCTSTR lpszDrvName,
LPVOID lpContext )
{
HWND hCombo = *(HWND *)lpContext;
LPGUID lpTemp = NULL;
if ( lpGUID != NULL )
{
if (( lpTemp = LPGUID(malloc( sizeof(GUID)))) == NULL )
return( TRUE );
memcpy( lpTemp, lpGUID, sizeof(GUID));
}
ComboBox_AddString( hCombo, lpszDesc );
ComboBox_SetItemData( hCombo,
ComboBox_FindString( hCombo, 0, lpszDesc ),
lpTemp );
return( TRUE );
}
LPDIRECTSOUND8 m_pSound; //wav
struct WAVE_HEADER
{
char riff_sig[4]; // 'RIFF'
long waveform_chunk_size; // 8
char wave_sig[4]; // 'WAVE'
char format_sig[4]; // 'fmt ' (notice space after)
long format_chunk_size; // 16;
short format_tag; // WAVE_FORMAT_PCM
short channels; // # of channels
long sample_rate; // sampling rate
long bytes_per_sec; // bytes per second
short block_align; // sample block alignment
short bits_per_sample; // bits per second
char data_sig[4]; // 'data'
DWORD data_size; // size of waveform data
};
const TCHAR* GetDefaultFilePath(string &str)
{
TCHAR szPath[MAX_PATH] = {NULL};
GetModuleFileName((HMODULE)NULL, szPath, MAX_PATH);
string strPath =szPath;
strPath = strPath.substr(0, strPath.rfind(_T('\\')) + 1);
str = strPath;
return str.c_str();
}
//从文件加载资源-辅缓冲-储存实际音频数据
IDirectSoundBuffer* CreateBufferFromFile(const TCHAR* pszName)
{
TCHAR szPath[MAX_PATH] = {NULL};
GetModuleFileName((HMODULE)NULL, szPath, MAX_PATH);
string strPath =szPath;
strPath = strPath.substr(0, strPath.rfind(_T('\\')) + 1);
strPath.append(pszName);
pszName = strPath.c_str();
HANDLE hFile = ::CreateFile(pszName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
WAVE_HEADER wh = { 0 };
DWORD dwRet = 0;
char chTest[20] = { 0 };
char chUseless[4] = {0};
DWORD dwRet2 = 0;
ReadFile(hFile, &wh, sizeof(WAVE_HEADER), &dwRet, NULL);
//check the 'fact' field
if (memcmp(wh.data_sig,"fact",4) == 0)
{
ReadFile(hFile,&chUseless,4,&dwRet2,NULL);
ReadFile(hFile,&wh.data_sig,4,&dwRet2,NULL);
ReadFile(hFile,&wh.data_size,4,&dwRet2,NULL);
}
// check the sig fields. returning if an error.
if(memcmp(wh.riff_sig, "RIFF", 4) || memcmp(wh.wave_sig, "WAVE", 4) ||
memcmp(wh.format_sig, "fmt ", 4) || memcmp(wh.data_sig, "data", 4)||
(wh.data_size == 0))
{
CloseHandle(hFile);
return NULL;
}
WAVEFORMATEX wave_format = { 0 };
wave_format.wFormatTag = WAVE_FORMAT_PCM;
wave_format.nChannels = wh.channels;
wave_format.nSamplesPerSec = wh.sample_rate;
wave_format.wBitsPerSample = wh.bits_per_sample;
wave_format.nBlockAlign = wave_format.wBitsPerSample / 8 * wave_format.nChannels;
wave_format.nAvgBytesPerSec = wave_format.nSamplesPerSec * wave_format.nBlockAlign;
DSBUFFERDESC dsbd = { 0 };
dsbd.dwSize = sizeof(DSBUFFERDESC);
dsbd.dwFlags = DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME;
dsbd.dwBufferBytes = wh.data_size;
dsbd.lpwfxFormat = &wave_format;
IDirectSoundBuffer* pBuffer = NULL;
HRESULT hr = m_pSound->CreateSoundBuffer(&dsbd, &pBuffer, NULL);
if (FAILED(hr))
{
CloseHandle(hFile);
return NULL;
}
void* pV = NULL; // Pointer to locked buffer memory
dwRet = 0;
// 锁定缓冲区-用于写入数据
if (FAILED(hr = pBuffer->Lock(0, wh.data_size, &pV, &dwRet, NULL, NULL, 0L)))
{
CloseHandle(hFile);
return NULL;
}
//读入数据
ReadFile(hFile, pV, wh.data_size, &dwRet, NULL);
if (dwRet == 0)
{
// Wav is blank, so just fill with silence
FillMemory((BYTE*)pV, wh.data_size,(BYTE)(wave_format.wBitsPerSample == 8 ? 128 : 0 ));
}
else if (dwRet < wh.data_size)
{
FillMemory(((BYTE*)pV + dwRet), wh.data_size - dwRet,(BYTE)(wave_format.wBitsPerSample == 8 ? 128 : 0 ));
}
pBuffer->Unlock(pV, wh.data_size, NULL, 0 );
return pBuffer;
}
return NULL;
}
//设置主缓冲区格式
bool SetPrimBuffFmat(HRESULT &hr)
{
LPDIRECTSOUNDBUFFER pDSBPrimary;
// Get the primary buffer
DSBUFFERDESC dsbd;
ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) );
dsbd.dwSize = sizeof(DSBUFFERDESC);
dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;//主缓冲标记,详见ReadMe.txt
dsbd.dwBufferBytes = 0;
dsbd.lpwfxFormat = NULL;
if(FAILED(m_pSound->CreateSoundBuffer( &dsbd, &pDSBPrimary, NULL)))
{
return false;
}
// 设置音频格式属性
WAVEFORMATEX wfx;
ZeroMemory( &wfx, sizeof(WAVEFORMATEX) );
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = (WORD)2;
wfx.nSamplesPerSec = 22050; // 22 Khz (CD quality is ~ 44 Khz, so this is "half-CD" quality)
wfx.wBitsPerSample = (WORD) 16; // 2 bytes per sample * 22050 samples/sec = 44100 bytes/sec)
wfx.nBlockAlign = (WORD) (wfx.wBitsPerSample / 8 * wfx.nChannels);
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
if( FAILED( hr = pDSBPrimary->SetFormat(&wfx) ) ) {
return 0;
}
// 释放主缓冲区接口-非物理删除,COM对象只有在所有接口都释放时才删除
SAFE_RELEASE( pDSBPrimary );
return 0;
}
// wav play
IDirectSoundBuffer *pDSBuffer = NULL;
int PlayForWav(char *Filename = NULL)
{
//枚举声音设备
/*************
HWND cmd = GetWindHwnd();
HWND hCombo = cmd;
//DSDEVID_DefaultPlayback-声音播放设备,可以通过DirectSoundEnumerate 枚举出所有可用设备
if FAILED(DirectSoundEnumerate((LPDSENUMCALLBACK)DSEnumProc,
(VOID*)&hCombo))
{
return( TRUE );
}
****************/
//创建设备对象
HRESULT hr = DirectSoundCreate8(&DSDEVID_DefaultPlayback, &m_pSound, NULL);
if (FAILED(hr))
{
return 0;
}
//设置声音协作度--多个程序可能使用同个设备,为防止冲突,不设置则没声音
if(FAILED(hr = m_pSound->SetCooperativeLevel(GetConsoleWindowHandle(), DSSCL_NORMAL))) {
return 0;
}
//设置主缓冲区格式
SetPrimBuffFmat(hr);
//加载资源
pDSBuffer=CreateBufferFromFile(Filename);
if(pDSBuffer != NULL) {
// Play sound looping
pDSBuffer->SetCurrentPosition(0);
pDSBuffer->SetVolume(DSBVOLUME_MAX);
pDSBuffer->Play(0,0,DSBPLAY_LOOPING);
}
return 1;
}
// mid playe intface
// DirectMusic Performance, loader, and segment objects
IDirectMusicPerformance8 *g_pDMPerformance; // 演奏对象
IDirectMusicLoader8 *g_pDMLoader; // 加载器
IDirectMusicSegment8 *g_pDMSegment; // 将要播放的生效段
IDirectMusicAudioPath *g_pDMPath;
void InitMidPlay()
{
CoInitialize(0);
// Create the DirectMusic loader object
CoCreateInstance(CLSID_DirectMusicLoader, NULL, CLSCTX_INPROC,
IID_IDirectMusicLoader8, (void**)&g_pDMLoader);
// Create the DirectMusic performance object
CoCreateInstance(CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC,
IID_IDirectMusicPerformance8, (void**)&g_pDMPerformance);
// Initialize the performance with the standard audio path.
// This initializes both DirectMusic and DirectSound and
// sets up the synthesizer.
//g_pDMPerformance->InitAudio(NULL, // 指向一个关联的IDirectMusic对象
// NULL, // 指向一个关联的IDirectSound对象
// g_hWnd, // 播放关联窗口,可以NULL
// DMUS_APATH_SHARED_STEREOPLUSREVERB, // 声音通道格式:立体声混响,3D音效,单声道,立体声
// 128, // 音乐通道数量
// DMUS_AUDIOF_ALL, // 声音播放参数-效果
// NULL // 指向一个DMUS_AUDIOPARAMS对象,更纤细地说明各种参数
// );
g_pDMPerformance->InitAudio(NULL, NULL, g_hWnd,
DMUS_APATH_SHARED_STEREOPLUSREVERB, 64,
DMUS_AUDIOF_ALL, NULL);
//g_pDMpath 控制音量
g_pDMPerformance->GetDefaultAudioPath(&g_pDMPath);
// Tell DirectMusic where the default search path is
char strPath[MAX_PATH];
WCHAR wstrSearchPath[MAX_PATH];
//GetCurrentDirectory(MAX_PATH, strPath);
string str;
const char *Path = GetDefaultFilePath(str);
sprintf(strPath,Path);
MultiByteToWideChar(CP_ACP, 0, strPath, -1, wstrSearchPath, MAX_PATH);
g_pDMLoader->SetSearchDirectory(GUID_DirectMusicAllTypes, // 指定搜索的文件格式
wstrSearchPath, // 指定搜索路径
FALSE // 如果为TRUE,则在设定路径前删除所有对象信息
); // 防止当有同名路径存在时访问上级目录
//g_pDMPath->SetVolume(0,0);//-9600~0
}
BOOL PlayMID(const char *Filename)
{
string str;
GetDefaultFilePath(str);
str.append(Filename);
Filename = str.c_str();
DMUS_OBJECTDESC dmod;
// Get the object
ZeroMemory(&dmod, sizeof(DMUS_OBJECTDESC));
dmod.dwSize = sizeof(DMUS_OBJECTDESC);
dmod.guidClass = CLSID_DirectMusicSegment;
dmod.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH;
mbstowcs(dmod.wszFileName, Filename, MAX_PATH);
if(FAILED(g_pDMLoader->GetObject(&dmod, IID_IDirectMusicSegment8, (LPVOID*)&g_pDMSegment)))
return FALSE;
// Setup MIDI playing
if(strstr(Filename, ".mid") != NULL) {
if(FAILED(g_pDMSegment->SetParam(GUID_StandardMIDIFile, 0xFFFFFFFF, 0, 0, NULL)))
return FALSE;
}
// Download the band
if(FAILED(g_pDMSegment->Download(g_pDMPerformance)))
return FALSE;
// Set to loop forever
g_pDMSegment->SetRepeats(DMUS_SEG_REPEAT_INFINITE);
// Play on default audio path
g_pDMPerformance->PlaySegmentEx(g_pDMSegment, NULL, NULL, 0, 0, NULL, NULL, NULL);
g_pDMPath->SetVolume(0,0);
return FALSE;
}
//MID playe 1
void playForMID(char *Filename = NULL)
{
InitMidPlay();
PlayMID(Filename);
}
//mid play 2
MCI_OPEN_PARMS OpenParms;
void playForMid2(const char *Filename = NULL)
{
string str;
GetDefaultFilePath(str);
str.append(Filename);
Filename = str.c_str();
//1. 打开设备
OpenParms.lpstrDeviceType = (LPCSTR) MCI_DEVTYPE_SEQUENCER; //MIDI类型
OpenParms.lpstrElementName = (LPCSTR) Filename;
OpenParms.wDeviceID = 0;
mciSendCommand (NULL, MCI_OPEN, MCI_WAIT | MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID | MCI_OPEN_ELEMENT, (DWORD)(LPVOID) &OpenParms);
//MCI设备ID指明打开了哪个设备,当发送了MCI_OPEN命令时,这个值在参数块中返回——应被保存备用。
//2. 关闭设备
//mciSendCommand (m_wDeviceID, MCI_CLOSE, NULL, NULL);
//3. 播放
MCI_PLAY_PARMS PlayParms;
PlayParms.dwFrom = 0;
// 指定从什么地方(时间)播放 MCI_DGV_PLAY_REPEAT-无效
mciSendCommand (OpenParms.wDeviceID, MCI_PLAY, MCI_FROM|MCI_NOTIFY, (DWORD)(LPVOID)&PlayParms);
}
void ExitMIDInt()
{
//释放DirectMusic
g_pDMPerformance->CloseDown();
g_pDMSegment->Release();
g_pDMPerformance->Release();
g_pDMLoader->Release();
CoUninitialize();//停止使用COM
}
MUSIC_TIME musicTime;
BOOL bPauseMID = FALSE;
int _tmain(int argc, _TCHAR* argv[])
{
g_hWnd = GetWindHwnd();
int i=0;
cout<<"按1号键 播放wav,2号键暂停wav,3号继续播放wav"<<endl;
cout<<"按4号键 播放MID-方法2,5号键暂停MID-2,6号继续播放MID-2"<<endl;
cout<<"按q号键 播放MID,w号键暂停MID,e号继续播放MID"<<endl;
int a = getch();//49-1,50-2.....
while(a != 0)
{
putch(a);
cout<<endl;
switch(a)
{
case 49://1
{
PlayForWav("test3.wav");
cout<<"播放 test.wav"<<endl;
}
break;
case 50://2
{
pDSBuffer->Stop();
cout<<"暂停 test.wav"<<endl;
}
break;
case 51://3
{
pDSBuffer->Play(0,0,DSBPLAY_LOOPING);
cout<<"继续播放 test.wav"<<endl;
}
break;
case 52://4 播放2
{
playForMid2("escape.mid");
cout<<"MID2播放 test.wav"<<endl;
}
break;
case 53://5 暂停2
{
MCI_PLAY_PARMS PlayParms;
mciSendCommand (OpenParms.wDeviceID, MCI_PAUSE, 0, (DWORD)(LPVOID) &PlayParms);
cout<<"MID2暂停播放 test.wav"<<endl;
}
break;
case 54://6 继续2
{
MCI_STATUS_PARMS StatusParms;
mciSendCommand(OpenParms.wDeviceID,MCI_PLAY,0,(DWORD)(LPVOID) &StatusParms);
cout<<"MID2继续播放 test.wav"<<endl;
}
break;
case 113://q
case 81:
{
playForMID("GAME_END.WAV");
cout<<"播放 escape.mid"<<endl;
}
break;
case 119://w
case 87:
{
//MUSIC_TIME就是一个long类型
g_pDMPerformance->GetTime(NULL, &musicTime);//得到暂停的位置
bPauseMID = TRUE;
REFERENCE_TIME refTime;
//g_pDMPerformance->GetQueueTime()
//g_pDMSegment->GetStartPoint(&musicTime);
g_pDMPerformance->Stop(g_pDMSegment,//要停止的段,NULL表示全部段都停止
NULL,//段状态
0,//多少时间后停止,0表示立即
0//标志
);
cout<<"暂停播放 escape.mid"<<endl;
}
break;
case 101://e
case 69:
{
if(bPauseMID)
{
g_pDMPerformance->GetTime(NULL, &musicTime);
}
g_pDMSegment->SetStartPoint(musicTime);//播放点
// g_pDMSegment->GetStartPoint(&musicTime);
g_pDMPerformance->PlaySegmentEx(g_pDMSegment, NULL, NULL, 0, 0, NULL, NULL, NULL);
g_pDMSegment->SetStartPoint(0);
cout<<"继续播放 escape.mid"<<endl;
}
break;
}
a = getch();
}
getch();
return 0;
}