From 0eb8ade0e9c0eb878b4c39f5614c840433468277 Mon Sep 17 00:00:00 2001 From: Emmm Monster <58943012+emmmx@users.noreply.github.com> Date: Sat, 26 Dec 2020 04:38:23 +0800 Subject: [PATCH] Add XM Decoder --- algo/xm/xm.go | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 algo/xm/xm.go diff --git a/algo/xm/xm.go b/algo/xm/xm.go new file mode 100644 index 0000000..25efe16 --- /dev/null +++ b/algo/xm/xm.go @@ -0,0 +1,90 @@ +package xm + +import ( + "bytes" + "errors" + "github.com/umlock-music/cli/algo/common" + "github.com/umlock-music/cli/internal/logging" + "go.uber.org/zap" +) + +var ( + xmMagicHeader = []byte{'i', 'f', 'm', 't'} + xmMagicHeader2 = []byte{0xfe, 0xfe, 0xfe, 0xfe} + xmHeaders map[string]string + ErrXmFileSize = errors.New("xm invalid file size") + ErrXmMagicHeader = errors.New("xm magic header not matched") +) + +func init() { + xmHeaders = map[string]string{ + " WAV": "wav", + "FLAC": "flac", + " MP3": "mp3", + " A4M": "m4a", + } +} + +type Decoder struct { + data []byte + 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 +} + +func NewDecoder(data []byte) *Decoder { + return &Decoder{data: data} +} + +func (d *Decoder) Validate() error { + lenData := len(d.data) + if lenData < 16 { + return ErrXmFileSize + } + if !bytes.Equal(xmMagicHeader, d.data[:4]) || + !bytes.Equal(xmMagicHeader2, d.data[8:12]) { + return ErrXmMagicHeader + } + + var ok bool + d.outputExt, ok = xmHeaders[string(d.data[4:8])] + if !ok { + return errors.New("detect unknown xm file type: " + string(d.data[4:8])) + } + + if d.data[14] != 0 { + logging.Log().Warn("not a simple xm file", zap.Uint8("b[14]", d.data[14])) + } + d.headerLen = uint32(d.data[12]) | uint32(d.data[13])<<8 | uint32(d.data[14])<<16 // LittleEndian Unit24 + if d.headerLen+16 > uint32(lenData) { + return ErrXmFileSize + } + return nil +} + +func (d *Decoder) Decode() error { + d.mask = d.data[15] + d.audio = d.data[16:] + dataLen := uint32(len(d.audio)) + for i := d.headerLen; i < dataLen; i++ { + d.audio[i] = ^(d.audio[i] - d.mask) + } + return nil +}