From adfc70db3b67df93f2414812daf92afbcd49aa90 Mon Sep 17 00:00:00 2001 From: Louis Dalibard Date: Sun, 20 Oct 2024 22:58:09 +0200 Subject: [PATCH] init: init commit --- .gitignore | 1 + audio/audio.go | 55 +++++++++++++++++++++++ build.sh | 1 + config/config.go | 114 +++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 17 +++++++ go.sum | 18 ++++++++ main.go | 52 +++++++++++++++++++++ utils/utils.go | 9 ++++ 8 files changed, 267 insertions(+) create mode 100644 .gitignore create mode 100644 audio/audio.go create mode 100644 build.sh create mode 100644 config/config.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100644 utils/utils.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..027ed67 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +xyosc \ No newline at end of file diff --git a/audio/audio.go b/audio/audio.go new file mode 100644 index 0000000..fbee8d9 --- /dev/null +++ b/audio/audio.go @@ -0,0 +1,55 @@ +package audio + +import ( + "fmt" + "xyosc/config" + "xyosc/utils" + + "github.com/gen2brain/malgo" + "github.com/smallnest/ringbuffer" +) + +var SampleRingBuffer *ringbuffer.RingBuffer +var SampleSizeInBytes uint32 + +const format = malgo.FormatF32 + +func Init() { + deviceConfig := malgo.DefaultDeviceConfig(malgo.Duplex) + SampleRingBuffer = ringbuffer.New(int(config.Config.RingBufferSize)) + deviceConfig.Capture.Format = format + SampleSizeInBytes = uint32(malgo.SampleSizeInBytes(deviceConfig.Capture.Format)) +} + +func Start() { + ctx, err := malgo.InitContext(nil, malgo.ContextConfig{}, func(message string) { + fmt.Printf("LOG <%v>\n", message) + }) + utils.CheckError(err) + defer func() { + _ = ctx.Uninit() + ctx.Free() + }() + + deviceConfig := malgo.DefaultDeviceConfig(malgo.Duplex) + deviceConfig.Capture.Format = format + deviceConfig.Capture.Channels = 2 + deviceConfig.SampleRate = config.Config.SampleRate + deviceConfig.Alsa.NoMMap = 1 + onRecvFrames := func(pSample2, pSample []byte, framecount uint32) { + SampleRingBuffer.Write(pSample) + } + captureCallbacks := malgo.DeviceCallbacks{ + Data: onRecvFrames, + } + device, err := malgo.InitDevice(ctx.Context, deviceConfig, captureCallbacks) + + utils.CheckError(err) + + err = device.Start() + + utils.CheckError(err) + for { + } + //device.Uninit() +} diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..43cb700 --- /dev/null +++ b/build.sh @@ -0,0 +1 @@ +go mod tidy && go build -tags noaudio && ./xyosc diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..72dec39 --- /dev/null +++ b/config/config.go @@ -0,0 +1,114 @@ +package config + +import ( + "bufio" + "fmt" + "image/color" + "os" + "path/filepath" + "xyosc/utils" + + "github.com/kirsle/configdir" + "gopkg.in/yaml.v2" +) + +type ConfigS struct { + FPSCounter bool + TargetFPS int32 + WindowWidth int32 + WindowHeight int32 + SampleRate uint32 + RingBufferSize uint32 + ReadBufferSize uint32 + Gain float32 + LineOpacity uint8 + LineThickness float32 +} + +var DefaultConfig = ConfigS{ + FPSCounter: false, + TargetFPS: 60, + WindowWidth: 800, + WindowHeight: 800, + SampleRate: 192000, + RingBufferSize: 192000 * 4, + ReadBufferSize: 192000, + Gain: 1, + LineOpacity: 50, + LineThickness: 2, +} + +var Config ConfigS + +var AccentColor color.RGBA + +func Init() { + configPath := configdir.LocalConfig("ontake", "xyosc") + err := configdir.MakePath(configPath) // Ensure it exists. + utils.CheckError(err) + + configFile := filepath.Join(configPath, "config.yml") + + // Does the file not exist? + if _, err = os.Stat(configFile); os.IsNotExist(err) { + // Create the new config file. + fh, err := os.Create(configFile) + utils.CheckError(err) + defer fh.Close() + + encoder := yaml.NewEncoder(fh) + encoder.Encode(&DefaultConfig) + Config = DefaultConfig + } else { + // Load the existing file. + fh, err := os.Open(configFile) + utils.CheckError(err) + defer fh.Close() + + decoder := yaml.NewDecoder(fh) + decoder.Decode(&Config) + } + + // Get pywal accent color + walPath := configdir.LocalCache("wal") + walFile := filepath.Join(walPath, "colors") + if _, err = os.Stat(walFile); os.IsNotExist(err) { + AccentColor = color.RGBA{255, 0, 0, Config.LineOpacity} + } else { + fh, err := os.Open(walFile) + utils.CheckError(err) + defer fh.Close() + scanner := bufio.NewScanner(fh) + var line int + var rgbaColor color.RGBA + for scanner.Scan() { + if line == 1 { + rgbaColor, err = ParseHexColor(scanner.Text()) + utils.CheckError(err) + break + } + line++ + } + + AccentColor = color.RGBA{rgbaColor.R, rgbaColor.G, rgbaColor.B, Config.LineOpacity} + } + fmt.Println(AccentColor) +} + +func ParseHexColor(s string) (c color.RGBA, err error) { + c.A = 0xff + switch len(s) { + case 7: + _, err = fmt.Sscanf(s, "#%02x%02x%02x", &c.R, &c.G, &c.B) + case 4: + _, err = fmt.Sscanf(s, "#%1x%1x%1x", &c.R, &c.G, &c.B) + // Double the hex digits: + c.R *= 17 + c.G *= 17 + c.B *= 17 + default: + err = fmt.Errorf("invalid length, must be 7 or 4") + + } + return +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..5bcb02a --- /dev/null +++ b/go.mod @@ -0,0 +1,17 @@ +module xyosc + +go 1.23.2 + +require ( + github.com/gen2brain/malgo v0.11.22 + github.com/gen2brain/raylib-go/raylib v0.0.0-20241019150900-b7833eeae8d0 + github.com/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f + github.com/smallnest/ringbuffer v0.0.0-20240827114233-62e3c686e6c0 + gopkg.in/yaml.v2 v2.4.0 +) + +require ( + github.com/ebitengine/purego v0.7.1 // indirect + golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect + golang.org/x/sys v0.20.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..ac0c7a7 --- /dev/null +++ b/go.sum @@ -0,0 +1,18 @@ +github.com/ebitengine/purego v0.7.1 h1:6/55d26lG3o9VCZX8lping+bZcmShseiqlh2bnUDiPA= +github.com/ebitengine/purego v0.7.1/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ= +github.com/gen2brain/malgo v0.11.22 h1:fRtTbzVI9CDWnfEJGo/GxKxN7pXtCb0NsAeUVUjZk9U= +github.com/gen2brain/malgo v0.11.22/go.mod h1:f9TtuN7DVrXMiV/yIceMeWpvanyVzJQMlBecJFVMxww= +github.com/gen2brain/raylib-go/raylib v0.0.0-20241019150900-b7833eeae8d0 h1:ssHgKQvc9AIHFSlpkfHr89lJU8nzGBdpKoQJBTPkvnQ= +github.com/gen2brain/raylib-go/raylib v0.0.0-20241019150900-b7833eeae8d0/go.mod h1:BaY76bZk7nw1/kVOSQObPY1v1iwVE1KHAGMfvI6oK1Q= +github.com/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f h1:dKccXx7xA56UNqOcFIbuqFjAWPVtP688j5QMgmo6OHU= +github.com/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f/go.mod h1:4rEELDSfUAlBSyUjPG0JnaNGjf13JySHFeRdD/3dLP0= +github.com/smallnest/ringbuffer v0.0.0-20240827114233-62e3c686e6c0 h1:6wTlHUWggWb8Y5Q4f7xnIBHa3L7DgijNQP8eM6oTEhQ= +github.com/smallnest/ringbuffer v0.0.0-20240827114233-62e3c686e6c0/go.mod h1:tAG61zBM1DYRaGIPloumExGvScf08oHuo0kFoOqdbT0= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/main.go b/main.go new file mode 100644 index 0000000..da7fe8a --- /dev/null +++ b/main.go @@ -0,0 +1,52 @@ +package main + +import ( + "encoding/binary" + "xyosc/audio" + "xyosc/config" + + rl "github.com/gen2brain/raylib-go/raylib" +) + +func main() { + config.Init() + audio.Init() + go audio.Start() + + scale := min(config.Config.WindowWidth, config.Config.WindowHeight) / 2 + rl.InitWindow(config.Config.WindowWidth, config.Config.WindowHeight, "xyosc") + defer rl.CloseWindow() + rl.SetWindowOpacity(0.0) + rl.SetConfigFlags(rl.FlagWindowTransparent) + rl.SetWindowState(rl.FlagWindowUndecorated) + rl.SetTargetFPS(config.Config.TargetFPS) + rl.SetWindowPosition(rl.GetMonitorWidth(rl.GetCurrentMonitor())/2, rl.GetMonitorHeight(rl.GetCurrentMonitor())/2) + var AX float32 + var AY float32 + var BX float32 + var BY float32 + for !rl.WindowShouldClose() { + rl.BeginDrawing() + rl.ClearBackground(rl.Blank) + + binary.Read(audio.SampleRingBuffer, binary.NativeEndian, &AX) + binary.Read(audio.SampleRingBuffer, binary.NativeEndian, &AY) + for i := uint32(0); i < config.Config.ReadBufferSize/audio.SampleSizeInBytes/2; i++ { + binary.Read(audio.SampleRingBuffer, binary.NativeEndian, &BX) + binary.Read(audio.SampleRingBuffer, binary.NativeEndian, &BY) + fAX := float32(AX) * config.Config.Gain * float32(scale) + fAY := -float32(AY) * config.Config.Gain * float32(scale) + fBX := float32(BX) * config.Config.Gain * float32(scale) + fBY := -float32(BY) * config.Config.Gain * float32(scale) + rl.DrawLineEx(rl.NewVector2(float32(config.Config.WindowWidth/2)+fAX, float32(config.Config.WindowWidth/2)+fAY), rl.NewVector2(float32(config.Config.WindowWidth/2)+fBX, float32(config.Config.WindowWidth/2)+fBY), config.Config.LineThickness, config.AccentColor) + AX = BX + AY = BY + } + if config.Config.FPSCounter { + rl.DrawFPS(0, 0) + } + rl.EndDrawing() + } + + rl.CloseWindow() +} diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 0000000..62ea048 --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,9 @@ +package utils + +import "log" + +func CheckError(err error) { + if err != nil { + log.Fatal(err) + } +}