Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android: Sine Wave Generation

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.

like image 392
K. Barresi Avatar asked Jul 11 '12 15:07

K. Barresi


3 Answers

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.

like image 102
Martijn Courteaux Avatar answered Nov 15 '22 21:11

Martijn Courteaux


The only material difference that I can see in your two code samples is that the equation in your first example contains an integer (I), and therefore you're probably doing integer (not floating-point) arithmetic. This would cause a staircasing effect, adding unwanted harmonics to your waveform.

I suspect that if you simply cast I to a float in your equation, it will produce a pure sine wave.

samples[i] 
    = (float) Math.sin( (float)i * ((float)(2*Math.PI) * frequency / 44100));
like image 35
Robert Harvey Avatar answered Nov 15 '22 20:11

Robert Harvey


None of these anwers fixes the problem. The buffer length should be a multiple of the sample rate, or at least the length of one rotation. Let's break it in ton of variables to show what's happening:

int sampleRate = 44100;
int bitsPerChannel = 16;
int bytesPerChannel = bitsPerChannel / 8;
int channelCount = 1;
int bytesPerSample = channelCount * bytesPerChannel;
int bytesPerRotation = sampleRate * bytesPerSample * (1d / (double) frequency);

Then you can multiply this bytesPerRotation by anything, it won't change a fact: there won't be glitch in the sound.

like image 31
Léon Pelletier Avatar answered Nov 15 '22 20:11

Léon Pelletier