1

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);

}
Community
  • 1
  • 1
AndroidNoob
  • 2,771
  • 2
  • 40
  • 53
  • Did you generate a tone of 0.5 seconds?? – ephramd Jul 09 '13 at 21:51
  • 1
    No I didn't manage to generate a tone less than 1 second, however I was able to stop the track straight away but first pausing then flushing then releasing the audiotrack, so it did not matter that the tone was 1 second long – AndroidNoob Jul 11 '13 at 09:01
  • Thanks for responding @AndroidNoob. I've done: **1.** Adding a durationFake (ex: 10 seconds) **2.** Repeat infinitely tone: `toneUnit.setLoopPoints (0, generatedSnd.length / (2 * AudioFormat.CHANNEL_OUT_MONO), -1);` **3.** Stop the sound in milliseconds with CountDownTimer. But this consumes a lot of memory. How did you exactly? – ephramd Jul 15 '13 at 01:37
  • 1
    I had lots of memory usage too, so on every touch I actually call 'audioTrack.pause()', 'audioTrack.flush()', 'audioTrack.release()' then 'audioTrack = null' to free up the memory, then I make an NDK call to generate a new tone of 1 second at sample rate of 8000 (converted tone generation code to NDK to speed up the tone generation process), reinstantiate the audiotrack and assign the tone the before setting it to play (no looping, as the tone is generated all the time the user is touching the screen). It's almost definitely not the proper way to do it but hey it's working for me! – AndroidNoob Jul 16 '13 at 14:00

1 Answers1

1

I think your crash is coming from using a buffer size that is too small for the AudioTrack you are trying to create.

int audioBufferSize = AudioTract.getMinBufferSize(sampleRate,
                                                  AudioFormat.CHANNEL_OUT_MONO,
                                                  AudioFormat.ENCODING_PCM-16BIT);
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
        sampleRate, AudioFormat.CHANNEL_OUT_MONO,
        AudioFormat.ENCODING_PCM_16BIT, audioBufferSize,
        AudioTrack.MODE_STREAM);

Then numSamples still applies to your generating buffer and the AudioTrack will have the buffer size it wants.

Douglas Jones
  • 2,522
  • 1
  • 23
  • 27