Additive synthesis in Zebra

Official support for: u-he.com
RELATED
PRODUCTS

Post

Would it be possible to take a spectrograph such as this:

http://media.soundonsound.com/sos/oct03 ... rum1.l.jpg

and transpose it to Zebra?

I presume that the max. amplitude of partials in Spectroblend mode follows the 1/n rule (so that the second partial's max amplitude is 1/2 of the fundamental's, the third 1/3.. etc), whereas the partials on a spectrograph are all measured relative to the same amplitude, which would make transposition by eye difficult.

Any ideas?

Post

That sounds exactly like the task for Spectroblend mode.

Post

Only if there's some way of getting a reasonable approximation for the relative amplitudes of the partials, and if they're quite different scales that might be rather difficult. :shrug:

Post

There aren't enough markings on the graph to go on... If it's a flute I'm guessing those are odd harmonics. From there I don't think it's much of a stretch to try copying that graph into the spectroblend editor, making sure to leave exactly one gap between each bar. The spectroblend editor IS famously fiddly to edit, though... I find it helps if you change the GUI size to something much larger.
http://sendy.bandcamp.com/releases < My new album at Bandcamp! Now pay what you like!

Post

Sendy wrote:There aren't enough markings on the graph to go on... If it's a flute I'm guessing those are odd harmonics.
Here's the text that goes with the spectrum:

"Figure 7 (above) shows the spectrum of a low note such as the bottom 'C'.
Played with moderate force, it is rich in harmonics, with strong contributions from the second, third and fourth harmonics, followed by a reasonably '1/n'-like shape from the fourth upward, until the spectrum disappears"
From there I don't think it's much of a stretch to try copying that graph into the spectroblend editor, making sure to leave exactly one gap between each bar.

You're missing the point about the relative maximum amplitudes of the partials in Spectroblend mode - they seem to follow a 1/n rule such that the max amplitude of the second harmonic is 1/2 of the first, the third is 1/3, and so on. In other words, the amplitude scale is different for each partial.

This does not appear to be the case with the graph for the flute spectrum - here the amplitudes of the partials are measured against the same scale.

It is this difference in scale that makes even approximating that particular spectrum in Spectroblend difficult.

Post

hakey wrote:Would it be possible to take a spectrograph such as this:

http://media.soundonsound.com/sos/oct03 ... rum1.l.jpg

and transpose it to Zebra?

I presume that the max. amplitude of partials in Spectroblend mode follows the 1/n rule (so that the second partial's max amplitude is 1/2 of the fundamental's, the third 1/3.. etc), whereas the partials on a spectrograph are all measured relative to the same amplitude, which would make transposition by eye difficult.

Any ideas?
A bit in a rush .. dunno if I understood correctly but you can approximately draw in the partials using the OSC spectroblend mode and use OSCFX like expander or scale or a combination of both. .... sync also helps I guess as it "transposes" harmonically.

Post

hakey wrote: You're missing the point about the relative maximum amplitudes of the partials in Spectroblend mode - they seem to follow a 1/n rule such that the max amplitude of the second harmonic is 1/2 of the first, the third is 1/3, and so on. In other words, the amplitude scale is different for each partial.
..hmm I think I get what you are saying but unfortunately I don't know a way around this. ....or some clever combination using the choplift OSCFX? :)

Post

Oh well, I guess it's just not possible, at least not with the kind of accuracy required to do it justice.

Post

It's quite possible and in fact pretty simple once you know the trick. :)
Leveraging on billstei's hard work with blueberry thing I spent some time examining his GNU Octave scripts. At the end of the day you have a template that looks like:

Code: Select all

#defaults=no
#patchname=no
<?

int OSC_WAVEFORM_SPECTROBLEND = 3;
int i;

float w1[128];

w1[0]=1.000;
.......
w1[127]=0.002;

for( i = 1; i <= 1; i++ )
{
  if (Oscillator[i].WaveForm == OSC_WAVEFORM_SPECTROBLEND)
  {
    Oscillator[i].WaveTable.set(1,w1);
  }
}

?>
I wrote a simple python program that generates the sequence you mentioned, 1/1, 1/2, 1/3...1/128:

Code: Select all

#!/usr/bin/env python

import sys

