?? alsa090_pcm_seq_howto.txt
字號(hào):
/* alsa090_pcm_seq_howto.txt 0.0.4 by Matthias Nagorni */CONTENTS: --------1. Introduction2. Basic PCM audio3. PCM capture 4. Writing a sequencer client5. A MIDI router6. Combining PCM and MIDI: miniFMsynth7. Scheduling MIDI events: miniArp8. Notes on writing a GUI based audio applicationAcknowledgement---------------The author is grateful to Takashi Iwai for his assistance in writingthis howto.1. Introduction---------------This howto intends to give a brief introduction in writing simple audioapplications with ALSA 0.9.0. Section 2. explains the most basic functionsfor PCM audio. If you remove the explaining text, you end up with a minimalPCM playback program. Section 3. briefly treats some functions for PCMcapture. In section 4. you will learn how to write a simple client for theALSA sequencer. The section is based on the example seqdemo.c, a program,which can receive MIDI events and displays the most important event types.Section 5. demonstrates how the ALSA MIDI sequencer can be used to routeMIDI events from one input port to several output ports. This section isbased on the example midiroute.c. Section 6. combines PCM playback and MIDIinput and explains the mini synthesizer miniFMsynth.c. This sectionintroduces callback-based audio playback, as proposed by Paul Davis on the linux-audio-dev mailinglist. It is recommended to also have a look at the doxygen-generated function reference of the ALSA library.Compiling an ALSA application: Just use -lasound and make sure you have included #include <alsa/asoundlib.h>2. Basic PCM audio ------------------To write a simple PCM application for ALSA 0.9.0 we first need a handle forthe PCM device. Then we have to specify the direction of the PCM stream,which can be either playback or capture. We also have to provide someinformation about the configuration we would like to use, like buffer size, sample rate, pcm data format. So, first we declare: /* Handle for the PCM device */ snd_pcm_t *pcm_handle; /* Playback stream */ snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK; /* This structure contains information about */ /* the hardware and can be used to specify the */ /* configuration to be used for the PCM stream. */ snd_pcm_hw_params_t *hwparams;The most important ALSA interfaces to the PCM devices are the "plughw" and the"hw" interface. If you use the "plughw" interface, you need not care muchabout the sound hardware. If your soundcard does not support the sample rate or sample format you specify, your data will be automatically converted.This also applies to the access type and the number of channels. With the "hw" interface, you have to check whether your hardware supportsthe configuration you would like to use. /* 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;Then we initialize the variables and allocate a hwparams structure: /* 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_hw_params_alloca(&hwparams);Now we can open the PCM device: /* 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); }Before we can write PCM data to the soundcard, we have to specify accesstype, sample format, sample rate, number of channels, number of periods and period size. First, we initialize the hwparams structure with the full configuration space of the soundcard. /* Init hwparams with full configuration space */ if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) { fprintf(stderr, "Can not configure this PCM device.\n"); return(-1); }Information about possible configurations can be obtained with a set offunctions named snd_pcm_hw_params_can_<capability> snd_pcm_hw_params_is_<property> snd_pcm_hw_params_get_<parameter>The availability of the most important parameters, namely access type, buffer size, number of channels, sample format, sample rate, and number of periods, can be tested with a set of functions named snd_pcm_hw_params_test_<parameter> These query functions are especially important, if the "hw" interface isused. The configuration space can be restricted to a certain configuration with a set of functions named snd_pcm_hw_params_set_<parameter>For this example, we assume that the soundcard can be configured forstereo playback of 16 Bit Little Endian data, sampled at 44100 Hz.Accordingly, we restrict the configurations space to match thisconfiguration: int rate = 44100; /* Sample rate */ int periods = 2; /* Number of periods */ int periodsize = 8192; /* Periodsize (bytes) */The access type specifies the way, multichannel data is stored in thebuffer. For INTERLEAVED access, each frame in the buffer contains theconsecutive sample data for the channels. For 16 Bit stereo data, thismeans that the buffer contains alternating words of sample data for the leftand right channel. For NONINTERLEAVED access, each period contains first allsample data for the first channel followed by the sample data for the secondchannel and so on. /* Set access type. This can be either */ /* SND_PCM_ACCESS_RW_INTERLEAVED or */ /* SND_PCM_ACCESS_RW_NONINTERLEAVED. */ /* 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. */ if (snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, rate, 0) < 0) { fprintf(stderr, "Error setting rate.\n"); return(-1); } /* 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(pcm_handle, hwparams, periods, 0) < 0) { fprintf(stderr, "Error setting periods.\n"); return(-1); }The unit of the buffersize depends on the function. Sometimes it is given inbytes, sometimes the number of frames have to be specified. One frame is thesample data vector for all channels. For 16 Bit stereo data, one frame has alength of four bytes. /* Set buffer size (in frames). 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); }Now we apply the configuration to the PCM device pointed to by pcm_handle.This will also prepare the PCM device. /* 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); }After the PCM device is configured, we can start writing PCM data to it.The first write access will start the PCM playback. For interleaved write access, we use the function /* Write num_frames frames from buffer data to */ /* the PCM device pointed to by pcm_handle. */ /* Returns the number of frames actually written. */ snd_pcm_sframes_t snd_pcm_writei(pcm_handle, data, num_frames);For noninterleaved access, we would have to use the function /* Write num_frames frames from buffer data to */ /* the PCM device pointed to by pcm_handle. */ /* Returns the number of frames actually written. */ snd_pcm_sframes_t snd_pcm_writen(pcm_handle, data, num_frames);After the PCM playback is started, we have to make sure, that ourapplication sends enough data to the soundcard buffer. Otherwise, abuffer underrun will occur. After such an underrun has occured,snd_pcm_prepare should be called. A simple stereo saw wave could begenerated this way: unsigned char *data; int pcmreturn, l1, l2; short s1, s2; int frames; data = (unsigned char *)malloc(periodsize); frames = periodsize >> 2; for(l1 = 0; l1 < 100; l1++) { for(l2 = 0; l2 < num_frames; l2++) { s1 = (l2 % 128) * 100 - 5000; s2 = (l2 % 256) * 100 - 5000; data[4*l2] = (unsigned char)s1; data[4*l2+1] = s1 >> 8; data[4*l2+2] = (unsigned char)s2; data[4*l2+3] = s2 >> 8; } while ((pcmreturn = snd_pcm_writei(pcm_handle, data, frames)) < 0) { snd_pcm_prepare(pcm_handle); fprintf(stderr, "<<<<<<<<<<<<<<< Buffer Underrun >>>>>>>>>>>>>>>\n"); } }If we want to stop playback, we can either use snd_pcm_drop orsnd_pcm_drain. The first function will immediately stop the playback anddrop pending frames. The latter function will stop after pending frames havebeen played. /* Stop PCM device and drop pending frames */ snd_pcm_drop(pcm_handle); /* Stop PCM device after pending frames have been played */ snd_pcm_drain(pcm_handle);3. PCM capture --------------It is not possible to use one pcm handle for both playback and capture. Soyou have to configure two handles if you want to access the PCM device inboth directions. The snd_pcm_open function now has to be called with stream set to SND_PCM_STREAM_CAPTURE. /* Capture stream */ snd_pcm_stream_t stream_capture = SND_PCM_STREAM_CAPTURE;The other settings are identical to the playback settings.For interleaved capture, we call /* Read num_frames frames from the PCM device */ /* pointed to by pcm_handle to buffer capdata. */ /* Returns the number of frames actually read. */ snd_pcm_readi(pcm_capture_handle, capdata, num_frames);For noninterleaved access, we would have to use the function /* Read num_frames frames from the PCM device */ /* pointed to by pcm_handle to buffer capdata. */ /* Returns the number of frames actually read. */ snd_pcm_readn(pcm_capture_handle, capdata, num_frames);As in the case of playback, we have to take care that the application callsthe read function before the capture buffer of the soundcard is completely filled.Otherwise there will be a buffer overrun. int pcmreturn;
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -