天行健 君子当自强而不息

用DirectX Audio和DirectShow播放声音和音乐(7)


本篇是用DirectX Audio和DirectShow播放声音和音乐(6)的续篇。



加入到MP3的革命中

MP3 是一种音频压缩格式,它通过删除或修改音乐中不易被人耳察觉的部分来使音乐更小,占用的存储空间更少。在项目中使用MP3(.MP3文件)需要使用 DirectX中的 DirectShow组件,在这个组件的帮助下,只需几行短短的代码,就能使用任意的MP3文件了(DirectShow也支持其他的媒体文件,比如 WMA,AVI,MPG等)。当然要想使用更多的媒体文件,必须已经在操作系统中安装了解码器。

解码器(codec)是一个程序,用于解码或编码一些指定的格式(比如MP3解码器专门解码.MP3文件)。通常可以从发明或者创建这种格式的公司中获取这种格式的解码器。比如,MP3解码器来自于Fraunhofer Insitute。幸运的是,MP3解码器等几种比较流行的解码器已经被集成到操作系统中(比如.mp3,.avi,.mpg等),而无需另外从 internet下载这些格式的解码器了。

要在项目中使用DirectShow,需要包含dshow.h头文件,并且在链接库中加入strmiids.lib。



使用DirectShow

DirectX是一组COM接口组件,DirectShow也不例外,DirectShow中经常使用的组件如下:

IGraphBuilder:  帮助建立滤波图,滤波过滤图是一组对象或接口的集合,用于处理某种媒体文件。
IMediaControl:控制数据在滤波图中的流程,使用该接口控制音乐的回放。
IMediaEvents:   从滤波图中获取事件及通告,当希望知道在滤波图中发生了什么的时候这个对象很有用,比如希望知道一个音乐是否仍然在播放或者已经停止播放。

其中第一个接口IGraphBuilder是比较重要的对象,其他对象都依赖于它,或者靠它创建。它创建滤波器,用于处理媒体问题,另外很多有用的功能也是依靠这个对象。

This interface provides methods that enable an application to build a filter graph. The Filter Graph Manager implements this interface.

IGraphBuilder inherits from the IFilterGraph interface. IFilterGraph provides basic operations, such as adding a filter to the graph or connecting two pins. IGraphBuilder adds further methods that construct graphs from partial information. For example, the IGraphBuilder::RenderFile method builds a graph for file playback, given the name of the file. The IGraphBuilder::Render method renders data from an output pin by connecting new filters to the pin.

Using these methods, an application does not need to specify every filter and pin connection in the graph. Instead, the Filter Graph Manager selects filters that are registered on the user's system, adds them to the graph, and connects them. For more information, see Intelligent Connect.

In addition to the methods inherited from IUnknown and IFilterGraph, the IGraphBuilder interface exposes the following methods.

Method Description
Connect Connects two pins. If they will not connect directly, this method connects them with intervening transforms.
Render Adds a chain of filters to a specified output pin to render it.
RenderFile Builds a filter graph that renders the specified file.
AddSourceFilter Adds a source filter to the filter graph for a specific file.
SetLogFile Sets the file for logging actions taken when attempting to perform an operation.
Abort Requests that the graph builder return as soon as possible from its current task.
ShouldOperationContinue Queries whether the current operation should continue.


使用DirectShow播放MP3的第一步是调用 CoCreateInstance函数创建滤波图对象IGraphBuilder。

    // initialize COM
    //
    // initialize the COM library on the current thread and identifies the concurrency model as single-thread
    // apartment (STA).
    CoInitialize(0);

    
// create the DirectMusic performance object
    //
    // creates a single uninitialized object of the class associated with a specified CLSID.
    if(FAILED(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, 
                               (
void**)&g_graph_builder)))
    {
        MessageBox(NULL, "Unable to create DirectShow Graph Builder object.", "Error", MB_OK);
        
return FALSE;
    }

一旦创建对象IGraphBuilder成功,就可以请求另两个接口了:
 
// Query for the media control and event objects
g_graph_builder->QueryInterface(IID_IMediaControl, (void**)&g_media_control);
g_graph_builder->QueryInterface(IID_IMediaEvent, (
void**)&g_media_event);
 

加载媒体文件
 
实际上, DirectShow并不加载媒体文件,而是创建一个DirectShow滤波器到文件的连接。数据在解码的时候被流化,这样可以减少在播放过程中的内存使用,创建连接的过程叫做渲染(rendering)。渲染一个文件,需要调用IGraphBuilder::RenderFile函数。

