2

I would like to make a basic tone generator in java which can be manipulated in real time (just pitch to start off with).

I would like to start simple and then add on more complicated tone generation and effects to end up with some kind of basic synthesizer.

I found a helpful post on this site featuring some sample code in an applet Beeper.java.

It generated a tone and saved it to a clip. It then played that clip back on a loop when required. Relevant tone generation bit:

/** Generates a tone, and assigns it to the Clip. */
public void generateTone()
    throws LineUnavailableException {
    if ( clip!=null ) {
        clip.stop();
        clip.close();
    } else {
        clip = AudioSystem.getClip();
    }
    boolean addHarmonic = harmonic.isSelected();

    int intSR = ((Integer)sampleRate.getSelectedItem()).intValue();
    int intFPW = framesPerWavelength.getValue();

    float sampleRate = (float)intSR;

    // oddly, the sound does not loop well for less than
    // around 5 or so, wavelengths
    int wavelengths = 20;
    byte[] buf = new byte[2*intFPW*wavelengths];
    AudioFormat af = new AudioFormat(
        sampleRate,
        8,  // sample size in bits
        2,  // channels
        true,  // signed
        false  // bigendian
        );

    int maxVol = 127;
    for(int i=0; i<intFPW*wavelengths; i++){
        double angle = ((float)(i*2)/((float)intFPW))*(Math.PI);
        buf[i*2]=getByteValue(angle);
        if(addHarmonic) {
            buf[(i*2)+1]=getByteValue(2*angle);
        } else {
            buf[(i*2)+1] = buf[i*2];
        }
    }

    try {
        byte[] b = buf;
        AudioInputStream ais = new AudioInputStream(
            new ByteArrayInputStream(b),
            af,
            buf.length/2 );

        clip.open( ais );
    } catch(Exception e) {
        e.printStackTrace();
    }
}

Loop bit:

/** Loops the current Clip until a commence false is passed. */
public void loopSound(boolean commence) {
    if ( commence ) {
        clip.setFramePosition(0);
        clip.loop( Clip.LOOP_CONTINUOUSLY );
    } else {
        clip.stop();
    }
}

I tried to wrangle this so that I would create another clip in the background and quickly change one for the other when I wanted to change pitch but of course there is a noticeable click as one clip starts and the other stops.

So I'm guessing I need some kind of clever buffering to do this that matches the end of one wave seamlessly with another?

Or is it just not possible with a pre-generated clip? If so how should I go about this?

As a relevant aside, how do software synthesizers work? Do they generate all their sounds and effects continuously or do they pregenerate "clips" and loop like Beeper.java?

Thanks!

Community
  • 1
  • 1
kiman
  • 359
  • 5
  • 13

1 Answers1

1

It sounds like you want to implement a numerically controlled oscillator (NCO), implemented with a phase accumulator, in Java.

Basically, you need to figure out the delta-phase for the frequency you want, and then keep adding this, modulo 2pi, to the accumulator. The value of the accumulator is used as the value to sin() to generate the sample value.

When you want to change frequency, you update the delta-phase. This ensures continuity in the samples (no abrupt breaks in the wave). I suspect this is what was causing your clicks. If you want better changes, then you need to gradually change the delta-phase over a bunch of samples.

mpdonadio
  • 2,891
  • 3
  • 35
  • 54
  • Thanks, I guess I understand what you are saying but it's all a bit above my head really. Is this something that I should do using clips or should I be continually generating the tone and playing back instantly? – kiman Apr 28 '12 at 17:23
  • I think if you want to not have discontinuities, clips may be tricky. Arbitrary frequencies will not have "nice" numbers of samples that correspond to exactly one wave cycle. – mpdonadio Apr 28 '12 at 17:32
  • Yeah I thought so... You couldn't point me to some basic java code using standard apis that could illustrate "live" generation and playback of a sine wave could you?! :) – kiman Apr 29 '12 at 02:39
  • well I've found some java examples and I've tried playing about with the code but I've realised that I just don't understand the basics of what I'm trying to do! If you know of any basic tutorials on this subject (ie not for Maths majors) that would be really helpful! – kiman May 04 '12 at 17:08