fmt_head = """
#defaults=no
#patchname=no
<?

int OSC_WAVEFORM_SPECTROBLEND = 3;
int i;

float w1[128];
"""

fmt_el = "w1[%d]=%.03f; "

fmt_foot = """

for( i = 1; i <= 1; i++ )
{
  if (Oscillator[i].WaveForm == OSC_WAVEFORM_SPECTROBLEND)
  {
    Oscillator[i].WaveTable.set(1,w1);
  }
}

?>
"""[:-1]

if __name__ == "__main__":
   w = sys.stdout.write
   w(fmt_head)
   for n in range(0,128):
       partial = 1.0 / (n + 1)
       w(fmt_el % (n, partial))
       if n > 0 and n % 8 == 0:
           w("\n")
   w(fmt_foot)
Which generates output:

Code: Select all

#defaults=no
#patchname=no
<?

int OSC_WAVEFORM_SPECTROBLEND = 3;
int i;

float w1[128];
w1[0]=1.000; w1[1]=0.500; w1[2]=0.333; w1[3]=0.250; w1[4]=0.200; w1[5]=0.167; w1[6]=0.143; w1[7]=0.125; w1[8]=0.111;
w1[9]=0.100; w1[10]=0.091; w1[11]=0.083; w1[12]=0.077; w1[13]=0.071; w1[14]=0.067; w1[15]=0.062; w1[16]=0.059;
w1[17]=0.056; w1[18]=0.053; w1[19]=0.050; w1[20]=0.048; w1[21]=0.045; w1[22]=0.043; w1[23]=0.042; w1[24]=0.040;
w1[25]=0.038; w1[26]=0.037; w1[27]=0.036; w1[28]=0.034; w1[29]=0.033; w1[30]=0.032; w1[31]=0.031; w1[32]=0.030;
w1[33]=0.029; w1[34]=0.029; w1[35]=0.028; w1[36]=0.027; w1[37]=0.026; w1[38]=0.026; w1[39]=0.025; w1[40]=0.024;
w1[41]=0.024; w1[42]=0.023; w1[43]=0.023; w1[44]=0.022; w1[45]=0.022; w1[46]=0.021; w1[47]=0.021; w1[48]=0.020;
w1[49]=0.020; w1[50]=0.020; w1[51]=0.019; w1[52]=0.019; w1[53]=0.019; w1[54]=0.018; w1[55]=0.018; w1[56]=0.018;
w1[57]=0.017; w1[58]=0.017; w1[59]=0.017; w1[60]=0.016; w1[61]=0.016; w1[62]=0.016; w1[63]=0.016; w1[64]=0.015;
w1[65]=0.015; w1[66]=0.015; w1[67]=0.015; w1[68]=0.014; w1[69]=0.014; w1[70]=0.014; w1[71]=0.014; w1[72]=0.014;
w1[73]=0.014; w1[74]=0.013; w1[75]=0.013; w1[76]=0.013; w1[77]=0.013; w1[78]=0.013; w1[79]=0.013; w1[80]=0.012;
w1[81]=0.012; w1[82]=0.012; w1[83]=0.012; w1[84]=0.012; w1[85]=0.012; w1[86]=0.011; w1[87]=0.011; w1[88]=0.011;
w1[89]=0.011; w1[90]=0.011; w1[91]=0.011; w1[92]=0.011; w1[93]=0.011; w1[94]=0.011; w1[95]=0.010; w1[96]=0.010;
w1[97]=0.010; w1[98]=0.010; w1[99]=0.010; w1[100]=0.010; w1[101]=0.010; w1[102]=0.010; w1[103]=0.010; w1[104]=0.010;
w1[105]=0.009; w1[106]=0.009; w1[107]=0.009; w1[108]=0.009; w1[109]=0.009; w1[110]=0.009; w1[111]=0.009; w1[112]=0.009;
w1[113]=0.009; w1[114]=0.009; w1[115]=0.009; w1[116]=0.009; w1[117]=0.008; w1[118]=0.008; w1[119]=0.008; w1[120]=0.008;
w1[121]=0.008; w1[122]=0.008; w1[123]=0.008; w1[124]=0.008; w1[125]=0.008; w1[126]=0.008; w1[127]=0.008;

