Compare commits

..

5 Commits

Author SHA1 Message Date
1a312e5e4a feat: add cjk text support 2024-10-21 01:30:26 +02:00
54e3cb5188 feat: add mpris 2024-10-21 00:44:44 +02:00
e8c1e1f952 feat: add transparency 2024-10-21 00:23:19 +02:00
b687d1622e feat: add transparency 2024-10-21 00:07:12 +02:00
a777d6da08 feat: add transparency 2024-10-20 23:45:40 +02:00
7 changed files with 244 additions and 31 deletions

View File

@ -2,6 +2,7 @@ package audio
import (
"fmt"
"os"
"xyosc/config"
"xyosc/utils"
@ -15,10 +16,8 @@ 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))
SampleSizeInBytes = uint32(malgo.SampleSizeInBytes(format))
}
func Start() {
@ -31,11 +30,31 @@ func Start() {
ctx.Free()
}()
deviceConfig := malgo.DefaultDeviceConfig(malgo.Duplex)
// Capture devices.
infos, err := ctx.Devices(malgo.Capture)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Println("Capture Devices")
for i, info := range infos {
e := "ok"
full, err := ctx.DeviceInfo(malgo.Capture, info.ID, malgo.Shared)
if err != nil {
e = err.Error()
}
fmt.Printf(" %d: %v, %s, [%s], formats: %+v\n",
i, info.ID, info.Name(), e, full.Formats)
}
deviceConfig := malgo.DefaultDeviceConfig(malgo.Capture)
deviceConfig.Capture.Format = format
deviceConfig.Capture.Channels = 2
deviceConfig.Capture.DeviceID = infos[config.Config.CaptureDeviceIndex].ID.Pointer()
deviceConfig.SampleRate = config.Config.SampleRate
deviceConfig.Alsa.NoMMap = 1
onRecvFrames := func(pSample2, pSample []byte, framecount uint32) {
SampleRingBuffer.Write(pSample)
}
@ -49,7 +68,6 @@ func Start() {
err = device.Start()
utils.CheckError(err)
for {
}
select {}
//device.Uninit()
}

View File

@ -13,34 +13,42 @@ import (
)
type ConfigS struct {
FPSCounter bool
TargetFPS int32
WindowWidth int32
WindowHeight int32
SampleRate uint32
RingBufferSize uint32
ReadBufferSize uint32
Gain float32
LineOpacity uint8
LineThickness float32
FPSCounter bool
ShowMPRIS bool
TargetFPS int32
WindowWidth int32
WindowHeight int32
WindowOpacity float32
CaptureDeviceIndex int
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,
FPSCounter: false,
ShowMPRIS: true,
TargetFPS: 60,
WindowWidth: 1080,
WindowHeight: 1080,
WindowOpacity: 0.8,
CaptureDeviceIndex: 0,
SampleRate: 48000,
RingBufferSize: 192000,
ReadBufferSize: 192000,
Gain: 1,
LineOpacity: 50,
LineThickness: 2,
}
var Config ConfigS
var AccentColor color.RGBA
var FirstColor color.RGBA
var ThirdColor color.RGBA
func Init() {
configPath := configdir.LocalConfig("ontake", "xyosc")
@ -74,6 +82,8 @@ func Init() {
walFile := filepath.Join(walPath, "colors")
if _, err = os.Stat(walFile); os.IsNotExist(err) {
AccentColor = color.RGBA{255, 0, 0, Config.LineOpacity}
FirstColor = color.RGBA{255, 120, 120, Config.LineOpacity}
ThirdColor = color.RGBA{255, 0, 0, Config.LineOpacity}
} else {
fh, err := os.Open(walFile)
utils.CheckError(err)
@ -82,17 +92,26 @@ func Init() {
var line int
var rgbaColor color.RGBA
for scanner.Scan() {
if line == 0 {
rgbaColor, err = ParseHexColor(scanner.Text())
utils.CheckError(err)
FirstColor = color.RGBA{rgbaColor.R, rgbaColor.G, rgbaColor.B, Config.LineOpacity}
}
if line == 1 {
rgbaColor, err = ParseHexColor(scanner.Text())
utils.CheckError(err)
AccentColor = color.RGBA{rgbaColor.R, rgbaColor.G, rgbaColor.B, Config.LineOpacity}
}
if line == 2 {
rgbaColor, err = ParseHexColor(scanner.Text())
utils.CheckError(err)
ThirdColor = color.RGBA{rgbaColor.R, rgbaColor.G, rgbaColor.B, Config.LineOpacity}
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) {

49
fonts/fonts.go Normal file
View File

@ -0,0 +1,49 @@
package fonts
import (
"unicode"
"xyosc/utils"
"github.com/flopp/go-findfont"
rl "github.com/gen2brain/raylib-go/raylib"
)
func getAllChars(table *unicode.RangeTable) []rune {
res := make([]rune, 0)
for _, r := range table.R16 {
for c := r.Lo; c <= r.Hi; c += r.Stride {
res = append(res, rune(c))
}
}
for _, r := range table.R32 {
for c := r.Lo; c <= r.Hi; c += r.Stride {
res = append(res, rune(c))
}
}
return res
}
var FontIosevka32 rl.Font
var FontIosevka16 rl.Font
func Init() {
// NOTE: Textures/Fonts MUST be loaded after Window initialization (OpenGL context is required)
fontPath, err := findfont.Find("SourceHanSansJP-Heavy.otf")
utils.CheckError(err)
runes := make([]rune, 0)
runes = append(runes, getAllChars(unicode.Katakana)...)
runes = append(runes, getAllChars(unicode.Hiragana)...)
runes = append(runes, getAllChars(unicode.Latin)...)
runes = append(runes, getAllChars(unicode.Digit)...)
runes = append(runes, getAllChars(unicode.Punct)...)
runes = append(runes, getAllChars(unicode.Han)...)
runes = append(runes, getAllChars(unicode.Symbol)...)
FontIosevka32 = rl.LoadFontEx(fontPath, 32, runes)
rl.GenTextureMipmaps(&FontIosevka32.Texture)
rl.SetTextureFilter(FontIosevka32.Texture, rl.FilterPoint)
FontIosevka16 = rl.LoadFontEx(fontPath, 16, runes)
rl.GenTextureMipmaps(&FontIosevka16.Texture)
rl.SetTextureFilter(FontIosevka16.Texture, rl.FilterPoint)
}

5
go.mod
View File

@ -3,15 +3,20 @@ module xyosc
go 1.23.2
require (
github.com/flopp/go-findfont v0.1.0
github.com/gen2brain/malgo v0.11.22
github.com/gen2brain/raylib-go/raylib v0.0.0-20241019150900-b7833eeae8d0
github.com/godbus/dbus v4.1.0+incompatible
github.com/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f
github.com/leberKleber/go-mpris v1.1.0
github.com/smallnest/ringbuffer v0.0.0-20240827114233-62e3c686e6c0
github.com/ztrue/tracerr v0.4.0
gopkg.in/yaml.v2 v2.4.0
)
require (
github.com/ebitengine/purego v0.7.1 // indirect
github.com/godbus/dbus/v5 v5.0.6 // indirect
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
golang.org/x/sys v0.20.0 // indirect
)

25
go.sum
View File

@ -1,18 +1,41 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/ebitengine/purego v0.7.1 h1:6/55d26lG3o9VCZX8lping+bZcmShseiqlh2bnUDiPA=
github.com/ebitengine/purego v0.7.1/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ=
github.com/flopp/go-findfont v0.1.0 h1:lPn0BymDUtJo+ZkV01VS3661HL6F4qFlkhcJN55u6mU=
github.com/flopp/go-findfont v0.1.0/go.mod h1:wKKxRDjD024Rh7VMwoU90i6ikQRCr+JTHB5n4Ejkqvw=
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/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4=
github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
github.com/godbus/dbus/v5 v5.0.6 h1:mkgN1ofwASrYnJ5W6U/BxG15eXXXjirgZc7CLqkcaro=
github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
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/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leberKleber/go-mpris v1.1.0 h1:bHAnmUjVoxAs4uMHH9lfQ8bOm284UWtI7JhLvkiF7O8=
github.com/leberKleber/go-mpris v1.1.0/go.mod h1:OwKywFZwFGC0p/8xBUTUXMIFZy0Rq/7C6EayfeASTA0=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/smallnest/ringbuffer v0.0.0-20240827114233-62e3c686e6c0 h1:6wTlHUWggWb8Y5Q4f7xnIBHa3L7DgijNQP8eM6oTEhQ=
github.com/smallnest/ringbuffer v0.0.0-20240827114233-62e3c686e6c0/go.mod h1:tAG61zBM1DYRaGIPloumExGvScf08oHuo0kFoOqdbT0=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/ztrue/tracerr v0.4.0 h1:vT5PFxwIGs7rCg9ZgJ/y0NmOpJkPCPFK8x0vVIYzd04=
github.com/ztrue/tracerr v0.4.0/go.mod h1:PaFfYlas0DfmXNpo7Eay4MFhZUONqvXM+T2HyGPpngk=
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/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/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=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

13
main.go
View File

@ -4,6 +4,8 @@ import (
"encoding/binary"
"xyosc/audio"
"xyosc/config"
"xyosc/fonts"
"xyosc/media"
rl "github.com/gen2brain/raylib-go/raylib"
)
@ -12,12 +14,14 @@ func main() {
config.Init()
audio.Init()
go audio.Start()
go media.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.SetWindowOpacity(config.Config.WindowOpacity)
rl.SetConfigFlags(rl.FlagWindowTransparent)
rl.SetConfigFlags(rl.FlagMsaa4xHint)
rl.SetWindowState(rl.FlagWindowUndecorated)
rl.SetTargetFPS(config.Config.TargetFPS)
rl.SetWindowPosition(rl.GetMonitorWidth(rl.GetCurrentMonitor())/2, rl.GetMonitorHeight(rl.GetCurrentMonitor())/2)
@ -25,6 +29,8 @@ func main() {
var AY float32
var BX float32
var BY float32
fonts.Init()
for !rl.WindowShouldClose() {
rl.BeginDrawing()
rl.ClearBackground(rl.Blank)
@ -43,8 +49,11 @@ func main() {
AY = BY
}
if config.Config.FPSCounter {
rl.DrawFPS(0, 0)
rl.DrawFPS(16, config.Config.WindowHeight)
}
rl.DrawTextEx(fonts.FontIosevka32, media.PlayingMediaInfo.Artist+" - "+media.PlayingMediaInfo.Title, rl.NewVector2(16, 16), 32, 2, config.AccentColor)
rl.DrawTextEx(fonts.FontIosevka16, media.PlayingMediaInfo.Album, rl.NewVector2(16, 48), 16, 1, config.ThirdColor)
rl.DrawTextEx(fonts.FontIosevka32, media.FmtDuration(media.PlayingMediaInfo.Position)+" / "+media.FmtDuration(media.PlayingMediaInfo.Duration), rl.NewVector2(16, 64), 32, 2, config.AccentColor)
rl.EndDrawing()
}

90
media/media.go Normal file
View File

@ -0,0 +1,90 @@
package media
import (
"fmt"
"math"
"runtime"
"strings"
"time"
"xyosc/utils"
"github.com/godbus/dbus"
"github.com/leberKleber/go-mpris"
"github.com/ztrue/tracerr"
)
func FmtDuration(s float64) string {
mf := int(math.Floor(s / 60))
sf := int((s/60 - math.Floor(s/60)) * 60)
return fmt.Sprintf("%02d:%02d", mf, sf)
}
type CurrentPlayingMediaInfo struct {
Title string
Album string
Artist string
Position float64
Duration float64
}
var PlayingMediaInfo CurrentPlayingMediaInfo
func Start() {
for {
PlayingMediaInfo = GetCurrentPlayingMediaInfo()
time.Sleep(time.Second * 1)
}
}
func ListPlayers() []string {
conn, err := dbus.SessionBus()
utils.CheckError(tracerr.Wrap(err))
var names []string
err = conn.BusObject().Call("org.freedesktop.DBus.ListNames", 0).Store(&names)
utils.CheckError(tracerr.Wrap(err))
var mprisNames []string
for _, name := range names {
if strings.HasPrefix(name, "org.mpris.MediaPlayer2") {
mprisNames = append(mprisNames, name)
}
}
return mprisNames
}
func GetCurrentPlayingMediaInfo() CurrentPlayingMediaInfo {
switch runtime.GOOS {
case "linux":
players := ListPlayers()
if len(players) == 0 {
return CurrentPlayingMediaInfo{"No media", "", "", 0, 0}
}
p, err := mpris.NewPlayer(players[0])
utils.CheckError(tracerr.Wrap(err))
mediaPositionMicroseconds, err := p.Position()
if err != nil {
mediaPositionMicroseconds = 0
}
mediaPosition := float64(mediaPositionMicroseconds) / 1000000
mediaMetadata, err := p.Metadata()
utils.CheckError(tracerr.Wrap(err))
mediaDurationMicroseconds, err := mediaMetadata.MPRISLength()
utils.CheckError(tracerr.Wrap(err))
mediaDuration := float64(mediaDurationMicroseconds) / 1000000
mediaTitle, err := mediaMetadata.XESAMTitle()
utils.CheckError(tracerr.Wrap(err))
mediaAlbum, err := mediaMetadata.XESAMAlbum()
utils.CheckError(tracerr.Wrap(err))
mediaArtists, err := mediaMetadata.XESAMArtist()
utils.CheckError(tracerr.Wrap(err))
mediaArtist := ""
if len(mediaArtists) != 0 {
mediaArtist = mediaArtists[0]
}
utils.CheckError(tracerr.Wrap(err))
return CurrentPlayingMediaInfo{mediaTitle, mediaAlbum, mediaArtist, mediaPosition, mediaDuration}
default:
return CurrentPlayingMediaInfo{"Platform not supported", "Sorry", "", 0, 0}
}
}