女儿:爸爸,我最近迷上了翻唱,哎,只是可惜~

爸爸:可惜啥??

女儿:可惜这么好听的声音不能跟别人一起分享。

爸爸:作为麦霸还有这种苦恼?

女儿:现场唱当然没问题,但总不能想要分享的时候就拉着朋友们去K歌吧。

爸爸:也是。

女儿:所以我想要的效果是把翻唱歌曲的伴奏和我的唱音一起录制保存成CD或mp3。就像在录音棚里录歌那样。

爸爸:明白。爸爸试试给你做个recording studio。

女儿:这么厉害?期待期待!

<思路>

  • 女儿收藏的mp3歌曲都是带原唱的作品,第一个要解决的问题是去原唱,转成伴音。sox里有个oops效果器可以完成这个任务。
  • 接着,需要将女儿的唱音(即麦克风录音) 跟伴音做混合,存成文件。
    • 这里有个唱音跟伴音同步的问题。可以在唱音路径和伴音路径上各加一个延时器,调音师通过调节这两个延时器来让唱音跟伴音同步。
    • 调音师需要能够听到唱伴混合的最终效果,所以需要设计一路最终播放到调音师的耳机。
    • 歌手需要只听到伴奏的声音,所以需要设计一路伴奏音播放到歌手的耳机。

<代码实现>

MyEngineerDaddy_6.cpp

