I’m trying to create an app that generates a tone depending on where you touch on the screen (currently the frequency is determined by the y value of the touch point) however I’m not able to generate a tone of less than a second without crashing my app! I took the code from the answer here and tweaked it to play when i touch the screen, but when I change the duration variable to a value of less than 1 the app crashes! Is it possible to generate a tone of less than 1 second?
The only reason I want to create a tone of less than a second is that the audio player does not seem to cut off straight away when i pause or stop the audiotrack. these are my methods for playing and generating the tone:
RelativeLayout background;
SoundPool soundPool;
TextView textView;
private final float duration = 1f; // seconds
private final int sampleRate = 8000;//was 8000
private final int numSamples = (int) duration * sampleRate;
private final double sample[] = new double[numSamples];
private final double freqOfTone = 250; // hz
AudioTrack audioTrack ;
int dispWidth = 0, dispHeight = 0;
Handler handler = new Handler();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_audio);
background = (RelativeLayout) findViewById(R.id.background);
textView = (TextView) findViewById(R.id.textView2);
soundPool = new SoundPool(1, AudioManager.STREAM_MUSIC, 0);
dispWidth = AppHelper.getDisplayWidth(getApplicationContext());
dispHeight = AppHelper.getDisplayHeight(getApplicationContext());
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
sampleRate, AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT, numSamples,
AudioTrack.MODE_STREAM);
final byte genTone[]= genTone(freqOfTone);
background.setClickable(true);
background.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction()==MotionEvent.ACTION_UP)
{
Log.d(LOG_TAG, "Touch up! Stopping tone...");
audioTrack.pause();
audioTrack.flush();
}
else
{
//these apparently cause immediate stop
audioTrack.pause();
audioTrack.flush();
int R = (int) (event.getY()%255);
int G = (int)(event.getX()%255);
int B = (int) ((event.getY()+event.getX())%255);
Log.i(LOG_TAG, "Changine colour to "+R+","+G+","+B);
background.setBackgroundColor(Color.rgb(R, G, B));
int colorFromPressure = (int) (event.getPressure()*255);
textView.setTextColor(Color.rgb(colorFromPressure,colorFromPressure,colorFromPressure));
playSoundAtFrequency(250+event.getY(), event.getX()/dispWidth);
}
return false;
}
});
}
byte[] genTone(double toneFrequency){
final byte generatedSnd[] = new byte[2 * numSamples];
// fill out the array
for (int i = 0; i < numSamples; ++i) {
sample[i] = Math.sin(2 * Math.PI * i / (sampleRate/toneFrequency));
}
// convert to 16 bit pcm sound array
// assumes the sample buffer is normalised.
int idx = 0;
for (double dVal : sample) {
short val = (short) (dVal * 32767);
generatedSnd[idx++] = (byte) (val & 0x00ff);
generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8);
}
return generatedSnd;
}
void playSound(final byte[] generatedSnd, float volume){
//these apparently cause immediate stop
audioTrack.pause();
audioTrack.flush();
audioTrack.write(generatedSnd, 0, numSamples);
audioTrack.setStereoVolume(volume, volume);
audioTrack.play();
Log.d(LOG_TAG, "Playing tone...");
}
public void playSoundAtFrequency(final double toneFrequency,float volume)
{
final byte genTone[]= genTone(toneFrequency);
playSound(genTone, volume);
}
I think your crash is coming from using a buffer size that is too small for the AudioTrack you are trying to create.
Then numSamples still applies to your generating buffer and the AudioTrack will have the buffer size it wants.