for( i = 1; i <= 1; i++ )
{
  if (Oscillator[i].WaveForm == OSC_WAVEFORM_SPECTROBLEND)
  {
    Oscillator[i].WaveTable.set(1,w1);
  }
}

?>
Now if you save that as a regular zebra preset. Next change OSC1 mode to Spectroblend. Now load the preset in in Zebra you'll see the current preset has changed and the spectroblend waveform 1 to your harmonic sequence:
Image

It's quite simple to extrapolate on this to generate all sorts of programatic partial sequences. You can even run them by hand if you're really serious about getting that sequence exact.

---

Notes: I have no idea about the precision of Zebra so I stuck with billstei's usage of 4 significant digits. I also did not invert the wave into the negative domain but it would be possible to do so. With a little knowledge of programming you should be able to make reasonable guesses as to what to change in the .h2p script to set OSC2 or waveform 3 or even change the type.

Is's also likely that the actually generation of the values 1/n can be done by the .h2p scripting interface; I just used python because I know the syntax and was able to quickly copy billstei's wavetable template.

---

billstei, if you're still around KVR I miss your wacky and informative posts. Please come back. :)
Last edited by bmrzycki on Sun Sep 25, 2011 10:50 pm, edited 1 time in total.

Post

Ok, I opened the original image you had shown with Paint.NET and treated the tallest harmonic as being 1.0. Measuring the length of the red bars I got the following (resolution in pixels):

1 = 142 / 160 = 0.887
2 = 160 / 160 = 1.0
3 = 150 / 160 = 0.938
4 = 160 / 160 = 1.0

I then modified the main loop of the python program:

Code: Select all

if __name__ == "__main__":
   w = sys.stdout.write
   w(fmt_head)
   # Specify first 3 partials by hand (zero indexed)
   w(fmt_el % (0, 142.0/160))
   w(fmt_el % (1, 160.0/160))
   w(fmt_el % (2, 150.0/160))
   w("\n")
   # Partials 4 - 128 follow the 1/n algorithm
   for n in range(3,128):
       partial = 1.0 / (n - 2)
       w(fmt_el % (n, partial))
       if n > 0 and n % 8 == 0:
           w("\n")
   w(fmt_foot)
And generated a new preset:

Code: Select all

#defaults=no
#patchname=no
<?

int OSC_WAVEFORM_SPECTROBLEND = 3;
int i;

float w1[128];
w1[0]=0.887; w1[1]=1.000; w1[2]=0.938;
w1[3]=1.000; w1[4]=0.500; w1[5]=0.333; w1[6]=0.250; w1[7]=0.200; w1[8]=0.167;
w1[9]=0.143; w1[10]=0.125; w1[11]=0.111; w1[12]=0.100; w1[13]=0.091; w1[14]=0.083; w1[15]=0.077; w1[16]=0.071;
w1[17]=0.067; w1[18]=0.062; w1[19]=0.059; w1[20]=0.056; w1[21]=0.053; w1[22]=0.050; w1[23]=0.048; w1[24]=0.045;
w1[25]=0.043; w1[26]=0.042; w1[27]=0.040; w1[28]=0.038; w1[29]=0.037; w1[30]=0.036; w1[31]=0.034; w1[32]=0.033;
w1[33]=0.032; w1[34]=0.031; w1[35]=0.030; w1[36]=0.029; w1[37]=0.029; w1[38]=0.028; w1[39]=0.027; w1[40]=0.026;
w1[41]=0.026; w1[42]=0.025; w1[43]=0.024; w1[44]=0.024; w1[45]=0.023; w1[46]=0.023; w1[47]=0.022; w1[48]=0.022;
w1[49]=0.021; w1[50]=0.021; w1[51]=0.020; w1[52]=0.020; w1[53]=0.020; w1[54]=0.019; w1[55]=0.019; w1[56]=0.019;
w1[57]=0.018; w1[58]=0.018; w1[59]=0.018; w1[60]=0.017; w1[61]=0.017; w1[62]=0.017; w1[63]=0.016; w1[64]=0.016;
w1[65]=0.016; w1[66]=0.016; w1[67]=0.015; w1[68]=0.015; w1[69]=0.015; w1[70]=0.015; w1[71]=0.014; w1[72]=0.014;
w1[73]=0.014; w1[74]=0.014; w1[75]=0.014; w1[76]=0.014; w1[77]=0.013; w1[78]=0.013; w1[79]=0.013; w1[80]=0.013;
w1[81]=0.013; w1[82]=0.013; w1[83]=0.012; w1[84]=0.012; w1[85]=0.012; w1[86]=0.012; w1[87]=0.012; w1[88]=0.012;
w1[89]=0.011; w1[90]=0.011; w1[91]=0.011; w1[92]=0.011; w1[93]=0.011; w1[94]=0.011; w1[95]=0.011; w1[96]=0.011;
w1[97]=0.011; w1[98]=0.010; w1[99]=0.010; w1[100]=0.010; w1[101]=0.010; w1[102]=0.010; w1[103]=0.010; w1[104]=0.010;
w1[105]=0.010; w1[106]=0.010; w1[107]=0.010; w1[108]=0.009; w1[109]=0.009; w1[110]=0.009; w1[111]=0.009; w1[112]=0.009;
w1[113]=0.009; w1[114]=0.009; w1[115]=0.009; w1[116]=0.009; w1[117]=0.009; w1[118]=0.009; w1[119]=0.009; w1[120]=0.008;
w1[121]=0.008; w1[122]=0.008; w1[123]=0.008; w1[124]=0.008; w1[125]=0.008; w1[126]=0.008; w1[127]=0.008;

