1
0
forked from um/cli
cli/algo/xm/xm.go

103 lines
2.2 KiB
Go
Raw Permalink Normal View History

2020-12-25 20:38:23 +00:00
package xm
import (
"bytes"
"errors"
2021-03-02 10:16:19 +00:00
"github.com/unlock-music/cli/algo/common"
"github.com/unlock-music/cli/internal/logging"
2020-12-25 20:38:23 +00:00
"go.uber.org/zap"
)
var (
2020-12-25 20:54:59 +00:00
magicHeader = []byte{'i', 'f', 'm', 't'}
magicHeader2 = []byte{0xfe, 0xfe, 0xfe, 0xfe}
typeMapping = map[string]string{
2020-12-25 20:38:23 +00:00
" WAV": "wav",
"FLAC": "flac",
" MP3": "mp3",
" A4M": "m4a",
}
2020-12-25 20:54:59 +00:00
ErrFileSize = errors.New("xm invalid file size")
ErrMagicHeader = errors.New("xm magic header not matched")
)
2020-12-25 20:38:23 +00:00
type Decoder struct {
2020-12-25 20:54:59 +00:00
file []byte
2020-12-25 20:38:23 +00:00
headerLen uint32
outputExt string
mask byte
audio []byte
}
func (d *Decoder) GetCoverImage() []byte {
return nil
}
func (d *Decoder) GetAudioData() []byte {
return d.audio
}
func (d *Decoder) GetAudioExt() string {
return d.outputExt
}
func (d *Decoder) GetMeta() common.Meta {
return nil
}
2020-12-26 07:47:10 +00:00
func NewDecoder(data []byte) common.Decoder {
2020-12-25 20:54:59 +00:00
return &Decoder{file: data}
2020-12-25 20:38:23 +00:00
}
func (d *Decoder) Validate() error {
2020-12-25 20:54:59 +00:00
lenData := len(d.file)
2020-12-25 20:38:23 +00:00
if lenData < 16 {
2020-12-25 20:54:59 +00:00
return ErrFileSize
2020-12-25 20:38:23 +00:00
}
2020-12-25 20:54:59 +00:00
if !bytes.Equal(magicHeader, d.file[:4]) ||
!bytes.Equal(magicHeader2, d.file[8:12]) {
return ErrMagicHeader
2020-12-25 20:38:23 +00:00
}
var ok bool
2020-12-25 20:54:59 +00:00
d.outputExt, ok = typeMapping[string(d.file[4:8])]
2020-12-25 20:38:23 +00:00
if !ok {
2020-12-25 20:54:59 +00:00
return errors.New("detect unknown xm file type: " + string(d.file[4:8]))
2020-12-25 20:38:23 +00:00
}
2020-12-25 20:54:59 +00:00
if d.file[14] != 0 {
logging.Log().Warn("not a simple xm file", zap.Uint8("b[14]", d.file[14]))
2020-12-25 20:38:23 +00:00
}
2020-12-25 20:54:59 +00:00
d.headerLen = uint32(d.file[12]) | uint32(d.file[13])<<8 | uint32(d.file[14])<<16 // LittleEndian Unit24
2020-12-25 20:38:23 +00:00
if d.headerLen+16 > uint32(lenData) {
2020-12-25 20:54:59 +00:00
return ErrFileSize
2020-12-25 20:38:23 +00:00
}
return nil
}
func (d *Decoder) Decode() error {
2020-12-25 20:54:59 +00:00
d.mask = d.file[15]
d.audio = d.file[16:]
2020-12-25 20:38:23 +00:00
dataLen := uint32(len(d.audio))
for i := d.headerLen; i < dataLen; i++ {
d.audio[i] = ^(d.audio[i] - d.mask)
}
return nil
}
func DecoderFuncWithExt(ext string) common.NewDecoderFunc {
return func(file []byte) common.Decoder {
return &Decoder{file: file, outputExt: ext}
}
}
2020-12-26 07:47:10 +00:00
func init() {
// Xiami Wav/M4a/Mp3/Flac
common.RegisterDecoder("xm", NewDecoder)
// Xiami Typed Format
common.RegisterDecoder("wav", DecoderFuncWithExt("wav"))
common.RegisterDecoder("mp3", DecoderFuncWithExt("mp3"))
common.RegisterDecoder("flac", DecoderFuncWithExt("flac"))
common.RegisterDecoder("m4a", DecoderFuncWithExt("m4a"))
2020-12-26 07:47:10 +00:00
}