Here is a list of all the material within this user manual. You may obtain the source code here, and obtain Nyquist here. Note that this document is slightly modified from the one that is present in the source package.

The `tuning.lsp`

library gives the Nyquist user easy access to tuning systems other than 12-Tone Equal Temperament (12-TET). This manual will first describe the library and how it ought to be used, and will follow on with details of the various structures and design decisions made.

Nyquist uses the MIDI Tuning Standard (MTS) with A4=440Hz by default as the standard tuning system; this is compatible with 12-TET, which is the mainstay for Western music tuning for years. The difference between `tuning.lsp`

over the system in Nyquist lies in the fact that more than one tuning system can exist at the same time, which provides a richer pool of pitches that can be used in composing xenharmonic pieces.

There are a few terms that keep getting used and re-used in this document, and this section will provide a brief definition of what they are.

- Interval ratio
- The ratio of the frequency of a pitch to a reference pitch.
- Unison
- The interval ratio of 1/1.
- Octave
- The interval ratio of 2/1. The most common interval ratio for repeated pitches to be considered musically ``the same''.
- Pseudo-octave
- This is an interval ratio that is not 2/1 but is treated as the interval ratio for repeated pitches to be considered musically ``the same''. We tend to use pseudo-octave here as a general term for any interval ratio that is used repeatedly to define pitches that are musically ``the same''.
- MIDI Tuning Standard (MTS)
- This is a specification of musical pitch agreed upon by the MIDI Manufacturers Association. It specifies a mapping from integers to pitches from 12-TET with A4=440 Hz. Nyquist uses this when parameters are referred to as requiring either
`pitch`or`step`. One fixed point in MTS to remember is 69=A4. - Equal Temperament
- This is a type of tuning that where every pair of music pitches have an identical interval ratio. One classical example of this is 12-TET or 12-Tone Equal Temperament, the prevalent tuning system in Western music today.

Using `tuning.lsp`

requires two major steps:

- Create a tuning and save it to a variable.
- Use
`steps-of`

and`tuning-freq`

with the saved tuning to obtain the MTS step count and actual pitch frequency respectively in places where they are valid under normal circumstances.

If there are any errors in any of the functions used in any step, it will be displayed on the console of Nyquist, with some feedback on what is wrong.

As a simple overview of how `tuning.lsp`

might be used, consider the following code fragment that uses `tuning.lsp`

and `score-gen`

to create a [boring] melody.

```
load "tuning.lsp"
define function a-sound()
begin
with 12-tet = make-equal-temperament(12, 2, 440.0, 9, 4),
; Pitch patterns involving C3, E4 and G3 respectively
pitch-pat = make-cycle(list(steps-of(12-tet, 0, 3),
steps-of(12-tet, 4, 4),
steps-of(12-tet, 7, 3))),
rhythm-pat = make-cycle(list(q))
exec score-gen(save: quote(sound-a),
score-len: 5,
ioi: next(rhythm-pat),
pitch: next(pitch-pat))
return sound-a
end
play(a-sound())
```

We will now examine each step in detail in the next two subsections.

There are two types of tuning systems that `tuning.lsp`

can support; these are Equal Temperament and Explicit Mapping respectively. The following are the two functions necessary to create the two tuning systems.

`make-equal-temperament(`

`pitches`,`pseudo-octave-ratio`,`ref-freq`[,`ref-freq-offset`[,`ref-freq-pseudo-octave`]])Returns a tuple containing information necessary to recreate any pitch within the constructed Equal Temperament scale. The number of pitches in the Equal Temperament system is given by

`pitches`, the ratio of the frequency of the highest pitch to the lowest in one cycle of the scale is given by`pseudo-octave-ratio`, the reference frequency is given by`ref-freq`. If`ref-freq-offset`and`ref-freq-pseudo-octave`are given,`ref-freq`is assumed to be the pitch equal to`ref-freq-pseudo-octave`cycles of the scale followed by`ref-freq-offset`notes into the cycle above the ``start'' point.For example, for 12-TET with A4=440Hz, we can create and save the scale to a variable

`12-tet`

using the following:`load "tuning.lsp" set 12-tet = make-equal-temperament(12, 2, 440, 9, 4)`

Observe that

`ref-freq-pseudo-octave`and`ref-freq-offset`are set to 9 and 4 respectively since A is nine pitches above the reference of C, and A4 is 4 octaves over the lowest note we want to reference from, i.e. C0.`make-tuning(`

`intervals`,`ref-freq`[,`ref-freq-offset`[,`ref-freq-pseudo-octave`]])Returns a tuple containing information necessary to recreate any pitch within the constructed scale. To use this function, construct a list of interval ratios that govern the relationships of pitches relative to the reference frequency and pass this as

`intervals`; provide the reference frequency in`ref-freq`. The two remaining optional parameters work the same way as defined in`make-equal-temperament`

above.There are four assumptions made about the Explicit Mapped tuning system thus defined:

- The scale is
*always*monotonically increasing---interval ratios*will*be sorted to ensure this fact. - The smallest interval ratio is
*always*1/1---if the smallest ratio is not 1/1, all interval ratios*will*be normalised till this is so. - The largest interval ratio determines the interval ratio for the pseudo-octave.
- Integer pitch numbers are
*guaranteed*to have the correct [in-order] ratios---fractional pitch numbers*will*be estimated using interpolation of an Equal Temperament curve with the normalised unison and normalised pseudo-octave as fixed points.

These four assumptions will explain some of the behaviour when malformed interval ratios are provided.

For example, if we want to construct a 12-tone Pythagorean scale explicitly and save the scale to a variable`12-pyth`, we can use the following:`load "tuning.lsp" set 12-pyth = make-tuning(list(1.0, 2187.0 / 2048.0, 9.0 / 8.0, 32.0 / 27.0, 81.0 / 64.0, 4.0 / 3.0, 729.0 / 512.0, 3.0 / 2.0, 6561.0 / 4096.0, 27.0 / 16.0, 16.0 / 9.0, 243.0 / 128.0, 2.0), 440.0)`

- The scale is

There are two general purpose functions that will allow us to generate the correct MTS pitch number and frequency for a required note given a variable storing the tuning system that was constructed using `make-equal-temperament`

and `make-tuning`

. These functions will be explained below.

`steps-of(`

`tuning`,`pitch-number`,`pseudo-octave`)Returns the equivalent MTS step number of a pitch in the different scale given a tuple

`tuning`created with`make-equal-temperament`

/`make-tuning`

, and the pitch specification in`pseudo-octave`and the specific pitch number within the pseudo-octave in`pitch-number`. Note that`steps-of`

depends on`*A4-Hertz*`

, so expect the MTS pitch numbers to vary if`*A4-Hertz*`

has been changed. The consistent thing about`steps-of`

will be that the frequency of the output MTS step will be correct for the required pitch in the different tuning system.Here is an example on how to use

`steps-of`

with`make-equal-temperament`

:`load "tuning.lsp" set *A4-Hertz* = 440.0 exec set-pitch-names() set 12-tet = make-equal-temperament(12, 2, 440, 9, 4) print(steps-of(12-tet, 5, 4))`

The output of this fragment of SAL code will be

`65`

, which is the MTS pitch number for F4. Recall that A4 is the 9-th pitch in the 4-th octave (pitches and octaves begin counting from 0).And now for something more exotic, 12-tone Pythagorean tuning made with

`make-tuning`

:`load "tuning.lsp" set *A4-Hertz* = 440.0 exec set-pitch-names() set 12-pyth = make-tuning(list(1.0, 2187.0 / 2048.0, 9.0 / 8.0, 32.0 / 27.0, 81.0 / 64.0, 4.0 / 3.0, 729.0 / 512.0, 3.0 / 2.0, 6561.0 / 4096.0, 27.0 / 16.0, 16.0 / 9.0, 243.0 / 128.0, 2.0), 440.0) print(steps-of(12-pyth, 11, 4))`

The output of this fragment of SAL code will be

`71.0391`

, which is the MTS pitch number equivalent for B4 under the 12-tone Pythagorean Tuning. Recall once more that A4 is the 9-th pitch in the 4-th octave (pitches and octaves begin counting from 0).`tuning-freq(`

`tuning`,`pitch-number`,`pseudo-octave`)Returns the frequency of a pitch in the different scale given a tuple

`tuning`created with`make-equal-temperament`

/`make-tuning`

, and the pitch specification in`pseudo-octave`and the specific pitch number within the pseudo-octave in`pitch-number`.Here is an example on how to use

`tuning-freq`

with`make-equal-temperament`

:`load "tuning.lsp" set *A4-Hertz* = 440.0 exec set-pitch-names() set 12-tet = make-equal-temperament(12, 2, 440, 9, 4) print(tuning-freq(12-tet, 9, 5))`

The output of this fragment of SAL code will be

