(Conversion) The waveoutopen function is required to enable the sound device.

Source: Internet
Author: User
Transferred from: bytes (which can be found in your document ). Like many other Windows objects, you can simply use a handle to call the device. For example, if hwnd is used to store windows window handles, we can use hwaveout handles to call sound devices. The following code snippet illustrates how to enable a waveform device with the CD Standard Sound Quality and then disable it. # Include <windows. h> # include <mmsystem. h> # include <stdio. h> int main (INT argc, char * argv []) {hwaveout;/* Device handle */waveformatex WFX;/* look this up in your Documentation */mmresult result; /* For waveout return values * // ** first we need to set up the waveformatex structure. * The Structure describes the format of the audio. */WFX. nsamplespersec = 44100;/* sample rate */WFX. wbitspersa Mple = 16;/* Sample size */WFX. nchannels = 2;/* channels * // ** waveformatex also has other fields which need filling. * As long as the three fields abve are filled this shoshould * work for any PCM (Pulse Code Modulation) format. */WFX. cbsize = 0;/* size of _ extra _ INFO */WFX. wformattag = wave_format_pcm; WFX. nblockalign = (WFX. wbitspersample> 3) * WFX. nchannels; WFX. navgbytespersec = WFX. nblockalign * WFX. nsamplespersec;/** try to open the default wave device. wave_mapper is * a constant defined in mmsystem. h, it always points to the * default wave device on the system (some people have 2 or * more sound cards ). */If (waveoutopen (& hwaveout, wave_mapper, & WFX, 0, 0, callback_null )! = Mmsyserr_noerror) {fprintf (stderr, "unable to open wave_mapper device \ n"); exitprocess (1 );} /** device is now open so print the success message * and then close the device again. */printf ("the wave mapper device was opened successfully! \ N "); waveoutclose (hwaveout); Return 0;} Note: To compile the program, you must add winmm. lib to your project. Otherwise, the link will fail. Now that the sound device is ready, we can write audio data. It is interesting to enable or disable a sound device, but the above Code does not really do anything. What we want is to hear the sound from the device. Before that, we have two things to do. You can use the disk writer plug-in of Winamp to convert a music file into the original audio file. For example, you can convert the Windows Audio File (such as ding.wav) under \ windows \ media to the original audio file. If you cannot convert these files, playing unconverted files directly is also very interesting. Direct playback sounds very fast, because most of these files are stored at a 22 KHz sampling frequency. Problem 2 is a little more complicated. Audio is written into the device in block form. Each block has its own header ). Writing a block is very easy, but most of the time, we need to establish a queue mechanism and write a lot of blocks ). The reason for learning with a small file is that the following example loads the entire file into a block and writes it to the device. First, we need to write a function to send a piece of data to the audio device. The function is named writeaudioblock. To write audio data, we need three interface functions: waveoutprepareheader, waveoutwrite, and waveoutunprepareheader, and call them in this order. You can find and familiarize yourself with these functions in related documents. The following code is the initial version of the writeaudioblock function. Void writeaudioblock (hwaveout, lpstr block, DWORD size) {wavehdr header;/** initialise the block header with the size * and pointer. */zeromemory (& header, sizeof (wavehdr); header. dwbufferlength = size; header. lpdata = block;/** prepare the block for playback */waveoutprepareheader (hwaveout, & header, sizeof (wavehdr);/** write the block to the device. waveoutwrite returns immediately * unless Synchronous driver is used (not often ). */waveoutwrite (hwaveout, & header, sizeof (wavehdr);/** wait a while for the block to play then start trying * To unprepare the header. this will fail until the block has * played. */sleep (500); While (waveoutunprepareheader (hwaveout, & header, sizeof (wavehdr) = waverr_stillplaying) sleep (100 );} now we have a function to write block data. We also need a function to obtain the audio data block. This is the task of loadaudioblock. The loadaudioblock function reads the file to the memory and returns the pointer. The following is the loadaudioblock code: lpstr loadaudioblock (const char * filename, DWORD * blocksize) {handle hfile = invalid_handle_value; DWORD size = 0; DWORD readbytes = 0; void * block = NULL; /** open the file */If (hfile = createfile (filename, generic_read, file_1__read, null, open_existing, 0, null) = invalid_handle_value) return NULL; /** get it's size, allocate memory and read the file * into memory. don't use this on Large files! */Do {If (size = getfilesize (hfile, null) = 0) break; If (Block = heapalloc (getprocessheap (), 0, size )) = NULL) break; readfile (hfile, block, size, & readbytes, null);} while (0); closehandle (hfile); * blocksize = size; Return (lpstr) block;} at the end of this part is the entire program call and main function. # Include <windows. h> # include <mmsystem. h> # include <stdio. h> lpstr loadaudioblock (const char * filename, DWORD * blocksize); void writeaudioblock (hwaveout, lpstr block, DWORD size); int main (INT argc, char * argv []) {hwaveout; waveformatex WFX; lpstr block;/* pointer to the block */DWORD blocksize;/* holds the size of the block */.. (leave middle section as it was ). printf ("the wave mapper Device was opened successfully! \ N ");/** load and play the block of audio */If (Block = loadaudioblock (" C: \ temp \ ding. raw ", & blocksize) = NULL) {fprintf (stderr," unable to Load file \ n "); exitprocess (1) ;}writeaudioblock (hwaveout, block, blocksize); waveoutclose (hwaveout); Return 0;} Put the above Code in a project for compilation to play a small sound file. We have implemented functions similar to playsound functions. Please try to do some small experiments: Change the sampling frequency of playback (in the main function) or change the sample size (Note: it must be a multiple of 8) to see what will happen, you can even change the number of channels. We will find that changing the sampling frequency or number of channels will speed up or slow down the playback speed, and changing the sample size may have a devastating impact! When playing streaming audio to a device, you may notice several important defects in the above Code (note that these are intentional). The obvious defects are as follows: limited by the way of loading data, we cannot play too many files. The current method caches the entire file and finishes playing it again. However, audio is very large in nature, so we need to find a way to convert audio data into streaming data and write one block to the device. The existing writeaudioblock function is executed synchronously, so one bit) writing multiple blocks in a single place has an interval between two block outputs (that is, the buffer cannot be refilled quickly enough )). Microsoft recommends that at least two buffer systems be required, so that we can fill the other block while playing one block, and then swap the playing and filling blocks. In fact, the problem cannot be solved completely. Even switching data block playback can cause a very small (but annoying) interval. Fortunately, data block reading is very simple, so you don't need to worry about it for the moment. Now let's focus on how to set up a caching mechanism to avoid audio device sound intervals. The block switching problem is not as serious as it sounds. We cannot switch two data blocks at intervals, but the interface has a mechanism that allows us to avoid this problem. Interfaces manage the queue of a block. Each data block transmitted using waveoutprepareheader can be inserted into this queue by calling waveoutwrite. This means that we can write two (or more) data blocks to the device. When the first data block is played, it is filled with the third data block, then, when the second player is playing, switch back. In this way, we can get the audio output without interval. There is another question about this method. How do we know that a data block has been played out? In the first example of writeaudioblock, the method of calling waveoutunprepareheader after the block is completed is very bad. We can't do this in practical applications, because we need to fill in new data blocks to the device to continue playing. For this, we provide a better method in the waveout interface. The waveout interface provides four callback mechanisms to notify us that the data block has been played. They are: An Event Callback Function is triggered when the data block is played completely. A function thread is called when the data block is played completely) -- a thread message window (window) is sent when data block playback is complete) -- When data block playback is complete, a window message is sent. You only need to specify the dwcallback parameter when calling the waveoutopen function. The callback function is used in the following example. So we need a new function: waveoutproc. How to define this function can be found in the relevant documentation. You can see that this function will be called in the following three cases: when the device is turned on (opened) when the device is turned off (closed) when data block playback is complete, we are only interested in this situation when data block playback is complete. The Caching mechanism we will implement runs as we mentioned above. It requires a variable to store the number of idle caches at any time (you may have thought of Using semaphore to control the number, but we cannot use it, which will be explained later ). This variable is initialized to the number of caches. This variable decreases when data blocks are written and increases when data blocks are completed. If no cache is available, we will wait until the variable counter is greater than 1 and then continue writing. This will allow us to effectively write data to any number of data block queues cyclically. In our example, there are not three data block queues, but more, such as 20, so that each time we can process about 8 KB of data. You may have guessed something: waveoutproc is called in different threads. Windows creates a special thread to manage audio playback. There are many restrictions on what you can do in this callback function. Let's take a look at what Microsoft says: "applications shocould not call any system-defined functions from inside a callback function, role t for entercriticalsection, leavecriticalsection, midioutlongmsg, midioutshortmsg, outputdebugstring, postmessage, postthreadmessage, setevent, timegetsystemtime, timegettime, timekillevent, and timesetevent. calling other wave functions will cause deadlock. "The application cannot call system functions except the following in the callback function: Entercriticalsection, leavecriticalsection, midioutlongmsg, midioutshortmsg, outputdebugstring, postmessage, postthreadmessage, setevent, timegetsystemtime, timegettime, timekillevent, and timesetevent. Calling other wave functions may cause deadlocks. This explains why we cannot use semaphores (semaphore) -- this will need to call the releasesemaphore system function, which we cannot do. It may be more flexible in practical applications-I have seen the code for using semaphores in callback functions, however, such programs may be executed on some versions of Windows, but not on machines of other versions. Similarly, calling the waveout function in the callback function also causes a deadlock. In fact, we will also call the waveoutunprepareheader In the callback function, but we cannot do that. (If you do not call waveoutreset, no deadlock will occur ). You may notice that waveoutopen provides a method for passing instance data to the callback function (a user-defined pointer). We will use this method to pass our counter variable pointer. In addition, since waveoutproc is called in another thread, more than two threads operate on this counter variable. To avoid thread conflicts, we need to use the critical section object (we will use a static variable and name it wavecriticalsection ). The following code is the waveoutproc function: static void callback waveoutproc (hwaveout, uint umsg, DWORD dwinstance, DWORD dwparam1, DWORD dwparam2) {/** pointer to free block counter */int * freeblockcounter = (int *) dwinstance;/** ignore cballs that occur due to openining and closing the * device. */If (umsg! = Wom_done) return; entercriticalsection (& wavecriticalsection); (* freeblockcounter) ++; leavecriticalsection (& wavecriticalsection );} then we need two functions to allocate and release the data block memory and a new writeaudioblock implementation named writeaudio. The following two functions allocateblocks and freeblocks implement data block allocation and release. Allocateblocks is allocated with a group of data blocks. The header of each data block is of a fixed length. Freeblocks releases the memory of the data block. If allocateblocks fails, the program exits. This means that we do not need to check its return value in the main function. Wavehdr * allocateblocks (INT size, int count) {unsigned char * buffer; int I; wavehdr * blocks; DWORD totalbuffersize = (size + sizeof (wavehdr) * count; /** allocate memory for the entire set in one go */If (buffer = heapalloc (getprocessheap (), heap_zero_memory, totalbuffersize) = NULL) {fprintf (stderr, "Memory Allocation Error \ n"); exitprocess (1);}/** and set up the pointers to each bit */blocks = (wavehdr *) Buffer; buffer + = sizeof (wavehdr) * count; for (I = 0; I <count; I ++) {blocks [I]. dwbufferlength = size; blocks [I]. lpdata = buffer; buffer + = size;} return blocks;} void freeblocks (wavehdr * blockarray) {/** and this is why allocateblocks works the way it does */heapfree (getprocessheap (), 0, blockarray );} the new writeaudio function needs to be able to write necessary data blocks into the queue. The basic logic is as follows: while there's data availableif the current free block is preparedunprepare itend ifif there's space in the current free blockwrite all the data to the blockexit the functionelsewrite as much data as is possible to fill blockprepare the blockwrite itdecrement the free blocks countersubtract however blocks bytes were written from the data availablewait for at least one block to become freeu Pdate the current block pointerend ifend while generates a problem: how do we know when a data block is ready and when it is not? In fact, this is quite simple. Windows uses the dwflags member variable of the wavehdr struct to solve this problem. One of the functions of the waveoutprepareheader function is to set dwflags to whdr_prepared. So what we need to do is to check this identifier in dwflags. We will use the dwuser member variable in the wavehdr struct to manage the counters of data blocks. The code for the writeaudio function is as follows: void writeaudio (hwaveout, lpstr data, int size) {wavehdr * Current; int remain; current = & waveblocks [wavecurrentblock]; while (size> 0) {/** first make sure the header we're going to use is unprepared */If (current-> dwflags & whdr_prepared) waveoutunprepareheader (hwaveout, current, sizeof (wavehdr); If (size <(INT) (block_size-current-> dwuser) {memcpy (current-> lpdata + current -> Dwuser, Data, size); current-> dwuser + = size; break;} remain = block_size-current-> dwuser; memcpy (current-> lpdata + current-> dwuser, Data, remain); size-= remain; Data + = remain; Current-> dwbufferlength = block_size; waveoutprepareheader (hwaveout, current, sizeof (wavehdr); waveoutwrite (hwaveout, current, sizeof (wavehdr); entercriticalsection (& wavecriticalsection); wavefreeblockcount --; leavecriticalsec Tion (& wavecriticalsection);/** wait for a block to become free */while (! Wavefreeblockcount) sleep (10);/** point to the next block */wavecurrentblock ++; wavecurrentblock % = block_count; current = & waveblocks [wavecurrentblock]; current-> dwuser = 0 ;}} now we have a new function for writing audio, because it will not be used again, so you can discard the writeaudioblock function. You can also discard the loadaudioblock function, because the loadaudioblock function is no longer needed to implement new methods in the main function. If you follow this tutorial to run the program, you should now have a c file containing the following functions: in mainwaveoutprocallocateblocksfreeblockswriteaudio, let's complete the new main function version to stream the files on the hard disk to the waveout device. The following code also includes the declaration of the module variables required for running the program and the prototype of the function we have written. # Include <windows. h> # include <mmsystem. h> # include <stdio. h>/** some good values for block size and count */# define block_size 8192 # define block_count 20/** function prototypes */static void callback waveoutproc (hwaveout, uint, DWORD, DWORD, DWORD); static wavehdr * allocateblocks (INT size, int count); static void freeblocks (wavehdr * blockarray); static void writeaudio (hwaveout, lpstr data, int s Ize);/** module level variables */static critical_section wavecriticalsection; static wavehdr * waveblocks; static volatile int wavefreeblockcount; static int wavecurrentblock; int main (INT argc, char * argv []) {hwaveout;/* Device handle */handlehfile;/* file handle */waveformatex WFX; /* look this up in your Documentation */Char buffer [1024];/* intermediate buffer for reading */int I;/** quick Argument check */If (argc! = 2) {fprintf (stderr, "Usage: % s \ n", argv [0]); exitprocess (1 );} /** initialise the module variables */waveblocks = allocateblocks (block_size, block_count); wavefreeblockcount = block_count; wavecurrentblock = 0; initializecriticalsection (& wavecriticalsection ); /** try and open the file */If (hfile = createfile (argv [1], generic_read, file_1__read, null, open_existing, 0, null) = invalid_handle_value) {fprintf (Stderr, "% s: Unable to open file '% s' \ n", argv [0], argv [1]); exitprocess (1 );} /** set up the waveformatex structure. */WFX. nsamplespersec = 44100;/* sample rate */WFX. wbitspersample = 16;/* Sample size */WFX. nchannels = 2;/* channels */WFX. cbsize = 0;/* size of _ extra _ INFO */WFX. wformattag = wave_format_pcm; WFX. nblockalign = (WFX. wbitspersample * WFX. nchannels)> 3; WFX. navgbytespersec = WFX. nblo Ckalign * WFX. nsamplespersec;/** try to open the default wave device. wave_mapper is * a constant defined in mmsystem. h, it always points to the * default wave device on the system (some people have 2 or * more sound cards ). */If (waveoutopen (& hwaveout, wave_mapper, & WFX, (dword_ptr) waveoutproc, (dword_ptr) & wavefreeblockcount, callback_function )! = Mmsyserr_noerror) {fprintf (stderr, "% s: Unable to open wave mapper device \ n", argv [0]); exitprocess (1 );} /** playback loop */while (1) {DWORD readbytes; If (! Readfile (hfile, buffer, sizeof (buffer), & readbytes, null) break; If (readbytes = 0) break; If (readbytes <sizeof (buffer )) {printf ("at end of buffer \ n"); memset (buffer + readbytes, 0, sizeof (buffer)-readbytes); printf ("after memcpy \ n ");} writeaudio (hwaveout, buffer, sizeof (buffer);}/** wait for all blocks to complete */while (wavefreeblockcount <block_count) sleep (10 ); /** unprepare any blocks that are St Ill prepared */for (I = 0; I <wavefreeblockcount; I ++) if (waveblocks [I]. dwflags & whdr_prepared) blocks (hwaveout, & waveblocks [I], sizeof (wavehdr); deletecriticalsection (& signature); freeblocks (waveblocks); waveoutclose (hwaveout ); closehandle (hfile); Return 0;} what should I do next? What you need to do next depends on yourself. I have several suggestions you may be interested in: Try to modify the original audio program so that it can be read from the standard input. You can directly control the output audio file of the pipeline from the command line. Rewrite the read part so that it can directly read the wave file (*. wav) instead of the raw file (original audio file ). You will find this surprising simple. The wave file uses the struct waveformatex to define the data format. You can use it after opening the sound device. For more information about file formats, see wotsit's format website (http://www.wotsit.org ). Check whether you can create a new or better caching mechanism to add the code to an open-source decoder, such as Vorbis or MP3 decoder. In this way, you can get a media player of your own :)

(Conversion) The waveoutopen function is required to enable the sound device.

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.