青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

天行健 君子當自強而不息

用DirectX Audio和DirectShow播放聲音和音樂(4)


本篇是 用DirectX Audio和DirectShow播放聲音和音樂(3)的續篇。

使用通告

“通告”是一種觸發機制,當緩存中播放位置達到某個固定的位置時,就會向程序發出通知。有了通告,就可以知道播放什么時候結束,這種機制在比較長的聲音中特別有效。通告使用一個叫做 IDirectSoundNotify8的對象,這個程序的作用就是在音頻緩存中標記一個位置,然后觸發事件通知應用程序,而應用程序可以通過消息循環或者單獨的線程進行處理。

標記的位置可以是一個緩存中的偏移值,也可以是由宏指定的停止標記,這個表示停止的宏是 DSBPN_OFFSETSTOP。并不是任何偏移值都可以用來作為通告發生的位置,這個值必須和音頻的數據塊對齊,并且通告的偏移必須按照從小到大的順序排列。偏移值是不能夠共享的,如果使用 DSBPN_OFFSETSTOP宏,它必須被放在最后。舉例來說,對于一個塊大小為2的音頻(單聲道、16位),嘗試對偏移為4和5的位置設通告會導致失敗,因為偏移量位置4和5都在同一個數據塊中。

如果要在緩存中使用通告,必須在創建緩存的時候使用 DSBCAPS_CTRLPOSITIONNOTIFY標志,并且如果在創建緩存的過程中使用了這個標志,就必須使用通告對象。如果希望獲取 IDirectSoundNotify8對象,可以在IDirectSoundBuffer8對象中通過請求接口來獲得。
 
 IDirectSound8*          g_ds;           // directsound component

    // create main sound buffer
    if(FAILED(g_ds->CreateSoundBuffer(&ds_buffer_desc, &ds_buffer_primary, NULL)))
        
return NULL;

    
// get newer interface
    if(FAILED(ds_buffer_primary->QueryInterface(IID_IDirectSoundBuffer8, (void**)&ds_buffer_second)))
    {
        ds_buffer_primary->Release();
        
return NULL;
    }

如下圖所示,通告可以放置在緩存中的任何位置:

通告對象只有一個成員函數SetNotificationPositions。

The SetNotificationPositions method sets the notification positions. During capture or playback, whenever the read or play cursor reaches one of the specified offsets, the associated event is signaled.

HRESULT SetNotificationPositions(
DWORD dwPositionNotifies,
LPCDSBPOSITIONNOTIFY pcPositionNotifies
);

Parameters

dwPositionNotifies
Number of DSBPOSITIONNOTIFY structures.
pcPositionNotifies
Pointer to an array of DSBPOSITIONNOTIFY structures (the maximum array size is DSBNOTIFICATIONS_MAX).

Return Values

If the method succeeds, the return value is DS_OK. If the method fails, the return value may be one of the following error values:

Return code
DSERR_INVALIDPARAM
DSERR_OUTOFMEMORY

Remarks

The value DSBPN_OFFSETSTOP can be specified in the dwOffset member to tell DirectSound to signal the associated event when the Stop or Stop method is called or when the end of the buffer has been reached and the playback is not looping. If it is used, this should be the last item in the position-notify array.

If a position-notify array has already been set, the method replaces the previous array.

The buffer must be stopped when this method is called.


pcPositionNotifies是一個DSBPOSITIONNOTIFY結構的數組。

The DSBPOSITIONNOTIFY structure describes a notification position. It is used by IDirectSoundNotify8::SetNotificationPositions.

typedef struct DSBPOSITIONNOTIFY {
DWORD dwOffset;
HANDLE hEventNotify;
} DSBPOSITIONNOTIFY;

Members

dwOffset
Offset from the beginning of the buffer where the notify event is to be triggered, or DSBPN_OFFSETSTOP.
hEventNotify
Handle to the event to be signaled when the offset has been reached.

Remarks

The DSBPN_OFFSETSTOP value in the dwOffset member causes the event to be signaled when playback or capture stops, either because the end of the buffer has been reached (and playback or capture is not looping) or because the application called the IDirectSoundBuffer8::Stop or IDirectSoundCaptureBuffer8::Stop method.

>When a playback buffer was created with DSBCAPS_LOCDEFER and DSBCAPS_CTRLPOSITIONNOTIFY along with any voice management flag, it is possible that a sound that has notifications set, but not yet reached, will be terminated by the voice manager. In this case, no event is signaled.


在使用通告的時候,一般需要使用事件句柄,事件一般有兩種狀態存在,已發出信號和未發出信號,可以通過CreateEvent函數來創建事件。
 
The CreateEvent function creates or opens a named or unnamed event object.
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes,
BOOL bManualReset,
BOOL bInitialState,
LPCTSTR lpName
);

Parameters

lpEventAttributes
[in] Pointer to a SECURITY_ATTRIBUTES structure that determines whether the returned handle can be inherited by child processes. If lpEventAttributes is NULL, the handle cannot be inherited.

The lpSecurityDescriptor member of the structure specifies a security descriptor for the new event. If lpEventAttributes is NULL, the event gets a default security descriptor. The ACLs in the default security descriptor for an event come from the primary or impersonation token of the creator.

bManualReset
[in] If this parameter is TRUE, the function creates a manual-reset event object, which requires the use of the ResetEvent function to set the event state to nonsignaled. If this parameter is FALSE, the function creates an auto-reset event object, and system automatically resets the event state to nonsignaled after a single waiting thread has been released.
bInitialState
[in] If this parameter is TRUE, the initial state of the event object is signaled; otherwise, it is nonsignaled.
lpName
[in] Pointer to a null-terminated string specifying the name of the event object. The name is limited to MAX_PATH characters. Name comparison is case sensitive.

If lpName matches the name of an existing named event object, this function requests the EVENT_ALL_ACCESS access right. In this case, the bManualReset and bInitialState parameters are ignored because they have already been set by the creating process. If the lpEventAttributes parameter is not NULL, it determines whether the handle can be inherited, but its security-descriptor member is ignored.

If lpName is NULL, the event object is created without a name.

If lpName matches the name of an existing semaphore, mutex, waitable timer, job, or file-mapping object, the function fails and the GetLastError function returns ERROR_INVALID_HANDLE. This occurs because these objects share the same name space.

Terminal Services:   The name can have a "Global\" or "Local\" prefix to explicitly create the object in the global or session name space. The remainder of the name can contain any character except the backslash character (\). For more information, see Kernel Object Namespaces.
Windows XP:   Fast user switching is implemented using Terminal Services sessions. The first user to log on uses session 0, the next user to log on uses session 1, and so on. Kernel object names must follow the guidelines outlined for Terminal Services so that applications can support multiple users.
Windows 2000:   If Terminal Services is not running, the "Global\" and "Local\" prefixes are ignored. The remainder of the name can contain any character except the backslash character.
Windows NT:  The name can contain any character except the backslash character.
Windows Me/98/95:   The name can contain any character except the backslash character. The empty string ("") is a valid object name.

Return Values

If the function succeeds, the return value is a handle to the event object. If the named event object existed before the function call, the function returns a handle to the existing object and GetLastError returns ERROR_ALREADY_EXISTS.

If the function fails, the return value is NULL. To get extended error information, call GetLastError.

Remarks

The handle returned by CreateEvent has the EVENT_ALL_ACCESS access right; it can be used in any function that requires a handle to an event object, provided that the caller has been granted access. If an event is created from a service or a thread that is impersonating a different user, you can either apply a security descriptor to the event when you create it, or change the default security descriptor for the creating process by changing its default DACL. For more information, see Synchronization Object Security and Access Rights.

Any thread of the calling process can specify the event-object handle in a call to one of the wait functions. The single-object wait functions return when the state of the specified object is signaled. The multiple-object wait functions can be instructed to return either when any one or when all of the specified objects are signaled. When a wait function returns, the waiting thread is released to continue its execution.

The initial state of the event object is specified by the bInitialState parameter. Use the SetEvent function to set the state of an event object to signaled. Use the ResetEvent function to reset the state of an event object to nonsignaled.

When the state of a manual-reset event object is signaled, it remains signaled until it is explicitly reset to nonsignaled by the ResetEvent function. Any number of waiting threads, or threads that subsequently begin wait operations for the specified event object, can be released while the object's state is signaled.

When the state of an auto-reset event object is signaled, it remains signaled until a single waiting thread is released; the system then automatically resets the state to nonsignaled. If no threads are waiting, the event object's state remains signaled.

Multiple processes can have handles of the same event object, enabling use of the object for interprocess synchronization. The following object-sharing mechanisms are available:

     
  • A child process created by the CreateProcess function can inherit a handle to an event object if the lpEventAttributes parameter of CreateEvent enabled inheritance.
  • A process can specify the event-object handle in a call to the DuplicateHandle function to create a duplicate handle that can be used by another process.
  • A process can specify the name of an event object in a call to the OpenEvent or CreateEvent function.

Use the CloseHandle function to close the handle. The system closes the handle automatically when the process terminates. The event object is destroyed when its last handle has been closed.


以下代碼創建了4個事件,并且給pos_notify設置了4個事件觸發的位置標記。
 
HANDLE  g_events[4];        // notification handles
DSBPOSITIONNOTIFY   pos_notify[4];

    // create the event handles and set the notifications
    for(long i = 0; i < 4; i++)
    {
        
// create an unnamed event object
        g_events[i] = CreateEvent(NULL, FALSE, FALSE, NULL);

        pos_notify[i].dwOffset     = 16384 * (i+1) - 1;
        pos_notify[i].hEventNotify = g_events[i];
    }

    
// set the notification position
    //
    // During capture or playback, whenever the read or play cursor reached one of the specified offsets,
    // the associated event is signaled.
    g_ds_notify->SetNotificationPositions(4, pos_notify);

在緩存已經準備就緒之后,就可以開始播放了,播放時等待通告事件被觸發,它是WaitForMultipleObjects函數的工作。
 
The WaitForMultipleObjects function returns when any one or all of the specified objects are in the signaled state or the time-out interval elapses.

