This commit is contained in:
TonyChyi 2018-03-19 10:25:16 +08:00
parent 3ab177a7fe
commit a8b3540a37
12 changed files with 363 additions and 1 deletions

21
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,21 @@
{
// 使 IntelliSense
//
// 访: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch",
"type": "go",
"request": "launch",
"mode": "debug",
"remotePath": "",
"port": 2345,
"host": "127.0.0.1",
"program": "${fileDirname}",
"env": {},
"args": [],
"showLog": true
}
]
}

View File

@ -1,7 +1,9 @@
Arremi
=====
Arremi is a simple router from serial to MIDI
![Screen Shot](ScreenShot.png)
Arremi is a simple driver/router from serial to MIDI
Now only for OSX

BIN
ScreenShot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

55
backend/backend.go Normal file
View File

@ -0,0 +1,55 @@
package backend
import (
"io"
"github.com/jacobsa/go-serial/serial"
"github.com/tonychee7000/Arremi/consts"
)
var (
// MidiDev is a global midi device.
MidiDev *MIDIDevice
// MIDIError check this. if not nil, go exit
MIDIError error
)
func init() {
MidiDev, MIDIError = NewMIDIDevice()
}
// Run called by frontend.
func Run(chSerialName chan string, ch chan int, errCh chan error) {
serialName := <-chSerialName
sPort, err := serial.Open(serial.OpenOptions{
PortName: "/dev/" + serialName,
BaudRate: consts.SerialBaudrate,
DataBits: 8,
StopBits: 1,
ParityMode: serial.PARITY_NONE,
MinimumReadSize: 3,
})
if err != nil {
errCh <- err
return
}
defer MidiDev.AllNoteOff()
defer sPort.Close()
go func() {
_, err := io.Copy(MidiDev, sPort)
if err != nil {
errCh <- err
}
ch <- 1
}()
for {
select {
case <-ch:
return
}
}
}

7
backend/backend_test.go Normal file
View File

@ -0,0 +1,7 @@
package backend
import "testing"
func TestBackendRun(t *testing.T) {
}

50
backend/midiDevice.go Normal file
View File

@ -0,0 +1,50 @@
package backend
import (
"github.com/tonychee7000/Arremi/consts"
midi "github.com/youpy/go-coremidi"
)
// MIDIDevice implies a Writer interface.
type MIDIDevice struct {
client midi.Client
source midi.Source
Signal chan midi.Packet
}
// NewMIDIDevice construction func
func NewMIDIDevice() (*MIDIDevice, error) {
var mididev = new(MIDIDevice)
err := mididev.Init()
return mididev, err
}
// Init the client and source
func (midiDev *MIDIDevice) Init() error {
var err error
midiDev.Signal = make(chan midi.Packet, 4096)
midiDev.client, err = midi.NewClient(consts.ClientName)
if err != nil {
return err
}
midiDev.source, err = midi.NewSource(midiDev.client, consts.SourceName)
return err
}
func (midiDev *MIDIDevice) Write(p []byte) (int, error) {
var pack = midi.NewPacket(p, 0)
midiDev.Signal <- pack
err := pack.Received(&(midiDev.source))
return len(p), err
}
// AllNoteOff I don't want panic!
func (midiDev *MIDIDevice) AllNoteOff() {
for i := 0; i < 16; i++ {
for j := 0; j < 128; j++ {
midiDev.Write([]byte{byte(0x90 + i), byte(j), 0})
}
}
}

15
consts/consts.go Normal file
View File

@ -0,0 +1,15 @@
package consts
const (
// WindowTitle for display
WindowTitle = "Arremi"
// ClientName for make a client
ClientName = "Arremi"
// SourceName for register Source
SourceName = "Arremi"
MIDISignalOn = "⚪️"
MIDISignalOff = "⚫️"
SerialBaudrate = 31250
)

BIN
debug Executable file

Binary file not shown.

154
frontend/window.go Normal file
View File

