V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
glumess
V2EX  ›  Android

音频变速 | libsonic 开源库的介绍与实践

  •  
  •   glumess · 2021-11-01 09:51:46 +08:00 · 9816 次点击
    这是一个创建于 1148 天前的主题,其中的信息可能已经有所发展或是发生改变。

    在做音视频编辑的时候,大家关注更多的是视频开发,热衷于 FFmpeg 、OpenGL 这些技巧,实际上音频开发也是很重要的,甚至可以说音频开发比视频开发更难一点。

    对音频的常见处理就是变速、变调、混音这些,一般都是用开源库来搞定,常见的就是 libsonic 和 libsoundtouch 两个,当然有实力的公司会自己研发音频算法。

    本篇文章会简单介绍 libsonic 的使用。

    libsonic 获取

    libsonic 是一个支持音频倍速播放的开源库,并且支持大于 2 倍速的播放,它的主页地址如下:

    https://android.googlesource.com/platform/external/sonic/+/refs/heads/master/doc/index.md

    可以通过该链接下载 libsonic 库:

    git clone git://github.com/waywardgeek/sonic.git

    这个仓库里面内容还挺全的,包含了 Java 和 C 版本的实现和使用演示,很方便做移植。

    如果你只需要用到变速和音量调整,那么使用 sonic_lite.h 和 sonic_lite.c 两个文件就行了,这里面对功能做了裁剪。

    libsonic 主页上还提供了一个 Android NDK 版本的仓库,通过该链接可下载:

    git clone git://github.com/waywardgeek/sonic-ndk.git

    不过这个版本有的太老旧了,还是 Android.mk 的接入方式,现在都换成 CMake 接入了。

    有兴趣的同学可以写一个 sonic-ndk CMake 版本的封装库,说不定能在 Github 上刷一波 star 呢。

    libsonic 使用

    libsonic 的调用接口不多,具体使用完全可以参考给的代码演示。

    以下是一个音频倍速的使用代码:

    /* Run sonic_lite. */
    static void runSonic(char* inFileName, char* outFileName, float speed, float volume) {
      waveFile inFile, outFile = NULL;
      // 定义输入和输出的 buffer 
      short inBuffer[SONIC_INPUT_SAMPLES], outBuffer[SONIC_INPUT_SAMPLES];
      int samplesRead, samplesWritten, sampleRate, numChannels;
      // 打开输入文件,并且获取输入文件的相关信息
      inFile = openInputWaveFile(inFileName, &sampleRate, &numChannels);
      if (numChannels != 1) {
        fprintf(stderr, "sonic_lite only processes mono wave files.  This file has %d channels.\n",
            numChannels);
        exit(1);
      }
      if (sampleRate != SONIC_SAMPLE_RATE) {
        fprintf(stderr,
            "sonic_lite only processes wave files with a sample rate of %d Hz.  This file uses %d\n",
            SONIC_SAMPLE_RATE, sampleRate);
        exit(1);
      }
      if (inFile == NULL) {
        fprintf(stderr, "Unable to read wave file %s\n", inFileName);
        exit(1);
      }
      // 打开输出文件
      outFile = openOutputWaveFile(outFileName, sampleRate, 1);
      if (outFile == NULL) {
        closeWaveFile(inFile);
        fprintf(stderr, "Unable to open wave file %s for writing\n", outFileName);
        exit(1);
      }
      // 初始化 sonic 并且设置速度 speed 和音量 volume 
      sonicInit();
      sonicSetSpeed(speed);
      sonicSetVolume(volume);
      do {
       // 从输入文件中读取 sample
        samplesRead = readFromWaveFile(inFile, inBuffer, SONIC_INPUT_SAMPLES);
        if (samplesRead == 0) {
          sonicFlushStream();
        } else {
          // 将读取的 sample 给到 sonic 中的 sonicStream
          // sonicStream 会根据 speed 和 volume 做相应处理
          sonicWriteShortToStream(inBuffer, samplesRead);
        }
        // 将 sonic 中 sonicStream 处理后的数据给到 outBuffer
        // 并将 outBuffer 写入到输出文件中
        do {
          samplesWritten = sonicReadShortFromStream(outBuffer, SONIC_INPUT_SAMPLES);
          if (samplesWritten > 0) {
            writeToWaveFile(outFile, outBuffer, samplesWritten);
          }
        } while (samplesWritten > 0);
      } while (samplesRead > 0);
      closeWaveFile(inFile);
      closeWaveFile(outFile);
    }
    

    这个代码示例还是比较简单易懂的。

    首先是检查输入和输出文件是不是有问题,这一点很基础但也非常重要。

    代码中用到的示例文件是 wav 格式的,libsonic 仓库中有对应的资源可以下载,不用自己满世界找文件了。

    wav 文件的读取可以用仓库中的 wave.c 和 wave.h 文件操作。

    接下来就是从输入文件中读取 sample ,并交由 libsonic 处理,最后将处理好 sample 写入到输出文件中。

    这有个概念就是:视频的处理,我们都是说一帧一帧的,音频就没有帧的概念,一般都是说采样点,比如要处理多少个采样点之类的说法。

    代码示例中,readFromWaveFile 方法从文件中读取 sample ,sonicWriteShortToStream 方法将 sample 交由 libsonic 处理。

    libsonic 中有个 sonicStream 的结构,它用来存储变换后的数据,这里面会根据设置的 speed 和 volume 做处理。

    最后再通过 sonicReadShortFromStream 将数据从 libsonic 读出,然后通过 writeToWaveFile 方法写入到输出文件中。

    经过测试,以上方法确实可以实现音量调节和变速的效果,并且在变速的同时保持着原来的音调。

    小结

    以上就是关于 libsonic 的介绍与实践,除了 wav 格式文件,pcm 格式数据也同样可以做变速的。

    关注公众号 音视频开发进阶,在后续还会进行更新源码分析等更多内容,敬请期待~~~

    8 条回复    2021-11-01 18:01:04 +08:00
    shawndev
        1
    shawndev  
       2021-11-01 10:20:28 +08:00
    之前刚好接触过音频变速开发 ,当时了解的主要有 libsoundtouch 和 fmod ,libsonic 有哪些额外的优势吗?
    glumess
        2
    glumess  
    OP
       2021-11-01 10:23:38 +08:00
    @shawndev 可以支持高于两倍速的播放吧,另外在视频播放上比较好,libsoundtouch 侧重于音乐? 两个的实现方式不太一样。而且 libsonic 的接入方式很简单...
    minami
        3
    minami  
       2021-11-01 11:02:41 +08:00
    实测 libsonic 效果会比 libsoundtouch 差多少,相差不大的话感觉确实 libsonic 用起来更方便
    kimown
        4
    kimown  
       2021-11-01 12:14:06 +08:00 via Android
    lz 有音频合并的库推荐吗,就是多个 mp3 合并成 1 个
    glumess
        5
    glumess  
    OP
       2021-11-01 12:51:40 +08:00
    @minami 是的,这个也是看使用场景来区分了...
    glumess
        6
    glumess  
    OP
       2021-11-01 12:52:03 +08:00   ❤️ 1
    @kimown 我搜一搜看看,有没有好得库推荐
    xlsepiphone
        7
    xlsepiphone  
       2021-11-01 13:02:59 +08:00
    比 ffmpeg 的 atempo filter 有什么优势吗
    glumess
        8
    glumess  
    OP
       2021-11-01 18:01:04 +08:00
    @xlsepiphone 性能上没测过,可能包体积有优势吧,不用引入 ffmpeg 了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1052 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 20ms · UTC 19:28 · PVG 03:28 · LAX 11:28 · JFK 14:28
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.