Android: Sine Wave Generation

K. Barresi picture K. Barresi · Jul 11, 2012 · Viewed 14k times · Source

I'm trying to use AudioTrack to generate sine, square, and sawtooth waves. However, the audio this is creating doesn't sound like a pure sine wave, but like it has some kind of other wave overlayed. How would I go about getting the pure sine wave like in the second code example, while using the method in my first example? Since the top example only moves around some of the arithmetic used in the second, shouldn't they produce an identical wave?

@Override
        protected Void doInBackground(Void... foo) {
            short[] buffer = new short[1024];
            this.track = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, minBufferSize, AudioTrack.MODE_STREAM);
            float samples[] = new float[1024];

            this.track.play();

            while (true) {
                for (int i = 0; i < samples.length; i++) {
                    samples[i] = (float) Math.sin( (float)i * ((float)(2*Math.PI) * frequency / 44100));    //the part that makes this a sine wave....
                    buffer[i] = (short) (samples[i] * Short.MAX_VALUE);
                }
                this.track.write( buffer, 0, samples.length );  //write to the audio buffer.... and start all over again!

            }           
        }

Note: This does give me a pure sine wave:

@Override
        protected Void doInBackground(Void... foo) {
            short[] buffer = new short[1024];
            this.track = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, minBufferSize, AudioTrack.MODE_STREAM);
            float increment = (float)(2*Math.PI) * frequency / 44100; // angular increment for each sample
            float angle = 0;
            float samples[] = new float[1024];

            this.track.play();

            while (true) {
                for (int i = 0; i < samples.length; i++) {
                    samples[i] = (float) Math.sin(angle);   //the part that makes this a sine wave....
                    buffer[i] = (short) (samples[i] * Short.MAX_VALUE);
                    angle += increment;
                }
                this.track.write( buffer, 0, samples.length );  //write to the audio buffer.... and start all over again!

            }           
        }

Thanks to Martijn: The problem is that the wave is getting cut off between wavelengths in the buffer. Increasing the buffer size solves the problem in the second example. It appears that the Math.PI * 2 arithmetic was the most intensive of the loop, so moving that value to an external variable that is only computed once solves everything.

Answer

Martijn Courteaux picture Martijn Courteaux · Jul 12, 2012

Try to optimise your code by

  1. increase buffer size
  2. prepare the buffer once, and keep rewriting it to the output stream (this will require some math calculating the perfect size for the buffer to make sure that the whole sine wave fits perfectly in it).

Why? Because I suspect the buffer to taking to long to prepare, what causes a lag between two buffer pushes to big, which might be causing the noise.