Example 8: directsound for playing PCM

Source: Internet
Author: User
This document records the techniques used by directsound to play audio. Directsound is the most common Audio Playback Technology in windows. Currently, most audio playback applications use directsound. This article records an example of using directsound to play PCM.
Note: A dear friend has reminded me that directsound is planned to be replaced by xaudio2. Later, I found that this was the case. Therefore, in the next update, consider adding xaudio2 to play PCM. This article still records the directsound veteran ".



Directsound introduction directsound is one of the components of DirectX developed by Microsoft. It can be recorded on Windows operating systems and waveform sound (waveform sound ). Currently, directsound is a mature API that provides many useful features, such as the ability to play multi-channel sound at a high resolution.
Directsound3d (ds3d) was first published in 1993 with DirectX 3. Directsound and directsound3d (ds3d) after DirectX 8 are collectively called DirectX audio.


Directsound has the following objects:

Object

Quantity

Function

Main Interface

Device

Each application has only one device object.

Used to manage devices and create secondary buffers

Idirectsound8

Secondary Buffer

Each sound corresponds to a secondary buffer.

Used to manage a static or dynamic sound stream, and then mixing in the main buffer zone

Idirectsoundbuffer8,

Idirectsound3dbuffer8,

Idirectsoundpolicy8

Primary buffer zone

An application has only one primary buffer.

Mixing the data in the secondary buffer and controlling 3D parameters.

Idirectsoundbuffer,

Idirectsound3dlistener8



Directsound audio playback process

To use directsound to play audio, follow these steps:

1. Initialization
1) Create an idirectsound8 interface object
2) Set collaboration level
3) create a primary buffer object
4) create a secondary buffer object
5) create a notification object
6) set the notification location

7) start playing

2. Loop playback
1) Data is filled into the secondary buffer.

2) wait until the playback is complete

The following describes the process in detail.


1. Initialization
1) Create an idirectsound8 interface object

You can use the directsoundcreate8 () method to create a device object. This object usually represents the default playing device. The directsoundcreate8 () function is defined as follows.
HRESULT DirectSoundCreate8( LPCGUID lpcGuidDevice, LPDIRECTSOUND8 * ppDS8, LPUNKNOWN pUnkOuter)

The parameters are described as follows:
Lpcguiddevice: guid of the device object to be created. You can specify NULL to indicate the default playback device.
Ppds8: Address of the returned idirectsound8 object.
Punkouter: It must be set to null.
For example, the following code creates an idirectsound8 interface object.
IDirectSound8 *m_pDS=NULL;DirectSoundCreate8(NULL,&m_pDS,NULL);

2) Set collaboration level
Windows is a multi-task environment where multiple applications access devices at the same time. By using the collaboration level, directsound ensures that applications are not accessed by other devices. Each directsound application has a collaboration level, which determines the hardware access permission.
After creating a device object, you must use setcooperativelevel () of idirectsound8 to set the cooperation permission. Otherwise, you will not be able to hear the sound. Setcooperativelevel () is defined as follows:
HRESULT SetCooperativeLevel( HWND hwnd, DWORD dwLevel)

The parameters are described as follows:
Hwnd: application window handle.
Dwlevel: the following levels are supported.
Dsscl_exclusive: same role as dsscl_priority.
Dsscl_normal: the normal coordination level sign. Other programs can share the sound card device for playing.
Dsscl_priority: Set the sound card device to exclusive to the current program.
Dsscl_writeprimar: The primary buffer zone can be written. At this time, the secondary buffer zone cannot be played, that is, the data in the secondary buffer zone cannot be sent to the sound mixer and then output to the primary buffer zone. This is the most comprehensive way to control sound playback.


3) create a primary buffer object
You can use createsoundbuffer () of idirectsound8 to create a primary buffer object for the idirectsoundbuffer interface. Createsoundbuffer () is defined as follows.
HRESULT CreateSoundBuffer( LPCDSBUFFERDESC pcDSBufferDesc, LPDIRECTSOUNDBUFFER * ppDSBuffer, LPUNKNOWN pUnkOuter)

