fxtz

时间:2024-10-08 04:53:12编辑:思创君

东方绯想天和东方非想天则全部人物、格式:FXT: FXTZ:

FXT:博丽 灵梦、雾雨 魔理沙、十六夜 咲夜、爱丽丝·玛格特罗依德、帕秋莉·诺蕾姬、魂魄 妖梦、蕾米莉亚·斯卡雷特、西行寺 幽幽子、八云 紫、伊吹 萃香、铃仙·优昙华院·因幡、射命丸 文、小野冢 小町、永江依玖、比那名居 天子。

FXTZ:东风谷 早苗、琪露诺、红 美铃、灵乌路 空、泄矢诹访子。(此外也可以使用FXT全人物)


如何用Directshow访问存储在内存中的资源?

Introduction (序)
这一章我们学习怎样用DX来播放声音和音乐。我们将会使用DirectX Audio来播放WAV和MIDI文件,使用DirectShow来播放MP3文件。此章我们仍然有一个简单的程序作例子,它会播放MP3的背景音乐,当用户用鼠标点击某个数字时它会播放一些音效。

DirectX Audio and DirectShow (DirectX Audio和 DirectShow)
我们已经学习过Direct3D和DirectInput了,现在,我要介绍另外两个DirectX组件:DirectX Audio和DirectShow。我们用DirectX Audio来播放WAV和MIDI文件;用DirectShow来播放媒体流,例如AVI和MP3。在此教程中,我们只学习一下用DirectShow播放MP3的方法。

Wav, Midi and Mp3 - When should I use what?
(WAV,MIDI和MP3——什么时候该用什么?)
那末,什么时候用什么格式好呢?嗯,这基本上取决于你的个人爱好。下面是这些格式的简介,过后我会说一下我的选择:

Wav files (WAV文件)
WAV是一种未经压缩的数字音频,CD音质,但是体积非常庞大。

Midi files (MIDI文件)
MIDI文件并没有保存音频记录,它实际上更像是一套演奏指令,所以,它的体积是非常小的。MIDI的音质完全取决于演奏设备(如我们的声卡),它在高端的设备上能表现出色,而在低端设备上则表现较差。

Mp3 files (MP3文件)
同WAV文件一样,MP3也是一种数字音频格式。不同的是,MP3文件是经过压缩的,而且是有损压缩,这意味着它的体积将大大地减小,而音质上将会有一些失真(实际上接近CD音质,基本听不出失真)。而且,MP3是一种媒体流,这意味着在播放它的时候不会将它整个的读入,取而代之的是分期的做部分读入。

My preference (我的选择)
你大概会选择MP3或MIDI来作背景音乐,特别是当你希望用户能从网上下载你的程序时。MP3和MIDI格式文件的体积都比较小,它们适合做长时间的背景音乐播放。而当需要播放简短的声效时,例如爆炸声等,我更趋向于使用WAV文件,品质能稍好一些。如果文件的体积不是问题的话,实际上我们可以使用WAV文件来播放所有的声音和音乐。

Include and Library files (头文件与库文件)
我们的项目需要增加以下的头文件和库文件:

o dmusici.h
o dsound.h
o dshow.h
o dsound.lib
o strmiids.lib


Setting up DirectX Audio (设置DirectX Audio)
要使用Direct Audio我们需要创建两个对象:演奏对象(Performance Object,有些资料翻译为执行对象)和加载器(Loader)。演奏对象是Direct Audio中的最高级对象,用于处理从源到合成器的数据流。加载器用于把文件(WAV或MIDI)加载到声音段以待播放。这两个对象在整个应用程序中我们每样只需要一个,所以,我们已经把它们创建为CGame的成员变量了。下面是它们的定义:

IDirectMusicPerformance8* m_pDirectAudioPerformance;
IDirectMusicLoader8* m_pDirectAudioLoader;

由于Direct Audio是一个纯COM,所以我们需要初始化COM。其实很简单,我们只需要在CGame的构造函数中调用CoInitialize函数即可:

CoInitialize(NULL);

