我的工程师爸爸 - 音频应用DIY系列之六:录音棚
女儿:爸爸,我最近迷上了翻唱,哎,只是可惜~
爸爸:可惜啥??
女儿:可惜这么好听的声音不能跟别人一起分享。
爸爸:作为麦霸还有这种苦恼?
女儿:现场唱当然没问题,但总不能想要分享的时候就拉着朋友们去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系列之六:录音棚相关推荐
- 我的工程师爸爸 - 音频应用DIY系列之四:慢速英语
女儿:爸爸,最近我在练英语听力,可是学校发的听力课文说的太快了,我听不懂,能让听力语音说慢点吗? 爸爸:嗯- 这个容易,马上给你做一个. <思路> 调节节拍(tempo)可实现减速或加速播 ...
- 我的工程师爸爸 - 音频应用DIY系列之五:音乐创作
女儿:爸爸,今天我在广场上看到一个人拿一把吉他自弹自唱,非常动听! 爸爸:街头艺人呗. 女儿:他的吉他能发出各种各样神奇的音效,一会儿是起势蓬勃的合唱效果,一会儿是余音绕梁的颤抖效果.您能帮我也买一个 ...
- php diy,PHP DIY 系列------基础篇:1. PSR
PHP DIY系列–一起手写一个api框架 创作初衷 有没有用烦了CURD? 各种框架是不是有点头大? 有没有尝试自己设计一个框架? 学了PHP语法,没有项目去实战,夯实基础 希望能帮助能让你快速地搭 ...
- 树莓派slam_SLAM+语音机器人DIY系列:(五)树莓派3开发环境搭建——6.树莓派USB与tty串口号绑定...
摘要 通过前面一系列的铺垫,相信大家对整个miiboo机器人的DIY有了一个清晰整体的认识.接下来就正式进入机器人大脑(嵌入式主板:树莓派3)的开发.本章将从树莓派3的开发环境搭建入手,为后续ros开 ...
- SLAM+语音机器人DIY系列:(二)ROS入门——2.ROS系统整体架构
摘要 ROS机器人操作系统在机器人应用领域很流行,依托代码开源和模块间协作等特性,给机器人开发者带来了很大的方便.我们的机器人"miiboo"中的大部分程序也采用ROS进行开发,所 ...
- 软件测试工程师工作日常100问系列三【乐搏TestPRO】
上两个系列讲了软件测试基本常识问题,包括软件测试概念.流程及工作日常及需要具备的只是体系.请参阅: 软件测试工程师工作日常100问系列一 软件测试工程师工作日常100问系列二 今天我们来讲讲接口测试在 ...
- 《视频直播技术详解》系列之六:延迟优化
七牛云于 6 月底发布了一个针对视频直播的实时流网络 LiveNet 和完整的直播云解决方案,很多开发者对这个网络和解决方案的细节和使用场景非常感兴趣. 结合七牛实时流网络 LiveNet 和直播云解 ...
- 「视频直播技术详解」系列之六:现代播放器原理
关于直播的技术文章不少,成体系的不多.我们将用七篇文章,更系统化地介绍当下大热的视频直播各环节的关键技术,帮助视频直播创业者们更全面.深入地了解视频直播技术,更好地技术选型. 本系列文章大纲如下: ...
- Camera开发系列之六-使用mina框架实现视频推流
章节 Camera开发系列之一-显示摄像头实时画面 Camera开发系列之二-相机预览数据回调 Camera开发系列之三-相机数据硬编码为h264 Camera开发系列之四-使用MediaMuxer封 ...
最新文章
- 轻松搞定Retrofit不同网络请求方式的请求参数配置,及常用注解使用
- wxWidgets:wxMemoryInputStream类用法
- fpga初始化错误_FPGA低温启动失败
- 用数据库的方式编辑上一页 下一页
- 刺激味蕾的甜品果汁饮品psd分层海报素材,愉悦使用图层!
- 计算机的的打印服务,win7电脑打印机服务被强行关闭怎么办
- python是什么软件-Python 是什么软件?
- quilleditor 字体大小设置_quill-editor如何更改字体配置?
- OpenCV 累加一个三通道矩阵的所有元素
- python3爬虫必学Xpath,快速使用lxml.etree
- 如何将XDF转换成PDF(内容可编辑)
- Linux 基础知识
- 骨骼动画原理学习笔记
- 如何备份以及恢复Windows系统的环境变量
- Jenkins上配置Robot Framework测试邮件通知模板
- 蓝牙耳机蓝牙音箱出口加拿大亚马逊ICID认证周期费用
- 光绘文件 c语言 解析,基于NXP TEA19051的Type-C 60W多协议快充方案设计(含gerber+方案阐述 )...
- Excel删除区域内的空白格
- SQuirrel SQL Client数据库连接工具的配置与使用
- Unity URP DOTS Pathfinding+Local avoidance