The parameters are described as follows:
Pcdsbufferdesc: the address of the dsbufferdesc struct that describes the sound buffer.
Ppdsbuffer: the address of the object of the returned idirectsoundbuffer interface.
Punkouter: It must be set to null.
A structure dsbufferdesc describing sound buffering is involved. The structure is defined as follows:
typedef struct _DSBUFFERDESC{    DWORD           dwSize;    DWORD           dwFlags;    DWORD           dwBufferBytes;    DWORD           dwReserved;    LPWAVEFORMATEX  lpwfxFormat;} DSBUFFERDESC

Briefly explain the meanings of the variables:
Dwsize: the size of the struct. This value must be initialized.
Dwflags: Set the audio cache attributes. There are many options, which can be used in combination and will not be listed one by one. For detailed parameters, see the documentation.
Dwbufferbytes: the buffer size.
Dwreserved: Reserved parameter. It is useless for the moment.
Lpwfxformat: pointer to a wave format file header.
After dsbufferdesc is set, you can use createsoundbuffer () to create the primary buffer. The sample code is as follows:
DSBUFFERDESC dsbd;memset(&dsbd,0,sizeof(dsbd));dsbd.dwSize=sizeof(dsbd);dsbd.dwFlags=DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLPOSITIONNOTIFY |DSBCAPS_GETCURRENTPOSITION2;dsbd.dwBufferBytes=MAX_AUDIO_BUF*BUFFERNOTIFYSIZE; //WAVE Headerdsbd.lpwfxFormat=(WAVEFORMATEX*)malloc(sizeof(WAVEFORMATEX));dsbd.lpwfxFormat->wFormatTag=WAVE_FORMAT_PCM;   /* format type */(dsbd.lpwfxFormat)->nChannels=channels;          /* number of channels (i.e. mono, stereo...) */(dsbd.lpwfxFormat)->nSamplesPerSec=sample_rate;     /* sample rate */(dsbd.lpwfxFormat)->nAvgBytesPerSec=sample_rate*(bits_per_sample/8)*channels; /* for buffer estimation */(dsbd.lpwfxFormat)->nBlockAlign=(bits_per_sample/8)*channels;        /* block size of data */(dsbd.lpwfxFormat)->wBitsPerSample=bits_per_sample;     /* number of bits per sample of mono data */(dsbd.lpwfxFormat)->cbSize=0;//Creates a sound buffer object to manage audio samples. HRESULT hr1;if( FAILED(m_pDS->CreateSoundBuffer(&dsbd,&m_pDSBuffer,NULL))){   return FALSE;}


4) create a secondary buffer object
You can use QueryInterface () of idirectsoundbuffer to obtain an object of the idirectsoundbuffer8 interface. The GUID of idirectsoundbuffer8 is iid_idirectsoundbuffer8. The sample code is as follows.
IDirectSoundBuffer *m_pDSBuffer=NULL;IDirectSoundBuffer8 *m_pDSBuffer8=NULL;...if( FAILED(m_pDSBuffer->QueryInterface(IID_IDirectSoundBuffer8,(LPVOID*)&m_pDSBuffer8))){return FALSE ;}


5) create a notification object
Use QueryInterface () of idirectsoundbuffer8 to obtain an idirectsoundpolicy8 interface object. The GUID of idirectsoundbuffer8 is iid_idirectsoundnotify. The sample code is as follows.
IDirectSoundBuffer8 *m_pDSBuffer8=NULL;IDirectSoundNotify8 *m_pDSNotify=NULL;…if(FAILED(m_pDSBuffer8->QueryInterface(IID_IDirectSoundNotify,(LPVOID*)&m_pDSNotify))){return FALSE ;}


