package ddf.minim.ugens;
/**
* WavetableGenerator is a helper class for generating Wavetables.
* The method names come from <a href="http://www.cara.gsu.edu/courses/Csound_Users_Seminar/csound/3.46/CsGens.html">CSound</a>.
* Generally speaking, it will often be easier to use the static methods in the Waves class, but the methods
* in this class provide more flexibility.
*
* @related Wavetable
* @related Waves
*
* @author Mark Godfrey <mark.godfrey@gatech.edu>
*/
public class WavetableGenerator
{
// private constructor so it doesn't show up in documentation
// and so that instances of this class cannot be created.
private WavetableGenerator() {}
/**
* Generate a piecewise linear waveform given an array of sample values and the distances
* between them. The <code>dist</code> array should contain one value less than the <code>val</code>
* array. The values in the <code>dist</code> array should also add up to <code>size</code>. For instance, a
* call like this:
* <p>
* <code>Wavetable table = WavetableGenerator.gen7( 4096, new float[] { 1.0, -1.0, 1.0 }, new int[] { 2048, 2048 } );</code>
* <p>
* Would generate a Wavetable that was 4096 samples long and the values of those samples would start at 1.0,
* linearly decrease to -1.0 over 2048 samples, and then increase to 1.0 over the next 2048 samples.
* <p>
* If you wanted to generate a triangle wavetable with 4096 samples, you'd do this:
* <p>
* <code>Wavetable table = WavetableGenerator.gen7( 4069, new float[] { 0.0, 1.0, 0.0, -1.0, 0.0 }, new int[] { 1024, 1024, 1024, 1024 } );</code>
*
* @shortdesc Generate a piecewise linear waveform given an array of sample values and the distances
* between them.
*
* @param size
* int: the size of the Wavetable that you want generate
* @param val
* float[]: the sample values used as control points for generating the waveform
* @param dist
* int[]: the sample distances between control points in val
*
* @return a Wavetable
*
* @related Wavetable
*/
public static Wavetable gen7(int size, float[] val, int[] dist)
{
//System.out.println("gen7: " + size + ", " + val + ", " + dist);
float[] waveform = new float[size];
// check lengths of arrays
if (val.length - 1 != dist.length)
{
System.out.println("Input arrays of invalid sizes!");
return null;
}
// check if size is sum of dists
int sum = 0;
for (int i = 0; i < dist.length; i++)
{
sum += dist[i];
}
if (size != sum)
{
System.out.println("Distances do not sum to size!");
return null;
}
// waveform[0] = val[0];
int i = 0;
for (int j = 1; j < val.length && i < waveform.length; j++)
{
waveform[i] = val[j - 1];
float m = (val[j] - val[j - 1]) / (float)(dist[j - 1]);
for (int k = i + 1; k < i + dist[j - 1]; k++)
{
waveform[k] = m * (k - i) + val[j - 1];
}
i += dist[j - 1];
}
waveform[waveform.length - 1] = val[val.length - 1];
// for(int n = 0; n < waveform.length; n++)
// System.out.println(waveform[n]);
return new Wavetable(waveform);
}
/**
*
* Generates a Wavetable from a list of partials with matching amplitudes and phases. Partial, here, refers
* to a particular sine wave in the harmonic series (see: <a href="http://en.wikipedia.org/wiki/Harmonic_series_%28music%29#Harmonic_vs._partial">Harmonic vs. partial</a>).
* If you want to generate a single sine wave, suitable for playing a single tone of a particular frequency
* in an Oscil, you could use this code:
* <p>
* <code>Wavetable sine = WavetableGenerator.gen9(4096, new float[] { 1 }, new float[] { 1 }, new float[] { 0 });</code>
* <p>
* But what this method lets you do, is create a Wavetable that contains several different partials, each with
* a particular amplitude or phase shift. For instance, you could create a Wavetable that plays two pitches an octave
* apart like this:
* <p>
* <code>Wavetable octave = WavetableGenerator.gen9(4096, new float[] { 1, 2 }, new float[] { 1, 1 }, new float[] { 0, 0 });</code>
* <p>
* If this is something you want a particular instrument you write to do, then creating a Wavetable that already
* contains the octave and using that in an Oscil will be less computationally expensive than creating two Oscils
* and setting their frequencies an octave apart.
*
* @shortdesc Generates a Wavetable from a list of partials with matching amplitudes and phases.
*
* @param size
* int: how many samples the Wavetable should contain
* @param partial
* float[]: a list of partials to generate
* @param amp
* float[]: the amplitude of each partial
* @param phase
* float[]: the phase of each partial
*
* @return a Wavetable
*
* @related Wavetable
*
*/
// generates waveform from lists of partials
// phases are between 0 and 1
public static Wavetable gen9(int size, float[] partial, float[] amp, float[] phase)
{
if (partial.length != amp.length
|| partial.length != phase.length
|| amp.length != phase.length)
{
System.err.println("Input arrays of different size!");
return null;
}
float[] waveform = new float[size];
float index = 0;
for (int i = 0; i < size; i++)
{
index = (float)i / (size - 1);
for (int j = 0; j < partial.length; j++)
{
waveform[i] += amp[j]
* Math.sin(2 * Math.PI * partial[j] * index + phase[j]);
}
}
return new Wavetable(waveform);
}
/**
*
* Generate a Wavetable given a list of amplitudes for successive partials (harmonics). These two method
* calls are equivalent:
* <p>
* <code>Wavetable table = WavetableGenerator.gen9(4096, new float[] { 1, 2, 3 }, new float[] { 1, 0.5, 0.2 }, new float[] { 0, 0, 0 });</code>
* <p>
* <code>Wavetable table = WavetableGenerator.gen10(4096, new float[] { 1, 0.5, 0.2 });</code>
*
* @shortdesc Generate a Wavetable given a list of amplitudes for successive partials (harmonics).
*
* @param size
* int: the number of samples the Wavetable should contain
* @param amp
* float[]: the amplitude of each successive partial, beginning with partial 1.
*
* @return a Wavetable
*
* @see #gen9
* @related gen9 ( )
* @related Wavetable
*/
public static Wavetable gen10(int size, float[] amp)
{
float[] waveform = new float[size];
float index = 0;
for (int i = 0; i < size; i++)
{
index = (float)i / (size - 1);
for (int j = 0; j < amp.length; j++)
{
waveform[i] += amp[j] * Math.sin(2 * Math.PI * (j + 1) * index);
}
}
return new Wavetable(waveform);
}
}