This is only to complement the already provided and accepted answer (which I gave +1).
You can use wavetables as an alternative to triggering trigger functions on the fly - sorting half-sampled / half-synthesized. I also use a sine wave table with six independent cursors pointing to it for FM synthesis, and duplicated several Yamaha DX7 patches this way. But all this is done through javax.sound.sampled. Once a software synthesizer has been created, you can control it using the midi library classes.
Suppose you populate a 1K array with float for a single sine wave.
If you “play” the wave table, increasing and looping it and extracting each member of the array in turn (for recording on a sound card through SourceDataLine), you will get a height directly related to the sampling frequency. For 44100 samples per second, an array of 1024 elements will cycle 44100/1024 = 43.066 ... times to fill this “second” data (a very low step - about 43 Hz). If you skip every second element of the table, the step is twice as large, etc. To get a height of 440, you need to find the correct "increment" to use for the step along the array of wave tables, which can be found: incr = (wave size Table * desired step) / sample rate
For example (1024 * 440) / 44100 gives the increment: 10.21678 ... Thus, if the first value from waveTable is at location 0 of the array, the second value that will be used will be between locations 10 and 11. To get the value that is between two array locations, use linear interpolation.
I use this method with javax.sound.sampled libraries for "Terminal" at this link. A keyboard is displayed, but you can easily hear / see microtonal controls when moving the mouse around the keys.
http://www.hexara.com/VSL/JTheremin.htm
In the above position, the mouse (called via MouseMotionListener) is used to calculate the desired tone using this function:
return Math.pow(2, ((mouseX + tuningLoc) / (octaveWidth)));
where octaveWidth is the number of pixels that spans an octave.
Phil freihofner
source share