To enter an alertable wait state, use the WaitForMultipleObjectsEx function.

DWORD WaitForMultipleObjects(
DWORD nCount,
const HANDLE* lpHandles,
BOOL bWaitAll,
DWORD dwMilliseconds
);

Parameters

nCount
[in] Number of object handles in the array pointed to by lpHandles. The maximum number of object handles is MAXIMUM_WAIT_OBJECTS.
lpHandles
[in] Pointer to an array of object handles. For a list of the object types whose handles can be specified, see the following Remarks section. The array can contain handles to objects of different types. It may not contain the multiple copies of the same handle.

If one of these handles is closed while the wait is still pending, the function's behavior is undefined.

The handles must have the SYNCHRONIZE access right. For more information, see Standard Access Rights.

Windows Me/98/95:  No handle may be a duplicate of another handle created using DuplicateHandle.
bWaitAll
[in] If this parameter is TRUE, the function returns when the state of all objects in the lpHandles array is signaled. If FALSE, the function returns when the state of any one of the objects is set to signaled. In the latter case, the return value indicates the object whose state caused the function to return.
dwMilliseconds
[in] Time-out interval, in milliseconds. The function returns if the interval elapses, even if the conditions specified by the bWaitAll parameter are not met. If dwMilliseconds is zero, the function tests the states of the specified objects and returns immediately. If dwMilliseconds is INFINITE, the function's time-out interval never elapses.

Return Values

If the function succeeds, the return value indicates the event that caused the function to return. It can be one of the following values. (Note that WAIT_OBJECT_0 is defined as 0 and WAIT_ABANDONED_0 is defined as 0x00000080L.)

Return code/value Description

WAIT_OBJECT_0 to (WAIT_OBJECT_0 + nCount– 1)
If bWaitAll is TRUE, the return value indicates that the state of all specified objects is signaled.

If bWaitAll is FALSE, the return value minus WAIT_OBJECT_0 indicates the lpHandles array index of the object that satisfied the wait. If more than one object became signaled during the call, this is the array index of the signaled object with the smallest index value of all the signaled objects.


WAIT_ABANDONED_0 to (WAIT_ABANDONED_0 + nCount– 1)
If bWaitAll is TRUE, the return value indicates that the state of all specified objects is signaled and at least one of the objects is an abandoned mutex object.

If bWaitAll is FALSE, the return value minus WAIT_ABANDONED_0 indicates the lpHandles array index of an abandoned mutex object that satisfied the wait.

WAIT_TIMEOUT
0x00000102L
The time-out interval elapsed and the conditions specified by the bWaitAll parameter are not satisfied.

If the function fails, the return value is WAIT_FAILED. To get extended error information, call GetLastError.


WaitForMultipleObjects函數可以同時等待64個對象,請確定自己的對象數目小于這個最大值。在沒有接收到任何事件的情況下,函數可能會返回WAIT_FAILED,遇到這種情況,只需要再調用一次這個函數,就能恢復正常。取得 WaitForMultipleObjects函數的返回值就能夠獲取事件號。獲取到事件號后,用這個數字減去 WAIT_OBJECT_0,得到的結果就是觸發的事件索引號,這個值的范圍從0到事件總數減1。

以下代碼示例了如何使用WaitForMultipleObjects,以及如何得到事件索引號。
 
HANDLE  g_events[4];        // notification handles

// wait for a message
//
// return when any one or all of the specified objects are in the signaled state or the time-out 
 // interval elapses.
DWORD result = MsgWaitForMultipleObjects(4, g_events, FALSE, INFINITE, QS_ALLEVENTS);

// get notification #
DWORD thread_index = result - WAIT_OBJECT_0;


使用線程控制事件


創建線程可以通過調用CreateThread函數來實現。
 
The CreateThread function creates a thread to execute within the virtual address space of the calling process.

To create a thread that runs in the virtual address space of another process, use the CreateRemoteThread function.

HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);

Parameters

lpThreadAttributes
[in] Pointer to a SECURITY_ATTRIBUTES structure that determines whether the returned handle can be inherited by child processes. If lpThreadAttributes is NULL, the handle cannot be inherited.

The lpSecurityDescriptor member of the structure specifies a security descriptor for the new thread. If lpThreadAttributes is NULL, the thread gets a default security descriptor. The ACLs in the default security descriptor for a thread come from the primary token of the creator.

Windows XP/2000/NT:  The ACLs in the default security descriptor for a thread come from the primary or impersonation token of the creator. This behavior changed with Windows XP SP2 and Windows Server 2003.

 

dwStackSize
[in] Initial size of the stack, in bytes. The system rounds this value to the nearest page. If this parameter is zero, the new thread uses the default size for the executable. For more information, see Thread Stack Size.
lpStartAddress
[in] Pointer to the application-defined function to be executed by the thread and represents the starting address of the thread. For more information on the thread function, see ThreadProc.
lpParameter
[in] Pointer to a variable to be passed to the thread.
dwCreationFlags
[in] Flags that control the creation of the thread. If the CREATE_SUSPENDED flag is specified, the thread is created in a suspended state, and will not run until the ResumeThread function is called. If this value is zero, the thread runs immediately after creation.

If the STACK_SIZE_PARAM_IS_A_RESERVATION flag is specified, the dwStackSize parameter specifies the initial reserve size of the stack. Otherwise, dwStackSize specifies the commit size.

Windows 2000/NT and Windows Me/98/95:  The STACK_SIZE_PARAM_IS_A_RESERVATION flag is not supported.
lpThreadId
[out] Pointer to a variable that receives the thread identifier. If this parameter is NULL, the thread identifier is not returned.
Windows Me/98/95:  This parameter may not be NULL.

Return Values

If the function succeeds, the return value is a handle to the new thread.

If the function fails, the return value is NULL. To get extended error information, call GetLastError.

Note that CreateThread may succeed even if lpStartAddress points to data, code, or is not accessible. If the start address is invalid when the thread runs, an exception occurs, and the thread terminates. Thread termination due to a invalid start address is handled as an error exit for the thread's process. This behavior is similar to the asynchronous nature of CreateProcess, where the process is created even if it refers to invalid or missing dynamic-link libraries (DLLs).

Windows Me/98/95:  CreateThread succeeds only when it is called in the context of a 32-bit program. A 32-bit DLL cannot create an additional thread when that DLL is being called by a 16-bit program.

lpStartAddress是一個函數指針,該函數指針的定義如下:
 
The ThreadProc function is an application-defined function that serves as the starting address for a thread. Specify this address when calling the CreateThread or CreateRemoteThread function. The LPTHREAD_START_ROUTINE type defines a pointer to this callback function. ThreadProc is a placeholder for the application-defined function name.
DWORD WINAPI ThreadProc(
LPVOID lpParameter
);

Parameters

lpParameter
[in] Thread data passed to the function using the lpParameter parameter of the CreateThread or CreateRemoteThread function.

Return Values

The function should return a value that indicates its success or failure.

Remarks

A process can determine when a thread it created has completed by using one of the wait functions. It can also obtain the return value of its ThreadProc by calling the GetExitCodeThread function.

Each thread receives a unique copy of the local variables of this function. Any static or global variables are shared by all threads in the process. To provide unique data to each thread using a global index, use thread local storage.


以下代碼演示了如何使用函數CreateThread。
 
HANDLE  g_thread_handle;    // thread handle
DWORD   g_thread_id;        // thread id

// create a thread for notifications
g_thread_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) Handle_Notifications, NULL, 0, &g_thread_id);
......

DWORD Handle_Notifications(LPVOID thread_data)
{
   
// thread function to handle notification event 
   ......
}

 

音頻流

音頻流是一種很簡單很容易理解的處理方式,就是循環播放一個緩沖區的音頻數據,在播放的時候不斷更新這個緩沖區的數據,使得播放能無縫播放。在播放音頻緩沖區的時候設置一系列的標志位,然后需要一個指示器告訴應該在剛剛播放掉的部分填充一些新數據。在每次緩沖區播放完畢后,只需要從頭開始繼續播放,就能將音頻連續地播放下去。

下面的這段音頻緩沖區有4個標志位,當回放到某個標志位時就表示應該填充新數據到剛剛播放的部分中。



使用音頻流的第一步是用數據填充整個緩沖區,然后開始播放聲音,直到播放到達第一個標志位的時候,再讀取一段新的數據,取代剛剛播放完成的數據。然后繼續播放到達第二個標志位,再讀取新數據取代剛剛播放完成的部分。持續這個過程,直到整段音頻數據播放完成,最后一個播放到的標志觸發一個停止播放的事件。如果需要循環播放音頻,只需不觸發停止事件,重新開始剛才的過程。在處理通告事件的線程中,使用加載數據的函數不斷更新音頻緩沖區的數據,保證緩沖區中擁有完成的音頻信息。

下面是整個操作的全部過程:

1、創建音頻緩沖區,65536字節大小。
2、設置4個通告位置,每個通告標志都在緩沖區的1/4處。
3、用數據填充整個緩沖區。
4、使用DSBPLAY_LOOP標志播放音頻。
5、在每個通告被觸發的時候,用新數據填充剛剛播放掉的部分,持續這個過程,直到有通告顯示緩沖區中已經沒有更多的數據可以放入緩沖區中,最后再播放剩余部分即可。
6、最后判斷哪個事件表示已經到達了音頻數據末端。

以下是代碼示例:
 

IDirectSound8*          g_ds;           // directsound component
IDirectSoundBuffer8*    g_ds_buffer;    // sound buffer object
IDirectSoundNotify8*    g_ds_notify;    // notification object

HANDLE  g_thread_handle;    
// thread handle
DWORD   g_thread_id;        // thread id
HANDLE  g_events[4];        // notification handles
FILE*   g_fp;               // .WAV file handle
long    g_ds_size;          // directsound data buffer size
long    g_ds_pos;           // current directsound buffer position
long    g_ds_leave;         // leave directsound buffer data needed to be played

