Compare commits
6 Commits
d59a2859b6
...
ebb4d7102b
Author | SHA1 | Date |
---|---|---|
filifa | ebb4d7102b | |
filifa | 6a85dcb816 | |
filifa | 92c3b48fa7 | |
filifa | 96e962b56a | |
filifa | 1e87393d07 | |
filifa | 6d045ff304 |
10
README.md
10
README.md
|
@ -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 -p $preset
|
ffmpeg -i $audio -ar 44100 -f s16le - | ./milkbucket $preset
|
||||||
```
|
```
|
||||||
Note that you can pass in multiple presets with multiple `-p` flags, then use
|
Note that you can pass in multiple presets, then use the arrow keys to cycle
|
||||||
the arrow keys to cycle through the presets while running.
|
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 -p $preset
|
ffmpeg -i $audio -ar 44100 -f s16le - | tee >(pw-play --rate=44100 --format=s16 -) | ./milkbucket $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 -p $preset
|
pw-record --target Firefox - | ./milkbucket $preset
|
||||||
```
|
```
|
||||||
|
|
53
cmd/root.go
53
cmd/root.go
|
@ -20,14 +20,12 @@ 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
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -46,16 +44,29 @@ func checkStdin() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* validatePresets performs some basic checks on the presets passed in and
|
* validatePreset performs some basic checks on the preset passed in and
|
||||||
* returns an error if it finds a problem.
|
* returns an error if it finds a problem.
|
||||||
*/
|
*/
|
||||||
func validatePresets() error {
|
func validatePreset(preset string) error {
|
||||||
for _, p := range presets {
|
info, err := os.Stat(preset)
|
||||||
info, err := os.Stat(p)
|
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.
|
||||||
|
*/
|
||||||
|
func validatePresets(cmd *cobra.Command, args []string) error {
|
||||||
|
for _, p := range args {
|
||||||
|
err := validatePreset(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if info.IsDir() {
|
|
||||||
return errors.New("preset " + p + " is a directory")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,45 +147,43 @@ 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 {
|
||||||
log.Fatal(err)
|
cobra.CheckErr(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = sdl.Init(sdl.INIT_VIDEO)
|
err = sdl.Init(sdl.INIT_VIDEO)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
cobra.CheckErr(err)
|
||||||
}
|
}
|
||||||
defer sdl.Quit()
|
defer sdl.Quit()
|
||||||
|
|
||||||
err = validatePresets()
|
m, err := newMilkDropWindow(800, 600, args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
cobra.CheckErr(err)
|
||||||
}
|
|
||||||
|
|
||||||
m, err := newMilkDropWindow(800, 600, presets)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
}
|
||||||
defer m.destroy()
|
defer m.destroy()
|
||||||
|
|
||||||
m.loadPreset(false)
|
if len(args) > 0 {
|
||||||
|
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 {
|
||||||
log.Fatal(err)
|
cobra.CheckErr(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",
|
Use: "milkbucket [presets]",
|
||||||
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.
|
||||||
|
@ -194,6 +203,4 @@ 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")
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,8 +67,10 @@ func (m *milkDropWindow) setupPresets(presets []string) {
|
||||||
* preset.
|
* preset.
|
||||||
*/
|
*/
|
||||||
func (m *milkDropWindow) nextPreset(smooth bool) {
|
func (m *milkDropWindow) nextPreset(smooth bool) {
|
||||||
m.preset = m.preset.Next()
|
if m.preset.Len() > 0 {
|
||||||
m.loadPreset(smooth)
|
m.preset = m.preset.Next()
|
||||||
|
m.loadPreset(smooth)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -76,8 +78,10 @@ func (m *milkDropWindow) nextPreset(smooth bool) {
|
||||||
* preset.
|
* preset.
|
||||||
*/
|
*/
|
||||||
func (m *milkDropWindow) prevPreset(smooth bool) {
|
func (m *milkDropWindow) prevPreset(smooth bool) {
|
||||||
m.preset = m.preset.Prev()
|
if m.preset.Len() > 0 {
|
||||||
m.loadPreset(smooth)
|
m.preset = m.preset.Prev()
|
||||||
|
m.loadPreset(smooth)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Reference in New Issue