"linux& Audio" ALSA Audio Programming "essence"
Preface: Here is a look at the meaning of each parameter and some basic concepts.
Sample length: Sample is the most basic unit for recording audio data, with 8-bit and 16-bit common.
Channel number (channel): This parameter is 1 for mono, and 2 for stereo. Frame: Hardwood records a sound unit whose length is the product of the sample length and the number of channels.
Sample rate (Rate): The number of samples per second, which is the number of frames.
Cycle (Period): The number of frames required for an audio device to be processed at a time, and the data access for audio devices and the storage of audio data are all in this unit.
Interleaved mode (interleaved): is a record of audio data, in staggered mode, the data in the form of continuous frame storage, that is, the first record of 1 of the left channel samples and right channel samples (assumed to be in stereo format), and then began to frame 2 records. In the non staggered mode, the first record is a period of all the frames of the left channel samples, and then record the right channel samples, data is stored in a continuous channel. But in most cases, we just need to use staggered mode. Period (cycle): The time between interrupts in the hardware. It represents the input delay.
The sound card interface has a pointer to indicate the current read/write location in the hardware cache of the sound card. As long as the interface is running, the pointer loops to a location in the cache.
Frame size = sizeof (one sample) * Nchannels
The cache (buffer) and period (size) sizes configured in ALSA are stored in frames (frames) in runtime.
Period_bytes = Frames_to_bytes (runtime, runtime->period_size);
Bytes_to_frames ()
Introduction to ALSA Sound programming
ALSA represents the Advanced Linux sound Architecture (Advanced Linux Sound architecture). It consists of a series of kernel-driven, application-compiled interfaces (APIs), and utilities that support sound under Linux. In this article, I will briefly describe the basic framework of the ALSA project and its software composition. The main focus is on PCM interface programming, including program examples that you can automate.
The reason you use ALSA may be that it is new, but it is not the only sound API available. Alsa is a good choice if you want to perform low-level sound operations so that you can maximize your sound control and maximize performance, or if you are using features that are not available from other voice APIs. If you have already written an audio program, you may want to add local support for the ALSA sound card driver. If you are not interested in audio, just want to play audio files, then the Advanced API will be a better choice, such as sdl,openal and those desktop environment provided by the toolset. In addition, you can only use ALSA in Linux environments with ALSA support.
Second, ALSA history
The cause of the ALSA project was that the sound card driver (Oss/free drivers) under Linux was not actively maintained. And lag behind the new sound card technology. Jaroslav Kysela earlier wrote a sound card driver, and thus began the ALSA project, casual, more developers to join the development team, more sound cards are supported, the structure of the API has been reorganized.
Linux kernel 2.5 During the development process, ALSA was merged into the official source tree. After the release of kernel 2.6, ALSA has been built into a stable kernel version and will be widely used.
Three, digital audio basics
The sound is made up of varying air pressure. It is converted into electronic form by a converter such as a microphone. The modulus/number (ADC) converter converts the analog voltage to a discrete sample value. The sound is sampled at fixed intervals, and the rate of sampling is called the sampling rate. Output the sample to a digital/analog (DAC) converter, such as a loudspeaker, and finally converted to the original analog signal.
The sample size is represented by a bit. Sample size is one of the factors that affect the accuracy of the sound being converted into digital signals. Another major factor is the sampling rate. In Nyquist (Nyquist) theory, aliasing can be avoided as long as the Nyquist frequency of discrete systems is higher than the maximum frequency or bandwidth of the sampled signals.
Four, ALSA Foundation
Alsa is made up of sound card drivers for many sound cards, and it also provides an API library called Libasound . Application developers should use Libasound instead of ALSA interfaces in the kernel. Because Libasound provides the most advanced and programming interfaces that are easy to program. and provides a device logic naming function, so that developers do not even need to know similar device files such as low-level interfaces. Instead, theoss/free driver is programmed at the kernel system call level , which requires the developer to provide the device file name and use Ioctrl to implement the appropriate functionality.
For backward compatibility, ALSA provides kernel modules to simulate OSS, so many of the previous applications developed on the OSS basis do not require any changes to run on the ALSA. In addition, the Libaoss library can simulate OSS, and it does not require kernel modules.
ALSA contains plug-in features that allow you to extend new sound card drivers, including virtual sound cards that are fully implemented with software. ALSA provides a set of command-line-based toolset, such as a mixer (mixer), an audio file player (Aplay), and a tool that controls specific properties of a particular sound card.
Five, ALSA architecture
The ALSA API can be decomposed into the following key interfaces:
1 Control Interface: Provides common functions for managing sound card registration and requesting available devices
2 PCM interface: an interface for managing digital audio playback (playback) and recording (capture). The following summary of this article is focused on this interface because it is the most commonly used interface for developing digital audio programs.
3 Raw MIDI Interface: Support MIDI (musical instrument Digital Interface), standard electronic musical instruments. These APIs provide access to the MIDI bus on the sound card. The original interface is based on MIDI event work, and the programmer is responsible for managing protocols and time processing.
4 Timer (Timer) interface: Provides access to the time processing hardware on the sound card for synchronous audio events.
5 timer (Sequencer) interface
6 Mixer (Mixer) interface
VI, equipment naming
The API library uses logical device names instead of device files. The device name can be a real hardware name or a plugin name. The hardware name uses a format such as Hw:i,j. Where I is the card number, J is the device number on this sound card.
The first sound device is hw:0,0. This alias refers to the first sound device by default and is used in the example in this article.
Plug-ins use another unique name, such as PLUGHW: A plug-in, which does not provide access to hardware devices, but provides software features such as sample rate conversions, which are not supported by the hardware itself.
Seven, sound caching and data transfer
Each sound card has a hardware buffer to hold the recorded sample. When the buffer is full enough, the sound card will cause an interruption. The kernel sound card driver then uses a direct memory (DMA) Access channel to transfer the sample to an in-memory application cache. Similarly, for playback, any application uses DMA to transfer its cache data to the hardware cache of the sound card.
This hardware buffer is the ring cache. That is, when the data reaches the end of the buffer, it returns to the starting position of the buffer. ALSA maintains a pointer to the hardware cache and the current location of the data operations in the application buffer. From the outside of the kernel, we are only interested in the application's cache, so this article only discusses the application cache.
The size of the application cache can be controlled by Alsa library function calls. The buffer can be large , and a single transfer operation may result in unacceptable latency , which we call a delay (latency). To solve this problem, alsa the buffer into a series of cycles (period) (Oss/free called Fragment fragments). ALSA transmits data using period as a unit.
One cycle (period) stores some frames (frames). Each frame contains a sample that is crawled at a point in time. For stereo devices, a frame contains a sample of two channels. Decomposition process: A buffer is decomposed into cycles, then frames, then samples. The left and right channel information is alternately stored in a frame. This is known as interleaving (interleaved) mode. In a non-interleaved mode, all sample data for one channel is stored after data from another channel.
Viii. over and Under Run
When a sound card is active, the data is always transmitted continuously between the hardware buffer and the application cache . But there are exceptions. In the recording example, if the application reads data quickly enough, the recycle buffer will be overwritten by the new data . The loss of this data is called over run. In the playback example, if the application writes data to the cache quickly enough, the buffer will "starve to death". Such a mistake is called "under Run". In ALSA documents, these two situations are sometimes collectively referred to as "Xrun". Properly designed applications can minimize xrun and recover from them.
Nine, a typical sound program
Programs that use PCM are typically similar to the following pseudocode:
Turn on playback or recording interface
Set hardware parameters (access mode, data format, channel number, sampling rate, etc.) while there is data to be processed:
Read PCM data (recording)
or write PCM data (playback)
Close interface
The list of all instances associated with this article can be obtained from ftp: ftp.ssc.com/pub/lj/listings/issue126/6735.tgz.
Listing 1. Display Some PCM Types and Formats
[HTML] View Plain copy print? #include <alsa/asoundlib.h> int main () {int val; printf ("Alsa Library version:%s\n", snd_lib_version_str); printf ( "\NPCM stream types:\n"); for (val = 0; Val <= snd_pcm_stream_last val++) printf ("%s\n", Snd_pcm_stream_name ((snd_pcm_stream_t) val)); printf ("\NPCM access types:\n"); for (val = 0; Val <= snd_pcm_access_last; val++) {printf ("%s\n", Snd_pcm_access_name ((snd_pcm_access_t) val)); F ("\NPCM formats:\n"); for (val = 0; Val <= snd_pcm_format_last; val++) {if (Snd_pcm_format_name (snd_pcm_format_t) val)!= NULL) {printf ("%s") (%s) \ n ", Snd_pcm_format_name (snd_pcm_format_t) val), Snd_pcm_format_description ((snd_pcm_format_t) val)); } printf ("\NPCM subformats:\n"); for (val = 0; Val <= snd_pcm_subformat_last;val++) {printf (%s) \ n, Snd_pcm_subformat_name ((snd_pcm_subformat_t) val), Snd_pcm_subformat_description ((snd_pcm_subformat_t) val)); printf ("\NPCM states:\n"); for (val = 0; Val <= snd_pcm_state_last val++) printF ("%s\n", Snd_pcm_state_name ((snd_pcm_state_t) val)); return 0; }
#include <alsa/asoundlib.h> int main () {int val;
printf ("ALSA Library version:%s\n", SND_LIB_VERSION_STR);
printf ("\NPCM stream types:\n"); for (val = 0; Val <= snd_pcm_stream_last val++) printf ("%s\n", Snd_pcm_stream_name ((snd_pcm_stream_t) v
AL));
printf ("\NPCM access types:\n"); for (val = 0; Val <= snd_pcm_access_last val++) {printf ("%s\n", Snd_pcm_access_name (Snd_pcm_access_
T) Val));
printf ("\NPCM formats:\n");
for (val = 0; Val <= snd_pcm_format_last; val++) {if (Snd_pcm_format_name (snd_pcm_format_t) val)!= NULL) {
printf ("%s (%s) \ n", Snd_pcm_format_name (snd_pcm_format_t) val), Snd_pcm_format_description (
(snd_pcm_format_t) val));
} printf ("\NPCM subformats:\n");
for (val = 0; Val <= snd_pcm_subformat_last;val++) {printf (%s) \ n, Snd_pcm_subformat_name (
snd_pcm_subformat_t) Val), Snd_pcm_subformat_description ((snd_pcm_subformat_t) val));
printf ("\NPCM states:\n"); for (val = 0; Val <= snd_pcm_state_last val++) printf ("%s\n", Snd_pcm_state_name ((snd_pcm_state_t)
val));
return 0; }
Listing one shows the PCM data types and parameters used by some alsa. The first thing to do is to include the header file. These header files contain declarations for all library functions. One of these is to display the version of the ALSA library.
The remainder of this program iterations some PCM data types, starting with the stream type. ALSA provides a symbolic constant name for the last value of each iteration, and provides a function function to display a description string for a particular value. As you will see, ALSA supports many formats, and in my 1.0.15 version, it supports up to 36 formats.
This program must be linked to the Alsalib library by adding the-lasound option at compile time. Some ALSA library functions use the Dlopen function as well as floating-point operations, so you might also need to add the-LDL,-LM option.
Compiling: Gcc-o main test.c-lasound
The following is the program's makefile:
Listing 2. Opening PCM Device and Setting Parameters
[HTML] View Plain copy print? /* This example opens the default PCM device, sets some parameters, and then displays the value of most of the hardware PA Rameters. It does not perform any sound playback or recording. *//* Use the newer ALSA API/#define ALSA_PCM_NEW_HW_PARAMS_API/* All of the ALSA library APIs is defined * into this hea Der/#include <alsa/asoundlib.h> int main () {int rc; snd_pcm_t *handle; snd_pcm_hw_params_t *params; unsigned int Val, Val2; int dir; snd_pcm_uframes_t frames; /* Open PCM device for playback. * rc = Snd_pcm_open (&handle, "Default", Snd_pcm_stream_playback, 0); if (RC < 0) {fprintf (stderr, "Unable to open PCM device:%s\n", Snd_strerror (RC)); exit (1);}/* Allocate a hardware P Arameters object. * * SND_PCM_HW_PARAMS_ALLOCA (¶MS); /* Fill it in with default values. * * Snd_pcm_hw_params_any (handle, params); /* Set the desired hardware parameters. */////* Interleaved mode/snd_pcm_hw_params_set_access (handle, params, snd_pcm_access_rw_interleaved); /* Signed 16-bit Little-endian format * * * SND_PCM_HW_PARAMS_SET_FORMAT (handle, params, snd_pcm_format_s16_le); /* Two channels (stereo) * * Snd_pcm_hw_params_set_channels (handle, params, 2); /* 44100 Bits/second sampling rate (CD quality)/val = 44100; Snd_pcm_hw_params_set_rate_near (handle, params, &val, &dir); * Write the parameters to the driver/rc = Snd_pcm_hw_params (handle, params); if (RC < 0) {fprintf (stderr, "Unable to set HW parameters:%s\n", Snd_strerror (RC)); exit (1);}/* Display information About the PCM interface */printf ("PCM Handle name = '%s '), Snd_pcm_name (handle)"); printf ("PCM state =%s\n", Snd_pcm_state_name (Snd_pcm_state (handle)); Snd_pcm_hw_params_get_access (params, (snd_pcm_access_t *) &val); printf ("Access type =%s\n", Snd_pcm_access_name ((snd_pcm_access_t) val)); Snd_pcm_hw_params_get_format (params, &val); printf ("format = '%s ' (%s) \ n", Snd_pcm_format_name (snd_pcm_format_t) val), Snd_pcm_format_description (snd_pcm_ format_t) Val)); Snd_pcm_hw_params_get_subformat (params, (snd_pcm_subformat_t *) &val); printf (' Subformat = '%s ' (%s) \ n ', Snd_pcm_subformat_name ((snd_pcm_subformat_t) val), Snd_pcm_subformat_description ( snd_pcm_subformat_t)); Snd_pcm_hw_params_get_channels (params, &val); printf ("channels =%d\n", Val); Snd_pcm_hw_params_get_rate (params, &val, &dir); printf ("rate =%d bps\n", Val); Snd_pcm_hw_params_get_period_time (params, &val, &dir); printf ("Period time =%d us\n", Val); Snd_pcm_hw_params_get_period_size (params, &frames, &dir); printf ("Period size =%d frames\n", (int) frames); Snd_pcm_hw_params_get_buffer_time (params, &val, &dir); printf ("Buffer time =%d us\n", Val); Snd_pcm_hw_params_get_buffer_size (params, (snd_pcm_uframes_t *) &val); printf ("Buffer size =%d frames\n", Val); Snd_pcm_hw_params_get_periods (params, &val, &dir); printf ("periods per buffer =%d frames\n", Val); Snd_pcm_hw_params_get_rate_numden (params, &val, &val2); PriNTF ("exact rate =%d/%d bps\n", Val, Val2); val = snd_pcm_hw_params_get_sbits (params); printf ("significant bits =%d\n", Val); Snd_pcm_hw_params_get_tick_time (params, &val, &dir); printf ("Tick time =%d us\n", Val); val = Snd_pcm_hw_params_is_batch (params); printf ("is batch =%d\n", Val); val = Snd_pcm_hw_params_is_block_transfer (params); printf ("are block transfer =%d\n", Val); val = snd_pcm_hw_params_is_double (params); printf ("is double =%d\n", Val); val = Snd_pcm_hw_params_is_half_duplex (params); printf ("is half duplex =%d\n", Val); val = Snd_pcm_hw_params_is_joint_duplex (params); printf ("is joint duplex =%d\n", Val); val = Snd_pcm_hw_params_can_overrange (params); printf ("Can overrange =%d\n", Val); val = snd_pcm_hw_params_can_mmap_sample_resolution (params); printf ("Can mmap =%d\n", Val); val = snd_pcm_hw_params_can_pause (params); printf ("Can pause =%d\n", Val); val = Snd_pcm_hw_params_can_resume (params); printf ("Can resume =%d\n", Val); val = Snd_pcm_hw_params_can_syNc_start (params); printf ("can sync start =%d\n", Val); Snd_pcm_close (handle); return 0; }
/* This example opens the default PCM device, sets some parameters, and then displays the value of most of the hardware P Arameters.
It does not perform any sound playback or recording. */* Use the newer ALSA API/#define ALSA_PCM_NEW_HW_PARAMS_API/* All of the ALSA library APIs is defined * Eader */#include <alsa/asoundlib.h> int main () {int rc; snd_pcm_t *handle; snd_pcm_hw_params_t *params; unsigned
int Val, val2;
int dir;
snd_pcm_uframes_t frames; /* Open PCM device for playback.
* rc = Snd_pcm_open (&handle, "Default", Snd_pcm_stream_playback, 0);
if (RC < 0) {fprintf (stderr, "Unable to open PCM device:%s\n", Snd_strerror (RC));
Exit (1); }/* Allocate a hardware Parameters object.
* * SND_PCM_HW_PARAMS_ALLOCA (¶MS); /* Fill it in with default values.
* * Snd_pcm_hw_params_any (handle, params); /* Set the desired hardware parameters. *//////* Interleaved mode/snd_pcm_hw_params_set_access (handle, pArams, snd_pcm_access_rw_interleaved); /* Signed 16-bit Little-endian format * * Snd_pcm_hw_params_set_format (handle, params, snd_pc
M_format_s16_le);
/* Two channels (stereo) * * Snd_pcm_hw_params_set_channels (handle, params, 2);
/* 44100 Bits/second sampling rate (CD quality)/val = 44100;
Snd_pcm_hw_params_set_rate_near (handle, params, &val, &dir);
* Write the parameters to the driver/rc = Snd_pcm_hw_params (handle, params);
if (RC < 0) {fprintf (stderr, "Unable to set HW parameters:%s\n", Snd_strerror (RC));
Exit (1);
}/* Display information about the PCM interface/printf ("PCM Handle name = '%s ' \ n", Snd_pcm_name (handle));
printf ("PCM state =%s\n", Snd_pcm_state_name (Snd_pcm_state (handle));
Snd_pcm_hw_params_get_access (params, (snd_pcm_access_t *) &val); printf ("Access type =%s\n", snd_pcm_access_name ((snd_pcm_access_t) val));
Snd_pcm_hw_params_get_format (params, &val);
printf ("format = '%s ' (%s) \ n", Snd_pcm_format_name (snd_pcm_format_t) val), Snd_pcm_format_description (
(snd_pcm_format_t) val));
Snd_pcm_hw_params_get_subformat (params, (snd_pcm_subformat_t *) &val);
printf (' Subformat = '%s ' (%s) \ n ', Snd_pcm_subformat_name ((snd_pcm_subformat_t) val), Snd_pcm_subformat_description (
(snd_pcm_subformat_t) val));
Snd_pcm_hw_params_get_channels (params, &val);
printf ("channels =%d\n", Val);
Snd_pcm_hw_params_get_rate (params, &val, &dir);
printf ("rate =%d bps\n", Val);
Snd_pcm_hw_params_get_period_time (params, &val, &dir);
printf ("Period time =%d us\n", Val);
Snd_pcm_hw_params_get_period_size (params, &frames, &dir);
printf ("Period size =%d frames\n", (int) frames); Snd_Pcm_hw_params_get_buffer_time (params, &val, &dir);
printf ("Buffer time =%d us\n", Val);
Snd_pcm_hw_params_get_buffer_size (params, (snd_pcm_uframes_t *) &val);
printf ("Buffer size =%d frames\n", Val);
Snd_pcm_hw_params_get_periods (params, &val, &dir);
printf ("periods per buffer =%d frames\n", Val);
Snd_pcm_hw_params_get_rate_numden (params, &val, &val2);
printf ("Exact rate =%d/%d bps\n", Val, Val2);
val = snd_pcm_hw_params_get_sbits (params);
printf ("significant bits =%d\n", Val);
Snd_pcm_hw_params_get_tick_time (params, &val, &dir);
printf ("Tick time =%d us\n", Val);
val = Snd_pcm_hw_params_is_batch (params);
printf ("is batch =%d\n", Val);
val = Snd_pcm_hw_params_is_block_transfer (params);
printf ("are block transfer =%d\n", Val);
val = snd_pcm_hw_params_is_double (params);
printf ("is double =%d\n", Val); val = Snd_pcm_hw_params_is_half_duplex (params);
printf ("is half duplex =%d\n", Val);
val = Snd_pcm_hw_params_is_joint_duplex (params);
printf ("is joint duplex =%d\n", Val); val = snd_pcm_hw