2024-08-25 02:05:15 +00:00
/ *
Copyright © 2024 filifa
This program is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
* /
package cmd
import (
2024-09-06 03:40:31 +00:00
"encoding/binary"
2024-09-05 02:13:42 +00:00
"errors"
2024-09-06 03:40:31 +00:00
"io"
2024-08-25 02:05:15 +00:00
"os"
"github.com/spf13/cobra"
"github.com/veandco/go-sdl2/sdl"
)
2024-09-05 01:04:47 +00:00
var transition bool
2024-09-08 01:43:46 +00:00
var softCutDuration float64
2024-09-08 01:56:02 +00:00
var width int32
var height int32
2024-08-25 02:05:15 +00:00
2024-09-05 02:35:38 +00:00
/ *
* checkStdin checks if any data has been passed in through stdin and returns
* an error if we don ' t believe so .
* /
func checkStdin ( ) error {
stat , err := os . Stdin . Stat ( )
if err != nil {
return err
} else if ( stat . Mode ( ) & os . ModeCharDevice ) != 0 {
return errors . New ( "nothing to read from stdin" )
}
return nil
}
2024-09-05 02:13:42 +00:00
/ *
2024-09-08 00:26:26 +00:00
* validatePreset performs some basic checks on the preset passed in and
2024-09-05 02:13:42 +00:00
* returns an error if it finds a problem .
* /
2024-09-08 00:26:26 +00:00
func validatePreset ( preset string ) error {
info , err := os . Stat ( preset )
if err != nil {
return err
} else if info . IsDir ( ) {
return errors . New ( "preset " + preset + " is a directory" )
}
return nil
}
/ *
* validatePresets validates each preset passed in and returns an error if it
* finds a problem .
* /
2024-09-08 00:16:08 +00:00
func validatePresets ( cmd * cobra . Command , args [ ] string ) error {
for _ , p := range args {
2024-09-08 00:26:26 +00:00
err := validatePreset ( p )
2024-09-05 02:13:42 +00:00
if err != nil {
return err
}
}
return nil
}
2024-09-05 02:10:30 +00:00
/ *
* handleWindowEvent handles window events like resizing .
* /
2024-08-25 02:05:15 +00:00
func handleWindowEvent ( event * sdl . WindowEvent , m * milkDropWindow ) {
switch event . Event {
case sdl . WINDOWEVENT_RESIZED :
w , h := event . Data1 , event . Data2
2024-08-25 03:03:37 +00:00
m . resize ( w , h )
2024-08-25 02:05:15 +00:00
}
}
2024-09-05 02:10:30 +00:00
/ *
* handleKeyboardEvent handles keyboard inputs .
* /
2024-09-03 03:17:11 +00:00
func handleKeyboardEvent ( event * sdl . KeyboardEvent , m * milkDropWindow ) {
scancode := event . Keysym . Scancode
2024-09-03 03:44:24 +00:00
if event . Type == sdl . KEYDOWN && scancode == sdl . SCANCODE_RIGHT && event . Repeat == 0 {
2024-09-05 01:04:47 +00:00
m . nextPreset ( transition )
2024-09-03 03:44:24 +00:00
} else if event . Type == sdl . KEYDOWN && scancode == sdl . SCANCODE_LEFT && event . Repeat == 0 {
2024-09-05 01:04:47 +00:00
m . prevPreset ( transition )
2024-09-03 03:17:11 +00:00
}
}
2024-09-05 02:10:30 +00:00
/ *
* handleEvent passes the event to a more specific function based on its type .
* It returns false if the program should quit , true otherwise .
* /
2024-08-25 02:05:15 +00:00
func handleEvent ( event sdl . Event , m * milkDropWindow ) bool {
switch event . ( type ) {
case * sdl . QuitEvent :
return false
case * sdl . WindowEvent :
event := event . ( * sdl . WindowEvent )
handleWindowEvent ( event , m )
2024-09-03 03:17:11 +00:00
case * sdl . KeyboardEvent :
event := event . ( * sdl . KeyboardEvent )
handleKeyboardEvent ( event , m )
2024-08-25 02:05:15 +00:00
}
return true
}
2024-09-05 02:31:52 +00:00
/ *
* update handles events and renders new frames of the visualization . It
* returns a bool indicating whether the program should keep running and an
* error , if any .
* /
2024-09-06 04:16:14 +00:00
func update ( m * milkDropWindow ) ( bool , error ) {
2024-09-05 02:31:52 +00:00
for event := sdl . PollEvent ( ) ; event != nil ; event = sdl . PollEvent ( ) {
keepRunning := handleEvent ( event , m )
if ! keepRunning {
return false , nil
}
}
2024-09-07 20:54:40 +00:00
audioData := make ( [ ] int16 , maxSamples ( 2 , 2 ) )
2024-09-06 03:40:31 +00:00
2024-09-06 04:16:14 +00:00
err := binary . Read ( os . Stdin , binary . LittleEndian , audioData )
2024-09-06 03:40:31 +00:00
if err == io . ErrUnexpectedEOF {
return false , nil
} else if err != nil {
return true , err
}
m . render ( audioData )
return true , nil
2024-09-05 02:31:52 +00:00
}
2024-09-05 02:10:30 +00:00
/ *
* milkbucket sets up the program and starts a rendering loop .
* /
2024-08-25 02:05:15 +00:00
func milkbucket ( cmd * cobra . Command , args [ ] string ) {
2024-09-05 02:35:38 +00:00
err := checkStdin ( )
2024-08-26 02:03:57 +00:00
if err != nil {
2024-09-08 00:45:50 +00:00
cobra . CheckErr ( err )
2024-08-26 02:03:57 +00:00
}
err = sdl . Init ( sdl . INIT_VIDEO )
2024-08-25 02:05:15 +00:00
if err != nil {
2024-09-08 00:45:50 +00:00
cobra . CheckErr ( err )
2024-08-25 02:05:15 +00:00
}
defer sdl . Quit ( )
2024-09-08 01:56:02 +00:00
m , err := newMilkDropWindow ( width , height , args , softCutDuration )
2024-08-25 02:05:15 +00:00
if err != nil {
2024-09-08 00:45:50 +00:00
cobra . CheckErr ( err )
2024-08-25 02:05:15 +00:00
}
defer m . destroy ( )
2024-09-08 00:20:09 +00:00
if len ( args ) > 0 {
m . loadPreset ( false )
}
2024-08-25 02:05:15 +00:00
running := true
for running {
2024-09-06 04:16:14 +00:00
running , err = update ( m )
2024-08-25 02:05:15 +00:00
if err != nil {
2024-09-08 00:45:50 +00:00
cobra . CheckErr ( err )
2024-08-25 02:05:15 +00:00
}
}
}
// rootCmd represents the base command when called without any subcommands
var rootCmd = & cobra . Command {
2024-09-08 00:22:10 +00:00
Use : "milkbucket [presets]" ,
2024-09-03 02:03:17 +00:00
Short : "Audio visualizer" ,
Long : ` milkbucket is an audio visualizer .
2024-08-25 02:05:15 +00:00
2024-09-03 02:03:17 +00:00
It uses Milkdrop preset files to generate visualizations from standard input . ` ,
2024-09-08 00:16:08 +00:00
Run : milkbucket ,
Args : validatePresets ,
2024-08-25 02:05:15 +00:00
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute ( ) {
err := rootCmd . Execute ( )
if err != nil {
os . Exit ( 1 )
}
}
func init ( ) {
// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.
// Cobra also supports local flags, which will only run
// when this action is called directly.
2024-09-05 01:04:47 +00:00
rootCmd . Flags ( ) . BoolVarP ( & transition , "transition" , "t" , false , "smoothly transition between presets" )
2024-09-08 01:43:46 +00:00
rootCmd . Flags ( ) . Float64VarP ( & softCutDuration , "soft-cut-duration" , "s" , 3 , "time in seconds for a soft transition between two presets (use with --transition)" )
2024-09-08 01:56:02 +00:00
rootCmd . Flags ( ) . Int32Var ( & width , "width" , 800 , "window width" )
rootCmd . Flags ( ) . Int32Var ( & height , "height" , 600 , "window height" )
2024-08-25 02:05:15 +00:00
}