The purpose of the notification object is summarized in one sentence: After the data in the directsound buffer is played, the system is notified that new data should be filled.


6) set the notification location
You can use setnotifposipositions () of idirectsoundpolicy8 to set the notification location. Setnotifpositions () is defined as follows.
HRESULT SetNotificationPositions(         DWORD dwPositionNotifies,         LPCDSBPOSITIONNOTIFY pcPositionNotifies)

The parameter description is as follows.
Dwpositionnotifies: Number of dsbpositionpolicy struct. Contains the location of several notifications.
Pcpositionnotifies: pointer to the dsbpositionpolicy struct array.
Here, a struct dsbpositionpolicy describes the location of the notification. Dsbpositionpolicy is defined as follows.
typedef struct DSBPOSITIONNOTIFY {    DWORD dwOffset;    HANDLE hEventNotify;} DSBPOSITIONNOTIFY;

Its member meanings are as follows.
Dwoffset: the trigger position of the notification event (offset from the starting position of the buffer ).
Heventpolicy: handle of the event to be triggered.


7) start playing
You can use setcurrentposition () of idirectsoundbuffer8 to set the playback position. Setcurrentposition () is defined as follows:
HRESULT SetCurrentPosition(         DWORD dwNewPosition)

Dwnewposition is the offset between the playback point and the first byte in the buffer zone.
You can use play () of idirectsoundbuffer8 to start playing audio data. Play () is defined as follows.
HRESULT Play(         DWORD dwReserved1,         DWORD dwPriority,         DWORD dwFlags)

Parameter description:
Dwreserved1: Reserved parameter, which must be 0.
Dwpriority: priority. Generally, you can set it to 0.
Dwflags: flag. Currently, dsbplay_looping is common. When playing the video to the end of the buffer zone, play the video again from the beginning of the buffer zone.


2. Loop playback
1) Data is filled into the secondary buffer.

Use lock () to lock the buffer before filling data in the secondary buffer. Then, you can use fread (), memcpy (), and other methods to fill the PCM audio sample data into the buffer zone. After the data is filled, use unlock () to cancel the buffer lock.
The lock () function is defined as follows.
HRESULT Lock(         DWORD dwOffset,         DWORD dwBytes,         LPVOID * ppvAudioPtr1,         LPDWORD  pdwAudioBytes1,         LPVOID * ppvAudioPtr2,         LPDWORD pdwAudioBytes2,         DWORD dwFlags)

The parameter description is as follows.
Dwoffset: the offset between the locked memory and the first address of the buffer.
Dwbytes: the size of the locked cache.
Ppvaudioptr1: The obtained pointer to the cached data.
Pdwaudiobytes1: size of the retrieved cache data.
Ppvaudioptr2: not used, set to null.
Pdwaudiobytes2: it is not used and is set to 0.
Dwflags: no research at the moment.


The unlock () function is defined as follows.
HRESULT Unlock(         LPVOID pvAudioPtr1,         DWORD dwAudioBytes1,         LPVOID pvAudioPtr2,         DWORD dwAudioBytes2)

The parameter description is as follows.
Pvaudioptr1: pointer to cached data obtained through lock.
Dwaudiobytes1: the volume of data written.
Pvaudioptr2: not used.

Dwaudiobytes2: not used.

2) wait until the playback is complete

Based on the previously configured Notification Mechanism, use waitformultipleobjects () to wait for the data in the buffer zone to finish playing and then enter the next loop.


Audio playback process summary

Shows the process for directsound to play PCM audio data.

 

The relationships between several struct are shown in.
 




Code

Paste the source code.

