Question

I'm trying to play a continuous stream of sine waves. The frequency of the sine wave changes when I press and hold the buttons. It returns to its normal frequency once I release the buttons.

When I run the following code, the output sound has periodic 'clicks' (sharp acoustic pressure changes) occuring at regular intervals - around 5 clicks per second - rather than playing a pure sine tone.

A funny behavior is, there are no clicks when I press and hold the C5 button. But when I press and hold the C4 button, the clicks are still there.

What is the cause of this problem?

public class MainActivity extends Activity {
Thread t;
private Button buttonC4, buttonC5;

float buttonC4_val = 0;
float buttonC5_val = 0;

int sr = 44100;
boolean isRunning = true;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    // button initialization
    buttonC4 = (Button) this.findViewById(R.id.button_c4);
    buttonC5 = (Button) this.findViewById(R.id.button_c5);

    buttonC4.setOnTouchListener(new OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            switch(event.getAction())
            {
            case MotionEvent.ACTION_DOWN:
                //insert code here
                buttonC4_val = 300;
                buttonC4.setText("You Clicked ME!!!");
                return true;
            case MotionEvent.ACTION_UP:
                //insert code here
                buttonC4_val = 0;
                buttonC4.setText("C4");
                return true;
            }
            return false;
        }
    });

    buttonC5.setOnTouchListener(new OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            switch(event.getAction())
            {
            case MotionEvent.ACTION_DOWN:
                //insert code here
                buttonC5_val = 500;
                buttonC5.setText("You Clicked ME!!!");
                return true;
            case MotionEvent.ACTION_UP:
                //insert code here
                buttonC5_val = 0;
                buttonC5.setText("C5");
                return true;
            }
            return false;
        }
    });

    t = new Thread() {
        public void run() {
            setPriority(Thread.MAX_PRIORITY);

    int bufferSize = AudioTrack.getMinBufferSize(sr,
            AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);

    AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sr, 
            AudioFormat.CHANNEL_OUT_MONO, 
            AudioFormat.ENCODING_PCM_16BIT, 
            bufferSize, 
            AudioTrack.MODE_STREAM);

    audioTrack.play();

    // synthesis loop
     while(isRunning){                  
           double upFreq = (double) buttonC4_val + (double) buttonC5_val;   
       short samples[] = new short[bufferSize];
           int amp = 10000;
           double twopi = 8.*Math.atan(1.);
           double fr = 440.f;
           double ph = 0.0;                 
           fr =  440 + upFreq;

           for(int i=0; i < bufferSize; i++){
         samples[i] = (short) (amp*Math.sin(ph));
         ph += twopi*fr/sr;
           }

      audioTrack.write(samples, 0, bufferSize);
            }
    audioTrack.stop();
    audioTrack.release();
        }
    };
    t.start();
}
}
Was it helpful?

Solution

It looks like you're filling the entire samples buffer with the sine wave - if the bufferSize is not a multiple of the frequency you're generating then you're going to be partially through a wave when the next sample starts playing - that might be what's causing the pops.

Amazing diagram

You can test this by changing the bufferSize and seeing if that changes the frequency of the popping. Bigger buffer should mean these sample mismatches happen less often so the popping should be less frequent.

The solution would be to change

audioTrack.write(samples, 0, bufferSize);

to replace bufferSize with the location in the buffer of the last 0 crossing.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top