/ A Real-Time Audio Scheduler for Pentium PCs
parameters. An extreme method computes samples at a fraction of the actual sampling rate, resulting in loss of high frequency content. The OctaneMeister normally assumes that other processes sharing the CPU are bounded in their CPU use, i.e., there is a fixed rate of execution of machine instructions which the other processes will not exceed no matter how much "idle time" is available, and this fixed rate is in fact less than the maximum rate which the CPU can support. The OctaneMeister has a second mode of operation for working with processes that are not CPU-bounded, such as certain 3D video games which acquire as much CPU as they can in order to maximize their visual frame rate. In this mode, the application itself is responsible for allocating resources to its components (audio, graphics, modelling) and ensuring that none of them starve. So rather than adapting to CPU load, the OctaneMeister follows the application's explicit commands for how much CPU time to give to the audio synthesis objects. In this scenario, the default "polite" behavior from the OctaneMeister would fail in the face of "greedy" behavior from the other processes, and audio would end up running at its lowest possible quality. 3.2 Mapping Application Data to Audio Parameters It is often desirable to control the parameters of a synthesis algorithm in ways other than individually and separately. To this end, AREAL implements the separate concepts of model parameters and synthesis parameters. A model parameter belongs to the application sending data to AREAL, for example the position or speed of an object or the rate of execution of some process in the model. A set of synthesis parameters, on the other hand, is precisely the run-time interface to a synthesis algorithm, consisting of scalars such as carrier-to-modulator ratios or filter coefficients. In a particular application, each synthesis parameter might be a function of one, several, or no model parameters; a complete set of these functions is called a mapping. An example illustrates the syntax for assigning several synthesis parameters sj from model parameters mi: arealSetMapping(hSynthObj, "sl=(ml,10,40); s2=.3*(m1,10,40)+4*(m2,.5,-.5); s3=0"); Here s, varies from zero to one as mi varies from 10 to 40; s2 is a weighted sum of two similarly normalized values, the first as before, the second varying from 0 to 1 as m2 varies from 0.5 down to -0.5; finally, s3 is a constant. (The identifier hSynthObj is a handle to a particular synthesis object.) This mapping is defined once at initialization; during execution, it is evaluated lazily whenever a model parameter changes. Such changes are communicated to areal with function calls like arealSetParm(hSynthObj, "ml", 39); arealSetParmAll("ml", 39); where the first of these functions updates a single synthesis object, and the second automatically updates all synthesis objects whose mappings use the model parameter mi. 4. Using AREAL The function areallnitAudioLibO initializes the AREAL subsystem with a particular sampling rate and number of audio channels. Computation of audio starts and stops with calls to arealStartAudioO and arealEndAudioO respectively. Synthesis objects of a particular synthesis class are instantiated and commence computing audio samples with arealAddSynthO; this function returns a handle to the new synthesis object. The functions arealSetMapping(), arealSetParm(), and arealSetParmAll() control running synthesis objects as described above. Finally there are functions which control the OctaneMeister's behavior: arealSetCPULimitO sets a limit on AREAL' s CPU usage; arealSetCPUModeO and arealSetCPUValueO are intended for applications with nonbounded CPU usage. This example of an AREAL synthesizer is a sample-and-hold noise generator. Class SynthParm encapsulates the smooth updating and k-rate functionality, used here for amplitude and hold time. class SynthNoise: public Synth { SynthParm ampl; // amplitude scalar SynthParm seconds; // how long to hold this value of the rnd. num gen. float duration; // how many seconds until the next rand() call Samp sampValue; // current output value (floating point) void AllBgn(void) { ampl.Bgn(); seconds.Bgn(); } void AllUpdate(float z) { ampl.Update(z); seconds.Update(z); } void AllUpdateLast() { ampl.UpdateLast(); seconds.UpdateLast(); }
Top of page Top of page