diff --git a/Pi/go.mod b/Pi/go.mod new file mode 100644 index 0000000..50c4cfb --- /dev/null +++ b/Pi/go.mod @@ -0,0 +1,17 @@ +module git.ctdo.de/interfisch/bananenkeyboard + +go 1.19 + +require ( + github.com/faiface/beep v1.1.0 + github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07 +) + +require ( + github.com/hajimehoshi/oto v0.7.1 // indirect + github.com/pkg/errors v0.9.1 // indirect + golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8 // indirect + golang.org/x/image v0.0.0-20190227222117-0694c2d4d067 // indirect + golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6 // indirect + golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756 // indirect +) diff --git a/Pi/go.sum b/Pi/go.sum new file mode 100644 index 0000000..bebe126 --- /dev/null +++ b/Pi/go.sum @@ -0,0 +1,39 @@ +github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/d4l3k/messagediff v1.2.2-0.20190829033028-7e0a312ae40b/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo= +github.com/faiface/beep v1.1.0 h1:A2gWP6xf5Rh7RG/p9/VAW2jRSDEGQm5sbOb38sf5d4c= +github.com/faiface/beep v1.1.0/go.mod h1:6I8p6kK2q4opL/eWb+kAkk38ehnTunWeToJB+s51sT4= +github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= +github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM= +github.com/go-audio/audio v1.0.0/go.mod h1:6uAu0+H2lHkwdGsAY+j2wHPNPpPoeg5AaEFh9FlA+Zs= +github.com/go-audio/riff v1.0.0/go.mod h1:l3cQwc85y79NQFCRB7TiPoNiaijp6q8Z0Uv38rVG498= +github.com/go-audio/wav v1.0.0/go.mod h1:3yoReyQOsiARkvPl3ERCi8JFjihzG6WhjYpZCf5zAWE= +github.com/hajimehoshi/go-mp3 v0.3.0/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM= +github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI= +github.com/hajimehoshi/oto v0.7.1 h1:I7maFPz5MBCwiutOrz++DLdbr4rTzBsbBuV2VpgU9kk= +github.com/hajimehoshi/oto v0.7.1/go.mod h1:wovJ8WWMfFKvP587mhHgot/MBr4DnNy9m6EepeVGnos= +github.com/icza/bitio v1.0.0/go.mod h1:0jGnlLAx8MKMr9VGnn/4YrvZiprkvBelsVIbA9Jjr9A= +github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6/go.mod h1:xQig96I1VNBDIWGCdTt54nHt6EeI639SmHycLYL7FkA= +github.com/jfreymuth/oggvorbis v1.0.1/go.mod h1:NqS+K+UXKje0FUYUPosyQ+XTVvjmVjps1aEZH1sumIk= +github.com/jfreymuth/vorbis v1.0.0/go.mod h1:8zy3lUAm9K/rJJk223RKy6vjCZTWC61NA2QD06bfOE0= +github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mewkiz/flac v1.0.7/go.mod h1:yU74UH277dBUpqxPouHSQIar3G1X/QIclVbFahSd1pU= +github.com/mewkiz/pkg v0.0.0-20190919212034-518ade7978e2/go.mod h1:3E2FUC/qYUfM8+r9zAwpeHJzqRVVMIYnpzD/clwWxyA= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07 h1:UyzmZLoiDWMRywV4DUYb9Fbt8uiOSooupjTq10vpvnU= +github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8 h1:idBdZTd9UioThJp8KpM/rTSinK/ChZFBE43/WtIy8zg= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/image v0.0.0-20190220214146-31aff87c08e9/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067 h1:KYGJGHOQy8oSi1fDlSpcZF0+juKwk/hEMv5SiwHogR0= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6 h1:vyLBGJPIl9ZYbcQFM2USFmJBK6KI+t+z6jL0lbwjrnc= +golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756 h1:9nuHUbU8dRnRRfj9KjWUVrJeoexdbeMjttk6Oh1rD10= +golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/Pi/main.go b/Pi/main.go new file mode 100644 index 0000000..73c66e7 --- /dev/null +++ b/Pi/main.go @@ -0,0 +1,165 @@ +package main + +import ( + "github.com/faiface/beep" + "github.com/faiface/beep/effects" + "github.com/faiface/beep/speaker" + "github.com/faiface/beep/wav" + "github.com/tarm/serial" + "log" + "math" + "os" + "sync" + "time" +) + +var sounds = []*beep.Buffer{ + mustLoadStream("./mini_grand/mini_grand-001.wav"), + mustLoadStream("./mini_grand/mini_grand-003.wav"), + mustLoadStream("./mini_grand/mini_grand-005.wav"), + mustLoadStream("./mini_grand/mini_grand-006.wav"), + mustLoadStream("./mini_grand/mini_grand-008.wav"), + mustLoadStream("./mini_grand/mini_grand-010.wav"), + mustLoadStream("./mini_grand/mini_grand-012.wav"), + mustLoadStream("./mini_grand/mini_grand-013.wav"), +} + +func mustLoadStream(p string) *beep.Buffer { + f, err := os.Open(p) + if err != nil { + log.Fatal(err) + } + defer f.Close() + + streamer, format, err := wav.Decode(f) + if err != nil { + log.Fatal(err) + } + defer streamer.Close() + + newFormat := format + newFormat.SampleRate = format.SampleRate / 4 + resample := beep.Resample(4, format.SampleRate, newFormat.SampleRate, streamer) + + buffer := beep.NewBuffer(newFormat) + buffer.Append(resample) + + return buffer +} + +type soundWorker struct { + buffer *beep.Buffer + ctrl *beep.Ctrl + volume *effects.Volume + isRunning bool + isStopping bool + forceEnd bool + mtx sync.Mutex +} + +func (w *soundWorker) End() { + w.isStopping = true + w.mtx.Lock() + defer w.mtx.Unlock() + + i := 1 + for w.isRunning { + if i >= 80 || w.forceEnd { + break + } + + w.volume.Volume = float64(1) / math.Pow(10, float64(i)) + i++ + } + w.isRunning = false + w.isStopping = false + w.forceEnd = false + w.ctrl.Paused = true +} + +func (w *soundWorker) Start() { + if w.isRunning { + w.forceEnd = true + } + + w.mtx.Lock() + defer w.mtx.Unlock() + + w.isRunning = true + w.ctrl.Streamer.(beep.StreamSeeker).Seek(0) + w.volume.Volume = 1 + w.ctrl.Paused = false +} + +func main() { + config := &serial.Config{ + Name: "/dev/ttyS0", + Baud: 115200, + Size: 8, + } + + stream, err := serial.OpenPort(config) + if err != nil { + log.Fatal(err) + } + + format := sounds[0].Format() + if err := speaker.Init(format.SampleRate, format.SampleRate.N(time.Second/100)); err != nil { + return + } + + var soundWorkers []*soundWorker + + mixer := beep.Mixer{} + for _, buffer := range sounds { + c := &beep.Ctrl{ + Streamer: buffer.Streamer(0, buffer.Len()), + Paused: true, + } + + volume := &effects.Volume{ + Streamer: c, + Base: 2, + Volume: 1, + } + + mixer.Add(volume) + + soundWorkers = append(soundWorkers, &soundWorker{ + buffer: buffer, + ctrl: c, + volume: volume, + }) + } + + speaker.Play(&mixer) + + c := make(chan byte) + + go func() { + for b := range c { + for i, w := range soundWorkers { + setStop := (b&(1<>i == 0 + + if w.isRunning && !w.isStopping && setStop { + w.End() + } + + if !w.isRunning && !setStop { + w.Start() + } + } + } + }() + + for { + buf := make([]byte, 1) + _, err := stream.Read(buf) + if err != nil { + log.Fatal(err) + } + + c <- buf[0] + } + +}