@ -0,0 +1,154 @@
package frontend
import (
"fmt"
"time"
"regexp"
"github.com/andlabs/ui"
"github.com/tonychee7000/Arremi/consts"
"github.com/tonychee7000/Arremi/serialPort"
"github.com/tonychee7000/arremi/backend"
)
// WindowMain is to show the UI
func WindowMain() {
// Let backend stop
//var ch = make(chan int)
var errCh = make(chan error, 0)
var chCon = make(chan int, 1)
var chSerialName = make(chan string, 1)
var serialList []string
window := ui.NewWindow(consts.WindowTitle, 300, 100, false)
if backend.MIDIError != nil {
ui.MsgBox(window, "MIDI Failure",
fmt.Sprint("MIDI driver initialize failed: ", backend.MIDIError))
ui.Quit()
}
labelSerialSelection := ui.NewLabel("Serial Port")
serialSelection := ui.NewCombobox()
buttonRefresh := ui.NewButton("Refresh")
buttonRun := ui.NewCheckbox("Run")
labelMidiActive := ui.NewLabel("MIDI Active ")
labelMidiSignal := ui.NewLabel(consts.MIDISignalOff)
labelHint := ui.NewLabel(
fmt.Sprint(
"HINT:\nBaudrate of Arduino USB serial port should be set to ",
consts.SerialBaudrate, " by using\n\n\tSerial.begin(", consts.SerialBaudrate, ")",
),
)
serialList, err := loadSerial(serialSelection, buttonRun)
if err != nil {
ui.MsgBox(window, "Serial Failure",
fmt.Sprint("Cannot list serial ports for reason: ", err))
ui.Quit()
}
buttonRefresh.OnClicked(func(*ui.Button) {
serialList, err = loadSerial(serialSelection, buttonRun)
if err != nil {
ui.MsgBox(window, "Serial Failure",
fmt.Sprint("Cannot list serial ports for reason: ", err))
ui.Quit()
}
})
buttonRun.OnToggled(func(*ui.Checkbox) {
if buttonRun.Checked() {
serialSelection.Disable()
buttonRefresh.Disable()
chSerialName <- serialList[serialSelection.Selected()]
} else {
serialSelection.Enable()
buttonRefresh.Enable()
chCon <- 1
}
})
mainBox := ui.NewVerticalBox()
topBox := ui.NewHorizontalBox()
buttomBox := ui.NewHorizontalBox()
topBox.Append(serialSelection, true)
topBox.Append(buttonRefresh, false)
topBox.Append(ui.NewLabel(" "), false)
topBox.Append(buttonRun, false)
buttomBox.Append(labelSerialSelection, false)
buttomBox.Append(ui.NewHorizontalSeparator(), true)
buttomBox.Append(labelMidiActive, false)
buttomBox.Append(labelMidiSignal, false)
mainBox.Append(ui.NewHorizontalBox(), true)
mainBox.Append(buttomBox, false)
mainBox.Append(topBox, false)
mainBox.Append(labelHint, true)
mainBox.Append(ui.NewHorizontalBox(), true)
window.SetMargined(true)
window.SetChild(mainBox)
window.OnClosing(func(*ui.Window) bool {
if buttonRun.Checked() {
return false
}
ui.Quit()
return true
})
go func() {
for {
select {
case err := <-errCh:
ui.QueueMain(func() {
if err != nil &&
!regexp.MustCompile("file already closed").MatchString(err.Error()) {
ui.MsgBox(window, "Backend Failure", fmt.Sprint(err))
}
serialSelection.Enable()
buttonRefresh.Enable()
buttonRun.SetChecked(false)
labelMidiSignal.SetText(consts.MIDISignalOff)
})
case <-backend.MidiDev.Signal:
ui.QueueMain(func() {
labelMidiSignal.SetText(consts.MIDISignalOn)
})
case <-time.After(100 * time.Millisecond):
ui.QueueMain(func() {
labelMidiSignal.SetText(consts.MIDISignalOff)
})
}
}
}()
go func() {
for {
backend.Run(chSerialName, chCon, errCh)
}
}()
window.Show()
}
func loadSerial(serialSelection *ui.Combobox, buttonRun *ui.Checkbox) ([]string, error) {
serialList, err := serialPort.GetSerialPorts()
if len(serialList) == 0 {
buttonRun.Disable()
serialSelection.Append("-- No Arduino Found --")
serialSelection.Disable()
} else {
for _, seiral := range serialList {
serialSelection.Append(seiral)
}
}
serialSelection.SetSelected(0)
return serialList, err
}

18
main.go Normal file
View File

@ -0,0 +1,18 @@
package main
import (
"fmt"
"github.com/andlabs/ui"
"github.com/tonychee7000/Arremi/frontend"
)
func main() {
defer func() {
fmt.Println("Arremi Exit.")
}()
err := ui.Main(frontend.WindowMain)
if err != nil {
fmt.Print("Got error:", err, "\n")
}
}

23
serialPort/get_serial.go Normal file
View File

@ -0,0 +1,23 @@
package serialPort
import (
"io/ioutil"
"regexp"
)
// GetSerialPorts is to list all serial port for Arduino device.
func GetSerialPorts() ([]string, error) {
f, err := ioutil.ReadDir("/dev")
if err != nil {
return nil, err
}
var fileList []string
for _, file := range f {
if regexp.MustCompile("^ttyACM([0-9]+)|^cu.usbmodem").MatchString(file.Name()) {
fileList = append(fileList, file.Name())
}
}
return fileList, nil
}

View File

@ -0,0 +1,17 @@
package serialPort
import (
"testing"
)
func TestGetSerialPorts(T *testing.T) {
portList, err := GetSerialPorts()
if err != nil {
T.Error("ERROR: ", err, "\n")
}
T.Log("Show all serial ports")
for _, p := range portList {
T.Log("\t", p)
}
}