(what a Korg synthesizer does) Input: - a stream of events with - sound to play (0-k) - priority - start time (usually instant) - a database of sounds with - audio file (0.au-k.au) - elapsed time (how long it'll last) embedded in each au. Data Structure: - int array of the NEXT time each channel will be available (0 if available) int endt[CHANNELS]; - int array of the START times for each channel (0 if idle) int startt[CHANNELS]; - int array of priorities of each event scheduled in the channel. int priorit[CHANNELS]; - int array of minimal wall time at which a channel can be preempted (for intelligibility) int minendt[CHANNELS]; Event Algorithm: - when an event comes in: - try to pick a channel c that's idle. - if that's not possible, pick a channel c with: already played for minimal time (currenttime-startt[c]>minendt[c]) minimal priorit[c] (interrupt the lowest priority sound) minimal startt[c] (for c's with the same priority) (this is the sound that's been playing the longest) ( // here's the code for these two steps bestc=0; // no channel selected for (ind=0; indpriorit[c]) { bestc=c; continue; } if (bestc==0 || (priorit[bestc]==priorit[c] && startt[bestc]>startt[c])){ bestc=c; continue; } } // here bestc is either a blank channel or the lowest priority one // that started earliest. if bestc=0, then we don't have a channel yet! // if bestc is 0, then use temporal queueing as below. ) - now we have a c: interrupt the channel if playing. schedule the new sound. startt[c] = current time. endt[c] = next time available = startt[c] + sound length; minendt[c] = startt[c] + min play time; priorit[c] = new priority from incoming message. Comments: - can use a queue only for short window representation. - idea: if MANY TOO MANY events come in, 1000 events in 1/2 second, then represent the SPACE of events through start times that differ from the real start time by an acceptable amount. sounds **************** ------------------------------------------------------------------------time events ******* ******* ******* Question is HOW LONG the sounds should continue to play after the events are over. I.e., should we allow a lag in event representation in order to show more of them? Here's an idea: - suppose that we INTERRUPT lower priority sounds regardless of play time when higher-priority sounds occur. - suppose that we QUEUE sounds of the SAME PRIORITY as ones that are playing, and then play them for the minimum time, pre-empting after the minimum time. - suppose that what's happening NOW is inherently MORE important than what's happened before. Then we'd proceed as follows: - if we get event, channel clear, => play it. - if we get event, channel lower priority => clear and play it. - if we get event, channel same or greater priority and busy => queue it. - meanwhile, we try to play the queue 'in the silent times': - can do everything with interrupts. - SIGIO: I/O is available. - SIGALRM: timeout expired. Basic handling of input: - get SIGIO: - cancel any SIGALRM that might be in effect. - read and process incoming events - queue if necessary. - if queue nonempty, set SIGALRM at first time you can pre-empt a channel. (to cause an interrupt to be generated the next time you can pre-empt anything) - get SIGALRM: Event queue is REALLY a priority queue because the highest priority events should occur first. - try to schedule event in newly blank channel. - the top event in a priority queue is the highest priority event. - check that event. If too old, discard and try again. - if not too old, dequeue and schedule. - set SIGALRM for NEXT pre-emption time. Comments: - we need to start discarding lowest priority events when the queue fills up. This is a matter of REPLACING the leaves of the priority queue rather than adding leaves. - queue SHOULD have a finite size chosen based upon how many events we'll allow to play later. - only need to look at the top element. To test, simply write an exerciser that calls your SIGIO routine (without an interrupt) and exercises each channel. You can debug SIGALRM without even using SIGIO.