美文网首页Android 音视频
Android音视频开发(三):变声

Android音视频开发(三):变声

作者: JYangkai | 来源:发表于2019-07-07 15:05 被阅读122次

    简介

    前面两章我们介绍了PCM音频格式的录制和播放,分别是使用AudioRecord录制,使用AudioTrack播放,其实得到了PCM格式的音频,我们并不能随意在播放器中播放,因为PCM格式的音频,播放器还不能识别,需要编码封装成mp3或者wav等格式才能播放,但是今天我们暂时不讨论如何进行PCM编码,先来对PCM进行一些处理,比如变声,添加BGM等,今天我们的主题是变声

    变声原理

    对于变声的处理一般有以下三种

    • 变速又变调

    即改变音频的速度(语速),又改变音频的频率(音调)

    我们可以对原始音频进行重采样,重采样有上采样和下采样,分别是对原始音频进行插值和抽取,比如P/Q重采样,一般我们的处理是,先对原始音频进行插值,在相邻两点间插入P个采样点,全部插入结束后再每隔Q个采样点进行采样,这样得到的音频语速和音调都是原来的Q/P

    • 变速不变调

    只改变语速,不改变音调

    只是改变语速的话,那么就要稍微复杂点,和重采样方法差不多,区别在于我们需要先规定一帧音频的长度,一般我们的采样率设为44.1KHz,也就是一秒钟采样44.1K次,我们可以规定一帧为1024,所有我们可以简单的根据丢帧和重复帧来实现变速不变调,比如对于P/Q变速,我们先对原始音频的每一帧重复P次,最后的结果再进行每隔Q帧取一帧,这样就得到音频音调不变,语速变为Q/P的音频

    • 变调不变速

    只改变音调,不改变语速

    如果只是变调的话,就要结合重采样和变速不变调来做,我们先对音频信号进行变速不变调处理,再对其进行重采样,比如,我想要让音调变为原来的P/Q倍,那么我们需要先对其进行P/Q变速不变调,语速变为原来的Q/P,接着,在对其进行Q/P重采样,这样,最后就得到了语速不变,而音调变为原来的P/Q倍

    当然,还有很多对声音的处理,比如一些K歌软件,可以实现KTV、空灵、磁性等效果,那些效果就比较复杂,不在今天的讨论范围内,我们暂时只讨论简单的变声

    代码实现(Java)

    我们这样是使用纯Java代码实现,其实这样是存在效率问题的,如果音频较大,还是得用C语言(使用JNI和NDK去实现),对于一些特殊情况,算法可能存在问题,仅供参考

    • 帧长
    private static final int FRAME_LENGTH = 1024;
    
    • 变调又变速(提高)
    //变调又变速(提高)
    public static byte[] up(byte[] data, int up) {
        if (up == 1) {
            return data;
        }
        int length = data.length;
        int upLength = length / up;
        byte[] upData = new byte[upLength];
        for (int i = 0, j = 0; i < length; ) {
            if (j >= upLength) {
                break;
            }
            upData[j] = data[i];
            i += up;
            j++;
        }
        return upData;
    }
    
    • 变调又变速(降低)
    public static byte[] down(byte[] data, int down) {
        if (down == 1) {
            return data;
        }
        int length = data.length;
        int downLength = length * down;
        byte[] downData = new byte[downLength];
        for (int i = 0, j = 0; i < length - 1; ) {
            for (int k = 0; k < down; k++) {
                downData[j] = data[i];
                j++;
            }
            i++;
        }
        return downData;
    }
    
    • 变速不变调(提高)
    public static byte[] speedUp(byte[] data, int up) {
        if (up == 1) {
            return data;
        }
        int length = data.length;
        int frameShift = FRAME_LENGTH * up;
        int upLength = length / up;
        byte[] upData = new byte[upLength];
        for (int i = 0, j = 0; i < length; ) {
            if (i + FRAME_LENGTH >= length) {
                System.arraycopy(data, i, upData, j, length - i);
                break;
            }
            System.arraycopy(data, i, upData, j, FRAME_LENGTH);
            i += (FRAME_LENGTH + frameShift);
            j += FRAME_LENGTH;
        }
        return upData;
    }
    
    • 变速不变调(降低)
    public static byte[] speedDown(byte[] data, int down) {
        if (down == 1) {
            return data;
        }
        int length = data.length;
        int downLength = length * down;
        byte[] downData = new byte[downLength];
        for (int i = 0, j = 0; i < length; ) {
            if (i + FRAME_LENGTH >= length) {
                int lastlength = length - i;
                for (int k = 0; k < down; k++) {
                    System.arraycopy(data, lastlength, downData, j, lastlength);
                    j += lastlength;
                }
                break;
            }
            for (int k = 0; k < down; k++) {
                System.arraycopy(data, i, downData, j, FRAME_LENGTH);
                j += FRAME_LENGTH;
            }
            i += FRAME_LENGTH;
        }
        return downData;
    }
    
    • 设置语速
    public static byte[] setSpeed(byte[] data, int up, int down) {
        byte[] downData = speedDown(data, down);
        byte[] upData = speedUp(downData, up);
        return upData;
    }
    
    • 设置音调
    public static byte[] setTone(byte[] data, int up, int down) {
        byte[] speedData = setSpeed(data, down, up);
        byte[] downData = down(speedData, down);
        byte[] upData = up(downData, up);
        return upData;
    }
    

    总结

    我们在使用AudioRecord录音结束后,可以调用以上函数进行处理,然后再使用AudioTrack进行播放,对于updown参数的配置,可以自己调,我将up设为4,down设为5,我的声音就变得低沉大叔的声音,更多的参数你可以自己去测试

    注:如果设置的参数过大可能会出现异常,比如50,可能是算法存在问题,还需改进。

    变声的简单介绍,希望大家喜欢。

    相关文章

      网友评论

        本文标题:Android音视频开发(三):变声

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