//--------------------------------------------------------------------------------
// Handle directsound nofification evenet, just stream load sound buffer.
//--------------------------------------------------------------------------------
DWORD Handle_Notifications(LPVOID thread_data)
{
    
while(1)
    {
        
// wait for a message
        //
        // return when any one or all of the specified objects are in the signaled state or the time-out 
        // interval elapses.
        DWORD result = MsgWaitForMultipleObjects(4, g_events, FALSE, INFINITE, QS_ALLEVENTS);

        
// get notification #
        DWORD thread_index = result - WAIT_OBJECT_0;

        
// check for update #
        if(thread_index >= 0 && thread_index < 4)
        {
            
// stop sound and quit thread if no more
            if(g_ds_leave == 0)
                ExitThread(0);

            
// seek to read position in file
            fseek(g_fp, g_ds_pos, SEEK_SET);

            
// stream in data based on amount of data left
            if(g_ds_leave <= 16384)
            {
                
// if this is reached, then end of sound is coming up.
                Load_Sound_Data(g_ds_buffer, thread_index * 16384, g_ds_leave, g_fp);

                g_ds_leave = 0;
            }
            
else
            {
                Load_Sound_Data(g_ds_buffer, thread_index * 16384, 65536, g_fp);

                
// reset directsound buffer leave and current position
                g_ds_leave -= 16384;
                g_ds_pos   += 16384;
            }
        }
    }

    
return 0;
}

//--------------------------------------------------------------------------------
// Play WAVE file sound which specified by filename.
//--------------------------------------------------------------------------------
void Play_Stream_Sound(char* filename)
{
    DSBPOSITIONNOTIFY   pos_notify[4];
    WAVE_HEADER         wave_header;

    
// open the source file
    if((g_fp = fopen(filename, "rb")) == NULL)
        
return;

    
// create a 2 second buffer to stream in wave
    if((g_ds_buffer = Create_Buffer_From_WAV(g_fp, &wave_header)) == NULL)
    {
        fclose(g_fp);
        
return;
    }

    
// get streaming size and pointer
    g_ds_size   = wave_header.data_size;
    g_ds_pos    = 
sizeof(WAVE_HEADER);
    g_ds_leave  = g_ds_size;

    
// create a thread for notifications
    g_thread_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) Handle_Notifications, NULL, 0, &g_thread_id);
    
    
// create failed
    if(g_thread_handle == NULL)    
        
return;
    
    
// create the notification interface
    if(FAILED(g_ds_buffer->QueryInterface(IID_IDirectSoundNotify8, (void**)&g_ds_notify)))
        
return;

    
// create the event handles and set the notifications
    for(long i = 0; i < 4; i++)
    {
        
// create an unnamed event object
        g_events[i] = CreateEvent(NULL, FALSE, FALSE, NULL);

        pos_notify[i].dwOffset     = 16384 * (i+1) - 1;
        pos_notify[i].hEventNotify = g_events[i];
    }

    
// set the notification position
    //
    // During capture or playback, whenever the read or play cursor reached one of the specified offsets,
    // the associated event is signaled.
    g_ds_notify->SetNotificationPositions(4, pos_notify);

    
// fill buffer completely with sound

    // advance file pointer to data buffer's head
    fseek(g_fp, sizeof(WAVE_HEADER), SEEK_SET);

    
// load sound buffer data with 65536 bytes
    Load_Sound_Data(g_ds_buffer, 0, 65536, g_fp);

    
// reset leave sound buffer and current sound buffer position
    g_ds_leave -= 65536;
    g_ds_pos   += 65536;

    
// play sound looping
    g_ds_buffer->SetCurrentPosition(0);
    g_ds_buffer->SetVolume(DSBVOLUME_MAX);
    g_ds_buffer->Play(0, 0, DSBPLAY_LOOPING);
}

下面用一個例子來演示如何使用剛才介紹的那些知識。

點擊下載源碼和工程

完整代碼示例:
 
/***************************************************************************************
PURPOSE:
    Streaming Playback Demo
 ***************************************************************************************/


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

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

#pragma warning(disable : 4996)

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

// .WAV file header
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'
    long    data_size;              // size of waveform data
};

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

IDirectSound8*          g_ds;           
// directsound component
IDirectSoundBuffer8*    g_ds_buffer;    // sound buffer object
IDirectSoundNotify8*    g_ds_notify;    // notification object

HANDLE  g_thread_handle;    
// thread handle
DWORD   g_thread_id;        // thread id
HANDLE  g_events[4];        // notification handles
FILE*   g_fp;               // .WAV file handle
long    g_ds_size;          // directsound data buffer size
long    g_ds_pos;           // current directsound buffer position
long    g_ds_leave;         // leave directsound buffer data needed to be played

