[alsa-devel] sending a sequencer event to a delayed queue
Unfortunately the ALSA doc does not say, what happens if I send an event to a queue that is not running. I expected that the event is being delivered once the queue is started. But it seems that instead the event is dropped. What is the reason for that behaviour?
What I want to do is the following: I have a program that computes a sequence of events in real-time. Since computation of an event requires varying amount of time, I want to add a latency in order to get a correctly timed output. To this end I set up two queues: One queue triggers computation of events via Echo messages and the other queue delivers the computed events. I want to use the same time stamps for both queues and just start the queues with a delay between them. Why is it not possible to use one queue and add latency manually to the timestamps? Because the user of the program shall be able to halt the computation. Thus the computation must be halted first and the delivery of events must be halted later. I cannot achieve this with one queue and a queue_stop command. Now the problem is: When my program starts it computes the first event and sends it to the second (delayed) queue. But since this queue is started only after a delay, it is not yet running and thus my event is not delayed, but lost. :-( How to solve that problem? Is there a proposed way to manage latency in ALSA sequencing?
Regards, Henning
Henning Thielemann wrote:
Unfortunately the ALSA doc does not say, what happens if I send an event to a queue that is not running.
Exactly the same as with a running queue: the event stays in the client's output buffer if its scheduled time has not yet been reached.
I expected that the event is being delivered once the queue is started. But it seems that instead the event is dropped.
Perhaps it was delivered too early?
Regards, Clemens
Clemens Ladisch schrieb:
Henning Thielemann wrote:
Unfortunately the ALSA doc does not say, what happens if I send an event to a queue that is not running.
Exactly the same as with a running queue: the event stays in the client's output buffer if its scheduled time has not yet been reached.
I expected that the event is being delivered once the queue is started. But it seems that instead the event is dropped.
Perhaps it was delivered too early?
I have attached a C program that demonstrates the effect: I start the "player" queue with one second delay and immediately send a message with a timestamp 0 that is meant to be the local time of the "player" queue. I expected that this event is delivered when the player queue starts, that is, one second after program start. But actually it is played immediately. If I choose a time larger than 0, say 1ns or 1s, then the event is not delivered at all.
I use output_direct, but the effect is the same if I use output and drain.
Henning Thielemann wrote:
Clemens Ladisch schrieb:
Henning Thielemann wrote:
Unfortunately the ALSA doc does not say, what happens if I send an event to a queue that is not running.
Exactly the same as with a running queue: the event stays in the client's output buffer if its scheduled time has not yet been reached.
I have attached a C program that demonstrates the effect: I start the "player" queue with one second delay and immediately send a message with a timestamp 0 that is meant to be the local time of the "player" queue. I expected that this event is delivered when the player queue starts, that is, one second after program start. But actually it is played immediately.
Yes; whether an event is delivered depends only on its scheduled time, not whether the queue is running.
If I choose a time larger than 0, say 1ns or 1s, then the event is not delivered at all.
Starting a queue also clears it. (This is done explicitly in the code, so I guess this is a feature.)
I'd suggest to use one queue for both kinds of events, and to add the delay to the scheduled time. To remove certain events, use snd_seq_remove_events().
Regards, Clemens
On Sun, 11 Mar 2012, Clemens Ladisch wrote:
Yes; whether an event is delivered depends only on its scheduled time, not whether the queue is running.
That is, if the queue time is zero and the event time is zero, then the event is sent, independent of whether the queue is running or not. Right?
This is good to know and good to be documented. Is this documented somewhere and if not how could I document it myself? I remember there is an ALSA wiki ...
If I choose a time larger than 0, say 1ns or 1s, then the event is not delivered at all.
Starting a queue also clears it. (This is done explicitly in the code, so I guess this is a feature.)
Should be certainly documented somehow. So maybe, 'CONTINUE' is the solution.
I'd suggest to use one queue for both kinds of events, and to add the delay to the scheduled time. To remove certain events, use snd_seq_remove_events().
I'll think about that.
Since we are at documentation. seq.h states:
#define SND_SEQ_PORT_CAP_SYNC_READ (1<<2) /**< allow read subscriptions */ #define SND_SEQ_PORT_CAP_SYNC_WRITE (1<<3) /**< allow write subscriptions */
...
#define SND_SEQ_PORT_CAP_SUBS_READ (1<<5) /**< allow read subscription */ #define SND_SEQ_PORT_CAP_SUBS_WRITE (1<<6) /**< allow write subscription */
So are the comments for SYNC_READ and SYNC_WRITE correct?
Henning Thielemann wrote:
On Sun, 11 Mar 2012, Clemens Ladisch wrote:
Yes; whether an event is delivered depends only on its scheduled time, not whether the queue is running.
That is, if the queue time is zero and the event time is zero, then the event is sent, independent of whether the queue is running or not.
Yes.
Since we are at documentation. seq.h states:
#define SND_SEQ_PORT_CAP_SYNC_READ (1<<2) /**< allow read subscriptions */ #define SND_SEQ_PORT_CAP_SYNC_WRITE (1<<3) /**< allow write subscriptions */ ... #define SND_SEQ_PORT_CAP_SUBS_READ (1<<5) /**< allow read subscription */ #define SND_SEQ_PORT_CAP_SUBS_WRITE (1<<6) /**< allow write subscription */
So are the comments for SYNC_READ and SYNC_WRITE correct?
No, they should say "obsolete; no effect".
Regards, Clemens
On Mon, 12 Mar 2012, Clemens Ladisch wrote:
Henning Thielemann wrote:
On Sun, 11 Mar 2012, Clemens Ladisch wrote:
Yes; whether an event is delivered depends only on its scheduled time, not whether the queue is running.
That is, if the queue time is zero and the event time is zero, then the event is sent, independent of whether the queue is running or not.
Yes.
Ok, I had an idea of how to solve my problem. I want to halt the queue and send all messages immediately that are still in the queue. To this end I think I could just increase the queue time.
Thus I did a test whether this will work. I did the following:
output note-on event with timestamp 1s output note-off event with timestamp 2s control_queue SND_SEQ_EVENT_SETPOS_TIME 3s drain
Nothing happens.
I continue the queue after increasing the time:
output note-on event with timestamp 1s output note-off event with timestamp 2s control_queue SND_SEQ_EVENT_SETPOS_TIME 3s control_queue SND_SEQ_EVENT_CONTINUE drain
Now the note events are scheduled at 1s and 2s as if the SETPOS_TIME control was ignored.
Then I continued the queue before increasing the time:
output note-on event with timestamp 1s output note-off event with timestamp 2s control_queue SND_SEQ_EVENT_CONTINUE control_queue SND_SEQ_EVENT_SETPOS_TIME 3s drain
Now the events arrive instantly. This is the behavior I want to have.
It looks like SETPOS_TIME is ignored if the queue is halted. Is this correct?
I get interesting result if I print the queue time using snd_seq_queue_status_get_real_time.
On Wed, 14 Mar 2012, Henning Thielemann wrote:
Ok, I had an idea of how to solve my problem. I want to halt the queue and send all messages immediately that are still in the queue. To this end I think I could just increase the queue time.
Thus I did a test whether this will work. I did the following:
output note-on event with timestamp 1s output note-off event with timestamp 2s control_queue SND_SEQ_EVENT_SETPOS_TIME 3s drain
Nothing happens.
Queue time is exactly 3s after drain.
I continue the queue after increasing the time:
output note-on event with timestamp 1s output note-off event with timestamp 2s control_queue SND_SEQ_EVENT_SETPOS_TIME 3s control_queue SND_SEQ_EVENT_CONTINUE drain
Now the note events are scheduled at 1s and 2s as if the SETPOS_TIME control was ignored.
Queue time is some microseconds larger than 0.
That is, SETPOS_TIME seems to successfully alter the time, but then CONTINUE seems to reset the time. Is this a bug or a feature?
SETPOS_TIME does not emit events that become older than queue time. Is it a bug or a feature?
Henning Thielemann wrote:
On Wed, 14 Mar 2012, Henning Thielemann wrote:
output note-on event with timestamp 1s output note-off event with timestamp 2s control_queue SND_SEQ_EVENT_SETPOS_TIME 3s drain
Nothing happens.
To get events to be dispatched, you need a timer tick, i.e., you have to make the timer run again.
I continue the queue after increasing the time:
output note-on event with timestamp 1s output note-off event with timestamp 2s control_queue SND_SEQ_EVENT_SETPOS_TIME 3s control_queue SND_SEQ_EVENT_CONTINUE drain
Now the note events are scheduled at 1s and 2s as if the SETPOS_TIME control was ignored.
Queue time is some microseconds larger than 0.
That is, SETPOS_TIME seems to successfully alter the time, but then CONTINUE seems to reset the time. Is this a bug or a feature?
CONTINUE behaves like START if the timer hasn't run yet.
Regards, Clemens
On Sun, 11 Mar 2012, Clemens Ladisch wrote:
I'd suggest to use one queue for both kinds of events, and to add the delay to the scheduled time. To remove certain events, use snd_seq_remove_events().
In order to understand how snd_seq_remove_events works, I looked into its source code ... I wondered why I can set the queue as condition with snd_seq_remove_events_set_queue but there is no flag that enables queue matching and actually the queue is not checked in remove_match, as far as I can see.
I have another question: Is snd_seq_remove_events atomic, that is, if some events have the same time stamp, is it guaranteed that they are removed all or none at all? Are events still delivered while removing? Or is there a possibility to check what I actually removed? I also thought about using snd_seq_extract_output, but I am concerned about removing events while events are delivered.
Regards, Henning
Henning Thielemann wrote:
In order to understand how snd_seq_remove_events works, I looked into its source code ... I wondered why I can set the queue as condition with snd_seq_remove_events_set_queue but there is no flag that enables queue matching
SND_SEQ_REMOVE_DEST enables queue/client/port matching.
and actually the queue is not checked in remove_match, as far as I can see.
Well, it's checked in the kernel. :) I guess this is a bug in alsa-lib.
I have another question: Is snd_seq_remove_events atomic, that is, if some events have the same time stamp, is it guaranteed that they are removed all or none at all? Are events still delivered while removing?
Queues are locked one at a time; events in the same queue with the same timestamping mode should be removed atomically.
Regards, Clemens
On Sun, 11 Mar 2012, Clemens Ladisch wrote:
I'd suggest to use one queue for both kinds of events, and to add the delay to the scheduled time. To remove certain events, use snd_seq_remove_events().
Hm, I am thinking about how to make use of snd_seq_remove_events in my application. It's sad that I cannot find out what events I have actually removed. It would be bad if e.g. a NoteOff gets lost and thus a tone is played forever. It would be great if I could find out pending events in the queue (in the kernel space). Then I could fetch them and re-send them immediately, when the user stops my sequencer. Unfortunately snd_seq_extract_output only fetches events from the user buffer. Is there a snd_seq_extract_output for drained messages?
Regards, Henning
participants (3)
-
Clemens Ladisch
-
Henning Thielemann
-
Henning Thielemann