Klein Bottle art work

IngoognI

A blog.

Perlin and the BottleIngo 2021-04-07

As Wave Terrain Synthesis is an expansion of a wave table, it self can be expanded into tree dimensions. Let's call it Wave Space Synthesis. But first,

A word of warning

MIND YOUR EARS!

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

Don't use headphones! Turn your amps down! Build attenuators into your scripts!

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

Sound scanners

Most textures, pigments in POV-Ray extend into three dimensions. For sound generation they can be samples with a two dimensional shape. They can also be sampled with three dimensional shapes. Splines are perfect for this, but there are other options.

Three dimensional parametric functions spring to mind and one of my favourites is the Klein Bottle. It is the reason why I wrote the mesh maker macro's and parametric include file two decades ago.

The Klein Bottle

The Bottle is a single surface, without boundaries and without discontinuities. Its parametric function in POV-Ray SDL:

Fries of Klein Bottles
//Klein-bottle
#declare Rk = function(u){4*(1-cos(u)/2)}
#declare Fx = function(u,v){ 
  select(
    pi - u,
    (6*cos(u)*(1+sin(u))+(Rk(u)*cos(v+pi))),  
    (6*cos(u)*(1+sin(u))+(Rk(u)*cos(u)*cos(v)))
  )
}
#declare Fy = function(u,v){ 
  select(
    pi - u,
    (16*sin(u)),  
    (16*sin(u)+(Rk(u)*sin(u)*cos(v)))
  )
}
#declare Fz = function(u,v){Rk(u)*sin(v)}

By progressing the parameters u and v Tick by tick from 0 to pi. For this I created a new oscillator based on the Ramp oscillator, the Phasor. It has a user settable range. Phasor, is added to the povosc.inc file:

#declare Phasor = function(Freq, Phase, Min, Max, Tick, SRate){
  adj_range2(Ramp(Freq, Phase, Tick, SRate), 0, 1, Min, Max)
}

Now we can scan the intersection of a function in space and the Klein Bottle. But what to scan?

Perlin noise

Noise in all kind of colours, white, pink, brown, black have many uses in sound synthesis. But for this application they have a problem that makes the useless. They are not deterministic. They do not always have the same value at the same position is space.

Perlin noise to the rescue. It's not only deterministic, it's also smooth and it is available in POV-Ray. Set noise_generator 3 in the global_settings or use the f_noise_generator() from math.inc and set its fourth parameter to 3.

Make noise

With the basics set up, lets make noise. After declaring all the variables, the recording loop looks and sounds like:

#declare Tick = 0.0;
#fopen WaveOutFile Path write
  #declare PadByte = WriteWaveHeader(Dur, 1, BitDepth, SampleRate);
  #while(Tick < Samples)

    #local U = Phasor(UFreq, 0, 0, TAU, Tick, SampleRate);
    #local V = Phasor(VFreq, 0, 0, TAU, Tick, SampleRate);
    
    #declare Sample = (
      f_noise_generator(Fx(U,V), Fy(U,V), Fz(U,V), 3) - 0.5
    ) * Amp * C;
    
    #write (WaveOutFile, sint16le Sample)
    #declare Tick = Tick + 1;
  #end
  #if (PadByte = 1)
    #write (WaveOutFile, uint8 0)
  #end
#fclose WaveOutFile 

The full source file is in POVSound.zip

There are many ways to explore this all. One I tried is using the five dimensions of a spline as parameters to control the functions (file is in the zip). Even the FFT image looks wild

FFT of spline parameters

An other way to change the sounds is by transforming the bottle. Use matrices to squish or stretch it. Go funky!

#declare TransBottle = function{
  transform {
    scale<1/ScaleX, 1/ScaleY, 1/ScaleZ>
  } 
}

// inside the sampling loop:
#local P = TransBottle(Fx(U,V),Fy(U,V),Fz(U,V));    
#declare Sample =(f_noise_generator(P.x, P.y, P.z, 3)-0.5) * Amp * C;

More

There's more than Perlin noise to sample. How about crackle, or a turbulent wood. Just make sure the functions results are within the [-1,1] range.

Also, it does not have to be the Klein Bottle. Really, there's more. In the download section there's my old mesh macro include. It has several useful shapes in the demo section that can easily be adapted. The Supertoroid for example:

#declare R0=1;     //major radius
#declare R1=0.35;  //minor radius
#declare T=2.5;    //shape
#declare S=1;

#declare Fx=function(u,v){
  sgn(
    cos(u))*pow(abs(cos(u)),T)*
    (R0+R1*sgn(cos(v))*pow(abs(cos(v)),S)
  )
}
#declare Fy=function(u,v){
  sgn(
    sin(u))*pow(abs(sin(u)),T)*
    (R0+R1*sgn(cos(v))*pow(abs(cos(v)),S)
  )
}
#declare Fz=function(u,v){
  R1*sgn(sin(v))*pow(abs(sin(v)),S)
}