Builds a filter graph that renders the specified file.

Syntax

HRESULT RenderFile(
LPCWSTR lpwstrFile,
LPCWSTR lpwstrPlayList
);

Parameters

lpwstrFile
[in] Pointer to the name of the file containing the data to be rendered.
lpwstrPlayList
[in] Pointer to the playlist name. Reserved; must be NULL. (This parameter is currently unimplemented.)

Return Value

Returns an HRESULT value, which can include one of the following:

  • VFW_S_AUDIO_NOT_RENDERED
  • VFW_S_DUPLICATE_NAME
  • VFW_S_PARTIAL_RENDER
  • VFW_S_RPZA
  • VFW_S_VIDEO_NOT_RENDERED

Remarks

If the lpwstrPlayList parameter is NULL, this method would use the default playlist, which typically renders the entire file.

 

控制音乐的播放

在媒体文件被渲染之后,就可以使用另外两个接口 IMediaControl和IMediaEvent进行播放或者对播放进行控制了。

第一个接口 IMediaControl用于播放和各种播放相关的操作,这个接口有三个函数:IMediaControl::Run,IMediaControl:: Pause,IMediaControl::Stop。

The IMediaControl interface provides methods for controlling the flow of data through the filter graph. It includes methods for running, pausing, and stopping the graph. The Filter Graph Manager implements this interface. For more information on filter graph states, see Data Flow in the Filter Graph.

IMediaControl also provides Automation-compatible methods for building graphs. Applications written in Microsoft® Visual Basic® can use these methods to construct filter graphs or retrieve information about the graph. Applications written in C or C++ should use the methods in IGraphBuilder and IFilterGraph2 instead, because they are more efficient.

In addition to the methods inherited from IDispatch, the IMediaControl interface exposes the following methods.

Method Description
Run Runs all the filters in the filter graph.
Pause Pauses all filters in the filter graph.
Stop Stops all the filters in the filter graph.
StopWhenReady Pauses the filter graph, allowing filters to queue data, and then stops the filter graph.
GetState Retrieves the state of the filter graph.
RenderFile Builds a filter graph that renders the specified file. (For Visual Basic.)
AddSourceFilter Adds a source filter to the filter graph, for a specified file. (For Visual Basic.)
get_FilterCollection Retrieves a collection of the filters in the filter graph. (For Visual Basic.)
get_RegFilterCollection Retrieves a collection of all the filters listed in the registry. (For Visual Basic.)

 

如果要开始播放一段音乐,调用 IMediaControl::Run就可以了。

Switches the entire filter graph into a running state.

Syntax

HRESULT Run(void); 

Return Value

Returns S_OK if the graph is actually running.

