Android Tone Playback

Is there a way to get Android to emit sound of arbitrary frequency (which means that I don't want to have pre-recorded audio files)?

I looked around and the ToneGenerator was the only thing I could find, which was even close, but it seemed to be able to output standard DTMF tones.

Any ideas?

+75
android audio
Mar 09 '10 at 23:30
source share
10 answers

I initially found this sample code on a blog, but there were some bugs that generated awful sounds. I fixed the errors 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 <steve@staticfree.info> 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(); } } 
+98
Sep 16 '10 at 21:45
source share

Improving the above code:

Add amplitude and decrease amplitude to avoid clicks.

Add code to determine when the game has ended.

  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{ // Montior 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. 
+23
Nov 26 '12 at 12:44
source share

Here's another blog post showing a simple synthesizer plus some user interface

http://audioprograming.wordpress.com/2012/10/18/a-simple-synth-in-android-step-by-step-guide-using-the-java-sdk/

You may also be interested in csound or pdlib (pure data lib) for Android.

+3
Jul 06 '13 at 18:16
source share

Since some older versions of Android have an error that causes a memory leak when using MODE_STATIC, I modified the Xarph answer above to use MODE_STREAM. Hope this helps some.

 public void playTone(double freqOfTone, double duration) { //double duration = 1000; // 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 { int bufferSize = AudioTrack.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, bufferSize, AudioTrack.MODE_STREAM); audioTrack.play(); // Play the track audioTrack.write(generatedSnd, 0, generatedSnd.length); // Load the track } catch (Exception e){ } if (audioTrack != null) audioTrack.release(); // Track play done. Release track. } 
+3
Apr 30 '14 at 22:19
source share

Modified code based on Singhaks response

 public class MainActivity extends Activity { private final int duration = 30; // 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(); private AudioTrack audioTrack; private boolean play = false; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 8000, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, numSamples, AudioTrack.MODE_STREAM); } @Override protected void onResume() { super.onResume(); // Use a new tread as this can take a while Thread thread = new Thread(new Runnable() { public void run() { handler.post(new Runnable() { public void run() { playSound(); genTone(); } }); } }); thread.start(); } void genTone(){ // fill out the array while(play){ for (int i = 0; i < numSamples; ++i) { // float angular_frequency = sample[i] = Math.sin(2 * Math.PI * i / (sampleRate/freqOfTone)); } int idx = 0; // convert to 16 bit pcm sound array // assumes the sample buffer is normalised. for (double dVal : sample) { short val = (short) (dVal * 32767); generatedSnd[idx++] = (byte) (val & 0x00ff); generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8); } audioTrack.write(generatedSnd, 0, numSamples); } } void playSound(){ play = true; audioTrack.play(); } } 
+3
May 21 '14 at 2:47
source share

I wrapped the above wonderful solutions in a neat little package that can be used out of the box as a simple custom buzzer. It runs it in the background thread and has stop and play methods and a few parameters that you can set.

This is on JCenter so you can add it to your dependency list like this

 compile 'net.mabboud:android-tone-player:0.2' 

and you use it that way for a continuous buzzer

 ContinuousBuzzer tonePlayer = new ContinuousBuzzer(); tonePlayer.play(); // just an example don't actually use Thread.sleep in your app Thread.sleep(1000); tonePlayer.stop(); 

or an audio signal is played only once, and you can set the frequency and volume as shown

 OneTimeBuzzer buzzer = new OneTimeBuzzer(); buzzer.setDuration(5); // volume values are from 0-100 buzzer.setVolume(50); buzzer.setToneFreqInHz(110); 

Extended blog entry here about it

+3
01 Oct '16 at 3:18
source share
  float synth_frequency = 440; int minSize = AudioTrack.getMinBufferSize(SAMPLE_RATE, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT); AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, SAMPLE_RATE, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, minSize, AudioTrack.MODE_STREAM); audioTrack.play(); short[] buffer = new short[minSize]; float angle = 0; while (true) { if (play) { for (int i = 0; i < buffer.length; i++) { float angular_frequency = (float)(2*Math.PI) * synth_frequency / SAMPLE_RATE; buffer[i] = (short)(Short.MAX_VALUE * ((float) Math.sin(angle))); angle += angular_frequency; } audioTrack.write(buffer, 0, buffer.length); } 

// You can add an arbitrary value to synth_frequency to get the sound of the change, for example, you can add a random variable to get the sound

+2
May 04 '13 at 9:04 am
source share

Take Major (16 notes)

  public class MainActivity extends AppCompatActivity { private double mInterval = 0.125; private int mSampleRate = 8000; private byte[] generatedSnd; private final double mStandardFreq = 440; Handler handler = new Handler(); private AudioTrack audioTrack; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_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() { byte[] tempByte = new byte[0]; for (int i = 0; i < 16 ; i++ ){ double note = getNoteFrequencies(i); byte[] tonByteNote = getTone(mInterval, mSampleRate, note); tempByte = concat(tonByteNote, tempByte); } generatedSnd = tempByte; handler.post(new Runnable() { public void run() { playTrack(generatedSnd); } }); } }); thread.start(); } public byte[] concat(byte[] a, byte[] b) { int aLen = a.length; int bLen = b.length; byte[] c= new byte[aLen+bLen]; System.arraycopy(a, 0, c, 0, aLen); System.arraycopy(b, 0, c, aLen, bLen); return c; } private double getNoteFrequencies(int index){ return mStandardFreq * Math.pow(2, (double) index/12.0d); } private byte[] getTone(double duration, int rate, double frequencies){ int maxLength = (int)(duration * rate); byte generatedTone[] = new byte[2 * maxLength]; double[] sample = new double[maxLength]; int idx = 0; for (int x = 0; x < maxLength; x++){ sample[x] = sine(x, frequencies / rate); } for (final double dVal : sample) { final short val = (short) ((dVal * 32767)); // in 16 bit wav PCM, first byte is the low order byte generatedTone[idx++] = (byte) (val & 0x00ff); generatedTone[idx++] = (byte) ((val & 0xff00) >>> 8); } return generatedTone; } private AudioTrack getAudioTrack(int length){ if (audioTrack == null) audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, mSampleRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, length, AudioTrack.MODE_STATIC); return audioTrack; } private double sine(int x, double frequencies){ return Math.sin( 2*Math.PI * x * frequencies); } void playTrack(byte[] generatedSnd){ getAudioTrack(generatedSnd.length) .write(generatedSnd, 0, generatedSnd.length); audioTrack.play(); } } 
+2
Oct. 29 '16 at 11:50
source share

see this useful library

https://github.com/karlotoy/perfectTune

easy to use

add this to your dependencies

  compile 'com.github.karlotoy:perfectTune:1.0.2' 

And you use it as follows:

 PerfectTune perfectTune = new PerfectTune(); perfectTune.setTuneFreq(desire_freq); perfectTune.playTune(); 

To stop the melody:

 perfectTune.stopTune(); 
+2
Feb 23 '17 at 9:46 on
source share

There are several programs for this, but they suck. I measured a few:

http://www.endolith.com/wordpress/2009/11/24/android-audio-applications/

So don’t do what they do .: D

+1
Apr 28 '10 at 18:12
source share



All Articles