简体   繁体   中英

How do you create a tone generator whose tones can be manipulated “live” or on the fly in java?

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!

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM