Android 纯音生成方法

作者: 小小小小怪兽_666 | 来源:发表于2019-08-06 23:01 被阅读10次

    因项目需要在 APP 中播放纯音,耳机测听的需求,了解在 Android 系统中,纯音播放有两种方式,一种是在程序中模拟出一个波形满足正弦波的音频数据,另一种就是事先准备好多个音频文件然后直接播放。若使用事先准备的音频,效果是可以达到(且可能更好),但需要准备各种分贝、频率的音频文件,算下来,要制作上百个,所以这里讨论通过程序模拟生成纯音。

    所有声音都是有正弦波组成,只不过纯音是固定频率的正弦波。主要思路就是,可以使用 sin 函数 实现想要的频率的正弦波,然后再用AudioTrack类来实现声音的播放。

    一、简单纯音计算

    首先正弦波的高度设置为 127,因为这里使用 8 位的采样率,2 的 8 次方就应该是 256,所以正弦波的波峰就应该是 127 了。

        /** 正弦波的高度 **/
        public static final int HEIGHT = 127;
        /** 2PI **/
        public static final double TWOPI = 2 * 3.1415;
    
        /** 
         * 生成正弦波 
         * @param wave 
         * @param waveLen 每段正弦波的长度 
         * @param length 总长度
         * @return
         */
        public static byte[] sin(byte[] wave, int waveLen, int length) {
            for (int i = 0; i < length; i++) {
                wave[i] = (byte) (HEIGHT * (1 - Math.sin(TWOPI
                        * ((i % waveLen) * 1.00 / waveLen))));
            }
            return wave;
        }
    
    

    初始化 AudioTrack 首先传入一个指定频率,这里先将声音总频率设置为 44100,然后计算出单个正弦波(也就是 2PI)的长度,最后将单个正弦波的长度乘以频率取得声波的实际频率,这样的是为了避免声波最后出现一段空的声波。 然后将参数传入上述的 sin 方法中取得正弦波。
    AudioFormat.ENCODING_PCM_8BIT 就是指定 AudioTrack 使用 8 位采样率。

    /**
     * 设置频率
     * @param rate
     */
    public void start(int rate) {
        if (rate > 0) {
            Hz = rate;
            waveLen = 44100 / Hz;
            length = waveLen * Hz;
            audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 44100,
                    AudioFormat.CHANNEL_CONFIGURATION_STEREO, // CHANNEL_CONFIGURATION_MONO,
                    AudioFormat.ENCODING_PCM_8BIT, length, AudioTrack.MODE_STREAM);
            //生成正弦波
            wave = SinWave.sin(wave, waveLen, length);
            if (audioTrack != null) {
                audioTrack.play();
            }
        } else {
            return;
        }
    }
    

    AudioTrack.play() 是还不能播放声音的,因为这个时候 AudioTrack 里面还没有声波数据。下面这段代码喂数据才能真的播放声音:

    audioTrack.write(wave, 0, length);
    

    这种生产的方法,在频率高的时候,就会产生误差,频率越高,误差越大。

    二、生成不同分贝和频率的纯音

    根据不同分贝,不同频率的生成纯音。

    • 初始化 AudioTrack
    • 播放 audioTrackz.play()
    • 喂数据 AudioTrackZThread
      private int sampleRateInHz = 16000; // 采样率,MAX 44100
        private int mChannel = AudioFormat.CHANNEL_CONFIGURATION_MONO;// 声道 :单声道
        private int mSampBit = AudioFormat.ENCODING_PCM_16BIT;// 采样精度 :16bit
        private AudioTrackZThread audioTrackZThread;
        private boolean isRunning = false;
        private AudioTrack audioTrackz;
    
        public WaveOutZ() {
            int bufferSize = AudioTrack.getMinBufferSize(sampleRateInHz, mChannel, mSampBit);
            audioTrackz = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRateInHz, mChannel, mSampBit, bufferSize * 2, AudioTrack.MODE_STREAM);
            audioTrackz.setStereoVolume(1.0f, 1.0f);
            audioTrackz.play();
        }
    
        public void palyWaveZ(float rate, int db) {
            if (audioTrackZThread==null) {
                audioTrackZThread = new AudioTrackZThread();
                audioTrackZThread.start();
            }
            audioTrackZThread.setDb(rate,db);
        }
    
        public void colseWaveZ() {
            if (audioTrackz != null) {
                if (!AudioTrackZThread.interrupted()) {
                    isRunning = false;
                    audioTrackZThread=null;
                }
            }
        }
    
        class AudioTrackZThread extends Thread {
            private short m_iAmp = Short.MAX_VALUE;
            private short m_bitDateZ[] = new short[44100];
            private double x = 2.0 * Math.PI * 8250.0 / 44100.0;
            private int mM_bitDateZSize;
    
            public void setDb(float rate, int db) {
                x = 2.0 * Math.PI * rate / 44100.0;
                m_iAmp = (short) ((Math.pow(10.0, db / 20.0) * 1.414) * Short.MAX_VALUE);
                for (int i = 0; i < 44100; i++) {
                    m_bitDateZ[i] = (short) (m_iAmp * Math.sin(x * i));
                }
                mM_bitDateZSize = m_bitDateZ.length;
            }
    
            @Override
            public void run() {
                isRunning = true;
    
                do {
                    audioTrackz.write(m_bitDateZ, 0, mM_bitDateZSize); 
                } while (isRunning);
                super.run();
            }
        }
    

    相关文章

      网友评论

        本文标题:Android 纯音生成方法

        本文链接:https://www.haomeiwen.com/subject/lewgdctx.html