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!