package main import ( "container/list" "flag" "fmt" "os" "strings" "time" "gitlab.com/gomidi/midi" "gitlab.com/gomidi/midi/midimessage" "gitlab.com/gomidi/midi/reader" "gitlab.com/gomidi/midi/smf" "gitlab.com/gomidi/midi/writer" "gitlab.com/gomidi/rtmididrv" "go.terinstock.com/midifi/pkg/ttymididrv" ) var ( drv midi.Driver rt string midiSerial string ) func init() { flag.StringVar(&midiSerial, "com", "", "The serial port of the MIDI synth") flag.StringVar(&rt, "rt", "", "Connect to client:port with rtmidi") } func main() { flag.Parse() var out midi.Out if rt != "" { rtdrv, err := rtmididrv.New() if err != nil { panic(err) } drv = rtdrv outs, err := rtdrv.Outs() if err != nil { fmt.Printf("err: %v\n", err) os.Exit(-1) } for _, _out := range outs { if strings.Contains(_out.String(), rt) { out = _out break } } } else { drv = ttymididrv.New(midiSerial, 38400) outs, err := drv.Outs() if err != nil { fmt.Printf("err: %v\n", err) os.Exit(-1) } out = outs[0] } defer drv.Close() 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)) events := &list.List{} mf := reader.New( reader.NoLogger(), reader.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("===========") }), reader.Each(func(pos *reader.Position, msg midi.Message) { addEvent(events, Event{track: pos.Track, abs: pos.AbsoluteTicks, msg: msg}) }), ) reader.ReadSMF(mf, f) w := writer.New(out) var lastDur time.Duration for event := events.Front(); event != nil; event = event.Next() { dur := *reader.TimeAt(mf, 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 } } }