初始化COM后,我们就可以创建上述的那两个DirectX Audio对象了。为此,我们给CGame做了一个新的函数InitialiseDirectAudio,它将会在Initialise函数中被调用。代码如下:

bool CGame::InitialiseDirectAudio(HWND hWnd)
{
LogInfo("<br>Initialise DirectAudio:");


//Create the DirectAudio performance object
if(CoCreateInstance(CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC,
IID_IDirectMusicPerformance8,
(void**) &m_pDirectAudioPerformance) != S_OK)
{
LogError("<li>Failed to create the DirectAudio perfomance object.");
return false;
}
else
{
LogInfo("<li>DirectAudio perfomance object created OK.");
}

//Create the DirectAudio loader object
if(CoCreateInstance(CLSID_DirectMusicLoader, NULL, CLSCTX_INPROC,
IID_IDirectMusicLoader8,
(void**) &m_pDirectAudioLoader) != S_OK)
{
LogError("<li>Failed to create the DirectAudio loader object.");
return false;
}
else
{
LogInfo("<li>DirectAudio loader object created OK.");
}

//Initialise the performance object
if(FAILED(m_pDirectAudioPerformance->InitAudio(NULL, NULL, hWnd,
DMUS_APATH_SHARED_STEREOPLUSREVERB,
64, DMUS_AUDIOF_ALL, NULL)))
{
LogError("<li>Failed to initialise the DirectAudio perfomance object.");
return false;
}
else
{
LogInfo("<li>Initialised the DirectAudio perfomance object OK.");
}

//Get the our applications "sounds" directory.
CHAR strSoundPath[MAX_PATH];
GetCurrentDirectory(MAX_PATH, strSoundPath);
strcat(strSoundPath, "\\Sounds");

//Convert the path to unicode.
WCHAR wstrSoundPath[MAX_PATH];
MultiByteToWideChar(CP_ACP, 0, strSoundPath, -1, wstrSoundPath, MAX_PATH);

//Set the search directory.
if(FAILED(m_pDirectAudioLoader->SetSearchDirectory(GUID_DirectMusicAllTypes,
wstrSoundPath, FALSE)))
{
LogError("<li>Failed to set the search directory '%s'.", strSoundPath);
return false;
}
else
{
LogInfo("<li>Search directory '%s' set OK.", strSoundPath);
}


return true;
}

我们用CoCreateInstance函数创建了演奏对象和加载器,然后调用了InitAudio模块来初始化演奏对象。上面代码中给InitAudio的参数是很典型的,所以你可以沿用它(查看一下SDK中的关于InitAudio函数以及它的参数的内容以获得更多的了解)。最后,我们设置了搜索目录。搜索目录应该被设置为存放声音文件的目录,以便加载器可以正确的加载声音文件。在本例中,搜索目录被设置为“Sounds”文件夹,它应该在本例的项目文件夹当中。

A new class - CSound (一个新的类——CSound)
我们已经为使用DirectX Audio作了必要的准备工作,现在,我们就可以加载、播放声音和音乐了。为此,我们做了一个新的类CSound,下面是它的代码,稍后我会解释它是怎样工作的:

CSound::CSound()
{
m_pDirectAudioPerformance = NULL;
m_pDirectAudioLoader = NULL;
m_pSegment = NULL;
m_pGraph = NULL;
m_pMediaControl = NULL;
m_pMediaPosition = NULL;
m_enumFormat = Unknown;

LogInfo("<li>Sound created OK");
}

void CSound::InitialiseForWavMidi(IDirectMusicPerformance8* pDirectAudioPerformance, IDirectMusicLoader8* pDirectAudioLoader)
{
m_pDirectAudioPerformance = pDirectAudioPerformance;
m_pDirectAudioLoader = pDirectAudioLoader;

m_enumFormat = WavMidi;
}

void CSound::InitialiseForMP3()
{
CoCreateInstance(CLSID_FilterGraph, NULL,
CLSCTX_INPROC, IID_IGraphBuilder, (void**)&m_pGraph);

m_pGraph->QueryInterface(IID_IMediaControl, (void**)&m_pMediaControl);

m_pGraph->QueryInterface(IID_IMediaPosition, (void**)&m_pMediaPosition);

m_enumFormat = MP3;
}


