go-mp3 #
一个纯go语言编写的mp3解码器,它没有引用任何外部模块。没有使用 cgo 来调用c代码,理论上效率会很高,但只是单核的解码器。
作者 #
Hajime Hoshi 日本人,它的其它作品:
- Ebitengine - A dead simple 2D game engine for Go
- Oto - A low-level library to play sound on multiple platforms
go.mod文件
module github.com/hajimehoshi/go-mp3
go 1.14
require github.com/hajimehoshi/oto/v2 v2.1.0
oto 是因为要做例子才引用的。
Decoder 过程 #
简单的理解 mp3文件由 一堆tag 和一堆 frame 构成,frame 本身也是由自己的 header + data 构成。
读取文件跳过前面的mp3文件tags部分,按frame抽取,使用 huffman 还原数据。
创建Decoder的过程,它在创建的时候尝试读取了一个 frame 大概是用来验证是否是合法的 mp3 文件。
// NewDecoder decodes the given io.Reader and returns a decoded stream.
//
// The stream is always formatted as 16bit (little endian) 2 channels
// even if the source is single channel MP3.
// Thus, a sample always consists of 4 bytes.
func NewDecoder(r io.Reader) (*Decoder, error) {
s := &source{
reader: r,
}
d := &Decoder{
source: s,
length: invalidLength,
}
if err := s.skipTags(); err != nil {
return nil, err
}
// TODO: Is readFrame here really needed?
if err := d.readFrame(); err != nil {
return nil, err
}
freq, err := d.frame.SamplingFrequency()
if err != nil {
return nil, err
}
d.sampleRate = freq
if err := d.ensureFrameStartsAndLength(); err != nil {
return nil, err
}
return d, nil
}
解码 frame #
解码 frame 的过程:
func (d *Decoder) readFrame() error {
var err error
d.frame, _, err = frame.Read(d.source, d.source.pos, d.frame)
if err != nil {
if err == io.EOF {
return io.EOF
}
if _, ok := err.(*consts.UnexpectedEOF); ok {
// TODO: Log here?
return io.EOF
}
return err
}
d.buf = append(d.buf, d.frame.Decode()...)
return nil
}
frame.Read 函数内部调用了 huffman 还原数据。