⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 alsa090_pcm_seq_howto.txt

📁 alsa sound devlopment
💻 TXT
📖 第 1 页 / 共 2 页
字号:
  while ((pcmreturn = snd_pcm_readi(pcm_capture_handle, capdata, periodsize>>2)) < 0) {    snd_pcm_prepare(pcm_capture_handle);    fprintf(stderr, "<<<<<<<<<<<<<<< Buffer Overrun >>>>>>>>>>>>>>>\n");  }4. Writing a sequencer client-----------------------------The example seqdemo.c demonstrates how to create a simple client for theALSA sequencer. This client can receive MIDI data from other clients and itdisplays NOTE ON/OFF, 7-bit CONTROLLER and PITCHBENDER events. Let's have alook at the functions of seqdemo.c:a) snd_seq_t *open_seq() This function uses snd_seq_open to create a new client for the ALSAsequencer. Since we only want to receive MIDI events, the parameterstreams is set to SND_SEQ_OPEN_INPUT. The client is given a name using snd_seq_set_client_name. Now we need a port which can receive MIDIevents. We create it by calling snd_seq_create_simple_port. Here, we have to specify the capabilities of the port. SND_SEQ_PORT_CAP_WRITE allows writeaccess and SND_SEQ_PORT_CAP_SUBS_WRITE allows other clients to subscribe to the port as writeable port. Since our application reads from the port, wewould have to add SND_SEQ_PORT_CAP_READ, but at the moment, this can beomitted since it is not checked. b) void midi_action(snd_seq_t *seq_handle)This function retrieves an event from the input buffer of the ALSA sequencerby calling snd_seq_event_input. The event is processed and then freed usingsnd_seq_free_event. The loop is repeated until the input buffer isempty. This is checked with snd_seq_input_pending, which returns the bytesize of the events remaining in the input buffer.c) int main(int argc, char *argv[])seqdemo.c uses polling to check whether there are incoming MIDI events.snd_seq_poll_descriptors_count returns the number of poll descriptors forinput polling events (parameter POLLIN). At present, this is always 1. Thena struct pollfd is allocated on the stack and initialized usingsnd_seq_poll_descriptors. The call to poll returns either, if a MIDI eventcan be read from the ALSA sequencer or after timeout. You can check themanpage of poll, if you are not familiar with polling.5. A MIDI router----------------ALSA makes it very easy to implement any MIDI router you can think of.midiroute.c provides a simple example. It creates one input port and twoouput ports. The program takes the parameter split_point. Input note events with note < split_point are routed to the first output port, all other note events are routed to the second output port. Any other events(e.g. control or pitchbender events) are routed to both output portssimultaneously. Of course you can easily modify this in various ways by only changing the if conditions in the function midi_route. You can e.g. have different instrument played depending on the velocity. Let's see how it works: a) int open_seq(snd_seq_t **seq_handle, int in_ports[], int out_ports[], int                num_in, int num_out)Here, we open the ALSA sequencer with SND_SEQ_OPEN_DUPLEX, since we wouldlike to read and write MIDI events. Since we need the port IDs todistinguish between the different output ports, we have to store the returnvalue of snd_seq_create_simple_port. In addition to a) in the previoussection, we also create output ports. To let other clients read from them,we have to allow read access and read subscription. It is not necessary toexplicitely allow write access as well, although th application itself writes to the output port. At present, a client can always access its ports withoutexplicit permission. b) void midi_route(snd_seq_t *seq_handle, int out_ports[], int pick_channel)As in 4. b) snd_seq_event_input is used to read single events from the inputbuffer. Now we would like to route the event to one of the output ports. Since it is more flexible to allow arbitrary clients to subscribe to theoutput ports and receive the events, we do not explicitely specify thedestination with snd_seq_ev_set_dest. Instead, we call snd_seq_ev_set_sourceto make the event originate from the desired output port. The functionsnd_seq_ev_set_subs will then set all subscribers to the ouput port asdestination. We would like to output the event as quickly as possible.Therefore, we call snd_seq_ev_set_direct to bypass event queueing (this setsthe queue for the event to SND_SEQ_QUEUE_DIRECT).Then we call snd_seq_event_output_direct for unbuffered output of the event.By modifying the arguments of the if conditions, you can easily implementdifferent routing behaviour. Examples: if (ev->data.control.channel < split_channel) : channel splitting          if (ev->data.note.velocity < threshold)     : velocity dependence          if (ev->type == SND_SEQ_EVENT_CONTROLLER) : extract controller eventsc) int main(int argc, char *argv[])Well, this is not essentially different from 4. c).6. Combining PCM and MIDI: miniFMsynth--------------------------------------The tiny synthesizer miniFMsynth demonstrates MIDI event processing and PCMplayback. It takes several parameters:miniFMsynth <device> <FM> <harmonic> <subharmonic> <transpose> <a> <d> <s> <r> <device>        : PCM device<FM>            : Strength of the frequency modulation<harmonic>      : Harmonic of the master oscillator (integer)<subharmonic>   : Subharmonic of the master oscillator (integer)<transpose>     : Note offset for both oscillators (integer)<a> <d> <s> <r> : Attack, Decay, Sustain, ReleaseHarpsichord: ./miniFMsynth plughw:0 6.5 3 5 24 0.01 0.2 0.3 0.1Bell       : ./miniFMsynth plughw:0 3.5 7 9 0 0.01 0.2 0.3 1.5Oboe       : ./miniFMsynth plughw:0 0.7 1 3 24 0.05 0.3 0.8 0.2miniFMsynth reacts on pitchbender and modulation wheel events. Since it isnot optimized with respect to performance (you would e.g. never call theexpensive sin function in a "real" program), you might have to decrease thepolyphony in the source by modifying the #define POLY.Having read the previous sections, it should be easy to understand the MIDIpart of the program. As to PCM playback, miniFMsynth uses polling for thispurpose as well.  This technique is more advanced than the direct outputdescribed in section 2. Let's have a look at some interesting details:a) snd_pcm_t *open_pcm(char *pcm_name)Most of the snd_pcm_hw_params functions called here are already known fromsection 2. However, here we do not specify the buffersize, but we set theperiodsize instead. Since we would like to use polling for PCM output, we also have to set the software parameter avail_min. A poll on a PCM file descriptor will then onlyreturn, if at least avail_min frames can be written to the device. Weinitialize a snd_pcm_sw_params_t with the current configuration callingsnd_pcm_sw_params_current, then set avail_min with snd_pcm_sw_params_set_avail_min and activate this configuration by calling snd_pcm_sw_params.b) double envelope(int *note_active, int gate, double *env_level, double t,                   double attack, double decay, double sustain, double release)If gate==1, the note is still pressed ==> the envelope is in the attack, decay,sustain area. If gate==0, the envelope is in the release area. If t>release,the respective oscillator cell is freed by setting note_active to zero. c) int midi_callback()Most of this is already known from section 4. When a NOTEON event isreceived, a new oscillator cell is initialized by setting the respective note_active to 1. This only works, if the polyphony is not exceeded,otherwise the note is omitted. If you don't like this, you can modify itand let e.g. the oscillator cell with the lowest env_level be freed andused for the new note.There is one important detail about note events: Some keyboards (e.g. Kawai MP 9000) do not send NOTEOFF events, but send a NOTEON with velocity 0 instead. In this case, you have to add the linesif ((ev->type == SND_SEQ_EVENT_NOTEON) && (ev->data.note.velocity == 0))     ev->type = SND_SEQ_EVENT_NOTEOFF;directly after snd_seq_event_input.d) int playback_callback (snd_pcm_sframes_t nframes)This processes the PCM data and sends it to the device by callingsnd_pcm_writei. This call will immediately return, since we only call playback_callback, if we know that at least nframes can be written to thePCM device. miniFMsynth always writes chunks of fixed size, so nframesalways equals BUFSIZE.e) int main (int argc, char *argv[])Here, we use polling both for MIDI event input and for PCM playback.The call to poll will return either if a MIDI event can be read from thesequencer input buffer or if at least avail_min (== BUFSIZE) frames can bewritten to the PCM device. If the PCM data is not delivered to the PCMdevice before the buffer of the soundcard is empty, a buffer underrunoccurs. This will cause the playback to stop. In this case, the return valueof snd_pcm_writei will be smaller than BUFSIZE. We then have to restart theplayback by calling snd_pcm_prepare. 7. Scheduling MIDI events: miniArp----------------------------------In section 5. we have used immediate output of MIDI events. However, MIDIevents are usually scheduled on a queue which is driven by a hardware timer.The arpeggiator miniArp provides a simple example for MIDI scheduling.miniArp takes two parameters, the tempo (beats per minute) and the filenameof the sequence to be looped. This sequence file has to be made up of onesingle line of single characters. Each note is characterized by 4 or 5characters vwxyz, where v is the note ('c', 'd', 'e', 'f', 'g', 'a', 'h'), w is an optional sharp ('#'), x specifies the octave, y and z denote the length of the note and the interval between this note and the following note. Both values are given as a fraction of a quarter note: length = 1 / y quarters.A valid sequence would be:c488c#488d488d#488e484c584g584c684e684g584c684e544c584g484e488d#488d488c#488miniArp processes pitchbender and modulation wheel events. Just try it out.Any NOTEON event will transpose the sequence according to the difference ofthe pressed note and the middle C.Now, what are the secrets of this program ? Many techniques are alreadyknown from other sections. The interesting functions are init_queue, set_tempo, arpeggio, clear_queue, and get_tick. A signal handler for SIGINTand SIGTERM is implemented to avoid persisting notes.a) void init_queue()This allocates a queue and sets its buffer size. The buffer size is passed to snd_seq_set_client_pool_output as the number of events. While gainingexperience with queues it might be helpful to have a look at /proc/asound/seq/queues.b) void set_tempo()The schedule time of events can be given either in realtime or in ticks. Ifyou would like to vary the tempo, it's convenient to specify the timein ticks. This will allow to change the tempo even of events, which already have been scheduled on the queue. The tempo is passed tosnd_seq_queue_tempo_set_tempo in microseconds per tick. The functionsnd_seq_queue_tempo_set_ppq sets the ticks per quarter. Usually, one wouldlike to specify the tempo in beats per minute (bpm). Therefore set_tempo calculates the correct tempo and ppq parameters from bpm.c) void arpeggio()Here, all the notes of the sequence are scheduled on the queue. First, wehave to clear the event structure. Then, we define a note event of fixedlength by calling snd_seq_ev_set_note. This note event is scheduled by  specifying its tick time with snd_seq_ev_schedule_tick. The source of theevent is the output port of miniArp and we call snd_seq_set_subs to tellALSA that we would like the event to be passed to all subscribers of thisport. Finally, we use snd_seq_event_output_direct for unbuffered output ofthe event to the queue. Note events work as follows: At their scheduled time, the create a NOTEONevent and then convert themselves to a NOTEOFF event which is scheduled at the current time plus note length.Since we would like the sequence to be looped, we schedule aSND_SEQ_EVENT_ECHO event after each sequence. This time, the destinationis the application itself. When the SND_SEQ_EVENT_ECHO event is dispatched, it will cause the poll function in main() to return and midi_action iscalled. In midi_action, SND_SEQ_EVENT_ECHO will result in a call toarpeggio and the loop is complete. d) void clear_queue()When miniArp receives a NOTEON event, the sequence is transposed.clear_queue is called on each NOTEON event to trigger a new sequence.SND_SEQ_REMOVE_OUTPUT | SND_SEQ_REMOVE_IGNORE_OFF is passed tosnd_seq_remove_events_set_condition to tell ALSA, that only output eventsshould be removed from the queue and NOTEOFF events should not be deleted.The latter condition ensures, that all notes of the sequence receive aNOTEOFF event after their length has elapsed, even when a new sequence istriggered.e) snd_seq_tick_time_t get_tick()This functions retrieves the current tick value from a queue statusstructure.8. Notes on writing a GUI based audio application-------------------------------------------------You probably would like to use PCM audio in a GUI based application. In thiscase it is recommended to use two threads, an audio thread and a GUI thread.Both threads can then communicate e.g. via shared memory. This has also theadvantage, that you can increase the priority of the audio thread seperatelyfrom the priority of the GUI thread. A sample audio thread could look likewhile(shared_data->do_audio) {    process_buffer(buf, bufsize);    while ((pcm_return = snd_pcm_writei(pcm_handle, buf, bufsize)) < 0) {        snd_pcm_prepare(pcm_handle);        fprintf(stderr, "xrun !\n");    }}It is not necessary to use polling in this case, since snd_pcm_writei willblock until bufsize frames can be written to the device.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -