xyosc/main.go

169 lines
6.8 KiB
Go
Raw Normal View History

2024-10-20 22:58:09 +02:00
package main
import (
"encoding/binary"
2024-11-02 23:44:33 +01:00
"image"
"image/color"
2024-11-02 17:50:15 +01:00
"log"
2024-11-03 14:48:53 +01:00
"math/rand/v2"
2024-10-20 22:58:09 +02:00
"xyosc/audio"
"xyosc/config"
2024-11-02 23:44:33 +01:00
"xyosc/fastsqrt"
2024-10-21 01:30:26 +02:00
"xyosc/fonts"
2024-11-02 23:44:33 +01:00
"xyosc/icons"
2024-10-21 00:44:44 +02:00
"xyosc/media"
2024-11-03 14:48:53 +01:00
"xyosc/particles"
2024-10-20 22:58:09 +02:00
2024-11-02 17:50:15 +01:00
"fmt"
2024-11-03 14:48:53 +01:00
"github.com/chewxy/math32"
2024-11-02 17:50:15 +01:00
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
2025-01-16 21:25:11 +01:00
"github.com/hajimehoshi/ebiten/v2/inpututil"
2024-11-02 17:50:15 +01:00
"github.com/hajimehoshi/ebiten/v2/text/v2"
"github.com/hajimehoshi/ebiten/v2/vector"
2025-01-16 21:45:06 +01:00
2025-01-16 22:56:00 +01:00
"github.com/goccmack/godsp/peaks"
2024-10-20 22:58:09 +02:00
)
2024-11-02 17:50:15 +01:00
type Game struct {
}
func (g *Game) Update() error {
return nil
}
2024-10-20 22:58:09 +02:00
2024-11-02 17:50:15 +01:00
func (g *Game) Draw(screen *ebiten.Image) {
2024-10-20 22:58:09 +02:00
scale := min(config.Config.WindowWidth, config.Config.WindowHeight) / 2
var AX float32
var AY float32
var BX float32
var BY float32
2025-01-16 21:45:06 +01:00
var numSamples = config.Config.ReadBufferSize / audio.SampleSizeInBytes / 2
var FFTBuffer = make([]float64, numSamples)
2025-01-16 21:25:11 +01:00
if inpututil.IsKeyJustPressed(ebiten.KeyF) {
config.SingleChannel = !config.SingleChannel
}
2025-01-16 21:45:06 +01:00
if !config.SingleChannel {
2025-01-16 21:25:11 +01:00
binary.Read(audio.SampleRingBuffer, binary.NativeEndian, &AX)
binary.Read(audio.SampleRingBuffer, binary.NativeEndian, &AY)
S := float32(0)
2025-01-16 21:45:06 +01:00
for i := uint32(0); i < numSamples; i++ {
2025-01-16 21:25:11 +01:00
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)
if config.Config.LineInvSqrtOpacityControl {
inv := fastsqrt.FastInvSqrt32((fBX-fAX)*(fBX-fAX) + (fBY-fBY)*(fBY-fBY))
colorAdjusted := color.RGBA{config.ThirdColor.R, config.ThirdColor.G, config.ThirdColor.B, uint8(float32(config.Config.LineOpacity) * inv)}
vector.StrokeLine(screen, float32(config.Config.WindowWidth/2)+fAX, float32(config.Config.WindowHeight/2)+fAY, float32(config.Config.WindowWidth/2)+fBX, float32(config.Config.WindowHeight/2)+fBY, config.Config.LineThickness, colorAdjusted, true)
} else {
vector.StrokeLine(screen, float32(config.Config.WindowWidth/2)+fAX, float32(config.Config.WindowHeight/2)+fAY, float32(config.Config.WindowWidth/2)+fBX, float32(config.Config.WindowHeight/2)+fBY, config.Config.LineThickness, config.ThirdColorAdj, true)
}
S += float32(AX)*float32(AX) + float32(AY)*float32(AY)
if config.Config.Particles {
if rand.IntN(config.Config.ParticleGenPerFrameEveryXSamples) == 0 {
if len(particles.Particles) >= config.Config.ParticleMaxCount {
particles.Particles = particles.Particles[1:]
}
particles.Particles = append(particles.Particles, particles.Particle{
X: float32(AX) * config.Config.Gain,
Y: -float32(AY) * config.Config.Gain,
VX: 0,
VY: 0,
Size: rand.Float32()*(config.Config.ParticleMaxSize-config.Config.ParticleMinSize) + config.Config.ParticleMinSize,
})
2024-11-03 14:48:53 +01:00
}
}
2025-01-16 21:25:11 +01:00
AX = BX
AY = BY
}
2024-11-03 14:48:53 +01:00
2025-01-16 21:25:11 +01:00
for i, particle := range particles.Particles {
vector.DrawFilledCircle(screen, float32(config.Config.WindowWidth/2)+particle.X*float32(scale), float32(config.Config.WindowHeight/2)+particle.Y*float32(scale), particle.Size, config.ThirdColor, true)
norm := math32.Sqrt(particles.Particles[i].X*particles.Particles[i].X + particles.Particles[i].Y*particles.Particles[i].Y)
particles.Particles[i].X += particle.VX / float32(ebiten.ActualTPS())
particles.Particles[i].Y += particle.VY / float32(ebiten.ActualTPS())
speed := math32.Sqrt(particle.VX*particle.VX + particle.VY*particle.VY)
particles.Particles[i].VX += (config.Config.ParticleAcceleration*S - speed*config.Config.ParticleDrag) * particle.X / norm / float32(ebiten.ActualTPS())
particles.Particles[i].VY += (config.Config.ParticleAcceleration*S - speed*config.Config.ParticleDrag) * particle.Y / norm / float32(ebiten.ActualTPS())
}
} else {
2025-01-16 21:45:06 +01:00
for i := uint32(0); i < numSamples; i++ {
FFTBuffer[i] = float64(AX)
2025-01-16 21:25:11 +01:00
binary.Read(audio.SampleRingBuffer, binary.NativeEndian, &AX)
binary.Read(audio.SampleRingBuffer, binary.NativeEndian, &AY)
}
2025-01-16 22:56:00 +01:00
2025-01-16 22:58:28 +01:00
indices := peaks.Get(FFTBuffer, config.Config.PeakDetectSeparator)
2025-01-16 22:56:00 +01:00
offset := uint32(indices[0])
2025-01-16 21:45:06 +01:00
for i := uint32(0); i < numSamples-1; i++ {
2025-01-16 22:48:09 +01:00
fAX := float32(FFTBuffer[(i+offset)%numSamples]) * config.Config.Gain * float32(scale)
fBX := float32(FFTBuffer[(i+1+offset)%numSamples]) * config.Config.Gain * float32(scale)
2025-01-16 21:45:06 +01:00
vector.StrokeLine(screen, float32(config.Config.WindowWidth)*float32(i)/float32(numSamples), float32(config.Config.WindowHeight/2)+fAX, float32(config.Config.WindowWidth)*float32(i+1)/float32(numSamples), float32(config.Config.WindowHeight/2)+fBX, config.Config.LineThickness, config.ThirdColorAdj, true)
2025-01-16 21:25:11 +01:00
}
2024-11-03 14:48:53 +01:00
}
2024-11-02 17:50:15 +01:00
//audio.SampleRingBuffer.Reset()
if config.Config.FPSCounter {
ebitenutil.DebugPrint(screen, fmt.Sprintf("TPS: %0.2f", ebiten.ActualTPS()))
}
2024-10-21 01:30:26 +02:00
2024-11-02 17:50:15 +01:00
if config.Config.ShowMPRIS {
op := &text.DrawOptions{}
op.GeoM.Translate(16, 16)
2024-11-02 23:58:30 +01:00
op.ColorScale.ScaleWithColor(color.RGBA{config.AccentColor.R, config.AccentColor.G, config.AccentColor.B, config.Config.MPRISTextOpacity})
2024-11-02 17:50:15 +01:00
text.Draw(screen, media.PlayingMediaInfo.Artist+" - "+media.PlayingMediaInfo.Title, &text.GoTextFace{
Source: fonts.Font,
Size: 32,
}, op)
2024-10-20 22:58:09 +02:00
2024-11-02 17:50:15 +01:00
op = &text.DrawOptions{}
op.GeoM.Translate(16, 64)
2024-11-02 23:58:30 +01:00
op.ColorScale.ScaleWithColor(color.RGBA{config.ThirdColor.R, config.ThirdColor.G, config.ThirdColor.B, config.Config.MPRISTextOpacity})
2024-11-02 17:50:15 +01:00
text.Draw(screen, media.PlayingMediaInfo.Album, &text.GoTextFace{
Source: fonts.Font,
Size: 16,
}, op)
op = &text.DrawOptions{}
op.GeoM.Translate(16, 80)
2024-11-02 23:58:30 +01:00
op.ColorScale.ScaleWithColor(color.RGBA{config.AccentColor.R, config.AccentColor.G, config.AccentColor.B, config.Config.MPRISTextOpacity})
2024-11-02 17:50:15 +01:00
text.Draw(screen, media.FmtDuration(media.PlayingMediaInfo.Position)+" / "+media.FmtDuration(media.PlayingMediaInfo.Duration), &text.GoTextFace{
Source: fonts.Font,
Size: 32,
}, op)
2024-10-20 22:58:09 +02:00
}
2025-01-16 21:25:11 +01:00
2024-11-02 17:50:15 +01:00
}
func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
return int(config.Config.WindowWidth), int(config.Config.WindowHeight)
}
2024-10-20 22:58:09 +02:00
2024-11-02 17:50:15 +01:00
func main() {
config.Init()
audio.Init()
fonts.Init()
2024-11-02 23:44:33 +01:00
icons.Init()
2024-11-02 17:50:15 +01:00
go audio.Start()
go media.Start()
2024-11-02 23:44:33 +01:00
ebiten.SetWindowIcon([]image.Image{icons.WindowIcon48, icons.WindowIcon32, icons.WindowIcon16})
2024-11-02 17:50:15 +01:00
ebiten.SetWindowSize(int(config.Config.WindowWidth), int(config.Config.WindowHeight))
ebiten.SetWindowTitle("xyosc")
2024-11-02 23:44:33 +01:00
ebiten.SetWindowMousePassthrough(true)
2024-11-02 17:50:15 +01:00
ebiten.SetTPS(int(config.Config.TargetFPS))
ebiten.SetWindowDecorated(false)
screenW, screenH := ebiten.Monitor().Size()
ebiten.SetWindowPosition(screenW/2-int(config.Config.WindowWidth)/2, screenH/2-int(config.Config.WindowHeight)/2)
ebiten.SetVsyncEnabled(true)
2024-12-22 19:22:10 +01:00
if err := ebiten.RunGameWithOptions(&Game{}, &ebiten.RunGameOptions{ScreenTransparent: true}); err != nil {
2024-11-02 17:50:15 +01:00
log.Fatal(err)
}
2024-10-20 22:58:09 +02:00
}