//--------------------------------------------------------------------------------
// Create wave header information from wave file.
//--------------------------------------------------------------------------------
IDirectSoundBuffer8* Create_Buffer_From_WAV(FILE* fp, WAVE_HEADER* wave_header)
{
    IDirectSoundBuffer*     ds_buffer_primary;
    IDirectSoundBuffer8*    ds_buffer_second;    
    DSBUFFERDESC            ds_buffer_desc;
    WAVEFORMATEX            wave_format;

    
// read in the header from beginning of file
    fseek(fp, 0, SEEK_SET);
    fread(wave_header, 1, 
sizeof(WAVE_HEADER), fp);

    
// check the sig fields. returning if an error.
    if(memcmp(wave_header->riff_sig, "RIFF", 4) || memcmp(wave_header->wave_sig, "WAVE", 4) ||
       memcmp(wave_header->format_sig, "fmt ", 4) || memcmp(wave_header->data_sig, "data", 4))
    {
        
return NULL;
    }

    
// setup the playback format
    ZeroMemory(&wave_format, sizeof(WAVEFORMATEX));

    wave_format.wFormatTag      = WAVE_FORMAT_PCM;
    wave_format.nChannels       = wave_header->channels;
    wave_format.nSamplesPerSec  = wave_header->sample_rate;
    wave_format.wBitsPerSample  = wave_header->bits_per_sample;
    wave_format.nBlockAlign     = wave_format.wBitsPerSample / 8 * wave_format.nChannels;
    wave_format.nAvgBytesPerSec = wave_format.nSamplesPerSec * wave_format.nBlockAlign;

    
// create the sound buffer using the header data
    ZeroMemory(&ds_buffer_desc, sizeof(DSBUFFERDESC));

    ds_buffer_desc.dwSize        = 
sizeof(DSBUFFERDESC);
    ds_buffer_desc.dwFlags       = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_LOCSOFTWARE;
    ds_buffer_desc.dwBufferBytes = 65536;
    ds_buffer_desc.lpwfxFormat   = &wave_format;

    
// create main sound buffer
    if(FAILED(g_ds->CreateSoundBuffer(&ds_buffer_desc, &ds_buffer_primary, NULL)))
        
return NULL;

    
// get newer interface
    if(FAILED(ds_buffer_primary->QueryInterface(IID_IDirectSoundBuffer8, (void**)&ds_buffer_second)))
    {
        ds_buffer_primary->Release();
        
return NULL;
    }

    
// return the interface
    return ds_buffer_second;
}

//--------------------------------------------------------------------------------
// Load sound data from second directsound buffer.
//--------------------------------------------------------------------------------
BOOL Load_Sound_Data(IDirectSoundBuffer8* ds_buffer, long lock_pos, long lock_size, FILE* fp)
{
    BYTE* ptr1;
    BYTE* ptr2;
    DWORD size1, size2;

    
if(lock_size == 0)
        
return FALSE;

    
// lock the sound buffer at position specified
    if(FAILED(ds_buffer->Lock(lock_pos, lock_size, (void**)&ptr1, &size1, (void**)&ptr2, &size2, 0)))
        
return FALSE;

    
// read in the data
    fread(ptr1, 1, size1, fp);

    
if(ptr2 != NULL)
        fread(ptr2, 1, size2, fp);

    
// unlock it
    ds_buffer->Unlock(ptr1, size1, ptr2, size2);

    
return TRUE;
}

//--------------------------------------------------------------------------------
// Handle directsound nofification evenet, just stream load sound buffer.
//--------------------------------------------------------------------------------
DWORD Handle_Notifications(LPVOID thread_data)
{
    
while(1)
    {
        
// wait for a message
        //
        // return when any one or all of the specified objects are in the signaled state or the time-out 
        // interval elapses.
        DWORD result = MsgWaitForMultipleObjects(4, g_events, FALSE, INFINITE, QS_ALLEVENTS);

        
// get notification #
        DWORD thread_index = result - WAIT_OBJECT_0;

        
// check for update #
        if(thread_index >= 0 && thread_index < 4)
        {
            
// stop sound and quit thread if no more
            if(g_ds_leave == 0)
                ExitThread(0);

            
// seek to read position in file
            fseek(g_fp, g_ds_pos, SEEK_SET);

            
// stream in data based on amount of data left
            if(g_ds_leave <= 16384)
            {
                
// if this is reached, then end of sound is coming up.
                Load_Sound_Data(g_ds_buffer, thread_index * 16384, g_ds_leave, g_fp);

                g_ds_leave = 0;
            }
            
else
            {
                Load_Sound_Data(g_ds_buffer, thread_index * 16384, 65536, g_fp);

                
// reset directsound buffer leave and current position
                g_ds_leave -= 16384;
                g_ds_pos   += 16384;
            }
        }
    }

    
return 0;
}

//--------------------------------------------------------------------------------
// Play WAVE file sound which specified by filename.
//--------------------------------------------------------------------------------
void Play_Stream_Sound(char* filename)
{
    DSBPOSITIONNOTIFY   pos_notify[4];
    WAVE_HEADER         wave_header;

    
// open the source file
    if((g_fp = fopen(filename, "rb")) == NULL)
        
return;

    
// create a 2 second buffer to stream in wave
    if((g_ds_buffer = Create_Buffer_From_WAV(g_fp, &wave_header)) == NULL)
    {
        fclose(g_fp);
        
return;
    }

    
// get streaming size and pointer
    g_ds_size   = wave_header.data_size;
    g_ds_pos    = 
sizeof(WAVE_HEADER);
    g_ds_leave  = g_ds_size;

    
// create a thread for notifications
    g_thread_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) Handle_Notifications, NULL, 0, &g_thread_id);
    
    