#include <lark/lark.h>
#include <klogging.h>#if defined(__APPLE__)
#define SUFIX ".dylib"
#elif defined(_WIN32)
#define SUFIX ".dll"
#else
#define SUFIX ".so"
#endifint main()
{const unsigned int rate = 44100;const lark::SampleFormat format = lark::SampleFormat::S16_LE;const unsigned int chNum = 2;const unsigned int frameDuration_ms = 20;const lark::samples_t frameSizeInSamples = frameDuration_ms * rate / 1000;// Create the playback route named RouteAlark::Route *route = lark::Lark::Instance().NewRoute("RouteA");if (!route) {KLOGE("Failed to create route");return -1;}// Create RouteA's blocksconst char *soFileName = "libblkfilereader" SUFIX;lark::Parameters args;args.push_back("./original_song-44100_16_2.pcm");lark::Block *blkFileReader = route->NewBlock(soFileName, true, false, args);if (!blkFileReader) {KLOGE("Failed to new a block from %s", soFileName);return -1;}soFileName = "libblkformatadapter" SUFIX;lark::Block *blkFormatAdapter0 = route->NewBlock(soFileName, false, false);if (!blkFormatAdapter0) {KLOGE("Failed to new a block from %s", soFileName);return -1;}soFileName = "libblkformatadapter" SUFIX;lark::Block *blkFormatAdapter1 = route->NewBlock(soFileName, false, false);if (!blkFormatAdapter1) {KLOGE("Failed to new a block from %s", soFileName);return -1;}soFileName = "libblksoxeffect" SUFIX;args.clear();args.push_back("oops");lark::Block *blkSoxOops = route->NewBlock(soFileName, false, false, args);if (!blkSoxOops) {KLOGE("Failed to new a block from %s", soFileName);return -1;}soFileName = "libblkduplicator" SUFIX;lark::Block *blkDuplicator1 = route->NewBlock(soFileName, false, false);if (!blkDuplicator1) {KLOGE("Failed to new a block from %s", soFileName);return -1;}soFileName = "libblkduplicator" SUFIX;lark::Block *blkDuplicator0 = route->NewBlock(soFileName, false, false);if (!blkDuplicator0) {KLOGE("Failed to new a block from %s", soFileName);return -1;}soFileName = "libblkpaplayback" SUFIX;lark::Block *blkPlayback0 = route->NewBlock(soFileName, false, true);if (!blkPlayback0) {KLOGE("Failed to new a block from %s", soFileName);return -1;}soFileName = "libblkpaplayback" SUFIX;lark::Block *blkPlayback1 = route->NewBlock(soFileName, false, true);if (!blkPlayback1) {KLOGE("Failed to new a block from %s", soFileName);return -1;}soFileName = "libblkpacapture" SUFIX;lark::Block *blkCapture = route->NewBlock(soFileName, true, false);if (!blkCapture) {KLOGE("Failed to new a block from %s", soFileName);return -1;}soFileName = "libblkdelay" SUFIX;lark::Block *blkDelay0 = route->NewBlock(soFileName, false, false);if (!blkDelay0) {KLOGE("Failed to new a block from %s", soFileName);return -1;}soFileName = "libblkdelay" SUFIX;lark::Block *blkDelay1 = route->NewBlock(soFileName, false, false);if (!blkDelay1) {KLOGE("Failed to new a block from %s", soFileName);return -1;}soFileName = "libblkmixer" SUFIX;lark::Block *blkMixerL = route->NewBlock(soFileName, false, false);if (!blkMixerL) {KLOGE("Failed to new a block from %s", soFileName);return -1;}soFileName = "libblkdeinterleave" SUFIX;lark::Block *blkDeinterleave = route->NewBlock(soFileName, false, false);if (!blkDeinterleave) {KLOGE("Failed to new a block from %s", soFileName);return -1;}soFileName = "libblkmixer" SUFIX;lark::Block *blkMixerR = route->NewBlock(soFileName, false, false);if (!blkMixerR) {KLOGE("Failed to new a block from %s", soFileName);return -1;}soFileName = "libblkinterleave" SUFIX;lark::Block *blkInterleave = route->NewBlock(soFileName, false, false);if (!blkInterleave) {KLOGE("Failed to new a block from %s", soFileName);return -1;}soFileName = "libblkfilewriter" SUFIX;args.clear();args.push_back("./covered_song-44100_16_2.pcm");lark::Block *blkFileWriter = route->NewBlock(soFileName, false, true, args);if (!blkFileWriter) {KLOGE("Failed to new a block from %s", soFileName);return -1;}// Create RouteA's linksif (!route->NewLink(rate, format, chNum, frameSizeInSamples, blkFileReader, 0, blkFormatAdapter0, 0)) {KLOGE("Failed to new a link");return -1;}if (!route->NewLink(rate, lark::SampleFormat_S32, chNum, frameSizeInSamples, blkFormatAdapter0, 0, blkSoxOops, 0)) {KLOGE("Failed to new a link");return -1;}if (!route->NewLink(rate, lark::SampleFormat_S32, chNum, frameSizeInSamples, blkSoxOops, 0, blkDuplicator1, 0)) {KLOGE("Failed to new a link");return -1;}if (!route->NewLink(rate, lark::SampleFormat_S32, chNum, frameSizeInSamples, blkDuplicator1, 0, blkPlayback1, 0)) {KLOGE("Failed to new a link");return -1;}if (!route->NewLink(rate, lark::SampleFormat_S32, chNum, frameSizeInSamples, blkDuplicator1, 1, blkDelay1, 0)) {KLOGE("Failed to new a link");return -1;}if (!route->NewLink(rate, lark::SampleFormat_S32, 1, frameSizeInSamples, blkCapture, 0, blkDelay0, 0)) {KLOGE("Failed to new a link");return -1;}if (!route->NewLink(rate, lark::SampleFormat_S32, chNum, frameSizeInSamples, blkDelay1, 0, blkDeinterleave, 0)) {KLOGE("Failed to new a link");return -1;}if (!route->NewLink(rate, lark::SampleFormat_S32, 1, frameSizeInSamples, blkDeinterleave, 0, blkMixerL, 1)) {KLOGE("Failed to new a link");return -1;}if (!route->NewLink(rate, lark::SampleFormat_S32, 1, frameSizeInSamples, blkDeinterleave, 1, blkMixerR, 1)) {KLOGE("Failed to new a link");return -1;}if (!route->NewLink(rate, lark::SampleFormat_S32, 1, frameSizeInSamples, blkDelay0, 0, blkDuplicator0, 0)) {KLOGE("Failed to new a link");return -1;}if (!route->NewLink(rate, lark::SampleFormat_S32, 1, frameSizeInSamples, blkDuplicator0, 0, blkMixerL, 0)) {KLOGE("Failed to new a link");return -1;}if (!route->NewLink(rate, lark::SampleFormat_S32, 1, frameSizeInSamples, blkDuplicator0, 1, blkMixerR, 0)) {KLOGE("Failed to new a link");return -1;}if (!route->NewLink(rate, lark::SampleFormat_S32, 1, frameSizeInSamples, blkMixerL, 0, blkInterleave, 0)) {KLOGE("Failed to new a link");return -1;}if (!route->NewLink(rate, lark::SampleFormat_S32, 1, frameSizeInSamples, blkMixerR, 0, blkInterleave, 1)) {KLOGE("Failed to new a link");return -1;}if (!route->NewLink(rate, lark::SampleFormat_S32, chNum, frameSizeInSamples, blkInterleave, 0, blkPlayback0, 0)) {KLOGE("Failed to new a link");return -1;}if (!route->NewLink(rate, lark::SampleFormat_S32, chNum, frameSizeInSamples, blkInterleave, 1, blkFormatAdapter1, 0)) {KLOGE("Failed to new a link");return -1;}if (!route->NewLink(rate, lark::SampleFormat_S16, chNum, frameSizeInSamples, blkFormatAdapter1, 0, blkFileWriter, 0)) {KLOGE("Failed to new a link");return -1;}// Start() or Stop()if (route->Start() < 0) {KLOGE("Failed to start route");return -1;}while (1) {KLOGA("Press 's' to stop, 'p' to play, 'q' to exit");
again:char c;if (scanf("%c", &c) != 1)break;if (c == '\n')goto again;if (c == 's') {route->Stop();} else if (c == 'p') {route->Start();} else if (c == 'q') {break;}}// Automatically release resourcesreturn 0;
}

