Skip to content

Commit

Permalink
Intermediate docs for arrays and various ranges
Browse files Browse the repository at this point in the history
  • Loading branch information
frostburn committed May 2, 2024
1 parent 5fcae80 commit 046001d
Showing 1 changed file with 74 additions and 2 deletions.
76 changes: 74 additions & 2 deletions documentation/intermediate-dsl.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This document describes the SonicWeave domain-specific language in detail.
Make sure to read [basic DSL](https://github.com/xenharmonic-devs/sonic-weave/blob/main/documentation/dsl.md) documentation to get a feel for the language first.

## Basic operation
SonicWeave is intended for designing musical scales so a fundamental concept is the current scale (accessible through `$`).
SonicWeave is intended for designing musical scales so a fundamental concept is the current scale (accessible through `$`). It is an ordered array of intervals.

### Pushing
The current scale starts empty (`$ = []`) and the basic action is to push intervals onto the scale.
Expand Down Expand Up @@ -644,4 +644,76 @@ However this allows us to use non-uniform shapes. `[[1, 2], [3, [4, 5]]] + [10,
#### Universal operation and preference
Domain-aware operators can be instructed to ignore domain using tildes.

TODO
An expression like `x ~op y` is roughly equivalent to
```c
domainOf(x)(linear(x) op linear(y))
```
while `x op~ y` prefers y
```c
domainOf(y)(linear(x) op linear(y))
```

With tilde wings on both sides `x ~op~ y` evaluates to a linear quantity unless both operands are logarithmic.

The *wings of preference* also tries to format the result similar to the preferred operand. `P5 ~+ 3/2` formats as `P12` while `P5 +~ 3/2` formats as `6/2` trying to preserve the denominator between 3/2 and 6/2.

## Arrays

### Literals
Array literals are formed by enclosing expressions inside square brackets e.g. `[1, 2, 1+2]` evaluates to `[1, 2, 3]`.

### Ranges
Ranges of integers are generated by giving the start and end points e.g. `[1..5]` evaluates to `[1, 2, 3, 4, 5]`.

To skip over values you can specify the second element `[1,3..10]` evaluates to `[1, 3, 5, 7, 9]` (the iteration stops and rejects after reaching `11`).

Reversed ranges require the second element `[5..1]` evaluates to the empty array `[]` but `[5,4..1]` equals `[5, 4, 3, 2, 1]` as expected.

### Harmonic segments
Segments of the (musical) harmonic series can be obtained by specifying the root and the harmonic of equivalence e.g. `4::8` evaluates to `[5/4, 6/4, 7/4, 8/4]`.

Reversed ranges work too e.g. `8::4` evaluates to `[7/8, 6/8, 5/8, 4/8]` and as a scale sounds exactly the same as `4::8` but the sounding directions is changed such that higher frequencies would be towards the left if arranged on a piano.

#### Subharmonic segments
Segments of the subharmonic series can be obtained by prefixing the segment with `/`, specifying the root and the subharmonic of equivalence e.g. `/8::4` evaluates to (frequency ratios) `[8/7, 8/6, 8/5, 8/4]`. Recall that larger subharmonics sound lower so the largest/lowest root pitch should come first if you wish the resulting scale to sound upwards.

### Enumerated chords
Chord enumerations take the first interval and use it as the implied root in a scale e.g. `2:3:5` evaluates to `[3/2, 5/2]`.

#### Reflected enumerations
By default enumeration assumes that we're dealing with frequency ratios. If you wish to specify ratios of wavelengths, prefix the enumeration with `/` e.g. `/6:5:4:3` evaluates to (frequency ratios) `[6/5, 6/4, 6/3]`.

In effect `/a:b:c` is shorthand for `1/a:1/b:1/c`. The stdlib `u()` helper offers an alternative spelling e.g. `u(6:5:4:3)` and pairs nicely with the overtonal `o()` e.g. `o(3:4:5:6)`.

#### Mixed enumerations
Plain enumerals and harmonic segments may be freely mixed e.g. `8::10:12:14::16` is the same as `8:9:10:12:14:15:16` i.e.
```c
9/8
10/8 // 5/4
12/8 // 3/2
14/8 // 7/4
15/8
16/8 // 2
```

### Array access
Use square brackets to access array elements. Indexing starts from zero. Negative indices count back from the end. `$[-1]` is a handy shorthand for the last element in the current scale.

#### Nullish access
Accessing an array out of bounds raises an exception. Javascript-like behavior is available using `~[]` e.g. `arr~[777]` evaluates to `niente` if the array doesn't have at least 778 elements.

#### Using an array of indices
To obtain a subset of an array use an array of indices e.g. `[1, 2, 3, 4][0, 2]` evaluates to `[1, 3]`.

#### Using an array of booleans
Another way to obtain a subset is to use an array of booleans. This works especially well with vectorized operators like `>` here:
```c
const smallPrimes = [2, 3, 5, 7, 11]
smallPrimes[smallPrimes > 4]
```
results in `$ = [5, 7, 11]`

### Slices
Range syntax inside array access gets a copy of a subset of the array e.g. `[1, 2, 3, 4, 5][2..4]` evaluates to `[3, 4, 5]`.

In slice syntax the end points are optional e.g. `[1, 2, 3][..]` evaluates to `[1, 2, 3]` (a new copy).

0 comments on commit 046001d

Please sign in to comment.