mp4-sa-> the mp4-sa book-> SAOL->simple core opcodes |
Sections
|
Core Opcodes:abs acos aexprand agaussrand alinrand ampdb apoissonrand arand asin atan ceil cos cpsmidi cpsoct cpspch dbamp exp floor frac gettempo gettune iexprand igaussrand ilinrand int irand kexprand kgaussrand klinrand kpoissonrand krand log log10 max midicps midioct midipch min octcps octmidi octpch pchcps pchmidi pchoct pow settempo settune sgn sin sqrt |
IntroductionCore opcodes provide access to commonly-used algorithms through a function call syntax. Core opcodes serve the same role in SAOL that library functions serve in C. We introduced SAOL core opcodes in the second example in the tutorial in Part I. The right panel shows an assignment statement from this example that includes calls to the core opcodes sin and cpsmidi. In this chapter, we take a second look at core opcodes. We describe how to use core opcodes in SAOL expressions and statements, and introduce five rate rules that apply to core opcodes. Like our rate rules in Part II/2, our core opcode rate rules are more conservative than the SAOL language standard, but easier to understand and remember. We also describe 50 of the core opcodes that compute simple operations. These opcodes are a mix of general-purpose functions and utilities specialized for music and audio. |
From the tutorial:a = 2*sin(3.1415927* cpsmidi(num)/s_rate); |
Core Opcode SemanticsOpcode calls act as atomic elements in expressions. The value of the opcode call is computed as a function of its parameters. This computation occurs as the expression is being evaluated. Core opcode semantics are defined in the MP4-SA standard. SAOL also has user-defined opcodes, whose semantics are specified by an opcode definition written in SAOL itself. Since opcode calls act as atomic elements in expressions, they have a rate and a width. In the general case, determining the rate and width of a SAOL opcode may be quite subtle. In Part IV/4 of the book we describe the rate and width semantics for user-defined opcodes. In this section, we introduce several simple rules that apply to the core opcodes we introduce in Parts II and III of the book. Core opcode definitions include (1) a header syntax for the opcode that defines the structure of the opcode call and (2) the semantics for computing the return value. All core opcodes return a scalar value. |
|
Rate and Width RulesThe rate of fixed-rate opcodes is indicated in its header syntax. The keywords iopcode, kopcode, or aopcode indicate that an opcode is i-rate, k-rate, or a-rate respectively. The header syntax also indicates the rate of each opcode parameters. For example, the panel on the right shows the header syntax for the fixed-rate core opcode krand, which returns random numbers with a uniform distribution at the k-rate. The header for krand also defines the signal parameter p. This parameter sets the range of random numbers returned by krand to be from -p to p.
For fixed-rate core opcodes, it is simple to apply
our first rate rule:
The right panel shows examples that demonstrate rule 1, with expressions that include several calls to the opcode krand. These calls include a scalar expression within the parenthesis. To compute the value of krand, the expression is first evaluated, and then used to compute the return value.
Note that p is declared as a ksig
in the header syntax for krand. Our second rate rule concerns
the meaning of this declaration:
The right panel shows a krand example that breaks rule 2. |
krandHeader Syntaxkopcode krand(ksig p) Examplesivar i; ksig k; asig a; // rule 1 examples: // legal assignment to ksig k k = i*krand(5*i); // k-rate expr // illegal assignment to ivar i i = i*krand(5*i); // k-rate expr // statement breaks rule 2: k = krand(a + 0.5); |
The semantic specification of a fixed-rate core opcode may indicate the rate that a computation takes place. For example, the semantic description of krand states that the opcode generates a new random number when called at the k-rate. However, it is possible for the core opcode to be called at a faster rate. For example, a krand call on the right-hand side of an assignment to an a-rate variable will occur at the a-rate (see right panel).
Our third rate rule defines how SAOL programs execute in this
situation:
We can apply rule 3 to the example on the right panel. The krand call in this example generates a new random number on the first a-pass of an execution cycle. On the second a-pass, SAOL reuses the random number krand returned on the first a-pass. SAOL continues reusing this number until the first a-pass of the next execution cycle, at which time krand executes again and a new random number is generated. Note that if we wished to generate a new random number for each statement execution, we would choose the a-rate arand core opcode instead of the k-rate kand core opcode.
Our fourth rate rule defines what happens when an
i-rate opcode runs at a faster rate:
Note that rule 4 takes the same general approach to faster-rate execution as rule 3: execute once, then reuse the returned value. |
Exampleksig k; asig a; // krand called at a-rate a = krand(5); |
Not all opcodes are fixed-rate opcodes, and not all signal parameters in the header syntax for an opcode have a declared rate. The right panel shows the header syntax for the core opcode pow, which raises a number to a power. The keyword opcode indicates that pow has no fixed rate, but rather is a rate-polymorphic opcode. Its two signal parameters x and y are also rate-polymorphic, as indicated by their xsig declaration.
Our final rate rule concerns rate-polymorphic opcodes.
Once the rate of the opcode call is determined, rule 1 is used to determine the rate of an expression that contains the call. The semantics of rate-polymorphic core opcodes are not affected by the rate of the call. The examples on the right panel demonstrate rule 5.
Two width rules for core opcodes are mentioned in the description
above and formalized below:
|
powHeader Syntaxopcode pow(xsig x, xsig y) Example Callsivar i; ksig k; asig a; // pow calls are i-rate // statements are i-rate i = pow(2, 2); i = pow(i*2, 2); // pow calls are k-rate // statement is k-rate k = pow(i, pow(2, k+1)); // pow calls are a-rate // statements are a-rate a = pow(a, k); a = pow(a, i); // pow call is a-rate // statement is k-rate // // illegal assignment statement k = pow(i, a+1); |
Internal StateOpcodes may have internal state. On the right panel, we introduce the core opcode delay1 to explain the semantics of opcode state. The delay1 opcode has an internal variable, that holds a signal value. When the delay1 opcode is called, it returns the current value of its internal variable, and then sets the internal variable to the value of the calling parameter x.
We can now express the single semantic rule for opcode
state:
The core fragment on the right panel has three syntactically distinct calls to delay1. This code fragment implements a three-stage delay line, because each call to delay1 has its own internal state variable. In the following sections, we introduce sets of simple core opcodes that are useful in general-purpose programs. |
delay1Header Syntaxaopcode delay1(asig x) Exampleasig a1, a2, a3, a4; a2 = delay1(a1); a3 = delay1(a2); a4 = delay1(a3); |
TranscendentalsThe transcendental core opcodes are all rate-polymorphic. See the right panel for header syntax. Trigonometric OpcodesThe forward trigonometric opcodes take angle parameters in units of radians, and the inverse trigonometric opcodes return angle values in radians. The asin and acos opcodes may only be called with parameters in the range [-1, 1]. The asin and atan return values in the range [-pi/2, pi/2), and the acos returns values in the range [0, pi). Note that tan is missing from the core opcode library, as well as hyperbolic functions. The Slib library includes replacements for these missing functions, and the related constants pi and e. Logs and PowersThe log and log10 opcodes compute the natural and base-10 logarithms respectively, and may only be called with positive parameters. The exp opcode computes the exponential function. The sqrt opcode returns the square root of the parameter, and may not be called with negative parameters. The pow opcode returns the parameter x raised to the y power; if y is not an integer value, x may not be negative. |
Trigonometricopcode sin(xsig x) opcode cos(xsig x) opcode acos(xsig x) opcode asin(xsig x) opcode atan(xsig x) See Slib for other useful trigonometric functions like tan() and tanh(), and the constants pi and e. Logs and Expopcode log(xsig x) opcode log10(xsig x) opcode exp(xsig x) Powersopcode sqrt(xsig x) opcode pow(xsig x, xsig y) |
QuantizationFive rate-polymorphic core opcodes implement functions that relate to rounding and quantization. See the right panel for header syntax. The int opcode extracts the integer part of a parameter x and returns it as a floating-point value. It implements the C expression (float)((int)(x)). The frac opcode returns the signed fractional part of a parameter x. It implements the SAOL expression x - int(x). The floor opcode returns the greatest integral value y so that y<=x. The ceil opcode returns the smallest integral value y so that x<=y. The sgn opcode returns 1.0 if the parameter x is positive, -1.0 if x is negative, and 0.0 if x is zero. |
Quantizationopcode int(xsig x) opcode frac(xsig x) opcode floor(xsig x) opcode ceil(xsig x) opcode sgn(xsig x) |
Min, Max, and AbsThe rate-polymorphic min and max core opcodes return the smallest and largest of their parameters respectively. See the right panel for header syntax. These two opcodes must be called with at least one parameter, but may be called with an arbitrary number of parameters. The right panel introduces the header syntax notation for a variable number of opcode parameters. The rate-polymorphic abs core opcode returns the absolute value of the parameter x. |
Min, Max, and Absopcode min(xsig x1[, xsig ...]) opcode max(xsig x1[, xsig ...]) opcode abs(xsig x) |
Pseudorandom GeneratorsRandom NumbersA set of 12 fixed-rate core opcodes return pseudorandom numbers. Opcodes that return numbers at the i-rate, k-rate, and a-rate are provided for four different probability distributions. The right panel shows the header syntax, grouped by distribution. Note that opcode parameters have the same rate as the opcodes themselves. The probability distributions are also shown. The irand, krand, and arand opcodes generate pseudo-random numbers with a linear distribution in a range defined by the parameter p1. Numbers are uniformly distributed in the interval [-p1, p1]. The igaussrand, kgaussrand, and agaussrand opcodes generate pseudo-random numbers with a Gaussian distribution with mean mean and variance var, where mean and var are parameters. The var parameter must have a value greater than zero. The iexprand, kexprand, and aexprand opcodes generate pseudo-random numbers with an exponential (Poisson) distribution. The parameter p sets the single parameter of the distribution. and must have a value greater than zero. The ilinrand, klinrand, and alinrand opcodes generate pseudo-random numbers with a ramp distribution defined by the parameters p1 and p2. See the right panel for details. All calls to these 12 core opcodes share a common source of pseudorandom numbers. The opcodes themselves have no internal state. Binary Random SequencesThe core opcodes kpoissonrand and apoissonrand generate random sequences of binary return values according to a Poisson distribution. These opcodes return 1.0 if an event occurs and 0.0 otherwise. Each opcode has a single parameter p1. The opcode kpoissonrand generates sequences at the k-rate, using a Poisson distribution with a parameter dependent on the krate as well as p1. See the right panel for details. The opcode apoissonrand generates sequences at the a-rate, using a Poisson distribution with a parameter dependent on the arate as well as p1. Calls to kpoissonrand and apoissonrand share a common source of pseudorandom numbers with the opcodes described earlier in this section. However, the kpoissonrand and apoissonrand opcodes also use an internal state variable in its sequence generation algorithm. As explained in an earlier section, each syntactically distinct call to these opcodes accesses its own copy of the internal state variable. |
Random NumbersUniform Distributioniopcode irand(ivar p1) kopcode krand(ksig p1) aopcode arand(asig p1) prob(x) = 1/(2*p1), if x in [-p1, p1] prob(x) = 0 otherwise Gaussian Distributioniopcode igaussrand(ivar mean, ivar var) kopcode kgaussrand(ksig mean, ksig var) aopcode agaussrand(asig mean, asig var) prob(x) = (1/sqrt(2*pi*var))* exp(-(mean-x)*(mean-x)/(2*var)) Poisson Distributioniopcode iexprand(ivar p1) kopcode kexprand(ksig p1) aopcode aexprand(asig p1) prob(x) = (1/p1)*exp(-x/p1), if x > 0 prob(x) = 0 otherwise Linearly Ramped Distributioniopcode ilinrand(ivar p1, ivar p2) kopcode klinrand(ksig p1, ksig p2) aopcode alinrand(asig p1, asig p2) prob(x) = abs(d* (x-p1)) if x in [p1, p2] prob(x) = 0 otherwise where d = 2/((p2 - p1)*(p2 - p1)) Binary Random Sequenceskopcode kpoissonrand(ksig p1) generates binary (0/1) events using the distribution prob(x) = (1/(k_rate*p1)) *exp(-x/(k_rate*p1)) aopcode apoissonrand(asig p1) generates binary (0/1) events using the distribution prob(x) = (1/(s_rate*p1)) *exp(-x/(s_rate*p1)) both pdfs for (x > 0) only. |
LoudnessLinear increases in the amplitude of a sound waveform result in only logarithmic increases in the perceived loudness of the waveform. Therefore, waveform amplitude is often converted to the decibel (dB) scale to express loudness. SAOL has two rate-polymorphic core opcodes that handle conversion between amplitude units and decibel units. The right panel shows the header syntax for these opcodes and the equations the opcodes compute. The dbamp opcode converts the amplitude parameter x into decibels. The amplitude value 1.0 maps to 90dB. Only x values greater than zero may be used. The ampdb opcode converts the decibel parameter x into amplitude, using the same scaling as dbamp. These core opcodes introduce the SAOL convention for conversion opcodes: the return unit (db for dbamp) is the first part of the opcode name, and the parameter unit (amp for dbamp) is the second part of the name. |
Loudnessopcode dbamp(xsig x) computes 90 + 20 log10(x) for x > 0 opcode ampdb(xsig x) computes pow(10,(x - 90)/20) |
PitchSound generation algorithms usually require frequency variables in units of Hz (cycles per second). Western musicians usually label pitches with the names of notes on a piano keyboard (i.e. F# two octaves below middle C). Several numerical encodings of equally-tempered note names are used in computer music. A set of 12 rate-polymorphic core opcodes handles conversions between three popular note name encodings and cycles per second. The right panel shows the header syntax for these opcodes. These opcodes are named by the concatenation of the abbreviations for the four pitch types (cps, midi, pch, oct). The return type starts the opcode name. The cps type is cycles per second. Opcode parameters that are in cps notation must be greater that zero. The midi type is MIDI note numbers, which are integer values between 0 and 127 representing notes on a piano keyboard. MIDI note number 57 is A below middle C. MIDI note number return values always have integral values. The oct type is octave-fraction notation, that uses floating point numbers. The integer part of the number is the octave number, where 8 is the octave starting with middle C. The fractional part is the note within the octave, where 1/12 represents a semitone. For example, 7.75 is the A below middle C. The opcodes that return the oct type may return values between the 1/12 note steps, except for the octpch opcode. Opcode parameters that are in oct notation must be greater that zero. The pch type is pitch class notation, that uses floating point numbers. The integer part of the number is the octave number, where 8 is the octave starting with middle C. The fractional part is the note within the octave, where a 0.01 increment is a semitone. For example 7.09 is the A above middle C. Opcodes that return pch type generate values quantized to the nearest semitone. Opcodes parameters in pch notation are rounded to the nearest semitone, and parameter values greater than 0.11 are set to zero. By default, conversions between cps and the pitch representations assume that the A above middle C is 440.0 Hz. The k-rate core opcode settune changes this default to the value of the parameter x. The rate-polymorphic core opcode gettune returns the current global tuning value. The parameter dummy is simply used to set the rate of the opcode. If dummy is omitted the opcode is krate. |
Pitch Conversionopcode cpsmidi(xsig x) opcode cpspch(xsig x) opcode cpsoct(xsig x) opcode midicps(xsig x) opcode midipch(xsig x) opcode midioct(xsig x) opcode pchcps(xsig x) opcode pchmidi(xsig x) opcode pchoct(xsig x) opcode octcps(xsig x) opcode octmidi(xsig x) opcode octpch(xsig x) See Slib for useful constants for pitch calculations. Tuningkopcode settune(ksig x) opcode gettune([xsig dummy]) Conversion TableMIDI PCH OCT CPS (A440) C 36 6.00 6.000 65.40 C# 37 6.01 6.083... 69.29 D 38 6.02 6.166... 73.41 D# 39 6.03 6.250 77.78 E 40 6.04 6.333... 82.40 F 41 6.05 6.416... 87.30 F# 42 6.06 6.500 92.49 G 43 6.07 6.583... 97.99 G# 44 6.08 6.666... 103.82 A 45 6.09 6.750 110.00 A# 46 6.10 6.833... 116.54 B 47 6.11 6.916... 123.47 C 48 7.00 7.000 130.81 C# 49 7.01 7.083... 138.59 D 50 7.02 7.166... 146.83 D# 51 7.03 7.250 155.56 E 52 7.04 7.333... 164.81 F 53 7.05 7.416... 174.61 F# 54 7.06 7.500 184.99 G 55 7.07 7.583... 195.99 G# 56 7.08 7.666... 207.65 A 57 7.09 7.750 220.00 A# 58 7.10 7.833... 233.08 B 59 7.11 7.916... 246.94 C 60 8.00 8.000 261.62 <-- C# 61 8.01 8.083... 277.18 D 62 8.02 8.166... 293.66 D# 63 8.03 8.250 311.12 E 64 8.04 8.333... 329.62 F 65 8.05 8.416... 349.22 F# 66 8.06 8.500 369.99 G 67 8.07 8.583... 391.99 G# 68 8.08 8.666... 415.20 A 69 8.09 8.750 440.00 A# 70 8.10 8.833... 466.16 B 71 8.11 8.916... 493.88 C 72 9.00 9.000 523.25 Arrow denotes middle C. ... denotes repeating digit |
TempoWe introduced the SASL tempo command in the second example in the tutorial in Part I. This score command changed the relationship between score time and simulated time from the default 60 score beats per minute. The global tempo can also be changed in SAOL, through the k-rate core opcode settempo. The new tempo parameter x must be set to a value greater than zero. A companion rate-polymorphic opcode, gettempo, returns the current tempo. The optional parameter dummy sets the rate of the opcode, which defaults to k-rate. |
Tempokopcode settempo(ksig x) opcode gettempo([xsig dummy]) See Slib for useful constants for tempo calculations. |
SummaryWe have now finished our description of the 50 core opcodes that are useful when writing algorithms at the lowest level of abstraction. The remaining core opcodes work at higher level of abstractions, often implementing complete synthesis and effects methods in a single opcode call. We introduce these opcodes, often in conjunction with descriptions of related SAOL language features, in the remainder of the book. The right panel has a summary of the rate and width rules for core opcodes. Next section: Part II/4: Wavetables |
Core Opcode Rate Rules:
Core Opcode Width Rules:
Core Opcode State Rule:
|
mp4-sa-> the mp4-sa book-> SAOL->simple core opcodes |