首页 > 解决方案 > FLAC和MP3的色印指纹

问题描述

我想使用 Go 中的chromaprint库创建 FLAC 或 MP3 文件的声学指纹。我一直在玩以下两个 Go 库:

使用以下代码,可以创建“原始音频数据流”的指纹(其中reader类型为io.Reader):

fpcalc := gochroma.New(gochroma.AlgorithmDefault)
defer fpcalc.Close()

fprint, err := fpcalc.Fingerprint(
        fingerprint.RawInfo{
                Src:        reader,
                Channels:   2,
                Rate:       44100,  
                MaxSeconds: 120,
        }       
)

不幸的是,我无法弄清楚“原始音频数据流”的确切含义(我的猜测:WAVE LPCM 流),但我知道我不能简单地使用打开 FLAC 或 MP3 文件os.Open并将流传递给fingerprint.RawInfo.Src. 有一些示例,但这些示例适用于以 . 结尾的文件.raw

如何在 Go 中将 FLAC(或辅助 MP3)文件/流转换为原始音频数据流?我的猜测是使用像go-flac这样的 Go FLAC 库,但我不确定从哪里开始。欢迎任何提示!

编辑

通过go-flacGetStreamInfo应该可以访问 FLAC 文件的原始音频数据,然后可以使用阅读器将其传递给阅读fingerprint.RawInfo.Src器(我真的不喜欢go-flacGetStreamInfo不返回的事实io.Reader;而是返回[]byte,因此在实际发生进一步处理之前将整个流加载到内存中)。

使用以下代码,可以计算 FLAC 文件的指纹(基本上是做什么fpcalc的):

package main

import (
    "bytes"
    "fmt"
    "os"

    "github.com/go-fingerprint/fingerprint"
    "github.com/go-fingerprint/gochroma"
    "github.com/go-flac/go-flac"
)

func main() {
    f, err := flac.ParseFile(os.Args[1])
    if err != nil {
        panic(err)
    }

    si, err := f.GetStreamInfo()
    if err != nil {
        panic(err)
    }

    fpcalc := gochroma.New(gochroma.AlgorithmDefault)
    defer fpcalc.Close()

    fprint, err := fpcalc.Fingerprint(
        fingerprint.RawInfo{
            Src:        bytes.NewReader(f.Frames),
            Channels:   uint(si.ChannelCount),
            Rate:       uint(si.SampleRate),
            MaxSeconds: 120,
        },
    )

    fmt.Println(fprint)
}

不幸的是,上面的代码没有返回相同的指纹fpcalc。我究竟做错了什么?

标签: gocgoacoustid

解决方案


我最终得到了以下代码,它使用github.com/eaburns/flac将 FLAC 文件解码为原始音频数据(正如 Steven Penny 指出的那样),然后将数据传递给指纹/ gochroma

生成的指纹似乎与fpcalc同一 FLAC 文件报告的指纹不同,但使用生成的指纹查询 AcoustID 数据库时,结果是正确的。

package main

import (
    "bytes"
    "fmt"
    "log"
    "os"

    "github.com/eaburns/flac"
    "github.com/go-fingerprint/fingerprint"
    "github.com/go-fingerprint/gochroma"
)

func main() {
    if len(os.Args) != 2 {
        log.Fatalf("usage: go run fpcalc.go FILE")
    }

    f, err := os.Open(os.Args[1])
    if err != nil {
        log.Fatalf("os.Open(%s): %s", os.Args[1], err)
    }

    defer f.Close()

    d, metadata, err := flac.Decode(f)
    if err != nil {
        log.Fatalf("flac.Decode: %s", err)
    }

    fpcalc := gochroma.New(gochroma.AlgorithmDefault)
    defer fpcalc.Close()

    fprint, err := fpcalc.Fingerprint(
        fingerprint.RawInfo{
            Src:        bytes.NewBuffer(d),
            Channels:   uint(metadata.NChannels),
            Rate:       uint(metadata.SampleRate),
            MaxSeconds: 120,
        },
    )
    if err != nil {
        log.Fatalf("fpcalc.Fingerprint: %s", err)
    }

    fmt.Println(fprint)
}

推荐阅读