// create failed
    if(g_thread_handle == NULL)    
        
return;
    
    
// create the notification interface
    if(FAILED(g_ds_buffer->QueryInterface(IID_IDirectSoundNotify8, (void**)&g_ds_notify)))
        
return;

    
// create the event handles and set the notifications
    for(long i = 0; i < 4; i++)
    {
        
// create an unnamed event object
        g_events[i] = CreateEvent(NULL, FALSE, FALSE, NULL);

        pos_notify[i].dwOffset     = 16384 * (i+1) - 1;
        pos_notify[i].hEventNotify = g_events[i];
    }

    
// set the notification position
    //
    // During capture or playback, whenever the read or play cursor reached one of the specified offsets,
    // the associated event is signaled.
    g_ds_notify->SetNotificationPositions(4, pos_notify);

    
// fill buffer completely with sound

    // advance file pointer to data buffer's head
    fseek(g_fp, sizeof(WAVE_HEADER), SEEK_SET);

    
// load sound buffer data with 65536 bytes
    Load_Sound_Data(g_ds_buffer, 0, 65536, g_fp);

    
// reset leave sound buffer and current sound buffer position
    g_ds_leave -= 65536;
    g_ds_pos   += 65536;

    
// play sound looping
    g_ds_buffer->SetCurrentPosition(0);
    g_ds_buffer->SetVolume(DSBVOLUME_MAX);
    g_ds_buffer->Play(0, 0, DSBPLAY_LOOPING);
}

//--------------------------------------------------------------------------------
// 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_STREAM), 0, NULL);

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

    
// initialize and configure directsound

    // creates and initializes an object that supports the IDirectSound8 interface
    if(FAILED(DirectSoundCreate8(NULL, &g_ds, NULL)))
    {
        MessageBox(NULL, "Unable to create DirectSound object", "Error", MB_OK);
        
return 0;
    }

    
// set the cooperative level of the application for this sound device
    g_ds->SetCooperativeLevel(g_hwnd, DSSCL_NORMAL);

    
// play a streaming sound
    Play_Stream_Sound("test.wav");

    
if(g_ds_buffer)
    {
        
// play sound looping
        g_ds_buffer->SetCurrentPosition(0);
        
// set volume
        g_ds_buffer->SetVolume(DSBVOLUME_MAX);
        
// play sound
        g_ds_buffer->Play(0, 0, DSBPLAY_LOOPING);
    }

    
// 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);
        }        
    }

    
// stop sound
    if(g_ds_buffer)
        g_ds_buffer->Stop();

    
// kill the thread
    if(g_thread_handle != NULL)
    {
        
// terminates thread
        TerminateThread(g_thread_handle, 0);

        
// closes an open object handle
        CloseHandle(g_thread_handle);
    }

    
// release the event handle
    for(int i = 0; i < 4; i++)
        CloseHandle(g_events[i]);

    
// close .WAV file
    if(g_fp)
        fclose(g_fp);

    
// release directsound objects
    Safe_Release(g_ds_buffer);
    Safe_Release(g_ds);

    UnregisterClass(g_class_name, inst);
    
    
return (int) msg.wParam;
}
 

運行截圖:



閱讀下篇:用DirectX Audio和DirectShow播放聲音和音樂(5)

posted on 2007-07-29 14:09 lovedday 閱讀(3921) 評論(0)  編輯 收藏 引用

公告

導航

統計

常用鏈接

隨筆分類(178)

3D游戲編程相關鏈接

搜索