<编译运行>

$ g++ -lklogging -llark MyEngineerDaddy_6.cpp -o MyEngineerDaddy_6

女儿跟爸爸一起带上耳机。运行程序:

$ ./MyEngineerDaddy_6
06-23 20:44:20.464254 I | Created RouteA
06-23 20:44:20.465689 I | RouteA: blkfilereader_0: Created
06-23 20:44:20.466330 I | RouteA: blkformatadapter_0: Created
06-23 20:44:20.466500 I | RouteA: blkformatadapter_1: Created
06-23 20:44:20.479798 I | RouteA: blksoxeffect_oops_0: Created
06-23 20:44:20.480521 I | RouteA: blkduplicator_0: Created
06-23 20:44:20.480679 I | RouteA: blkduplicator_1: Created
06-23 20:44:20.481479 I | RouteA: blkpaplayback_0: Created
06-23 20:44:20.481626 I | RouteA: blkpaplayback_1: Created
06-23 20:44:20.482203 I | RouteA: blkpacapture_0: Created
06-23 20:44:20.482768 I | RouteA: blkdelay_0: Created
06-23 20:44:20.482881 I | RouteA: blkdelay_1: Created
06-23 20:44:20.483394 I | RouteA: blkmixer_0: Created
06-23 20:44:20.483877 I | RouteA: blkdeinterleave_0: Created
06-23 20:44:20.483970 I | RouteA: blkmixer_1: Created
06-23 20:44:20.484438 I | RouteA: blkinterleave_0: Created
06-23 20:44:20.484944 I | RouteA: blkfilewriter_0: Created
06-23 20:44:20.484982 I | RouteA: lnk_0: Created blkfilereader_0(O00) --> (I00)blkformatadapter_0
06-23 20:44:20.485131 I | RouteA: lnk_1: Created blkformatadapter_0(O00) --> (I00)blksoxeffect_oops_0
06-23 20:44:20.485158 I | RouteA: lnk_2: Created blksoxeffect_oops_0(O00) --> (I00)blkduplicator_0
06-23 20:44:20.537353 I | RouteA: lnk_3: Created blkduplicator_0(O00) --> (I00)blkpaplayback_1
06-23 20:44:20.537433 I | RouteA: lnk_4: Created blkduplicator_0(O01) --> (I00)blkdelay_1
06-23 20:44:20.541395 I | RouteA: lnk_5: Created blkpacapture_0(O00) --> (I00)blkdelay_0
06-23 20:44:20.541438 I | RouteA: lnk_6: Created blkdelay_1(O00) --> (I00)blkdeinterleave_0
06-23 20:44:20.541467 I | RouteA: lnk_7: Created blkdeinterleave_0(O00) --> (I01)blkmixer_0
06-23 20:44:20.541483 I | RouteA: lnk_8: Created blkdeinterleave_0(O01) --> (I01)blkmixer_1
06-23 20:44:20.541499 I | RouteA: lnk_9: Created blkdelay_0(O00) --> (I00)blkduplicator_1
06-23 20:44:20.541513 I | RouteA: lnk_10: Created blkduplicator_1(O00) --> (I00)blkmixer_0
06-23 20:44:20.541528 I | RouteA: lnk_11: Created blkduplicator_1(O01) --> (I00)blkmixer_1
06-23 20:44:20.541551 I | RouteA: lnk_12: Created blkmixer_0(O00) --> (I00)blkinterleave_0
06-23 20:44:20.541573 I | RouteA: lnk_13: Created blkmixer_1(O00) --> (I01)blkinterleave_0
06-23 20:44:20.543547 I | RouteA: lnk_14: Created blkinterleave_0(O00) --> (I00)blkpaplayback_0
06-23 20:44:20.543578 I | RouteA: lnk_15: Created blkinterleave_0(O01) --> (I00)blkformatadapter_1
06-23 20:44:20.543602 I | RouteA: lnk_16: Created blkformatadapter_1(O00) --> (I00)blkfilewriter_0
06-23 20:44:20.544356 I | RouteA: Started
06-23 20:44:20.544370 I | RouteA: Status is RUNNING
06-23 20:44:20.544388 A | Press 's' to stop, 'p' to play, 'q' to exit