for( i = 1; i <= 1; i++ )
{
  if (Oscillator[i].WaveForm == OSC_WAVEFORM_SPECTROBLEND)
  {
    Oscillator[i].WaveTable.set(1,w1);
  }
}

?>
Which looks like:
Image

Post

hakey wrote:You're missing the point about the relative maximum amplitudes of the partials in Spectroblend mode - they seem to follow a 1/n rule such that the max amplitude of the second harmonic is 1/2 of the first, the third is 1/3, and so on. In other words, the amplitude scale is different for each partial.

This does not appear to be the case with the graph for the flute spectrum - here the amplitudes of the partials are measured against the same scale.

It is this difference in scale that makes even approximating that particular spectrum in Spectroblend difficult.
Of course :dog:, in my own words "Spectroblend is a producer of filtered sawtooths"... I'm wondering if a seperate mode where you can set absolute harmonic levels might be possible/useful for Zebra? When playing with it by manually drawing, it can sometimes be hard to get away from that saw sound, which is why I'm usually more drawn to the Geo modes, at least for animated/interesting sounds.

There is a spectral FX that scales the levels of the harmonics in a very controlled way, I'm wondering if the right setting on that could be used to bring the volume of the harmonics to a straight level.

Anyway, I think bmrzycki wins the round with his posts :lol: :help:
http://sendy.bandcamp.com/releases < My new album at Bandcamp! Now pay what you like!

Post

Sendy wrote:I'm wondering if a seperate mode where you can set absolute harmonic levels might be possible/useful for Zebra?
This gets +100 from me!


And also the possibility to zoom in the waveform editor!

Post

Sendy wrote:Of course :dog:, in my own words "Spectroblend is a producer of filtered sawtooths"...
I never noticed this before, but you're absolutely right (hakey as well). I tried using the Scale FX to adjust the harmonic levels, but it doesn't work well (scale does multiplication on harmonics where the shift here sounds logarithmic).

If Urs can give me the exact volume adjustment per harmonic I can easily adjust for it in my script.

Of course, I'd really love this to be a new mode (HarmoMorph and HarmoBlend?) and +1 to a larger waveform editor like the mmaps.

Sendy wrote:Anyway, I think bmrzycki wins the round with his posts :lol: :help:
:oops: I hope I didn't geek out too much there, I'm a programmer first and a (poor) amateur musician second...

Post

HarmoMorph and HarmoBlend sound nice :)

Post

bmrzycki wrote:
Sendy wrote:Anyway, I think bmrzycki wins the round with his posts :lol: :help:
:oops: I hope I didn't geek out too much there
Haha! No, it was all good stuff, even though slightly off target. :)
If Urs can give me the exact volume adjustment per harmonic I can easily adjust for it in my script.

Pretty sure it's just 1/n for nth partial, so to reverse it double the 2nd partial, triple the third etc.

Post Reply

Return to “u-he”