Compare commits

..

No commits in common. "ebb4d7102bb393d5185ce318524850609d4790c8" and "d59a2859b61c3a18c1bf5dc4016bcfedec421dcc" have entirely different histories.

3 changed files with 32 additions and 43 deletions

View File

@ -20,16 +20,16 @@ milkbucket reads a PCM stream from standard input to generate visualizations.
If you have an audio file and a preset in mind, you can use `ffmpeg` to If you have an audio file and a preset in mind, you can use `ffmpeg` to
generate the PCM stream, then pipe to milkbucket, like so: generate the PCM stream, then pipe to milkbucket, like so:
``` ```
ffmpeg -i $audio -ar 44100 -f s16le - | ./milkbucket $preset ffmpeg -i $audio -ar 44100 -f s16le - | ./milkbucket -p $preset
``` ```
Note that you can pass in multiple presets, then use the arrow keys to cycle Note that you can pass in multiple presets with multiple `-p` flags, then use
through the presets while running. the arrow keys to cycle through the presets while running.
Note that neither of these commands (ffmpeg or milkbucket) will output any Note that neither of these commands (ffmpeg or milkbucket) will output any
audio! If you want to hear the audio at the same time (and assuming your audio! If you want to hear the audio at the same time (and assuming your
machine uses pipewire), run: machine uses pipewire), run:
``` ```
ffmpeg -i $audio -ar 44100 -f s16le - | tee >(pw-play --rate=44100 --format=s16 -) | ./milkbucket $preset ffmpeg -i $audio -ar 44100 -f s16le - | tee >(pw-play --rate=44100 --format=s16 -) | ./milkbucket -p $preset
``` ```
(If you don't use pipewire try using `aplay` instead of `pw-play`, or some (If you don't use pipewire try using `aplay` instead of `pw-play`, or some
other command for playing PCM streams.) other command for playing PCM streams.)
@ -37,5 +37,5 @@ other command for playing PCM streams.)
You can also generate a visualization from your system sound. Assuming pipewire You can also generate a visualization from your system sound. Assuming pipewire
again, and that you have audio coming from Firefox, run: again, and that you have audio coming from Firefox, run:
``` ```
pw-record --target Firefox - | ./milkbucket $preset pw-record --target Firefox - | ./milkbucket -p $preset
``` ```

View File

@ -20,12 +20,14 @@ import (
"encoding/binary" "encoding/binary"
"errors" "errors"
"io" "io"
"log"
"os" "os"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/veandco/go-sdl2/sdl" "github.com/veandco/go-sdl2/sdl"
) )
var presets []string
var transition bool var transition bool
/* /*
@ -44,29 +46,16 @@ func checkStdin() error {
} }
/* /*
* validatePreset performs some basic checks on the preset passed in and * validatePresets performs some basic checks on the presets passed in and
* returns an error if it finds a problem. * returns an error if it finds a problem.
*/ */
func validatePreset(preset string) error { func validatePresets() error {
info, err := os.Stat(preset) for _, p := range presets {
info, err := os.Stat(p)
if err != nil { if err != nil {
return err return err
} else if info.IsDir() { } else if info.IsDir() {
return errors.New("preset " + preset + " is a directory") return errors.New("preset " + p + " is a directory")
}
return nil
}
/*
* validatePresets validates each preset passed in and returns an error if it
* finds a problem.
*/
func validatePresets(cmd *cobra.Command, args []string) error {
for _, p := range args {
err := validatePreset(p)
if err != nil {
return err
} }
} }
@ -147,43 +136,45 @@ func update(m *milkDropWindow) (bool, error) {
func milkbucket(cmd *cobra.Command, args []string) { func milkbucket(cmd *cobra.Command, args []string) {
err := checkStdin() err := checkStdin()
if err != nil { if err != nil {
cobra.CheckErr(err) log.Fatal(err)
} }
err = sdl.Init(sdl.INIT_VIDEO) err = sdl.Init(sdl.INIT_VIDEO)
if err != nil { if err != nil {
cobra.CheckErr(err) log.Fatal(err)
} }
defer sdl.Quit() defer sdl.Quit()
m, err := newMilkDropWindow(800, 600, args) err = validatePresets()
if err != nil { if err != nil {
cobra.CheckErr(err) log.Fatal(err)
}
m, err := newMilkDropWindow(800, 600, presets)
if err != nil {
log.Fatal(err)
} }
defer m.destroy() defer m.destroy()
if len(args) > 0 {
m.loadPreset(false) m.loadPreset(false)
}
running := true running := true
for running { for running {
running, err = update(m) running, err = update(m)
if err != nil { if err != nil {
cobra.CheckErr(err) log.Fatal(err)
} }
} }
} }
// rootCmd represents the base command when called without any subcommands // rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{ var rootCmd = &cobra.Command{
Use: "milkbucket [presets]", Use: "milkbucket",
Short: "Audio visualizer", Short: "Audio visualizer",
Long: `milkbucket is an audio visualizer. Long: `milkbucket is an audio visualizer.
It uses Milkdrop preset files to generate visualizations from standard input.`, It uses Milkdrop preset files to generate visualizations from standard input.`,
Run: milkbucket, Run: milkbucket,
Args: validatePresets,
} }
// Execute adds all child commands to the root command and sets flags appropriately. // Execute adds all child commands to the root command and sets flags appropriately.
@ -203,4 +194,6 @@ func init() {
// Cobra also supports local flags, which will only run // Cobra also supports local flags, which will only run
// when this action is called directly. // when this action is called directly.
rootCmd.Flags().BoolVarP(&transition, "transition", "t", false, "smoothly transition between presets") rootCmd.Flags().BoolVarP(&transition, "transition", "t", false, "smoothly transition between presets")
rootCmd.Flags().StringArrayVarP(&presets, "presets", "p", []string{}, "preset files to use")
} }

View File

@ -67,10 +67,8 @@ func (m *milkDropWindow) setupPresets(presets []string) {
* preset. * preset.
*/ */
func (m *milkDropWindow) nextPreset(smooth bool) { func (m *milkDropWindow) nextPreset(smooth bool) {
if m.preset.Len() > 0 {
m.preset = m.preset.Next() m.preset = m.preset.Next()
m.loadPreset(smooth) m.loadPreset(smooth)
}
} }
/* /*
@ -78,10 +76,8 @@ func (m *milkDropWindow) nextPreset(smooth bool) {
* preset. * preset.
*/ */
func (m *milkDropWindow) prevPreset(smooth bool) { func (m *milkDropWindow) prevPreset(smooth bool) {
if m.preset.Len() > 0 {
m.preset = m.preset.Prev() m.preset = m.preset.Prev()
m.loadPreset(smooth) m.loadPreset(smooth)
}
} }
/* /*