Edward Eddy67716
Edward Eddy67716

Reputation: 69

Problems with formula to output a triangle waveform in Java

I got my wave file creator working and I split it into three classes, I also made a sinewave generator that inherits from an abstract class called waveform and I can export 8 and 16 bit mono or stereo sine waves. I am trying to make a class called TriangleWave Generator to output a triangle wave tone, but I can't get the algebra from https://en.wikipedia.org/wiki/Triangle_wave#, the first formula, to work. It will only export the highest harmonic stated and not blend them together with the fundamental

Sample length: length in seconds point: individual sample amp limit: the highest position possible harmonics: the number of harmonics to use to make the waveform 1 = fundamental, 2 = 1st overtone, 3 = 2nd overtone....... frequency: fundamental frequency (Middle C = 261.63) sample rate = 44100; (CD quality) triangle Samples array: sample data

This is my code

public class TriangleGenerator extends Waveform {
    
    // constants
    public static final int HARMONIC_COUNT = 16;

    // instance variabls
    int harmonics;
    int[] triangleSample;
    int addCount;

    // constructor
    public TriangleGenerator(double amplitude, double frequency, int bitRate, double duration, int harmonics) {
        super(amplitude, frequency, bitRate, duration);
        // sample data
        triangleSample = new int[sampleLength];
        calculateAmpLimit();
        this.harmonics = harmonics;
    }

    // one arg cunstructor
    public TriangleGenerator(double frequency) {
        this(AMPLITUDE, frequency, BIT_RATE, DURATION, HARMONIC_COUNT);
    }

    // no args constructor
    public TriangleGenerator() {
        this(AMPLITUDE, FREQUENCY, BIT_RATE, DURATION, HARMONIC_COUNT);
    }

    @Override
    public int[] generateWaveForm() {
        
        // generate the actual waveform
        for (int i = 0; i < sampleLength; i++) {
            point = (int)(ampLimit * ((8 / Math.pow(Math.PI, 2)) * sumnate(harmonics - 1, Math.pow(-1, addCount))
                    * Math.pow(harmonics, -2) * Math.sin(2 * Math.PI * frequency * harmonics * i / SAMPLE_RATE)));
            triangleSample[i] = point;
        }

        // return the sample data
        return triangleSample;
    }
    
    public double sumnate(int n, double adder) {
        double sum = 0;
        
        for (addCount = 0; addCount <= n; addCount++) {
            sum += adder;
        }
        
        return sum;
    }
}

Upvotes: 3

Views: 792

Answers (2)

Bouthaina
Bouthaina

Reputation: 1

Is there a reason you want to use sin to do this rather than produce the straight lines directly by a linear formula? There is an advantage in using sin in that you don't get harmonic distortion from aliasing, but if that's an issue then you can oversample. The issue is that sin is much slower than basic arithmetic.

Upvotes: 0

Topaco
Topaco

Reputation: 49276

In the formula for the triangle wave:

the mode number n is dependent on the harmonic label i:

This means that it must also be summed over the components

which doesn't happen in the current implementation. One possible implementation is:

public int[] generateWaveForm() {
    for (int t = 0; t < sampleLength; t++) {
        triangleSample[t] = (int)(ampLimit * 8.0 / Math.pow(Math.PI, 2.0) * getDataPoint(t, N));
    }
    return triangleSample;
}

private double getDataPoint(int t, int N) {
    double sum = 0;
    for (int i = 0; i <= N - 1; i++) {
        sum += getHarmonicShare(t, i);
    }
    return sum;
}

private double getHarmonicShare(int t, int i) {
double n = 2.0 * i + 1.0;
    return Math.pow(-1.0, i) * Math.pow(n, -2.0) * Math.sin(2.0 * Math.PI * frequency * (t / SAMPLE_RATE) * n);
}

Here t, i, n and N correspond to the values from the formula. frequency denotes the frequency. The remaining values correspond to the parameters of the posted code.

The curve reaches up to the value sampleLength / SAMPLE_RATE. The period is 1 / frequency and there are (sampleLength / SAMPLE_RATE) * frequency periods in the displayed frame.

Example:

sampleLength:   500
ampLimit:       100.00
SAMPLE_RATE:  44100.00
frequency:      261.63

sampleLength / SAMPLE_RATE:               0.0113378685
1 / frequency:                            0.0038221916
(sampleLength / SAMPLE_RATE) * frequency: 2.9663265306

In the following the corresponding curve is shown for different N using JFreeChart:

enter image description here

Upvotes: 2

Related Questions