Returns S_FALSE if the graph is preparing to run (the graph will run automatically when it's ready). Call GetState to wait for the transition to the running state to complete or to check if the transition has completed. If the method returns S_FALSE, subsequent calls to GetState will return a value of State_Running when the graph is actually running. If the transition to the running state is not complete GetState can return a return code of VFW_S_STATE_INTERMEDIATE.

Returns an HRESULT error code if the graph could not run and is now stopped.

Remarks

In a running state, data is pushed down the filter graph and rendered. The graph remains in a running state until it is stopped by the IMediaControl::Pause or IMediaControl::Stop method. The graph remains in a running state even after notifying the application of completion (that is, the EC_COMPLETE notification is sent to the application). This allows the application to determine whether to pause or stop after completion.

If the filter graph is in the stopped state, this method first pauses the graph before running.

If an error value is returned, some filters within the graph might have successfully entered the running state. In a multistream graph, entire streams might be playing successfully. The application must determine whether to stop running or not.

一旦播放开始,就可以随时暂停播放或者停止播放,如果希望暂停播放,调用IMediaControl::Pause函数。

Pauses all the filters in the filter graph.

Syntax

HRESULT Pause(void);

Return Value

Returns S_OK if the graph is actually paused.

Returns S_FALSE if the graph is in paused state but some filters have not completed the transition to pause. Call GetState to wait for the transition to the paused state to complete or to check if the transition has completed. If the method returns S_FALSE, subsequent calls to GetState will return a value of State_Paused when the graph is paused. If the transition to paused is not complete GetState can return a return code of VFW_S_STATE_INTERMEDIATE.

Returns an HRESULT error code if the graph could not transition to paused state and is now stopped.

Remarks

In the paused state, filters process data but do not render it. Data is pushed down the filter graph and is processed by transform filters as far as buffering permits. No data is rendered (except that media types capable of being rendered statically, such as video, have a static, poster frame rendered in paused mode). Therefore, putting a filter graph into a paused state cues the graph for immediate rendering when put into a running state.

开始播放之后,可以随时调用IMediaContrl::Stop函数来停止播放。

Switches all filters in the filter graph to a stopped state.

Syntax

HRESULT Stop(void); 

Return Value

Returns an HRESULT value.

Remarks

In this mode, filters release resources and no data is processed. If the filters are in a running state, this method pauses them before stopping them. This allows video renderers to make a copy of the current frame for poster frame display while stopped.

以下代码演示了如何播放MP3文件。
 
//--------------------------------------------------------------------------------
// Play mp3 which specified by filename.
//--------------------------------------------------------------------------------
BOOL Play_MP3(char* filename)
{
    
// convert filename to wide-character string
    WCHAR w_filename[MAX_PATH] = {0};

    mbstowcs(w_filename, filename, MAX_PATH);

    
// render the file
    g_graph_builder->RenderFile(w_filename, NULL);

    
// play the file, switches the entire filter graph into a running state.
    g_media_control->Run();

    
return TRUE;
}
 


检测播放事件

我们主要感兴趣的IMediaEvent函数大概有三个:GetEvent,FreeEventParams,WaitForCompletion。

The IMediaEvent interface contains methods for retrieving event notifications and for overriding the Filter Graph Manager's default handling of events. The IMediaEventEx interface inherits this interface and extends it.

The Filter Graph Manager implements this interface. Applications can use it to respond to events that occur in the filter graph, such as the end of a stream or a rendering error. Filters post events to the filter graph using the IMediaEventSink interface.

For more information about event notification, see Event Notification in DirectShow. For a list of system-defined event notifications, see Event Notification Codes.

In addition to the methods inherited from IDispatch, the IMediaEvent interface exposes the following methods.

Method Description
CancelDefaultHandling Cancels the Filter Graph Manager's default handling for a specified event.
FreeEventParams Frees resources associated with the parameters of an event.
GetEvent Retrieves the next event notification from the event queue.
GetEventHandle Retrieves a handle to a manual-reset event that remains signaled while the queue contains event notifications.
RestoreDefaultHandling Restores the Filter Graph Manager's default handling for a specified event.
WaitForCompletion Waits for the filter graph to render all available data.


第一个函数GetEvent可能是使用最多的函数,它用于找回通知播放状态的事件。

Retrieves the next notification event.

Syntax

HRESULT GetEvent(
long *lEventCode,
long *lParam1,
long *lParam2,
long msTimeout
);

Parameters

IEventCode
[out] Pointer to the next event notification.
lParam1
[out] Pointer to the first parameter of the event.
lParam2
[out] Pointer to the second parameter of the event.
msTimeout
[in] Time, in milliseconds, to wait before assuming that there are no events.

Return Value

Returns an HRESULT value that depends on the implementation of the interface. If the time-out is zero and no event is waiting, or if the time-out elapses before an event appears, this method returns E_ABORT.

Remarks

The application can pass a time-out value of INFINITE to indicate that the method should block until there is an event; however, applications should avoid using INFINITE. Threads cannot process any messages while waiting in GetEvent. If you call GetEvent from the thread that processes Windows messages, specify only small wait times on the call in order to remain responsive to user input. This is most important when streaming data from a source such as the Internet, because state transitions can take significantly more time to complete.

After calling GetEvent, applications should always call FreeEventParams to release any resource associated with the event.

For a list of notification codes and event parameter values, see Event Notification Codes.

一般我们最希望获取到的事件是播放完成,这个事件的值是EC_COMPLETE。如果调用GetEvent函数之后,lEventCode的值是 EC_COMPLETE,说明播放完成了。

当获取并处理了GetEvent产生的事件后,必须把事件所占用的资源释放,释放资源使用IMediaEvent::FreeEventParams函数。

Frees resources associated with the parameters of an event.

Syntax

HRESULT FreeEventParams(
long lEventCode,
long lParam1,
long lParam2
);

Parameters

lEventCode
[in] Next event notification.
lParam1
[in] First parameter of the event.
lParam2
[in] Second parameter of the event.

Return Value

Returns an HRESULT value.

Remarks

Event parameters can be of type LONG or BSTR. If a BSTR is passed as an event, it will have been allocated by the task allocator and should be freed using this method. No reference-counted interfaces are passed to an application using IMediaEvent::GetEvent, because these cannot be overridden by IMediaEvent::CancelDefaultHandling. Therefore, do not use this method to release interfaces.

以下代码演示了如何捕捉事件并释放事件所占用的资源:
 
        // get th status of the song, it if is done, exit program.

        
long event_code, param1, param2;

        
// retrieves the next notification event
        if(SUCCEEDED(g_media_event->GetEvent(&event_code, &param1, &param2, 1)))
        {
            
if(event_code == EC_COMPLETE)
            {
                
// frees resources associated with the parameters of an events.
                g_media_event->FreeEventParams(event_code, param1, param2);

                
break;
            }                
        }

在需要逐帧连续监视事件的时候,用GetEvent和FreeEventParams组合是很有用的,它能帮助我们获取事件并作恰当的处理。但当我们需要连续播放而不希望连续监视播放过程的时候,另外一个函数就会很有用,即WaitForCompletion函数。它可以一直播放,在播放完成的时候,会把控制权交给我们。

Blocks execution of the application thread until the graph's operation finishes.

Syntax

HRESULT WaitForCompletion(
long msTimeout,
long *pEvCode
);

Parameters

msTimeout
[in] Duration of the time-out, in milliseconds. Pass zero to return immediately. To block indefinitely, pass INFINITE.
pEvCode
[out] Pointer to the event that terminated the wait. This value can be one of the following:
EC_COMPLETE Operation completed.
EC_ERRORABORT Error. Playback can't continue.
EC_USERABORT User terminated the operation.
Zero Operation has not completed.

Return Value

Returns one of the following HRESULT values.

E_ABORT Function timed out before the operation completed. This is equivalent to a zero pEvCode value.
S_OK Operation completed.
 

Remarks

This method is the equivalent of blocking until the event notification EC_COMPLETE, EC_ERRORABORT, or EC_USERABORT is received, or the time-out occurs.

When this method returns, the filter graph is still running. This method assumes that separate calls to the IMediaEvent interface are not being made. The method fails if the graph is not in, or transitioning into, a running state.

The time-out parameter (msTimeout) specifies the length of time to wait for completion. To test whether the operation completed, specify a zero msTimeout value and check the event code value (pEvCode) for zero, indicating that the operation has not completed.

 

释放DirectShow资源

一旦播放完成,就要关闭和释放所有占用的DirectShow资源。
 
    // stop music and relaese DirectShow objects

    // switches all filters in the filter graph to a stopped state.
    g_media_control->Stop();

    g_media_event->Release();
    g_media_event = NULL;

    g_media_control->Release();
    g_media_control = NULL;

    g_graph_builder->Release();
    g_graph_builder = NULL;

下面给出完整的代码示例,由于DirectX9已经将DirectShow移除,所以需要安装DirectX8 SDK,并在Vistual Studio中设置头文件目录。

点击下载源码和工程

 
/***************************************************************************************
PURPOSE:
    MP3 Playing Demo
 ***************************************************************************************/


#include <windows.h>
#include <stdio.h>
#include <dshow.h>
#include "resource.h"

#pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "strmiids.lib")

#pragma warning(disable : 4996)

#define Safe_Release(p) if((p)) (p)->Release();

// window handles, class.
HWND g_hwnd;
char g_class_name[] = "MP3PlayClass";

// DirectShows components
IGraphBuilder*  g_graph_builder = NULL;
IMediaControl*  g_media_control = NULL;
IMediaEvent*    g_media_event   = NULL;

//--------------------------------------------------------------------------------
// Play mp3 which specified by filename.
//--------------------------------------------------------------------------------
BOOL Play_MP3(char* filename)
{
    
// convert filename to wide-character string
    WCHAR w_filename[MAX_PATH] = {0};

    mbstowcs(w_filename, filename, MAX_PATH);

    
// render the file
    g_graph_builder->RenderFile(w_filename, NULL);

    
// play the file, switches the entire filter graph into a running state.
    g_media_control->Run();

    
return TRUE;
}

//--------------------------------------------------------------------------------
// Window procedure.
//--------------------------------------------------------------------------------
long WINAPI Window_Proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    
switch(msg)
    {
    
case WM_DESTROY:
        PostQuitMessage(0);
        
return 0;
    }

    
return (long) DefWindowProc(hwnd, msg, wParam, lParam);
}

