@@ 1,627 0,0 @@
-package player
-
-import (
- "bytes"
- "encoding/binary"
- "fmt"
- "io"
- "os"
- "strings"
-)
-
-type XmInfo struct {
- //x int
- IDtext [17]byte
- ModuleName [20]byte
- _ byte
- TrackerName [20]byte
- VersionNumber uint16
-}
-
-type XmHeader struct {
- HeaderSize uint32
- SongLength uint16
- RestartPosition uint16
- NumberOfChannels uint16
- NumberOfPatterns uint16
- NumberOfInstruments uint16
- Flags uint16
- DefaultTempo uint16
- DefaultBPM uint16
- //PatternOrderTable [256]byte // TODO
-}
-
-type XmPattern struct {
- PatternHeaderLength uint32
- PackingType byte // always 0
- NumberOfRows uint16 // 1..256
- PackedPatterndataSize uint16
-}
-
-type XmNote struct {
- // (C-0 = 0 (xm docs; real value is C-0 = 1))
- Note byte // 0-71
- Instrument byte // 0-128
- VolumeColumnByte byte
- EffectType byte
- EffectParameter byte
-}
-
-type XmInstrument struct {
- InstrumentSize uint32
- InstrumentName [22]byte
- InstrumentType byte // always 0
- NumberOfSamples uint16
-}
-
-type XmSample struct {
- SampleLength uint32
- SampleLoopStart uint32
- SampleLoopLength uint32
- Volume byte
- Finetune byte // -16..15
- Type byte // Bit
- Panning byte // 0-255
- RelativeNoteNumber byte // signed byte
- _ byte // reserved
- SampleName [22]byte
-}
-
-type XmImporter struct {
- info XmInfo
- header XmHeader
- patterns []XmPattern
- patternData [][][]XmNote // use *xmnote?
- instruments []XmInstrument
- samples [][]XmSample // inst -> sample
- //sampleData [][]int16 // raw
- sampleDataConv [][][][2]float64 // inst -> sample -> data
-}
-
-func NewXmImporter() *XmImporter {
- xi := &XmImporter{
- //samples: make([][]xmsample, 0),
- }
- //xi.info.x = 1
- return xi
-}
-
-func (xi *XmImporter) Load(path string) *Module {
- _log.Dbg("%F; path:", path)
- f, err := os.Open(path)
- if err != nil {
- p("can't open file: path:", path)
- panic(err)
- }
- //defer f.Close()
-
- xi.Parse(f)
-
- f.Close()
-
- xmId := string(xi.info.IDtext[:])
- p("xmId:", xmId)
-
- xmName := string(xi.info.ModuleName[:])
- p("xmName:", xmName)
-
- xmVersionNumberMajor := (int(xi.info.VersionNumber) & 0xff00) >> 8 // high
- xmVersionNumberMinor := int(xi.info.VersionNumber) & 0xff // low
-
- p("xmVersionNumberMajor", xmVersionNumberMajor)
- p("xmVersionNumberMinor", xmVersionNumberMinor)
-
- if !(xmVersionNumberMajor == 1 && xmVersionNumberMinor == 4) {
- pp("not supported version?")
- }
- //pp(2)
-
- var m *Module
- if true {
- title := xmName
- bpm := int(xi.header.DefaultBPM)
- ntracks := int(xi.header.NumberOfChannels)
- if ntracks < 1 {
- pp("error")
- }
-
- m = NewModule()
- m.Info.Title = title
- m.Bpm = bpm
- m.Ntracks = ntracks
-
- m.SetNumTracks(ntracks)
-
- numPatterns := int(xi.header.NumberOfPatterns)
- if numPatterns < 1 {
- pp("error")
- }
-
- for i := 0; i < numPatterns; i++ {
- numRows := int(xi.patterns[i].NumberOfRows)
- pt := m.AddNewPattern(numRows)
- dx, dy := pt.Dim()
- p("dim:", dx, dy)
-
- // Fill pattern
- for row := 0; row < pt.Nrows; row++ {
- for tr := 0; tr < pt.Ntracks; tr++ {
- p("row:", row, "tr:", tr)
-
- xnote := xi.patternData[i][row][tr]
- if xnote.Note == 0 {
- // No note, skipping
- continue
- }
-
- c := pt.GetCellAt(tr, row)
-
- noteNum := int(xnote.Note) - 1
- note := GetNoteByNum(noteNum)
-
- c.Note = note
-
- instrument := int(xnote.Instrument)
- c.InstrumentNum = &instrument
-
- volCol := int(xnote.VolumeColumnByte)
-
- if volCol != 0 {
- // Scale volume to 0..80h range
- volume := (volCol - 0x10) * 2
-
- c.Vol = &volume
- }
- }
- }
- }
-
- // Load instruments
- numInstruments := int(xi.header.NumberOfInstruments)
- for i := 0; i < numInstruments; i++ {
- p("load instrument i:", i)
- inst := m.GetInstrument(i)
-
- numSamples := int(xi.instruments[i].NumberOfSamples)
-
- for j := 0; j < numSamples; j++ {
- xmsample := xi.samples[i][j]
- sd := xi.sampleDataConv[i][j]
-
- //smp := inst.loadSample(j, "/tmp/s1.raw")
- smp := inst.LoadSampleFromData(j, sd)
- smp.Num = j
- smp.Name = string(xmsample.SampleName[:])
-
- // Read Type Bit
- loop := xmsample.Type & 0x3
- if loop == 0 {
-
- } else if loop == 1 {
- smp.LoopType = LoopType_Forward
- } else if loop == 2 {
- smp.LoopType = LoopType_PingPong
- } else {
- pp("error")
- }
-
- //smp.loopType = LoopType_Forward
- }
- }
-
- m.AddNewOrder(0)
- //m.addNewOrder(1)
-
- }
-
- _log.Dbg("done %F")
-
- return m
-}
-
-func (xi *XmImporter) Parse(f *os.File) {
- _log.Dbg("%F")
-
- // Info
-
- //info := xminfo{}
- info := &xi.info
- data := readNextBytes(f, 60)
-
- p("data:")
- p(data)
- p(len(data))
-
- buffer := bytes.NewBuffer(data)
- err := binary.Read(buffer, binary.LittleEndian, info)
- if err != nil {
- pp("binary.Read failed", err)
- }
-
- fmt.Printf("parsed data:\n%+v\n", info)
-
- // Header
-
- p("parsing header...")
- data = readNextBytes(f, 20) //+256)
-
- p("data:")
- p(data)
- p(len(data))
-
- buffer = bytes.NewBuffer(data)
- err = binary.Read(buffer, binary.LittleEndian, &xi.header)
- if err != nil {
- pp("binary.Read failed", err)
- }
-
- fmt.Printf("parsed data:\n%+v\n", xi.header)
-
- numInst := int(xi.header.NumberOfInstruments)
- xi.instruments = make([]XmInstrument, 0)
- //pp(numInst, xi.instruments)
- xi.samples = make([][]XmSample, 0)
- xi.sampleDataConv = make([][][][2]float64, 0)
-
- // Skip pattern order
- nominalHeaderSize := 20
- diff := int(xi.header.HeaderSize) - nominalHeaderSize
- //pp(diff)
- p("skip pattern order...")
- _ = readNextBytes(f, diff)
-
- // Patterns
-
- numCh := int(xi.header.NumberOfChannels)
- numPt := int(xi.header.NumberOfPatterns)
- // Init PatternData
- xi.patternData = make([][][]XmNote, numPt)
-
- p("parsing patterns...")
- for i := 0; i < numPt; i++ {
-
- p("read pattern n:", i)
- data = readNextBytes(f, 9)
- p("data:", data)
- buffer = bytes.NewBuffer(data)
- pt := XmPattern{}
- err = binary.Read(buffer, binary.LittleEndian, &pt)
- if err != nil {
- pp("binary.Read failed", err)
- }
- fmt.Printf("parsed data:\n%+v\n", pt)
- xi.patterns = append(xi.patterns, pt)
-
- pdSize := int(pt.PackedPatterndataSize)
- _ = pdSize
- //pd := readNextBytes(f, pdSize)
- //_ = pd
-
- // Decode patterndata
-
- //pd := &xi.patternData[i]
- numRows := int(xi.patterns[i].NumberOfRows)
- pd := make([][]XmNote, numRows)
- for i := range pd {
- pd[i] = make([]XmNote, numCh)
- }
-
- // Set pd
- //xi.patternData[i] = pd
- //pp(pd[0][0])
-
- //for i := 0; i < pdSize; i++ {
- for row := 0; row < numRows; row++ {
- for ch := 0; ch < numCh; ch++ {
- p("read note...")
-
- xnote := XmNote{}
-
- //data = pd[i : 5]
- var note byte
- //if err := binary.Read(f, binary.LittleEndian, ¬e); err != nil {
- // panic(err)
- //}
- readBin(f, 1, ¬e, "note")
- //pp(note)
- offset := 1
-
- if note&0x80 != 0 {
- println("packed")
- // Packed
- if note&0x01 != 0 {
- readBin(f, 1, &xnote.Note, "p_note")
- offset++
- }
- if note&0x02 != 0 {
- readBin(f, 1, &xnote.Instrument, "p_instrument")
- offset++
- }
- if note&0x04 != 0 {
- readBin(f, 1, &xnote.VolumeColumnByte, "p_volumecolumntbyte")
- offset++
- }
- if note&0x08 != 0 {
- readBin(f, 1, &xnote.EffectType, "p_effecttype")
- offset++
- }
- if note&0x10 != 0 {
- readBin(f, 1, &xnote.EffectParameter, "p_effectparameter")
- offset++
- }
- } else {
- // Unpacked
- println("unpacked")
- xnote.Note = note
- readBin(f, 1, &xnote.Instrument, "u_instrument")
- readBin(f, 1, &xnote.VolumeColumnByte, "u_volumecolumnbyte")
- readBin(f, 1, &xnote.EffectType, "u_effecttype")
- readBin(f, 1, &xnote.EffectParameter, "u_effectparameter")
- offset += 4
- // pp(3)
- }
-
- pd[row][ch] = xnote
-
- p("row:", row, "ch:", ch)
- p("offset:", offset)
- pv(xnote)
- //pp(2)
- }
- }
-
- // Set
- xi.patternData[i] = pd
- //pdump(pd)
- //pp(3)
- }
-
- // Instruments
-
- println("parsing instruments...")
- p("numInst:", numInst)
- for i := 0; i < numInst; i++ {
- inst := XmInstrument{}
- readBin(f, 29, &inst, "instrument")
- pv(inst)
- instName := string(inst.InstrumentName[:])
- p("instName:", instName)
-
- xi.instruments = append(xi.instruments, inst)
- //xi.instruments[i] = inst
-
- //pp(2)
- numSamples := int(inst.NumberOfSamples)
-
- if numSamples == 0 {
- println("no samples, continue")
- continue
- }
-
- //xi.samples[i] = make([]xmsample, numSamples)
- //xi.samples[i] = make([]xmsample, 0)
- slot := make([]XmSample, 0)
- xi.samples = append(xi.samples, slot)
-
- /*
- var sampleHeaderSize uint32
- readBin(f, 4, &sampleHeaderSize, "sampleHeaderSize")
- p(sampleHeaderSize)
- pp(3)
- */
-
- // Skip the second half on the instrument
- //n := 214
- n := 263 - 29 // 263 = inst.InstrumentSize
- _ = readNextBytes(f, n)
-
- // Read samples
-
- for j := 0; j < numSamples; j++ {
- println("parsing samples...")
- smp := XmSample{}
- readBin(f, 40, &smp, "sample")
- pv(smp)
-
- //xi.samples[i][j] = smp
- xi.samples[i] = append(xi.samples[i], smp)
-
- smpName := string(smp.SampleName[:])
- p("smpName:", smpName)
- //pp(2)
-
- // Read Type Bit
- dWidth := 1
- if smp.Type&(1<<4) != 0 {
- dWidth = 2 // 16bit
- }
- p("dWidth:", dWidth)
- //pp(2)
-
- // Read sample data
- println("parsing sample data...")
- smpLength := int(smp.SampleLength)
- p("smpLength:", smpLength)
-
- sdSize := smpLength
- sdSizeSamples := smpLength / dWidth
- p("sdSize:", sdSize)
- p("sdSizeSamples:", sdSizeSamples)
- //sd := readNextBytes(f, sdSize)
- //pp(2)
-
- if dWidth == 1 {
- //pp(8)
- }
-
- var _ = `
-
- // Decode sample data
- sampleOrig8 := make([]byte, sdSizeSamples) // original 8bit data
- sample := make([]int16, sdSizeSamples)
- old := int16(0)
- for k := 0; k < sdSizeSamples; k++ {
- var rawS int16
- if dWidth == 2 {
- readBin(f, 2, &rawS, "rawS")
- } else {
- // Upscale to 16bit
- var tmpRawS byte
- readBin(f, 1, &tmpRawS, "tmpRawS")
- rawS = int16(tmpRawS)
- sampleOrig8[k] = tmpRawS
- }
- //s := int(rawS)
- s := rawS
- new_ := s + old
- sample[k] = new_
- old = new_
- //pp(5)
- }
- `
- _ = `
- // Decode sample data
- sampleOrig8 := make([]int8, sdSizeSamples) // original 8bit data
- sample := make([]int8, sdSizeSamples)
- old := int8(0)
- for k := 0; k < sdSizeSamples; k++ {
- var rawS int8
- readBin(f, 1, &rawS, "rawS")
- //s := int(rawS)
- s := rawS
- new_ := s + old
- sample[k] = new_
- old = new_
- //pp(5)
- }
- `
-
- // Decode sample data
- sampleOrig8 := make([]int8, sdSizeSamples) // original 8bit data
- sample := make([]int16, sdSizeSamples)
- old := int16(0)
- old8 := int8(0)
- for k := 0; k < sdSizeSamples; k++ {
- if dWidth == 2 {
- var rawS int16
- readBin(f, 2, &rawS, "rawS")
- s := rawS
- new_ := s + old
- sample[k] = new_
- old = new_
- } else {
- var tmpRawS int8
- readBin(f, 1, &tmpRawS, "tmpRawS")
- //sampleOrig8[k] = tmpRawS // XXX packed data
- s := tmpRawS
- new_ := s + old8
- sample[k] = int16(new_) << 8 // to 16bit value
- sampleOrig8[k] = new_ // unpacked
- old8 = new_
- }
-
- //pp(5)
- }
-
- //ioutil.WriteFile("/tmp/s1.raw", sample, 0644)
- name := smpName
- name = strings.ReplaceAll(name, "\x00", "\x20")
- name = strings.ReplaceAll(name, "/", "_")
- pathPrefix := fmt.Sprintf("/tmp/smp_%d_%s", j, name)
- path := pathPrefix + ".raw"
- //fmt.Printf("path: %#v\n", path)
- //path = "/tmp/1.raw"
- //p("pathPrefix:", pathPrefix)
- p("path:", path)
- //pp(2)
-
- outf, err := os.Create(path)
- if err != nil {
- panic(err)
- }
- err = binary.Write(outf, binary.LittleEndian, sample)
- if err != nil {
- panic(err)
- }
- outf.Close()
- // Save orig 8bit data
- if dWidth == 1 {
- outf, err := os.Create(pathPrefix + "_8bit_orig.raw")
- if err != nil {
- panic(err)
- }
- err = binary.Write(outf, binary.LittleEndian, sampleOrig8)
- if err != nil {
- panic(err)
- }
- outf.Close()
- }
-
- sampleConv := make([][2]float64, sdSizeSamples)
- for i, _ := range sampleConv {
- //if dWidth == 2 {
- // Mono to stereo
- vf := I16tof(int(sample[i]))
- sampleConv[i][0] = vf
- sampleConv[i][1] = vf
- //} else {
- // pp("8bit samples not supported")
- //}
- }
- //pp(4)
-
- //xi.sampleDataConv[i] = make([][][2]float64, 0)
- slot := make([][][2]float64, 0)
- xi.sampleDataConv = append(xi.sampleDataConv, slot)
- xi.sampleDataConv[i] = append(xi.sampleDataConv[i], sampleConv)
- //pdump(xi.sampleDataConv[i])
- //xi.sampleDataConv[i][j] = sampleConv
- }
- }
-
- _log.Dbg("done %F")
-
-}
-
-func readBin(file *os.File, number int, data interface{}, name string) {
- println("readBin")
- if name != "" {
- println("name:", name)
- }
- readdata := readNextBytes(file, number)
- p("readdata:", readdata)
- buffer := bytes.NewBuffer(readdata)
- if err := binary.Read(buffer, binary.LittleEndian, data); err != nil {
- panic(err)
- }
-}
-
-func readBin2(r io.Reader, data interface{}) {
- if err := binary.Read(r, binary.LittleEndian, data); err != nil {
- panic(err)
- }
-}
-
-func readNextBytes(file *os.File, number int) []byte {
- bytes := make([]byte, number)
-
- getOffset := func() int64 {
- offset, err := file.Seek(0, os.SEEK_CUR)
- if err != nil {
- panic(err)
- }
- // Seek back?
- file.Seek(offset, os.SEEK_SET)
- return offset
- }
-
- off1 := getOffset()
- fmt.Printf("read bytes from: %d (0x%x) number: %d until: %d (0x%x)\n",
- off1, off1, number, int(off1)+number, int(off1)+number)
- _, err := file.Read(bytes)
- if err != nil {
- panic(err)
- }
-
- return bytes
-}