diff --git a/.goreleaser.yml b/.goreleaser.yml index 522dba8..c7b37cb 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -8,6 +8,9 @@ version: 2 +env: + - CGO_ENABLED=0 + before: hooks: # You may remove this if you don't use go modules. @@ -16,8 +19,19 @@ before: - go generate ./... builds: - - env: - - CGO_ENABLED=0 + - main: ./cmd/x32-cli + id: 'x32-cli' + binary: x32-cli + goos: + - linux + - windows + - darwin + goarch: + - amd64 + + - main: ./cmd/xair-cli + id: 'xair-cli' + binary: xair-cli goos: - linux - windows diff --git a/bus.go b/cmd/x32-cli/bus.go similarity index 100% rename from bus.go rename to cmd/x32-cli/bus.go diff --git a/cmd/x32-cli/cli.go b/cmd/x32-cli/cli.go new file mode 100644 index 0000000..3634430 --- /dev/null +++ b/cmd/x32-cli/cli.go @@ -0,0 +1,131 @@ +package main + +import ( + "fmt" + "io" + "os" + "runtime/debug" + "strings" + "time" + + "github.com/alecthomas/kong" + "github.com/charmbracelet/log" + kongcompletion "github.com/jotaen/kong-completion" + "github.com/onyx-and-iris/xair-cli/internal/xair" +) + +var version string // Version of the CLI, set at build time. + +// VersionFlag is a custom flag type that prints the version and exits. +type VersionFlag string + +func (v VersionFlag) Decode(_ *kong.DecodeContext) error { return nil } // nolint: revive +func (v VersionFlag) IsBool() bool { return true } // nolint: revive +func (v VersionFlag) BeforeApply(app *kong.Kong, vars kong.Vars) error { // nolint: revive, unparam + fmt.Printf("x32-cli version: %s\n", vars["version"]) + app.Exit(0) + return nil +} + +type context struct { + Client *xair.X32Client + Out io.Writer +} + +type Config struct { + Host string `default:"mixer.local" help:"The host of the X32 device." env:"X32_CLI_HOST" short:"H"` + Port int `default:"10023" help:"The port of the X32 device." env:"X32_CLI_PORT" short:"P"` + Timeout time.Duration `default:"100ms" help:"Timeout for OSC operations." env:"X32_CLI_TIMEOUT" short:"T"` + Loglevel string `default:"warn" help:"Log level for the CLI." env:"X32_CLI_LOGLEVEL" short:"L" enum:"debug,info,warn,error,fatal"` +} + +// CLI is the main struct for the command-line interface. +// It embeds the Config struct for global configuration and defines the available commands and flags. +type CLI struct { + Config `embed:"" prefix:"" help:"The configuration for the CLI."` + + Version VersionFlag `help:"Print x32-cli version information and quit" name:"version" short:"v"` + + Completion kongcompletion.Completion `help:"Generate shell completion scripts." cmd:"" aliases:"c"` + + Raw RawCmd `help:"Send raw OSC messages to the mixer." cmd:"" group:"Raw"` + Main MainCmdGroup `help:"Control the Main L/R output" cmd:"" group:"Main"` + Mainmono MainMonoCmdGroup `help:"Control the Main Mono output" cmd:"" group:"MainMono"` + Strip StripCmdGroup `help:"Control the strips." cmd:"" group:"Strip"` + Bus BusCmdGroup `help:"Control the buses." cmd:"" group:"Bus"` + Headamp HeadampCmdGroup `help:"Control input gain and phantom power." cmd:"" group:"Headamp"` + Snapshot SnapshotCmdGroup `help:"Save and load mixer states." cmd:"" group:"Snapshot"` +} + +func main() { + var cli CLI + kongcompletion.Register(kong.Must(&cli)) + ctx := kong.Parse( + &cli, + kong.Name("x32-cli"), + kong.Description("A CLI to control Behringer X32 mixers."), + kong.UsageOnError(), + kong.ConfigureHelp(kong.HelpOptions{ + Compact: true, + }), + kong.Vars{ + "version": func() string { + if version == "" { + info, ok := debug.ReadBuildInfo() + if !ok { + return "(unable to read build info)" + } + version = strings.Split(info.Main.Version, "-")[0] + } + return version + }(), + }, + ) + + ctx.FatalIfErrorf(run(ctx, cli.Config)) +} + +// run is the main entry point for the CLI. +// It connects to the X32 device, retrieves mixer info, and then runs the command. +func run(ctx *kong.Context, config Config) error { + loglevel, err := log.ParseLevel(config.Loglevel) + if err != nil { + return fmt.Errorf("invalid log level: %w", err) + } + log.SetLevel(loglevel) + + client, err := connect(config) + if err != nil { + return fmt.Errorf("failed to connect to X32 device: %w", err) + } + defer client.Close() + + client.StartListening() + resp, err := client.RequestInfo() + if err != nil { + return err + } + log.Infof("Received mixer info: %+v", resp) + + ctx.Bind(&context{ + Client: client, + Out: os.Stdout, + }) + + return ctx.Run() +} + +// connect creates a new X32 client based on the provided configuration. +func connect(config Config) (*xair.X32Client, error) { + client, err := xair.NewX32Client( + config.Host, + config.Port, + xair.WithKind("x32"), + xair.WithTimeout(config.Timeout), + ) + if err != nil { + return nil, err + } + + return client, nil +} diff --git a/headamp.go b/cmd/x32-cli/headamp.go similarity index 100% rename from headamp.go rename to cmd/x32-cli/headamp.go diff --git a/main.go b/cmd/x32-cli/main.go similarity index 100% rename from main.go rename to cmd/x32-cli/main.go diff --git a/cmd/x32-cli/mainmono.go b/cmd/x32-cli/mainmono.go new file mode 100644 index 0000000..bf4fb64 --- /dev/null +++ b/cmd/x32-cli/mainmono.go @@ -0,0 +1,489 @@ +package main + +import ( + "fmt" + "time" + + "github.com/alecthomas/kong" +) + +// MainMonoCmdGroup defines the command group for controlling the Main Mono output, including commands for mute state, fader level, and fade-in/fade-out times. +type MainMonoCmdGroup struct { + Mute MainMonoMuteCmd `help:"Get or set the mute state of the Main Mono output." cmd:""` + + Fader MainMonoFaderCmd `help:"Get or set the fader level of the Main Mono output." cmd:""` + Fadein MainMonoFadeinCmd `help:"Fade in the Main Mono output over a specified duration." cmd:""` + Fadeout MainMonoFadeoutCmd `help:"Fade out the Main Mono output over a specified duration." cmd:""` + + Eq MainMonoEqCmdGroup `help:"Commands for controlling the equalizer settings of the Main Mono output." cmd:"eq"` + Comp MainMonoCompCmdGroup `help:"Commands for controlling the compressor settings of the Main Mono output." cmd:"comp"` +} + +// MainMonoMuteCmd defines the command for getting or setting the mute state of the Main Mono output, allowing users to specify the desired state as "true"/"on" or "false"/"off". +type MainMonoMuteCmd struct { + Mute *string `arg:"" help:"The mute state to set. If not provided, the current state will be printed." optional:"" enum:"true,false"` +} + +// Run executes the MainMonoMuteCmd command, either retrieving the current mute state of the Main Mono output or setting it based on the provided argument. +func (cmd *MainMonoMuteCmd) Run(ctx *context) error { + if cmd.Mute == nil { + resp, err := ctx.Client.MainMono.Mute() + if err != nil { + return fmt.Errorf("failed to get Main Mono mute state: %w", err) + } + fmt.Fprintf(ctx.Out, "Main Mono mute state: %t\n", resp) + return nil + } + + if err := ctx.Client.MainMono.SetMute(*cmd.Mute == "true"); err != nil { + return fmt.Errorf("failed to set Main Mono mute state: %w", err) + } + fmt.Fprintf(ctx.Out, "Main Mono mute state set to: %s\n", *cmd.Mute) + return nil +} + +// MainMonoFaderCmd defines the command for getting or setting the fader level of the Main Mono output, allowing users to specify the desired level in dB. +type MainMonoFaderCmd struct { + Level *float64 `arg:"" help:"The fader level to set. If not provided, the current level will be printed." optional:""` +} + +// Run executes the MainMonoFaderCmd command, either retrieving the current fader level of the Main Mono output or setting it based on the provided argument. +func (cmd *MainMonoFaderCmd) Run(ctx *context) error { + if cmd.Level == nil { + resp, err := ctx.Client.MainMono.Fader() + if err != nil { + return fmt.Errorf("failed to get Main Mono fader level: %w", err) + } + fmt.Fprintf(ctx.Out, "Main Mono fader level: %.2f\n", resp) + return nil + } + + if err := ctx.Client.MainMono.SetFader(*cmd.Level); err != nil { + return fmt.Errorf("failed to set Main Mono fader level: %w", err) + } + fmt.Fprintf(ctx.Out, "Main Mono fader level set to: %.2f\n", *cmd.Level) + return nil +} + +// MainMonoFadeinCmd defines the command for getting or setting the fade-in time of the Main Mono output, allowing users to specify the desired duration for the fade-in effect. +type MainMonoFadeinCmd struct { + Duration time.Duration `flag:"" help:"The duration of the fade-in. (in seconds.)" default:"5s"` + Target float64 ` help:"The target level for the fade-in. If not provided, the current target level will be printed." default:"0.0" arg:""` +} + +// Run executes the MainMonoFadeinCmd command, either retrieving the current fade-in time of the Main Mono output or setting it based on the provided argument, with an optional target level for the fade-in effect. +func (cmd *MainMonoFadeinCmd) Run(ctx *context) error { + currentLevel, err := ctx.Client.MainMono.Fader() + if err != nil { + return fmt.Errorf("failed to get Main Mono fader level: %w", err) + } + + if currentLevel >= cmd.Target { + return fmt.Errorf( + "current fader level (%.2f) is already at or above the target level (%.2f)", + currentLevel, + cmd.Target, + ) + } + + totalSteps := float64(cmd.Target - currentLevel) + stepDuration := time.Duration(cmd.Duration.Seconds()*1000/totalSteps) * time.Millisecond + for currentLevel < cmd.Target { + currentLevel++ + if err := ctx.Client.MainMono.SetFader(currentLevel); err != nil { + return fmt.Errorf("failed to set Main Mono fader level: %w", err) + } + time.Sleep(stepDuration) + } + fmt.Fprintf(ctx.Out, "Main Mono fade-in completed. Final level: %.2f\n", currentLevel) + return nil +} + +// MainMonoFadeoutCmd defines the command for getting or setting the fade-out time of the Main Mono output, allowing users to specify the desired duration for the fade-out effect and an optional target level to fade out to. +type MainMonoFadeoutCmd struct { + Duration time.Duration `flag:"" help:"The duration of the fade-out. (in seconds.)" default:"5s"` + Target float64 ` help:"The target level for the fade-out. If not provided, the current target level will be printed." default:"-90.0" arg:""` +} + +// Run executes the MainMonoFadeoutCmd command, either retrieving the current fade-out time of the Main Mono output or setting it based on the provided argument, with an optional target level for the fade-out effect. +func (cmd *MainMonoFadeoutCmd) Run(ctx *context) error { + currentLevel, err := ctx.Client.MainMono.Fader() + if err != nil { + return fmt.Errorf("failed to get Main Mono fader level: %w", err) + } + + if currentLevel <= cmd.Target { + return fmt.Errorf( + "current fader level (%.2f) is already at or below the target level (%.2f)", + currentLevel, + cmd.Target, + ) + } + + totalSteps := float64(currentLevel - cmd.Target) + stepDuration := time.Duration(cmd.Duration.Seconds()*1000/totalSteps) * time.Millisecond + for currentLevel > cmd.Target { + currentLevel-- + if err := ctx.Client.MainMono.SetFader(currentLevel); err != nil { + return fmt.Errorf("failed to set Main Mono fader level: %w", err) + } + time.Sleep(stepDuration) + } + fmt.Fprintf(ctx.Out, "Main Mono fade-out completed. Final level: %.2f\n", currentLevel) + return nil +} + +// MainMonoEqCmdGroup defines the command group for controlling the equalizer settings of the Main Mono output, including commands for getting or setting the EQ parameters. +type MainMonoEqCmdGroup struct { + On MainMonoEqOnCmd `help:"Get or set the EQ on/off state of the Main Mono output." cmd:"on"` + Band struct { + Band int `arg:"" help:"The EQ band number."` + Gain MainMonoEqBandGainCmd `help:"Get or set the gain of the specified EQ band." cmd:"gain"` + Freq MainMonoEqBandFreqCmd `help:"Get or set the frequency of the specified EQ band." cmd:"freq"` + Q MainMonoEqBandQCmd `help:"Get or set the Q factor of the specified EQ band." cmd:"q"` + Type MainMonoEqBandTypeCmd `help:"Get or set the type of the specified EQ band." cmd:"type"` + } `help:"Commands for controlling individual EQ bands of the Main Mono output." arg:""` +} + +// Validate checks if the provided EQ band number is within the valid range (1-6) for the Main Mono output. +func (cmd *MainMonoEqCmdGroup) Validate(ctx kong.Context) error { + if cmd.Band.Band < 1 || cmd.Band.Band > 6 { + return fmt.Errorf("invalid EQ band number: %d. Valid range is 1-6", cmd.Band.Band) + } + return nil +} + +// MainMonoEqOnCmd defines the command for getting or setting the EQ on/off state of the Main Mono output, allowing users to specify the desired state as "true"/"on" or "false"/"off". +type MainMonoEqOnCmd struct { + Enable *string `arg:"" help:"The EQ on/off state to set. If not provided, the current state will be printed." optional:"" enum:"true,false"` +} + +// Run executes the MainMonoEqOnCmd command, either retrieving the current EQ on/off state of the Main Mono output or setting it based on the provided argument. +func (cmd *MainMonoEqOnCmd) Run(ctx *context, main *MainCmdGroup) error { + if cmd.Enable == nil { + resp, err := ctx.Client.MainMono.Eq.On(0) + if err != nil { + return fmt.Errorf("failed to get Main Mono EQ on/off state: %w", err) + } + fmt.Fprintf(ctx.Out, "Main Mono EQ on/off state: %t\n", resp) + return nil + } + + if err := ctx.Client.MainMono.Eq.SetOn(0, *cmd.Enable == "true"); err != nil { + return fmt.Errorf("failed to set Main Mono EQ on/off state: %w", err) + } + fmt.Fprintf(ctx.Out, "Main Mono EQ on/off state set to: %t\n", *cmd.Enable == "true") + return nil +} + +// MainMonoEqBandGainCmd defines the command for getting or setting the gain of a specific EQ band on the Main Mono output, allowing users to specify the desired gain in dB. +type MainMonoEqBandGainCmd struct { + Level *float64 `arg:"" help:"The gain level to set for the specified EQ band. If not provided, the current gain will be printed." optional:""` +} + +// Run executes the MainMonoEqBandGainCmd command, either retrieving the current gain of a specific EQ band on the Main Mono output or setting it based on the provided argument. +func (cmd *MainMonoEqBandGainCmd) Run(ctx *context, main *MainCmdGroup, mainEq *MainMonoEqCmdGroup) error { + if cmd.Level == nil { + resp, err := ctx.Client.MainMono.Eq.Gain(0, mainEq.Band.Band) + if err != nil { + return fmt.Errorf("failed to get Main Mono EQ band %d gain: %w", mainEq.Band.Band, err) + } + fmt.Fprintf(ctx.Out, "Main Mono EQ band %d gain: %.2f dB\n", mainEq.Band.Band, resp) + return nil + } + + if err := ctx.Client.MainMono.Eq.SetGain(0, mainEq.Band.Band, *cmd.Level); err != nil { + return fmt.Errorf("failed to set Main Mono EQ band %d gain: %w", mainEq.Band.Band, err) + } + fmt.Fprintf(ctx.Out, "Main Mono EQ band %d gain set to: %.2f dB\n", mainEq.Band.Band, *cmd.Level) + return nil +} + +// MainMonoEqBandFreqCmd defines the command for getting or setting the frequency of a specific EQ band on the Main Mono output, allowing users to specify the desired frequency in Hz. +type MainMonoEqBandFreqCmd struct { + Frequency *float64 `arg:"" help:"The frequency to set for the specified EQ band. If not provided, the current frequency will be printed." optional:""` +} + +// Run executes the MainMonoEqBandFreqCmd command, either retrieving the current frequency of a specific EQ band on the Main Mono output or setting it based on the provided argument. +func (cmd *MainMonoEqBandFreqCmd) Run(ctx *context, main *MainCmdGroup, mainEq *MainMonoEqCmdGroup) error { + if cmd.Frequency == nil { + resp, err := ctx.Client.MainMono.Eq.Frequency(0, mainEq.Band.Band) + if err != nil { + return fmt.Errorf("failed to get Main Mono EQ band %d frequency: %w", mainEq.Band.Band, err) + } + fmt.Fprintf(ctx.Out, "Main Mono EQ band %d frequency: %.2f Hz\n", mainEq.Band.Band, resp) + return nil + } + + if err := ctx.Client.MainMono.Eq.SetFrequency(0, mainEq.Band.Band, *cmd.Frequency); err != nil { + return fmt.Errorf("failed to set Main Mono EQ band %d frequency: %w", mainEq.Band.Band, err) + } + fmt.Fprintf(ctx.Out, "Main Mono EQ band %d frequency set to: %.2f Hz\n", mainEq.Band.Band, *cmd.Frequency) + return nil +} + +// MainMonoEqBandQCmd defines the command for getting or setting the Q factor of a specific EQ band on the Main Mono output, allowing users to specify the desired Q factor. +type MainMonoEqBandQCmd struct { + Q *float64 `arg:"" help:"The Q factor to set for the specified EQ band. If not provided, the current Q factor will be printed." optional:""` +} + +// Run executes the MainMonoEqBandQCmd command, either retrieving the current Q factor of a specific EQ band on the Main Mono output or setting it based on the provided argument. +func (cmd *MainMonoEqBandQCmd) Run(ctx *context, main *MainCmdGroup, mainEq *MainMonoEqCmdGroup) error { + if cmd.Q == nil { + resp, err := ctx.Client.MainMono.Eq.Q(0, mainEq.Band.Band) + if err != nil { + return fmt.Errorf("failed to get Main Mono EQ band %d Q factor: %w", mainEq.Band.Band, err) + } + fmt.Fprintf(ctx.Out, "Main Mono EQ band %d Q factor: %.2f\n", mainEq.Band.Band, resp) + return nil + } + + if err := ctx.Client.MainMono.Eq.SetQ(0, mainEq.Band.Band, *cmd.Q); err != nil { + return fmt.Errorf("failed to set Main Mono EQ band %d Q factor: %w", mainEq.Band.Band, err) + } + fmt.Fprintf(ctx.Out, "Main Mono EQ band %d Q factor set to: %.2f\n", mainEq.Band.Band, *cmd.Q) + return nil +} + +// MainMonoEqBandTypeCmd defines the command for getting or setting the type of a specific EQ band on the Main Mono output, allowing users to specify the desired type as "peaking", "low_shelf", "high_shelf", "low_pass", or "high_pass". +type MainMonoEqBandTypeCmd struct { + Type *string `arg:"" help:"The type to set for the specified EQ band. If not provided, the current type will be printed." optional:"" enum:"peaking,low_shelf,high_shelf,low_pass,high_pass"` +} + +// Run executes the MainMonoEqBandTypeCmd command, either retrieving the current type of a specific EQ band on the Main Mono output or setting it based on the provided argument. +func (cmd *MainMonoEqBandTypeCmd) Run(ctx *context, main *MainCmdGroup, mainEq *MainMonoEqCmdGroup) error { + if cmd.Type == nil { + resp, err := ctx.Client.MainMono.Eq.Type(0, mainEq.Band.Band) + if err != nil { + return fmt.Errorf("failed to get Main Mono EQ band %d type: %w", mainEq.Band.Band, err) + } + fmt.Fprintf(ctx.Out, "Main Mono EQ band %d type: %s\n", mainEq.Band.Band, resp) + return nil + } + + if err := ctx.Client.MainMono.Eq.SetType(0, mainEq.Band.Band, *cmd.Type); err != nil { + return fmt.Errorf("failed to set Main Mono EQ band %d type: %w", mainEq.Band.Band, err) + } + fmt.Fprintf(ctx.Out, "Main Mono EQ band %d type set to: %s\n", mainEq.Band.Band, *cmd.Type) + return nil +} + +// MainMonoCompCmdGroup defines the command group for controlling the compressor settings of the Main Mono output, including commands for getting or setting the compressor parameters. +type MainMonoCompCmdGroup struct { + On MainMonoCompOnCmd `help:"Get or set the compressor on/off state of the Main Mono output." cmd:"on"` + Mode MainMonoCompModeCmd `help:"Get or set the compressor mode of the Main Mono output." cmd:"mode"` + Threshold MainMonoCompThresholdCmd `help:"Get or set the compressor threshold of the Main Mono output." cmd:"threshold"` + Ratio MainMonoCompRatioCmd `help:"Get or set the compressor ratio of the Main Mono output." cmd:"ratio"` + Mix MainMonoCompMixCmd `help:"Get or set the compressor mix level of the Main Mono output." cmd:"mix"` + Makeup MainMonoCompMakeupCmd `help:"Get or set the compressor makeup gain of the Main Mono output." cmd:"makeup"` + Attack MainMonoCompAttackCmd `help:"Get or set the compressor attack time of the Main Mono output." cmd:"attack"` + Hold MainMonoCompHoldCmd `help:"Get or set the compressor hold time of the Main Mono output." cmd:"hold"` + Release MainMonoCompReleaseCmd `help:"Get or set the compressor release time of the Main Mono output." cmd:"release"` +} + +// MainMonoCompOnCmd defines the command for getting or setting the compressor on/off state of the Main Mono output, allowing users to specify the desired state as "true"/"on" or "false"/"off". +type MainMonoCompOnCmd struct { + Enable *string `arg:"" help:"The compressor on/off state to set. If not provided, the current state will be printed." optional:"" enum:"true,false"` +} + +// Run executes the MainMonoCompOnCmd command, either retrieving the current compressor on/off state of the Main Mono output or setting it based on the provided argument. +func (cmd *MainMonoCompOnCmd) Run(ctx *context, main *MainCmdGroup) error { + if cmd.Enable == nil { + resp, err := ctx.Client.MainMono.Comp.On(0) + if err != nil { + return fmt.Errorf("failed to get Main Mono compressor on/off state: %w", err) + } + fmt.Fprintf(ctx.Out, "Main Mono compressor on/off state: %t\n", resp) + return nil + } + + if err := ctx.Client.MainMono.Comp.SetOn(0, *cmd.Enable == "true"); err != nil { + return fmt.Errorf("failed to set Main Mono compressor on/off state: %w", err) + } + fmt.Fprintf(ctx.Out, "Main Mono compressor on/off state set to: %t\n", *cmd.Enable == "true") + return nil +} + +// MainMonoCompModeCmd defines the command for getting or setting the compressor mode of the Main Mono output, allowing users to specify the desired mode as "comp" or "exp". +type MainMonoCompModeCmd struct { + Mode *string `arg:"" help:"The compressor mode to set. If not provided, the current mode will be printed." optional:"" enum:"comp,exp"` +} + +// Run executes the MainMonoCompModeCmd command, either retrieving the current compressor mode of the Main Mono output or setting it based on the provided argument. +func (cmd *MainMonoCompModeCmd) Run(ctx *context, main *MainCmdGroup) error { + if cmd.Mode == nil { + resp, err := ctx.Client.MainMono.Comp.Mode(0) + if err != nil { + return fmt.Errorf("failed to get Main Mono compressor mode: %w", err) + } + fmt.Fprintf(ctx.Out, "Main Mono compressor mode: %s\n", resp) + return nil + } + + if err := ctx.Client.MainMono.Comp.SetMode(0, *cmd.Mode); err != nil { + return fmt.Errorf("failed to set Main Mono compressor mode: %w", err) + } + fmt.Fprintf(ctx.Out, "Main Mono compressor mode set to: %s\n", *cmd.Mode) + return nil +} + +// MainMonoCompThresholdCmd defines the command for getting or setting the compressor threshold of the Main Mono output, allowing users to specify the desired threshold in dB. +type MainMonoCompThresholdCmd struct { + Threshold *float64 `arg:"" help:"The compressor threshold to set. If not provided, the current threshold will be printed." optional:""` +} + +// Run executes the MainMonoCompThresholdCmd command, either retrieving the current compressor threshold of the Main Mono output or setting it based on the provided argument. +func (cmd *MainMonoCompThresholdCmd) Run(ctx *context, main *MainCmdGroup) error { + if cmd.Threshold == nil { + resp, err := ctx.Client.MainMono.Comp.Threshold(0) + if err != nil { + return fmt.Errorf("failed to get Main Mono compressor threshold: %w", err) + } + fmt.Fprintf(ctx.Out, "Main Mono compressor threshold: %.2f dB\n", resp) + return nil + } + + if err := ctx.Client.MainMono.Comp.SetThreshold(0, *cmd.Threshold); err != nil { + return fmt.Errorf("failed to set Main Mono compressor threshold: %w", err) + } + fmt.Fprintf(ctx.Out, "Main Mono compressor threshold set to: %.2f dB\n", *cmd.Threshold) + return nil +} + +// MainMonoCompRatioCmd defines the command for getting or setting the compressor ratio of the Main Mono output, allowing users to specify the desired ratio. +type MainMonoCompRatioCmd struct { + Ratio *float64 `arg:"" help:"The compressor ratio to set. If not provided, the current ratio will be printed." optional:""` +} + +// Run executes the MainMonoCompRatioCmd command, either retrieving the current compressor ratio of the Main Mono output or setting it based on the provided argument. +func (cmd *MainMonoCompRatioCmd) Run(ctx *context, main *MainCmdGroup) error { + if cmd.Ratio == nil { + resp, err := ctx.Client.MainMono.Comp.Ratio(0) + if err != nil { + return fmt.Errorf("failed to get Main Mono compressor ratio: %w", err) + } + fmt.Fprintf(ctx.Out, "Main Mono compressor ratio: %.2f\n", resp) + return nil + } + + if err := ctx.Client.MainMono.Comp.SetRatio(0, *cmd.Ratio); err != nil { + return fmt.Errorf("failed to set Main Mono compressor ratio: %w", err) + } + fmt.Fprintf(ctx.Out, "Main Mono compressor ratio set to: %.2f\n", *cmd.Ratio) + return nil +} + +// MainMonoCompMixCmd defines the command for getting or setting the compressor mix level of the Main Mono output, allowing users to specify the desired mix level in percentage. +type MainMonoCompMixCmd struct { + Mix *float64 `arg:"" help:"The compressor mix level to set. If not provided, the current mix level will be printed." optional:""` +} + +// Run executes the MainMonoCompMixCmd command, either retrieving the current compressor mix level of the Main Mono output or setting it based on the provided argument. +func (cmd *MainMonoCompMixCmd) Run(ctx *context, main *MainCmdGroup) error { + if cmd.Mix == nil { + resp, err := ctx.Client.MainMono.Comp.Mix(0) + if err != nil { + return fmt.Errorf("failed to get Main Mono compressor mix level: %w", err) + } + fmt.Fprintf(ctx.Out, "Main Mono compressor mix level: %.2f%%\n", resp) + return nil + } + + if err := ctx.Client.MainMono.Comp.SetMix(0, *cmd.Mix); err != nil { + return fmt.Errorf("failed to set Main Mono compressor mix level: %w", err) + } + fmt.Fprintf(ctx.Out, "Main Mono compressor mix level set to: %.2f%%\n", *cmd.Mix) + return nil +} + +// MainMonoCompMakeupCmd defines the command for getting or setting the compressor makeup gain of the Main Mono output, allowing users to specify the desired makeup gain in dB. +type MainMonoCompMakeupCmd struct { + Makeup *float64 `arg:"" help:"The compressor makeup gain to set. If not provided, the current makeup gain will be printed." optional:""` +} + +// Run executes the MainMonoCompMakeupCmd command, either retrieving the current compressor makeup gain of the Main Mono output or setting it based on the provided argument. +func (cmd *MainMonoCompMakeupCmd) Run(ctx *context, main *MainCmdGroup) error { + if cmd.Makeup == nil { + resp, err := ctx.Client.MainMono.Comp.Makeup(0) + if err != nil { + return fmt.Errorf("failed to get Main Mono compressor makeup gain: %w", err) + } + fmt.Fprintf(ctx.Out, "Main Mono compressor makeup gain: %.2f dB\n", resp) + return nil + } + + if err := ctx.Client.MainMono.Comp.SetMakeup(0, *cmd.Makeup); err != nil { + return fmt.Errorf("failed to set Main Mono compressor makeup gain: %w", err) + } + fmt.Fprintf(ctx.Out, "Main Mono compressor makeup gain set to: %.2f dB\n", *cmd.Makeup) + return nil +} + +// MainMonoCompAttackCmd defines the command for getting or setting the compressor attack time of the Main Mono output, allowing users to specify the desired attack time in milliseconds. +type MainMonoCompAttackCmd struct { + Attack *float64 `arg:"" help:"The compressor attack time to set. If not provided, the current attack time will be printed." optional:""` +} + +// Run executes the MainMonoCompAttackCmd command, either retrieving the current compressor attack time of the Main Mono output or setting it based on the provided argument. +func (cmd *MainMonoCompAttackCmd) Run(ctx *context, main *MainCmdGroup) error { + if cmd.Attack == nil { + resp, err := ctx.Client.MainMono.Comp.Attack(0) + if err != nil { + return fmt.Errorf("failed to get Main Mono compressor attack time: %w", err) + } + fmt.Fprintf(ctx.Out, "Main Mono compressor attack time: %.2f ms\n", resp) + return nil + } + + if err := ctx.Client.MainMono.Comp.SetAttack(0, *cmd.Attack); err != nil { + return fmt.Errorf("failed to set Main Mono compressor attack time: %w", err) + } + fmt.Fprintf(ctx.Out, "Main Mono compressor attack time set to: %.2f ms\n", *cmd.Attack) + return nil +} + +// MainMonoCompHoldCmd defines the command for getting or setting the compressor hold time of the Main Mono output, allowing users to specify the desired hold time in milliseconds. +type MainMonoCompHoldCmd struct { + Hold *float64 `arg:"" help:"The compressor hold time to set. If not provided, the current hold time will be printed." optional:""` +} + +// Run executes the MainMonoCompHoldCmd command, either retrieving the current compressor hold time of the Main Mono output or setting it based on the provided argument. +func (cmd *MainMonoCompHoldCmd) Run(ctx *context, main *MainCmdGroup) error { + if cmd.Hold == nil { + resp, err := ctx.Client.MainMono.Comp.Hold(0) + if err != nil { + return fmt.Errorf("failed to get Main Mono compressor hold time: %w", err) + } + fmt.Fprintf(ctx.Out, "Main Mono compressor hold time: %.2f ms\n", resp) + return nil + } + + if err := ctx.Client.MainMono.Comp.SetHold(0, *cmd.Hold); err != nil { + return fmt.Errorf("failed to set Main Mono compressor hold time: %w", err) + } + fmt.Fprintf(ctx.Out, "Main Mono compressor hold time set to: %.2f ms\n", *cmd.Hold) + return nil +} + +// MainMonoCompReleaseCmd defines the command for getting or setting the compressor release time of the Main Mono output, allowing users to specify the desired release time in milliseconds. +type MainMonoCompReleaseCmd struct { + Release *float64 `arg:"" help:"The compressor release time to set. If not provided, the current release time will be printed." optional:""` +} + +// Run executes the MainMonoCompReleaseCmd command, either retrieving the current compressor release time of the Main Mono output or setting it based on the provided argument. +func (cmd *MainMonoCompReleaseCmd) Run(ctx *context, main *MainCmdGroup) error { + if cmd.Release == nil { + resp, err := ctx.Client.MainMono.Comp.Release(0) + if err != nil { + return fmt.Errorf("failed to get Main Mono compressor release time: %w", err) + } + fmt.Fprintf(ctx.Out, "Main Mono compressor release time: %.2f ms\n", resp) + return nil + } + + if err := ctx.Client.MainMono.Comp.SetRelease(0, *cmd.Release); err != nil { + return fmt.Errorf("failed to set Main Mono compressor release time: %w", err) + } + fmt.Fprintf(ctx.Out, "Main Mono compressor release time set to: %.2f ms\n", *cmd.Release) + return nil +} diff --git a/raw.go b/cmd/x32-cli/raw.go similarity index 100% rename from raw.go rename to cmd/x32-cli/raw.go diff --git a/snapshot.go b/cmd/x32-cli/snapshot.go similarity index 100% rename from snapshot.go rename to cmd/x32-cli/snapshot.go diff --git a/strip.go b/cmd/x32-cli/strip.go similarity index 100% rename from strip.go rename to cmd/x32-cli/strip.go diff --git a/cmd/xair-cli/bus.go b/cmd/xair-cli/bus.go new file mode 100644 index 0000000..cc1db94 --- /dev/null +++ b/cmd/xair-cli/bus.go @@ -0,0 +1,549 @@ +package main + +import ( + "fmt" + "time" + + "github.com/alecthomas/kong" +) + +// BusCmdGroup defines the commands related to controlling the buses of the X-Air device. +type BusCmdGroup struct { + Index struct { + Index int `arg:"" help:"The index of the bus. (1-based indexing)"` + Mute BusMuteCmd ` help:"Get or set the mute state of the bus." cmd:""` + Fader BusFaderCmd ` help:"Get or set the fader level of the bus." cmd:""` + Fadein BusFadeinCmd ` help:"Fade in the bus over a specified duration." cmd:""` + Fadeout BusFadeoutCmd ` help:"Fade out the bus over a specified duration." cmd:""` + Name BusNameCmd ` help:"Get or set the name of the bus." cmd:""` + + Eq BusEqCmdGroup ` help:"Commands related to the bus EQ." cmd:"eq"` + Comp BusCompCmdGroup ` help:"Commands related to the bus compressor." cmd:"comp"` + } `arg:"" help:"Control a specific bus by index."` +} + +// BusMuteCmd defines the command for getting or setting the mute state of a bus. +type BusMuteCmd struct { + State *string `arg:"" help:"The mute state to set (true or false). If not provided, the current mute state will be returned." optional:"" enum:"true,false"` +} + +// Run executes the BusMuteCmd command, either retrieving the current mute state or setting it based on the provided argument. +func (cmd *BusMuteCmd) Run(ctx *context, bus *BusCmdGroup) error { + if cmd.State == nil { + resp, err := ctx.Client.Bus.Mute(bus.Index.Index) + if err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d mute state: %t\n", bus.Index.Index, resp) + return nil + } + + if err := ctx.Client.Bus.SetMute(bus.Index.Index, *cmd.State == "true"); err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d mute state set to: %s\n", bus.Index.Index, *cmd.State) + return nil +} + +// BusFaderCmd defines the command for getting or setting the fader level of a bus. +type BusFaderCmd struct { + Level *float64 `arg:"" help:"The fader level to set (in dB). If not provided, the current fader level will be returned." optional:""` +} + +// Run executes the BusFaderCmd command, either retrieving the current fader level or setting it based on the provided argument. +func (cmd *BusFaderCmd) Run(ctx *context, bus *BusCmdGroup) error { + if cmd.Level == nil { + resp, err := ctx.Client.Bus.Fader(bus.Index.Index) + if err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d fader level: %.2f dB\n", bus.Index.Index, resp) + return nil + } + + if err := ctx.Client.Bus.SetFader(bus.Index.Index, *cmd.Level); err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d fader level set to: %.2f dB\n", bus.Index.Index, *cmd.Level) + return nil +} + +// BusFadeinCmd defines the command for fading in a bus over a specified duration to a target fader level. +type BusFadeinCmd struct { + Duration time.Duration `flag:"" help:"The duration of the fade-in effect." default:"5s"` + Target float64 ` help:"The target fader level (in dB)." default:"0.0" arg:""` +} + +// Run executes the BusFadeinCmd command, gradually increasing the fader level of the bus from its current level to the target level over the specified duration. +func (cmd *BusFadeinCmd) Run(ctx *context, bus *BusCmdGroup) error { + currentLevel, err := ctx.Client.Bus.Fader(bus.Index.Index) + if err != nil { + return fmt.Errorf("failed to get current fader level: %w", err) + } + + if currentLevel >= cmd.Target { + return fmt.Errorf( + "current fader level (%.2f dB) is already at or above the target level (%.2f dB)", + currentLevel, + cmd.Target, + ) + } + + totalSteps := float64(cmd.Target - currentLevel) + stepDuration := time.Duration(cmd.Duration.Seconds()*1000/totalSteps) * time.Millisecond + for currentLevel < cmd.Target { + currentLevel += totalSteps / float64(cmd.Duration.Seconds()*1000/stepDuration.Seconds()) + if currentLevel > cmd.Target { + currentLevel = cmd.Target + } + + if err := ctx.Client.Bus.SetFader(bus.Index.Index, currentLevel); err != nil { + return fmt.Errorf("failed to set fader level: %w", err) + } + time.Sleep(stepDuration) + } + + fmt.Fprintf(ctx.Out, "Bus %d fade-in complete. Final level: %.2f dB\n", bus.Index.Index, cmd.Target) + return nil +} + +// BusFadeoutCmd defines the command for fading out a bus over a specified duration to a target fader level. +type BusFadeoutCmd struct { + Duration time.Duration `flag:"" help:"The duration of the fade-out effect." default:"5s"` + Target float64 ` help:"The target fader level (in dB)." default:"-90.0" arg:""` +} + +// Run executes the BusFadeoutCmd command, gradually decreasing the fader level of the bus from its current level to the target level over the specified duration. +func (cmd *BusFadeoutCmd) Run(ctx *context, bus *BusCmdGroup) error { + currentLevel, err := ctx.Client.Bus.Fader(bus.Index.Index) + if err != nil { + return fmt.Errorf("failed to get current fader level: %w", err) + } + + if currentLevel <= cmd.Target { + return fmt.Errorf( + "current fader level (%.2f dB) is already at or below the target level (%.2f dB)", + currentLevel, + cmd.Target, + ) + } + + totalSteps := float64(currentLevel - cmd.Target) + stepDuration := time.Duration(cmd.Duration.Seconds()*1000/totalSteps) * time.Millisecond + for currentLevel > cmd.Target { + currentLevel -= totalSteps / float64(cmd.Duration.Seconds()*1000/stepDuration.Seconds()) + if currentLevel < cmd.Target { + currentLevel = cmd.Target + } + + if err := ctx.Client.Bus.SetFader(bus.Index.Index, currentLevel); err != nil { + return fmt.Errorf("failed to set fader level: %w", err) + } + time.Sleep(stepDuration) + } + + fmt.Fprintf(ctx.Out, "Bus %d fade-out complete. Final level: %.2f dB\n", bus.Index.Index, cmd.Target) + return nil +} + +// BusNameCmd defines the command for getting or setting the name of a bus. +type BusNameCmd struct { + Name *string `arg:"" help:"The name to set for the bus. If not provided, the current name will be returned." optional:""` +} + +// Run executes the BusNameCmd command, either retrieving the current name of the bus or setting it based on the provided argument. +func (cmd *BusNameCmd) Run(ctx *context, bus *BusCmdGroup) error { + if cmd.Name == nil { + resp, err := ctx.Client.Bus.Name(bus.Index.Index) + if err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d name: %s\n", bus.Index.Index, resp) + return nil + } + + if err := ctx.Client.Bus.SetName(bus.Index.Index, *cmd.Name); err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d name set to: %s\n", bus.Index.Index, *cmd.Name) + return nil +} + +// BusEqCmdGroup defines the commands related to controlling the EQ of a bus. +type BusEqCmdGroup struct { + On BusEqOnCmd `help:"Get or set the EQ on/off state of the bus." cmd:"on"` + Mode BusEqModeCmd `help:"Get or set the EQ mode of the bus (peq, geq or teq)." cmd:"mode"` + Band struct { + Band int `arg:"" help:"The EQ band number."` + Gain BusEqBandGainCmd `help:"Get or set the gain of the EQ band." cmd:"gain"` + Freq BusEqBandFreqCmd `help:"Get or set the frequency of the EQ band." cmd:"freq"` + Q BusEqBandQCmd `help:"Get or set the Q factor of the EQ band." cmd:"q"` + Type BusEqBandTypeCmd `help:"Get or set the type of the EQ band (lcut, lshv, peq, veq, hshv, hcut)." cmd:"type"` + } `help:"Commands for controlling a specific EQ band of the bus." arg:""` +} + +// Validate checks that the provided EQ band number is within the valid range (1-6). +func (cmd *BusEqCmdGroup) Validate(ctx kong.Context) error { + if cmd.Band.Band < 1 || cmd.Band.Band > 6 { + return fmt.Errorf("EQ band number must be between 1 and 6") + } + return nil +} + +// BusCompCmdGroup defines the commands related to controlling the compressor of a bus. +type BusEqOnCmd struct { + State *string `arg:"" help:"The EQ on/off state to set (true or false). If not provided, the current EQ state will be returned." optional:"" enum:"true,false"` +} + +// Run executes the BusEqOnCmd command, either retrieving the current EQ on/off state of the bus or setting it based on the provided argument. +func (cmd *BusEqOnCmd) Run(ctx *context, bus *BusCmdGroup) error { + if cmd.State == nil { + resp, err := ctx.Client.Bus.Eq.On(bus.Index.Index) + if err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d EQ on state: %t\n", bus.Index.Index, resp) + return nil + } + + if err := ctx.Client.Bus.Eq.SetOn(bus.Index.Index, *cmd.State == "true"); err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d EQ on state set to: %s\n", bus.Index.Index, *cmd.State) + return nil +} + +// BusEqModeCmd defines the command for getting or setting the EQ mode of a bus. +type BusEqModeCmd struct { + Mode *string `arg:"" help:"The EQ mode to set (peq, geq or teq). If not provided, the current EQ mode will be returned." optional:"" enum:"peq,geq,teq"` +} + +// Run executes the BusEqModeCmd command, either retrieving the current EQ mode of the bus or setting it based on the provided argument. +func (cmd *BusEqModeCmd) Run(ctx *context, bus *BusCmdGroup) error { + if cmd.Mode == nil { + resp, err := ctx.Client.Bus.Eq.Mode(bus.Index.Index) + if err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d EQ mode: %s\n", bus.Index.Index, resp) + return nil + } + + if err := ctx.Client.Bus.Eq.SetMode(bus.Index.Index, *cmd.Mode); err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d EQ mode set to: %s\n", bus.Index.Index, *cmd.Mode) + return nil +} + +// BusEqBandGainCmd defines the command for getting or setting the gain of a specific EQ band of a bus. +type BusEqBandGainCmd struct { + Gain *float64 `arg:"" help:"The gain to set for the EQ band (in dB). If not provided, the current gain will be returned." optional:""` +} + +// Run executes the BusEqBandGainCmd command, either retrieving the current gain of the specified EQ band of the bus or setting it based on the provided argument. +func (cmd *BusEqBandGainCmd) Run(ctx *context, bus *BusCmdGroup, busEq *BusEqCmdGroup) error { + if cmd.Gain == nil { + resp, err := ctx.Client.Bus.Eq.Gain(bus.Index.Index, busEq.Band.Band) + if err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d EQ band %d gain: %.2f dB\n", bus.Index.Index, busEq.Band.Band, resp) + return nil + } + + if err := ctx.Client.Bus.Eq.SetGain(bus.Index.Index, busEq.Band.Band, *cmd.Gain); err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d EQ band %d gain set to: %.2f dB\n", bus.Index.Index, busEq.Band.Band, *cmd.Gain) + return nil +} + +// BusEqBandFreqCmd defines the command for getting or setting the frequency of a specific EQ band of a bus. +type BusEqBandFreqCmd struct { + Freq *float64 `arg:"" help:"The frequency to set for the EQ band (in Hz). If not provided, the current frequency will be returned." optional:""` +} + +// Run executes the BusEqBandFreqCmd command, either retrieving the current frequency of the specified EQ band of the bus or setting it based on the provided argument. +func (cmd *BusEqBandFreqCmd) Run(ctx *context, bus *BusCmdGroup, busEq *BusEqCmdGroup) error { + if cmd.Freq == nil { + resp, err := ctx.Client.Bus.Eq.Frequency(bus.Index.Index, busEq.Band.Band) + if err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d EQ band %d frequency: %.2f Hz\n", bus.Index.Index, busEq.Band.Band, resp) + return nil + } + + if err := ctx.Client.Bus.Eq.SetFrequency(bus.Index.Index, busEq.Band.Band, *cmd.Freq); err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d EQ band %d frequency set to: %.2f Hz\n", bus.Index.Index, busEq.Band.Band, *cmd.Freq) + return nil +} + +// BusEqBandQCmd defines the command for getting or setting the Q factor of a specific EQ band of a bus. +type BusEqBandQCmd struct { + Q *float64 `arg:"" help:"The Q factor to set for the EQ band. If not provided, the current Q factor will be returned." optional:""` +} + +// Run executes the BusEqBandQCmd command, either retrieving the current Q factor of the specified EQ band of the bus or setting it based on the provided argument. +func (cmd *BusEqBandQCmd) Run(ctx *context, bus *BusCmdGroup, busEq *BusEqCmdGroup) error { + if cmd.Q == nil { + resp, err := ctx.Client.Bus.Eq.Q(bus.Index.Index, busEq.Band.Band) + if err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d EQ band %d Q factor: %.2f\n", bus.Index.Index, busEq.Band.Band, resp) + return nil + } + + if err := ctx.Client.Bus.Eq.SetQ(bus.Index.Index, busEq.Band.Band, *cmd.Q); err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d EQ band %d Q factor set to: %.2f\n", bus.Index.Index, busEq.Band.Band, *cmd.Q) + return nil +} + +// BusEqBandTypeCmd defines the command for getting or setting the type of a specific EQ band of a bus. +type BusEqBandTypeCmd struct { + Type *string `arg:"" help:"The type to set for the EQ band (lcut, lshv, peq, veq, hshv, hcut). If not provided, the current type will be returned." optional:"" enum:"lcut,lshv,peq,veq,hshv,hcut"` +} + +// Run executes the BusEqBandTypeCmd command, either retrieving the current type of the specified EQ band of the bus or setting it based on the provided argument. +func (cmd *BusEqBandTypeCmd) Run(ctx *context, bus *BusCmdGroup, busEq *BusEqCmdGroup) error { + if cmd.Type == nil { + resp, err := ctx.Client.Bus.Eq.Type(bus.Index.Index, busEq.Band.Band) + if err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d EQ band %d type: %s\n", bus.Index.Index, busEq.Band.Band, resp) + return nil + } + + if err := ctx.Client.Bus.Eq.SetType(bus.Index.Index, busEq.Band.Band, *cmd.Type); err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d EQ band %d type set to: %s\n", bus.Index.Index, busEq.Band.Band, *cmd.Type) + return nil +} + +// BusCompCmdGroup defines the commands related to controlling the compressor of a bus. +type BusCompCmdGroup struct { + On BusCompOnCmd `help:"Get or set the compressor on/off state of the bus." cmd:"on"` + Mode BusCompModeCmd `help:"Get or set the compressor mode of the bus (comp, exp)." cmd:"mode"` + Threshold BusCompThresholdCmd `help:"Get or set the compressor threshold of the bus (in dB)." cmd:"threshold"` + Ratio BusCompRatioCmd `help:"Get or set the compressor ratio of the bus." cmd:"ratio"` + Mix BusCompMixCmd `help:"Get or set the compressor mix level of the bus (in %)." cmd:"mix"` + Makeup BusCompMakeupCmd `help:"Get or set the compressor makeup gain of the bus (in dB)." cmd:"makeup"` + Attack BusCompAttackCmd `help:"Get or set the compressor attack time of the bus (in ms)." cmd:"attack"` + Hold BusCompHoldCmd `help:"Get or set the compressor hold time of the bus (in ms)." cmd:"hold"` + Release BusCompReleaseCmd `help:"Get or set the compressor release time of the bus (in ms)." cmd:"release"` +} + +// BusCompOnCmd defines the command for getting or setting the compressor on/off state of a bus. +type BusCompOnCmd struct { + State *string `arg:"" help:"The compressor on/off state to set (true or false). If not provided, the current compressor state will be returned." optional:"" enum:"true,false"` +} + +// Run executes the BusCompOnCmd command, either retrieving the current compressor on/off state of the bus or setting it based on the provided argument. +func (cmd *BusCompOnCmd) Run(ctx *context, bus *BusCmdGroup) error { + if cmd.State == nil { + resp, err := ctx.Client.Bus.Comp.On(bus.Index.Index) + if err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d compressor on state: %t\n", bus.Index.Index, resp) + return nil + } + + if err := ctx.Client.Bus.Comp.SetOn(bus.Index.Index, *cmd.State == "true"); err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d compressor on state set to: %s\n", bus.Index.Index, *cmd.State) + return nil +} + +// BusCompModeCmd defines the command for getting or setting the compressor mode of a bus. +type BusCompModeCmd struct { + Mode *string `arg:"" help:"The compressor mode to set (comp, exp). If not provided, the current compressor mode will be returned." optional:"" enum:"comp,exp"` +} + +// Run executes the BusCompModeCmd command, either retrieving the current compressor mode of the bus or setting it based on the provided argument. +func (cmd *BusCompModeCmd) Run(ctx *context, bus *BusCmdGroup) error { + if cmd.Mode == nil { + resp, err := ctx.Client.Bus.Comp.Mode(bus.Index.Index) + if err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d compressor mode: %s\n", bus.Index.Index, resp) + return nil + } + + if err := ctx.Client.Bus.Comp.SetMode(bus.Index.Index, *cmd.Mode); err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d compressor mode set to: %s\n", bus.Index.Index, *cmd.Mode) + return nil +} + +// BusCompThresholdCmd defines the command for getting or setting the compressor threshold of a bus. +type BusCompThresholdCmd struct { + Threshold *float64 `arg:"" help:"The compressor threshold to set (in dB). If not provided, the current compressor threshold will be returned." optional:""` +} + +// Run executes the BusCompThresholdCmd command, either retrieving the current compressor threshold of the bus or setting it based on the provided argument. +func (cmd *BusCompThresholdCmd) Run(ctx *context, bus *BusCmdGroup) error { + if cmd.Threshold == nil { + resp, err := ctx.Client.Bus.Comp.Threshold(bus.Index.Index) + if err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d compressor threshold: %.2f dB\n", bus.Index.Index, resp) + return nil + } + + if err := ctx.Client.Bus.Comp.SetThreshold(bus.Index.Index, *cmd.Threshold); err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d compressor threshold set to: %.2f dB\n", bus.Index.Index, *cmd.Threshold) + return nil +} + +// BusCompRatioCmd defines the command for getting or setting the compressor ratio of a bus. +type BusCompRatioCmd struct { + Ratio *float64 `arg:"" help:"The compressor ratio to set. If not provided, the current compressor ratio will be returned." optional:""` +} + +// Run executes the BusCompRatioCmd command, either retrieving the current compressor ratio of the bus or setting it based on the provided argument. +func (cmd *BusCompRatioCmd) Run(ctx *context, bus *BusCmdGroup) error { + if cmd.Ratio == nil { + resp, err := ctx.Client.Bus.Comp.Ratio(bus.Index.Index) + if err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d compressor ratio: %.2f\n", bus.Index.Index, resp) + return nil + } + + if err := ctx.Client.Bus.Comp.SetRatio(bus.Index.Index, *cmd.Ratio); err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d compressor ratio set to: %.2f\n", bus.Index.Index, *cmd.Ratio) + return nil +} + +// BusCompMixCmd defines the command for getting or setting the compressor mix level of a bus. +type BusCompMixCmd struct { + Mix *float64 `arg:"" help:"The compressor mix level to set (in %). If not provided, the current compressor mix level will be returned." optional:""` +} + +// Run executes the BusCompMixCmd command, either retrieving the current compressor mix level of the bus or setting it based on the provided argument. +func (cmd *BusCompMixCmd) Run(ctx *context, bus *BusCmdGroup) error { + if cmd.Mix == nil { + resp, err := ctx.Client.Bus.Comp.Mix(bus.Index.Index) + if err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d compressor mix level: %.2f%%\n", bus.Index.Index, resp) + return nil + } + + if err := ctx.Client.Bus.Comp.SetMix(bus.Index.Index, *cmd.Mix); err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d compressor mix level set to: %.2f%%\n", bus.Index.Index, *cmd.Mix) + return nil +} + +// BusCompMakeupCmd defines the command for getting or setting the compressor makeup gain of a bus. +type BusCompMakeupCmd struct { + Makeup *float64 `arg:"" help:"The compressor makeup gain to set (in dB). If not provided, the current compressor makeup gain will be returned." optional:""` +} + +// Run executes the BusCompMakeupCmd command, either retrieving the current compressor makeup gain of the bus or setting it based on the provided argument. +func (cmd *BusCompMakeupCmd) Run(ctx *context, bus *BusCmdGroup) error { + if cmd.Makeup == nil { + resp, err := ctx.Client.Bus.Comp.Makeup(bus.Index.Index) + if err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d compressor makeup gain: %.2f dB\n", bus.Index.Index, resp) + return nil + } + + if err := ctx.Client.Bus.Comp.SetMakeup(bus.Index.Index, *cmd.Makeup); err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d compressor makeup gain set to: %.2f dB\n", bus.Index.Index, *cmd.Makeup) + return nil +} + +// BusCompAttackCmd defines the command for getting or setting the compressor attack time of a bus. +type BusCompAttackCmd struct { + Attack *float64 `arg:"" help:"The compressor attack time to set (in ms). If not provided, the current compressor attack time will be returned." optional:""` +} + +// Run executes the BusCompAttackCmd command, either retrieving the current compressor attack time of the bus or setting it based on the provided argument. +func (cmd *BusCompAttackCmd) Run(ctx *context, bus *BusCmdGroup) error { + if cmd.Attack == nil { + resp, err := ctx.Client.Bus.Comp.Attack(bus.Index.Index) + if err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d compressor attack time: %.2f ms\n", bus.Index.Index, resp) + return nil + } + + if err := ctx.Client.Bus.Comp.SetAttack(bus.Index.Index, *cmd.Attack); err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d compressor attack time set to: %.2f ms\n", bus.Index.Index, *cmd.Attack) + return nil +} + +// BusCompHoldCmd defines the command for getting or setting the compressor hold time of a bus. +type BusCompHoldCmd struct { + Hold *float64 `arg:"" help:"The compressor hold time to set (in ms). If not provided, the current compressor hold time will be returned." optional:""` +} + +// Run executes the BusCompHoldCmd command, either retrieving the current compressor hold time of the bus or setting it based on the provided argument. +func (cmd *BusCompHoldCmd) Run(ctx *context, bus *BusCmdGroup) error { + if cmd.Hold == nil { + resp, err := ctx.Client.Bus.Comp.Hold(bus.Index.Index) + if err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d compressor hold time: %.2f ms\n", bus.Index.Index, resp) + return nil + } + + if err := ctx.Client.Bus.Comp.SetHold(bus.Index.Index, *cmd.Hold); err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d compressor hold time set to: %.2f ms\n", bus.Index.Index, *cmd.Hold) + return nil +} + +// BusCompReleaseCmd defines the command for getting or setting the compressor release time of a bus. +type BusCompReleaseCmd struct { + Release *float64 `arg:"" help:"The compressor release time to set (in ms). If not provided, the current compressor release time will be returned." optional:""` +} + +// Run executes the BusCompReleaseCmd command, either retrieving the current compressor release time of the bus or setting it based on the provided argument. +func (cmd *BusCompReleaseCmd) Run(ctx *context, bus *BusCmdGroup) error { + if cmd.Release == nil { + resp, err := ctx.Client.Bus.Comp.Release(bus.Index.Index) + if err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d compressor release time: %.2f ms\n", bus.Index.Index, resp) + return nil + } + + if err := ctx.Client.Bus.Comp.SetRelease(bus.Index.Index, *cmd.Release); err != nil { + return err + } + fmt.Fprintf(ctx.Out, "Bus %d compressor release time set to: %.2f ms\n", bus.Index.Index, *cmd.Release) + return nil +} diff --git a/cli.go b/cmd/xair-cli/cli.go similarity index 93% rename from cli.go rename to cmd/xair-cli/cli.go index 7cc72d6..b7fbcf7 100644 --- a/cli.go +++ b/cmd/xair-cli/cli.go @@ -28,14 +28,13 @@ func (v VersionFlag) BeforeApply(app *kong.Kong, vars kong.Vars) error { // noli } type context struct { - Client *xair.Client + Client *xair.XAirClient Out io.Writer } type Config struct { Host string `default:"mixer.local" help:"The host of the X-Air device." env:"XAIR_CLI_HOST" short:"H"` Port int `default:"10024" help:"The port of the X-Air device." env:"XAIR_CLI_PORT" short:"P"` - Kind string `default:"xair" help:"The kind of the X-Air device." env:"XAIR_CLI_KIND" short:"K" enum:"xair,x32"` Timeout time.Duration `default:"100ms" help:"Timeout for OSC operations." env:"XAIR_CLI_TIMEOUT" short:"T"` Loglevel string `default:"warn" help:"Log level for the CLI." env:"XAIR_CLI_LOGLEVEL" short:"L" enum:"debug,info,warn,error,fatal"` } @@ -116,11 +115,11 @@ func run(ctx *kong.Context, config Config) error { } // connect creates a new X-Air client based on the provided configuration. -func connect(config Config) (*xair.Client, error) { - client, err := xair.NewClient( +func connect(config Config) (*xair.XAirClient, error) { + client, err := xair.NewXAirClient( config.Host, config.Port, - xair.WithKind(config.Kind), + xair.WithKind("xair"), xair.WithTimeout(config.Timeout), ) if err != nil { diff --git a/cmd/xair-cli/headamp.go b/cmd/xair-cli/headamp.go new file mode 100644 index 0000000..6b5ece5 --- /dev/null +++ b/cmd/xair-cli/headamp.go @@ -0,0 +1,130 @@ +package main + +import ( + "fmt" + "time" + + "github.com/charmbracelet/log" +) + +// HeadampCmdGroup defines the command group for controlling input gain and phantom power of a headamp, allowing users to specify the index of the headamp they want to control. +type HeadampCmdGroup struct { + Index struct { + Index int `arg:"" help:"The index of the headamp."` + Gain HeadampGainCmd `help:"Get or set the gain of the headamp." cmd:""` + Phantom HeadampPhantomCmd `help:"Get or set the phantom power state of the headamp." cmd:""` + } `arg:"" help:"Control a specific headamp by index."` +} + +// HeadampGainCmd defines the command for getting or setting the gain of a headamp, allowing users to specify the gain in dB and an optional duration for a gradual fade when setting the gain. +type HeadampGainCmd struct { + Duration time.Duration `help:"The duration of the fade in/out when setting the gain." default:"5s"` + Gain *float64 `help:"The gain of the headamp in dB." arg:"" optional:""` +} + +// Run executes the HeadampGainCmd command, either retrieving the current gain of the headamp or setting it based on the provided argument, with an optional fade duration for smooth transitions. +func (cmd *HeadampGainCmd) Run(ctx *context, headamp *HeadampCmdGroup) error { + if cmd.Gain == nil { + resp, err := ctx.Client.HeadAmp.Gain(headamp.Index.Index) + if err != nil { + return fmt.Errorf("failed to get headamp gain: %w", err) + } + fmt.Fprintf(ctx.Out, "Headamp %d gain: %.2f dB\n", headamp.Index.Index, resp) + return nil + } + + currentGain, err := ctx.Client.HeadAmp.Gain(headamp.Index.Index) + if err != nil { + return fmt.Errorf("failed to get current headamp gain: %w", err) + } + + if err := gradualGainAdjust(ctx, headamp.Index.Index, currentGain, *cmd.Gain, cmd.Duration); err != nil { + return fmt.Errorf("failed to set headamp gain: %w", err) + } + fmt.Fprintf(ctx.Out, "Headamp %d gain set to: %.2f dB\n", headamp.Index.Index, *cmd.Gain) + return nil +} + +// gradualGainAdjust gradually adjusts gain from current to target over specified duration +func gradualGainAdjust( + ctx *context, + index int, + currentGain, targetGain float64, + duration time.Duration, +) error { + gainDiff := targetGain - currentGain + + stepInterval := 100 * time.Millisecond + totalSteps := int(duration / stepInterval) + + if totalSteps < 1 { + totalSteps = 1 + stepInterval = duration + } + + stepIncrement := gainDiff / float64(totalSteps) + + log.Debugf("Adjusting Headamp %d gain from %.2f dB to %.2f dB over %v...\n", + index, currentGain, targetGain, duration) + + for step := 1; step <= totalSteps; step++ { + newGain := currentGain + (stepIncrement * float64(step)) + + if step == totalSteps { + newGain = targetGain + } + + err := ctx.Client.HeadAmp.SetGain(index, newGain) + if err != nil { + return err + } + + if step%10 == 0 || step == totalSteps { + log.Debugf(" Step %d/%d: %.2f dB\n", step, totalSteps, newGain) + } + + if step < totalSteps { + time.Sleep(stepInterval) + } + } + + return nil +} + +// HeadampPhantomCmd defines the command for getting or setting the phantom power state of a headamp, allowing users to specify the desired state as "true"/"on" or "false"/"off". +type HeadampPhantomCmd struct { + State *string `help:"The phantom power state of the headamp." arg:"" enum:"true,on,false,off" optional:""` +} + +// Validate checks if the provided phantom power state is valid and normalizes it to "true" or "false". +func (cmd *HeadampPhantomCmd) Validate() error { + if cmd.State != nil { + switch *cmd.State { + case "true", "on": + *cmd.State = "true" + case "false", "off": + *cmd.State = "false" + default: + return fmt.Errorf("invalid phantom power state: %s", *cmd.State) + } + } + return nil +} + +// Run executes the HeadampPhantomCmd command, either retrieving the current phantom power state of the headamp or setting it based on the provided argument. +func (cmd *HeadampPhantomCmd) Run(ctx *context, headamp *HeadampCmdGroup) error { + if cmd.State == nil { + resp, err := ctx.Client.HeadAmp.PhantomPower(headamp.Index.Index) + if err != nil { + return fmt.Errorf("failed to get headamp phantom power state: %w", err) + } + fmt.Fprintf(ctx.Out, "Headamp %d phantom power: %t\n", headamp.Index.Index, resp) + return nil + } + + if err := ctx.Client.HeadAmp.SetPhantomPower(headamp.Index.Index, *cmd.State == "true"); err != nil { + return fmt.Errorf("failed to set headamp phantom power state: %w", err) + } + fmt.Fprintf(ctx.Out, "Headamp %d phantom power set to: %s\n", headamp.Index.Index, *cmd.State) + return nil +} diff --git a/cmd/xair-cli/main.go b/cmd/xair-cli/main.go new file mode 100644 index 0000000..864ca77 --- /dev/null +++ b/cmd/xair-cli/main.go @@ -0,0 +1,489 @@ +package main + +import ( + "fmt" + "time" + + "github.com/alecthomas/kong" +) + +// MainCmdGroup defines the command group for controlling the Main L/R output, including commands for mute state, fader level, and fade-in/fade-out times. +type MainCmdGroup struct { + Mute MainMuteCmd `help:"Get or set the mute state of the Main L/R output." cmd:""` + + Fader MainFaderCmd `help:"Get or set the fader level of the Main L/R output." cmd:""` + Fadein MainFadeinCmd `help:"Fade in the Main L/R output over a specified duration." cmd:""` + Fadeout MainFadeoutCmd `help:"Fade out the Main L/R output over a specified duration." cmd:""` + + Eq MainEqCmdGroup `help:"Commands for controlling the equalizer settings of the Main L/R output." cmd:"eq"` + Comp MainCompCmdGroup `help:"Commands for controlling the compressor settings of the Main L/R output." cmd:"comp"` +} + +// MainMuteCmd defines the command for getting or setting the mute state of the Main L/R output, allowing users to specify the desired state as "true"/"on" or "false"/"off". +type MainMuteCmd struct { + Mute *string `arg:"" help:"The mute state to set. If not provided, the current state will be printed." optional:"" enum:"true,false"` +} + +// Run executes the MainMuteCmd command, either retrieving the current mute state of the Main L/R output or setting it based on the provided argument. +func (cmd *MainMuteCmd) Run(ctx *context) error { + if cmd.Mute == nil { + resp, err := ctx.Client.Main.Mute() + if err != nil { + return fmt.Errorf("failed to get Main L/R mute state: %w", err) + } + fmt.Fprintf(ctx.Out, "Main L/R mute state: %t\n", resp) + return nil + } + + if err := ctx.Client.Main.SetMute(*cmd.Mute == "true"); err != nil { + return fmt.Errorf("failed to set Main L/R mute state: %w", err) + } + fmt.Fprintf(ctx.Out, "Main L/R mute state set to: %s\n", *cmd.Mute) + return nil +} + +// MainFaderCmd defines the command for getting or setting the fader level of the Main L/R output, allowing users to specify the desired level in dB. +type MainFaderCmd struct { + Level *float64 `arg:"" help:"The fader level to set. If not provided, the current level will be printed." optional:""` +} + +// Run executes the MainFaderCmd command, either retrieving the current fader level of the Main L/R output or setting it based on the provided argument. +func (cmd *MainFaderCmd) Run(ctx *context) error { + if cmd.Level == nil { + resp, err := ctx.Client.Main.Fader() + if err != nil { + return fmt.Errorf("failed to get Main L/R fader level: %w", err) + } + fmt.Fprintf(ctx.Out, "Main L/R fader level: %.2f\n", resp) + return nil + } + + if err := ctx.Client.Main.SetFader(*cmd.Level); err != nil { + return fmt.Errorf("failed to set Main L/R fader level: %w", err) + } + fmt.Fprintf(ctx.Out, "Main L/R fader level set to: %.2f\n", *cmd.Level) + return nil +} + +// MainFadeinCmd defines the command for getting or setting the fade-in time of the Main L/R output, allowing users to specify the desired duration for the fade-in effect. +type MainFadeinCmd struct { + Duration time.Duration `flag:"" help:"The duration of the fade-in. (in seconds.)" default:"5s"` + Target float64 ` help:"The target level for the fade-in. If not provided, the current target level will be printed." default:"0.0" arg:""` +} + +// Run executes the MainFadeinCmd command, either retrieving the current fade-in time of the Main L/R output or setting it based on the provided argument, with an optional target level for the fade-in effect. +func (cmd *MainFadeinCmd) Run(ctx *context) error { + currentLevel, err := ctx.Client.Main.Fader() + if err != nil { + return fmt.Errorf("failed to get Main L/R fader level: %w", err) + } + + if currentLevel >= cmd.Target { + return fmt.Errorf( + "current fader level (%.2f) is already at or above the target level (%.2f)", + currentLevel, + cmd.Target, + ) + } + + totalSteps := float64(cmd.Target - currentLevel) + stepDuration := time.Duration(cmd.Duration.Seconds()*1000/totalSteps) * time.Millisecond + for currentLevel < cmd.Target { + currentLevel++ + if err := ctx.Client.Main.SetFader(currentLevel); err != nil { + return fmt.Errorf("failed to set Main L/R fader level: %w", err) + } + time.Sleep(stepDuration) + } + fmt.Fprintf(ctx.Out, "Main L/R fade-in completed. Final level: %.2f\n", currentLevel) + return nil +} + +// MainFadeoutCmd defines the command for getting or setting the fade-out time of the Main L/R output, allowing users to specify the desired duration for the fade-out effect and an optional target level to fade out to. +type MainFadeoutCmd struct { + Duration time.Duration `flag:"" help:"The duration of the fade-out. (in seconds.)" default:"5s"` + Target float64 ` help:"The target level for the fade-out. If not provided, the current target level will be printed." default:"-90.0" arg:""` +} + +// Run executes the MainFadeoutCmd command, either retrieving the current fade-out time of the Main L/R output or setting it based on the provided argument, with an optional target level for the fade-out effect. +func (cmd *MainFadeoutCmd) Run(ctx *context) error { + currentLevel, err := ctx.Client.Main.Fader() + if err != nil { + return fmt.Errorf("failed to get Main L/R fader level: %w", err) + } + + if currentLevel <= cmd.Target { + return fmt.Errorf( + "current fader level (%.2f) is already at or below the target level (%.2f)", + currentLevel, + cmd.Target, + ) + } + + totalSteps := float64(currentLevel - cmd.Target) + stepDuration := time.Duration(cmd.Duration.Seconds()*1000/totalSteps) * time.Millisecond + for currentLevel > cmd.Target { + currentLevel-- + if err := ctx.Client.Main.SetFader(currentLevel); err != nil { + return fmt.Errorf("failed to set Main L/R fader level: %w", err) + } + time.Sleep(stepDuration) + } + fmt.Fprintf(ctx.Out, "Main L/R fade-out completed. Final level: %.2f\n", currentLevel) + return nil +} + +// MainEqCmdGroup defines the command group for controlling the equalizer settings of the Main L/R output, including commands for getting or setting the EQ parameters. +type MainEqCmdGroup struct { + On MainEqOnCmd `help:"Get or set the EQ on/off state of the Main L/R output." cmd:"on"` + Band struct { + Band int `arg:"" help:"The EQ band number."` + Gain MainEqBandGainCmd `help:"Get or set the gain of the specified EQ band." cmd:"gain"` + Freq MainEqBandFreqCmd `help:"Get or set the frequency of the specified EQ band." cmd:"freq"` + Q MainEqBandQCmd `help:"Get or set the Q factor of the specified EQ band." cmd:"q"` + Type MainEqBandTypeCmd `help:"Get or set the type of the specified EQ band." cmd:"type"` + } `help:"Commands for controlling individual EQ bands of the Main L/R output." arg:""` +} + +// Validate checks if the provided EQ band number is within the valid range (1-6) for the Main L/R output. +func (cmd *MainEqCmdGroup) Validate(ctx kong.Context) error { + if cmd.Band.Band < 1 || cmd.Band.Band > 6 { + return fmt.Errorf("invalid EQ band number: %d. Valid range is 1-6", cmd.Band.Band) + } + return nil +} + +// MainEqOnCmd defines the command for getting or setting the EQ on/off state of the Main L/R output, allowing users to specify the desired state as "true"/"on" or "false"/"off". +type MainEqOnCmd struct { + Enable *string `arg:"" help:"The EQ on/off state to set. If not provided, the current state will be printed." optional:"" enum:"true,false"` +} + +// Run executes the MainEqOnCmd command, either retrieving the current EQ on/off state of the Main L/R output or setting it based on the provided argument. +func (cmd *MainEqOnCmd) Run(ctx *context, main *MainCmdGroup) error { + if cmd.Enable == nil { + resp, err := ctx.Client.Main.Eq.On(0) + if err != nil { + return fmt.Errorf("failed to get Main L/R EQ on/off state: %w", err) + } + fmt.Fprintf(ctx.Out, "Main L/R EQ on/off state: %t\n", resp) + return nil + } + + if err := ctx.Client.Main.Eq.SetOn(0, *cmd.Enable == "true"); err != nil { + return fmt.Errorf("failed to set Main L/R EQ on/off state: %w", err) + } + fmt.Fprintf(ctx.Out, "Main L/R EQ on/off state set to: %t\n", *cmd.Enable == "true") + return nil +} + +// MainEqBandGainCmd defines the command for getting or setting the gain of a specific EQ band on the Main L/R output, allowing users to specify the desired gain in dB. +type MainEqBandGainCmd struct { + Level *float64 `arg:"" help:"The gain level to set for the specified EQ band. If not provided, the current gain will be printed." optional:""` +} + +// Run executes the MainEqBandGainCmd command, either retrieving the current gain of a specific EQ band on the Main L/R output or setting it based on the provided argument. +func (cmd *MainEqBandGainCmd) Run(ctx *context, main *MainCmdGroup, mainEq *MainEqCmdGroup) error { + if cmd.Level == nil { + resp, err := ctx.Client.Main.Eq.Gain(0, mainEq.Band.Band) + if err != nil { + return fmt.Errorf("failed to get Main L/R EQ band %d gain: %w", mainEq.Band.Band, err) + } + fmt.Fprintf(ctx.Out, "Main L/R EQ band %d gain: %.2f dB\n", mainEq.Band.Band, resp) + return nil + } + + if err := ctx.Client.Main.Eq.SetGain(0, mainEq.Band.Band, *cmd.Level); err != nil { + return fmt.Errorf("failed to set Main L/R EQ band %d gain: %w", mainEq.Band.Band, err) + } + fmt.Fprintf(ctx.Out, "Main L/R EQ band %d gain set to: %.2f dB\n", mainEq.Band.Band, *cmd.Level) + return nil +} + +// MainEqBandFreqCmd defines the command for getting or setting the frequency of a specific EQ band on the Main L/R output, allowing users to specify the desired frequency in Hz. +type MainEqBandFreqCmd struct { + Frequency *float64 `arg:"" help:"The frequency to set for the specified EQ band. If not provided, the current frequency will be printed." optional:""` +} + +// Run executes the MainEqBandFreqCmd command, either retrieving the current frequency of a specific EQ band on the Main L/R output or setting it based on the provided argument. +func (cmd *MainEqBandFreqCmd) Run(ctx *context, main *MainCmdGroup, mainEq *MainEqCmdGroup) error { + if cmd.Frequency == nil { + resp, err := ctx.Client.Main.Eq.Frequency(0, mainEq.Band.Band) + if err != nil { + return fmt.Errorf("failed to get Main L/R EQ band %d frequency: %w", mainEq.Band.Band, err) + } + fmt.Fprintf(ctx.Out, "Main L/R EQ band %d frequency: %.2f Hz\n", mainEq.Band.Band, resp) + return nil + } + + if err := ctx.Client.Main.Eq.SetFrequency(0, mainEq.Band.Band, *cmd.Frequency); err != nil { + return fmt.Errorf("failed to set Main L/R EQ band %d frequency: %w", mainEq.Band.Band, err) + } + fmt.Fprintf(ctx.Out, "Main L/R EQ band %d frequency set to: %.2f Hz\n", mainEq.Band.Band, *cmd.Frequency) + return nil +} + +// MainEqBandQCmd defines the command for getting or setting the Q factor of a specific EQ band on the Main L/R output, allowing users to specify the desired Q factor. +type MainEqBandQCmd struct { + Q *float64 `arg:"" help:"The Q factor to set for the specified EQ band. If not provided, the current Q factor will be printed." optional:""` +} + +// Run executes the MainEqBandQCmd command, either retrieving the current Q factor of a specific EQ band on the Main L/R output or setting it based on the provided argument. +func (cmd *MainEqBandQCmd) Run(ctx *context, main *MainCmdGroup, mainEq *MainEqCmdGroup) error { + if cmd.Q == nil { + resp, err := ctx.Client.Main.Eq.Q(0, mainEq.Band.Band) + if err != nil { + return fmt.Errorf("failed to get Main L/R EQ band %d Q factor: %w", mainEq.Band.Band, err) + } + fmt.Fprintf(ctx.Out, "Main L/R EQ band %d Q factor: %.2f\n", mainEq.Band.Band, resp) + return nil + } + + if err := ctx.Client.Main.Eq.SetQ(0, mainEq.Band.Band, *cmd.Q); err != nil { + return fmt.Errorf("failed to set Main L/R EQ band %d Q factor: %w", mainEq.Band.Band, err) + } + fmt.Fprintf(ctx.Out, "Main L/R EQ band %d Q factor set to: %.2f\n", mainEq.Band.Band, *cmd.Q) + return nil +} + +// MainEqBandTypeCmd defines the command for getting or setting the type of a specific EQ band on the Main L/R output, allowing users to specify the desired type as "peaking", "low_shelf", "high_shelf", "low_pass", or "high_pass". +type MainEqBandTypeCmd struct { + Type *string `arg:"" help:"The type to set for the specified EQ band. If not provided, the current type will be printed." optional:"" enum:"peaking,low_shelf,high_shelf,low_pass,high_pass"` +} + +// Run executes the MainEqBandTypeCmd command, either retrieving the current type of a specific EQ band on the Main L/R output or setting it based on the provided argument. +func (cmd *MainEqBandTypeCmd) Run(ctx *context, main *MainCmdGroup, mainEq *MainEqCmdGroup) error { + if cmd.Type == nil { + resp, err := ctx.Client.Main.Eq.Type(0, mainEq.Band.Band) + if err != nil { + return fmt.Errorf("failed to get Main L/R EQ band %d type: %w", mainEq.Band.Band, err) + } + fmt.Fprintf(ctx.Out, "Main L/R EQ band %d type: %s\n", mainEq.Band.Band, resp) + return nil + } + + if err := ctx.Client.Main.Eq.SetType(0, mainEq.Band.Band, *cmd.Type); err != nil { + return fmt.Errorf("failed to set Main L/R EQ band %d type: %w", mainEq.Band.Band, err) + } + fmt.Fprintf(ctx.Out, "Main L/R EQ band %d type set to: %s\n", mainEq.Band.Band, *cmd.Type) + return nil +} + +// MainCompCmdGroup defines the command group for controlling the compressor settings of the Main L/R output, including commands for getting or setting the compressor parameters. +type MainCompCmdGroup struct { + On MainCompOnCmd `help:"Get or set the compressor on/off state of the Main L/R output." cmd:"on"` + Mode MainCompModeCmd `help:"Get or set the compressor mode of the Main L/R output." cmd:"mode"` + Threshold MainCompThresholdCmd `help:"Get or set the compressor threshold of the Main L/R output." cmd:"threshold"` + Ratio MainCompRatioCmd `help:"Get or set the compressor ratio of the Main L/R output." cmd:"ratio"` + Mix MainCompMixCmd `help:"Get or set the compressor mix level of the Main L/R output." cmd:"mix"` + Makeup MainCompMakeupCmd `help:"Get or set the compressor makeup gain of the Main L/R output." cmd:"makeup"` + Attack MainCompAttackCmd `help:"Get or set the compressor attack time of the Main L/R output." cmd:"attack"` + Hold MainCompHoldCmd `help:"Get or set the compressor hold time of the Main L/R output." cmd:"hold"` + Release MainCompReleaseCmd `help:"Get or set the compressor release time of the Main L/R output." cmd:"release"` +} + +// MainCompOnCmd defines the command for getting or setting the compressor on/off state of the Main L/R output, allowing users to specify the desired state as "true"/"on" or "false"/"off". +type MainCompOnCmd struct { + Enable *string `arg:"" help:"The compressor on/off state to set. If not provided, the current state will be printed." optional:"" enum:"true,false"` +} + +// Run executes the MainCompOnCmd command, either retrieving the current compressor on/off state of the Main L/R output or setting it based on the provided argument. +func (cmd *MainCompOnCmd) Run(ctx *context, main *MainCmdGroup) error { + if cmd.Enable == nil { + resp, err := ctx.Client.Main.Comp.On(0) + if err != nil { + return fmt.Errorf("failed to get Main L/R compressor on/off state: %w", err) + } + fmt.Fprintf(ctx.Out, "Main L/R compressor on/off state: %t\n", resp) + return nil + } + + if err := ctx.Client.Main.Comp.SetOn(0, *cmd.Enable == "true"); err != nil { + return fmt.Errorf("failed to set Main L/R compressor on/off state: %w", err) + } + fmt.Fprintf(ctx.Out, "Main L/R compressor on/off state set to: %t\n", *cmd.Enable == "true") + return nil +} + +// MainCompModeCmd defines the command for getting or setting the compressor mode of the Main L/R output, allowing users to specify the desired mode as "comp" or "exp". +type MainCompModeCmd struct { + Mode *string `arg:"" help:"The compressor mode to set. If not provided, the current mode will be printed." optional:"" enum:"comp,exp"` +} + +// Run executes the MainCompModeCmd command, either retrieving the current compressor mode of the Main L/R output or setting it based on the provided argument. +func (cmd *MainCompModeCmd) Run(ctx *context, main *MainCmdGroup) error { + if cmd.Mode == nil { + resp, err := ctx.Client.Main.Comp.Mode(0) + if err != nil { + return fmt.Errorf("failed to get Main L/R compressor mode: %w", err) + } + fmt.Fprintf(ctx.Out, "Main L/R compressor mode: %s\n", resp) + return nil + } + + if err := ctx.Client.Main.Comp.SetMode(0, *cmd.Mode); err != nil { + return fmt.Errorf("failed to set Main L/R compressor mode: %w", err) + } + fmt.Fprintf(ctx.Out, "Main L/R compressor mode set to: %s\n", *cmd.Mode) + return nil +} + +// MainCompThresholdCmd defines the command for getting or setting the compressor threshold of the Main L/R output, allowing users to specify the desired threshold in dB. +type MainCompThresholdCmd struct { + Threshold *float64 `arg:"" help:"The compressor threshold to set. If not provided, the current threshold will be printed." optional:""` +} + +// Run executes the MainCompThresholdCmd command, either retrieving the current compressor threshold of the Main L/R output or setting it based on the provided argument. +func (cmd *MainCompThresholdCmd) Run(ctx *context, main *MainCmdGroup) error { + if cmd.Threshold == nil { + resp, err := ctx.Client.Main.Comp.Threshold(0) + if err != nil { + return fmt.Errorf("failed to get Main L/R compressor threshold: %w", err) + } + fmt.Fprintf(ctx.Out, "Main L/R compressor threshold: %.2f dB\n", resp) + return nil + } + + if err := ctx.Client.Main.Comp.SetThreshold(0, *cmd.Threshold); err != nil { + return fmt.Errorf("failed to set Main L/R compressor threshold: %w", err) + } + fmt.Fprintf(ctx.Out, "Main L/R compressor threshold set to: %.2f dB\n", *cmd.Threshold) + return nil +} + +// MainCompRatioCmd defines the command for getting or setting the compressor ratio of the Main L/R output, allowing users to specify the desired ratio. +type MainCompRatioCmd struct { + Ratio *float64 `arg:"" help:"The compressor ratio to set. If not provided, the current ratio will be printed." optional:""` +} + +// Run executes the MainCompRatioCmd command, either retrieving the current compressor ratio of the Main L/R output or setting it based on the provided argument. +func (cmd *MainCompRatioCmd) Run(ctx *context, main *MainCmdGroup) error { + if cmd.Ratio == nil { + resp, err := ctx.Client.Main.Comp.Ratio(0) + if err != nil { + return fmt.Errorf("failed to get Main L/R compressor ratio: %w", err) + } + fmt.Fprintf(ctx.Out, "Main L/R compressor ratio: %.2f\n", resp) + return nil + } + + if err := ctx.Client.Main.Comp.SetRatio(0, *cmd.Ratio); err != nil { + return fmt.Errorf("failed to set Main L/R compressor ratio: %w", err) + } + fmt.Fprintf(ctx.Out, "Main L/R compressor ratio set to: %.2f\n", *cmd.Ratio) + return nil +} + +// MainCompMixCmd defines the command for getting or setting the compressor mix level of the Main L/R output, allowing users to specify the desired mix level in percentage. +type MainCompMixCmd struct { + Mix *float64 `arg:"" help:"The compressor mix level to set. If not provided, the current mix level will be printed." optional:""` +} + +// Run executes the MainCompMixCmd command, either retrieving the current compressor mix level of the Main L/R output or setting it based on the provided argument. +func (cmd *MainCompMixCmd) Run(ctx *context, main *MainCmdGroup) error { + if cmd.Mix == nil { + resp, err := ctx.Client.Main.Comp.Mix(0) + if err != nil { + return fmt.Errorf("failed to get Main L/R compressor mix level: %w", err) + } + fmt.Fprintf(ctx.Out, "Main L/R compressor mix level: %.2f%%\n", resp) + return nil + } + + if err := ctx.Client.Main.Comp.SetMix(0, *cmd.Mix); err != nil { + return fmt.Errorf("failed to set Main L/R compressor mix level: %w", err) + } + fmt.Fprintf(ctx.Out, "Main L/R compressor mix level set to: %.2f%%\n", *cmd.Mix) + return nil +} + +// MainCompMakeupCmd defines the command for getting or setting the compressor makeup gain of the Main L/R output, allowing users to specify the desired makeup gain in dB. +type MainCompMakeupCmd struct { + Makeup *float64 `arg:"" help:"The compressor makeup gain to set. If not provided, the current makeup gain will be printed." optional:""` +} + +// Run executes the MainCompMakeupCmd command, either retrieving the current compressor makeup gain of the Main L/R output or setting it based on the provided argument. +func (cmd *MainCompMakeupCmd) Run(ctx *context, main *MainCmdGroup) error { + if cmd.Makeup == nil { + resp, err := ctx.Client.Main.Comp.Makeup(0) + if err != nil { + return fmt.Errorf("failed to get Main L/R compressor makeup gain: %w", err) + } + fmt.Fprintf(ctx.Out, "Main L/R compressor makeup gain: %.2f dB\n", resp) + return nil + } + + if err := ctx.Client.Main.Comp.SetMakeup(0, *cmd.Makeup); err != nil { + return fmt.Errorf("failed to set Main L/R compressor makeup gain: %w", err) + } + fmt.Fprintf(ctx.Out, "Main L/R compressor makeup gain set to: %.2f dB\n", *cmd.Makeup) + return nil +} + +// MainCompAttackCmd defines the command for getting or setting the compressor attack time of the Main L/R output, allowing users to specify the desired attack time in milliseconds. +type MainCompAttackCmd struct { + Attack *float64 `arg:"" help:"The compressor attack time to set. If not provided, the current attack time will be printed." optional:""` +} + +// Run executes the MainCompAttackCmd command, either retrieving the current compressor attack time of the Main L/R output or setting it based on the provided argument. +func (cmd *MainCompAttackCmd) Run(ctx *context, main *MainCmdGroup) error { + if cmd.Attack == nil { + resp, err := ctx.Client.Main.Comp.Attack(0) + if err != nil { + return fmt.Errorf("failed to get Main L/R compressor attack time: %w", err) + } + fmt.Fprintf(ctx.Out, "Main L/R compressor attack time: %.2f ms\n", resp) + return nil + } + + if err := ctx.Client.Main.Comp.SetAttack(0, *cmd.Attack); err != nil { + return fmt.Errorf("failed to set Main L/R compressor attack time: %w", err) + } + fmt.Fprintf(ctx.Out, "Main L/R compressor attack time set to: %.2f ms\n", *cmd.Attack) + return nil +} + +// MainCompHoldCmd defines the command for getting or setting the compressor hold time of the Main L/R output, allowing users to specify the desired hold time in milliseconds. +type MainCompHoldCmd struct { + Hold *float64 `arg:"" help:"The compressor hold time to set. If not provided, the current hold time will be printed." optional:""` +} + +// Run executes the MainCompHoldCmd command, either retrieving the current compressor hold time of the Main L/R output or setting it based on the provided argument. +func (cmd *MainCompHoldCmd) Run(ctx *context, main *MainCmdGroup) error { + if cmd.Hold == nil { + resp, err := ctx.Client.Main.Comp.Hold(0) + if err != nil { + return fmt.Errorf("failed to get Main L/R compressor hold time: %w", err) + } + fmt.Fprintf(ctx.Out, "Main L/R compressor hold time: %.2f ms\n", resp) + return nil + } + + if err := ctx.Client.Main.Comp.SetHold(0, *cmd.Hold); err != nil { + return fmt.Errorf("failed to set Main L/R compressor hold time: %w", err) + } + fmt.Fprintf(ctx.Out, "Main L/R compressor hold time set to: %.2f ms\n", *cmd.Hold) + return nil +} + +// MainCompReleaseCmd defines the command for getting or setting the compressor release time of the Main L/R output, allowing users to specify the desired release time in milliseconds. +type MainCompReleaseCmd struct { + Release *float64 `arg:"" help:"The compressor release time to set. If not provided, the current release time will be printed." optional:""` +} + +// Run executes the MainCompReleaseCmd command, either retrieving the current compressor release time of the Main L/R output or setting it based on the provided argument. +func (cmd *MainCompReleaseCmd) Run(ctx *context, main *MainCmdGroup) error { + if cmd.Release == nil { + resp, err := ctx.Client.Main.Comp.Release(0) + if err != nil { + return fmt.Errorf("failed to get Main L/R compressor release time: %w", err) + } + fmt.Fprintf(ctx.Out, "Main L/R compressor release time: %.2f ms\n", resp) + return nil + } + + if err := ctx.Client.Main.Comp.SetRelease(0, *cmd.Release); err != nil { + return fmt.Errorf("failed to set Main L/R compressor release time: %w", err) + } + fmt.Fprintf(ctx.Out, "Main L/R compressor release time set to: %.2f ms\n", *cmd.Release) + return nil +} diff --git a/cmd/xair-cli/raw.go b/cmd/xair-cli/raw.go new file mode 100644 index 0000000..bdb6309 --- /dev/null +++ b/cmd/xair-cli/raw.go @@ -0,0 +1,39 @@ +package main + +import ( + "fmt" + + "github.com/charmbracelet/log" +) + +// RawCmd represents the command to send raw OSC messages to the mixer. +type RawCmd struct { + Address string `help:"The OSC address to send the message to." arg:""` + Args []string `help:"The arguments to include in the OSC message." arg:"" optional:""` +} + +// Run executes the RawCmd by sending the specified OSC message to the mixer and optionally waiting for a response. +func (cmd *RawCmd) Run(ctx *context) error { + params := make([]any, len(cmd.Args)) + for i, arg := range cmd.Args { + params[i] = arg + } + if err := ctx.Client.SendMessage(cmd.Address, params...); err != nil { + return fmt.Errorf("failed to send raw OSC message: %w", err) + } + + if len(params) > 0 { + log.Debugf("Sent OSC message: %s with args: %v\n", cmd.Address, cmd.Args) + return nil + } + + msg, err := ctx.Client.ReceiveMessage() + if err != nil { + return fmt.Errorf("failed to receive response for raw OSC message: %w", err) + } + if msg != nil { + fmt.Fprintf(ctx.Out, "Received response: %s with args: %v\n", msg.Address, msg.Arguments) + } + + return nil +} diff --git a/cmd/xair-cli/snapshot.go b/cmd/xair-cli/snapshot.go new file mode 100644 index 0000000..e047637 --- /dev/null +++ b/cmd/xair-cli/snapshot.go @@ -0,0 +1,75 @@ +package main + +import "fmt" + +type SnapshotCmdGroup struct { + List ListCmd `help:"List all snapshots." cmd:"list"` + Index struct { + Index int `arg:"" help:"The index of the snapshot."` + Name NameCmd `help:"Get or set the name of a snapshot." cmd:"name"` + Save SaveCmd `help:"Save the current mixer state to a snapshot." cmd:"save"` + Load LoadCmd `help:"Load a mixer state from a snapshot." cmd:"load"` + Delete DeleteCmd `help:"Delete a snapshot." cmd:"delete"` + } `help:"The index of the snapshot." arg:""` +} + +type ListCmd struct { +} + +func (c *ListCmd) Run(ctx *context) error { + for i := range 64 { + name, err := ctx.Client.Snapshot.Name(i + 1) + if err != nil { + break + } + if name == "" { + continue + } + fmt.Fprintf(ctx.Out, "%d: %s\n", i+1, name) + } + return nil +} + +type NameCmd struct { + Name *string `arg:"" help:"The name of the snapshot." optional:""` +} + +func (c *NameCmd) Run(ctx *context, snapshot *SnapshotCmdGroup) error { + if c.Name == nil { + name, err := ctx.Client.Snapshot.Name(snapshot.Index.Index) + if err != nil { + return err + } + fmt.Fprintln(ctx.Out, name) + return nil + } + + return ctx.Client.Snapshot.SetName(snapshot.Index.Index, *c.Name) +} + +type SaveCmd struct { + Name string `arg:"" help:"The name of the snapshot."` +} + +func (c *SaveCmd) Run(ctx *context, snapshot *SnapshotCmdGroup) error { + err := ctx.Client.Snapshot.CurrentName(c.Name) + if err != nil { + return err + } + + return ctx.Client.Snapshot.CurrentSave(snapshot.Index.Index) +} + +type LoadCmd struct { +} + +func (c *LoadCmd) Run(ctx *context, snapshot *SnapshotCmdGroup) error { + return ctx.Client.Snapshot.CurrentLoad(snapshot.Index.Index) +} + +type DeleteCmd struct { +} + +func (c *DeleteCmd) Run(ctx *context, snapshot *SnapshotCmdGroup) error { + return ctx.Client.Snapshot.CurrentDelete(snapshot.Index.Index) +} diff --git a/cmd/xair-cli/strip.go b/cmd/xair-cli/strip.go new file mode 100644 index 0000000..eb72f60 --- /dev/null +++ b/cmd/xair-cli/strip.go @@ -0,0 +1,723 @@ +package main + +import ( + "fmt" + "time" + + "github.com/alecthomas/kong" +) + +// StripCmdGroup defines the command group for controlling the strips of the mixer, including commands for getting and setting various parameters such as mute state, fader level, send levels, and EQ settings. +type StripCmdGroup struct { + Index struct { + Index int `arg:"" help:"The index of the strip. (1-based indexing)"` + Mute StripMuteCmd ` help:"Get or set the mute state of the strip." cmd:""` + Fader StripFaderCmd ` help:"Get or set the fader level of the strip." cmd:""` + Fadein StripFadeinCmd ` help:"Fade in the strip over a specified duration." cmd:""` + Fadeout StripFadeoutCmd ` help:"Fade out the strip over a specified duration." cmd:""` + Send StripSendCmd ` help:"Get or set the send level for a specific bus." cmd:""` + Name StripNameCmd ` help:"Get or set the name of the strip." cmd:""` + + Gate StripGateCmdGroup ` help:"Commands related to the strip gate." cmd:"gate"` + Eq StripEqCmdGroup ` help:"Commands related to the strip EQ." cmd:"eq"` + Comp StripCompCmdGroup ` help:"Commands related to the strip compressor." cmd:"comp"` + } `arg:"" help:"Control a specific strip by index."` +} + +// StripMuteCmd defines the command for getting or setting the mute state of a strip. +type StripMuteCmd struct { + State *string `arg:"" help:"The mute state to set (true or false). If not provided, the current mute state will be returned." optional:"" enum:"true,false"` +} + +// Run executes the StripMuteCmd command, either retrieving the current mute state of the strip or setting it based on the provided argument. +func (cmd *StripMuteCmd) Run(ctx *context, strip *StripCmdGroup) error { + if cmd.State == nil { + resp, err := ctx.Client.Strip.Mute(strip.Index.Index) + if err != nil { + return fmt.Errorf("failed to get mute state: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d mute state: %t\n", strip.Index.Index, resp) + return nil + } + + if err := ctx.Client.Strip.SetMute(strip.Index.Index, *cmd.State == "true"); err != nil { + return fmt.Errorf("failed to set mute state: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d mute state set to: %s\n", strip.Index.Index, *cmd.State) + return nil +} + +// StripFaderCmd defines the command for getting or setting the fader level of a strip. +type StripFaderCmd struct { + Level *float64 `arg:"" help:"The fader level to set (in dB)." optional:""` +} + +// Run executes the StripFaderCmd command, either retrieving the current fader level of the strip or setting it based on the provided argument. +func (cmd *StripFaderCmd) Run(ctx *context, strip *StripCmdGroup) error { + if cmd.Level == nil { + resp, err := ctx.Client.Strip.Fader(strip.Index.Index) + if err != nil { + return fmt.Errorf("failed to get fader level: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d fader level: %.2f dB\n", strip.Index.Index, resp) + return nil + } + + if err := ctx.Client.Strip.SetFader(strip.Index.Index, *cmd.Level); err != nil { + return fmt.Errorf("failed to set fader level: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d fader level set to: %.2f dB\n", strip.Index.Index, *cmd.Level) + return nil +} + +// StripFadeinCmd defines the command for fading in a strip over a specified duration, gradually increasing the fader level from its current value to a target value. +type StripFadeinCmd struct { + Duration time.Duration `flag:"" help:"The duration of the fade-in (in seconds)." default:"5s"` + Target float64 ` help:"The target fader level (in dB)." default:"0.0" arg:""` +} + +// Run executes the StripFadeinCmd command, gradually increasing the fader level of the strip from its current value to the specified target value over the specified duration. +func (cmd *StripFadeinCmd) Run(ctx *context, strip *StripCmdGroup) error { + currentLevel, err := ctx.Client.Strip.Fader(strip.Index.Index) + if err != nil { + return fmt.Errorf("failed to get current fader level: %w", err) + } + + if currentLevel >= cmd.Target { + return fmt.Errorf( + "current fader level (%.2f dB) is already at or above the target level (%.2f dB)", + currentLevel, + cmd.Target, + ) + } + + totalSteps := float64(cmd.Target - currentLevel) + stepDuration := time.Duration(cmd.Duration.Seconds()*1000/totalSteps) * time.Millisecond + for currentLevel < cmd.Target { + currentLevel++ + if err := ctx.Client.Strip.SetFader(strip.Index.Index, currentLevel); err != nil { + return fmt.Errorf("failed to set fader level during fade-in: %w", err) + } + time.Sleep(stepDuration) + } + + fmt.Fprintf(ctx.Out, "Strip %d fade-in complete. Final level: %.2f dB\n", strip.Index.Index, cmd.Target) + return nil +} + +// StripFadeoutCmd defines the command for fading out a strip over a specified duration, gradually decreasing the fader level from its current value to a target value. +type StripFadeoutCmd struct { + Duration time.Duration `flag:"" help:"The duration of the fade-out (in seconds)." default:"5s"` + Target float64 ` help:"The target fader level (in dB)." default:"-90.0" arg:""` +} + +// Run executes the StripFadeoutCmd command, gradually decreasing the fader level of the strip from its current value to the specified target value over the specified duration. +func (cmd *StripFadeoutCmd) Run(ctx *context, strip *StripCmdGroup) error { + { + currentLevel, err := ctx.Client.Strip.Fader(strip.Index.Index) + if err != nil { + return fmt.Errorf("failed to get current fader level: %w", err) + } + + if currentLevel <= cmd.Target { + return fmt.Errorf( + "current fader level (%.2f dB) is already at or below the target level (%.2f dB)", + currentLevel, + cmd.Target, + ) + } + + totalSteps := float64(currentLevel - cmd.Target) + stepDuration := time.Duration(cmd.Duration.Seconds()*1000/totalSteps) * time.Millisecond + for currentLevel > cmd.Target { + currentLevel-- + if err := ctx.Client.Strip.SetFader(strip.Index.Index, currentLevel); err != nil { + return fmt.Errorf("failed to set fader level during fade-out: %w", err) + } + time.Sleep(stepDuration) + } + + fmt.Fprintf(ctx.Out, "Strip %d fade-out complete. Final level: %.2f dB\n", strip.Index.Index, cmd.Target) + return nil + } +} + +// StripSendCmd defines the command for getting or setting the send level for a specific bus on a strip, allowing users to control the level of the signal being sent from the strip to a particular bus. +type StripSendCmd struct { + BusNum int `arg:"" help:"The bus number to get or set the send level for."` + Level *float64 `arg:"" help:"The send level to set (in dB)." optional:""` +} + +// Run executes the StripSendCmd command, either retrieving the current send level for the specified bus on the strip or setting it based on the provided argument. +func (cmd *StripSendCmd) Run(ctx *context, strip *StripCmdGroup) error { + if cmd.Level == nil { + resp, err := ctx.Client.Strip.SendLevel(strip.Index.Index, cmd.BusNum) + if err != nil { + return fmt.Errorf("failed to get send level: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d send level for bus %d: %.2f dB\n", strip.Index.Index, cmd.BusNum, resp) + return nil + } + + if err := ctx.Client.Strip.SetSendLevel(strip.Index.Index, cmd.BusNum, *cmd.Level); err != nil { + return fmt.Errorf("failed to set send level: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d send level for bus %d set to: %.2f dB\n", strip.Index.Index, cmd.BusNum, *cmd.Level) + return nil +} + +// StripNameCmd defines the command for getting or setting the name of a strip, allowing users to assign custom names to strips for easier identification and organization. +type StripNameCmd struct { + Name *string `arg:"" help:"The name to set for the strip." optional:""` +} + +// Run executes the StripNameCmd command, either retrieving the current name of the strip or setting it based on the provided argument. +func (cmd *StripNameCmd) Run(ctx *context, strip *StripCmdGroup) error { + if cmd.Name == nil { + resp, err := ctx.Client.Strip.Name(strip.Index.Index) + if err != nil { + return fmt.Errorf("failed to get strip name: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d name: %s\n", strip.Index.Index, resp) + return nil + } + + if err := ctx.Client.Strip.SetName(strip.Index.Index, *cmd.Name); err != nil { + return fmt.Errorf("failed to set strip name: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d name set to: %s\n", strip.Index.Index, *cmd.Name) + return nil +} + +// StripGateCmdGroup defines the command group for controlling the gate settings of a strip, including commands for getting and setting the gate on/off state, mode, threshold, range, attack time, hold time, and release time. +type StripGateCmdGroup struct { + On StripGateOnCmd `help:"Get or set the gate on/off state of the strip." cmd:""` + Mode StripGateModeCmd `help:"Get or set the gate mode of the strip." cmd:""` + Threshold StripGateThresholdCmd `help:"Get or set the gate threshold of the strip." cmd:""` + Range StripGateRangeCmd `help:"Get or set the gate range of the strip." cmd:""` + Attack StripGateAttackCmd `help:"Get or set the gate attack time of the strip." cmd:""` + Hold StripGateHoldCmd `help:"Get or set the gate hold time of the strip." cmd:""` + Release StripGateReleaseCmd `help:"Get or set the gate release time of the strip." cmd:""` +} + +// StripGateOnCmd defines the command for getting or setting the gate on/off state of a strip, allowing users to enable or disable the gate effect on the strip. +type StripGateOnCmd struct { + Enable *string `arg:"" help:"Whether to enable or disable the gate." optional:"" enum:"true,false"` +} + +// Run executes the StripGateOnCmd command, either retrieving the current gate on/off state of the strip or setting it based on the provided argument. +func (cmd *StripGateOnCmd) Run(ctx *context, strip *StripCmdGroup) error { + if cmd.Enable == nil { + resp, err := ctx.Client.Strip.Gate.On(strip.Index.Index) + if err != nil { + return fmt.Errorf("failed to get gate state: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d gate state: %t\n", strip.Index.Index, resp) + return nil + } + + if err := ctx.Client.Strip.Gate.SetOn(strip.Index.Index, *cmd.Enable == "true"); err != nil { + return fmt.Errorf("failed to set gate state: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d gate state set to: %s\n", strip.Index.Index, *cmd.Enable) + return nil +} + +// StripGateModeCmd defines the command for getting or setting the gate mode of a strip, allowing users to choose from different gate modes such as exp2, exp3, exp4, gate, or duck. +type StripGateModeCmd struct { + Mode *string `arg:"" help:"The gate mode to set." optional:"" enum:"exp2,exp3,exp4,gate,duck"` +} + +// Run executes the StripGateModeCmd command, either retrieving the current gate mode of the strip or setting it based on the provided argument. +func (cmd *StripGateModeCmd) Run(ctx *context, strip *StripCmdGroup) error { + if cmd.Mode == nil { + resp, err := ctx.Client.Strip.Gate.Mode(strip.Index.Index) + if err != nil { + return fmt.Errorf("failed to get gate mode: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d gate mode: %s\n", strip.Index.Index, resp) + return nil + } + + if err := ctx.Client.Strip.Gate.SetMode(strip.Index.Index, *cmd.Mode); err != nil { + return fmt.Errorf("failed to set gate mode: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d gate mode set to: %s\n", strip.Index.Index, *cmd.Mode) + return nil +} + +// StripGateThresholdCmd defines the command for getting or setting the gate threshold of a strip, allowing users to specify the threshold level at which the gate will start to attenuate the signal. +type StripGateThresholdCmd struct { + Threshold *float64 `arg:"" help:"The gate threshold to set (in dB)." optional:""` +} + +// Run executes the StripGateThresholdCmd command, either retrieving the current gate threshold of the strip or setting it based on the provided argument. +func (cmd *StripGateThresholdCmd) Run(ctx *context, strip *StripCmdGroup) error { + if cmd.Threshold == nil { + resp, err := ctx.Client.Strip.Gate.Threshold(strip.Index.Index) + if err != nil { + return fmt.Errorf("failed to get gate threshold: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d gate threshold: %.2f\n", strip.Index.Index, resp) + return nil + } + + if err := ctx.Client.Strip.Gate.SetThreshold(strip.Index.Index, *cmd.Threshold); err != nil { + return fmt.Errorf("failed to set gate threshold: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d gate threshold set to: %.2f\n", strip.Index.Index, *cmd.Threshold) + return nil +} + +// StripGateRangeCmd defines the command for getting or setting the gate range of a strip, allowing users to specify the amount of attenuation applied by the gate when the signal falls below the threshold. +type StripGateRangeCmd struct { + Range *float64 `arg:"" help:"The gate range to set (in dB)." optional:""` +} + +// Run executes the StripGateRangeCmd command, either retrieving the current gate range of the strip or setting it based on the provided argument. +func (cmd *StripGateRangeCmd) Run(ctx *context, strip *StripCmdGroup) error { + if cmd.Range == nil { + resp, err := ctx.Client.Strip.Gate.Range(strip.Index.Index) + if err != nil { + return fmt.Errorf("failed to get gate range: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d gate range: %.2f\n", strip.Index.Index, resp) + return nil + } + + if err := ctx.Client.Strip.Gate.SetRange(strip.Index.Index, *cmd.Range); err != nil { + return fmt.Errorf("failed to set gate range: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d gate range set to: %.2f\n", strip.Index.Index, *cmd.Range) + return nil +} + +// StripGateAttackCmd defines the command for getting or setting the gate attack time of a strip, allowing users to specify the time it takes for the gate to fully open after the signal exceeds the threshold. +type StripGateAttackCmd struct { + Attack *float64 `arg:"" help:"The gate attack time to set (in ms)." optional:""` +} + +// Run executes the StripGateAttackCmd command, either retrieving the current gate attack time of the strip or setting it based on the provided argument. +func (cmd *StripGateAttackCmd) Run(ctx *context, strip *StripCmdGroup) error { + if cmd.Attack == nil { + resp, err := ctx.Client.Strip.Gate.Attack(strip.Index.Index) + if err != nil { + return fmt.Errorf("failed to get gate attack time: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d gate attack time: %.2f ms\n", strip.Index.Index, resp) + return nil + } + + if err := ctx.Client.Strip.Gate.SetAttack(strip.Index.Index, *cmd.Attack); err != nil { + return fmt.Errorf("failed to set gate attack time: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d gate attack time set to: %.2f ms\n", strip.Index.Index, *cmd.Attack) + return nil +} + +// StripGateHoldCmd defines the command for getting or setting the gate hold time of a strip, allowing users to specify the time that the gate remains open after the signal falls below the threshold before it starts to close. +type StripGateHoldCmd struct { + Hold *float64 `arg:"" help:"The gate hold time to set (in ms)." optional:""` +} + +// Run executes the StripGateHoldCmd command, either retrieving the current gate hold time of the strip or setting it based on the provided argument. +func (cmd *StripGateHoldCmd) Run(ctx *context, strip *StripCmdGroup) error { + if cmd.Hold == nil { + resp, err := ctx.Client.Strip.Gate.Hold(strip.Index.Index) + if err != nil { + return fmt.Errorf("failed to get gate hold time: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d gate hold time: %.2f ms\n", strip.Index.Index, resp) + return nil + } + + if err := ctx.Client.Strip.Gate.SetHold(strip.Index.Index, *cmd.Hold); err != nil { + return fmt.Errorf("failed to set gate hold time: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d gate hold time set to: %.2f ms\n", strip.Index.Index, *cmd.Hold) + return nil +} + +// StripGateReleaseCmd defines the command for getting or setting the gate release time of a strip, allowing users to specify the time it takes for the gate to fully close after the signal falls below the threshold and the hold time has elapsed. +type StripGateReleaseCmd struct { + Release *float64 `arg:"" help:"The gate release time to set (in ms)." optional:""` +} + +// Run executes the StripGateReleaseCmd command, either retrieving the current gate release time of the strip or setting it based on the provided argument. +func (cmd *StripGateReleaseCmd) Run(ctx *context, strip *StripCmdGroup) error { + if cmd.Release == nil { + resp, err := ctx.Client.Strip.Gate.Release(strip.Index.Index) + if err != nil { + return fmt.Errorf("failed to get gate release time: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d gate release time: %.2f ms\n", strip.Index.Index, resp) + return nil + } + + if err := ctx.Client.Strip.Gate.SetRelease(strip.Index.Index, *cmd.Release); err != nil { + return fmt.Errorf("failed to set gate release time: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d gate release time set to: %.2f ms\n", strip.Index.Index, *cmd.Release) + return nil +} + +// StripEqCmdGroup defines the command group for controlling the EQ settings of a strip, including commands for getting and setting the EQ on/off state and parameters for each EQ band such as gain, frequency, Q factor, and type. +type StripEqCmdGroup struct { + On StripEqOnCmd `help:"Get or set the EQ on/off state of the strip." cmd:""` + Band struct { + Band int `arg:"" help:"The EQ band number."` + Gain StripEqBandGainCmd `help:"Get or set the gain of the EQ band." cmd:""` + Freq StripEqBandFreqCmd `help:"Get or set the frequency of the EQ band." cmd:""` + Q StripEqBandQCmd `help:"Get or set the Q factor of the EQ band." cmd:""` + Type StripEqBandTypeCmd `help:"Get or set the type of the EQ band." cmd:""` + } `help:"Commands for controlling a specific EQ band of the strip." arg:""` +} + +// Validate checks if the provided EQ band number is valid (between 1 and 4) and returns an error if it is not. +func (cmd *StripEqCmdGroup) Validate(ctx kong.Context) error { + if cmd.Band.Band < 1 || cmd.Band.Band > 4 { + return fmt.Errorf("EQ band number must be between 1 and 4") + } + return nil +} + +// StripEqOnCmd defines the command for getting or setting the EQ on/off state of a strip, allowing users to enable or disable the EQ effect on the strip. +type StripEqOnCmd struct { + Enable *string `arg:"" help:"Whether to enable or disable the EQ." optional:"" enum:"true,false"` +} + +// Run executes the StripEqOnCmd command, either retrieving the current EQ on/off state of the strip or setting it based on the provided argument. +func (cmd *StripEqOnCmd) Run(ctx *context, strip *StripCmdGroup) error { + if cmd.Enable == nil { + resp, err := ctx.Client.Strip.Eq.On(strip.Index.Index) + if err != nil { + return fmt.Errorf("failed to get EQ state: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d EQ state: %t\n", strip.Index.Index, resp) + return nil + } + + if err := ctx.Client.Strip.Eq.SetOn(strip.Index.Index, *cmd.Enable == "true"); err != nil { + return fmt.Errorf("failed to set EQ state: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d EQ state set to: %s\n", strip.Index.Index, *cmd.Enable) + return nil +} + +// StripEqBandGainCmd defines the command for getting or setting the gain of a specific EQ band on a strip, allowing users to adjust the level of the signal for that band in decibels (dB). +type StripEqBandGainCmd struct { + Gain *float64 `arg:"" help:"The gain to set for the EQ band (in dB)." optional:""` +} + +// Run executes the StripEqBandGainCmd command, either retrieving the current gain of the specified EQ band on the strip or setting it based on the provided argument. +func (cmd *StripEqBandGainCmd) Run(ctx *context, strip *StripCmdGroup, stripEq *StripEqCmdGroup) error { + if cmd.Gain == nil { + resp, err := ctx.Client.Strip.Eq.Gain(strip.Index.Index, stripEq.Band.Band) + if err != nil { + return fmt.Errorf("failed to get EQ band gain: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d EQ band %d gain: %.2f\n", strip.Index.Index, stripEq.Band.Band, resp) + return nil + } + + if err := ctx.Client.Strip.Eq.SetGain(strip.Index.Index, stripEq.Band.Band, *cmd.Gain); err != nil { + return fmt.Errorf("failed to set EQ band gain: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d EQ band %d gain set to: %.2f\n", strip.Index.Index, stripEq.Band.Band, *cmd.Gain) + return nil +} + +// StripEqBandFreqCmd defines the command for getting or setting the frequency of a specific EQ band on a strip, allowing users to adjust the center frequency of the band in hertz (Hz). +type StripEqBandFreqCmd struct { + Freq *float64 `arg:"" help:"The frequency to set for the EQ band (in Hz)." optional:""` +} + +// Run executes the StripEqBandFreqCmd command, either retrieving the current frequency of the specified EQ band on the strip or setting it based on the provided argument. +func (cmd *StripEqBandFreqCmd) Run(ctx *context, strip *StripCmdGroup, stripEq *StripEqCmdGroup) error { + if cmd.Freq == nil { + resp, err := ctx.Client.Strip.Eq.Frequency(strip.Index.Index, stripEq.Band.Band) + if err != nil { + return fmt.Errorf("failed to get EQ band frequency: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d EQ band %d frequency: %.2f Hz\n", strip.Index.Index, stripEq.Band.Band, resp) + return nil + } + + if err := ctx.Client.Strip.Eq.SetFrequency(strip.Index.Index, stripEq.Band.Band, *cmd.Freq); err != nil { + return fmt.Errorf("failed to set EQ band frequency: %w", err) + } + fmt.Fprintf( + ctx.Out, + "Strip %d EQ band %d frequency set to: %.2f Hz\n", + strip.Index.Index, + stripEq.Band.Band, + *cmd.Freq, + ) + return nil +} + +// StripEqBandQCmd defines the command for getting or setting the Q factor of a specific EQ band on a strip, allowing users to adjust the bandwidth of the band, which determines how wide or narrow the affected frequency range is. +type StripEqBandQCmd struct { + Q *float64 `arg:"" help:"The Q factor to set for the EQ band." optional:""` +} + +// Run executes the StripEqBandQCmd command, either retrieving the current Q factor of the specified EQ band on the strip or setting it based on the provided argument. +func (cmd *StripEqBandQCmd) Run(ctx *context, strip *StripCmdGroup, stripEq *StripEqCmdGroup) error { + if cmd.Q == nil { + resp, err := ctx.Client.Strip.Eq.Q(strip.Index.Index, stripEq.Band.Band) + if err != nil { + return fmt.Errorf("failed to get EQ band Q factor: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d EQ band %d Q factor: %.2f\n", strip.Index.Index, stripEq.Band.Band, resp) + return nil + } + + if err := ctx.Client.Strip.Eq.SetQ(strip.Index.Index, stripEq.Band.Band, *cmd.Q); err != nil { + return fmt.Errorf("failed to set EQ band Q factor: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d EQ band %d Q factor set to: %.2f\n", strip.Index.Index, stripEq.Band.Band, *cmd.Q) + return nil +} + +// StripEqBandTypeCmd defines the command for getting or setting the type of a specific EQ band on a strip, allowing users to choose from different EQ types such as low cut (lcut), low shelf (lshv), parametric (peq), variable Q (veq), high shelf (hshv), or high cut (hcut). +type StripEqBandTypeCmd struct { + Type *string `arg:"" help:"The type to set for the EQ band." optional:"" enum:"lcut,lshv,peq,veq,hshv,hcut"` +} + +// Run executes the StripEqBandTypeCmd command, either retrieving the current type of the specified EQ band on the strip or setting it based on the provided argument. +func (cmd *StripEqBandTypeCmd) Run(ctx *context, strip *StripCmdGroup, stripEq *StripEqCmdGroup) error { + if cmd.Type == nil { + resp, err := ctx.Client.Strip.Eq.Type(strip.Index.Index, stripEq.Band.Band) + if err != nil { + return fmt.Errorf("failed to get EQ band type: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d EQ band %d type: %s\n", strip.Index.Index, stripEq.Band.Band, resp) + return nil + } + + if err := ctx.Client.Strip.Eq.SetType(strip.Index.Index, stripEq.Band.Band, *cmd.Type); err != nil { + return fmt.Errorf("failed to set EQ band type: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d EQ band %d type set to: %s\n", strip.Index.Index, stripEq.Band.Band, *cmd.Type) + return nil +} + +// StripCompCmdGroup defines the command group for controlling the compressor settings of a strip, including commands for getting and setting the compressor on/off state, mode, threshold, ratio, mix, makeup gain, attack time, hold time, and release time. +type StripCompCmdGroup struct { + On StripCompOnCmd `help:"Get or set the compressor on/off state of the strip." cmd:""` + Mode StripCompModeCmd `help:"Get or set the compressor mode of the strip." cmd:""` + Threshold StripCompThresholdCmd `help:"Get or set the compressor threshold of the strip." cmd:""` + Ratio StripCompRatioCmd `help:"Get or set the compressor ratio of the strip." cmd:""` + Mix StripCompMixCmd `help:"Get or set the compressor mix of the strip." cmd:""` + Makeup StripCompMakeupCmd `help:"Get or set the compressor makeup gain of the strip." cmd:""` + Attack StripCompAttackCmd `help:"Get or set the compressor attack time of the strip." cmd:""` + Hold StripCompHoldCmd `help:"Get or set the compressor hold time of the strip." cmd:""` + Release StripCompReleaseCmd `help:"Get or set the compressor release time of the strip." cmd:""` +} + +// StripCompOnCmd defines the command for getting or setting the compressor on/off state of a strip, allowing users to enable or disable the compressor effect on the strip. +type StripCompOnCmd struct { + Enable *string `arg:"" help:"Whether to enable or disable the compressor." optional:"" enum:"true,false"` +} + +// Run executes the StripCompOnCmd command, either retrieving the current compressor on/off state of the strip or setting it based on the provided argument. +func (cmd *StripCompOnCmd) Run(ctx *context, strip *StripCmdGroup) error { + if cmd.Enable == nil { + resp, err := ctx.Client.Strip.Comp.On(strip.Index.Index) + if err != nil { + return fmt.Errorf("failed to get compressor state: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d compressor state: %t\n", strip.Index.Index, resp) + return nil + } + + if err := ctx.Client.Strip.Comp.SetOn(strip.Index.Index, *cmd.Enable == "true"); err != nil { + return fmt.Errorf("failed to set compressor state: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d compressor state set to: %s\n", strip.Index.Index, *cmd.Enable) + return nil +} + +// StripCompModeCmd defines the command for getting or setting the compressor mode of a strip, allowing users to choose from different compressor modes such as comp (standard compression) or exp (expander). +type StripCompModeCmd struct { + Mode *string `arg:"" help:"The compressor mode to set." optional:"" enum:"comp,exp"` +} + +// Run executes the StripCompModeCmd command, either retrieving the current compressor mode of the strip or setting it based on the provided argument. +func (cmd *StripCompModeCmd) Run(ctx *context, strip *StripCmdGroup) error { + if cmd.Mode == nil { + resp, err := ctx.Client.Strip.Comp.Mode(strip.Index.Index) + if err != nil { + return fmt.Errorf("failed to get compressor mode: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d compressor mode: %s\n", strip.Index.Index, resp) + return nil + } + + if err := ctx.Client.Strip.Comp.SetMode(strip.Index.Index, *cmd.Mode); err != nil { + return fmt.Errorf("failed to set compressor mode: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d compressor mode set to: %s\n", strip.Index.Index, *cmd.Mode) + return nil +} + +// StripCompThresholdCmd defines the command for getting or setting the compressor threshold of a strip, allowing users to specify the threshold level at which the compressor will start to reduce the signal level. +type StripCompThresholdCmd struct { + Threshold *float64 `arg:"" help:"The compressor threshold to set (in dB)." optional:""` +} + +// Run executes the StripCompThresholdCmd command, either retrieving the current compressor threshold of the strip or setting it based on the provided argument. +func (cmd *StripCompThresholdCmd) Run(ctx *context, strip *StripCmdGroup) error { + if cmd.Threshold == nil { + resp, err := ctx.Client.Strip.Comp.Threshold(strip.Index.Index) + if err != nil { + return fmt.Errorf("failed to get compressor threshold: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d compressor threshold: %.2f\n", strip.Index.Index, resp) + return nil + } + + if err := ctx.Client.Strip.Comp.SetThreshold(strip.Index.Index, *cmd.Threshold); err != nil { + return fmt.Errorf("failed to set compressor threshold: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d compressor threshold set to: %.2f\n", strip.Index.Index, *cmd.Threshold) + return nil +} + +// StripCompRatioCmd defines the command for getting or setting the compressor ratio of a strip, allowing users to specify the amount of gain reduction applied by the compressor once the signal exceeds the threshold. +type StripCompRatioCmd struct { + Ratio *float64 `arg:"" help:"The compressor ratio to set." optional:""` +} + +// Run executes the StripCompRatioCmd command, either retrieving the current compressor ratio of the strip or setting it based on the provided argument. +func (cmd *StripCompRatioCmd) Run(ctx *context, strip *StripCmdGroup) error { + if cmd.Ratio == nil { + resp, err := ctx.Client.Strip.Comp.Ratio(strip.Index.Index) + if err != nil { + return fmt.Errorf("failed to get compressor ratio: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d compressor ratio: %.2f\n", strip.Index.Index, resp) + return nil + } + + if err := ctx.Client.Strip.Comp.SetRatio(strip.Index.Index, *cmd.Ratio); err != nil { + return fmt.Errorf("failed to set compressor ratio: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d compressor ratio set to: %.2f\n", strip.Index.Index, *cmd.Ratio) + return nil +} + +// StripCompMixCmd defines the command for getting or setting the compressor mix of a strip, allowing users to specify the blend between the dry (unprocessed) signal and the wet (compressed) signal, typically expressed as a percentage. +type StripCompMixCmd struct { + Mix *float64 `arg:"" help:"The compressor mix to set (0-100%)." optional:""` +} + +// Run executes the StripCompMixCmd command, either retrieving the current compressor mix of the strip or setting it based on the provided argument. +func (cmd *StripCompMixCmd) Run(ctx *context, strip *StripCmdGroup) error { + if cmd.Mix == nil { + resp, err := ctx.Client.Strip.Comp.Mix(strip.Index.Index) + if err != nil { + return fmt.Errorf("failed to get compressor mix: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d compressor mix: %.2f%%\n", strip.Index.Index, resp) + return nil + } + + if err := ctx.Client.Strip.Comp.SetMix(strip.Index.Index, *cmd.Mix); err != nil { + return fmt.Errorf("failed to set compressor mix: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d compressor mix set to: %.2f%%\n", strip.Index.Index, *cmd.Mix) + return nil +} + +// StripCompMakeupCmd defines the command for getting or setting the compressor makeup gain of a strip, allowing users to specify the amount of gain applied to the signal after compression to compensate for any reduction in level caused by the compressor. +type StripCompMakeupCmd struct { + Makeup *float64 `arg:"" help:"The compressor makeup gain to set (in dB)." optional:""` +} + +// Run executes the StripCompMakeupCmd command, either retrieving the current compressor makeup gain of the strip or setting it based on the provided argument. +func (cmd *StripCompMakeupCmd) Run(ctx *context, strip *StripCmdGroup) error { + if cmd.Makeup == nil { + resp, err := ctx.Client.Strip.Comp.Makeup(strip.Index.Index) + if err != nil { + return fmt.Errorf("failed to get compressor makeup gain: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d compressor makeup gain: %.2f\n", strip.Index.Index, resp) + return nil + } + + if err := ctx.Client.Strip.Comp.SetMakeup(strip.Index.Index, *cmd.Makeup); err != nil { + return fmt.Errorf("failed to set compressor makeup gain: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d compressor makeup gain set to: %.2f\n", strip.Index.Index, *cmd.Makeup) + return nil +} + +// StripCompAttackCmd defines the command for getting or setting the compressor attack time of a strip, allowing users to specify the time it takes for the compressor to start reducing the signal level after the signal exceeds the threshold. +type StripCompAttackCmd struct { + Attack *float64 `arg:"" help:"The compressor attack time to set (in ms)." optional:""` +} + +// Run executes the StripCompAttackCmd command, either retrieving the current compressor attack time of the strip or setting it based on the provided argument. +func (cmd *StripCompAttackCmd) Run(ctx *context, strip *StripCmdGroup) error { + if cmd.Attack == nil { + resp, err := ctx.Client.Strip.Comp.Attack(strip.Index.Index) + if err != nil { + return fmt.Errorf("failed to get compressor attack time: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d compressor attack time: %.2f ms\n", strip.Index.Index, resp) + return nil + } + + if err := ctx.Client.Strip.Comp.SetAttack(strip.Index.Index, *cmd.Attack); err != nil { + return fmt.Errorf("failed to set compressor attack time: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d compressor attack time set to: %.2f ms\n", strip.Index.Index, *cmd.Attack) + return nil +} + +// StripCompHoldCmd defines the command for getting or setting the compressor hold time of a strip, allowing users to specify the time that the compressor continues to reduce the signal level after the signal falls below the threshold before it starts to release. +type StripCompHoldCmd struct { + Hold *float64 `arg:"" help:"The compressor hold time to set (in ms)." optional:""` +} + +// Run executes the StripCompHoldCmd command, either retrieving the current compressor hold time of the strip or setting it based on the provided argument. +func (cmd *StripCompHoldCmd) Run(ctx *context, strip *StripCmdGroup) error { + if cmd.Hold == nil { + resp, err := ctx.Client.Strip.Comp.Hold(strip.Index.Index) + if err != nil { + return fmt.Errorf("failed to get compressor hold time: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d compressor hold time: %.2f ms\n", strip.Index.Index, resp) + return nil + } + + if err := ctx.Client.Strip.Comp.SetHold(strip.Index.Index, *cmd.Hold); err != nil { + return fmt.Errorf("failed to set compressor hold time: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d compressor hold time set to: %.2f ms\n", strip.Index.Index, *cmd.Hold) + return nil +} + +// StripCompReleaseCmd defines the command for getting or setting the compressor release time of a strip, allowing users to specify the time it takes for the compressor to stop reducing the signal level after the signal falls below the threshold and the hold time has elapsed. +type StripCompReleaseCmd struct { + Release *float64 `arg:"" help:"The compressor release time to set (in ms)." optional:""` +} + +// Run executes the StripCompReleaseCmd command, either retrieving the current compressor release time of the strip or setting it based on the provided argument. +func (cmd *StripCompReleaseCmd) Run(ctx *context, strip *StripCmdGroup) error { + if cmd.Release == nil { + resp, err := ctx.Client.Strip.Comp.Release(strip.Index.Index) + if err != nil { + return fmt.Errorf("failed to get compressor release time: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d compressor release time: %.2f ms\n", strip.Index.Index, resp) + return nil + } + + if err := ctx.Client.Strip.Comp.SetRelease(strip.Index.Index, *cmd.Release); err != nil { + return fmt.Errorf("failed to set compressor release time: %w", err) + } + fmt.Fprintf(ctx.Out, "Strip %d compressor release time set to: %.2f ms\n", strip.Index.Index, *cmd.Release) + return nil +}