美文网首页
使用Go播放音频:提取断点

使用Go播放音频:提取断点

作者: 豆腐匠 | 来源:发表于2020-10-19 20:03 被阅读0次

    处理原文地址:https://dylanmeeus.github.io/posts/audio-from-scratch-pt7/

    到目前为止,我们对断点所做的工作涉及到创建断点文件并使用该文件来自动执行轨道的一部分。现在,我们来看看如何采用现有轨道并从中提取一些断点。在本文中,我们将介绍从.wave文件提取振幅的方法,因为这是我们可以提取的最直接的属性之一。

    这里提及的所有内容在过去的文章中都有提及,但是我们将结合我们已经学到的知识来创建一个新的工具来执行此操作。

    简单讲,我们将做下面的工作:

    • 遍历我们的帧
    • 记录给定的幅度(帧值)
    • 写入断点文件

    从本质上讲,这足够简单,但是我们将对该算法进行两个小的修改。首先,我们不想只为每个帧生成一个断点,这只会导致列表等于我们的帧数据。通过以一定间隔(例如,每10ms)拍摄快照,我们将使用更少的空间,并利用在特定时间查找断点值时发生的线性插值的优势。

    当前断点代码的一个小扩展的功能是,我们需要能够将样本“分批”到给定时间的切片中。

    所有的代码可以在GitHub找到。

    批量处理

    BatchSamples函数将以Waveseconds float64作为输入,因为这为我们提供了足够的数据来批处理帧。给定特定的SampleRate,我们知道每秒在文件中看到多少个样本。有了这些信息,我们可以找到每秒的帧数。尽管每秒帧数仅等于单声道文件的每秒样本数,但是适应多声道音频只是将SampleSize与声道数量相乘的问题。

    因此,SampleSize可以表示为: SampleSize = SampleRate * Channels * seconds

    当我们知道样本大小时,我们可以将原始帧分割成这个尺寸大小的切片。

    func BatchSamples(data Wave, seconds float64) [][]Frame {
        if seconds == 0 {
            return [][]Frame{
                data.Frames,
            }
        }
    
        samples := data.Frames
    
        sampleSize := int(float64(data.SampleRate * data.NumChannels) * float64(seconds))
    
        batches := len(samples) / sampleSize
        if len(samples)%sampleSize != 0 {
            batches++
        }
    
        batched := make([][]Frame, batches) // this should be round up..
        for i := 0; i < len(batched); i++ {
            start := i * sampleSize
            if start > len(samples) {
                return batched
            }
            maxTake := i*sampleSize + sampleSize
            if maxTake >= len(samples)-1 {
                maxTake = len(samples)
            }
            subs := samples[start:maxTake]
            batched[i] = subs
        }
        return batched
    }
    
    

    从批次中提取数据

    一旦我们可以将帧拆分为批次,就可以调用BatchSamples(wave,x)以获取给定持续时间的请求批次。然后,我们可以找到最大振幅,并跟踪所有批次的最大振幅。

    我们使用GoAudio解析输入文件 。除此之外,我们还将帧发送到“ BatchSamples”方法。(为简便起见,忽略了错误处理)

    func main() {
        flag.Parse()
        infile := *input
        outfile := *output
        wave, _ := wav.ReadWaveFile(infile)
    
            frameDuration := 15.0 // 15 ms
        ticks := frameDuration / 1000.0
        batches := wav.BatchSamples(wave, ticks)
            ...
    
    

    完成此操作后,我们将在15 ms的切片中分批采样。我们将通过遍历它们并收集每个最大幅度来继续。由于最终需要将其写入输出文件,因此现在将其存储在StringBuilder中。

    我们还需要跟踪的另一件事是在time:value断点文件中哪个时间可以插入。对于每个批次,此时间值将以15ms为增量。

    我们继续主函数:

            ...
        strout := strings.Builder{}
        elapsed := 0.0
        for _, b := range batches {
            maxa := maxAmp(b)
            es := strconv.FormatFloat(elapsed, 'f', 8, 64)
            fs := strconv.FormatFloat(maxa, 'f', 8, 64)
            strout.WriteString(es + ":" + fs + "\n")
            elapsed += ticks
        }
        ioutil.WriteFile(outfile, []byte(strout.String()), 0644)
    }
    
    

    最后剩下的就是通过遍历切片中的每个帧并返回最大安培来填充我们的maxAmp方法。

    func maxAmp(ss []wav.Frame) float64 {
        if len(ss) == 0 {
            return 0
        }
        max := -1.0 // because they are in range -1 .. 1
        for _, a := range ss {
            if float64(a) > max {
                max = float64(a)
            }
        }
        return float64(max)
    }
    
    

    运行断点提取器

    当我们运行go run main.go -i input.wav -w 15 -o output.brk时,我们可以生成一个断点文件。我创建了一个振幅可变的示例波形(以后我会写一些有关它的文章)。你可以在这里听。

    提取的断点文件可以在这里看到。

    使用audacity和绘图工具,可以将波形与断点提取器生成的波形进行比较。

    audacityplot breakpointplot

    相关文章

      网友评论

          本文标题:使用Go播放音频:提取断点

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