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(); }
0