我是靠谱客的博主 霸气短靴,最近开发中收集的这篇文章主要介绍左右声道测试音频_[译]使用Go播放音频:立体声,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

在上一篇文章中我们编写了代码来更改 wave 文件的幅度。

现在,我们将看一下如何通过调节声像将单声道 wave 文件转换为立体声 wave 文件,并探索 WAVE 文件格式如何在内部表示该文件。

频道

WAVE 文件中的原始音频数据由多个帧组成。目前,我们称它们为“样本”,尽管严格来讲这并不完全正确。实际上,当我们假设一个单声道音频文件时,原始音频数据中的单个浮动仅对应于一个样本。

当你有多个频道时,单个“样本”可以包含多个帧。由于每个频道都需要在任何给定的时间点播放特定的“帧”。

在 WAVE 文件格式中,频道是交错的。例如,立体声文件的布局应如下所示:

f3473f2c790cf9ee708a017cb19a0f32.png
image

在这里,每个样本都由两个帧组成。这样,1 和 2 构成样本 1,3 和 4 构成样本 2,依此类推。

程序由于音频文件中的fmt块而知道如何解析原始音频数据,这些块指定了原始音频数据中存在的频道数。wave 文件中的最大频道数实际上高达 65,536,对于音频数据而言实际上没有任何意义。

一些常见的是:

  • 1 频道:单声道
  • 2 频道:立体声
  • 3 频道:立体声+中央声道
  • 4 频道:四声道
  • 5 频道:“环绕声”

为了方便起见,我们主要处理单声道和立体声文件。它们不仅是最常用的,而且还使我们可以更方便的测试代码。

音频调节(Panning)

那么什么是 pan?平移音频信号时,实际上是在左侧或右侧使音频信号“更大声”。通常在DAW[1]中由“自动化轨道”表示,其值-1 到 1 之间的。

应用平底锅的程序将采用三个参数:

  • 输入文件
  • 输出文件
  • 音频调节(-1 至 1)

对于输入文件,我们将其限制为单声道文件,对于输出文件,我们将生成立体声文件。pan 变量应在-1(左)和 1(右)之间。在开始应用 pan 之前,我们需要从输入 wave 文件中读取原始音频数据。请记住,要读取 wave 文件,我们将使用我们之前制作的GoAudio[2]库:

import (
        wav "github.com/DylanMeeus/GoAudio/wave"
)

该程序的设置非常简单,我们将使用内置flags程序包来解析 CLI 的输入。

var (
 input  = flag.String("i", "", "input file")
 output = flag.String("o", "", "output file")
 pan    = flag.Float64("p", 0.0, "pan in range of -1 (left) to 1 (right)")
)

设置好标志后,我们就可以解析它们并读取输入文件。

func main() {
 flag.Parse()
 infile := *input
 outfile := *output
 panfac := *pan
 wave, err := wav.ReadWaveFile(infile)
 if err != nil {
  panic("Could not parse wave file")
 }
        ...
}

到目前为止,一切都很好。我们已经解析了输入,因此我们知道要为 pan 使用哪个值,并且还读取了原始音频数据。但是,如何从(-1)到(1)范围内的值变为左侧或右侧的实际响度变化?我们可以想象一个简单的函数看起来像下面这样:

type panposition struct {
 left, right float64
}

func calculatePosition(position float64) panposition {
 position *= 0.5
 return panposition{
  left:  position - 0.5,
  right: position + 0.5,
 }
}

在这里,我们使用的结构可以代表左声道和右声道的幅度在 0 到 1 的范围内。这样我们观察到以下值:

位置左声道右声道
00.50.5
1 个01 个
-11 个0

换句话说,如果位置为零,则声音在耳机的左侧和右侧之间达到完美平衡。而在极值中,声音只能是左侧或右侧。

就像上一篇文章一样,我们实际上需要根据在calculatePosition函数中找到的位置数据来更改帧。我们可以创建一个函数,该函数根据上一个函数中panposition返回的值修改帧。

func applyPan(frames []wav.Frame, p panposition) []wav.Frame {
 out := []wav.Frame{}
 for _, s := range frames {
  out = append(out, wav.Frame(float64(s)*p.left))
  out = append(out, wav.Frame(float64(s)*p.right))
 }
 return out
}

请注意,我们如何实际将两个frame附加到frames结果切片上!这就是我们交错左右声道的方式。

现在我们可以完成 main 方法:

        ...
 pos := calculatePosition(panfac)
 scaledFrames := applyPan(wave.Frames, calculatePosition(panfac))
 wave.NumChannels = 2 // samples are now stereo, so we need dual channels
 if err := wav.WriteFrames(scaledFrames, wave.WaveFmt, outfile); err != nil {
  panic(err)
 }

这里至关重要的一步是,在编写样本之前,我们已经运行了wave.NumChannels=2。否则,wave 文件将被解释为单声道声音文件,而我们的声像效果将会丢失。

测试代码

为了测试,我主要使用这个简单的mono 文件。

如果运行go run main.go -i mono.wav -o left-side.wav -p -1,将得到:

left-side.wav:

当我们运行时,go run main.go -i mono.wav -o right-side.wav -p 1我们得到:

right-side.wav:

下一步?

我们正在使用的 pan 功能实际上存在一个缺陷。但是,对于我们来说,这还不是很明显,因为我们只为整个音频源设置 pan。要了解为什么不完美,我们需要首先引入断点作为创建自动化跟踪的一种方式,因此我们接下来的几篇文章的重点将是断点。:-)

参考资料

[1]

DAW: https://en.wikipedia.org/wiki/Digital_audio_workstation

[2]

GoAudio: https://github.com/DylanMeeus/GoAudio

最后

以上就是霸气短靴为你收集整理的左右声道测试音频_[译]使用Go播放音频:立体声的全部内容,希望文章能够帮你解决左右声道测试音频_[译]使用Go播放音频:立体声所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(45)

评论列表共有 0 条评论

立即
投稿
返回
顶部