/*** The simplest example of directsound playing audio (directsound playing PCM) * simplest audio play directsound (directsound play PCM) ** leixiao Li Lei Xiaohua * [email protected] * China Media University/Digital TV technology * Communication University of China/Digital TV technology * http://blog.csdn.net/leixiaohua1020 ** this program uses directsound to play PCM audio sample data. * Is the simplest tutorial for playing audio in directsound. ** Function call procedure: ** [initialization] * directsoundcreate8 (): Create a directsound object. * Setcooperativelevel (): sets the collaboration permission, otherwise there is no sound. * Idirectsound8-> createsoundbuffer (): Creates a primary buffer object. * Idirectsoundbuffer-> QueryInterface (iid_idirectsoundbuffer8 ..): * creates a secondary buffer object to store the audio data file to be played. * Idirectsoundbuffer8-> QueryInterface (iid_idirectsoundnotify ..): * Create a notification object to notify the application that the specified playback position has reached. * Idirectsoundpolicy8-> setnotifposipositions (): sets the notification location. * Idirectsoundbuffer8-> setcurrentposition (): sets the start point of playback. * Idirectsoundbuffer8-> play (): start playing. ** [Loop playback data] * idirectsoundbuffer8-> lock (): Lock the secondary buffer and prepare to write data. * Fread (): reads data. * Idirectsoundbuffer8-> unlock (): Unlock the secondary buffer. * Waitformultipleobjects (): Wait for the notification that "the playing position has reached. ** This software plays PCM raw audio data using directsound. * it's the simplest tutorial about directsound. ** the process is shown as follows: ** [init] * directsoundcreate8 (): init directsound object. * setcooperativelevel (): must set, or we won't hear sound. * idirectsound8-> createsoundbuffer (): Create primary sound buffer. * idirectsoundbuffer-> QueryInterface (iid_idirectsoundbuffer8 ..): * C Reate secondary sound buffer. * idirectsoundbuffer8-> QueryInterface (iid_idirectsoundnotify ..): * Create notification object. * idirectsoundpolicy8-> setnotifposipositions (): * Set notification positions. * idirectsoundbuffer8-> setcurrentposition (): Set position to start. * idirectsoundbuffer8-> play (): begin to play. ** [loop to play data] * idirectsoundbuffer8-> lock (): Lock secondary buffer. * fr EAD (): Get PCM data. * idirectsoundbuffer8-> unlock (): Unlock secondary buffer. * waitformultipleobjects (): Wait for communications. */# include <stdio. h> # include <stdlib. h> # include <windows. h> # include <dsound. h> # define max_audio_buf 4 # define bufferpolicysize 192000 int sample_rate = 44100; // PCM sample rateint channels = 2; // PCM channel numberint bits_per_sample = 16; // bits per samplebool main (INT argc, CH Ar * argv []) {int I; file * FP; If (FP = fopen (".. /nocturneno2ineflat_44.1k_s16le.pcm "," rb ") = NULL) {printf (" cannot open this file \ n "); Return-1;} idirectsound8 * m_tp= NULL; idirectsoundbuffer8 * m_pdsbuffer8 = NULL; // used to manage sound buffers. idirectsoundbuffer * m_pdsbuffer = NULL; idirectsoundpolicy8 * m_pdsnoloud = NULL; dsbpositionpolicy m_pdsposnotify [max_audio_buf]; handle m_event [max_audio_buf]; setconsole Title (text ("simplest audio play directsound"); // console title // init directsoundif (failed (directsoundcreate8 (null, & m_tp, null) return false; if (failed (m_pds-> setcooperativelevel (findwindow (null, text ("simplest audio play directsound"), dsscl_normal) return false; dsbufferdesc dsbd; memset (& dsbd, 0, sizeof (dsbd); dsbd. dwsize = sizeof (dsbd); dsbd. dwflags = dsbcaps_globalfocus | dsbcaps_ctrlpositionpolicy | DS Bcaps_getcurrentposition2; dsbd. dwbufferbytes = max_audio_buf * bufferpolicysize; // wave headerdsbd. lpwfxformat = (waveformatex *) malloc (sizeof (waveformatex); dsbd. lpwfxformat-> wformattag = wave_format_pcm;/* format type */(dsbd. lpwfxformat)-> nchannels = channels;/* number of channels (I. e. mono, stereo ...) */(dsbd. lpwfxformat)-> nsamplespersec = sample_rate;/* sample rate */(dsbd. lpwfxformat)-> navgbytesperse C = sample_rate * (bits_per_sample/8) * channels;/* for buffer estimation */(dsbd. lpwfxformat)-> nblockalign = (bits_per_sample/8) * channels;/* block size of Data */(dsbd. lpwfxformat)-> wbitspersample = bits_per_sample;/* Number of BITs per sample of Mono data */(dsbd. lpwfxformat)-> cbsize = 0; // creates a sound buffer object to manage audio samples. hresult HR1; If (failed (m_pds-> createsoundbuffer (& dsbd, & m_pdsb Uffer, null) {return false;} If (failed (m_pdsbuffer-> QueryInterface (iid_idirectsoundbuffer8, (lpvoid *) & m_pdsbuffer8) {return false ;} // get idirectsoundpolicy8if (failed (m_pdsbuffer8-> QueryInterface (response, (lpvoid *) & m_pdsnooid) {return false ;}for (I = 0; I <max_audio_buf; I ++) {m_pdsposnotify [I]. dwoffset = I * bufferpolicysize; m_event [I] =: createevent (null, false, false, null); m_pdsposnotify [I]. heventnotify = m_event [I];} m_pdsnotify-> seticationposipositions (optional, m_pdsposnotify); m_pdsnotify-> release (); // start playingbool isplaying = true; lpvoid Buf = NULL; DWORD buf_len = 0; DWORD res = wait_object_0; DWORD offset = bufferpolicysize; m_pdsbuffer8-> setcurrentposition (0); m_pdsbuffer8-> play (0, 0, callback); // loopwhile (isplaying) {If (RES> = wait_object_0) & (RES <= wait_object_0 + 3) {m_pdsbuf Fer8-> lock (offset, bufferpolicysize, & Buf, & buf_len, null, null, 0); If (fread (BUF, 1, buf_len, FP )! = Buf_len) {// file end // loop: fseek (FP, 0, seek_set); fread (BUF, 1, buf_len, FP); // close: // isplaying = 0;} m_pdsbuffer8-> unlock (BUF, buf_len, null, 0); offset + = buf_len; offset % = (bufferpolicysize * max_audio_buf ); printf ("this is % 7D of buffer \ n", offset);} res = waitformultipleobjects (max_audio_buf, m_event, false, infinite);} return 0 ;}


