Playing an arbitrary tone with Android

后端 未结 10 2074
伪装坚强ぢ
伪装坚强ぢ 2020-11-22 10:38

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

相关标签:
10条回答
  • 2020-11-22 10:41

    Since there is a bug in some older android versions that causes a memory leak when using MODE_STATIC, I modified Xarph's answer above to use MODE_STREAM. Hopefully it will help 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.
    }
    
    0 讨论(0)
  • 2020-11-22 10:42

    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 <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();
        }
    }
    
    0 讨论(0)
  • 2020-11-22 10:45

    Modified Code Based on Singhaks' answer

    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();
        }
    }
    
    0 讨论(0)
  • 2020-11-22 10:46

    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.
    
    0 讨论(0)
  • 2020-11-22 10:47

    see this helpful library

    https://github.com/karlotoy/perfectTune

    it's easy to use

    add this to your dependencies

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

    And you use it like this:

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

    to stop the tune:

    perfectTune.stopTune();
    
    0 讨论(0)
  • 2020-11-22 10:48

    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 whatever they do. :D

    0 讨论(0)
提交回复
热议问题