//--------------------------------------------------------------------------------
// Main function, routine entry.
//--------------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE inst, HINSTANCE, LPSTR cmd_line, int cmd_show)
{
    WNDCLASS            win_class;
    MSG                 msg;    

    
// create window class and register it
    win_class.style         = CS_HREDRAW | CS_VREDRAW;
    win_class.lpfnWndProc   = Window_Proc;
    win_class.cbClsExtra    = 0;
    win_class.cbWndExtra    = DLGWINDOWEXTRA;
    win_class.hInstance     = inst;
    win_class.hIcon         = LoadIcon(inst, IDI_APPLICATION);
    win_class.hCursor       = LoadCursor(NULL, IDC_ARROW);
    win_class.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
    win_class.lpszMenuName  = NULL;
    win_class.lpszClassName = g_class_name;    

    
if(! RegisterClass(&win_class))
        
return FALSE;

    
// create the main window
    g_hwnd = CreateDialog(inst, MAKEINTRESOURCE(IDD_MP3PLAY), 0, NULL);

    ShowWindow(g_hwnd, cmd_show);
    UpdateWindow(g_hwnd);

    
// initialize COM
    //
    // initialize the COM library on the current thread and identifies the concurrency model as single-thread
    // apartment (STA).
    CoInitialize(0);

    
// create the DirectMusic performance object
    //
    // creates a single uninitialized object of the class associated with a specified CLSID.
    if(FAILED(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, 
                               (
void**)&g_graph_builder)))
    {
        MessageBox(NULL, "Unable to create DirectShow Graph Builder object.", "Error", MB_OK);
        
return FALSE;
    }

    
