Compress Wave Audio using CODECS

zhaozj2021-02-16  52

Compress Wave Audio using CODECS

SUMMARY Microsoft's WIN95 and Winnt operating systems include CODECs that compresses the decompression WAVE audio stream. Save your Wave audio in compressed forms not only reduce the demand for storage space, but also reduce data transmission time when transmitting on the network.

This article and its included instance code tell you how to use CODECs installed in the Windows system to compress and decompress the audio. Slightly change these code to use as uncompressed data, perform data format conversion. The accompanying example code is developed with Microsoft Visual C 5.0 and is tested in Win95 and Winnt 4.0 operating systems.

Introduction WIN95 and the nearest Winnt have the ability to over-installed CODECS processing compressed WAVE format audio and video data streams.

A CODEC is a code for compressing and decompressing data streams (therefore, name Co-Dec). Many CODECs can compress and decompress. Some CODECs can only be used to decompress, so private data can be played on the system, but the data format cannot be created on the system.

Although a CODEC can be used for compression decompression any of the data streams, or designed with a wide variety of CODECs to compress some data types with high compression ratio, better fidelity or real-time compression performance. . For example, the best way to get high video compression data compression ratio is not necessarily to obtain the same effect when it is applied to audio data, and vice versa.

This article focuses on how to use CODEC to compress the audio data in a manner supported in your system in your own code. One main reason for compressed audio data is to reduce the amount of data required to store a sound sequence. The amount of data means that the sound has a small space, and can be passed on Modem and network at a faster speed. If the data is compressed in a universal format supported by the Windows system, you can use it directly to use its own CODECs to extract the data and play.

What is the CODECS? Win95 and Winnt itself include several standard CODECs, or other Codecs can be installed by the applications installed in the system. For example, DSP Group, INC. TRUESPEECH CODEC sends with Win95, so any of the programs you write to Win95 can apply this CODEC (if the user does not delete it or disable it in the control panel). An example of CODEC that may be installed later is the audio data used by the Microsoft Network (MSN) software.

All installed CODECs are managed by the Audio Compression Manager (ACM). We can query the ACM from a small program to find which CODECs installed, which all supports what format. You can also double-click the multimedia options in the control panel, select the advanced tab, you can see the CODECs installed in the system.

Introduction Application ACM, I know that a good way to do what it managed every CODEC is to write a simple query ACM's command line application. This feature is completed in this article - let us look at its code, I will give you a search for this program to explain what features of each step.

First start from the included header file required to call the ACM programming interface:

#include & ltwindows.h> #include & ltmmsystem.h> #include & ltmmmreg.h> // Multimedia registration

#include & ltmsacm.h> // Audio Compression Manager

#include & ltstdio.h>

MMSystem.h header sets Windows to support most of the multimedia features, but does not include ACM interfaces and any vendor definition. Mmreg.h contains definitions of formatted labels for various WAVE data types designed for different vendors. It also includes definitions for processing different WAVE data types (based on WaveForamTex). MSACM.H contains APIs, markers, and more required by ACM. The first thing we have to do is to perform some common ACM queries to determine the version number, get information such as how much driver is currently managed. Below is part of the code of the ACM:

// get the ACM version number

DWORD DWACMVER = acmgetversion ();

