package main import ( "container/list" "flag" "fmt" "os" "time" "gitlab.com/gomidi/midi" "gitlab.com/gomidi/midi/mid" "gitlab.com/gomidi/midi/midimessage" "gitlab.com/gomidi/midi/smf" "go.terinstock.com/midifi/pkg/ttymididrv" ) var ( drv mid.Driver midiSerial string ) func init() { flag.StringVar(&midiSerial, "com", "", "The serial port of the MIDI synth") } func main() { flag.Parse() drv = ttymididrv.New(midiSerial, 38400) defer drv.Close() outs, err := drv.Outs() if err != nil { fmt.Printf("err: %v\n", err) os.Exit(-1) } out := outs[0] err = out.Open() if err != nil { fmt.Printf("err: %v\n", err) os.Exit(-1) } defer out.Close() if flag.NArg() == 0 { flag.Usage() os.Exit(-1) } f, err := os.Open(flag.Arg(0)) if err != nil { fmt.Println("unable to load SMF file") os.Exit(-2) } defer f.Close() fmt.Printf("file: %s\n", flag.Arg(0)) mf := mid.NewReader(mid.NoLogger()) mf.SMFHeader = func(header smf.Header) { fmt.Printf("file format: %s\n", header.Format.String()) fmt.Printf("number of tracks: %d\n", header.NumTracks) fmt.Printf("time format: %s\n", header.TimeFormat.String()) fmt.Println("===========") } events := &list.List{} mf.Msg.Each = func(pos *mid.Position, msg midi.Message) { addEvent(events, Event{track: pos.Track, abs: pos.AbsoluteTicks, msg: msg}) } mf.ReadSMF(f) w := mid.WriteTo(out) var lastDur time.Duration for event := events.Front(); event != nil; event = event.Next() { dur := *mf.TimeAt(event.Value.(Event).abs) if lastDur != dur { <-time.After(dur - lastDur) lastDur = dur } msg := event.Value.(Event).msg if midimessage.IsLive(msg) { fmt.Printf("%s\n", msg.String()) w.Write(msg) } } } type Event struct { track int16 abs uint64 msg midi.Message } func addEvent(l *list.List, evt Event) { if l.Front() == nil { l.PushBack(evt) return } for cur := l.Front(); cur != nil; cur = cur.Next() { peek := cur.Next() if peek == nil { l.InsertAfter(evt, cur) return } if peek.Value.(Event).abs >= evt.abs { l.InsertAfter(evt, cur) return } } }