// Query for the media control and event objects
    g_graph_builder->QueryInterface(IID_IMediaControl, (void**)&g_media_control);
    g_graph_builder->QueryInterface(IID_IMediaEvent, (
void**)&g_media_event);
    
    
// play mp3
    Play_MP3("escape.mp3");
    
    
// start message pump, waiting for signal to quit.
    ZeroMemory(&msg, sizeof(MSG));

    
while(msg.message != WM_QUIT)
    {
        
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);            
        }             

        
// get th status of the song, it if is done, exit program.

        
long event_code, param1, param2;

        
// retrieves the next notification event
        if(SUCCEEDED(g_media_event->GetEvent(&event_code, &param1, &param2, 1)))
        {
            
if(event_code == EC_COMPLETE)
            {
                
// frees resources associated with the parameters of an events.
                g_media_event->FreeEventParams(event_code, param1, param2);

                
break;
            }                
        }

        
// frees resources associated with the parameters of an events.
        g_media_event->FreeEventParams(event_code, param1, param2);
    }

    
// stop music and relaese DirectShow objects

    // switches all filters in the filter graph to a stopped state.
    g_media_control->Stop();

    g_media_event->Release();
    g_media_event = NULL;

    g_media_control->Release();
    g_media_control = NULL;

    g_graph_builder->Release();
    g_graph_builder = NULL;

    UnregisterClass(g_class_name, inst);

    
// release COM system
    //
    // Closes the COM library on the current thread, unloads all DLLs loaded by the thread, frees any other
    // resources that the thread maintains, and forces all RPC connections on the thread to close.
    CoUninitialize();
    
    
return (int) msg.wParam;
}
 

运行截图:



posted on 2007-08-04 18:18 lovedday 阅读(8026) 评论(20)  编辑 收藏 引用

评论

# re: 用DirectX Audio和DirectShow播放声音和音乐(7) 2007-08-13 22:32 artcpp

非常感谢您的一系列文件,对我自学编程(尤其是Direct方面)起到了很大的引导作用!!谢谢您!  回复  更多评论   

# re: 用DirectX Audio和DirectShow播放声音和音乐(7) 2007-08-13 22:33 artcpp

不知能否QQ联系:请教一个DirectShow播放MP3,在个别机器上失败的奇怪问题。我的QQ:1090833,盼,多谢!!  回复  更多评论   

# re: 用DirectX Audio和DirectShow播放声音和音乐(7) 2007-08-13 22:52 lovedday

@artcpp
不用客气,用“你”就好了,用“您”我担当不起,很高兴我的文章能帮到你,这也是我继续写技术文章的动力,提高自己的同时还能帮到别人。其实我的水平也很菜的,如果可以帮到你,一定尽力。^_^  回复  更多评论   

# re: 用DirectX Audio和DirectShow播放声音和音乐(7) 2007-10-04 18:09 bluesea

不知道为什么到 directX 9.0 里,要把 DirectShow 去掉了呢?  回复  更多评论   

