Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Playing an arbitrary tone with Android

Tags:

android

audio

Is there any way to make Android emit a sound of arbitrary frequency (meaning, I don't want to have pre-recorded sound files)?

I've looked around and ToneGenerator was the only thing I was able to find that was even close, but it seems to only be capable of outputting the standard DTMF tones.

Any ideas?

like image 249
Jeremy Logan Avatar asked Mar 09 '10 23:03

Jeremy Logan


2 Answers

I originally found this example code on a blog, but it had some bugs in it that generated some horrendous sounds. I've fixed the bugs and posted the resulting code here. Seems to work well for me!

public class PlaySound extends Activity {     // originally from http://marblemice.blogspot.com/2010/04/generate-and-play-tone-in-android.html     // and modified by Steve Pomeroy <[email protected]>     private final int duration = 3; // seconds     private final int sampleRate = 8000;     private final int numSamples = duration * sampleRate;     private final double sample[] = new double[numSamples];     private final double freqOfTone = 440; // hz      private final byte generatedSnd[] = new byte[2 * numSamples];      Handler handler = new Handler();      @Override     public void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.main);     }      @Override     protected void onResume() {         super.onResume();          // Use a new tread as this can take a while         final Thread thread = new Thread(new Runnable() {             public void run() {                 genTone();                 handler.post(new Runnable() {                      public void run() {                         playSound();                     }                 });             }         });         thread.start();     }      void genTone(){         // fill out the array         for (int i = 0; i < numSamples; ++i) {             sample[i] = Math.sin(2 * Math.PI * i / (sampleRate/freqOfTone));         }          // convert to 16 bit pcm sound array         // assumes the sample buffer is normalised.         int idx = 0;         for (final double dVal : sample) {             // scale to maximum amplitude             final short val = (short) ((dVal * 32767));             // in 16 bit wav PCM, first byte is the low order byte             generatedSnd[idx++] = (byte) (val & 0x00ff);             generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8);          }     }      void playSound(){         final AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,                 sampleRate, AudioFormat.CHANNEL_OUT_MONO,                 AudioFormat.ENCODING_PCM_16BIT, generatedSnd.length,                 AudioTrack.MODE_STATIC);         audioTrack.write(generatedSnd, 0, generatedSnd.length);         audioTrack.play();     } } 
like image 124
4 revs, 2 users 99% Avatar answered Sep 16 '22 15:09

4 revs, 2 users 99%


Improving on the above code:

Add amplitude ramp up and ramp down to avoid the clicks.

Add code to determine when the tack has finished playing.

double duration = 1;            // seconds double freqOfTone = 1000;       // hz int sampleRate = 8000;          // a number  double dnumSamples = duration * sampleRate; dnumSamples = Math.ceil(dnumSamples); int numSamples = (int) dnumSamples; double sample[] = new double[numSamples]; byte generatedSnd[] = new byte[2 * numSamples];   for (int i = 0; i < numSamples; ++i) {    // Fill the sample array     sample[i] = Math.sin(freqOfTone * 2 * Math.PI * i / (sampleRate)); }  // convert to 16 bit pcm sound array // assumes the sample buffer is normalized. // convert to 16 bit pcm sound array // assumes the sample buffer is normalised. int idx = 0; int i = 0 ;  int ramp = numSamples / 20 ;                                     // Amplitude ramp as a percent of sample count   for (i = 0; i< ramp; ++i) {                                      // Ramp amplitude up (to avoid clicks)     double dVal = sample[i];                                                                  // Ramp up to maximum     final short val = (short) ((dVal * 32767 * i/ramp));                                                                  // in 16 bit wav PCM, first byte is the low order byte     generatedSnd[idx++] = (byte) (val & 0x00ff);     generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8); }   for (i = i; i< numSamples - ramp; ++i) {                         // Max amplitude for most of the samples     double dVal = sample[i];                                                                  // scale to maximum amplitude     final short val = (short) ((dVal * 32767));                                                                  // in 16 bit wav PCM, first byte is the low order byte     generatedSnd[idx++] = (byte) (val & 0x00ff);     generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8); }  for (i = i; i< numSamples; ++i) {                                // Ramp amplitude down     double dVal = sample[i];                                                                  // Ramp down to zero     final short val = (short) ((dVal * 32767 * (numSamples-i)/ramp ));                                                                  // in 16 bit wav PCM, first byte is the low order byte     generatedSnd[idx++] = (byte) (val & 0x00ff);     generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8); }  AudioTrack audioTrack = null;                                    // Get audio track try {     audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,         sampleRate, AudioFormat.CHANNEL_CONFIGURATION_MONO,         AudioFormat.ENCODING_PCM_16BIT, (int)numSamples*2,         AudioTrack.MODE_STATIC);     audioTrack.write(generatedSnd, 0, generatedSnd.length);        // Load the track     audioTrack.play();                                             // Play the track } catch (Exception e){     RunTimeError("Error: " + e);     return false; }  int x =0; do{                                                              // Monitor playback to find when done     if (audioTrack != null)          x = audioTrack.getPlaybackHeadPosition();      else          x = numSamples; } while (x<numSamples);  if (audioTrack != null) audioTrack.release();                    // Track play done. Release track. 
like image 41
Xarph Avatar answered Sep 16 '22 15:09

Xarph