[alsa-devel] usleep() and nanosleep() timings seem inaccurate using ALSA
Good Day!
After studying the intricacies of MIDI, I ended up writing an implementation of the MIDI protocol and file format.
I then studied the ALSA sequencer API to be able to control a synthesizer keyboard and play MIDI files. I've used aplaymidi, aconnect, arecordmidi, and all those great ALSA utilities. They're very good!
However, I wanted to have a simple ncurses based, command line MIDI sequencer meant for small Linux distributions such as DSL or Trustix so I began to write a command line sequencer using ALSA. I've encountered one problem about using usleep() and nanosleep() especially in 2.4 kernels.
The core of my problem in my application is that the MIDI files slow down their tempo when pitch changes are made on a particular channel/track, while a NOTE-ON event is being played.
For simplicity's sake, I'll give timing examples in terms of seconds and not bars nor measures.
For example, if I've got a 2 second NOTE-ON playing a guitar note, all timings are ok. If the NOTE-ON is interspersed with CONTROLLER messages such as a pitch bend, the part which is supposed to sound in approximately 2 seconds, sounds much longer than that.
The effect of this is that a ritardando is heard everytime short-duration CONTROLLER messages are sent.
The problem disappears when I use the sequencer queues. However, the problem manifests itself when using direct output (i.e. not using queues, using snd_seq_event_output and snd_seq_drain_output one after the other). The reason why I would not want to use the queue is that I'd want the UI of my program (such as metronome ticks and counts) to somehow synch with the sound that is being heard. I've tried removing all UI elements, thinking that the UI controls might have caused the delay, but still the ritardando effect persists.
The structure of my code is like this:
parse_midi_file(); merge_all_tracks_to_one_single_track(); normalize_the_delta_times() for all events; do usleep(delta_time_for_each_event); play_the_event_using_alsa_direct_output(); done
The ritardando effect is a bit lesser using 2.6 kernels. If I use queues, the problem disappears completely in 2.4 and 2.6 kernels. I don't hear the effect with simple MIDI files, such as with piano pieces.
By the way, I intend to use my app with a 2.4 kernel.
Does anybody have a solution to the delay problem? Please point out to me if I'm doing anything wrong since I've just been working with alsa-seq for a few months.
Thank you very much.
Best Regards,
Carlo
At Thu, 29 Mar 2007 17:46:38 +0800, Carlo Florendo wrote:
Good Day!
After studying the intricacies of MIDI, I ended up writing an implementation of the MIDI protocol and file format.
I then studied the ALSA sequencer API to be able to control a synthesizer keyboard and play MIDI files. I've used aplaymidi, aconnect, arecordmidi, and all those great ALSA utilities. They're very good!
However, I wanted to have a simple ncurses based, command line MIDI sequencer meant for small Linux distributions such as DSL or Trustix so I began to write a command line sequencer using ALSA. I've encountered one problem about using usleep() and nanosleep() especially in 2.4 kernels.
You cannot get a small sleep usually on user-space processes. Usually, usleep() is implemented with select/poll and its timeslice is defined by HZ in kernel config. In most cases, it's HZ=100, 250 or 1000 while 2.4-i386 kernel supports only HZ=100. That is, the least sleep time is 10ms no matter what value you pass to usleep().
This can be overcome by using a realtime schedule class and priority like JACK does.
Takashi
Takashi Iwai wrote:
At Thu, 29 Mar 2007 17:46:38 +0800, Carlo Florendo wrote:
Good Day!
After studying the intricacies of MIDI, I ended up writing an implementation of the MIDI protocol and file format.
I then studied the ALSA sequencer API to be able to control a synthesizer keyboard and play MIDI files. I've used aplaymidi, aconnect, arecordmidi, and all those great ALSA utilities. They're very good!
However, I wanted to have a simple ncurses based, command line MIDI sequencer meant for small Linux distributions such as DSL or Trustix so I began to write a command line sequencer using ALSA. I've encountered one problem about using usleep() and nanosleep() especially in 2.4 kernels.
You cannot get a small sleep usually on user-space processes. Usually, usleep() is implemented with select/poll and its timeslice is defined by HZ in kernel config. In most cases, it's HZ=100, 250 or 1000 while 2.4-i386 kernel supports only HZ=100. That is, the least sleep time is 10ms no matter what value you pass to usleep().
This can be overcome by using a realtime schedule class and priority like JACK does.
Ok. I will take that seriously. I've heard about JACK and know it's popular but I've never used it.
In any case, how does aplaymidi (or even timidity) produce the proper timings using ALSA? (In other words, how does the queue output sounds with correct timings?)
I've tried my best to read the alsa-lib and alsa-driver code, but I am not able to trace, with my very limited skills, how the timings are done. Could anyone point out where I should start?
Thank you very much and more power to the ALSA-dev team :)
Best Regards,
Carlo
At Fri, 30 Mar 2007 09:53:33 +0800, Carlo Florendo wrote:
Takashi Iwai wrote:
At Thu, 29 Mar 2007 17:46:38 +0800, Carlo Florendo wrote:
Good Day!
After studying the intricacies of MIDI, I ended up writing an implementation of the MIDI protocol and file format.
I then studied the ALSA sequencer API to be able to control a synthesizer keyboard and play MIDI files. I've used aplaymidi, aconnect, arecordmidi, and all those great ALSA utilities. They're very good!
However, I wanted to have a simple ncurses based, command line MIDI sequencer meant for small Linux distributions such as DSL or Trustix so I began to write a command line sequencer using ALSA. I've encountered one problem about using usleep() and nanosleep() especially in 2.4 kernels.
You cannot get a small sleep usually on user-space processes. Usually, usleep() is implemented with select/poll and its timeslice is defined by HZ in kernel config. In most cases, it's HZ=100, 250 or 1000 while 2.4-i386 kernel supports only HZ=100. That is, the least sleep time is 10ms no matter what value you pass to usleep().
This can be overcome by using a realtime schedule class and priority like JACK does.
Ok. I will take that seriously. I've heard about JACK and know it's popular but I've never used it.
Well, I don't mean JACK handles the MIDI in that way but as an example of real-time (audio) application. Basically you have to just set process scehduler class to SCHED_FIFO and raise the priority. Then your process would get invoked as soon as triggered, such as return from poll() events.
In any case, how does aplaymidi (or even timidity) produce the proper timings using ALSA? (In other words, how does the queue output sounds with correct timings?)
The ALSA sequencer queue is implemented on kernel, so it can have and use more accurate timer sources.
Takashi
Takashi Iwai wrote:
At Fri, 30 Mar 2007 09:53:33 +0800, Carlo Florendo wrote:
Takashi Iwai wrote:
At Thu, 29 Mar 2007 17:46:38 +0800, Carlo Florendo wrote:
Good Day!
After studying the intricacies of MIDI, I ended up writing an implementation of the MIDI protocol and file format.
I then studied the ALSA sequencer API to be able to control a synthesizer keyboard and play MIDI files. I've used aplaymidi, aconnect, arecordmidi, and all those great ALSA utilities. They're very good!
However, I wanted to have a simple ncurses based, command line MIDI sequencer meant for small Linux distributions such as DSL or Trustix so I began to write a command line sequencer using ALSA. I've encountered one problem about using usleep() and nanosleep() especially in 2.4 kernels.
You cannot get a small sleep usually on user-space processes. Usually, usleep() is implemented with select/poll and its timeslice is defined by HZ in kernel config. In most cases, it's HZ=100, 250 or 1000 while 2.4-i386 kernel supports only HZ=100. That is, the least sleep time is 10ms no matter what value you pass to usleep().
This can be overcome by using a realtime schedule class and priority like JACK does.
Ok. I will take that seriously. I've heard about JACK and know it's popular but I've never used it.
Well, I don't mean JACK handles the MIDI in that way but as an example of real-time (audio) application. Basically you have to just set process scehduler class to SCHED_FIFO and raise the priority. Then your process would get invoked as soon as triggered, such as return from poll() events.
In any case, how does aplaymidi (or even timidity) produce the proper timings using ALSA? (In other words, how does the queue output sounds with correct timings?)
The ALSA sequencer queue is implemented on kernel, so it can have and use more accurate timer sources.
Clear enough :) Thanks for the pointers.
Best Regards,
Carlo
participants (2)
-
Carlo Florendo
-
Takashi Iwai