 # IngoognI

A blog.

ANS is a optoelectronic sound synthesizer invented by Yevgeny Murzin. He named his device after Alexander Nikolayevich Scriabin (ANS)

## ANS Ingo 2021-04-19

Joseph Fourier stated that a signal can be broken down in a series of sine waves. This is the base of ANS, where the opposite is done. The amplitudes of many sine waves of different frequencies are modulated. The results are then added to get one signal.

With modern technology this can all be done with our computers. Images can be converted to sound. Inverse Fourier transforms make it a relative quick process. There is software that can do this, Virtual ANS for example.

In POV-Ray there is no FFT, so I have to use brute force. That keeps thing simple. But first,

### A word of warning

Experiments with sound synthesis can create extremely loud and nasty sounds.

Check the resulting waveform in an audio editor before listening. Audacity or Ocenaudio for example. Both support multiple platforms.

### Frequencies

Instead of scanning images, I'm going to sample functions created in POV-Ray. These can be rendered to sound and to an image. The sampled region is `[<0,0>, <1,1>]`. The y-axis represents the frequencies and the x-axis is time.

#### Distribution

The y-axis are frequencies sounds simple, but there is a bit more to that. Each position on the y-axis is actually a sine wave oscillator that emits a single frequency. The amplitude of that frequency is changed depending on the outcome of the functions that we'll sample.

The distribution of the oscillators along the y-axis is not equidistant.

`octaves = log2 ( f1/f2 )`

The equation above shows the relation between octaves and frequencies. Based on that relation positions of oscillators (frequencies) on the y-axis can be interpolated:

```#declare Pos2Freq = function(Pos, Min, Max){
(Max-Min)
* (Min * pow(2, Pos * (log(Max) - log(Min)) / log(2)) - Min)
/ (Min * pow(2, (log(Max) - log(Min)) / log(2)) - Min) + Min
}```

Pos:
The position on between min and max. The position is given in a range [0,1].
Min:
The minimum frequency of the range.
Max:
The maximum frequency of the range.

#### The Range

Now the way to distribute the frequencies is implemented let's look the minimum and maximum frequency values. The starting point is the range of frequencies to cover. The number of octaves. Then decide on how many bands the octaves have to be divided in.

With these we calculate the vertical resolution. The amount of oscillators that are needed. It's just as with images. The higher the resolution the better the quality.

Then set a minimum frequency and calculate the maximum. You can of course go from 20Hz to 20kHz but effects from the sound function can result in values outside that range. Going over the Nyquest frequency (halve the sample frequency) can have unwanted results.

```#declare Octaves = 9;
#declare OctaveRes = 60;
#declare Vres = Octaves * OctaveRes;
#declare Fmin = 30;
#declare Fmax = pow(2, Octaves) * Fmin;```

#### No array

Ideally one would pre-calculate the frequencies once for the oscillators and put them in an array. The thing is that POV-Ray does not support arrays in functions.

Splines are supported in function and can be used as arrays, even if it is not their intended purpose. It works fine.

```#declare FreqPos = function {
spline{
#for(i, 0, Vres)
i, <Pos2Freq(i / Vres, Fmin, Fmax), 0>,
#end
}
}
#undef i```

### Sampling

The x-axis is the time line. For now I have chosen to let the time line go from 0 to 1, regardless of how long the track will be. Simmilar to how we deal with animations in POV-Ray with a clock going to one instead of counting frames. I might change my mind on that.

The straight forward way is to set a `Dur`ation and multiply it by the `SampleRate`. The result is the amount of `Samples`, or `Tick`s that are needed.

With `Samples` and `SampleRate` the usual tick until `Samples` loop can be build and the data from the inner function(s) can be written to a wave file.

#### The sampling function

For every `Tick` on the time line we have to sample every oscillator on the frequency axis. For every oscillator on the frequency axis we have to sample the sound function and use the result of that as amplitude of the oscillator. Then all the oscillator results for this whole column have to be added.

POV-Ray has a nice sum() function for that. This I used to do all the column related operations.

```#declare ColVal = function(Vr, nSamples, Tick, SampleRate){
// #for(i, 0, Vr, 1) sum(SinOsc(.i...)) #end
sum(
i, 0, Vr,
SinOsc(
FreqPos(i).x,
0,
fSound(Tick/nSamples, i/Vr, 0)/Vr,
Tick,
SampleRate
)
)
};```

Vr:
The vertical resolution, actually the number of frequency bands to be generated. (I should change this to FreqRes)
nSamples:
The total of samples to be generated
Tick:
One step that adds a sample
SampleRate
Samples per second of digital audio

Note: The result of `fSound()` is divided by the number of samples. As the sine waves are not coherent a division by `pow(Vr, 0.5)` may suffice. There could be a problem with that at the very beginning of the sound when the `Phase` of the sine waves is not randomised. Something to investigate.

#### The sound function

Inside the above `ColVal` function lives the sine oscillator. Inside that lives the sound function `fSound`

First lets get the good old SinOsc and have a look at that:

```#declare SinOsc = function(Freq, Phase, Amp, Tick, SRate){
Amp * sin((Tick * TAU * (Freq/SRate)) + Phase)
};```

The first parameter is the frequency. That we get from the spline function `FreqPos()` we defined earlier. We leave the phase at 0.

Note: While creating the spline the y-value of the vector could be randomised between -pi and pi. This value could be used for the `Phase`. Then not all sine oscillators would start at zero.

The Amplitude. Now it gets interesting. Here we put the sound function and it has to return a single value. When using a colour function a `.gray` after the function: `fSound(Tick/nSamples, i/Vr, 0).gray/Vr`

Anything that POV-Ray allows inside functions can be used. Or rebuild parts of this to use macros and use the `trace` function to sample a scene.

## Zugabe

While fiddling with all this it occurred to me that a function set can be created that is a additive sine oscillator. Again, based on the spline:

```#declare FreqPos = function{
spline{
0,   <120,0,1>
0.5, <250,0,1>
1,   <500,0,1>
}
}

#declare SinAddOsc = function(FreqRes, Amp, Tick, SampleRate) {
Amp * sum(
i, 0, FreqRes,
SinOsc(
FreqPos(i/FreqRes).x,
FreqPos(i/FreqRes).y,
FreqPos(i/FreqRes).z/FreqRes,
Tick,
SampleRate
)
)
};```

The spline defines the frequency, phase and amplitude of the individual oscillators. You can stick strictly to the defined points. Yet, the nice feature that the spline adds is the interpolation between the defined points. For example, only change the middle t value form 0.5 to 0.2. And of course instead of `sum` `prod` can be used, but remove the division by `FreqRes` of the amplitude.