# re: 用DirectX Audio和DirectShow播放声音和音乐(7) 2007-10-07 13:29 lovedday

据说是转移到win32 sdk去了,可能是DirectShow功能越来越通用,越来越重要的原因。  回复  更多评论   

# re: 用DirectX Audio和DirectShow播放声音和音乐(7)[未登录] 2008-01-02 15:42 max

您好~~ 很感謝你的文章, 對我幫助很大, 不過, 我有個問題, 關於只播音訊時, 為什麼不論我用你的方式, 或msdn上的教學, 我的directshow (on mobile 6) 都只能播放出wav檔, 可以給我解感一下嗎?? 需要另外加filter或什麼類的嗎, 不是graphfilter就自動建立基本的filter嗎, 我弄好久了, 我需要能播放midi, mp3, wma.....  回复  更多评论   

# re: 用DirectX Audio和DirectShow播放声音和音乐(7) 2008-04-06 13:46 wind

很感谢你这一系列啊,我正在做基于DIRECTX的MP3播放系统设计的毕业设计,还有没有这方面比较好用的资料啊,最好注释比较浅显详尽的,感激不尽啊~~~我的邮箱是coolwind2006@126.com  回复  更多评论   

# re: 用DirectX Audio和DirectShow播放声音和音乐(7) 2008-04-06 14:03 lovedday

没了,去买书看看吧,这些东西我也是书上看到的。  回复  更多评论   

# re: 用DirectX Audio和DirectShow播放声音和音乐(7) 2008-04-21 18:21 淼焱

我有一些问题想请教一下 我的QQ 282338785 谢谢!!  回复  更多评论   

# re: 用DirectX Audio和DirectShow播放声音和音乐(7) 2008-04-21 18:26 lovedday

隔了这么久了,DirectShow的一些知识我可能都忘了,这样吧,你给我写信,我的email是lovedday@gmail.com,看我能不能帮上忙,别说什么请教,我也没什么经验的。  回复  更多评论   

# re: 用DirectX Audio和DirectShow播放声音和音乐(7) 2008-08-18 04:54 lyra

你的东西太好了 谢谢  回复  更多评论   

# re: 用DirectX Audio和DirectShow播放声音和音乐(7) 2008-10-17 22:12 来来往往

你的资料太好了,谢谢共享  回复  更多评论   

# re: 用DirectX Audio和DirectShow播放声音和音乐(7) 2008-11-10 01:04 wlxfm

怎样做一个模拟键盘的软件,用VC++?基于DirectX,就象iDreamPiano一样。  回复  更多评论   

# re: 用DirectX Audio和DirectShow播放声音和音乐(7) 2009-05-06 20:05 李平

真的很感谢兄弟啊,这么珍贵的资料,看来自学DirectSound有希望了!!!万分感激啊!!!  回复  更多评论   

# re: 用DirectX Audio和DirectShow播放声音和音乐(7) 2009-05-19 00:46 果蝇

太感谢了  回复  更多评论   

# re: 用DirectX Audio和DirectShow播放声音和音乐(7)[未登录] 2009-05-19 13:49 李帅

怎么让这个音乐循环播放?  回复  更多评论   

# re: 用DirectX Audio和DirectShow播放声音和音乐(7) 2009-09-01 04:54 lyra

非常感谢,看你的资料有两年了,谢谢您。祝您家庭幸福,身体健康,事业成功。  回复  更多评论   

# re: 用DirectX Audio和DirectShow播放声音和音乐(7) 2010-01-17 10:09 yinian

非常感谢您这么详细的介绍,实际上也是一个非常好的教程了。我现在有个疑惑的是,微软为什么要在DirectX9里面把DirectShow删除呢,是不是DShow本身存在技术方面的原因,另外,既然新的DirectX中已经没有DShow了,是不是意味着windows以后的版本对DShow的支持也不会更新了,那么现在使用DShow是不是会以后会碰到一些难以解决的问题呢?  回复  更多评论   

# re: 用DirectX Audio和DirectShow播放声音和音乐(7) 2013-03-15 17:40

谢谢你的文章,我还有一些具体的问题想问一下,希望能加一下qq:1054359972  回复  更多评论   

# re: 用DirectX Audio和DirectShow播放声音和音乐(7) 2014-09-27 17:52 CMZ

其实我想问下。通过这些demo可以播放音乐。但是怎么实现循环播放呢?  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


公告

导航

统计

常用链接

随笔分类(178)

3D游戏编程相关链接

搜索

最新评论