Running result

After the code is run, a "console" dialog box is displayed, as shown in. At the same time, the audio device can hear the playing sound.



 
Download

The code is in "simplest media play"


SourceForge project: https://sourceforge.net/projects/simplestmediaplay/

Http://download.csdn.net/detail/leixiaohua1020/8054395 (csdn)


The above project contains the use of various APIs (direct3d, OpenGL, GDI, directsound, sdl2) to play multimedia examples. The audio input is PCM sampling data. Output to the sound card of the system. The video input is YUV/RGB pixel data. Output to a display window for playing.
The code of this project allows beginners to quickly learn how to use these APIs to play videos and audios.
The following sub-projects are included:
Simplest_audio_play_directsound: Use directsound to play PCM audio sample data.
Simplest_audio_play_sdl2: Use sdl2 to play PCM audio sample data.
Simplest_video_play_direct3d: Use the direct3d surface to play RGB/YUV video pixel data.
Simplest_video_play_direct3d_texture: Use direct3d texture to play RGB video pixel data.
Simplest_video_play_gdi: Use GDI to play RGB/YUV video pixel data.
Simplest_video_play_opengl: uses OpenGL to play pixel data of RGB/YUV videos.
Simplest_video_play_opengl_texture: use OpenGL texture to play the YUV video pixel data.
Simplest_video_play_sdl2: Use sdl2 to play RGB/YUV video pixel data.



Example 8: directsound for playing PCM

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.