最新評論

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美激情第五页| 一区二区三区视频在线播放| 久久xxxx| 噜噜噜在线观看免费视频日韩| 国产亚洲成精品久久| 欧美在线资源| 欧美一区深夜视频| 欧美激情综合色| 欧美在线一二三| 亚洲欧美国产77777| 午夜欧美精品久久久久久久| 欧美成人精品在线播放| 一区二区黄色| 国产主播精品在线| 欧美理论电影在线播放| 亚洲调教视频在线观看| 欧美成人午夜激情视频| 欧美一区日本一区韩国一区| 亚洲欧洲视频在线| 黄色成人av网| 国产农村妇女毛片精品久久莱园子 | 国产精品红桃| 欧美刺激性大交免费视频| 午夜激情综合网| 在线午夜精品自拍| 最新日韩在线视频| 国产精品成人播放| 国产精品私房写真福利视频| 欧美电影在线免费观看网站| 欧美美女bbbb| 国产欧美综合一区二区三区| 在线免费一区三区| 伊人久久av导航| 激情五月婷婷综合| 国内久久精品视频| 一区二区三区高清在线| 欧美一区三区二区在线观看| 亚洲夫妻自拍| 欧美成人四级电影| 亚洲综合色视频| 香蕉亚洲视频| 欧美另类99xxxxx| 国产综合亚洲精品一区二| 亚洲品质自拍| 麻豆国产精品va在线观看不卡| 99精品视频免费观看视频| 宅男噜噜噜66一区二区66| 久久精品亚洲一区二区三区浴池| 久久国产精品99精品国产| 欧美在线视频网站| 国产精品日韩欧美大师| 亚洲精品五月天| 夜夜精品视频一区二区| 一个色综合av| 欧美电影免费网站| 久久精品99国产精品日本| 国产精品欧美日韩一区二区| 亚洲精选在线观看| 欧美精品v日韩精品v韩国精品v| 久久久久一区二区三区| 狠狠综合久久| 久久伊人一区二区| 一区二区三区视频在线播放| 久久午夜av| 国产主播一区| 久久久久一区二区三区| 久久伊伊香蕉| 亚洲视频综合| 久久在线视频在线| 欧美影视一区| 国产在线视频不卡二| 欧美在线视屏| 欧美一区二区三区播放老司机| 国产农村妇女精品一区二区| 久久精品国产综合| 亚洲精品乱码久久久久久日本蜜臀 | 欧美一级播放| 激情av一区| 欧美国产日韩免费| 欧美电影免费| 亚洲视频欧美在线| 亚洲一区视频| 欧美精品成人91久久久久久久| 亚洲国产欧美久久| 久久深夜福利| 欧美国产激情| 中文精品视频| 欧美伊人久久| 亚洲精品免费在线播放| 亚洲精品免费网站| 国产日本欧美视频| 亚洲第一在线| 国产精品久久久久9999| 久久蜜桃香蕉精品一区二区三区| 免费成人在线观看视频| 国产免费一区二区三区香蕉精| 亚洲高清不卡在线| 久久久美女艺术照精彩视频福利播放| 久久精品99国产精品酒店日本| 亚洲激情黄色| 欧美一区二区在线看| 亚洲精品在线三区| 午夜精品久久久久久久蜜桃app | 亚洲乱码国产乱码精品精98午夜| 欧美国产视频日韩| 国产欧美日韩不卡免费| 蜜臀a∨国产成人精品| 亚洲欧美日韩综合一区| 国产一区二区三区在线观看免费 | 欧美国产精品久久| 亚洲精品在线三区| 亚洲深爱激情| 亚洲国产清纯| 日韩西西人体444www| 快射av在线播放一区| 欧美一区二区三区日韩| 国产一区激情| 裸体女人亚洲精品一区| 欧美午夜精品理论片a级按摩| 亚洲福利视频专区| 亚洲成色最大综合在线| 在线观看欧美精品| 亚洲精品国产精品国自产观看| 亚洲国产精品一区二区第一页| 日韩亚洲成人av在线| 亚洲欧美综合另类中字| 欧美乱妇高清无乱码| 在线日韩视频| 亚洲女人天堂成人av在线| 亚洲婷婷在线| 久久嫩草精品久久久精品一| 国产精品女人网站| 欧美日韩国产精品成人| 亚洲综合不卡| 亚洲一区二区高清视频| 在线成人亚洲| 美女精品网站| 欧美亚洲综合在线| 亚洲在线观看视频网站| 一区精品在线| 亚洲国产精彩中文乱码av在线播放| 一本色道久久综合亚洲精品按摩 | 欧美韩日一区| 欧美午夜剧场| 亚洲精品一区二区三区蜜桃久| 欧美中文在线视频| 亚洲欧美日韩精品综合在线观看| 久久综合九色综合网站| 国产精品丝袜白浆摸在线| 国产欧美日韩一区| 久久精品一区四区| 欧美一区二区播放| 国产精品白丝黑袜喷水久久久| 最近中文字幕日韩精品| 亚洲无玛一区| 久久久久久久精| 久久精品国产99| 久久av一区二区三区漫画| 亚洲午夜未删减在线观看| 性欧美video另类hd性玩具| 亚洲一区二三| 亚洲三级影片| 亚洲人成免费| 久久综合伊人77777麻豆| 亚洲手机成人高清视频| 永久555www成人免费| 亚洲国产精品一区在线观看不卡 | 久久―日本道色综合久久| 久久国产一二区| 亚洲一级免费视频| 国产日本亚洲高清| 香蕉久久夜色精品| 欧美一级专区| 亚洲欧美中文在线视频| 久久久久一区二区三区| 久久精品国产91精品亚洲| 亚洲一区二区动漫| 免费视频一区二区三区在线观看| 国产一区999| 欧美va亚洲va香蕉在线| 夜夜嗨av一区二区三区| 欧美在线综合视频| 国产专区欧美精品| 久久综合99re88久久爱| 亚洲福利av| 久久精品国产99| 亚洲欧美在线免费观看| 国产精品私房写真福利视频 | 国产精品一区二区久久| 亚洲国产精品日韩| 亚洲性夜色噜噜噜7777| 国产午夜精品全部视频播放| 欧美精品1区2区| 久久一二三区| 香蕉av777xxx色综合一区| 亚洲一区二区视频| 欧美激情视频在线免费观看 欧美视频免费一 | 久久综合久久综合这里只有精品| 久久精品一区二区| 欧美高清在线播放|