CSound::~CSound()
{
Stop();

SafeRelease(m_pSegment);
SafeRelease(m_pGraph);
SafeRelease(m_pMediaControl);
SafeRelease(m_pMediaPosition);

LogInfo("<li>Sound destroyed OK");
}

bool CSound::LoadSound(const char* szSoundFileName)
{
WCHAR wstrSoundPath[MAX_PATH];
CHAR strSoundPath[MAX_PATH];

switch(m_enumFormat)
{
case MP3:
//Get the our applications "sounds" directory.
GetCurrentDirectory(MAX_PATH, strSoundPath);
strcat(strSoundPath, "\\Sounds\\");
strcat(strSoundPath, szSoundFileName);

//Convert the path to unicode.
MultiByteToWideChar(CP_ACP, 0, strSoundPath, -1,
wstrSoundPath, MAX_PATH);

m_pGraph->RenderFile(wstrSoundPath, NULL);
break;
case WavMidi:
//Convert the filename to unicode.
MultiByteToWideChar(CP_ACP, 0, szSoundFileName, -1,
wstrSoundPath, MAX_PATH);

//Load a sound
m_pDirectAudioLoader->LoadObjectFromFile(CLSID_DirectMusicSegment,
IID_IDirectMusicSegment8,
wstrSoundPath,
(void**) &m_pSegment);

m_pSegment->Download(m_pDirectAudioPerformance);
break;
default:
return false;
}

return true;
}

bool CSound::Play(DWORD dwNumOfRepeats)
{

switch(m_enumFormat)
{
case MP3:
//Make sure that we are at the start of the stream
m_pMediaPosition->put_CurrentPosition(0);

//Play mp3
m_pMediaControl->Run();
break;
case WavMidi:
//Set the number of times the sound repeats
m_pSegment->SetRepeats(dwNumOfRepeats); //To loop the sound forever, pass
//in DMUS_SEG_REPEAT_INFINITE

//Play the loaded sound
m_pDirectAudioPerformance->PlaySegmentEx(m_pSegment, NULL, NULL, 0, 0,
NULL, NULL, NULL);
break;
default:
return false;
}

return true;
}

bool CSound::Stop()
{
switch(m_enumFormat)
{
case MP3:
m_pMediaControl->Stop();
break;
case WavMidi:
//Stop the loaded sound
m_pDirectAudioPerformance->StopEx(m_pSegment, 0, 0);
break;
default:
return false;
}

return true;
}

bool CSound::IsPlaying()
{
switch(m_enumFormat)
{
case MP3:
REFTIME refPosition;
REFTIME refDuration;

m_pMediaPosition->get_CurrentPosition(&refPosition);
m_pMediaPosition->get_Duration(&refDuration);

if(refPosition < refDuration)
{
return true;
}
else
{
return false;
}
break;
case WavMidi:
if(m_pDirectAudioPerformance->IsPlaying(m_pSegment, NULL) == S_OK)
{
return true;
}
else
{
return false;
}
break;
default:
return false;
}
}

那末,怎样用CSound来播放音乐呢?这很简单。首先,创建一个CSound对象,然后根据你想播放的声音类型来调用InitialiseForWavMidi或是InitialiseForMP3,二者选其一。接着,调用LoadSound读取声音文件。最后,调用Play模块来播放声音即可。很简单,不是吗?嗯,还有两个模块:Stop模块用来停止播放;IsPlaying模块用来检测声音是否正在播放。下面介绍了各个模块是怎样工作的:

InitialiseForWavMidi
InitialiseForWavMidi模块用于初始化CSound对象以待播放WAV或MIDI文件。它的两个输入参数是我们在CGame中创建好的演奏对象和加载器的接口指针,然后这两个指针会被保存为它的成员变量以备用。我们会把CSound的格式设置为WavMidi。

