贴个ALSA例程
ALSA Introduction
The Advanced Linux Sound Architecture (ALSA) provides audio and MIDI functionality to the Linux operating system. ALSA has the following significant features:
- Efficient support for all types of audio interfaces, from consumer sound cards to professional multichannel audio interfaces.
- Fully modularized sound drivers.
- SMP and thread-safe design.
- User space library (alsa-lib) to simplify application programming and provide higher level functionality.
- Support for the older Open Sound System (OSS) API, providing binary compatibility for most OSS programs.
ALSA Project: http://www.alsa-project.org/main/index.php/Main_Page
2.6以后内核都用的ALSA来管理音频设备,我们的Ubuntu也是,所以我们的项目也可以通过ALSA来收发声音,而不需要接触最底层的硬件驱动。
下面是我学习用的一个例程,后面用两个循环来合成了一些乱七八糟的声音,编译好之后运行程序,会从默认的外放设备放出声音来。
#include <alsa/asoundlib.h>
int
main()
{
/* Handle for the PCM device */
snd_pcm_t *pcm_handle;
/*Playback stream*/
snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
/***************
Thins structure sontaions informatin about
the hardware and can be used to specify the
configuration to be used for the PCM stream.
******************/
snd_pcm_hw_params_t *hwparams;
/* Name of the PCM device, like plughw:0,0
The first number is the number of the soundcard,
the second number is the number of the device.*/
char *pcm_name;
/*Init pcm_name. Of course, later you
will make this configurable ;-)*/
pcm_name = strdup("plughw:0,0");
/* Allocate the snd_pcm_hw_params_t structure on the stack*/
snd_pcm_params_alloca(&hwparams);
/*Open PCM. The last parameter of this function is the mode.
If this is set to 0, the standard mode is used. Possible
other values are SND_PCM_NONBLOCK and SND_PCM_ASYNC.
If SND_PCM_NONBLOCK is used, read/write access to the PCM device
will return immediately. If SND_PCM_ASYNC is specified, SIGIO
will be emitted whenever a period has been completely processed
by the soundcard.*/
if (snd_pcm_open(&pcm_handle, pcm_name, stream, 0) < 0 ){
fprintf(stderr, "Error opening PCM device %s\n", pcm_name);
return(-1);
}
/* For this example, we assume that the soundcard can be configured
for stereo playback of 16 Bit Little Endian data, sampled at
44100 Hz. Accordingly, we restrict the configuration space to
match this configuration */
int rate = 44100;
int exact_rate ;/*Sample rate returned by snd_pcm_hw_params_set_rate_near*/
int dir; /*exact_rate == rate, dir = 0
exact_rate < rate, dir = -1
exact_rate > rate, dir = 1*/
int periods = 2;/*Number of periods*/
snd_pcm_uframes_t periodsize = 8192; /*Periodsize (bytes)*/
/*Set acccess type. This can be either
SND_PCM_ACCESS_RW_INTERLEAVED or
SND_PCM_ACCESS_RW_NONINTERLAEAVED
There are also access types for MMAPed
access, but this is beyond the scope
of this introduction.*/
if (snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
fprintf(stderr, "Error setting access.\n");
return(-1);
}
/*Set sample format */
if (snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE) <0){
fprintf(stderr, "Error setting format. \n");
return(-1);
}
/* Set sample rate. If the exact rate is not supported
by the hardware, use nearest possible rate.*/
exact_rate = rate;
if (snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &exact_rate, 0) < 0){
fprintf(stderr, "Error setting rate. \n");
return(-1);
}
if (rate != exact_rate){
fprintf(stderr, "The rate %d Hz is not supported by your hardware. \n
==> Using %d Hz instead. \n", rate, exact_rate);
}
/*Set number of channels*/
if (snd_pcm_hw_params_set_channels(pcm_handle, hwparams, 2) < 0) {
fprintf(stderr, "Error setting channels \n");
return(-1);
}
/*Set number of periods. Periods used to be called fragments*/
if ( snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, periods, 0) < 0) {
fprintf(stderr, "Error setting periods. \n");
return(-1);
}
/* Set buffer size (in frames). buffersize = periodsize * periods * framesize = bytes
The resulting latency is given by latency = periodsize * periods / (rate * bytes_per_frame)*/
if (snd_pcm_hw_params_set_buffer_size(pcm_handle, hwparams, (periodsize * periods)>>2) < 0) {
fprintf(stderr, "Error setting buffersize. \n");
return(-1);
}
/* Apply HW parameter settings to
PCM device and prepare device*/
if (snd_pcm_hw_params(pcm_handle, hwparams) < 0){
fprintf(stderr, "Error setting HW params. \n");
return(-1);
}
/* Write num_frames frames from buffer data to
the PCM device pointed to by pcm_handle.*/
// The next line is for interleaved mode
//snd_pcm_sframes_t snd_pcm_writei(pcm_handle, data, num_frames);
/* Write num_frames frames from buffer data to
the PCM device pointed to by pcm_handle.
Returns the number of frames actually written.*/
//The next line is for noninterleaved only
//snd_pcm_sframes_t snd_pcm_writen(pcm_handle, data, num_frames);
//-----------Playback start here------------------------------
/* We have to make sure that our application sends enough data
to the soundcard buffer. Otherwise, a buffer underrun will
occur. After such an underrun has occured, snd_pcm_prepare
should be called.*/
unsigned char *data;
int pcmreturn, l1, l2;
short s1, s2; //The PCM data
int num_frames;
data = (unsigned char *)malloc(periodsize);
num_frames = periodsize >>2; //num_frames = periodsize / 4 = 2048
for(l1 = 0; l1 < 100; l1++){ //creates PCM data for 100 cycles
for(l2 = 0; l2 < num_frames; l2++) {
s1 = (l2 % 128) * 100 - 5000; //creates -5000 < s1 < 7800
s2 = (l2 % 256) * 100 - 5000; //creates -5000 < s2 < 20600
data[4*12] = (unsigned char)s1; //data format ::= F1(s1[0..7]s1[8..15]s2[1..7]s2[8..15])F2(...)...F2048(...)
data[4*12+1] = s1 >> 8;
data[4*12+2] = (unsigned char)s2;
data[4*12+3] = s2 >> 8;
}
while ((pcmreturn = snd_pcm_writei(pcm_handle, data, num_frames)) < 0){
snd_pcm_prepare(pcm_handle);
fprintf(stderr, "<<<<<<<<<Buffer Underrun >>>>>>>>>>");
}
}
int
main()
{
/* Handle for the PCM device */
snd_pcm_t *pcm_handle;
/*Playback stream*/
snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
/***************
Thins structure sontaions informatin about
the hardware and can be used to specify the
configuration to be used for the PCM stream.
******************/
snd_pcm_hw_params_t *hwparams;
/* Name of the PCM device, like plughw:0,0
The first number is the number of the soundcard,
the second number is the number of the device.*/
char *pcm_name;
/*Init pcm_name. Of course, later you
will make this configurable ;-)*/
pcm_name = strdup("plughw:0,0");
/* Allocate the snd_pcm_hw_params_t structure on the stack*/
snd_pcm_params_alloca(&hwparams);
/*Open PCM. The last parameter of this function is the mode.
If this is set to 0, the standard mode is used. Possible
other values are SND_PCM_NONBLOCK and SND_PCM_ASYNC.
If SND_PCM_NONBLOCK is used, read/write access to the PCM device
will return immediately. If SND_PCM_ASYNC is specified, SIGIO
will be emitted whenever a period has been completely processed
by the soundcard.*/
if (snd_pcm_open(&pcm_handle, pcm_name, stream, 0) < 0 ){
fprintf(stderr, "Error opening PCM device %s\n", pcm_name);
return(-1);
}
/* For this example, we assume that the soundcard can be configured
for stereo playback of 16 Bit Little Endian data, sampled at
44100 Hz. Accordingly, we restrict the configuration space to
match this configuration */
int rate = 44100;
int exact_rate ;/*Sample rate returned by snd_pcm_hw_params_set_rate_near*/
int dir; /*exact_rate == rate, dir = 0
exact_rate < rate, dir = -1
exact_rate > rate, dir = 1*/
int periods = 2;/*Number of periods*/
snd_pcm_uframes_t periodsize = 8192; /*Periodsize (bytes)*/
/*Set acccess type. This can be either
SND_PCM_ACCESS_RW_INTERLEAVED or
SND_PCM_ACCESS_RW_NONINTERLAEAVED
There are also access types for MMAPed
access, but this is beyond the scope
of this introduction.*/
if (snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
fprintf(stderr, "Error setting access.\n");
return(-1);
}
/*Set sample format */
if (snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE) <0){
fprintf(stderr, "Error setting format. \n");
return(-1);
}
/* Set sample rate. If the exact rate is not supported
by the hardware, use nearest possible rate.*/
exact_rate = rate;
if (snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &exact_rate, 0) < 0){
fprintf(stderr, "Error setting rate. \n");
return(-1);
}
if (rate != exact_rate){
fprintf(stderr, "The rate %d Hz is not supported by your hardware. \n
==> Using %d Hz instead. \n", rate, exact_rate);
}
/*Set number of channels*/
if (snd_pcm_hw_params_set_channels(pcm_handle, hwparams, 2) < 0) {
fprintf(stderr, "Error setting channels \n");
return(-1);
}
/*Set number of periods. Periods used to be called fragments*/
if ( snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, periods, 0) < 0) {
fprintf(stderr, "Error setting periods. \n");
return(-1);
}
/* Set buffer size (in frames). buffersize = periodsize * periods * framesize = bytes
The resulting latency is given by latency = periodsize * periods / (rate * bytes_per_frame)*/
if (snd_pcm_hw_params_set_buffer_size(pcm_handle, hwparams, (periodsize * periods)>>2) < 0) {
fprintf(stderr, "Error setting buffersize. \n");
return(-1);
}
/* Apply HW parameter settings to
PCM device and prepare device*/
if (snd_pcm_hw_params(pcm_handle, hwparams) < 0){
fprintf(stderr, "Error setting HW params. \n");
return(-1);
}
/* Write num_frames frames from buffer data to
the PCM device pointed to by pcm_handle.*/
// The next line is for interleaved mode
//snd_pcm_sframes_t snd_pcm_writei(pcm_handle, data, num_frames);
/* Write num_frames frames from buffer data to
the PCM device pointed to by pcm_handle.
Returns the number of frames actually written.*/
//The next line is for noninterleaved only
//snd_pcm_sframes_t snd_pcm_writen(pcm_handle, data, num_frames);
//-----------Playback start here------------------------------
/* We have to make sure that our application sends enough data
to the soundcard buffer. Otherwise, a buffer underrun will
occur. After such an underrun has occured, snd_pcm_prepare
should be called.*/
unsigned char *data;
int pcmreturn, l1, l2;
short s1, s2; //The PCM data
int num_frames;
data = (unsigned char *)malloc(periodsize);
num_frames = periodsize >>2; //num_frames = periodsize / 4 = 2048
for(l1 = 0; l1 < 100; l1++){ //creates PCM data for 100 cycles
for(l2 = 0; l2 < num_frames; l2++) {
s1 = (l2 % 128) * 100 - 5000; //creates -5000 < s1 < 7800
s2 = (l2 % 256) * 100 - 5000; //creates -5000 < s2 < 20600
data[4*12] = (unsigned char)s1; //data format ::= F1(s1[0..7]s1[8..15]s2[1..7]s2[8..15])F2(...)...F2048(...)
data[4*12+1] = s1 >> 8;
data[4*12+2] = (unsigned char)s2;
data[4*12+3] = s2 >> 8;
}
while ((pcmreturn = snd_pcm_writei(pcm_handle, data, num_frames)) < 0){
snd_pcm_prepare(pcm_handle);
fprintf(stderr, "<<<<<<<<<Buffer Underrun >>>>>>>>>>");
}
}
关于PCM 的period,frame,fragment等问题的ALSA文档。
http://www.alsa-project.org/main/index.php/FramesPeriods
ubuntu下编译需先安装alsa开发包:sudo apt-get install libasound2-dev
gcc编译指令:gcc -o play play.c -lasound
——————无论在哪里做什么,只要坚持服务、创新、创造价值,其他的东西自然都会来的。