问题描述
我想使用 Go 中的 chromaprint 库创建 FLAC 或 MP3 文件的声学指纹。我一直在使用以下两个 Go 库:
- gochroma,用于色度打印的 Go 绑定
- fingerprint
使用以下代码,可以创建“原始音频数据流”的指纹(其中 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 文件1}} 并将流传递给 os.Open
。有一些 examples,但这些适用于以 fingerprint.RawInfo.Src
结尾的文件。
如何在 Go 中将 FLAC(或辅助 MP3)文件/流转换为原始音频数据流?我的猜测是使用像 go-flac 这样的 Go FLAC 库,但我不确定从哪里开始。欢迎任何提示!
编辑
通过 go-flac 的 .raw
应该可以访问 FLAC 文件的原始音频数据,然后可以使用阅读器将其传递给 GetStreamInfo
(我真的不喜欢这样的事实go-flac 的 fingerprint.RawInfo.Src
不返回 GetStreamInfo
;而是返回 io.Reader
,因此在实际进行进一步处理之前,整个流已加载到内存中)。>
使用以下代码,可以计算 FLAC 文件的指纹(基本上就是 []byte
所做的):
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),},)
fmt.Println(fprint)
}
相同的指纹。我做错了什么?
解决方法
我最终得到了以下代码,它使用 github.com/eaburns/flac(正如 Steven Penny 指出的)将 FLAC 文件解码为原始音频数据,然后将数据传递给 fingerprint/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)
}