此时女儿耳机响起了去掉原声的伴奏,同时爸爸耳机响起了伴奏加女儿话筒声音的混合。

开始试音!女儿跟着伴奏唱了起来,爸爸仔细地听着伴奏和人声是否同步,当感觉人声比伴奏快时,就将人声那路的延时器(blkdelay_0)调多一点延时;

$ lkdb setparam RouteA blkdelay_0 1 150000   # 人声那路延时150毫秒
Success

当感觉伴奏比人声快时,就将伴奏那路的延时器(blkdelay_1)调多一点延时。

$ lkdb setparam RouteA blkdelay_1 1 240000   # 伴音那路延时240毫秒
Success

最终确定了一个完美同步的延时值为:伴奏那路需要延时476毫秒。

试音结束,正式录音!
在程序中延时器刚被创建后的位置,添加一行初始延时值的参数设定:

route->SetParameter(blkDelay1, 1, 476000);  // 伴奏那路延时476毫秒

再次编译运行程序。

女儿对着麦克风把整首歌完美地演唱完成。伴奏结束,翻唱的录音文件被blkfilewriter_0自动保存成了./covered_song-44100_16_2.pcm

爸爸:最后用sox工具把pcm文件 转成mp3文件。完成!

女儿:哈哈,我听听我听听!

爸爸:(开始播放mp3)

女儿:嘻嘻,真的是我的翻唱版本耶!

爸爸:还不赖,期待你更多的作品。

女儿:好嘞!

lark项目地址:https://gitee.com/wksuper/lark-release