Printf ("ACM VERSION% u.%. 02u build% U",

HiWord (dwacmver) >> 8,

HiWord (dwacmver) & 0x00FF,

Loword (dwacmver);

IF (loword (dwacmver) == 0) Printf ("(Retail));

Printf ("/ n");

// Show some information about ACM

Printf ("ACM Metrics: / N");

DWORD DWCODECS = 0;

MmResult MMR = ACMMETRICS (NULL, ACM_METRIC_COUNT_CODECS, & DWCODECS);

IF (mmr) {

Show_ERROR (MMR);

}

Else {

Printf ("% Lu Codecs Installed / N", DWCODECS);

}

The Caps instance queries more ACM information. You can take a closer look at its code and run the program to know the result.

After the ACM has a simple understanding, it is now possible to request all of the current drivers in the system. The enumeration function we call in the program uses the callback function to report the data of each device, which is a very common method in Windows programming. The following call is to enumerate all devices managed by the current ACM:

// Enumerate all permissible drivers

Printf ("enabled drivers: / n");

MMR = ACMDRIVERENUM (DriveRenumProc, 0, 0);

IF (mmr) show_error (mmr);

As with other multimedia functions, many ACM function calls return a mmResult value and point out possible errors. This value is 0 indicates that the function is successfully executed. Now let's take a look at the enumerated back function DriveRenumProc, which is called by each drive in the system:

Bool Callback DriveRenumProc (Hacmdriverid Hadid, DWORD DWInstance, DWORD FDWSUPPORT)

{

Printf ("ID:% 8.8LXH", HADID);

Printf ("supports: / n");

IF (FDWSupport & AcmdriverDetails_supportf_async) Printf ("async convers / n");

IF (FDWSupport & AcmdriverDetails_supportf_codec) Printf ("DiffERENT FORMAT Conversions / N);

IF (fdwsupport & acmdriverdetails_supportf_converter) Printf ("Same Format Conversions / N);

IF (fdwsupport & acmdriverdetails_supportf_filter) Printf ("Filtering / N"); // Get some specific information

Acmdriverdetails DD;

Dd.cbstruct = SizeOf (DD);

MmResult MMR = AcmdriverDetails (Hadid, & DD, 0);

IF (mmr) {

PRINTF (""); show_error (mmr);

}

Else {

Printf ("Short Name:% S / N", DD.SZSHORTNAME);

Printf ("Long Name:% S / N", DD.SZLONGNAME;

Printf ("CopyRight:% S / N", DD.SZCopyRight);

Printf ("Licensing:% S / N", DD.SZLICENSING;

Printf ("Features:% S / N", DD.SZFEATURES;

Printf ("Supports% u formats / n", dd.cformattags;

Printf ("Supports% U Filter Formats / N", DD.cfilTertags;

}

/ / Open the driver

Hacmdriver Had = NULL;

MMR = ACMDRIVEROPEN (& Had, Hadid, 0);

IF (mmr) {

PRINTF (""); show_error (mmr);

}

Else {

DWORD DWSIZE = 0;

MMR = ACMMETRICS (HAD, ACM_METRIC_MAX_SIZE_FORMAT, & DWSIZE);

IF (DWSIZE CBSIZE = LOWORD (DWSIZE) - SIZEOF (WAVEFormatex);

PWF-> wformattag = Wave_Format_unknown;

ACMFORMATDETAILS FD;

MEMSET (& fd, 0, sizeof (fd));

fd.cbstruct = SizeOf (FD);

fd.pwfx = pWF;

fd.cbwfx = dwsize;

fd.dwformattag = Wave_FORMAT_UNKNOWN;

MMR = ACMFORMATENUM (HAD, & FD, FORMATENUMPROC, 0, 0);

IF (mmr) {

Printf ("");

Show_ERROR (MMR);

}

Free (PWF);

Acmdriverclose (HAD, 0);

}

Return true; // Continue enumeration

} The driver passed a set of flags that describe the types supported by the driver. Some drivers can operate asynchronously, while other drivers cannot be. Some drivers can convert a WAVE data format into another format (called CODECS), while others can only complete the filtering operation, which is the same as the input and output format. Note ACM maintains the names, copyright information of the driver, so we can get this data without loading or opening the specified driver. This is convenient, such as when you need to place the data in the list box is selected by the user.

To get more details about a certain driver capability, you must load the driver and open it, and can be implemented by calling the ACMOPENDRIVER. Once the driver is open, you can request enumerated WAVE data format it supported. At the same time, there is a small problem - although all WAVE format describes the WaveForamTex, many formats use this structure's extended form to save their specific information. If we want to enumerate all formats, you need to know how much for this structure is allocated to fill out the details of the details. The size of the desired structure can be obtained by passing the ACM_METRIC_MAX_SIZE_FORMAT flag to the ACMMETRICS function. If you have read the code above, you will find that I just simply convert the assigned space to the Waveformatex pointer. I only interested in common information instead of any particular type of data, so this pointer meets my requirements.

After allocating space for the structure, I can now enumerate the supported format. This time I use a backup function to get the relevant data of the enumerated format:

Bool Callback FormatenumProc (Hacmdriverid Hadid, LPACMMMATDETAILS PAFD,

DWORD DWINSTANCE, DWORD FDWSUPPORT)

{

Printf ("% 4.4LXH,% S / N", PAFD-> DWFORMATTAG, PAFD-> SZFORMAT);

Return true; // Continue enumeration

} As you can see, this is an attempt and only print some of the formats.

In this way, through the above code, you can query all the ACM drivers to find the format supported by each driver. I suggest you run the CAPS program now and see what you have installed on your system.

Using specific CODEC, we have learned what CODECs installed on your system - now look at how to find a particular CODEC and use it to compress audio data. Let's take a look at the Conv instance, which uses a valid CODEC to compress a simple WAVE packet. In order to make the code more simple, I implemented it in the form of a console application, and did not try to play the compressed data or store it into the file. The code of this instance is only to show you how to find the drive you need and use it to convert the data to a compressed format. Leave it to you.

Two-step implementation is compressed in the ideal case, compressing some data may only say to the system: "This has some data, please compress into this format." Unfortunately, Windows programming is very far, like usually the same We have to do many trivial work. The first thing to solve is also the most important question is that a given CODEC may not compress the data format you use. For example, we entered some 11025Hz, 8-bit, mono PCM data (perhaps the user said to the microphone), and almost all multimedia PCs in this format can be recorded. We might want to pass the data through MODEM, so we want to compress data as possible to reduce the amount of data. We chose TrucSpeech Codec, which is installed in Windows, which can get about 10: 1 compression ratio. The question we have to encounter is that TrueSpeech Codec cannot handle 11025Hz, 8-bit, mono PCM data. It can only process 8000Hz, 16-bit, mono data (8 bits in some cases). Therefore, we must first convert the source data to the intermediate PCM format supported by Truespeech Codec and then convert the intermediate data to the final desired format.

You can use a different CODEC that distributes to Windows to another, so you need to convert data to other CODEC to other CODEC. We have known how to enumerate the format that CODECS and its support, so this is achievable. But there is still a problem, I ignore it in the instance code, leave you to solve it. If a CODEC can create the compressed format we want, but support several different input formats, how do we choose the best intermediate format? According to Nigel's guidelines, "always do the least least work", I chose the first PCM format that uses CODEC supported. Since it is easy to implement, data distortion may be caused. Suppose there are some CODEC we have to use, there are some nearly distorted compression algorithms that receive 8-bit or 16-bit 11025 Hz or 22050 Hz PCM data. We want to convert high-fidelity samples recorded at 441000 Hz and 16-bit stereo. We tried to reduce the amount of data without caution. If we enumerate the format supported by this CODEC, the first result may be 11025Hz, 8-bit mono format. Let me convert the data to this format, then compress, this is certain to lose some quality because this intermediate is not good enough. If you use 16 bits 22050 Hz, it will be better. Tell you this shortcomings, let us look at the CONV instance, see how it works.

CONV instance program CONV instance divorced four phases: it creates some samples of WAVE format data, finds a suitable CODEC, transform the data to this CODEC to process the intermediate format, and finally convert the data into the desired format. For simplicity, the source data program creates instead of entry or read from the Wave file:

// First, we create a Wave in the WAVE that is just recorded in the format, 11.025kHz.

// 8-bit mono PCM, this is an entry format available on all machines, our example

// The child is a 1-second long 1kHz sine wave Wave, just 1000 cycles

WAVEFORMATEX WFSRC;

MEMSET (& WFSRC, 0, SIZEOF (WFSRC));

Wfsrc.cbsize = 0;

Wfsrc.wformattag = Wave_format_pcm; // pcm

Wfsrc.nchannels = 1; // mono

Wfsrc.nsamplespergec = 11025; // 11.025 kHz

Wfsrc.wbitsPersample = 8; // 8 bit

Wfsrc.nblockAlign = wfsrc.nchannels * wfsrc.wbitsPersample / 8;

Wfsrc.navgbytespersec = wfsrc.nsamplespersec * wfsrc.nblockalign;

DWORD dwsrcsamples = wfsrc.nsamplespespec;

BYTE * psrcdata = new byte [dwsrcsamples]; // 1 second length

BYTE * PDATA = Psrcdata;

Double f = 1000.0;

Double Pi = 4.0 * Atan (1.0);

Double W = 2.0 * pi * f;

For (DWORD DW = 0; DW / PRE>

The above code creates a WAVEFormatex structure to describe the source data format, and generate 1 with a simple mathematical method.

The second half of 11.025 kHz, 8-bit mono PCM WAVE data.

The next step is to choose to convert data to what format and select a suitable CODEC.

Word wformattag = Wave_format_dspgroup_truespeech; // Now we have selected a CODEC that supports the target format tag

HACMDRIVERID HADID = FIND_DRIVER (WFORMATTAG);

IF (hadid == null) {

Printf ("No Driver Found / N);

Exit (1);

}

Printf ("Driver Found (Hadid:% 4.4LXH / N", Hadid);

The Find_Driver function enumerates all drivers until a driver that supports a given tag value (this example is

Wave_format_dspgroup_truespeech). I have not given this code because it is in front of the enumeration

The code is very similar. You can then see how it works.

The driver is selected, and now you have to create a Waveformatex to the resulting compressed data format for the final driver.

Structure, and generate a WAVEFORMATEX structure for the intermediate PCM format used for the driver.

// Get the details of the format

// Note: This is just the first or most likely format for a given format.

Waveformatex * pwfdrv = get_driver_format (hadid, wformattag);

IF (pwfdrv == null) {

Printf ("Error getting format info / n");

Exit (1);

}

Printf ("Driver Format:% U BITS,% Lu Samples Per Second / N",

PWFDRV-> WBITSPERSPERSPLE, PWFDRV-> NSAMPLESPERSEC);

/ / Get the PCM format tag supported by the driver

// Note: We just choose the first supported PCM format but not necessarily the best choice.

Waveformatex * pwfpcm = get_driver_format (Hadid, Wave_Format_PCM);

IF (pWFPCM == null) {

Printf ("Error getting PCM Format Info / N);

Exit (1);

}

Printf ("PCM Format:% U BITS,% Lu Samples Per Second / N",

PWFPCM-> WBITSPERSPLE, PWFPCM-> NSAMPLESPERSEC);

It is a bit repeated. It is important to note that the get_driver_format function only enumerates the first matching format - may not be available

Possible best quality.

Now we have a WAVEFormatex structure description source format, intermediate PCM format, and final compression format. Can start turn

Data. The conversion is achieved by an object called the ACM. We can open the stream, pass the source format, target format to it,

Require it for conversion.

Be careful here if synchronous conversion is time consuming if CODEC's algorithm is complicated. Some CODEC can work asynchronously, through

The window sends a message, or calls a callback function, or set an event to inform you to the conversion process. The following code is

At least troubleshoot, complete task - you have to wait until it is completed. There is another point, it is very important. As you know, let's fight

Open switch, indicate the ACM_STREAMOPENF_NONREALTIME flag. This is very important. If you omit this mark, then some

Drivers (such as TRUESPEECH drivers) will report an error 512 (meaning is not possible). This error tells you what you want

The conversion of seeking cannot be performed in real time. There is no such problem in my instance code, but if you try to convert large amounts of data while playing data, you should pay attention to this.

Let's take a look at the conversion of the first step, it is done to convert the source data into an intermediate format:

/

// convert the source Wave to the PCM format supported by CODEC

// We use any of the converted drivers that enable PCM format

Hacmstream HSTR = NULL;

MMR = ACMSTREAMOPEN (& HSTR,

Null, // Any driver

& wfsrc, // source format

PWFPCM, // Target format

Null, // No filter

Null, // Did not call

0, // instance data (unused)

ACM_STREAMOPENF_NONREALTIME); // Sign

IF (mmr) {

Printf ("Failed to Open A Stream to Do PCM To PCM Conversion / N);

Exit (1);

}

/ / Open a buffer for the result of the conversion

DWORD DWSRCBYTES = dwsrcsamples * wfsrc.wbitsPersample / 8;

DWORD DWDST1SAMPLES = dwsrcsamples * pwfpcm-> nsamplespersperspec / wfsrc.nsamplespespec;

DWORD DWDST1BYTES = DWDST1SAMPLES * PWFPCM-> WBITSPERSAMPLE / 8;

BYTE * pdst1data = new byte [dwdst1bytes]; // Fill in the conversion information

ACMSTreamheader strHDR;

MEMSET (& strHDR, 0, SIZEOF (STRHDR));

strHdr.cbstruct = SizeOf (strHDR);

strHDr.pbsrc = psrcdata; // Source data to be converted

Strhdr.cbsrclength = dwsrcbytes;

strHDr.pbdst = pdst1data;

strHDr.cbdstlength = dwdst1bytes; // Prepare the head

MMR = ACMSTREAMPREPAREHEADER (HSTR, & strHDR, 0); // Conversion Data

Printf ("Converting to Intermediate PCM Format ... / N");

MMR = ACMSTREAMCONVERT (HSTR, & STRHDR, 0);

IF (mmr) {

Printf ("Failed to Do PCM To PCM Conversion / N");

Exit (1);

}

Printf ("CONVERTED OK / N");

// Close the flow

AcmstreamClose (HSTR, 0);

When the current is turned on, the second parameter is NULL, indicating that any driver is transformed. Complex only is calculated to be output

Data allocate how large buffers. Due to the conversion between the PCM format does not involve compression and decompression, it is calculated directly.

You may notice that ACMSTREAMPREPAREHEADER is called, which arranges everything for the driver and allows the driver before conversion.

Lock memory.

The final step is to convert the intermediate format to the final compression format:

///

/ / Convert intermediate format to the final compression format

/ / Open the driver

Hacmdriver Had = NULL;

MMR = ACMDRIVEROPEN (& Had, Hadid, 0);

IF (mmr) {

Printf ("failed to open driver / n"); exit (1);

}

// Open the conversion stream

// Note Use the ACM_STREAMOPENF_NONREALTIME flag.

// Without this flag Some software compression will report the 512 error - ie

MMR = ACMSTREAMOPEN (& HSTR,

Had, // Driver handle

PWFPCM, // Source Format

PWFDRV, // Target format

NULL, / /

NULL, // Non-return function

0, // instance data (unused)

ACM_STREAMOPENF_NONREALTIME); // Sign

IF (mmr) {

Printf ("Failed to Open A Street to Do Pcm To Driver Format Conversion / N");

Exit (1);

}

/ / Assign a buffer for the conversion result

/ / According to the size of the output buffer according to the average rate of bytes

/ / Plus a one-action position (bit)

/ / No extra space IMA_ADPCM driver will not be converted

DWORD DWDST2BYTES = PWFDRV-> NAVGBYTESPERSEC * DWDST1SAMPLES /

PWFPCM-> nsamplespersec;

DWDST2BYTES = dwdst2bytes * 3/2; // Add a point of space

BYTE * pdst2data = new byte [dwdst2bytes]; // Fill in the conversion information

ACMSTreamHeader strHDR2;

MEMSET (& strHDR2, 0, SIZEOF (strHDR2));

strHdr2.cbstruct = sizeof (strHDR2);

strHDr2.pbsrc = pdst1data; // To convert source data

Strhdr2.cbsrclength = dwdst1bytes;

strHDr2.pbdst = pdst2data;

strHDr2.cbdstlength = dwdst2bytes; // Prepare the head

MMR = ACMSTREAMPREPAREHEADER (HSTR, & strHDR2, 0); // Conversion Data

Printf ("Converting to Final Formal ... / N");

MMR = ACMSTREAMCONVERT (HSTR, & strHDR2, 0);

IF (mmr) {

Printf ("Failed to Do Pcm To Driver Format Conversion / N");

Exit (1);

}

Printf ("CONVERTED OK / N");

// Close flow and driver

MMR = acmstreamclose (HSTR, 0);

MMR = acmdriverclose (HAD, 0);

The above conversion is similar to the PCM format, but this time we provide the handle of the driver who wants to use when opening the stream. In fact,

Null can still be used here because this driver has presented, but the handle is provided to avoid the system waste time for us.

This driver is. Calculating the size of the buffer for compressed data is a bit difficult, requiring it to guess. The NavgBytespeSec domain of the WaveFormatex structure represents the average rate of reading bytes during playback. We can use it to estimate how much space needs to store compressed WAVE. The data given by some drivers is indeed average, rather than the value under the worst, so I choose to add more space. This method is a waste but effective in practice. Once the conversion is completed, the CBDStLengthused domain of the ACMStreamHeader's structure indicates how many bytes actually used. I use it to calculate the compression ratio: // Display conversion statistics

Printf ("Source Wave Had% Lu Bytes / N", DWSRCBYTES;

Printf ("Converted Wave Has% Lu Bytes / n", strHDr2.cbdstlength;

Printf ("Compression Ratio IS% F / N", (Double) DWSRCBYTES /

(Double) strHdr2.cbdstlength;

Summary Using the CODECS attached to the Windows operating system to compress the WAVE format data is easy to use, and make the data occupy less storage.

The transmission time is shorter. If you have your own compression format, you can create and install your own CODEC, using it as the code here.

转载请注明原文地址:https://www.9cbs.com/read-23947.html

New Post(0)