Making waves: Creating a basic sinewave oscillator

The basics of electronic sound synthesis are rooted in oscillators: producing sound using a simple, oscillating signal. One of the simplest of these is the humble sine wave, which you may remember from your high school trigonometry classes.


The sine wave is a function of dividing the opposite by the hypotenuse in a right triangle (remember soh-cah-toa?). Electronically, a sine wave produces a very simple beeping sound. At high frequencies, it sounds like a standard beep, at low frequencies, it is a deep bass sound. It's one of the basic wave forms that are used to synthesize sound - others include the triangle wave, the sawtooth wave, and the square wave (also known as the pulse wave).

Sine, square/pulse, triangle, and sawtooth, in order

For this exercise, I decided to create a sine wave and feed it into my source data line. This will produce sound without relying on an existing sound file - it uses math instead. I used this Java example as a basis of my efforts. My function looks like this:



There are several things going on here. Like my first example of using a source data line, I create an audio format and get a source data line from the audio system using that format. Again, I'm going with 16 bits and one channel (mono).

This time, I am using a byte buffer to feed audio data into the data line - you can place a 16-bit short into it and the byte buffer will handle converting it into bytes for you. A lot more convenient.

I decide to have 5 seconds of a sine wave beeping, and so I multiply the frequency (44khz) by 5 to get a list of total sample numbers (remember, a sample is basically the smallest increment of data that a data line can accept). I then calculate a cycle increment for this pass of the while loop by dividing the frequency by the sampling rate.

The main sequence is the while loop. Within the while loop, I calculate how much space the source data line has available and use that to determine how many samples I can put in this pass through the loop. Then, I make a for loop and calculate the actual value of the short I am going to put onto the buffer. The math for this is the sine of 2 times PI times the current cycle position. I have to cast the result to an into and then to a short, because Kotlin doesn't yet support direct conversion of doubles to shorts. Maybe there is a better way. I also multiply this by the max value of short to get maximum amplitude of the wave.

All of this goes into the byte buffer. Once I'm done calculating the values, I put the contents of the buffer into the source data line.

The end result: a five second beep. Hooray! I am now an expert audio programmer, obviously.

You can try this yourself with my code example above if you like. I will next experiment with modifying the frequency and amplitude with parameters.

Comments