`880`

, which is the frequency of A5 under MTS with A4=440Hz. Recall that A4 is the 9-th pitch in the 4-th octave (pitches and octaves begin counting from 0).And now for something more exotic, 12-tone Pythagorean tuning made with

`make-tuning`

:`load "tuning.lsp" set *A4-Hertz* = 440.0 exec set-pitch-names() set 12-pyth = make-tuning(list(1.0, 2187.0 / 2048.0, 9.0 / 8.0, 32.0 / 27.0, 81.0 / 64.0, 4.0 / 3.0, 729.0 / 512.0, 3.0 / 2.0, 6561.0 / 4096.0, 27.0 / 16.0, 16.0 / 9.0, 243.0 / 128.0, 2.0), 440.0, 9, 4) print(tuning-freq(12-pyth, 11, 4))`

The output of this fragment of SAL code will be

`495`

, which is the frequency of B4 under the 12-tone Pythagorean Tuning. Recall once more that A4 is the 9-th pitch in the 4-th octave (pitches and octaves begin counting from 0).

While `make-equal-temperament`

and `make-tuning`

are great for programmatically creating new tuning systems based on known parameters like pseudo-octave ratios and interval ratios respectively, sometimes one might want to use scales that were designed by others which can be difficult to replicate using either of these two functions by hand (like Partch's 43-tone system). Fortunately, there is a free tool available for creating scales---this tool is SCALA. The `tuning.lsp`

supports the loading of scale files that were created with SCALA; the file format is fairly flexible and straightforward and can be found here.

Briefly speaking, SCALA scale files store the interval ratios in the scale. As such, tuning systems created with `make-equal-temperament`

will need to have the interval ratios extracted and saved to the file. To do this task in a general way, we can use the one function `get-intervals`

, which works for tuples generated by either `make-equal-temperament`

or `make-tuning`

.

`get-intervals(`

`tuning`)Returns a list of the pitch interval ratios within the pseudo-octave determined at creation time of the tuple

`tuning`. The tuple`tuning`can be created either with`make-equal-temperament`

or`make-tuning`

.

There are two functions that help in saving and loading from SCALA Scale files (also known as ``.scl files''). Both of these functions use scales that are in interval ratio form (see `get-intervals`

for more information on how to convert a scale to a list of interval ratios).

`load-intervals-from-scala(`

`filename`)Returns a list of interval ratios that is loaded from the .scl file specified in the string

`filename`.Here is an example to load the ``12-tet.scl'' SCALA scale file and use the loaded interval to create a tuning tuple:

`set interval-ratios = load-intervals-from-scala("12-tet.scl") set 12-tet = make-tuning(interval-ratios, 440.0, 9, 4)`

Notice that we can only use

`make-tuning`

to create a tuning system from the loaded .scl file. This is true even if the .scl file we are loading represents an Equal Temperament scale.`save-intervals-to-scala(`

`filename`,`interval-list`[,`scale-name`])Saves the interval ratios in the list

`interval-list`to the .scl file specified by`filename`, optionally writing out the string`scale-name`as a part of the name of the scale.Note that

`save-intervals-to-scala`

will always save interval ratios in musical cents format. This is true even if the interval ratios to be saved were originally defined through rational division like`4.0/5.0`

.Here is an example that creates a 7-TET tuning system and saves it to ``7-tet.scl'' SCALA scale file. Notice how

`get-intervals`

is used to obtain the interval ratios necessary for`save-intervals-from-scale`

.`set 7-tet = make-equal-temperament(7, 2, 100.0) set interval-ratios = get-intervals(7-tet) exec save-intervals-to-scala("7-tet.scl", interval-ratios, "7-Tone Equal Temperament")`

We can do this for a tuple created with

`make-tuning`

in a similar way.

Support on SCALA .scl files are fairly robust in non-degenerate cases. One limitation of .scl files will be that only the interval ratios are stored; the reference frequency is not stored anywhere in the .scl file.

Contained within this section are some of the design decisions and specifications that are related to the development of `tuning.lsp`

. It is unnecessary for a user of `tuning.lsp`

to understand the intricacies of the code design to use this library. These notes are meant to supplement the plentiful comments that exist within the source files themselves.

There are two forms of dependencies that we will be looking at, namely the file dependencies and next the function dependencies.

Referring to the diagram above, we see the relationships of the files with each other. Note that `lib`

here refers to the default Nyquist directory for all other libraries, while `lib/tuning`

is where all the other support files are located. The user will have to issue a `load "tuning.lsp"`

statement in SAL and the search path will automatically find `tuning.lsp`

, which will automatically load the three component files `et-tuning.lsp`

, `em-tuning.lsp`

and `scala.lsp`

that implement much of the functionality.

The file `et-tuning.lsp`

contains functions related to the Equal Temperament tuning system (which builds equal temperament scales according to the interval ratio of the pseudo-octave, number of pitches required and parameters for the reference frequency); the file `em-tuning.lsp`

contains the functions related to the Explicit Mapping tuning system (which constructs a tuning system based on explicit interval-ratios for each pitch relative to some reference pitch, and again parameters for the reference frequency); the file `scala.lsp`

contains the code necessary for reading and writing to .scl files.

Again referring to the diagram above, we find that the globally viewable functions are grouped according to which files they can be found---this will aid in isolation and debugging specific parts of the code should things not work out well.

The diagram immediately above lists the function dependencies of all the globally accessible functions. Globally accessible here refers to the fact that any user program that issues a `load "tuning.lsp"`

statement will be able to see these functions. The inclusion of the dependencies among functions are to aid in the modification of the library in the future.

This section talks a little about the tuples that `make-equal-temperament`

and `make-tuning`

create and how these are used by `make-tuning`

and `get-intervals`

to detect the correct way to decode the information.

The tuple created by `make-equal-temperament`

is in reality a list, with the following items in it:

- First element set to the string
`"et"`

for ``equal temperament''. - Second element set to a float that stores the interval ratio between consecutive pairs of notes (they are all equal, so we can just store one of them).
- Third element set to the number of pitches to the pseudo-octave.
- Last element is a reference frequency based off the one that was passed to
`make-equal-temperament`

. This frequency is calculated by reducing the passed reference frequency by the supposed position of that passed reference frequency so that we get the actual frequency for the pitch whose pitch number is 0 and whose pseudo-octave is also 0.

The data that are stored in this list are designed to reduce computations required to generate the correct frequency for a particular pitch given the pitch's position within the pseudo-octave and the number of pseudo-octaves away relative to the reference frequency stored in the list.

The tuple created by `make-tuning`

is more complex. Again the tuple is really a list, but this time, it has the following items in it:

- First element set to the strong
`"em"`

for ``explicit mapping''. - Second elemnt set to an ascending list of floating point numbers, canonically known as
`tet-fractions`. This list always begins with 0.0 and ends with 1.0, and each element in this list corresponds to the relative position of the interval ratio on an equal temperament curve whose lowest end point (0.0 mark) is on the lowest interval ratio and whose highest end point (1.0 mark) is on the highest interval ratio (or the pseudo-octave). The choice of this representation is to make it easier to interpolate for pitch numbers that are not integral, i.e. falling in between the ``cracks'' of the defined scale. The choice of an equal temperament curve for the interpolation is to be consistent with the interpretation in the case where the interval ratios define an equal temperament scale. - Third element confirms the number of pitches in the scale, which allows for some sanity checking and reduction in use of
`length`

to figure out the number of pitches. - Fourth element stores the interval ratio for the pseudo octave, which allows us to scale
`tet-fractions`accordingly when we need to obtain actual ratios. - Last element is a reference frequency based off the one that was passed to
`make-tuning`

, defined in the tuple description for`make-equal-temperament`

.

So, much of the magic in `tuning-freq`

and `get-intervals`

(`steps-of`

is dependent on `tuning-freq`

and thus does not need to perform this ``magic'') in determining which functions to call when given a tuning tuple comes from reading off the information in the first element of the tuple, which basically uniquely identifies each of the tuning systems that were used to generate that tuple. From there, the functions will despatch the arguments passed to it to the correct functions from either `et-tuning.lsp`

or `em-tuning.lsp`

and return the result. This maintains the illusion that the `tuning.lsp`

library is versatile from the perspective of the user.

As noted earlier in Saving/Loading to .scl Files, support for .scl files are rudimentary. The .scl file format is remarkably flexible, which leads to the following problem: if the scale in the .scl file is *not* monotonically increasing, then `make-tuning`

will fail terribly due to the failure of the assumption that all tuning scales will have increasing interval ratios as the pitch number increases. As far as I can tell, this is *usually* the case, and should the non-monotonically increasing scale be required, a workaround will be to write an intermediate layer to translate from the scale's ``proper'' pitch number to the one that is in order that `make-tuning`

uses. But as noted, since this problem does not seem to occur in real life, no work has been done to ensure that the support for non-monotonically increasing interval ratios in a scale behaves correctly.

Here is a list of all the globally accessible functions (and variables) and a brief description of what they do in alphabetical order. Refer to the sources for more details.

`*tuning-dir*`

- This is set to
`"tuning/"`

when deployed; set this to`""`

when testing locally. `cents-to-ratio(`

`cents`)- Converts musical cents into an interval ratio.
`em-frequency(`

`tuning`,`pitch-number`,`pseudo-octave`)- Deals with the generation of the correct frequency for an explicitly mapped tuning system. This function is here so that we can consolidate all the ``magic'' parts of the tuple relevant to the explicit mapping in one place (instead of building into
`tuning.lsp`

, which confuses things). `em-intervals(`

`tuning`)- Deals with the generation of the list of intervals for saving as a SCALA .scl file.
`em-linear-scale-of(`

`tet-fractions`,`pitch`)- Returns the TET-scale that the pitch value represents, given a list of TET-fractions.
`et-frequency(`

`tuning`,`pitch-number`,`pseudo-octave`)- Deals with the generation of the correct frequency for an equal temperament tuning. This function is here so that we can consolidate all the ``magic'' parts of the tuple relevant to equal temperament in one place (instead of building them into
`tuning.lsp`

, which confuses things). `et-intervals(`

`tuning`)- Deals with the generation of the list of interval ratios for saving as SCALA .scl file format by
`save-intervals-to-scala`

. `et-linear-scale-to-ratio(`

`pseudo-octave-ratio`,`linear-scale`)- Takes a pseudo-octave ratio and ``linear'' scale, and returns the associated ratio.
`et-ratio-to-linear-scale(`

`pseudo-octave-ratio`,`interval-ratio`)- Takes a pseudo-octave ratio and an interval ratio, and returns the log-based scale of the interval ratio with respect to the pseudo-octave ratio.
`fifth(`

`L`)- Surprisingly, fifth is not defined, so this returns the fifth element in the list
`L`. `get-intervals(`

`tuning`)- A wrapper that extracts interval information given the tuning tuple.
`load-intervals-from-scala(`

`filename`)- Loads an explicit mapped scale from a SCALA .scl file.
`make-equal-temperament(`

`pitches`,`pseudo-octave-ratio`,`ref-freq`[,`ref-freq-offset`[,`ref-freq-pseudo-octave`]])- Creates a tuple that will allow
`tuning-freq`

to create the correct frequencies. `make-tuning(`

`intervals`,`ref-freq`[,`ref-freq-offset`[,`ref-freq-pseudo-octave`]])- Takes a list of intervals and creates a tuple necessary to compute the correct pitch frequency.
`ratio-to-cents(`

`interval-ratio`)- Conerts an interval ratio into musical cents.
`save-intervals-to-scala(`

`filename`,`interval-list`[,`scale-name`])- Takes a list of intervals and writes a SCALA .scl file.
`steps-of(`

`tuning`,`pitch-number`,`pseudo-octve`)- A wrapper to obtain the ``steps'' the Nyquist uses from directly calculating the frequency given the tuning system.
`tuning-freq(`

`tuning`,`pitch-number`,`pseudo-octave`)- Takes a tuple of tuning information, a pseudo-octave, a pitch number and returns the frequency of the pitch given the tuning tuple.

Unit tests for each of the core components can be found in `test.lsp`

. The unit test harness was written from scratch and suffers from the single flaw of not being able to intercept errors that were thrown by the functions it is unit-testing---this can be a future work in expanding Nyquist to have a unit-testing framework.

When developing, all the files are in a ``flat'' structure and unit tests are run locally on the files. When creating the package for deployment, a directory named `tuning`

ought to be made and all source and documentation files ought to be in that directory. The only file that should sit outside of `tuning`

is `tuning.lsp`

. The `*tuning-dir*`

variable must be set to `"tuning/"`

so that the other dependencies can be found when `tuning.lsp`

is loaded by the user.

I would like to thank Professor Roger Dannenberg for writing Nyquist, which is one of the more interesting programming tools for sound/music generation that I have used thus far. Indeed, I probably would not have the chance to write a system in LISP had this class not be taught in Nyquist/SAL. I would also like to thank the TAs for the time they spent in listening to my compositions. The Introduction to Computer Music class has been a most interesting class, and I daresay that it is the best way to end my undergraduate career this way.

*Last updated on 2021-03-19T16:43:15+0800.*