我的工程师爸爸 - 音频应用DIY系列之六:录音棚相关推荐

  1. 我的工程师爸爸 - 音频应用DIY系列之四:慢速英语

    女儿:爸爸,最近我在练英语听力,可是学校发的听力课文说的太快了,我听不懂,能让听力语音说慢点吗? 爸爸:嗯- 这个容易,马上给你做一个. <思路> 调节节拍(tempo)可实现减速或加速播 ...

  2. 我的工程师爸爸 - 音频应用DIY系列之五:音乐创作

    女儿:爸爸,今天我在广场上看到一个人拿一把吉他自弹自唱,非常动听! 爸爸:街头艺人呗. 女儿:他的吉他能发出各种各样神奇的音效,一会儿是起势蓬勃的合唱效果,一会儿是余音绕梁的颤抖效果.您能帮我也买一个 ...

  3. php diy,PHP DIY 系列------基础篇:1. PSR

    PHP DIY系列–一起手写一个api框架 创作初衷 有没有用烦了CURD? 各种框架是不是有点头大? 有没有尝试自己设计一个框架? 学了PHP语法,没有项目去实战,夯实基础 希望能帮助能让你快速地搭 ...

  4. 树莓派slam_SLAM+语音机器人DIY系列:(五)树莓派3开发环境搭建——6.树莓派USB与tty串口号绑定...

    摘要 通过前面一系列的铺垫,相信大家对整个miiboo机器人的DIY有了一个清晰整体的认识.接下来就正式进入机器人大脑(嵌入式主板:树莓派3)的开发.本章将从树莓派3的开发环境搭建入手,为后续ros开 ...

  5. SLAM+语音机器人DIY系列:(二)ROS入门——2.ROS系统整体架构

    摘要 ROS机器人操作系统在机器人应用领域很流行,依托代码开源和模块间协作等特性,给机器人开发者带来了很大的方便.我们的机器人"miiboo"中的大部分程序也采用ROS进行开发,所 ...

  6. 软件测试工程师工作日常100问系列三【乐搏TestPRO】

    上两个系列讲了软件测试基本常识问题,包括软件测试概念.流程及工作日常及需要具备的只是体系.请参阅: 软件测试工程师工作日常100问系列一 软件测试工程师工作日常100问系列二 今天我们来讲讲接口测试在 ...

  7. 《视频直播技术详解》系列之六:延迟优化

    七牛云于 6 月底发布了一个针对视频直播的实时流网络 LiveNet 和完整的直播云解决方案,很多开发者对这个网络和解决方案的细节和使用场景非常感兴趣. 结合七牛实时流网络 LiveNet 和直播云解 ...

  8. 「视频直播技术详解」系列之六:现代播放器原理

    ​关于直播的技术文章不少,成体系的不多.我们将用七篇文章,更系统化地介绍当下大热的视频直播各环节的关键技术,帮助视频直播创业者们更全面.深入地了解视频直播技术,更好地技术选型. 本系列文章大纲如下: ...

  9. Camera开发系列之六-使用mina框架实现视频推流

    章节 Camera开发系列之一-显示摄像头实时画面 Camera开发系列之二-相机预览数据回调 Camera开发系列之三-相机数据硬编码为h264 Camera开发系列之四-使用MediaMuxer封 ...

最新文章

  1. 轻松搞定Retrofit不同网络请求方式的请求参数配置,及常用注解使用
  2. wxWidgets:wxMemoryInputStream类用法
  3. fpga初始化错误_FPGA低温启动失败
  4. 用数据库的方式编辑上一页 下一页
  5. 刺激味蕾的甜品果汁饮品psd分层海报素材,愉悦使用图层!
  6. 计算机的的打印服务,win7电脑打印机服务被强行关闭怎么办
  7. python是什么软件-Python 是什么软件?
  8. quilleditor 字体大小设置_quill-editor如何更改字体配置?
  9. OpenCV 累加一个三通道矩阵的所有元素
  10. python3爬虫必学Xpath,快速使用lxml.etree
  11. 如何将XDF转换成PDF(内容可编辑)
  12. Linux 基础知识
  13. 骨骼动画原理学习笔记
  14. 如何备份以及恢复Windows系统的环境变量
  15. Jenkins上配置Robot Framework测试邮件通知模板
  16. 蓝牙耳机蓝牙音箱出口加拿大亚马逊ICID认证周期费用
  17. 光绘文件 c语言 解析,基于NXP TEA19051的Type-C 60W多协议快充方案设计(含gerber+方案阐述 )...
  18. Excel删除区域内的空白格
  19. SQuirrel SQL Client数据库连接工具的配置与使用
  20. Unity URP DOTS Pathfinding+Local avoidance

热门文章

  1. 从 Context 家谱来聊一聊 Context 家族使用的设计模式?
  2. docker快速安装redis
  3. Python中的面向对象编程(类编程)由简单到复杂的示例代码
  4. 常量表达式概念与用处
  5. untiy加载一张本地图片到Image 精灵
  6. 计算机职称考试有几个类别?
  7. 全国职称计算机考试办公应用选几科,全国职称计算机考试科目有哪些
  8. 一个自定义的html5视频播放器
  9. lisp 习题 钱币换算问题
  10. 周鸿祎的葵花宝典你敢照学吗?