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();
}
}
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.
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.