InitialiseForMP3
InitialiseForMP3模块用于初始化CSound对象以待播放MP3文件,它没有输入参数。InitialiseForMP3调用CoCreateInstance函数创建了一个DirectShow过滤器图管理器对象。我们能使用CoCreateInstance是因为我们在CGame的构造函数中已经调用过CoInitialize函数了。有了过滤器图管理器对象之后,我们调用它的模块QueryInterface来创建另外两个对象:一个媒体控制对象和一个媒体定位对象。这三个对象的指针将会被保存为CSound对象的成员变量。同理,我们把CSound的格式设置为MP3;

LoadSound
初始化好CSound对象后,我们就可以调用LoadSound来读取声音文件了。LoadSound只有一个参数,就是声音文件的文件名。这里不是一个路径而是一个文件名是因为所有的声音文件都应该在我们的项目的“Sounds”目录中。

如果这个CSound对象的格式为MP3,我们首先会构造出一个完整的文件路径,然后把它转换为Unicode字符集的字符串。接着,我们只需要调用过滤器图管理器对象的RenderFile模块并构造一个用于播放指定文件的过滤器图即可。

如果这个CSound对象的格式为WavMidi,我们首先会把文件名转换成Unicode字符集的字符串。接着我们会调用LoadObjectFromFile,它会载入文件并返回一个段(segment)指针。最后,我们调用段的Download模块下载波段到演奏对象里。

现在我们已经准备好来播放声音了。

Play
当文件载入后,我们就能播放它了。Play模块唯一的可选参数(默认为0)就是重复播放次数,只在播放WAV或MIDI时有效。

如果这个CSound对象的格式为MP3,我们首先会调用媒体定位对象的put_CurrentPosition模块来确认一下我们是否在媒体流的开始处。然后,我们调用媒体控制对象的Run模块来播放MP3。

如果这个CSound对象的格式为WavMidi,我们首先会设置重复次数。然后我们调用PlaySegmentEx来播放已经载入的段。如果你想让声音永远重复,输入DMUS_SEG_REPEAT_INFINITE恒量。

Stop
这能简单地停止正在播放的声音。如果是MP3,就调用媒体控制对象的Stop模块。如果是WAV或MIDI,就调用演奏对象的StopEx模块并输入要停止的段。

IsPlaying
最后,IsPlaying模块用于检测当前是否正在播放。是就返回true,不是则返回false。如果是MP3,我们会根据当前的媒体流定位与整个媒体流的长度做对比,以得知我们是否正在播放(看是否在流的终点)。如果是WAV,我们就调用演奏对象的IsPlaying模块并输入要检测的段以测试。

Cleaning up (清理)
在Csound的析构函数中我们会停止声音的播放并释放对象。在CGame中有一个新的函数CleanUpDirectAudio代码如下:

void CGame::CleanUpDirectAudio()
{
//Stop all sounds.
m_pDirectAudioPerformance->Stop(NULL, NULL, 0, 0);

//CleanUp
m_pDirectAudioPerformance->CloseDown();

SafeRelease(m_pDirectAudioLoader);
SafeRelease(m_pDirectAudioPerformance);

LogInfo("<li>CleanUpDirectAudio finished.");
}

Here we first stop all sounds from playing (just in case), then we close down the performance object before releasing it and the loader object. Also, in the CGame deconstructor we need to call CoUninitialize to close down the COM library (shown below).
这里我们首先停止所有声音的播放,然后关闭演奏对象,释放演奏对象和加载器。而且,在CGame的析构函数中我们会调用CoUninitialize来关掉COM:

CoUninitialize();

那末现在我们能在程序中播放音乐和声音了。当程序开始时,MP3的背景音乐就会开始播放。移动鼠标并点击不同的数字我们对听到不同的声音。这里的背景MP3只是一个小片断的循环,愿意的话你可以替换成你喜欢的音乐。(译者:我把我的MP3都放了一遍爽)



Summary (摘要)
In this tutorial we learnt how to play sounds and music using two new DirectX components, DirectX Audio and DirectShow. In the next tutorial we will use all of the skills we have seen so far to create our first game: 3D Pong. This game will be simple, but will have all of the key features that you would expect from any game.


上一篇:仙剑奇侠传3 花絮

下一篇:没有了